iptablesを使ったIPv4/v6ファイアウォールの構築

いちごパック > サーバ > iptablesを使ったIPv4/v6ファイアウォールの構築

サーバが受ける攻撃

LinuxカーネルやLiunx上で動くサーバは、常に全世界からアクセス可能な状態にあります。 これは、常に攻撃の危機にさらされていることを意味します。 サーバは次のような攻撃を受けることを予め想定し、備えておく必要があります。
  • カーネルやサーバの不具合を狙った攻撃
  • サーバ管理者のミスや知識不足を狙った攻撃

  • Linux上で動くソフトウェアに対するアクセスを制限することで、これらの攻撃を受ける確率を減らせます。 ネットワークからのアクセスを制限するソフトウェアはファイアウォールと呼ばれています。
    サーバで動くソフトウェアは、すべての攻撃に耐えられる状態であるべきです。 サーバ攻撃への対策は、攻撃に耐えられる状態をつくることです。 ファイアウォールは攻撃を受けにくくするものですので、 次の対策とあわせて利用します。
  • 自動更新設定などを活用し、常に最新のソフトウェアに更新する
  • インストールするソフトウェアを必要最低限に絞り込む
  • 利用するソフトウェアが受ける攻撃を調べておき、攻撃に耐えられる設定をしておく

  • iptablesとは

    iptablesは、Linuxカーネルが備えるネットワーク通信機能を制御するためのソフトウェアです。 ネットワークを流れる通信データは、IPパケットと呼ばれる単位でやりとりされています。 IPパケットは、送信元や宛先を含むIPヘッダと、通信の内容であるIPデータで構成されています。 iptablesを使うと、このIPヘッダの内容を用いて通信を制御できます。 そこで、IPヘッダの内容から攻撃と判断できるIPパケットを破棄あるいは拒否することで、 iptablesはファイアウォールとして機能します。
    IPについては、IPv4/v6のしくみをご覧ください。

    iptablesによる制御方法

    Linuxカーネルの通信制御を行うコマンドとして、 IPv4に対してはiptablesを、IPv6に対してはip6tablesが用意されています。 主にiptablesを例として説明しますが、ip6tablesはiptablesと同様の方法で利用できます。
    iptablesはチェインと呼ばれる命令の配列を管理しています。 命令の配列内の各命令は、その命令が何回実行されたかを数えるカウンタを持っています。 iptablesにははじめから次のチェインが定義されています。 ユーザ定義のチェインを追加することも可能です。
    チェイン内容
    INPUTユーザが編集可能な入力パケット処理チェイン。
    OUTPUTユーザが編集可能な出力パケット処理チェイン。
    FORWARDユーザが編集可能な転送パケット処理チェイン。
    チェインには次のターゲットも指定できます。ターゲットは編集できません。
    ターゲット内容
    ACCEPTパケットを通過させる。
    DROPパケットを破棄する。
    RETURN呼び出し元のチェインに戻す。
    REJECT--reject-withで指定されたエラーパケットを返す。
    LOG/var/log/syslogにログを生成し、処理を継続する。ログ形式は後述のオプションで制御可能。

    Linuxカーネルにネットワークパケットが送られると、iptablesは次の処理を行います。
  • 外部からLinux自身へのパケットはINPUTチェインに送られます。
  • Linux自身から外部へのパケットはOUTPUTチェインに送られます。
  • 外部から別の外部へのパケットはFORWARDチェインに送られます。

  • したがって、例えばINPUTチェインとFORWARDチェインにDROPを呼びだす命令、 OUTPUTチェインにACCEPTを呼びだす命令を与えれば、 外部に送り出されるパケットのみを通過させるファイアウォールになります。 (実際のファイアウォールでは、送り出したパケットに対するリプライパケットに対してもACCEPTを呼び出します。)

    必須コマンドライン

    iptablesは、次のいずれかのコマンドラインを指定して呼び出します。
    コマンドライン内容
    -A チェイン (命令)指定されたチェインに(命令)を追加する。
    -C チェイン (命令)指定されたチェインに(命令)があれば0、なければそれ以外の値をシェルに返す。
    -D チェイン (命令)指定されたチェインにある(命令)を削除する。
    -D チェイン (命令番号)指定されたチェインで(命令番号)番目の命令を削除する。(命令番号)は1から始まる。
    -I チェイン (命令番号) (命令)指定されたチェインで(命令番号)番目の前に命令を挿入する。(命令番号)は1から始まる。
    -R チェイン (命令番号) (命令)指定されたチェインで(命令番号)番目の命令を(命令)で置き換える。
    -L [チェイン]すべてのチェインに登録された命令を表示する。チェインを指定するとそのチェインのみが対象になる。
    -S [チェイン]すべてのチェインに登録された命令をコマンドラインオプション形式で表示する。チェインを指定するとそのチェインのみが対象になる。
    -F [チェイン]すべてのチェインに登録された命令をすべて削除する。チェインを指定するとそのチェインのみが対象になる。
    -Z [チェイン]すべてのチェインに関連づけられたカウンタをゼロにリセットする。チェインを指定するとそのチェインのみが対象になる。
    -N チェインユーザ定義のチェインを新しく作成する。
    -X [チェイン]ユーザ定義のチェインをすべて削除する。チェインを指定するとそのチェインのみが対象になる。
    -P 定義済みチェイン ターゲット定義済みチェインが終わるかRETURNした場合のふるまいを指定する。

    命令

    iptablesで(命令)と書かれた部分には、次の命令を指定できます。
  • 条件の絞り込み
  • コマンドライン内容
    -4iptablesでは無視されるが、ip6tablesはエラーになる。同じコマンドラインをIPv4とIPv6で使いまわしたい場合に使える。
    -6ip6tablesでは無視されるが、iptablesはエラーになる。同じコマンドラインをIPv4とIPv6で使いまわしたい場合に使える。
    [!] -p プロトコルプロトコルにはtcp、udp、icmp、icmpv6などから1つを指定できる。指定されたプロトコルのみを命令の対象とする。!をつけた場合はそれ以外を意味する。
    [!] -s アドレス[/マスク]指定された送信元アドレスのみを命令実行の対象とする。!をつけた場合はそれ以外を意味する。
    [!] -d アドレス[/マスク]指定された宛先アドレスのみを命令実行の対象とする。!をつけた場合はそれ以外を意味する。
    -m modulenameiptables-extensions内の拡張モジュールを実行し、それが真の場合のみ命令実行の対象とする。
    [!] -i 入力インターフェース名指定されたインターフェースを介した入力パケットのみを命令実行の対象とする。
    [!] -o 出力インターフェース名指定されたインターフェースを介した出力パケットのみを命令実行の対象とする。
    [!] -fIPv4の分割パケットのみを命令実行の対象とする。
  • 命令実行時のふるまい
  • コマンドライン内容
    -j チェイン命令実行時にチェインを呼び出す。-jがなければ命令実行時であっても何もしない。
    -g チェイン現在のチェインに戻らない形で命令実行時にチェインへ飛ぶ。RETURNされたときには、現在のチェインの呼び出し元に戻る。
  • その他
  • コマンドライン内容
    -v表示メッセージを増やす。
    -n表示の際にIPアドレスから名前を取得せず、数値のまま表示する。
    -wLinuxカーネルを他のプロセスが操作している場合に、操作できるようになるまで待つ

    制限つき命令実行モジュール

    limitモジュールを-m limitにより指定すると、命令実行の条件として回数制限を導入できます。
    コマンドライン内容
    --limit rate/unit命令実行の回数が平均で指定レート未満の場合のみ、命令実行の対象とする。unitにはsecond,minute,hour,dayのいずれかを指定します。
    --limit-burst number命令実行の連続的な回数がこの値を超えない場合のみ、命令実行の対象とする。
    条件として送信元アドレスや宛先アドレスを利用したい場合は、hashimitというモジュールを使います。

    TCPのオプション

    TCPに対しては、次のオプションを指定できます。
    コマンドライン内容
    [!] --sport port[:port]指定された送信元TCPポートのみを命令実行の対象とする。連続する複数のポートも指定できます。
    [!] --dport port[:port]指定された宛先TCPポートのみを命令実行の対象とする。連続する複数のポートも指定できます。
    [!] --synTCPのSYNビットがセットされたパケットのみを命令実行の対象とする。
    [!] --tcp-flags mask valuemaskとvalueはカンマ区切りのフラグ列で与える。指定可能なフラグはSYN ACK FIN RST URG PSHの6つ、およびALLとNONE。パケットをmaskでマスクした値がvalueと一致するパケットのみを命令実行の対象とする。
    [!] -m multiport --sports port,port,...,port--sportを最大15個までまとめて指定する。port:portも指定可能で、最大15個の制限に対しては2つと数える。
    [!] -m multiport --dports port,port,...,port--dportを最大15個までまとめて指定する。port:portも指定可能で、最大15個の制限に対しては2つと数える。
    [!] -m state --state statesstatesにはカンマ区切りの接続状態を与える。指定可能な接続状態はNEW ESTABLISHED RELATED UNTRACKED INVALIDの5つ。NEWは新規接続、ESTABLISHEDは確立済みの接続、RELATEDは確立済みの接続に関連する新規接続をあらわす。

    UDPのオプション

    UDPに対しては、次のオプションを指定できます。
    コマンドライン内容
    [!] --sport port[:port]指定された送信元TCPポートのみを命令実行の対象とする。連続する複数のポートも指定できます。
    [!] --dport port[:port]指定された宛先TCPポートのみを命令実行の対象とする。連続する複数のポートも指定できます。
    [!] -m multiport --sports port,port,...,port--sportを最大15個までまとめて指定する。port:portも指定可能で、最大15個の制限に対しては2つと数える。
    [!] -m multiport --dports port,port,...,port--dportを最大15個までまとめて指定する。port:portも指定可能で、最大15個の制限に対しては2つと数える。

    ICMPv4のオプション

    ICMPv4に対しては、次のオプションを指定できます。
    コマンドライン内容
    [!] --icmp-type type[/code]指定されたICMPv4タイプおよびICMPv4コードを持つパケットのみを命令実行の対象とする。

    ICMPv6のオプション

    ICMPv6に対しては、次のオプションを指定できます。
    コマンドライン内容
    [!] --icmpv6-type type[/code]指定されたICMPv6タイプおよびICMPv6コードを持つパケットのみを命令実行の対象とする。

    ログ形式

    ターゲットとしてLOGを指定した場合は、次のログ形式を指定できます。
    コマンドライン内容
    --log-level levelemerg,alert,crit,error,warning,notice,info,debugのいずれかを指定する。
    --log-prefix prefixログに出力する固定文字列を29文字以内で指定する。
    --log-tcp-optionsログにTCPのヘッダ情報を出力する。
    --log-ip-optionsログにIPv4/IPv6のヘッダ情報を出力する。
    --log-uidログにこのログを生成させたプロセスのユーザIDを出力する。

    ファイアウォールの構築

    ファイアウォール構築の例を説明します。 サーバ機能をリスト化したうえでiptablesを実行し、最後にそれをファイルとして保存します。

    サーバ機能のリスト化

    例として、IPv4、IPv6の両方に対応したwebサーバを考えます。 webサーバはHTTPを必要とします。sshを利用する方も多いでしょう。 ルータではありませんので、FORWARDは必要としません。
    プロトコルタイプ/ポート番号方向内容
    TCPssh(10022)IN暗号化リモートアクセス用。22番ポートは攻撃されるため、別のポートを利用する。
    TCPHTTP(80)INwebサーバ用。HTTPSを使う場合は443も必要になります。
    TCPHTTP(80)OUTサーバソフトウェアのアップデート用。
    UDPDNS(53)IN+OUT名前とアドレスの相互変換にDNSを利用する。
    UDPNTP(123)IN+OUT時刻あわせにNTPを利用する。
    ICMPv61-4IN+OUTIPv6のエラー情報送受信に利用する。
    ICMPv6RS(133)OUTRA発行リクエストに利用する。
    ICMPv6RA(134)INルータ情報の取得に利用する。
    ICMPv6NS(135)IN+OUTIPv6アドレスとMACアドレスの対応付けに利用する。
    ICMPv6NA(136)IN+OUTIPv6アドレスとMACアドレスの対応付けに利用する。

    実行するコマンド

    リスト化されたルールをファイアウォールとして構築するには、次に示すコマンドを実行します。 攻撃の大半をサーバソフトウェアに到達することなく破棄できるように、必須でないと考えられるパケットは積極的に破棄します。 この例でも効果的かとはおもいますが、より強固なファイアウォールを構築したい場合は工夫してみてください。
    ssh、DNS、NTPはIPv4とIPv6のいずれか一方で実行できれば十分と考えられます。 それぞれどちらで実行するかを決めておいて、そちらの設定だけを残すと良いでしょう。 以下では、HTTP以外はすべてIPv4で実行するものとしています。 なお、以下の例ではsshを10022としていますが、より推測されにくいポートを選択すると良いでしょう。
    IPv4の例は次のようになります。この例では、ICMPv4はすべてDROPしています。
    % sudo iptables -F
    % sudo iptables -X
    % sudo iptables -Z
    % sudo iptables -A INPUT -i lo -j ACCEPT
    % sudo iptables -A OUTPUT -o lo -j ACCEPT
    % sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    % sudo iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    % sudo iptables -A INPUT -p tcp -m state --state NEW --dport 80 -j ACCEPT
    % sudo iptables -A INPUT -p tcp -m state --state NEW --dport 10022 -j ACCEPT
    % sudo iptables -A INPUT -p udp -s <DNSサーバ> --sport 53 -j ACCEPT
    % sudo iptables -A OUTPUT -p udp -d <DNSサーバ> --dport 53 -j ACCEPT
    % sudo iptables -A INPUT -p udp -s <NTPサーバ> --sport 123 -j ACCEPT
    % sudo iptables -A OUTPUT -p udp -d <NTPサーバ> --dport 123 -j ACCEPT
    % sudo iptables -A OUTPUT -p tcp --dport 80 \
        -d <Linuxアップデートサーバ> -j ACCEPT
    % sudo iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix \
        "iptables denied [in]: " --log-level 7
    % sudo iptables -A OUTPUT -m limit --limit 5/min -j LOG --log-prefix \
        "iptables denied [out]: " --log-level 7
    % sudo iptables -A INPUT -j DROP
    % sudo iptables -A OUTPUT -j DROP
    % sudo iptables -A FORWARD -j DROP
    
    IPv6の例は次のようになります。 この例では、ICMPv6の一部とHTTPのみを通過させています。 pingを通過させたい場合は、ICMPv6のINPUT 128とOUTPUT 129を許可すればpingに応答するようになります。
    % sudo ip6tables -F
    % sudo ip6tables -X
    % sudo ip6tables -Z
    % sudo ip6tables -A INPUT -i lo -j ACCEPT
    % sudo ip6tables -A OUTPUT -o lo -j ACCEPT
    % sudo ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    % sudo ip6tables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 1 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 2 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 3 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 4 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 134 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 135 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 136 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 1 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 2 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 3 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 4 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 133 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 135 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 136 -j ACCEPT
    % sudo ip6tables -A INPUT -p tcp -m state --state NEW --dport 80 -j ACCEPT
    % sudo ip6tables -A INPUT -m limit --limit 5/min -j LOG --log-prefix \
        "ip6tables denied [in]: " --log-level 7
    % sudo ip6tables -A OUTPUT -m limit --limit 5/min -j LOG --log-prefix \
        "ip6tables denied [out]: " --log-level 7
    % sudo ip6tables -A INPUT -j DROP
    % sudo ip6tables -A OUTPUT -j DROP
    % sudo ip6tables -A FORWARD -j DROP
    
    設定後は自分自身で攻撃パケットを送ってそのふるまいを確認したり、 iptables -L等で設定内容に誤りがないか確認したうえでファイルとして保存します。 Debianでは次のコマンドでファイル化できます。 netfilter-persistent がない場合は iptables -S と ip6tables -S を使うと良いでしょう。
    % sudo netfilter-persistent save
    

    iptables 各コマンドの意味

    各コマンドは次の意味を持ちます。
    % sudo iptables -F
    % sudo iptables -X
    % sudo iptables -Z
    
    すでに登録されたチェインとそのカウンタをクリアして、初期状態に戻します。
    % sudo iptables -A INPUT -i lo -j ACCEPT
    % sudo iptables -A OUTPUT -o lo -j ACCEPT
    
    ローカル通信(ループバックインターフェースを介した通信)をすべて許可します。
    % sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    % sudo iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    
    すでに確立された通信とその関連通信のパケットを許可します。 通信の種類はTCPに限定していませんが、限定してもかまいません。 これを許可しない状態でログを参照するとわかりますが、これを許可しないとTCP通信ができません。
    % sudo iptables -A INPUT -p tcp -m state --state NEW --dport 80 -j ACCEPT
    % sudo iptables -A INPUT -p tcp -m state --state NEW --dport 10022 -j ACCEPT
    
    新規の接続リクエストがHTTP(80)、ssh(10022)に送られてきた場合に、それを許可します。 接続後のINPUT、OUTPUTパケットは確立された通信として許可してありますので、 この行を入れることによりHTTPとsshの通信ができるようになります。
    % sudo iptables -A INPUT -p udp -s <DNSサーバ> --sport 53 -j ACCEPT
    % sudo iptables -A OUTPUT -p udp -d <DNSサーバ> --dport 53 -j ACCEPT
    % sudo iptables -A INPUT -p udp -s <NTPサーバ> --sport 123 -j ACCEPT
    % sudo iptables -A OUTPUT -p udp -d <NTPサーバ> --dport 123 -j ACCEPT
    
    このLinuxサーバからDNS(53)、NTP(123)にリクエストする通信と、その返信を許可します。 外部サーバへのリクエストになりますので、INPUT方向は送信元ポート、OUTPUT方向は宛先ポートを限定して許可します。 なお、UDPに接続の概念はありませんので、確立された通信を許可していてもその効果はありません。
    % sudo iptables -A OUTPUT -p tcp --dport 80 \
        -d <Linuxアップデートサーバ> -j ACCEPT
    
    アップデートサーバへのOUTPUT方向の接続を許可します。 アップデートサーバが複数ある場合は、それらすべてを許可します。
    % sudo iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix \
        "iptables denied [in]: " --log-level 7
    % sudo iptables -A OUTPUT -m limit --limit 5/min -j LOG --log-prefix \
        "iptables denied [out]: " --log-level 7
    
    INPUT、OUTPUTのそれぞれについて、破棄するパケットに関する記録を/var/log/syslogに残します。 一度に大量の攻撃を受けるとログがあふれますので、1分あたり5行の上限を設けています。 外部からの攻撃の多くはINPUTパケットとして記録されます。 OUTPUTパケットの記録も残しておくと、攻撃を受けて侵入されたがrootをとられていない状態の多くを検出できます。
    % sudo iptables -A INPUT -j DROP
    % sudo iptables -A OUTPUT -j DROP
    % sudo iptables -A FORWARD -j DROP
    
    残されたすべてのパケットを破棄します。 チェインの最後にDROPを追加する代わりに-PのターゲットをDROPとすることもできますが、 設定順序を間違えると設定中のssh接続に必要なパケットが破棄され、sshが切断されることがあります。

    ip6tables 各コマンドの意味

    IPv6の各コマンドについても説明しておきます。
    % sudo ip6tables -F
    % sudo ip6tables -X
    % sudo ip6tables -Z
    
    ここはiptablesと同じです。すでに登録されたチェインとそのカウンタをクリアして、初期状態に戻します。
    % sudo ip6tables -A INPUT -i lo -j ACCEPT
    % sudo ip6tables -A OUTPUT -o lo -j ACCEPT
    
    ここもiptablesと同じです。ローカル通信(ループバックインターフェースを介した通信)をすべて許可します。
    % sudo ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    % sudo ip6tables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    
    ここもiptablesと同じです。 確立されたTCP通信のパケットが破棄されないように、 すでに確立された通信とその関連通信のパケットを許可します。
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 1 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 2 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 3 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 4 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 134 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 135 -j ACCEPT
    % sudo ip6tables -A INPUT -p icmpv6 --icmpv6-type 136 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 1 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 2 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 3 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 4 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 133 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 135 -j ACCEPT
    % sudo ip6tables -A OUTPUT -p icmpv6 --icmpv6-type 136 -j ACCEPT
    
    IPv6はIPv4と違い、ICMPv6に大きく依存しています。 ICMPv6の推奨設定にも説明がありますが、 ICMPv6をすべて破棄すると通信ができなくなります。 例えば Type 2 (Packet Too Big) を破棄すると、 IPv6として送信して良いパケットサイズの最大値を知る方法がなくなります。 この例では、エラー通知であるType 1〜4、 ルータ情報の取得に使われるType 133、134、 MACアドレスとの対応付けに使われるType 135、136を許可しています。 IPv4のホストをIPv6に接続するTeredoはpingを必要としますので、 IPv4を持たないサーバを構築する場合はINPUT 128とOUTPUT 129もあわせて許可すると良いでしょう。
    % sudo ip6tables -A INPUT -p tcp -m state --state NEW --dport 80 -j ACCEPT
    
    HTTPへのアクセスはIPv4、IPv6の両方に対して許可します。 自分自身しか使わないアクセスはIPv4、IPv6のうち一方を許可すれば良いため、 例えばssh(10022)については、ここでは許可していません。
    % sudo ip6tables -A INPUT -m limit --limit 5/min -j LOG --log-prefix \
        "ip6tables denied [in]: " --log-level 7
    % sudo ip6tables -A OUTPUT -m limit --limit 5/min -j LOG --log-prefix \
        "ip6tables denied [out]: " --log-level 7
    
    IPv6についても、INPUTとOUTPUTの両方のログを残します。
    % sudo ip6tables -A INPUT -j DROP
    % sudo ip6tables -A OUTPUT -j DROP
    % sudo ip6tables -A FORWARD -j DROP
    
    残されたすべてのパケットを破棄します。

    OUTPUTチェインの基本設定について

    インターネット上を検索すると、OUTPUTチェインをDROPではなくACCEPTとする例が多くあります。 この設定では、例えばwebサーバやCGIの不具合を利用して通信コマンドを実行されると、 外部へ任意のリクエストを送ることができます。 したがって、例えば、迷惑メールを送ったり、他のwebサーバにアクセスするための踏み台として使われてしまいます。
    ファイアウォールは、サーバに残る不具合への攻撃を緩和する最後の砦です。 多少手間はかかりますが、必要な通信に限定して個別に許可を出したほうが良いでしょう。

    より強固なファイアウォールの構築のために

    最後に、より強固なファイアウォールを構築するための工夫をいくつか紹介します。

    異常なパケットのブロック

    詳しくはTCP/IPの説明に書きましたが、 異常なパケットが攻撃に使われることがあります。 例えば、TCPの接続リクエストでは必ず最初にSYNフラグがセットされていますが、 TCPの状態を調べるために、このフラグをセットしないパケットを故意に送りつける攻撃があります。 このようなパケットは、次の方法で破棄できます。
    % sudo iptables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
    % sudo ip6tables -A INPUT -p tcp ! --syn -m state --state NEW -j DROP
    

    送信元IPにあわせたブロック

    インターネット上でLinuxサーバを公開するものとします。 この場合、インターネット上での利用が許されないIPアドレスは破棄することができます。 これは特別なIPアドレス一覧で定められています。 これとは別に、キャリアグレードNATと呼ばれるアドレスも予約されています。 インターネット上での利用が許されないIPv4アドレスの範囲は次の通りです。
    IPv4アドレスの範囲iptablesでのアドレス指定内容
    10.0.0.0 - 10.255.255.25510.0.0.0/8プライベートアドレス
    100.64.0.0 - 100.127.255.255100.64.0.0/10キャリアグレードNATアドレス
    127.0.0.0 - 127.255.255.255127.0.0.0/8自分自身への通信(ループバック)アドレス
    169.254.0.0 - 169.254.255.255169.254.0.0/16ルータがない環境用の(リンクローカル)アドレス
    172.16.0.0 - 172.31.255.255172.16.0.0/12プライベートアドレス
    192.0.2.0 - 192.0.2.255192.0.2.0/24テスト用アドレス(インターネットでは使用禁止)
    192.168.0.0 - 192.168.255.255192.168.0.0/16プライベートアドレス
    インターネット上での利用が許されないIPv6アドレスの範囲は次の通りです。 IPv6はIPv4と違い、グローバルアドレスを持っていても必ずリンクローカルアドレスを併せ持ちます。
    ip6tablesでのアドレス指定内容
    fd00::/8プライベートアドレス
    fe80::/10リンクローカルアドレス
    例えば、IPv4のプライベートアドレスとリンクローカルアドレスから送られてきたパケットは、次の4行で破棄できます。 Linuxが接続されているネットワーク構成にあわせて、不要なパケットは破棄すると良いでしょう。
    % sudo iptables -A INPUT -s 10.0.0.0/8 -j DROP
    % sudo iptables -A INPUT -s 172.16.0.0/12 -j DROP
    % sudo iptables -A INPUT -s 192.168.0.0/16 -j DROP
    % sudo iptables -A INPUT -s 169.254.0.0/16 -j DROP
    
    IPv6のプライベートアドレスから送られてきたパケットは次の1行で破棄できます。
    % sudo ip6tables -A INPUT -s fd00::/8 -j DROP
    
    複数のイーサネットインターフェースがある場合は、次のようにインターフェースを条件指定に加えることで選択的に破棄できます。
    % sudo iptables -A INPUT -i eth0 -s 10.0.0.0/8 -j DROP
    % sudo iptables -A INPUT -i eth0 -s 172.16.0.0/12 -j DROP
    % sudo iptables -A INPUT -i eth0 -s 192.168.0.0/16 -j DROP
    % sudo iptables -A INPUT -i eth0 -s 169.254.0.0/16 -j DROP
    
    チェインは上から評価されるため、 これらの設定はloインターフェースに対するACCEPTの直後に追加すると良いでしょう。

    攻撃にあわせたファイアウォールの更新

    ここで説明したファイアウォールの例は1通りの機能を備えており、 HTTPとsshを除くほとんどの攻撃を防ぎます。 また、sshは推測されにくいTCPポートに移すという対策を行っています。
    しかしながらHTTPは素通ししており、何も対策されていません。 基本はwebサーバの設定で攻撃を防ぐことですが、 受けた攻撃にあわせてファイアウォールを更新することも有効です。
    サーバを構築した後は、ファイアウォールやサーバソフトウェア(webサーバなど)のログを時々確認すると良いでしょう。 いちごパックの攻撃例のように、どのような攻撃を受けていたかがわかります。
    例えば、webサーバがIPv4の172.16.0.0/12から攻撃を受けたログが残っていたとします。 この場合、INPUTチェインで80番ポートをACCEPTする前に送信元IPアドレスを見て破棄すれば、 同じ攻撃者から攻撃を受ける可能性を減らせます。
    % sudo iptables -A INPUT -s 172.16.0.0/12 -j DROP
    

    多数のサブネットの遮断

    複数のサブネットをまとめて遮断したい場合は、 シェルスクリプトを作成してforコマンドを使うと便利です。 例えば、次のサブネットをまとめて遮断したいとします。
    アドレスマスクビット数
    10.0.0.0/8
    100.64.0.0/10
    169.254.0.0/16
    172.16.0.0/12
    192.0.2.0/24
    192.168.0.0/16
    これらのサブネットをまとめて遮断するために、まずdropattackers.shを作りました。
    #! /bin/sh
    DROP_SOURCES="10.0.0.0/8 100.64.0.0/10 169.254.0.0/16 \
                  172.16.0.0/12 192.0.2.0,24 192.168.0.0/16"
    for drop_hosts in $DROP_SOURCES
    do
      iptables -A INPUT -s $drop_hosts -j DROP
    done
    
    あとは、INPUTチェインの設定中にこのスクリプトを実行することで、アクセスを遮断できます。
    % sudo /bin/sh dropattackers.sh
    

    ファイアウォール状態の確認

    どのチェインにどの命令が登録されているか、 いくつのパケットに対して命令を実行したかは、次のコマンドで確認できます。 オプションとして -n を与えると、アドレスは数値のままになります。
    % sudo iptables -L -v -n