Pages

Tuesday, June 28, 2011

How to SSH from PowerShell using Putty/Plink

Today working with my brother we needed to automate some administration of some Linux VMs for Students. The goal was simple enough, in a PowerShell script create the Linux VMs, Power it on and then SSH into it to configure Linux and create student account on their VM.

Its an odd project that is sort of last minute but we think we can script a solution for the college faculty. One of the assumed easy features of this plan was to SSH into the Linux VM from PowerShell. But surprisingly their didn't a appear to be any easy solutions. The two options available appeared to be:

SharpSSH - I tried to use it but could only get the following error:

Exception getting "formatValueList": "Microsoft.PowerShell.Commands.Internal.Format.FreeFormatEntry.formatValueList"

And /N Software NetCmdlets which is a paid solution i don't have access to and didn't find very good usage on. Lastly I found that it appears best to use plink.exe. Plink comes with putty and is the command line only version of putty. The result being you can pipe your command and through plink however you first have to accept to trust the host key. That a problem because despite all my attempts I couldn't find a command parameter to allow it to accept the host key. Here is the prompt.

C:\Program Files (x86)\PuTTY>plink -ssh username@host -pw password  
The server's host key is not cached in the registry. You ave no guarantee that the server is the computer you think it is.  
The server's rsa2 key fingerprint is:  
ssh-rsa 2048 8e:39:5f:6e:6d:80:77:50:6e:39:b0:c1:13:98:ab:c1 
If you trust this host, enter "y" to add the key to PuTTY's cache and carry on connecting. If you want to carry on connecting just once, without adding the key to the cache, enter "n".
If you do not trust this host, press Return to abandon the connection. 
Store key in cache? (y/n)  

However I worked out a solution, it isn't pretty but it works well.

Using " Echo y " and piping that to the plink it will accept the host key, but keep entering y over and over. The solution is to then pass the command exit to plink which will logout the ssh session the second it gets logged in. With that the host key is cache and trusted. How simply plink again with out the "echo y"

ehco y | plink -ssh username@host -pw password exit

plink -ssh username@host -pw password ls

Below is the PowerShell script that will allow you to pass multiple SSH commands at once. Comment if you have questions or ways to improve this.

test

Function Invoke-SSHCommands {
 Param($Hostname,$Username,$Password, $CommandArray,
  $PlinkAndPath,
  $ConnectOnceToAcceptHostKey = $true)

 $Target = $Username + '@' + $Hostname
 $plinkoptions = "-ssh $Target -pw $Password"

 #Build ssh Commands
 $CommandArray += "exit"
 $remoteCommand = ""
 $CommandArray | % {
  $remoteCommand += [string]::Format('{0}; ', $_) 
 }

 #plist prompts to accept client host key. This section will
 #login and accept the host key then logout.
 if($ConnectOnceToAcceptHostKey)
 {
  $PlinkCommand  = [string]::Format('echo y | & "{0}" {1} exit',
   $PlinkAndPath, $plinkoptions )
  #Write-Host $PlinkCommand
  $msg = Invoke-Expression $PlinkCommand
 }

 #format plist command
 $PlinkCommand = [string]::Format('& "{0}" {1} "{2}"',
  $PlinkAndPath, $plinkoptions , $remoteCommand)

 #ready to run the following command
 #Write-Host $PlinkCommand
 $msg = Invoke-Expression $PlinkCommand
 $msg
}

$PlinkAndPath = "C:\Program Files (x86)\PuTTY\plink.exe"
$Username = "remoteshell"
$Password = "pa$$w0rd"
$Hostname = "Linuxhost"

$Commands = @()
$Commands += "ls"
$Commands += "whoami"

Invoke-SSHCommands -User $Username  `
 -Hostname $Hostname `
 -Password $Password `
 -PlinkAndPath $PlinkAndPath `
 -CommandArray $Command

26 comments:

  1. "Was one one else surprised that Powershell didn't have a better way to do this as I was?"

    - What surprises me is that a 'linux guy' cant figure out a better way ;)

    #####
    # 1) On any of your linux nodes (could also be your windows box in a moment, but lets make it simple) , generate yourself an SSH key. Here is an example of how.

    cantin@sodium:~> ssh-keygen -t rsa
    Generating public/private rsa key pair.
    Enter file in which to save the key (/home/cantin/.ssh/id_rsa):
    Enter passphrase (empty for no passphrase):
    Enter same passphrase again:
    Your identification has been saved in /home/cantin/.ssh/id_rsa.
    Your public key has been saved in /home/cantin/.ssh/id_rsa.pub.

    - The one thing they don't show is creation of the authorized_keys file which is simple at this point. You need this file to tell each linux box which key(s) you trust as authorization rather than the users password, during ssh.

    - I would run these two commands (should work without modification if your output files from key creation are named similarly).

    cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

    chmod 600 ~/.ssh/*

    - The first command above (cat) will append (or create new) your public key (hence the .pub extension) into the file authorized_keys . If this file already has one or more keys, this command will not hurt that file, simply add to it. Duplicate entries do not hurt either.

    - The second command (chmod) will set proper permissions on all files in ~/.ssh/* .
    SSH requires the files in ~/.ssh/* to be set to 600 (user only, read/write with no group or other permissions). If it is not set this way, SSH will fail to read any keys/data from this directory and simply prompt you for users password instead.

    When complete, your ~/.ssh/ directory will look like so:

    [deployer@att-prod-deploy-401 DPS-11928]$ ls ~/.ssh -lrth
    total 96K
    -rw------- 1 deployer users 248 Aug 27 2010 id_rsa.pub
    -rw------- 1 deployer users 887 Aug 27 2010 id_rsa
    -rw------- 1 deployer users 248 Aug 27 2010 authorized_keys

    #####
    # 2)
    - Using putty to connect from a windows box is alright, but if you truly need command line for things like piping data from one server to the next via ssh, or passwordless scp, Just install OpenSSH for windows!!

    http://sshwindows.sourceforge.net/download/


    #####
    # 3
    - Once ssh/scp (same package) is installed:
    - Add the installed path\bin to your system path. (C:\Program Files\OpenSSH\bin)
    - ssh will act in powershell, just as it does on your linux boxes (With a few exceptions with remote commands, due to powershell having different escape characters as compared to BASH ,but you'll figure it out)

    - You can even add your ~/.ssh/ files...
    From: Any of the linux VM's (id-rsa , id-rsa.pub , authorized_keys)
    To: widnows box at %USERPROFILE%\.ssh\
    - This will give you passwordless ssh/scp to that node or set of nodes.

    #####
    # 4
    - Launch a new powershell (so that the new path addition is added to this shell session) and type ssh
    ~FIN


    Summary:

    1) Make an ssh key:
    http://rcsg-gsir.imsb-dsgi.nrc-cnrc.gc.ca/documents/internet/node31.html
    - Dont forget authorized_keys and file permissions.
    - Copy tar/gz the directory ~/.ssh/ up , and untar it on the other VM's, same path.

    2) Install OpenSSH for windows:
    http://sshwindows.sourceforge.net/download/
    - There are others, but this is the one I use. Even Cygwin's ssh should work.

    3) Configure Windows box, at %USERPROFILE%\.ssh\ to contain the same files your linux ~/.ssh/ directories have.

    4) Start new PowerShell, then Profit. You have achieved passwordless scp/ssh, usable in PowerShell.

    ?5) At this point, make an alias or some jazz that you can pipe any ssh commands output to for conversion to powershell usable data, as this solution eliminates all the user/password bullsh*t from the script , which is most of the script to begin with.


    - Zhoul : The Old-n-Busted (and quite pertentious, but well meaning) Tech

    ReplyDelete
  2. If you enjoy linux, I can send you a package of "Unix commands, converted to Windows" that I've collected over the years. I have confirmed that 70% of them function in PowerShell, and the remaining 30% I simply don't use frequently.

    Below is the all-awesome list (ssh and scp absent because they are in the openssh\bin directory):


    agrep.exe
    ansi2knr.exe
    basename.exe
    bc.exe
    bison.exe
    bunzip2.exe
    bzip2.exe
    bzip2recover.exe
    cat.exe
    chgrp.exe
    chmod.exe
    chown.exe
    cksum.exe
    cmp.exe
    comm.exe
    compress.exe
    cp.exe
    csplit.exe
    cut.exe
    date.exe
    dc.exe
    dd.exe
    df.exe
    diff.exe
    diff3.exe
    dircolors.exe
    dirname.exe
    du.exe
    echo.exe
    egrep.exe
    env.exe
    expr.exe
    factor.exe
    fgrep.exe
    flex.exe
    fmt.exe
    fold.exe
    fsplit.exe
    gawk.exe
    gclip.exe
    gplay.exe
    grep.exe
    grephelp.bat
    gsar.exe
    gunzip.exe
    gzip.exe
    head.exe
    id.exe
    indent.exe
    install.exe
    join.exe
    jwhois.exe
    less.exe
    lesskey.exe
    libeay32.dll
    libfl.a
    libfl.lib
    libiconv2.dll
    libintl3.dll
    libssl32.dll
    ln.exe
    logname.exe
    ls.exe
    m4.exe
    make.exe
    makedepend.exe
    makemsg.exe
    man.exe
    md5sum.exe
    mkdir.exe
    mkfifo.exe
    mknod.exe
    mv.exe
    mvdir.exe
    nc.exe
    nl.exe
    od.exe
    paste.exe
    patch.exe
    pathchk.exe
    pclip.exe
    pr.exe
    printenv.exe
    printf.exe
    pwd.exe
    recode.exe
    rm.exe
    rman.exe
    rmdir.exe
    sdiff.exe
    sed.exe
    seq.exe
    sh.exe
    shar.exe
    sleep.exe
    split.exe
    stego.exe
    su.exe
    sum.exe
    sync.exe
    tac.exe
    tail.exe
    tar.exe
    tee.exe
    test.exe
    test.txt
    touch.exe
    tr.exe
    type.exe
    uname.exe
    unexpand.exe
    uniq.exe
    unix_find.exe
    unixdate.exe
    unixexpand.exe
    unixfind.exe
    unixsort.exe
    unrar.exe
    unshar.exe
    unzip.exe
    uudecode.exe
    uuencode.exe
    wc.exe
    wget.exe
    wget.GID
    wget.hlp
    which.exe
    whoami.exe
    xargs.exe
    xmllint.exe
    yes.exe
    zcat.exe
    zip.exe

    ReplyDelete
    Replies
    1. Can you send me the command please

      Delete
    2. Hi,
      This list is really interesting and am curious and excited to use these commands from powershell. May I request you to kindly share these commands with me, please?
      Do you have it as a package or individual files? Or do you have a link to your webpage from where we can download these?

      Regards,
      Sreeram

      Delete
  3. (Note: This was supposed to preceed my previous post, but did not post correctly. Attempting again.)


    "Was one one else surprised that Powershell didn't have a better way to do this as I was?"

    - What surprises me is that a 'linux guy' cant figure out a better way ;)

    #####
    # 1) On any of your linux nodes (could also be your windows box in a moment, but lets make it simple) , generate yourself an SSH key. Here is an example of how.

    cantin@sodium:~> ssh-keygen -t rsa
    Generating public/private rsa key pair.
    Enter file in which to save the key (/home/cantin/.ssh/id_rsa):
    Enter passphrase (empty for no passphrase):
    Enter same passphrase again:
    Your identification has been saved in /home/cantin/.ssh/id_rsa.
    Your public key has been saved in /home/cantin/.ssh/id_rsa.pub.

    - The one thing they don't show is creation of the authorized_keys file which is simple at this point. You need this file to tell each linux box which key(s) you trust as authorization rather than the users password, during ssh.

    - I would run these two commands (should work without modification if your output files from key creation are named similarly).

    cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

    chmod 600 ~/.ssh/*

    - The first command above (cat) will append (or create new) your public key (hence the .pub extension) into the file authorized_keys . If this file already has one or more keys, this command will not hurt that file, simply add to it. Duplicate entries do not hurt either.

    - The second command (chmod) will set proper permissions on all files in ~/.ssh/* .
    SSH requires the files in ~/.ssh/* to be set to 600 (user only, read/write with no group or other permissions). If it is not set this way, SSH will fail to read any keys/data from this directory and simply prompt you for users password instead.

    When complete, your ~/.ssh/ directory will look like so:

    [deployer@att-prod-deploy-401 DPS-11928]$ ls ~/.ssh -lrth
    total 96K
    -rw------- 1 deployer users 248 Aug 27 2010 id_rsa.pub
    -rw------- 1 deployer users 887 Aug 27 2010 id_rsa
    -rw------- 1 deployer users 248 Aug 27 2010 authorized_keys

    #####
    # 2)
    - Using putty to connect from a windows box is alright, but if you truly need command line for things like piping data from one server to the next via ssh, or passwordless scp, Just install OpenSSH for windows!!

    http://sshwindows.sourceforge.net/download/


    #####
    # 3
    - Once ssh/scp (same package) is installed:
    - Add the installed path\bin to your system path. (C:\Program Files\OpenSSH\bin)
    - ssh will act in powershell, just as it does on your linux boxes (With a few exceptions with remote commands, due to powershell having different escape characters as compared to BASH ,but you'll figure it out)

    - You can even add your ~/.ssh/ files...
    From: Any of the linux VM's (id-rsa , id-rsa.pub , authorized_keys)
    To: widnows box at %USERPROFILE%\.ssh\
    - This will give you passwordless ssh/scp to that node or set of nodes.

    #####
    # 4
    - Launch a new powershell (so that the new path addition is added to this shell session) and type ssh
    ~FIN

    ReplyDelete
  4. (Part 2 of the above comment)


    Summary:

    1) Make an ssh key:
    http://rcsg-gsir.imsb-dsgi.nrc-cnrc.gc.ca/documents/internet/node31.html
    - Dont forget authorized_keys and file permissions.
    - Copy tar/gz the directory ~/.ssh/ up , and untar it on the other VM's, same path.

    2) Install OpenSSH for windows:
    http://sshwindows.sourceforge.net/download/
    - There are others, but this is the one I use. Even Cygwin's ssh should work.

    3) Configure Windows box, at %USERPROFILE%\.ssh\ to contain the same files your linux ~/.ssh/ directories have.

    4) Start new PowerShell, then Profit. You have achieved passwordless scp/ssh, usable in PowerShell.

    ?5) At this point, make an alias or some jazz that you can pipe any ssh commands output to for conversion to powershell usable data, as this solution eliminates all the user/password bullsh*t from the script , which is most of the script to begin with.


    - Zhoul - The Old-n-Busted (and quite pertentious) Tech

    ReplyDelete
  5. You can alway install msysgit

    ReplyDelete
  6. I should note that this Powershell code needed be run from many VMs. For myself I didn't want to have additional software dependencies.

    ReplyDelete
    Replies
    1. Chris,
      You do have a dependency either on Putty/Plink or OpenSSH, don't you?
      Unless you do run Putty/Plink from a file share ;-)

      To Zhoul:
      authorized_keys logon works pretty much same way for both Putty/Plink and OpenSSH.
      I guess, you can pipe in and out too.
      What is the OpenSSH advantage than?

      Delete
    2. Chris,
      You can bypass:
      echo y | plink -ssh username@host -pw password exit
      if the key exists already.
      Better have it thought, because host key might have been changed for whatever reason ;-(
      http://www.xinotes.org/notes/note/156/

      Delete
    3. I do have an dependency on Putty/plink. I however it should work on a remote share just fine.

      As for the Host Key the section "#plist prompts to accept client host key." will accept the host key no matter what is in case I haven't connected to that machine before if its Key has changed.

      Delete
  7. YOU ROCK!!!

    echo y | plink -ssh username@host -pw password exit

    That command is not elegant but its easy & IT WORKS on my datadomains.
    Thank you for ending my GOD DAMNED headache!!!

    ReplyDelete
  8. Hi, Thanks for the guide. would like to know how can i execute a .sh file using the script?

    ReplyDelete
  9. Hello Mr. Towles,

    I just started a number of automation projects, and this script was instrumental in getting them off the ground. Thanks for sharing it, it's been very useful and much appreciated.

    ReplyDelete
  10. Brilliant! Thanks for the advice!

    ReplyDelete
  11. This is a very nice post for all those who are working with power cli. I was wandering where to add the power shell script given at the end, to work with multiple commands at a time. Please anyone let me know where should i keep it.

    Thanks All

    ReplyDelete
  12. I've said Jimminy Jillekers so often- the words have lost all meaning!!!

    ReplyDelete
  13. I wrote an SSH-Sessions module for executing commands via SSH from PowerShell, based on the SSH.NET library found on CodePlex. It sports New-SshSession, Invoke-SshCommand and even an Enter-SshSession. Pretty cool stuff. Check it out at: http://www.powershelladmin.com/wiki/SSH_from_PowerShell_using_the_SSH.NET_library

    ReplyDelete
  14. Hello everyone and thanks for the great script! it gets the job done! Only one question, why isn't the output written in powershell command line until the end of all operations?

    I would really like to see whats happening in real time as if i was connected with putty.

    Thanks!1

    ReplyDelete
  15. Have a look at HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys, you can pre-populate that with the appropriate value for your Linux server. IOW, connect on one machine, make a note of the values, then you can plonk that straight into HKCU as the first step in your Powershell script, instead of the echo y | plink command. Ultimately it's no different, except that it saves you from a (probably extremely unlikely) MITM attack.

    ReplyDelete
  16. This isn't working for me. When I run it in Powershell ISE it just seems to hang, the connection via Plink doesn't exit. Simplifing things I ran this command (with the corrent login id/password)

    Invoke-Expression -Command "plink -ssh id@IHRDEV -pw password -V"

    and it returns: "Using keyboard-interactive authentication." It never continues...

    Yet if I run the same plink -ssh id@IHRDEV -pw password -V at a DOS prompt, it runs fine and exits.

    Any ideas?

    thanks,
    Bruce

    ReplyDelete
  17. Awesome! Line 49 should be "CommandArray $Commands"

    ReplyDelete
  18. Really great script, thank you Chris

    ReplyDelete
  19. Thanks, it helped me a lot!

    ReplyDelete
  20. This is the closest I've found to a CSSH type bit for powershell. However this still fails. Thanks to special characters in my passwords. I can connect via plink if I have the password in double quotes (") but I haven't been able to update the plinkoptions to pass the password with quotes in tact. Ideas?

    ReplyDelete

Please leave a comment; someone, anyone!