mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 22:46:12 +00:00
authn/z: optionally opt-out of mandatory authn/authz kubeconfig
This commit is contained in:
parent
029bb4e213
commit
a671d65673
@ -41,6 +41,7 @@ import (
|
|||||||
type DelegatingAuthenticatorConfig struct {
|
type DelegatingAuthenticatorConfig struct {
|
||||||
Anonymous bool
|
Anonymous bool
|
||||||
|
|
||||||
|
// TokenAccessReviewClient is a client to do token review. It can be nil. Then every token is ignored.
|
||||||
TokenAccessReviewClient authenticationclient.TokenReviewInterface
|
TokenAccessReviewClient authenticationclient.TokenReviewInterface
|
||||||
|
|
||||||
// CacheTTL is the length of time that a token authentication answer will be cached.
|
// CacheTTL is the length of time that a token authentication answer will be cached.
|
||||||
|
@ -106,6 +106,9 @@ type DelegatingAuthenticationOptions struct {
|
|||||||
// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
|
// 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.
|
// TokenAccessReview.authentication.k8s.io endpoint for checking tokens.
|
||||||
RemoteKubeConfigFile string
|
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 is the length of time that a token authentication answer will be cached.
|
||||||
CacheTTL time.Duration
|
CacheTTL time.Duration
|
||||||
@ -139,9 +142,13 @@ func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
|||||||
return
|
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, ""+
|
fs.StringVar(&s.RemoteKubeConfigFile, "authentication-kubeconfig", s.RemoteKubeConfigFile, ""+
|
||||||
"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
|
"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,
|
fs.DurationVar(&s.CacheTTL, "authentication-token-webhook-cache-ttl", s.CacheTTL,
|
||||||
"The duration to cache responses from the webhook token authenticator.")
|
"The duration to cache responses from the webhook token authenticator.")
|
||||||
@ -152,7 +159,6 @@ func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
|||||||
fs.BoolVar(&s.SkipInClusterLookup, "authentication-skip-lookup", s.SkipInClusterLookup, ""+
|
fs.BoolVar(&s.SkipInClusterLookup, "authentication-skip-lookup", s.SkipInClusterLookup, ""+
|
||||||
"If false, the authentication-kubeconfig will be used to lookup missing authentication "+
|
"If false, the authentication-kubeconfig will be used to lookup missing authentication "+
|
||||||
"configuration from the cluster.")
|
"configuration from the cluster.")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo, servingInfo *server.SecureServingInfo, openAPIConfig *openapicommon.Config) error {
|
func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo, servingInfo *server.SecureServingInfo, openAPIConfig *openapicommon.Config) error {
|
||||||
@ -161,15 +167,19 @@ func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.AuthenticationInfo,
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg := authenticatorfactory.DelegatingAuthenticatorConfig{
|
||||||
|
Anonymous: true,
|
||||||
|
CacheTTL: s.CacheTTL,
|
||||||
|
}
|
||||||
|
|
||||||
client, err := s.getClient()
|
client, err := s.getClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
|
return fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := authenticatorfactory.DelegatingAuthenticatorConfig{
|
// configure token review
|
||||||
Anonymous: true,
|
if client != nil {
|
||||||
CacheTTL: s.CacheTTL,
|
cfg.TokenAccessReviewClient = client.AuthenticationV1beta1().TokenReviews()
|
||||||
TokenAccessReviewClient: client.AuthenticationV1beta1().TokenReviews(),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// look into configmaps/external-apiserver-authentication for missing authn info
|
// look into configmaps/external-apiserver-authentication for missing authn info
|
||||||
@ -217,6 +227,15 @@ func (s *DelegatingAuthenticationOptions) lookupMissingConfigInCluster(client ku
|
|||||||
if len(s.ClientCert.ClientCA) > 0 && len(s.RequestHeader.ClientCAFile) > 0 {
|
if len(s.ClientCert.ClientCA) > 0 && len(s.RequestHeader.ClientCAFile) > 0 {
|
||||||
return nil
|
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{})
|
authConfigMap, err := client.CoreV1().ConfigMaps(authenticationConfigMapNamespace).Get(authenticationConfigMapName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -321,6 +340,8 @@ func deserializeStrings(in string) ([]string, error) {
|
|||||||
return ret, nil
|
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) {
|
func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, error) {
|
||||||
var clientConfig *rest.Config
|
var clientConfig *rest.Config
|
||||||
var err error
|
var err error
|
||||||
@ -329,11 +350,13 @@ func (s *DelegatingAuthenticationOptions) getClient() (kubernetes.Interface, err
|
|||||||
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
|
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
|
||||||
|
|
||||||
clientConfig, err = loader.ClientConfig()
|
clientConfig, err = loader.ClientConfig()
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
|
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
|
||||||
// use this path
|
// use this path
|
||||||
clientConfig, err = rest.InClusterConfig()
|
clientConfig, err = rest.InClusterConfig()
|
||||||
|
if err == rest.ErrNotInCluster && s.RemoteKubeConfigFileOptional {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
|
return nil, fmt.Errorf("failed to get delegated authentication kubeconfig: %v", err)
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/glog"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||||
@ -41,6 +42,9 @@ type DelegatingAuthorizationOptions struct {
|
|||||||
// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
|
// 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.
|
// SubjectAccessReview.authorization.k8s.io endpoint for checking tokens.
|
||||||
RemoteKubeConfigFile string
|
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 is the length of time that a successful authorization response will be cached
|
||||||
AllowCacheTTL time.Duration
|
AllowCacheTTL time.Duration
|
||||||
@ -72,9 +76,13 @@ func (s *DelegatingAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
|
|||||||
return
|
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,
|
fs.StringVar(&s.RemoteKubeConfigFile, "authorization-kubeconfig", s.RemoteKubeConfigFile,
|
||||||
"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
|
"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",
|
fs.DurationVar(&s.AllowCacheTTL, "authorization-webhook-cache-authorized-ttl",
|
||||||
s.AllowCacheTTL,
|
s.AllowCacheTTL,
|
||||||
@ -115,16 +123,20 @@ func (s *DelegatingAuthorizationOptions) toAuthorizer(client kubernetes.Interfac
|
|||||||
authorizers = append(authorizers, a)
|
authorizers = append(authorizers, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := authorizerfactory.DelegatingAuthorizerConfig{
|
if client == nil {
|
||||||
SubjectAccessReviewClient: client.AuthorizationV1beta1().SubjectAccessReviews(),
|
glog.Warningf("No authorization-kubeconfig provided, so SubjectAccessReview of authorization tokens won't work.")
|
||||||
AllowCacheTTL: s.AllowCacheTTL,
|
} else {
|
||||||
DenyCacheTTL: s.DenyCacheTTL,
|
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)
|
||||||
}
|
}
|
||||||
a, err := cfg.New()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
authorizers = append(authorizers, a)
|
|
||||||
|
|
||||||
return union.New(authorizers...), nil
|
return union.New(authorizers...), nil
|
||||||
}
|
}
|
||||||
@ -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
|
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
|
||||||
// use this path
|
// use this path
|
||||||
clientConfig, err = rest.InClusterConfig()
|
clientConfig, err = rest.InClusterConfig()
|
||||||
|
if err == rest.ErrNotInCluster && s.RemoteKubeConfigFileOptional {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get delegated authorization kubeconfig: %v", err)
|
return nil, fmt.Errorf("failed to get delegated authorization kubeconfig: %v", err)
|
||||||
|
@ -18,6 +18,7 @@ package rest
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
@ -44,6 +45,8 @@ const (
|
|||||||
DefaultBurst int = 10
|
DefaultBurst int = 10
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var ErrNotInCluster = errors.New("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
|
||||||
|
|
||||||
// Config holds the common attributes that can be passed to a Kubernetes client on
|
// Config holds the common attributes that can be passed to a Kubernetes client on
|
||||||
// initialization.
|
// initialization.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@ -308,12 +311,12 @@ func DefaultKubernetesUserAgent() string {
|
|||||||
|
|
||||||
// InClusterConfig returns a config object which uses the service account
|
// InClusterConfig returns a config object which uses the service account
|
||||||
// kubernetes gives to pods. It's intended for clients that expect to be
|
// kubernetes gives to pods. It's intended for clients that expect to be
|
||||||
// running inside a pod running on kubernetes. It will return an error if
|
// running inside a pod running on kubernetes. It will return ErrNotInCluster
|
||||||
// called from a process not running in a kubernetes environment.
|
// if called from a process not running in a kubernetes environment.
|
||||||
func InClusterConfig() (*Config, error) {
|
func InClusterConfig() (*Config, error) {
|
||||||
host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
|
host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
|
||||||
if len(host) == 0 || len(port) == 0 {
|
if len(host) == 0 || len(port) == 0 {
|
||||||
return nil, fmt.Errorf("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
|
return nil, ErrNotInCluster
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
|
token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/token")
|
||||||
|
Loading…
Reference in New Issue
Block a user