Deploying SSH on Windows is usually straightforward: enable the built-in OpenSSH Server capability, start sshd, and configure authentication. In practice, Windows servicing failures, Feature-on-Demand dependencies, network restrictions, and remote-access constraints can turn the process into a much more involved systems-engineering exercise.

This article describes a robust approach for deploying Microsoft OpenSSH on Windows when the built-in optional feature cannot be installed, then hardening it for public-key-only access and placing it behind an isolated FRP tunnel.

All hostnames, usernames, domains, ports, paths, keys, and other environment-specific values below are anonymized.

1. Intended Architecture

The final architecture is deliberately layered:

<ADMIN_HOST>
    |
    | SSH public-key authentication
    v
<FRPS_PUBLIC_ENDPOINT>:<REMOTE_SSH_PORT>
    |
    v
<FRPS_INSTANCE>
    |
    v
<FRPC_INSTANCE>
    |
    v
<WINDOWS_LOOPBACK>:<LOCAL_SSH_PORT>
    |
    v
Windows OpenSSH Server

The important property is that the Windows SSH daemon is not directly exposed to the LAN or Internet.

FRPC forwards traffic internally to:

<WINDOWS_LOOPBACK>:<LOCAL_SSH_PORT>

while FRPS exposes a separate external port.

This separates three concerns:

  • Windows SSH authentication;
  • FRP transport and remote publishing;
  • Internet-facing exposure.

2. When the Built-In OpenSSH Capability Fails

Windows normally provides OpenSSH Server as a Feature on Demand.

A typical installation attempt is:

Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

A servicing failure may return an error such as:

0x800f0950

The important point is that this does not necessarily indicate a broken OpenSSH package. It usually means that Windows servicing could not obtain or install the requested capability.

Possible causes include:

  • damaged component-store metadata;
  • unavailable Feature-on-Demand source files;
  • Windows Update source problems;
  • servicing-stack inconsistencies;
  • policy restrictions;
  • mismatched optional-feature media.

3. Diagnosing the Windows Component Store

The component store can be checked without modifying the system:

DISM /Online /Cleanup-Image /ScanHealth

If Windows reports that the component store is repairable, the next step is:

DISM /Online /Cleanup-Image /RestoreHealth

When unrestricted network access is available, DISM may retrieve missing repair material from Windows Update.

For systems on metered or constrained networks, a local-only repair can be tested first:

DISM /Online /Cleanup-Image /RestoreHealth /LimitAccess

If this fails because no repair source exists locally, that result is still useful: it confirms that an external or offline repair source is required.

A successful component-store repair does not guarantee that every Feature-on-Demand package will subsequently install. If OpenSSH still returns the same servicing error after DISM succeeds, continuing to retry the same capability may provide little value.

4. Switching to Microsoft Win32-OpenSSH

A practical alternative is Microsoft’s separately packaged Win32-OpenSSH distribution.

For an x86-64 system, the package can be obtained from the official Microsoft-maintained release repository and transferred to the target host.

A server-only MSI installation may use:

msiexec.exe /i "<OPENSSH_MSI_PATH>" ADDLOCAL=Server

After installation:

Get-Service sshd

should show the service.

Set it to start automatically:

Set-Service sshd -StartupType Automatic

The MSI deployment commonly places binaries under a path such as:

<OPENSSH_INSTALL_DIRECTORY>

while server configuration remains under:

<PROGRAMDATA_SSH_DIRECTORY>

5. Verifying the Listener

The SSH service should initially be verified before any hardening changes:

Get-NetTCPConnection -State Listen -LocalPort <LOCAL_SSH_PORT>

A fresh installation may listen on all interfaces:

<ALL_IPV4_INTERFACES>:<LOCAL_SSH_PORT>
<ALL_IPV6_INTERFACES>:<LOCAL_SSH_PORT>

This is functional, but it is broader exposure than necessary when FRPC only needs a loopback endpoint.

6. Administrator Public-Key Authentication on Windows

Windows OpenSSH handles administrator accounts differently from ordinary users.

For a non-administrator account, the usual file is:

C:\Users\<WINDOWS_USER>\.ssh\authorized_keys

For accounts in the local Administrators group, the default OpenSSH configuration commonly contains:

Match Group administrators
    AuthorizedKeysFile <ADMINISTRATOR_AUTHORIZED_KEYS_PATH>

Therefore the administrator public key belongs in:

<ADMINISTRATOR_AUTHORIZED_KEYS_PATH>

A public key from the administrative source host may look conceptually like:

ssh-ed25519 <PUBLIC_KEY_DATA> <KEY_COMMENT>

or:

ssh-rsa <PUBLIC_KEY_DATA> <KEY_COMMENT>

Only the public key is copied.

The private key must remain on the administrative host.

7. Correct ACLs Matter

Windows OpenSSH is strict about permissions on administrator key files.

A safe pattern is to remove inherited ACLs and grant access only to the system and local administrators.

For example:

icacls "<ADMINISTRATOR_AUTHORIZED_KEYS_PATH>" /inheritance:r

Then grant access to the relevant well-known security principals.

Using SIDs instead of localized group names is useful because the same command works across different Windows display languages.

The final ACL should effectively be limited to:

SYSTEM
Administrators

8. Test the Key Before Disabling Passwords

The first remote test should explicitly require public-key authentication.

From the administrative host:

ssh \
  -o PreferredAuthentications=publickey \
  -o PasswordAuthentication=no \
  -p <REMOTE_SSH_PORT> \
  <WINDOWS_USER>@<FRPS_PUBLIC_ENDPOINT>

If this succeeds, it proves that:

  • FRPS is reachable;
  • FRPC is connected;
  • FRPC reaches the local Windows SSH port;
  • sshd is running;
  • the public key is accepted;
  • the administrator key file and ACLs are correct.

Only after this test should password authentication be disabled.

9. Harden sshd_config

A hardened SSH configuration can contain:

PubkeyAuthentication yes
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no

For an FRPC-only design, the SSH listener can also be restricted:

ListenAddress <WINDOWS_LOOPBACK>

Before restarting the service:

& "<OPENSSH_INSTALL_DIRECTORY>\sshd.exe" -t

No output normally indicates valid syntax.

Then:

Restart-Service sshd

10. Prove Password Authentication Is Actually Disabled

Do not rely only on configuration-file inspection.

Perform a negative test:

ssh \
  -o PubkeyAuthentication=no \
  -o PreferredAuthentications=password,keyboard-interactive \
  -p <REMOTE_SSH_PORT> \
  <WINDOWS_USER>@<FRPS_PUBLIC_ENDPOINT>

A hardened server should reject the request without offering a password prompt.

This proves that public-key-only authentication is effective in practice.

11. Restrict SSH to Loopback

If FRPC connects to:

<WINDOWS_LOOPBACK>:<LOCAL_SSH_PORT>

there is usually no need for Windows SSH to listen on every physical interface.

After setting:

ListenAddress <WINDOWS_LOOPBACK>

verify:

Get-NetTCPConnection -State Listen -LocalPort <LOCAL_SSH_PORT>

The result should show only the loopback listener.

The security effect is:

LAN -> Windows SSH                blocked
Direct Internet -> Windows SSH    blocked
FRPC -> Windows loopback SSH      allowed

This significantly narrows the attack surface.

12. Making PowerShell the Default SSH Shell

Windows OpenSSH can be configured to launch PowerShell instead of cmd.exe.

A registry value under the OpenSSH configuration key can set:

DefaultShell = <POWERSHELL_EXECUTABLE>

After restarting sshd, new SSH sessions can land directly at:

PS C:\Users\<WINDOWS_USER>>

This is much more convenient for remote Windows administration.

13. Fixing SFTP with an MSI OpenSSH Installation

An interesting failure mode is:

SSH shell works
SFTP immediately closes

If:

Subsystem sftp sftp-server.exe

is configured but the MSI installation places sftp-server.exe outside the directory expected by sshd, SFTP may fail even though normal SSH works.

The subsystem can be made explicit:

Subsystem sftp "<OPENSSH_SFTP_SERVER_FULL_PATH>"

After validating the configuration and restarting sshd, SFTP can be tested again:

sftp -P <REMOTE_SSH_PORT> <WINDOWS_USER>@<FRPS_PUBLIC_ENDPOINT>

This distinction is useful because successful SSH authentication does not automatically prove that every SSH subsystem is functional.

14. Temporary FRPC Autostart for Maintenance Reboots

During maintenance, a remote reboot may be necessary before the normal FRPC operator is available.

A temporary Scheduled Task can start FRPC at Windows boot:

Trigger: At startup
Run as: SYSTEM
Run level: Highest
Executable: <FRPC_EXECUTABLE>
Arguments: -c "<FRPC_CONFIG>"

This provides a one-time recovery path after reboot.

Once remote access is confirmed again, the task can be removed.

This is preferable to permanently changing the client’s operational model merely to survive one maintenance reboot.

15. Final Security Model

The completed design is:

Public Internet
     |
     v
<FRPS_PUBLIC_ENDPOINT>:<REMOTE_SSH_PORT>
     |
     v
Dedicated FRPS instance
     |
     v
FRPC
     |
     v
<WINDOWS_LOOPBACK>:<LOCAL_SSH_PORT>
     |
     v
Windows OpenSSH
     |
     +-- Public key only
     +-- Password disabled
     +-- Keyboard-interactive disabled
     +-- Administrator key ACL restricted
     +-- Loopback listener only

The most important lesson is that a failed Windows Feature-on-Demand installation does not have to block the deployment.

A controlled fallback to Microsoft’s standalone Win32-OpenSSH package, combined with strict authentication, loopback-only listening, precise key permissions, and an isolated FRP tunnel, can produce a clean and defensible remote-management architecture.

Leave a Reply

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