HDFS配置Kerberos認證
2014.11.04本文主要記錄 CDH Hadoop 集群上配置 HDFS 集成 Kerberos 的過程,包括 Kerberos 的安裝和 Hadoop 相關配置修改說明。
注意:
下面第一、二部分內容,摘抄自《Hadoop的kerberos的實踐部署》,主要是為了對 Hadoop 的認證機制和 Kerberos 認證協議做個簡單介紹。在此,對原作者表示感謝。
1. Hadoop 的認證機制
簡單來說,沒有做 kerberos 認證的 Hadoop,只要有 client 端就能夠連接上。而且,通過一個有 root 的權限的內網機器,通過創建對應的 linux 用戶,就能夠得到 Hadoop 集群上對應的權限。
而實行 Kerberos 后,任意機器的任意用戶都必須現在 Kerberos 的 KDC 中有記錄,才允許和集群中其它的模塊進行通信。
詳細介紹請參考 Hadoop安全機制研究
2. Kerberos 認證協議
Kerberos 是一種網絡認證協議,其設計目標是通過密鑰系統為客戶機/服務器應用程序提供強大的認證服務。
使用 Kerberos 時,一個客戶端需要經過三個步驟來獲取服務:
- 認證:客戶端向認證服務器發送一條報文,并獲取一個含時間戳的 Ticket-Granting Ticket(TGT)。
- 授權:客戶端使用 TGT 向 Ticket-Granting Server(TGS)請求一個服務 Ticket。
- 服務請求:客戶端向服務器出示服務 Ticket ,以證實自己的合法性。
為此,Kerberos 需要 The Key Distribution Centers(KDC)來進行認證。KDC 只有一個 Master,可以帶多個 slaves 機器。slaves 機器僅進行普通驗證。Mater 上做的修改需要自動同步到 slaves。
另外,KDC 需要一個 admin,來進行日常的管理操作。這個 admin 可以通過遠程或者本地方式登錄。
3. 搭建 Kerberos
3.1 環境
我們在三個節點的服務器上安裝 Kerberos,這三個節點上安裝了 hadoop 集群,安裝 hadoop 過程見:使用yum安裝CDH Hadoop集群。這三個節點機器分布為:cdh1、cdh2、cdh3。
- 操作系統:CentOs 6.6
- 運行用戶:root
3.2 安裝過程
3.2.1 準備工作
確認添加主機名解析到 /etc/hosts
文件中。
$ cat /etc/hosts 127.0.0.1 localhost 192.168.56.121 cdh1 192.168.56.122 cdh2 192.168.56.123 cdh3
注意:hostname 請使用小寫,要不然在集成 kerberos 時會出現一些錯誤。
3.2.2 安裝 kdc server
在 KDC (這里是 cdh1 ) 上安裝包 krb5、krb5-server 和 krb5-client。
$ yum install krb5-server krb5-libs krb5-auth-dialog krb5-workstation -y
在其他節點(cdh1、cdh2、cdh3)安裝 krb5-devel、krb5-workstation :
$ ssh cdh1 "yum install krb5-devel krb5-workstation -y" $ ssh cdh2 "yum install krb5-devel krb5-workstation -y" $ ssh cdh3 "yum install krb5-devel krb5-workstation -y"
3.2.3 修改配置文件
kdc 服務器涉及到三個配置文件:
/etc/krb5.conf /var/kerberos/krb5kdc/kdc.conf /var/kerberos/krb5kdc/kadm5.acl
配置 Kerberos 的一種方法是編輯配置文件 /etc/krb5.conf。默認安裝的文件中包含多個示例項。
$ cat /etc/krb5.conf [logging] default = FILE:/var/log/krb5libs.log kdc = FILE:/var/log/krb5kdc.log admin_server = FILE:/var/log/kadmind.log [libdefaults] default_realm = JAVACHEN.COM dns_lookup_realm = false dns_lookup_kdc = false clockskew = 120 ticket_lifetime = 24h renew_lifetime = 7d forwardable = true renewable = true udp_preference_limit = 1 default_tgs_enctypes = arcfour-hmac default_tkt_enctypes = arcfour-hmac [realms] JAVACHEN.COM = { kdc = cdh1:88 admin_server = cdh1:749 } [domain_realm] .javachen.com = JAVACHEN.COM www.javachen.com = JAVACHEN.COM [kdc] profile=/var/kerberos/krb5kdc/kdc.conf
說明:
[logging]
:表示 server 端的日志的打印位置[libdefaults]
:每種連接的默認配置,需要注意以下幾個關鍵的小配置default_realm = JAVACHEN.COM
:設置 Kerberos 應用程序的默認領域。如果您有多個領域,只需向 [realms] 節添加其他的語句。udp_preference_limit= 1
:禁止使用 udp 可以防止一個Hadoop中的錯誤clockskew
:時鐘偏差是不完全符合主機系統時鐘的票據時戳的容差,超過此容差將不接受此票據。通常,將時鐘扭斜設置為 300 秒(5 分鐘)。這意味著從服務器的角度看,票證的時間戳與它的偏差可以是在前后 5 分鐘內。ticket_lifetime
: 表明憑證生效的時限,一般為24小時。renew_lifetime
: 表明憑證最長可以被延期的時限,一般為一個禮拜。當憑證過期之后,對安全認證的服務的后續訪問則會失敗。
[realms]
:列舉使用的 realm。kdc
:代表要 kdc 的位置。格式是機器:端口
admin_server
:代表 admin 的位置。格式是機器:端口
default_domain
:代表默認的域名
[appdefaults]
:可以設定一些針對特定應用的配置,覆蓋默認配置。
修改 /var/kerberos/krb5kdc/kdc.conf
,該文件包含 Kerberos 的配置信息。例如,KDC 的位置,Kerbero 的 admin 的realms 等。需要所有使用的 Kerberos 的機器上的配置文件都同步。這里僅列舉需要的基本配置。詳細介紹參考:krb5conf
$ cat /var/kerberos/krb5kdc/kdc.conf [kdcdefaults] v4_mode = nopreauth kdc_ports = 88 kdc_tcp_ports = 88 [realms] JAVACHEN.COM = { #master_key_type = aes256-cts acl_file = /var/kerberos/krb5kdc/kadm5.acl dict_file = /usr/share/dict/words admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab supported_enctypes = des3-hmac-sha1:normal arcfour-hmac:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal des-cbc-crc:v4 des-cbc-crc:afs3 max_life = 24h max_renewable_life = 10d default_principal_flags = +renewable, +forwardable }
說明:
JAVACHEN.COM
: 是設定的 realms。名字隨意。Kerberos 可以支持多個 realms,會增加復雜度。大小寫敏感,一般為了識別使用全部大寫。這個 realms 跟機器的 host 沒有大關系。master_key_type
:和supported_enctypes
默認使用aes256-cts
。由于,JAVA 使用aes256-cts
驗證方式需要安裝額外的 jar 包(后面再做說明)。推薦不使用,并且刪除 aes256-cts。acl_file
:標注了 admin 的用戶權限,需要用戶自己創建。文件格式是:Kerberos_principal permissions [target_principal] [restrictions]
supported_enctypes
:支持的校驗方式。admin_keytab
:KDC 進行校驗的 keytab。
關于AES-256加密:
對于使用 centos5. 6及以上的系統,默認使用 AES-256 來加密的。這就需要集群中的所有節點上安裝 Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy File。
下載的文件是一個 zip 包,解開后,將里面的兩個文件放到下面的目錄中:
$JAVA_HOME/jre/lib/security
為了能夠不直接訪問 KDC 控制臺而從 Kerberos 數據庫添加和刪除主體,請對 Kerberos 管理服務器指示允許哪些主體執行哪些操作。通過編輯文件 /var/lib/kerberos/krb5kdc/kadm5.acl 完成此操作。ACL(訪問控制列表)允許您精確指定特權。
$ cat /var/kerberos/krb5kdc/kadm5.acl */admin@JAVACHEN.COM *
3.2.4 同步配置文件
將 kdc 中的 /etc/krb5.conf
拷貝到集群中其他服務器即可。
$ scp /etc/krb5.conf cdh2:/etc/krb5.conf $ scp /etc/krb5.conf cdh3:/etc/krb5.conf
請確認集群如果關閉了 selinux。
3.2.5 創建數據庫
在 cdh1 上運行初始化數據庫命令。其中 -r
指定對應 realm。
$ kdb5_util create -r JAVACHEN.COM -s
出現 Loading random data
的時候另開個終端執行點消耗CPU的命令如 cat /dev/sda > /dev/urandom
可以加快隨機數采集。
該命令會在 /var/kerberos/krb5kdc/
目錄下創建 principal 數據庫。
如果遇到數據庫已經存在的提示,可以把 /var/kerberos/krb5kdc/
目錄下的 principal 的相關文件都刪除掉。默認的數據庫名字都是 principal。可以使用 -d
指定數據庫名字。
3.2.6 啟動服務
在 cdh1 節點上運行:
$ chkconfig --level 35 krb5kdc on $ chkconfig --level 35 kadmin on $ service krb5kdc start $ service kadmin start
3.2.7 創建 kerberos 管理員
關于 kerberos 的管理,可以使用 kadmin.local
或 kadmin
,至于使用哪個,取決于賬戶和訪問權限:
- 如果有訪問 kdc 服務器的 root 權限,但是沒有 kerberos admin 賬戶,使用
kadmin.local
- 如果沒有訪問 kdc 服務器的 root 權限,但是用 kerberos admin 賬戶,使用
kadmin
在 cdh1 上創建遠程管理的管理員:
#手動輸入兩次密碼,這里密碼為 root $ kadmin.local -q "addprinc root/admin" # 也可以不用手動輸入密碼 $ echo -e "root\nroot" | kadmin.local -q "addprinc root/admin"
系統會提示輸入密碼,密碼不能為空,且需妥善保存。
3.2.8 測試 kerberos
以下內容僅僅是為了測試,你可以直接跳到下部分內容。
查看當前的認證用戶:
# 查看principals $ kadmin: list_principals # 添加一個新的 principal kadmin: addprinc user1 WARNING: no policy specified for user1@JAVACHEN.COM; defaulting to no policy Enter password for principal "user1@JAVACHEN.COM": Re-enter password for principal "user1@JAVACHEN.COM": Principal "user1@JAVACHEN.COM" created. # 刪除 principal kadmin: delprinc user1 Are you sure you want to delete the principal "user1@JAVACHEN.COM"? (yes/no): yes Principal "user1@JAVACHEN.COM" deleted. Make sure that you have removed this principal from all ACLs before reusing. kadmin: exit
也可以直接通過下面的命令來執行:
# 提示需要輸入密碼 $ kadmin -p root/admin -q "list_principals" $ kadmin -p root/admin -q "addprinc user2" $ kadmin -p root/admin -q "delprinc user2" # 不用輸入密碼 $ kadmin.local -q "list_principals" $ kadmin.local -q "addprinc user2" $ kadmin.local -q "delprinc user2"
創建一個測試用戶 test,密碼設置為 test:
$ echo -e "test\ntest" | kadmin.local -q "addprinc test"
獲取 test 用戶的 ticket:
# 通過用戶名和密碼進行登錄 $ kinit test Password for test@JAVACHEN.COM: $ klist -e Ticket cache: FILE:/tmp/krb5cc_0 Default principal: test@JAVACHEN.COM Valid starting Expires Service principal 11/07/14 15:29:02 11/08/14 15:29:02 krbtgt/JAVACHEN.COM@JAVACHEN.COM renew until 11/17/14 15:29:02, Etype (skey, tkt): AES-128 CTS mode with 96-bit SHA-1 HMAC, AES-128 CTS mode with 96-bit SHA-1 HMAC Kerberos 4 ticket cache: /tmp/tkt0 klist: You have no tickets cached
銷毀該 test 用戶的 ticket:
$ kdestroy $ klist klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_0) Kerberos 4 ticket cache: /tmp/tkt0 klist: You have no tickets cached
更新 ticket:
$ kinit root/admin Password for root/admin@JAVACHEN.COM: $ klist Ticket cache: FILE:/tmp/krb5cc_0 Default principal: root/admin@JAVACHEN.COM Valid starting Expires Service principal 11/07/14 15:33:57 11/08/14 15:33:57 krbtgt/JAVACHEN.COM@JAVACHEN.COM renew until 11/17/14 15:33:57 Kerberos 4 ticket cache: /tmp/tkt0 klist: You have no tickets cached $ kinit -R $ klist Ticket cache: FILE:/tmp/krb5cc_0 Default principal: root/admin@JAVACHEN.COM Valid starting Expires Service principal 11/07/14 15:34:05 11/08/14 15:34:05 krbtgt/JAVACHEN.COM@JAVACHEN.COM renew until 11/17/14 15:33:57 Kerberos 4 ticket cache: /tmp/tkt0 klist: You have no tickets cached
抽取密鑰并將其儲存在本地 keytab 文件 /etc/krb5.keytab 中。這個文件由超級用戶擁有,所以您必須是 root 用戶才能在 kadmin shell 中執行以下命令:
$ kadmin.local -q "ktadd kadmin/admin" $ klist -k /etc/krb5.keytab Keytab name: FILE:/etc/krb5.keytab KVNO Principal ---- -------------------------------------------------------------------------- 3 kadmin/admin@LASHOU-INC.COM 3 kadmin/admin@LASHOU-INC.COM 3 kadmin/admin@LASHOU-INC.COM 3 kadmin/admin@LASHOU-INC.COM 3 kadmin/admin@LASHOU-INC.COM
4. HDFS 上配置 kerberos
4.1 創建認證規則
在 Kerberos 安全機制里,一個 principal 就是 realm 里的一個對象,一個 principal 總是和一個密鑰(secret key)成對出現的。
這個 principal 的對應物可以是 service,可以是 host,也可以是 user,對于 Kerberos 來說,都沒有區別。
Kdc(Key distribute center) 知道所有 principal 的 secret key,但每個 principal 對應的對象只知道自己的那個 secret key 。這也是“共享密鑰“的由來。
對于 hadoop,principals 的格式為 username/fully.qualified.domain.name@YOUR-REALM.COM
。
通過 yum 源安裝的 cdh 集群中,NameNode 和 DataNode 是通過 hdfs 啟動的,故為集群中每個服務器節點添加兩個principals:hdfs、HTTP。
在 KCD server 上(這里是 cdh1)創建 hdfs principal:
kadmin.local -q "addprinc -randkey hdfs/cdh1@JAVACHEN.COM" kadmin.local -q "addprinc -randkey hdfs/cdh2@JAVACHEN.COM" kadmin.local -q "addprinc -randkey hdfs/cdh3@JAVACHEN.COM"
-randkey
標志沒有為新 principal 設置密碼,而是指示 kadmin 生成一個隨機密鑰。之所以在這里使用這個標志,是因為此 principal 不需要用戶交互。它是計算機的一個服務器帳戶。
創建 HTTP principal:
kadmin.local -q "addprinc -randkey HTTP/cdh1@JAVACHEN.COM" kadmin.local -q "addprinc -randkey HTTP/cdh2@JAVACHEN.COM" kadmin.local -q "addprinc -randkey HTTP/cdh3@JAVACHEN.COM"
創建完成后,查看:
$ kadmin.local -q "listprincs"
4.2 創建keytab文件
keytab 是包含 principals 和加密 principal key 的文件。
keytab 文件對于每個 host 是唯一的,因為 key 中包含 hostname。keytab 文件用于不需要人工交互和保存純文本密碼,實現到 kerberos 上驗證一個主機上的 principal。
因為服務器上可以訪問 keytab 文件即可以以 principal 的身份通過 kerberos 的認證,所以,keytab 文件應該被妥善保存,應該只有少數的用戶可以訪問
創建包含 hdfs principal 和 host principal 的 hdfs keytab:
xst -norandkey -k hdfs.keytab hdfs/fully.qualified.domain.name host/fully.qualified.domain.name
創建包含 mapred principal 和 host principal 的 mapred keytab:
xst -norandkey -k mapred.keytab mapred/fully.qualified.domain.name host/fully.qualified.domain.name
注意:
上面的方法使用了xst的norandkey參數,有些kerberos不支持該參數。
當不支持該參數時有這樣的提示:Principal -norandkey does not exist.
,需要使用下面的方法來生成keytab文件。
在 cdh1 節點,即 KDC server 節點上執行下面命令:
$ cd /var/kerberos/krb5kdc/ $ kadmin.local -q "xst -k hdfs-unmerged.keytab hdfs/cdh1@JAVACHEN.COM" $ kadmin.local -q "xst -k hdfs-unmerged.keytab hdfs/cdh2@JAVACHEN.COM" $ kadmin.local -q "xst -k hdfs-unmerged.keytab hdfs/cdh3@JAVACHEN.COM" $ kadmin.local -q "xst -k HTTP.keytab HTTP/cdh1@JAVACHEN.COM" $ kadmin.local -q "xst -k HTTP.keytab HTTP/cdh2@JAVACHEN.COM" $ kadmin.local -q "xst -k HTTP.keytab HTTP/cdh3@JAVACHEN.COM"
這樣,就會在 /var/kerberos/krb5kdc/
目錄下生成 hdfs-unmerged.keytab
和 HTTP.keytab
兩個文件,接下來使用 ktutil
合并者兩個文件為 hdfs.keytab
。
$ cd /var/kerberos/krb5kdc/ $ ktutil ktutil: rkt hdfs-unmerged.keytab ktutil: rkt HTTP.keytab ktutil: wkt hdfs.keytab
使用 klist 顯示 hdfs.keytab 文件列表:
$ klist -ket hdfs.keytab Keytab name: FILE:hdfs.keytab KVNO Timestamp Principal ---- ----------------- -------------------------------------------------------- 2 11/13/14 10:40:18 hdfs/cdh1@JAVACHEN.COM (des3-cbc-sha1) 2 11/13/14 10:40:18 hdfs/cdh1@JAVACHEN.COM (arcfour-hmac) 2 11/13/14 10:40:18 hdfs/cdh1@JAVACHEN.COM (des-hmac-sha1) 2 11/13/14 10:40:18 hdfs/cdh1@JAVACHEN.COM (des-cbc-md5) 4 11/13/14 10:40:18 hdfs/cdh2@JAVACHEN.COM (des3-cbc-sha1) 4 11/13/14 10:40:18 hdfs/cdh2@JAVACHEN.COM (arcfour-hmac) 4 11/13/14 10:40:18 hdfs/cdh2@JAVACHEN.COM (des-hmac-sha1) 4 11/13/14 10:40:18 hdfs/cdh2@JAVACHEN.COM (des-cbc-md5) 4 11/13/14 10:40:18 hdfs/cdh3@JAVACHEN.COM (des3-cbc-sha1) 4 11/13/14 10:40:18 hdfs/cdh3@JAVACHEN.COM (arcfour-hmac) 4 11/13/14 10:40:18 hdfs/cdh3@JAVACHEN.COM (des-hmac-sha1) 4 11/13/14 10:40:18 hdfs/cdh3@JAVACHEN.COM (des-cbc-md5) 3 11/13/14 10:40:18 HTTP/cdh1@JAVACHEN.COM (des3-cbc-sha1) 3 11/13/14 10:40:18 HTTP/cdh1@JAVACHEN.COM (arcfour-hmac) 3 11/13/14 10:40:18 HTTP/cdh1@JAVACHEN.COM (des-hmac-sha1) 3 11/13/14 10:40:18 HTTP/cdh1@JAVACHEN.COM (des-cbc-md5) 3 11/13/14 10:40:18 HTTP/cdh2@JAVACHEN.COM (des3-cbc-sha1) 3 11/13/14 10:40:18 HTTP/cdh2@JAVACHEN.COM (arcfour-hmac) 3 11/13/14 10:40:18 HTTP/cdh2@JAVACHEN.COM (des-hmac-sha1) 3 11/13/14 10:40:18 HTTP/cdh2@JAVACHEN.COM (des-cbc-md5) 3 11/13/14 10:40:18 HTTP/cdh3@JAVACHEN.COM (des3-cbc-sha1) 3 11/13/14 10:40:18 HTTP/cdh3@JAVACHEN.COM (arcfour-hmac) 3 11/13/14 10:40:18 HTTP/cdh3@JAVACHEN.COM (des-hmac-sha1) 3 11/13/14 10:40:18 HTTP/cdh3@JAVACHEN.COM (des-cbc-md5)
驗證是否正確合并了key,使用合并后的keytab,分別使用hdfs和host principals來獲取證書。
$ kinit -k -t hdfs.keytab hdfs/cdh1@JAVACHEN.COM $ kinit -k -t hdfs.keytab HTTP/cdh1@JAVACHEN.COM
如果出現錯誤:kinit: Key table entry not found while getting initial credentials
,
則上面的合并有問題,重新執行前面的操作。
4.3 部署kerberos keytab文件
拷貝 hdfs.keytab 文件到其他節點的 /etc/hadoop/conf 目錄
$ cd /var/kerberos/krb5kdc/ $ scp hdfs.keytab cdh1:/etc/hadoop/conf $ scp hdfs.keytab cdh2:/etc/hadoop/conf $ scp hdfs.keytab cdh3:/etc/hadoop/conf
并設置權限,分別在 cdh1、cdh2、cdh3 上執行:
$ ssh cdh1 "chown hdfs:hadoop /etc/hadoop/conf/hdfs.keytab ;chmod 400 /etc/hadoop/conf/hdfs.keytab" $ ssh cdh2 "chown hdfs:hadoop /etc/hadoop/conf/hdfs.keytab ;chmod 400 /etc/hadoop/conf/hdfs.keytab" $ ssh cdh3 "chown hdfs:hadoop /etc/hadoop/conf/hdfs.keytab ;chmod 400 /etc/hadoop/conf/hdfs.keytab"
由于 keytab 相當于有了永久憑證,不需要提供密碼(如果修改kdc中的principal的密碼,則該keytab就會失效),所以其他用戶如果對該文件有讀權限,就 可以冒充 keytab 中指定的用戶身份訪問 hadoop,所以 keytab 文件需要確保只對 owner 有讀權限(0400)
4.4 修改 hdfs 配置文件
先停止集群:
$ for x in `cd /etc/init.d ; ls hive-*` ; do sudo service $x stop ; done $ for x in `cd /etc/init.d ; ls impala-*` ; do sudo service $x stop ; done $ for x in `cd /etc/init.d ; ls hadoop-*` ; do sudo service $x stop ; done $ for x in `cd /etc/init.d ; ls zookeeper-*` ; do sudo service $x stop ; done
在集群中所有節點的 core-site.xml 文件中添加下面的配置:
<property> <name>hadoop.security.authentication</name> <value>kerberos</value> </property> <property> <name>hadoop.security.authorization</name> <value>true</value> </property>
在集群中所有節點的 hdfs-site.xml 文件中添加下面的配置:
<property> <name>dfs.block.access.token.enable</name> <value>true</value> </property> <property> <name>dfs.datanode.data.dir.perm</name> <value>700</value> </property> <property> <name>dfs.namenode.keytab.file</name> <value>/etc/hadoop/conf/hdfs.keytab</value> </property> <property> <name>dfs.namenode.kerberos.principal</name> <value>hdfs/_HOST@JAVACHEN.COM</value> </property> <property> <name>dfs.namenode.kerberos.https.principal</name> <value>HTTP/_HOST@JAVACHEN.COM</value> </property> <property> <name>dfs.datanode.address</name> <value>0.0.0.0:1004</value> </property> <property> <name>dfs.datanode.http.address</name> <value>0.0.0.0:1006</value> </property> <property> <name>dfs.datanode.keytab.file</name> <value>/etc/hadoop/conf/hdfs.keytab</value> </property> <property> <name>dfs.datanode.kerberos.principal</name> <value>hdfs/_HOST@JAVACHEN.COM</value> </property> <property> <name>dfs.datanode.kerberos.https.principal</name> <value>HTTP/_HOST@JAVACHEN.COM</value> </property>
如果想開啟 SSL,請添加(本文不對這部分做說明):
<property> <name>dfs.http.policy</name> <value>HTTPS_ONLY</value> </property>
如果 HDFS 配置了 QJM HA,則需要添加(另外,你還要在 zookeeper 上配置 kerberos):
<property> <name>dfs.journalnode.keytab.file</name> <value>/etc/hadoop/conf/hdfs.keytab</value> </property> <property> <name>dfs.journalnode.kerberos.principal</name> <value>hdfs/_HOST@JAVACHEN.COM</value> </property> <property> <name>dfs.journalnode.kerberos.internal.spnego.principal</name> <value>HTTP/_HOST@JAVACHEN.COM</value> </property>
如果配置了 WebHDFS,則添加:
<property> <name>dfs.webhdfs.enabled</name> <value>true</value> </property> <property> <name>dfs.web.authentication.kerberos.principal</name> <value>HTTP/_HOST@JAVACHEN.COM</value> </property> <property> <name>dfs.web.authentication.kerberos.keytab</name> <value>/etc/hadoop/conf/hdfs.keytab</value> </property>
配置中有幾點要注意的:
- 1.
dfs.datanode.address
表示 data transceiver RPC server 所綁定的 hostname 或 IP 地址,如果開啟 security,端口號必須小于1024
(privileged port),否則的話啟動 datanode 時候會報Cannot start secure cluster without privileged resources
錯誤 - 2. principal 中的 instance 部分可以使用
_HOST
標記,系統會自動替換它為全稱域名 - 3. 如果開啟了 security, hadoop 會對 hdfs block data(由
dfs.data.dir
指定)做 permission check,方式用戶的代碼不是調用hdfs api而是直接本地讀block data,這樣就繞過了kerberos和文件權限驗證,管理員可以通過設置dfs.datanode.data.dir.perm
來修改 datanode 文件權限,這里我們設置為700
4.5 檢查集群上的 HDFS 和本地文件的權限
請參考 Verify User Accounts and Groups in CDH 5 Due to Security 或者 Hadoop in Secure Mode。
4.6 啟動 NameNode
啟動之前,請確認 JCE jar 已經替換,請參考前面的說明。
在每個節點上獲取 root 用戶的 ticket,這里 root 為之前創建的 root/admin 的密碼。
$ ssh cdh1 "echo root|kinit root/admin" $ ssh cdh1 "echo root|kinit root/admin" $ ssh cdh1 "echo root|kinit root/admin"
獲取 cdh1的 ticket:
$ kinit -k -t /etc/hadoop/conf/hdfs.keytab hdfs/cdh1@JAVACHEN.COM
如果出現下面異常 kinit: Password incorrect while getting initial credentials
,則重新導出 keytab 再試試。
然后啟動服務,觀察日志:
$ /etc/init.d/hadoop-hdfs-namenode start
驗證 NameNode 是否啟動,一是打開 web 界面查看啟動狀態,一是運行下面命令查看 hdfs:
$ hadoop fs -ls / Found 4 items drwxrwxrwx - yarn hadoop 0 2014-06-26 15:24 /logroot drwxrwxrwt - hdfs hadoop 0 2014-11-04 10:44 /tmp drwxr-xr-x - hdfs hadoop 0 2014-08-10 10:53 /user drwxr-xr-x - hdfs hadoop 0 2013-05-20 22:52 /var
如果在你的憑據緩存中沒有有效的 kerberos ticket,執行上面命令將會失敗,將會出現下面的錯誤:
14/11/04 12:08:12 WARN ipc.Client: Exception encountered while connecting to the server : javax.security.sasl.SaslException: GSS initiate failed [Caused by GS***ception: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)] Bad connection to FS. command aborted. exception: Call to cdh1/192.168.56.121:8020 failed on local exception: java.io.IOException: javax.security.sasl.SaslException: GSS initiate failed [Caused by GS***ception: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)]
4.6 啟動DataNode
DataNode 需要通過 JSVC 啟動。首先檢查是否安裝了 JSVC 命令,然后配置環境變量。
在 cdh1 節點查看是否安裝了 JSVC:
$ ls /usr/lib/bigtop-utils/ bigtop-detect-classpath bigtop-detect-javahome bigtop-detect-javalibs jsvc
然后編輯 /etc/default/hadoop-hdfs-datanode
,取消對下面的注釋并添加一行設置 JSVC_HOME
,修改如下:
export HADOOP_SECURE_DN_USER=hdfs export HADOOP_SECURE_DN_PID_DIR=/var/run/hadoop-hdfs export HADOOP_SECURE_DN_LOG_DIR=/var/log/hadoop-hdfs export JSVC_HOME=/usr/lib/bigtop-utils
將該文件同步到其他節點:
$ scp /etc/default/hadoop-hdfs-datanode cdh2:/etc/default/hadoop-hdfs-datanode $ scp /etc/default/hadoop-hdfs-datanode cdh3:/etc/default/hadoop-hdfs-datanode
分別在 cdh2、cdh3 獲取 ticket 然后啟動服務:
#root 為 root/admin 的密碼 $ ssh cdh1 "kinit -k -t /etc/hadoop/conf/hdfs.keytab hdfs/cdh1@JAVACHEN.COM; service hadoop-hdfs-datanode start" $ ssh cdh2 "kinit -k -t /etc/hadoop/conf/hdfs.keytab hdfs/cdh2@JAVACHEN.COM; service hadoop-hdfs-datanode start" $ ssh cdh3 "kinit -k -t /etc/hadoop/conf/hdfs.keytab hdfs/cdh3@JAVACHEN.COM; service hadoop-hdfs-datanode start"
觀看 cdh1 上 NameNode 日志,出現下面日志表示 DataNode 啟動成功:
14/11/04 17:21:41 INFO security.UserGroupInformation: Login successful for user hdfs/cdh2@JAVACHEN.COM using keytab file /etc/hadoop/conf/hdfs.keytab
5. 其他
5.1 批量生成 keytab
為了方便批量生成 keytab,寫了一個腳本,如下:
#!/bin/bash DNS=LASHOU.COM hostname=`hostname -i` yum install krb5-server krb5-libs krb5-auth-dialog krb5-workstation -y rm -rf /var/kerberos/krb5kdc/*.keytab /var/kerberos/krb5kdc/prin* kdb5_util create -r LASHOU.COM -s chkconfig --level 35 krb5kdc on chkconfig --level 35 kadmin on service krb5kdc restart service kadmin restart echo -e "root\nroot" | kadmin.local -q "addprinc root/admin" for host in `cat /etc/hosts|grep 10|grep -v $hostname|awk '{print $2}'` ;do for user in hdfs hive; do kadmin.local -q "addprinc -randkey $user/$host@$DNS" kadmin.local -q "xst -k /var/kerberos/krb5kdc/$user-un.keytab $user/$host@$DNS" done for user in HTTP lashou yarn mapred impala zookeeper sentry llama zkcli ; do kadmin.local -q "addprinc -randkey $user/$host@$DNS" kadmin.local -q "xst -k /var/kerberos/krb5kdc/$user.keytab $user/$host@$DNS" done done cd /var/kerberos/krb5kdc/ echo -e "rkt lashou.keytab\nrkt hdfs-un.keytab\nrkt HTTP.keytab\nwkt hdfs.keytab" | ktutil echo -e "rkt lashou.keytab\nrkt hive-un.keytab\nwkt hive.keytab" | ktutil #kerberos 重新初始化之后,還需要添加下面代碼用于集成 ldap kadmin.local -q "addprinc ldapadmin@JAVACHEN.COM" kadmin.local -q "addprinc -randkey ldap/cdh1@JAVACHEN.COM" kadmin.local -q "ktadd -k /etc/openldap/ldap.keytab ldap/cdh1@JAVACHEN.COM" #如果安裝了 openldap ,重啟 slapd /etc/init.d/slapd restart #測試 ldap 是否可以正常使用 ldapsearch -x -b 'dc=javachen,dc=com'
以下腳本用于在每個客戶端上獲得 root/admin 的 ticket,其密碼為 root:
#!/bin/sh for node in 56.121 56.122 56.123 ;do echo "========10.168.$node========" ssh 192.168.$node 'echo root|kinit root/admin' done
5.2 管理集群腳本
另外,為了方便管理集群,在 cdh1 上創建一個 shell 腳本用于批量管理集群,腳本如下(保存為 manager_cluster.sh
):
#!/bin/bash role=$1 command=$2 dir=$role if [ X"$role" == X"hdfs" ];then dir=hadoop fi if [ X"$role" == X"yarn" ];then dir=hadoop fi if [ X"$role" == X"mapred" ];then dir=hadoop fi for node in 56.121 56.122 56.123 ;do echo "========192.168.$node========" ssh 192.168.$node ' #先獲取 root/admin 的憑證 echo root|kinit root/admin host=`hostname -f| tr "[:upper:]" "[:lower:]"` path="'$role'/$host" #echo $path principal=`klist -k /etc/'$dir'/conf/'$role'.keytab | grep $path | head -n1 | cut -d " " -f5` #echo $principal if [ X"$principal" == X ]; then principal=`klist -k /etc/'$dir'/conf/'$role'.keytab | grep $path | head -n1 | cut -d " " -f4` if [ X"$principal" == X ]; then echo "Failed to get hdfs Kerberos principal" exit 1 fi fi kinit -r 24l -kt /etc/'$dir'/conf/'$role'.keytab $principal if [ $? -ne 0 ]; then echo "Failed to login as hdfs by kinit command" exit 1 fi kinit -R for src in `ls /etc/init.d|grep '$role'`;do service $src '$command'; done ' done
使用方法為:
$ sh manager_cluster.sh hdfs start #啟動 hdfs 用戶管理的服務 $ sh manager_cluster.sh yarn start #啟動 yarn 用戶管理的服務 $ sh manager_cluster.sh mapred start #啟動 mapred 用戶管理的服務 $ sh manager_cluster.sh hdfs status # 在每個節點上獲取 hdfs 的 ticket,然后可以執行其他操作,如批量啟動 datanode 等等
5.3 使用 java 代碼測試 kerberos
在 hdfs 中集成 kerberos 之前,可以先使用下面代碼(Krb.java)進行測試:
import com.sun.security.auth.module.Krb5LoginModule; import javax.security.auth.Subject; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; public class Krb { private void loginImpl(final String propertiesFileName) throws Exception { System.out.println("NB: system property to specify the krb5 config: [java.security.krb5.conf]"); //System.setProperty("java.security.krb5.conf", "/etc/krb5.conf"); System.out.println(System.getProperty("java.version")); System.setProperty("sun.security.krb5.debug", "true"); final Subject subject = new Subject(); final Krb5LoginModule krb5LoginModule = new Krb5LoginModule(); final Map<String,String> optionMap = new HashMap<String,String>(); if (propertiesFileName == null) { //optionMap.put("ticketCache", "/tmp/krb5cc_1000"); optionMap.put("keyTab", "/etc/krb5.keytab"); optionMap.put("principal", "foo"); // default realm optionMap.put("doNotPrompt", "true"); optionMap.put("refreshKrb5Config", "true"); optionMap.put("useTicketCache", "true"); optionMap.put("renewTGT", "true"); optionMap.put("useKeyTab", "true"); optionMap.put("storeKey", "true"); optionMap.put("isInitiator", "true"); } else { File f = new File(propertiesFileName); System.out.println("======= loading property file ["+f.getAbsolutePath()+"]"); Properties p = new Properties(); InputStream is = new FileInputStream(f); try { p.load(is); } finally { is.close(); } optionMap.putAll((Map)p); } optionMap.put("debug", "true"); // switch on debug of the Java implementation krb5LoginModule.initialize(subject, null, new HashMap<String,String>(), optionMap); boolean loginOk = krb5LoginModule.login(); System.out.println("======= login: " + loginOk); boolean commitOk = krb5LoginModule.commit(); System.out.println("======= commit: " + commitOk); System.out.println("======= Subject: " + subject); } public static void main(String[] args) throws Exception { System.out.println("A property file with the login context can be specified as the 1st and the only paramater."); final Krb krb = new Krb(); krb.loginImpl(args.length == 0 ? null : args[0]); } }
創建一個配置文件krb5.properties:
keyTab=/etc/hadoop/conf/hdfs.keytab principal=hdfs/cdh1@JAVACHEN.COM doNotPrompt=true refreshKrb5Config=true useTicketCache=true renewTGT=true useKeyTab=true storeKey=true isInitiator=true
編譯 java 代碼并運行:
# 先銷毀當前 ticket $ kdestroy $ javac Krb.java $ java -cp . Krb ./krb5.properties
6. 總結
本文介紹了 CDH Hadoop 集成 kerberos 認證的過程,其中主要需要注意以下幾點:
- 1. 配置 hosts,
hostname
請使用小寫。 - 2. 確保 kerberos 客戶端和服務端連通
- 3. 替換 JRE 自帶的 JCE jar 包
- 4. 為 DataNode 設置運行用戶并配置
JSVC_HOME
- 5. 啟動服務前,先獲取 ticket 再運行相關命令
接下來就是配置 Hadoop 集群中其他服務以集成 kerberos 認證,由于篇幅原因,后面再做說明。