はじめに (対象読者・この記事でわかること)
- 対象読者: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割です。
- クラウド側ファイアウォール(Security Group / VPC フィルタ)
- OS内部のnftables/iptables(ufw以外のルール)
- アプリケーション自身(127.0.0.1のみListen、IPv6 only、SELinux denyなど)
この記事では2,3を中心に絞って解説します。
80/tcpが開かない原因を切り分ける実践ガイド
ステップ1: ローカルですでにListenしているか確認
Bashsudo 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のルールを一覧・カウンタで確認
Bashsudo 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だけでは見落とす。
Bashsudo nft list ruleset | less # nftablesの全ルール sudo iptables-legacy -L -n # レガシー版
VPSイメージによってはiptables-persistentやfirewalldが有効のままなので無効化:
Bashsudo systemctl disable --now firewalld sudo systemctl disable --now iptables-persistent
ステップ4: クラウド側セキュリティグループを疑う
AWS/GCP/さくらのクラウドなど、コンソールで「80番を許可」していないケースが圧倒的多い。
Bashcurl -s ifconfig.io # グローバルIPを確認
そのIP宛にローカル端末からtelnet <IP> 80を打ち、タイムアウトすればセキュリティグループが99%。
ステップ5: SELinux/AppArmorで拒否されていないか
Bashsudo aa-status | grep nginx # AppArmor sudo getenforce # SELinux sudo grep denied /var/log/audit/audit.log | grep nginx
強制モードでhttpd_t/nginx_tがport 80を許可していない場合、次で許可:
Bashsudo setsebool -P httpd_can_network_connect 1 sudo semanage port -a -t http_port_t -p tcp 80 # SELinux
AppArmorの場合は/etc/apparmor.d/local/nginxにnetwork inet streamを追加してaa-complain→aa-enforce。
ステップ6: Docker/Podmanを使っている場合の注意
Dockerはiptables=falseにしていない限り、自動でDOCKER-USERチェインにDROPを入れる。
Bashsudo iptables -L DOCKER-USER -n
ホスト側80番を使いたい場合は、コンテナ側で
Bashdocker run -p 127.0.0.1:80:80 nginx # 127.0.0.1のみ
とバインドしていると、もちろん外部から到達しない。
0.0.0.0:80:80に修正するか、docker-compose.ymlのports:を見直す。
ステップ7: マルチホーム・NICの優先順位
VPSにプライベートIP + パブリックIPが割り当てられている場合、Nginxがプライベート側だけListenしていることがある。
Baship addr show sudo ss -tlnp | grep 80
該当IPをlisten 10.x.x.x:80;からlisten 80;に変更してnginx -s reload。
ハマった点とエラー解決
現象
ufw allow 80/tcp → ssではListen確認済み → ローカルcurl http://localhostは200 → 外部curl http://(グローバル)がタイムアウト
原因
Ubuntu 22.04のVPSイメージにデフォルトでnftablesルールが残っており、ufwとは別のテーブルでdrop allしていた。
解決策
nftablesを一旦リセット:
Bashsudo 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を自動化する時の落とし穴」をまとめます。
参考資料
