从 nginx 流代理提取 HTTP 主机头

从 nginx 流代理提取 HTTP 主机头

我正在寻找使用 nginx 的流模块来代理 HTTP 流量的方法。由于ngx_stream_ssl_preread存在该模块,因此这对于 HTTPS 非常有效。这使我能够从 TLS 握手中提取请求的服务器名称,然后我可以使用该名称来确定应该将流代理到哪个服务器。但是,我似乎找不到纯 HTTP 的等效方法。

我认为这是因为大多数人只是使用普通的 HTTP 代理,因为代理服务器可以看到 HTTP 请求中的 Host 标头(因为它未加密)。不过,对于我的情况来说,使用流是一个更好的解决方案,而且它似乎比完整的 HTTP 代理更轻量级。

使用该ngx_stream_ssl_preread模块,您可以访问名为 的变量ssl_preread_server_name。我正在寻找可以提供基本相同功能的东西,但从HostHTTP 请求中的标头派生而来。有这样的事吗?

答案1

我找不到任何内置的方法,所以我自己实现了一个。

随着ngx_stream_js_模块模块和一些自定义 JavaScript (njs) 我可以从 HTTP 主机标头中读取服务器名称到 $preread_server_name 中,并配置与 HTTPS 非常相似的 HTTP 流量。

首先,安装 nginx 支持 JavaScript 所需的额外模块:

$ apt install nginx-module-njs

在nginx.conf中加载模块:

load_module modules/ngx_stream_js_module.so;

/etc/nginx/http_server_name.js实现读取服务器名称:

var server_name = '-';

/**
 * Read the server name from the HTTP stream.
 *
 * @param s
 *   Stream.
 */
function read_server_name(s) {
  s.on('upload', function (data, flags) {
    if (data.length || flags.last) {
      s.done();
    }

    // If we can find the Host header.
    var n = data.indexOf('\r\nHost: ');
    if (n != -1) {
      // Determine the start of the Host header value and of the next header.
      var start_host = n + 8;
      var next_header = data.indexOf('\r\n', start_host);

      // Extract the Host header value.
      server_name = data.substr(start_host, next_header - start_host);

      // Remove the port if given.
      var port_start = server_name.indexOf(':');
      if (port_start != -1) {
        server_name = server_name.substr(0, port_start);
      }
    }
  });
}

function get_server_name(s) {
  return server_name;
}

export default {read_server_name, get_server_name}

这是我的 stream.conf

stream {
  # The HTTP map is based on the server name read from the HTTP stream in
  # http_server_name.js.
  js_import main from http_server_name.js;
  js_set $preread_server_name main.get_server_name;

  map $preread_server_name $internal_port {
    foo.example.com 8080;
    bar.example.com 8081;
  }

  # The HTTPS map is based on the server name provided by the
  # ngx_stream_ssl_preread_module module.
  map $ssl_preread_server_name $ssl_internal_port {
    foo.example.com 8443;
    bar.example.com 8444;
  }

  server {
    listen 443;

    # Have $ssl_preread_server_name populated.
    ssl_preread on;

    proxy_protocol on;

    proxy_pass my_host:$ssl_internal_port;
  }

  server {
    listen 80;

    # Read the server name at the preread phase.
    js_preread main.read_server_name;

    proxy_protocol on;

    proxy_pass my_host:$internal_port;
  }
}

在 nginx.conf 中包含 stream.conf:

include /etc/nginx/stream.conf;

通过重新加载 nginx 应用配置:

$ service nginx reload

现在可以以一种方式配置代理服务器,以从 HTTP 和 HTTPS 的 PROXY 协议读取真实客户端数据:

server {
  listen 80 proxy_protocol;
  listen 443 ssl proxy_protocol;

  set_real_ip_from 10.0.0.0/8;
  set_real_ip_from 172.16.0.0/12;
  set_real_ip_from 192.168.0.0/16;
  set_real_ip_from fc00::/7;
  real_ip_header proxy_protocol;
  real_ip_recursive on;
}

相关内容