k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/plugin/pkg/auth/authenticator/token/bootstrap/bootstrap.go (about) 1 /* 2 Copyright 2017 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 /* 18 Package bootstrap provides a token authenticator for TLS bootstrap secrets. 19 */ 20 package bootstrap 21 22 import ( 23 "context" 24 "crypto/subtle" 25 "fmt" 26 "time" 27 28 "k8s.io/klog/v2" 29 30 corev1 "k8s.io/api/core/v1" 31 "k8s.io/apimachinery/pkg/api/errors" 32 "k8s.io/apiserver/pkg/authentication/authenticator" 33 "k8s.io/apiserver/pkg/authentication/user" 34 corev1listers "k8s.io/client-go/listers/core/v1" 35 bootstrapapi "k8s.io/cluster-bootstrap/token/api" 36 bootstrapsecretutil "k8s.io/cluster-bootstrap/util/secrets" 37 bootstraptokenutil "k8s.io/cluster-bootstrap/util/tokens" 38 ) 39 40 // TODO: A few methods in this package is copied from other sources. Either 41 // because the existing functionality isn't exported or because it is in a 42 // package that shouldn't be directly imported by this packages. 43 44 // NewTokenAuthenticator initializes a bootstrap token authenticator. 45 // 46 // Lister is expected to be for the "kube-system" namespace. 47 func NewTokenAuthenticator(lister corev1listers.SecretNamespaceLister) *TokenAuthenticator { 48 return &TokenAuthenticator{lister} 49 } 50 51 // TokenAuthenticator authenticates bootstrap tokens from secrets in the API server. 52 type TokenAuthenticator struct { 53 lister corev1listers.SecretNamespaceLister 54 } 55 56 // tokenErrorf prints a error message for a secret that has matched a bearer 57 // token but fails to meet some other criteria. 58 // 59 // tokenErrorf(secret, "has invalid value for key %s", key) 60 func tokenErrorf(s *corev1.Secret, format string, i ...interface{}) { 61 format = fmt.Sprintf("Bootstrap secret %s/%s matching bearer token ", s.Namespace, s.Name) + format 62 klog.V(3).Infof(format, i...) 63 } 64 65 // AuthenticateToken tries to match the provided token to a bootstrap token secret 66 // in a given namespace. If found, it authenticates the token in the 67 // "system:bootstrappers" group and with the "system:bootstrap:(token-id)" username. 68 // 69 // All secrets must be of type "bootstrap.kubernetes.io/token". An example secret: 70 // 71 // apiVersion: v1 72 // kind: Secret 73 // metadata: 74 // # Name MUST be of form "bootstrap-token-( token id )". 75 // name: bootstrap-token-( token id ) 76 // namespace: kube-system 77 // # Only secrets of this type will be evaluated. 78 // type: bootstrap.kubernetes.io/token 79 // data: 80 // token-secret: ( private part of token ) 81 // token-id: ( token id ) 82 // # Required key usage. 83 // usage-bootstrap-authentication: true 84 // auth-extra-groups: "system:bootstrappers:custom-group1,system:bootstrappers:custom-group2" 85 // # May also contain an expiry. 86 // 87 // Tokens are expected to be of the form: 88 // 89 // ( token-id ).( token-secret ) 90 func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) { 91 tokenID, tokenSecret, err := bootstraptokenutil.ParseToken(token) 92 if err != nil { 93 // Token isn't of the correct form, ignore it. 94 return nil, false, nil 95 } 96 97 secretName := bootstrapapi.BootstrapTokenSecretPrefix + tokenID 98 secret, err := t.lister.Get(secretName) 99 if err != nil { 100 if errors.IsNotFound(err) { 101 klog.V(3).Infof("No secret of name %s to match bootstrap bearer token", secretName) 102 return nil, false, nil 103 } 104 return nil, false, err 105 } 106 107 if secret.DeletionTimestamp != nil { 108 tokenErrorf(secret, "is deleted and awaiting removal") 109 return nil, false, nil 110 } 111 112 if string(secret.Type) != string(bootstrapapi.SecretTypeBootstrapToken) || secret.Data == nil { 113 tokenErrorf(secret, "has invalid type, expected %s.", bootstrapapi.SecretTypeBootstrapToken) 114 return nil, false, nil 115 } 116 117 ts := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenSecretKey) 118 if subtle.ConstantTimeCompare([]byte(ts), []byte(tokenSecret)) != 1 { 119 tokenErrorf(secret, "has invalid value for key %s.", bootstrapapi.BootstrapTokenSecretKey) 120 return nil, false, nil 121 } 122 123 id := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenIDKey) 124 if id != tokenID { 125 tokenErrorf(secret, "has invalid value for key %s.", bootstrapapi.BootstrapTokenIDKey) 126 return nil, false, nil 127 } 128 129 if bootstrapsecretutil.HasExpired(secret, time.Now()) { 130 // logging done in isSecretExpired method. 131 return nil, false, nil 132 } 133 134 if bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenUsageAuthentication) != "true" { 135 tokenErrorf(secret, "not marked %s=true.", bootstrapapi.BootstrapTokenUsageAuthentication) 136 return nil, false, nil 137 } 138 139 groups, err := bootstrapsecretutil.GetGroups(secret) 140 if err != nil { 141 tokenErrorf(secret, "has invalid value for key %s: %v.", bootstrapapi.BootstrapTokenExtraGroupsKey, err) 142 return nil, false, nil 143 } 144 145 return &authenticator.Response{ 146 User: &user.DefaultInfo{ 147 Name: bootstrapapi.BootstrapUserPrefix + string(id), 148 Groups: groups, 149 }, 150 }, true, nil 151 }