【问题标题】:Using service principal to access Azure blob storage使用服务主体访问 Azure Blob 存储
【发布时间】:2019-09-18 14:26:05
【问题描述】:

我想使用来自活动目录服务主体的凭据从 python 访问私有 blob 存储。

我知道这个相关问题How do I authenticate a user against an Azure storage blob in python? 这帮助我走到了这一步,但现在我被困住了。

我可以进行身份​​验证并获得一个令牌,它可以让我列出容器、创建新容器,但不会让我列出或访问任何 blob。

我希望通过az cli 进行设置。

服务主体的设置如下:

az ad sp create-for-rbac -n "http://$NAME" --role Contributor \
    --scopes "/subscriptions/$SUB_ID/resourceGroups/$RESOURCE_GROUP" 

我认为应该提供完全访问权限,但我也添加了这个以确保:

az role assignment create \
    --role "Storage Blob Data Contributor" \
    --assignee-object-id "$OBJECT_ID" \
    --assignee-principal-type "ServicePrincipal" \
    --scope "/subscriptions/$SUB_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT/blobServices/default/containers/$CONTAINER"

然后我像这样进行身份验证:

from azure.common.credentials import ServicePrincipalCredentials
import adal
from azure.storage.blob import (
    BlockBlobService,
    ContainerPermissions,
)
from azure.storage.common import (
    TokenCredential
)

# Tenant ID for your Azure Subscription
TENANT_ID = TENANT

# Your Service Principal App ID
CLIENT = APP_ID

# Your Service Principal Password
KEY = PASSWORD

# RESOURCE = "https://storage.azure.com/" # using this resource has the same behaviour as uncommented one. Using no resource fails with authentication errors
RESOURCE = f"https://{ACCOUNT_NAME}.blob.core.windows.net"

credentials = ServicePrincipalCredentials(
    client_id = CLIENT,
    secret = KEY,
    tenant = TENANT_ID,
    resource = RESOURCE
)
tokenCre = TokenCredential(credentials.token["access_token"])

然后我尝试使用 blob 服务

blobService = BlockBlobService(account_name=ACCOUNT_NAME, token_credential=tokenCre)

print ([c.name for c in blobService.list_containers()]) # successfully lists containers
print(blobService.create_container('test')) # prints "True" and the container is created


blobService.list_blobs(CONTAINER_NAME) # fails with AzureHttpError: This request is not authorized to perform this operation using this permission. ErrorCode: AuthorizationPermissionMismatch 

blobService.get_blob_to_bytes("test", "hello.txt") # fails with same error

如上面的代码块所示,我似乎能够执行“容器”级别的操作,但不能执行“blob”级别的操作。像列出 blob、读取 blob 等任何事情都会出错:

AzureHttpError: Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. ErrorCode: AuthenticationFailed
<?xml version="1.0" encoding="utf-8"?><Error><Code>AuthenticationFailed</Code><Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:86ff0241-c01e-00d4-512c-6e22b5000000
Time:2019-09-18T14:20:23.5619727Z</Message><AuthenticationErrorDetail>Audience validation failed. Audience did not match.</AuthenticationErrorDetail></Error>

有什么想法吗?

【问题讨论】:

    标签: azure azure-active-directory azure-blob-storage


    【解决方案1】:

    如果您想使用 Azure AD 访问令牌来访问您的 Azure 存储,您必须为您的资源分配必要的存储 ** 角色(您在第二个 az 命令中执行了此操作)。

    但是,在您的第二个 az 命令中:

    az role assignment create \
        --role "Storage Blob Data Contributor" \
        --assignee-object-id "$OBJECT_ID" \
        --assignee-principal-type "ServicePrincipal" \
        --scope "/subscriptions/$SUB_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Storage/storageAccounts/$STORAGE_ACCOUNT/blobServices/default/containers/$CONTAINER"
    

    您只需将范围设置为特定容器:$CONTAINER。因此,您只能访问该容器下的 blob。

    我测试了,并且成功了。

    from azure.common.credentials import ServicePrincipalCredentials
    import adal
    from azure.storage.blob import (
        BlockBlobService,
        ContainerPermissions,
    )
    from azure.storage.common import (
        TokenCredential
    )
    
    # Tenant ID for your Azure Subscription
    TENANT_ID = "e4c9****-****-****-****-230b****57fb"
    
    # Your Service Principal App ID
    CLIENT = "3bee****-****-****-****-b0b8****f7a4"
    
    # Your Service Principal Password
    KEY = "*******************"
    
    ACCOUNT_NAME = "storagetest789"
    
    CONTAINER_NAME = "newcontainer"
    
    RESOURCE = "https://storage.azure.com/"
    
    credentials = ServicePrincipalCredentials(
        client_id = CLIENT,
        secret = KEY,
        tenant = TENANT_ID,
        resource = RESOURCE
    )
    tokenCre = TokenCredential(credentials.token["access_token"])
    blobService = BlockBlobService(account_name=ACCOUNT_NAME, token_credential=tokenCre)
    
    print("\nList blobs in the container")
    generator = blobService.list_blobs(CONTAINER_NAME)
    for blob in generator:
        print("\t Blob name: " + blob.name)
    
    print("\nOutput test.txt")
    blob = blobService.get_blob_to_text(CONTAINER_NAME, "test.txt")
    print(blob.content)
    

    结果:

    如果我尝试访问其他容器,我会收到与您相同的错误。但我不知道为什么允许容器创建操作。这似乎是对访问控制的疏忽。

    如果您想管理整个存储帐户,则需要将storage account scope 分配给您的服务主体。然后,您可以访问该存储帐户中的其他容器。

    【讨论】:

    • 非常感谢。这确实有效,我认为在所有步骤中的某个地方我一直在分配对错误容器或错误服务主体等的访问权限。
    • 我有点惊讶az ad sp create-for-rbac -n "http://$NAME" --role Contributor \ --scopes "/subscriptions/$SUB_ID/resourceGroups/$RESOURCE_GROUP" 还不够。感觉它应该授予$$RESOURCE_GROUP 下的所有内容
    【解决方案2】:

    使用 Python 库 azure-storage-blob 12.2 和 azure-identity(截至 2020 年 1 月的最新 API) (Jack Lia 回答的修改版)

    from azure.identity import ClientSecretCredential
    from azure.storage.blob import BlobServiceClient
    
    # Tenant ID for your Azure Subscription
    TENANT_ID = "e4c9****-****-****-****-230b****57fb"
    
    # Your Service Principal App ID
    CLIENT = "3bee****-****-****-****-b0b8****f7a4"
    
    # Your Service Principal Password
    KEY = "*******************"
    
    ACCOUNT_NAME = "storagetest789"
    
    CONTAINER_NAME = "newcontainer"
    
    RESOURCE = "https://storage.azure.com/"
    
    credentials = ClientSecretCredential(TENANT_ID, CLIENT, KEY)
    blobService = BlobServiceClient(
       "https://{}.blob.core.windows.net".format(ACCOUNT_NAME),
       credential=credentials
    )
    
    print("\nList blobs in the container")
    
    container = blobService.get_container_client(CONTAINER_NAME)
    for blob in container.list_blobs():
        print("\t Blob name: " + blob.name)
    
    print("\nOutput test.txt")
    blob = container.get_blob_client("test.txt")
    print(blob.download_blob().readall())
    

    【讨论】:

    • 我有所有的模块,但是找不到模块错误。也是 azure-storage-blob 的 12.2 版吗?我使用pip install azure-storage-blob安装时看不到它
    • 确保您没有安装“azure-storage”包。如果同时安装了“azure-storage”和“azure-storage-blob”,则它们将不起作用。
    • 我卸载了azure 包并单独安装了提到的包......这成功了。现在我想要一种方法来下载容器路径中的所有 blob,比如 storagetest789/test/docs 保留路径结构,我需要先创建路径然后复制 blob 吗?!?还是有一种简单的方法来复制整个容器路径...
    猜你喜欢
    • 2021-06-15
    • 2016-08-23
    • 2012-09-30
    • 2013-01-28
    • 2018-11-12
    • 1970-01-01
    • 2015-08-13
    • 1970-01-01
    • 2013-12-04
    相关资源
    最近更新 更多