diff --git a/cmd/kubeadm/app/phases/certs/BUILD b/cmd/kubeadm/app/phases/certs/BUILD index 23eeb3ea539..846ac4efad0 100644 --- a/cmd/kubeadm/app/phases/certs/BUILD +++ b/cmd/kubeadm/app/phases/certs/BUILD @@ -8,7 +8,10 @@ load( go_test( name = "go_default_test", - srcs = ["certs_test.go"], + srcs = [ + "certlist_test.go", + "certs_test.go", + ], embed = [":go_default_library"], deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", @@ -16,12 +19,14 @@ go_test( "//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library", "//cmd/kubeadm/test:go_default_library", "//cmd/kubeadm/test/certs:go_default_library", + "//staging/src/k8s.io/client-go/util/cert:go_default_library", ], ) go_library( name = "go_default_library", srcs = [ + "certlist.go", "certs.go", "doc.go", ], diff --git a/cmd/kubeadm/app/phases/certs/certlist.go b/cmd/kubeadm/app/phases/certs/certlist.go new file mode 100644 index 00000000000..fc85252fedc --- /dev/null +++ b/cmd/kubeadm/app/phases/certs/certlist.go @@ -0,0 +1,303 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package certs + +import ( + "crypto/rsa" + "crypto/x509" + "fmt" + + certutil "k8s.io/client-go/util/cert" + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" + kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil" +) + +type configMutatorsFunc func(*kubeadmapi.InitConfiguration, *certutil.Config) error + +// KubeadmCert represents a certificate that Kubeadm will create to function properly. +type KubeadmCert struct { + Name string + BaseName string + CAName string + // Some attributes will depend on the InitConfiguration, only known at runtime. + // These functions will be run in series, passed both the InitConfiguration and a cert Config. + configMutators []configMutatorsFunc + config certutil.Config +} + +// GetConfig returns the definition for the given cert given the provided InitConfiguration +func (k *KubeadmCert) GetConfig(ic *kubeadmapi.InitConfiguration) (*certutil.Config, error) { + for _, f := range k.configMutators { + if err := f(ic, &k.config); err != nil { + return nil, err + } + } + + return &k.config, nil +} + +// CreateFromCA makes and writes a certificate using the given CA cert and key. +func (k *KubeadmCert) CreateFromCA(ic *kubeadmapi.InitConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey) error { + cfg, err := k.GetConfig(ic) + if err != nil { + return fmt.Errorf("couldn't create %q certificate: %v", k.Name, err) + } + cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, *cfg) + if err != nil { + return err + } + writeCertificateAuthorithyFilesIfNotExist( + ic.CertificatesDir, + k.BaseName, + cert, + key, + ) + + return nil +} + +// CertificateTree is represents a one-level-deep tree, mapping a CA to the certs that depend on it. +type CertificateTree map[*KubeadmCert]Certificates + +// CreateTree creates the CAs, certs signed by the CAs, and writes them all to disk. +func (t CertificateTree) CreateTree(ic *kubeadmapi.InitConfiguration) error { + for ca, leaves := range t { + // TODO: NewCACertAndKey should take an ic + caCert, caKey, err := NewCACertAndKey() + if err != nil { + return err + } + + for _, leaf := range leaves { + if err := leaf.CreateFromCA(ic, caCert, caKey); err != nil { + return err + } + } + + if err := writeCertificateAuthorithyFilesIfNotExist( + ic.CertificatesDir, + ca.BaseName, + caCert, + caKey, + ); err != nil { + return err + } + } + return nil +} + +// CertificateMap is a flat map of certificates, keyed by Name. +type CertificateMap map[string]*KubeadmCert + +// CertTree returns a one-level-deep tree, mapping a CA cert to an array of certificates that should be signed by it. +func (m CertificateMap) CertTree() (CertificateTree, error) { + caMap := make(CertificateTree) + + for _, cert := range m { + if cert.CAName == "" { + if _, ok := caMap[cert]; !ok { + caMap[cert] = []*KubeadmCert{} + } + } else { + ca, ok := m[cert.CAName] + if !ok { + return nil, fmt.Errorf("Certificate %q references unknown CA %q", cert.Name, cert.CAName) + } + caMap[ca] = append(caMap[ca], cert) + } + } + + return caMap, nil +} + +// Certificates is a list of Certificates that Kubeadm should create. +type Certificates []*KubeadmCert + +// AsMap returns the list of certificates as a map, keyed by name. +func (c Certificates) AsMap() CertificateMap { + certMap := make(map[string]*KubeadmCert) + for _, cert := range c { + certMap[cert.Name] = cert + } + + return certMap +} + +// GetDefaultCertList returns all of the certificates kubeadm requires to function. +func GetDefaultCertList() Certificates { + return Certificates{ + &KubeadmCertRootCA, + &KubeadmCertAPIServer, + &KubeadmCertKubeletClient, + // Front Proxy certs + &KubeadmCertFrontProxyCA, + &KubeadmCertFrontProxyClient, + // etcd certs + &KubeadmCertEtcdCA, + &KubeadmCertEtcdServer, + &KubeadmCertEtcdPeer, + &KubeadmCertEtcdHealthcheck, + &KubeadmCertEtcdAPIClient, + } +} + +// GetCertsWithoutEtcd returns all of the certificates kubeadm needs when etcd is hosted externally. +func GetCertsWithoutEtcd() Certificates { + return Certificates{ + &KubeadmCertRootCA, + &KubeadmCertAPIServer, + &KubeadmCertKubeletClient, + // Front Proxy certs + &KubeadmCertFrontProxyCA, + &KubeadmCertFrontProxyClient, + } +} + +var ( + // KubeadmCertRootCA is the definition of the Kubernetes Root CA for the API Server and kubelet. + KubeadmCertRootCA = KubeadmCert{ + Name: "root-ca", + BaseName: kubeadmconstants.CACertAndKeyBaseName, + config: certutil.Config{ + CommonName: "kubernetes", + }, + } + // KubeadmCertAPIServer is the definition of the cert used to serve the kubernetes API. + KubeadmCertAPIServer = KubeadmCert{ + Name: "api-server", + BaseName: kubeadmconstants.APIServerCertAndKeyBaseName, + CAName: "root-ca", + config: certutil.Config{ + CommonName: kubeadmconstants.APIServerCertCommonName, + Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + }, + configMutators: []configMutatorsFunc{ + makeAltNamesMutator(pkiutil.GetAPIServerAltNames), + }, + } + // KubeadmCertKubeletClient is the definition of the cert used by the API server to access the kubelet. + KubeadmCertKubeletClient = KubeadmCert{ + Name: "api-server-kubelet-client", + BaseName: kubeadmconstants.APIServerKubeletClientCertAndKeyBaseName, + CAName: "root-ca", + config: certutil.Config{ + CommonName: kubeadmconstants.APIServerKubeletClientCertCommonName, + Organization: []string{kubeadmconstants.MastersGroup}, + Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }, + } + + // KubeadmCertFrontProxyCA is the definition of the CA used for the front end proxy. + KubeadmCertFrontProxyCA = KubeadmCert{ + Name: "front-proxy-ca", + BaseName: kubeadmconstants.FrontProxyCACertAndKeyBaseName, + config: certutil.Config{ + CommonName: "front-proxy-ca", + }, + } + + // KubeadmCertFrontProxyClient is the definition of the cert used by the API server to access the front proxy. + KubeadmCertFrontProxyClient = KubeadmCert{ + Name: "front-proxy-client", + BaseName: kubeadmconstants.FrontProxyClientCertAndKeyBaseName, + CAName: "front-proxy-ca", + config: certutil.Config{ + CommonName: kubeadmconstants.FrontProxyClientCertCommonName, + Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }, + } + + // KubeadmCertEtcdCA is the definition of the root CA used by the hosted etcd server. + KubeadmCertEtcdCA = KubeadmCert{ + Name: "etcd-ca", + BaseName: kubeadmconstants.EtcdCACertAndKeyBaseName, + config: certutil.Config{ + CommonName: "etcd-ca", + }, + } + // KubeadmCertEtcdServer is the definition of the cert used to serve etcd to clients. + KubeadmCertEtcdServer = KubeadmCert{ + Name: "etcd-server", + BaseName: kubeadmconstants.EtcdServerCertAndKeyBaseName, + CAName: "etcd-ca", + config: certutil.Config{ + // TODO: etcd 3.2 introduced an undocumented requirement for ClientAuth usage on the + // server cert: https://github.com/coreos/etcd/issues/9785#issuecomment-396715692 + // Once the upstream issue is resolved, this should be returned to only allowing + // ServerAuth usage. + Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + }, + configMutators: []configMutatorsFunc{ + makeAltNamesMutator(pkiutil.GetEtcdAltNames), + setCommonNameToNodeName(), + }, + } + // KubeadmCertEtcdPeer is the definition of the cert used by etcd peers to access each other. + KubeadmCertEtcdPeer = KubeadmCert{ + Name: "etcd-peer", + BaseName: kubeadmconstants.EtcdPeerCertAndKeyBaseName, + CAName: "etcd-ca", + config: certutil.Config{ + Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, + }, + configMutators: []configMutatorsFunc{ + makeAltNamesMutator(pkiutil.GetEtcdPeerAltNames), + setCommonNameToNodeName(), + }, + } + // KubeadmCertEtcdHealthcheck is the definition of the cert used by Kubernetes to check the health of the etcd server. + KubeadmCertEtcdHealthcheck = KubeadmCert{ + Name: "etcd-healthcheck", + BaseName: kubeadmconstants.EtcdHealthcheckClientCertAndKeyBaseName, + CAName: "etcd-ca", + config: certutil.Config{ + CommonName: kubeadmconstants.EtcdHealthcheckClientCertCommonName, + Organization: []string{kubeadmconstants.MastersGroup}, + Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }, + } + // KubeadmCertEtcdAPIClient is the definition of the cert used by the API server to access etcd. + KubeadmCertEtcdAPIClient = KubeadmCert{ + Name: "etcd-api-client", + BaseName: kubeadmconstants.APIServerEtcdClientCertAndKeyBaseName, + CAName: "etcd-ca", + config: certutil.Config{ + CommonName: kubeadmconstants.APIServerEtcdClientCertCommonName, + Organization: []string{kubeadmconstants.MastersGroup}, + Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }, + } +) + +func makeAltNamesMutator(f func(*kubeadmapi.InitConfiguration) (*certutil.AltNames, error)) configMutatorsFunc { + return func(mc *kubeadmapi.InitConfiguration, cc *certutil.Config) error { + altNames, err := f(mc) + if err != nil { + return nil + } + cc.AltNames = *altNames + return nil + } +} + +func setCommonNameToNodeName() configMutatorsFunc { + return func(mc *kubeadmapi.InitConfiguration, cc *certutil.Config) error { + cc.CommonName = mc.NodeRegistration.Name + return nil + } +} diff --git a/cmd/kubeadm/app/phases/certs/certlist_test.go b/cmd/kubeadm/app/phases/certs/certlist_test.go new file mode 100644 index 00000000000..a5ef1866c44 --- /dev/null +++ b/cmd/kubeadm/app/phases/certs/certlist_test.go @@ -0,0 +1,185 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package certs + +import ( + "crypto" + "crypto/tls" + "crypto/x509" + "io/ioutil" + "os" + "path" + "testing" + + certutil "k8s.io/client-go/util/cert" + kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" +) + +func TestCAPointersValid(t *testing.T) { + tests := []struct { + certs Certificates + name string + }{ + { + name: "Default Certificate List", + certs: GetDefaultCertList(), + }, + { + name: "Cert list less etcd", + certs: GetCertsWithoutEtcd(), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + + certMap := test.certs.AsMap() + + for _, cert := range test.certs { + if cert.CAName != "" && certMap[cert.CAName] == nil { + t.Errorf("Certificate %q references nonexistent CA %q", cert.Name, cert.CAName) + } + } + }) + } +} + +func TestMakeCertTree(t *testing.T) { + rootCert := &KubeadmCert{ + Name: "root", + } + leaf0 := &KubeadmCert{ + Name: "leaf0", + CAName: "root", + } + leaf1 := &KubeadmCert{ + Name: "leaf1", + CAName: "root", + } + selfSigned := &KubeadmCert{ + Name: "self-signed", + } + + certMap := CertificateMap{ + "root": rootCert, + "leaf0": leaf0, + "leaf1": leaf1, + "self-signed": selfSigned, + } + + orphanCertMap := CertificateMap{ + "leaf0": leaf0, + } + + if _, err := orphanCertMap.CertTree(); err == nil { + t.Error("expected orphan cert map to error, but got nil") + } + + certTree, err := certMap.CertTree() + t.Logf("cert tree: %v", certTree) + if err != nil { + t.Errorf("expected no error, but got %v", err) + } + + if len(certTree) != 2 { + t.Errorf("Expected tree to have 2 roots, got %d", len(certTree)) + } + + if len(certTree[rootCert]) != 2 { + t.Errorf("Expected root to have 2 leaves, got %d", len(certTree[rootCert])) + } + + if _, ok := certTree[selfSigned]; !ok { + t.Error("Expected selfSigned to be present in tree, but missing") + } +} + +func TestCreateCertificateChain(t *testing.T) { + dir, err := ioutil.TempDir("", t.Name()) + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + ic := &kubeadmapi.InitConfiguration{ + CertificatesDir: dir, + NodeRegistration: kubeadmapi.NodeRegistrationOptions{ + Name: "test-node", + }, + } + + caCfg := Certificates{ + { + config: certutil.Config{}, + Name: "test-ca", + BaseName: "test-ca", + }, + { + config: certutil.Config{ + AltNames: certutil.AltNames{ + DNSNames: []string{"test-domain.space"}, + }, + Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }, + configMutators: []configMutatorsFunc{ + setCommonNameToNodeName(), + }, + CAName: "test-ca", + Name: "test-daughter", + BaseName: "test-daughter", + }, + } + + certTree, err := caCfg.AsMap().CertTree() + if err != nil { + t.Fatalf("unexpected error getting tree: %v", err) + } + + if certTree.CreateTree(ic); err != nil { + t.Fatal(err) + } + + caCert, _ := parseCertAndKey(path.Join(dir, "test-ca"), t) + daughterCert, _ := parseCertAndKey(path.Join(dir, "test-daughter"), t) + + pool := x509.NewCertPool() + pool.AddCert(caCert) + + _, err = daughterCert.Verify(x509.VerifyOptions{ + DNSName: "test-domain.space", + Roots: pool, + KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, + }) + if err != nil { + t.Errorf("couldn't verify daughter cert: %v", err) + } + +} + +func parseCertAndKey(basePath string, t *testing.T) (*x509.Certificate, crypto.PrivateKey) { + certPair, err := tls.LoadX509KeyPair(basePath+".crt", basePath+".key") + if err != nil { + t.Fatalf("couldn't parse certificate and key: %v", err) + } + + parsedCert, err := x509.ParseCertificate(certPair.Certificate[0]) + if err != nil { + t.Fatalf("couldn't parse certificate: %v", err) + } + + return parsedCert, certPair.PrivateKey +} diff --git a/cmd/kubeadm/app/phases/certs/certs.go b/cmd/kubeadm/app/phases/certs/certs.go index 40cdd41eca8..b65d2682a5d 100644 --- a/cmd/kubeadm/app/phases/certs/certs.go +++ b/cmd/kubeadm/app/phases/certs/certs.go @@ -35,35 +35,34 @@ import ( // If the PKI assets already exists in the target folder, they are used only if evaluated equal; otherwise an error is returned. func CreatePKIAssets(cfg *kubeadmapi.InitConfiguration) error { glog.V(1).Infoln("creating PKI assets") - certActions := []func(cfg *kubeadmapi.InitConfiguration) error{ - CreateCACertAndKeyFiles, - CreateAPIServerCertAndKeyFiles, - CreateAPIServerKubeletClientCertAndKeyFiles, - CreateServiceAccountKeyAndPublicKeyFiles, - CreateFrontProxyCACertAndKeyFiles, - CreateFrontProxyClientCertAndKeyFiles, - } - etcdCertActions := []func(cfg *kubeadmapi.InitConfiguration) error{ - CreateEtcdCACertAndKeyFiles, - CreateEtcdServerCertAndKeyFiles, - CreateEtcdPeerCertAndKeyFiles, - CreateEtcdHealthcheckClientCertAndKeyFiles, - CreateAPIServerEtcdClientCertAndKeyFiles, + + // This structure cannot handle multilevel CA hierarchies. + // This isn't a problem right now, but may become one in the future. + + var certList Certificates + + if cfg.Etcd.Local == nil { + certList = GetCertsWithoutEtcd() + } else { + certList = GetDefaultCertList() } - if cfg.Etcd.Local != nil { - certActions = append(certActions, etcdCertActions...) + certTree, err := certList.AsMap().CertTree() + if err != nil { + return err } - for _, action := range certActions { - err := action(cfg) - if err != nil { - return err - } + if err := certTree.CreateTree(cfg); err != nil { + return fmt.Errorf("Error creating PKI assets: %v", err) } fmt.Printf("[certificates] valid certificates and keys now exist in %q\n", cfg.CertificatesDir) + // Service accounts are not x509 certs, so handled separately + if err := CreateServiceAccountKeyAndPublicKeyFiles(cfg); err != nil { + return err + } + return nil } @@ -118,7 +117,7 @@ func CreateAPIServerKubeletClientCertAndKeyFiles(cfg *kubeadmapi.InitConfigurati return err } - apiKubeletClientCert, apiKubeletClientKey, err := NewAPIServerKubeletClientCertAndKey(caCert, caKey) + apiKubeletClientCert, apiKubeletClientKey, err := NewAPIServerKubeletClientCertAndKey(cfg, caCert, caKey) if err != nil { return err } @@ -209,7 +208,7 @@ func CreateEtcdHealthcheckClientCertAndKeyFiles(cfg *kubeadmapi.InitConfiguratio return err } - etcdHealthcheckClientCert, etcdHealthcheckClientKey, err := NewEtcdHealthcheckClientCertAndKey(etcdCACert, etcdCAKey) + etcdHealthcheckClientCert, etcdHealthcheckClientKey, err := NewEtcdHealthcheckClientCertAndKey(cfg, etcdCACert, etcdCAKey) if err != nil { return err } @@ -233,7 +232,7 @@ func CreateAPIServerEtcdClientCertAndKeyFiles(cfg *kubeadmapi.InitConfiguration) return err } - apiEtcdClientCert, apiEtcdClientKey, err := NewAPIServerEtcdClientCertAndKey(etcdCACert, etcdCAKey) + apiEtcdClientCert, apiEtcdClientKey, err := NewAPIServerEtcdClientCertAndKey(cfg, etcdCACert, etcdCAKey) if err != nil { return err } @@ -293,7 +292,7 @@ func CreateFrontProxyClientCertAndKeyFiles(cfg *kubeadmapi.InitConfiguration) er return err } - frontProxyClientCert, frontProxyClientKey, err := NewFrontProxyClientCertAndKey(frontProxyCACert, frontProxyCAKey) + frontProxyClientCert, frontProxyClientKey, err := NewFrontProxyClientCertAndKey(cfg, frontProxyCACert, frontProxyCAKey) if err != nil { return err } @@ -318,41 +317,27 @@ func NewCACertAndKey() (*x509.Certificate, *rsa.PrivateKey, error) { return caCert, caKey, nil } +func newCertAndKeyFromSpec(certSpec *KubeadmCert, cfg *kubeadmapi.InitConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { + certConfig, err := certSpec.GetConfig(cfg) + if err != nil { + return nil, nil, fmt.Errorf("failure while creating certificate %s: %v", certSpec.Name, err) + } + cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, *certConfig) + if err != nil { + return nil, nil, fmt.Errorf("failure while creating %s key and certificate: %v", certSpec.Name, err) + } + + return cert, key, err +} + // NewAPIServerCertAndKey generate certificate for apiserver, signed by the given CA. func NewAPIServerCertAndKey(cfg *kubeadmapi.InitConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { - - altNames, err := pkiutil.GetAPIServerAltNames(cfg) - if err != nil { - return nil, nil, fmt.Errorf("failure while composing altnames for API server: %v", err) - } - - config := certutil.Config{ - CommonName: kubeadmconstants.APIServerCertCommonName, - AltNames: *altNames, - Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, - } - apiCert, apiKey, err := pkiutil.NewCertAndKey(caCert, caKey, config) - if err != nil { - return nil, nil, fmt.Errorf("failure while creating API server key and certificate: %v", err) - } - - return apiCert, apiKey, nil + return newCertAndKeyFromSpec(&KubeadmCertAPIServer, cfg, caCert, caKey) } // NewAPIServerKubeletClientCertAndKey generate certificate for the apiservers to connect to the kubelets securely, signed by the given CA. -func NewAPIServerKubeletClientCertAndKey(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { - - config := certutil.Config{ - CommonName: kubeadmconstants.APIServerKubeletClientCertCommonName, - Organization: []string{kubeadmconstants.MastersGroup}, - Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - } - apiClientCert, apiClientKey, err := pkiutil.NewCertAndKey(caCert, caKey, config) - if err != nil { - return nil, nil, fmt.Errorf("failure while creating API server kubelet client key and certificate: %v", err) - } - - return apiClientCert, apiClientKey, nil +func NewAPIServerKubeletClientCertAndKey(cfg *kubeadmapi.InitConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { + return newCertAndKeyFromSpec(&KubeadmCertKubeletClient, cfg, caCert, caKey) } // NewEtcdCACertAndKey generate a self signed etcd CA. @@ -368,80 +353,22 @@ func NewEtcdCACertAndKey() (*x509.Certificate, *rsa.PrivateKey, error) { // NewEtcdServerCertAndKey generate certificate for etcd, signed by the given CA. func NewEtcdServerCertAndKey(cfg *kubeadmapi.InitConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { - - altNames, err := pkiutil.GetEtcdAltNames(cfg) - if err != nil { - return nil, nil, fmt.Errorf("failure while composing altnames for etcd: %v", err) - } - - // TODO: etcd 3.2 introduced an undocumented requirement for ClientAuth usage on the - // server cert: https://github.com/coreos/etcd/issues/9785#issuecomment-396715692 - // Once the upstream issue is resolved, this should be returned to only allowing - // ServerAuth usage. - config := certutil.Config{ - CommonName: cfg.NodeRegistration.Name, - AltNames: *altNames, - Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - } - etcdServerCert, etcdServerKey, err := pkiutil.NewCertAndKey(caCert, caKey, config) - if err != nil { - return nil, nil, fmt.Errorf("failure while creating etcd key and certificate: %v", err) - } - - return etcdServerCert, etcdServerKey, nil + return newCertAndKeyFromSpec(&KubeadmCertEtcdServer, cfg, caCert, caKey) } // NewEtcdPeerCertAndKey generate certificate for etcd peering, signed by the given CA. func NewEtcdPeerCertAndKey(cfg *kubeadmapi.InitConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { - - altNames, err := pkiutil.GetEtcdPeerAltNames(cfg) - if err != nil { - return nil, nil, fmt.Errorf("failure while composing altnames for etcd peering: %v", err) - } - - config := certutil.Config{ - CommonName: cfg.NodeRegistration.Name, - AltNames: *altNames, - Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, - } - etcdPeerCert, etcdPeerKey, err := pkiutil.NewCertAndKey(caCert, caKey, config) - if err != nil { - return nil, nil, fmt.Errorf("failure while creating etcd peer key and certificate: %v", err) - } - - return etcdPeerCert, etcdPeerKey, nil + return newCertAndKeyFromSpec(&KubeadmCertEtcdPeer, cfg, caCert, caKey) } // NewEtcdHealthcheckClientCertAndKey generate certificate for liveness probes to healthcheck etcd, signed by the given CA. -func NewEtcdHealthcheckClientCertAndKey(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { - - config := certutil.Config{ - CommonName: kubeadmconstants.EtcdHealthcheckClientCertCommonName, - Organization: []string{kubeadmconstants.MastersGroup}, - Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - } - etcdHealcheckClientCert, etcdHealcheckClientKey, err := pkiutil.NewCertAndKey(caCert, caKey, config) - if err != nil { - return nil, nil, fmt.Errorf("failure while creating etcd healthcheck client key and certificate: %v", err) - } - - return etcdHealcheckClientCert, etcdHealcheckClientKey, nil +func NewEtcdHealthcheckClientCertAndKey(cfg *kubeadmapi.InitConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { + return newCertAndKeyFromSpec(&KubeadmCertEtcdHealthcheck, cfg, caCert, caKey) } // NewAPIServerEtcdClientCertAndKey generate certificate for the apiservers to connect to etcd securely, signed by the given CA. -func NewAPIServerEtcdClientCertAndKey(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { - - config := certutil.Config{ - CommonName: kubeadmconstants.APIServerEtcdClientCertCommonName, - Organization: []string{kubeadmconstants.MastersGroup}, - Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - } - apiClientCert, apiClientKey, err := pkiutil.NewCertAndKey(caCert, caKey, config) - if err != nil { - return nil, nil, fmt.Errorf("failure while creating API server etcd client key and certificate: %v", err) - } - - return apiClientCert, apiClientKey, nil +func NewAPIServerEtcdClientCertAndKey(cfg *kubeadmapi.InitConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { + return newCertAndKeyFromSpec(&KubeadmCertEtcdHealthcheck, cfg, caCert, caKey) } // NewServiceAccountSigningKey generate public/private key pairs for signing service account tokens. @@ -468,18 +395,8 @@ func NewFrontProxyCACertAndKey() (*x509.Certificate, *rsa.PrivateKey, error) { } // NewFrontProxyClientCertAndKey generate certificate for proxy server client, signed by the given front proxy CA. -func NewFrontProxyClientCertAndKey(frontProxyCACert *x509.Certificate, frontProxyCAKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { - - config := certutil.Config{ - CommonName: kubeadmconstants.FrontProxyClientCertCommonName, - Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}, - } - frontProxyClientCert, frontProxyClientKey, err := pkiutil.NewCertAndKey(frontProxyCACert, frontProxyCAKey, config) - if err != nil { - return nil, nil, fmt.Errorf("failure while creating front-proxy client key and certificate: %v", err) - } - - return frontProxyClientCert, frontProxyClientKey, nil +func NewFrontProxyClientCertAndKey(cfg *kubeadmapi.InitConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) { + return newCertAndKeyFromSpec(&KubeadmCertFrontProxyClient, cfg, caCert, caKey) } // loadCertificateAuthority loads certificate authority diff --git a/cmd/kubeadm/app/phases/certs/certs_test.go b/cmd/kubeadm/app/phases/certs/certs_test.go index 7da7bd9c9df..446f0ab35fe 100644 --- a/cmd/kubeadm/app/phases/certs/certs_test.go +++ b/cmd/kubeadm/app/phases/certs/certs_test.go @@ -60,7 +60,7 @@ func TestWriteCertificateAuthorithyFilesIfNotExist(t *testing.T) { }, { // cert exists, but it is not a ca > err setupFunc: func(pkiDir string) error { - cert, key, _ := NewFrontProxyClientCertAndKey(setupCert, setupKey) + cert, key, _ := NewFrontProxyClientCertAndKey(&kubeadmapi.InitConfiguration{}, setupCert, setupKey) return writeCertificateFilesIfNotExist(pkiDir, "dummy", setupCert, cert, key) }, expectedError: true, @@ -111,8 +111,8 @@ func TestWriteCertificateAuthorithyFilesIfNotExist(t *testing.T) { func TestWriteCertificateFilesIfNotExist(t *testing.T) { caCert, caKey, _ := NewFrontProxyCACertAndKey() - setupCert, setupKey, _ := NewFrontProxyClientCertAndKey(caCert, caKey) - cert, key, _ := NewFrontProxyClientCertAndKey(caCert, caKey) + setupCert, setupKey, _ := NewFrontProxyClientCertAndKey(&kubeadmapi.InitConfiguration{}, caCert, caKey) + cert, key, _ := NewFrontProxyClientCertAndKey(&kubeadmapi.InitConfiguration{}, caCert, caKey) var tests = []struct { setupFunc func(pkiDir string) error @@ -138,7 +138,7 @@ func TestWriteCertificateFilesIfNotExist(t *testing.T) { { // cert exists, is signed by another ca > err setupFunc: func(pkiDir string) error { anotherCaCert, anotherCaKey, _ := NewFrontProxyCACertAndKey() - anotherCert, anotherKey, _ := NewFrontProxyClientCertAndKey(anotherCaCert, anotherCaKey) + anotherCert, anotherKey, _ := NewFrontProxyClientCertAndKey(&kubeadmapi.InitConfiguration{}, anotherCaCert, anotherCaKey) return writeCertificateFilesIfNotExist(pkiDir, "dummy", anotherCaCert, anotherCert, anotherKey) }, @@ -300,7 +300,7 @@ func TestNewAPIServerKubeletClientCertAndKey(t *testing.T) { t.Fatalf("failed creation of ca cert and key: %v", err) } - apiKubeletClientCert, _, err := NewAPIServerKubeletClientCertAndKey(caCert, caKey) + apiKubeletClientCert, _, err := NewAPIServerKubeletClientCertAndKey(&kubeadmapi.InitConfiguration{}, caCert, caKey) if err != nil { t.Fatalf("failed creation of cert and key: %v", err) } @@ -395,7 +395,7 @@ func TestNewEtcdHealthcheckClientCertAndKey(t *testing.T) { t.Fatalf("failed creation of ca cert and key: %v", err) } - etcdHealthcheckClientCert, _, err := NewEtcdHealthcheckClientCertAndKey(caCert, caKey) + etcdHealthcheckClientCert, _, err := NewEtcdHealthcheckClientCertAndKey(&kubeadmapi.InitConfiguration{}, caCert, caKey) if err != nil { t.Fatalf("failed creation of cert and key: %v", err) } @@ -411,7 +411,7 @@ func TestNewAPIServerEtcdClientCertAndKey(t *testing.T) { t.Fatalf("failed creation of ca cert and key: %v", err) } - apiEtcdClientCert, _, err := NewAPIServerEtcdClientCertAndKey(caCert, caKey) + apiEtcdClientCert, _, err := NewAPIServerEtcdClientCertAndKey(&kubeadmapi.InitConfiguration{}, caCert, caKey) if err != nil { t.Fatalf("failed creation of cert and key: %v", err) } @@ -448,7 +448,7 @@ func TestNewFrontProxyClientCertAndKey(t *testing.T) { t.Fatalf("failed creation of ca cert and key: %v", err) } - frontProxyClientCert, _, err := NewFrontProxyClientCertAndKey(frontProxyCACert, frontProxyCAKey) + frontProxyClientCert, _, err := NewFrontProxyClientCertAndKey(&kubeadmapi.InitConfiguration{}, frontProxyCACert, frontProxyCAKey) if err != nil { t.Fatalf("failed creation of cert and key: %v", err) }