diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..8b75647 --- /dev/null +++ b/NOTES @@ -0,0 +1 @@ +Trezor MUST Use the "Crypto" firmware with shitcoin support in order for 2FA (WEBAUTHN) to work. Bummer. \ No newline at end of file diff --git a/defaults.sh b/defaults.sh index 3020f40..e3ef5ea 100755 --- a/defaults.sh +++ b/defaults.sh @@ -73,8 +73,7 @@ export VLAN_INTERFACE= export VM_NAME="sovereign-stack-base" export DEV_MEMORY_MB="4096" export DEV_CPU_COUNT="4" -export SSHFS_PATH="/tmp/sshfs_temp" -mkdir -p "$SSHFS_PATH" + export DOCKER_IMAGE_CACHE_FQDN="registry-1.docker.io" export NEXTCLOUD_SPACE_GB=10 @@ -92,7 +91,7 @@ export NEXTCLOUD_SPACE_GB=10 # exit 1 # fi -ENABLE_NGINX_CACHING=false + # TODO @@ -105,16 +104,17 @@ BTC_CHAIN=regtest export BTC_CHAIN="$BTC_CHAIN" -DEFAULT_DB_IMAGE="mariadb:10.8.3-jammy" -export ENABLE_NGINX_CACHING="$ENABLE_NGINX_CACHING" +DEFAULT_DB_IMAGE="mariadb:10.9.3-jammy" + # run the docker stack. -export GHOST_IMAGE="ghost:5.12.3" +export GHOST_IMAGE="ghost:5.14.2" export GHOST_DB_IMAGE="$DEFAULT_DB_IMAGE" export NGINX_IMAGE="nginx:1.23.1" -export NEXTCLOUD_IMAGE="nextcloud:24.0.4" +export NEXTCLOUD_IMAGE="nextcloud:24.0.5" export NEXTCLOUD_DB_IMAGE="$DEFAULT_DB_IMAGE" +# TODO PIN the gitea version number. export GITEA_IMAGE="gitea/gitea:latest" export GITEA_DB_IMAGE="$DEFAULT_DB_IMAGE" diff --git a/deploy.sh b/deploy.sh index 94d8a0d..8529bc3 100755 --- a/deploy.sh +++ b/deploy.sh @@ -27,12 +27,13 @@ fi DOMAIN_NAME= RESTORE_ARCHIVE= VPS_HOSTING_TARGET=lxd -RUN_CERT_RENEWAL=true +RUN_CERT_RENEWAL=false RESTORE_WWW=false -BACKUP_WWW=true +BACKUP_CERTS=true +BACKUP_GHOST=true RESTORE_BTCPAY=false -BACKUP_BTCPAY=true +BACKUP_BTCPAY=false MIGRATE_WWW=false MIGRATE_BTCPAY=false @@ -52,7 +53,7 @@ for i in "$@"; do ;; --restore-www) RESTORE_WWW=true - BACKUP_WWW=false + BACKUP_GHOST=false RUN_CERT_RENEWAL=false shift ;; @@ -61,6 +62,10 @@ for i in "$@"; do BACKUP_BTCPAY=false shift ;; + --backup-certs) + BACKUP_CERTS=true + shift + ;; --archive=*) RESTORE_ARCHIVE="${i#*=}" shift @@ -81,12 +86,12 @@ for i in "$@"; do USER_SKIP_BTCPAY=true shift ;; - --no-backup-www) - BACKUP_WWW=false + --backup-ghost) + BACKUP_GHOST=true shift ;; - --no-backup-btcpay) - BACKUP_BTCPAY=false + --backup-btcpay) + BACKUP_BTCPAY=true shift ;; --migrate-www) @@ -99,8 +104,8 @@ for i in "$@"; do RUN_CERT_RENEWAL=false shift ;; - --no-cert-renew) - RUN_CERT_RENEWAL=false + --renew-certs) + RUN_CERT_RENEWAL=true shift ;; --reconfigure-btcpay) @@ -123,7 +128,9 @@ export DOMAIN_NAME="$DOMAIN_NAME" export REGISTRY_DOCKER_IMAGE="registry:2" export RESTORE_ARCHIVE="$RESTORE_ARCHIVE" export RESTORE_WWW="$RESTORE_WWW" -export BACKUP_WWW="$BACKUP_WWW" + +export BACKUP_CERTS="$BACKUP_CERTS" +export BACKUP_GHOST="$BACKUP_GHOST" export RESTORE_BTCPAY="$RESTORE_BTCPAY" export BACKUP_BTCPAY="$RESTORE_BTCPAY" export MIGRATE_WWW="$MIGRATE_WWW" @@ -252,10 +259,6 @@ function instantiate_vms { fi - # create the local packup path if it's not there! - BACKUP_PATH_CREATED=false - - export BACKUP_PATH_CREATED="$BACKUP_PATH_CREATED" export MAC_ADDRESS_TO_PROVISION= export VPS_HOSTNAME="$VPS_HOSTNAME" export FQDN="$VPS_HOSTNAME.$DOMAIN_NAME" @@ -309,25 +312,14 @@ function instantiate_vms { export DDNS_HOST="$DDNS_HOST" export FQDN="$DDNS_HOST.$DOMAIN_NAME" export LXD_VM_NAME="${FQDN//./-}" - BACKUP_TIMESTAMP="$(date +"%Y-%m")" - UNIX_BACKUP_TIMESTAMP="$(date +%s)" export VIRTUAL_MACHINE="$VIRTUAL_MACHINE" - export REMOTE_BACKUP_PATH="$REMOTE_HOME/backups/$VIRTUAL_MACHINE" - export BACKUP_TIMESTAMP="$BACKUP_TIMESTAMP" - export UNIX_BACKUP_TIMESTAMP="$UNIX_BACKUP_TIMESTAMP" export REMOTE_CERT_DIR="$REMOTE_CERT_BASE_DIR/$FQDN" - export REMOTE_BACKUP_PATH="$REMOTE_BACKUP_PATH" + export MAC_ADDRESS_TO_PROVISION="$MAC_ADDRESS_TO_PROVISION" - LOCAL_BACKUP_PATH="$SITE_PATH/backups/$VIRTUAL_MACHINE/$BACKUP_TIMESTAMP" - export LOCAL_BACKUP_PATH="$LOCAL_BACKUP_PATH" + - if [ ! -d "$LOCAL_BACKUP_PATH" ]; then - mkdir -p "$LOCAL_BACKUP_PATH" - BACKUP_PATH_CREATED=true - fi - # This next section of if statements is our sanity checking area. if [ "$VPS_HOSTING_TARGET" = aws ]; then @@ -432,6 +424,7 @@ function stub_site_definition { #!/bin/bash export DOMAIN_NAME="${DOMAIN_NAME}" +export SITE_LANGUAGE_CODES="en" export DUPLICITY_BACKUP_PASSPHRASE="$(new_pass)" # AWS only #export DDNS_PASSWORD= @@ -478,7 +471,6 @@ export WWW_SERVER_MAC_ADDRESS="CHANGE_ME_REQUIRED" export DEPLOY_BTCPAY_SERVER=true export BTCPAYSERVER_MAC_ADDRESS="CHANGE_ME_REQUIRED" # export BTC_CHAIN=mainnet -# export ENABLE_NGINX_CACHING=true export PRIMARY_DOMAIN="CHANGE_ME" export OTHER_SITES_LIST= EOL diff --git a/deployment/www/backup.sh b/deployment/www/backup_path.sh similarity index 53% rename from deployment/www/backup.sh rename to deployment/www/backup_path.sh index 59cb5ce..33c8d5f 100755 --- a/deployment/www/backup.sh +++ b/deployment/www/backup_path.sh @@ -3,18 +3,45 @@ set -eux cd "$(dirname "$0")" + +#$1 should be the app path (ghost,nextcloud,gitea) +#$2 should be the domain to backup + +if [ -z "$1" ]; then + echo "ERROR: the app path was not specified." + exit 1 +fi + # TODO: We are using extra space on the remote VPS at the moment for the duplicity backup files. # we could eliminate that and simply save duplicity backups to the management machine running the script # this could be done by using a local path and mounting it on the remote VPS. # maybe something like https://superuser.com/questions/616182/how-to-mount-local-directory-to-remote-like-sshfs +REMOTE_BACKUP_PATH="$REMOTE_HOME/backups/www/$DOCKER_STACK_SUFFIX-$LANGUAGE_CODE" +REMOTE_BACKUP_LOCATION="$REMOTE_BACKUP_PATH/$1/$DOMAIN_NAME" + # step 1: run duplicity on the remote system to backup all files to the remote system. -ssh "$PRIMARY_WWW_FQDN" sudo PASSPHRASE="$DUPLICITY_BACKUP_PASSPHRASE" duplicity --allow-source-mismatch --exclude "$REMOTE_HOME/backups" "$REMOTE_HOME" "file://$REMOTE_BACKUP_PATH" -ssh "$PRIMARY_WWW_FQDN" sudo chown -R ubuntu:ubuntu "$REMOTE_BACKUP_PATH" +# --allow-source-mismatch +ssh "$PRIMARY_WWW_FQDN" sudo PASSPHRASE="$DUPLICITY_BACKUP_PASSPHRASE" duplicity "$REMOTE_HOME/$1/$DOMAIN_NAME" "file://$REMOTE_BACKUP_LOCATION" +ssh "$PRIMARY_WWW_FQDN" sudo chown -R ubuntu:ubuntu "$REMOTE_BACKUP_LOCATION" + + +SSHFS_PATH="/tmp/sshfs_temp" +mkdir -p "$SSHFS_PATH" # now let's pull down the latest files from the backup directory. # create a temp directory to serve as the mountpoint for the remote machine backups directory -sshfs "$PRIMARY_WWW_FQDN:$REMOTE_BACKUP_PATH" "$SSHFS_PATH" +sshfs "$PRIMARY_WWW_FQDN:$REMOTE_BACKUP_LOCATION" "$SSHFS_PATH" + +# ensure our local backup path exists so we can pull down the duplicity archive to the management machine. +LOCAL_BACKUP_PATH="$SITE_PATH/backups/www/$BACKUP_TIMESTAMP" +if [ "$1" = letsencrypt ]; then + LOCAL_BACKUP_PATH="$SITE_PATH/backups/www/letsencrypt" +fi + +if [ ! -d "$LOCAL_BACKUP_PATH" ]; then + mkdir -p "$LOCAL_BACKUP_PATH" +fi # rsync the files from the remote server to our local backup path. rsync -av "$SSHFS_PATH/" "$LOCAL_BACKUP_PATH/" @@ -22,3 +49,4 @@ rsync -av "$SSHFS_PATH/" "$LOCAL_BACKUP_PATH/" # step 4: unmount the SSHFS filesystem and cleanup. umount "$SSHFS_PATH" rm -rf "$SSHFS_PATH" + diff --git a/deployment/www/generate_certs.sh b/deployment/www/generate_certs.sh index 19329fd..cb6c115 100755 --- a/deployment/www/generate_certs.sh +++ b/deployment/www/generate_certs.sh @@ -54,5 +54,6 @@ elif [ "$VPS_HOSTING_TARGET" = lxd ]; then -v "$REMOTE_HOME/letsencrypt/$DOMAIN_NAME/_logs":/var/log/letsencrypt \ certbot/certbot certonly -v --noninteractive --agree-tos --key-type ecdsa --standalone --expand -d "$DOMAIN_NAME" -d "$WWW_FQDN" -d "$BTCPAY_USER_FQDN" -d "$NEXTCLOUD_FQDN" -d "$GITEA_FQDN" -d "$NOSTR_FQDN" --email "$CERTIFICATE_EMAIL_ADDRESS" + sleep 3 done fi diff --git a/deployment/www/go.sh b/deployment/www/go.sh index 57c6163..b4abac6 100755 --- a/deployment/www/go.sh +++ b/deployment/www/go.sh @@ -4,7 +4,7 @@ set -exu cd "$(dirname "$0")" # Create the nginx config file which covers all domains. -bash -c ./stub_nginxconf.sh +bash -c ./stub/nginx_config.sh # redirect all docker commands to the remote host. export DOCKER_HOST="ssh://ubuntu@$PRIMARY_WWW_FQDN" @@ -97,26 +97,12 @@ for DOMAIN_NAME in ${DOMAIN_LIST//,/ }; do done -### The next series of commands -# stop services. -if docker stack list --format "{{.Name}}" | grep -q webstack; then - docker stack rm webstack - sleep 15 -fi - -if [ "$BACKUP_WWW" = true ]; then - ./backup.sh -fi +./stop_docker_stacks.sh if [ "$RESTORE_WWW" = true ]; then # Generally speaking we try to restore data. But if the BACKUP directory was # just created, we know that we'll deploy fresh. ./restore.sh -else - - if [ "$RUN_CERT_RENEWAL" = true ]; then - ./generate_certs.sh - fi fi @@ -145,7 +131,8 @@ if [ "$DEPLOY_ONION_SITE" = true ]; then # fi fi -bash -c ./stub_docker_yml.sh +bash -c ./stub/nginx_yml.sh +bash -c ./stub/ghost_yml.sh # # start a browser session; point it to port 80 to ensure HTTPS redirect. # wait-for-it -t 320 "$PRIMARY_WWW_FQDN:80" diff --git a/deployment/www/stop_docker_stacks.sh b/deployment/www/stop_docker_stacks.sh new file mode 100755 index 0000000..3fa88a5 --- /dev/null +++ b/deployment/www/stop_docker_stacks.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +set -exu +cd "$(dirname "$0")" + +# bring down ghost instances. +for DOMAIN_NAME in ${DOMAIN_LIST//,/ }; do + export DOMAIN_NAME="$DOMAIN_NAME" + export SITE_PATH="$SITES_PATH/$DOMAIN_NAME" + + # source the site path so we know what features it has. + source ../../reset_env.sh + source "$SITE_PATH/site_definition" + source ../../domain_env.sh + + ### Stop all services. + for APP in ghost; do + for LANGUAGE_CODE in ${SITE_LANGUAGE_CODES//,/ }; do + STACK_NAME="$DOCKER_STACK_SUFFIX-$LANGUAGE_CODE" + if docker stack list --format "{{.Name}}" | grep -q "$STACK_NAME"; then + docker stack rm "$STACK_NAME" + sleep 2 + fi + + if [ "$BACKUP_GHOST" = true ]; then + ./backup_path.sh "$APP" + fi + done + done + +done + + +if docker stack list --format "{{.Name}}" | grep -q reverse-proxy; then + docker stack rm reverse-proxy + + # wait for all docker containers to stop. + # TODO see if there's a way to check for this. + sleep 10 +fi + +# generate the certs and grab a backup +if [ "$RUN_CERT_RENEWAL" = true ]; then + ./generate_certs.sh +fi + +if [ "$BACKUP_CERTS" = true ]; then + # Back each domain's certificates under /home/ubuntu/letsencrypt/domain + for DOMAIN_NAME in ${DOMAIN_LIST//,/ }; do + export DOMAIN_NAME="$DOMAIN_NAME" + export SITE_PATH="$SITES_PATH/$DOMAIN_NAME" + + # source the site path so we know what features it has. + source ../../reset_env.sh + source "$SITE_PATH/site_definition" + source ../../domain_env.sh + + ./backup_path.sh "letsencrypt" + done + +fi diff --git a/deployment/www/stub/ghost_yml.sh b/deployment/www/stub/ghost_yml.sh new file mode 100755 index 0000000..9bb7fbb --- /dev/null +++ b/deployment/www/stub/ghost_yml.sh @@ -0,0 +1,102 @@ +#!/bin/bash + + +domain_number=0 +for DOMAIN_NAME in ${DOMAIN_LIST//,/ }; do + export DOMAIN_NAME="$DOMAIN_NAME" + export SITE_PATH="$SITES_PATH/$DOMAIN_NAME" + + + # source the site path so we know what features it has. + source ../../reset_env.sh + source "$SITE_PATH/site_definition" + source ../../domain_env.sh + + # for each language specified in the site_definition, we spawn a separate ghost container + # at https://www.domain.com/$LANGUAGE_CODE + for LANGUAGE_CODE in ${SITE_LANGUAGE_CODES//,/ }; do + STACK_NAME="$DOCKER_STACK_SUFFIX-$LANGUAGE_CODE" + + # ensure directories on remote host exist so we can mount them into the containers. + ssh "$PRIMARY_WWW_FQDN" mkdir -p "$REMOTE_HOME/ghost/$DOMAIN_NAME/$LANGUAGE_CODE/ghost" "$REMOTE_HOME/ghost/$DOMAIN_NAME/$LANGUAGE_CODE/db" + + export GHOST_STACK_TAG="ghost-$STACK_NAME" + export GHOST_DB_STACK_TAG="ghostdb-$STACK_NAME" + + # todo append domain number or port number. + WEBSTACK_PATH="$SITE_PATH/webstack" + mkdir -p "$WEBSTACK_PATH" + export DOCKER_YAML_PATH="$WEBSTACK_PATH/ghost-$LANGUAGE_CODE.yml" + + # here's the NGINX config. We support ghost and nextcloud. + echo "" > "$DOCKER_YAML_PATH" + cat >>"$DOCKER_YAML_PATH" <>"$DOCKER_YAML_PATH" <>"$DOCKER_YAML_PATH" <>"$DOCKER_YAML_PATH" <>"$DOCKER_YAML_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" + for LANGUAGE_CODE in ${SITE_LANGUAGE_CODES//,/ }; do + STACK_NAME="$DOCKER_STACK_SUFFIX-$LANGUAGE_CODE" + cat >>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" <>"$NGINX_CONF_PATH" < "$DOCKER_YAML_PATH" <> "$DOCKER_YAML_PATH" <> "$DOCKER_YAML_PATH" <> "$DOCKER_YAML_PATH" <> "$DOCKER_YAML_PATH" <>"$NGINX_CONF_PATH" < "$DOCKER_YAML_PATH" <> "$DOCKER_YAML_PATH" <> "$DOCKER_YAML_PATH" <> "$DOCKER_YAML_PATH" <> "$DOCKER_YAML_PATH" < "$DOCKER_YAML_PATH" - - cat >>"$DOCKER_YAML_PATH" <>"$DOCKER_YAML_PATH" <>"$DOCKER_YAML_PATH" <>"$DOCKER_YAML_PATH" <>"$DOCKER_YAML_PATH" < /dev/null && pwd )" source "$SCRIPT_DIR/defaults.sh"