Java登录Kerberos认证过期问题

9 篇文章 0 订阅
2 篇文章 0 订阅

该问题属于hadoop低版本的bug,升级到hadoop3.0.0+版本后,自动解决。

问题:

最近CDH集群增加了kerberos认证,发现了JavaWeb应用启动后,超过24小时后,kerberos凭证过期导致查询Hbase失败的问题。

Spark程序连接CDH时,通过principal和keytab配置方式,内部会将凭证到hdfs上,供Executor和Driver使用。当凭证Ticket快要失效时,会通过Keytab重新生成凭证。

spark2-submit \
 --master yarn \
 --deploy-mode cluster \
 --num-executors 3 \
 --driver-memory 4g \
 --executor-memory 4g \
 --executor-cores 2 \
 --conf spark.default.parallelism=10 \
 --conf spark.shuffle.file.buffe=1024k \
 --conf spark.executor.memoryOverhead=4096 \
 --class com.hypers.streaming.PanoramaMain \
 --conf "spark.executor.extraJavaOptions=-verbose:gc -XX:+PrintGCDetails" \
 --principal xxxx \
 --keytab xxxx.keytab \
  xxxx.jar

但我们自己开发的JavaWeb服务没有该机制,导致Ticket在程序运行时过期失效,最终查询Hbase时提示没有有效凭证,查询失败。

源码调试:

本地调试源码验证后,发现确实是因为Ticket的到期时间每次启动时候都会指定成一个固定的时间,后续也没有进行更新,最终过期导致应用查询Hbase失败。

重新编译了源码,增加了Ticket凭证相关的日志,最终确认了问题就是Ticket没有更新到期有效时间,导致认证过期。打印的日志如下:

checkTGTAndReloginFromKeytab! shouldRenewImmediatelyForTests:false, Time.now():1606492834061, getRefreshTime(tgt):1606526141000, tgt:Ticket (hex) = 
0000: 61 82 01 40 30 82 01 3C   A0 03 02 01 05 A1 0D 1B  a..@0..<........
0010: 0B 45 53 53 45 4E 43 45   2E 43 4F 4D A2 20 30 1E  .ESSENCE.COM. 0.
0020: A0 03 02 01 02 A1 17 30   15 1B 06 6B 72 62 74 67  .......0...krbtg
0030: 74 1B 0B 45 53 53 45 4E   43 45 2E 43 4F 4D A3 82  t..ESSENCE.COM..
0040: 01 02 30 81 FF A0 03 02   01 12 A1 03 02 01 01 A2  ..0.............
0050: 81 F2 04 81 EF B4 0C DB   10 F7 5F 24 41 A8 91 70  .........._$A..p
0060: A0 76 13 DE 32 C2 29 82   D3 2D D0 50 34 E3 A9 B1  .v..2.)..-.P4...
0070: 4B B0 CF 55 CF 2E 83 8C   CB AE 05 2E 77 C5 14 6B  K..U........w..k
0080: 56 B0 63 DD 32 A7 C9 BA   DE 51 9B FF 06 C5 01 6F  V.c.2....Q.....o
0090: 16 01 AD E2 BB C4 6E C5   B3 BC 35 FE 0D AF 20 49  ......n...5... I
00A0: CF F7 4C 90 C7 7F A0 F5   A1 19 A2 FF 3D FF AB 67  ..L.........=..g
00B0: EF 91 FA C4 ED 59 C1 53   38 7A BD B2 FF FD FC 84  .....Y.S8z......
00C0: E5 16 DE 9F 08 62 49 65   57 86 57 6D 03 A2 96 83  .....bIeW.Wm....
00D0: F9 80 1E E6 F7 E4 4B 4F   9C 00 55 B9 4A A2 DA E2  ......KO..U.J...
00E0: F2 01 86 11 1C B5 B1 01   9A F6 29 75 C2 D1 80 8A  ..........)u....
00F0: 90 7F 35 C2 D0 A2 65 C3   9A 8B 9C 00 5E 20 EB 6C  ..5...e.....^ .l
0100: CF 1A 04 FC 20 8C 7B 4B   98 0A 0F 08 36 EC 94 7E  .... ..K....6...
0110: AF 71 4D A1 E1 DA 95 4A   50 5A A8 1B 39 4A F0 B6  .qM....JPZ..9J..
0120: 99 60 71 C7 E4 05 1A 54   0C 05 50 C5 42 B0 97 08  .`q....T..P.B...
0130: 29 5A 48 7E 8F A7 C9 BD   A6 9B 19 E4 A7 2A ED 48  )ZH..........*.H
0140: 8F 5F 1A D2                                        ._..

Client Principal = @xx.COM
Server Principal = xxxx/xx.COM@xx.COM
Session Key = EncryptionKey: keyType=18 keyBytes (hex dump)=
0000: A2 ED 15 B4 25 87 D1 B8   70 23 96 41 48 4B B6 79  ....%...p#.AHK.y
0010: 96 60 1A 1D EF D7 50 C0   31 C6 F5 63 8A 65 56 9E  .`....P.1..c.eV.


Forwardable Ticket true
Forwarded Ticket false
Proxiable Ticket false
Proxy Ticket false
Postdated Ticket false
Renewable Ticket false
Initial Ticket false
Auth Time = Fri Nov 27 14:03:41 CST 2020
Start Time = Fri Nov 27 14:03:41 CST 2020
End Time = Sat Nov 28 14:03:41 CST 2020
Renew Till = null
Client Addresses  Null 
主要是End Time、Renew Till字段,源码里的解释:
/**
 * Returns the start time for this ticket's validity period.
 *
 * @return the start time for this ticket's validity period
 *         or null if not set.
 */
public final java.util.Date getStartTime() {
    return (startTime == null) ? null : (Date)startTime.clone();
}

/**
 * Returns the expiration time for this ticket's validity period.
 *
 * @return the expiration time for this ticket's validity period.
 */
public final java.util.Date getEndTime() {
    return (endTime == null) ? null : (Date) endTime.clone();
}

/**
 * Returns the latest expiration time for this ticket, including all
 * renewals. This will return a null value for non-renewable tickets.
 *
 * @return the latest expiration time for this ticket.
 */
public final java.util.Date getRenewTill() {
    return (renewTill == null) ? null: (Date)renewTill.clone();
}

解决思路:

最后找到的解决方案是需要我们自己去定期刷新(重新登录)凭证来使凭证永久生效,或者指定一个较长的 ticket_lifetime 凭证有效期,来让应用尽可能推迟到期时间。

参考:HBase Kerberos connection renewal strategy - Stack Overflow

最终解决方案:

在连接Hbase,触发了首次登录Kerberos的时候,启动一个定时任务,每次定时调用checkTGTAndReloginFromKeytab()方法来更新凭证,来达到定时更新的效果。

  conf.addResource("hbase-site-test.xml");
                            conf.addResource("core-site-test.xml");
                            conf.addResource("hdfs-site-test.xml");
                            try {
                                System.setProperty("java.security.krb5.conf", Constants.KRB5_CONF_PATH);
                                UserGroupInformation.setConfiguration(conf);
                                UserGroupInformation.loginUserFromKeytab(Constants.KEYTAB_USER, Constants.KEYTAB_PATH);
                                startCheckKeytabTgtAndReloginJob(); // 定时更新凭证任务
                            } catch (IOException e) {
                                logger.error("Login failure", e);
                            }
 /**
     * * 定时更新凭证
     */
    private static void startCheckKeytabTgtAndReloginJob() {
        //10分钟循环 达到距离到期时间一定范围就会更新凭证
        ThreadPool.updateConfigThread.scheduleWithFixedDelay(() -> {
            try {
                UserGroupInformation.getLoginUser().checkTGTAndReloginFromKeytab();
                logger.warn("get tgt:{}", UserGroupInformation.getLoginUser().getTGT());
                logger.warn("Check Kerberos Tgt And Relogin From Keytab Finish.");
            } catch (IOException e) {
                logger.error("Check Kerberos Tgt And Relogin From Keytab Error", e);
            }
        }, 0, 10, TimeUnit.MINUTES);
        logger.warn("Start Check Keytab TGT And Relogin Job Success.");
    }

注:也可通过reloginFromTicketCache()方法进行更新,但依赖于服务器上的认证缓存。或者直接调用kinit -R来更新(参考Hadoop源码中的续期方式)。

            // 参考Hadoop源码中的续期方式
            try {
                String cmd = "kinit";
                Shell.execCommand(cmd, "-R");
                log.warn("exec kinit -R!");
            } catch (IOException e) {
               log.warn("relogin error.",e);
            }
            try {
                UserGroupInformation.getLoginUser().reloginFromTicketCache();
            } catch (IOException e) {
                log.warn("relogin error.",e);
            }

观察结论:

Ticket的有效期已经更新,目前运行了几天,一起正常。后续继续观察。

  • 15
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
使用Java API连接使用Kerberos认证的Elasticsearch集群,你需要遵循以下步骤: 1. 首先,你需要为你的Java应用程序配置Kerberos认证。这可以通过在你的应用程序中使用JAAS(Java Authentication and Authorization Service)框架来完成。 2. 然后,你需要在Elasticsearch集群中启用Kerberos认证。这可以通过在elasticsearch.yml配置文件中设置以下属性来完成: ``` xpack.security.authc.realms: kerberos.kerb1: type: kerberos order: 0 krb5_file_path: /path/to/krb5.conf keytab_path: /path/to/elasticsearch.keytab ``` 其中,`krb5_file_path`是指向Kerberos配置文件的路径,`keytab_path`是指向Elasticsearch服务的keytab文件路径。 3. 接下来,你需要使用Java API创建一个Elasticsearch客户端,并使用Kerberos认证进行身份验证。以下是一个示例代码: ``` public class KerberosESClient { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); conf.setBoolean("hadoop.security.authentication", true); UserGroupInformation.setConfiguration(conf); UserGroupInformation.loginUserFromKeytab("your_principal", "/path/to/your/keytab"); Settings settings = Settings.builder() .put("cluster.name", "your_cluster_name") .put("xpack.security.user", "your_username:your_password") .put("client.transport.sniff", true) .build(); TransportClient client = new PreBuiltXPackTransportClient(settings) .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("your_es_host"), 9300)); SearchResponse response = client.prepareSearch().execute().actionGet(); client.close(); } } ``` 在上面的代码中,`UserGroupInformation`类用于从指定的keytab文件中获取Kerberos凭证,然后使用这些凭证创建一个Elasticsearch客户端。`Settings`类用于配置一些连接参数,例如集群名称、节点授权信息等。`TransportClient`类用于实现与Elasticsearch节点的通信,可以使用`prepareSearch`方法发送一个查询请求并获取结果。 希望这个回答能够帮助到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值