April 14, 2019
Why am I choosing this route.
The outcome I’m expecting… An automated process for renewing my SSL certificate for my website https://thecb4.io
￼ HTTPS (HTTP Secure) is transitioning from optional to required across the internet for websites and APIs. Browsers are starting to warn of insecure websites. Apple has started requiring any link accessed in an app to be HTTPS unless you specifically opt out. In order for your website to use HTTPS, you have to have SSL certificates. These certificates confirm your ownership of the domain and that the domain itself can be trusted. Historically, you would obtain SSL certificates through your domain registrar and it was expensive; several hundred dollars if you want to include a wildcard (all subdomains). Recently The Linux Foundation has supported a project called Let’s Encrypt. This provides SSL certificate issuance for free in order to drive adoption. The caveat is that these certificates need to be reviewed on a more frequent basis (every 3 months) than historical certificates (annually). I will be using the certbot command line tool and hosting my website on Gitlab Pages.
Gitlab provides free hosting for static websites similar to GitHub. Gitlab has the advantage of having CI/CD as part of their tooling infrastructure. This allows me to better manage the workflow for deploying my website. Both GitHub and Gitlab require docs to be in a certain directory in order to serve up on pages. With Gitlab I don’t have to have that directory in source control. I can create that directory and copy files into it as part of the deployment process. This also requires me to understand how to deploy a website. My website is very straight forward, I'm simply copying files into a directory.
Much of the work from here on out is a derivative of the work completed by Michael Ummels. I would like to personally thank him for going through the process initially. He provided a framework for using an additional step for GitLab CI that will run the Certbot command on a recurring basis automatically and post the certs to GitLab Pages. Ummels leverages the dns challenge from his DNS service to produce the certificates. I will do the same thing using Digital Ocean.
I love Digital Ocean. They provide several great services, from generic virtual servers all the way to deploying a Kubernetes cluster. Their pricing is great and they good command line tools. One of their features is managing DNS (Domain Name Servers). I can have TLD (top level domain) from a registrar, but all of the aliases, redirects, routing can occurring with Digital Ocean. Why is that important? Digital Ocean has a dns challenge plugin for certbot that allows me to do a dns challenge based request for my SSL certificate. I [simply] have to point Gitlab and Digital Ocean at each other and then certbot can continue the conversation on an ongoing basis through a scheduled CI action. It’s quite a few steps. I’ve included some screen shots to assist. This article is being written in Spring of 2019. Some things may change by the time you read this. I will note which versions of the toolchain I am using.
Alright. Let's get started.
stages: - deploy pages: image: alpine:latest stage: deploy script: - mkdir public - cp -a deployment/client/. public/ artifacts: paths: - public only: - master
Get a domain (Namecheap) from a registrar. Some as cheap at $10 annually.
Add domain to gitlab
Add Domain to Digital Ocean "Add Domain to Digital Ocean")
Use Digital Ocean Nameservers for Namecheap registrar. If you have mail through your registrar, it’s ok. You can point DNS records back at those servers. The Digital Ocean Name Servers are ns[1-3].digitalocean.com
Add an A Record to Digital Ocean pointing to gitlab pages server IP (instead of CNAME). At the time of writing this. The IP Address is 22.214.171.124
doctl compute domain records create space-cowboy.io --record-type A --record-name @ --record-data 126.96.36.199 --record-ttl 1800
doctl compute domain records create space-cowboy.io --record-type TXT --record-name space-cowboy.io --record-data "_gitlab-pages-verification-code.space-cowboy.io TXT gitlab-pages-verification-code=aaaabbbbbcccccdddeeeee" --record-ttl 1800
Both personal access tokens are stored as variables in Gitlab, specifically for CI.
This becomes another stage in the gitlab-ci.yaml file. We also revise the original deploy stage to ignore the schedule.
variables: LC_ALL: "C.UTF-8" DOMAIN: "space-cowboy.io" EMAIL: "firstname.lastname@example.org" stages: - deploy - update pages: image: alpine:latest stage: deploy script: - mkdir public - cp -a deployment/client/. public/ artifacts: paths: - public only: - master except: - schedules update_cert: image: name: thecb4/gitlabcertbotdigitalocean entrypoint: [""] variables: CERT_FILE: "/etc/letsencrypt/live/$DOMAIN/fullchain.pem" KEY_FILE: "/etc/letsencrypt/live/$DOMAIN/privkey.pem" stage: update before_script: - echo "Adding digital ocean token" - echo "dns_digitalocean_token = $DIGITAL_OCEAN_TOKEN" > .secret - chmod 600 .secret script: - bin/certbot.sh "$DOMAIN" "$EMAIL" - "curl --silent --fail --show-error --request PUT --header \"Private-Token: $GITLAB_CI_TOKEN\" --form \"certificate=@$CERT_FILE\" --form \"key=@$KEY_FILE\" https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/pages/domains/$DOMAIN" only: - schedules
#!/usr/bin/env sh certbot certonly \ --non-interactive \ --agree-tos \ --dns-digitalocean --dns-digitalocean-credentials .secret \ --dns-digitalocean-propagation-seconds 60 \ --email "$2" \ -d "$1" \ -d "*.$1"
Did I achieve my outcome? I have a custom TLD with HTTPS that is hosted in Gitlab using Let’s Encrypt for SSL that auto-renews every month with a DNS challenge against Digital Ocean’s name servers. I never have to touch the renew step again. I can focus on publishing content.
What did I learn:
This is very specific solution to the problem. However you can use these three services in multiple ways that I will get into in later posts.
Questions, comments, concerns? Find me on Twitter. @thecb4