我正在寻找使用 nginx 的流模块来代理 HTTP 流量的方法。由于ngx_stream_ssl_preread
存在该模块,因此这对于 HTTPS 非常有效。这使我能够从 TLS 握手中提取请求的服务器名称,然后我可以使用该名称来确定应该将流代理到哪个服务器。但是,我似乎找不到纯 HTTP 的等效方法。
我认为这是因为大多数人只是使用普通的 HTTP 代理,因为代理服务器可以看到 HTTP 请求中的 Host 标头(因为它未加密)。不过,对于我的情况来说,使用流是一个更好的解决方案,而且它似乎比完整的 HTTP 代理更轻量级。
使用该ngx_stream_ssl_preread
模块,您可以访问名为 的变量ssl_preread_server_name
。我正在寻找可以提供基本相同功能的东西,但从Host
HTTP 请求中的标头派生而来。有这样的事吗?
答案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;
}