HTTPS is important, even for static sites. I assume you’re already on board with that.1
Naturally, this blog should also use HTTPS then.
GitLab Pages automatically uses HTTPS if you are running your page on their domains (i.e. <username>.gitlab.io
). But if you’re using a custom domain, you have to bring your own certificate to make connections secure.
While GitLab has some help articles on this, getting my own TLS certificate set up with my page was not as easy as I would have liked. So I collected my steps in this article, hoping it will be useful to the next person trying the same thing.
The information in this article is based mainly on GitLab’s docs and the Tutorial linked from there, as well as the documentation of Let’s Encrypt and certbot
.
What You Need Before Starting
If you want to follow these steps, I’ll assume you have the following set up and working:
- a GitLab Pages site using Jekyll, which is built with GitLab’s CI,
- a custom domain (can be a top domain or a subdomain), already set up with your GitLab Pages site, and
- a local computer (Mac or Linux) on which we will create the certificate.
Note: I’m using a Mac for this, but other than the installation method for certbot
, Linux should work just the same.
What We Are Doing
Our goal is to set up a custom TLS certificate for the GitLab Pages site. We are going to get such a certificate from Let’s Encrypt, a services that provides free TLS certificates with automated provisioning.
Let’s Encrypt provides a command-line tool called certbot
, with which you can request new certificates and renew existing ones.
certbot
is usually intended to be run on the machine that also hosts the web server, but GitLab Pages provides no shell acces, so that’s not an option for us. Luckily, certbot
also has a manual mode to manage certificates for a different machine, which is what we’re going to do.
Let’s Get Securin’
First of all, we’re going to need
certbot
.- If you don’t already have it, download and install it.
- For macOS, Homebrew makes this as easy as running
brew install certbot
in a terminal.
Next, create a folder to work in. You can name it whatever you like; we’re going to refer to it as
<cert>
. Inside this folder, create three folders:config
,logs
, andwork-dir
.- You’re going to need to keep the contents of this
<cert>
, so don’t put it in/tmp
or the like. - Other than that, it doesn’t really matter where you put
<cert>
. Inside your user folder works, for example. - Why do we do this? Since
certbot
is intended to be used on the web server machine, it keeps its data in/etc/letsencrypt
by default (see its docs). Writing to this folder requires superuser privileges, which we don’t really need for our purposes. Makingcertbot
work in a folder that we own eliminates this need.
- You’re going to need to keep the contents of this
Side Note: Please remember to use Let’s Encrypt’s staging environment to try things, so you don’t accidentally hit their rate limits.
Open a terminal in
<cert>
.Run
certbot certonly --work-dir=work-dir/ --logs-dir=logs/ --config-dir=config/ -a manual -d <custom_domain> --staging
, replacing<custom_domain>
with the actual domain you want to use.- Note: There’s no need to create an account with Let’s Encrypt beforehand.
certbot
prompts you to enter your email address and creates an account on the fly. The credentials for this will be stored on the config folder we gave it, which you should back up afterwards (I’ll remind you later).
- Note: There’s no need to create an account with Let’s Encrypt beforehand.
certbot
runs interactively and will prompt you with a few questions. Read and answer them until it tells you to create a file:- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Create a file containing just this data: -X12Aa37zsmEoAavGa-biE5rtiw9n64A_DOmngOGSFw.D9as8-6Jg-RXy9dzO54V65GyBnoT324nMzFyjOIW0rPg And make it available on your web server at this URL: http://<custom_domain>/.well-known/acme-challenge/-X12Aa37zsmEoAavGa-biE5rtiw9n64A_DOmngOGSFw - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Press Enter to Continue
Leave this terminal window open and waiting for the moment.
In the local clone of your Jekyll page, create a plain file with the following contents based on
certbots
’s instructions:--- layout: null permalink: /.well-known/acme-challenge/-X12Aa37zsmEoAavGa-biE5rtiw9n64A_DOmngOGSFw/index.html --- -X12Aa37zsmEoAavGa-biE5rtiw9n64A_DOmngOGSFw.D9as8-6Jg-RXy9dzO54V65GyBnoT324nMzFyjOIW0rPg
- You can call the file whatever you want, since the output filename is determined by the
permalink
we specify. I usedlets-encrypt-token.html
, for example. - Specifying the layout “null” in the YAML frontmatter tells Jekyll not to generate anything around the contents of this file. Instead, it just writes a plain text file with the given content at the given permalink URL.
- In contrast to Gitlab’s tutorial, we need to use a permalink URL like
/.well-known/acme-challenge/<challenge>/index.html
instead of/.well-known/acme-challenge/<challenge>.html
. - Why? GitLab Pages does not serve the contents of HTML (or text) files at URLs without file extension (at least not anymore), but
certbot
does not use a file extension when fetching the URL. - Luckily,
certbot
does follow HTTP redirects, so we can use this little trick of making a folder<challenge>
with anindex.html
instead of a file called<challenge>.html
.
- You can call the file whatever you want, since the output filename is determined by the
If you want, you can compile your Jekyll site locally and test if the file shows up.
Commit the change and push, then wait for the GitLab CI build to finish.
- Note: Sometimes it may take a few minutes for changes to be visible.
- If the challenge token is not available after several minutes, pause at this point and double-check your build and deployment setup, and fix it if necessary.
Once you manually confirmed that the challenge token is available at the URL that
certbot
expects, return to thecertbot
terminal window and press Enter.Success!
- This should be it — you should get a success message from
certbot
. certbot
tells you where you can find the certificate and private key files.- It also tells you to make regular backups of its config folder, because that contains both the certificate files and your Let’s Encrypt account credentials! Now is a good time to do just that.
- This should be it — you should get a success message from
Unfortunately, the certificate we just created is based on Let’s Encrypt’s staging system, so it’s not actually trusted and therefore cannot be used to test it on GitLab Pages.
Therefore, re-generate the certificate with the same steps, but without the
--staging
switch incertbot
’s invocation.- Note: If you followed these instructions and have successfully created a staging certificate (and used the same domain for it),
certbot
asks if you really want to replace this certificate (because it’s not expiring yet). Since the staging certificate is of no further use, you can confirm to replace it.
- Note: If you followed these instructions and have successfully created a staging certificate (and used the same domain for it),
Once that is done, you can now add the certificate to your GitLab Pages site as described in the docs.
- In the “Certificate (PEM)” field, place the contents of the
fullchain.pem
file thatcertbot
generated. - In the “Key (PEM)” field, place the contents of the
privkey.pem
file thatcertbot
generated.
- In the “Certificate (PEM)” field, place the contents of the
Note: You may have to trigger another GitLab CI deployment (and wait a little while) to make these changes take effect.
Well done! Connections to your blog are now secure(r)!
What’s next
As you may have read, Let’s Encrypt certificates expire after 90 days. This means that we’re going to have to renew them regularly.
Being the lazy programmer that I am, I don’t want to do the above dance every 2-3 months when this comes up. So I’m likely going to automate this at some point.2
If and when that happens, I might just write another article about it!
So… stay tuned, I guess? 😄
Update: I have since published a post on automating renewal of Let’s Encrypt certificates with GitLab CI!
Footnotes
- If you want to know more, I recommend the section Why should I care about HTTPS? of GitLab’s documentation and the Electronic Frontier Foundation’s article Encrypting the Web).↩
- There are a one or two tools that purport to automate generation and renewal of Let’s Encrypt certificates for GitLab Pages sites, but none of them looked both simple enough and trustworthy enough to me to jump right on them. Most likely, I’m going to use them as inspiration and set up my own tooling.↩