SSD Nodes Learn
ガイド Matt Connor著者 Matt Connor ・更新日 2026-07-24

UFWでIPv6のポートが開放されている原因と対策

UFWでIPv4のみを制限しても、IPv6経由でサービスが公開されたままになるリスクがあります。VPSのパブリックIPv6アドレスと、デフォルトで両方のスタックで待機するサービスの危険性を解説します。設定ミスを防ぎ、セキュリティの隙を埋めるための具体的な確認手順を詳しく説明します。

IPv6 firewallの落とし穴(要約)

ファイアウォールがIPv4のみを保護している場合に発生する問題です。VPSにはほぼ確実にパブリックIPv6アドレスが付与されており、多くのサービスがデフォルトでそのアドレスで待機しています。ファイアウォールがIPv4のみを対象としている場合、IPv4側が保護されていても、IPv6経由ではインターネット全体からそれらのサービスへアクセス可能です。curlでポートを確認し、接続拒否(refused connection)が確認できれば、安全だと誤解してしまいます。しかし、攻撃者はIPv6経由で同じポートに接続し、侵入してきます。

このガイドでは、標準的なUbuntu 24.04 VPSにおいて、この脆弱性がどこから生じるのか、何が公開されているのかを確認する方法、そしてその対策について説明します。UFW自体に問題はありません。最新のUbuntuでは、UFWはすでにIPv6を処理しています。問題は、UFWの周囲にあるレイヤーや、意図せず待機状態にあるサービスによって発生します。

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も受け入れるためです。したがって、新規サーバーのデフォルト設定は「両方のスタックで、どこからでも応答する」となります。これを防げるのはファイアウォールだけです。そのため、片方のスタックしか見ていないファイアウォールは、重大な問題となります。

IPv6の脆弱性が生じる真の原因

主な原因は4つあります。特定のサーバーにおいて、これらは1つ、あるいは複数同時に発生している可能性があります。

1. IPv4のみをフィルタリングするクラウドファイアウォール。 多くのプロバイダーのファイアウォールやsecurity-group製品はIPv4を前提に設計されています。そのため、IPv6を無視するか、手動で個別のIPv6ルールを追加する必要があります。プロバイダーのダッシュボードにあるファイアウォールのみを使用しており、それがIPv6をカバーしていない場合、IPv4のport 22の設定に関わらず、[::] サービスは公開された状態になります。プロバイダーのファイアウォールに関するドキュメントを確認し、特にIPv6に関する記述を探してください。

2. ip6tablesを使用しない、手動設定のiptables。 iptables コマンドはIPv4のテーブルのみを操作します。IPv6には、独自のルールを持つip6tables という完全に別のコマンドが存在します。iptables -A INPUT ... の行で構成されたファイアウォールスクリプトを作成し、対応する ip6tables のルールを記述していない場合、IPv6のファイアウォールは空の状態になります。デフォルトのpolicyがACCEPTである空のINPUT chainは、すべての通信を許可します。

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

この出力結果が、陥りやすい罠の全容です。IPv4はフィルタリングされていますが、IPv6はすべての通信を受け入れています。

3. Dockerによるファイアウォールをバイパスしたポート公開。 docker run -p 8080:80 を実行すると、DockerはUFWよりも優先される形で独自のルールを挿入します。そのため、ufw status でそのポートが拒否されていても、公開されたポートにはアクセス可能です。最新のDockerでは、これはIPv6でも同様に発生します。仕組みと修正方法については、Why Docker bypasses UFW, and how to filter container ports properly を参照してください。これらの公開ポートの宣言方法については、the basics of Docker Compose on a VPS を参照してください。

4. IPv6が無効化されたUFW。 UFWはIPv6を処理できますが、設定が必要な場合に限られます。設定を確認してください。

grep IPV6 /etc/default/ufw

最新のUbuntuには IPV6=yes が搭載されているため、UFWは両方のスタックに各ルールを適用します。古いイメージや古いガイドに基づいた IPV6=no が表示されている場合、作成したすべてのUFWルールはIPv4のみに適用され、IPv6は管理されていない状態になります。

公開されている範囲を正確に把握する

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

sudo ss -tlnp | grep '::'

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

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

ページやバナーが返された場合、そのポートは IPv6 で開いています。ポートが閉じている場合は、Connection refused またはタイムアウトが返されます。詳細を確認するには、サーバー外部から nmap を使用して IPv6 アドレスをスキャンしてください。

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

nmap が IPv6 で open と報告するポートは、IPv4 のスキャン結果に関わらず、インターネット全体から到達可能です。IPv4 と IPv6 のスキャン結果を比較するのが、差異を見つける最短の方法です。-6 で open なのに IPv4 で closed なものは、ファイアウォールの設定漏れがあるサービスです。

隙間を埋める

UFWで両方のスタックをカバーし、デフォルトをdenyに設定します。 設定が反映されたことを確認し、デフォルトのinboundポリシーをdenyに設定して、必要な通信のみを許可してください。

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

IPV6=yesを切り替えた時点でUFWが既に有効な場合、sudo ufw reloadを実行するまで変更は適用されません。

ufw statusには、各ルールがプレーンな形式と(v6)サフィックス付きの形式で2回ずつリストされています。(v6)の行が表示されている場合、UFWはIPv6をフィルタリングしています。

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

iptablesを手動で管理している場合は、すべてのルールをip6tablesにも反映させてください。 または、nftablesへの移行を検討してください。nftablesのinetテーブルはIPv4とIPv6を1箇所でカバーするため、この種の間違いを防げます。自身でルールを記述する場合、単一のnftables inetフィルタテーブルを使用するのが最も確実な解決策です。

公開する必要のないサービスはloopbackにバインドしてください。 データベース、管理パネル、メトリクスエンドポイントなどがパブリックアドレスを必要とすることは稀です。127.0.0.1::1にバインドすることで、ルーティング可能なアドレスでのリスニングを最初から防げます。Postgresの場合はlisten_addresses = 'localhost'を設定してください。アプリサーバーの場合は127.0.0.1にバインドし、その前にリバースプロキシを配置してください。リスナー自体を閉じることは、ファイアウォールで制限するよりも効果的です。到達すべき対象自体が存在しなくなるためです。

Dockerの公開ポートの保護をUFWだけに頼らないでください。 コンテナのポートをすべてのインターフェースではなく、特定のドレス(例:-p 127.0.0.1:8080:80)に公開してください。これにより、ホストおよび意図的にプロキシ設定した通信からのみポートへのアクセスが可能になります。コンテナを公開する必要がある場合は、Traefikリバースプロキシの背後に配置し、各アプリではなくプロキシのみを公開してください。

プロバイダーのファイアウォールにIPv6ルールを追加してください。 もしくは、プロバイダーのファイアウォールはIPv6には対応していないと割り切り、ホスト上のUFWまたはnftablesにその役割を任せてください。

実際に閉じているか確認する

変更を適用した後、再度外部からテストを実行してください:

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

以前応答があったポートは、現在は拒否(refuse)またはタイムアウト(time out)するはずです。nmapの結果が filtered または closed と表示されることを確認してください。もしポートがまだ open の場合は、以下の4つの原因を順に確認してください:ルールが設定されていない :: にサービスがバインドされている、DockerのルールがUFWより先に適用されている、またはプロバイダーのファイアウォールがIPv6を認識していない。

機密性の高いサービスをパブリックインターネットから完全に切り離す方が、より安全です。SSHと管理パネルをWireGuard VPNの背後に配置することで、ポートをファイアウォールで保護し、トンネル経由のみで応答するように設定してください。これにより、IPv6の露出問題は対象外となります。公開状態のサービスに対するブルートフォーススキャン対策として、デフォルト拒否(default-deny)のファイアウォールに加えて SSHの前にFail2banを導入することを検討してください。

ポートの概念自体が初めてである場合は、まず ポートの概要とサービスのListen方法 を読んでください。

FAQ

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

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

VPSがIPv6で何を公開しているか確認する方法は?

sudo ss -tlnpを実行し、ローカルアドレスが[::]で始まるリスナーをすべて確認してください。これは、すべてのIPv6インターフェースで応答することを意味します。次に、別のマシンからcurl -6 -v http://[YOUR:IPV6::ADDR]:PORT/を使用してサーバーのパブリックIPv6アドレスに直接テストするか、nmap -6 YOUR:IPV6::ADDRでスキャンしてください。IPv6のスキャンではオープンだが、IPv4ではクローズされているポートが、セキュリティ上の隙となります。

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のファイアウォールルールやループバックへのバインドによって停止されない限り、IPv6経由でアクセス可能なままとなります。

サービスをIPv4のみ、またはlocalhostのみでリスナーにするには?

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