Recommended pattern for Vault ACL policy path templates
HashiCorp Vault is known for its ability to provide secrets at scale. An organization may have many applications that can potentially benefit from Vault's centralized secrets management. This tutorial shares patterns for onboarding applications to Vault while minimizing policy management overhead.
You will apply the pattern to a common use case where Vault provides secrets for applications running in Kubernetes clusters. An organization may have hundreds of applications spread out across multiple clusters. Creating a unique policy per application is an easy way to provide initial access. However, without careful planning, it may be difficult to effectively manage and audit application policies over time.
In this pattern, you will use Vault's identity system and policy path templating features to create a single ACL policy that allows multiple applications to access unique secret paths in a secure and auditable way. This will reduce the total number of policies that need to be managed. The approach can be generalized to other use cases with other Auth Methods and secrets engines.
Prerequisites
This tutorial requires the Kubernetes command-line interface (CLI) and the Helm CLI installed, Minikube, the Vault and Consul Helm charts, the sample web application, and additional configuration to bring it all together.
Next, retrieve the web application and additional configuration by cloning the hashicorp-education/learn-vault-policy-path-templates repository from GitHub.
This repository contains supporting content for all of the Vault learn tutorials. The content specific to this tutorial can be found within a sub-directory.
Go into the learn-vault-policy-path-templates
directory.
Working directory
This tutorial assumes that the remainder of commands are executed within this directory.
Start Minikube
Minikube is a CLI tool that provisions and manages the lifecycle of single-node Kubernetes clusters locally inside Virtual Machines (VM) on your system.
Start a Kubernetes cluster.
The initialization process takes several minutes as it retrieves any necessary dependencies and executes various container images.
Verify the status of the Minikube cluster.
Additional waiting
Even if this last command completed successfully, you may have to wait for Minikube to be available. If an error is displayed, try again after a few minutes.
The host, kubelet, apiserver report that they are running. The kubectl
, a
command line interface (CLI) for running commands against Kubernetes cluster, is
also configured to communicate with this recently started cluster.
Install the Vault Helm chart
The Vault Helm chart is able to install a Vault pod, with a vault
service, a
and vault
service account.
Add the HashiCorp Helm repository.
Update all the repositories to ensure helm
is aware of the latest versions.
Install the latest version of the Vault server running in development mode.
The Vault pod is deployed in the default namespace.
Display all the pods within the default namespace.
The vault-0
pod runs a Vault server in development mode.
Development mode
Running a Vault server in development is automatically initialized and unsealed. This is ideal in a learning environment but NOT recommended for a production environment.
Wait until the vault-0
pod is running and ready (1/1
).
Configure Kubernetes auth method
Start an interactive shell session on the
vault-0
pod.Your system prompt is replaced with a new prompt
/ $
. Commands issued at this prompt are executed on thevault-0
container.Based on the path naming convention, set the
CLUSTER_NAME
environment value to "minikube" since you are running Vault on minikube.Enable the Kubernetes authentication method.
Configure the Kubernetes authentication method to use the location of the Kubernetes host.
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.Enable the audit log.
Exit the
vault-0
pod.
Workflow
The high level steps for this recommended pattern:
- Establish naming conventions
- Write a templated policy
- Onboard applications
- Check metadata populated by Vault
Upon login, Vault auto-populates certain metadata that can be used as part of templated ACL policies. The pattern can be extended by including custom metadata as part of the secret access path. This will be demonstrated in Step 5: Including custom metadata.
Step 1: Establish naming conventions
Best practice: Establish naming standards that allow applications to login and read secrets from consistent and predictable paths. Each application in an organization will likely have a unique identifier such as an application ID and be part of a logical group such as a line of business (LOB), project etc. These elements can be used to create the naming convention.
Example
The table below to help plan for the various relevant paths within Vault. It is encouraged that you build this out for a representative use-case in your organization. For this guide, the application grouping is based on Kubernetes Namespaces, where each Namespace represents a line of business (LOB). You will also create a Kubernetes Service Account per application which is considered a common best practice.
You will choose the LOB name retail, and app100, app200 and app300 as application IDs for the examples in this guide.
Vault item | Example for Kubernetes applications |
---|---|
Auth Method Mount path | The default path is kubernetes, but we recommend making it specific to a cluster name, since each cluster has a different API endpoint. - Naming convention: <cluster-name> - Examples: minikube , gke-useast1 etc. |
Auth Method Role name | We recommend making the role name something predictable for the application to login. - Naming convention: <namespace>-<app-name> - Example: retail-app100 |
Login path | The Login endpoint for Kubernetes Auth method will be: - API endpoint: auth/<auth-mount>/login Following our scheme, these will translate to: - Naming convention: auth/<cluster-name>/login/ - Example: auth/minikube/login |
Secret Engine Mount path | The secret engine can be mounted anywhere. In this example, you will mount one secret engine per Kubernetes Namespace: - Naming convention: secrets/<namespace> - Example: secret/data/retail |
Secret access path | We will assume that each application will access secrets at the following path: - Naming convention: secrets/<namespace>/<app-name> - Example: secret/data/retail/app100 |
The actual paths on the above table will vary depending on how the company is structured but it should give us a starting point for organizing elements within Vault.
Step 2: Write a templated policy and associate it with the login role
The Available Templating Parameters table shows various dynamic substitutions for a templated ACL policy. These parameters can be used to construct a policy that aligns with the naming conventions from step 1. We are interested in the <metadata key>
dynamic element for this pattern. For the Kubernetes Authentication method, the available metadata keys can be found in the Login endpoint API Sample Response.
Recall from the table in step 1 that the secret access path is: secrets/<namespace>/<app-name>
. Below is a mapping between the desired path elements and metadata keys.
<namespace>
:identity.entity.aliases.<mount accessor>.metadata.service_account_namespace.
This is the Kubernetes Namespace.<app-name>
:identity.entity.aliases.<mount accessor>.metadata.service_account_name.
This is the Kubernetes Service Account name for the application.
To access the metadata for an auth method requires its unique mount accessor. Display the mount accessors for all auth methods.
Example output:
The "Accessor" column displays the
kubernetes
auth method's mount accessor.Create a variable to store the mount accessor for the
kubernetes
auth method.Create an ACL policy, named
minikube-kv-read
, that grantsread
andlist
capabilities to secrets at the pathsecret/<service_account_namespace>/<service_account_name>
.This templatized policy uses
identity.entity.aliases.$MOUNT_ACCESSOR.metadata.service_account_namespace
andidentity.entity.aliases.$MOUNT_ACCESSOR.metadata.service_account_name
to create a policy that is flexible based on the Kubernetes namespace and service account.Refer to the complete list of available templating parameters for more information.
Display the
minikube-kv-read
policy.The created policy displays the templatized path with the
kubernetes/
auth method's mount accessor in the path.
Step 3: Onboard applications
In this step, you will deploy a demo application named basic-example
and ensure that it can read the intended secret using the templated policy. You will repeat the process once to see how multiple applications can access unique paths using the same ACL policy.
Create Role and write secret
Create a Kubernetes Service Account and a Vault login role. The role allows us to enforce the application Service Account name and Namespace, and associate the templated ACL policy. You will also store a secret in the KV secrets engine for this application.
Create an
APP_NAME
environment variable.Create an
APP_NAMESPACE
environment variable.Create an
CLUSTER_NAME
environment variable.Create a Kubernetes namespace called, "retail".
Create application Service Account.
Each application requires a unique Kubernetes auth method role path based on the application's service account and namespace. Create a Kubernetes auth role named
$APP_NAMESPACE-$APP_NAME
.Output:
Write a secret for the
secret/data/<APP_NAMESPACE>/<APP_NAME>
path.Output:
Deploy application
Use the commands below to create a Kubernetes Deployment object. As a first step, you will make a series of substitutions using the sed
command to create a Deployment manifest yaml file from a template. Please display the resulting yaml file and validate the items below.
- The deployment yaml file should contain a
SECRET_PATH
environment variable with the value:secret/data/retail/app123
. The application will try to read secrets from this path. - The
VAULT_ADDR
environment variable should reflect the DNS name where application pods can reach Vault. If Vault is deployed in the same Kubernetes cluster, please issue the commandkubectl get svc
to view the vault service name. Assuming that the service name is vault, and the Namespace is default, then the FQDN will be vault.default.svc.cluster.local (please see Kubernetes DNS records).
Create the app deployment yaml file with a set of substitutions.
Display the deployment file to ensure all the fields are correct.
Create the deployment.
Verify that the pod is running.
Wait until the pod, prefixed with the
$APP_NAME
, is running and ready (1/1
).
Display logs
Create a variable to store the unique name of the pod.
Display the logs of the pod.
The log displays the secret path and secret that it request from Vault.
Warning
Do not write secrets to logs in production. This is for demonstration only. This application will read its service account JWT and use it to authenticate with Vault. It will then log the secret and keep the token renewed. Below is a snippet from the application log showing a successful secret read from the secret access path.
The Vault audit log should show that Vault applied the
minikube-kv-read
ACL policy upon login and dynamically allowed access to the secret path:secrets/data/retail/app100
.
Onboard additional application
You will use this method to onboard another application called app200
in the same Namespace. Since the secret path scheme will remain the same, secret/data/<namespace>/<app-name>
, you will not need to create another policy.
Create Role and write secret
Issue the commands below to create an application role, associate the policy and store a secret.
Update the
APP_NAME
environment variable value.Create an application Service Account.
Create role and associate
kv-read
policy.Write a secret for the
secret/data/<APP_NAMESPACE>/<APP_NAME>
path.
Deploy application
Create the Deployment manifest file which should now contain a SECRET_PATH
with the value: secret/data/retail/app200
.
Create the app deployment yaml file with a set of substitutions.
Create the application and display pods.
Verify that the pod is running.
Display logs
Display the application logs and check for a successful login and secrets retrieval from
secret/data/retail/app200
:Read application logs.
If you change the
SECRET_PATH
tosecret/data/retail/app100
in the deployment-retail-app200.yaml, then the secrets retrieval will fail forapp200
. Below is a snippet from the application logs when theapp200
was deployed with theSECRET_PATH: "secret/data/retail/app100"
:
The above failure confirms that the templated policy is working as intended. This is because the ACL policy only allows access to a path ending with the application's own service account name which is app200
. This prevents service accounts from being reused.
Step 4: Check metadata populated by Vault
This is an optional step to view the entity and entity-alias objects that Vault created automatically upon login. It will also allow us to review the Entity alias metadata populated by Vault. Below are the steps to view these items using the UI and the CLI.
Note
At least one previous successful login using the Kubernetes Authentication method is needed before proceeding with these steps.
If you deployed both app100 and app200, you should see two Entity objects. Each Entity will be linked to an Authentication alias named as the Kubernetes service account ID (represented as service_account_uid
under alias metadata).
You can expose the Vault UI with port-forwarding.
Login to Vault UI with address
http://127.0.0.1:8200
, and enterroot
as password.Select Access, and then Entities.
Select an entity and then select the Aliases tab.
Select the alias, and then Metadata tab to see auto-populated metadata keys.
Vault automatically created the entity and entity alias objects, and then added the Kubernetes namespace and Service Account names as the metadata. You used this information to create a templated ACL policy that applied to multiple applications. The uniqueness of the metadata is what allows us to create one ACL policy that can apply to multiple applications.
Step 5: Include custom metadata
Thus far the secret access path included auto-populated metadata: the Kubernetes Service Account Name and Namespace. However, you may want to include custom information as part of the secret access path that is not automatically populated by Vault.
To illustrate this scenario, let's imagine that the Secret access path from the table in step 1 needs to have a geography name in it.
- Naming convention:
secret/data/<namespace>/<geography>/<app-name>
- Examples:
In this scenario, presumably each geography has the same application deployed and the secrets stored in Vault need to contain some localized information. The <namespace>
and <app-name>
elements will be auto-populated by Vault as confirmed earlier in Step 4. However, the <geography>
portion will need to be added as a custom metadata. To add custom metadata the Vault Entity and Entity-Alias objects will need to be created prior to login.
Create application Service Account, Entity and Entity-Alias
Create the Kubernetes Service Account called app300, then create the Vault Entity and Entity Alias objects. You will add two metadata fields: geography and owner. The owner field is optional and only for tracking purposes, it will not be used as part of the secret access path.
Update the
APP_NAME
environment variable value.Create application Service Account.
Create an entity with metadata information.
Store the generated entity ID.
Obtain the service account name UID.
Create the alias to associate the entity and alias. (You set the
MOUNT_ACCESSOR
environment variable in step 2.)Example output:
Check the UI to see the new Vault entity by selecting Access > Entities.
Select the app300 entity and choose the Metadata tab to view the custom metadata that was just entered.
The entity and metadata can be displayed using the CLI.
Example output:
Create policy with custom metadata
Create a new templated policy which includes the custom metadata: geography. Note the additional templating element {{identity.entity.metadata.geography}}
being used to populate the <geography>
portion of the secret access path.
Write the templated ACL policy.
The remaining steps to create the application role and deploy the application should be familiar from before. The snippets are included below.
Create Role and write secret
Update the
APP_NAME
environment variable value toapp300
.Create a role and attach the
kv-read
policy.Write a secret for the
secrets/data/<namespace>/<geo>/<APP_NAME>
path.
Deploy application
After executing the substitution commands, the resulting deployment yaml manifest should indicate SECRET_PATH
as secret/data/retail/useast1/app300
.
Create the app deployment yaml file with a set of substitutions.
Create the deployment.
Verify that the pod is running.
Display logs
Display the application logs and check for a successful login and secrets retrieval from secret/data/retail/us-east1/app300
.
Read the application logs.
Example output:
To deploy the application in another geography, the approximate steps will be as follows:
- Create the Kubernetes application Service Account
- Create the Entity (with metadata) and Entity-Alias
- Create Vault Role and write a secret
- Deploy application
Conclusion
In this pattern, you reviewed how to use a single templated ACL policy for multiple applications. The main advantage of this approach over writing a policy per application is to simplify onboarding steps. Fewer policies also result in easier policy management and auditing. Finally, this pattern enforces standard conventions on where applications can read and write secrets from within Vault, allowing for better organization and anomaly detection.
While you have provided a Kubernetes-based example, other Authentication methods will also have associated metadata that can be useful in creating templated policies. Similarly, while you accessed the static Key/Value secrets engine, the secret access path can be applied to dynamic secrets engines where the effective path corresponds to an application role.
Reference material
- Auth Methods: This documentation describes how clients can login to Vault using Auth Method plugins.
- Kubernetes Authentication Method: This documentation covers how to authenticate Kubernetes applications to Vault. The Recommended Pattern described here uses the Kubernetes Auth Method.
- Identity Entity: This documentation describes the Entity system in Vault.
- ACL Policies learn guide: This Learn guide covers how to restrict access to secret paths in Vault by defining ACL policies.
- ACL Policy Path Templates: This Learn guide covers how to use the ACL Templating feature.