@@ -18,6 +18,7 @@ package blob
1818
1919import (
2020 "context"
21+ "encoding/json"
2122 "errors"
2223 "flag"
2324 "fmt"
@@ -156,6 +157,9 @@ const (
156157 FSGroupChangeNone = "None"
157158 // define tag value delimiter and default is comma
158159 tagValueDelimiterField = "tagvaluedelimiter"
160+
161+ DefaultTokenAudience = "api://AzureADTokenExchange" //nolint:gosec // G101 ignore this!
162+
159163)
160164
161165var (
@@ -568,14 +572,19 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
568572 tenantID = d .cloud .TenantID
569573 }
570574
571- // if client id is specified, we only use service account token to get account key
575+ // if client id is specified, we only use workload identity for blobfuse auth
572576 if clientID != "" {
573- klog .V (2 ).Infof ("clientID(%s) is specified, use service account token to get account key" , clientID )
574- if subsID == "" {
575- subsID = d .cloud .SubscriptionID
577+ klog .V (2 ).Infof ("clientID(%s) is specified, use workload identity for blobfuse auth" , clientID )
578+
579+ workload_identity_token , err := parseServiceAccountToken (serviceAccountToken )
580+ if err != nil {
581+ return rgName , accountName , accountKey , containerName , authEnv , err
576582 }
577- accountKey , err := d .cloud .GetStorageAccesskeyFromServiceAccountToken (ctx , subsID , accountName , rgName , clientID , tenantID , serviceAccountToken )
578- authEnv = append (authEnv , "AZURE_STORAGE_ACCESS_KEY=" + accountKey )
583+
584+ authEnv = append (authEnv , "AZURE_STORAGE_SPN_CLIENT_ID=" + clientID )
585+ authEnv = append (authEnv , "AZURE_STORAGE_SPN_TENANT_ID=" + tenantID )
586+ authEnv = append (authEnv , "WORKLOAD_IDENTITY_TOKEN=" + workload_identity_token )
587+
579588 return rgName , accountName , accountKey , containerName , authEnv , err
580589 }
581590
@@ -1136,3 +1145,28 @@ func generateVolumeName(clusterName, pvName string, maxLength int) string {
11361145 }
11371146 return prefix + "-" + pvName
11381147}
1148+
1149+ // serviceAccountToken represents the service account token sent from NodePublishVolume Request.
1150+ // ref: https://kubernetes-csi.github.io/docs/token-requests.html
1151+ type serviceAccountToken struct {
1152+ APIAzureADTokenExchange struct {
1153+ Token string `json:"token"`
1154+ ExpirationTimestamp time.Time `json:"expirationTimestamp"`
1155+ } `json:"api://AzureADTokenExchange"`
1156+ }
1157+
1158+ // parseServiceAccountToken parses the bound service account token from the token passed from NodePublishVolume Request.
1159+ // ref: https://kubernetes-csi.github.io/docs/token-requests.html
1160+ func parseServiceAccountToken (tokenStr string ) (string , error ) {
1161+ if len (tokenStr ) == 0 {
1162+ return "" , fmt .Errorf ("service account token is empty" )
1163+ }
1164+ token := serviceAccountToken {}
1165+ if err := json .Unmarshal ([]byte (tokenStr ), & token ); err != nil {
1166+ return "" , fmt .Errorf ("failed to unmarshal service account tokens, error: %w" , err )
1167+ }
1168+ if token .APIAzureADTokenExchange .Token == "" {
1169+ return "" , fmt .Errorf ("token for audience %s not found" , DefaultTokenAudience )
1170+ }
1171+ return token .APIAzureADTokenExchange .Token , nil
1172+ }
0 commit comments