Skip to content

Commit

Permalink
Adding a simulator + regopolicyinterpreter. (#1558)
Browse files Browse the repository at this point in the history
* Adding a simulator + regopolicyinterpreter.

This PR separates all the interaction with Rego into its own extractable package
called `regopolicyinterpreter`. Instead of calling Rego directly,
the `securitypolicy` package now uses this package to implement Rego policies.
Separating out the Rego interpreter behavior in this way allows the same
code to be used by a new `policyenginesimulator` tool, which provides the
ability to simulate security policy execution on the command line.

`regopolicyinterpreter` exposes various Rego things like modules and metadata
in a typed way to make them easier to work with:
    - `RegoPolicyInterpreter` is the main interface
    - `RegoModule` is a standalone Rego module that can be included in the
       policy execution. There are `AddModule` and `RemoveModule` methods for
       modifying the interpreter to include various modules.
    - `RegoQueryResult` wraps the results that come from the Rego policy with
       some useful methods for extracting scalar data types
       (i.e. `bool`/`int`/`float`/`string`)
    - `EnableLogging` provides a way to get multiple levels of policy logging
      for debugging purposes, ranging from `Info`, which will output prints that
      come from the Rego policy itself, to `Metadata`, which will dump the
      entire policy metadata structure to the log with each interaction. This is
      primarily intended for offline use (e.g. by the simulator).

The `policyenginesimulator` tool uses `RegoPolicyInterpreter` to simulate
policy enforcement. Usage:

```
  -commands string
        commands JSON
  -data string
        initial data state
  -log string
        log path
  -logLevel string
        None|Info|Results|Metadata (default "Info")
  -policy string
        policy Rego
```

The commands JSON allows the user to specify the type and order of the commands
send by the host to the guest that will interact with the simulated policy, for
example:

``` json
[
    {
        "name": "load_fragment",
        "input": {
            "issuer": "did:web:contoso.github.io",
            "feed": "contoso.azurecr.io/custom",
            "namespace": "custom",
            "local_path": "custom.rego"
        }
    },
    {
        "name": "mount_device",
        "input": {
            "target": "/mnt/layer0",
            "deviceHash": "16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415"
        }
    },
    {
        "name": "mount_overlay",
        "input": {
            "target": "/mnt/overlay0",
            "containerID": "container0",
            "layerPaths": [
                "/mnt/layer0"
            ]
        }
    },
    {
        "name": "create_container",
        "input": {
            "containerID": "container0",
            "argList": [
                "/pause"
            ],
            "envList": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "TERM=xterm"
            ],
            "mounts": [],
            "workingDir": "/",
            "sandboxDir": "/sandbox",
            "hugePagesDir": "/hugepages"
        }
    }
]
```

Signed-off-by: Matthew A Johnson <matjoh@microsoft.com>
  • Loading branch information
matajoh committed Jan 10, 2023
1 parent cbdbb48 commit 939de61
Show file tree
Hide file tree
Showing 18 changed files with 2,840 additions and 551 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@ jobs:

- name: Test rego security policy
run: go test --tags=rego -timeout=30m -mod=mod -gcflags=all=-d=checkptr -v ./pkg/securitypolicy

- name: Test rego policy interpreter
run: go test -mod=mod -gcflags=all=-d=checkptr -v ./internal/regopolicyinterpreter

test-windows:
needs: [lint, protos, verify-vendor, go-gen]
Expand Down Expand Up @@ -195,6 +198,9 @@ jobs:
- run: go build -mod=mod -o sample-logging-driver.exe ./cri-containerd/helpers/log.go
working-directory: test

- name: Test rego policy interpreter
run: go test -mod=mod -gcflags=all=-d=checkptr -v ./internal/regopolicyinterpreter

- uses: actions/upload-artifact@v3
if: ${{ github.event_name == 'pull_request' }}
with:
Expand Down
138 changes: 138 additions & 0 deletions internal/regopolicyinterpreter/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Rego Policy Interpreter

This module provides a general purpose Rego Policy interpreter. This is
used both by the [security_policy](../securitypolicy/) package, as well
as the [policy engine simulator](../../internal/tools/policyenginesimulator/).

## Metadata

Each rule in a policy can optionally return a series of metadata commands in addition to
`allowed` which will then be made available in the `data.metadata` namespace
for use by the policy in future rule evaluations. A metadata command has the
following format:

``` json
{
{
"name": "<metadata key>",
"action": "<add|update|remove>",
"key": "<key>",
"value": "<optional value>"
}
}
```

Metadata values can be any Rego object, *i.e.* arbitrary JSON. Importantly,
the Go code does not need to understand what they are or what they contain, just
place them in the specified point in the hierarchy such that the policy can find
them in later rule evaluations. To give a sense of how this works, here are a
sequence of rule results and the resulting metadata state:

**Initial State**
``` json
{
"metadata": {}
}
```

**Result 1**
``` json
{
"allowed": true,
"metadata": [{
"name": "devices",
"action": "add",
"key": "/dev/layer0",
"value": "5c5d1ae1aff5e1f36d5300de46592efe4ccb7889e60a4b82bbaf003c2248f2a7"
}]
}
```

**State 1**
``` json
{
"metadata": {
"devices": {
"/dev/layer0": "5c5d1ae1aff5e1f36d5300de46592efe4ccb7889e60a4b82bbaf003c2248f2a7"
}
}
}
```

**Result 2**
``` json
{
"allowed": true,
"metadata": [{
"name": "matches",
"action": "add",
"key": "container1",
"value": [{<container>}, {<container>}, {<container>}]
}]
}
```

**State 2**
``` json
{
"metadata": {
"devices": {
"/dev/layer0": "5c5d1ae1aff5e1f36d5300de46592efe4ccb7889e60a4b82bbaf003c2248f2a7"
},
"matches": {
"container1": [{<container>}, {<container>}, {<container>}]
}
}
}
```

**Result 3**
``` json
{
"allowed": true,
"metadata": [{
"name": "matches",
"action": "update",
"key": "container1",
"value": [{<container>}]
}]
}
```

**State 3**
``` json
{
"metadata": {
"devices": {
"/dev/layer0": "5c5d1ae1aff5e1f36d5300de46592efe4ccb7889e60a4b82bbaf003c2248f2a7"
},
"matches": {
"container1": [{<container>}]
}
}
}
```

**Result 4**
``` json
{
"allowed": true,
"metadata": [{
"name": "devices",
"action": "remove",
"key": "/dev/layer0"
}]
}
```

**State 4**
``` json
{
"metadata": {
"devices": {},
"matches": {
"container1": [{<container>}]
}
}
}
```
9 changes: 9 additions & 0 deletions internal/regopolicyinterpreter/module.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package module

subtract := {"result": result} {
result := input.a - input.b
}

subtract := {"result": result} {
result := concat("-", [input.a, input.b])
}

0 comments on commit 939de61

Please sign in to comment.