はじめに (対象読者・この記事でわかること)

  • 対象読者:VPSやオンプレでWebサービスを立ち上げたばかりの中級者
  • この記事でわかること:
  • ufw allow 80/tcpを打っても「繋がらない」理由の切り分け手順
  • ファイアウォール、NIC、systemd、Docker、SELinux/AppArmorなどが絡むときの見極め方
  • 最小構成で80番を開放するためのチェックリスト

前提知識

  • Ubuntu 20.04/22.04の基本コマンドが使える
  • ss/netstatでListen状態が読める
  • sudo権限がある

なぜ「ufw allow 80」だけでは足りないのか

ufwはiptables/nftablesのフロントエンドに過ぎません。
「ローカルではListenしているのに外部からタイムアウト」は次の3層のどこかで遮られていることが9割です。

  1. クラウド側ファイアウォール(Security Group / VPC フィルタ)
  2. OS内部のnftables/iptables(ufw以外のルール)
  3. アプリケーション自身(127.0.0.1のみListen、IPv6 only、SELinux denyなど)

この記事では2,3を中心に絞って解説します。

80/tcpが開かない原因を切り分ける実践ガイド

ステップ1: ローカルですでにListenしているか確認

Bash
sudo ss -tlnp | grep :80

出力例

LISTEN 0  4096  0.0.0.0:80  0.0.0.0:*  users:(("nginx",pid=1234,fd=6))
  • 0.0.0.0:80でなければ外部から到達不可(127.0.0.1:80や[::1]:80の場合もある)
  • プロセスが見えなければNginx/Apacheが起動していないかエラー

ステップ2: ufwのルールを一覧・カウンタで確認

Bash
sudo ufw status numbered sudo iptables -L -n -v | grep 80
  • ALLOWでもDROPルールが手前に来ているとマッチしない
  • ufw status verboseでロギングを有効にしてsudo dmesg | grep UFWでドロップログを追う

ステップ3: 他のファイアウォールルールを潰す

Ubuntu 22.04以降、nftablesがデフォルトなのでレガシーなiptables -Lだけでは見落とす。

Bash
sudo nft list ruleset | less # nftablesの全ルール sudo iptables-legacy -L -n # レガシー版

VPSイメージによってはiptables-persistentfirewalldが有効のままなので無効化:

Bash
sudo systemctl disable --now firewalld sudo systemctl disable --now iptables-persistent

ステップ4: クラウド側セキュリティグループを疑う

AWS/GCP/さくらのクラウドなど、コンソールで「80番を許可」していないケースが圧倒的多い。

Bash
curl -s ifconfig.io # グローバルIPを確認

そのIP宛にローカル端末からtelnet <IP> 80を打ち、タイムアウトすればセキュリティグループが99%。

ステップ5: SELinux/AppArmorで拒否されていないか

Bash
sudo aa-status | grep nginx # AppArmor sudo getenforce # SELinux sudo grep denied /var/log/audit/audit.log | grep nginx

強制モードでhttpd_t/nginx_tport 80を許可していない場合、次で許可:

Bash
sudo setsebool -P httpd_can_network_connect 1 sudo semanage port -a -t http_port_t -p tcp 80 # SELinux

AppArmorの場合は/etc/apparmor.d/local/nginxnetwork inet streamを追加してaa-complainaa-enforce

ステップ6: Docker/Podmanを使っている場合の注意

Dockerはiptables=falseにしていない限り、自動でDOCKER-USERチェインにDROPを入れる。

Bash
sudo iptables -L DOCKER-USER -n

ホスト側80番を使いたい場合は、コンテナ側で

Bash
docker run -p 127.0.0.1:80:80 nginx # 127.0.0.1のみ

とバインドしていると、もちろん外部から到達しない。
0.0.0.0:80:80に修正するか、docker-compose.ymlports:を見直す。

ステップ7: マルチホーム・NICの優先順位

VPSにプライベートIP + パブリックIPが割り当てられている場合、Nginxがプライベート側だけListenしていることがある。

Bash
ip addr show sudo ss -tlnp | grep 80

該当IPをlisten 10.x.x.x:80;からlisten 80;に変更してnginx -s reload

ハマった点とエラー解決

現象

ufw allow 80/tcpssではListen確認済み → ローカルcurl http://localhostは200 → 外部curl http://(グローバル)がタイムアウト

原因

Ubuntu 22.04のVPSイメージにデフォルトでnftablesルールが残っており、ufwとは別のテーブルでdrop allしていた。

解決策

nftablesを一旦リセット:

Bash
sudo nft flush ruleset sudo systemctl disable --now nftables sudo reboot

その後ufwのみ有効化して無事80番到達を確認。

まとめ

  • ufw allow 80だけではクラウドセキュリティグループ・nftables・SELinux・Dockerなど複数のレイヤで遮られる
  • ローカルListen → クラウドSG → OSファイアウォール → LSM(SELinux/AppArmor)→ コンテナの順で切り分けると最短で特定できる
  • チェックリストを紙に書いて消し込みしながら潜り抜けると、トラブルが短時間で解決する

次回は「443/tcp(HTTPS)でLet's Encryptを自動化する時の落とし穴」をまとめます。

参考資料