├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── crontab ├── init.sh └── monthly.sh /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:jessie 2 | 3 | WORKDIR /root 4 | 5 | RUN apt-get update \ 6 | && apt-get install -y wget python xz-utils cron git \ 7 | && rm -rf /var/lib/apt/lists/* 8 | 9 | RUN wget https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-176.0.0-linux-x86_64.tar.gz \ 10 | && tar -zxf google-cloud-sdk-176.0.0-linux-x86_64.tar.gz \ 11 | && ./google-cloud-sdk/install.sh --usage-reporting false \ 12 | && rm google-cloud-sdk-176.0.0-linux-x86_64.tar.gz 13 | 14 | RUN wget https://github.com/go-acme/lego/releases/download/v3.0.2/lego_v3.0.2_linux_amd64.tar.gz \ 15 | && tar -xzf lego_v3.0.2_linux_amd64.tar.gz lego \ 16 | && rm lego_v3.0.2_linux_amd64.tar.gz 17 | 18 | COPY init.sh /root/init.sh 19 | COPY monthly.sh /root/monthly.sh 20 | 21 | COPY crontab /etc/cron.d/letsencrypt 22 | 23 | CMD /root/init.sh && cron -f 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 BloomAPI 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: 4 | docker build . -t bloomapi/letsencrypt-gcloud-balancer:latest 5 | 6 | push: build 7 | docker push bloomapi/letsencrypt-gcloud-balancer:latest 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Let's Encrypt Google Compute HTTP Load Balancer Docker Updater 2 | =========== 3 | 4 | Run from a machine already in GCP where the machine itself is authorized to update HTTPS load balancer certificates. This container also requires you use GCP as your DNS provider as it uses GCP-DNS-based Let's Encrypt verification. 5 | 6 | docker run --env GCE_PROJECT=gcp-project-name --env LETSENCRYPT_EMAIL=my@email.com --env TARGET_PROXY=name-of-gcp-target-https-proxy --env DOMAINS_LIST="-d domains.list -d where.each -d is.prefixed.by.a.dash.d" bloomapi/letsencrypt-gcloud-balancer 7 | 8 | In production, consider mounting a persistent volume at `/root/.lego` so you don't loose your Let's Encrypt credentials / certs. That said, the scripts currently only work when one certificate / key pair is stored in the container. If you want to change the domains while using persistant storage, make sure you clear the certs and keys out of the `/root/.lego/certificates` directory. 9 | 10 | If you are testing, its also worth setting `--env USE_STAGING_SERVER=true` to avoid being rate limited by Let's Encrypt for the month. Keep in mind that since this uses DNS-based verification, it depends on the expiration of DNS TXT records. While this wont matter in production, while testing, you may need to wait 120 seconds between tests. 11 | 12 | This container will attempt to renew certificates once a month. The container will also try to have an initial issuing of the certs on first run. 13 | 14 | ### Required Environment Variables 15 | 16 | * `GCE_PROJECT` Your GCP/GCE project 17 | * `LETSENCRYPT_EMAIL` Email to use for Let's Encrypt registration 18 | * `TARGET_PROXY` Name of your GCP https proxy. Find it with `gcloud compute target-https-proxies list` after you've already created a HTTPS load balancer frontent 19 | * `DOMAINS_LIST` A list of domains. Each domain must be prefixed with `-d`. If you want multiple domains, just seperate with a space as demonstrated above. 20 | 21 | ### Optional Variables 22 | 23 | * `USE_STAGING_SERVER` if set, We'll use the Let's Encrypt staging server. This wont issue usable certs, but will allow you to use / reuse the same domains list. *Warning* if you re-create this container more than 5 times in a month without a persistent volume, you will be rate limited and you wont be able to get more certificates until the next month. 24 | * `CERT_ID_PREFIX` will create new certificates with this string followed by `cert-${random string}` 25 | 26 | 27 | Leave the docker container running, and it will attempt to update the cert once a month and remove the older cert once the new cert is installed. 28 | -------------------------------------------------------------------------------- /crontab: -------------------------------------------------------------------------------- 1 | 0 0 1 * * root /root/monthly.sh 2 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | # Set Staging server if parameter is set 6 | USE_STAGING_SERVER="${USE_STAGING_SERVER+--server=https://acme-staging-v02.api.letsencrypt.org/directory}" 7 | 8 | # On Startup 9 | # Lets Encrypt Initialize 10 | ./lego $USE_STAGING_SERVER --dns-timeout 30 -k rsa2048 -m $LETSENCRYPT_EMAIL -dns gcloud $DOMAINS_LIST -a run 11 | 12 | # Create certificate chain 13 | CERT=$(ls -1 /root/.lego/certificates | grep crt\$ | grep -m1 -v issuer) 14 | CERT_ISSUER=$(ls -1 /root/.lego/certificates | grep crt\$ | grep -m1 issuer) 15 | KEY=$(ls -1 /root/.lego/certificates | grep key\$) 16 | cat /root/.lego/certificates/$CERT /root/.lego/certificates/$CERT_ISSUER > cert.crt 17 | 18 | # Create name for new certificate in gcloud 19 | CERT_ID=${CERT_ID_PREFIX}cert-$(cat /dev/urandom | tr -dc 'a-z' | fold -w 16 | head -n 1) 20 | OLD_CERT_ID=$(./google-cloud-sdk/bin/gcloud -q compute target-https-proxies list --filter "name=${TARGET_PROXY}" | sed -n 2p | awk '{print $2}') 21 | 22 | # Generate new gcloud certificate and attach to https proxy 23 | ./google-cloud-sdk/bin/gcloud -q compute ssl-certificates create $CERT_ID --certificate=cert.crt --private-key=/root/.lego/certificates/$KEY 24 | ./google-cloud-sdk/bin/gcloud -q compute target-https-proxies update $TARGET_PROXY --ssl-certificates $CERT_ID 25 | rm cert.crt 26 | 27 | # Remove old, unused certificate 28 | ./google-cloud-sdk/bin/gcloud -q compute ssl-certificates delete $OLD_CERT_ID 29 | -------------------------------------------------------------------------------- /monthly.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | 6 | # Set Staging server if parameter is set 7 | USE_STAGING_SERVER="${USE_STAGING_SERVER+--server=https://acme-staging-v02.api.letsencrypt.org/directory}" 8 | 9 | # Lets Encrypt Renew 10 | ./lego $USE_STAGING_SERVER --dns-timeout 30 -k rsa2048 -m $LETSENCRYPT_EMAIL -dns gcloud $DOMAINS_LIST -a renew 11 | 12 | # Create certificate chain 13 | CERT=$(ls -1 /root/.lego/certificates | grep crt\$ | grep -m1 -v issuer) 14 | CERT_ISSUER=$(ls -1 /root/.lego/certificates | grep crt\$ | grep -m1 issuer) 15 | KEY=$(ls -1 /root/.lego/certificates | grep key\$) 16 | cat /root/.lego/certificates/$CERT /root/.lego/certificates/$CERT_ISSUER > cert.crt 17 | 18 | # Create name for new certificate in gcloud 19 | CERT_ID=${CERT_ID_PREFIX}cert-$(cat /dev/urandom | tr -dc 'a-z' | fold -w 16 | head -n 1) 20 | OLD_CERT_ID=$(./google-cloud-sdk/bin/gcloud -q compute target-https-proxies list --filter "name=${TARGET_PROXY}" | sed -n 2p | awk '{print $2}') 21 | 22 | # Generate new gcloud certificate and attach to https proxy 23 | ./google-cloud-sdk/bin/gcloud -q compute ssl-certificates create $CERT_ID --certificate=cert.crt --private-key=/root/.lego/certificates/$KEY 24 | ./google-cloud-sdk/bin/gcloud -q compute target-https-proxies update $TARGET_PROXY --ssl-certificates $CERT_ID 25 | rm cert.crt 26 | 27 | # Remove old, unused certificate 28 | ./google-cloud-sdk/bin/gcloud -q compute ssl-certificates delete $OLD_CERT_ID 29 | --------------------------------------------------------------------------------