sigs.k8s.io/cluster-api@v1.7.1/bootstrap/kubeadm/internal/controllers/token.go (about) 1 /* 2 Copyright 2019 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package controllers 18 19 import ( 20 "context" 21 "time" 22 23 "github.com/pkg/errors" 24 corev1 "k8s.io/api/core/v1" 25 apierrors "k8s.io/apimachinery/pkg/api/errors" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 bootstrapapi "k8s.io/cluster-bootstrap/token/api" 28 bootstraputil "k8s.io/cluster-bootstrap/token/util" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 ) 31 32 // createToken attempts to create a token with the given ID. 33 func createToken(ctx context.Context, c client.Client, ttl time.Duration) (string, error) { 34 token, err := bootstraputil.GenerateBootstrapToken() 35 if err != nil { 36 return "", errors.Wrap(err, "unable to generate bootstrap token") 37 } 38 39 substrs := bootstraputil.BootstrapTokenRegexp.FindStringSubmatch(token) 40 if len(substrs) != 3 { 41 return "", errors.Errorf("the bootstrap token %q was not of the form %q", token, bootstrapapi.BootstrapTokenPattern) 42 } 43 tokenID := substrs[1] 44 tokenSecret := substrs[2] 45 46 secretName := bootstraputil.BootstrapTokenSecretName(tokenID) 47 secretToken := &corev1.Secret{ 48 ObjectMeta: metav1.ObjectMeta{ 49 Name: secretName, 50 Namespace: metav1.NamespaceSystem, 51 }, 52 Type: bootstrapapi.SecretTypeBootstrapToken, 53 Data: map[string][]byte{ 54 bootstrapapi.BootstrapTokenIDKey: []byte(tokenID), 55 bootstrapapi.BootstrapTokenSecretKey: []byte(tokenSecret), 56 bootstrapapi.BootstrapTokenExpirationKey: []byte(time.Now().UTC().Add(ttl).Format(time.RFC3339)), 57 bootstrapapi.BootstrapTokenUsageSigningKey: []byte("true"), 58 bootstrapapi.BootstrapTokenUsageAuthentication: []byte("true"), 59 bootstrapapi.BootstrapTokenExtraGroupsKey: []byte("system:bootstrappers:kubeadm:default-node-token"), 60 bootstrapapi.BootstrapTokenDescriptionKey: []byte("token generated by cluster-api-bootstrap-provider-kubeadm"), 61 }, 62 } 63 64 if err := c.Create(ctx, secretToken); err != nil { 65 return "", err 66 } 67 return token, nil 68 } 69 70 // getToken fetches the token Secret and returns an error if it is invalid. 71 func getToken(ctx context.Context, c client.Client, token string) (*corev1.Secret, error) { 72 substrs := bootstraputil.BootstrapTokenRegexp.FindStringSubmatch(token) 73 if len(substrs) != 3 { 74 return nil, errors.Errorf("the bootstrap token %q was not of the form %q", token, bootstrapapi.BootstrapTokenPattern) 75 } 76 tokenID := substrs[1] 77 78 secretName := bootstraputil.BootstrapTokenSecretName(tokenID) 79 secret := &corev1.Secret{} 80 if err := c.Get(ctx, client.ObjectKey{Name: secretName, Namespace: metav1.NamespaceSystem}, secret); err != nil { 81 return secret, err 82 } 83 84 if secret.Data == nil { 85 return nil, errors.Errorf("Invalid bootstrap secret %q, remove the token from the kubeadm config to re-create", secretName) 86 } 87 return secret, nil 88 } 89 90 // shouldRotate returns true if an existing token is past half of its TTL and should to be rotated. 91 func shouldRotate(ctx context.Context, c client.Client, token string, ttl time.Duration) (bool, error) { 92 secret, err := getToken(ctx, c, token) 93 if err != nil { 94 // If the secret is deleted before due to unknown reasons, machine pools cannot be scaled up. 95 // Since that, secret should be rotated if missing. 96 // Normally, it is not expected to reach this line. 97 if apierrors.IsNotFound(err) { 98 return true, nil 99 } 100 return false, err 101 } 102 103 expiration, err := time.Parse(time.RFC3339, string(secret.Data[bootstrapapi.BootstrapTokenExpirationKey])) 104 if err != nil { 105 return false, err 106 } 107 return expiration.Before(time.Now().UTC().Add(ttl / 2)), nil 108 }