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"
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-01-09 22:10:56 +00:00
func SetUpAuthentication ( ctx context . Context , kubeCluster , currentCluster * Cluster ) error {
2017-11-14 18:11:21 +00:00
if kubeCluster . Authentication . Strategy == X509AuthenticationProvider {
2017-11-02 10:07:10 +00:00
var err error
if currentCluster != nil {
2017-11-15 02:54:26 +00:00
kubeCluster . Certificates = currentCluster . Certificates
2018-07-17 18:19:08 +00:00
// this is the case of handling upgrades for API server aggregation layer ca cert and API server proxy client key and cert
if kubeCluster . Certificates [ pki . RequestHeaderCACertName ] . Certificate == nil {
kubeCluster . Certificates , err = regenerateAPIAggregationCerts ( kubeCluster , kubeCluster . Certificates )
if err != nil {
return fmt . Errorf ( "Failed to regenerate Aggregation layer certificates %v" , err )
}
}
2017-11-02 10:07:10 +00:00
} else {
2018-04-20 04:08:24 +00:00
var backupPlane string
var backupHosts [ ] * hosts . Host
2018-02-15 03:25:36 +00:00
if len ( kubeCluster . Services . Etcd . ExternalURLs ) > 0 {
2018-04-20 04:08:24 +00:00
backupPlane = ControlPlane
backupHosts = kubeCluster . ControlPlaneHosts
2018-02-15 03:25:36 +00:00
} else {
2018-05-14 22:00:45 +00:00
// Save certificates on etcd and controlplane hosts
backupPlane = fmt . Sprintf ( "%s,%s" , EtcdPlane , ControlPlane )
backupHosts = hosts . GetUniqueHostList ( kubeCluster . EtcdHosts , kubeCluster . ControlPlaneHosts , nil )
2018-02-15 03:25:36 +00:00
}
2018-04-20 04:08:24 +00:00
log . Infof ( ctx , "[certificates] Attempting to recover certificates from backup on [%s] hosts" , backupPlane )
kubeCluster . Certificates , err = fetchBackupCertificates ( ctx , backupHosts , kubeCluster )
2018-01-10 23:03:08 +00:00
if err != nil {
return err
}
if kubeCluster . Certificates != nil {
2018-04-25 05:11:57 +00:00
log . Infof ( ctx , "[certificates] Certificate backup found on [%s] hosts" , backupPlane )
// make sure I have all the etcd certs, We need handle dialer failure for etcd nodes https://github.com/rancher/rancher/issues/12898
for _ , host := range kubeCluster . EtcdHosts {
certName := pki . GetEtcdCrtName ( host . InternalAddress )
if kubeCluster . Certificates [ certName ] . Certificate == nil {
if kubeCluster . Certificates , err = pki . RegenerateEtcdCertificate ( ctx ,
kubeCluster . Certificates ,
host ,
kubeCluster . EtcdHosts ,
kubeCluster . ClusterDomain ,
kubeCluster . KubernetesServiceIP ) ; err != nil {
return err
}
}
}
2018-02-15 03:25:36 +00:00
// this is the case of adding controlplane node on empty cluster with only etcd nodes
if kubeCluster . Certificates [ pki . KubeAdminCertName ] . Config == "" && len ( kubeCluster . ControlPlaneHosts ) > 0 {
if err := rebuildLocalAdminConfig ( ctx , kubeCluster ) ; err != nil {
return err
}
kubeCluster . Certificates , err = regenerateAPICertificate ( kubeCluster , kubeCluster . Certificates )
if err != nil {
return fmt . Errorf ( "Failed to regenerate KubeAPI certificate %v" , err )
}
}
2018-07-17 18:19:08 +00:00
// this is the case of handling upgrades for API server aggregation layer ca cert and API server proxy client key and cert
if kubeCluster . Certificates [ pki . RequestHeaderCACertName ] . Certificate == nil {
kubeCluster . Certificates , err = regenerateAPIAggregationCerts ( kubeCluster , kubeCluster . Certificates )
if err != nil {
return fmt . Errorf ( "Failed to regenerate Aggregation layer certificates %v" , err )
}
}
2018-01-10 23:03:08 +00:00
return nil
}
2018-04-20 04:08:24 +00:00
log . Infof ( ctx , "[certificates] No Certificate backup found on [%s] hosts" , backupPlane )
2018-01-10 23:03:08 +00:00
2018-02-06 19:25:54 +00:00
kubeCluster . Certificates , err = pki . GenerateRKECerts ( ctx , kubeCluster . RancherKubernetesEngineConfig , kubeCluster . LocalKubeConfigPath , "" )
2017-11-02 10:07:10 +00:00
if err != nil {
return fmt . Errorf ( "Failed to generate Kubernetes certificates: %v" , err )
}
2018-04-20 04:08:24 +00:00
log . Infof ( ctx , "[certificates] Temporarily saving certs to [%s] hosts" , backupPlane )
if err := deployBackupCertificates ( ctx , backupHosts , kubeCluster ) ; err != nil {
2018-01-10 23:03:08 +00:00
return err
}
2018-04-20 04:08:24 +00:00
log . Infof ( ctx , "[certificates] Saved certs to [%s] hosts" , backupPlane )
2017-11-02 10:07:10 +00:00
}
}
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-01-16 23:10:14 +00:00
func getClusterCerts ( ctx context . Context , kubeClient * kubernetes . Clientset , 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" )
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 ,
}
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 {
2017-11-07 15:44:17 +00:00
secret , err := k8s . GetSecret ( kubeClient , certName )
2018-04-25 05:11:57 +00:00
if err != nil && ! strings . HasPrefix ( certName , "kube-etcd" ) {
2017-11-02 10:07:10 +00:00
return nil , err
}
2018-04-25 05:11:57 +00:00
// If I can't find an etcd cert, I will not fail and will create it later.
2018-09-04 19:26:59 +00:00
if ( secret == nil || secret . Data == nil ) && strings . HasPrefix ( certName , "kube-etcd" ) {
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-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
for _ , host := range backupHosts {
runHost := host
errgrp . Go ( func ( ) error {
return pki . DeployCertificatesOnHost ( ctx , runHost , kubeCluster . Certificates , kubeCluster . SystemImages . CertDownloader , pki . TempCertPath , kubeCluster . PrivateRegistriesMap )
} )
}
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 {
backupHosts := c . getBackupHosts ( )
var errgrp errgroup . Group
for _ , host := range backupHosts {
runHost := host
errgrp . Go ( func ( ) error {
return pki . SaveBackupBundleOnHost ( ctx , runHost , c . SystemImages . Alpine , services . EtcdSnapshotPath , c . PrivateRegistriesMap )
} )
}
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-05-29 15:21:24 +00:00
for _ , host := range backupHosts {
runHost := host
errgrp . Go ( func ( ) error {
2018-06-13 21:26:03 +00:00
if err := pki . ExtractBackupBundleOnHost ( ctx , runHost , 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" , runHost . Address , err ) . Error ( ) )
}
return nil
2018-05-29 15:21:24 +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" )
requestHeaderCACrt , requestHeaderCAKey , err := pki . GenerateCACertAndKey ( pki . RequestHeaderCACertName )
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
}