I hope this post will help you through the agony of configuring private key authentication in ssh for Windows so you can experience the ecstasy of actually using it with PowerShell for PowerShell 7 ssh-based remoting.
It’s been over five years since the PowerShell team announced that ssh was coming to Windows. That’s plenty of time to have exhaustively documented the steps to make it work well with public key authentication. But as my hours of agony configuring ssh on Windows Server prove, that hasn’t happened. As of this writing, there is an open pull request to improve the doc. But the pull request largely revises the existing documentation. That doc set is so badly organized and written, I’m not convinced the updates will make much difference in the topic I cover here. That topic is setting up file permissions for using Windows ssh with public key authentication.
Windows ssh works fine out-of-the-box with password authentication. However, it’s another story with public key authentication. (Available authentication methods are configured in
sshd_config.) For maximum security, enable public key authentication and disable password authentication. One should never have ssh password authentication enabled for Azure and/or AWS VMs. Never. Another security suggestion: move the sshd listener to a non-standard TCP port (not TCP 22). In fact, I’ve never worked in a client that does use password authentication for ssh access. And with PowerShell 7 ssh remoting, password authentication would mean constant calls to
Get-Credential. With public key authentication,
ssh-agent can be used to make credentials available to
Most Linux infrastructure architects are familiar with the access controls required for common ssh configuration files including
~/.ssh/authorized_keys and whatever private key is specified via either
ssh-agent or via the
-i identity parameter on the ssh command. These files must be owned by the user and must not allow group or world write access and, preferably, no group or world access at all. In Linux, this is accomplished with a simple
chmod 600 [filename].
Not so in Windows. Windows Server uses a flexible but complicated ACL security scheme — which is integrated with Active Directory users and groups — to control access to files. Failure to have the correct ACLs for one of the ssh configuration files produces a range of errors raging from obvious to the impenetrable.
For example, in this first screenshot in which I am attempting to ssh to localhost using a public key, the error is explicit. The public key’s ACLs are too broad.
But when the user’s
$HOME\.ssh\authorized_keys file — which contains the public key to be be matched to the incoming private key — is incorrectly ACLed, the ssh event log entry shows a maddeningly unhelpful response, as seen in this screenshot.
Before we discuss the proper ACLs for these files, let me suggest another tip that might make your life easier: avoid using
%programdata%/ssh/administrators_authorized_keys. This configuration setting in
sshd_config is supposed to allow centralization of public keys for domain users in the AD administrator groups. I spent hours and hours working on this without any success. I simply commented this out in
sshd_config and set up an administrator account as any end user account is set up: with the public keys in
$HOME/.ssh. This is a good idea but a terrible implementation and so, IMHO, it’s not worth the effort.
So, what are the proper ACL’s for user configuration files? The settings are both simple and undocumented. ACLs for ssh files must:
- Be owned by the user which will attempt to authenticate via ssh
- Have no inherited permissions
- Give full control to these three identities and no others: SYSTEM, Administrators and, of course, the user.
Here’s a screenshot showing the correct ACL settings for a user’s
authorized_keys file. Note that inheritance is disabled.
And here’s a screenshot showing the correct ACL settings for a private key used by
ssh-agent and/or an
ssh -i [filename] command.
There may be other combinations that work — but I couldn’t find any in my extensive testing on Windows Server 2019. In my testing, the results were binary. With these settings, connections work every time. Changing any of the settings, for example, adding the system administrator identity to a user’s
authorized_keys file instantly created the hard-to-debug error you see above.
Administratively, managing these required ACLs on a per-user basis is somewhat clumsy. To scale this, you would need to automate applying these settings. That shouldn’t be too hard. You could have a model file, use
Get-Acl to create the required input object for
Set-Acl and modify the file as required via PowerShell script. But once you know the required settings (hello, doc writers?), it’s no worse than it is in Linux.
Finally, here’s a bonus tip: remove all
Write-Host statements from the destination user’s
$Profile which you will PowerShell remote into. See this GitHub issue for more.
I hope you find this information helpful. Please feel free to leave a comment below with your tips for configuring ssh on Windows.