mirror of https://github.com/k3s-io/k3s
fully implement kubeadm-phase-certs - stash
parent
4361b4d9be
commit
c2e9052aea
|
@ -13,7 +13,6 @@ go_library(
|
||||||
srcs = [
|
srcs = [
|
||||||
"cmd.go",
|
"cmd.go",
|
||||||
"completion.go",
|
"completion.go",
|
||||||
"defaults.go",
|
|
||||||
"init.go",
|
"init.go",
|
||||||
"join.go",
|
"join.go",
|
||||||
"reset.go",
|
"reset.go",
|
||||||
|
@ -31,13 +30,13 @@ go_library(
|
||||||
"//cmd/kubeadm/app/node:go_default_library",
|
"//cmd/kubeadm/app/node:go_default_library",
|
||||||
"//cmd/kubeadm/app/phases/addons:go_default_library",
|
"//cmd/kubeadm/app/phases/addons:go_default_library",
|
||||||
"//cmd/kubeadm/app/phases/apiconfig: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/controlplane:go_default_library",
|
||||||
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
|
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
|
||||||
"//cmd/kubeadm/app/phases/selfhosting:go_default_library",
|
"//cmd/kubeadm/app/phases/selfhosting:go_default_library",
|
||||||
"//cmd/kubeadm/app/phases/token:go_default_library",
|
"//cmd/kubeadm/app/phases/token:go_default_library",
|
||||||
"//cmd/kubeadm/app/preflight:go_default_library",
|
"//cmd/kubeadm/app/preflight:go_default_library",
|
||||||
"//cmd/kubeadm/app/util: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/kubeconfig:go_default_library",
|
||||||
"//cmd/kubeadm/app/util/token:go_default_library",
|
"//cmd/kubeadm/app/util/token:go_default_library",
|
||||||
"//pkg/api: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/apis/meta/v1:go_default_library",
|
||||||
"//vendor/k8s.io/apimachinery/pkg/fields: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/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/apimachinery/pkg/version:go_default_library",
|
||||||
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||||
|
@ -66,7 +64,6 @@ go_library(
|
||||||
go_test(
|
go_test(
|
||||||
name = "go_default_test",
|
name = "go_default_test",
|
||||||
srcs = [
|
srcs = [
|
||||||
"defaults_test.go",
|
|
||||||
"reset_test.go",
|
"reset_test.go",
|
||||||
"token_test.go",
|
"token_test.go",
|
||||||
],
|
],
|
||||||
|
|
|
@ -31,16 +31,17 @@ import (
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
"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"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
addonsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons"
|
addonsphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/addons"
|
||||||
apiconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/apiconfig"
|
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"
|
controlplanephase "k8s.io/kubernetes/cmd/kubeadm/app/phases/controlplane"
|
||||||
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
kubeconfigphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/kubeconfig"
|
||||||
selfhostingphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/selfhosting"
|
selfhostingphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/selfhosting"
|
||||||
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/token"
|
tokenphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/token"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
|
"k8s.io/kubernetes/cmd/kubeadm/app/preflight"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
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/api"
|
||||||
"k8s.io/kubernetes/pkg/util/version"
|
"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.)
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if !skipPreFlight {
|
||||||
fmt.Println("[preflight] Running pre-flight checks")
|
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 {
|
func (i *Init) Run(out io.Writer) error {
|
||||||
|
|
||||||
// PHASE 1: Generate certificates
|
// PHASE 1: Generate certificates
|
||||||
err := certphase.CreatePKIAssets(i.cfg)
|
err := cmdphases.CreatePKIAssets(i.cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ licenses(["notice"])
|
||||||
load(
|
load(
|
||||||
"@io_bazel_rules_go//go:def.bzl",
|
"@io_bazel_rules_go//go:def.bzl",
|
||||||
"go_library",
|
"go_library",
|
||||||
|
"go_test",
|
||||||
)
|
)
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
|
@ -21,16 +22,31 @@ go_library(
|
||||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
"//cmd/kubeadm/app/apis/kubeadm/v1alpha1:go_default_library",
|
||||||
"//cmd/kubeadm/app/apis/kubeadm/validation: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:go_default_library",
|
||||||
|
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
|
||||||
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
|
"//cmd/kubeadm/app/phases/kubeconfig:go_default_library",
|
||||||
"//cmd/kubeadm/app/phases/selfhosting:go_default_library",
|
"//cmd/kubeadm/app/phases/selfhosting:go_default_library",
|
||||||
"//cmd/kubeadm/app/preflight:go_default_library",
|
"//cmd/kubeadm/app/preflight:go_default_library",
|
||||||
"//cmd/kubeadm/app/util: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/kubeconfig:go_default_library",
|
||||||
"//pkg/api:go_default_library",
|
"//pkg/api:go_default_library",
|
||||||
"//vendor/github.com/spf13/cobra: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",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -17,18 +17,20 @@ limitations under the License.
|
||||||
package phases
|
package phases
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
netutil "k8s.io/apimachinery/pkg/util/net"
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
kubeadmapiext "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1"
|
||||||
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
|
||||||
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
certphase "k8s.io/kubernetes/cmd/kubeadm/app/phases/certs"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
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/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,63 +42,342 @@ func NewCmdCerts() *cobra.Command {
|
||||||
RunE: subCmdRunE("certs"),
|
RunE: subCmdRunE("certs"),
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.AddCommand(NewCmdSelfSign())
|
cmd.AddCommand(newSubCmdCerts()...)
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCmdSelfSign() *cobra.Command {
|
// newSubCmdCerts returns sub commands for certs phase
|
||||||
// TODO: Move this into a dedicated Certificates Phase API object
|
func newSubCmdCerts() []*cobra.Command {
|
||||||
|
|
||||||
cfg := &kubeadmapiext.MasterConfiguration{}
|
cfg := &kubeadmapiext.MasterConfiguration{}
|
||||||
// Default values for the cobra help text
|
// Default values for the cobra help text
|
||||||
api.Scheme.Default(cfg)
|
api.Scheme.Default(cfg)
|
||||||
|
|
||||||
cmd := &cobra.Command{
|
var cfgPath string
|
||||||
Use: "selfsign",
|
var subCmds []*cobra.Command
|
||||||
Short: "Generate the CA, APIServer signing/client cert, the ServiceAccount public/private keys and a CA and client cert for the front proxy",
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
|
||||||
|
|
||||||
// Run the defaulting once again to take passed flags into account
|
subCmdProperties := []struct {
|
||||||
api.Scheme.Default(cfg)
|
use string
|
||||||
internalcfg := &kubeadmapi.MasterConfiguration{}
|
short string
|
||||||
api.Scheme.Convert(cfg, internalcfg, nil)
|
cmdFunc func(cfg *kubeadmapi.MasterConfiguration) error
|
||||||
|
}{
|
||||||
err := RunSelfSign(internalcfg)
|
{
|
||||||
kubeadmutil.CheckErr(err)
|
use: "all",
|
||||||
|
short: "Generate all PKI assets necessary to establish the control plane",
|
||||||
|
cmdFunc: CreatePKIAssets,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
use: "ca",
|
||||||
|
short: "Generate CA certificate and key for a Kubernetes cluster.",
|
||||||
|
cmdFunc: createOrUseCACertAndKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
use: "apiserver",
|
||||||
|
short: "Generate API Server serving certificate and key.",
|
||||||
|
cmdFunc: createOrUseAPIServerCertAndKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
use: "apiserver-kubelet-client",
|
||||||
|
short: "Generate a client certificate for the API Server to connect to the kubelets securely.",
|
||||||
|
cmdFunc: createOrUseAPIServerKubeletClientCertAndKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
use: "sa",
|
||||||
|
short: "Generate a private key for signing service account tokens along with its public key.",
|
||||||
|
cmdFunc: createOrUseServiceAccountKeyAndPublicKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
use: "front-proxy-ca",
|
||||||
|
short: "Generate front proxy CA certificate and key for a Kubernetes cluster.",
|
||||||
|
cmdFunc: createOrUseFrontProxyCACertAndKey,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
use: "front-proxy-client",
|
||||||
|
short: "Generate front proxy CA client certificate and key for a Kubernetes cluster.",
|
||||||
|
cmdFunc: createOrUseFrontProxyClientCertAndKey,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cmd.Flags().StringVar(&cfg.Networking.DNSDomain, "dns-domain", cfg.Networking.DNSDomain, "The DNS Domain for the Kubernetes cluster.")
|
|
||||||
cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, "The path where to save and store the certificates.")
|
|
||||||
cmd.Flags().StringVar(&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet, "The subnet for the Services in the cluster.")
|
|
||||||
cmd.Flags().StringSliceVar(&cfg.APIServerCertSANs, "cert-altnames", []string{}, "Optional extra altnames to use for the API Server serving cert. Can be both IP addresses and dns names.")
|
|
||||||
cmd.Flags().StringVar(&cfg.API.AdvertiseAddress, "apiserver-advertise-address", cfg.API.AdvertiseAddress, "The IP address the API Server will advertise it's listening on. 0.0.0.0 means the default network interface's address.")
|
|
||||||
|
|
||||||
return cmd
|
for _, properties := range subCmdProperties {
|
||||||
|
// Creates the UX Command
|
||||||
|
cmd := &cobra.Command{
|
||||||
|
Use: properties.use,
|
||||||
|
Short: properties.short,
|
||||||
|
Run: runCmdFunc(properties.cmdFunc, &cfgPath, cfg),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add flags to the command
|
||||||
|
cmd.Flags().StringVar(&cfgPath, "config", cfgPath, "Path to kubeadm config file (WARNING: Usage of a configuration file is experimental)")
|
||||||
|
cmd.Flags().StringVar(&cfg.CertificatesDir, "cert-dir", cfg.CertificatesDir, "The path where to save and store the certificates")
|
||||||
|
if properties.use == "all" || properties.use == "apiserver" {
|
||||||
|
cmd.Flags().StringVar(&cfg.Networking.DNSDomain, "service-dns-domain", cfg.Networking.DNSDomain, "Use alternative domain for services, e.g. \"myorg.internal\"")
|
||||||
|
cmd.Flags().StringVar(&cfg.Networking.ServiceSubnet, "service-cidr", cfg.Networking.ServiceSubnet, "Use alternative range of IP address for service VIPs")
|
||||||
|
cmd.Flags().StringSliceVar(&cfg.APIServerCertSANs, "apiserver-cert-extra-sans", []string{}, "Optional extra altnames to use for the API Server serving cert. Can be both IP addresses and dns names.")
|
||||||
|
cmd.Flags().StringVar(&cfg.API.AdvertiseAddress, "apiserver-advertise-address", cfg.API.AdvertiseAddress, "The IP address the API Server will advertise it's listening on. 0.0.0.0 means the default network interface's address.")
|
||||||
|
}
|
||||||
|
|
||||||
|
subCmds = append(subCmds, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
return subCmds
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunSelfSign generates certificate assets in the specified directory
|
// runCmdFunc creates a cobra.Command Run function, by composing the call to the given cmdFunc with necessary additional steps (e.g preparation of inpunt parameters)
|
||||||
func RunSelfSign(config *kubeadmapi.MasterConfiguration) error {
|
func runCmdFunc(cmdFunc func(cfg *kubeadmapi.MasterConfiguration) error, cfgPath *string, cfg *kubeadmapiext.MasterConfiguration) func(cmd *cobra.Command, args []string) {
|
||||||
if err := validateArgs(config); err != nil {
|
|
||||||
return fmt.Errorf("The argument validation failed: %v", err)
|
// the following statement build a clousure that wraps a call to a CreateCertFunc, binding
|
||||||
|
// the function itself with the specific parameters of each sub command.
|
||||||
|
// Please note that specific parameter should be passed as value, while other parameters - passed as reference -
|
||||||
|
// are shared between sub commnands and gets access to current value e.g. flags value.
|
||||||
|
|
||||||
|
return func(cmd *cobra.Command, args []string) {
|
||||||
|
internalcfg := &kubeadmapi.MasterConfiguration{}
|
||||||
|
|
||||||
|
// Takes passed flags into account; the defaulting is executed once again enforcing assignement of
|
||||||
|
// static default values to cfg only for values not provided with flags
|
||||||
|
api.Scheme.Default(cfg)
|
||||||
|
api.Scheme.Convert(cfg, internalcfg, nil)
|
||||||
|
|
||||||
|
// Loads configuration from config file, if provided
|
||||||
|
// Nb. --config overrides command line flags
|
||||||
|
err := configutil.TryLoadMasterConfiguration(*cfgPath, internalcfg)
|
||||||
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
|
// Applies dynamic defaults to settings not provided with flags
|
||||||
|
err = configutil.SetInitDynamicDefaults(internalcfg)
|
||||||
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
|
// Validates cfg (flags/configs + defaults + dynamic defaults)
|
||||||
|
err = validation.ValidateMasterConfiguration(internalcfg).ToAggregate()
|
||||||
|
kubeadmutil.CheckErr(err)
|
||||||
|
|
||||||
|
// Execute the cmdFunc
|
||||||
|
err = cmdFunc(internalcfg)
|
||||||
|
kubeadmutil.CheckErr(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane.
|
||||||
|
// Please note that this action is a bulk action calling all the atomic certphase actions
|
||||||
|
func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration) error {
|
||||||
|
|
||||||
|
certActions := []func(cfg *kubeadmapi.MasterConfiguration) error{
|
||||||
|
createOrUseCACertAndKey,
|
||||||
|
createOrUseAPIServerCertAndKey,
|
||||||
|
createOrUseAPIServerKubeletClientCertAndKey,
|
||||||
|
createOrUseServiceAccountKeyAndPublicKey,
|
||||||
|
createOrUseFrontProxyCACertAndKey,
|
||||||
|
createOrUseFrontProxyClientCertAndKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If it's possible to detect the default IP, add it to the SANs as well. Otherwise, just go with the provided ones
|
for _, action := range certActions {
|
||||||
ip, err := netutil.ChooseBindAddress(net.ParseIP(config.API.AdvertiseAddress))
|
err := action(cfg)
|
||||||
if err == nil {
|
if err != nil {
|
||||||
config.API.AdvertiseAddress = ip.String()
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = certphase.CreatePKIAssets(config); err != nil {
|
fmt.Printf("[certificates] Valid certificates and keys now exist in %q\n", cfg.CertificatesDir)
|
||||||
return err
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createOrUseCACertAndKey create a new self signed CA, or use the existing one.
|
||||||
|
func createOrUseCACertAndKey(cfg *kubeadmapi.MasterConfiguration) error {
|
||||||
|
|
||||||
|
return createOrUseCertificateAuthorithy(
|
||||||
|
cfg.CertificatesDir,
|
||||||
|
kubeadmconstants.CACertAndKeyBaseName,
|
||||||
|
"CA",
|
||||||
|
certphase.NewCACertAndKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createOrUseAPIServerCertAndKey create a new CA certificate for apiserver, or use the existing one.
|
||||||
|
// It assumes the CA certificates should exists into the CertificatesDir
|
||||||
|
func createOrUseAPIServerCertAndKey(cfg *kubeadmapi.MasterConfiguration) error {
|
||||||
|
|
||||||
|
return createOrUseSignedCertificate(
|
||||||
|
cfg.CertificatesDir,
|
||||||
|
kubeadmconstants.CACertAndKeyBaseName,
|
||||||
|
kubeadmconstants.APIServerCertAndKeyBaseName,
|
||||||
|
"API server",
|
||||||
|
func(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||||
|
return certphase.NewAPIServerCertAndKey(cfg, caCert, caKey)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new CA certificate for kubelets calling apiserver, or use the existing one
|
||||||
|
// It assumes the CA certificates should exists into the CertificatesDir
|
||||||
|
func createOrUseAPIServerKubeletClientCertAndKey(cfg *kubeadmapi.MasterConfiguration) error {
|
||||||
|
|
||||||
|
return createOrUseSignedCertificate(
|
||||||
|
cfg.CertificatesDir,
|
||||||
|
kubeadmconstants.CACertAndKeyBaseName,
|
||||||
|
kubeadmconstants.APIServerKubeletClientCertAndKeyBaseName,
|
||||||
|
"API server kubelet client",
|
||||||
|
certphase.NewAPIServerKubeletClientCertAndKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createOrUseServiceAccountKeyAndPublicKey create a new public/private key pairs for signing service account user, or use the existing one.
|
||||||
|
func createOrUseServiceAccountKeyAndPublicKey(cfg *kubeadmapi.MasterConfiguration) error {
|
||||||
|
|
||||||
|
return createOrUseKeyAndPublicKey(
|
||||||
|
cfg.CertificatesDir,
|
||||||
|
kubeadmconstants.ServiceAccountKeyBaseName,
|
||||||
|
"service account",
|
||||||
|
certphase.NewServiceAccountSigningKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createOrUseFrontProxyCACertAndKey create a new self signed front proxy CA, or use the existing one.
|
||||||
|
func createOrUseFrontProxyCACertAndKey(cfg *kubeadmapi.MasterConfiguration) error {
|
||||||
|
|
||||||
|
return createOrUseCertificateAuthorithy(
|
||||||
|
cfg.CertificatesDir,
|
||||||
|
kubeadmconstants.FrontProxyCACertAndKeyBaseName,
|
||||||
|
"front-proxy CA",
|
||||||
|
certphase.NewFrontProxyCACertAndKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createOrUseFrontProxyClientCertAndKey create a new certificate for proxy server client, or use the existing one.
|
||||||
|
// It assumes the front proxy CA certificates should exists into the CertificatesDir
|
||||||
|
func createOrUseFrontProxyClientCertAndKey(cfg *kubeadmapi.MasterConfiguration) error {
|
||||||
|
|
||||||
|
return createOrUseSignedCertificate(
|
||||||
|
cfg.CertificatesDir,
|
||||||
|
kubeadmconstants.FrontProxyCACertAndKeyBaseName,
|
||||||
|
kubeadmconstants.FrontProxyClientCertAndKeyBaseName,
|
||||||
|
"front-proxy client",
|
||||||
|
certphase.NewFrontProxyClientCertAndKey,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// createOrUseCertificateAuthorithy is a generic function that will create a new certificate Authorithy using the given newFunc,
|
||||||
|
// assign file names according to the given baseName, or use the existing one already present in pkiDir.
|
||||||
|
func createOrUseCertificateAuthorithy(pkiDir string, baseName string, UXName string, newFunc func() (*x509.Certificate, *rsa.PrivateKey, error)) error {
|
||||||
|
|
||||||
|
// If cert or key exists, we should try to load them
|
||||||
|
if pkiutil.CertOrKeyExist(pkiDir, baseName) {
|
||||||
|
|
||||||
|
// Try to load .crt and .key from the PKI directory
|
||||||
|
caCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, baseName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failure loading %s certificate: %v", UXName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the existing cert is a CA
|
||||||
|
if !caCert.IsCA {
|
||||||
|
return fmt.Errorf("certificate %s is not a CA", UXName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[certificates] Using the existing %s certificate and key.\n", UXName)
|
||||||
|
} else {
|
||||||
|
// The certificate and the key did NOT exist, let's generate them now
|
||||||
|
caCert, caKey, err := newFunc()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failure while generating %s certificate and key: %v", UXName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write .crt and .key files to disk
|
||||||
|
if err = pkiutil.WriteCertAndKey(pkiDir, baseName, caCert, caKey); err != nil {
|
||||||
|
return fmt.Errorf("failure while saving %s certificate and key: %v", UXName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[certificates] Generated %s certificate and key.\n", UXName)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateArgs(config *kubeadmapi.MasterConfiguration) error {
|
// createOrUseSignedCertificate is a generic function that will create a new signed certificate using the given newFunc,
|
||||||
allErrs := field.ErrorList{}
|
// assign file names according to the given baseName, or use the existing one already present in pkiDir.
|
||||||
allErrs = append(allErrs, validation.ValidateNetworking(&config.Networking, field.NewPath("networking"))...)
|
func createOrUseSignedCertificate(pkiDir string, CABaseName string, baseName string, UXName string, newFunc func(*x509.Certificate, *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error)) error {
|
||||||
allErrs = append(allErrs, validation.ValidateAbsolutePath(config.CertificatesDir, field.NewPath("cert-dir"))...)
|
|
||||||
allErrs = append(allErrs, validation.ValidateAPIServerCertSANs(config.APIServerCertSANs, field.NewPath("cert-altnames"))...)
|
|
||||||
allErrs = append(allErrs, validation.ValidateIPFromString(config.API.AdvertiseAddress, field.NewPath("apiserver-advertise-address"))...)
|
|
||||||
|
|
||||||
return allErrs.ToAggregate()
|
// Checks if certificate authorithy exists in the PKI directory
|
||||||
|
if !pkiutil.CertOrKeyExist(pkiDir, CABaseName) {
|
||||||
|
return fmt.Errorf("couldn't load certificate authorithy for %s from certificate dir", UXName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to load certificate authorithy .crt and .key from the PKI directory
|
||||||
|
caCert, caKey, err := pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, CABaseName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failure loading certificate authorithy for %s: %v", UXName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure the loaded CA cert actually is a CA
|
||||||
|
if !caCert.IsCA {
|
||||||
|
return fmt.Errorf("certificate authorithy for %s is not a CA", UXName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the signed certificate exists in the PKI directory
|
||||||
|
if pkiutil.CertOrKeyExist(pkiDir, baseName) {
|
||||||
|
// Try to load signed certificate .crt and .key from the PKI directory
|
||||||
|
signedCert, _, err := pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, baseName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failure loading %s certificate: %v", UXName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the existing cert is signed by the given CA
|
||||||
|
if err := signedCert.CheckSignatureFrom(caCert); err != nil {
|
||||||
|
return fmt.Errorf("certificate %s is not signed by corresponding CA", UXName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[certificates] Using the existing %s certificate and key.\n", UXName)
|
||||||
|
} else {
|
||||||
|
// The certificate and the key did NOT exist, let's generate them now
|
||||||
|
signedCert, signedKey, err := newFunc(caCert, caKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failure while generating %s key and certificate: %v", UXName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write .crt and .key files to disk
|
||||||
|
if err = pkiutil.WriteCertAndKey(pkiDir, baseName, signedCert, signedKey); err != nil {
|
||||||
|
return fmt.Errorf("failure while saving %s certificate and key: %v", UXName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[certificates] Generated %s certificate and key.\n", UXName)
|
||||||
|
if pkiutil.HasServerAuth(signedCert) {
|
||||||
|
fmt.Printf("[certificates] %s serving cert is signed for DNS names %v and IPs %v\n", UXName, signedCert.DNSNames, signedCert.IPAddresses)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// createOrUseKeyAndPublicKey is a generic function that will create a new public/private key pairs using the given newFunc,
|
||||||
|
// assign file names according to the given baseName, or use the existing one already present in pkiDir.
|
||||||
|
func createOrUseKeyAndPublicKey(pkiDir string, baseName string, UXName string, newFunc func() (*rsa.PrivateKey, error)) error {
|
||||||
|
|
||||||
|
// Checks if the key exists in the PKI directory
|
||||||
|
if pkiutil.CertOrKeyExist(pkiDir, baseName) {
|
||||||
|
|
||||||
|
// Try to load .key from the PKI directory
|
||||||
|
_, err := pkiutil.TryLoadKeyFromDisk(pkiDir, baseName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s key existed but they could not be loaded properly: %v", UXName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("[certificates] Using the existing %s key.\n", UXName)
|
||||||
|
} else {
|
||||||
|
// The key does NOT exist, let's generate it now
|
||||||
|
key, err := newFunc()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failure while generating %s key: %v", UXName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write .key and .pub files to disk
|
||||||
|
if err = pkiutil.WriteKey(pkiDir, baseName, key); err != nil {
|
||||||
|
return fmt.Errorf("failure while saving %s key: %v", UXName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = pkiutil.WritePublicKey(pkiDir, baseName, &key.PublicKey); err != nil {
|
||||||
|
return fmt.Errorf("failure while saving %s public key: %v", UXName, err)
|
||||||
|
}
|
||||||
|
fmt.Printf("[certificates] Generated %s key and public key.\n", UXName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,270 @@
|
||||||
|
/*
|
||||||
|
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"
|
APIServerCertAndKeyBaseName = "apiserver"
|
||||||
APIServerCertName = "apiserver.crt"
|
APIServerCertName = "apiserver.crt"
|
||||||
APIServerKeyName = "apiserver.key"
|
APIServerKeyName = "apiserver.key"
|
||||||
|
APIServerCertCommonName = "kube-apiserver" //used as subject.commonname attribute (CN)
|
||||||
|
|
||||||
APIServerKubeletClientCertAndKeyBaseName = "apiserver-kubelet-client"
|
APIServerKubeletClientCertAndKeyBaseName = "apiserver-kubelet-client"
|
||||||
APIServerKubeletClientCertName = "apiserver-kubelet-client.crt"
|
APIServerKubeletClientCertName = "apiserver-kubelet-client.crt"
|
||||||
APIServerKubeletClientKeyName = "apiserver-kubelet-client.key"
|
APIServerKubeletClientKeyName = "apiserver-kubelet-client.key"
|
||||||
|
APIServerKubeletClientCertCommonName = "kube-apiserver-kubelet-client" //used as subject.commonname attribute (CN)
|
||||||
|
|
||||||
ServiceAccountKeyBaseName = "sa"
|
ServiceAccountKeyBaseName = "sa"
|
||||||
ServiceAccountPublicKeyName = "sa.pub"
|
ServiceAccountPublicKeyName = "sa.pub"
|
||||||
|
@ -53,6 +55,7 @@ const (
|
||||||
FrontProxyClientCertAndKeyBaseName = "front-proxy-client"
|
FrontProxyClientCertAndKeyBaseName = "front-proxy-client"
|
||||||
FrontProxyClientCertName = "front-proxy-client.crt"
|
FrontProxyClientCertName = "front-proxy-client.crt"
|
||||||
FrontProxyClientKeyName = "front-proxy-client.key"
|
FrontProxyClientKeyName = "front-proxy-client.key"
|
||||||
|
FrontProxyClientCertCommonName = "front-proxy-client" //used as subject.commonname attribute (CN)
|
||||||
|
|
||||||
AdminKubeConfigFileName = "admin.conf"
|
AdminKubeConfigFileName = "admin.conf"
|
||||||
KubeletKubeConfigFileName = "kubelet.conf"
|
KubeletKubeConfigFileName = "kubelet.conf"
|
||||||
|
|
|
@ -15,7 +15,7 @@ go_test(
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
"//cmd/kubeadm/app/apis/kubeadm:go_default_library",
|
"//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/constants:go_default_library",
|
||||||
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
|
"//cmd/kubeadm/app/phases/certs/pkiutil:go_default_library",
|
||||||
"//pkg/registry/core/service/ipallocator: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/apimachinery/pkg/util/validation:go_default_library",
|
||||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||||
],
|
],
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
setutil "k8s.io/apimachinery/pkg/util/sets"
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation"
|
"k8s.io/apimachinery/pkg/util/validation"
|
||||||
certutil "k8s.io/client-go/util/cert"
|
certutil "k8s.io/client-go/util/cert"
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
|
@ -32,257 +31,139 @@ import (
|
||||||
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Integration test cases
|
// NewCACertAndKey will generate a self signed CA.
|
||||||
// no files exist => create all four files
|
func NewCACertAndKey() (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||||
// valid ca.{crt,key} exists => create apiserver.{crt,key}
|
|
||||||
// valid ca.{crt,key} and apiserver.{crt,key} exists => do nothing
|
|
||||||
// invalid ca.{crt,key} exists => error
|
|
||||||
// only one of the .crt or .key file exists => error
|
|
||||||
|
|
||||||
// CreatePKIAssets will create and write to disk all PKI assets necessary to establish the control plane.
|
caCert, caKey, err := pkiutil.NewCertificateAuthority()
|
||||||
// It generates a self-signed CA certificate and a server certificate (signed by the CA)
|
if err != nil {
|
||||||
func CreatePKIAssets(cfg *kubeadmapi.MasterConfiguration) error {
|
return nil, nil, fmt.Errorf("failure while generating CA certificate and key: %v", err)
|
||||||
pkiDir := cfg.CertificatesDir
|
}
|
||||||
|
|
||||||
|
return caCert, caKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAPIServerCertAndKey generate CA certificate for apiserver, signed by the given CA.
|
||||||
|
func NewAPIServerCertAndKey(cfg *kubeadmapi.MasterConfiguration, caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||||
|
|
||||||
|
altNames, err := getAltNames(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failure while composing altnames for API server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := certutil.Config{
|
||||||
|
CommonName: kubeadmconstants.APIServerCertCommonName,
|
||||||
|
AltNames: *altNames,
|
||||||
|
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||||
|
}
|
||||||
|
apiCert, apiKey, err := pkiutil.NewCertAndKey(caCert, caKey, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failure while creating API server key and certificate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiCert, apiKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAPIServerKubeletClientCertAndKey generate CA certificate for the apiservers to connect to the kubelets securely, signed by the given CA.
|
||||||
|
func NewAPIServerKubeletClientCertAndKey(caCert *x509.Certificate, caKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||||
|
|
||||||
|
config := certutil.Config{
|
||||||
|
CommonName: kubeadmconstants.APIServerKubeletClientCertCommonName,
|
||||||
|
Organization: []string{kubeadmconstants.MastersGroup},
|
||||||
|
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||||
|
}
|
||||||
|
apiClientCert, apiClientKey, err := pkiutil.NewCertAndKey(caCert, caKey, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failure while creating API server kubelet client key and certificate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return apiClientCert, apiClientKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServiceAccountSigningKey generate public/private key pairs for signing service account tokens.
|
||||||
|
func NewServiceAccountSigningKey() (*rsa.PrivateKey, error) {
|
||||||
|
|
||||||
|
// The key does NOT exist, let's generate it now
|
||||||
|
saSigningKey, err := certutil.NewPrivateKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failure while creating service account token signing key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return saSigningKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFrontProxyCACertAndKey generate a self signed front proxy CA.
|
||||||
|
// Front proxy CA and client certs are used to secure a front proxy authenticator which is used to assert identity
|
||||||
|
// without the client cert.
|
||||||
|
// This is a separte CA, so that front proxy identities cannot hit the API and normal client certs cannot be used
|
||||||
|
// as front proxies.
|
||||||
|
func NewFrontProxyCACertAndKey() (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||||
|
|
||||||
|
frontProxyCACert, frontProxyCAKey, err := pkiutil.NewCertificateAuthority()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failure while generating front-proxy CA certificate and key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return frontProxyCACert, frontProxyCAKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFrontProxyClientCertAndKey generate CA certificate for proxy server client, signed by the given front proxy CA.
|
||||||
|
func NewFrontProxyClientCertAndKey(frontProxyCACert *x509.Certificate, frontProxyCAKey *rsa.PrivateKey) (*x509.Certificate, *rsa.PrivateKey, error) {
|
||||||
|
|
||||||
|
config := certutil.Config{
|
||||||
|
CommonName: kubeadmconstants.FrontProxyClientCertCommonName,
|
||||||
|
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
||||||
|
}
|
||||||
|
frontProxyClientCert, frontProxyClientKey, err := pkiutil.NewCertAndKey(frontProxyCACert, frontProxyCAKey, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("failure while creating front-proxy client key and certificate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return frontProxyClientCert, frontProxyClientKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAltNames builds an AltNames object for to be used when generating apiserver certificate
|
||||||
|
func getAltNames(cfg *kubeadmapi.MasterConfiguration) (*certutil.AltNames, error) {
|
||||||
|
|
||||||
|
// host name
|
||||||
hostname, err := os.Hostname()
|
hostname, err := os.Hostname()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("couldn't get the hostname: %v", err)
|
return nil, fmt.Errorf("couldn't get the hostname: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// advertise address
|
||||||
|
advertiseAddress := net.ParseIP(cfg.API.AdvertiseAddress)
|
||||||
|
if advertiseAddress == nil {
|
||||||
|
return nil, fmt.Errorf("error parsing API AdvertiseAddress %v: is not a valid textual representation of an IP address", cfg.API.AdvertiseAddress)
|
||||||
|
}
|
||||||
|
|
||||||
|
// internal IP address for the API server
|
||||||
_, svcSubnet, err := net.ParseCIDR(cfg.Networking.ServiceSubnet)
|
_, svcSubnet, err := net.ParseCIDR(cfg.Networking.ServiceSubnet)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error parsing CIDR %q: %v", cfg.Networking.ServiceSubnet, err)
|
return nil, fmt.Errorf("error parsing CIDR %q: %v", cfg.Networking.ServiceSubnet, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the list of SANs
|
internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(svcSubnet, 1)
|
||||||
altNames := getAltNames(cfg.APIServerCertSANs, hostname, cfg.Networking.DNSDomain, svcSubnet)
|
if err != nil {
|
||||||
// Append the address the API Server is advertising
|
return nil, fmt.Errorf("unable to get first IP address from the given CIDR (%s): %v", svcSubnet.String(), err)
|
||||||
altNames.IPs = append(altNames.IPs, net.ParseIP(cfg.API.AdvertiseAddress))
|
|
||||||
|
|
||||||
var caCert *x509.Certificate
|
|
||||||
var caKey *rsa.PrivateKey
|
|
||||||
// If at least one of them exists, we should try to load them
|
|
||||||
// In the case that only one exists, there will most likely be an error anyway
|
|
||||||
if pkiutil.CertOrKeyExist(pkiDir, kubeadmconstants.CACertAndKeyBaseName) {
|
|
||||||
// Try to load ca.crt and ca.key from the PKI directory
|
|
||||||
caCert, caKey, err = pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, kubeadmconstants.CACertAndKeyBaseName)
|
|
||||||
if err != nil || caCert == nil || caKey == nil {
|
|
||||||
return fmt.Errorf("certificate and/or key existed but they could not be loaded properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The certificate and key could be loaded, but the certificate is not a CA
|
|
||||||
if !caCert.IsCA {
|
|
||||||
return fmt.Errorf("certificate and key could be loaded but the certificate is not a CA")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("[certificates] Using the existing CA certificate and key.")
|
|
||||||
} else {
|
|
||||||
// The certificate and the key did NOT exist, let's generate them now
|
|
||||||
caCert, caKey, err = pkiutil.NewCertificateAuthority()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failure while generating CA certificate and key [%v]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.CACertAndKeyBaseName, caCert, caKey); err != nil {
|
|
||||||
return fmt.Errorf("failure while saving CA certificate and key [%v]", err)
|
|
||||||
}
|
|
||||||
fmt.Println("[certificates] Generated CA certificate and key.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If at least one of them exists, we should try to load them
|
// create AltNames with defaults DNSNames/IPs
|
||||||
// In the case that only one exists, there will most likely be an error anyway
|
altNames := &certutil.AltNames{
|
||||||
if pkiutil.CertOrKeyExist(pkiDir, kubeadmconstants.APIServerCertAndKeyBaseName) {
|
|
||||||
// Try to load apiserver.crt and apiserver.key from the PKI directory
|
|
||||||
apiCert, apiKey, err := pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, kubeadmconstants.APIServerCertAndKeyBaseName)
|
|
||||||
if err != nil || apiCert == nil || apiKey == nil {
|
|
||||||
return fmt.Errorf("certificate and/or key existed but they could not be loaded properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("[certificates] Using the existing API Server certificate and key.")
|
|
||||||
} else {
|
|
||||||
// The certificate and the key did NOT exist, let's generate them now
|
|
||||||
// TODO: Add a test case to verify that this cert has the x509.ExtKeyUsageServerAuth flag
|
|
||||||
config := certutil.Config{
|
|
||||||
CommonName: "kube-apiserver",
|
|
||||||
AltNames: altNames,
|
|
||||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
|
||||||
}
|
|
||||||
apiCert, apiKey, err := pkiutil.NewCertAndKey(caCert, caKey, config)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failure while creating API server key and certificate [%v]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.APIServerCertAndKeyBaseName, apiCert, apiKey); err != nil {
|
|
||||||
return fmt.Errorf("failure while saving API server certificate and key [%v]", err)
|
|
||||||
}
|
|
||||||
fmt.Println("[certificates] Generated API server certificate and key.")
|
|
||||||
fmt.Printf("[certificates] API Server serving cert is signed for DNS names %v and IPs %v\n", altNames.DNSNames, altNames.IPs)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If at least one of them exists, we should try to load them
|
|
||||||
// In the case that only one exists, there will most likely be an error anyway
|
|
||||||
if pkiutil.CertOrKeyExist(pkiDir, kubeadmconstants.APIServerKubeletClientCertAndKeyBaseName) {
|
|
||||||
// Try to load apiserver-kubelet-client.crt and apiserver-kubelet-client.key from the PKI directory
|
|
||||||
apiCert, apiKey, err := pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, kubeadmconstants.APIServerKubeletClientCertAndKeyBaseName)
|
|
||||||
if err != nil || apiCert == nil || apiKey == nil {
|
|
||||||
return fmt.Errorf("certificate and/or key existed but they could not be loaded properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("[certificates] Using the existing API Server kubelet client certificate and key.")
|
|
||||||
} else {
|
|
||||||
// The certificate and the key did NOT exist, let's generate them now
|
|
||||||
// TODO: Add a test case to verify that this cert has the x509.ExtKeyUsageClientAuth flag
|
|
||||||
config := certutil.Config{
|
|
||||||
CommonName: "kube-apiserver-kubelet-client",
|
|
||||||
Organization: []string{kubeadmconstants.MastersGroup},
|
|
||||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
||||||
}
|
|
||||||
apiClientCert, apiClientKey, err := pkiutil.NewCertAndKey(caCert, caKey, config)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failure while creating API server kubelet client key and certificate [%v]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.APIServerKubeletClientCertAndKeyBaseName, apiClientCert, apiClientKey); err != nil {
|
|
||||||
return fmt.Errorf("failure while saving API server kubelet client certificate and key [%v]", err)
|
|
||||||
}
|
|
||||||
fmt.Println("[certificates] Generated API server kubelet client certificate and key.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the key exists, we should try to load it
|
|
||||||
if pkiutil.CertOrKeyExist(pkiDir, kubeadmconstants.ServiceAccountKeyBaseName) {
|
|
||||||
// Try to load sa.key from the PKI directory
|
|
||||||
_, err := pkiutil.TryLoadKeyFromDisk(pkiDir, kubeadmconstants.ServiceAccountKeyBaseName)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("certificate and/or key existed but they could not be loaded properly [%v]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("[certificates] Using the existing service account token signing key.")
|
|
||||||
} else {
|
|
||||||
// The key does NOT exist, let's generate it now
|
|
||||||
saTokenSigningKey, err := certutil.NewPrivateKey()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failure while creating service account token signing key [%v]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pkiutil.WriteKey(pkiDir, kubeadmconstants.ServiceAccountKeyBaseName, saTokenSigningKey); err != nil {
|
|
||||||
return fmt.Errorf("failure while saving service account token signing key [%v]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pkiutil.WritePublicKey(pkiDir, kubeadmconstants.ServiceAccountKeyBaseName, &saTokenSigningKey.PublicKey); err != nil {
|
|
||||||
return fmt.Errorf("failure while saving service account token signing public key [%v]", err)
|
|
||||||
}
|
|
||||||
fmt.Println("[certificates] Generated service account token signing key and public key.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// front proxy CA and client certs are used to secure a front proxy authenticator which is used to assert identity
|
|
||||||
// without the client cert, you cannot make use of the front proxy and the kube-aggregator uses this connection
|
|
||||||
// so we generate and enable it unconditionally
|
|
||||||
// This is a separte CA, so that front proxy identities cannot hit the API and normal client certs cannot be used
|
|
||||||
// as front proxies.
|
|
||||||
var frontProxyCACert *x509.Certificate
|
|
||||||
var frontProxyCAKey *rsa.PrivateKey
|
|
||||||
// If at least one of them exists, we should try to load them
|
|
||||||
// In the case that only one exists, there will most likely be an error anyway
|
|
||||||
if pkiutil.CertOrKeyExist(pkiDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName) {
|
|
||||||
// Try to load front-proxy-ca.crt and front-proxy-ca.key from the PKI directory
|
|
||||||
frontProxyCACert, frontProxyCAKey, err = pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName)
|
|
||||||
if err != nil || frontProxyCACert == nil || frontProxyCAKey == nil {
|
|
||||||
return fmt.Errorf("certificate and/or key existed but they could not be loaded properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The certificate and key could be loaded, but the certificate is not a CA
|
|
||||||
if !frontProxyCACert.IsCA {
|
|
||||||
return fmt.Errorf("certificate and key could be loaded but the certificate is not a CA")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("[certificates] Using the existing front-proxy CA certificate and key.")
|
|
||||||
} else {
|
|
||||||
// The certificate and the key did NOT exist, let's generate them now
|
|
||||||
frontProxyCACert, frontProxyCAKey, err = pkiutil.NewCertificateAuthority()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failure while generating front-proxy CA certificate and key [%v]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.FrontProxyCACertAndKeyBaseName, frontProxyCACert, frontProxyCAKey); err != nil {
|
|
||||||
return fmt.Errorf("failure while saving front-proxy CA certificate and key [%v]", err)
|
|
||||||
}
|
|
||||||
fmt.Println("[certificates] Generated front-proxy CA certificate and key.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point we have a front proxy CA signing key. We can use that create the front proxy client cert if
|
|
||||||
// it doesn't already exist.
|
|
||||||
// If at least one of them exists, we should try to load them
|
|
||||||
// In the case that only one exists, there will most likely be an error anyway
|
|
||||||
if pkiutil.CertOrKeyExist(pkiDir, kubeadmconstants.FrontProxyClientCertAndKeyBaseName) {
|
|
||||||
// Try to load apiserver-kubelet-client.crt and apiserver-kubelet-client.key from the PKI directory
|
|
||||||
apiCert, apiKey, err := pkiutil.TryLoadCertAndKeyFromDisk(pkiDir, kubeadmconstants.FrontProxyClientCertAndKeyBaseName)
|
|
||||||
if err != nil || apiCert == nil || apiKey == nil {
|
|
||||||
return fmt.Errorf("certificate and/or key existed but they could not be loaded properly")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("[certificates] Using the existing front-proxy client certificate and key.")
|
|
||||||
} else {
|
|
||||||
// The certificate and the key did NOT exist, let's generate them now
|
|
||||||
// TODO: Add a test case to verify that this cert has the x509.ExtKeyUsageClientAuth flag
|
|
||||||
config := certutil.Config{
|
|
||||||
CommonName: "front-proxy-client",
|
|
||||||
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
|
|
||||||
}
|
|
||||||
apiClientCert, apiClientKey, err := pkiutil.NewCertAndKey(frontProxyCACert, frontProxyCAKey, config)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failure while creating front-proxy client key and certificate [%v]", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = pkiutil.WriteCertAndKey(pkiDir, kubeadmconstants.FrontProxyClientCertAndKeyBaseName, apiClientCert, apiClientKey); err != nil {
|
|
||||||
return fmt.Errorf("failure while saving front-proxy client certificate and key [%v]", err)
|
|
||||||
}
|
|
||||||
fmt.Println("[certificates] Generated front-proxy client certificate and key.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("[certificates] Valid certificates and keys now exist in %q\n", pkiDir)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkAltNamesExist verifies that the cert is valid for all IPs and DNS names it should be valid for
|
|
||||||
func checkAltNamesExist(IPs []net.IP, DNSNames []string, altNames certutil.AltNames) bool {
|
|
||||||
dnsset := setutil.NewString(DNSNames...)
|
|
||||||
|
|
||||||
for _, dnsNameThatShouldExist := range altNames.DNSNames {
|
|
||||||
if !dnsset.Has(dnsNameThatShouldExist) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ipThatShouldExist := range altNames.IPs {
|
|
||||||
found := false
|
|
||||||
for _, ip := range IPs {
|
|
||||||
if ip.Equal(ipThatShouldExist) {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// getAltNames builds an AltNames object for the certutil to use when generating the certificates
|
|
||||||
func getAltNames(cfgAltNames []string, hostname, dnsdomain string, svcSubnet *net.IPNet) certutil.AltNames {
|
|
||||||
altNames := certutil.AltNames{
|
|
||||||
DNSNames: []string{
|
DNSNames: []string{
|
||||||
hostname,
|
hostname,
|
||||||
"kubernetes",
|
"kubernetes",
|
||||||
"kubernetes.default",
|
"kubernetes.default",
|
||||||
"kubernetes.default.svc",
|
"kubernetes.default.svc",
|
||||||
fmt.Sprintf("kubernetes.default.svc.%s", dnsdomain),
|
fmt.Sprintf("kubernetes.default.svc.%s", cfg.Networking.DNSDomain),
|
||||||
|
},
|
||||||
|
IPs: []net.IP{
|
||||||
|
internalAPIServerVirtualIP,
|
||||||
|
advertiseAddress,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Populate IPs/DNSNames from AltNames
|
// adds additional SAN
|
||||||
for _, altname := range cfgAltNames {
|
for _, altname := range cfg.APIServerCertSANs {
|
||||||
if ip := net.ParseIP(altname); ip != nil {
|
if ip := net.ParseIP(altname); ip != nil {
|
||||||
altNames.IPs = append(altNames.IPs, ip)
|
altNames.IPs = append(altNames.IPs, ip)
|
||||||
} else if len(validation.IsDNS1123Subdomain(altname)) == 0 {
|
} else if len(validation.IsDNS1123Subdomain(altname)) == 0 {
|
||||||
|
@ -290,11 +171,5 @@ func getAltNames(cfgAltNames []string, hostname, dnsdomain string, svcSubnet *ne
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// and lastly, extract the internal IP address for the API server
|
return altNames, nil
|
||||||
internalAPIServerVirtualIP, err := ipallocator.GetIndexedIP(svcSubnet, 1)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("[certs] WARNING: Unable to get first IP address from the given CIDR (%s): %v\n", svcSubnet.String(), err)
|
|
||||||
}
|
|
||||||
altNames.IPs = append(altNames.IPs, internalAPIServerVirtualIP)
|
|
||||||
return altNames
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,158 +17,151 @@ limitations under the License.
|
||||||
package certs
|
package certs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"crypto/x509"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
certutil "k8s.io/client-go/util/cert"
|
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
|
"k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreatePKIAssets(t *testing.T) {
|
func TestNewCACertAndKey(t *testing.T) {
|
||||||
tmpdir, err := ioutil.TempDir("", "")
|
caCert, _, err := NewCACertAndKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Couldn't create tmpdir")
|
t.Fatalf("failed call NewCACertAndKey: %v", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpdir)
|
|
||||||
|
|
||||||
var tests = []struct {
|
assertIsCa(t, caCert)
|
||||||
cfg *kubeadmapi.MasterConfiguration
|
}
|
||||||
expected bool
|
|
||||||
}{
|
func TestNewAPIServerCertAndKey(t *testing.T) {
|
||||||
{
|
hostname, err := os.Hostname()
|
||||||
cfg: &kubeadmapi.MasterConfiguration{},
|
if err != nil {
|
||||||
expected: false,
|
t.Errorf("couldn't get the hostname: %v", err)
|
||||||
},
|
|
||||||
{
|
|
||||||
// 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 {
|
advertiseIP := "1.2.3.4"
|
||||||
actual := CreatePKIAssets(rt.cfg)
|
cfg := &kubeadmapi.MasterConfiguration{
|
||||||
if (actual == nil) != rt.expected {
|
API: kubeadmapi.API{AdvertiseAddress: advertiseIP},
|
||||||
t.Errorf(
|
Networking: kubeadmapi.Networking{ServiceSubnet: "10.96.0.0/12", DNSDomain: "cluster.local"},
|
||||||
"failed CreatePKIAssets with an error:\n\texpected: %t\n\t actual: %t",
|
}
|
||||||
rt.expected,
|
caCert, caKey, err := NewCACertAndKey()
|
||||||
(actual == nil),
|
|
||||||
)
|
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 TestCheckAltNamesExist(t *testing.T) {
|
func TestNewAPIServerKubeletClientCertAndKey(t *testing.T) {
|
||||||
var tests = []struct {
|
caCert, caKey, err := NewCACertAndKey()
|
||||||
IPs []net.IP
|
|
||||||
DNSNames []string
|
apiClientCert, _, err := NewAPIServerKubeletClientCertAndKey(caCert, caKey)
|
||||||
requiredAltNames certutil.AltNames
|
if err != nil {
|
||||||
succeed bool
|
t.Fatalf("failed creation of cert and key: %v", err)
|
||||||
}{
|
|
||||||
{
|
|
||||||
// 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 {
|
assertIsSignedByCa(t, apiClientCert, caCert)
|
||||||
succeeded := checkAltNamesExist(rt.IPs, rt.DNSNames, rt.requiredAltNames)
|
assertHasClientAuth(t, apiClientCert)
|
||||||
if succeeded != rt.succeed {
|
assertHasOrganization(t, apiClientCert, constants.MastersGroup)
|
||||||
t.Errorf(
|
}
|
||||||
"failed checkAltNamesExist:\n\texpected: %t\n\t actual: %t",
|
|
||||||
rt.succeed,
|
func TestNewNewServiceAccountSigningKey(t *testing.T) {
|
||||||
succeeded,
|
|
||||||
)
|
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 TestGetAltNames(t *testing.T) {
|
func TestNewFrontProxyCACertAndKey(t *testing.T) {
|
||||||
var tests = []struct {
|
frontProxyCACert, _, err := NewFrontProxyCACertAndKey()
|
||||||
cfgaltnames []string
|
if err != nil {
|
||||||
hostname string
|
t.Fatalf("failed creation of cert and key: %v", err)
|
||||||
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 {
|
assertIsCa(t, frontProxyCACert)
|
||||||
_, svcSubnet, _ := net.ParseCIDR(rt.servicecidr)
|
}
|
||||||
actual := getAltNames(rt.cfgaltnames, rt.hostname, rt.dnsdomain, svcSubnet)
|
|
||||||
for i := range actual.IPs {
|
func TestNewFrontProxyClientCertAndKey(t *testing.T) {
|
||||||
if rt.expectedIPs[i] != actual.IPs[i].String() {
|
frontProxyCACert, frontProxyCAKey, err := NewFrontProxyCACertAndKey()
|
||||||
t.Errorf(
|
|
||||||
"failed getAltNames:\n\texpected: %s\n\t actual: %s",
|
frontProxyClientCert, _, err := NewFrontProxyClientCertAndKey(frontProxyCACert, frontProxyCAKey)
|
||||||
rt.expectedIPs[i],
|
if err != nil {
|
||||||
actual.IPs[i].String(),
|
t.Fatalf("failed creation of cert and key: %v", err)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
}
|
assertIsSignedByCa(t, frontProxyClientCert, frontProxyCACert)
|
||||||
for i := range actual.DNSNames {
|
assertHasClientAuth(t, frontProxyClientCert)
|
||||||
if rt.expectedDNSNames[i] != actual.DNSNames[i] {
|
}
|
||||||
t.Errorf(
|
|
||||||
"failed getAltNames:\n\texpected: %s\n\t actual: %s",
|
func assertIsCa(t *testing.T, cert *x509.Certificate) {
|
||||||
rt.expectedDNSNames[i],
|
if !cert.IsCA {
|
||||||
actual.DNSNames[i],
|
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 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 assertHasOrganization(t *testing.T, cert *x509.Certificate, OU string) {
|
||||||
|
for i := range cert.Subject.Organization {
|
||||||
|
if cert.Subject.Organization[i] == OU {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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
|
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 {
|
func WriteCertAndKey(pkiPath string, name string, cert *x509.Certificate, key *rsa.PrivateKey) error {
|
||||||
if err := WriteKey(pkiPath, name, key); err != nil {
|
if err := WriteKey(pkiPath, name, key); err != nil {
|
||||||
return err
|
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) {
|
func TestWriteCertAndKey(t *testing.T) {
|
||||||
tmpdir, err := ioutil.TempDir("", "")
|
tmpdir, err := ioutil.TempDir("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -52,6 +52,7 @@ filegroup(
|
||||||
name = "all-srcs",
|
name = "all-srcs",
|
||||||
srcs = [
|
srcs = [
|
||||||
":package-srcs",
|
":package-srcs",
|
||||||
|
"//cmd/kubeadm/app/util/config:all-srcs",
|
||||||
"//cmd/kubeadm/app/util/kubeconfig:all-srcs",
|
"//cmd/kubeadm/app/util/kubeconfig:all-srcs",
|
||||||
"//cmd/kubeadm/app/util/token:all-srcs",
|
"//cmd/kubeadm/app/util/token:all-srcs",
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
netutil "k8s.io/apimachinery/pkg/util/net"
|
netutil "k8s.io/apimachinery/pkg/util/net"
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||||
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
|
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
|
||||||
|
"k8s.io/kubernetes/pkg/api"
|
||||||
"k8s.io/kubernetes/pkg/util/version"
|
"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
|
// 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
|
// 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)
|
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 == "" {
|
if cfg.Token == "" {
|
||||||
var err error
|
var err error
|
||||||
cfg.Token, err = tokenutil.GenerateToken()
|
cfg.Token, err = tokenutil.GenerateToken()
|
||||||
|
@ -73,3 +67,19 @@ func setInitDynamicDefaults(cfg *kubeadmapi.MasterConfiguration) error {
|
||||||
|
|
||||||
return nil
|
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
|
||||||
|
}
|
Loading…
Reference in New Issue