實現了一個端口服務復用的透明代理,可以在同一個端口上運行多個協議。根據每次連接中客戶端發起的首個請求檢測協議,根據協議或各種條件選擇代理的上游。

需要打一個補丁。由@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.