Windows 远程管理

与默认使用 SSH 的 Linux/Unix 主机不同,Windows 主机配置了 WinRM。本主题介绍如何配置和使用 Ansible 的 WinRM。

什么是 WinRM?

WinRM 是 Windows 使用的管理协议,用于与另一台服务器进行远程通信。它是一种基于 SOAP 的协议,通过 HTTP/HTTPS 进行通信,并包含在所有最新的 Windows 操作系统中。自 Windows Server 2012 起,WinRM 默认启用,但在大多数情况下,需要额外的配置才能将 WinRM 与 Ansible 一起使用。

Ansible 使用 pywinrm 包通过 WinRM 与 Windows 服务器通信。它不是 Ansible 包默认安装的。

如果您选择了 pipx 安装说明,则可以通过运行以下命令安装它

pipx inject ansible pywinrm  # if you installed ansible with pipx
pipx inject ansible-core pywinrm  # if you installed ansible-core with pipx

或者,如果您选择了 pip 安装说明

pip install "pywinrm>=0.3.0"

注意

在具有多个 Python 版本的发行版上,请使用 pip2 或 pip2.x,其中 x 与 Ansible 运行的 Python 次要版本匹配。

警告

在最新版本中,在 MacOS 上使用 Ansible 中的 winrmpsrp 连接插件通常会失败。这是一个已知问题,发生在 Python 堆栈深处,Ansible 无法更改。目前唯一的解决方法是设置环境变量 no_proxy=* 并避免使用 Kerberos 身份验证。

WinRM 身份验证选项

连接到 Windows 主机时,使用帐户进行身份验证时可以使用几种不同的选项。身份验证类型可以在清单主机或组中使用 ansible_winrm_transport 变量进行设置。

以下矩阵是对选项的高级概述

选项

本地帐户

Active Directory 帐户

凭据委托

HTTP 加密

基本

证书

Kerberos

NTLM

CredSSP

基本

基本身份验证是最简单的身份验证选项之一,但也是最不安全的。这是因为用户名和密码只是 base64 编码的,如果未使用安全通道(例如,HTTPS),则任何人都可以对其进行解码。基本身份验证只能用于本地帐户(而不是域帐户)。

以下示例显示了为基本身份验证配置的主机变量

ansible_user: LocalUsername
ansible_password: Password
ansible_connection: winrm
ansible_winrm_transport: basic

默认情况下,Windows 主机上未启用基本身份验证,但可以通过在 PowerShell 中运行以下命令来启用

Set-Item -Path WSMan:\localhost\Service\Auth\Basic -Value $true

证书

证书身份验证使用证书作为密钥,类似于 SSH 密钥对,但文件格式和密钥生成过程不同。

以下示例显示了为证书身份验证配置的主机变量

ansible_connection: winrm
ansible_winrm_cert_pem: /path/to/certificate/public/key.pem
ansible_winrm_cert_key_pem: /path/to/certificate/private/key.pem
ansible_winrm_transport: certificate

默认情况下,Windows 主机上未启用证书身份验证,但可以通过在 PowerShell 中运行以下命令来启用

Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true

注意

加密的私钥不能使用,因为 Ansible 用于 WinRM 的 urllib3 库不支持此功能。

注意

对于启用具有 TLS 1.3 连接的证书身份验证,需要 Python 3.8+、3.7.1 或 3.6.7 以及 Python 包 urllib3==2.0.7 或更高版本。

.._winrm_certificate_generate

生成证书

必须先生成证书,然后才能将其映射到本地用户。可以使用以下方法之一完成此操作

  • OpenSSL

  • PowerShell,使用 New-SelfSignedCertificate cmdlet

  • Active Directory 证书服务

Active Directory 证书服务不在本文档的范围内,但在域环境中运行时,它可能是最佳选择。有关更多信息,请参见 Active Directory 证书服务文档

注意

仅当从 Windows 10 或 Windows Server 2012 R2 或更高版本的主机生成时,使用 PowerShell cmdlet New-SelfSignedCertificate 生成用于身份验证的证书才有效。仍然需要 OpenSSL 才能从 PFX 证书中提取私钥到 PEM 文件供 Ansible 使用。

要使用 OpenSSL 生成证书

# Set the name of the local user that will have the key mapped to
USERNAME="username"

cat > openssl.conf << EOL
distinguished_name = req_distinguished_name
[req_distinguished_name]
[v3_req_client]
extendedKeyUsage = clientAuth
subjectAltName = otherName:1.3.6.1.4.1.311.20.2.3;UTF8:$USERNAME@localhost
EOL

export OPENSSL_CONF=openssl.conf
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -out cert.pem -outform PEM -keyout cert_key.pem -subj "/CN=$USERNAME" -extensions v3_req_client
rm openssl.conf

要使用 New-SelfSignedCertificate 生成证书

# Set the name of the local user that will have the key mapped
$username = "username"
$output_path = "C:\temp"

# Instead of generating a file, the cert will be added to the personal
# LocalComputer folder in the certificate store
$cert = New-SelfSignedCertificate -Type Custom `
    -Subject "CN=$username" `
    -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2","2.5.29.17={text}upn=$username@localhost") `
    -KeyUsage DigitalSignature,KeyEncipherment `
    -KeyAlgorithm RSA `
    -KeyLength 2048

# Export the public key
$pem_output = @()
$pem_output += "-----BEGIN CERTIFICATE-----"
$pem_output += [System.Convert]::ToBase64String($cert.RawData) -replace ".{64}", "$&`n"
$pem_output += "-----END CERTIFICATE-----"
[System.IO.File]::WriteAllLines("$output_path\cert.pem", $pem_output)

# Export the private key in a PFX file
[System.IO.File]::WriteAllBytes("$output_path\cert.pfx", $cert.Export("Pfx"))

注意

要将 PFX 文件转换为 pywinrm 可以使用的私钥,请使用 OpenSSL 运行以下命令 openssl pkcs12 -in cert.pfx -nocerts -nodes -out cert_key.pem -passin pass: -passout pass:

将证书导入到证书存储

生成证书后,需要将颁发证书导入 LocalMachine 存储的 Trusted Root Certificate Authorities 中,并且客户端证书公钥必须存在于 LocalMachine 存储的 Trusted People 文件夹中。在此示例中,颁发证书和公钥相同。

以下示例说明如何导入颁发证书

$cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 "cert.pem"

$store_name = [System.Security.Cryptography.X509Certificates.StoreName]::Root
$store_location = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
$store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $store_name, $store_location
$store.Open("MaxAllowed")
$store.Add($cert)
$store.Close()

注意

如果使用 ADCS 生成证书,则颁发证书将已导入,可以跳过此步骤。

导入客户端证书公钥的代码为

$cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 "cert.pem"

$store_name = [System.Security.Cryptography.X509Certificates.StoreName]::TrustedPeople
$store_location = [System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
$store = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Store -ArgumentList $store_name, $store_location
$store.Open("MaxAllowed")
$store.Add($cert)
$store.Close()

将证书映射到帐户

导入证书后,将其映射到本地用户帐户

$username = "username"
$password = ConvertTo-SecureString -String "password" -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password

# This is the issuer thumbprint which in the case of a self generated cert
# is the public key thumbprint, additional logic may be required for other
# scenarios
$thumbprint = (Get-ChildItem -Path cert:\LocalMachine\root | Where-Object { $_.Subject -eq "CN=$username" }).Thumbprint

New-Item -Path WSMan:\localhost\ClientCertificate `
    -Subject "$username@localhost" `
    -URI * `
    -Issuer $thumbprint `
    -Credential $credential `
    -Force

完成后,应将主机变量 ansible_winrm_cert_pem 设置为公钥的路径,并将 ansible_winrm_cert_key_pem 变量设置为私钥的路径。

NTLM

NTLM 是 Microsoft 使用的较旧的身份验证机制,可以同时支持本地帐户和域帐户。默认情况下,WinRM 服务上启用 NTLM,因此在使用它之前无需进行任何设置。

NTLM 是最容易使用的身份验证协议,并且比 Basic 身份验证更安全。如果在域环境中运行,应使用 Kerberos 而不是 NTLM。

与使用 NTLM 相比,Kerberos 具有以下几个优势

  • NTLM 是一种较旧的协议,不支持较新的加密协议。

  • NTLM 的身份验证速度较慢,因为它在身份验证阶段需要更多往返主机的行程。

  • 与 Kerberos 不同,NTLM 不允许凭据委托。

此示例显示了配置为使用 NTLM 身份验证的主机变量

ansible_user: LocalUsername
ansible_password: Password
ansible_connection: winrm
ansible_winrm_transport: ntlm

Kerberos

在域环境中运行时,建议使用 Kerberos 作为身份验证选项。Kerberos 支持诸如凭据委派和通过 HTTP 进行消息加密等功能,并且是通过 WinRM 可用的更安全的选项之一。

Kerberos 需要在 Ansible 主机上进行一些额外的设置工作才能正常使用。

以下示例显示了为 Kerberos 身份验证配置的主机变量

ansible_user: [email protected]
ansible_password: Password
ansible_connection: winrm
ansible_port: 5985
ansible_winrm_transport: kerberos

从 Ansible 2.3 版本开始,Kerberos 票证将基于 ansible_useransible_password 创建。如果在较旧版本的 Ansible 上运行或当 ansible_winrm_kinit_modemanual 时,必须已获得 Kerberos 票证。请参阅下文了解更多详细信息。

可以设置一些额外的主机变量

ansible_winrm_kinit_mode: managed/manual (manual means Ansible will not obtain a ticket)
ansible_winrm_kinit_cmd: the kinit binary to use to obtain a Kerberos ticket (default to kinit)
ansible_winrm_service: overrides the SPN prefix that is used, the default is ``HTTP`` and should rarely ever need changing
ansible_winrm_kerberos_delegation: allows the credentials to traverse multiple hops
ansible_winrm_kerberos_hostname_override: the hostname to be used for the Kerberos exchange

安装 Kerberos 库

在使用 Kerberos 之前,必须安装一些系统依赖项。下面的脚本列出了基于发行版的依赖项

# Through Yum (RHEL/Centos/Fedora for the older version)
yum -y install gcc python-devel krb5-devel krb5-libs krb5-workstation

# Through DNF (RHEL/Centos/Fedora for the newer version)
dnf -y install gcc python3-devel krb5-devel krb5-libs krb5-workstation

# Through Apt (Ubuntu older than 20.04 LTS (focal))
sudo apt-get install python-dev libkrb5-dev krb5-user

# Through Apt (Ubuntu newer than 20.04 LTS)
sudo apt-get install python3-dev libkrb5-dev krb5-user

# Through Portage (Gentoo)
emerge -av app-crypt/mit-krb5
emerge -av dev-python/setuptools

# Through Pkg (FreeBSD)
sudo pkg install security/krb5

# Through OpenCSW (Solaris)
pkgadd -d http://get.opencsw.org/now
/opt/csw/bin/pkgutil -U
/opt/csw/bin/pkgutil -y -i libkrb5_3

# Through Pacman (Arch Linux)
pacman -S krb5

安装依赖项后,可以安装 python-kerberos 包装器。

如果您选择了 pipx 安装说明,则可以通过运行以下命令安装它

pipx inject ansible pywinrm[kerberos]  # if you installed ansible with pipx
pipx inject ansible-core pywinrm[kerberos]  # if you installed ansible-core with pipx

或者,如果您选择了 pip 安装说明

pip install pywinrm[kerberos]

注意

虽然 Ansible 通过 pywinrm 支持 Kerberos 身份验证已有一段时间,但可选功能或更安全的选项可能仅在较新版本的 pywinrm 和/或 pykerberos 库中可用。建议您将每个版本升级到最新版本,以解决任何警告或错误。这可以通过诸如 pip 之类的工具或诸如 dnfyumapt 之类的系统包管理器来完成,但可用包的名称和版本可能因工具而异。

配置主机 Kerberos

安装依赖项后,需要配置 Kerberos 以使其可以与域通信。此配置通过 /etc/krb5.conf 文件完成,该文件与上面脚本中的包一起安装。

要配置 Kerberos,请在以以下内容开头的节中

[realms]

添加完整的域名以及主域和辅助 Active Directory 域控制器的完全限定域名。它应该看起来像这样

[realms]
    MY.DOMAIN.COM = {
        kdc = domain-controller1.my.domain.com
        kdc = domain-controller2.my.domain.com
    }

在以以下内容开头的节中

[domain_realm]

为 Ansible 需要访问的每个域添加如下行

[domain_realm]
    .my.domain.com = MY.DOMAIN.COM

您可以在此文件中配置其他设置,例如默认域。有关更多详细信息,请参阅 krb5.conf

自动 Kerberos 票证管理

当为主机指定 ansible_useransible_password 时,Ansible 2.3 及更高版本默认为自动管理 Kerberos 票证。在此过程中,将为每个主机在临时凭据缓存中创建一个新票证。这是在每次任务执行之前完成的,以最大程度地减少票证过期的可能性。临时凭据缓存将在每个任务完成后删除,并且不会干扰默认凭据缓存。

要禁用自动票证管理,请通过清单设置 ansible_winrm_kinit_mode=manual

自动票证管理需要在控制主机系统路径上使用标准的 kinit 二进制文件。要指定不同的位置或二进制名称,请将 ansible_winrm_kinit_cmd hostvar 设置为与 MIT krbv5 kinit 兼容的二进制文件的完全限定路径。

手动 Kerberos 票证管理

要手动管理 Kerberos 票证,请使用 kinit 二进制文件。要获取新票证,请使用以下命令

注意

域必须与配置的 Kerberos 领域完全匹配,并且必须为大写。

要查看已获取的票证(如果有),请使用以下命令

klist

要销毁已获取的所有票证,请使用以下命令

kdestroy

Kerberos 故障排除

Kerberos 依赖于正确配置的环境才能工作。要解决 Kerberos 问题,请确保

  • 为 Windows 主机设置的主机名是 FQDN,而不是 IP 地址。* 如果您使用 IP 地址连接,将收到错误消息 在 Kerberos 数据库中找不到服务器。* 要确定您是否使用 IP 地址或 FQDN 连接,请使用 -vvv 标志运行您的 playbook(或调用 win_ping 模块)。

  • 正向和反向 DNS 查找在域中正常工作。要测试这一点,请通过名称 ping Windows 主机,然后使用 nslookup 返回的 IP 地址。在 IP 地址上使用 nslookup 时,应返回相同的名称。

  • Ansible 主机的时钟与 AD 域控制器同步。Kerberos 对时间敏感,少量的时钟漂移可能会导致票证生成过程失败。

  • 确保在 krb5.conf 文件中配置了域的完全限定域名。要检查这一点,请运行

    kinit -C [email protected]
    klist
    

    如果 klist 返回的域名与请求的域名不同,则正在使用别名。krb5.conf 文件需要更新,以便使用完全限定域名而不是别名。

  • 如果默认的 Kerberos 工具已被替换或修改(某些 IdM 解决方案可能会这样做),则在安装或升级 Python Kerberos 库时可能会导致问题。在撰写本文时,此库名为 pykerberos,并且已知可与 MIT 和 Heimdal Kerberos 库一起使用。要解决 pykerberos 安装问题,请确保满足 Kerberos 的系统依赖项(请参阅:安装 Kerberos 库),从 PATH 环境变量中删除任何自定义 Kerberos 工具路径,然后重试安装 Python Kerberos 库包。

CredSSP

CredSSP 身份验证是一种较新的身份验证协议,允许凭据委派。这是通过在身份验证成功后加密用户名和密码,然后使用 CredSSP 协议将其发送到服务器来实现的。

由于用户名和密码被发送到服务器以用于双跳身份验证,因此请确保 Windows 主机与之通信的主机未被入侵并且是受信任的。

CredSSP 可以用于本地帐户和域帐户,并且还支持通过 HTTP 进行消息加密。

要使用 CredSSP 身份验证,主机变量的配置如下

ansible_user: Username
ansible_password: Password
ansible_connection: winrm
ansible_winrm_transport: credssp

一些额外的可以在下面设置的主机变量

ansible_winrm_credssp_disable_tlsv1_2: when true, will not use TLS 1.2 in the CredSSP auth process

默认情况下,Windows 主机上未启用 CredSSP 身份验证,但可以通过在 PowerShell 中运行以下命令来启用

Enable-WSManCredSSP -Role Server -Force

安装 CredSSP 库

可以使用 pip 安装 requests-credssp 包装器

pip install pywinrm[credssp]

CredSSP 和 TLS 1.2

默认情况下,requests-credssp 库配置为通过 TLS 1.2 协议进行身份验证。对于 Windows Server 2012 和 Windows 8 及更高版本,默认情况下已安装并启用了 TLS 1.2。

有两种方法可以将较旧的主机与 CredSSP 一起使用

  • 安装并启用修补程序以启用 TLS 1.2 支持(建议用于 Server 2008 R2 和 Windows 7)。

  • 在清单中设置 ansible_winrm_credssp_disable_tlsv1_2=True 以通过 TLS 1.0 运行。这是连接到 Windows Server 2008 的唯一选项,Windows Server 2008 无法支持 TLS 1.2

有关如何在 Windows 主机上启用 TLS 1.2 的更多信息,请参阅 TLS 1.2 支持

设置 CredSSP 证书

CredSSP 通过 TLS 协议加密凭据,并且默认使用自签名证书。WinRM 服务配置下的 CertificateThumbprint 选项可用于指定另一个证书的指纹。

注意

此证书配置独立于 WinRM 侦听器证书。使用 CredSSP 时,消息传输仍然通过 WinRM 侦听器进行,但通道内部的 TLS 加密消息使用服务级别证书。

要显式设置用于 CredSSP 的证书

# Note the value $certificate_thumbprint will be different in each
# situation, this needs to be set based on the cert that is used.
$certificate_thumbprint = "7C8DCBD5427AFEE6560F4AF524E325915F51172C"

# Set the thumbprint value
Set-Item -Path WSMan:\localhost\Service\CertificateThumbprint -Value $certificate_thumbprint

非管理员帐户

默认情况下,WinRM 配置为仅允许来自本地 Administrators 组中帐户的连接。可以通过运行以下命令进行更改

winrm configSDDL default

这将显示一个 ACL 编辑器,您可以在其中添加新的用户或组。要通过 WinRM 运行命令,用户和组必须至少启用 ReadExecute 权限。

虽然非管理帐户可以与 WinRM 一起使用,但大多数典型的服务器管理任务都需要一定级别的管理访问权限,因此该实用程序的用途通常有限。

WinRM 加密

默认情况下,当通过未加密通道运行时,WinRM 将无法工作。如果使用基于 HTTP 的 TLS (HTTPS) 或使用消息级加密,则 WinRM 协议认为该通道已加密。建议使用带有 TLS 的 WinRM,因为它适用于所有身份验证选项,但需要在 WinRM 侦听器上创建和使用证书。

如果在域环境中,ADCS 可以为由域本身颁发的主机创建证书。

如果无法使用 HTTPS,则当身份验证选项为 NTLMKerberosCredSSP 时,可以使用 HTTP。这些协议将在 WinRM 有效负载发送到服务器之前使用它们自己的加密方法对其进行加密。当通过 HTTPS 运行而不是使用更安全的 TLS 协议时,不使用消息级加密。如果需要传输和消息加密,请在主机变量中设置 ansible_winrm_message_encryption=always

注意

通过 HTTP 进行消息加密需要 pywinrm>=0.3.0。

最后的办法是禁用 Windows 主机上的加密要求。这应该仅用于开发和调试目的,因为从 Ansible 发送的任何内容都可以被查看或操纵,并且同一网络上的任何人都可以完全接管远程会话。要禁用加密要求

Set-Item -Path WSMan:\localhost\Service\AllowUnencrypted -Value $true

注意

除非绝对必要,否则不要禁用加密检查。这样做可能会允许网络上的其他人拦截诸如凭据和文件之类的敏感信息。

清单选项

Ansible 的 Windows 支持依赖于一些标准变量来指示远程主机的主机名、密码和连接类型。这些变量最容易在清单中设置,但可以在 host_vars/ group_vars 级别设置。

设置清单时,需要以下变量

# It is suggested that these be encrypted with ansible-vault:
# ansible-vault edit group_vars/windows.yml
ansible_connection: winrm

# May also be passed on the command-line through --user
ansible_user: Administrator

# May also be supplied at runtime with --ask-pass
ansible_password: SecretPasswordGoesHere

使用上述变量,Ansible 将通过 HTTPS 使用基本身份验证连接到 Windows 主机。如果 ansible_user 具有像 [email protected] 这样的 UPN 值,则除非 ansible_winrm_transport 已设置为除 kerberos 之外的其他值,否则身份验证选项将自动尝试使用 Kerberos。

还支持以下自定义清单变量,用于 WinRM 连接的附加配置

  • ansible_port:WinRM 将在其上运行的端口,HTTPS 为 5986,这是默认值,而 HTTP 为 5985

  • ansible_winrm_scheme:指定用于 WinRM 连接的连接方案(httphttps)。除非 ansible_port5985,否则 Ansible 默认使用 https

  • ansible_winrm_path:指定 WinRM 端点的备用路径,Ansible 默认使用 /wsman

  • ansible_winrm_realm:指定用于 Kerberos 身份验证的领域。如果 ansible_user 包含 @,Ansible 将默认使用 @ 后的用户名部分

  • ansible_winrm_transport:以逗号分隔的列表形式指定一个或多个身份验证传输选项。默认情况下,如果安装了 kerberos 模块并且定义了领域,Ansible 将使用 kerberos, basic,否则它将是 plaintext

  • ansible_winrm_server_cert_validation:指定服务器证书验证模式(ignorevalidate)。Ansible 默认为 Python 2.7.9 及更高版本上的 validate,这将导致针对 Windows 自签名证书的证书验证错误。除非已在 WinRM 侦听器上配置了可验证的证书,否则应将其设置为 ignore

  • ansible_winrm_operation_timeout_sec:增加 WinRM 操作的默认超时,Ansible 默认使用 20

  • ansible_winrm_read_timeout_sec:增加 WinRM 读取超时,Ansible 默认使用 30。如果存在间歇性网络问题且不断发生读取超时错误,则此设置非常有用

  • ansible_winrm_message_encryption:指定要使用的消息加密操作(autoalwaysnever),Ansible 默认使用 autoauto 表示仅当 ansible_winrm_schemehttpansible_winrm_transport 支持消息加密时才使用消息加密。always 表示将始终使用消息加密,而 never 表示将永远不会使用消息加密

  • ansible_winrm_ca_trust_path:用于指定与 certifi 模块中使用的不同的 cacert 容器。有关更多详细信息,请参阅 HTTPS 证书验证部分。

  • ansible_winrm_send_cbt:当通过 HTTPS 使用 ntlmkerberos 时,身份验证库将尝试发送通道绑定令牌以缓解中间人攻击。此标志控制是否发送这些绑定(默认值:true)。

  • ansible_winrm_*:可以提供 winrm.Protocol 支持的任何其他关键字参数来代替 *

此外,对于每个身份验证选项,还需要设置特定的变量。有关更多信息,请参阅上面的身份验证部分。

注意

Ansible 2.0 已将 ansible_ssh_useransible_ssh_passansible_ssh_hostansible_ssh_port 中的“ssh”弃用为 ansible_useransible_passwordansible_hostansible_port。如果使用 Ansible 2.0 之前的版本,则应改用较旧的样式(ansible_ssh_*)。较短的变量在旧版本的 Ansible 中会被忽略,而不会发出警告。

注意

ansible_winrm_message_encryption 与通过 TLS 完成的传输加密不同。即使 ansible_winrm_message_encryption=never,WinRM 有效负载在通过 HTTPS 运行时仍然使用 TLS 加密。

IPv6 地址

可以使用 IPv6 地址代替 IPv4 地址或主机名。此选项通常在清单中设置。Ansible 将尝试使用 ipaddress 包解析地址,并正确传递给 pywinrm

使用 IPv6 地址定义主机时,只需像添加 IPv4 地址或主机名一样添加 IPv6 地址即可

[windows-server]
2001:db8::1

[windows-server:vars]
ansible_user=username
ansible_password=password
ansible_connection=winrm

注意

ipaddress 库仅在 Python 3.x 中默认包含。要在 Python 2.7 中使用 IPv6 地址,请确保运行 pip install ipaddress,它会安装一个反向移植的包。

HTTPS 证书验证

作为 TLS 协议的一部分,将验证证书以确保主机与主题匹配,并且客户端信任服务器证书的颁发者。当使用自签名证书或设置 ansible_winrm_server_cert_validation: ignore 时,这些安全机制将被绕过。虽然自签名证书总是需要 ignore 标志,但是从证书颁发机构颁发的证书仍然可以验证。

在域环境中设置 HTTPS 侦听器的一种更常见的方法是使用 Active Directory 证书服务 (AD CS)。AD CS 用于从证书签名请求 (CSR) 生成签名证书。如果 WinRM HTTPS 侦听器正在使用由其他机构(如 AD CS)签名的证书,则可以将 Ansible 设置为信任该颁发者作为 TLS 握手的一部分。

为了让 Ansible 信任像 AD CS 这样的证书颁发机构 (CA),可以将 CA 的颁发者证书导出为 PEM 编码的证书。然后,可以将此证书复制到 Ansible 控制节点本地,并用作证书验证的来源,也称为 CA 链。

CA 链可以包含一个或多个颁发者证书,每个条目都位于新的一行中。要将自定义 CA 链用作验证过程的一部分,请将 ansible_winrm_ca_trust_path 设置为该文件的路径。如果未设置此变量,则将使用默认 CA 链,该链位于 Python 包 certifi 的安装路径中。

注意

每个 HTTP 调用都由 Python requests 库完成,该库不使用系统内置的证书存储作为信任机构。如果服务器的证书颁发者仅添加到系统的信任存储中,则证书验证将失败。

TLS 1.2 支持

由于 WinRM 通过 HTTP 协议运行,使用 HTTPS 意味着使用 TLS 协议来加密 WinRM 消息。TLS 将自动尝试协商客户端和服务器都可用的最佳协议和密码套件。如果找不到匹配项,则 Ansible 将报错,并显示类似于以下内容的消息:

HTTPSConnectionPool(host='server', port=5986): Max retries exceeded with url: /wsman (Caused by SSLError(SSLError(1, '[SSL: UNSUPPORTED_PROTOCOL] unsupported protocol (_ssl.c:1056)')))

通常,这是因为 Windows 主机尚未配置为支持 TLS v1.2,但也可能意味着 Ansible 控制节点安装了较旧的 OpenSSL 版本。

Windows 8 和 Windows Server 2012 默认安装并启用了 TLS v1.2,但较旧的主机(如 Server 2008 R2 和 Windows 7)必须手动启用。

注意

Server 2008 的 TLS 1.2 补丁存在一个错误,这将阻止 Ansible 连接到 Windows 主机。这意味着 Server 2008 不能配置为使用 TLS 1.2。Server 2008 R2 和 Windows 7 不受此问题的影响,可以使用 TLS 1.2。

要验证 Windows 主机支持的协议,可以在 Ansible 控制节点上运行以下命令:

openssl s_client -connect <hostname>:5986

输出将包含有关 TLS 会话的信息,并且 Protocol 行将显示协商的版本。

New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1
    Cipher    : ECDHE-RSA-AES256-SHA
    Session-ID: 962A00001C95D2A601BE1CCFA7831B85A7EEE897AECDBF3D9ECD4A3BE4F6AC9B
    Session-ID-ctx:
    Master-Key: ....
    Start Time: 1552976474
    Timeout   : 7200 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---

New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: AE16000050DA9FD44D03BB8839B64449805D9E43DBD670346D3D9E05D1AEEA84
    Session-ID-ctx:
    Master-Key: ....
    Start Time: 1552976538
    Timeout   : 7200 (sec)
    Verify return code: 21 (unable to verify the first certificate)

如果主机返回 TLSv1,则应将其配置为启用 TLS v1.2。您可以通过运行以下 PowerShell 脚本来实现此目的:

Function Enable-TLS12 {
    param(
        [ValidateSet("Server", "Client")]
        [String]$Component = "Server"
    )

    $protocols_path = 'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols'
    New-Item -Path "$protocols_path\TLS 1.2\$Component" -Force
    New-ItemProperty -Path "$protocols_path\TLS 1.2\$Component" -Name Enabled -Value 1 -Type DWORD -Force
    New-ItemProperty -Path "$protocols_path\TLS 1.2\$Component" -Name DisabledByDefault -Value 0 -Type DWORD -Force
}

Enable-TLS12 -Component Server

# Not required but highly recommended to enable the Client side TLS 1.2 components
Enable-TLS12 -Component Client

Restart-Computer

以下 Ansible 任务也可用于启用 TLS v1.2:

- name: enable TLSv1.2 support
  win_regedit:
    path: HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\{{ item.type }}
    name: '{{ item.property }}'
    data: '{{ item.value }}'
    type: dword
    state: present
  register: enable_tls12
  loop:
  - type: Server
    property: Enabled
    value: 1
  - type: Server
    property: DisabledByDefault
    value: 0
  - type: Client
    property: Enabled
    value: 1
  - type: Client
    property: DisabledByDefault
    value: 0

- name: reboot if TLS config was applied
  win_reboot:
  when: enable_tls12 is changed

还有其他方法可以配置 TLS 协议以及 Windows 主机提供的密码套件。Nartac Software 提供的 IIS Crypto 工具可以为您提供一个 GUI 来管理这些设置。

WinRM 限制

由于 WinRM 协议的设计,在使用 WinRM 时存在一些限制,这可能会在为 Ansible 创建 playbook 时导致问题。这些限制包括:

  • 大多数身份验证类型的凭据都不会被委派,这会导致访问网络资源或安装某些程序时出现身份验证错误。

  • 通过 WinRM 运行时,对 Windows Update API 的许多调用会被阻止。

  • 由于没有凭据委派,或者因为它们通过 WinRM 访问禁止的 Windows API(如 WUA),某些程序无法使用 WinRM 安装。

  • WinRM 下的命令是在非交互式会话下完成的,这可能会阻止某些命令或可执行文件运行。

  • 您不能运行与 DPAPI 交互的进程,该进程被某些安装程序(如 Microsoft SQL Server)使用。

可以通过执行以下操作之一来缓解其中一些限制:

  • ansible_winrm_transport 设置为 credsspkerberos(使用 ansible_winrm_kerberos_delegation=true)以绕过双跳问题并访问网络资源

  • 使用 become 绕过所有 WinRM 限制,并像在本地一样运行命令。与使用 credssp 等身份验证传输不同,这还将消除非交互式限制和 API 限制(如 WUA 和 DPAPI)

  • 使用计划任务运行可以使用 win_scheduled_task 模块创建的命令。与 become 类似,这会绕过所有 WinRM 限制,但只能运行命令而不能运行模块。

另请参阅:

Ansible playbook

playbook 简介

Ansible 技巧和窍门

playbook 的提示和技巧

Windows 模块列表

特定于 Windows 的模块列表,全部用 PowerShell 实现

沟通

有问题?需要帮助?想分享您的想法?请访问 Ansible 通信指南