NFSv4サーバーの作り方

検証環境

NFSセキュリティメモ

NFSについてググって見ると使用状況に応じて色々と苦労している点が見られる。 Kerberos 必須という人も居れば不要という人も居るわけで、ここでは以下のアンセキュアな環境を前提とする。

専用NIC(VLAN)割り当て

NFS共有用に専用NIC(VLAN)を割り当てる。そのNICにはNFS以外のサービスは提供しない。

  • GbEはいいぞ。単体~数台なら十分以上の性能を発揮する。
  • これは「同時」数台が帯域を使い込むような状態を想定しているので、数台がネットワークに存在するだけで飽和することを意味しない:-)。
  • 可能なら10GbEだろうけど高いし。
  • 検証環境では同一ホスト内にNFSサーバーとNFSクライアントを共存させてみたが、単純に / 以下を対象に tar(1) で固めただけで、1.6Gbps の性能が出た。
  • つまり単体で最大性能発揮しようと思ったらGbEは足りないかもしれないけど、10GbE埋めるのは結構大変。
  • ましてや「複数台が同時に」という確率と合わせると数台程度ならGbEでも余裕。
  • まぁアレだ。キャパシティプランニングで、動画再生のような常時帯域を使いまくるような前提は勘弁な。

パケットフィルタリング無しで

ipfw/pf/iptables/tcpwrapper といったパケットフィルタリングを行わない。

  • NFSv4に絞ってるので制限しやすくなってるが、専用ネットワークならそれほどの必要性を感じない。
  • 不特定多数の人が使うような用途で、NFSクライアントの偽装やら root 権限奪取やら、あるいはそれに準じるようなアクセスがあることを想定しているなら、Kerberos 認証とか IEEE 802.1X(認証VLAN)とか IEEE 802.1AE(MACsec)とか導入した方がいいと思うの(言ってることの現実性はテキトー)。

認証や暗号化・アクセス制御は無しで

認証?何それ。UNIXアカウント一択(デフォルトの -sec=sys)で。

  • NFSクライアントとNFSサーバーでのファイル所有者の取り扱いは可能な限りシームレスで、つまり、root は root として扱う(違うと色々面倒なんだよ)。
  • Kerberos は Kerberos で大変なんだよ。 orz
  • 通信の暗号化?ちょっ(ry。
    • Kerberos 有効にしないとアカンって。
    • IPsec?パケットは暗号化されるけど、NFSクライアントで tcpdump(8) したらバレるよねぇ。
    • あぁ。VPN越しなら IPsec もアリか。
  • SELinux の類い。
    • 自分アレ、コントロール(ステップアップ)できないので、disabled の扱いで。

結論

とりあえず少しずつステップアップすることがぢゅーよー、ということで、この手順を参考に難しいことに挑戦してみてください。

セットアップ

ZFS領域確保(作業例)

sysctl vfs.zfs.min_auto_ashift=12
zpool create -O atime=off export /dev/da1

/boot/loader.conf(環境依存)

vfs.zfs.trim.enabled="0"

※検証環境に使用した VMware ESXi 6.0U2 上(5.5/5.5U1/5.5U2/6.0/6.0U1 でも確認、いずれも LSI Logic SAS アダプタによる)では TRIM/UNMAP がエラーになってログがウザいので ZFS TRIM 機能をオフにする。

物理環境下なら今時のHDDであれば TRIM/UNMAP 試してエラーになることは無いと思われるので、CAMのエラーメッセージがうるさい時だけ設定する。

どういうエラーメッセージが表示されるかというと、下記のようなカーネルメッセージ(SCSI/SAS なので TRIM というよりは UNMAP が実行された)で3回リトライするので3セット出力される。

(da1:mpt1:0:0:0): UNMAP. CDB: 42 00 00 00 00 00 00 00 18 00
(da1:mpt1:0:0:0): CAM status: SCSI Status Error
(da1:mpt1:0:0:0): SCSI status: Check Condition
(da1:mpt1:0:0:0): SCSI sense: Vendor Specific asc:0,0 (No additional sense information)
(da1:mpt1:0:0:0): Error 5, Unretryable error

このあたり、dmesg(5) コマンドの結果や /var/log/messages ファイルの中を確認のこと。

/etc/rc.conf(NICチューニング)

ifconfig_vmxn="inet XXX.XXX.XXX.XXX netmask XXX.XXX.XXX.XXX mtu 9000"

今時のファイル共有ならGbEや10GbE当たり前なので、MTU値を設定する。 MTU値はネットワーク依存で、通信に絡んでいる一番小さな値を指定することになる。必ずしも9000(バイト)とは限らないので、ちゃんと調べよう(&設定しよう)。

なお手動で設定できるが、その場合、MTU値を反映するために一度、下記のようにインターフェースをダウンさせる必要がある。

ifconfig vmxn mtu 9000
ifconfig vmxn down
ifconfig vmxn up

リンクダウン時に、そのインターフェースが持っていた経路情報を失うので(設定してなければ問題無い)、再起動(shutdown -r now)してしまうのが吉。 再起動後、インターフェースの設定を確認する。

# ifconfig vmxn
vmxn: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 9000
        options=60039b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,TSO4,TSO6,RXCSUM_IPV6,TXCSUM_IPV6>
        ether 00:0c:29:XX:XX:XX
        inet XXX.XXX.XXX.XXX netmask 0xXXXXXXXX broadcast XXX.XXX.XXX.XXX
        nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
        media: Ethernet autoselect
        status: active

/etc/sysctl.conf

NFSの最低利用バージョンの指定

vfs.nfsd.server_min_nfsvers=4

なお最大利用バージョンの指定は rc.conf 中の「nfsv4_server_enable="YES"」設定で行われている。

ZFSのチューニング

vfs.zfs.min_auto_ashift=12
vfs.zfs.vdev.async_write_max_active=31

特に async_write_max_active はドライブが対応しているNCQ・TCQの数(-1)を指定する(台数があれば台数分を乗算する)。

NFSのチューニング(未検証)

vfs.nfsd.issue_delegations=0
vfs.nfsd.enable_locallocks=0

vfs.nfsd.issue_delegations

vfs.nfsd.enable_locallocks

/etc/exports

V4: /export -network ネットワークアドレス -mask ネットマスク

とりあえず、/export がNFSv4共有できるようになる。ただし、/export 自体は sharenfs=off なので、どこからからもマウントできない(厳密にはできるけどアクセスできない)。

/etc/rc.conf

rpcbind_enable="YES"
nfsuserd_enable="YES"
mountd_enable="YES"
nfs_server_enable="YES"
nfsv4_server_enable="YES"

rpcbind_flags="-h 公開するNFSサーバーのIPアドレス"
nfsuserd_flags="-domain アカウント識別用ドメイン"
mountd_flags="-l -h 公開するNFSサーバーのIPアドレス"
nfs_server_flags="-t -h 公開するNFSサーバーのIPアドレス"

このうち、nfsuserd_flags で指定する「ドメイン」の指定が重要となる。 NFSv4では、NFSサーバーとNFSクライアント間のユーザー識別子を「ユーザー@ドメイン」で識別するため、合わせておく必要がある。

これはNFS共有するようなサーバー群では、同じドメインである場合、本設定を省略しても良い。

しかし、ホスティング環境でのバックアップサーバー提供のような、NFSクライアントのサーバーのドメインが統一できないような場合、サービス提供者側として、別途決め打ちする必要がある。

アカウント作成

アカウント作成例

pw groupadd postgres -g 5432
pw useradd  postgres -u 5432 -g postgres -c "postgres" -s /usr/sbin/nologin
install -d -o postgres -g postgres -m 0755 /home/postgres

※FreeBSD の PostgreSQL 用アカウントとして pgsql (/usr/ports/UIDs ファイル参照のこと)があるが、CentOS の PostgreSQL 用アカウント(postgres)とはマッチしないので、別途作成する。

CentOS 6.x との相互運用問題

CentOS 6.8(おそらく CentOS 5.11 も) との相互運用では以下の点に注意する必要がある。

  • NFSサーバー・クライアント間でUID/GIDを合致させないといけないバグがある(NFSv4の仕様上、バグ認定)。
  • というのもNFSクライアント(CentOS 6.8)で postgres(26):postgres(26) のプロセスがアクセス(cd など)した時の、NFSサーバーへのユーザー問い合わせ結果を tcpdump(8)した結果、「postgres」ではなく「26」という文字列をNFSサーバーに問い合わせ(fattr_owner)ている現象を確認した。
  • なお、chown(8) や ls(1) した時には問題無い。
  • 本現象について調査したところ、下記のバグがレポートされていることを確認した。
  • バグレポートでは以下の点から、CentOS 6 系(少なくとも 6.8、2016年06月09日現在)以下には反映されていないものと考える。
    • バグレポートで言及されているディストリビューションが FedoraCore 17 である。

    • CentOS 6(RHEL6) は FedoraCore 12/13 ベースである。

    • 更に CentOS 6 の nfs-utils のバージョンは 1.2.3 であるが、FedoraCore 17 では 1.2.6 である。

    • FedoraCore 17 ベースの CentOS 7 では問題無いと思われる(未検証)。

領域の共有設定

共有設定

/export をどう共有するかによるが「/export/NFSv4クライアント名」として、クライアント単位で共有設定する場合は、下記のようにパーティションを切る。

zfs create -o sharenfs="-maproot=root NFSクライアントIP" export/NFSクライアント名

sharenfs 設定を行うと、/etc/zfs/exports ファイルに掃き出されるのと、合わせて mountd(8) に再読み込み(HUP シグナル)が実施される。

一つの領域を複数のNFSクライアント間で共有するなら、/etc/exports に集約するのが良いと思われ(未実験)。

なおNFSクライアントからは、「/NFSクライアント名」でマウントすることになる(/export がトップディレクトリとなって見えなくなる)。

更にお好みに応じて、exec=off、setuid=off も追加しよう。これはNFSサーバーとしてはこれら属性を「解釈しない」という意味であって、NFSクライアントはNFSクライアントで、別途設定が必要となる。

設定確認

mountd(8) の再読み込みによりNFSクライアントからマウントできるようになるわけだが、これが成功したのか失敗したのかはよくわからない。 そこで、「showmount -e」コマンドと「zfs get -t filesystem sharenfs」コマンドの実行結果を見比べて判断すること(設定した内容が反映されてないなど)。

# showmount -e
Exports list on localhost:
/export/NFSクライアント1       XXX.XXX.XXX.XXX
/export/NFSクライアント2       YYY.YYY.YYY.YYY
# zfs get -t filesystem sharenfs
NAME                                                  PROPERTY  VALUE                          SOURCE
export                                                sharenfs  off                            default
export/NFSクライアント1                           sharenfs  -maproot=root XXX.XXX.XXX.XXX  local
export/NFSクライアント2                           sharenfs  -maproot=root YYY.YYY.YYY.YYY  local

※zfs get sharenfs やべぇ。スナップショットもリストアップして大変な量に…。 orz

よくある質問とその答え

Q.ジャンボパケットで通信してますかー?

A.大丈夫だ。問題無い。(なぜ?)

サーバーやクライアントはもちろんのこと、サーバーとクライアントの間を結んでいるネットワーク機器の全てでジャンボパケットに対応している必要がある。 単純にジャンボパケットが通るか(設定が正しいのか)確認するのに ping を使う。

# ping -D -s8972 NFSv4クライアントのIP
PING NFSv4クライアントのIP (NFSv4クライアントのIP): 8972 data bytes
8980 bytes from NFSv4クライアントのIP: icmp_seq=0 ttl=64 time=0.256 ms
8980 bytes from NFSv4クライアントのIP: icmp_seq=1 ttl=64 time=0.289 ms
^C
--- NFSv4クライアントのIP ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.256/0.272/0.289/0.017 ms

1バイトでもオーバーすると下記のようなメッセージが出力される。

# ping -D -s8973 NFSv4クライアントのIP
PING NFSv4クライアントのIP (NFSv4クライアントのIP): 8973 data bytes
ping: sendto: Message too long
ping: sendto: Message too long
^C
--- NFSv4クライアントのIP ping statistics ---
2 packets transmitted, 0 packets received, 100.0% packet loss

しかし、サーバーサイドは問題無くても、途中経路またはクライアントサイドにMTUミスマッチ(典型的には 1500 のまま)があると無応答となる。 この場合、クライアントサイドに問題が無いとすると、特定は非常に困難になる。 一台一台、地味に特定していく必要がある。

ping での通信に問題が無ければ、最低限、ネットワーク周りの設定に問題は無い。

しかし実際にNFS通信(over TCP)がその状態で通信できているか確認するのは難しい。 特に手動で設定した直後は、設定前のMTU値で通信していることがある。 この場合、tcpdump(8)を使えば観測できなくもないが、その場合、length 8948 というパケットが観測できれば、問題無い。

# tcpdump -npivmxn host NFSv4クライアントのIP
  :
XX:XX:XX.XXXXXX IP XXX.XXX.XXX.XXX.nfs > YYY.YYY.YYY.YYY.782: Flags [.], seq 54720:63668, ack 3705, win 1588, options [nop,nop,TS val 444521882 ecr 816267359], length 8948
  :

下記のように length 1448 のままだとMTU値変更が反映されていないか、クライアント側もMTU値が反映されていない可能性がある。

# tcpdump -npivmxn host NFSv4クライアントのIP
  :
XX:XX:XX.XXXXXX IP XXX.XXX.XXX.XXX.nfs > XXX.XXX.XXX.XXX.782: Flags [.], seq 333288:334736, ack 621, win 1040, options [nop,nop,TS val 2971896848 ecr 2075608049], length 1448
  :

そのあたり反映させるための操作が面倒なら、クライアントもサーバーも shutdown -r now してしまうのが吉。

参考文献

NFSv4/サーバー (最終更新日時 2016-07-24 23:43:53 更新者 NorikatsuShigemura)