2015-04-21 03:25:56 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
2015-04-21 03:25:56 +00:00
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 (
2015-07-15 12:53:21 +00:00
"bytes"
2015-04-21 03:25:56 +00:00
"fmt"
"time"
2015-08-05 22:05:17 +00:00
"github.com/golang/glog"
2017-06-22 18:24:23 +00:00
"k8s.io/api/core/v1"
2017-01-13 17:48:50 +00:00
apierrors "k8s.io/apimachinery/pkg/api/errors"
2017-01-11 14:09:48 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
2017-06-23 20:56:37 +00:00
informers "k8s.io/client-go/informers/core/v1"
clientset "k8s.io/client-go/kubernetes"
listersv1 "k8s.io/client-go/listers/core/v1"
2017-01-24 14:11:51 +00:00
"k8s.io/client-go/tools/cache"
2017-08-10 07:03:41 +00:00
clientretry "k8s.io/client-go/util/retry"
2017-01-27 15:20:40 +00:00
"k8s.io/client-go/util/workqueue"
2017-05-17 04:01:50 +00:00
"k8s.io/kubernetes/pkg/controller"
2016-09-21 13:14:26 +00:00
"k8s.io/kubernetes/pkg/registry/core/secret"
2015-12-24 21:54:40 +00:00
"k8s.io/kubernetes/pkg/serviceaccount"
2016-04-13 18:38:32 +00:00
"k8s.io/kubernetes/pkg/util/metrics"
2015-04-21 03:25:56 +00:00
)
2015-10-22 02:09:05 +00:00
// RemoveTokenBackoff is the recommended (empirical) retry interval for removing
// a secret reference from a service account when the secret is deleted. It is
// exported for use by custom secret controllers.
var RemoveTokenBackoff = wait . Backoff {
Steps : 10 ,
Duration : 100 * time . Millisecond ,
Jitter : 1.0 ,
}
2015-06-02 05:36:18 +00:00
2015-04-21 03:25:56 +00:00
// TokensControllerOptions contains options for the TokensController
type TokensControllerOptions struct {
// TokenGenerator is the generator to use to create new tokens
2015-12-24 21:54:40 +00:00
TokenGenerator serviceaccount . TokenGenerator
2015-04-21 03:25:56 +00:00
// ServiceAccountResync is the time.Duration at which to fully re-list service accounts.
// If zero, re-list will be delayed as long as possible
ServiceAccountResync time . Duration
// SecretResync is the time.Duration at which to fully re-list secrets.
// If zero, re-list will be delayed as long as possible
SecretResync time . Duration
2016-05-23 16:51:02 +00:00
// This CA will be added in the secrets of service accounts
2015-06-23 22:43:59 +00:00
RootCA [ ] byte
2016-05-23 16:51:02 +00:00
// MaxRetries controls the maximum number of times a particular key is retried before giving up
// If zero, a default max is used
MaxRetries int
2015-04-21 03:25:56 +00:00
}
// NewTokensController returns a new *TokensController.
2017-10-31 14:19:55 +00:00
func NewTokensController ( serviceAccounts informers . ServiceAccountInformer , secrets informers . SecretInformer , cl clientset . Interface , options TokensControllerOptions ) ( * TokensController , error ) {
2016-05-23 16:51:02 +00:00
maxRetries := options . MaxRetries
if maxRetries == 0 {
maxRetries = 10
}
2015-04-21 03:25:56 +00:00
e := & TokensController {
client : cl ,
token : options . TokenGenerator ,
2015-06-23 22:43:59 +00:00
rootCA : options . RootCA ,
2016-05-23 16:51:02 +00:00
2016-08-22 18:15:45 +00:00
syncServiceAccountQueue : workqueue . NewNamedRateLimitingQueue ( workqueue . DefaultControllerRateLimiter ( ) , "serviceaccount_tokens_service" ) ,
syncSecretQueue : workqueue . NewNamedRateLimitingQueue ( workqueue . DefaultControllerRateLimiter ( ) , "serviceaccount_tokens_secret" ) ,
2016-05-23 16:51:02 +00:00
maxRetries : maxRetries ,
2015-04-21 03:25:56 +00:00
}
2017-10-25 15:54:32 +00:00
if cl != nil && cl . CoreV1 ( ) . RESTClient ( ) . GetRateLimiter ( ) != nil {
2017-10-31 14:19:55 +00:00
if err := metrics . RegisterMetricAndTrackRateLimiterUsage ( "serviceaccount_tokens_controller" , cl . CoreV1 ( ) . RESTClient ( ) . GetRateLimiter ( ) ) ; err != nil {
return nil , err
}
2016-04-13 18:38:32 +00:00
}
2016-05-23 16:51:02 +00:00
2017-05-17 04:01:50 +00:00
e . serviceAccounts = serviceAccounts . Lister ( )
e . serviceAccountSynced = serviceAccounts . Informer ( ) . HasSynced
serviceAccounts . Informer ( ) . AddEventHandlerWithResyncPeriod (
2016-09-14 18:35:38 +00:00
cache . ResourceEventHandlerFuncs {
2016-05-23 16:51:02 +00:00
AddFunc : e . queueServiceAccountSync ,
UpdateFunc : e . queueServiceAccountUpdateSync ,
DeleteFunc : e . queueServiceAccountSync ,
2015-04-21 03:25:56 +00:00
} ,
2017-05-17 04:01:50 +00:00
options . ServiceAccountResync ,
2015-04-21 03:25:56 +00:00
)
2017-05-17 04:01:50 +00:00
secretCache := secrets . Informer ( ) . GetIndexer ( )
e . updatedSecrets = cache . NewIntegerResourceVersionMutationCache ( secretCache , secretCache , 60 * time . Second , true )
e . secretSynced = secrets . Informer ( ) . HasSynced
secrets . Informer ( ) . AddEventHandlerWithResyncPeriod (
cache . FilteringResourceEventHandler {
FilterFunc : func ( obj interface { } ) bool {
switch t := obj . ( type ) {
case * v1 . Secret :
return t . Type == v1 . SecretTypeServiceAccountToken
default :
utilruntime . HandleError ( fmt . Errorf ( "object passed to %T that is not expected: %T" , e , obj ) )
return false
}
2015-04-21 03:25:56 +00:00
} ,
2017-05-17 04:01:50 +00:00
Handler : cache . ResourceEventHandlerFuncs {
AddFunc : e . queueSecretSync ,
UpdateFunc : e . queueSecretUpdateSync ,
DeleteFunc : e . queueSecretSync ,
2015-04-21 03:25:56 +00:00
} ,
} ,
options . SecretResync ,
)
2017-10-31 14:19:55 +00:00
return e , nil
2015-04-21 03:25:56 +00:00
}
// TokensController manages ServiceAccountToken secrets for ServiceAccount objects
type TokensController struct {
2016-01-29 06:34:08 +00:00
client clientset . Interface
2015-12-24 21:54:40 +00:00
token serviceaccount . TokenGenerator
2015-04-21 03:25:56 +00:00
2015-06-23 22:43:59 +00:00
rootCA [ ] byte
2017-05-17 04:01:50 +00:00
serviceAccounts listersv1 . ServiceAccountLister
// updatedSecrets is a wrapper around the shared cache which allows us to record
// and return our local mutations (since we're very likely to act on an updated
// secret before the watch reports it).
updatedSecrets cache . MutationCache
2015-04-21 03:25:56 +00:00
// Since we join two objects, we'll watch both of them with controllers.
2017-05-17 04:01:50 +00:00
serviceAccountSynced cache . InformerSynced
secretSynced cache . InformerSynced
2015-05-19 09:24:17 +00:00
2016-05-23 16:51:02 +00:00
// syncServiceAccountQueue handles service account events:
// * ensures a referenced token exists for service accounts which still exist
// * ensures tokens are removed for service accounts which no longer exist
// key is "<namespace>/<name>/<uid>"
syncServiceAccountQueue workqueue . RateLimitingInterface
// syncSecretQueue handles secret events:
// * deletes tokens whose service account no longer exists
// * updates tokens with missing token or namespace data, or mismatched ca data
// * ensures service account secret references are removed for tokens which are deleted
// key is a secretQueueKey{}
syncSecretQueue workqueue . RateLimitingInterface
maxRetries int
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
// Runs controller blocks until stopCh is closed
func ( e * TokensController ) Run ( workers int , stopCh <- chan struct { } ) {
2017-05-17 04:01:50 +00:00
// Shut down queues
2016-05-23 16:51:02 +00:00
defer utilruntime . HandleCrash ( )
2017-05-17 04:01:50 +00:00
defer e . syncServiceAccountQueue . ShutDown ( )
defer e . syncSecretQueue . ShutDown ( )
2016-05-23 16:51:02 +00:00
2017-05-17 04:01:50 +00:00
if ! controller . WaitForCacheSync ( "tokens" , stopCh , e . serviceAccountSynced , e . secretSynced ) {
return
2015-04-21 03:25:56 +00:00
}
2017-05-17 04:01:50 +00:00
glog . V ( 5 ) . Infof ( "Starting workers" )
2016-05-23 16:51:02 +00:00
for i := 0 ; i < workers ; i ++ {
go wait . Until ( e . syncServiceAccount , 0 , stopCh )
go wait . Until ( e . syncSecret , 0 , stopCh )
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
<- stopCh
2017-05-17 04:01:50 +00:00
glog . V ( 1 ) . Infof ( "Shutting down" )
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
func ( e * TokensController ) queueServiceAccountSync ( obj interface { } ) {
2016-11-18 20:50:17 +00:00
if serviceAccount , ok := obj . ( * v1 . ServiceAccount ) ; ok {
2016-05-23 16:51:02 +00:00
e . syncServiceAccountQueue . Add ( makeServiceAccountKey ( serviceAccount ) )
2015-04-21 03:25:56 +00:00
}
}
2016-05-23 16:51:02 +00:00
func ( e * TokensController ) queueServiceAccountUpdateSync ( oldObj interface { } , newObj interface { } ) {
2016-11-18 20:50:17 +00:00
if serviceAccount , ok := newObj . ( * v1 . ServiceAccount ) ; ok {
2016-05-23 16:51:02 +00:00
e . syncServiceAccountQueue . Add ( makeServiceAccountKey ( serviceAccount ) )
2015-04-21 03:25:56 +00:00
}
}
2016-05-23 16:51:02 +00:00
// complete optionally requeues key, then calls queue.Done(key)
func ( e * TokensController ) retryOrForget ( queue workqueue . RateLimitingInterface , key interface { } , requeue bool ) {
if ! requeue {
queue . Forget ( key )
2015-04-21 03:25:56 +00:00
return
}
2016-05-23 16:51:02 +00:00
requeueCount := queue . NumRequeues ( key )
if requeueCount < e . maxRetries {
queue . AddRateLimited ( key )
2015-04-21 03:25:56 +00:00
return
}
2016-05-23 16:51:02 +00:00
glog . V ( 4 ) . Infof ( "retried %d times: %#v" , requeueCount , key )
queue . Forget ( key )
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
func ( e * TokensController ) queueSecretSync ( obj interface { } ) {
2016-11-18 20:50:17 +00:00
if secret , ok := obj . ( * v1 . Secret ) ; ok {
2016-05-23 16:51:02 +00:00
e . syncSecretQueue . Add ( makeSecretQueueKey ( secret ) )
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
}
func ( e * TokensController ) queueSecretUpdateSync ( oldObj interface { } , newObj interface { } ) {
2016-11-18 20:50:17 +00:00
if secret , ok := newObj . ( * v1 . Secret ) ; ok {
2016-05-23 16:51:02 +00:00
e . syncSecretQueue . Add ( makeSecretQueueKey ( secret ) )
2015-04-21 03:25:56 +00:00
}
}
2016-05-23 16:51:02 +00:00
func ( e * TokensController ) syncServiceAccount ( ) {
key , quit := e . syncServiceAccountQueue . Get ( )
if quit {
return
}
defer e . syncServiceAccountQueue . Done ( key )
retry := false
defer func ( ) {
e . retryOrForget ( e . syncServiceAccountQueue , key , retry )
} ( )
saInfo , err := parseServiceAccountKey ( key )
2015-04-21 03:25:56 +00:00
if err != nil {
glog . Error ( err )
return
}
2016-05-23 16:51:02 +00:00
sa , err := e . getServiceAccount ( saInfo . namespace , saInfo . name , saInfo . uid , false )
switch {
case err != nil :
glog . Error ( err )
retry = true
case sa == nil :
// service account no longer exists, so delete related tokens
glog . V ( 4 ) . Infof ( "syncServiceAccount(%s/%s), service account deleted, removing tokens" , saInfo . namespace , saInfo . name )
2017-01-17 03:38:19 +00:00
sa = & v1 . ServiceAccount { ObjectMeta : metav1 . ObjectMeta { Namespace : saInfo . namespace , Name : saInfo . name , UID : saInfo . uid } }
2017-04-13 04:50:51 +00:00
retry , err = e . deleteTokens ( sa )
if err != nil {
2016-05-23 16:51:02 +00:00
glog . Errorf ( "error deleting serviceaccount tokens for %s/%s: %v" , saInfo . namespace , saInfo . name , err )
}
default :
// ensure a token exists and is referenced by this service account
2017-04-13 04:50:51 +00:00
retry , err = e . ensureReferencedToken ( sa )
if err != nil {
2016-05-23 16:51:02 +00:00
glog . Errorf ( "error synchronizing serviceaccount %s/%s: %v" , saInfo . namespace , saInfo . name , err )
2015-04-21 03:25:56 +00:00
}
}
}
2016-05-23 16:51:02 +00:00
func ( e * TokensController ) syncSecret ( ) {
key , quit := e . syncSecretQueue . Get ( )
if quit {
2015-04-21 03:25:56 +00:00
return
}
2016-05-23 16:51:02 +00:00
defer e . syncSecretQueue . Done ( key )
2015-04-21 03:25:56 +00:00
2016-05-23 16:51:02 +00:00
// Track whether or not we should retry this sync
retry := false
defer func ( ) {
e . retryOrForget ( e . syncSecretQueue , key , retry )
} ( )
secretInfo , err := parseSecretQueueKey ( key )
2015-04-21 03:25:56 +00:00
if err != nil {
glog . Error ( err )
return
}
2016-05-23 16:51:02 +00:00
secret , err := e . getSecret ( secretInfo . namespace , secretInfo . name , secretInfo . uid , false )
switch {
case err != nil :
glog . Error ( err )
retry = true
case secret == nil :
// If the service account exists
if sa , saErr := e . getServiceAccount ( secretInfo . namespace , secretInfo . saName , secretInfo . saUID , false ) ; saErr == nil && sa != nil {
// secret no longer exists, so delete references to this secret from the service account
2016-10-13 18:49:19 +00:00
if err := clientretry . RetryOnConflict ( RemoveTokenBackoff , func ( ) error {
2016-05-23 16:51:02 +00:00
return e . removeSecretReference ( secretInfo . namespace , secretInfo . saName , secretInfo . saUID , secretInfo . name )
} ) ; err != nil {
glog . Error ( err )
}
}
default :
// Ensure service account exists
sa , saErr := e . getServiceAccount ( secretInfo . namespace , secretInfo . saName , secretInfo . saUID , true )
switch {
case saErr != nil :
glog . Error ( saErr )
retry = true
case sa == nil :
// Delete token
glog . V ( 4 ) . Infof ( "syncSecret(%s/%s), service account does not exist, deleting token" , secretInfo . namespace , secretInfo . name )
if retriable , err := e . deleteToken ( secretInfo . namespace , secretInfo . name , secretInfo . uid ) ; err != nil {
glog . Errorf ( "error deleting serviceaccount token %s/%s for service account %s: %v" , secretInfo . namespace , secretInfo . name , secretInfo . saName , err )
retry = retriable
}
default :
// Update token if needed
if retriable , err := e . generateTokenIfNeeded ( sa , secret ) ; err != nil {
glog . Errorf ( "error populating serviceaccount token %s/%s for service account %s: %v" , secretInfo . namespace , secretInfo . name , secretInfo . saName , err )
retry = retriable
}
}
2015-04-21 03:25:56 +00:00
}
}
2016-11-18 20:50:17 +00:00
func ( e * TokensController ) deleteTokens ( serviceAccount * v1 . ServiceAccount ) ( /*retry*/ bool , error ) {
2016-05-23 16:51:02 +00:00
tokens , err := e . listTokenSecrets ( serviceAccount )
2015-04-21 03:25:56 +00:00
if err != nil {
2016-05-23 16:51:02 +00:00
// don't retry on cache lookup errors
return false , err
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
retry := false
errs := [ ] error { }
for _ , token := range tokens {
r , err := e . deleteToken ( token . Namespace , token . Name , token . UID )
if err != nil {
errs = append ( errs , err )
}
if r {
retry = true
2015-04-21 03:25:56 +00:00
}
}
2016-05-23 16:51:02 +00:00
return retry , utilerrors . NewAggregate ( errs )
}
2015-04-21 03:25:56 +00:00
2016-05-23 16:51:02 +00:00
func ( e * TokensController ) deleteToken ( ns , name string , uid types . UID ) ( /*retry*/ bool , error ) {
2017-01-24 15:38:21 +00:00
var opts * metav1 . DeleteOptions
2016-05-23 16:51:02 +00:00
if len ( uid ) > 0 {
2017-01-24 15:38:21 +00:00
opts = & metav1 . DeleteOptions { Preconditions : & metav1 . Preconditions { UID : & uid } }
2016-05-23 16:51:02 +00:00
}
2017-10-25 15:54:32 +00:00
err := e . client . CoreV1 ( ) . Secrets ( ns ) . Delete ( name , opts )
2016-05-23 16:51:02 +00:00
// NotFound doesn't need a retry (it's already been deleted)
// Conflict doesn't need a retry (the UID precondition failed)
if err == nil || apierrors . IsNotFound ( err ) || apierrors . IsConflict ( err ) {
return false , nil
}
// Retry for any other error
return true , err
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
// ensureReferencedToken makes sure at least one ServiceAccountToken secret exists, and is included in the serviceAccount's Secrets list
2016-11-18 20:50:17 +00:00
func ( e * TokensController ) ensureReferencedToken ( serviceAccount * v1 . ServiceAccount ) ( /* retry */ bool , error ) {
2017-04-13 04:50:51 +00:00
if hasToken , err := e . hasReferencedToken ( serviceAccount ) ; err != nil {
// Don't retry cache lookup errors
return false , err
} else if hasToken {
// A service account token already exists, and is referenced, short-circuit
return false , nil
2016-05-23 16:51:02 +00:00
}
2015-06-02 05:36:18 +00:00
// We don't want to update the cache's copy of the service account
// so add the secret to a freshly retrieved copy of the service account
2017-10-25 15:54:32 +00:00
serviceAccounts := e . client . CoreV1 ( ) . ServiceAccounts ( serviceAccount . Namespace )
2016-12-07 13:26:33 +00:00
liveServiceAccount , err := serviceAccounts . Get ( serviceAccount . Name , metav1 . GetOptions { } )
2015-06-02 05:36:18 +00:00
if err != nil {
2017-04-13 04:50:51 +00:00
// Retry if we cannot fetch the live service account (for a NotFound error, either the live lookup or our cache are stale)
return true , err
2015-06-02 05:36:18 +00:00
}
if liveServiceAccount . ResourceVersion != serviceAccount . ResourceVersion {
2017-04-13 04:50:51 +00:00
// Retry if our liveServiceAccount doesn't match our cache's resourceVersion (either the live lookup or our cache are stale)
glog . V ( 4 ) . Infof ( "liveServiceAccount.ResourceVersion (%s) does not match cache (%s), retrying" , liveServiceAccount . ResourceVersion , serviceAccount . ResourceVersion )
return true , nil
2015-06-02 05:36:18 +00:00
}
2015-04-21 03:25:56 +00:00
// Build the secret
2016-11-18 20:50:17 +00:00
secret := & v1 . Secret {
2017-01-17 03:38:19 +00:00
ObjectMeta : metav1 . ObjectMeta {
2015-04-21 03:25:56 +00:00
Name : secret . Strategy . GenerateName ( fmt . Sprintf ( "%s-token-" , serviceAccount . Name ) ) ,
Namespace : serviceAccount . Namespace ,
Annotations : map [ string ] string {
2016-11-18 20:50:17 +00:00
v1 . ServiceAccountNameKey : serviceAccount . Name ,
v1 . ServiceAccountUIDKey : string ( serviceAccount . UID ) ,
2015-04-21 03:25:56 +00:00
} ,
} ,
2016-11-18 20:50:17 +00:00
Type : v1 . SecretTypeServiceAccountToken ,
2015-04-21 03:25:56 +00:00
Data : map [ string ] [ ] byte { } ,
}
// Generate the token
2018-02-06 04:07:50 +00:00
token , err := e . token . GenerateToken ( serviceaccount . LegacyClaims ( * serviceAccount , * secret ) )
2015-04-21 03:25:56 +00:00
if err != nil {
2016-05-23 16:51:02 +00:00
// retriable error
return true , err
2015-04-21 03:25:56 +00:00
}
2016-11-18 20:50:17 +00:00
secret . Data [ v1 . ServiceAccountTokenKey ] = [ ] byte ( token )
secret . Data [ v1 . ServiceAccountNamespaceKey ] = [ ] byte ( serviceAccount . Namespace )
2015-06-23 22:43:59 +00:00
if e . rootCA != nil && len ( e . rootCA ) > 0 {
2016-11-18 20:50:17 +00:00
secret . Data [ v1 . ServiceAccountRootCAKey ] = e . rootCA
2015-06-23 22:43:59 +00:00
}
2015-04-21 03:25:56 +00:00
// Save the secret
2017-10-25 15:54:32 +00:00
createdToken , err := e . client . CoreV1 ( ) . Secrets ( serviceAccount . Namespace ) . Create ( secret )
2016-05-23 16:51:02 +00:00
if err != nil {
// retriable error
return true , err
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
// Manually add the new token to the cache store.
// This prevents the service account update (below) triggering another token creation, if the referenced token couldn't be found in the store
2017-05-17 04:01:50 +00:00
e . updatedSecrets . Mutation ( createdToken )
2015-04-21 03:25:56 +00:00
2017-04-13 04:50:51 +00:00
// Try to add a reference to the newly created token to the service account
addedReference := false
err = clientretry . RetryOnConflict ( clientretry . DefaultRetry , func ( ) error {
// refresh liveServiceAccount on every retry
defer func ( ) { liveServiceAccount = nil } ( )
// fetch the live service account if needed, and verify the UID matches and that we still need a token
if liveServiceAccount == nil {
liveServiceAccount , err = serviceAccounts . Get ( serviceAccount . Name , metav1 . GetOptions { } )
if err != nil {
return err
}
if liveServiceAccount . UID != serviceAccount . UID {
// If we don't have the same service account, stop trying to add a reference to the token made for the old service account.
return nil
}
2015-04-21 03:25:56 +00:00
2017-04-13 04:50:51 +00:00
if hasToken , err := e . hasReferencedToken ( liveServiceAccount ) ; err != nil {
// Don't retry cache lookup errors
return nil
} else if hasToken {
// A service account token already exists, and is referenced, short-circuit
return nil
}
}
// Try to add a reference to the token
liveServiceAccount . Secrets = append ( liveServiceAccount . Secrets , v1 . ObjectReference { Name : secret . Name } )
if _ , err := serviceAccounts . Update ( liveServiceAccount ) ; err != nil {
return err
}
addedReference = true
return nil
} )
if ! addedReference {
2015-06-02 05:35:45 +00:00
// we weren't able to use the token, try to clean it up.
2016-05-23 16:51:02 +00:00
glog . V ( 2 ) . Infof ( "deleting secret %s/%s because reference couldn't be added (%v)" , secret . Namespace , secret . Name , err )
2017-01-24 15:38:21 +00:00
deleteOpts := & metav1 . DeleteOptions { Preconditions : & metav1 . Preconditions { UID : & createdToken . UID } }
2017-10-25 15:54:32 +00:00
if deleteErr := e . client . CoreV1 ( ) . Secrets ( createdToken . Namespace ) . Delete ( createdToken . Name , deleteOpts ) ; deleteErr != nil {
2016-05-23 16:51:02 +00:00
glog . Error ( deleteErr ) // if we fail, just log it
2015-06-02 05:35:45 +00:00
}
2017-04-13 04:50:51 +00:00
}
2016-05-23 16:51:02 +00:00
2017-04-13 04:50:51 +00:00
if err != nil {
2016-05-23 16:51:02 +00:00
if apierrors . IsConflict ( err ) || apierrors . IsNotFound ( err ) {
// if we got a Conflict error, the service account was updated by someone else, and we'll get an update notification later
// if we got a NotFound error, the service account no longer exists, and we don't need to create a token for it
return false , nil
}
// retry in all other cases
return true , err
2015-06-02 05:35:45 +00:00
}
2016-05-23 16:51:02 +00:00
// success!
return false , nil
2015-04-21 03:25:56 +00:00
}
2017-04-13 04:50:51 +00:00
// hasReferencedToken returns true if the serviceAccount references a service account token secret
func ( e * TokensController ) hasReferencedToken ( serviceAccount * v1 . ServiceAccount ) ( bool , error ) {
if len ( serviceAccount . Secrets ) == 0 {
return false , nil
}
allSecrets , err := e . listTokenSecrets ( serviceAccount )
if err != nil {
return false , err
}
referencedSecrets := getSecretReferences ( serviceAccount )
for _ , secret := range allSecrets {
if referencedSecrets . Has ( secret . Name ) {
return true , nil
}
}
return false , nil
}
2016-11-18 20:50:17 +00:00
func ( e * TokensController ) secretUpdateNeeded ( secret * v1 . Secret ) ( bool , bool , bool ) {
caData := secret . Data [ v1 . ServiceAccountRootCAKey ]
2015-07-15 12:53:21 +00:00
needsCA := len ( e . rootCA ) > 0 && bytes . Compare ( caData , e . rootCA ) != 0
2016-11-18 20:50:17 +00:00
needsNamespace := len ( secret . Data [ v1 . ServiceAccountNamespaceKey ] ) == 0
2016-02-11 19:46:56 +00:00
2016-11-18 20:50:17 +00:00
tokenData := secret . Data [ v1 . ServiceAccountTokenKey ]
2015-07-15 12:53:21 +00:00
needsToken := len ( tokenData ) == 0
2016-05-23 16:51:02 +00:00
return needsCA , needsNamespace , needsToken
}
// generateTokenIfNeeded populates the token data for the given Secret if not already set
2016-11-18 20:50:17 +00:00
func ( e * TokensController ) generateTokenIfNeeded ( serviceAccount * v1 . ServiceAccount , cachedSecret * v1 . Secret ) ( /* retry */ bool , error ) {
2016-05-23 16:51:02 +00:00
// Check the cached secret to see if changes are needed
if needsCA , needsNamespace , needsToken := e . secretUpdateNeeded ( cachedSecret ) ; ! needsCA && ! needsToken && ! needsNamespace {
return false , nil
}
// We don't want to update the cache's copy of the secret
// so add the token to a freshly retrieved copy of the secret
2017-10-25 15:54:32 +00:00
secrets := e . client . CoreV1 ( ) . Secrets ( cachedSecret . Namespace )
2016-12-07 13:26:33 +00:00
liveSecret , err := secrets . Get ( cachedSecret . Name , metav1 . GetOptions { } )
2016-05-23 16:51:02 +00:00
if err != nil {
// Retry for any error other than a NotFound
return ! apierrors . IsNotFound ( err ) , err
}
if liveSecret . ResourceVersion != cachedSecret . ResourceVersion {
// our view of the secret is not up to date
// we'll get notified of an update event later and get to try again
glog . V ( 2 ) . Infof ( "secret %s/%s is not up to date, skipping token population" , liveSecret . Namespace , liveSecret . Name )
return false , nil
}
needsCA , needsNamespace , needsToken := e . secretUpdateNeeded ( liveSecret )
2016-02-11 19:46:56 +00:00
if ! needsCA && ! needsToken && ! needsNamespace {
2016-05-23 16:51:02 +00:00
return false , nil
}
if liveSecret . Annotations == nil {
liveSecret . Annotations = map [ string ] string { }
}
if liveSecret . Data == nil {
liveSecret . Data = map [ string ] [ ] byte { }
2015-04-21 03:25:56 +00:00
}
2015-07-15 12:53:21 +00:00
// Set the CA
if needsCA {
2016-11-18 20:50:17 +00:00
liveSecret . Data [ v1 . ServiceAccountRootCAKey ] = e . rootCA
2015-06-23 22:43:59 +00:00
}
2016-02-11 19:46:56 +00:00
// Set the namespace
if needsNamespace {
2016-11-18 20:50:17 +00:00
liveSecret . Data [ v1 . ServiceAccountNamespaceKey ] = [ ] byte ( liveSecret . Namespace )
2016-02-11 19:46:56 +00:00
}
2015-04-21 03:25:56 +00:00
// Generate the token
2015-07-15 12:53:21 +00:00
if needsToken {
2018-02-06 04:07:50 +00:00
token , err := e . token . GenerateToken ( serviceaccount . LegacyClaims ( * serviceAccount , * liveSecret ) )
2015-07-15 12:53:21 +00:00
if err != nil {
2016-05-23 16:51:02 +00:00
return false , err
2015-07-15 12:53:21 +00:00
}
2016-11-18 20:50:17 +00:00
liveSecret . Data [ v1 . ServiceAccountTokenKey ] = [ ] byte ( token )
2015-04-21 03:25:56 +00:00
}
2015-07-15 12:53:21 +00:00
// Set annotations
2016-11-18 20:50:17 +00:00
liveSecret . Annotations [ v1 . ServiceAccountNameKey ] = serviceAccount . Name
liveSecret . Annotations [ v1 . ServiceAccountUIDKey ] = string ( serviceAccount . UID )
2015-04-21 03:25:56 +00:00
// Save the secret
2016-05-23 16:51:02 +00:00
_ , err = secrets . Update ( liveSecret )
if apierrors . IsConflict ( err ) || apierrors . IsNotFound ( err ) {
// if we got a Conflict error, the secret was updated by someone else, and we'll get an update notification later
// if we got a NotFound error, the secret no longer exists, and we don't need to populate a token
return false , nil
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
if err != nil {
return true , err
}
return false , nil
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
// removeSecretReference updates the given ServiceAccount to remove a reference to the given secretName if needed.
func ( e * TokensController ) removeSecretReference ( saNamespace string , saName string , saUID types . UID , secretName string ) error {
2015-04-21 03:25:56 +00:00
// We don't want to update the cache's copy of the service account
// so remove the secret from a freshly retrieved copy of the service account
2017-10-25 15:54:32 +00:00
serviceAccounts := e . client . CoreV1 ( ) . ServiceAccounts ( saNamespace )
2016-12-07 13:26:33 +00:00
serviceAccount , err := serviceAccounts . Get ( saName , metav1 . GetOptions { } )
2016-05-23 16:51:02 +00:00
// Ignore NotFound errors when attempting to remove a reference
if apierrors . IsNotFound ( err ) {
return nil
}
2015-04-21 03:25:56 +00:00
if err != nil {
2015-10-22 02:09:05 +00:00
return err
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
// Short-circuit if the UID doesn't match
if len ( saUID ) > 0 && saUID != serviceAccount . UID {
return nil
}
// Short-circuit if the secret is no longer referenced
2015-04-21 03:25:56 +00:00
if ! getSecretReferences ( serviceAccount ) . Has ( secretName ) {
2015-10-22 02:09:05 +00:00
return nil
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
// Remove the secret
2016-11-18 20:50:17 +00:00
secrets := [ ] v1 . ObjectReference { }
2015-04-21 03:25:56 +00:00
for _ , s := range serviceAccount . Secrets {
if s . Name != secretName {
secrets = append ( secrets , s )
}
}
serviceAccount . Secrets = secrets
_ , err = serviceAccounts . Update ( serviceAccount )
2016-05-23 16:51:02 +00:00
// Ignore NotFound errors when attempting to remove a reference
if apierrors . IsNotFound ( err ) {
return nil
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
return err
2015-04-21 03:25:56 +00:00
}
2016-11-18 20:50:17 +00:00
func ( e * TokensController ) getServiceAccount ( ns string , name string , uid types . UID , fetchOnCacheMiss bool ) ( * v1 . ServiceAccount , error ) {
2016-05-23 16:51:02 +00:00
// Look up in cache
2017-05-17 04:01:50 +00:00
sa , err := e . serviceAccounts . ServiceAccounts ( ns ) . Get ( name )
if err != nil && ! apierrors . IsNotFound ( err ) {
2016-05-23 16:51:02 +00:00
return nil , err
}
2017-05-17 04:01:50 +00:00
if sa != nil {
2016-05-23 16:51:02 +00:00
// Ensure UID matches if given
if len ( uid ) == 0 || uid == sa . UID {
return sa , nil
}
}
if ! fetchOnCacheMiss {
2015-04-21 03:25:56 +00:00
return nil , nil
}
2016-05-23 16:51:02 +00:00
// Live lookup
2017-10-25 15:54:32 +00:00
sa , err = e . client . CoreV1 ( ) . ServiceAccounts ( ns ) . Get ( name , metav1 . GetOptions { } )
2016-05-23 16:51:02 +00:00
if apierrors . IsNotFound ( err ) {
return nil , nil
}
2015-04-21 03:25:56 +00:00
if err != nil {
return nil , err
}
2016-05-23 16:51:02 +00:00
// Ensure UID matches if given
if len ( uid ) == 0 || uid == sa . UID {
return sa , nil
2015-04-21 03:25:56 +00:00
}
2016-05-23 16:51:02 +00:00
return nil , nil
}
2015-04-21 03:25:56 +00:00
2016-11-18 20:50:17 +00:00
func ( e * TokensController ) getSecret ( ns string , name string , uid types . UID , fetchOnCacheMiss bool ) ( * v1 . Secret , error ) {
2016-05-23 16:51:02 +00:00
// Look up in cache
2017-05-17 04:01:50 +00:00
obj , exists , err := e . updatedSecrets . GetByKey ( makeCacheKey ( ns , name ) )
2016-05-23 16:51:02 +00:00
if err != nil {
return nil , err
}
if exists {
2016-11-18 20:50:17 +00:00
secret , ok := obj . ( * v1 . Secret )
2016-05-23 16:51:02 +00:00
if ! ok {
2016-11-18 20:50:17 +00:00
return nil , fmt . Errorf ( "expected *v1.Secret, got %#v" , secret )
2015-06-18 14:42:41 +00:00
}
2016-05-23 16:51:02 +00:00
// Ensure UID matches if given
if len ( uid ) == 0 || uid == secret . UID {
return secret , nil
2015-06-18 14:42:41 +00:00
}
2016-05-23 16:51:02 +00:00
}
2015-09-16 20:04:26 +00:00
2016-05-23 16:51:02 +00:00
if ! fetchOnCacheMiss {
return nil , nil
2015-06-18 14:42:41 +00:00
}
2016-05-23 16:51:02 +00:00
// Live lookup
2017-10-25 15:54:32 +00:00
secret , err := e . client . CoreV1 ( ) . Secrets ( ns ) . Get ( name , metav1 . GetOptions { } )
2016-05-23 16:51:02 +00:00
if apierrors . IsNotFound ( err ) {
return nil , nil
}
if err != nil {
return nil , err
}
// Ensure UID matches if given
if len ( uid ) == 0 || uid == secret . UID {
return secret , nil
}
2015-04-21 03:25:56 +00:00
return nil , nil
}
// listTokenSecrets returns a list of all of the ServiceAccountToken secrets that
// reference the given service account's name and uid
2016-11-18 20:50:17 +00:00
func ( e * TokensController ) listTokenSecrets ( serviceAccount * v1 . ServiceAccount ) ( [ ] * v1 . Secret , error ) {
2017-05-17 04:01:50 +00:00
namespaceSecrets , err := e . updatedSecrets . ByIndex ( "namespace" , serviceAccount . Namespace )
2015-04-21 03:25:56 +00:00
if err != nil {
return nil , err
}
2016-11-18 20:50:17 +00:00
items := [ ] * v1 . Secret { }
2015-04-21 03:25:56 +00:00
for _ , obj := range namespaceSecrets {
2016-11-18 20:50:17 +00:00
secret := obj . ( * v1 . Secret )
2015-09-16 20:04:26 +00:00
2015-12-24 21:54:40 +00:00
if serviceaccount . IsServiceAccountToken ( secret , serviceAccount ) {
2015-09-16 20:04:26 +00:00
items = append ( items , secret )
2015-04-21 03:25:56 +00:00
}
}
return items , nil
}
// serviceAccountNameAndUID is a helper method to get the ServiceAccount Name and UID from the given secret
// Returns "","" if the secret is not a ServiceAccountToken secret
// If the name or uid annotation is missing, "" is returned instead
2016-11-18 20:50:17 +00:00
func serviceAccountNameAndUID ( secret * v1 . Secret ) ( string , string ) {
if secret . Type != v1 . SecretTypeServiceAccountToken {
2015-04-21 03:25:56 +00:00
return "" , ""
}
2016-11-18 20:50:17 +00:00
return secret . Annotations [ v1 . ServiceAccountNameKey ] , secret . Annotations [ v1 . ServiceAccountUIDKey ]
2015-04-21 03:25:56 +00:00
}
2016-11-18 20:50:17 +00:00
func getSecretReferences ( serviceAccount * v1 . ServiceAccount ) sets . String {
2015-09-09 17:45:01 +00:00
references := sets . NewString ( )
2015-04-21 03:25:56 +00:00
for _ , secret := range serviceAccount . Secrets {
references . Insert ( secret . Name )
}
return references
}
2016-05-23 16:51:02 +00:00
// serviceAccountQueueKey holds information we need to sync a service account.
// It contains enough information to look up the cached service account,
// or delete owned tokens if the service account no longer exists.
type serviceAccountQueueKey struct {
namespace string
name string
uid types . UID
}
2016-11-18 20:50:17 +00:00
func makeServiceAccountKey ( sa * v1 . ServiceAccount ) interface { } {
2016-05-23 16:51:02 +00:00
return serviceAccountQueueKey {
namespace : sa . Namespace ,
name : sa . Name ,
uid : sa . UID ,
}
}
func parseServiceAccountKey ( key interface { } ) ( serviceAccountQueueKey , error ) {
queueKey , ok := key . ( serviceAccountQueueKey )
if ! ok || len ( queueKey . namespace ) == 0 || len ( queueKey . name ) == 0 || len ( queueKey . uid ) == 0 {
return serviceAccountQueueKey { } , fmt . Errorf ( "invalid serviceaccount key: %#v" , key )
}
return queueKey , nil
}
// secretQueueKey holds information we need to sync a service account token secret.
// It contains enough information to look up the cached service account,
// or delete the secret reference if the secret no longer exists.
type secretQueueKey struct {
namespace string
name string
uid types . UID
saName string
// optional, will be blank when syncing tokens missing the service account uid annotation
saUID types . UID
}
2016-11-18 20:50:17 +00:00
func makeSecretQueueKey ( secret * v1 . Secret ) interface { } {
2016-05-23 16:51:02 +00:00
return secretQueueKey {
namespace : secret . Namespace ,
name : secret . Name ,
uid : secret . UID ,
2016-11-18 20:50:17 +00:00
saName : secret . Annotations [ v1 . ServiceAccountNameKey ] ,
saUID : types . UID ( secret . Annotations [ v1 . ServiceAccountUIDKey ] ) ,
2016-05-23 16:51:02 +00:00
}
}
func parseSecretQueueKey ( key interface { } ) ( secretQueueKey , error ) {
queueKey , ok := key . ( secretQueueKey )
if ! ok || len ( queueKey . namespace ) == 0 || len ( queueKey . name ) == 0 || len ( queueKey . uid ) == 0 || len ( queueKey . saName ) == 0 {
return secretQueueKey { } , fmt . Errorf ( "invalid secret key: %#v" , key )
}
return queueKey , nil
}
// produce the same key format as cache.MetaNamespaceKeyFunc
func makeCacheKey ( namespace , name string ) string {
return namespace + "/" + name
}