Let's Encrypt is a new certificate authority. It is a huge improvement over the manual complex process of acquiring and deploying an HTTPS server. The process is now:

  • Free
  • Automatic (no more login to sites, filling forms, concatenating certificates)

This article is focusing on a neat feature that makes acquiring certs even easier. Let's Encrypt supports multiples identifier validation challenges. These challenges provide the server with assurance that an account key holder is also the entity that controls an identifier:

  • HTTP (http-01)
  • TLS with Server Name Indication (tls-sni-01)
  • DNS (dns-01)

If each of these approaches have their advantages and inconveniences, I find the DNS challenge to be very convenient when you want to request certificates on a machine that is not the one serving the requested domain.

  • It avoids the chicken/egg issue of starting a web server configured with TLS before having the certificate and the key
  • Proxy requests from all the web servers to a central location
  • Get the certificate / key on a machine and distribute them on a cluster of web servers

Prerequisites

You will need the following prerequisites:

  • GO 1.6 installed
  • Lego GO letsencrypt (ACME) client go get -u github.com/xenolf/lego
  • gcloud auth login

Usage

Lego has a built-in support for a large spectrum of dns providers:

  • cloudfare
  • digitalocean
  • dnssimple
  • dyn
  • gandi
  • googlecloud
  • namecheap
  • route53
  • vultr

In the example below we are going to use gcloud.
I would recommend you use --server=https://acme-staging.api.letsencrypt.org/directory during your dry run because letsencrypt.org does some rate limiting that limits the number of times you can run the following commands.

You can obtain a certificate like this:

GCE_PROJECT="example" GCE_DOMAIN="le.test.example.com" lego \
    --email="yann.malet@example.com" \
    --domains="le.test.example.com" \
    --dns="gcloud" \
    --server=https://acme-v01.api.letsencrypt.org/directory \
    run

The command line above will create a directory called .lego by default and will ask you to accept the TOS. Inside this folder you will get the following structure:

.lego/
├── accounts
│   ├── acme-staging.api.letsencrypt.org
│   │   └── yann.malet@example.com
│   │       ├── account.json
│   │       └── keys
│   │           └── yann.malet@example.com.key
│   └── acme-v01.api.letsencrypt.org
│       └── yann.malet@example.com
│           ├── account.json
│           └── keys
│               └── yann.malet@example.com.key
└── certificates
    ├── le.test.example.com.crt
    ├── le.test.example.com.json
    └── le.test.example.com.key

The following command renews your certificate:

GCE_PROJECT="example" GCE_DOMAIN="le.test.example.com" lego \
    --email="yann.malet@example.com" \
    --domains="le.test.example.com" \
    --dns="gcloud" \
    --server=https://acme-v01.api.letsencrypt.org/directory \
    renew

Finally you need to copy and configure your certificates on your web servers. Here it is a simple GO TLS server that you can use to test your new certificate.

package main

import (
  "io"
  "log"
  "net/http"
  "os"
)

func HelloServer(w http.ResponseWriter, req *http.Request) {
  hostname, err := os.Hostname()
  if err != nil {
    log.Fatal("Hostname err:", err)
  }
  io.WriteString(w, "hello, world! from: ")
  io.WriteString(w, hostname)
}

func main() {
  log.Printf("About to listen on 443. Go to https://127.0.0.1:443/")
  http.HandleFunc("/", HelloServer)
  err := http.ListenAndServeTLS(
    ":443",
    "le.test.example.com.crt",
    "le.test.example.com.key",
    nil)
  if err != nil {
    log.Fatal("ListenAndServe: ", err)
  }
}

When the GO program above is compiled with GO 1.6 you get A overall rating by the excellent Qualys SSL labs test server and native support for HTTP2.