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/hostsso 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
--proxyfeature) - Chromium/Chrome (only for
vpn connect— headless SAML auth) - AWS VPN Client or OpenVPN (only for
vpn connect)
Installation
CLI Usage
Run awsx2 <command>. Instance commands accept --name or read from the INSTANCE_NAME environment variable.
Instance Management
Authentication
DNS Resolution
Traces the full path: hostname → DNS → ALB → target group → EC2/Fargate backend.
Tunnels
Direct tunnel to an EC2 instance by name pattern:
# ^pattern ^local ^remote
URL tunnel with smart ALB resolution (auto-detects bastion and remote port):
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:
This additionally:
- Writes an nginx config forwarding port 80 → localhost:8080
- Adds
127.0.0.1 app.internal.example.comto/etc/hosts - Reloads nginx and flushes DNS cache
DNS tunnel (resolve hostname, tunnel to the resolved IP):
Remote tunnel via a specific bastion to an arbitrary host:
# ^bastion ^target ^local ^remote
Tunnel management:
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:
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:
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
# Connect (prompts for MFA if not provided)
# Check status
# Disconnect
The connect flow:
- Sends initial auth to VPN server to obtain SAML challenge URL
- Launches headless Chromium to complete SSO login (username, password, MFA)
- Captures SAML response via local HTTP callback
- Reconnects to VPN with the SAML token (uses AWS patched OpenVPN if available)
- Configures DNS routing via
resolvectlfor 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/configprofiles - 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
- Writes a site config to nginx (
proxy_passto the tunnel's local port) - Adds a
/etc/hostsentry pointing the hostname to127.0.0.1 - Reloads nginx and flushes the DNS cache
awsx2 tunnel-stopcleans 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
# Debian / Ubuntu
# RHEL / CentOS / Amazon Linux
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
awsCLI rather than using the AWS SDK — leverages existing SSO/credential configuration with zero extra setup - Tunnels are detached child processes, discovered by parsing
psoutput forsession-manager-plugin - TUI runs background operations on threads, communicates via
mpscchannels - 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