Skip to content

feat: expose the SSH port-forward shadow (#546) - #56

Draft
matbun wants to merge 2 commits into
interlink-hq:mainfrom
matbun:feat/ssh-shadow-tunnel
Draft

feat: expose the SSH port-forward shadow (#546)#56
matbun wants to merge 2 commits into
interlink-hq:mainfrom
matbun:feat/ssh-shadow-tunnel

Conversation

@matbun

@matbun matbun commented Jul 29, 2026

Copy link
Copy Markdown

Closes interlink-hq/interLink#546. Chart side of interlink-hq/interLink#550. Part of interlink-hq/interLink#545.

Surfaces the virtual kubelet's SSH port-forward shadow through values, so an air-gapped HPC site can be reached without hand-writing a shadow template. Needs a virtual kubelet with interlink-hq/interLink#550 in it — before that, shadowMode is a knob that renders config the VK ignores.

Usage

virtualNode:
  network:
    enableTunnel: true
    shadowMode: ssh
    ssh:
      loginHost: login.hpc.example.org
      user: alice
      keySecret: hpc-ssh-key       # or auth: kerberos + keytabSecret + principal

enableTunnel turns on shadow pods for offloaded pods with exposed ports; shadowMode picks which shadow is rendered. Both are needed.

Full deployment in interlink/examples/ssh_tunnel.yaml.

What's in it

network.shadowMode"" (default, wstunnel) or "ssh". Only emitted into the VK config when set, so the default render is untouched.

network.ssh.*loginHost, user, port, image, auth, keySecret/keySecretKey, keytabSecret/keytabSecretKey, principal, krb5ConfigMap, knownHostsConfigMap, replicateCredentials, nodeWaitTimeout, extraOptions.

loginHost and user use required and fail rendering when shadowMode: ssh:

Error: execution error at (interlink/templates/virtual-kubelet-config.yaml:89:19):
virtualNode.network.ssh.user is required when shadowMode is ssh

Everything else is only emitted when set, so defaults live in the VK rather than being pinned in two places that can drift.

The wstunnel template ConfigMap is no longer rendered or mounted in ssh mode. This is the part worth reviewing. The chart sets WstunnelTemplatePath and mounts an nginx/traefik wstunnel template whenever enableTunnel is on — and the VK prefers a template file over its built-in ones. In ssh mode that silently produces an nginx wstunnel Deployment instead of the SSH shadow, which is the kind of failure that costs an afternoon. New helper interlink.useWstunnelTemplateFile gates the ConfigMap, the volume, the mount, and WstunnelTemplatePath on one condition. An explicit customTemplate still wins in either mode.

RBAC. Secrets gain create/update/patch, but only when shadowMode: ssh and ssh.replicateCredentials is on, since that grant is cluster-wide.

network.ssh.forwardMode — portforward (default, ssh -L) or exec, for login nodes that set AllowTcpForwarding no and answer every channel with administratively prohibited. execConnectCommand (default nc) is the relay command run on the login node in that mode. Both omitted when unset.

Security note

ssh.replicateCredentials defaults to true, matching the VK default. It copies the SSH key or keytab Secret into each shadow's namespace, which makes it readable by anyone who can read Secrets there. It defaults on because shadows follow the offloaded pod into per-user namespaces (interlink.eu/shadow-same-ns) and multi-tenant setups are unusable otherwise — but it is a real tradeoff. Set it to false and provision the Secret yourself if that isn't acceptable, and the cluster-wide Secret write grant goes away with it.

Compatibility

helm template with both existing test values files produces output byte-identical to main:

$ diff <(git stash; helm template t ./interlink -f test_values.yaml) \
       <(git stash pop; helm template t ./interlink -f test_values.yaml)
$ diff ... test_values-wstunnel-wireguard.yaml

helm lint clean.

matbun and others added 2 commits July 31, 2026 17:03
Surfaces the virtual kubelet's ssh shadow mode through values, so an
air-gapped HPC site can be reached without hand-writing a shadow template:

    virtualNode:
      network:
        enableTunnel: true
        shadowMode: ssh
        ssh:
          loginHost: login.hpc.example.org
          user: alice
          keySecret: hpc-ssh-key    # or keytabSecret + principal

loginHost and user are required when shadowMode is ssh and fail rendering
if missing; everything else falls through to the virtual kubelet's own
defaults rather than being pinned here twice.

The wstunnel template ConfigMap is no longer rendered or mounted in ssh
mode. The virtual kubelet prefers a template file over its built-in ones,
so mounting it would silently override the ssh shadow with an nginx
wstunnel Deployment - the kind of failure that costs an afternoon. An
explicit customTemplate still wins in either mode.

Secrets gain create/update/patch only when credential replication is on,
since that grant is cluster-wide.

Rendering with the existing test values is byte-identical to before.

Signed-off-by: Matteo Bunino <48362942+matbun@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
interLink gained Network.SSH.ForwardMode for login nodes that refuse TCP
forwarding, where ssh -L is answered with "administratively prohibited". Surface
it, and the command the exec mode relays through, so a site does not have to drop
to a hand-written virtual kubelet config to use it.

Both are omitted when unset, so the rendered config is unchanged for every
existing values file.

Signed-off-by: Matteo Bunino <48362942+matbun@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@matbun
matbun force-pushed the feat/ssh-shadow-tunnel branch from 84e1e2e to 93a640a Compare July 31, 2026 15:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update Helm chart to support in-cluster deployment via ssh

1 participant