それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

sslhでSNI認識してサーバを振り分ける。(apln 有無)

sslh にはSNI機能があった

nginx で捌いても良いんですが、sslhはnginxより設定がシンプルだ。

443ポートを直接リッスンするならsslhでリッスンして各種ポートに透過プロキシしたほうが再利用性が高くて良い。

443 ポートで、SNIで接続先を選定

特定のホスト名のときだけ別のnginxへつなぎに行く。

protocols:
(
  {
    name: "ssh";
    service: "ssh";
    host: "192.168.2.5";
    port: "22";
    fork: true;
  },
  {
    name: "tls";
    alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ];
    host: "192.168.100.2";
    port: "443";
    sni_hostnames: [
      "example.tld",
      "example.com",
    ];
    log_level: 0;
    tfo_ok: true
  },
  {
    name: "tls";
    alpn_protocols : [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ];
    host: "192.168.100.5";
    port: "443";
    log_level: 0;
    tfo_ok: true
  },

起動してみる。

sslh -f -F /root/sslh.conf --listen 127.0.0.1:443

テスト

curl -v --resolve example.tld:443:127.0.0.1 https://example.tld/ # 200 ok from 100.2
curl -v --resolve example.com:443:127.0.0.1 https://example.com/  # 200 ok from 100.2
curl -v --resolve example.net:443:127.0.0.1 https://example.net/  # from 100.5

APLN とSNI

ALPNをサポートするかしないかでSNIの振り分けが変わってくる

次のようなALPN付きの条件を書いたら、SNIより先にALPNで仕分けられる

  {
    name: "tls";
    alpn_protocols: [ "h2", "http/1.1", "spdy/1", "spdy/2", "spdy/3" ];
    host: "192.168.100.2";
    port: "443";
    sni_hostnames: ["nginx.lan"]
  },

ALPNを条件にした振り分けの接続チェック

openssl s_client --connect router.lan:443 -alpn h2 --servername nginx.lan < /dev/null 

openssl に -alpn を条件に入れて、接続をチェックする。このとき、SNIは--servernameで指定している。

ALPN未指定の設定

  {
    name: "tls";
    # alpn_protocols: なし
    host: "192.168.100.4";
    port: "443";
    sni_hostnames: ["nginx2.lan"]
  },

上記のように、ALPNを未指定で残す設定で書いたとき。

以下のように接続をチェックする

openssl s_client --connect router.lan:443 --servername nginx2.lan < /dev/null 

SSLHではALPNがサポートされていて、ALPNつけることで絞り込みができるし、Nginxなどが対応しているのでつけてもつけなくても、ほとんど影響がない。まれにALPN非対応なWebサーバーがあったりするとこの条件を意識しないといけない。

また、curlの場合 --no-alpn を使うことでALPNの有無でSSLHの反応が変わることがわかる。

curl -vs  --http2 https://t.co > /dev/null
curl -vs --no-alpn https://t.co
curl -vs --no-alpn --http1.1 https://t.co > /dev/null

docker サーバーを分離できる。

docker マシンが重くなってきたので、nginxと別マシンに移動させようと思ったけど、ルーターで直接443をリッスンして振り分けることができて、レイヤ7のルーターとして動作するの便利だ。

nginxで書いても良いんだけど。nginxを入れるほどでもないし、nginxでopenvpn/ipsec/sshマッピングするのが面倒だったので、sslhを使うことにした

sslhのコマンド引数にはない。

sslh の「設定ファイル」を書く必要があり、コマンド引数ではALPN/SNIを指定できないのが注意。

443 ポート制限に負けない。

TCP/443ポート以外の通信を「不正」とパケット破棄されるような「接続サービス」をインターネット接続と呼んで欲しくない。

UDP/443ポートまで制限するのは流石にないと思ってたけど、最近、とある旅館で遭遇したのでとても恐ろしい。HTTP/3すら否定された。

2023-05-24

SNIとALPNについて追記