From c3bac42f72b5fd9d12e150b6a1b5e0959a82a8cf Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 9 Oct 2020 15:02:42 -0400 Subject: [PATCH 01/29] Split frontend into nginx and certbot containers --- Dockerfile | 8 +- certmgr/Dockerfile | 11 +++ certmgr/scripts/README.md | 83 ++++++++++++++++ certmgr/scripts/entrypoint.sh | 20 ++++ certmgr/scripts/func.sh | 94 +++++++++++++++++++ certmgr/scripts/letsencrypt.sh | 35 +++++++ certmgr/scripts/selfsigned.sh | 30 ++++++ docker_deploy/group_vars/qa/main.yml | 10 ++ .../roles/combine_config/defaults/main.yml | 1 + .../roles/combine_config/tasks/main.yml | 16 ++-- .../templates/docker-compose.yml.j2 | 20 +++- .../combine_config/templates/env.certmgr.j2 | 9 ++ nginx/certs/cert.pem | 27 ------ nginx/certs/key.pem | 52 ---------- .../thecombine.template} | 25 ++--- 15 files changed, 332 insertions(+), 109 deletions(-) create mode 100644 certmgr/Dockerfile create mode 100644 certmgr/scripts/README.md create mode 100755 certmgr/scripts/entrypoint.sh create mode 100755 certmgr/scripts/func.sh create mode 100755 certmgr/scripts/letsencrypt.sh create mode 100755 certmgr/scripts/selfsigned.sh create mode 100644 docker_deploy/roles/combine_config/templates/env.certmgr.j2 delete mode 100644 nginx/certs/cert.pem delete mode 100644 nginx/certs/key.pem rename nginx/{conf.d/thecombine.conf => templates/thecombine.template} (64%) diff --git a/Dockerfile b/Dockerfile index 4434e76f04..f2422cf55e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ COPY . ./ RUN npm run build # Production environment. -FROM staticfloat/nginx-certbot +FROM nginx:1.18 WORKDIR /app @@ -19,8 +19,4 @@ ENV NGINX_HOST_DIR /usr/share/nginx/html COPY --from=builder /app/build ${NGINX_HOST_DIR} -# Copy default self-signed certificate. -# Overwrite this with real certificate for authentication in production. -COPY nginx/certs /ssl - -COPY nginx/conf.d/thecombine.conf /etc/nginx/user.conf.d/thecombine.conf +COPY nginx/templates/* /etc/nginx/templates diff --git a/certmgr/Dockerfile b/certmgr/Dockerfile new file mode 100644 index 0000000000..f5fcbf1312 --- /dev/null +++ b/certmgr/Dockerfile @@ -0,0 +1,11 @@ +FROM certbot/certbot:v1.8.0 + +VOLUME /etc/letsencrypt + +RUN apk update && \ + apk upgrade && \ + apk add --no-cache bash + +COPY scripts/*.sh /usr/bin/ + +ENTRYPOINT ["entrypoint.sh"] diff --git a/certmgr/scripts/README.md b/certmgr/scripts/README.md new file mode 100644 index 0000000000..2c1be6e8d8 --- /dev/null +++ b/certmgr/scripts/README.md @@ -0,0 +1,83 @@ +# Entrypoint scripts for the certbot container for TheCombine + +## self-signed.sh +Creates a self-signed certificate to be used by the nginx web server and exits. + +#### Environment Variables + +| Variable Name | Description | +| ------------------- | ---------------------------------------------------------------------------- | +| CERT_PATH | Directory for the SSL certificates | +| CERT_CLEAN | Set to 1 to remove old certificates first | +| CERT_PRIMARY_DOMAIN | Primary domain for the certificate - used to specify location of certificate | +| | | + +## letsencrypt.sh +Uses certbot to create/renew a certificate for the nginx web server. If there are +no certificates or if the certificate is for `localhost` then the certificates will +be deleted and a certificate will be generated by `letsencrypt`. `letsencrypt` +will then enter a loop to check for certificate renewal every 12 hours. + +#### Environment Variables + +| Variable Name | Description | +| ----------------- | ------------------------------------------------ | +| CERT_PATH | Directory for the SSL certificates | +| CERT_STAGING | Set to 1 to generate a testing certificate | +| CERT_DOMAINS | List of domains for the certificate | +| CERT_KEY_SIZE | Size for the RSA Key (Optional: default is 4096) | +| CERT_EMAIL | Support email for the certificate | +| FORCE | Delete any existing certificates first | +| EXIT_AFTER_CREATE | Exit after creating the certificates | + +## certserver.sh +*Not Implemented Yet* + +creates multiple certificates and pushes them to an AWS S3 bucket + +## certclient.sh +*Not Implemented Yet* + +fetches an SSL certificate from the AWS S3 bucket (when an internet connection is +available) + +# Installation Requirements + +The following scenarios list the various requirements for the Ansible playbook that +is used to setup the various targets, `playbook_target_setup.yml` and the Python +script that is used to setup the development environment, `docker_setup.py`. `docker_setup.py` +is only used for setting up a development environment that uses a self-signed +certificate. + +For each scenario, the first step is to build each of the containers and then run +the appropriate script. + +The sections below list the steps that must be performed for each of the installation +scenarios. + +## Self-Signed Certificates +When installing TheCombine for using self-signed certificates, e.g. for the QA server +or for development use, the following steps are required: + 1. Build each container: + - frontend + - backend + - certbot + 2. Install & configure the docker files: + 1. for Linux server (e.g. QA server) run the Ansible script, `playbook_target_setup.yml` + 2. for Development mode, run `docker_setup.py` + 3. Run `docker-compose up --detach` + +## Let's Encrypt Certificates +When installing TheCombine for using certificates from letsencrypt, e.g. for the +Live server, the following steps are required: + 1. Build each container: + - frontend + - backend + - certbot + 2. Install & configure the docker files: run the `playbook_target_setup.yml` playbook; The Playbook will + 1. + 3. Create a dummy, self-signed certificate: run `docker-compose run --rm --entrypoint "selfsigned.sh" certbot` + 4. Start up the frontend: run `docker-compose up --force-recreate -d frontend` + 5. Start the certbot container to get a certificate from letsencrypt: run `docker-compose run --rm -e CERT_CLEAN=1 --entrypoint "letsencrypt.sh" certbot` + 6. Reload the nginx configuration: run `docker-compose exec frontend nginx -s reload` (may not be necessary since certificates are in the same location) + 7. Run TheCombine: `docker-compose up --detach` diff --git a/certmgr/scripts/entrypoint.sh b/certmgr/scripts/entrypoint.sh new file mode 100755 index 0000000000..8b451b9a22 --- /dev/null +++ b/certmgr/scripts/entrypoint.sh @@ -0,0 +1,20 @@ +#! /bin/bash + +CERT_MODE=${CERT_MODE:="self-signed"} + +case ${CERT_MODE} in + self-signed) + selfsigned.sh + ;; + letsencrypt) + letsencrypt.sh + ;; + cert-server | cert-client) + echo "${CERT_MODE} is not implemented yet" + ;; + *) + echo "${CERT_MODE} is not recognized" + ;; +esac + +exit 99 diff --git a/certmgr/scripts/func.sh b/certmgr/scripts/func.sh new file mode 100755 index 0000000000..d500c7eb7b --- /dev/null +++ b/certmgr/scripts/func.sh @@ -0,0 +1,94 @@ +#! /bin/bash + +#################################################### +# Initialize all the variables used by these scripts +#################################################### +init_vars() { + # set variables to their default values if they are not + # specified + CERT_PATH=${CERT_PATH:="/etc/letsencrypt"} + CERT_CLEAN=${CERT_CLEAN:=0} + CERT_CREATE_ONLY=${CERT_CREATE_ONLY:=0} + CERT_EMAIL=${CERT_EMAIL:="jimgrady.jg@gmail.com"} + CERT_STAGING=${CERT_STAGING:=1} + MAX_CONNECT_TRIES=${MAX_CONNECT_TRIES:=15} + if [ "${CERT_MODE}" = "self-signed" ] ; then + CERT_SELF_SIGNED_EXPIRE=1 + CERT_MIN_DAYS_TO_EXPIRE=30 + else + CERT_SELF_SIGNED_EXPIRE=1 + CERT_MIN_DAYS_TO_EXPIRE=1 + fi + # create $cert_domains as an array of domain names + IFS=" " read -r -a cert_domains <<< "${CERT_DOMAINS}" + if [ "$CERT_VERBOSE" = "1" ] ; then + echo "Certificates stored in ${CERT_PATH}" + echo "Domains: ${cert_domains[*]}" + echo "Certificates expire in ${CERT_SELF_SIGNED_EXPIRE}" + echo "Minimum days before renewal: ${CERT_MIN_DAYS_TO_EXPIRE}" + fi +} + +clean_certs() { + if [ "$CERT_VERBOSE" = "1" ] ; then + echo "Removing certificates in ${CERT_PATH} for ${cert_domains}" + fi + rm -rf "${CERT_PATH}/live/${cert_domains}" + rm -rf "${CERT_PATH}/archive/${cert_domains}" + rm -f "${CERT_PATH}/renewal/${cert_domains}.conf" +} + +create_selfsigned_cert() { + DEST_DIR="${CERT_PATH}/live/${cert_domains}" + mkdir -p ${DEST_DIR} + + cd ${DEST_DIR} + openssl req -x509 -nodes -newkey rsa:1024 -days ${CERT_SELF_SIGNED_EXPIRE} -keyout privkey.pem -out fullchain.pem -subj '/CN=localhost' + if [ "$CERT_VERBOSE" = "1" ] ; then + echo "Created certificate in ${CERT_PATH} for ${cert_domains}" + echo "Expires: "`openssl x509 -in fullchain.pem -noout -enddate` + fi +} + +create_certbot_cert() { + if [ "$CERT_VERBOSE" = "1" ] ; then + echo "create certbot certificate" + fi +} + +wait_for_webserver() { + if [ "$CERT_VERBOSE" = "1" ] ; then + echo "Attempting connection to ${cert_domains}" + echo "Max attempts = ${MAX_CONNECT_TRIES}" + fi + count=0; + while [ ${count} -lt ${MAX_CONNECT_TRIES} ] ; do + if [ "${CERT_VERBOSE}" = "1" ] ; then + echo "Connection attempt ${count}" + fi + if curl -I "http://${cert_domains}" 2>&1 | grep -w "200\|301" ; then + if [ "$CERT_VERBOSE" = "1" ] ; then + echo "${cert_domains} is up." + fi + return 0; + fi + let "count+=1" + sleep 15 + done + echo "Failed to connect to ${cert_domains}" >2 + return 1; +} + +check_renewal() +{ + if [ "$CERT_VERBOSE" = "1" ] ; then + echo "Checking for renewal of ${CERT_PATH}/live/${cert_domains}/fullchain.pem" + fi + + CERT_MIN_SEC_TO_EXPIRE=$(( ${CERT_MIN_DAYS_TO_EXPIRE} * 3600 * 24 )) + openssl x509 -noout -in "${CERT_PATH}/live/${cert_domains}/fullchain.pem" -checkend ${CERT_MIN_SEC_TO_EXPIRE} > /dev/null + if [ "$?" = "1" ] ; then + echo "Renewing the certificate for ${cert_domains}" + create_selfsigned_cert + fi +} diff --git a/certmgr/scripts/letsencrypt.sh b/certmgr/scripts/letsencrypt.sh new file mode 100755 index 0000000000..be88743fe7 --- /dev/null +++ b/certmgr/scripts/letsencrypt.sh @@ -0,0 +1,35 @@ +#! /bin/bash + +# Source in util.sh so we can have our nice tools +. $(cd $(dirname $0); pwd)/func.sh + +CERT_MODE="self-signed" + +# Container initialization +init_vars + +if [ ${CERT_CLEAN} = "1" ] ; then + if [ -d "${CERT_PATH}/live/${cert_domains}" ]; then + clean_certs + fi +fi + +# Create the initial certificate if the certs have not already +# been created +if [ ! -d "${CERT_PATH}/live/${cert_domains}" ]; then + echo "Creating initial self-signed certificate" + create_selfsigned_cert + echo "Waiting for webserver to come up" + if ! wait_for_webserver ; then + exit 1 + fi + echo "Request certificate from Let's Encrypt" + create_certbot_cert +fi + +# Check for certificate renewal every 12 hours +while :; do + certbot renew + sleep 12h & + wait $! +done diff --git a/certmgr/scripts/selfsigned.sh b/certmgr/scripts/selfsigned.sh new file mode 100755 index 0000000000..e183a79a6b --- /dev/null +++ b/certmgr/scripts/selfsigned.sh @@ -0,0 +1,30 @@ +#! /bin/bash + +# Source in util.sh so we can have our nice tools +. $(cd $(dirname $0); pwd)/func.sh + +CERT_MODE="self-signed" + +# Container initialization +init_vars + +if [ ${CERT_CLEAN} = "1" ] ; then + if [ -d "${CERT_PATH}/live/${cert_domains}" ]; then + clean_certs + fi +fi + +if [ -d "${CERT_PATH}/live/${cert_domains}" ]; then + create_selfsigned_cert +fi + +if [ "$CERT_CREATE_ONLY" = "0" ] ; then + while true; do + check_renewal + sleep 60 & + wait $! + + done +else + exit 0 +fi diff --git a/docker_deploy/group_vars/qa/main.yml b/docker_deploy/group_vars/qa/main.yml index 22ee03852d..6a707686c3 100644 --- a/docker_deploy/group_vars/qa/main.yml +++ b/docker_deploy/group_vars/qa/main.yml @@ -17,7 +17,17 @@ image_tag: "${IMAGE_TAG}" combine_image_backend: "{{ aws_ecr }}/combine/backend:{{ image_tag }}" combine_image_frontend: "{{ aws_ecr }}/combine/frontend:{{ image_tag }}" +combine_image_certmgr: "{{ aws_ecr }}/combine/certmgr:{{ image_tag }}" +# SSL Certificate variables +# for both Let's Encrypt and Self-signed certs +cert_type: self-signed +cert_email: jimgrady.jg@gmail.com +cert_is_staging: 1 +cert_domains: + - "{{ combine_server_name }}" +cert_clean: 0 +cert_verbose: 1 # setup variables for roles/aws_access aws_user: "{{ combine_user }}" diff --git a/docker_deploy/roles/combine_config/defaults/main.yml b/docker_deploy/roles/combine_config/defaults/main.yml index 1068517053..8e1d8f216a 100644 --- a/docker_deploy/roles/combine_config/defaults/main.yml +++ b/docker_deploy/roles/combine_config/defaults/main.yml @@ -14,3 +14,4 @@ certbot_is_staging: 0 combine_pull_images: true combine_image_backend: combine/backend combine_image_frontend: combine/frontend +combine_image_certmgr: combine/certmgr diff --git a/docker_deploy/roles/combine_config/tasks/main.yml b/docker_deploy/roles/combine_config/tasks/main.yml index 9cee584de3..d9fb97ff39 100644 --- a/docker_deploy/roles/combine_config/tasks/main.yml +++ b/docker_deploy/roles/combine_config/tasks/main.yml @@ -31,19 +31,15 @@ - name: create frontend environment file template: - src: env.frontend.j2 - dest: "{{ combine_app_dir }}/.env.frontend" - owner: "{{ combine_user }}" - group: "{{ combine_group }}" - mode: 0600 - -- name: create backend environment file - template: - src: env.backend.j2 - dest: "{{ combine_app_dir }}/.env.backend" + src: "{{ item }}.j2" + dest: "{{ combine_app_dir }}/.{{ item }}" owner: "{{ combine_user }}" group: "{{ combine_group }}" mode: 0600 + with_items: + - env.frontend + - env.backend + - env.certmgr - name: create frontend runtime configuration file template: diff --git a/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 b/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 index 37371f926b..8847d1107b 100644 --- a/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 +++ b/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 @@ -15,6 +15,7 @@ services: env_file: - ./.env.backend restart: unless-stopped + frontend: {% if combine_pull_images %} image: {{ combine_image_frontend }} @@ -25,14 +26,30 @@ services: - ./.env.frontend volumes: - ./nginx/scripts:/usr/share/nginx/html/scripts:ro - - frontend_ssl_certs:/ssl - letsencrypt:/etc/letsencrypt ports: - 80:80 - 443:443 depends_on: + - certmgr - backend restart: unless-stopped + + certmgr: +{% if combine_pull_images %} + image: {{ combine_image_certmgr }} +{% else %} + build: . +{% endif %} + env_file: + - ./.env.certmgr + volumes: + - letsencrypt:/etc/letsencrypt + ports: + - 80:80 + - 443:443 + restart: unless-stopped + database: image: mongo:4.2 volumes: @@ -43,6 +60,5 @@ services: volumes: backend_data: - frontend_ssl_certs: database_data: letsencrypt: diff --git a/docker_deploy/roles/combine_config/templates/env.certmgr.j2 b/docker_deploy/roles/combine_config/templates/env.certmgr.j2 new file mode 100644 index 0000000000..ade0858edb --- /dev/null +++ b/docker_deploy/roles/combine_config/templates/env.certmgr.j2 @@ -0,0 +1,9 @@ +CERT_PATH=/etc/letsencrypt +CERT_MODE={{ cert_type }} +CERT_EMAIL={{ cert_email }} +CERT_STAGING={{ cert_is_staging }} +CERT_DOMAINS={{ cert_domains|join(", ") }} +CERT_CLEAN={{ cert_clean }} +CERT_VERBOSE={{ cert_verbose }} +CERT_CREATE_ONLY=0 +MAX_CONNECT_TRIES=10 diff --git a/nginx/certs/cert.pem b/nginx/certs/cert.pem deleted file mode 100644 index a408971b6b..0000000000 --- a/nginx/certs/cert.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEpDCCAowCCQDuEu0OV4yNWjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwHhcNMjAwNjEyMTc0NjMyWhcNMjEwNjEyMTc0NjMyWjAUMRIwEAYD -VQQDDAlsb2NhbGhvc3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCi -eJAxGYTvZpbV5k4Gj4fY2K7UZ3DI0hybbWHQsMsD20QhEpLaCQYtK03uGs+4lMFl -InSth2jLt7ERajVK83ERTdZk5Ecuom6VL6Nwj48nWV9TGIrfBhxTS1s61TStT6eY -roRSnoEf+/fM/BYipZrWC47KIZNLv9UEF3o7u5rHO4iU4Pz+RazoG3bnbdiji6UU -Z30uBwLV2I1h7c1oPJW0VE64ODtnVHcjHwk0p//UvP2Ub1FHfnzRBhHAq7JcCgdL -5zj59tutCidEtfgtZNGsEKyoxORX130rMkGEjO5NSwqfyhjDVNVVyvVAKRCBLQzv -icXKrz/+w6dSiABL5YyIvHP9VoeV1i738yv4SjcZFcSriUwh+uD9m+QoUaqAU0n6 -uuN7nmGhByvvFHasCdaiiAKW1XWYXy8W/Q3r4d1VDFHZymsV83r9bwfPUyMd8Yfo -8fbfSNataotfoQ1kDVuRR6TqayGA3+Aw2zvdADDuBHGJCUAF+yIioNPRhmzLQ8tI -q/mSYbSVdLUogjySk0PFhKG/n+66T6vrPhTAW3+EeFwKFmkTnko6ImaGzJS8WjBz -WWpARgW7fxtS36gM7SJMkcMsh1aiBRHkgdZ9/Ds584QCk7tXQXE3l7Yy5CnpBppt -uBqsqdyuUFlGU5GcsLgqTHnnXv7x9c1dXsJt42m7GwIDAQABMA0GCSqGSIb3DQEB -CwUAA4ICAQBGq4vSbMYVuaTR+ONDtFIunGnbAmGYhQOArJOx0JRUEjDtGOou7zxO -33N5PbE9cpN0vlspZlUxuXJtBDSPlxVIsh6zNfLB/QnLUL/p8lP/VQxoWbnN8qDm -QgFdOdJIwAjeiKTkUzBfY+em9SlL5FZdbOxhmNFjDLXXfSe7Kz5UcEQqjvPnZufk -JNqFHJ0SFuc7GMhqqJJn/PgzmupBmVfUxgBCYDbylRT5rZFYX7EZnwuDeEB4Vo2I -LvX3HJbfmAdh6CNzMEUsijiyMbB9ogS1v+c356K5SWP/0Li8N/AWeajE+/JaYUGc -sAfgeWTX8ADFMozMhN58kTD3pIvQ3EuCF28EuBLdTk6fpyQzhajqZkhqSnz7nwpF -6kXHII2nES2gpKd4Qu/A3SvPO6BayA/KPf+GjJyNlfGRASXZex2YGTUr3o+tYi9B -FLVa2gViL6uCK5BJsBRQU0K3u7kxxytFDZLMfCjAU54MyssnGHRiGtQz4rBDp943 -KV7i/j4OfIFNugr3yW/GdIdHKdf8NJbj/sS1I+KLskddbilSsUa0fvFV9D9Vio88 -rmDRlFN5qwRXh1AWVyf+KEcjw5PnCmB/ZReRppMlJW8dNqv9yZdKwMDHeiNeVLXC -KvWrCxH0vk2S+VdxiGbwSbvSn/mK0ZlgWTDEBdyQ66P1E/FDOelhZg== ------END CERTIFICATE----- diff --git a/nginx/certs/key.pem b/nginx/certs/key.pem deleted file mode 100644 index 7314716e0e..0000000000 --- a/nginx/certs/key.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQCieJAxGYTvZpbV -5k4Gj4fY2K7UZ3DI0hybbWHQsMsD20QhEpLaCQYtK03uGs+4lMFlInSth2jLt7ER -ajVK83ERTdZk5Ecuom6VL6Nwj48nWV9TGIrfBhxTS1s61TStT6eYroRSnoEf+/fM -/BYipZrWC47KIZNLv9UEF3o7u5rHO4iU4Pz+RazoG3bnbdiji6UUZ30uBwLV2I1h -7c1oPJW0VE64ODtnVHcjHwk0p//UvP2Ub1FHfnzRBhHAq7JcCgdL5zj59tutCidE -tfgtZNGsEKyoxORX130rMkGEjO5NSwqfyhjDVNVVyvVAKRCBLQzvicXKrz/+w6dS -iABL5YyIvHP9VoeV1i738yv4SjcZFcSriUwh+uD9m+QoUaqAU0n6uuN7nmGhByvv -FHasCdaiiAKW1XWYXy8W/Q3r4d1VDFHZymsV83r9bwfPUyMd8Yfo8fbfSNataotf -oQ1kDVuRR6TqayGA3+Aw2zvdADDuBHGJCUAF+yIioNPRhmzLQ8tIq/mSYbSVdLUo -gjySk0PFhKG/n+66T6vrPhTAW3+EeFwKFmkTnko6ImaGzJS8WjBzWWpARgW7fxtS -36gM7SJMkcMsh1aiBRHkgdZ9/Ds584QCk7tXQXE3l7Yy5CnpBpptuBqsqdyuUFlG -U5GcsLgqTHnnXv7x9c1dXsJt42m7GwIDAQABAoICADaXdjPZVE6w1Pv4j70qm0Xk -wRID0+TWnOAo59YcoYqhMYo/WHLE13tz8pKQFb361eTqp/Pq7O6OwYONgWy20gYz -dleeUMC9uZSNVkirJDnfkOU1+OupfPkL+BKTs0iM3E60TSgJtG+P1ZreFX380UMc -zRTkJT8trSDb1qmdhwts2J7V3ox90vSGzfRDd6i0cnqhr3E4z94VDqSaKAvfV5op -Pe4ujt9QhVuawRgdXxwXiMlIgjIYsU320R5BUwQUJpF9lyp+pR/Gqp0u2749Ayri -y3FF/0f400khcNxeEnCMR3ybhqiTp79VHAU8Bxywi4ktbPWG32T2TxGvOxJFhmue -d0aAZruPRUL6IJZx0eprBN7DwbB+s7r6obVca67F+z0T5ekb3rVrWXqS/samTfLW -6ZzRr6MSaHl5ffiDdmoiK3nsvaTegqTh+ZQaUEvuBgheLNpXAAV9fWzTdXzklYri -yvOstwn1v+Jz+g29S9euspB+scNTAhWLlcO6+SqApBCItN0/VK646swtJyIKnYzh -g1pIPShLdOhsy6RcIAU5JswAGDr4O+bg3K597kvKdxox1SANbhbfIvUnDDwQFk1U -yQi3eUwyqChKwJyCl4RavSAae0hY2EIuvWWDfDPHgScE151uWgwoIR/oL9QYx6yA -nNuz4u/xI5dfkesY4vE5AoIBAQDQyVGJFrkvjg+lzjbTy8S50MlQ794+TD7n748s -zr9unWSx8v2mgk1+zhA/T14FcQL2qFxvoSqtfjB9ukEbWjVed+KIbV5fMAGicQXg -nvmddJGVTrC/Jqs4FH4B5wEYaOEIlUGjsAxki7rc55pB4HFrBsNSi29dtjtliUkO -ttcuVHCDqm0x29/+fv6ehPZS8yT60J3MScXuZL5HyMP60FT//anUM16IgZGeXvmn -w+jW0e7DKyZwRnpJUinp3dRKxX20QF36D3qBiztb+m9/obDxxAwgo5x2nhN5QGLn -6dGu/D9tYqG25PakIjep9OovE4JorAL/Iln7aJts+feNHemPAoIBAQDHNgkBiuco -/BfYU/7W2L5n14s1mZW/k+ZmJrQFJOi2tS0sZYkA0HdBpsAII0xUoKqXFBWZr0/U -CY5X5n6AWsP44qwNKMUe8/+vutANPmwfUg4V4TiSeNdLQBxsctGPDCdpvUnxq2My -i8KMJsk1dHqR+VqeZNyu3cQsdCXagOPkYDJBhQ1aU3iVu7avByXuo/xybJZr2U8A -4QOgR9tpP2qDTsvf/zIcBDaUSWeEb551TcsJag6/5szljNhB6F1ABSkKnN1nLokt -nHVtmalhIQFKXgiFEo7zsTHrDuQZM1jIx27M8BLI785u1JRHnTqPvFVM9lUtaYdd -XWqFRPkE3Fe1AoIBAFPiftLggVjc7+aDGr4voYOyoh/Vr+pBwgh31IOBMaKktB9W -7Jhow/ANpLN7FOCEzlcBv9nN3BLGn370RVJTBRxGeIqtvnvqEs4MMPtVhB7CBfgC -HLtAMwyeFx52jJcdFETZivzUL7Z5+YLlvrF3FrqUhgd1JDJ3lBewd9zkk0H3BRmx -cJvWpoFezrgr3bfoHtRy2uOIRHEKovS+chckevI7HW+3j+SygioqKI5CBRvCTva1 -MIVtm5Y338UprjC4dk/kiJ1+eI7xvNCwpvmVoj0QsnquoZ961YIi31okNG3kxSsS -tLyB86JwaQLs9uYhJYUejzmouJRtK/011UMqYHsCggEAbHVl59oZSr4duWDZ0V1G -JUE8AJ2y4xACE8Gkp1xV+wufIZnSQog9NZg02vlk2Hp1BjYgIIFLRBVD02Whn/5K -zhmTNMLqt5R+s81Q8rT2pwLEunLZmx2Bke3Ay+ajY29pke9F7/aw1lczP5zqVVkq -VoldJfmiEO6anN1LsrAT40Xgdr2a7H+fml4N8zNAGrJbgmzOZL92fPMFR2F3+tBV -DFJ9O2I0hP5iPM/ONVOUASeukFyW1TIyghPxtcht5KoqkyWegUvb0eJqKJbNmh0f -F78kglb9T348wE5mL7Y982ppT9ixUYcGbF7+sBLRTGpsQq+m5kebNGDk2tQXg+b3 -NQKCAQAjeBWFYRiwflh5c4cg2ZZOANggs7ZgqT6mBlxfXhwmQL3gvLms1WlEiutL -0kjNL9V2Qbw00eBf5RrJ9Oby4E7mi0NVy1FYqaUis14ZymrbfLhroSmo1ELY6D1G -zKCrqXKOVrprgS6WV82/8Q84mAV1qUMxGj9wtEKzA41blu4KHlQ55luuuRaOfhkd -5mwzkwLyO2tTkkcRsp6E2jrK3lBG9nikgDYFR+ANo51omFdDzRgC7aqsieMX8P5P -JxvbeqWfVg0EAydHh4i/6NqvmG7uyaGCs+k8ktgDz3+rVyK3F+vGYdJbJhuDyK3I -IolxcIBBifVrTKoBKLOQZESgoB8p ------END PRIVATE KEY----- diff --git a/nginx/conf.d/thecombine.conf b/nginx/templates/thecombine.template similarity index 64% rename from nginx/conf.d/thecombine.conf rename to nginx/templates/thecombine.template index 39698a61c4..e07c3ccc7d 100644 --- a/nginx/conf.d/thecombine.conf +++ b/nginx/templates/thecombine.template @@ -1,15 +1,16 @@ -############################################################## -# -# N O T E : -# -# This file only includes the configuration for the https -# connections. -# The configuration for http connections is created by the -# base image for the container. It will redirect requests -# for /.well-known/acme-challenge to certbot to service -# creation and renewal of certificates from letsencrypt. -# All other http requests are redirected to https. -############################################################## +server { + listen 80; + server_name ${SERVER_NAME}; + server_tokens off; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} server { listen 443 ssl; From be638e4f4a6a1996d24becf4c9f5483d76f2d2dc Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 9 Oct 2020 15:15:27 -0400 Subject: [PATCH 02/29] Split frontend into nginx and certbot containers --- docker_deploy/group_vars/qa/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker_deploy/group_vars/qa/main.yml b/docker_deploy/group_vars/qa/main.yml index 6a707686c3..06975f46f8 100644 --- a/docker_deploy/group_vars/qa/main.yml +++ b/docker_deploy/group_vars/qa/main.yml @@ -23,11 +23,11 @@ combine_image_certmgr: "{{ aws_ecr }}/combine/certmgr:{{ image_tag }}" # for both Let's Encrypt and Self-signed certs cert_type: self-signed cert_email: jimgrady.jg@gmail.com -cert_is_staging: 1 +cert_is_staging: 0 cert_domains: - "{{ combine_server_name }}" cert_clean: 0 -cert_verbose: 1 +cert_verbose: 0 # setup variables for roles/aws_access aws_user: "{{ combine_user }}" From 5b1841f3e7755da54152b2893fc10224caf6e1b7 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Sun, 11 Oct 2020 09:32:52 -0400 Subject: [PATCH 03/29] Create templates directory for nginx --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f2422cf55e..246e0806ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,10 +13,12 @@ RUN npm run build # Production environment. FROM nginx:1.18 +RUN mkdir /etc/nginx/templates + WORKDIR /app ENV NGINX_HOST_DIR /usr/share/nginx/html COPY --from=builder /app/build ${NGINX_HOST_DIR} -COPY nginx/templates/* /etc/nginx/templates +COPY nginx/templates/* /etc/nginx/templates/ From 9c7003b28c7ac77c33229f99bf668955b9640d95 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Sun, 11 Oct 2020 09:54:28 -0400 Subject: [PATCH 04/29] Fix SSL cert location for QA hosts --- docker_deploy/group_vars/qa/main.yml | 4 ++-- .../{thecombine.template => thecombine.conf.template} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename nginx/templates/{thecombine.template => thecombine.conf.template} (100%) diff --git a/docker_deploy/group_vars/qa/main.yml b/docker_deploy/group_vars/qa/main.yml index 06975f46f8..e87ff49712 100644 --- a/docker_deploy/group_vars/qa/main.yml +++ b/docker_deploy/group_vars/qa/main.yml @@ -5,8 +5,8 @@ # Group: qa ################################################# -ssl_certificate: "/ssl/cert.pem" -ssl_private_key: "/ssl/key.pem" +ssl_certificate: "/etc/letsencrypt/live/{{ combine_server_name }}/fullchain.pem" +ssl_private_key: "/etc/letsencrypt/live/{{ combine_server_name }}/privkey.pem" certbot_email: "" diff --git a/nginx/templates/thecombine.template b/nginx/templates/thecombine.conf.template similarity index 100% rename from nginx/templates/thecombine.template rename to nginx/templates/thecombine.conf.template From 6478c157631283de627209f947250048a6346f89 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Sun, 11 Oct 2020 13:22:42 -0400 Subject: [PATCH 05/29] Fix duplicate port definition --- .../roles/combine_config/templates/docker-compose.yml.j2 | 3 --- 1 file changed, 3 deletions(-) diff --git a/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 b/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 index 8847d1107b..34a4ac1c2f 100644 --- a/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 +++ b/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 @@ -45,9 +45,6 @@ services: - ./.env.certmgr volumes: - letsencrypt:/etc/letsencrypt - ports: - - 80:80 - - 443:443 restart: unless-stopped database: From 8da54c3c4fdb85916f5d1898874ad59168d03b8d Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Sun, 11 Oct 2020 13:23:46 -0400 Subject: [PATCH 06/29] Fix build context for certmgr --- .../roles/combine_config/templates/docker-compose.yml.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 b/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 index 34a4ac1c2f..525eafea17 100644 --- a/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 +++ b/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 @@ -39,7 +39,7 @@ services: {% if combine_pull_images %} image: {{ combine_image_certmgr }} {% else %} - build: . + build: ./certmgr {% endif %} env_file: - ./.env.certmgr From f3efe924d17996d9722c1b6ff749cfbf6f14c681 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Sun, 11 Oct 2020 15:26:56 -0400 Subject: [PATCH 07/29] Set self-signed key size to 4096 bits --- certmgr/scripts/func.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certmgr/scripts/func.sh b/certmgr/scripts/func.sh index d500c7eb7b..9a4ee2adf1 100755 --- a/certmgr/scripts/func.sh +++ b/certmgr/scripts/func.sh @@ -43,7 +43,7 @@ create_selfsigned_cert() { mkdir -p ${DEST_DIR} cd ${DEST_DIR} - openssl req -x509 -nodes -newkey rsa:1024 -days ${CERT_SELF_SIGNED_EXPIRE} -keyout privkey.pem -out fullchain.pem -subj '/CN=localhost' + openssl req -x509 -nodes -newkey rsa:4096 -days ${CERT_SELF_SIGNED_EXPIRE} -keyout privkey.pem -out fullchain.pem -subj '/CN=localhost' if [ "$CERT_VERBOSE" = "1" ] ; then echo "Created certificate in ${CERT_PATH} for ${cert_domains}" echo "Expires: "`openssl x509 -in fullchain.pem -noout -enddate` From b536d9acbf52d6f6d0aec47e3a89840527a8e2f9 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Sun, 11 Oct 2020 15:44:47 -0400 Subject: [PATCH 08/29] Fix default self-signed cert expiration --- certmgr/scripts/func.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certmgr/scripts/func.sh b/certmgr/scripts/func.sh index 9a4ee2adf1..939396deb0 100755 --- a/certmgr/scripts/func.sh +++ b/certmgr/scripts/func.sh @@ -13,8 +13,8 @@ init_vars() { CERT_STAGING=${CERT_STAGING:=1} MAX_CONNECT_TRIES=${MAX_CONNECT_TRIES:=15} if [ "${CERT_MODE}" = "self-signed" ] ; then - CERT_SELF_SIGNED_EXPIRE=1 - CERT_MIN_DAYS_TO_EXPIRE=30 + CERT_SELF_SIGNED_EXPIRE=3650 + CERT_MIN_DAYS_TO_EXPIRE=10 else CERT_SELF_SIGNED_EXPIRE=1 CERT_MIN_DAYS_TO_EXPIRE=1 From c939102ba6d41ffe20a60990e5494091d801e571 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Mon, 12 Oct 2020 09:26:13 -0400 Subject: [PATCH 09/29] Add CERT_NAME environment variable Add CERT_NAME environment variable to specify the folder where the certificate should be stored. Defaults to the first domain in the list of domains. --- certmgr/scripts/func.sh | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/certmgr/scripts/func.sh b/certmgr/scripts/func.sh index 939396deb0..493393c795 100755 --- a/certmgr/scripts/func.sh +++ b/certmgr/scripts/func.sh @@ -21,8 +21,10 @@ init_vars() { fi # create $cert_domains as an array of domain names IFS=" " read -r -a cert_domains <<< "${CERT_DOMAINS}" + CERT_NAME=${CERT_NAME:="$cert_domains"} if [ "$CERT_VERBOSE" = "1" ] ; then echo "Certificates stored in ${CERT_PATH}" + echo "Certificate name: ${CERT_NAME}" echo "Domains: ${cert_domains[*]}" echo "Certificates expire in ${CERT_SELF_SIGNED_EXPIRE}" echo "Minimum days before renewal: ${CERT_MIN_DAYS_TO_EXPIRE}" @@ -31,15 +33,15 @@ init_vars() { clean_certs() { if [ "$CERT_VERBOSE" = "1" ] ; then - echo "Removing certificates in ${CERT_PATH} for ${cert_domains}" + echo "Removing certificates in ${CERT_PATH} for ${CERT_NAME}" fi - rm -rf "${CERT_PATH}/live/${cert_domains}" - rm -rf "${CERT_PATH}/archive/${cert_domains}" - rm -f "${CERT_PATH}/renewal/${cert_domains}.conf" + rm -rf "${CERT_PATH}/live/${CERT_NAME}" + rm -rf "${CERT_PATH}/archive/${CERT_NAME}" + rm -f "${CERT_PATH}/renewal/${CERT_NAME}.conf" } create_selfsigned_cert() { - DEST_DIR="${CERT_PATH}/live/${cert_domains}" + DEST_DIR="${CERT_PATH}/live/${CERT_NAME}" mkdir -p ${DEST_DIR} cd ${DEST_DIR} @@ -82,11 +84,11 @@ wait_for_webserver() { check_renewal() { if [ "$CERT_VERBOSE" = "1" ] ; then - echo "Checking for renewal of ${CERT_PATH}/live/${cert_domains}/fullchain.pem" + echo "Checking for renewal of ${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" fi CERT_MIN_SEC_TO_EXPIRE=$(( ${CERT_MIN_DAYS_TO_EXPIRE} * 3600 * 24 )) - openssl x509 -noout -in "${CERT_PATH}/live/${cert_domains}/fullchain.pem" -checkend ${CERT_MIN_SEC_TO_EXPIRE} > /dev/null + openssl x509 -noout -in "${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" -checkend ${CERT_MIN_SEC_TO_EXPIRE} > /dev/null if [ "$?" = "1" ] ; then echo "Renewing the certificate for ${cert_domains}" create_selfsigned_cert From ba9c8420d4dd2de8f7065c023a92186b7da49d34 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Mon, 12 Oct 2020 09:29:04 -0400 Subject: [PATCH 10/29] Move functions for self-signed certs to selfsigned.sh --- certmgr/scripts/func.sh | 26 -------------------------- certmgr/scripts/selfsigned.sh | 29 ++++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/certmgr/scripts/func.sh b/certmgr/scripts/func.sh index 493393c795..f7a4cc24fa 100755 --- a/certmgr/scripts/func.sh +++ b/certmgr/scripts/func.sh @@ -40,18 +40,6 @@ clean_certs() { rm -f "${CERT_PATH}/renewal/${CERT_NAME}.conf" } -create_selfsigned_cert() { - DEST_DIR="${CERT_PATH}/live/${CERT_NAME}" - mkdir -p ${DEST_DIR} - - cd ${DEST_DIR} - openssl req -x509 -nodes -newkey rsa:4096 -days ${CERT_SELF_SIGNED_EXPIRE} -keyout privkey.pem -out fullchain.pem -subj '/CN=localhost' - if [ "$CERT_VERBOSE" = "1" ] ; then - echo "Created certificate in ${CERT_PATH} for ${cert_domains}" - echo "Expires: "`openssl x509 -in fullchain.pem -noout -enddate` - fi -} - create_certbot_cert() { if [ "$CERT_VERBOSE" = "1" ] ; then echo "create certbot certificate" @@ -80,17 +68,3 @@ wait_for_webserver() { echo "Failed to connect to ${cert_domains}" >2 return 1; } - -check_renewal() -{ - if [ "$CERT_VERBOSE" = "1" ] ; then - echo "Checking for renewal of ${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" - fi - - CERT_MIN_SEC_TO_EXPIRE=$(( ${CERT_MIN_DAYS_TO_EXPIRE} * 3600 * 24 )) - openssl x509 -noout -in "${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" -checkend ${CERT_MIN_SEC_TO_EXPIRE} > /dev/null - if [ "$?" = "1" ] ; then - echo "Renewing the certificate for ${cert_domains}" - create_selfsigned_cert - fi -} diff --git a/certmgr/scripts/selfsigned.sh b/certmgr/scripts/selfsigned.sh index e183a79a6b..34bd09cb24 100755 --- a/certmgr/scripts/selfsigned.sh +++ b/certmgr/scripts/selfsigned.sh @@ -1,5 +1,32 @@ #! /bin/bash +create_selfsigned_cert() { + DEST_DIR="${CERT_PATH}/live/${CERT_NAME}" + mkdir -p ${DEST_DIR} + + cd ${DEST_DIR} + openssl req -x509 -nodes -newkey rsa:4096 -days ${CERT_SELF_SIGNED_EXPIRE} -keyout privkey.pem -out fullchain.pem -subj '/CN=localhost' + if [ "$CERT_VERBOSE" = "1" ] ; then + echo "Created certificate in ${CERT_PATH} for ${cert_domains}" + echo "Expires: "`openssl x509 -in fullchain.pem -noout -enddate` + fi +} + + +renew_selfsigned_cert() +{ + if [ "$CERT_VERBOSE" = "1" ] ; then + echo "Checking for renewal of ${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" + fi + + CERT_MIN_SEC_TO_EXPIRE=$(( ${CERT_MIN_DAYS_TO_EXPIRE} * 3600 * 24 )) + openssl x509 -noout -in "${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" -checkend ${CERT_MIN_SEC_TO_EXPIRE} > /dev/null + if [ "$?" = "1" ] ; then + echo "Renewing the certificate for ${cert_domains}" + create_selfsigned_cert + fi +} + # Source in util.sh so we can have our nice tools . $(cd $(dirname $0); pwd)/func.sh @@ -20,7 +47,7 @@ fi if [ "$CERT_CREATE_ONLY" = "0" ] ; then while true; do - check_renewal + renew_selfsigned_cert sleep 60 & wait $! From 28961566ee91b0afb0132680f852c585bdae7a3f Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Mon, 12 Oct 2020 09:58:37 -0400 Subject: [PATCH 11/29] Prevent false error message When renewing certificate that does not exist, an error is generated when trying to check the expiration. Change to just create the missing cert. --- certmgr/scripts/selfsigned.sh | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/certmgr/scripts/selfsigned.sh b/certmgr/scripts/selfsigned.sh index 34bd09cb24..8cbaca6b37 100755 --- a/certmgr/scripts/selfsigned.sh +++ b/certmgr/scripts/selfsigned.sh @@ -15,14 +15,20 @@ create_selfsigned_cert() { renew_selfsigned_cert() { + CERT_FILE="${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" if [ "$CERT_VERBOSE" = "1" ] ; then - echo "Checking for renewal of ${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" + echo "Checking for renewal of ${CERT_FILE}" fi CERT_MIN_SEC_TO_EXPIRE=$(( ${CERT_MIN_DAYS_TO_EXPIRE} * 3600 * 24 )) - openssl x509 -noout -in "${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" -checkend ${CERT_MIN_SEC_TO_EXPIRE} > /dev/null - if [ "$?" = "1" ] ; then - echo "Renewing the certificate for ${cert_domains}" + if [ -f ${CERT_FILE} ] ; then + openssl x509 -noout -in "${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" -checkend ${CERT_MIN_SEC_TO_EXPIRE} > /dev/null + if [ "$?" = "1" ] ; then + echo "Renewing the certificate for ${cert_domains}" + create_selfsigned_cert + fi + else + echo "Restoring the certificate for ${cert_domains}" create_selfsigned_cert fi } @@ -35,13 +41,11 @@ CERT_MODE="self-signed" # Container initialization init_vars -if [ ${CERT_CLEAN} = "1" ] ; then - if [ -d "${CERT_PATH}/live/${cert_domains}" ]; then +if [ ${CERT_CLEAN} = "1" && -d "${CERT_PATH}/live/${CERT_NAME}" ]; then clean_certs - fi fi -if [ -d "${CERT_PATH}/live/${cert_domains}" ]; then +if [ ! -d "${CERT_PATH}/live/${CERT_NAME}" ]; then create_selfsigned_cert fi From 03301307f95a8ed09663bc895319ae3a58b7f570 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Mon, 12 Oct 2020 10:30:10 -0400 Subject: [PATCH 12/29] Add default variables for combine_config role --- certmgr/scripts/selfsigned.sh | 2 +- docker_deploy/roles/combine_config/defaults/main.yml | 8 +++++++- .../roles/combine_config/templates/env.frontend.j2 | 2 +- scripts/docker_setup.py | 2 +- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/certmgr/scripts/selfsigned.sh b/certmgr/scripts/selfsigned.sh index 8cbaca6b37..903659f535 100755 --- a/certmgr/scripts/selfsigned.sh +++ b/certmgr/scripts/selfsigned.sh @@ -42,7 +42,7 @@ CERT_MODE="self-signed" init_vars if [ ${CERT_CLEAN} = "1" && -d "${CERT_PATH}/live/${CERT_NAME}" ]; then - clean_certs + clean_certs fi if [ ! -d "${CERT_PATH}/live/${CERT_NAME}" ]; then diff --git a/docker_deploy/roles/combine_config/defaults/main.yml b/docker_deploy/roles/combine_config/defaults/main.yml index 8e1d8f216a..cf9747ca4a 100644 --- a/docker_deploy/roles/combine_config/defaults/main.yml +++ b/docker_deploy/roles/combine_config/defaults/main.yml @@ -9,7 +9,13 @@ config_captcha_sitekey: "6Le6BL0UAAAAAMjSs1nINeB5hqDZ4m3mMg3k67x3" # for non-development deployments combine_env_vars: -certbot_is_staging: 0 +cert_type: self-signed +cert_email: jimgrady.jg@gmail.com +cert_is_staging: 0 +cert_domains: + - "{{ combine_server_name }}" +cert_clean: 0 +cert_verbose: 0 combine_pull_images: true combine_image_backend: combine/backend diff --git a/docker_deploy/roles/combine_config/templates/env.frontend.j2 b/docker_deploy/roles/combine_config/templates/env.frontend.j2 index c890ceb7aa..a70fb2877a 100644 --- a/docker_deploy/roles/combine_config/templates/env.frontend.j2 +++ b/docker_deploy/roles/combine_config/templates/env.frontend.j2 @@ -1,5 +1,5 @@ CERTBOT_EMAIL={{ certbot_email }} -IS_STAGING={{ certbot_is_staging }} +IS_STAGING={{ cert_is_staging }} ENVSUBST_VARS=SERVER_NAME SSL_CERTIFICATE SSL_PRIVATE_KEY SERVER_NAME={{ combine_server_name }} SSL_CERTIFICATE={{ ssl_certificate }} diff --git a/scripts/docker_setup.py b/scripts/docker_setup.py index 09543a45b5..03185079fb 100755 --- a/scripts/docker_setup.py +++ b/scripts/docker_setup.py @@ -58,7 +58,7 @@ def main() -> None: "combine_image_frontend": "combine/frontend:latest", "combine_image_backend": "combine/backend:latest", "certbot_email": "", - "certbot_is_staging": 0, + "cert_is_staging": 0, "combine_server_name": "localhost", "ssl_certificate": "/ssl/cert.pem", "ssl_private_key": "/ssl/key.pem", From 2994baabc927058833a475b7625aed33a6b428c4 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Mon, 12 Oct 2020 10:49:06 -0400 Subject: [PATCH 13/29] Fix compound if statement --- certmgr/scripts/selfsigned.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certmgr/scripts/selfsigned.sh b/certmgr/scripts/selfsigned.sh index 903659f535..5b7de59b37 100755 --- a/certmgr/scripts/selfsigned.sh +++ b/certmgr/scripts/selfsigned.sh @@ -41,7 +41,7 @@ CERT_MODE="self-signed" # Container initialization init_vars -if [ ${CERT_CLEAN} = "1" && -d "${CERT_PATH}/live/${CERT_NAME}" ]; then +if [ ${CERT_CLEAN} = "1" ] && [ -d "${CERT_PATH}/live/${CERT_NAME}" ]; then clean_certs fi From 6d0d3da431dadba20832fc45e1bea0c7756856b2 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Mon, 12 Oct 2020 15:02:01 -0400 Subject: [PATCH 14/29] Update docker_setup.py for certmgr files --- .gitignore | 1 + docker_deploy/group_vars/qa/main.yml | 2 +- docker_deploy/group_vars/server/main.yml | 2 +- .../roles/combine_config/templates/env.frontend.j2 | 3 +-- scripts/docker_setup.py | 12 +++++++++--- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 5498a1170c..f9d04bd7d3 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ docker-compose.yml .env.backend .env.frontend +.env.certmgr .env*.local nginx/scripts diff --git a/docker_deploy/group_vars/qa/main.yml b/docker_deploy/group_vars/qa/main.yml index e87ff49712..988dd28ada 100644 --- a/docker_deploy/group_vars/qa/main.yml +++ b/docker_deploy/group_vars/qa/main.yml @@ -8,7 +8,7 @@ ssl_certificate: "/etc/letsencrypt/live/{{ combine_server_name }}/fullchain.pem" ssl_private_key: "/etc/letsencrypt/live/{{ combine_server_name }}/privkey.pem" -certbot_email: "" +cert_email: "" # TheCombine is installed on the QA Server by TeamCity. It will set the # IMAGE_TAG environment variable to major.minor.build_no and launch diff --git a/docker_deploy/group_vars/server/main.yml b/docker_deploy/group_vars/server/main.yml index 895a773af9..97356f68eb 100644 --- a/docker_deploy/group_vars/server/main.yml +++ b/docker_deploy/group_vars/server/main.yml @@ -8,7 +8,7 @@ ssl_certificate: "/etc/letsencrypt/live/{{ combine_server_name }}/fullchain.pem" ssl_private_key: "/etc/letsencrypt/live/{{ combine_server_name }}/privkey.pem" -certbot_email: jimgrady.jg@gmail.com +cert_email: jimgrady.jg@gmail.com # TheCombine is installed on the Live Server by TeamCity. It will set the # IMAGE_TAG environment variable to major.minor.build_no and launch diff --git a/docker_deploy/roles/combine_config/templates/env.frontend.j2 b/docker_deploy/roles/combine_config/templates/env.frontend.j2 index a70fb2877a..874315534a 100644 --- a/docker_deploy/roles/combine_config/templates/env.frontend.j2 +++ b/docker_deploy/roles/combine_config/templates/env.frontend.j2 @@ -1,6 +1,5 @@ -CERTBOT_EMAIL={{ certbot_email }} +CERTBOT_EMAIL={{ cert_email }} IS_STAGING={{ cert_is_staging }} -ENVSUBST_VARS=SERVER_NAME SSL_CERTIFICATE SSL_PRIVATE_KEY SERVER_NAME={{ combine_server_name }} SSL_CERTIFICATE={{ ssl_certificate }} SSL_PRIVATE_KEY={{ ssl_private_key }} diff --git a/scripts/docker_setup.py b/scripts/docker_setup.py index 03185079fb..deed40181e 100755 --- a/scripts/docker_setup.py +++ b/scripts/docker_setup.py @@ -57,11 +57,16 @@ def main() -> None: # instructions. "combine_image_frontend": "combine/frontend:latest", "combine_image_backend": "combine/backend:latest", - "certbot_email": "", + "combine_image_certmgr": "combine/certmgr:latest", + "cert_email": "", + "cert_type": "self-signed", "cert_is_staging": 0, + "cert_domains": ["localhost"], + "cert_clean": 0, + "cert_verbose": 1, "combine_server_name": "localhost", - "ssl_certificate": "/ssl/cert.pem", - "ssl_private_key": "/ssl/key.pem", + "ssl_certificate": "/etc/letsencrypt/live/localhost/fullchain.pem", + "ssl_private_key": "/etc/letsencrypt/live/localhost/privkey.pem", "combine_env_vars": "", "combine_private_env_vars": [ { @@ -85,6 +90,7 @@ def main() -> None: "docker-compose.yml.j2": project_dir / "docker-compose.yml", "env.frontend.j2": project_dir / ".env.frontend", "env.backend.j2": project_dir / ".env.backend", + "env.certmgr.j2": project_dir / ".env.certmgr", "config.js.j2": project_dir / "nginx" / "scripts" / "config.js", } From 9fe52553838c75e0d6c685b9f3141387f52cc4b2 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Mon, 12 Oct 2020 16:18:26 -0400 Subject: [PATCH 15/29] Update environment variables for server --- certmgr/scripts/letsencrypt.sh | 2 -- docker_deploy/group_vars/qa/main.yml | 4 +--- docker_deploy/group_vars/server/main.yml | 14 +++++++++++--- docker_deploy/roles/combine_config/tasks/main.yml | 1 - 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/certmgr/scripts/letsencrypt.sh b/certmgr/scripts/letsencrypt.sh index be88743fe7..7ea038b35d 100755 --- a/certmgr/scripts/letsencrypt.sh +++ b/certmgr/scripts/letsencrypt.sh @@ -3,8 +3,6 @@ # Source in util.sh so we can have our nice tools . $(cd $(dirname $0); pwd)/func.sh -CERT_MODE="self-signed" - # Container initialization init_vars diff --git a/docker_deploy/group_vars/qa/main.yml b/docker_deploy/group_vars/qa/main.yml index 988dd28ada..4d9f72d091 100644 --- a/docker_deploy/group_vars/qa/main.yml +++ b/docker_deploy/group_vars/qa/main.yml @@ -8,8 +8,6 @@ ssl_certificate: "/etc/letsencrypt/live/{{ combine_server_name }}/fullchain.pem" ssl_private_key: "/etc/letsencrypt/live/{{ combine_server_name }}/privkey.pem" -cert_email: "" - # TheCombine is installed on the QA Server by TeamCity. It will set the # IMAGE_TAG environment variable to major.minor.build_no and launch # Docker compose @@ -22,7 +20,7 @@ combine_image_certmgr: "{{ aws_ecr }}/combine/certmgr:{{ image_tag }}" # SSL Certificate variables # for both Let's Encrypt and Self-signed certs cert_type: self-signed -cert_email: jimgrady.jg@gmail.com +cert_email: "" cert_is_staging: 0 cert_domains: - "{{ combine_server_name }}" diff --git a/docker_deploy/group_vars/server/main.yml b/docker_deploy/group_vars/server/main.yml index 97356f68eb..c8c06eab40 100644 --- a/docker_deploy/group_vars/server/main.yml +++ b/docker_deploy/group_vars/server/main.yml @@ -8,8 +8,6 @@ ssl_certificate: "/etc/letsencrypt/live/{{ combine_server_name }}/fullchain.pem" ssl_private_key: "/etc/letsencrypt/live/{{ combine_server_name }}/privkey.pem" -cert_email: jimgrady.jg@gmail.com - # TheCombine is installed on the Live Server by TeamCity. It will set the # IMAGE_TAG environment variable to major.minor.build_no and launch # Docker compose @@ -17,8 +15,18 @@ image_tag: "${IMAGE_TAG}" combine_image_backend: "{{ aws_ecr }}/combine/backend:{{ image_tag }}" combine_image_frontend: "{{ aws_ecr }}/combine/frontend:{{ image_tag }}" +combine_image_certmgr: "{{ aws_ecr }}/combine/certmgr:{{ image_tag }}" + +# SSL Certificate variables +# for both Let's Encrypt and Self-signed certs +cert_type: letsencrypt +cert_email: jimgrady.jg@gmail.com +cert_is_staging: 0 +cert_domains: + - "{{ combine_server_name }}" +cert_clean: 0 +cert_verbose: 0 -aws_config_dir: "/home/{{ combine_user }}/.aws" aws_user: "{{ combine_user }}" aws_group: "{{ combine_group }}" diff --git a/docker_deploy/roles/combine_config/tasks/main.yml b/docker_deploy/roles/combine_config/tasks/main.yml index d9fb97ff39..bea1c2230d 100644 --- a/docker_deploy/roles/combine_config/tasks/main.yml +++ b/docker_deploy/roles/combine_config/tasks/main.yml @@ -19,7 +19,6 @@ - "{{ combine_app_dir }}" - "{{ combine_app_dir }}/uploads" - "{{ combine_app_dir }}/nginx/scripts" - - "{{ combine_app_dir }}/nginx/conf.d" - name: install docker-compose.yml file template: From 856f3d91fb72902218fe0b1c1dede6df3ec76de6 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Wed, 14 Oct 2020 15:27:02 -0400 Subject: [PATCH 16/29] Refactor to use symbolic link to select self-signed or letsencrypt cert. --- certmgr/Dockerfile | 3 +- certmgr/scripts/README.md | 40 ++++-- certmgr/scripts/func.sh | 134 +++++++++++++----- certmgr/scripts/letsencrypt.sh | 26 ++-- certmgr/scripts/selfsigned.sh | 41 +----- .../roles/the_combine_app/defaults/main.yml | 2 +- .../templates/combine_backend.service.j2 | 2 +- docker_deploy/group_vars/qa/main.yml | 13 +- docker_deploy/group_vars/server/main.yml | 15 +- .../roles/combine_config/defaults/main.yml | 18 +-- .../templates/docker-compose.yml.j2 | 9 +- .../combine_config/templates/env.backend.j2 | 4 +- .../combine_config/templates/env.certmgr.j2 | 12 +- .../combine_config/templates/env.frontend.j2 | 10 +- docker_deploy/vars/config_common.yml | 34 +++++ scripts/docker_setup.py | 29 +++- 16 files changed, 245 insertions(+), 147 deletions(-) diff --git a/certmgr/Dockerfile b/certmgr/Dockerfile index f5fcbf1312..90fd373031 100644 --- a/certmgr/Dockerfile +++ b/certmgr/Dockerfile @@ -4,7 +4,8 @@ VOLUME /etc/letsencrypt RUN apk update && \ apk upgrade && \ - apk add --no-cache bash + apk add --no-cache bash && \ + apk add --no-cache curl COPY scripts/*.sh /usr/bin/ diff --git a/certmgr/scripts/README.md b/certmgr/scripts/README.md index 2c1be6e8d8..4f7745284d 100644 --- a/certmgr/scripts/README.md +++ b/certmgr/scripts/README.md @@ -1,18 +1,38 @@ -# Entrypoint scripts for the certbot container for TheCombine +# Design notes for the certbot container for TheCombine -## self-signed.sh +## Certificate storage + +The certificates are stored in a docker volume. The volume is named cert_store +and is mounted by the `frontend` and the `certmgr` containers. Both containers mount +the volume at `/etc/cert_store`. + +At the top level, there are 3 directories in `/etc/cert_store`: + - `letsencrypt` - stores the certificates created by letsencrypt and managed by `certbot`. + `/etc/letsencrypt` is created as a symbolic link to `/etc/cert_store/letsencrypt` + - `nginx` - The Nginx web server is configured to read its SSL certificates from: + - SSL Certificate: `/etc/cert_store/nginx//fullchain.pem` + - SSL Private Key: `/etc/cert_store/nginx//privkey.pem` + + This is implemented by setting up `/etc/cert_store/nginx/ssl/` as a + symbolic link to the self-signed or letsencrypt certificates. + - `selfsigned` - stores a self-signed certificate for the server in + `/etc/cert_store/selfsigned/` + +## Entrypoint scripts for the certbot container for TheCombine + +### self-signed.sh Creates a self-signed certificate to be used by the nginx web server and exits. #### Environment Variables | Variable Name | Description | | ------------------- | ---------------------------------------------------------------------------- | -| CERT_PATH | Directory for the SSL certificates | +| CERT_STORE | Directory for the SSL certificates | | CERT_CLEAN | Set to 1 to remove old certificates first | | CERT_PRIMARY_DOMAIN | Primary domain for the certificate - used to specify location of certificate | | | | -## letsencrypt.sh +### letsencrypt.sh Uses certbot to create/renew a certificate for the nginx web server. If there are no certificates or if the certificate is for `localhost` then the certificates will be deleted and a certificate will be generated by `letsencrypt`. `letsencrypt` @@ -22,7 +42,7 @@ will then enter a loop to check for certificate renewal every 12 hours. | Variable Name | Description | | ----------------- | ------------------------------------------------ | -| CERT_PATH | Directory for the SSL certificates | +| CERT_STORE | Directory for the SSL certificates | | CERT_STAGING | Set to 1 to generate a testing certificate | | CERT_DOMAINS | List of domains for the certificate | | CERT_KEY_SIZE | Size for the RSA Key (Optional: default is 4096) | @@ -30,18 +50,18 @@ will then enter a loop to check for certificate renewal every 12 hours. | FORCE | Delete any existing certificates first | | EXIT_AFTER_CREATE | Exit after creating the certificates | -## certserver.sh +### certserver.sh *Not Implemented Yet* creates multiple certificates and pushes them to an AWS S3 bucket -## certclient.sh +### certclient.sh *Not Implemented Yet* fetches an SSL certificate from the AWS S3 bucket (when an internet connection is available) -# Installation Requirements +## Installation Requirements The following scenarios list the various requirements for the Ansible playbook that is used to setup the various targets, `playbook_target_setup.yml` and the Python @@ -55,7 +75,7 @@ the appropriate script. The sections below list the steps that must be performed for each of the installation scenarios. -## Self-Signed Certificates +### Self-Signed Certificates When installing TheCombine for using self-signed certificates, e.g. for the QA server or for development use, the following steps are required: 1. Build each container: @@ -67,7 +87,7 @@ or for development use, the following steps are required: 2. for Development mode, run `docker_setup.py` 3. Run `docker-compose up --detach` -## Let's Encrypt Certificates +### Let's Encrypt Certificates When installing TheCombine for using certificates from letsencrypt, e.g. for the Live server, the following steps are required: 1. Build each container: diff --git a/certmgr/scripts/func.sh b/certmgr/scripts/func.sh index f7a4cc24fa..cb44ac2463 100755 --- a/certmgr/scripts/func.sh +++ b/certmgr/scripts/func.sh @@ -6,64 +6,134 @@ init_vars() { # set variables to their default values if they are not # specified - CERT_PATH=${CERT_PATH:="/etc/letsencrypt"} - CERT_CLEAN=${CERT_CLEAN:=0} + CERT_STORE=${CERT_STORE:="/etc/"} + # Folder for certicates as configured by the Nginx webserver CERT_CREATE_ONLY=${CERT_CREATE_ONLY:=0} - CERT_EMAIL=${CERT_EMAIL:="jimgrady.jg@gmail.com"} - CERT_STAGING=${CERT_STAGING:=1} + CERT_EMAIL=${CERT_EMAIL:=""} + CERT_STAGING=${CERT_STAGING:=0} MAX_CONNECT_TRIES=${MAX_CONNECT_TRIES:=15} if [ "${CERT_MODE}" = "self-signed" ] ; then - CERT_SELF_SIGNED_EXPIRE=3650 + SELF_SIGNED_EXPIRE=3650 CERT_MIN_DAYS_TO_EXPIRE=10 else - CERT_SELF_SIGNED_EXPIRE=1 + SELF_SIGNED_EXPIRE=1 CERT_MIN_DAYS_TO_EXPIRE=1 fi # create $cert_domains as an array of domain names IFS=" " read -r -a cert_domains <<< "${CERT_DOMAINS}" - CERT_NAME=${CERT_NAME:="$cert_domains"} - if [ "$CERT_VERBOSE" = "1" ] ; then - echo "Certificates stored in ${CERT_PATH}" - echo "Certificate name: ${CERT_NAME}" - echo "Domains: ${cert_domains[*]}" - echo "Certificates expire in ${CERT_SELF_SIGNED_EXPIRE}" - echo "Minimum days before renewal: ${CERT_MIN_DAYS_TO_EXPIRE}" + debug_log "Certificates stored in ${CERT_STORE}" + debug_log "Certificate name: ${SERVER_NAME}" + debug_log "Domains: ${cert_domains[*]}" + debug_log "Certificates expire in ${SELF_SIGNED_EXPIRE}" + debug_log "Minimum days before renewal: ${CERT_MIN_DAYS_TO_EXPIRE}" + + CERTBOT_DIR="/etc/letsencrypt/live/${SERVER_NAME}" + # Path where the self-signed certificates are stored + SELF_SIGNED_PATH="${CERT_STORE}/selfsigned/${SERVER_NAME}" + NGINX_CERT_PATH="${CERT_STORE}/nginx/${SERVER_NAME}" +} + +init_cert_store() { + # Create nginx, letsencrypt, and selfsigned directories + for subdir in "nginx" "letsencrypt" "selfsigned" ; do + mkdir -p "${CERT_STORE}/${subdir}" + done + + # link /etc/letsencrypt to /etc/cert_store/letsencrypt + ln -s "${CERT_STORE}/letsencrypt" "/etc/letsencrypt" +} + +debug_log() { + if [ "${CERT_VERBOSE}" = "1" ] ; then + echo $* fi } -clean_certs() { - if [ "$CERT_VERBOSE" = "1" ] ; then - echo "Removing certificates in ${CERT_PATH} for ${CERT_NAME}" +update_link() { + src=$1 + target=$2 + + debug_log "linking ${src} to ${target}" + if [ `readlink ${target}` != "${src}" ] ; then + if [ -L "${target}" ]; then + rm "${target}" + debug_log "Old link removed" + fi + ln -s ${src} ${target} + else + debug_log "${target} already points to ${src}" + fi + +} + +create_selfsigned_cert() { + mkdir -p "${SELF_SIGNED_PATH}" + openssl req -x509 -nodes -newkey rsa:4096 -days ${SELF_SIGNED_EXPIRE} -keyout "${SELF_SIGNED_PATH}/privkey.pem" -out "${SELF_SIGNED_PATH}/fullchain.pem" -subj '/CN=localhost' + debug_log "Created certificate in ${CERT_STORE} for ${cert_domains}" + debug_log "Expires: "`openssl x509 -in "${SELF_SIGNED_PATH}/fullchain.pem" -noout -enddate` + + # Update Nginx link + update_link "${SELF_SIGNED_PATH}" "${NGINX_CERT_PATH}" +} + + +renew_selfsigned_cert() +{ + CERT_FILE="${SELF_SIGNED_PATH}/fullchain.pem" + debug_log "Checking for renewal of ${CERT_FILE}" + + CERT_MIN_SEC_TO_EXPIRE=$(( ${CERT_MIN_DAYS_TO_EXPIRE} * 3600 * 24 )) + if [ -f ${CERT_FILE} ] ; then + openssl x509 -noout -in "${CERT_FILE}" -checkend ${CERT_MIN_SEC_TO_EXPIRE} > /dev/null + if [ "$?" = "1" ] ; then + echo "Renewing the certificate for ${cert_domains}" + create_selfsigned_cert + fi + else + echo "Restoring the certificate for ${cert_domains}" + create_selfsigned_cert fi - rm -rf "${CERT_PATH}/live/${CERT_NAME}" - rm -rf "${CERT_PATH}/archive/${CERT_NAME}" - rm -f "${CERT_PATH}/renewal/${CERT_NAME}.conf" } create_certbot_cert() { - if [ "$CERT_VERBOSE" = "1" ] ; then - echo "create certbot certificate" + debug_log "### Requesting Let's Encrypt certificate for ${cert_domains} ..." + + # Select appropriate email arg + if [ -z "${CERT_EMAIL}" ] ; then + email_arg="--register-unsafely-without-email" + else + email_arg="--email '${CERT_EMAIL}'" fi + + # Enable staging mode if needed + if [ ${CERT_STAGING} != "0" ]; then staging_arg="--staging"; fi + + debug_log `pwd` + cert_cmd="certbot certonly --webroot -w /var/www/certbot \ + ${staging_arg} \ + ${email_arg} \ + -d ${CERT_DOMAINS} \ + --rsa-key-size 4096 \ + --agree-tos \ + --force-renewal" + debug_log "$cert_cmd" + $cert_cmd + + update_link "${CERT_STORE}/letsencrypt/live/${SERVER_NAME}" "${NGINX_CERT_PATH}" } wait_for_webserver() { - if [ "$CERT_VERBOSE" = "1" ] ; then - echo "Attempting connection to ${cert_domains}" - echo "Max attempts = ${MAX_CONNECT_TRIES}" - fi + debug_log "Attempting connection to ${cert_domains}" + debug_log "Max attempts = ${MAX_CONNECT_TRIES}" count=0; while [ ${count} -lt ${MAX_CONNECT_TRIES} ] ; do - if [ "${CERT_VERBOSE}" = "1" ] ; then - echo "Connection attempt ${count}" - fi + debug_log "Connection attempt ${count}" if curl -I "http://${cert_domains}" 2>&1 | grep -w "200\|301" ; then - if [ "$CERT_VERBOSE" = "1" ] ; then - echo "${cert_domains} is up." - fi + debug_log "${cert_domains} is up." return 0; fi let "count+=1" - sleep 15 + sleep 10 done echo "Failed to connect to ${cert_domains}" >2 return 1; diff --git a/certmgr/scripts/letsencrypt.sh b/certmgr/scripts/letsencrypt.sh index 7ea038b35d..a07a06cbb6 100755 --- a/certmgr/scripts/letsencrypt.sh +++ b/certmgr/scripts/letsencrypt.sh @@ -5,22 +5,32 @@ # Container initialization init_vars - -if [ ${CERT_CLEAN} = "1" ] ; then - if [ -d "${CERT_PATH}/live/${cert_domains}" ]; then - clean_certs - fi -fi +init_cert_store # Create the initial certificate if the certs have not already # been created -if [ ! -d "${CERT_PATH}/live/${cert_domains}" ]; then +if [ ! -d "${CERT_LIVE_DIR}" ] ; then + mkdir -p "${CERT_LIVE_DIR}" echo "Creating initial self-signed certificate" create_selfsigned_cert +fi + +# Lookup the issuer of the certificate (either pre-existing or just created) +CERT_ISSUER="" +if [ -f "${CERT_LIVE_DIR}/fullchain.pem" ] ; then + CERT_ISSUER=`openssl x509 -in "${CERT_LIVE_DIR}/fullchain.pem" -noout -issuer | sed 's/issuer=CN *= *//'` + debug_log "Issuer for existing certificate is: ${CERT_ISSUER}" +fi + +# If it is a self-signed cert, wait for the webserver to come up +# and replace it with a cert from letsencrypt +if [ "${CERT_ISSUER}" = "localhost" ] ; then echo "Waiting for webserver to come up" if ! wait_for_webserver ; then - exit 1 + debug_log "Could not connect to webserver" + #exit 1 fi + echo "Request certificate from Let's Encrypt" create_certbot_cert fi diff --git a/certmgr/scripts/selfsigned.sh b/certmgr/scripts/selfsigned.sh index 5b7de59b37..80d98b2d74 100755 --- a/certmgr/scripts/selfsigned.sh +++ b/certmgr/scripts/selfsigned.sh @@ -1,38 +1,5 @@ #! /bin/bash -create_selfsigned_cert() { - DEST_DIR="${CERT_PATH}/live/${CERT_NAME}" - mkdir -p ${DEST_DIR} - - cd ${DEST_DIR} - openssl req -x509 -nodes -newkey rsa:4096 -days ${CERT_SELF_SIGNED_EXPIRE} -keyout privkey.pem -out fullchain.pem -subj '/CN=localhost' - if [ "$CERT_VERBOSE" = "1" ] ; then - echo "Created certificate in ${CERT_PATH} for ${cert_domains}" - echo "Expires: "`openssl x509 -in fullchain.pem -noout -enddate` - fi -} - - -renew_selfsigned_cert() -{ - CERT_FILE="${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" - if [ "$CERT_VERBOSE" = "1" ] ; then - echo "Checking for renewal of ${CERT_FILE}" - fi - - CERT_MIN_SEC_TO_EXPIRE=$(( ${CERT_MIN_DAYS_TO_EXPIRE} * 3600 * 24 )) - if [ -f ${CERT_FILE} ] ; then - openssl x509 -noout -in "${CERT_PATH}/live/${CERT_NAME}/fullchain.pem" -checkend ${CERT_MIN_SEC_TO_EXPIRE} > /dev/null - if [ "$?" = "1" ] ; then - echo "Renewing the certificate for ${cert_domains}" - create_selfsigned_cert - fi - else - echo "Restoring the certificate for ${cert_domains}" - create_selfsigned_cert - fi -} - # Source in util.sh so we can have our nice tools . $(cd $(dirname $0); pwd)/func.sh @@ -40,12 +7,10 @@ CERT_MODE="self-signed" # Container initialization init_vars +init_cert_store -if [ ${CERT_CLEAN} = "1" ] && [ -d "${CERT_PATH}/live/${CERT_NAME}" ]; then - clean_certs -fi - -if [ ! -d "${CERT_PATH}/live/${CERT_NAME}" ]; then +if [ ! f "${SELF_SIGNED_PATH}/fullchain.pem" ] ; then + echo "Creating self-signed certificate" create_selfsigned_cert fi diff --git a/deploy/roles/the_combine_app/defaults/main.yml b/deploy/roles/the_combine_app/defaults/main.yml index 3f7a64dd86..155e20d130 100644 --- a/deploy/roles/the_combine_app/defaults/main.yml +++ b/deploy/roles/the_combine_app/defaults/main.yml @@ -22,6 +22,6 @@ combine_program_id: CombineBackend ############################# # environment variables for service -combine_env_vars: +combine_backend_env_vars: ASPNETCORE_ENVIRONMENT: Production DOTNET_PRINT_TELEMETRY_MESSAGE: false diff --git a/deploy/roles/the_combine_app/templates/combine_backend.service.j2 b/deploy/roles/the_combine_app/templates/combine_backend.service.j2 index 8d5b6b64e5..9e73cbca75 100644 --- a/deploy/roles/the_combine_app/templates/combine_backend.service.j2 +++ b/deploy/roles/the_combine_app/templates/combine_backend.service.j2 @@ -4,7 +4,7 @@ Description=The Combine Backend service. [Service] User={{ combine_user }} WorkingDirectory={{ combine_backend_dir }}/publish -{% for item in combine_env_vars|dict2items %} +{% for item in combine_backend_env_vars|dict2items %} Environment="{{ item.key }}={{ item.value }}" {% endfor %} {% for item in combine_private_env_vars|dict2items %} diff --git a/docker_deploy/group_vars/qa/main.yml b/docker_deploy/group_vars/qa/main.yml index 4d9f72d091..6aea62eac4 100644 --- a/docker_deploy/group_vars/qa/main.yml +++ b/docker_deploy/group_vars/qa/main.yml @@ -5,9 +5,6 @@ # Group: qa ################################################# -ssl_certificate: "/etc/letsencrypt/live/{{ combine_server_name }}/fullchain.pem" -ssl_private_key: "/etc/letsencrypt/live/{{ combine_server_name }}/privkey.pem" - # TheCombine is installed on the QA Server by TeamCity. It will set the # IMAGE_TAG environment variable to major.minor.build_no and launch # Docker compose @@ -17,15 +14,9 @@ combine_image_backend: "{{ aws_ecr }}/combine/backend:{{ image_tag }}" combine_image_frontend: "{{ aws_ecr }}/combine/frontend:{{ image_tag }}" combine_image_certmgr: "{{ aws_ecr }}/combine/certmgr:{{ image_tag }}" -# SSL Certificate variables -# for both Let's Encrypt and Self-signed certs -cert_type: self-signed +# SSL cert variables specific to QA targets +cert_mode: "self-signed" cert_email: "" -cert_is_staging: 0 -cert_domains: - - "{{ combine_server_name }}" -cert_clean: 0 -cert_verbose: 0 # setup variables for roles/aws_access aws_user: "{{ combine_user }}" diff --git a/docker_deploy/group_vars/server/main.yml b/docker_deploy/group_vars/server/main.yml index c8c06eab40..414d1cd174 100644 --- a/docker_deploy/group_vars/server/main.yml +++ b/docker_deploy/group_vars/server/main.yml @@ -5,8 +5,6 @@ # Group: server ################################################# -ssl_certificate: "/etc/letsencrypt/live/{{ combine_server_name }}/fullchain.pem" -ssl_private_key: "/etc/letsencrypt/live/{{ combine_server_name }}/privkey.pem" # TheCombine is installed on the Live Server by TeamCity. It will set the # IMAGE_TAG environment variable to major.minor.build_no and launch @@ -17,15 +15,10 @@ combine_image_backend: "{{ aws_ecr }}/combine/backend:{{ image_tag }}" combine_image_frontend: "{{ aws_ecr }}/combine/frontend:{{ image_tag }}" combine_image_certmgr: "{{ aws_ecr }}/combine/certmgr:{{ image_tag }}" -# SSL Certificate variables -# for both Let's Encrypt and Self-signed certs -cert_type: letsencrypt -cert_email: jimgrady.jg@gmail.com -cert_is_staging: 0 -cert_domains: - - "{{ combine_server_name }}" -cert_clean: 0 -cert_verbose: 0 +# SSL cert variables specific to Server targets +cert_mode: "letsencrypt" +cert_email: "jimgrady.jg@gmail.com" + aws_user: "{{ combine_user }}" aws_group: "{{ combine_group }}" diff --git a/docker_deploy/roles/combine_config/defaults/main.yml b/docker_deploy/roles/combine_config/defaults/main.yml index cf9747ca4a..420cb0db97 100644 --- a/docker_deploy/roles/combine_config/defaults/main.yml +++ b/docker_deploy/roles/combine_config/defaults/main.yml @@ -7,17 +7,17 @@ config_captcha_sitekey: "6Le6BL0UAAAAAMjSs1nINeB5hqDZ4m3mMg3k67x3" ####################################### # environment variable overrides # for non-development deployments -combine_env_vars: - -cert_type: self-signed -cert_email: jimgrady.jg@gmail.com -cert_is_staging: 0 -cert_domains: - - "{{ combine_server_name }}" -cert_clean: 0 -cert_verbose: 0 +combine_backend_env_vars: combine_pull_images: true combine_image_backend: combine/backend combine_image_frontend: combine/frontend combine_image_certmgr: combine/certmgr + +####################################### +# Environment variable defaults for the +# certmgr +cert_is_staging: 0 +cert_verbose: 0 +cert_create_only: 0 +cert_max_connect_tries: 15 diff --git a/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 b/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 index 525eafea17..559a026812 100644 --- a/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 +++ b/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 @@ -26,7 +26,8 @@ services: - ./.env.frontend volumes: - ./nginx/scripts:/usr/share/nginx/html/scripts:ro - - letsencrypt:/etc/letsencrypt + - cert_store:/etc/cert_store + - certbot_data:/var/www/certbot ports: - 80:80 - 443:443 @@ -44,7 +45,8 @@ services: env_file: - ./.env.certmgr volumes: - - letsencrypt:/etc/letsencrypt + - cert_store:/etc/cert_store + - certbot_data:/var/www/certbot restart: unless-stopped database: @@ -58,4 +60,5 @@ services: volumes: backend_data: database_data: - letsencrypt: + cert_store: + certbot_data: diff --git a/docker_deploy/roles/combine_config/templates/env.backend.j2 b/docker_deploy/roles/combine_config/templates/env.backend.j2 index 76fa8434d2..4489249252 100644 --- a/docker_deploy/roles/combine_config/templates/env.backend.j2 +++ b/docker_deploy/roles/combine_config/templates/env.backend.j2 @@ -1,5 +1,5 @@ -{% if combine_env_vars %} -{% for item in combine_env_vars %} +{% if combine_backend_env_vars %} +{% for item in combine_backend_env_vars %} {{ item.key }}={{ item.value }} {% endfor %} {% endif %} diff --git a/docker_deploy/roles/combine_config/templates/env.certmgr.j2 b/docker_deploy/roles/combine_config/templates/env.certmgr.j2 index ade0858edb..86be16a2df 100644 --- a/docker_deploy/roles/combine_config/templates/env.certmgr.j2 +++ b/docker_deploy/roles/combine_config/templates/env.certmgr.j2 @@ -1,9 +1,3 @@ -CERT_PATH=/etc/letsencrypt -CERT_MODE={{ cert_type }} -CERT_EMAIL={{ cert_email }} -CERT_STAGING={{ cert_is_staging }} -CERT_DOMAINS={{ cert_domains|join(", ") }} -CERT_CLEAN={{ cert_clean }} -CERT_VERBOSE={{ cert_verbose }} -CERT_CREATE_ONLY=0 -MAX_CONNECT_TRIES=10 +{% for item in combine_cert_env_vars %} +{{ item.key }}={{ item.value }} +{% endfor %} diff --git a/docker_deploy/roles/combine_config/templates/env.frontend.j2 b/docker_deploy/roles/combine_config/templates/env.frontend.j2 index 874315534a..59ec8a1703 100644 --- a/docker_deploy/roles/combine_config/templates/env.frontend.j2 +++ b/docker_deploy/roles/combine_config/templates/env.frontend.j2 @@ -1,5 +1,5 @@ -CERTBOT_EMAIL={{ cert_email }} -IS_STAGING={{ cert_is_staging }} -SERVER_NAME={{ combine_server_name }} -SSL_CERTIFICATE={{ ssl_certificate }} -SSL_PRIVATE_KEY={{ ssl_private_key }} +{% if combine_frontend_env_vars %} +{% for item in combine_frontend_env_vars %} +{{ item.key }}={{ item.value }} +{% endfor %} +{% endif %} diff --git a/docker_deploy/vars/config_common.yml b/docker_deploy/vars/config_common.yml index c390f24cc5..7373bf4666 100644 --- a/docker_deploy/vars/config_common.yml +++ b/docker_deploy/vars/config_common.yml @@ -10,3 +10,37 @@ frontend_env_dir: "{{ source_dir }}" combine_user: combine combine_group: app combine_app_dir: /opt/combine + +# common configuration items that are shared between the certmgr and frontend +# containers and across target types: +ssl_dir: "/etc/cert_store/nginx/{{ combine_server_name }}" +ssl_certificate: "{{ ssl_dir }}/fullchain.pem" +ssl_private_key: "{{ ssl_dir }}/privkey.pem" + +# SSL Certificate variables +# for both Let's Encrypt and Self-signed certs +combine_cert_env_vars: + - key: "CERT_MODE" + value: "{{ cert_mode }}" + - key: "CERT_EMAIL" + value: "{{ cert_email }}" + - key: "CERT_STAGING" + value: "{{ cert_is_staging }}" + - key: "CERT_DOMAINS" + value: "{{ combine_server_name }}" + - key: "CERT_VERBOSE" + value: "{{ cert_verbose }}" + - key: "CERT_CREATE_ONLY" + value: "{{ cert_create_only }}" + - key: "MAX_CONNECT_TRIES" + value: "{{ cert_max_connect_tries }}" + - key: "SERVER_NAME" + value: "{{ combine_server_name }}" + +combine_frontend_env_vars: + - key: "SERVER_NAME" + value: "{{ combine_server_name }}" + - key: "SSL_CERTIFICATE" + value: "{{ ssl_certificate }}" + - key: "SSL_PRIVATE_KEY" + value: "{{ ssl_private_key }}" diff --git a/scripts/docker_setup.py b/scripts/docker_setup.py index deed40181e..5b81547bd1 100755 --- a/scripts/docker_setup.py +++ b/scripts/docker_setup.py @@ -65,13 +65,13 @@ def main() -> None: "cert_clean": 0, "cert_verbose": 1, "combine_server_name": "localhost", - "ssl_certificate": "/etc/letsencrypt/live/localhost/fullchain.pem", - "ssl_private_key": "/etc/letsencrypt/live/localhost/privkey.pem", - "combine_env_vars": "", + "ssl_certificate": "/etc/nginx/ssl/localhost/fullchain.pem", + "ssl_private_key": "/etc/nginx/ssl/localhost/privkey.pem", + "combine_backend_env_vars": "", "combine_private_env_vars": [ { "key": "COMBINE_JWT_SECRET_KEY", - "value": "JwtSecretKeyForDevelopmentUseOnly", + "value": "JwtSecretKeyForDevelopmentUseOnly" }, {"key": "COMBINE_SMTP_SERVER", "value": ""}, {"key": "COMBINE_SMTP_PORT", "value": "587"}, @@ -79,7 +79,24 @@ def main() -> None: {"key": "COMBINE_SMTP_USERNAME", "value": ""}, {"key": "COMBINE_SMTP_PASSWORD", "value": ""}, {"key": "COMBINE_SMTP_FROM", "value": ""}, - {"key": "COMBINE_PASSWORD_RESET_EXPIRE_TIME", "value": "15"}, + {"key": "COMBINE_PASSWORD_RESET_EXPIRE_TIME", "value": "15"} + ], + "combine_frontend_env_vars": [ + {"key": "SERVER_NAME", "value": "localhost"}. + {"key": "SSL_CERTIFICATE", "value": "/etc/nginx/ssl/localhost/fullchain.pem"}, + {"key": "SSL_PRIVATE_KEY", "value": "/etc/nginx/ssl/localhost/privkey.pem"} + ], + "combine_cert_env_vars": [ + {"key": "CERT_MODE", "value": "self-signed"}. + {"key": "CERT_EMAIL", "value": ""}. + {"key": "CERT_STAGING", "value": "0"}. + {"key": "CERT_DOMAINS", "value": "localhost"}. + {"key": "CERT_VERBOSE", "value": "0"}. + {"key": "CERT_CREATE_ONLY", "value": "0"}. + {"key": "MAX_CONNECT_TRIES", "value": "10"}. + {"key": "SSL_CERTIFICATE", "value": "/etc/nginx/ssl/localhost/fullchain.pem"}, + {"key": "SSL_PRIVATE_KEY", "value": "/etc/nginx/ssl/localhost/privkey.pem"}, + {"key": "SSL_DIR", "value": "/etc/nginx/ssl/localhost"}. ], "config_captcha_required": json.dumps(not args.no_captcha), "config_captcha_sitekey": "6Le6BL0UAAAAAMjSs1nINeB5hqDZ4m3mMg3k67x3", @@ -100,7 +117,7 @@ def main() -> None: env_var["value"] = os.environ[env_var["key"]] # Set backend common env_vars if they are defined for our process - for env_var in dev_config["combine_env_vars"]: + for env_var in dev_config["combine_backend_env_vars"]: if env_var["key"] in os.environ: env_var["value"] = os.environ[env_var["key"]] From 7ed0280c4d63d85d0bd800b4e61506d2df7178e0 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 08:41:38 -0400 Subject: [PATCH 17/29] Store certbot cert in its own volume The certbot/certbot container expects /etc/letsencrypt directory to exist from the beginning and resists efforts to replace it with a symbolic link to /etc/cert_store/letsencrypt. --- certmgr/Dockerfile | 2 -- certmgr/scripts/README.md | 16 +++++----- certmgr/scripts/func.sh | 30 +++++++++---------- certmgr/scripts/letsencrypt.sh | 7 ++--- .../templates/docker-compose.yml.j2 | 3 ++ 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/certmgr/Dockerfile b/certmgr/Dockerfile index 90fd373031..39faeb858b 100644 --- a/certmgr/Dockerfile +++ b/certmgr/Dockerfile @@ -1,7 +1,5 @@ FROM certbot/certbot:v1.8.0 -VOLUME /etc/letsencrypt - RUN apk update && \ apk upgrade && \ apk add --no-cache bash && \ diff --git a/certmgr/scripts/README.md b/certmgr/scripts/README.md index 4f7745284d..75c5027712 100644 --- a/certmgr/scripts/README.md +++ b/certmgr/scripts/README.md @@ -2,21 +2,21 @@ ## Certificate storage -The certificates are stored in a docker volume. The volume is named cert_store +The certificates are stored in a docker volume. The volume is named letsencrypt and is mounted by the `frontend` and the `certmgr` containers. Both containers mount -the volume at `/etc/cert_store`. +the volume at `/etc/letsencrypt`. -At the top level, there are 3 directories in `/etc/cert_store`: +At the top level, there are 3 directories in `/etc/letsencrypt`: - `letsencrypt` - stores the certificates created by letsencrypt and managed by `certbot`. - `/etc/letsencrypt` is created as a symbolic link to `/etc/cert_store/letsencrypt` + `/etc/letsencrypt` is created as a symbolic link to `/etc/letsencrypt/letsencrypt` - `nginx` - The Nginx web server is configured to read its SSL certificates from: - - SSL Certificate: `/etc/cert_store/nginx//fullchain.pem` - - SSL Private Key: `/etc/cert_store/nginx//privkey.pem` + - SSL Certificate: `/etc/letsencrypt/nginx//fullchain.pem` + - SSL Private Key: `/etc/letsencrypt/nginx//privkey.pem` - This is implemented by setting up `/etc/cert_store/nginx/ssl/` as a + This is implemented by setting up `/etc/letsencrypt/nginx/ssl/` as a symbolic link to the self-signed or letsencrypt certificates. - `selfsigned` - stores a self-signed certificate for the server in - `/etc/cert_store/selfsigned/` + `/etc/letsencrypt/selfsigned/` ## Entrypoint scripts for the certbot container for TheCombine diff --git a/certmgr/scripts/func.sh b/certmgr/scripts/func.sh index cb44ac2463..4fe6c8f258 100755 --- a/certmgr/scripts/func.sh +++ b/certmgr/scripts/func.sh @@ -6,7 +6,7 @@ init_vars() { # set variables to their default values if they are not # specified - CERT_STORE=${CERT_STORE:="/etc/"} + CERT_STORE=${CERT_STORE:="/etc/cert_store"} # Folder for certicates as configured by the Nginx webserver CERT_CREATE_ONLY=${CERT_CREATE_ONLY:=0} CERT_EMAIL=${CERT_EMAIL:=""} @@ -21,7 +21,7 @@ init_vars() { fi # create $cert_domains as an array of domain names IFS=" " read -r -a cert_domains <<< "${CERT_DOMAINS}" - debug_log "Certificates stored in ${CERT_STORE}" + debug_log "Self-signed Certificates stored in ${CERT_STORE}" debug_log "Certificate name: ${SERVER_NAME}" debug_log "Domains: ${cert_domains[*]}" debug_log "Certificates expire in ${SELF_SIGNED_EXPIRE}" @@ -34,13 +34,10 @@ init_vars() { } init_cert_store() { - # Create nginx, letsencrypt, and selfsigned directories - for subdir in "nginx" "letsencrypt" "selfsigned" ; do + # Create nginx, and selfsigned directories + for subdir in "nginx" "selfsigned" ; do mkdir -p "${CERT_STORE}/${subdir}" done - - # link /etc/letsencrypt to /etc/cert_store/letsencrypt - ln -s "${CERT_STORE}/letsencrypt" "/etc/letsencrypt" } debug_log() { @@ -54,20 +51,22 @@ update_link() { target=$2 debug_log "linking ${src} to ${target}" - if [ `readlink ${target}` != "${src}" ] ; then + link_target=`readlink ${target}` + if [ "${link_target}" != "${src}" ] ; then if [ -L "${target}" ]; then rm "${target}" - debug_log "Old link removed" + debug_log " Old link removed" fi ln -s ${src} ${target} else - debug_log "${target} already points to ${src}" + debug_log " ${target} already points to ${src}" fi } create_selfsigned_cert() { - mkdir -p "${SELF_SIGNED_PATH}" + debug_log "Self-signed certificates are stored in ${SELF_SIGNED_PATH}" + mkdir -p ${SELF_SIGNED_PATH} openssl req -x509 -nodes -newkey rsa:4096 -days ${SELF_SIGNED_EXPIRE} -keyout "${SELF_SIGNED_PATH}/privkey.pem" -out "${SELF_SIGNED_PATH}/fullchain.pem" -subj '/CN=localhost' debug_log "Created certificate in ${CERT_STORE} for ${cert_domains}" debug_log "Expires: "`openssl x509 -in "${SELF_SIGNED_PATH}/fullchain.pem" -noout -enddate` @@ -102,7 +101,7 @@ create_certbot_cert() { if [ -z "${CERT_EMAIL}" ] ; then email_arg="--register-unsafely-without-email" else - email_arg="--email '${CERT_EMAIL}'" + email_arg="--email ${CERT_EMAIL}" fi # Enable staging mode if needed @@ -115,11 +114,12 @@ create_certbot_cert() { -d ${CERT_DOMAINS} \ --rsa-key-size 4096 \ --agree-tos \ + --non-interactive \ --force-renewal" debug_log "$cert_cmd" - $cert_cmd - - update_link "${CERT_STORE}/letsencrypt/live/${SERVER_NAME}" "${NGINX_CERT_PATH}" + if $cert_cmd ; then + update_link "/etc/letsencrypt/live/${SERVER_NAME}" "${NGINX_CERT_PATH}" + fi } wait_for_webserver() { diff --git a/certmgr/scripts/letsencrypt.sh b/certmgr/scripts/letsencrypt.sh index a07a06cbb6..0152dd0143 100755 --- a/certmgr/scripts/letsencrypt.sh +++ b/certmgr/scripts/letsencrypt.sh @@ -9,22 +9,21 @@ init_cert_store # Create the initial certificate if the certs have not already # been created -if [ ! -d "${CERT_LIVE_DIR}" ] ; then - mkdir -p "${CERT_LIVE_DIR}" +if [ ! -L "${NGINX_CERT_PATH}" ] ; then echo "Creating initial self-signed certificate" create_selfsigned_cert fi # Lookup the issuer of the certificate (either pre-existing or just created) CERT_ISSUER="" -if [ -f "${CERT_LIVE_DIR}/fullchain.pem" ] ; then +if [ -f "${NGINX_LIVE_DIR}/fullchain.pem" ] ; then CERT_ISSUER=`openssl x509 -in "${CERT_LIVE_DIR}/fullchain.pem" -noout -issuer | sed 's/issuer=CN *= *//'` debug_log "Issuer for existing certificate is: ${CERT_ISSUER}" fi # If it is a self-signed cert, wait for the webserver to come up # and replace it with a cert from letsencrypt -if [ "${CERT_ISSUER}" = "localhost" ] ; then +if [ -z "${CERT_ISSUER}" ] || [ "${CERT_ISSUER}" = "localhost" ] ; then echo "Waiting for webserver to come up" if ! wait_for_webserver ; then debug_log "Could not connect to webserver" diff --git a/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 b/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 index 559a026812..87452d760a 100644 --- a/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 +++ b/docker_deploy/roles/combine_config/templates/docker-compose.yml.j2 @@ -26,6 +26,7 @@ services: - ./.env.frontend volumes: - ./nginx/scripts:/usr/share/nginx/html/scripts:ro + - letsencrypt:/etc/letsencrypt - cert_store:/etc/cert_store - certbot_data:/var/www/certbot ports: @@ -45,6 +46,7 @@ services: env_file: - ./.env.certmgr volumes: + - letsencrypt:/etc/letsencrypt - cert_store:/etc/cert_store - certbot_data:/var/www/certbot restart: unless-stopped @@ -60,5 +62,6 @@ services: volumes: backend_data: database_data: + letsencrypt: cert_store: certbot_data: From b737ca963a25a95eca5293ccfac9358067232e22 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 08:51:49 -0400 Subject: [PATCH 18/29] Fix Python lint issues --- scripts/docker_setup.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/scripts/docker_setup.py b/scripts/docker_setup.py index 5b81547bd1..1ceff3c3fe 100755 --- a/scripts/docker_setup.py +++ b/scripts/docker_setup.py @@ -69,34 +69,31 @@ def main() -> None: "ssl_private_key": "/etc/nginx/ssl/localhost/privkey.pem", "combine_backend_env_vars": "", "combine_private_env_vars": [ - { - "key": "COMBINE_JWT_SECRET_KEY", - "value": "JwtSecretKeyForDevelopmentUseOnly" - }, + {"key": "COMBINE_JWT_SECRET_KEY", "value": "JwtSecretKeyForDevelopmentUseOnly"}, {"key": "COMBINE_SMTP_SERVER", "value": ""}, {"key": "COMBINE_SMTP_PORT", "value": "587"}, {"key": "COMBINE_SMTP_ADDRESS", "value": ""}, {"key": "COMBINE_SMTP_USERNAME", "value": ""}, {"key": "COMBINE_SMTP_PASSWORD", "value": ""}, {"key": "COMBINE_SMTP_FROM", "value": ""}, - {"key": "COMBINE_PASSWORD_RESET_EXPIRE_TIME", "value": "15"} + {"key": "COMBINE_PASSWORD_RESET_EXPIRE_TIME", "value": "15"}, ], "combine_frontend_env_vars": [ - {"key": "SERVER_NAME", "value": "localhost"}. + {"key": "SERVER_NAME", "value": "localhost"}, {"key": "SSL_CERTIFICATE", "value": "/etc/nginx/ssl/localhost/fullchain.pem"}, - {"key": "SSL_PRIVATE_KEY", "value": "/etc/nginx/ssl/localhost/privkey.pem"} + {"key": "SSL_PRIVATE_KEY", "value": "/etc/nginx/ssl/localhost/privkey.pem"}, ], "combine_cert_env_vars": [ - {"key": "CERT_MODE", "value": "self-signed"}. - {"key": "CERT_EMAIL", "value": ""}. - {"key": "CERT_STAGING", "value": "0"}. - {"key": "CERT_DOMAINS", "value": "localhost"}. - {"key": "CERT_VERBOSE", "value": "0"}. - {"key": "CERT_CREATE_ONLY", "value": "0"}. - {"key": "MAX_CONNECT_TRIES", "value": "10"}. + {"key": "CERT_MODE", "value": "self-signed"}, + {"key": "CERT_EMAIL", "value": ""}, + {"key": "CERT_STAGING", "value": "0"}, + {"key": "CERT_DOMAINS", "value": "localhost"}, + {"key": "CERT_VERBOSE", "value": "0"}, + {"key": "CERT_CREATE_ONLY", "value": "0"}, + {"key": "MAX_CONNECT_TRIES", "value": "10"}, {"key": "SSL_CERTIFICATE", "value": "/etc/nginx/ssl/localhost/fullchain.pem"}, {"key": "SSL_PRIVATE_KEY", "value": "/etc/nginx/ssl/localhost/privkey.pem"}, - {"key": "SSL_DIR", "value": "/etc/nginx/ssl/localhost"}. + {"key": "SSL_DIR", "value": "/etc/nginx/ssl/localhost"}, ], "config_captcha_required": json.dumps(not args.no_captcha), "config_captcha_sitekey": "6Le6BL0UAAAAAMjSs1nINeB5hqDZ4m3mMg3k67x3", From 564dc38d816b4d3c3c5716f0830751f6e38e2ed4 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 09:27:33 -0400 Subject: [PATCH 19/29] Fix test argument in selfsigned.sh --- certmgr/scripts/selfsigned.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certmgr/scripts/selfsigned.sh b/certmgr/scripts/selfsigned.sh index 80d98b2d74..29313e03e1 100755 --- a/certmgr/scripts/selfsigned.sh +++ b/certmgr/scripts/selfsigned.sh @@ -9,7 +9,7 @@ CERT_MODE="self-signed" init_vars init_cert_store -if [ ! f "${SELF_SIGNED_PATH}/fullchain.pem" ] ; then +if [ ! -f "${SELF_SIGNED_PATH}/fullchain.pem" ] ; then echo "Creating self-signed certificate" create_selfsigned_cert fi From 9a793a89ed553285d374e0ef4db9204c4718389f Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 09:28:05 -0400 Subject: [PATCH 20/29] Update environment variable definitions for new design --- scripts/docker_setup.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/docker_setup.py b/scripts/docker_setup.py index 1ceff3c3fe..0c5f5c2752 100755 --- a/scripts/docker_setup.py +++ b/scripts/docker_setup.py @@ -80,20 +80,18 @@ def main() -> None: ], "combine_frontend_env_vars": [ {"key": "SERVER_NAME", "value": "localhost"}, - {"key": "SSL_CERTIFICATE", "value": "/etc/nginx/ssl/localhost/fullchain.pem"}, - {"key": "SSL_PRIVATE_KEY", "value": "/etc/nginx/ssl/localhost/privkey.pem"}, + {"key": "SSL_CERTIFICATE", "value": "/etc/cert_store/nginx/localhost/fullchain.pem"}, + {"key": "SSL_PRIVATE_KEY", "value": "/etc/cert_store/nginx/localhost/privkey.pem"}, ], "combine_cert_env_vars": [ {"key": "CERT_MODE", "value": "self-signed"}, {"key": "CERT_EMAIL", "value": ""}, {"key": "CERT_STAGING", "value": "0"}, {"key": "CERT_DOMAINS", "value": "localhost"}, - {"key": "CERT_VERBOSE", "value": "0"}, + {"key": "CERT_VERBOSE", "value": "1"}, {"key": "CERT_CREATE_ONLY", "value": "0"}, {"key": "MAX_CONNECT_TRIES", "value": "10"}, - {"key": "SSL_CERTIFICATE", "value": "/etc/nginx/ssl/localhost/fullchain.pem"}, - {"key": "SSL_PRIVATE_KEY", "value": "/etc/nginx/ssl/localhost/privkey.pem"}, - {"key": "SSL_DIR", "value": "/etc/nginx/ssl/localhost"}, + {"key": "SERVER_NAME", "value": "localhost"} ], "config_captcha_required": json.dumps(not args.no_captcha), "config_captcha_sitekey": "6Le6BL0UAAAAAMjSs1nINeB5hqDZ4m3mMg3k67x3", From 629ca8d2b31fcb733ae48f31cb30be750ef991b4 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 09:32:30 -0400 Subject: [PATCH 21/29] Add superfluous comma to keep lint happy --- scripts/docker_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docker_setup.py b/scripts/docker_setup.py index 0c5f5c2752..66b49cb268 100755 --- a/scripts/docker_setup.py +++ b/scripts/docker_setup.py @@ -91,7 +91,7 @@ def main() -> None: {"key": "CERT_VERBOSE", "value": "1"}, {"key": "CERT_CREATE_ONLY", "value": "0"}, {"key": "MAX_CONNECT_TRIES", "value": "10"}, - {"key": "SERVER_NAME", "value": "localhost"} + {"key": "SERVER_NAME", "value": "localhost"}, ], "config_captcha_required": json.dumps(not args.no_captcha), "config_captcha_sitekey": "6Le6BL0UAAAAAMjSs1nINeB5hqDZ4m3mMg3k67x3", From 7e81541294ffb2f161c732496f014bfcc4910b26 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 10:45:13 -0400 Subject: [PATCH 22/29] Remove CERT_CREATE_ONLY variable --- certmgr/scripts/func.sh | 1 - certmgr/scripts/selfsigned.sh | 14 +++++--------- docker_deploy/vars/config_common.yml | 2 -- scripts/docker_setup.py | 1 - 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/certmgr/scripts/func.sh b/certmgr/scripts/func.sh index 4fe6c8f258..59653de0a0 100755 --- a/certmgr/scripts/func.sh +++ b/certmgr/scripts/func.sh @@ -8,7 +8,6 @@ init_vars() { # specified CERT_STORE=${CERT_STORE:="/etc/cert_store"} # Folder for certicates as configured by the Nginx webserver - CERT_CREATE_ONLY=${CERT_CREATE_ONLY:=0} CERT_EMAIL=${CERT_EMAIL:=""} CERT_STAGING=${CERT_STAGING:=0} MAX_CONNECT_TRIES=${MAX_CONNECT_TRIES:=15} diff --git a/certmgr/scripts/selfsigned.sh b/certmgr/scripts/selfsigned.sh index 29313e03e1..8bbc08beb8 100755 --- a/certmgr/scripts/selfsigned.sh +++ b/certmgr/scripts/selfsigned.sh @@ -14,13 +14,9 @@ if [ ! -f "${SELF_SIGNED_PATH}/fullchain.pem" ] ; then create_selfsigned_cert fi -if [ "$CERT_CREATE_ONLY" = "0" ] ; then - while true; do - renew_selfsigned_cert - sleep 60 & - wait $! +while true; do + renew_selfsigned_cert + sleep 60 & + wait $! - done -else - exit 0 -fi +done diff --git a/docker_deploy/vars/config_common.yml b/docker_deploy/vars/config_common.yml index 7373bf4666..e0e85ac256 100644 --- a/docker_deploy/vars/config_common.yml +++ b/docker_deploy/vars/config_common.yml @@ -30,8 +30,6 @@ combine_cert_env_vars: value: "{{ combine_server_name }}" - key: "CERT_VERBOSE" value: "{{ cert_verbose }}" - - key: "CERT_CREATE_ONLY" - value: "{{ cert_create_only }}" - key: "MAX_CONNECT_TRIES" value: "{{ cert_max_connect_tries }}" - key: "SERVER_NAME" diff --git a/scripts/docker_setup.py b/scripts/docker_setup.py index 66b49cb268..9ea1f14733 100755 --- a/scripts/docker_setup.py +++ b/scripts/docker_setup.py @@ -89,7 +89,6 @@ def main() -> None: {"key": "CERT_STAGING", "value": "0"}, {"key": "CERT_DOMAINS", "value": "localhost"}, {"key": "CERT_VERBOSE", "value": "1"}, - {"key": "CERT_CREATE_ONLY", "value": "0"}, {"key": "MAX_CONNECT_TRIES", "value": "10"}, {"key": "SERVER_NAME", "value": "localhost"}, ], From ef84a95797d431073bdff945983a9b70f238d0e1 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 10:45:36 -0400 Subject: [PATCH 23/29] Update documentation for frontend refactoring --- certmgr/scripts/README.md | 96 ++++++++++++++++-------------------- docs/docker_deploy/README.md | 19 ++++--- 2 files changed, 54 insertions(+), 61 deletions(-) diff --git a/certmgr/scripts/README.md b/certmgr/scripts/README.md index 75c5027712..4af918187f 100644 --- a/certmgr/scripts/README.md +++ b/certmgr/scripts/README.md @@ -20,46 +20,46 @@ At the top level, there are 3 directories in `/etc/letsencrypt`: ## Entrypoint scripts for the certbot container for TheCombine -### self-signed.sh -Creates a self-signed certificate to be used by the nginx web server and exits. +### Environment Variables -#### Environment Variables +The following environment variables are used by the certmgr to control its +behavior: -| Variable Name | Description | -| ------------------- | ---------------------------------------------------------------------------- | -| CERT_STORE | Directory for the SSL certificates | -| CERT_CLEAN | Set to 1 to remove old certificates first | -| CERT_PRIMARY_DOMAIN | Primary domain for the certificate - used to specify location of certificate | -| | | +| Variable Name | Description | +| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| CERT_MODE | May be one of `self-signed`, `letsencrypt`, `cert-server`, or `cert-client` to specify which of the certificate management personalities to use | +| CERT_EMAIL | Set to 1 to remove old certificates first | +| CERT_STAGING | Primary domain for the certificate - used to specify location of certificate | +| CERT_DOMAINS | A space separated list of domains for the certificate. | +| CERT_VERBOSE | Set to 1 for verbose output to aid in debugging; 0 otherwise. | +| SERVER_NAME | Name of the server - used to specify the directory where the certificates are stored. | +| MAX_CONNECT_TRIES | Number of times to check if the webserver is up before attempting to get a certificate from letsencrypt. | + +### selfsigned.sh +`selfsigned.sh` implements the certmgr behavior when `CERT_MODE` is set to `self-signed`. +It creates a self-signed certificate to be used by the nginx web server and enters a loop to periodically check to see if the certificate needs to be renewed/recreated. The certificate is valid for 10 years and will be renewed when there are less than 10 days until is expires. ### letsencrypt.sh -Uses certbot to create/renew a certificate for the nginx web server. If there are -no certificates or if the certificate is for `localhost` then the certificates will -be deleted and a certificate will be generated by `letsencrypt`. `letsencrypt` -will then enter a loop to check for certificate renewal every 12 hours. - -#### Environment Variables - -| Variable Name | Description | -| ----------------- | ------------------------------------------------ | -| CERT_STORE | Directory for the SSL certificates | -| CERT_STAGING | Set to 1 to generate a testing certificate | -| CERT_DOMAINS | List of domains for the certificate | -| CERT_KEY_SIZE | Size for the RSA Key (Optional: default is 4096) | -| CERT_EMAIL | Support email for the certificate | -| FORCE | Delete any existing certificates first | -| EXIT_AFTER_CREATE | Exit after creating the certificates | +`letsencrypt.sh` implements the certmgr behavior when `CERT_MODE` is set to `letsencrypt`. +If there is no certificate or if it was issued by `localhost`, then `letsencrypt.sh` will: + - create a self-signed certificate + - wait for the webserver to come up + - use `certbot` to request a certificate from `letsencrypt` using the webroot authentication method + - redirect the webserver configuration to point to the new certificate +Once there is a letsencrypt certificate, `letsencrypt.sh` enters a loop to check every 12 hours if the certificate should be renewed. ### certserver.sh *Not Implemented Yet* -creates multiple certificates and pushes them to an AWS S3 bucket +`certserver.sh` will implement the certmgr behavior when `CERT_MODE` is set to `cert-server`. +It will create multiple certificates and push them to an AWS S3 bucket ### certclient.sh *Not Implemented Yet* -fetches an SSL certificate from the AWS S3 bucket (when an internet connection is -available) +`certclient.sh` will implement the certmgr behavior when `CERT_MODE` is set to `cert-client`. +It will fetch an SSL certificate from the AWS S3 bucket (when an internet connection is +available). ## Installation Requirements @@ -69,13 +69,6 @@ script that is used to setup the development environment, `docker_setup.py`. `d is only used for setting up a development environment that uses a self-signed certificate. -For each scenario, the first step is to build each of the containers and then run -the appropriate script. - -The sections below list the steps that must be performed for each of the installation -scenarios. - -### Self-Signed Certificates When installing TheCombine for using self-signed certificates, e.g. for the QA server or for development use, the following steps are required: 1. Build each container: @@ -83,21 +76,18 @@ or for development use, the following steps are required: - backend - certbot 2. Install & configure the docker files: - 1. for Linux server (e.g. QA server) run the Ansible script, `playbook_target_setup.yml` - 2. for Development mode, run `docker_setup.py` - 3. Run `docker-compose up --detach` - -### Let's Encrypt Certificates -When installing TheCombine for using certificates from letsencrypt, e.g. for the -Live server, the following steps are required: - 1. Build each container: - - frontend - - backend - - certbot - 2. Install & configure the docker files: run the `playbook_target_setup.yml` playbook; The Playbook will - 1. - 3. Create a dummy, self-signed certificate: run `docker-compose run --rm --entrypoint "selfsigned.sh" certbot` - 4. Start up the frontend: run `docker-compose up --force-recreate -d frontend` - 5. Start the certbot container to get a certificate from letsencrypt: run `docker-compose run --rm -e CERT_CLEAN=1 --entrypoint "letsencrypt.sh" certbot` - 6. Reload the nginx configuration: run `docker-compose exec frontend nginx -s reload` (may not be necessary since certificates are in the same location) - 7. Run TheCombine: `docker-compose up --detach` + 1. for Linux server (e.g. QA server) + - run the Ansible script, `playbook_target_setup.yml` + - connect to the target as the user `combine` + - cd to `/opt/combine` + - run `docker-compose up --detach` + 2. for Development mode, + - cd to the project root directory + - run `scripts/docker_setup.py` + - run `docker-compose build` + - run `docker-compose up --detach` + +Note that the machines that are configured to get their certificates from _Let's Encrypt_ may need have the frontend webserver reloaded in order to switch from the self-signed certificate to the one from _Let's Encrypt_. To do this, run: +``` +docker-compose exec frontend nginx -s reload +``` diff --git a/docs/docker_deploy/README.md b/docs/docker_deploy/README.md index 1876105afc..393a490a18 100644 --- a/docs/docker_deploy/README.md +++ b/docs/docker_deploy/README.md @@ -97,7 +97,7 @@ ansible-playbook playbook_target_setup.yml --limit -u -K Notes: - Do not add the `-K` option if you do not need to enter your password to - run `sudo` commands + run `sudo` commands _on the target machine_. - The *\* must be listed in the hosts.yml file (in \/docker_deploy). If it is not, then you need to create your own inventory file (see [below](#creating-your-own-inventory-file)). @@ -105,9 +105,9 @@ Notes: #### Running the *TheCombine* Docker Containers *TheCombine*'s docker containers are built by SIL's *TeamCity* server. Once they -are built successfully, they are installed on the QA or are pushed to -Amazon's Elastic Container Registry (AWS ECR). When the container images are -pushed to AWS ECR, these images are then used to update the Live server. +are built successfully, they are pushed to +Amazon's Elastic Container Registry (AWS ECR). The production `docker-compose.yml` +files will pull the images from AWS_ECR. #### Creating Your Own Inventory File @@ -129,9 +129,7 @@ To use your own inventory file: config_captcha_required should be "true" or "false" (including the quotes); if it is "false", config_captcha_sitekey can be an empty string. - * add any variables whose default value you want to override. In particular, - `combine_source_version` specifies the branch in the git repository to be - checked out when the project is deployed. + * add any variables whose default value you want to override. * to use the custom inventory file, add the following option to the ansible-playbook commands above: `-i custom-inventory.yml` where `custom-inventory.yml` is the name of the inventory file that you created. @@ -147,9 +145,11 @@ up *TheCombine*. The following files are used to define the containers and their dependencies: * ./docker-compose.yml * ./Dockerfile + * ./.env.frontend * ./Backend/Dockerfile * ./.env.backend - * ./.env.frontend + * ./certmgr/Dockerfile + * ./.env.certmgr With these files, we can use `docker-compose` to startup *TheCombine* in the development environment (see the project top-level README.md). @@ -184,6 +184,9 @@ once at initial setup and if ever the playbook or its roles change. containers, `.env.frontend` and `.env.backend`; 3. Create the runtime configuration for the UI; 4. Create the configuration file for the nginx web server. + 5. Setup access to the Amazon Web Services + 1. Install the `aws-cli` package + 2. Create the AWS access profiles # Additional Details From d7cda32c3cd37510620a859c460dd5d03128c192 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 11:45:11 -0400 Subject: [PATCH 24/29] Update comments, fix variable name --- certmgr/scripts/letsencrypt.sh | 4 ++-- certmgr/scripts/selfsigned.sh | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/certmgr/scripts/letsencrypt.sh b/certmgr/scripts/letsencrypt.sh index 0152dd0143..874ab4ce2e 100755 --- a/certmgr/scripts/letsencrypt.sh +++ b/certmgr/scripts/letsencrypt.sh @@ -1,9 +1,9 @@ #! /bin/bash -# Source in util.sh so we can have our nice tools +# Source in func.sh so we can have our nice tools . $(cd $(dirname $0); pwd)/func.sh -# Container initialization +# Initialize the container init_vars init_cert_store diff --git a/certmgr/scripts/selfsigned.sh b/certmgr/scripts/selfsigned.sh index 8bbc08beb8..e58d2849e1 100755 --- a/certmgr/scripts/selfsigned.sh +++ b/certmgr/scripts/selfsigned.sh @@ -1,14 +1,13 @@ #! /bin/bash -# Source in util.sh so we can have our nice tools +# Source in func.sh so we can have our nice tools . $(cd $(dirname $0); pwd)/func.sh -CERT_MODE="self-signed" - -# Container initialization +# Initialize the container init_vars init_cert_store +# Check to see if the certificate already exists if [ ! -f "${SELF_SIGNED_PATH}/fullchain.pem" ] ; then echo "Creating self-signed certificate" create_selfsigned_cert @@ -18,5 +17,4 @@ while true; do renew_selfsigned_cert sleep 60 & wait $! - done From 56f67b7a0a48eae36bd098a69e1d51dfbef63731 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 14:15:25 -0400 Subject: [PATCH 25/29] Fix environment variable for Nginx certificate location --- certmgr/scripts/letsencrypt.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certmgr/scripts/letsencrypt.sh b/certmgr/scripts/letsencrypt.sh index 874ab4ce2e..7c5e1cdcb7 100755 --- a/certmgr/scripts/letsencrypt.sh +++ b/certmgr/scripts/letsencrypt.sh @@ -16,8 +16,8 @@ fi # Lookup the issuer of the certificate (either pre-existing or just created) CERT_ISSUER="" -if [ -f "${NGINX_LIVE_DIR}/fullchain.pem" ] ; then - CERT_ISSUER=`openssl x509 -in "${CERT_LIVE_DIR}/fullchain.pem" -noout -issuer | sed 's/issuer=CN *= *//'` +if [ -f "${NGINX_CERT_PATH}/fullchain.pem" ] ; then + CERT_ISSUER=`openssl x509 -in "${NGINX_CERT_PATH}/fullchain.pem" -noout -issuer | sed 's/issuer=CN *= *//'` debug_log "Issuer for existing certificate is: ${CERT_ISSUER}" fi From c29d77b00cd792e1e87e71fbe0d5da68a52a05cb Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 14:16:00 -0400 Subject: [PATCH 26/29] Documentation update --- certmgr/scripts/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certmgr/scripts/README.md b/certmgr/scripts/README.md index 4af918187f..8fe9bf34ff 100644 --- a/certmgr/scripts/README.md +++ b/certmgr/scripts/README.md @@ -87,7 +87,7 @@ or for development use, the following steps are required: - run `docker-compose build` - run `docker-compose up --detach` -Note that the machines that are configured to get their certificates from _Let's Encrypt_ may need have the frontend webserver reloaded in order to switch from the self-signed certificate to the one from _Let's Encrypt_. To do this, run: +Note that the machines that are configured to get their certificates from _Let's Encrypt_ need to have the frontend webserver reloaded in order to switch from the self-signed certificate to the one from _Let's Encrypt_. To do this, run: ``` docker-compose exec frontend nginx -s reload ``` From bd3a5cf77994ee76348da554d2b19ee695d771a7 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 16:11:36 -0400 Subject: [PATCH 27/29] Changes from review comments. --- certmgr/{scripts => }/README.md | 49 ++++++++++++++--------- certmgr/scripts/selfsigned.sh | 2 +- docker_deploy/group_vars/server/main.yml | 1 - docker_deploy/group_vars/server/vault.yml | 22 +++++----- 4 files changed, 42 insertions(+), 32 deletions(-) rename certmgr/{scripts => }/README.md (72%) diff --git a/certmgr/scripts/README.md b/certmgr/README.md similarity index 72% rename from certmgr/scripts/README.md rename to certmgr/README.md index 8fe9bf34ff..35fdc31d75 100644 --- a/certmgr/scripts/README.md +++ b/certmgr/README.md @@ -28,7 +28,7 @@ behavior: | Variable Name | Description | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | CERT_MODE | May be one of `self-signed`, `letsencrypt`, `cert-server`, or `cert-client` to specify which of the certificate management personalities to use | -| CERT_EMAIL | Set to 1 to remove old certificates first | +| CERT_EMAIL | Set to e-mail address for certificate expiration notices first | | CERT_STAGING | Primary domain for the certificate - used to specify location of certificate | | CERT_DOMAINS | A space separated list of domains for the certificate. | | CERT_VERBOSE | Set to 1 for verbose output to aid in debugging; 0 otherwise. | @@ -36,41 +36,47 @@ behavior: | MAX_CONNECT_TRIES | Number of times to check if the webserver is up before attempting to get a certificate from letsencrypt. | ### selfsigned.sh -`selfsigned.sh` implements the certmgr behavior when `CERT_MODE` is set to `self-signed`. -It creates a self-signed certificate to be used by the nginx web server and enters a loop to periodically check to see if the certificate needs to be renewed/recreated. The certificate is valid for 10 years and will be renewed when there are less than 10 days until is expires. +`selfsigned.sh` implements the certmgr behavior when `CERT_MODE` is set to +`self-signed`. It creates a self-signed certificate to be used by the nginx web +server and enters a loop to periodically check to see if the certificate needs +to be renewed/recreated. The certificate is valid for 10 years and will be +renewed when there are less than 10 days until is expires. ### letsencrypt.sh -`letsencrypt.sh` implements the certmgr behavior when `CERT_MODE` is set to `letsencrypt`. -If there is no certificate or if it was issued by `localhost`, then `letsencrypt.sh` will: +`letsencrypt.sh` implements the certmgr behavior when `CERT_MODE` is set to +`letsencrypt`. If there is no certificate or if it was issued by `localhost`, +then `letsencrypt.sh` will: - create a self-signed certificate - wait for the webserver to come up - - use `certbot` to request a certificate from `letsencrypt` using the webroot authentication method + - use `certbot` to request a certificate from `letsencrypt` using the webroot + authentication method - redirect the webserver configuration to point to the new certificate -Once there is a letsencrypt certificate, `letsencrypt.sh` enters a loop to check every 12 hours if the certificate should be renewed. +Once there is a letsencrypt certificate, `letsencrypt.sh` enters a loop to check +every 12 hours if the certificate should be renewed. ### certserver.sh *Not Implemented Yet* -`certserver.sh` will implement the certmgr behavior when `CERT_MODE` is set to `cert-server`. -It will create multiple certificates and push them to an AWS S3 bucket +`certserver.sh` will implement the certmgr behavior when `CERT_MODE` is set to +`cert-server`. It will create multiple certificates and push them to an AWS S3 bucket ### certclient.sh *Not Implemented Yet* -`certclient.sh` will implement the certmgr behavior when `CERT_MODE` is set to `cert-client`. -It will fetch an SSL certificate from the AWS S3 bucket (when an internet connection is -available). +`certclient.sh` will implement the certmgr behavior when `CERT_MODE` is set to +`cert-client`. It will fetch an SSL certificate from the AWS S3 bucket (when an +internet connection is available). ## Installation Requirements -The following scenarios list the various requirements for the Ansible playbook that -is used to setup the various targets, `playbook_target_setup.yml` and the Python -script that is used to setup the development environment, `docker_setup.py`. `docker_setup.py` -is only used for setting up a development environment that uses a self-signed -certificate. +The following scenarios list the various requirements for the Ansible playbook +that is used to setup the various targets, `playbook_target_setup.yml` and the +Python script that is used to setup the development environment, +`docker_setup.py`. `docker_setup.py` is only used for setting up a development +environment that uses a self-signed certificate. -When installing TheCombine for using self-signed certificates, e.g. for the QA server -or for development use, the following steps are required: +When installing TheCombine for using self-signed certificates, e.g. for the QA +server or for development use, the following steps are required: 1. Build each container: - frontend - backend @@ -87,7 +93,10 @@ or for development use, the following steps are required: - run `docker-compose build` - run `docker-compose up --detach` -Note that the machines that are configured to get their certificates from _Let's Encrypt_ need to have the frontend webserver reloaded in order to switch from the self-signed certificate to the one from _Let's Encrypt_. To do this, run: +Note that the machines that are configured to get their certificates from +_Let's Encrypt_ need to have the frontend webserver reloaded in order to switch +from the self-signed certificate to the one from _Let's Encrypt_. To do this, +run: ``` docker-compose exec frontend nginx -s reload ``` diff --git a/certmgr/scripts/selfsigned.sh b/certmgr/scripts/selfsigned.sh index e58d2849e1..4408499fce 100755 --- a/certmgr/scripts/selfsigned.sh +++ b/certmgr/scripts/selfsigned.sh @@ -15,6 +15,6 @@ fi while true; do renew_selfsigned_cert - sleep 60 & + sleep 24h & wait $! done diff --git a/docker_deploy/group_vars/server/main.yml b/docker_deploy/group_vars/server/main.yml index 414d1cd174..c09336af40 100644 --- a/docker_deploy/group_vars/server/main.yml +++ b/docker_deploy/group_vars/server/main.yml @@ -17,7 +17,6 @@ combine_image_certmgr: "{{ aws_ecr }}/combine/certmgr:{{ image_tag }}" # SSL cert variables specific to Server targets cert_mode: "letsencrypt" -cert_email: "jimgrady.jg@gmail.com" aws_user: "{{ combine_user }}" diff --git a/docker_deploy/group_vars/server/vault.yml b/docker_deploy/group_vars/server/vault.yml index 8689f0ac3a..b1a9504581 100644 --- a/docker_deploy/group_vars/server/vault.yml +++ b/docker_deploy/group_vars/server/vault.yml @@ -1,11 +1,13 @@ $ANSIBLE_VAULT;1.1;AES256 -35366363646461616464306238626561613865386561386164643965666437643335646663373565 -3431356132396230343738313361306432666235333635320a333330663933363137373335616364 -62346435643064306136643737323331373934353430656435383433663436376130376438333230 -3866623866643034310a323563333435656332346335333566356432316638646236333161613063 -39363531396562346130383539316532326534663633643961396162643361386264306634333132 -66343839386564366466656464313730393065373464666138366464356339373739306264666634 -64383937336139306266646435323839373763616539376438386462383339326534623365613733 -65386432386136623664613463613737316237663761616238363438663735663937306536393930 -30633837303635613064393539633461313035343563653331356166343737616435333162386666 -3865383766323566333733663139373936353133396664656264 +33653739636635396139636336336162303937623631353437366465363064653537666633626338 +3834366465396265336466666436343033316535376666630a366631323866356262613736366233 +33303863663066336230643235623430313933316635313337663630316266306464663662386539 +6662356362663133610a306265616463306434346164373866623435316435613938393533363536 +63633930393437396139313031376139313463373734666566326462366139613939396638336565 +33396130373836636239623763383434633434336138613432343430636339643336643266383732 +36316133623566343466353535656562353034376335393663393033363633306438383361376134 +63316432343439623763343661313436343632626334663130336236376633633165623239376366 +36346633323634333232356266633132343465306236393339393562306266373138623831366465 +61386639333961646366343732386661303839303836373264383330343165316633346237623963 +30643861396232613164616536656464636332383265343039613938643862666136303661396437 +38363664646663343538 From b0cf09a3f8a5cb354b62fbbe2c1e765f66f0b2e5 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 16:42:58 -0400 Subject: [PATCH 28/29] Combine docker environment file specifications --- .gitignore | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index f9d04bd7d3..f56cec41df 100644 --- a/.gitignore +++ b/.gitignore @@ -14,9 +14,7 @@ # Generated Docker files for development use docker-compose.yml -.env.backend -.env.frontend -.env.certmgr +.env.* .env*.local nginx/scripts From 66e4674ae6517b7fae0d2058a07fb1249be02441 Mon Sep 17 00:00:00 2001 From: Jim Grady Date: Fri, 16 Oct 2020 16:53:28 -0400 Subject: [PATCH 29/29] Update nginx image to use 1.19 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 246e0806ff..6d7a068225 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ COPY . ./ RUN npm run build # Production environment. -FROM nginx:1.18 +FROM nginx:1.19 RUN mkdir /etc/nginx/templates