Skip to content

Commit 452606b

Browse files
authored
Merge pull request #1798 from cvvz/support-workload-identity
feat: support workload identity
2 parents 59e7a96 + 21fb0d0 commit 452606b

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

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

+1-1
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

+40-6
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"
@@ -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

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

Comments
 (0)