1

I want to mask the password in my Open Policy Agent (OPA) logs.

This is my input data:

{
"decision_id":"71e99093-b980-4c67-bd0c-87fcc071571a",
"input":{
  "attributes":{
     .....
     ,
     "request":{
        "http":{
           "body":"{\r\n    \"username\": \"MyUsername\",\r\n    \"password\": \"MySecetPassword\"\r\n}",
           "headers":{
           ....
           },
           "method":"POST",
           "path":"/login",
           "protocol":"HTTP/1.1",
           "scheme":"http",
           "size":"63"
        },
        "time":"2022-09-06T05:51:13.802770Z"
     },
     ...
  },
  "parsed_body":{
     "password":"MySecetPassword",
     "username":"MyUsername"
  },
  "parsed_path":[
     "login"
  ],
  "parsed_query":{
     
  },
  "truncated_body":false,
  "version":{
     "encoding":"protojson",
     "ext_authz":"v3"
  }
},
"labels":{
  "id":"3987b552-f128-47f2-9b96-34289d677d76",
  "version":"0.35.0-envoy"
},
"level":"info",
"metrics":{
  "timer_rego_query_eval_ns":49451,
  "timer_server_handler_ns":395446
},
"msg":"Decision Log",
"path":"istio/authz/allow",
"requested_by":"",
"result":true,
"time":"2022-09-06T05:51:13Z",
"timestamp":"2022-09-06T05:51:13.804132822Z",
"type":"openpolicyagent.org/decision_logs"
}

My OPA mask rule:

mask[{"op": "upsert", "path": "/input/attributes/request/http/body", "value": x}] {
    # conditionally upsert password if it existed in the orginal event
    contains(input.attributes.request.http.body, "password")
    x := "***CENSORED***"
}

mask[{"op": "upsert", "path": "/input/parsed_body/password", "value": x}] {
    # conditionally upsert password if it existed in the orginal event
    input.parsed_body.password
    x := "***CENSORED***"
}

But when OPA checks the request, the sensitive data won't be masked with the new text. Any idea?!

I checked the rule in the The Rego Playground (https://play.openpolicyagent.org) which was fine...

flo-ferox
  • 158
  • 1
  • 8

2 Answers2

2

From the OPA docs on the topic, you'll see that the original input attribute may be referenced under input.input (i.e. not just input, as that's the input attribute for the masking policy itself).

package system.log

mask["/input/password"] {
  # OPA provides the entire decision log event as input to the masking policy.
  # Refer to the original input document under input.input.
  input.input.resource == "user"
}

Changing input.attributes.request.http.body to input.input.attributes.request.http.body should do it.

Devoops
  • 2,018
  • 8
  • 21
  • I changed the mask as you suggested, but I can still see the passwords. mask[{"op": "upsert", "path": "/input/attributes/request/http/body", "value": x}] { # conditionally upsert password if it existed in the orginal event contains(input.input.attributes.request.http.body, "password") x := "***CENSORED***" } – flo-ferox Sep 06 '22 at 11:56
  • mask[{"op": "upsert", "path": "/input/parsed_body/password", "value": x}] { # conditionally upsert password if it existed in the orginal event input.input.parsed_body.password x := "***CENSORED***" } and the playground also marks the coverages as red. (Coverage explains which statements were evaluated. Red expressions were never evaluated. Red rule heads were never defined. Green expressions were evaluated at least once. Green rule heads were defined at least once.) – flo-ferox Sep 06 '22 at 11:56
  • For the Rego Playground, it's up to you to format the input data so that it would look like what the decision logging mask policies sees - i.e. double wrapping of "input" would be required. – Devoops Sep 06 '22 at 12:08
2

To complete the solution...

The solution from Devoops is correct.

My fault was, that I tried to combine the masking part with the policy. But they have to be in two different files (in to different packages!).

flo-ferox
  • 158
  • 1
  • 8