p0cli: SSH: AWS: SSH into a AWS EC2 via SSM

Last updated: April 29, 2025

P0 CLI – SSH into AWS EC2

Audience: Engineers new to P0 + AWS
Platforms: macOS 13+, Windows 10/11
CLI version: @p0security/cli v0.4.x+
AWS tooling: AWS CLI v2, Session Manager Plugin v1.2+


1  Why wrap AWS SSH with P0?

Traditional hurdleHow p0 ssh fixes it

🔑 Key rotation – distributing & revoking public keys on hundreds of hosts.

Generates an ephemeral key pair and injects it only for the approved session duration.

📝 Change control / approvals – Slack tickets drafted manually.

Each invocation creates a Permission Request with reason + sudo scope; approvers click to grant.

🛂 Audit trail – who connected, when, and as which OS user?

P0 logs are exported to your SIEM; session ID captured.

 IAM propagation delay (~8 min for SSM policies).

CLI detects transient AccessDenied errors and transparently retries until access is live.

🌍 Bastion management – public IPs, security groups.

Uses AWS SSM Session Manager as a tunnel – zero inbound ports required.


2  Prerequisites & installation

RequirementmacOSWindows

Node.js ≥ 20

brew install node@20 or nvm install 20

nvm install 20 or Node installer

P0 CLI

npm i -g @p0security/cli

Same in PowerShell

AWS CLI v2

brew install awscli

Official MSI or choco install awscli

Session Manager Plugin

brew install --cask session-manager-plugin

Installed with AWS CLI v2 MSI, or get separate MSI

Login to AWS

aws sso login --profile <name> or long‑lived keys in ~/.aws/credentials

Same

Login to P0

p0 login <P0_ORG_ID>

Same

Why each piece?

  1. AWS CLI provides the aws ssm start-session command that acts as an SSH ProxyCommand.

  2. Session Manager plugin translates the bidirectional stream into websockets consumed by SSM.

  3. AWS credentials (SSO or keys) authorise the tunnel; P0 does not supply them – it only grants the policy.

  4. P0 identity authenticates you to the P0 back‑end so it can file requests on your behalf.


3  Discover EC2 instance aliases

p0 ls ssh destination --provider aws

Why run this first? – It reveals the alias → EC2 instance‑ID mapping configured by your platform team, so you know the correct <destination> token.


4  Command anatomy

p0 ssh <destination> --provider aws [p0-flags] -- [raw SSH flags]

SegmentPurpose

<destination>

Friendly alias (e.g. web-prod). Distinct from hostname or IP.

--provider aws

Forces AWS driver (needed only when org has multi‑cloud aliases).

p0-flags

Influence Permission Request (reason, sudo…).

--

Literal double-dash – marks the start of flags passed untouched to ssh.

raw SSH flags

Classic OpenSSH options: -L-N-o, etc.

4.1  P0-specific flags (full AWS list)

FlagWhen to useBackend effect

--reason "text"

Give context: on‑call, deploy, audit.

Shown in Slack + audit JSON.

--sudo

Need root privileges.

Tags request sudo=true; backend adds you to /etc/sudoers.d/.

--parent <acct>

Org uses nested approval scopes.

Routes request to correct approver group.

--debug

Diagnose or copy commands.

CLI prints every child process incl. fully‑formed aws & sshcommands.

--approved-only

Scripts must not hang waiting for manual sign‑off.

Immediate failure (exit 1) if access isn’t already approved.


5  Hands‑on walkthroughs

5.1  Open an interactive shell

p0 ssh web-prod \
       --provider aws \
       --reason "on-call: investigate 5xx spikes"

Under the hood

  1. CLI P0 API → Permission Request created with fields {dest:web-prod,provider:aws,reason:"…"}.

  2. Slack message posts → approver clicks Approve.

  3. P0 grants you a temporary SSM session policy + uploads your public key to the instance.

  4. IAM propagates (~8 min). CLI keeps launching aws ssm start-session inside the ProxyCommand until it no longer sees AccessDenied in stderr.

  5. OpenSSH handshake succeeds; shell opens.

5.2  Run a one‑off remote command

p0 ssh web-prod --provider aws -- "hostname && uptime"

Why? – Good for CI health checks; exit code bubbles up to pipeline.

5.3  Forward a local port

p0 ssh db-prod --provider aws -- -L 5432:localhost:5432

ElementPurpose

-L 5432:localhost:5432

Expose remote Postgres on local 5432. (Traffic travels over SSM tunnel.)

5.4  Request sudo privileges

p0 ssh bastion --provider aws --sudo \
       --reason "rotate TLS certs"

Effect – Backend writes a short‑lived file in /etc/sudoers.d/p0-<requestId> granting NOPASSWD ALL to your temp user.

5.5  Generate reproducible commands (for debugging)

p0 ssh api-vm --provider aws --debug -- -v
  • --debug prints both:

    • Fully resolved AWS CLI command with region/profile.

    • Final ssh command (with escaped options) you could run manually if you already had the temp key + policy.

  • Extra -v feeds more handshake logs to your terminal.


6  Automatic SSH options that P0 injects (and why)

OptionRationale

-i ~/.p0/keys/<ephemeral>.pem

Force OpenSSH to present only the temp key – avoids Too many authentication failures.

-o IdentitiesOnly=yes

Complements -i; prevents agent from offering other keys.

-o ProxyCommand="aws ssm start-session …"

Reroutes traffic through SSM.

-v

Always added so parser can detect error strings; printed only if --debug.


7  Windows quoting primer

PowerShell rules:

  1. Surround the entire SSH tail in double quotes.

  2. Escape inner double quotes with back‑slash.

p0 ssh web-prod --provider aws -- "-L 8080:localhost:80 -o \"ConnectTimeout=5\""

8  Troubleshooting matrix

SymptomLikely causeResolution

AccessDeniedException … not authorized to perform ssm:StartSession

IAM policy hasn’t propagated yet.

Wait – CLI keeps retrying up to 8 min. See progress logs with --debug.

Unable to locate credentials in ProxyCommand

AWS CLI can’t find env vars/SSO profile.

Run aws sso login --profile <name>or set AWS_PROFILE.

error: exec: "session-manager-plugin": …

Plugin missing.

Install Session Manager Plugin (see §2).

SSH connects but port forward won’t bind

Forgot GatewayPorts yeson reverse tunnels.

Add -o "GatewayPorts yes" after --.


9  Security & cleanup

  • Ephemeral key: stored in ~/.p0/keys (mac) or %USERPROFILE%\.p0\keys (Win) → auto‑deleted on exit.

  • SSM policy & OS account: P0 schedules a reaper job (default TTL = 1 h) to revoke both.

  • AbortCtrl‑C triggers an AbortController; CLI tears down SSM tunnel & runs provider teardown.


10  Quick command vault

# List aliases you can request (any provider)
p0 ls ssh destination

# Copy build artefact to EC2 (uses SSM + scp)
p0 scp ./app.tgz web-prod:/opt \
        --provider aws \
        --reason "deploy app 2025‑04‑29"

# Dry‑run – print full aws+ssh for replication
p0 ssh web-prod --provider aws --debug -- -v

11  Need help?

Happy secure shelling into AWS! 🎉