@@ -18,6 +18,7 @@ package blob
1818
1919import (
2020 "context"
21+ "encoding/json"
2122 "errors"
2223 "flag"
2324 "fmt"
@@ -155,6 +156,9 @@ const (
155156 FSGroupChangeNone = "None"
156157 // define tag value delimiter and default is comma
157158 tagValueDelimiterField = "tagvaluedelimiter"
159+
160+ DefaultTokenAudience = "api://AzureADTokenExchange" //nolint:gosec // G101 ignore this!
161+
158162)
159163
160164var (
@@ -567,14 +571,19 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
567571 tenantID = d .cloud .TenantID
568572 }
569573
570- // if client id is specified, we only use service account token to get account key
574+ // if client id is specified, we only use workload identity for blobfuse auth
571575 if clientID != "" {
572- klog .V (2 ).Infof ("clientID(%s) is specified, use service account token to get account key" , clientID )
573- if subsID == "" {
574- subsID = d .cloud .SubscriptionID
576+ klog .V (2 ).Infof ("clientID(%s) is specified, use workload identity for blobfuse auth" , clientID )
577+
578+ workloadIdentityToken , err := parseServiceAccountToken (serviceAccountToken )
579+ if err != nil {
580+ return rgName , accountName , accountKey , containerName , authEnv , err
575581 }
576- accountKey , err := d .cloud .GetStorageAccesskeyFromServiceAccountToken (ctx , subsID , accountName , rgName , clientID , tenantID , serviceAccountToken )
577- authEnv = append (authEnv , "AZURE_STORAGE_ACCESS_KEY=" + accountKey )
582+
583+ authEnv = append (authEnv , "AZURE_STORAGE_SPN_CLIENT_ID=" + clientID )
584+ authEnv = append (authEnv , "AZURE_STORAGE_SPN_TENANT_ID=" + tenantID )
585+ authEnv = append (authEnv , "WORKLOAD_IDENTITY_TOKEN=" + workloadIdentityToken )
586+
578587 return rgName , accountName , accountKey , containerName , authEnv , err
579588 }
580589
@@ -1146,3 +1155,28 @@ func generateVolumeName(clusterName, pvName string, maxLength int) string {
11461155 }
11471156 return prefix + "-" + pvName
11481157}
1158+
1159+ // serviceAccountToken represents the service account token sent from NodePublishVolume Request.
1160+ // ref: https://kubernetes-csi.github.io/docs/token-requests.html
1161+ type serviceAccountToken struct {
1162+ APIAzureADTokenExchange struct {
1163+ Token string `json:"token"`
1164+ ExpirationTimestamp time.Time `json:"expirationTimestamp"`
1165+ } `json:"api://AzureADTokenExchange"`
1166+ }
1167+
1168+ // parseServiceAccountToken parses the bound service account token from the token passed from NodePublishVolume Request.
1169+ // ref: https://kubernetes-csi.github.io/docs/token-requests.html
1170+ func parseServiceAccountToken (tokenStr string ) (string , error ) {
1171+ if len (tokenStr ) == 0 {
1172+ return "" , fmt .Errorf ("service account token is empty" )
1173+ }
1174+ token := serviceAccountToken {}
1175+ if err := json .Unmarshal ([]byte (tokenStr ), & token ); err != nil {
1176+ return "" , fmt .Errorf ("failed to unmarshal service account tokens, error: %w" , err )
1177+ }
1178+ if token .APIAzureADTokenExchange .Token == "" {
1179+ return "" , fmt .Errorf ("token for audience %s not found" , DefaultTokenAudience )
1180+ }
1181+ return token .APIAzureADTokenExchange .Token , nil
1182+ }
0 commit comments