Unverified Commit 1e4ad048 authored by k8s-ci-robot's avatar k8s-ci-robot Committed by GitHub

Merge pull request #67763 from rosti/join-discovery-split

kubeadm: Split discovery from JoinConfiguration
parents ee434665 576b8d3f
...@@ -133,7 +133,9 @@ func fuzzJoinConfiguration(obj *kubeadm.JoinConfiguration, c fuzz.Continue) { ...@@ -133,7 +133,9 @@ func fuzzJoinConfiguration(obj *kubeadm.JoinConfiguration, c fuzz.Continue) {
// Pinning values for fields that get defaults if fuzz value is empty string or nil (thus making the round trip test fail) // Pinning values for fields that get defaults if fuzz value is empty string or nil (thus making the round trip test fail)
obj.CACertPath = "foo" obj.CACertPath = "foo"
obj.ClusterName = "bar" obj.ClusterName = "bar"
obj.DiscoveryTimeout = &metav1.Duration{Duration: 1234} obj.Discovery = kubeadm.Discovery{
obj.DiscoveryToken = "baz" BootstrapToken: &kubeadm.BootstrapTokenDiscovery{Token: "baz"},
obj.TLSBootstrapToken = "qux" TLSBootstrapToken: "qux",
Timeout: &metav1.Duration{Duration: 1234},
}
} }
...@@ -280,27 +280,55 @@ type JoinConfiguration struct { ...@@ -280,27 +280,55 @@ type JoinConfiguration struct {
// secure comunications between node and master. // secure comunications between node and master.
// Defaults to "/etc/kubernetes/pki/ca.crt". // Defaults to "/etc/kubernetes/pki/ca.crt".
CACertPath string CACertPath string
// DiscoveryFile is a file or url to a kubeconfig file from which to
// load cluster information. // Discovery specifies the options for the kubelet to use during the TLS Bootstrap process
DiscoveryFile string Discovery Discovery
// DiscoveryToken is a token used to validate cluster information
// fetched from the master. // The cluster name
DiscoveryToken string ClusterName string
// DiscoveryTokenAPIServers is a set of IPs to API servers from which info
// will be fetched. Currently we only pay attention to one API server but // ControlPlane flag specifies that the joining node should host an additional
// hope to support >1 in the future. // control plane instance.
DiscoveryTokenAPIServers []string ControlPlane bool
// DiscoveryTimeout modifies the discovery timeout
DiscoveryTimeout *metav1.Duration // APIEndpoint represents the endpoint of the instance of the API server eventually to be deployed on this node.
APIEndpoint APIEndpoint
// FeatureGates enabled by the user.
FeatureGates map[string]bool
}
// Discovery specifies the options for the kubelet to use during the TLS Bootstrap process
type Discovery struct {
// BootstrapToken is used to set the options for bootstrap token based discovery
// BootstrapToken and File are mutually exclusive
BootstrapToken *BootstrapTokenDiscovery
// File is used to specify a file or URL to a kubeconfig file from which to load cluster information
// BootstrapToken and File are mutually exclusive
File *FileDiscovery
// TLSBootstrapToken is a token used for TLS bootstrapping. // TLSBootstrapToken is a token used for TLS bootstrapping.
// Defaults to Token. // If .BootstrapToken is set, this field is defaulted to .BootstrapToken.Token, but can be overridden.
// If .File is set, this field **must be set** in case the KubeConfigFile does not contain any other authentication information
TLSBootstrapToken string TLSBootstrapToken string
// Token is used for both discovery and TLS bootstrapping.
// Timeout modifies the discovery timeout
Timeout *metav1.Duration
}
// BootstrapTokenDiscovery is used to set the options for bootstrap token based discovery
type BootstrapTokenDiscovery struct {
// Token is a token used to validate cluster information
// fetched from the master.
Token string Token string
// The cluster name
ClusterName string
// DiscoveryTokenCACertHashes specifies a set of public key pins to verify // APIServerEndpoints is a set of IPs or domain names to API servers from which info
// will be fetched. Currently we only pay attention to one API server but
// hope to support >1 in the future.
APIServerEndpoints []string
// CACertHashes specifies a set of public key pins to verify
// when token-based discovery is used. The root CA found during discovery // when token-based discovery is used. The root CA found during discovery
// must match one of these values. Specifying an empty set disables root CA // must match one of these values. Specifying an empty set disables root CA
// pinning, which can be unsafe. Each hash is specified as "<type>:<value>", // pinning, which can be unsafe. Each hash is specified as "<type>:<value>",
...@@ -308,22 +336,18 @@ type JoinConfiguration struct { ...@@ -308,22 +336,18 @@ type JoinConfiguration struct {
// SHA-256 hash of the Subject Public Key Info (SPKI) object in DER-encoded // SHA-256 hash of the Subject Public Key Info (SPKI) object in DER-encoded
// ASN.1. These hashes can be calculated using, for example, OpenSSL: // ASN.1. These hashes can be calculated using, for example, OpenSSL:
// openssl x509 -pubkey -in ca.crt openssl rsa -pubin -outform der 2>&/dev/null | openssl dgst -sha256 -hex // openssl x509 -pubkey -in ca.crt openssl rsa -pubin -outform der 2>&/dev/null | openssl dgst -sha256 -hex
DiscoveryTokenCACertHashes []string CACertHashes []string
// DiscoveryTokenUnsafeSkipCAVerification allows token-based discovery // UnsafeSkipCAVerification allows token-based discovery
// without CA verification via DiscoveryTokenCACertHashes. This can weaken // without CA verification via CACertHashes. This can weaken
// the security of kubeadm since other nodes can impersonate the master. // the security of kubeadm since other nodes can impersonate the master.
DiscoveryTokenUnsafeSkipCAVerification bool UnsafeSkipCAVerification bool
}
// ControlPlane flag specifies that the joining node should host an additional
// control plane instance.
ControlPlane bool
// APIEndpoint represents the endpoint of the instance of the API server eventually to be deployed on this node.
APIEndpoint APIEndpoint
// FeatureGates enabled by the user. // FileDiscovery is used to specify a file or URL to a kubeconfig file from which to load cluster information
FeatureGates map[string]bool type FileDiscovery struct {
// KubeConfigPath is used to specify the actual file path or URL to the kubeconfig file from which to load cluster information
KubeConfigPath string
} }
// GetControlPlaneImageRepository returns name of image repository // GetControlPlaneImageRepository returns name of image repository
......
...@@ -4,6 +4,7 @@ go_library( ...@@ -4,6 +4,7 @@ go_library(
name = "go_default_library", name = "go_default_library",
srcs = [ srcs = [
"bootstraptokenstring.go", "bootstraptokenstring.go",
"conversion.go",
"defaults.go", "defaults.go",
"defaults_unix.go", "defaults_unix.go",
"defaults_windows.go", "defaults_windows.go",
......
/*
Copyright 2018 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 v1alpha3
import (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)
func Convert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(in *JoinConfiguration, out *kubeadm.JoinConfiguration, s conversion.Scope) error {
if err := autoConvert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(in, out, s); err != nil {
return err
}
out.Discovery.Timeout = in.DiscoveryTimeout
if len(in.TLSBootstrapToken) != 0 {
out.Discovery.TLSBootstrapToken = in.TLSBootstrapToken
} else {
out.Discovery.TLSBootstrapToken = in.Token
}
if len(in.DiscoveryFile) != 0 {
out.Discovery.File = &kubeadm.FileDiscovery{
KubeConfigPath: in.DiscoveryFile,
}
} else {
out.Discovery.BootstrapToken = &kubeadm.BootstrapTokenDiscovery{
APIServerEndpoints: in.DiscoveryTokenAPIServers,
CACertHashes: in.DiscoveryTokenCACertHashes,
UnsafeSkipCAVerification: in.DiscoveryTokenUnsafeSkipCAVerification,
}
if len(in.DiscoveryToken) != 0 {
out.Discovery.BootstrapToken.Token = in.DiscoveryToken
} else {
out.Discovery.BootstrapToken.Token = in.Token
}
}
return nil
}
func Convert_kubeadm_JoinConfiguration_To_v1alpha3_JoinConfiguration(in *kubeadm.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error {
if err := autoConvert_kubeadm_JoinConfiguration_To_v1alpha3_JoinConfiguration(in, out, s); err != nil {
return err
}
out.DiscoveryTimeout = in.Discovery.Timeout
out.TLSBootstrapToken = in.Discovery.TLSBootstrapToken
if in.Discovery.BootstrapToken != nil {
out.DiscoveryToken = in.Discovery.BootstrapToken.Token
out.DiscoveryTokenAPIServers = in.Discovery.BootstrapToken.APIServerEndpoints
out.DiscoveryTokenCACertHashes = in.Discovery.BootstrapToken.CACertHashes
out.DiscoveryTokenUnsafeSkipCAVerification = in.Discovery.BootstrapToken.UnsafeSkipCAVerification
} else if in.Discovery.File != nil {
out.DiscoveryFile = in.Discovery.File.KubeConfigPath
}
return nil
}
...@@ -177,6 +177,16 @@ func RegisterConversions(s *runtime.Scheme) error { ...@@ -177,6 +177,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil { }); err != nil {
return err return err
} }
if err := s.AddConversionFunc((*kubeadm.JoinConfiguration)(nil), (*JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_kubeadm_JoinConfiguration_To_v1alpha3_JoinConfiguration(a.(*kubeadm.JoinConfiguration), b.(*JoinConfiguration), scope)
}); err != nil {
return err
}
if err := s.AddConversionFunc((*JoinConfiguration)(nil), (*kubeadm.JoinConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(a.(*JoinConfiguration), b.(*kubeadm.JoinConfiguration), scope)
}); err != nil {
return err
}
return nil return nil
} }
...@@ -483,15 +493,15 @@ func autoConvert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(in *Joi ...@@ -483,15 +493,15 @@ func autoConvert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(in *Joi
return err return err
} }
out.CACertPath = in.CACertPath out.CACertPath = in.CACertPath
out.DiscoveryFile = in.DiscoveryFile // WARNING: in.DiscoveryFile requires manual conversion: does not exist in peer-type
out.DiscoveryToken = in.DiscoveryToken // WARNING: in.DiscoveryToken requires manual conversion: does not exist in peer-type
out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers)) // WARNING: in.DiscoveryTokenAPIServers requires manual conversion: does not exist in peer-type
out.DiscoveryTimeout = (*v1.Duration)(unsafe.Pointer(in.DiscoveryTimeout)) // WARNING: in.DiscoveryTimeout requires manual conversion: does not exist in peer-type
out.TLSBootstrapToken = in.TLSBootstrapToken // WARNING: in.TLSBootstrapToken requires manual conversion: does not exist in peer-type
out.Token = in.Token // WARNING: in.Token requires manual conversion: does not exist in peer-type
out.ClusterName = in.ClusterName out.ClusterName = in.ClusterName
out.DiscoveryTokenCACertHashes = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenCACertHashes)) // WARNING: in.DiscoveryTokenCACertHashes requires manual conversion: does not exist in peer-type
out.DiscoveryTokenUnsafeSkipCAVerification = in.DiscoveryTokenUnsafeSkipCAVerification // WARNING: in.DiscoveryTokenUnsafeSkipCAVerification requires manual conversion: does not exist in peer-type
out.ControlPlane = in.ControlPlane out.ControlPlane = in.ControlPlane
if err := Convert_v1alpha3_APIEndpoint_To_kubeadm_APIEndpoint(&in.APIEndpoint, &out.APIEndpoint, s); err != nil { if err := Convert_v1alpha3_APIEndpoint_To_kubeadm_APIEndpoint(&in.APIEndpoint, &out.APIEndpoint, s); err != nil {
return err return err
...@@ -500,25 +510,13 @@ func autoConvert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(in *Joi ...@@ -500,25 +510,13 @@ func autoConvert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(in *Joi
return nil return nil
} }
// Convert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration is an autogenerated conversion function.
func Convert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(in *JoinConfiguration, out *kubeadm.JoinConfiguration, s conversion.Scope) error {
return autoConvert_v1alpha3_JoinConfiguration_To_kubeadm_JoinConfiguration(in, out, s)
}
func autoConvert_kubeadm_JoinConfiguration_To_v1alpha3_JoinConfiguration(in *kubeadm.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error { func autoConvert_kubeadm_JoinConfiguration_To_v1alpha3_JoinConfiguration(in *kubeadm.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error {
if err := Convert_kubeadm_NodeRegistrationOptions_To_v1alpha3_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil { if err := Convert_kubeadm_NodeRegistrationOptions_To_v1alpha3_NodeRegistrationOptions(&in.NodeRegistration, &out.NodeRegistration, s); err != nil {
return err return err
} }
out.CACertPath = in.CACertPath out.CACertPath = in.CACertPath
out.DiscoveryFile = in.DiscoveryFile // WARNING: in.Discovery requires manual conversion: does not exist in peer-type
out.DiscoveryToken = in.DiscoveryToken
out.DiscoveryTokenAPIServers = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenAPIServers))
out.DiscoveryTimeout = (*v1.Duration)(unsafe.Pointer(in.DiscoveryTimeout))
out.TLSBootstrapToken = in.TLSBootstrapToken
out.Token = in.Token
out.ClusterName = in.ClusterName out.ClusterName = in.ClusterName
out.DiscoveryTokenCACertHashes = *(*[]string)(unsafe.Pointer(&in.DiscoveryTokenCACertHashes))
out.DiscoveryTokenUnsafeSkipCAVerification = in.DiscoveryTokenUnsafeSkipCAVerification
out.ControlPlane = in.ControlPlane out.ControlPlane = in.ControlPlane
if err := Convert_kubeadm_APIEndpoint_To_v1alpha3_APIEndpoint(&in.APIEndpoint, &out.APIEndpoint, s); err != nil { if err := Convert_kubeadm_APIEndpoint_To_v1alpha3_APIEndpoint(&in.APIEndpoint, &out.APIEndpoint, s); err != nil {
return err return err
...@@ -527,11 +525,6 @@ func autoConvert_kubeadm_JoinConfiguration_To_v1alpha3_JoinConfiguration(in *kub ...@@ -527,11 +525,6 @@ func autoConvert_kubeadm_JoinConfiguration_To_v1alpha3_JoinConfiguration(in *kub
return nil return nil
} }
// Convert_kubeadm_JoinConfiguration_To_v1alpha3_JoinConfiguration is an autogenerated conversion function.
func Convert_kubeadm_JoinConfiguration_To_v1alpha3_JoinConfiguration(in *kubeadm.JoinConfiguration, out *JoinConfiguration, s conversion.Scope) error {
return autoConvert_kubeadm_JoinConfiguration_To_v1alpha3_JoinConfiguration(in, out, s)
}
func autoConvert_v1alpha3_LocalEtcd_To_kubeadm_LocalEtcd(in *LocalEtcd, out *kubeadm.LocalEtcd, s conversion.Scope) error { func autoConvert_v1alpha3_LocalEtcd_To_kubeadm_LocalEtcd(in *LocalEtcd, out *kubeadm.LocalEtcd, s conversion.Scope) error {
out.Image = in.Image out.Image = in.Image
out.DataDir = in.DataDir out.DataDir = in.DataDir
......
...@@ -120,30 +120,14 @@ func SetDefaults_JoinConfiguration(obj *JoinConfiguration) { ...@@ -120,30 +120,14 @@ func SetDefaults_JoinConfiguration(obj *JoinConfiguration) {
if obj.CACertPath == "" { if obj.CACertPath == "" {
obj.CACertPath = DefaultCACertPath obj.CACertPath = DefaultCACertPath
} }
if len(obj.TLSBootstrapToken) == 0 {
obj.TLSBootstrapToken = obj.Token
}
if len(obj.DiscoveryToken) == 0 && len(obj.DiscoveryFile) == 0 {
obj.DiscoveryToken = obj.Token
}
// Make sure file URLs become paths
if len(obj.DiscoveryFile) != 0 {
u, err := url.Parse(obj.DiscoveryFile)
if err == nil && u.Scheme == "file" {
obj.DiscoveryFile = u.Path
}
}
if obj.DiscoveryTimeout == nil {
obj.DiscoveryTimeout = &metav1.Duration{
Duration: DefaultDiscoveryTimeout,
}
}
if obj.ClusterName == "" { if obj.ClusterName == "" {
obj.ClusterName = DefaultClusterName obj.ClusterName = DefaultClusterName
} }
SetDefaults_NodeRegistrationOptions(&obj.NodeRegistration) SetDefaults_NodeRegistrationOptions(&obj.NodeRegistration)
SetDefaults_APIEndpoint(&obj.APIEndpoint) SetDefaults_APIEndpoint(&obj.APIEndpoint)
SetDefaults_Discovery(&obj.Discovery)
} }
func SetDefaults_NodeRegistrationOptions(obj *NodeRegistrationOptions) { func SetDefaults_NodeRegistrationOptions(obj *NodeRegistrationOptions) {
...@@ -152,6 +136,34 @@ func SetDefaults_NodeRegistrationOptions(obj *NodeRegistrationOptions) { ...@@ -152,6 +136,34 @@ func SetDefaults_NodeRegistrationOptions(obj *NodeRegistrationOptions) {
} }
} }
// SetDefaults_Discovery assigns default values for the discovery process
func SetDefaults_Discovery(obj *Discovery) {
if len(obj.TLSBootstrapToken) == 0 && obj.BootstrapToken != nil {
obj.TLSBootstrapToken = obj.BootstrapToken.Token
}
if obj.Timeout == nil {
obj.Timeout = &metav1.Duration{
Duration: DefaultDiscoveryTimeout,
}
}
if obj.File != nil {
SetDefaults_FileDiscovery(obj.File)
}
}
// SetDefaults_FileDiscovery assigns default values for file based discovery
func SetDefaults_FileDiscovery(obj *FileDiscovery) {
// Make sure file URL becomes path
if len(obj.KubeConfigPath) != 0 {
u, err := url.Parse(obj.KubeConfigPath)
if err == nil && u.Scheme == "file" {
obj.KubeConfigPath = u.Path
}
}
}
// SetDefaults_AuditPolicyConfiguration sets default values for the AuditPolicyConfiguration // SetDefaults_AuditPolicyConfiguration sets default values for the AuditPolicyConfiguration
func SetDefaults_AuditPolicyConfiguration(obj *ClusterConfiguration) { func SetDefaults_AuditPolicyConfiguration(obj *ClusterConfiguration) {
if obj.AuditPolicyConfiguration.LogDir == "" { if obj.AuditPolicyConfiguration.LogDir == "" {
......
...@@ -258,28 +258,55 @@ type JoinConfiguration struct { ...@@ -258,28 +258,55 @@ type JoinConfiguration struct {
// secure comunications between node and master. // secure comunications between node and master.
// Defaults to "/etc/kubernetes/pki/ca.crt". // Defaults to "/etc/kubernetes/pki/ca.crt".
CACertPath string `json:"caCertPath"` CACertPath string `json:"caCertPath"`
// DiscoveryFile is a file or url to a kubeconfig file from which to
// load cluster information. // Discovery specifies the options for the kubelet to use during the TLS Bootstrap process
DiscoveryFile string `json:"discoveryFile"` Discovery Discovery `json:"discovery"`
// DiscoveryToken is a token used to validate cluster information
// fetched from the master. // ClusterName is the name for the cluster in kubeconfig.
DiscoveryToken string `json:"discoveryToken"` ClusterName string `json:"clusterName,omitempty"`
// DiscoveryTokenAPIServers is a set of IPs to API servers from which info
// will be fetched. Currently we only pay attention to one API server but // ControlPlane flag specifies that the joining node should host an additional
// hope to support >1 in the future. // control plane instance.
DiscoveryTokenAPIServers []string `json:"discoveryTokenAPIServers,omitempty"` ControlPlane bool `json:"controlPlane,omitempty"`
// DiscoveryTimeout modifies the discovery timeout
DiscoveryTimeout *metav1.Duration `json:"discoveryTimeout,omitempty"` // APIEndpoint represents the endpoint of the instance of the API server eventually to be deployed on this node.
APIEndpoint APIEndpoint `json:"apiEndpoint,omitempty"`
// FeatureGates enabled by the user.
FeatureGates map[string]bool `json:"featureGates,omitempty"`
}
// Discovery specifies the options for the kubelet to use during the TLS Bootstrap process
type Discovery struct {
// BootstrapToken is used to set the options for bootstrap token based discovery
// BootstrapToken and File are mutually exclusive
BootstrapToken *BootstrapTokenDiscovery `json:"bootstrapToken,omitempty"`
// File is used to specify a file or URL to a kubeconfig file from which to load cluster information
// BootstrapToken and File are mutually exclusive
File *FileDiscovery `json:"file,omitempty"`
// TLSBootstrapToken is a token used for TLS bootstrapping. // TLSBootstrapToken is a token used for TLS bootstrapping.
// Defaults to Token. // If .BootstrapToken is set, this field is defaulted to .BootstrapToken.Token, but can be overridden.
// If .File is set, this field **must be set** in case the KubeConfigFile does not contain any other authentication information
TLSBootstrapToken string `json:"tlsBootstrapToken"` TLSBootstrapToken string `json:"tlsBootstrapToken"`
// Token is used for both discovery and TLS bootstrapping.
// Timeout modifies the discovery timeout
Timeout *metav1.Duration `json:"timeout,omitempty"`
}
// BootstrapTokenDiscovery is used to set the options for bootstrap token based discovery
type BootstrapTokenDiscovery struct {
// Token is a token used to validate cluster information
// fetched from the master.
Token string `json:"token"` Token string `json:"token"`
// ClusterName is the name for the cluster in kubeconfig. // APIServerEndpoints is a set of IPs or domain names to API servers from which info
ClusterName string `json:"clusterName,omitempty"` // will be fetched. Currently we only pay attention to one API server but
// hope to support >1 in the future.
APIServerEndpoints []string `json:"apiServerEndpoints,omitempty"`
// DiscoveryTokenCACertHashes specifies a set of public key pins to verify // CACertHashes specifies a set of public key pins to verify
// when token-based discovery is used. The root CA found during discovery // when token-based discovery is used. The root CA found during discovery
// must match one of these values. Specifying an empty set disables root CA // must match one of these values. Specifying an empty set disables root CA
// pinning, which can be unsafe. Each hash is specified as "<type>:<value>", // pinning, which can be unsafe. Each hash is specified as "<type>:<value>",
...@@ -287,22 +314,18 @@ type JoinConfiguration struct { ...@@ -287,22 +314,18 @@ type JoinConfiguration struct {
// SHA-256 hash of the Subject Public Key Info (SPKI) object in DER-encoded // SHA-256 hash of the Subject Public Key Info (SPKI) object in DER-encoded
// ASN.1. These hashes can be calculated using, for example, OpenSSL: // ASN.1. These hashes can be calculated using, for example, OpenSSL:
// openssl x509 -pubkey -in ca.crt openssl rsa -pubin -outform der 2>&/dev/null | openssl dgst -sha256 -hex // openssl x509 -pubkey -in ca.crt openssl rsa -pubin -outform der 2>&/dev/null | openssl dgst -sha256 -hex
DiscoveryTokenCACertHashes []string `json:"discoveryTokenCACertHashes,omitempty"` CACertHashes []string `json:"caCertHashes,omitempty"`
// DiscoveryTokenUnsafeSkipCAVerification allows token-based discovery // UnsafeSkipCAVerification allows token-based discovery
// without CA verification via DiscoveryTokenCACertHashes. This can weaken // without CA verification via CACertHashes. This can weaken
// the security of kubeadm since other nodes can impersonate the master. // the security of kubeadm since other nodes can impersonate the master.
DiscoveryTokenUnsafeSkipCAVerification bool `json:"discoveryTokenUnsafeSkipCAVerification"` UnsafeSkipCAVerification bool `json:"unsafeSkipCAVerification"`
}
// ControlPlane flag specifies that the joining node should host an additional
// control plane instance.
ControlPlane bool `json:"controlPlane,omitempty"`
// APIEndpoint represents the endpoint of the instance of the API server eventually to be deployed on this node.
APIEndpoint APIEndpoint `json:"apiEndpoint,omitempty"`
// FeatureGates enabled by the user. // FileDiscovery is used to specify a file or URL to a kubeconfig file from which to load cluster information
FeatureGates map[string]bool `json:"featureGates,omitempty"` type FileDiscovery struct {
// KubeConfigPath is used to specify the actual file path or URL to the kubeconfig file from which to load cluster information
KubeConfigPath string `json:"kubeConfigPath"`
} }
// HostPathMount contains elements describing volumes that are mounted from the // HostPathMount contains elements describing volumes that are mounted from the
......
...@@ -104,6 +104,32 @@ func (in *BootstrapToken) DeepCopy() *BootstrapToken { ...@@ -104,6 +104,32 @@ func (in *BootstrapToken) DeepCopy() *BootstrapToken {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BootstrapTokenDiscovery) DeepCopyInto(out *BootstrapTokenDiscovery) {
*out = *in
if in.APIServerEndpoints != nil {
in, out := &in.APIServerEndpoints, &out.APIServerEndpoints
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.CACertHashes != nil {
in, out := &in.CACertHashes, &out.CACertHashes
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapTokenDiscovery.
func (in *BootstrapTokenDiscovery) DeepCopy() *BootstrapTokenDiscovery {
if in == nil {
return nil
}
out := new(BootstrapTokenDiscovery)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BootstrapTokenString) DeepCopyInto(out *BootstrapTokenString) { func (in *BootstrapTokenString) DeepCopyInto(out *BootstrapTokenString) {
*out = *in *out = *in
return return
...@@ -228,6 +254,37 @@ func (in *ClusterStatus) DeepCopyObject() runtime.Object { ...@@ -228,6 +254,37 @@ func (in *ClusterStatus) DeepCopyObject() runtime.Object {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Discovery) DeepCopyInto(out *Discovery) {
*out = *in
if in.BootstrapToken != nil {
in, out := &in.BootstrapToken, &out.BootstrapToken
*out = new(BootstrapTokenDiscovery)
(*in).DeepCopyInto(*out)
}
if in.File != nil {
in, out := &in.File, &out.File
*out = new(FileDiscovery)
**out = **in
}
if in.Timeout != nil {
in, out := &in.Timeout, &out.Timeout
*out = new(v1.Duration)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Discovery.
func (in *Discovery) DeepCopy() *Discovery {
if in == nil {
return nil
}
out := new(Discovery)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Etcd) DeepCopyInto(out *Etcd) { func (in *Etcd) DeepCopyInto(out *Etcd) {
*out = *in *out = *in
if in.Local != nil { if in.Local != nil {
...@@ -275,6 +332,22 @@ func (in *ExternalEtcd) DeepCopy() *ExternalEtcd { ...@@ -275,6 +332,22 @@ func (in *ExternalEtcd) DeepCopy() *ExternalEtcd {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FileDiscovery) DeepCopyInto(out *FileDiscovery) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileDiscovery.
func (in *FileDiscovery) DeepCopy() *FileDiscovery {
if in == nil {
return nil
}
out := new(FileDiscovery)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HostPathMount) DeepCopyInto(out *HostPathMount) { func (in *HostPathMount) DeepCopyInto(out *HostPathMount) {
*out = *in *out = *in
return return
...@@ -330,21 +403,7 @@ func (in *JoinConfiguration) DeepCopyInto(out *JoinConfiguration) { ...@@ -330,21 +403,7 @@ func (in *JoinConfiguration) DeepCopyInto(out *JoinConfiguration) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
in.NodeRegistration.DeepCopyInto(&out.NodeRegistration) in.NodeRegistration.DeepCopyInto(&out.NodeRegistration)
if in.DiscoveryTokenAPIServers != nil { in.Discovery.DeepCopyInto(&out.Discovery)
in, out := &in.DiscoveryTokenAPIServers, &out.DiscoveryTokenAPIServers
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.DiscoveryTimeout != nil {
in, out := &in.DiscoveryTimeout, &out.DiscoveryTimeout
*out = new(v1.Duration)
**out = **in
}
if in.DiscoveryTokenCACertHashes != nil {
in, out := &in.DiscoveryTokenCACertHashes, &out.DiscoveryTokenCACertHashes
*out = make([]string, len(*in))
copy(*out, *in)
}
out.APIEndpoint = in.APIEndpoint out.APIEndpoint = in.APIEndpoint
if in.FeatureGates != nil { if in.FeatureGates != nil {
in, out := &in.FeatureGates, &out.FeatureGates in, out := &in.FeatureGates, &out.FeatureGates
......
...@@ -56,5 +56,9 @@ func SetObjectDefaults_InitConfiguration(in *InitConfiguration) { ...@@ -56,5 +56,9 @@ func SetObjectDefaults_InitConfiguration(in *InitConfiguration) {
func SetObjectDefaults_JoinConfiguration(in *JoinConfiguration) { func SetObjectDefaults_JoinConfiguration(in *JoinConfiguration) {
SetDefaults_JoinConfiguration(in) SetDefaults_JoinConfiguration(in)
SetDefaults_NodeRegistrationOptions(&in.NodeRegistration) SetDefaults_NodeRegistrationOptions(&in.NodeRegistration)
SetDefaults_Discovery(&in.Discovery)
if in.Discovery.File != nil {
SetDefaults_FileDiscovery(in.Discovery.File)
}
SetDefaults_APIEndpoint(&in.APIEndpoint) SetDefaults_APIEndpoint(&in.APIEndpoint)
} }
...@@ -68,7 +68,7 @@ func ValidateClusterConfiguration(c *kubeadm.ClusterConfiguration) field.ErrorLi ...@@ -68,7 +68,7 @@ func ValidateClusterConfiguration(c *kubeadm.ClusterConfiguration) field.ErrorLi
// ValidateJoinConfiguration validates node configuration and collects all encountered errors // ValidateJoinConfiguration validates node configuration and collects all encountered errors
func ValidateJoinConfiguration(c *kubeadm.JoinConfiguration) field.ErrorList { func ValidateJoinConfiguration(c *kubeadm.JoinConfiguration) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateDiscovery(c)...) allErrs = append(allErrs, ValidateDiscovery(&c.Discovery, field.NewPath("discovery"))...)
allErrs = append(allErrs, ValidateNodeRegistrationOptions(&c.NodeRegistration, field.NewPath("nodeRegistration"))...) allErrs = append(allErrs, ValidateNodeRegistrationOptions(&c.NodeRegistration, field.NewPath("nodeRegistration"))...)
allErrs = append(allErrs, ValidateAPIEndpoint(&c.APIEndpoint, field.NewPath("apiEndpoint"))...) allErrs = append(allErrs, ValidateAPIEndpoint(&c.APIEndpoint, field.NewPath("apiEndpoint"))...)
...@@ -92,56 +92,66 @@ func ValidateNodeRegistrationOptions(nro *kubeadm.NodeRegistrationOptions, fldPa ...@@ -92,56 +92,66 @@ func ValidateNodeRegistrationOptions(nro *kubeadm.NodeRegistrationOptions, fldPa
} }
// ValidateDiscovery validates discovery related configuration and collects all encountered errors // ValidateDiscovery validates discovery related configuration and collects all encountered errors
func ValidateDiscovery(c *kubeadm.JoinConfiguration) field.ErrorList { func ValidateDiscovery(d *kubeadm.Discovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if len(c.DiscoveryToken) != 0 {
allErrs = append(allErrs, ValidateToken(c.DiscoveryToken, field.NewPath("discoveryToken"))...) if d.BootstrapToken == nil && d.File == nil {
allErrs = append(allErrs, field.Invalid(fldPath, "", "bootstrapToken or file must be set"))
}
if d.BootstrapToken != nil && d.File != nil {
allErrs = append(allErrs, field.Invalid(fldPath, "", "bootstrapToken and file cannot both be set"))
} }
if len(c.DiscoveryFile) != 0 {
allErrs = append(allErrs, ValidateDiscoveryFile(c.DiscoveryFile, field.NewPath("discoveryFile"))...) if d.BootstrapToken != nil {
if len(c.TLSBootstrapToken) != 0 { allErrs = append(allErrs, ValidateDiscoveryBootstrapToken(d.BootstrapToken, fldPath.Child("bootstrapToken"))...)
allErrs = append(allErrs, ValidateToken(c.TLSBootstrapToken, field.NewPath("tlsBootstrapToken"))...) allErrs = append(allErrs, ValidateToken(d.TLSBootstrapToken, fldPath.Child("tlsBootstrapToken"))...)
}
if d.File != nil {
allErrs = append(allErrs, ValidateDiscoveryFile(d.File, fldPath.Child("file"))...)
if len(d.TLSBootstrapToken) != 0 {
allErrs = append(allErrs, ValidateToken(d.TLSBootstrapToken, fldPath.Child("tlsBootstrapToken"))...)
} }
} else {
allErrs = append(allErrs, ValidateToken(c.TLSBootstrapToken, field.NewPath("tlsBootstrapToken"))...)
} }
allErrs = append(allErrs, ValidateArgSelection(c, field.NewPath("discovery"))...)
allErrs = append(allErrs, ValidateJoinDiscoveryTokenAPIServer(c.DiscoveryTokenAPIServers, field.NewPath("discoveryTokenAPIServers"))...)
return allErrs return allErrs
} }
// ValidateArgSelection validates discovery related configuration and collects all encountered errors // ValidateDiscoveryBootstrapToken validates bootstrap token discovery configuration
func ValidateArgSelection(cfg *kubeadm.JoinConfiguration, fldPath *field.Path) field.ErrorList { func ValidateDiscoveryBootstrapToken(b *kubeadm.BootstrapTokenDiscovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
if len(cfg.DiscoveryToken) != 0 && len(cfg.DiscoveryFile) != 0 {
allErrs = append(allErrs, field.Invalid(fldPath, "", "discoveryToken and discoveryFile cannot both be set"))
}
if len(cfg.DiscoveryToken) == 0 && len(cfg.DiscoveryFile) == 0 {
allErrs = append(allErrs, field.Invalid(fldPath, "", "discoveryToken or discoveryFile must be set"))
}
if len(cfg.DiscoveryTokenAPIServers) < 1 && len(cfg.DiscoveryToken) != 0 {
allErrs = append(allErrs, field.Required(fldPath, "discoveryTokenAPIServers not set"))
}
if len(cfg.DiscoveryFile) != 0 && len(cfg.DiscoveryTokenCACertHashes) != 0 {
allErrs = append(allErrs, field.Invalid(fldPath, "", "discoveryTokenCACertHashes cannot be used with discoveryFile"))
}
if len(cfg.DiscoveryFile) == 0 && len(cfg.DiscoveryToken) != 0 && if len(b.APIServerEndpoints) < 1 {
len(cfg.DiscoveryTokenCACertHashes) == 0 && !cfg.DiscoveryTokenUnsafeSkipCAVerification { allErrs = append(allErrs, field.Required(fldPath, "APIServerEndpoints not set"))
allErrs = append(allErrs, field.Invalid(fldPath, "", "using token-based discovery without discoveryTokenCACertHashes can be unsafe. set --discovery-token-unsafe-skip-ca-verification to continue"))
} }
// TODO remove once we support multiple api servers // TODO remove once we support multiple api servers
if len(cfg.DiscoveryTokenAPIServers) > 1 { if len(b.APIServerEndpoints) > 1 {
fmt.Println("[validation] WARNING: kubeadm doesn't fully support multiple API Servers yet") fmt.Println("[validation] WARNING: kubeadm doesn't fully support multiple API Servers yet")
} }
if len(b.CACertHashes) == 0 && !b.UnsafeSkipCAVerification {
allErrs = append(allErrs, field.Invalid(fldPath, "", "using token-based discovery without caCertHashes can be unsafe. Set unsafeSkipCAVerification to continue"))
}
allErrs = append(allErrs, ValidateToken(b.Token, fldPath.Child("token"))...)
allErrs = append(allErrs, ValidateDiscoveryTokenAPIServer(b.APIServerEndpoints, fldPath.Child("apiServerEndpoints"))...)
return allErrs
}
// ValidateDiscoveryFile validates file discovery configuration
func ValidateDiscoveryFile(f *kubeadm.FileDiscovery, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, ValidateDiscoveryKubeConfigPath(f.KubeConfigPath, fldPath.Child("kubeConfigPath"))...)
return allErrs return allErrs
} }
// ValidateJoinDiscoveryTokenAPIServer validates discovery token for API server // ValidateDiscoveryTokenAPIServer validates discovery token for API server
func ValidateJoinDiscoveryTokenAPIServer(apiServers []string, fldPath *field.Path) field.ErrorList { func ValidateDiscoveryTokenAPIServer(apiServers []string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
for _, m := range apiServers { for _, m := range apiServers {
_, _, err := net.SplitHostPort(m) _, _, err := net.SplitHostPort(m)
...@@ -152,8 +162,8 @@ func ValidateJoinDiscoveryTokenAPIServer(apiServers []string, fldPath *field.Pat ...@@ -152,8 +162,8 @@ func ValidateJoinDiscoveryTokenAPIServer(apiServers []string, fldPath *field.Pat
return allErrs return allErrs
} }
// ValidateDiscoveryFile validates location of a discovery file // ValidateDiscoveryKubeConfigPath validates location of a discovery file
func ValidateDiscoveryFile(discoveryFile string, fldPath *field.Path) field.ErrorList { func ValidateDiscoveryKubeConfigPath(discoveryFile string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{} allErrs := field.ErrorList{}
u, err := url.Parse(discoveryFile) u, err := url.Parse(discoveryFile)
if err != nil { if err != nil {
......
...@@ -106,6 +106,32 @@ func (in *BootstrapToken) DeepCopy() *BootstrapToken { ...@@ -106,6 +106,32 @@ func (in *BootstrapToken) DeepCopy() *BootstrapToken {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BootstrapTokenDiscovery) DeepCopyInto(out *BootstrapTokenDiscovery) {
*out = *in
if in.APIServerEndpoints != nil {
in, out := &in.APIServerEndpoints, &out.APIServerEndpoints
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.CACertHashes != nil {
in, out := &in.CACertHashes, &out.CACertHashes
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapTokenDiscovery.
func (in *BootstrapTokenDiscovery) DeepCopy() *BootstrapTokenDiscovery {
if in == nil {
return nil
}
out := new(BootstrapTokenDiscovery)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *BootstrapTokenString) DeepCopyInto(out *BootstrapTokenString) { func (in *BootstrapTokenString) DeepCopyInto(out *BootstrapTokenString) {
*out = *in *out = *in
return return
...@@ -257,6 +283,37 @@ func (in *ComponentConfigs) DeepCopy() *ComponentConfigs { ...@@ -257,6 +283,37 @@ func (in *ComponentConfigs) DeepCopy() *ComponentConfigs {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Discovery) DeepCopyInto(out *Discovery) {
*out = *in
if in.BootstrapToken != nil {
in, out := &in.BootstrapToken, &out.BootstrapToken
*out = new(BootstrapTokenDiscovery)
(*in).DeepCopyInto(*out)
}
if in.File != nil {
in, out := &in.File, &out.File
*out = new(FileDiscovery)
**out = **in
}
if in.Timeout != nil {
in, out := &in.Timeout, &out.Timeout
*out = new(v1.Duration)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Discovery.
func (in *Discovery) DeepCopy() *Discovery {
if in == nil {
return nil
}
out := new(Discovery)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Etcd) DeepCopyInto(out *Etcd) { func (in *Etcd) DeepCopyInto(out *Etcd) {
*out = *in *out = *in
if in.Local != nil { if in.Local != nil {
...@@ -304,6 +361,22 @@ func (in *ExternalEtcd) DeepCopy() *ExternalEtcd { ...@@ -304,6 +361,22 @@ func (in *ExternalEtcd) DeepCopy() *ExternalEtcd {
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *FileDiscovery) DeepCopyInto(out *FileDiscovery) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FileDiscovery.
func (in *FileDiscovery) DeepCopy() *FileDiscovery {
if in == nil {
return nil
}
out := new(FileDiscovery)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HostPathMount) DeepCopyInto(out *HostPathMount) { func (in *HostPathMount) DeepCopyInto(out *HostPathMount) {
*out = *in *out = *in
return return
...@@ -359,21 +432,7 @@ func (in *JoinConfiguration) DeepCopyInto(out *JoinConfiguration) { ...@@ -359,21 +432,7 @@ func (in *JoinConfiguration) DeepCopyInto(out *JoinConfiguration) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
in.NodeRegistration.DeepCopyInto(&out.NodeRegistration) in.NodeRegistration.DeepCopyInto(&out.NodeRegistration)
if in.DiscoveryTokenAPIServers != nil { in.Discovery.DeepCopyInto(&out.Discovery)
in, out := &in.DiscoveryTokenAPIServers, &out.DiscoveryTokenAPIServers
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.DiscoveryTimeout != nil {
in, out := &in.DiscoveryTimeout, &out.DiscoveryTimeout
*out = new(v1.Duration)
**out = **in
}
if in.DiscoveryTokenCACertHashes != nil {
in, out := &in.DiscoveryTokenCACertHashes, &out.DiscoveryTokenCACertHashes
*out = make([]string, len(*in))
copy(*out, *in)
}
out.APIEndpoint = in.APIEndpoint out.APIEndpoint = in.APIEndpoint
if in.FeatureGates != nil { if in.FeatureGates != nil {
in, out := &in.FeatureGates, &out.FeatureGates in, out := &in.FeatureGates, &out.FeatureGates
......
...@@ -195,9 +195,13 @@ func getDefaultInitConfigBytes(kind string) ([]byte, error) { ...@@ -195,9 +195,13 @@ func getDefaultInitConfigBytes(kind string) ([]byte, error) {
func getDefaultNodeConfigBytes() ([]byte, error) { func getDefaultNodeConfigBytes() ([]byte, error) {
internalcfg, err := configutil.JoinConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1beta1.JoinConfiguration{ internalcfg, err := configutil.JoinConfigFileAndDefaultsToInternalConfig("", &kubeadmapiv1beta1.JoinConfiguration{
Token: sillyToken.Token.String(), Discovery: kubeadmapiv1beta1.Discovery{
DiscoveryTokenAPIServers: []string{"kube-apiserver:6443"}, BootstrapToken: &kubeadmapiv1beta1.BootstrapTokenDiscovery{
DiscoveryTokenUnsafeSkipCAVerification: true, // TODO: DiscoveryTokenUnsafeSkipCAVerification: true needs to be set for validation to pass, but shouldn't be recommended as the default Token: sillyToken.Token.String(),
APIServerEndpoints: []string{"kube-apiserver:6443"},
UnsafeSkipCAVerification: true, // TODO: UnsafeSkipCAVerification: true needs to be set for validation to pass, but shouldn't be recommended as the default
},
},
}) })
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
......
...@@ -159,6 +159,10 @@ func NewCmdJoin(out io.Writer) *cobra.Command { ...@@ -159,6 +159,10 @@ func NewCmdJoin(out io.Writer) *cobra.Command {
cfg := &kubeadmapiv1beta1.JoinConfiguration{} cfg := &kubeadmapiv1beta1.JoinConfiguration{}
kubeadmscheme.Scheme.Default(cfg) kubeadmscheme.Scheme.Default(cfg)
fd := &kubeadmapiv1beta1.FileDiscovery{}
btd := &kubeadmapiv1beta1.BootstrapTokenDiscovery{}
var token string
var cfgPath string var cfgPath string
var featureGatesString string var featureGatesString string
var ignorePreflightErrors []string var ignorePreflightErrors []string
...@@ -168,22 +172,37 @@ func NewCmdJoin(out io.Writer) *cobra.Command { ...@@ -168,22 +172,37 @@ func NewCmdJoin(out io.Writer) *cobra.Command {
Short: "Run this on any machine you wish to join an existing cluster", Short: "Run this on any machine you wish to join an existing cluster",
Long: joinLongDescription, Long: joinLongDescription,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
j, err := NewValidJoin(cmd.PersistentFlags(), cfg, args, cfgPath, featureGatesString, ignorePreflightErrors)
if len(fd.KubeConfigPath) != 0 {
cfg.Discovery.File = fd
} else {
cfg.Discovery.BootstrapToken = btd
cfg.Discovery.BootstrapToken.APIServerEndpoints = args
if len(cfg.Discovery.BootstrapToken.Token) == 0 {
cfg.Discovery.BootstrapToken.Token = token
}
}
if len(cfg.Discovery.TLSBootstrapToken) == 0 {
cfg.Discovery.TLSBootstrapToken = token
}
j, err := NewValidJoin(cmd.PersistentFlags(), cfg, cfgPath, featureGatesString, ignorePreflightErrors)
kubeadmutil.CheckErr(err) kubeadmutil.CheckErr(err)
kubeadmutil.CheckErr(j.Run(out)) kubeadmutil.CheckErr(j.Run(out))
}, },
} }
AddJoinConfigFlags(cmd.PersistentFlags(), cfg, &featureGatesString) AddJoinConfigFlags(cmd.PersistentFlags(), cfg, &featureGatesString, &token)
AddJoinBootstrapTokenDiscoveryFlags(cmd.PersistentFlags(), btd)
AddJoinFileDiscoveryFlags(cmd.PersistentFlags(), fd)
AddJoinOtherFlags(cmd.PersistentFlags(), &cfgPath, &ignorePreflightErrors) AddJoinOtherFlags(cmd.PersistentFlags(), &cfgPath, &ignorePreflightErrors)
return cmd return cmd
} }
// NewValidJoin validates the command line that are passed to the cobra command // NewValidJoin validates the command line that are passed to the cobra command
func NewValidJoin(flagSet *flag.FlagSet, cfg *kubeadmapiv1beta1.JoinConfiguration, args []string, cfgPath, featureGatesString string, ignorePreflightErrors []string) (*Join, error) { func NewValidJoin(flagSet *flag.FlagSet, cfg *kubeadmapiv1beta1.JoinConfiguration, cfgPath, featureGatesString string, ignorePreflightErrors []string) (*Join, error) {
cfg.DiscoveryTokenAPIServers = args
var err error var err error
if cfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, featureGatesString); err != nil { if cfg.FeatureGates, err = features.NewFeatureGate(&features.InitFeatureGates, featureGatesString); err != nil {
return nil, err return nil, err
...@@ -198,32 +217,17 @@ func NewValidJoin(flagSet *flag.FlagSet, cfg *kubeadmapiv1beta1.JoinConfiguratio ...@@ -198,32 +217,17 @@ func NewValidJoin(flagSet *flag.FlagSet, cfg *kubeadmapiv1beta1.JoinConfiguratio
return nil, err return nil, err
} }
return NewJoin(cfgPath, args, cfg, ignorePreflightErrorsSet) return NewJoin(cfgPath, cfg, ignorePreflightErrorsSet)
} }
// AddJoinConfigFlags adds join flags bound to the config to the specified flagset // AddJoinConfigFlags adds join flags bound to the config to the specified flagset
func AddJoinConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1beta1.JoinConfiguration, featureGatesString *string) { func AddJoinConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1beta1.JoinConfiguration, featureGatesString *string, token *string) {
flagSet.StringVar(
&cfg.DiscoveryFile, "discovery-file", "",
"A file or url from which to load cluster information.")
flagSet.StringVar(
&cfg.DiscoveryToken, "discovery-token", "",
"A token used to validate cluster information fetched from the api server.")
flagSet.StringVar( flagSet.StringVar(
&cfg.NodeRegistration.Name, "node-name", cfg.NodeRegistration.Name, &cfg.NodeRegistration.Name, "node-name", cfg.NodeRegistration.Name,
"Specify the node name.") "Specify the node name.")
flagSet.StringVar( flagSet.StringVar(
&cfg.TLSBootstrapToken, "tls-bootstrap-token", "", token, "token", "",
"A token used for TLS bootstrapping.") "Use this token for both discovery-token and tls-bootstrap-token when those values are not provided.")
flagSet.StringSliceVar(
&cfg.DiscoveryTokenCACertHashes, "discovery-token-ca-cert-hash", []string{},
"For token-based discovery, validate that the root CA public key matches this hash (format: \"<type>:<value>\").")
flagSet.BoolVar(
&cfg.DiscoveryTokenUnsafeSkipCAVerification, "discovery-token-unsafe-skip-ca-verification", false,
"For token-based discovery, allow joining without --discovery-token-ca-cert-hash pinning.")
flagSet.StringVar(
&cfg.Token, "token", "",
"Use this token for both discovery-token and tls-bootstrap-token.")
flagSet.StringVar( flagSet.StringVar(
featureGatesString, "feature-gates", *featureGatesString, featureGatesString, "feature-gates", *featureGatesString,
"A set of key=value pairs that describe feature gates for various features. "+ "A set of key=value pairs that describe feature gates for various features. "+
...@@ -245,6 +249,26 @@ func AddJoinConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1beta1.JoinConfig ...@@ -245,6 +249,26 @@ func AddJoinConfigFlags(flagSet *flag.FlagSet, cfg *kubeadmapiv1beta1.JoinConfig
) )
} }
// AddJoinBootstrapTokenDiscoveryFlags adds bootstrap token specific discovery flags to the specified flagset
func AddJoinBootstrapTokenDiscoveryFlags(flagSet *flag.FlagSet, btd *kubeadmapiv1beta1.BootstrapTokenDiscovery) {
flagSet.StringVar(
&btd.Token, "discovery-token", "",
"A token used to validate cluster information fetched from the API server.")
flagSet.StringSliceVar(
&btd.CACertHashes, "discovery-token-ca-cert-hash", []string{},
"For token-based discovery, validate that the root CA public key matches this hash (format: \"<type>:<value>\").")
flagSet.BoolVar(
&btd.UnsafeSkipCAVerification, "discovery-token-unsafe-skip-ca-verification", false,
"For token-based discovery, allow joining without --discovery-token-ca-cert-hash pinning.")
}
// AddJoinFileDiscoveryFlags adds file discovery flags to the specified flagset
func AddJoinFileDiscoveryFlags(flagSet *flag.FlagSet, fd *kubeadmapiv1beta1.FileDiscovery) {
flagSet.StringVar(
&fd.KubeConfigPath, "discovery-file", "",
"A file or URL from which to load cluster information.")
}
// AddJoinOtherFlags adds join flags that are not bound to a configuration file to the given flagset // AddJoinOtherFlags adds join flags that are not bound to a configuration file to the given flagset
func AddJoinOtherFlags(flagSet *flag.FlagSet, cfgPath *string, ignorePreflightErrors *[]string) { func AddJoinOtherFlags(flagSet *flag.FlagSet, cfgPath *string, ignorePreflightErrors *[]string) {
flagSet.StringVar( flagSet.StringVar(
...@@ -264,7 +288,7 @@ type Join struct { ...@@ -264,7 +288,7 @@ type Join struct {
} }
// NewJoin instantiates Join struct with given arguments // NewJoin instantiates Join struct with given arguments
func NewJoin(cfgPath string, args []string, defaultcfg *kubeadmapiv1beta1.JoinConfiguration, ignorePreflightErrors sets.String) (*Join, error) { func NewJoin(cfgPath string, defaultcfg *kubeadmapiv1beta1.JoinConfiguration, ignorePreflightErrors sets.String) (*Join, error) {
if defaultcfg.NodeRegistration.Name == "" { if defaultcfg.NodeRegistration.Name == "" {
glog.V(1).Infoln("[join] found NodeName empty; using OS hostname as NodeName") glog.V(1).Infoln("[join] found NodeName empty; using OS hostname as NodeName")
......
...@@ -70,7 +70,6 @@ func TestNewValidJoin(t *testing.T) { ...@@ -70,7 +70,6 @@ func TestNewValidJoin(t *testing.T) {
testCases := []struct { testCases := []struct {
name string name string
args []string
skipPreFlight bool skipPreFlight bool
cfgPath string cfgPath string
configToWrite string configToWrite string
...@@ -163,7 +162,7 @@ func TestNewValidJoin(t *testing.T) { ...@@ -163,7 +162,7 @@ func TestNewValidJoin(t *testing.T) {
} }
} }
join, err := NewValidJoin(cmd.PersistentFlags(), cfg, tc.args, tc.cfgPath, tc.featureGatesString, tc.ignorePreflightErrors) join, err := NewValidJoin(cmd.PersistentFlags(), cfg, tc.cfgPath, tc.featureGatesString, tc.ignorePreflightErrors)
if tc.nodeConfig != nil { if tc.nodeConfig != nil {
join.cfg = tc.nodeConfig join.cfg = tc.nodeConfig
......
...@@ -41,7 +41,7 @@ func For(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) { ...@@ -41,7 +41,7 @@ func For(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) {
return nil, fmt.Errorf("couldn't validate the identity of the API Server: %v", err) return nil, fmt.Errorf("couldn't validate the identity of the API Server: %v", err)
} }
if len(cfg.TLSBootstrapToken) == 0 { if len(cfg.Discovery.TLSBootstrapToken) == 0 {
return config, nil return config, nil
} }
clusterinfo := kubeconfigutil.GetClusterFromKubeConfig(config) clusterinfo := kubeconfigutil.GetClusterFromKubeConfig(config)
...@@ -50,19 +50,20 @@ func For(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) { ...@@ -50,19 +50,20 @@ func For(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) {
cfg.ClusterName, cfg.ClusterName,
TokenUser, TokenUser,
clusterinfo.CertificateAuthorityData, clusterinfo.CertificateAuthorityData,
cfg.TLSBootstrapToken, cfg.Discovery.TLSBootstrapToken,
), nil ), nil
} }
// DiscoverValidatedKubeConfig returns a validated Config object that specifies where the cluster is and the CA cert to trust // DiscoverValidatedKubeConfig returns a validated Config object that specifies where the cluster is and the CA cert to trust
func DiscoverValidatedKubeConfig(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) { func DiscoverValidatedKubeConfig(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) {
switch { switch {
case len(cfg.DiscoveryFile) != 0: case cfg.Discovery.File != nil:
if isHTTPSURL(cfg.DiscoveryFile) { kubeConfigPath := cfg.Discovery.File.KubeConfigPath
return https.RetrieveValidatedConfigInfo(cfg.DiscoveryFile, cfg.ClusterName) if isHTTPSURL(kubeConfigPath) {
return https.RetrieveValidatedConfigInfo(kubeConfigPath, cfg.ClusterName)
} }
return file.RetrieveValidatedConfigInfo(cfg.DiscoveryFile, cfg.ClusterName) return file.RetrieveValidatedConfigInfo(kubeConfigPath, cfg.ClusterName)
case len(cfg.DiscoveryToken) != 0: case cfg.Discovery.BootstrapToken != nil:
return token.RetrieveValidatedConfigInfo(cfg) return token.RetrieveValidatedConfigInfo(cfg)
default: default:
return nil, fmt.Errorf("couldn't find a valid discovery configuration") return nil, fmt.Errorf("couldn't find a valid discovery configuration")
......
...@@ -30,25 +30,31 @@ func TestFor(t *testing.T) { ...@@ -30,25 +30,31 @@ func TestFor(t *testing.T) {
{d: kubeadm.JoinConfiguration{}, expect: false}, {d: kubeadm.JoinConfiguration{}, expect: false},
{ {
d: kubeadm.JoinConfiguration{ d: kubeadm.JoinConfiguration{
DiscoveryFile: "notnil", Discovery: kubeadm.Discovery{
File: &kubeadm.FileDiscovery{
KubeConfigPath: "notnil",
},
},
}, },
expect: false, expect: false,
}, },
{ {
d: kubeadm.JoinConfiguration{ d: kubeadm.JoinConfiguration{
DiscoveryFile: "https://localhost", Discovery: kubeadm.Discovery{
File: &kubeadm.FileDiscovery{
KubeConfigPath: "https://localhost",
},
},
}, },
expect: false, expect: false,
}, },
{ {
d: kubeadm.JoinConfiguration{ d: kubeadm.JoinConfiguration{
DiscoveryFile: "notnil", Discovery: kubeadm.Discovery{
}, BootstrapToken: &kubeadm.BootstrapTokenDiscovery{
expect: false, Token: "foo.bar@foobar",
}, },
{ },
d: kubeadm.JoinConfiguration{
DiscoveryToken: "foo.bar@foobar",
}, },
expect: false, expect: false,
}, },
......
...@@ -44,21 +44,21 @@ const BootstrapUser = "token-bootstrap-client" ...@@ -44,21 +44,21 @@ const BootstrapUser = "token-bootstrap-client"
// It then makes sure it can trust the API Server by looking at the JWS-signed tokens and (if cfg.DiscoveryTokenCACertHashes is not empty) // It then makes sure it can trust the API Server by looking at the JWS-signed tokens and (if cfg.DiscoveryTokenCACertHashes is not empty)
// validating the cluster CA against a set of pinned public keys // validating the cluster CA against a set of pinned public keys
func RetrieveValidatedConfigInfo(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) { func RetrieveValidatedConfigInfo(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) {
token, err := kubeadmapi.NewBootstrapTokenString(cfg.DiscoveryToken) token, err := kubeadmapi.NewBootstrapTokenString(cfg.Discovery.BootstrapToken.Token)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Load the cfg.DiscoveryTokenCACertHashes into a pubkeypin.Set // Load the cfg.DiscoveryTokenCACertHashes into a pubkeypin.Set
pubKeyPins := pubkeypin.NewSet() pubKeyPins := pubkeypin.NewSet()
err = pubKeyPins.Allow(cfg.DiscoveryTokenCACertHashes...) err = pubKeyPins.Allow(cfg.Discovery.BootstrapToken.CACertHashes...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// The function below runs for every endpoint, and all endpoints races with each other. // The function below runs for every endpoint, and all endpoints races with each other.
// The endpoint that wins the race and completes the task first gets its kubeconfig returned below // The endpoint that wins the race and completes the task first gets its kubeconfig returned below
baseKubeConfig, err := runForEndpointsAndReturnFirst(cfg.DiscoveryTokenAPIServers, cfg.DiscoveryTimeout.Duration, func(endpoint string) (*clientcmdapi.Config, error) { baseKubeConfig, err := runForEndpointsAndReturnFirst(cfg.Discovery.BootstrapToken.APIServerEndpoints, cfg.Discovery.Timeout.Duration, func(endpoint string) (*clientcmdapi.Config, error) {
insecureBootstrapConfig := buildInsecureBootstrapKubeConfig(endpoint, cfg.ClusterName) insecureBootstrapConfig := buildInsecureBootstrapKubeConfig(endpoint, cfg.ClusterName)
clusterName := insecureBootstrapConfig.Contexts[insecureBootstrapConfig.CurrentContext].Cluster clusterName := insecureBootstrapConfig.Contexts[insecureBootstrapConfig.CurrentContext].Cluster
......
...@@ -928,16 +928,18 @@ func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.JoinConfigura ...@@ -928,16 +928,18 @@ func RunJoinNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.JoinConfigura
} }
addIPv6Checks := false addIPv6Checks := false
for _, server := range cfg.DiscoveryTokenAPIServers { if cfg.Discovery.BootstrapToken != nil {
ipstr, _, err := net.SplitHostPort(server) for _, server := range cfg.Discovery.BootstrapToken.APIServerEndpoints {
if err == nil { ipstr, _, err := net.SplitHostPort(server)
checks = append(checks, if err == nil {
HTTPProxyCheck{Proto: "https", Host: ipstr}, checks = append(checks,
) HTTPProxyCheck{Proto: "https", Host: ipstr},
if !addIPv6Checks { )
if ip := net.ParseIP(ipstr); ip != nil { if !addIPv6Checks {
if ip.To4() == nil && ip.To16() != nil { if ip := net.ParseIP(ipstr); ip != nil {
addIPv6Checks = true if ip.To4() == nil && ip.To16() != nil {
addIPv6Checks = true
}
} }
} }
} }
......
...@@ -254,13 +254,21 @@ func TestRunJoinNodeChecks(t *testing.T) { ...@@ -254,13 +254,21 @@ func TestRunJoinNodeChecks(t *testing.T) {
}, },
{ {
cfg: &kubeadmapi.JoinConfiguration{ cfg: &kubeadmapi.JoinConfiguration{
DiscoveryTokenAPIServers: []string{"192.168.1.15"}, Discovery: kubeadmapi.Discovery{
BootstrapToken: &kubeadmapi.BootstrapTokenDiscovery{
APIServerEndpoints: []string{"192.168.1.15"},
},
},
}, },
expected: false, expected: false,
}, },
{ {
cfg: &kubeadmapi.JoinConfiguration{ cfg: &kubeadmapi.JoinConfiguration{
DiscoveryTokenAPIServers: []string{"2001:1234::1:15"}, Discovery: kubeadmapi.Discovery{
BootstrapToken: &kubeadmapi.BootstrapTokenDiscovery{
APIServerEndpoints: []string{"2001:1234::1:15"},
},
},
}, },
expected: false, expected: false,
}, },
......
...@@ -4,18 +4,19 @@ APIEndpoint: ...@@ -4,18 +4,19 @@ APIEndpoint:
CACertPath: /etc/kubernetes/pki/ca.crt CACertPath: /etc/kubernetes/pki/ca.crt
ClusterName: kubernetes ClusterName: kubernetes
ControlPlane: false ControlPlane: false
DiscoveryFile: "" Discovery:
DiscoveryTimeout: 5m0s BootstrapToken:
DiscoveryToken: abcdef.0123456789abcdef APIServerEndpoints:
DiscoveryTokenAPIServers: - kube-apiserver:6443
- kube-apiserver:6443 CACertHashes: null
DiscoveryTokenCACertHashes: null Token: abcdef.0123456789abcdef
DiscoveryTokenUnsafeSkipCAVerification: true UnsafeSkipCAVerification: true
File: null
TLSBootstrapToken: abcdef.0123456789abcdef
Timeout: 5m0s
FeatureGates: null FeatureGates: null
NodeRegistration: NodeRegistration:
CRISocket: /var/run/dockershim.sock CRISocket: /var/run/dockershim.sock
KubeletExtraArgs: null KubeletExtraArgs: null
Name: master-1 Name: master-1
Taints: null Taints: null
TLSBootstrapToken: abcdef.0123456789abcdef
Token: abcdef.0123456789abcdef
...@@ -4,15 +4,15 @@ apiEndpoint: ...@@ -4,15 +4,15 @@ apiEndpoint:
apiVersion: kubeadm.k8s.io/v1beta1 apiVersion: kubeadm.k8s.io/v1beta1
caCertPath: /etc/kubernetes/pki/ca.crt caCertPath: /etc/kubernetes/pki/ca.crt
clusterName: kubernetes clusterName: kubernetes
discoveryFile: "" discovery:
discoveryTimeout: 5m0s bootstrapToken:
discoveryToken: abcdef.0123456789abcdef apiServerEndpoints:
discoveryTokenAPIServers: - kube-apiserver:6443
- kube-apiserver:6443 token: abcdef.0123456789abcdef
discoveryTokenUnsafeSkipCAVerification: true unsafeSkipCAVerification: true
timeout: 5m0s
tlsBootstrapToken: abcdef.0123456789abcdef
kind: JoinConfiguration kind: JoinConfiguration
nodeRegistration: nodeRegistration:
criSocket: /var/run/dockershim.sock criSocket: /var/run/dockershim.sock
name: master-1 name: master-1
tlsBootstrapToken: abcdef.0123456789abcdef
token: abcdef.0123456789abcdef
...@@ -4,15 +4,15 @@ apiEndpoint: ...@@ -4,15 +4,15 @@ apiEndpoint:
apiVersion: kubeadm.k8s.io/v1beta1 apiVersion: kubeadm.k8s.io/v1beta1
caCertPath: /etc/kubernetes/pki/ca.crt caCertPath: /etc/kubernetes/pki/ca.crt
clusterName: kubernetes clusterName: kubernetes
discoveryFile: "" discovery:
discoveryTimeout: 5m0s bootstrapToken:
discoveryToken: abcdef.0123456789abcdef apiServerEndpoints:
discoveryTokenAPIServers: - kube-apiserver:6443
- kube-apiserver:6443 token: abcdef.0123456789abcdef
discoveryTokenUnsafeSkipCAVerification: true unsafeSkipCAVerification: true
timeout: 5m0s
tlsBootstrapToken: abcdef.0123456789abcdef
kind: JoinConfiguration kind: JoinConfiguration
nodeRegistration: nodeRegistration:
criSocket: /var/run/dockershim.sock criSocket: /var/run/dockershim.sock
name: thegopher name: thegopher
tlsBootstrapToken: abcdef.0123456789abcdef
token: abcdef.0123456789abcdef
apiVersion: kubeadm.k8s.io/v1beta1 apiVersion: kubeadm.k8s.io/v1beta1
kind: NodeConfiguration kind: NodeConfiguration
apiEndpoint: caCertPath: relativepath
advertiseAddress: INVALID-ADDRESS-!!!! discovery:
bindPort: 6443 timeout: not-a-time
bootstrapToken:
token: invalidtoken
apiServerEndpoints:
- INVALID_URL
unsafeSkipCAVerification: false
file:
kubeConfigPath: relativepath
nodeRegistration:
criSocket: relativepath
name: NODE-1
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