Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uploading fails when metadata is added in S3 upload presigned url #423

Closed
higumachan opened this issue Jan 21, 2022 · 9 comments
Closed
Assignees
Labels
bug This issue is a bug.

Comments

@higumachan
Copy link

higumachan commented Jan 21, 2022

What is the problem?

If you publish an upload presigned url with metadata like the following code, you will get an error when you try to upload data to that url.

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_s3::presigning::config::PresigningConfig;
use aws_sdk_s3::Region;
use serde_json::Value;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // part of create uri

    let region = Some("ap-northeast-1");
    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    let shared_config = aws_config::from_env().region(region_provider).load().await;

    let mut s3_config_builder = aws_sdk_s3::config::Builder::from(&shared_config);

    let s3_client = aws_sdk_s3::Client::from_conf(s3_config_builder.build());

    let presigned_request = {
        s3_client
            .put_object()
            .bucket(std::env::var("BUCKET").unwrap())
            .key("test-key")
            .metadata("some-metadata", "abcd") //  If you comment out this line, it works fine
            .presigned(PresigningConfig::expires_in(Duration::from_secs(3000))?)
            .await?
    };

    let uri = presigned_request.uri().to_string();

    // part of request

    let client = reqwest::Client::new();

    let resp = client
        .put(uri)
        .header("content-type", "application/octet-stream")
        .body("test")
        .send()
        .await?;
    assert_eq!(resp.status(), 200); // but get 403
    Ok(())
}

Version

├── aws-config v0.5.1
│   ├── aws-http v0.5.1
│   │   ├── aws-smithy-http v0.35.1
│   │   │   ├── aws-smithy-eventstream v0.35.1
│   │   │   │   ├── aws-smithy-types v0.35.1
│   │   │   ├── aws-smithy-types v0.35.1 (*)
│   │   ├── aws-smithy-types v0.35.1 (*)
│   │   ├── aws-types v0.5.1
│   │   │   ├── aws-smithy-async v0.35.1
│   │   │   ├── aws-smithy-types v0.35.1 (*)
│   ├── aws-sdk-sso v0.5.0
│   │   ├── aws-endpoint v0.5.1
│   │   │   ├── aws-smithy-http v0.35.1 (*)
│   │   │   ├── aws-types v0.5.1 (*)
│   │   ├── aws-http v0.5.1 (*)
│   │   ├── aws-sig-auth v0.5.1
│   │   │   ├── aws-sigv4 v0.5.1
│   │   │   │   ├── aws-smithy-eventstream v0.35.1 (*)
│   │   │   │   ├── aws-smithy-http v0.35.1 (*)
│   │   │   ├── aws-smithy-eventstream v0.35.1 (*)
│   │   │   ├── aws-smithy-http v0.35.1 (*)
│   │   │   ├── aws-types v0.5.1 (*)
│   │   ├── aws-smithy-async v0.35.1 (*)
│   │   ├── aws-smithy-client v0.35.1
│   │   │   ├── aws-smithy-async v0.35.1 (*)
│   │   │   ├── aws-smithy-http v0.35.1 (*)
│   │   │   ├── aws-smithy-http-tower v0.35.1
│   │   │   │   ├── aws-smithy-http v0.35.1 (*)
│   │   │   ├── aws-smithy-types v0.35.1 (*)
│   │   ├── aws-smithy-http v0.35.1 (*)
│   │   ├── aws-smithy-http-tower v0.35.1 (*)
│   │   ├── aws-smithy-json v0.35.1
│   │   │   └── aws-smithy-types v0.35.1 (*)
│   │   ├── aws-smithy-types v0.35.1 (*)
│   │   ├── aws-types v0.5.1 (*)
│   ├── aws-sdk-sts v0.5.0
│   │   ├── aws-endpoint v0.5.1 (*)
│   │   ├── aws-http v0.5.1 (*)
│   │   ├── aws-sig-auth v0.5.1 (*)
│   │   ├── aws-smithy-async v0.35.1 (*)
│   │   ├── aws-smithy-client v0.35.1 (*)
│   │   ├── aws-smithy-http v0.35.1 (*)
│   │   ├── aws-smithy-http-tower v0.35.1 (*)
│   │   ├── aws-smithy-query v0.35.1
│   │   │   ├── aws-smithy-types v0.35.1 (*)
│   │   ├── aws-smithy-types v0.35.1 (*)
│   │   ├── aws-smithy-xml v0.35.1
│   │   ├── aws-types v0.5.1 (*)
│   ├── aws-smithy-async v0.35.1 (*)
│   ├── aws-smithy-client v0.35.1 (*)
│   ├── aws-smithy-http v0.35.1 (*)
│   ├── aws-smithy-http-tower v0.35.1 (*)
│   ├── aws-smithy-json v0.35.1 (*)
│   ├── aws-smithy-types v0.35.1 (*)
│   ├── aws-types v0.5.1 (*)
├── aws-sdk-s3 v0.5.0
│   ├── aws-endpoint v0.5.1 (*)
│   ├── aws-http v0.5.1 (*)
│   ├── aws-sig-auth v0.5.1 (*)
│   ├── aws-sigv4 v0.5.1 (*)
│   ├── aws-smithy-async v0.35.1 (*)
│   ├── aws-smithy-client v0.35.1 (*)
│   ├── aws-smithy-eventstream v0.35.1 (*)
│   ├── aws-smithy-http v0.35.1 (*)
│   ├── aws-smithy-http-tower v0.35.1 (*)
│   ├── aws-smithy-types v0.35.1 (*)
│   ├── aws-smithy-xml v0.35.1 (*)
│   ├── aws-types v0.5.1 (*)
├── aws-smithy-http v0.35.1 (*)

Platform

Darwin MacBook-Pro-3.local 20.3.0 Darwin Kernel Version 20.3.0: Thu Jan 21 00:07:06 PST 2021; root:xnu-7195.81.3~1/RELEASE_X86_64 x86_64

AWS Services

No response

Description

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_s3::presigning::config::PresigningConfig;
use aws_sdk_s3::Region;
use serde_json::Value;
use std::time::Duration;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // part of create uri

    let region = Some("ap-northeast-1");
    let region_provider = RegionProviderChain::first_try(region.map(Region::new))
        .or_default_provider()
        .or_else(Region::new("us-west-2"));
    let shared_config = aws_config::from_env().region(region_provider).load().await;

    let mut s3_config_builder = aws_sdk_s3::config::Builder::from(&shared_config);

    let s3_client = aws_sdk_s3::Client::from_conf(s3_config_builder.build());

    let presigned_request = {
        s3_client
            .put_object()
            .bucket(std::env::var("BUCKET").unwrap())
            .key("test-key")
            .metadata("some-metadata", "abcd") //  If you comment out this line, it works fine
            .presigned(PresigningConfig::expires_in(Duration::from_secs(3000))?)
            .await?
    };

    let uri = presigned_request.uri().to_string();

    // part of request

    let client = reqwest::Client::new();

    let resp = client
        .put(uri)
        .header("content-type", "application/octet-stream")
        .body("test")
        .send()
        .await?;
    assert_eq!(resp.status(), 200); // but get 403
    Ok(())
}

Logs

No response

@higumachan higumachan added bug This issue is a bug. needs-triage This issue or PR still needs to be triaged. labels Jan 21, 2022
@rcoh
Copy link
Contributor

rcoh commented Jan 21, 2022

Seems like the presigner doesn't properly include metadata in the signature. Thanks for the bug report, we'll get this fixed!

@rcoh rcoh removed the needs-triage This issue or PR still needs to be triaged. label Jan 21, 2022
@Velfi Velfi added the investigating This issue is being investigated and/or work is in progress to resolve the issue. label Jan 24, 2022
@higumachan
Copy link
Author

if you don't mind, is it possible for me to contribute to this issue?

If I do, do you have any advice on which part I should check? (I'm focusing on https://github.com/awslabs/smithy-rs/tree/main/aws/rust-runtime/aws-sigv4 for now)

The investigation seems to have started, so if the fix has already started, I will wait.

@rcoh
Copy link
Contributor

rcoh commented Jan 24, 2022

it definitely is! Yeah it's probably a bug in aws-sigv4. I would probably generate the same URL with the AWS CLI, then compare and attempt to isolate the bug. We haven't started investigating or fix yet, but @Velfi was planning on digging in this week if you would like to collaborate

@higumachan
Copy link
Author

higumachan commented Jan 25, 2022

OK, if the problem is one that even I can create a PR to fix after investigation, please call me again!
I think I can do Rust in one way or another.

@Velfi
Copy link
Contributor

Velfi commented Jan 25, 2022

Hey @higumachan, I looked into this and I believe the problem is that you weren't attaching some headers when making your request with reqwest::Client::put.

Could you try updating that section of code to add the headers and let me know if that fixes things for you?

let resp = client
    .put(uri)
    // This will ensure that all headers on the presigned request get transferred over
    .headers(presigned_request.headers().clone())
    .header("content-type", "application/octet-stream")
    .body("test")
    .send()
    .await?;

To hopefully help future people, I made some changes to our presigning example in this PR that demonstrates how to convert a PresignedRequest into a cURL command.

@Velfi Velfi added response-requested Waiting on additional info and feedback. Will move to 'closing-soon' in 7 days. and removed investigating This issue is being investigated and/or work is in progress to resolve the issue. labels Jan 25, 2022
@higumachan
Copy link
Author

@Velfi

Thank you a lot! I was able to get it to work with the code here.

@github-actions github-actions bot removed the response-requested Waiting on additional info and feedback. Will move to 'closing-soon' in 7 days. label Jan 26, 2022
@higumachan
Copy link
Author

@Velfi @rcoh

Can I close this issue?

@Velfi
Copy link
Contributor

Velfi commented Jan 26, 2022

I can close it. Don't hesitate to let us know if you run into any more issues. Also, if you're still interested in contributing, we have a few "good first issue"s in the smithy-rs repo.

@Velfi Velfi closed this as completed Jan 26, 2022
@github-actions
Copy link

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see.
If you need more assistance, please either tag a team member or open a new issue that references this one.
If you wish to keep having a conversation with other community members under this issue feel free to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue is a bug.
Projects
None yet
Development

No branches or pull requests

3 participants