Summary: Microsoft Scripting Guy, Ed Wilson, talks about the dreaded “second-hop” problem with Windows PowerShell and how to overcome it with CredSSP.
Microsoft Scripting Guy, Ed Wilson, is here.
First, a description of the problem
In yesterday’s article, I talked about using the Windows Update module to update drivers, patches, etc. on my local computer. This worked really great.
Note For more information about the Windows Update module and using it to update a local system, see yesterday’s blog post: Use a PowerShell Module to Run Windows Update.
By storing the module in a centralized shed location, I should be able to maintain a single location for the module—and, therefore, simplify my maintenance problem of updating dozens of servers every time the module receives an upgrade. In fact, it should be a simple matter to run the module and to update the remote system. I only need to create a remote session, import the module into the session, and run the Get-WUInstall cmdlet (advanced function).
Unfortunately, when I try to run the Invoke-Command cmdlet, I receive a “ResourceUnavailable” error and a “not recognized as the name of a cmdlet” error. These errors are shown here.
When I use Invoke-Command to test the path, I receive an “Access is denied” error. This appears strange because I am running with administrator rights, and I know that I have access through the share and through the NTFS file system permissions. I can test the path from my local computer (as I did yesterday) and it works perfectly. Hmm … I wonder what is going on?
I decide to use Remote Desktop Protocol (RDP) to access my SQL1 server and run the test path from there. (By the way, with Windows PowerShell, all I need to do is type MSTSC to open the Remote Desktop Connection screen).
I open Windows PowerShell from the RDP session on my SQL1 server, and type Test-Path. It resolves and returns True. This is shown here.
PS C:\> Test-Path \\dc1\Share\PSWindowsUpdate
True
I can also import the module from the DC1 share. This command is shown here.
Import-Module -Name \\dc1\Share\PSWindowsUpdate
In addition to importing the module, I can also test out the cmdlets (advanced functions), and I see that they work as well. This is shown in the following image.
So, it is not a name resolution issue because the SQL1 server is definitely able to see the DC1 server. Also, from the SQL1 server I am able to see the share, import the module, and even run commands exposed from the module. I am just not able to do these from my computer, to the SQL1 server, TO the DC1 server—it is the notorious “second hop” problem.
This situation is shown in the figure that follows. Client A enters a remote PS Session onto Server A. Inside the session, the Get-Process cmdlet runs, everything works perfectly, and Client A sees a listing of all the running processes on Server A. Now, from within this remote Windows PowerShell session, Test-Path is used to check the path to a share on Server B. This fails with an Access Denied error. This is due to the second hop, in which the credentials from Client A that were used to enter the remote PS Session are attempted to be carried over to Server B to check the path.
Using CredSSP to solve the second-hop problem
Back in the Windows Vista days, we introduced a new security delegation module called Credential Security Service Provider (CredSSP). This was originally designed to work with Terminal Services because everything in Terminal Services is basically a second hop. What is going on with the second hop? Well, I can remote into my SQL1 server and do things locally—no problem. I use the credentials I specify when I make my connection to the remote server. But when I want my SQL1 server to go off and talk to the DC1 server—that is a second hop. Now where do the credentials for that hop come from? Well, it wants to use the credentials I specified when I connected to SQL1, but that COULD be a problem is SQL1 is a compromised machine because I could take my credentials and go off and do a lot of stuff.
Think of it this way. My dad loans his credit card to me so I can use it to make specifically authorized purchases (perhaps to purchase petrol so I can come home for the holidays). Now my dad trusts me, and he trusts that I will use the credit card for only those specific petrol purchases. This is the first hop, and is permitted in all of our management scenarios. Now, suppose I decide to loan my dad’s credit card to some of my friends. This is not permitted by my dad because he does not know who my friends are or what they are going to do with his credit card.
However, suppose he did know one of my friends. And he wanted to help my friend out and permit him to go home for the holidays as well. Then, in that case, he would specifically permit a second hop for a particular person and for a particular situation—this is where CredSSP comes into play.
Note For more information about CredSSP, read this excellent article on MSDN.
I need to make two changes. On my client workstation, I need to use the Enable-WSManCredSSP cmdlet to enable the client role and then specify the computer to which I want to delegate my credentials. This command is shown here.
Enable-WSManCredSSP -Role Client -DelegateComputer *.iammred.net -Force
Now, I also need to make a change on the remote server to permit it to use delegated credentials. This command is shown here.
Enable-WSMaCredSSP -Role Server –Force
The use of these two commands is shown in the image that follows.
Making the second hop
I can now use CredSSP to make the second hop, and therefore, to use the centralized module. There are, however, two very important things to keep in mind when making the connections.
- When making the PS Session, ensure that I use –Authentication CredSSP.
- When making the PS Session, ensure that I use the FQDN of the remote server. (This is true because I specified the delegate computer as *.mydomain.net. If I had specified the delegate computer as Sql1.mydomain.net, I would not need the FQDN.)
My use CredSSP script is shown here.
$credential = Get-Credential -Credential iammred\administrator
$session = New-PSSession -cn SQL1.Iammred.Net -Credential $credential -Authentication Credssp
Invoke-Command -Session $session -ScriptBlock {Test-Path \\dc1\share\PSWindowsUpdate}
Invoke-Command -Session $session -ScriptBlock {
Import-Module -Name \\dc1\Share\PSWindowsUpdate }
Invoke-Command -Session $session -ScriptBlock {Get-WUHistory }
Join me tomorrow when I will have a guest blog article written by Microsoft MVP Don Jones as he excerpts his new “Learn Windows PowerShell 3 in a Month of Lunches” book. The blog post is called “Digging into PowerShell Remote Authentication.” A very good post indeed—you will NOT want to miss it.
I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
Ed Wilson, Microsoft Scripting Guy
0 comments