diff --git a/internal/concierge/impersonator/impersonator_test.go b/internal/concierge/impersonator/impersonator_test.go index a0ec03f9f..ef62ed9a6 100644 --- a/internal/concierge/impersonator/impersonator_test.go +++ b/internal/concierge/impersonator/impersonator_test.go @@ -685,9 +685,7 @@ func TestImpersonator(t *testing.T) { Name: "panda", UID: "", Groups: []string{"other-peeps", "system:authenticated"}, - Extra: map[string][]string{ - "party~~time": {"danger"}, - }, + Extra: map[string][]string{"party~~time": {"danger"}}, }, Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", }, @@ -807,425 +805,6 @@ func TestImpersonator(t *testing.T) { } }, }, - { - name: "failed client cert authentication", - clientCert: newClientCert(t, unrelatedCA, "test-username", []string{"test-group1"}), - wantError: "Unauthorized", - wantAuthorizerAttributes: nil, - }, - { - name: "nested impersonation by regular users calls delegating authorizer", - clientCert: newClientCert(t, ca, "test-username", []string{"test-group1", "test-group2"}), - clientImpersonateUser: rest.ImpersonationConfig{UserName: "some-other-username"}, - // this fails because the delegating authorizer in this test only allows system:masters and fails everything else - wantError: `users "some-other-username" is forbidden: User "test-username" ` + - `cannot impersonate resource "users" in API group "" at the cluster scope: ` + - `decision made by impersonation-proxy.concierge.pinniped.dev`, - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{ - { - User: defaultInfoForTestUsername(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "users", Subresource: "", Name: "some-other-username", ResourceRequest: true, Path: "", - }, - } - }, - }, - { - name: "nested impersonation by admin users calls delegating authorizer", - clientCert: newClientCert(t, ca, "test-admin", []string{"system:masters", "test-group2"}), - clientImpersonateUser: rest.ImpersonationConfig{ - UserName: "fire", - Groups: []string{"elements"}, - Extra: map[string][]string{ - "colors": {"red", "orange", "blue"}, - - // gke - "iam.gke.io/user-assertion": {"good", "stuff"}, - "user-assertion.cloud.google.com": {"smaller", "things"}, - - // openshift - "scopes.authorization.openshift.io": {"user:info", "user:full", "user:check-access"}, - - // openstack - "alpha.kubernetes.io/identity/roles": {"a-role1", "a-role2"}, - "alpha.kubernetes.io/identity/project/id": {"a-project-id"}, - "alpha.kubernetes.io/identity/project/name": {"a-project-name"}, - "alpha.kubernetes.io/identity/user/domain/id": {"a-domain-id"}, - "alpha.kubernetes.io/identity/user/domain/name": {"a-domain-name"}, - }, - }, - wantKubeAPIServerRequestHeaders: func(credentialID string) http.Header { - return http.Header{ - "Impersonate-User": {"fire"}, - "Impersonate-Group": {"elements", "system:authenticated"}, - "Impersonate-Extra-Colors": {"red", "orange", "blue"}, - "Impersonate-Extra-Iam.gke.io%2fuser-Assertion": {"good", "stuff"}, - "Impersonate-Extra-User-Assertion.cloud.google.com": {"smaller", "things"}, - "Impersonate-Extra-Scopes.authorization.openshift.io": {"user:info", "user:full", "user:check-access"}, - "Impersonate-Extra-Alpha.kubernetes.io%2fidentity%2froles": {"a-role1", "a-role2"}, - "Impersonate-Extra-Alpha.kubernetes.io%2fidentity%2fproject%2fid": {"a-project-id"}, - "Impersonate-Extra-Alpha.kubernetes.io%2fidentity%2fproject%2fname": {"a-project-name"}, - "Impersonate-Extra-Alpha.kubernetes.io%2fidentity%2fuser%2fdomain%2fid": {"a-domain-id"}, - "Impersonate-Extra-Alpha.kubernetes.io%2fidentity%2fuser%2fdomain%2fname": {"a-domain-name"}, - "Impersonate-Extra-Original-User-Info.impersonation-Proxy.concierge.pinniped.dev": {`{"username":"test-admin","groups":["test-group2","system:masters","system:authenticated"],"extra":{"authentication.kubernetes.io/credential-id":["` + credentialID + `"]}}`}, - "Authorization": {"Bearer some-service-account-token"}, - "User-Agent": {"test-agent"}, - "Accept": {"application/vnd.kubernetes.protobuf,application/json"}, - "Accept-Encoding": {"gzip"}, - "X-Forwarded-For": {"127.0.0.1"}, - } - }, - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{ - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "users", Subresource: "", Name: "fire", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "groups", Subresource: "", Name: "elements", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "iam.gke.io/user-assertion", Name: "good", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "iam.gke.io/user-assertion", Name: "stuff", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "alpha.kubernetes.io/identity/roles", Name: "a-role1", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "alpha.kubernetes.io/identity/roles", Name: "a-role2", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "user-assertion.cloud.google.com", Name: "smaller", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "user-assertion.cloud.google.com", Name: "things", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "colors", Name: "red", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "colors", Name: "orange", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "colors", Name: "blue", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "scopes.authorization.openshift.io", Name: "user:info", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "scopes.authorization.openshift.io", Name: "user:full", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "scopes.authorization.openshift.io", Name: "user:check-access", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "alpha.kubernetes.io/identity/project/name", Name: "a-project-name", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "alpha.kubernetes.io/identity/user/domain/id", Name: "a-domain-id", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "alpha.kubernetes.io/identity/user/domain/name", Name: "a-domain-name", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "alpha.kubernetes.io/identity/project/id", Name: "a-project-id", ResourceRequest: true, Path: "", - }, - { - User: &user.DefaultInfo{ - Name: "fire", - UID: "", - Groups: []string{"elements", "system:authenticated"}, - Extra: map[string][]string{ - "alpha.kubernetes.io/identity/project/id": {"a-project-id"}, - "alpha.kubernetes.io/identity/project/name": {"a-project-name"}, - "alpha.kubernetes.io/identity/roles": {"a-role1", "a-role2"}, - "alpha.kubernetes.io/identity/user/domain/id": {"a-domain-id"}, - "alpha.kubernetes.io/identity/user/domain/name": {"a-domain-name"}, - "colors": {"red", "orange", "blue"}, - "iam.gke.io/user-assertion": {"good", "stuff"}, - "scopes.authorization.openshift.io": {"user:info", "user:full", "user:check-access"}, - "user-assertion.cloud.google.com": {"smaller", "things"}, - }, - }, - Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", - }, - } - }, - }, - { - name: "nested impersonation by admin users cannot impersonate UID", - clientCert: newClientCert(t, ca, "test-admin", []string{"system:masters", "test-group2"}), - clientImpersonateUser: rest.ImpersonationConfig{UserName: "some-other-username"}, - clientMutateHeaders: func(header http.Header) { - header["Impersonate-Uid"] = []string{"root"} - }, - wantError: "Internal error occurred: unimplemented functionality - unable to act as current user", - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{ - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "users", Subresource: "", Name: "some-other-username", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "uids", Subresource: "", Name: "root", ResourceRequest: true, Path: "", - }, - { - User: &user.DefaultInfo{ - Name: "some-other-username", - UID: "root", - Groups: []string{"system:authenticated"}, - Extra: map[string][]string{}, - }, - Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", - }, - } - }, - }, - { - name: "nested impersonation by admin users cannot impersonate UID header canonicalization", - clientCert: newClientCert(t, ca, "test-admin", []string{"system:masters", "test-group2"}), - clientImpersonateUser: rest.ImpersonationConfig{UserName: "some-other-username"}, - clientMutateHeaders: func(header http.Header) { - header["imPerSoNaTE-uid"] = []string{"magic"} - }, - wantError: "Internal error occurred: unimplemented functionality - unable to act as current user", - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{ - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "users", Subresource: "", Name: "some-other-username", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "uids", Subresource: "", Name: "magic", ResourceRequest: true, Path: "", - }, - { - User: &user.DefaultInfo{ - Name: "some-other-username", - UID: "magic", - Groups: []string{"system:authenticated"}, - Extra: map[string][]string{}, - }, - Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", - }, - } - }, - }, - { - name: "nested impersonation by admin users cannot use reserved key", - clientCert: newClientCert(t, ca, "test-admin", []string{"system:masters", "test-group2"}), - clientImpersonateUser: rest.ImpersonationConfig{ - UserName: "other-user-to-impersonate", - Groups: []string{"other-peeps"}, - Extra: map[string][]string{ - "key": {"good"}, - "something.impersonation-proxy.concierge.pinniped.dev": {"bad data"}, - }, - }, - wantError: "Internal error occurred: unimplemented functionality - unable to act as current user", - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{ - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "users", Subresource: "", Name: "other-user-to-impersonate", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "groups", Subresource: "", Name: "other-peeps", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "something.impersonation-proxy.concierge.pinniped.dev", Name: "bad data", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "key", Name: "good", ResourceRequest: true, Path: "", - }, - { - User: &user.DefaultInfo{ - Name: "other-user-to-impersonate", - UID: "", - Groups: []string{"other-peeps", "system:authenticated"}, - Extra: map[string][]string{ - "key": {"good"}, - "something.impersonation-proxy.concierge.pinniped.dev": {"bad data"}, - }, - }, - Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", - }, - } - }, - }, - { - name: "nested impersonation by admin users cannot use invalid key", - clientCert: newClientCert(t, ca, "test-admin", []string{"system:masters", "test-group2"}), - clientImpersonateUser: rest.ImpersonationConfig{ - UserName: "panda", - Groups: []string{"other-peeps"}, - Extra: map[string][]string{ - "party~~time": {"danger"}, - }, - }, - wantError: "Internal error occurred: unimplemented functionality - unable to act as current user", - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{ - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "users", Subresource: "", Name: "panda", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "groups", Subresource: "", Name: "other-peeps", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "party~~time", Name: "danger", ResourceRequest: true, Path: "", - }, - { - User: &user.DefaultInfo{ - Name: "panda", - UID: "", - Groups: []string{"other-peeps", "system:authenticated"}, - Extra: map[string][]string{"party~~time": {"danger"}}, - }, - Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", - }, - } - }, - }, - { - name: "nested impersonation by admin users can use uppercase key because impersonation is lossy", - clientCert: newClientCert(t, ca, "test-admin", []string{"system:masters", "test-group2"}), - clientImpersonateUser: rest.ImpersonationConfig{ - UserName: "panda", - Groups: []string{"other-peeps"}, - Extra: map[string][]string{ - "ROAR": {"tiger"}, // by the time our code sees this key, it is lowercased to "roar" - }, - }, - wantKubeAPIServerRequestHeaders: func(credentialID string) http.Header { - return http.Header{ - "Impersonate-User": {"panda"}, - "Impersonate-Group": {"other-peeps", "system:authenticated"}, - "Impersonate-Extra-Roar": {"tiger"}, - "Impersonate-Extra-Original-User-Info.impersonation-Proxy.concierge.pinniped.dev": {`{"username":"test-admin","groups":["test-group2","system:masters","system:authenticated"],"extra":{"authentication.kubernetes.io/credential-id":["` + credentialID + `"]}}`}, - "Authorization": {"Bearer some-service-account-token"}, - "User-Agent": {"test-agent"}, - "Accept": {"application/vnd.kubernetes.protobuf,application/json"}, - "Accept-Encoding": {"gzip"}, - "X-Forwarded-For": {"127.0.0.1"}, - } - }, - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{ - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "users", Subresource: "", Name: "panda", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "groups", Subresource: "", Name: "other-peeps", ResourceRequest: true, Path: "", - }, - { - User: defaultInfoForTestAdmin(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "authentication.k8s.io", APIVersion: "v1", Resource: "userextras", Subresource: "roar", Name: "tiger", ResourceRequest: true, Path: "", - }, - { - User: &user.DefaultInfo{ - Name: "panda", - UID: "", - Groups: []string{"other-peeps", "system:authenticated"}, - Extra: map[string][]string{"roar": {"tiger"}}, - }, - Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", - }, - } - }, - }, - { - name: "unexpected healthz response", - kubeAPIServerHealthz: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - _, _ = w.Write([]byte("broken")) - }), - wantConstructionError: `could not detect if anonymous authentication is enabled: an error on the server ("broken") has prevented the request from succeeding`, - wantAuthorizerAttributes: nil, - }, - { - name: "header canonicalization user header", - clientCert: newClientCert(t, ca, "test-username", []string{"test-group1", "test-group2"}), - clientMutateHeaders: func(header http.Header) { - header["imPerSonaTE-USer"] = []string{"PANDA"} - }, - wantError: `users "PANDA" is forbidden: User "test-username" ` + - `cannot impersonate resource "users" in API group "" at the cluster scope: ` + - `decision made by impersonation-proxy.concierge.pinniped.dev`, - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{ - { - User: defaultInfoForTestUsername(credentialID), - Verb: "impersonate", Namespace: "", APIGroup: "", APIVersion: "", Resource: "users", Subresource: "", Name: "PANDA", ResourceRequest: true, Path: "", - }, - } - }, - }, - { - name: "header canonicalization future UID header", // no longer future as it exists in Kube v1.22 - clientCert: newClientCert(t, ca, "test-username", []string{"test-group1", "test-group2"}), - clientMutateHeaders: func(header http.Header) { - header["imPerSonaTE-uid"] = []string{"007"} - }, - wantError: `an error on the server ("Internal Server Error: \"/api/v1/namespaces\": requested [{UID 007 authentication.k8s.io/v1 }] without impersonating a user") has prevented the request from succeeding (get namespaces)`, - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{} - }, - }, - { - name: "future UID header", // no longer future as it exists in Kube v1.22 - clientCert: newClientCert(t, ca, "test-username", []string{"test-group1", "test-group2"}), - clientMutateHeaders: func(header http.Header) { - header["Impersonate-Uid"] = []string{"008"} - }, - wantError: `an error on the server ("Internal Server Error: \"/api/v1/namespaces\": requested [{UID 008 authentication.k8s.io/v1 }] without impersonating a user") has prevented the request from succeeding (get namespaces)`, - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{} - }, - }, - { - name: "when there is no service account token cached for the impersonator to use to call the KAS", - clientCert: newClientCert(t, ca, "test-username", []string{"test-group1", "test-group2"}), - noServiceAcctTokenInCache: true, - wantKubeAPIServerRequestHeaders: nil, // no request should have been made to the KAS on behalf of the user - wantError: `an error on the server ("") has prevented the request from succeeding (get namespaces)`, - wantAuthorizerAttributes: func(credentialID string) []authorizer.AttributesRecord { - return []authorizer.AttributesRecord{ - { - User: defaultInfoForTestUsername(credentialID), - Verb: "list", Namespace: "", APIGroup: "", APIVersion: "v1", Resource: "namespaces", Subresource: "", Name: "", ResourceRequest: true, Path: "/api/v1/namespaces", - }, - } - }, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {