序章

ファイアウォールの実装は、サーバーを保護するための重要なステップです。 その大部分は、ネットワークにトラフィック制限を適用する個々のルールとポリシーを決定することです。 iptablesのようなファイアウォールを使用すると、ルールが適用される構造的なフレームワークについて発言することもできます。

このガイドでは、より複雑なルールセットの基礎となるファイアウォールを構築する方法を学習します。 このファイアウォールは、主に合理的なデフォルトを提供し、拡張性を促進するフレームワークを確立することに重点を置いています。

前提条件

このチュートリアルを完了するには、sudo権限で構成されたroot以外のユーザーがいるUbuntu20.04サーバーにアクセスする必要があります。 このチュートリアルではファイアウォールを設定するため、手順4 を除き、Ubuntu20.04初期サーバー設定ガイドで概説されているすべての手順に従うことでこれを行うことができます。

さらに、実装するファイアウォールポリシーを確認することをお勧めします。 このガイドに従って、何を考慮すべきかをよりよく理解することができます。

永続ファイアウォールサービスのインストール

ローカルパッケージキャッシュを更新することから始めます。

  1. sudo apt update

次に、iptables-persistentパッケージをインストールします。 これにより、ルールセットを保存し、起動時に自動的に適用することができます。

  1. sudo apt install iptables-persistent

インストール中に、現在のルールを保存するかどうかを尋ねられます。netfilter-persistentコマンドを実行して、iptables永続ファイアウォールサービスを実行することに注意してください。 次に、生成されたルールファイルを編集します。

このガイドのIPv6に関する注意

始める前に、IPv4とIPv6について簡単に説明します。 iptablesコマンドは、IPv4トラフィックのみを処理します。 IPv6トラフィックの場合、ip6tablesと呼ばれる別のコンパニオンツールが使用されます。 ルールは別々のテーブルとチェーンに保存されます。 netfilter-persistentコマンドの場合、IPv4ルールは/etc/iptables/rules.v4から読み取られ、IPv6ルールは/etc/iptables/rules.v6に格納されます。

このガイドは、サーバーでIPv6を積極的に使用していないことを前提としています。 サービスがIPv6を利用していない場合は、このガイドで説明するように、アクセスを完全にブロックする方が安全です。

基本的なファイアウォールポリシーの実装(クイックウェイ)

できるだけ早く起動して実行するために、ルールファイルを直接編集し、完成したファイアウォールポリシーをコピーして貼り付ける方法を説明します。 その後、一般的な戦略と、ファイルを変更する代わりにiptablesコマンドを使用してこれらのルールを実装する方法について説明します。

ファイアウォールポリシーとフレームワークを実装するには、/etc/iptables/rules.v4ファイルと/etc/iptables/rules.v6ファイルを編集します。 お好みのテキストエディタでrules.v4ファイルを開きます。 ここでは、nanoを使用します。

  1. sudo nano /etc/iptables/rules.v4

内部には、ファイルに次の内容が含まれます。

/etc/iptables/rules.v4
# Generated by iptables-save v1.8.4 on Tue Mar  1 19:03:10 2022
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
# Completed on Tue Mar  1 19:03:10 2022

これらのコンテンツを削除し、次のように置き換えます。

/etc/iptables/rules.v4
*filter
# Allow all outgoing, but drop incoming and forwarding packets by default
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]

# Custom per-protocol chains
:UDP - [0:0]
:TCP - [0:0]
:ICMP - [0:0]

# Acceptable UDP traffic

# Acceptable TCP traffic
-A TCP -p tcp --dport 22 -j ACCEPT

# Acceptable ICMP traffic

# Boilerplate acceptance policy
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A INPUT -i lo -j ACCEPT

# Drop invalid packets
-A INPUT -m conntrack --ctstate INVALID -j DROP

# Pass traffic to protocol-specific chains
## Only allow new connections (established and related should already be handled)
## For TCP, additionally only allow new SYN packets since that is the only valid
## method for establishing a new TCP connection
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
-A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP

# Reject anything that's fallen through to this point
## Try to be protocol-specific w/ rejection message
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable

# Commit the changes
COMMIT

*raw
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT

*security
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
COMMIT

ファイルを保存して閉じます。 nanoを使用している場合は、CTRL + XYENTERの順に押すとこれを行うことができます。

次のコマンドを実行して、ファイルの構文エラーをテストできます。 次のいずれかが発生した場合は、構文エラーを修正してください。

  1. sudo iptables-restore -t /etc/iptables/rules.v4

次に、/etc/iptables/rules.v6ファイルを開いて、IPv6ルールを変更します。

  1. sudo nano /etc/iptables/rules.v6

このファイルの内容は次のとおりです。

/etc/iptables/rules.v6
# Generated by ip6tables-save v1.8.4 on Tue Mar  1 19:03:10 2022
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
# Completed on Tue Mar  1 19:03:10 2022

ファイルの内容を次の構成に置き換えることで、すべてのIPv6トラフィックをブロックできます。

/etc/iptables/rules.v6
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
COMMIT

*raw
:PREROUTING DROP [0:0]
:OUTPUT DROP [0:0]
COMMIT

*nat
:PREROUTING DROP [0:0]
:INPUT DROP [0:0]
:OUTPUT DROP [0:0]
:POSTROUTING DROP [0:0]
COMMIT

*security
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
COMMIT

*mangle
:PREROUTING DROP [0:0]
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]
:POSTROUTING DROP [0:0]
COMMIT

ファイルを保存して閉じます。

このファイルの構文エラーをテストするには、ip6tables-restoreコマンドを-tオプションとともに使用します。

  1. sudo ip6tables-restore -t /etc/iptables/rules.v6

両方のルールファイルで構文エラーが報告されていない場合は、次を実行して設定したルールを適用できます。

  1. sudo service netfilter-persistent reload
Output
* Loading netfilter rules... run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables start run-parts: executing /usr/share/netfilter-persistent/plugins.d/25-ip6tables start [ OK ]

これにより、ファイルに概説されているポリシーがすぐに実装されます。 これは、現在使用されているiptablesルールを一覧表示することで確認できます。 IPv4の最初のチェック:

  1. sudo iptables -S
Output
-P INPUT DROP -P FORWARD DROP -P OUTPUT ACCEPT -N ICMP -N TCP -N UDP -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -m conntrack --ctstate INVALID -j DROP -A INPUT -p udp -m conntrack --ctstate NEW -j UDP -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP -A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable -A INPUT -p tcp -j REJECT --reject-with tcp-reset -A INPUT -j REJECT --reject-with icmp-proto-unreachable -A TCP -p tcp -m tcp --dport 22 -j ACCEPT

次に、現在のIPv6ルールを確認します。

  1. sudo ip6tables -S
Output
-P INPUT DROP -P FORWARD DROP -P OUTPUT DROP

これらのファイアウォールルールは、起動するたびに再適用されます。 テストして、ログインできることと、他のすべてのアクセスがブロックされていることを確認してください。

一般的なファイアウォール戦略の説明

前のセクションのルールで構築された基本的なファイアウォールでは、ルールを追加または削除するために調整できる拡張可能なフレームワークを作成しました。 IPv4トラフィックの場合、主にfilterテーブル内のINPUTチェーンに関係します。 このチェーンは、サーバー宛てのすべてのパケットを処理します。 また、すべての送信トラフィックを許可し、すべてのパケット転送を拒否しました。これは、このサーバーが他のホストのルーターとして機能している場合にのみ適切です。 このガイドではパケットをフィルタリングするだけなので、他のすべてのテーブルでパケットを受け入れます。

一般に、私たちのルールは、デフォルトで着信トラフィックを拒否するファイアウォールを設定します。 次に、このポリシーから除外するサービスとトラフィックタイプの例外を作成します。

メインのINPUTチェーンには、常に同じ方法で処理されると確信しているトラフィックの一般的なルールがいくつか追加されています。 たとえば、「無効」と見なされるパケットを常に拒否し、ローカルループバックインターフェイス上のトラフィックと確立された接続に関連付けられたデータを常に許可する必要があります。

その後、使用しているプロトコルに基づいてトラフィックを照合し、プロトコル固有のチェーンにシャッフルします。 これらのプロトコル固有のチェーンは、特定のサービスのトラフィックに一致して許可するルールを保持することを目的としています。 この例では、許可するサービスはTCPチェーンのSSHのみです。 HTTP(S)サーバーなどの別のサービスを提供している場合は、ここにも例外を追加できます。 これらのチェーンは、ほとんどのカスタマイズの焦点になります。

プロトコル固有のチェーンの汎用ルールまたはサービスルールと一致しないトラフィックは、INPUTチェーンの最後のいくつかのルールによって処理されます。 ファイアウォールのデフォルトポリシーをDROPに設定しました。これにより、ルールを通過するパケットが拒否されます。 ただし、INPUTチェーンの最後にあるルールはパケットを拒否し、そのポートでサービスが実行されていない場合にサーバーがどのように応答するかを模倣したメッセージをクライアントに送信します。

IPv6トラフィックの場合、すべてのトラフィックをドロップします。 私たちのサーバーはこのプロトコルを使用していないので、トラフィックにまったく関与しないのが最も安全です。

iptablesコマンドを使用したファイアウォールの実装

作成したポリシーの背後にある一般的な考え方を理解したので、iptablesコマンドを使用してこれらのルールを作成する方法について説明します。 上記で指定したものと同じルールが作成されますが、ルールを繰り返し追加してポリシーを作成します。 iptablesは各ルールをすぐに適用するため、ルールの順序は非常に重要です(たとえば、パケットを拒否するルールは最後まで残します)。

ファイアウォールをリセットする

コマンドラインからポリシーを構築する方法を確認できるように、ファイアウォールルールをリセットすることから始めます。 次のコマンドを実行して、すべてのルールをフラッシュします。

  1. sudo service netfilter-persistent flush

次に、ルールがリセットされていることを確認します。

  1. sudo iptables -S

filterテーブルのルールがなくなり、すべてのチェーンでデフォルトのポリシーがACCEPTに設定されていることを示す出力が表示されます。

Output
-P INPUT ACCEPT -P FORWARD ACCEPT -P OUTPUT ACCEPT

プロトコル固有のチェーンの作成

次に、プロトコル固有のチェーンをすべて作成します。 これらは、公開するサービスの拒否ポリシーの例外を作成するルールを保持するために使用されます。 UDPトラフィック用に1つ作成します。

  1. sudo iptables -N UDP

次に、TCP用にもう1つ:

  1. sudo iptables -N TCP

そしてICMPのためにもう1つ:

  1. sudo iptables -N ICMP

次に、SSHトラフィックの例外を追加します。 SSHはTCPを使用するため、ポート22宛てのTCPトラフィックをTCPチェーンに受け入れるルールを追加します。

  1. sudo iptables -A TCP -p tcp --dport 22 -j ACCEPT

TCPサービスを追加する場合は、ポート番号を置き換えてコマンドを繰り返すことで、ここで追加できます。

汎用の承認および拒否ルールの作成

すべての着信トラフィックがフィルタリングを開始するINPUTチェーンでは、汎用ルールを追加する必要があります。 これらは、リスクの低いトラフィック(ローカルトラフィックと、すでにチェックした接続に関連付けられているトラフィック)を受け入れ、明らかに役に立たないトラフィック(無効なパケット)をドロップすることによってファイアウォールのベースラインを設定するいくつかの常識的なルールです。

まず、確立された接続の一部であるか、確立された接続に関連するすべてのトラフィックを受け入れるための例外を作成します。

  1. sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

このルールは、conntrack拡張機能を使用します。これにより、iptablesが、個別の無関係なパケットのストリームとしてではなく、より大きな接続の一部としてパケットを評価するために必要なコンテキストを持つように、内部追跡が提供されます。 TCPは接続ベースのプロトコルであるため、確立された接続はかなり明確に定義されています。 UDPおよびその他のコネクションレス型プロトコルの場合、確立された接続とは、応答があったトラフィックを指します(元のパケットの送信元が応答パケットの宛先になり、その逆も同様です)。 関連する接続とは、既存の接続に関連して開始された新しい接続を指します。 ここでの典型的な例は、FTPデータ転送接続です。これは、すでに確立されているFTP制御接続に関連しています。

また、ローカルループバックインターフェイスから発信されるすべてのトラフィックを許可する必要があります。 これは、サーバーによって生成され、サーバー宛てのトラフィックです。 これは、ホスト上のサービスが相互に通信するために使用します。

  1. sudo iptables -A INPUT -i lo -j ACCEPT

最後に、すべての無効なパケットを拒否します。 パケットは、いくつかの理由で無効になる可能性があります。 それらは、存在しない接続を参照する場合もあれば、存在しないインターフェイス、アドレス、またはポートを宛先とする場合もあります。あるいは、不正な形式である場合もあります。 いずれの場合も、無効なパケットを処理する適切な方法がなく、悪意のあるアクティビティを表す可能性があるため、すべての無効なパケットをドロップします。

  1. sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

プロトコル固有のチェーンへのジャンプルールの作成

これまで、INPUTチェーンでいくつかの一般的なルールを作成し、プロトコル固有のチェーン内で特定の受け入れ可能なサービスについていくつかのルールを作成しました。 ただし、現在、トラフィックはINPUTチェーンに入り、プロトコル固有のチェーンに到達する方法がありません。

次に、INPUTチェーンのトラフィックを適切なプロトコル固有のチェーンに転送する必要があります。 プロトコルタイプを照合して、正しいチェーンに送信できます。 また、パケットが新しい接続を表していることを確認してください(確立された接続または関連する接続はすべて以前に処理されている必要があります)。 UDPトラフィックから開始します。

  1. sudo iptables -A INPUT -p udp -m conntrack --ctstate NEW -j UDP

次に、TCPトラフィックに対して次のコマンドを実行します。 TCPパケットでは、パケットが SYN パケットであるという追加要件が追加されることに注意してください。これは、TCP接続を開始するための唯一の有効なタイプです。

  1. sudo iptables -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP

次に、ICMPトラフィックに対して以下を実行します。

  1. sudo iptables -A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP

残りのすべてのトラフィックを拒否する

プロトコル固有のチェーンに渡されたパケットが内のどのルールにも一致しなかった場合、制御はINPUTチェーンに戻されます。 このポイントに到達するものはすべて、ファイアウォールで許可されるべきではありません。

REJECTターゲットを使用してトラフィックを拒否します。このターゲットは、クライアントに応答メッセージを送信します。 これにより、アウトバウンドメッセージングを指定して、クライアントが通常の閉じたポートにパケットを送信しようとした場合に返される応答を模倣できるようになります。 応答は、クライアントが使用するプロトコルによって異なります。

閉じたUDPポートに到達しようとすると、「ポートに到達できません」というICMPメッセージが表示されます。 次のコマンドを実行することで、これを模倣できます。

  1. sudo iptables -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable

閉じたポートでTCP接続を確立しようとすると、TCPRST応答が発生します。

  1. sudo iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset

他のすべてのパケットについては、ICMP「プロトコル到達不能」メッセージを送信して、サーバーがそのタイプのパケットに応答しないことを示すことができます。

  1. sudo iptables -A INPUT -j REJECT --reject-with icmp-proto-unreachable

デフォルトポリシーの調整

追加した最後の3つのルールは、INPUTチェーンの残りのすべてのトラフィックを処理する必要があります。 ただし、次のように、予防策としてデフォルトのポリシーをDROPに設定する必要があります。

  1. sudo iptables -P INPUT DROP

このサーバーが他のマシンへのルーターとして構成されていない場合は、FORWARDチェーンにもこのポリシーを設定する必要があります。

  1. sudo iptables -P FORWARD DROP

警告:ポリシーをDROPに設定し、iptablessudo iptables -Fでクリアすると、現在のSSH接続が切断されます。 sudo netfilter-persistent flushでフラッシュすると、デフォルトポリシーもリセットされるため、ルールをクリアするためのより良い方法です。

すべてのトラフィックをドロップするというIPv6ポリシーに一致させるには、INPUTで始まる次のip6tablesコマンドを使用できます。

  1. sudo ip6tables -P INPUT DROP

次に、FORWARDに対して以下を実行します。

  1. sudo ip6tables -P FORWARD DROP

OUTPUTのポリシーを設定して終了します。

  1. sudo ip6tables -P OUTPUT DROP

これにより、ルールセットがかなり厳密に複製されます。

iptablesルールの保存

この時点で、ファイアウォールルールをテストし、通常のアクセスを妨げないようにしながら、遮断したいトラフィックをブロックしていることを確認する必要があります。 ルールが正しく動作していることを確認したら、ルールを保存して、起動時にシステムに自動的に適用されるようにすることができます。

次のコマンドを実行して、現在のルール(IPv4とIPv6の両方)を保存します。

  1. sudo service netfilter-persistent save

これにより、/etc/iptables/rules.v4および/etc/iptables/rules.v6ファイルがコマンドラインで作成したポリシーで上書きされます。

結論

このガイドに従うことにより、ファイアウォールルールを構成ファイルに直接貼り付けるか、コマンドラインに手動で適用して保存することにより、適切な開始ファイアウォール構成を作成できます。 利用可能にしたいサービスへのアクセスを許可するには、個々のルールを追加する必要があります。

このガイドで確立されたフレームワークにより、調整が可能になり、既存のポリシーを明確にすることができます。 いくつかの人気のあるサービスを使用してファイアウォールポリシーを構築する方法については、他のガイドをご覧ください。