Skip to content

Instantly share code, notes, and snippets.

@rezan
Last active April 22, 2020 07:03
Show Gist options
  • Save rezan/bb798b7dc5fcaae30ea015d2ce7ac194 to your computer and use it in GitHub Desktop.
Save rezan/bb798b7dc5fcaae30ea015d2ce7ac194 to your computer and use it in GitHub Desktop.
S3 v4 request signing in VCL
#
# S3 v4 request signing (v1.6)
#
import crypto;
import goto;
import kvstore;
import std;
import urlplus;
import utils;
import xbody;
/*
# S3 v4 signing conf file format
# In vcl_init: s3_settings.init_conf("/etc/varnish/s3.conf");
# These are the defaults
access-key = AKIA....
secret-key = abcd1234....
bucket = bucket-name
region = somewhere
host = s3-somehwere.amazonaws.com
# This section is used if the Host == somehost.com
[somehost.com]
access-key = AKIA....
secret_key = wxyz6789....
bucket = another-bucket
region = elsewhere
host = another-bucket.amazonaws.com
*/
sub vcl_init
{
new s3_settings = kvstore.init();
new s3_stats = kvstore.init();
}
# Grab the bereq.body hash (optional)
sub s3_v4_req_body
{
if (req.method == "PUT" && !req.http.x-amz-content-sha256) {
std.cache_req_body(25MB);
set req.http.x-amz-content-sha256 = crypto.hex_encode(xbody.get_req_body_hash(sha256));
}
}
# Can we sign the bereq
sub s3_v4_sign
{
unset bereq.http.s3-prefix;
if (s3_settings.get(bereq.http.Host + "_access-key")) {
set bereq.http.s3-prefix = bereq.http.Host + "_";
}
if (bereq.http.Authorization !~ "AWS4-HMAC-SHA256" &&
s3_settings.get(bereq.http.s3-prefix + "access-key")) {
call s3_v4_sign_go;
}
}
# Sign the actual request
sub s3_v4_sign_go
{
s3_stats.counter("signatures", 1, varnishstat = true);
if (s3_settings.get(bereq.http.s3-prefix + "host")) {
set bereq.http.Host = s3_settings.get(bereq.http.s3-prefix + "host");
set bereq.backend = goto.dns_backend(bereq.http.Host, ssl = true);
}
set bereq.http.x-amz-date = utils.time_format("%Y%m%dT%H%M%SZ");
set bereq.http.s3-datestamp = utils.time_format("%Y%m%d");
set bereq.http.s3-signed-headers = "host;x-amz-content-sha256;x-amz-date";
set bereq.http.s3-scope = bereq.http.s3-datestamp + "/" +
s3_settings.get(bereq.http.s3-prefix + "region") + "/" +
s3_settings.get(bereq.http.s3-prefix + "service", "s3") +
"/aws4_request";
if (!bereq.http.x-amz-content-sha256) {
set bereq.http.x-amz-content-sha256 = crypto.hex_encode(crypto.hash(sha256, ""));
}
set bereq.http.s3-canonical-request =
crypto.hex_encode(crypto.hash(sha256,
bereq.method + utils.newline() +
urlplus.url_as_string() + utils.newline() +
urlplus.query_as_string() + utils.newline() +
"host:" + bereq.http.Host + utils.newline() +
"x-amz-content-sha256:" + bereq.http.x-amz-content-sha256 + utils.newline() +
"x-amz-date:" + bereq.http.x-amz-date + utils.newline() +
utils.newline() +
bereq.http.s3-signed-headers + utils.newline() +
bereq.http.x-amz-content-sha256)
);
set bereq.http.s3-signature =
crypto.hex_encode(crypto.hmac(sha256,
crypto.hmac(sha256,
crypto.hmac(sha256,
crypto.hmac(sha256,
crypto.hmac(sha256,
crypto.blob("AWS4" + s3_settings.get(bereq.http.s3-prefix + "secret-key")),
bereq.http.s3-datestamp),
s3_settings.get(bereq.http.s3-prefix + "region")),
s3_settings.get(bereq.http.s3-prefix + "service", "s3")),
"aws4_request"),
"AWS4-HMAC-SHA256" + utils.newline() +
bereq.http.x-amz-date + utils.newline() +
bereq.http.s3-scope + utils.newline() +
bereq.http.s3-canonical-request)
);
set bereq.http.Authorization =
"AWS4-HMAC-SHA256 " +
"Credential=" + s3_settings.get(bereq.http.s3-prefix + "access-key") + "/" +
bereq.http.s3-scope + ", " +
"SignedHeaders=" + bereq.http.s3-signed-headers + ", " +
"Signature=" + bereq.http.s3-signature;
unset bereq.http.s3-prefix;
unset bereq.http.s3-datestamp;
unset bereq.http.s3-signed-headers;
unset bereq.http.s3-scope;
unset bereq.http.s3-canonical-request;
unset bereq.http.s3-signature;
}
# Ignore vcc unused warning for s3_v4_req_body
sub vcl_recv
{
if (0) {
call s3_v4_req_body;
}
}
#EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment