10 단계로 Ansible에 대한 Certificates로 WinRM 보안 설정하기

Ansible은 현재 가장, 아니면 가장 인기 있는 구성 관리 도구 중 하나가 되고 있습니다. Ansible은 DevOps 엔지니어, 시스템 엔지니어/관리자가 일관성 있는 방식으로 구조화된 코드 형태로 모든 환경에서 인프라를 구축하고 유지 관리할 수 있게 해주는 편리하고 대부분의 경우 무료인 도구입니다. 그러나 Ansible을 Windows와 WinRM을 통해 통신하도록 설정하는 것은 도전이 될 수 있습니다.

다른 인프라 구성 요소들처럼, Ansible은 Windows 호스트들에 걸쳐 구성 상태를 배포하고 유지 관리할 수 있습니다. Ansible은 WinRM을 통해 이들 Windows 호스트에 연결하지만, SSH를 실험하고 있습니다.

Ansible을 위해 WinRM을 구성할 때, 설정의 용이성부터 보안 문제까지 다양한 옵션 중에서 선택할 수 있습니다. 많은 사람들이 쉬운 접근 방식인 HTTP를 사용한 기본 인증을 선택합니다. 인증서와 관련된 추가 작업을 포기하게 되지만, 반드시 필요한 경우가 아니라면 네트워크를 통해 암호화되지 않은 데이터를 전송하는 것은 결코 좋은 생각이 아닙니다.

이 문서에서는 자체 서명된 인증서를 사용하여 Ansible에 대한 WinRm을 설정하는 방법을 배우게 됩니다. 이렇게 하면 Ansible이 해당 인증서를 사용하여 통신할 수 있습니다. 이 튜토리얼에서 배울 설정 방법은 Ansible만을 대상으로 하는 것이 아니라 다른 Windows 호스트와 같은 다른 WinRm 클라이언트에도 적용할 수 있습니다.

이 튜토리얼에서 배울 설정은 Ansible이 클라이언트로 사용되는 경우뿐만 아니라 다른 Windows 호스트와 같은 다른 WinRm 클라이언트에도 적용할 수 있습니다.

가정

이 문서는 튜토리얼 기반으로 제공될 것입니다. Ansible의 WinRm을 구성하기 위해 여기에 제시된 단계를 따라가려는 경우, 튜토리얼은 다음을 가정합니다:

  • Linux 호스트에 이미 Ansible이 설치되어 있어야 합니다.
  • Windows 호스트를 기반으로한 Ansible 인벤토리가 설정되어 있어야 합니다.
  • 관리할 Windows Server 2016 이상의 버전이 있어야 합니다. 이 튜토리얼에서 사용하는 일부 cmdlet은 이전 버전의 Windows에서는 작동하지 않을 수 있습니다.
  • Windows 호스트가 도메인에 속하지 않아야 합니다. 예시는 도메인에 속하지 않은 호스트에서 수행되었지만, 이 구성은 도메인에 속한 호스트에서도 여전히 작동할 것입니다.
  • Windows 호스트에 RDP 또는 콘솔 액세스가 있으며 로컬 관리자로서 로그인되어 있어야 합니다.
  • 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은 인증서 기반 인증을 위해 구성되어 있지 않습니다. 아래에 표시된 대로 WSMan을 구성하여 이를 활성화해야 합니다.

#인증서 기반 인증 활성화
Set-Item -Path WSMan:\localhost\Service\Auth\Certificate -Value $true
#끝

로컬 사용자 계정 생성

Ansible 인증을 위해 인증서 기반 WinRM을 사용하려면 로컬 사용자 계정을 인증서와 “매핑”해야 합니다. 이를 위해 로컬 관리자 계정을 사용할 수도 있지만, 관리를 쉽게 하기 위해 별도의 계정을 생성하는 것이 좋습니다.

다음 코드 스니펫은 WinRm을 위한 Ansible용 로컬 사용자 계정인 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
}

클라이언트 인증서 생성

WinRm을 위한 Ansible은(안전하게) 클라이언트 인증서와 서버 인증서 두 개가 필요합니다.

클라이언트/서버에 동일한 인증서를 사용할 수 있지만, 이 방법을 사용하면 문제가 발생할 수 있습니다. Ansible 문서 및 다른 많은 소스에서는 PowerShell의 New-SelfSignedCert cmdlet을 사용하여 클라이언트 인증서를 생성하는 방법에 대한 지침을 찾을 수 있지만, 이 방법을 쉽게 작동시키지 못했습니다.

Ansible을 위한 WinRM 클라이언트 인증서를 생성하려면 개인 키와 공개 키를 만들어야 합니다. 먼저 Ansible 호스트에 SSH로 접속한 다음 다음 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 클라이언트 인증서를 생성한 후에는 WinRM이 작동하려면 Windows 호스트의 두 개의 인증서 저장소에 해당 인증서를 가져와야 합니다. 이를 위해 먼저 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' }

## 수신기가 전혀 정의되지 않았거나 서버 인증서와 작동할 수 있는 수신기가 없는 경우
## 컴퓨터 호스트 이름을 Subject로 사용하여 새로운 수신기를 만듭니다.
## 그리고 서버 인증서에 바인딩합니다.
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)와 함께 Ansible을 위해 WinRm을 허용하십시오.

인증서를 매핑하기 위해 로컬 계정을 사용하는 경우, UAC가 방해하지 않도록 LocalAccountTokenFilterPolicy를 1로 설정해야 합니다. 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을 엽니다.

HTTPS를 통한 WinRm은 포트 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 및 인증서 기반 인증을 성공적으로 구성한 것입니다!

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