SSM Session Client
A single, self-contained binary for AWS SSM Session Manager. Access EC2 instances via shell, native SSH, port forwarding, or RDP β without bastion hosts, open inbound ports, or the AWS CLI plugin. Designed for restricted enterprise environments where installing the AWS CLI or Session Manager plugin is not an option.
Interactive Shell
Full terminal access to your instances via SSM. Supports ANSI colours and virtual terminal processing on Windows.
Native SSH β No Client Needed
Built-in SSH using Go's crypto library. No external SSH client, no ProxyCommand. Works on Windows out of the box.
VSCode Remote SSH
Drop-in replacement for the SSH executable in VSCode Remote SSH. Point remote.SSH.path at the binary and connect.
Port Forwarding
Tunnel TCP traffic to private instances. Multiplexed connections for SSM agents v3.0.196.0+, single-connection fallback for older agents.
RDP (Windows targets)
One command opens an RDP tunnel and launches mstsc.exe. Optional automatic password retrieval via the EC2 API.
PrivateLink Support
Override SSM, SSM Messages, EC2, STS, and KMS endpoints. Works in VPN/Direct Connect environments where AWS PrivateLink replaces public endpoints.
EC2 Instance Connect
Push an ephemeral public key via the EC2 Instance Connect API β no permanent SSH key management needed.
AWS Identity Center SSO
Automatic SSO device-code login with optional browser launch. No separate aws sso login step needed.
Flexible Target Lookup
Resolve targets by instance ID, EC2 tag, IP address, DNS TXT record, or short aliases defined in your config file.
Why use SSM Session Client instead of the AWS CLI?
| Capability | AWS CLI + Plugin | SSM Session Client |
|---|---|---|
| Interactive shell access | β | β |
| SSH ProxyCommand | β | β |
| Native SSH (no external client) | β | β |
| VSCode Remote SSH replacement | β | β |
| EC2 Instance Connect integration | β | β |
| Custom SSM Messages endpoint | β | β |
| Works in AppLocker/AirLock environments | β | β (single binary) |
| Built-in SSO login | Requires separate aws sso login | β built-in |
| Target lookup by tag / IP / DNS | β | β |
| RDP tunneling with auto password | β | β (Windows) |
| VPC endpoint overrides | β | β |
How it works
SSM Session Client uses the AWS SSM StartSession API to create an
encrypted WebSocket tunnel to your EC2 instance via the SSM Messages service.
All traffic flows through AWS β no inbound firewall rules, no bastion hosts,
no VPN to the instances themselves.
Access is controlled by IAM policy, not by distributing SSH keys. You grant (or revoke) access to an instance with a policy change, and every session is logged by AWS CloudTrail.
In environments where only AWS PrivateLink endpoints are reachable (VPN or
Direct Connect), the ssmmessages-endpoint flag replaces the public
StreamUrl returned by StartSession with your private endpoint.
This is the primary feature that makes SSM Session Client useful in air-gapped
or restricted networks.
Built for restricted enterprise environments
In many enterprise environments, application whitelisting and endpoint security tools prevent users from installing or running the AWS CLI, Python, or the Session Manager plugin. SSM Session Client is a single, statically compiled Go binary with zero runtime dependencies β it runs anywhere a standard executable is allowed.
Tested and used in environments protected by:
- Microsoft AppLocker β the binary can be deployed to an approved path and whitelisted by publisher or hash rule
- Airlock Digital β approved as a single file, no additional libraries or interpreters to trust
- ManageEngine / Endpoint Central β no installer, no registry changes, no service dependencies
- CrowdStrike, Carbon Black, SentinelOne β no child processes to spawn (unlike the AWS CLI which invokes Python and the plugin)
Because it ships as one file with everything built in β SSH client, SSM data channel, WebSocket transport, SSO login β there is nothing extra to install, configure, or get approved by your security team. Copy the binary, set your AWS credentials, and connect.
This also makes SSM Session Client ideal for complex network environments where
AWS service endpoints are only reachable via VPN or
Direct Connect through AWS PrivateLink. The
ssmmessages-endpoint flag lets you override the public
StreamUrl with your private VPC endpoint, keeping all session
traffic within your private network.
Install
SSM Session Client is a single binary with no runtime dependencies. Download
the binary for your platform, make it executable, and add it to your
PATH.
macOS
# Download the latest binary
curl -fsSL \
https://github.com/alexbacchin/ssm-session-client/releases/latest/download/ssm-session-client-darwin-arm64 \
-o /usr/local/bin/ssm-session-client
# Make it executable
chmod +x /usr/local/bin/ssm-session-client
# Verify
ssm-session-client --version
# Download the latest binary
curl -fsSL \
https://github.com/alexbacchin/ssm-session-client/releases/latest/download/ssm-session-client-darwin-amd64 \
-o /usr/local/bin/ssm-session-client
# Make it executable
chmod +x /usr/local/bin/ssm-session-client
# Verify
ssm-session-client --version
xattr -d com.apple.quarantine /usr/local/bin/ssm-session-client
Linux
sudo curl -fsSL \
https://github.com/alexbacchin/ssm-session-client/releases/latest/download/ssm-session-client-linux-amd64 \
-o /usr/local/bin/ssm-session-client
sudo chmod +x /usr/local/bin/ssm-session-client
ssm-session-client --version
sudo curl -fsSL \
https://github.com/alexbacchin/ssm-session-client/releases/latest/download/ssm-session-client-linux-arm64 \
-o /usr/local/bin/ssm-session-client
sudo chmod +x /usr/local/bin/ssm-session-client
ssm-session-client --version
sudo curl -fsSL \
https://github.com/alexbacchin/ssm-session-client/releases/latest/download/ssm-session-client-linux-386 \
-o /usr/local/bin/ssm-session-client
sudo chmod +x /usr/local/bin/ssm-session-client
ssm-session-client --version
# For ARMv7 (Raspberry Pi 3/4, most modern ARM boards)
sudo curl -fsSL \
https://github.com/alexbacchin/ssm-session-client/releases/latest/download/ssm-session-client-linux-arm7 \
-o /usr/local/bin/ssm-session-client
# For ARMv6 (Raspberry Pi Zero/1)
# Replace "arm7" with "arm6" in the URL above
sudo chmod +x /usr/local/bin/ssm-session-client
ssm-session-client --version
Windows
# Download to a folder already on your PATH (e.g. C:\tools)
$url = "https://github.com/alexbacchin/ssm-session-client/releases/latest/download/ssm-session-client-windows-amd64.exe"
Invoke-WebRequest -Uri $url -OutFile "C:\tools\ssm-session-client.exe"
# Verify
ssm-session-client --version
$url = "https://github.com/alexbacchin/ssm-session-client/releases/latest/download/ssm-session-client-windows-arm64.exe"
Invoke-WebRequest -Uri $url -OutFile "C:\tools\ssm-session-client.exe"
ssm-session-client --version
No OpenSSH required. The
ssh-direct command and the
VSCode Remote SSH integration use a built-in Go SSH client β you do not need to
install the Windows Optional Feature "OpenSSH Client".
Optional: AWS Session Manager Plugin
The AWS Session Manager Plugin is not required.
ssm-session-client handles all SSM functionality natively, including
KMS-encrypted sessions. No additional software is needed.
If the plugin is installed, ssm-session-client can optionally use it
(controlled by the --ssm-session-plugin flag). Install instructions:
Install the Session Manager plugin for the AWS CLI.
Code Signing Certificate
Windows binaries are signed with an Authenticode code signing certificate. This allows enterprise security tools to verify the publisher and integrity of the binary. The certificate details below can be used to create whitelisting rules in AppLocker, Airlock Digital, and other endpoint security products.
| Property | Value |
|---|---|
| Subject | CN=Alex Bacchin, O=github.com/alexbacchin, C=AU |
| Issuer | CN=Alex Bacchin, O=github.com/alexbacchin, C=AU |
| Serial Number | 1B:B7:DA:54:2F:FA:C2:4E:A4:F3:9C:A5:71:D2:F6:EF:4E:EE:EC:D3 |
| Valid From | 21 February 2026 |
| Valid To | 20 February 2029 |
| Key Algorithm | RSA 4096-bit |
| Signature Algorithm | SHA-256 with RSA |
| Key Usage | Digital Signature |
| Extended Key Usage | Code Signing |
| SHA-256 Fingerprint | 75:89:E4:12:77:43:A6:9D:01:91:75:E0:B7:19:B9:FE:A7:7B:B4:5F:08:C5:35:55:41:7F:5F:B3:F2:F0:9B:6B |
| SHA-1 Fingerprint | 51:EC:2D:E1:25:26:AE:25:AF:DB:BC:10:4D:11:53:FD:4A:DC:18:27 |
| Subject Key Identifier | A3:46:9A:84:7D:7E:6C:69:9D:F1:48:55:6D:6E:D9:92:92:65:92:74 |
AppLocker β Publisher Rule
Create a publisher rule in your AppLocker EXE policy that allows binaries signed by this certificate. In Group Policy Editor:
- Navigate to Computer Configuration → Windows Settings → Security Settings → Application Control Policies → AppLocker → Executable Rules
- Right-click → Create New Rule → select Publisher
- Browse to the signed
ssm-session-client.exebinary - Set the slider to Publisher level to allow any binary signed by
O=github.com/alexbacchin, CN=Alex Bacchin
Alternatively, use a File Hash rule with the SHA-256 hash of the specific release binary.
Airlock Digital
Add the binary by certificate fingerprint or file hash. Use the SHA-256 fingerprint above to create a trusted publisher rule, or add the specific binary hash from the release checksums file.
Verifying the Signature
On Windows, right-click the .exe → Properties → Digital Signatures tab to inspect the certificate. From PowerShell:
# View the Authenticode signature
Get-AuthenticodeSignature .\ssm-session-client.exe
# Detailed certificate info
(Get-AuthenticodeSignature .\ssm-session-client.exe).SignerCertificate | Format-List
Configuration
ssm-session-client reads its configuration from three sources, in
order of precedence (highest first):
- Command-line flags β highest priority, override everything
- Environment variables β prefix
SSC_(e.g.SSC_AWS_REGION) - YAML config file β lowest priority, supplies defaults
AWS Credentials
ssm-session-client uses the standard AWS SDK credential chain. No
special configuration is needed beyond what you would set up for the AWS CLI.
Refer to the AWS documentation:
Authentication and access credentials for the AWS CLI.
Common methods:
- Named profiles in
~/.aws/credentialsand~/.aws/config - Environment variables:
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN - EC2 instance metadata / ECS task role
- AWS Identity Center (SSO) β see SSO Login below
Configuration Options
| Description | Config file / Flag | Environment variable | AWS SDK variable |
|---|---|---|---|
| Config file path | config | SSC_CONFIG_FILE | β |
Log level (debug, info, warn, error) | log-level | SSC_LOG_LEVEL | β |
| AWS profile name | aws-profile | SSC_AWS_PROFILE | AWS_PROFILE |
| AWS region | aws-region | SSC_AWS_REGION | AWS_REGION / AWS_DEFAULT_REGION |
Trigger SSO login flow (true/false) | sso-login | SSC_SSO_LOGIN | β |
Open browser for SSO (true/false) | sso-open-browser | SSC_SSO_OPEN_BROWSER | β |
| STS endpoint override | sts-endpoint | SSC_STS_ENDPOINT | AWS_ENDPOINT_URL_STS |
| EC2 endpoint override | ec2-endpoint | SSC_EC2_ENDPOINT | AWS_ENDPOINT_URL_EC2 |
| SSM endpoint override | ssm-endpoint | SSC_SSM_ENDPOINT | AWS_ENDPOINT_URL_SSM |
| SSM Messages WebSocket endpoint | ssmmessages-endpoint | SSC_SSMMESSAGES_ENDPOINT | β |
| HTTP proxy URL | proxy-url | SSC_PROXY_URL | HTTPS_PROXY |
| Use Session Manager plugin | ssm-session-plugin | SSC_SSM_SESSION_PLUGIN | β |
| Enable auto-reconnect | enable-reconnect | SSC_ENABLE_RECONNECT | β |
| Max reconnection attempts | max-reconnects | SSC_MAX_RECONNECTS | β |
Port-forward outbound rate limit (kbps; 0 = unlimited) | port-forward-kbps | SSC_PORT_FORWARD_KBPS | β |
| Target aliases (config file only) | aliases | β | β |
| Target alias (CLI flag, repeatable) | --alias name=tag:val | β | β |
ssh-direct options (nested under ssh-direct: in config file) | |||
| SSH private key file path | ssh-direct.ssh-key-file | SSC_SSH_DIRECT_SSH_KEY_FILE | β |
| Skip host key verification | ssh-direct.no-host-key-check | SSC_SSH_DIRECT_NO_HOST_KEY_CHECK | β |
| Run a command instead of an interactive shell | ssh-direct.ssh-exec-command | SSC_SSH_DIRECT_SSH_EXEC_COMMAND | β |
| Push ephemeral key via EC2 Instance Connect | ssh-direct.instance-connect | SSC_SSH_DIRECT_INSTANCE_CONNECT | β |
| Disable Instance Connect even if set in config | ssh-direct.no-instance-connect | SSC_SSH_DIRECT_NO_INSTANCE_CONNECT | β |
Default SSH username (avoids needing user@ in target or ~/.ssh/config) | ssh-direct.ssh-user | SSC_SSH_DIRECT_SSH_USER | β |
Default SSH port (avoids needing :port in target or ~/.ssh/config) | ssh-direct.ssh-port | SSC_SSH_DIRECT_SSH_PORT | β |
shell options (nested under shell: in config file) | |||
| SSM document to run for the session | shell.document-name | SSC_SHELL_DOCUMENT_NAME | β |
| SSM document parameters (config file only) | shell.parameters | β | β |
| SSM document parameter (CLI flag, repeatable) | --parameter key=value | β | β |
proxy-url only applies to services that do not have a
custom endpoint set. ssmmessages-endpoint replaces the StreamUrl
returned by StartSession so that the WebSocket connection uses your private endpoint.
Config File
The default config file path is $HOME/.ssm-session-client.yaml (Linux/macOS)
or %USERPROFILE%\.ssm-session-client.yaml (Windows).
The file is searched in this order:
- Current working directory
- User home directory (
$HOME/%USERPROFILE%) - Directory containing the
ssm-session-clientexecutable
Pass a custom path with --config=/path/to/config.yaml.
Minimal config (AWS profile + region)
aws-profile: my-profile
aws-region: ap-southeast-2
AWS Identity Center SSO Login
ssm-session-client provides two ways to manage SSO credentials:
- Automatic login before each session: Set
sso-login: truein your config to trigger a device-code login automatically when the cached token has expired. - Standalone login/logout commands: Use
sso-loginandsso-logoutsubcommands to manage credentials separately from session commands.
Option 1: Automatic SSO login (during session setup)
-
Configure the AWS CLI for SSO. Follow the AWS guide: Configuring the AWS CLI to use AWS IAM Identity Center. This creates a profile section in
~/.aws/configwithsso_start_url,sso_account_id,sso_role_name, andsso_region. -
Set
aws-profileandsso-login: truein your config file (or pass them as flags/env vars). -
(Optional) Set
sso-open-browser: trueif your machine can open a browser. Otherwise, copy the verification URL printed to the terminal and open it in any browser.
Automatic login config file example
aws-profile: my-sso-profile
aws-region: ap-southeast-2
sso-login: true
sso-open-browser: true
Option 2: Standalone SSO login and logout commands
Use the sso-login and sso-logout commands to manage SSO
credentials independently from session commands.
sso-login
Authenticate with AWS SSO and cache the token. The authorization URL is printed to stdout.
# Basic login (opens browser if --sso-open-browser is set)
ssm-session-client sso-login --aws-profile my-sso-profile
# Login with custom timeout (default 120 seconds)
ssm-session-client sso-login --aws-profile my-sso-profile --timeout 180
# Headless mode (no browser, just print the URL)
ssm-session-client sso-login --aws-profile my-sso-profile
sso-logout
Delete the cached SSO credentials for a profile. Removes the token from ~/.aws/sso/cache/.
ssm-session-client sso-logout --aws-profile my-sso-profile
VPC Endpoints (PrivateLink)
In environments where AWS services are only reachable via AWS PrivateLink (VPN or Direct Connect), you can override each service endpoint individually.
The ssmmessages-endpoint flag is the critical one: the StartSession
API always returns a public StreamUrl. This flag replaces that URL with
your private VPC endpoint so the WebSocket tunnel stays entirely within your private network.
Config file with VPC endpoint overrides
aws-profile: sandbox
aws-region: us-west-2
# VPC endpoint DNS names (from your VPC endpoint console/outputs)
ec2-endpoint: vpce-059c3b85db66f8165-mzb6o9nb.ec2.us-west-2.vpce.amazonaws.com
ssm-endpoint: vpce-06ef6f173680a1306-bt58rzff.ssm.us-west-2.vpce.amazonaws.com
ssmmessages-endpoint: vpce-0e5e5b0c558a14bf2-r3p6zkdm.ssmmessages.us-west-2.vpce.amazonaws.com
sts-endpoint: vpce-0877b4abeb479ee06-arkdktlc.sts.us-west-2.vpce.amazonaws.com
# Optional: HTTP proxy for services without a custom endpoint set
proxy-url: http://myproxy:3128
com.amazonaws.<region>.ssm,
com.amazonaws.<region>.ssmmessages,
com.amazonaws.<region>.ec2, and
com.amazonaws.<region>.sts.
See the AWS documentation:
Create VPC endpoints.
Target Lookup
All session commands accept a target as the first argument. Targets are resolved using the following chain (first match wins):
| Priority | Method | Example input | How it works |
|---|---|---|---|
| 1 | Instance ID | i-0abc1234def56789 | Passed directly to the SSM API |
| 2 | Alias | devbox | Looks up the alias in your config, queries EC2 by tag β errors if more than one match |
| 3 | EC2 tag | Name:web-server | Queries EC2 for running instances with tag key=value |
| 4 | IP address | 10.0.1.5 | Queries EC2 by private or public IPv4 |
| 5 | DNS TXT record | web.example.com | Resolves the hostname as a DNS TXT record expected to contain the instance ID |
Target Aliases
Aliases map a short friendly name to an EC2 tag key + value. They are unique β the lookup errors if more than one running instance matches, so the alias always refers to exactly one host.
Define aliases in the config file
aliases:
devbox:
tag-name: Name
tag-value: my-devbox
prod-web:
tag-name: Environment
tag-value: production
Define aliases on the command line (repeatable)
# Single alias on the fly
ssm-session-client shell devbox --alias devbox=Name:my-devbox
# Multiple aliases
ssm-session-client shell prod-web \
--alias devbox=Name:my-devbox \
--alias prod-web=Environment:production
CLI aliases override same-named aliases in the config file. Use aliases with any command:
ssm-session-client shell devbox
ssm-session-client ssh-direct ec2-user@devbox
ssm-session-client port-forwarding devbox:443 8443
Automatic Reconnection
For multiplexed port forwarding (SSM agent β₯ 3.0.196.0),
ssm-session-client automatically reopens the SSM session when the
underlying WebSocket drops β for example, due to a proxy idle-timeout or a
transient network interruption. The local TCP listener stays open throughout, so
connected clients can reconnect without restarting the tool.
--enable-reconnectβ enable/disable (default:true)--max-reconnectsβ maximum attempts before giving up (default:5, set0for unlimited)
Data Channel Encryption
When KMS encryption is enabled for SSM sessions, the data channel is encrypted using
AES-256-GCM. Encryption keys are derived from AWS KMS using the
GenerateDataKey API, with the session ID and target ID as encryption context.
SSM Session Client has built-in support for KMS-encrypted sessions
and does not require the AWS Session Manager plugin. The encryption is handled natively
using Go's crypto libraries. If the Session Manager plugin is installed and enabled
(ssm-session-plugin: true), it will be used instead, but it is not required.
com.amazonaws.<region>.kms) for the
key exchange to succeed within your private network.
Session Modes
Shell
Opens an interactive terminal session on the target instance. Equivalent to
aws ssm start-session.
ssm-session-client shell i-0bdb4f892de4bb54c
ssm-session-client shell i-0bdb4f892de4bb54c --config=~/.ssm-session-client.yaml
SSM documents and parameters
By default AWS applies the account/region default shell document. Use
--document-name to run a specific SSM document, and
--parameter key=value to supply the parameters that document declares.
Repeat --parameter with the same key to build a multi-value parameter.
# Run a specific document
ssm-session-client shell i-0bdb4f892de4bb54c --document-name SSM-SessionManagerRunShell
# Custom document with parameters
ssm-session-client shell i-0bdb4f892de4bb54c \
--document-name MyShellDoc \
--parameter linuxcmd=top \
--parameter runAsUser=ec2-user
# Repeat a key for a multi-value parameter
ssm-session-client shell i-0bdb4f892de4bb54c --parameter cmd=uptime --parameter cmd=whoami
The same settings can be set in the config file:
shell:
document-name: MyShellDoc
parameters:
linuxcmd: ["top"]
runAsUser: ["ec2-user"]
--parameter values are appended to any
parameters defined in the config file, while --document-name overrides
the config file value.
On Windows, the shell command supports virtual terminal processing with ANSI escape sequences for proper colour and formatting in remote shells.
IAM reference: Sample IAM policies for Session Manager
SSH (ProxyCommand)
Uses ssm-session-client as an SSH ProxyCommand, forwarding stdio to the
SSM data channel. The SSH client (system ssh) handles authentication and
encryption. Requires an SSH server on the target instance.
1. Configure SSH
Add to ~/.ssh/config (Linux/macOS) or %USERPROFILE%\.ssh\config (Windows):
Host i-* mi-*
ProxyCommand ssm-session-client ssh %r@%h --config=~/.ssm-session-client.yaml
2. Automatically configure SSH (optional)
ssm-session-client ssh add-proxy-command ec2-user@i-0bdb4f892de4bb54c
3. Connect
ssh ec2-user@i-0bdb4f892de4bb54c
IAM reference: Enabling SSH connections through Session Manager
SSH Direct (Native SSH Client)
A fully integrated SSH session over SSM using Go's native SSH library. No external SSH client, no ProxyCommand, no SSH server listening on an open port.
# Interactive session
ssm-session-client ssh-direct ec2-user@i-0123456789abcdef0
# With a specific key file
ssm-session-client ssh-direct ec2-user@i-0123456789abcdef0 --ssh-key ~/.ssh/my-key
# Run a single command
ssm-session-client ssh-direct ec2-user@i-0123456789abcdef0 --exec "uptime"
# Using EC2 Instance Connect (no SSH key management needed β ephemeral key only)
ssm-session-client ssh-direct ec2-user@i-0123456789abcdef0 --instance-connect
# Custom SSH port embedded in the target
ssm-session-client ssh-direct ec2-user@i-0123456789abcdef0:2222
# Default user and port via flags (no user@ or :port needed in target)
ssm-session-client ssh-direct i-0123456789abcdef0 --ssh-user ec2-user --ssh-port 22
Authentication chain (tried in order)
- Ephemeral key via EC2 Instance Connect (when
--instance-connectis used β only this method is tried; agent/key-file/password fallbacks are skipped) - SSH agent (
SSH_AUTH_SOCK) - Private key file (
--ssh-keyor auto-discovered~/.ssh/id_ed25519/~/.ssh/id_rsa) - Password prompt (interactive)
~/.ssh/known_hosts. Use
--no-host-key-check to skip verification (not recommended for production).
Passing -o UserKnownHostsFile=/dev/null in SSH-compat mode also disables
host key checking, matching OpenSSH behaviour.
Avoiding dependency on ~/.ssh/config
By default the target must include a user@ prefix and an optional
:port suffix, or those values must come from ~/.ssh/config.
The ssh-user and ssh-port settings let you set these defaults
in the app config file (or via environment variables) so neither is required in the
target argument or in an SSH config file.
Priority for each value (highest wins):
- User:
user@targetpositional arg >-lflag (compat mode) >~/.ssh/config(compat mode) >ssh-direct.ssh-userconfig/env > built-in default (ec2-user) - Port:
target:portpositional arg >-pflag (compat mode) >~/.ssh/config(compat mode) >ssh-direct.ssh-portconfig/env > built-in default (22)
Config file example
ssh-direct options live under the ssh-direct: section in the config file:
ssh-direct:
ssh-key-file: ~/.ssh/id_ed25519 # private key (omit to auto-discover)
no-host-key-check: false # set true only in trusted environments
instance-connect: true # push ephemeral key via EC2 Instance Connect
ssh-user: ubuntu # default username (avoids needing user@target or ~/.ssh/config)
ssh-port: 22 # default SSH port (avoids needing target:port or ~/.ssh/config)
VSCode Remote SSH Integration
ssm-session-client can act as the SSH executable for VSCode Remote SSH,
eliminating the need for an external SSH client or ProxyCommand setup. This is
especially useful on Windows where OpenSSH may not be installed.
-
Configure the binary path in VSCode. Open Settings (
Ctrl+,) and search forremote.SSH.path, or add tosettings.json:// Linux / macOS { "remote.SSH.path": "/usr/local/bin/ssm-session-client" } // Windows { "remote.SSH.path": "C:\\tools\\ssm-session-client.exe" } -
Configure your SSH config file (
~/.ssh/configor%USERPROFILE%\.ssh\config):# A named host mapped to an instance ID Host my-server HostName i-0123456789abcdef0 User ec2-user # Wildcard for all instance IDs Host i-* User ec2-user StrictHostKeyChecking accept-newAlternatively, set the default username and port in the app config file so you do not need an SSH config file at all β useful on Windows or in environments where
~/.ssh/configis managed by policy:ssh-direct: ssh-user: ec2-user instance-connect: true -
Set AWS credentials. Use a config file, environment variables (
AWS_PROFILE,AWS_REGION), or the standard credential chain. Thessm-session-clientconfig file is also honoured for VPC endpoint overrides. -
Connect. In VSCode, open the Command Palette (
Ctrl+Shift+P) β Remote-SSH: Connect to Hostβ¦ β select your configured host.
-T -o ConnectTimeout=15 -F /path/to/config hostname bash), the binary
detects SSH-compat mode, parses the SSH config to resolve the hostname to an EC2
instance ID, and establishes an SSH-over-SSM tunnel using the built-in Go SSH client.
Symlink mode: Alternatively, create a symlink named
ssh
pointing to ssm-session-client. When invoked as ssh, it
always operates in OpenSSH-compat mode.
Supported SSH Flags
When operating in OpenSSH-compat mode (VSCode or symlink), the following SSH flags are recognised:
| Flag | Description |
|---|---|
-T | Disable PTY allocation (used by VSCode) |
-t | Force PTY allocation |
-N | No remote command |
-o key=value | SSH options (StrictHostKeyChecking, UserKnownHostsFile, ConnectTimeout, etc.) |
-F <config> | SSH config file path |
-p <port> | Remote port |
-l <user> | Login username |
-i <keyfile> | Identity file (private key) |
-D <port> | Dynamic port forwarding (accepted but ignored) |
-v / -vv / -vvv | Verbosity (maps to log levels) |
SSH with EC2 Instance Connect
The instance-connect command combines SSM session tunnelling with
EC2 Instance Connect. It pushes an ephemeral SSH
public key to the instance before connecting, so no permanent key management is needed.
Authentication is controlled by the IAM action ec2-instance-connect:SendSSHPublicKey.
# SSH config using instance-connect as ProxyCommand
Host i-*
ProxyCommand ssm-session-client instance-connect %r@%h
# With a custom key pair
Host i-*
IdentityFile ~/.ssh/custom
ProxyCommand ssm-session-client instance-connect %r@%h --ssh-public-key-file=~/.ssh/custom.pub
# Automatically configure SSH
ssm-session-client instance-connect add-proxy-command ec2-user@i-0bdb4f892de4bb54c
# Then connect normally
ssh ec2-user@i-0bdb4f892de4bb54c
Port Forwarding
Securely tunnel TCP traffic to a port on a private instance without SSH, open firewall rules, or a bastion host.
# Forward local port 2222 β instance port 22
ssm-session-client port-forwarding i-0bdb4f892de4bb54c --remote-port 22 --local-port 2222
# Auto-assign local port (prints the assigned port)
ssm-session-client port-forwarding i-0bdb4f892de4bb54c --remote-port 443
# Forward to a different host reachable from the instance (e.g. a private RDS endpoint)
ssm-session-client port-forwarding i-0bdb4f892de4bb54c --remote-port 5432 --host db.internal --local-port 5432
# Using an alias
ssm-session-client port-forwarding devbox --remote-port 443 --local-port 8443
Flags
| Flag | Description | Example |
|---|---|---|
<target> | Instance ID, alias, tag, IP, or DNS name (resolved like other commands) | i-0123456789abcdef0, devbox, 10.0.1.5 |
--remote-port | Port on the instance (or --host) to forward to (required) | --remote-port 5432 |
--host | Optional hostname or IP reachable from the instance to forward to instead of the instance itself | --host db.internal |
--local-port | Local listening port (omit for auto-assign) | --local-port 8888 |
--port-forward-kbps | Outbound rate limit in kbps for smux mode (agents β₯ 3.0.196.0). Default 1000 (1 Mbps); set to 0 to disable | --port-forward-kbps 0 |
smux),
allowing multiple concurrent TCP connections over a single SSM session. Older
agents fall back to single-connection mode automatically.
--enable-reconnect=false to disable, or
--max-reconnects N to limit attempts. See
Automatic Reconnection for details.
ssh and ssh-direct commands, so the
flag has no effect there.
--port-forward-kbps throttles outbound data at the TCP stream level so
the agent's smux receive buffer never overflows. The default 1000 (1 Mbps)
is well above typical SSM throughput and transparent for interactive use. Lower it
(e.g. 450) if you see buffer overflow errors during large SCP/SFTP
uploads over high-latency internet paths.
Set
--port-forward-kbps 0 to disable throttling entirely, for example
when using a PrivateLink endpoint with very low RTT where the smux buffer is
unlikely to fill.
Common use cases
- Tunnel to a database on the instance:
ssm-session-client port-forwarding i-abc123 --remote-port 5432 --local-port 5432β connect tolocalhost:5432 - Tunnel through an instance to another service:
ssm-session-client port-forwarding bastion --remote-port 8080 --host internal-api.local --local-port 8080β forwards tointernal-api.local:8080as seen from the bastion instance - Multiple tunnels in parallel: Open multiple port-forwarding sessions in separate terminals
RDP (Windows targets only)
The rdp command (Windows build only) sets up a port-forwarding tunnel
to port 3389 and launches mstsc.exe automatically.
# Basic RDP connection
ssm-session-client rdp i-0bdb4f892de4bb54c
# Retrieve the Windows administrator password automatically
ssm-session-client rdp i-0bdb4f892de4bb54c --get-password --key-pair-file ~/.ssh/my-ec2-keypair.pem
# Custom RDP port and username
ssm-session-client rdp i-0bdb4f892de4bb54c --rdp-port 3390 --username admin
| Flag | Default | Description |
|---|---|---|
--rdp-port | 3389 | Remote RDP port on the EC2 instance |
--local-port | 0 (auto) | Local port for the SSM tunnel |
--get-password | false | Retrieve the Windows administrator password via the EC2 API |
--key-pair-file | β | EC2 key pair private key file (required with --get-password) |
--username | Administrator | RDP username |
When --get-password is used, the password is retrieved from the EC2 API,
decrypted with the provided key pair, and copied to the clipboard for pasting into the
RDP credentials prompt.
IAM: requires ec2:GetPasswordData in addition to standard SSM session permissions.
SSO Commands (sso-login / sso-logout)
Manage AWS Identity Center SSO credentials independently from session commands.
sso-login
Authenticate with AWS SSO and cache the token for later use. This is useful for pre-authenticating before running multiple session commands, or for environments where you want to separate credential management from session setup.
# Basic login
ssm-session-client sso-login --aws-profile my-sso-profile
# Login with custom timeout
ssm-session-client sso-login --aws-profile my-sso-profile --timeout 180
# Auto-open browser (if available)
ssm-session-client sso-login --aws-profile my-sso-profile --sso-open-browser
# Headless mode (prints URL to stdout, no browser)
SSC_SSO_OPEN_BROWSER=false ssm-session-client sso-login --aws-profile my-sso-profile
| Flag | Default | Description |
|---|---|---|
--aws-profile | Required | The AWS profile name from ~/.aws/config |
--timeout | 120 | Seconds to wait for browser approval |
--sso-open-browser | false | Open browser for the SSO OIDC URL (requires sso-open-browser: true in config or flag) |
sso-logout
Delete the cached SSO credentials for a profile. This removes the token from
~/.aws/sso/cache/, forcing a new login on the next session.
# Logout from a profile
ssm-session-client sso-logout --aws-profile my-sso-profile
# Logout via environment variable
SSC_AWS_PROFILE=my-sso-profile ssm-session-client sso-logout
| Flag | Required | Description |
|---|---|---|
--aws-profile | Yes | The AWS profile name to logout from |
When to use sso-login vs auto-login
Auto-login (set sso-login: true in config):
- Triggered automatically when running any session command if the cached token has expired
- Transparent to the user β login happens in the background
- Best for occasional users who just want to connect
Standalone sso-login/sso-logout commands:
- Give explicit control over when authentication happens
- Useful for scripting or automation workflows
- Best for power users who prefer to manage credentials separately
- Useful in CI/CD pipelines where you want to authenticate once, then run multiple commands
Troubleshooting
Log Files
Logs are written to both the console and a rotating log file. The log directory depends on your operating system:
| OS | Log directory |
|---|---|
| Windows | %USERPROFILE%\AppData\Local\ssm-session-client\logs |
| macOS | $HOME/Library/Logs/ssm-session-client |
| Linux / other Unix | $HOME/.ssm-session-client/logs |
Log files are rotated daily or when they reach 10 MB. The last three log files are kept.
Debug Mode
Set the log level to debug to capture verbose output including AWS API calls,
WebSocket messages, and connection lifecycle events.
Via environment variable (recommended for quick debugging)
SSC_LOG_LEVEL=debug ssm-session-client shell i-0bdb4f892de4bb54c
Via config file
log-level: debug
Via CLI flag
ssm-session-client shell i-0bdb4f892de4bb54c --log-level=debug
log-level: debug in your config file or via the environment variable.
Common Issues
macOS: "ssm-session-client cannot be opened"
macOS Gatekeeper quarantines downloaded binaries. Remove the quarantine flag:
xattr -d com.apple.quarantine /usr/local/bin/ssm-session-client
Windows: SmartScreen warning
Click More info β Run anyway. The binary is code-signed; SmartScreen may still warn on first run until the binary has sufficient reputation. Running from an Administrator PowerShell session bypasses this.
Connection times out / "target not connected"
- Verify the SSM Agent is running on the instance: in the EC2 console, check Systems Manager β Fleet Manager β instance status.
- Ensure the instance has the required IAM instance profile with the
AmazonSSMManagedInstanceCoremanaged policy. - If using VPC endpoints, confirm all four endpoints
(
ssm,ssmmessages,ec2,sts) are in place and their security groups allow traffic from the instance. - Enable debug logging (see above) and check the log file for API error details.
SSO: "no valid SSO token found"
- Ensure
sso-login: trueis set in your config (or--sso-loginflag). - If you cannot open a browser, copy the verification URL from the debug log or console and open it on another device.
- Verify the SSO profile in
~/.aws/confighassso_start_url,sso_account_id,sso_role_name, andsso_region.
Target not found
- Confirm the EC2 instance is in a running state.
- For tag-based lookup, ensure the IAM credentials have
ec2:DescribeInstances. - For alias lookup, confirm the alias is defined in the config file or
passed with
--alias.
Large file transfers stall or fail
All SSM session traffic flows through a single WebSocket between your machine and the SSM service, which relays it to the agent on the instance. The agent must acknowledge each batch before more is accepted β on high-latency internet paths this limits effective throughput to ~10β60 KB/s.
On the SSH ProxyCommand path (ssh), all SSH traffic β including
keepalives β shares this single pipe. If the agent is slow to acknowledge, the pipe
stalls and keepalives cannot get through, causing the SSH client to declare the
server unresponsive and drop the connection mid-transfer.
For files larger than ~10 MB, prefer one of these alternatives:
-
Amazon S3 (recommended): Upload from your machine with
aws s3 cp file.tar.gz s3://my-bucket/, then download on the instance via a shell session:aws s3 cp s3://my-bucket/file.tar.gz /tmp/. This bypasses the SSM WebSocket entirely and is not subject to its throughput limits. -
Port-forwarding + SCP (up to ~10 MB): Port-forwarding uses
stream multiplexing (smux) which isolates backpressure to the file transfer stream,
keeping session control traffic flowing independently. Rate limiting prevents
overwhelming the SSM agent during upload:
ssm-session-client port-forwarding i-abc123 --remote-port 22 --local-port 2222 --port-forward-kbps 1000 scp -P 2222 file.tar.gz ec2-user@localhost:/tmp/ - SSH ProxyCommand + SCP (up to ~1 MB): Works reliably for small to medium files. Larger transfers may stall due to SSM WebSocket backpressure on this path.
Why --port-forward-kbps exists
Multiplexed port forwarding uses smux
to run multiple TCP connections over one SSM session. The smux protocol has two
wire versions: v1, with no flow control, and v2, which adds per-stream flow control
via acknowledgement frames. ssm-session-client and the AWS-managed
session-manager-plugin/SSM Agent must agree on the protocol version, and
as of this writing the AWS side only negotiates smux v1 β it constructs its session
with the library's unmodified default config, which is v1. This isn't tied to a
specific SSM Agent version; every current agent speaks v1 only, so
ssm-session-client cannot unilaterally switch to v2.
Without v2's flow control, an unthrottled multiplexed stream can write data faster
than the SSM Agent's session-wide cap (roughly 500 kbps) can drain it. When that
happens the smux receive buffer fills, backpressure stalls the WebSocket reader, the
agent stops receiving acknowledgements, and it eventually terminates the session
mid-transfer. --port-forward-kbps (default 1000, 0 disables
it) paces outbound writes on each forwarded stream to stay under that cap and avoid
the stall.
AWS's own session-manager-plugin works around the same v1 limitation
internally: its client→agent bridge loop sleeps for 1 ms after every read/send
cycle, capping aggregate throughput to a fixed, hardcoded rate. It isn't user
configurable and doesn't provide per-stream fairness β it just keeps the shared pipe
under the agent's cap. --port-forward-kbps is the same category of
workaround, made tunable instead of fixed. The only real fix would be smux v2
support landing in AWS's session-manager-plugin/Agent; until then, this
flag isn't removable.
Contributing
Contributions are welcome! Please open an issue to discuss significant changes before submitting a pull request. All code must pass the lint and unit tests before merging.
Prerequisites
- Go (version from
go.mod) - golangci-lint for linting
- Acceptance tests only: An AWS account, and OpenTofu (or Terraform) for provisioning test infrastructure
Building from Source
# Clone the repository
git clone https://github.com/alexbacchin/ssm-session-client.git
cd ssm-session-client
# Build for the current platform
go build -o ssm-session-client .
# Cross-compile
GOOS=linux GOARCH=amd64 go build -o ssm-session-client-linux-amd64 .
GOOS=darwin GOARCH=arm64 go build -o ssm-session-client-darwin-arm64 .
GOOS=windows GOARCH=amd64 go build -o ssm-session-client.exe .
Unit Tests
# Run all unit tests with the race detector and coverage
go test ./... -race -coverprofile=coverage.out -covermode=atomic
# View coverage summary
go tool cover -func=coverage.out
# Run a single test
go test ./ssmclient/... -run TestTargetResolver -race
Linting
golangci-lint run
Key lint constraints enforced in this project:
- Maximum function length: 75 lines / 50 statements
- Maximum cognitive/cyclomatic complexity: 15
- Maximum line length: 132 characters
- Active linters:
errcheck,gosec,funlen,gocognit,gocyclo,revive,bodyclose
Acceptance Tests
The acceptance test suite provisions real AWS infrastructure (an EC2 instance with
no inbound SSH access), runs end-to-end tests against it, and tears everything down.
Tests are tagged acceptance and are excluded from the standard
go test ./... run.
What the tests cover
- Shell access by instance ID, tag, alias, private IP, and DNS TXT record
- SSH direct and SSH proxy connections
- EC2 Instance Connect with ephemeral key (instance-connect mode)
- SSH direct / SSH compat user and port defaults from app config file and CLI flags β no
~/.ssh/configrequired - SSH compat mode: VSCode-style flags, compound flags, piped stdin, TOFU host key prompt
- Host key verification: TOFU accept-new, known_hosts persistence,
/dev/nullbypass - Port forwarding (single and multiple concurrent connections)
- RDP tunnel (Windows runner, optional)
- SSO login with cached token (optional, requires an SSO profile)
- Error conditions: invalid target, missing region, session termination
1. Configure AWS credentials
The tests use standard AWS SDK credential chain. For CI, OIDC is recommended. For local runs, use a named profile or environment variables. The IAM principal needs permissions to manage EC2, SSM, IAM roles, and (optionally) Route53, KMS.
2. Provision infrastructure
cd test/scripts
make acceptance-prepare AWS_REGION=ap-southeast-2
# Wait for the SSM agent to come online
make acceptance-wait-ssm AWS_REGION=ap-southeast-2
3. Run the tests
make acceptance-run-local
4. Tear down
make acceptance-destroy AWS_REGION=ap-southeast-2
Full lifecycle (provision β test β destroy)
make acceptance AWS_REGION=ap-southeast-2
Interactive SSO test
# Requires a human to approve the device code in a browser
make acceptance-sso-interactive SSO_PROFILE=my-sso-profile
Windows / RDP tests
# Provision with a Windows instance
make acceptance-prepare TF_EXTRA_VARS="-var=create_windows_instance=true"
# Run on a Windows runner with the acceptance+windows build tags
go test ./test/acceptance/... -tags "acceptance windows" -run RDP -v