@@ -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"
@@ -156,6 +157,9 @@ const (
156
157
FSGroupChangeNone = "None"
157
158
// define tag value delimiter and default is comma
158
159
tagValueDelimiterField = "tagvaluedelimiter"
160
+
161
+ DefaultTokenAudience = "api://AzureADTokenExchange" //nolint:gosec // G101 ignore this!
162
+
159
163
)
160
164
161
165
var (
@@ -568,14 +572,19 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
568
572
tenantID = d .cloud .TenantID
569
573
}
570
574
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
572
576
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
+ workloadIdentityToken , err := parseServiceAccountToken (serviceAccountToken )
580
+ if err != nil {
581
+ return rgName , accountName , accountKey , containerName , authEnv , err
576
582
}
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=" + workloadIdentityToken )
587
+
579
588
return rgName , accountName , accountKey , containerName , authEnv , err
580
589
}
581
590
@@ -1136,3 +1145,28 @@ func generateVolumeName(clusterName, pvName string, maxLength int) string {
1136
1145
}
1137
1146
return prefix + "-" + pvName
1138
1147
}
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