In the week leading up to the new year I scratched some long standing itches of mine, adding HTTPS support and setting up email for doshitan.com.
HTTPS Everywhere
I’ve wanted to enable HTTPS on doshitan.com since I first started the website, but didn’t really want to shell out much money (especially reoccurring costs) for something which should really be free/one-time cost. The leading free option (that I know of) for a long time was StartSSL, which has a slightly better setup, with free 1-year certificates or charging just for identity verification (which actually costs something) and not for every single certificate you want to generate (which really doesn’t cost anything), but I didn’t want to have to remember to login and renew/regenerate a free certificate every year or give a bunch of personal information to an Israeli company to store for years.
In short, I was waiting for Let’s Encrypt to be generally available. I think there were other automated CAs issuing free/cheap DV certificates before Let’s Encrypt, but Let’s Encrypt is more my style (smart, open, principled, and free). With it entering public beta on December 3rd, it was just a matter of finding some time to play with it. I’m happy to say it was fairly painless to get it all set up and with the proper config settings for nginx, doshitan.com now has an A+ on the SSL Labs test (give it a run yourself).
I’ll go through the general setup, but it’s not terribly interesting so feel free to skip.
Let’s Encrypt Setup
First step was getting the Let’s Encrypt client running. I’m currently running
from git, though there is a packaged version in the experimental repos for
Debian Jessie. As the client stabilizes I’ll move over to the packaged version,
but for now I want to be running the latest code and using letsencrypt-auto
instead of letsencrypt
. So just clone the repo somewhere:
$ git clone https://github.com/letsencrypt/letsencrypt /wherever/
This site is served by nginx, and Let’s Encrypt doesn’t yet have automagical support for nginx like it does for Apache, but all it takes is a few extra command line flags to get it working. Let’s Encrypt certificates are valid for 90 days, so you’ll want to renew them at least every couple of months (~60 days) to be safe. I setup a simple systemd unit to run the requisite commands and a timer unit to trigger it every month, which I’ll move out to every two months after a while. They are as follows:
[Unit]
Description=Generate/renew SSL certificate for example.com
[Service]
Type=oneshot
ExecStart=/wherever/letsencrypt-auto certonly --renew-by-default --agree-tos -a webroot --webroot-path /var/www/example.com -d example.com -d www.example.com --email webmaster@example.com
ExecStart=/bin/systemctl reload nginx
[Unit]
Description=Run letsencrypt-example
[Timer]
OnCalendar=monthly
Persistent=true
[Install]
WantedBy=timers.target
Then enable and start the timer unit:
$ systemctl enable letsencrypt-example.timer
$ systemctl start letsencrypt-example.timer
And that’s it, automatic generation and renewal of a certificate for example.com and www.example.com.
A more general approach
On a bit of a tangent, if you had lots of different sites to generate/renew certificates for, you could break some of this out into template unit with some environment stuff.
[Unit]
Description=Generate/renew SSL certificate for %I
[Service]
Type=oneshot
EnvironmentFile=/etc/sysconfig/%I
ExecStart=/wherever/letsencrypt-auto certonly --renew-by-default --agree-tos -a webroot --webroot-path /var/www/%I --email webmaster@%i.com $OPTIONS
ExecStart=/bin/systemctl reload nginx
[Unit]
Description=Run letsencrypt for %I
[Timer]
OnCalendar=monthly
Persistent=true
[Install]
WantedBy=timers.target
Drop a config file for each domain you’re interested in
$OPTIONS=-d example1.com \
-d www.example1.com \
-d sub.example1.com
Followed by a:
$ systemctl enable letsencrypt@example1.com.timer
$ systemctl start letsencrypt@example1.com.timer
If all the sites follow the same pattern (as most do), you could change a couple of the service file lines to something like:
EnvironmentFile=-/etc/sysconfig/%I
ExecStart=/wherever/letsencrypt-auto certonly --renew-by-default --agree-tos -a webroot --webroot-path /var/www/%I --email webmaster@%i.com -d %i -d www.%i $OPTIONS
So no config file is needed if the site only needs the common stuff.
Nginx
We need to allow nginx to read the certificates. For me it’s easiest to just
give the www-data
user read access with:
$ sudo setfacl -R -m user:www-data:r-x /etc/letsencrypt/archive/ /etc/letsencrypt/live/
A better approach would keep the stuff (or at least the private keys) strictly readable by root only (i.e., the web server starts under root but forks a less privileged process to handle requests and such), but as I only run a static site under nginx, I’m not too worried about something happening that exposes the private key.
After the certificates are readable by nginx, we need to have nginx actually use them. Somewhere in the blocks for both www.example.com and example.com add:
...
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
...
The rest of the ssl settings are gonna vary depending on your needs, but I generally follow the h5bp config with whatever tweaks I need. An important step is to generate a unique Diffie-Hellman parameter with something like:
$ nice -n19 openssl dhparam -out /etc/nginx/ssl/dhparam.pem
Or:
$ nice -n19 certtool --generate-dh-params --outfile /etc/nginx/ssl/dhparam.pem --bits 4096
Once it eventually finishes, gotta make use of it for each SSL-enabled block in nginx with:
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
Ensure it’s all good:
$ sudo nginx -t
Then:
$ systemctl reload nginx
And now the site is set.
I also setup email for the doshitan.com domain. The hardest part was deciding how I wanted to host the mail server. I considered a couple options.
The easiest option is probably just Google Apps as I’ve already been using Gmail has my primary mail service for years, but I really wanted to detangle from Google. While I’ve never had an issue with their services, they are rather omnipresent and I’d rather not contribute to a proprietary mono-culture if I can help it (which is also part of the reason I primary host my code on GitLab instead of GitHub and use DuckDuckGo as my primary search engine).
Dreamhost
I currently register my domains through Dreamhost, partly for historical reasons, but they are also cheap and reputable, so no real reason to change. Dreamhost offers email hosting for any domain registered through them, but I’ve heard some not so good things about their email services. Mail not getting sent, mail not getting received, etc., so I wasn’t too keen on even trying it out.
Self Hosted
I’ve tossed around the idea of self hosting an email server for quite a while actually as it’s not terribly difficult to get something basic setup and I certainly have all the relevant skills to do so, but there are a lot of downsides to hosting it myself as well. The main downside is simply not getting outgoing mail flagged as spam by other mail services. The other big downside is the maintenance. Self hosting gives you a lot of control, but also a lot of responsibility and potential headache that I don’t really want to have to deal with for something I just want to work.
FastMail
I finally settled on FastMail. They are a dedicated email service that strongly supports open standards and have been around forever (well, since 1999). They also have some support for calendar and contacts that I might give a shot, but their email support is the main focus.
The rest
After the host was decided on, it was just a few simple DNS changes to get the mail flowing. And flowing it is! I’ve been quite happy with the performance of FastMail so far, in both their Web app and Android app. IMAP has been good so far too. Haven’t yet given the calendar or contacts stuff a good run, but I suspect those will be just fine too.
Wrap up
Not complicated stuff, but I’m happy to have it all set up now.