diff --git a/pkg/apiserver/handlers.go b/pkg/apiserver/handlers.go index b969621f2aa..d9b2eb85460 100644 --- a/pkg/apiserver/handlers.go +++ b/pkg/apiserver/handlers.go @@ -410,7 +410,7 @@ func (r *requestAttributeGetter) GetAttribs(req *http.Request) authorizer.Attrib return &attribs } -func WithActingAs(handler http.Handler, requestContextMapper api.RequestContextMapper, a authorizer.Authorizer) http.Handler { +func WithImpersonation(handler http.Handler, requestContextMapper api.RequestContextMapper, a authorizer.Authorizer) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { requestedSubject := req.Header.Get("Impersonate-User") if len(requestedSubject) == 0 { @@ -430,13 +430,18 @@ func WithActingAs(handler http.Handler, requestContextMapper api.RequestContextM } actingAsAttributes := &authorizer.AttributesRecord{ - User: requestor, - Verb: "impersonate", - APIGroup: api.GroupName, - Resource: "users", - // ResourceName: requestedSubject, + User: requestor, + Verb: "impersonate", + APIGroup: api.GroupName, + Resource: "users", + Name: requestedSubject, ResourceRequest: true, } + if namespace, name, err := serviceaccount.SplitUsername(requestedSubject); err == nil { + actingAsAttributes.Resource = "serviceaccounts" + actingAsAttributes.Namespace = namespace + actingAsAttributes.Name = name + } err := a.Authorize(actingAsAttributes) if err != nil { diff --git a/pkg/genericapiserver/genericapiserver.go b/pkg/genericapiserver/genericapiserver.go index 82e4bdb0092..4412c009877 100644 --- a/pkg/genericapiserver/genericapiserver.go +++ b/pkg/genericapiserver/genericapiserver.go @@ -475,7 +475,7 @@ func (s *GenericAPIServer) init(c *Config) { attributeGetter := apiserver.NewRequestAttributeGetter(s.RequestContextMapper, s.NewRequestInfoResolver()) handler = apiserver.WithAuthorizationCheck(handler, attributeGetter, s.authorizer) - handler = apiserver.WithActingAs(handler, s.RequestContextMapper, s.authorizer) + handler = apiserver.WithImpersonation(handler, s.RequestContextMapper, s.authorizer) // Install Authenticator if c.Authenticator != nil { diff --git a/test/integration/auth_test.go b/test/integration/auth_test.go index b7383aaf4d5..deb233437a6 100644 --- a/test/integration/auth_test.go +++ b/test/integration/auth_test.go @@ -47,6 +47,7 @@ import ( "k8s.io/kubernetes/pkg/auth/authorizer/abac" "k8s.io/kubernetes/pkg/auth/user" "k8s.io/kubernetes/pkg/master" + "k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/plugin/pkg/admission/admit" "k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/tokentest" "k8s.io/kubernetes/test/integration/framework" @@ -724,12 +725,22 @@ type impersonateAuthorizer struct{} // alice can't act as anyone and bob can't do anything but act-as someone func (impersonateAuthorizer) Authorize(a authorizer.Attributes) error { + // alice can impersonate service accounts and do other actions + if a.GetUserName() == "alice" && a.GetVerb() == "impersonate" && a.GetResource() == "serviceaccounts" { + return nil + } if a.GetUserName() == "alice" && a.GetVerb() != "impersonate" { return nil } + // bob can impersonate anyone, but that it if a.GetUserName() == "bob" && a.GetVerb() == "impersonate" { return nil } + // service accounts can do everything + if strings.HasPrefix(a.GetUserName(), serviceaccount.ServiceAccountUsernamePrefix) { + return nil + } + return errors.New("I can't allow that. Go ask alice.") } @@ -752,6 +763,7 @@ func TestImpersonateIsForbidden(t *testing.T) { transport := http.DefaultTransport + // bob can't perform actions himself for _, r := range getTestRequests() { token := BobToken bodyBytes := bytes.NewReader([]byte(r.body)) @@ -776,6 +788,7 @@ func TestImpersonateIsForbidden(t *testing.T) { }() } + // bob can impersonate alice to do other things for _, r := range getTestRequests() { token := BobToken bodyBytes := bytes.NewReader([]byte(r.body)) @@ -799,6 +812,58 @@ func TestImpersonateIsForbidden(t *testing.T) { } }() } + + // alice can't impersonate bob + for _, r := range getTestRequests() { + token := AliceToken + bodyBytes := bytes.NewReader([]byte(r.body)) + req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) + req.Header.Set("Impersonate-User", "bob") + + func() { + resp, err := transport.RoundTrip(req) + defer resp.Body.Close() + if err != nil { + t.Logf("case %v", r) + t.Fatalf("unexpected error: %v", err) + } + // Expect all of bob's actions to return Forbidden + if resp.StatusCode != http.StatusForbidden { + t.Logf("case %v", r) + t.Errorf("Expected not status Forbidden, but got %s", resp.Status) + } + }() + } + + // alice can impersonate a service account + for _, r := range getTestRequests() { + token := BobToken + bodyBytes := bytes.NewReader([]byte(r.body)) + req, err := http.NewRequest(r.verb, s.URL+r.URL, bodyBytes) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token)) + req.Header.Set("Impersonate-User", serviceaccount.MakeUsername("default", "default")) + func() { + resp, err := transport.RoundTrip(req) + defer resp.Body.Close() + if err != nil { + t.Logf("case %v", r) + t.Fatalf("unexpected error: %v", err) + } + // Expect all the requests to be allowed, don't care what they actually do + if resp.StatusCode == http.StatusForbidden { + t.Logf("case %v", r) + t.Errorf("Expected status not %v, but got %v", http.StatusForbidden, resp.StatusCode) + } + }() + } + } func newAuthorizerWithContents(t *testing.T, contents string) authorizer.Authorizer {