#ssm #tui #ec2 #aws-ec2 #aws #vpn

app awsx2

AWS management CLI and interactive TUI — EC2 lifecycle, SSM tunnels, ALB-aware routing, nginx reverse proxy, and Client VPN with SAML

17 releases (6 breaking)

0.7.1 Mar 23, 2026
0.7.0 Mar 5, 2026
0.6.9 Mar 5, 2026
0.5.0 Mar 2, 2026
0.1.0 Feb 25, 2026

#605 in Command line utilities

MIT license

235KB
5K SLoC

awsx2

A fast AWS management CLI and interactive TUI built in Rust. Manages EC2 instances, SSM tunnels, local reverse proxies, and AWS Client VPN with SAML authentication through a single tool.

awsx2          # launch interactive TUI
awsx2 list     # CLI mode — list all instances

Features

  • Dual-mode — full-screen TUI for interactive use, CLI for scripts and automation
  • EC2 management — list, start, stop, force-stop, switch instance types (GPU/CPU)
  • Smart tunneling — SSM port-forwarding with ALB-aware routing, security group analysis, and bastion fallback
  • Client VPN — AWS Client VPN with SAML/SSO authentication, headless browser MFA, and automatic DNS configuration
  • Reverse proxy — auto-configures nginx + /etc/hosts so internal URLs work directly in the browser
  • Cross-platform — macOS (Homebrew) and Linux (Debian/Ubuntu, RHEL/CentOS, Amazon Linux)

Prerequisites

  • AWS CLI v2 with SSO configured
  • Session Manager Plugin
  • SSM Agent running on target EC2 instances
  • nginx (only for --proxy feature)
  • Chromium/Chrome (only for vpn connect — headless SAML auth)
  • AWS VPN Client or OpenVPN (only for vpn connect)

Installation

One-liner (downloads pre-built binary from GitHub Releases):

curl -fsSL https://raw.githubusercontent.com/JeanMaximilienCadic/myawesomescripts/master/aws/awsx2/install.sh | bash

Detects your OS and architecture automatically. Installs to /usr/local/bin (override with INSTALL_DIR).

From crates.io:

cargo install awsx2

From source:

cargo build --release
cp target/release/awsx2 /usr/local/bin/

CLI Usage

Run awsx2 <command>. Instance commands accept --name or read from the INSTANCE_NAME environment variable.

Instance Management

awsx2 list                          # List all EC2 instances
awsx2 status --name my-server       # Show instance details
awsx2 start --name my-server        # Start an instance
awsx2 stop --name my-server         # Graceful stop
awsx2 force-stop --name my-server   # Force stop (immediate)
awsx2 switch gpu --name my-server   # Switch to g4dn.4xlarge
awsx2 switch cpu --name my-server   # Switch to m6i.2xlarge

Authentication

awsx2 login                # SSO login using $AWS_PROFILE
awsx2 login my-profile     # SSO login with explicit profile

DNS Resolution

awsx2 resolve https://app.internal.example.com

Traces the full path: hostname → DNS → ALB → target group → EC2/Fargate backend.

Tunnels

Direct tunnel to an EC2 instance by name pattern:

awsx2 tunnel web-server 8080 8000
#              ^pattern  ^local ^remote

URL tunnel with smart ALB resolution (auto-detects bastion and remote port):

awsx2 tunnel-url https://app.internal.example.com 8080

The resolution chain: URL → ALB match → healthy target group → security group rules → SSM-online hop instance. Falls back to trying all available bastions if ALB resolution fails.

URL tunnel with reverse proxy so the URL works directly in the browser:

awsx2 tunnel-url https://app.internal.example.com 8080 --proxy

This additionally:

  1. Writes an nginx config forwarding port 80 → localhost:8080
  2. Adds 127.0.0.1 app.internal.example.com to /etc/hosts
  3. Reloads nginx and flushes DNS cache

DNS tunnel (resolve hostname, tunnel to the resolved IP):

awsx2 tunnel-dns https://app.internal.example.com 8080 8501

Remote tunnel via a specific bastion to an arbitrary host:

awsx2 tunnel-remote bastion 10.0.1.42 8080 8501
#                   ^bastion ^target   ^local ^remote

Tunnel management:

awsx2 tunnel-test 8080    # Check if port is open
awsx2 tunnel-stop         # Kill all SSM tunnels + clean up proxies

SSH via SSM

Seamless SSH to EC2 instances using SSM as transport — no bastion hosts, no public IPs, no key management.

Generate ~/.ssh/config entries for all running, SSM-online instances:

awsx2 ssh-config              # Update ~/.ssh/config
awsx2 ssh-config --dry-run    # Preview without writing
awsx2 ssh-config --user ubuntu # Override SSH user (default: ec2-user)

This creates a managed block in ~/.ssh/config with entries like:

# BEGIN awsx2-managed
Host dev-risk-model-ec2
    User ec2-user
    ProxyCommand awsx2 ssm-proxy --name dev-risk-model-ec2 --port %p --region ap-northeast-1
# END awsx2-managed

Running ssh-config again replaces the managed block and removes any stale duplicate Host entries outside it.

Then just SSH normally:

ssh dev-risk-model-ec2

On first connection, ssm-proxy automatically pushes your SSH public key to the instance via SSM send-command (cached for 7 days). Subsequent connections skip this step.

The ssm-proxy subcommand resolves the EC2 Name tag to an instance ID dynamically, so instance replacements (e.g. ASG rotations) are handled transparently.

VPN

Connect to AWS Client VPN endpoints that use SAML/SSO authentication. Credentials are saved locally so you only need to enter the MFA code each time.

# One-time setup — saves credentials to ~/.config/awsx2/vpn.json
awsx2 vpn setup \
  --username user@example.com \
  --password 'secret' \
  --ovpn /path/to/client.ovpn \
  --dns-server 10.0.0.2 \
  --dns-domain '~internal.example.com'

# Connect (prompts for MFA if not provided)
sudo -E awsx2 vpn connect 123456

# Check status
awsx2 vpn status

# Disconnect
sudo -E awsx2 vpn disconnect

The connect flow:

  1. Sends initial auth to VPN server to obtain SAML challenge URL
  2. Launches headless Chromium to complete SSO login (username, password, MFA)
  3. Captures SAML response via local HTTP callback
  4. Reconnects to VPN with the SAML token (uses AWS patched OpenVPN if available)
  5. Configures DNS routing via resolvectl for the specified domain

Requires sudo -E to create the tun interface and configure DNS. The -E flag preserves your AWS environment variables.

TUI

Launch with awsx2 (no arguments). Navigate with keyboard — no mouse required.

Global Keys

Key Action
Tab / Shift+Tab Switch tabs
? Toggle help overlay
q / Ctrl+c Quit

Instances Tab

Key Action
j / k / Up / Down Navigate
g / G Jump to first / last
/ Filter by name, ID, or type
Esc Clear filter
s Start instance
S Stop instance
f Force-stop instance
r Refresh

Columns: Instance ID, Name, Type, State, SSM Status, Tunnel, Private IP. States are color-coded: green = running, red = stopped, yellow = pending/stopping.

Tunnels Tab

Key Action
j / k / Up / Down Navigate
n New tunnel by instance name (wizard)
u New tunnel by URL (smart ALB resolution)
b New tunnel via bastion (wizard)
d / Delete Stop selected tunnel
A Stop all tunnels
r Refresh

Each tunnel shows real-time status with latency measurement:

  • OK 42ms — tunnel active, measured round-trip
  • OPEN — port open, not yet probed
  • DOWN — tunnel unreachable

Tunnels auto-refresh every ~15 seconds.

Tools Tab

Key Action
j / k / Up / Down Navigate menu
Enter Execute

Available tools:

  • Switch Profile — select from ~/.aws/config profiles
  • Switch Region — change AWS region
  • Login — SSO login
  • Resolve URL — trace DNS to backend resource
  • Test Port — check if a tunnel port is open
  • Stop All Tunnels — kill all SSM sessions

VPN Tab

Key Action
j / k / Up / Down Navigate menu
Enter Execute
r Refresh status

Available actions:

  • Connect — enter MFA code and connect to VPN
  • Disconnect — stop active VPN session
  • Setup — configure SSO credentials and .ovpn path (multi-step wizard)
  • Status — check VPN connection state, IP, and PID

Reverse Proxy

The --proxy flag on tunnel-url sets up nginx so the original hostname works in your browser over the SSM tunnel.

How It Works

  1. Writes a site config to nginx (proxy_pass to the tunnel's local port)
  2. Adds a /etc/hosts entry pointing the hostname to 127.0.0.1
  3. Reloads nginx and flushes the DNS cache
  4. awsx2 tunnel-stop cleans everything up automatically

Platform Support

macOS (Homebrew) Linux (Debian/Ubuntu) Linux (RHEL/CentOS)
Config path /opt/homebrew/etc/nginx/servers/ sites-available/ + symlink to sites-enabled/ /etc/nginx/conf.d/
Nginx reload nginx -s reload systemctl reload nginx systemctl reload nginx
DNS flush dscacheutil + mDNSResponder resolvectl / systemd-resolve resolvectl / nscd

nginx Installation

# macOS
brew install nginx

# Debian / Ubuntu
sudo apt install nginx

# RHEL / CentOS / Amazon Linux
sudo yum install nginx

Architecture

awsx2
├── main.rs          # Entry point, CLI (clap) + TUI event loop
├── aws.rs           # AWS CLI wrapper (EC2, SSM, ALB, SG, DNS)
├── tunnel.rs        # SSM tunnel lifecycle (start, detect, stop, probe)
├── proxy.rs         # nginx reverse proxy + /etc/hosts management
├── vpn.rs           # Client VPN with SAML auth (headless Chrome, config persistence)
├── models.rs        # Domain types (Instance, TunnelProcess, VpnConfig, etc.)
├── error.rs         # Error types (AppError enum with thiserror)
└── tui/
    ├── app.rs       # Application state, background task channels
    ├── ui.rs        # Layout, colors, popup rendering
    └── pages/
        ├── instances.rs   # Instances tab (table + key handlers)
        ├── tunnels.rs     # Tunnels tab (table + creation wizards)
        ├── tools.rs       # Tools tab (menu + actions)
        └── vpn.rs         # VPN tab (connect, disconnect, setup)

Key design decisions:

  • Shells out to aws CLI rather than using the AWS SDK — leverages existing SSO/credential configuration with zero extra setup
  • Tunnels are detached child processes, discovered by parsing ps output for session-manager-plugin
  • TUI runs background operations on threads, communicates via mpsc channels
  • No runtime dependencies beyond the AWS CLI and session manager plugin

Dependencies

Crate Purpose
ratatui Terminal UI framework
crossterm Terminal I/O (raw mode, key events)
clap CLI argument parsing with env var support
serde + serde_json AWS CLI JSON output parsing
thiserror Error type derivation
libc Unix signal handling (SIGTERM for tunnel cleanup)
headless_chrome Chrome DevTools Protocol for SAML browser automation
tiny_http Lightweight HTTP server for SAML callback listener
regex Parsing SAML URL and session ID from OpenVPN output
url URL parsing for SAML form data extraction
dirs Platform-correct config directory (~/.config/awsx2/)
tempfile Secure temporary files for OpenVPN configs and credentials
exec Unix exec replacement for SSM proxy (replaces process without forking)

Environment Variables

Variable Used by
AWS_PROFILE Default profile for all AWS operations
AWS_DEFAULT_REGION Default region
INSTANCE_NAME Default instance name for CLI commands

License

MIT

Dependencies

~14–30MB
~434K SLoC