#!/bin/bash echo -e "\n[+] Let's begin!\n\n-------\n" # define these first [[ -z "$BASE_DOMAIN" ]] && echo "base domain missing" && exit 1 [[ -z "$CF_EMAIL_ALIAS" ]] && echo "domain email missing" && exit 1 [[ -z "$UBUNTU_PRO_TOKEN" ]] && echo "ubuntu pro token missing" && exit 1 [[ -z "$B2_COLON_BUCKET_NAME" ]] && echo "b2 bucket name missing" && exit 1 [[ -z "$NTFY_URL" ]] && echo "ntfy endpoint missing" && exit 1 domain=$BASE_DOMAIN email_address=${CF_EMAIL_ALIAS} echo "BASE_DOMAIN=${BASE_DOMAIN}" | sudo tee -a /etc/environment echo "BACKUP_BUCKET=${B2_COLON_BUCKET_NAME}" | sudo tee -a /etc/environment # current: the startingOut one echo "NOTIF_URL=${NTFY_URL}" | sudo tee -a /etc/environment # current: endpoint on ntfy.sh # some useful aliases cat instance-bash_aliases | tee -a ~/.bash_aliases cat instance-bash_aliases | sudo tee -a /etc/skel/.bash_aliases # some useful autocompletions chmod 774 instance-bash_autocompletions ./instance-bash_autocompletions cd ~ || exit sudo apt-get update sudo apt-get upgrade -y sudo pro attach "$UBUNTU_PRO_TOKEN" if [[ $(cloud-init query platform) == 'oracle' ]]; then # https://www.reddit.com/r/oraclecloud/comments/r8lkf7/a_quick_tips_to_people_who_are_having_issue/ echo "[+] disabling ufw and netfilter rules (OCI default)" sudo ufw disable sudo iptables -I INPUT -j ACCEPT sudo iptables-save | sudo dd of=/etc/iptables/rules.v4 fi echo "[+] packages" # JDK 17 or higher needed for MC sudo apt-get install build-essential curl gnupg2 ca-certificates lsb-release ubuntu-keyring apt-transport-https expect -y sudo apt-get install openjdk-21-jdk-headless systemd-container fail2ban -y sudo systemctl enable --now fail2ban.service echo "[+] docker" sudo install -m 0775 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \ https://download.docker.com/linux/ubuntu $(lsb_release -cs 2>/dev/null) stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null echo "[+] nginx" # http://nginx.org/en/linux_packages.html#Ubuntu curl -L https://nginx.org/keys/nginx_signing.key | gpg --dearmor | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null expected_nginx_fingerprint='573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62' if ! gpg --dry-run --quiet --no-keyring --import --import-options \ import-show /usr/share/keyrings/nginx-archive-keyring.gpg | grep -c $expected_nginx_fingerprint; then echo -e "\n[!] Nginx GPG key fingerprint does not match, aborting...\n" sudo rm /usr/share/keyrings/nginx-archive-keyring.gpg exit 1 fi echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \ http://nginx.org/packages/ubuntu $(lsb_release -cs 2>/dev/null) nginx" | sudo tee /etc/apt/sources.list.d/nginx.list echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" | sudo tee /etc/apt/preferences.d/99nginx echo "[+] syncthing" sudo curl -L -o /etc/apt/keyrings/syncthing-archive-keyring.gpg https://syncthing.net/release-key.gpg echo "deb [signed-by=/etc/apt/keyrings/syncthing-archive-keyring.gpg]\ https://apt.syncthing.net/ syncthing stable-v2" | sudo tee /etc/apt/sources.list.d/syncthing.list echo -e "Package: *\nPin: origin apt.syncthing.net\nPin-Priority: 990\n" | sudo tee /etc/apt/preferences.d/syncthing.pref echo "[+] putting it all together" sudo apt-get update sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin nginx syncthing -y if ! sudo docker run hello-world | grep -c 'installation appears to be working correctly'; then echo -e "\n[!] Docker installation failed, aborting...\n" exit 1 fi echo "[+] rclone" curl https://rclone.org/install.sh | sudo bash echo "[+] certbot from snap ugh" sudo snap install core sudo snap refresh core sudo apt-get remove certbot sudo snap install --classic certbot sudo ln -s /snap/bin/certbot /usr/bin/certbot echo "[+] add users for applications" # format - tool name underscore 'server' users=( "actual_server" "authelia_server" "foundry_server" "ghost_server" "gitea_server" "homepage_server" "mealie_server" "memos_server" "minecraft_server" "pwpush_server" "shlink_server" "spotmgr_server" "stirling_server" "syncthing_server" "vikunja_server" "wg_server" ) for username in "${users[@]}"; do sudo useradd -m -U -s /bin/bash "${username}" # setup script sudo cp ~/"${username}"-setup /home/"${username}"/ sudo chmod 774 /home/"${username}"/"${username}"-setup sudo chown "${username}":"${username}" /home/"${username}"/"${username}"-setup sudo cp ~/"${username}"-env /home/"${username}"/ sudo chmod 600 /home/"${username}"/"${username}"-env sudo chown "${username}":"${username}" /home/"${username}"/"${username}"-env # user services won't linger by default sudo loginctl enable-linger "${username}" done # admin privileges, needed for anyone running docker admin_users=( "actual_server" "authelia_server" "ghost_server" "gitea_server" "homepage_server" "mealie_server" "memos_server" "pwpush_server" "shlink_server" "spotmgr_server" "stirling_server" "vikunja_server" "wg_server" ) for username in "${admin_users[@]}"; do sudo usermod -aG sudo "${username}" echo "${username} ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/"${username}" # compose files sudo cp ~/"${username}"-compose_template.yaml /home/"${username}"/ sudo chmod 664 /home/"${username}"/"${username}"-compose_template.yaml sudo chown "${username}":"${username}" /home/"${username}"/"${username}"-compose_template.yaml sudo cp ~/"${username}"-compose.yaml /home/"${username}"/ sudo chmod 600 /home/"${username}"/"${username}"-compose.yaml sudo chown "${username}":"${username}" /home/"${username}"/"${username}"-compose.yaml done echo "[+] distribute and apply respective config files" echo -e "\t[-] rclone" for username in "${users[@]}"; do sudo mkdir -p /home/"${username}"/.config/rclone/ sudo cp ~/.config/rclone/rclone.conf /home/"${username}"/.config/rclone/ sudo chmod -R 600 /home/"${username}"/.config/rclone/rclone.conf sudo chown -R "${username}":"${username}" /home/"${username}"/ done # consider switching to acme.sh instead of certbot to avoid snap echo -e "\t[-] nginx and certbot" cert_subdomains=( "api.spotify-manager" "auth" "budget" "dash" "git" "lnk" "notes" "paste" "planning" "pdf" "recipes" "syncthing" "vpn" "vtt" ) # ghost handles SSL by itself, might be worth looking into it to either shift to certbot for subdomain in "${cert_subdomains[@]}"; do # revoke existing certs if any sudo certbot revoke -n --delete-after-revoke --cert-name "${subdomain}"."${domain}" sudo cp ~/"${subdomain}"."${domain}".conf /etc/nginx/conf.d/ sudo chmod 664 /etc/nginx/conf.d/"${subdomain}"."${domain}".conf sudo chown root:root /etc/nginx/conf.d/"${subdomain}"."${domain}".conf if ! sudo nginx -t; then echo -e "\n\t[!] Bad Nginx config for ${subdomain}.${domain}, aborting...\n" exit 1 fi sudo nginx -s reload # ---------------------------------------------------------------------- # STOP! # Check DNS records before proceeding # ---------------------------------------------------------------------- # https://letsencrypt.org/docs/duplicate-certificate-limit/#description # certbot has 5 per week duplicate cert limit. use --test-cert flag for testing if ! sudo certbot -n --nginx --agree-tos -m "${email_address}" -d "${subdomain}"."${domain}"; then echo -e "\n\t[!] Certbot failed to get cert for ${subdomain}.${domain}, aborting...\n" exit 1 fi sudo nginx -s reload done echo -e "\t[-] user-specific files" # bash variable expansion ftw - https://stackoverflow.com/a/63821858/7630441 user_files=( "authelia_server-configuration.yaml" "foundry_server-start.service" "ghost_server-config.production.json" "ghost_server-credentials.exp" "minecraft_server-start.service" "minecraft_server-start.socket" "pwpush_server-settings.yaml" ) for f in "${user_files[@]}"; do username=${f%%-*} # strips the part from before the hyphen sudo cp ~/"${f}" /home/"${username}"/ sudo chmod 664 /home/"${username}"/"${f}" sudo chown "${username}":"${username}" /home/"${username}"/"${f}" done echo -e "[+] cronjobs: backups, updates" for username in "${users[@]}"; do sudo cp ~/"${username}"-backup /home/"${username}"/ sudo chmod 774 /home/"${username}"/"${username}"-backup sudo chown "${username}":"${username}" /home/"${username}"/"${username}"-backup sudo cp ~/"${username}"-update /home/"${username}"/ sudo chmod 774 /home/"${username}"/"${username}"-update sudo chown "${username}":"${username}" /home/"${username}"/"${username}"-update { # first add some useful env vars that aren't in cron's exec env echo "USER=$username" echo "XDG_RUNTIME_DIR=/run/user/$(id -u "$username")" echo "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u "$username")/bus" # then the defined cronjob cat ~/"${username}"-cronjob } >~/"${username}".cronjobs # install to crontab sudo crontab -u "${username}" ~/"${username}".cronjobs rm ~/"${username}".cronjobs done # shellcheck disable=SC2024 sudo crontab -l -u ubuntu >~/ubuntu.cronjobs cat ~/ubuntu-cronjob >>~/ubuntu.cronjobs sudo crontab -u ubuntu ~/ubuntu.cronjobs rm ~/ubuntu.cronjobs for username in "${users[@]}"; do chmod ug+x "${username}"-teardown done