mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-02 00:10:45 +02:00
config/k8s: allow configuring claims used to detect user/group
adds 'kubernetes_impersonate_user_claim' (default 'email') and 'kubernetes_impersonate_group_claim' (default 'groups') route settings, which can be used to control the claims that are used to obtain user and group names for kubernetes impersonation.
This commit is contained in:
parent
0e13248685
commit
cc3431bd4a
6 changed files with 698 additions and 613 deletions
|
@ -23,6 +23,8 @@ type HeadersRequest struct {
|
|||
EnableRoutingKey bool `json:"enable_routing_key"`
|
||||
Issuer string `json:"issuer"`
|
||||
KubernetesServiceAccountToken string `json:"kubernetes_service_account_token"`
|
||||
KubernetesImpersonateUserClaim string `json:"kubernetes_impersonate_user_claim"`
|
||||
KubernetesImpersonateGroupClaim string `json:"kubernetes_impersonate_group_claim"`
|
||||
ToAudience string `json:"to_audience"`
|
||||
Session RequestSession `json:"session"`
|
||||
ClientCertificate ClientCertificateInfo `json:"client_certificate"`
|
||||
|
@ -38,6 +40,8 @@ func NewHeadersRequestFromPolicy(policy *config.Policy, http RequestHTTP) *Heade
|
|||
input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH ||
|
||||
policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
|
||||
input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
|
||||
input.KubernetesImpersonateUserClaim = policy.KubernetesImpersonateUserClaim
|
||||
input.KubernetesImpersonateGroupClaim = policy.KubernetesImpersonateGroupClaim
|
||||
for _, wu := range policy.To {
|
||||
input.ToAudience = "https://" + wu.URL.Hostname()
|
||||
}
|
||||
|
|
|
@ -81,10 +81,14 @@ func TestHeadersEvaluator(t *testing.T) {
|
|||
ctx := context.Background()
|
||||
ctx = storage.WithQuerier(ctx, storage.NewStaticQuerier(data...))
|
||||
store := store.New()
|
||||
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "CUSTOM_KEY"))
|
||||
store.UpdateJWTClaimHeaders(config.NewJWTClaimHeaders("email", "groups", "user", "pomerium_io_groups", "CUSTOM_KEY"))
|
||||
store.UpdateSigningKey(privateJWK)
|
||||
var buf bytes.Buffer
|
||||
e, err := NewHeadersEvaluator(ctx, store, rego.Time(iat))
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() {
|
||||
t.Log(buf.String())
|
||||
})
|
||||
return e.Evaluate(ctx, input, rego.EvalTime(iat))
|
||||
}
|
||||
|
||||
|
@ -240,18 +244,24 @@ func TestHeadersEvaluator(t *testing.T) {
|
|||
output, err := eval(t,
|
||||
[]protoreflect.ProtoMessage{
|
||||
&session.Session{Id: "s1", UserId: "u1"},
|
||||
&user.User{Id: "u1", Email: "u1@example.com"},
|
||||
&user.User{Id: "u1", Email: "u1@example.com", Claims: map[string]*structpb.ListValue{
|
||||
"pomerium_io_groups": {Values: []*structpb.Value{
|
||||
structpb.NewStringValue("admin"),
|
||||
}},
|
||||
}},
|
||||
},
|
||||
&HeadersRequest{
|
||||
Issuer: "from.example.com",
|
||||
ToAudience: "to.example.com",
|
||||
KubernetesServiceAccountToken: "TOKEN",
|
||||
Session: RequestSession{ID: "s1"},
|
||||
Issuer: "from.example.com",
|
||||
ToAudience: "to.example.com",
|
||||
KubernetesServiceAccountToken: "TOKEN",
|
||||
KubernetesImpersonateUserClaim: "email",
|
||||
KubernetesImpersonateGroupClaim: "pomerium_io_groups",
|
||||
Session: RequestSession{ID: "s1"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Bearer TOKEN", output.Headers.Get("Authorization"))
|
||||
assert.Equal(t, "u1@example.com", output.Headers.Get("Impersonate-User"))
|
||||
assert.Empty(t, output.Headers["Impersonate-Group"])
|
||||
assert.Equal(t, "admin", output.Headers.Get("Impersonate-Group"))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -132,6 +132,14 @@ base_jwt_claims := [
|
|||
["name", jwt_payload_name],
|
||||
]
|
||||
|
||||
session_claims := c if {
|
||||
c := session.claims
|
||||
} else := {}
|
||||
|
||||
user_claims := c if {
|
||||
c := user.claims
|
||||
} else := {}
|
||||
|
||||
additional_jwt_claims := [[k, v] |
|
||||
some header_name
|
||||
claim_key := data.jwt_claim_headers[header_name]
|
||||
|
@ -143,7 +151,7 @@ additional_jwt_claims := [[k, v] |
|
|||
]) == 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))
|
||||
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)
|
||||
|
@ -159,13 +167,33 @@ jwt_payload := {key: value |
|
|||
|
||||
signed_jwt := io.jwt.encode_sign(jwt_headers, jwt_payload, data.signing_key)
|
||||
|
||||
impersonate_user_claim := u if {
|
||||
u := input.kubernetes_impersonate_user_claim
|
||||
u != ""
|
||||
} else := "email"
|
||||
|
||||
impersonate_group_claim := g if {
|
||||
g := input.kubernetes_impersonate_group_claim
|
||||
g != ""
|
||||
} else := "groups"
|
||||
|
||||
impersonate_user := v if {
|
||||
[k, v] := jwt_claims[_]
|
||||
k == impersonate_user_claim
|
||||
}
|
||||
|
||||
impersonate_group := v if {
|
||||
[k, v] := jwt_claims[_]
|
||||
k == impersonate_group_claim
|
||||
}
|
||||
|
||||
kubernetes_headers := h if {
|
||||
input.kubernetes_service_account_token != ""
|
||||
|
||||
h := remove_empty_header_values([
|
||||
["Authorization", concat(" ", ["Bearer", input.kubernetes_service_account_token])],
|
||||
["Impersonate-User", jwt_payload_email],
|
||||
["Impersonate-Group", get_header_string_value(jwt_payload_groups)],
|
||||
["Impersonate-User", impersonate_user],
|
||||
["Impersonate-Group", get_header_string_value(impersonate_group)],
|
||||
])
|
||||
} else := []
|
||||
|
||||
|
|
|
@ -154,6 +154,9 @@ type Policy struct {
|
|||
// KubernetesServiceAccountTokenFile contains the kubernetes token to use for upstream requests.
|
||||
KubernetesServiceAccountTokenFile string `mapstructure:"kubernetes_service_account_token_file" yaml:"kubernetes_service_account_token_file,omitempty"`
|
||||
|
||||
KubernetesImpersonateUserClaim string `mapstructure:"kubernetes_impersonate_user_claim" yaml:"kubernetes_impersonate_user_claim,omitempty"`
|
||||
KubernetesImpersonateGroupClaim string `mapstructure:"kubernetes_impersonate_group_claim" yaml:"kubernetes_impersonate_group_claim,omitempty"`
|
||||
|
||||
// EnableGoogleCloudServerlessAuthentication adds "Authorization: Bearer ID_TOKEN" headers
|
||||
// to upstream requests.
|
||||
EnableGoogleCloudServerlessAuthentication bool `mapstructure:"enable_google_cloud_serverless_authentication" yaml:"enable_google_cloud_serverless_authentication,omitempty"`
|
||||
|
@ -274,6 +277,8 @@ func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
|
|||
HostPathRegexRewriteSubstitution: pb.GetHostPathRegexRewriteSubstitution(),
|
||||
PassIdentityHeaders: pb.PassIdentityHeaders,
|
||||
KubernetesServiceAccountToken: pb.GetKubernetesServiceAccountToken(),
|
||||
KubernetesImpersonateUserClaim: pb.GetKubernetesImpersonateUserClaim(),
|
||||
KubernetesImpersonateGroupClaim: pb.GetKubernetesImpersonateGroupClaim(),
|
||||
SetResponseHeaders: pb.GetSetResponseHeaders(),
|
||||
EnableGoogleCloudServerlessAuthentication: pb.GetEnableGoogleCloudServerlessAuthentication(),
|
||||
IDPClientID: pb.GetIdpClientId(),
|
||||
|
@ -434,6 +439,12 @@ func (p *Policy) ToProto() (*configpb.Route, error) {
|
|||
if p.IDPClientSecret != "" {
|
||||
pb.IdpClientSecret = proto.String(p.IDPClientSecret)
|
||||
}
|
||||
if p.KubernetesImpersonateUserClaim != "" {
|
||||
pb.KubernetesImpersonateUserClaim = proto.String(p.KubernetesImpersonateUserClaim)
|
||||
}
|
||||
if p.KubernetesImpersonateGroupClaim != "" {
|
||||
pb.KubernetesImpersonateGroupClaim = proto.String(p.KubernetesImpersonateGroupClaim)
|
||||
}
|
||||
if p.Redirect != nil {
|
||||
pb.Redirect = &configpb.RouteRedirect{
|
||||
HttpsRedirect: p.Redirect.HTTPSRedirect,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -38,7 +38,7 @@ message RouteDirectResponse {
|
|||
string body = 2;
|
||||
}
|
||||
|
||||
// Next ID: 63.
|
||||
// Next ID: 66.
|
||||
message Route {
|
||||
string name = 1;
|
||||
|
||||
|
@ -103,6 +103,8 @@ message Route {
|
|||
optional bool pass_identity_headers = 25;
|
||||
|
||||
string kubernetes_service_account_token = 26;
|
||||
optional string kubernetes_impersonate_user_claim = 64;
|
||||
optional string kubernetes_impersonate_group_claim = 65;
|
||||
bool enable_google_cloud_serverless_authentication = 42;
|
||||
|
||||
envoy.config.cluster.v3.Cluster envoy_opts = 36;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue