milvus/internal/core/src/storage/AliyunSTSClient.cpp

220 lines
8.5 KiB
C++

// Copyright (C) 2019-2020 Zilliz. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions and limitations under the License
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/core/internal/AWSHttpResourceClient.h>
#include <aws/core/client/DefaultRetryStrategy.h>
#include <aws/core/http/HttpClient.h>
#include <aws/core/http/HttpClientFactory.h>
#include <aws/core/http/HttpResponse.h>
#include <aws/core/utils/logging/LogMacros.h>
#include <aws/core/utils/StringUtils.h>
#include <aws/core/utils/HashingUtils.h>
#include <aws/core/platform/Environment.h>
#include <aws/core/client/AWSError.h>
#include <aws/core/client/CoreErrors.h>
#include <aws/core/utils/xml/XmlSerializer.h>
#include <limits.h>
#include <mutex>
#include <sstream>
#include <random>
#include "AliyunSTSClient.h"
namespace Aws {
namespace Http {
class HttpClient;
class HttpRequest;
enum class HttpResponseCode;
} // namespace Http
namespace Client {
Aws::String
ComputeUserAgentString();
}
namespace Internal {
static const char STS_RESOURCE_CLIENT_LOG_TAG[] =
"AliyunSTSResourceClient"; // [aliyun]
int
IntRand(const int& min, const int& max) {
static thread_local std::mt19937 generator;
std::uniform_int_distribution<int> distribution(min, max);
return distribution(generator);
}
AliyunSTSCredentialsClient::AliyunSTSCredentialsClient(
const Aws::Client::ClientConfiguration& clientConfiguration)
: AWSHttpResourceClient(clientConfiguration, STS_RESOURCE_CLIENT_LOG_TAG) {
m_aliyunOidcProviderArn =
Aws::Environment::GetEnv("ALIBABA_CLOUD_OIDC_PROVIDER_ARN");
if (m_aliyunOidcProviderArn.empty()) {
AWS_LOGSTREAM_WARN(STS_RESOURCE_CLIENT_LOG_TAG,
"oidc role arn must be specified to use STS "
"AssumeRole web identity creds provider.");
return;
}
SetErrorMarshaller(Aws::MakeUnique<Aws::Client::XmlErrorMarshaller>(
STS_RESOURCE_CLIENT_LOG_TAG));
// [aliyun]
m_endpoint = "https://sts.aliyuncs.com";
AWS_LOGSTREAM_INFO(
STS_RESOURCE_CLIENT_LOG_TAG,
"Creating STS ResourceClient with endpoint: " << m_endpoint);
}
AliyunSTSCredentialsClient::STSAssumeRoleWithWebIdentityResult
AliyunSTSCredentialsClient::GetAssumeRoleWithWebIdentityCredentials(
const STSAssumeRoleWithWebIdentityRequest& request) {
// Calculate query string
Aws::StringStream ss;
// [aliyun]
// linux curl example:
// Action=AssumeRoleWithOIDC
// Timestamp=`date -Iseconds`
// Version="2015-04-01"
// SignatureNonce=`$RANDOM`
// RoleSessionName="default_session"
// RoleArn=$ALIBABA_CLOUD_ROLE_ARN
// OIDCProviderArn=$ALIBABA_CLOUD_OIDC_PROVIDER_ARN
// OIDCToken=`cat $ALIBABA_CLOUD_OIDC_TOKEN_FILE`
// curl "https://sts.aliyuncs.com?Action=$Action&Timestamp=$time" \
// -H "Host: sts.aliyuncs.com" \
// -H "Accept-Encoding: identity" \
// -H "SignatureNonce: $SignatureNonce" \
// -d
// "RoleArn=$RoleArn&OIDCProviderArn=$OIDCProviderArn&OIDCToken=$OIDCToken&RoleSessionName=$RoleSessionName&Version=$Version"
ss << "Action=AssumeRoleWithOIDC"
<< "&Timestamp=" /*iso8601*/
<< Aws::Utils::StringUtils::URLEncode(
Aws::Utils::DateTime::Now()
.ToGmtString(Aws::Utils::DateFormat::ISO_8601)
.c_str())
<< "&Version=2015-04-01"
<< "&SignatureNonce="
<< Aws::Utils::HashingUtils::HashString(
Aws::Utils::StringUtils::to_string(IntRand(0, INT_MAX)).c_str())
<< "&RoleSessionName="
<< Aws::Utils::StringUtils::URLEncode(request.roleSessionName.c_str())
<< "&RoleArn="
<< Aws::Utils::StringUtils::URLEncode(request.roleArn.c_str())
<< "&OIDCProviderArn="
<< Aws::Utils::StringUtils::URLEncode(m_aliyunOidcProviderArn.c_str())
<< "&OIDCToken="
<< Aws::Utils::StringUtils::URLEncode(request.webIdentityToken.c_str());
std::shared_ptr<Aws::Http::HttpRequest> httpRequest(
Aws::Http::CreateHttpRequest(
m_endpoint,
Aws::Http::HttpMethod::HTTP_POST,
Aws::Utils::Stream::DefaultResponseStreamFactoryMethod));
httpRequest->SetUserAgent(Aws::Client::ComputeUserAgentString());
std::shared_ptr<Aws::IOStream> body =
Aws::MakeShared<Aws::StringStream>("STS_RESOURCE_CLIENT_LOG_TAG");
*body << ss.str();
httpRequest->AddContentBody(body);
body->seekg(0, body->end);
auto streamSize = body->tellg();
body->seekg(0, body->beg);
Aws::StringStream contentLength;
contentLength << streamSize;
httpRequest->SetContentLength(contentLength.str());
httpRequest->SetContentType("application/x-www-form-urlencoded");
Aws::String credentialsStr =
GetResourceWithAWSWebServiceResult(httpRequest).GetPayload();
// Parse credentials
STSAssumeRoleWithWebIdentityResult result;
if (credentialsStr.empty()) {
AWS_LOGSTREAM_WARN(STS_RESOURCE_CLIENT_LOG_TAG,
"Get an empty credential from sts");
return result;
}
// [aliyun] output example
// <?xml version='1.0' encoding='UTF-8'?>
// <AssumeRoleWithOIDCResponse>
// <RequestId>D94AFCC3-54CA-508C-B6AF-21481E761BDB</RequestId>
// <OIDCTokenInfo>
// <Issuer>https://oidc-ack-cn-shanghai.oss-cn-shanghai.aliyuncs.com/c532c4ce5e84048a1972535df283f737d</Issuer>
// <Subject>system:serviceaccount:default:my-release</Subject>
// <ClientIds>sts.aliyuncs.com</ClientIds>
// </OIDCTokenInfo>
// <AssumedRoleUser>
// <Arn>acs:ram::1413891078881348:role/vdc-poc-milvus/default</Arn>
// <AssumedRoleId>383373758575348335:default</AssumedRoleId>
// </AssumedRoleUser>
// <Credentials>
// <SecurityToken>xxx</SecurityToken>
// <AccessKeyId>xxx</AccessKeyId>
// <AccessKeySecret>xxx</AccessKeySecret>
// <Expiration>2023-03-02T07:39:09Z</Expiration>
// </Credentials>
// </AssumeRoleWithOIDCResponse>
const Utils::Xml::XmlDocument xmlDocument =
Utils::Xml::XmlDocument::CreateFromXmlString(credentialsStr);
Utils::Xml::XmlNode rootNode = xmlDocument.GetRootElement();
Utils::Xml::XmlNode resultNode = rootNode;
if (!rootNode.IsNull() &&
(rootNode.GetName() != "AssumeRoleWithOIDCResponse")) {
resultNode =
rootNode.FirstChild("AssumeRoleWithOIDCResponse"); // [aliyun]
}
if (!resultNode.IsNull()) {
Utils::Xml::XmlNode credentialsNode =
resultNode.FirstChild("Credentials");
if (!credentialsNode.IsNull()) {
Utils::Xml::XmlNode accessKeyIdNode =
credentialsNode.FirstChild("AccessKeyId");
if (!accessKeyIdNode.IsNull()) {
result.creds.SetAWSAccessKeyId(accessKeyIdNode.GetText());
}
Utils::Xml::XmlNode secretAccessKeyNode =
credentialsNode.FirstChild("AccessKeySecret"); // [aliyun]
if (!secretAccessKeyNode.IsNull()) {
result.creds.SetAWSSecretKey(secretAccessKeyNode.GetText());
}
Utils::Xml::XmlNode sessionTokenNode =
credentialsNode.FirstChild("SecurityToken"); // [aliyun]
if (!sessionTokenNode.IsNull()) {
result.creds.SetSessionToken(sessionTokenNode.GetText());
}
Utils::Xml::XmlNode expirationNode =
credentialsNode.FirstChild("Expiration");
if (!expirationNode.IsNull()) {
result.creds.SetExpiration(
Aws::Utils::DateTime(Aws::Utils::StringUtils::Trim(
expirationNode.GetText().c_str())
.c_str(),
Aws::Utils::DateFormat::ISO_8601));
}
}
}
return result;
}
} // namespace Internal
} // namespace Aws