Flink on Yarn Kerberos安全認(rèn)證
點(diǎn)擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)”

Flink作為新一代的大數(shù)據(jù)處理引擎,其批流一體化的設(shè)計(jì)與出色的流處理性能,在業(yè)界得到了很多頭部公司的青睞。目前運(yùn)行Flink的集群多采用Yarn進(jìn)行資源管理,這是最成熟的方案。Yarn做為Hadoop生態(tài)系統(tǒng)中的工具之一,客戶端通常需要經(jīng)過Kerberos認(rèn)證才能使用Yarn提交或管理任務(wù)。那么Flink任務(wù)是如何提交到帶有Kerberos認(rèn)證的Yarn集群的呢?我們先從Kerberos的原理開始說起,再說如何讓Flink在帶有Kerberos認(rèn)證的Yarn集群上跑起來,知其然知其所以然。
為什么需要Kerberos
在Hadoop1.0.0或者CDH3以前,Hadoop集群中的所有節(jié)點(diǎn)幾乎就是裸奔的,主要存在以下安全問題:
NameNode與JobTracker上沒有用戶認(rèn)證,用戶可以偽裝成管理員入侵到一個(gè)HDFS 或者M(jìn)apReduce集群上。
DataNode上沒有認(rèn)證:Datanode對讀入輸出并沒有認(rèn)證,如果知道block的ID,就可以任意的訪問DataNode上block的數(shù)據(jù)
JobTracker上沒有認(rèn)證:可以任意的殺死或更改用戶的jobs,也可以更改JobTracker的工作狀態(tài)
沒有對DataNode與TaskTracker的認(rèn)證:用戶可以偽裝成DataNode與TaskTracker,去接受JobTracker與Namenode的任務(wù)指派。
為了解決這些問題,kerberos認(rèn)證出現(xiàn)了,它實(shí)現(xiàn)的是機(jī)器級別的安全認(rèn)證。

kerberos是希臘神話中的三頭狗,地獄之門的守護(hù)者
其原理是事先將集群中的機(jī)器添加到kerberos數(shù)據(jù)庫中,在數(shù)據(jù)庫中分別產(chǎn)生主機(jī)與各個(gè)節(jié)點(diǎn)的keytab,并將這些keytab分發(fā)到對應(yīng)的節(jié)點(diǎn)上。通過這些keytab文件,節(jié)點(diǎn)可以從數(shù)據(jù)庫中獲得與目標(biāo)節(jié)點(diǎn)通信的密鑰防止身份被冒充。針對Hadoop集群可以解決兩方面的認(rèn)證
解決服務(wù)器到服務(wù)器的認(rèn)證,確保不會(huì)冒充服務(wù)器的情況。集群中的機(jī)器都是是可靠的,有效防止了用戶偽裝成Datanode,Tasktracker,去接受JobTracker,Namenode的任務(wù)指派。
解決client到服務(wù)器的認(rèn)證,防止用戶惡意冒充client提交作業(yè),也無法發(fā)送對于作業(yè)的操作到JobTracker上,即使知道datanode的相關(guān)信息,也無法讀取HDFS上的數(shù)據(jù)。
對于具體到用戶粒度上的權(quán)限控制,如哪些用戶可以提交某種類型的作業(yè),哪些用戶不能,目前Kerberos還沒有實(shí)現(xiàn),需要有專門的ACL模塊進(jìn)行把控。
Kerberos認(rèn)證過程
Kerberos認(rèn)證過程會(huì)涉及以下幾個(gè)基本概念:
Principal(安全個(gè)體):被認(rèn)證的個(gè)體,有一個(gè)名字和口令,每個(gè)server都對應(yīng)一個(gè)principal,其格式如下,@前面部分為具體身份,后面的部分稱為REALM。
component1 / component2 @ REALMKDC(key distribution center ) : 是一個(gè)網(wǎng)絡(luò)服務(wù),提供ticket 和臨時(shí)會(huì)話密鑰。
Ticket:一個(gè)記錄,客戶用它來向服務(wù)器證明自己的身份,包括客戶標(biāo)識、會(huì)話密鑰、時(shí)間戳。
AS (Authentication Server):認(rèn)證服務(wù)器,率屬于KDC,用于認(rèn)證Client身份。
TGS(Ticket Granting Server):許可證服務(wù)器,率屬于KDC。
事先對集群中確定的機(jī)器由管理員手動(dòng)添加到kerberos數(shù)據(jù)庫中,在KDC上分別產(chǎn)生主機(jī)與各個(gè)節(jié)點(diǎn)的keytab(包含了host和對應(yīng)節(jié)點(diǎn)的名字,還有他們之間的密鑰——Master Key),并將這些keytab分發(fā)到對應(yīng)的節(jié)點(diǎn)上。
1. AS Exchange
通過這個(gè)過程,KDC(確切地說是KDC中的Authentication Service)可以實(shí)現(xiàn)對Client身份的確認(rèn),并頒發(fā)給該Client一個(gè)TGT。具體過程如下:

Client向KDC的Authentication Service發(fā)送Authentication Service Request(KRB_AS_REQ), 為了確保KRB_AS_REQ僅限于自己和KDC知道,Client使用自己的Master Key對KRB_AS_REQ的內(nèi)容進(jìn)行加密。KRB_AS_REQ內(nèi)容主要包括:
Pre-authentication data:用于證明自己知道自己聲稱的那個(gè)account的Password,一般是一個(gè)被Client的Master key加密過的Timestamp。
Client信息: 可以理解為client的principal。
TGS的Server Name。
AS在接收到的KRB_AS_REQ后從Account Database中提取Client對應(yīng)的Master Key對Pre-authentication data進(jìn)行解密,如果是一個(gè)合法的Timestamp,則可以證明發(fā)送方的確是Client信息中聲稱的那個(gè)人。
驗(yàn)證通過之后,AS將一份Authentication Service Response(KRB_AS_REP)發(fā)送給請求方。KRB_AS_REQ主要包含兩個(gè)部分:該Client的Master Key加密過的Session Key(SKDC-Client:Logon Session Key)和被自己(KDC)的Master Key加密的TGT。而TGT大體又包含以下的內(nèi)容:
Session Key;
Client信息:即Client的principal;
End time: TGT到期的時(shí)間。
Client通過自己的Master Key對第一部分解密獲得Session Key之后,利用TGT便可以進(jìn)行Kerberos認(rèn)證的下一步:TGS Exchange。
2. TGS Exchange
Client先向TGS發(fā)送Ticket Granting Service Request(KRB_TGS_REQ),其主要內(nèi)容為:
被KDC的Master Key加密的TGT;
Authenticator:用于驗(yàn)證確認(rèn)Client提供的那個(gè)TGT是否是AS頒發(fā)給它的,其內(nèi)容為Client信息與Timestamp,并且用Session Key進(jìn)行加密。
Client信息;
Server信息:Client試圖訪問的Server的Principal。
TGS收到KRB_TGS_REQ后,先使用他自己的Master Key對TGT進(jìn)行解密,從而獲得Session Key。隨后使用該Session Key解密Authenticator,通過比較Authenticator中的Client Info和Session Ticket中的Client Info從而實(shí)現(xiàn)對Client的驗(yàn)證。

驗(yàn)證通過向?qū)Ψ桨l(fā)送Ticket Granting Service Response(KRB_TGS_REP)。這個(gè)KRB_TGS_REP有兩部分組成:使用Logon Session Key(SKDC-Client)加密過用于Client和Server認(rèn)證的Session Key(SServer-Client)和使用Server的Master Key進(jìn)行加密的Ticket。該Ticket大體包含以下一些內(nèi)容:
Session Key:SServer-Client;
Client信息;
End time: Ticket的到期時(shí)間。
Client收到KRB_TGS_REP,使用Logon Session Key(SKDC-Client)解密第一部分后獲得Session Key(SServer-Client)。有了Session Key和Ticket,Client就可以和Server進(jìn)行交互,這時(shí)無須KDC介入了。我們看看 Client是如何使用Ticket與Server怎樣進(jìn)行交互的。
3. CS(Client/Server )Exchange
先是Client向Server認(rèn)證自己的身份。這個(gè)過程與TGS Exchange中認(rèn)證的過程類似,Client創(chuàng)建用于證明自己就是Ticket的真正所有者的Authenticator,并使用上一步獲得的Session Key(SServer-Client)進(jìn)行加密,然后將它和Ticket一起作為Application Service Request(KRB_AP_REQ)發(fā)送給Server。
除了上述兩項(xiàng)內(nèi)容之外,KRB_AP_REQ還包含一個(gè)Flag用于表示Client是否需要進(jìn)行雙向驗(yàn)證(Mutual Authentication)。

Server接收到KRB_AP_REQ之后,通過自己的Master Key解密Ticket,從而獲得Session Key(SServer-Client)。通過Session Key(SServer-Client)解密Authenticator,進(jìn)而驗(yàn)證對方的身份。驗(yàn)證成功,讓Client訪問需要訪問的資源,否則直接拒絕對方的請求。
對于需要進(jìn)行雙向驗(yàn)證,Server從Authenticator提取Timestamp,使用Session Key(SServer-Client)進(jìn)行加密,并將其發(fā)送給Client用于Client驗(yàn)證Server的身份。
Flink on Kerberos Yarn實(shí)現(xiàn)方式
在客戶端使用Kerberos認(rèn)證來獲取服務(wù)時(shí),需要經(jīng)過三個(gè)步驟:
認(rèn)證:客戶端向認(rèn)證服務(wù)器發(fā)送一條報(bào)文,并獲取一個(gè)含時(shí)間戳的TGT。
授權(quán):客戶端使用TGT向TGS請求一個(gè)服務(wù)Ticket。
服務(wù)請求:客戶端向服務(wù)器出示服務(wù)Ticket,以證實(shí)自己的合法性。
其關(guān)鍵在于獲取TGT,客戶端有了它就可以申請?jiān)L問服務(wù)。所以第一種方式就是使用
1. 使用Delegation token
如果本地安裝了Kerberos客戶端,可以使用kinit命令來獲取TGT,
可以使用密碼來向KDC申請
kinit wanghuan70
Password for [email protected]:也可以直接使用keytab來獲取,keytab文件中包含了密碼的散列值;
kinit -kt wanghuan70.keytab wanghuan70使用klist命令可以查看獲取到的tgt的詳細(xì)信息,包括Client principal、Service principal、位置、有效期等;
$ klist
Ticket cache: FILE:/tmp/krb5cc_2124
Default principal: [email protected]
Valid starting Expires Service principal
08/03/2017 09:31:52 08/11/2017 09:31:52 krbtgt/IDC.XXX- [email protected]
renew until 08/10/2017 09:31:52在Flink client上執(zhí)行這一系列操作后,再在Flink配置文件flink-conf.yaml里面添加如下配置
security.kerberos.login.use-ticket-cache: true這時(shí)Flink客戶端就可以像一般情況一樣直接用command向Yarn集群提交任務(wù)了。但是tgt有一個(gè)有效期,通常是一周,過期了就無法使用了,所以這種方式不適合長期任務(wù)。這就有了第二種方式——使用keytab,先獲取token,后臺再啟動(dòng)一個(gè)進(jìn)程定期刷新token。
2. 使用keytab
這種方式時(shí)通過客戶端將keytab提交到Hadoop集群,再通過YARN分發(fā)keytab給AM和其他 worker container,具體步驟如下
Flink客戶端在提交任務(wù)時(shí),將keytab上傳至HDFS,將其作為AM需要本地化的資源。
AM container初始化時(shí)NodeManager將keytab拷貝至container的資源目錄,然后再AM啟動(dòng)時(shí)通過
UserGroupInformation.loginUserFromKeytab()來重新認(rèn)證。當(dāng)AM需要申請其他worker container時(shí),也將 HDFS 上的keytab列為需要本地化的資源,因此worker container也可以仿照AM的認(rèn)證方式進(jìn)行認(rèn)證。
此外AM和container都必須額外實(shí)現(xiàn)一個(gè)線程來定時(shí)刷新TGT。

任務(wù)運(yùn)行結(jié)束后,集群中的keytab也會(huì)隨container被清理掉。
使用這種方式的話,F(xiàn)link客戶端需要持有keytab文件,并且在Flink配置文件flink-conf.yaml里面添加如下配置
security.kerberos.login.keytab: /home/hadoop_runner/hadoop-3.2.1/etc/hadoop/krb5.keytab
security.kerberos.login.principal: superuser
security.kerberos.login.contexts: Client注意:Flink客戶端進(jìn)行Kerberos認(rèn)證是在加載集群動(dòng)態(tài)配置之前進(jìn)行的,所以需要在flink-conf.yaml文件中位置principal與keytab。在命令行中添加這個(gè)配置參數(shù),實(shí)際還是用客戶端的用戶名作為principal進(jìn)行認(rèn)證,會(huì)報(bào)找不到tgt的錯(cuò)誤。
設(shè)置Hadoop代理用戶
出于于安全考慮,很多時(shí)候我們希望客戶端能以某一個(gè)hadoop用戶的身份去運(yùn)提交任務(wù)、訪問hdfs文件,目前實(shí)現(xiàn)方式主要有以下幾種
client端root用戶su為joe用戶,再使用joe用戶的名義提交作業(yè),但這種方法前提是客戶端已經(jīng)有joe的token,并且會(huì)造成潛在的權(quán)限濫用風(fēng)險(xiǎn)。
設(shè)置環(huán)境變量或者系統(tǒng)變量HADOOP_USER_NAME,例如希望訪問hdfs文件,并在hdfs中進(jìn)行讀寫操作,可將用戶名設(shè)置為hdfs,因?yàn)樵趆dfs文件系統(tǒng)中hdfs具有最高權(quán)限。這種方法對于帶有Kerberos認(rèn)證的Hadoop集群并不起作用。
export HADOOP_USER_NAME=hdfs設(shè)置環(huán)境變量或者系統(tǒng)變量HADOOP_PROXY_USER,即設(shè)置Hadoop代理用戶,因?yàn)閷τ趲в蠯erberos認(rèn)證的集群,都是通過UserGroupInformation進(jìn)行認(rèn)證的,用戶名是由getLoginUser方法獲取的。
@Public
@Evolving
public static synchronized UserGroupInformation getLoginUser() throws IOException {
if (loginUser == null) {
loginUserFromSubject((Subject)null);
}
return loginUser;
}
@Public
@Evolving
public static synchronized void loginUserFromSubject(Subject subject) throws IOException {
ensureInitialized();
try {
if (subject == null) {
subject = new Subject();
}
LoginContext login = newLoginContext(authenticationMethod.getLoginAppName(), subject, new UserGroupInformation.HadoopConfiguration());
login.login();
UserGroupInformation realUser = new UserGroupInformation(subject);
realUser.setLogin(login);
realUser.setAuthenticationMethod(authenticationMethod);
realUser = new UserGroupInformation(login.getSubject());
String proxyUser = System.getenv("HADOOP_PROXY_USER");
if (proxyUser == null) {
proxyUser = System.getProperty("HADOOP_PROXY_USER");
}
loginUser = proxyUser == null ? realUser : createProxyUser(proxyUser, realUser);
String fileLocation = System.getenv("HADOOP_TOKEN_FILE_LOCATION");
if (fileLocation != null) {
Credentials cred = Credentials.readTokenStorageFile(new File(fileLocation), conf);
loginUser.addCredentials(cred);
}
loginUser.spawnAutoRenewalThreadForUserCreds();
} catch (LoginException var6) {
LOG.debug("failure to login", var6);
throw new IOException("failure to login", var6);
}
if (LOG.isDebugEnabled()) {
LOG.debug("UGI loginUser:" + loginUser);
}
}然而直接在客戶端設(shè)置這個(gè)環(huán)境變量或者Java系統(tǒng)變量是不work的,因?yàn)閷?shí)際訪問Hadoop的Operator是在TaskManager中運(yùn)行的,所以需要將這個(gè)變量傳到運(yùn)行tm的NodeManager中,可以通過在Flink客戶端配置參數(shù)env.ssh.opts或者env.java.opts來實(shí)現(xiàn)。
env.java.opts: -DHADOOP_PROXY_USER=hdfs # 配置所有Flink進(jìn)程的JVM啟動(dòng)參數(shù)
env.ssh.opts: export HADOOP_PROXY_USER=hdfs # 啟動(dòng)jm、tm、zookeeper等服務(wù)的額外命令也可以通過配置containerized.master.env.與containerized.taskmanager.env.來傳遞環(huán)境變量。
然而,在目前版本(Flink 1.10)中,如果配置了keytab文件與Principal,F(xiàn)link在后續(xù)中始終會(huì)以該P(yáng)rincipal的名義提交任務(wù),即便配置了HADOOP_PROXY_USER也起不到效果。針對這個(gè)issue,Uber提出了Flink on Yarn Security的改進(jìn)方案,其進(jìn)展詳見Flink-11271。

版權(quán)聲明:
文章不錯(cuò)?點(diǎn)個(gè)【在看】吧!??




