Vault installation to Amazon Elastic Kubernetes Service via Helm
Amazon Elastic Kubernetes Service (EKS) can run and scale Vault in the Amazon Web Services (AWS) cloud or on-premises. Creating a Kubernetes cluster and launching Vault via the Helm chart can be accomplished all from the command-line.
In this tutorial, you create a cluster in AWS, deploy a MySQL server, install Vault in high-availability (HA) mode via the Helm chart and then configure the authentication between Vault and the cluster. Then you deploy a web application with deployment annotations so the application's secrets are installed via the Vault Agent injector service.
Prerequisites
This tutorial requires an AWS account, AWS command-line interface (CLI), Amazon EKS CLI, Kubernetes CLI and the Helm CLI.
First, create an AWS account.
Next, install AWS CLI, Amazon EKS CLI, kubectl CLI and helm CLI.
Install aws
with Homebrew.
Install eksctl
with Homebrew.
Install kubectl
with Homebrew.
Install helm
with Homebrew.
Next, configure the aws
CLI with credentials.
This command prompts you to enter an AWS access key ID, AWS secret access key, and default region name.
Tip
The above example uses IAM user authentication. You can use any authentication method described in the AWS provider documentation.
Next, create a keypair to enable you to SSH into created nodes.
Start cluster
A Vault cluster that is launched in high-availability requires a Kubernetes cluster with three nodes.
Provision with Terraform
An alternative way to manage the lifecycle of cluster is with Terraform. Learn more in the Provision an EKS Cluster (AWS) tutorial.
Create a three node cluster named
learn-vault
.Example output:
The cluster is created, deployed and then health-checked. When the cluster is ready the command modifies the
kubectl
configuration so that the commands you issue are performed against that cluster.Managing multiple clusters
kubectl
enables you to manage multiple clusters through the context configuration. You display the available contextskubectl config get-contexts
and set the context by namekubectl config use-context NAME
.Display the nodes of the cluster.
Enable volume support with the EBS CSI driver add-on.
The cluster is ready.
Install the MySQL Helm chart
MySQL is a fast, reliable, scalable, and easy to use open-source relational database system. MySQL Server is intended for mission-critical, heavy-load production systems as well as for embedding into mass-deployed software.
Add the Bitnami Helm repository.
Install the latest version of the MySQL Helm chart.
Output:
By default the MySQL Helm chart deploys a single pod a service.
Get all the pods within the default namespace.
Wait until the
mysql-0
pod is running and ready (1/1
).The
mysql-0
pod runs a MySQL server. aDemonstration Only
MySQL should be run with additional pods to ensure reliability when used in production. Refer to the MySQL Helm chart to override default parameters.
Get all the services within the default namespace.
The
mysql
service directs request to themysql-0
pod. Pods within the cluster may address the MySQL server with the addressmysql.default.svc.cluster.local
.The MySQL root password is stored as Kubernetes secret. This password is required by Vault to create credentials for the application pod deployed later.
Create a variable named
ROOT_PASSWORD
that stores the mysql root user password.The MySQL server, addressed through the service, is ready.
Install the Vault Helm chart
The recommended way to run Vault on Kubernetes is via the Helm chart.
Add the HashiCorp Helm repository.
Update all the repositories to ensure
helm
is aware of the latest versions.Search for all the Vault Helm chart versions.
The Vault Helm chart contains all the necessary components to run Vault in several different modes.
Default behavior
By default, Vault is launched on a single pod in standalone mode with a file storage backend. Enabling high-availability with Integrated Storage requires that you override these defaults.
Create a file named
helm-vault-raft-values.yml
with the following contents:Recommendation
If you are using Prometheus for monitoring and alerting, we recommend to set the
cluster_name
in the HCL configuration. With the Vault Helm chart, this is accomplished with the config parameter.Install the latest version of the Vault Helm chart with Integrated Storage.
Example output:
This creates three Vault server instances with an Integrated Storage (Raft) backend.
Along with the Vault pods and Vault Agent Injector pod are deployed in the default namespace.
Get all the pods within the default namespace.
The
vault-0
,vault-1
, andvault-2
pods deployed run a Vault server and report that they areRunning
but that they are not ready (0/1
). This is because the status check defined in a readinessProbe returns a non-zero exit code.The
vault-agent-injector
pod deployed is a Kubernetes Mutation Webhook Controller. The controller intercepts pod events and applies mutations to the pod if specific annotations exist within the request.Retrieve the status of Vault on the
vault-0
pod.Example output:
The status command reports that Vault is not initialized and that it is sealed. For Vault to authenticate with Kubernetes and manage secrets requires that that is initialized and unsealed.
Initialize and unseal one Vault pod
Vault starts uninitialized and in the sealed state. Prior to initialization the Integrated Storage backend is not prepared to receive data.
Initialize Vault with one key share and one key threshold.
The
operator init
command generates a root key that it disassembles into key shares-key-shares=1
and then sets the number of key shares required to unseal Vault-key-threshold=1
. These key shares are written to the output as unseal keys in JSON format-format=json
. Here the output is redirected to a file namedcluster-keys.json
.Display the unseal key found in
cluster-keys.json
.Insecure operation
Do not run an unsealed Vault in production with a single key share and a single key threshold. This approach is only used here to simplify the unsealing process for this demonstration.
Create a variable named
VAULT_UNSEAL_KEY
to capture the Vault unseal key.After initialization, Vault is configured to know where and how to access the storage, but does not know how to decrypt any of it. Unsealing is the process of constructing the root key necessary to read the decryption key to decrypt the data, allowing access to the Vault.
Unseal Vault running on the
vault-0
pod.Example output: The
operator unseal
command reports that Vault is initialized and unsealed.Insecure operation
Providing the unseal key with the command writes the key to your shell's history. This approach is only used here to simplify the unsealing process for this demonstration.
Retrieve the status of Vault on the
vault-0
pod.The Vault server is initialized and unsealed.
Join the other Vaults to the Vault cluster
The Vault server running on the vault-0
pod is a Vault HA cluster with a
single node. To display the list of nodes requires that you are logging in with
the root token.
Display the root token found in
cluster-keys.json
.Create a variable named
CLUSTER_ROOT_TOKEN
to capture the Vault unseal key.Login with the root token on the
vault-0
pod.Insecure operation
The login command stores the root token in a file for the container user. Subsequent commands are executed with that token. This approach is only used here to simplify the cluster configuration demonstration.
List all the nodes within the Vault cluster for the
vault-0
pod.This displays the one node within the Vault cluster. This cluster is addressable through the Kubernetes service
vault-0.vault-internal
created by the Helm chart. The Vault servers on the other pods need to join this cluster and be unsealed.Join the Vault server on
vault-1
to the Vault cluster.This Vault server joins the cluster sealed. To unseal the Vault server requires the same unseal key,
VAULT_UNSEAL_KEY
, provided to the first Vault server.Unseal the Vault server on
vault-1
with the unseal key.The Vault server on
vault-1
is now a functional node within the Vault cluster.Join the Vault server on
vault-2
to the Vault cluster.Unseal the Vault server on
vault-2
with the unseal key.The Vault server on
vault-2
is now a functional node within the Vault cluster.List all the nodes within the Vault cluster for the
vault-0
pod.This displays all three nodes within the Vault cluster.
Voter status
It may take additional time for each node's voter status to return true.
Get all the pods within the default namespace.
The
vault-0
,vault-1
, andvault-2
pods report that they areRunning
and ready (1/1
).
Create a Vault database role
The web application that you deploy in the Launch a web
application section, expects Vault to store a
username and password at the path secret/webapp/config
. To create this secret
requires you to login with the root token, enable the key-value secret
engine, and store a
secret username and password at that defined path.
Enable database secrets at the path
database
.Configure the database secrets engine with the connection credentials for the MySQL database.
Output:
Create a database secrets engine role named
readonly
.The
readonly
role generates credentials that are able to perform queries for any table in the database.Output:
Note
Important: when you define the role in a production deployment, you must create user creation_statements and revocation_statements, which are valid for the database you've configured. If you do not specify statements appropriate to creating, revoking, or rotating users, Vault inserts generic statements which can be unsuitable for your deployment.
Read credentials from the
readonly
database role.Learn more
For more information refer to the Database Secrets Engine tutorial.
Vault is able to generate crentials within the MySQL database.
Configure Kubernetes authentication
The initial root token is a privileged user that can perform any operation at any path. The web application only requires the ability to read secrets defined at a single path. This application should authenticate and be granted a token with limited access.
Best practice
We recommend that root tokens are used only for initial setup of an authentication method and policies. Afterwards they should be revoked. This tutorial does not show you how to revoke the root token.
Vault provides a Kubernetes authentication method that enables clients to authenticate with a Kubernetes Service Account Token.
Start an interactive shell session on the
vault-0
pod.Your system prompt is replaced with a new prompt
/ $
.Note
The prompt within this section is shown as
$
but the commands are intended to be executed within this interactive shell on thevault-0
container.Enable the Kubernetes authentication method.
Vault accepts a service token from any client within the Kubernetes cluster. During authentication, Vault verifies that the service account token is valid by querying a token review Kubernetes endpoint.
Configure the Kubernetes authentication method to use the location of the Kubernetes API.
For the best compatibility with recent Kubernetes versions, ensure you are using Vault v1.9.3 or greater.
Output:
The environment variable
KUBERNETES_PORT_443_TCP_ADDR
is defined and references the internal network address of the Kubernetes host.For a client of the Vault server to read the credentials defined in the Create a Vault database role step requires that the read capability be granted for the path
database/creds/readonly
.Write out the policy named
devwebapp
that enables theread
capability for secrets at pathdatabase/creds/readonly
Create a Kubernetes authentication role named
devweb-app
.Output:
The role connects a Kubernetes service account,
internal-app
(created in the next step), and namespace,default
, with the Vault policy,devwebapp
. The tokens returned after authentication are valid for 24 hours.Exit the
vault-0
pod.
Launch a web application
The web application pod requires the creation of the internal-app
Kubernetes
service account specified in the Vault Kubernetes authentication role created in
the Configure Kubernetes authentication
step.
Define a Kubernetes service account named
internal-app
.Create the
internal-app
service account.Define a pod named
devwebapp
with the web application.Create the
devwebapp
pod.This definition creates a pod with the specified container running with the
internal-app
Kubernetes service account. The container within the pod is unaware of the Vault cluster. The Vault Injector service reads the annotations and determines that it should take actionvault.hashicorp.com/agent-inject
. The credentials, read from Vault atdatabase/creds/readonly
, are retrieved by thedevwebapp-role
Vault role and stored at the file location,/vault/secrets/database-connect.sh
, and then mounted on the pod.The credentials are requested first by the
vault-agent-init
container to ensure they are present when the application pod initializes. After the application pod initializes, the injector service creates avault-agent
pod that assist the application in maintaining the credentials during initialization. The credentials requested by thevault-agent-init
container are cached,vault.hashicorp.com/agent-cache-enable: "true"
, and used byvault-agent
container.Agent Cache
Prior to Vault 1.7 and Vault-K8s 0.9.0 the
vault.hashicorp.com/agent-cache-enable
parameter was not available. The credentials requested by thevault-agent-init
container were requested again by thevault-agent
container resulting in multiple credentials issued for the same pod.Learn more
For more information about annotations refer to the Injecting Secrets into Kubernetes Pods via Vault Agent Injector tutorial and the Annotations documentation.
Get all the pods within the default namespace.
Wait until the
devwebapp
pod reports that is running and ready (2/2
).Display the secrets written to the file
/vault/secrets/database-connect.sh
on thedevwebapp
pod.The result displays a
mysql
command with the credentials generated for this pod.
Clean up
Destroy the cluster.
The cluster is destroyed.
Next steps
You launched Vault in high-availability mode with a Helm chart. Learn more about the Vault Helm chart by reading the documentation or exploring the project source code.
The pod you deployed used annotations to inject the secret into the file system. Explore how pods can retrieve secrets through the Vault Injector service via annotations, or secrets mounted on ephemeral volumes.