Ubuntu 14.04でIptablesを使用して基本的なファイアウォールテンプレートを実装する方法

前書き

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

このガイドでは、より複雑なルールセットの基礎となるファイアウォールを構築します。 このファイアウォールは、合理的なデフォルトを提供し、簡単に拡張できるフレームワークを確立することに主に焦点を合わせます。 これをUbuntu 14.04サーバーで実演します。

前提条件

開始する前に、実装するファイアウォールポリシーの基本的な考え方が必要です。 this guideをたどると、考えるべきいくつかのことをよりよく理解できます。

従うには、Ubuntu 14.04サーバーにアクセスする必要があります。 このガイドでは、sudo権限で構成されたroot以外のユーザーを使用します。 このタイプのユーザーを構成する方法は、Ubuntu 14.04 initial server setup guideで学ぶことができます。

終了したら、以下に進みます。

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

まだインストールしていない場合は、開始するために、iptables-persistentパッケージをインストールする必要があります。 これにより、ルールセットを保存し、ブート時にそれらを自動的に適用できます。

sudo apt-get update
sudo apt-get install iptables-persistent

インストール中に、現在のルールを保存するかどうかを尋ねられます。 ここで「はい」と言います。 生成されたルールファイルを一時的に編集します。

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

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

このガイドは、サーバーでIPv6をアクティブに使用しているnotであることを前提としています。 サービスがIPv6を利用しない場合、この記事で行うように、アクセスを完全にブロックする方が安全です。

基本的なファイアウォールポリシーの実装(簡単な方法)

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

ファイアウォールポリシーとフレームワークを実装するために、/etc/iptables/rules.v4ファイルと/etc/iptables/rules.v6ファイルを編集します。 sudo特権を使用して、テキストエディタでrules.v4ファイルを開きます。

sudo nano /etc/iptables/rules.v4

内部には、次のようなファイルが表示されます。

/etc/iptables/rules.v4

# Generated by iptables-save v1.4.21 on Tue Jul 28 13:29:56 2015
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
# Completed on Tue Jul 28 13:29:56 2015

内容を次のものに置き換えます。

/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

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

このコマンドを入力して、ファイルの構文エラーをテストできます。 続行する前に、これにより明らかになる構文エラーを修正します。

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

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

sudo nano /etc/iptables/rules.v6

ファイルの内容を以下の構成に置き換えることにより、すべての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

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

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

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

両方のルールファイルで構文エラーが報告されない場合は、次のように入力してルールを適用できます。

sudo service iptables-persistent reload

これにより、ファイルに記載されているポリシーがすぐに実装されます。 これは、現在使用されているiptablesルールを一覧表示することで確認できます。

sudo iptables -S
sudo ip6tables -S

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

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

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

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

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

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

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

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

(オプション)ネームサーバーの更新

すべてのIPv6トラフィックをブロックすると、サーバーがインターネット上の問題を解決する方法に干渉する可能性があります。 たとえば、これはAPTの使用方法に影響を与える可能性があります。

apt-get updateを実行しようとしたときに次のようなエラーが発生した場合:

エラー

Err http://security.ubuntu.com trusty-security InRelease

Err http://security.ubuntu.com trusty-security Release.gpg
  Could not resolve 'security.ubuntu.com'

. . .

APTを再び機能させるには、このセクションに従ってください。

最初に、ネームサーバーを外部ネームサーバーに設定します。 この例では、Googleのネームサーバーを使用しています。 編集のために/etc/network/interfacesを開きます。

sudo nano /etc/network/interfaces

次のようにdns-nameservers行を更新します。

/etc/network/interfaces

. . .
iface eth0 inet6 static
        address 2604:A880:0800:0010:0000:0000:00B2:0001
        netmask 64
        gateway 2604:A880:0800:0010:0000:0000:0000:0001
        autoconf 0
        dns-nameservers 8.8.8.8 8.8.4.4

ネットワーク設定を更新します。

sudo ifdown eth0 && sudo ifup eth0

予想される出力は次のとおりです。

出力

RTNETLINK answers: No such process
Waiting for DAD... Done

次に、使用可能な場合にIPv4を強制する新しいファイアウォールルールを作成します。 この新しいファイルを作成します。

sudo nano /etc/apt/apt.conf.d/99force-ipv4

この単一行をファイルに追加します。

/etc/apt/apt.conf.d/99force-ipv4

Acquire::ForceIPv4 "true";

ファイルを保存して閉じます。 これで、APTを使用できるはずです。

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

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

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

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

sudo service iptables-persistent flush

次のように入力して、ルールがリセットされたことを確認できます。

sudo iptables -S

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

output-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

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

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

sudo iptables -N UDP
sudo iptables -N TCP
sudo iptables -N ICMP

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

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

TCPサービスを追加したい場合は、ポート番号を置き換えてコマンドを繰り返すことにより、これを行うことができます。

汎用の受け入れルールと拒否ルールを作成する

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

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

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

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

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

sudo iptables -A INPUT -i lo -j ACCEPT

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

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

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

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

INPUTチェーンのトラフィックを適切なプロトコル固有のチェーンに転送する必要があります。 プロトコルタイプを照合して、正しいチェーンに送信できます。 また、パケットが新しい接続を表していることを確認します(確立された接続または関連する接続は、すでに早く処理されている必要があります)。 TCPパケットの場合、TCP接続を開始するための唯一の有効なタイプであるSYNパケットであるという追加の要件を追加します。

sudo iptables -A INPUT -p udp -m conntrack --ctstate NEW -j UDP
sudo iptables -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
sudo iptables -A INPUT -p icmp -m conntrack --ctstate NEW -j ICMP

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

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

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

閉じたUDPポートに到達しようとすると、ICMPの「ポート到達不能」メッセージが表示されます。 次のように入力して、これを模倣できます。

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

閉じたポートでTCP接続を確立しようとすると、TCP RST応答が返されます。

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

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

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

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

追加した最後の3つのルールは、INPUTチェーン内の残りのすべてのトラフィックを処理する必要があります。 ただし、予防策として、デフォルトのポリシーをDROPに設定する必要があります。 このサーバーが他のマシンへのルーターとして構成されていない場合は、FORWARDチェーンにもこのポリシーを設定する必要があります。

sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP

警告

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

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

sudo ip6tables -P INPUT DROP
sudo ip6tables -P FORWARD DROP
sudo ip6tables -P OUTPUT DROP

これにより、ルールセットをかなり厳密に複製する必要があります。

IPTablesルールの保存

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

次のように入力して、現在のルール(IPv4とIPv6の両方)を保存します。

sudo service iptables-persistent save

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

結論

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

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