...

Source file src/k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap/bootstrap.go

Documentation: k8s.io/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap

     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  }
   152  

View as plain text