123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- /*
- 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 (
- "errors"
- "fmt"
- "time"
- "gopkg.in/square/go-jose.v2/jwt"
- "k8s.io/klog"
- apiserverserviceaccount "k8s.io/apiserver/pkg/authentication/serviceaccount"
- "k8s.io/kubernetes/pkg/apis/core"
- )
- // time.Now stubbed out to allow testing
- var now = time.Now
- type privateClaims struct {
- Kubernetes kubernetes `json:"kubernetes.io,omitempty"`
- }
- type kubernetes struct {
- Namespace string `json:"namespace,omitempty"`
- Svcacct ref `json:"serviceaccount,omitempty"`
- Pod *ref `json:"pod,omitempty"`
- Secret *ref `json:"secret,omitempty"`
- }
- type ref struct {
- Name string `json:"name,omitempty"`
- UID string `json:"uid,omitempty"`
- }
- func Claims(sa core.ServiceAccount, pod *core.Pod, secret *core.Secret, expirationSeconds int64, audience []string) (*jwt.Claims, interface{}) {
- now := now()
- sc := &jwt.Claims{
- Subject: apiserverserviceaccount.MakeUsername(sa.Namespace, sa.Name),
- Audience: jwt.Audience(audience),
- IssuedAt: jwt.NewNumericDate(now),
- NotBefore: jwt.NewNumericDate(now),
- Expiry: jwt.NewNumericDate(now.Add(time.Duration(expirationSeconds) * time.Second)),
- }
- pc := &privateClaims{
- Kubernetes: kubernetes{
- Namespace: sa.Namespace,
- Svcacct: ref{
- Name: sa.Name,
- UID: string(sa.UID),
- },
- },
- }
- switch {
- case pod != nil:
- pc.Kubernetes.Pod = &ref{
- Name: pod.Name,
- UID: string(pod.UID),
- }
- case secret != nil:
- pc.Kubernetes.Secret = &ref{
- Name: secret.Name,
- UID: string(secret.UID),
- }
- }
- return sc, pc
- }
- func NewValidator(getter ServiceAccountTokenGetter) Validator {
- return &validator{
- getter: getter,
- }
- }
- type validator struct {
- getter ServiceAccountTokenGetter
- }
- var _ = Validator(&validator{})
- func (v *validator) Validate(_ string, public *jwt.Claims, privateObj interface{}) (*ServiceAccountInfo, error) {
- private, ok := privateObj.(*privateClaims)
- if !ok {
- klog.Errorf("jwt validator expected private claim of type *privateClaims but got: %T", privateObj)
- return nil, errors.New("Token could not be validated.")
- }
- err := public.Validate(jwt.Expected{
- Time: now(),
- })
- switch {
- case err == nil:
- case err == jwt.ErrExpired:
- return nil, errors.New("Token has expired.")
- default:
- klog.Errorf("unexpected validation error: %T", err)
- return nil, errors.New("Token could not be validated.")
- }
- namespace := private.Kubernetes.Namespace
- saref := private.Kubernetes.Svcacct
- podref := private.Kubernetes.Pod
- secref := private.Kubernetes.Secret
- // Make sure service account still exists (name and UID)
- serviceAccount, err := v.getter.GetServiceAccount(namespace, saref.Name)
- if err != nil {
- klog.V(4).Infof("Could not retrieve service account %s/%s: %v", namespace, saref.Name, err)
- return nil, err
- }
- if serviceAccount.DeletionTimestamp != nil {
- klog.V(4).Infof("Service account has been deleted %s/%s", namespace, saref.Name)
- return nil, fmt.Errorf("ServiceAccount %s/%s has been deleted", namespace, saref.Name)
- }
- if string(serviceAccount.UID) != saref.UID {
- klog.V(4).Infof("Service account UID no longer matches %s/%s: %q != %q", namespace, saref.Name, string(serviceAccount.UID), saref.UID)
- return nil, fmt.Errorf("ServiceAccount UID (%s) does not match claim (%s)", serviceAccount.UID, saref.UID)
- }
- if secref != nil {
- // Make sure token hasn't been invalidated by deletion of the secret
- secret, err := v.getter.GetSecret(namespace, secref.Name)
- if err != nil {
- klog.V(4).Infof("Could not retrieve bound secret %s/%s for service account %s/%s: %v", namespace, secref.Name, namespace, saref.Name, err)
- return nil, errors.New("Token has been invalidated")
- }
- if secret.DeletionTimestamp != nil {
- klog.V(4).Infof("Bound secret is deleted and awaiting removal: %s/%s for service account %s/%s", namespace, secref.Name, namespace, saref.Name)
- return nil, errors.New("Token has been invalidated")
- }
- if secref.UID != string(secret.UID) {
- klog.V(4).Infof("Secret UID no longer matches %s/%s: %q != %q", namespace, secref.Name, string(secret.UID), secref.UID)
- return nil, fmt.Errorf("Secret UID (%s) does not match claim (%s)", secret.UID, secref.UID)
- }
- }
- var podName, podUID string
- if podref != nil {
- // Make sure token hasn't been invalidated by deletion of the pod
- pod, err := v.getter.GetPod(namespace, podref.Name)
- if err != nil {
- klog.V(4).Infof("Could not retrieve bound pod %s/%s for service account %s/%s: %v", namespace, podref.Name, namespace, saref.Name, err)
- return nil, errors.New("Token has been invalidated")
- }
- if pod.DeletionTimestamp != nil {
- klog.V(4).Infof("Bound pod is deleted and awaiting removal: %s/%s for service account %s/%s", namespace, podref.Name, namespace, saref.Name)
- return nil, errors.New("Token has been invalidated")
- }
- if podref.UID != string(pod.UID) {
- klog.V(4).Infof("Pod UID no longer matches %s/%s: %q != %q", namespace, podref.Name, string(pod.UID), podref.UID)
- return nil, fmt.Errorf("Pod UID (%s) does not match claim (%s)", pod.UID, podref.UID)
- }
- podName = podref.Name
- podUID = podref.UID
- }
- return &ServiceAccountInfo{
- Namespace: private.Kubernetes.Namespace,
- Name: private.Kubernetes.Svcacct.Name,
- UID: private.Kubernetes.Svcacct.UID,
- PodName: podName,
- PodUID: podUID,
- }, nil
- }
- func (v *validator) NewPrivateClaims() interface{} {
- return &privateClaims{}
- }
|