6. セキュリティと NFS

ここでいくつか述べるセキュリティ上の留意点は、 あなたのサイトを完全に安全にしてくれるわけではありません。 なにものも、サイトを完全に安全にすることはできません。 この節の内容は、 NFS 絡みのセキュリティ問題に関する知識を与えてくれますが、 網羅的なガイドではありませんし、 この内容も常に変化し続けています。 もしセキュリティ関連の技やヒントをお持ちでしたら、 HOWTO の管理者に送ってください。

もしあなたのネットワークが、外部といっさいの通信を行わず (モデムもだめ)、かつ内部のマシンすべてとユーザすべてを信頼できるなら、 この節の内容はあなたの役には立ちません。 しかしこのような状況にあるネットワークはどちらかというと少数でしょうから、 NFS を設定する人には、この節を徹底的に熟読することをおすすめします。

NFS におけるアクセスには 2 つの種類 (段階) があります。 最初の段階はマウントアクセスです。 マウントアクセスは、サーバにアタッチしようとしている クライアントマシンによって行われます。 この段階でのセキュリティは /etc/exports ファイルが左右します。 このファイルは、共有ポイントへのアクセスを許可するマシンの 名前または ip アドレスをリストしたものです。 クライアントの ip アドレスがこのアクセスリストのエントリのどれかに マッチすれば、そのマシンはマウントを許されます。 これはものすごく安全、というわけではありません。 アドレスを詐称されたり乗っ取られたりすると、 マウントポイントへのアクセスを許してしまいます。 このタイプの「認証」を実世界に例えてみましょうか: 誰かが自己紹介をしてきたとして、 その人に「こんにちは、私の名前は ... です」 という名札がついていることを理由に、 その自己紹介の内容を信じるようなものです。

二番目の段階はファイルアクセスです。 これは通常のファイルシステムにもあるアクセス制御の機能であり、 NFS 独自のものではありません。 ドライブがマウントされると、 そこのファイルのユーザパーミッション・グループパーミッションが アクセス制御を決めることになります。

再び例を: bob はサーバでユーザ ID 9999 にマップされているとしましょう。 ボブはサーバでユーザのみがアクセスできるファイル (8 進数で 0600) を作ります。 そのファイルが保存されたドライブへのアクセスを、 あるクライアントが許可されました。 そのクライアントでは、ユーザ ID 9999 には mary がマップされています。 この場合、bob が自分にしかアクセスできないようにしたファイルに対して、 そのクライアントでのユーザ mary がアクセスできてしまいます。 さらに悪いことに、そのクライアントで誰かが root になってしまうと、 その誰かは su - [username] によって どんなユーザにもなれてしまうのです。 NFS は賢いとは言えません。

これは絶望的な状況というわけではありません。 このクライアントによる危険性は、 サーバにいくつかの手段を施せば軽減することができます。 それらも簡単に紹介します。

セキュリティ問題は自分には関係ない、という考えはおそらく間違いです。 Section 6.1 ではポートマッパを安全にする方法を述べ、 Section 6.2 ではサーバを、 Section 6.3 ではクライアントを安全にする方法を それぞれ説明します。 最後に Section 6.4 で、 NFS サーバ向けの正しいファイアウォール設定について 簡単に議論したいと思います。

最後にもう一つ、 nfs のデーモンとクライアントプログラムのすべてを最新にしておくことは 非常に重要です。ごく最近にアナウンスされた問題だから 自分には関係ないだろう、という考えている人は、 すでにその時点で侵入されているかもしれませんよ。

最新のセキュリティ勧告を逃さないようにするには、 bugtraq メーリングリストを購読するのが良いでしょう。 購読の方法など、bugtraq に関する各種の情報は http://www.securityfocus.com/forums/bugtraq/faq.html にあります。

また securityfocus.com の検索エンジンで NFS を検索すれば、 NFS に関連するセキュリティ報告のすべてを見ることもできます。

CERT の勧告も定期的にチェックしましょう。 www.cert.org にある CERT のウェブページをご覧になってください。

6.1. ポートマッパ

ポートマッパはどのサービスがどのポートで動作しているかの一覧を 保管します。接続してくるマシンは、 あるサービスにアクセスするにはどのポートに接続すれば良いかを、 このリストを用いて知るのです。

ポートマッパは、数年前よりはだいぶましになりましたが、 しかし現在でも多くのシステム管理者の頭痛の種です。 ポートマッパも、NFS や NIS と同じく、 信頼できるローカルエリアネットワークの外からは アクセスを許すべきではありません。 もし外部世界に晒さなければならない環境では、 常に注意して、これらのシステムを入念に監視しなければなりません。

Linux ディストリビューションは、すべて同じにはできていません。 最新に見えるディストリビューションでも、 安全でないポートマッパを採用していることがあります。 現在使っているポートマッパが安全なものかどうかを調べるには、 strings(1) を用いて、ポートマッパが /etc/hosts.deny/etc/hosts.allow といったファイルを見ているか調べることです。 ポートマッパが /sbin/portmap にあるのでしたら、次のコマンドでチェックできます:
     strings /sbin/portmap | grep hosts.  
     

安全なマシンでは、次のような内容が出力されるはずです。
   /etc/hosts.allow
   /etc/hosts.deny
   @(#) hosts_ctl.c 1.4 94/12/28 17:42:27
   @(#) hosts_access.c 1.21 97/02/12 02:13:22
  

まず /etc/hosts.deny を編集します。 次のような行を含むようにします。

   portmap: ALL
  

こうするとあらゆるアクセスを拒否します。 このクローズした状態で
   rpcinfo -p
  
を実行し、ポートマッパが実際にこのファイルを読み、 その指定に従っているかを調べてみてください。 rpcinfo は何の出力も出さないはずです (あるいはエラーメッセージを出すかもしれません)。 /etc/hosts.allow および /etc/hosts.deny の各ファイルは、保存すればすぐに反映されます。 いずれのデーモンも再起動する必要はありません。

ポートマッパをすべて閉じてしまうのは少々極端に過ぎるので、 /etc/hosts.allow を編集して 再びオープンしていきましょう。 しかしまず、このファイルに何を書くか決めなければなりません。 基本的にはこのポートマッパにアクセスしなければならない すべてのマシンをリストします。 通常の Linux システムを稼働させるにあたっては、 なんらかの理由で何かのアクセスが必要になるマシンは非常に少ないはずです。 ポートマッパが管理しているのは nfsd, mountd, ypbind/ypserv, pcnfsd など、そして ruptimerusers のような "r" 系コマンド群です。 このうちなんらかの重要性があるのは、 nfsd, mountd, ypbind/ypserv およびおそらく pcnfsd だけです。 サーバマシンにアクセスが必要なマシンには、 それを許可してあげる必要があります。 いまサーバのアドレスが 192.168.0.254 で、 サブネット 192.168.0.0 につながっているとします。 そしてそのサブネットのすべてのマシンはサーバにアクセスする必要が あるとします (これらの用語は Networking-Overview-HOWTO で説明されています。必要のある方はこちらを見てください: 日本語訳も JF にあります)。 この場合は
   portmap: 192.168.0.0/255.255.255.0
   
のような行を /etc/hosts.allow に書きます。 これは route に与えるネットワークアドレス、 および ifconfig に与えるサブネットマスクと同じです。 このマシンでのデバイス eth0 への ifconfig は次のようになっているはずです。

   ...
   eth0   Link encap:Ethernet  HWaddr 00:60:8C:96:D5:56
          inet addr:192.168.0.254  Bcast:192.168.0.255 Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:360315 errors:0 dropped:0 overruns:0
          TX packets:179274 errors:0 dropped:0 overruns:0
          Interrupt:10 Base address:0x320
   ...
   
また netstat -rn は次のようになるはずです。
   Kernel routing table
   Destination     Gateway         Genmask         Flags Metric Ref Use    Iface
   ...
   192.168.0.0     0.0.0.0         255.255.255.0   U     0      0   174412 eth0
   ...
   
(ネットワークアドレスは最初の列にあります。)

/etc/hosts.deny および /etc/hosts.allow 各ファイルについては、 それぞれ同名の man ページで説明されています。 [訳注: hosts_access(5) の場合が多いと思いますが。]

重要: これらのファイルの portmap の行に、 IP 番号以外のものを書いてはいけません。 ホスト名の名前引きは間接的にポートマッパを呼び出すことがあり、 するとまたホスト名の名前引き生じてポートマッパが呼び出され、 するとまた...

バージョン 0.2.0 以降では、nfs-utils パッケージも hosts.allowhosts.deny の各ファイルを利用します。従ってこれらのファイルには、 lockd, statd, mountd, rquotad の各エントリも書いておきましょう。

以上の作業によって、サーバはよりしっかりするはずです。 残りの問題は (ご明察!) 信頼したマシンで root が破られ (あるいはそのマシンが MS-DOS でブートされ)、 その特権でもって、 安全なポートから任意のユーザになりすましたリクエストを 送ってくるような場合です。

6.2. サーバのセキュリティ: nfsd と mountd

サーバではクライアントの root アカウントを信頼しないような設定ができます。 これには /etc/exports の指定に root_squash オプションを用います:
   /home slave1(rw,root_squash)
   

実はこれはデフォルトです。 無効にすべきやむにやまれぬ事情がない限り、 これは常に有効にしておくべきです。 無効にするには no_root_squash オプションを使います。

root_squash の状態では、クライアントで UID 0 (root のユーザ番号) のユーザがファイルにアクセス (read, write, delete) しようとすると、 サーバは UID をサーバにおける 'nobody' アカウントのものと置き換えます。 つまりサーバの root だけにアクセスや変更が許されているファイルに対して、 クライアントの root がアクセスや変更を行うことができなくなるのです。 これは良い設定ですので、 export する全てのファイルシステムに root_squash を用いるべきです。 「でもクライアントの root ユーザが su を使えば、 他のユーザになってそのユーザのファイルを変更できちゃうじゃないですか!」 とあなたはおっしゃるかもしれません。答えは、まさにその通り、 それが Unix や NFS の流儀なのです。 これにはひとつ重要な側面があります。 重要なバイナリやファイルは、すべて root の所有にすべきで、 bin などの root 以外のアカウントにすべきではありません。 なぜならクライアントの root ユーザがアクセスできないのは、 サーバの root アカウントのファイルだけだからです。 exports(5) の man ページには、 他にもいくつか squash (排他) オプションが記述されています。 これらを用いれば、好きな (あるいは嫌いな) クライアントを信頼しないように設定できます。

TCP のポート 1〜1024 は root が利用するために予約されており (従って "secure ports" と呼ばれることがあります)、 root でないユーザはこれらのポートにバインドできません。 /etc/exports のエントリに secure オプションを 追加すると、1024 以下のポートで動作するようになります。 すると悪意を持った非 root ユーザが、 偽装 NFS 通信を 1024 以上のポートを使って開くことが防げます。 このオプションはデフォルトで有効になっています。

6.3. クライアントのセキュリティ

6.3.1. nosuid マウントオプション

クライアント側では、サーバを信頼しすぎないように設定することが可能で、 これはマウント時のオプションで指定します。 例えば NFS ファイルシステムにある suid プログラムを 動作させないようにするには nosuid オプションを使います。 unix プログラムの中には (例えば passwd)、 "suid" プログラムと呼ばれるものがあります。 これはファイルを実行するユーザ id を、 そのファイルの所有者と同じにするものです。 ファイルが root の所有で、かつ suid されていると、 そのプログラムは root として動作し、 よって root にしか許されていない操作 (パスワードファイルの書き込みなど) がすべて行えてしまいます。 nosuid オプションを用いるのはよい考えですから、 NFS マウントしたディスクすべてに対し、利用を検討してください。 こうするとサーバの root ユーザが件のファイルシステムに suid-root プログラムを作り、クライアントに一般ユーザとしてログインし、 その suid-root プログラムを使ってクライアントでも root になる、 ということができなくなります。 さらに noexec オプションをつければ、 マウントしたファイルシステムでのファイルの実行を禁止することもできます。 しかしこれは nosuid に比べるとあまり実用的ではないでしょう。 ファイルシステムには少なくとも 実行すべきスクリプトやプログラムが含まれているでしょうから。

6.3.2. broken_suid マウントオプション

古いプログラム (xterm などがそうです) では、 root はどこにでも書き込み可能である、 という前提に依存していることがあります。 これは新しいカーネルと NFS マウントの下では成立しません。 このような suid 動作を行うプログラムは、 uid マッピングを行う nfs サーバでは、 uid を変更するのに利用できてしまうため、 セキュリティ上問題となります。 従って linux カーネルのデフォルトでは、 この broken_suid は無効になっています。

かいつまんで言いますと、古い linux ディストリビューションで ある種の suid プログラムを使う場合や、 なんらかの古い unix を使っている場合は、 マウントの際に mountbroken_suid オプションを指定する必要があるかもしれません。 しかし最近の unix や linux ディストリビューションの xterm のようなプログラムは、 suid を必要としない通常の実行ファイルになっていて、 setuid 動作を行うプログラムを別に呼び出すようになっています。

これらのオプションは、オプションのカラムに、 rsizewsize などといっしょにコンマで区切って書きます。

6.3.3. ポートマッパ、rpc.statd, rpc.lockd をクライアントで安全にする

nfs の現在の (2.2.18 以降) の実装では、 ファイルロッキングのすべての機能がサポートされています。 すなわちロック機能を正しく動作させるには、クライアントで rpc.statdrpc.lockd を実行する必要があります。 従って、これまで nfs のサーバで見てきた問題が、 そのままクライアントにも当てはまります。 上記のポートマッパの節をもう一度読んで、 ポートマッパを安全にするための情報を再確認してください。

6.4. NFS とファイアウォール (ipchains と iptables)

IPchains (2.2.x カーネル) と iptables (2.4.x カーネル) を用いると、高い安全性を実現できます。 誰が接続できるかの決定をデーモンに (あるいはこの場合は tcp ラッパーに) 行わせるのではなく、接続の試みをより下層で許可/拒否するのです。 この場合、接続をより早い段階で、またよりグローバルに切断でき、 あらゆる攻撃からマシンを守ることができるのです。

Linux のファイアウォールをどう設定するかは、 この文書の範囲を大きく越えています。 興味を持った読者は Firewall-HOWTO や IPCHAINS-HOWTO を見てください (日本語訳が JF にあります)。カーネル 2.4 以降のユーザは、 http://netfilter.samba.org にある netfilter/iptables ウェブページにいってみてください すでに ipchains や netfilter の動作を熟知している人には、 ファイアウォールと NFS をどう設定するかに関して、 この節の内容はいくつかのヒントを与えてくれるでしょう。

ファイアウォールの設定において従うべきルールは、 まずすべてを禁止し、少しだけ許可することです。 こうすれば意図しない許可を間違って通さずにすみます。

考慮すべきポートは以下の通り:

  1. ポートマッパは 111 (tcp と udp) です。

  2. nfsd は 2049 で、TCP のことも UDP のこともあります。 NFS over TCP はサーバではまだ実験段階なので、 サーバではほぼ UDP だけでしょうが、 クライアント側での TCP の利用はかなり安定しています。

  3. mountd, lockd, statd はあちこち動き回ります (これがそもそもポートマッパが必要な利用でした) - これが問題の原因になります。 これを扱うには基本的に 2 つのやり方があります。

    1. ほぼすべてのポートを塞いでおくが、 特定の IP については大部分のポートを明示的に開ける。

    2. これらのユーティリティの最近のバージョンでは、 "-p" オプションによって特定のポートを利用できるようになっています。 自分の使っているバージョンがサポートしているかどうかは man ページを見てください。こうすれば、 NFS クライアントマシンからのこれらのポートへのアクセスのみを許し、 ローカルネットワークからでも他のすべてのポートは塞ぐようにできます。

前者の方を採用する場合は、IPCHAINS では次のようになります。
  ipchains -A input -f -j ACCEPT
  ipchains -A input -s trusted.net.here/trusted.netmask -d host.ip/255.255.255.255 -j ACCEPT
  ipchains -A input -s 0/0 -d 0/0 -p 6 -j DENY -y -l
  ipchains -A input -s 0/0 -d 0/0 -p 17 -j DENY -l
   

同じ設定を iptables (2.4 でのファイアウォールツール) で行うと次のようになります。
 
  iptables -A INPUT -f -j ACCEPT
  iptables -A INPUT -s trusted.net.here/trusted.netmask -d \
      host.ip/255.255.255.255 -j ACCEPT
  iptables -A INPUT -s 0/0 -d 0/0 -p 6 -j DENY --syn --log-level 5
  iptables -A INPUT -s 0/0 -d 0/0 -p 17 -j DENY --log-level 5
  

最初の行はすべてのパケットフラグメントを許可する (ただし最初のパケットフラグメントを除く。 これは通常のパケットと同じに扱われます) ことを示します。 理論的には再構成されるまでパケットが通ることはなく、 また最初のフラグメントが通るまで再構成は行われません。 もちろんパケットフラグメントを用いて、 マシンの負荷をあげるような攻撃は可能になってしまいます。 しかし NFS はフラグメントを通さないと正しく動作しません。 詳細は Section 7 を見てください。

他の 3 行は、ローカルネットワークは信頼するが、 それ以外はすべて拒否して記録するようにしています。 これは木目の細かなすばらしい設定というわけではないですが、 より詳細なルール設定はここでの議論の範囲を越えています。

より疑い深く、厳格なルールを設定をしたい人向けに、 いくつか情報源を示しておきます。 statd, rquotad, mountd, lockd が移動するごとに (あり得ることです) ファイアウォールのルールを再設定することを決めた場合は、 nfs クライアントからのフラグメントを通すように サーバを設定する必要があります。 さもないと、フラグメントの拒否に関して、 非常に不思議な報告をカーネルから受けることになります。 このメッセージでは、クライアントのポート 65535 から サーバのポート 65535 へのパケットが拒否された、と言うはずです。 フラグメントを許可すればこの問題は解決できます。

6.5. まとめ

hosts.allow, hosts.deny, root_squash, nosuid, 特権ポートの機能などをポートマッパや nfs ソフトウェアに用いれば、 現在 nfs で知られている多くのバグを避けることができ、 少なくともおおむね安全になったと考えることができるでしょう。 しかしそれでも結局のところ、侵入者がネットワークにアクセスできてしまえば、 怪しいコマンドを .forward に書いたり、 /home/var/mail が NFS エクスポートされていればメールを読んだりできてしまいます。 また同じ理由から、PGP の秘密鍵は nfs 上に置いてはなりません。 少なくとも危険性があることは知っておくべきです。 いまや知ったわけですけど。

NFS とポートマッパは複雑なシステムになっているので、 新しいバグが、基本的な設計にせよ我々の用いている実装にせよ、 将来見つからないとは思えません。 いまでも新しい穴がわかっていて、 誰かがそれを悪用しているかもしれません。 でもそれが人生というものです。