2017-11-02 10:07:10 +00:00
package cluster
import (
2018-01-09 22:10:56 +00:00
"context"
2017-11-02 10:07:10 +00:00
"crypto/rsa"
"fmt"
2018-04-25 05:11:57 +00:00
"strings"
2017-11-02 10:07:10 +00:00
"time"
2018-01-16 23:10:14 +00:00
"github.com/rancher/rke/hosts"
2017-11-02 10:07:10 +00:00
"github.com/rancher/rke/k8s"
2018-01-09 22:10:56 +00:00
"github.com/rancher/rke/log"
2017-11-02 10:07:10 +00:00
"github.com/rancher/rke/pki"
2018-05-29 15:21:24 +00:00
"github.com/rancher/rke/services"
2018-10-17 22:26:54 +00:00
"github.com/rancher/rke/util"
2017-11-13 21:28:38 +00:00
"github.com/sirupsen/logrus"
2018-02-01 14:27:28 +00:00
"golang.org/x/sync/errgroup"
2017-11-02 10:07:10 +00:00
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/util/cert"
)
2018-11-03 01:45:23 +00:00
func SetUpAuthentication ( ctx context . Context , kubeCluster , currentCluster * Cluster , fullState * RKEFullState ) error {
2018-11-02 05:53:29 +00:00
if kubeCluster . Authentication . Strategy == X509AuthenticationProvider {
kubeCluster . Certificates = TransformV3CertsToCerts ( fullState . DesiredState . CertificatesBundle )
return nil
}
return nil
}
2017-11-21 19:25:08 +00:00
func regenerateAPICertificate ( c * Cluster , certificates map [ string ] pki . CertificatePKI ) ( map [ string ] pki . CertificatePKI , error ) {
logrus . Debugf ( "[certificates] Regenerating kubeAPI certificate" )
2018-03-22 18:36:25 +00:00
kubeAPIAltNames := pki . GetAltNames ( c . ControlPlaneHosts , c . ClusterDomain , c . KubernetesServiceIP , c . Authentication . SANs )
2017-11-21 19:25:08 +00:00
caCrt := certificates [ pki . CACertName ] . Certificate
caKey := certificates [ pki . CACertName ] . Key
2017-11-26 22:29:52 +00:00
kubeAPIKey := certificates [ pki . KubeAPICertName ] . Key
2018-01-16 23:10:14 +00:00
kubeAPICert , _ , err := pki . GenerateSignedCertAndKey ( caCrt , caKey , true , pki . KubeAPICertName , kubeAPIAltNames , kubeAPIKey , nil )
2017-11-21 19:25:08 +00:00
if err != nil {
return nil , err
}
2018-01-16 23:10:14 +00:00
certificates [ pki . KubeAPICertName ] = pki . ToCertObject ( pki . KubeAPICertName , "" , "" , kubeAPICert , kubeAPIKey )
2017-11-21 19:25:08 +00:00
return certificates , nil
}
2018-11-07 00:24:49 +00:00
func GetClusterCertsFromKubernetes ( ctx context . Context , localConfigPath string , k8sWrapTransport k8s . WrapTransport , etcdHosts [ ] * hosts . Host ) ( map [ string ] pki . CertificatePKI , error ) {
2018-01-09 22:10:56 +00:00
log . Infof ( ctx , "[certificates] Getting Cluster certificates from Kubernetes" )
2018-11-07 00:24:49 +00:00
k8sClient , err := k8s . NewClient ( localConfigPath , k8sWrapTransport )
if err != nil {
return nil , fmt . Errorf ( "Failed to create Kubernetes Client: %v" , err )
}
2017-11-02 10:07:10 +00:00
certificatesNames := [ ] string {
pki . CACertName ,
pki . KubeAPICertName ,
2018-01-16 23:10:14 +00:00
pki . KubeNodeCertName ,
pki . KubeProxyCertName ,
pki . KubeControllerCertName ,
pki . KubeSchedulerCertName ,
pki . KubeAdminCertName ,
2018-09-08 00:23:47 +00:00
pki . APIProxyClientCertName ,
pki . RequestHeaderCACertName ,
2018-08-20 04:37:04 +00:00
pki . ServiceAccountTokenKeyName ,
2018-01-16 23:10:14 +00:00
}
for _ , etcdHost := range etcdHosts {
etcdName := pki . GetEtcdCrtName ( etcdHost . InternalAddress )
certificatesNames = append ( certificatesNames , etcdName )
2017-11-02 10:07:10 +00:00
}
2018-01-16 23:10:14 +00:00
2017-11-02 10:07:10 +00:00
certMap := make ( map [ string ] pki . CertificatePKI )
for _ , certName := range certificatesNames {
2018-11-07 00:24:49 +00:00
secret , err := k8s . GetSecret ( k8sClient , certName )
2018-09-08 00:23:47 +00:00
if err != nil && ! strings . HasPrefix ( certName , "kube-etcd" ) &&
! strings . Contains ( certName , pki . RequestHeaderCACertName ) &&
2018-08-20 04:37:04 +00:00
! strings . Contains ( certName , pki . APIProxyClientCertName ) &&
! strings . Contains ( certName , pki . ServiceAccountTokenKeyName ) {
2017-11-02 10:07:10 +00:00
return nil , err
}
2018-09-08 00:23:47 +00: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 04:37:04 +00:00
strings . Contains ( certName , pki . APIProxyClientCertName ) ||
strings . Contains ( certName , pki . ServiceAccountTokenKeyName ) ) {
2018-04-25 05:11:57 +00:00
certMap [ certName ] = pki . CertificatePKI { }
continue
}
2018-09-04 19:26:59 +00: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 10:07:10 +00:00
secretConfig := string ( secret . Data [ "Config" ] )
2018-09-04 19:26:59 +00:00
if len ( secretCert ) == 0 || secretKey == nil {
return nil , fmt . Errorf ( "certificate or key of %s is not found" , certName )
}
2017-11-02 10:07:10 +00:00
certMap [ certName ] = pki . CertificatePKI {
2017-11-14 18:11:21 +00:00
Certificate : secretCert [ 0 ] ,
Key : secretKey . ( * rsa . PrivateKey ) ,
Config : secretConfig ,
EnvName : string ( secret . Data [ "EnvName" ] ) ,
ConfigEnvName : string ( secret . Data [ "ConfigEnvName" ] ) ,
KeyEnvName : string ( secret . Data [ "KeyEnvName" ] ) ,
2018-04-02 18:36:40 +00:00
Path : string ( secret . Data [ "Path" ] ) ,
KeyPath : string ( secret . Data [ "KeyPath" ] ) ,
ConfigPath : string ( secret . Data [ "ConfigPath" ] ) ,
2017-11-02 10:07:10 +00:00
}
}
2018-11-07 00:24:49 +00: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 22:10:56 +00:00
log . Infof ( ctx , "[certificates] Successfully fetched Cluster certificates from Kubernetes" )
2017-11-02 10:07:10 +00:00
return certMap , nil
}
2018-01-09 22:10:56 +00: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 14:27:28 +00:00
var errgrp errgroup . Group
2017-11-02 10:07:10 +00:00
for crtName , crt := range crts {
2018-02-01 14:27:28 +00: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 10:07:10 +00:00
}
2018-01-09 22:10:56 +00:00
log . Infof ( ctx , "[certificates] Successfully saved certificates as kubernetes secret [%s]" , pki . CertificatesSecretName )
2017-11-02 10:07:10 +00:00
return nil
}
2017-11-07 15:44:17 +00:00
func saveCertToKubernetes ( kubeClient * kubernetes . Clientset , crtName string , crt pki . CertificatePKI ) error {
2017-11-02 10:07:10 +00:00
logrus . Debugf ( "[certificates] Saving certificate [%s] to kubernetes" , crtName )
timeout := make ( chan bool , 1 )
2018-02-01 14:27:28 +00:00
// build secret Data
2018-02-14 20:58:35 +00:00
secretData := make ( map [ string ] [ ] byte )
if crt . Certificate != nil {
secretData [ "Certificate" ] = cert . EncodeCertPEM ( crt . Certificate )
secretData [ "EnvName" ] = [ ] byte ( crt . EnvName )
2018-04-02 18:36:40 +00:00
secretData [ "Path" ] = [ ] byte ( crt . Path )
2018-02-14 20:58:35 +00:00
}
if crt . Key != nil {
secretData [ "Key" ] = cert . EncodePrivateKeyPEM ( crt . Key )
secretData [ "KeyEnvName" ] = [ ] byte ( crt . KeyEnvName )
2018-04-02 18:36:40 +00:00
secretData [ "KeyPath" ] = [ ] byte ( crt . KeyPath )
2018-02-01 14:27:28 +00:00
}
if len ( crt . Config ) > 0 {
secretData [ "ConfigEnvName" ] = [ ] byte ( crt . ConfigEnvName )
secretData [ "Config" ] = [ ] byte ( crt . Config )
2018-04-02 18:36:40 +00:00
secretData [ "ConfigPath" ] = [ ] byte ( crt . ConfigPath )
2018-02-01 14:27:28 +00:00
}
2017-11-02 10:07:10 +00:00
go func ( ) {
for {
2018-02-01 14:27:28 +00:00
err := k8s . UpdateSecret ( kubeClient , secretData , crtName )
2017-11-14 18:11:21 +00:00
if err != nil {
time . Sleep ( time . Second * 5 )
continue
}
2017-11-02 10:07:10 +00: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 04:08:24 +00:00
func deployBackupCertificates ( ctx context . Context , backupHosts [ ] * hosts . Host , kubeCluster * Cluster ) error {
var errgrp errgroup . Group
2018-10-17 22:26:54 +00:00
hostsQueue := util . GetObjectQueue ( backupHosts )
for w := 0 ; w < WorkerThreads ; w ++ {
2018-04-20 04:08:24 +00:00
errgrp . Go ( func ( ) error {
2018-10-17 22:26:54 +00: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 04:08:24 +00:00
} )
}
return errgrp . Wait ( )
}
func fetchBackupCertificates ( ctx context . Context , backupHosts [ ] * hosts . Host , kubeCluster * Cluster ) ( map [ string ] pki . CertificatePKI , error ) {
var err error
certificates := map [ string ] pki . CertificatePKI { }
for _ , host := range backupHosts {
certificates , err = pki . FetchCertificatesFromHost ( ctx , kubeCluster . EtcdHosts , host , kubeCluster . SystemImages . Alpine , kubeCluster . LocalKubeConfigPath , kubeCluster . PrivateRegistriesMap )
if certificates != nil {
return certificates , nil
}
}
// reporting the last error only.
return nil , err
}
2018-05-09 17:39:19 +00:00
func fetchCertificatesFromEtcd ( ctx context . Context , kubeCluster * Cluster ) ( [ ] byte , [ ] byte , error ) {
// Get kubernetes certificates from the etcd hosts
certificates := map [ string ] pki . CertificatePKI { }
var err error
for _ , host := range kubeCluster . EtcdHosts {
certificates , err = pki . FetchCertificatesFromHost ( ctx , kubeCluster . EtcdHosts , host , kubeCluster . SystemImages . Alpine , kubeCluster . LocalKubeConfigPath , kubeCluster . PrivateRegistriesMap )
if certificates != nil {
break
}
}
if err != nil || certificates == nil {
return nil , nil , fmt . Errorf ( "Failed to fetch certificates from etcd hosts: %v" , err )
}
clientCert := cert . EncodeCertPEM ( certificates [ pki . KubeNodeCertName ] . Certificate )
clientkey := cert . EncodePrivateKeyPEM ( certificates [ pki . KubeNodeCertName ] . Key )
return clientCert , clientkey , nil
}
2018-05-29 15:21:24 +00:00
func ( c * Cluster ) SaveBackupCertificateBundle ( ctx context . Context ) error {
var errgrp errgroup . Group
2018-10-17 22:26:54 +00:00
hostsQueue := util . GetObjectQueue ( c . getBackupHosts ( ) )
for w := 0 ; w < WorkerThreads ; w ++ {
2018-05-29 15:21:24 +00:00
errgrp . Go ( func ( ) error {
2018-10-17 22:26:54 +00: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 15:21:24 +00:00
} )
}
2018-10-17 22:26:54 +00:00
2018-05-29 15:21:24 +00:00
return errgrp . Wait ( )
}
func ( c * Cluster ) ExtractBackupCertificateBundle ( ctx context . Context ) error {
backupHosts := c . getBackupHosts ( )
var errgrp errgroup . Group
2018-06-13 21:26:03 +00:00
errList := [ ] string { }
2018-10-17 22:26:54 +00:00
hostsQueue := util . GetObjectQueue ( backupHosts )
for w := 0 ; w < WorkerThreads ; w ++ {
2018-05-29 15:21:24 +00:00
errgrp . Go ( func ( ) error {
2018-10-17 22:26:54 +00: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 21:26:03 +00:00
}
return nil
2018-05-29 15:21:24 +00:00
} )
}
2018-10-17 22:26:54 +00:00
2018-06-13 21:26:03 +00:00
errgrp . Wait ( )
if len ( errList ) == len ( backupHosts ) {
return fmt . Errorf ( strings . Join ( errList , "," ) )
}
return nil
2018-05-29 15:21:24 +00: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 18:19:08 +00: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 04:37:04 +00:00
requestHeaderCACrt , requestHeaderCAKey , err := pki . GenerateCACertAndKey ( pki . RequestHeaderCACertName , nil )
2018-07-17 18:19:08 +00: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 19:46:05 +00:00
logrus . Debugf ( "[certificates] Regenerating Kubernetes API server proxy client certificates" )
2018-07-17 18:19:08 +00: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 04:37:04 +00:00
func RotateRKECertificates ( ctx context . Context , c * Cluster , configPath , configDir string , components [ ] string , rotateCACerts bool ) 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 ,
}
if rotateCACerts {
// rotate CA cert and RequestHeader CA cert
if err := pki . GenerateRKECACerts ( ctx , c . Certificates , configPath , configDir ) ; err != nil {
return err
}
components = nil
}
for _ , k8sComponent := range components {
genFunc := componentsCertsFuncMap [ k8sComponent ]
if genFunc != nil {
if err := genFunc ( ctx , c . Certificates , c . RancherKubernetesEngineConfig , configPath , configDir ) ; err != nil {
return err
}
}
}
if len ( components ) == 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 , configPath , configDir ) ; 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 ) )
}
}
return nil
}