sniproxyとは
sniproxyは、TLS接続(httpsなど)をドメイン名によって他のサーバーに振り分けるプロキシサーバーである。基本的にはリバースプロキシとして使うことになる。なお、HTTPも扱える。
すなわち、sniproxyではこういうことができる(雑な図)
|クライアント| ─(HTTPS)─ |sniproxy| ┬─(HTTPS)─ |nginxなど|┬─(HTTP)─|サーバー|
│ └─(HTTP)─|サーバー|
│
├─(HTTPS)─ |他のサーバー|
└─(HTTP)─ |また他のサーバー|
sniproxyと同じ事ができるソフトにはHAProxyなどがある。
リバースプロキシの定番ことnginxでは、ドメイン名毎に異なるポートやIPアドレスにHTTPリクエストを振り分けられるが、TLS接続を「TLSのままドメイン名毎に」振り分けることはできない。(ドメイン毎の振り分けが不要なら --with-stream
をつけてビルドすれば可能。)
ダウンロード
https://github.com/dlundquist/sniproxy
↑からダウンロードできる。git cloneでもzipでもいい。
ビルドとインストール
公式ドキュメントの通りにやればできる…はずだったのだが、Fedora 31ではrpmの作成ができなかった。
ビルド(実行ファイル等の作成)まではできたので、その状態から必要なファイルを適切な場所に設置すれば使用自体はできる。
とりあえずこれでビルドする。
# 必要なパッケージをインストール
dnf install autoconf automake curl gettext-devel libev-devel pcre-devel perl pkgconfig rpm-build udns-devel
cd (sniproxyをダウンロードしたフォルダ)
./autogen.sh && ./configure && make dist
その後、必要なファイルをコピーしていく。
cd (sniproxyをダウンロードしたフォルダ)
cp src/sniproxy /usr/local/bin
cp debian/logrotate.conf /etc/logrotate.d/sniproxy
cp sniproxy.conf /etc
systemdで動かすための設定ファイルを作る。
/etc/systemd/system/sniproxy.service
というファイルを作る。
内容は↓のようにする。
[Unit]
Description=Transparent TLS proxy
Documentation=https://github.com/dlundquist/sniproxy
After=syslog.target
After=network-online.target
[Service]
Type=forking
ExecStart=/usr/local/bin/sniproxy
Restart=always
[Install]
WantedBy=multi-user.target
後は設定完了後に
systemctl enable sniproxy
systemctl start sniproxy
などとすればよい。
設定
/etc/sniproxy.conf
を編集する。
多くは公式ドキュメントや、このファイルに最初からコメントで書いてある例に倣えばできる。
Proxy Protocol
プロキシ先のサーバーに対して、アクセス元のアドレスやポートを伝えるプロトコルである。
もともとはHAProxyが採用した方式である。詳細は↓
https://www.haproxy.com/blog/haproxy/proxy-protocol/
HTTPの場合は X-Forwarded-For
などのヘッダを使えるが、TLSではそういうのが用意されていない。
そのため、TLS接続開始に先立って所定のフォーマットで接続元の情報を送るようになっている。
プロキシ先のサーバーも対応が必要である。(nginxの設定例は後述)
下の「設定例」にもあるが、sniproxyでは80,443ポート宛てではこれを設定しても黙って無視されるのでハマる。
ポート番号を変えれば正常に動作する。
設定例
listen [::]:80 {
proto http
table http_hosts
}
listen [::]:443 {
proto tls
table https_hosts
}
table http_hosts {
# 80ポートではproxy_protocolが無視されるので別のポートにする
twigaten.204504byse.info [::1]:8080 proxy_protocol
# UNIXソケットでは特に工夫しなくてもproxy_protocolが効く
204504byse.info unix:/var/run/nginx_http.sock proxy_protocol
homoo.social unix:/var/run/nginx_http.sock proxy_protocol
}
table https_hosts {
# 443ポートではproxy_protocolが無視されるので別のポートにする
twigaten.204504byse.info [::1]:8443 proxy_protocol
#UNIXソケットでhttpとhttpsを分けるには別のソケットにする必要がある
204504byse.info unix:/var/run/nginx_tls.sock proxy_protocol
homoo.social unix:/var/run/nginx_tls.sock proxy_protocol
}
nginxの設定
nginxの公式ドキュメントが充実している。
https://docs.nginx.com/nginx/admin-guide/load-balancer/using-proxy-protocol/
注意点は、sniproxyが80,443ポートではproxy_protocolを使ってくれないことくらいである。
http {
# ログなどの設定も必要(省略)
server {
# sniproxyは80,443ポートではproxy_protocolを使えないので別のポートにする
listen 8080 proxy_protocol;
listen 8443 ssl http2 proxy_protocol;
set_real_ip_from 1234:5678:90ab:cdef::/64;
# 実際にはプロキシ先が使うやつだけ入れればよい
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
# プロキシ先によって適切な設定は異なる
# ↓例えばmastodon(pumaに送る分)はこうする必要がある
# proxy_set_header X-Real-IP $remote_addr;
# proxy_set_header X-Forwarded-For $proxy_protocol_addr;
# HTTPヘッダの設定もする(省略)
-------------------
server {
listen unix:/var/run/nginx.sock proxy_protocol ssl http2;
# UNIXソケットの場合はこうする
set_real_ip_from unix:;
これで動くはず(`・ω・´)