@@ -18,6 +18,7 @@ package blob
1818
1919import (
2020 "context"
21+ "encoding/json"
2122 "errors"
2223 "flag"
2324 "fmt"
@@ -157,6 +158,9 @@ const (
157158 FSGroupChangeNone = "None"
158159 // define tag value delimiter and default is comma
159160 tagValueDelimiterField = "tagValueDelimiter"
161+
162+ DefaultTokenAudience = "api://AzureADTokenExchange" //nolint:gosec // G101 ignore this!
163+
160164)
161165
162166var (
@@ -564,14 +568,19 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
564568 tenantID = d .cloud .TenantID
565569 }
566570
567- // if client id is specified, we only use service account token to get account key
571+ // if client id is specified, we only use workload identity for blobfuse auth
568572 if clientID != "" {
569- klog .V (2 ).Infof ("clientID(%s) is specified, use service account token to get account key" , clientID )
570- if subsID == "" {
571- subsID = d .cloud .SubscriptionID
573+ klog .V (2 ).Infof ("clientID(%s) is specified, use workload identity for blobfuse auth" , clientID )
574+
575+ workload_identity_token , err := parseServiceAccountToken (serviceAccountToken )
576+ if err != nil {
577+ return rgName , accountName , accountKey , containerName , authEnv , err
572578 }
573- accountKey , err := d .cloud .GetStorageAccesskeyFromServiceAccountToken (ctx , subsID , accountName , rgName , clientID , tenantID , serviceAccountToken )
574- authEnv = append (authEnv , "AZURE_STORAGE_ACCESS_KEY=" + accountKey )
579+
580+ authEnv = append (authEnv , "AZURE_STORAGE_SPN_CLIENT_ID=" + clientID )
581+ authEnv = append (authEnv , "AZURE_STORAGE_SPN_TENANT_ID=" + tenantID )
582+ authEnv = append (authEnv , "WORKLOAD_IDENTITY_TOKEN=" + workload_identity_token )
583+
575584 return rgName , accountName , accountKey , containerName , authEnv , err
576585 }
577586
@@ -1117,3 +1126,28 @@ func isReadOnlyFromCapability(vc *csi.VolumeCapability) bool {
11171126 return (mode == csi .VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY ||
11181127 mode == csi .VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY )
11191128}
1129+
1130+ // serviceAccountToken represents the service account token sent from NodePublishVolume Request.
1131+ // ref: https://kubernetes-csi.github.io/docs/token-requests.html
1132+ type serviceAccountToken struct {
1133+ APIAzureADTokenExchange struct {
1134+ Token string `json:"token"`
1135+ ExpirationTimestamp time.Time `json:"expirationTimestamp"`
1136+ } `json:"api://AzureADTokenExchange"`
1137+ }
1138+
1139+ // parseServiceAccountToken parses the bound service account token from the token passed from NodePublishVolume Request.
1140+ // ref: https://kubernetes-csi.github.io/docs/token-requests.html
1141+ func parseServiceAccountToken (tokenStr string ) (string , error ) {
1142+ if len (tokenStr ) == 0 {
1143+ return "" , fmt .Errorf ("service account token is empty" )
1144+ }
1145+ token := serviceAccountToken {}
1146+ if err := json .Unmarshal ([]byte (tokenStr ), & token ); err != nil {
1147+ return "" , fmt .Errorf ("failed to unmarshal service account tokens, error: %w" , err )
1148+ }
1149+ if token .APIAzureADTokenExchange .Token == "" {
1150+ return "" , fmt .Errorf ("token for audience %s not found" , DefaultTokenAudience )
1151+ }
1152+ return token .APIAzureADTokenExchange .Token , nil
1153+ }
0 commit comments