Windows 远程管理

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

什么是 WinRM?

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

Ansible 可以通过 psrpwinrm 连接插件使用 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 中使用 winrmpsrp 连接插件通常会失败。这是一个已知问题,它发生在 Python 堆栈深处,Ansible 无法更改。目前唯一的解决方法是设置环境变量 OBJC_DISABLE_INITIALIZE_FORK_SAFETY=yesno_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 主机的用户进行身份验证。每个选项都有其自身的优点和缺点,因此了解何时使用每个选项以及何时不使用每个选项非常重要。

下表是选项的高级概述:

选项

本地帐户

活动目录帐户

凭据委派

HTTP 加密

基本

证书

Kerberos

NTLM

CredSSP

不应通过 HTTP 侦听器使用 BasicNTLM 身份验证选项,因为它们要么不提供加密,要么提供非常弱的加密。psrp 连接插件还提供 Negotiate 身份验证选项,它将在回退到 NTLM 之前尝试使用 Kerberoswinrm 连接插件必须指定 kerberosntlm

要指定身份验证协议,您可以使用以下变量:

# 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使用的一种较旧的身份验证机制,它可以支持本地帐户和域帐户。WinRM服务默认启用NTLM,因此使用前无需任何设置。

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和Negotiate

在域环境中运行时,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运行命令,用户和组必须至少具有ReadExecute权限。

虽然可以使用非管理员帐户使用WinRM,但大多数典型的服务器管理任务都需要某种级别的管理员访问权限,因此其效用通常有限。

WinRM加密

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

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

如果HTTPS不可用,则当身份验证选项为NTLMKerberosCredSSP时,可以使用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发行版可能已将psrpwinrm连接插件使用的底层Python requests库配置为使用系统的证书存储区而不是certifi。如果是这种情况,则CA链将与系统的证书存储区相同。

WinRM限制

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

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

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

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

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

  • 无法运行与DPAPI交互的进程,某些安装程序(如Microsoft SQL Server)使用此进程。

可以通过执行以下操作来减轻其中一些限制

  • 将身份验证方法设置为使用credsspkerberos并启用凭据委派

  • 使用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 沟通指南