The problem: Legacy certbot-auto is no longer supported, and ACMEv1 challenge has been
deprecated and removed. On newer systems, you can simply upgrade certbot using
apt-get install --upgrade-only certbot
and you'll be good to go.
However, on legacy servers (Ubuntu 14), it isn't possible to upgrade. Installing manually isn't good
either because python3
uses python3.4 which has a broken pip installation (ugh).
Docker to the rescue!
First, confirm what your existing letsencrypt cronjob is (if applicable) with sudo crontab -l
. In my case I see
that there is a pre-hook and post-hook for starting and stopping the local nginx (which is managed by gitlab):
0 3 * * 1 /home/user/certbot/certbot-auto renew --no-self-upgrade --pre-hook "gitlab-ctl stop nginx" --post-hook "gitlab-ctl start nginx" --quiet
To stop nginx, I can do that prior to running the docker command (or pass along the binaries into docker so docker can access them). To utilize stand-alone mode (which is the default) we'll also have to forward port 80.
Install docker. That's about it actually. sudo apt-get install docker.io
and confirm it works:
docker --version
Docker version 1.6.2, build 7c8fca2
service docker status
docker start/running, process 6044
In the event that you try running it, get an error about not being able to bind to port 80, the image may not successfully shut down. In this case, running the same command again may give you a docker error that the image is already created.
The easiest thing is to just confirm the process is still running, delete the image, and run it again. Alternatively, try running with docker start
instead of docker run
(which creates AND starts an image).
sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
49ca53fe8746 certbot/certbot:latest "certbot renew --sta 3 minutes ago certbot
sudo docker rm certbot
certbot
The instructions from certbot give us a good starting point: https://certbot.eff.org/docs/install.html#running-with-docker
Using --dry-run
is really good because it'll let you experiment without writing files. I found that first,
nginx was still running (so I had to stop it to free up port 80 sudo gitlab-ctl stop nginx
). Then, port 80 wasn't accessible so I had
to forward local port 80 to the docker instance.
sudo docker run -it --rm --name certbot \
-v "/etc/letsencrypt:/etc/letsencrypt" \
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
-p 80:80 \
certbot/certbot renew --standalone --dry-run
With any luck you will see that this works just fine. There are some complaints about gitlab-ctl stop nginx
not found, but they are just warnings (we are doing that part manually). Then run it again without --dry-run
.
After it is all said and done, turn nginx back on (sudo gitlab-ctl start nginx
).
Thus, the whole script should now be:
ssl-renew.sh
gitlab-ctl stop nginx
docker run -it --rm --name certbot \
-v "/etc/letsencrypt:/etc/letsencrypt" \
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
-p 80:80 \
certbot/certbot renew --standalone
gitlab-ctl start nginx
which can be saved in a file and ran by cron.
crontab -l
0 3 * * 1 /home/user/ssl-renew.sh
Setting up a fresh certificate is similar. Be sure to stop nginx first, though!
sudo /etc/init.d/nginx stop
sudo docker run -it --rm --name certbot \
-v "/etc/letsencrypt:/etc/letsencrypt" \
-v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
-p 80:80 \
certbot/certbot --standalone certonly
(follow the directions, then confirm cert is present in /etc/letsencrypt/live/)
Update nginx config to use the new certificate:
upstream unicorn_website_production {
server unix:/tmp/unicorn.website_production.sock fail_timeout=0;
}
# https redirect example
server {
listen 443;
server_name www.website.com;
add_header Strict-Transport-Security "max-age=0;";
rewrite ^(.*) https://website.com$1 permanent;
}
server {
listen 443;
server_name website.com;
#add_header Strict-Transport-Security "max-age=0;";
ssl on;
ssl_certificate /etc/letsencrypt/live/website.com/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/website.com/privkey.pem; # managed by Certbot
root /home/user/website/production/current/public;
add_header Strict-Transport-Security "max-age=31536000";
location ^~ /assets/ {
gzip_static on;
expires max;
add_header Cache-Control public;
}
try_files $uri @unicorn;
location @unicorn {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn_website_production;
}
error_page 500 502 503 504 /500.html;
client_max_body_size 4G;
keepalive_timeout 10;
}