Integration: SSH: Using the p0cli SSH command

Last updated: April 29, 2025

P0 CLI – Comprehensive SSH Guide

Audience: Engineers with zero prior experience of p0 + SSH
Platforms: macOS 13 (or later) • Windows 10/11
Providers supported in this guide: AWS EC2Google Cloud (GCE)
CLI version: @p0security/cli v0.4.x (or later)


1  Why use p0 ssh instead of plain ssh?

Challenge (without P0)How P0 CLI solves it

🔒 Just‑in‑time access — you should not keep long‑lived users/keys on every VM.

p0 ssh requests, waits, and tears down access on demand, following your org’s approval workflow.

🗝 Key distribution — copying public keys to many hosts is painful.

P0 injects a temporary key pair only for the approved lifetime.

📜 Audit trail — who accessed what, when, and why?

Every p0 ssh run creates a Permission Request logged in your SIEM & Slack.

 IAM propagation delays (AWS) / IAP tunnel setup (GCP)

The CLI automatically polls & retries until the cloud confirms access is live.

💻 Multiple clouds & OSes

One syntax works on macOS, Windows (PowerShell/CMD), and Linux.

Take‑away: p0 ssh wraps the cloud‑specific plumbing so you type one command and get a secure shell when—and only when—you’re authorised.


2  Install & log in

StepmacOSWindows

Install Node 20+

brew install node@20 or nvm install 20

nvm install 20 or Node installer

Install P0 CLI

npm install -g @p0security/cli

Same in PowerShell

CLI in PATH?

p0 --version

Same

Log in to your org

p0 login <P0_ORG_ID>

Same

Why each step?

  1. Node – The CLI is a TypeScript binary distributed via npm.

  2. Global install – Makes p0 available in every terminal.

  3. Login – Stores an encrypted identity file that authenticates every CLI call.

Tip (Windows): Use the modern Windows Terminal; quoting is more predictable than the old console.


3  Anatomy of the p0 ssh command

p0 ssh <destination> [command [args…]] [p0‑options] -- [raw‑SSH‑options]

Segment by segment:

PartMandatory?Purpose

<destination>

friendly alias registered with P0, not an IP/hostname. It tells the backend which VM you want.

command [args…]

If supplied, P0 passes it to the remote shell non‑interactively and exits with the command’s status code. Omit to open an interactive shell.

p0‑options

Flags interpreted by the P0 CLI itself (see next table).

--

Literal double‑dash separates P0 flags from raw OpenSSH flags so they aren’t swallowed by yargs.

raw‑SSH‑options

Forwarded unchanged to the underlying ssh/scp binary. Ideal for -L-o, etc.

3.1  Every P0‑specific flag explained

FlagWhy / when to use itEffect on backend

--provider <aws|gcloud|azure>

Alias exists in multiple clouds or your default differs from the alias prefix.

Pins the request to that cloud’s driver.

--reason "text"

Your approvers need context.

Embeds the string in the Permission Request. May be mandatory by policy.

--sudo

You’ll need root privileges once connected.

Backend flags the session; approvers see the elevated scope. The session user is added to sudoers.

--debug

Troubleshooting, CI logs.

CLI prints verbose progress + repro commands you can replay manually.

--parent <resource>

Org uses hierarchical approvals (project, account…).

Routes the request to the right approver group.

--approved-only

You’re in a script that can’t wait for manual approval.

CLI fails fast if access isn’t already pre‑approved.


4  SSH into AWS EC2 – step by step (with reasoning)

  1. Discover aliases you could reach (optional but helpful):

    p0 ls ssh destination --provider aws
    

    Why: Lists registered instances, not necessarily ones you already have access to. Saves guessing.

  2. Start a session:

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

    What happens under the hood

    1. CLI calls P0 API → creates Permission Request tagged sudo? reason provider=aws.

    2. Slack bot notifies approvers. CLI polls every 2 s.

    3. Once Approved, P0:

      • Generates a short‑lived EC2 Session Manager (SSM) policy.

      • Injects your public key onto the instance.

    4. IAM changes take ~8 min to propagate. CLI retries aws ssm start-session until the error patterns disappear.

    5. OpenSSH finally connects via -o ProxyCommand="aws ssm start-session …".

  3. Interactive vs one‑off commands

    # Interactive shell
    

a) p0 ssh app1-prod

Non‑interactive health check

b) p0 ssh app1-prod -- "cat /var/app/version.txt"

*Why*: scripts (CI/CD) can treat exit codes programmatically.

4. **Port forwarding example** (Mac):
```bash
p0 ssh db-prod --provider aws -- -L 5439:localhost:5432

Why: Tunnel local port 5439 → remote Postgres 5432 without opening security‑group rules.

  1. Understanding automatic SSH options

    Option injected by CLIWhy

    -i <temp_key>

    Guarantees OpenSSH tries only the ephemeral key – avoids “Too many authentication failures”.

    -o IdentitiesOnly=yes

    Complements -i. Prevents agent from spraying other keys.

    -v

    Always enabled so the parser can detect error patterns; echoed to screen only with --debug.


5  SSH into Google Cloud (GCE) – step by step

  1. Authenticate the gcloud CLI (first time / every 12 h)

    gcloud auth login
    gcloud config set project <PROJECT_ID>
    

    Whyp0 ssh drives IAP TCP Tunnel, which in turn relies on your local gcloud credentials.

  2. Open a session

    p0 ssh analytics-vm --provider gcloud --reason "ad‑hoc data pull"
    

    Under the hood

    1. Permission Request created.

    2. Once approved, backend adds your account to the VM’s OS Login w/ SSH keys.

    3. CLI spawns gcloud compute start-iap-tunnel as a ProxyCommand. Propagation ≤ 1 min.

  3. Reverse tunnel example (expose web UI running on VM → localhost:8080)

    p0 ssh analytics-vm --provider gcloud -- -NR '*:8080:localhost:8088' -o "GatewayPorts yes"
    

    Flags explained

    FlagWhy

    -N

    No remote command → keep tunnel open.

    -R '*:8080:localhost:8088'

    Reverse‑forward all interfaces on VM :8080 → your laptop :8088.

    -o GatewayPorts yes

    Allow remote users on VM to hit the bound port (needed for *:).

  4. Sudo example

    p0 ssh db-vm --provider gcloud --sudo --reason "apply emergency patch"
    

    Why: Adds your temp OS‑Login profile to /etc/sudoers.d/p0‑<id>. Approvers see elevated scope.


6  Windows‑specific quoting pitfalls

PowerShell treats single quotes literally and uses double quotes for interpolation. When nesting quotes for the trailing SSH section, escape wisely:

p0 ssh web-prod --provider aws -- "-NR '*:8080:localhost:8080' -o \"GatewayPorts yes\""

👉 Rule of thumb:

  1. Wrap the entire trailing part in double quotes.

  2. Inside that, escape inner double quotes with backslashes.


7  Session teardown & security hygiene

  • Ephemeral key – stored in ~/.p0/keys (macOS/Linux) or %USERPROFILE%\.p0\keys (Windows). Deleted automatically after the process ends.

  • Cloud role bindings – P0 schedules a reaper job that strips your IAM entry when the request expires (default 1 h).

  • Ctrl‑C – sends an AbortController signal; CLI runs any provider teardown hooks (e.g., deleting IAP tunnel). Safe to kill.


8  Cheat‑sheet – common tasks (with purpose)

GoalCommandWhy it works

List destinations you currently can request

p0 ls ssh destination

Reads catalogue published by org admin. No IAM needed.

Assume AWS role then SSH with same temp creds

$(p0 aws role assume dev-admin) && p0 ssh bastion --provider aws

First sub‑shell exports STS env vars consumed by second command.

Copy build artefacts to VM

p0 scp -r ./dist api-vm:/opt/apps --provider aws

scp path semantics preserved; CLI handles Session Manager transport.

Dry‑run and print reproducible commands

p0 ssh api-vm --debug -- -v

--debug prints all child commands + OpenSSH ‑v prints handshake details.


9  Need more help?

Happy secure shelling! 🎉