实现了一个端口服务复用的透明代理,可以在同一个端口上运行多个协议。根据每次连接中客户端发起的首个请求检测协议,根据协议或各种条件选择代理的上游。

需要打一个补丁。由@fcicq这个讨论中贡献。这个补丁实现了BSD的socket recv()语义。目前官方也有这个feature的PR

示例配置:

示例中服务监听在80端口,并定义规则:

  • 如果客户端来自 10.0.0.1,代理到 internal-host.com:80
  • 如果请求协议是HTTP 而且客户端来自10.0.0.2,代理到 internal-host:8001
  • 如果请求协议是 SSH,代理到 github.com:22
  • 如果请求协议是 DNS,代理到 1.1.1.1:53
  • 如果请求协议是 SSL/TLS 而且现在的时间是 0 到 30分,代理到 twitter.com:443
  • 如果请求协议是 SSL/TLS 而且现在的时间是 31 到 59分,代理到  www.google.com:443
  • 以上均不满足,代理到 127.0.0.1:80

说明

  • 只能实现识别连接建立后客户端先发送请求的协议,不兼容服务端先发送响应的协议(比如FTP,SMTP等)
  • 如果实现了ngx.reqsock.peak(),则可以使用ngx_stream_proxy来转发流量,这样的话除了首个请求以外同一连接的后续请求将没有额外的性能损失;目前只能在Lua层转发。

This module consists of two parts: protocol identifiers and matchers.

Protocol

The protocol part analyzes the first request that is sent from client and try to match it using known protocol signatures.

Currently supported: dnshttpsshtlsxmpp. Based on the bytes of signature, each protocol may have different possibilities to be falsely identified.

Protocol Length of signature False rate
dns 9 1/4 5.29e-23
http 4 2.33e-10
ssh 4 2.33e-10
tls 6 3.55e-15
xmpp 6 in 8 1/4 ?

Add new protocol

Create a new protocol_name.lua file under resty/multiplexer/protocol in the format of:

required_bytes is the length of bytes we need to read before identifying the protocol.

Matcher

client-host

Match if $remote_addr equals to expected value.

protocol

Match if protocol equals to expected value.

time

Match if current time is in configured range in mul.matcher_config.time. If no range is defined, the matcher will always return false.

For example, to match year 2018January and March and hour 6 to 24 except for hour 12:

default

Always matches.

Add new matcher

Create a new matcher_name.lua file under resty/multiplexer/matchers in the format of:

Where protocol is the identified protocol in lowercase string, and expected is the expected value for this matcher defined in set_rules.