2017-10-31 13:55:35 +00:00
|
|
|
package pki
|
|
|
|
|
|
|
|
import (
|
2018-01-09 22:10:56 +00:00
|
|
|
"context"
|
2017-10-31 13:55:35 +00:00
|
|
|
"crypto/rsa"
|
|
|
|
"crypto/x509"
|
2018-02-06 19:25:54 +00:00
|
|
|
"fmt"
|
2017-10-31 13:55:35 +00:00
|
|
|
"net"
|
2018-05-29 15:21:24 +00:00
|
|
|
"path"
|
|
|
|
"strings"
|
2017-10-31 13:55:35 +00:00
|
|
|
|
2018-05-29 15:21:24 +00:00
|
|
|
"github.com/docker/docker/api/types/container"
|
|
|
|
"github.com/rancher/rke/docker"
|
2017-10-31 13:55:35 +00:00
|
|
|
"github.com/rancher/rke/hosts"
|
2018-01-09 22:10:56 +00:00
|
|
|
"github.com/rancher/rke/log"
|
2018-02-06 19:25:54 +00:00
|
|
|
"github.com/rancher/types/apis/management.cattle.io/v3"
|
2017-10-31 13:55:35 +00:00
|
|
|
"k8s.io/client-go/util/cert"
|
|
|
|
)
|
|
|
|
|
|
|
|
type CertificatePKI struct {
|
2017-11-10 02:39:10 +00:00
|
|
|
Certificate *x509.Certificate
|
|
|
|
Key *rsa.PrivateKey
|
|
|
|
Config string
|
|
|
|
Name string
|
|
|
|
CommonName string
|
|
|
|
OUName string
|
|
|
|
EnvName string
|
|
|
|
Path string
|
|
|
|
KeyEnvName string
|
|
|
|
KeyPath string
|
|
|
|
ConfigEnvName string
|
|
|
|
ConfigPath string
|
2017-10-31 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
2018-02-06 19:25:54 +00:00
|
|
|
const (
|
2018-05-29 15:21:24 +00:00
|
|
|
etcdRole = "etcd"
|
|
|
|
controlRole = "controlplane"
|
|
|
|
workerRole = "worker"
|
|
|
|
BundleCertContainer = "rke-bundle-cert"
|
2018-02-06 19:25:54 +00:00
|
|
|
)
|
2017-10-31 13:55:35 +00:00
|
|
|
|
2018-02-06 19:25:54 +00:00
|
|
|
func GenerateRKECerts(ctx context.Context, rkeConfig v3.RancherKubernetesEngineConfig, configPath, configDir string) (map[string]CertificatePKI, error) {
|
2017-10-31 13:55:35 +00:00
|
|
|
certs := make(map[string]CertificatePKI)
|
|
|
|
// generate CA certificate and key
|
2018-01-09 22:10:56 +00:00
|
|
|
log.Infof(ctx, "[certificates] Generating CA kubernetes certificates")
|
2018-07-17 18:19:08 +00:00
|
|
|
caCrt, caKey, err := GenerateCACertAndKey(CACertName)
|
2017-10-31 13:55:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-16 23:10:14 +00:00
|
|
|
certs[CACertName] = ToCertObject(CACertName, "", "", caCrt, caKey)
|
2017-10-31 13:55:35 +00:00
|
|
|
|
|
|
|
// generate API certificate and key
|
2018-01-09 22:10:56 +00:00
|
|
|
log.Infof(ctx, "[certificates] Generating Kubernetes API server certificates")
|
2018-02-06 19:25:54 +00:00
|
|
|
kubernetesServiceIP, err := GetKubernetesServiceIP(rkeConfig.Services.KubeAPI.ServiceClusterIPRange)
|
|
|
|
clusterDomain := rkeConfig.Services.Kubelet.ClusterDomain
|
|
|
|
cpHosts := hosts.NodesToHosts(rkeConfig.Nodes, controlRole)
|
2018-07-17 18:19:08 +00:00
|
|
|
etcdHosts := hosts.NodesToHosts(rkeConfig.Nodes, etcdRole)
|
2018-02-06 19:25:54 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Failed to get Kubernetes Service IP: %v", err)
|
|
|
|
}
|
2018-03-22 18:36:25 +00:00
|
|
|
kubeAPIAltNames := GetAltNames(cpHosts, clusterDomain, kubernetesServiceIP, rkeConfig.Authentication.SANs)
|
2018-01-16 23:10:14 +00:00
|
|
|
kubeAPICrt, kubeAPIKey, err := GenerateSignedCertAndKey(caCrt, caKey, true, KubeAPICertName, kubeAPIAltNames, nil, nil)
|
2017-10-31 13:55:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-16 23:10:14 +00:00
|
|
|
certs[KubeAPICertName] = ToCertObject(KubeAPICertName, "", "", kubeAPICrt, kubeAPIKey)
|
2017-10-31 13:55:35 +00:00
|
|
|
|
|
|
|
// generate Kube controller-manager certificate and key
|
2018-01-09 22:10:56 +00:00
|
|
|
log.Infof(ctx, "[certificates] Generating Kube Controller certificates")
|
2018-01-16 23:10:14 +00:00
|
|
|
kubeControllerCrt, kubeControllerKey, err := GenerateSignedCertAndKey(caCrt, caKey, false, getDefaultCN(KubeControllerCertName), nil, nil, nil)
|
2017-10-31 13:55:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-16 23:10:14 +00:00
|
|
|
certs[KubeControllerCertName] = ToCertObject(KubeControllerCertName, "", "", kubeControllerCrt, kubeControllerKey)
|
2017-10-31 13:55:35 +00:00
|
|
|
|
|
|
|
// generate Kube scheduler certificate and key
|
2018-01-09 22:10:56 +00:00
|
|
|
log.Infof(ctx, "[certificates] Generating Kube Scheduler certificates")
|
2018-01-16 23:10:14 +00:00
|
|
|
kubeSchedulerCrt, kubeSchedulerKey, err := GenerateSignedCertAndKey(caCrt, caKey, false, getDefaultCN(KubeSchedulerCertName), nil, nil, nil)
|
2017-10-31 13:55:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-16 23:10:14 +00:00
|
|
|
certs[KubeSchedulerCertName] = ToCertObject(KubeSchedulerCertName, "", "", kubeSchedulerCrt, kubeSchedulerKey)
|
2017-10-31 13:55:35 +00:00
|
|
|
|
|
|
|
// generate Kube Proxy certificate and key
|
2018-01-09 22:10:56 +00:00
|
|
|
log.Infof(ctx, "[certificates] Generating Kube Proxy certificates")
|
2018-01-16 23:10:14 +00:00
|
|
|
kubeProxyCrt, kubeProxyKey, err := GenerateSignedCertAndKey(caCrt, caKey, false, getDefaultCN(KubeProxyCertName), nil, nil, nil)
|
2017-10-31 13:55:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-16 23:10:14 +00:00
|
|
|
certs[KubeProxyCertName] = ToCertObject(KubeProxyCertName, "", "", kubeProxyCrt, kubeProxyKey)
|
2017-10-31 13:55:35 +00:00
|
|
|
|
2018-01-09 22:10:56 +00:00
|
|
|
log.Infof(ctx, "[certificates] Generating Node certificate")
|
2018-01-16 23:10:14 +00:00
|
|
|
nodeCrt, nodeKey, err := GenerateSignedCertAndKey(caCrt, caKey, false, KubeNodeCommonName, nil, nil, []string{KubeNodeOrganizationName})
|
2017-10-31 13:55:35 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-01-16 23:10:14 +00:00
|
|
|
certs[KubeNodeCertName] = ToCertObject(KubeNodeCertName, KubeNodeCommonName, KubeNodeOrganizationName, nodeCrt, nodeKey)
|
2017-11-01 21:46:43 +00:00
|
|
|
|
2018-01-16 23:10:14 +00:00
|
|
|
// generate Admin certificate and key
|
2018-02-01 21:28:31 +00:00
|
|
|
log.Infof(ctx, "[certificates] Generating admin certificates and kubeconfig")
|
2018-02-06 19:25:54 +00:00
|
|
|
if len(configPath) == 0 {
|
|
|
|
configPath = ClusterConfig
|
|
|
|
}
|
|
|
|
localKubeConfigPath := GetLocalKubeConfig(configPath, configDir)
|
2018-01-16 23:10:14 +00:00
|
|
|
kubeAdminCrt, kubeAdminKey, err := GenerateSignedCertAndKey(caCrt, caKey, false, KubeAdminCertName, nil, nil, []string{KubeAdminOrganizationName})
|
2017-10-31 13:55:35 +00:00
|
|
|
if err != nil {
|
2018-01-16 23:10:14 +00:00
|
|
|
return nil, err
|
2017-10-31 13:55:35 +00:00
|
|
|
}
|
2018-01-16 23:10:14 +00:00
|
|
|
kubeAdminCertObj := ToCertObject(KubeAdminCertName, KubeAdminCertName, KubeAdminOrganizationName, kubeAdminCrt, kubeAdminKey)
|
2018-02-15 03:25:36 +00:00
|
|
|
if len(cpHosts) > 0 {
|
|
|
|
kubeAdminConfig := GetKubeConfigX509WithData(
|
|
|
|
"https://"+cpHosts[0].Address+":6443",
|
2018-03-14 00:18:07 +00:00
|
|
|
rkeConfig.ClusterName,
|
2018-02-15 03:25:36 +00:00
|
|
|
KubeAdminCertName,
|
|
|
|
string(cert.EncodeCertPEM(caCrt)),
|
|
|
|
string(cert.EncodeCertPEM(kubeAdminCrt)),
|
|
|
|
string(cert.EncodePrivateKeyPEM(kubeAdminKey)))
|
|
|
|
kubeAdminCertObj.Config = kubeAdminConfig
|
|
|
|
kubeAdminCertObj.ConfigPath = localKubeConfigPath
|
|
|
|
} else {
|
|
|
|
kubeAdminCertObj.Config = ""
|
|
|
|
}
|
2018-01-16 23:10:14 +00:00
|
|
|
certs[KubeAdminCertName] = kubeAdminCertObj
|
2018-02-06 19:25:54 +00:00
|
|
|
// generate etcd certificate and key
|
2018-02-14 20:58:35 +00:00
|
|
|
if len(rkeConfig.Services.Etcd.ExternalURLs) > 0 {
|
|
|
|
clientCert, err := cert.ParseCertsPEM([]byte(rkeConfig.Services.Etcd.Cert))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
clientKey, err := cert.ParsePrivateKeyPEM([]byte(rkeConfig.Services.Etcd.Key))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
certs[EtcdClientCertName] = ToCertObject(EtcdClientCertName, "", "", clientCert[0], clientKey.(*rsa.PrivateKey))
|
|
|
|
|
|
|
|
caCert, err := cert.ParseCertsPEM([]byte(rkeConfig.Services.Etcd.CACert))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
certs[EtcdClientCACertName] = ToCertObject(EtcdClientCACertName, "", "", caCert[0], nil)
|
|
|
|
}
|
2018-03-22 18:36:25 +00:00
|
|
|
etcdAltNames := GetAltNames(etcdHosts, clusterDomain, kubernetesServiceIP, []string{})
|
2018-01-16 23:10:14 +00:00
|
|
|
for _, host := range etcdHosts {
|
2018-02-01 21:28:31 +00:00
|
|
|
log.Infof(ctx, "[certificates] Generating etcd-%s certificate and key", host.InternalAddress)
|
2018-01-16 23:10:14 +00:00
|
|
|
etcdCrt, etcdKey, err := GenerateSignedCertAndKey(caCrt, caKey, true, EtcdCertName, etcdAltNames, nil, nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
etcdName := GetEtcdCrtName(host.InternalAddress)
|
|
|
|
certs[etcdName] = ToCertObject(etcdName, "", "", etcdCrt, etcdKey)
|
2017-10-31 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
2018-06-22 03:00:12 +00:00
|
|
|
// generate request header client CA certificate and key
|
|
|
|
log.Infof(ctx, "[certificates] Generating Kubernetes API server aggregation layer requestheader client CA certificates")
|
2018-07-17 18:19:08 +00:00
|
|
|
requestHeaderCACrt, requestHeaderCAKey, err := GenerateCACertAndKey(RequestHeaderCACertName)
|
2018-06-22 03:00:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
certs[RequestHeaderCACertName] = ToCertObject(RequestHeaderCACertName, "", "", requestHeaderCACrt, requestHeaderCAKey)
|
|
|
|
|
|
|
|
//generate API server proxy client key and certs
|
2018-07-21 19:46:05 +00:00
|
|
|
log.Infof(ctx, "[certificates] Generating Kubernetes API server proxy client certificates")
|
2018-07-17 18:19:08 +00:00
|
|
|
apiserverProxyClientCrt, apiserverProxyClientKey, err := GenerateSignedCertAndKey(requestHeaderCACrt, requestHeaderCAKey, true, APIProxyClientCertName, nil, nil, nil)
|
2018-06-22 03:00:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
certs[APIProxyClientCertName] = ToCertObject(APIProxyClientCertName, "", "", apiserverProxyClientCrt, apiserverProxyClientKey)
|
|
|
|
|
2018-01-16 23:10:14 +00:00
|
|
|
return certs, nil
|
2017-10-31 13:55:35 +00:00
|
|
|
}
|
|
|
|
|
2018-02-06 19:25:54 +00:00
|
|
|
func GenerateRKENodeCerts(ctx context.Context, rkeConfig v3.RancherKubernetesEngineConfig, nodeAddress string, certBundle map[string]CertificatePKI) map[string]CertificatePKI {
|
|
|
|
crtMap := make(map[string]CertificatePKI)
|
|
|
|
crtKeys := []string{}
|
2018-02-23 22:37:18 +00:00
|
|
|
removeCAKey := true
|
2018-02-06 19:25:54 +00:00
|
|
|
for _, node := range rkeConfig.Nodes {
|
|
|
|
if node.Address == nodeAddress {
|
|
|
|
for _, role := range node.Role {
|
|
|
|
switch role {
|
|
|
|
case controlRole:
|
|
|
|
keys := getControlCertKeys()
|
|
|
|
crtKeys = append(crtKeys, keys...)
|
2018-02-23 22:37:18 +00:00
|
|
|
removeCAKey = false
|
2018-02-06 19:25:54 +00:00
|
|
|
case workerRole:
|
|
|
|
keys := getWorkerCertKeys()
|
|
|
|
crtKeys = append(crtKeys, keys...)
|
|
|
|
case etcdRole:
|
|
|
|
keys := getEtcdCertKeys(rkeConfig.Nodes, etcdRole)
|
|
|
|
crtKeys = append(crtKeys, keys...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for _, key := range crtKeys {
|
|
|
|
crtMap[key] = certBundle[key]
|
|
|
|
}
|
2018-02-23 22:37:18 +00:00
|
|
|
if removeCAKey {
|
|
|
|
caCert := crtMap[CACertName]
|
|
|
|
caCert.Key = nil
|
|
|
|
caCert.KeyEnvName = ""
|
|
|
|
caCert.KeyPath = ""
|
|
|
|
crtMap[CACertName] = caCert
|
|
|
|
}
|
2018-02-06 19:25:54 +00:00
|
|
|
return crtMap
|
|
|
|
}
|
|
|
|
|
2018-01-16 23:10:14 +00:00
|
|
|
func RegenerateEtcdCertificate(
|
|
|
|
ctx context.Context,
|
|
|
|
crtMap map[string]CertificatePKI,
|
|
|
|
etcdHost *hosts.Host,
|
|
|
|
etcdHosts []*hosts.Host,
|
|
|
|
clusterDomain string,
|
|
|
|
KubernetesServiceIP net.IP) (map[string]CertificatePKI, error) {
|
2017-10-31 13:55:35 +00:00
|
|
|
|
2018-01-16 23:10:14 +00:00
|
|
|
log.Infof(ctx, "[certificates] Regenerating new etcd-%s certificate and key", etcdHost.InternalAddress)
|
|
|
|
caCrt := crtMap[CACertName].Certificate
|
|
|
|
caKey := crtMap[CACertName].Key
|
2018-03-22 18:36:25 +00:00
|
|
|
etcdAltNames := GetAltNames(etcdHosts, clusterDomain, KubernetesServiceIP, []string{})
|
2017-10-31 13:55:35 +00:00
|
|
|
|
2018-01-16 23:10:14 +00:00
|
|
|
etcdCrt, etcdKey, err := GenerateSignedCertAndKey(caCrt, caKey, true, EtcdCertName, etcdAltNames, nil, nil)
|
2017-11-26 22:29:52 +00:00
|
|
|
if err != nil {
|
2018-01-16 23:10:14 +00:00
|
|
|
return nil, err
|
2017-11-10 02:39:10 +00:00
|
|
|
}
|
2018-01-16 23:10:14 +00:00
|
|
|
etcdName := GetEtcdCrtName(etcdHost.InternalAddress)
|
|
|
|
crtMap[etcdName] = ToCertObject(etcdName, "", "", etcdCrt, etcdKey)
|
|
|
|
log.Infof(ctx, "[certificates] Successfully generated new etcd-%s certificate and key", etcdHost.InternalAddress)
|
|
|
|
return crtMap, nil
|
2017-11-10 02:39:10 +00:00
|
|
|
}
|
2018-05-29 15:21:24 +00:00
|
|
|
|
|
|
|
func SaveBackupBundleOnHost(ctx context.Context, host *hosts.Host, alpineSystemImage, etcdSnapshotPath string, prsMap map[string]v3.PrivateRegistry) error {
|
|
|
|
imageCfg := &container.Config{
|
|
|
|
Cmd: []string{
|
|
|
|
"sh",
|
|
|
|
"-c",
|
2018-07-27 18:29:28 +00:00
|
|
|
fmt.Sprintf("if [ -d %s ] && [ \"$(ls -A %s)\" ]; then tar czvf %s %s;fi", TempCertPath, TempCertPath, BundleCertPath, TempCertPath),
|
2018-05-29 15:21:24 +00:00
|
|
|
},
|
|
|
|
Image: alpineSystemImage,
|
|
|
|
}
|
|
|
|
hostCfg := &container.HostConfig{
|
|
|
|
|
|
|
|
Binds: []string{
|
|
|
|
fmt.Sprintf("%s:/etc/kubernetes:z", path.Join(host.PrefixPath, "/etc/kubernetes")),
|
|
|
|
fmt.Sprintf("%s:/backup:z", etcdSnapshotPath),
|
|
|
|
},
|
|
|
|
Privileged: true,
|
|
|
|
}
|
|
|
|
if err := docker.DoRunContainer(ctx, host.DClient, imageCfg, hostCfg, BundleCertContainer, host.Address, "certificates", prsMap); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
status, err := docker.WaitForContainer(ctx, host.DClient, host.Address, BundleCertContainer)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if status != 0 {
|
|
|
|
return fmt.Errorf("Failed to run certificate bundle compress, exit status is: %d", status)
|
|
|
|
}
|
2018-07-18 20:44:55 +00:00
|
|
|
log.Infof(ctx, "[certificates] successfully saved certificate bundle [%s/pki.bundle.tar.gz] on host [%s]", etcdSnapshotPath, host.Address)
|
2018-05-29 15:21:24 +00:00
|
|
|
return docker.RemoveContainer(ctx, host.DClient, host.Address, BundleCertContainer)
|
|
|
|
}
|
|
|
|
|
|
|
|
func ExtractBackupBundleOnHost(ctx context.Context, host *hosts.Host, alpineSystemImage, etcdSnapshotPath string, prsMap map[string]v3.PrivateRegistry) error {
|
|
|
|
imageCfg := &container.Config{
|
|
|
|
Cmd: []string{
|
|
|
|
"sh",
|
|
|
|
"-c",
|
2018-07-18 20:44:55 +00:00
|
|
|
fmt.Sprintf(
|
|
|
|
"mkdir -p %s; tar xzvf %s -C %s --strip-components %d",
|
2018-07-27 18:29:28 +00:00
|
|
|
TempCertPath,
|
2018-07-18 20:44:55 +00:00
|
|
|
BundleCertPath,
|
2018-07-27 18:29:28 +00:00
|
|
|
TempCertPath,
|
|
|
|
len(strings.Split(TempCertPath, "/"))-1),
|
2018-05-29 15:21:24 +00:00
|
|
|
},
|
|
|
|
Image: alpineSystemImage,
|
|
|
|
}
|
|
|
|
hostCfg := &container.HostConfig{
|
|
|
|
|
|
|
|
Binds: []string{
|
|
|
|
fmt.Sprintf("%s:/etc/kubernetes:z", path.Join(host.PrefixPath, "/etc/kubernetes")),
|
|
|
|
fmt.Sprintf("%s:/backup:z", etcdSnapshotPath),
|
|
|
|
},
|
|
|
|
Privileged: true,
|
|
|
|
}
|
|
|
|
if err := docker.DoRunContainer(ctx, host.DClient, imageCfg, hostCfg, BundleCertContainer, host.Address, "certificates", prsMap); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
status, err := docker.WaitForContainer(ctx, host.DClient, host.Address, BundleCertContainer)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if status != 0 {
|
2018-07-21 07:14:45 +00:00
|
|
|
containerLog, err := docker.GetContainerLogsStdoutStderr(ctx, host.DClient, BundleCertContainer, "5", false)
|
2018-07-18 20:44:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// removing the container in case of an error too
|
|
|
|
if err := docker.RemoveContainer(ctx, host.DClient, host.Address, BundleCertContainer); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return fmt.Errorf("Failed to run certificate bundle extract, exit status is: %d, container logs: %s", status, containerLog)
|
2018-05-29 15:21:24 +00:00
|
|
|
}
|
2018-07-27 18:29:28 +00:00
|
|
|
log.Infof(ctx, "[certificates] successfully extracted certificate bundle on host [%s] to backup path [%s]", host.Address, TempCertPath)
|
2018-05-29 15:21:24 +00:00
|
|
|
return docker.RemoveContainer(ctx, host.DClient, host.Address, BundleCertContainer)
|
|
|
|
}
|