序章

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

このガイドでは、より複雑なルールセットの基礎となるファイアウォールを構築します。 このファイアウォールは、主に合理的なデフォルトを提供し、簡単な拡張性を促進するフレームワークを確立することに重点を置いています。 これをUbuntu14.04サーバーでデモンストレーションします。

前提条件

始める前に、実装するファイアウォールポリシーの基本的な考え方を理解しておく必要があります。 このガイドに従って、考えるべきいくつかのことをよりよく理解することができます。

フォローするには、Ubuntu14.04サーバーにアクセスできる必要があります。 このガイドでは、sudo権限で構成されたroot以外のユーザーを使用します。 このタイプのユーザーを構成する方法については、Ubuntu14.04初期サーバーセットアップガイドを参照してください。

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

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

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

  1. sudo apt-get update
  2. sudo apt-get install iptables-persistent

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

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

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

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

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

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

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

  1. 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

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

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

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

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

  1. 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

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

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

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

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

  1. sudo service iptables-persistent reload

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

  1. sudo iptables -S
  2. 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を開いて編集します。

  1. 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

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

  1. sudo ifdown eth0 && sudo ifup eth0

期待される出力は次のとおりです。

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

次に、新しいファイアウォールルールを作成して、IPv4が利用可能になったときにそれを強制します。 この新しいファイルを作成します。

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

次の1行をファイルに追加します。

/etc/apt/apt.conf.d/99force-ipv4
Acquire::ForceIPv4 "true";

ファイルを保存して閉じます。 これで、APTを使用できるようになります。

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

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

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

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

  1. sudo service iptables-persistent flush

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

  1. sudo iptables -S

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

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

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

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

  1. sudo iptables -N UDP
  2. sudo iptables -N TCP
  3. 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チェーンのトラフィックを適切なプロトコル固有のチェーンに転送する必要があります。 プロトコルタイプを照合して、正しいチェーンに送信できます。 また、パケットが新しい接続を表していることを確認します(確立された接続または関連する接続はすべて以前に処理されている必要があります)。 TCPパケットの場合、パケットがSYNパケットであるという追加要件を追加します。これは、TCP接続を開始するための唯一の有効なタイプです。

  1. sudo iptables -A INPUT -p udp -m conntrack --ctstate NEW -j UDP
  2. sudo iptables -A INPUT -p tcp --syn -m conntrack --ctstate NEW -j TCP
  3. 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に設定する必要があります。 このサーバーが他のマシンへのルーターとして構成されていない場合は、FORWARDチェーンにもこのポリシーを設定する必要があります。

  1. sudo iptables -P INPUT DROP
  2. sudo iptables -P FORWARD DROP

警告

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

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

  1. sudo ip6tables -P INPUT DROP
  2. sudo ip6tables -P FORWARD DROP
  3. sudo ip6tables -P OUTPUT DROP

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

IPTablesルールの保存

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

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

  1. sudo service iptables-persistent save

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

結論

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

このガイドで確立されたフレームワークにより、簡単に調整を行うことができ、既存のポリシーを明確にするのに役立ちます。 他のガイドをチェックして、人気のあるサービスを使用してファイアウォールポリシーを構築する方法を確認してください。