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
|