mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-25 04:33:26 +00:00
kubeadm: add --print-join-command
flag for token create.
This change adds a new flag `kubeadm token create --print-join-command`. When this flag is passed, kubeadm prints the full `kubeadm join [...]` command, including the CA certificate hash which is otherwise annoying to calculate. Example: ``` $ kubeadm token create --print-join-command kubeadm join --token 447067.20b55955bd6abe6c 192.168.99.100:8443 --discovery-token-ca-cert-hash sha256:17023a5c90b996e50c514e63e161e46f78be216fd48c0c3df3be67e008b28889 ```
This commit is contained in:
parent
277d866111
commit
b4b275d255
@ -74,6 +74,7 @@ go_library(
|
||||
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||
"//vendor/k8s.io/utils/exec:go_default_library",
|
||||
],
|
||||
|
@ -17,12 +17,15 @@ limitations under the License.
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/renstrom/dedent"
|
||||
@ -33,6 +36,8 @@ import (
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcertutil "k8s.io/client-go/util/cert"
|
||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||
kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants"
|
||||
@ -40,12 +45,17 @@ import (
|
||||
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient"
|
||||
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
|
||||
"k8s.io/kubernetes/cmd/kubeadm/app/util/pubkeypin"
|
||||
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
)
|
||||
|
||||
var joinCommandTemplate = template.Must(template.New("join").Parse(`` +
|
||||
`kubeadm join --token {{.Token}} {{.MasterHostPort}}{{range $h := .CAPubKeyPins}} --discovery-token-ca-cert-hash {{$h}}{{end}}`,
|
||||
))
|
||||
|
||||
// NewCmdToken returns cobra.Command for token management
|
||||
func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
||||
var kubeConfigFile string
|
||||
@ -89,6 +99,7 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
||||
var extraGroups []string
|
||||
var tokenDuration time.Duration
|
||||
var description string
|
||||
var printJoinCommand bool
|
||||
createCmd := &cobra.Command{
|
||||
Use: "create [token]",
|
||||
Short: "Create bootstrap tokens on the server.",
|
||||
@ -108,7 +119,7 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
||||
client, err := getClientset(kubeConfigFile, dryRun)
|
||||
kubeadmutil.CheckErr(err)
|
||||
|
||||
err = RunCreateToken(out, client, token, tokenDuration, usages, extraGroups, description)
|
||||
err = RunCreateToken(out, client, token, tokenDuration, usages, extraGroups, description, printJoinCommand, kubeConfigFile)
|
||||
kubeadmutil.CheckErr(err)
|
||||
},
|
||||
}
|
||||
@ -121,6 +132,8 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
||||
fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q.", bootstrapapi.BootstrapGroupPattern))
|
||||
createCmd.Flags().StringVar(&description,
|
||||
"description", "", "A human friendly description of how this token is used.")
|
||||
createCmd.Flags().BoolVar(&printJoinCommand,
|
||||
"print-join-command", false, "Instead of printing only the token, print the full 'kubeadm join' flag needed to join the cluster using the token.")
|
||||
tokenCmd.AddCommand(createCmd)
|
||||
|
||||
tokenCmd.AddCommand(NewCmdTokenGenerate(out))
|
||||
@ -190,7 +203,7 @@ func NewCmdTokenGenerate(out io.Writer) *cobra.Command {
|
||||
}
|
||||
|
||||
// RunCreateToken generates a new bootstrap token and stores it as a secret on the server.
|
||||
func RunCreateToken(out io.Writer, client clientset.Interface, token string, tokenDuration time.Duration, usages []string, extraGroups []string, description string) error {
|
||||
func RunCreateToken(out io.Writer, client clientset.Interface, token string, tokenDuration time.Duration, usages []string, extraGroups []string, description string, printJoinCommand bool, kubeConfigFile string) error {
|
||||
if len(token) == 0 {
|
||||
var err error
|
||||
token, err = tokenutil.GenerateToken()
|
||||
@ -228,7 +241,18 @@ func RunCreateToken(out io.Writer, client clientset.Interface, token string, tok
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, token)
|
||||
// if --print-join-command was specified, print the full `kubeadm join` command
|
||||
// otherwise, just print the token
|
||||
if printJoinCommand {
|
||||
joinCommand, err := getJoinCommand(token, kubeConfigFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get join command: %v", err)
|
||||
}
|
||||
fmt.Fprintln(out, joinCommand)
|
||||
} else {
|
||||
fmt.Fprintln(out, token)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -369,3 +393,52 @@ func getClientset(file string, dryRun bool) (clientset.Interface, error) {
|
||||
client, err := kubeconfigutil.ClientSetFromFile(file)
|
||||
return client, err
|
||||
}
|
||||
|
||||
func getJoinCommand(token string, kubeConfigFile string) (string, error) {
|
||||
// load the kubeconfig file to get the CA certificate and endpoint
|
||||
config, err := clientcmd.LoadFromFile(kubeConfigFile)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to load kubeconfig: %v", err)
|
||||
}
|
||||
|
||||
// load the default cluster config
|
||||
clusterConfig := kubeconfigutil.GetClusterFromKubeConfig(config)
|
||||
if clusterConfig == nil {
|
||||
return "", fmt.Errorf("failed to get default cluster config")
|
||||
}
|
||||
|
||||
// load CA certificates from the kubeconfig (either from PEM data or by file path)
|
||||
var caCerts []*x509.Certificate
|
||||
if clusterConfig.CertificateAuthorityData != nil {
|
||||
caCerts, err = clientcertutil.ParseCertsPEM(clusterConfig.CertificateAuthorityData)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse CA certificate from kubeconfig: %v", err)
|
||||
}
|
||||
} else if clusterConfig.CertificateAuthority != "" {
|
||||
caCerts, err = clientcertutil.CertsFromFile(clusterConfig.CertificateAuthority)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to load CA certificate referenced by kubeconfig: %v", err)
|
||||
}
|
||||
} else {
|
||||
return "", fmt.Errorf("no CA certificates found in kubeconfig")
|
||||
}
|
||||
|
||||
// hash all the CA certs and include their public key pins as trusted values
|
||||
publicKeyPins := make([]string, 0, len(caCerts))
|
||||
for _, caCert := range caCerts {
|
||||
publicKeyPins = append(publicKeyPins, pubkeypin.Hash(caCert))
|
||||
}
|
||||
|
||||
ctx := map[string]interface{}{
|
||||
"Token": token,
|
||||
"CAPubKeyPins": publicKeyPins,
|
||||
"MasterHostPort": strings.Replace(clusterConfig.Server, "https://", "", -1),
|
||||
}
|
||||
|
||||
var out bytes.Buffer
|
||||
err = joinCommandTemplate.Execute(&out, ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to render join command template: %v", err)
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user