mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-21 19:01:49 +00:00
Merge pull request #92183 from wallrj/2163-csr-only-external-ca-mode-2
kubeadm alpha certs generate-csr
This commit is contained in:
commit
19f0a54d6b
@ -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",
|
||||
],
|
||||
)
|
||||
|
@ -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{
|
||||
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user