There is a lot of excellent articles on the internet about Powershell remoting, and I can only assume interest in the feature is going to increase as windows administrators move to automating as much of their work as possible.
While I may have missed it, though, I don’t think I ever saw a good breakdown in a single article on the steps required to get your environment ready to handle ssl encrypted credSSP capable sessions.
I am, as of writing this article, working on a module that simplifies this entire process, basically doing it for you, but it’s not entirely production ready, and I think understanding the steps required is interesting anyway. These are the steps I’m going to take to reach my goal of secure credSSP capable sessions.
- Enable WinRM on the server, configure authentication and set up a HTTPS listener
- Allow incoming connections on 5986 (the default WinRM https port)
- Add a certificate to the WinRM service
- Add a certificate to the WinRM listener
- Enable credSSP role for the server
- Enable credSSP role for the client
Powershell remoting runs on top of winrm, and to my best knowledge, not every winrm command has been implemented as a powershell CmdLet, however things start off nicely with the Set-WSManQuickConfig CmdLet. The first few steps of this guide must be run on the server.
Set-WSManQuickConfig -UseSSL
WinRM Quick Configuration
Running the Set-WSManQuickConfig command has significant security implications, as it enables remote management through
the WinRM service on this computer.
This command:
1. Checks whether the WinRM service is running. If the WinRM service is not running, the service is started.
2. Sets the WinRM service startup type to automatic.
3. Creates a listener to accept requests on any IP address. By default, the transport is HTTP.
4. Enables a firewall exception for WS-Management traffic.
5. Enables Kerberos and Negotiate service authentication.
Do you want to enable remote management through the WinRM service on this computer?
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"):
This takes care of the first two steps for us, WinRM is now up and running on the server, and we don’t have to toy around with firewall rules as the CmdLet takes care of creating one for us.
The next step is to add the required certificates. By default Powershell will expect a certificate whose name matches the server’s name, but this is not necessary. It is entirely up to you whether you create a server specific certificate, or just a generic one to distribute to servers on your domain.
Very important detail of adding your certificate to the WinRM service, is that it must both be present in the WinRM service configuration, and in the HTTPS listener configuration. Another important detail is that the default system account NETWORK SERVICE must have read access to the certificate’s private key.
I have created a simple function that should take care of all this for you, with only the CN of the certificate provided.
function Add-SSLCredential($CN)
{
#First thing is to locate the certificate by CN. Make sure we get the most recent one.
try
{
$certificate = Get-ChildItem CERT:\LocalMachine\My | Where-Object {$_.Subject -match $CN} | sort $_.NotAfter -Descending | select -first 1 -erroraction STOP
$thumbprint = $certificate.Thumbprint
$UKCN = $certificate.PrivateKey.CspKeyContainerInfo.UniqueKeyContainerName
}
catch
{
Write-Output "Error: cannot find the certificate"
}
#Then we check if the HTTPS listener exists, it probably does if you're following this post
#That's okay though, we delete it and set up a new listener with our Certificate.
$checkconfig = winrm e winrm/config/listener
if($checkconfig -contains " Transport = HTTPS")
{
Write-Host -ForegroundColor Yellow "1. Delete old config"
winrm delete winrm/config/Listener?Address=*+Transport=HTTPS
}
Write-Host -ForegroundColor Yellow "2. Add a certificate to the listener"
winrm create winrm/config/listener?Address=*+Transport=HTTPS `@`{Hostname=`"$CN`"`; CertificateThumbprint=`"$thumbprint`"`}
#Then we add the same certificate to the winrm service
Write-Host -ForegroundColor Yellow "3. Add certificate to the winrm service"
winrm set winrm/config/service `@`{CertificateThumbprint=`"$thumbprint`"`}
#And finally, we make sure the NETWORK SERVICE account has access to the private key of the certificate
Write-Host -ForegroundColor Yellow "4. Allow the Network Service access to the certificate"
$machinekyepath = "$env:SystemDrive\ProgramData\Microsoft\Crypto\RSA\MachineKeys\"
$pathtoactualkey = $machinekyepath+$UKCN
$acl = Get-Acl -Path $pathtoactualkey
$permission="NETWORK SERVICE","Read","Allow"
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule $permission
$acl.AddAccessRule($accessRule)
Try
{
Set-Acl $pathtoactualkey $acl
}
Catch
{
Write-Output "Error: unable to set ACL on certificate"
}
}
Finally, you have to allow the server to receive credentials through credSSP authentication. This is quite simple.
Enable-WSManCredSSP -Role Server
Now we’re all done with the server. The client configuration is much more simple. All we have to do is make sure we allow our credentials to be delegated to the server.
Enable-WSManCredSSP -Role Client -DelegateComputer you.server.here
Note that you can add wildcards too, so you could specify the -DelegateComputer to be *.domain.local if your domain is called domain.local and thus enable credSSP delegation to all computers in the domain.
When the configuration is all done there’s nothing stopping us from entering a new and improved PSSession
Enter-PSSession server.domain.local -UseSSL -Cred $credential -authentication CredSSP
And as promised, if the certificate name doesn’t match the server’s name, or in case you’re using a self-signed certificate, all you have to do is append -SessionOption(New-PSSessionOption -skipcnckeck -skipcacheck) to the command above. The flags will skip the certificate name check and the certificate authority check respectively.
I hope this post isn’t too long and can be useful for someone wanting to configure their environment to use secure credSSP enabled sessions.