pomerium/authorize/evaluator/opa/policy/headers.rego
Caleb Doxsey a64e5b5fa1
authorize: add sid to JWT claims (#2420)
* authorize: add sid to JWT claims

* fix import ordering
2021-08-02 16:11:05 -06:00

250 lines
5.4 KiB
Rego

package pomerium.headers
# input:
# enable_google_cloud_serverless_authentication: boolean
# from_audience: string
# kubernetes_service_account_token: string
# session:
# id: string
# to_audience: string
#
# data:
# issuer: string
# jwt_claim_headers: map[string]string
# signing_key:
# alg: string
# kid: string
#
# functions:
# get_databroker_record
# get_google_cloud_serverless_headers
#
#
# output:
# identity_headers: map[string][]string
# 5 minutes from now in seconds
five_minutes := round((time.now_ns() / 1e9) + (60 * 5))
# get the session
session = v {
# try a service account
v = get_databroker_record("type.googleapis.com/user.ServiceAccount", input.session.id)
v != null
} else = iv {
# try an impersonated session
v = get_databroker_record("type.googleapis.com/session.Session", input.session.id)
v != null
object.get(v, "impersonate_session_id", "") != ""
iv = get_databroker_record("type.googleapis.com/session.Session", v.impersonate_session_id)
iv != null
} else = v {
# try a normal session
v = get_databroker_record("type.googleapis.com/session.Session", input.session.id)
v != null
object.get(v, "impersonate_session_id", "") == ""
} else = {} {
true
}
user = u {
u = get_databroker_record("type.googleapis.com/user.User", session.user_id)
u != null
} else = {} {
true
}
directory_user = du {
du = get_databroker_record("type.googleapis.com/directory.User", session.user_id)
du != null
} else = {} {
true
}
group_ids = gs {
gs = directory_user.group_ids
gs != null
} else = [] {
true
}
groups := array.concat(group_ids, array.concat(get_databroker_group_names(group_ids), get_databroker_group_emails(group_ids)))
jwt_headers = {
"typ": "JWT",
"alg": data.signing_key.alg,
"kid": data.signing_key.kid,
}
jwt_payload_aud = v {
v := input.from_audience
} else = "" {
true
}
jwt_payload_iss = data.issuer
jwt_payload_jti = v {
v = session.id
} else = "" {
true
}
jwt_payload_exp = v {
v = min([five_minutes, round(session.expires_at.seconds)])
} else = v {
v = five_minutes
} else = null {
true
}
jwt_payload_iat = v {
# sessions store the issued_at on the id_token
v = round(session.id_token.issued_at.seconds)
} else = v {
# service accounts store the issued at directly
v = round(session.issued_at.seconds)
} else = null {
true
}
jwt_payload_sub = v {
v = user.id
} else = "" {
true
}
jwt_payload_user = v {
v = user.id
} else = "" {
true
}
jwt_payload_email = v {
v = directory_user.email
} else = v {
v = user.email
} else = "" {
true
}
jwt_payload_groups = v {
v = array.concat(group_ids, get_databroker_group_names(group_ids))
v != []
} else = v {
v = session.claims.groups
v != null
} else = [] {
true
}
# the session id is always set to the input session id, even if impersonating
jwt_payload_sid := input.session.id
base_jwt_claims := [
["iss", jwt_payload_iss],
["aud", jwt_payload_aud],
["jti", jwt_payload_jti],
["exp", jwt_payload_exp],
["iat", jwt_payload_iat],
["sub", jwt_payload_sub],
["user", jwt_payload_user],
["email", jwt_payload_email],
["groups", jwt_payload_groups],
["sid", jwt_payload_sid]
]
additional_jwt_claims := [[k, v] |
some header_name
claim_key := data.jwt_claim_headers[header_name]
# exclude base_jwt_claims
count([1 |
[xk, xv] := base_jwt_claims[_]
xk == claim_key
]) == 0
# the claim value can come from session claims or user claims
claim_value := object.get(session.claims, claim_key, object.get(user.claims, claim_key, null))
k := claim_key
v := get_header_string_value(claim_value)
]
jwt_claims := array.concat(base_jwt_claims, additional_jwt_claims)
jwt_payload = {key: value |
# use a comprehension over an array to remove nil values
[key, value] := jwt_claims[_]
value != null
}
signed_jwt = io.jwt.encode_sign(jwt_headers, jwt_payload, data.signing_key)
kubernetes_headers = h {
input.kubernetes_service_account_token != ""
h := [
["Authorization", concat(" ", ["Bearer", input.kubernetes_service_account_token])],
["Impersonate-User", jwt_payload_email],
["Impersonate-Group", get_header_string_value(jwt_payload_groups)],
]
} else = [] {
true
}
google_cloud_serverless_authentication_service_account = s {
s := data.google_cloud_serverless_authentication_service_account
} else = "" {
true
}
google_cloud_serverless_headers = h {
input.enable_google_cloud_serverless_authentication
h := get_google_cloud_serverless_headers(google_cloud_serverless_authentication_service_account, input.to_audience)
} else = {} {
true
}
identity_headers := {key: values |
h1 := [["x-pomerium-jwt-assertion", signed_jwt]]
h2 := [[header_name, header_value] |
some header_name
k := data.jwt_claim_headers[header_name]
header_value := array.concat(
[cv |
[ck, cv] := jwt_claims[_]
ck == k
],
[""]
)[0]
]
h3 := kubernetes_headers
h4 := [[k, v] | v := google_cloud_serverless_headers[k]]
h := array.concat(array.concat(array.concat(h1, h2), h3), h4)
some i
[key, v1] := h[i]
values := [v2 |
some j
[k2, v2] := h[j]
key == k2
]
}
get_databroker_group_names(ids) = gs {
gs := [name | id := ids[i]; group := get_databroker_record("type.googleapis.com/directory.Group", id); name := group.name]
}
get_databroker_group_emails(ids) = gs {
gs := [email | id := ids[i]; group := get_databroker_record("type.googleapis.com/directory.Group", id); email := group.email]
}
get_header_string_value(obj) = s {
is_array(obj)
s := concat(",", obj)
} else = s {
s := concat(",", [obj])
}