附录

附录 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 部分,通过将其添加到列表等方式确保您的服务器受到信任。