Monday, 15 November 2010

Encrypting PowerShell credential passwords

So continuing from my last post, after getting my monitoring script to work, I set about trying to introduce some semblance of security to at least partially hide the plain text password held within the script.

My initial success was through the use of the ConvertTo-SecureString and ConvertFrom-SecureString cmdlets. Essentially (or least as far as I understand it), the first of these converts some form of password string into some kind of system value, eg if you try simply outputting the contents of $password in the first script, it simply returns something like 'Secure-String', rather than a representation of the password itself. The second cmdlet converts that system value into an actual string value, though it's encrypted rather than the original ascii text as we'll see shortly.

So, my first task was to get hold of that encrypted version of the string, so I could then put that direct into the script, and use it in place of the plain text password. Using the following code I encrypted the password and then outputted it into a text file.

$password = "<password>"
$secure = ConvertTo-SecureString $password -force -asPlainText
$bytes = ConvertFrom-SecureString $secure
$bytes | out-file c:\batchscripts\securepassword.txt

Extracting the resulting string from the text file, I added it into my script, and then adjusted the ConvertTo-SecureString line to receive an encrypted string rather than a plaintext one as you can see below.

$encrypted = "01000000d0818c7a00c04fc297eb0100005facd6ca6de9cea76f00d66edd9b000200000ad8d083414008862f428071c6ae0100e0000bc55db88fe323270008f8719303e96f00000a81f1800000003660000a8000000100000009b2d8ff163728442ad93544b7dccad8500000000048000c9ddf0115d1100a000e0045dfadc703191efaac7e6000490ad18f2ced20db2ccabac0000b2b660bd2a75dc73ba7a278f448"
$username = "<username>"
$password = ConvertTo-SecureString -string $encrypted
$myCred = New-Object System.Management.Automation.PSCredential $username, $password
if (test-connection -computername <computername> -source <source> -Credential $myCred -Quiet)
{"1";exit "1"}
else {"2";exit "2"}

To my relief this then worked, exactly like the original script! Filled with joy (ok, maybe not joy, but certainly pleased with myself), I updated the monitoring system to use the new script, set it running, and can you guess what happened? Yep, it failed. I went through all the settings, they were fine. I switched back to using the old script, it worked again.

After scratching my head for a while I had an idea, possibly a crazy one, but worth a try. I set the monitoring system to execute the script that generates the encrypted password so I could see what the output would look like. It was completely different to the one outputted direct on the server! I re-ran the server version, and sure enough, it produced the same output as it did the first time.

Clearly something about the encryption process used in ConvertTo-SecureString is dependant on where it is run. I updated the script with the new string, set the monitoring system going again, and finally it worked!

So the lesson here is that if you try this yourself, what ever system you are using to call your script, you must also get it to call the encryption script as well, and use THAT output to populate your monitoring script with its password.

Friday, 12 November 2010

Remote connection monitoring with PowerShell and IPMonitor

Recently I've been trying to find a way to use a combination of PowerShell and our monitoring system IPMonitor to check connections between two sites.

Picture the scene, three different sites, A, B and C. At site A we have a monitoring system to check various aspects of the services we look after, sites B and C contain various servers, with VPN links joining A to B, A to C and B to C. From A I can check that the VPN links to B and C are up, but what about the link between B and C? That's where I figured PowerShell could come in handy.

After a bit of searching I found the test-command cmdlet which not only allows you to test a connection like you would with ping, but also to specify the source server to test from. After a little tinkering I came up with the following :

$username = ""
$password = ConvertTo-SecureString "" -AsPlainText -Force
$myCred = New-Object System.Management.Automation.PSCredential $username, $password
if (test-connection -computername -source -Credential $myCred -Quiet)
else {"2"}

As you can see, running the script outputs either 1 or 2 depending on the result of the test. Within IPMonitor I then used an External Process Monitor to call the script, compare the script output with the test value (1 obviously), and then display if the connection is up.

The final piece to the puzzle was making the whole thing at least a bit more secure. For the terminally paranoid, having a script sat there containing a server password doesn't really fill you with joy, so I managed to encrypt the password and use that instead. As it turned out though, doing that in conjunction with IPMonitor turned out to be harder than expected, but I'll go into details on that later.