Nginx
從 nginx 流代理中提取 HTTP 主機頭
我正在尋找使用 nginx 的流模組來代理 HTTP 流量。這適用於 HTTPS,因為該
ngx_stream_ssl_preread
模組存在。這允許我從 TLS 握手中提取請求的伺服器名稱,然後我可以使用它來確定我應該將流代理到哪個伺服器。但是,我似乎找不到普通 HTTP 的等價物。我想這是因為大多數人只是使用普通的 HTTP 代理,因為代理伺服器可以看到 HTTP 請求中的 Host 標頭(因為它是未加密的)。不過,對於我的場景來說,使用流將是一個更好的解決方案,而且它似乎比完整的 HTTP 代理更輕量級。
使用該
ngx_stream_ssl_preread
模組,您可以訪問名為ssl_preread_server_name
. 我正在尋找能夠提供基本相同但從Host
HTTP 請求中的標頭派生的東西。這樣的事情存在嗎?
我找不到任何內置方法,所以我自己實現了一個。
使用ngx_stream_js_module模組和一些自定義 JavaScript (njs),我可以將 HTTP Host 標頭中的伺服器名稱讀取到 $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 的代理協議讀取真實客戶端數據的一種方式:
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; }