Skip to content

Commit aa5f93f

Browse files
committed
Add authentication to S3 GET requests
This is configurable through the `SCCACHE_S3_GET_AUTH` environment variable. When `"true"`, authentication will be performed in the same manner as for PUT requests, allowing completely private buckets to be used.
1 parent 646be05 commit aa5f93f

File tree

2 files changed

+44
-4
lines changed

2 files changed

+44
-4
lines changed

src/cache/s3.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ pub struct S3Cache {
4141
bucket: Rc<Bucket>,
4242
/// Credentials provider.
4343
provider: AutoRefreshingProvider<ChainProvider>,
44+
/// Whether or not to authenticate S3 GET requests.
45+
get_auth: bool,
4446
}
4547

4648
impl S3Cache {
@@ -57,9 +59,11 @@ impl S3Cache {
5759
let provider = AutoRefreshingProvider::new(ChainProvider::with_profile_providers(profile_providers, handle));
5860
//TODO: configurable SSL
5961
let bucket = Rc::new(Bucket::new(bucket, endpoint, Ssl::No, handle)?);
62+
let get_auth = env::var("SCCACHE_S3_GET_AUTH").unwrap_or("false".to_string()).to_lowercase() == "true";
6063
Ok(S3Cache {
6164
bucket: bucket,
6265
provider: provider,
66+
get_auth: get_auth,
6367
})
6468
}
6569
}
@@ -71,7 +75,8 @@ fn normalize_key(key: &str) -> String {
7175
impl Storage for S3Cache {
7276
fn get(&self, key: &str) -> SFuture<Cache> {
7377
let key = normalize_key(key);
74-
Box::new(self.bucket.get(&key).then(|result| {
78+
79+
let result_cb = |result| {
7580
match result {
7681
Ok(data) => {
7782
let hit = CacheRead::from(io::Cursor::new(data))?;
@@ -82,7 +87,24 @@ impl Storage for S3Cache {
8287
Ok(Cache::Miss)
8388
}
8489
}
85-
}))
90+
};
91+
92+
if self.get_auth {
93+
let bucket = self.bucket.clone();
94+
let authed = self.provider
95+
.credentials()
96+
.chain_err(|| {
97+
"failed to get AWS credentials"
98+
})
99+
.and_then(move |credentials| bucket.get(&key, Some(&credentials)))
100+
.then(result_cb);
101+
Box::new(authed)
102+
} else {
103+
let open = self.bucket
104+
.get(&key, None)
105+
.then(result_cb);
106+
Box::new(open)
107+
}
86108
}
87109

88110
fn put(&self, key: &str, entry: CacheWrite) -> SFuture<Duration> {

src/simples3/s3.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,29 @@ impl Bucket {
7777
})
7878
}
7979

80-
pub fn get(&self, key: &str) -> SFuture<Vec<u8>> {
80+
pub fn get(&self, key: &str, creds: Option<&AwsCredentials>) -> SFuture<Vec<u8>> {
8181
let url = format!("{}{}", self.base_url, key);
8282
debug!("GET {}", url);
8383
let url2 = url.clone();
84-
Box::new(self.client.get(url.parse().unwrap()).chain_err(move || {
84+
let mut request = Request::new(Method::Get, url.parse().unwrap());
85+
match creds {
86+
Some(creds) => {
87+
let mut canonical_headers = String::new();
88+
89+
if let Some(token) = creds.token().as_ref().map(|s| s.as_str()) {
90+
request.headers_mut()
91+
.set_raw("x-amz-security-token", vec!(token.as_bytes().to_vec()));
92+
canonical_headers.push_str(format!("{}:{}\n", "x-amz-security-token", token).as_ref());
93+
}
94+
let date = time::now_utc().rfc822().to_string();
95+
let auth = self.auth("GET", &date, key, "", &canonical_headers, "", creds);
96+
request.headers_mut().set_raw("Date", vec!(date.into_bytes()));
97+
request.headers_mut().set_raw("Authorization", vec!(auth.into_bytes()));
98+
}
99+
// request is fine as-is
100+
None => {},
101+
}
102+
Box::new(self.client.request(request).chain_err(move || {
85103
format!("failed GET: {}", url)
86104
}).and_then(|res| {
87105
if res.status().is_success() {

0 commit comments

Comments
 (0)