Skip to content

Commit

Permalink
feat: support workload identity for blobfuse2
Browse files Browse the repository at this point in the history
  • Loading branch information
cvvz committed Nov 4, 2024
1 parent b22b084 commit e7bf9b3
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 7 deletions.
2 changes: 1 addition & 1 deletion docs/workload-identity-static-pv-mount.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export IDENTITY_TENANT=$(az aks show --name $CLUSTER_NAME --resource-group $RESO
export ACCOUNT_SCOPE=$(az storage account show --name $ACCOUNT --query id -o tsv)
# 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
az role assignment create --role "Storage Account Contributor" --assignee $USER_ASSIGNED_CLIENT_ID --scope $ACCOUNT_SCOPE
az role assignment create --role "Storage Blob Data Contributor" --assignee $USER_ASSIGNED_CLIENT_ID --scope $ACCOUNT_SCOPE
```

### 4. Create service account on AKS
Expand Down
46 changes: 40 additions & 6 deletions pkg/blob/blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package blob

import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
Expand Down Expand Up @@ -157,6 +158,9 @@ const (
FSGroupChangeNone = "None"
// define tag value delimiter and default is comma
tagValueDelimiterField = "tagValueDelimiter"

DefaultTokenAudience = "api://AzureADTokenExchange" //nolint:gosec // G101 ignore this!

)

var (
Expand Down Expand Up @@ -564,14 +568,19 @@ func (d *Driver) GetAuthEnv(ctx context.Context, volumeID, protocol string, attr
tenantID = d.cloud.TenantID
}

// if client id is specified, we only use service account token to get account key
// if client id is specified, we only use workload identity for blobfuse auth
if clientID != "" {
klog.V(2).Infof("clientID(%s) is specified, use service account token to get account key", clientID)
if subsID == "" {
subsID = d.cloud.SubscriptionID
klog.V(2).Infof("clientID(%s) is specified, use workload identity for blobfuse auth", clientID)

workload_identity_token, err := parseServiceAccountToken(serviceAccountToken)

Check failure on line 575 in pkg/blob/blob.go

View workflow job for this annotation

GitHub Actions / Go Lint

var-naming: don't use underscores in Go names; var workload_identity_token should be workloadIdentityToken (revive)
if err != nil {
return rgName, accountName, accountKey, containerName, authEnv, err
}
accountKey, err := d.cloud.GetStorageAccesskeyFromServiceAccountToken(ctx, subsID, accountName, rgName, clientID, tenantID, serviceAccountToken)
authEnv = append(authEnv, "AZURE_STORAGE_ACCESS_KEY="+accountKey)

authEnv = append(authEnv, "AZURE_STORAGE_SPN_CLIENT_ID="+clientID)
authEnv = append(authEnv, "AZURE_STORAGE_SPN_TENANT_ID="+tenantID)
authEnv = append(authEnv, "WORKLOAD_IDENTITY_TOKEN="+workload_identity_token)

return rgName, accountName, accountKey, containerName, authEnv, err
}

Expand Down Expand Up @@ -1117,3 +1126,28 @@ func isReadOnlyFromCapability(vc *csi.VolumeCapability) bool {
return (mode == csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY ||
mode == csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY)
}

// serviceAccountToken represents the service account token sent from NodePublishVolume Request.
// ref: https://kubernetes-csi.github.io/docs/token-requests.html
type serviceAccountToken struct {
APIAzureADTokenExchange struct {
Token string `json:"token"`
ExpirationTimestamp time.Time `json:"expirationTimestamp"`
} `json:"api://AzureADTokenExchange"`
}

// parseServiceAccountToken parses the bound service account token from the token passed from NodePublishVolume Request.
// ref: https://kubernetes-csi.github.io/docs/token-requests.html
func parseServiceAccountToken(tokenStr string) (string, error) {
if len(tokenStr) == 0 {
return "", fmt.Errorf("service account token is empty")
}
token := serviceAccountToken{}
if err := json.Unmarshal([]byte(tokenStr), &token); err != nil {
return "", fmt.Errorf("failed to unmarshal service account tokens, error: %w", err)
}
if token.APIAzureADTokenExchange.Token == "" {
return "", fmt.Errorf("token for audience %s not found", DefaultTokenAudience)
}
return token.APIAzureADTokenExchange.Token, nil
}

0 comments on commit e7bf9b3

Please sign in to comment.