mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-05 02:09:56 +00:00
Add new helper functions for creating keys, kubeconfig and CSR files
Signed-off-by: Richard Wall <richard.wall@jetstack.io>
This commit is contained in:
parent
21153e7b6a
commit
57712220a1
@ -28,6 +28,11 @@ import (
|
|||||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errInvalid = "invalid argument"
|
||||||
|
errExist = "file already exists"
|
||||||
|
)
|
||||||
|
|
||||||
type configMutatorsFunc func(*kubeadmapi.InitConfiguration, *pkiutil.CertConfig) error
|
type configMutatorsFunc func(*kubeadmapi.InitConfiguration, *pkiutil.CertConfig) error
|
||||||
|
|
||||||
// KubeadmCert represents a certificate that Kubeadm will create to function properly.
|
// KubeadmCert represents a certificate that Kubeadm will create to function properly.
|
||||||
@ -397,3 +402,63 @@ func setCommonNameToNodeName() configMutatorsFunc {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// leafCertificates returns non-CA certificates from the supplied Certificates.
|
||||||
|
func leafCertificates(c Certificates) (Certificates, error) {
|
||||||
|
certTree, err := c.AsMap().CertTree()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var out Certificates
|
||||||
|
for _, leafCertificates := range certTree {
|
||||||
|
out = append(out, leafCertificates...)
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createKeyAndCSR(kubeadmConfig *kubeadmapi.InitConfiguration, cert *KubeadmCert) error {
|
||||||
|
if kubeadmConfig == nil {
|
||||||
|
return errors.Errorf("%s: kubeadmConfig was nil", errInvalid)
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
return errors.Errorf("%s: cert was nil", errInvalid)
|
||||||
|
}
|
||||||
|
certDir := kubeadmConfig.CertificatesDir
|
||||||
|
name := cert.BaseName
|
||||||
|
if pkiutil.CSROrKeyExist(certDir, name) {
|
||||||
|
return errors.Errorf("%s: key or CSR %s/%s", errExist, certDir, name)
|
||||||
|
}
|
||||||
|
cfg, err := cert.GetConfig(kubeadmConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
csr, key, err := pkiutil.NewCSRAndKey(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = pkiutil.WriteKey(certDir, name, key)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = pkiutil.WriteCSR(certDir, name, csr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDefaultKeysAndCSRFiles is used in ExternalCA mode to create key files
|
||||||
|
// and adjacent CSR files.
|
||||||
|
func CreateDefaultKeysAndCSRFiles(config *kubeadmapi.InitConfiguration) error {
|
||||||
|
certificates, err := leafCertificates(GetDefaultCertList())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, cert := range certificates {
|
||||||
|
if err := createKeyAndCSR(config, cert); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||||
certutil "k8s.io/client-go/util/cert"
|
certutil "k8s.io/client-go/util/cert"
|
||||||
@ -34,9 +35,13 @@ import (
|
|||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
|
||||||
|
|
||||||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||||
|
pkiutil "k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
errInvalid = "invalid argument"
|
||||||
|
errExist = "file already exists"
|
||||||
)
|
)
|
||||||
|
|
||||||
// clientCertAuth struct holds info required to build a client certificate to provide authentication info in a kubeconfig object
|
// clientCertAuth struct holds info required to build a client certificate to provide authentication info in a kubeconfig object
|
||||||
@ -103,7 +108,7 @@ func createKubeConfigFiles(outDir string, cfg *kubeadmapi.InitConfiguration, kub
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// writes the kubeconfig to disk if it not exists
|
// writes the kubeconfig to disk if it does not exist
|
||||||
if err = createKubeConfigFileIfNotExists(outDir, kubeConfigFileName, config); err != nil {
|
if err = createKubeConfigFileIfNotExists(outDir, kubeConfigFileName, config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -113,57 +118,21 @@ func createKubeConfigFiles(outDir string, cfg *kubeadmapi.InitConfiguration, kub
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getKubeConfigSpecs returns all KubeConfigSpecs actualized to the context of the current InitConfiguration
|
// getKubeConfigSpecs returns all KubeConfigSpecs actualized to the context of the current InitConfiguration
|
||||||
// NB. this methods holds the information about how kubeadm creates kubeconfig files.
|
// NB. this method holds the information about how kubeadm creates kubeconfig files.
|
||||||
func getKubeConfigSpecs(cfg *kubeadmapi.InitConfiguration) (map[string]*kubeConfigSpec, error) {
|
func getKubeConfigSpecs(cfg *kubeadmapi.InitConfiguration) (map[string]*kubeConfigSpec, error) {
|
||||||
|
|
||||||
caCert, caKey, err := pkiutil.TryLoadCertAndKeyFromDisk(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
|
caCert, caKey, err := pkiutil.TryLoadCertAndKeyFromDisk(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "couldn't create a kubeconfig; the CA files couldn't be loaded")
|
return nil, errors.Wrap(err, "couldn't create a kubeconfig; the CA files couldn't be loaded")
|
||||||
}
|
}
|
||||||
|
configs, err := getKubeConfigSpecsBase(cfg)
|
||||||
controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
for _, spec := range configs {
|
||||||
var kubeConfigSpec = map[string]*kubeConfigSpec{
|
spec.CACert = caCert
|
||||||
kubeadmconstants.AdminKubeConfigFileName: {
|
spec.ClientCertAuth.CAKey = caKey
|
||||||
CACert: caCert,
|
|
||||||
APIServer: controlPlaneEndpoint,
|
|
||||||
ClientName: "kubernetes-admin",
|
|
||||||
ClientCertAuth: &clientCertAuth{
|
|
||||||
CAKey: caKey,
|
|
||||||
Organizations: []string{kubeadmconstants.SystemPrivilegedGroup},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
kubeadmconstants.KubeletKubeConfigFileName: {
|
|
||||||
CACert: caCert,
|
|
||||||
APIServer: controlPlaneEndpoint,
|
|
||||||
ClientName: fmt.Sprintf("%s%s", kubeadmconstants.NodesUserPrefix, cfg.NodeRegistration.Name),
|
|
||||||
ClientCertAuth: &clientCertAuth{
|
|
||||||
CAKey: caKey,
|
|
||||||
Organizations: []string{kubeadmconstants.NodesGroup},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
kubeadmconstants.ControllerManagerKubeConfigFileName: {
|
|
||||||
CACert: caCert,
|
|
||||||
APIServer: controlPlaneEndpoint,
|
|
||||||
ClientName: kubeadmconstants.ControllerManagerUser,
|
|
||||||
ClientCertAuth: &clientCertAuth{
|
|
||||||
CAKey: caKey,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
kubeadmconstants.SchedulerKubeConfigFileName: {
|
|
||||||
CACert: caCert,
|
|
||||||
APIServer: controlPlaneEndpoint,
|
|
||||||
ClientName: kubeadmconstants.SchedulerUser,
|
|
||||||
ClientCertAuth: &clientCertAuth{
|
|
||||||
CAKey: caKey,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
return configs, nil
|
||||||
return kubeConfigSpec, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildKubeConfigFromSpec creates a kubeconfig object for the given kubeConfigSpec
|
// buildKubeConfigFromSpec creates a kubeconfig object for the given kubeConfigSpec
|
||||||
@ -182,13 +151,8 @@ func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string) (*clientc
|
|||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, create a client certs
|
// otherwise, create a client certs
|
||||||
clientCertConfig := pkiutil.CertConfig{
|
clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec)
|
||||||
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)
|
clientCert, clientKey, err := pkiutil.NewCertAndKey(spec.CACert, spec.ClientCertAuth.CAKey, &clientCertConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failure while creating %s client certificate", spec.ClientName)
|
return nil, errors.Wrapf(err, "failure while creating %s client certificate", spec.ClientName)
|
||||||
@ -209,6 +173,16 @@ func buildKubeConfigFromSpec(spec *kubeConfigSpec, clustername string) (*clientc
|
|||||||
), nil
|
), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newClientCertConfigFromKubeConfigSpec(spec *kubeConfigSpec) pkiutil.CertConfig {
|
||||||
|
return pkiutil.CertConfig{
|
||||||
|
Config: certutil.Config{
|
||||||
|
CommonName: spec.ClientName,
|
||||||
|
Organization: spec.ClientCertAuth.Organizations,
|
||||||
|
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// validateKubeConfig check if the kubeconfig file exist and has the expected CA and server URL
|
// validateKubeConfig check if the kubeconfig file exist and has the expected CA and server URL
|
||||||
func validateKubeConfig(outDir, filename string, config *clientcmdapi.Config) error {
|
func validateKubeConfig(outDir, filename string, config *clientcmdapi.Config) error {
|
||||||
kubeConfigFilePath := filepath.Join(outDir, filename)
|
kubeConfigFilePath := filepath.Join(outDir, filename)
|
||||||
@ -386,3 +360,115 @@ func ValidateKubeconfigsForExternalCA(outDir string, cfg *kubeadmapi.InitConfigu
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getKubeConfigSpecsBase(cfg *kubeadmapi.InitConfiguration) (map[string]*kubeConfigSpec, error) {
|
||||||
|
apiServer, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return map[string]*kubeConfigSpec{
|
||||||
|
kubeadmconstants.AdminKubeConfigFileName: {
|
||||||
|
APIServer: apiServer,
|
||||||
|
ClientName: "kubernetes-admin",
|
||||||
|
ClientCertAuth: &clientCertAuth{
|
||||||
|
Organizations: []string{kubeadmconstants.SystemPrivilegedGroup},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
kubeadmconstants.KubeletKubeConfigFileName: {
|
||||||
|
APIServer: apiServer,
|
||||||
|
ClientName: fmt.Sprintf("%s%s", kubeadmconstants.NodesUserPrefix, cfg.NodeRegistration.Name),
|
||||||
|
ClientCertAuth: &clientCertAuth{
|
||||||
|
Organizations: []string{kubeadmconstants.NodesGroup},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
kubeadmconstants.ControllerManagerKubeConfigFileName: {
|
||||||
|
APIServer: apiServer,
|
||||||
|
ClientName: kubeadmconstants.ControllerManagerUser,
|
||||||
|
ClientCertAuth: &clientCertAuth{},
|
||||||
|
},
|
||||||
|
kubeadmconstants.SchedulerKubeConfigFileName: {
|
||||||
|
APIServer: apiServer,
|
||||||
|
ClientName: kubeadmconstants.SchedulerUser,
|
||||||
|
ClientCertAuth: &clientCertAuth{},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createKubeConfigAndCSR(kubeConfigDir string, kubeadmConfig *kubeadmapi.InitConfiguration, name string, spec *kubeConfigSpec) error {
|
||||||
|
if kubeConfigDir == "" {
|
||||||
|
return errors.Errorf("%s: kubeConfigDir was empty", errInvalid)
|
||||||
|
}
|
||||||
|
if kubeadmConfig == nil {
|
||||||
|
return errors.Errorf("%s: kubeadmConfig was nil", errInvalid)
|
||||||
|
}
|
||||||
|
if name == "" {
|
||||||
|
return errors.Errorf("%s: name was empty", errInvalid)
|
||||||
|
}
|
||||||
|
if spec == nil {
|
||||||
|
return errors.Errorf("%s: spec was nil", errInvalid)
|
||||||
|
}
|
||||||
|
kubeConfigPath := filepath.Join(kubeConfigDir, name)
|
||||||
|
if _, err := os.Stat(kubeConfigPath); err == nil {
|
||||||
|
return errors.Errorf("%s: kube config: %s", errExist, kubeConfigPath)
|
||||||
|
} else if !os.IsNotExist(err) {
|
||||||
|
return errors.Wrapf(err, "unexpected error while checking if file exists: %s", kubeConfigPath)
|
||||||
|
}
|
||||||
|
if pkiutil.CSROrKeyExist(kubeConfigDir, name) {
|
||||||
|
return errors.Errorf("%s: csr: %s", errExist, kubeConfigPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec)
|
||||||
|
|
||||||
|
clientKey, err := pkiutil.NewPrivateKey(clientCertConfig.PublicKeyAlgorithm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
clientCSR, err := pkiutil.NewCSR(clientCertConfig, clientKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
encodedClientKey, err := keyutil.MarshalPrivateKeyToPEM(clientKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
emptyCACert []byte
|
||||||
|
emptyClientCert []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
// create a kubeconfig with the client certs
|
||||||
|
config := kubeconfigutil.CreateWithCerts(
|
||||||
|
spec.APIServer,
|
||||||
|
kubeadmConfig.ClusterName,
|
||||||
|
spec.ClientName,
|
||||||
|
emptyCACert,
|
||||||
|
encodedClientKey,
|
||||||
|
emptyClientCert,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := kubeconfigutil.WriteToDisk(kubeConfigPath, config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Write CSR to disk
|
||||||
|
if err := pkiutil.WriteCSR(kubeConfigDir, name, clientCSR); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDefaultKubeConfigsAndCSRFiles is used in ExternalCA mode to create
|
||||||
|
// kubeconfig files and adjacent CSR files.
|
||||||
|
func CreateDefaultKubeConfigsAndCSRFiles(kubeConfigDir string, kubeadmConfig *kubeadmapi.InitConfiguration) error {
|
||||||
|
kubeConfigs, err := getKubeConfigSpecsBase(kubeadmConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for name, spec := range kubeConfigs {
|
||||||
|
if err := createKubeConfigAndCSR(kubeConfigDir, kubeadmConfig, name, spec); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -290,16 +290,14 @@ func TryLoadKeyFromDisk(pkiPath, name string) (crypto.Signer, error) {
|
|||||||
|
|
||||||
// TryLoadCSRAndKeyFromDisk tries to load the CSR and key from the disk
|
// TryLoadCSRAndKeyFromDisk tries to load the CSR and key from the disk
|
||||||
func TryLoadCSRAndKeyFromDisk(pkiPath, name string) (*x509.CertificateRequest, crypto.Signer, error) {
|
func TryLoadCSRAndKeyFromDisk(pkiPath, name string) (*x509.CertificateRequest, crypto.Signer, error) {
|
||||||
csrPath := pathForCSR(pkiPath, name)
|
csr, err := TryLoadCSRFromDisk(pkiPath, name)
|
||||||
|
|
||||||
csr, err := CertificateRequestFromFile(csrPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrapf(err, "couldn't load the certificate request %s", csrPath)
|
return nil, nil, errors.Wrap(err, "could not load CSR file")
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := TryLoadKeyFromDisk(pkiPath, name)
|
key, err := TryLoadKeyFromDisk(pkiPath, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.Wrap(err, "couldn't load key file")
|
return nil, nil, errors.Wrap(err, "could not load key file")
|
||||||
}
|
}
|
||||||
|
|
||||||
return csr, key, nil
|
return csr, key, nil
|
||||||
@ -334,6 +332,18 @@ func TryLoadPrivatePublicKeyFromDisk(pkiPath, name string) (*rsa.PrivateKey, *rs
|
|||||||
return k, p, nil
|
return k, p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TryLoadCSRFromDisk tries to load the CSR from the disk
|
||||||
|
func TryLoadCSRFromDisk(pkiPath, name string) (*x509.CertificateRequest, error) {
|
||||||
|
csrPath := pathForCSR(pkiPath, name)
|
||||||
|
|
||||||
|
csr, err := CertificateRequestFromFile(csrPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "could not load the CSR %s", csrPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return csr, nil
|
||||||
|
}
|
||||||
|
|
||||||
// PathsForCertAndKey returns the paths for the certificate and key given the path and basename.
|
// PathsForCertAndKey returns the paths for the certificate and key given the path and basename.
|
||||||
func PathsForCertAndKey(pkiPath, name string) (string, string) {
|
func PathsForCertAndKey(pkiPath, name string) (string, string) {
|
||||||
return pathForCert(pkiPath, name), pathForKey(pkiPath, name)
|
return pathForCert(pkiPath, name), pathForKey(pkiPath, name)
|
||||||
|
Loading…
Reference in New Issue
Block a user