s3v4: A crate for signing S3 requests and pre-signing URLs
reference
This crate provides a signature function that can be used to sign a request to an S3 endpoint
and a pre_signed_url function for generating a presigned URL.
Both functions return an Error generated by the error_chain crate which can be
converted to a String or accessed through the description method or display_chain
and backtrace methods in case a full backtrace is needed.
Examples showing how to upload and download data with signed headers or pre-signed
URLs are provided in the ./examples folder.
Examples
Signing a request
let signature: s3v4::Signature = s3v4::signature(
url,
method,
&access,
&secret,
®ion,
&"s3",
"UNSIGNED-PAYLOAD", ).map_err(|err| format!("Signature error: {}", err.display_chain()))?;
Using the signature data to make a request
Hyper
let req = Request::builder()
.method(Method::PUT)
.header("x-amz-content-sha256", "UNSIGNED-PAYLOAD")
.header("x-amz-date", &signature.date_time)
.header("authorization", &signature.auth_header)
Ureq
let agent = AgentBuilder::new().build();
let response = agent
.put(&uri)
.set("x-amz-content-sha256", "UNSIGNED-PAYLOAD")
.set("x-amz-date", &signature.date_time)
.set("authorization", &signature.auth_header)
Generting a pre-signed URL
let pre_signed_url = s3v4::pre_signed_url(
&access,
&secret,
expiration,
&url,
&method,
&payload_hash,
®ion,
&date_time,
&service,
)
.map_err(|err| format!("{:?}", err))?;
The following code can be used as is to generate a presigned URL.
use url;
fn main() -> Result<(), String> {
let url =
url::Url::parse(&std::env::args().nth(1).expect("missing url")).expect("malformed URL");
let access = std::env::args().nth(2).expect("missing access");
let secret = std::env::args().nth(3).expect("missing secret");
let method = std::env::args().nth(4).expect("missing method");
let expiration = std::env::args()
.nth(5)
.expect("missing expiration (seconds)")
.parse::<u64>()
.expect("wrong expiration format");
let region = std::env::args().nth(6).expect("missing region");
let service = std::env::args().nth(7).expect("missing service");
let date_time: chrono::DateTime<chrono::Utc> = match std::env::args().nth(8) {
Some(d) => chrono::DateTime::parse_from_rfc3339(&d)
.expect("Invalid date format (should be \"YYYY-MM-DDTHH:MM:SSZ)\"")
.into(),
None => chrono::Utc::now(),
};
let payload_hash = "UNSIGNED-PAYLOAD";
let pre_signed_url = s3v4::pre_signed_url(
&access,
&secret,
expiration,
&url,
&method,
&payload_hash,
®ion,
&date_time,
&service,
)
.map_err(|err| format!("{:?}", err))?;
println!("{}", pre_signed_url);
Ok(())
}
Run with
cargo run --example presign -- <endpoint URL> <access> <secret> <method> \
<expiration in seconds> <region> ["YYYY-MM-DDTHH:MM:SSZ" (timestamp)]
To send the request just use curl with
-I for HEAD requests
- --file-upload for
PUT requests
- nothing for
GET requests