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 }
// startServiceAccountTestServer returns a started server // It is the responsibility of the caller to ensure the returned stopFunc is called func startServiceAccountTestServer(t *testing.T) (*clientset.Clientset, restclient.Config, func()) { // Listener h := &framework.MasterHolder{Initialized: make(chan struct{})} apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { <-h.Initialized h.M.GenericAPIServer.Handler.ServeHTTP(w, req) })) // Anonymous client config clientConfig := restclient.Config{Host: apiServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(v1.GroupName).GroupVersion}} // Root client // TODO: remove rootClient after we refactor pkg/admission to use the clientset. rootClientset := clientset.NewForConfigOrDie(&restclient.Config{Host: apiServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(v1.GroupName).GroupVersion}, BearerToken: rootToken}) internalRootClientset := internalclientset.NewForConfigOrDie(&restclient.Config{Host: apiServer.URL, ContentConfig: restclient.ContentConfig{GroupVersion: &api.Registry.GroupOrDie(v1.GroupName).GroupVersion}, BearerToken: rootToken}) // Set up two authenticators: // 1. A token authenticator that maps the rootToken to the "root" user // 2. A ServiceAccountToken authenticator that validates ServiceAccount tokens rootTokenAuth := authenticator.TokenFunc(func(token string) (user.Info, bool, error) { if token == rootToken { return &user.DefaultInfo{Name: rootUserName}, true, nil } return nil, false, nil }) serviceAccountKey, _ := rsa.GenerateKey(rand.Reader, 2048) serviceAccountTokenGetter := serviceaccountcontroller.NewGetterFromClient(rootClientset) serviceAccountTokenAuth := serviceaccount.JWTTokenAuthenticator([]interface{}{&serviceAccountKey.PublicKey}, true, serviceAccountTokenGetter) authenticator := union.New( bearertoken.New(rootTokenAuth), bearertoken.New(serviceAccountTokenAuth), ) // Set up a stub authorizer: // 1. The "root" user is allowed to do anything // 2. ServiceAccounts named "ro" are allowed read-only operations in their namespace // 3. ServiceAccounts named "rw" are allowed any operation in their namespace authorizer := authorizer.AuthorizerFunc(func(attrs authorizer.Attributes) (bool, string, error) { username := "" if user := attrs.GetUser(); user != nil { username = user.GetName() } ns := attrs.GetNamespace() // If the user is "root"... if username == rootUserName { // allow them to do anything return true, "", nil } // If the user is a service account... if serviceAccountNamespace, serviceAccountName, err := serviceaccountapiserver.SplitUsername(username); err == nil { // Limit them to their own namespace if serviceAccountNamespace == ns { switch serviceAccountName { case readOnlyServiceAccountName: if attrs.IsReadOnly() { return true, "", nil } case readWriteServiceAccountName: return true, "", nil } } } return false, fmt.Sprintf("User %s is denied (ns=%s, readonly=%v, resource=%s)", username, ns, attrs.IsReadOnly(), attrs.GetResource()), nil }) // Set up admission plugin to auto-assign serviceaccounts to pods serviceAccountAdmission := serviceaccountadmission.NewServiceAccount() serviceAccountAdmission.SetInternalClientSet(internalRootClientset) masterConfig := framework.NewMasterConfig() masterConfig.GenericConfig.EnableIndex = true masterConfig.GenericConfig.Authenticator = authenticator masterConfig.GenericConfig.Authorizer = authorizer masterConfig.GenericConfig.AdmissionControl = serviceAccountAdmission framework.RunAMasterUsingServer(masterConfig, apiServer, h) // Start the service account and service account token controllers stopCh := make(chan struct{}) tokenController := serviceaccountcontroller.NewTokensController(rootClientset, serviceaccountcontroller.TokensControllerOptions{TokenGenerator: serviceaccount.JWTTokenGenerator(serviceAccountKey)}) go tokenController.Run(1, stopCh) informers := informers.NewSharedInformerFactory(rootClientset, nil, controller.NoResyncPeriodFunc()) serviceAccountController := serviceaccountcontroller.NewServiceAccountsController(informers.ServiceAccounts(), informers.Namespaces(), rootClientset, serviceaccountcontroller.DefaultServiceAccountsControllerOptions()) informers.Start(stopCh) go serviceAccountController.Run(5, stopCh) // Start the admission plugin reflectors serviceAccountAdmission.Run() stop := func() { close(stopCh) serviceAccountAdmission.Stop() apiServer.Close() } return rootClientset, clientConfig, stop }
func TestBasicAuth(t *testing.T) { testCases := map[string]struct { Header string Password testPassword ExpectedCalled bool ExpectedUsername string ExpectedPassword string ExpectedUser string ExpectedOK bool ExpectedErr bool }{ "no auth": {}, "empty password basic header": { ExpectedCalled: true, ExpectedUsername: "******", ExpectedPassword: "", }, "valid basic header": { ExpectedCalled: true, ExpectedUsername: "******", ExpectedPassword: "******", }, "password auth returned user": { Password: testPassword{User: &user.DefaultInfo{Name: "returneduser"}, OK: true}, ExpectedCalled: true, ExpectedUsername: "******", ExpectedPassword: "******", ExpectedUser: "******", ExpectedOK: true, }, "password auth returned error": { Password: testPassword{Err: errors.New("auth error")}, ExpectedCalled: true, ExpectedUsername: "******", ExpectedPassword: "******", ExpectedErr: true, }, } for k, testCase := range testCases { password := testCase.Password auth := authenticator.Request(New(&password)) req, _ := http.NewRequest("GET", "/", nil) if testCase.ExpectedUsername != "" || testCase.ExpectedPassword != "" { req.SetBasicAuth(testCase.ExpectedUsername, testCase.ExpectedPassword) } user, ok, err := auth.AuthenticateRequest(req) if testCase.ExpectedCalled != password.Called { t.Errorf("%s: Expected called=%v, got %v", k, testCase.ExpectedCalled, password.Called) continue } if testCase.ExpectedUsername != password.Username { t.Errorf("%s: Expected called with username=%v, got %v", k, testCase.ExpectedUsername, password.Username) continue } if testCase.ExpectedPassword != password.Password { t.Errorf("%s: Expected called with password=%v, got %v", k, testCase.ExpectedPassword, password.Password) continue } if testCase.ExpectedErr != (err != nil) { t.Errorf("%s: Expected err=%v, got err=%v", k, testCase.ExpectedErr, err) continue } if testCase.ExpectedOK != ok { t.Errorf("%s: Expected ok=%v, got ok=%v", k, testCase.ExpectedOK, ok) continue } if testCase.ExpectedUser != "" && testCase.ExpectedUser != user.GetName() { t.Errorf("%s: Expected user.GetName()=%v, got %v", k, testCase.ExpectedUser, user.GetName()) continue } } }
func TestTLSConfig(t *testing.T) { // Verify the cert/key pair works. cert1 := path.Join(os.TempDir(), "oidc-cert-1") key1 := path.Join(os.TempDir(), "oidc-key-1") cert2 := path.Join(os.TempDir(), "oidc-cert-2") key2 := path.Join(os.TempDir(), "oidc-key-2") defer os.Remove(cert1) defer os.Remove(key1) defer os.Remove(cert2) defer os.Remove(key2) oidctesting.GenerateSelfSignedCert(t, "127.0.0.1", cert1, key1) oidctesting.GenerateSelfSignedCert(t, "127.0.0.1", cert2, key2) tests := []struct { testCase string serverCertFile string serverKeyFile string trustedCertFile string wantErr bool }{ { testCase: "provider using untrusted custom cert", serverCertFile: cert1, serverKeyFile: key1, wantErr: true, }, { testCase: "provider using untrusted cert", serverCertFile: cert1, serverKeyFile: key1, trustedCertFile: cert2, wantErr: true, }, { testCase: "provider using trusted cert", serverCertFile: cert1, serverKeyFile: key1, trustedCertFile: cert1, wantErr: false, }, } for _, tc := range tests { func() { op := oidctesting.NewOIDCProvider(t, "") srv, err := op.ServeTLSWithKeyPair(tc.serverCertFile, tc.serverKeyFile) if err != nil { t.Errorf("%s: %v", tc.testCase, err) return } defer srv.Close() issuer := srv.URL clientID := "client-foo" options := OIDCOptions{ IssuerURL: srv.URL, ClientID: clientID, CAFile: tc.trustedCertFile, UsernameClaim: "email", GroupsClaim: "groups", } authenticator, err := New(options) if err != nil { t.Errorf("%s: failed to initialize authenticator: %v", tc.testCase, err) return } defer authenticator.Close() email := "*****@*****.**" groups := []string{"group1", "group2"} sort.Strings(groups) token := generateGoodToken(t, op, issuer, "user-1", clientID, "email", email, "groups", groups) // Because this authenticator behaves differently for subsequent requests, run these // tests multiple times (but expect the same result). for i := 1; i < 4; i++ { user, ok, err := authenticator.AuthenticateToken(token) if err != nil { if !tc.wantErr { t.Errorf("%s (req #%d): failed to authenticate token: %v", tc.testCase, i, err) } continue } if tc.wantErr { t.Errorf("%s (req #%d): expected error authenticating", tc.testCase, i) continue } if !ok { t.Errorf("%s (req #%d): did not get user or error", tc.testCase, i) continue } if gotUsername := user.GetName(); email != gotUsername { t.Errorf("%s (req #%d): GetName() expected=%q got %q", tc.testCase, i, email, gotUsername) } gotGroups := user.GetGroups() sort.Strings(gotGroups) if !reflect.DeepEqual(gotGroups, groups) { t.Errorf("%s (req #%d): GetGroups() expected=%q got %q", tc.testCase, i, groups, gotGroups) } } }() } }
func TestX509Verifier(t *testing.T) { multilevelOpts := DefaultVerifyOptions() multilevelOpts.Roots = x509.NewCertPool() multilevelOpts.Roots.AddCert(getCertsFromFile(t, "root")[0]) testCases := map[string]struct { Insecure bool Certs []*x509.Certificate Opts x509.VerifyOptions AllowedCNs sets.String ExpectOK bool ExpectErr bool }{ "non-tls": { Insecure: true, ExpectOK: false, ExpectErr: false, }, "tls, no certs": { ExpectOK: false, ExpectErr: false, }, "self signed": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, selfSignedCert), ExpectErr: true, }, "server cert disallowed": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, serverCert), ExpectErr: true, }, "server cert allowing non-client cert usages": { Opts: x509.VerifyOptions{Roots: getRootCertPool(t)}, Certs: getCerts(t, serverCert), ExpectOK: true, ExpectErr: false, }, "valid client cert": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, clientCNCert), ExpectOK: true, ExpectErr: false, }, "valid client cert with wrong CN": { Opts: getDefaultVerifyOptions(t), AllowedCNs: sets.NewString("foo", "bar"), Certs: getCerts(t, clientCNCert), ExpectOK: false, ExpectErr: true, }, "valid client cert with right CN": { Opts: getDefaultVerifyOptions(t), AllowedCNs: sets.NewString("client_cn"), Certs: getCerts(t, clientCNCert), ExpectOK: true, ExpectErr: false, }, "future cert": { Opts: x509.VerifyOptions{ CurrentTime: time.Now().Add(-100 * time.Hour * 24 * 365), Roots: getRootCertPool(t), }, Certs: getCerts(t, clientCNCert), ExpectOK: false, ExpectErr: true, }, "expired cert": { Opts: x509.VerifyOptions{ CurrentTime: time.Now().Add(100 * time.Hour * 24 * 365), Roots: getRootCertPool(t), }, Certs: getCerts(t, clientCNCert), ExpectOK: false, ExpectErr: true, }, "multi-level, valid": { Opts: multilevelOpts, Certs: getCertsFromFile(t, "client-valid", "intermediate"), ExpectOK: true, ExpectErr: false, }, "multi-level, expired": { Opts: multilevelOpts, Certs: getCertsFromFile(t, "client-expired", "intermediate"), ExpectOK: false, ExpectErr: true, }, } for k, testCase := range testCases { req, _ := http.NewRequest("GET", "/", nil) if !testCase.Insecure { req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs} } authCall := false auth := authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) { authCall = true return &user.DefaultInfo{Name: "innerauth"}, true, nil }) a := NewVerifier(testCase.Opts, auth, testCase.AllowedCNs) user, ok, err := a.AuthenticateRequest(req) if testCase.ExpectErr && err == nil { t.Errorf("%s: Expected error, got none", k) continue } if !testCase.ExpectErr && err != nil { t.Errorf("%s: Got unexpected error: %v", k, err) continue } if testCase.ExpectOK != ok { t.Errorf("%s: Expected ok=%v, got %v", k, testCase.ExpectOK, ok) continue } if testCase.ExpectOK { if !authCall { t.Errorf("%s: Expected inner auth called, wasn't", k) continue } if "innerauth" != user.GetName() { t.Errorf("%s: Expected user.name=%v, got %v", k, "innerauth", user.GetName()) continue } } else { if authCall { t.Errorf("%s: Expected inner auth not to be called, was", k) continue } } } }
func TestX509(t *testing.T) { multilevelOpts := DefaultVerifyOptions() multilevelOpts.Roots = x509.NewCertPool() multilevelOpts.Roots.AddCert(getCertsFromFile(t, "root")[0]) testCases := map[string]struct { Insecure bool Certs []*x509.Certificate Opts x509.VerifyOptions User UserConversion ExpectUserName string ExpectGroups []string ExpectOK bool ExpectErr bool }{ "non-tls": { Insecure: true, ExpectOK: false, ExpectErr: false, }, "tls, no certs": { ExpectOK: false, ExpectErr: false, }, "self signed": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, selfSignedCert), User: CommonNameUserConversion, ExpectErr: true, }, "server cert": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, serverCert), User: CommonNameUserConversion, ExpectErr: true, }, "server cert allowing non-client cert usages": { Opts: x509.VerifyOptions{Roots: getRootCertPool(t)}, Certs: getCerts(t, serverCert), User: CommonNameUserConversion, ExpectUserName: "******", ExpectGroups: []string{"My Org"}, ExpectOK: true, ExpectErr: false, }, "common name": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, clientCNCert), User: CommonNameUserConversion, ExpectUserName: "******", ExpectGroups: []string{"My Org"}, ExpectOK: true, ExpectErr: false, }, "ca with multiple organizations": { Opts: x509.VerifyOptions{ Roots: getRootCertPoolFor(t, caWithGroups), }, Certs: getCerts(t, caWithGroups), User: CommonNameUserConversion, ExpectUserName: "******", ExpectGroups: []string{"My Org", "My Org 1", "My Org 2"}, ExpectOK: true, ExpectErr: false, }, "empty dns": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, clientCNCert), User: DNSNameUserConversion, ExpectOK: false, ExpectErr: false, }, "dns": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, clientDNSCert), User: DNSNameUserConversion, ExpectUserName: "******", ExpectOK: true, ExpectErr: false, }, "empty email": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, clientCNCert), User: EmailAddressUserConversion, ExpectOK: false, ExpectErr: false, }, "email": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, clientEmailCert), User: EmailAddressUserConversion, ExpectUserName: "******", ExpectOK: true, ExpectErr: false, }, "custom conversion error": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, clientCNCert), User: UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) { return nil, false, errors.New("custom error") }), ExpectOK: false, ExpectErr: true, }, "custom conversion success": { Opts: getDefaultVerifyOptions(t), Certs: getCerts(t, clientCNCert), User: UserConversionFunc(func(chain []*x509.Certificate) (user.Info, bool, error) { return &user.DefaultInfo{Name: "custom"}, true, nil }), ExpectUserName: "******", ExpectOK: true, ExpectErr: false, }, "future cert": { Opts: x509.VerifyOptions{ CurrentTime: time.Now().Add(time.Duration(-100 * time.Hour * 24 * 365)), Roots: getRootCertPool(t), }, Certs: getCerts(t, clientCNCert), User: CommonNameUserConversion, ExpectOK: false, ExpectErr: true, }, "expired cert": { Opts: x509.VerifyOptions{ CurrentTime: time.Now().Add(time.Duration(100 * time.Hour * 24 * 365)), Roots: getRootCertPool(t), }, Certs: getCerts(t, clientCNCert), User: CommonNameUserConversion, ExpectOK: false, ExpectErr: true, }, "multi-level, valid": { Opts: multilevelOpts, Certs: getCertsFromFile(t, "client-valid", "intermediate"), User: CommonNameUserConversion, ExpectUserName: "******", ExpectOK: true, ExpectErr: false, }, "multi-level, expired": { Opts: multilevelOpts, Certs: getCertsFromFile(t, "client-expired", "intermediate"), User: CommonNameUserConversion, ExpectOK: false, ExpectErr: true, }, } for k, testCase := range testCases { req, _ := http.NewRequest("GET", "/", nil) if !testCase.Insecure { req.TLS = &tls.ConnectionState{PeerCertificates: testCase.Certs} } a := New(testCase.Opts, testCase.User) user, ok, err := a.AuthenticateRequest(req) if testCase.ExpectErr && err == nil { t.Errorf("%s: Expected error, got none", k) continue } if !testCase.ExpectErr && err != nil { t.Errorf("%s: Got unexpected error: %v", k, err) continue } if testCase.ExpectOK != ok { t.Errorf("%s: Expected ok=%v, got %v", k, testCase.ExpectOK, ok) continue } if testCase.ExpectOK { if testCase.ExpectUserName != user.GetName() { t.Errorf("%s: Expected user.name=%v, got %v", k, testCase.ExpectUserName, user.GetName()) } groups := user.GetGroups() sort.Strings(testCase.ExpectGroups) sort.Strings(groups) if !reflect.DeepEqual(testCase.ExpectGroups, groups) { t.Errorf("%s: Expected user.groups=%v, got %v", k, testCase.ExpectGroups, groups) } } } }