2017-11-02 12:07:10 +02:00
package cluster
import (
2018-01-09 15:10:56 -07:00
"context"
2017-11-02 12:07:10 +02:00
"crypto/rsa"
"fmt"
2018-04-25 07:11:57 +02:00
"strings"
2017-11-02 12:07:10 +02:00
"time"
2018-01-17 01:10:14 +02:00
"github.com/rancher/rke/hosts"
2017-11-02 12:07:10 +02:00
"github.com/rancher/rke/k8s"
2018-01-09 15:10:56 -07:00
"github.com/rancher/rke/log"
2017-11-02 12:07:10 +02:00
"github.com/rancher/rke/pki"
2018-05-29 17:21:24 +02:00
"github.com/rancher/rke/services"
2018-10-18 00:26:54 +02:00
"github.com/rancher/rke/util"
2017-11-13 23:28:38 +02:00
"github.com/sirupsen/logrus"
2018-02-01 16:27:28 +02:00
"golang.org/x/sync/errgroup"
2017-11-02 12:07:10 +02:00
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/util/cert"
)
2018-11-13 01:24:59 +02:00
type RotateCertificatesFlags struct {
RotateCACerts bool
RotateComponents [ ] string
}
2018-11-08 01:54:08 +02:00
func SetUpAuthentication ( ctx context . Context , kubeCluster , currentCluster * Cluster , fullState * FullState ) error {
2018-11-02 07:53:29 +02:00
if kubeCluster . Authentication . Strategy == X509AuthenticationProvider {
2018-11-08 01:54:08 +02:00
kubeCluster . Certificates = fullState . DesiredState . CertificatesBundle
2018-11-02 07:53:29 +02:00
return nil
}
return nil
}
2017-11-21 21:25:08 +02:00
func regenerateAPICertificate ( c * Cluster , certificates map [ string ] pki . CertificatePKI ) ( map [ string ] pki . CertificatePKI , error ) {
logrus . Debugf ( "[certificates] Regenerating kubeAPI certificate" )
2018-03-22 11:36:25 -07:00
kubeAPIAltNames := pki . GetAltNames ( c . ControlPlaneHosts , c . ClusterDomain , c . KubernetesServiceIP , c . Authentication . SANs )
2017-11-21 21:25:08 +02:00
caCrt := certificates [ pki . CACertName ] . Certificate
caKey := certificates [ pki . CACertName ] . Key
2017-11-27 00:29:52 +02:00
kubeAPIKey := certificates [ pki . KubeAPICertName ] . Key
2018-01-17 01:10:14 +02:00
kubeAPICert , _ , err := pki . GenerateSignedCertAndKey ( caCrt , caKey , true , pki . KubeAPICertName , kubeAPIAltNames , kubeAPIKey , nil )
2017-11-21 21:25:08 +02:00
if err != nil {
return nil , err
}
2018-01-17 01:10:14 +02:00
certificates [ pki . KubeAPICertName ] = pki . ToCertObject ( pki . KubeAPICertName , "" , "" , kubeAPICert , kubeAPIKey )
2017-11-21 21:25:08 +02:00
return certificates , nil
}
2018-11-08 01:54:08 +02:00
func GetClusterCertsFromKubernetes ( ctx context . Context , kubeCluster * Cluster ) ( map [ string ] pki . CertificatePKI , error ) {
2018-01-09 15:10:56 -07:00
log . Infof ( ctx , "[certificates] Getting Cluster certificates from Kubernetes" )
2018-11-07 02:24:49 +02:00
2018-11-08 01:54:08 +02:00
k8sClient , err := k8s . NewClient ( kubeCluster . LocalKubeConfigPath , kubeCluster . K8sWrapTransport )
2018-11-07 02:24:49 +02:00
if err != nil {
return nil , fmt . Errorf ( "Failed to create Kubernetes Client: %v" , err )
}
2017-11-02 12:07:10 +02:00
certificatesNames := [ ] string {
pki . CACertName ,
pki . KubeAPICertName ,
2018-01-17 01:10:14 +02:00
pki . KubeNodeCertName ,
pki . KubeProxyCertName ,
pki . KubeControllerCertName ,
pki . KubeSchedulerCertName ,
pki . KubeAdminCertName ,
2018-09-08 02:23:47 +02:00
pki . APIProxyClientCertName ,
pki . RequestHeaderCACertName ,
2018-08-20 06:37:04 +02:00
pki . ServiceAccountTokenKeyName ,
2018-01-17 01:10:14 +02:00
}
2018-11-08 01:54:08 +02:00
for _ , etcdHost := range kubeCluster . EtcdHosts {
2018-01-17 01:10:14 +02:00
etcdName := pki . GetEtcdCrtName ( etcdHost . InternalAddress )
certificatesNames = append ( certificatesNames , etcdName )
2017-11-02 12:07:10 +02:00
}
2018-01-17 01:10:14 +02:00
2017-11-02 12:07:10 +02:00
certMap := make ( map [ string ] pki . CertificatePKI )
for _ , certName := range certificatesNames {
2018-11-07 02:24:49 +02:00
secret , err := k8s . GetSecret ( k8sClient , certName )
2018-09-08 02:23:47 +02:00
if err != nil && ! strings . HasPrefix ( certName , "kube-etcd" ) &&
! strings . Contains ( certName , pki . RequestHeaderCACertName ) &&
2018-08-20 06:37:04 +02:00
! strings . Contains ( certName , pki . APIProxyClientCertName ) &&
! strings . Contains ( certName , pki . ServiceAccountTokenKeyName ) {
2017-11-02 12:07:10 +02:00
return nil , err
}
2018-09-08 02:23:47 +02:00
// 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 ) ||
2018-08-20 06:37:04 +02:00
strings . Contains ( certName , pki . APIProxyClientCertName ) ||
strings . Contains ( certName , pki . ServiceAccountTokenKeyName ) ) {
2018-04-25 07:11:57 +02:00
certMap [ certName ] = pki . CertificatePKI { }
continue
}
2018-09-04 21:26:59 +02:00
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 )
}
2017-11-02 12:07:10 +02:00
secretConfig := string ( secret . Data [ "Config" ] )
2018-09-04 21:26:59 +02:00
if len ( secretCert ) == 0 || secretKey == nil {
return nil , fmt . Errorf ( "certificate or key of %s is not found" , certName )
}
2018-11-08 01:54:08 +02:00
certificatePEM := string ( cert . EncodeCertPEM ( secretCert [ 0 ] ) )
keyPEM := string ( cert . EncodePrivateKeyPEM ( secretKey . ( * rsa . PrivateKey ) ) )
2017-11-02 12:07:10 +02:00
certMap [ certName ] = pki . CertificatePKI {
2018-11-08 01:54:08 +02:00
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" ] ) ,
2017-11-02 12:07:10 +02:00
}
}
2018-11-07 02:24:49 +02:00
// 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 )
}
2018-01-09 15:10:56 -07:00
log . Infof ( ctx , "[certificates] Successfully fetched Cluster certificates from Kubernetes" )
2017-11-02 12:07:10 +02:00
return certMap , nil
}
2018-01-09 15:10:56 -07:00
func saveClusterCerts ( ctx context . Context , kubeClient * kubernetes . Clientset , crts map [ string ] pki . CertificatePKI ) error {
log . Infof ( ctx , "[certificates] Save kubernetes certificates as secrets" )
2018-02-01 16:27:28 +02:00
var errgrp errgroup . Group
2017-11-02 12:07:10 +02:00
for crtName , crt := range crts {
2018-02-01 16:27:28 +02:00
name := crtName
certificate := crt
errgrp . Go ( func ( ) error {
return saveCertToKubernetes ( kubeClient , name , certificate )
} )
}
if err := errgrp . Wait ( ) ; err != nil {
return err
2017-11-02 12:07:10 +02:00
}
2018-01-09 15:10:56 -07:00
log . Infof ( ctx , "[certificates] Successfully saved certificates as kubernetes secret [%s]" , pki . CertificatesSecretName )
2017-11-02 12:07:10 +02:00
return nil
}
2017-11-07 17:44:17 +02:00
func saveCertToKubernetes ( kubeClient * kubernetes . Clientset , crtName string , crt pki . CertificatePKI ) error {
2017-11-02 12:07:10 +02:00
logrus . Debugf ( "[certificates] Saving certificate [%s] to kubernetes" , crtName )
timeout := make ( chan bool , 1 )
2018-02-01 16:27:28 +02:00
// build secret Data
2018-02-14 22:58:35 +02:00
secretData := make ( map [ string ] [ ] byte )
if crt . Certificate != nil {
secretData [ "Certificate" ] = cert . EncodeCertPEM ( crt . Certificate )
secretData [ "EnvName" ] = [ ] byte ( crt . EnvName )
2018-04-02 20:36:40 +02:00
secretData [ "Path" ] = [ ] byte ( crt . Path )
2018-02-14 22:58:35 +02:00
}
if crt . Key != nil {
secretData [ "Key" ] = cert . EncodePrivateKeyPEM ( crt . Key )
secretData [ "KeyEnvName" ] = [ ] byte ( crt . KeyEnvName )
2018-04-02 20:36:40 +02:00
secretData [ "KeyPath" ] = [ ] byte ( crt . KeyPath )
2018-02-01 16:27:28 +02:00
}
if len ( crt . Config ) > 0 {
secretData [ "ConfigEnvName" ] = [ ] byte ( crt . ConfigEnvName )
secretData [ "Config" ] = [ ] byte ( crt . Config )
2018-04-02 20:36:40 +02:00
secretData [ "ConfigPath" ] = [ ] byte ( crt . ConfigPath )
2018-02-01 16:27:28 +02:00
}
2017-11-02 12:07:10 +02:00
go func ( ) {
for {
2018-02-01 16:27:28 +02:00
err := k8s . UpdateSecret ( kubeClient , secretData , crtName )
2017-11-14 20:11:21 +02:00
if err != nil {
time . Sleep ( time . Second * 5 )
continue
}
2017-11-02 12:07:10 +02:00
timeout <- true
break
}
} ( )
select {
case <- timeout :
return nil
case <- time . After ( time . Second * KubernetesClientTimeOut ) :
return fmt . Errorf ( "[certificates] Timeout waiting for kubernetes to be ready" )
}
}
2018-04-20 06:08:24 +02:00
func deployBackupCertificates ( ctx context . Context , backupHosts [ ] * hosts . Host , kubeCluster * Cluster ) error {
var errgrp errgroup . Group
2018-10-18 00:26:54 +02:00
hostsQueue := util . GetObjectQueue ( backupHosts )
for w := 0 ; w < WorkerThreads ; w ++ {
2018-04-20 06:08:24 +02:00
errgrp . Go ( func ( ) error {
2018-10-18 00:26:54 +02:00
var errList [ ] error
for host := range hostsQueue {
err := pki . DeployCertificatesOnHost ( ctx , host . ( * hosts . Host ) , kubeCluster . Certificates , kubeCluster . SystemImages . CertDownloader , pki . TempCertPath , kubeCluster . PrivateRegistriesMap )
if err != nil {
errList = append ( errList , err )
}
}
return util . ErrList ( errList )
2018-04-20 06:08:24 +02:00
} )
}
return errgrp . Wait ( )
}
2018-05-29 17:21:24 +02:00
func ( c * Cluster ) SaveBackupCertificateBundle ( ctx context . Context ) error {
var errgrp errgroup . Group
2018-10-18 00:26:54 +02:00
hostsQueue := util . GetObjectQueue ( c . getBackupHosts ( ) )
for w := 0 ; w < WorkerThreads ; w ++ {
2018-05-29 17:21:24 +02:00
errgrp . Go ( func ( ) error {
2018-10-18 00:26:54 +02:00
var errList [ ] error
for host := range hostsQueue {
err := pki . SaveBackupBundleOnHost ( ctx , host . ( * hosts . Host ) , c . SystemImages . Alpine , services . EtcdSnapshotPath , c . PrivateRegistriesMap )
if err != nil {
errList = append ( errList , err )
}
}
return util . ErrList ( errList )
2018-05-29 17:21:24 +02:00
} )
}
2018-10-18 00:26:54 +02:00
2018-05-29 17:21:24 +02:00
return errgrp . Wait ( )
}
func ( c * Cluster ) ExtractBackupCertificateBundle ( ctx context . Context ) error {
backupHosts := c . getBackupHosts ( )
var errgrp errgroup . Group
2018-06-13 23:26:03 +02:00
errList := [ ] string { }
2018-10-18 00:26:54 +02:00
hostsQueue := util . GetObjectQueue ( backupHosts )
for w := 0 ; w < WorkerThreads ; w ++ {
2018-05-29 17:21:24 +02:00
errgrp . Go ( func ( ) error {
2018-10-18 00:26:54 +02:00
for host := range hostsQueue {
if err := pki . ExtractBackupBundleOnHost ( ctx , host . ( * hosts . Host ) , c . SystemImages . Alpine , services . EtcdSnapshotPath , c . PrivateRegistriesMap ) ; err != nil {
errList = append ( errList , fmt . Errorf (
"Failed to extract certificate bundle on host [%s], please make sure etcd bundle exist in /opt/rke/etcd-snapshots/pki.bundle.tar.gz: %v" , host . ( * hosts . Host ) . Address , err ) . Error ( ) )
}
2018-06-13 23:26:03 +02:00
}
return nil
2018-05-29 17:21:24 +02:00
} )
}
2018-10-18 00:26:54 +02:00
2018-06-13 23:26:03 +02:00
errgrp . Wait ( )
if len ( errList ) == len ( backupHosts ) {
return fmt . Errorf ( strings . Join ( errList , "," ) )
}
return nil
2018-05-29 17:21:24 +02:00
}
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
}
2018-07-17 20:19:08 +02:00
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" )
2018-08-20 06:37:04 +02:00
requestHeaderCACrt , requestHeaderCAKey , err := pki . GenerateCACertAndKey ( pki . RequestHeaderCACertName , nil )
2018-07-17 20:19:08 +02:00
if err != nil {
return nil , err
}
certificates [ pki . RequestHeaderCACertName ] = pki . ToCertObject ( pki . RequestHeaderCACertName , "" , "" , requestHeaderCACrt , requestHeaderCAKey )
//generate API server proxy client key and certs
2018-07-21 21:46:05 +02:00
logrus . Debugf ( "[certificates] Regenerating Kubernetes API server proxy client certificates" )
2018-07-17 20:19:08 +02:00
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
}
2018-08-20 06:37:04 +02:00
2018-11-13 01:24:59 +02:00
func RotateRKECertificates ( ctx context . Context , c * Cluster , flags ExternalFlags , rotateflags RotateCertificatesFlags , clusterState * FullState ) error {
2018-08-20 06:37:04 +02:00
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 ,
}
2018-11-13 01:24:59 +02:00
if rotateflags . RotateCACerts {
2018-08-20 06:37:04 +02:00
// rotate CA cert and RequestHeader CA cert
2018-11-08 01:54:08 +02:00
if err := pki . GenerateRKECACerts ( ctx , c . Certificates , flags . ClusterFilePath , flags . ConfigDir ) ; err != nil {
2018-08-20 06:37:04 +02:00
return err
}
2018-11-13 01:24:59 +02:00
rotateflags . RotateComponents = nil
2018-08-20 06:37:04 +02:00
}
2018-11-13 01:24:59 +02:00
for _ , k8sComponent := range rotateflags . RotateComponents {
2018-08-20 06:37:04 +02:00
genFunc := componentsCertsFuncMap [ k8sComponent ]
if genFunc != nil {
2018-11-13 01:24:59 +02:00
if err := genFunc ( ctx , c . Certificates , c . RancherKubernetesEngineConfig , flags . ClusterFilePath , flags . ConfigDir , true ) ; err != nil {
2018-08-20 06:37:04 +02:00
return err
}
}
}
2018-11-13 01:24:59 +02:00
if len ( rotateflags . RotateComponents ) == 0 {
2018-08-20 06:37:04 +02:00
// do not rotate service account token
if c . Certificates [ pki . ServiceAccountTokenKeyName ] . Key != nil {
serviceAccountTokenKey = string ( cert . EncodePrivateKeyPEM ( c . Certificates [ pki . ServiceAccountTokenKeyName ] . Key ) )
}
2018-11-13 01:24:59 +02:00
if err := pki . GenerateRKEServicesCerts ( ctx , c . Certificates , c . RancherKubernetesEngineConfig , flags . ClusterFilePath , flags . ConfigDir , true ) ; err != nil {
2018-08-20 06:37:04 +02:00
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 ) )
}
}
2018-11-13 01:24:59 +02:00
clusterState . DesiredState . CertificatesBundle = c . Certificates
clusterState . DesiredState . RancherKubernetesEngineConfig = & c . RancherKubernetesEngineConfig
2018-08-20 06:37:04 +02:00
return nil
}
2018-11-13 01:24:59 +02:00
func GetRotateCertsFlags ( rotateCACerts bool , components [ ] string ) RotateCertificatesFlags {
return RotateCertificatesFlags {
RotateCACerts : rotateCACerts ,
RotateComponents : components ,
}
}