使用 bash、Azure CLI 和 REST API 访问 CosmosDB - 如何正确获取令牌和哈希?
Use bash, Azure CLI and REST API to access CosmosDB - how to get token and hash right?
我的最终目标是使用 bash 和 Azure CLI 将文档上传到现有的 CosmosDB (SQL) 实例。问题是:Azure CLI 会 not offer a command to modify documents.
为了解决这个问题,我查看了 az rest
命令并希望调用 CosmosDB 的 REST API 来完成任务,但是在尝试数小时不成功之后,我总是得到错误:
Unauthorized({"code":"Unauthorized","message":"The input authorization
token can't serve the request. Please check that the expected payload
is built as per the protocol, and check the key being used. Server
used the following payload to sign: 'get\ndbs\n\nsat, 25 apr 2020
13:50:22 +0000\n\n'\r\nActivityId: ...,
Microsoft.Azure.Documents.Common/2.10.0"})
为了简单起见,我正在尝试使用 REST API、as described in the docs 列出我的所有数据库,如果可行,请继续进行实际的文档上传。
我也在尝试按照 in the docs 提供的说明生成授权 header。
获取数据库列表的请求使用的格式是:GET https://{databaseaccount}.documents.azure.com/dbs
这是我的 bash 脚本,突出显示了 problems/questions。
第 1 部分:获取访问令牌 - 问题:这是开始的正确令牌吗?
masterKey=$(az cosmosdb keys list --name MYDBINSTANCENAME --query primaryMasterKey --output tsv)
第 2 部分:生成要散列的有效负载 - 所有内容必须小写
verb="get"
resourceType="dbs"
resourceLink="dbs"
now=$((date -uR) | tr '[A-Z]' '[a-z]')
payload="$verb\n$resourceType\n$resourceLink\n$now\n\n"
第 3 部分:哈希负载 - 问题:此哈希的结果与 what the sample code in C# builds 不同。因此,其中一个肯定是错误的,但两者都会导致相同的错误消息。
hashedPayload=$(printf $payload | openssl dgst -sha256 -hmac $masterKey -binary)
第 4 部分:创建所需的身份验证字符串并转换为 base 64 - 问题: 是与 az rest
一起使用所需的 base 64 编码吗?
authString="type=master&ver=1.0&sig=$hashedPayload" | base64
第 5 部分:创建 headers 字符串。这是使用 JSON 表示法,因为空格分隔方法不起作用,although the docs state it should.
headers="{\"x-ms-date\": \"$now\", \"x-ms-version\": \"2018-12-31\", \"x-ms-documentdb-isquery\": \"true\", \"Content-Type\": \"application/query+json\", \"Authorization\": \"$authString\"}"
第 6 部分:调用 REST API
az rest --verbose -m get -u "https://MYDBINSTANCENAME.documents.azure.com:443/dbs" --headers $headers
输出:
Request URL: 'https://MYDBINSTANCENAME.documents.azure.com:443/dbs'
Request method: 'GET'
Request headers:
'User-Agent': 'AZURECLI/2.4.0 (HOMEBREW)'
'Accept-Encoding': 'gzip, deflate'
'Accept': '*/*'
'Connection': 'keep-alive'
'x-ms-date': 'sat, 25 apr 2020 13:54:10 +0000'
'x-ms-version': '2018-12-31'
'x-ms-documentdb-isquery': 'true'
'Content-Type': 'application/query+json'
'Authorization': 'type%3dmaster%26ver%...'
'x-ms-client-request-id': 'a55357fe-411c-4adf-9fd6-1a255e010cca'
'CommandName': 'rest'
'ParameterSetName': '--verbose -m -u --headers'
Request body:
None
Response status: 401
Response headers:
'Transfer-Encoding': 'chunked'
'Content-Type': 'application/json'
'Content-Location': 'https://MYDBINSTANCENAME.documents.azure.com/dbs'
'Server': 'Microsoft-HTTPAPI/2.0'
'x-ms-activity-id': '9119f8bd-53d9-4a87-8aff-a887ec652fed'
'Strict-Transport-Security': 'max-age=31536000'
'x-ms-gatewayversion': 'version=2.10.0'
'Date': 'Sat, 25 Apr 2020 13:54:11 GMT'
Response content:
{"code":"Unauthorized","message":"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ndbs\n\nsat, 25 apr 2020 13:54:10 +0000\n\n'\r\nActivityId: 9119f8bd-53d9-4a87-8aff-a887ec652fed, Microsoft.Azure.Documents.Common/2.10.0"}
感谢您对@Gaurav Mantri 和@Mark Brown 的支持。您的评论帮助避免了一百万次剪纸死亡:-)
我已经启动了一个存储库,用于收集 Azure CLI bash 脚本。在 https://github.com/Krumelur/AzureScripts
找到它
让我回答我自己的问题并提供一个脚本来读取 CosmosDB 实例中的现有数据库。
这将在浏览器中启动交互式登录 window。
az login
指定我们要访问的资源。可以在以下位置找到 URI 和所需的参数值:https://docs.microsoft.com/en-us/rest/api/cosmos-db/cosmosdb-resource-uri-syntax-for-rest
comsosDbInstanceName="YOUR INSTANCE NAME GOES HERE"
baseUrl="https://$comsosDbInstanceName.documents.azure.com/"
verb="get"
resourceType="dbs"
resourceLink="dbs"
resourceId=""
获取 CosmosDB 的主密钥。我们需要这个来获得访问权限。这与可在 CosmosDB 实例的 "Keys" 部分的门户中找到的密钥相同。主键是 REST API 所指的 "master" 键。
masterKey=$(az cosmosdb keys list --name $comsosDbInstanceName --query primaryMasterKey --output tsv)
echo "Masterkey: $masterKey"
CosmosDB REST API 需要哈希授权 header:https://docs.microsoft.com/de-de/rest/api/cosmos-db/access-control-on-cosmosdb-resources#authorization-header
要获取 HTTP 格式的日期,区域设置必须设置为美国。否则日期名称将被本地化(例如德语)。
bash 不直接支持 HTTP 格式。要使其工作,请将当前时区设置为 GMT。时间格式如下所示:"mon, 27 apr 2020 09:46:58 gmt".
now=$(env LANG=en_US TZ=GMT date '+%a, %d %b %Y %T %Z')
echo "Date: " $now
Concat 谓词、资源类型、资源 ID 和预期格式的日期。 REST API 要求签名为小写。
我没有意识到的 "little" 问题:尾随换行符 (\n
) 在输出字符串时总是被截断。这会破坏散列,因为 CosmosDB 期望它们在那里。这就是为什么在小写操作之后将两个尾随换行符追加回来的原因。
signature="$(printf "%s" "$verb\n$resourceType\n$resourceId\n$now" | tr '[A-Z]' '[a-z]')\n\n"
echo "Signature: $signature"
使用 CosmosDB 实例的主键计算签名的哈希值。
有关这为何如此棘手的详细信息,请参阅 https://superuser.com/questions/1546027/what-is-the-openssl-equivalent-of-this-given-c-hashing-code/1546036。 tl;博士;版本:OpenSSL 和服务器实现以不同方式解释散列密钥。
hexKey=$(printf "$masterKey" | base64 --decode | hexdump -v -e '/1 "%02x"')
echo "Hex key: " $hexKey
hashedSignature=$(printf "$signature" | openssl dgst -sha256 -mac hmac -macopt hexkey:$hexKey -binary | base64)
echo "Hashed signature: $hashedSignature"```
散列签名应URL 编码。但当然,bash 中没有 built-in 方法可以做到这一点。哎呀。
幸运的是,我们正在处理 base64 字符串。唯一需要编码的字符是变成“%3d”的等号。
使用格式 "type={typeoftoken}&ver={tokenversion}&sig={hashsignature}"
构建授权 header
authString="type=master&ver=1.0&sig=$hashedSignature"
echo "Auth string: $authString"
Auth 字符串应进行 URL 编码。但是当然 bash 中没有 built-in 方法可以做到这一点。哎呀。
这不是完整的 base64 编码,而是仅更改我们可能看到的字符:= -> %3d, & -> %26, + => %2b, / => %2f
urlEncodedAuthString=$(printf "$authString" | sed 's/=/%3d/g' | sed 's/&/%26/g' | sed 's/+/%2b/g' | sed 's/\//%2f/g')
echo "URL encoded auth string: $urlEncodedAuthString"
通过组合基础 URL 和资源 link 进行 API 调用。
url="$baseUrl$resourceLink"
echo "URL: $url"
可以使用"az rest"命令:
az rest --verbose -m $verb -u $url --headers x-ms-date="$now" x-ms-version=2018-12-31 x-ms-documentdb-isquery=true Content-Type=application/query+json Authorization=$urlEncodedAuthString --debug
备选方案:使用 cURL
curl --request $verb -H "x-ms-date: $now" -H "x-ms-version: 2018-12-31" -H "x-ms-documentdb-isquery: true" -H "Content-Type: application/query+json" -H "Authorization: $urlEncodedAuthString" $url
我的最终目标是使用 bash 和 Azure CLI 将文档上传到现有的 CosmosDB (SQL) 实例。问题是:Azure CLI 会 not offer a command to modify documents.
为了解决这个问题,我查看了 az rest
命令并希望调用 CosmosDB 的 REST API 来完成任务,但是在尝试数小时不成功之后,我总是得到错误:
Unauthorized({"code":"Unauthorized","message":"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ndbs\n\nsat, 25 apr 2020 13:50:22 +0000\n\n'\r\nActivityId: ..., Microsoft.Azure.Documents.Common/2.10.0"})
为了简单起见,我正在尝试使用 REST API、as described in the docs 列出我的所有数据库,如果可行,请继续进行实际的文档上传。
我也在尝试按照 in the docs 提供的说明生成授权 header。
获取数据库列表的请求使用的格式是:GET https://{databaseaccount}.documents.azure.com/dbs
这是我的 bash 脚本,突出显示了 problems/questions。
第 1 部分:获取访问令牌 - 问题:这是开始的正确令牌吗?
masterKey=$(az cosmosdb keys list --name MYDBINSTANCENAME --query primaryMasterKey --output tsv)
第 2 部分:生成要散列的有效负载 - 所有内容必须小写
verb="get"
resourceType="dbs"
resourceLink="dbs"
now=$((date -uR) | tr '[A-Z]' '[a-z]')
payload="$verb\n$resourceType\n$resourceLink\n$now\n\n"
第 3 部分:哈希负载 - 问题:此哈希的结果与 what the sample code in C# builds 不同。因此,其中一个肯定是错误的,但两者都会导致相同的错误消息。
hashedPayload=$(printf $payload | openssl dgst -sha256 -hmac $masterKey -binary)
第 4 部分:创建所需的身份验证字符串并转换为 base 64 - 问题: 是与 az rest
一起使用所需的 base 64 编码吗?
authString="type=master&ver=1.0&sig=$hashedPayload" | base64
第 5 部分:创建 headers 字符串。这是使用 JSON 表示法,因为空格分隔方法不起作用,although the docs state it should.
headers="{\"x-ms-date\": \"$now\", \"x-ms-version\": \"2018-12-31\", \"x-ms-documentdb-isquery\": \"true\", \"Content-Type\": \"application/query+json\", \"Authorization\": \"$authString\"}"
第 6 部分:调用 REST API
az rest --verbose -m get -u "https://MYDBINSTANCENAME.documents.azure.com:443/dbs" --headers $headers
输出:
Request URL: 'https://MYDBINSTANCENAME.documents.azure.com:443/dbs'
Request method: 'GET'
Request headers:
'User-Agent': 'AZURECLI/2.4.0 (HOMEBREW)'
'Accept-Encoding': 'gzip, deflate'
'Accept': '*/*'
'Connection': 'keep-alive'
'x-ms-date': 'sat, 25 apr 2020 13:54:10 +0000'
'x-ms-version': '2018-12-31'
'x-ms-documentdb-isquery': 'true'
'Content-Type': 'application/query+json'
'Authorization': 'type%3dmaster%26ver%...'
'x-ms-client-request-id': 'a55357fe-411c-4adf-9fd6-1a255e010cca'
'CommandName': 'rest'
'ParameterSetName': '--verbose -m -u --headers'
Request body:
None
Response status: 401
Response headers:
'Transfer-Encoding': 'chunked'
'Content-Type': 'application/json'
'Content-Location': 'https://MYDBINSTANCENAME.documents.azure.com/dbs'
'Server': 'Microsoft-HTTPAPI/2.0'
'x-ms-activity-id': '9119f8bd-53d9-4a87-8aff-a887ec652fed'
'Strict-Transport-Security': 'max-age=31536000'
'x-ms-gatewayversion': 'version=2.10.0'
'Date': 'Sat, 25 Apr 2020 13:54:11 GMT'
Response content:
{"code":"Unauthorized","message":"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ndbs\n\nsat, 25 apr 2020 13:54:10 +0000\n\n'\r\nActivityId: 9119f8bd-53d9-4a87-8aff-a887ec652fed, Microsoft.Azure.Documents.Common/2.10.0"}
感谢您对@Gaurav Mantri 和@Mark Brown 的支持。您的评论帮助避免了一百万次剪纸死亡:-)
我已经启动了一个存储库,用于收集 Azure CLI bash 脚本。在 https://github.com/Krumelur/AzureScripts
找到它让我回答我自己的问题并提供一个脚本来读取 CosmosDB 实例中的现有数据库。
这将在浏览器中启动交互式登录 window。
az login
指定我们要访问的资源。可以在以下位置找到 URI 和所需的参数值:https://docs.microsoft.com/en-us/rest/api/cosmos-db/cosmosdb-resource-uri-syntax-for-rest
comsosDbInstanceName="YOUR INSTANCE NAME GOES HERE"
baseUrl="https://$comsosDbInstanceName.documents.azure.com/"
verb="get"
resourceType="dbs"
resourceLink="dbs"
resourceId=""
获取 CosmosDB 的主密钥。我们需要这个来获得访问权限。这与可在 CosmosDB 实例的 "Keys" 部分的门户中找到的密钥相同。主键是 REST API 所指的 "master" 键。
masterKey=$(az cosmosdb keys list --name $comsosDbInstanceName --query primaryMasterKey --output tsv)
echo "Masterkey: $masterKey"
CosmosDB REST API 需要哈希授权 header:https://docs.microsoft.com/de-de/rest/api/cosmos-db/access-control-on-cosmosdb-resources#authorization-header
要获取 HTTP 格式的日期,区域设置必须设置为美国。否则日期名称将被本地化(例如德语)。
bash 不直接支持 HTTP 格式。要使其工作,请将当前时区设置为 GMT。时间格式如下所示:"mon, 27 apr 2020 09:46:58 gmt".
now=$(env LANG=en_US TZ=GMT date '+%a, %d %b %Y %T %Z')
echo "Date: " $now
Concat 谓词、资源类型、资源 ID 和预期格式的日期。 REST API 要求签名为小写。
我没有意识到的 "little" 问题:尾随换行符 (\n
) 在输出字符串时总是被截断。这会破坏散列,因为 CosmosDB 期望它们在那里。这就是为什么在小写操作之后将两个尾随换行符追加回来的原因。
signature="$(printf "%s" "$verb\n$resourceType\n$resourceId\n$now" | tr '[A-Z]' '[a-z]')\n\n"
echo "Signature: $signature"
使用 CosmosDB 实例的主键计算签名的哈希值。 有关这为何如此棘手的详细信息,请参阅 https://superuser.com/questions/1546027/what-is-the-openssl-equivalent-of-this-given-c-hashing-code/1546036。 tl;博士;版本:OpenSSL 和服务器实现以不同方式解释散列密钥。
hexKey=$(printf "$masterKey" | base64 --decode | hexdump -v -e '/1 "%02x"')
echo "Hex key: " $hexKey
hashedSignature=$(printf "$signature" | openssl dgst -sha256 -mac hmac -macopt hexkey:$hexKey -binary | base64)
echo "Hashed signature: $hashedSignature"```
散列签名应URL 编码。但当然,bash 中没有 built-in 方法可以做到这一点。哎呀。 幸运的是,我们正在处理 base64 字符串。唯一需要编码的字符是变成“%3d”的等号。
使用格式 "type={typeoftoken}&ver={tokenversion}&sig={hashsignature}"
构建授权 headerauthString="type=master&ver=1.0&sig=$hashedSignature"
echo "Auth string: $authString"
Auth 字符串应进行 URL 编码。但是当然 bash 中没有 built-in 方法可以做到这一点。哎呀。 这不是完整的 base64 编码,而是仅更改我们可能看到的字符:= -> %3d, & -> %26, + => %2b, / => %2f
urlEncodedAuthString=$(printf "$authString" | sed 's/=/%3d/g' | sed 's/&/%26/g' | sed 's/+/%2b/g' | sed 's/\//%2f/g')
echo "URL encoded auth string: $urlEncodedAuthString"
通过组合基础 URL 和资源 link 进行 API 调用。
url="$baseUrl$resourceLink"
echo "URL: $url"
可以使用"az rest"命令:
az rest --verbose -m $verb -u $url --headers x-ms-date="$now" x-ms-version=2018-12-31 x-ms-documentdb-isquery=true Content-Type=application/query+json Authorization=$urlEncodedAuthString --debug
备选方案:使用 cURL
curl --request $verb -H "x-ms-date: $now" -H "x-ms-version: 2018-12-31" -H "x-ms-documentdb-isquery: true" -H "Content-Type: application/query+json" -H "Authorization: $urlEncodedAuthString" $url