6. セキュリティと NFS

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

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

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

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

再び例を: bob はサーバでユーザ ID 9999 にマップされているとしましょう。 ボブはサーバでユーザのみがアクセスできるファイル を作ります (chmod 600 filename と入力するのと同じです)。 そのファイルが保存されたドライブへのアクセスを、 あるクライアントが許可されました。 そのクライアントでは、ユーザ ID 9999 には mary がマップされています。 この場合、bob が自分にしかアクセスできないようにしたファイルに対して、 そのクライアントでのユーザ mary がアクセスできてしまいます。 さらに悪いことに、そのクライアントで誰かがスーパーユーザになってしまうと、 その誰かは 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, rquotad, lockd (nlockmgr と表示されます), statd (status と表示されます) など、および ruptimerusers のような "r" 系コマンド群です。 このうちなんらかの重要性があるのは、 nfsd, mountd, ypbind/ypserv および場合によっては rquotad,lockd, statd だけでしょう。 サーバマシンにアクセスが必要なマシンには、 それを許可してあげる必要があります。 いまサーバのアドレスが 192.168.0.254 で、 サブネット 192.168.0.0 につながっているとします。 そしてそのサブネットのすべてのマシンはサーバにアクセスする必要が あるとします (これらの用語の概論としては Networking-Overview-HOWTO を見てください。 日本語訳も JF にあります)。 この場合は
   portmap: 192.168.0.0/255.255.255.0
   
のような行を /etc/hosts.allow に書きます。 ネットワークとネットマスクがはっきりしない場合は、 ifconfig コマンドを使えばネットマスクがわかり、 netstat コマンドを使えばネットワークがわかります。 例えば、このマシンでデバイス 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 の各エントリも書いておきましょう。 詳細な例は Section 3.2.2 を見てください。

以上の作業によって、サーバはよりしっかりするはずです。 残りの問題は、信頼したクライアントマシンで誰かが管理者権限を取得し、 任意の NFS リクエストを送信可能となってしまうような場合です。 次のセクションでは、この問題に関する安全対策を扱います。

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 オプションを追加すると、 クライアント側のポート 1-1024 から来た接続要求のみを 受け付けるようになります。 こうすると悪意を持った非 root ユーザが、 偽装 NFS 通信を非特権ポートを使って開くことを防げます。 このオプションはデフォルトで有効になっています。

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 の変更に利用できてしまうため、 uid マッピングを行う nfs サーバでは セキュリティ上問題となります。 従って 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 (JF の 日本語訳) や IPCHAINS-HOWTO (JF の 日本語訳) を見てください。カーネル 2.4 以降のユーザは、 http://netfilter.samba.org にある netfilter/iptables ウェブページに行ってみてください。 すでに ipchains や netfilter の動作を熟知している人には、 この節の内容は NFS デーモンをファイアウォールによってどう守るかについて、 いくつかのヒントを与えてくれるでしょう。

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

ファイアウォールよる NFS デーモンの守り方を理解するため、 各デーモンがどのポートを使うかについて見ておくことにしましょう。

デーモンは起動すると、ポートマッパに開いているポートを 割り当ててくれるよう要求します。 ポートマッパはそのデーモンのためにポートを取得し、 そのデーモンがポートを現在使っているかどうか追跡し続けます。 他のホストやプロセスがこのデーモンと通信する必要が生じたときは、 それらはポートマッパに接続に用いるポート番号を尋ねます。 よってポートはずっと浮動 (floating) 状態になります。 いろいろなポートがいろいろなタイミングで解放されるので、 ポートマッパは場合によって別々のポートを割り当てるからです。 これはファイアウォール設定にとっては厄介な問題です。 デーモンの場所がわからなければ、 どのポートにアクセスを許すべきかが正確にはわからないからです。 ほとんどの人にとっては保護された、あるいは隔離された LAN で作業していますから、これはあまり大きな問題にはならないでしょう。 しかし公開ネットワークにいる人たちにとっては、これは悪夢です。

カーネル 2.4.13 以降と nfs-utils 0.3.3 以降を組み合わせると、 このポートマッパによるポートの浮動を気にする必要はなくなります。 今では NFS に関わるすべてのデーモンは、 ポートを「固定」することができます。 デーモンのほとんどは起動時に行儀良く -p オプションを受け付けます。カーネルから起動されるデーモンは、 カーネル引数やモジュールオプションを取ります。 これらは以下で解説します。

NFS でデータ共有を行うためのデーモンのいくつかは、 すでにポートにバインドされています。 portmap は常に tcp と udp のポート 111 を使います。 nfsd は常に tcp と udp のポート 2049 を使います (しかしカーネル 2.4.17 までは NFS over TCP は実験的とみなされており、 実用のマシンでは用いられていませんでした)。

他のデーモン、 statd, mountd, lockd, rquotad は、 ポートマッパから利用できると知らされた最初のポートを利用するので、 ポートはいろいろになります。

statd を特定のポートにバインドさせるには、 -p portnum オプションを用います。 statd を特定のポートに反応させるには、さらに起動時に -o portnum オプションを用います。

mountd を特定のポートにバインドさせるには、 -p portnum オプションを用います。

例えば statd にポート 32765 をブロードキャストさせてポート 32766 で待機させ、 mountd にポート 32767 で待機させるには、次のように入力します。

# statd -p 32765 -o 32766
# mountd -p 32767

lockd は必要に応じてカーネルから起動されます。 よって lockd に特定のポートで待機・反応させるには、 カーネルオプションや (モジュールとしてビルドされた場合は) モジュールオプションを用います。

ローダブルモジュールを用いており、 これらのオプションを /etc/modules.conf ファイルで指定するには、 次のような行をこのファイルに追加します。

options lockd nlm_udpport=32768 nlm_tcpport=32768

この指定では、lockd の udp と tcp のポートを 32768 にします。

ローダブルモジュールを使わない場合や、 lockd をモジュールとしてビルドせずに カーネルに組み込んだ場合は、 選んだポートはカーネルのブート時に渡さなければなりません。

次のような感じになります。

 vmlinuz 3 root=/dev/hda1 lockd.udpport=32768 lockd.tcpport=32768

ポート番号は同じにしなくてもかまいませんが、 揃えないと不必要な混乱の原因になってしまうでしょう。

quota を使っていて、その情報を nfs 経由でも見せるために rpc.quotad を使っている場合は、 これもファイアウォールの設定時に考慮しなければなりません。 rpc.rquotad には 2 つのソースツリーがあります。 1 つは nfs-utils で管理されているツリーで、 もう 1 つは quota-utils のツリーです。 これらは同じようには動作しません。 nfs-utils のものは、 -p で指定すればデーモンをポートにバインドできます。 quota-utils のものはできません。 自分のディストリビューションがどちらを使っているかは、 ディストリビューションの文書にあたってください。

ここでの議論のために、 ネットワークと NFS サーバを守るためのファイアウォールの設定を見ていきましょう。 私たちの NFS サーバは 192.168.0.42 にあり、 クライアントは 192.168.0.45 のみとします。 上述の例のように、 statd は 入ってくるリクエストについてはポート 32765 のみにバインド、 応答するポートは 32766 を使うとします。 mountd はポート 32767 にバインドします。 lockd のモジュールパラメータは、 32768 にバインドするよう設定します。 もちろん nfsd はポート 2049 を使い、 ポートマッパはポート 111 を使います。

quota は使わないことにします。

IPCHAINS を使った、 簡単なファイアウォール設定は次のようになります。

ipchains -A input -f -j ACCEPT -s 192.168.0.45
ipchains -A input -s 192.168.0.45 -d 0/0 32765:32768 -p 6 -j ACCEPT
ipchains -A input -s 192.168.0.45 -d 0/0 32765:32768 -p 17 -j ACCEPT
ipchains -A input -s 192.168.0.45 -d 0/0 2049 -p 17 -j ACCEPT
ipchains -A input -s 192.168.0.45 -d 0/0 2049 -p 6 -j ACCEPT
ipchains -A input -s 192.168.0.45 -d 0/0 111 -p 6 -j ACCEPT
ipchains -A input -s 192.168.0.45 -d 0/0 111 -p 17 -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

同じ設定を netfilter で行う場合は次になります。

iptables -A INPUT -f -j ACCEPT -s 192.168.0.45
iptables -A INPUT -s 192.168.0.45 -d 0/0 32765:32768 -p 6 -j ACCEPT
iptables -A INPUT -s 192.168.0.45 -d 0/0 32765:32768 -p 17 -j ACCEPT
iptables -A INPUT -s 192.168.0.45 -d 0/0 2049 -p 17 -j ACCEPT
iptables -A INPUT -s 192.168.0.45 -d 0/0 2049 -p 6 -j ACCEPT
iptables -A INPUT -s 192.168.0.45 -d 0/0 111 -p 6 -j ACCEPT
iptables -A INPUT -s 192.168.0.45 -d 0/0 111 -p 17 -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.8 を見てください。

他の行は、サーバで利用することにした特定のポートそれぞれへの、 クライアントの任意のポートからの接続を許可しています。 この設定によって、 もし例えば 192.158.0.46 が NFS サーバに接続を試みたとしても、 そのマシンはマウントもできず、 なにがマウントできるかもわからないことになります。

新たに導入されたポート固定機能を用いれば、 NFS 共有へのマウントを許すホストの制御は見ての通りずっと簡単になります。 ただし NFS は暗号化されたプロトコルではありませんから、 同じ物理ネットワークにつながっていれば、誰でもトラフィックを盗聴し、 行き交う情報を再構成することが可能です。

6.5. NFS に SSH をトンネルさせる

ネットワークの NFS トラフィックを暗号化する方法のひとつとして、 ssh のポートフォワード機能を用いるやり方があります。 しかし、以下で見るように、 サーバのローカルユーザを完全に信頼できない場合は、 これには深刻な欠点もあります。

最初のステップは、ファイルをローカルホストにエクスポートすることです。 例えば /home パーティションをエクスポートする場合は、 次の行を /etc/exports に追加します。
/home   127.0.0.1(rw)

次のステップは ssh を用いてポートをフォワードすることです。 ssh を使うと、 クライアントの任意のポートを、 任意のマシンの任意のポートにフォワードできます。 例えば前節のように、我々のサーバは 192.168.0.42 とし、 -p 32767 引数を用いて mountd をポート 32767 に固定したとしましょう。 そして、クライアントから次のように入力します。
     # ssh root@192.168.0.42 -L 250:localhost:2049  -f sleep 60m
     # ssh root@192.168.0.42 -L 251:localhost:32767 -f sleep 60m

このコマンドを使うと、クライアントの ssh はクライアントのポート 250 に対して行なわれたリクエストをフォワードし、 サーバの sshd を経由して、 サーバのポート 2049 へとフォワードします。 次の行もおなじタイプのフォワードで、 クライアントのポート 251 へのリクエストをサーバの 32767 へフォワードします。 localhost はサーバから見たホストです。 つまり、フォワードはサーバ自身に対してなされます。 これとはべつに、ポートを任意の別のマシンにフォワードすることもでき、 そのリクエストは外世界に向け、 サーバからなされたかのように行われることになります。 よって、このリクエストはサーバの nfsd に対して、 サーバ自身から来たかのように見えます。 ところで、クライアントで 1024 以下のポートをバインドするには、 このコマンドをクライアントの root として実行する必要があります。 ファイルシステムをデフォルトの secure オプションでエクスポートした場合には、こうしなければなりません。

なおここでは、最後のオプション -f sleep 60m を用いてちょっとしたトリックを行っています。 普通 ssh を用いると、 -L オプションを用いた場合でも、 リモートマシンでシェルをオープンすることになります。 しかしここではポートフォワードをバックグラウンドで実行したいだけで、 シェルはクライアントに戻したいわけです。 したがって ssh には、 60 分眠り続けるコマンドをサーバのバックグラウンドで実行させているのです。 これはポートを、接続があるまで 60 分間フォワードします。 接続があると、接続が切れるか 60 分が過ぎるまで、 どちらか遅い方まで接続は継続します。 上記のコマンドは、クライアントの起動スクリプトの ネットワーク通信開始以降に置くこともできます。

次に、ファイルシステムをクライアントでマウントしなければなりません。 これを行うには、クライアントに対して localhost のファイルシステムをマウントするように、 ただし通常の 2049 とは違うポートを用いるように、伝えます。 具体的に /etc/fstab のエントリを示すと、 次のようになります。
  localhost:/home  /mnt/home  nfs  rw,hard,intr,port=250,mountport=251  0 0

ここまで来ると、 サーバのローカルにログインできる通常のユーザがいると、 なぜ非常に危険になってしまうのかが理解できます。 このようなユーザがいると、 我々が行ってきたことを妨げるすべは一切なく、 ssh を用いて 自分のクライアントマシン (ここでは合法的に root になれます) の特権ポートをサーバのポート 2049 や 32767 にフォワードできます。 このようにして、サーバの任意のユーザは、 我々のクライアントの root と同じ権限で、 任意のファイルシステムをマウントできることになります。 [訳注: 要するに、サーバが localhost に対して エクスポートすることの危険性を言っています。]

NFS サーバで通常のユーザをログインさせておらず、 かつこの方法を使いたい場合でも、 まだ 2 つ、警告しておくことがあります。 まずひとつめ、クライアントからサーバへの接続は sshd を経由します。 よってファイアウォールで、 ポート 22 (sshd が待機するポート) をクライアントに対して開けておく必要があります。 しかし、2049 や 32767 のような他のポートを開けておく必要はもうありません。 ふたつめ、ファイルロックが動作しません。 statd やロックマネージャに対して、 特定のマウントに対するリクエストを特定のポートに行なわせることはできません。 したがって、ロックリクエストがあると statd は localhost の statd に、つまり自分自身に接続し、 エラーは発生せずに失敗します。 これを修正するためには、NFS を大きく書き換えなければなりません。

IPSec を用いれば、 サーバでのローカルなセキュリティの問題を生じさせない形で クライアントとサーバ間でのネットワーク通信を暗号化できます。 これはここでは取り上げません。 Linux で IPSec を用いるための詳細については、 FreeS/WAN ホームページを見てください。

6.6. まとめ

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

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