Sentinel Policies

Sentinel is a language framework for policy build to be embedded in Vault Enterprise to enable fine-grained, logic-based policy decisions which cannot be fully handled by the ACL policies.

Role Governing Policies (RGPs) and Endpoint Governing Policies (EGPs) can be defined using Sentinel:

  • RGPs are tied to particular tokens, identity entities, or identity groups
  • EGPs are tied to particular paths (e.g. aws/creds/)

This guide walks you through the authoring of Sentinel policies in Vault. For ACL policy authoring, refer to the Policies guide.


ACL policies are path-based that it has the following challenges:

  • Cannot grant permissions based on logics other than paths
  • Paths are merged in ACL policies which could potentially cause a conflict as the number of policies grows

What if the policy requirement was to grant read permission on secret/orders path only if the request came from an IP address within a certain CIDR?


Use Sentinel policies (RGPs and/or EGPs) to fulfill more complex policy requirements.

Sentinel can access properties of the incoming requests and make a decision based on a certain set of conditions. Available properties include:

  • request - Information about the request itself (path, operation type, parameters, etc.)
  • token - Information about the token being used (creation time, attached policies, etc.)
  • identity - Identity entities and all related data
  • mfa - Information about successful MFA validations


To perform the tasks described in this guide, you need to have a Vault Enterprise environment.

Policy requirements

Since this guide demonstrates the creation of policies, log in with highly privileged token such as root. Required permissions are:

# To list policies
path "sys/policies/*"
  capabilities = ["list"]

# Create and manage EGPs
path "sys/policies/egp/*"
  capabilities = ["create", "read", "update", "delete", "list"]

Step 1: Write Sentinel Policies

Anatomy of Sentinel Policies

import "<library>"

<variable> = <value>

main = rule {
  • import - Enables your policy to access reusable libraries. There are a set of built-in imports available to help define your policy rules.

  • main (required) - Every Sentinel policy must have a main rule which is evaluated to determine the result of a policy.

  • rule - A first-class construct in Sentinel. It describes a set of conditions resulting in either true or false. (NOTE: Refer to the Boolean Expressions for the full list of available operators in writing rules.)

  • <variable> - Variables are dynamically typed in Sentinel. You can define its value explicitly or implicitly by the host system or function.

Policy requirements

In this guide, you are going to write Sentinel policies that fulfill the following requirements:

  1. Any incoming request against the "secret/accounting/*" to be performed during the business hours (7:00 am to 6:00 pm during the work days).

  2. Any create, update and delete operations against Key/Value secret engine (mounted at "secret") must come from an internal IP of CIDR.

Sentinel Policies

Requirement #1: business-hrs.sentinel

import "time"

# Expect requests to only happen during work days (Monday through Friday)
# 0 for Sunday and 6 for Saturday
workdays = rule { > 0 and < 6

# Expect requests to only happen during work hours (7:00 am - 6:00 pm)
workhours = rule { > 7 and < 18

main = rule {
    workdays and workhours

Requirement #2: cidr-check.sentinel

import "sockaddr"
import "strings"

# Only care about create, update, and delete operations against secret path
precond = rule {
    request.operation in ["create", "update", "delete"] and
    strings.has_prefix(request.path, "secret/")

# Requests to come only from our private IP range
cidrcheck = rule {
    sockaddr.is_contained(request.connection.remote_addr, "")

# Check the precondition before execute the cidrcheck
main = rule when precond {

The main has conditional rule (when precond) to ensure that the rule gets evaluated only if the request is relevant.

Step 2: Test the Sentinel Policies

You can test the Sentinel policies prior to deployment in orders to validate syntax and to document expected behavior.

First, you need to download the Sentinel simulator.

$ wget

Unzip the downloaded file.

$ unzip -d /usr/local/bin

Create a sub-folder named test where cidr-check.sentinel and business-hrs.sentinel policies are located.

Under the test folder, create a sub-folder for cidr-check.

$ mkdir -p test/cidr-check

Also, create a sub-folder for business-hrs under the test directory.

$ mkdir -p test/business-hrs

Write a passing test case in a file named success.json under test/business-hrs directory.

  "mock": {
    "time": {
      "now": {
        "weekday": 1,
        "hour": 12
  "test": {
    "main": true

Under mock, you specify the mock test data. In this example, the weekday is set to 1 which is Monday and hour is set to 12 which is noon. Therefore the main should return true.

Write a failing test in a file named fail.json under test/business-hrs.

  "mock": {
    "time": {
      "now": {
        "weekday": 0,
        "hour": 12
  "test": {
    "main": false

The mock data is set to Sunday at noon; therefore Therefore, the main should return false.

Similarly, write a passing test case for cidr-check policy, test/cidr-check/success.json.

  "global": {
    "request": {
      "connection": {
        "remote_addr": ""
      "operation": "create",
      "path": "secret/orders"

In this example, the global specifies the create operation is invoked on secret/orders endpoint which initiated from an IP address Therefore the main should return true.

Write a failing test for cidr-check policy, test/cidr-check/fail.json.

  "global": {
    "request": {
      "connection": {
        "remote_addr": ""
      "operation": "create",
      "path": "secret/orders"
  "test": {
    "precond": true,
    "main": false

This test will fail because of the IP address mismatch. However, the precond should pass since the requested operation is create and the targeted endpoint is secret/orders.

The optional test definition adds more context to why the test should fail. The expected behavior is that the test fails because main returns false but precond should return true.

Now, you have written both success and failure tests.

├── business-hrs.sentinel
├── cidr-check.sentinel
└── test
   ├── business-hrs
   │   ├── fail.json
   │   └── success.json
   └── cidr-check
       ├── fail.json
       └── success.json

Execute the test.

$ sentinel test

PASS - business-hrs.sentinel
 PASS - test/business-hrs/success.json  PASS - test/business-hrs/fail.json
PASS - cidr-check.sentinel
 PASS - test/cidr-check/success.json  PASS - test/cidr-check/fail.json

Step 3: Deploy your EGP policies

Sentinel policies have three enforcement levels:

advisoryThe policy is allowed to fail. Can be used as a tool to educate new users.
soft-mandatoryThe policy must pass unless an override is specified.
hard-mandatoryThe policy must pass no matter what!

Since both policies are tied to specific paths, the policy type that you are going to create is Endpoint Governing Policies (EGPs).

Store the Base64 encoded cidr-check.sentinel policy in an environment variable named POLICY.

$ POLICY=$(base64 cidr-check.sentinel)

Create a policy cidr-check with enforcement level of hard-mandatory to reject all requests coming from IP addressed that are not internal.

$ vault write sys/policies/egp/cidr-check \
        policy="${POLICY}" \
        paths="secret/*" \

You can read the policy by executing the following command:

$ vault read sys/policies/egp/cidr-check

Repeat the steps to create a policy named business-hrs. First, encode the business-hrs policy.

$ POLICY2=$(base64 business-hrs.sentinel)

Create a policy with soft-mandatory enforcement-level.

$ vault write sys/policies/egp/business-hrs \
        policy="${POLICY2}" \
        paths="secret/accounting/*" \

To read the policy you just created, execute the following command.

$ vault read sys/policies/egp/business-hrs


Once the policies were deployed, create, update and delete operations coming from an IP address other than will be denied.

$ vault kv put secret/accounting/test acct_no="293472309423"

Error writing data to secret/accounting/test: Error making API request.

Code: 400. Errors:

* 1 error occurred:

* egp standard policy "cidr-check" evaluation resulted in denial.

The specific error was:

A trace of the execution for policy "cidr-check" is available:

Result: false

Description: Check the precondition before execute the cidrcheck

Rule "main" (byte offset 442) = false
  false (offset 314): sockaddr.is_contained

Rule "cidrcheck" (byte offset 291) = false

Rule "precond" (byte offset 113) = true
  true (offset 134): request.operation in ["create", "update", "delete"]
  true (offset 194): strings.has_prefix

Similarly, you will get an error if any request is made outside of the business hours defined by the business-hrs policy.

Step 4: Delete Sentinel Policies

To delete the business-hrs EGP, execute the following command.

$ vault delete sys/policies/egp/business-hrs

To delete the cidr-check EGP, execute the following command.

$ vault delete sys/policies/egp/cidr-check

Help and Reference