Possibly related to #64 but I need to change proxy headers and not response headers. I.e. I need to change header which are going to be send to a proxied server.
I am trying to make a proxy which serves files from OpenStack Swift with S3 protocol (Signature v2). I am trying to rewrite headers with r.headersOut['Authorization'] = <myvalue> but looks like it doesn't work.
My conf file is:
js_include /etc/nginx/include/s3redirect.js;
upstream storage_urls {
server 192.168.56.50:8080;
}
server {
listen 8085;
location / {
js_content redirectFuncation;
}
location @storage {
server_tokens off;
types { } default_type "";
proxy_pass http://storage_urls$uri;
}
}
My /etc/nginx/include/s3redirect.js is:
function redirectFuncation(r) {
var accessId = 'b6ec1a37512848cf854d10dc0054121b';
var secret = 'acb33be39add4b2b83edd3edb8c3783c';
var hmac = require('crypto').createHmac('sha1', secret);
var currentTime = new Date().toUTCString();
var stringToSign = "GET\n\n\n"+currentTime+"\n"+r.uri;
var s3signature = hmac.update(stringToSign).digest('base64');
r.headersOut['x-amz-date'] = currentTime;
r.headersOut['Authorization'] = 'AWS '+accessId+':'+s3signature;
r.internalRedirect("@storage");
}
Looks like there is a problem with headers. When the request is received by the Nginx there is already Authorization header but it should be rewritten to a new Authorization header. It looks like the old value is passed from proxy to S3 storage and a new r.headersOut['Authorization'] = 'AWS '+accessId+':'+s3signature; is ignored.
It there a way to clearly setup headers via njs which are going to be passed to S3?
Please, tell if it isn't a right place for questions. Regards
@porunov
In nginx, headersIn are immutable by design.
To send headers different from original headers to a proxied server, the proxy_set_header directive can be used. To send a completely new set of headers to a proxied server, the proxy_pass_request_headers directive can be used.
njs way is to define a nginx variable by js_set and use it with proxy_set_header directive.
@porunov
r.headersOut['x-amz-date'] = currentTime;
r.headersOut['Authorization'] = 'AWS '+accessId+':'+s3signature;
this way you set outgoing headers to the client.
var accessId = 'b6ec1a37512848cf854d10dc0054121b';
var secret = 'acb33be39add4b2b83edd3edb8c3783c';
var hmac = require('crypto').createHmac('sha1', secret);
BTW, it is better to store keys as env variables. See process.env.
var stringToSign = "GET\n\n\n"+currentTime+"\n"+r.uri;
template literals are supported:
var stringToSign = `GET\n\n\n${currentTime}\n${r.uri}`;
@xeioex You just saved my day. Thank you very much!
BTW, it is better to store keys as env variables. See process.env.
Those accessId and secret are fake, I used them for simplicity but process.env is an interesting feature.
Below I am posting a changed version of the code if someone else have the same questions. I was able to successfully retrieve data from S3 with Signature V2.
conf:
js_include /etc/nginx/include/s3redirect.js;
js_set $s3date s3date;
js_set $s3auth s3auth;
upstream storage_urls {
server 192.168.56.50:8080;
}
server {
listen 8085;
location / {
js_content redirectFuncation;
}
location @storage {
proxy_set_header Accept '*/*';
proxy_pass_request_headers off;
proxy_set_header Authorization $s3auth;
proxy_set_header Date $s3date;
proxy_set_header content-type '';
server_tokens off;
types { } default_type "";
proxy_pass http://storage_urls$uri;
}
}
/etc/nginx/include/s3redirect.js:
var awsS3Date = new Date().toUTCString();
function s3date(r) {
return awsS3Date;
}
function s3auth(r) {
// Private data should be placed in environment variables. These is just for simplicity
var accessId = 'b6ec1a37512848cf854d10dc0054121b';
var secret = 'acb33be39add4b2b83edd3edb8c3783c';
var hmac = require('crypto').createHmac('sha1', secret);
var stringToSign = 'GET\n\n\n'+awsS3Date+'\n'+r.uri;
var s3signature = hmac.update(stringToSign).digest('base64');
return 'AWS '+accessId+':'+s3signature;
}
function redirectFuncation(r) {
// Some custom headers validation logic is placed here
// If custom validation succeed, redirect to storage endpoint
r.internalRedirect("@storage");
}