オフィシャルページ :
ミシガン大学 CITI: Projects: NFS Version 4,
Linux NFSv4 Wiki,
RFC 3530

NFSv4

「良い意味でも悪い意味でも古いNFS」 を、インターネット経由でも使用できるようにと改良したのが、 NFSv4 すなわち NFS Version 4 だ。本当に安心してインターネット経由で使えるかどうかにあまり興味はないが、NFS の Version 2 や 3 に比べると、mountd, lockd, statd 及び quotad の機能を NFS デーモン内部に取り込んだこと、(基本的には) portmapper が必要なく、サーバは 2049 番ポートだけ解放すればよくなったこと。NFS over TCP が標準になったこと、デフォルトの読み取りブロックサイズが格段に大きくなったこと、ファイルのオープン/クローズやキャッシュなど多くのオペレーションをクライアントに任せるようになったことなどが特徴だ。従来の NFS と NFSv4 との違いは NFSv4 TESTING for LinuxThe NFS Version 4 Protocol - SANE 2000 (PDFファイル) に要約されている。それを読むと、NFSv4 が NFSv3 よりもスピードで劣ることは考えにくい。特に、細かいファイルをたくさん開いたり閉じたりするオペレーションでは、(実装が正しければ) パフォーマンスは向上するはずだ。大きな単一ファイルでベンチマークを採っても差は出まい。

検証は、Fedora Core 3 と 5、RedHat Enterprise Linux 4 (RHEL4) で行った (FedoraCore 3 は NFSクライアントとしてのみ)。章立ては、できる限り 旧 NFS (v2, v3) のページ とそろえることにした。そのほうが旧プロトコルとの違いがよく分かると思ったからだ。それもあって、ここは NFSv4 のみを動作させる時の内容としている。サーバをバージョン 3 など旧プロトコルでも待機させるには、前ページの内容と組み合わせて設定する必要があるだろう。(併用する時のコツは 年越しそばと初詣は絶対に欠かせない さんのブログに紹介されている)。また、当ページでは nfs-utils のバージョンが変わって変更となったデフォルト値なども反映した。記述の中に出てくるデフォルト値は nfs-utils 1.0.6 (RHEL4) 及び 1.0.8 (FC5) で調べたものだ。

Table of Contents

NFSv4 の動作に関係するデーモン

RedHat 系の RPM パッケージでは、portmap だけは portmap パッケージ、それ以外は nfs-utils パッケージに含まれる。下表のセルの色分けは、RedHat 系 ディストリビューションで、どの rc スクリプトで起動されるかを示す。 NFS に関係する rc スクリプトとして、この他に netfs もあるが、それはログイン時に勝手に /etc/fstab を読んで samba や NFS タイプのファイルシステムを自動的にマウントするためのものであり、その都度手動マウントする場合にはサーバ側にもクライアント側にも必要なく、セキュリティ面からも停止が推奨されている。

本来の NFSv4 の定義 (RFC 3530) からすると、portmap は要らないはずだ。しかし、RedHat EL 4, Fedora Core 3 及び 5 に実装されている nfsd では、portmap なしでは rpc.nfsd が起動できない。mountd もまた、要らないけれども要る。その辺りの実験報告は後半のコラム参照

実行ファイル名 役割 デフォルトポート サーバに必要 クライアントに必要
rpc.idmapd NFSv4 内部での制御に使用する NFSv4 ID と、UNIX ユーザ名とのマッピングを行うデーモン。RedHat系パッケージでは nfsd 本体と同じ nfs-utils に含まれる。本来は nfsd が立ち上がってから idmapd を起動させるべきもののようだが、nfsd がきちんと起動するには /etc/init.d/rpcidmapd の中に書かれている sunrpc カーネルモジュールが要る、というおかしな依存関係になっている。そのため、/etc/init.d/rpcidmapd は通常、マシン起動時に起動するよう /etc/rc3.d/rc5.d/ にリンクが作られているが、nfsd 起動スクリプトの中でも、idmapd が立ち上がっていなければ `service rpcidmapd start' するようなシェルが組まれている -
portmap RPC系デーモンの使用ポートナンバーを管理する、いわばポート版の DNS のようなもの。NFS 関連だけでなく NIS (ypbind, ypserv) と, ruser, ruptime などの "r" 系サービスもその守備範囲。NFSv4 だけを使う場合は本来的には要らないが、RedHat系ではしがらみ的に必要 111 (TCP), 111 (UDP)
rpc.nfsd NFS サーバのユーザ空間プログラム。rpc.nfsd の役目はスレッドの生成のみで、 NFS の根幹機能はカーネルモジュール nfsd.o が司る 2049 (TCP) ×
rpc.mountd マウント要求を受け付けるデーモン。NFSv4 の本来の仕様からすると不要なはずだが、RedHat系の実装方法では捨てられない。せめてもの抵抗として mountd に `--no-nfs-versions 2',`--no-nfs-version 3' を与えて最小限の状態で立ち上がらせる (パケットフィルタリングのための準備 の項を参照) 動的 × ×
rpc.rquotad クォータの問い合わせに答えるデーモン。 NFSv4 だけを使う場合は不要なので、RedHat 系では /etc/sysconfig/nfsRQUOTAD=no と書くことによって、起動しないようにしておく 動的 × ×

実行ファイル名/hosts_access名/RPCサービス名 対応表

実行ファイル名 host_access名 RPC名
rpc.idmapd - -
portmap portmap portmapper
rpc.nfsd nfsd nfs
rpc.mountd mountd mountd
rpc.rquotad rquotad rquotad
rpc.lockd lockd nlockmgr
rpc.statd statd status

パッケージインストール後の調整

v4recoveryディレクトリの作成

RedHat系 nfs-utils RPMパッケージの不備だろう。サーバ側にあるべきディレクトリがひとつ足りないので、作っておく必要がある。RHEL4.5 でも Fedora Core 5 でもそうだった。

サーバ側ファイルシステムに /var/lib/nfs/v4recovery/ というディレクトリが存在するか確認して、なければ root 権限で、パーミッション 755 で作成する。v4recovery/ は、マウントが不意に切れたりロックに異常が起きた時のためのキャッシュを保存するディレクトリだと思われる。これがないと、nfsd を起動した時、システムログに "unable to find recovery directory /var/lib/nfs/v4recovery" というメッセージが出る。

idmapd設定ファイル /etc/idmapd.conf

NFSv4 サーバとクライアントは、UNIX UID/GID とは別の NFSv4 ID というものによってユーザを区別している。それを UNIX ID に対応づけるのが rpc.idmapd の仕事だ。その設定ファイルである /etc/idmapd.conf は主に 3つのセクションから成る。

[General] セクション
ディレクティブ 説明
Verbosity メッセージの冗長度
Pipefs-Directory RPC Pipefs 仮想ファイルシステムの在処。nfs-utils を RedHat系 RPM でインストールした時のパスは /var/lib/nfs/rpc_pipefsrpc_pipefs 仮想ファイルシステムのマウントは、sunrpc カーネルモジュールをロードすると行われ、その sunrpc モジュールは rpcidmapd 起動スクリプトの中でロードされるようになっている
Domain NFSv4 はユーザを user@domain のカタチで扱うのでこれが必須。NFSv4 を Kerberos5 と組み合わせて使用しない限りは、必ずしも DNSドメインと同じである必要はないが、NFSサーバとクライアントで同じ設定でなければならない。異なっていると、次項の Mapping セクションも実際のユーザマッピングもきちんと機能せず、どのファイルも nobody の所有であるかのように見えたりする
[Mapping] セクション
Nobody-User root_squash, all_squash した際に割り付けられるデフォルトのユーザ。exports ファイルの説明も参照
Nobody-Group root_squash, all_squash した際に割り付けられるデフォルトのグループ。exports ファイルの説明も参照
[Translation] セクション
Method UNIX側ユーザID/グループID の取得方法を指定する。指定可能な値には nsswitchumich_ldap のふたつがあるが、後者はまだコードが未熟らしい
[UMICH_LDAP] セクション (コードが未熟らしい)
LDAP_SERVER など 前述の Methodumich_ldap にした場合のユーザ情報問い合わせ先 LDAP サーバや取得するアトリビュート名などが指定できるらしい。CITI: Projects: NFS Version 4 Open Source Reference Implementation 参照。UMICH という変な名前は、NFSv4 実装の先駆者 CITI プロジェクトのある University of Michigan (ミシガン大学) に由来しているようだ。(RedHat EL4 でも rpc.idmapdidmapd.conf の man に CITI のメンバーの氏名がクレジットされているのが見て取れる)

現在のところ、設定可能な項目はやや不十分で、不足している部分は rpcidmapd 起動スクリプト冒頭の OPTIONS 変数に定義する。例えば、下記のようにすると、サーバ側の idmapd はサーバ専用、クライアント側はクライアント専用に働かせることができる;

サーバ側の init.d/rpcidmapd

OPTIONS="-S"

クライアント側の init.d/rpcidmapd

OPTIONS="-C" 

idmapd.conf に変更を加えたら必ず、サーバ、クライアントとも、

root# service rpcidmapd restart

サーバ主設定ファイル /etc/exports

書式とパラメータ

書式:

directory client(option,option...) client(option,option...) ...
各パラメータの詳細:
directory:
開放するディレクトリを指定
client:
マウントを許可するクライアント。指定方法はいくつかある:
  1. ホスト名か FQDN (例: somemachine, somemachine.hoge.cxm, あるいはワイルドカードを利用して *.hoge.cxm)
  2. ネットグループ (例: @somegroup)
  3. IP範囲 (例: 192.168.1.1-15 なら 192.168.1.0/255.255.255.240 または 192.168.1.0/28)。ワイルドカード併用不可
3. の指定方法を推奨する。その際には、「内部サブネットに属するアドレスは外部から入ってくるはずがない」 というポリシーをファイヤーウォールやルータに設定しておくと、安全性がさらに高まる。当家 iptalbes設定例 (特に bad_input チェーン) 参照。
option:
上記client部と、オプションの `(' との間にスペースを入れてはいけないことに注意。
fsid=num 本来、fsid は、エクスポートするファイルシステムをマウントしているブロックデバイスのデバイスナンバー (メジャー+マイナー) から計算されるらしいが、それを決め打ちしたい時に用いる。設定できる値は任意の 32bit値。これは NFSファイルサーバを冗長化している場合に、サーバが切り替わった時にファイルハンドルが変に残ったりしないようにするために用いられるようだ。ただし、そうした用途でこのパラメータを設定することは希。NFSv4 特有の 0 という値を与えて、エクスポートしたディレクトリを / (ルート) であるように見せるために用いるのが Linux での実質唯一の用法 (ここでは説明しきれない -- 下記設定例と、後述の クライアントの設定 参照)
ro 読み取り専用で共有。デフォルト
rw 読み書き可能で共有
sync
async
遅延書き込みを有効にするか。遅延書き込みを有効 (async) にしておくと、サーバ上で実際に sync が行われる前にサーバがリブートした場合データが壊れる恐れがある。NFS-1.1 以上では sync がデフォルト
root_squash
no_root_squash
root_squash を直訳すれば「root 権限つぶし」。クライアントから UID 0 としてのファイル操作要求を受けた際、権限を剥奪し、nobody なユーザにマッピングする。デフォルトは root_squash 有効。下記 anonuid, anongid によってマッピング先 UID/GID が指定できる
all_squash
no_all_squash
上記の拡大版で、 root に限らず全てのユーザが特定の 1ユーザにマッピングされる。デフォルトでは no_all_squash つまり無効。マッピング先UID/GIDは下記 anonuid, anongid で指定できる
anonuid=xxx
anongid=xxx
上記の squash を行う際のマッピング先ユーザを指定したい場合に使用。idmapd.confNobody-User, Nobody-Group の設定をこちらで上書きできる。ID は文字列呼称でなくナンバーで指定しなければならない。つまり、マッピング先ユーザ/グループは、サーバとクライアントとで同一の UIDナンバー/GIDナンバー、同一の文字列呼称を持っていないと、おかしなことになる
no_subtree_check
subtree_check
subtree_check が有効 (デフォルト) だと、ファイルシステムの一部サブディレクトリだけがエクスポートされている時、NFSリクエストが来る度に、操作対象のファイルが操作の許されたファイルシステム上にあるかどうかに加え、その親階層が確かにエクスポートされておりその上流のディレクトリが操作 (読み・書きなど) を許可しているかまでチェックする。セキュリティ的には subtree_check 有効に越したことはないが、多くの細かいファイルが存在するディレクトリをエクスポートする際には、パフォーマンスを犠牲にしないために no_subtree_check を設定した方がよいとされている
secure
insecure
secure (デフォルト) は、クライアントからの操作要求パケットの送信元ポートが 1024 より下、つまり特権ポートから発せられていないと受け付けない。insecure はこれをとやかく言わない
hide
nohide
例で説明しよう。サーバ上に、/var/export/ というディレクトリがあり、エクスポートしたいとする。ただし、その下位にある /var/export/hoge/ の内容は、じつは /home/chome/ をサーバ上でマウントしたものだったとする。この場合、hide (デフォルト) がセットされていると、NFSクライアントで /var/export/ を NFSマウントしただけでは /home/chome/ にある内容は見えない。見たければ、/var/export/ に加えてサーバが /var/export/hoge を明示的にエクスポートしてクライアントがそれを /var/export/hoge/ に NFSマウントしなければならない

単純な設定例

/home/hoge  192.168.1.0/28(ro,sync,fsid=0)

このようにエクスポーとすると、NFSv4 では、サーバ上の /home/hoge/server:/ として開放される。Windowsファイル共有のイメージに近い。
NFS 起動後に主設定ファイルを書き換えた場合は、 exportfs -ra コマンドを発行すれば変更が反映される。RedHat系 nfs INITスクリプトの場合は、`service nfs reload' でもほぼ同じことができる。意図通りにエクスポートされたかどうか確認するには、

root# exportfs -v

複数のディレクトリを別々にエクスポートする例

現在のところ fsid=0 パラメータは /etc/exports の中で少なくとも 1回は唱える必要がある。これは NFSv4 が疑似ファイルシステム (pseudo-file system) という概念を採用しており、エクスポートされるオブジェクトは、概念上ひとつの基準ディレクトリの下位に属さなければならないからだ。そのため、少なくとも今の実装では、複数のディレクトリを別々の存在としてエクスポートするにはひと手間要る。例として、/var/export/server://home/hoge/data/server:/data/ として開放し、クライアントで server:/data/ 単独のマウントも可能にしたい場合を考えよう。

/etc/exports の記述:
/var/export       192.168.1.0/28(ro,sync,fsid=0)
/var/export/data  192.168.1.0/28(ro,sync,no_subtree_check,nohide)

まだ早合点して`exportfs -ra' してはいけない。 /home/hoge/data が出てこないじゃないか、と思っただろう。トリックはここから。サーバ上で、/home/hoge/data/var/export/data/別名マウントしてやるのだ。別名マウントは mount コマンドの --bind 機能で行う。サーバ上で /home/hoge/data/var/export/data としても参照できるようにしておくわけだ。やるべきことは、

root# mkdir -p /var/export/data
root# mount --bind /home/hoge/data /var/export/data

マシンを再起動しても bind が自動的に行われるようにするには /etc/fstab に下記のような 1行を加える;

/home/hoge/data  /var/export/data  none  bind  0 0

ファイルシステムのマウントは当然 nfsd などデーモンの起動よりも先に行われるので、めでたしめでたし。バインドマウントをやり直したい時には、一時的に共有を解除しないと "device is busy" と言われてアンマウントできないので、`exportfs -au' してから作業をする。なお、上記の例で言う /home/hoge/data の配下にもマウントによる枝が存在しそれも一緒にエクスポートしたい場合には、--bind の代わりに --rbind を使わなければならないかもしれない。

クライアント側の設定

/etc/idmapd.conf の設定

/etc/idmapd.conf を設定する。サーバのものと同じ内容でなければならない。そして `service rpcidmapd restart'。

マウント

単純に一時的にマウントするなら、例えば単純な例 の NFSサーバ hoge/home/hoge/mnt/export にマウントする場合、クライアント上で:

root# mount -t nfs4 hoge:/ /mnt/export -o hard,intr

とコマンドすれば良い。複数のディレクトリを別々にエクスポートする例 での /var/export/data だけをマウントする場合ならば、

root# mount -t nfs4 hoge:/data /mnt/export -o hard,intr

しばしばマウントするのなら、より最適なオプションを指定してクライアントの /etc/fstab に書いておくのが賢明。エントリは:

hoge:/  /mnt/export  nfs4  ro,nosuid,_netdev,noauto,hard,intr  0 0

主なマウントオプションの意味:

nosuid 実行バイナリファイルに立っている SetUID ビットや SetGID を無視する
_netdev このマウントエントリにはネットワークアクセスが必須であることを mount に伝え、ネットワークが上がっていなければマウントを許さない
noauto mount -a による fstab エントリ一斉マウントに含めない (典型的にはブート時の自動マウント)
noexec バイナリファイルの実行を禁止する。上の記述例では使用していないが、さらに厳重にするなら指定しても良い
rsize=xxx データをサーバから読み取る際の転送ブロックサイズの最大制限値。 1024 の倍数にすべき。 NFS Ver.4 でのデフォルトは 32768。ただしこれは上限であって、実際に遣り取りされる時のブロックサイズは動的に変わるようだ。設定できる上限値はカーネルによって異なり、その値は カーネルソースの include/linux/nfsd/const.h にある NFSSVC_MAXBLKSIZE で決まる
wsize=xxx データをサーバに書き込む際の転送ブロックサイズ。上記同様
hard クライアント上のプログラムがファイルにアクセスしている最中にサーバがクラッシュした場合、そのプログラムはタイムアウトせずに、サーバが回復するまで待ち続ける。相反するオプションに soft があるが、そちらだとプログラムは自動的にタイムアウトするが、高い確率でファイルが破損するらしい
intr interrupt の意 (たぶん)。上記 hard に伴ってハングしたプログラムを、中断や KILL できるようにする
nfsvers=x NFSv4 では指定無用
mountvers=x NFSv4 では mountd は使用しないので指定無用
proto=tcp/udp NFS サーバに対してどちらのプロトコルを使うかを指定。NFSv4 でのデフォルトは TCP。こうして TCP だけに制限すると、ファイヤーウォールで開放しなければならないポートがより限定できるというセキュリティ上のメリットがある。両ピア間がネットワーク的に非対称 (例えばサーバのNICが100Mでクライアントが1000M) だったり、間にルータを挟んでいたりする場合は特に、TCP を使ったほうがトラブルを避けられるだろう。 NFS over UDP ではリトライが起きた場合にその「ファイル」全体を再要求/再送してもらわなければならないが、 NFS over TCP ならば、落ちたパケットだけの再送で済むし、経路途中でのネットワークスピードの変化を上手に「吸収」してくれるからだ。NFSv4 サーバの中には、TCP しか実装していないものもあるし、UDP で遣り取りしたいのなら NFSv3 を使い続けた方がいいだろう
timeo=x 要求(例えばファイル読取り要求) に対するサーバからの返答をどれだけの時間待つかの初期値。単位は 1/10 秒。NFSv4 では、例えば初期タイムアウトが `100' つまり 10 秒であれば、最初、それだけ待ってサーバから返答が返ってこないと、次にクライアントは timeo を 2倍の 20 秒にして要求しなおし、それでもダメならさらに倍の 40 秒にしてまた再要求。これを、再送回数が retrans (次項参照) に達するか、timeo が 600 (60秒) に達するかのどちらか早いほうがやってくるまで繰り返す。NFS over TCP の初期 timeo のデフォルト値は 60 秒で、timeo の最大値は 60秒で固定されているので、その場合はもうそれ以上 timeo が延びることはない。 UDP の時のデフォルト値 `7' はややせっかち過ぎるきらいがあり、調整時の現実的な値は 50 (5秒) や 100 (10秒) といった辺りのようだ。
retrans=x 上記 timeo によって再要求が生じた場合に、何度まで再要求するか。TCP の場合のデフォルトである 2 回の場合、2回目の再要求でなおタイムアウトが起こると「メジャータイムアウト」となり `server not responding' というメッセージを表示するとともに、あと 1回 (つまり 3回目) だけトライする。それでもダメな場合は、またマイナータイムアウトに立ち返り、 2回目の再要求で...となる。UDP の場合のデフォルト値は 5回
sec=sys ユーザ認証に使用する仕組みを指定。デフォルトは、システムの UNIX UID/GID を使う sys。筆者は Kerberos との併用は未検証だが、ローカル UID/GID の代わりに Kerberos V5 での認証を利用する krb5、認証に加えてチェックサムによるデータ改ざんチェックも行う krb5i、さらに上乗せでトラフィックの暗号化も行う krb5p もある
要らないはずなのに要る portmap と mountd

少なくとも nfs-utils 1.0.8 で試した結果では、両方とも必須だった。まず、NFSサーバ側で portmap を立ち上げておかないと、nfsd は起動すら拒む。NFSv2 と v3 を無効にしておいても、どうやら、このバージョンの nfsd はローカル上の portmap に一度はお伺いを立てずにいられないようだ。クライアント側もまた、 portmap を起動しておかないと "can't read superblock" というエラーが出て NFSv4 マウントに失敗する。

mountd については、Fedora Core 5 で、 mountd 起動/停止記述を徹底的に排除した INITスクリプトを書いて試してみた。そうしても nfsd は正常に起動するし停止もできた。しかし、いざクライアントからマウントを掛けようとすると、

mount: block device server:/ is write-protected, mounting read-only
mount: cannot mount block device server:/ read-only
と文句を吐いてやはりマウントが成り立たない。さらに言わせてもらえば、nfslock はスタートしていないのに nfsd を起動するだけで nlockmgrrpcinfo の出力に現れるのも気になるし、クライアント側でパケットをキャプチャしてみるとサーバの mountd (?) からクライアントの RPC系動的ポートに向かって何やらパケットが送られるのも気になるところだ。

セキュリティ

意図したクライアント以外からアクセスされないよう、サーバに制限を設ける。NFSv4 に対しては TCP Wrappers による規制は働かない模様。 iptables (ファイヤーウォール) によるパケットフィルタを中心に考えるといいだろう。

TCP Wrappers

筆者の実験 (nfs-utils 1.0.8 サーバ) では、NFSv4 のみを使う環境では、hosts.denynfsd, portmap など全て禁止して hosts.allow で何も許可しない状態でも NFS通信が成立した。逆に言えば、NFSv4 だけの時は全て禁止しておいたほうがセキュリティ上良いのではないだろうか。ここに書くサービス名は実行ファイル名とは異なるので、実行ファイル名/hosts_access名/RPCサービス名対応表 参照。記述法については `man 5 hosts_access' してみると良い。

/etc/hosts.deny (拒否ホストリスト)

こちらの設定ファイルで、すべてのホストからのアクセスを拒否する。

portmap mountd statd nfsd lockd rquotad: ALL

/etc/hosts.allow (許可ホストリスト)

どれも許可しないのでこちらはコメントアウトするか、はなから書かない。

#portmap mountd statd nfsd lockd rquotad: 192.168.1.0/255.255.255.240

規制がちゃんと効いているかどうかは、相手側でデーモン群を起動しておき、クライアントから以下のコマンドを発行すれば分かる。 host の部分は相手のホスト名に置き換えるべし。host 引数を指定しないと localhost が対象となる:

root# rcpinfo -p host

または、TCP パケットを送って反応を調べるコマンド。こちらは引数省略不可:

root# rcpinfo -t host service

で状態を詳細表示してみる。 service の部分はこれまた実行ファイル名ともホストアクセスのサービス名とも違いrpcinfo -p で表示される名称でなくてはならない。例えば lockd であれば nlockmgr を指定するといった具合。

iptables による規制

iptables によるパケットフィルタは、その要求がどのプロセスに渡されるよりも前に行われるので、まさに水際での防御となる。

パケットフィルタリングのための準備

NFSv4 では statdrquotad が要らなくなったため、ポートの固定など、やることはほとんどなくなった。ただし、やっておくと良い、少々の設定はある。

RedHat系の init.d/nfs 起動スクリプトでは、幾つかの変数を /etc/sysconfig/nfs から拾って、それらを nfsdmountd などにオプションとして渡すことができる。/etc/sysconfig/nfs の内容は、例えばこんな塩梅だ;

RPCMOUNTDOPTS="--no-nfs-version 1"
MOUNTD_NFS_V2=no
#MOUNTD_NFS_V3=no
MOUNTD_PORT=768
RQUOTAD=no
RPCNFSDARGS="-N 2 -N 3 -U"
RPCNFSDCOUNT=10
1~3行目 本当は必要のない rpc.mountd を実装上やむなく起動させるわけなので、待ち受けバージョンをなるべく少なくする。 2行目によって、 init.d/nfs 内での rpc.mountd の起動に際して "--no-nfs-version 2" オプションが渡される。NFSv1 に関しては、 nfs INITスクリプトに V2, V3同様の変数の仕組みがないので RPCMOUNTDOPTS 変数に引数を直接セットする(1行目)。 3行目の V3 殺しを書きながらコメントアウトしているのは、全バージョンを殺そうとすると mountd が起動できないから。どれかひとつ残すこととなるわけだが、古臭い NFSv1 サポートを残すのはかえって危険かもしれない
4行目 MOUNTD_PORT 要らない上にポートが動的とは生意気なので、固定。rpc.mountd 起動オプションの -p (--port) にあたる。768 は単なる例だが、/etc/services ファイルを調べて他で使われていないポートを選ぶこと。理想的には、ここ指定したポートは /etc/services に追加しておくと良い
5行目 RQUOTAD=no こうしておけば、NFSv4 にとってもはや無用な rquotad 起動ルーティンはスキップされる
6行目 RPCNFSDARGS mountd と同じように rpc.nfsd に対しても -N オプション (--no-nfs-version と同義) で NFSv2/v3 を無効化する。-N オプションを複数回唱えることは当初うまくいかなかったが、パッケージをアップデートしたら効くようになった。待ち受けは -U (--no-udp) で TCP だけにする。なお、 rpc.nfsd は既に NFSv1 をサポートしていないので、`-N 1' を指定すると "Unsupported version" と言って怒られる
7行目 RPCNFSDCOUNT ファイヤーウォールとは関係ないが、nfsd の初期起動スレッド数を変えたい場合に指定。デフォルトは 8本

意図通りになったか確認したければ、

root# service nfs restart
root# rpcinfo -p

フィルタリングルール

最低限、許可しなければならないポートは以下。下記の例では、 NFSv4 は TCP でのみ使用する前提に立っている。
まずは汎用的なルール記述例を挙げることにしよう。

INPUT chain

Proto/Port TCP 2049 (nfsd)
設定例 -A INPUT -p tcp --dport 2049 -s 192.168.1.0/28 -j ACCEPT

OUTPUT chain

Proto/Port TCP 2049 (nfsd)
設定例 -A OUTPUT -p tcp --sport 2049 -d 192.168.1.0/28 -j ACCEPT
当家 iptables 「インターネットサーバの自己防衛」 に倣った設定例

Iptables の設定例を記載した当家「インターネットサーバの自己防衛」に NFS サーバを追加する場合の例を示す。

tcp_packets chain (INPUT の子チェーン)

# {1-3-7} tcp_packets chain rules
-A tcp_packets -p tcp -m multiport --dports 80,110,25,22,443 -j allowed
-A tcp_packets -p tcp -s 192.168.1.0/28 --dport 2049 -j allowed
-A tcp_packets -p tcp -i eth0 -j ms_packets_tcp

OUTPUT chain

OUTPUT パケットに関しては、ローカル発のパケットかどうか確認してから基本的には全部許可しているので、追加のルールは特に必要ない。とはいえ、より厳重にしたい場合は OUTPUT チェーンに以下のようなルールを加えてもいいだろう。ポート 111 (portmap) は念のため。なお、最初にあるローカル to ローカルのパケットを許可するルールは、NFS デーモン群のスタート/ストップ時に portmap がそうしたパケットを出すので、一応設定しておいた方が良い。

# {1-3-C} OUTPUT chain rules
-A OUTPUT -p all -d 127.0.0.1 -s 127.0.0.1 -j ACCEPT
-A OUTPUT -p tcp -d ! 192.168.1.0/28 --sport 111 -j DROP
-A OUTPUT -p udp -d ! 192.168.1.0/28 --sport 111 -j DROP
-A OUTPUT -p tcp -d ! 192.168.1.0/28 --sport 2049 -j DROP
-A OUTPUT -p all -s 127.0.0.1 -j ACCEPT 
-A OUTPUT -p all -s 192.168.1.1 -j ACCEPT
-A OUTPUT -m limit --limit 3/minute --limit-burst 3 -j LOG --log-level info --log-prefix "IPT OUTPUT packs died: "
 COMMIT

Kerberos V5 との併用

未検証のため未稿。

チューニング

パフォーマンス以前の問題なのだが、マウントは出来るが存在するはずのファイルが読めない、`server not responding' が多発する、といった場合には、まず第一に、NFS-HOWTO の Optimizing NFS Performance (NFSの性能を最適化する) の章をありがたく御拝読すること。その中からつまみ食いで、筆者が体験的に得た戒めや功を奏した項目を並べると...

要求キューバッファの拡大

前ページ参照。