Powershell remoting using SSL and credSSP

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.

  1. Enable WinRM on the server, configure authentication and set up a HTTPS listener
  2. Allow incoming connections on 5986 (the default WinRM https port)
  3. Add a certificate to the WinRM service
  4. Add a certificate to the WinRM listener
  5. Enable credSSP role for the server
  6. 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.
        $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
        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
        Set-Acl $pathtoactualkey $acl
        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.


Manipulating your PATH variable using Powershell

The PATH variable in windows is something you occasionally have to touch.

The classic command for manipulating PATH is simply

set PATH=%PATH%;x:\your\folder\here

Now this does the trick, and you don’t have to do a lot of work to get powershell to do the same thing, the command is simply

$env:Path = $env:Path+";x:\your\folder\here"

The only problem is that the $env:Path variable is only a temporary variable that is available in your current session. This won’t do if you want to make your changes persistent. In addition, I found making the PATH variable a bit more readable helped when dealing with it.

What we will be doing is leveraging the setx command to make the changes permanent.


function get-path
    $env:Path -split (";")

function add-path($folder)
    $addtopath = ";"+$folder
    $env:Path = $env:Path + $addtopath
    $setpath = "setx Path ""$env:Path"" -m"
    Invoke-Expression $setpath

function remove-path($folder)
    $removepath = ";"+$folder
    $env:Path = $env:Path.Replace($removepath,$NULL)
    $setpath = "setx Path ""$env:Path"" -m"
    Invoke-Expression  $setpath

This is the simplest way I could come up with for editing the path variable through powershell and making the changes permanent. This module will throw an error if you don’t have administrative privileges, as those are required for editing the registry, however even in a non-elevated session it will still change your local path variable for that session.

get-path simply displays the path variable in your current session in a comfortable way.

There is a more elegant way of doing this, but I like the simplicity of this one. If you’re looking for a pure powershell implementation though, here’s a link to a great article on Technet