使用服务主体访问Azure Blob存储

时间:2019-09-18 14:26:05

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

我想使用活动目录服务主体的凭据从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>

有什么想法吗?

2 个答案:

答案 0 :(得分:2)

如果要使用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)

结果:

enter image description here

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

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

答案 1 :(得分:2)

使用Python库azure-storage-blob 12.2和azure-identity(截至2020年1月的最新API) (根据杰克·利亚的回答改编)

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())