Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog.d/23029_s3_r2_checksum_invalidrequest.fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fixed `InvalidRequest` errors ("You can only specify one non-default checksum at a time") from the `aws_s3` sink and source against S3-compatible services such as Cloudflare R2. The AWS SDK for Rust now calculates an `x-amz-checksum-*` request checksum by default, which conflicts with the `Content-MD5` header Vector already sends and which some S3-compatible providers reject outright. Request checksum calculation and response checksum validation are now restricted to only when required, matching AWS's own guidance for third-party S3-compatible endpoints.

authors: Socialpranker
52 changes: 49 additions & 3 deletions src/common/s3.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use aws_sdk_s3::config;
use aws_sdk_s3::config::{self, RequestChecksumCalculation, ResponseChecksumValidation};

use crate::aws::ClientBuilder;

Expand All @@ -10,8 +10,54 @@ impl ClientBuilder for S3ClientBuilder {
type Client = aws_sdk_s3::client::Client;

fn build(&self, config: &aws_types::SdkConfig) -> Self::Client {
let builder =
config::Builder::from(config).force_path_style(self.force_path_style.unwrap_or(true));
let builder = config::Builder::from(config)
.force_path_style(self.force_path_style.unwrap_or(true))
// The AWS SDK defaults to always calculating a `x-amz-checksum-*` request
// checksum and validating response checksums. Vector already computes and
// sends its own `Content-MD5` header for `PutObject` requests (see
// `s3_common::service::S3Service`), so the SDK's additional checksum is
// redundant against AWS S3 and gets rejected outright by S3-compatible
// providers such as Cloudflare R2 with
// "InvalidRequest: You can only specify one non-default checksum at a time."
// Restricting checksum calculation/validation to only when required keeps
// AWS S3 behavior correct while fixing compatibility with those providers.
// See https://github.com/vectordotdev/vector/issues/23029 and
// https://github.com/awslabs/aws-sdk-rust/issues/1240.
.request_checksum_calculation(RequestChecksumCalculation::WhenRequired)
.response_checksum_validation(ResponseChecksumValidation::WhenRequired);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep S3 response validation enabled for AWS downloads

When the aws_s3 source reads from real AWS S3, its get_object() call does not set checksum_mode(Enabled) (src/sources/aws_s3/sqs.rs:677-680). Setting ResponseChecksumValidation::WhenRequired here tells the SDK to validate only when the caller explicitly enables checksums, so source downloads lose the SDK's default response checksum validation and can accept a corrupted object body that would previously fail while being read. The R2 failure is caused by the extra request checksum on PutObject, so response validation should be left at the default or disabled only for the compatible-endpoint case.

Useful? React with 👍 / 👎.

aws_sdk_s3::client::Client::from_conf(builder.build())
}
}

#[cfg(test)]
mod tests {
use aws_types::{SdkConfig, region::Region};

use super::*;

#[test]
fn checksum_behavior_defaults_to_when_required() {
let sdk_config = SdkConfig::builder()
.region(Region::new("us-east-1"))
.build();

let client = S3ClientBuilder {
force_path_style: None,
}
.build(&sdk_config);

let config = client.config();

assert_eq!(
config.request_checksum_calculation(),
Some(&RequestChecksumCalculation::WhenRequired),
"request checksum calculation must be restricted to when required so Vector's own \
Content-MD5 header isn't paired with an SDK-added x-amz-checksum-* header, which \
S3-compatible providers such as Cloudflare R2 reject as an InvalidRequest",
);
assert_eq!(
config.response_checksum_validation(),
Some(&ResponseChecksumValidation::WhenRequired),
);
}
}
Loading