我被被派去做別的事情了,所以與hadoop相關的工作就只能擱下。寫篇總結,把最近遇到的和kerberos相關的東西列一下。
JAAS是Java 認證和授權服務(Java Authentication and Authorization Service)的縮寫,是PAM框架的Java實現。
javax.sercurity.auth.Subject是一個不可繼承的實體類,它表示單個實體的一組相關信息,與請求的來源相關。
javax.security.auth.Principal是一個接口,表示帶有不同類型憑證的標識,基本上來說,Principal可以是任意對象。
JAAS的授權機制主要就是圍繞着Subject和Principal。關於JAAS比較詳細的參考是這里:http://docs.oracle.com/javase/6/docs/technotes/guides/security/jaas/JAASRefGuide.html
幾個比較重要的java屬性:
java.security.krb5.realm
java.security.krb5.kdc
java.security.krb5.conf
hadoop的身份認證和授權都是建立在JAAS之上。
hadoop.security.authentication屬性有2種值:
simple: Security is disabled。
kerberos: Security is enabled。
org.apache.hadoop.security.UserGroupInformation有一個靜態方法:getCurrentUser()。它會返回一個UserGroupInformation類的實例(以下簡稱UGI)。如果subject為空,或者這個subject中與org.apache.hadoop.security.User對應的Principal為空,那么說明尚未登錄過,調用getLoginUser()創建UserGroupInformation的實例。
getLoginUser()的流程:
1.創建LoginContext:
LoginContext是由UserGroupInformation.newLoginContext(String, Subject)方法創建的。LoginContext的name有三種取值:
- hadoop-user-kerberos
- hadoop-keytab-kerberos
- hadoop-simple 即沒有啟用kerberos認證
這個name的主要作用是作為appName傳遞給UserGroupInformation.HadoopConfiguration.getAppConfigurationEntry(String appName)方法。
kerberos具體的login邏輯,還是交給com.sun.security.auth.module.Krb5LoginModule完成的。
如果name是hadoop-user-kerberos,那么useTicketCache是true,否則是false。
如果useTicketCache是true,那么會從 /tmp/krb5cc_${uid}或者用戶的home目錄下krb5cc_${user.name}中讀ticket信息。這種情況下,hadoop會創建一個新線程用kinit -R去刷新這個ticket cache(默認是總有效時間過了80%之后刷新),並且,jdk也會嘗試利用它的ticket cache進行renew。
如果name是hadoop-keytab-kerberos,那么就沒人幫忙renew了。
subject:
callbackHandler: 空
Configuration: UserGroupInformation.HadoopConfiguration的實例。
2.login.login();
這個會調用HadoopLoginModule的login()和commit()方法。
HadoopLoginModule的login()方法是一個空函數,只打印了一行調試日志 LOG.debug("hadoop login");
commit()方法負責把Principal添加到Subject中。
此時一個首要問題是username是什么?
在使用了kerberos的情況下,從javax.security.auth.kerberos.KerberosPrincipal的實例獲取username。
在未使用kerberos的情況下,優先讀取HADOOP_USER_NAME這個系統環境變量,如果不為空,那么拿它作username。否則,讀取HADOOP_USER_NAME這個java環境變量。否則,從com.sun.security.auth.NTUserPrincipal或者com.sun.security.auth.UnixPrincipal的實例獲取username。
如果以上嘗試都失敗,那么拋出異常LoginException("Can't find user name")。
最終拿username構造org.apache.hadoop.security.User的實例添加到Subject中。
測試登錄:
HADOOP_JAAS_DEBUG=true HADOOP_ROOT_LOGGER=DEBUG,console bin/hadoop org.apache.hadoop.security.UserGroupInformation
其中,UGI應該是這樣的形式:
UGI: host/xx.xx.xx.com@xx.xx.com (auth:KERBEROS)
如果是下面這樣,就說明錯了
12/03/28 18:44:52 DEBUG security.Groups: Returning fetched groups for 'app_admin'
Groups: app_admin
UGI: app_admin (auth:KERBEROS)
Auth method KERBEROS
Keytab false
據我觀察,目前好像只有hadoop內部的通信可以用keytab。如果想在shell下執行bin/hdfs什么的,還是得手動調用kinit。而且,我不知道hadoop用keytab登錄后,把Ticket cache放哪了。好像跟系統默認的不一樣。
在執行kinit的時候,如果沒有root權限,可以用KRB5_CONFIG這個環境變量來指定krb5.conf的位置。這個在kinit的文檔中並沒有提到,它只提到了KRB5CCNAME和KRBTKFILE。