ソケットとポートの診断
目標
リッスンソケットを読み、バインドアドレスがリモート接続に与える影響を自分で再現し、Connection refusedと正常な接続の違いを確認します。
なぜ重要なのか
127.0.0.1:8080でリッスンするプロセスは、同じホストからは完全に動作しますが、外から来る接続はカーネルがRSTで拒否します。つまりrefusedが出ます。そのため、「ローカルでは動くのにリモートだけ動かない」という報告を受けたら、ss -ltnpのLocal Addressの列の1マスを見るだけで、半分は終わります。逆に、そのアドレスが0.0.0.0なのに外からtimeoutなら、そこからファイアウォールとネットワークポリシーを見ます。この切り分けが、調査範囲を半分に減らします。
ステップ
/root/sockディレクトリを作成し、現在リッスン中のTCPソケットの一覧を/root/sock/listen.txtに保存してください。:2222が含まれている必要があります。0.0.0.0:8080でリッスンするHTTPサーバーをバックグラウンドで起動してください。採点の時点でも生きている必要があります。127.0.0.1:8081でだけリッスンする2つ目のサーバーを起動し、2つのソケットのLocal Address:Portを1行ずつ/root/sock/bind.txtに書いてください。(例:0.0.0.0:8080と127.0.0.1:8081)127.0.0.1:8080に接続できるか確認した結果を/root/sock/nc-open.txtに保存してください。- 誰もリッスンしていない
127.0.0.1:9999への接続を試した結果を/root/sock/nc-refused.txtに保存してください。出力にrefusedを意味する文言が含まれている必要があります。 /proc/net/tcpから8080ポートのLISTENソケットの行を探し、そのまま/root/sock/proctcp.txtに保存してください。- 自分のインターフェースのIP(ループバックではないもの)で8080と8081をそれぞれ叩き、結果を
/root/sock/scope-test.txtに2行で書いてください。8080=<open|closed>/8081=<open|closed> /root/sock/report.csvを作成してください。1行目はport,bind,protoのヘッダーで、その下に2222、8080、8081の3つのポートについて、それぞれ1行ずつ書いてください。bindは実際のリッスンアドレス、protoはtcpです。
参考
ss -ltnp、nc -zv -w 2 <ip> <port>、ip -4 -br addr show scope globalの組み合わせで十分です。- サーバーは、
nohup python3 -m http.server 8080 --bind 0.0.0.0 >/dev/null 2>&1 &の形で起動します。 /proc/net/tcpのポートは16進数です。printf '%04X\n' 8080で変換値を確認してください。- よくある間違い1: ステップ7でループバックアドレスで試すと、2つのポートとも開いているように見え、違いが現れません。
- よくある間違い2: バックグラウンドのサーバーが、シェルの終了とともに死ぬ場合です。
nohupやsetsidで切り離してください。
リッスンソケットの確認
/root/sockディレクトリを作成し、現在リッスン中のTCPソケットの一覧を/root/sock/listen.txtに保存してください。:2222が含まれている必要があります。
TCPのリッスンソケットだけを、名前解決なしで、プロセスまで見るオプションの組み合わせです。sshdが2222で動いています。
テストサーバーの起動
0.0.0.0:8080でリッスンするHTTPサーバーをバックグラウンドで起動してください。採点の時点でも生きている必要があります。
python3の組み込みHTTPサーバーをバックグラウンドで起動してください。採点の時点でも生きている必要があるため、nohupやsetsidを併用するほうが安全です。
バインド範囲が異なる2つのサーバー
127.0.0.1:8081でだけリッスンする2つ目のサーバーを起動し、2つのソケットのLocal Address:Portを1行ずつ/root/sock/bind.txtに書いてください。(例: 0.0.0.0:8080と127.0.0.1:8081)
python3 -m http.serverには、バインドアドレスを指定するオプションがあります。2つのサーバーのLocal Addressの列を比べてみてください。
開いているポートの確認
127.0.0.1:8080に接続できるか確認した結果を/root/sock/nc-open.txtに保存してください。
ncの-zは、データを送らず接続だけを試みます。-vを付けると結果の文が出ます。
閉じているポートの確認
誰もリッスンしていない127.0.0.1:9999への接続を試した結果を/root/sock/nc-refused.txtに保存してください。出力にrefusedを意味する文言が含まれている必要があります。
誰もリッスンしていないポートに接続すると、カーネルがRSTを返します。その結果の文言をそのまま保存してください。
/proc/net/tcpを直接読む
/proc/net/tcpから8080ポートのLISTENソケットの行を探し、そのまま/root/sock/proctcp.txtに保存してください。
ポートは16進数で書かれています。8080を16進数にすると何になるでしょうか。状態列の0AはLISTENです。
外側のアドレスから叩いてみる
自分のインターフェースのIP(ループバックではないもの)で8080と8081をそれぞれ叩き、結果を/root/sock/scope-test.txtに2行で書いてください。
8080=<open|closed> / 8081=<open|closed>
ループバックではなく自分のインターフェースのIPで接続してこそ、違いが現れます。2つのポートの結果が異なる必要があります。
リッスンソケットの表を作る
/root/sock/report.csvを作成してください。1行目はport,bind,protoのヘッダーで、その下に2222、8080、8081の3つのポートについて、それぞれ1行ずつ書いてください。bindは実際のリッスンアドレス、protoはtcpです。
CSVはカンマで区切ります。ヘッダー行を含め、先ほど起動した2つのポートがどちらも入っている必要があります。