Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enable resource name and service account cases for impersonation #24795

Merged
merged 1 commit into from
May 9, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 11 additions & 6 deletions pkg/apiserver/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}
Copy link
Member

@liggitt liggitt Apr 26, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a slight preference for an if/else rather than setting to users,<name> and overriding here, but otherwise LGTM

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as-is is fine with me.


err := a.Authorize(actingAsAttributes)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/genericapiserver/genericapiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,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 {
Expand Down
65 changes: 65 additions & 0 deletions test/integration/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -729,12 +730,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.")
}

Expand All @@ -757,6 +768,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))
Expand All @@ -781,6 +793,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))
Expand All @@ -804,6 +817,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 {
Expand Down