Windows 远程管理

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

什么是 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 证书服务文档.

注意

使用 PowerShell cmdlet New-SelfSignedCertificate 生成用于身份验证的证书仅在从 Windows 10 或 Windows Server 2012 R2 主机或更高版本生成时有效。仍然需要 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:

将证书导入证书存储区

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

以下示例显示了如何导入颁发证书

$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 使用的较旧的身份验证机制,它可以支持本地帐户和域帐户。NTLM 在 WinRM 服务中默认情况下处于启用状态,因此使用它之前无需进行任何设置。

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

Kerberos 在使用 NTLM 时具有几个优点

  • 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 域控制器和辅助 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 2.3 及更高版本默认情况下会在为主机指定 ansible_useransible_password 时自动管理 Kerberos 票据。在此过程中,将在每个主机的临时凭据缓存中创建新票据。这将在每个任务执行之前完成,以最大限度地减少票据过期的可能性。临时凭据缓存将在每个任务完成后删除,不会干扰默认凭据缓存。

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

自动票据管理需要控制主机系统路径上的标准 kinit 二进制文件。要指定其他位置或二进制文件名称,请将 ansible_winrm_kinit_cmd 主机变量设置为 MIT krbv5 kinit 兼容二进制文件的完全限定路径。

手动 Kerberos 票据管理

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

注意

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

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

klist

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

kdestroy

Kerberos 故障排除

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

  • 为 Windows 主机设置的主机名是 FQDN,而不是 IP 地址。* 如果您使用 IP 地址连接,您将收到错误消息 Kerberos 数据库中找不到服务器。* 要确定您是使用 IP 地址还是 FQDN 连接,请使用 -vvv 标记运行您的剧本(或调用 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

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

Enable-WSManCredSSP -Role Server -Force

安装 CredSSP 库

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

pip install pywinrm[credssp]

CredSSP 和 TLS 1.2

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

可以使用两种方法将较旧的主机与 CredSSP 配合使用

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

  • 在清单中设置 ansible_winrm_credssp_disable_tlsv1_2=True 以通过 TLS 1.0 运行。当连接到 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 具有 UPN 值(如 [email protected]),则身份验证选项将自动尝试使用 Kerberos,除非 ansible_winrm_transport 已设置为除 kerberos 之外的其他内容。

以下自定义清单变量也支持用于 WinRM 连接的额外配置

  • ansible_port:WinRM 运行的端口,HTTPS 为 5986(默认值),而 HTTP 为 5985

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

  • 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_schemehttp 并且 ansible_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 地址定义主机时,只需添加 IPv6 地址,就像添加 IPv4 地址或主机名一样

[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 信任证书颁发机构 (CA)(如 AD CS),可以将 CA 的发行者证书导出为 PEM 编码的证书。然后,可以将此证书复制到 Ansible 控制节点本地,并用作证书验证的来源,也称为 CA 链。

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

注意

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

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

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

WinRM 限制

由于 WinRM 协议的设计,在使用 WinRM 时存在一些限制,这可能会在为 Ansible 创建剧本时造成问题。这些限制包括

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

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

  • 一些程序无法通过 WinRM 安装,因为没有凭据委派或因为它们访问了 WinRM 上禁止的 Windows API,例如 WUA。

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

  • 无法运行与 DPAPI 交互的进程,而 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 剧本

剧本简介

Ansible 提示和技巧

剧本技巧

Windows 模块列表

Windows 特定的模块列表,全部在 PowerShell 中实现

沟通

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