SSM Session Client

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 loginRequires 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
macOS Gatekeeper: The first time you run the binary, macOS may block it as an unidentified developer. Run the following command to remove the quarantine flag, then try again:

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
Windows binaries are code-signed. If Windows Defender SmartScreen warns about the binary, click More infoRun anyway. The binary is signed with a trusted certificate.

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.

PropertyValue
SubjectCN=Alex Bacchin, O=github.com/alexbacchin, C=AU
IssuerCN=Alex Bacchin, O=github.com/alexbacchin, C=AU
Serial Number1B:B7:DA:54:2F:FA:C2:4E:A4:F3:9C:A5:71:D2:F6:EF:4E:EE:EC:D3
Valid From21 February 2026
Valid To20 February 2029
Key AlgorithmRSA 4096-bit
Signature AlgorithmSHA-256 with RSA
Key UsageDigital Signature
Extended Key UsageCode Signing
SHA-256 Fingerprint75: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 Fingerprint51:EC:2D:E1:25:26:AE:25:AF:DB:BC:10:4D:11:53:FD:4A:DC:18:27
Subject Key IdentifierA3: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:

  1. Navigate to Computer Configuration → Windows Settings → Security Settings → Application Control Policies → AppLocker → Executable Rules
  2. Right-click → Create New Rule → select Publisher
  3. Browse to the signed ssm-session-client.exe binary
  4. 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 .exePropertiesDigital 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):

  1. Command-line flags β€” highest priority, override everything
  2. Environment variables β€” prefix SSC_ (e.g. SSC_AWS_REGION)
  3. 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/credentials and ~/.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 pathconfigSSC_CONFIG_FILEβ€”
Log level (debug, info, warn, error)log-levelSSC_LOG_LEVELβ€”
AWS profile nameaws-profileSSC_AWS_PROFILEAWS_PROFILE
AWS regionaws-regionSSC_AWS_REGIONAWS_REGION / AWS_DEFAULT_REGION
Trigger SSO login flow (true/false)sso-loginSSC_SSO_LOGINβ€”
Open browser for SSO (true/false)sso-open-browserSSC_SSO_OPEN_BROWSERβ€”
STS endpoint overridests-endpointSSC_STS_ENDPOINTAWS_ENDPOINT_URL_STS
EC2 endpoint overrideec2-endpointSSC_EC2_ENDPOINTAWS_ENDPOINT_URL_EC2
SSM endpoint overridessm-endpointSSC_SSM_ENDPOINTAWS_ENDPOINT_URL_SSM
SSM Messages WebSocket endpointssmmessages-endpointSSC_SSMMESSAGES_ENDPOINTβ€”
HTTP proxy URLproxy-urlSSC_PROXY_URLHTTPS_PROXY
Use Session Manager pluginssm-session-pluginSSC_SSM_SESSION_PLUGINβ€”
Enable auto-reconnectenable-reconnectSSC_ENABLE_RECONNECTβ€”
Max reconnection attemptsmax-reconnectsSSC_MAX_RECONNECTSβ€”
Port-forward outbound rate limit (kbps; 0 = unlimited)port-forward-kbpsSSC_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 pathssh-direct.ssh-key-fileSSC_SSH_DIRECT_SSH_KEY_FILEβ€”
Skip host key verificationssh-direct.no-host-key-checkSSC_SSH_DIRECT_NO_HOST_KEY_CHECKβ€”
Run a command instead of an interactive shellssh-direct.ssh-exec-commandSSC_SSH_DIRECT_SSH_EXEC_COMMANDβ€”
Push ephemeral key via EC2 Instance Connectssh-direct.instance-connectSSC_SSH_DIRECT_INSTANCE_CONNECTβ€”
Disable Instance Connect even if set in configssh-direct.no-instance-connectSSC_SSH_DIRECT_NO_INSTANCE_CONNECTβ€”
Default SSH username (avoids needing user@ in target or ~/.ssh/config)ssh-direct.ssh-userSSC_SSH_DIRECT_SSH_USERβ€”
Default SSH port (avoids needing :port in target or ~/.ssh/config)ssh-direct.ssh-portSSC_SSH_DIRECT_SSH_PORTβ€”
shell options (nested under shell: in config file)
SSM document to run for the sessionshell.document-nameSSC_SHELL_DOCUMENT_NAMEβ€”
SSM document parameters (config file only)shell.parametersβ€”β€”
SSM document parameter (CLI flag, repeatable)--parameter key=valueβ€”β€”
Note: 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:

  1. Current working directory
  2. User home directory ($HOME / %USERPROFILE%)
  3. Directory containing the ssm-session-client executable

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:

  1. Automatic login before each session: Set sso-login: true in your config to trigger a device-code login automatically when the cached token has expired.
  2. Standalone login/logout commands: Use sso-login and sso-logout subcommands to manage credentials separately from session commands.

Option 1: Automatic SSO login (during session setup)

  1. 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/config with sso_start_url, sso_account_id, sso_role_name, and sso_region.
  2. Set aws-profile and sso-login: true in your config file (or pass them as flags/env vars).
  3. (Optional) Set sso-open-browser: true if 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
Tip: Required VPC endpoints for full SSM functionality: 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):

PriorityMethodExample inputHow it works
1Instance IDi-0abc1234def56789Passed directly to the SSM API
2AliasdevboxLooks up the alias in your config, queries EC2 by tag β€” errors if more than one match
3EC2 tagName:web-serverQueries EC2 for running instances with tag key=value
4IP address10.0.1.5Queries EC2 by private or public IPv4
5DNS TXT recordweb.example.comResolves 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, set 0 for unlimited)
Automatic reconnection applies to multiplexed port forwarding only. Shell and SSH sessions are not reconnected automatically.

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.

KMS endpoint: If you are using VPC endpoints, you may also need to configure a KMS VPC endpoint (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"]
Precedence: --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.

KMS encryption: KMS-encrypted sessions are supported natively. No Session Manager plugin is required β€” encryption is handled using built-in AES-256-GCM routines. See Data Channel Encryption for details.

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)

  1. Ephemeral key via EC2 Instance Connect (when --instance-connect is used β€” only this method is tried; agent/key-file/password fallbacks are skipped)
  2. SSH agent (SSH_AUTH_SOCK)
  3. Private key file (--ssh-key or auto-discovered ~/.ssh/id_ed25519 / ~/.ssh/id_rsa)
  4. Password prompt (interactive)
Host key verification uses Trust-On-First-Use (TOFU): on the first connection the host key is saved to ~/.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@target positional arg > -l flag (compat mode) > ~/.ssh/config (compat mode) > ssh-direct.ssh-user config/env > built-in default (ec2-user)
  • Port: target:port positional arg > -p flag (compat mode) > ~/.ssh/config (compat mode) > ssh-direct.ssh-port config/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.

  1. Configure the binary path in VSCode. Open Settings (Ctrl+,) and search for remote.SSH.path, or add to settings.json:
    // Linux / macOS
    { "remote.SSH.path": "/usr/local/bin/ssm-session-client" }
    
    // Windows
    { "remote.SSH.path": "C:\\tools\\ssm-session-client.exe" }
  2. Configure your SSH config file (~/.ssh/config or %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-new

    Alternatively, 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/config is managed by policy:

    ssh-direct:
      ssh-user: ec2-user
      instance-connect: true
  3. Set AWS credentials. Use a config file, environment variables (AWS_PROFILE, AWS_REGION), or the standard credential chain. The ssm-session-client config file is also honoured for VPC endpoint overrides.
  4. Connect. In VSCode, open the Command Palette (Ctrl+Shift+P) β†’ Remote-SSH: Connect to Host… β†’ select your configured host.
How it works: When VSCode invokes the binary with OpenSSH-style flags (-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:

FlagDescription
-TDisable PTY allocation (used by VSCode)
-tForce PTY allocation
-NNo remote command
-o key=valueSSH 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 / -vvvVerbosity (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.

Linux targets only. EC2 Instance Connect is only available for Linux instances. Additionally, the EC2 Instance Connect API endpoint is not available via AWS PrivateLink β€” internet access (or an HTTP proxy) is required for the key push step.
# 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

FlagDescriptionExample
<target>Instance ID, alias, tag, IP, or DNS name (resolved like other commands)i-0123456789abcdef0, devbox, 10.0.1.5
--remote-portPort on the instance (or --host) to forward to (required)--remote-port 5432
--hostOptional hostname or IP reachable from the instance to forward to instead of the instance itself--host db.internal
--local-portLocal listening port (omit for auto-assign)--local-port 8888
--port-forward-kbpsOutbound 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
Multiplexed connections: For SSM agents v3.0.196.0 and above, port forwarding automatically uses stream multiplexing (via smux), allowing multiple concurrent TCP connections over a single SSM session. Older agents fall back to single-connection mode automatically.
Automatic reconnection: If the WebSocket drops (e.g. proxy idle-timeout), the session is re-established automatically and the local port keeps listening. Use --enable-reconnect=false to disable, or --max-reconnects N to limit attempts. See Automatic Reconnection for details.
Bandwidth limit and large file transfers: This applies to smux mode only (SSM agents β‰₯ 3.0.196.0). The agent's smux receive buffer (4 MB) fills within seconds if the client pushes data faster than the agent can forward it β€” causing the session to be terminated mid-transfer with errors like Connection reset by peer or Broken pipe during SCP/SFTP uploads tunnelled through port-forwarding. The SSH protocol's built-in flow control handles this correctly for 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 to localhost: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 to internal-api.local:8080 as 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
FlagDefaultDescription
--rdp-port3389Remote RDP port on the EC2 instance
--local-port0 (auto)Local port for the SSM tunnel
--get-passwordfalseRetrieve the Windows administrator password via the EC2 API
--key-pair-fileβ€”EC2 key pair private key file (required with --get-password)
--usernameAdministratorRDP 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
FlagDefaultDescription
--aws-profileRequiredThe AWS profile name from ~/.aws/config
--timeout120Seconds to wait for browser approval
--sso-open-browserfalseOpen 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
FlagRequiredDescription
--aws-profileYesThe 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:

OSLog 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
Debug output is always written to the log file (see paths above), even when the console is in a lower-verbosity mode. To see debug output in the terminal, set 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 AmazonSSMManagedInstanceCore managed 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: true is set in your config (or --sso-login flag).
  • 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/config has sso_start_url, sso_account_id, sso_role_name, and sso_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/config required
  • SSH compat mode: VSCode-style flags, compound flags, piped stdin, TOFU host key prompt
  • Host key verification: TOFU accept-new, known_hosts persistence, /dev/null bypass
  • 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
CI/CD: Acceptance tests run automatically on every release tag via release.yml. They also run nightly via acceptance.yml. AWS authentication uses GitHub OIDC (no long-lived secrets stored in GitHub).