繁体   English   中英

如何使用 Python 和托管标识/SAS 凭据从 VM 访问 Azure 存储帐户

[英]How to access Azure storage account from VM using Python and managed identity / SAS credentials

目标我有一个 Azure VM 设置了系统分配的托管标识。 我希望能够:

  1. 允许用户使用 VM 访问存储帐户内的 blob

  2. 确保用户无法访问 VM 外部的 blob

  3. 使用 Python - 我们的大多数用户都使用 Python,但不了解 Powershell。

设置详细信息:存储帐户: sa030802util。 容器: testutils。 Blob: hello3.txt托管身份和角色。 VM 具有为 sa030802util 分配了托管标识和参与者、存储帐户参与者、存储 blob 数据参与者角色的系统。

方法我尝试了四种方法来解决这个问题。

部分成功的方法1:Python。 在 Python 中,我已经能够使用以下代码访问 sa030802util 存储帐户,该代码源自linklinklink 问题在于,这使用存储帐户和密钥,而不是仅依赖于 VM 的托管标识。 我担心这可能会导致用户提取存储密钥并访问 VM 外部的 blob。

优点:在 Python 中。 缺点:不使用托管身份进行身份验证。 BlockBlobService 还不能使用 MSI 进行身份验证。

部分成功的方法2:Powershell。 在 Powershell 中,我找到了两种使用托管标识访问 blob 的方法。 挑战在于既不能创建我可以轻松替换到 Python 中的凭证,如下所述。 第一种方法来自 Microsoft 教授的关于为 Microsoft Azure 资源实施托管身份的 Pluralsight 课程( 链接)。 它使用 Az 模块。

优点:使用托管身份,相对简单。 缺点:不在 Python 中。 不生成可在 Python 中使用的凭据。

部分成功的方法3:Powershell。 这个方法来自于链接 它使用 VM 托管标识生成 SAS 凭据并访问 Azure 存储。

优点:使用托管标识并生成 SAS 凭证,这可能很有价值,因为 Python 中的 BlockBlobService 可以接受 SAS 令牌。 缺点:不在 Python 中。 上面给出的方法 2 对 Powershell 本身的过度杀伤以更少的努力实现了同样的目标。 我试用了它,因为我想看看是否可以提取 SAS 凭据以在 Python 中使用。

不成功的方法4:Python和Powershell。 我想我可以使用方法 3 在 Powershell 中生成一个 SAS 令牌,然后将令牌插入到方法 1 中的 BlockBlobService 代码中。我所拥有的不起作用。 我怀疑原因是为 testutils 容器创建了 SAS 凭证,而 Python BlockBlobService 需要 sa030802util 存储帐户的 SAS 凭证。

优点:允许我依靠 VM 的托管标识来访问 Azure 存储。 骗局:不起作用!

问题我的问题是:

  1. 如果我想确保用户只能访问 VM 内的存储帐户,我是否认为依赖 VM 托管标识和/或 SAS 凭据比帐户密钥更好?

  2. 有没有办法将代码拼凑在一起,让我可以使用 Python 访问数据? 方法4是有前途还是浪费时间?

代码

方法一:Python

from azure.mgmt.storage import StorageManagementClient
from azure.mgmt.storage.models import StorageAccountCreateParameters
from msrestazure.azure_active_directory import MSIAuthentication
from azure.mgmt.resource import SubscriptionClient
from azure.storage.blob import BlockBlobService

# find credentials and subscription id
credentials = MSIAuthentication()
subscription_client = SubscriptionClient(credentials)
subscription = next(subscription_client.subscriptions.list())
subscription_id = subscription.subscription_id

# find storage keys
storage_client = StorageManagementClient(credentials, subscription_id)
storage_account = storage_client.storage_accounts.get_properties("<resourcegroup>", "sa030802util")
storage_keys = storage_client.storage_accounts.list_keys("<resourcegroup>", "sa030802util")
storage_keys = {v.key_name: v.value for v in storage_keys.keys}

# create BlockBlobService and for e.g. print blobs in container
account_name = "sa030802util"
account_key = storage_keys["key1"]
container_name = "testutils"
block_blob_service = BlockBlobService(account_name = account_name, account_key = account_key)

print("List blobs in container")
generator = block_blob_service.list_blobs(container_name)
for blob in generator:
    print("Blob name: " + blob.name)

这段代码的输出是:

List blobs in container
Blob name: hello3.txt

方法 2:Powershell

Connect-AzAccount -MSI -Subscription <subscriptionid>
$context = New-AzStorageContext -StorageAccountName sa030802util
Get-AzStorageBlob -Name testutils -Context $context

这段代码的输出是:

Name                 BlobType  Length          ContentType                    LastModified         AccessTier SnapshotTime         IsDeleted
----                 --------  ------          -----------                    ------------         ---------- ------------         ---------
hello3.txt           BlockBlob 15              application/octet-stream       2019-08-02 05:45:33Z Hot                             False

方法 3:Powershell

# to get an access token using the VM's identity and use it to call Azure Resource Manager
$response = Invoke-WebRequest -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' -Method GET -Headers @{Metadata="true"}
$ content = $response.Content | ConvertFrom-Json
#ArmToken = $content.access_token

# to get SAS credential from Azure Resource Manager to make storage calls
## convert parameters to JSON
$params = @{canonicalizedResource="/blob/sa030802util/testutils"; signedResource="c"; signedPermission="rcwl"; signedProtocol="https"; signedExpiry="2019-08-30T00:00:00Z"}
$jsonParams = $params | ConvertTo-Json

## call storage listServiceSas endpoint to create SAS credential
$sasResponse = Invoke-WebRequest -Uri https://management.azure.com/subscriptions/<subscription_id>/resourceGroups/<resourceGroup>/providers/Microsoft.Storage/storageAccounts/sa030802util/listServiceSas/?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F -Method POST -Body $jsonParams -Headers @{Authorization = "Bearer $ArmToken"} -UseBasicParsing

## extract SAS credential from response
$sasContent = $sasResponse.Content | ConvertFrom-Json
$sasCred = $sasContent.serviceSasToken

# as example, list contents of container
$context = New-AzStorageContext -StorageAccountName sa030802util -SasToken $sasCred
Get-AzStorageBlob -Name testutils -Context $context

此代码的输出与方法 2 的输出相同。

方法 4:Python 和 Powershell Powershell 代码

# to get an access token using the VM's identity and use it to call Azure Resource Manager
$response = Invoke-WebRequest -Uri 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' -Method GET -Headers @{Metadata="true"}
$content = $response.Content | ConvertFrom-Json
$ArmToken = $content.access_token

# to get SAS credential from Azure Resource Manager to make storage calls
## convert parameters to JSON
$params = @{canonicalizedResource="/blob/sa030802util/testutils"; signedResource="c"; signedPermission="rcwl"; signedProtocol="https"; signedExpiry="2019-08-30T00:00:00Z"}
$jsonParams = $params | ConvertTo-Json

## call storage listServiceSas endpoint to create SAS credential
$sasResponse = Invoke-WebRequest -Uri https://management.azure.com/subscriptions/<subscription_id>/resourceGroups/<resourceGroup>/providers/Microsoft.Storage/storageAccounts/sa030802util/listServiceSas/?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F -Method POST -Body $jsonParams -Headers @{Authorization = "Bearer $ArmToken"} -UseBasicParsing

## extract SAS credential from response
$sasContent = $sasResponse.Content | ConvertFrom-Json
$sasCred = $sasContent.serviceSasToken

# then export the SAS credential ready to be used in Python

Python代码

from azure.storage.blob import BlockBlobService, PublicAccess
import os

# import SAS credential
with open("cred.txt") as f:
    line = f.readline()

# create BlockBlobService
block_blob_service = BlockBlobService(account_name = "sa030802util", sas_token=line)

# print content of testutils container
generator = block_blob_service.list_blobs("testutils")
for blob in generator:
    print(blob.name)

Python 代码返回以下错误:

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:<subscriptionid>
Time:2019-08-05T05:33:40.0175771Z</Message><AuthenticationErrorDetail>Signature did not match. String to sign used was rcwl

2019-08-30T00:00:00.0000000Z
/blob/sa030802util/testutils


https
2018-03-28




</AuthenticationErrorDetail></Error>

非常有趣的帖子,不幸的是我不是 Python 专家,但这可能会有所帮助: https : //github.com/Azure-Samples/resource-manager-python-manage-resources-with-msi

如果我想确保用户只能访问 VM 内的存储帐户?

您可以在没有 MSI 的情况下实现此目的: https : //docs.microsoft.com/en-us/azure/storage/common/storage-network-security

MSI 确实提供了额外的安全层,并且在某种程度上简化了管理,因为您不需要管理密钥/SAS 令牌,但这不是绝对要求,您可以在没有它的情况下构建安全设计。

祝你好运!

在适用于 Python 的 Azure SDK 中,创建BlobServiceClient然后使用其get_blob_client方法检索BlobClient类。 然后在该客户端上使用download_blob来获取 blob 内容。

BlobServiceClient接受一个credentials参数,您可以将MSIAuthentication()传递给该参数

您可以使用 azure Key Vault 将存储帐户的连接字符串存储为机密,并从那里检索凭据以连接到所需的容器

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM