手順

ufwの設定方法:Linuxのサーバーファイアウォールとnftablesの基礎

更新日: · Skillaria.Top

ufwの設定は3つの手順で済みます。デフォルトのポリシーを設定し(sudo ufw default deny incoming と sudo ufw default allow outgoing)、SSHと必要なポートを許可し(sudo ufw allow 22/tcp、sudo ufw allow 443/tcp)、最後に sudo ufw enable でファイアウォールを有効にします。ルールは sudo ufw status numbered で確認します。内部ではufwがiptablesまたはnftablesでnetfilterを制御しています。

Linuxのファイアウォールは、サーバーに届くネットワークパケットのうち、どれを通し、どれを破棄するかを決めます。実際の処理はカーネルのnetfilterというサブシステムが行い、それを操作するのが低レベルの iptables や nftables、そしてその上位ツールの ufw(Uncomplicated Firewall)です。UbuntuやDebianのほとんどのサーバーでは、ufwの設定は5分程度で終わります。受信をすべて閉じ、SSHとサービスのポートを開けて、有効にするだけです。以下では、その手順、よくある間違い、そして内部の仕組みを理解するための最低限のnftablesを紹介します。

Linuxのファイアウォールの仕組み

パケットのフィルタリングはカーネル内で行われます。これがnetfilterです。netfilterは、ホスト自身へ向かう受信トラフィック、送信トラフィック、転送トラフィック(forward)のための処理ポイント(フック)を提供します。ユーザー空間のツールは、それらのフックに対するルールをカーネルに書き込むだけです。

ツールレベル使う場面
ufw上位ツール。シンプルな構文UbuntuやDebianの単体サーバー、基本的なルール
firewalldゾーンを持つ上位ツールRHEL、Rocky、Fedora
nftables(nft)低レベル。最新複雑なルール、NAT、アドレスのセット
iptables低レベル。非推奨へ移行中古いシステムやスクリプト

重要なのは、管理ツールを1つだけ使うことです。ufwと手動のiptablesで同時にルールを変更すると、互いに上書きし合い、動作が予測できなくなります。

ufwのインストールと確認

Ubuntuではufwが通常すでにインストールされていますが、無効になっています。Debianでは別途インストールします。

$ sudo apt update
$ sudo apt install ufw
$ sudo ufw status
Status: inactive

inactive は、ufwのルールが適用されていないことを意味します。まだ有効にしないでください。先にSSHを許可しておかないと、リモートのサーバーで自分自身の接続を遮断してしまいます。

サーバーでのufw設定の手順

手順1:デフォルトポリシー

デフォルトポリシーは、どのルールにも一致しなかったパケットをどう扱うかを決めます。サーバーにとって安全な方法は、受信を拒否し、送信を許可することです。

$ sudo ufw default deny incoming
Default incoming policy changed to 'deny'
$ sudo ufw default allow outgoing
Default outgoing policy changed to 'allow'

自分から始めた送信接続への応答は自動的に通過します。ufwが接続の状態を追跡している(connection tracking)ためです。応答のためにポートを開ける必要はありません。

手順2:SSHを許可する

$ sudo ufw allow OpenSSH
Rules updated
Rules updated (v6)

OpenSSH はアプリケーションプロファイルです。SSHが標準以外のポートで待ち受けている場合は、ポートを明示的に許可してください(例:sudo ufw allow 2222/tcp)。sshdが実際にどのポートで待ち受けているかは、sudo ss -tlnp | grep sshd で確認できます。

手順3:サービスのポートを開ける

$ sudo ufw allow 80/tcp
$ sudo ufw allow 443/tcp

プロトコルを指定してください。/tcp を省略すると、ufwはTCPとUDPの両方でポートを開けます。使わないUDPのポートを開けておく必要はありません。

手順4:ファイアウォールを有効にする

$ sudo ufw enable
Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
Firewall is active and enabled on system startup

ufwはすぐに起動し、以降は再起動のたびに自動で有効になります。現在のSSHセッションは閉じないでください。別のセッションを開いて、接続できることを確認してください。

手順5:ルールを確認する

$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp (OpenSSH)           ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere
22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)
80/tcp (v6)                ALLOW IN    Anywhere (v6)
443/tcp (v6)               ALLOW IN    Anywhere (v6)

IPv6のルールは、/etc/default/ufw に IPV6=yes が設定されている場合(既定の値)、ufwが自動で作成します。

ufwでポートを開ける:構文のすべてのパターン

ufw allow は短い形式と完全な形式の両方に対応しています。短い形式は一般的なケースに便利で、完全な形式は送信元、インターフェース、宛先アドレスを指定する必要があるときに使います。

目的コマンド
TCPポートsudo ufw allow 443/tcp
UDPポートsudo ufw allow 51820/udp
ポートの範囲sudo ufw allow 6000:6007/tcp
サービス名で指定sudo ufw allow https
特定のIPからsudo ufw allow from 203.0.113.10
サブネットからポートへsudo ufw allow from 10.0.0.0/24 to any port 5432 proto tcp
特定のインターフェースでsudo ufw allow in on eth1 to any port 3306 proto tcp
拒否するsudo ufw deny 23/tcp
応答を返して拒否するsudo ufw reject 25/tcp

deny と reject の違いは次のとおりです。deny はパケットを黙って破棄するので、接続しようとした側はタイムアウトまで待つことになります。reject は「ポートは閉じています」という応答を返します。外部からのアクセスには通常 deny を、内部ネットワークでは reject を使うと、クライアントはすぐにエラーを確認できます。

データベースへのアクセスを1台のサーバーに限定する

よくあるケースとして、PostgreSQLやMySQLをインターネットから見えないようにしたいが、隣のサーバーのアプリケーションからは接続できるようにしたい場合があります。

$ sudo ufw allow from 10.0.0.5 to any port 5432 proto tcp
Rule added

deny incoming のポリシーにより、ポート5432は他のすべての送信元に対して閉じたままになります。さらに、DBサービス自体を内部アドレスだけで待ち受けさせることもお勧めします。ファイアウォールとサービスの設定が互いを補強します。

アプリケーションプロファイル

パッケージは、自分が使うポートの説明を /etc/ufw/applications.d/ に置くことがあります。プロファイルの一覧と内容は次のように確認します。

$ sudo ufw app list
Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH
$ sudo ufw app info "Nginx Full"
Profile: Nginx Full
Title: Web Server (Nginx, HTTP + HTTPS)
Description: Small, but very powerful and efficient web server

Ports:
  80,443/tcp

プロファイルを許可するには sudo ufw allow "Nginx Full" を実行します。空白を含む名前は引用符で囲んでください。独自のプロファイルは同じディレクトリにINI形式で記述し、sudo ufw app update 名前 で反映できます。

ufwのルールを削除する

最も確実な方法は番号を指定することです。

$ sudo ufw status numbered
Status: active

     To                         Action      From
     --                         ------      ----
[ 1] OpenSSH                    ALLOW IN    Anywhere
[ 2] 80/tcp                     ALLOW IN    Anywhere
[ 3] 443/tcp                    ALLOW IN    Anywhere
[ 4] OpenSSH (v6)               ALLOW IN    Anywhere (v6)
[ 5] 80/tcp (v6)                ALLOW IN    Anywhere (v6)
[ 6] 443/tcp (v6)               ALLOW IN    Anywhere (v6)

$ sudo ufw delete 2
Deleting:
 allow 80/tcp
Proceed with operation (y|n)? y
Rule deleted

ルールを削除すると番号が詰められるため、次の削除の前に必ず status numbered を再度実行してください。もう一つの方法は、同じルールを delete を付けて指定することです(例:sudo ufw delete allow 80/tcp)。この場合、IPv4とIPv6の両方のルールが一度に削除されます。

ルールの順序と先頭への挿入

ufwはルールを上から順に確認し、最初に一致したものを適用します。そのため、特定のアドレスを拒否するルールは、一般的な許可ルールより上にある必要があります。

$ sudo ufw insert 1 deny from 198.51.100.23
Rule inserted

通常のコマンドで deny を追加すると、allow 443/tcp の後ろに入ってしまい、そのポートに対しては効果がありません。

SSHを総当たりから守る:ufw limit

limit ルールは接続を許可しますが、30秒以内に6回以上の接続を試みたアドレスをブロックします。

$ sudo ufw limit OpenSSH
Rule updated

これは大量のパスワード総当たりに対する簡単な防御です。より強固にするには、鍵認証だけでのログインを有効にし、必要に応じてfail2banを併用してください。fail2banはufwを通じてアドレスをブロックできます。

ufwのログ

ログは sudo ufw logging on で有効になります。レベルは low、medium、high、full から選べます。記録は /var/log/ufw.log(rsyslog経由)とカーネルのログに書き込まれます。

$ sudo journalctl -k | grep 'UFW BLOCK' | tail -2
Oct 14 10:12:03 web kernel: [UFW BLOCK] IN=eth0 OUT= SRC=45.155.205.1 DST=192.0.2.10 PROTO=TCP SPT=51544 DPT=3389
Oct 14 10:12:09 web kernel: [UFW BLOCK] IN=eth0 OUT= SRC=89.248.165.7 DST=192.0.2.10 PROTO=TCP SPT=40312 DPT=23

SRC と DPT のフィールドは、誰がどのポートにアクセスしようとしたかを示します。サービスが応答せず、ログにそのポートの UFW BLOCK が記録されている場合は、ルールが追加されていないことを意味します。

ufwとDocker:ルールを無視してポートが開く理由

これは非常に有名な落とし穴です。Dockerはポートを公開する(-p 8080:80)と、NATのルールと DOCKER チェーンを自動で追加します。コンテナへのトラフィックはforwardを通るため、ufwの受信ルールをバイパスします。その結果、ufw status にはポート8080が表示されないのに、外部からはアクセスできてしまいます。

解決方法は次のとおりです。

  • ポートをlocalhostだけに公開する(-p 127.0.0.1:8080:80)、そして外部へはリバースプロキシ(nginx)経由で公開する。
  • DOCKER-USER チェーンに独自のルールを追加する。Dockerはこのチェーンを上書きしません。
  • 内部用のコンテナのポートは公開せず、Dockerのネットワークを介して接続する。

リセットと無効化

$ sudo ufw disable     # 無効にする。ルールは保持される
$ sudo ufw reset       # すべてのルールを削除して無効にする
$ sudo ufw reload      # 設定を読み直す

reset を実行する前に、ufwは /etc/ufw/ にルールファイルのバックアップを日付付きのサフィックスで保存します。実際のルールは /etc/ufw/user.rules と user6.rules に保存されています。これらのファイルは手で編集せず、必ずコマンドで変更してください。

nftablesの基礎

nftablesは、カーネル内でiptablesを置き換える現代的な仕組みです。Debian 10以降やUbuntu 20.10以降では、iptables コマンドは既定で互換レイヤー iptables-nft を通して動作します。そのため、ufwのルールもnftablesに格納されています。カーネルに読み込まれているすべてのルールを表示するには次のとおりです。

$ sudo nft list ruleset

nftablesの構造は次のとおりです。テーブル(ファミリーは ip、ip6、または両方に対応する inet)がチェーンを持ち、チェーンはnetfilterの処理ポイントに結び付けられています。チェーンにはルールが含まれます。サーバー向けの最小限のファイアウォールを /etc/nftables.conf に書くと次のようになります。

#!/usr/sbin/nft -f
flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;
        ct state established,related accept
        ct state invalid drop
        iif "lo" accept
        ip protocol icmp accept
        meta l4proto ipv6-icmp accept
        tcp dport { 22, 80, 443 } accept
    }
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
    chain output {
        type filter hook output priority 0; policy accept;
    }
}

適用して起動時に有効にするには次のとおりです。

$ sudo nft -c -f /etc/nftables.conf   # 構文チェックのみ
$ sudo systemctl enable --now nftables

ルールは稼働中に追加することもできますが、ファイルに保存しなければ再起動後に消えます。

$ sudo nft add rule inet filter input tcp dport 5432 ip saddr 10.0.0.5 accept
$ sudo nft -a list chain inet filter input   # -a でルールのhandleを表示
$ sudo nft delete rule inet filter input handle 9

nftablesの大きな利点は、セット({ 22, 80, 443 })が使えることと、IPv4とIPv6を1つの inet テーブルで扱えることです。ufwと同時に独自の nftables.service を動かさないでください。flush ruleset によってufwのルールが消えてしまいます。

よくある間違い

  • SSHのルールがないままufwを有効にした:サーバーへの接続が失われます。プロバイダーのコンソール(VNCやシリアル)が頼りになります。sudo ufw allow OpenSSH を実行するか、sudo ufw disable で無効にしてください。
  • ポートを開けたのにサービスに接続できない:サービスが外部アドレスではなく 127.0.0.1 で待ち受けています。ss -tlnp で確認してください。
  • クラウドのファイアウォールがある:多くのプロバイダーにはセキュリティグループがあります。ポートはそちらでも開ける必要があります。
  • denyが効かない:ルールが許可ルールより下にあります。insert 1 を使ってください。

まとめ

基本的なufwの設定は、default deny incoming の指定、SSHとサービスのポートの許可、ufw enable での有効化、そして status numbered での確認から成ります。特定の相手だけに許可するには from と port を使ったルールを、SSHには limit を使います。Dockerを使うときは、Dockerがufwをバイパスすることを忘れないでください。NATや複雑なルールセットが必要になったら、nftablesへ移行します。サーバーのファイアウォールは、Linuxコースで実践的に解説しています。

よくある質問

ufwとiptablesの違いは何ですか?

ufwは、シンプルな構文を持つ上位ツールです。iptablesやnftablesのルールを自動で生成します。iptablesとnftablesは低レベルのツールで、完全な制御ができますが、より多くの知識が必要です。

ufwのルールは再起動後も保持されますか?

はい。sudo ufw enable を実行すると、ファイアウォールは起動時に自動で有効になります。ルールは /etc/ufw/user.rules と user6.rules に保存されています。

送信接続のためにポートを開ける必要はありますか?

送信ポリシーが allow outgoing であれば必要ありません。ufwは接続の状態を追跡しているため、送信接続への応答を自動的に通します。

ufwが有効なままpingを許可するにはどうすればよいですか?

ufwは既定で、受信のICMP echo要求(ping)を通します。このルールは /etc/ufw/before.rules に定義されています。pingを禁止するには、そのファイル内のルールを ACCEPT から DROP に変更し、sudo ufw reload を実行します。

CentOSやRocky Linuxでもufwを使えますか?

これらの標準的なツールはfirewalldで、コマンドは firewall-cmd です。ufwはEPELからインストールできますが、firewalldと併用しないことをお勧めします。

動画でこのテーマを学ぶ

このテーマはコース「Linux: с нуля до уверенного администрирования」で実践とともに詳しく解説しています。

コースへ