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  }