mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Refactor the pki, cert, kubeconfig code in the kubeadm binary into two separate and logically independent phases
This commit is contained in:
parent
733393d800
commit
720f3b45aa
@ -70,13 +70,6 @@ type Etcd struct {
|
||||
KeyFile string `json:"keyFile"`
|
||||
}
|
||||
|
||||
type Secrets struct {
|
||||
GivenToken string `json:"givenToken"` // dot-separated `<TokenID>.<Token>` set by the user
|
||||
TokenID string `json:"tokenID"` // optional on master side, will be generated if not specified
|
||||
Token []byte `json:"token"` // optional on master side, will be generated if not specified
|
||||
BearerToken string `json:"bearerToken"` // set based on Token
|
||||
}
|
||||
|
||||
type NodeConfiguration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
|
@ -27,6 +27,8 @@ go_library(
|
||||
"//cmd/kubeadm/app/discovery:go_default_library",
|
||||
"//cmd/kubeadm/app/master:go_default_library",
|
||||
"//cmd/kubeadm/app/node:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/certs:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
|
||||
"//cmd/kubeadm/app/preflight:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
|
||||
"github.com/renstrom/dedent"
|
||||
"github.com/spf13/cobra"
|
||||
@ -30,6 +31,10 @@ import (
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/cmd/flags"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
|
||||
kubemaster "k8s.io/kubernetes/cmd/kubeadm/app/master"
|
||||
|
||||
certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
||||
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
@ -103,31 +108,6 @@ func NewCmdInit(out io.Writer) *cobra.Command {
|
||||
|
||||
cmd.PersistentFlags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file")
|
||||
|
||||
// TODO (phase1+) @errordeveloper make the flags below not show up in --help but rather on --advanced-help
|
||||
cmd.PersistentFlags().StringSliceVar(
|
||||
&cfg.Etcd.Endpoints, "external-etcd-endpoints", cfg.Etcd.Endpoints,
|
||||
"etcd endpoints to use, in case you have an external cluster",
|
||||
)
|
||||
cmd.PersistentFlags().MarkDeprecated("external-etcd-endpoints", "this flag will be removed when componentconfig exists")
|
||||
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&cfg.Etcd.CAFile, "external-etcd-cafile", cfg.Etcd.CAFile,
|
||||
"etcd certificate authority certificate file. Note: The path must be in /etc/ssl/certs",
|
||||
)
|
||||
cmd.PersistentFlags().MarkDeprecated("external-etcd-cafile", "this flag will be removed when componentconfig exists")
|
||||
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&cfg.Etcd.CertFile, "external-etcd-certfile", cfg.Etcd.CertFile,
|
||||
"etcd client certificate file. Note: The path must be in /etc/ssl/certs",
|
||||
)
|
||||
cmd.PersistentFlags().MarkDeprecated("external-etcd-certfile", "this flag will be removed when componentconfig exists")
|
||||
|
||||
cmd.PersistentFlags().StringVar(
|
||||
&cfg.Etcd.KeyFile, "external-etcd-keyfile", cfg.Etcd.KeyFile,
|
||||
"etcd client key file. Note: The path must be in /etc/ssl/certs",
|
||||
)
|
||||
cmd.PersistentFlags().MarkDeprecated("external-etcd-keyfile", "this flag will be removed when componentconfig exists")
|
||||
|
||||
cmd.PersistentFlags().BoolVar(
|
||||
&skipPreFlight, "skip-preflight-checks", skipPreFlight,
|
||||
"skip preflight checks normally run before modifying the system",
|
||||
@ -215,6 +195,13 @@ func (i *Init) Validate() error {
|
||||
// Run executes master node provisioning, including certificates, needed static pod manifests, etc.
|
||||
func (i *Init) Run(out io.Writer) error {
|
||||
|
||||
// PHASE 1: Generate certificates
|
||||
caCert, err := certphase.CreatePKIAssets(i.cfg, kubeadmapi.GlobalEnvParams.HostPKIPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Exception:
|
||||
if i.cfg.Discovery.Token != nil {
|
||||
if err := kubemaster.PrepareTokenDiscovery(i.cfg.Discovery.Token); err != nil {
|
||||
return err
|
||||
@ -224,36 +211,23 @@ func (i *Init) Run(out io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
// PHASE 2: Generate kubeconfig files for the admin and the kubelet
|
||||
|
||||
// TODO this is not great, but there is only one address we can use here
|
||||
// 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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Phase 3: Bootstrap the control plane
|
||||
if err := kubemaster.WriteStaticPodManifests(i.cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
caKey, caCert, err := kubemaster.CreatePKIAssets(i.cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
kubeconfigs, err := kubemaster.CreateCertsAndConfigForClients(i.cfg.API, []string{"kubelet", "admin"}, caKey, caCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// kubeadm is responsible for writing the following kubeconfig file, which
|
||||
// kubelet should be waiting for. Help user avoid foot-shooting by refusing to
|
||||
// write a file that has already been written (the kubelet will be up and
|
||||
// running in that case - they'd need to stop the kubelet, remove the file, and
|
||||
// start it again in that case).
|
||||
// TODO(phase1+) this is no longer the right place to guard against foo-shooting,
|
||||
// we need to decide how to handle existing files (it may be handy to support
|
||||
// importing existing files, may be we could even make our command idempotant,
|
||||
// or at least allow for external PKI and stuff)
|
||||
for name, kubeconfig := range kubeconfigs {
|
||||
if err := kubeadmutil.WriteKubeconfigIfNotExists(name, kubeconfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
client, err := kubemaster.CreateClientAndWaitForAPI(kubeconfigs["admin"])
|
||||
client, err := kubemaster.CreateClientAndWaitForAPI(path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeconfigphase.AdminKubeConfigFileName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
|
||||
"github.com/renstrom/dedent"
|
||||
"github.com/spf13/cobra"
|
||||
@ -29,6 +30,7 @@ import (
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/discovery"
|
||||
kubenode "k8s.io/kubernetes/cmd/kubeadm/app/node"
|
||||
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
@ -159,7 +161,7 @@ func (j *Join) Run(out io.Writer) error {
|
||||
}
|
||||
}
|
||||
|
||||
err = kubeadmutil.WriteKubeconfigIfNotExists("kubelet", cfg)
|
||||
err = kubeconfigphase.WriteKubeconfigToDisk(path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, kubeconfigphase.KubeletKubeConfigFileName), cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -14,9 +14,7 @@ go_library(
|
||||
"addons.go",
|
||||
"apiclient.go",
|
||||
"discovery.go",
|
||||
"kubeconfig.go",
|
||||
"manifests.go",
|
||||
"pki.go",
|
||||
"tokens.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
@ -51,9 +49,7 @@ go_test(
|
||||
"addons_test.go",
|
||||
"apiclient_test.go",
|
||||
"discovery_test.go",
|
||||
"kubeconfig_test.go",
|
||||
"manifests_test.go",
|
||||
"pki_test.go",
|
||||
"tokens_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
@ -62,7 +58,6 @@ go_test(
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/util/cert:go_default_library",
|
||||
"//pkg/util/intstr:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@ -60,8 +60,8 @@ func CreateClientFromFile(path string) (*clientset.Clientset, error) {
|
||||
return createAPIClient(adminKubeconfig)
|
||||
}
|
||||
|
||||
func CreateClientAndWaitForAPI(adminConfig *clientcmdapi.Config) (*clientset.Clientset, error) {
|
||||
client, err := createAPIClient(adminConfig)
|
||||
func CreateClientAndWaitForAPI(file string) (*clientset.Clientset, error) {
|
||||
client, err := CreateClientFromFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 master
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
certutil "k8s.io/kubernetes/pkg/util/cert"
|
||||
)
|
||||
|
||||
func CreateCertsAndConfigForClients(cfg kubeadmapi.API, clientNames []string, caKey *rsa.PrivateKey, caCert *x509.Certificate) (map[string]*clientcmdapi.Config, error) {
|
||||
|
||||
basicClientConfig := kubeadmutil.CreateBasicClientConfig(
|
||||
"kubernetes",
|
||||
// TODO this is not great, but there is only one address we can use here
|
||||
// so we'll pick the first one, there is much of chance to have an empty
|
||||
// slice by the time this gets called
|
||||
fmt.Sprintf("https://%s:%d", cfg.AdvertiseAddresses[0], cfg.Port),
|
||||
certutil.EncodeCertPEM(caCert),
|
||||
)
|
||||
|
||||
configs := map[string]*clientcmdapi.Config{}
|
||||
|
||||
for _, client := range clientNames {
|
||||
key, cert, err := newClientKeyAndCert(caCert, caKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure while creating %s client certificate - [%v]", client, err)
|
||||
}
|
||||
config := kubeadmutil.MakeClientConfigWithCerts(
|
||||
basicClientConfig,
|
||||
"kubernetes",
|
||||
client,
|
||||
certutil.EncodePrivateKeyPEM(key),
|
||||
certutil.EncodeCertPEM(cert),
|
||||
)
|
||||
configs[client] = config
|
||||
}
|
||||
|
||||
return configs, nil
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 master
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"testing"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
)
|
||||
|
||||
func TestCreateCertsAndConfigForClients(t *testing.T) {
|
||||
var tests = []struct {
|
||||
a kubeadmapi.API
|
||||
cn []string
|
||||
caKeySize int
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
a: kubeadmapi.API{AdvertiseAddresses: []string{"foo"}},
|
||||
cn: []string{"localhost"},
|
||||
caKeySize: 128,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
a: kubeadmapi.API{AdvertiseAddresses: []string{"foo"}},
|
||||
cn: []string{},
|
||||
caKeySize: 128,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
a: kubeadmapi.API{AdvertiseAddresses: []string{"foo"}},
|
||||
cn: []string{"localhost"},
|
||||
caKeySize: 2048,
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, rt := range tests {
|
||||
caKey, err := rsa.GenerateKey(rand.Reader, rt.caKeySize)
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create rsa Private Key")
|
||||
}
|
||||
caCert := &x509.Certificate{}
|
||||
_, actual := CreateCertsAndConfigForClients(rt.a, rt.cn, caKey, caCert)
|
||||
if (actual == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed CreateCertsAndConfigForClients:\n\texpected: %t\n\t actual: %t",
|
||||
rt.expected,
|
||||
(actual == nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 master
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"path"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||
certutil "k8s.io/kubernetes/pkg/util/cert"
|
||||
)
|
||||
|
||||
func newCertificateAuthority() (*rsa.PrivateKey, *x509.Certificate, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
|
||||
}
|
||||
|
||||
config := certutil.Config{
|
||||
CommonName: "kubernetes",
|
||||
}
|
||||
|
||||
cert, err := certutil.NewSelfSignedCACert(config, key)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to create self-signed certificate [%v]", err)
|
||||
}
|
||||
|
||||
return key, cert, nil
|
||||
}
|
||||
|
||||
func newServerKeyAndCert(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey, altNames certutil.AltNames) (*rsa.PrivateKey, *x509.Certificate, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
|
||||
}
|
||||
|
||||
internalAPIServerFQDN := []string{
|
||||
"kubernetes",
|
||||
"kubernetes.default",
|
||||
"kubernetes.default.svc",
|
||||
fmt.Sprintf("kubernetes.default.svc.%s", cfg.Networking.DNSDomain),
|
||||
}
|
||||
|
||||
_, n, err := net.ParseCIDR(cfg.Networking.ServiceSubnet)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error parsing CIDR %q: %v", cfg.Networking.ServiceSubnet, err)
|
||||
}
|
||||
internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(n, 1)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to allocate IP address for the API server from the given CIDR (%q) [%v]", &cfg.Networking.ServiceSubnet, err)
|
||||
}
|
||||
|
||||
altNames.IPs = append(altNames.IPs, internalAPIServerVirtualIP)
|
||||
altNames.DNSNames = append(altNames.DNSNames, internalAPIServerFQDN...)
|
||||
|
||||
config := certutil.Config{
|
||||
CommonName: "kube-apiserver",
|
||||
AltNames: altNames,
|
||||
}
|
||||
cert, err := certutil.NewSignedCert(config, key, caCert, caKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err)
|
||||
}
|
||||
|
||||
return key, cert, nil
|
||||
}
|
||||
|
||||
func newClientKeyAndCert(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*rsa.PrivateKey, *x509.Certificate, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
|
||||
}
|
||||
|
||||
config := certutil.Config{
|
||||
CommonName: "kubernetes-admin",
|
||||
}
|
||||
cert, err := certutil.NewSignedCert(config, key, caCert, caKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err)
|
||||
}
|
||||
|
||||
return key, cert, nil
|
||||
}
|
||||
|
||||
func writeKeysAndCert(pkiPath string, name string, key *rsa.PrivateKey, cert *x509.Certificate) error {
|
||||
publicKeyPath, privateKeyPath, certificatePath := pathsKeysCerts(pkiPath, name)
|
||||
|
||||
if key != nil {
|
||||
if err := certutil.WriteKey(privateKeyPath, certutil.EncodePrivateKeyPEM(key)); err != nil {
|
||||
return fmt.Errorf("unable to write private key file (%q) [%v]", privateKeyPath, err)
|
||||
}
|
||||
if pubKey, err := certutil.EncodePublicKeyPEM(&key.PublicKey); err == nil {
|
||||
if err := certutil.WriteKey(publicKeyPath, pubKey); err != nil {
|
||||
return fmt.Errorf("unable to write public key file (%q) [%v]", publicKeyPath, err)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("unable to encode public key to PEM [%v]", err)
|
||||
}
|
||||
}
|
||||
|
||||
if cert != nil {
|
||||
if err := certutil.WriteCert(certificatePath, certutil.EncodeCertPEM(cert)); err != nil {
|
||||
return fmt.Errorf("unable to write certificate file (%q) [%v]", certificatePath, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pathsKeysCerts(pkiPath, name string) (string, string, string) {
|
||||
return path.Join(pkiPath, fmt.Sprintf("%s-pub.pem", name)),
|
||||
path.Join(pkiPath, fmt.Sprintf("%s-key.pem", name)),
|
||||
path.Join(pkiPath, fmt.Sprintf("%s.pem", name))
|
||||
}
|
||||
|
||||
func newServiceAccountKey() (*rsa.PrivateKey, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane.
|
||||
// It first generates a self-signed CA certificate, a server certificate (signed by the CA) and a key for
|
||||
// signing service account tokens. It returns CA key and certificate, which is convenient for use with
|
||||
// client config funcs.
|
||||
func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration) (*rsa.PrivateKey, *x509.Certificate, error) {
|
||||
var (
|
||||
err error
|
||||
altNames certutil.AltNames
|
||||
)
|
||||
|
||||
for _, a := range cfg.API.AdvertiseAddresses {
|
||||
if ip := net.ParseIP(a); ip != nil {
|
||||
altNames.IPs = append(altNames.IPs, ip)
|
||||
} else {
|
||||
return nil, nil, fmt.Errorf("could not parse ip %q", a)
|
||||
}
|
||||
}
|
||||
altNames.DNSNames = append(altNames.DNSNames, cfg.API.ExternalDNSNames...)
|
||||
|
||||
pkiPath := kubeadmapi.GlobalEnvParams.HostPKIPath
|
||||
|
||||
caKey, caCert, err := newCertificateAuthority()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failure while creating CA keys and certificate [%v]", err)
|
||||
}
|
||||
|
||||
if err := writeKeysAndCert(pkiPath, "ca", caKey, caCert); err != nil {
|
||||
return nil, nil, fmt.Errorf("failure while saving CA keys and certificate [%v]", err)
|
||||
}
|
||||
fmt.Println("[certificates] Generated Certificate Authority key and certificate.")
|
||||
|
||||
apiKey, apiCert, err := newServerKeyAndCert(cfg, caCert, caKey, altNames)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failure while creating API server keys and certificate [%v]", err)
|
||||
}
|
||||
|
||||
if err := writeKeysAndCert(pkiPath, "apiserver", apiKey, apiCert); err != nil {
|
||||
return nil, nil, fmt.Errorf("failure while saving API server keys and certificate [%v]", err)
|
||||
}
|
||||
fmt.Println("[certificates] Generated API Server key and certificate")
|
||||
|
||||
saKey, err := newServiceAccountKey()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failure while creating service account signing keys [%v]", err)
|
||||
}
|
||||
if err := writeKeysAndCert(pkiPath, "sa", saKey, nil); err != nil {
|
||||
return nil, nil, fmt.Errorf("failure while saving service account signing keys [%v]", err)
|
||||
}
|
||||
fmt.Println("[certificates] Generated Service Account signing keys")
|
||||
fmt.Printf("[certificates] Created keys and certificates in %q\n", pkiPath)
|
||||
return caKey, caCert, nil
|
||||
}
|
@ -18,6 +18,7 @@ go_library(
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apis/certificates:go_default_library",
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"time"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/certificates"
|
||||
@ -109,13 +110,15 @@ func EstablishMasterConnection(c *kubeadmapi.TokenDiscovery, clusterInfo *kubead
|
||||
|
||||
// creates a set of clients for this endpoint
|
||||
func createClients(caCert []byte, endpoint, token string, nodeName types.NodeName) (*clientset.Clientset, error) {
|
||||
bareClientConfig := kubeadmutil.CreateBasicClientConfig("kubernetes", endpoint, caCert)
|
||||
bootstrapClientConfig, err := clientcmd.NewDefaultClientConfig(
|
||||
*kubeadmutil.MakeClientConfigWithToken(
|
||||
bareClientConfig, "kubernetes", fmt.Sprintf("kubelet-%s", nodeName), token,
|
||||
),
|
||||
&clientcmd.ConfigOverrides{},
|
||||
).ClientConfig()
|
||||
clientConfig := kubeconfigphase.MakeClientConfigWithToken(
|
||||
endpoint,
|
||||
"kubernetes",
|
||||
fmt.Sprintf("kubelet-%s", nodeName),
|
||||
caCert,
|
||||
token,
|
||||
)
|
||||
|
||||
bootstrapClientConfig, err := clientcmd.NewDefaultClientConfig(*clientConfig, &clientcmd.ConfigOverrides{}).ClientConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create API client configuration [%v]", err)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
@ -30,15 +30,13 @@ import (
|
||||
)
|
||||
|
||||
func PerformTLSBootstrapDeprecated(connection *ConnectionDetails) (*clientcmdapi.Config, error) {
|
||||
csrClient := connection.CertClient.CertificateSigningRequests()
|
||||
|
||||
fmt.Println("[csr] Created API client to obtain unique certificate for this node, generating keys and certificate signing request")
|
||||
|
||||
key, err := certutil.MakeEllipticPrivateKeyPEM()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generating private key [%v]", err)
|
||||
}
|
||||
cert, err := csr.RequestNodeCertificate(csrClient, key, connection.NodeName)
|
||||
cert, err := csr.RequestNodeCertificate(connection.CertClient.CertificateSigningRequests(), key, connection.NodeName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to request signed certificate from the API server [%v]", err)
|
||||
}
|
||||
@ -49,13 +47,16 @@ func PerformTLSBootstrapDeprecated(connection *ConnectionDetails) (*clientcmdapi
|
||||
fmt.Printf("[csr] Received signed certificate from the API server:\n%s\n", fmtCert)
|
||||
fmt.Println("[csr] Generating kubelet configuration")
|
||||
|
||||
bareClientConfig := kubeadmutil.CreateBasicClientConfig("kubernetes", connection.Endpoint, connection.CACert)
|
||||
finalConfig := kubeadmutil.MakeClientConfigWithCerts(
|
||||
bareClientConfig, "kubernetes", fmt.Sprintf("kubelet-%s", connection.NodeName),
|
||||
key, cert,
|
||||
newConfig := kubeconfigphase.MakeClientConfigWithCerts(
|
||||
connection.Endpoint,
|
||||
"kubernetes",
|
||||
fmt.Sprintf("kubelet-%s", connection.NodeName),
|
||||
connection.CACert,
|
||||
key,
|
||||
cert,
|
||||
)
|
||||
|
||||
return finalConfig, nil
|
||||
return newConfig, nil
|
||||
}
|
||||
|
||||
// PerformTLSBootstrap executes a certificate signing request with the
|
||||
@ -75,22 +76,22 @@ func PerformTLSBootstrap(cfg *clientcmdapi.Config) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("<node/csr> created API client to obtain unique certificate for this node, generating keys and certificate signing request")
|
||||
fmt.Println("[csr] Created API client to obtain unique certificate for this node, generating keys and certificate signing request")
|
||||
|
||||
key, err := certutil.MakeEllipticPrivateKeyPEM()
|
||||
if err != nil {
|
||||
return fmt.Errorf("<node/csr> failed to generating private key [%v]", err)
|
||||
return fmt.Errorf("failed to generating private key [%v]", err)
|
||||
}
|
||||
cert, err := csr.RequestNodeCertificate(c.Certificates().CertificateSigningRequests(), key, name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("<node/csr> failed to request signed certificate from the API server [%v]", err)
|
||||
return fmt.Errorf("failed to request signed certificate from the API server [%v]", err)
|
||||
}
|
||||
fmtCert, err := certutil.FormatBytesCert(cert)
|
||||
if err != nil {
|
||||
return fmt.Errorf("<node/csr> failed to format certificate [%v]", err)
|
||||
return fmt.Errorf("failed to format certificate [%v]", err)
|
||||
}
|
||||
fmt.Printf("<node/csr> received signed certificate from the API server:\n%s\n", fmtCert)
|
||||
fmt.Println("<node/csr> generating kubelet configuration")
|
||||
fmt.Printf("[csr] Received signed certificate from the API server")
|
||||
fmt.Println("[csr] Generating kubelet configuration")
|
||||
|
||||
cfg.AuthInfos["kubelet"] = &clientcmdapi.AuthInfo{
|
||||
ClientKeyData: key,
|
||||
|
38
cmd/kubeadm/app/phases/certs/BUILD
Normal file
38
cmd/kubeadm/app/phases/certs/BUILD
Normal file
@ -0,0 +1,38 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"certs_test.go",
|
||||
"pki_helpers_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//pkg/util/cert:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"certs.go",
|
||||
"doc.go",
|
||||
"pki_helpers.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//pkg/registry/core/service/ipallocator:go_default_library",
|
||||
"//pkg/util/cert:go_default_library",
|
||||
],
|
||||
)
|
102
cmd/kubeadm/app/phases/certs/certs.go
Normal file
102
cmd/kubeadm/app/phases/certs/certs.go
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright 2016 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 certs
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||
certutil "k8s.io/kubernetes/pkg/util/cert"
|
||||
)
|
||||
|
||||
// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane.
|
||||
// It first generates a self-signed CA certificate, a server certificate (signed by the CA) and a key for
|
||||
// signing service account tokens. It returns CA key and certificate, which is convenient for use with
|
||||
// client config funcs.
|
||||
func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration, pkiPath string) (*x509.Certificate, error) {
|
||||
altNames := certutil.AltNames{}
|
||||
|
||||
// First, define all domains this cert should be signed for
|
||||
internalAPIServerFQDN := []string{
|
||||
"kubernetes",
|
||||
"kubernetes.default",
|
||||
"kubernetes.default.svc",
|
||||
fmt.Sprintf("kubernetes.default.svc.%s", cfg.Networking.DNSDomain),
|
||||
}
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't get the hostname: %v", err)
|
||||
}
|
||||
altNames.DNSNames = append(cfg.API.ExternalDNSNames, hostname)
|
||||
altNames.DNSNames = append(altNames.DNSNames, internalAPIServerFQDN...)
|
||||
|
||||
// then, add all IP addresses we're bound to
|
||||
for _, a := range cfg.API.AdvertiseAddresses {
|
||||
if ip := net.ParseIP(a); ip != nil {
|
||||
altNames.IPs = append(altNames.IPs, ip)
|
||||
} else {
|
||||
return nil, fmt.Errorf("could not parse ip %q", a)
|
||||
}
|
||||
}
|
||||
// and lastly, extract the internal IP address for the API server
|
||||
_, n, err := net.ParseCIDR(cfg.Networking.ServiceSubnet)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing CIDR %q: %v", cfg.Networking.ServiceSubnet, err)
|
||||
}
|
||||
internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(n, 1)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to allocate IP address for the API server from the given CIDR (%q) [%v]", &cfg.Networking.ServiceSubnet, err)
|
||||
}
|
||||
|
||||
altNames.IPs = append(altNames.IPs, internalAPIServerVirtualIP)
|
||||
|
||||
caKey, caCert, err := newCertificateAuthority()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure while creating CA keys and certificate [%v]", err)
|
||||
}
|
||||
|
||||
if err := writeKeysAndCert(pkiPath, "ca", caKey, caCert); err != nil {
|
||||
return nil, fmt.Errorf("failure while saving CA keys and certificate [%v]", err)
|
||||
}
|
||||
fmt.Println("[certificates] Generated Certificate Authority key and certificate.")
|
||||
|
||||
apiKey, apiCert, err := newServerKeyAndCert(caCert, caKey, altNames)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure while creating API server keys and certificate [%v]", err)
|
||||
}
|
||||
|
||||
if err := writeKeysAndCert(pkiPath, "apiserver", apiKey, apiCert); err != nil {
|
||||
return nil, fmt.Errorf("failure while saving API server keys and certificate [%v]", err)
|
||||
}
|
||||
fmt.Println("[certificates] Generated API Server key and certificate")
|
||||
|
||||
// Generate a private key for service accounts
|
||||
saKey, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failure while creating service account signing keys [%v]", err)
|
||||
}
|
||||
if err := writeKeysAndCert(pkiPath, "sa", saKey, nil); err != nil {
|
||||
return nil, fmt.Errorf("failure while saving service account signing keys [%v]", err)
|
||||
}
|
||||
fmt.Println("[certificates] Generated Service Account signing keys")
|
||||
fmt.Printf("[certificates] Created keys and certificates in %q\n", pkiPath)
|
||||
return caCert, nil
|
||||
}
|
77
cmd/kubeadm/app/phases/certs/certs_test.go
Normal file
77
cmd/kubeadm/app/phases/certs/certs_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
Copyright 2016 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 certs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
)
|
||||
|
||||
func TestCreatePKIAssets(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.Remove(tmpdir)
|
||||
|
||||
var tests = []struct {
|
||||
cfg *kubeadmapi.MasterConfiguration
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
cfg: &kubeadmapi.MasterConfiguration{},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
// CIDR too small
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{AdvertiseAddresses: []string{"10.0.0.1"}},
|
||||
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/1"},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
// CIDR invalid
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{AdvertiseAddresses: []string{"10.0.0.1"}},
|
||||
Networking: kubeadmapi.Networking{ServiceSubnet: "invalid"},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadmapi.API{AdvertiseAddresses: []string{"10.0.0.1"}},
|
||||
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/24"},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
_, actual := CreatePKIAssets(rt.cfg, fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir))
|
||||
if (actual == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed CreatePKIAssets with an error:\n\texpected: %t\n\t actual: %t",
|
||||
rt.expected,
|
||||
(actual == nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
41
cmd/kubeadm/app/phases/certs/doc.go
Normal file
41
cmd/kubeadm/app/phases/certs/doc.go
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright 2016 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 certs
|
||||
|
||||
/*
|
||||
|
||||
PHASE: CERTIFICATES
|
||||
|
||||
INPUTS:
|
||||
From MasterConfiguration
|
||||
.API.AdvertiseAddresses is needed for knowing which IPs the certs should be signed for
|
||||
.API.ExternalDNSNames is needed for knowing which DNS names the certs should be signed for
|
||||
.Networking.DNSDomain is needed for knowing which DNS name the internal kubernetes service has
|
||||
.Networking.ServiceSubnet is needed for knowing which IP the internal kubernetes service is going to point to
|
||||
The PKIPath is required for knowing where all certificates should be stored
|
||||
|
||||
OUTPUTS:
|
||||
Files to PKIPath (default /etc/kubernetes/pki):
|
||||
- apiserver-key.pem
|
||||
- apiserver-pub.pem
|
||||
- apiserver.pem
|
||||
- ca-key.pem
|
||||
- ca-pub.pem
|
||||
- ca.pem
|
||||
- sa-key.pem
|
||||
- sa-pub.pem
|
||||
*/
|
110
cmd/kubeadm/app/phases/certs/pki_helpers.go
Normal file
110
cmd/kubeadm/app/phases/certs/pki_helpers.go
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
Copyright 2016 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 certs
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
certutil "k8s.io/kubernetes/pkg/util/cert"
|
||||
)
|
||||
|
||||
func newCertificateAuthority() (*rsa.PrivateKey, *x509.Certificate, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
|
||||
}
|
||||
|
||||
config := certutil.Config{
|
||||
CommonName: "kubernetes",
|
||||
}
|
||||
|
||||
cert, err := certutil.NewSelfSignedCACert(config, key)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to create self-signed certificate [%v]", err)
|
||||
}
|
||||
|
||||
return key, cert, nil
|
||||
}
|
||||
|
||||
func newServerKeyAndCert(caCert *x509.Certificate, caKey *rsa.PrivateKey, altNames certutil.AltNames) (*rsa.PrivateKey, *x509.Certificate, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
|
||||
}
|
||||
|
||||
config := certutil.Config{
|
||||
CommonName: "kube-apiserver",
|
||||
AltNames: altNames,
|
||||
}
|
||||
cert, err := certutil.NewSignedCert(config, key, caCert, caKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err)
|
||||
}
|
||||
|
||||
return key, cert, nil
|
||||
}
|
||||
|
||||
func NewClientKeyAndCert(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*rsa.PrivateKey, *x509.Certificate, error) {
|
||||
key, err := certutil.NewPrivateKey()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to create private key [%v]", err)
|
||||
}
|
||||
|
||||
config := certutil.Config{
|
||||
CommonName: "kubernetes-client",
|
||||
}
|
||||
cert, err := certutil.NewSignedCert(config, key, caCert, caKey)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to sign certificate [%v]", err)
|
||||
}
|
||||
|
||||
return key, cert, nil
|
||||
}
|
||||
|
||||
func writeKeysAndCert(pkiPath string, name string, key *rsa.PrivateKey, cert *x509.Certificate) error {
|
||||
publicKeyPath, privateKeyPath, certificatePath := pathsKeysCerts(pkiPath, name)
|
||||
|
||||
if key != nil {
|
||||
if err := certutil.WriteKey(privateKeyPath, certutil.EncodePrivateKeyPEM(key)); err != nil {
|
||||
return fmt.Errorf("unable to write private key file (%q) [%v]", privateKeyPath, err)
|
||||
}
|
||||
if pubKey, err := certutil.EncodePublicKeyPEM(&key.PublicKey); err == nil {
|
||||
if err := certutil.WriteKey(publicKeyPath, pubKey); err != nil {
|
||||
return fmt.Errorf("unable to write public key file (%q) [%v]", publicKeyPath, err)
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("unable to encode public key to PEM [%v]", err)
|
||||
}
|
||||
}
|
||||
|
||||
if cert != nil {
|
||||
if err := certutil.WriteCert(certificatePath, certutil.EncodeCertPEM(cert)); err != nil {
|
||||
return fmt.Errorf("unable to write certificate file (%q) [%v]", certificatePath, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func pathsKeysCerts(pkiPath, name string) (string, string, string) {
|
||||
return path.Join(pkiPath, fmt.Sprintf("%s-pub.pem", name)),
|
||||
path.Join(pkiPath, fmt.Sprintf("%s-key.pem", name)),
|
||||
path.Join(pkiPath, fmt.Sprintf("%s.pem", name))
|
||||
}
|
@ -14,19 +14,16 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package master
|
||||
package certs
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
certutil "k8s.io/kubernetes/pkg/util/cert"
|
||||
)
|
||||
|
||||
@ -53,38 +50,16 @@ func TestNewCertificateAuthority(t *testing.T) {
|
||||
|
||||
func TestNewServerKeyAndCert(t *testing.T) {
|
||||
var tests = []struct {
|
||||
cfg *kubeadmapi.MasterConfiguration
|
||||
caKeySize int
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
// given CIDR too small
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/1"},
|
||||
},
|
||||
caKeySize: 2048,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
// bad CIDR
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Networking: kubeadm.Networking{ServiceSubnet: "foo"},
|
||||
},
|
||||
caKeySize: 2048,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
// RSA key too small
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/24"},
|
||||
},
|
||||
caKeySize: 128,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/24"},
|
||||
},
|
||||
// Should succeed
|
||||
caKeySize: 2048,
|
||||
expected: true,
|
||||
},
|
||||
@ -97,7 +72,7 @@ func TestNewServerKeyAndCert(t *testing.T) {
|
||||
}
|
||||
caCert := &x509.Certificate{}
|
||||
altNames := certutil.AltNames{}
|
||||
_, _, actual := newServerKeyAndCert(rt.cfg, caCert, caKey, altNames)
|
||||
_, _, actual := newServerKeyAndCert(caCert, caKey, altNames)
|
||||
if (actual == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed newServerKeyAndCert:\n\texpected: %t\n\t actual: %t",
|
||||
@ -130,10 +105,10 @@ func TestNewClientKeyAndCert(t *testing.T) {
|
||||
t.Fatalf("Couldn't create rsa Private Key")
|
||||
}
|
||||
caCert := &x509.Certificate{}
|
||||
_, _, actual := newClientKeyAndCert(caCert, caKey)
|
||||
_, _, actual := NewClientKeyAndCert(caCert, caKey)
|
||||
if (actual == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed newClientKeyAndCert:\n\texpected: %t\n\t actual: %t",
|
||||
"failed NewClientKeyAndCert:\n\texpected: %t\n\t actual: %t",
|
||||
rt.expected,
|
||||
(actual == nil),
|
||||
)
|
||||
@ -194,65 +169,3 @@ func TestPathsKeysCerts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewServiceAccountKey(t *testing.T) {
|
||||
r, err := newServiceAccountKey()
|
||||
if r == nil {
|
||||
t.Errorf(
|
||||
"failed newServiceAccountKey, rsa key == nil",
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf(
|
||||
"failed newServiceAccountKey with an error: %v",
|
||||
err,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreatePKIAssets(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
}
|
||||
defer os.Remove(tmpdir)
|
||||
|
||||
// set up tmp GlobalEnvParams values for testing
|
||||
oldEnv := kubeadmapi.GlobalEnvParams
|
||||
kubeadmapi.GlobalEnvParams.HostPKIPath = fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir)
|
||||
defer func() { kubeadmapi.GlobalEnvParams = oldEnv }()
|
||||
|
||||
var tests = []struct {
|
||||
cfg *kubeadmapi.MasterConfiguration
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
cfg: &kubeadmapi.MasterConfiguration{},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadm.API{AdvertiseAddresses: []string{"10.0.0.1"}},
|
||||
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/1"},
|
||||
},
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
cfg: &kubeadmapi.MasterConfiguration{
|
||||
API: kubeadm.API{AdvertiseAddresses: []string{"10.0.0.1"}},
|
||||
Networking: kubeadm.Networking{ServiceSubnet: "10.0.0.1/24"},
|
||||
},
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
for _, rt := range tests {
|
||||
_, _, actual := CreatePKIAssets(rt.cfg)
|
||||
if (actual == nil) != rt.expected {
|
||||
t.Errorf(
|
||||
"failed CreatePKIAssets with an error:\n\texpected: %t\n\t actual: %t",
|
||||
rt.expected,
|
||||
(actual == nil),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
32
cmd/kubeadm/app/phases/kubeconfig/BUILD
Normal file
32
cmd/kubeadm/app/phases/kubeconfig/BUILD
Normal file
@ -0,0 +1,32 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["kubeconfig_test.go"],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//cmd/kubeadm/app/apis/kubeadm:go_default_library"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"kubeconfig.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/phases/certs:go_default_library",
|
||||
"//pkg/client/unversioned/clientcmd:go_default_library",
|
||||
"//pkg/client/unversioned/clientcmd/api:go_default_library",
|
||||
"//pkg/util/cert:go_default_library",
|
||||
],
|
||||
)
|
33
cmd/kubeadm/app/phases/kubeconfig/doc.go
Normal file
33
cmd/kubeadm/app/phases/kubeconfig/doc.go
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2016 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 kubeconfig
|
||||
|
||||
/*
|
||||
|
||||
PHASE: KUBECONFIG
|
||||
|
||||
INPUTS:
|
||||
From MasterConfiguration
|
||||
(.API.AdvertiseAddresses is currently needed for knowing the) MasterAPIEndpoint is required so the KubeConfig file knows where to find the master
|
||||
The KubernetesDir path is required for knowing where to put the KubeConfig files
|
||||
The PKIPath is required for knowing where all certificates should be stored
|
||||
|
||||
OUTPUTS:
|
||||
Files to KubernetesDir (default /etc/kubernetes):
|
||||
- admin.conf
|
||||
- kubelet.conf
|
||||
*/
|
164
cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go
Normal file
164
cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright 2016 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 kubeconfig
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
certutil "k8s.io/kubernetes/pkg/util/cert"
|
||||
)
|
||||
|
||||
const (
|
||||
KubernetesDirPermissions = 0700
|
||||
AdminKubeConfigFileName = "admin.conf"
|
||||
KubeletKubeConfigFileName = "kubelet.conf"
|
||||
)
|
||||
|
||||
// This function is called from the main init and does the work for the default phase behaviour
|
||||
// TODO: Make an integration test for this function that runs after the certificates phase
|
||||
// and makes sure that those two phases work well together...
|
||||
func CreateAdminAndKubeletKubeConfig(masterEndpoint, pkiDir, outDir string) error {
|
||||
// Parse the certificate from a file
|
||||
caCertPath := path.Join(pkiDir, "ca.pem")
|
||||
caCerts, err := certutil.CertsFromFile(caCertPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't load the CA cert file %s: %v", caCertPath, err)
|
||||
}
|
||||
// We are only putting one certificate in the CA certificate pem file, so it's safe to just use the first one
|
||||
caCert := caCerts[0]
|
||||
|
||||
// Parse the rsa private key from a file
|
||||
caKeyPath := path.Join(pkiDir, "ca-key.pem")
|
||||
priv, err := certutil.PrivateKeyFromFile(caKeyPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't load the CA private key file %s: %v", caKeyPath, err)
|
||||
}
|
||||
var caKey *rsa.PrivateKey
|
||||
switch k := priv.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
caKey = k
|
||||
case *ecdsa.PrivateKey:
|
||||
// TODO: Abstract rsa.PrivateKey away and make certutil.NewSignedCert accept a ecdsa.PrivateKey as well
|
||||
// After that, we can support generating kubeconfig files from ecdsa private keys as well
|
||||
return fmt.Errorf("the CA private key file %s isn't in RSA format", caKeyPath)
|
||||
default:
|
||||
return fmt.Errorf("the CA private key file %s isn't in RSA format", caKeyPath)
|
||||
}
|
||||
|
||||
// User admin should have full access to the cluster
|
||||
if err := createKubeConfigFileForClient(masterEndpoint, "admin", outDir, caCert, caKey); err != nil {
|
||||
return fmt.Errorf("couldn't create a kubeconfig file for admin: %v", err)
|
||||
}
|
||||
|
||||
// TODO: The kubelet should have limited access to the cluster
|
||||
if err := createKubeConfigFileForClient(masterEndpoint, "kubelet", outDir, caCert, caKey); err != nil {
|
||||
return fmt.Errorf("couldn't create a kubeconfig file for kubelet: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createKubeConfigFileForClient(masterEndpoint, client, outDir string, caCert *x509.Certificate, caKey *rsa.PrivateKey) error {
|
||||
key, cert, err := certphase.NewClientKeyAndCert(caCert, caKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failure while creating %s client certificate [%v]", client, err)
|
||||
}
|
||||
|
||||
config := MakeClientConfigWithCerts(
|
||||
masterEndpoint,
|
||||
"kubernetes",
|
||||
client,
|
||||
certutil.EncodeCertPEM(caCert),
|
||||
certutil.EncodePrivateKeyPEM(key),
|
||||
certutil.EncodeCertPEM(cert),
|
||||
)
|
||||
|
||||
// Write it now to a file
|
||||
filepath := path.Join(outDir, fmt.Sprintf("%s.conf", client))
|
||||
return WriteKubeconfigToDisk(filepath, config)
|
||||
}
|
||||
|
||||
func WriteKubeconfigToDisk(filepath string, kubeconfig *clientcmdapi.Config) error {
|
||||
// Make sure the dir exists or can be created
|
||||
if err := os.MkdirAll(path.Dir(filepath), KubernetesDirPermissions); err != nil {
|
||||
return fmt.Errorf("failed to create directory %q [%v]", path.Dir(filepath), err)
|
||||
}
|
||||
|
||||
// If err == nil, the file exists. Oops, we don't allow the file to exist already, fail.
|
||||
if _, err := os.Stat(filepath); err == nil {
|
||||
return fmt.Errorf("kubeconfig file %s already exists, but must not exist.", filepath)
|
||||
}
|
||||
|
||||
if err := clientcmd.WriteToFile(*kubeconfig, filepath); err != nil {
|
||||
return fmt.Errorf("failed to write to %q [%v]", filepath, err)
|
||||
}
|
||||
|
||||
fmt.Printf("[kubeconfig] Wrote KubeConfig file to disk: %q\n", filepath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func createBasicClientConfig(serverURL string, clusterName string, userName string, caCert []byte) *clientcmdapi.Config {
|
||||
config := clientcmdapi.NewConfig()
|
||||
|
||||
// Make a new cluster, specify the endpoint we'd like to talk to and the ca cert we're gonna use
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.Server = serverURL
|
||||
cluster.CertificateAuthorityData = caCert
|
||||
|
||||
// Specify a context where we're using that cluster and the username as the auth information
|
||||
contextName := fmt.Sprintf("%s@%s", userName, clusterName)
|
||||
context := clientcmdapi.NewContext()
|
||||
context.Cluster = clusterName
|
||||
context.AuthInfo = userName
|
||||
|
||||
// Lastly, apply the created objects above to the config
|
||||
config.Clusters[clusterName] = cluster
|
||||
config.Contexts[contextName] = context
|
||||
config.CurrentContext = contextName
|
||||
return config
|
||||
}
|
||||
|
||||
// Creates a clientcmdapi.Config object with access to the API server with client certificates
|
||||
func MakeClientConfigWithCerts(serverURL, clusterName, userName string, caCert []byte, clientKey []byte, clientCert []byte) *clientcmdapi.Config {
|
||||
config := createBasicClientConfig(serverURL, clusterName, userName, caCert)
|
||||
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.ClientKeyData = clientKey
|
||||
authInfo.ClientCertificateData = clientCert
|
||||
|
||||
config.AuthInfos[userName] = authInfo
|
||||
return config
|
||||
}
|
||||
|
||||
// Creates a clientcmdapi.Config object with access to the API server with a token
|
||||
func MakeClientConfigWithToken(serverURL, clusterName, userName string, caCert []byte, token string) *clientcmdapi.Config {
|
||||
config := createBasicClientConfig(serverURL, clusterName, userName, caCert)
|
||||
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.Token = token
|
||||
|
||||
config.AuthInfos[userName] = authInfo
|
||||
return config
|
||||
}
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package util
|
||||
package kubeconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@ -25,7 +25,6 @@ import (
|
||||
"testing"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -33,65 +32,54 @@ const (
|
||||
clusters:
|
||||
- cluster:
|
||||
server: ""
|
||||
name: ""
|
||||
contexts: []
|
||||
current-context: ""
|
||||
name: k8s
|
||||
contexts:
|
||||
- context:
|
||||
cluster: k8s
|
||||
user: user1
|
||||
name: user1@k8s
|
||||
current-context: user1@k8s
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
users:
|
||||
- name: user1
|
||||
user:
|
||||
token: abc
|
||||
`
|
||||
configOut2 = `apiVersion: v1
|
||||
clusters:
|
||||
- cluster:
|
||||
server: ""
|
||||
server: localhost:8080
|
||||
name: kubernetes
|
||||
contexts: []
|
||||
current-context: ""
|
||||
contexts:
|
||||
- context:
|
||||
cluster: kubernetes
|
||||
user: user2
|
||||
name: user2@kubernetes
|
||||
current-context: user2@kubernetes
|
||||
kind: Config
|
||||
preferences: {}
|
||||
users: []
|
||||
users:
|
||||
- name: user2
|
||||
user:
|
||||
token: cba
|
||||
`
|
||||
)
|
||||
|
||||
type configClient struct {
|
||||
c string
|
||||
s string
|
||||
ca []byte
|
||||
clusterName string
|
||||
userName string
|
||||
serverURL string
|
||||
caCert []byte
|
||||
}
|
||||
|
||||
type configClientWithCerts struct {
|
||||
c *clientcmdapi.Config
|
||||
clusterName string
|
||||
userName string
|
||||
clientKey []byte
|
||||
clientCert []byte
|
||||
clientKey []byte
|
||||
clientCert []byte
|
||||
}
|
||||
|
||||
type configClientWithToken struct {
|
||||
c *clientcmdapi.Config
|
||||
clusterName string
|
||||
userName string
|
||||
token string
|
||||
}
|
||||
|
||||
func TestCreateBasicClientConfig(t *testing.T) {
|
||||
var createBasicTest = []struct {
|
||||
cc configClient
|
||||
expected string
|
||||
}{
|
||||
{configClient{}, ""},
|
||||
{configClient{c: "kubernetes"}, ""},
|
||||
}
|
||||
for _, rt := range createBasicTest {
|
||||
c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca)
|
||||
if c.Kind != rt.expected {
|
||||
t.Errorf(
|
||||
"failed CreateBasicClientConfig:\n\texpected: %s\n\t actual: %s",
|
||||
c.Kind,
|
||||
rt.expected,
|
||||
)
|
||||
}
|
||||
}
|
||||
token string
|
||||
}
|
||||
|
||||
func TestMakeClientConfigWithCerts(t *testing.T) {
|
||||
@ -101,23 +89,22 @@ func TestMakeClientConfigWithCerts(t *testing.T) {
|
||||
expected string
|
||||
}{
|
||||
{configClient{}, configClientWithCerts{}, ""},
|
||||
{configClient{c: "kubernetes"}, configClientWithCerts{}, ""},
|
||||
{configClient{clusterName: "kubernetes"}, configClientWithCerts{}, ""},
|
||||
}
|
||||
for _, rt := range createBasicTest {
|
||||
c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca)
|
||||
rt.ccWithCerts.c = c
|
||||
cwc := MakeClientConfigWithCerts(
|
||||
rt.ccWithCerts.c,
|
||||
rt.ccWithCerts.clusterName,
|
||||
rt.ccWithCerts.userName,
|
||||
rt.cc.serverURL,
|
||||
rt.cc.clusterName,
|
||||
rt.cc.userName,
|
||||
rt.cc.caCert,
|
||||
rt.ccWithCerts.clientKey,
|
||||
rt.ccWithCerts.clientCert,
|
||||
)
|
||||
if cwc.Kind != rt.expected {
|
||||
t.Errorf(
|
||||
"failed MakeClientConfigWithCerts:\n\texpected: %s\n\t actual: %s",
|
||||
c.Kind,
|
||||
rt.expected,
|
||||
cwc.Kind,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -130,28 +117,27 @@ func TestMakeClientConfigWithToken(t *testing.T) {
|
||||
expected string
|
||||
}{
|
||||
{configClient{}, configClientWithToken{}, ""},
|
||||
{configClient{c: "kubernetes"}, configClientWithToken{}, ""},
|
||||
{configClient{clusterName: "kubernetes"}, configClientWithToken{}, ""},
|
||||
}
|
||||
for _, rt := range createBasicTest {
|
||||
c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca)
|
||||
rt.ccWithToken.c = c
|
||||
cwc := MakeClientConfigWithToken(
|
||||
rt.ccWithToken.c,
|
||||
rt.ccWithToken.clusterName,
|
||||
rt.ccWithToken.userName,
|
||||
rt.cc.serverURL,
|
||||
rt.cc.clusterName,
|
||||
rt.cc.userName,
|
||||
rt.cc.caCert,
|
||||
rt.ccWithToken.token,
|
||||
)
|
||||
if cwc.Kind != rt.expected {
|
||||
t.Errorf(
|
||||
"failed MakeClientConfigWithCerts:\n\texpected: %s\n\t actual: %s",
|
||||
c.Kind,
|
||||
rt.expected,
|
||||
cwc.Kind,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteKubeconfigIfNotExists(t *testing.T) {
|
||||
func TestWriteKubeconfigToDisk(t *testing.T) {
|
||||
tmpdir, err := ioutil.TempDir("", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Couldn't create tmpdir")
|
||||
@ -162,37 +148,41 @@ func TestWriteKubeconfigIfNotExists(t *testing.T) {
|
||||
oldEnv := kubeadmapi.GlobalEnvParams
|
||||
kubeadmapi.GlobalEnvParams = kubeadmapi.SetEnvParams()
|
||||
kubeadmapi.GlobalEnvParams.KubernetesDir = fmt.Sprintf("%s/etc/kubernetes", tmpdir)
|
||||
kubeadmapi.GlobalEnvParams.HostPKIPath = fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir)
|
||||
kubeadmapi.GlobalEnvParams.HostEtcdPath = fmt.Sprintf("%s/var/lib/etcd", tmpdir)
|
||||
kubeadmapi.GlobalEnvParams.DiscoveryImage = fmt.Sprintf("%s/var/lib/etcd", tmpdir)
|
||||
defer func() { kubeadmapi.GlobalEnvParams = oldEnv }()
|
||||
|
||||
var writeConfig = []struct {
|
||||
name string
|
||||
cc configClient
|
||||
expected error
|
||||
file []byte
|
||||
name string
|
||||
cc configClient
|
||||
ccWithToken configClientWithToken
|
||||
expected error
|
||||
file []byte
|
||||
}{
|
||||
{"test1", configClient{}, nil, []byte(configOut1)},
|
||||
{"test2", configClient{c: "kubernetes"}, nil, []byte(configOut2)},
|
||||
{"test1", configClient{clusterName: "k8s", userName: "user1"}, configClientWithToken{token: "abc"}, nil, []byte(configOut1)},
|
||||
{"test2", configClient{clusterName: "kubernetes", userName: "user2", serverURL: "localhost:8080"}, configClientWithToken{token: "cba"}, nil, []byte(configOut2)},
|
||||
}
|
||||
for _, rt := range writeConfig {
|
||||
c := CreateBasicClientConfig(rt.cc.c, rt.cc.s, rt.cc.ca)
|
||||
err := WriteKubeconfigIfNotExists(rt.name, c)
|
||||
c := MakeClientConfigWithToken(
|
||||
rt.cc.serverURL,
|
||||
rt.cc.clusterName,
|
||||
rt.cc.userName,
|
||||
rt.cc.caCert,
|
||||
rt.ccWithToken.token,
|
||||
)
|
||||
configPath := filepath.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, fmt.Sprintf("%s.conf", rt.name))
|
||||
err := WriteKubeconfigToDisk(configPath, c)
|
||||
if err != rt.expected {
|
||||
t.Errorf(
|
||||
"failed WriteKubeconfigIfNotExists with an error:\n\texpected: %s\n\t actual: %s",
|
||||
err,
|
||||
"failed WriteKubeconfigToDisk with an error:\n\texpected: %s\n\t actual: %s",
|
||||
rt.expected,
|
||||
err,
|
||||
)
|
||||
}
|
||||
configPath := filepath.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, fmt.Sprintf("%s.conf", rt.name))
|
||||
newFile, err := ioutil.ReadFile(configPath)
|
||||
if !bytes.Equal(newFile, rt.file) {
|
||||
t.Errorf(
|
||||
"failed WriteKubeconfigIfNotExists config write:\n\texpected: %s\n\t actual: %s",
|
||||
newFile,
|
||||
"failed WriteKubeconfigToDisk config write:\n\texpected: %s\n\t actual: %s",
|
||||
rt.file,
|
||||
newFile,
|
||||
)
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@ go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"error.go",
|
||||
"kubeconfig.go",
|
||||
"tokens.go",
|
||||
"version.go",
|
||||
],
|
||||
@ -26,8 +25,6 @@ go_library(
|
||||
"//pkg/api/v1:go_default_library",
|
||||
"//pkg/apis/meta/v1:go_default_library",
|
||||
"//pkg/client/clientset_generated/clientset:go_default_library",
|
||||
"//pkg/client/unversioned/clientcmd:go_default_library",
|
||||
"//pkg/client/unversioned/clientcmd/api:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -35,7 +32,6 @@ go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"error_test.go",
|
||||
"kubeconfig_test.go",
|
||||
"tokens_test.go",
|
||||
"version_test.go",
|
||||
],
|
||||
@ -44,6 +40,5 @@ go_test(
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||
"//cmd/kubeadm/app/preflight:go_default_library",
|
||||
"//pkg/client/unversioned/clientcmd/api:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@ -1,96 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
|
||||
clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
|
||||
)
|
||||
|
||||
func CreateBasicClientConfig(clusterName string, serverURL string, caCert []byte) *clientcmdapi.Config {
|
||||
cluster := clientcmdapi.NewCluster()
|
||||
cluster.Server = serverURL
|
||||
cluster.CertificateAuthorityData = caCert
|
||||
|
||||
config := clientcmdapi.NewConfig()
|
||||
config.Clusters[clusterName] = cluster
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func MakeClientConfigWithCerts(config *clientcmdapi.Config, clusterName string, userName string, clientKey []byte, clientCert []byte) *clientcmdapi.Config {
|
||||
newConfig := config
|
||||
name := fmt.Sprintf("%s@%s", userName, clusterName)
|
||||
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.ClientKeyData = clientKey
|
||||
authInfo.ClientCertificateData = clientCert
|
||||
|
||||
context := clientcmdapi.NewContext()
|
||||
context.Cluster = clusterName
|
||||
context.AuthInfo = userName
|
||||
|
||||
newConfig.AuthInfos[userName] = authInfo
|
||||
newConfig.Contexts[name] = context
|
||||
newConfig.CurrentContext = name
|
||||
|
||||
return newConfig
|
||||
}
|
||||
|
||||
func MakeClientConfigWithToken(config *clientcmdapi.Config, clusterName string, userName string, token string) *clientcmdapi.Config {
|
||||
newConfig := config
|
||||
name := fmt.Sprintf("%s@%s", userName, clusterName)
|
||||
|
||||
authInfo := clientcmdapi.NewAuthInfo()
|
||||
authInfo.Token = token
|
||||
|
||||
context := clientcmdapi.NewContext()
|
||||
context.Cluster = clusterName
|
||||
context.AuthInfo = userName
|
||||
|
||||
newConfig.AuthInfos[userName] = authInfo
|
||||
newConfig.Contexts[name] = context
|
||||
newConfig.CurrentContext = name
|
||||
|
||||
return newConfig
|
||||
}
|
||||
|
||||
func WriteKubeconfigIfNotExists(name string, kubeconfig *clientcmdapi.Config) error {
|
||||
if err := os.MkdirAll(kubeadmapi.GlobalEnvParams.KubernetesDir, 0700); err != nil {
|
||||
return fmt.Errorf("failed to create directory %q [%v]", kubeadmapi.GlobalEnvParams.KubernetesDir, err)
|
||||
}
|
||||
|
||||
filename := path.Join(kubeadmapi.GlobalEnvParams.KubernetesDir, fmt.Sprintf("%s.conf", name))
|
||||
// Create and open the file, only if it does not already exist.
|
||||
f, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create %q, it already exists [%v]", filename, err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
if err := clientcmd.WriteToFile(*kubeconfig, filename); err != nil {
|
||||
return fmt.Errorf("failed to write to %q [%v]", filename, err)
|
||||
}
|
||||
|
||||
fmt.Printf("[kubeconfig] Wrote KubeConfig file to disk: %q\n", filename)
|
||||
return nil
|
||||
}
|
@ -13,7 +13,7 @@ go_library(
|
||||
srcs = ["init.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//cmd/kubeadm/app/util:go_default_library",
|
||||
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
|
||||
"//federation/pkg/kubefed/util:go_default_library",
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/api/resource:go_default_library",
|
||||
|
@ -36,7 +36,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
kubeadmkubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
||||
"k8s.io/kubernetes/federation/pkg/kubefed/util"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/resource"
|
||||
@ -360,16 +360,11 @@ func createAPIServerCredentialsSecret(clientset *client.Clientset, namespace, cr
|
||||
}
|
||||
|
||||
func createControllerManagerKubeconfigSecret(clientset *client.Clientset, namespace, name, svcName, kubeconfigName string, entKeyPairs *entityKeyPairs, dryRun bool) (*api.Secret, error) {
|
||||
basicClientConfig := kubeadmutil.CreateBasicClientConfig(
|
||||
name,
|
||||
config := kubeadmkubeconfigphase.MakeClientConfigWithCerts(
|
||||
fmt.Sprintf("https://%s", svcName),
|
||||
certutil.EncodeCertPEM(entKeyPairs.ca.Cert),
|
||||
)
|
||||
|
||||
config := kubeadmutil.MakeClientConfigWithCerts(
|
||||
basicClientConfig,
|
||||
name,
|
||||
"federation-controller-manager",
|
||||
certutil.EncodeCertPEM(entKeyPairs.ca.Cert),
|
||||
certutil.EncodePrivateKeyPEM(entKeyPairs.controllerManager.Key),
|
||||
certutil.EncodeCertPEM(entKeyPairs.controllerManager.Cert),
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user