">To enable Kubernetes authentication with terraform we need to set two environment variables VAULT_ADDR (vault server address) and VAULT_TOKEN (root token). To set VAULT_ADDR, we can use service port forwarding to access the vault server on localhost.</p><figure id="9aed"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*EeGuNKF1GYyjvadZ1PILbQ.png"><figcaption>Vault server on localhost</figcaption></figure><p id="3888">export environment variables and login to vault using <b><i>vault login $VAULT_TOKEN</i></b> command.</p><figure id="c823"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*_T2m-bKDkcPKAd1IKrb5Ew.png"><figcaption></figcaption></figure><p id="81b4">Now enable secrets engine in vault and create one secret as per application location by running the following commands:
<b><i>vault secrets enable -path=secrets kv (kv for key-value pair)
</i></b>verify by running<b><i> the vault secrets list </i></b>command. Your console output will look this:</p><figure id="8861"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*K4JXa-WTx2PnvZBT0E3grA.png"><figcaption></figcaption></figure><p id="1dba">Run <b><i>vault kv put secrets/your secret path/name key=value</i></b> command to write one secret in vault and verify by running <b><i>vault kv get secrets/path/name</i></b>. Refer to below example.</p><figure id="d6ae"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*LMM-BkxOXghTDyB0Y_tuTw.png"><figcaption></figcaption></figure><p id="c4e9">Now we have a secrets engine enabled and we store one secret which will be read by our test application.</p><p id="ce90">Run following terraform to enable Kubernetes authentication in the vault.</p>
<figure id="000b">
<div>
<div>
<iframe class="gist-iframe" src="/gist/ajaychoudhary01/9986d21187f1c162dfcdc0a3a3ae1160.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="5197">I have also created one data.tf file to read service account secret and define vault policy.</p>
<figure id="8139">
<div>
<div>
<iframe class="gist-iframe" src="/gist/ajaychoudhary01/e8e482cac2046250cfa3c8d0d411611b.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="5b6b">This solution will change based on your Kubernetes installation. If you are using Cloud PASS solution then you need to use different data source objects to read Kubernetes endpoint, certificate, etc. I am using docker-desktop local cluster so reading configuration from local.
Once terraform apply completes you will see the below output, run <b><i>vault auth list</i></b> to verify Kubernetes authentication is enabled.</p><figure id="9ebe"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*WXRHtcgHeaRng-PBjkB_Wg.png"><figcaption></figcaption></figure><h2 id="3177">Dynamically Injecting Secrets in running POD</h2><figure id="1eab"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*13nr36Jm9-wr9JCookRkSQ.png"><figcaption></figcaption></figure><p id="2d45">Now we have a vault with Kubernetes authentication enabled and some test secrets available for application to consume. I have created one sample Kubernetes deployment using terraform to test this. Here I am using the vault injector feature which was deployed as part of the vault helm release. Vault injector makes it really easy to inject secrets in a running pod with just a few annotations on the deployment object. Refer below an example:</p>
<figure id="335b">
<div>
<div>
<iframe class="gist-iframe" src="/gist/ajaychoudhary01/c2b875141e2f6b0b17f216556af7daf2.js" allowfullscreen="" frameborder="0" height="undefined" width="undefined">
</div>
</div>
</figure></iframe></div></div></figure><p id="1b33">Notice annotations in the template. It will use an injector agent to read secrets from the application path (secrets/myproject/dbpassword) and store in /vault/secrets/mydbpassword location inside the pod. Vault injector will automatically start the side-car container to read secrets and mount on the application pod. Notice 2/2 Ready check here although we provided only one container in the deployment manifest.</p><figure id="2270"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*YvgWcY41wwjSs799gdvZzw.png"><figcaption></figcaption></figure><figure id="4d02"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*Yq4fIkpiAKBOjqJysfzPQA.png"><figcaption></figcaption></figure><p id="1662">Exec inside pod to see if vault agent has mounted secrets in file store:
run <b><i>kubectl exec -n namespace -it pod-name — bash</i></b> and then cd /vault/secrets you will notice one mydbpassword file. cat the file to see the content.</p><figure id="9244"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*eA4X6UCcxNMNa97tm5kcRQ.png"><figcaption></figcaption></figure><p i
Options
d="611b">With the annotation approach, we can inject multiple secrets by using multiple “vault.hashicorp.com/agent-inject-secret-yourfilename”: secret path (keep file name unique).</p><h2 id="e245">Other Solutions:</h2><p id="19a7">To inject secrets dynamically inside the running pod, there are other options as well like using HCM template in config map and then mount config map on the pod or using HCM rest API calls inside init container to mount secrets on shared file store. References: <a href="https://learn.hashicorp.com/tutorials/vault/agent-kubernetes?in=vault/kubernetes">https://learn.hashicorp.com/tutorials/vault/agent-kubernetes?in=vault/kubernetes</a>
<a href="https://www.vaultproject.io/api">https://www.vaultproject.io/api</a></p><h2 id="28b3">Best Practices:</h2><ol><li>Deploy consul and vault with tls enabled. This can be enabled with helm values override. Refer: <a href="https://www.vaultproject.io/docs/platform/k8s/helm/configuration">https://www.vaultproject.io/docs/platform/k8s/helm/configuration</a></li><li>Use auto-unseal feature if possible with Cloud PASS solution. Refer: <a href="https://learn.hashicorp.com/collections/vault/auto-unseal">https://learn.hashicorp.com/collections/vault/auto-unseal</a></li><li>Create separate vault auth role and policy for each application with access to only application-related secrets path.</li><li>Use base64 encoded secrets instead of plain credentials.</li><li>Avoid root token and create tokens for applications/users with fine-grained access.</li><li>Integrate with authentication models like Azure Active Directory, OKTA, LDAP, GitHub, etc. for user authentication and authorization. Refer to this link for available authentication methods: <a href="https://www.vaultproject.io/docs/auth">https://www.vaultproject.io/docs/auth</a></li></ol><p id="043e"><b><i>Full code reference</i></b>: <a href="https://github.com/ajaychoudhary01/terraform-vault-installation">https://github.com/ajaychoudhary01/terraform-vault-installation</a></p><h2 id="9242">Further Reading/References:</h2><div id="6a58" class="link-block">
<a href="https://learn.hashicorp.com/collections/vault/kubernetes">
<div>
<div>
<h2>Kubernetes | Vault - HashiCorp Learn</h2>
<div><h3>Vault secures, stores, and tightly controls access to passwords, certificates, and other secrets in modern computing…</h3></div>
<div><p>learn.hashicorp.com</p></div>
</div>
<div>
<div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*fhXvv7Bv4iIhB9Fw)"></div>
</div>
</div>
</a>
</div><div id="6432" class="link-block">
<a href="https://www.vaultproject.io/docs/commands">
<div>
<div>
<h2>Commands (CLI) | Vault by HashiCorp</h2>
<div><h3>In addition to a verbose HTTP API, Vault features a command-line interface that wraps common functionality and formats…</h3></div>
<div><p>www.vaultproject.io</p></div>
</div>
<div>
<div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*DteCxnSs215WiHkj)"></div>
</div>
</div>
</a>
</div><div id="fe8c" class="link-block">
<a href="https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands">
<div>
<div>
<h2>Kubectl Reference Docs</h2>
<div><h3>This section contains the most basic commands for getting a workload running on your cluster. run will start running 1…</h3></div>
<div><p>kubernetes.io</p></div>
</div>
<div>
<div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/)"></div>
</div>
</div>
</a>
</div><div id="16c3" class="link-block">
<a href="https://learn.hashicorp.com/collections/vault/recommended-patterns">
<div>
<div>
<h2>Recommended Patterns | Vault - HashiCorp Learn</h2>
<div><h3>Recommended Patterns for Vault users</h3></div>
<div><p>learn.hashicorp.com</p></div>
</div>
<div>
<div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*39OGftGlz1f9p3e4)"></div>
</div>
</div>
</a>
</div><div id="7b13" class="link-block">
<a href="https://www.terraform.io/docs/cli/commands/index.html">
<div>
<div>
<h2>Basic CLI Features - Terraform by HashiCorp</h2>
<div><h3>Hands-on: Try the Terraform: Get Started collection on HashiCorp Learn. The command line interface to Terraform is via…</h3></div>
<div><p>www.terraform.io</p></div>
</div>
<div>
<div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*qdOmWF9LAHw447FE)"></div>
</div>
</div>
</a>
</div></article></body>
Hashicorp Vault for secret management in Kubernetes Cluster
Vault
INTRODUCTION
Hashicorp Vault provides all of the power and security of Vault, without the complexity and overhead of managing it yourself. It also provides various authentication methods like AWS, Kubernetes, Tokens, OIDC, Azure Active Directory, etc. to provision and dynamically injects secrets in infrastructure like EC2 Machine, Kubernetes pods, etc.
HashiCorp Cloud Platform features a web user interface to deploy and manage resources, including HCP Vault deployments in AWS. However, If you prefer to automate HCP Vault deployment, one recommended approach is to use HashiCorp Terraform with the HCP provider.
What we will cover:
In this post we will cover the following:
Vault installation with High availability configuration in Kubernetes cluster using terraform.
Enabling Kubernetes authentication in Vault using terraform.
Inject Secrets in running pod dynamically.
Pre-requisites
Kubernetes cluster up and running
kubectl, terraform, helm, vault CLI installed
some basic knowledge of terraform, kubectl and vault commands. (I will provide a link in end for reference)
Vault installation using terraform:
First of all, It is required to run the HashiCorp vault in any infrastructure. In this example, we will install the Hashicorp vault in the Kubernetes cluster using the Helm release with terraform provider.
Note*: This helm release is using some value overrides based on my local cluster configuration. Please find below sample/my values.tf file to override helm values in vault and consul release.
Run terraform init and terraform apply commands to deploy vault with consul backend. Once helm releases complete, run kubectl -n vault get pods commands to see running pods. Notice vault-0 pod is running but not in a ready state.
Terraform Apply output
Note:* K is an alias for kubectl (in the above example command).
When a Vault server is started, it starts in a sealed state. In this state, Vault is configured to know where and how to access the physical storage but doesn’t know how to decrypt any of it.
Unsealing is the process of obtaining the plaintext master key necessary to read the decryption key to decrypt the data, allowing access to the Vault.
Prior to unsealing, almost no operations are possible with Vault. For example, authentication, managing the mount tables, etc. are all not possible. The only possible operations are to unseal the Vault and check the status of the seal.
We need to run the following commands to initialize and unseal the vault.
Kubectl -n vault -it vault-0 — vault operator init
It will initialize the vault server and generate unseal keys and root token. Keep these unseal keys and root token somewhere secured.
Vault initialized
Now run unseal command on all vault-related pods to unseal vault servers.
kubectl -n vault -it vault-0 — vault operator unseal ‘Key1’ (run this at least 3 times as vault server requires 3 unique unseal keys), then run kubectl -n vault get pods and you will notice vault-0 (vault related pods) is in Ready state.
Vault Unseal
Note*: There are solutions available from HCP vault to automatically unseal vault servers with cloud providers provided Kubernetes PASS solutions like EKS, GKS, AKS.
Enable Kubernetes Authentication
Now, we have a vault server up and running in the Kubernetes cluster and we have securely stored unseal keys and a root token so that they can be used for vault administration or unseal pods if there is any scale-up.
To enable Kubernetes authentication with terraform we need to set two environment variables VAULT_ADDR (vault server address) and VAULT_TOKEN (root token). To set VAULT_ADDR, we can use service port forwarding to access the vault server on localhost.
Vault server on localhost
export environment variables and login to vault using vault login $VAULT_TOKEN command.
Now enable secrets engine in vault and create one secret as per application location by running the following commands:
vault secrets enable -path=secrets kv (kv for key-value pair)
verify by running the vault secrets list command. Your console output will look this:
Run vault kv put secrets/your secret path/name key=value command to write one secret in vault and verify by running vault kv get secrets/path/name. Refer to below example.
Now we have a secrets engine enabled and we store one secret which will be read by our test application.
Run following terraform to enable Kubernetes authentication in the vault.
I have also created one data.tf file to read service account secret and define vault policy.
This solution will change based on your Kubernetes installation. If you are using Cloud PASS solution then you need to use different data source objects to read Kubernetes endpoint, certificate, etc. I am using docker-desktop local cluster so reading configuration from local.
Once terraform apply completes you will see the below output, run vault auth list to verify Kubernetes authentication is enabled.
Dynamically Injecting Secrets in running POD
Now we have a vault with Kubernetes authentication enabled and some test secrets available for application to consume. I have created one sample Kubernetes deployment using terraform to test this. Here I am using the vault injector feature which was deployed as part of the vault helm release. Vault injector makes it really easy to inject secrets in a running pod with just a few annotations on the deployment object. Refer below an example:
Notice annotations in the template. It will use an injector agent to read secrets from the application path (secrets/myproject/dbpassword) and store in /vault/secrets/mydbpassword location inside the pod. Vault injector will automatically start the side-car container to read secrets and mount on the application pod. Notice 2/2 Ready check here although we provided only one container in the deployment manifest.
Exec inside pod to see if vault agent has mounted secrets in file store:
run kubectl exec -n namespace -it pod-name — bash and then cd /vault/secrets you will notice one mydbpassword file. cat the file to see the content.
With the annotation approach, we can inject multiple secrets by using multiple “vault.hashicorp.com/agent-inject-secret-yourfilename”: secret path (keep file name unique).
Create separate vault auth role and policy for each application with access to only application-related secrets path.
Use base64 encoded secrets instead of plain credentials.
Avoid root token and create tokens for applications/users with fine-grained access.
Integrate with authentication models like Azure Active Directory, OKTA, LDAP, GitHub, etc. for user authentication and authorization. Refer to this link for available authentication methods: https://www.vaultproject.io/docs/auth