mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 10:43:56 +00:00
kubeadm: add --groups
flag for kubeadm token create
.
This adds support for creating a bootstrap token that authenticates with extra `system:bootstrappers:*` groups in addition to `system:bootstrappers`.
This commit is contained in:
parent
fd5c00b38d
commit
77f1b72a40
@ -66,6 +66,7 @@ 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/sets: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",
|
||||||
|
@ -339,7 +339,7 @@ func (i *Init) Run(out io.Writer) error {
|
|||||||
|
|
||||||
// Create the default node bootstrap token
|
// Create the default node bootstrap token
|
||||||
tokenDescription := "The default bootstrap token generated by 'kubeadm init'."
|
tokenDescription := "The default bootstrap token generated by 'kubeadm init'."
|
||||||
if err := nodebootstraptokenphase.UpdateOrCreateToken(client, i.cfg.Token, false, i.cfg.TokenTTL, kubeadmconstants.DefaultTokenUsages, tokenDescription); err != nil {
|
if err := nodebootstraptokenphase.UpdateOrCreateToken(client, i.cfg.Token, false, i.cfg.TokenTTL, kubeadmconstants.DefaultTokenUsages, []string{}, tokenDescription); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Create RBAC rules that makes the bootstrap tokens able to post CSRs
|
// Create RBAC rules that makes the bootstrap tokens able to post CSRs
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
clientset "k8s.io/client-go/kubernetes"
|
clientset "k8s.io/client-go/kubernetes"
|
||||||
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
|
||||||
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util"
|
||||||
@ -87,6 +88,7 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
|||||||
"dry-run", dryRun, "Whether to enable dry-run mode or not")
|
"dry-run", dryRun, "Whether to enable dry-run mode or not")
|
||||||
|
|
||||||
var usages []string
|
var usages []string
|
||||||
|
var extraGroups []string
|
||||||
var tokenDuration time.Duration
|
var tokenDuration time.Duration
|
||||||
var description string
|
var description string
|
||||||
createCmd := &cobra.Command{
|
createCmd := &cobra.Command{
|
||||||
@ -114,7 +116,7 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
|||||||
fmt.Fprintln(errW, "[kubeadm] WARNING: starting in 1.8, tokens expire after 24 hours by default (if you require a non-expiring token use --ttl 0)")
|
fmt.Fprintln(errW, "[kubeadm] WARNING: starting in 1.8, tokens expire after 24 hours by default (if you require a non-expiring token use --ttl 0)")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = RunCreateToken(out, client, token, tokenDuration, usages, description)
|
err = RunCreateToken(out, client, token, tokenDuration, usages, extraGroups, description)
|
||||||
kubeadmutil.CheckErr(err)
|
kubeadmutil.CheckErr(err)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -122,6 +124,9 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
|
|||||||
"ttl", kubeadmconstants.DefaultTokenDuration, "The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). 0 means 'never expires'.")
|
"ttl", kubeadmconstants.DefaultTokenDuration, "The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). 0 means 'never expires'.")
|
||||||
createCmd.Flags().StringSliceVar(&usages,
|
createCmd.Flags().StringSliceVar(&usages,
|
||||||
"usages", kubeadmconstants.DefaultTokenUsages, "The ways in which this token can be used. Valid options: [signing,authentication].")
|
"usages", kubeadmconstants.DefaultTokenUsages, "The ways in which this token can be used. Valid options: [signing,authentication].")
|
||||||
|
createCmd.Flags().StringSliceVar(&extraGroups,
|
||||||
|
"groups", []string{},
|
||||||
|
fmt.Sprintf("Extra groups that this token will authenticate as when used for authentication. Must match %q.", bootstrapapi.BootstrapGroupPattern))
|
||||||
createCmd.Flags().StringVar(&description,
|
createCmd.Flags().StringVar(&description,
|
||||||
"description", "", "A human friendly description of how this token is used.")
|
"description", "", "A human friendly description of how this token is used.")
|
||||||
tokenCmd.AddCommand(createCmd)
|
tokenCmd.AddCommand(createCmd)
|
||||||
@ -192,7 +197,7 @@ func NewCmdTokenGenerate(out io.Writer) *cobra.Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RunCreateToken generates a new bootstrap token and stores it as a secret on the server.
|
// 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, description string) error {
|
func RunCreateToken(out io.Writer, client clientset.Interface, token string, tokenDuration time.Duration, usages []string, extraGroups []string, description string) error {
|
||||||
|
|
||||||
if len(token) == 0 {
|
if len(token) == 0 {
|
||||||
var err error
|
var err error
|
||||||
@ -207,8 +212,22 @@ func RunCreateToken(out io.Writer, client clientset.Interface, token string, tok
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// adding groups only makes sense for authentication
|
||||||
|
var usagesSet sets.String
|
||||||
|
usagesSet.Insert(usages...)
|
||||||
|
if len(extraGroups) > 0 && !usagesSet.Has("authentication") {
|
||||||
|
return fmt.Errorf("--groups cannot be specified unless --usages includes \"authentication\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate any extra group names
|
||||||
|
for _, group := range extraGroups {
|
||||||
|
if err := bootstrapapi.ValidateBootstrapGroupName(group); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Validate usages here so we don't allow something unsupported
|
// TODO: Validate usages here so we don't allow something unsupported
|
||||||
err := tokenphase.CreateNewToken(client, token, tokenDuration, usages, description)
|
err := tokenphase.CreateNewToken(client, token, tokenDuration, usages, extraGroups, description)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ package node
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/api/core/v1"
|
"k8s.io/api/core/v1"
|
||||||
@ -33,12 +34,12 @@ const tokenCreateRetries = 5
|
|||||||
// TODO(mattmoyer): Move CreateNewToken, UpdateOrCreateToken and encodeTokenSecretData out of this package to client-go for a generic abstraction and client for a Bootstrap Token
|
// TODO(mattmoyer): Move CreateNewToken, UpdateOrCreateToken and encodeTokenSecretData out of this package to client-go for a generic abstraction and client for a Bootstrap Token
|
||||||
|
|
||||||
// CreateNewToken tries to create a token and fails if one with the same ID already exists
|
// CreateNewToken tries to create a token and fails if one with the same ID already exists
|
||||||
func CreateNewToken(client clientset.Interface, token string, tokenDuration time.Duration, usages []string, description string) error {
|
func CreateNewToken(client clientset.Interface, token string, tokenDuration time.Duration, usages []string, extraGroups []string, description string) error {
|
||||||
return UpdateOrCreateToken(client, token, true, tokenDuration, usages, description)
|
return UpdateOrCreateToken(client, token, true, tokenDuration, usages, extraGroups, description)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOrCreateToken attempts to update a token with the given ID, or create if it does not already exist.
|
// UpdateOrCreateToken attempts to update a token with the given ID, or create if it does not already exist.
|
||||||
func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists bool, tokenDuration time.Duration, usages []string, description string) error {
|
func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists bool, tokenDuration time.Duration, usages []string, extraGroups []string, description string) error {
|
||||||
tokenID, tokenSecret, err := tokenutil.ParseToken(token)
|
tokenID, tokenSecret, err := tokenutil.ParseToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -52,7 +53,7 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists
|
|||||||
return fmt.Errorf("a token with id %q already exists", tokenID)
|
return fmt.Errorf("a token with id %q already exists", tokenID)
|
||||||
}
|
}
|
||||||
// Secret with this ID already exists, update it:
|
// Secret with this ID already exists, update it:
|
||||||
secret.Data = encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, description)
|
secret.Data = encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description)
|
||||||
if _, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Update(secret); err == nil {
|
if _, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Update(secret); err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -67,7 +68,7 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists
|
|||||||
Name: secretName,
|
Name: secretName,
|
||||||
},
|
},
|
||||||
Type: v1.SecretType(bootstrapapi.SecretTypeBootstrapToken),
|
Type: v1.SecretType(bootstrapapi.SecretTypeBootstrapToken),
|
||||||
Data: encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, description),
|
Data: encodeTokenSecretData(tokenID, tokenSecret, tokenDuration, usages, extraGroups, description),
|
||||||
}
|
}
|
||||||
if _, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Create(secret); err == nil {
|
if _, err := client.CoreV1().Secrets(metav1.NamespaceSystem).Create(secret); err == nil {
|
||||||
return nil
|
return nil
|
||||||
@ -85,12 +86,16 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists
|
|||||||
}
|
}
|
||||||
|
|
||||||
// encodeTokenSecretData takes the token discovery object and an optional duration and returns the .Data for the Secret
|
// encodeTokenSecretData takes the token discovery object and an optional duration and returns the .Data for the Secret
|
||||||
func encodeTokenSecretData(tokenID, tokenSecret string, duration time.Duration, usages []string, description string) map[string][]byte {
|
func encodeTokenSecretData(tokenID, tokenSecret string, duration time.Duration, usages []string, extraGroups []string, description string) map[string][]byte {
|
||||||
data := map[string][]byte{
|
data := map[string][]byte{
|
||||||
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
|
||||||
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(extraGroups) > 0 {
|
||||||
|
data[bootstrapapi.BootstrapTokenExtraGroupsKey] = []byte(strings.Join(extraGroups, ","))
|
||||||
|
}
|
||||||
|
|
||||||
if duration > 0 {
|
if duration > 0 {
|
||||||
// Get the current time, add the specified duration, and format it accordingly
|
// Get the current time, add the specified duration, and format it accordingly
|
||||||
durationString := time.Now().Add(duration).Format(time.RFC3339)
|
durationString := time.Now().Add(duration).Format(time.RFC3339)
|
||||||
|
@ -33,7 +33,7 @@ func TestEncodeTokenSecretData(t *testing.T) {
|
|||||||
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, t: time.Second}, // should use default
|
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, t: time.Second}, // should use default
|
||||||
}
|
}
|
||||||
for _, rt := range tests {
|
for _, rt := range tests {
|
||||||
actual := encodeTokenSecretData(rt.token.ID, rt.token.Secret, rt.t, []string{}, "")
|
actual := encodeTokenSecretData(rt.token.ID, rt.token.Secret, rt.t, []string{}, []string{}, "")
|
||||||
if !bytes.Equal(actual["token-id"], []byte(rt.token.ID)) {
|
if !bytes.Equal(actual["token-id"], []byte(rt.token.ID)) {
|
||||||
t.Errorf(
|
t.Errorf(
|
||||||
"failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s",
|
"failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s",
|
||||||
|
Loading…
Reference in New Issue
Block a user