diff --git a/cmd/kubeadm/app/constants/constants.go b/cmd/kubeadm/app/constants/constants.go index a7b4b70151e..93a7a5b9e5c 100644 --- a/cmd/kubeadm/app/constants/constants.go +++ b/cmd/kubeadm/app/constants/constants.go @@ -49,12 +49,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" diff --git a/cmd/kubeadm/app/master/manifests.go b/cmd/kubeadm/app/master/manifests.go index 03d0dead9df..925303720b8 100644 --- a/cmd/kubeadm/app/master/manifests.go +++ b/cmd/kubeadm/app/master/manifests.go @@ -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 diff --git a/cmd/kubeadm/app/master/manifests_test.go b/cmd/kubeadm/app/master/manifests_test.go index 375ecb244ae..b469553dbd5 100644 --- a/cmd/kubeadm/app/master/manifests_test.go +++ b/cmd/kubeadm/app/master/manifests_test.go @@ -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", }, }, } diff --git a/cmd/kubeadm/app/phases/certs/certs.go b/cmd/kubeadm/app/phases/certs/certs.go index abba646d459..1311035a9fa 100644 --- a/cmd/kubeadm/app/phases/certs/certs.go +++ b/cmd/kubeadm/app/phases/certs/certs.go @@ -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) diff --git a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go index 5c2caac16aa..d04879e2a75 100644 --- a/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go +++ b/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go @@ -18,7 +18,6 @@ package kubeconfig import ( "bytes" - "crypto/rsa" "crypto/x509" "fmt" "os" @@ -32,6 +31,15 @@ import ( kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" ) +type KubeConfigProperties 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 +50,112 @@ 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]KubeConfigProperties{ + 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) +func GetKubeConfigBytesFromSpec(config KubeConfigProperties) ([]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 KubeConfigProperties) (*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