使用 UserDeleatedCredential 和 azure-sdk-for-go 的跨租户 Azure Blob Storage SAS 失败,并显示“签名不匹配”

问题描述 投票:0回答:1

我的基础设施设置如下:

有两个租户 - 租户 A 和租户 B。

在租户 A 中,我有一个包含 k8s 集群的资源组、托管身份、带有容器的存储帐户以及其他资源。在租户 B 中,我有一个仅包含托管标识的资源组和一个带有容器的存储帐户。

我可以成功地从租户 A 中的 k8s 集群中运行的应用程序写入租户 B 中的存储容器。我之所以能够做到这一点,是因为在租户 B 中,我将

Storage Account Contributor
Storage Blob Data Owner
角色授予了托管身份相同的资源组。然后,我创建了一个联合凭证,允许租户 A 中的 k8s 集群通过使用以下命令对租户 B 中的托管身份进行身份验证:

az identity federated-credential create \
  --name fc-x-tenant-test \
  --identity-name tenantB-managedIdentity \
  --resource-group tenantB-rg \
  --issuer <Tenant A AKS Cluster OIDC Provider URL> \
  --subject system:serviceaccount:default:<Tenant A Service Account name>

(我按照https://paulyu.dev/article/cross-tenant-workload-identity-on-aks/进行了设置 - 谢谢https://stackoverflow.com/users/21008480/pauldotyu! )

然后,使用 azure-sdk-for-go,创建一个客户端(我检查

err != nil
,但为了简单起见,此处省略)

url := fmt.Sprintf("https://%s.blob.core.windows.net", accountName)
opts := &azidentity.WorkloadIdentityCredentialOptions{
    TenantID: *tenantBID,
    ClientID: *tenantBmanagedIdentityClientID,
}
cred, err := azidentity.NewWorkloadIdentityCredential(opts)
client, err := azblob.NewClient(url, cred, nil)
serviceClient := client.ServiceClient()
containerClient := serviceClient.NewContainerClient(containerName)

从那里,我可以成功地读取和写入容器。注意:创建凭据时,我需要指定租户 B 的租户 ID 和托管身份客户端 ID,否则 SDK 将尝试使用租户 A(AKS 集群所在的租户)进行身份验证!


但是,当我尝试为客户端生成预签名 URL 以便能够将对象放入此容器时,它不起作用。签名的 URL 看起来像这样

https://<Tenant B Storage Account Name>.blob.core.windows.net/<Tenant B Container Name>/path/to/object/metadata.json?se=2024-05-03T01%3A33%3A10Z&sig=KL3t1EGRODo4%2FUUu5Irtaw11vXwIEpBkO2sYTqrTg5o%3D&sp=w&spr=https&sr=b&sv=2023-11-03
。 我得到了 403 错误

ErrorCode:AuthenticationFailed
authenticationerrordetail:Signature did not match

我使用以下代码创建预签名 URL(使用如上所示创建的

client
,以及租户 B 的租户 ID 和托管身份客户端 ID)。

serviceClient := client.ServiceClient()
now := time.Now().UTC().Add(-10 * time.Second)
expiry := now.Add(24 * time.Hour)
info := azblobService.KeyInfo{
    Start:  to.Ptr(now.UTC().Format(sas.TimeFormat)),
    Expiry: to.Ptr(expiry.UTC().Format(sas.TimeFormat)),
}
udc, err := serviceClient.GetUserDelegationCredential(ctx, info, nil)
sigVals := sas.BlobSignatureValues{
    Protocol:      sas.ProtocolHTTPS,
    ExpiryTime:    time.Now().Add(24 * time.Hour).UTC(),
    Permissions:   perm.String(),
    ContainerName: containerName,
    BlobName:      blobName,
}
query, err := sigVals.SignWithUserDelegation(udc)

然后我在生成签名 URL 时调用

query.Encode()

我能够记录

query
的值,如下所示。两个字段已被编辑,但描述描述了其中的值。

Query {
    version:2023-11-03 
    services: 
    resourceTypes: 
    protocol:https 
    startTime:{wall:0 ext:0 loc:<nil>} 
    expiryTime:{wall:151009920 ext:63850466819 loc:<nil>} 
    snapshotTime:{wall:0 ext:0 loc:<nil>} 
    ipRange:{Start:[] End:[]} 
    identifier: 
    resource:b 
    permissions:w 
    signature:bDcn0MCs1kGakVX9OOtKt/ApBllv68AJLZnwZK4g+5g= 
    cacheControl: contentDisposition: contentEncoding: contentLanguage: contentType: 
    signedOID:<Tenant B Managed Identity Object ID> 
    signedTID:<Tenant B Tenant ID> 
    signedStart:{wall:0 ext:63850380409 loc:<nil>} 
    signedService:b 
    signedExpiry:{wall:0 ext:63850466809 loc:<nil>} 
    signedVersion:2023-11-03 
    signedDirectoryDepth: authorizedObjectID: unauthorizedObjectID: correlationID: encryptionScope: stTimeFormat: seTimeFormat:
}

在这里我注意到过期时间看起来很奇怪。不过,我验证了

expiryTime
没问题,因为我能够使用共享访问密钥让 SAS 在租户 A 中正常工作,并且它的
expiryTime
就是这样设置的。

签署的开始/到期时间一开始也看起来不一样。所以我记录了这些值,它们看起来是正确的。然后我尝试看看如果我做到了,那么

signedStart
等于
{wall:0 ext:0 loc:<nil>}
会发生什么,但问题仍然存在。 (您可以在创建用户委托 SAS 中了解所有这些值的含义 。)

我还在

Tenant B
中启用了目标存储帐户的日志,并且我可以在
PutBlob
容器中看到成功的
GetBlob
DeleteBlob
$logs
调用(对于跨租户读/写)不要使用 SAS)。但是,我没有看到任何失败的 SAS PUT 不断被拒绝的日志。

编辑:更新,我现在已经在单个租户中重现了相同的问题

azure-active-directory azure-blob-storage azure-sdk-for-go
1个回答
0
投票

问题是,使用 UserDelegationCredential 签名时,我无法在

BlobName
中指定
BlobSignatureValues

但是,当我使用 SharedKeyCredential 并使用

SignWithSharedKey
进行签名时,您可以在那里指定 BlobName

© www.soinside.com 2019 - 2024. All rights reserved.