Powershell Everywhere
Note: This post requires some editing. I need to write a separate article for setting up SSH since it's useful for other purposes, and also because the instructions as I originally wrote them were terrible from a security perspective on setups other than my own where every system was a virtual machine that is only accessible through the physical system.
One of the major challanges of developing cross-platform software is automating the build process across all platforms. While setting my system up after a wipe, I ended up divising a strategy to automate the build process across three platforms, macOS, Windows, and Fedora (Linux). Using this setup, I can build my code on all three systems with a single script.
The foundation of this setup is using PowerShell over OpenSSH. With the release of Windows 10 April 2018 update (1803), OpenSSH is included with Windows 10, and (mostly) ready to run out of the box, and with the release of PowerShell Core 6.0, PowerShell is available for all three of Windows, macOS, and Linux, and supports remote access over SSH.
For this setup to work, assigning fixed IP addresses to each system is essential. This can be accomplished either by setting a static IP address in each system, or by assigning a fixed address to the system with your DHCP server. Having a DNS server setup also helps, so that you can access each system by a hostname rather than its IP address. The specifics of the infrastructure process could fill another complete article. I might write an article on my particular setup in the future.
There are three or four steps to getting this process going:
- Install and Enable sshd
- Install PowerShell Core 6 or later (Even on Windows; No, Windows PowerShell 5.1 won't work.)
- Exchange OpenSSH keys between system
- Configure sshd for PowerShell
Installing sshd on Windows
A default installation of Windows 10 (1803) includes the OpenSSH client software, ready to run, out of the box. This allows Windows to connect to other systems via SSH, but in order for other systems to connect to the Windows system, the system will need sshd installed. In previous editions, this was a complicated process, as OpenSSH on Windows was still in beta, but now, to get sshd working on Windows only requires two steps now: installing it, and enabling it.
To download and install sshd on Windows, from the Start menu open Settings. In Settings window, select Apps. In the Apps & features window, select Manage Optional Features. In the Manage Optional features Window, select Add a Feature. In the Add a feature Window, select OpenSSH Server.
To enable the OpenSSH server, from the Start menu, under Windows Administrative Tools, select Services. In the Services window, find the item "OpenSSH SSH Server", right click on it, and select start. After the service starts up, right click on the OpenSSH SSH Server, item again, and select properties. In the Properties window, ensure that Startup Type is set to Automatic, and click OK. The OpenSSH server (sshd) should be running now, and will start automatically whenever you restart your system.
Enabling sshd On macOS
To enable sshd on macOS, open the Apple menu and select System Preferences. Under System Preferences, select sharing. Enable Remote Login, and close the System Preferences window.
Enabling sshd on Linux
Most Linux distributions, including Fedora, enable sshd on a default installation.
Installing PowerShell Core
Installing PowerShell Core is a straightforward process. Simply download and run the installer. Windows PowerShell 5.1 doesn't support remote access over SSH, so Windows will still need PowerShell Core (6.0 or later) installed. PowerShell Core can be downloaded from the PowerShell release page on GitHub. These instructions were tested with PowerShell Core 6.0.4. Later versions, once released, should work without a problem. PowerShell will be installed in the following locations, depending on your system. This location will be important when configuring sshd for PowerShell.
- Linux:
/usr/bin/pwsh
- macOS:
/usr/local/bin/pwsh
- Windows (PowerShell Core 6.0.4):
C:\Program Files\PowerShell\6.0.4\pwsh.exe
- Windows (PowerShell Core 6.1.x):
C:\Program Files\PowerShell\6\pwsh.exe
- Windows (PowerShell 7):
C:\Program Files\PowerShell\7\pwsh.exe
Exchanging SSH keys between systems
OpenSSH uses public and private encryption keys to identify and authenticate remote servers, and can use them to authenticate users. When OpenSSH connects to a server, the remote host sends its public key to the local client. The local client then checks a list of known hosts to see if it knows the host to which it is connecting, and if it does, to see if the remote host is using the right key. If there is no match in the list for the remote host, the ssh client will ask the user if it should add the remote host to the list.
Additionally, SSH users can use keys to authenticate themselves during a connection. When user keys are properly setup, Each user on each system has their own public and private keys, and a list of the public keys of users on other systems allowed to login as them. Using key authentication for users is essential for remote scripting as it allows the user to authenticate without a password.
SSH stores users' files in their home directory in a directory called .ssh
. There are four relevent files there.
known_hosts
: The list of remote hosts to which the user has connected before, and their public keysid_rsa
: The user's private keyid_rsa.pub
: The user's public keyauthorized_keys
: The user's list of remote users' keys authorized to login as them
Known Hosts
The easiest way to set up the known_hosts
file is to connect to each of your systems, including aliases, from one of your systems. Using a normal ssh
command. This includes accessing the local system itself through ssh. For example, if you have a system at IP address 192.168.1.5
, with the name cherry
, and another system at 192.168.1.6 with the name orange
, and an alias of www
, you would want to use ssh to connect to each one like this, where user is your username on the remote system.
ssh user@192.168.1.5
ssh user@cherry
ssh user@192.168.1.6
ssh user@orange
ssh user@www
Each time you make a connection, you will be prompted to verify the host's fingerprint. Type yes to confirm the fingerprint, then disconnect from the remote system by typing exit
and hitting enter. When you have finished with the last, you will need to copy your known_hosts
file to each system. Before you can do that, you will need to ensure that the .ssh
directory exists. Use one of the following commands, depending on the operating system of the remote system, and ignore any messages saying that the directory already exists.
- Windows:
ssh user@host "mkdir %USERPROFILE%\.ssh"
- Linux and macOS:
ssh user@host "mkdir ~/.ssh"
After the .ssh
directory is made, you can copy the known_hosts
file using the scp
command.
- Windows (with PowerShell):
scp $Env:USERPROFILE/.ssh/known_hosts user@host:~/.ssh
- Linux and macOS:
scp ~/.ssh/known_hosts user@host:~/.ssh
Users' Public Keys
You will need to generate a public and private key pair on the system you will use as your main workstation, or one for each system your will use to access others. You can generate each key pair with the following command on each system, and taking the default values offered:
- Generate a key on the local system:
ssh-keygen
- Generate a key on a remote system:
ssh user@host ssh-keygen
After you have each key pair generated, you will need to copy them all to one system, and generate an authorized_keys
file. Use this command to copy a remote user's public key:
scp user@host:.ssh/id_rsa.pub host.pub
When you have downloaded every user's public key, you can combine them into an authorized_keys file with this command:
Get-Content *.pub | Set-Content authorized_keys
You can then use scp to install the authorized_keys file in each system you wish to remotely access with this command:
scp authorized_keys user@host:.ssh
When that's complete, all that's left is to enable the PowerShell subsystem in sshd.
Configuring sshd For PowerShell
While other shells simply run when the user connects to a system with SSH, PowerShell includes its own remote-access system with support for remote-access construct in the language itself. For this to work, you will need to enable a subsystem in the configuration file for sshd. Each system has a file named sshd_config
at one of the following locations, depending on operating system:
- Windows:
C:\ProgramData\ssh\sshd_config
- Linux and macOS:
/etc/ssh/sshd_config
On Windows, PowerShell installs to the Program Files
directory, which causes problems with sshd's configuration, since it can't handle paths with spaces in them. To fix this, run the following command before editing the sshd_config file in Windows.
New-Item -ItemType SymbolicLink -Path "C:\ProgramFiles" -Target "Program Files"
As an Administrator on Windows, or with sudo
on Linux and macOS, edit this file and add one of the following lines, depending on the operating system.
- Linux:
Subsystem powershell /usr/bin/pwsh -sshs -NoLogo -NoProfile
- macOS:
Subsystem powershell /usr/local/bin/pwsh -sshs -NoLogo -NoProfile
- Windows (with Core 6.0.4):
Subsystem powershell C:\ProgramFiles\PowerShell\6.0.4\pwsh.exe -sshs -NoLogo -NoProfile
- Windows (With Core 6.1.x):
Subsystem powershell C:\ProgramFiles\PowerShell\6\pwsh.exe -sshs -NoLogo -NoProfile
- Windows (With PowerShell 7):
Subsystem powershell C:\ProgramFiles\PowerShell\7\pwsh.exe -sshs -NoLogo -NoProfile
On newer versions of Windows 10, there are also two lines that need to be removed from this file.
Match Group administrators AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
Remove them entirely.
Save the change, and restart the OpenSSH Server service.
Restarting sshd in Windows
In the Start menu, under Windows Administration, select Services. Find the OpenSSH SSH Server item, right click on it, and select restart.
Restarting sshd in macOS
In the Apple menu, select System Preferences. In system preferences, select sharing. Uncheck Remote Login, then check it again.
Restarting sshd in Fedora
Run the following command.
- sudo systemctl restart sshd
Testing it All
After this you should be able to use PowerShell remoting between your systems, and hopefully, each system will know each other system's host key, and each user's public key. With this configuration complete, you will be able to run various scripts involving remote access over PowerShell.
Copyright 2020 Vincent Damewood