witty-holiday-65473
02/04/2024, 2:48 PMremote
, and remote_json
(to start). The config option I've named: signed_payload
and would include an enhancement to the config schema along these lines:
"signed_payload": {
"title": "Sign payload and set the signature to a named header",
"default": null,
"properties": {
"header": {
"title": "Header to contain the payload signature (base64)",
"type": "string",
"description": "The name of the request header that will be populated with the signature of the payload that will be sent to the authorizer",
"examples": ["X-Request-Signature", "X-Jwks-Signature"]
},
"shared_key": {
"title": "Shared key to use to sign the payload. (Cannot be set along with 'jwks_url')",
"type": "string",
"description": "The shared key to use to sign the payload to generate an HMAC signature of the payload",
"examples": ["47ac7a07-b504-458d-a081-c80da1cfe6cd"]
},
"jwks_url": {
"type": "string",
"format": "uri",
"title": "JSON Web Key URL (Cannot be set along with 'shared_key')",
"description": "Sets the URL where keys should be fetched from. Supports remote locations (http, https) as well as local filesystem paths.\n.",
"examples": [
"<https://fetch-keys/from/this/location.json>",
"file:///from/this/absolute/location.json",
"file://../from/this/relative/location.json"
]
}
}
}
Initially, my thought is to support shared-key (HMAC256) and jwks (RS256, etc) signing. The default headers for each of these would be X-Request-Signature
and X-Jwks-Signature
respectively with jwks signing including an additional header matching the X-Jwks-Signature
(or user-specified header) with -Kid
appended which would represent the keyId used from the key set (jwks). Internally to support jwks signing I made enhancements to credentials/Signer.go
and credentials/SignerDefault.go
to support signing a simple string
payload (in addition to what it currently can handle: jwt.Claims
. The signature of the additional interface function looks like this:
SignPayload(ctx context.Context, location *url.URL, payload string) (string, string, error)
The strings that this function returns are: Signature
and KeyId
. These are then used in the authz/remote_json.go
and authz/remote.go
to populat the associated header fields. I took an additional step to calculate the signature of the raw request body in authz/remote.go
but since oathkeeper is intercepting (piping) an http request, the raw payload as it looked originally on the client (when the client would have generated a request signature) I feel there could be concerns upstream regarding whether the signature verification should calculate the payload signature on the raw payload or on the payload after any http transforms have been un applied e,g,: gzip compression. For this reason, I use the remote_json
authz handler but included the remote
handler anyway.
Why? My concern isn't so much a concern over who is sending the request, my concern is more about what is being sent. I need to ensure that the endpoint processing the authz request knows that the request has not been forged by a bad actor. Simply put, I want the endpoint to be able to reasonably conclude that the message came from oathkeeper. Note, I can probably accomplish this by using remote
and sending custom headers. But I feel like that would be a workaround for something that may be more useful if more broad configuration options were made available. I can see a use for signing payloads to other authz handlers as well. I can imagine that this could enhance security around any hydrator
upstreams that may be in a similar situation that my legacy authz handler is in.