Policy rulesets in cloud-native environments
Just Enough
Infrastructure as Code (IaC) has become a successful recipe for declarative, machine-readable code, so it only makes sense to apply this system to security and, in particular, to authoring policies in an attempt to implement rules within an organization in a scalable way. One representative of this genre that has recently received greater attention is the Open Policy Agent (OPA) project [1], which is backed by startup Styra. OPA is a general-purpose policy engine that enables consistent, context-aware policy enforcement across the stack.
OPA at a Glance
OPA is hosted by Cloud Native Computing Foundation (CNCF), the organization behind Kubernetes. Designed for cloud-native environments, OPA combines the relatively easy-to-learn and -read Rego policy language with a policy model and application programming interface (API), which allows for a kind of universal framework that applies rules to any kind of stacks. One of the great advantages of OPA is the ability to decouple security policies from code and its use – regardless of how often the code changes.
From a technical point of view, OPA is tied to the input. Once data is available, the OPA code decides how to handle it (e.g., allowing or blocking with an allow or deny policy). Another advantage is that OPA processes take input and create output in both JSON and YAML formats, meaning that IT managers do not have to stick to a predefined API. All told, writing rules is relatively easy, and OPA supports read, evaluate, print, and loop (REPL, i.e., shell-based code execution). Of practical value is that you do not have to write all the policies yourself, because you can easily find ready-made policy bundles online for many use cases, and they are likely to contain a useful, predefined set of rules. A freely accessible Playground [2] and a free Styra Academy [3] can help you learn.
As expected, OPA use cases all relate to security:
- The expressiveness of OPA and Rego make it easy to define rules for user authorization and keep them in a central location.
- OPA can help with authorization if you use API gateways such as Kong, Traefik, or Tyk.
- OPA has various areas of application for continuous integration and continuous delivery or deployment (CI/CD), such as checking IaC code against a ruleset. Typical examples include preventing public IP addresses on virtual machines.
- OPA plays a major role in the Kubernetes environment. Admission controllers can send requests to OPA to receive a decision on which resources can be deployed in a Kubernetes cluster.
One hurdle you might encounter is having to relearn Rego. OPA is based on the Go programming language, and the only client SDK is for Go at the moment.
Installing OPA
OPA is a single, quickly installed binary file. It supports Linux, macOS, and Windows. On Linux, you install the binary, change the file attributes to make it executable, and add the location of the OPA binaries to your PATH variable:
curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64 chmod 755 opa export PATH=<path to the OPA binary>
If you work with Visual Studio Code, you should install a plugin by selecting the Extension menu in the left pane and searching for Open Policy Agent . When found, click Install (Figure 1). Once you have installed the extension, you will be able to create a new file in Rego with rules such as:
package hello default allow_hello = false default allow_world = false allow_hello { "hello" != "" } allow_world { "world" != "world" }
To evaluate the rule, run View | Command Palette | OPA Evaluate Package
. A second tab then opens to display the results. The results on the right side are easy to understand: To begin, the allow_hello
and allow_world
variables are set to false
, then their values are checked in a function.
Working with Variables, Objects, and Functions
To run simple expressions, you can start an interactive shell from the terminal with the opa run
command and then run commands. For example, begin by defining some variables,
greeting := "Hello" max_height := 42 pi := 3.14159 allowed := true location := null
then output the values (in an array, if so desired),
[greeting, max_height, pi, allowed, location] [ "Hello", 42, 3.14159, true, null ]
and define objects:
ips_by_port := { 80: ["1.1.1.1", "1.1.1.2"], 443: ["2.2.2.1"], } ips_by_port[80] [ "1.1.1.1", "1.1.1.2" ]
This example first defines an object that comprises sub-elements, including an array of strings for defining IP addresses. The second block accesses those elements:
Rules are an important element in Rego. In the next example, the default value is false
. If an input has a role
value equal to admin
, the value changes to true
. Alternatively, you can change the default if the role is user
and the has_permissions
field is true
:
default allow = false allow = true { input.role == "admin" } allow = true { input.role == "user" input.has_permission == true }
Functions can also be defined, as in this code fragment, which implements and calls a multiply function:
package function multiply(a,b) = m { m := a*b } result1 = r { r := multiply(3,4) } result2 = r { r := multiply(3,9) }
The Rego programming language has native functions for:
- numbers
- bit manipulation
- aggregation
- sets (array, set, object)
- strings
- regular expressions
- HTTP
- JSON web tokens (JWT)
Listing 1 shows some function call examples.
Listing 1
Example Function Calls
# Rounding up # 11 round(10.9) # Generate an array of numbers # [9, 8, 7, 6, 5, 4, 3, 2, 1] numbers.range(9, 1) # The type is "array" # "array" type_name(["apple","banana","pineaplle"]) # The input is a set # true is_set({1,2,3}) # The object includes two elements # 2 count({"width":1024, "height":768}) # [1,2,3,4,5,4,5,6] array.concat([1,2,3,4,5],[4,5,6]) # Extract key3, because this is not part of the object; a "val3" value is returned obj := {"key1":"val1", "key2":"val2"} # "val3" object.get(obj, "key3", "val3") # Ayt what position does the word "world" occur? # 7 indexof("Hello, world", "world") # POST HTTP call with header and body http.send({"url":"http://httpbin.org/post", "method":"post", "timeout":"3s", "headers":{"token":"111"}, "body":{"key": "val"}})
Buy this article as PDF
(incl. VAT)