Enterprise Only: Control Groups is a part of Vault Enterprise Premium.
Control Groups add additional authorization factors to be required before processing requests to increase the governance, accountability, and security of your secrets. When a control group is required for a request, the requesting client receives the wrapping token in return. Only when all authorizations are satisfied, the wrapping token can be used to unwrap the requested secrets.
» Personas
The end-to-end scenario described in this guide involves three personas:
admin
with privileged permissions to create policies and identities- processor with permission to approve secret access
- controller with limited permission to access secrets
» Challenge
In order to operate in EU, a company must abide by the General Data Protection Regulation (GDPR) as of May 2018. The regulation enforces two or more controllers jointly determine the purposes and means of processing (Chapter 4: Controller and Processor).
Consider the following scenarios:
Anytime an authorized user requests to read data at "
EU_GDPR_data/orders/*
", at least two people from the Security group must approve to ensure that the user has a valid business reason for requesting the data.Anytime a database configuration is updated, it requires that one person from the DBA and one person from Security group must approve it.
» Solution
Use Control Groups in your policies to implement dual controller authorization required.
» Prerequisites
To perform the tasks described in this guide, you need to have a Vault Enterprise environment.
This guide assumes that you have some hands-on experience with ACL policies as well as Identities. If you are not familiar, go through the following guides first:
» Policy requirements
Since this guide demonstrates the creation of policies, log in with a highly
privileged token such as root
.
Otherwise, required permissions to perform
the steps in this guide are:
# Create and manage ACL policies via CLI
path "sys/policy/*"
{
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
# Create and manage ACL policies via Web UI
path "sys/policies/acl/*"
{
capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
# To enable secret engines
path "sys/mounts/*" {
capabilities = [ "create", "read", "update", "delete" ]
}
# Setting up test data
path "EU_GDPR_data/*"
{
capabilities = ["create", "read", "update", "delete", "list"]
}
# Manage userpass auth method
path "auth/userpass/*"
{
capabilities = ["create", "read", "update", "delete", "list"]
}
# List, create, update, and delete auth methods
path "sys/auth/*"
{
capabilities = ["create", "read", "update", "delete"]
}
# Create and manage entities and groups
path "identity/*" {
capabilities = [ "create", "read", "update", "delete", "list" ]
}
» Steps
The scenario in this guide is that a user, Bob Smith
has
read-only permission on the "EU_GDPR_data/orders/*
" path; however,
someone in the acct_manager
group must approve it before he can actually
read the data.
As a member of the acct_manager
group, Ellen Wright
can authorize
Bob's request.
You are going to perform the following:
- Implement a control group
- Deploy the policies
- Setup entities and a group
- Verification
- ACL Policies vs. Sentinel Policies
Step 1, 2 and 3 are the tasks need to be performed by administrators or operators who have the privileges to create policies and configure entities and groups.
» Step 1: Implement a control group
Author a policy named read-gdpr-order.hcl
.
Bob needs read
permissions on EU_GDPR_data/orders/*
:
path "EU_GDPR_data/orders/*" {
capabilities = [ "read" ]
}
Now, add control group to this policy:
path "EU_GDPR_data/orders/*" {
capabilities = [ "read" ]
control_group = {
factor "authorizer" {
identity {
group_names = [ "acct_manager" ]
approvals = 1
}
}
}
}
For the purpose of this guide, the number of approvals
is set to
1
to keep it simple and easy to test. Any member of the identity
group, acct_manager
can approve the read request. Although this
example has only one factor (authorizer
), you can add as many factor
blocks as you need.
Now, write another policy for the acct_manager
group named
acct_manager.hcl
.
# To approve the request
path "sys/control-group/authorize" {
capabilities = ["create", "update"]
}
# To check control group request status
path "sys/control-group/request" {
capabilities = ["create", "update"]
}
NOTE: The important thing here is that the authorizer (acct_manager
)
must have create
and update
permission on the
sys/control-group/authorize
endpoint so that they can approve the request.
Enable the key/value secrets engine at EU_GDPR_data
and write some mock data:
# Enable kv-v1 at EU_GDPR_data
$ vault secrets enable -path=EU_GDPR_data -version=1 kv
# Write some mock data
$ vault kv put EU_GDPR_data/orders/acct1 order_number="12345678" product_id="987654321"
» Step 2: Deploy the policies
Deploy the read-gdpr-order
and acct_manager
policies that you wrote.
» CLI command
# Create read-gdpr-order policy
$ vault policy write read-gdpr-order read-gdpr-order.hcl
# Create acct_manager policy
$ vault policy write acct_manager acct_manager.hcl
» API call using cURL
# Construct API request payload to create read-gdpr-read policy
$ tee payload-1.json <<EOF
{
"policy": "path \"EU_GDPR_data/orders/*\" {capabilities = [ \"read\" ]control_group = {factor \"authorizer\" ..."
}
EOF
# Create read-gdpr-order policy
$ curl --header "X-Vault-Token: ..." \
--request PUT \
--data @payload-1.json \
http://127.0.0.1:8200/v1/sys/policies/acl/read-gdpr-order
# Construct API request payload to create acct_manager policy
$ tee payload-2.json <<EOF
{
"policy": "path \"sys/control-group/authorize\" {capabilities = [\"create\", \"update\"]} ..."
}
EOF
# Create acct_manager policy
$ curl --header "X-Vault-Token: ..." \
--request PUT \
--data @payload-2.json \
http://127.0.0.1:8200/v1/sys/policies/acl/acct_manager
» Web UI
Open a web browser and launch the Vault UI (e.g. http://127.0.0.1:8200/ui) and then login.
Click the Policies tab, and then select Create ACL policy.
Toggle Upload file, and click Choose a file to select your
read-gdpr-order.hcl
file you authored at Step 1.This loads the policy and sets the Name to be
read-gdpr-order
.Click Create Policy to complete.
Repeat the steps to create a policy for
acct_manager
.
» Step 3: Setup entities and a group
This step only demonstrates CLI commands and Web UI to create entities and groups. Refer to the Identity - Entities and Groups guide if you need the full details.
Now you have policies, let's create a user, bob
and an acct_manager
group with ellen
as a group member.
NOTE: For the purpose of this guide, use the userpass
auth method to create
user bob
and ellen
so that the scenario can be easily tested.
» CLI command
The following command uses jq
tool
to parse JSON output.
# Enable userpass
$ vault auth enable userpass
# Create a user, bob
$ vault write auth/userpass/users/bob password="training"
# Create a user, ellen
$ vault write auth/userpass/users/ellen password="training"
# Retrieve the userpass mount accessor and save it in a file named accessor.txt
$ vault auth list -format=json | jq -r '.["userpass/"].accessor' > accessor.txt
# Create Bob Smith entity and save the identity ID in the entity_id_bob.txt
$ vault write -format=json identity/entity name="Bob Smith" policies="read-gdpr-order" \
metadata=team="Processor" \
| jq -r ".data.id" > entity_id_bob.txt
# Add an entity alias for the Bob Smith entity
$ vault write identity/entity-alias name="bob" \
canonical_id=$(cat entity_id_bob.txt) \
mount_accessor=$(cat accessor.txt)
# Create Ellen Wright entity and save the identity ID in the entity_id_ellen.txt
$ vault write -format=json identity/entity name="Ellen Wright" policies="default" \
metadata=team="Acct Controller" \
| jq -r ".data.id" > entity_id_ellen.txt
# Add an entity alias for the Ellen Wright entity
$ vault write identity/entity-alias name="ellen" \
canonical_id=$(cat entity_id_ellen.txt) \
mount_accessor=$(cat accessor.txt)
# Finally, create acct_manager group and add Ellen Wright entity as a member
$ vault write identity/group name="acct_manager" \
policies="acct_manager" \
member_entity_ids=$(cat entity_id_ellen.txt)
» Web UI
Click the Access tab, and select Enable new method.
Select Username & Password from the Type drop-down menu.
Click Enable Method.
Click the Vault CLI shell icon (
>_
) to open a command shell. Entervault write auth/userpass/users/bob password="training"
to create a new userbob
.Enter
vault write auth/userpass/users/ellen password="training"
to create a new userellen
.Click the icon (
>_
) again to hide the shell.From the Access tab, select Entities and then Create entity.
Populate the Name, Policies and Metadata fields as shown below.
Click Create.
Select Add alias. Enter
bob
in the Name field and selectuserpass/ (userpass)
from the Auth Backend drop-down list.Click Create.
Return to the Entities tab and then Create entity.
Populate the Name, Policies and Metadata fields as shown below.
Click Create.
Select Add alias. Enter
ellen
in the Name field and selectuserpass/ (userpass)
from the Auth Backend drop-down list.Click Create.
Click Entities from the left navigation, select the
Ellen Wright
from the entities list and copy its ID.Click Groups from the left navigation, and select Create group.
Enter
acct_manager
in the Name, and again enteracct_manager
in the Policies fields.Enter the
Ellen Wright
entity ID in the Member Entity IDs field, and then click Create.
» Step 4: Verification
Now, let's see how the control group works.
» CLI Command
Log in as bob
.
$ vault login -method=userpass username="bob" password="training"
Request to read "EU_GDPR_data/orders/acct1
":
$ vault kv get EU_GDPR_data/orders/acct1
Key Value
--- -----
wrapping_token: 1f1411bc-2f18-551a-5e58-0fe44432e9a5
wrapping_accessor: bbb4deef-e06d-9b2a-64a9-56f815c69ee7
wrapping_token_ttl: 24h
wrapping_token_creation_time: 2018-08-08 09:36:32 -0700 PDT
wrapping_token_creation_path: EU_GDPR_data/orders/acct1
The response includes wrapping_token
and wrapping_accessor
.
Copy this wrapping_accessor
value.
Now, a member of acct_manager
must approve this request. Log in as
ellen
who is a member of acct_manager
group.
$ vault login -method=userpass username="ellen" password="training"
As a user, ellen
, you can check and authorize bob's request using the
following commands.
# To check the current status
$ vault write sys/control-group/request accessor=<wrapping_accessor>
# To approve the request
$ vault write sys/control-group/authorize accessor=<wrapping_accessor>
Example:
# Check the current status
$ vault write sys/control-group/request accessor=bbb4deef-e06d-9b2a-64a9-56f815c69ee7
Key Value
--- -----
approved false
authorizations <nil>
request_entity map[name:Bob Smith id:38700386-723d-3d65-43b7-4fb44d7e6c30]
request_path EU_GDPR_data/orders/acct1
# Approve the request
$ vault write sys/control-group/authorize accessor=bbb4deef-e06d-9b2a-64a9-56f815c69ee7
Key Value
--- -----
approved true
Now, the approved
status is true
.
Since the control group requires one approval from a member of acct_manager
group, the condition has been met. Log back in as bob
and unwrap the secret.
Example:
# Log back in as bob - you can use the bob's token: vault login <bob_token>
$ vault login -method=userpass username="bob" password="training"
# Unwrap the secrets by passing the wrapping_token
$ vault unwrap 1f1411bc-2f18-551a-5e58-0fe44432e9a5
Key Value
--- -----
refresh_interval 768h
order_number 12345678
product_id 987654321
» API call using cURL
Log in as bob
.
$ curl --request POST \
--data '{"password": "training"}' \
http://127.0.0.1:8200/v1/auth/userpass/login/bob | jq
Copy the generated client_token
value.
Request to EU_GDPR_data/orders/acct1
:
$ curl --header "X-Vault-Token: <bob_client_token>" \
http://127.0.0.1:8200/v1/EU_GDPR_data/orders/acct1 | jq
{
...
"wrap_info": {
"token": "20a2f2b3-8bea-4e16-980b-82724dcdc38b",
"accessor": "9910cb38-600c-29d8-1c39-764a1c89a481",
"ttl": 86400,
"creation_time": "2018-08-08T10:13:06-07:00",
"creation_path": "EU_GDPR_data/orders/acct1"
},
...
}
The response includes wrap_info
instead of the actual data.
Copy the accessor
value.
Now, a member of acct_manager
must approve this request. Log in as
ellen
who is a member of acct_manager
group.
$ curl --request POST \
--data '{"password": "training"}' \
http://127.0.0.1:8200/v1/auth/userpass/login/ellen | jq
Copy the generated client_token
value.
As a user, ellen
, you can check the current status and then authorize bob's
request.
NOTE: Be sure to replace <accessor>
with the accessor
value you
copied earlier.
# To check the current status using sys/control-group/request endpoint
$ curl --header "X-Vault-Token: <ellen_client_token>" \
--request POST \
--data '{"accessor": "<accessor>"}' \
http://127.0.0.1:8200/v1/sys/control-group/request | jq
{
...
"data": {
"approved": false,
"authorizations": null,
"request_entity": {
"id": "38700386-723d-3d65-43b7-4fb44d7e6c30",
"name": "Bob Smith"
},
"request_path": "EU_GDPR_data/orders/acct1"
},
...
}
# Now, authorize the request using sys/control-group/authorize endpoint
$ curl --header "X-Vault-Token: <ellen_client_token>" \
--request POST \
--data '{"accessor": "<accessor>"}' \
http://127.0.0.1:8200/v1/sys/control-group/authorize | jq
{
...
"data": {
"approved": true
},
...
}
Now, the approved
status is true
.
The bob
user should be able to unwrap the secrets.
$ curl --header "X-Vault-Token: <bob_client_token>" \
--request POST \
--data '{"token": "<wrapping_token>"}' \
http://127.0.0.1:8200/v1/sys/wrapping/unwrap | jq
{
...
"data": {
"order_number": "12345678",
"product_id": "987654321"
},
...
}
» Web UI
The user, ellen
can approve the data access request via UI.
Open the Vault sign in page in a web browser (e.g. http://127.0.0.1:8200/ui/vault/auth?with=userpass). In the Userpass tab, enter
ellen
in the Username field, andtraining
in the Password field.Click Sign in.
Select the Access tab, and then Control Groups.
Enter the
wrapping_accessor
value in the Accessor field and click Lookup.Awaiting authorization message displays.
Click Authorize. The message changes to "Thanks! You have given authorization."
Bob needs to request data access via CLI or API. Once the access request is approved, use the CLI or API to unwrap the secrets.
» Step 5: ACL Policy vs. Sentinel Policy
Although the read-gdpr-order.hcl
was written as ACL policy, you
can implement Control Groups in either ACL or Sentinel policies.
Using Sentinel, the same policy may look something like:
import "controlgroup"
control_group = func() {
numAuthzs = 0
for controlgroup.authorizations as authz {
if "acct_manager" in authz.groups.by_name {
numAuthzs = numAuthzs + 1
}
}
if numAuthzs >= 1 {
return true
}
return false
}
main = rule {
control_group()
}
Deploy this policy as an Endpoint Governing Policy attached to
"EU_GDPR_data/orders/*
" path.
Refer to the Sentinel Properties documentation for the list of available properties associated with control groups.