Hadoop Delegation Tokens詳解【譯文】

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

譯文

Hadoop Security在2009年被設計並實現,此後趨於穩定。但是,由於相關文檔不足,當出現問題時很難理解並進行debug。Delegation tokens作爲一種認證機制,在Hadoop生態系統中被廣泛應用。本文在Hadoop的分佈式文件系統(HDFS)和密鑰管理服務(KMS)基礎上介紹Haodop Delegation Tokens,並提供一些基礎代碼和簡單樣例。值得注意的是Hadoop生態中有許多其它的服務也是用到了Delegation Tokens,爲了簡單起見,這裏只討論Delegation Tokens在HDFS和KMS中的應用。

本文假設讀者對基本的認證和Kerberos有一定了解,以瞭解認證流程,以及HDFS架構和HDFS透明加密。對於那些對HDFS透明加密不感興趣的讀者,可以跳過KMS部分內容。之前的一篇關於Hadoop通用的認證和授權的博文可以點擊這裏

Hadoop Security簡單介紹

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

  1. 拒絕未授權的操作訪問HDFS中的數據。
  2. 在實現1的基礎上,避免太大的性能損耗。

爲了實現第一個目標,我們需要保證:

  1. 任何一個客戶端要訪問集羣必須要經過認證,以確保它就是自己聲稱的身份。
  2. 集羣中任何服務器,需要被認證爲集羣中的一部分。

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


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

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

想要了解更多關於Hadoop Security的設計,可以參考設計文檔HADOOP-4487Hadoop Security Architecture Presentation

什麼是Delegation Token?

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

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

  1. client通過Kerberos與Server完成認證,並從server獲取相應的Delegation Tokens。
  2. client與server之間後續的認證都是通過Delegation Tokens,而不進過Kerberos。

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

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

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

server和client在處理Delegation Tokens時會有不同的職責。下面兩節內容作詳細說明。

server端的Delegation Tokens

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

  1. 發佈Delegation Tokens,並保存用以驗證。
  2. 響應更新Delegation Tokens請求。
  3. 當client端執行刪除操作或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對象形式存在。主要組成如下:

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

注意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主要負責:

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

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


Delegation Tokens的認證過程將在下一節介紹。

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

Example: Delegation Tokens的生命週期

我們現在已經知道Delegation Token是什麼了,下面來探究下其在實際場景中如何使用。下圖展示的是一個運行一個典型應用的認證流程,先通過YARN提交作業,然後將任務分發到各個worker節點執行。



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

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

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

InvalidToken錯誤

到目前爲止,我們已經瞭解什麼是Delegation Tokens以及其如何在運行作業時使用。下面讓我們來看幾個典型的Delegation Token相關的錯誤。

Token is expired

有的時候,應用失敗是由於AuthenticationException,其中包含是InvalidToken異常。異常信息顯示"Token is expired",猜測下這是爲什麼?


Token can not be found in cache

還有的時候,應用失敗也是由InvalidToken造成,而其中的異常日誌顯示"token can’t be found in cache",猜測下這又是爲什麼?


解釋

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


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

長時間運行的應用

至此,你已經基本瞭解Hadoop Delegation Tokens。但是還有一點未提及。我們知道Delegations Tokens超過其最大生命週期後無法被更新,那麼如果一個任務需要運行時間比token的最大生命週期還要長怎麼辦?壞消息是,Hadoop無法通過現有的配置解決這一問題。但是好消息是,對於spark-submit提交的任務,Spark已經實現一些"神奇"的參數,Spark獲取Delegation Tokens,並用它做認證,這和前面章節提到的內容類似。但是,Spark在token將要過期時並不會更新tokens,而是獲取一個新的token。這就可以讓應用永遠地運行下去。這裏是相關代碼。需要注意的是,這需要爲spark應用生成Kerberos keytab文件。

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

實現一個Token Renewer

首先需要了解更新token操作都做了什麼,最好的方式是學習YARN RM的DelegationTokenRenewer代碼。
這個類中關鍵點如下:

  1. DelegationTokenRenewer用以管理所有的token,並通過其內部的一個線程池來更新token,還有一個線程用來取消token。更新操作發生在過期時間的90%。取消操作有一個延遲(30秒)用以避免競爭。
  2. 每個token的過期時間被單獨管理,並通過調用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中解碼得到,調用其 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. 根據2和3可知,server端不需要通過讀取配置文件來確定token過期時間和最大生命週期。由於server的配置可能隨時變動,client不應該依賴於它。

最大生命週期後如何處理Token

token的更新者直到最大生命週期纔會執行更新操作。最大生命週期後,作業就會失敗。如果你的作業是一個長耗時作業,你應該考慮利用YARN documentation about long-lived services中描述的機制,或者爲token更新者增加一些自定義邏輯,用來在現有tokens即將過期時,重新獲取delegation tokens。

Token的其它用處

恭喜!你已經瀏覽完delegation tokens的概念和細節,不過還有一些相關內容上文沒有提及,下面簡單介紹下。

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

總結

Delegation Tokens在Hadoop生態中發揮着非常重要的作用,你現在應該理解設計Delegation Tokens的初衷、如何使用以及爲什麼這麼使用。這些內容在開發和debug時尤爲重要。

附錄

附錄A Configurations at the server-side

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

附錄B:Example Code of Authenticating with Delegation Tokens

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

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調用會涉及Kerberos認證。否則將會拋出IOException,異常信息:"Delegation Token can be issued only with kerberos or web authentication"。現在,我們可以用獲取的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;
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章