mirror of
https://github.com/minio/minio-cpp.git
synced 2026-01-26 04:01:32 +03:00
Add more credential providers (#34)
* ChainedProvider * EnvAwsProvider * EnvMinioProvider * AwsConfigProvider * MinioClientConfigProvider * AssumeRoleProvider * ClientGrantsProvider * WebIdentityProvider * IamAwsProvider * LdapIdentityProvider * CertificateIdentityProvider Signed-off-by: Bala.FA <bala@minio.io>
This commit is contained in:
@@ -48,6 +48,7 @@ ENDIF()
|
||||
SET(requiredlibs)
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cmake/modules/FindPugiXML.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/cmake/modules/FindInih.cmake")
|
||||
|
||||
find_package(CURL REQUIRED)
|
||||
IF(CURL_FOUND)
|
||||
@@ -63,6 +64,9 @@ SET(requiredlibs ${requiredlibs} unofficial::curlpp::curlpp)
|
||||
find_package(nlohmann_json CONFIG REQUIRED)
|
||||
SET(requiredlibs ${requiredlibs} nlohmann_json::nlohmann_json)
|
||||
|
||||
find_package(nlohmann_json CONFIG REQUIRED)
|
||||
SET(requiredlibs ${requiredlibs} nlohmann_json::nlohmann_json)
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
IF(OPENSSL_FOUND)
|
||||
INCLUDE_DIRECTORIES(${OPENSSL_INCLUDE_DIR})
|
||||
@@ -79,6 +83,13 @@ ELSE(PUGIXML_FOUND)
|
||||
MESSAGE(FATAL_ERROR "Could not find the pugixml library and development files.")
|
||||
ENDIF(PUGIXML_FOUND)
|
||||
|
||||
IF(INIH_FOUND)
|
||||
INCLUDE_DIRECTORIES(${INIH_INCLUDE_DIR})
|
||||
SET(requiredlibs ${requiredlibs} inih)
|
||||
ELSE(INIH_FOUND)
|
||||
MESSAGE(FATAL_ERROR "Could not find the inih library and development files.")
|
||||
ENDIF(INIH_FOUND)
|
||||
|
||||
message(STATUS "Found required libs: ${requiredlibs}")
|
||||
|
||||
INCLUDE (CheckIncludeFiles)
|
||||
|
||||
31
cmake/modules/FindInih.cmake
Normal file
31
cmake/modules/FindInih.cmake
Normal file
@@ -0,0 +1,31 @@
|
||||
# Find the inih XML parsing library.
|
||||
#
|
||||
# Sets the usual variables expected for find_package scripts:
|
||||
#
|
||||
# INIH_INCLUDE_DIR - header location
|
||||
# INIH_LIBRARIES - library to link against
|
||||
# INIH_FOUND - true if inih was found.
|
||||
|
||||
find_path (INIH_INCLUDE_DIR
|
||||
NAMES INIReader.h
|
||||
PATHS ${INIH_HOME}/include)
|
||||
find_library (INIH_LIBRARY
|
||||
NAMES inih
|
||||
PATHS ${INIH_HOME}/lib)
|
||||
|
||||
# Support the REQUIRED and QUIET arguments, and set INIH_FOUND if found.
|
||||
include (FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS (inih DEFAULT_MSG INIH_LIBRARY
|
||||
INIH_INCLUDE_DIR)
|
||||
|
||||
if (INIH_FOUND)
|
||||
set (INIH_LIBRARIES ${INIH_LIBRARY})
|
||||
if (NOT inih_FIND_QUIETLY)
|
||||
message (STATUS "inih include = ${INIH_INCLUDE_DIR}")
|
||||
message (STATUS "inih library = ${INIH_LIBRARY}")
|
||||
endif ()
|
||||
else ()
|
||||
message (STATUS "No inih found")
|
||||
endif()
|
||||
|
||||
mark_as_advanced (INIH_LIBRARY INIH_INCLUDE_DIR)
|
||||
75
include/credentials.h
Normal file
75
include/credentials.h
Normal file
@@ -0,0 +1,75 @@
|
||||
// MinIO C++ Library for Amazon S3 Compatible Cloud Storage
|
||||
// Copyright 2022 MinIO, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef _MINIO_CREDS_CREDENTIALS_H
|
||||
#define _MINIO_CREDS_CREDENTIALS_H
|
||||
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
namespace minio {
|
||||
namespace creds {
|
||||
static bool expired(utils::Time expiration) {
|
||||
if (!expiration) return false;
|
||||
utils::Time now = utils::Time::Now();
|
||||
now.Add(10);
|
||||
return expiration < now;
|
||||
}
|
||||
|
||||
/**
|
||||
* Credentials contains access key and secret key with optional session token
|
||||
* and expiration.
|
||||
*/
|
||||
struct Credentials {
|
||||
error::Error err;
|
||||
std::string access_key;
|
||||
std::string secret_key;
|
||||
std::string session_token;
|
||||
utils::Time expiration;
|
||||
|
||||
bool IsExpired() { return expired(expiration); }
|
||||
|
||||
operator bool() const {
|
||||
return !err && !access_key.empty() && expired(expiration);
|
||||
}
|
||||
|
||||
static Credentials ParseXML(std::string_view data, std::string root) {
|
||||
pugi::xml_document xdoc;
|
||||
pugi::xml_parse_result result = xdoc.load_string(data.data());
|
||||
if (!result) return Credentials{error::Error("unable to parse XML")};
|
||||
|
||||
auto credentials = xdoc.select_node((root + "/Credentials").c_str());
|
||||
|
||||
auto text = credentials.node().select_node("AccessKeyId/text()");
|
||||
std::string access_key = text.node().value();
|
||||
|
||||
text = credentials.node().select_node("SecretAccessKey/text()");
|
||||
std::string secret_key = text.node().value();
|
||||
|
||||
text = credentials.node().select_node("SessionToken/text()");
|
||||
std::string session_token = text.node().value();
|
||||
|
||||
text = credentials.node().select_node("Expiration/text()");
|
||||
auto expiration = utils::Time::FromISO8601UTC(text.node().value());
|
||||
|
||||
return Credentials{error::SUCCESS, access_key, secret_key, session_token,
|
||||
expiration};
|
||||
}
|
||||
}; // class Credentials
|
||||
} // namespace creds
|
||||
} // namespace minio
|
||||
|
||||
#endif // #ifndef _MINIO_CREDS_CREDENTIALS_H
|
||||
@@ -1,70 +0,0 @@
|
||||
// MinIO C++ Library for Amazon S3 Compatible Cloud Storage
|
||||
// Copyright 2022 MinIO, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef _MINIO_CREDS_H
|
||||
#define _MINIO_CREDS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace minio {
|
||||
namespace creds {
|
||||
/**
|
||||
* Credentials contains access key and secret key with optional session token
|
||||
* and expiration.
|
||||
*/
|
||||
class Credentials {
|
||||
private:
|
||||
std::string_view access_key_;
|
||||
std::string_view secret_key_;
|
||||
std::string_view session_token_;
|
||||
unsigned int expiration_;
|
||||
|
||||
public:
|
||||
Credentials(const Credentials& creds);
|
||||
Credentials(std::string_view access_key, std::string_view secret_key,
|
||||
std::string_view session_token = "", unsigned int expiration = 0);
|
||||
std::string AccessKey();
|
||||
std::string SecretKey();
|
||||
std::string SessionToken();
|
||||
bool IsExpired();
|
||||
}; // class Credentials
|
||||
|
||||
/**
|
||||
* Credential provider interface.
|
||||
*/
|
||||
class Provider {
|
||||
public:
|
||||
Provider() {}
|
||||
virtual ~Provider() {}
|
||||
virtual Credentials Fetch() = 0;
|
||||
}; // class Provider
|
||||
|
||||
/**
|
||||
* Static credential provider.
|
||||
*/
|
||||
class StaticProvider : public Provider {
|
||||
private:
|
||||
Credentials* creds_ = NULL;
|
||||
|
||||
public:
|
||||
StaticProvider(std::string_view access_key, std::string_view secret_key,
|
||||
std::string_view session_token = "");
|
||||
~StaticProvider();
|
||||
Credentials Fetch();
|
||||
}; // class StaticProvider
|
||||
} // namespace creds
|
||||
} // namespace minio
|
||||
|
||||
#endif // #ifndef _MINIO_CREDS_H
|
||||
@@ -175,6 +175,8 @@ struct Request {
|
||||
bool debug = false;
|
||||
bool ignore_cert_check = false;
|
||||
std::string ssl_cert_file;
|
||||
std::string key_file;
|
||||
std::string cert_file;
|
||||
|
||||
Request(Method method, Url url);
|
||||
Response Execute();
|
||||
@@ -200,7 +202,7 @@ struct Response {
|
||||
operator bool() const {
|
||||
return error.empty() && status_code >= 200 && status_code <= 299;
|
||||
}
|
||||
error::Error GetError() {
|
||||
error::Error Error() {
|
||||
if (!error.empty()) return error::Error(error);
|
||||
if (status_code && (status_code < 200 || status_code > 299)) {
|
||||
return error::Error("failed with HTTP status code " +
|
||||
|
||||
616
include/providers.h
Normal file
616
include/providers.h
Normal file
@@ -0,0 +1,616 @@
|
||||
// MinIO C++ Library for Amazon S3 Compatible Cloud Storage
|
||||
// Copyright 2022 MinIO, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#ifndef _MINIO_CREDS_PROVIDERS_H
|
||||
#define _MINIO_CREDS_PROVIDERS_H
|
||||
|
||||
#include <INIReader.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
|
||||
#include "credentials.h"
|
||||
#include "signer.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define DEFAULT_DURATION_SECONDS (60 * 60 * 24) // 1 day.
|
||||
#define MIN_DURATION_SECONDS (60 * 15) // 15 minutes.
|
||||
#define MAX_DURATION_SECONDS (60 * 60 * 24 * 7) // 7 days.
|
||||
|
||||
namespace minio {
|
||||
namespace creds {
|
||||
struct Jwt {
|
||||
std::string token;
|
||||
unsigned int expiry = 0;
|
||||
|
||||
operator bool() const { return !token.empty(); }
|
||||
}; // struct Jwt
|
||||
|
||||
using JwtFunction = std::function<Jwt()>;
|
||||
|
||||
static error::Error checkLoopbackHost(std::string host) {
|
||||
struct addrinfo hints = {0};
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
int status;
|
||||
struct addrinfo* res = NULL;
|
||||
if ((status = getaddrinfo(host.c_str(), NULL, &hints, &res)) != 0) {
|
||||
return error::Error(std::string("getaddrinfo: ") + gai_strerror(status));
|
||||
}
|
||||
|
||||
for (struct addrinfo* ai = res; ai != NULL; ai = ai->ai_next) {
|
||||
std::string ip(inet_ntoa(((struct sockaddr_in*)ai->ai_addr)->sin_addr));
|
||||
if (!utils::StartsWith(ip, "127.")) {
|
||||
return error::Error(host + " is not loopback only host");
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(res); // free the linked list
|
||||
|
||||
return error::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Credential provider interface.
|
||||
*/
|
||||
class Provider {
|
||||
protected:
|
||||
error::Error err_;
|
||||
Credentials creds_;
|
||||
|
||||
public:
|
||||
Provider() {}
|
||||
|
||||
virtual ~Provider() {}
|
||||
|
||||
operator bool() const { return !err_; }
|
||||
|
||||
virtual Credentials Fetch() = 0;
|
||||
}; // class Provider
|
||||
|
||||
class ChainedProvider : public Provider {
|
||||
private:
|
||||
std::list<Provider*> providers_;
|
||||
Provider* provider_ = NULL;
|
||||
|
||||
public:
|
||||
ChainedProvider(std::list<Provider*> providers) {
|
||||
this->providers_ = providers;
|
||||
}
|
||||
|
||||
Credentials Fetch() {
|
||||
if (err_) return Credentials{err_};
|
||||
|
||||
if (creds_) return creds_;
|
||||
|
||||
if (provider_ != NULL) {
|
||||
creds_ = provider_->Fetch();
|
||||
if (creds_) return creds_;
|
||||
}
|
||||
|
||||
for (auto provider : providers_) {
|
||||
provider_ = provider;
|
||||
creds_ = provider_->Fetch();
|
||||
if (creds_) return creds_;
|
||||
}
|
||||
|
||||
return Credentials{error::Error("All providers fail to fetch credentials")};
|
||||
}
|
||||
}; // class ChainedProvider
|
||||
|
||||
/**
|
||||
* Static credential provider.
|
||||
*/
|
||||
class StaticProvider : public Provider {
|
||||
public:
|
||||
StaticProvider(std::string access_key, std::string secret_key,
|
||||
std::string session_token = "") {
|
||||
this->creds_ =
|
||||
Credentials{error::SUCCESS, access_key, secret_key, session_token};
|
||||
}
|
||||
|
||||
Credentials Fetch() { return creds_; }
|
||||
}; // class StaticProvider
|
||||
|
||||
class EnvAwsProvider : public Provider {
|
||||
public:
|
||||
EnvAwsProvider() {
|
||||
std::string access_key;
|
||||
std::string secret_key;
|
||||
std::string session_token;
|
||||
|
||||
if (!utils::GetEnv(access_key, "AWS_ACCESS_KEY_ID")) {
|
||||
utils::GetEnv(access_key, "AWS_ACCESS_KEY");
|
||||
}
|
||||
if (!utils::GetEnv(secret_key, "AWS_SECRET_ACCESS_KEY")) {
|
||||
utils::GetEnv(secret_key, "AWS_SECRET_KEY");
|
||||
}
|
||||
utils::GetEnv(session_token, "AWS_SESSION_TOKEN");
|
||||
|
||||
this->creds_ =
|
||||
Credentials{error::SUCCESS, access_key, secret_key, session_token};
|
||||
}
|
||||
|
||||
Credentials Fetch() { return creds_; }
|
||||
}; // class EnvAwsProvider
|
||||
|
||||
class EnvMinioProvider : public Provider {
|
||||
public:
|
||||
EnvMinioProvider() {
|
||||
std::string access_key;
|
||||
std::string secret_key;
|
||||
|
||||
utils::GetEnv(access_key, "MINIO_ACCESS_KEY");
|
||||
utils::GetEnv(secret_key, "MINIO_SECRET_KEY");
|
||||
this->creds_ = Credentials{error::SUCCESS, access_key, secret_key};
|
||||
}
|
||||
|
||||
Credentials Fetch() { return creds_; }
|
||||
}; // class EnvMinioProvider
|
||||
|
||||
class AwsConfigProvider : public Provider {
|
||||
public:
|
||||
AwsConfigProvider(std::string filename = "", std::string profile = "") {
|
||||
if (filename.empty()) {
|
||||
if (!utils::GetEnv(filename, "AWS_SHARED_CREDENTIALS_FILE")) {
|
||||
filename = utils::GetHomeDir() + "/aws/credentials";
|
||||
}
|
||||
}
|
||||
|
||||
if (profile.empty()) {
|
||||
if (!utils::GetEnv(profile, "AWS_PROFILE")) profile = "default";
|
||||
}
|
||||
|
||||
INIReader reader(filename);
|
||||
if (reader.ParseError() < 0) {
|
||||
this->creds_ = Credentials{error::Error("unable to read " + filename)};
|
||||
} else {
|
||||
this->creds_ = Credentials{
|
||||
error::SUCCESS, reader.Get(profile, "aws_access_key_id", ""),
|
||||
reader.Get(profile, "aws_secret_access_key", ""),
|
||||
reader.Get(profile, "aws_session_token", "")};
|
||||
}
|
||||
}
|
||||
|
||||
Credentials Fetch() { return creds_; }
|
||||
}; // class AwsConfigProvider
|
||||
|
||||
class MinioClientConfigProvider : public Provider {
|
||||
public:
|
||||
MinioClientConfigProvider(std::string filename = "", std::string alias = "") {
|
||||
if (filename.empty()) filename = utils::GetHomeDir() + "/.mc/config.json";
|
||||
|
||||
if (alias.empty()) {
|
||||
if (!utils::GetEnv(alias, "MINIO_ALIAS")) alias = "s3";
|
||||
}
|
||||
|
||||
std::ifstream ifs(filename);
|
||||
nlohmann::json json = nlohmann::json::parse(ifs);
|
||||
ifs.close();
|
||||
|
||||
nlohmann::json aliases;
|
||||
if (json.contains("hosts")) {
|
||||
aliases = json["hosts"];
|
||||
} else if (json.contains("aliases")) {
|
||||
aliases = json["aliases"];
|
||||
} else {
|
||||
this->creds_ = Credentials{
|
||||
error::Error("invalid configuration in file " + filename)};
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aliases.contains(alias)) {
|
||||
this->creds_ = Credentials{error::Error(
|
||||
"alias " + alias + " not found in MinIO client configuration file " +
|
||||
filename)};
|
||||
return;
|
||||
}
|
||||
|
||||
this->creds_ = Credentials{error::SUCCESS, aliases[alias]["accessKey"],
|
||||
aliases[alias]["secretKey"]};
|
||||
}
|
||||
|
||||
Credentials Fetch() { return creds_; }
|
||||
}; // class MinioClientConfigProvider
|
||||
|
||||
class AssumeRoleProvider : public Provider {
|
||||
private:
|
||||
http::Url sts_endpoint_;
|
||||
std::string access_key_;
|
||||
std::string secret_key_;
|
||||
std::string region_;
|
||||
std::string body_;
|
||||
std::string content_sha256_;
|
||||
|
||||
public:
|
||||
AssumeRoleProvider(http::Url sts_endpoint, std::string access_key,
|
||||
std::string secret_key, unsigned int duration_seconds = 0,
|
||||
std::string policy = "", std::string region = "",
|
||||
std::string role_arn = "",
|
||||
std::string role_session_name = "",
|
||||
std::string external_id = "") {
|
||||
this->sts_endpoint_ = sts_endpoint;
|
||||
this->access_key_ = access_key;
|
||||
this->secret_key_ = secret_key;
|
||||
this->region_ = region;
|
||||
|
||||
if (duration_seconds < DEFAULT_DURATION_SECONDS) {
|
||||
duration_seconds = DEFAULT_DURATION_SECONDS;
|
||||
}
|
||||
|
||||
utils::Multimap map;
|
||||
map.Add("Action", "AssumeRole");
|
||||
map.Add("Version", "2011-06-15");
|
||||
map.Add("DurationSeconds", std::to_string(duration_seconds));
|
||||
if (!role_arn.empty()) map.Add("RoleArn", role_arn);
|
||||
if (!role_session_name.empty()) {
|
||||
map.Add("RoleSessionName", role_session_name);
|
||||
}
|
||||
if (!policy.empty()) map.Add("Policy", policy);
|
||||
if (!external_id.empty()) map.Add("ExternalId", external_id);
|
||||
|
||||
this->body_ = map.ToQueryString();
|
||||
this->content_sha256_ = utils::Sha256Hash(body_);
|
||||
}
|
||||
|
||||
Credentials Fetch() {
|
||||
if (err_) return Credentials{err_};
|
||||
|
||||
if (creds_) return creds_;
|
||||
|
||||
utils::Time date = utils::Time::Now();
|
||||
utils::Multimap headers;
|
||||
headers.Add("Content-Type", "application/x-www-form-urlencoded");
|
||||
headers.Add("Host", sts_endpoint_.host);
|
||||
headers.Add("X-Amz-Date", date.ToAmzDate());
|
||||
|
||||
http::Method method = http::Method::kPost;
|
||||
signer::SignV4STS(method, sts_endpoint_.path, region_, headers,
|
||||
utils::Multimap(), access_key_, secret_key_,
|
||||
content_sha256_, date);
|
||||
|
||||
http::Request req(method, sts_endpoint_);
|
||||
req.headers = headers;
|
||||
req.body = body_;
|
||||
http::Response resp = req.Execute();
|
||||
if (!resp) {
|
||||
creds_ = Credentials{resp.Error()};
|
||||
} else {
|
||||
creds_ = Credentials::ParseXML(resp.body, "AssumeRoleResult");
|
||||
}
|
||||
|
||||
return creds_;
|
||||
}
|
||||
}; // class AssumeRoleProvider
|
||||
|
||||
class WebIdentityClientGrantsProvider : public Provider {
|
||||
private:
|
||||
JwtFunction jwtfunc_ = NULL;
|
||||
http::Url sts_endpoint_;
|
||||
unsigned int duration_seconds_ = 0;
|
||||
std::string policy_;
|
||||
std::string role_arn_;
|
||||
std::string role_session_name_;
|
||||
|
||||
public:
|
||||
WebIdentityClientGrantsProvider(JwtFunction jwtfunc, http::Url sts_endpoint,
|
||||
unsigned int duration_seconds = 0,
|
||||
std::string policy = "",
|
||||
std::string role_arn = "",
|
||||
std::string role_session_name = "") {
|
||||
this->jwtfunc_ = jwtfunc;
|
||||
this->sts_endpoint_ = sts_endpoint;
|
||||
this->duration_seconds_ = duration_seconds;
|
||||
this->policy_ = policy;
|
||||
this->role_arn_ = role_arn;
|
||||
this->role_session_name_ = role_session_name;
|
||||
}
|
||||
|
||||
virtual bool IsWebIdentity() = 0;
|
||||
|
||||
unsigned int getDurationSeconds(unsigned int expiry) {
|
||||
if (duration_seconds_) expiry = duration_seconds_;
|
||||
if (expiry > MAX_DURATION_SECONDS) return MAX_DURATION_SECONDS;
|
||||
if (expiry == 0) return expiry;
|
||||
if (expiry < MIN_DURATION_SECONDS) return MIN_DURATION_SECONDS;
|
||||
return expiry;
|
||||
}
|
||||
|
||||
Credentials Fetch() {
|
||||
if (creds_) return creds_;
|
||||
|
||||
Jwt jwt = jwtfunc_();
|
||||
|
||||
utils::Multimap map;
|
||||
map.Add("Version", "2011-06-15");
|
||||
unsigned int duration_seconds = getDurationSeconds(jwt.expiry);
|
||||
if (duration_seconds) {
|
||||
map.Add("DurationSeconds", std::to_string(duration_seconds));
|
||||
}
|
||||
if (!policy_.empty()) map.Add("Policy", policy_);
|
||||
|
||||
if (IsWebIdentity()) {
|
||||
map.Add("Action", "AssumeRoleWithWebIdentity");
|
||||
map.Add("WebIdentityToken", jwt.token);
|
||||
if (!role_arn_.empty()) {
|
||||
map.Add("RoleArn", role_arn_);
|
||||
if (!role_session_name_.empty()) {
|
||||
map.Add("RoleSessionName", role_session_name_);
|
||||
} else {
|
||||
map.Add("RoleSessionName", utils::Time::Now().ToISO8601UTC());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
map.Add("Action", "AssumeRoleWithClientGrants");
|
||||
map.Add("Token", jwt.token);
|
||||
}
|
||||
|
||||
http::Url url = sts_endpoint_;
|
||||
url.query_string = map.ToQueryString();
|
||||
http::Request req(http::Method::kPost, url);
|
||||
http::Response resp = req.Execute();
|
||||
if (!resp) {
|
||||
creds_ = Credentials{resp.Error()};
|
||||
} else {
|
||||
creds_ = Credentials::ParseXML(
|
||||
resp.body, IsWebIdentity() ? "AssumeRoleWithWebIdentityResult"
|
||||
: "AssumeRoleWithClientGrantsResult");
|
||||
}
|
||||
return creds_;
|
||||
}
|
||||
}; // class WebIdentityClientGrantsProvider
|
||||
|
||||
class ClientGrantsProvider : public WebIdentityClientGrantsProvider {
|
||||
public:
|
||||
ClientGrantsProvider(JwtFunction jwtfunc, http::Url sts_endpoint,
|
||||
unsigned int duration_seconds = 0,
|
||||
std::string policy = "", std::string role_arn = "",
|
||||
std::string role_session_name = "")
|
||||
: WebIdentityClientGrantsProvider(jwtfunc, sts_endpoint, duration_seconds,
|
||||
policy, role_arn, role_session_name) {}
|
||||
bool IsWebIdentity() { return false; }
|
||||
}; // class ClientGrantsProvider
|
||||
|
||||
class WebIdentityProvider : public WebIdentityClientGrantsProvider {
|
||||
public:
|
||||
WebIdentityProvider(JwtFunction jwtfunc, http::Url sts_endpoint,
|
||||
unsigned int duration_seconds = 0,
|
||||
std::string policy = "", std::string role_arn = "",
|
||||
std::string role_session_name = "")
|
||||
: WebIdentityClientGrantsProvider(jwtfunc, sts_endpoint, duration_seconds,
|
||||
policy, role_arn, role_session_name) {}
|
||||
bool IsWebIdentity() { return true; }
|
||||
}; // class WebIdentityProvider
|
||||
|
||||
class IamAwsProvider : public Provider {
|
||||
private:
|
||||
http::Url custom_endpoint_;
|
||||
std::string token_file_;
|
||||
std::string aws_region_;
|
||||
std::string role_arn_;
|
||||
std::string role_session_name_;
|
||||
std::string relative_uri_;
|
||||
std::string full_uri_;
|
||||
|
||||
Credentials fetch(http::Url url) {
|
||||
http::Request req(http::Method::kGet, url);
|
||||
http::Response resp = req.Execute();
|
||||
if (!resp) return Credentials{resp.Error()};
|
||||
|
||||
nlohmann::json json = nlohmann::json::parse(resp.body);
|
||||
std::string code = json.value("Code", "Success");
|
||||
if (code != "Success") {
|
||||
return Credentials{error::Error(url.String() + " failed with code " +
|
||||
code + " and message " +
|
||||
json.value("Message", ""))};
|
||||
}
|
||||
|
||||
std::string expiration = json["Expiration"];
|
||||
return Credentials{error::SUCCESS, json["AccessKeyId"],
|
||||
json["SecretAccessKey"], json["Token"],
|
||||
utils::Time::FromISO8601UTC(expiration.c_str())};
|
||||
}
|
||||
|
||||
error::Error getRoleName(std::string& role_name, http::Url url) {
|
||||
http::Request req(http::Method::kGet, url);
|
||||
http::Response resp = req.Execute();
|
||||
if (!resp) return resp.Error();
|
||||
|
||||
std::list<std::string> role_names;
|
||||
std::string lines = resp.body;
|
||||
size_t pos;
|
||||
while ((pos = lines.find("\n")) != std::string::npos) {
|
||||
role_names.push_back(lines.substr(0, pos));
|
||||
lines.erase(0, pos + 1);
|
||||
}
|
||||
if (!lines.empty()) role_names.push_back(lines);
|
||||
|
||||
if (role_names.empty()) {
|
||||
return error::Error("no IAM roles attached to EC2 service " + url);
|
||||
}
|
||||
|
||||
role_name = utils::Trim(role_names.front(), '\r');
|
||||
return error::SUCCESS;
|
||||
}
|
||||
|
||||
public:
|
||||
IamAwsProvider(http::Url custom_endpoint = http::Url()) {
|
||||
this->custom_endpoint_ = custom_endpoint;
|
||||
utils::GetEnv(this->token_file_, "AWS_WEB_IDENTITY_TOKEN_FILE");
|
||||
utils::GetEnv(this->aws_region_, "AWS_REGION");
|
||||
utils::GetEnv(this->role_arn_, "AWS_ROLE_ARN");
|
||||
utils::GetEnv(this->role_session_name_, "AWS_ROLE_SESSION_NAME");
|
||||
utils::GetEnv(this->relative_uri_,
|
||||
"AWS_CONTAINER_CREDENTIALS_RELATIVE_URI");
|
||||
if (!this->relative_uri_.empty() && this->relative_uri_.front() != '/') {
|
||||
this->relative_uri_ = "/" + this->relative_uri_;
|
||||
}
|
||||
utils::GetEnv(this->full_uri_, "AWS_CONTAINER_CREDENTIALS_FULL_URI");
|
||||
}
|
||||
|
||||
Credentials Fetch() {
|
||||
if (creds_) return creds_;
|
||||
|
||||
http::Url url = custom_endpoint_;
|
||||
if (!token_file_.empty()) {
|
||||
if (!url) {
|
||||
url.https = true;
|
||||
url.host = "sts.amazonaws.com";
|
||||
if (!aws_region_.empty()) {
|
||||
url.host = "sts." + aws_region_ + ".amazonaws.com";
|
||||
}
|
||||
}
|
||||
|
||||
WebIdentityProvider provider = WebIdentityProvider(
|
||||
[&token_file = token_file_]() -> Jwt {
|
||||
std::ifstream ifs(token_file);
|
||||
nlohmann::json json = nlohmann::json::parse(ifs);
|
||||
ifs.close();
|
||||
return Jwt{json["access_token"], json["expires_in"]};
|
||||
},
|
||||
url, 0, "", role_arn_, role_session_name_);
|
||||
creds_ = provider.Fetch();
|
||||
return creds_;
|
||||
}
|
||||
|
||||
if (!relative_uri_.empty()) {
|
||||
if (!url) {
|
||||
url.https = true;
|
||||
url.host = "169.254.170.2";
|
||||
url.path = relative_uri_;
|
||||
}
|
||||
} else if (!full_uri_.empty()) {
|
||||
if (!url) url = http::Url::Parse(full_uri_);
|
||||
if (error::Error err = checkLoopbackHost(url.host)) {
|
||||
creds_ = Credentials{err};
|
||||
return creds_;
|
||||
}
|
||||
} else {
|
||||
if (!url) {
|
||||
url.https = true;
|
||||
url.host = "169.254.169.254";
|
||||
url.path = "/latest/meta-data/iam/security-credentials/";
|
||||
}
|
||||
|
||||
std::string role_name;
|
||||
if (error::Error err = getRoleName(role_name, url)) {
|
||||
creds_ = Credentials{err};
|
||||
return creds_;
|
||||
}
|
||||
|
||||
url.path += "/" + role_name;
|
||||
}
|
||||
|
||||
creds_ = fetch(url);
|
||||
return creds_;
|
||||
}
|
||||
}; // class IamAwsProvider
|
||||
|
||||
class LdapIdentityProvider : public Provider {
|
||||
private:
|
||||
http::Url sts_endpoint_;
|
||||
|
||||
public:
|
||||
LdapIdentityProvider(http::Url sts_endpoint, std::string ldap_username,
|
||||
std::string ldap_password) {
|
||||
this->sts_endpoint_ = sts_endpoint;
|
||||
utils::Multimap map;
|
||||
map.Add("Action", "AssumeRoleWithLDAPIdentity");
|
||||
map.Add("Version", "2011-06-15");
|
||||
map.Add("LDAPUsername", ldap_username);
|
||||
map.Add("LDAPPassword", ldap_password);
|
||||
this->sts_endpoint_.query_string = map.ToQueryString();
|
||||
}
|
||||
|
||||
Credentials Fetch() {
|
||||
if (creds_) return creds_;
|
||||
|
||||
http::Request req(http::Method::kPost, sts_endpoint_);
|
||||
http::Response resp = req.Execute();
|
||||
if (!resp) return Credentials{resp.Error()};
|
||||
|
||||
creds_ =
|
||||
Credentials::ParseXML(resp.body, "AssumeRoleWithLDAPIdentityResult");
|
||||
return creds_;
|
||||
}
|
||||
}; // class LdapIdentityProvider
|
||||
|
||||
struct CertificateIdentityProvider : public Provider {
|
||||
private:
|
||||
http::Url sts_endpoint_;
|
||||
std::string key_file_;
|
||||
std::string cert_file_;
|
||||
std::string ssl_cert_file_;
|
||||
|
||||
public:
|
||||
CertificateIdentityProvider(http::Url sts_endpoint, std::string key_file,
|
||||
std::string cert_file,
|
||||
std::string ssl_cert_file = "",
|
||||
unsigned int duration_seconds = 0) {
|
||||
if (!sts_endpoint.https) {
|
||||
this->err_ = error::Error("sts endpoint scheme must be HTTPS");
|
||||
return;
|
||||
}
|
||||
|
||||
if (key_file.empty() || cert_file.empty()) {
|
||||
this->err_ = error::Error("client key and certificate must be provided");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int expiry = duration_seconds;
|
||||
if (duration_seconds < DEFAULT_DURATION_SECONDS) {
|
||||
expiry = DEFAULT_DURATION_SECONDS;
|
||||
}
|
||||
|
||||
utils::Multimap map;
|
||||
map.Add("Action", "AssumeRoleWithCertificate");
|
||||
map.Add("Version", "2011-06-15");
|
||||
map.Add("DurationSeconds", std::to_string(expiry));
|
||||
|
||||
sts_endpoint_ = sts_endpoint;
|
||||
sts_endpoint_.query_string = map.ToQueryString();
|
||||
key_file_ = key_file;
|
||||
cert_file_ = cert_file;
|
||||
ssl_cert_file_ = ssl_cert_file;
|
||||
}
|
||||
|
||||
Credentials Fetch() {
|
||||
if (err_) return Credentials{err_};
|
||||
|
||||
if (creds_) return creds_;
|
||||
|
||||
http::Request req(http::Method::kPost, sts_endpoint_);
|
||||
req.ssl_cert_file = ssl_cert_file_;
|
||||
req.key_file = key_file_;
|
||||
req.cert_file = cert_file_;
|
||||
|
||||
http::Response resp = req.Execute();
|
||||
if (!resp) return Credentials{resp.Error()};
|
||||
|
||||
creds_ =
|
||||
Credentials::ParseXML(resp.body, "AssumeRoleWithCertificateResult");
|
||||
return creds_;
|
||||
}
|
||||
}; // struct CertificateIdentityProvider
|
||||
} // namespace creds
|
||||
} // namespace minio
|
||||
|
||||
#endif // #ifndef _MINIO_CREDS_PROVIDERS_H
|
||||
@@ -16,7 +16,8 @@
|
||||
#ifndef _MINIO_REQUEST_H
|
||||
#define _MINIO_REQUEST_H
|
||||
|
||||
#include "creds.h"
|
||||
#include "credentials.h"
|
||||
#include "providers.h"
|
||||
#include "signer.h"
|
||||
|
||||
namespace minio {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
SET(SRCS args.cc baseclient.cc client.cc creds.cc http.cc request.cc response.cc select.cc signer.cc types.cc utils.cc)
|
||||
SET(SRCS args.cc baseclient.cc client.cc http.cc request.cc response.cc select.cc signer.cc types.cc utils.cc)
|
||||
SET(S3CLIENT_INSTALL_LIST)
|
||||
|
||||
add_library(miniocpp STATIC ${SRCS})
|
||||
|
||||
@@ -927,17 +927,16 @@ minio::s3::BaseClient::GetPresignedObjectUrl(GetPresignedObjectUrlArgs args) {
|
||||
|
||||
if (provider_ != NULL) {
|
||||
creds::Credentials creds = provider_->Fetch();
|
||||
if (!creds.SessionToken().empty()) {
|
||||
query_params.Add("X-Amz-Security-Token", creds.SessionToken());
|
||||
if (!creds.session_token.empty()) {
|
||||
query_params.Add("X-Amz-Security-Token", creds.session_token);
|
||||
}
|
||||
|
||||
utils::Time date = utils::Time::Now();
|
||||
if (args.request_time) date = args.request_time;
|
||||
|
||||
std::string access_key = creds.AccessKey();
|
||||
std::string secret_key = creds.SecretKey();
|
||||
signer::PresignV4(args.method, url.host, url.path, region, query_params,
|
||||
access_key, secret_key, date, args.expiry_seconds);
|
||||
creds.access_key, creds.secret_key, date,
|
||||
args.expiry_seconds);
|
||||
url.query_string = query_params.ToQueryString();
|
||||
}
|
||||
|
||||
@@ -963,12 +962,10 @@ minio::s3::BaseClient::GetPresignedPostFormData(PostPolicy policy) {
|
||||
}
|
||||
|
||||
creds::Credentials creds = provider_->Fetch();
|
||||
std::string access_key = creds.AccessKey();
|
||||
std::string secret_key = creds.SecretKey();
|
||||
std::string session_token = creds.SessionToken();
|
||||
std::map<std::string, std::string> data;
|
||||
if (error::Error err = policy.FormData(data, access_key, secret_key,
|
||||
session_token, region)) {
|
||||
if (error::Error err =
|
||||
policy.FormData(data, creds.access_key, creds.secret_key,
|
||||
creds.session_token, region)) {
|
||||
return err;
|
||||
}
|
||||
return data;
|
||||
|
||||
59
src/creds.cc
59
src/creds.cc
@@ -1,59 +0,0 @@
|
||||
// MinIO C++ Library for Amazon S3 Compatible Cloud Storage
|
||||
// Copyright 2022 MinIO, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
#include "creds.h"
|
||||
|
||||
minio::creds::Credentials::Credentials(const Credentials& creds) {
|
||||
access_key_ = creds.access_key_;
|
||||
secret_key_ = creds.secret_key_;
|
||||
session_token_ = creds.session_token_;
|
||||
expiration_ = creds.expiration_;
|
||||
}
|
||||
|
||||
minio::creds::Credentials::Credentials(std::string_view access_key,
|
||||
std::string_view secret_key,
|
||||
std::string_view session_token,
|
||||
unsigned int expiration) {
|
||||
access_key_ = access_key;
|
||||
secret_key_ = secret_key;
|
||||
session_token_ = session_token;
|
||||
expiration_ = expiration;
|
||||
}
|
||||
|
||||
std::string minio::creds::Credentials::AccessKey() {
|
||||
return std::string(access_key_);
|
||||
}
|
||||
|
||||
std::string minio::creds::Credentials::SecretKey() {
|
||||
return std::string(secret_key_);
|
||||
}
|
||||
|
||||
std::string minio::creds::Credentials::SessionToken() {
|
||||
return std::string(session_token_);
|
||||
}
|
||||
|
||||
bool minio::creds::Credentials::IsExpired() { return expiration_ != 0; }
|
||||
|
||||
minio::creds::StaticProvider::StaticProvider(std::string_view access_key,
|
||||
std::string_view secret_key,
|
||||
std::string_view session_token) {
|
||||
creds_ = new Credentials(access_key, secret_key, session_token);
|
||||
}
|
||||
|
||||
minio::creds::StaticProvider::~StaticProvider() { delete creds_; }
|
||||
|
||||
minio::creds::Credentials minio::creds::StaticProvider::Fetch() {
|
||||
return *creds_;
|
||||
}
|
||||
@@ -195,6 +195,12 @@ minio::http::Response minio::http::Request::execute() {
|
||||
request.setOpt(new curlpp::Options::SslVerifyPeer(true));
|
||||
request.setOpt(new curlpp::Options::CaInfo(ssl_cert_file));
|
||||
}
|
||||
if (!key_file.empty()) {
|
||||
request.setOpt(new curlpp::Options::SslKey(key_file));
|
||||
}
|
||||
if (!cert_file.empty()) {
|
||||
request.setOpt(new curlpp::Options::SslCert(cert_file));
|
||||
}
|
||||
}
|
||||
|
||||
utils::CharBuffer charbuf((char *)body.data(), body.size());
|
||||
|
||||
@@ -170,14 +170,12 @@ void minio::s3::Request::BuildHeaders(http::Url& url,
|
||||
|
||||
if (provider != NULL) {
|
||||
creds::Credentials creds = provider->Fetch();
|
||||
if (!creds.SessionToken().empty()) {
|
||||
headers.Add("X-Amz-Security-Token", creds.SessionToken());
|
||||
if (!creds.session_token.empty()) {
|
||||
headers.Add("X-Amz-Security-Token", creds.session_token);
|
||||
}
|
||||
|
||||
std::string access_key = creds.AccessKey();
|
||||
std::string secret_key = creds.SecretKey();
|
||||
signer::SignV4S3(method, url.path, region, headers, query_params,
|
||||
access_key, secret_key, sha256, date);
|
||||
creds.access_key, creds.secret_key, sha256, date);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"openssl",
|
||||
"curlpp",
|
||||
"nlohmann-json",
|
||||
"inih",
|
||||
"pugixml"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user