Virtual Event
Join us for the next HashiConf Digital October 12-15, 2020 Register for Free



Control Groups

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.


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


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


Use Control Groups in your policies to implement dual controller authorization required.


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
path "sys/policies/acl/*"
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]

# To enable secrets 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" ]

»Scenario Introduction

The scenario in this guide is that a user, Bob Smith has read-only permission on the "EU_GDPR_data/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:

  1. Implement a control group
  2. Deploy the policies
  3. Setup entities and a group
  4. Verification
  5. ACL Policies vs. Sentinel Policies

»Step 1: Implement a control group

Author a policy named read-gdpr-order.hcl.

Bob needs read permissions on EU_GDPR_data/data/orders/*.

path "EU_GDPR_data/data/orders/*" {
  capabilities = [ "read" ]

Now, add control group to this policy:

path "EU_GDPR_data/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"]

»Step 2: Deploy the policies

Deploy the read-gdpr-order and acct_manager policies that you wrote.

Create a new policy named read-gdpr-order.

$ vault policy write read-gdpr-order read-gdpr-order.hcl

Create a new policy named acct_manager.

$ vault policy write acct_manager acct_manager.hcl

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

The following command uses jq tool to parse JSON output.

  1. Enable the userpass auth method.

    $ vault auth enable userpass
  2. Create a new user, bob with password, "training".

    $ vault write auth/userpass/users/bob password="training"
  3. Create a new user, ellen with password, "training".

    $ vault write auth/userpass/users/ellen password="training"
  4. 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
  5. 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 "" > entity_id_bob.txt
  6. 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)
  7. 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 "" > entity_id_ellen.txt
  8. 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)
  9. 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)

»Step 4: Verification

Now, let's see how the control group works.

  1. First, enable the key/value secrets engine at EU_GDPR_data.

    $ vault secrets enable -path=EU_GDPR_data -version=2 kv
  2. Write some mock data.

    $ vault kv put EU_GDPR_data/orders/acct1 \
            order_number="12345678" product_id="987654321"
  3. Log in as bob.

    $ vault login -method=userpass username="bob" password="training"
  4. Request to read "EU_GDPR_data/orders/acct1".

    $ vault kv get EU_GDPR_data/orders/acct1
    Key                              Value
    ---                              -----
    wrapping_token:                  s.h9jGEBexp0o7vQhmImZKAAKo
    wrapping_accessor:               HDUlEfkrbrfPOlfUnwgar025
    wrapping_token_ttl:              24h
    wrapping_token_creation_time:    2019-05-10 16:38:36 -0700 PDT
    wrapping_token_creation_path:    EU_GDPR_data/data/orders/acct1

    The response includes wrapping_token and wrapping_accessor. Copy this wrapping_accessor value.

  5. 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"
  6. 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>


    Check the current status.

    $ vault write sys/control-group/request accessor=HDUlEfkrbrfPOlfUnwgar025
    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=HDUlEfkrbrfPOlfUnwgar025
    Key         Value
    ---         -----
    approved    true

    Now, the approved status is true.

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


    Log back in as bob using 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 s.h9jGEBexp0o7vQhmImZKAAKo
    Key                 Value
    ---                 -----
    refresh_interval    768h
    order_number        12345678
    product_id          987654321

»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 {

Deploy this policy as an Endpoint Governing Policy attached to "EU_GDPR_data/data/orders/*" path.

»Help and Reference