Firewall Management
Linux ファイアウォール管理 完全ガイド
目次
- はじめに
- ファイアウォールの基礎概念
- Netfilter アーキテクチャ
- iptables 詳細解説
- 4.1 テーブルとチェイン
- 4.2 ルールの構文と記述方法
- 4.3 ターゲット(アクション)
- 4.4 NAT(ネットワークアドレス変換)
- 4.5 接続追跡(Connection Tracking)
- 4.6 レート制限
- 4.7 ロギング
- 4.8 ルールの永続化
- ip6tables(IPv6 ファイアウォール)
- nftables 詳細解説
- 6.1 nftables の概要と利点
- 6.2 基本構文
- 6.3 テーブルとチェイン
- 6.4 セットとマップ
- 6.5 iptables からの移行
- firewalld 詳細解説
- 7.1 ゾーンの概念
- 7.2 サービス管理
- 7.3 リッチルール
- 7.4 ダイレクトルール
- 7.5 firewall-cmd コマンド体系
- 実践シナリオ
- トラブルシューティング
- ベストプラクティス
- 比較表
- 参考文献
1. はじめに
Linux ファイアウォールは、ネットワークセキュリティの最前線であり、システム管理者にとって最も重要なスキルの一つである。本ガイドでは、Linux におけるファイアウォール管理の全体像を包括的に解説する。iptables、nftables、firewalld の3つの主要なファイアウォール管理ツールについて、基礎概念から実践的な構成例まで、網羅的にカバーする。
対象読者
- Linux システム管理者
- ネットワークエンジニア
- セキュリティエンジニア
- SRE(Site Reliability Engineer)
前提知識
- TCP/IP ネットワーキングの基礎
- Linux コマンドラインの操作
- 基本的なネットワークサービス(HTTP、SSH、DNS 等)の理解
2. ファイアウォールの基礎概念
2.1 ファイアウォールとは
ファイアウォールは、ネットワークトラフィックを監視・制御し、事前に定義されたセキュリティルールに基づいてパケットの通過を許可または拒否するネットワークセキュリティシステムである。
2.2 パケットフィルタリングの種類
| 種類 | 説明 | 特徴 |
|---|---|---|
| ステートレスフィルタリング | 個々のパケットを独立して評価 | 高速だが柔軟性に欠ける |
| ステートフルフィルタリング | 接続状態を追跡して判断 | より高度なフィルタリングが可能 |
| アプリケーション層フィルタリング | L7 レベルでの検査 | 深い検査が可能だが負荷が高い |
2.3 Linux におけるファイアウォールの歴史
ipfwadm (Linux 2.0) → ipchains (Linux 2.2) → iptables (Linux 2.4+) → nftables (Linux 3.13+)
3. Netfilter アーキテクチャ
3.1 Netfilter フレームワーク
Netfilter は Linux カーネルに組み込まれたパケット処理フレームワークである。iptables と nftables はいずれも Netfilter のフロントエンドとして機能する。
3.2 フックポイント
Netfilter は、パケット処理パスの5つのポイントにフックを提供する。
┌─────────────┐
│ PREROUTING │
└──────┬──────┘
│
┌──────▼──────┐
│ ルーティング │
│ 判定 │
└──┬───────┬──┘
│ │
┌────────▼───┐ ┌─▼────────┐
│ INPUT │ │ FORWARD │
└────────┬───┘ └─┬────────┘
│ │
┌────────▼───┐ │
│ ローカル │ │
│ プロセス │ │
└────────┬───┘ │
│ │
┌────────▼───┐ │
│ OUTPUT │ │
└────────┬───┘ │
│ │
┌──▼───────▼──┐
│ POSTROUTING │
└──────┬──────┘
│
[出力]
| フックポイント | 説明 |
|---|---|
| PREROUTING | パケットがインターフェースに到着した直後(ルーティング前) |
| INPUT | ローカルプロセス宛のパケット |
| FORWARD | 転送されるパケット(ルーターとして動作時) |
| OUTPUT | ローカルプロセスが生成したパケット |
| POSTROUTING | パケットがインターフェースから出る直前 |
4. iptables 詳細解説
4.1 テーブルとチェイン
iptables は複数のテーブルで構成され、各テーブルには複数のチェインが含まれる。
テーブル一覧
| テーブル | 用途 | 含まれるチェイン |
|---|---|---|
| filter | パケットフィルタリング(デフォルト) | INPUT, FORWARD, OUTPUT |
| nat | ネットワークアドレス変換 | PREROUTING, INPUT, OUTPUT, POSTROUTING |
| mangle | パケットヘッダの変更 | 全5チェイン |
| raw | 接続追跡の除外 | PREROUTING, OUTPUT |
| security | SELinux 用のセキュリティマーキング | INPUT, FORWARD, OUTPUT |
テーブルの確認
# filter テーブルのルール一覧(デフォルト)
$ sudo iptables -L -n -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
# nat テーブルのルール一覧
$ sudo iptables -t nat -L -n -v
# mangle テーブルのルール一覧
$ sudo iptables -t mangle -L -n -v
4.2 ルールの構文と記述方法
基本構文
iptables [-t テーブル] コマンド チェイン [マッチ条件] -j ターゲット
コマンド一覧
| コマンド | 説明 |
|---|---|
-A (append) | チェインの末尾にルールを追加 |
-I (insert) | チェインの指定位置にルールを挿入 |
-D (delete) | ルールを削除 |
-R (replace) | ルールを置換 |
-F (flush) | チェインの全ルールを削除 |
-Z (zero) | カウンタをリセット |
-N (new) | カスタムチェインを作成 |
-X (delete-chain) | カスタムチェインを削除 |
-P (policy) | チェインのデフォルトポリシーを設定 |
-L (list) | ルールを一覧表示 |
マッチ条件
# プロトコル指定
-p tcp
-p udp
-p icmp
# 送信元アドレス
-s 192.168.1.0/24
-s 10.0.0.1
# 宛先アドレス
-d 172.16.0.0/16
# インターフェース指定
-i eth0 # 入力インターフェース
-o eth1 # 出力インターフェース
# ポート指定(TCP/UDP)
--dport 80 # 宛先ポート
--sport 1024:65535 # 送信元ポート範囲
-m multiport --dports 80,443,8080 # 複数ポート
# 接続状態
-m conntrack --ctstate NEW,ESTABLISHED,RELATED
# MAC アドレス
-m mac --mac-source 00:11:22:33:44:55
基本的なルール例
# SSH(ポート22)を許可
$ sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# 特定のIPからのSSHを許可
$ sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 22 -j ACCEPT
# HTTP/HTTPS を許可
$ sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
# ICMP(ping)を許可
$ sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
# ループバックインターフェースを許可
$ sudo iptables -A INPUT -i lo -j ACCEPT
$ sudo iptables -A OUTPUT -o lo -j ACCEPT
# 確立済み接続を許可
$ sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# デフォルトポリシーをDROPに設定
$ sudo iptables -P INPUT DROP
$ sudo iptables -P FORWARD DROP
$ sudo iptables -P OUTPUT ACCEPT
4.3 ターゲット(アクション)
| ターゲット | 説明 |
|---|---|
| ACCEPT | パケットを許可 |
| DROP | パケットを無言で破棄 |
| REJECT | パケットを拒否し、エラーメッセージを返す |
| LOG | パケット情報をログに記録(処理は継続) |
| SNAT | 送信元アドレスを変換 |
| DNAT | 宛先アドレスを変換 |
| MASQUERADE | 動的な送信元NAT |
| REDIRECT | パケットをローカルにリダイレクト |
| RETURN | 現在のチェインから呼び出し元に戻る |
REJECT の詳細
# TCP RST で拒否
$ sudo iptables -A INPUT -p tcp --dport 23 -j REJECT --reject-with tcp-reset
# ICMP port-unreachable で拒否(デフォルト)
$ sudo iptables -A INPUT -p tcp --dport 23 -j REJECT --reject-with icmp-port-unreachable
# ICMP host-unreachable で拒否
$ sudo iptables -A INPUT -j REJECT --reject-with icmp-host-unreachable
4.4 NAT(ネットワークアドレス変換)
SNAT(Source NAT)
送信元IPアドレスを変換する。通常、内部ネットワークからインターネットへのアクセスに使用する。
# 静的SNAT: 内部ネットワークからの通信の送信元を203.0.113.1に変換
$ sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.1
# 複数のIPアドレスでSNAT(ロードバランシング)
$ sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j SNAT --to-source 203.0.113.1-203.0.113.10
MASQUERADE
動的なIPアドレス(DHCPなど)を持つインターフェースでの SNAT。
# マスカレード設定
$ sudo iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -o eth0 -j MASQUERADE
# IP フォワーディングの有効化(必須)
$ sudo sysctl -w net.ipv4.ip_forward=1
# 永続化
$ echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
DNAT(Destination NAT)
宛先IPアドレスを変換する。ポートフォワーディングやロードバランシングに使用する。
# ポートフォワーディング: 外部からのポート80を内部サーバー192.168.1.100:8080に転送
$ sudo iptables -t nat -A PREROUTING -p tcp -i eth0 --dport 80 -j DNAT --to-destination 192.168.1.100:8080
# FORWARDチェインでも許可が必要
$ sudo iptables -A FORWARD -p tcp -d 192.168.1.100 --dport 8080 -m conntrack --ctstate NEW -j ACCEPT
$ sudo iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 複数サーバーへのロードバランシング
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 192.168.1.100-192.168.1.103
REDIRECT
パケットをローカルマシンの別のポートにリダイレクトする。
# ポート80へのアクセスをポート8080にリダイレクト(透過プロキシ用)
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 8080
4.5 接続追跡(Connection Tracking)
conntrack モジュール
# 接続追跡テーブルの確認
$ sudo conntrack -L
tcp 6 431999 ESTABLISHED src=192.168.1.10 dst=93.184.216.34 sport=54321 dport=80 src=93.184.216.34 dst=192.168.1.10 sport=80 dport=54321 [ASSURED] mark=0 use=1
# 接続数の確認
$ sudo conntrack -C
256
# 特定の接続をフラッシュ
$ sudo conntrack -D -s 192.168.1.10
# 接続追跡テーブルのサイズ確認・変更
$ cat /proc/sys/net/netfilter/nf_conntrack_max
65536
$ sudo sysctl -w net.netfilter.nf_conntrack_max=131072
接続状態
| 状態 | 説明 |
|---|---|
| NEW | 新しい接続の最初のパケット |
| ESTABLISHED | 確立済み接続に属するパケット |
| RELATED | 既存の接続に関連する新しい接続(例: FTP データ接続) |
| INVALID | 既知の接続に属さない、または不正なパケット |
| UNTRACKED | raw テーブルで NOTRACK としてマークされたパケット |
# ステートフルファイアウォールの基本設定
$ sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
$ sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
$ sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
$ sudo iptables -P INPUT DROP
4.6 レート制限
limit モジュール
# SSH接続のレート制限(1分間に3回まで、バースト5)
$ sudo iptables -A INPUT -p tcp --dport 22 -m limit --limit 3/minute --limit-burst 5 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 22 -j DROP
# ICMP のレート制限
$ sudo iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/second --limit-burst 4 -j ACCEPT
$ sudo iptables -A INPUT -p icmp --icmp-type echo-request -j DROP
# ログのレート制限
$ sudo iptables -A INPUT -m limit --limit 5/minute -j LOG --log-prefix "iptables-dropped: " --log-level 4
hashlimit モジュール(送信元IPごとのレート制限)
# 送信元IPごとにSSH接続を制限
$ sudo iptables -A INPUT -p tcp --dport 22 -m hashlimit \
--hashlimit-name ssh_limit \
--hashlimit-above 4/minute \
--hashlimit-burst 5 \
--hashlimit-mode srcip \
-j DROP
recent モジュール(ブルートフォース対策)
# SSH ブルートフォース攻撃対策
$ sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSH
$ sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
4.7 ロギング
# 全てのドロップされたパケットをログに記録
$ sudo iptables -A INPUT -j LOG --log-prefix "IPT-DROP-INPUT: " --log-level 4
# 特定のトラフィックをログ
$ sudo iptables -A INPUT -p tcp --dport 22 -j LOG --log-prefix "IPT-SSH: " --log-level info
# ログの確認
$ sudo dmesg | grep "IPT-DROP"
$ sudo journalctl -k | grep "IPT-DROP"
# rsyslog でファイアウォールログを専用ファイルに出力
$ cat /etc/rsyslog.d/iptables.conf
:msg, contains, "IPT-" /var/log/iptables.log
& stop
# NFLOG ターゲット(ulogd2 と連携)
$ sudo iptables -A INPUT -j NFLOG --nflog-prefix "firewall: " --nflog-group 1
4.8 ルールの永続化
iptables-persistent(Debian/Ubuntu)
# インストール
$ sudo apt install iptables-persistent
# ルールの保存
$ sudo netfilter-persistent save
# ルールの復元
$ sudo netfilter-persistent reload
# 手動での保存と復元
$ sudo iptables-save > /etc/iptables/rules.v4
$ sudo ip6tables-save > /etc/iptables/rules.v6
$ sudo iptables-restore < /etc/iptables/rules.v4
iptables-services(RHEL/CentOS)
# インストール
$ sudo yum install iptables-services
# サービスの有効化
$ sudo systemctl enable iptables
$ sudo systemctl start iptables
# ルールの保存
$ sudo service iptables save
# または
$ sudo iptables-save > /etc/sysconfig/iptables
# ルールの復元
$ sudo iptables-restore < /etc/sysconfig/iptables
iptables-save/restore の出力形式
$ sudo iptables-save
# Generated by iptables-save v1.8.7 on Thu Apr 10 10:00:00 2026
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
COMMIT
5. ip6tables(IPv6 ファイアウォール)
5.1 基本概念
ip6tables は iptables の IPv6 版であり、構文はほぼ同一だが IPv6 固有の機能がある。
5.2 IPv6 固有の考慮事項
# ICMPv6 は IPv6 の動作に必須(NDP: Neighbor Discovery Protocol)
# 以下のICMPv6タイプは必ず許可する必要がある
# Router Solicitation
$ sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type router-solicitation -j ACCEPT
# Router Advertisement
$ sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT
# Neighbor Solicitation
$ sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbour-solicitation -j ACCEPT
# Neighbor Advertisement
$ sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbour-advertisement -j ACCEPT
# Echo Request/Reply
$ sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT
$ sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT
5.3 IPv6 ファイアウォールの基本設定
# ポリシー設定
$ sudo ip6tables -P INPUT DROP
$ sudo ip6tables -P FORWARD DROP
$ sudo ip6tables -P OUTPUT ACCEPT
# ループバック許可
$ sudo ip6tables -A INPUT -i lo -j ACCEPT
# 確立済み接続を許可
$ sudo ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# 必須 ICMPv6 を許可
$ sudo ip6tables -A INPUT -p icmpv6 -j ACCEPT
# リンクローカルアドレスからの通信を許可
$ sudo ip6tables -A INPUT -s fe80::/10 -j ACCEPT
# SSH を許可
$ sudo ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
# HTTP/HTTPS を許可
$ sudo ip6tables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
6. nftables 詳細解説
6.1 nftables の概要と利点
nftables は iptables の後継として開発されたパケットフィルタリングフレームワークである。
iptables と nftables の比較
| 項目 | iptables | nftables |
|---|---|---|
| カーネルサポート | Linux 2.4+ | Linux 3.13+ |
| IPv4/IPv6 | 別々のコマンド (iptables/ip6tables) | 統一された構文 |
| ルール変更 | ルール毎にカーネルコール | アトミック操作が可能 |
| 構文 | コマンドライン引数ベース | 独自の構文言語 |
| パフォーマンス | 線形ルール評価 | セット/マップによる高速マッチ |
| テーブル | 固定(filter, nat, mangle, raw) | ユーザー定義 |
| デフォルトルール | なし(全テーブルに全チェインが存在) | 必要なものだけ作成 |
6.2 基本構文
nft コマンドの基本操作
# ルールセット一覧
$ sudo nft list ruleset
# テーブル一覧
$ sudo nft list tables
# 特定のテーブルの内容
$ sudo nft list table inet filter
# ルールセットのクリア
$ sudo nft flush ruleset
ファミリー
| ファミリー | 説明 |
|---|---|
| ip | IPv4 のみ |
| ip6 | IPv6 のみ |
| inet | IPv4 と IPv6 の両方 |
| arp | ARP |
| bridge | ブリッジ |
| netdev | ネットワークデバイス |
6.3 テーブルとチェイン
# テーブルの作成
$ sudo nft add table inet filter
# ベースチェインの作成(入力)
$ sudo nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
# ベースチェインの作成(転送)
$ sudo nft add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }
# ベースチェインの作成(出力)
$ sudo nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }
# ルールの追加
$ sudo nft add rule inet filter input iif lo accept
$ sudo nft add rule inet filter input ct state established,related accept
$ sudo nft add rule inet filter input ct state invalid drop
$ sudo nft add rule inet filter input tcp dport 22 accept
$ sudo nft add rule inet filter input tcp dport { 80, 443 } accept
$ sudo nft add rule inet filter input ip protocol icmp accept
$ sudo nft add rule inet filter input ip6 nexthdr icmpv6 accept
nftables 設定ファイル
# /etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# ループバックインターフェース
iif lo accept
# 確立済み接続
ct state established,related accept
# 不正なパケットを破棄
ct state invalid drop
# ICMPv4
ip protocol icmp accept
# ICMPv6
ip6 nexthdr icmpv6 accept
# SSH
tcp dport 22 accept
# HTTP/HTTPS
tcp dport { 80, 443 } accept
# ログしてドロップ
log prefix "nft-drop: " counter drop
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
}
chain output {
type filter hook output priority 0; policy accept;
}
}
# NAT テーブル
table inet nat {
chain prerouting {
type nat hook prerouting priority -100;
}
chain postrouting {
type nat hook postrouting priority 100;
# マスカレード
oif "eth0" masquerade
}
}
6.4 セットとマップ
名前付きセット
# IPアドレスセットの作成
$ sudo nft add set inet filter allowed_ips { type ipv4_addr \; }
# セットに要素を追加
$ sudo nft add element inet filter allowed_ips { 192.168.1.10, 192.168.1.20, 10.0.0.0/24 }
# セットをルールで使用
$ sudo nft add rule inet filter input ip saddr @allowed_ips accept
# ポートセットの作成
$ sudo nft add set inet filter web_ports { type inet_service \; }
$ sudo nft add element inet filter web_ports { 80, 443, 8080, 8443 }
$ sudo nft add rule inet filter input tcp dport @web_ports accept
匿名セット(インラインセット)
# インラインで複数のポートを指定
$ sudo nft add rule inet filter input tcp dport { 22, 80, 443 } accept
# インラインで複数のIPを指定
$ sudo nft add rule inet filter input ip saddr { 192.168.1.0/24, 10.0.0.0/8 } accept
タイムアウト付きセット(動的セット)
# タイムアウト付きのブラックリストセット
$ sudo nft add set inet filter blacklist { type ipv4_addr \; timeout 1h \; }
# 動的にIPを追加(ルールから)
$ sudo nft add rule inet filter input tcp dport 22 ct state new meter ssh_bruteforce { ip saddr limit rate over 3/minute burst 5 packets } add @blacklist { ip saddr } drop
マップ
# DNAT マップの作成
$ sudo nft add map inet nat dnat_map { type inet_service : ipv4_addr \; }
$ sudo nft add element inet nat dnat_map { 80 : 192.168.1.100, 443 : 192.168.1.101, 8080 : 192.168.1.102 }
# マップをルールで使用
$ sudo nft add rule inet nat prerouting dnat to tcp dport map @dnat_map
# バーディクト(verdict)マップ
$ sudo nft add map inet filter port_policy { type inet_service : verdict \; }
$ sudo nft add element inet filter port_policy { 22 : accept, 80 : accept, 443 : accept }
$ sudo nft add rule inet filter input tcp dport vmap @port_policy
6.5 iptables からの移行
iptables-translate コマンド
# iptables ルールを nftables に変換
$ iptables-translate -A INPUT -p tcp --dport 22 -j ACCEPT
nft add rule ip filter INPUT tcp dport 22 counter accept
$ iptables-translate -A INPUT -s 192.168.1.0/24 -p tcp --dport 80 -j ACCEPT
nft add rule ip filter INPUT ip saddr 192.168.1.0/24 tcp dport 80 counter accept
$ iptables-translate -t nat -A POSTROUTING -o eth0 -j MASQUERADE
nft add rule ip nat POSTROUTING oifname "eth0" counter masquerade
# ルールセット全体を変換
$ iptables-save | iptables-restore-translate > /etc/nftables.conf
移行時の注意点
# 1. iptables の互換レイヤーを確認
$ sudo iptables -V
iptables v1.8.7 (nf_tables) # nf_tables バックエンド使用中
# 2. xtables-nft-multi の確認
$ ls -la /usr/sbin/iptables
lrwxrwxrwx 1 root root 26 Apr 10 10:00 /usr/sbin/iptables -> /etc/alternatives/iptables
# 3. 互換レイヤーの切り替え
$ sudo update-alternatives --config iptables
7. firewalld 詳細解説
7.1 ゾーンの概念
firewalld はゾーンベースのファイアウォール管理ツールである。各ネットワークインターフェースはゾーンに割り当てられる。
デフォルトゾーン
| ゾーン | 説明 | デフォルト動作 |
|---|---|---|
| drop | 全ての受信パケットを無言で破棄 | 送信のみ許可 |
| block | 全ての受信パケットを拒否(icmp-host-prohibited) | 送信のみ許可 |
| public | 信頼されないネットワーク向け | SSH, DHCPv6-client |
| external | マスカレード有効のNAT向け | SSH |
| dmz | DMZ サーバー向け | SSH |
| work | 職場ネットワーク向け | SSH, DHCPv6-client |
| home | ホームネットワーク向け | SSH, mDNS, Samba, DHCPv6-client |
| internal | 内部ネットワーク向け | home と同等 |
| trusted | 全ての接続を許可 | 全て許可 |
ゾーン管理コマンド
# デフォルトゾーンの確認
$ sudo firewall-cmd --get-default-zone
public
# アクティブゾーンの確認
$ sudo firewall-cmd --get-active-zones
public
interfaces: eth0
internal
interfaces: eth1
# 全ゾーンの一覧
$ sudo firewall-cmd --get-zones
block dmz drop external home internal public trusted work
# ゾーンの詳細情報
$ sudo firewall-cmd --zone=public --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: dhcpv6-client ssh
ports:
protocols:
forward: no
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
# インターフェースのゾーン変更
$ sudo firewall-cmd --zone=internal --change-interface=eth1
# デフォルトゾーンの変更
$ sudo firewall-cmd --set-default-zone=public
# ソースベースのゾーン割り当て
$ sudo firewall-cmd --zone=trusted --add-source=192.168.1.0/24 --permanent
7.2 サービス管理
# 利用可能なサービス一覧
$ sudo firewall-cmd --get-services
RH-Satellite-6 amanda-client amanda-k5-client amqp amqps ... ssh ...
# サービスの追加(一時的)
$ sudo firewall-cmd --zone=public --add-service=http
success
# サービスの追加(永続的)
$ sudo firewall-cmd --zone=public --add-service=http --permanent
success
# 複数サービスの追加
$ sudo firewall-cmd --zone=public --add-service={http,https,dns} --permanent
# サービスの削除
$ sudo firewall-cmd --zone=public --remove-service=http --permanent
# ポート番号での追加
$ sudo firewall-cmd --zone=public --add-port=8080/tcp --permanent
$ sudo firewall-cmd --zone=public --add-port=5000-5100/tcp --permanent
# 設定のリロード
$ sudo firewall-cmd --reload
# カスタムサービスの作成
$ sudo firewall-cmd --permanent --new-service=myapp
$ sudo firewall-cmd --permanent --service=myapp --set-description="My Application"
$ sudo firewall-cmd --permanent --service=myapp --add-port=9090/tcp
$ sudo firewall-cmd --permanent --service=myapp --add-port=9091/tcp
$ sudo firewall-cmd --reload
カスタムサービス定義ファイル
<!-- /etc/firewalld/services/myapp.xml -->
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>MyApp</short>
<description>My Custom Application</description>
<port protocol="tcp" port="9090"/>
<port protocol="tcp" port="9091"/>
<port protocol="udp" port="9092"/>
</service>
7.3 リッチルール
リッチルールは、firewalld でより複雑なルールを定義するための機能である。
# 基本構文
# rule [family="ipv4|ipv6"]
# [source address="address[/mask]" [invert="true"]]
# [destination address="address[/mask]" [invert="true"]]
# [service name="service"]
# [port port="port" protocol="protocol"]
# [log [prefix="prefix"] [level="level"] [limit value="rate/duration"]]
# [audit]
# [accept|reject|drop|mark]
# 特定IPからのSSHを許可
$ sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="ssh" accept' --permanent
# 特定IPからのアクセスを拒否
$ sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.0.100" drop' --permanent
# レート制限付きのSSH許可
$ sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" service name="ssh" accept limit value="3/m"' --permanent
# ログ付きのルール
$ sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" source address="10.0.0.0/8" service name="http" log prefix="HTTP-ACCESS: " level="info" accept' --permanent
# ポートフォワーディング
$ sudo firewall-cmd --zone=public --add-rich-rule='rule family="ipv4" forward-port port="80" protocol="tcp" to-port="8080" to-addr="192.168.1.100"' --permanent
# リッチルールの一覧
$ sudo firewall-cmd --zone=public --list-rich-rules
7.4 ダイレクトルール
ダイレクトルールは、firewalld を通じて iptables/nftables ルールを直接挿入する機能である。
# ダイレクトルールの追加
$ sudo firewall-cmd --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 9999 -j ACCEPT
# 永続的なダイレクトルール
$ sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 9999 -j ACCEPT
# ダイレクトルールの一覧
$ sudo firewall-cmd --direct --get-all-rules
# ダイレクトチェインの追加
$ sudo firewall-cmd --permanent --direct --add-chain ipv4 filter CUSTOM_CHAIN
$ sudo firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -j CUSTOM_CHAIN
$ sudo firewall-cmd --permanent --direct --add-rule ipv4 filter CUSTOM_CHAIN 0 -s 192.168.1.0/24 -j ACCEPT
注意: ダイレクトルールは非推奨となりつつあり、リッチルールまたはポリシーの使用が推奨される。
7.5 firewall-cmd コマンド体系
主要コマンド一覧
# 状態確認
$ sudo firewall-cmd --state
running
# 設定リロード
$ sudo firewall-cmd --reload
# 完全リロード(接続も切断)
$ sudo firewall-cmd --complete-reload
# パニックモード(全通信遮断)
$ sudo firewall-cmd --panic-on
$ sudo firewall-cmd --panic-off
$ sudo firewall-cmd --query-panic
# マスカレード
$ sudo firewall-cmd --zone=external --add-masquerade --permanent
# ポートフォワーディング
$ sudo firewall-cmd --zone=external --add-forward-port=port=80:proto=tcp:toport=8080:toaddr=192.168.1.100 --permanent
# ICMP ブロック
$ sudo firewall-cmd --zone=public --add-icmp-block=echo-request --permanent
# ランタイムからパーマネントへの移行
$ sudo firewall-cmd --runtime-to-permanent
8. 実践シナリオ
8.1 Web サーバーのファイアウォール構成
iptables での構成
#!/bin/bash
# Web サーバー用ファイアウォール設定スクリプト
# ルールのクリア
iptables -F
iptables -X
iptables -t nat -F
iptables -t mangle -F
# デフォルトポリシー
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# ループバック
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# 確立済み接続
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# SSH(管理ネットワークからのみ)
iptables -A INPUT -p tcp -s 10.0.0.0/8 --dport 22 -m conntrack --ctstate NEW -j ACCEPT
# HTTP/HTTPS
iptables -A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW -j ACCEPT
# ICMP(制限付き)
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/s --limit-burst 4 -j ACCEPT
# SYN Flood 対策
iptables -A INPUT -p tcp --syn -m limit --limit 25/s --limit-burst 50 -j ACCEPT
iptables -A INPUT -p tcp --syn -j DROP
# HTTP スローロリス対策
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j DROP
iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 50 -j DROP
# ログ
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "IPT-DROPPED: " --log-level 4
# 保存
iptables-save > /etc/iptables/rules.v4
nftables での構成
#!/usr/sbin/nft -f
# Web サーバー用 nftables 設定
flush ruleset
table inet webserver {
set admin_networks {
type ipv4_addr
flags interval
elements = { 10.0.0.0/8 }
}
chain input {
type filter hook input priority 0; policy drop;
# ループバック
iif lo accept
# 確立済み接続
ct state established,related accept
ct state invalid drop
# ICMPv4/v6
ip protocol icmp limit rate 1/second burst 4 packets accept
ip6 nexthdr icmpv6 accept
# SSH(管理ネットワークのみ)
tcp dport 22 ip saddr @admin_networks ct state new accept
# HTTP/HTTPS
tcp dport { 80, 443 } ct state new accept
# SYN Flood 対策
tcp flags syn limit rate 25/second burst 50 packets accept
tcp flags syn drop
# 接続数制限
tcp dport { 80, 443 } ct count over 50 drop
# ログ
limit rate 5/minute log prefix "nft-drop: " counter drop
}
chain output {
type filter hook output priority 0; policy accept;
}
}
8.2 データベースサーバーのファイアウォール構成
#!/usr/sbin/nft -f
# データベースサーバー用 nftables 設定
flush ruleset
table inet dbserver {
set app_servers {
type ipv4_addr
flags interval
elements = {
192.168.10.10,
192.168.10.11,
192.168.10.12
}
}
set admin_networks {
type ipv4_addr
flags interval
elements = { 10.0.0.0/24 }
}
set monitoring_servers {
type ipv4_addr
elements = { 10.0.1.50, 10.0.1.51 }
}
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state established,related accept
ct state invalid drop
# SSH(管理ネットワークのみ)
tcp dport 22 ip saddr @admin_networks accept
# MySQL/MariaDB(アプリサーバーのみ)
tcp dport 3306 ip saddr @app_servers accept
# PostgreSQL(アプリサーバーのみ)
tcp dport 5432 ip saddr @app_servers accept
# MongoDB(アプリサーバーのみ)
tcp dport 27017 ip saddr @app_servers accept
# Redis(アプリサーバーのみ)
tcp dport 6379 ip saddr @app_servers accept
# 監視(SNMP, Node Exporter)
udp dport 161 ip saddr @monitoring_servers accept
tcp dport 9100 ip saddr @monitoring_servers accept
# ICMP(管理ネットワークのみ)
ip protocol icmp ip saddr @admin_networks accept
# ログ
log prefix "db-drop: " counter drop
}
chain output {
type filter hook output priority 0; policy accept;
}
}
8.3 マルチティアアーキテクチャのファイアウォール構成
[インターネット]
│
┌───▼───┐
│ FW-1 │ (フロントエンドファイアウォール)
└───┬───┘
│
┌───▼───────────┐
│ DMZ 層 │ Web サーバー (192.168.1.0/24)
│ nginx/Apache │
└───┬───────────┘
│
┌───▼───┐
│ FW-2 │ (内部ファイアウォール)
└───┬───┘
│
┌───▼───────────┐
│ APP 層 │ アプリケーションサーバー (192.168.10.0/24)
│ Java/Python │
└───┬───────────┘
│
┌───▼───┐
│ FW-3 │ (バックエンドファイアウォール)
└───┬───┘
│
┌───▼───────────┐
│ DB 層 │ データベースサーバー (192.168.20.0/24)
│ MySQL/PgSQL │
└─────────────┘
フロントエンドファイアウォール(FW-1)
#!/usr/sbin/nft -f
# FW-1: フロントエンドファイアウォール
flush ruleset
table inet fw1 {
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state established,related accept
ct state invalid drop
# 管理SSH
tcp dport 22 ip saddr 10.0.0.0/24 accept
# ICMP
ip protocol icmp limit rate 2/second accept
}
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
ct state invalid drop
# インターネット → DMZ (HTTP/HTTPS のみ)
iif "eth0" oif "eth1" tcp dport { 80, 443 } ct state new accept
# DMZ → APP (特定ポートのみ)
iif "eth1" oif "eth2" ip saddr 192.168.1.0/24 ip daddr 192.168.10.0/24 tcp dport { 8080, 8443 } accept
# ログ
log prefix "fw1-drop: " counter drop
}
chain output {
type filter hook output priority 0; policy accept;
}
# NAT
chain prerouting {
type nat hook prerouting priority -100;
# 外部からの HTTP/HTTPS を Web サーバーに転送
iif "eth0" tcp dport 80 dnat to 192.168.1.10:80
iif "eth0" tcp dport 443 dnat to 192.168.1.10:443
}
chain postrouting {
type nat hook postrouting priority 100;
oif "eth0" masquerade
}
}
内部ファイアウォール(FW-2)
#!/usr/sbin/nft -f
# FW-2: 内部ファイアウォール
flush ruleset
table inet fw2 {
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
ct state invalid drop
# DMZ → APP (アプリケーションポートのみ)
ip saddr 192.168.1.0/24 ip daddr 192.168.10.0/24 tcp dport { 8080, 8443 } accept
# APP → DB (データベースポートのみ)
ip saddr 192.168.10.0/24 ip daddr 192.168.20.0/24 tcp dport { 3306, 5432, 6379 } accept
# APP → 外部 (API コール用)
ip saddr 192.168.10.0/24 oif "eth0" tcp dport { 80, 443 } accept
# DMZ からDB層への直接アクセスは禁止(暗黙のdrop)
log prefix "fw2-drop: " counter drop
}
}
9. トラブルシューティング
9.1 一般的な問題と対処法
ルールが効かない場合
# ルールの順序を確認(上から順に評価される)
$ sudo iptables -L -n -v --line-numbers
$ sudo nft list ruleset
# パケットカウンタを確認してルールにヒットしているか確認
$ sudo iptables -L -n -v | grep -i "dport 80"
0 0 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
# カウンタが0の場合、ルールにマッチしていない
接続が切れる場合
# conntrack テーブルの確認
$ sudo conntrack -L | grep "dport=22"
# conntrack テーブルのサイズ確認
$ cat /proc/sys/net/netfilter/nf_conntrack_count
$ cat /proc/sys/net/netfilter/nf_conntrack_max
# conntrack テーブルが満杯の場合の対処
$ sudo sysctl -w net.netfilter.nf_conntrack_max=262144
NAT が動作しない場合
# IP フォワーディングの確認
$ cat /proc/sys/net/ipv4/ip_forward
0 # 0 の場合は無効
# 有効化
$ sudo sysctl -w net.ipv4.ip_forward=1
# NAT ルールの確認
$ sudo iptables -t nat -L -n -v
$ sudo nft list table inet nat
# FORWARD チェインのポリシー確認
$ sudo iptables -L FORWARD -n -v
9.2 デバッグツール
# パケットのトレース(TRACE ターゲット)
$ sudo iptables -t raw -A PREROUTING -p tcp --dport 80 -j TRACE
$ sudo iptables -t raw -A OUTPUT -p tcp --dport 80 -j TRACE
# ログの確認
$ sudo dmesg | tail -50
$ sudo journalctl -k -f
# nftables のカウンター
$ sudo nft list ruleset | grep -A2 "counter"
# tcpdump でパケットを確認
$ sudo tcpdump -i eth0 -n port 80
$ sudo tcpdump -i any -n host 192.168.1.100
# ss/netstat でリスニングポートを確認
$ sudo ss -tlnp
$ sudo netstat -tlnp
9.3 firewalld のトラブルシューティング
# firewalld のログレベルを上げる
$ sudo firewall-cmd --set-log-denied=all
# unicast, broadcast, multicast, off も指定可能
# firewalld のバックエンドを確認
$ grep FirewallBackend /etc/firewalld/firewalld.conf
FirewallBackend=nftables
# firewalld が生成したルールを確認
$ sudo nft list ruleset
# または(iptables バックエンドの場合)
$ sudo iptables -L -n -v
# firewalld のリロード
$ sudo firewall-cmd --reload
# firewalld の完全再起動
$ sudo systemctl restart firewalld
10. ベストプラクティス
10.1 一般的なセキュリティ原則
- 最小権限の原則: デフォルトで全てを拒否し、必要なものだけを許可する
- 深層防御: 複数のセキュリティ層を重ねる
- 監査可能性: 適切なロギングを設定し、定期的にレビューする
- 変更管理: ファイアウォールルールの変更は文書化し、テスト環境で検証する
10.2 iptables/nftables ベストプラクティス
# 1. デフォルトポリシーは DROP
iptables -P INPUT DROP
iptables -P FORWARD DROP
# 2. ステートフルルールを最初に配置(パフォーマンス向上)
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
# 3. ループバックを必ず許可
iptables -A INPUT -i lo -j ACCEPT
# 4. 頻繁にマッチするルールを上位に配置
# 5. カスタムチェインで整理
iptables -N SSH_RULES
iptables -A SSH_RULES -s 10.0.0.0/8 -j ACCEPT
iptables -A SSH_RULES -j DROP
iptables -A INPUT -p tcp --dport 22 -j SSH_RULES
# 6. スプーフィング対策
iptables -A INPUT -s 127.0.0.0/8 ! -i lo -j DROP
iptables -A INPUT -s 0.0.0.0/8 -j DROP
iptables -A INPUT -s 169.254.0.0/16 -j DROP
iptables -A INPUT -s 224.0.0.0/4 -j DROP
iptables -A INPUT -s 240.0.0.0/5 -j DROP
10.3 運用ベストプラクティス
- ルールのバックアップ: 変更前に必ず現在のルールをバックアップする
- リモートアクセスの保護: ファイアウォール変更時は
atコマンドでルールリセットをスケジュール - テスト手順: 変更後は必ず接続テストを実施
- 自動化: Ansible や Puppet 等の構成管理ツールでファイアウォールルールを管理する
- 監視: ファイアウォールログを SIEM に集約し、異常を検知する
# リモート作業時のセーフガード
# 5分後にルールをリセットするスケジュール
$ echo "iptables -F; iptables -P INPUT ACCEPT" | at now + 5 minutes
# ファイアウォールルールの変更を実施
$ sudo iptables -P INPUT DROP
# ...ルール追加...
# 接続が維持されていることを確認後、atジョブをキャンセル
$ atrm <job_number>
11. 比較表
11.1 iptables vs nftables vs firewalld
| 機能 | iptables | nftables | firewalld |
|---|---|---|---|
| 対象バージョン | Linux 2.4+ | Linux 3.13+ | RHEL 7+ / Fedora 18+ |
| IPv4/IPv6 | 別コマンド | 統一 | 統一 |
| 構文 | CLI引数 | 独自言語 | CLI / GUI |
| セット機能 | ipset(別ツール) | ネイティブ | ゾーン/サービス |
| アトミック操作 | 非対応 | 対応 | 対応 |
| バックエンド | netfilter | netfilter | iptables/nftables |
| パフォーマンス | 線形評価 | 最適化あり | バックエンド依存 |
| 学習曲線 | 中程度 | やや高い | 低い |
| 柔軟性 | 高い | 非常に高い | 中程度 |
| 推奨用途 | レガシー環境 | 新規構築 | RHEL系サーバー |
11.2 DROP vs REJECT
| 特性 | DROP | REJECT |
|---|---|---|
| 応答 | なし(無言で破棄) | ICMPエラーまたはTCP RST |
| クライアント挙動 | タイムアウトまで待機 | 即座にエラー |
| ポートスキャン対策 | filtered として表示 | closed として表示 |
| 帯域消費 | 低い | 応答分の帯域が必要 |
| 推奨場面 | 外部向け | 内部ネットワーク |
12. 参考文献
- Netfilter プロジェクト公式: https://www.netfilter.org/
- nftables wiki: https://wiki.nftables.org/
- firewalld 公式ドキュメント: https://firewalld.org/documentation/
- iptables man page:
man iptables,man iptables-extensions - nftables man page:
man nft - Red Hat ファイアウォールガイド: https://access.redhat.com/documentation/
- Arch Linux Wiki - nftables: https://wiki.archlinux.org/title/Nftables
- Debian Wiki - nftables: https://wiki.debian.org/nftables
- CIS Benchmarks: https://www.cisecurity.org/cis-benchmarks
- NIST SP 800-41 Rev.1 - ファイアウォールとファイアウォールポリシーに関するガイドライン
本ドキュメントは 2026年4月時点の情報に基づいて作成されています。