Skip to content

Commit

Permalink
Merge pull request #67545 from sttts/sttts-auth-optional-kubeconfig
Browse files Browse the repository at this point in the history
Automatic merge from submit-queue (batch tested with PRs 66960, 67545). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

delegated authn/z: optionally opt-out of mandatory authn/authz kubeconfig

This adds `RemoteKubeConfigFileOptional` field to the delegated authn/z option structs. If set to true, the authn/z kubeconfig file flags are optional. If no kubeconfig is given, all token requests are considered to be anonymous and no client CA is looked up in the cluster.

Prerequisite for kubernetes/kubernetes#64149 and kubernetes/kubernetes#67069.

Kubernetes-commit: 1b3a2dd0830ca0e02d5b95d2ecc0161d0c93a0c7
  • Loading branch information
k8s-publishing-bot committed Sep 6, 2018
2 parents b8485df + 755b671 commit 8f4cb16
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 17 deletions.
1 change: 1 addition & 0 deletions pkg/authentication/authenticatorfactory/delegating.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
type DelegatingAuthenticatorConfig struct {
Anonymous bool

// TokenAccessReviewClient is a client to do token review. It can be nil. Then every token is ignored.
TokenAccessReviewClient authenticationclient.TokenReviewInterface

// CacheTTL is the length of time that a token authentication answer will be cached.
Expand Down
37 changes: 30 additions & 7 deletions pkg/server/options/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ type DelegatingAuthenticationOptions struct {
// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
// TokenAccessReview.authentication.k8s.io endpoint for checking tokens.
RemoteKubeConfigFile string
// RemoteKubeConfigFileOptional is specifying whether not specifying the kubeconfig or
// a missing in-cluster config will be fatal.
RemoteKubeConfigFileOptional bool

// CacheTTL is the length of time that a token authentication answer will be cached.
CacheTTL time.Duration
Expand Down Expand Up @@ -139,9 +142,13 @@ func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
return
}

var optionalKubeConfigSentence string
if s.RemoteKubeConfigFileOptional {
optionalKubeConfigSentence = " This is optional. If empty, all token requests are considered to be anonymous and no client CA is looked up in the cluster."
}
fs.StringVar(&s.RemoteKubeConfigFile, "authentication-kubeconfig", s.RemoteKubeConfigFile, ""+
"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
"tokenaccessreviews.authentication.k8s.io.")
"tokenaccessreviews.authentication.k8s.io."+optionalKubeConfigSentence)

fs.DurationVar(&s.CacheTTL, "authentication-token-webhook-cache-ttl", s.CacheTTL,
"The duration to cache responses from the webhook token authenticator.")
Expand All @@ -152,7 +159,6 @@ func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
fs.BoolVar(&s.SkipInClusterLookup, "authentication-skip-lookup", s.SkipInClusterLookup, ""+
"If false, the authentication-kubeconfig will be used to lookup missing authentication "+
"configuration from the cluster.")

}

func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo, servingInfo *server.SecureServingInfo, openAPIConfig *openapicommon.Config) error {
Expand All @@ -161,15 +167,19 @@ func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo,
return nil
}

cfg := authenticatorfactory.DelegatingAuthenticatorConfig{
Anonymous: true,
CacheTTL: s.CacheTTL,
}

client, err := s.getClient()
if err != nil {
return fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
}

cfg := authenticatorfactory.DelegatingAuthenticatorConfig{
Anonymous: true,
CacheTTL: s.CacheTTL,
TokenAccessReviewClient: client.AuthenticationV1beta1().TokenReviews(),
// configure token review
if client != nil {
cfg.TokenAccessReviewClient = client.AuthenticationV1beta1().TokenReviews()
}

// look into configmaps/external-apiserver-authentication for missing authn info
Expand Down Expand Up @@ -217,6 +227,15 @@ func (s *DelegatingAuthenticationOptions) lookupMissingConfigInCluster(client ku
if len(s.ClientCert.ClientCA) > 0 && len(s.RequestHeader.ClientCAFile) > 0 {
return nil
}
if client == nil {
if len(s.ClientCert.ClientCA) == 0 {
glog.Warningf("No authentication-kubeconfig provided in order to lookup client-ca-file in configmap/%s in %s, so client certificate authentication to extension api-server won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
}
if len(s.RequestHeader.ClientCAFile) == 0 {
glog.Warningf("No authentication-kubeconfig provided in order to lookup requestheader-client-ca-file in configmap/%s in %s, so request-header client certificate authentication to extension api-server won't work.", authenticationConfigMapName, authenticationConfigMapNamespace)
}
return nil
}

authConfigMap, err := client.CoreV1().ConfigMaps(authenticationConfigMapNamespace).Get(authenticationConfigMapName, metav1.GetOptions{})
if err != nil {
Expand Down Expand Up @@ -321,6 +340,8 @@ func deserializeStrings(in string) ([]string, error) {
return ret, nil
}

// getClient returns a Kubernetes clientset. If s.RemoteKubeConfigFileOptional is true, nil will be returned
// if no kubeconfig is specified by the user and the in-cluster config is not found.
func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, error) {
var clientConfig *rest.Config
var err error
Expand All @@ -329,11 +350,13 @@ func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, err
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})

clientConfig, err = loader.ClientConfig()

} else {
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
// use this path
clientConfig, err = rest.InClusterConfig()
if err == rest.ErrNotInCluster && s.RemoteKubeConfigFileOptional {
return nil, nil
}
}
if err != nil {
return nil, fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
Expand Down
35 changes: 25 additions & 10 deletions pkg/server/options/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"time"

"github.com/golang/glog"
"github.com/spf13/pflag"

"k8s.io/apiserver/pkg/authorization/authorizer"
Expand All @@ -41,6 +42,9 @@ type DelegatingAuthorizationOptions struct {
// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
// SubjectAccessReview.authorization.k8s.io endpoint for checking tokens.
RemoteKubeConfigFile string
// RemoteKubeConfigFileOptional is specifying whether not specifying the kubeconfig or
// a missing in-cluster config will be fatal.
RemoteKubeConfigFileOptional bool

// AllowCacheTTL is the length of time that a successful authorization response will be cached
AllowCacheTTL time.Duration
Expand Down Expand Up @@ -72,9 +76,13 @@ func (s *DelegatingAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
return
}

var optionalKubeConfigSentence string
if s.RemoteKubeConfigFileOptional {
optionalKubeConfigSentence = " This is optional. If empty, all requests not skipped by authorization are forbidden."
}
fs.StringVar(&s.RemoteKubeConfigFile, "authorization-kubeconfig", s.RemoteKubeConfigFile,
"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
" subjectaccessreviews.authorization.k8s.io.")
"subjectaccessreviews.authorization.k8s.io."+optionalKubeConfigSentence)

fs.DurationVar(&s.AllowCacheTTL, "authorization-webhook-cache-authorized-ttl",
s.AllowCacheTTL,
Expand Down Expand Up @@ -115,16 +123,20 @@ func (s *DelegatingAuthorizationOptions) toAuthorizer(client kubernetes.Interfac
authorizers = append(authorizers, a)
}

cfg := authorizerfactory.DelegatingAuthorizerConfig{
SubjectAccessReviewClient: client.AuthorizationV1beta1().SubjectAccessReviews(),
AllowCacheTTL: s.AllowCacheTTL,
DenyCacheTTL: s.DenyCacheTTL,
}
a, err := cfg.New()
if err != nil {
return nil, err
if client == nil {
glog.Warningf("No authorization-kubeconfig provided, so SubjectAccessReview of authorization tokens won't work.")
} else {
cfg := authorizerfactory.DelegatingAuthorizerConfig{
SubjectAccessReviewClient: client.AuthorizationV1beta1().SubjectAccessReviews(),
AllowCacheTTL: s.AllowCacheTTL,
DenyCacheTTL: s.DenyCacheTTL,
}
delegatedAuthorizer, err := cfg.New()
if err != nil {
return nil, err
}
authorizers = append(authorizers, delegatedAuthorizer)
}
authorizers = append(authorizers, a)

return union.New(authorizers...), nil
}
Expand All @@ -141,6 +153,9 @@ func (s *DelegatingAuthorizationOptions) getClient() (kubernetes.Interface, erro
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
// use this path
clientConfig, err = rest.InClusterConfig()
if err == rest.ErrNotInCluster && s.RemoteKubeConfigFileOptional {
return nil, nil
}
}
if err != nil {
return nil, fmt.Errorf("failed to get delegated authorization kubeconfig: %v", err)
Expand Down

0 comments on commit 8f4cb16

Please sign in to comment.