mirror of
https://github.com/rancher/rke.git
synced 2025-08-24 09:28:29 +00:00
Allow multiple authn strategies to be defined, including new 'webhook' strategy. Webhook strategy configuration contains the contents of the authentication webhook file as well as the cache timeout period. This change allows a Kubernetes API Auth service to authenticate user requests without proxying through the Rancher server.
206 lines
8.4 KiB
Go
206 lines
8.4 KiB
Go
package cluster
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rsa"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/rancher/rke/hosts"
|
|
"github.com/rancher/rke/k8s"
|
|
"github.com/rancher/rke/log"
|
|
"github.com/rancher/rke/pki"
|
|
"github.com/rancher/rke/services"
|
|
"github.com/sirupsen/logrus"
|
|
"k8s.io/client-go/util/cert"
|
|
)
|
|
|
|
func SetUpAuthentication(ctx context.Context, kubeCluster, currentCluster *Cluster, fullState *FullState) error {
|
|
if kubeCluster.AuthnStrategies[AuthnX509Provider] {
|
|
kubeCluster.Certificates = fullState.DesiredState.CertificatesBundle
|
|
return nil
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func regenerateAPICertificate(c *Cluster, certificates map[string]pki.CertificatePKI) (map[string]pki.CertificatePKI, error) {
|
|
logrus.Debugf("[certificates] Regenerating kubeAPI certificate")
|
|
kubeAPIAltNames := pki.GetAltNames(c.ControlPlaneHosts, c.ClusterDomain, c.KubernetesServiceIP, c.Authentication.SANs)
|
|
caCrt := certificates[pki.CACertName].Certificate
|
|
caKey := certificates[pki.CACertName].Key
|
|
kubeAPIKey := certificates[pki.KubeAPICertName].Key
|
|
kubeAPICert, _, err := pki.GenerateSignedCertAndKey(caCrt, caKey, true, pki.KubeAPICertName, kubeAPIAltNames, kubeAPIKey, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
certificates[pki.KubeAPICertName] = pki.ToCertObject(pki.KubeAPICertName, "", "", kubeAPICert, kubeAPIKey)
|
|
return certificates, nil
|
|
}
|
|
|
|
func GetClusterCertsFromKubernetes(ctx context.Context, kubeCluster *Cluster) (map[string]pki.CertificatePKI, error) {
|
|
log.Infof(ctx, "[certificates] Getting Cluster certificates from Kubernetes")
|
|
|
|
k8sClient, err := k8s.NewClient(kubeCluster.LocalKubeConfigPath, kubeCluster.K8sWrapTransport)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to create Kubernetes Client: %v", err)
|
|
}
|
|
certificatesNames := []string{
|
|
pki.CACertName,
|
|
pki.KubeAPICertName,
|
|
pki.KubeNodeCertName,
|
|
pki.KubeProxyCertName,
|
|
pki.KubeControllerCertName,
|
|
pki.KubeSchedulerCertName,
|
|
pki.KubeAdminCertName,
|
|
pki.APIProxyClientCertName,
|
|
pki.RequestHeaderCACertName,
|
|
pki.ServiceAccountTokenKeyName,
|
|
}
|
|
|
|
for _, etcdHost := range kubeCluster.EtcdHosts {
|
|
etcdName := pki.GetEtcdCrtName(etcdHost.InternalAddress)
|
|
certificatesNames = append(certificatesNames, etcdName)
|
|
}
|
|
|
|
certMap := make(map[string]pki.CertificatePKI)
|
|
for _, certName := range certificatesNames {
|
|
secret, err := k8s.GetSecret(k8sClient, certName)
|
|
if err != nil && !strings.HasPrefix(certName, "kube-etcd") &&
|
|
!strings.Contains(certName, pki.RequestHeaderCACertName) &&
|
|
!strings.Contains(certName, pki.APIProxyClientCertName) &&
|
|
!strings.Contains(certName, pki.ServiceAccountTokenKeyName) {
|
|
return nil, err
|
|
}
|
|
// If I can't find an etcd, requestheader, or proxy client cert, I will not fail and will create it later.
|
|
if (secret == nil || secret.Data == nil) &&
|
|
(strings.HasPrefix(certName, "kube-etcd") ||
|
|
strings.Contains(certName, pki.RequestHeaderCACertName) ||
|
|
strings.Contains(certName, pki.APIProxyClientCertName) ||
|
|
strings.Contains(certName, pki.ServiceAccountTokenKeyName)) {
|
|
certMap[certName] = pki.CertificatePKI{}
|
|
continue
|
|
}
|
|
|
|
secretCert, err := cert.ParseCertsPEM(secret.Data["Certificate"])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to parse certificate of %s: %v", certName, err)
|
|
}
|
|
secretKey, err := cert.ParsePrivateKeyPEM(secret.Data["Key"])
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Failed to parse private key of %s: %v", certName, err)
|
|
}
|
|
secretConfig := string(secret.Data["Config"])
|
|
if len(secretCert) == 0 || secretKey == nil {
|
|
return nil, fmt.Errorf("certificate or key of %s is not found", certName)
|
|
}
|
|
certificatePEM := string(cert.EncodeCertPEM(secretCert[0]))
|
|
keyPEM := string(cert.EncodePrivateKeyPEM(secretKey.(*rsa.PrivateKey)))
|
|
|
|
certMap[certName] = pki.CertificatePKI{
|
|
Certificate: secretCert[0],
|
|
Key: secretKey.(*rsa.PrivateKey),
|
|
CertificatePEM: certificatePEM,
|
|
KeyPEM: keyPEM,
|
|
Config: secretConfig,
|
|
EnvName: string(secret.Data["EnvName"]),
|
|
ConfigEnvName: string(secret.Data["ConfigEnvName"]),
|
|
KeyEnvName: string(secret.Data["KeyEnvName"]),
|
|
Path: string(secret.Data["Path"]),
|
|
KeyPath: string(secret.Data["KeyPath"]),
|
|
ConfigPath: string(secret.Data["ConfigPath"]),
|
|
}
|
|
}
|
|
// Handle service account token key issue
|
|
kubeAPICert := certMap[pki.KubeAPICertName]
|
|
if certMap[pki.ServiceAccountTokenKeyName].Key == nil {
|
|
log.Infof(ctx, "[certificates] Creating service account token key")
|
|
certMap[pki.ServiceAccountTokenKeyName] = pki.ToCertObject(pki.ServiceAccountTokenKeyName, pki.ServiceAccountTokenKeyName, "", kubeAPICert.Certificate, kubeAPICert.Key)
|
|
}
|
|
log.Infof(ctx, "[certificates] Successfully fetched Cluster certificates from Kubernetes")
|
|
return certMap, nil
|
|
}
|
|
|
|
func (c *Cluster) getBackupHosts() []*hosts.Host {
|
|
var backupHosts []*hosts.Host
|
|
if len(c.Services.Etcd.ExternalURLs) > 0 {
|
|
backupHosts = c.ControlPlaneHosts
|
|
} else {
|
|
// Save certificates on etcd and controlplane hosts
|
|
backupHosts = hosts.GetUniqueHostList(c.EtcdHosts, c.ControlPlaneHosts, nil)
|
|
}
|
|
return backupHosts
|
|
}
|
|
|
|
func regenerateAPIAggregationCerts(c *Cluster, certificates map[string]pki.CertificatePKI) (map[string]pki.CertificatePKI, error) {
|
|
logrus.Debugf("[certificates] Regenerating Kubernetes API server aggregation layer requestheader client CA certificates")
|
|
requestHeaderCACrt, requestHeaderCAKey, err := pki.GenerateCACertAndKey(pki.RequestHeaderCACertName, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
certificates[pki.RequestHeaderCACertName] = pki.ToCertObject(pki.RequestHeaderCACertName, "", "", requestHeaderCACrt, requestHeaderCAKey)
|
|
|
|
//generate API server proxy client key and certs
|
|
logrus.Debugf("[certificates] Regenerating Kubernetes API server proxy client certificates")
|
|
apiserverProxyClientCrt, apiserverProxyClientKey, err := pki.GenerateSignedCertAndKey(requestHeaderCACrt, requestHeaderCAKey, true, pki.APIProxyClientCertName, nil, nil, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
certificates[pki.APIProxyClientCertName] = pki.ToCertObject(pki.APIProxyClientCertName, "", "", apiserverProxyClientCrt, apiserverProxyClientKey)
|
|
return certificates, nil
|
|
}
|
|
|
|
func RotateRKECertificates(ctx context.Context, c *Cluster, flags ExternalFlags, clusterState *FullState) error {
|
|
var (
|
|
serviceAccountTokenKey string
|
|
)
|
|
componentsCertsFuncMap := map[string]pki.GenFunc{
|
|
services.KubeAPIContainerName: pki.GenerateKubeAPICertificate,
|
|
services.KubeControllerContainerName: pki.GenerateKubeControllerCertificate,
|
|
services.SchedulerContainerName: pki.GenerateKubeSchedulerCertificate,
|
|
services.KubeproxyContainerName: pki.GenerateKubeProxyCertificate,
|
|
services.KubeletContainerName: pki.GenerateKubeNodeCertificate,
|
|
services.EtcdContainerName: pki.GenerateEtcdCertificates,
|
|
}
|
|
rotateFlags := c.RancherKubernetesEngineConfig.RotateCertificates
|
|
if rotateFlags.CACertificates {
|
|
// rotate CA cert and RequestHeader CA cert
|
|
if err := pki.GenerateRKECACerts(ctx, c.Certificates, flags.ClusterFilePath, flags.ConfigDir); err != nil {
|
|
return err
|
|
}
|
|
rotateFlags.Services = nil
|
|
}
|
|
for _, k8sComponent := range rotateFlags.Services {
|
|
genFunc := componentsCertsFuncMap[k8sComponent]
|
|
if genFunc != nil {
|
|
if err := genFunc(ctx, c.Certificates, c.RancherKubernetesEngineConfig, flags.ClusterFilePath, flags.ConfigDir, true); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
// to handle kontainer engine sending empty string for services
|
|
if len(rotateFlags.Services) == 0 || (len(rotateFlags.Services) == 1 && rotateFlags.Services[0] == "") {
|
|
// do not rotate service account token
|
|
if c.Certificates[pki.ServiceAccountTokenKeyName].Key != nil {
|
|
serviceAccountTokenKey = string(cert.EncodePrivateKeyPEM(c.Certificates[pki.ServiceAccountTokenKeyName].Key))
|
|
}
|
|
if err := pki.GenerateRKEServicesCerts(ctx, c.Certificates, c.RancherKubernetesEngineConfig, flags.ClusterFilePath, flags.ConfigDir, true); err != nil {
|
|
return err
|
|
}
|
|
if serviceAccountTokenKey != "" {
|
|
privateKey, err := cert.ParsePrivateKeyPEM([]byte(serviceAccountTokenKey))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.Certificates[pki.ServiceAccountTokenKeyName] = pki.ToCertObject(
|
|
pki.ServiceAccountTokenKeyName,
|
|
pki.ServiceAccountTokenKeyName,
|
|
"",
|
|
c.Certificates[pki.ServiceAccountTokenKeyName].Certificate,
|
|
privateKey.(*rsa.PrivateKey))
|
|
}
|
|
}
|
|
clusterState.DesiredState.CertificatesBundle = c.Certificates
|
|
clusterState.DesiredState.RancherKubernetesEngineConfig = &c.RancherKubernetesEngineConfig
|
|
return nil
|
|
}
|