diff options
author | Mark Benvenuto <mark.benvenuto@mongodb.com> | 2020-01-02 20:55:49 +0000 |
---|---|---|
committer | evergreen <evergreen@mongodb.com> | 2020-01-02 20:55:49 +0000 |
commit | 87f947409fdd0074acf4c8301355dabe4d4098f9 (patch) | |
tree | 6ec4c9f070dc02f44cb25ed306828f323097652e | |
parent | c86d4ad8c63e27f733b29c5f6bdb394540975782 (diff) | |
download | mongo-87f947409fdd0074acf4c8301355dabe4d4098f9.tar.gz |
SERVER-45241 Use instance metadata service v2 instead of v1
-rw-r--r-- | src/mongo/client/sasl_iam_client_conversation.cpp | 45 | ||||
-rw-r--r-- | src/mongo/client/sasl_iam_client_protocol.cpp | 7 | ||||
-rw-r--r-- | src/mongo/util/net/http_client.h | 5 | ||||
-rw-r--r-- | src/mongo/util/net/http_client_curl.cpp | 15 | ||||
-rw-r--r-- | src/mongo/util/net/http_client_winhttp.cpp | 5 |
5 files changed, 63 insertions, 14 deletions
diff --git a/src/mongo/client/sasl_iam_client_conversation.cpp b/src/mongo/client/sasl_iam_client_conversation.cpp index a5b7f0c8b71..89fdc13794e 100644 --- a/src/mongo/client/sasl_iam_client_conversation.cpp +++ b/src/mongo/client/sasl_iam_client_conversation.cpp @@ -47,6 +47,7 @@ namespace iam { SASLIamClientGlobalParams saslIamClientGlobalParams; } // namespace iam +namespace { std::string getDefaultEC2Host() { return iam::saslIamClientGlobalParams.awsEC2InstanceMetadataUrl; } @@ -55,6 +56,14 @@ std::string getDefaultECSHost() { return iam::saslIamClientGlobalParams.awsECSInstanceMetadataUrl; } +StringData toString(DataBuilder& builder) { + ConstDataRange cdr = builder.getCursor(); + StringData str; + cdr.readInto<StringData>(&str); + return str; +} +} // namespace + SaslIAMClientConversation::SaslIAMClientConversation(SaslClientSession* saslClientSession) : SaslClientConversation(saslClientSession) {} @@ -91,6 +100,7 @@ iam::AWSCredentials SaslIAMClientConversation::_getLocalAWSCredentials() const { return _getEc2Credentials(); } + iam::AWSCredentials SaslIAMClientConversation::_getEc2Credentials() const { try { @@ -99,13 +109,25 @@ iam::AWSCredentials SaslIAMClientConversation::_getEc2Credentials() const { // The local web server is just a normal HTTP server httpClient->allowInsecureHTTP(true); + // Get the token for authenticating with Instance Metadata Version 2 + // Set a lifetime of 30 seconds since we are only going to use this token for one set of + // requests. + std::vector<std::string> headers{"X-aws-ec2-metadata-token-ttl-seconds: 30"}; + httpClient->setHeaders(headers); + DataBuilder getToken = httpClient->put(getDefaultEC2Host() + "/latest/api/token", + ConstDataRange(nullptr, nullptr)); + + StringData token = toString(getToken); + + headers.clear(); + headers.push_back("X-aws-ec2-metadata-token: " + token); + httpClient->setHeaders(headers); + // Retrieve the role attached to the EC2 instance DataBuilder getRoleResult = httpClient->get(getDefaultEC2Host() + "/latest/meta-data/iam/security-credentials/"); - ConstDataRange cdrRole = getRoleResult.getCursor(); - StringData getRoleOutput; - cdrRole.readInto<StringData>(&getRoleOutput); + StringData getRoleOutput = toString(getRoleResult); std::string role = iam::parseRoleFromEC2IamSecurityCredentials(getRoleOutput); @@ -114,15 +136,15 @@ iam::AWSCredentials SaslIAMClientConversation::_getEc2Credentials() const { str::stream() << getDefaultEC2Host() + "/latest/meta-data/iam/security-credentials/" << role); - ConstDataRange cdrCredentials = getRoleCredentialsResult.getCursor(); - StringData getRoleCredentialsOutput; - cdrCredentials.readInto<StringData>(&getRoleCredentialsOutput); + StringData getRoleCredentialsOutput = toString(getRoleCredentialsResult); return iam::parseCredentialsFromEC2IamSecurityCredentials(getRoleCredentialsOutput); } catch (const DBException& e) { // Wrap exceptions from HTTP to make them clearer - uassertStatusOKWithContext(e.toStatus(), - "Failed to retrieve EC2 Instance Metadata Credentials"); + uassertStatusOKWithContext( + e.toStatus(), + "Failed to retrieve EC2 Instance Metadata Service Credentials. Ensure there is a role " + "via an instance profile assigned to this machine."); } MONGO_UNREACHABLE; @@ -139,15 +161,14 @@ iam::AWSCredentials SaslIAMClientConversation::_getEcsCredentials(StringData rel // Retrieve the security token attached to the ECS task DataBuilder getRoleResult = httpClient->get(getDefaultECSHost() + relativeUri); - ConstDataRange cdrRole = getRoleResult.getCursor(); - StringData getRoleOutput; - cdrRole.readInto<StringData>(&getRoleOutput); + StringData getRoleOutput = toString(getRoleResult); return iam::parseCredentialsFromECSTaskIamCredentials(getRoleOutput); } catch (const DBException& e) { // Wrap exceptions from HTTP to make them clearer uassertStatusOKWithContext(e.toStatus(), - "Failed to retrieve ECS Instance Metadata Credentials"); + "Failed to retrieve ECS Tasks Metadata Credentials. Ensure " + "there is an execution role assigned to this ECS task."); } MONGO_UNREACHABLE; diff --git a/src/mongo/client/sasl_iam_client_protocol.cpp b/src/mongo/client/sasl_iam_client_protocol.cpp index b6c6c4a3a51..41137c322cb 100644 --- a/src/mongo/client/sasl_iam_client_protocol.cpp +++ b/src/mongo/client/sasl_iam_client_protocol.cpp @@ -214,10 +214,13 @@ std::string getRegionFromHost(StringData host) { } std::string parseRoleFromEC2IamSecurityCredentials(StringData data) { + // Before the Nov 2019 AWS update, they added \n to the role_name. size_t pos = data.find('\n'); - uassert( - 51294, "Failed to parse role name from EC2 instance metadata", pos != std::string::npos); + if (pos == std::string::npos) { + pos = data.size(); + } + return data.substr(0, pos).toString(); } diff --git a/src/mongo/util/net/http_client.h b/src/mongo/util/net/http_client.h index 9f5767b51fd..f6dea4b4ab5 100644 --- a/src/mongo/util/net/http_client.h +++ b/src/mongo/util/net/http_client.h @@ -80,6 +80,11 @@ public: virtual DataBuilder post(StringData url, ConstDataRange data) const = 0; /** + * Perform a PUT request to specified URL. + */ + virtual DataBuilder put(StringData url, ConstDataRange data) const = 0; + + /** * Perform a GET request from the specified URL. */ virtual DataBuilder get(StringData url) const = 0; diff --git a/src/mongo/util/net/http_client_curl.cpp b/src/mongo/util/net/http_client_curl.cpp index ca51d73111f..20ca65442b3 100644 --- a/src/mongo/util/net/http_client_curl.cpp +++ b/src/mongo/util/net/http_client_curl.cpp @@ -277,6 +277,21 @@ public: return doRequest(myHandle.get(), url); } + DataBuilder put(StringData url, ConstDataRange cdr) const final { + // Make a local copy of the base handle for this request. + CurlHandle myHandle(curl_easy_duphandle(_handle.get())); + uassert(ErrorCodes::InternalError, "Curl initialization failed", myHandle); + + curl_easy_setopt(myHandle.get(), CURLOPT_PUT, 1); + + ConstDataRangeCursor cdrc(cdr); + curl_easy_setopt(myHandle.get(), CURLOPT_READFUNCTION, ReadMemoryCallback); + curl_easy_setopt(myHandle.get(), CURLOPT_READDATA, &cdrc); + curl_easy_setopt(myHandle.get(), CURLOPT_INFILESIZE_LARGE, (long)cdrc.length()); + + return doRequest(myHandle.get(), url); + } + private: /** * Helper for use with curl_easy_setopt which takes a vararg list, diff --git a/src/mongo/util/net/http_client_winhttp.cpp b/src/mongo/util/net/http_client_winhttp.cpp index f774e7387e4..1a54d4be355 100644 --- a/src/mongo/util/net/http_client_winhttp.cpp +++ b/src/mongo/util/net/http_client_winhttp.cpp @@ -163,6 +163,11 @@ public: L"POST", url, const_cast<void*>(static_cast<const void*>(cdr.data())), cdr.length()); } + DataBuilder put(StringData url, ConstDataRange cdr) const final { + return doRequest( + L"PUT", url, const_cast<void*>(static_cast<const void*>(cdr.data())), cdr.length()); + } + DataBuilder get(StringData url) const final { return doRequest(L"GET", url, nullptr, 0); } |