Commit e11020fa authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #48196 from fabriziopandini/kubeadm-phase-certs2

Automatic merge from submit-queue kubeadm: Implementing the certificates phase fully **What this PR does / why we need it:** This contains implementation of certs phases in kubeadm, which is part of the wider effort of implementing phases in kubeadm, previously in alpha stage. The original proposal for this activity can be found [here](https://github.com/kubernetes/kubeadm/pull/156/files) and related comments. Kubeadm phase implementation checklist is defined [here](https://github.com/kubernetes/kubeadm/issues/267) Common implementation guidelines and principles for all phases are defined [here](https://docs.google.com/document/d/1VQMyFIVMfRGQPP3oCUpfjiWtOr3pLxp4g7cP-hXQFXc/edit?usp=sharing) This PR implements: - [x] kubeadm phase certs - [x] kubeadm phase certs all - [x] kubeadm phase certs ca - [x] kubeadm phase certs apiserver - [x] kubeadm phase certs apiserver-kubelet-client - [x] kubeadm phase certs sa - [x] kubeadm phase certs front-proxy-ca - [x] kubeadm phase certs front-proxy-client **Which issue this PR fixes:** none **Special notes for your reviewer:** This PR resubmits the work of #45617, and already includes @luxas reviews. Please note that: - the API - phase\certs.go - is now totally free by any UX concerns, and implements only the core logic for cert generation. - the UX - cmd\phase\certs.go - now takes charge of UX commands and kubeadm own's rules for placing certs in the certificate dir (e.g. create only if exists)
parents e2f6981a c2e9052a
......@@ -13,7 +13,6 @@ go_library(
srcs = [
"cmd.go",
"completion.go",
"defaults.go",
"init.go",
"join.go",
"reset.go",
......@@ -31,13 +30,13 @@ go_library(
"//cmd/kubeadm/app/node:go_default_library",
"//cmd/kubeadm/app/phases/addons:go_default_library",
"//cmd/kubeadm/app/phases/apiconfig:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/controlplane:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/phases/selfhosting:go_default_library",
"//cmd/kubeadm/app/phases/token:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/config:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//cmd/kubeadm/app/util/token:go_default_library",
"//pkg/api:go_default_library",
......@@ -55,7 +54,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
......@@ -66,7 +64,6 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"defaults_test.go",
"reset_test.go",
"token_test.go",
],
......
......@@ -31,16 +31,17 @@ import (
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
cmdphases "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
addonsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons"
apiconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/apiconfig"
certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
selfhostingphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/selfhosting"
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/token"
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/version"
)
......@@ -164,11 +165,20 @@ func NewInit(cfgPath string, cfg *kubeadmapi.MasterConfiguration, skipPreFlight,
}
// Set defaults dynamically that the API group defaulting can't (by fetching information from the internet, looking up network interfaces, etc.)
err := setInitDynamicDefaults(cfg)
err := configutil.SetInitDynamicDefaults(cfg)
if err != nil {
return nil, err
}
fmt.Printf("[init] Using Kubernetes version: %s\n", cfg.KubernetesVersion)
fmt.Printf("[init] Using Authorization mode: %v\n", cfg.AuthorizationModes)
// Warn about the limitations with the current cloudprovider solution.
if cfg.CloudProvider != "" {
fmt.Println("[init] WARNING: For cloudprovider integrations to work --cloud-provider must be set for all kubelets in the cluster.")
fmt.Println("\t(/etc/systemd/system/kubelet.service.d/10-kubeadm.conf should be edited for this purpose)")
}
if !skipPreFlight {
fmt.Println("[preflight] Running pre-flight checks")
......@@ -202,7 +212,7 @@ func (i *Init) Validate(cmd *cobra.Command) error {
func (i *Init) Run(out io.Writer) error {
// PHASE 1: Generate certificates
err := certphase.CreatePKIAssets(i.cfg)
err := cmdphases.CreatePKIAssets(i.cfg)
if err != nil {
return err
}
......
......@@ -5,6 +5,7 @@ licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
......@@ -21,16 +22,31 @@ 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/constants:go_default_library",
"//cmd/kubeadm/app/phases/certs:go_default_library",
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
"//cmd/kubeadm/app/phases/selfhosting:go_default_library",
"//cmd/kubeadm/app/preflight:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/config:go_default_library",
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//pkg/api:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["certs_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm/install:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
"//vendor/github.com/renstrom/dedent:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
],
)
......
/*
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"
"html/template"
"io/ioutil"
"os"
"path"
"testing"
"github.com/renstrom/dedent"
"github.com/spf13/cobra"
// required for triggering api machinery startup when running unit tests
_ "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/install"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil"
)
func TestSubCmdCertsCreateFiles(t *testing.T) {
subCmds := newSubCmdCerts()
var tests = []struct {
subCmds []string
expectedFiles []string
}{
{
subCmds: []string{"all"},
expectedFiles: []string{
kubeadmconstants.CACertName, kubeadmconstants.CAKeyName,
kubeadmconstants.APIServerCertName, kubeadmconstants.APIServerKeyName,
kubeadmconstants.APIServerKubeletClientCertName, kubeadmconstants.APIServerKubeletClientKeyName,
kubeadmconstants.ServiceAccountPrivateKeyName, kubeadmconstants.ServiceAccountPublicKeyName,
kubeadmconstants.FrontProxyCACertName, kubeadmconstants.FrontProxyCAKeyName,
kubeadmconstants.FrontProxyClientCertName, kubeadmconstants.FrontProxyClientKeyName,
},
},
{
subCmds: []string{"ca"},
expectedFiles: []string{kubeadmconstants.CACertName, kubeadmconstants.CAKeyName},
},
{
subCmds: []string{"ca", "apiserver"},
expectedFiles: []string{kubeadmconstants.CACertName, kubeadmconstants.CAKeyName, kubeadmconstants.APIServerCertName, kubeadmconstants.APIServerKeyName},
},
{
subCmds: []string{"ca", "apiserver-kubelet-client"},
expectedFiles: []string{kubeadmconstants.CACertName, kubeadmconstants.CAKeyName, kubeadmconstants.APIServerKubeletClientCertName, kubeadmconstants.APIServerKubeletClientKeyName},
},
{
subCmds: []string{"sa"},
expectedFiles: []string{kubeadmconstants.ServiceAccountPrivateKeyName, kubeadmconstants.ServiceAccountPublicKeyName},
},
{
subCmds: []string{"front-proxy-ca"},
expectedFiles: []string{kubeadmconstants.FrontProxyCACertName, kubeadmconstants.FrontProxyCAKeyName},
},
{
subCmds: []string{"front-proxy-ca", "front-proxy-client"},
expectedFiles: []string{kubeadmconstants.FrontProxyCACertName, kubeadmconstants.FrontProxyCAKeyName, kubeadmconstants.FrontProxyClientCertName, kubeadmconstants.FrontProxyClientKeyName},
},
}
for _, test := range tests {
// Temporary folder for the test case
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.RemoveAll(tmpdir)
// executes given sub commands
for _, subCmdName := range test.subCmds {
subCmd := getSubCmd(t, subCmdName, subCmds)
subCmd.SetArgs([]string{fmt.Sprintf("--cert-dir=%s", tmpdir)})
if err := subCmd.Execute(); err != nil {
t.Fatalf("Could not execute subcommand: %s", subCmdName)
}
}
// verify expected files are there
assertFilesCount(t, tmpdir, len(test.expectedFiles))
for _, file := range test.expectedFiles {
assertFileExists(t, tmpdir, file)
}
}
}
func TestSubCmdApiServerFlags(t *testing.T) {
subCmds := newSubCmdCerts()
// Temporary folder for the test case
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.RemoveAll(tmpdir)
// creates ca cert
subCmd := getSubCmd(t, "ca", subCmds)
subCmd.SetArgs([]string{fmt.Sprintf("--cert-dir=%s", tmpdir)})
if err := subCmd.Execute(); err != nil {
t.Fatalf("Could not execute subcommand ca")
}
// creates apiserver cert
subCmd = getSubCmd(t, "apiserver", subCmds)
subCmd.SetArgs([]string{
fmt.Sprintf("--cert-dir=%s", tmpdir),
"--apiserver-cert-extra-sans=foo,boo",
"--service-cidr=10.0.0.0/24",
"--service-dns-domain=mycluster.local",
"--apiserver-advertise-address=1.2.3.4",
})
if err := subCmd.Execute(); err != nil {
t.Fatalf("Could not execute subcommand apiserver")
}
APIserverCert, err := pkiutil.TryLoadCertFromDisk(tmpdir, kubeadmconstants.APIServerCertAndKeyBaseName)
if err != nil {
t.Fatalf("Error loading API server certificate: %v", err)
}
hostname, err := os.Hostname()
if err != nil {
t.Errorf("couldn't get the hostname: %v", err)
}
for i, name := range []string{hostname, "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.mycluster.local"} {
if APIserverCert.DNSNames[i] != name {
t.Errorf("APIserverCert.DNSNames[%d] is %s instead of %s", i, APIserverCert.DNSNames[i], name)
}
}
for i, ip := range []string{"10.0.0.1", "1.2.3.4"} {
if APIserverCert.IPAddresses[i].String() != ip {
t.Errorf("APIserverCert.IPAddresses[%d] is %s instead of %s", i, APIserverCert.IPAddresses[i], ip)
}
}
}
func TestSubCmdReadsConfig(t *testing.T) {
subCmds := newSubCmdCerts()
var tests = []struct {
subCmds []string
expectedFileCount int
}{
{
subCmds: []string{"sa"},
expectedFileCount: 2,
},
{
subCmds: []string{"front-proxy-ca", "front-proxy-client"},
expectedFileCount: 4,
},
{
subCmds: []string{"ca", "apiserver", "apiserver-kubelet-client"},
expectedFileCount: 6,
},
{
subCmds: []string{"all"},
expectedFileCount: 12,
},
}
for _, test := range tests {
// Temporary folder for the test case
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.RemoveAll(tmpdir)
configPath := saveDummyCfg(t, tmpdir)
// executes given sub commands
for _, subCmdName := range test.subCmds {
subCmd := getSubCmd(t, subCmdName, subCmds)
subCmd.SetArgs([]string{fmt.Sprintf("--config=%s", configPath)})
if err := subCmd.Execute(); err != nil {
t.Fatalf("Could not execute command: %s", subCmdName)
}
}
// verify expected files are there
// NB. test.expectedFileCount + 1 because in this test case the tempdir where key/certificates
// are saved contains also the dummy configuration file
assertFilesCount(t, tmpdir, test.expectedFileCount+1)
}
}
func getSubCmd(t *testing.T, name string, subCmds []*cobra.Command) *cobra.Command {
for _, subCmd := range subCmds {
if subCmd.Name() == name {
return subCmd
}
}
t.Fatalf("Unable to find sub command %s", name)
return nil
}
func assertFilesCount(t *testing.T, dirName string, count int) {
files, err := ioutil.ReadDir(dirName)
if err != nil {
t.Fatalf("Couldn't read files from tmpdir: %s", err)
}
if len(files) != count {
t.Errorf("dir does contains %d, %d expected", len(files), count)
for _, f := range files {
t.Error(f.Name())
}
}
}
func assertFileExists(t *testing.T, dirName string, fileName string) {
path := path.Join(dirName, fileName)
if _, err := os.Stat(path); os.IsNotExist(err) {
t.Errorf("file %s does not exist", fileName)
}
}
func saveDummyCfg(t *testing.T, dirName string) string {
path := path.Join(dirName, "dummyconfig.yaml")
cfgTemplate := template.Must(template.New("init").Parse(dedent.Dedent(`
apiVersion: kubeadm.k8s.io/v1alpha1
kind: MasterConfiguration
certificatesDir: {{.CertificatesDir}}
`)))
f, err := os.Create(path)
if err != nil {
t.Errorf("error creating dummyconfig file %s: %v", path, err)
}
templateData := struct {
CertificatesDir string
}{
CertificatesDir: dirName,
}
err = cfgTemplate.Execute(f, templateData)
if err != nil {
t.Errorf("error generating dummyconfig file %s: %v", path, err)
}
f.Close()
return path
}
......@@ -37,10 +37,12 @@ const (
APIServerCertAndKeyBaseName = "apiserver"
APIServerCertName = "apiserver.crt"
APIServerKeyName = "apiserver.key"
APIServerCertCommonName = "kube-apiserver" //used as subject.commonname attribute (CN)
APIServerKubeletClientCertAndKeyBaseName = "apiserver-kubelet-client"
APIServerKubeletClientCertName = "apiserver-kubelet-client.crt"
APIServerKubeletClientKeyName = "apiserver-kubelet-client.key"
APIServerKubeletClientCertCommonName = "kube-apiserver-kubelet-client" //used as subject.commonname attribute (CN)
ServiceAccountKeyBaseName = "sa"
ServiceAccountPublicKeyName = "sa.pub"
......@@ -53,6 +55,7 @@ const (
FrontProxyClientCertAndKeyBaseName = "front-proxy-client"
FrontProxyClientCertName = "front-proxy-client.crt"
FrontProxyClientKeyName = "front-proxy-client.key"
FrontProxyClientCertCommonName = "front-proxy-client" //used as subject.commonname attribute (CN)
AdminKubeConfigFileName = "admin.conf"
KubeletKubeConfigFileName = "kubelet.conf"
......
......@@ -15,7 +15,7 @@ go_test(
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//vendor/k8s.io/client-go/util/cert:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
],
)
......@@ -31,7 +31,6 @@ go_library(
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
"//pkg/registry/core/service/ipallocator:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
"//vendor/k8s.io/client-go/util/cert:go_default_library",
],
......
......@@ -17,158 +17,151 @@ limitations under the License.
package certs
import (
"fmt"
"io/ioutil"
"crypto/x509"
"net"
"os"
"testing"
certutil "k8s.io/client-go/util/cert"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
)
func TestCreatePKIAssets(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
func TestNewCACertAndKey(t *testing.T) {
caCert, _, err := NewCACertAndKey()
if err != nil {
t.Fatalf("Couldn't create tmpdir")
}
defer os.RemoveAll(tmpdir)
var tests = []struct {
cfg *kubeadmapi.MasterConfiguration
expected bool
}{
{
cfg: &kubeadmapi.MasterConfiguration{},
expected: false,
},
{
// CIDR too small
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"},
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/1"},
CertificatesDir: fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir),
},
expected: false,
},
{
// CIDR invalid
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"},
Networking: kubeadmapi.Networking{ServiceSubnet: "invalid"},
CertificatesDir: fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir),
},
expected: false,
},
{
cfg: &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{AdvertiseAddress: "1.2.3.4"},
Networking: kubeadmapi.Networking{ServiceSubnet: "10.0.0.1/24"},
CertificatesDir: fmt.Sprintf("%s/etc/kubernetes/pki", tmpdir),
},
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),
)
t.Fatalf("failed call NewCACertAndKey: %v", err)
}
assertIsCa(t, caCert)
}
func TestNewAPIServerCertAndKey(t *testing.T) {
hostname, err := os.Hostname()
if err != nil {
t.Errorf("couldn't get the hostname: %v", err)
}
advertiseIP := "1.2.3.4"
cfg := &kubeadmapi.MasterConfiguration{
API: kubeadmapi.API{AdvertiseAddress: advertiseIP},
Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"},
}
caCert, caKey, err := NewCACertAndKey()
apiServerCert, _, err := NewAPIServerCertAndKey(cfg, caCert, caKey)
if err != nil {
t.Fatalf("failed creation of cert and key: %v", err)
}
assertIsSignedByCa(t, apiServerCert, caCert)
assertHasServerAuth(t, apiServerCert)
for _, DNSName := range []string{hostname, "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.local"} {
assertHasDNSNames(t, apiServerCert, DNSName)
}
for _, IPAddress := range []string{"10.96.0.1", advertiseIP} {
assertHasIPAddresses(t, apiServerCert, net.ParseIP(IPAddress))
}
}
func TestNewAPIServerKubeletClientCertAndKey(t *testing.T) {
caCert, caKey, err := NewCACertAndKey()
apiClientCert, _, err := NewAPIServerKubeletClientCertAndKey(caCert, caKey)
if err != nil {
t.Fatalf("failed creation of cert and key: %v", err)
}
assertIsSignedByCa(t, apiClientCert, caCert)
assertHasClientAuth(t, apiClientCert)
assertHasOrganization(t, apiClientCert, constants.MastersGroup)
}
func TestNewNewServiceAccountSigningKey(t *testing.T) {
key, err := NewServiceAccountSigningKey()
if err != nil {
t.Fatalf("failed creation of key: %v", err)
}
if key.N.BitLen() < 2048 {
t.Error("Service account signing key has less than 2048 bits size")
}
}
func TestNewFrontProxyCACertAndKey(t *testing.T) {
frontProxyCACert, _, err := NewFrontProxyCACertAndKey()
if err != nil {
t.Fatalf("failed creation of cert and key: %v", err)
}
assertIsCa(t, frontProxyCACert)
}
func TestNewFrontProxyClientCertAndKey(t *testing.T) {
frontProxyCACert, frontProxyCAKey, err := NewFrontProxyCACertAndKey()
frontProxyClientCert, _, err := NewFrontProxyClientCertAndKey(frontProxyCACert, frontProxyCAKey)
if err != nil {
t.Fatalf("failed creation of cert and key: %v", err)
}
assertIsSignedByCa(t, frontProxyClientCert, frontProxyCACert)
assertHasClientAuth(t, frontProxyClientCert)
}
func assertIsCa(t *testing.T, cert *x509.Certificate) {
if !cert.IsCA {
t.Error("cert is not a valida CA")
}
}
func assertIsSignedByCa(t *testing.T, cert *x509.Certificate, ca *x509.Certificate) {
if err := cert.CheckSignatureFrom(ca); err != nil {
t.Error("cert is not signed by ca")
}
}
func assertHasClientAuth(t *testing.T, cert *x509.Certificate) {
for i := range cert.ExtKeyUsage {
if cert.ExtKeyUsage[i] == x509.ExtKeyUsageClientAuth {
return
}
}
t.Error("cert is not a ClientAuth")
}
func TestCheckAltNamesExist(t *testing.T) {
var tests = []struct {
IPs []net.IP
DNSNames []string
requiredAltNames certutil.AltNames
succeed bool
}{
{
// equal
requiredAltNames: certutil.AltNames{IPs: []net.IP{net.ParseIP("1.1.1.1"), net.ParseIP("192.168.1.2")}, DNSNames: []string{"foo", "bar", "baz"}},
IPs: []net.IP{net.ParseIP("1.1.1.1"), net.ParseIP("192.168.1.2")},
DNSNames: []string{"foo", "bar", "baz"},
succeed: true,
},
{
// the loaded cert has more ips than required, ok
requiredAltNames: certutil.AltNames{IPs: []net.IP{net.ParseIP("1.1.1.1"), net.ParseIP("192.168.1.2")}, DNSNames: []string{"foo", "bar", "baz"}},
IPs: []net.IP{net.ParseIP("192.168.2.5"), net.ParseIP("1.1.1.1"), net.ParseIP("192.168.1.2")},
DNSNames: []string{"a", "foo", "b", "bar", "baz"},
succeed: true,
},
{
// the loaded cert doesn't have all ips
requiredAltNames: certutil.AltNames{IPs: []net.IP{net.ParseIP("1.1.1.1"), net.ParseIP("192.168.2.5"), net.ParseIP("192.168.1.2")}, DNSNames: []string{"foo", "bar", "baz"}},
IPs: []net.IP{net.ParseIP("1.1.1.1"), net.ParseIP("192.168.1.2")},
DNSNames: []string{"foo", "bar", "baz"},
succeed: false,
},
{
// the loaded cert doesn't have all ips
requiredAltNames: certutil.AltNames{IPs: []net.IP{net.ParseIP("1.1.1.1"), net.ParseIP("192.168.1.2")}, DNSNames: []string{"foo", "bar", "b", "baz"}},
IPs: []net.IP{net.ParseIP("1.1.1.1"), net.ParseIP("192.168.1.2")},
DNSNames: []string{"foo", "bar", "baz"},
succeed: false,
},
}
for _, rt := range tests {
succeeded := checkAltNamesExist(rt.IPs, rt.DNSNames, rt.requiredAltNames)
if succeeded != rt.succeed {
t.Errorf(
"failed checkAltNamesExist:\n\texpected: %t\n\t actual: %t",
rt.succeed,
succeeded,
)
func assertHasServerAuth(t *testing.T, cert *x509.Certificate) {
for i := range cert.ExtKeyUsage {
if cert.ExtKeyUsage[i] == x509.ExtKeyUsageServerAuth {
return
}
}
t.Error("cert is not a ServerAuth")
}
func TestGetAltNames(t *testing.T) {
var tests = []struct {
cfgaltnames []string
hostname string
dnsdomain string
servicecidr string
expectedIPs []string
expectedDNSNames []string
}{
{
cfgaltnames: []string{"foo", "192.168.200.1", "bar.baz"},
hostname: "my-node",
dnsdomain: "cluster.external",
servicecidr: "10.96.0.1/12",
expectedIPs: []string{"192.168.200.1", "10.96.0.1"},
expectedDNSNames: []string{"my-node", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster.external", "foo", "bar.baz"},
},
}
for _, rt := range tests {
_, svcSubnet, _ := net.ParseCIDR(rt.servicecidr)
actual := getAltNames(rt.cfgaltnames, rt.hostname, rt.dnsdomain, svcSubnet)
for i := range actual.IPs {
if rt.expectedIPs[i] != actual.IPs[i].String() {
t.Errorf(
"failed getAltNames:\n\texpected: %s\n\t actual: %s",
rt.expectedIPs[i],
actual.IPs[i].String(),
)
}
func assertHasOrganization(t *testing.T, cert *x509.Certificate, OU string) {
for i := range cert.Subject.Organization {
if cert.Subject.Organization[i] == OU {
return
}
for i := range actual.DNSNames {
if rt.expectedDNSNames[i] != actual.DNSNames[i] {
t.Errorf(
"failed getAltNames:\n\texpected: %s\n\t actual: %s",
rt.expectedDNSNames[i],
actual.DNSNames[i],
)
}
}
t.Errorf("cert does not contain OU %s", OU)
}
func assertHasDNSNames(t *testing.T, cert *x509.Certificate, DNSName string) {
for i := range cert.DNSNames {
if cert.DNSNames[i] == DNSName {
return
}
}
t.Errorf("cert does not contain DNSName %s", DNSName)
}
func assertHasIPAddresses(t *testing.T, cert *x509.Certificate, IPAddress net.IP) {
for i := range cert.IPAddresses {
if cert.IPAddresses[i].Equal(IPAddress) {
return
}
}
t.Errorf("cert does not contain IPAddress %s", IPAddress)
}
......@@ -61,6 +61,16 @@ func NewCertAndKey(caCert *x509.Certificate, caKey *rsa.PrivateKey, config certu
return cert, key, nil
}
// HasServerAuth returns true if the given certificate is a ServerAuth
func HasServerAuth(cert *x509.Certificate) bool {
for i := range cert.ExtKeyUsage {
if cert.ExtKeyUsage[i] == x509.ExtKeyUsageServerAuth {
return true
}
}
return false
}
func WriteCertAndKey(pkiPath string, name string, cert *x509.Certificate, key *rsa.PrivateKey) error {
if err := WriteKey(pkiPath, name, key); err != nil {
return err
......
......@@ -87,6 +87,45 @@ func TestNewCertAndKey(t *testing.T) {
}
}
func TestHasServerAuth(t *testing.T) {
caCert, caKey, _ := NewCertificateAuthority()
var tests = []struct {
config certutil.Config
expected bool
}{
{
config: certutil.Config{
CommonName: "test",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
},
expected: true,
},
{
config: certutil.Config{
CommonName: "test",
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
expected: false,
},
}
for _, rt := range tests {
cert, _, err := NewCertAndKey(caCert, caKey, rt.config)
if err != nil {
t.Fatalf("Couldn't create cert: %v", err)
}
actual := HasServerAuth(cert)
if actual != rt.expected {
t.Errorf(
"failed HasServerAuth:\n\texpected: %t\n\t actual: %t",
rt.expected,
actual,
)
}
}
}
func TestWriteCertAndKey(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "")
if err != nil {
......
......@@ -52,6 +52,7 @@ filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//cmd/kubeadm/app/util/config:all-srcs",
"//cmd/kubeadm/app/util/kubeconfig:all-srcs",
"//cmd/kubeadm/app/util/token:all-srcs",
],
......
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = ["masterconfig.go"],
tags = ["automanaged"],
deps = [
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
"//cmd/kubeadm/app/constants:go_default_library",
"//cmd/kubeadm/app/util:go_default_library",
"//cmd/kubeadm/app/util/token:go_default_library",
"//pkg/api:go_default_library",
"//pkg/util/version:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["masterconfig_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
......@@ -18,17 +18,20 @@ package cmd
import (
"fmt"
"io/ioutil"
"net"
"k8s.io/apimachinery/pkg/runtime"
netutil "k8s.io/apimachinery/pkg/util/net"
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util/version"
)
func setInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error {
func SetInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error {
// Choose the right address for the API Server to advertise. If the advertise address is localhost or 0.0.0.0, the default interface's IP address is used
// This is the same logic as the API Server uses
......@@ -54,15 +57,6 @@ func setInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error {
return fmt.Errorf("this version of kubeadm only supports deploying clusters with the control plane version >= %s. Current version: %s", kubeadmconstants.MinimumControlPlaneVersion.String(), cfg.KubernetesVersion)
}
fmt.Printf("[init] Using Kubernetes version: %s\n", cfg.KubernetesVersion)
fmt.Printf("[init] Using Authorization modes: %v\n", cfg.AuthorizationModes)
// Warn about the limitations with the current cloudprovider solution.
if cfg.CloudProvider != "" {
fmt.Println("[init] WARNING: For cloudprovider integrations to work --cloud-provider must be set for all kubelets in the cluster.")
fmt.Println("\t(/etc/systemd/system/kubelet.service.d/10-kubeadm.conf should be edited for this purpose)")
}
if cfg.Token == "" {
var err error
cfg.Token, err = tokenutil.GenerateToken()
......@@ -73,3 +67,19 @@ func setInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error {
return nil
}
// TryLoadMasterConfiguration tries to loads a Master configuration from the given file (if defined)
func TryLoadMasterConfiguration(cfgPath string, cfg *kubeadmapi.MasterConfiguration) error {
if cfgPath != "" {
b, err := ioutil.ReadFile(cfgPath)
if err != nil {
return fmt.Errorf("unable to read config from %q [%v]", cfgPath, err)
}
if err := runtime.DecodeInto(api.Codecs.UniversalDecoder(), b, cfg); err != nil {
return fmt.Errorf("unable to decode config from %q [%v]", cfgPath, err)
}
}
return nil
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment