Generate mTLS certificates for Nomad using Vault
You can use consul-template in your Nomad cluster to integrate with Vault's PKI Secrets Engine to generate and renew dynamic X.509 certificates. By using this method, you enable each node to have a unique certificate with a relatively short time-to-live (ttl). This feature, along with automatic certificate rotation, allows you to safely and securely scale your cluster while using mutual TLS (mTLS).
In this guide, your goal will be to secure your existing Nomad cluster with mTLS. To accomplish this, you will configure Vault's PKI secrets engine to create both a root and intermediate CA. You will also use consul-template to fetch, renew, and periodically rotate your mTLS certificates on your Nomad nodes.
Prerequisites
To perform the tasks described in this guide, you need to have
a Nomad environment with Consul and Vault installed. You can use this repository to provision a sandbox environment. This tutorial will assume a cluster with one server node and three client nodes.
You will also need consul-template installed on your nodes.
Note
This tutorial is for demo purposes and is only using a single Nomad server with a Vault server configured alongside it. In a production cluster, 3 or 5 Nomad server nodes are recommended along with a separate Vault cluster. Consult the [Vault Reference Architecture][vault-ra] to learn how to securely deploy a Vault cluster.
Prepare Vault
If you already have a Vault environment that you are integrating with, you will be able to skip ahead to "Log in to Vault"
Initialize Vault server
Run the following command to initialize Vault server and receive an unseal key and initial root token (if you are running the environment provided in this guide, the Vault server is co-located with the Nomad server). Be sure to note the unseal key and initial root token as you will need these two pieces of information.
The vault operator init
command above creates a single Vault unseal key for
convenience. For a production environment, it is recommended that you create at
least five unseal key shares and securely distribute them to independent
operators. The vault operator init
command defaults to five key shares and a
key threshold of three. If you provisioned more than one server, the others will
become standby nodes but should still be unsealed.
Unseal Vault
Run the following command and then provide your unseal key to Vault.
The output of unsealing Vault will look similar to the following.
Log in to Vault
Use the login command to authenticate yourself against Vault using the initial root token you received earlier. You will need to authenticate to run the necessary commands to write policies, create roles, and configure your root and intermediate CAs.
If your login is successful, the vault login
command will generate output
similar to this.
Prepare the PKI environment
Generate the root CA
Enable the PKI secrets engine at the pki
path.
Tune the PKI secrets engine to issue certificates with a maximum time-to-live (TTL) of 87600 hours.
Note
This tutorial uses a common and recommended pattern which is to have one mount act as the root CA and to use this CA only to sign intermediate CA CSRs from other PKI secrets engines (which you will create in the next few steps). For tighter security, you can store your CA outside of Vault and use the PKI engine only as an intermediate CA.
Generate the root certificate and save the certificate as CA_cert.crt
.
Generate the intermediate CA and CSR
Enable the PKI secrets engine at the pki_int
path.
Tune the PKI secrets engine at the pki_int
path to issue certificates with a
maximum time-to-live (TTL) of 43800 hours.
Generate a CSR from your intermediate CA and save it as pki_intermediate.csr
.
Sign and deploy the intermediate CA certificate
Sign the intermediate CA CSR with the root certificate and save the generated
certificate as intermediate.cert.pem
.
Once the CSR is signed and the root CA returns a certificate, it can be imported back into Vault.
Create a role
A role is a logical name that maps to a policy used to generate credentials. In our example, it will allow you to use configuration parameters that specify certificate common names, designate alternate names, and enable subdomains along with a few other key settings.
Create a role named nomad-cluster
that specifies the allowed domains, enables
you to create certificates for subdomains, and generates certificates with a TTL
of 86400 seconds (24 hours).
You will receive a success message if your role was created properly.
Create a policy to access the role endpoint
Recall from earlier that you generated a root token that you used to log in to Vault. Although you could use that token in our next steps to generate our TLS certs, the recommended security approach is to create a new token based on a specific policy with limited privileges.
Create a policy file named tls-policy.hcl
and provide it the following
contents.
Note that you are specifying the update
capability on the
path pki_int/issue/nomad-cluster
. All other privileges will be denied. You can
read more about Vault policies here.
Write the policy you just created into Vault.
Configure consul-template
Generate a token based on tls-policy
Create a token based on tls-policy
with the following command.
On success, you will receive output similar to the following.
Make a note of this token as you will need it in the upcoming steps.
Create and populate the templates directory
You need to create templates that consul-template can use to render the actual
certificates and keys on the nodes in our cluster. In this guide, you will place
these templates in /opt/nomad/templates
.
Create a directory called templates
in /opt/nomad
.
Below are the templates that the consul-template configuration will use. You will provide different templates to the nodes depending on whether they are server nodes or client nodes. All of the nodes will get the CLI templates (since you want to use the CLI on any of the nodes).
Nomad servers
agent.crt.tpl.
agent.key.tpl.
ca.crt.tpl.
Nomad clients
Replace the word server
in the common_name
option in each template with the
word client
.
agent.crt.tpl.
agent.key.tpl.
ca.crt.tpl.
Nomad CLI
cli.crt.tpl.
cli.key.tpl.
Configure consul-template on all nodes
If you are using the AWS environment provided in this guide, you already have consul-template installed on all nodes. If you are using your own environment, ensure consul-template is installed. You can download it here.
Provide the token you created based on tls-policy
to the consul-template
configuration file located at /etc/consul-template.d/consul-template.hcl
. You
will also need to specify the template stanza so you can
render each of the following on your nodes at the specified location from the
templates you created in the previous step.
- Node certificate
- Node private key
- CA public certificate
You will also specify the template stanza to create certs and keys from the templates you previously created for the Nomad CLI (which defaults to HTTP but will need to use HTTPS once TLS is enabled in our cluster).
Your consul-template.hcl
configuration file should look similar to the
following (you will need to provide this to each node in the cluster).
Note
This tutorial hard-codes the token you created into the consul-template
configuration file. Although you can avoid this by assigning it to the
environment variable VAULT_TOKEN
, this method can still pose a security
concern. The recommended approach is to securely introduce this token to
Consul Template. To learn how to accomplish this, consult Secure
Introduction.
Note
This tutorial applies file permissions 0700
to the agent.crt
and
agent.key
since only the root user should be able to read those files. Any
other user using the Nomad CLI will be able to read the CLI certs and key that
you have created for them along with intermediate CA cert.
Start the consul-template service
Start the consul-template service on each node.
You can quickly confirm the appropriate certs and private keys were generated in
the destination
directory you specified in your consul-template configuration
by listing them out.
Configure Nomad to use TLS
Add the following tls stanza to the configuration of all
Nomad agents (servers and clients) in the cluster (configuration file located at
/etc/nomad.d/nomad.hcl
in this example).
Additionally, ensure the rpc_upgrade_mode
option is set to
true
on your server nodes (this is to ensure the Nomad servers will accept
both TLS and non-TLS connections during the upgrade).
Reload Nomad's configuration on all nodes.
Once Nomad has been reloaded on all nodes, go back to your server nodes and
change the rpc_upgrade_mode
option to false (or remove the line since the
option defaults to false) so that your Nomad servers will only accept TLS
connections.
You will need to reload Nomad on your servers after changing this setting. You can read more about RPC Upgrade Mode here.
If you run nomad status
, you will now receive the following error.
This is because the Nomad CLI defaults to communicating via HTTP instead of HTTPS. You can configure the local Nomad client to connect using TLS and specify our custom key and certificates by setting the following environments variables.
After these environment variables are correctly configured, the CLI will respond as expected.
Encrypt server gossip
At this point all of Nomad's RPC and HTTP communication is secured with mTLS. However, Nomad servers also communicate with a gossip protocol, Serf, that does not use TLS.
- HTTP - Used to communicate between CLI and Nomad agents. Secured by mTLS.
- RPC - Used to communicate between Nomad agents. Secured by mTLS.
- Serf - Used to communicate between Nomad servers. Secured by a shared key.
You can learn how to configure gossip encryption in the Enable Gossip Encryption for Nomad guide.
Next steps
Now that you have completed this guide,
- you have explored the Vault PKI engine,
- you have used consul-template to fetch certificates from Vault, and
- you have enabled a Nomad cluster to use mTLS certificates to encrypt traffic