Consul doesn’t actually use TLS certificates to configure agents; it uses them to secure communication between agents and between agents and clients.

Here’s how you set it up:

Consul agents communicate with each other over the network. By default, this communication is unencrypted. To secure it, you can enable TLS. This involves generating or obtaining TLS certificates and configuring your Consul agents to use them.

Let’s walk through a typical setup.

Generating Certificates

You’ll need a Certificate Authority (CA) certificate, a server certificate, and a server key for each Consul server node. For agent-to-agent communication, you’ll also need client certificates and keys.

First, we need a CA.

openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -sha256 -days 1825 -out ca.crt -subj "/CN=Consul Test CA"

This creates a private key ca.key and a self-signed root certificate ca.crt.

Next, we create a server certificate. This certificate needs to be valid for all IP addresses and DNS names that your Consul servers will use to communicate.

# For server 1
openssl genrsa -out server1.key 2048
openssl req -new -key server1.key -out server1.csr -subj "/CN=consul-server-1" -addext "subjectAltName=IP:192.168.1.101,DNS:consul-server-1,DNS:localhost"
openssl x509 -req -in server1.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server1.crt -days 365 -sha256 -extfile <(printf "subjectAltName=IP:192.168.1.101,DNS:consul-server-1,DNS:localhost")

# Repeat for server2 and server3, adjusting CN and subjectAltName

You’ll also need client certificates for any Consul clients that need to connect to the Consul servers.

# For client 1
openssl genrsa -out client1.key 2048
openssl req -new -key client1.key -out client1.csr -subj "/CN=consul-client-1" -addext "subjectAltName=IP:192.168.1.102,DNS:consul-client-1"
openssl x509 -req -in client1.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client1.crt -days 365 -sha256 -extfile <(printf "subjectAltName=IP:192.168.1.102,DNS:consul-client-1")

After generating, you’ll have ca.crt, server1.crt, server1.key, client1.crt, client1.key, etc.

Configuring Consul Agents

Now, you need to tell your Consul agents about these certificates. This is done in the agent’s configuration file (e.g., consul.json).

For server agents, you’ll need to configure the tls section:

{
  "server": true,
  "datacenter": "dc1",
  "data_dir": "/opt/consul",
  "bind_addr": "192.168.1.101",
  "client_addr": "192.168.1.101,127.0.0.1",
  "retry_join": ["192.168.1.101", "192.168.1.102", "192.168.1.103"],
  "tls": {
    "defaults": {
      "cipher_suites": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"
    },
    "cert_file": "/etc/consul.d/certs/server1.crt",
    "key_file": "/etc/consul.d/certs/server1.key",
    "ca_file": "/etc/consul.d/certs/ca.crt",
    "verify_incoming": true,
    "verify_outgoing": true,
    "verify_server_hostname": true
  }
}

Key parameters here:

  • cert_file: Path to the agent’s server certificate.
  • key_file: Path to the agent’s server private key.
  • ca_file: Path to the CA certificate used to sign the server certificates.
  • verify_incoming: If true, Consul will verify that incoming connections are from clients with valid certificates signed by the ca_file.
  • verify_outgoing: If true, Consul will verify that outgoing connections to other Consul agents are from agents with valid certificates signed by the ca_file.
  • verify_server_hostname: If true, Consul will verify that the hostname in the certificate of the server it’s connecting to matches the hostname it expects.

For client agents, the configuration is similar, but you’ll point to the client’s certificate and key:

{
  "server": false,
  "datacenter": "dc1",
  "data_dir": "/opt/consul",
  "bind_addr": "192.168.1.102",
  "client_addr": "192.168.1.102,127.0.0.1",
  "retry_join": ["192.168.1.101", "192.168.1.102", "192.168.1.103"],
  "tls": {
    "defaults": {
      "cipher_suites": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"
    },
    "cert_file": "/etc/consul.d/certs/client1.crt",
    "key_file": "/etc/consul.d/certs/client1.key",
    "ca_file": "/etc/consul.d/certs/ca.crt",
    "verify_incoming": true,
    "verify_outgoing": true
  }
}

Notice verify_server_hostname is omitted for clients by default, as clients typically connect to servers by IP address.

Distributing Certificates

You need to distribute the ca.crt to all Consul nodes (servers and clients). Each Consul server node needs its own serverX.crt and serverX.key. Each Consul client node needs its own clientX.crt and clientX.key.

Crucially, the private keys (.key files) must be kept secret and only accessible by the Consul agent process. Permissions should be set to 400 or 600.

chmod 400 /etc/consul.d/certs/*.key
chmod 644 /etc/consul.d/certs/*.crt

Starting Consul Agents

After placing the configuration files and certificates in the correct locations (e.g., /etc/consul.d/), you can start your Consul agents.

consul agent -config-dir=/etc/consul.d/

Verification

Once agents are running, you can verify TLS is active by checking the agent logs for TLS handshake messages or by using consul members.

consul members -tls -ca-cert=/path/to/ca.crt -client-cert=/path/to/client.crt -client-key=/path/to/client.key

If you omit the client certificate/key and are running consul members on a node that is not part of the Consul cluster, you might need to provide client credentials if verify_incoming is enabled on the servers.

The most surprising thing about Consul’s TLS configuration is that verify_server_hostname is disabled by default for client agents. This means that if a malicious actor could intercept traffic and present a certificate for consul.example.com on a different IP address, a client agent might still trust it if the certificate is signed by the correct CA. It’s generally a good practice to enable this for clients if they connect via DNS names.

The next step is often to configure TLS for API requests to the Consul HTTP API.

Want structured learning?

Take the full Consul course →