hdfs/hbase 程式利用Kerberos認證超過ticket_lifetime期限後異常

来源:https://www.cnblogs.com/gaofeng-henu/archive/2020/01/14/12190580.html
-Advertisement-
Play Games

問題描述 業務需要一個長期運行的程式,將上傳的文件存放至HDFS,程式啟動後,剛開始一切正常,執行一段時間(一般是一天,有的現場是三天),就會出現認證錯誤,用的JDK是1.8,hadoop client,對應的版本是2.5.1,為什麼強調這個版本號,因為錯誤的根本原因就在於版本問題 錯誤日誌 業務程 ...


問題描述

業務需要一個長期運行的程式,將上傳的文件存放至HDFS,程式啟動後,剛開始一切正常,執行一段時間(一般是一天,有的現場是三天),就會出現認證錯誤,用的JDK是1.8,hadoop-client,對應的版本是2.5.1,為什麼強調這個版本號,因為錯誤的根本原因就在於版本問題

錯誤日誌

Caused by: org.ietf.jgss.GSSException: No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)
    at sun.security.jgss.krb5.Krb5InitCredential.getInstance(Krb5InitCredential.java:147) ~[?:1.8.0_212]
    at sun.security.jgss.krb5.Krb5MechFactory.getCredentialElement(Krb5MechFactory.java:122) ~[?:1.8.0_212]
    at sun.security.jgss.krb5.Krb5MechFactory.getMechanismContext(Krb5MechFactory.java:187) ~[?:1.8.0_212]
    at sun.security.jgss.GSSManagerImpl.getMechanismContext(GSSManagerImpl.java:224) ~[?:1.8.0_212]
    at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:212) ~[?:1.8.0_212]
    at sun.security.jgss.GSSContextImpl.initSecContext(GSSContextImpl.java:179) ~[?:1.8.0_212]
    at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(GssKrb5Client.java:192) ~[?:1.8.0_212]
    at org.apache.hadoop.security.SaslRpcClient.saslConnect(SaslRpcClient.java:413) ~[hadoop-common-2.5.1.jar:?]
    at org.apache.hadoop.ipc.Client$Connection.setupSaslConnection(Client.java:552) ~[hadoop-common-2.5.1.jar:?]
    at org.apache.hadoop.ipc.Client$Connection.access$1800(Client.java:367) ~[hadoop-common-2.5.1.jar:?]
    at org.apache.hadoop.ipc.Client$Connection$2.run(Client.java:717) ~[hadoop-common-2.5.1.jar:?]
    at org.apache.hadoop.ipc.Client$Connection$2.run(Client.java:713) ~[hadoop-common-2.5.1.jar:?]
    at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_212]
    at javax.security.auth.Subject.doAs(Subject.java:422) ~[?:1.8.0_212]
    at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1614) ~[hadoop-common-2.5.1.jar:?]
    at org.apache.hadoop.ipc.Client$Connection.setupIOstreams(Client.java:712) ~[hadoop-common-2.5.1.jar:?]
    at org.apache.hadoop.ipc.Client$Connection.access$2800(Client.java:367) ~[hadoop-common-2.5.1.jar:?]
    at org.apache.hadoop.ipc.Client.getConnection(Client.java:1463) ~[hadoop-common-2.5.1.jar:?]
    at org.apache.hadoop.ipc.Client.call(Client.java:1382) ~[hadoop-common-2.5.1.jar:?]
    ... 61 more

業務程式調用認證方法


public void init() {

    System.setProperty("java.security.krb5.conf", "krb5.conf");
                        
}

public void kerberosLogin() throws IOException {
        // 已經認證通過
        if ("hdfsuser".concat("@").concat("DATAHOUSE.COM")
                .equals(UserGroupInformation.getCurrentUser().getUserName())) {
            UserGroupInformation.getCurrentUser().checkTGTAndReloginFromKeytab();
            return;
        }
        // ksbName 表示用戶名 keytabPath表示秘鑰存放位置
        UserGroupInformation.loginUserFromKeytab("hdfsuser", "/etc/keytab/hdfsuser.keytab");
    
    }
    
  • 主要思想就是第一次認證通過loginUserFromKeytab進行認證,之後每次請求再調用checkTGTAndReloginFromKeytab方法判斷是否需要重新認證,防止ticket過期

  • 應用在每次獲取FileSystem時,都會先調用kerberosLogin,之後才獲取FileSystem

public FileSystem getFileSystem() throws IOException {
        
        try {
            kerberosLogin();
            return FileSystem.get(configuration);
        } catch (Exception e) {
            logger.error("create hdfs FileSystem has error", e);
            throw e;
        }
    }

問題調查過程

根據錯誤在網上各種搜索,出來的結果和上面的代碼大同小異,有的猜測是客戶端調用間隔太大,超過了ticket_lifetime的值,建議加一個定時任務來周期性的調用kerberosLogin()方法,雖然我們業務不太可能出現這種情況,還是加上了這個處理,問題依舊,只好開始慢慢調試

UserGroupInformation.loginUserFromKeytab的認證過程

  1. UserGroupInformation.loginUserFromKeytab 利用傳入的user和keytab路徑信息,構建一個LoginContext,接著調用LoginContext的login方法

    try {
      login = newLoginContext(HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME,
            subject, new HadoopConfiguration());
      start = Time.now();
      login.login();
    
    。。。
  2. LoginContext.login方法依次通過反射調用了登陸模塊的login和commit兩個方法,調用的主要邏輯在invokePriv方法內

    public void login() throws LoginException {
    
        ...
    
        try {
            // module invoked in doPrivileged
            invokePriv(LOGIN_METHOD);
            invokePriv(COMMIT_METHOD);
    
    ...
  3. LoginContext.invokePriv方法主要在doPrivileged內調用invoke方法,invoke方法依次調用登陸模塊對應的方法,第一次調用時,還會調用對應的initialize方法

    for (int i = moduleIndex; i < moduleStack.length; i++, moduleIndex++) {
            try {
    
                ...
    
                    // 查找initialize方法
                    methods = moduleStack[i].module.getClass().getMethods();
                    for (mIndex = 0; mIndex < methods.length; mIndex++) {
                        if (methods[mIndex].getName().equals(INIT_METHOD)) {
                            break;
                        }
                    }
    
                    Object[] initArgs = {subject,
                                        callbackHandler,
                                        state,
                                        moduleStack[i].entry.getOptions() };
                    // 調用 initialize 方法
                    methods[mIndex].invoke(moduleStack[i].module, initArgs);
                }
    
                // 接著查找相應的方法
                for (mIndex = 0; mIndex < methods.length; mIndex++) {
                    if (methods[mIndex].getName().equals(methodName)) {
                        break;
                    }
                }
    
                // set up the arguments to be passed to the LoginModule method
                Object[] args = { };
    
                // 調用相應的方法
    
                boolean status = ((Boolean)methods[mIndex].invoke
                                (moduleStack[i].module, args)).booleanValue();

實際執行時對應的moduleStack中有兩個LoginModule

  • HadoopLoginModule :和kerberos認證關係不大,暫且不看
  • Krb5LoginModule : kerberos認證類,根據第2步LoginContext.login中的方法可知,會依次調用這個module中的login和commit兩個方法
  1. Krb5LoginModule.login方法,就是利用我們提供的user名稱和krb5.conf中的配置信息以及keytab信息進行認證。代碼就不展示了,主要是調用attemptAuthentication進行的處理。
  2. Krb5LoginModule.commit方法是要把認證後證書信息存入到Subject中,以便後續能重覆使用subject進行認證,和本次調查問題有關的代碼片段如下

        public boolean commit() throws LoginException {
    
        Set<Object> privCredSet =  subject.getPrivateCredentials();
    
            。。。
    
    
                    if (ktab != null) {
                        if (!privCredSet.contains(ktab)) {
                            // 把keytab保存下來,再次認證使用
                            privCredSet.add(ktab);
                        }
                    } else {
                        succeeded = false;
                        throw new LoginException("No key to store");
                    }
    
            。。。
    
  3. 按照這個邏輯,既然keytab保存到Subject中了,再次使用UserGroupInformation.getCurrentUser().checkTGTAndReloginFromKeytab();進行認證時,就可以使用保存的keytab直接認證了,應該是不會出錯的,我們看下checkTGTAndReloginFromKeytab方法

     public synchronized void checkTGTAndReloginFromKeytab() throws IOException     {
    if (!isSecurityEnabled()
        || user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS
        || !isKeytab)
      return;
    KerberosTicket tgt = getTGT();
    if (tgt != null && Time.now() < getRefreshTime(tgt)) {
      return;
    }
    reloginFromKeytab();
     }

    方法邏輯,就是判斷如果是用keytab進行的認證,就調用reloginFromKeytab進行認證。但在實際執行時卻發現isKeytab的值是false,可代碼明明是使用keytab來認證的,怎麼是false呢,只能看看isKeytab這個值怎麼賦值的了,對應邏輯在UserGroupInformation的構造函數里

    
        UserGroupInformation(Subject subject) {
            ...
            this.isKeytab = !subject.getPrivateCredentials(KerberosKey.class).isEmpty();
            ...
        }
    
  4. 至此終於發現問題所在,我們在第5步,認證成功後在subject的PrivateCredentials中存入的是keytab對象,而這個地方判斷的是KerberosKey,這肯定是不一樣呀,那就只有一種可能,就是引用jar包的版本問題了。更換hadoop-client的版本號為2.10.0,再查看UserGroupInformation對應的構造函數

    private UserGroupInformation(Subject subject, final boolean externalKeyTab) {
    ...
      this.isKeytab = KerberosUtil.hasKerberosKeyTab(subject);
    ...
     }

    將判斷邏輯移到了KerberosUtil.hasKerberosKeyTab方法中

/** * Check if the subject contains Kerberos keytab related objects. * The Kerberos keytab object attached in subject has been changed * from KerberosKey (JDK 7) to KeyTab (JDK 8) * * * @param subject subject to be checked * @return true if the subject contains Kerberos keytab */ public static boolean hasKerberosKeyTab(Subject subject) { return !subject.getPrivateCredentials(KeyTab.class).isEmpty(); }
可以看到判斷對象已經變成了KeyTab了,並且從註釋信息中明確看到在JDK7時使用的是KerberosKey,在JDK8時換成了KeyTab。

總結,kerberos認證功能雖然強大,實際使用還是有點複雜,特別是和jaas結合後,出了錯還是有些難調查,可只要慢慢分析,還是會找到解決方法的,還有一點就是雖然程式出現的錯誤一樣,引起錯誤的根本原因還是會有所不同,不能只是按照網上說法一改就萬事大吉,有時還是需要靠我們自己刨根問底好好研究。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • zabbix 基礎 第1章 關於zabbix 1.1 為什麼要使用監控 1.對系統不間斷實時監控2.實時反饋系統當前狀態3.保證服務可靠性安全性4.保證業務持續穩定運行 1.2 如何進行監控 比如我們需要監控磁碟的使用率1.如何查看磁碟使用率df h2.監控磁碟的那些指標block、inode3.如 ...
  • 場景 在使用RedisDeskTopManager客戶端可視化工具連接Redis服務端時提示: 註: 博客: https://blog.csdn.net/badao_liumang_qizhi 關註公眾號 霸道的程式猿 獲取編程相關電子書、教程推送與免費下載。 實現 出現以上情況需要檢查 1.red ...
  • 轉自 http://www.maomao365.com/?p=9858 摘要: 下文講述sqlserver中最快獲取一個字元串在另一個字元串中出現個數的方法分享 實驗環境:sql server 2008 R2 實現思路: 1.使用replace替換字元串後 2.使用Len獲取替換後的長度, 3.得出 ...
  • 利用空閑時間花幾分鐘回顧一下 7.1 檢索數據 為了查詢出資料庫表中的行(數據),使用SELECE語句。 格式: 第一種寫法使用\ 通配符,會把表中行的列全部查詢出來,而不必取一一列出全部列。但是不推薦使用,這跟INSERT語句的規範寫法一樣。 使用\ 通配符,列的順序一般是列在表定義中出現的順序, ...
  • 原創文章,轉載務必註明出處。 今天工作的時候,碰到一個問題,涉及oracle行轉列,用了半小時解決,因此在這裡寫個博客記錄一下解決辦法。 原資料庫表的數據是: 想要達到的效果是: 經過思考,這是一個oracle行轉列的操作,根據xn,qxdm,nj這三列值來分組查詢所有的數據,把xn,qxdm,nj ...
  • 問題描述:需要將140伺服器中的tbomnew實例下的部分用戶導入到118伺服器下的tbompx實例中,本次導入導出的兩個資料庫均為19C 部分用戶名:CORE,MSTDATA,BOMMGMT,CFGMGMT,CHGMGMT,CUST,integration 1.導入指定140伺服器的實例名tbom ...
  • 場景 Centos中Redis的下載編譯與安裝(超詳細): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/103967334 在上面安裝好Redis後會在安裝目錄下的bin下自動生成一堆腳本。 其中redis-server就是re ...
  • 一、記憶體文件系統足夠的緩存 Elasticsearch嚴重依賴於文件系統緩存,以加快搜索速度。通常,您應確保至少有一半的可用記憶體分配給文件系統緩存,以便Elasticsearch可以將索引的熱區保留在物理記憶體中。 二、使用更快的硬體 如果搜索是受CPU限制的,那就加大CPU。ES對CPU的要求,使用 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...