123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- /*
- Copyright 2016 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package apiserver
- import (
- "fmt"
- "net/http"
- "net/http/httptest"
- "reflect"
- "sync"
- "testing"
- "k8s.io/kubernetes/pkg/api"
- authenticationapi "k8s.io/kubernetes/pkg/apis/authentication"
- "k8s.io/kubernetes/pkg/auth/authorizer"
- "k8s.io/kubernetes/pkg/auth/user"
- )
- type impersonateAuthorizer struct{}
- func (impersonateAuthorizer) Authorize(a authorizer.Attributes) (authorized bool, reason string, err error) {
- user := a.GetUser()
- switch {
- case user.GetName() == "system:admin":
- return true, "", nil
- case user.GetName() == "tester":
- return false, "", fmt.Errorf("works on my machine")
- case user.GetName() == "deny-me":
- return false, "denied", nil
- }
- if len(user.GetGroups()) > 0 && user.GetGroups()[0] == "wheel" && a.GetVerb() == "impersonate" && a.GetResource() == "users" {
- return true, "", nil
- }
- if len(user.GetGroups()) > 0 && user.GetGroups()[0] == "sa-impersonater" && a.GetVerb() == "impersonate" && a.GetResource() == "serviceaccounts" {
- return true, "", nil
- }
- if len(user.GetGroups()) > 0 && user.GetGroups()[0] == "regular-impersonater" && a.GetVerb() == "impersonate" && a.GetResource() == "users" {
- return true, "", nil
- }
- if len(user.GetGroups()) > 1 && user.GetGroups()[1] == "group-impersonater" && a.GetVerb() == "impersonate" && a.GetResource() == "groups" {
- return true, "", nil
- }
- if len(user.GetGroups()) > 1 && user.GetGroups()[1] == "extra-setter-scopes" && a.GetVerb() == "impersonate" && a.GetResource() == "userextras" && a.GetSubresource() == "scopes" {
- return true, "", nil
- }
- if len(user.GetGroups()) > 1 && user.GetGroups()[1] == "extra-setter-particular-scopes" &&
- a.GetVerb() == "impersonate" && a.GetResource() == "userextras" && a.GetSubresource() == "scopes" && a.GetName() == "scope-a" {
- return true, "", nil
- }
- if len(user.GetGroups()) > 1 && user.GetGroups()[1] == "extra-setter-project" && a.GetVerb() == "impersonate" && a.GetResource() == "userextras" && a.GetSubresource() == "project" {
- return true, "", nil
- }
- return false, "deny by default", nil
- }
- func TestImpersonationFilter(t *testing.T) {
- testCases := []struct {
- name string
- user user.Info
- impersonationUser string
- impersonationGroups []string
- impersonationUserExtras map[string][]string
- expectedUser user.Info
- expectedCode int
- }{
- {
- name: "not-impersonating",
- user: &user.DefaultInfo{
- Name: "tester",
- },
- expectedUser: &user.DefaultInfo{
- Name: "tester",
- },
- expectedCode: http.StatusOK,
- },
- {
- name: "impersonating-error",
- user: &user.DefaultInfo{
- Name: "tester",
- },
- impersonationUser: "anyone",
- expectedUser: &user.DefaultInfo{
- Name: "tester",
- },
- expectedCode: http.StatusForbidden,
- },
- {
- name: "impersonating-group-without-user",
- user: &user.DefaultInfo{
- Name: "tester",
- },
- impersonationGroups: []string{"some-group"},
- expectedUser: &user.DefaultInfo{
- Name: "tester",
- },
- expectedCode: http.StatusForbidden,
- },
- {
- name: "impersonating-extra-without-user",
- user: &user.DefaultInfo{
- Name: "tester",
- },
- impersonationUserExtras: map[string][]string{"scopes": {"scope-a"}},
- expectedUser: &user.DefaultInfo{
- Name: "tester",
- },
- expectedCode: http.StatusForbidden,
- },
- {
- name: "disallowed-group",
- user: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"wheel"},
- },
- impersonationUser: "system:admin",
- impersonationGroups: []string{"some-group"},
- expectedUser: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"wheel"},
- },
- expectedCode: http.StatusForbidden,
- },
- {
- name: "allowed-group",
- user: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"wheel", "group-impersonater"},
- },
- impersonationUser: "system:admin",
- impersonationGroups: []string{"some-group"},
- expectedUser: &user.DefaultInfo{
- Name: "system:admin",
- Groups: []string{"some-group"},
- Extra: map[string][]string{},
- },
- expectedCode: http.StatusOK,
- },
- {
- name: "disallowed-userextra-1",
- user: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"wheel"},
- },
- impersonationUser: "system:admin",
- impersonationGroups: []string{"some-group"},
- impersonationUserExtras: map[string][]string{"scopes": {"scope-a"}},
- expectedUser: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"wheel"},
- },
- expectedCode: http.StatusForbidden,
- },
- {
- name: "disallowed-userextra-2",
- user: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"wheel", "extra-setter-project"},
- },
- impersonationUser: "system:admin",
- impersonationGroups: []string{"some-group"},
- impersonationUserExtras: map[string][]string{"scopes": {"scope-a"}},
- expectedUser: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"wheel", "extra-setter-project"},
- },
- expectedCode: http.StatusForbidden,
- },
- {
- name: "disallowed-userextra-3",
- user: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"wheel", "extra-setter-particular-scopes"},
- },
- impersonationUser: "system:admin",
- impersonationGroups: []string{"some-group"},
- impersonationUserExtras: map[string][]string{"scopes": {"scope-a", "scope-b"}},
- expectedUser: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"wheel", "extra-setter-particular-scopes"},
- },
- expectedCode: http.StatusForbidden,
- },
- {
- name: "allowed-userextras",
- user: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"wheel", "extra-setter-scopes"},
- },
- impersonationUser: "system:admin",
- impersonationUserExtras: map[string][]string{"scopes": {"scope-a", "scope-b"}},
- expectedUser: &user.DefaultInfo{
- Name: "system:admin",
- Groups: []string{},
- Extra: map[string][]string{"scopes": {"scope-a", "scope-b"}},
- },
- expectedCode: http.StatusOK,
- },
- {
- name: "allowed-users-impersonation",
- user: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"regular-impersonater"},
- },
- impersonationUser: "tester",
- expectedUser: &user.DefaultInfo{
- Name: "tester",
- Groups: []string{},
- Extra: map[string][]string{},
- },
- expectedCode: http.StatusOK,
- },
- {
- name: "disallowed-impersonating",
- user: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"sa-impersonater"},
- },
- impersonationUser: "tester",
- expectedUser: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"sa-impersonater"},
- },
- expectedCode: http.StatusForbidden,
- },
- {
- name: "allowed-sa-impersonating",
- user: &user.DefaultInfo{
- Name: "dev",
- Groups: []string{"sa-impersonater"},
- Extra: map[string][]string{},
- },
- impersonationUser: "system:serviceaccount:foo:default",
- expectedUser: &user.DefaultInfo{
- Name: "system:serviceaccount:foo:default",
- Groups: []string{"system:serviceaccounts", "system:serviceaccounts:foo"},
- Extra: map[string][]string{},
- },
- expectedCode: http.StatusOK,
- },
- }
- requestContextMapper = api.NewRequestContextMapper()
- var ctx api.Context
- var actualUser user.Info
- var lock sync.Mutex
- doNothingHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- currentCtx, _ := requestContextMapper.Get(req)
- user, exists := api.UserFrom(currentCtx)
- if !exists {
- actualUser = nil
- return
- }
- actualUser = user
- })
- handler := func(delegate http.Handler) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- defer func() {
- if r := recover(); r != nil {
- t.Errorf("Recovered %v", r)
- }
- }()
- lock.Lock()
- defer lock.Unlock()
- requestContextMapper.Update(req, ctx)
- currentCtx, _ := requestContextMapper.Get(req)
- user, exists := api.UserFrom(currentCtx)
- if !exists {
- actualUser = nil
- return
- } else {
- actualUser = user
- }
- delegate.ServeHTTP(w, req)
- })
- }(WithImpersonation(doNothingHandler, requestContextMapper, impersonateAuthorizer{}))
- handler, _ = api.NewRequestContextFilter(requestContextMapper, handler)
- server := httptest.NewServer(handler)
- defer server.Close()
- for _, tc := range testCases {
- func() {
- lock.Lock()
- defer lock.Unlock()
- ctx = api.WithUser(api.NewContext(), tc.user)
- }()
- req, err := http.NewRequest("GET", server.URL, nil)
- if err != nil {
- t.Errorf("%s: unexpected error: %v", tc.name, err)
- continue
- }
- req.Header.Add(authenticationapi.ImpersonateUserHeader, tc.impersonationUser)
- for _, group := range tc.impersonationGroups {
- req.Header.Add(authenticationapi.ImpersonateGroupHeader, group)
- }
- for extraKey, values := range tc.impersonationUserExtras {
- for _, value := range values {
- req.Header.Add(authenticationapi.ImpersonateUserExtraHeaderPrefix+extraKey, value)
- }
- }
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- t.Errorf("%s: unexpected error: %v", tc.name, err)
- continue
- }
- if resp.StatusCode != tc.expectedCode {
- t.Errorf("%s: expected %v, actual %v", tc.name, tc.expectedCode, resp.StatusCode)
- continue
- }
- if !reflect.DeepEqual(actualUser, tc.expectedUser) {
- t.Errorf("%s: expected %#v, actual %#v", tc.name, tc.expectedUser, actualUser)
- continue
- }
- }
- }
|