Java登錄Kerberos認證過期問題

問題:

最近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/[email protected]
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 憑證有效期,來讓應用盡可能推遲到期時間。

參考:https://stackoverflow.com/questions/33211134/hbase-kerberos-connection-renewal-strategy/33243360

 

最終解決方案:

在連接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的有效期已經更新,目前運行了幾天,一起正常。後續繼續觀察。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章