» Creating Certificates

Correctly configuring TLS can be a complex process, especially given the wide range of deployment methodologies. This guide will provide you with a production ready TLS configuration.

The first step to configuring TLS for Consul is generating certificates. In order to prevent unauthorized cluster access, Consul requires all certificates be signed by the same Certificate Authority (CA). This should be a private CA and not a public one like Let's Encrypt as any certificate signed by this CA will be allowed to communicate with the cluster.

» Reference Material

» Estimated Time to Complete

20 minutes

» Prerequisites

This guide assumes you have cfssl installed (be sure to install cfssljson as well).

» Steps

» Step 1: Create Certificate Authority

There are a variety of tools for managing your own CA, like the PKI secret backend in Vault, but for the sake of simplicity this guide will use cfssl. You can generate a private CA certificate and key with cfssl:

# Generate a default CSR
$ cfssl print-defaults csr > ca-csr.json

Change the key field to use RSA with a size of 2048

{
    "CN": "example.net",
    "hosts": [
        "example.net",
        "www.example.net"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "US",
            "ST": "CA",
            "L": "San Francisco"
        }
    ]
}
# Generate the CA's private key and certificate
$ cfssl gencert -initca ca-csr.json | cfssljson -bare consul-ca

The CA key (consul-ca-key.pem) will be used to sign certificates for Consul nodes and must be kept private. The CA certificate (consul-ca.pem) contains the public key necessary to validate Consul certificates and therefore must be distributed to every node that requires access.

» Step 2: Generate and Sign Node Certificates

Once you have a CA certificate and key you can generate and sign the certificates Consul will use directly. TLS certificates commonly use the fully-qualified domain name of the system being identified as the certificate's Common Name (CN). However, hosts (and therefore hostnames and IPs) are often ephemeral in Consul clusters. Not only would signing a new certificate per Consul node be difficult, but using a hostname provides no security or functional benefits to Consul. To fulfill the desired security properties (above) Consul certificates are signed with their region and role such as:

To create certificates for the client and server in the cluster with cfssl, create the following configuration file as cfssl.json to increase the default certificate expiration time:

{
  "signing": {
    "default": {
      "expiry": "87600h",
      "usages": [
        "signing",
        "key encipherment",
        "server auth",
        "client auth"
      ]
    }
  }
}
# Generate a certificate for the Consul server
$ echo '{"key":{"algo":"rsa","size":2048}}' | cfssl gencert -ca=consul-ca.pem -ca-key=consul-ca-key.pem -config=cfssl.json \
    -hostname="server.global.consul,localhost,127.0.0.1" - | cfssljson -bare server

# Generate a certificate for the Consul client
$ echo '{"key":{"algo":"rsa","size":2048}}' | cfssl gencert -ca=consul-ca.pem -ca-key=consul-ca-key.pem -config=cfssl.json \
    -hostname="client.global.consul,localhost,127.0.0.1" - | cfssljson -bare client

# Generate a certificate for the CLI
$ echo '{"key":{"algo":"rsa","size":2048}}' | cfssl gencert -ca=consul-ca.pem -ca-key=consul-ca-key.pem -profile=client \
    - | cfssljson -bare cli

Using localhost and 127.0.0.1 as subject alternate names (SANs) allows tools like curl to be able to communicate with Consul's HTTP API when run on the same host. Other SANs may be added including a DNS resolvable hostname to allow remote HTTP requests from third party tools.

You should now have the following files:

  • cfssl.json - cfssl configuration.
  • consul-ca.csr - CA signing request.
  • consul-ca-key.pem - CA private key. Keep safe!
  • consul-ca.pem - CA public certificate.
  • cli.csr - Consul CLI certificate signing request.
  • cli-key.pem - Consul CLI private key.
  • cli.pem - Consul CLI certificate.
  • client.csr - Consul client node certificate signing request for the global region.
  • client-key.pem - Consul client node private key for the global region.
  • client.pem - Consul client node public certificate for the global region.
  • server.csr - Consul server node certificate signing request for the global region.
  • server-key.pem - Consul server node private key for the global region.
  • server.pem - Consul server node public certificate for the global region.

Each Consul node should have the appropriate key (-key.pem) and certificate (.pem) file for its region and role. In addition each node needs the CA's public certificate (consul-ca.pem).

Please note you will need the keys for the CLI if you choose to disable HTTP (in which case running the command consul members will return an error). This is because the Consul CLI defaults to communicating via HTTP instead of HTTPS. We can configure the local Consul client to connect using TLS and specify our custom keys and certificates using the command line:

$ consul members -ca-file=consul-ca.pem -client-cert=cli.pem -client-key=cli-key.pem -http-addr="https://localhost:9090" 

(The command is assuming HTTPS is configured to use port 9090. To see how you can change this, visit the Configuration page)

This process can be cumbersome to type each time, so the Consul CLI also searches environment variables for default values. Set the following environment variables in your shell:

$ export CONSUL_HTTP_ADDR=https://localhost:9090
$ export CONSUL_CACERT=consul-ca.pem
$ export CONSUL_CLIENT_CERT=cli.pem
$ export CONSUL_CLIENT_KEY=cli-key.pem
  • CONSUL_HTTP_ADDR is the URL of the Consul agent and sets the default for -http-addr.
  • CONSUL_CACERT is the location of your CA certificate and sets the default for -ca-file.
  • CONSUL_CLIENT_CERT is the location of your CLI certificate and sets the default for -client-cert.
  • CONSUL_CLIENT_KEY is the location of your CLI key and sets the default for -client-key.

After these environment variables are correctly configured, the CLI will respond as expected.