Skip to content

Commit e7bf9b3

Browse files
committed
feat: support workload identity for blobfuse2
1 parent b22b084 commit e7bf9b3

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

docs/workload-identity-static-pv-mount.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export IDENTITY_TENANT=$(az aks show --name $CLUSTER_NAME --resource-group $RESO
3333
export ACCOUNT_SCOPE=$(az storage account show --name $ACCOUNT --query id -o tsv)
3434
3535
# please retry if you meet `Cannot find user or service principal in graph database` error, it may take a while for the identity to propagate
36-
az role assignment create --role "Storage Account Contributor" --assignee $USER_ASSIGNED_CLIENT_ID --scope $ACCOUNT_SCOPE
36+
az role assignment create --role "Storage Blob Data Contributor" --assignee $USER_ASSIGNED_CLIENT_ID --scope $ACCOUNT_SCOPE
3737
```
3838

3939
### 4. Create service account on AKS

pkg/blob/blob.go

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package blob
1818

1919
import (
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

162166
var (
@@ -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

Comments
 (0)