Windows 远程管理
与默认使用 SSH 的 Linux/Unix 主机不同,Windows 主机配置了 WinRM。本主题介绍如何配置和使用 WinRM 与 Ansible。
什么是 WinRM?
WinRM 是 Windows 用于远程与另一台服务器通信的管理协议。它是一种基于 SOAP 的协议,通过 HTTP/HTTPS 通信,包含在所有最新的 Windows 操作系统中。从 Windows Server 2012 开始,WinRM 默认启用,但在某些情况下,需要额外的配置才能将 WinRM 与 Ansible 一起使用。
Ansible 可以通过 psrp 或 winrm 连接插件使用 WinRM。这些插件有自己的 Python 要求,这些要求未包含在 Ansible 软件包中,必须单独安装。
如果你选择了 pipx
安装说明,你可以通过运行以下命令来安装这些要求
pipx inject "pypsrp<=1.0.0" # for psrp
pipx inject "pywinrm>=0.4.0" # for winrm
或者,如果你选择了 pip
安装说明
pip3 install "pypsrp<=1.0.0" # for psrp
pip3 install "pywinrm>=0.4.0" # for winrm
警告
在最新版本中,在 MacOS 上的 Ansible 中使用 winrm
或 psrp
连接插件通常会失败。这是一个已知问题,发生在 Python 堆栈的深处,Ansible 无法更改。如今,唯一的解决方法是设置环境变量 OBJC_DISABLE_INITIALIZE_FORK_SAFETY=yes
、no_proxy=*
并且避免使用 Kerberos 身份验证。
WinRM 设置
在 Ansible 可以使用 WinRM 连接之前,Windows 主机必须配置 WinRM 监听器。此监听器将在配置的端口上监听并接受传入的 WinRM 请求。
虽然本指南详细介绍了如何枚举、添加和删除监听器,但你可以运行以下 PowerShell 代码片段来设置具有默认值的 HTTP 监听器
# Enables the WinRM service and sets up the HTTP listener
Enable-PSRemoting -Force
# Opens port 5985 for all profiles
$firewallParams = @{
Action = 'Allow'
Description = 'Inbound rule for Windows Remote Management via WS-Management. [TCP 5985]'
Direction = 'Inbound'
DisplayName = 'Windows Remote Management (HTTP-In)'
LocalPort = 5985
Profile = 'Any'
Protocol = 'TCP'
}
New-NetFirewallRule @firewallParams
# Allows local user accounts to be used with WinRM
# This can be ignored if using domain accounts
$tokenFilterParams = @{
Path = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
Name = 'LocalAccountTokenFilterPolicy'
Value = 1
PropertyType = 'DWORD'
Force = $true
}
New-ItemProperty @tokenFilterParams
要添加具有自签名证书的 HTTPS 监听器,我们可以运行以下命令
# Create self signed certificate
$certParams = @{
CertStoreLocation = 'Cert:\LocalMachine\My'
DnsName = $env:COMPUTERNAME
NotAfter = (Get-Date).AddYears(1)
Provider = 'Microsoft Software Key Storage Provider'
Subject = "CN=$env:COMPUTERNAME"
}
$cert = New-SelfSignedCertificate @certParams
# Create HTTPS listener
$httpsParams = @{
ResourceURI = 'winrm/config/listener'
SelectorSet = @{
Transport = "HTTPS"
Address = "*"
}
ValueSet = @{
CertificateThumbprint = $cert.Thumbprint
Enabled = $true
}
}
New-WSManInstance @httpsParams
# Opens port 5986 for all profiles
$firewallParams = @{
Action = 'Allow'
Description = 'Inbound rule for Windows Remote Management via WS-Management. [TCP 5986]'
Direction = 'Inbound'
DisplayName = 'Windows Remote Management (HTTPS-In)'
LocalPort = 5986
Profile = 'Any'
Protocol = 'TCP'
}
New-NetFirewallRule @firewallParams
警告
以上脚本仅用于演示目的,应在生产环境中运行之前进行审查。某些更改,例如为所有传入连接打开防火墙端口,允许使用 WinRM 的本地帐户,自签名证书,可能不适合所有环境。
枚举监听器
要查看在 WinRM 服务上运行的当前监听器
winrm enumerate winrm/config/Listener
这将输出类似以下内容
Listener
Address = *
Transport = HTTP
Port = 5985
Hostname
Enabled = true
URLPrefix = wsman
CertificateThumbprint
ListeningOn = 10.0.2.15, 127.0.0.1, 192.168.56.155, ::1, fe80::5efe:10.0.2.15%6, fe80::5efe:192.168.56.155%8, fe80::
ffff:ffff:fffe%2, fe80::203d:7d97:c2ed:ec78%3, fe80::e8ea:d765:2c69:7756%7
Listener
Address = *
Transport = HTTPS
Port = 5986
Hostname = SERVER2016
Enabled = true
URLPrefix = wsman
CertificateThumbprint = E6CDAA82EEAF2ECE8546E05DB7F3E01AA47D76CE
ListeningOn = 10.0.2.15, 127.0.0.1, 192.168.56.155, ::1, fe80::5efe:10.0.2.15%6, fe80::5efe:192.168.56.155%8, fe80::
ffff:ffff:fffe%2, fe80::203d:7d97:c2ed:ec78%3, fe80::e8ea:d765:2c69:7756%7
在上面的示例中,配置了两个 WinRM 监听器。一个通过 HTTP 监听端口 5985,另一个通过 HTTPS 监听端口 5986。一些有助于理解的关键选项是
Transport
:监听器是通过 HTTP 还是 HTTPS 运行Port
:要监听的端口,HTTP 的默认值为5985
,HTTPS 的默认值为5986
CertificateThumbprint
:对于 HTTPS,这是用于 TLS 连接的证书的指纹
要查看由 CertificateThumbprint
指定的证书详细信息,你可以运行以下 PowerShell 命令
$thumbprint = "E6CDAA82EEAF2ECE8546E05DB7F3E01AA47D76CE"
Get-Item -Path "Cert:\LocalMachine\My\$thumbprint" | Select-Object *
创建监听器
可以通过 Enable-PSRemoting
cmdlet 创建 HTTP 监听器,但你也可以使用以下 PowerShell 代码手动创建 HTTP 监听器。
$listenerParams = @{
ResourceURI = 'winrm/config/listener'
SelectorSet = @{
Transport = "HTTP"
Address = "*"
}
ValueSet = @{
Enabled = $true
Port = 5985
}
}
New-WSManInstance @listenerParams
创建 HTTPS 监听器类似,但 Port
现在为 5985
,并且必须设置 CertificateThumbprint
值。证书可以是自签名证书,也可以是来自证书颁发机构的证书。如何生成证书超出了本节的范围。
$listenerParams = @{
ResourceURI = 'winrm/config/listener'
SelectorSet = @{
Transport = "HTTP"
Address = "*"
}
ValueSet = @{
CertificateThumbprint = 'E6CDAA82EEAF2ECE8546E05DB7F3E01AA47D76CE'
Enabled = $true
Port = 5985
}
}
New-WSManInstance @listenerParams
必须将 CertificateThumbprint
值设置为安装在 LocalMachine\My
证书存储中的证书的指纹。
可以将 Address
选择器值设置为以下三个值之一
*
- 绑定到所有地址IP:...
- 绑定到由...
指定的 IPv4 或 IPv6 地址MAC:32-a3-58-90-be-cc
- 绑定到具有指定 MAC 地址的适配器
删除监听器
以下代码可以删除所有监听器或特定监听器
# Removes all listeners
Remove-Item -Path WSMan:\localhost\Listener\* -Recurse -Force
# Removes only HTTP listeners
Get-ChildItem -Path WSMan:\localhost\Listener |
Where-Object Keys -contains "Transport=HTTP" |
Remove-Item -Recurse -Force
# Removes only HTTPS listeners
Get-ChildItem -Path WSMan:\localhost\Listener |
Where-Object Keys -contains "Transport=HTTPS" |
Remove-Item -Recurse -Force
WinRM 身份验证
WinRM 具有几种不同的身份验证选项,可用于验证用户与 Windows 主机的身份。每个选项都有自己的优点和缺点,因此了解何时使用每个选项以及何时不使用每个选项非常重要。
以下矩阵是对选项的高级概述
选项 |
本地帐户 |
Active Directory 帐户 |
凭据委派 |
HTTP 加密 |
---|---|---|---|---|
基本 |
是 |
否 |
否 |
否 |
证书 |
是 |
否 |
否 |
否 |
Kerberos |
否 |
是 |
是 |
是 |
NTLM |
是 |
是 |
否 |
是 |
CredSSP |
是 |
是 |
是 |
是 |
通过 HTTP 监听器使用 Basic
和 NTLM
身份验证选项不应该,因为它们要么不提供加密,要么提供非常弱的加密。 psrp
连接插件还提供 Negotiate
身份验证选项,它将在回退到 NTLM
之前尝试使用 Kerberos
。 winrm
连接插件必须指定 kerberos
或 ntlm
。
要指定身份验证协议,你可以使用以下变量
# For psrp
ansible_psrp_auth: basic|certificate|negotiate|kerberos|ntlm|credssp
# For winrm
ansible_winrm_transport: basic|certificate|kerberos|ntlm|credssp
WinRM 的建议是在域环境中通过 HTTP 使用 Kerberos 身份验证,或者对于本地帐户通过 HTTPS 使用 Basic/NTLM。CredSSP 应该只在绝对必要时使用,因为它使用不受约束的委派,存在安全风险。
基本
基本身份验证是最容易使用的身份验证选项之一,但也是最不安全的。这是因为用户名和密码只是 base64 编码的,如果未在使用安全通道(例如 HTTPS),则任何人都可以对其进行解码。基本身份验证只能用于本地帐户(不能用于域帐户)。
以下示例显示了为基本身份验证配置的主机变量
ansible_user: LocalUsername
ansible_password: Password
# psrp
ansible_connection: psrp
ansible_psrp_auth: basic
# winrm
ansible_connection: winrm
ansible_winrm_transport: basic
Windows 主机默认情况下未启用基本身份验证,但可以通过在 PowerShell 中运行以下命令来启用
Set-Item -Path WSMan:\localhost\Service\Auth\Basic -Value $true
证书
有关如何配置和使用证书身份验证的更多信息,请参阅 WinRM 证书身份验证。
NTLM
NTLM 是 Microsoft 使用的一种较旧的身份验证机制,可以支持本地帐户和域帐户。NTLM 在 WinRM 服务中默认启用,因此在使用它之前不需要进行任何设置。
NTLM 是最容易使用的身份验证协议,比 Basic
身份验证更安全。如果在域环境中运行,应该使用 Kerberos
而不是 NTLM。
Kerberos 与使用 NTLM 相比,有以下几个优点
NTLM 是一个较旧的协议,不支持更新的加密协议。
NTLM 身份验证速度较慢,因为它在身份验证阶段需要与主机进行更多轮次通信。
与 Kerberos 不同,NTLM 不允许凭据委派。
此示例显示了配置为使用 NTLM 身份验证的主机变量
ansible_user: LocalUsername
ansible_password: Password
# psrp
ansible_connection: psrp
ansible_psrp_auth: negotiate # or ntlm to only use NTLM
# winrm
ansible_connection: winrm
ansible_winrm_transport: ntlm
Kerberos 和协商
在域环境中运行时,建议使用 Kerberos 身份验证选项。Kerberos 支持身份验证委派和通过 HTTP 的消息加密等功能,是 WinRM 提供的最安全的选项之一。
Kerberos 要求在 Ansible 主机上进行一些额外的设置才能正常使用。有关如何配置、使用和排除 Kerberos 身份验证故障的更多信息,请参阅 Kerberos 身份验证。
以下示例显示了为 Kerberos 身份验证配置的主机变量
ansible_user: [email protected]
ansible_password: Password
# psrp
ansible_connection: psrp
ansible_psrp_auth: negotiate # or kerberos to disable ntlm fallback
# winrm
ansible_connection: winrm
ansible_winrm_transport: kerberos
CredSSP
CredSSP 身份验证是一种较新的身份验证协议,允许身份验证委派。这是通过在身份验证成功后对用户名和密码进行加密,并将它们使用 CredSSP 协议发送到服务器来实现的。
由于用户名和密码被发送到服务器以用于双跳身份验证,请确保与 Windows 主机通信的主机没有被破坏并且是可信的。
CredSSP 可用于本地和域帐户,并且还支持通过 HTTP 的消息加密。
要使用 CredSSP 身份验证,主机变量配置如下
ansible_user: Username
ansible_password: Password
# psrp
ansible_connection: psrp
ansible_psrp_auth: credssp
# winrm
ansible_connection: winrm
ansible_winrm_transport: credssp
CredSSP 身份验证在 Windows 主机上默认情况下未启用,但可以通过在 PowerShell 中运行以下命令来启用
Enable-WSManCredSSP -Role Server -Force
CredSSP 要求安装可选的 Python 库,可以使用 pipx 完成
pipx inject "pypsrp[credssp]<=1.0.0" # for psrp
pipx inject "pywinrm[credssp]>=0.4.0" # for winrm
或者,如果你选择了 pip
安装说明
pip3 install "pypsrp[credssp]<=1.0.0" # for psrp
pip3 install "pywinrm[credssp]>=0.4.0" # for winrm
CredSSP 通过使用 TLS 连接来包装身份验证令牌和随后通过连接发送的消息来工作。默认情况下,它将使用 Windows 自动生成的自签名证书。虽然通过 HTTPS 连接使用 CredSSP 仍然需要验证 WinRM 侦听器使用的 HTTPS 证书,但对 CredSSP 证书没有进行验证。可以通过在 WinRM 服务配置下设置 CertificateThumbprint
选项来配置 CredSSP 以使用不同的证书。
# Note the value $thumprint will be different in each situation, this needs
# to be set based on the cert that is used.
$thumbprint = "7C8DCBD5427AFEE6560F4AF524E325915F51172C"
# Set the thumbprint value
Set-Item -Path WSMan:\localhost\Service\CertificateThumbprint -Value $thumbprint
非管理员帐户
WinRM 默认情况下配置为仅允许来自本地 Administrators
组中帐户的连接。可以通过运行以下命令来更改此设置
winrm configSDDL default
这将显示一个 ACL 编辑器,可以在其中添加新的用户或组。要通过 WinRM 运行命令,用户和组必须至少具有已启用的 Read
和 Execute
权限。
虽然非管理员帐户可与 WinRM 一起使用,但大多数典型的服务器管理任务需要一定程度的管理访问权限,因此该实用程序通常有限。
WinRM 加密
默认情况下,WinRM 在通过未加密的通道运行时将无法正常工作。如果使用通过 HTTP 的 TLS(HTTPS)或使用消息级加密,WinRM 协议将认为通道已加密。建议使用 TLS 与 WinRM 一起使用,因为它适用于所有身份验证选项,但需要在 WinRM 侦听器上创建和使用证书。
如果在域环境中,ADCS 可以为由域本身颁发的主机创建证书。
如果使用 HTTPS 不是一种选择,则当身份验证选项为 NTLM
、Kerberos
或 CredSSP
时,可以使用 HTTP。这些协议将在将其发送到服务器之前使用自己的加密方法对 WinRM 负载进行加密。在通过 HTTPS 运行时,不会使用消息级加密,因为加密使用更安全的 TLS 协议。如果需要传输和消息加密,可以设置以下主机变量
# psrp
ansible_psrp_message_encryption: always
# winrm
ansible_winrm_message_encryption: always
注意
通过 HTTP 的消息加密需要 pywinrm>=0.3.0。
最后一种方法是在 Windows 主机上禁用加密要求。这仅应用于开发和调试目的,因为从 Ansible 发送的任何内容都可以被查看或操作,并且远程会话可以被同一网络上的任何人完全接管。要禁用加密要求
Set-Item -Path WSMan:\localhost\Service\AllowUnencrypted -Value $true
注意
除非绝对必要,否则请不要禁用加密检查。这样做可能会使敏感信息(如凭据和文件)被网络上的其他人拦截。
HTTPS 证书验证
作为 TLS 协议的一部分,证书将被验证以确保主机与主体匹配,并且客户端信任服务器证书的发行者。如果使用自签名证书,则客户端将不信任证书,连接将失败。要绕过此问题,请根据使用的连接插件设置以下主机变量
ansible_psrp_cert_validation: ignore
ansible_winrm_server_cert_validation: 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 链用作验证过程的一部分,请根据用于 CA PEM 格式文件的路径设置以下主机变量,具体取决于使用的连接插件
ansible_psrp_ca_cert
ansible_winrm_ca_trust_path
如果未设置此变量,则将使用默认 CA 链,该链位于 Python 包 certifi 的安装路径中。某些 Linux 发行版可能已配置基础 Python requests
库,该库由 psrp
和 winrm
连接插件使用,以使用系统的证书存储而不是 certifi
。如果是这种情况,CA 链将与系统证书存储相同。
WinRM 限制
由于 WinRM 协议的设计,在使用 WinRM 时存在一些限制,这可能会在为 Ansible 创建剧本时造成问题。这些限制包括
对于大多数身份验证类型,凭据不会被委派,这会导致在访问网络资源或安装某些程序时出现身份验证错误。
在通过 WinRM 运行时,许多对 Windows 更新 API 的调用被阻止。
由于没有凭据委派,或者因为它们通过 WinRM 访问了被禁止的 Windows API(如 WUA),因此某些程序无法通过 WinRM 安装。
WinRM 下的命令是在非交互式会话中执行的,这可能会阻止某些命令或可执行文件运行。
无法运行与
DPAPI
交互的进程,该进程被某些安装程序(如 Microsoft SQL Server)使用。
可以通过执行以下操作之一来缓解其中一些限制
将身份验证方法设置为使用
credssp
或kerberos
并且启用身份验证委派使用
become
绕过所有 WinRM 限制,并以本地方式运行命令。与使用credssp
等身份验证传输不同,这也将删除非交互式限制和 API 限制(如 WUA 和 DPAPI)使用计划任务运行可以使用
win_scheduled_task
模块创建的命令。与become
一样,这将绕过所有 WinRM 限制,但只能运行命令,而不能运行模块。
WinRM 故障排除
WinRM 有很多配置选项,这使得它的配置很复杂。因此,Ansible 显示的错误实际上可能是主机设置方面的问题。
要确定主机问题,请从另一个 Windows 主机运行以下命令以测试与目标 Windows 主机的连接。
要测试 HTTP
# winrm
winrs -r:http://server:5985/wsman -u:Username -p:Password ipconfig
# psrp
Invoke-Command -ComputerName server { ipconfig } -Credential username
要测试 HTTPS
# winrm
winrs -r:https://server:5986/wsman -u:Username -p:Password -ssl ipconfig
# psrp
Invoke-Command -UseSSL -ComputerName server { ipconfig } -Credential username
# psrp ignoring certs
$sessionOption = New-PSSessionOption -SkipCACheck -SkipCNCheck -SkipRevocationCheck
Invoke-Command -UseSSL -ComputerName server { ipconfig } -Credential username -SessionOption $sessionOption
要验证目标主机名是否可以在 Ansible 控制节点上解析,请运行以下命令之一
dig +search server
# May fail if the Windows firewall is set to block ICMP pings
# but will show the hostname if resolvable.
ping server
要验证 WinRM 服务是否正在侦听以及防火墙是否未阻止连接,可以使用 nc
测试通过 WinRM 端口的连接
# HTTP port
> nc -zv server 5985
Connection to server port 5985 [tcp/wsman] succeeded!
# HTTPS port
> nc -zv server 5986
Connection to server port 5986 [tcp/wsmans] succeeded!
要验证 WinRM 是否具有 HTTPS 侦听器并正常工作,可以使用 openssl s_client
测试连接并查看证书详细信息
echo '' | openssl s_client -connect server:5986
注意
openssl s_client
命令将使用系统信任存储来验证证书,这可能与 Ansible 中使用的信任存储不一致。有关更多信息,请参阅 HTTPS 证书验证。
另请参阅
- Ansible 剧本
剧本简介
- Ansible 技巧和窍门
剧本的技巧和窍门
- Windows 模块列表
Windows 特定的模块列表,所有模块都在 PowerShell 中实现
- 通信
有疑问?需要帮助?想分享你的想法?请访问 Ansible 通信指南