SSD Nodes Learn Hosting plans →
ガイド Matt Connor著者 Matt Connor ・更新日 2026-08-26

UFWのIPv6設定を確認してVPSの穴を塞ぐ方法

Ubuntu 24.04のVPSでは、IPv4を制限してもIPv6でサービスが公開されることがあります。UFWとクラウドファイアウォールの違い、ssでの確認方法、正しい封鎖手順を解説します。

1文で分かる IPv6 ファイアウォールの落とし穴

ファイアウォールで IPv4 を保護していても、VPS にはほぼ確実にパブリック IPv6 アドレスも割り当てられ、多くのサービスがデフォルトでそのアドレス上でも待ち受けます。ファイアウォールが IPv4 しか対象にしていない場合や、IPv4 だけをフィルタリングするクラウドファイアウォールに依存している場合、IPv4 側が適切にロックダウンされていても、これらのサービスは IPv6 経由でインターネット全体から到達可能です。curl でポートを確認して接続拒否を確認すると、安全だと考えてしまいます。しかし、攻撃者は同じポートへ IPv6 経由で接続し、そのまま侵入できます。

このガイドでは、通常の Ubuntu 24.04 VPS でこの差が生じる理由、公開しているポートを正確に確認する方法、そして公開状態を解消する方法を説明します。ここで UFW が問題の原因というわけではありません。最新の Ubuntu では、UFW はすでに IPv6 を処理します。公開状態を生むのは、その周囲にある各層と、待ち受けていることを把握していなかったサービスです。

そもそも VPS が IPv6 で接続可能になっている理由

現在のほぼすべての VPS には、IPv4 アドレスとともにパブリック IPv6 アドレスが割り当てられています。多くの場合、完全な /64 が提供されます。自分の環境を確認します。

ip -6 addr show scope global
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
    inet6 2001:db8:2a::1/64 scope global

この 2001:db8:2a::1 は、IPv4 アドレスと同じく、インターネット上のどこからでもルーティングできます。次に、待ち受けているサービスを確認します。

sudo ss -tlnp
State   Recv-Q  Local Address:Port   Process
LISTEN  0       0.0.0.0:22           sshd
LISTEN  0       [::]:22              sshd
LISTEN  0       127.0.0.1:5432       postgres
LISTEN  0       [::]:8080            docker-proxy

Local Address 列を注意して確認してください。0.0.0.0:22 は「すべての IPv4 アドレスで待ち受ける」という意味です。[::]:22 は「すべての IPv6 アドレスで待ち受ける」という意味です。127.0.0.1:5432 は loopback にバインドされており、外部公開されていないため、Postgres の行は安全です。2 つの [::] の行は IPv6 経由でインターネット全体からの接続に応答します。docker-proxy の行は、起動したことを忘れがちなサービスです。

ほとんどのデーモンは、初期状態では :: にバインドされます。Linux では通常、:: ソケットが IPv4 接続も受け付けるためです。つまり、新しいサーバーの初期状態は「すべての場所で、IPv4 と IPv6 の両方に応答する」状態です。これを防ぐ唯一の手段がファイアウォールです。そのため、片方のプロトコルスタックしか認識しないファイアウォールは、実際の問題になります。

IPv6 の空白が実際に生じる原因

一般的な原因は 4 つあります。1 台のサーバーで 1 つだけ該当する場合もあれば、複数が同時に該当する場合もあります。

1. IPv4 だけをフィルタリングするクラウドファイアウォール。 多くのプロバイダーのファイアウォールやセキュリティグループ製品は、IPv4 を前提に発展してきました。そのため、IPv6 を無視するか、手動で追加する個別の IPv6 ルールが必要です。プロバイダーのダッシュボードにあるファイアウォールだけを使用していて、それが IPv6 に対応していない場合、IPv4 のポート 22 について何と表示されていても、[::] サービスは外部に公開されたままです。プロバイダーのファイアウォールのドキュメントを読み、IPv6 という語を明示的に探してください。

2. ip6tables を設定していない手作業の iptables。 iptables コマンドが操作するのは IPv4 のテーブルだけです。IPv6 には完全に別のコマンド ip6tables があり、ルールも別に管理します。iptables -A INPUT ... の行を多数含むファイアウォールスクリプトを書いていても、対応する ip6tables のルールを書いていなければ、IPv6 のファイアウォールは空です。デフォルトポリシーが ACCEPT の空の INPUT チェーンでは、すべての通信が許可されます。

sudo ip6tables -L INPUT -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

この出力を見ると、問題の全体像が 1 画面で分かります。IPv4 はフィルタリングされていますが、IPv6 はすべての送信元を受け入れています。

3. Docker がファイアウォールを迂回してポートを直接公開している。 docker run -p 8080:80 を実行すると、Docker は UFW より前に独自のルールを挿入します。そのため、ufw status でそのポートを拒否していても、公開したポートには到達できます。最新の Docker では、IPv6 でも同じことが起こります。Docker が UFW を迂回する仕組みと、コンテナのポートを適切にフィルタリングする方法では、その仕組みと対策を説明しています。公開ポートの宣言方法については、VPS での Docker Compose の基本を参照してください。

4. IPv6 を無効にした UFW。 UFW は IPv6 を処理できますが、有効に設定されている場合に限ります。次のスイッチを確認してください。

grep IPV6 /etc/default/ufw

最新の Ubuntu では IPV6=yes が設定されているため、UFW は各ルールを IPv4 と IPv6 の両方に適用します。古いイメージや古いガイドの影響で IPV6=no になっている場合、作成した UFW ルールはすべて IPv4 専用です。その場合、IPv6 は管理されていません。

公開している内容を正確に確認する

推測してはいけません。外部から測定します。まずリスナーを一覧表示し、:: にバインドされているものをすべて確認します。

sudo ss -tlnp | grep '::'

次に、別のマシンからサーバーのパブリック IPv6 アドレスへ接続し、閉じているはずのポートを試します。

curl -6 -v http://[2001:db8:2a::1]:8080/

ページまたはバナーが返る場合、そのポートは IPv6 で開いています。閉じているポートでは Connection refused またはタイムアウトが発生します。この2つの失敗は同じ意味ではありません。refused と timed out の違いにより、ホストが応答して接続を拒否したのか、ファイアウォールがパケットを無言で破棄したのかを判断できます。全体像を把握するには、サーバーの外部から nmap で IPv6 アドレスをスキャンします。

nmap -6 2001:db8:2a::1

nmap が IPv6 で open と報告するポートは、IPv4 のスキャン結果に関係なく、インターネット全体から到達可能です。IPv4 と IPv6 のスキャン結果を横に並べて比較すると、差異を最も早く見つけられます。-6 で開いていて IPv4 では閉じているポートは、ファイアウォールで保護できていないサービスです。

Close the gap

Make UFW cover both stacks, and default to deny. Confirm the switch, then set a default-deny inbound policy and allow only what you need:

sudo sed -i 's/^IPV6=no/IPV6=yes/' /etc/default/ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose

If UFW was already active when you flipped IPV6=yes, the change does not take effect until you run sudo ufw reload.

ufw status lists each rule twice, once plain and once with a (v6) suffix. When you see the (v6) lines, UFW is filtering IPv6:

22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

If you manage iptables by hand, mirror every rule in ip6tables, or move to nftables, whose inet tables cover IPv4 and IPv6 in one place and remove this whole class of mistake. A single nftables inet filter table is the cleanest fix when you are writing rules yourself. If your VPS runs Rocky or AlmaLinux rather than Ubuntu, there is no UFW to configure and firewalld is the front end you manage instead, which applies its zone rules to both stacks at once.

Bind services you do not want public to loopback. A database, an admin panel, or a metrics endpoint rarely needs a public address at all. Bind it to 127.0.0.1 and ::1 so it never listens on a routable address in the first place. For Postgres, set listen_addresses = 'localhost'. For an app server, bind it to 127.0.0.1 and put a reverse proxy in front. Closing the listener beats firewalling it, because then there is nothing to reach.

Do not trust UFW to guard Docker's published ports. Publish container ports to a specific address rather than every interface, for example -p 127.0.0.1:8080:80, so the port is reachable only from the host and whatever you deliberately proxy to it. When a container genuinely must be public, put it behind a Traefik reverse proxy and publish only the proxy, not each app.

Add IPv6 rules to your provider firewall, or accept that it is not your firewall for IPv6 and let UFW or nftables on the host do that job instead.

実際に閉じていることを確認する

変更後、同じ外部テストをもう一度実行します。

curl -6 -v http://[2001:db8:2a::1]:8080/
nmap -6 2001:db8:2a::1

以前応答したポートが、現在は接続を拒否するかタイムアウトする状態になっていることを確認します。nmap では filtered または closed と表示されるはずです。ポートがまだ開いている場合は、前述の4つの確認元を順に見直します。:: に引き続きバインドしていて、その前段にルールがないサービス、UFW より前に適用される Docker のルール、または IPv6 をまったく確認していなかったプロバイダー側のファイアウォールが考えられます。

機密性の高いサービスをパブリックインターネットから完全に切り離すと、さらに安全です。SSH と管理パネルを WireGuard VPN の背後に置く ことで、ファイアウォールのポートをトンネル経由の通信だけに応答するよう設定できます。これにより、IPv6 経由の公開を検討する必要もなくなります。公開したままにするサービスへ届くブルートフォーススキャンを抑えるには、デフォルト拒否のファイアウォールに加えて、SSH の前段に Fail2ban を配置する 方法もあります。

ポートの仕組みが初めての場合は、まず ポートとは何か、サービスがどのように待ち受けるか を読んでください。

FAQ

UFW はデフォルトで IPv6 をブロックしますか?

最新の Ubuntu 24.04 をインストールした場合は、ブロックします。UFW は /etc/default/ufw から IPV6=yes を読み込み、各ルールを IPv4 と IPv6 の両方に適用します。ufw status では、IPv6 のルールに (v6) のサフィックスが付いて表示されます。問題になるのは、IPV6=no の場合です。これは古いイメージや古いチュートリアルが原因であることがあります。また、IPv4 だけをフィルタリングするプロバイダーのファイアウォールに依存している場合や、Docker が UFW を経由せずにポートを公開している場合にも発生します。grep IPV6 /etc/default/ufw でこの設定を確認してください。

VPS が IPv6 で公開しているポートを確認するにはどうすればよいですか?

sudo ss -tlnp を実行し、ローカルアドレスが [::] で始まるリスナーをすべて確認してください。これは、すべての IPv6 インターフェースで接続を受け付けることを示します。次に、別のマシンからサーバーのパブリック IPv6 アドレスを直接 curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/ でテストするか、nmap -6 YOUR:IPV6::ADDR でスキャンします。IPv4 では閉じているのに IPv6 のスキャンで開いているポートが、設定上の抜けです。

UFW ではブロックされているのに、Docker コンテナのポートへ接続できるのはなぜですか?

-p でポートを公開すると、Docker は UFW より前に独自のファイアウォールルールを挿入します。そのため、ufw status では拒否として表示されていても、公開ポートには接続できます。これは IPv4 で発生し、Docker の IPv6 サポートが有効な場合は IPv6 でも発生します。-p 127.0.0.1:8080:80 のように特定のアドレスへバインドして公開するか、コンテナをリバースプロキシの背後に置き、プロキシだけを公開してください。

IPv4 のファイアウォールが適切に設定されていても、IPv6 のファイアウォールは必要ですか?

はい。IPv4 と IPv6 は別のネットワークスタックであり、ファイアウォールルールも別です。IPv4 のルールが完全でも、IPv6 のトラフィックには影響しません。VPS にパブリック IPv6 アドレスがある場合、ほとんどの VPS にはありますが、:: で待ち受けているサービスは、IPv6 のファイアウォールルールを設定するか、loopback にバインドしない限り、IPv6 経由で接続できます。

サービスを IPv4 のみ、または localhost のみで待ち受けさせるにはどうすればよいですか?

サービス自身の設定でバインドアドレスを指定します。IPv4 の loopback のみの場合は 127.0.0.1 にバインドします。IPv6 のリスナーを作成せず、すべての IPv4 アドレスで待ち受ける場合は 0.0.0.0 にバインドします。Postgres では listen_addresses、SSH では ListenAddress を使用します。ほとんどのアプリケーションサーバーには、host または bind のフラグがあります。sudo ss -tlnp で結果を確認し、Local Address[::] が表示されなくなったことを確認してください。