EQVPS
ダッシュボード始める

VPSの開いているポートを確認する方法:何が待ち受けているか、インターネットから何が見えるか、そしてDockerの落とし穴

サーバーが待ち受けているポートをすべて確認し、その裏のプロセスを特定し、攻撃者と同じように外部からスキャンして、不要なものを閉じます。ファイアウォールを有効にしても開いたままになるDockerのポートも扱います。数値はすべて新しいUbuntu 24.04サーバーで計測しました。

開けたまま忘れられたポートは、サーバーへの侵入経路の定番です。公開アドレス上のデータベース、デバッグ用ダッシュボード、誰も意図していないポートを公開したコンテナ。2つのコマンドと1つの落とし穴を知っていれば、確認は1分で終わります。以下はすべて当社イメージから作った新しいUbuntu 24.04サーバーで実行し、2台目のマシンからスキャンしたものなので、出力はご自身のサーバーで見るものと同じです。

1. 何が待ち受けているか

コマンドはssです。オプションの文字は、TCP、UDP、待ち受け中のソケット、数値のポート番号、プロセス名を意味します。

sudo ss -tulpn

新しいサーバーでは6行が表示されます。

Netid State  Local Address:Port  Process
udp   UNCONN    127.0.0.54:53    systemd-resolve
udp   UNCONN 127.0.0.53%lo:53    systemd-resolve
tcp   LISTEN    127.0.0.54:53    systemd-resolve
tcp   LISTEN 127.0.0.53%lo:53    systemd-resolve
tcp   LISTEN       0.0.0.0:22    sshd
tcp   LISTEN          [::]:22    sshd

重要なのはLocal Address列です。誰が接続できるかを示します。

アドレス到達できる相手
127.0.0.1:…または127.0.0.53このサーバー上のプログラムだけ
0.0.0.0:…ファイアウォールが遮断しない限り、IPv4で誰でも
[::]:…IPv6で誰でも(通常はIPv4も)
10.0.0.5:…(特定のアドレス)そのアドレスに到達できる人なら誰でも

つまり新しいサーバーが外部に公開しているのは1つだけで、IPv4とIPv6のポート22のSSHです。127.0.0.53のDNSリゾルバーはプライベートです。

インターネットから見える可能性があるものだけを一覧にするには、ループバックアドレスを除外します。

sudo ss -tlnp | awk 'NR>1 && $4 !~ /^(127\.|\[::1\])/ {print $4, $6}'

インストールのたびに実行してください。nginx、Docker、小さなPythonアプリを入れたところ、一覧は2行から10行に増え、その中には0.0.0.0:8081のdocker-proxyや、ランダムなlocalhostポートのcontainerdのソケットがありました。

2. どのプロセスがポートを使っているか

ポートが使用中で、誰が使っているか知りたいとき:

sudo lsof -i :80 -P -n
sudo fuser 80/tcp
sudo ss -tlnp 'sport = :80'

最も読みやすいのはlsofです。nginxではrootのマスタープロセスとwww-dataのワーカーが表示され、どちらもIPv4とIPv6の*:80でした。fuserはPIDだけを表示するので、スクリプトで便利です。ポートを空ける必要があるときは、PIDをkillするのではなくサービスを止めてください。そうしないとsystemdが再起動します:sudo systemctl stop nginx。

3. インターネットから何が見えるか

ssはサーバー側の視点を示します。インターネット側の視点は異なることがあります。間にファイアウォールがある場合や、サービスが127.0.0.1にバインドされている場合です。実際の状態を見るには、サーバーを別のマシンからスキャンします。ノートPCでも2台目のサーバーでも構いません。ツールはnmapです(apt install nmap、WindowsとmacOSはnmap.orgのインストーラー)。

nmap your-server-ip
nmap -p- your-server-ip
nmap -p 22,80,443,3306 your-server-ip

1つ目の形式はよく使われる1,000ポート、2つ目は全65,535ポート、3つ目は指定したリストを調べます。ファイアウォールを入れる前の新しいサーバーでの結果です。

Not shown: 999 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
Nmap done: 1 IP address (1 host up) scanned in 6.83 seconds

全65,535ポートのフルスキャンは8.9秒で終わり、同じ1つのポートが見つかりました。閉じたポートはすぐにリセットで応答するため、スキャンが速いのです。

重要な状態は3つです。

  • open:プログラムが接続を受け入れました。これが攻撃対象領域です。
  • closed:そこでは何も待ち受けていないとサーバーが応答しました。安全ですが、サーバーが存在し、何を動かしていないかがわかります。
  • filtered:応答なし。ファイアウォールがパケットを破棄しました。スキャナーはその裏にサービスがあるかどうかわかりません。

何もインストールせずに1つのポートを調べるなら、nc -zv サーバーのIP 443がsucceededかrefusedを返し、フィルターされたポートではタイムアウトまで待ちます。UDPはnmapに-sUを付けますが、時間がかかります。当社のサーバーでは、よく使われるUDPポートの53、123、161はすべてclosedでした。

NATプランについての注意:インターネットから届くのは、当社がお客様に転送したSSHポートだけです。共有アドレスをスキャンすると見えるのは当社のネットワークの境界で、お客様のサーバーではありません。ほかのポートは、何が待ち受けていても到達できません。専用のIPv4アドレスがあるプランでは、開いているポートはすべてお客様自身で管理します。

4. 不要なものを閉じる

方法は3つあり、上から順に望ましいものです。

localhostにバインドする。 サーバー上のプログラムだけが使うサービスなら、127.0.0.1で待ち受けさせます。--bind 127.0.0.1 8080で小さなWebアプリを起動したところ、ssには127.0.0.1:8080と表示され、外部からのnmapは8080/tcp closedと報告しました。データベースではこれが正しい初期設定です。UbuntuのRedis、MySQL、PostgreSQLはインストール直後からlocalhostで待ち受けており、当社のRedisとMySQLのガイドもその設定のままにしています。

サービスを止める。 sudo systemctl disable --now 名前で待ち受けを完全に取り除けます。ssで消えたことを確認してください。

ファイアウォールを有効にする。 まずSSHを、次に提供するものを許可してから有効にします。

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw status verbose

外部からのスキャン結果はまったく変わりました。

Not shown: 997 filtered tcp ports (no-response)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8081/tcp open  blackice-icecap
Nmap done: 1 IP address (1 host up) scanned in 10.18 seconds

許可していないポートはすべてclosedからfilteredに変わり、全65,535ポートのフルスキャンは8.9秒から110秒になりました。スキャナーにとってはポートごとの時間が増え、サーバーについての情報は減ります。当社のUFWガイドでは、レート制限、特定IPだけの許可、IPv6を扱っています。

ただし、3行目を見てください。

5. Dockerの落とし穴

ファイアウォールが有効で、許可するルールもないのに、ポート8081は開いたままでした。このポートはdocker run -p 8081:80 nginx:alpineで起動したコンテナのものです。Dockerはカーネルのファイアウォールに独自のルールを追加し、それはufwのルールより先に評価されます。当社のサーバーに書き込まれたルールはこれです。

-A DOCKER ! -i docker0 -p tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80

ポート8081宛てのパケットは、ufwが判断する前にすべてコンテナへ転送されます。-pやports:の指定があるコンテナはすべて同じで、ssはそれを正直に0.0.0.0:8081のdocker-proxyとして表示します。

対策はlocalhostにだけ公開することです。

docker rm -f web
docker run -d --name web -p 127.0.0.1:8081:80 nginx:alpine

Composeではports:に"127.0.0.1:8081:80"と書きます。変更後、ssは127.0.0.1:8081を示し、Dockerのルールには-d 127.0.0.1/32が加わり、外部からのスキャンは8081/tcp filteredを報告しました。公開側にはnginxをリバースプロキシとしてHTTPS付きで前に置き、サーバーで開いているポートを22、80、443だけにしましょう。

変更のたびに確認すること

  1. sudo ss -tulpn:0.0.0.0や[::]の行は、どれも公開するつもりのものですか?
  2. ノートPCからnmap サーバーのIP:開いているポートは一致していますか?
  3. 公開アドレス上のデータベース、キャッシュ、管理画面:今日中に127.0.0.1かファイアウォールの内側へ移しましょう。
  4. Dockerのports:の各指定:公開Webポート以外は127.0.0.1:で始まっていますか?
  5. SSHが全員に開いている:fail2banか鍵だけのログインを追加しましょう。

Windows Serverでの同じ確認はGet-NetTCPConnection -State Listenで、ルールはWindowsファイアウォールにあります。$3のNano以上のすべてのLinuxプランには専用IPv4アドレス付きの版があり、そこではssに見えるものが、ファイアウォールで止めたもの以外、そのまま世界から見えます。

よくある質問

新しいVPSではどのポートが開いていますか?

当社のUbuntu 24.04イメージでは1つだけです。ポート22のSSHです。ほかに待ち受けているのは127.0.0.53と127.0.0.54のローカルDNSリゾルバーだけで、インターネットからは届きません。別のマシンからTCPの全65,535ポートをスキャンしたところ、開いていたのはちょうど1つでした。

closedとfilteredの違いは何ですか?

closedは、そこでは何も待ち受けていないとサーバーが応答した状態で、スキャナーはすぐにわかります。filteredはまったく応答がない状態で、パケットを破棄するファイアウォールがこう振る舞います。どちらも侵入者を防ぎますが、filteredはどのサービスがあるかも隠し、当社のサーバーではフルスキャンが約12倍遅くなりました。

ssには表示されるのに、nmapではclosedになるのはなぜですか?

そのサービスはループバックアドレスの127.0.0.1でしか待ち受けていません。サーバー上のプログラムは接続できますが、外部からは誰も接続できません。ssではLocal Address列を見てください。127.0.0.1:8080はプライベート、0.0.0.0:8080や[::]:8080はファイアウォールが許可すればインターネットから到達できます。

ufwを有効にしたのに、Dockerのポートが開いたままなのはなぜですか?

Dockerは独自のファイアウォールルールを書き込み、それがufwのルールより先に評価されます。-p 8081:80で起動したコンテナは、ufwが有効で8081のルールがない状態でも、当社のサーバーでは外部から到達できました。代わりにlocalhostに公開し(-p 127.0.0.1:8081:80)、公開側にはnginxやCaddyを前に置いてください。

自分のパソコンからポートを確認するには?

1つのポートならnc -zv サーバーのIP 443、よく使われる1000ポートならnmapをインストールしてnmap サーバーのIPを実行します。NATプランでは、インターネットから応答するのは転送されたSSHポートだけで、それ以外は設計上到達できません。

コメント

まだコメントはありません。最初になりましょう。

コメントを残す

コメントは表示される前にモデレートされます。