mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #66482 from dixudx/kubeadm_use_existing_config
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. kubeadm join the cluster with pre-existing client certificate if provided **What this PR does / why we need it**: support `kubeadm join` with a pre-existing client certificate **Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*: Fixes kubernetes/kubeadm#945 **Special notes for your reviewer**: /cc @luxas @timothysc @kubernetes/sig-cluster-lifecycle-pr-reviews **Release note**: ```release-note kubeadm now can join the cluster with pre-existing client certificate if provided ```
This commit is contained in:
commit
f7641e8710
@ -36,11 +36,15 @@ const TokenUser = "tls-bootstrap-token-user"
|
||||
func For(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) {
|
||||
// TODO: Print summary info about the CA certificate, along with the checksum signature
|
||||
// we also need an ability for the user to configure the client to validate received CA cert against a checksum
|
||||
clusterinfo, err := GetValidatedClusterInfoObject(cfg)
|
||||
config, err := DiscoverValidatedKubeConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("couldn't validate the identity of the API Server: %v", err)
|
||||
}
|
||||
|
||||
if len(cfg.TLSBootstrapToken) == 0 {
|
||||
return config, nil
|
||||
}
|
||||
clusterinfo := kubeconfigutil.GetClusterFromKubeConfig(config)
|
||||
return kubeconfigutil.CreateWithToken(
|
||||
clusterinfo.Server,
|
||||
cfg.ClusterName,
|
||||
@ -50,16 +54,16 @@ func For(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) {
|
||||
), nil
|
||||
}
|
||||
|
||||
// GetValidatedClusterInfoObject returns a validated Cluster object that specifies where the cluster is and the CA cert to trust
|
||||
func GetValidatedClusterInfoObject(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Cluster, error) {
|
||||
// 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) {
|
||||
switch {
|
||||
case len(cfg.DiscoveryFile) != 0:
|
||||
if isHTTPSURL(cfg.DiscoveryFile) {
|
||||
return https.RetrieveValidatedClusterInfo(cfg.DiscoveryFile, cfg.ClusterName)
|
||||
return https.RetrieveValidatedConfigInfo(cfg.DiscoveryFile, cfg.ClusterName)
|
||||
}
|
||||
return file.RetrieveValidatedClusterInfo(cfg.DiscoveryFile, cfg.ClusterName)
|
||||
return file.RetrieveValidatedConfigInfo(cfg.DiscoveryFile, cfg.ClusterName)
|
||||
case len(cfg.DiscoveryToken) != 0:
|
||||
return token.RetrieveValidatedClusterInfo(cfg)
|
||||
return token.RetrieveValidatedConfigInfo(cfg)
|
||||
default:
|
||||
return nil, fmt.Errorf("couldn't find a valid discovery configuration")
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"io/ioutil"
|
||||
"k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@ -30,39 +30,73 @@ import (
|
||||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||
)
|
||||
|
||||
// RetrieveValidatedClusterInfo connects to the API Server and makes sure it can talk
|
||||
// RetrieveValidatedConfigInfo connects to the API Server and makes sure it can talk
|
||||
// securely to the API Server using the provided CA cert and
|
||||
// optionally refreshes the cluster-info information from the cluster-info ConfigMap
|
||||
func RetrieveValidatedClusterInfo(filepath, clustername string) (*clientcmdapi.Cluster, error) {
|
||||
clusterinfo, err := clientcmd.LoadFromFile(filepath)
|
||||
func RetrieveValidatedConfigInfo(filepath, clustername string) (*clientcmdapi.Config, error) {
|
||||
config, err := clientcmd.LoadFromFile(filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ValidateClusterInfo(clusterinfo, clustername)
|
||||
return ValidateConfigInfo(config, clustername)
|
||||
}
|
||||
|
||||
// ValidateClusterInfo connects to the API Server and makes sure it can talk
|
||||
// securely to the API Server using the provided CA cert and
|
||||
// ValidateConfigInfo connects to the API Server and makes sure it can talk
|
||||
// securely to the API Server using the provided CA cert/client certificates and
|
||||
// optionally refreshes the cluster-info information from the cluster-info ConfigMap
|
||||
func ValidateClusterInfo(clusterinfo *clientcmdapi.Config, clustername string) (*clientcmdapi.Cluster, error) {
|
||||
err := validateClusterInfoKubeConfig(clusterinfo)
|
||||
func ValidateConfigInfo(config *clientcmdapi.Config, clustername string) (*clientcmdapi.Config, error) {
|
||||
err := validateKubeConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// This is the cluster object we've got from the cluster-info KubeConfig file
|
||||
defaultCluster := kubeconfigutil.GetClusterFromKubeConfig(clusterinfo)
|
||||
defaultCluster := kubeconfigutil.GetClusterFromKubeConfig(config)
|
||||
|
||||
// Create a new kubeconfig object from the given, just copy over the server and the CA cert
|
||||
// We do this in order to not pick up other possible misconfigurations in the clusterinfo file
|
||||
configFromClusterInfo := kubeconfigutil.CreateBasic(
|
||||
kubeconfig := kubeconfigutil.CreateBasic(
|
||||
defaultCluster.Server,
|
||||
clustername,
|
||||
"", // no user provided
|
||||
defaultCluster.CertificateAuthorityData,
|
||||
)
|
||||
// load pre-existing client certificates
|
||||
if config.Contexts[config.CurrentContext] != nil && len(config.AuthInfos) > 0 {
|
||||
user := config.Contexts[config.CurrentContext].AuthInfo
|
||||
authInfo, ok := config.AuthInfos[user]
|
||||
if !ok || authInfo == nil {
|
||||
return nil, fmt.Errorf("empty settings for user %q", user)
|
||||
}
|
||||
if len(authInfo.ClientCertificateData) == 0 && len(authInfo.ClientCertificate) != 0 {
|
||||
clientCert, err := ioutil.ReadFile(authInfo.ClientCertificate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authInfo.ClientCertificateData = clientCert
|
||||
}
|
||||
if len(authInfo.ClientKeyData) == 0 && len(authInfo.ClientKey) != 0 {
|
||||
clientKey, err := ioutil.ReadFile(authInfo.ClientKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authInfo.ClientKeyData = clientKey
|
||||
}
|
||||
|
||||
client, err := kubeconfigutil.ToClientSet(configFromClusterInfo)
|
||||
if len(authInfo.ClientCertificateData) == 0 || len(authInfo.ClientKeyData) == 0 {
|
||||
return nil, fmt.Errorf("couldn't read authentication info from the given kubeconfig file")
|
||||
}
|
||||
kubeconfig = kubeconfigutil.CreateWithCerts(
|
||||
defaultCluster.Server,
|
||||
clustername,
|
||||
"", // no user provided
|
||||
defaultCluster.CertificateAuthorityData,
|
||||
authInfo.ClientKeyData,
|
||||
authInfo.ClientCertificateData,
|
||||
)
|
||||
}
|
||||
|
||||
client, err := kubeconfigutil.ToClientSet(kubeconfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -88,19 +122,19 @@ func ValidateClusterInfo(clusterinfo *clientcmdapi.Config, clustername string) (
|
||||
|
||||
// If we couldn't fetch the cluster-info ConfigMap, just return the cluster-info object the user provided
|
||||
if clusterinfoCM == nil {
|
||||
return defaultCluster, nil
|
||||
return kubeconfig, nil
|
||||
}
|
||||
|
||||
// We somehow got hold of the ConfigMap, try to read some data from it. If we can't, fallback on the user-provided file
|
||||
refreshedBaseKubeConfig, err := tryParseClusterInfoFromConfigMap(clusterinfoCM)
|
||||
if err != nil {
|
||||
fmt.Printf("[discovery] The %s ConfigMap isn't set up properly (%v), but the TLS cert is valid so proceeding...\n", bootstrapapi.ConfigMapClusterInfo, err)
|
||||
return defaultCluster, nil
|
||||
return kubeconfig, nil
|
||||
}
|
||||
|
||||
fmt.Println("[discovery] Synced cluster-info information from the API Server so we have got the latest information")
|
||||
// In an HA world in the future, this will make more sense, because now we've got new information, possibly about new API Servers to talk to
|
||||
return kubeconfigutil.GetClusterFromKubeConfig(refreshedBaseKubeConfig), nil
|
||||
return refreshedBaseKubeConfig, nil
|
||||
}
|
||||
|
||||
// tryParseClusterInfoFromConfigMap tries to parse a kubeconfig file from a ConfigMap key
|
||||
@ -116,14 +150,14 @@ func tryParseClusterInfoFromConfigMap(cm *v1.ConfigMap) (*clientcmdapi.Config, e
|
||||
return parsedKubeConfig, nil
|
||||
}
|
||||
|
||||
// validateClusterInfoKubeConfig makes sure the user-provided cluster-info KubeConfig file is valid
|
||||
func validateClusterInfoKubeConfig(clusterinfo *clientcmdapi.Config) error {
|
||||
if len(clusterinfo.Clusters) < 1 {
|
||||
// validateKubeConfig makes sure the user-provided KubeConfig file is valid
|
||||
func validateKubeConfig(config *clientcmdapi.Config) error {
|
||||
if len(config.Clusters) < 1 {
|
||||
return fmt.Errorf("the provided cluster-info KubeConfig file must have at least one Cluster defined")
|
||||
}
|
||||
defaultCluster := kubeconfigutil.GetClusterFromKubeConfig(clusterinfo)
|
||||
defaultCluster := kubeconfigutil.GetClusterFromKubeConfig(config)
|
||||
if defaultCluster == nil {
|
||||
return fmt.Errorf("the provided cluster-info KubeConfig file must have an unnamed Cluster or a CurrentContext that specifies a non-nil Cluster")
|
||||
}
|
||||
return nil
|
||||
return clientcmd.Validate(*config)
|
||||
}
|
||||
|
@ -26,10 +26,10 @@ import (
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/discovery/file"
|
||||
)
|
||||
|
||||
// RetrieveValidatedClusterInfo connects to the API Server and makes sure it can talk
|
||||
// RetrieveValidatedConfigInfo connects to the API Server and makes sure it can talk
|
||||
// securely to the API Server using the provided CA cert and
|
||||
// optionally refreshes the cluster-info information from the cluster-info ConfigMap
|
||||
func RetrieveValidatedClusterInfo(httpsURL, clustername string) (*clientcmdapi.Cluster, error) {
|
||||
func RetrieveValidatedConfigInfo(httpsURL, clustername string) (*clientcmdapi.Config, error) {
|
||||
client := &http.Client{Transport: netutil.SetOldTransportDefaults(&http.Transport{})}
|
||||
response, err := client.Get(httpsURL)
|
||||
if err != nil {
|
||||
@ -42,9 +42,9 @@ func RetrieveValidatedClusterInfo(httpsURL, clustername string) (*clientcmdapi.C
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clusterinfo, err := clientcmd.Load(kubeconfig)
|
||||
config, err := clientcmd.Load(kubeconfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return file.ValidateClusterInfo(clusterinfo, clustername)
|
||||
return file.ValidateConfigInfo(config, clustername)
|
||||
}
|
||||
|
@ -40,10 +40,10 @@ import (
|
||||
// BootstrapUser defines bootstrap user name
|
||||
const BootstrapUser = "token-bootstrap-client"
|
||||
|
||||
// RetrieveValidatedClusterInfo connects to the API Server and tries to fetch the cluster-info ConfigMap
|
||||
// RetrieveValidatedConfigInfo connects to the API Server and tries to fetch the cluster-info ConfigMap
|
||||
// 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
|
||||
func RetrieveValidatedClusterInfo(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Cluster, error) {
|
||||
func RetrieveValidatedConfigInfo(cfg *kubeadmapi.JoinConfiguration) (*clientcmdapi.Config, error) {
|
||||
token, err := kubeadmapi.NewBootstrapTokenString(cfg.DiscoveryToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -163,7 +163,7 @@ func RetrieveValidatedClusterInfo(cfg *kubeadmapi.JoinConfiguration) (*clientcmd
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kubeconfigutil.GetClusterFromKubeConfig(baseKubeConfig), nil
|
||||
return baseKubeConfig, nil
|
||||
}
|
||||
|
||||
// buildInsecureBootstrapKubeConfig makes a KubeConfig object that connects insecurely to the API Server for bootstrapping purposes
|
||||
|
@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
// CreateBasic creates a basic, general KubeConfig object that then can be extended
|
||||
func CreateBasic(serverURL string, clusterName string, userName string, caCert []byte) *clientcmdapi.Config {
|
||||
func CreateBasic(serverURL, clusterName, userName string, caCert []byte) *clientcmdapi.Config {
|
||||
// Use the cluster and the username as the context name
|
||||
contextName := fmt.Sprintf("%s@%s", userName, clusterName)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user