The SSH Adapter
How iddio proxies SSH connections with the same classify-enforce-audit pipeline as Kubernetes. JIT SSH certificates, compound command classification, and PTY relay.
Why SSH Matters
Kubernetes isn’t the only way AI agents access infrastructure. Many production operations — debugging a bare-metal node, tailing a log file on a VM, running a database migration — happen over SSH. If iddio only proxies kubectl, agents that need SSH access fall back to uncontrolled, unaudited connections.
The SSH adapter extends iddio’s classify-enforce-audit pipeline to SSH connections. Same five tiers, same policy engine, same audit log format — different protocol.
Architecture
The SSH adapter runs as a separate listener alongside the HTTPS proxy. It implements an SSH server that accepts connections from agents, classifies the requested command, enforces policy, and if approved, opens a proxied connection to the target host.
Agent SSH Client → Iddio SSH Adapter → Target Host
├─ Classify command
├─ Evaluate policy
├─ Escalate if needed
├─ Audit log event
└─ PTY relay (if interactive)
The agent’s SSH config points at iddio instead of the real host. From the agent’s perspective, it’s a normal SSH connection.
JIT SSH Certificates
Instead of distributing static SSH keys, iddio mints short-lived SSH certificates for each connection. The certificate is signed by iddio’s CA and includes:
- Principal — the SSH username on the target host
- Valid after / valid before — typically a 5-minute window
- Extensions —
permit-pty,permit-port-forwarding(based on policy) - Critical options —
source-addressrestriction to the proxy’s IP
cert := &ssh.Certificate{
Key: agentPubKey,
CertType: ssh.UserCert,
KeyId: fmt.Sprintf("iddio:%s:%s", agent, host),
ValidPrincipals: []string{principal},
ValidAfter: uint64(now.Unix()),
ValidBefore: uint64(now.Add(5 * time.Minute).Unix()),
Permissions: ssh.Permissions{
Extensions: extensions,
},
}
cert.SignCert(rand.Reader, caKey)
The target host trusts iddio’s CA via TrustedUserCAKeys in sshd_config. No per-agent key distribution needed.
Compound Command Classification
SSH commands are more complex than Kubernetes API calls. A single SSH session might run:
ssh prod-web-1 "systemctl restart nginx && tail -f /var/log/nginx/error.log"
The SSH classifier parses compound commands (connected by &&, ||, ;, |) and classifies each component individually. The overall tier is the maximum of all components:
| Component | Classification | Tier |
|---|---|---|
systemctl restart nginx | Service management | T2 (MODIFY) |
tail -f /var/log/nginx/error.log | Log reading | T0 (OBSERVE) |
| Overall | Maximum | T2 (MODIFY) |
This ensures that piping a destructive command after a benign one doesn’t bypass classification.
PTY Relay
Interactive SSH sessions (ssh -t host) require a pseudo-terminal (PTY). The SSH adapter allocates a PTY on the proxy side, connects it to the target host, and relays bytes bidirectionally — identical to how exec session recording works for kubectl exec.
Every byte flowing through the PTY is recorded to the session log. The recording is forensic-quality: you can replay exactly what the agent typed and what the host responded, including control characters, terminal escape sequences, and timing.
Policy Configuration
SSH rules use the same YAML policy format with SSH-specific scope fields:
agents:
claude-code:
rules:
- protocol: ssh
hosts: ["prod-web-*", "staging-*"]
ssh_principals: ["deploy", "readonly"]
tiers:
0: allow # read-only commands
1: allow # runbook-matched ops
2: escalate # service management
3: escalate # package install, config changes
4: deny # rm -rf, shutdown, reboot
The hosts field supports glob patterns. The ssh_principals field restricts which SSH usernames the agent can use on the target host.
Audit Log Integration
SSH events use the same audit log format with SSH-specific fields:
{
"timestamp": "2026-02-05T14:30:22Z",
"agent": "claude-code",
"protocol": "ssh",
"host": "prod-web-1",
"principal": "deploy",
"command": "systemctl restart nginx",
"tier": 2,
"decision": "escalate",
"session_id": "ssh_a1b2c3",
"hash": "7f8a9b...",
"prev_hash": "6e7d8c..."
}
The hash and prev_hash fields integrate into the same hash chain as Kubernetes audit events. A single verification pass covers both protocols.
Try It Yourself
Iddio is open source. Deploy a zero-trust command proxy for your AI agents in minutes.