Distributing SSH User Keys via PowerShell

Page content

Folks in the Linux world are used to moving SSH keys to and from systems enabling password-less authentication. Let’s take a minute to look at what it takes to use PowerShell to distribute SSH user keys to remote systems.

In the OpenSSH package there’s a command ssh-copy-id which is a bash script that copies a user’s public key to a remote system. There’s a little intelligence in the script to set things up properly on the remote system for password-less key based authentication. If the appropriate directory and key file aren’t set up, ssh-copy-id will create the directory and key file with the correct permissions on remote system. As far as I can tell, ssh-copy-id has not been implemented in the Win32-OpenSSH port. So that leaves us with implementing this functionality ourselves, in PowerShell.

Since ssh-copy-id isn’t implemented on the OpenSSH port for Windows (because it’s a bash script), I wanted to replicate that functionality so that I could easily copy ssh user keys to systems, consistently and easily. So I implemented this functionality as PowerShell. 

Let’s walk though this…and first up, let’s discuss what’s needed for password-less, key based authentication.

The Components of Password-less Key Based Authentication

For password-less key based authentication to work, you need to copy the user’s public key from the local system you want to authenticate from, to the remote system. On the remote system, this key file has to live in a place where the SSH deamon expects it, and that’s in the file ~./ssh/authorized_keys by default.

Let’s take a second to look at the details of how this needs to be configured on a remote system.

  • authorized_keys – this is the default file in which user public keys are stored. The permissions on this file should be 600. Which is read/write for the owner and no access to group or other/world.

-rw-r–r–. 1 demo demo 412 Feb 18 08:53 .ssh/authorized_keys 


  • ~./ssh – the authorized_keys file lives in a hidden directory in your home directory. That’s what that syntax means, the ~ (tilde) is short for the current user’s home directory and that . (dot) indicates that the directory is a hidden directory. Now, the permissions on this directory should be 700, this means it’s it’s read/write/execute to the owner and no access to group or other/world. The execute bit on a directory gives you access to list the contents of the directory and enter that directory.

drwx——. 2 demo  demo         29 Feb 18 08:53 .ssh

It’s kinda like ssh-copy-id, but in PowerShell

First up, I’m assuming that you have SSH remoting already configured, have generated your ssh user key and that you’re on a Windows, Linux/Mac system and you want to copy and SSH user key to a Linux/Mac system. I plan on covering copying keys to Windows systems in an upcoming post. The only real difference between the two is how you set permissions on the .ssh directory and the authorized_keys file. 
 
The first thing that we want to do is to create a PSSession to our host. We’ll reuse this session a few times to execute the required commands on the remote host. This demo user is the user we will want to setup key based authentication for. This session creation will ask for our password. Hopefully this is the last time you have to type it ;)
 

$s New-PSSession -HostName “172.16.94.10” -UserName demo


Then, we’ll read in our public key from our local system into a variable. It’s imperative that you read the public key, id_rsa.pub. The other file, id_rsa is your private key. That needs to stay on the system you want to authenticate from and needs to stay secure.

$key Get-Content -Path ~/.ssh/id_rsa.pub


Next, we’ll want to check to see if the .ssh directory exists in the home directory of our user on the remove system. If not, create the .ssh directory.

Invoke-Command -Session $s -ScriptBlock { If(!(Test-Path -Path ~./ssh)) { New-Item ~/.ssh -ItemType Directory} } 

 
Now, with the directory in place, let’s be sure the permissions are set properly, and that’s 700 in octal notation.
 

Invoke-Command -Session $s -ScriptBlock { chmod 700 ~/.ssh  }  

 
After that, we can copy our key to the remote system’s authorized_keys file. We’ll take advantage of the Out-File cmdlet and use the -Append switch to handle file existence on the remote system and append our key to an existing file or create a new file if it doesn’t exist yet. All that fancy syntax around Invoke-Command is so we can pass a local variable into the Out-File cmdlet over our remoting session.
 

Invoke-Command -Session $s -ScriptBlock { param([string] $key) Out-File -FilePath ~/.ssh/authorized_keys -Append -InputObject $key } -Args $key

 
Now, with the file on the remote system, let’s ensure the permissions are set properly.
 

Invoke-Command -Session $s -ScriptBlock { chmod 600 ~/.ssh/authorized_keys  }

 
..and with that let’s take it for a test run and see if we can open a PSSession without a password using Enter-PSSession
 

PS /Users/demo> Enter-PSSession -HostName server1 -UserName demo
[server1]: PS /home/demo> 

 
Now, there’s a few things I want to point out. This code here is to highlight the needed steps to configure key based authentication. I certainly could (and should) make this code more production ready…but I’ll leave that up to you as the reader. What I really want to highlight here are the steps required for proper key distribution to remote systems, such as directories, files and the required permissions. Oh, if you’re like why don’t you just use ssh-copy-id…fan out remoting. We can use this technique to easily distribute our keys to many systems.

I hope this helps you get an understanding of how key based authentication works, how to configure it and also how to get those keys out to your remote systems!