Consul
Federation Between Kubernetes Clusters with Vault as Secrets Backend
Note: This topic requires familiarity with Mesh Gateways, WAN Federation Via Mesh Gateways.
This page describes how you can federate multiple Kubernetes clusters using Vault as the secrets backend. See the Multi-Cluster Overview for more information on use cases and how it works.
Differences Between Using Kubernetes Secrets vs. Vault
The Federation Between Kubernetes Clusters page provides an overview of WAN Federation using Mesh Gateways with Kubernetes secrets as the secret backend. When using Vault as the secrets backend, there are different systems and data integration configuration that will be explained in the Usage section of this page. The other main difference is that when using Vault, there is no need for you to export and import a Federation Secret in each datacenter.
Usage
The expected use case is to create WAN Federation on Kubernetes clusters. The following procedure results in a WAN Federation with Vault as the secrets backend between two clusters, dc1 and dc2. dc1 acts as the primary Consul cluster and also contains the Vault server installation. dc2 is the secondary Consul cluster.
The Vault Injectors in each cluster will ensure that every pod in cluster has a Vault agent inject into the pod.
The Vault Agents on each Consul pod will communicate directly with Vault on its externally accessible endpoint. Consul pods are also configured with Vault annotations that configure the secrets that the pod needs as well as the path that the Vault agent should locally store those secrets.
The two data centers will federated using mesh gateways. This communication topology is also described in the WAN Federation Via Mesh Gateways section of Multi-Cluster Federation Overview.
Install Vault
In this setup, you will deploy Vault server in the primary datacenter (dc1) Kubernetes cluster, which is also the primary Consul datacenter. You will configure your Vault Helm installation in the secondary datacenter (dc2) Kubernetes cluster to use it as an external server. This way there will be a single vault server cluster that will be used by both Consul datacenters.
Note: For demonstration purposes, the following example deploys a Vault server in dev mode. Do not use dev mode for production installations. Refer to the Vault Deployment Guide for guidance on how to install Vault in a production setting.
Change your current Kubernetes context to target the primary datacenter (dc1).
$ kubectl config use-context <context for dc1>
Now, use the values files below for your Helm install.
vault-dc1.yaml
server: dev: enabled: true service: enabled: true type: LoadBalancer ui: enabled: true
$ helm install vault-dc1 --values vault-dc1.yaml hashicorp/vault --wait
Configuring your local environment
Install Consul locally so that you can generate the gossip key. Please see the Precompiled Binaries section of the Install Consul page.
Set the VAULT_TOKEN with a default value.
$ export VAULT_ADDR=root
Get the external IP or DNS name of the Vault server's load balancer.
On EKS, you can get the hostname of the Vault server's load balancer with the following command:$ export VAULT_SERVER_HOST=$(kubectl get svc vault-dc1 -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
Set the VAULT_ADDR environment variable.
$ export VAULT_ADDR=http://${VAULT_SERVER_HOST}:8200
Systems Integration
There are two main procedures to enable Vault as the service mesh certificate provider in Kubernetes.
Complete the following steps once:
- Enabling Vault KV Secrets Engine - Version 2.
- Enabling Vault PKI Engine.
Repeat the following steps for each datacenter in the cluster:
- Installing the Vault Injector within the Consul datacenter installation
- Configuring a Kubernetes Auth Method in Vault to authenticate and authorize operations from the Consul datacenter
- Enable Vault as the Secrets Backend in the Consul datacenter
Configure Vault Secrets engines
Enable Vault KV secrets engine - Version 2 in order to store the Gossip Encryption Key and the ACL Replication token (
global.acls.replicationToken
).$ vault secrets enable -path=consul-kv kv-v2
Enable Vault PKI Engine in order to leverage Vault for issuing Consul Server TLS certificates.
$ vault secrets enable pki
$ vault secrets tune -max-lease-ttl=87600h pki
Primary Datacenter (dc1)
Install the Vault Injector in your Consul Kubernetes cluster (dc1), which is used for accessing secrets from Vault.
Note: In the primary datacenter (dc1), you will not have to configure
injector.externalvaultaddr
value because the Vault server is in the same primary datacenter (dc1) cluster.vault-dc1.yaml
server: dev: enabled: true service: enabled: true type: LoadBalancer injector: enabled: true authPath: auth/kubernetes-dc1 ui: enabled: true
Next, install Vault in the Kubernetes cluster.
$ helm upgrade vault-dc1 --values vault-dc1.yaml hashicorp/vault --wait
Configure the Kubernetes Auth Method in Vault for the primary datacenter (dc1).
$ vault auth enable -path=kubernetes-dc1 kubernetes
Because Consul is in the same datacenter cluster as Vault, the Vault Auth Method can use its own CA Cert and JWT to authenticate Consul dc1 service account requests. Therefore, you do not need to set
token_reviewer
andkubernetes_ca_cert
on the dc1 Kubernetes Auth Method.Configure Auth Method with Kubernetes API host
$ vault write auth/kubernetes-dc1/config kubernetes_host=https://kubernetes.default.svc
Enable Vault as the secrets backend in the primary datacenter (dc1). However, you will not yet apply the Helm install command. You will issue the Helm upgrade command after the Data Integration section.
consul-dc1.yaml
global: secretsBackend: vault: enabled: true
Secondary Datacenter (dc2)
Install the Vault Injector in the secondary datacenter (dc2).
In the secondary datacenter (dc2), you will configure the
externalvaultaddr
value point to the external address of the Vault server in the primary datacenter (dc1).Change your Kubernetes context to target the secondary datacenter (dc2):
$ kubectl config use-context <context for dc2>
vault-dc2.yaml
server: enabled: false injector: enabled: true externalVaultAddr: ${VAULT_ADDR} authPath: auth/kubernetes-dc2
Next, install Vault in the Kubernetes cluster.
$ helm install vault-dc2 --values vault-dc2.yaml hashicorp/vault --wait
Configure the Kubernetes Auth Method in Vault for the datacenter
$ vault auth enable -path=kubernetes-dc2 kubernetes
Create a service account with access to the Kubernetes API in the secondary datacenter (dc2). For the secondary datacenter (dc2) auth method, you first need to create a service account that allows the Vault server in the primary datacenter (dc1) cluster to talk to the Kubernetes API in the secondary datacenter (dc2) cluster.
$ cat <<EOF >> auth-method-serviceaccount.yaml # auth-method.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: vault-dc2-auth-method roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegator subjects: - kind: ServiceAccount name: vault-dc2-auth-method namespace: default --- apiVersion: v1 kind: ServiceAccount metadata: name: vault-dc2-auth-method namespace: default EOF
$ kubectl apply --values auth-method-serviceaccount.yaml
Next, you will need to get the token and CA cert from that service account secret.
$ export K8S_DC2_CA_CERT="$(kubectl get secret `kubectl get serviceaccounts vault-dc2-auth-method --output jsonpath='{.secrets[0].name}'` --output jsonpath='{.data.ca\.crt}' | base64 --decode)"
$ export K8S_DC2_JWT_TOKEN="$(kubectl get secret `kubectl get serviceaccounts vault-dc2-auth-method --output jsonpath='{.secrets[0].name}'` --output jsonpath='{.data.token}' | base64 --decode)"
Configure the auth method with the JWT token of service account. First, get the externally reachable address of the secondary Consul datacenter (dc2) in the secondary Kubernetes cluster. Then set
kubernetes_host
in the auth method configuration.$ export KUBE_API_URL_DC2=$(kubectl config view --output jsonpath="{.clusters[?(@.name == \"$(kubectl config current-context)\")].cluster.server}")
$ vault write auth/kubernetes-dc2/config \ kubernetes_host="${KUBE_API_URL_DC2}" \ token_reviewer_jwt="${K8S_DC2_JWT_TOKEN}" \ kubernetes_ca_cert="${K8S_DC2_CA_CERT}"
Enable Vault as the secrets backend in the secondary Consul datacenter (dc2). However, you will not yet apply the Helm install command. You will issue the Helm upgrade command after the Data Integration section.
values-dc2.yaml
global: secretsBackend: vault: enabled: true
Data Integration
There are two main procedures for using Vault as the service mesh certificate provider in Kubernetes.
Complete the following steps once:
- Store the secrets in Vault.
- Create a Vault policy that authorizes the desired level of access to the secrets.
Repeat the following steps for each datacenter in the cluster:
- Create Vault Kubernetes auth roles that link the policy to each Consul on Kubernetes service account that requires access.
- Update the Consul on Kubernetes helm chart.
Secrets and Policies
Store the ACL bootstrap and replication tokens, gossip encryption key, and root CA certificate secrets in Vault.
$ vault kv put consul-kv/secret/gossip key="$(consul keygen)"
$ vault kv put consul-kv/secret/bootstrap token="$(uuidgen | tr '[:upper:]' '[:lower:]')"
$ vault kv put consul-kv/secret/replication token="$(uuidgen | tr '[:upper:]' '[:lower:]')"
$ vault write pki/root/generate/internal common_name="Consul CA" ttl=87600h
Create Vault policies that authorize the desired level of access to the secrets.
$ vault policy write gossip - <<EOF path "consul-kv/data/secret/gossip" { capabilities = ["read"] } EOF
$ vault policy write bootstrap-token - <<EOF path "consul-kv/data/secret/bootstrap" { capabilities = ["read"] } EOF
$ vault policy write replication-token - <<EOF path "consul-kv/data/secret/replication" { capabilities = ["read"] } EOF
$ vault policy write ca-policy - <<EOF path "pki/cert/ca" { capabilities = ["read"] } EOF
Pre-installation for Primary Datacenter (dc1)
- Change your Kubernetes context to target the primary datacenter (dc1):
$ kubectl config use-context <context for dc1>
Primary Datacenter (dc1)
Create Server TLS and Service Mesh Cert Policies
$ vault policy write consul-cert-dc1 - <<EOF path "pki/issue/consul-cert-dc1" { capabilities = ["create","update"] } EOF
$ vault policy write connect-ca-dc1 - <<EOF path "/sys/mounts" { capabilities = [ "read" ] } path "/sys/mounts/connect_root" { capabilities = [ "create", "read", "update", "delete", "list" ] } path "/sys/mounts/dc1/connect_inter" { capabilities = [ "create", "read", "update", "delete", "list" ] } path "/connect_root/*" { capabilities = [ "create", "read", "update", "delete", "list" ] } path "/dc1/connect_inter/*" { capabilities = [ "create", "read", "update", "delete", "list" ] } EOF
Create Vault Kubernetes auth roles that link the policy to each Consul on Kubernetes service account that requires access. For each auth method in Vault, you will need roles for the Consul server, Consul client, the
server-acl-init
job, and Consul server CA.$ vault write auth/kubernetes-dc1/role/consul-server \ bound_service_account_names=consul-server \ bound_service_account_namespaces="default" \ policies="gossip,connect-ca-dc1,consul-cert-dc1" \ ttl=24h
$ vault write auth/kubernetes-dc1/role/consul-client \ bound_service_account_names=consul-client \ bound_service_account_namespaces="default" \ policies="gossip" \ ttl=24h
$ vault write auth/kubernetes-dc1/role/server-acl-init \ bound_service_account_names=consul-server-acl-init \ bound_service_account_namespaces="default" \ policies="bootstrap-token,replication-token" \ ttl=24h
$ vault write auth/kubernetes-dc1/role/consul-ca \ bound_service_account_names="*" \ bound_service_account_namespaces="default" \ policies=ca-policy \ ttl=1h
Create the server TLS Cert role.
$ vault write pki/roles/consul-cert-dc1 \ allowed_domains="dc1.consul,consul-server,consul-server.default,consul-server.default.svc" \ allow_subdomains=true \ allow_bare_domains=true \ allow_localhost=true \ generate_lease=true \ max_ttl="720h"
Configure and install Consul in the primary datacenter (dc1).
consul-dc1.yaml
global: datacenter: "dc1" name: consul secretsBackend: vault: enabled: true consulServerRole: consul-server consulClientRole: consul-client consulCARole: consul-ca manageSystemACLsRole: server-acl-init connectCA: address: http://vault-dc1.default:8200 rootPKIPath: connect_root/ intermediatePKIPath: dc1/connect_inter/ authMethodPath: kubernetes-dc1 tls: enabled: true enableAutoEncrypt: true caCert: secretName: pki/cert/ca federation: enabled: true createFederationSecret: false acls: manageSystemACLs: true createReplicationToken: true bootstrapToken: secretName: consul-kv/data/secret/bootstrap secretKey: token replicationToken: secretName: consul-kv/data/secret/replication secretKey: token gossipEncryption: secretName: consul-kv/data/secret/gossip secretKey: key server: replicas: 1 serverCert: secretName: "pki/issue/consul-cert-dc1" connectInject: replicas: 1 enabled: true meshGateway: enabled: true replicas: 1
Next, install Consul in the primary Kubernetes cluster (dc1).
$ helm install consul-dc1 --values consul-dc1.yaml hashicorp/consul
Pre-installation for Secondary Datacenter (dc2)
Update the Consul on Kubernetes Helm chart. For secondary datacenter (dc2), you need to get the address of the mesh gateway from the primary datacenter (dc1) cluster.
Keep your Kubernetes context targeting dc1 and set the
MESH_GW_HOST
environment variable that you will use in the Consul Helm chart for secondary datacenter (dc2).$ kubectl config use-context <context for dc1>
Next, get mesh gateway address:
$ export MESH_GW_HOST=$(kubectl get svc consul-mesh-gateway --output jsonpath='{.status.loadBalancer.ingress[0].hostname}')
Change your Kubernetes context to target the primary datacenter (dc2):
$ kubectl config use-context <context for dc2>
Secondary Datacenter (dc2)
Create Server TLS and Service Mesh Cert Policies
$ vault policy write consul-cert-dc2 - <<EOF path "pki/issue/consul-cert-dc2" { capabilities = ["create","update"] } EOF
$ vault policy write connect-ca-dc2 - <<EOF path "/sys/mounts" { capabilities = [ "read" ] } path "/sys/mounts/connect_root" { capabilities = [ "create", "read", "update", "delete", "list" ] } path "/sys/mounts/dc2/connect_inter" { capabilities = [ "create", "read", "update", "delete", "list" ] } path "/connect_root/*" { capabilities = [ "create", "read", "update", "delete", "list" ] } path "/dc2/connect_inter/*" { capabilities = [ "create", "read", "update", "delete", "list" ] } EOF
Create Vault Kubernetes auth roles that link the policy to each Consul on Kubernetes service account that requires access. For each auth method in Vault, you will need roles for the Consul server, Consul client, the server-acl-init job, and Consul server CA.
$ vault write auth/kubernetes-dc2/role/consul-server \ bound_service_account_names=consul-server \ bound_service_account_namespaces="default" \ policies="gossip,connect-ca-dc2,consul-cert-dc2,replication-token" \ ttl=24h
$ vault write auth/kubernetes-dc2/role/consul-client \ bound_service_account_names=consul-client \ bound_service_account_namespaces="default" \ policies="gossip" \ ttl=24h
$ vault write auth/kubernetes-dc2/role/server-acl-init \ bound_service_account_names=consul-server-acl-init \ bound_service_account_namespaces="default" \ policies="replication-token" \ ttl=24h
$ vault write auth/kubernetes-dc2/role/consul-ca \ bound_service_account_names="*" \ bound_service_account_namespaces="default" \ policies=ca-policy \ ttl=1h
Create the Server TLS Cert role.
$ vault write pki/roles/consul-cert-dc2 \ allowed_domains="dc2.consul,consul-server,consul-server.default,consul-server.default.svc" \ allow_subdomains=true \ allow_bare_domains=true \ allow_localhost=true \ generate_lease=true \ max_ttl="720h"
Configure and install Consul in the secondary datacenter (dc2).
Note: To configure Vault as the service mesh (connect) CA in secondary datacenters, you need to make sure that the Root CA path is the same. The intermediate path is different for each datacenter. In the
connectCA
Helm configuration for a secondary datacenter, you can specify aintermediatePKIPath
that is, for example, prefixed with the datacenter for which this configuration is intended (e.g.dc2/connect-intermediate
).consul-dc2.yaml
global: datacenter: "dc2" name: consul secretsBackend: vault: enabled: true consulServerRole: consul-server consulClientRole: consul-client consulCARole: consul-ca manageSystemACLsRole: server-acl-init connectCA: address: ${VAULT_ADDR} rootPKIPath: connect_root/ intermediatePKIPath: dc2/connect_inter/ authMethodPath: kubernetes-dc2 tls: enabled: true enableAutoEncrypt: true caCert: secretName: "pki/cert/ca" federation: enabled: true primaryDatacenter: dc1 k8sAuthMethodHost: ${KUBE_API_URL_DC2} primaryGateways: - ${MESH_GW_HOST}:443 acls: manageSystemACLs: true replicationToken: secretName: consul-kv/data/secret/replication secretKey: token gossipEncryption: secretName: consul-kv/data/secret/gossip secretKey: key server: replicas: 1 serverCert: secretName: "pki/issue/consul-cert-dc2" connectInject: replicas: 1 enabled: true controller: enabled: true meshGateway: enabled: true replicas: 1
Next, install Consul in the consul Kubernetes cluster (dc2).
$ helm install consul-dc2 -f consul-dc2.yaml hashicorp/consul
Next steps
You have completed the process of federating the secondary datacenter (dc2) with the primary datacenter (dc1) using Vault as the Secrets backend. To validate that everything is configured properly, please confirm that all pods within both datacenters are in a running state.
For additional information about specific Consul secrets that you can store in Vault, refer to Data Integration in the Vault as a Secrets Backend documentation.