In my previous post, I walked through getting Blogger pages moved to GitLab. The impetus for my move was so that I could enable TLS for my blog pages. In this post, I will go over getting a Let’s Encrypt certificate installed. I’m running this from a Mac, so not all the commands will be precisely the same from a Linux or Windows host.

By utilizing the EFF’s Certbot we can have an SSL certificate generated, validated, and signed to be used with out GitLab pages. The validation process requires that we have a special page availble from our site. The name and content of this page is generated by Certbot, but the page location is the regardless of GitLab or any other hosting method.

Prepare Your Gitlab Pages

To accommodate the Let’s Encrypt HTTP validation method, we need to take some steps on the GitLab side of things. In the root your local GitLab repository perform the following:

$ mkdir le
$ git add le
$ git commit -a -m "Temporary Let's Encrypt directory used for validation files."

Modify your .gitlab-ci.yml file to add the following lines under the script: directive.

  - mkdir -p public/.well-known/acme-challenge/
  - cp le/*  public/.well-known/acme-challenge/

Or as mine currently looks:

image: ruby:2.3

  - gem install json
  - gem install rouge
  - gem install jekyll
  - gem install jekyll-sitemap
  - gem install jekyll-feed
  - gem install jekyll-paginate
  - gem install jekyll-gist
  - jekyll build -d public
  - mkdir -p public/.well-known/acme-challenge/
  - cp le/*  public/.well-known/acme-challenge/
    - public
  - master

Now commit and push your changes to GitLab and validate that your build still works.

Get and Run Certbot

Certbot is a python application developed to script many of the operations necessary to get, renew, and manage a Let’s Encrypt certificate. It works best when run on the webserver that serves the domain we are generating a certificate for, but we can work around that.

From our Mac terminal window.

$ brew install certbot

I’m not a fan of running certbot as root, so you need to prepare some bits before running. Otherwise, it’ll bomb out with permissions problems.

Make some directories…

$ mkdir -p ~/tmp/certbot/logs
$ mkdir -p ~/tmp/certbot/work
$ sudo mkdir -p /etc/letsencrypt/
$ sudo chown [YourUserName] /etc/letsencrypt/

Create a config

In your favorite text editor create a cli.ini file with contents similar to the following:

text = True
domains = [your, list, of, domains]
email = [your email address]
rsa-key-size = 4096
logs-dir = /Users/[YourUserName]/tmp/certbot/logs/
work-dir = /Users/[YourUserName]/tmp/certbot/work/

Run Certbot

Step 1 with many parts

$ certbot certonly -a manual -c cli.ini 
Saving debug log to /Users/something/tmp/certbot/logs/letsencrypt.log
Starting new HTTPS connection (1):
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for

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?

Enter Y and hit return

Make sure your web server displays the following content at before continuing:


If you don't have HTTP server configured, you can run the following
command on the target server (as root):

mkdir -p /tmp/certbot/public_html/.well-known/acme-challenge
cd /tmp/certbot/public_html
printf "%s" 1j-ronLqg5OfLqWZ1CiPUq9TfLDhQ6mh28DGH1_9p4s.wyMCCkUN6yUmKBQ7NEJJNtLnXmaTmlbRoIL2TNcIj-s > .well-known/acme-challenge/1j-ronLqg5OfLqWZ1CiPUq9TfLDhQ6mh28DGH1_9p4s
# run only once per server:
$(command -v python2 || command -v python2.7 || command -v python2.6) -c \
"import BaseHTTPServer, SimpleHTTPServer; \
s = BaseHTTPServer.HTTPServer(('', 80), SimpleHTTPServer.SimpleHTTPRequestHandler); \
Press ENTER to continue

Before you hit enter, we need to take care of some business. The above text specifies the validation file we’ll need to host. In a different Terminal window, you’ll need to complete the next few steps.

In the le directory we created earlier, run a modified version of the printf command specified above. Like so:

$ printf "%s" 1j-ronLqg5OfLqWZ1CiPUq9TfLDhQ6mh28DGH1_9p4s.wyMCCkUN6yUmKBQ7NEJJNtL
nXmaTmlbRoIL2TNcIj-s > 1j-ronLqg5OfLqWZ1CiPUq9TfLDhQ6mh28DGH1_9p4s

Notice that in the version we run, the .well-known/acme-challenge is removed from the destination filename. Our gitlab-ci.yml will take care of moving the file to the appropriate location. We just need to add and push the file up to GitLab to make this happen.

$ git add *
$ git commit -a -m "Let's Encrypt validation files."
$ git push

After pushing, watch the CI build process to make sure it completed. Then you can test to set that the file is served from http://[your site]/.well-known/acme-challenge/. If not, check to make sure you’ve created and properly committed the challenge file into your GitLab pages /le directory and you .gitlab-ci.yml is also up to date.

Once you have confirmed that the challenge file is available, you can go back to your certbot terminal window and hit enter.

Starting new HTTP connection (1):
Waiting for verification...
Cleaning up challenges
Generating key (4096 bits): /etc/letsencrypt/keys/0003_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0003_csr-certbot.pem
Non-standard path(s), might not work with crontab installed by your operating system package manager

 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/ Your cert
   will expire on 2017-01-25. 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
 - If you lose your account credentials, you can recover through
   e-mails sent to
 - Your account credentials have been saved in your Certbot
   configuration directory at /etc/letsencrypt. You should make a
   secure backup of this folder now. This configuration directory will
   also contain certificates and private keys obtained by Certbot so
   making regular backups of this folder is ideal.
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:
   Donating to EFF:          

Congratulations. You now have a 4096bit RSA SSL certificate, that’s signed by a recognized public CA, and it’s valid for 90 days. Now you just need to get it setup with your GitLab pages.

Note: If you have multiple domains that you are entering into your Certificate request (e.g.,, then you’ll have each of those domains listed in the configuration. You will also cycle through the challenge/validation process for each of those domains when you run certbot.

Update GitLab with your Certificate

In the GitLab Web UI, navigate to your Pages project. Then click on the gear icon and Pages. Pages

Unfortunately, there is no way to add or update a certificate for an existing domain. You will first need to remove your domain. Then you can click the Add
Domain button.

On the page that comes up, you will enter your domain name, and copy-and-paste the private and full chain keys into the appropriate boxes. You can find the keys in the /etc/letsencrypt/live/[yourdomain]/ directory. You will want the privkey.pem and fullchain.pem files.


After clicking the Create New Domain button, you should be able to hit your site a https://[yoursite]/ with your new certificates. As mentioned, the Let’s Encrypt certificates are only valid for 90 days. In a future post, I’ll go over the renew process and how to hopefully make this less painful.

“Forcing” HTTPS

Gitlab does not force HTTPS. So even with the certificate installed, browsers can hit your pages in an insecure HTTP fashion. We can work around this a bit by throwing some JS into our pages that redirect people to the HTTPS version if they hit us over HTTP. It’s less than perfect, but it is better than nothing. The Javascript code, with some Jekyll wrappings looks like the following:

{% if site.enforce_ssl %}
<script type="text/javascript">
if (window.location.protocol != "https:")
        window.location.protocol = "https";
{% endif %}

For my theme, I added this code to the _includes/scripts.html file. The Jekyll if statements, means that I can have a config that doesn’t require SSL that I use on my local machine for testing and development.

For my deployment to GitLab, I updated the _config.yml file enforce_ssl: true was set. Push these two changes to GitLab then updated my pages appropriately, activating the above JS, and for any modern browser with Javascript turned on, they will get redirected to the secured version of my pages.

In my next related post, I’ll go over renewing your certificate. Since this has to be done every 90 days, automating the process is a good idea. On webservers where certbot can run, this is fairly trivial. With Gitlab pages, we have a few more hurdles to jump over.

It’s been several years since Blogger turned on HTTPS, but only for https://[yourbloghere] resourced blogs. It continues to not support HTTPS for custom domain blogs. While what I post does not need the same level of authorization, authentication, and crytographic controls as a commerce site, it would be nice to encrypt everything. There are a number of options and I initially leaned towards using GitHub. Unfortunately, it suffers from the same problem for its pages as blogger- TLS, except for custom domains. GitLab, while similar to GitHub, does support HTTPS for custom domains, and uses the same git framework. In the end, that’s where I’ve gone.

What I ended up going with is the following:

All scripts and testing were performed from a MacBook Pro running OS X 10.11, but should work from any Linux machine with minimal changes.

Continue reading

While Blogger and GitHub now turn on SSL by default, neither support it for custom domains. Some suggested solutions around this are to put an SSL proxy layer between the browser and the above providers, but that breaks the expected end-to-end encryption that the end user expects and allows for MitM attacks. As such, I won’t mention the company that keeps touting this as a real solution.

I eventually came across GitLabs and happily they support SSL not just on their * pages, but also for custom domains. Their docs and these blog posts really help to get you started.

Unfortunately, while they do support SSL on all their pages and with custom domains, there does not appear to be a way to enforce SSL on all your pages. This means that if someone comes into your page over HTTP they will not get automatically redirected to your secure page. While this is not a big deal for static content that is not handling sensitive information, it does defeat the purpose of encrypting your page in the first place.

Continue reading

As Formula 1 comes out of its “summer break,” the Telenovela of a race series returns to bore the fans. Here are my 5 ideas that could bring the excitement back to the track.

Nearly all the excitement and intrigue exists off the track while the races themselves seem like parades.  Modern tracks with near infinite run-off areas, flailing attempts at cost cutting, ever changing and inconsistently applied rules, a façade of greenwashing, and the FIA’s inability to manage the sport contribute to dreadfully dull races.  The troll that runs the circus continues be more interested in placating dictators than actually putting on a show.

If F1 is supposed to be the pinnacle of motorsport with the most advanced machines then we’d get back active suspension, ABS, traction control, and active aero among other things.  Here are my fantasy changes to the sport to mix things up, make the races less predictable, and generally more exciting.  While I’m sure Bernie Ecclestone would love to be able to charge me for the privilege of commenting on Formula 1, I’m going to do the next best thing and give it to him for free.

Continue reading

Frustration with my home network’s WAN connectivity randomly flapping and being somewhat cheap led me to see if I could get paged without having worry about email relays or paying for your traditional notification service à la PagerDuty (pricey) or VictorOps (unreliable).

The first order of business was to write up a simple script to perform some checks against my home network.  It’s a simple shell script run through cron that maintains a running log of check attempts and results.  Each time it runs, reads the last result, performs new checks, and if the state changed from the previous attempt, then send out an alert.

Continue reading