目標是希望把 Port 443 同時用在 HTTPS 網站以及 HTTPS Proxy (i.e. 瀏覽器到 Proxy 中間有加密的協定),其中 HTTPS Proxy 是跑 Squid,本來是用 nginx 做這件事情 (透過 ngx_stream_ssl_preread_module 的 ssl_preread_server_name 判斷 hostname),但遇到了 nginx 無法動態決定是否要啟用 proxy_protocol 的問題:「How to set the proxy_protocol to 'on' in a conditional manner in nginx?」,不確定是 bug 還是 feature。
第二個問題是 Squid 對 proxy protocol 的支援是透過 require-proxy-header 指定在 http_port 上的,而 https_port 不支援這個參數,也就是說在配合這個情境下,如果還想讓 Squid 可以記錄正確的 IP address 資訊,就需要讓 nginx 解 TLS,用 HTTP Proxy 協定丟進 Squid,這個也就是 TLS termination。
最後遇到的問題是 nginx 這邊的「網站端 vhost」會記錄到錯誤的 IP address,永遠是 127.0.0.1,這個問題卡了一年多沒解我就放掉了,剛好發現 Raspbery Pi 3B 上面跑的是 32-bit 版的 OS,就想說趁著重裝 64-bit 版的 OS 後改用 Caddy。
Caddy 這邊目前是透過 github.com/mholt/caddy-l4 這個模組實作「讀取 TLS SNI 資訊決定後續行為」,我是透過 xcaddy 編譯 Caddy 裝起來的,裝好後需要的設定是這樣:
layer4 {
:443 {
@proxytwhinet tls sni proxy-tw-hinet.gslin.com
route @proxytwhinet {
tls {
connection_policy {
alpn http/1.1
}
}
proxy {
proxy_protocol v1
upstream 127.0.0.1:3128
}
}
route {
proxy {
proxy_protocol v1
upstream 127.0.0.1:444
}
}
}
}
servers 127.0.0.1:444 {
listener_wrappers {
proxy_protocol {
allow 127.0.0.1/32
fallback_policy require
}
tls
}
}
看到 proxy-tw-hinet.gslin.com 後往 127.0.0.1:3128 丟,其他的都往 127.0.0.1:444 丟。
這邊有個比較特別的是針對 proxy-tw-hinet.gslin.com 要強制走 HTTP/1.1 協定,這是因為 Squid 這端的 http_port 不吃 HTTP/2,而 Firefox 連 HTTPS Proxy 時讀到 Caddy 透過 ALPN 說可以用 h2,於是就不會通,所以針對這個 domain 我只收 HTTP/1.1。
再來就是網站的部分,127.0.0.1:444 都要吃 proxy protocol 的 IP address 資訊。
而每個網站實際定義的部分則是這樣,裡面把不相關的設定都刪掉了:
proxy-tw-hinet.gslin.com:444 {
tls caddytls@gslin.com
[...]
}
rent-hinet.gslin.com:444 {
tls caddytls@gslin.com
[...]
}
接下來是 Squid 這邊,主要就是 require-proxy-header 以及允許哪些來源的 IP address 才是合法的:
http_port 3128 require-proxy-header proxy_protocol_access allow localhost
當初就只差記錄正確的 IP address,為了做到改下去這包工程好大,但總算是都搞定了...





