Skip to main content

SSH architecture

KubeVPN's SSH subsystem provides two core capabilities:

  1. SSH Jump Host — Access private Kubernetes clusters through SSH tunnels
  2. SSH Remote Terminal — Create interactive terminal sessions on remote servers with optional TUN tunnel

SSH Jump Host​

When the Kubernetes API Server is in a private network and not directly reachable, use SSH tunnels to access it:

┌──────┐    SSH tunnel    ┌──────────┐       ┌──────────────┐
│ Local ├────────────────►│ Jump Host├──────►│ K8s API Server│
└──────┘ 127.0.0.1:N └──────────┘ └──────────────┘

Connection Flow​

  1. Establish SSH connection to the jump host
  2. Create a local port-forward through SSH to the API server
  3. Rewrite kubeconfig to point to the local forwarded port
  4. All subsequent kubectl operations go through the SSH tunnel

Authentication Methods​

MethodFlags
Password--ssh-password
Key file--ssh-keyfile
SSH config alias--ssh-alias
GSSAPI/Kerberos password--gssapi-password
GSSAPI keytab--gssapi-keytab
GSSAPI credential cache--gssapi-cache

ProxyJump Support​

Supports multi-hop SSH chains via --ssh-jump or SSH config ProxyJump:

┌──────┐     ┌──────┐     ┌──────┐     ┌──────┐                 ┌────────────┐
│ pc ├────►│ ssh1 ├────►│ ssh2 ├────►│ ssh3 ├─────►... ─────► │ api-server │
└──────┘ └──────┘ └──────┘ └──────┘ └────────────┘

SSH Data-plane Bypass (auto-detect)​

When you connect with --ssh-addr and that SSH host happens to be a Kubernetes node, kubevpn can tunnel the entire VPN data plane through the SSH connection instead of kubectl port-forward. This avoids the API server entirely and sidesteps the well-known port-forward "black-hole" problem (a local TCP connection that looks alive but never carries data). Because a node's kube-proxy rules make the traffic manager's ClusterIP directly reachable, the SSH host can dial it directly.

Auto-detection (zero new flags)​

When --ssh-addr is provided, the root daemon automatically probes whether the SSH host can reach the traffic manager's ClusterIP:

probeSSHDataPlane()
1. Get Service "kubevpn-traffic-manager" → ClusterIP
2. SSH dial → tcp://<ClusterIP>:10801 (3s timeout)
3. Success → sshForward() — SSH tunnel for all data-plane ports
Failure → portForward() — standard kubectl port-forward (unchanged)

No new CLI flags and no new proto fields. A bastion-only SSH host (not a K8s node) fails the probe silently and falls back to the standard port-forward path — fully backward compatible.

Path comparison​

Standard path (port-forward):
TUN ─▶ 127.0.0.1:<local> ─▶ [SPDY/WS via API server] ─▶ pod:10801

SSH data-plane path:
TUN ─▶ 127.0.0.1:<local> ─▶ [SSH tunnel] ─▶ node ─▶ ClusterIP:10801 ─▶ pod:10801
Aspectport-forwardSSH data-plane
API server dependencyYes (SPDY/WebSocket)No
Black-hole riskKnown issueSSH TCP is reliable
ReconnectionClient-side detect + retryPortMapUntil auto-reconnects
Pod restart handlingMust re-find podClusterIP is stable; kube-proxy updates endpoints
Requirementkubeconfig accessSSH access to a K8s node

Liveness is unchanged: the heartbeat watchdog operates at the TUN layer, so it works regardless of whether the underlying transport is port-forward or an SSH tunnel.

SSH Remote Terminal​

The kubevpn ssh command creates an interactive terminal on a remote server through a WebSocket connection to the daemon. Two modes are supported:

  • Full mode (default): Connect to SSH server and create a two-way TUN tunnel for inner IP communication (using 198.18.0.0/16 CIDR range)
  • Lite mode (--lite): Only connect to SSH server, no tunnel creation

The terminal session supports automatic window resize detection and raw terminal mode.

ssh.svg