diff --git a/services/apiserver.go b/services/apiserver.go index 35a2eee4be67eba8de7e70f3a9e229d085f58412..42072d8e210dc38755af0ce9fc442a4a0c2955da 100644 --- a/services/apiserver.go +++ b/services/apiserver.go @@ -20,6 +20,8 @@ import ( "k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy" "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating" "k8s.io/apiserver/pkg/admission/plugin/webhook/validating" + "k8s.io/apiserver/pkg/authentication/authenticatorfactory" + "k8s.io/apiserver/pkg/authentication/request/headerrequest" "k8s.io/apiserver/pkg/cel/openapi/resolver" "k8s.io/apiserver/pkg/endpoints/discovery/aggregated" apifilters "k8s.io/apiserver/pkg/endpoints/filters" @@ -87,7 +89,7 @@ var kubeApiserver = &Unit{ if err != nil { return fmt.Errorf("could not initialize generic apiserver: %w", err) } - aggregatorConfig, _ := buildAggregatorConfig(*config, clients) + aggregatorConfig, _ := buildAggregatorConfig(c, *config, clients) aggregatorServer, err := aggregatorConfig.Complete().NewWithDelegate(apiServer.GenericAPIServer) if err != nil { return fmt.Errorf("could not initialize aggregator: %w", err) @@ -173,10 +175,17 @@ func buildConfig(c *Cluster) (config *server.Config, clients *k8s.Clients, err e err = fmt.Errorf("could not get api CA file: %w", err) return } + proxyCA, err := dynamiccertificates.NewDynamicCAContentFromFile("proxy-ca", c.pki.Proxy.CertPath()) + if err != nil { + err = fmt.Errorf("could not get proxy CA file: %w", err) + return + } config.SecureServing = &server.SecureServingInfo{ Listener: listener, Cert: cert, - ClientCA: clientCA, // not setup upstream, might be an issue + // This is performed in vanilla when applying authentication configuration, + // especially in AuthenticationInfo.ApplyClientCet + ClientCA: dynamiccertificates.NewUnionCAContentProvider(clientCA, proxyCA), } config.PublicAddress = c.networking.NodeAddress.Addr().AsSlice() @@ -201,6 +210,15 @@ func buildConfig(c *Cluster) (config *server.Config, clients *k8s.Clients, err e clients.Informer.Core().V1().Pods().Lister(), ), SecretsWriter: clients.Client.CoreV1(), + // This is currently not strictly required, since we do not use proxified + // requests to apiservers themselves, though we might at some point. Private + // key is only delivered to apiserver itself, so little harm is done + RequestHeaderConfig: &authenticatorfactory.RequestHeaderConfig{ + UsernameHeaders: headerrequest.StaticStringSlice([]string{"X-Remote-User"}), + GroupHeaders: headerrequest.StaticStringSlice([]string{"X-Remote-Group"}), + ExtraHeaderPrefixes: headerrequest.StaticStringSlice([]string{"X-Remote-Extra"}), + CAContentProvider: proxyCA, + }, } auth, _, err := authConfig.New() if err != nil { @@ -208,8 +226,9 @@ func buildConfig(c *Cluster) (config *server.Config, clients *k8s.Clients, err e return } config.Authentication = server.AuthenticationInfo{ - APIAudiences: []string{audience}, - Authenticator: auth, + APIAudiences: []string{audience}, + Authenticator: auth, + RequestHeaderConfig: authConfig.RequestHeaderConfig, } // Setup authorizations @@ -347,14 +366,22 @@ func buildApiConfig(c *Cluster, config server.Config, clients *k8s.Clients) (*co APIResourceConfigSource: generic.MergedResourceConfig, StorageFactory: restOptionsGetter.StorageFactory, ClusterAuthenticationInfo: clusterauthenticationtrust.ClusterAuthenticationInfo{ - ClientCA: config.SecureServing.ClientCA, + // This is duplicated information from the authentication layer, so that + // the start-cluster-authentication-info-controller controller properly + // populates the extension-apiserver-authentication ConfigMap with + // authentication info + ClientCA: config.SecureServing.ClientCA, + RequestHeaderCA: config.Authentication.RequestHeaderConfig.CAContentProvider, + RequestHeaderUsernameHeaders: config.Authentication.RequestHeaderConfig.UsernameHeaders, + RequestHeaderGroupHeaders: config.Authentication.RequestHeaderConfig.GroupHeaders, + RequestHeaderExtraHeaderPrefixes: config.Authentication.RequestHeaderConfig.ExtraHeaderPrefixes, }, }, }, nil } // Customize the generic config then build an aggregator config -func buildAggregatorConfig(config server.Config, clients *k8s.Clients) (*aggregator.Config, error) { +func buildAggregatorConfig(c *Cluster, config server.Config, clients *k8s.Clients) (*aggregator.Config, error) { generic := config generic.MergedResourceConfig = aggregator.DefaultAPIResourceConfigSource() generic.RESTOptionsGetter = k8s.PrepareStorage(aggregatorscheme.Codecs, aggregatorscheme.Scheme, generic.MergedResourceConfig) @@ -365,6 +392,11 @@ func buildAggregatorConfig(config server.Config, clients *k8s.Clients) (*aggrega }, ExtraConfig: aggregator.ExtraConfig{ ServiceResolver: clients.ServiceResolver(), + // This is for the aggregation layer to authenticate proxified + // requests to webhooks and other aggregated services using a dedicated + // certificate and certificate authority + ProxyClientCertFile: c.masterCerts.Proxy.CertPath(), + ProxyClientKeyFile: c.masterCerts.Proxy.KeyPath(), }, }, nil }