1
0
mirror of https://github.com/rancher/rke.git synced 2025-08-24 09:28:29 +00:00
rke/cluster/certificates.go
Erik Wilson e04b7d4413 Add support for Kubernetes API Authn Webhook
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.
2019-01-03 17:15:23 -07:00

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
}