Fixes using externally managed certs for kubeadm

Walk the certificate tree, at each step checking for a CACert.
If the CACert is found, try to use it to generate certificates.
Otherwise, generate a new CA cert.
This commit is contained in:
liz 2018-09-05 12:11:16 -04:00
parent be11540775
commit cda8c39f77
No known key found for this signature in database
GPG Key ID: 42D1F3A8C4A02586
5 changed files with 170 additions and 14 deletions

View File

@ -62,7 +62,7 @@ func (k *KubeadmCert) CreateFromCA(ic *kubeadmapi.InitConfiguration, caCert *x50
if err != nil {
return err
}
writeCertificateFilesIfNotExist(
err = writeCertificateFilesIfNotExist(
ic.CertificatesDir,
k.BaseName,
caCert,
@ -70,6 +70,10 @@ func (k *KubeadmCert) CreateFromCA(ic *kubeadmapi.InitConfiguration, caCert *x50
key,
)
if err != nil {
return fmt.Errorf("failed to write certificate %q: %v", k.Name, err)
}
return nil
}
@ -108,17 +112,39 @@ func (t CertificateTree) CreateTree(ic *kubeadmapi.InitConfiguration) error {
return err
}
caCert, caKey, err := NewCACertAndKey(cfg)
var caKey *rsa.PrivateKey
caCert, err := pkiutil.TryLoadCertFromDisk(ic.CertificatesDir, ca.BaseName)
if err == nil {
// Cert exists already, make sure it's valid
if !caCert.IsCA {
return fmt.Errorf("certificate %q is not a CA", ca.Name)
}
// Try and load a CA Key
caKey, err = pkiutil.TryLoadKeyFromDisk(ic.CertificatesDir, ca.BaseName)
if err != nil {
// If there's no CA key, make sure every certificate exists.
for _, leaf := range leaves {
cl := certKeyLocation{
pkiDir: ic.CertificatesDir,
baseName: leaf.BaseName,
uxName: leaf.Name,
}
if err := validateSignedCertWithCA(cl, caCert); err != nil {
return fmt.Errorf("could not load expected certificate %q or validate the existence of key %q for it: %v", leaf.Name, ca.Name, err)
}
}
// CACert exists and all clients exist, continue to next CA.
continue
}
// CA key exists; just use that to create new certificates.
} else {
// CACert doesn't already exist, create a new cert and key.
caCert, caKey, err = NewCACertAndKey(cfg)
if err != nil {
return err
}
for _, leaf := range leaves {
if err := leaf.CreateFromCA(ic, caCert, caKey); err != nil {
return err
}
}
err = writeCertificateAuthorithyFilesIfNotExist(
ic.CertificatesDir,
ca.BaseName,
@ -129,6 +155,13 @@ func (t CertificateTree) CreateTree(ic *kubeadmapi.InitConfiguration) error {
return err
}
}
for _, leaf := range leaves {
if err := leaf.CreateFromCA(ic, caCert, caKey); err != nil {
return err
}
}
}
return nil
}

View File

@ -396,6 +396,11 @@ func validateSignedCert(l certKeyLocation) error {
return fmt.Errorf("failure loading certificate authority for %s: %v", l.uxName, err)
}
return validateSignedCertWithCA(l, caCert)
}
// validateSignedCertWithCA tries to load a certificate and validate it with the given caCert
func validateSignedCertWithCA(l certKeyLocation, caCert *x509.Certificate) error {
// Try to load key and signed certificate
signedCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(l.pkiDir, l.baseName)
if err != nil {

View File

@ -374,6 +374,89 @@ func TestSharedCertificateExists(t *testing.T) {
}
}
func TestCreatePKIAssetsWithSparseCerts(t *testing.T) {
caCert, caKey := createCACert(t)
fpCACert, fpCAKey := createCACert(t)
etcdCACert, etcdCAKey := createCACert(t)
fpCert, fpKey := createTestCert(t, fpCACert, fpCAKey)
tests := []struct {
name string
files pkiFiles
expectError bool
}{
{
name: "nothing present",
},
{
name: "CAs already exist",
files: pkiFiles{
"ca.crt": caCert,
"ca.key": caKey,
"front-proxy-ca.crt": fpCACert,
"front-proxy-ca.key": fpCAKey,
"etcd/ca.crt": etcdCACert,
"etcd/ca.key": etcdCAKey,
},
},
{
name: "CA certs only",
files: pkiFiles{
"ca.crt": caCert,
"front-proxy-ca.crt": fpCACert,
"etcd/ca.crt": etcdCACert,
},
expectError: true,
},
{
name: "FrontProxyCA with certs",
files: pkiFiles{
"ca.crt": caCert,
"ca.key": caKey,
"front-proxy-ca.crt": fpCACert,
"front-proxy-client.crt": fpCert,
"front-proxy-client.key": fpKey,
"etcd/ca.crt": etcdCACert,
"etcd/ca.key": etcdCAKey,
},
},
{
name: "FrontProxy certs missing CA",
files: pkiFiles{
"front-proxy-client.crt": fpCert,
"front-proxy-client.key": fpKey,
},
expectError: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
tmpdir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpdir)
cfg := testutil.GetDefaultInternalConfig(t)
cfg.ClusterConfiguration.CertificatesDir = tmpdir
writePKIFiles(t, tmpdir, test.files)
err := CreatePKIAssets(cfg)
if err != nil {
if test.expectError {
return
}
t.Fatalf("Unexpected error: %v", err)
}
if test.expectError {
t.Fatal("Expected error from CreatePKIAssets, got none")
}
assertCertsExist(t, tmpdir)
})
}
}
func TestUsingExternalCA(t *testing.T) {
tests := []struct {
setupFuncs []func(cfg *kubeadmapi.InitConfiguration) error
@ -610,3 +693,24 @@ func deleteFrontProxyCAKey(cfg *kubeadmapi.InitConfiguration) error {
}
return nil
}
func assertCertsExist(t *testing.T, dir string) {
tree, err := GetDefaultCertList().AsMap().CertTree()
if err != nil {
t.Fatalf("unexpected error getting certificates: %v", err)
}
for caCert, certs := range tree {
if err := validateCACert(certKeyLocation{dir, caCert.BaseName, "", caCert.Name}); err != nil {
t.Errorf("couldn't validate CA certificate %v: %v", caCert.Name, err)
// Don't bother validating child certs, but do try the other CAs
continue
}
for _, cert := range certs {
if err := validateSignedCert(certKeyLocation{dir, caCert.BaseName, cert.BaseName, cert.Name}); err != nil {
t.Errorf("couldn't validate certificate %v: %v", cert.Name, err)
}
}
}
}

View File

@ -11,8 +11,10 @@ go_library(
importpath = "k8s.io/kubernetes/cmd/kubeadm/test",
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1alpha3:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
"//cmd/kubeadm/app/util/config:go_default_library",
"//cmd/kubeadm/test/certs:go_default_library",
"//vendor/github.com/renstrom/dedent:go_default_library",
],

View File

@ -26,8 +26,10 @@ import (
"github.com/renstrom/dedent"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1alpha3 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha3"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
certtestutil "k8s.io/kubernetes/cmd/kubeadm/test/certs"
)
@ -140,3 +142,13 @@ func AssertFileExists(t *testing.T, dirName string, fileNames ...string) {
}
}
}
// GetDefaultInternalConfig returns a defaulted kubeadmapi.InitConfiguration
func GetDefaultInternalConfig(t *testing.T) *kubeadmapi.InitConfiguration {
internalcfg, err := configutil.ConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1alpha3.InitConfiguration{})
if err != nil {
t.Fatalf("unexpected error getting default config: %v", err)
}
return internalcfg
}