New to KubeVault? Please start here.

Vault Policy Management

You can easily manage Vault Policy in Kubernetes way using Vault operator.

You should be familiar with the following CRD:

Before you begin:

  • Install Vault operator in your cluster following the steps here.

  • Deploy Vault. It could be in the Kubernetes cluster or external.

To keep things isolated, we are going to use a separate namespace called demo throughout this tutorial.

$ kubectl create ns demo
namespace/demo created

VaultPolicy

Using VaultPolicy, you can create, update and delete policy in Vault. In this tutorial, we are going to create demo-policy in demo namespace.

apiVersion: policy.kubevault.com/v1alpha1
kind: VaultPolicy
metadata:
  name: demo-policy
  namespace: demo
spec:
  vaultAppRef:
    name: vault
    namespace: demo
  policy: |
    path "secret/*" {
      capabilities = ["create", "read", "update", "delete", "list"]
    }

We already have deployed Vault using Vault operator. For this tutorial, we deployed Vault with inmem backend storage and unsealed with kubernetes secret mode.

$ kubectl get vaultserver -n demo
NAME      NODES     VERSION   STATUS    AGE
vault     1         1.0.0     Running   46s
$ kubectl get vaultserver/vault -n demo -o yaml
apiVersion: kubevault.com/v1alpha1
kind: VaultServer
metadata:
  name: vault
  namespace: demo
spec:
  backend:
    inmem: {}
  nodes: 1
  unsealer:
    mode:
      kubernetesSecret:
        secretName: vault-keys
    secretShares: 4
    secretThreshold: 2
  version: 1.0.0
status:
  clientPort: 8200
  initialized: true
  observedGeneration: 1$6208915667192219204
  phase: Running
  serviceName: vault
  updatedNodes:
  - vault-69cc8bfb45-jggnz
  vaultStatus:
    active: vault-69cc8bfb45-jggnz
    unsealed:
    - vault-69cc8bfb45-jggnz

When Vault is deployed by Vault operator using VaultServer. It does following things:

  • It enables and configures the Kubernetes auth in Vault.

  • It also create a policy in Vault that has policy create, update and delete permission. Also create a role in Vault that binds a serviceaccount with policy. This serviceaccount name and role name are specified in AppBinding that is also created by Vault operator.

For VaultServer, AppBinding is created in same namespace of the VaultServer CRD and also name is the same as VaultServer CRD’s name.

$ kubectl get appbindings/vault -n demo -o yaml
apiVersion: appcatalog.appscode.com/v1alpha1
kind: AppBinding
metadata:
  labels:
    app: vault
    vault_cluster: vault
  name: vault
  namespace: demo
spec:
  clientConfig:
    caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN1RENDQWFDZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFOTVFzd0NRWURWUVFERXdKallUQWUKRncweE9ERXlNall3TmpBeU1qaGFGdzB5T0RFeU1qTXdOakF5TWpoYU1BMHhDekFKQmdOVkJBTVRBbU5oTUlJQgpJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBd3kxaFk0eTlrWDNPc0M1eHJJTXlzdzFyCkFMKzY0S1YwSXhNM0ltYlh3b3B2eEFjQVNXdi9PWXJTZ2VPaE9sUnM2aUVLVUNGZjA5blRvVjdlenR0bVVtSzgKUXBCYldxVFd2NHlhbGxpMTFSMUc1VXdIQzlIRUdjeUc4QkRtTTBWL1R2YW94aHkvUkFTZGJFRXltVVZJdSt6Zwp3TVovSWdPQ3ZyRFNiZ291OExFdkZjYTVvQmxic2YzbWNRRUpSVnFnaUx4bjV0MG1VcVROZ2Nzc0pVcWtnL0o4CnpZVlhaTTN5d1NWZ1NwcnIzMFExa1FPYjNzalNSd3diVnlHRG5LMTN0WExkSEJla3JVYjZub0ZPQWJDVWtKUm0KK25ZREQ4TmZ3U1RwRVlzem1CR0dzYkU0UVJoMFVlNGd3a01UMGU4QkxOeVVIelJ5U21QQjh1OHk1V1RPRFFJRApBUUFCb3lNd0lUQU9CZ05WSFE4QkFmOEVCQU1DQXFRd0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHCjl3MEJBUXNGQUFPQ0FRRUFQSjVUK2phaXhTZ3cxS2tSYjdybys5U3VmbHJKZVhaNzZNMktkck9nMTBXUlFFajEKRG5xakdRSStVY0NYbUpEMHRvSXFlNmgvUmtkblVYZlA3WElUVHdlZnR0T0toM0VTbTZGbk9XMFJPSno5eDRTbQpQVFBiLzl5NjIvNE9OZ1RNU1V0MG1RWVBOUm15a0VkZ2tNOTZqRmgwZkRtUGc4dUxkZG5RNlZsaVBmZitUcnQ0CldDNWVhVmdFcWVQSkdGcFR0SHRSblNNR3pRNDRMWjlyYlN4Wk1RV1krYnJJTm9tRHZ6cW1DWjA5Rit0ZFpCVWcKOWRBUllwR28zV2h3enEyNisvS0NSZnNMY01zZ2htZmVxNFlzUW5VelhBL240QVV2bG40MDF2TjhYQmkxbUFtSApoNm1oWlh1YWVlT0tCVTllc2dlMk8wSmJkTW5HZkZjVWlwbmRNUT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    service:
      name: vault
      port: 8200
      scheme: HTTPS
  parameters:
    apiVersion: config.kubevault.com/v1alpha1
    kind: VaultServerConfiguration
    authPath: kubernetes
    serviceAccountName: vault
    policyControllerRole: vault-policy-controller
    authMethodControllerRole: k8s.-.demo.vault-auth-method-controller
    tokenReviewerServiceAccountName: vault-k8s-token-reviewer
    usePodServiceAccountForCsiDriver: true

Here, vault-policy-controller role binds the serviceaccount vault with the policy. So, this serviceaccount vault has policy create, update and delete permission in Vault.

In this tutorial, we are going to use this AppBinding to authenticate against the Vault. See here for Vault authentication using AppBinding in Vault operator.

Now, we are going to create VaultPolicy.

$ cat examples/guides/policy-management/demo-policy.yaml
apiVersion: policy.kubevault.com/v1alpha1
kind: VaultPolicy
metadata:
  name: demo-policy
  namespace: demo
spec:
  vaultAppRef:
    name: vault
    namespace: demo
  policy: |
    path "secret/*" {
      capabilities = ["create", "read", "update", "delete", "list"]
    }

$ kubectl apply -f examples/guides/policy-management/demo-policy.yaml
vaultpolicy.policy.kubevault.com/demo-policy created

Check whether the VaultPolicy is successful.

$ kubectl get vaultpolicies -n demo
NAME                           STATUS    AGE
demo-policy                    Success   1m

Check whether the policy is created in Vault. To resolve the naming conflict, name of policy in Vault will follow this format: k8s.{spec.clusterName or -}.{spec.namespace}.{spec.name}. For this case, it is k8s.-.demo.demo-policy.

$ vault policy list
k8s.-.demo.demo-policy

$ vault policy read k8s.-.demo.demo-policy
path "secret/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

If we delete VaultPolicy demo-policy, then respective policy will also be deleted from Vault.

$ kubectl delete vaultpolicies/demo-policy -n demo
vaultpolicy.policy.kubevault.com "demo-policy" deleted

Check whether the policy is deleted in Vault.

$ vault policy read k8s.-.demo.demo-policy
No policy named: k8s.-.demo.demo-policy

$ vault policy list
default
k8s.-.demo.vault-auth-method-controller
vault-policy-controller
root

VaultPolicyBinding

Using VaultPolicyBinding, you can bind serviceaccount with the policy created using VaultPolicy. Vault operator will create Vault kuberenetes role when VaultPolicyBinding is created. In this tutorial, we are going to create demo-role VaultPolicyBinding in demo namespace.

apiVersion: policy.kubevault.com/v1alpha1
kind: VaultPolicyBinding
metadata:
  name: demo-role
  namespace: demo
spec:
  roleName: "demo-role"
  policies : ["demo-policy"] # name of the VaultPolicies
  serviceAccountNames: ["demo-sa"]
  serviceAccountNamespaces: ["demo"]
  TTL: "1000"
  maxTTL: "2000"
  Period: "1000"

Here, demo-sa in demo namespace will have the permission that is specified in demo-policy VaultPolicy.

Now, we are going to create VaultPolicyBinding demo-role.

$ cat examples/guides/policy-management/demo-role.yaml
apiVersion: policy.kubevault.com/v1alpha1
kind: VaultPolicyBinding
metadata:
  name: demo-role
  namespace: demo
spec:
  roleName: "demo-role"
  policies : ["demo-policy"]
  serviceAccountNames: ["demo-sa"]
  serviceAccountNamespaces: ["demo"]
  TTL: "1000"
  maxTTL: "2000"
  Period: "1000"

$ kubectl apply -f examples/guides/policy-management/demo-role.yaml
vaultpolicybinding.policy.kubevault.com/demo-role created

Check whether the demo-role is successful.

$ kubectl get vaultpolicybindings -n demo
NAME                           STATUS    AGE
demo-role                      Success   2m

Check whether role is created in Vault.

$ vault list auth/kubernetes/role
Keys
----
demo-role

$ vault read auth/kubernetes/role/demo-role
Key                                 Value
---                                 -----
bound_cidrs                         []
bound_service_account_names         [demo-sa]
bound_service_account_namespaces    [demo]
max_ttl                             33m20s
num_uses                            0
period                              0s
policies                            [k8s.-.demo.demo-policy]
ttl                                 16m40s

Now, we are going to login using serviceaccount demo-sa and create secret data in Vault.

# get the serviceaccount token secret name
$ kubectl get serviceaccount/demo-sa -n demo -o json | jq '.secrets'
[
  {
    "name": "demo-sa-token-ddk7c"
  }
]

# get serviceaccount token
$ kubectl get secrets/demo-sa-token-ddk7c -n demo -o json | jq -r '.data.token' | base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZW1vIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlbW8tc2EtdG9rZW4tZGRrN2MiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVtby1zYSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjQ2NDQ4YzhhLTA4ZjYtMTFlOS1iMWYyLTA4MDAyN2I3MTg5ZCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZW1vOmRlbW8tc2EifQ.a604tfmrqtE0Dsaslenqc8LSBIA8CLGXY7xvwORdIGV5dXZHCvyDM-qZTUso_tQasyHduFZd5c0kQFFg6jJHfp3jfVOReGFFzeqOV3TWq-eL79Up8YxZ4Jt8INiTbaGWXF8sU7_kM7aJ_PcXuZ508q62QXoL6_GWIy1I2xjdYfgb3tHKLVZ10GiyMcve6Go8tPbeY2emaSBfVWZGxcJrMuy6qCuSFowfjbpzQJkfKxqXMZN1SNyUuXVkZnpWIqUBDAqBY_NCKRDKt_iwxIDsklXTl8ANsGIc_8FZXVJUKOX5pccs4KARu5_gLxqq14fKvAXV7bwsbSM-Xe03obWyMA

# login with serviceaccount token
$ vault write auth/kubernetes/login role=demo-role jwt="eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZW1vIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlbW8tc2EtdG9rZW4tZGRrN2MiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVtby1zYSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjQ2NDQ4YzhhLTA4ZjYtMTFlOS1iMWYyLTA4MDAyN2I3MTg5ZCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZW1vOmRlbW8tc2EifQ.a604tfmrqtE0Dsaslenqc8LSBIA8CLGXY7xvwORdIGV5dXZHCvyDM-qZTUso_tQasyHduFZd5c0kQFFg6jJHfp3jfVOReGFFzeqOV3TWq-eL79Up8YxZ4Jt8INiTbaGWXF8sU7_kM7aJ_PcXuZ508q62QXoL6_GWIy1I2xjdYfgb3tHKLVZ10GiyMcve6Go8tPbeY2emaSBfVWZGxcJrMuy6qCuSFowfjbpzQJkfKxqXMZN1SNyUuXVkZnpWIqUBDAqBY_NCKRDKt_iwxIDsklXTl8ANsGIc_8FZXVJUKOX5pccs4KARu5_gLxqq14fKvAXV7bwsbSM-Xe03obWyMA"
Key                                       Value
---                                       -----
token                                     s.8eeuo1Cu5xCsIWKQ5WcNnTPn
token_accessor                            2czXQjEdRz2EAio2VJyrLAyD
token_duration                            16m40s
token_renewable                           true
token_policies                            ["default" "k8s.-.demo.demo-policy"]
identity_policies                         []
policies                                  ["default" "k8s.-.demo.demo-policy"]
token_meta_service_account_name           demo-sa
token_meta_service_account_namespace      demo
token_meta_service_account_secret_name    demo-sa-token-ddk7c
token_meta_service_account_uid            46448c8a-08f6-11e9-b1f2-080027b7189d
token_meta_role                           demo-role

$ export VAULT_TOKEN='s.8eeuo1Cu5xCsIWKQ5WcNnTPn'
$ vault kv put secret/foo A=B
Success! Data written to: secret/foo

$ vault kv get secret/foo
== Data ==
Key    Value
---    -----
A      B

$ vault auth list
Error listing enabled authentications: Error making API request.

URL: GET https://127.0.0.1:8200/v1/sys/auth
Code: 403. Errors:

* 1 error occurred:
	* permission denied

If we delete VaultPolicyBinding, then respective role will be deleted from Vault.

$ kubectl delete vaultpolicybindings/demo-role -n demo
vaultpolicybinding.policy.kubevault.com "demo-role" deleted

Check whether role is deleted from Vault.

$ vault read auth/kubernetes/role/demo-role
No value found at auth/kubernetes/role/demo-role

$ vault list auth/kubernetes/role
Keys
----
k8s.-.demo.vault-auth-method-controller
vault-policy-controller