Skip to content

Commit 168ef4c

Browse files
authored
Merge pull request #1889 from k8s-infra-cherrypick-robot/cherry-pick-1798-to-release-1.25
[release-1.25] feat: support workload identity
2 parents 14c2b36 + a02013b commit 168ef4c

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"
@@ -156,6 +157,9 @@ const (
156157
FSGroupChangeNone = "None"
157158
// define tag value delimiter and default is comma
158159
tagValueDelimiterField = "tagvaluedelimiter"
160+
161+
DefaultTokenAudience = "api://AzureADTokenExchange" //nolint:gosec // G101 ignore this!
162+
159163
)
160164

161165
var (
@@ -568,14 +572,19 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
568572
tenantID = d.cloud.TenantID
569573
}
570574

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
572576
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
576582
}
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+
579588
return rgName, accountName, accountKey, containerName, authEnv, err
580589
}
581590

@@ -1136,3 +1145,28 @@ func generateVolumeName(clusterName, pvName string, maxLength int) string {
11361145
}
11371146
return prefix + "-" + pvName
11381147
}
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

Comments
 (0)