351 lines
11 KiB
Bash
351 lines
11 KiB
Bash
|
|
#!/bin/bash
|
|||
|
|
|
|||
|
|
echo "This Script must be ran as Root"
|
|||
|
|
|
|||
|
|
if [ "$(grep -c "debian" /etc/os-release)" -gt 0 ]; then
|
|||
|
|
echo "Installing Security Packages"
|
|||
|
|
|
|||
|
|
## Basic Server hardening and setup
|
|||
|
|
|
|||
|
|
sudo apt install fail2ban ufw
|
|||
|
|
sudo ufw allow http
|
|||
|
|
sudo ufw allow https
|
|||
|
|
sudo ufw allow ssh
|
|||
|
|
if [ -d "/etc/docker/" ];then
|
|||
|
|
|
|||
|
|
sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release
|
|||
|
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
|||
|
|
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|||
|
|
sudo apt-get update
|
|||
|
|
sudo apt-get install docker-ce docker-ce-cli containerd.io
|
|||
|
|
sudo docker run hello-world # < Check for install error
|
|||
|
|
else
|
|||
|
|
echo "Skipping install of Docker (already Installed)"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
elif [ "$(grep -c "debian" /etc/os-release)" -gt 0 ]; then
|
|||
|
|
echo "Installing Security Packages"
|
|||
|
|
|
|||
|
|
# Basic Server hardening and setup
|
|||
|
|
|
|||
|
|
sudo apt install fail2ban ufw
|
|||
|
|
sudo ufw allow http
|
|||
|
|
sudo ufw allow https
|
|||
|
|
sudo ufw allow ssh
|
|||
|
|
if [ -d "/etc/docker/" ];then
|
|||
|
|
|
|||
|
|
sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release
|
|||
|
|
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
|||
|
|
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|||
|
|
sudo apt-get update
|
|||
|
|
sudo apt-get install docker-ce docker-ce-cli containerd.io
|
|||
|
|
sudo docker run hello-world
|
|||
|
|
else
|
|||
|
|
echo "Skipping install of Docker (already Installed)"
|
|||
|
|
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
if [ -d "/etc/docker/" ];then
|
|||
|
|
echo "Installing Docker Compose"
|
|||
|
|
|
|||
|
|
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname - s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|||
|
|
sudo chmod +x /usr/local/bin/docker-compose
|
|||
|
|
docker-compose --version # < Check for install error
|
|||
|
|
|
|||
|
|
else
|
|||
|
|
echo "Skipping install of Docker Compose (Already Installed)"
|
|||
|
|
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
## Setup folder Sutucture
|
|||
|
|
|
|||
|
|
sudo mkdir /opt/vaultwarden
|
|||
|
|
sudo mkdir /opt/vaultwarden/vw-data
|
|||
|
|
sudo mkdir /opt/vaultwarden/data
|
|||
|
|
sudo mkdir /opt/vaultwarden/data/vaultwarden
|
|||
|
|
sudo mkdir /opt/vaultwarden/data/vaultwarden/db_backup
|
|||
|
|
sudo mkdir /opt/vaultwarden/data/certbot
|
|||
|
|
sudo mkdir /opt/vaultwarden/data/certbot/conf
|
|||
|
|
sudo mkdir /opt/vaultwarden/data/certbot/www
|
|||
|
|
sudo mkdir /opt/vaultwarden/data/nginx
|
|||
|
|
sudo mkdir /opt/vaultwarden/data/ssl
|
|||
|
|
|
|||
|
|
## Input user veriables
|
|||
|
|
|
|||
|
|
echo "Please Specify the Domain Name you would like to use: "
|
|||
|
|
read chosenFQDN
|
|||
|
|
echo "Please Specify the email address you would like to use for LeysEncrypt Certs: "
|
|||
|
|
read chosenEmail
|
|||
|
|
echo "Generating Random Admin Token"
|
|||
|
|
echo "You can also find this in the docker-compose.yml file in /opt/vaultwarden"
|
|||
|
|
wait 5
|
|||
|
|
chosenAdminToken=$(openssl rand -base64 48)
|
|||
|
|
echo "Your Admin Token is: $chosenAdminToken"
|
|||
|
|
echo "Please make a note of this"
|
|||
|
|
read -p "Press enter to continue"
|
|||
|
|
|
|||
|
|
|
|||
|
|
## Create nginx conf
|
|||
|
|
sudo cat <<EOF > /opt/vaultwarden/data/nginx/nginx.conf
|
|||
|
|
|
|||
|
|
|
|||
|
|
http{
|
|||
|
|
## This Server returns a 444 error if the host IP is browsed
|
|||
|
|
server {
|
|||
|
|
listen 80 default_server;
|
|||
|
|
server_name "";
|
|||
|
|
return 444;
|
|||
|
|
}
|
|||
|
|
## This Server is the HTTP Vaultwarden instance
|
|||
|
|
server {
|
|||
|
|
listen 80;
|
|||
|
|
server_name $chosenFQDN;
|
|||
|
|
## The first location returns a redirect to the https site
|
|||
|
|
location / {
|
|||
|
|
return 301 https://$host$request_uri;
|
|||
|
|
}
|
|||
|
|
## The second location is for the acme challenge for LetsEncrypt
|
|||
|
|
location /.well-known/acme-challenge/ {
|
|||
|
|
root /var/www/certbot;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
## This Server returns a 444 error if the host IP is browsed
|
|||
|
|
server {
|
|||
|
|
listen 443 default_server;
|
|||
|
|
server_name "";
|
|||
|
|
return 444;
|
|||
|
|
ssl_certificate /etc/dummyssl/dummycert.pem;
|
|||
|
|
ssl_certificate_key /etc/dummyssl/dummykey.pem;
|
|||
|
|
}
|
|||
|
|
## This Server is the HTTPS Vaultwarden instance
|
|||
|
|
server {
|
|||
|
|
listen 443 ssl;
|
|||
|
|
server_name $chosenFQDN;
|
|||
|
|
|
|||
|
|
ssl_certificate /etc/letsencrypt/live/$chosenFQDN/fullchain.pem;
|
|||
|
|
ssl_certificate_key /etc/letsencrypt/live/$chosenFQDN/privkey.pem;
|
|||
|
|
|
|||
|
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|||
|
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
|||
|
|
include /etc/nginx/cloudflare;
|
|||
|
|
|
|||
|
|
location / {
|
|||
|
|
proxy_pass http://localhost;
|
|||
|
|
proxy_set_header Host $host;
|
|||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|||
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|||
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|||
|
|
}
|
|||
|
|
location /admin {
|
|||
|
|
proxy_pass http://localhost/admin;
|
|||
|
|
proxy_set_header Host $host;
|
|||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|||
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|||
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|||
|
|
auth_basic "Administrator’s Area";
|
|||
|
|
auth_basic_user_file /etc/nginx/.htpasswd;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
EOF
|
|||
|
|
|
|||
|
|
## Create Lets Encrypt Script
|
|||
|
|
sudo cat <<EOF > /opt/vaultwarden/data/nginx/nginx.conf
|
|||
|
|
#!/bin/bash
|
|||
|
|
|
|||
|
|
if ! [ -x "$(command -v docker-compose)" ]; then
|
|||
|
|
echo 'Error: docker-compose is not installed.' >&2
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
domains=($chosenFQDN)
|
|||
|
|
rsa_key_size=4096
|
|||
|
|
data_path="./data/certbot"
|
|||
|
|
email="$chosenEmail" # Adding a valid address is strongly recommended
|
|||
|
|
staging=1 # Set to 1 if you're testing your setup to avoid hitting request limits
|
|||
|
|
|
|||
|
|
if [ -d "$data_path" ]; then
|
|||
|
|
read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
|
|||
|
|
if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
|
|||
|
|
exit
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
|
|||
|
|
if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
|
|||
|
|
echo "### Downloading recommended TLS parameters ..."
|
|||
|
|
mkdir -p "$data_path/conf"
|
|||
|
|
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
|
|||
|
|
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
|
|||
|
|
echo
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo "### Creating dummy certificate for $domains ..."
|
|||
|
|
path="/etc/letsencrypt/live/$domains"
|
|||
|
|
mkdir -p "$data_path/conf/live/$domains"
|
|||
|
|
docker-compose run --rm --entrypoint "\
|
|||
|
|
openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\
|
|||
|
|
-keyout '$path/privkey.pem' \
|
|||
|
|
-out '$path/fullchain.pem' \
|
|||
|
|
-subj '/CN=localhost'" certbot
|
|||
|
|
echo
|
|||
|
|
|
|||
|
|
|
|||
|
|
echo "### Starting nginx ..."
|
|||
|
|
docker-compose up --force-recreate -d nginx
|
|||
|
|
echo
|
|||
|
|
|
|||
|
|
echo "### Deleting dummy certificate for $domains ..."
|
|||
|
|
docker-compose run --rm --entrypoint "\
|
|||
|
|
rm -Rf /etc/letsencrypt/live/$domains && \
|
|||
|
|
rm -Rf /etc/letsencrypt/archive/$domains && \
|
|||
|
|
rm -Rf /etc/letsencrypt/renewal/$domains.conf" certbot
|
|||
|
|
echo
|
|||
|
|
|
|||
|
|
|
|||
|
|
echo "### Requesting Let's Encrypt certificate for $domains ..."
|
|||
|
|
|
|||
|
|
##Join $domains to -d args
|
|||
|
|
domain_args=""
|
|||
|
|
for domain in "${domains[@]}"; do
|
|||
|
|
domain_args="$domain_args -d $domain"
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
## Select appropriate email arg
|
|||
|
|
case "$email" in
|
|||
|
|
"") email_arg="--register-unsafely-without-email" ;;
|
|||
|
|
*) email_arg="--email $email" ;;
|
|||
|
|
esac
|
|||
|
|
|
|||
|
|
## Enable staging mode if needed
|
|||
|
|
if [ $staging != "0" ]; then staging_arg="--staging"; fi
|
|||
|
|
|
|||
|
|
docker-compose run --rm --entrypoint "\
|
|||
|
|
certbot certonly --webroot -w /var/www/certbot \
|
|||
|
|
$staging_arg \
|
|||
|
|
$email_arg \
|
|||
|
|
$domain_args \
|
|||
|
|
--rsa-key-size $rsa_key_size \
|
|||
|
|
--agree-tos \
|
|||
|
|
--force-renewal" certbot
|
|||
|
|
echo
|
|||
|
|
|
|||
|
|
echo "### Reloading nginx ..."
|
|||
|
|
docker-compose exec nginx nginx -s reload
|
|||
|
|
EOF
|
|||
|
|
# Create cloudflare-update-ip-ranges script
|
|||
|
|
sudo cat <<EOF > /opt/vaultwarden/cloudflare-update-ip-ranges.sh
|
|||
|
|
#!/bin/bash
|
|||
|
|
# Location of the nginx config file that contains the CloudFlare IP addresses.
|
|||
|
|
CF_NGINX_CONFIG="/opt/vaultwarden/data/nginx/cloudflare"
|
|||
|
|
# The URLs with the actual IP addresses used by CloudFlare.
|
|||
|
|
CF_URL_IP4="https://www.cloudflare.com/ips-v4"
|
|||
|
|
CF_URL_IP6="https://www.cloudflare.com/ips-v6"
|
|||
|
|
# Temporary files.
|
|||
|
|
CF_TEMP_IP4="/tmp/cloudflare-ips-v4.txt"
|
|||
|
|
CF_TEMP_IP6="/tmp/cloudflare-ips-v6.txt"
|
|||
|
|
# Download the files.
|
|||
|
|
if [ -f /usr/bin/curl ];
|
|||
|
|
then
|
|||
|
|
curl --silent --output $CF_TEMP_IP4 $CF_URL_IP4
|
|||
|
|
curl --silent --output $CF_TEMP_IP6 $CF_URL_IP6
|
|||
|
|
elif [ -f /usr/bin/wget ];
|
|||
|
|
then
|
|||
|
|
wget --quiet --output-document=$CF_TEMP_IP4 --no-check-certificate $CF_URL_IP4
|
|||
|
|
wget --quiet --output-document=$CF_TEMP_IP6 --no-check-certificate $CF_URL_IP6
|
|||
|
|
else
|
|||
|
|
echo "Unable to download CloudFlare files."
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
# Generate the new config file.
|
|||
|
|
echo "# CloudFlare IP Ranges" > $CF_NGINX_CONFIG
|
|||
|
|
echo "# Generated at $(date) by $0" >> $CF_NGINX_CONFIG
|
|||
|
|
echo "" >> $CF_NGINX_CONFIG
|
|||
|
|
echo "# - IPv4 ($CF_URL_IP4)" >> $CF_NGINX_CONFIG
|
|||
|
|
awk '{ print "set_real_ip_from " $0 ";" }' $CF_TEMP_IP4 >> $CF_NGINX_CONFIG
|
|||
|
|
echo "" >> $CF_NGINX_CONFIG
|
|||
|
|
echo "# - IPv6 ($CF_URL_IP6)" >> $CF_NGINX_CONFIG
|
|||
|
|
awk '{ print "set_real_ip_from " $0 ";" }' $CF_TEMP_IP6 >> $CF_NGINX_CONFIG
|
|||
|
|
echo "" >> $CF_NGINX_CONFIG
|
|||
|
|
echo "real_ip_header CF-Connecting-IP;" >> $CF_NGINX_CONFIG
|
|||
|
|
echo "" >> $CF_NGINX_CONFIG
|
|||
|
|
# Remove the temporary files.
|
|||
|
|
rm $CF_TEMP_IP4 $CF_TEMP_IP6
|
|||
|
|
# Reload the nginx config.
|
|||
|
|
cd /opt/vaultwarden
|
|||
|
|
docker-compose restart
|
|||
|
|
|
|||
|
|
EOF
|
|||
|
|
|
|||
|
|
# Create Docker-compose.yml
|
|||
|
|
|
|||
|
|
sudo cat <<EOF > /opt/vaultwarden/docker-compose.yml
|
|||
|
|
version: '3'
|
|||
|
|
|
|||
|
|
services:
|
|||
|
|
vaultwarden:
|
|||
|
|
image: vaultwardenrs/server
|
|||
|
|
hostname: vaultwarden
|
|||
|
|
restart: always
|
|||
|
|
expose:
|
|||
|
|
- "80"
|
|||
|
|
volumes:
|
|||
|
|
- ./vw-data:/data
|
|||
|
|
environment:
|
|||
|
|
WEBSOCKET_ENABLED: 'true'
|
|||
|
|
SIGNUPS_ALLOWED: 'false'
|
|||
|
|
ADMIN_TOKEN: "$chosenAdminToken"
|
|||
|
|
LOG_FILE: ./data/vaultwarden.log
|
|||
|
|
|
|||
|
|
nginx:
|
|||
|
|
image: nginx:1.15-alpine
|
|||
|
|
restart: always
|
|||
|
|
ports:
|
|||
|
|
- "80:80"
|
|||
|
|
- "443:443"
|
|||
|
|
volumes:
|
|||
|
|
- ./data/nginx:/etc/nginx
|
|||
|
|
- ./data/ssl:/etc/ssl
|
|||
|
|
- ./data/certbot/conf:/etc/letsencrypt
|
|||
|
|
- ./data/certbot/www:/var/www/certbot
|
|||
|
|
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
|
|||
|
|
|
|||
|
|
bw_backup:
|
|||
|
|
image: bruceforce/bw_backup:latest
|
|||
|
|
container_name: bw_backup
|
|||
|
|
restart: on-failure
|
|||
|
|
depends_on:
|
|||
|
|
- vaultwarden
|
|||
|
|
volumes:
|
|||
|
|
- ./data/vaultwarden:/data
|
|||
|
|
- /etc/timezone:/etc/timezone:ro
|
|||
|
|
- /etc/localtime:/etc/localtime:ro
|
|||
|
|
environment:
|
|||
|
|
- DB_FILE=/data/db.sqlite3
|
|||
|
|
- BACKUP_FILE=/data/db_backup/bwbackup.sqlite3
|
|||
|
|
- CRON_TIME= 10 * * * *
|
|||
|
|
- TIMESTAMP=true
|
|||
|
|
- UID=0
|
|||
|
|
- GID=0
|
|||
|
|
- ./data/ssl:/etc/ssl
|
|||
|
|
- ./data/certbot/conf:/etc/letsencrypt
|
|||
|
|
- ./data/certbot/www:/var/www/certbot
|
|||
|
|
command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"
|
|||
|
|
|
|||
|
|
certbot:
|
|||
|
|
image: certbot/certbot
|
|||
|
|
restart: always
|
|||
|
|
volumes:
|
|||
|
|
- ./data/certbot/conf:/etc/letsencrypt
|
|||
|
|
- ./data/certbot/www:/var/www/certbot
|
|||
|
|
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
|
|||
|
|
|
|||
|
|
|
|||
|
|
EOF
|
|||
|
|
|
|||
|
|
else
|
|||
|
|
echo "Sorry, this OS is not Debian or Ubuntu based."
|
|||
|
|
|
|||
|
|
fi
|