docker-zulip icon indicating copy to clipboard operation
docker-zulip copied to clipboard

docker: Restore ability to generate SSL certs with LetsEncrypt.

Open klardotsh opened this issue 1 year ago • 6 comments

Zulip Server 4.9+ regressed Docker setups by always creating a /etc/letsencrypt directory in the top layer of the Docker container, meaning it couldn't be symlinked over from the volume mount. Since that volume mount has useful properties (providing and/or overriding LetsEncrypt setting), restore it and copy the in-image configs into the volume as defaults if and only if those files don't already exist in the volume.

Fixes #381.

Testing Plan

I applied the following diff:

diff --git a/docker-compose.yml b/docker-compose.yml
index 45f5949..9c2b16a 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -49,7 +49,8 @@ services:
     volumes:
       - "redis:/data:rw"
   zulip:
-    image: "zulip/docker-zulip:6.1-0"
+    #image: "zulip/docker-zulip:6.1-0"
+    image: "zulip:local"
     restart: unless-stopped
     build:
       context: .
@@ -66,7 +67,8 @@ services:
       DB_HOST: "database"
       DB_HOST_PORT: "5432"
       DB_USER: "zulip"
-      SSL_CERTIFICATE_GENERATION: "self-signed"
+      #SSL_CERTIFICATE_GENERATION: "self-signed"
+      SSL_CERTIFICATE_GENERATION: "certbot"
       SETTING_MEMCACHED_LOCATION: "memcached:11211"
       SETTING_RABBITMQ_HOST: "rabbitmq"
       SETTING_REDIS_HOST: "redis"

... and then built a zulip:local image from this repo (with no build args). It appears to work: /var/lib/docker/volumes/docker-zulip_zulip/_data/letsencrypt/ contains the cli.ini that originates from a Zulip Server install, and I see the following log output:

docker-compose log output
docker-zulip-zulip-1      | Scheduling LetsEncrypt cert generation ...
docker-zulip-zulip-1      | Generating self-signed certificates ...
docker-zulip-zulip-1      | + is_redhat=false
docker-zulip-zulip-1      | + '[' -e /etc/redhat-release ']'
docker-zulip-zulip-1      | + SSLDIR=/etc/ssl
docker-zulip-zulip-1      | + KEYFILE=/etc/ssl/private/zulip.key
docker-zulip-zulip-1      | + CERTFILE=/etc/ssl/certs/zulip.combined-chain.crt
docker-zulip-zulip-1      | + '[' -n '' ']'
docker-zulip-zulip-1      | + '[' -z '' ']'
docker-zulip-zulip-1      | + '[' -e /etc/ssl/private/zulip.key ']'
docker-zulip-zulip-1      | + '[' -e /etc/ssl/certs/zulip.combined-chain.crt ']'
docker-zulip-zulip-1      | + rm -f /etc/ssl/private/zulip.key /etc/ssl/certs/zulip.combined-chain.crt
docker-zulip-zulip-1      | + [[ localhost.localdomain =~ ^(([0-9]+\.){3}[0-9]+)(:[0-9]+)?$ ]]
docker-zulip-zulip-1      | + [[ localhost.localdomain =~ ^\[([^][]*)](:[0-9]+)?$ ]]
docker-zulip-zulip-1      | + [[ localhost.localdomain =~ ^([^:]+)(:[0-9]+)?$ ]]
docker-zulip-zulip-1      | + subjectAltName=DNS:localhost.localdomain
docker-zulip-zulip-1      | ++ mktemp
docker-zulip-zulip-1      | + config=/tmp/tmp.5Dxc2cuRb4
docker-zulip-zulip-1      | + trap 'rm -f "$config"' EXIT
docker-zulip-zulip-1      | + cat
docker-zulip-zulip-1      | + '[' false = true ']'
docker-zulip-zulip-1      | + apt-get install -y openssl
docker-zulip-zulip-1      | Reading package lists...
docker-zulip-zulip-1      | Building dependency tree...
docker-zulip-zulip-1      | Reading state information...
docker-zulip-zulip-1      | openssl is already the newest version (1.1.1f-1ubuntu2.17).
docker-zulip-zulip-1      | openssl set to manually installed.
docker-zulip-zulip-1      | 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
docker-zulip-zulip-1      | + openssl req -new -x509 -config /tmp/tmp.5Dxc2cuRb4 -days 3650 -nodes -sha256 -out /etc/ssl/certs/zulip.combined-chain.crt -keyout /etc/ssl/private/zulip.key
docker-zulip-zulip-1      | Generating a RSA private key
docker-zulip-zulip-1      | ..+++++
docker-zulip-zulip-1      | ..................+++++
docker-zulip-zulip-1      | writing new private key to '/etc/ssl/private/zulip.key'
docker-zulip-zulip-1      | -----
docker-zulip-zulip-1      | + chmod 644 /etc/ssl/certs/zulip.combined-chain.crt
docker-zulip-zulip-1      | + chmod 640 /etc/ssl/private/zulip.key
docker-zulip-zulip-1      | + rm -f /tmp/tmp.5Dxc2cuRb4
docker-zulip-zulip-1      | Self-signed certificate generation succeeded.
docker-zulip-zulip-1      | Certificates configuration succeeded.

klardotsh avatar Feb 28 '23 19:02 klardotsh

This didn’t work for me.

On the first docker compose up, it generated a certificate, but did not restart nginx, so it was still serving the self-signed certificate.

After docker compose down; docker compose up, I got

docker-zulip-zulip-1      | === Begin Run Phase ===
docker-zulip-zulip-1      | Waiting for nginx to come online before generating certbot certificate ...
docker-zulip-zulip-1      | Starting Zulip using supervisor with "/etc/supervisor/supervisord.conf" config ...
…
docker-zulip-zulip-1      | Generating LetsEncrypt/certbot certificate ...
docker-zulip-zulip-1      | + case " $os_id $os_id_like " in
docker-zulip-zulip-1      | + apt-get update
…
docker-zulip-zulip-1      | + apt-get install -y certbot
…
docker-zulip-zulip-1      | + certbot certonly --webroot --webroot-path=/var/lib/zulip/certbot-webroot/ -d andersk.zulipdev.org -m [email protected] --agree-tos --force-interactive --no-eff-email
docker-zulip-zulip-1      | Saving debug log to /var/log/letsencrypt/letsencrypt.log
docker-zulip-zulip-1      | Plugins selected: Authenticator webroot, Installer None
docker-zulip-zulip-1      | Cert not yet due for renewal  
docker-zulip-zulip-1      | 
docker-zulip-zulip-1      | You have an existing certificate that has exactly the same domains or certificate name you requested and isn't close to expiry.
docker-zulip-zulip-1      | (ref: /etc/letsencrypt/renewal/andersk.zulipdev.org.conf)
docker-zulip-zulip-1      | 
docker-zulip-zulip-1      | What would you like to do?
docker-zulip-zulip-1      | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
docker-zulip-zulip-1      | 1: Keep the existing certificate for now
docker-zulip-zulip-1      | 2: Renew & replace the cert (limit ~5 per 7 days)
docker-zulip-zulip-1      | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
docker-zulip-zulip-1      | Select the appropriate number [1-2] then [enter] (press 'c' to cancel): An unexpected error occurred:
docker-zulip-zulip-1      | EOFError
docker-zulip-zulip-1      | Please see the logfiles in /var/log/letsencrypt for more details.

and it was still serving a self-signed certificate.

After docker compose stop; docker compose up, I got

docker-zulip-zulip-1      | === Begin Initial Configuration Phase ===
docker-zulip-zulip-1      | ln: failed to create symbolic link '/etc/letsencrypt': File exists
docker-zulip-zulip-1 exited with code 1

andersk avatar Feb 28 '23 21:02 andersk

:thinking: I'll take another look. I haven't seen either of those error messages so far, but maybe I need more up-stop-up-stop cycles to trigger it.

klardotsh avatar Feb 28 '23 22:02 klardotsh

I reproed at least the lack of nginx restarting issue on a DO droplet. I'm now playing with #142 and a potential revert of the underlying zulip/zulip changes (I think there's a path to --standalone followed by a conversion to webroot, but want to confirm on "bare metal" before going the rest of the way down that rabbit hole integrating the changes into Zulip)

klardotsh avatar Mar 01 '23 21:03 klardotsh

Further discussion: https://chat.zulip.org/#narrow/stream/3-backend/topic/certbot/near/1516306

andersk avatar Mar 01 '23 22:03 andersk

This should really be merged. Currently, if you rebuild the docker image you lose your certs and its a whole mess to get them back

napter avatar Nov 14 '23 15:11 napter

The certbot brokenness is a high importance bug that needs to be solved, but this PR does not solve it. See above comments and the linked discussion.

andersk avatar Nov 15 '23 07:11 andersk