Hadoop Delegation Tokens詳解【譯文】

本文是cloudera公司的一篇技術(shù)博客,原文地址:
Hadoop Delegation Tokens Explained

譯文

Hadoop Security在2009年被設(shè)計并實現(xiàn),此后趨于穩(wěn)定。但是,由于相關(guān)文檔不足,當出現(xiàn)問題時很難理解并進行debug。Delegation tokens作為一種認證機制,在Hadoop生態(tài)系統(tǒng)中被廣泛應(yīng)用。本文在Hadoop的分布式文件系統(tǒng)(HDFS)和密鑰管理服務(wù)(KMS)基礎(chǔ)上介紹Haodop Delegation Tokens,并提供一些基礎(chǔ)代碼和簡單樣例。值得注意的是Hadoop生態(tài)中有許多其它的服務(wù)也是用到了Delegation Tokens,為了簡單起見,這里只討論Delegation Tokens在HDFS和KMS中的應(yīng)用。

本文假設(shè)讀者對基本的認證和Kerberos有一定了解,以了解認證流程,以及HDFS架構(gòu)和HDFS透明加密。對于那些對HDFS透明加密不感興趣的讀者,可以跳過KMS部分內(nèi)容。之前的一篇關(guān)于Hadoop通用的認證和授權(quán)的博文可以點擊這里

Hadoop Security簡單介紹

Hadoop最初的實現(xiàn)中并沒有認證機制,這意味著存儲在Hadoop中的數(shù)據(jù)很容易泄露。在2010年,安全特性被加入Hadoop(HADOOP-4487),主要實現(xiàn)下面兩個目標:

  1. 拒絕未授權(quán)的操作訪問HDFS中的數(shù)據(jù)。
  2. 在實現(xiàn)1的基礎(chǔ)上,避免太大的性能損耗。

為了實現(xiàn)第一個目標,我們需要保證:

  1. 任何一個客戶端要訪問集群必須要經(jīng)過認證,以確保它就是自己聲稱的身份。
  2. 集群中任何服務(wù)器,需要被認證為集群中的一部分。

為了實現(xiàn)這個目標,Kerberos被選作基礎(chǔ)的認證服務(wù)。其它的機制,如:Delegation Token, Block Access Token, Trust等被加入當做Kerberos的補充。特別是Delegation Token機制被引入,其主要用以實現(xiàn)第二個目標(詳情見下一節(jié))。下圖簡要描述了Kerberos and Delegation Tokens在HDFS中應(yīng)用。(其它服務(wù)類似)


Figure 1: Simplified Diagram of HDFS Authentication Mechanisms

在上面的樣例中,會涉及到下面幾條認證流程:

  1. 用戶(joe)利用Kerberos來訪問NameNode。
  2. 用戶(joe)提交的分布式任務(wù)用joe的Delegation Tokens來訪問NameNode。這是本文接下來的重點。
  3. HDFS中的DataNode通過Kerberos和NameNode進行交互。
  4. 用戶及其提交的任務(wù)通過Block Access Tokens來訪問DataNodes。

想要了解更多關(guān)于Hadoop Security的設(shè)計,可以參考設(shè)計文檔HADOOP-4487Hadoop Security Architecture Presentation

什么是Delegation Token?

理論上,雖然可以只使用Kerberos實現(xiàn)認證機制,但這會有一定問題,尤其是應(yīng)用在像Hadoop這樣的分布式系統(tǒng)中。想像一下,對于每個MapReduce任務(wù),如果所有的任務(wù)都需要使用TGT (Ticket Granting Ticket)通過Kerberos來進行認證,KDC(Kerberos Key Distribution Center)將很快成為系統(tǒng)瓶頸。下圖中的紅線說明該問題:一個任務(wù)中可能涉及到成千個節(jié)點之間的通信,從而導(dǎo)致KDC網(wǎng)絡(luò)擁堵。事實上,如果集群規(guī)模較大,這無意間就對KDC執(zhí)行了一次DDos(distributed denial of service attack)攻擊。

Figure 2: Simplified Diagram Showing The Authentication Scaling Problem in Hadoop

因此,Delegation Tokens作為Kerberos的一個補充,實現(xiàn)了一種輕量級的認證機制。Kerberos是三方認證協(xié)議,而Delegation Tokens只涉及到兩方。
Delegation Tokens的認證過程如下:

  1. client通過Kerberos與Server完成認證,并從server獲取相應(yīng)的Delegation Tokens。
  2. client與server之間后續(xù)的認證都是通過Delegation Tokens,而不進過Kerberos。

client可以把Delegation Tokens傳遞給其它的服務(wù)(如:YARN),如此一來,這些服務(wù)(如:MapReduce任務(wù))以client身份進行認證。換句話說,client可以將身份憑證"委托"給這些服務(wù)。Delegation Tokens有一個過期時間的概念,需要周期性的更新以保證其有效性。但是,它也不能無限制的更新,這由最大生命周期控制。此外,在Delegation Token過期前也被取消。

Delegation Tokens可以避免分發(fā)Kerberos TGT 或 keytab,而這些信息一旦泄露,將獲得所有服務(wù)的訪問權(quán)限。另一方面,每個Delegation Token與其關(guān)聯(lián)服務(wù)嚴格的綁定在一起,且最終會過期。所以,即使Delegation Token泄露,也不會造成太大損失。此外,Delegation Token使身份憑證的更新更加輕量化。這是因為Token更新過程只涉及到"更新者"和相關(guān)服務(wù)。token本身并不會改變,所以已經(jīng)使用token的各個組件并不需要更新。

考慮到高可用性,Delegation Tokens會被server進行持久化。HDFS NameNode將Delegation Tokens持久化到元數(shù)據(jù)中(又稱為:fsimage and edit logs),KMS會將其以ZNodes形式持久化到ZooKeeper中。即使服務(wù)重啟或故障切換,Delegation Tokens也會一直可用。

server和client在處理Delegation Tokens時會有不同的職責(zé)。下面兩節(jié)內(nèi)容作詳細說明。

server端的Delegation Tokens

server端(圖2中的HDFS NN和KMS)主要負責(zé):

  1. 發(fā)布Delegation Tokens,并保存用以驗證。
  2. 響應(yīng)更新Delegation Tokens請求。
  3. 當client端執(zhí)行刪除操作或token過期時,移除Token。
  4. 通過驗證client提供的Tokens和server端存儲的token是否一致,來對client進行認證。

Hadoop中Delegation Tokens的生成和驗證主要依賴于HMAC機制。Delegation Token主要由兩部分組成:public部分和private部分,在Server端以<key,value>形式存儲在hashmap中,其中public部分作為key,private部分作為value。

public部分信息用于token識別,以identifier對象形式存在。主要組成如下:

Table 1: Token Identifier (public part of a Delegation Token)

private部分信息是由AbstractDelegationTokenSecretManager中的DelegationTokenInformation類來表示,主要組成如下:

Table 2: Delegation Token Information (private part of a Delegation Token)

注意Table1中的Master key ID,其存儲于server端,并用于生成每個Delegation Token。該ID會定期更新,且一直只存在于server端。Server同樣可以配置更新周期(renew-interval,默認24小時),以及Delegation Token的過期時間。過期的Delegation Tokens不能用于認證,且Server端專門有一個后臺線程用于將過期token移除。

只有Delegation Token的renewer可以在token過期前進行更新操作。每次更新過后,token的過期時間會延長一個更新周期(renew-interval),直到token達到最大生命周期(默認7天)。Appendix A中的表格中就是這些配置項的名稱及默認值。

client端的Delegation tokens

client主要負責(zé):

  1. 從server端請求一個新的Delegation Tokens,請求同時可以指定token的更新者(renewer)。
  2. 更新Delegation Tokens(如果client將自己指定為renewer),亦或請求別的組件更新token(指定的renewer)
  3. 向server發(fā)送取消Delegation Tokens的請求。
  4. 提供Delegation Tokens供server進行認證。

client端Token主要包含以下信息:


Table 3: Delegation Token at Client Side

Delegation Tokens的認證過程將在下一節(jié)介紹。

下面是提交job時的日志的一段摘錄,在下面的日志中,我們看到了HDFS Delegation Token和KMS Delegation Token。

job submission

Example: Delegation Tokens的生命周期

我們現(xiàn)在已經(jīng)知道Delegation Token是什么了,下面來探究下其在實際場景中如何使用。下圖展示的是一個運行一個典型應(yīng)用的認證流程,先通過YARN提交作業(yè),然后將任務(wù)分發(fā)到各個worker節(jié)點執(zhí)行。


Figure 3: How Delegation Token is Used for Authentication In A Typical Job

簡單起見,此處將忽略Kerberos認證和Task分發(fā)流程。圖中通常有5個步驟:

  1. client希望在集群中運行一個job,它分別從NameNode和KMS獲取HDFS Delegation Token和KMS Delegation Token。
  2. client將作業(yè)提交到Y(jié)ARN資源管理器(RM),同時提交的還有step1中獲取的Delegation Token以及ApplicationSubmissionContext
  3. YARN RM通過更新操作來核實接收的Token,隨后,YARN啟動job,并將其和Delegation Tokens一同分發(fā)到各個worker節(jié)點上。
  4. 每個工作節(jié)點中的Task利用這些Token來進行認證,比如:需要訪問HDFS上數(shù)據(jù)時,使用HDFS Delegation Token進行認證。需要解密HDFS加密區(qū)的文件時,使用KMS Delegation Token。
  5. job結(jié)束后,RM則取消該job的Delegation Tokens。

注意:有一個步驟沒有在上圖中畫出,就是RM會跟蹤每個Delegation Token的過期時間,并在即將過期時(達到過期時間的90%)更新Delegation Token。此外,RM是對每個Token進行跟蹤,而不是按照Token種類。這樣即使每個token的更新間隔(renewal intervals)不同,也能正確地被更新。toekn更新者(renewer)是通過Java ServiceLoader機制實現(xiàn),因此RM不需要知道Token的種類。對于感興趣的讀者可以閱讀DelegationTokenRenewer相關(guān)代碼。

InvalidToken錯誤

到目前為止,我們已經(jīng)了解什么是Delegation Tokens以及其如何在運行作業(yè)時使用。下面讓我們來看幾個典型的Delegation Token相關(guān)的錯誤。

Token is expired

有的時候,應(yīng)用失敗是由于AuthenticationException,其中包含是InvalidToken異常。異常信息顯示"Token is expired",猜測下這是為什么?


Token is expired

Token can not be found in cache

還有的時候,應(yīng)用失敗也是由InvalidToken造成,而其中的異常日志顯示"token can’t be found in cache",猜測下這又是為什么?


Token can’t be found in cache

解釋

上述兩個錯誤都是由一個共同的原因引起的:被用于認證的token過期了,因此無法使用。第一個異常日志中可以明確看到token過期信息,因為token依然存在于server端。因此,當server驗證token有效性的時候,會因token過期而驗證失敗,拋出“token is expired”異常。現(xiàn)在你可以猜測下第二個異常如何發(fā)生的?在"server端的Delegation Tokens"這一小節(jié),我們提到server端有一個后臺線程用于移除過期的token。因此,當某個過期token被移除后,server端在進行token驗證的過程中就無法找到該token,即拋出"token can’t be found"異常。

Figure 4: Life Cycle of Delegation Token

需要注意的是,當一個token被明確的撤銷后,那么該token立即會被移除。因此,對于被撤銷的token,錯誤日志只可能是"token can’t be found"。為了進一步debug這些errors,獲取client和server端日志中的token序列號(樣例中的“sequenceNumber=7” or “sequenceNumber=8”)是非常有必要的,你應(yīng)該可以看到和token相關(guān)的事件信息,如:creations,renewals,cancelations等。

長時間運行的應(yīng)用

至此,你已經(jīng)基本了解Hadoop Delegation Tokens。但是還有一點未提及。我們知道Delegations Tokens超過其最大生命周期后無法被更新,那么如果一個任務(wù)需要運行時間比token的最大生命周期還要長怎么辦?壞消息是,Hadoop無法通過現(xiàn)有的配置解決這一問題。但是好消息是,對于spark-submit提交的任務(wù),Spark已經(jīng)實現(xiàn)一些"神奇"的參數(shù),Spark獲取Delegation Tokens,并用它做認證,這和前面章節(jié)提到的內(nèi)容類似。但是,Spark在token將要過期時并不會更新tokens,而是獲取一個新的token。這就可以讓應(yīng)用永遠地運行下去。這里是相關(guān)代碼。需要注意的是,這需要為spark應(yīng)用生成Kerberos keytab文件。

如果我們實現(xiàn)了一個長時間應(yīng)用,并希望其能明確地處理token呢?這主要涉及兩部分內(nèi)容:(1)到達最大生命周期前更新token(2)token過期后,需要考慮替換當前token。

實現(xiàn)一個Token Renewer

首先需要了解更新token操作都做了什么,最好的方式是學(xué)習(xí)YARN RM的DelegationTokenRenewer代碼。
這個類中關(guān)鍵點如下:

  1. DelegationTokenRenewer用以管理所有的token,并通過其內(nèi)部的一個線程池來更新token,還有一個線程用來取消token。更新操作發(fā)生在過期時間的90%。取消操作有一個延遲(30秒)用以避免競爭。
  2. 每個token的過期時間被單獨管理,并通過調(diào)用renew() API來獲取,該方法返回一個過期時間。
dttr.expirationDate =
          UserGroupInformation.getLoginUser().doAs(
            new PrivilegedExceptionAction<Long>() {
              @Override
              public Long run() throws Exception {
                return dttr.token.renew(dttr.conf);
              }
            });

這就是為什么YARN RM在接收到token后立即更新它,就是為了獲取該token的過期時間。

  1. token的最大生命周期是從token的identifier中解碼得到,調(diào)用其 getMaxDate() API。identifier中的其它字段也可以通過這個API得到。
 if (token.getKind().equals(HDFS_DELEGATION_KIND)) {
        try {
          AbstractDelegationTokenIdentifier identifier =
              (AbstractDelegationTokenIdentifier) token.decodeIdentifier();
          maxDate = identifier.getMaxDate();
        } catch (IOException e) {
          throw new YarnRuntimeException(e);
        }
      }
  1. 根據(jù)2和3可知,server端不需要通過讀取配置文件來確定token過期時間和最大生命周期。由于server的配置可能隨時變動,client不應(yīng)該依賴于它。

最大生命周期后如何處理Token

token的更新者直到最大生命周期才會執(zhí)行更新操作。最大生命周期后,作業(yè)就會失敗。如果你的作業(yè)是一個長耗時作業(yè),你應(yīng)該考慮利用YARN documentation about long-lived services中描述的機制,或者為token更新者增加一些自定義邏輯,用來在現(xiàn)有tokens即將過期時,重新獲取delegation tokens。

Token的其它用處

恭喜!你已經(jīng)瀏覽完delegation tokens的概念和細節(jié),不過還有一些相關(guān)內(nèi)容上文沒有提及,下面簡單介紹下。

  • Delegation Tokens在其它服務(wù)中的應(yīng)用,如:Apache Oozie, Apache Hive, and Apache Hadoop’s YARN RM,這些服務(wù)都是用Delegation Tokens認證機制。
  • Block Access Token:client在訪問HDFS上的文件時,首先需要和NameNode通信,獲取該文件的Block位置信息。然后直接和DataNode通信訪問這些Blocks。訪問權(quán)限的檢查是在NameNode端完成。但是,client直接訪問DataNode中的Block數(shù)據(jù),這也需要做權(quán)限認證。Block Access Tokens就是用來解決這一問題。Block Access Tokens是由NameNode發(fā)布給Client,然后由Client發(fā)送給DataNode。Block Access Tokens的生命周期很短(默認10小時),并且不能更新。也就意味著如果一個Block Access Token過期,那么client必須重新獲取一個新的token。
  • Authentication Token:我們已經(jīng)介紹很多的內(nèi)容都是關(guān)于Delegation Tokens。Hadoop中還有一種機制稱為:Authentication Token,主要目的是實現(xiàn)一種更輕量級、高可擴展的認證方案。類似于client和server端的cookie。Authentication Tokens由server端授權(quán),且無法更新以及仿冒他人。和Delegation Tokens不同的是,server端不需要單獨存儲Authentication Tokens,

總結(jié)

Delegation Tokens在Hadoop生態(tài)中發(fā)揮著非常重要的作用,你現(xiàn)在應(yīng)該理解設(shè)計Delegation Tokens的初衷、如何使用以及為什么這么使用。這些內(nèi)容在開發(fā)和debug時尤為重要。

附錄

附錄A Configurations at the server-side

下面是server端Delegation Tokens相關(guān)的配置項。更詳細的解釋請參考Delegation Tokens at the Server-side

Table 4: Configuration Properties and Default Values for HDFS and KMS

附錄B:Example Code of Authenticating with Delegation Tokens

在分析下面代碼前需要了解UserGroupInformation (UGI) 這個類,UGI是hadoop中用于完成認證相關(guān)操作的API,下面樣例中的代碼,在上文的異常棧日志中也出現(xiàn)過。

UserGroupInformation tokenUGI = UserGroupInformation.createRemoteUser("user_name");
UserGroupInformation kerberosUGI = UserGroupInformation.loginUserFromKeytabAndReturnUGI("principal_in_keytab", "path_to_keytab_file");
Credentials creds = new Credentials();
kerberosUGI.doAs((PrivilegedExceptionAction<Void>) () -> {
  Configuration conf = new Configuration();
  FileSystem fs = FileSystem.get(conf);
  fs.getFileStatus(filePath); // ← kerberosUGI can authenticate via Kerberos

  // get delegation tokens, add them to the provided credentials. set renewer to ‘yarn’
  Token<?>[] newTokens = fs.addDelegationTokens("yarn", creds);
  // Add all the credentials to the UGI object
  tokenUGI.addCredentials(creds);

  // Alternatively, you can add the tokens to UGI one by one.
  // This is functionally the same as the above, but you have the option to log each token acquired.
  for (Token<?> token : newTokens) {
    tokenUGI.addToken(token);
  }
  return null;
});

需要注意的是,addDelegationTokens這一RPC調(diào)用會涉及Kerberos認證。否則將會拋出IOException,異常信息:"Delegation Token can be issued only with kerberos or web authentication"。現(xiàn)在,我們可以用獲取的delegation token訪問HDFS了。

tokenUGI.doAs((PrivilegedExceptionAction<Void>) () -> {
 Configuration conf = new Configuration();
 FileSystem fs = FileSystem.get(conf);
 fs.getFileStatus(filePath); // ← tokenUGI can authenticate via Delegation Token
 return null;
});
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容