@@ -18,6 +18,7 @@ package blob
18
18
19
19
import (
20
20
"context"
21
+ "encoding/json"
21
22
"errors"
22
23
"flag"
23
24
"fmt"
@@ -155,6 +156,9 @@ const (
155
156
FSGroupChangeNone = "None"
156
157
// define tag value delimiter and default is comma
157
158
tagValueDelimiterField = "tagvaluedelimiter"
159
+
160
+ DefaultTokenAudience = "api://AzureADTokenExchange" //nolint:gosec // G101 ignore this!
161
+
158
162
)
159
163
160
164
var (
@@ -567,14 +571,19 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
567
571
tenantID = d .cloud .TenantID
568
572
}
569
573
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
571
575
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
575
581
}
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
+
578
587
return rgName , accountName , accountKey , containerName , authEnv , err
579
588
}
580
589
@@ -1146,3 +1155,28 @@ func generateVolumeName(clusterName, pvName string, maxLength int) string {
1146
1155
}
1147
1156
return prefix + "-" + pvName
1148
1157
}
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