github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/lib/scoped/token_retriever.go (about) 1 package scoped 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" 8 "github.com/sirupsen/logrus" 9 authv1 "k8s.io/api/authentication/v1" 10 corev1 "k8s.io/api/core/v1" 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 ) 13 14 // BearerTokenRetriever retrieves bearer token from a service account. 15 type BearerTokenRetriever struct { 16 kubeclient operatorclient.ClientInterface 17 logger logrus.FieldLogger 18 } 19 20 // Retrieve returns the bearer token for API access from a given service account reference. 21 func (r *BearerTokenRetriever) Retrieve(reference *corev1.ObjectReference) (token string, err error) { 22 logger := r.logger.WithFields(logrus.Fields{ 23 "sa": reference.Name, 24 "namespace": reference.Namespace, 25 logFieldName: logFieldValue, 26 }) 27 28 sa, err := r.kubeclient.KubernetesInterface().CoreV1().ServiceAccounts(reference.Namespace).Get(context.TODO(), reference.Name, metav1.GetOptions{}) 29 if err != nil { 30 return 31 } 32 33 secret, err := getAPISecret(logger, r.kubeclient, sa) 34 if err != nil { 35 err = fmt.Errorf("error occurred while retrieving API secret associated with the service account sa=%s/%s - %v", sa.GetNamespace(), sa.GetName(), err) 36 return 37 } 38 39 if secret == nil { 40 token, err = requestSAToken(r.kubeclient, sa) 41 if err != nil { 42 err = fmt.Errorf("creating service account token from TokenRequest API for sa=%s/%s; %v", 43 sa.GetNamespace(), 44 sa.GetName(), 45 err, 46 ) 47 } 48 return 49 } 50 51 token = string(secret.Data[corev1.ServiceAccountTokenKey]) 52 if token == "" { 53 err = fmt.Errorf("the secret does not have any API token sa=%s/%s secret=%s", sa.GetNamespace(), sa.GetName(), secret.GetName()) 54 } 55 56 return 57 } 58 59 // requestSAToken requests for a service account token from the Kubernetes API server whenever the Operator 60 // Lifecycle manager is unable to find a service account token secret 61 func requestSAToken(kubeclient operatorclient.ClientInterface, sa *corev1.ServiceAccount) (string, error) { 62 req := new(authv1.TokenRequest) 63 req, err := kubeclient.KubernetesInterface(). 64 CoreV1().ServiceAccounts(sa.GetNamespace()). 65 CreateToken(context.Background(), sa.GetName(), req, metav1.CreateOptions{}) 66 if err != nil { 67 return "", err 68 } 69 70 return req.Status.Token, nil 71 } 72 73 func getAPISecret(logger logrus.FieldLogger, kubeclient operatorclient.ClientInterface, sa *corev1.ServiceAccount) (APISecret *corev1.Secret, err error) { 74 seList, err := kubeclient.KubernetesInterface().CoreV1().Secrets(sa.GetNamespace()).List(context.TODO(), metav1.ListOptions{}) 75 if err != nil { 76 logger.Errorf("unable to retrieve list of secrets in the namespace %s - %v", sa.GetNamespace(), err) 77 return nil, err 78 } 79 secrets := filterSecretsBySAName(sa.Name, seList) 80 81 for _, ref := range sa.Secrets { 82 if _, ok := secrets[ref.Name]; !ok { 83 logger.Warnf("skipping secret %s: secret not found", ref.Name) 84 continue 85 } 86 } 87 88 for _, secret := range secrets { 89 // Validate that this is a token for API access. 90 if !IsServiceAccountToken(secret, sa) { 91 logger.Warnf("skipping secret %s: not token secret", secret.Name) 92 continue 93 } 94 // The first eligible secret that has an API access token is returned. 95 APISecret = secret 96 break 97 } 98 99 return 100 } 101 102 // filterSecretsBySAName returna a maps of secrets that are associated with a 103 // specific ServiceAccount via annotations kubernetes.io/service-account.name 104 func filterSecretsBySAName(saName string, secrets *corev1.SecretList) map[string]*corev1.Secret { 105 secretMap := make(map[string]*corev1.Secret) 106 for _, ref := range secrets.Items { 107 // Avoid referencing the "ref" created by the range-for loop as 108 // the secret stored in the map will change if there are more 109 // entries in the list of secrets that the range-for loop is 110 // iterating over. 111 ref := ref 112 113 annotations := ref.GetAnnotations() 114 value := annotations[corev1.ServiceAccountNameKey] 115 if value == saName { 116 secretMap[ref.Name] = &ref 117 } 118 } 119 120 return secretMap 121 }