附錄

附錄 A:本文件中使用的材料

示例中使用的虛擬 UserDetailsService,因為我們沒有真實的使用者源。

public class DummyUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username)
            throws UsernameNotFoundException {
        return new User(username, "notUsed", true, true, true, true,
                AuthorityUtils.createAuthorityList("ROLE_USER"));
    }

}

附錄 B:Kerberos 速成課

在任何認證過程中,通常涉及三方。

drawio kerb cc1

首先是 client(客戶端),有時它是一臺客戶端計算機,但在大多數情況下,它是坐在計算機前試圖訪問資源的實際使用者。然後是使用者試圖訪問的 resource(資源)。在本例中,它是一臺 web 伺服器。

然後是 Key Distribution Center(金鑰分發中心)或 KDC。在 Windows 環境中,它將是 Domain Controller(域控制器)。KDC 是真正將所有事物連線在一起的元件,因此是您環境中最關鍵的元件。正因為如此,它也被認為是單點故障。

最初設定 Kerberos 環境並將域使用者主體建立到資料庫中時,也會建立加密金鑰。這些加密金鑰基於共享秘密(即使用者密碼),實際密碼從不以明文形式儲存。實際上,KDC 擁有自己的金鑰以及用於域使用者的其他金鑰。

有趣的是,在認證過程中,resource(資源)和 KDC 之間沒有通訊。

drawio kerb cc2

當客戶端希望使用 resource 進行自我認證時,它首先需要與 KDC 通訊。Client 將構建一個包含加密和未加密部分的特殊資料包。未加密部分包含使用者資訊,加密部分包含協議中的其他資訊。Client 將使用自己的金鑰對資料包資料進行加密。

KDC 從客戶端接收到此認證資料包時,它會檢查未加密部分中的 client 聲稱是誰,並根據該資訊使用其資料庫中已有的 client 解密金鑰。如果解密成功,KDC 就知道此 client 就是它聲稱的身份。

KDC 返回給客戶端的是一個稱為 Ticket Granting Ticket(票據授予票據)的票據,該票據由 KDC 自己的私鑰簽名。稍後,當 client 將此票據傳送回來時,它可以嘗試解密它,如果操作成功,它就知道這是 KDC 最初自己簽名並提供給 client 的票據。

drawio kerb cc3

當客戶端想要獲取可用於向服務進行認證的票據時,會將 TGT 傳送到 KDC,然後 KDC 使用服務自己的金鑰簽署服務票據。這時,clientservice 之間建立了信任。此服務票據包含只有 service 本身才能解密的資料。

drawio kerb cc4

client 向服務進行認證時,它會將之前收到的服務票據傳送給服務,然後服務會想:“我對此人一無所知,但他給了我一個認證票據。” 接下來 service 可以嘗試解密該票據,如果操作成功,它就知道唯一知道我的憑據的另一方是 KDC,並且由於我信任他,我也可以信任此客戶端就是他聲稱的身份。

附錄 C:設定 Kerberos 環境

Kerberos 生產環境的設定超出了本文件的範圍,但本附錄提供了一些幫助,以便您開始為開發設定所需的元件。

設定 MIT Kerberos

第一步是設定一個新的領域和資料庫。

# kdb5_util create -s -r EXAMPLE.ORG
Loading random data
Initializing database '/var/lib/krb5kdc/principal' for realm 'EXAMPLE.ORG',
master key name 'K/[email protected]'
You will be prompted for the database Master Password.
It is important that you NOT FORGET this password.
Enter KDC database master key:
Re-enter KDC database master key to verify:

kadmin 命令可用於管理 Kerberos 環境,但您尚不能使用它,因為資料庫中沒有管理員使用者。

root@neo:/etc/krb5kdc# kadmin
Authenticating as principal root/[email protected] with password.
kadmin: Client not found in Kerberos database while initializing
kadmin interface

讓我們使用 kadmin.local 命令建立一個。

root@neo:/etc/krb5kdc# kadmin.local
Authenticating as principal root/[email protected] with password.

kadmin.local:  listprincs
K/[email protected]
kadmin/[email protected]
kadmin/[email protected]
kadmin/[email protected]
krbtgt/[email protected]

kadmin.local:  addprinc root/[email protected]
WARNING: no policy specified for root/[email protected]; defaulting to
no policy
Enter password for principal "root/[email protected]":
Re-enter password for principal "root/[email protected]":
Principal "root/[email protected]" created.

然後透過修改 kadm5.acl 檔案並重新啟動 Kerberos 服務來啟用管理員。

# cat /etc/krb5kdc/kadm5.acl
# This file Is the access control list for krb5 administration.
*/admin *

現在,您可以使用先前建立的 root/admin 主體來使用 kadmin。讓我們建立第一個使用者 user1

kadmin:  addprinc user1
WARNING: no policy specified for [email protected]; defaulting to no
policy
Enter password for principal "[email protected]":
Re-enter password for principal "[email protected]":
Principal "[email protected]" created.

讓我們建立第二個使用者 user2 並匯出 keytab 檔案。

kadmin:  addprinc user2
WARNING: no policy specified for [email protected]; defaulting to no
policy
Enter password for principal "[email protected]":
Re-enter password for principal "[email protected]":
Principal "[email protected]" created.

kadmin:  ktadd -k /tmp/user2.keytab [email protected]
Entry for principal [email protected] with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/user2.keytab.
Entry for principal [email protected] with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/tmp/user2.keytab.
Entry for principal [email protected] with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/tmp/user2.keytab.
Entry for principal [email protected] with kvno 2, encryption type des-cbc-crc added to keytab WRFILE:/tmp/user2.keytab.

讓我們為 tomcat 建立服務票據,並將憑據匯出到名為 tomcat.keytab 的 keytab 檔案。

kadmin:  addprinc -randkey HTTP/[email protected]
WARNING: no policy specified for HTTP/[email protected];
defaulting to no policy
Principal "HTTP/[email protected]" created.

kadmin:  ktadd -k /tmp/tomcat.keytab HTTP/[email protected]
Entry for principal HTTP/[email protected] with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/tmp/tomcat2.keytab.
Entry for principal HTTP/[email protected] with kvno 2, encryption type arcfour-hmac added to keytab WRFILE:/tmp/tomcat2.keytab.
Entry for principal HTTP/[email protected] with kvno 2, encryption type des3-cbc-sha1 added to keytab WRFILE:/tmp/tomcat2.keytab.
Entry for principal HTTP/[email protected] with kvno 2, encryption type des-cbc-crc added to keytab WRFILE:/tmp/tomcat2.keytab.

設定 Windows 域控制器

這是使用 Windows Server 2012 R2 測試的

網際網路上有大量關於如何設定 Windows AD 的優秀文章和影片,但這兩篇非常有用:RackspaceMicrosoft Technet

  • 完成了正常的域控制器和活動目錄設定。

  • 使用了 dns 域 example.org 和 windows 域 EXAMPLE

  • 我建立了各種域使用者,如 user1user2user3tomcat,並將密碼設定為 Password#

我最終還將所有 vm 的 IP 地址新增到 AD 的 dns 伺服器中,以避免任何問題。

Name: WIN-EKBO0EQ7TS7.example.org
Address: 172.16.101.135

Name: win8vm.example.org
Address: 172.16.101.136

Name: neo.example.org
Address: 172.16.101.1

需要使用 HTTP 和執行 tomcat servlet 容器的伺服器名稱 neo.example.org 設定服務主體名稱(SPN)。這與 tomcat 域使用者一起使用,其 keytab 被用作服務憑據。

PS C:\> setspn -A HTTP/neo.example.org tomcat

我匯出了 keytab 檔案,並將其複製到執行 tomcat 的 linux 伺服器上。

PS C:\> ktpass /out c:\tomcat.keytab /mapuser [email protected] /princ HTTP/[email protected] /pass Password# /ptype KRB5_NT_PRINCIPAL /crypto All
 Targeting domain controller: WIN-EKBO0EQ7TS7.example.org
 Using legacy password setting method
 Successfully mapped HTTP/neo.example.org to tomcat.

附錄 D:故障排除

本附錄提供了有關故障排除錯誤和問題的通用資訊。

如果您認為環境和配置已正確設定,請仔細檢查並請其他人檢查可能存在的明顯錯誤或拼寫錯誤。Kerberos 的設定通常非常脆弱,並且除錯問題所在並不總是那麼容易。

找不到適當型別的金鑰進行解密
GSSException: Failure unspecified at GSS-API level (Mechanism level:
Invalid argument (400) - Cannot find key of appropriate type to
decrypt AP REP - RC4 with HMAC)

如果您看到上面指示缺少金鑰型別的錯誤,這會在兩種不同的用例中發生。首先,您的 JVM 可能不支援適當的加密型別,或者它在您的 krb5.conf 檔案中被停用。

default_tkt_enctypes = rc4-hmac
default_tgs_enctypes = rc4-hmac

第二種情況不太明顯且難以追蹤,因為它會導致相同的錯誤。如果您只是缺少所需的加密金鑰,也會丟擲此特定的 GSSException,這可能是由您的 kerberos 伺服器配置錯誤或主體中的簡單拼寫錯誤引起的。

使用錯誤的 kerberos 配置


在大多數系統中,所有命令和庫都會從預設位置或 JDK 等特殊位置搜尋 kerberos 配置。特別是在從 unix 系統(可能已經有與 MIT kerberos 配合使用的預設設定)轉向 Windows 域時,很容易混淆。

這是一個具體的例子,說明 ldapsearch 嘗試使用 kerberos 認證查詢 Windows AD 時會發生什麼。

$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b "dc=example,dc=org"
SASL/GSSAPI authentication started
ldap_sasl_interactive_bind_s: Local error (-2)
  additional info: SASL(-1): generic failure: GSSAPI Error:
  Unspecified GSS failure.  Minor code may provide more information
  (No Kerberos credentials available)

這看起來不太好,並且簡單地表明我沒有有效的 kerberos 票據,如下所示。

$ klist
klist: Credentials cache file '/tmp/krb5cc_1000' not found

我們已經有一個從 Windows AD 匯出用於在 Linux 上執行的 tomcat 的 keytab 檔案。讓我們嘗試使用它來向 Windows AD 進行認證。

您可以有一個專用的配置檔案,通常可以透過系統屬性與原生 Linux 命令和 JVM 一起使用。

$ cat krb5.ini
[libdefaults]
default_realm = EXAMPLE.ORG
default_keytab_name = /tmp/tomcat.keytab
forwardable=true

[realms]
EXAMPLE.ORG = {
  kdc = WIN-EKBO0EQ7TS7.example.org:88
}

[domain_realm]
example.org=EXAMPLE.ORG
.example.org=EXAMPLE.ORG

讓我們使用該配置和 keytab 來獲取初始憑據。

$ env KRB5_CONFIG=/path/to/krb5.ini kinit -kt tomcat.keytab HTTP/[email protected]

$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: HTTP/[email protected]

Valid starting     Expires            Service principal
26/03/15 09:04:37  26/03/15 19:04:37  krbtgt/[email protected]
  renew until 27/03/15 09:04:37

現在讓我們看看如果嘗試對 Windows AD 執行簡單查詢會發生什麼。

$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b "dc=example,dc=org"
SASL/GSSAPI authentication started
ldap_sasl_interactive_bind_s: Local error (-2)
  additional info: SASL(-1): generic failure: GSSAPI Error:
  Unspecified GSS failure.  Minor code may provide more information
  (KDC returned error string: PROCESS_TGS)

這可能僅僅是因為 ldapsearch 混淆並使用了錯誤的配置。您可以透過 KRB5_CONFIG 環境變數告訴 ldapsearch 使用不同的配置,就像我們使用 kinit 所做的那樣。您還可以使用 KRB5_TRACE=/dev/stderr 來獲取原生庫正在做什麼的更詳細輸出。

$ env KRB5_CONFIG=/path/to/krb5.ini ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org -b "dc=example,dc=org"

$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: HTTP/[email protected]

Valid starting     Expires            Service principal
26/03/15 09:11:03  26/03/15 19:11:03  krbtgt/[email protected]
  renew until 27/03/15 09:11:03
  26/03/15 09:11:44  26/03/15 19:11:03
  ldap/[email protected]
    renew until 27/03/15 09:11:03

上面您可以透過檢視 kerberos 票據來了解查詢成功時發生了什麼。現在您可以進一步嘗試查詢命令,例如,如果您正在使用 KerberosLdapContextSource

$ ldapsearch -H ldap://WIN-EKBO0EQ7TS7.example.org \
-b "dc=example,dc=org" \
"(| ([email protected])
([email protected]))" \
dn

...
# test user, example.org
dn: CN=test user,DC=example,DC=org

附錄 E:為 Spnego 協商配置瀏覽器

Firefox

完成以下步驟,確保您的 Firefox 瀏覽器已啟用 Spnego 認證。

  • 開啟 Firefox。

  • 在位址列中,輸入 about:config

  • 在過濾/搜尋框中,輸入 negotiate

  • 引數 network.negotiate-auth.trusted-uris 可能被設定為預設的 https://,這對您不起作用。一般來說,如果需要 Kerberos 委託,必須將此引數替換為伺服器地址。

  • 建議所有通訊都使用 https

Chrome

對於 Google Chrome,通常需要設定命令列引數以將 Chrome 將協商的伺服器列入白名單。

  • 在 Windows 機器(客戶端)上:Chrome 與 Internet Explorer 共享配置,因此如果所有更改都已應用於 IE(如 E.3 中所述),則無需透過命令列引數傳遞任何內容。

  • 在 Linux/Mac OS 機器(客戶端)上:命令列引數 --auth-negotiate-delegate-whitelist 只應在需要 Kerberos 委託時使用(否則不要設定此引數)。

  • 建議所有通訊都使用 https

--auth-server-whitelist="*.example.com"
--auth-negotiate-delegate-whitelist="*.example.com"

透過在 Chrome 的位址列中輸入 chrome://policy/,您可以檢視哪些策略已啟用。

在 Linux 上,Chrome 還會從 /etc/opt/chrome/policies/managed 目錄讀取策略檔案。

mypolicy.json
{
  "AuthServerWhitelist" : "*.example.org",
  "AuthNegotiateDelegateWhitelist" : "*.example.org",
  "DisableAuthNegotiateCnameLookup" : true,
  "EnableAuthNegotiatePort" : true
}

Internet Explorer

完成以下步驟,確保您的 Internet Explorer 瀏覽器已啟用 Spnego 認證。

  • 開啟 Internet Explorer。

  • 點選 工具 > Internet 選項 > 安全 選項卡。

  • 本地 intranet 部分,透過將其新增到列表等方式確保您的伺服器受到信任。