Sunday, February 10, 2013

PowerShell Remoting – The Double Hop Problem And A Solution

I’ve been doing quite a bit of work lately with remoting – running scripts and script blocks on other machines. As part of my series on developing a Hyper-V VM lab, I’ve scripted the installation and configuration of a mini network. One of the patterns I am using to do most of the VM configuration work is defining a script block (on one machine), and running it in the target VM. In the development of the scripts, I kept falling over errors due to what we call the double-hop problem.
What is the Double Hop Problem?
In remoting, a user on one machine (e.g. Win8.Cookham.Net – my laptop) uses Invoke-Command to run a script block in a VM (e.g. I want to run block on server SRV1.Reskit.Org). Cookham.net is my home network, complete with DC, etc., while Reskit.Org is my test lab domain/network. For most configuration, this works fine, but in some cases it doesn’t. When I run a script block on SRV1, I do it by using Invoke-Command, and specify my (Reskit) domain administrator credentials.
The double-hop problem occurs when my target machine, i.e SRV1, needs to go to another machine  for something. For example, running Get-Certificate in a script block on SRV1 requires SRV1 to go off to DC1 to get the appropriate X.509 certificate. This second hop is where the problem lies.
When the second hop is attempted, SRV1 by default uses the credentials of the PowerShell process running on SRV1, NOT your user credentials. The problem is that those credentials are not likely to have (and in my case did NOT have) sufficient privileges to carry out the necessary action (i.e. getting the certificate from the CA on DC1). 
This problem is widely known about and the solution is the Credential Security Support Provider, also known as CredSSP. CredSSP was added to Windows as part of Windows Vista/Server 2008, and is leveraged by PowerShell. As should be obvious, CredSSP is key component of Single Sign On (SSO) as well as being rather useful in my VM building scenario.
The Solution – CredSSP
With CredSSP, you pass explicit credentials on the initial hop (from Win8 into SRV1), and when SRV1 needs to go to DC1, it uses those same credentials. And if you configure DC1 and other servers correctly, you can in theory go hopping further!
In order to make use of CredSSP, you need to enable CredSSP on both client and server systems, then explicitly specify you want to use CredSSP when you run the Invoke-Command (or Enter-PSSession) cmdlet. In my case, I could conceivably run a script block against any of the servers in my VM farm which could in theory double hop to any other machine in my farm. Since all my VMs could in theory be both client and server, I run the following cmdlets on all the servers:
Enable-WSManCredSSP -Role Client -DelegateComputer '*.reskit.org' –Force Enable-WSManCredSSP -Role Server –Force
What Does This Do To My Host?
Using Enable-WsManCredSSP and enabling the client role does two things. First it sets the
WS-Management setting WSMan:\localhost\Client\Auth\CredSSP to true. Second, it sets a local policy, Allow delegating fresh Credentials (and updates that policy with the list of servers you are going to use delegation with). The server list can be a single server,  a set of servers, or a wildcard set of servers. In the above example, I am going to allow the client to delegate credentials to any server in the Reskit.Org domain. These two settings allow the local client to negotiate the use of CredSSP when creating the session on the remote machine.
Using Enable-WsManCredSSP and enabling the server role does just one things – configuring the
WS-Management setting WSMan:\localhost\Service\Auth\CredSSP to true. This allows the WinRM Service on the remote machine to use the credentials in the second hop.
What about Group Policy?
Whilst researching this issue, I came across several web pages that talked about setting up Group Policy to enable CredSSP. And for a large environment, it might be appropriate to do that. However, just using Enable-WsManCredSSP does all you need. There is one small gotcha that I kept running into. When you enable the client role, as I note above, the Enable-WsManCredSSP cmdlet sets a local policy. The one thing I kept hitting is that while the policy is set by using the cmdlet, it takes a GP refresh on the client in order for the client to be able to use CredSSP against the computers in the DelegateComputer List.
To get around this, in my configuration scripts, I just set the client/server roles (remember in my test lab, any computer in theory can be involved in a 2nd hop with any other computer) on each system, then I force a GPUpdate (or do a reboot) which means after the refresh/reboot, the policy is in force!

3 comments:

Don Jones said...

The reason to do this via GPO in a production environment relates to the potential security issue you didn't address. CredSSP allows you to loan your car keys to your kid... and allows your kid to loan them to someone else! Obviously, you want to exercise some control over who can re-loan your keys (credentials) like that. In your example, you wisely limited it to your domain (*.reskit.org), which is a very good practice. GPO-based configuration lets an organization enforce that centrally. It's not too hard to set up, either - I have a screenshot walkthrough in "Secrets of PowerShell Remoting" at PowerShellBooks.com.

Don Jones said...

The reason to do this via GPO in a production environment relates to the potential security issue you didn't address. CredSSP allows you to loan your car keys to your kid... and allows your kid to loan them to someone else! Obviously, you want to exercise some control over who can re-loan your keys (credentials) like that. In your example, you wisely limited it to your domain (*.reskit.org), which is a very good practice. GPO-based configuration lets an organization enforce that centrally. It's not too hard to set up, either - I have a screenshot walkthrough in "Secrets of PowerShell Remoting" at PowerShellBooks.com.

midstoveilan said...

The module you are referring to is available in ps version 2.0 as well. It is not limited to v3+. You might want to even check back to v1 and then update to be more accurate.