Njs: How to change proxied header?

Created on 9 Jul 2019  路  3Comments  路  Source: nginx/njs

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

answered question

All 3 comments

@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");
}
Was this page helpful?
0 / 5 - 0 ratings