Windows ssh settings and configuration

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 New-PsSession and Enter-PsSession.

Most Linux infrastructure architects are familiar with the access controls required for common ssh configuration files including ~/.ssh/config, ~/.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.

Windows ssh error message when private key ACLs are too open (click to enlarge)

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.

Event log entry for user attempting to ssh into host when authorized_keys is too open (click to enlarge)

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.

Required ACL settings for Windows ssh authorized_keys file (click to enlarge)

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.

Required ACL settings for Windows ssh private key files (click to enlarge)

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.



, ,



Leave a Reply

Your email address will not be published. Required fields are marked *