2012年3月29日 星期四

解決 Linux 上 NFS 群組上限16個的問題

前言:
在 Linux 上使用 NFS server 在 client 要求存取時,會驗證群組的權限(癈話),當權限不夠時,聰明的我們會立刻檢查Owner 及 Group 或 ACL 權限是否足夠。但是前幾年,我卻遇到明明權限設定是足夠的,使用 id 指令查詢時,也出現該目標的 group ID ,但是檔案就是無法讀寫,一直回應 permission denied ,試到三字經都駡出來了!查了很久才發現是 NFS 協定本身的限制,它在 server 端,只會 cache 16 個群組,也就是說第 17 個以後的群組,它完全不會理會。這也可以說明,為什麼我在 server 端直接對 filesystem 讀寫是正常,但是使用 NFS 在 client 端時,就會出現 permission denied ,這個問題當然也會出現在其它 UNIX like 及 BSD 的系統。這個問題其實已經困擾好幾年了,但一直沒有個好方法解決,只能消極的不要讓 group 超過 16 個。隨著公司開的專案越來越多,最近實在是覺得一直幫 user 調 group 數量很煩,所以就再次查看看有沒有解決的辦法。天時、地利是站在我這邊的,我找到一篇說明很詳細的文章,它講解原因、無效的解決方式、正確解決方式、充要條件等等,有興趣的人一定要詳讀:
Solving the NFS 16-Group Limit Problem(https://xkyle.com/solving-the-nfs-16-group-limit-problem/)



充要條件:
NFS server 版本在 1.0.12 以上,可以使用指令 rpc.mountd -v 查詢
kernel 版本在 2.6.21 以上,可以使用指令 uname -r 查詢
PS:只要處理 NFS server 這台機器即可,這個問題跟 client 無關,client 端的版本是舊的也不會影響解決的效果。


範例:
我就以 CentOS 6 作為範例:(CentOS 5, CentOS4, CentOS3 ...... 都不符合充要條件,除非你自行make 新版的 NFS server 套件及 kernel)

增加 NFS server 分享 ( rpc.mountd )時的選項:--manage-gids
[root]# vi /etc/sysconfig/nfs修改這一行,預設選項為空白: RPCMOUNTDOPTS=""
請修改成:RPCMOUNTDOPTS="--manage-gids"

(儲存、離開)


重新啟動 NFS server 服務:
請注意!若有 client 已經 mount 了此 NFS server 的 export 目錄時,最好先 umount ,不然會出現卡住的狀況。

[root]# /etc/rc.d/init.d/nfs restart

[root]# service nfs restart

這樣就完成了,超簡單的!但是改完之後還是有上限 ,不過不是以 group 的個數為上限,而是以 1001 bytes 。我的實際測試如下:
a) gid 從 60001 ~ 600167,每一個 group 使用 5 個 bytes,是 167x5=835
再加每個 gid 之間需要有個分隔符號,所以再加上 166(167-1),835+166=1001

重新讀取 NFS server 端的 rpc.mountd 的 gid cache:
[root@tp9bak tmp]# date +%s > /proc/net/rpc/auth.unix.gid/flush


查看 NFS server 端的 rpc.mountd 的 gid cache :
[root@tp9bak tmp]# cat /proc/net/rpc/auth.unix.gid/content
#uid cnt: gids...
60001 167: 60001 60002 60003 60004 60005 60006 60007 60008 60009 60010 60011 60012 60013 60014 60015 60016 60017 60018 60019 60020 60021 60022 60023 60024 60025 60026 60027 60028 60029 60030 60031 60032 60033 60034 60035 60036 60037 60038 60039 60040 60041 60042 60043 60044 60045 60046 60047 60048 60049 60050 60051 60052 60053 60054 60055 60056 60057 60058 60059 60060 60061 60062 60063 60064 60065 60066 60067 60068 60069 60070 60071 60072 60073 60074 60075 60076 60077 60078 60079 60080 60081 60082 60083 60084 60085 60086 60087 60088 60089 60090 60091 60092 60093 60094 60095 60096 60097 60098 60099 60100 60101 60102 60103 60104 60105 60106 60107 60108 60109 60110 60111 60112 60113 60114 60115 60116 60117 60118 60119 60120 60121 60122 60123 60124 60125 60126 60127 60128 60129 60130 60131 60132 60133 60134 60135 60136 60137 60138 60139 60140 60141 60142 60143 60144 60145 60146 60147 60148 60149 60150 60151 60152 60153 60154 60155 60156 60157 60158 60159 60161 60162 60163 60164 60165 60166 60167


b) 上限真的是 1001 嗎?那就試試 1002 吧!
為了湊 1002 ,gid 範圍以下:
60001 ~ 600148 (148個)
    6149 ~ 6171 (23個)
148 x 5 + 23 x 4 + 148 + 23 - 1 = 1002

結果........client 端在讀寫時不會出現  permission denied ,而是會卡住,很慘的結果。而 NFS server 端的 rpc.mountd 的 gid cache 則會出現以下結果:
[root]# cat /proc/net/rpc/auth.unix.gid/content
#uid cnt: gids...
# 60001 0:


若沒有修改過之前,會發生你明明有個 user 加到 17 以上的 group ,可是這裡永遠只有列出前面 16 個的 gid,但是不至於完全沒有。但是修改後若 gid 的 bytes 數超過上限 1001 時,不是只列出前面 1001 bytes 的 gid ,反而一個也沒有,造成此 user 完全沒有 group 權限。