From b6f3010a5c247d1411bfcf9a69b7c4ba945e26e1 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 28 May 2021 11:03:18 +0200 Subject: [PATCH 01/10] Add note about lowercase-ness --- .env.sample | 2 +- compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.env.sample b/.env.sample index 073f796..fcbeeab 100644 --- a/.env.sample +++ b/.env.sample @@ -175,5 +175,5 @@ DEFAULT_LOCALE=en # Hidden services (Not Supported) # =============================== -# http_proxy= +# http_proxy= # yes, this should be lowercase # ALLOW_ACCESS_TO_HIDDEN_SERVICE= diff --git a/compose.yml b/compose.yml index 53bebc5..1498a03 100644 --- a/compose.yml +++ b/compose.yml @@ -162,7 +162,7 @@ services: - SAML_UID_ATTRIBUTE - SAML_ATTRIBUTES_STATEMENTS_VERIFIED - SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL - - http_proxy + - http_proxy # yes, this should be lowercase - ALLOW_ACCESS_TO_HIDDEN_SERVICE streaming: From 53a5079e7a2b7d80b809ba3e268667fc26c2334f Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 28 May 2021 11:06:48 +0200 Subject: [PATCH 02/10] Running pass at the .env file --- .env.sample | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.env.sample b/.env.sample index fcbeeab..92c490c 100644 --- a/.env.sample +++ b/.env.sample @@ -4,9 +4,8 @@ DOMAIN=mastodon.swarm-test.autonomic.zone # Enables WEB_DOMAIN if set (FOR FUTURE USE) # USER_DOMAIN= - ## Domain aliases -#EXTRA_DOMAINS=', `www.mastodon.example.com`' +# EXTRA_DOMAINS=', `www.mastodon.example.com`' LETS_ENCRYPT_ENV=production # Please look at https://docs.joinmastodon.org/admin/config/ for the full documentation. @@ -27,9 +26,9 @@ LETS_ENCRYPT_ENV=production # fi LOCAL_DOMAIN=$DOMAIN -#WEB_DOMAIN=$DOMAIN +# WEB_DOMAIN=$DOMAIN -#ALTERNATE_DOMAINS=$EXTRA_DOMAINS +# ALTERNATE_DOMAINS=$EXTRA_DOMAINS AUTHORIZED_FETCH=false LIMITED_FEDERATION_MODE=false @@ -47,7 +46,6 @@ RAILS_SERVE_STATIC_FILES=true # might need this for traefik, need to test DB_HOST=db DB_USER=mastodon DB_NAME=mastodon_production -DB_PASS= DB_PORT=5432 # Redis @@ -80,8 +78,7 @@ OTP_SECRET= # Web Push # ======== -VAPID_PRIVATE_KEY= -VAPID_PUBLIC_KEY= +# VAPID_PUBLIC_KEY= # Limits # ====== From 18eac8b5e092ef55917fd518fd8f6f529ffe1d81 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 28 May 2021 11:17:49 +0200 Subject: [PATCH 03/10] Get secrets laid out --- .env.sample | 7 +++++-- compose.yml | 24 +++++++++++++++++------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/.env.sample b/.env.sample index 92c490c..c64c626 100644 --- a/.env.sample +++ b/.env.sample @@ -73,8 +73,11 @@ ES_ENABLED=false # Secrets # ======= -SECRET_KEY_BASE= -OTP_SECRET= +SECRET_KEY_BASE_VERSION=v1 +SECRET_OTP_SECRET_VERSION=v1 +SECRET_VAPID_PRIVATE_KEY_VERSION=v1 +SECRET_DB_PASSWORD_VERSION=v1 +SECRET_SMTP_PASSWORD_VERSION=v1 # Web Push # ======== diff --git a/compose.yml b/compose.yml index 1498a03..c11780e 100644 --- a/compose.yml +++ b/compose.yml @@ -203,13 +203,23 @@ services: volumes: *appVolume environment: *env -# secrets: -# secret_key_base: -# name: ${STACK_NAME}_secret_key_base_${SECRET_DB_PASSWORD_VERSION} -# external: true -# otp_secret: -# name: ${STACK_NAME}_otp_secret_${SECRET_DB_ROOT_PASSWORD_VERSION} -# external: true +secrets: + secret_key_base: + name: ${STACK_NAME}_secret_key_base_${SECRET_KEY_BASE_VERSION} + external: true + otp_secret: + name: ${STACK_NAME}_otp_secret_${SECRET_OTP_SECRET_VERSION} + external: true + vapid_private_key: + name: ${STACK_NAME}_vapid_private_key_${SECRET_VAPID_PRIVATE_KEY_VERSION} + external: true + db_password: + name: ${STACK_NAME}_db_password_${SECRET_DB_PASSWORD_VERSION} + external: true + smtp_password: + name: ${STACK_NAME}_smtp_password_${SECRET_SMTP_PASSWORD_VERSION} + external: true + volumes: app: redis: From 57cdd8e23f30497a001abaf8db587d299eaf3956 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 28 May 2021 11:25:09 +0200 Subject: [PATCH 04/10] Try to get ES in the mix --- .env.sample | 9 ++++----- compose.yml | 37 +++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.env.sample b/.env.sample index c64c626..4d9c344 100644 --- a/.env.sample +++ b/.env.sample @@ -59,12 +59,11 @@ REDIS_PORT=6379 # CACHE_REDIS_URL= # CACHE_REDIS_NAMESPACE= -# ElasticSearch (CURRENTLY NOT SUPPORTED) +# ElasticSearch # -------------------------------------- -ES_ENABLED=false -# ES_HOST=localhost -# ES_PORT=9200 -# ES_PREFIX= +ES_ENABLED=true +ES_HOST=es +ES_PORT=9200 # StatsD (CURRENTLY NOT SUPPORTED) # ------------------------------- diff --git a/compose.yml b/compose.yml index c11780e..48bfe54 100644 --- a/compose.yml +++ b/compose.yml @@ -23,24 +23,23 @@ services: volumes: - redis:/data - # es: - # restart: always - # image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.10 - # environment: - # - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - # - "cluster.name=es-mastodon" - # - "discovery.type=single-node" - # - "bootstrap.memory_lock=true" - # networks: - # - internal_network - # healthcheck: - # test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"] - # volumes: - # - ./elasticsearch:/usr/share/elasticsearch/data - # ulimits: - # memlock: - # soft: -1 - # hard: -1 + es: + image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.10 + environment: + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - "cluster.name=es-mastodon" + - "discovery.type=single-node" + - "bootstrap.memory_lock=true" + networks: + - internal_network + healthcheck: + test: ["CMD-SHELL", "curl --silent --fail localhost:9200/_cluster/health || exit 1"] + volumes: + - es:/usr/share/elasticsearch/data + ulimits: + memlock: + soft: -1 + hard: -1 web: image: &image tootsuite/mastodon:v3.3.0 @@ -224,6 +223,8 @@ volumes: app: redis: postgres: + es: + networks: proxy: external: true From 899a5b3b829767786782699155a3422b4b1d8f5c Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 28 May 2021 11:36:24 +0200 Subject: [PATCH 05/10] Wire up db_password --- compose.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compose.yml b/compose.yml index 48bfe54..6dfd419 100644 --- a/compose.yml +++ b/compose.yml @@ -10,10 +10,13 @@ services: test: ["CMD", "pg_isready", "-U", "postgres"] volumes: - postgres:/var/lib/postgresql/data + secrets: + - db_password environment: - - POSTGRES_PASSWORD=${DB_PASS} - - POSTGRES_USER=${DB_USER} - POSTGRES_DB=${DB_NAME} + - POSTGRES_PASSWORD=${DB_PASS} + - POSTGRES_PASSWORD_FILE=/run/secrets/db_password + - POSTGRES_USER=${DB_USER} redis: image: redis:6.0-alpine From 8482921f370f0beee349c9d6b7002be8a83d488a Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 28 May 2021 11:36:31 +0200 Subject: [PATCH 06/10] Use our usual deploy config --- compose.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/compose.yml b/compose.yml index 6dfd419..b4db3cd 100644 --- a/compose.yml +++ b/compose.yml @@ -57,8 +57,9 @@ services: "wget -q --spider --proxy=off localhost:3000/health || exit 1", ] deploy: - restart_policy: - condition: on-failure + update_config: + failure_action: rollback + order: start-first labels: - "traefik.enable=true" - "traefik.docker.network=proxy" @@ -178,8 +179,9 @@ services: "wget -q --spider --proxy=off localhost:4000/api/v1/streaming/health || exit 1", ] deploy: - restart_policy: - condition: on-failure + update_config: + failure_action: rollback + order: start-first labels: - "traefik.enable=true" - "traefik.docker.network=proxy" @@ -199,8 +201,9 @@ services: image: *image command: bundle exec sidekiq deploy: - restart_policy: - condition: on-failure + update_config: + failure_action: rollback + order: start-first networks: *bothNetworks volumes: *appVolume environment: *env From 04855dc49474814c0024bd86bbaee9d41b68c2cc Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 28 May 2021 11:38:35 +0200 Subject: [PATCH 07/10] Share those secrets around --- compose.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/compose.yml b/compose.yml index b4db3cd..a47d519 100644 --- a/compose.yml +++ b/compose.yml @@ -78,9 +78,12 @@ services: volumes: &appVolume - app:/mastodon - # secrets: &secrets - # - secret_key_base - # - otp_secret + secrets: &secrets + - db_password + - otp_secret + - secret_key_base + - smtp_password + - vapid_private_key environment: &env - DB_HOST - DB_USER @@ -171,6 +174,7 @@ services: streaming: image: *image command: node ./streaming + secrets: *secrets networks: *bothNetworks healthcheck: test: @@ -199,6 +203,7 @@ services: sidekiq: image: *image + secrets: *secrets command: bundle exec sidekiq deploy: update_config: From 92a94e93c67f5f887e2ba1f430cd43be6b152e36 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 28 May 2021 11:49:12 +0200 Subject: [PATCH 08/10] Get the secret/entrypoint/config thing wired up --- abra.sh | 1 + compose.yml | 25 ++++++++++++++++++++----- entrypoint.sh.tmpl | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 entrypoint.sh.tmpl diff --git a/abra.sh b/abra.sh index 09b6f31..9af2fa4 100644 --- a/abra.sh +++ b/abra.sh @@ -1,4 +1,5 @@ # shellcheck disable=SC2148 +export ENTRYPOINT_CONF_VERSION=v1 #MASTO_APP_DIR="mastodon/public" sub_rake() { diff --git a/compose.yml b/compose.yml index a47d519..599d365 100644 --- a/compose.yml +++ b/compose.yml @@ -76,6 +76,11 @@ services: # - "traefik.http.routers.${STACK_NAME}_hack.entrypoints=websecure" # - "traefik.http.routers.${STACK_NAME}_hack.middlewares=mastodon-webfinger@docker" + configs: &configs + - source: entrypoint_sh + target: /usr/local/bin/entrypoint.sh + mode: 0555 + entrypoint: &entrypoint /usr/local/bin/entrypoint.sh volumes: &appVolume - app:/mastodon secrets: &secrets @@ -88,7 +93,7 @@ services: - DB_HOST - DB_USER - DB_NAME - - DB_PASS + - DB_PASS_FILE=/run/secrets/db_password - DB_PORT - REDIS_HOST - REDIS_PORT @@ -104,10 +109,10 @@ services: - ES_PREFIX - STATSD_ADDR - STATSD_NAMESPACE - - VAPID_PRIVATE_KEY + - VAPID_PRIVATE_KEY_FILE=/run/secrets/vapid_private_key - VAPID_PUBLIC_KEY - - OTP_SECRET - - SECRET_KEY_BASE + - OTP_SECRET_FILE=/run/secrets/otp_secret + - SECRET_KEY_BASE_FILE=/run/secrets/secret_key_base - LOCAL_DOMAIN - WEB_DOMAIN - ALTERNATE_DOMAINS @@ -124,7 +129,7 @@ services: - SMTP_SERVER - SMTP_PORT - SMTP_LOGIN - - SMTP_PASSWORD + - SMTP_PASSWORD_FILE=/run/secrets/smtp_password - SMTP_FROM_ADDRESS - SMTP_DOMAIN - SMTP_DELIVERY_METHOD @@ -174,6 +179,8 @@ services: streaming: image: *image command: node ./streaming + configs: *configs + entrypoint: *entrypoint secrets: *secrets networks: *bothNetworks healthcheck: @@ -205,6 +212,8 @@ services: image: *image secrets: *secrets command: bundle exec sidekiq + configs: *configs + entrypoint: *entrypoint deploy: update_config: failure_action: rollback @@ -241,3 +250,9 @@ networks: external: true internal_network: internal: true + +configs: + entrypoint_sh: + name: ${STACK_NAME}_entrypoint_conf_${ENTRYPOINT_CONF_VERSION} + file: entrypoint.sh.tmpl + template_driver: golang diff --git a/entrypoint.sh.tmpl b/entrypoint.sh.tmpl new file mode 100644 index 0000000..b9cc5c1 --- /dev/null +++ b/entrypoint.sh.tmpl @@ -0,0 +1,32 @@ +#!/bin/bash + +set -eu + +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + + if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + + local val="$def" + if [ "${!var:-}" ]; then + val="${!var}" + elif [ "${!fileVar:-}" ]; then + val="$(< "${!fileVar}")" + fi + + export "$var"="$val" + unset "$fileVar" +} + +file_env "DB_PASS" +file_env "OTP_SECRET" +file_env "SECRET_KEY_BASE" +file_env "SMTP_PASSWORD" +file_env "VAPID_PRIVATE_KEY" + +/usr/bin/tini -- "$@" From 3c8770d5fb1c893b97dec9c73f67321db6881239 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 28 May 2021 11:56:22 +0200 Subject: [PATCH 09/10] Re-add that EXTRA_DOMAINS config, woops --- compose.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compose.yml b/compose.yml index 599d365..ec505f2 100644 --- a/compose.yml +++ b/compose.yml @@ -76,6 +76,12 @@ services: # - "traefik.http.routers.${STACK_NAME}_hack.entrypoints=websecure" # - "traefik.http.routers.${STACK_NAME}_hack.middlewares=mastodon-webfinger@docker" + ## Redirect from EXTRA_DOMAINS to DOMAIN + #- "traefik.http.routers.${STACK_NAME}.middlewares=${STACK_NAME}-redirect" + #- "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLForceHost=true" + #- "traefik.http.middlewares.${STACK_NAME}-redirect.headers.SSLHost=${DOMAIN}" + + configs: &configs - source: entrypoint_sh target: /usr/local/bin/entrypoint.sh From 580fc560c7e7b4555d7dcb4bb181877db2641419 Mon Sep 17 00:00:00 2001 From: decentral1se Date: Fri, 28 May 2021 12:54:02 +0200 Subject: [PATCH 10/10] Document secret insertion and format --- README.md | 68 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 096a41d..7738a2b 100644 --- a/README.md +++ b/README.md @@ -3,26 +3,64 @@ Your self-hosted, globally interconnected microblogging community -* **Category**: -* **Status**: -* **Image**: [`tootsuite/mastodon`](https://hub.docker.com/r/tootsuite/mastodon) -* **Healthcheck**: -* **Backups**: -* **Email**: -* **Tests**: -* **SSO**: + +- **Category**: +- **Status**: +- **Image**: [`tootsuite/mastodon`](https://hub.docker.com/r/tootsuite/mastodon) +- **Healthcheck**: +- **Backups**: +- **Email**: +- **Tests**: +- **SSO**: ## Basic usage 1. Set up Docker Swarm and [`abra`] -2. Deploy [`coop-cloud/traefik`] -3. `abra app new mastodon` -4. `abra app YOURAPPDOMAIN config` - be sure to change `DOMAIN` to something that resolves to - your Docker swarm box. Install the npm package webpush to create VAPID keys `npm install web-push -g && web-push generate-vapid-keys`. -5. `abra app YOURAPPDOMAIN deploy --no-domain-poll`. App will fail for now. -6. `abra app mastodon run streaming rake db:setup` -7. Open the configured domain in your browser to finish set-up. To make an admin account `abra app mastodon run web "bin/tootctl accounts create coolusername --email helo@autonomic.zone --confirmed --role admin"` +1. Deploy [`coop-cloud/traefik`] +1. `abra app new mastodon` +1. `abra app YOURAPPDOMAIN config` - be sure to change `DOMAIN` to something that resolves to + your Docker swarm box. +1. Follow the secrets setup documentation below. +1. `abra app YOURAPPDOMAIN deploy --no-domain-poll`. App will fail for now. +1. `abra app mastodon run streaming rake db:setup` +1. Open the configured domain in your browser to finish set-up. To make an admin account `abra app mastodon run web "bin/tootctl accounts create coolusername --email helo@autonomic.zone --confirmed --role admin"` [`abra`]: https://git.autonomic.zone/autonomic-cooperative/abra [`coop-cloud/traefik`]: https://git.autonomic.zone/coop-cloud/traefik + +## Secrets setup + +Because Mastodon expects secrets generated by specific tools, we don't support that in `abra` yet. However, you can run these commands yourself using the underlying Docker CLI. You can then load them in as secrets to the swarm using `abra` though and then they will be picked up on the deployment. + +First, generate the `SECRET_KEY_BASE` and `OTP_SECRET` and store them in your local shell environment, you'll need them for subsequent commands. + +``` +$ SECRET_KEY_BASE=$(docker run --rm tootsuite/mastodon:v3.4.0 bundle exec rake secret) +$ OTP_SECRET=$(docker run --rm tootsuite/mastodon:v3.4.0 bundle exec rake secret) +$ printf $SECRET_KEY_BASE | abra app YOURAPPDOMAIN secret insert secret_key_base v1 - +$ printf $OTP_SECRET | abra app YOURAPPDOMAIN secret insert otp_secret v1 - +``` + +Then you need to generate the `VAPID_{PUBLIC/PRIVATE}_KEY` values using the `SECRET_KEY_BASE`/`OTP_SECRET`: + +``` +$ docker run \ + -e SECRET_KEY_BASE=$SECRET_KEY_BASE \ + -e OTP_SECRET=$OTP_SECRET \ + --rm tootsuite/mastodon:v3.4.0 \ + bundle exec rake mastodon:webpush:generate_vapid_key +``` + +Once you see the values generated, you can load the `VAPID_PUBLIC_KEY` into your `.env` file and `VAPID_PRIVATE_KEY` into a secret. + +``` +$ printf YOURVAPIDPRIVATEKEY | abra app YOURDOMAIN secret insert vapid_private_key v1 - +``` + +And finally, to end your whirlwind secrets loading adventure, get the `DB_PASS` and `SMTP_PASSWORD` loaded. + +``` +$ abra app YOURAPPDOMAIN secret generate db_password v1 +$ printf YOURSMTPPASSWORD | abra app YOURDOMAIN secret insert smtp_password v1 - +```