mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 12:43:23 +00:00
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:
parent
be11540775
commit
cda8c39f77
@ -62,7 +62,7 @@ func (k *KubeadmCert) CreateFromCA(ic *kubeadmapi.InitConfiguration, caCert *x50
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
writeCertificateFilesIfNotExist(
|
err = writeCertificateFilesIfNotExist(
|
||||||
ic.CertificatesDir,
|
ic.CertificatesDir,
|
||||||
k.BaseName,
|
k.BaseName,
|
||||||
caCert,
|
caCert,
|
||||||
@ -70,6 +70,10 @@ func (k *KubeadmCert) CreateFromCA(ic *kubeadmapi.InitConfiguration, caCert *x50
|
|||||||
key,
|
key,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to write certificate %q: %v", k.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,9 +112,48 @@ func (t CertificateTree) CreateTree(ic *kubeadmapi.InitConfiguration) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
caCert, caKey, err := NewCACertAndKey(cfg)
|
var caKey *rsa.PrivateKey
|
||||||
if err != nil {
|
|
||||||
return err
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
err = writeCertificateAuthorithyFilesIfNotExist(
|
||||||
|
ic.CertificatesDir,
|
||||||
|
ca.BaseName,
|
||||||
|
caCert,
|
||||||
|
caKey,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, leaf := range leaves {
|
for _, leaf := range leaves {
|
||||||
@ -118,16 +161,6 @@ func (t CertificateTree) CreateTree(ic *kubeadmapi.InitConfiguration) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = writeCertificateAuthorithyFilesIfNotExist(
|
|
||||||
ic.CertificatesDir,
|
|
||||||
ca.BaseName,
|
|
||||||
caCert,
|
|
||||||
caKey,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -396,6 +396,11 @@ func validateSignedCert(l certKeyLocation) error {
|
|||||||
return fmt.Errorf("failure loading certificate authority for %s: %v", l.uxName, err)
|
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
|
// Try to load key and signed certificate
|
||||||
signedCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(l.pkiDir, l.baseName)
|
signedCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(l.pkiDir, l.baseName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -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) {
|
func TestUsingExternalCA(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
setupFuncs []func(cfg *kubeadmapi.InitConfiguration) error
|
setupFuncs []func(cfg *kubeadmapi.InitConfiguration) error
|
||||||
@ -610,3 +693,24 @@ func deleteFrontProxyCAKey(cfg *kubeadmapi.InitConfiguration) error {
|
|||||||
}
|
}
|
||||||
return nil
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,8 +11,10 @@ go_library(
|
|||||||
importpath = "k8s.io/kubernetes/cmd/kubeadm/test",
|
importpath = "k8s.io/kubernetes/cmd/kubeadm/test",
|
||||||
deps = [
|
deps = [
|
||||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
"//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/constants:go_default_library",
|
||||||
"//cmd/kubeadm/app/phases/certs/pkiutil: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",
|
"//cmd/kubeadm/test/certs:go_default_library",
|
||||||
"//vendor/github.com/renstrom/dedent:go_default_library",
|
"//vendor/github.com/renstrom/dedent:go_default_library",
|
||||||
],
|
],
|
||||||
|
@ -26,8 +26,10 @@ import (
|
|||||||
"github.com/renstrom/dedent"
|
"github.com/renstrom/dedent"
|
||||||
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
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"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil"
|
"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"
|
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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user