diff options
Diffstat (limited to 'vendor/k8s.io/kubernetes/pkg/serviceaccount/legacy.go')
-rw-r--r-- | vendor/k8s.io/kubernetes/pkg/serviceaccount/legacy.go | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/vendor/k8s.io/kubernetes/pkg/serviceaccount/legacy.go b/vendor/k8s.io/kubernetes/pkg/serviceaccount/legacy.go new file mode 100644 index 000000000..5055db7cb --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/serviceaccount/legacy.go @@ -0,0 +1,135 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package serviceaccount + +import ( + "bytes" + "errors" + "fmt" + + "k8s.io/api/core/v1" + apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount" + + "github.com/golang/glog" + "gopkg.in/square/go-jose.v2/jwt" +) + +func LegacyClaims(serviceAccount v1.ServiceAccount, secret v1.Secret) (*jwt.Claims, interface{}) { + return &jwt.Claims{ + Subject: apiserverserviceaccount.MakeUsername(serviceAccount.Namespace, serviceAccount.Name), + }, &legacyPrivateClaims{ + Namespace: serviceAccount.Namespace, + ServiceAccountName: serviceAccount.Name, + ServiceAccountUID: string(serviceAccount.UID), + SecretName: secret.Name, + } +} + +const LegacyIssuer = "kubernetes/serviceaccount" + +type legacyPrivateClaims struct { + ServiceAccountName string `json:"kubernetes.io/serviceaccount/service-account.name"` + ServiceAccountUID string `json:"kubernetes.io/serviceaccount/service-account.uid"` + SecretName string `json:"kubernetes.io/serviceaccount/secret.name"` + Namespace string `json:"kubernetes.io/serviceaccount/namespace"` +} + +func NewLegacyValidator(lookup bool, getter ServiceAccountTokenGetter) Validator { + return &legacyValidator{ + lookup: lookup, + getter: getter, + } +} + +type legacyValidator struct { + lookup bool + getter ServiceAccountTokenGetter +} + +var _ = Validator(&legacyValidator{}) + +func (v *legacyValidator) Validate(tokenData string, public *jwt.Claims, privateObj interface{}) (string, string, string, error) { + private, ok := privateObj.(*legacyPrivateClaims) + if !ok { + glog.Errorf("jwt validator expected private claim of type *legacyPrivateClaims but got: %T", privateObj) + return "", "", "", errors.New("Token could not be validated.") + } + + // Make sure the claims we need exist + if len(public.Subject) == 0 { + return "", "", "", errors.New("sub claim is missing") + } + namespace := private.Namespace + if len(namespace) == 0 { + return "", "", "", errors.New("namespace claim is missing") + } + secretName := private.SecretName + if len(secretName) == 0 { + return "", "", "", errors.New("secretName claim is missing") + } + serviceAccountName := private.ServiceAccountName + if len(serviceAccountName) == 0 { + return "", "", "", errors.New("serviceAccountName claim is missing") + } + serviceAccountUID := private.ServiceAccountUID + if len(serviceAccountUID) == 0 { + return "", "", "", errors.New("serviceAccountUID claim is missing") + } + + subjectNamespace, subjectName, err := apiserverserviceaccount.SplitUsername(public.Subject) + if err != nil || subjectNamespace != namespace || subjectName != serviceAccountName { + return "", "", "", errors.New("sub claim is invalid") + } + + if v.lookup { + // Make sure token hasn't been invalidated by deletion of the secret + secret, err := v.getter.GetSecret(namespace, secretName) + if err != nil { + glog.V(4).Infof("Could not retrieve token %s/%s for service account %s/%s: %v", namespace, secretName, namespace, serviceAccountName, err) + return "", "", "", errors.New("Token has been invalidated") + } + if secret.DeletionTimestamp != nil { + glog.V(4).Infof("Token is deleted and awaiting removal: %s/%s for service account %s/%s", namespace, secretName, namespace, serviceAccountName) + return "", "", "", errors.New("Token has been invalidated") + } + if bytes.Compare(secret.Data[v1.ServiceAccountTokenKey], []byte(tokenData)) != 0 { + glog.V(4).Infof("Token contents no longer matches %s/%s for service account %s/%s", namespace, secretName, namespace, serviceAccountName) + return "", "", "", errors.New("Token does not match server's copy") + } + + // Make sure service account still exists (name and UID) + serviceAccount, err := v.getter.GetServiceAccount(namespace, serviceAccountName) + if err != nil { + glog.V(4).Infof("Could not retrieve service account %s/%s: %v", namespace, serviceAccountName, err) + return "", "", "", err + } + if serviceAccount.DeletionTimestamp != nil { + glog.V(4).Infof("Service account has been deleted %s/%s", namespace, serviceAccountName) + return "", "", "", fmt.Errorf("ServiceAccount %s/%s has been deleted", namespace, serviceAccountName) + } + if string(serviceAccount.UID) != serviceAccountUID { + glog.V(4).Infof("Service account UID no longer matches %s/%s: %q != %q", namespace, serviceAccountName, string(serviceAccount.UID), serviceAccountUID) + return "", "", "", fmt.Errorf("ServiceAccount UID (%s) does not match claim (%s)", serviceAccount.UID, serviceAccountUID) + } + } + + return private.Namespace, private.ServiceAccountName, private.ServiceAccountUID, nil +} + +func (v *legacyValidator) NewPrivateClaims() interface{} { + return &legacyPrivateClaims{} +} |