Merge pull request #48724 from johanngan/sse-kms

Support SSE-KMS configuration with S3 client
This commit is contained in:
Antonio Andelic 2023-05-04 13:20:54 +02:00 committed by GitHub
commit a68a023ca7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 383 additions and 103 deletions

View File

@ -155,6 +155,9 @@ The following settings can be specified in configuration file for given endpoint
- `no_sign_request` - Ignore all the credentials so requests are not signed. Useful for accessing public buckets. - `no_sign_request` - Ignore all the credentials so requests are not signed. Useful for accessing public buckets.
- `header` — Adds specified HTTP header to a request to given endpoint. Optional, can be specified multiple times. - `header` — Adds specified HTTP header to a request to given endpoint. Optional, can be specified multiple times.
- `server_side_encryption_customer_key_base64` — If specified, required headers for accessing S3 objects with SSE-C encryption will be set. Optional. - `server_side_encryption_customer_key_base64` — If specified, required headers for accessing S3 objects with SSE-C encryption will be set. Optional.
- `server_side_encryption_kms_key_id` - If specified, required headers for accessing S3 objects with [SSE-KMS encryption](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html) will be set. If an empty string is specified, the AWS managed S3 key will be used. Optional.
- `server_side_encryption_kms_encryption_context` - If specified alongside `server_side_encryption_kms_key_id`, the given encryption context header for SSE-KMS will be set. Optional.
- `server_side_encryption_kms_bucket_key_enabled` - If specified alongside `server_side_encryption_kms_key_id`, the header to enable S3 bucket keys for SSE-KMS will be set. Optional, can be `true` or `false`, defaults to nothing (matches the bucket-level setting).
- `max_single_read_retries` — The maximum number of attempts during single read. Default value is `4`. Optional. - `max_single_read_retries` — The maximum number of attempts during single read. Default value is `4`. Optional.
- `max_put_rps`, `max_put_burst`, `max_get_rps` and `max_get_burst` - Throttling settings (see description above) to use for specific endpoint instead of per query. Optional. - `max_put_rps`, `max_put_burst`, `max_get_rps` and `max_get_burst` - Throttling settings (see description above) to use for specific endpoint instead of per query. Optional.
@ -173,6 +176,9 @@ The following settings can be specified in configuration file for given endpoint
<!-- <no_sign_request>false</no_sign_request> --> <!-- <no_sign_request>false</no_sign_request> -->
<!-- <header>Authorization: Bearer SOME-TOKEN</header> --> <!-- <header>Authorization: Bearer SOME-TOKEN</header> -->
<!-- <server_side_encryption_customer_key_base64>BASE64-ENCODED-KEY</server_side_encryption_customer_key_base64> --> <!-- <server_side_encryption_customer_key_base64>BASE64-ENCODED-KEY</server_side_encryption_customer_key_base64> -->
<!-- <server_side_encryption_kms_key_id>KMS_KEY_ID</server_side_encryption_kms_key_id> -->
<!-- <server_side_encryption_kms_encryption_context>KMS_ENCRYPTION_CONTEXT</server_side_encryption_kms_encryption_context> -->
<!-- <server_side_encryption_kms_bucket_key_enabled>true</server_side_encryption_kms_bucket_key_enabled> -->
<!-- <max_single_read_retries>4</max_single_read_retries> --> <!-- <max_single_read_retries>4</max_single_read_retries> -->
</endpoint-name> </endpoint-name>
</s3> </s3>

View File

@ -1055,7 +1055,11 @@ Configuration markup:
<access_key_id>your_access_key_id</access_key_id> <access_key_id>your_access_key_id</access_key_id>
<secret_access_key>your_secret_access_key</secret_access_key> <secret_access_key>your_secret_access_key</secret_access_key>
<region></region> <region></region>
<header>Authorization: Bearer SOME-TOKEN</header>
<server_side_encryption_customer_key_base64>your_base64_encoded_customer_key</server_side_encryption_customer_key_base64> <server_side_encryption_customer_key_base64>your_base64_encoded_customer_key</server_side_encryption_customer_key_base64>
<server_side_encryption_kms_key_id>your_kms_key_id</server_side_encryption_kms_key_id>
<server_side_encryption_kms_encryption_context>your_kms_encryption_context</server_side_encryption_kms_encryption_context>
<server_side_encryption_kms_bucket_key_enabled>true</server_side_encryption_kms_bucket_key_enabled>
<proxy> <proxy>
<uri>http://proxy1</uri> <uri>http://proxy1</uri>
<uri>http://proxy2</uri> <uri>http://proxy2</uri>
@ -1106,7 +1110,11 @@ Optional parameters:
- `min_bytes_for_seek` — Minimal number of bytes to use seek operation instead of sequential read. Default value is `1 Mb`. - `min_bytes_for_seek` — Minimal number of bytes to use seek operation instead of sequential read. Default value is `1 Mb`.
- `metadata_path` — Path on local FS to store metadata files for S3. Default value is `/var/lib/clickhouse/disks/<disk_name>/`. - `metadata_path` — Path on local FS to store metadata files for S3. Default value is `/var/lib/clickhouse/disks/<disk_name>/`.
- `skip_access_check` — If true, disk access checks will not be performed on disk start-up. Default value is `false`. - `skip_access_check` — If true, disk access checks will not be performed on disk start-up. Default value is `false`.
- `header` — Adds specified HTTP header to a request to given endpoint. Optional, can be specified multiple times.
- `server_side_encryption_customer_key_base64` — If specified, required headers for accessing S3 objects with SSE-C encryption will be set. - `server_side_encryption_customer_key_base64` — If specified, required headers for accessing S3 objects with SSE-C encryption will be set.
- `server_side_encryption_kms_key_id` - If specified, required headers for accessing S3 objects with [SSE-KMS encryption](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingKMSEncryption.html) will be set. If an empty string is specified, the AWS managed S3 key will be used. Optional.
- `server_side_encryption_kms_encryption_context` - If specified alongside `server_side_encryption_kms_key_id`, the given encryption context header for SSE-KMS will be set. Optional.
- `server_side_encryption_kms_bucket_key_enabled` - If specified alongside `server_side_encryption_kms_key_id`, the header to enable S3 bucket keys for SSE-KMS will be set. Optional, can be `true` or `false`, defaults to nothing (matches the bucket-level setting).
- `s3_max_put_rps` — Maximum PUT requests per second rate before throttling. Default value is `0` (unlimited). - `s3_max_put_rps` — Maximum PUT requests per second rate before throttling. Default value is `0` (unlimited).
- `s3_max_put_burst` — Max number of requests that can be issued simultaneously before hitting request per second limit. By default (`0` value) equals to `s3_max_put_rps`. - `s3_max_put_burst` — Max number of requests that can be issued simultaneously before hitting request per second limit. By default (`0` value) equals to `s3_max_put_rps`.
- `s3_max_get_rps` — Maximum GET requests per second rate before throttling. Default value is `0` (unlimited). - `s3_max_get_rps` — Maximum GET requests per second rate before throttling. Default value is `0` (unlimited).

View File

@ -135,7 +135,7 @@ func TestConfigFileFrameCopy(t *testing.T) {
sizes := map[string]int64{ sizes := map[string]int64{
"users.xml": int64(2017), "users.xml": int64(2017),
"default-password.xml": int64(188), "default-password.xml": int64(188),
"config.xml": int64(61260), "config.xml": int64(61662),
"server-include.xml": int64(168), "server-include.xml": int64(168),
"user-include.xml": int64(559), "user-include.xml": int64(559),
} }

View File

@ -1260,8 +1260,12 @@
<access_key_id>REPLACE_ME</access_key_id> <access_key_id>REPLACE_ME</access_key_id>
<secret_access_key>REPLACE_ME</secret_access_key> <secret_access_key>REPLACE_ME</secret_access_key>
<region></region> <region></region>
<header>Authorization: Bearer SOME-TOKEN</header>
<server_side_encryption_customer_key_base64>your_base64_encoded_customer_key <server_side_encryption_customer_key_base64>your_base64_encoded_customer_key
</server_side_encryption_customer_key_base64> </server_side_encryption_customer_key_base64>
<server_side_encryption_kms_key_id>REPLACE_ME</server_side_encryption_kms_key_id>
<server_side_encryption_kms_encryption_context>REPLACE_ME</server_side_encryption_kms_encryption_context>
<server_side_encryption_kms_bucket_key_enabled>true</server_side_encryption_kms_bucket_key_enabled>
<proxy> <proxy>
<uri>http://proxy1</uri> <uri>http://proxy1</uri>
<uri>http://proxy2</uri> <uri>http://proxy2</uri>

View File

@ -65,6 +65,7 @@ namespace
credentials.GetAWSAccessKeyId(), credentials.GetAWSAccessKeyId(),
credentials.GetAWSSecretKey(), credentials.GetAWSSecretKey(),
settings.auth_settings.server_side_encryption_customer_key_base64, settings.auth_settings.server_side_encryption_customer_key_base64,
settings.auth_settings.server_side_encryption_kms_config,
std::move(headers), std::move(headers),
S3::CredentialsConfiguration S3::CredentialsConfiguration
{ {

View File

@ -102,6 +102,7 @@ void KeeperSnapshotManagerS3::updateS3Configuration(const Poco::Util::AbstractCo
credentials.GetAWSAccessKeyId(), credentials.GetAWSAccessKeyId(),
credentials.GetAWSSecretKey(), credentials.GetAWSSecretKey(),
auth_settings.server_side_encryption_customer_key_base64, auth_settings.server_side_encryption_customer_key_base64,
auth_settings.server_side_encryption_kms_config,
std::move(headers), std::move(headers),
S3::CredentialsConfiguration S3::CredentialsConfiguration
{ {

View File

@ -142,6 +142,9 @@ std::unique_ptr<S3::Client> getClient(
= [proxy_config](const auto & request_config) { proxy_config->errorReport(request_config); }; = [proxy_config](const auto & request_config) { proxy_config->errorReport(request_config); };
} }
HTTPHeaderEntries headers = S3::getHTTPHeaders(config_prefix, config);
S3::ServerSideEncryptionKMSConfig sse_kms_config = S3::getSSEKMSConfig(config_prefix, config);
client_configuration.retryStrategy client_configuration.retryStrategy
= std::make_shared<Aws::Client::DefaultRetryStrategy>(config.getUInt(config_prefix + ".retry_attempts", 10)); = std::make_shared<Aws::Client::DefaultRetryStrategy>(config.getUInt(config_prefix + ".retry_attempts", 10));
@ -151,7 +154,8 @@ std::unique_ptr<S3::Client> getClient(
config.getString(config_prefix + ".access_key_id", ""), config.getString(config_prefix + ".access_key_id", ""),
config.getString(config_prefix + ".secret_access_key", ""), config.getString(config_prefix + ".secret_access_key", ""),
config.getString(config_prefix + ".server_side_encryption_customer_key_base64", ""), config.getString(config_prefix + ".server_side_encryption_customer_key_base64", ""),
{}, std::move(sse_kms_config),
std::move(headers),
S3::CredentialsConfiguration S3::CredentialsConfiguration
{ {
config.getBool(config_prefix + ".use_environment_credentials", config.getBool("s3.use_environment_credentials", true)), config.getBool(config_prefix + ".use_environment_credentials", config.getBool("s3.use_environment_credentials", true)),

View File

@ -174,6 +174,6 @@ void registerDiskS3(DiskFactory & factory, bool global_skip_access_check)
#else #else
void registerDiskS3(DiskFactory &, bool /* global_skip_access_check */) {} void registerDiskS3(DB::DiskFactory &, bool /* global_skip_access_check */) {}
#endif #endif

View File

@ -96,6 +96,7 @@ void verifyClientConfiguration(const Aws::Client::ClientConfiguration & client_c
std::unique_ptr<Client> Client::create( std::unique_ptr<Client> Client::create(
size_t max_redirects_, size_t max_redirects_,
ServerSideEncryptionKMSConfig sse_kms_config_,
const std::shared_ptr<Aws::Auth::AWSCredentialsProvider> & credentials_provider, const std::shared_ptr<Aws::Auth::AWSCredentialsProvider> & credentials_provider,
const Aws::Client::ClientConfiguration & client_configuration, const Aws::Client::ClientConfiguration & client_configuration,
Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy sign_payloads, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy sign_payloads,
@ -103,7 +104,7 @@ std::unique_ptr<Client> Client::create(
{ {
verifyClientConfiguration(client_configuration); verifyClientConfiguration(client_configuration);
return std::unique_ptr<Client>( return std::unique_ptr<Client>(
new Client(max_redirects_, credentials_provider, client_configuration, sign_payloads, use_virtual_addressing)); new Client(max_redirects_, std::move(sse_kms_config_), credentials_provider, client_configuration, sign_payloads, use_virtual_addressing));
} }
std::unique_ptr<Client> Client::create(const Client & other) std::unique_ptr<Client> Client::create(const Client & other)
@ -113,12 +114,14 @@ std::unique_ptr<Client> Client::create(const Client & other)
Client::Client( Client::Client(
size_t max_redirects_, size_t max_redirects_,
ServerSideEncryptionKMSConfig sse_kms_config_,
const std::shared_ptr<Aws::Auth::AWSCredentialsProvider> & credentials_provider, const std::shared_ptr<Aws::Auth::AWSCredentialsProvider> & credentials_provider,
const Aws::Client::ClientConfiguration & client_configuration, const Aws::Client::ClientConfiguration & client_configuration,
Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy sign_payloads, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy sign_payloads,
bool use_virtual_addressing) bool use_virtual_addressing)
: Aws::S3::S3Client(credentials_provider, client_configuration, std::move(sign_payloads), use_virtual_addressing) : Aws::S3::S3Client(credentials_provider, client_configuration, std::move(sign_payloads), use_virtual_addressing)
, max_redirects(max_redirects_) , max_redirects(max_redirects_)
, sse_kms_config(std::move(sse_kms_config_))
, log(&Poco::Logger::get("S3Client")) , log(&Poco::Logger::get("S3Client"))
{ {
auto * endpoint_provider = dynamic_cast<Aws::S3::Endpoint::S3DefaultEpProviderBase *>(accessEndpointProvider().get()); auto * endpoint_provider = dynamic_cast<Aws::S3::Endpoint::S3DefaultEpProviderBase *>(accessEndpointProvider().get());
@ -141,6 +144,7 @@ Client::Client(const Client & other)
, detect_region(other.detect_region) , detect_region(other.detect_region)
, provider_type(other.provider_type) , provider_type(other.provider_type)
, max_redirects(other.max_redirects) , max_redirects(other.max_redirects)
, sse_kms_config(other.sse_kms_config)
, log(&Poco::Logger::get("S3Client")) , log(&Poco::Logger::get("S3Client"))
{ {
cache = std::make_shared<ClientCache>(*other.cache); cache = std::make_shared<ClientCache>(*other.cache);
@ -178,6 +182,28 @@ void Client::insertRegionOverride(const std::string & bucket, const std::string
LOG_INFO(log, "Detected different region ('{}') for bucket {} than the one defined ('{}')", region, bucket, explicit_region); LOG_INFO(log, "Detected different region ('{}') for bucket {} than the one defined ('{}')", region, bucket, explicit_region);
} }
template <typename RequestType>
void Client::setKMSHeaders(RequestType & request) const
{
// Don't do anything unless a key ID was specified
if (sse_kms_config.key_id)
{
request.SetServerSideEncryption(Model::ServerSideEncryption::aws_kms);
// If the key ID was specified but is empty, treat it as using the AWS managed key and omit the header
if (!sse_kms_config.key_id->empty())
request.SetSSEKMSKeyId(*sse_kms_config.key_id);
if (sse_kms_config.encryption_context)
request.SetSSEKMSEncryptionContext(*sse_kms_config.encryption_context);
if (sse_kms_config.bucket_key_enabled)
request.SetBucketKeyEnabled(*sse_kms_config.bucket_key_enabled);
}
}
// Explicitly instantiate this method only for the request types that support KMS headers
template void Client::setKMSHeaders<CreateMultipartUploadRequest>(CreateMultipartUploadRequest & request) const;
template void Client::setKMSHeaders<CopyObjectRequest>(CopyObjectRequest & request) const;
template void Client::setKMSHeaders<PutObjectRequest>(PutObjectRequest & request) const;
Model::HeadObjectOutcome Client::HeadObject(const HeadObjectRequest & request) const Model::HeadObjectOutcome Client::HeadObject(const HeadObjectRequest & request) const
{ {
const auto & bucket = request.GetBucket(); const auto & bucket = request.GetBucket();
@ -574,6 +600,7 @@ std::unique_ptr<S3::Client> ClientFactory::create( // NOLINT
const String & access_key_id, const String & access_key_id,
const String & secret_access_key, const String & secret_access_key,
const String & server_side_encryption_customer_key_base64, const String & server_side_encryption_customer_key_base64,
ServerSideEncryptionKMSConfig sse_kms_config,
HTTPHeaderEntries headers, HTTPHeaderEntries headers,
CredentialsConfiguration credentials_configuration) CredentialsConfiguration credentials_configuration)
{ {
@ -596,6 +623,7 @@ std::unique_ptr<S3::Client> ClientFactory::create( // NOLINT
Aws::Utils::HashingUtils::Base64Encode(Aws::Utils::HashingUtils::CalculateMD5(str_buffer))}); Aws::Utils::HashingUtils::Base64Encode(Aws::Utils::HashingUtils::CalculateMD5(str_buffer))});
} }
// These will be added after request signing
client_configuration.extra_headers = std::move(headers); client_configuration.extra_headers = std::move(headers);
Aws::Auth::AWSCredentials credentials(access_key_id, secret_access_key); Aws::Auth::AWSCredentials credentials(access_key_id, secret_access_key);
@ -607,6 +635,7 @@ std::unique_ptr<S3::Client> ClientFactory::create( // NOLINT
client_configuration.retryStrategy = std::make_shared<Client::RetryStrategy>(std::move(client_configuration.retryStrategy)); client_configuration.retryStrategy = std::make_shared<Client::RetryStrategy>(std::move(client_configuration.retryStrategy));
return Client::create( return Client::create(
client_configuration.s3_max_redirects, client_configuration.s3_max_redirects,
std::move(sse_kms_config),
credentials_provider, credentials_provider,
client_configuration, // Client configuration. client_configuration, // Client configuration.
Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never,

View File

@ -1,7 +1,27 @@
#pragma once #pragma once
#include <optional>
#include <base/types.h>
#include "config.h" #include "config.h"
namespace DB::S3
{
/// See https://docs.aws.amazon.com/AmazonS3/latest/userguide/specifying-kms-encryption.html
/// Needed by S3Common.h even if USE_AWS_S3 is 0
struct ServerSideEncryptionKMSConfig
{
// If key_id is non-null, enable SSE-KMS. If key_id is "", use the AWS managed key
std::optional<String> key_id = std::nullopt;
std::optional<String> encryption_context = std::nullopt;
std::optional<bool> bucket_key_enabled = std::nullopt;
bool operator==(const ServerSideEncryptionKMSConfig & other) const = default;
};
}
#if USE_AWS_S3 #if USE_AWS_S3
#include <Common/assert_cast.h> #include <Common/assert_cast.h>
@ -85,6 +105,7 @@ public:
/// e.g. Client::RetryStrategy should be used /// e.g. Client::RetryStrategy should be used
static std::unique_ptr<Client> create( static std::unique_ptr<Client> create(
size_t max_redirects_, size_t max_redirects_,
ServerSideEncryptionKMSConfig sse_kms_config_,
const std::shared_ptr<Aws::Auth::AWSCredentialsProvider> & credentials_provider, const std::shared_ptr<Aws::Auth::AWSCredentialsProvider> & credentials_provider,
const Aws::Client::ClientConfiguration & client_configuration, const Aws::Client::ClientConfiguration & client_configuration,
Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy sign_payloads, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy sign_payloads,
@ -143,6 +164,13 @@ public:
std::shared_ptr<Aws::Client::RetryStrategy> wrapped_strategy; std::shared_ptr<Aws::Client::RetryStrategy> wrapped_strategy;
}; };
/// SSE-KMS headers MUST be signed, so they need to be added before the SDK signs the message
/// (before sending the request with one of the methods below).
/// Per the docs (https://docs.aws.amazon.com/AmazonS3/latest/userguide/specifying-kms-encryption.html),
/// the headers should only be set for PutObject, CopyObject, POST Object, and CreateMultipartUpload.
template <typename RequestType>
void setKMSHeaders(RequestType & request) const;
Model::HeadObjectOutcome HeadObject(const HeadObjectRequest & request) const; Model::HeadObjectOutcome HeadObject(const HeadObjectRequest & request) const;
Model::ListObjectsV2Outcome ListObjectsV2(const ListObjectsV2Request & request) const; Model::ListObjectsV2Outcome ListObjectsV2(const ListObjectsV2Request & request) const;
Model::ListObjectsOutcome ListObjects(const ListObjectsRequest & request) const; Model::ListObjectsOutcome ListObjects(const ListObjectsRequest & request) const;
@ -165,6 +193,7 @@ public:
ProviderType getProviderType() const; ProviderType getProviderType() const;
private: private:
Client(size_t max_redirects_, Client(size_t max_redirects_,
ServerSideEncryptionKMSConfig sse_kms_config_,
const std::shared_ptr<Aws::Auth::AWSCredentialsProvider>& credentials_provider, const std::shared_ptr<Aws::Auth::AWSCredentialsProvider>& credentials_provider,
const Aws::Client::ClientConfiguration& client_configuration, const Aws::Client::ClientConfiguration& client_configuration,
Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy sign_payloads, Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy sign_payloads,
@ -215,6 +244,8 @@ private:
const size_t max_redirects; const size_t max_redirects;
const ServerSideEncryptionKMSConfig sse_kms_config;
Poco::Logger * log; Poco::Logger * log;
}; };
@ -231,6 +262,7 @@ public:
const String & access_key_id, const String & access_key_id,
const String & secret_access_key, const String & secret_access_key,
const String & server_side_encryption_customer_key_base64, const String & server_side_encryption_customer_key_base64,
ServerSideEncryptionKMSConfig sse_kms_config,
HTTPHeaderEntries headers, HTTPHeaderEntries headers,
CredentialsConfiguration credentials_configuration); CredentialsConfiguration credentials_configuration);

View File

@ -100,9 +100,8 @@ namespace
std::mutex bg_tasks_mutex; std::mutex bg_tasks_mutex;
std::condition_variable bg_tasks_condvar; std::condition_variable bg_tasks_condvar;
void createMultipartUpload() void fillCreateMultipartRequest(S3::CreateMultipartUploadRequest & request)
{ {
S3::CreateMultipartUploadRequest request;
request.SetBucket(dest_bucket); request.SetBucket(dest_bucket);
request.SetKey(dest_key); request.SetKey(dest_key);
@ -116,6 +115,14 @@ namespace
if (!storage_class_name.empty()) if (!storage_class_name.empty())
request.SetStorageClass(Aws::S3::Model::StorageClassMapper::GetStorageClassForName(storage_class_name)); request.SetStorageClass(Aws::S3::Model::StorageClassMapper::GetStorageClassForName(storage_class_name));
client_ptr->setKMSHeaders(request);
}
void createMultipartUpload()
{
S3::CreateMultipartUploadRequest request;
fillCreateMultipartRequest(request);
ProfileEvents::increment(ProfileEvents::S3CreateMultipartUpload); ProfileEvents::increment(ProfileEvents::S3CreateMultipartUpload);
if (for_disk_s3) if (for_disk_s3)
ProfileEvents::increment(ProfileEvents::DiskS3CreateMultipartUpload); ProfileEvents::increment(ProfileEvents::DiskS3CreateMultipartUpload);
@ -465,6 +472,8 @@ namespace
/// If we don't do it, AWS SDK can mistakenly set it to application/xml, see https://github.com/aws/aws-sdk-cpp/issues/1840 /// If we don't do it, AWS SDK can mistakenly set it to application/xml, see https://github.com/aws/aws-sdk-cpp/issues/1840
request.SetContentType("binary/octet-stream"); request.SetContentType("binary/octet-stream");
client_ptr->setKMSHeaders(request);
} }
void processPutRequest(const S3::PutObjectRequest & request) void processPutRequest(const S3::PutObjectRequest & request)
@ -660,6 +669,8 @@ namespace
/// If we don't do it, AWS SDK can mistakenly set it to application/xml, see https://github.com/aws/aws-sdk-cpp/issues/1840 /// If we don't do it, AWS SDK can mistakenly set it to application/xml, see https://github.com/aws/aws-sdk-cpp/issues/1840
request.SetContentType("binary/octet-stream"); request.SetContentType("binary/octet-stream");
client_ptr->setKMSHeaders(request);
} }
void processCopyRequest(const S3::CopyObjectRequest & request) void processCopyRequest(const S3::CopyObjectRequest & request)

View File

@ -8,38 +8,66 @@
#include <Poco/Net/HTTPRequestHandlerFactory.h> #include <Poco/Net/HTTPRequestHandlerFactory.h>
#include <Poco/Net/HTTPServer.h> #include <Poco/Net/HTTPServer.h>
#include <Poco/Net/HTTPServerParams.h> #include <Poco/Net/HTTPServerParams.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Poco/Net/MessageHeader.h>
#include <Poco/Net/NetException.h> #include <Poco/Net/NetException.h>
#include <Poco/Net/ServerSocket.h> #include <Poco/Net/ServerSocket.h>
#include <Poco/AutoPtr.h> #include <Poco/AutoPtr.h>
#include <Poco/SharedPtr.h> #include <Poco/SharedPtr.h>
class MockRequestHandler : public Poco::Net::HTTPRequestHandler
{
Poco::Net::MessageHeader & last_request_header;
public:
MockRequestHandler(Poco::Net::MessageHeader & last_request_header_)
: Poco::Net::HTTPRequestHandler(), last_request_header(last_request_header_)
{
}
void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override
{
response.setStatus(Poco::Net::HTTPResponse::HTTP_OK);
last_request_header = request;
response.send();
}
};
template <typename RequestHandler>
class HTTPRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory class HTTPRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory
{ {
Poco::Net::MessageHeader & last_request_header;
virtual Poco::Net::HTTPRequestHandler * createRequestHandler(const Poco::Net::HTTPServerRequest &) override virtual Poco::Net::HTTPRequestHandler * createRequestHandler(const Poco::Net::HTTPServerRequest &) override
{ {
return new RequestHandler(); return new MockRequestHandler(last_request_header);
} }
public: public:
HTTPRequestHandlerFactory(Poco::Net::MessageHeader & last_request_header_)
: Poco::Net::HTTPRequestHandlerFactory(), last_request_header(last_request_header_)
{
}
virtual ~HTTPRequestHandlerFactory() override virtual ~HTTPRequestHandlerFactory() override
{ {
} }
}; };
template <typename RequestHandler>
class TestPocoHTTPServer class TestPocoHTTPServer
{ {
std::unique_ptr<Poco::Net::ServerSocket> server_socket; std::unique_ptr<Poco::Net::ServerSocket> server_socket;
Poco::SharedPtr<HTTPRequestHandlerFactory<RequestHandler>> handler_factory; Poco::SharedPtr<HTTPRequestHandlerFactory> handler_factory;
Poco::AutoPtr<Poco::Net::HTTPServerParams> server_params; Poco::AutoPtr<Poco::Net::HTTPServerParams> server_params;
std::unique_ptr<Poco::Net::HTTPServer> server; std::unique_ptr<Poco::Net::HTTPServer> server;
// Stores the last request header handled. It's obviously not thread-safe to share the same
// reference across request handlers, but it's good enough for this the purposes of this test.
Poco::Net::MessageHeader last_request_header;
public: public:
TestPocoHTTPServer(): TestPocoHTTPServer():
server_socket(std::make_unique<Poco::Net::ServerSocket>(0)), server_socket(std::make_unique<Poco::Net::ServerSocket>(0)),
handler_factory(new HTTPRequestHandlerFactory<RequestHandler>()), handler_factory(new HTTPRequestHandlerFactory(last_request_header)),
server_params(new Poco::Net::HTTPServerParams()), server_params(new Poco::Net::HTTPServerParams()),
server(std::make_unique<Poco::Net::HTTPServer>(handler_factory, *server_socket, server_params)) server(std::make_unique<Poco::Net::HTTPServer>(handler_factory, *server_socket, server_params))
{ {
@ -50,4 +78,9 @@ public:
{ {
return "http://" + server_socket->address().toString(); return "http://" + server_socket->address().toString();
} }
const Poco::Net::MessageHeader & getLastRequestHeader() const
{
return last_request_header;
}
}; };

View File

@ -7,13 +7,9 @@
#if USE_AWS_S3 #if USE_AWS_S3
#include <memory> #include <memory>
#include <ostream>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Poco/URI.h> #include <Poco/URI.h>
#include <aws/core/client/AWSError.h> #include <aws/core/client/AWSError.h>
@ -25,6 +21,7 @@
#include <IO/ReadBufferFromS3.h> #include <IO/ReadBufferFromS3.h>
#include <IO/ReadHelpers.h> #include <IO/ReadHelpers.h>
#include <IO/ReadSettings.h> #include <IO/ReadSettings.h>
#include <IO/WriteBufferFromS3.h>
#include <IO/S3Common.h> #include <IO/S3Common.h>
#include <IO/S3/Client.h> #include <IO/S3/Client.h>
#include <IO/HTTPHeaderEntries.h> #include <IO/HTTPHeaderEntries.h>
@ -41,83 +38,33 @@ public:
~NoRetryStrategy() override = default; ~NoRetryStrategy() override = default;
}; };
String getSSEAndSignedHeaders(const Poco::Net::MessageHeader & message_header)
TEST(IOTestAwsS3Client, AppendExtraSSECHeaders)
{ {
/// See https://github.com/ClickHouse/ClickHouse/pull/19748 String content;
for (const auto & [header_name, header_value] : message_header)
class MyRequestHandler : public Poco::Net::HTTPRequestHandler
{ {
public: if (boost::algorithm::starts_with(header_name, "x-amz-server-side-encryption"))
void handleRequest(Poco::Net::HTTPServerRequest & request, Poco::Net::HTTPServerResponse & response) override
{ {
response.setStatus(Poco::Net::HTTPResponse::HTTP_OK); content += header_name + ": " + header_value + "\n";
std::ostream & out = response.send();
for (const auto & [header_name, header_value] : request)
{
if (boost::algorithm::starts_with(header_name, "x-amz-server-side-encryption-customer-"))
{
out << header_name << ": " << header_value << "\n";
}
else if (header_name == "authorization")
{
std::vector<String> parts;
boost::split(parts, header_value, [](char c){ return c == ' '; });
for (const auto & part : parts)
{
if (boost::algorithm::starts_with(part, "SignedHeaders="))
out << header_name << ": ... " << part << " ...\n";
}
}
}
out.flush();
} }
}; else if (header_name == "authorization")
{
std::vector<String> parts;
boost::split(parts, header_value, [](char c){ return c == ' '; });
for (const auto & part : parts)
{
if (boost::algorithm::starts_with(part, "SignedHeaders="))
content += header_name + ": ... " + part + " ...\n";
}
}
}
return content;
}
TestPocoHTTPServer<MyRequestHandler> http; void doReadRequest(std::shared_ptr<const DB::S3::Client> client, const DB::S3::URI & uri)
{
DB::RemoteHostFilter remote_host_filter;
unsigned int s3_max_redirects = 100;
DB::S3::URI uri(http.getUrl() + "/IOTestAwsS3ClientAppendExtraHeaders/test.txt");
String access_key_id = "ACCESS_KEY_ID";
String secret_access_key = "SECRET_ACCESS_KEY";
String region = "us-east-1";
String version_id; String version_id;
UInt64 max_single_read_retries = 1; UInt64 max_single_read_retries = 1;
bool enable_s3_requests_logging = false;
DB::S3::PocoHTTPClientConfiguration client_configuration = DB::S3::ClientFactory::instance().createClientConfiguration(
region,
remote_host_filter,
s3_max_redirects,
enable_s3_requests_logging,
/* for_disk_s3 = */ false,
/* get_request_throttler = */ {},
/* put_request_throttler = */ {}
);
client_configuration.endpointOverride = uri.endpoint;
client_configuration.retryStrategy = std::make_shared<NoRetryStrategy>();
String server_side_encryption_customer_key_base64 = "Kv/gDqdWVGIT4iDqg+btQvV3lc1idlm4WI+MMOyHOAw=";
DB::HTTPHeaderEntries headers;
bool use_environment_credentials = false;
bool use_insecure_imds_request = false;
std::shared_ptr<DB::S3::Client> client = DB::S3::ClientFactory::instance().create(
client_configuration,
uri.is_virtual_hosted_style,
access_key_id,
secret_access_key,
server_side_encryption_customer_key_base64,
headers,
DB::S3::CredentialsConfiguration
{
.use_environment_credentials = use_environment_credentials,
.use_insecure_imds_request = use_insecure_imds_request
}
);
ASSERT_TRUE(client);
DB::ReadSettings read_settings; DB::ReadSettings read_settings;
DB::S3Settings::RequestSettings request_settings; DB::S3Settings::RequestSettings request_settings;
@ -133,7 +80,170 @@ TEST(IOTestAwsS3Client, AppendExtraSSECHeaders)
String content; String content;
DB::readStringUntilEOF(content, read_buffer); DB::readStringUntilEOF(content, read_buffer);
EXPECT_EQ(content, "authorization: ... SignedHeaders=amz-sdk-invocation-id;amz-sdk-request;content-type;host;x-amz-api-version;x-amz-content-sha256;x-amz-date, ...\nx-amz-server-side-encryption-customer-algorithm: AES256\nx-amz-server-side-encryption-customer-key: Kv/gDqdWVGIT4iDqg+btQvV3lc1idlm4WI+MMOyHOAw=\nx-amz-server-side-encryption-customer-key-md5: fMNuOw6OLU5GG2vc6RTA+g==\n"); }
void doWriteRequest(std::shared_ptr<const DB::S3::Client> client, const DB::S3::URI & uri)
{
UInt64 max_unexpected_write_error_retries = 1;
DB::S3Settings::RequestSettings request_settings;
request_settings.max_unexpected_write_error_retries = max_unexpected_write_error_retries;
DB::WriteBufferFromS3 write_buffer(
client,
uri.bucket,
uri.key,
request_settings
);
write_buffer.write('\0'); // doesn't matter what we write here, just needs to be something
write_buffer.finalize();
}
using RequestFn = std::function<void(std::shared_ptr<const DB::S3::Client>, const DB::S3::URI &)>;
void testServerSideEncryption(
RequestFn do_request,
String server_side_encryption_customer_key_base64,
DB::S3::ServerSideEncryptionKMSConfig sse_kms_config,
String expected_headers)
{
TestPocoHTTPServer http;
DB::RemoteHostFilter remote_host_filter;
unsigned int s3_max_redirects = 100;
DB::S3::URI uri(http.getUrl() + "/IOTestAwsS3ClientAppendExtraHeaders/test.txt");
String access_key_id = "ACCESS_KEY_ID";
String secret_access_key = "SECRET_ACCESS_KEY";
String region = "us-east-1";
bool enable_s3_requests_logging = false;
DB::S3::PocoHTTPClientConfiguration client_configuration = DB::S3::ClientFactory::instance().createClientConfiguration(
region,
remote_host_filter,
s3_max_redirects,
enable_s3_requests_logging,
/* for_disk_s3 = */ false,
/* get_request_throttler = */ {},
/* put_request_throttler = */ {}
);
client_configuration.endpointOverride = uri.endpoint;
client_configuration.retryStrategy = std::make_shared<NoRetryStrategy>();
DB::HTTPHeaderEntries headers;
bool use_environment_credentials = false;
bool use_insecure_imds_request = false;
std::shared_ptr<DB::S3::Client> client = DB::S3::ClientFactory::instance().create(
client_configuration,
uri.is_virtual_hosted_style,
access_key_id,
secret_access_key,
server_side_encryption_customer_key_base64,
sse_kms_config,
headers,
DB::S3::CredentialsConfiguration
{
.use_environment_credentials = use_environment_credentials,
.use_insecure_imds_request = use_insecure_imds_request
}
);
ASSERT_TRUE(client);
do_request(client, uri);
String content = getSSEAndSignedHeaders(http.getLastRequestHeader());
EXPECT_EQ(content, expected_headers);
}
TEST(IOTestAwsS3Client, AppendExtraSSECHeadersRead)
{
/// See https://github.com/ClickHouse/ClickHouse/pull/19748
testServerSideEncryption(
doReadRequest,
"Kv/gDqdWVGIT4iDqg+btQvV3lc1idlm4WI+MMOyHOAw=",
{},
"authorization: ... SignedHeaders="
"amz-sdk-invocation-id;"
"amz-sdk-request;"
"content-type;"
"host;"
"x-amz-api-version;"
"x-amz-content-sha256;"
"x-amz-date, ...\n"
"x-amz-server-side-encryption-customer-algorithm: AES256\n"
"x-amz-server-side-encryption-customer-key: Kv/gDqdWVGIT4iDqg+btQvV3lc1idlm4WI+MMOyHOAw=\n"
"x-amz-server-side-encryption-customer-key-md5: fMNuOw6OLU5GG2vc6RTA+g==\n");
}
TEST(IOTestAwsS3Client, AppendExtraSSECHeadersWrite)
{
/// See https://github.com/ClickHouse/ClickHouse/pull/19748
testServerSideEncryption(
doWriteRequest,
"Kv/gDqdWVGIT4iDqg+btQvV3lc1idlm4WI+MMOyHOAw=",
{},
"authorization: ... SignedHeaders="
"amz-sdk-invocation-id;"
"amz-sdk-request;"
"content-length;"
"content-md5;"
"content-type;"
"host;"
"x-amz-content-sha256;"
"x-amz-date, ...\n"
"x-amz-server-side-encryption-customer-algorithm: AES256\n"
"x-amz-server-side-encryption-customer-key: Kv/gDqdWVGIT4iDqg+btQvV3lc1idlm4WI+MMOyHOAw=\n"
"x-amz-server-side-encryption-customer-key-md5: fMNuOw6OLU5GG2vc6RTA+g==\n");
}
TEST(IOTestAwsS3Client, AppendExtraSSEKMSHeadersRead)
{
DB::S3::ServerSideEncryptionKMSConfig sse_kms_config;
sse_kms_config.key_id = "alias/test-key";
sse_kms_config.encryption_context = "arn:aws:s3:::bucket_ARN";
sse_kms_config.bucket_key_enabled = true;
// KMS headers shouldn't be set on a read request
testServerSideEncryption(
doReadRequest,
"",
sse_kms_config,
"authorization: ... SignedHeaders="
"amz-sdk-invocation-id;"
"amz-sdk-request;"
"content-type;"
"host;"
"x-amz-api-version;"
"x-amz-content-sha256;"
"x-amz-date, ...\n");
}
TEST(IOTestAwsS3Client, AppendExtraSSEKMSHeadersWrite)
{
DB::S3::ServerSideEncryptionKMSConfig sse_kms_config;
sse_kms_config.key_id = "alias/test-key";
sse_kms_config.encryption_context = "arn:aws:s3:::bucket_ARN";
sse_kms_config.bucket_key_enabled = true;
testServerSideEncryption(
doWriteRequest,
"",
sse_kms_config,
"authorization: ... SignedHeaders="
"amz-sdk-invocation-id;"
"amz-sdk-request;"
"content-length;"
"content-md5;"
"content-type;"
"host;"
"x-amz-content-sha256;"
"x-amz-date;"
"x-amz-server-side-encryption;"
"x-amz-server-side-encryption-aws-kms-key-id;"
"x-amz-server-side-encryption-bucket-key-enabled;"
"x-amz-server-side-encryption-context, ...\n"
"x-amz-server-side-encryption: aws:kms\n"
"x-amz-server-side-encryption-aws-kms-key-id: alias/test-key\n"
"x-amz-server-side-encryption-bucket-key-enabled: true\n"
"x-amz-server-side-encryption-context: arn:aws:s3:::bucket_ARN\n");
} }
#endif #endif

View File

@ -70,6 +70,41 @@ namespace ErrorCodes
namespace S3 namespace S3
{ {
HTTPHeaderEntries getHTTPHeaders(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config)
{
HTTPHeaderEntries headers;
Poco::Util::AbstractConfiguration::Keys subconfig_keys;
config.keys(config_elem, subconfig_keys);
for (const std::string & subkey : subconfig_keys)
{
if (subkey.starts_with("header"))
{
auto header_str = config.getString(config_elem + "." + subkey);
auto delimiter = header_str.find(':');
if (delimiter == std::string::npos)
throw Exception(ErrorCodes::INVALID_CONFIG_PARAMETER, "Malformed s3 header value");
headers.emplace_back(header_str.substr(0, delimiter), header_str.substr(delimiter + 1, String::npos));
}
}
return headers;
}
ServerSideEncryptionKMSConfig getSSEKMSConfig(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config)
{
ServerSideEncryptionKMSConfig sse_kms_config;
if (config.has(config_elem + ".server_side_encryption_kms_key_id"))
sse_kms_config.key_id = config.getString(config_elem + ".server_side_encryption_kms_key_id");
if (config.has(config_elem + ".server_side_encryption_kms_encryption_context"))
sse_kms_config.encryption_context = config.getString(config_elem + ".server_side_encryption_kms_encryption_context");
if (config.has(config_elem + ".server_side_encryption_kms_bucket_key_enabled"))
sse_kms_config.bucket_key_enabled = config.getBool(config_elem + ".server_side_encryption_kms_bucket_key_enabled");
return sse_kms_config;
}
AuthSettings AuthSettings::loadFromConfig(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config) AuthSettings AuthSettings::loadFromConfig(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config)
{ {
auto access_key_id = config.getString(config_elem + ".access_key_id", ""); auto access_key_id = config.getString(config_elem + ".access_key_id", "");
@ -93,26 +128,15 @@ AuthSettings AuthSettings::loadFromConfig(const std::string & config_elem, const
if (config.has(config_elem + ".no_sign_request")) if (config.has(config_elem + ".no_sign_request"))
no_sign_request = config.getBool(config_elem + ".no_sign_request"); no_sign_request = config.getBool(config_elem + ".no_sign_request");
HTTPHeaderEntries headers; HTTPHeaderEntries headers = getHTTPHeaders(config_elem, config);
Poco::Util::AbstractConfiguration::Keys subconfig_keys; ServerSideEncryptionKMSConfig sse_kms_config = getSSEKMSConfig(config_elem, config);
config.keys(config_elem, subconfig_keys);
for (const std::string & subkey : subconfig_keys)
{
if (subkey.starts_with("header"))
{
auto header_str = config.getString(config_elem + "." + subkey);
auto delimiter = header_str.find(':');
if (delimiter == std::string::npos)
throw Exception(ErrorCodes::INVALID_CONFIG_PARAMETER, "Malformed s3 header value");
headers.emplace_back(header_str.substr(0, delimiter), header_str.substr(delimiter + 1, String::npos));
}
}
return AuthSettings return AuthSettings
{ {
std::move(access_key_id), std::move(secret_access_key), std::move(access_key_id), std::move(secret_access_key),
std::move(region), std::move(region),
std::move(server_side_encryption_customer_key_base64), std::move(server_side_encryption_customer_key_base64),
std::move(sse_kms_config),
std::move(headers), std::move(headers),
use_environment_credentials, use_environment_credentials,
use_insecure_imds_request, use_insecure_imds_request,
@ -135,6 +159,7 @@ void AuthSettings::updateFrom(const AuthSettings & from)
headers = from.headers; headers = from.headers;
region = from.region; region = from.region;
server_side_encryption_customer_key_base64 = from.server_side_encryption_customer_key_base64; server_side_encryption_customer_key_base64 = from.server_side_encryption_customer_key_base64;
server_side_encryption_kms_config = from.server_side_encryption_kms_config;
if (from.use_environment_credentials.has_value()) if (from.use_environment_credentials.has_value())
use_environment_credentials = from.use_environment_credentials; use_environment_credentials = from.use_environment_credentials;

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <IO/S3/Client.h>
#include <IO/S3/PocoHTTPClient.h> #include <IO/S3/PocoHTTPClient.h>
#include <IO/HTTPHeaderEntries.h> #include <IO/HTTPHeaderEntries.h>
@ -14,7 +15,6 @@
#include <Common/Exception.h> #include <Common/Exception.h>
#include <Common/Throttler_fwd.h> #include <Common/Throttler_fwd.h>
#include <IO/S3/Client.h>
#include <IO/S3/URI.h> #include <IO/S3/URI.h>
#include <aws/core/Aws.h> #include <aws/core/Aws.h>
@ -71,6 +71,10 @@ namespace Poco::Util
namespace DB::S3 namespace DB::S3
{ {
HTTPHeaderEntries getHTTPHeaders(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config);
ServerSideEncryptionKMSConfig getSSEKMSConfig(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config);
struct AuthSettings struct AuthSettings
{ {
static AuthSettings loadFromConfig(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config); static AuthSettings loadFromConfig(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config);
@ -79,6 +83,7 @@ struct AuthSettings
std::string secret_access_key; std::string secret_access_key;
std::string region; std::string region;
std::string server_side_encryption_customer_key_base64; std::string server_side_encryption_customer_key_base64;
ServerSideEncryptionKMSConfig server_side_encryption_kms_config;
HTTPHeaderEntries headers; HTTPHeaderEntries headers;

View File

@ -238,9 +238,8 @@ void WriteBufferFromS3::finalizeImpl()
} }
} }
void WriteBufferFromS3::createMultipartUpload() void WriteBufferFromS3::fillCreateMultipartRequest(DB::S3::CreateMultipartUploadRequest & req)
{ {
DB::S3::CreateMultipartUploadRequest req;
req.SetBucket(bucket); req.SetBucket(bucket);
req.SetKey(key); req.SetKey(key);
@ -250,6 +249,14 @@ void WriteBufferFromS3::createMultipartUpload()
if (object_metadata.has_value()) if (object_metadata.has_value())
req.SetMetadata(object_metadata.value()); req.SetMetadata(object_metadata.value());
client_ptr->setKMSHeaders(req);
}
void WriteBufferFromS3::createMultipartUpload()
{
DB::S3::CreateMultipartUploadRequest req;
fillCreateMultipartRequest(req);
ProfileEvents::increment(ProfileEvents::S3CreateMultipartUpload); ProfileEvents::increment(ProfileEvents::S3CreateMultipartUpload);
if (write_settings.for_object_storage) if (write_settings.for_object_storage)
ProfileEvents::increment(ProfileEvents::DiskS3CreateMultipartUpload); ProfileEvents::increment(ProfileEvents::DiskS3CreateMultipartUpload);
@ -571,6 +578,8 @@ void WriteBufferFromS3::fillPutRequest(S3::PutObjectRequest & req)
/// If we don't do it, AWS SDK can mistakenly set it to application/xml, see https://github.com/aws/aws-sdk-cpp/issues/1840 /// If we don't do it, AWS SDK can mistakenly set it to application/xml, see https://github.com/aws/aws-sdk-cpp/issues/1840
req.SetContentType("binary/octet-stream"); req.SetContentType("binary/octet-stream");
client_ptr->setKMSHeaders(req);
} }
void WriteBufferFromS3::processPutRequest(const PutObjectTask & task) void WriteBufferFromS3::processPutRequest(const PutObjectTask & task)

View File

@ -61,6 +61,7 @@ private:
void processWithStrictParts(); void processWithStrictParts();
void processWithDynamicParts(); void processWithDynamicParts();
void fillCreateMultipartRequest(S3::CreateMultipartUploadRequest & req);
void createMultipartUpload(); void createMultipartUpload();
void writePart(); void writePart();
void completeMultipartUpload(); void completeMultipartUpload();

View File

@ -1252,6 +1252,7 @@ void StorageS3::Configuration::connect(ContextPtr context)
credentials.GetAWSAccessKeyId(), credentials.GetAWSAccessKeyId(),
credentials.GetAWSSecretKey(), credentials.GetAWSSecretKey(),
auth_settings.server_side_encryption_customer_key_base64, auth_settings.server_side_encryption_customer_key_base64,
auth_settings.server_side_encryption_kms_config,
std::move(headers), std::move(headers),
S3::CredentialsConfiguration{ S3::CredentialsConfiguration{
auth_settings.use_environment_credentials.value_or(context->getConfigRef().getBool("s3.use_environment_credentials", true)), auth_settings.use_environment_credentials.value_or(context->getConfigRef().getBool("s3.use_environment_credentials", true)),