Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save FNGarvin/730789da5e18fbadaf98105e4ee92e52 to your computer and use it in GitHub Desktop.
Save FNGarvin/730789da5e18fbadaf98105e4ee92e52 to your computer and use it in GitHub Desktop.

Guide: Configuring Windows OpenSSH Server for Full WSL Sessions

Last Updated: June 23, 2025

Authorship: This guide was developed in a collaborative session between a user and Google's Gemini assistant.

Objective

This guide details a robust method to configure the Windows OpenSSH Server to provide users with a direct and seamless WSL/Linux shell.

The primary benefit of this configuration over simply launching bash.exe from a default CMD/DOS shell is that it provides a native Linux environment for all SSH protocols. This allows tools like scp, sftp, and git to work correctly with Linux-style paths (e.g., ~/file.txt or /etc/profile) right out of the box, which is not possible when operating through a conventional Windows shell.

Prerequisites

  1. A modern Windows machine (Windows 10/11 or Server 2019/2022).
  2. WSL installed with at least one Linux distribution (e.g., Ubuntu).
  3. OpenSSH Server for Windows installed and running.

Note: OpenSSH Server can be installed via Settings -> System -> Optional features -> Add a feature, and searching for "OpenSSH Server".


Step 1: Create the PowerShell "Shim" Script

The core of this solution is a PowerShell "shim" script that intelligently directs ssh and scp requests. This script will be called by the OpenSSH service.

  1. Create the file %ProgramData%\ssh\wsl_shim.ps1.

  2. Paste the following code into the file. This script is designed to be generic; it receives the target WSL username as a parameter from the sshd_config file. For security, it will fail the connection if this parameter is not provided.

    #
    # Filename: wsl_shim.ps1
    # Author:   FNGarvin
    # License:  MIT
    # A generic shim to route OpenSSH requests to the appropriate WSL user shell.
    # It accepts a '-WslUser' parameter to specify the target user within WSL.
    #
    
    param (
        # Define a string parameter named WslUser. Default to an empty string.
        [string]$WslUser
    )
    
    # Check if the -WslUser parameter was provided and is not an empty string.
    if (-not $WslUser) {
        # If not, write an error and exit with a non-zero code.
        # sshd will see the failure and terminate the client connection.
        # This is a "fail-fast" approach to prevent misconfiguration.
        Write-Error "FATAL: The -WslUser parameter was not provided in the sshd_config ForceCommand. Aborting for security."
        exit 1
    }
    
    # If validation passes, set the username from the parameter.
    $wsl_username = $WslUser
    
    # Check the SSH_ORIGINAL_COMMAND environment variable set by sshd.
    # This is the reliable way to detect non-interactive sessions (scp, sftp, etc.).
    if ($env:SSH_ORIGINAL_COMMAND) {
        # If the variable exists, execute its contents in a bash shell as the specified WSL user.
        & wsl.exe -u $wsl_username -- /bin/bash -c "$($env:SSH_ORIGINAL_COMMAND)"
    }
    else {
        # If the variable is not set, it's an interactive session. Start a login shell.
        & wsl.exe -u $wsl_username --exec /bin/bash --login
    }
    
    #END OF wsl_shim.ps1
    
  3. Secure the Shim File. In an Administrator PowerShell window, run the following commands to harden the script's permissions. This is a critical security step.

    # Reset permissions, removing rights for standard users.
    icacls "$env:ProgramData\ssh\wsl_shim.ps1" /inheritance:r
    
    # Grant only necessary permissions: SYSTEM (Read/Execute), Administrators (Full).
    icacls "$env:ProgramData\ssh\wsl_shim.ps1" /grant "SYSTEM:(RX)" /grant "BUILTIN\Administrators:(F)"
    

Step 2: Configure sshd_config

Open your sshd_config file (located at %ProgramData%\ssh\sshd_config) as an Administrator. Go to the very end of the file and paste the following consolidated block of code. This block contains all necessary changes and is clearly marked for easy identification.

    # === CUSTOM WSL CONFIGURATION START ===
    # This block contains all custom settings for enabling WSL-based SSH sessions.
    
    # Global settings overrides. These will take precedence over any defaults set earlier in the file.
    PubkeyAuthentication yes
    PasswordAuthentication no
    
    # --- Authentication and ForceCommand Rules ---
    
    # Block 1: Define the authentication method for all administrators.
    # It specifies a central authorized_keys file on the Windows filesystem.
    Match Group administrators
        AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
    
    # Block 2: For a specific Windows user, force their connection through the shim.
    # This passes the target WSL username as a parameter to the script.
    Match User YOUR_WINDOWS_USERNAME
        ForceCommand powershell.exe -ExecutionPolicy Bypass -File %PROGRAMDATA%\ssh\wsl_shim.ps1 -WslUser "your_wsl_username"
    
    # To add more users, copy the 'Match User' block for each additional user.
    # Match User Another_Windows_User
    #    ForceCommand powershell.exe -ExecutionPolicy Bypass -File %PROGRAMDATA%\ssh\wsl_shim.ps1 -WslUser "another_wsl_username"

    # === CUSTOM WSL CONFIGURATION END ===

Step 3: Manage a Central authorized_keys File

This architecture uses a single, central file on the Windows host to manage SSH keys, which is simpler and more secure than managing keys inside each user's WSL home directory.

  1. Ensure the file exists at %ProgramData%\ssh\administrators_authorized_keys.

  2. Set Permissions: For security, this file should only be writable by Administrators. Open an Administrator PowerShell and run:

    # Create an empty file first if it doesn't exist
    New-Item -Path "$env:ProgramData\ssh\administrators_authorized_keys" -ItemType File -Force
    
    # Get the Access Control List (ACL) of the parent ssh folder
    $acl = Get-Acl "$env:ProgramData\ssh"
    
    # Apply that same restrictive ACL to our new keys file
    Set-Acl -Path "$env:ProgramData\ssh\administrators_authorized_keys" -AclObject $acl
    
  3. Add User Public Keys: Open this file in a text editor. Paste the public key (the content of the user's id_ed25519.pub file) for each user who needs access, one key per line.


Step 4: Restart and Test

To apply all changes, restart the OpenSSH service in an Administrator PowerShell window:

Restart-Service sshd

Your configuration is now complete. Users can connect with ssh YOUR_WINDOWS_USERNAME@SERVER to get a WSL prompt, and scp and sftp will work correctly against their specified WSL home directory.


Advanced Scenarios and Security Considerations

  • Multi-User Support: The provided configuration is already designed for multiple users. Simply add a new Match User Another_Windows_User block to your sshd_config for each additional user, specifying their target WSL username in the -WslUser parameter.
  • Mismatched Usernames: This guide's design explicitly supports mismatched usernames. The Match User YOUR_WINDOWS_USERNAME directive identifies the connecting Windows account, while the -WslUser "your_wsl_username" parameter directs them to the correct, possibly different, username inside WSL.
  • Targeting a Specific WSL Distribution: If you have multiple WSL distributions (e.g., Ubuntu-20.04 and Debian) and need to target a non-default one, you can add the -d <DistroName> flag to the wsl.exe commands inside the wsl_shim.ps1 script.
    • Example: & wsl.exe -d Debian -u $wsl_username -- /bin/bash -c ...
  • Security Disclaimer: This guide provides a functional configuration based on collaborative development with an AI (Gemini). System administrators are responsible for understanding each component and securing their systems in accordance with their organization's security policies. This configuration should be reviewed and adapted to your specific requirements.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment