---
layout: docs
page_title: Sentinel examples
description: >-
    Examples for managing Vault policies with Sentinel.
---

> [!IMPORTANT]  
> **Documentation Update:** Product documentation, which were located in this repository under `/website`, are now located in [`hashicorp/web-unified-docs`](https://github.com/hashicorp/web-unified-docs), colocated with all other product documentation. Contributions to this content should be done in the `web-unified-docs` repo, and not this one. Changes made to `/website` content in this repo will not be reflected on the developer.hashicorp.com website.

# Examples

@include 'alerts/enterprise-and-hcp.mdx'

Following are some examples that help to introduce concepts. If you are
unfamiliar with writing Sentinel policies in Vault, please read through to
understand some best practices.

Additional examples can be found [here](https://github.com/hashicorp/vault-guides/tree/master/governance).

## MFA and CIDR check on login

The following Sentinel policy requires the incoming user to successfully
validate with an Okta MFA push request before authenticating with LDAP.
Additionally, it ensures that only users on the 10.20.0.0/16 subnet are able to
authenticate using LDAP.

```python
import "sockaddr"
import "mfa"
import "strings"

# We expect logins to come only from our private IP range
cidrcheck = rule {
    sockaddr.is_contained("10.20.0.0/16", request.connection.remote_addr)
}

# Require ping MFA validation to succeed
ping_valid = rule {
    mfa.methods.ping.valid
}

main = rule when strings.has_prefix(request.path, "auth/ldap/login") {
    ping_valid and cidrcheck
}
```

Note the `rule when` construct on the `main` rule. This scopes the policy to
the given condition.

Vault takes a default-deny approach to security. Without such scoping, because
active Sentinel policies must all pass successfully, the user would be forced
to start with a passing status and then define the conditions under which
access is denied, breaking the default-deny concept.

By instead indicating the conditions under which the `main` rule (and thus, in
this example, the entire policy) should be evaluated, the policy instead
describes the conditions under which a matching request is successful. This
keeps the default-deny feeling of Vault; if the evaluation condition isn't met,
the policy is simply a no-op.

## Allow only specific identity entities or groups

```python
main = rule {
    identity.entity.name is "jeff" or
    identity.entity.id is "fe2a5bfd-c483-9263-b0d4-f9d345efdf9f" or
    "sysops" in identity.groups.names or
    "14c0940a-5c07-4b97-81ec-0d423accb8e0" in keys(identity.groups.by_id)
}
```

This example shows accessing Identity properties to make decisions, showing
that for Identity values IDs or names can be used for reference.

In general, it is more secure to use IDs. While convenient, entity names and
group names can be switched from one entity to another, because their only
constraint is that they must be unique. Using IDs guarantees that only that
specific entity or group is sufficient; if the group or entity are deleted and
recreated with the same name, the match will fail.

## Instantly disallow all Previously-Generated tokens

Imagine a break-glass scenario where it is discovered that there have been
compromises of some unknown number of previously-generated tokens.

In such a situation it would be possible to revoke all previous tokens, but
this may take a while for a number of reasons, from requiring revocation of
generated secrets to the simple delay required to remove many entries from
storage. In addition, it could revoke tokens and generated secrets that later
forensic analysis shows were not compromised, unnecessarily widening the impact
of the mass revocation.

In Vault's ACL system a simple deny could be put into place, but this is a very
coarse-grained control and would require forethought to ensure that a policy
that can be modified in such a way is attached to every token. It also would
not prevent access to login paths or other unauthenticated paths.

Sentinel offers much more fine-grained control:

```python
import "time"

main = rule when not request.unauthenticated {
    time.load(token.creation_time).unix >
      time.load("2017-09-17T13:25:29Z").unix
}
```

Created as an EGP on `*`, this will block all access to any path Sentinel
operates on with a token created before the given time. Tokens created after
this time, since they were not a part of the compromise, will not be subject to
this restriction.

## Delegate EGP policy management under a path

The following policy gives token holders with this policy (via their tokens or
their Identity entities/groups) the ability to write EGP policies that can only
take effect at Vault paths below certain prefixes. This effectively delegates
policy management to the team for their own key-value spaces.

```python
import "strings"

data_match = func() {
    # Make sure there is request data
    if length(request.data else 0) is 0 {
        return false
    }

    # Make sure request data includes paths
    if length(request.data.paths else 0) is 0 {
        return false
    }

    # For each path, verify that it is in the allowed list
    for strings.split(request.data.paths, ",") as path {
        # Make it easier for users who might be used to starting paths with
        # slashes
        sanitizedPath = strings.trim_prefix(path, "/")
        if not strings.has_prefix(sanitizedPath, "dev-kv/teama/") and
           not strings.has_prefix(sanitizedPath, "prod-kv/teama/") {
            return false
        }
    }

    return true
}

# Only care about writing; reading can be allowed by normal ACLs
precond = rule {
    request.operation in ["create", "update"] and
    strings.has_prefix(request.path, "sys/policies/egp/")
}

main = rule when precond {
    strings.has_prefix(request.path, "sys/policies/egp/teama-") and data_match()
}
```
