From 2306149009e497ff302855ff9a94f85836632d2b Mon Sep 17 00:00:00 2001 From: Axel Christ Date: Wed, 6 Oct 2021 16:38:36 +0200 Subject: [PATCH] Implement api server client ca authentication --- .../v1alpha1/kubeapiserver_types.go | 15 +++++++-- .../v1alpha1/zz_generated.deepcopy.go | 5 +++ .../matryoshka.onmetal.de_kubeapiservers.yaml | 15 +++++++++ .../internal/kubeapiserver/kubeapiserver.go | 32 +++++++++++++++++++ 4 files changed, 64 insertions(+), 3 deletions(-) diff --git a/apis/matryoshka/v1alpha1/kubeapiserver_types.go b/apis/matryoshka/v1alpha1/kubeapiserver_types.go index a088b84..da86849 100644 --- a/apis/matryoshka/v1alpha1/kubeapiserver_types.go +++ b/apis/matryoshka/v1alpha1/kubeapiserver_types.go @@ -110,9 +110,14 @@ type KubeAPIServerETCDKey struct { Secret corev1.LocalObjectReference `json:"secret"` } -// DefaultKubeAPIServerAuthenticationTokenSecretKey is the default key to look up for tokens when the key in -// KubeAPIServerAuthentication is blank. -const DefaultKubeAPIServerAuthenticationTokenSecretKey = "token.csv" +const ( + // DefaultKubeAPIServerAuthenticationTokenSecretKey is the default key to look up for tokens when the key in + // KubeAPIServerAuthentication is blank. + DefaultKubeAPIServerAuthenticationTokenSecretKey = "token.csv" + // DefaultKubeAPIServerAuthenticationClientCertificateSecretKey is the default key to look up for client + // certificates when the key in KubeAPIServerAuthentication.ClientCertificateSecret is blank. + DefaultKubeAPIServerAuthenticationClientCertificateSecretKey = "ca.crt" +) // KubeAPIServerAuthentication specifies how users may authenticate to the api server. type KubeAPIServerAuthentication struct { @@ -122,6 +127,10 @@ type KubeAPIServerAuthentication struct { Anonymous bool `json:"anonymous,omitempty"` // TokenSecret specifies whether token authentication is enabled and where these tokens are located at. TokenSecret *SecretSelector `json:"tokenSecret,omitempty"` + // ClientCertificateSecret makes any request presenting a client certificate signed by one of the authorities in + // the client-ca-file to be authenticated with an identity corresponding to the CommonName of the + // client certificate. + ClientCertificateSecret *SecretSelector `json:"clientCertificateSecret,omitempty"` } // KubeAPIServerSecureServing specifies where tls configuration for the api server is found. diff --git a/apis/matryoshka/v1alpha1/zz_generated.deepcopy.go b/apis/matryoshka/v1alpha1/zz_generated.deepcopy.go index 05e9b1f..e6c1b35 100644 --- a/apis/matryoshka/v1alpha1/zz_generated.deepcopy.go +++ b/apis/matryoshka/v1alpha1/zz_generated.deepcopy.go @@ -139,6 +139,11 @@ func (in *KubeAPIServerAuthentication) DeepCopyInto(out *KubeAPIServerAuthentica *out = new(SecretSelector) **out = **in } + if in.ClientCertificateSecret != nil { + in, out := &in.ClientCertificateSecret, &out.ClientCertificateSecret + *out = new(SecretSelector) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeAPIServerAuthentication. diff --git a/config/crd/bases/matryoshka.onmetal.de_kubeapiservers.yaml b/config/crd/bases/matryoshka.onmetal.de_kubeapiservers.yaml index c48af2f..c91bcdb 100644 --- a/config/crd/bases/matryoshka.onmetal.de_kubeapiservers.yaml +++ b/config/crd/bases/matryoshka.onmetal.de_kubeapiservers.yaml @@ -64,6 +64,21 @@ spec: description: BootstrapToken specifies whether bootstrap token authentication is enabled. type: boolean + clientCertificateSecret: + description: ClientCertificateSecret makes any request presenting + a client certificate signed by one of the authorities in the + client-ca-file to be authenticated with an identity corresponding + to the CommonName of the client certificate. + properties: + key: + description: Key is the key to look up in the config map data. + Some types use a default if this value is unset. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object tokenSecret: description: TokenSecret specifies whether token authentication is enabled and where these tokens are located at. diff --git a/controllers/matryoshka/internal/kubeapiserver/kubeapiserver.go b/controllers/matryoshka/internal/kubeapiserver/kubeapiserver.go index f53564e..749b984 100644 --- a/controllers/matryoshka/internal/kubeapiserver/kubeapiserver.go +++ b/controllers/matryoshka/internal/kubeapiserver/kubeapiserver.go @@ -74,6 +74,13 @@ const ( // TokenVolumePath is the path of the token volume. TokenVolumePath = PathPrefix + TokenName + // ClientCAName is the name used for the client ca volume name and path. + ClientCAName = "client-ca" + // ClientCAVolumeName is the name of the client ca volume. + ClientCAVolumeName = VolumePrefix + ClientCAName + // ClientCAVolumePath is the path of the client ca volume. + ClientCAVolumePath = PathPrefix + ClientCAName + // ETCDCAName is the name used for the etcd ca volume name and path. ETCDCAName = "etcd-ca" // ETCDCAVolumeName is the name of the etcd ca volume. @@ -126,6 +133,12 @@ func (r *Resolver) getRequests(server *matryoshkav1alpha1.KubeAPIServer) *client Object: &corev1.Secret{}, }) } + if clientCA := server.Spec.Authentication.ClientCertificateSecret; clientCA != nil { + s.Insert(clientutils.GetRequest{ + Key: client.ObjectKey{Namespace: server.Namespace, Name: clientCA.Name}, + Object: &corev1.Secret{}, + }) + } if tls := server.Spec.SecureServing; tls != nil { s.Insert(clientutils.GetRequest{ @@ -173,6 +186,14 @@ func (r *Resolver) apiServerVolumes(server *matryoshkav1alpha1.KubeAPIServer) [] }, }) } + if clientCA := server.Spec.Authentication.ClientCertificateSecret; clientCA != nil { + volumes = append(volumes, corev1.Volume{ + Name: ClientCAVolumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{SecretName: clientCA.Name}, + }, + }) + } if etcdCA := server.Spec.ETCD.CertificateAuthoritySecret; etcdCA != nil { volumes = append(volumes, corev1.Volume{ Name: ETCDCAVolumeName, @@ -214,6 +235,12 @@ func (r *Resolver) apiServerVolumeMounts(server *matryoshkav1alpha1.KubeAPIServe MountPath: TokenVolumePath, }) } + if clientCA := server.Spec.Authentication.ClientCertificateSecret; clientCA != nil { + mounts = append(mounts, corev1.VolumeMount{ + Name: ClientCAVolumeName, + MountPath: ClientCAVolumePath, + }) + } if etcdCA := server.Spec.ETCD.CertificateAuthoritySecret; etcdCA != nil { mounts = append(mounts, corev1.VolumeMount{ Name: ETCDCAVolumeName, @@ -262,6 +289,11 @@ func (r *Resolver) apiServerCommand(server *matryoshkav1alpha1.KubeAPIServer) [] fmt.Sprintf("--token-auth-file=%s/%s", TokenVolumePath, utils.StringOrDefault(tokenSecret.Key, matryoshkav1alpha1.DefaultKubeAPIServerAuthenticationTokenSecretKey)), ) } + if clientCA := server.Spec.Authentication.ClientCertificateSecret; clientCA != nil { + cmd = append(cmd, + fmt.Sprintf("--client-ca-file=%s/%s", ClientCAVolumePath, utils.StringOrDefault(clientCA.Key, matryoshkav1alpha1.DefaultKubeAPIServerAuthenticationClientCertificateSecretKey)), + ) + } if etcdCA := server.Spec.ETCD.CertificateAuthoritySecret; etcdCA != nil { cmd = append(cmd, fmt.Sprintf("--etcd-cafile=%s/%s", ETCDCAVolumePath, utils.StringOrDefault(etcdCA.Key, matryoshkav1alpha1.DefaultKubeAPIServerETCDCertificateAuthoritySecretKey)),