When decision logging is enabled the OPA server will include a field in API calls that return policy decisions.

See the for configuration details.

OPA expects the service to expose an API endpoint that will receive decision logs.

The partition name is an optional path segment that can be used to route logs to different backends. If the partition name is not configured on the agent, updates will be sent to /logs.

The message body contains a gzip compressed JSON array. Each array element (event) represents a policy decision returned by OPA.

  1. [
  2. {
  3. "labels": {
  4. "app": "my-example-app",
  5. "id": "1780d507-aea2-45cc-ae50-fa153c8e4a5a",
  6. "version": "v0.30.2"
  7. },
  8. "decision_id": "4ca636c1-55e4-417a-b1d8-4aceb67960d1",
  9. "bundles": {
  10. "authz": {
  11. "revision": "W3sibCI6InN5cy9jYXRhbG9nIiwicyI6NDA3MX1d"
  12. }
  13. },
  14. "path": "http/example/authz/allow",
  15. "input": {
  16. "method": "GET",
  17. "path": "/salary/bob"
  18. },
  19. "result": "true",
  20. "requested_by": "[::1]:59943",
  21. "timestamp": "2018-01-01T00:00:00.000000Z"
  22. }
  23. ]

Decision log updates contain the following fields:

  1. decision_logs:
  2. console: true

This will dump all decisions to the console. See Configuration Reference for more details.

Policy queries may contain sensitive information in the input document that must be removed or modified before decision logs are uploaded to the remote API (e.g., usernames, passwords, etc.) Similarly, parts of the policy decision itself may be considered sensitive.

By default, OPA queries the data.system.log.mask path prior to encoding and uploading decision logs or calling custom decision log plugins.

OPA provides the decision log event as input to the policy query and expects the query to return a set of JSON Pointers that refer to fields in the decision log event to either erase or modify.

For example, assume OPA is queried with the following input document:

To remove the password field from decision log events related to “user” resources, supply the following policy to OPA:

  1. mask["/input/password"] {
  2. # OPA provides the entire decision log event as input to the masking policy.
  3. # Refer to the original input document under input.input.
  4. input.input.resource == "user"
  5. }
  6. # To mask certain fields unconditionally, omit the rule body.
  7. mask["/input/ssn"]
  1. {
  2. "decision_id": "b4638167-7fcb-4bc7-9e80-31f5f87cb738",
  3. "erased": [
  4. "/input/password",
  5. "/input/ssn"
  6. ],
  7. "input": {
  8. "name": "bob",
  9. "resource": "user"
  10. },
  11. ------------------------- 8< -------------------------
  12. "path": "system/main",
  13. "result": true,
  14. "timestamp": "2019-06-03T20:07:16.939402185Z"
  15. }

There are a few restrictions on the JSON Pointers that OPA will erase:

  • Pointers must be prefixed with /input or /result.
  • Pointers may be undefined. For example /input/name/first in the example above would be undefined. Undefined pointers are ignored.

In order to modify the contents of an input field, the mask rule may utilize the following format.

  • "op" – The operation to apply when masking. All operations are done at the path specified. Valid options include:
opDescription
“remove”The “path” specified will be removed from the resulting log message. The “value” mask field is ignored for “remove” operations.
“upsert”The “value” will be set at the specified “path”. If the field exists it is overwritten, if it does not exist it will be added to the resulting log message.
  • "path" – A JSON pointer path to the field to perform the operation on.

Optional Fields:

  • "value" – Only required for "upsert" operations.

To always upsert a value, even if it didn’t exist in the original event, the following rule format can be used.

  1. package system.log
  2. # always upsert, no conditions in rule body
  3. mask[{"op": "upsert", "path": "/input/password", "value": x}] {
  4. x := "**REDACTED**"
  5. }

The result of this mask operation on the decision log event produces the following output. Notice that the mask event field exists to track remove vs upsert mask operations.

  1. {
  2. "decision_id": "b4638167-7fcb-4bc7-9e80-31f5f87cb738",
  3. "erased": [
  4. "/input/ssn"
  5. ],
  6. "masked": [
  7. "/input/password"
  8. ],
  9. "input": {
  10. "name": "bob",
  11. "resource": "user",
  12. "password": "**REDACTED**"
  13. },
  14. ------------------------- 8< -------------------------
  15. "path": "system/main",
  16. "requested_by": "127.0.0.1:36412",
  17. "result": true,