iptables ルールの保存と復元

LinuxBeginner
オンラインで実践に進む

はじめに

iptables コマンドで追加したルールは、稼働中のカーネルのファイアウォールを変更します。しかし、稼働中の状態を変更しただけでは、永続的な設定にはなりません。管理者は通常、iptables-save で IPv4 ルールをシリアライズして保存し、その形式を iptables-restore で再読み込みします。Debian および Ubuntu システムでは、適切な起動時ローダーが有効になっている場合、/etc/iptables/rules.v4 が一般的な永続化ファイルとして使用されます。

この作業は、3 つの独立した層に分けて考えると分かりやすくなります。稼働中のルールセットは、現在カーネルが適用している内容です。保存済みルールファイルは、ツールやサービスによって復元されるまでは、ディスク上の単なるテキストファイルにすぎません。ベースラインのバックアップは、ロールバック用に保持しておく既知の開始状態です。いずれか 1 つの層を変更しても、残り 2 つが自動的に更新されるわけではありません。そのため、管理者は各状態の保存、復元、検証を明示的に行う必要があります。

この実験では、元のファイアウォールをロールバックポイントとして保存し、対象を限定したサービス用ルールを追加・保存します。その後、保存ファイルを適用する前に検証し、ルールセットの再読み込みをシミュレートして、最後に元のベースラインを復元します。この手順では、稼働中のルール、保存済みルールファイル、ロールバック用コピーという 3 つの重要な状態を明確に分けて扱います。

稼働中の IPv4 ファイアウォールをバックアップする

このステップでは、変更を加える前に開始時点のファイアウォール状態を取得します。ロールバック用ファイルがあれば、後続のコマンドが期待どおりに動作しなかった場合でも、既知の正常な状態へ戻せます。

プロジェクトディレクトリへ移動します。

cd /home/labex/project

ルールを変更する前に、テストサービスが利用可能であることを確認します。

curl --noproxy '*' --fail --silent http://127.0.0.1:18087/status

これは動作確認用のテストです。127.0.0.1 はループバックインターフェースを使用し、--noproxy '*' によってプロキシ用の環境変数が設定されていてもリクエストをローカルに限定します。また、--fail --silent により、余分な出力を抑えつつ信頼性のある終了ステータスを取得できます。あらかじめ動作するベースラインを確立しておくと、後でファイアウォールの挙動を解釈しやすくなります。

iptables-save を使用して、IPv4 ルールセット全体を出力します。パイプによって出力を tee に渡し、baseline.v4 に書き込みながら、通常の学習用アカウントでも実行しやすい形にしています。

sudo iptables-save | tee baseline.v4 >/dev/null

権限の境界を分けているのは意図的です。sudo iptables-save は保護されたカーネルのルールセットを読み取り、権限を持たない tee はプロジェクトディレクトリに書き込みます。最後のリダイレクトは、tee が画面にも出力する複製分を /dev/null に送るだけであり、ファイルを空にするものではありません。生成されたバックアップには、このホストで報告されたすべての IPv4 テーブルとチェーンがシリアライズされており、後で追加する 1 つのルールだけが含まれるわけではありません。

保存ファイルの先頭部分を確認します。

head -n 12 baseline.v4

正確なカウンター値は環境によって異なりますが、このファイルはテーブル単位のセクションで構成された iptables-restore 形式を使用しています。最小限の filter セクションは、次のような形式になります。

*filter
:INPUT ACCEPT ...
...
COMMIT

各セクションは *filter のようなテーブル名で始まり、チェーンとポリシーを宣言し、ルールを列挙して、COMMIT で終了します。iptables-restore は、このマーカーに到達した時点でテーブルセクションを適用します。このファイルを、単独で実行できるシェルコマンドの羅列として扱わないでください。

ファイルに filter テーブルのマーカーと commit マーカーが含まれていることを確認します。

grep -E '^\*filter$|^COMMIT$' baseline.v4

サービス用ルールを追加して保存する

このステップでは、稼働中のファイアウォールに明示的な許可ルールを追加し、結果のルールセットを標準的な IPv4 永続化パスに保存します。

INPUT の先頭に、ループバック経由の TCP 通信でポート 18087 宛ての接続を許可するルールを挿入します。インターフェースとポートを限定することで、この実験に必要な範囲だけを対象にします。

sudo iptables -I INPUT 1 -i lo -p tcp --dport 18087 -j ACCEPT

INPUT は、ホスト自身を宛先とする通信を扱います。-I INPUT 1 はルールを先頭に配置し、-i lo はループバックインターフェースに限定します。-p tcp --dport 18087 はテストサービスを指定し、-j ACCEPT は一致した通信を許可します。対象を明確に絞ったルールは、多数のサービスをまとめて許可する広範なルールよりも、監査や削除が容易です。

稼働中のルールを確認します。終了ステータスが 0 であれば、指定した一致条件とターゲットが存在します。このホストでは nftables をバックエンドとする実装が使用されているため、一致するルールが正規化された形式で表示される場合もあります。

sudo iptables -C INPUT -i lo -p tcp --dport 18087 -j ACCEPT

稼働中の IPv4 ルールセット全体を保存します。/etc/iptables への書き込みには管理者権限が必要なため、sudo tee を使用します。

sudo iptables-save | sudo tee /etc/iptables/rules.v4 >/dev/null

このコマンドは、ルール追加後の新しいスナップショットを取得します。パイプの左右で sudo が必要な理由は異なります。左側はファイアウォールの状態を読み取り、右側は root 所有の保存先ファイルを開くためです。rules.v4 を作成しただけでは、ルールが有効になったり、何らかの処理が予約されたりすることはありません。これは、復元コマンドや起動時サービスが読み込めるファイルを準備する操作です。

保存ファイル内でサービスのポートを検索します。grep の後ろにある -- はオプション解析の終了を示します。検索パターンがハイフンで始まる可能性がある場合に便利です。

sudo grep -- '--dport 18087' /etc/iptables/rules.v4

出力には、-j ACCEPT で終わる INPUT ルールが含まれているはずです。この時点で、稼働中のルールセットと保存ファイルの両方に、意図したルールが含まれています。

新しいファイルとベースラインをバイト単位で比較することは避けてください。パケットカウンターや、ほかのソフトウェアが管理するルールは正当に変化する場合があります。この実験で重要なのは、意図したルールが稼働中の状態と保存済みの成果物の両方に存在することです。

保存済みルールセットを検証して復元する

このステップでは、稼働中のルールを削除し、保存ファイルを適用せずに検証してから、実際に再読み込みします。これにより、1 回の実験セッション内で、再起動時の復元処理における重要な部分をシミュレートできます。

稼働中の INPUT チェーンからルールを削除します。

sudo iptables -D INPUT -i lo -p tcp --dport 18087 -j ACCEPT

この削除で変更されるのは、カーネルが現在使用しているルールセットだけです。/etc/iptables/rules.v4 は編集されないため、後続のコマンドで保存済みのルールを復元できます。この意図的な不一致によって、稼働中の状態と保存済みの状態の違いを確認できます。

ルールが存在しないことを確認します。このチェックは失敗することが想定されているため、|| でメッセージを表示し、処理を続行します。

sudo iptables -C INPUT -i lo -p tcp --dport 18087 -j ACCEPT 2>/dev/null || echo "Live rule is absent"

保存済みファイアウォールを適用する前に、iptables-restore --test を使用します。これによりファイル全体が解析され、構文が無効な場合はエラーが報告されますが、稼働中のルールセットは置き換えられません。

sudo iptables-restore --test < /etc/iptables/rules.v4

< 演算子によって、シェルはファイルを iptables-restore の標準入力として渡します。何も表示されずに終了すれば構文が受け入れられたという意味であり、ファイルがすでに適用されたという意味ではありません。検証は重要な安全確認です。構文が壊れていたり不完全だったりするルールセットを復元すると、接続が失われる可能性があります。

検証が成功すると、処理は何も表示せずに完了します。次に、保存済みルールを稼働中のファイアウォールへ復元します。

sudo iptables-restore < /etc/iptables/rules.v4

iptables で 1 つのルールを追加する場合とは異なり、iptables-restore はファイル内に存在するテーブルセクション全体を処理します。影響の大きい管理操作として扱い、事前に検証し、ロールバック用コピーを保持し、復元直後に重要なアクセスを確認してください。この管理された実験では、保存ファイルは同じホストから数分前に取得されています。

サービス用ルールが復元されたことを確認します。

sudo iptables -C INPUT -i lo -p tcp --dport 18087 -j ACCEPT

復元後もアプリケーションに接続できることを確認します。

curl --noproxy '*' --fail --silent http://127.0.0.1:18087/status

元のベースラインへロールバックする

このステップでは、ステップ 1 で作成したバックアップを使用して、実験で追加した変更を稼働中のファイアウォールから取り除きます。一方で、対象ルールセットを保存したファイルは検査用に残します。

適用する前にベースラインを検証します。

sudo iptables-restore --test < baseline.v4

ここでは、baseline.v4 と /etc/iptables/rules.v4 に意図的に異なる役割を持たせています。ベースラインは元の稼働状態を表し、rules.v4 はサービス用ルールを含む適用対象の状態を表します。一方のファイルを復元しても、もう一方のファイルは変更されません。

元の稼働中ルールセットを復元します。

sudo iptables-restore < baseline.v4

実験用ルールが稼働中の状態から削除されたことを確認します。

sudo iptables -C INPUT -i lo -p tcp --dport 18087 -j ACCEPT 2>/dev/null || echo "Original firewall baseline restored"

保存済みの対象ファイルは別の成果物であり、復元操作を練習したルールを引き続き保持しています。

sudo grep -- '--dport 18087' /etc/iptables/rules.v4

この確認は、状態が分離されていることを最も明確に示します。稼働中のファイアウォールはロールバックされていますが、永続化用の成果物には対象ルールが記録されたままです。管理者は後でこのファイルを再び復元したり、起動時に永続化サービスが読み込むよう設定したりできます。

最後に、元の許可的なベースラインへ戻した後も、ローカルサービスに接続できることを確認します。

curl --noproxy '*' --fail --silent http://127.0.0.1:18087/status

本番環境では、ディストリビューションの永続化ファイルを起動時に読み込むサービスを確認してください。たとえば Debian 系システムでは、iptables-persistent などのパッケージと netfilter-persistent を組み合わせて使用することが多いですが、利用可能なサービスや運用ポリシーはホストによって異なります。正しい rules.v4 ファイルを保存することでルールセットは準備できますが、起動時に自動復元されるかどうかは、ローダーがインストールされ有効化されているかによって決まります。

まとめ

稼働中の IPv4 ファイアウォールをバックアップし、対象を限定したサービス用ルールを追加・保存しました。また、iptables-restore --test でシリアライズされたルールセットを検証し、カーネルへ再読み込みした後、元のベースラインへロールバックしました。さらに、稼働中のファイアウォールと、保存用の永続化ファイルおよびロールバック用ファイルを区別しました。これは、パケットフィルタリングの状態を変更または復元する前に身につけておくべき重要な習慣です。