Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save orimanabu/dddeb14935a3e74874aca7c9faf1a87c to your computer and use it in GitHub Desktop.
Save orimanabu/dddeb14935a3e74874aca7c9faf1a87c to your computer and use it in GitHub Desktop.
Enable WinRM for Ansible using self signed certificate generated by OpenSSL

はじめに

これは、AnsibleでWindowsを管理するために、WinRMにHTTPSで接続できるよう設定するためのドキュメントです。 自己証明書は、PowerShellの New-SelfSignedCertificate ではなくLinux上でopensslコマンドで作成したものを使用します。

設定対象のWindows VMを

  • ホスト名: wintest1
  • IPアドレス: 192.168.122.70

と仮定しています。

以下の手順では、証明書を3つ作成します。

  • オレオレ認証局のCA証明書 (ca.pfx)
  • Windows VMにHTTPSでWinRM接続するためのサーバー証明書 (wintest1.pfx)
  • WinRMへの認証に使用するクライアント証明書 (client.crt)

Linuxマシン上で自己証明書を作成する

以下、Linuxマシン上での作業です。

まず最初にopensslコマンドに渡す設定ファイル openssl.conf を作成します。

[v3_server] セクションの subjectAltName は環境にあわせて変更してください。

$ cat openssl.conf
distinguished_name = req_distinguished_name
[req_distinguished_name]

[v3_ca]
basicConstraints = CA:true
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
keyUsage = cRLSign, keyCertSign

[v3_server]
basicConstraints = CA:false
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid, issuer
subjectAltName = DNS:wintest1, DNS:WINTEST1, IP:192.168.122.70

[v3_client]
basicConstraints = CA:false
extendedKeyUsage = clientAuth
subjectAltName = otherName:1.3.6.1.4.1.311.20.2.3;UTF8:ansible@localhost

下記コマンドを実行して、CAのself-signedな証明書を作成します。

mkdir -p ./pki
openssl genrsa -out ./pki/ca.key 4096
openssl req -new -key ./pki/ca.key -out ./pki/ca.csr -sha256 -subj "/O=MYCOMPANY/OU=HQ/CN=myauthority"
openssl x509 -req -days 3650 -signkey ./pki/ca.key -in ./pki/ca.csr -out ./pki/ca.crt -extensions v3_ca -extfile openssl.conf
openssl pkcs12 -export -password pass: -out ./pki/ca.pfx -inkey ./pki/ca.key -in ./pki/ca.crt

作成したCA証明書を使用して、サーバー証明書を作成します。

openssl genrsa -out ./pki/wintest1.key 4096
openssl req -new -key ./pki/wintest1.key -out ./pki/wintest1.csr -sha256 -subj "/O=MYCOMPANY/OU=HQ/CN=wintest1"
openssl x509 -req -days 3650 -CA ./pki/ca.crt -CAkey ./pki/ca.key -CAcreateserial -in ./pki/wintest1.csr -out ./pki/wintest1.crt -extensions v3_server -extfile openssl.conf
openssl pkcs12 -export -password pass: -out ./pki/wintest1.pfx -inkey ./pki/wintest1.key -in ./pki/wintest1.crt

最後に、WinRM接続時の認証で使用するクライアント証明書を作成します。

openssl genrsa -out ./pki/client.key 4096
openssl req -new -key ./pki/client.key -out ./pki/client.csr -sha256 -subj "/CN=ansible"
openssl x509 -req -days 3650 -CA ./pki/ca.crt -CAkey ./pki/ca.key -CAcreateserial -in ./pki/client.csr -out ./pki/client.crt -extensions v3_client -extfile openssl.conf

作成した下記3つのファイルを、後でWindows VMに持っていって証明書ストアに登録します。

  • ca.pfx
  • wintest1.pfx
  • client.crt

Windows VMへのHTTPSでのWinRM接続

Windows Server 2022の仮想マシンを準備します。 下記サイトからPowerShellの最新版をインストールしておきます。

https://aka.ms/PSWindows

以下、Windows VMのPowerShellで作業します。

ホスト名の変更

ホスト名を wintest1 に変更します。

Rename-Computer -NewName "wintest1" -DomainCredential Administrator -Restart

ユーザー作成

ユーザー ansible を作成し、Administrators グループに所属させます。

$username = "ansible"
$password = ConvertTo-SecureString "secret" -AsPlainText -Force
New-LocalUser -Name $username -FullName 'Ansible User' -Password $password -PasswordNeverExpires -Description "Ansible Test User" -AccountNeverExpires

Add-LocalGroupMember -Group "Administrators" -Member $username

WinRMの設定

WinRMの起動

Set-Service -Name "WinRM" -StartupType Automatic
Start-Service -Name "WinRM" -ErrorAction Stop
Enable-PSRemoting -Force -ErrorAction Stop

証明書のインポート

Linuxマシンから、作成した自己証明書をコピーします。

scp -r USER@LINUXBOX:SOMEWHERE/pki/*.pfx .
scp -r USER@LINUXBOX:SOMEWHERE/pki/client.crt .

CA証明書を ローカルコンピューター信頼されたルート証明機関 にインポートします。

$certificate = Import-PfxCertificate -FilePath .\ca.pfx -CertStoreLocation Cert:\LocalMachine\Root

サーバー証明書を ローカルコンピューター個人 にインポートします。

$certificate = Import-PfxCertificate -FilePath .\wintest1.pfx -CertStoreLocation Cert:\LocalMachine\my

HTTPSリスナーの起動

WinRMのHTTPSリスナーを有効化します。

New-Item -Path WSMan:\localhost\Listener -Transport HTTPS -Address '*' -CertificateThumbPrint $certificate.Thumbprint -Force

メモ

MS公式含むいくつかのドキュメントで、HTTPSリスナーの登録方法として

$thumbprint = (Get-ChildItem -Path cert:\LocalMachine\My | Where-Object {$_.Subject -match "CN=wintest1"}).Thumbprint
$valueset = @{
  Hostname = "wintest1"
  CertificateThumbprint = $thumbprint
}
$selectorset = @{
  Transport = "HTTPS"
  Address = "*"
}
New-WSManInstance -ResourceURI 'winrm/config/Listener' -SelectorSet $selectorset -ValueSet $valueset

というコマンドが紹介されていますが、手元の環境 (Windows Server 2022 + PowerShell v7.4.1) では下記のエラーが出てうまくリスナーを起動できませんでした。

WS-Management サービスは要求を処理できません。リソース URI が見つからないか、間違った形式です。 リソース URI を構築する方法については、マニュアルを参照するか、または "winrm help uris" コマンドを使用してください。

でも ConfigureRemotingForAnsible.ps1 は中で New-WSManInstance を実行してるけどうまくいくんですよね...謎

ファイアウォールルールの追加

WinRMへのHTTPS通信をするために、TCP/5986 への接続を許可するルール Windows Remote Management (HTTPS-In) を追加します。

$ruleDisplayName = 'Windows Remote Management (HTTPS-In)'
$newRuleParams = @{
    DisplayName   = $ruleDisplayName
    Direction     = 'Inbound'
    LocalPort     = 5986
    RemoteAddress = 'Any'
    Protocol      = 'TCP'
    Action        = 'Allow'
    Enabled       = 'True'
    Group         = 'Windows Remote Management'
}
New-NetFirewallRule @newRuleParams

WinRMの設定確認

リスナーの表示

winrm enumerate winrm/config/Listener

WinRM設定の表示

winrm get winrm/config/Service

接続確認

一時的にBasic認証を有効化します。

winrm set winrm/config/service/Auth '@{Basic="true"}'

Ansibleによる接続

Linuxマシンから、以下のインベントリファイルを使って win_ping します。

$ cat inventory_file1
[windows]
192.168.122.70

[windows:vars]
ansible_user=ansible
ansible_password="secret"
ansible_winrm_scheme=https
ansible_winrm_port=5986
ansible_connection=winrm
ansible_winrm_server_cert_validation=ignore

以下のようになれば成功です。

$ ansible windows -i inventory_file1 -m win_ping
192.168.122.70 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

winrm-cli による確認

https://github.com/masterzen/winrm-cli から git clone してコンパイルします。Linuxマシン上で実行しました。

git clone https://github.com/masterzen/winrm-cli
cd winrm-cli/
export GOPATH=~/devel
sudo dnf install -y golang git make
make

以下のようになれば成功です。

$ ~/devel/bin/winrm -insecure -port 5986 -https -username ansible -password 'secret' -hostname 192.168.122.70 "ipconfig /all"

Windows IP Configuration

   Host Name . . . . . . . . . . . . : wintest1
   Primary Dns Suffix  . . . . . . . :
   Node Type . . . . . . . . . . . . : Hybrid
   IP Routing Enabled. . . . . . . . : No
   WINS Proxy Enabled. . . . . . . . : No

Ethernet adapter �C�[�T�l�b�g �C���X�^���X 0:

   Connection-specific DNS Suffix  . :
   Description . . . . . . . . . . . : Intel(R) 82574L Gigabit Network Connection
   Physical Address. . . . . . . . . : 52-54-00-26-A4-0B
   DHCP Enabled. . . . . . . . . . . : Yes
   Autoconfiguration Enabled . . . . : Yes
   Link-local IPv6 Address . . . . . : fe80::38a2:b984:9bd6:3e73%3(Preferred)
   IPv4 Address. . . . . . . . . . . : 192.168.122.70(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Lease Obtained. . . . . . . . . . : 2024�N2��27�� 22:41:04
   Lease Expires . . . . . . . . . . : 2024�N2��28�� 18:55:45
   Default Gateway . . . . . . . . . : 192.168.122.1
   DHCP Server . . . . . . . . . . . : 192.168.122.1
   DHCPv6 IAID . . . . . . . . . . . : 106058752
   DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-2D-6F-9B-30-52-54-00-26-A4-0B
   DNS Servers . . . . . . . . . . . : 192.168.122.1
   NetBIOS over Tcpip. . . . . . . . : Enabled

PowerShellによる確認

別のWindowsから接続してみます。

まず、証明書をコピーしてインポートします。

scp -r USER@LINUXBOX:SOMEWHERE/ca.pfx .
Import-PfxCertificate -Filepath .\ca.crt -CertStoreLocation cert:\LocalMachine\Root

PowerShellで対象のWindowsに接続します。

Enter-PSSession -ComputerName WINTEST1 -Credential "ansible" -UseSSL -Port 5986

PowerShellのプロンプトが [WINTEST1]: PS C:\Users\ansible\Documents> のようになれば成功です。

PS C:\Users\Administrator\Downloads> Enter-PSSession -ComputerName WINTEST1 -Credential "ansible" -UseSSL -Port 5986

PowerShell credential request
Enter your credentials.
Password for user ansible: *********

[WINTEST1]: PS C:\Users\ansible\Documents> ipconfig /all

Windows IP 構成

   ホスト名. . . . . . . . . . . . . . .: wintest1
   プライマリ DNS サフィックス . . . . .:
   ノード タイプ . . . . . . . . . . . .: ハイブリッド
   IP ルーティング有効 . . . . . . . . .: いいえ
   WINS プロキシ有効 . . . . . . . . . .: いいえ

イーサネット アダプター イーサネット インスタンス 0:

   接続固有の DNS サフィックス . . . . .:
   説明. . . . . . . . . . . . . . . . .: Intel(R) 82574L Gigabit Network Connection
   物理アドレス. . . . . . . . . . . . .: 52-54-00-26-A4-0B
   DHCP 有効 . . . . . . . . . . . . . .: はい
   自動構成有効. . . . . . . . . . . . .: はい
   リンクローカル IPv6 アドレス. . . . .: fe80::38a2:b984:9bd6:3e73%3(優先)
   IPv4 アドレス . . . . . . . . . . . .: 192.168.122.70(優先)
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   リース取得. . . . . . . . . . . . . .: 2024年2月27日 22:41:04
   リースの有効期限. . . . . . . . . . .: 2024年2月28日 18:55:45
   デフォルト ゲートウェイ . . . . . . .: 192.168.122.1
   DHCP サーバー . . . . . . . . . . . .: 192.168.122.1
   DHCPv6 IAID . . . . . . . . . . . . .: 106058752
   DHCPv6 クライアント DUID. . . . . . .: 00-01-00-01-2D-6F-9B-30-52-54-00-26-A4-0B
   DNS サーバー. . . . . . . . . . . . .: 192.168.122.1
   NetBIOS over TCP/IP . . . . . . . . .: 有効
[WINTEST1]: PS C:\Users\ansible\Documents>

以上で、Basic認証を使ってWindows VMに対してHTTPSでのWinRM接続をしました。 この後、さらに証明書認証で接続できるように設定します。

証明書認証でのWinRM接続

WinRMの証明書認証の設定

以下、Windows VMでの作業です。

まず、Basic認証を無効化し、証明書認証を有効化します。

winrm set winrm/config/service/Auth '@{Basic="false"}'
winrm set winrm/config/service/Auth '@{Certificate="true"}'

クライアント証明書を ローカルコンピューター信頼されたユーザー にインポートします。

Import-Certificate -FilePath .\client.crt -CertStoreLocation 'Cert:\LocalMachine\TrustedPeople'

クライアント証明書をローカルユーザーに紐づけます。

$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password
$cacert = Get-ChildItem -Path 'Cert:\LocalMachine\Root' | Where-Object {$_.Subject -match 'CN=myauthority'}
$params = @{
	Path = 'WSMan:\localhost\ClientCertificate'
	Subject = "ansible@localhost"
	URI = '*'
	Issuer = $cacert.Thumbprint
	Credential = $credential
	Force = $true
}
New-Item @params

UAC (User Account Control) がネットワークログオンを邪魔しないよう、LocalAccountTokenFilterPolicy を設定します。

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

以上でクライアント証明書による認証の設定は終わりです。

Ansibleによる接続確認

Linuxマシン上で以下のインベントリファイルを用意します。証明書認証したいので、ansible_winrm_cert{,_key}_pem に証明書関連ファイルのパスを指定している一方で、ansible_useransible_password は設定していません。

$ cat inventory_file2
[windows]
192.168.122.70

[windows:vars]
ansible_connection=winrm
ansible_winrm_port=5986
ansible_winrm_server_cert_validation=ignore
ansible_winrm_transport=certificate
ansible_winrm_cert_pem=pki/client.crt
ansible_winrm_cert_key_pem=pki/client.key

このインベントリファイルを使って win_ping します。

$ ansible windows -i inventory_file2 -m win_ping
192.168.122.70 | SUCCESS => {
    "changed": false,
    "ping": "pong"

Pythonスクリプトによる接続確認

Linuxマシン上でpywinrmを使ったPythonスクリプトを実行して、証明書認証でWinRMに接続します。

まずpywinrmをインストールしておきます。

pip install --user pywinrm

こんなPythonスクリプトを実行すると...

#!/usr/bin/env python3

import winrm

p = winrm.protocol.Protocol(
    endpoint='https://192.168.122.70:5986/wsman',
    transport='certificate',
    server_cert_validation='ignore',
    cert_pem='pki/client.crt',
    cert_key_pem='pki/client.key')
shell_id = p.open_shell()
command_id = p.run_command(shell_id, 'ipconfig', ['/all'])
std_out, std_err, status_code = p.get_command_output(shell_id, command_id)

print(std_out.decode('sjis'), end="")

p.cleanup_command(shell_id, command_id)
p.close_shell(shell_id)

こんな出力を得ます。

$ ./mywinrm.py

Windows IP Configuration

   Host Name . . . . . . . . . . . . : wintest1
   Primary Dns Suffix  . . . . . . . :
   Node Type . . . . . . . . . . . . : Hybrid
   IP Routing Enabled. . . . . . . . : No
   WINS Proxy Enabled. . . . . . . . : No

Ethernet adapter イーサネット インスタンス 0:

   Connection-specific DNS Suffix  . :
   Description . . . . . . . . . . . : Intel(R) 82574L Gigabit Network Connection
   Physical Address. . . . . . . . . : 52-54-00-26-A4-0B
   DHCP Enabled. . . . . . . . . . . : Yes
   Autoconfiguration Enabled . . . . : Yes
   Link-local IPv6 Address . . . . . : fe80::38a2:b984:9bd6:3e73%3(Preferred)
   IPv4 Address. . . . . . . . . . . : 192.168.122.70(Preferred)
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Lease Obtained. . . . . . . . . . : 2024年2月27日 22:41:04
   Lease Expires . . . . . . . . . . : 2024年2月28日 23:56:02
   Default Gateway . . . . . . . . . : 192.168.122.1
   DHCP Server . . . . . . . . . . . : 192.168.122.1
   DHCPv6 IAID . . . . . . . . . . . : 106058752
   DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-2D-6F-9B-30-52-54-00-26-A4-0B
   DNS Servers . . . . . . . . . . . : 192.168.122.1
   NetBIOS over Tcpip. . . . . . . . : Enabled

Ansible Automation Platform (もしくはAWX) でWindowsノードに証明書認証でWinRM接続する

既存の Machine Credentialでは証明書認証に対応していないため、まずカスタムのCredential Typeを作成し、それを使用したCredentialを作成します。

カスタムCredential Typeの作成

左ペインの AdministrationCredential Types をクリックし、カスタムのCredential Typeを作成します。 image

Input configuration にはこちらに記載の内容を入力します。

fields:
  - id: cert
    type: string
    label: Certificate
    secret: true
    multiline: true
  - id: key
    type: string
    label: Key
    secret: true
    multiline: true

Injector configuration にはこちらに記載の内容を入力します。

file:
  template.key_file: '{{ key }}'
  template.cert_file: '{{ cert }}'
extra_vars:
  ansible_winrm_cert_pem: '{{ tower.filename.cert_file }}'
  ansible_winrm_cert_key_pem: '{{ tower.filename.key_file }}'

Credentialの作成

ResourceCredentials から、証明書認証用のCredentialを作成します。 image

Credential Type のところで上記で作成したCredential Typeを選択します。Certificate にはクライアント証明書を、Key にはクライアント証明書の秘密鍵を入力してください。

あとは、このCredentialを使用するジョブテンプレートを作成して実行します。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment