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 身份验证时才需要它们。

安装完成后,kinitklistkrb5-config 软件包将可用。您可以使用以下命令测试它们

$ krb5-config --version

Kerberos 5 release 1.21.3

psrpwinrm 连接插件需要额外的 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.commy.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 的票据:

如果密码正确,则命令将返回而没有任何输出。要验证是否已获得票据,可以使用 klist 命令:

> klist
Ticket cache: KCM:1000
Default principal: [email protected]

Valid starting     Expires            Service principal
29/08/24 13:54:51  29/08/24 23:54:51  krbtgt/[email protected]
        renew until 05/09/24 13:54:48

如果成功,这将验证 Kerberos 配置是否正确以及用户是否可以从 KDC 获取票据授予票据 (TGT)。如果 kinit 无法为请求的领域找到 KDC,请验证您的 Kerberos 配置,确保 DNS 可以使用 SRV 记录找到 KDC,或者 KDC 在 krb5.conf 中被手动映射。

在基于 MIT Kerberos 的系统上,可以使用 kvno 命令验证您是否能够为特定服务检索服务票据。例如,如果您使用基于 WinRM 的连接来使用 server.my.domain.com 进行身份验证,则可以使用以下命令验证您的 TGT 是否能够为目标服务器获取服务票据:

$ kvno http/server.my.domain.com
http/[email protected]: kvno = 2

klist 命令也可用于验证票据是否已存储在 Kerberos 缓存中:

$ klist
Ticket cache: KCM:1000
Default principal: [email protected]

Valid starting     Expires            Service principal
29/08/24 13:54:51  29/08/24 23:54:51  krbtgt/[email protected]
        renew until 05/09/24 13:54:48
29/08/24 13:55:30  29/08/24 23:55:30  http/[email protected]
        renew until 05/09/24 13:55:30

在上面的示例中,我们的 TGT 存储在 krbtgt 服务主体下,我们的 http/server.my.domain.com 存储在它自己的服务主体下。

kdestroy 命令可用于删除票据缓存。

票据管理

要使 Kerberos 身份验证与 Ansible 一起工作,必须存在用户的 Kerberos TGT,以便 Ansible 可以为目标服务器请求服务票据。某些连接插件(如 ssh)要求 TGT 已经存在并且 Ansible 控制进程可以访问它。其他连接插件(如 psrpwinrm)如果清单中提供了用户的密码,则可以自动为用户获取 TGT。

要手动为用户检索 TGT,请使用用户的用户名和域运行 kinit 命令,如 验证 Kerberos 配置 中所示。当 Ansible 中的连接插件请求 Kerberos 身份验证时,此 TGT 将自动使用。

如果您使用的是 psrpwinrm 连接插件,并且清单中提供了用户的密码,则连接插件将自动为用户获取 TGT。这是通过使用用户的用户名和密码运行 kinit 命令来完成的。TGT 将存储在临时凭据缓存中,并将用于该任务。

委托

Kerberos 委托允许凭据遍历多个跃点。当您需要向服务器进行身份验证,然后让该服务器代表您向另一台服务器进行身份验证时,这非常有用。要启用委托,您必须:

  • 使用 kinit 获取票据时请求可转发 TGT

  • 请求连接插件允许向服务器进行委托

  • AD 用户未标记为敏感用户,无法委托,并且不是 Protected Users 组的成员

  • 根据 krb5.conf 配置,目标服务器可能需要通过其 AD 对象委托设置允许不受约束的委托

要请求可转发的 TGT,请向 kinit 命令添加 -f 标志,或者在 krb5.conf 文件的 [libdefaults] 部分中设置 forwardable = true 选项。如果您使用 psrpwinrm 连接插件从清单中的用户密码检索 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 查找行为、时间同步问题和服务器名称查找失败)非常有用。