Back to Blog
Phase 4 2026-02-05

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
  • Extensionspermit-pty, permit-port-forwarding (based on policy)
  • Critical optionssource-address restriction 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:

ComponentClassificationTier
systemctl restart nginxService managementT2 (MODIFY)
tail -f /var/log/nginx/error.logLog readingT0 (OBSERVE)
OverallMaximumT2 (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.