Like everybody else on the internet today was the day I started rolling out SSL certificates, via let's encrypt.
The process wasn't too difficult, but I did have to make some changes. Pretty much every website I have runs under its own UID, and I use a proxy to pass content through to the right back-end.
Running 15+ webservers feels like overkill, but it means that the code running start.steve.org.uk cannot read/modify/break the code that is running this blog - because they run as different UIDs.
To start with I made sure that all requests to the top-level /.well-known directory were shunted to a local directory - via this in /etc/apache2/conf-enabled/well-known.conf:
Alias /.well-known/ /srv/well-known/ <Directory "/srv/well-known/"> ForceType text/plain Options Indexes FollowSymLinks MultiViews AllowOverride all AuthType None Require all granted </Directory>
Then configured each proxy to avoid forwarding that path to the back-ends, by adding this to each of the individual virtual-hosts that run proxying:
<Proxy *> Order allow,deny Allow from all </Proxy> ProxyPass /.well-known ! ProxyPass / http://localhost:$port/ ..
Then it came to be time to actually generate the certificates. Rather than using the official client I used a simpler one that allowed me to generate requests easily:
CSR=/etc/apache2/ssl/csr/ KEYS=/etc/apache2/ssl/keys/ CERTS=/etc/apache2/ssl/certs/ # generate a key openssl genrsa 4096 > $KEYS/lumail.key # make a CSR openssl req -new -sha256 -key $KEYS/lumail.key -subj "/" -reqexts SAN \ -config <(cat /etc/ssl/openssl.cnf \ <(printf "[SAN]\nsubjectAltName=DNS:www.lumail.org,DNS:lumail.org")) \ > $CSR/lumail.csr # Do the validation acme_tiny.py --account-key ./account.key --csr $CSR/lumail.csr \ --acme-dir /srv/well-known/acme-challenge/ > $CERTS/lumail.crt.new
And then I was done. Along the way I found some niggles:
- If you have a host that listens on IPv6 only you cannot validate your request - this seems like a clear failure.
- It is assumed that you generate all your certificates in their live-location. e.g. You cannot generate a certificate for foo.example.com on the host bar.example.com.
- If you forward HTTP -> HTTPS the validation fails. I had to setup rewrite rules to avoid this, for example lumail.org contains this:
- RewriteEngine On
- RewriteCond %{REQUEST_URI} !^/.well-known
- RewriteRule ^/(.*) https://lumail.org/$1 [L]
The first issue is an annoyance. The second issue is a real pain. For example *.steve.org.uk listens on one machine except for webmail.steve.org.uk. Since there are no wildcards created a single certificate with Alt-names for a bunch of names such as:
- ..
- blog.steve.org.uk
- start.steve.org.uk
- ..
Then seperately create a certificate for the webmail host - which I've honestly not done yet.
Still I wrote a nice little script to generate SSL for a number of domains, with different Alt-Names, wrapping around the acme_tiny.py script, and regenerating all my deployed certificates is now a two minute job.
(People talk about renewing certificates. I don't see the gain. Just replace them utterly every two months and you'll be fine.)
Tags: letsencrypt, ssl 10 comments
I did similar today also but hide everything behind haproxy as I have some web services that I can't run via Apache.
I use some simple rerouting in haproxy to divert all my host's letsencrypt auth requests into a single private virtual domain in Apache.
Only concern I have ATM is sometimes the cert renewal fails so automating it fully is a bit hit and miss.