2018-08-20 04:37:04 +00:00
package cmd
import (
"context"
2020-03-18 20:12:53 +00:00
"crypto/x509"
2018-08-20 04:37:04 +00:00
"fmt"
2019-11-11 22:29:24 +00:00
"time"
2018-08-20 04:37:04 +00:00
"github.com/rancher/rke/cluster"
"github.com/rancher/rke/hosts"
"github.com/rancher/rke/log"
"github.com/rancher/rke/pki"
2019-08-19 17:53:15 +00:00
"github.com/rancher/rke/pki/cert"
2018-08-20 04:37:04 +00:00
"github.com/rancher/rke/services"
2020-07-11 16:24:19 +00:00
v3 "github.com/rancher/rke/types"
2020-03-18 20:12:53 +00:00
"github.com/sirupsen/logrus"
2018-08-20 04:37:04 +00:00
"github.com/urfave/cli"
)
func CertificateCommand ( ) cli . Command {
2019-08-08 18:06:40 +00:00
rotateFlags := [ ] cli . Flag {
cli . StringFlag {
Name : "config" ,
Usage : "Specify an alternate cluster YAML file" ,
Value : pki . ClusterConfig ,
EnvVar : "RKE_CONFIG" ,
} ,
cli . StringSliceFlag {
Name : "service" ,
Usage : fmt . Sprintf ( "Specify a k8s service to rotate certs, (allowed values: %s, %s, %s, %s, %s, %s)" ,
services . KubeAPIContainerName ,
services . KubeControllerContainerName ,
services . SchedulerContainerName ,
services . KubeletContainerName ,
services . KubeproxyContainerName ,
services . EtcdContainerName ,
) ,
} ,
cli . BoolFlag {
Name : "rotate-ca" ,
Usage : "Rotate all certificates including CA certs" ,
} ,
}
rotateFlags = append ( rotateFlags , commonFlags ... )
2018-08-20 04:37:04 +00:00
return cli . Command {
Name : "cert" ,
Usage : "Certificates management for RKE cluster" ,
Subcommands : cli . Commands {
cli . Command {
Name : "rotate" ,
Usage : "Rotate RKE cluster certificates" ,
Action : rotateRKECertificatesFromCli ,
2019-08-08 18:06:40 +00:00
Flags : rotateFlags ,
2018-08-20 04:37:04 +00:00
} ,
2019-01-02 23:02:34 +00:00
cli . Command {
Name : "generate-csr" ,
Usage : "Generate certificate sign requests for k8s components" ,
Action : generateCSRFromCli ,
Flags : [ ] cli . Flag {
cli . StringFlag {
Name : "config" ,
Usage : "Specify an alternate cluster YAML file" ,
Value : pki . ClusterConfig ,
EnvVar : "RKE_CONFIG" ,
} ,
cli . StringFlag {
Name : "cert-dir" ,
Usage : "Specify a certificate dir path" ,
} ,
} ,
} ,
2018-08-20 04:37:04 +00:00
} ,
}
}
func rotateRKECertificatesFromCli ( ctx * cli . Context ) error {
2019-09-03 20:01:22 +00:00
logrus . Infof ( "Running RKE version: %v" , ctx . App . Version )
2018-11-19 19:30:45 +00:00
k8sComponents := ctx . StringSlice ( "service" )
rotateCACerts := ctx . Bool ( "rotate-ca" )
2018-08-20 04:37:04 +00:00
clusterFile , filePath , err := resolveClusterFile ( ctx )
if err != nil {
return fmt . Errorf ( "Failed to resolve cluster file: %v" , err )
}
rkeConfig , err := cluster . ParseConfig ( clusterFile )
if err != nil {
return fmt . Errorf ( "Failed to parse cluster file: %v" , err )
}
rkeConfig , err = setOptionsFromCLI ( ctx , rkeConfig )
if err != nil {
return err
}
2018-11-07 23:54:08 +00:00
// setting up the flags
2020-08-03 13:35:38 +00:00
externalFlags := cluster . GetExternalFlags ( false , false , false , false , "" , filePath )
2018-11-19 19:30:45 +00:00
// setting up rotate flags
rkeConfig . RotateCertificates = & v3 . RotateCertificates {
CACertificates : rotateCACerts ,
Services : k8sComponents ,
}
if err := ClusterInit ( context . Background ( ) , rkeConfig , hosts . DialersOptions { } , externalFlags ) ; err != nil {
2018-11-12 23:24:59 +00:00
return err
}
2019-06-17 20:52:15 +00:00
_ , _ , _ , _ , _ , err = ClusterUp ( context . Background ( ) , hosts . DialersOptions { } , externalFlags , map [ string ] interface { } { } )
2018-11-19 19:30:45 +00:00
return err
2018-08-20 04:37:04 +00:00
}
2019-01-02 23:02:34 +00:00
func generateCSRFromCli ( ctx * cli . Context ) error {
2019-09-03 20:01:22 +00:00
logrus . Infof ( "Running RKE version: %v" , ctx . App . Version )
2019-01-02 23:02:34 +00:00
clusterFile , filePath , err := resolveClusterFile ( ctx )
if err != nil {
return fmt . Errorf ( "Failed to resolve cluster file: %v" , err )
}
rkeConfig , err := cluster . ParseConfig ( clusterFile )
if err != nil {
return fmt . Errorf ( "Failed to parse cluster file: %v" , err )
}
rkeConfig , err = setOptionsFromCLI ( ctx , rkeConfig )
if err != nil {
return err
}
// setting up the flags
2020-08-03 13:35:38 +00:00
externalFlags := cluster . GetExternalFlags ( false , false , false , false , "" , filePath )
2019-01-02 23:02:34 +00:00
externalFlags . CertificateDir = ctx . String ( "cert-dir" )
externalFlags . CustomCerts = ctx . Bool ( "custom-certs" )
return GenerateRKECSRs ( context . Background ( ) , rkeConfig , externalFlags )
}
2018-12-20 22:01:42 +00:00
func rebuildClusterWithRotatedCertificates ( ctx context . Context ,
2018-11-12 23:24:59 +00:00
dialersOptions hosts . DialersOptions ,
2019-09-06 22:53:14 +00:00
flags cluster . ExternalFlags , svcOptionData map [ string ] * v3 . KubernetesServicesOptions ) ( string , string , string , string , map [ string ] pki . CertificatePKI , error ) {
2018-11-19 19:30:45 +00:00
var APIURL , caCrt , clientCert , clientKey string
2018-11-12 23:24:59 +00:00
log . Infof ( ctx , "Rebuilding Kubernetes cluster with rotated certificates" )
2018-11-07 23:54:08 +00:00
clusterState , err := cluster . ReadStateFile ( ctx , cluster . GetStateFilePath ( flags . ClusterFilePath , flags . ConfigDir ) )
2018-08-20 04:37:04 +00:00
if err != nil {
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
2018-08-20 04:37:04 +00:00
}
2019-10-03 01:56:39 +00:00
kubeCluster , err := cluster . InitClusterObject ( ctx , clusterState . DesiredState . RancherKubernetesEngineConfig . DeepCopy ( ) , flags , clusterState . DesiredState . EncryptionConfig )
2018-11-03 01:45:23 +00:00
if err != nil {
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
2018-11-03 01:45:23 +00:00
}
2018-11-07 23:54:08 +00:00
if err := kubeCluster . SetupDialers ( ctx , dialersOptions ) ; err != nil {
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
2018-11-03 01:45:23 +00:00
}
2018-11-07 23:54:08 +00:00
if err := kubeCluster . TunnelHosts ( ctx , flags ) ; err != nil {
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
2018-08-20 04:37:04 +00:00
}
2018-11-12 23:24:59 +00:00
if err := cluster . SetUpAuthentication ( ctx , kubeCluster , nil , clusterState ) ; err != nil {
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
2018-08-20 04:37:04 +00:00
}
2019-12-04 19:22:23 +00:00
if len ( kubeCluster . ControlPlaneHosts ) > 0 {
APIURL = fmt . Sprintf ( "https://%s:6443" , kubeCluster . ControlPlaneHosts [ 0 ] . Address )
}
2018-11-19 19:30:45 +00:00
clientCert = string ( cert . EncodeCertPEM ( kubeCluster . Certificates [ pki . KubeAdminCertName ] . Certificate ) )
clientKey = string ( cert . EncodePrivateKeyPEM ( kubeCluster . Certificates [ pki . KubeAdminCertName ] . Key ) )
caCrt = string ( cert . EncodeCertPEM ( kubeCluster . Certificates [ pki . CACertName ] . Certificate ) )
2018-08-20 04:37:04 +00:00
2019-01-14 17:51:20 +00:00
if err := kubeCluster . SetUpHosts ( ctx , flags ) ; err != nil {
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
2018-08-20 04:37:04 +00:00
}
2019-11-11 22:29:24 +00:00
2018-11-12 23:24:59 +00:00
// Save new State
2019-11-11 22:29:24 +00:00
if err := saveClusterState ( ctx , kubeCluster , clusterState ) ; err != nil {
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
2018-08-20 04:37:04 +00:00
}
// Restarting Kubernetes components
servicesMap := make ( map [ string ] bool )
2018-11-19 19:30:45 +00:00
for _ , component := range kubeCluster . RotateCertificates . Services {
2018-08-20 04:37:04 +00:00
servicesMap [ component ] = true
}
2018-11-19 19:30:45 +00:00
if len ( kubeCluster . RotateCertificates . Services ) == 0 || kubeCluster . RotateCertificates . CACertificates || servicesMap [ services . EtcdContainerName ] {
2018-08-20 04:37:04 +00:00
if err := services . RestartEtcdPlane ( ctx , kubeCluster . EtcdHosts ) ; err != nil {
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
2018-08-20 04:37:04 +00:00
}
}
2019-03-09 02:09:16 +00:00
isLegacyKubeAPI , err := cluster . IsLegacyKubeAPI ( ctx , kubeCluster )
if err != nil {
return APIURL , caCrt , clientCert , clientKey , nil , err
}
if isLegacyKubeAPI {
log . Infof ( ctx , "[controlplane] Redeploying controlplane to update kubeapi parameters" )
2020-02-26 21:33:22 +00:00
if _ , err := kubeCluster . DeployControlPlane ( ctx , svcOptionData , true ) ; err != nil {
2019-03-09 02:09:16 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
}
}
2018-08-20 04:37:04 +00:00
if err := services . RestartControlPlane ( ctx , kubeCluster . ControlPlaneHosts ) ; err != nil {
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
2018-08-20 04:37:04 +00:00
}
allHosts := hosts . GetUniqueHostList ( kubeCluster . EtcdHosts , kubeCluster . ControlPlaneHosts , kubeCluster . WorkerHosts )
if err := services . RestartWorkerPlane ( ctx , allHosts ) ; err != nil {
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , nil , err
2018-08-20 04:37:04 +00:00
}
2018-11-19 19:30:45 +00:00
if kubeCluster . RotateCertificates . CACertificates {
if err := cluster . RestartClusterPods ( ctx , kubeCluster ) ; err != nil {
return APIURL , caCrt , clientCert , clientKey , nil , err
}
2018-08-20 04:37:04 +00:00
}
2018-11-19 19:30:45 +00:00
return APIURL , caCrt , clientCert , clientKey , kubeCluster . Certificates , nil
2018-08-20 04:37:04 +00:00
}
2018-11-12 23:24:59 +00:00
2019-11-11 22:29:24 +00:00
func saveClusterState ( ctx context . Context , kubeCluster * cluster . Cluster , clusterState * cluster . FullState ) error {
var err error
if err = kubeCluster . UpdateClusterCurrentState ( ctx , clusterState ) ; err != nil {
return err
}
// Attempt to store cluster full state to Kubernetes
for i := 1 ; i <= 3 ; i ++ {
err = cluster . SaveFullStateToKubernetes ( ctx , kubeCluster , clusterState )
if err != nil {
time . Sleep ( time . Second * time . Duration ( 2 ) )
continue
}
break
}
if err != nil {
logrus . Warnf ( "Failed to save full cluster state to Kubernetes" )
}
return nil
}
2018-11-19 19:30:45 +00:00
func rotateRKECertificates ( ctx context . Context , kubeCluster * cluster . Cluster , flags cluster . ExternalFlags , rkeFullState * cluster . FullState ) ( * cluster . FullState , error ) {
2018-11-12 23:24:59 +00:00
log . Infof ( ctx , "Rotating Kubernetes cluster certificates" )
2018-11-19 19:30:45 +00:00
currentCluster , err := kubeCluster . GetClusterState ( ctx , rkeFullState )
2018-11-12 23:24:59 +00:00
if err != nil {
2018-11-19 19:30:45 +00:00
return nil , err
2018-11-12 23:24:59 +00:00
}
if currentCluster == nil {
2018-11-19 19:30:45 +00:00
return nil , fmt . Errorf ( "Failed to rotate certificates: can't find old certificates" )
2018-11-12 23:24:59 +00:00
}
2018-11-19 19:30:45 +00:00
currentCluster . RotateCertificates = kubeCluster . RotateCertificates
2020-03-18 20:12:53 +00:00
if ! kubeCluster . RotateCertificates . CACertificates {
caCertPKI , ok := rkeFullState . CurrentState . CertificatesBundle [ pki . CACertName ]
if ! ok {
return nil , fmt . Errorf ( "Failed to rotate certificates: can't find CA certificate" )
}
caCert := caCertPKI . Certificate
if caCert == nil {
return nil , fmt . Errorf ( "Failed to rotate certificates: CA certificate is nil" )
}
certPool := x509 . NewCertPool ( )
certPool . AddCert ( caCert )
if _ , err := caCert . Verify ( x509 . VerifyOptions { Roots : certPool , KeyUsages : [ ] x509 . ExtKeyUsage { x509 . ExtKeyUsageClientAuth } } ) ; err != nil {
return nil , fmt . Errorf ( "Failed to rotate certificates: CA certificate is invalid, please use the --rotate-ca flag to rotate CA certificate, error: %v" , err )
}
}
2018-11-19 19:30:45 +00:00
if err := cluster . RotateRKECertificates ( ctx , currentCluster , flags , rkeFullState ) ; err != nil {
return nil , err
2018-11-12 23:24:59 +00:00
}
2019-04-03 22:25:24 +00:00
rkeFullState . DesiredState . RancherKubernetesEngineConfig = & kubeCluster . RancherKubernetesEngineConfig
2018-11-19 19:30:45 +00:00
return rkeFullState , nil
2018-11-12 23:24:59 +00:00
}
2019-01-02 23:02:34 +00:00
func GenerateRKECSRs ( ctx context . Context , rkeConfig * v3 . RancherKubernetesEngineConfig , flags cluster . ExternalFlags ) error {
log . Infof ( ctx , "Generating Kubernetes cluster CSR certificates" )
if len ( flags . CertificateDir ) == 0 {
flags . CertificateDir = cluster . GetCertificateDirPath ( flags . ClusterFilePath , flags . ConfigDir )
}
certBundle , err := pki . ReadCSRsAndKeysFromDir ( flags . CertificateDir )
if err != nil {
return err
}
// initialze the cluster object from the config file
2019-10-03 01:56:39 +00:00
kubeCluster , err := cluster . InitClusterObject ( ctx , rkeConfig , flags , "" )
2019-01-02 23:02:34 +00:00
if err != nil {
return err
}
// Generating csrs for kubernetes components
if err := pki . GenerateRKEServicesCSRs ( ctx , certBundle , kubeCluster . RancherKubernetesEngineConfig ) ; err != nil {
return err
}
return pki . WriteCertificates ( kubeCluster . CertificateDir , certBundle )
}