Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a simulator + regopolicyinterpreter. #1558

Merged
merged 9 commits into from
Jan 10, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 ./pkg/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 ./pkg/regopolicyinterpreter

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

This tool provides a means to test out security policies. Usage:

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

## Getting started

From the tool directory run:

go run . -policy [samples/simple_framework/policy.rego](samples/simple_framework/policy.rego) -commands [samples/simple_framework/commands.json](samples/simple_framework/commands.json)

This will load the authored policy and then simulate the enforcement behavior
for the provided commands.

This policy uses the framework, however, the simulator also handles completely
custom policies:

go run . -policy [samples/simple_custom/policy.rego](samples/simple_framework/policy.rego) -commands [samples/simple_custom/commands.json](samples/simple_custom/commands.json)

## Commands

Consists of a sequential list of commands that will be issued for enforcement to
the policy. These commands take the form of JSON, *e.g.*:

``` json
matajoh marked this conversation as resolved.
Show resolved Hide resolved
[
{
"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"
}
}
]
```

Each command has a name (corresponding to the API enforcement point) and then
matajoh marked this conversation as resolved.
Show resolved Hide resolved
an input which will be passed directly to the policy. The API being tested
is defined in [`api.rego`](../../../pkg/securitypolicy/api.rego).

## Data
matajoh marked this conversation as resolved.
Show resolved Hide resolved

If the authored policy requires certain values in the Rego data structure to
function, or if the author wants to test enforcement behavior given a known
starting state, they can provide an optional initial data file as an argument
to the tool. This should take the form of a single JSON object, *e.g.*:

``` json
{
"defaultMounts": [/*some mount constraint objects*/],
"privilegedMounts": [/*some mount constraint objects*/],
"sandboxPrefix": "sandbox://",
"hugePagesPrefix": "hugepages://"
}
```

## Log

To aid in debugging, the user can specify a log file. This will enable logging
at the `Info` level, which consists of the output of any Rego `print()` calls
from the policy.

## Log Level

There are several log levels that the user can use to gain greater insight into
policy enforcement:

| Name | Description |
| ---------- | --------------------------------------------------------- |
| `None` | Used when no log file is provided via the `-log` argument |
| `Info` | Outputs the results of Rego `print()` statements. |
| `Results` | Outputs the results returned by each policy query |
| `Metadata` | Outputs the entire metadata state after each query |

## Policy

This can be any Rego with a package name of `policy`, though policies which do
not define the required enforcement point rules will result in enforcement
failures. We include some straightforward samples below, but see
[simple_custom](simple_custom) and [simple_framework](simple_framework)
for more detail.

### Framework-based policy

``` rego
package policy

api_svn := "0.7.0"

import future.keywords.every
import future.keywords.in

containers := [
{
"command": ["/pause"],
"env_rules": [
{
"pattern": "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"strategy": "string",
"required": false
},
{
"pattern": "TERM=xterm",
"strategy": "string",
"required": false
}
],
"layers": ["16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415"],
"mounts": [],
"exec_processes": [],
"signals": [],
"allow_elevated": false,
"working_dir": "/"
}
]

mount_device := data.framework.mount_device
unmount_device := data.framework.unmount_device
mount_overlay := data.framework.mount_overlay
unmount_overlay := data.framework.unmount_overlay
create_container := data.framework.create_container
exec_in_container := data.framework.exec_in_container
exec_external := data.framework.exec_external
shutdown_container := data.framework.shutdown_container
signal_container_process := data.framework.signal_container_process
plan9_mount := data.framework.plan9_mount
plan9_unmount := data.framework.plan9_unmount
load_fragment := data.framework.load_fragment
reason := {"errors": data.framework.errors}
```

### Custom Policy

``` rego
package policy

api_svn := "0.7.0"

overlays := {
"pause": {
"deviceHashes": ["16b514057a06ad665f92c02863aca074fd5976c755d26bff16365299169e8415"],
"mounts": []
}
}

custom_containers := [
{
"id": "pause",
"command": ["/pause"],
"overlayID": "pause",
"depends": []
}
]

mount_device := data.custom.mount_device
mount_overlay := data.custom.mount_overlay
create_container := data.custom.create_container
unmount_device := {"allowed": true}
unmount_overlay := {"allowed": true}
exec_in_container := {"allowed": true}
exec_external := {"allowed": true}
shutdown_container := {"allowed": true}
signal_container_process := {"allowed": true}
plan9_mount := {"allowed": true}
plan9_unmount := {"allowed": true}

default load_fragment := {"allowed": false}
load_fragment := {"allowed": true, "add_module": true} {
input.issuer == "did:web:contoso.github.io"
input.feed == "contoso.azurecr.io/custom"
}
```
Loading