From 7bd4858c7ecd43081f233cc0ab3e9df688e3cbd1 Mon Sep 17 00:00:00 2001 From: Kris Lamoureux Date: Sat, 19 Nov 2022 20:58:07 -0500 Subject: [PATCH] Add DNS-01 ACME wildcard certificate - Add Ansible Vault convenience script --- .gitignore | 2 + Makefile | 2 +- README.md | 10 +++++ dev/vars/webserver.yml | 7 +++- roles/webserver/files/docker-compose.yml | 20 +++++++++- forward-ssh.sh => scripts/forward-ssh.sh | 0 scripts/vault-key.sh | 51 ++++++++++++++++++++++++ 7 files changed, 87 insertions(+), 5 deletions(-) rename forward-ssh.sh => scripts/forward-ssh.sh (100%) create mode 100755 scripts/vault-key.sh diff --git a/.gitignore b/.gitignore index fb9023b..961961f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.ansible_vault +.bitwarden environments *.log .playbook diff --git a/Makefile b/Makefile index 050960b..970a6b0 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ all: vagrant vagrant: vagrant up --no-destroy-on-error --no-color | tee ./vagrantup.log - ./forward-ssh.sh + ./scripts/forward-ssh.sh clean: vagrant destroy -f --no-color diff --git a/README.md b/README.md index 7055862..e832920 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,16 @@ and services operated by Free I.T. Athens (FRITA). - [WordPress](https://www.local.freeitathens.org) 4. Click through the HTTPS security warning +## Production +1. Clone [production-env](https://github.com/freeitathens/production-env/) to `./environments` +2. Run `./scripts/vault-key.sh` from the root of the project to obtain the Ansible Vault password +3. Enter the Bitwarden Master Password +4. Run `ansible-playbook` against the production servers, e.g., + ``` + ansible-playbook -u root -i environments/production --vault-pass-file ./.ansible_vault webserver.yml --check + ``` +5. Delete the `.ansible_vault` file when you are done + ## Authors * **Kris Lamoureux** - *Project Founder* - [@krislamo](https://github.com/krislamo) diff --git a/dev/vars/webserver.yml b/dev/vars/webserver.yml index 1f830b6..1ebb7b5 100644 --- a/dev/vars/webserver.yml +++ b/dev/vars/webserver.yml @@ -25,11 +25,15 @@ webserver: ### Traefik ### ############### #TRAEFIK_VERSION: latest + #TRAEFIK_ROOT_DOMAIN: local.freeitathens.org #TRAEFIK_DOMAIN: traefik.local.freeitathens.org #TRAEFIK_DASHBOARD: true #TRAEFIK_EXPOSED_DEFAULT: false - #TRAEFIK_TRAEFIK_ENABLE: true + #TRAEFIK_WEB_ENABLED: true TRAEFIK_DEBUG: true + TRAEFIK_ACME_PROVIDER: dreamhost + TRAEFIK_ACME_CASERVER: https://acme-v02.api.letsencrypt.org/directory + TRAEFIK_ACME_EMAIL: frita@example.org ################# ### WordPress ### @@ -39,5 +43,4 @@ webserver: #WORDPRESS_DB_HOST: host.docker.internal #WORDPRESS_DB_NAME: wordpress #WORDPRESS_DB_USER: wordpress - #WORDPRESS_WEB_ENABLED: true WORDPRESS_DB_PASSWORD: "{{ secret.WORDPRESS_DB_PASSWORD }}" diff --git a/roles/webserver/files/docker-compose.yml b/roles/webserver/files/docker-compose.yml index 6754beb..e0a22c7 100644 --- a/roles/webserver/files/docker-compose.yml +++ b/roles/webserver/files/docker-compose.yml @@ -14,25 +14,38 @@ services: command: - --api.dashboard=${TRAEFIK_DASHBOARD:-true} - --api.debug=${TRAEFIK_DEBUG:-false} + - --log.level=${TRAEFIK_LOG_LEVEL:-ERROR} - --providers.docker=true - --providers.docker.exposedbydefault=${TRAEFIK_EXPOSED_DEFAULT:-false} - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + - --entrypoints.local.address=:8443 - --entrypoints.web.http.redirections.entrypoint.to=websecure - --entrypoints.web.http.redirections.entrypoint.scheme=https - --entrypoints.web.http.redirections.entrypoint.permanent=true - - --entrypoints.websecure.address=:443 - - --entrypoints.local.address=:8443 + - --certificatesresolvers.letsencrypt.acme.email=${TRAEFIK_ACME_EMAIL} + - --certificatesresolvers.letsencrypt.acme.storage=/etc/letsencrypt/acme.json + - --certificatesresolvers.letsencrypt.acme.dnschallenge=true + - --certificatesresolvers.letsencrypt.acme.dnschallenge.provider=${TRAEFIK_ACME_PROVIDER} + - --certificatesresolvers.letsencrypt.acme.dnschallenge.delaybeforecheck=0 + - --certificatesresolvers.letsencrypt.acme.caserver=${TRAEFIK_ACME_CASERVER:-https://acme-staging-v02.api.letsencrypt.org/directory} + environment: + DREAMHOST_API_KEY: ${TRAEFIK_DREAMHOST_APIKEY} ports: - 80:80 - 443:443 - "127.0.0.1:8443:8443" volumes: - /var/run/docker.sock:/var/run/docker.sock + - ./.acme:/etc/letsencrypt labels: traefik.http.routers.api.rule: Host(`${TRAEFIK_DOMAIN:-traefik.local.freeitathens.org}`) traefik.http.routers.api.entrypoints: local traefik.http.routers.api.service: api@internal traefik.http.routers.api.tls: true + traefik.http.routers.api.tls.certresolver: letsencrypt + traefik.http.routers.api.tls.domains[0].main: ${TRAEFIK_ACME_DOMAIN_MAIN:-local.freeitathens.org} + traefik.http.routers.api.tls.domains[0].sans: "${TRAEFIK_ACME_DOMAIN_SANS:-*.local.freeitathens.org}" traefik.enable: ${TRAEFIK_WEB_ENABLED:-true} networks: - traefik @@ -49,6 +62,9 @@ services: traefik.http.routers.wordpress.rule: Host(`${WORDPRESS_DOMAIN:-www.local.freeitathens.org}`) traefik.http.routers.wordpress.entrypoints: websecure traefik.http.routers.wordpress.tls: true + traefik.http.routers.wordpress.tls.certresolver: letsencrypt + traefik.http.routers.wordpress.tls.domains[0].main: ${TRAEFIK_ACME_DOMAIN_MAIN:-local.freeitathens.org} + traefik.http.routers.wordpress.tls.domains[0].sans: "${TRAEFIK_ACME_DOMAIN_SANS:-*.local.freeitathens.org}" traefik.http.services.wordpress.loadbalancer.server.port: 80 traefik.docker.network: traefik traefik.enable: ${WORDPRESS_WEB_ENABLED:-true} diff --git a/forward-ssh.sh b/scripts/forward-ssh.sh similarity index 100% rename from forward-ssh.sh rename to scripts/forward-ssh.sh diff --git a/scripts/vault-key.sh b/scripts/vault-key.sh new file mode 100755 index 0000000..d422b82 --- /dev/null +++ b/scripts/vault-key.sh @@ -0,0 +1,51 @@ +#!/bin/bash +BW_USERNAME="contact@freeitathens.org" +ANSIBLE_VAULT_ITEM="e16b2542-f6c1-4e9f-8e33-af5201574a15" + +# Does the key already exist? +if [ -f .ansible_vault ]; then + echo "Ansible Vault file already exists at ./.ansible_vault" + exit 1 +fi + +# Install Bitwarden CLI binary to ./.bitwarden/bw +if [ ! -d .bitwarden ]; then + mkdir .bitwarden + cd .bitwarden || exit 1 + wget "https://vault.bitwarden.com/download/?app=cli&platform=linux" -O bw-linux.zip + unzip bw-linux.zip + rm bw-linux.zip + chmod u+x bw +else + cd .bitwarden || exit 1 +fi + +# Get Master Password to unlock vault +read -rsp "Master Password: " BW_PASSWORD +export BW_PASSWORD +echo + +# Login +LOGIN_RESPONSE=$(./bw login "$BW_USERNAME" "$BW_PASSWORD" --response --nointeraction) +if [ ! "$(echo "$LOGIN_RESPONSE" | jq -r .success)" == "true" ]; then + echo "$LOGIN_RESPONSE" | jq -r .message + exit 1 +fi + +# Unlock +UNLOCK_RESPONSE=$(./bw unlock --passwordenv BW_PASSWORD --response --nointeraction) +if [ ! "$(echo "$UNLOCK_RESPONSE" | jq -r .success)" == "true" ]; then + echo "$UNLOCK_RESPONSE" | jq -r .message + exit 1 +fi + +# Trade password for session +unset BW_PASSWORD +BW_SESSION=$(echo "$UNLOCK_RESPONSE" | jq -r .data.raw) +export BW_SESSION + +# Place Ansible Vault secret and logout +./bw get password "$ANSIBLE_VAULT_ITEM" --response --nointeraction | jq -r .data.data > ../.ansible_vault +truncate -s -1 ../.ansible_vault +chmod 600 ../.ansible_vault +./bw logout --quiet