hdfs實作了posix的檔案目錄的權限模型。每個檔案和目錄都關聯一個owner和一個group。檔案或者目錄有獨立分開的權限來對應到owner,對應到組成員,對應到其他使用者。對于檔案來說,讀檔案需要r權限,寫或者append檔案需要w權限。對于目錄來說,展示目錄裡内容需要r權限,在目錄裡建立或删除檔案或目錄需要w權限,通路目錄裡的内容則需要x權限。
與posix模型相反的,hdfs沒有對檔案的setuid或者setgid位,因為hdfs檔案是不可執行的。對于目錄,為了簡化,也不設定setuid或者setgid位。目錄可以設定粘滞位,來保護目錄内的檔案不被其他非超級使用者和檔案owner的使用者删除或移動。對于檔案設定粘滞位是無效的。總的來說,檔案或目錄權限有其自身的模式。通常情況,對于權限模式是用unix的習慣來表示的,包括使用八進制的描述。當一個檔案或者目錄建立時,它的owner就是用戶端程序,它的group就是所在父目錄的group(這是bsd的規矩)。
hdfs也提供對于posix acl的可選的支援,以細粒度的規則來聲明特定使用者或組,以增強檔案權限。acl會在後面詳細讨論。
每個通路hdfs的用戶端程序都包含兩部分id,一部分是使用者名,另一部分是組清單。每當hdfs必須對一個用戶端程序通路一個檔案或目錄foo做權限檢查時,
如果使用者名比對了foo的owner,那麼校驗owner權限;
否則如果foo的group與組清單中的任意一個group比對,那麼校驗group權限;
否則校驗foo的其他權限。
如果權限檢查失敗,那麼該次用戶端操作失敗。
hadoop 0.22版本裡,支援兩種不同的操作模式來決定使用者身份,在屬性hadoop.security.authentication裡配置:
simple
kerberos
一旦使用者名已經決定了,那麼group清單會由組映射服務來決定,這個服務由hadoop.security.group.mapping配置屬性确定。預設實作是<code>org.apache.hadoop.security.jnibasedunixgroupsmappingwithfallback</code>,前提是java native interface (jni)可用。如果jni可用,實作方案會在hadoop内使用api來解析使用者的group清單。如果jni不可用,那麼shell實作方案<code>org.apache.hadoop.security.shellbasedunixgroupsmapping</code>會啟用。這一方案利用<code>bash -c groups</code>指令(linux/unix環境),或者<code>net group</code>指令(windows環境)來解析使用者組清單。
一個替代的實作,是通過直接連接配接一個ldap伺服器來解析group清單,這個實作通過類<code>org.apache.hadoop.security.ldapgroupsmapping</code>來完成。然而這個提供者使用起來有兩個要求,被要求的組在ldap上獨占式駐留,并且要求不在unix伺服器上物化實作。關于配置組映射服務的更多資訊參見javadoc。
對于hdfs,使用者到組的映射是通過namenode完成的。是以,namenode的主機系統配置決定了使用者的組映射。
注意,hdfs以字元串的形式存儲使用者群組的檔案和目錄資訊;不會像在unix系統上一樣對使用者群組id做數字的轉換。
每個檔案或者目錄操作都會傳完整的路徑名給到namenode,每個操作都會要求檢查權限。用戶端架構會隐式的關聯namenode連接配接和使用者id,來減少改變現有用戶端api的需求。一直以來,當一對檔案的操作成功後,再重複該操作可能會導緻失敗,因為檔案或者目錄也許已經不再存在了。比如,當用戶端一開始讀取一個檔案時,它會發起一個請求到namenode,用來定位第一個檔案塊的位置。第二個來尋找其他的塊的請求可能失敗。另一方面,删除檔案并不會撤銷已經知道檔案塊的用戶端的通路。随着權限的增加,一個用戶端對檔案的通路可能在請求之間撤銷。改變權限也不會撤銷已經知道檔案塊的用戶端的通路。
所有使用路徑作為參數的方法在權限校驗失敗後會抛出一個<code>accesscontrolexception</code>的異常。
新方法:
<code>public fsdataoutputstream create(path f, fspermission permission, boolean overwrite, int buffersize, short replication, long blocksize, progressable progress) throws ioexception;</code>
<code>public boolean mkdirs(path f, fspermission permission) throws ioexception;</code>
<code>public void setpermission(path p, fspermission permission) throws ioexception;</code>
<code>public void setowner(path p, string username, string groupname) throws ioexception;</code>
<code>public filestatus getfilestatus(path f) throws ioexception;</code>
會額外傳回使用者,組和與路徑相關的權限mode。
新的檔案或目錄的mode由作為配置參數的umask來限制。當使用<code>create(path, …)</code>方法(無權限參數)時,新檔案的mode是<code>0666 & ^umask</code>。當使用<code>mkdirs(path)</code>方法(無權限參數)建立新目錄時,新目錄的mode是<code>0777 & ^umask</code>。當使用新的<code>mkdirs(path, permission)</code>方法(權限參數為p)時,新目錄的mode是<code>p & ^umask & 0777</code>。
新操作:
<code>chmod [-r] mode file …</code>
隻有檔案的owner或者超級使用者允許來變更檔案的權限mode。
<code>chgrp [-r] group file …</code>
調用<code>chgrp</code>的使用者必須屬于對應的group并且是檔案的owner,或者是超級使用者。
<code>chown [-r] [owner][:[group]] file …</code>
隻有超級使用者能改變一個檔案的owner。
<code>ls file …</code>
<code>lsr file …</code>
輸出,并且輸出被格式化為顯示owner,group和權限mode。
超級使用者就是namenode程序本身。寬松的條件下,如果你啟動了namenode,你就是超級使用者。超級使用者擁有最大權限,對于超級使用者的權限檢查永久不會失敗。沒有人是永久的超級使用者,誰啟動namenode程序決定誰是超級使用者。hdfs超級使用者不一定是namenode主機的超級使用者,所有的叢集也不必擁有同一個超級使用者。同樣的,如果一個在個人電腦上嘗試性運作hdfs的人,也不需要任何配置就是該次運作的超級使用者。
此外,對應某個group的管理者由一個配置參數來指定。如果設定了,那麼該組的成員也将是超級使用者。
預設情況,web伺服器的識别是一個配置參數。namenode對于實際使用者的辨別并不知情,但是web伺服器就像由管理者標明的使用者一樣來運作。除非被選擇的id是超級使用者,否則部分命名空間将不可通路。
對于傳統的posix權限模型,hdfs也支援posix的acls。acls對于實作不同于正常使用者群組的組織層級權限需求非常有效。一條acl提供了一種方法來為特定的使用者或組設定不同的權限,不僅僅是檔案的owner和檔案的group。
預設情況,對于acls的支援是disabled,并且namenode不允許建立acls。要開啟acls,需要在namenode的配置裡設定dfs.namenode.acls.enabled為true。
一條acl包含了一組acl的項。每個acl項指定了一個使用者或組,然後授予或拒絕讀、寫和執行權限。舉例如下:
acl項包含了一個類型,一個可選的名字和一個權限字元串。為了顯示的需要,':'用來作為分隔符。在這個例子裡,檔案的owner擁有讀寫權限,檔案的group擁有讀和執行權限,其他人擁有讀權限。到此,這等價于設定檔案的權限bit為654。
除此之外,還有兩個擴充的acl項,一個對應特定的使用者bruce,一個對應特定的組sales,兩者都被授予了完全通路權限。mask是一種特殊的acl項,用來過濾授予特定使用者或組,也包含非特定的組的權限。在此例中,mask隻有讀權限,對應的幾個acl項也被相應的過濾,隻有讀權限。
每條acl必須有一個mask。如果使用者不提供mask,那麼系統會自動插入一條mask,該mask是通過union所有項的權限作為過濾項計算出來的。
對一個擁有acl的檔案運作<code>chmod</code>會導緻改變mask的權限。由于mask是一個過濾器,這有效的限制了所有擴充的acl項,而不是僅僅改變group項和可能丢失的其他擴充acl項。
該模型也區分“通路acl”和“預設acl”,通路acl定義了在權限檢查時的規則,而預設acl定義了新的子檔案或者子目錄建立時自動生效的acl項。舉例如下:
隻有目錄才會有預設acl。當新的檔案或子目錄建立時,系統自動複制父目錄的預設acl到其檔案或子目錄。子目錄同時會把該預設acl作為自己的預設acl。在此原則下,預設acl會傳遞到系統任意深度的目錄樹結構中。
對于子檔案或目錄準确的通路acl值是由mode參數過濾決定的。假設預設的umask是022,那麼對于新的檔案來說就是644,對于新目錄就是755。mode參數過濾對于未指定的使用者(檔案owner),mask和其他使用者的複制來的權限。使用這一特定的示例acl,建立一個新的子目錄,其權限mode是755,那麼mode過濾器對于最後的結果不起作用。然而,如果我們假設建立一個檔案mode是644,那mode過濾器會過濾新檔案的acl,導緻對于未指定使用者(檔案owner)擁有讀寫權限,對于mask擁有讀權限,對于其他使用者擁有讀權限。這個mask同樣作用于已制定名字的bruce和sales,其權限也将是隻讀。
注意,acl的複制發生在新目錄或檔案建立時。後續的對于父目錄的預設acl的變更不會影響已經存在的子檔案或目錄。
預設acl必須擁有最小的acl項集,包括未指定使用者(檔案owner),未指定group(檔案group)和其他使用者。如果使用者沒有提供上述這些項的設定,那麼系統自動複制通路acl中的對應項,或者如果沒有通路acl,就複制權限位。預設acl必須有mask。如上所述,如果mask未聲明,系統自動建立一個mask,此mask通過union所有項的權限過濾計算得出。
當一個檔案擁有acl時,對于權限的校驗算法修改為:
if使用者名與檔案owner比對,那麼檢測owner權限;
else if使用者名比對指定的使用者項,那麼檢測這些比對的使用者權限,這些權限先要經過mask過濾;
else if檔案的group與group清單的任意成員比對,并且經過mask過濾的權限授權通路,那麼使用這些權限;
else if指定的group項比對group清單中的任意成員,并且經過mask過濾的權限授權通路,那麼使用這些權限;
else if檔案group或者任意指定的group項與group清單中的任意成員比對,但是沒有被授權通路,那麼通路拒絕;
otherwise校驗檔案其他使用者的權限。
實作權限控制的最佳實踐是利用傳統的權限位,同時定義很小的一組acls來對可能的例外規則增強權限校驗。帶有acl的檔案權限校驗相比隻用權限位的檔案會導緻namenode額外的記憶體消耗。
<code>public void modifyaclentries(path path, list<aclentry> aclspec) throws ioexception;</code>
<code>public void removeaclentries(path path, list<aclentry> aclspec) throws ioexception;</code>
<code>public void public void removedefaultacl(path path) throws ioexception;</code>
<code>public void removeacl(path path) throws ioexception;</code>
<code>public void setacl(path path, list<aclentry> aclspec) throws ioexception;</code>
<code>public aclstatus getaclstatus(path path) throws ioexception;</code>
<code>hdfs dfs -getfacl [-r] <path></code>
顯示檔案和目錄的acls。如果一個目錄包含預設acl,那麼getfacl會顯示該預設acl。
<code>hdfs dfs -setfacl [-r] [-b|-k -m|-x <acl_spec> <path>]|[--set <acl_spec> <path>]</code>
設定檔案和目錄的acls。
<code>hdfs dfs -ls <args></code>
如果檔案或目錄包含acl,ls的輸出會附加一個'+'到權限字元串。