Merge pull request #92183 from wallrj/2163-csr-only-external-ca-mode-2

kubeadm alpha certs generate-csr
This commit is contained in:
Kubernetes Prow Robot 2020-07-13 07:18:32 -07:00 committed by GitHub
commit 19f0a54d6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 509 additions and 57 deletions

View File

@ -21,6 +21,7 @@ go_library(
"//cmd/kubeadm/app/cmd/util:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/features:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/certs/renewal:go_default_library",
"//cmd/kubeadm/app/phases/copycerts:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
@ -34,6 +35,7 @@ go_library(
"//vendor/github.com/lithammer/dedent:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
],
)
@ -59,6 +61,8 @@ go_test(
],
embed = [":go_default_library"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
@ -68,5 +72,8 @@ go_test(
"//cmd/kubeadm/test/kubeconfig:go_default_library",
"//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
],
)

View File

@ -24,6 +24,7 @@ import (
"github.com/lithammer/dedent"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/duration"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
@ -33,8 +34,10 @@ import (
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/renewal"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/copycerts"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
)
@ -68,6 +71,22 @@ var (
You can also use "kubeadm init --upload-certs" without specifying a certificate key and it will
generate and print one for you.
`)
generateCSRLongDesc = cmdutil.LongDesc(`
Generates keys and certificate signing requests (CSRs) for all the certificates required to run the control plane.
This command also generates partial kubeconfig files with private key data in the "users > user > client-key-data" field,
and for each kubeconfig file an accompanying ".csr" file is created.
This command is designed for use in [Kubeadm External CA Mode](https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-certs/#external-ca-mode).
It generates CSRs which you can then submit to your external certificate authority for signing.
The PEM encoded signed certificates should then be saved alongside the key files, using ".crt" as the file extension,
or in the case of kubeconfig files, the PEM encoded signed certificate should be base64 encoded
and added to the kubeconfig file in the "users > user > client-certificate-data" field.
`)
generateCSRExample = cmdutil.Examples(`
# The following command will generate keys and CSRs for all control-plane certificates and kubeconfig files:
kubeadm alpha certs generate-csr --kubeconfig-dir /tmp/etc-k8s --cert-dir /tmp/etc-k8s/pki
`)
)
@ -82,9 +101,83 @@ func newCmdCertsUtility(out io.Writer) *cobra.Command {
cmd.AddCommand(newCmdCertsRenewal(out))
cmd.AddCommand(newCmdCertsExpiration(out, constants.KubernetesDir))
cmd.AddCommand(NewCmdCertificateKey())
cmd.AddCommand(newCmdGenCSR())
return cmd
}
// genCSRConfig is the configuration required by the gencsr command
type genCSRConfig struct {
kubeadmConfigPath string
certDir string
kubeConfigDir string
kubeadmConfig *kubeadmapi.InitConfiguration
}
func newGenCSRConfig() *genCSRConfig {
return &genCSRConfig{
kubeConfigDir: kubeadmconstants.KubernetesDir,
}
}
func (o *genCSRConfig) addFlagSet(flagSet *pflag.FlagSet) {
options.AddConfigFlag(flagSet, &o.kubeadmConfigPath)
options.AddCertificateDirFlag(flagSet, &o.certDir)
options.AddKubeConfigDirFlag(flagSet, &o.kubeConfigDir)
}
// load merges command line flag values into kubeadm's config.
// Reads Kubeadm config from a file (if present)
// else use dynamically generated default config.
// This configuration contains the DNS names and IP addresses which
// are encoded in the control-plane CSRs.
func (o *genCSRConfig) load() (err error) {
o.kubeadmConfig, err = configutil.LoadOrDefaultInitConfiguration(
o.kubeadmConfigPath,
&kubeadmapiv1beta2.InitConfiguration{},
&kubeadmapiv1beta2.ClusterConfiguration{},
)
if err != nil {
return err
}
// --cert-dir takes priority over kubeadm config if set.
if o.certDir != "" {
o.kubeadmConfig.CertificatesDir = o.certDir
}
return nil
}
// newCmdGenCSR returns cobra.Command for generating keys and CSRs
func newCmdGenCSR() *cobra.Command {
config := newGenCSRConfig()
cmd := &cobra.Command{
Use: "generate-csr",
Short: "Generate keys and certificate signing requests",
Long: generateCSRLongDesc,
Example: generateCSRExample,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
if err := config.load(); err != nil {
return err
}
return runGenCSR(config)
},
}
config.addFlagSet(cmd.Flags())
return cmd
}
// runGenCSR contains the logic of the generate-csr sub-command.
func runGenCSR(config *genCSRConfig) error {
if err := certsphase.CreateDefaultKeysAndCSRFiles(config.kubeadmConfig); err != nil {
return err
}
if err := kubeconfigphase.CreateDefaultKubeConfigsAndCSRFiles(config.kubeConfigDir, config.kubeadmConfig); err != nil {
return err
}
return nil
}
// NewCmdCertificateKey returns cobra.Command for certificate key generate
func NewCmdCertificateKey() *cobra.Command {
return &cobra.Command{

View File

@ -27,6 +27,13 @@ import (
"time"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"k8s.io/client-go/tools/clientcmd"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
certsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
@ -285,3 +292,185 @@ func TestRenewUsingCSR(t *testing.T) {
t.Fatalf("couldn't load certificate %q: %v", cert.Name, err)
}
}
func TestRunGenCSR(t *testing.T) {
tmpDir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpDir)
kubeConfigDir := filepath.Join(tmpDir, "kubernetes")
certDir := kubeConfigDir + "/pki"
expectedCertificates := []string{
"apiserver",
"apiserver-etcd-client",
"apiserver-kubelet-client",
"front-proxy-client",
"etcd/healthcheck-client",
"etcd/peer",
"etcd/server",
}
expectedKubeConfigs := []string{
"admin",
"kubelet",
"controller-manager",
"scheduler",
}
config := genCSRConfig{
kubeConfigDir: kubeConfigDir,
kubeadmConfig: &kubeadmapi.InitConfiguration{
LocalAPIEndpoint: kubeadmapi.APIEndpoint{
AdvertiseAddress: "192.0.2.1",
BindPort: 443,
},
ClusterConfiguration: kubeadmapi.ClusterConfiguration{
Networking: kubeadmapi.Networking{
ServiceSubnet: "192.0.2.0/24",
},
CertificatesDir: certDir,
KubernetesVersion: "v1.19.0",
},
},
}
err := runGenCSR(&config)
require.NoError(t, err, "expected runGenCSR to not fail")
t.Log("The command generates key and CSR files in the configured --cert-dir")
for _, name := range expectedCertificates {
_, err = pkiutil.TryLoadKeyFromDisk(certDir, name)
assert.NoErrorf(t, err, "failed to load key file: %s", name)
_, err = pkiutil.TryLoadCSRFromDisk(certDir, name)
assert.NoError(t, err, "failed to load CSR file: %s", name)
}
t.Log("The command generates kubeconfig files in the configured --kubeconfig-dir")
for _, name := range expectedKubeConfigs {
_, err = clientcmd.LoadFromFile(kubeConfigDir + "/" + name + ".conf")
assert.NoErrorf(t, err, "failed to load kubeconfig file: %s", name)
_, err = pkiutil.TryLoadCSRFromDisk(kubeConfigDir, name+".conf")
assert.NoError(t, err, "failed to load kubeconfig CSR file: %s", name)
}
}
func TestGenCSRConfig(t *testing.T) {
type assertion func(*testing.T, *genCSRConfig)
hasCertDir := func(expected string) assertion {
return func(t *testing.T, config *genCSRConfig) {
assert.Equal(t, expected, config.kubeadmConfig.CertificatesDir)
}
}
hasKubeConfigDir := func(expected string) assertion {
return func(t *testing.T, config *genCSRConfig) {
assert.Equal(t, expected, config.kubeConfigDir)
}
}
hasAdvertiseAddress := func(expected string) assertion {
return func(t *testing.T, config *genCSRConfig) {
assert.Equal(t, expected, config.kubeadmConfig.LocalAPIEndpoint.AdvertiseAddress)
}
}
// A minimal kubeadm config with just enough values to avoid triggering
// auto-detection of config values at runtime.
const kubeadmConfig = `
apiVersion: kubeadm.k8s.io/v1beta2
kind: InitConfiguration
localAPIEndpoint:
advertiseAddress: 192.0.2.1
nodeRegistration:
criSocket: /path/to/dockershim.sock
---
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
certificatesDir: /custom/config/certificates-dir
kubernetesVersion: v1.19.0
`
tmpDir := testutil.SetupTempDir(t)
defer os.RemoveAll(tmpDir)
customConfigPath := tmpDir + "/kubeadm.conf"
f, err := os.Create(customConfigPath)
require.NoError(t, err)
_, err = f.Write([]byte(kubeadmConfig))
require.NoError(t, err)
tests := []struct {
name string
flags []string
assertions []assertion
expectErr bool
}{
{
name: "default",
assertions: []assertion{
hasCertDir(kubeadmapiv1beta2.DefaultCertificatesDir),
hasKubeConfigDir(kubeadmconstants.KubernetesDir),
},
},
{
name: "--cert-dir overrides default",
flags: []string{"--cert-dir", "/foo/bar/pki"},
assertions: []assertion{
hasCertDir("/foo/bar/pki"),
},
},
{
name: "--config is loaded",
flags: []string{"--config", customConfigPath},
assertions: []assertion{
hasCertDir("/custom/config/certificates-dir"),
hasAdvertiseAddress("192.0.2.1"),
},
},
{
name: "--config not found",
flags: []string{"--config", "/does/not/exist"},
expectErr: true,
},
{
name: "--cert-dir overrides --config certificatesDir",
flags: []string{
"--config", customConfigPath,
"--cert-dir", "/foo/bar/pki",
},
assertions: []assertion{
hasCertDir("/foo/bar/pki"),
hasAdvertiseAddress("192.0.2.1"),
},
},
{
name: "--kubeconfig-dir overrides default",
flags: []string{
"--kubeconfig-dir", "/foo/bar/kubernetes",
},
assertions: []assertion{
hasKubeConfigDir("/foo/bar/kubernetes"),
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
flagset := pflag.NewFlagSet("flags-for-gencsr", pflag.ContinueOnError)
config := newGenCSRConfig()
config.addFlagSet(flagset)
require.NoError(t, flagset.Parse(test.flags))
err := config.load()
if test.expectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
for _, assertFunc := range test.assertions {
assertFunc(t, config)
}
})
}
}

View File

@ -66,7 +66,9 @@ func NewCertsPhase() workflow.Phase {
func localFlags() *pflag.FlagSet {
set := pflag.NewFlagSet("csr", pflag.ExitOnError)
options.AddCSRFlag(set, &csrOnly)
set.MarkDeprecated(options.CSROnly, "This flag will be removed in a future version. Please use kubeadm alpha certs generate-csr instead.")
options.AddCSRDirFlag(set, &csrDir)
set.MarkDeprecated(options.CSRDir, "This flag will be removed in a future version. Please use kubeadm alpha certs generate-csr instead.")
return set
}

View File

@ -28,6 +28,11 @@ import (
"k8s.io/kubernetes/cmd/kubeadm/app/util/pkiutil"
)
const (
errInvalid = "invalid argument"
errExist = "file already exists"
)
type configMutatorsFunc func(*kubeadmapi.InitConfiguration, *pkiutil.CertConfig) error
// KubeadmCert represents a certificate that Kubeadm will create to function properly.
@ -397,3 +402,63 @@ func setCommonNameToNodeName() configMutatorsFunc {
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
}

View File

@ -26,6 +26,7 @@ import (
"path/filepath"
"github.com/pkg/errors"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
certutil "k8s.io/client-go/util/cert"
@ -34,9 +35,13 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
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"
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
@ -103,7 +108,7 @@ func createKubeConfigFiles(outDir string, cfg *kubeadmapi.InitConfiguration, kub
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 {
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
// 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) {
caCert, caKey, err := pkiutil.TryLoadCertAndKeyFromDisk(cfg.CertificatesDir, kubeadmconstants.CACertAndKeyBaseName)
if err != nil {
return nil, errors.Wrap(err, "couldn't create a kubeconfig; the CA files couldn't be loaded")
}
controlPlaneEndpoint, err := kubeadmutil.GetControlPlaneEndpoint(cfg.ControlPlaneEndpoint, &cfg.LocalAPIEndpoint)
configs, err := getKubeConfigSpecsBase(cfg)
if err != nil {
return nil, err
}
var kubeConfigSpec = map[string]*kubeConfigSpec{
kubeadmconstants.AdminKubeConfigFileName: {
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,
},
},
for _, spec := range configs {
spec.CACert = caCert
spec.ClientCertAuth.CAKey = caKey
}
return kubeConfigSpec, nil
return configs, nil
}
// 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
clientCertConfig := pkiutil.CertConfig{
Config: certutil.Config{
CommonName: spec.ClientName,
Organization: spec.ClientCertAuth.Organizations,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
}
clientCertConfig := newClientCertConfigFromKubeConfigSpec(spec)
clientCert, clientKey, err := pkiutil.NewCertAndKey(spec.CACert, spec.ClientCertAuth.CAKey, &clientCertConfig)
if err != nil {
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
}
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
func validateKubeConfig(outDir, filename string, config *clientcmdapi.Config) error {
kubeConfigFilePath := filepath.Join(outDir, filename)
@ -386,3 +360,115 @@ func ValidateKubeconfigsForExternalCA(outDir string, cfg *kubeadmapi.InitConfigu
}
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
}

View File

@ -291,16 +291,14 @@ func TryLoadKeyFromDisk(pkiPath, name string) (crypto.Signer, error) {
// TryLoadCSRAndKeyFromDisk tries to load the CSR and key from the disk
func TryLoadCSRAndKeyFromDisk(pkiPath, name string) (*x509.CertificateRequest, crypto.Signer, error) {
csrPath := pathForCSR(pkiPath, name)
csr, err := CertificateRequestFromFile(csrPath)
csr, err := TryLoadCSRFromDisk(pkiPath, name)
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)
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
@ -335,6 +333,18 @@ func TryLoadPrivatePublicKeyFromDisk(pkiPath, name string) (*rsa.PrivateKey, *rs
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.
func PathsForCertAndKey(pkiPath, name string) (string, string) {
return pathForCert(pkiPath, name), pathForKey(pkiPath, name)