kubeadm: allow creating a cluster with ECDSA keys

The selected key type is defined by kubeadm's --feature-gates option:
if it contains PublicKeysECDSA=true then ECDSA keys will be generated
and used.

By default RSA keys are used still.

Signed-off-by: Dmitry Rozhkov <dmitry.rozhkov@linux.intel.com>
This commit is contained in:
Dmitry Rozhkov 2020-02-21 16:43:37 +02:00
parent ac25069a05
commit 109f5db5a3
22 changed files with 241 additions and 133 deletions

View File

@ -19,6 +19,7 @@ go_library(
],
importpath = "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm",
deps = [
"//cmd/kubeadm/app/features:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",

View File

@ -17,8 +17,11 @@ limitations under the License.
package kubeadm
import (
"crypto/x509"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/cmd/kubeadm/app/features"
"k8s.io/apimachinery/pkg/runtime/schema"
)
@ -400,6 +403,15 @@ func (cfg *ClusterConfiguration) GetControlPlaneImageRepository() string {
return cfg.ImageRepository
}
// PublicKeyAlgorithm returns the type of encryption keys used in the cluster.
func (cfg *ClusterConfiguration) PublicKeyAlgorithm() x509.PublicKeyAlgorithm {
if features.Enabled(cfg.FeatureGates, features.PublicKeysECDSA) {
return x509.ECDSA
}
return x509.RSA
}
// HostPathMount contains elements describing volumes that are mounted from the
// host.
type HostPathMount struct {

View File

@ -200,7 +200,7 @@ func runCertsSa(c workflow.RunData) error {
}
// create the new service account key (or use existing)
return certsphase.CreateServiceAccountKeyAndPublicKeyFiles(data.CertificateWriteDir())
return certsphase.CreateServiceAccountKeyAndPublicKeyFiles(data.CertificateWriteDir(), data.Cfg().ClusterConfiguration.PublicKeyAlgorithm())
}
func runCerts(c workflow.RunData) error {

View File

@ -35,7 +35,7 @@ go_test(
srcs = ["features_test.go"],
embed = [":go_default_library"],
deps = [
"//cmd/kubeadm/app/constants:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/version:go_default_library",
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
],
)

View File

@ -30,11 +30,14 @@ import (
const (
// IPv6DualStack is expected to be alpha in v1.16
IPv6DualStack = "IPv6DualStack"
// PublicKeysECDSA is expected to be alpha in v1.19
PublicKeysECDSA = "PublicKeysECDSA"
)
// InitFeatureGates are the default feature gates for the init command
var InitFeatureGates = FeatureList{
IPv6DualStack: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
IPv6DualStack: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
PublicKeysECDSA: {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Alpha}},
}
// Feature represents a feature being gated

View File

@ -20,8 +20,8 @@ import (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/util/version"
"k8s.io/component-base/featuregate"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
)
func TestKnownFeatures(t *testing.T) {
@ -129,7 +129,7 @@ func TestNewFeatureGate(t *testing.T) {
func TestValidateVersion(t *testing.T) {
var someFeatures = FeatureList{
"feature1": {FeatureSpec: featuregate.FeatureSpec{Default: false, PreRelease: featuregate.Beta}},
"feature2": {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Alpha}, MinimumVersion: constants.MinimumControlPlaneVersion.WithPreRelease("alpha.1")},
"feature2": {FeatureSpec: featuregate.FeatureSpec{Default: true, PreRelease: featuregate.Alpha}, MinimumVersion: version.MustParseSemantic("v1.17.0").WithPreRelease("alpha.1")},
}
var tests = []struct {
@ -146,7 +146,7 @@ func TestValidateVersion(t *testing.T) {
{
name: "min version but correct value given",
requestedFeatures: map[string]bool{"feature2": true},
requestedVersion: constants.MinimumControlPlaneVersion.String(),
requestedVersion: "v1.17.0",
expectedError: false,
},
{

View File

@ -28,7 +28,7 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
)
type configMutatorsFunc func(*kubeadmapi.InitConfiguration, *certutil.Config) error
type configMutatorsFunc func(*kubeadmapi.InitConfiguration, *pkiutil.CertConfig) error
// KubeadmCert represents a certificate that Kubeadm will create to function properly.
type KubeadmCert struct {
@ -39,17 +39,18 @@ type KubeadmCert struct {
// 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
config pkiutil.CertConfig
}
// GetConfig returns the definition for the given cert given the provided InitConfiguration
func (k *KubeadmCert) GetConfig(ic *kubeadmapi.InitConfiguration) (*certutil.Config, error) {
func (k *KubeadmCert) GetConfig(ic *kubeadmapi.InitConfiguration) (*pkiutil.CertConfig, error) {
for _, f := range k.configMutators {
if err := f(ic, &k.config); err != nil {
return nil, err
}
}
k.config.PublicKeyAlgorithm = ic.ClusterConfiguration.PublicKeyAlgorithm()
return &k.config, nil
}
@ -239,8 +240,10 @@ var (
Name: "ca",
LongName: "self-signed Kubernetes CA to provision identities for other Kubernetes components",
BaseName: kubeadmconstants.CACertAndKeyBaseName,
config: certutil.Config{
CommonName: "kubernetes",
config: pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "kubernetes",
},
},
}
// KubeadmCertAPIServer is the definition of the cert used to serve the Kubernetes API.
@ -249,9 +252,11 @@ var (
LongName: "certificate for serving the Kubernetes API",
BaseName: kubeadmconstants.APIServerCertAndKeyBaseName,
CAName: "ca",
config: certutil.Config{
CommonName: kubeadmconstants.APIServerCertCommonName,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
config: pkiutil.CertConfig{
Config: certutil.Config{
CommonName: kubeadmconstants.APIServerCertCommonName,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
},
},
configMutators: []configMutatorsFunc{
makeAltNamesMutator(pkiutil.GetAPIServerAltNames),
@ -263,10 +268,12 @@ var (
LongName: "certificate for the API server to connect to kubelet",
BaseName: kubeadmconstants.APIServerKubeletClientCertAndKeyBaseName,
CAName: "ca",
config: certutil.Config{
CommonName: kubeadmconstants.APIServerKubeletClientCertCommonName,
Organization: []string{kubeadmconstants.SystemPrivilegedGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
config: pkiutil.CertConfig{
Config: certutil.Config{
CommonName: kubeadmconstants.APIServerKubeletClientCertCommonName,
Organization: []string{kubeadmconstants.SystemPrivilegedGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
},
}
@ -275,8 +282,10 @@ var (
Name: "front-proxy-ca",
LongName: "self-signed CA to provision identities for front proxy",
BaseName: kubeadmconstants.FrontProxyCACertAndKeyBaseName,
config: certutil.Config{
CommonName: "front-proxy-ca",
config: pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "front-proxy-ca",
},
},
}
@ -286,9 +295,11 @@ var (
BaseName: kubeadmconstants.FrontProxyClientCertAndKeyBaseName,
LongName: "certificate for the front proxy client",
CAName: "front-proxy-ca",
config: certutil.Config{
CommonName: kubeadmconstants.FrontProxyClientCertCommonName,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
config: pkiutil.CertConfig{
Config: certutil.Config{
CommonName: kubeadmconstants.FrontProxyClientCertCommonName,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
},
}
@ -297,8 +308,10 @@ var (
Name: "etcd-ca",
LongName: "self-signed CA to provision identities for etcd",
BaseName: kubeadmconstants.EtcdCACertAndKeyBaseName,
config: certutil.Config{
CommonName: "etcd-ca",
config: pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "etcd-ca",
},
},
}
// KubeadmCertEtcdServer is the definition of the cert used to serve etcd to clients.
@ -307,12 +320,14 @@ var (
LongName: "certificate for serving etcd",
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},
config: pkiutil.CertConfig{
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),
@ -325,8 +340,10 @@ var (
LongName: "certificate for etcd nodes to communicate with each other",
BaseName: kubeadmconstants.EtcdPeerCertAndKeyBaseName,
CAName: "etcd-ca",
config: certutil.Config{
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
config: pkiutil.CertConfig{
Config: certutil.Config{
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
},
},
configMutators: []configMutatorsFunc{
makeAltNamesMutator(pkiutil.GetEtcdPeerAltNames),
@ -339,10 +356,12 @@ var (
LongName: "certificate for liveness probes to healthcheck etcd",
BaseName: kubeadmconstants.EtcdHealthcheckClientCertAndKeyBaseName,
CAName: "etcd-ca",
config: certutil.Config{
CommonName: kubeadmconstants.EtcdHealthcheckClientCertCommonName,
Organization: []string{kubeadmconstants.SystemPrivilegedGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
config: pkiutil.CertConfig{
Config: certutil.Config{
CommonName: kubeadmconstants.EtcdHealthcheckClientCertCommonName,
Organization: []string{kubeadmconstants.SystemPrivilegedGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
},
}
// KubeadmCertEtcdAPIClient is the definition of the cert used by the API server to access etcd.
@ -351,16 +370,18 @@ var (
LongName: "certificate the apiserver uses to access etcd",
BaseName: kubeadmconstants.APIServerEtcdClientCertAndKeyBaseName,
CAName: "etcd-ca",
config: certutil.Config{
CommonName: kubeadmconstants.APIServerEtcdClientCertCommonName,
Organization: []string{kubeadmconstants.SystemPrivilegedGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
config: pkiutil.CertConfig{
Config: certutil.Config{
CommonName: kubeadmconstants.APIServerEtcdClientCertCommonName,
Organization: []string{kubeadmconstants.SystemPrivilegedGroup},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
},
}
)
func makeAltNamesMutator(f func(*kubeadmapi.InitConfiguration) (*certutil.AltNames, error)) configMutatorsFunc {
return func(mc *kubeadmapi.InitConfiguration, cc *certutil.Config) error {
return func(mc *kubeadmapi.InitConfiguration, cc *pkiutil.CertConfig) error {
altNames, err := f(mc)
if err != nil {
return err
@ -371,7 +392,7 @@ func makeAltNamesMutator(f func(*kubeadmapi.InitConfiguration) (*certutil.AltNam
}
func setCommonNameToNodeName() configMutatorsFunc {
return func(mc *kubeadmapi.InitConfiguration, cc *certutil.Config) error {
return func(mc *kubeadmapi.InitConfiguration, cc *pkiutil.CertConfig) error {
cc.CommonName = mc.NodeRegistration.Name
return nil
}

View File

@ -27,6 +27,7 @@ import (
certutil "k8s.io/client-go/util/cert"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
)
func TestCertListOrder(t *testing.T) {
@ -160,16 +161,18 @@ func TestCreateCertificateChain(t *testing.T) {
caCfg := Certificates{
{
config: certutil.Config{},
config: pkiutil.CertConfig{},
Name: "test-ca",
BaseName: "test-ca",
},
{
config: certutil.Config{
AltNames: certutil.AltNames{
DNSNames: []string{"test-domain.space"},
config: pkiutil.CertConfig{
Config: certutil.Config{
AltNames: certutil.AltNames{
DNSNames: []string{"test-domain.space"},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
configMutators: []configMutatorsFunc{
setCommonNameToNodeName(),

View File

@ -24,7 +24,6 @@ import (
"path/filepath"
"github.com/pkg/errors"
certutil "k8s.io/client-go/util/cert"
"k8s.io/client-go/util/keyutil"
"k8s.io/klog"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
@ -61,12 +60,12 @@ func CreatePKIAssets(cfg *kubeadmapi.InitConfiguration) error {
fmt.Printf("[certs] Valid certificates and keys now exist in %q\n", cfg.CertificatesDir)
// Service accounts are not x509 certs, so handled separately
return CreateServiceAccountKeyAndPublicKeyFiles(cfg.CertificatesDir)
return CreateServiceAccountKeyAndPublicKeyFiles(cfg.CertificatesDir, cfg.ClusterConfiguration.PublicKeyAlgorithm())
}
// CreateServiceAccountKeyAndPublicKeyFiles creates new public/private key files for signing service account users.
// If the sa public/private key files already exist in the target folder, they are used only if evaluated equals; otherwise an error is returned.
func CreateServiceAccountKeyAndPublicKeyFiles(certsDir string) error {
func CreateServiceAccountKeyAndPublicKeyFiles(certsDir string, keyType x509.PublicKeyAlgorithm) error {
klog.V(1).Infoln("creating new public/private key files for signing service account users")
_, err := keyutil.PrivateKeyFromFile(filepath.Join(certsDir, kubeadmconstants.ServiceAccountPrivateKeyName))
if err == nil {
@ -80,7 +79,7 @@ func CreateServiceAccountKeyAndPublicKeyFiles(certsDir string) error {
}
// The key does NOT exist, let's generate it now
key, err := pkiutil.NewPrivateKey()
key, err := pkiutil.NewPrivateKey(keyType)
if err != nil {
return err
}
@ -215,7 +214,7 @@ func writeCertificateAuthorityFilesIfNotExist(pkiDir string, baseName string, ca
// If there already is a certificate file at the given path; kubeadm tries to load it and check if the values in the
// existing and the expected certificate equals. If they do; kubeadm will just skip writing the file as it's up-to-date,
// otherwise this function returns an error.
func writeCertificateFilesIfNotExist(pkiDir string, baseName string, signingCert *x509.Certificate, cert *x509.Certificate, key crypto.Signer, cfg *certutil.Config) error {
func writeCertificateFilesIfNotExist(pkiDir string, baseName string, signingCert *x509.Certificate, cert *x509.Certificate, key crypto.Signer, cfg *pkiutil.CertConfig) error {
// Checks if the signed certificate exists in the PKI directory
if pkiutil.CertOrKeyExist(pkiDir, baseName) {
@ -426,7 +425,7 @@ func validatePrivatePublicKey(l certKeyLocation) error {
// validateCertificateWithConfig makes sure that a given certificate is valid at
// least for the SANs defined in the configuration.
func validateCertificateWithConfig(cert *x509.Certificate, baseName string, cfg *certutil.Config) error {
func validateCertificateWithConfig(cert *x509.Certificate, baseName string, cfg *pkiutil.CertConfig) error {
for _, dnsName := range cfg.AltNames.DNSNames {
if err := cert.VerifyHostname(dnsName); err != nil {
return errors.Wrapf(err, "certificate %s is invalid", baseName)

View File

@ -42,8 +42,10 @@ import (
func createTestCSR(t *testing.T) (*x509.CertificateRequest, crypto.Signer) {
csr, key, err := pkiutil.NewCSRAndKey(
&certutil.Config{
CommonName: "testCert",
&pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "testCert",
},
})
if err != nil {
t.Fatalf("couldn't create test cert: %v", err)
@ -344,7 +346,7 @@ func TestCreateServiceAccountKeyAndPublicKeyFiles(t *testing.T) {
}
}
err := CreateServiceAccountKeyAndPublicKeyFiles(dir)
err := CreateServiceAccountKeyAndPublicKeyFiles(dir, x509.RSA)
if (err != nil) != tt.expectedErr {
t.Fatalf("expected error: %v, got: %v, error: %v", tt.expectedErr, err != nil, err)
} else if tt.expectedErr {

View File

@ -52,7 +52,7 @@ func NewAPIRenewer(client clientset.Interface) *APIRenewer {
}
// Renew a certificate using the K8s certificate API
func (r *APIRenewer) Renew(cfg *certutil.Config) (*x509.Certificate, crypto.Signer, error) {
func (r *APIRenewer) Renew(cfg *pkiutil.CertConfig) (*x509.Certificate, crypto.Signer, error) {
reqTmp := &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: cfg.CommonName,
@ -62,7 +62,7 @@ func (r *APIRenewer) Renew(cfg *certutil.Config) (*x509.Certificate, crypto.Sign
IPAddresses: cfg.AltNames.IPs,
}
key, err := pkiutil.NewPrivateKey()
key, err := pkiutil.NewPrivateKey(cfg.PublicKeyAlgorithm)
if err != nil {
return nil, nil, errors.Wrap(err, "couldn't create new private key")
}

View File

@ -33,7 +33,9 @@ import (
)
func TestAPIRenewer(t *testing.T) {
caCertCfg := &certutil.Config{CommonName: "kubernetes"}
caCertCfg := &pkiutil.CertConfig{
Config: certutil.Config{CommonName: "kubernetes"},
}
caCert, caKey, err := pkiutil.NewCertificateAuthority(caCertCfg)
if err != nil {
t.Fatalf("couldn't create CA: %v", err)
@ -55,12 +57,14 @@ func TestAPIRenewer(t *testing.T) {
// override the timeout so tests are faster
watchTimeout = time.Second
certCfg := &certutil.Config{
CommonName: "test-certs",
AltNames: certutil.AltNames{
DNSNames: []string{"test-domain.space"},
certCfg := &pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "test-certs",
AltNames: certutil.AltNames{
DNSNames: []string{"test-domain.space"},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
renewer := &APIRenewer{
@ -92,12 +96,14 @@ func defaultReactionFunc(obj runtime.Object) k8stesting.ReactionFunc {
}
func getCertReq(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer) *certsapi.CertificateSigningRequest {
cert, _, err := pkiutil.NewCertAndKey(caCert, caKey, &certutil.Config{
CommonName: "testcert",
AltNames: certutil.AltNames{
DNSNames: []string{"test-domain.space"},
cert, _, err := pkiutil.NewCertAndKey(caCert, caKey, &pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "testcert",
AltNames: certutil.AltNames{
DNSNames: []string{"test-domain.space"},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
if err != nil {
t.Fatalf("couldn't generate cert: %v", err)

View File

@ -20,7 +20,6 @@ import (
"crypto"
"crypto/x509"
certutil "k8s.io/client-go/util/cert"
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
)
@ -39,6 +38,6 @@ func NewFileRenewer(caCert *x509.Certificate, caKey crypto.Signer) *FileRenewer
}
// Renew a certificate using a given CA cert and key
func (r *FileRenewer) Renew(cfg *certutil.Config) (*x509.Certificate, crypto.Signer, error) {
func (r *FileRenewer) Renew(cfg *pkiutil.CertConfig) (*x509.Certificate, crypto.Signer, error) {
return pkiutil.NewCertAndKey(r.caCert, r.caKey, cfg)
}

View File

@ -21,6 +21,7 @@ import (
"testing"
certutil "k8s.io/client-go/util/cert"
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
)
func TestFileRenewer(t *testing.T) {
@ -28,12 +29,14 @@ func TestFileRenewer(t *testing.T) {
fr := NewFileRenewer(testCACert, testCAKey)
// renews a certificate
certCfg := &certutil.Config{
CommonName: "test-certs",
AltNames: certutil.AltNames{
DNSNames: []string{"test-domain.space"},
certCfg := &pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "test-certs",
AltNames: certutil.AltNames{
DNSNames: []string{"test-domain.space"},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
cert, _, err := fr.Renew(certCfg)

View File

@ -224,7 +224,10 @@ func (rm *Manager) RenewUsingLocalCA(name string) (bool, error) {
}
// extract the certificate config
cfg := certToConfig(cert)
cfg := &pkiutil.CertConfig{
Config: certToConfig(cert),
PublicKeyAlgorithm: rm.cfg.PublicKeyAlgorithm(),
}
// reads the CA
caCert, caKey, err := certsphase.LoadCertificateAuthority(rm.cfg.CertificatesDir, handler.CABaseName)
@ -264,7 +267,10 @@ func (rm *Manager) RenewUsingCSRAPI(name string, client clientset.Interface) err
}
// extract the certificate config
cfg := certToConfig(cert)
cfg := &pkiutil.CertConfig{
Config: certToConfig(cert),
PublicKeyAlgorithm: rm.cfg.PublicKeyAlgorithm(),
}
// create a new certificate with the same config
newCert, newKey, err := NewAPIRenewer(client).Renew(cfg)
@ -298,7 +304,10 @@ func (rm *Manager) CreateRenewCSR(name, outdir string) error {
}
// extracts the certificate config
cfg := certToConfig(cert)
cfg := &pkiutil.CertConfig{
Config: certToConfig(cert),
PublicKeyAlgorithm: rm.cfg.PublicKeyAlgorithm(),
}
// generates the CSR request and save it
csr, key, err := pkiutil.NewCSRAndKey(cfg)
@ -407,8 +416,8 @@ func (rm *Manager) IsExternallyManaged(caBaseName string) (bool, error) {
}
}
func certToConfig(cert *x509.Certificate) *certutil.Config {
return &certutil.Config{
func certToConfig(cert *x509.Certificate) certutil.Config {
return certutil.Config{
CommonName: cert.Subject.CommonName,
Organization: cert.Subject.Organization,
AltNames: certutil.AltNames{

View File

@ -34,18 +34,22 @@ import (
)
var (
testCACertCfg = &certutil.Config{CommonName: "kubernetes"}
testCACertCfg = &pkiutil.CertConfig{
Config: certutil.Config{CommonName: "kubernetes"},
}
testCACert, testCAKey, _ = pkiutil.NewCertificateAuthority(testCACertCfg)
testCertCfg = &certutil.Config{
CommonName: "test-common-name",
Organization: []string{"sig-cluster-lifecycle"},
AltNames: certutil.AltNames{
IPs: []net.IP{net.ParseIP("10.100.0.1")},
DNSNames: []string{"test-domain.space"},
testCertCfg = &pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "test-common-name",
Organization: []string{"sig-cluster-lifecycle"},
AltNames: certutil.AltNames{
IPs: []net.IP{net.ParseIP("10.100.0.1")},
DNSNames: []string{"test-domain.space"},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
}
)

View File

@ -154,13 +154,15 @@ func writeTestCertificate(t *testing.T, dir, name string, caCert *x509.Certifica
// writeTestKubeconfig is a utility for creating a test kubeconfig with an embedded certificate
func writeTestKubeconfig(t *testing.T, dir, name string, caCert *x509.Certificate, caKey crypto.Signer) *x509.Certificate {
cfg := &certutil.Config{
CommonName: "test-common-name",
Organization: []string{"sig-cluster-lifecycle"},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
AltNames: certutil.AltNames{
IPs: []net.IP{net.ParseIP("10.100.0.1")},
DNSNames: []string{"test-domain.space"},
cfg := &pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "test-common-name",
Organization: []string{"sig-cluster-lifecycle"},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
AltNames: certutil.AltNames{
IPs: []net.IP{net.ParseIP("10.100.0.1")},
DNSNames: []string{"test-domain.space"},
},
},
}
cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, cfg)

View File

@ -182,10 +182,12 @@ func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string) (*clientc
}
// otherwise, create a client certs
clientCertConfig := certutil.Config{
CommonName: spec.ClientName,
Organization: spec.ClientCertAuth.Organizations,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
clientCertConfig := pkiutil.CertConfig{
Config: certutil.Config{
CommonName: spec.ClientName,
Organization: spec.ClientCertAuth.Organizations,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
}
clientCert, clientKey, err := pkiutil.NewCertAndKey(spec.CACert, spec.ClientCertAuth.CAKey, &clientCertConfig)
if err != nil {

View File

@ -32,7 +32,9 @@ import (
// SetupCertificateAuthority is a utility function for kubeadm testing that creates a
// CertificateAuthority cert/key pair
func SetupCertificateAuthority(t *testing.T) (*x509.Certificate, crypto.Signer) {
caCert, caKey, err := pkiutil.NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
caCert, caKey, err := pkiutil.NewCertificateAuthority(&pkiutil.CertConfig{
Config: certutil.Config{CommonName: "kubernetes"},
})
if err != nil {
t.Fatalf("failure while generating CA certificate and key: %v", err)
}
@ -132,7 +134,7 @@ func AssertCertificateHasIPAddresses(t *testing.T, cert *x509.Certificate, IPAdd
// CreateCACert creates a generic CA cert.
func CreateCACert(t *testing.T) (*x509.Certificate, crypto.Signer) {
certCfg := &certutil.Config{CommonName: "kubernetes"}
certCfg := &pkiutil.CertConfig{Config: certutil.Config{CommonName: "kubernetes"}}
cert, key, err := pkiutil.NewCertificateAuthority(certCfg)
if err != nil {
t.Fatalf("couldn't create CA: %v", err)
@ -141,11 +143,13 @@ func CreateCACert(t *testing.T) (*x509.Certificate, crypto.Signer) {
}
// CreateTestCert makes a generic certificate with the given CA and alternative names.
func CreateTestCert(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer, altNames certutil.AltNames) (*x509.Certificate, crypto.Signer, *certutil.Config) {
config := &certutil.Config{
CommonName: "testCert",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
AltNames: altNames,
func CreateTestCert(t *testing.T, caCert *x509.Certificate, caKey crypto.Signer, altNames certutil.AltNames) (*x509.Certificate, crypto.Signer, *pkiutil.CertConfig) {
config := &pkiutil.CertConfig{
Config: certutil.Config{
CommonName: "testCert",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
AltNames: altNames,
},
}
cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, config)
if err != nil {

View File

@ -19,6 +19,7 @@ package pkiutil
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
cryptorand "crypto/rand"
"crypto/rsa"
"crypto/x509"
@ -56,14 +57,20 @@ const (
rsaKeySize = 2048
)
// CertConfig is a wrapper around certutil.Config extending it with PublicKeyAlgorithm.
type CertConfig struct {
certutil.Config
PublicKeyAlgorithm x509.PublicKeyAlgorithm
}
// NewCertificateAuthority creates new certificate and private key for the certificate authority
func NewCertificateAuthority(config *certutil.Config) (*x509.Certificate, crypto.Signer, error) {
key, err := NewPrivateKey()
func NewCertificateAuthority(config *CertConfig) (*x509.Certificate, crypto.Signer, error) {
key, err := NewPrivateKey(config.PublicKeyAlgorithm)
if err != nil {
return nil, nil, errors.Wrap(err, "unable to create private key while generating CA certificate")
}
cert, err := certutil.NewSelfSignedCACert(*config, key)
cert, err := certutil.NewSelfSignedCACert(config.Config, key)
if err != nil {
return nil, nil, errors.Wrap(err, "unable to create self-signed CA certificate")
}
@ -72,8 +79,8 @@ func NewCertificateAuthority(config *certutil.Config) (*x509.Certificate, crypto
}
// NewCertAndKey creates new certificate and key by passing the certificate authority certificate and key
func NewCertAndKey(caCert *x509.Certificate, caKey crypto.Signer, config *certutil.Config) (*x509.Certificate, crypto.Signer, error) {
key, err := NewPrivateKey()
func NewCertAndKey(caCert *x509.Certificate, caKey crypto.Signer, config *CertConfig) (*x509.Certificate, crypto.Signer, error) {
key, err := NewPrivateKey(config.PublicKeyAlgorithm)
if err != nil {
return nil, nil, errors.Wrap(err, "unable to create private key")
}
@ -87,8 +94,8 @@ func NewCertAndKey(caCert *x509.Certificate, caKey crypto.Signer, config *certut
}
// NewCSRAndKey generates a new key and CSR and that could be signed to create the given certificate
func NewCSRAndKey(config *certutil.Config) (*x509.CertificateRequest, crypto.Signer, error) {
key, err := NewPrivateKey()
func NewCSRAndKey(config *CertConfig) (*x509.CertificateRequest, crypto.Signer, error) {
key, err := NewPrivateKey(config.PublicKeyAlgorithm)
if err != nil {
return nil, nil, errors.Wrap(err, "unable to create private key")
}
@ -496,7 +503,7 @@ func CertificateRequestFromFile(file string) (*x509.CertificateRequest, error) {
}
// NewCSR creates a new CSR
func NewCSR(cfg certutil.Config, key crypto.Signer) (*x509.CertificateRequest, error) {
func NewCSR(cfg CertConfig, key crypto.Signer) (*x509.CertificateRequest, error) {
template := &x509.CertificateRequest{
Subject: pkix.Name{
CommonName: cfg.CommonName,
@ -538,12 +545,16 @@ func EncodePublicKeyPEM(key crypto.PublicKey) ([]byte, error) {
}
// NewPrivateKey creates an RSA private key
func NewPrivateKey() (crypto.Signer, error) {
func NewPrivateKey(keyType x509.PublicKeyAlgorithm) (crypto.Signer, error) {
if keyType == x509.ECDSA {
return ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader)
}
return rsa.GenerateKey(cryptorand.Reader, rsaKeySize)
}
// NewSignedCert creates a signed certificate using the given CA certificate and key
func NewSignedCert(cfg *certutil.Config, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) {
func NewSignedCert(cfg *CertConfig, key crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer) (*x509.Certificate, error) {
serial, err := cryptorand.Int(cryptorand.Reader, new(big.Int).SetInt64(math.MaxInt64))
if err != nil {
return nil, err

View File

@ -33,7 +33,9 @@ import (
)
func TestNewCertificateAuthority(t *testing.T) {
cert, key, err := NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
cert, key, err := NewCertificateAuthority(&CertConfig{
Config: certutil.Config{CommonName: "kubernetes"},
})
if cert == nil {
t.Error("failed NewCertificateAuthority, cert == nil")
@ -86,10 +88,12 @@ func TestNewCertAndKey(t *testing.T) {
t.Fatalf("Couldn't create Private Key")
}
caCert := &x509.Certificate{}
config := &certutil.Config{
CommonName: "test",
Organization: []string{"test"},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
config := &CertConfig{
Config: certutil.Config{
CommonName: "test",
Organization: []string{"test"},
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
}
_, _, actual := NewCertAndKey(caCert, caKey, config)
if (actual == nil) != rt.expected {
@ -104,26 +108,41 @@ func TestNewCertAndKey(t *testing.T) {
}
func TestHasServerAuth(t *testing.T) {
caCert, caKey, _ := NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
caCert, caKey, _ := NewCertificateAuthority(&CertConfig{Config: certutil.Config{CommonName: "kubernetes"}})
var tests = []struct {
name string
config certutil.Config
config CertConfig
expected bool
}{
{
name: "has ServerAuth",
config: certutil.Config{
CommonName: "test",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
config: CertConfig{
Config: certutil.Config{
CommonName: "test",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
},
},
expected: true,
},
{
name: "has ServerAuth ECDSA",
config: CertConfig{
Config: certutil.Config{
CommonName: "test",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
},
PublicKeyAlgorithm: x509.ECDSA,
},
expected: true,
},
{
name: "doesn't have ServerAuth",
config: certutil.Config{
CommonName: "test",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
config: CertConfig{
Config: certutil.Config{
CommonName: "test",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
},
expected: false,
},
@ -285,7 +304,9 @@ func TestTryLoadCertAndKeyFromDisk(t *testing.T) {
}
defer os.RemoveAll(tmpdir)
caCert, caKey, err := NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
caCert, caKey, err := NewCertificateAuthority(&CertConfig{
Config: certutil.Config{CommonName: "kubernetes"},
})
if err != nil {
t.Errorf(
"failed to create cert and key with an error: %v",
@ -340,7 +361,9 @@ func TestTryLoadCertFromDisk(t *testing.T) {
}
defer os.RemoveAll(tmpdir)
caCert, _, err := NewCertificateAuthority(&certutil.Config{CommonName: "kubernetes"})
caCert, _, err := NewCertificateAuthority(&CertConfig{
Config: certutil.Config{CommonName: "kubernetes"},
})
if err != nil {
t.Errorf(
"failed to create cert and key with an error: %v",

View File

@ -356,6 +356,10 @@ func TestCmdInitFeatureGates(t *testing.T) {
name: "feature gate IPv6DualStack=true",
args: "--feature-gates=IPv6DualStack=true",
},
{
name: "feature gate PublicKeysECDSA=true",
args: "--feature-gates=PublicKeysECDSA=true",
},
}
for _, rt := range initTest {