Let’s kick this off with a nice simple one; while I was choosing hosting for the current incarnation of this site, I found that GitLab Pages allow you to supply a certificate to serve the site using HTTPS. I ended up choosing GitLab for this, and a few other reasons, so here’s how I got a shiny green padlock to adorn my site URL.
Assuming I’ve not been ignoring the Let’s Encrypt cert expiration emails, you should be able to access this site using HTTPS; if you’re not already doing so, give it a try. Your browser should inform you (as seen above) that the connection to the site is encrypted and that the identity of the site is certified by Let’s Encrypt. Let’s Encrypt is a free Certificate Authority (CA) that automatically issues certs for anyone that requests them, on the condition that they can prove they have control of the domains they’re requesting certs for.
The intended use of Let’s Encrypt is to configure the web server itself to automatically request the certs it needs, whenever they need renewing. To this end, many of the (numerous) available clients focus on automation and integration. Since I don’t have control of (or want responsibility for) my web server, this isn’t useful for my particular setup. What I’m looking for is the simplest and easiest process to manually generate certs that I can paste into GitLab’s configuration pages.
Since I am almost exclusively using Windows and I upload to my web server by pushing direct to my GitLab site repository, I started by looking at shell script based clients – hoping to be able to run one in Git for Windows’ MSYS. I settled on dehydrated because it runs perfectly happily in MSYS with only minor modifications, does not require root access or any installation procedure, and allows the user to specify a hook script to deploy challenge files to their server.
That last part is critical; when requesting certs from Let’s Encrypt, it will issue a challenge, requiring the client to host a challenge file at a specific HTTP address under the domain in question (there is another challenge mode that requires updating DNS records, but I have no means of automating that, so we’ll ignore it). I wrote a script (see below) to write these challenge files (and automatically push them to GitLab) in a format that Jekyll will process correctly.
For convenience, I threw the script and my config files for dehydrated (see their documentation for details) into a “letsencrypt” git repository, and pulled in the source for dehydrated itself (which, again, is self contained and does not need to be built or installed) as a git submodule to simplify setup and track the version I’m using. With this arrangement, it’s a dead simple process to get new certs on any machine where I have access to the site git repository:
Clone the “letsencrypt” repository:
~/projects/web $ git clone --recurse-submodules email@example.com:haddoncd/letsencrypt.git Cloning into 'letsencrypt'... ~/projects/web $ cd letsencrypt/
Generate a new account key:
~/projects/web/letsencrypt $ dehydrated/dehydrated --register --accept-terms # INFO: Using main config file /c/Users/Haddon/projects/web/letsencrypt/config + Generating account key... + Registering account key with ACME server... + Done!
Start the certificate request process:
~/projects/web/letsencrypt $ dehydrated/dehydrated -c # INFO: Using main config file /c/Users/Haddon/projects/web/letsencrypt/config Processing haddon.org.uk with alternative names: www.haddon.org.uk
At this point, the script will push some changes to the website and wait for us:
hook: acme-challenge pushed, please wait for jekyll to deploy Press enter to continue...
Let’s head over to GitLab see if it’s deploying:
Running with gitlab-ci-multi-runner 1.11.1 (a67a225) on docker-auto-scale (4e4528ca) Using Docker executor with image ruby:2.1 ... Pulling docker image ruby:2.1 ... Running on runner-4e4528ca-project-2108767-concurrent-0 via runner-4e4528ca-machine-1490786569-856e1e7f-digital-ocean-2gb... Cloning repository... Cloning into '/builds/haddoncd/haddoncd.gitlab.io'... Checking out 3922b597 as master... Skipping Git submodules setup $ gem install jekyll Successfully installed public_suffix-2.0.5 Successfully installed addressable-2.5.0 Successfully installed colorator-1.1.0 Successfully installed sass-3.4.23 Successfully installed jekyll-sass-converter-1.5.0 Successfully installed rb-fsevent-0.9.8 Building native extensions. This could take a while...
Right. I’ll go get a drink.
Successfully installed ffi-1.9.18 Successfully installed rb-inotify-0.9.8 Successfully installed listen-3.0.8 Successfully installed jekyll-watch-1.5.0 Successfully installed kramdown-1.13.2 Successfully installed liquid-3.0.6 Successfully installed mercenary-0.3.6 Successfully installed forwardable-extended-2.6.0 Successfully installed pathutil-0.14.0 Successfully installed rouge-1.11.1 Successfully installed safe_yaml-1.0.4 Successfully installed jekyll-3.4.3 18 gems installed $ jekyll build -d public/ Configuration file: /builds/haddoncd/haddoncd.gitlab.io/_config.yml Source: /builds/haddoncd/haddoncd.gitlab.io Destination: public/ Incremental build: disabled. Enable with --incremental Generating... done in 0.069 seconds. Auto-regeneration: disabled. Use --watch to enable. Uploading artifacts... public: found 13 matching files Uploading artifacts to coordinator... ok id=13222812 responseStatus=201 Created token=MB5LZp66 Job succeeded
Ah, it’s finished. Time to hit enter back in our terminal. GitLab says that only took 1 minute 7 seconds; which is just shy of a thousand times longer than Jekyll took to generate the HTML. At least I won’t be compelled to spend time getting the HTML generation to run fast!
After repeating step 4 for each other domain being requested, dehydrated should report success, meaning our certificate files have been written to the
+ Requesting certificate... + Checking certificate... + Done! + Creating fullchain.pem... hook: Not deploying certs... + Done!
Now we just need to delete our existing domains in the GitLab pages configuration, and add each of them back with the new cert:
Here’s the hook script I use for deploying challenge files to a Jekyll git repository.