nginx njs
前面通过 nginx 实现 bmcweb 代理时由于后端返回的 cookie 没有设置 Path 属性,因此需要修改 bmcweb 后端以添加该属性,实际上可以通过 njs 等 nginx 扩展实现同样的功能。

install

❯ yay -S nginx-mod-njs
❯ yay -Ql nginx-mod-njs
nginx-mod-njs /usr/lib/nginx/modules/ngx_http_js_module.so
nginx-mod-njs /usr/lib/nginx/modules/ngx_stream_js_module.so

❯ ls /usr/lib/nginx/modules/
ngx_http_js_module.so  ngx_stream_js_module.so

ArchLinux 构建的 nginx --prefix 构建参数是 /etc/nginx,且没有指定 --modules-path 构建参数,因此默认的 modules 目录在 /etc/nginx 下:

❯ sudo mkdir /etc/nginx/modules
❯ sudo cp /usr/lib/nginx/modules/* /etc/nginx/modules

load_module 支持指定动态模块的绝对路径:

load_module /usr/lib/nginx/modules/ngx_http_js_module.so;

因此,也可以不进行拷贝。

setup

load_module modules/ngx_http_js_module.so;

http {
    js_path "/etc/nginx/njs/";
    js_import main from modify_header.js;

    location /bmcweb/api {
        # delete Secure flag
        proxy_cookie_flags ~ nosecure;
        js_header_filter main.header_fix;
    }
}

njs script

由于 bmcweb 没有为 cookie 设置 Path 属性,我们可以通过 njs 进行添加:

sudo mkdir /etc/nginx/njs
sudo vi /etc/nginx/njs/modify_header.js

function cookies_filter(r) {
  let cookies = r.headersOut['Set-Cookie'];
  if (cookies !== undefined) {
    for (let c = 0; c < cookies.length; c++) {
      const cookie = cookies[c];
      const props = cookie.split(';');

      let i = 0;
      for (; i < props.length; i++) {
        const p = props[i];
        const o = p.indexOf('=');
        if (o !== -1) {
          const k = p.slice(0, o);
          if (k.trim() === 'Path') {
            break;
          }
        }
      }
      if (i === props.length) {
        // add a space after semicolon
        props.push(' Path=/');
      }

      cookies[c] = props.join(';');
    }

    r.headersOut['Set-Cookie'] = cookies;
  }
}

const header_fix = (r) => {
  cookies_filter(r);

  let headers = r.headersOut;
  // njs does not support for of yet
  Object.keys(headers).forEach(function(h) {
    if (h === 'Clear-Site-Data') {
      delete headers[h];
    } else if (h === 'Cross-Origin-Opener-Policy') {
      headers[h] = 'unsafe-none';
    }
  });

  r.headersOut = headers;
}

export default {
  cookies_filter,
  header_fix,
};

注意修改 js 文件之后 nginx 需要重新加载。

test

对 Cookie 修改的效果如下:

js: ['XSRF-TOKEN=aZvRx5NParYXJQRE6XdK; SameSite=Strict','SESSION=wYqedUQ6eKmkBlq2f3wT; SameSite=Strict; HttpOnly']

js: ['XSRF-TOKEN=aZvRx5NParYXJQRE6XdK; SameSite=Strict; Path=/','SESSION=wYqedUQ6eKmkBlq2f3wT; SameSite=Strict; HttpOnly; Path=/']

appendix

njs scripting language
https://nginx.org/en/docs/njs/

njs Compatibility
https://nginx.org/en/docs/njs/compatibility.html

Module ngx_http_js_module
https://nginx.org/en/docs/http/ngx_http_js_module.html

NGINX JavaScript examples
https://github.com/nginx/njs-examples/

r.error(string)
writes a string to the error log on the error level of logging

r.log(string)
writes a string to the error log on the info level of logging

r.warn(string)
writes a string to the error log on the warning level of logging

How to log in js_content?
https://github.com/nginx/njs/issues/76


最后修改于 2023-12-31