SSTPがポート443を奪った:Apacheが起動しない理由
失敗しているサイトが443を握っているわけではない。Windows SSTPはHTTPスタック経由でポートを予約する。リスナーを移すか、IISを前に置くか、サイトを移そう。
Apacheが443で応答しなくなるのは、誰かが受信VPN接続を有効にしたその午後であり、エラーログはその後ろにある予約ではなくbindを責める。Windows Routing and Remote Accessがその瞬間にSSTPを有効にし、SSTPリスナーはどのクライアントも認証される前にHTTPスタック経由でTCP 443を要求する。要求は証明書が欠けていても、名前が間違っていても、すべてのhandshakeが拒否されても保持されるため、誰もログインできないVPNでもポートを占有し続ける。まず待ち受けテーブルを読むこと。443を所有するPIDが、下のどの修正がそもそも自分に開かれているかを決める。
予約は壊れた証明書より長く生き残る
SSTPはPPPをTLSセッションの中に包む。だから443が必要であり、だから他のトンネルを落とすプロキシを通り抜ける。リスナーはVPNサービスが自分のために開くソケットではない。SstpSvcがカーネルのHTTPドライバに登録するため、サービスが動いている限りポートは予約されたままになる。その登録は証明書を一切参照しないので、期限切れの名前、一致しないsubject、証明書がまったく無い場合でも443は占有されたままで、障害はやがて二方向に分かれる。拒否されたhandshakeはRasSstp配下のSystemログに残り、Webサーバーはbindできなかったと報告することしかできない。
netstat -aon | findstr :443
tasklist /svc /fi "PID eq <pid>"
最初の行は所有するPIDを返し、二行目はその背後のサービスを名指しする。答えはたいていHTTPサービスかRRASスタックである。tasklistが共有ホストプロセスで応答したら、プロセス名から推測せずサービス列を読むこと。
RasSstpの警告を読む価値があるのはその後だ。失敗したhandshakeを述べるだけで、衝突を起こした予約については何も語らない。すべてのクライアントを拒否するVPN端点でもポートは所有したままである。ログは最初の問いではなく二番目の問いとして扱うこと。
IISはドライバを共有し、Apacheはソケットを開く
IISは自分のbindingsをSSTPと同じカーネルドライバに渡し、そのドライバが受信接続をホスト名とURLプレフィックスで振り分けるので、どちらも失敗させずに両サービスへ443を望ませることができる。Apacheとnginxは逆を行う。自分のソケットでbindを呼び、アドレスがすでに握られているためOSが拒否する。どちらの設定も間違ってはいない。違いはどのAPIがポートを所有するかにある。だからVPNを有効にすると一方のホストのサイトが落ち、もう一方のホストの同一サイトは無傷のままになる。
この非対称性が修復の形を決める。IISがすでに玄関なら、衝突はプレフィックス、bindings、証明書の問題になる。Apacheが玄関なら、Apacheが443から退くかSSTPリスナーが退くかのどちらかで、次の節は両方の移動を扱う。
443をどちらのサービスに残すかを選ぶ
リスナーのポートは単一のレジストリ値にあり、Sstpsvcのパラメータキー配下の十進DWORDである。値を変えてRouting and Remote Accessを再起動し、待ち受けテーブルで古いポートが空いたことを確認する。
reg query "HKLM\SYSTEM\CurrentControlSet\Services\Sstpsvc\Parameters" /v ListenerPort
reg add "HKLM\SYSTEM\CurrentControlSet\Services\Sstpsvc\Parameters" /v ListenerPort /t REG_DWORD /d 4443 /f
Restart-Service RemoteAccess
ホスト上の他の何も欲しがらないポートを選び、十進で入力する:reg addはDWORDを書かれたとおりに保存するので、十六進のつもりで入れた値は別のポートとして着地する。クエリが値が存在しないと言うなら、二行目がそれを作る。Restart-Service RemoteAccessは接続中の全員を切断するので、計画してから行う価値がある。その後で待ち受けチェックを再実行すること。443に古い項目が残っているなら、サービスが古い登録を保持したままということだ。
リスナーを移すことにはレジストリ編集が示さない代償がある。通常のSSTPクライアントは443に発信するので、すべてのクライアントプロファイルに新しいポートが必要になり、経路を検査する何かに対して流れはWebリクエストらしくなくなる。HTTPSに見える通信しか許さないネットワークでは、それでSSTPを選んだ理由の大半が消える。小規模で管理されたクライアント層なら吸収できるが、公開またはBYODの利用者基盤では無理だ。
もう一方の移動は443をIISに残し、Apacheをプロキシが転送するbackendとして扱う。SSTPの既定を保ち、ポートの所有者を一人に保つが、その代償は転送ヘッダー、アプリケーションがもはや直接見ないクライアントアドレス、そしてプロキシが実際に送るプロトコルを話さねばならないbackendである。
所有者、証明書、handshakeの順に証明する
完了したTCP接続が証明するのはリスナーが存在することだけで、他には何もない。だからトンネルがまだ失敗していてもポートスキャンは健全に見える。四つの段階はそれぞれ違う形で失敗し、自分が壊していない段階に向けた修復はたいてい衝突を深める。順番に当たり、答えが分かれるところで止めること。
netstat -aon | findstr :443
certutil -store My
Get-WinEvent -LogName System -ProviderName RasSstp -MaxEvents 20
最初の行は443を保持するPIDを名指しし、それは今設定したサービスであるはずだ。二行目はマシン証明書をsubjectとともに列挙するので、クライアントプロファイルのアドレスと合わない名前はすぐ目に付く。イベントクエリは最近のSSTPレコードを返し、PPP認証に決して届かないhandshakeは、認証されてから拒否されるものとは別のレコードを残す。最後の段階はクライアントでしか見えず、そこでは項目が接続済みを報告するかPPPエラーを名指しする。
一台のホストが両方の仕事を担わねばならないなら、443の所有者を意識的に選び、その選択を書き残すこと。そうしなければ次に機能を有効にする人がこの衝突を再発見するが、役割を分ける方が常に清廉な答えだ。公開Webサーバーとリモートアクセス端点は、パッチ、証明書、再起動のサイクルを別々に持ち、それらのサイクルはそれぞれの予定で出会う。いずれにせよ、待ち受けチェック、レジストリのパス、選んだポートを引き継ぎメモに残すこと。