Introduction
How to create a policy such that the user assigned the policy is an administrator of a particular namespace as well as child namespaces. The policy, as well as the commands required to set this up can be confusing as working with namespaces adds complexity.
Procedure
Before getting into the complexities of the policies and commands, please review some documentation to familiarize yourself with some topics.
Root Namespace Example
The first approach to take is creating a policy in the root
namespace that will grant the user administrator access to a particular namespace named foo
in our case. Additionally, this policy allows the user to administrate namespaces that are children to the foo
namespace, up to 1 level deep. This policy must be assigned to a token created in the root namespace.
$ vault policy write foo-admin - <<EOF
# Manage namespaces
path "foo/sys/namespaces/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
path "foo/+/sys/namespaces/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
# Manage policies
path "foo/sys/policies/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
path "foo/+/sys/policies/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
# List policies
path "foo/sys/policies/acl" {
capabilities = ["list"]
}
path "foo/+/sys/policies/acl" {
capabilities = ["list"]
}
# Enable and manage secrets engines
path "foo/sys/mounts/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "foo/+/sys/mounts/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# List available secrets engines
path "foo/sys/mounts" {
capabilities = ["read"]
}
path "foo/+/sys/mounts" {
capabilities = ["read"]
}
EOF
In the above policy, there are 2 versions of each policy path, one with a +
and one without a +
. The path without a +
allows the policy user permissions directly in the foo
namespace. The path with the +
allows the policy user permissions in any namespace that is a child of foo
. This is because the +
can used to denote any number of characters bounded within a single path segment (in our case, that path segment is a namespace).
This pattern can be repeated any number of times. For example, if wanting to update the policy to administrate 2 levels of nested namespaces rather than just 1, update the policy to include another path with the syntax +/+
which would denote that the user has permissions to all namespaces 2 levels deep. Example below:
path "dev/foo/sys/mounts" {
capabilities = ["read"]
}
path "dev/foo/+/sys/mounts" {
capabilities = ["read"]
}
path "dev/foo/+/+/sys/mounts" {
capabilities = ["read"]
}
After creating this policy, you would then be able to create a token in the "root" namespace and assign it the foo-admin
policy. The resultant token is then an administrator of the foo
namespace. The example below demonstrates creating the token with the appropriate policy, creating a child bar
namespace and listing the capabilities to prove that we can administrate the child namespace.
$ FOO_ADMIN_TOKEN=$(vault token create -format=json -policy=foo-admin | jq -r '.auth.client_token')
$ vault login $FOO_ADMIN_TOKEN
$ vault namespace create -namespace=foo bar
Key Value
--- -----
id glTCa
path foo/bar/
$ vault token capabilities foo/bar/sys/namespaces/
create, delete, list, read, sudo, update
Foo Namespace Example
The second approach is setting up a policy in the foo
namespace, rather than in the root
namespace. Some of the benefits of this approach are that the user with the foo-admin
policy can actually alter the foo-admin
policy themselves, something that's not possible with a policy created in the root
namespace. Additionally, it can be a "cleaner" approach as the policies related to the namespace are held within the namespace, rather than cluttering the root
namespace.
Many of the commands we will be using leverage the -namespace
option, which lets you choose which namespace the command is executed against.
vault policy write -namespace=foo foo-admin - << EOF
# Manage namespaces
path "sys/namespaces/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
path "+/sys/namespaces/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
# Manage policies
path "sys/policies/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
path "+/sys/policies/*" {
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
# List policies
path "sys/policies/acl" {
capabilities = ["list"]
}
path "+/sys/policies/acl" {
capabilities = ["list"]
}
# Enable and manage secrets engines
path "sys/mounts/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "+/sys/mounts/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# List available secrets engines
path "sys/mounts" {
capabilities = [ "read" ]
}
path "+/sys/mounts" {
capabilities = [ "read" ]
}
EOF
Note that this policy is created in foo
namespace via the -namespace=foo
option. This means that it will only be returned when running a vault policy
list -namespace=foo
. It won't be returned when listing policies in the root
namespace via vault policy
list
. Also note how the policy paths change. Since this policy exists in the foo
namespace, there's no need to include foo
in the policy path. The policy path is thus relative to the namespace which it was created in.
To repeat the previous example for this particular use case, a few small changes need to be made in how to create the token. There's a need to specify the -namespace
option when creating the token as well as specifying it when logging in. An example is below:
$ FOO_ADMIN_TOKEN=$(vault token create -format=json -policy=foo-admin -namespace=foo | jq -r '.auth.client_token') $ vault login -namespace=foo $FOO_ADMIN_TOKEN
$vault namespace create -namespace=foo bar Key Value --- ----- id asjOj path foo/bar/
$ vault token capabilities -namespace=foo bar/sys/namespaces/ create, delete, list, read, sudo, update