Controller Runner Mode¶
Introduction¶
Postee can also be run in Controller/Runner mode. The idea is to decouple enforcement from execution, if applicable.
Scenario¶
In the following scenario, consider two services: A and B. In the case of Service A, a Trivy scan is run and results of the scan result are sent to Postee for executing Actions upon.
In the case of Service B, a Tracee container is constantly monitoring for malicious activity that happens on the host. When a Tracee finding is observed, it is sent to a local Postee Runner. This Postee Runner has the ability to locally execute a pre-defined Postee Action.
Configuration¶
Run Postee in Controller mode:¶
postee --cfgfile=./cfg-controller-runner.yaml --controller-mode --controller-ca-root="./rootCA.pem" --controller-tls-cert="./server-cert.pem" --controller-tls-key="./server-key.pem" --controller-seed-file="./seed.txt"
Option | Required | Description |
---|---|---|
controller-mode | true | Enable Postee to run as a Controller |
controller-ca-root | false | TLS CA Root Certificate for Controller |
controller-tls-cert | false | TLS Certificate for Controller |
controller-tls-key | false | TLS Key for Controller |
controller-seed-file | false | Seed file for Controller |
Example Controller/Runner Configuration
name: Postee Controller Runner Demo
routes:
- name: controller-only-route
input: contains(input.image, "alpine")
actions: [my-http-post-from-controller]
template: raw-json
- name: runner-only-route
input: contains(input.SigMetadata.ID, "TRC-1")
serialize-actions: true
actions: [my-exec-from-runner, my-http-post-from-runner]
template: raw-json
- name: controller-runner-route
input: contains(input.SigMetadata.ID, "TRC-2")
actions: [my-exec-from-runner, my-http-post-from-runner, my-http-post-from-controller]
template: raw-json
templates:
- name: raw-json
rego-package: postee.rawmessage.json
actions:
- name: stdout
type: stdout
enable: true
- name: my-http-post-from-controller
type: http
enable: true
url: "https://webhook.site/<uuid>"
method: POST
headers:
"Foo": [ "bar" ]
timeout: 10s
body-content: |
This is an example of a inline body
Input Image: event.input.image
- name: my-exec-from-runner
runs-on: "postee-runner-1"
type: exec
enable: true
env: ["MY_ENV_VAR=foo_bar_baz", "MY_KEY=secret"]
exec-script: |
#!/bin/sh
echo $POSTEE_EVENT
echo "this is hello from postee"
- name: my-http-post-from-runner
runs-on: "postee-runner-1"
type: http
enable: true
url: "https://webhook.site/<uuid>"
method: POST
body-content: |
This is an another example of a inline body
Event ID: event.input.SigMetadata.ID
The only notable change in the configuration as defined is of the Actions that can run on Runners. Observe the runs-on
clause below.
- name: my-exec-from-runner
runs-on: "postee-runner-1"
type: exec
enable: true
exec-script: |
#!/bin/sh
echo $POSTEE_EVENT
echo "this is hello from postee"
In this case this particular Action will run on Postee Runner that identifies itself as postee-runner-1
Run Postee in Runner mode:¶
postee --controller-url="nats://0.0.0.0:4222" --runner-ca-cert="./rootCA.pem" --runner-tls-cert="./runner-cert.pem" --runner-tls-key="./runner-key.pem" --runner-seed-file="./seed.txt", --runner-name="postee-runner-1" --url=0.0.0.0:9082 --tls=0.0.0.0:9445
Option | Required | Description |
---|---|---|
controller-url | true | The URL to the Postee Controller |
runner-name | true | The Name of the Runner, as defined in configuration YAML |
runner-ca-root | false | TLS Root CA Certificate for Runner |
runner-tls-cert | false | TLS Certificate for Runner |
runner-tls-key | false | TLS Key for Runner |
runner-seed-file | false | Seed file for Runner |
Secured Controller/Runner Channel¶
The communication channel between Controller and Runner can be optionally secured with TLS and be Authentication (AuthN).
TLS can be enabled by passing the TLS cert and key through the optional --controller-tls-cert
and --controller-tls-key
flags for Controller and --runner-tls-cert
and --runner-tls-key
flags for Runner.
AuthN can be enabled by passing the NATS Seed File. Postee uses NKeys, a public-key signature system based on Ed25519.
A seed file should be treated as a secret. It can be passed to the Controller via the --controller-seed-file
and the Runner via --runner-seed-file
.
This can be helpful in situations where Postee Config contains secrets that are configured in an Action that runs on a Runner.
Walkthrough¶
In the case of Tracee reporting a malicious finding, the Action might only make sense to run locally within the same environment where Tracee reported from. For instance, in the case of a Postee Action to kill a process reported within the malicious finding, the process will only exist on the host where Tracee reported from. Therefore, the need for a localized Postee that can handle this arises.
Postee Runners can automatically bootstrap themselves upon startup, given the address of the Postee Controller. They only receive the relevant config info from the Postee Controller for the Actions and Routes they are responsible for. This helps by limiting the spread of secrets in your configuration to only those Runners where they are needed. If your deployment uses Actions where secrets are required, we recommend you run these Actions at the Controller level.
The only Actions that a Postee Runner should run are Actions that are context/environment specific. A few examples (but not limited to) are: Killing a local process, Shipping local logs on host to a remote endpoint, etc.
Additional Info¶
Postee Runners and Controllers are no different from a normal instance of vanilla Postee. Therefore, no changes to the producers are required to use this functionality.
All events received by Postee Runners are reported upstream to the Controller. This has two benefits:
- Executions and Events received by the Runners can be monitored at a central level (Controller).
- Mixing of Runner and Controller Actions within a single Route, for ease of usage.
Mixing of Runner and Controller Actions can be explained with a following sample configuration:
- name: controller-only-route
input: contains(input.image, "alpine")
actions: [my-slack-message-from-controller]
template: raw-json
- name: runner-only-route
input: contains(input.SigMetadata.ID, "TRC-1")
serialize-actions: true
actions: [my-exec-from-runner, my-http-post-from-runner]
template: raw-json
- name: controller-runner-route
input: contains(input.SigMetadata.ID, "TRC-2")
serialize-actions: true
actions: [my-exec-from-runner, my-http-post-from-runner, my-jira-ticket-from-controller]
template: raw-json
In this sample configuration, we have three routes. One that solely executes on the Controller, another that solely executes on the Runner and a Mixed route.
In the case of the Mixed route, the first two Actions are run on the Runner. These Actions are run locally as they might require environment specific things to run, as discussed above. The third Action is run from a Controller because of security reasons to not distribute secrets to a Runner.
A quick note on Serialization¶
The option of serialize-actions
works as expected and guarantees true serialization for execution of Actions in the case of Controller only and Runner only routes. But for the case of Mixed routes (as described above) where executions can run on both Controller and Runner, this serialization cannot be strongly guaranteed due to the difference of execution environments (Runner and Controller).