简介

Apache Hadoop的安全性是在2009年左右设计和实施的,此后一直保持稳定。但是,由于缺少有关此领域的文档,因此在出现问题时很难理解或调试。Delegation tokens(委托令牌),作为身份验证方法在Hadoop生态系统中广泛使用。这篇文章将介绍Hadoop分布式文件系统(HDFS)和Hadoop密钥管理服务器(KMS)中的Hadoop授权令牌的概念,并提供了一些基本代码和故障排除示例。值得注意的是,Hadoop生态系统中还有许多其他服务也使用委托令牌,但是为了简洁起见,我们仅讨论HDFS和KMS。
文章假定读者了解了authentication, Kerberos的基本概念,以便了解身份验证流程,以及HDFS体系结构和HDFS透明加密等。有关Hadoop的授权和身份验证的先前文章可以在这找到。

Hadoop安全性快速入门

Hadoop最初是在没有真实身份验证的情况下实施的,这意味着存储在Hadoop中的数据很容易遭到破坏。此安全功能随后于2010年通过HADOOP-4487添加,具有以下两个基本目标:

  1. 防止未经授权访问HDFS中存储的数据
  2. 在实现目标1的同时不增加大量成本。

为了实现第一个目标,我们需要确保:

  • 任何访问群集的客户端都经过身份验证,以确保它们是声称的身份。
  • 集群中的所有服务器都经过身份验证,可以成为集群的一部分。

为了这个目标,选择Kerberos作为基础身份验证服务。添加了其他机制,例如委托令牌,块访问令牌,信任等,来补充Kerberos。特别是,引入了Delegation Token以实现第二个目标(有关方法,请参阅下一节)。下面的简化图,说明了在HDFS上下文中使用Kerberos和委派令牌(Delegation Token)的位置(其他服务类似):
image.png
在上面的简单HDFS示例中,有几种身份验证机制在起作用:

  • 最终用户(joe)可以使用Kerberos与HDFS NameNode对话
  • 最终用户(joe)提交的分布式任务可以使用joe的委派令牌访问HDFS NameNode。这将是本文章其余部分的重点
  • HDFS数据节点(DataNode)使用Kerberos与HDFS NameNode对话
  • 最终用户和分布式任务可以使用“块访问令牌”访问HDFS DataNode

我们将在本文末尾的“其他方式使用令牌”部分中简要介绍上述机制。要阅读有关Hadoop安全设计的更多详细信息,请参考HADOOP-4487中的设计文档Hadoop安全体系结构演示

什么是Delegation Token?

从理论上讲,可以完全使用Kerberos进行身份验证,但在像Hadoop这样的分布式系统中使用时,就会有它自己的问题。想象一下,对于每个MapReduce作业,如果所有工作任务都必须使用委派的TGT(Ticket Granting Ticket)通过Kerberos进行身份验证,则Kerberos密钥分发中心(KDC)将很快成为瓶颈。下图中的红线说明了此问题:一个工作可能有成千上万的节点到节点通信,从而导致相同数量的KDC流量。实际上,它会在非常大的集群中无意中对KDC进行分布式拒绝服务攻击
image.png
因此,引入了委托令牌作为一种轻量级身份验证方法,以补充Kerberos身份验证。 Kerberos是一种三方协议。相反,委托令牌身份验证是两方身份验证协议。
授权令牌的工作方式是:

  1. 客户端最初通过Kerberos对每个服务器进行身份验证,然后从该服务器获取委托令牌。
  2. 客户端使用委派令牌来与服务器进行后续身份验证,而不是使用Kerberos。

客户端可以并且确实经常将委派令牌传递给其他服务(例如YARN),这样其他服务(例如mappers and reducers)可以作为客户端进行身份验证并代表客户端运行作业。换句话说,客户端可以将凭据“委托”给这些服务。授权令牌有一个到期时间,需要定期更新以保持其有效性。但是,它们不能无限期续约——有最大的寿命。在到期之前,也可以取消委托令牌。
委托令牌消除了分发Kerberos TGT或密钥表的需要,如果泄露,则将授予对所有服务的访问权限。另一方面,委派令牌严格与其相关的服务绑定在一起到最终到期,如果暴露,则造成的损害较小。此外,委派令牌使凭据更新更加轻巧。这是因为更新的设计方式使得更新过程中仅涉及更新程序和服务。令牌本身保持不变,因此不必更新已经使用令牌的所有参与者。
出于可用性的原因,委派令牌由服务器保留。HDFS NameNode将委托令牌保留到其元数据中(也就是fsimage和edit logs)。KMS以ZNodes的形式将委派令牌保持在Apache ZooKeeper中。即使服务器重新启动或故障转移,这也可使委托令牌仍然可用。
服务器和客户端在处理委托令牌方面有不同的职责。以下两个部分提供了一些详细信息:

服务器端的委托令牌

该服务器(即图2中的HDFS NN和KMS)负责:

  • 发行委托令牌,并存储它们以进行验证。
  • 根据请求续订委派令牌。
  • 在客户端取消授权令牌或到期时删除授权令牌。
  • 通过对照存储的委托令牌验证客户端提供的委托令牌。

Hadoop中的委托令牌是根据HMAC机制生成和验证的。委托令牌中有两部分信息:公共部分和私有部分。委托令牌作为哈希图存储在服务器端,以公共信息作为键,私有信息作为值。
公共信息以标识符对象的形式用于令牌识别。它包括:
Table 1: Token Identifier (public part of a Delegation Token)

Identifier 解释
Kind 令牌的种类(HDFS_DELEGATION_TOKEN或kms-dt)。令牌还包含与标识符种类匹配的种类。
Owner 拥有令牌的用户。
Renewer 可以续签令牌的用户。
Real User 仅在所有者被假冒时相关。如果令牌是由模拟用户创建的,则这将标识模拟用户。例如,当oozie冒充用户joe时,所有者将是joe,而实际用户将是oozie。
Issue Date 令牌发行的时代时间。
Max Date 令牌可以更新到的时期。
Sequence Number 标识令牌的UUID。
Master Key ID 用于创建和验证令牌的主密钥的ID。

私有信息由AbstractDelegationTokenSecretManager中的类DealerTokenInformation表示,它对于安全性至关重要,并且包含以下字段:

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

field 解释
renewDate 令牌预计将更新的时期。 如果小于当前时间,则表示令牌已过期
password 使用主密钥作为HMAC密钥计算为令牌标识符的HMAC的密码。 用于验证客户端向服务器提供的委派令牌。
trackingId 跟踪标识符,可用于在多个客户端会话之间关联令牌的用法。 它被计算为令牌标识符的MD5。

请注意表1中的主密钥ID,这是服务器上存在的主密钥的ID。主密钥用于生成每个委派令牌。它以配置的时间间隔滚动,并且永不离开服务器。
服务器还具有一个指定更新间隔的配置(默认为24小时),这也是委托令牌的到期时间。过期的委托令牌不能用于身份验证,并且服务器具有后台线程,可以从服务器的令牌存储中删除过期的委托令牌。
在过期之前,只有授权令牌的续订者才能续签它。成功的续订将授权令牌的有效期延长了另一个续订间隔,直到达到其最大生命周期(默认为7天)。
附录A中的表提供了HDFS和KMS的配置名称及其默认值。

客户端的委托令牌

客户负责如下:

  • 从服务器请求新的委派令牌。请求令牌时可以指定续订。
  • 更新委托令牌(如果客户将自己指定为“更新者”),或要求另一方(指定的“更新者”)更新委托令牌。
  • 请求服务器取消委派令牌。
  • 出示委派令牌以与服务器进行身份验证。

客户端可见的Token类的定义在这。下表描述了令牌中包含的内容:
Table 3: Delegation Token at Client Side

field 解释
identifier 与服务器端的公共信息部分匹配的令牌标识符。
password 与服务器端密码匹配的密码。
kind 令牌的种类(例如HDFS_DELEGATION_TOKEN或kms-dt),它与标识符的种类匹配。
service 服务名称(例如ha-hdfs:<nameservice-name> for HDFS, <ip_address>:<port> for KMS)。
renewer 可以续签令牌的用户(例如yarn)。

下一部分将说明使用委派令牌进行身份验证的方式。
以下是作业提交时的示例日志摘录。 INFO日志打印有来自所有服务的令牌。在下面的示例中,我们看到一个HDFS委托令牌和一个KMS委托令牌。

  1. $ hadoop jar /opt/cloudera/parcels/CDH/jars/hadoop-mapreduce-examples-2.6.0-cdh5.13.0.jar pi 2 3
  2. Number of Maps = 2
  3. Samples per Map = 3
  4. Wrote input for Map #0
  5. Wrote input for Map #1
  6. Starting Job
  7. 17/10/22 20:50:03 INFO client.RMProxy: Connecting to ResourceManager at example.cloudera.com/172.31.113.88:8032
  8. 17/10/22 20:50:03 INFO hdfs.DFSClient: Created token for xiao: HDFS_DELEGATION_TOKEN owner=xiao@GCE.CLOUDERA.COM, renewer=yarn, realUser=, issueDate=1508730603423, maxDate=1509335403423, sequenceNumber=4, masterKeyId=39 on ha-hdfs:ns1
  9. 17/10/22 20:50:03 INFO security.TokenCache: Got dt for hdfs://ns1; Kind: HDFS_DELEGATION_TOKEN, Service: ha-hdfs:ns1, Ident: (token for xiao: HDFS_DELEGATION_TOKEN owner=xiao@GCE.CLOUDERA.COM, renewer=yarn, realUser=, issueDate=1508730603423, maxDate=1509335403423, sequenceNumber=4, masterKeyId=39)
  10. 17/10/22 20:50:03 INFO security.TokenCache: Got dt for hdfs://ns1; Kind: kms-dt, Service: 172.31.113.88:16000, Ident: (kms-dt owner=xiao, renewer=yarn, realUser=, issueDate=1508730603474, maxDate=1509335403474, sequenceNumber=7, masterKeyId=69)

对于想要编写Java代码进行身份验证的读者,附录B中提供了示例代码。

授权令牌的生命周期

现在,我们了解了授权令牌是什么,让我们看一下它在实践中的用法。下图显示了用于运行典型应用程序的身份验证示例流程,其中该作业是通过YARN提交的,然后被分发到集群中的多个工作程序节点以执行。
Figure 3: How Delegation Token is Used for Authentication In A Typical Job
image.png
为简便起见,省略了Kerberos身份验证的步骤以及有关任务分发的详细信息。图中一般有5个步骤:

  • 客户端要在集群中运行作业。它从HDFS NameNode获取一个HDFS委托令牌,并从KMS获得一个KMS委托令牌
  • 客户端将作业提交给YARN资源管理器(RM),并传递刚获取的委托令牌以及ApplicationSubmissionContext
  • YARN RM通过立即续签来验证委托令牌是否有效。然后,它将启动作业,该作业(与委托令牌一起)分发到集群中的工作节点。
  • 每个工作节点在访问HDFS数据时都使用HDFS委派令牌向HDFS进行身份验证,并在解密加密区域中的HDFS文件时使用KMS委派令牌向KMS进行身份验证。
  • 作业完成后,RM取消该作业的委派令牌。

Note:
上图中未绘制的步骤是,RM还跟踪每个委托令牌的到期时间,并在到期时间的90%时更新委托令牌。请注意,在RM中会单独跟踪委派令牌,而不是基于令牌种类。这样,具有不同更新间隔的令牌都可以正确更新。令牌更新类是使用Java ServiceLoader实现的,因此RM不必知道令牌的种类。对于非常感兴趣的读者,相关代码在RM的PrincipledTokenRenewer类中。

这是什么InvalidToken错误

现在我们知道什么是委派令牌,以及在执行典型任务时如何使用它们。但是不要在这里停下来!我们来看看应用程序日志中一些与委托令牌相关的典型错误消息,并弄清它们的含义。

令牌已过期(Token is expired)

有时,应用程序会因AuthenticationException而失败,并带有一个InvalidToken异常。异常消息指示“令牌已过期”。为什么会发生这种情况?错误如下:

….
17/10/22 20:50:12 INFO mapreduce.Job: Job job_1508730362330_0002 failed with state FAILED due to: Application application_1508730362330_0002 failed 2 times due to AM Container for appattempt_1508730362330_0002_000002 exited with  exitCode: -1000
For more detailed output, check application tracking page:https://example.cloudera.com:8090/proxy/application_1508730362330_0002/Then, click on links to logs of each attempt.
Diagnostics: org.apache.hadoop.security.authentication.client.AuthenticationException: org.apache.hadoop.security.token.SecretManager$InvalidToken: token (kms-dt owner=xiao, renewer=yarn, realUser=, issueDate=1508730603474, maxDate=1509335403474, sequenceNumber=7, masterKeyId=69) is expired, current time: 2017-10-22 20:50:12,166-0700 expected renewal time: 2017-10-22 20:50:05,518-0700
….
Caused by: org.apache.hadoop.security.authentication.client.AuthenticationException: org.apache.hadoop.security.token.SecretManager$InvalidToken: token (kms-dt owner=xiao, renewer=yarn, realUser=, issueDate=1508730603474, maxDate=1509335403474, sequenceNumber=7, masterKeyId=69) is expired, current time: 2017-10-22 20:50:12,166-0700 expected renewal time: 2017-10-22 20:50:05,518-0700
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
…
at org.apache.hadoop.crypto.key.kms.KMSClientProvider.call(KMSClientProvider.java:535)
…

在缓存中找不到令牌(Token can’t be found in cache)

有时,应用程序会因AuthenticationException而失败,并带有一个InvalidToken异常。异常消息表示“在缓存中找不到令牌”。为什么会发生这种情况,与“令牌已过期”错误有什么区别?错误如下:

….
17/10/22 20:55:47 INFO mapreduce.Job: Job job_1508730362330_0003 failed with state FAILED due to: Application application_1508730362330_0003 failed 2 times due to AM Container for appattempt_1508730362330_0003_000002 exited with  exitCode: -1000
For more detailed output, check application tracking page:https://example.cloudera.com:8090/proxy/application_1508730362330_0003/Then, click on links to logs of each attempt.
Diagnostics: org.apache.hadoop.security.authentication.client.AuthenticationException: org.apache.hadoop.security.token.SecretManager$InvalidToken: token (kms-dt owner=xiao, renewer=yarn, realUser=, issueDate=1508730937041, maxDate=1509335737041, sequenceNumber=8, masterKeyId=73) can’t be found in cache
java.io.IOException: org.apache.hadoop.security.authentication.client.AuthenticationException: org.apache.hadoop.security.token.SecretManager$InvalidToken: token (kms-dt owner=xiao, renewer=yarn, realUser=, issueDate=1508730937041, maxDate=1509335737041, sequenceNumber=8, masterKeyId=73) can’t be found in cache
at org.apache.hadoop.crypto.key.kms.LoadBalancingKMSClientProvider.decryptEncryptedKey(LoadBalancingKMSClientProvider.java:294)
at org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.decryptEncryptedKey(KeyProviderCryptoExtension.java:528)
at org.apache.hadoop.hdfs.DFSClient.decryptEncryptedDataEncryptionKey(DFSClient.java:1448)
…
at org.apache.hadoop.fs.FileSystem.open(FileSystem.java:784)
at org.apache.hadoop.fs.FileUtil.copy(FileUtil.java:367)
at org.apache.hadoop.yarn.util.FSDownload.copy(FSDownload.java:265)
at org.apache.hadoop.yarn.util.FSDownload.access$000(FSDownload.java:61)
at org.apache.hadoop.yarn.util.FSDownload$2.run(FSDownload.java:359)
at org.apache.hadoop.yarn.util.FSDownload$2.run(FSDownload.java:357)
…
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: org.apache.hadoop.security.authentication.client.AuthenticationException: org.apache.hadoop.security.token.SecretManager$InvalidToken: token (kms-dt owner=xiao, renewer=yarn, realUser=, issueDate=1508730937041, maxDate=1509335737041, sequenceNumber=8, masterKeyId=73) can’t be found in cache
…
at org.apache.hadoop.crypto.key.kms.KMSClientProvider.call(KMSClientProvider.java:535)
…

解释说明

上面的2个错误具有相同的原因:用于身份验证的令牌已过期,不能再用于身份验证。
第一条消息能够明确表明令牌已过期,因为该令牌仍存储在服务器中。因此,当服务器验证令牌时,对过期时间的验证将失败,并引发“令牌已过期”异常。
现在问题何时会发生第二个错误?在“服务器端的委托令牌”部分中,我们解释了服务器具有后台线程来删除过期的令牌。因此,如果在服务器的后台线程删除令牌后将令牌用于身份验证,则服务器上的验证逻辑将找不到该令牌。这导致抛出异常,说“找不到令牌”。
下图显示了这些事件的顺序:
Figure 4: Life Cycle of Delegation Token
image.png
请注意,当令牌被明确取消后,它将立即从储存中删除。因此,对于已取消的令牌,错误将始终是“找不到令牌”。
为了进一步调试这些错误,有必要在客户端日志和服务器日志中添加特定令牌序列号(在上述示例中为"sequenceNumber=7" or "sequenceNumber=8")您应该能够在服务器日志中看到与令牌创建,续订(如果有),取消(如果有)有关的事件。

长期运行的应用程序

至此,您已经了解了有关Hadoop委托令牌的所有基础知识。但除此之外,还有一个缺失的环节:我们知道,授权令牌无法在其最大生存期之后进行更新,那么对于需要运行超过最大生存期的应用程序,会发生什么情况呢?
坏消息是,Hadoop没有为所有应用程序提供统一的方法来执行此操作,因此不存在一个魔术配置使任何应用程序可以使其“正常运行”的。但这仍然是可能的。对于Spark提交的应用程序来说,好消息是,Spark已经实现了这些神奇的参数。Spark会获得委托令牌并将其用于身份验证,类似于我们在前面的部分中所述。但是,Spark不会续签令牌,而只是在即将过期时获取新令牌。这使应用程序可以永远运行。相关代码在这里。请注意,这需要为您的Spark应用程序提供Kerberos keytab。
但是,如果您要实现一个长期运行的服务应用程序,并希望该应用程序显式处理令牌,该怎么办?这将涉及两个部分:更新令牌直到最大寿命;在最大使用寿命后处理令牌替换。请注意,这不是通常的做法,只有在实施服务时才建议这样做。

实施令牌续订

首先,我们来研究令牌更新应该如何进行。最好的方法是研究YARN RM的PrincipledTokenRenewer代码。
该类的一些关键点是:

  1. 它是管理所有令牌的单一类。在内部,它具有用于续订令牌的线程池,以及用于取消令牌的另一个线程。续约发生在到期时间的90%处。取消会有一个小的延迟(30秒),以防止竞争。
  2. 每个令牌的到期时间是分开管理的。通过调用令牌的renew() API以编程方式检索令牌的到期时间。该API的返回值是到期时间。```java dttr.expirationDate = UserGroupInformation.getLoginUser().doAs( new PrivilegedExceptionAction() { @Override public Long run() throws Exception {
     return dttr.token.renew(dttr.conf);
    
    } }); ``` 这是YARN RM收到令牌后立即更新令牌的另一个原因:知道令牌何时到期。
  3. 通过解码令牌的标识符并调用其getMaxDate() API来检索每个令牌的最大生命周期。标识符中的其他字段也可以通过调用类似的API获得。```java if (token.getKind().equals(HDFS_DELEGATION_KIND)) { try { AbstractDelegationTokenIdentifier identifier =

       (AbstractDelegationTokenIdentifier) token.decodeIdentifier();
    

    maxDate = identifier.getMaxDate(); } catch (IOException e) { throw new YarnRuntimeException(e); } } ```

  4. 由于2和3,无需读取配置即可确定更新间隔和最大寿命。服务器的配置可能会更改。客户不应该依赖它,也没有办法知道它。

    Max Lifetime之后处理令牌

    令牌续签仅是续签令牌,直到其最大寿命为止。最长寿命后,该作业将失败。如果您的应用程序长时间运行,则应考虑使用YARN文档中描述的有关长期服务的机制,或者向自己的委托令牌续订类添加逻辑,以在现有授权令牌即将达到最大生存期时检索新的委托令牌。

    令牌使用的其他方式

    现在,您已经阅读完有关委托令牌的概念和详细信息。本博客文章中还有未涉及一些相关方面。以下是它们的简要介绍:
    其他服务中的委托令牌:Apache Oozie,Apache Hive和Apache Hadoop的YARN RM等服务也利用委托令牌进行身份验证。
    块访问令牌:HDFS客户端通过首先联系NameNode来获取文件的块位置,然后直接在DataNode上访问这些块来访问文件。文件权限检查在NameNode中进行。但是,对于随后在DataNodes上进行的数据块访问,也需要进行授权检查。块访问令牌用于此目的。它们由HDFS NameNode发布给客户端,然后由客户端传递给DataNode。阻止访问令牌的寿命很短(默认情况下为10个小时),并且无法更新。如果“阻止访问令牌”过期,则客户端必须请求一个新令牌。
    身份验证令牌:我们已经专门介绍了代理令牌。Hadoop还具有“身份验证令牌”的概念,该令牌旨在成为一种更便宜且更具可扩展性的身份验证机制。它就像服务器和客户端之间的cookie。身份验证令牌是由服务器授予的,不能被更新或用于模拟其他人。而且与委派令牌不同,服务器不需要单独存储它们。您不需要针对身份验证令牌显式编码。

    结论

    委托令牌在Hadoop生态系统中扮演重要角色。您现在应该了解委托令牌的用途,如何使用它们以及为什么以这种方式使用它们。在编写和调试应用程序时,此知识必不可少。

    附录

    附录A:服务器端的配置

    下表是与委托令牌相关的配置表。请参阅服务器端的委托令牌以获取这些属性的说明。

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

属性 在HDFS中配置名称 在HDFS中默认值 在KMS中配置名称 在KMS中默认值
续订间隔 dfs.namenode.delegation.token.renew-interval 86400000(1 day) hadoop.kms.authentication.delegation-token.renew-interval.sec 86400(1 day)
最长寿命 dfs.namenode.delegation.token.max-lifetime 604800000(7 days) hadoop.kms.authentication.delegation-token.max-lifetime.sec 604800(7 days)
删除过期的令牌的后台间隔 不可配置 3600000(1 hour) hadoop.kms.authentication.delegation-token.removal-scan-interval.sec 3600(1 hour)
主密钥滚动间隔 dfs.namenode.delegation.key.update-interval 86400000(1 day) hadoop.kms.authentication.delegation-token.update-interval.sec 86400(1 day)

附录B:使用委托令牌进行身份验证的示例代码

在查看下面的代码之前,需要理解的一个概念是UserGroupInformation(UGI)类。UGI是Hadoop的公共API,用于针对身份验证进行编码。它在下面的代码示例中使用,并且早些时候出现在某些异常堆栈跟踪中。
GetFileStatus用作使用UGI访问HDFS的示例。有关详细信息,请参见FileSystem类javadoc

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;
});

请注意,使用Kerberos身份验证调用addDelegationTokens的RPC调用。否则,将导致抛出IOException,信息为“只能使用kerberos或Web身份验证才能发布委托令牌”。
现在,我们可以使用获取的委托令牌来访问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;
});

参考连接:https://blog.cloudera.com/hadoop-delegation-tokens-explained/