Generate Free SSL Certs in Go

January 29th, 2016

Managing SSL certificates has been a pain for years. Since everyone these days uses HTTPS, choosing a certificate authority, paying them and deploying the updated certificates every year has become a chore for almost every system administrator. The Let's Encrypt organization is looking to change that. You can now generate free SSL certs for your domain names automatically without running anything on your server. This is awesome.

Previously, to use Let's Encrypt, you needed to run the agent on a server that your domain name points to in order to verify ownership of the domain. What would be way better is if you could run the agent on any server, generate the certs and do whatever you want with them.

Let's Encrypt recently added support for DNS-based verification, so that is now possible. Let's see how that works.

Generating a Certificate

letsencrypt-reg to register for Let's Encrypt and letsencrypt-cert to generate the cert. The examples are short and meant to be easily adapted to whatever environment you have. This implementation requires that you use We're going to use the amazingly useful lego library which will interface with Let's Encrypt for us. I've made a couple of short example programsto register for Let's Encrypt andto generate the cert. The examples are short and meant to be easily adapted to whatever environment you have. This implementation requires that you use CloudFlare for your DNS, which is also free, but it could be easily made to work with other DNS providers (such as Route53).

letsencrypt-reg.json , which can then be used to generate certificates. To get started, you need to create a Let's Encrypt user. To do this, you can download letsencrypt-reg and put it in your GOPATH. This is an example Go program to create a Let's Encrypt user and save the user info to a file,, which can then be used to generate certificates.

GO15VENDOREXPERIMENT = 1 go build letsencrypt - reg

And run it:

. / letsencrypt - register

It will ask you for your email address and save the user information in letsencrypt-reg.json .

DOMAIN , CLOUDFLARE_EMAIL , CLOUDFLARE_API_KEY . You can get CLOUDFLARE_API_KEY from To generate the certificate, use letsencrypt-cert . It takes the following environment variables:. You can getfrom https://www.cloudflare.com/a/account/my-account . Build it and run it:

GO15VENDOREXPERIMENT = 1 go build letsencrypt - certificate

export DOMAIN = example . com export CLOUDFLARE_EMAIL = you @ example . com export CLOUDFLARE_API_KEY = randomsstuff . / letsencrypt - cert

The lego library will connect to CloudFlare during this process and add a TXT record to verify that you own the domain. When Let's Encrypt sees that record, it will issue the certificate. It seems to be a little flaky so you may need to wait a couple of minutes if it fails before retrying. Since the DNS feature was recently added, it's likely there are some bugs in lego or Let's Encrypt that will be fixed soon.

When it runs successfully, it will create cert.pem and key.pem . To try out your new cert, you can start an HTTPS server:

package main import ( "log" "net/http" ) func handler ( w http . ResponseWriter , req * http . Request ) { w . Header ( ) . Set ( "Content-Type" , "text/plain" ) w . Write ( [ ] byte ( "This is an example server.

" ) ) } func main ( ) { http . HandleFunc ( "/" , handler ) log . Printf ( "About to listen on 10443. Go to https://127.0.0.1:10443/" ) err : = http . ListenAndServeTLS ( ":10443" , "cert.pem" , "key.pem" , nil ) if err ! = nil { log . Fatal ( err ) } }

go get goroutines.com/ssl-https-serve

And test it out with:

curl - - insecure https : //localhost:10443/

If you have the server running with the correct domain name pointed to it, you can remove the --insecure option and change the domain name and it should work with no errors.

Automation

The best setup though, is to do this automatically once per week so that your certs are always up to date. Since no two server configurations are alike, it's hard to have a general thing that works for everyone. One way would be to pick a random time of day (to reduce load spikes on Let's Encrypt's servers) and create a cron job that runs once per week at that time. Let's Encrypt has a ratelimit of 5 certs issued per domain per week, so this means you will be significantly under that limit.