Attach a new VS Code window to CRI-based containers (containerd / CRI-O) running on a Kubernetes node — like Dev Containers' "Attach to Running Container", but for plain CRI runtimes reached through Remote-SSH.
The key property: it reuses your existing Remote-SSH session (including ProxyJump
setups and password auth). No extra credentials, no second login — the extension runs on
the node side of the window you already trust.
┌──────────┐ existing SSH (ProxyJump ok) ┌───────────────────────┐
│ VS Code │ ──────────────────────────────► │ K8s node │
│ (local) │ Remote-SSH tunnel │ └─ crictl ──► containerd
└────┬─────┘ │ │ │
│ new window: │ ▼ │
│ cri-container+<port>-<token>-<name> │ ┌─ pod netns ──────┐ │
│ resolved to 127.0.0.1:<port> │ │ vscode-server │ │
└──────────────────────────────────────►│ │ :4xxxx (token) │ │
│ └──────────────────┘ │
└───────────────────────┘
Two extensions, one codebase:
| Package | Runs where | Job |
|---|---|---|
cri-container-attach (kind workspace,ui) |
node side of the Remote-SSH window | container picker, install & start vscode-server inside the container, detect the pod IP, bridge it to node loopback, forward the port over the existing SSH connection |
cri-container-resolver (kind ui) |
UI side of every window | resolves cri-container+… authorities via the resolvers proposed API so the new window knows where to connect; labels windows with the container name |
Attach flow:
crictl ps→ pick a container.- Match the node's VS Code commit, install the matching
vscode-serverinto the container (~/.vscode-serverinside it), start it on a random port in40000-49999with a random connection token, bound to0.0.0.0. - Resolve the container/pod IP (container inspect → pod sandbox inspect →
hostname -ifallback; host-networked containers fall back to127.0.0.1). - Open
vscode-remote://cri-container+<token>-<port>-…/in a new window. The resolver extension in that window returns a managed connection: it spawns its ownssh -W <podIP>:<port> <ssh-target>from your desktop (your~/.ssh/config, includingProxyJump, applies), so the attached window owns its connection and the Remote-SSH window that ran the attach can be closed. - A forwarded port over the existing Remote-SSH session is still prepared as a fallback for VS Code builds without managed-connection support.
The password story: the window's ssh is tried passwordless first (keys or
ControlMaster). If your host needs a password, you are prompted once and the
password is remembered in VS Code's SecretStorage (the OS keychain) — later container
windows reuse it across reloads and restarts. It is handed to ssh via SSH_ASKPASS,
never written to disk or config, and dropped automatically if it stops working. For a
fully prompt-free experience, set up key auth (ssh-copy-id).
Prerequisites:
- VS Code ≥ 1.85 with the Remote-SSH extension, connected to the node
crictlon the node with access to the runtime socketsh,tar, andcurlorwgetinside the target container (first install only)
Steps:
- Build the two VSIX packages (see below) — or grab them from releases.
- In the Remote-SSH window: Extensions → Install from VSIX →
cri-container-attach-<version>.vsix(install in the SSH remote). - In the same window: install
cri-container-resolver-<version>.vsix— it must land locally (it is UI-only, VS Code installs it on the desktop automatically; verify withls ~/.vscode/extensions | grep cri-container-resolver). - On first activation the extension whitelists itself for the
resolversproposed API by patching VS Code'sproduct.json(extensionEnabledApiProposals). Fully quit VS Code (Cmd+Q / exit) and relaunch once — this is read only at startup. - Run CRI Container: Attach, pick a container, and a new window opens attached to it. The new window brings its own SSH connection — the Remote-SSH window that ran the attach can be closed once the container window is up.
Commands (in the Remote-SSH window, palette CRI Container):
- Attach to CRI Container — pick a container and open a new attached window
- List CRI Containers — quick view of containers with pod/namespace
- Exec into CRI Container (Terminal) — interactive shell via
crictl exec
Configuration (settings.json):
| Setting | Default | Description |
|---|---|---|
cri-container.crictlPath |
crictl |
crictl binary path on the node |
cri-container.runtimeEndpoint |
unix:///run/containerd/containerd.sock |
CRI runtime socket |
cri-container.defaultShell |
/bin/sh |
shell for Exec into Container |
cri-container.serverInstallPath |
/root/.vscode-server |
server install base inside containers |
npm install
npm run compile
npx @vscode/vsce package # then rename/package twice for the resolver variant,
# or use the two-extension packaging scriptThe two packages differ only in name, displayName, and extensionKind
(workspace,ui vs ui) — src/ is shared. src/proposedApi.d.ts declares the small
slice of the resolvers proposed API that is used.
- Only containers on the node you are SSH'd into (that is where
crictlruns). - Requires the
resolversproposed API, enabled via theproduct.jsonpatch — if a VS Code update replacesproduct.json, re-run attach once and restart to re-patch. - The attached window dials the node itself with
ssh -W; if that host uses password auth you are prompted once and the password is remembered (SecretStorage); key auth orControlMasteravoids the prompt entirely. TCP forwarding must be allowed on the node's sshd (AllowTcpForwarding). - Stale servers from previous attach runs inside a container are killed automatically on the next attach to that container.
Provided as-is for internal tooling; no warranty.