#はじめに
グローバルIPが1つしかないプライベートなサーバーで443ポートはかなり貴重です。
元々SSH,OpenVPN,HTTPSといった各サービスの標準ポートを使えば何ら問題はないんですが、80,443しか通さない強固なプロキシや◯盾等のけしからんネットワークを掻い潜るためには、どうしても443ポートを使用してHTTPSに偽装する必要が生じます。
それらのサービスを同時にホストするためのプロキシとしてSSLH
がありますが、プロトコル判定ルールがソースレベルで定義されているため、定義にないプロトコルを追加することは少々難しいです。
拙記事 SSLHでSoftether(SSTP)とApacheを共存
https://qiita.com/Shachihoko/items/64a519b930b26ee4e81e
そこで今回はDPIっぽい挙動ができる汎用プロキシである Haproxy を用いていくつかのプロトコルの通信を同時にホストする方法を模索します。
参考サイト
WebサーバーとSSHサーバーが同一ポートで待ち受けるSSLHの動作原理とは
https://blog.millenary.net/archives/48
2020/04/24 フロントエンドサーバーの設定に1行追記しました。
#環境
今回対象とするプロトコル
・HTTPS
・SSTP
・OpenVPN
・Softether VPN
・SSH
現状443ポートをリッスンしているサービスがあれば変更しておきます
ここでは各サービスのアドレスを以下として進めます.
・Nginx(HTTPS) 127.0.0.1:10443
・sshd 127.0.0.1:22
・Softether(SSTP,OpenVPN,SoftetherVPN) 127.0.0.1:1194
※SSTPとSoftetherVPNプロトコルの判別にSNIホスト名を使用するので、加えてvpn.foo.bar
というホスト名で正引きできるようにしておきます
#HAproxyの設定
インストール
$sudo apt-get install haproxy
設定
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base
crt-base
# Default ciphers to use on SSL-enabled listening sockets.
# For more information, see ciphers(1SSL). This list is from:
# https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
# An alternative list with additional directives can be obtained from
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-bind-options no-sslv3
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
[global]で書き換えが必要なのは以下だけです
# Default SSL material locations
ca-base #SSL証明書の公開鍵
crt-base #SSL証明書の秘密鍵
末尾にプロキシの設定を追記していきます。
###フロントエンドサーバーの設定
frontend https
mode tcp #TCPプロキシとして稼働させるのでtcpを指定
bind *:443 #443で待受
tcp-request inspect-delay 5s #遅延を5秒まで許可(SSLでは必要)
###SSHプロトコルの判別
SSHプロトコルの最初のペイロードパケットはSSH-2.0
の7文字で始まるため、これを16進数表記にした5353482d322e30
で始まるという条件を acl 属性で設定し、これにマッチする接続をbk-ssh-server
に転送
acl ssh-server req.payload(0,7) -m bin 5353482d322e30
tcp-request content accept if ssh-server
use_backend bk-ssh-server if ssh-server
###SSTP,SoftetherVPNプロトコルの判別
SSTPプロトコルのパケットには通常のHTTPS通信と同様にSNIのためのホスト名情報が含まれているので、これをHAproxyで読み取り、事前にVPN用として設定したホスト名vpn.foo.bar
と一致すればbk-sstp-server
に転送
use_backend bk-sstp-server if { req_ssl_sni -i vpn.foo.bar }
###HTTPSプロトコルの判別
SSTPプロトコルを分岐した後に、SSLのClient Helloを通知してきた残りのパケットをbk-nginx-server
に転送
use_backend bk-nginx-server if { req.ssl_hello_type 1 }
###OpenVPNの判別
(WireSharkでキャプチャしても尻尾が掴めなかったので)
以上の条件にマッチしなかったパケットは全てbk-openvpn-server
に転送
default_backend bk-ovpn-server
以下がフロントエンドの設定です
frontend https
mode tcp
bind *:443
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 } #これを明示的に記述しないと判定が安定しなかった
acl ssh-server req.payload(0,7) -m bin 5353482d322e30
tcp-request content accept if ssh-server
use_backend bk-ssh-server if ssh-server
use_backend bk-sstp-server if { req_ssl_sni -i vpn.foo.bar }
use_backend bk-nginx-server if { req.ssl_hello_type 1 }
default_backend bk-ovpn-server
###バックエンドサーバの設定
フロントエンドの設定に続ける形で書き込む
各自の環境に合わせて設定してください、ここでは前述の仮定環境を想定しています
backend bk-sstp-server
mode tcp
server default 127.0.0.1:1194
backend bk-ssh-server
mode tcp
server default 127.0.0.1:22
backend bk-nginx-server
mode tcp
server default 127.0.0.1:10443
backend bk-ovpn-server
mode tcp
server default 127.0.0.1:1194
###HAproxyの起動
$ sudo systemctl start haproxy
$ sudo systemctl enable haproxy
#最後に
※このプロキシを設定した状態で、SoftetherVPNクライアントから接続する場合はクライアントの設定でUDP高速化機能を無効にするにチェックを入れてください。
SSLHを使用するのが設定としては簡単ですが、この方法だと判別ルールさえ設定できれば独自プロトコルを含めた柔軟なTCPプロキシサーバーが構築できるので応用は効くのではないかと思います。