Publish and secure your blog for free using GitLab Pages

I would like to write a small guide of recommendations for host your static website (blog, portfolio, landing page, etc.) in GitLab.com. Why not GitHub.com?, basically because of this open Feature Request, you can not (for the moment) use your own SSL Certificates and custom domain in GitHub Pages. You should also read my previous article where I describe why I decided to do this.

This guide is divided into 4 major topics that I will proceed to document.

1. Content hosting

Source code of my entire blog is hosted in GitLab here, you just need to follow GitLab rules and good practices to host your content using GitLab.

Three important points to keep in mind about hosting your content in GitLab:

1.1 Configuration file

You should create in the root folder of your content a GitLab’s configuration file, the name of the file is important,
must be called .gitlab-ci.yml and with this file you’ll tell GitLab what to do with your content.

For instance, to build a plain HTML site, this file configuration should be enough:

1
2
3
4
5
6
7
8
9
10
11
pages:
stage: deploy
script:
# Copy all content and paste it into public folder
- mkdir public
- tar --exclude=./public -cf - . | cat | (cd public && tar xbf 1 -)
artifacts:
paths:
- public
only:
- master

Important sections from previous configuration file are:

  • script section: You will indicate a list of commands to build your content in GitLab.
  • artifact section: This should point always to public folder.

If you want to read a real and complete GitLab configuration file you can use this one from my blog based on Hexo.io, a static site generator in Node.js. Also in here you can search default projects containing GitLab configuration files.

1.2 Default public url

What will be the default public url of my published content?. For me this question was tricky to answer, for a better understanding you will need to read about GitLab namespaces here and also here.

The rule of thumb is one public site per GitLab user or per GitLab group.

If my GitLab account is https://gitlab.com/xcafebabe I have the chance to publish content through the public GitLab url https://xcafebabe.gitlab.io.

If you understand this convention, the moment you push your content to your repository in GitLab and your repo includes a proper GitLab configuration file, your publishded content will be automatically available in the url http://YOURUSERNAME.gitlab.io

For instance, my blog is reachable at https://xcafebabe.gitlab.io and @xcafebabe is my GitLab user account.

1.3 Repository name

As usual you can have multiple repositories in your GitLab account, but only one of the repositories is taken into account to publish your content. The GitLab convention says that the repository should be called username.gitlab.io, this is why the repository that hosts my blog is called xcafebabe.gitlab.io.

In the case you have a group and several projects within, the same rule applies. You must create a repository called yourgrup.gitLlab.io in order to publish the content in the url yourgroup.gitlab.io.

To check if GitLab is serving your content through the default url convention, you could use the Administration panel of your repository, go to Settings > Pages and check if the url is available.

So far you can publish your content in a GitLab public url (it includes GitLab SSL Certificates), however, if you want also to link your content with a custom domain, keep the reading.

2. Domain Managment

Once your content is available in a Gitab url you should create a new DNS A record in your DNS configuration and should point to GitLab server’s IP (IP: 52.167.214.135). I’m using 1and1 as my Domain dealer, so this is how it should looks the setup.

1.- Click on DNS Configuration

2.- In A/AAAA records section instead of 1and1 IP address select Another IP and introduce this one 52.167.214.135

3.- In CNAME records section you should add a new entry pointing to your Gitlab public url

To prove that the domain has been configured correctly, you can create a new domain on the Administration page of your Gitlab repository, go to Settings > Pages and click on New Domain.

Add your domain and just save it. If everything has correctly setup, you will be able to access your published content through your domain url.

3. Create and validate your Let’s encrypt certificates

We are almost done. In fact, if you’re not interested in Https, you can finish the guide here, but since you’re brave I recommend you to continue.

It’s time now to create and validate a TLS certificate for your domain. The fastest and most effective way is to rely on Docker, some time ago I wrote about this fabulous technology in my article Docker & Raspberry Pi, perfect combo!.

If you have Docker installed, using the official Let’s Encrypt Docker image called Cerbot you can generate and validate certificates in less than five minutes.

3.1 Generate certificates

1
2
mkdir certs
sudo docker run -it --rm -v certs:/etc/letsencrypt certbot/certbot certonly -a manual -d www.YOUR-DOMAIN.com -d YOUR-DOMAIN.com -m luis@toub.es

From previous command in Line 2, with -d option you should add your domain with and without www and with -m option you should add the email address to receive renewal reminders.

Below is how it looks the output after executes previous commands

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for toub.es

-------------------------------------------------------------------------------
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.

Are you OK with your IP being logged?
-------------------------------------------------------------------------------
(Y)es/(N)o: y

-------------------------------------------------------------------------------
Create a file containing just this data:

ei63vo4bg79x9Z7P-1lyAPyvDCa3FodyPgUpRt8MA3M.06r7ceL-YTSS1-1sGwLTDjwRQ228gt8lCHuJQBUTKqE

And make it available on your web server at this URL:

http://toub.es/.well-known/acme-challenge/ei63vo4bg79x9Z7P-1lyAPyvDCa3FodyPgUpRt8MA3M

-------------------------------------------------------------------------------
Press Enter to Continue

3.2 Validate Certificates

Cerbot script will challengue you to prove you are the owner of the domain using a server file validation.

From previous step 3.1 do not press ENTER until you do what the script says:

1
2
3
4
5
6
7
Create a file containing just this data:

ei63vo4bg79x9Z7P-1lyAPyvDCa3FodyPgUpRt8MA3M.06r7ceL-YTSS1-1sGwLTDjwRQ228gt8lCHuJQBUTKqE

And make it available on your web server at this URL:

http://toub.es/.well-known/acme-challenge/ei63vo4bg79x9Z7P-1lyAPyvDCa3FodyPgUpRt8MA3M

Now, steps involved to publish this validation file in your GitLab public url will depend on how complex your build process is.

If you are using a static site generator like Jekyll, in this article you will find steps to publish the file required by Let’s Encrypt.

If you are using Hexo.io, you may find useful to see how the plugin hexo-processor-static works and also how I’m using it in my repository.

Assuming that your build process uses the entire repository as published content, you could have done something similar to the next commands to validate Let’s Encrypt challenge.

1
2
3
4
5
6
mkdir -p .well-known/acme-challenge
cd .well-known/acme-challenge
echo ei63vo4bg79x9Z7P-1lyAPyvDCa3FodyPgUpRt8MA3M.06r7ceL-YTSS1-1sGwLTDjwRQ228gt8lCHuJQBUTKqE > ei63vo4bg79x9Z7P-1lyAPyvDCa3FodyPgUpRt8MA3M
git add .
git commit -m "Letsencrypt validation challengue"
git push

When you push this code to Gitlab you will see in the Pipelines view a new build job running.

Once GitLab completes the build you could check if everything has went well by accessing the Cerbot url from step 3.1, in my case is http://toub.es/.well-known/acme-challenge/ei63vo4bg79x9Z7P-1lyAPyvDCa3FodyPgUpRt8MA3M

If you see the content, it’s time to tell Cerbot to finish the process we started in step 3.1

This is what would happen if Certbot validates the authentication process.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at:
/etc/letsencrypt/live/theme.bytesauce.com/fullchain.pem
Your key file has been saved at:
/etc/letsencrypt/live/theme.bytesauce.com/privkey.pem
Your cert will expire on 2018-01-18. To obtain a new or tweaked
version of this certificate in the future, simply run certbot
again. To non-interactively renew *all* of your certificates, run
"certbot renew"
- If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le

Since we use Docker with option -v, new TLS certificates are available in the cert folder (created in step 3.1) and will remain valid for 90 days. If you provide an email address to Let’s Encrypt when you create the certicates, they will automatically send you expire notices when your certificate is coming up for renewal.

3.3 Upload certicates to Gitlab

Now we just need to upload the certificate and the key to GitLab. Go to Settings -> Pages inside your project, remove the old domain you have created in step 2 and add a new one with the same domain, but now you’ll also include content from your certificate.

Paste the content of ./cert/live/YOURDOMAIN.org/fullchain.pem (you’ll need sudo to read the file) to the “Certificate (PEM)” field and ./cert/live/YOURDOMAIN.org/privkey.pem (also needs sudo) to the “Key (PEM)” field.

Repeat this step to include www sub-domain in Gitlab (using same generated certificates).

4. Profit

Congratulations on completing the necessary steps. If you have any questions I will gladly answer them in the comments.

Happy coding!