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 监听器。一个在端口 5985 上通过 HTTP 监听,另一个在端口 5986 上通过 HTTPS 监听。一些有用的关键选项是
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 |
是 |
是 |
是 |
是 |
Basic
和 NTLM
身份验证选项不应通过 HTTP 监听器使用,因为它们要么不提供加密,要么提供非常弱的加密。psrp
连接插件还提供 Negotiate
身份验证选项,该选项会尝试使用 Kerberos
,然后再回退到 NTLM
。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 是微软使用的一种较旧的身份验证机制,可以支持本地和域帐户。默认情况下,WinRM 服务启用 NTLM,因此在使用之前无需进行任何设置。
NTLM 是最容易使用的身份验证协议,并且比 Basic
身份验证更安全。如果在域环境中运行,则应使用 Kerberos
而不是 NTLM。
与使用 NTLM 相比,Kerberos 有几个优点
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
默认情况下,Windows 主机上未启用 CredSSP 身份验证,但可以通过在 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 (HTTPS) 使用 TLS 或使用消息级加密,则 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 创建 playbook 时导致问题。这些限制包括
大多数身份验证类型不会委派凭据,这会在访问网络资源或安装某些程序时导致身份验证错误。
通过 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 通信指南