// newServiceAccountAuthenticator returns an authenticator.Request or an error func newServiceAccountAuthenticator(keyfile string, lookup bool, serviceAccountGetter serviceaccount.ServiceAccountTokenGetter) (authenticator.Request, error) { publicKey, err := serviceaccount.ReadPublicKey(keyfile) if err != nil { return nil, err } tokenAuthenticator := serviceaccount.JWTTokenAuthenticator([]*rsa.PublicKey{publicKey}, lookup, serviceAccountGetter) return bearertoken.New(tokenAuthenticator), nil }
func newAuthenticator(config configapi.MasterConfig, restOptionsGetter restoptions.Getter, tokenGetter serviceaccount.ServiceAccountTokenGetter, apiClientCAs *x509.CertPool, groupMapper identitymapper.UserToGroupMapper) (authenticator.Request, error) { authenticators := []authenticator.Request{} // ServiceAccount token if len(config.ServiceAccountConfig.PublicKeyFiles) > 0 { publicKeys := []*rsa.PublicKey{} for _, keyFile := range config.ServiceAccountConfig.PublicKeyFiles { publicKey, err := serviceaccount.ReadPublicKey(keyFile) if err != nil { return nil, fmt.Errorf("Error reading service account key file %s: %v", keyFile, err) } publicKeys = append(publicKeys, publicKey) } tokenAuthenticator := serviceaccount.JWTTokenAuthenticator(publicKeys, true, tokenGetter) authenticators = append(authenticators, bearertoken.New(tokenAuthenticator, true)) } // OAuth token if config.OAuthConfig != nil { tokenAuthenticator, err := getEtcdTokenAuthenticator(restOptionsGetter, groupMapper) if err != nil { return nil, fmt.Errorf("Error building OAuth token authenticator: %v", err) } tokenRequestAuthenticators := []authenticator.Request{ bearertoken.New(tokenAuthenticator, true), // Allow token as access_token param for WebSockets paramtoken.New("access_token", tokenAuthenticator, true), } authenticators = append(authenticators, // if you have a bearer token, you're a human (usually) // if you change this, have a look at the impersonationFilter where we attach groups to the impersonated user group.NewGroupAdder(unionrequest.NewUnionAuthentication(tokenRequestAuthenticators...), []string{bootstrappolicy.AuthenticatedOAuthGroup})) } if configapi.UseTLS(config.ServingInfo.ServingInfo) { // build cert authenticator // TODO: add "system:" prefix in authenticator, limit cert to username // TODO: add "system:" prefix to groups in authenticator, limit cert to group name opts := x509request.DefaultVerifyOptions() opts.Roots = apiClientCAs certauth := x509request.New(opts, x509request.SubjectToUserConversion) authenticators = append(authenticators, certauth) } ret := &unionrequest.Authenticator{ FailOnError: true, Handlers: []authenticator.Request{ // if you change this, have a look at the impersonationFilter where we attach groups to the impersonated user group.NewGroupAdder(unionrequest.NewUnionAuthentication(authenticators...), []string{bootstrappolicy.AuthenticatedGroup}), anonymous.NewAuthenticator(), }, } return ret, nil }
func ValidateServiceAccountConfig(config api.ServiceAccountConfig, builtInKubernetes bool, fldPath *field.Path) ValidationResults { validationResults := ValidationResults{} managedNames := sets.NewString(config.ManagedNames...) managedNamesPath := fldPath.Child("managedNames") if !managedNames.Has(bootstrappolicy.BuilderServiceAccountName) { validationResults.AddWarnings(field.Invalid(managedNamesPath, "", fmt.Sprintf("missing %q, which will require manual creation in each namespace before builds can run", bootstrappolicy.BuilderServiceAccountName))) } if !managedNames.Has(bootstrappolicy.DeployerServiceAccountName) { validationResults.AddWarnings(field.Invalid(managedNamesPath, "", fmt.Sprintf("missing %q, which will require manual creation in each namespace before deployments can run", bootstrappolicy.DeployerServiceAccountName))) } if builtInKubernetes && !managedNames.Has(bootstrappolicy.DefaultServiceAccountName) { validationResults.AddWarnings(field.Invalid(managedNamesPath, "", fmt.Sprintf("missing %q, which will prevent creation of pods that do not specify a valid service account", bootstrappolicy.DefaultServiceAccountName))) } for i, name := range config.ManagedNames { if reasons := kvalidation.ValidateServiceAccountName(name, false); len(reasons) != 0 { validationResults.AddErrors(field.Invalid(managedNamesPath.Index(i), name, strings.Join(reasons, ", "))) } } if len(config.PrivateKeyFile) > 0 { privateKeyFilePath := fldPath.Child("privateKeyFile") if fileErrs := ValidateFile(config.PrivateKeyFile, privateKeyFilePath); len(fileErrs) > 0 { validationResults.AddErrors(fileErrs...) } else if privateKey, err := serviceaccount.ReadPrivateKey(config.PrivateKeyFile); err != nil { validationResults.AddErrors(field.Invalid(privateKeyFilePath, config.PrivateKeyFile, err.Error())) } else if err := privateKey.Validate(); err != nil { validationResults.AddErrors(field.Invalid(privateKeyFilePath, config.PrivateKeyFile, err.Error())) } } else if builtInKubernetes { validationResults.AddWarnings(field.Invalid(fldPath.Child("privateKeyFile"), "", "no service account tokens will be generated, which could prevent builds and deployments from working")) } if len(config.PublicKeyFiles) == 0 { validationResults.AddWarnings(field.Invalid(fldPath.Child("publicKeyFiles"), "", "no service account tokens will be accepted by the API, which will prevent builds and deployments from working")) } for i, publicKeyFile := range config.PublicKeyFiles { idxPath := fldPath.Child("publicKeyFiles").Index(i) if fileErrs := ValidateFile(publicKeyFile, idxPath); len(fileErrs) > 0 { validationResults.AddErrors(fileErrs...) } else if _, err := serviceaccount.ReadPublicKey(publicKeyFile); err != nil { validationResults.AddErrors(field.Invalid(idxPath, publicKeyFile, err.Error())) } } if len(config.MasterCA) > 0 { validationResults.AddErrors(ValidateFile(config.MasterCA, fldPath.Child("masterCA"))...) } else if builtInKubernetes { validationResults.AddWarnings(field.Invalid(fldPath.Child("masterCA"), "", "master CA information will not be automatically injected into pods, which will prevent verification of the API server from inside a pod")) } return validationResults }
func TestReadPublicKey(t *testing.T) { f, err := ioutil.TempFile("", "") if err != nil { t.Fatalf("error creating tmpfile: %v", err) } defer os.Remove(f.Name()) if err := ioutil.WriteFile(f.Name(), []byte(rsaPublicKey), os.FileMode(0600)); err != nil { t.Fatalf("error writing public key to tmpfile: %v", err) } if _, err := serviceaccount.ReadPublicKey(f.Name()); err != nil { t.Fatalf("error reading RSA public key: %v", err) } if err := ioutil.WriteFile(f.Name(), []byte(ecdsaPublicKey), os.FileMode(0600)); err != nil { t.Fatalf("error writing public key to tmpfile: %v", err) } if _, err := serviceaccount.ReadPublicKey(f.Name()); err != nil { t.Fatalf("error reading ECDSA public key: %v", err) } }
func newAuthenticator(config configapi.MasterConfig, etcdHelper storage.Interface, tokenGetter serviceaccount.ServiceAccountTokenGetter, apiClientCAs *x509.CertPool, groupMapper identitymapper.UserToGroupMapper) authenticator.Request { authenticators := []authenticator.Request{} // ServiceAccount token if len(config.ServiceAccountConfig.PublicKeyFiles) > 0 { publicKeys := []*rsa.PublicKey{} for _, keyFile := range config.ServiceAccountConfig.PublicKeyFiles { publicKey, err := serviceaccount.ReadPublicKey(keyFile) if err != nil { glog.Fatalf("Error reading service account key file %s: %v", keyFile, err) } publicKeys = append(publicKeys, publicKey) } tokenAuthenticator := serviceaccount.JWTTokenAuthenticator(publicKeys, true, tokenGetter) authenticators = append(authenticators, bearertoken.New(tokenAuthenticator, true)) } // OAuth token if config.OAuthConfig != nil { tokenAuthenticator := getEtcdTokenAuthenticator(etcdHelper, groupMapper) authenticators = append(authenticators, bearertoken.New(tokenAuthenticator, true)) // Allow token as access_token param for WebSockets authenticators = append(authenticators, paramtoken.New("access_token", tokenAuthenticator, true)) } if configapi.UseTLS(config.ServingInfo.ServingInfo) { // build cert authenticator // TODO: add "system:" prefix in authenticator, limit cert to username // TODO: add "system:" prefix to groups in authenticator, limit cert to group name opts := x509request.DefaultVerifyOptions() opts.Roots = apiClientCAs certauth := x509request.New(opts, x509request.SubjectToUserConversion) authenticators = append(authenticators, certauth) } // TODO: make anonymous auth optional? ret := &unionrequest.Authenticator{ FailOnError: true, Handlers: []authenticator.Request{ group.NewGroupAdder(unionrequest.NewUnionAuthentication(authenticators...), []string{bootstrappolicy.AuthenticatedGroup}), authenticator.RequestFunc(func(req *http.Request) (user.Info, bool, error) { return &user.DefaultInfo{Name: unauthenticatedUsername, Groups: []string{bootstrappolicy.UnauthenticatedGroup}}, true, nil }), }, } return ret }
// IsValidServiceAccountKeyFile returns true if a valid public RSA key can be read from the given file func IsValidServiceAccountKeyFile(file string) bool { _, err := serviceaccount.ReadPublicKey(file) return err == nil }