ssh-easy-config
0.2.27
dotnet tool install --global ssh-easy-config --version 0.2.27
dotnet new tool-manifest
dotnet tool install --local ssh-easy-config --version 0.2.27
#tool dotnet:?package=ssh-easy-config&version=0.2.27
nuke :add-package ssh-easy-config --version 0.2.27
ssh-easy-config
Cross-platform SSH key management, sharing, and diagnostics.
A .NET 10 CLI tool that handles the entire SSH setup lifecycle: generating keys, sharing them between machines, configuring client and server settings, and diagnosing connectivity problems.
Windows is fully tested and production-ready. Linux, macOS, and WSL support is implemented but not yet validated end-to-end -- confirmations and PRs from users on those platforms are very welcome.
Quick Start
Zero-install (requires .NET 10)
dnx ssh-easy-config
This downloads and runs the tool in a single command with no permanent installation.
Global tool install
dotnet tool install -g ssh-easy-config
ssh-easy-config
Features
- Ed25519 key generation -- generates keys using modern cryptography, stored in platform-standard locations
- Three key exchange modes -- share keys over the local network (mDNS + pairing code), via clipboard, or through a file
- Interactive wizard -- step-by-step guided setup when run with no arguments
- 5-layer connectivity diagnostics -- pinpoints SSH problems from network reachability down to WSL-specific issues
- SSH config management -- create and manage host aliases, audit and harden sshd_config
- Cross-platform -- native support for Windows, Linux, macOS, and WSL (treated as a distinct platform, not generic Linux)
- Permission enforcement -- validates and fixes file permissions on keys and config files
- No centralized relay -- all key exchange happens directly between machines
- Windows-hardened -- handles ghost OpenSSH Server installs, MS-linked account key migration, service registration, and automatic sshd restarts
Commands
Interactive Wizard
ssh-easy-config
Running with no arguments launches a guided wizard using Spectre.Console. It walks through key generation, sharing, configuration, and diagnostics interactively.
setup
Full SSH provisioning: generate keys, install/start/enable sshd, open firewall, fix permissions.
ssh-easy-config setup
ssh-easy-config setup -y # auto-confirm all prompts
ssh-easy-config setup --approve-admin # also approve UAC elevation
On Windows, setup handles:
- Installing OpenSSH Server (via
Add-WindowsCapability, withwingetfallback for ghost installs) - Registering the sshd service if Windows fails to create it
- MS-linked account detection and
administrators_authorized_keysconfiguration - UAC elevation prompts when admin rights are needed
Options:
| Option | Description |
|---|---|
--yes / -y |
Auto-confirm all prompts |
--verbose |
Show detailed output |
--approve-admin |
Also approve UAC elevation (Windows) |
share
Share your public key with another machine.
# Network mode (default) -- uses mDNS discovery and pairing code
ssh-easy-config share
# Clipboard mode -- outputs a base64-encoded block to paste on the other machine
ssh-easy-config share --mode clipboard
# File mode -- writes a .sshec bundle to disk
ssh-easy-config share --mode file --output ./my-key-bundle.sshec
Options:
| Option | Description |
|---|---|
--mode |
Transfer mode: network (default), clipboard, or file |
--output |
Output file path (used with --mode file) |
--host |
Hostname/IP to advertise (skips the selection prompt) |
receive
Receive a key shared from another machine.
# Network mode (default) -- discovers the sender via mDNS
ssh-easy-config receive
# Clipboard mode -- prompts you to paste the base64 block
ssh-easy-config receive --mode clipboard
# File mode -- reads from a .sshec bundle
ssh-easy-config receive --mode file --input ./my-key-bundle.sshec
Options:
| Option | Description |
|---|---|
--mode |
Transfer mode: network (default), clipboard, or file |
--input |
Input file path (used with --mode file) |
--host |
Host to connect to (skips mDNS discovery) |
--port |
Port to connect to |
--code |
Pairing code (skips the prompt) |
After key exchange, the tool:
- Adds the remote key to
authorized_keys(andadministrators_authorized_keyson Windows admin accounts) - Checks
PubkeyAuthenticationis enabled insshd_configand offers to enable it - Prompts to restart sshd so changes take effect
- Offers to create an SSH config alias for the remote machine
diagnose
Diagnose SSH connectivity to a host.
# Diagnose a specific host
ssh-easy-config diagnose myserver
# JSON output for scripting or CI
ssh-easy-config diagnose myserver --json
# Show all checks including skipped ones
ssh-easy-config diagnose myserver --verbose
# General local SSH health check (no host)
ssh-easy-config diagnose
Each check offers an auto-fix prompt where safe (start sshd, open firewall port, fix file permissions, etc.).
Options:
| Option | Description |
|---|---|
host |
The host to diagnose (optional) |
--json |
Output results as JSON |
--verbose |
Show all checks including skipped |
config
Manage SSH client and server configuration.
# Audit current sshd_config settings
ssh-easy-config config audit
# Apply security hardening to sshd_config
ssh-easy-config config harden
# List and manage host aliases in ~/.ssh/config
ssh-easy-config config hosts
# Fix Windows key migration issues (admin authorized_keys, Match block)
ssh-easy-config config fix
Actions:
| Action | Description |
|---|---|
audit |
Review sshd_config and recommend changes |
harden |
Apply security settings (disable password auth, enable pubkey auth, etc.) |
hosts |
Manage Host alias entries in ~/.ssh/config |
fix |
Fix Windows admin key migration and permissions (run as Administrator) |
Hardening applies these changes with user confirmation and always creates a backup first:
- Disable
PasswordAuthentication - Set
PubkeyAuthentication yes - Configure
AllowUsers/AllowGroups - Set
PermitRootLogin no(orprohibit-password)
Key Exchange
ssh-easy-config supports three modes for transferring public keys between machines. All three exchange the same data (public keys, hostnames, preferred usernames, and optional host alias suggestions) -- only the transport differs.
Network Mode (default)
The primary mode. Two machines on the same network exchange keys directly:
- Machine A runs
ssh-easy-config share-- starts a temporary listener, displays a 6-digit pairing code, and advertises via mDNS - Machine B runs
ssh-easy-config receive-- discovers Machine A via mDNS (_ssh-easy._tcpservice) and prompts for the pairing code - The pairing code derives a shared encryption key (via HKDF) to secure the transfer
- Both machines display a fingerprint confirmation of the exchanged keys for visual verification
- After exchange, both sides prompt to add keys, enable pubkey auth, restart sshd, and create config aliases
- The listener shuts down after one successful exchange
The share side detects available hostnames including the machine name, mDNS .local name, Tailscale FQDN (if available), and local IP addresses. This lets you choose the right address for the receiving machine's network context.
If mDNS discovery fails (different subnets, mDNS blocked), the user can fall back to entering an IP address and port manually. The share side displays a copyable receive command for quick one-liner usage.
Clipboard Mode
For machines that cannot reach each other over the network:
# On the sending machine
ssh-easy-config share --mode clipboard
# Copy the base64-encoded output
# On the receiving machine
ssh-easy-config receive --mode clipboard
# Paste the block when prompted
File Mode
For transferring via USB drive, shared folder, or any other file transport:
# On the sending machine
ssh-easy-config share --mode file --output ./keys.sshec
# Transfer the .sshec file to the other machine, then:
ssh-easy-config receive --mode file --input ./keys.sshec
Platform Support
ssh-easy-config detects the current platform at startup and adapts its behavior accordingly.
| Concern | Linux | macOS | Windows | WSL |
|---|---|---|---|---|
| SSH paths | ~/.ssh/ |
~/.ssh/ |
%USERPROFILE%\.ssh\ |
~/.ssh/ |
| Service management | systemctl |
launchctl |
sc.exe / Get-Service |
systemctl or service |
| Permissions | chmod |
chmod |
icacls |
chmod |
| sshd_config location | /etc/ssh/sshd_config |
/etc/ssh/sshd_config |
%ProgramData%\ssh\sshd_config |
/etc/ssh/sshd_config |
| Admin authorized keys | authorized_keys |
authorized_keys |
administrators_authorized_keys |
authorized_keys |
| Firewall | iptables/ufw/firewalld |
pfctl |
netsh |
Inherits Windows firewall |
| Tested | Community | Community | Yes | Community |
Windows-Specific Handling
Windows SSH has several pitfalls that ssh-easy-config handles automatically:
- Ghost OpenSSH installs --
Add-WindowsCapabilitysometimes reports success but doesn't installsshd.exe. The tool detects this and falls back towinget install. - Service registration -- even when sshd.exe exists, the Windows service may not be created. The tool registers it via
sc create. - MS-linked accounts -- Windows admin users with Microsoft-linked accounts need keys in
%ProgramData%\ssh\administrators_authorized_keyswith aMatch Group administratorsblock in sshd_config. The tool handles this automatically. - sshd restart -- Windows OpenSSH requires a service restart to pick up authorized_keys changes. The tool prompts for this after key exchange.
- PubkeyAuthentication -- checked and enabled automatically during key exchange if not already set.
WSL as a First-Class Target
WSL is treated as a distinct platform, not generic Linux:
- Detected via
/proc/versioncontaining "Microsoft" or theWSL_DISTRO_NAMEenvironment variable - Supports cross-boundary setup between the Windows host and WSL instances
- Detects WSL1 vs WSL2 for correct network stack handling and port forwarding
- Translates paths between WSL (
/mnt/c/Users/james/.ssh/) and Windows (C:\Users\james\.ssh\) - When running inside WSL, offers to configure both the WSL-side and Windows-side SSH
Diagnostics
The diagnose command runs a 5-layer check sequence. Each layer builds on the previous one, stopping to report at the first failure with an actionable message and an offer to auto-fix where safe.
Layer 1 -- Network Reachability
- DNS resolution (forward and reverse)
- TCP port connectivity (default 22, or custom port from ssh_config)
- Firewall detection heuristics (connection refused vs. timeout)
- Windows Firewall rule checks
Layer 2 -- SSH Service Status
- Whether sshd is running on the target (inferred from connection handshake)
- SSH protocol version banner check
- Local sshd service status (running and enabled)
Layer 3 -- Authentication Audit
- Key-based authentication attempt and result interpretation
- Local key existence and match against remote authorized_keys
- Detection of common key mismatches (wrong type, expired, revoked)
Layer 4 -- Configuration Audit
- Client-side:
~/.ssh/configsyntax validation,IdentityFilepath checks, permission checks - Server-side (if accessible):
sshd_configreview -- pubkey auth enabled, password auth status, AllowUsers/AllowGroups - File permission checks on both ends
- Windows:
administrators_authorized_keysexistence and Match block verification
Layer 5 -- WSL-Specific Checks
- Whether SSH is configured inside WSL, on the Windows host, or both
- Port forwarding between Windows and WSL
- Interop path issues (
/mnt/c/Users/...vsC:\Users\...)
Output Modes
- Interactive (default): colored terminal output with fix-it prompts
- JSON (
--json): machine-readable output for scripting and CI pipelines - Verbose (
--verbose): full trace of every check, including those that were skipped
Known Issues and Platform Gaps
Linux
RestartSshServiceAsyncandIsSshServiceRunningAsynchard-code the service namesshd, but Debian/Ubuntu usesssh. Affects service start/restart/status checks.GetLinuxServiceNameonly checks/lib/systemd/system/ssh.service, not/usr/lib/systemd/system/ssh.service(used by newer distros).systemctl startis called withoutsudofirst (then retried withsudo serviceon failure).firewalldopen-port command passes abash -ccompound command that may not split correctly viaProcessStartInfo.- Clipboard (
xsel) is called without--inputflag. - Writing to
/etc/ssh/sshd_configfails for non-root with no helpful error message.
macOS
launchctl stop/start com.openssh.sshdis deprecated on macOS 12+; should uselaunchctl kickstart.systemsetup -setremotelogin onis a no-op under SIP on macOS 13+ (Ventura) without Full Disk Access.IsSshServiceRunningAsyncmay return false positives (checks if service was ever loaded, not if currently running).pffirewall check always returns "open" without inspecting actual rules.
WSL
CheckLinuxSshdEnabledAsynchard-codesPlatformKind.Linuxinstead of using the platform's actual kind.- WSL2 mirrored networking (
networkingMode=mirroredin.wslconfig) still triggers the port-forwarding warning.
Contributions and bug reports for any platform are welcome at GitHub Issues.
Building from Source
Requires .NET 10 SDK.
# Build
dotnet build src/SshEasyConfig.csproj
# Run tests
dotnet test
# Create NuGet package
dotnet pack src/SshEasyConfig.csproj
Dependencies
- System.CommandLine -- CLI parsing and subcommand routing
- Spectre.Console -- rich terminal UI for the wizard and colored diagnostics
- Makaretu.Dns.Multicast -- mDNS discovery and advertisement for network key exchange
- NSec.Cryptography -- cryptographic operations
No SSH library is bundled. Key generation uses .NET cryptography APIs directly. Connection testing in diagnostics and service management shell out to the system's ssh, systemctl, launchctl, sc.exe, etc.
License
MIT
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.2.27 | 0 | 3/30/2026 |
| 0.2.26 | 3 | 3/29/2026 |
| 0.2.25 | 31 | 3/29/2026 |
| 0.2.24 | 26 | 3/29/2026 |
| 0.2.23 | 22 | 3/29/2026 |
| 0.2.22 | 19 | 3/29/2026 |
| 0.2.21 | 25 | 3/29/2026 |
| 0.2.20 | 60 | 3/28/2026 |
| 0.2.19 | 100 | 3/28/2026 |
| 0.2.18 | 36 | 3/28/2026 |
| 0.2.17 | 44 | 3/27/2026 |
| 0.2.16 | 33 | 3/27/2026 |
| 0.2.15 | 36 | 3/27/2026 |
| 0.2.14 | 31 | 3/27/2026 |
| 0.2.13 | 35 | 3/27/2026 |
| 0.2.12 | 33 | 3/27/2026 |
| 0.2.11 | 36 | 3/27/2026 |
| 0.2.10 | 28 | 3/26/2026 |
| 0.2.9 | 30 | 3/26/2026 |
| 0.2.8 | 26 | 3/26/2026 |