bootstrap.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. Copyright 2017 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. /*
  14. Package bootstrap provides a token authenticator for TLS bootstrap secrets.
  15. */
  16. package bootstrap
  17. import (
  18. "context"
  19. "crypto/subtle"
  20. "fmt"
  21. "time"
  22. "k8s.io/klog"
  23. corev1 "k8s.io/api/core/v1"
  24. "k8s.io/apimachinery/pkg/api/errors"
  25. "k8s.io/apiserver/pkg/authentication/authenticator"
  26. "k8s.io/apiserver/pkg/authentication/user"
  27. corev1listers "k8s.io/client-go/listers/core/v1"
  28. bootstrapapi "k8s.io/cluster-bootstrap/token/api"
  29. bootstrapsecretutil "k8s.io/cluster-bootstrap/util/secrets"
  30. bootstraptokenutil "k8s.io/cluster-bootstrap/util/tokens"
  31. )
  32. // TODO: A few methods in this package is copied from other sources. Either
  33. // because the existing functionality isn't exported or because it is in a
  34. // package that shouldn't be directly imported by this packages.
  35. // NewTokenAuthenticator initializes a bootstrap token authenticator.
  36. //
  37. // Lister is expected to be for the "kube-system" namespace.
  38. func NewTokenAuthenticator(lister corev1listers.SecretNamespaceLister) *TokenAuthenticator {
  39. return &TokenAuthenticator{lister}
  40. }
  41. // TokenAuthenticator authenticates bootstrap tokens from secrets in the API server.
  42. type TokenAuthenticator struct {
  43. lister corev1listers.SecretNamespaceLister
  44. }
  45. // tokenErrorf prints a error message for a secret that has matched a bearer
  46. // token but fails to meet some other criteria.
  47. //
  48. // tokenErrorf(secret, "has invalid value for key %s", key)
  49. //
  50. func tokenErrorf(s *corev1.Secret, format string, i ...interface{}) {
  51. format = fmt.Sprintf("Bootstrap secret %s/%s matching bearer token ", s.Namespace, s.Name) + format
  52. klog.V(3).Infof(format, i...)
  53. }
  54. // AuthenticateToken tries to match the provided token to a bootstrap token secret
  55. // in a given namespace. If found, it authenticates the token in the
  56. // "system:bootstrappers" group and with the "system:bootstrap:(token-id)" username.
  57. //
  58. // All secrets must be of type "bootstrap.kubernetes.io/token". An example secret:
  59. //
  60. // apiVersion: v1
  61. // kind: Secret
  62. // metadata:
  63. // # Name MUST be of form "bootstrap-token-( token id )".
  64. // name: bootstrap-token-( token id )
  65. // namespace: kube-system
  66. // # Only secrets of this type will be evaluated.
  67. // type: bootstrap.kubernetes.io/token
  68. // data:
  69. // token-secret: ( private part of token )
  70. // token-id: ( token id )
  71. // # Required key usage.
  72. // usage-bootstrap-authentication: true
  73. // auth-extra-groups: "system:bootstrappers:custom-group1,system:bootstrappers:custom-group2"
  74. // # May also contain an expiry.
  75. //
  76. // Tokens are expected to be of the form:
  77. //
  78. // ( token-id ).( token-secret )
  79. //
  80. func (t *TokenAuthenticator) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
  81. tokenID, tokenSecret, err := bootstraptokenutil.ParseToken(token)
  82. if err != nil {
  83. // Token isn't of the correct form, ignore it.
  84. return nil, false, nil
  85. }
  86. secretName := bootstrapapi.BootstrapTokenSecretPrefix + tokenID
  87. secret, err := t.lister.Get(secretName)
  88. if err != nil {
  89. if errors.IsNotFound(err) {
  90. klog.V(3).Infof("No secret of name %s to match bootstrap bearer token", secretName)
  91. return nil, false, nil
  92. }
  93. return nil, false, err
  94. }
  95. if secret.DeletionTimestamp != nil {
  96. tokenErrorf(secret, "is deleted and awaiting removal")
  97. return nil, false, nil
  98. }
  99. if string(secret.Type) != string(bootstrapapi.SecretTypeBootstrapToken) || secret.Data == nil {
  100. tokenErrorf(secret, "has invalid type, expected %s.", bootstrapapi.SecretTypeBootstrapToken)
  101. return nil, false, nil
  102. }
  103. ts := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenSecretKey)
  104. if subtle.ConstantTimeCompare([]byte(ts), []byte(tokenSecret)) != 1 {
  105. tokenErrorf(secret, "has invalid value for key %s, expected %s.", bootstrapapi.BootstrapTokenSecretKey, tokenSecret)
  106. return nil, false, nil
  107. }
  108. id := bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenIDKey)
  109. if id != tokenID {
  110. tokenErrorf(secret, "has invalid value for key %s, expected %s.", bootstrapapi.BootstrapTokenIDKey, tokenID)
  111. return nil, false, nil
  112. }
  113. if bootstrapsecretutil.HasExpired(secret, time.Now()) {
  114. // logging done in isSecretExpired method.
  115. return nil, false, nil
  116. }
  117. if bootstrapsecretutil.GetData(secret, bootstrapapi.BootstrapTokenUsageAuthentication) != "true" {
  118. tokenErrorf(secret, "not marked %s=true.", bootstrapapi.BootstrapTokenUsageAuthentication)
  119. return nil, false, nil
  120. }
  121. groups, err := bootstrapsecretutil.GetGroups(secret)
  122. if err != nil {
  123. tokenErrorf(secret, "has invalid value for key %s: %v.", bootstrapapi.BootstrapTokenExtraGroupsKey, err)
  124. return nil, false, nil
  125. }
  126. return &authenticator.Response{
  127. User: &user.DefaultInfo{
  128. Name: bootstrapapi.BootstrapUserPrefix + string(id),
  129. Groups: groups,
  130. },
  131. }, true, nil
  132. }