#!/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 < /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 < /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 < /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 < /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