Skip to content

Commit

Permalink
Add --groups flag for kubeadm token create.
Browse files Browse the repository at this point in the history
This adds support for creating a bootstrap token that authenticates with extra `system:bootstrappers:*` groups in addition to `system:bootstrappers`.
  • Loading branch information
mattmoyer committed Aug 22, 2017
1 parent cfde86d commit e179db9
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 11 deletions.
2 changes: 1 addition & 1 deletion cmd/kubeadm/app/cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ func (i *Init) Run(out io.Writer) error {

// Create the default node bootstrap token
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
}
// Create RBAC rules that makes the bootstrap tokens able to post CSRs
Expand Down
16 changes: 13 additions & 3 deletions cmd/kubeadm/app/cmd/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
"dry-run", dryRun, "Whether to enable dry-run mode or not")

var usages []string
var extraGroups []string
var tokenDuration time.Duration
var description string
createCmd := &cobra.Command{
Expand Down Expand Up @@ -120,14 +121,17 @@ 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)")
}

err = RunCreateToken(out, client, token, tokenDuration, usages, description)
err = RunCreateToken(out, client, token, tokenDuration, usages, extraGroups, description)
kubeadmutil.CheckErr(err)
},
}
createCmd.Flags().DurationVar(&tokenDuration,
"ttl", kubeadmconstants.DefaultTokenDuration, "The duration before the token is automatically deleted (e.g. 1s, 2m, 3h). 0 means 'never expires'.")
createCmd.Flags().StringSliceVar(&usages,
"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,
"description", "", "A human friendly description of how this token is used.")
tokenCmd.AddCommand(createCmd)
Expand Down Expand Up @@ -198,7 +202,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, 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 {
var err error
Expand All @@ -213,8 +217,14 @@ func RunCreateToken(out io.Writer, client clientset.Interface, token string, tok
}
}

for _, group := range extraGroups {
if err := bootstrapapi.ValidateBootstrapGroupName(group); err != nil {
return err
}
}

// 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 {
return err
}
Expand Down
17 changes: 11 additions & 6 deletions cmd/kubeadm/app/phases/bootstraptoken/node/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package node

import (
"fmt"
"strings"
"time"

"k8s.io/api/core/v1"
Expand All @@ -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

// 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 {
return UpdateOrCreateToken(client, token, true, tokenDuration, usages, description)
func CreateNewToken(client clientset.Interface, token string, tokenDuration time.Duration, usages []string, extraGroups []string, description string) error {
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.
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)
if err != nil {
return err
Expand All @@ -52,7 +53,7 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists
return fmt.Errorf("a token with id %q already exists", tokenID)
}
// 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 {
return nil
}
Expand All @@ -67,7 +68,7 @@ func UpdateOrCreateToken(client clientset.Interface, token string, failIfExists
Name: secretName,
},
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 {
return nil
Expand All @@ -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
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{
bootstrapapi.BootstrapTokenIDKey: []byte(tokenID),
bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret),
}

if len(extraGroups) > 0 {
data[bootstrapapi.BootstrapTokenExtraGroupsKey] = []byte(strings.Join(extraGroups, ","))
}

if duration > 0 {
// Get the current time, add the specified duration, and format it accordingly
durationString := time.Now().Add(duration).Format(time.RFC3339)
Expand Down
2 changes: 1 addition & 1 deletion cmd/kubeadm/app/phases/bootstraptoken/node/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func TestEncodeTokenSecretData(t *testing.T) {
{token: &kubeadmapi.TokenDiscovery{ID: "foo", Secret: "bar"}, t: time.Second}, // should use default
}
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)) {
t.Errorf(
"failed EncodeTokenSecretData:\n\texpected: %s\n\t actual: %s",
Expand Down

0 comments on commit e179db9

Please sign in to comment.