mirror of https://github.com/k3s-io/k3s
Merge pull request #41897 from luxas/kubeadm_secure_controlplane
Automatic merge from submit-queue (batch tested with PRs 41701, 41818, 41897, 41119, 41562) kubeadm: Secure the control plane communication and add the kubeconfig phase command **What this PR does / why we need it**: This generates kubeconfig files for the controller-manager and the scheduler, ref: https://github.com/kubernetes/kubeadm/issues/172 The second commit adds the `kubeadm alpha phase kubeconfig` command as described in the design doc: https://github.com/kubernetes/kubeadm/pull/156 **Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes # **Special notes for your reviewer**: @dmmcquay What kind of tests would you like for the kubeconfig phase command? **Release note**: ```release-note ``` @jbeda @mikedanese @dmmcquay @pires @liggitt @deads2k @errordeveloperpull/6/head
commit
b2765427a2
|
@ -24,6 +24,7 @@ go_library(
|
|||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
||||
"//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library",
|
||||
"//cmd/kubeadm/app/cmd/phases:go_default_library",
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/discovery:go_default_library",
|
||||
"//cmd/kubeadm/app/master:go_default_library",
|
||||
|
@ -78,6 +79,9 @@ filegroup(
|
|||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//cmd/kubeadm/app/cmd/phases:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/apiserver/pkg/util/flag"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases"
|
||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
|
@ -88,6 +89,7 @@ func NewKubeadmCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
|
|||
Short: "Experimental sub-commands not yet fully functional.",
|
||||
}
|
||||
experimentalCmd.AddCommand(NewCmdToken(out, err))
|
||||
experimentalCmd.AddCommand(phases.NewCmdPhase(out))
|
||||
cmds.AddCommand(experimentalCmd)
|
||||
|
||||
return cmds
|
||||
|
|
|
@ -190,7 +190,7 @@ func (i *Init) Run(out io.Writer) error {
|
|||
// so we'll pick the first one, there is much of chance to have an empty
|
||||
// slice by the time this gets called
|
||||
masterEndpoint := fmt.Sprintf("https://%s:%d", i.cfg.API.AdvertiseAddresses[0], i.cfg.API.Port)
|
||||
err = kubeconfigphase.CreateAdminAndKubeletKubeConfig(masterEndpoint, kubeadmapi.GlobalEnvParams.HostPKIPath, kubeadmapi.GlobalEnvParams.KubernetesDir)
|
||||
err = kubeconfigphase.CreateInitKubeConfigFiles(masterEndpoint, kubeadmapi.GlobalEnvParams.HostPKIPath, kubeadmapi.GlobalEnvParams.KubernetesDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"kubeconfig.go",
|
||||
"phase.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/constants:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//vendor:github.com/spf13/cobra",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package phases
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
)
|
||||
|
||||
func NewCmdKubeConfig(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "kubeconfig",
|
||||
Short: "Create KubeConfig files from given credentials.",
|
||||
RunE: subCmdRunE("kubeconfig"),
|
||||
}
|
||||
|
||||
cmd.AddCommand(NewCmdToken(out))
|
||||
cmd.AddCommand(NewCmdClientCerts(out))
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewCmdToken(out io.Writer) *cobra.Command {
|
||||
config := &kubeconfigphase.BuildConfigProperties{
|
||||
MakeClientCerts: false,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "token",
|
||||
Short: "Output a valid KubeConfig file to STDOUT with a token as the authentication method.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := RunCreateWithToken(out, config)
|
||||
kubeadmutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
addCommonFlags(cmd, config)
|
||||
cmd.Flags().StringVar(&config.Token, "token", "", "The path to the directory where the certificates are.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func NewCmdClientCerts(out io.Writer) *cobra.Command {
|
||||
config := &kubeconfigphase.BuildConfigProperties{
|
||||
MakeClientCerts: true,
|
||||
}
|
||||
cmd := &cobra.Command{
|
||||
Use: "client-certs",
|
||||
Short: "Output a valid KubeConfig file to STDOUT with a client certificates as the authentication method.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
err := RunCreateWithClientCerts(out, config)
|
||||
kubeadmutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
addCommonFlags(cmd, config)
|
||||
cmd.Flags().StringSliceVar(&config.Organization, "organization", []string{}, "The organization (group) the certificate should be in.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func addCommonFlags(cmd *cobra.Command, config *kubeconfigphase.BuildConfigProperties) {
|
||||
cmd.Flags().StringVar(&config.CertDir, "cert-dir", kubeadmconstants.DefaultCertDir, "The path to the directory where the certificates are.")
|
||||
cmd.Flags().StringVar(&config.ClientName, "client-name", "", "The name of the client for which the KubeConfig file will be generated.")
|
||||
cmd.Flags().StringVar(&config.APIServer, "server", "", "The location of the api server.")
|
||||
}
|
||||
|
||||
func validateCommonFlags(config *kubeconfigphase.BuildConfigProperties) error {
|
||||
if len(config.ClientName) == 0 {
|
||||
return fmt.Errorf("The --client-name flag is required")
|
||||
}
|
||||
if len(config.APIServer) == 0 {
|
||||
return fmt.Errorf("The --server flag is required")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunCreateWithToken generates a kubeconfig file from with a token as the authentication mechanism
|
||||
func RunCreateWithToken(out io.Writer, config *kubeconfigphase.BuildConfigProperties) error {
|
||||
if len(config.Token) == 0 {
|
||||
return fmt.Errorf("The --token flag is required")
|
||||
}
|
||||
if err := validateCommonFlags(config); err != nil {
|
||||
return err
|
||||
}
|
||||
kubeConfigBytes, err := kubeconfigphase.GetKubeConfigBytesFromSpec(*config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(out, string(kubeConfigBytes))
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunCreateWithClientCerts generates a kubeconfig file from with client certs as the authentication mechanism
|
||||
func RunCreateWithClientCerts(out io.Writer, config *kubeconfigphase.BuildConfigProperties) error {
|
||||
if err := validateCommonFlags(config); err != nil {
|
||||
return err
|
||||
}
|
||||
kubeConfigBytes, err := kubeconfigphase.GetKubeConfigBytesFromSpec(*config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(out, string(kubeConfigBytes))
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package phases
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func NewCmdPhase(out io.Writer) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "phase",
|
||||
Short: "Invoke subsets of kubeadm functions separately for a manual install.",
|
||||
RunE: subCmdRunE("phase"),
|
||||
}
|
||||
cmd.AddCommand(NewCmdKubeConfig(out))
|
||||
return cmd
|
||||
}
|
||||
|
||||
// subCmdRunE returns a function that handles a case where a subcommand must be specified
|
||||
// Without this callback, if a user runs just the command without a subcommand,
|
||||
// or with an invalid subcommand, cobra will print usage information, but still exit cleanly.
|
||||
// We want to return an error code in these cases so that the
|
||||
// user knows that their command was invalid.
|
||||
func subCmdRunE(name string) func(*cobra.Command, []string) error {
|
||||
return func(_ *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return fmt.Errorf("missing subcommand; %q is not meant to be run on its own", name)
|
||||
} else {
|
||||
return fmt.Errorf("invalid subcommand: %q", args[0])
|
||||
}
|
||||
}
|
||||
}
|
|
@ -223,6 +223,8 @@ func resetConfigDir(configPathDir, pkiPathDir string) {
|
|||
filesToClean := []string{
|
||||
filepath.Join(configPathDir, kubeadmconstants.AdminKubeConfigFileName),
|
||||
filepath.Join(configPathDir, kubeadmconstants.KubeletKubeConfigFileName),
|
||||
filepath.Join(configPathDir, kubeadmconstants.ControllerManagerKubeConfigFileName),
|
||||
filepath.Join(configPathDir, kubeadmconstants.SchedulerKubeConfigFileName),
|
||||
}
|
||||
fmt.Printf("[reset] Deleting files: %v\n", filesToClean)
|
||||
for _, path := range filesToClean {
|
||||
|
|
|
@ -51,12 +51,23 @@ const (
|
|||
FrontProxyClientCertName = "front-proxy-client.crt"
|
||||
FrontProxyClientKeyName = "front-proxy-client.key"
|
||||
|
||||
AdminKubeConfigFileName = "admin.conf"
|
||||
KubeletKubeConfigFileName = "kubelet.conf"
|
||||
AdminKubeConfigFileName = "admin.conf"
|
||||
KubeletKubeConfigFileName = "kubelet.conf"
|
||||
ControllerManagerKubeConfigFileName = "controller-manager.conf"
|
||||
SchedulerKubeConfigFileName = "scheduler.conf"
|
||||
|
||||
DefaultCertDir = "/etc/kubernetes/pki"
|
||||
|
||||
// Important: a "v"-prefix shouldn't exist here; semver doesn't allow that
|
||||
MinimumControlPlaneVersion = "1.6.0-alpha.2"
|
||||
|
||||
// Some well-known users and groups in the core Kubernetes authorization system
|
||||
|
||||
ControllerManagerUser = "system:kube-controller-manager"
|
||||
SchedulerUser = "system:kube-scheduler"
|
||||
MastersGroup = "system:masters"
|
||||
NodesGroup = "system:nodes"
|
||||
|
||||
// Constants for what we name our ServiceAccounts with limited access to the cluster in case of RBAC
|
||||
KubeDNSServiceAccountName = "kube-dns"
|
||||
KubeProxyServiceAccountName = "kube-proxy"
|
||||
|
|
|
@ -91,10 +91,11 @@ func WriteStaticPodManifests(cfg *kubeadmapi.MasterConfiguration) error {
|
|||
Name: kubeScheduler,
|
||||
Image: images.GetCoreImage(images.KubeSchedulerImage, cfg, kubeadmapi.GlobalEnvParams.HyperkubeImage),
|
||||
Command: getSchedulerCommand(cfg, false),
|
||||
VolumeMounts: []api.VolumeMount{k8sVolumeMount()},
|
||||
LivenessProbe: componentProbe(10251, "/healthz"),
|
||||
Resources: componentResources("100m"),
|
||||
Env: getProxyEnvVars(),
|
||||
}),
|
||||
}, k8sVolume(cfg)),
|
||||
}
|
||||
|
||||
// Add etcd static pod spec only if external etcd is not configured
|
||||
|
@ -378,7 +379,7 @@ func getControllerManagerCommand(cfg *kubeadmapi.MasterConfiguration, selfHosted
|
|||
command = append(getComponentBaseCommand(controllerManager),
|
||||
"--address=127.0.0.1",
|
||||
"--leader-elect",
|
||||
"--master=127.0.0.1:8080",
|
||||
"--kubeconfig="+path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeadmconstants.ControllerManagerKubeConfigFileName),
|
||||
"--root-ca-file="+getCertFilePath(kubeadmconstants.CACertName),
|
||||
"--service-account-private-key-file="+getCertFilePath(kubeadmconstants.ServiceAccountPrivateKeyName),
|
||||
"--cluster-signing-cert-file="+getCertFilePath(kubeadmconstants.CACertName),
|
||||
|
@ -416,7 +417,7 @@ func getSchedulerCommand(cfg *kubeadmapi.MasterConfiguration, selfHosted bool) [
|
|||
command = append(getComponentBaseCommand(scheduler),
|
||||
"--address=127.0.0.1",
|
||||
"--leader-elect",
|
||||
"--master=127.0.0.1:8080",
|
||||
"--kubeconfig="+path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeadmconstants.SchedulerKubeConfigFileName),
|
||||
)
|
||||
|
||||
return command
|
||||
|
|
|
@ -483,7 +483,7 @@ func TestGetControllerManagerCommand(t *testing.T) {
|
|||
"kube-controller-manager",
|
||||
"--address=127.0.0.1",
|
||||
"--leader-elect",
|
||||
"--master=127.0.0.1:8080",
|
||||
"--kubeconfig=" + kubeadmapi.GlobalEnvParams.KubernetesDir + "/controller-manager.conf",
|
||||
"--root-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.crt",
|
||||
"--service-account-private-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/sa.key",
|
||||
"--cluster-signing-cert-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.crt",
|
||||
|
@ -498,7 +498,7 @@ func TestGetControllerManagerCommand(t *testing.T) {
|
|||
"kube-controller-manager",
|
||||
"--address=127.0.0.1",
|
||||
"--leader-elect",
|
||||
"--master=127.0.0.1:8080",
|
||||
"--kubeconfig=" + kubeadmapi.GlobalEnvParams.KubernetesDir + "/controller-manager.conf",
|
||||
"--root-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.crt",
|
||||
"--service-account-private-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/sa.key",
|
||||
"--cluster-signing-cert-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.crt",
|
||||
|
@ -514,7 +514,7 @@ func TestGetControllerManagerCommand(t *testing.T) {
|
|||
"kube-controller-manager",
|
||||
"--address=127.0.0.1",
|
||||
"--leader-elect",
|
||||
"--master=127.0.0.1:8080",
|
||||
"--kubeconfig=" + kubeadmapi.GlobalEnvParams.KubernetesDir + "/controller-manager.conf",
|
||||
"--root-ca-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.crt",
|
||||
"--service-account-private-key-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/sa.key",
|
||||
"--cluster-signing-cert-file=" + kubeadmapi.GlobalEnvParams.HostPKIPath + "/ca.crt",
|
||||
|
@ -552,7 +552,7 @@ func TestGetSchedulerCommand(t *testing.T) {
|
|||
"kube-scheduler",
|
||||
"--address=127.0.0.1",
|
||||
"--leader-elect",
|
||||
"--master=127.0.0.1:8080",
|
||||
"--kubeconfig=" + kubeadmapi.GlobalEnvParams.KubernetesDir + "/scheduler.conf",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration, pkiDir string) error {
|
|||
// TODO: Add a test case to verify that this cert has the x509.ExtKeyUsageClientAuth flag
|
||||
config := certutil.Config{
|
||||
CommonName: "kube-apiserver-kubelet-client",
|
||||
Organization: []string{"system:masters"},
|
||||
Organization: []string{kubeadmconstants.MastersGroup},
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
apiClientCert, apiClientKey, err := pkiutil.NewCertAndKey(caCert, caKey, config)
|
||||
|
|
|
@ -18,7 +18,6 @@ package kubeconfig
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
|
@ -32,6 +31,16 @@ import (
|
|||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||
)
|
||||
|
||||
// BuildConfigProperties holds some simple information about how this phase should build the KubeConfig object
|
||||
type BuildConfigProperties struct {
|
||||
CertDir string
|
||||
ClientName string
|
||||
Organization []string
|
||||
APIServer string
|
||||
Token string
|
||||
MakeClientCerts bool
|
||||
}
|
||||
|
||||
// TODO: Make an integration test for this function that runs after the certificates phase
|
||||
// and makes sure that those two phases work well together...
|
||||
|
||||
|
@ -42,61 +51,113 @@ import (
|
|||
// /etc/kubernetes/{admin,kubelet}.conf exist but the CA cert doesn't match what's in the pki dir => error
|
||||
// /etc/kubernetes/{admin,kubelet}.conf exist but not certs => certs will be generated and conflict with the kubeconfig files => error
|
||||
|
||||
// CreateAdminAndKubeletKubeConfig is called from the main init and does the work for the default phase behaviour
|
||||
func CreateAdminAndKubeletKubeConfig(masterEndpoint, pkiDir, outDir string) error {
|
||||
// CreateInitKubeConfigFiles is called from the main init and does the work for the default phase behaviour
|
||||
func CreateInitKubeConfigFiles(masterEndpoint, pkiDir, outDir string) error {
|
||||
|
||||
// Try to load ca.crt and ca.key from the PKI directory
|
||||
caCert, caKey, err := pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, kubeadmconstants.CACertAndKeyBaseName)
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't create a kubeconfig; the CA files couldn't be loaded: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// User admin should have full access to the cluster
|
||||
// TODO: Add test case that make sure this cert has the x509.ExtKeyUsageClientAuth flag
|
||||
adminCertConfig := certutil.Config{
|
||||
CommonName: "kubernetes-admin",
|
||||
Organization: []string{"system:masters"},
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
adminKubeConfigFilePath := filepath.Join(outDir, kubeadmconstants.AdminKubeConfigFileName)
|
||||
if err := createKubeConfigFileForClient(masterEndpoint, adminKubeConfigFilePath, adminCertConfig, caCert, caKey); err != nil {
|
||||
return fmt.Errorf("couldn't create config for the admin: %v", err)
|
||||
// Create a lightweight specification for what the files should look like
|
||||
filesToCreateFromSpec := map[string]BuildConfigProperties{
|
||||
kubeadmconstants.AdminKubeConfigFileName: {
|
||||
ClientName: "kubernetes-admin",
|
||||
APIServer: masterEndpoint,
|
||||
CertDir: pkiDir,
|
||||
Organization: []string{kubeadmconstants.MastersGroup},
|
||||
MakeClientCerts: true,
|
||||
},
|
||||
kubeadmconstants.KubeletKubeConfigFileName: {
|
||||
ClientName: fmt.Sprintf("system:node:%s", hostname),
|
||||
APIServer: masterEndpoint,
|
||||
CertDir: pkiDir,
|
||||
Organization: []string{kubeadmconstants.NodesGroup},
|
||||
MakeClientCerts: true,
|
||||
},
|
||||
kubeadmconstants.ControllerManagerKubeConfigFileName: {
|
||||
ClientName: kubeadmconstants.ControllerManagerUser,
|
||||
APIServer: masterEndpoint,
|
||||
CertDir: pkiDir,
|
||||
MakeClientCerts: true,
|
||||
},
|
||||
kubeadmconstants.SchedulerKubeConfigFileName: {
|
||||
ClientName: kubeadmconstants.SchedulerUser,
|
||||
APIServer: masterEndpoint,
|
||||
CertDir: pkiDir,
|
||||
MakeClientCerts: true,
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: The kubelet should have limited access to the cluster. Right now, this gives kubelet basically root access
|
||||
// and we do need that in the bootstrap phase, but we should swap it out after the control plane is up
|
||||
// TODO: Add test case that make sure this cert has the x509.ExtKeyUsageClientAuth flag
|
||||
kubeletCertConfig := certutil.Config{
|
||||
CommonName: "kubelet",
|
||||
Organization: []string{"system:nodes"},
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
kubeletKubeConfigFilePath := filepath.Join(outDir, kubeadmconstants.KubeletKubeConfigFileName)
|
||||
if err := createKubeConfigFileForClient(masterEndpoint, kubeletKubeConfigFilePath, kubeletCertConfig, caCert, caKey); err != nil {
|
||||
return fmt.Errorf("couldn't create a kubeconfig file for the kubelet: %v", err)
|
||||
// Loop through all specs for kubeconfig files and create them if necessary
|
||||
for filename, config := range filesToCreateFromSpec {
|
||||
kubeconfig, err := buildKubeConfig(config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kubeConfigFilePath := filepath.Join(outDir, filename)
|
||||
err = writeKubeconfigToDiskIfNotExists(kubeConfigFilePath, kubeconfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO make credentials for the controller-manager and scheduler
|
||||
return nil
|
||||
}
|
||||
|
||||
func createKubeConfigFileForClient(masterEndpoint, kubeConfigFilePath string, config certutil.Config, caCert *x509.Certificate, caKey *rsa.PrivateKey) error {
|
||||
cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, config)
|
||||
// GetKubeConfigBytesFromSpec takes properties how to build a KubeConfig file and then returns the bytes of that file
|
||||
func GetKubeConfigBytesFromSpec(config BuildConfigProperties) ([]byte, error) {
|
||||
kubeconfig, err := buildKubeConfig(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure while creating %s client certificate [%v]", config.CommonName, err)
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
kubeconfig := kubeconfigutil.CreateWithCerts(
|
||||
masterEndpoint,
|
||||
"kubernetes",
|
||||
config.CommonName,
|
||||
certutil.EncodeCertPEM(caCert),
|
||||
certutil.EncodePrivateKeyPEM(key),
|
||||
certutil.EncodeCertPEM(cert),
|
||||
)
|
||||
kubeConfigBytes, err := clientcmd.Write(*kubeconfig)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
return kubeConfigBytes, nil
|
||||
}
|
||||
|
||||
// Write it now to a file if there already isn't a valid one
|
||||
return writeKubeconfigToDiskIfNotExists(kubeConfigFilePath, kubeconfig)
|
||||
// buildKubeConfig creates a kubeconfig object from some commonly specified properties in the struct above
|
||||
func buildKubeConfig(config BuildConfigProperties) (*clientcmdapi.Config, error) {
|
||||
|
||||
// Try to load ca.crt and ca.key from the PKI directory
|
||||
caCert, caKey, err := pkiutil.TryLoadCertAndKeyFromDisk(config.CertDir, kubeadmconstants.CACertAndKeyBaseName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't create a kubeconfig; the CA files couldn't be loaded: %v", err)
|
||||
}
|
||||
|
||||
// If this file should have client certs, generate one from the spec
|
||||
if config.MakeClientCerts {
|
||||
certConfig := certutil.Config{
|
||||
CommonName: config.ClientName,
|
||||
Organization: config.Organization,
|
||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||
}
|
||||
cert, key, err := pkiutil.NewCertAndKey(caCert, caKey, certConfig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure while creating %s client certificate [%v]", certConfig.CommonName, err)
|
||||
}
|
||||
return kubeconfigutil.CreateWithCerts(
|
||||
config.APIServer,
|
||||
"kubernetes",
|
||||
config.ClientName,
|
||||
certutil.EncodeCertPEM(caCert),
|
||||
certutil.EncodePrivateKeyPEM(key),
|
||||
certutil.EncodeCertPEM(cert),
|
||||
), nil
|
||||
}
|
||||
|
||||
// otherwise, create a kubeconfig with a token
|
||||
return kubeconfigutil.CreateWithToken(
|
||||
config.APIServer,
|
||||
"kubernetes",
|
||||
config.ClientName,
|
||||
certutil.EncodeCertPEM(caCert),
|
||||
config.Token,
|
||||
), nil
|
||||
}
|
||||
|
||||
// writeKubeconfigToDiskIfNotExists saves the KubeConfig struct to disk if there isn't any file at the given path
|
||||
|
|
|
@ -74,6 +74,7 @@ cleanup-iptables
|
|||
client-ca-file
|
||||
client-certificate
|
||||
client-key
|
||||
client-name
|
||||
clientset-api-path
|
||||
clientset-name
|
||||
clientset-only
|
||||
|
|
Loading…
Reference in New Issue