2017-11-14 11:13:11 +00:00
|
|
|
---
|
|
|
|
layout: "docs"
|
|
|
|
page_title: "Sentinel Examples"
|
New Docs Website (#5535)
* conversion stage 1
* correct image paths
* add sidebar title to frontmatter
* docs/concepts and docs/internals
* configuration docs and multi-level nav corrections
* commands docs, index file corrections, small item nav correction
* secrets converted
* auth
* add enterprise and agent docs
* add extra dividers
* secret section, wip
* correct sidebar nav title in front matter for apu section, start working on api items
* auth and backend, a couple directory structure fixes
* remove old docs
* intro side nav converted
* reset sidebar styles, add hashi-global-styles
* basic styling for nav sidebar
* folder collapse functionality
* patch up border length on last list item
* wip restructure for content component
* taking middleman hacking to the extreme, but its working
* small css fix
* add new mega nav
* fix a small mistake from the rebase
* fix a content resolution issue with middleman
* title a couple missing docs pages
* update deps, remove temporary markup
* community page
* footer to layout, community page css adjustments
* wip downloads page
* deps updated, downloads page ready
* fix community page
* homepage progress
* add components, adjust spacing
* docs and api landing pages
* a bunch of fixes, add docs and api landing pages
* update deps, add deploy scripts
* add readme note
* update deploy command
* overview page, index title
* Update doc fields
Note this still requires the link fields to be populated -- this is solely related to copy on the description fields
* Update api_basic_categories.yml
Updated API category descriptions. Like the document descriptions you'll still need to update the link headers to the proper target pages.
* Add bottom hero, adjust CSS, responsive friendly
* Add mega nav title
* homepage adjustments, asset boosts
* small fixes
* docs page styling fixes
* meganav title
* some category link corrections
* Update API categories page
updated to reflect the second level headings for api categories
* Update docs_detailed_categories.yml
Updated to represent the existing docs structure
* Update docs_detailed_categories.yml
* docs page data fix, extra operator page remove
* api data fix
* fix makefile
* update deps, add product subnav to docs and api landing pages
* Rearrange non-hands-on guides to _docs_
Since there is no place for these on learn.hashicorp, we'll put them
under _docs_.
* WIP Redirects for guides to docs
* content and component updates
* font weight hotfix, redirects
* fix guides and intro sidenavs
* fix some redirects
* small style tweaks
* Redirects to learn and internally to docs
* Remove redirect to `/vault`
* Remove `.html` from destination on redirects
* fix incorrect index redirect
* final touchups
* address feedback from michell for makefile and product downloads
2018-10-19 15:40:11 +00:00
|
|
|
sidebar_title: "Examples"
|
2017-11-14 11:13:11 +00:00
|
|
|
sidebar_current: "docs-vault-enterprise-sentinel-examples"
|
|
|
|
description: |-
|
|
|
|
An overview of how Sentinel interacts with Vault Enterprise.
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
# Examples
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
## 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"
|
2017-11-15 02:42:43 +00:00
|
|
|
import "mfa"
|
2018-07-05 20:11:21 +00:00
|
|
|
import "strings"
|
2017-11-14 11:13:11 +00:00
|
|
|
|
|
|
|
# We expect logins to come only from our private IP range
|
|
|
|
cidrcheck = rule {
|
|
|
|
sockaddr.is_contained(request.connection.remote_addr, "10.20.0.0/16")
|
|
|
|
}
|
|
|
|
|
|
|
|
# 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()
|
|
|
|
}
|
|
|
|
```
|