• HashiCorp Developer

  • HashiCorp Cloud Platform
  • Terraform
  • Packer
  • Consul
  • Vault
  • Boundary
  • Nomad
  • Waypoint
  • Vagrant
Vault
  • Install
  • Tutorials
  • Documentation
  • API
  • Try Cloud(opens in new tab)
  • Sign up
Recommended Patterns

Skip to main content
6 tutorials
  • Recommended Pattern for Vault Unseal
  • Recommended Pattern for Stateless Vault for Transit Auto Unseal
  • Recommended Pattern for Vault AppRole Use
  • Recommended Pattern for Vault Centralized Secrets Management
  • Recommended Pattern for Vault ACL Policy Path Templates
  • Vault Namespace and Mount Structuring Guide

  • Resources

  • Tutorial Library
  • Certifications
  • Community Forum
    (opens in new tab)
  • Support
    (opens in new tab)
  • GitHub
    (opens in new tab)
  1. Developer
  2. Vault
  3. Tutorials
  4. Recommended Patterns
  5. Recommended Pattern for Vault ACL Policy Path Templates

Recommended Pattern for Vault ACL Policy Path Templates

  • 22min

  • EnterpriseEnterprise
  • VaultVault

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 Secret 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/vault-guides repository from GitHub.

$ git clone https://github.com/hashicorp/vault-guides.git

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 vault-guides/identity/pattern-policy-templates directory.

$ cd vault-guides/identity/pattern-policy-templates

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.

$ minikube start --driver=docker
😄  minikube v1.12.1 on Darwin 10.15.6
✨  Using the docker driver based on user configuration
👍  Starting control plane node minikube in cluster minikube
🔥  Creating docker container (CPUs=2, Memory=1991MB) ...
🐳  Preparing Kubernetes v1.18.3 on Docker 19.03.2 ...
🔎  Verifying Kubernetes components...
🌟  Enabled addons: default-storageclass, storage-provisioner
🏄  Done! kubectl is now configured to use "minikube"

The initialization process takes several minutes as it retrieves any necessary dependencies and executes various container images.

Verify the status of the Minikube cluster.

$ minikube status

minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

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.

$ helm repo add hashicorp https://helm.releases.hashicorp.com
"hashicorp" has been added to your repositories

Update all the repositories to ensure helm is aware of the latest versions.

$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "hashicorp" chart repository
Update Complete. ⎈Happy Helming!⎈

Install the latest version of the Vault server running in development mode.

$ helm install vault hashicorp/vault --set "server.dev.enabled=true"
NAME: vault
## ...

The Vault pod is deployed in the default namespace.

Display all the pods within the default namespace.

$ kubectl get pods
NAME                                    READY   STATUS    RESTARTS   AGE
vault-0                                 1/1     Running   0          80s
vault-agent-injector-5945fb98b5-tpglz   1/1     Running   0          80s

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

  1. Start an interactive shell session on the vault-0 pod.

    $ kubectl exec -it vault-0 -- /bin/sh
    / $
    

    Your system prompt is replaced with a new prompt / $. Commands issued at this prompt are executed on the vault-0 container.

  2. Based on the path naming convention, set the CLUSTER_NAME environment value to "minikube" since you are running Vault on minikube.

    $ export CLUSTER_NAME="minikube"
    
  3. Enable the Kubernetes authentication method.

    $ vault auth enable -path=$CLUSTER_NAME kubernetes
    Success! Enabled kubernetes auth method at: minikube/
    
  4. 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.

    $ vault write auth/$CLUSTER_NAME/config \
        kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"
    

    Output:

    Success! Data written to: auth/minikube/config
    

    The environment variable KUBERNETES_PORT_443_TCP_ADDR is defined and references the internal network address of the Kubernetes host.

  5. Enable the audit log.

    $ vault audit enable file file_path=/tmp/audit_log.json
    
  6. Exit the vault-0 pod.

    $ exit
    

Workflow

The high level steps for this recommended pattern:

  1. Establish naming conventions
  2. Write a templated policy
  3. Onboard applications
  4. 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 itemExample for Kubernetes applications
Auth Method Mount pathThe 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 nameWe recommend making the role name something predictable for the application to login.
- Naming convention: <namespace>-<app-name>
- Example: retail-app100
Login pathThe 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 pathThe secret engine can be mounted anywhere. In this example, you will mount one secret
engine per Kubernetes Namespace:
- Naming convention: kv/<namespace>
- Example: kv/retail
Secret access pathWe will assume that each application will access secrets at the following path:
- Naming convention: kv/<namespace>/<app-name>
- Example: kv/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: kv/<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.
  1. To access the metadata for an auth method requires its unique mount accessor. Display the mount accessors for all auth methods.

    $ kubectl exec --namespace default vault-0 -- vault auth list
    

    Example output:

    Path         Type          Accessor                    Description
    ----         ----          --------                    -----------
    minikube/    kubernetes    auth_kubernetes_f9be05d8    n/a
    token/       token         auth_token_8b8425b0         token based credentials
    

    The "Accessor" column displays the kubernetes auth method's mount accessor.

  2. Create a variable to store the mount accessor for the kubernetes auth method.

    $ export MOUNT_ACCESSOR=$(kubectl exec --namespace default vault-0 -- vault auth list -format=json | jq -r '."minikube/".accessor')
    
  3. Create an ACL policy, named kubernetes-kv-read, that grants read and list capabilities to secrets at the path secret/<service_account_namespace>/<service_account_name>.

    $ kubectl exec -i --namespace default vault-0 -- \
      vault policy write kubernetes-kv-read - << EOF
        path "secret/data/{{identity.entity.aliases.$MOUNT_ACCESSOR.metadata.service_account_namespace}}/{{identity.entity.aliases.$MOUNT_ACCESSOR.metadata.service_account_name}}" {
            capabilities=["read","list"]
        }
    EOF
    

    This templatized policy uses identity.entity.aliases.$MOUNT_ACCESSOR.metadata.service_account_namespace and identity.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.

  4. Display the kubernetes-kv-read policy.

    $ kubectl exec --namespace default vault-0 -- vault policy read kubernetes-kv-read
    

    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.

  1. Create an APP_NAME environment variable.

    $ export APP_NAME=app100
    
  2. Create an APP_NAMESPACE environment variable.

    $ export APP_NAMESPACE=retail
    
  3. Create an CLUSTER_NAME environment variable.

    $ export CLUSTER_NAME=minikube
    
  4. Create a Kubernetes namespace called, "retail".

    $ kubectl create ns $APP_NAMESPACE
    
  5. Create application Service Account.

    $ kubectl create sa $APP_NAME -n $APP_NAMESPACE
    
  6. 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.

    $ kubectl exec --namespace default -it vault-0 --  \
        vault write auth/$CLUSTER_NAME/role/$APP_NAMESPACE-$APP_NAME \
           bound_service_account_names=$APP_NAME \
           bound_service_account_namespaces=$APP_NAMESPACE \
           policies=$CLUSTER_NAME-kv-read \
           period=120s
    

    Output:

    Success! Data written to: auth/minikube/role/retail-app100
    
  7. Write a secret for the kv/<APP_NAMESPACE>/<APP_NAME> path.

    $ kubectl exec --namespace default -it vault-0 -- \
        vault kv put secret/$APP_NAMESPACE/$APP_NAME \
          app=$APP_NAME \
          username=demo \
          password=test
    

    Output:

    Key              Value
    ---              -----
    created_time     2021-02-19T07:43:59.618761Z
    deletion_time    n/a
    destroyed        false
    version          1
    

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: kv/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 command kubectl 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).
  1. Change to the basic-example directory.

    $ cd basic-example/
    
  2. Create the app deployment yaml file with a set of substitutions.

    $ cat deployment-template.yaml \
        | sed -e s/"my-deployment"/"$APP_NAME-deployment"/ \
              -e s/"my-namespace"/$APP_NAMESPACE/ \
              -e s/"my-app-name"/"$APP_NAME"/g \
              -e s/"my-app-sa"/$APP_NAME/ \
              -e s/"login-path"/"auth\/$CLUSTER_NAME\/login"/ \
              -e s/"secret-path"/"secret\/data\/$APP_NAMESPACE\/$APP_NAME"/ \
              -e s/"my-role"/"$APP_NAMESPACE-$APP_NAME"/ \
        > deployment-$APP_NAMESPACE-$APP_NAME.yaml
    
  3. Display the deployment file to ensure all the fields are correct.

    $ cat deployment-$APP_NAMESPACE-$APP_NAME.yaml
    
  4. Create the deployment.

    $ kubectl create -f deployment-$APP_NAMESPACE-$APP_NAME.yaml
    
  5. Verify that the pod is running.

    $ kubectl get pods -n $APP_NAMESPACE
    
    NAME                                READY   STATUS    RESTARTS   AGE
    app100-deployment-74dcb4c76-gqxhz   1/1     Running   0          39s
    

    Wait until the pod, prefixed with the $APP_NAME, is running and ready (1/1).

Display logs

  1. Create a variable to store the unique name of the pod.

    $ export POD_NAME=$(kubectl get pods -l app=$APP_NAME \
        -o jsonpath='{.items[0].metadata.name}' --namespace=$APP_NAMESPACE)
    
  2. Display the logs of the pod.

    $ kubectl logs $POD_NAME --namespace $APP_NAMESPACE
    
    ==> WARNING: Don't ever write secrets to logs.
    ==>          This is for demonstration only.
    s.dTAovm6YUfLorrnNSzduIzr9
    secret secret/data/marketing/webapp -> &{90679862-6968-d539-4074-3c2fde92094b  0 false map[data:map[app:webapp password:test username:demo] metadata:map[created_time:2021-02-10T22:26:10.9068052Z deletion_time: destroyed:false version:1]] [] <nil> <nil>}
    Starting renewal loop
    Successfully renewed: &api.RenewOutput{RenewedAt:time.Time{wall:0x237141e8, ext:63748594333, loc:(*time.Location)(nil)}, Secret:(*api.Secret)(0xc00004c1e0)}
    secret secret/data/marketing/webapp -> &{2fb03b8e-8574-3bda-6dc6-fd7f1d436fc2  0 false map[data:map[app:webapp password:test username:demo] metadata:map[created_time:2021-02-10T22:26:10.9068052Z deletion_time: destroyed:false version:1]] [] <nil> <nil>}
    

    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.

  3. 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: kv/retail/app100.

    $ kubectl exec -it vault-0 -- cat /tmp/audit_log.json | jq .
    {
      ...snip...
      "auth": {
        "client_token": "hmac-sha256:97aaf5c989f...",
        ...snip...
        "token_policies": [
          "default", "minikube-kv-read" ],
        "metadata": {
          "role": "retail-app100",
          "service_account_name": "app100",
          "service_account_namespace": "retail",
          ...snip...
        "operation": "read", "mount_type": "kv",
        ...snip...
        "path": "kv/retail/app100",
      },
      "response": {
        "mount_type": "kv",
        ...snip...
        "data": {
          "app": "hmac-sha256:7af4ccfd...",
          "password": "hmac-sha256:da5ff1936...",
          "username": "hmac-sha256:ff948a5ec..." }
    

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, kv/<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.

  1. Update the APP_NAME environment variable value.

    $ export APP_NAME=app200
    
  2. Create an application Service Account.

    $ kubectl create sa $APP_NAME -n $APP_NAMESPACE
    
  3. Create role and associate kv-read policy.

    $ kubectl exec --namespace default -it vault-0 --  \
        vault write auth/$CLUSTER_NAME/role/$APP_NAMESPACE-$APP_NAME \
           bound_service_account_names=$APP_NAME \
           bound_service_account_namespaces=$APP_NAMESPACE \
           policies=$CLUSTER_NAME-kv-read \
           period=120s
    
  4. Write a secret for the kv/<APP_NAMESPACE>/<APP_NAME> path.

    $ kubectl exec --namespace default -it vault-0 -- \
        vault kv put secret/$APP_NAMESPACE/$APP_NAME \
          app=$APP_NAME \
          username=demo \
          password=test
    

Deploy application

Create the Deployment manifest file which should now contain a SECRET_PATH with the value: kv/retail/app200.

  1. Create the app deployment yaml file with a set of substitutions.

    $ cat deployment-template.yaml \
        | sed -e s/"my-deployment"/"$APP_NAME-deployment"/ \
              -e s/"my-namespace"/$APP_NAMESPACE/ \
              -e s/"my-app-name"/"$APP_NAME"/g \
              -e s/"my-app-sa"/$APP_NAME/ \
              -e s/"login-path"/"auth\/$CLUSTER_NAME\/login"/ \
              -e s/"secret-path"/"secret\/data\/$APP_NAMESPACE\/$APP_NAME"/ \
              -e s/"my-role"/"$APP_NAMESPACE-$APP_NAME"/ \
        > deployment-$APP_NAMESPACE-$APP_NAME.yaml
    
  2. Create the application and display pods.

    $ kubectl create -f deployment-$APP_NAMESPACE-$APP_NAME.yaml
    
  3. Verify that the pod is running.

    $ kubectl get pods -n $APP_NAMESPACE
    
    NAME                                 READY   STATUS    RESTARTS   AGE
    app100-deployment-74dcb4c76-gqxhz    1/1     Running   0          12m
    app200-deployment-7c876b7597-p75ns   1/1     Running   0          14s
    

Display logs

  1. Display the application logs and check for a successful login and secrets retrieval from kv/retail/app200:

    $ export POD_NAME=$(kubectl get pods -l app=$APP_NAME \
        -o jsonpath='{.items[0].metadata.name}' --namespace=$APP_NAMESPACE)
    
  2. Read application logs.

    $ kubectl logs $POD_NAME -n $APP_NAMESPACE
    

    If you change the SECRET_PATH to kv/retail/app100, then the secrets retrieval will fail for app200. Below is a snippet from the application logs when the app200 was deployed with the SECRET_PATH: "kv/retail/app100":

    URL: GET http://vault.default.svc.cluster.local:8200/v1/secret/data/retail/app200
    Code: 403. Errors:
    
    * 1 error occurred:
            * permission denied
    

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.

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).

  1. You can expose the Vault UI with port-forwarding.

    $ kubectl port-forward vault-0 8200:8200
    Forwarding from 127.0.0.1:8200 -> 8200
    Forwarding from [::1]:8200 -> 8200
    ##...
    
  2. Login to Vault UI with address http://127.0.0.1:8200, and enter root as password.

  3. Select Access, and then Entities.

    Vault Entities

  4. Select an entity and then select the Aliases tab.

  5. Select the alias, and then Metadata tab to see auto-populated metadata keys.

    Vault Entity Metadata

Save the list of all entity alias objects, then display each object as part of a loop. The example below shows how to display the first 10 objects. Adjust the variable n to display additional ones.

  1. Save the entity alias list in a file, entity_alias_list.json.

    $ kubectl exec --namespace default -it vault-0 -- \
         vault list -format=json identity/entity-alias/id | jq . -r > entity_alias_list.json
    
  2. View the entity_alias_list.json file to list the retrieved entity IDs.

    $ cat entity_alias_list.json
    

    Example output:

    [
      "15906b75-6910-147a-960c-9058effb3e63",
      "2a0c107d-31d4-fa7d-b66e-630d4599373f"
    ]
    
  3. Retrieve the first entity alias's metadata.

    $ kubectl exec --namespace default -it vault-0 -- \
        vault read identity/entity-alias/id/$(cat entity_alias_list.json | jq -r ".[0]")
    

    Example output:

    Key                          Value
    ---                          -----
    canonical_id                 28f22e87-d7b3-9b8f-daae-045aaf13374f
    creation_time                2021-02-19T08:53:27.6657874Z
    id                           15906b75-6910-147a-960c-9058effb3e63
    last_update_time             2021-02-19T08:53:27.6657874Z
    merged_from_canonical_ids    <nil>
    metadata                     map[service_account_name:app200 service_account_namespace:retail service_account_secret_name:app200-token-9fwpg service_account_uid:d5b94793-f64f-416c-a842-d6bbaa420551]
    mount_accessor               auth_kubernetes_f9be05d8
    mount_path                   auth/minikube/
    mount_type                   kubernetes
    name                         d5b94793-f64f-416c-a842-d6bbaa420551
    namespace_id                 root
    

    The metadata will be shown as a map of key value pairs.

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: kv/<namespace>/<geography>/<app-name>
  • Examples:
    • kv/retail/useast1/app300
    • kv/retail/emea1/app300
    • kv/retail/apac1/app300

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.

  1. Update the APP_NAME environment variable value.

    $ export APP_NAME=app300
    
  2. Create application Service Account.

    $ kubectl create sa $APP_NAME -n $APP_NAMESPACE
    
  3. Create an entity with metadata information.

    $ kubectl exec --namespace default -it vault-0 -- \
         vault write identity/entity name=$APP_NAME \
            metadata='geography=useast1' \
            metadata='owner=foo'
    
  4. Store the generated entity ID.

    $ export ENTITY_ID=$(kubectl exec --namespace default -it vault-0 -- \
         vault read identity/entity/name/$APP_NAME -format=json \
         | jq -r '.data.id')
    
  5. Obtain the service account name UID.

    $ export SERVICE_ACCOUNT_UID=$(kubectl get sa $APP_NAME -n retail \
         -o jsonpath="{.metadata.uid}")
    
  6. Create the alias to associate the entity and alias. (You set the MOUNT_ACCESSOR environment variable in step 2.)

    $ kubectl exec --namespace default -it vault-0 -- \
        vault write identity/entity-alias name=$SERVICE_ACCOUNT_UID \
           canonical_id=$ENTITY_ID \
           mount_accessor=$MOUNT_ACCESSOR
    

    Example output:

    Key             Value
    ---             -----
    canonical_id    d31535dd-22db-ea1a-f187-a9a9fae2884b
    id              ee47946c-008c-b15d-fd2a-c529b403bfdc
    
  7. Check the UI to see the new Vault entity by selecting Access > Entities.

    Vault Entities

  8. Select the app300 entity and choose the Metadata tab to view the custom metadata that was just entered.

    Vault Entities

  9. The entity and metadata can be displayed using the CLI.

    $ kubectl exec --namespace default -it vault-0 -- \
        vault read identity/entity/name/app300
    

    Example output:

    Key                    Value
    ---                    -----
    aliases                [map[canonical_id:d31535dd-22db-ea1a-f187-a9a9fae2884b creation_time:2021-02-19T10:20:34.4567655Z id:ee47946c-008c-b15d-fd2a-c529b403bfdc last_update_time:2021-02-19T10:20:34.4567655Z merged_from_canonical_ids:<nil> metadata:<nil> mount_accessor:auth_kubernetes_f9be05d8 mount_path:auth/minikube/ mount_type:kubernetes name:65652c6a-5352-4711-b37a-e1b4c64cbc7b]]
    creation_time          2021-02-19T10:09:59.1194815Z
    direct_group_ids       []
    disabled               false
    group_ids              []
    id                     d31535dd-22db-ea1a-f187-a9a9fae2884b
    inherited_group_ids    []
    last_update_time       2021-02-19T10:20:34.4567531Z
    merged_entity_ids      <nil>
    metadata               map[geography:useast1 owner:foo]
    name                   app300
    namespace_id           root
    policies               <nil>
    

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.

$ kubectl exec -i --namespace default vault-0 -- \
  vault policy write $CLUSTER_NAME-geo-kv-read - << EOF
 path "secret/data/{{identity.entity.aliases.$MOUNT_ACCESSOR.metadata.service_account_namespace}}/{{identity.entity.metadata.geography}}/{{identity.entity.aliases.$MOUNT_ACCESSOR.metadata.service_account_name}}" {
       capabilities=["read","list"]
}
EOF

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

  1. Update the APP_NAME environment variable value to app300.

    $ export APP_NAME=app300
    
  2. Create a role and attach the kv-read policy.

    $ kubectl exec --namespace default -it vault-0 --  \
        vault write auth/$CLUSTER_NAME/role/$APP_NAMESPACE-$APP_NAME \
           bound_service_account_names=$APP_NAME \
           bound_service_account_namespaces=$APP_NAMESPACE \
           policies=$CLUSTER_NAME-geo-kv-read \
           period=120s
    
  3. Write a secret for the kv/<namespace>/<geo>/<APP_NAME> path.

    $ kubectl exec --namespace default -it vault-0 -- \
        vault kv put secret/$APP_NAMESPACE/useast1/$APP_NAME \
          app=$APP_NAME \
          username=demo \
          password=test
    

Deploy application

After executing the substitution commands, the resulting deployment yaml manifest should indicate SECRET_PATH as kv/retail/useast1/app300.

  1. Create the app deployment yaml file with a set of substitutions.

    $ cat deployment-template.yaml \
        | sed -e s/"my-deployment"/"$APP_NAME-deployment"/ \
              -e s/"my-namespace"/$APP_NAMESPACE/ \
              -e s/"my-app-name"/"$APP_NAME"/g \
              -e s/"my-app-sa"/$APP_NAME/ \
              -e s/"login-path"/"auth\/$CLUSTER_NAME\/login"/ \
              -e s/"secret-path"/"secret\/data\/$APP_NAMESPACE\/useast1/\/$APP_NAME"/ \
              -e s/"my-role"/"$APP_NAMESPACE-$APP_NAME"/ \
        > deployment-$APP_NAMESPACE-$APP_NAME.yaml
    
  2. Create the deployment.

    $ kubectl create -f deployment-$APP_NAMESPACE-$APP_NAME.yaml
    
  3. Verify that the pod is running.

    $ kubectl get pods -n $APP_NAMESPACE
    
    NAME                                 READY   STATUS    RESTARTS   AGE
    app100-deployment-74dcb4c76-gqxhz    1/1     Running   0          110m
    app200-deployment-7c876b7597-p75ns   1/1     Running   0          97m
    app300-deployment-7cb9dc57f8-668lb   1/1     Running   0          8s
    

Display logs

Display the application logs and check for a successful login and secrets retrieval from kv/retail/us-east1/app300.

$ export POD_NAME=$(kubectl get pods -l app=$APP_NAME \
    -o jsonpath='{.items[0].metadata.name}' --namespace=$APP_NAMESPACE)

Read the application logs.

$ kubectl logs $POD_NAME -n $APP_NAMESPACE

Example output:

==> WARNING: Don't ever write secrets to logs.
==>          This is for demonstration only.
s.JRWjqG50ZzuQdihxpC51kFvl
secret secret/data/retail/useast1/app300 -> &{5a6b79be-719b-913b-5b38-867acacd0456  0 false map[data:map[app:app300 password:test username:demo] metadata:map[created_time:2021-02-19T11:31:12.8160839Z deletion_time: destroyed:false version:1]] [] <nil> <nil>}
Starting renewal loop
Successfully renewed: &api.RenewOutput{RenewedAt:time.Time{wall:0xa1d4520, ext:63749331458, loc:(*time.Location)(nil)}, Secret:(*api.Secret)(0xc000292900)}
secret secret/data/retail/useast1/app300 -> &{1b2e9fab-b266-c2c0-b988-6c1840cf2a2f  0 false map[data:map[app:app300 password:test username:demo] metadata:map[created_time:2021-02-19T11:31:12.8160839Z deletion_time: destroyed:false version:1]] [] <nil> <nil>}

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 secret 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.
 Previous
 Next

On this page

  1. Recommended Pattern for Vault ACL Policy Path Templates
  2. Prerequisites
  3. Workflow
  4. Step 1: Establish naming conventions
  5. Step 2: Write a templated policy and associate it with the login role
  6. Step 3: Onboard applications
  7. Onboard additional application
  8. Step 4: Check metadata populated by Vault
  9. Step 5: Include custom metadata
  10. Conclusion
  11. Reference material
Give Feedback(opens in new tab)
  • Certifications
  • System Status
  • Terms of Use
  • Security
  • Privacy
  • Trademark Policy
  • Trade Controls
  • Give Feedback(opens in new tab)