nginx/njs

context for js_var variables

Closed this issue · 2 comments

Using nginx/1.25.3 on debian from mainline repo.

During my test moving from lua to njs, I got some problems with njs variables and context, say, 2 vhost using same variable name, like

Config:

server {
       listen poc:80;
       server_name poc;

       set         $MYVAR       "VAR_POC";
       js_var      $MY_JS_VAR   "JS_POC";
       js_import   test from njs/test/dist/main.js;

       location / {
              js_content  test.hello;
       }
}
server {
       listen poc:80;
       server_name poc2;

       set         $MYVAR       "VAR_POC2";
       js_var      $MY_JS_VAR   "JS_POC2";
       js_import   test from njs/test/dist/main.js;

       location / {
              js_content  test.hello;
       }
}

js:

function hello(r) {
    let res = "Hello world!\n";
    res += "MYVAR=" + r.variables.MYVAR + "\n";
    res += "MY_JS_VAR=" + r.variables.MY_JS_VAR + "\n";

    r.return(200, res);
}

export default {hello}

testing:
curl http://poc

Hello world!
MYVAR=VAR_POC
MY_JS_VAR=JS_POC2

curl http://poc2

Hello world!
MYVAR=VAR_POC
MY_JS_VAR=JS_POC2

Look, always having MY_JS_VAR=JS_POC2, but for nginx native set, got the good value. This is annoying, the context is broken, value is overwrittened.

Another point, trying the exact same code, but with SSL on the host and trying to set ssl certs using njs, like

# get cert/key from shard dict or disk
js_set          $dynamic_ssl_cert getcert.cert;
js_set          $dynamic_ssl_key  getcert.key;

# use certs
ssl_certificate         data:$dynamic_ssl_cert;
ssl_certificate_key     data:$dynamic_ssl_key;

I can access the MY_JS_VAR variable using njs r.variables.MY_JS_VAR, but the var is wrong as I got multiple servers definition using same js_var name, but can't access the native MYVAR value, always empty.

Do I misunderstand something / what's the way ?

Hi @jaysee,

$dynamic_ssl_cert and $dynamic_ssl_key are called quite early while establishing TLS connection, even before NGX_HTTP_SERVER_REWRITE_PHASE where set directives actually set a value. That is why at this moment $MYVAR is empty. Use $ssl_server_name variable, this variable should be already available at the moment.

Example:

import fs from 'fs';

function cert(r) {
    let sn = r.variables.ssl_server_name;
    let path = ngx.conf_prefix + sn + '.cert.pem';
    r.log(`Reading ${path} for ${sn}`);
    return fs.readFileSync(path);
}

function key(r) {
    let sn = r.variables.ssl_server_name;
    let path = ngx.conf_prefix + sn + '.key.pem';
    r.log(`Reading ${path} for ${sn}`);
    return fs.readFileSync(path);
}

export default {cert, key};

The js_var is global, and is intended to be used for two additional cases which are not possible with set vars:

  1. Because js_var is not set every time at NGX_HTTP_SERVER_REWRITE_PHASE, js_var can be used to pass some information between locations during internalRedirect() from one location to another (example).
  2. js_var is always writable from njs, unlike a set varible.

hi @xeioex,

Yes, ssl_server_name is available, but I'm using multiples servers definitions (based on the listening IP, is it possible to get this one?), each server definition use a different location where it needs to look for certs (then it loop on dirs and search for a matching cert using x509 against ssl_server_name).

So there is no way to setup a configuration variable to target a dir where to search for certs ?
I need to go throught all my certs to find the matching one... not my prefered way, but should be OK... 2 backends should not have a cert for a ssl_server_name, and even, if cert is valid, it is OK to use it...

thanks for clarification, love njs.