-
entry000894
comments
ぼくの自宅環境では DHCP サーバーが稼動しているので、ハブに PC をつなぐだけでインターネット環境に出られるようになっています。 これまでは既存の自宅 LAN に直接参加させていたのですが、セキュリティ上はあまり好ましくない状況です。 そこで今回、dhcp による IP アドレスのアサインと iptables によるフィルタリングを組み合わせることで、登録されていない PC は仮想的に分離された別の LAN に参加させるように設定します。
今回の設定で実現できる動作今回の設定では以下のような振る舞いを実現します。
PC をハブにつなぐと、自動的に IP アドレスなどの情報が設定されます。 この情報を用いることでインターネットへの NAPT 経由のアクセスが可能となります。 さらに、登録済みの PC の場合は自宅 LAN 内のサービスを利用できます。 しかし、登録していない PC からは自宅 LAN へは接続できません。基本アイディアはシンプルで、同一の Ethernet セグメントに異なる IP サブネットワークを重畳させることで仮想的な分離を実現します。 一方のサブネットワークは通常の LAN で、比較的自由に振舞うことのできる環境です。 LAN 内のサーバーが提供する各種サービスはすべて利用可能です。 もう一方のサブネットワークは制限された LAN で、インターネットに出ることのみに行動を制限します。 LAN 内のサービスは(DHCP / DNS など必須のもの以外は)利用できません。 互いのサブネットワーク間の IP による通信はすべて禁止とします。
同一の Ethernet に二つの IP サブネットワークが載っている状態は慣れないと直感的には理解しにくいかもしれませんが、Ethernet と TCP/IP のプロトコル・スタックはこういったことができるような設計になっています(詳細はここでは取り上げませんが、ARP が今回の構成のキモとなります)。 しかし、ある程度ネットワークの知識のある方なら思い当たるでしょうが、この構成にはいくらでも抜け穴があります。 制限された LAN の中でも、もう一方の LAN で使用している IP アドレスを覗き読むことが可能です。 IP アドレスがわかれば、DHCP に頼らずに直接 NIC にアドレスを指定でき、もう一方の LAN に入ってゆけます。 インターネット層よりも上での分離なので、これは仕方のないことです。 完全な分離を目指すのであれば他の方法を検討すべきでしょう。 ここではあくまでも、自宅 LAN へ参加する障壁を高くすることでリスクを少しでもおさえることを目的とします。
MTEntryMore必要なツール / コマンド / 機能Linux Kernel 2.6 の機能を使用しますので、相応のディストリビューションを用意します。 その他に以下のツール / 機能も使用します。
- IP alias
- IP alias を使用すると、ひとつの NIC に複数の IP アドレスを割り当てることができます。Linux Kernel の機能で、ifconfig コマンドで設定することが可能です。
- dhcpd
- ネットワーク関連の設定を自動化するデーモンです。登録の有無を判断してクライアントPCを適切なサブネットワークに所属させるために使用します。
- iptables
- Linux の IP パケットフィルタリング・ルールの制御を行うコマンドです。各サブネットワークに適切なサービスのみを提供し、サブネットワーク間の通信を制御するために使用します。
これらを組み合わせることで、LAN の仮想的な分離を実現します。 Kernel コンパイルのコンフィギュレーションによっては iptables / IP alias が機能しないかもしれません。 その場合は insmod でモジュールを読み込むか、設定を変更して Linux Kernel の再構築を行ってください。
それぞれのコマンドについてのリファレンスをご紹介します。
- Setting up IP Aliasing on A Linux Machine Mini-HOWTO - JF
- Manpage of dhcpd-options - JM
- Manpage of IPTABLES - JM
iptables については、コマンドそのものよりも Linux の IP パケットフィルタリングについてきちんと理解しておくことが非常に重要です。 その目的には、以下のドキュメントは非常に役立ちます。 TCP/IP の基礎のお勉強にもなりますのでざっと目を通してください。。
- Iptables Tutorial 1.2.0
- Iptablesチュートリアル 1.2.0(Tatsuya Nonogaki 氏による日本語訳)
想定する構成以降の説明では各ネットワークを以下のように想定します。
目的 Subnet NIC GLOBAL 0.0.0.0/0 ppp0 自宅 LAN 172.16.100.0/24 eth0 制限された LAN 172.16.200.0/24 eth0:0 NIC に複数の IP アドレスを割り当てる設定ファイル /etc/sysconfig/network-scripts/ifcfg-eth0:0 を以下の内容で作成します。 ifcfg-eth0 を雛形にすると良いでしょう。
DEVICE=eth0:0 BOOTPROTO=static BROADCAST=172.16.200.255 IPADDR=172.16.200.254 NETMASK=255.255.255.0 NETWORK=172.16.200.0 ONBOOT=yes TYPE=Ethernet続いて eth0:0 を有効化します。
# ifup eth0\:0ifconfig などで 172.16.200.254 のアドレスを持つ NIC を確認してください。
dhcpd の設定多くの Linux で採用されている ISC の dhcpd は、設定ファイルにマッピング情報をもつことで、NIC の MAC アドレスから割り当てる IP アドレスを判断することが出来ます。 設定ファイルにマッピング情報が登録されていない PC (正確には NIC)は “unknown-clients” として扱われます。 この機能を利用すると、登録済みの PC と未登録の PC とで割り当てる IP アドレスを切り分けることが可能になります。 設定ファイルは /etc/dhcpd.conf となります。 まず、登録済み PC に対する設定を行いましょう。
pool { deny unknown-clients; range 172.16.100.1 172.16.100.253; option subnet-mask 255.255.255.0; option broadcast-address 172.16.100.255; option routers 172.16.100.254; option domain-name-servers 172.16.100.254; default-lease-time 86400; max-lease-time 2592000; host registered01 { hardware ethernet 00:11:22:33:44:55; fixed-address 172.16.100.1; } }1行目で未登録 PC への IP アドレス割り当てを拒否しています。 以降は 172.16.100.0/24 のサブネットワークの設定です。 最後に記述のある “host ~” の部分が PC の登録となります。 NIC の MAC アドレスと割り当てる IP アドレスの対が記述されていますね。
続いて、未登録 PC に対する設定です。
pool { allow unknown-clients; range 172.16.200.1 172.16.200.127; option subnet-mask 255.255.255.0; option broadcast-address 172.16.200.255; option routers 172.16.200.254; option domain-name-servers 172.16.200.254; default-lease-time 300 ; max-lease-time 300 ; }こちらは未登録 PC の接続を許可しています。 未登録のものはこちら側の pool から IP アドレスを割り当てられるので、172.16.200.0/24 を使用することになります。
以上の設定を含む dhcpd.conf 全体のサンプルは以下となります。
この設定により、登録済みの PC と未登録の PC は分離されたサブネットワークに所属することになります。
iptables の設定続いて、サブネットワークごとに提供サービスを切り分ける構成を iptables によるフィルタリングで実現します。 最初に各ネットワークに便宜上の名前をつけます。
名前 NIC Subnet 概要 GLOBAL ppp0 0.0.0.0/0 いわゆるインターネット。 KNOWN eth0 172.16.100.0/24 登録済み PC が参加するネットワーク。GLOBAL への NAPT および LAN 内のサービスを全て利用可能。 UNKNOWN eth0:0 172.16.200.0/24 未登録 PC が参加するネットワーク。GLOBAL への NAPT は制限なく利用できるが、LAN 内のサービスは一部を除いて利用不可。 繰り返しになりますが、iptables を利用する前に以下にざっと目を通しておいてください。 TCP/IP プロトコル・スイートの基本的な動作と、Linux の IP パケット・フィルタリングの動作原理を理解していないと iptables を使いこなすのは難しいでしょう。
- Iptables Tutorial 1.2.0
- Iptablesチュートリアル 1.2.0(Tatsuya Nonogaki 氏による日本語訳)
特に“Chapter 6. Traversing of tables and chains(日本語訳:テーブルとチェーンの道のり)”が重要です。 3つのテーブルと、それぞれのテーブルに属するビルトイン・チェーンの役割分担はしっかりと理解してください。 ipchains 時代を知る人が陥りがちなのが、転送パケットには INPUT / OUTPUT が適用されない点。 転送パケットは PREROUTING / FORWARD / POSTROUTING のみが適用され、INPUT / OUTPUT が適用されるのは当該 Linux Box 自身が発信元 / 送信先であるパケットのみです。 ipchains は転送パケットを INPUT => FORWARD => OUTPUT と処理していたため、これを想定すると iptables では痛い目を見ます。 ご注意ください。
これ以降、これら 3 つのテーブル、5 種類のビルトイン・チェーンに対して、ユーザー定義チェーンとルールを追加することで、様々なフィルタリングを実現します。
iptables 実行時の注意iptables を実行する際はスクリプトなどで一括設定を行いましょう。 実環境にて手作業でチクチクと設定変更を行うと、その過程で一時的にセキュリティが低下するタイミングが発生する恐れがあります。 さらに、スクリプト言えども0秒で設定が完了するわけではありません。 スクリプトの最初に以下の3行を入れておきましょう。
iptables -I INPUT -j DROP iptables -I OUTPUT -j DROP iptables -I FORWARD -j DROP
これは、パケットの出入りと転送全てを停止します。 そして最後に以下の3行で停止状態を解除します。
iptables -D INPUT -j DROP iptables -D OUTPUT -j DROP iptables -D FORWARD -j DROP
停止している間に届いたパケットはすべて破棄されます。 TCP であれば再送機能でリカバリされるはずですが、UDP などの場合の挙動はアプリケーション依存です。 ご注意ください。
ネットワークごとの処理振り分け一つの Linux Box は1セットの IP table を持ちます。 “NIC ごとに1セット”ではなく Linux Box につき1セットです。 つまり、GLOBAL から入ってくるパケットも KNOWN から入ってくるパケットもどちらも同一の INPUT チェーンで処理されます。 出てゆくパケットも同様です。 そこで、ルールの設定をシンプルにするために INPUT と OUTPUT の最初の時点で GLOBAL / KNOWN / UNKNOWN を別チェーンに振り分けてしまいます。
最初に、ユーザー定義チェーンを追加します。
iptables -N INPUT_GLOBAL iptables -N INPUT_KNOWN iptables -N INPUT_UNKNOWN iptables -N OUTPUT_GLOBAL iptables -N OUTPUT_KNOWN iptables -N OUTPUT_UNKNOWN
続いて、INPUT 側のパケットを振り分けます。 ここで注意したいのですが、iptables は “eth0” と “eth0:0” を区別できません。 そこで、KNOWN と UNKNOWN の識別にはネットワーク・アドレスを使用します。
iptable -P INPUT DROP iptable -A INPUT -i lo -j ACCEPT iptable -A INPUT -i ppp0 -j INPUT_GLOBAL iptable -A INPUT -s 172.16.100.0/24 -j INPUT_KNOWN iptable -A INPUT -s 172.16.200.0/24 -j INPUT_UNKNOWN
OUTPUT についても同様です。
iptables -P OUTPUT DROP iptables -A OUTPUT -o lo -j ACCEPT iptables -A OUTPUT -o ppp0 -j OUTPUT_GLOBAL iptables -A OUTPUT -d 172.16.100.0/24 -j OUTPUT_KNOWN iptables -A OUTPUT -d 172.16.200.0/24 -j OUTPUT_UNKNOWN
これ以降、各ネットワーク・インタフェースを出入りするパケットは、それぞれ対応する INPUT_* チェーンと OUTPUT_* チェーンでフィルタリングします。
サービスの提供 / 利用ネットワーク・インタフェースごとのフィルタリング・ルールを考えるにあたって、サービスの提供と利用の概念が重要になります。 提供しているサービスは新規通信の開始要求を受け入れなければなりませんが、利用しているだけのものはこちら主導で通信を開始するのみです。 つまり、INPUT_* については以下のようにフィルタリング処理をおこなう必要があります。
- 提供サービスの incoming はすべて受け入れる
- 通信開始要求はすべて落とす
- 利用サービスの incoming はすべて受け入れる
2 で通信開始要求を落としているので、3 にたどり着くものはすべて自分が開始した通信の応答パケットです。 逆に OUTPUT_* は以下のように設定します。
- 利用サービスの outgoing はすべて受け入れる
- 通信開始要求はすべて落とす
- 提供サービスの outgoing はすべて受け入れる
INPUT_* とは「利用 / 提供」が逆になっています。 このあたりは直感的には理解しにくいかもしれないので、具体的な通信開始時のパケットのやりとりを想像してみてください。
ここまで漠然と「通信開始要求」と書きましたが、TCP の SYN だけを指すわけではありません。 UDP や ICMP でもまとまりとして意味のある一連の通信が発生します。 その一連のパケットの一番最初のものを「通信開始要求」と呼んでいます。 iptable ではステートフル・パケット・フィルタリングが可能なので、通信開始要求に該当するパケットにマッチするルールを記述可能です。
例として、GLOBAL のネットワークに HTTP (tcp/80) / HTTPS (tcp/443) を提供し、DNS (udp/53) を利用する場合のフィルタリング・ルールを挙げます。
iptables -A INPUT_GLOBAL -p tcp --dport 80 -j ACCEPT iptables -A INPUT_GLOBAL -p tcp --dport 443 -j ACCEPT iptables -A INPUT_GLOBAL -m conntrack \ ! --ctstate ESTABLISHED -j DROP iptables -A INPUT_GLOBAL -p udp --dport 53 -j ACCEPT iptables -A OUTPUT_GLOBAL -p udp --sport 53 -j ACCEPT iptables -A OUTPUT_GLOBAL -m conntrack ! --ctstate ESTABLISHED -j DROP iptables -A OUTPUT_GLOBAL -p tcp --sport 80 -j ACCEPT iptables -A OUTPUT_GLOBAL -p tcp --sport 443 -j ACCEPT
FORWARD の設定FORWARD は以下のようなポリシーを設定します。
- TCP のみ許可し、UDP / ICMP は FORWARD しない
- KNOWN=>GLOBAL / UNKNOWN=>GLOBAL の通信は特に制限しない
- GLOBAL=>KNOWN / GLOBAL=>UNKNOWN の通信開始要求はすべて落とす
- KNOWN<=>UNKNOWN の通信はすべて落とす
以上をフィルタリング・ルールに翻訳すると以下のようになります。
iptables -A FORWARD ! -p tcp -j DROP iptables -A FORWARD -s 172.16.100.0/24 -o ppp0 -j ACCEPT iptables -A FORWARD -s 172.16.200.0/24 -o ppp0 -j ACCEPT iptables -A FORWARD -d 172.16.100.0/24 -i ppp0 \ -m conntrack --ctstate ESTABLISHED -j ACCEPT iptables -A FORWARD -d 172.16.200.0/24 -i ppp0 \ -m conntrack --ctstate ESTABLISHED -j ACCEPT iptables -P FORWARD DROP
“-P”はデフォルト・ポリシーで、いずれにもマッチしなかった場合に適用されるターゲットを指定します。 ビルトイン・チェーンでのみ設定可能です。
IP Masquerading (NAPT) の設定今回の環境では、グローバル IP アドレスを持つのは ppp0 だけで、他はすべてプライベート・アドレスを使用しています。 これでは、Linux Box 以外はインターネットに出てゆかれないので、NAPT を適用します。 NAPT はひとつのグローバル IP アドレスを複数のマシンで共用するためのしくみで、TCP / UDP のポートごとに各マシンに割り当てることが出来ます。 Linux Kernel で実装される NAPT は “IP Masquerade” と呼ばれています。
IP Masuquerade の設定は nat テーブルの POSTROUTING チェーンにて行います。
iptables -t nat -P POSTROUTING ACCEPT iptables -t nat -A POSTROUTING -s 172.16.100.0/24 \ -o ppp0 -j MASQUERADE iptables -t nat -A POSTROUTING -s 172.16.200.0/24 \ -o ppp0 -j MASQUERADE
今回の環境は、ppp0 は動的に IP アドレスが振られるため IP Masquerade を使用しましたが、固定 IP の場合は SNAT を使用してください。ログの記録iptables ではターゲット “LOG” を利用することで syslog にログを残すことが出来ます。 特に多いのが、DROP するパケットをログに残しておきたいというニーズだと考えられます。 これを実現するためには「ログに記録して破棄」をおこなうユーザー定義チェーンを用意すると便利です。
iptables -N LOGGED_DROP iptables -A LOGGED_DROP -j LOG iptables -A LOGGED_DROP -j DROP
iptables 以外の設定iptables で設定するルール・ベースのフィルタリング以外にも、Linux Kernel のパラメータも幾つか設定したほうが良いです。
ICMP echo のブロードキャストを無視します。 ブロードキャストの ICMP echo メッセージを許可すると、ネットワーク上で有効な IP アドレスを一覧される恐れがあります。
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts他のマシンが ICMP ブロードキャスト・メッセージに不正に反応した場合でも、その旨をログには残しません(デフォルトでは warning が出力される)。 これは直接セキュリティの向上には寄与しませんが、ログが読みやすくなります。
echo 1 > /proc/sys/net/ipv4/icmp_ignore_bogus_error_responses以下は NIC ごとに設定します。 どちらも、man-in-the-middle attack を防止するための設定です。
for i in /proc/sys/net/ipv4/conf/* ; do ## Ignore Source Routed Packet echo 0 > ${i}/accept_source_route ## Ignore ICMP Redirect echo 0 > ${i}/accept_redirects doneその他の設定項目については以下などを参照ください。
IP forwarding の設定Linux のディストリビューションや設定によっては、フォワードが無効になっている場合があります。 この場合は Linux Box がルーターとして動作することができません。 以下のように IP forwarding を有効にします。
echo 1 > /proc/sys/net/ipv4/ip_forward
以上のようなことを考慮しつつ、まとめられる部分は関数にしてくくりだしたりしました。 最終的に出来上がったスクリプトは以下のようになります。 (セキュリティ上、実環境で使っているものを少し改変してあります)
今回は dhcp と組み合わせて未登録 PC を隔離するという特殊な設定でしたが、フィルタリングの基本的な考え方は再利用が可能な筈です。 特に上で紹介したチュートリアルを読んだ上で、それぞれの環境に合ったフィルタリング・ルールを考えてみてください。