Skip to content

Commit

Permalink
setup: Add certbot support.
Browse files Browse the repository at this point in the history
The task is to generate a self-signed cert so Zulip can be started, then
to wait until Zulip is up before using certbot to generate new certs.
Zulip needs to be up so it can meet certbot's challenge. Using a deploy
hook, certs are persisted in the data directory. The same applies to
renewal.

Tweaked by tabbott mostly to edit comments remove an unnecessary
setting before merging.

Fixes #120.
  • Loading branch information
jeaye authored and timabbott committed Jul 24, 2018
1 parent e8526c2 commit 3b5d63b
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 10 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Expand Up @@ -84,6 +84,7 @@ RUN apt-get -q dist-upgrade -y && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

COPY entrypoint.sh /sbin/entrypoint.sh
COPY certbot-deploy-hook /sbin/certbot-deploy-hook

VOLUME ["$DATA_DIR"]
EXPOSE 80 443
Expand Down
10 changes: 5 additions & 5 deletions README.md
Expand Up @@ -168,11 +168,11 @@ which you need to encode in the YAML file. For example,
comma-separated list of the backend names
(E.g. `"EmailAuthBackend,GitHubAuthBackend"`).

**SSL Certificates**. By default, the image will generate a
self-signed cert. We
[will soon also support certbot](https://github.com/zulip/docker-zulip/issues/120)
for this, just like we do in normal Zulip installations
(contributions welcome!).
**SSL Certificates**. By default, the image will generate a self-signed cert.
You can set `SSL_CERTIFICATE_GENERATION: "certbot"` within `docker-compose.yml`
to enable automatically-renewed Let's Encrypt certificates. By using certbot
here, you are agreeing to the [Let's Encrypt
ToS](https://community.letsencrypt.org/tos).

You can also provide an SSL certificate for your Zulip server by
putting it in `/opt/docker/zulip/zulip/certs/` (by default, the
Expand Down
29 changes: 29 additions & 0 deletions certbot-deploy-hook
@@ -0,0 +1,29 @@
#!/usr/bin/env bash

set -euo pipefail

backup() {
if [ -e "$1" ]; then
# If the user is setting up our automatic certbot-management on a
# system that already has certs for Zulip, use some extra caution
# to keep the old certs available. This naming is consistent with Zulip's
# own setup-certbot backups.
mv -f --backup=numbered "$1" "$1".setup-certbot || true
fi
}

source_cert_dir=/etc/letsencrypt/live/"$SETTING_EXTERNAL_HOST"
dest_cert_dir="$DATA_DIR"/certs

# Persist the certs to the data directory.
backup "$dest_cert_dir"/zulip.key
backup "$dest_cert_dir"/zulip.combined-chain.crt
cp -f "$source_cert_dir"/privkey.pem "$dest_cert_dir"/zulip.key
cp -f "$source_cert_dir"/fullchain.pem "$dest_cert_dir"/zulip.combined-chain.crt

# Ensure nginx can find them.
ln -nsf "$dest_cert_dir"/zulip.key /etc/ssl/private/zulip.key
ln -nsf "$dest_cert_dir"/zulip.combined-chain.crt /etc/ssl/certs/zulip.combined-chain.crt

# Restart various services so the new certs can be used.
supervisorctl restart nginx
47 changes: 42 additions & 5 deletions entrypoint.sh
Expand Up @@ -168,12 +168,18 @@ configureCerts() {
;;
esac
if [ ! -e "$DATA_DIR/certs/zulip.key" ] && [ ! -e "$DATA_DIR/certs/zulip.combined-chain.crt" ]; then

if [ "$GENERATE_CERTBOT_CERT" = "True" ]; then
echo "Certbot not yet supported"
exit 1
# TODO: Run setup-certbot and move /etc/letsencrypt to the data dir?
# /home/zulip/deployments/current/setup/setup-certbot "$SETTING_EXTERNAL_HOST"
elif [ "$GENERATE_SELF_SIGNED_CERT" = "True" ]; then
# Zulip isn't yet running, so the certbot's challenge can't be met.
# We'll schedule this for later.
echo "Scheduling LetsEncrypt cert generation ..."
GENERATE_CERTBOT_CERT_SCHEDULED=True

# Generate self-signed certs just to get Zulip going.
GENERATE_SELF_SIGNED_CERT=True
fi

if [ "$GENERATE_SELF_SIGNED_CERT" = "True" ]; then
echo "Generating self-signed certificates ..."
mkdir -p "$DATA_DIR/certs"
/home/zulip/deployments/current/scripts/setup/generate-self-signed-cert "$SETTING_EXTERNAL_HOST"
Expand Down Expand Up @@ -407,12 +413,43 @@ runPostSetupScripts() {
set -e
echo "Post setup scripts execution succeeded."
}
function runCertbotAsNeeded() {
if [ ! "$GENERATE_CERTBOT_CERT_SCHEDULED" = "True" ]; then
echo "Certbot is not scheduled to run."
return
fi

echo "Waiting for nginx to come online before generating certbot certificate ..."
while ! curl -sk "$SETTING_EXTERNAL_HOST" >/dev/null 2>&1; do
sleep 1;
done

echo "Generating LetsEncrypt/certbot certificate ..."

# Remove the self-signed certs which were only needed to get Zulip going.
rm -f "$DATA_DIR"/certs/zulip.key "$DATA_DIR"/certs/zulip.combined-chain.crt

ZULIP_CERTBOT_DEPLOY_HOOK="/sbin/certbot-deploy-hook"

# Accept the terms of service automatically.
/home/zulip/deployments/current/scripts/setup/setup-certbot \
--agree-tos \
--hostname="$SETTING_EXTERNAL_HOST" \
--email="$SETTING_ZULIP_ADMINISTRATOR" \
--deploy-hook "$ZULIP_CERTBOT_DEPLOY_HOOK"

echo "LetsEncrypt cert generated."
}
bootstrappingEnvironment() {
echo "=== Begin Bootstrap Phase ==="
waitingForDatabase
zulipFirstStartInit
zulipMigration
runPostSetupScripts
# Hack: We run this in the background, since we need nginx to be
# started before we can create the certificate. See #142 for
# details on how we can clean this up.
runCertbotAsNeeded &
echo "=== End Bootstrap Phase ==="
}
# END appRun functions
Expand Down

0 comments on commit 3b5d63b

Please sign in to comment.