在10個步驟中使用證書為Ansible安全WinRM

Ansible正成為當今最常用的設定管理工具之一,如果不是常用的設定管理工具。Ansible是一個方便(在大多數情況下是免費的)工具,讓DevOps工程師和系統工程師/管理員能以冪等的基礎設施即代碼方式,建立和維護所有環境的基礎設施。但是,將其配置為通過WinRM與Ansible進行通信可能是一個挑戰。

像許多其他基礎設施組件一樣,Ansible可以部署並維護Windows主機的配置狀態。Ansible通過WinRM連接到這些Windows主機,雖然他們正在嘗試使用SSH

當你為Ansible配置WinRm時,你有幾種不同的選擇,從設置的簡易度到安全性的影響。很多人選擇易於操作的方法;使用HTTP進行基本認證。雖然你放棄了涉及證書的額外工作,但除非必須,否則將未加密的數據發送到網絡上絕不是一個好主意。

在本文中,您将学习如何使用基于证书的身份验证,使用自签名证书设置WinRm,以便Ansible可以与其通信。

您将在本教程中学习的设置不仅适用于Ansible作为客户端。这种基于证书的身份验证同样适用于其他WinRm客户端,如其他Windows主机。

假设

本文将以教程为基础进行演示。如果您打算按照这里介绍的步骤配置WinRm以供Ansible使用,教程将假设:

  • 您已经在Linux主机上安装了Ansible。
  • 您已根据您的Windows主机设置了Ansible清单。
  • 您拥有一个Windows Server 2016或更高版本进行管理。本教程中使用的某些cmdlet在较旧版本的Windows上无法工作。
  • Windows主机不在域中。虽然示例是在非域加入的主机上执行的,但这种配置应该仍然适用于域加入的主机。
  • 您可以通过RDP或控制台访问Windows主机,并以本地管理员身份登录。
  • 您熟悉PowerShell。几乎所有示例都将使用PowerShell代码在Windows主机上进行更改。

每個部分都將使用依賴於上一個部分的代碼片段。一些片段將引用先前定義的變量。如果您打算按照完全相同的方式進行操作,請在複製/粘貼代碼時保持控制台打開。

如果您只想要代碼而不需要所有的解釋,可以隨時從GitHub Gist中下載。

為Ansible啟用WinRm的PowerShell Remoting

儘管所有Windows Server 2016或更高版本的服務器都啟用了PowerShell Remoting,但確認一下仍然是個好主意。

在要管理的Windows主機上,以系統管理員身份打開PowerShell控制台,運行以下代碼片段。這段代碼確保WinRm服務已啟動並設置為系統啟動時自動啟動。

Set-Service -Name "WinRM" -StartupType Automatic
Start-Service -Name "WinRM"

接下來,確保啟用了PowerShell Remoting,首先檢查是否有任何會話配置處於活動狀態。如果沒有,則確保沒有可用的監聽器。Ansible的WinRm必須至少有一個監聽器。如果這兩個條件中的任何一個都沒有返回任何內容,請運行Enable-PSRemoting

if (-not (Get-PSSessionConfiguration) -or (-not (Get-ChildItem WSMan:\localhost\Listener))) {
    ## 使用SkipNetworkProfileCheck使其在Windows防火牆的公共配置文件上可用
    ## 使用Force來確定是否確定或不確定。
    Enable-PSRemoting -SkipNetworkProfileCheck -Force
}
Configuring WinRM for Ansible (a listener) via the winrm command

啟用基於證書的身份驗證

預設情況下,WinRM未設定為基於憑證的身份驗證。您必須按照下面的配置來啟用它。

#region 啟用基於憑證的身份驗證
Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true
#endregion

建立本地使用者帳戶

為了使用基於憑證的WinRM進行Ansible身份驗證,您必須將本地使用者帳戶與憑證進行”映射”。您可以使用本地管理員帳戶來執行此操作,但建立一個特定帳戶以便更容易進行管理是一個好主意。

以下程式碼片段將為Ansible的WinRm建立一個名為ansibletestuser,密碼為p@$$w0rd12的本地使用者帳戶,如果該帳戶不存在。為了確保它始終保持活動狀態,該帳戶的密碼將永不過期。

$testUserAccountName = 'ansibletestuser'
$testUserAccountPassword = (ConvertTo-SecureString -String 'p@$$w0rd12' -AsPlainText -Force)
if (-not (Get-LocalUser -Name $testUserAccountName -ErrorAction Ignore)) {
    $newUserParams = @{
        Name                 = $testUserAccountName
        AccountNeverExpires  = $true
        PasswordNeverExpires = $true
        Password             = $testUserAccountPassword
    }
    $null = New-LocalUser @newUserParams
}

建立客戶端憑證

Ansible的WinRm(安全地)需要兩個憑證:客戶端憑證和伺服器憑證。

您可以將同一個憑證用於客戶端和伺服器,但我在使用這種方法時遇到了問題。在Ansible文件和許多其他來源中,您將找到使用PowerShell的New-SelfSignedCert命令來生成客戶端憑證的說明。雖然這種方法可能有效,但我無法輕易使其運作。

要为Ansible的WinRM创建客户端证书,您必须创建一个私钥和一个公钥。首先通过SSH连接到Ansible主机,然后运行以下openssl命令。该命令会在一个名为cert_key.pem的文件中创建一个私钥,并创建一个名为cert.pem的公钥。密钥用途将是客户端身份验证(1.3.6.1.4.1.311.20.2.3)“映射”到之前创建的本地用户账户ansibletestuser

## 这是使用Ansible服务器生成的公钥
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:ansibletestuser@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=ansibletestuser" -extensions v3_req_client
rm openssl.conf 

离Ansible WinRM身份验证更近一步!

导入客户端证书

一旦为Ansible的WinRm创建了客户端证书,您需要将其导入到Windows主机上的两个证书存储中,以使Ansible上的WinRm正常工作。为此,首先将cert.pem公钥传输到Windows主机。以下示例假设密钥存在于C:\cert.pem位置。

在Windows主机上获取了公共证书后,使用Import-Certificate命令将其导入到可信根证书颁发机构受信任的人证书存储中,如下所示。

$pubKeyFilePath = 'C:\cert.pem'

## 将公钥导入到可信根证书颁发机构和受信任的人
$null = Import-Certificate -FilePath $pubKeyFilePath -CertStoreLocation 'Cert:\LocalMachine\Root'
$null = Import-Certificate -FilePath $pubKeyFilePath -CertStoreLocation 'Cert:\LocalMachine\TrustedPeople'

创建服务器证书

Ansible的WinRM需要定义一个具有用于服务器身份验证的密钥用途的证书。该证书将存储在Windows主机的LocalMachine\My证书存储中。使用下面的代码段创建自签名证书。

$hostname = hostname
$serverCert = New-SelfSignedCertificate -DnsName $hostName -CertStoreLocation 'Cert:\LocalMachine\My'

創建Ansible WinRM監聽器

創建完兩個證書後,現在必須在Windows主機上創建一個WinRM監聽器。該監聽器開始在端口5986上進行聆聽,以接受傳入的連接。一旦創建完成,該監聽器將接受傳入的連接,並嘗試使用上述創建的服務器證書加密數據。

在身份驗證完成後,PowerShell Remoting將使用此WinRM監聽器作為傳輸方式。

在下面的代碼片段中,您可以看到檢查現有HTTPS監聽器的一個很好的示例。如果未使用上述創建的服務器證書找到監聽器,它將創建一個新的監聽器。

## 查找所有HTTPS監聽器
$httpsListeners = Get-ChildItem -Path WSMan:\localhost\Listener\ | where-object { $_.Keys -match 'Transport=HTTPS' }

## 如果根本未定義監聽器,或者未配置與
## 服務器證書配合工作的監聽器,則創建一個新的
## 主題為計算機主機名的監聽器
if ((-not $httpsListeners) -or -not (@($httpsListeners).where( { $_.CertificateThumbprint -ne $serverCert.Thumbprint }))) {
    $newWsmanParams = @{
        ResourceUri = 'winrm/config/Listener'
        SelectorSet = @{ Transport = "HTTPS"; Address = "*" }
        ValueSet    = @{ Hostname = $hostName; CertificateThumbprint = $serverCert.Thumbprint }
        # UseSSL = $true
    }
    $null = New-WSManInstance @newWsmanParams
}

將客戶端證書映射到本地用戶帳戶

下一步是確保當Ansible使用服務器證書連接到Windows主機時,它將以本地用戶身份執行所有指令。在這種情況下,Ansible的WinRm執行的所有活動將使用本地用戶帳戶ansibletestuser

$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $testUserAccountName, $testUserAccountPassword

## 查找在Ansible主機上創建的客戶端證書的證書指紋
$ansibleCert = Get-ChildItem -Path 'Cert:\LocalMachine\Root' | Where-Object {$_.Subject -eq 'CN=ansibletestuser'}

$params = @{
	Path = 'WSMan:\localhost\ClientCertificate'
	Subject = "$testUserAccountName@localhost"
	URI = '*'
	Issuer = $ansibleCert.Thumbprint
  Credential = $credential
	Force = $true
}
New-Item @params

允許使用者帳戶控制 (UAC) 的 WinRm

如果使用本機帳戶對應到憑證,您還必須將 LocalAccountTokenFilterPolicy 設置為 1,以確保 UAC 不會妨礙。 LocalAccountTokenFilterPolicy 適用於所有本機帳戶(非域帳戶),並導致您的網路登錄成為令牌的有限部分。這將阻止其以管理員身份登錄,因為 Windows 不將其視為管理員,而 WinRM 預設要求使用者是本機管理員。

通過設置 LocalAccountTokenFilterPolicy,您告訴 Windows 不要為本機帳戶的網路登錄創建有限令牌,而使用其完整令牌。

$newItemParams = @{
    Path         = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
    Name         = 'LocalAccountTokenFilterPolicy'
    Value        = 1
    PropertyType = 'DWORD'
    Force        = $true
}
$null = New-ItemProperty @newItemParams

在 Windows 防火牆上開啟端口 5986

WinRm 透過 HTTPS 使用端口 5986。如果您啟用了 Windows 防火牆,您必須開放此端口。您可以通過執行以下 PowerShell 代碼片段來執行此操作。此代碼片段過於寬鬆,允許所有計算機使用。如果您想進一步限制,請確保使用 LocalAddress 參數並指定 Ansible 的 IP。

#region 確保 WinRM 5986 在防火牆上開啟
 $ruleDisplayName = 'Windows Remote Management (HTTPS-In)'
 if (-not (Get-NetFirewallRule -DisplayName $ruleDisplayName -ErrorAction Ignore)) {
     $newRuleParams = @{
         DisplayName   = $ruleDisplayName
         Direction     = 'Inbound'
         LocalPort     = 5986
         RemoteAddress = 'Any'
         Protocol      = 'TCP'
         Action        = 'Allow'
         Enabled       = 'True'
         Group         = 'Windows Remote Management'
     }
     $null = New-NetFirewallRule @newRuleParams
 }
 #endregion

將本機使用者添加到管理員組

您可能会问为什么此教程之前没有将本地用户添加到管理员组中。原因是当您尝试将客户端证书映射到用户时,用户帐户不能是本地管理员,原因未知。

运行以下代码片段将ansibletestuser本地用户帐户添加到管理员组中。

## 将本地用户添加到管理员组。如果没有执行此步骤,Ansible 将出现"AccessDenied"错误
Get-LocalUser -Name $testUserAccountName | Add-LocalGroupMember -Group 'Administrators'

结论

如果您按照所示的每个步骤进行操作,现在应该能够对Windows主机执行Ansible命令了。使用win_shell模块进行测试。如果此模块成功,表示您已成功配置了用于Ansible和基于证书的身份验证的WinRM!

Source:
https://adamtheautomator.com/winrm-for-ansible/