Kerberos 身份验证
Kerberos 身份验证是 Windows 环境中用于身份验证的一种现代方法。它允许客户端和服务器相互验证身份,并支持 AES 等现代加密方法。
安装 Kerberos
Kerberos 通过 GSSAPI 库提供,该库是系统软件包的一部分。某些发行版默认安装 Kerberos 软件包,而其他发行版可能需要手动安装。
要在基于 RHEL/Fedora 的系统上安装 Kerberos 库
$ sudo dnf install krb5-devel krb5-libs krb5-workstation python3-devel
对于基于 Debian/Ubuntu 的系统
$ sudo apt-get install krb5-user libkrb5-dev python3-dev
对于基于 Arch Linux 的系统
$ sudo pacman -S krb5
对于基于 FreeBSD 的系统
$ sudo pkg install heimdal
注意
如果使用 ssh 连接插件与 Kerberos 一起使用,则可以忽略 python3-devel / python3-dev 包。只有在使用基于 WinRM 的连接以及 Kerberos 身份验证时才需要它们。
安装完成后,kinit、klist 和 krb5-config 包将可用。您可以使用以下命令测试它们
$ krb5-config --version
Kerberos 5 release 1.21.3
psrp 和 winrm 连接插件需要额外的 Python 库才能进行 Kerberos 身份验证。如果使用 Kerberos 与 ssh 连接,则可以跳过以下步骤。
如果您选择 Ansible 的 pipx 安装说明,则可以通过运行以下命令来安装这些需求
pipx inject "pypsrp[kerberos]<=1.0.0"  # for psrp
pipx inject "pywinrm[kerberos]>=0.4.0"  # for winrm
或者,如果您选择 pip 安装说明
pip3 install "pypsrp[kerberos]<=1.0.0"  # for psrp
pip3 install "pywinrm[kerberos]>=0.4.0"  # for winrm
配置主机 Kerberos
安装依赖项后,需要配置 Kerberos 以便它可以与域通信。大多数 Kerberos 实现可以使用 DNS 或通过 /etc/krb5.conf 文件中的手动配置来查找域。有关可以在 /etc/krb5.conf 文件中设置的内容的详细信息,请参阅 krb5.conf 以了解更多详细信息。一个使用 DNS 来查找 KDC 的简单 krb5.conf 文件如下所示
[libdefaults]
    # Not required but helpful if the realm cannot be determined from
    # the hostname
    default_realm = MY.DOMAIN.COM
    # Enabled KDC lookups from DNS SRV records
    dns_lookup_kdc = true
使用上述配置,当为服务器 server.my.domain.com 请求 Kerberos 票据时,Kerberos 库将对 _kerberos._udp.my.domain.com 和 _kerberos._tcp.my.domain.com 执行 SRV 查询以查找 KDC。如果您希望手动设置 KDC 领域,可以使用以下配置
[libdefaults]
    default_realm = MY.DOMAIN.COM
    dns_lookup_kdc = false
[realms]
    MY.DOMAIN.COM = {
        kdc = domain-controller1.my.domain.com
        kdc = domain-controller2.my.domain.com
    }
[domain_realm]
    .my.domain.com = MY.DOMAIN.COM
    my.domain.com = MY.DOMAIN.COM
使用此配置,任何具有 DNS 后缀 .my.domain.com 和 my.domain.com 本身的票据请求都将发送到 KDC domain-controller1.my.domain.com,并回退到 domain-controller2.my.domain.com。
有关 Kerberos 库尝试查找 KDC 的方法的更多信息,请参阅 MIT Kerberos 文档。
注意
本节中的信息假设您使用的是 MIT Kerberos 实现,这通常是大多数 Linux 发行版的默认实现。某些平台(如 FreeBSD 或 macOS)使用不同的 GSSAPI 实现,称为 Heimdal,其作用方式类似于 MIT Kerberos,但某些行为可能有所不同。
验证 Kerberos 配置
要验证 Kerberos 是否正常工作,您可以使用 kinit 命令为域中的用户获取票据。以下命令将为域 MY.DOMAIN.COM 中的用户 username 请求票据
$ kinit username@MY.DOMAIN.COM
Password for username@REALM.COM
如果密码正确,命令将返回而没有任何输出。要验证是否已获得票据,可以使用 klist 命令
> klist
Ticket cache: KCM:1000
Default principal: username@MY.DOMAIN.COM
Valid starting     Expires            Service principal
29/08/24 13:54:51  29/08/24 23:54:51  krbtgt/MY.DOMAIN.COM@MY.DOMAIN.COM
        renew until 05/09/24 13:54:48
如果成功,则验证 Kerberos 配置正确,并且用户可以从 KDC 获取票据授予票据(TGT)。如果 kinit 无法为请求的领域找到 KDC,请通过确保 DNS 可以使用 SRV 记录找到 KDC 或 KDC 在 krb5.conf 中手动映射来验证您的 Kerberos 配置。
在基于 MIT Kerberos 的系统上,您可以使用 kvno 命令验证您是否能够为特定服务检索服务票据。例如,如果您使用基于 WinRM 的连接来使用 server.my.domain.com 进行身份验证,则可以使用以下命令来验证您的 TGT 是否能够为目标服务器获取服务票据
$ kvno http/server.my.domain.com
http/server2025.domain.test@DOMAIN.TEST: kvno = 2
klist 命令也可用于验证票据是否已存储在 Kerberos 缓存中
$ klist
Ticket cache: KCM:1000
Default principal: username@MY.DOMAIN.COM
Valid starting     Expires            Service principal
29/08/24 13:54:51  29/08/24 23:54:51  krbtgt/MY.DOMAIN.COM@MY.DOMAIN.COM
        renew until 05/09/24 13:54:48
29/08/24 13:55:30  29/08/24 23:55:30  http/server.my.domain.com@MY.DOMAIN.COM
        renew until 05/09/24 13:55:30
在上面的示例中,我们的 TGT 存储在 krbtgt 服务主体下,我们的 http/server.my.domain.com 存储在其自己的服务主体下。
kdestroy 命令可用于删除票据缓存。
票据管理
为了使 Kerberos 身份验证能够与 Ansible 一起使用,必须存在用户的 Kerberos TGT,以便 Ansible 可以为目标服务器请求服务票据。某些连接插件(如 ssh)要求 TGT 已经存在并可供 Ansible 控制进程访问。其他连接插件(如 psrp 和 winrm)如果在清单中提供了用户的密码,则可以自动为用户获取 TGT。
要手动为用户检索 TGT,请使用用户的用户名和域运行 kinit 命令,如 验证 Kerberos 配置 中所示。当 Ansible 中的连接插件请求 Kerberos 身份验证时,此 TGT 将被自动使用。
如果您使用的是 psrp 或 winrm 连接插件,并且在清单中提供了用户的密码,则连接插件将自动为用户获取 TGT。这是通过使用用户的用户名和密码运行 kinit 命令来完成的。TGT 将存储在临时凭据缓存中,并将用于该任务。
委托
Kerberos 委托允许凭据跨多个跳转点传递。当您需要向服务器进行身份验证,然后让该服务器代表您向另一台服务器进行身份验证时,这非常有用。要启用委托,您必须
- 使用 - kinit获取票证时请求可转发 TGT
- 请求连接插件允许向服务器进行委托 
- AD 用户未标记为敏感用户,无法进行委托,并且不是 - Protected Users组的成员
- 根据 - krb5.conf配置,目标服务器可能需要通过其 AD 对象委托设置允许不受限制的委托
要请求可转发的 TGT,请向 kinit 命令添加 -f 标志,或在 krb5.conf 文件的 [libdefaults] 部分设置 forwardable = true 选项。如果您正在使用 psrp 或 winrm 连接插件从清单中的用户密码检索 TGT,如果连接插件配置为使用委托,它将自动请求可转发的 TGT。
要让连接插件委托凭据,它需要在清单中设置以下主机变量
# psrp
ansible_psrp_negotiate_delegate: true
# winrm
ansible_winrm_kerberos_delegation: true
# ssh
ansible_ssh_common_args: -o GSSAPIDelegateCredentials=yes
注意
也可以在 ~/.ssh/config 文件中设置 GSSAPIDelegateCredentials yes 以允许所有 SSH 连接的委托。
要验证用户是否允许委托其凭据,您可以在同一域中的 Windows 主机上运行以下 PowerShell 脚本
Function Test-IsDelegatable {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $UserName
    )
    $NOT_DELEGATED = 0x00100000
    $searcher = [ADSISearcher]"(&(objectClass=user)(objectCategory=person)(sAMAccountName=$UserName))"
    $res = $searcher.FindOne()
    if (-not $res) {
        Write-Error -Message "Failed to find user '$UserName'"
    }
    else {
        $uac = $res.Properties.useraccountcontrol[0]
        $memberOf = @($res.Properties.memberof)
        $isSensitive = [bool]($uac -band $NOT_DELEGATED)
        $isProtectedUser = [bool]($memberOf -like 'CN=Protected Users,*').Count
        -not ($isSensitive -or $isProtectedUser)
    }
}
Test-IsDelegatable -UserName username
较新版本的 MIT Kerberos 在 krb5.conf 文件的 [libdefaults] 部分添加了一个配置选项 enforce_ok_as_delegate。如果此选项设置为 true,则只有在目标服务器帐户允许不受限制的委托时,委托才有效。要检查或设置 Windows 计算机主机上的不受限制的委托,您可以使用以下 PowerShell 脚本
# Check if the server allows unconstrained delegation
(Get-ADComputer -Identity WINHOST -Properties TrustedForDelegation).TrustedForDelegation
# Enable unconstrained delegation
Set-ADComputer -Identity WINHOST -TrustedForDelegation $true
要验证委托是否有效,您可以在 Windows 节点上使用 klist.exe 命令验证票证是否已转发。输出应显示票证服务器是 krbtgt/MY.DOMAIN.COM @ MY.CDOMAIN.COM,并且票证标志包含 forwarded。
$ ansible WINHOST -m ansible.windows.win_command -a klist.exe
WINHOST | CHANGED | rc=0 >>
Current LogonId is 0:0x82b6977
Cached Tickets: (1)
#0>     Client: username @ MY.DOMAIN.COM
        Server: krbtgt/MY.DOMAIN.COM @ MY.DOMAIN.COM
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
        Ticket Flags 0x60a10000 -> forwardable forwarded renewable pre_authent name_canonicalize
        Start Time: 8/30/2024 14:15:18 (local)
        End Time:   8/31/2024 0:12:49 (local)
        Renew Time: 9/6/2024 14:12:49 (local)
        Session Key Type: AES-256-CTS-HMAC-SHA1-96
        Cache Flags: 0x1 -> PRIMARY
        Kdc Called:
如果出现任何错误,klist.exe 的输出将不包含 forwarded 标志,并且服务器将是目标服务器主体而不是 krbtgt。
$ ansible WINHOST -m ansible.windows.win_command -a klist.exe
WINHOST | CHANGED | rc=0 >>
Current LogonId is 0:0x82c312c
Cached Tickets: (1)
#0>     Client: username @ MY.DOMAIN.COM
        Server: http/winhost.my.domain.com @ MY.DOMAIN.COM
        KerbTicket Encryption Type: AES-256-CTS-HMAC-SHA1-96
        Ticket Flags 0x40a10000 -> forwardable renewable pre_authent name_canonicalize
        Start Time: 8/30/2024 14:16:24 (local)
        End Time:   8/31/2024 0:16:12 (local)
        Renew Time: 0
        Session Key Type: AES-256-CTS-HMAC-SHA1-96
        Cache Flags: 0x8 -> ASC
        Kdc Called:
Kerberos 故障排除
Kerberos 依赖于正确配置的环境才能工作。一些可能导致 Kerberos 身份验证失败的常见问题是
- 为 Windows 主机设置的主机名是别名或 IP 地址 
- Ansible 控制节点上的时间与 AD 域控制器不同步 
- krb5.conf文件中未正确设置 KDC 领域,或者无法通过 DNS 解析
如果使用 MIT Kerberos 实现,您可以设置环境变量 KRB5_TRACE=/dev/stdout 以获取有关 Kerberos 库正在执行的操作的更详细信息。这对于调试 Kerberos 库的问题(例如 KDC 查找行为、时间同步问题和服务器名称查找失败)非常有用。
