Docker コンテナなどでミニマルなイメージを作っていると、 netcat
や nmap
はおろか、 vim
も dig
も curl
すらもないイメージを作ってしまうことがよくあります。通常の運用では困ることはほとんどありませんが、たまにネットワークの状況などを観察したい場合に docker exec
で実行中のコンテナに入って「このコンテナの中からあのホストのあのポートって叩けるのかな?」などと手軽に調べたいことがあります。
そういう場合に、 apt-get
, yum
, dnf
などを叩いてインストールしようとすると、リポジトリの取得から入る必要があって大変長く待たされたりします。場合によっては完全に閉じたネットワークで実行していて、外の世界のリポジトリを参照できないこともあります。
あくまで「さっとポートが空いてるか調べたい」だけなのです。こういうときに何か簡便な方法で (とにかく横着した方法で) ポートの開閉をチェックできないでしょうか?
そういうときに使えるハック
bash の net-redirection を利用すると、ポートの開閉くらいはかんたんにわかります。
詳しくは bash で TCP 通信 を御覧ください。
例として 8.8.8.8 の 53 番ポート (Google の Public DNS サービス) を叩いてみましょう。
open
$ exec 7<> /dev/tcp/8.8.8.8/53
特にエラーなく成功します。
filtered
存在していない 54 番ポートを叩いてみましょう。
$ exec 7<> /dev/tcp/8.8.8.8/54
(しばらく待たされる)
-bash: connect: Operation timed out
-bash: /dev/tcp/8.8.8.8/54: Operation timed out
closed
とくにわかりやすい例がなかったので、ローカルの 127.0.0.1 10000 に closed なポートが有る前提で書きます。
$ exec 7<> /dev/tcp/127.0.0.1/10000
-bash: connect: Connection refused
-bash: /dev/tcp/127.0.0.1/10000: Connection refused
簡易コマンドを作る
このような性質を利用して、簡易的なポート開閉チェックコマンドをつくってしまいましょう。
結果は終了ステータスで判別することにします。
$ check_tcp_port() (bash -c 'trap "exit 2" SIGTERM; (exec 7<> /dev/tcp/'"$1"'/'"$2"' & (sleep 1 && kill 0) & wait %1)' &> /dev/null)
使い方
$ check_tcp_port 8.8.8.8 53 ; echo $?
0
$ check_tcp_port 8.8.8.8 54 ; echo $?
2
$ check_tcp_port 127.0.0.1 10000 ; echo $?
1
open なときだけ 0 を返します。
filtered なときは 2, closed なときは 1 を返します。
(しかし厳密ではありませんが、ここまで横着しているわりにはちゃんととれます)
解説
sleep 1 && kill 0
kill 0 は現在のプロセスグループすべてに SIGTERM を送ります。
一定時間でのタイムアウトを実現します。
trap "exit 2" SIGTERM
タイムアウトで SIGTERM が発行された場合に終了ステータスを 2 にしたいので trap
します。
&> /dev/null
余計な出力は忘れてしまいましょう。 標準出力と標準エラー出力を消します。
まとめ
横着しつつも、これでちょっとは戦えます。
ネットワークの疎通はみたいけど、新たなパッケージの追加をしたいというほどでもない場合には使ってみてはいかがでしょうか。