github.com/Azure/aad-pod-identity@v1.8.17/test/image/identityvalidator/keyvault.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault"
     9  	"github.com/Azure/azure-sdk-for-go/services/keyvault/auth"
    10  	"github.com/Azure/go-autorest/autorest"
    11  	"github.com/Azure/go-autorest/autorest/adal"
    12  	"k8s.io/klog/v2"
    13  )
    14  
    15  const (
    16  	keyvaultResource = "https://vault.azure.net"
    17  )
    18  
    19  type keyvaultTester struct {
    20  	client             keyvault.BaseClient
    21  	subscriptionID     string
    22  	identityClientID   string
    23  	identityResourceID string
    24  	keyvaultName       string
    25  	secretName         string
    26  	secretVersion      string
    27  	secretValue        string
    28  }
    29  
    30  // assertWithIdentityClientID obtains the secret value from a keyvault using
    31  // aad-pod-identity and check if is the same as the expected secret values.
    32  func (kvt *keyvaultTester) assertWithIdentityClientID() error {
    33  	if kvt.identityClientID == "" {
    34  		return nil
    35  	}
    36  
    37  	// When new authorizer is created, azure-sdk-for-go  tries to create data plane authorizer using MSI. It checks the AZURE_CLIENT_ID to get the client id
    38  	// for the user assigned identity. If client id not found, then NewServicePrincipalTokenFromMSI is invoked instead of using the actual
    39  	// user assigned identity. Setting this env var ensures we validate GetSecret using the desired user assigned identity.
    40  	if err := os.Setenv("AZURE_CLIENT_ID", kvt.identityClientID); err != nil {
    41  		return fmt.Errorf("failed to set AZURE_CLIENT_ID environment variable, error: %+v", err)
    42  	}
    43  	defer os.Unsetenv("AZURE_CLIENT_ID")
    44  
    45  	authorizer, err := auth.NewAuthorizerFromEnvironment()
    46  	if err != nil {
    47  		return fmt.Errorf("failed to generate a new authorizer from environment, error: %+v", err)
    48  	}
    49  
    50  	klog.Infof("added authorizer with clientID: %s", kvt.identityClientID)
    51  
    52  	secret, err := kvt.getSecret(authorizer)
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	if err := kvt.assertSecret(secret); err != nil {
    58  		return err
    59  	}
    60  
    61  	klog.Info("successfully verified user-assigned identity on pod with identity client ID")
    62  	return nil
    63  }
    64  
    65  // assertWithIdentityResourceID obtains the secret value from a keyvault using
    66  // aad-pod-identity and check if is the same as the expected secret values.
    67  func (kvt *keyvaultTester) assertWithIdentityResourceID() error {
    68  	if kvt.identityResourceID == "" {
    69  		return nil
    70  	}
    71  
    72  	token, err := kvt.getADALTokenWithIdentityResourceID()
    73  	if err != nil {
    74  		return fmt.Errorf("failed to get ADAL token with identity resource ID, error: %+v", err)
    75  	}
    76  
    77  	klog.Infof("added authorizer with resource ID: %s", kvt.identityResourceID)
    78  
    79  	secret, err := kvt.getSecret(autorest.NewBearerAuthorizer(token))
    80  	if err != nil {
    81  		return err
    82  	}
    83  
    84  	if err := kvt.assertSecret(secret); err != nil {
    85  		return err
    86  	}
    87  
    88  	klog.Info("successfully verified user-assigned identity on pod with identity resource ID")
    89  	return nil
    90  }
    91  
    92  // assertSecret checks if kvt.secretValue == actualSecret.
    93  func (kvt *keyvaultTester) assertSecret(actualSecret string) error {
    94  	if kvt.secretValue != actualSecret {
    95  		return fmt.Errorf("expected %s to be equal to %s", actualSecret, kvt.secretValue)
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  // getSecret returns the secret value with a specific autorest authorizer.
   102  func (kvt *keyvaultTester) getSecret(authorizer autorest.Authorizer) (string, error) {
   103  	kvt.client.Authorizer = authorizer
   104  
   105  	ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
   106  	defer cancel()
   107  
   108  	secret, err := kvt.client.GetSecret(ctx, kvt.getKeyvaultURL(), kvt.secretName, kvt.secretVersion)
   109  	if err != nil {
   110  		return "", fmt.Errorf("failed to get secret, error: %+v", err)
   111  	}
   112  
   113  	return *secret.Value, nil
   114  }
   115  
   116  // getKeyvaultURL returns the FQDN of the Azure Key Vault.
   117  func (kvt *keyvaultTester) getKeyvaultURL() string {
   118  	return fmt.Sprintf("https://%s.vault.azure.net", kvt.keyvaultName)
   119  }
   120  
   121  // getADALTokenWithIdentityResourceID returns an ADAL token
   122  // using the resource ID of a user-assigned identity.
   123  func (kvt *keyvaultTester) getADALTokenWithIdentityResourceID() (*adal.Token, error) {
   124  	managedIdentityOptions := &adal.ManagedIdentityOptions{IdentityResourceID: kvt.identityResourceID}
   125  	spt, err := adal.NewServicePrincipalTokenFromManagedIdentity(keyvaultResource, managedIdentityOptions)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  	err = spt.Refresh()
   130  	if err != nil {
   131  		return nil, err
   132  	}
   133  	token := spt.Token()
   134  	return &token, nil
   135  }