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 侦听器。一个在端口 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 主机上的用户进行身份验证。每个选项都有其自身的优缺点,因此了解何时使用每个选项以及何时不使用每个选项非常重要。

下表是选项的高级概述

选项

本地帐户

活动目录帐户

凭据委派

HTTP 加密

基本

证书

Kerberos

NTLM

CredSSP

BasicNTLM 身份验证选项不应在 HTTP 侦听器上使用,因为它们要么不提供加密,要么提供非常弱的加密。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 使用的较旧的身份验证机制,可以支持本地帐户和域帐户。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 和 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

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 运行命令,用户和组必须至少具有启用的 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 侦听器的一种更常见的方法是使用活动目录证书服务 (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 库配置为 psrpwinrm 连接插件使用,以使用系统的证书存储而不是 certifi。如果是这种情况,则 CA 链将与系统的证书存储相同。

WinRM 限制

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

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

  • 通过 WinRM 运行时,对 Windows 更新 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 playbook

Playbook 入门

Ansible 提示和技巧

Playbook 提示和技巧

Windows 模块列表

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

沟通

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