HashiCorp: Introduction to Sentinel

Share At:

Policy as Code: Automate Cloud Security Governance with Sentinel | Trility  Consulting

What is Sentinel?

Sentinel is a language and framework for policy built to be embedded in existing software to enable fine-grained, logic-based policy decisions. A policy describes under what circumstances certain behaviors are allowed. Sentinel is an enterprise feature of HashiCorp Consul, Nomad, Terraform, and Vault.

Sentinel provides a language for writing policy and a workflow for developing and testing policies independent of the software they’ll be written to. We also provide a framework for developers to build plugins that allow a Sentinel-enabled system to access external information to make policy decisions.

Most systems today have some degree of access control. You are able to define identities and what they have access to. These ACL systems solve an immediate and necessary problem of locking down a system in very broad strokes. Sentinel is a reusable system for more advanced software policy decisions. Sentinel enables:

  • Fine-Grained Policy: Most ACL systems only enable coarse-grained behaviors: “read”, “write”, etc. Sentinel enables fine-grained behavior such as disallowing a certain API call when specific parameters are present.
  • Logic-Based Policy: You can write policy using full conditional logic. For example, you may only allow a certain application behavior on Monday to Thursday unless there is a manager override.
  • Accessing External Information: Sentinel can source external information to be used in policy decisions. For example, a policy that restricts the size of a payload may read data from Consul to determine the payload size limit.
  • Enforcement levels: Sentinel allows policies to be defined along with an “enforcement level” that dictates the pass/fail behavior of a policy. Advisory policies warn if they fail, soft mandatory policies can have their failures overridden, and hard mandatory policies must pass under all circumstances. Having this as a built-in concept enables you to model policy more accurately and completely for your organization.

Why Sentinel?

The growth of infrastructure and applications has been enabled in part by an increasing trend towards automation everywhere. Configuration as code (such as Chef or Puppet with Packer) has enabled automated machine configuration. Infrastructure as code (such as Terraform) has enabled automatic infrastructure creation. And schedulers (such as Nomad and Kubernetes) have enabled automatic application deployment. Sentinel enables guardrails to be put in place on automation while allowing the codification and automatic enforcement of business requirements in critical areas of your infrastructure.

Meanwhile, businesses have business requirements and sometimes legal requirements which must be expressed in policies. Traditionally, these policies are enforced by humans. But in a highly automated world, the automation is only as fast as its slowest component. In many cases, this is the human verification step.

As an example: before infrastructure as code and autoscaling, if an order came through for 5,000 new machines, a human would likely respond to the ticket verifying that the user really intended to order 5,000 new machines. Today, automation can almost always freely order 5,000 new compute instances without any hesitation, which can result in unintended expense or system instability.

Sentinel introduces policy as code and a powerful framework built-in to HashiCorp tooling to allow automation guardrails, business requirements, legal compliance, and more to be actively enforced by the running systems in realtime.

With Sentinel, you can require an override to create certain numbers of infrastructure resources. You can disallow unsafe deployment configurations with Nomad. You can enforce certain key/value formats in Consul. You can restrict secret access by time in Vault. And more.

Sentinel is available today in HashiCorp enterprise products. 

Getting Started With Sentinel

This section covers getting started with Sentinel. Here, we will take you through the first steps that you will need to do to get started with installing the Sentinel CLI, writing your first policy, and getting familiar with rules, boolean logic, imports, and testing.

Install Sentinel CLI

Sentinel is a policy framework that is embedded in the enterprise versions of HashiCorp tools. The policies you write are deployed to these applications and enforced there.

The Sentinel CLI is a command-line interface for local development and testing. For the getting started guide, we’ll use the CLI to learn how to write policies for Sentinel-enabled applications. The Sentinel CLI is distributed as a binary package for all supported platforms and architectures.

Installation Instructions

To install the Sentinel CLI, find the appropriate package for your system and download it. The CLI is packaged as a zip archive.

After downloading Sentinel, unzip the package. The CLI runs as a single binary named sentinel. Any other files in the package can be safely removed and Sentinel will still function.

The final step is to make sure that the sentinel binary is available on the PATH. See this page for instructions on setting the PATH on Linux and Mac. This page contains instructions for setting the PATH on Windows.

Verifying the Installation

After installing the Sentinel CLI, verify the installation worked by opening a new terminal session and checking that the sentinel binary is available. By executing sentinel, you should see help output similar to the following:

$ sentinel
Usage: sentinel [--version] [--help] <command> [<args>]

Available commands are:    
   apply      Execute a policy and output the result    
   fmt        Format Sentinel policy to a canonical format    
   test       Test policies    version    Prints the Sentinel version

If you get an error that the binary could not be found, then your PATH environment variable was not setup properly. Please go back and ensure that your PATH variable contains the directory where Sentinel was installed.

Otherwise, the CLI is installed and ready to go. Let’s celebrate by writing our first policy!

Your First Sentinel Policy

Sentinel is a system to enforce complex policies on an integrated application.

Writing Sentinel policy requires minimal programming experience. The Sentinel language is designed to be approachable and learned quickly and easily. Whether you’re a professional programmer or someone who uses SQL and Excel, you can learn to write Sentinel policies.

Let’s begin by writing a simple, working Sentinel policy:

hour = 4
main = rule { hour >= 0 and hour < 12 }

This is a valid Sentinel policy. It will pass since we hardcoded the hour to be 4. In a real system, hour may be something that is provided to us and actually set to the current hour. We’ll learn more about that later.

For now, try running this policy locally. Save the above example to a file named policy.sentinel and execute it. Then, modify the policy to make it fail. Play around more if you’d like.

$ sentinel apply policy.sentinel
Pass

Main

Every Sentinel policy must have a main rule. This is the rule that is evaluated to determine the result of a policy.

A rule describes an expression that generally means one of two things:

  • Does a policy pass a condition that would authorize an operation? In our above example, describe a policy that checks the supplied hour (4) is within an authorized time window (between 0 – midnight, and 12 noon).
  • Conversely, can a policy find any violations that would block authorization of the operation? Building on the above, consider a policy that takes a schedule, and finds all time blocks that fall outside of the example time window supplied in the above policy.

It is easy to imagine that such a rule might be used in a system such as Nomad to restrict the times when a deploy can occur. The power of arbitrary logical statements within Sentinel allows Sentinel policies to restrict almost any behavior.

Next, we’ll introduce and explain rules so we can use them in our policies.

Rules

JUMP TO SECTION

Rules in Sentinel are a first-class concept. Within a policy, rules serve a few purposes:

  • They make complex logic more understandable by allowing said logic to be broken down.
  • They allow assertion of this logic through testing of the rule’s contents.
  • They facilitate reporting of data as rules get published as part of the policy trace.

A rule functions in ways similar to both a variable and a function: they hold a value, but are lazily evaluated; a rule’s value is not assigned until the first time it’s referenced in a policy. Additionally, while the value of evaluated rules will be available within a policy’s trace after it’s evaluated, values of variables – and the return value of functions – are not. Finally, a rule value is memoized – further references to the rule will not change its result.

Rules can hold more than just boolean data. For more advanced rule patterns, see the language reference.

Writing Rules

Let’s look at the Sentinel policy we wrote in the previous section:

hour = 4
main = rule { hour >= 0 and hour < 12 }

The logic in this policy is straightforward and easy to understand. However, it is common for policy logic to become much more complicated. Rules are the key abstraction for making complex logic understandable. We can turn the policy above into a set of rules:

hour = 4

past_midnight = rule { hour >= 0 }
before_noon   = rule { hour < 12 }

main = rule {    
  past_midnight and    
  before_noon
}

This policy is easier to read. Our original policy was already quite simple, but it is easy to imagine a more complex policy becoming much more readable by extracting logical expressions into a set of rules.

Rules don’t just exist to make a policy more readable. They’re also a testing and debugging point. For testing, you can assert that certain rules are certain values given a specific input to verify that your policy works as you expect. Testing and debugging are covered in later sections.

Conditional Rules

In many cases, a rule is only valid when something else is also true.

Consider the human language statement “before you eat, you must wash your hands.” The rule “you must wash your hands” is only valid “before you eat”. In any other scenario, the rule is meaningless. This is also true in a technical context. Imagine the rule “if the driver is Docker, you must not expose any ports.” For this example, assume rules is_docker and has_no_exposed_ports exist. You can write this rule like this:

main = rule when is_docker { has_no_exposed_ports }

When is_docker is true, the rule is evaluated as normal. However, when is_docker is false, the rule always returns true, because the logic of the rule is meaningless.

Let’s learn how to create more complex rules with logical expressions.

Logic

The core of a policy is a set of logical expressions to determine whether a behavior is allowed or not. Sentinel makes it easy to write readable logical expressions to express the intended policy.

The logical expression syntax will be familiar to anyone who has programmed before. Sentinel also supports a couple more unique constructs to assist with common policy requirements. Detailed documentation on how to write logical expressions and the supported operators is available in the boolean expression reference.

Example logic:

a is b        // Equality, you can also use ==
a is not b    // Inequality, you can also use !=

a > b         // Relational operators, comparison
a < b
a >= b
a <= b

a contains b     // Inclusion check, substrings, etc.
a not contains b // Negated version of above

The full list of available operators can be found in the boolean expression reference.

Logic can be combined with and or or. When combined with and, both sides must result in true. When combined with or, only one side needs to be true to result in true.

With these building blocks, basic rules can be built up:

main = rule {    
  hour >= 8 and    
  hour < 17
}

Next, we’ll introduce imports so that we can write rules that interact with external data.

Imports

Imports enable Sentinel to access external data and functions.

Sentinel ships with a set of standard imports. Standard imports are available by default to every Sentinel policy. Note that the application embedding Sentinel may allow or deny the available set of imports.

To use an import, use the import statement. Then, access data and functions on the import using the import name followed by a period and the field you want to access. The example below checks whether the request path starts with a prefix. Assume that request_path is available.

import "strings"
main = rule { strings.has_prefix(request_path, "/admin") }

External Data

The true power in imports is their ability to reference external data. Imports can be added to a Sentinel-enabled application through plugins. This enables any Sentinel-enabled application to make policy decision based on any source of data.

This ability allows the policy system to enforce almost any necessary organizational policy, since the ability of a policy isn’t restricted purely to the embedding application’s data model.

For example, policies in Nomad can access data in Consul to determine attributes of a policy. In the example below, we use a hypothetical Consul import:

import "consul"

main = rule {    
  job.tasks[0].resources.memory <= int(consul.get("policy/nomad/max-memory"))
}

In this example, a Nomad job’s memory usage is limited to the value of a Consul key/value item. By simply changing a Consul KV entry, policy can be changed. Imports can do anything, you can write your own import plugins to extend Sentinel.

Next, we’ll learn how to test policies to ensure our policy logic is correct.

Testing

It is important to ensure the policies you write are correct. Sentinel includes a built-in test framework that can be run locally and in CI environments to test that policies behave as expected.

A common pitfall with many simple ACL systems is that they provide no easy way to verify their correctness. You basically have to set the ACL and try the behaviors against a real system to verify it is working as expected. This requires a lot of setup and is unique to each system.

Sentinel’s built-in test framework has zero dependencies. Contained within the Sentinel CLI, it can mock the data that real systems are exposing to the policy. It is designed to be CI-friendly and enables continuous testing of your policies. This is necessary for policy as code.

Detailed documentation on testing policies is available in the Sentinel Testing reference.

Writing Tests

For this example, save the following policy as policy.sentinel:

is_weekday = rule { day not in ["saturday", "sunday"] }
is_open_hours = rule { hour > 8 and hour < 17 }
main = rule { is_open_hours and is_weekday }

Next, let’s write a passing test case. This will test that the policy tests when we expect it to pass. Save the following in test/policy/good.hcl:

global "day" {  
  value = "monday"
}

global "hour" {  
  value = 14
}

And run sentinel test:

$ sentinel test
PASS - policy.sentinel  
 PASS - test/policy/good.json

The sentinel test command will automatically find all Sentinel policies and their associated test cases and run them all. The test framework will run all HCL files as individual test cases, allowing you to test a variety of scenarios for your policies.

Try adding another test case to force the policy to fail. This test case can be saved at test/policy/fail.hcl. For test cases with intentional failures, you’ll need to use the test assertions described in the testing reference.

Now that you have a good grasp of what Sentinel is and how to use it, please feel free to look through the next steps you can take to further your Sentinel knowledge.

Next Steps

That concludes the getting started guide for Sentinel. Hopefully you’re excited about the possibilities of Sentinel and ready to put this knowledge to use to improve the safety of your environments.

We’ve covered the basics of the core features of Sentinel in this guide. We recommend the following as next steps:

  • Documentation – The documentation is an in-depth reference guide of all the features of Sentinel.
  • Language Reference – The language reference is a complete reference to the features of the Sentine policy language.

Reference: https://docs.hashicorp.com/

Happy Learning !!!


Share At:
0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
Back To Top

Contact Us