Skip to content

refactor(ssh-proxy): fix the proxy-to-session target, gate the keygen, default to port 22 - #4675

Merged
SalimKayal merged 6 commits into
build/renku-ssh-proxyfrom
salimkayal/refactor/ssh-proxy-cleanup
Oct 8, 2026
Merged

SalimKayal merged 6 commits into
build/renku-ssh-proxyfrom
salimkayal/refactor/ssh-proxy-cleanup

Conversation

@SalimKayal

@SalimKayal SalimKayal commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Cleanup of the SSH proxy: the proxy-to-session target is now fixed rather than configurable, the hop-2 keygen is gated behind sshProxy.enabled, the proxy runs two hardened replicas, and the public entry defaults to port 22 so ssh <session>@ssh.<host> works with no -p. Stacked on #4586.

Motivation and context

  • The hop-2 target is fixed by the amalthea contract. The session Service port is 80 and the login user is renku. Exposing sshProxy.targetPort/targetUser in values invites an operator to change a value that silently breaks every session; the proxy's own fallback was also stale at 2222.
  • The platform-init hook generated the two hop-2 Secrets unconditionally, even when the proxy is disabled.
  • A single replica with no PodDisruptionBudget is not HA for an internet-facing entry point.
  • The entry defaulted to 2222, so the copy-paste command needed a non-standard port. data-services already omits :22 from the ssh:// URL, so defaulting to 22 makes the plain command correct.

Behavior

  • ssh <session>@ssh.<host> connects on the default port 22; sshProxy.service.port remains operator-overridable.
  • With sshProxy.enabled=false, the hop-2 key Secrets are no longer generated.
  • Two proxy replicas are spread across nodes and a PDB keeps one available during node drains.
  • The external ingress NetworkPolicy admits only the proxy's pod port (2222) instead of all ports.

Changes

  • renku-ssh-proxy/src/config.rs: hop-2 default target port 2222 → 80.
  • helm-chart/renku/templates/ssh-proxy/deployment.yaml: drop the RENKU_SSH_PROXY_TARGET_PORT/_TARGET_USER env; default to preferred hostname pod anti-affinity (overridable via sshProxy.affinity).
  • helm-chart/renku/templates/ssh-proxy/pdb.yaml (new): policy/v1 PDB, maxUnavailable: 50%, created only when enabled and replicaCount > 1.
  • helm-chart/renku/values.yaml: remove targetPort/targetUser; replicaCount: 1 → 2; service.port: 2222 → 22.
  • helm-chart/renku/templates/network-policies.yaml: pin allow-external-to-ssh-proxy to pod port 2222.
  • helm-chart/renku/templates/setup-job-platform-init.yaml + scripts/platform-init/platform-init.py: pass SSH_PROXY_ENABLED into the hook and guard ensure_ssh_proxy_session_keys on it.

Notes

  • The NetworkPolicy pins the pod port 2222, which assumes the CNI evaluates ingress policy post-DNAT (Calico/Cilium do). Verify on the first cluster that enables the proxy; a pre-DNAT CNI would need the Service port as well.

/deploy #notest extra-values=sshProxy.enabled=true,dataService.imageBuilders.enabled=true,dataService.imageBuilders.strategyName=renku-buildpacks-v3,dataService.imageBuilders.outputImagePrefix=harbor.dev.renku.ch/renku-build/,dataService.imageBuilders.nodeSelector.renku.io/node-purpose=user,dataService.imageBuilders.tolerations[0].effect=NoSchedule,dataService.imageBuilders.tolerations[0].key=renku.io/dedicated,dataService.imageBuilders.tolerations[0].operator=Equal,dataService.imageBuilders.tolerations[0].value=user renku-data-services=salimkayal/feat/ssh-keys renku-ui=lorenzo/ssh-keys-allow-disabled

@SalimKayal
SalimKayal requested review from a team as code owners October 7, 2026 12:56
@RenkuBot

RenkuBot commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

You can access the deployment of this PR at https://ci-renku-4675.dev.renku.ch

@SalimKayal
SalimKayal force-pushed the salimkayal/refactor/ssh-proxy-cleanup branch from af3ffc5 to 7db6f0a Compare October 7, 2026 15:20
@SalimKayal
SalimKayal changed the base branch from build/renku-ssh-proxy to lorenzo/expose-ssh-enabled-ui October 7, 2026 15:20
@SalimKayal
SalimKayal marked this pull request as draft October 7, 2026 15:38
@SalimKayal
SalimKayal marked this pull request as ready for review October 7, 2026 15:49
Comment thread helm-chart/renku/templates/ssh-proxy/deployment.yaml Outdated
Comment thread helm-chart/renku/templates/network-policies.yaml Outdated
Base automatically changed from lorenzo/expose-ssh-enabled-ui to build/renku-ssh-proxy October 8, 2026 09:27
@SalimKayal
SalimKayal merged commit 69423f5 into build/renku-ssh-proxy Oct 8, 2026
35 checks passed
@SalimKayal
SalimKayal deleted the salimkayal/refactor/ssh-proxy-cleanup branch October 8, 2026 13:04
@RenkuBot

RenkuBot commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Tearing down the temporary RenkuLab deployment for this PR.

olevski pushed a commit that referenced this pull request Oct 14, 2026
…, default to port 22 (#4675)

## Summary

Cleanup of the SSH proxy: the proxy-to-session target is now fixed rather than configurable, the hop-2 keygen is gated behind `sshProxy.enabled`, the proxy runs two hardened replicas, and the public entry defaults to port **22** so `ssh <session>@ssh.<host>` works with no `-p`. Stacked on #4586.

## Motivation and context

- The hop-2 target is fixed by the amalthea contract. The session Service port is `80` and the login user is `renku`. Exposing `sshProxy.targetPort`/`targetUser` in values invites an operator to change a value that silently breaks every session; the proxy's own fallback was also stale at `2222`.
- The `platform-init` hook generated the two hop-2 Secrets unconditionally, even when the proxy is disabled.
- A single replica with no PodDisruptionBudget is not HA for an internet-facing entry point.
- The entry defaulted to `2222`, so the copy-paste command needed a non-standard port. data-services already omits `:22` from the `ssh://` URL, so defaulting to 22 makes the plain command correct.

## Behavior

- `ssh <session>@ssh.<host>` connects on the default port 22; `sshProxy.service.port` remains operator-overridable.
- With `sshProxy.enabled=false`, the hop-2 key Secrets are no longer generated.
- Two proxy replicas are spread across nodes and a PDB keeps one available during node drains.
- The external ingress NetworkPolicy admits only the proxy's pod port (2222) instead of all ports.

## Changes

- `renku-ssh-proxy/src/config.rs`: hop-2 default target port `2222 → 80`.
- `helm-chart/renku/templates/ssh-proxy/deployment.yaml`: drop the `RENKU_SSH_PROXY_TARGET_PORT`/`_TARGET_USER` env; default to preferred hostname pod anti-affinity (overridable via `sshProxy.affinity`).
- `helm-chart/renku/templates/ssh-proxy/pdb.yaml` (new): `policy/v1` PDB, `maxUnavailable: 50%`, created only when enabled and `replicaCount > 1`.
- `helm-chart/renku/values.yaml`: remove `targetPort`/`targetUser`; `replicaCount: 1 → 2`; `service.port: 2222 → 22`.
- `helm-chart/renku/templates/network-policies.yaml`: pin `allow-external-to-ssh-proxy` to pod port `2222`.
- `helm-chart/renku/templates/setup-job-platform-init.yaml` + `scripts/platform-init/platform-init.py`: pass `SSH_PROXY_ENABLED` into the hook and guard `ensure_ssh_proxy_session_keys` on it.

## Notes

- The NetworkPolicy pins the **pod** port `2222`, which assumes the CNI evaluates ingress policy post-DNAT (Calico/Cilium do). Verify on the first cluster that enables the proxy; a pre-DNAT CNI would need the Service port as well.
SalimKayal added a commit that referenced this pull request Oct 24, 2026
* docs: clean outdated app references (#4635)

* Deleted old references to apps

* resolved broken links and how to configure frontends

* Run Prettier

* resolve links

* resolve broken link

* resolve first link

* resolve broken links

* chore: add renku cli docs (#4630)

* chore: add renku cli docs

* address comments

* Initial implementation of renku-ssh-proxy

- allow to connect to a fixed target host
- include nix testing vm
- authenticate with fixed public key
- Add dockerfile and workflow for build/publish
- Add building ssh-proxy image to chartpress

* Authorize ssh access using data services (#4613)

* Enable local port-forwarding
* Do not allow forwarding outside the session host
* Don't break the session if port-forwarding fails
* Don't drop connection when data-service connection fails

* feat: ssh proxy session keygen (#4626)

* chore(helm): drop unused internal-authn mount from ssh-proxy

* fix(helm): generate ssh-proxy host key when unset

* feat(ssh-proxy-session): add ssh-proxy-session-keys generator image

* feat(ssh-proxy-session): generate static proxy-to-session keys via hook job

* refactor(ssh-proxy-session): generate proxy-to-session keys in platform-init

* feat: ssh proxy session authn (#4627)

* feat(ssh-proxy-session): add ssh-proxy-session-keys generator image

* feat(ssh-proxy-session): generate static proxy-to-session keys via hook job

* refactor(ssh-proxy-session): generate proxy-to-session keys in platform-init

* feat(ssh-proxy-session): load session host/auth keys from config

* feat(ssh-proxy-session): pin session host key and use publickey auth

* feat(ssh-proxy-session): mount session host/auth keys in ssh-proxy

* chore(ssh-proxy-session): devshell and dev VM support for session keys

* feat(ssh-proxy-session): pass session key secret names to data-services

* refactor(ssh-proxy): remove b64 decode fallback from load_private_key_from_file

* squashme: Update helm-chart/renku/templates/data-service/session-env-secret.yaml

---------

Co-authored-by: Tasko Olevski <16360283+olevski@users.noreply.github.com>

* feat: ssh proxy hardening (#4628)

* feat(ssh-proxy-session): add ssh-proxy-session-keys generator image

* feat(ssh-proxy-session): generate static proxy-to-session keys via hook job

* refactor(ssh-proxy-session): generate proxy-to-session keys in platform-init

* feat(ssh-proxy-session): load session host/auth keys from config

* refactor(ssh-proxy): remove b64 decode fallback from load_private_key_from_file

* feat(ssh-proxy): set explicit SSH auth and keepalive limits

* feat(ssh-proxy): harden proxy pod security context

* feat(ssh-proxy): restrict proxy egress to required peers

* fix(ssh-proxy): disable unused ServiceAccount token projection

* squashme: Apply batched suggestions from code review

Co-authored-by: Samuel Gaist <samuel.gaist@idiap.ch>

* Update helm-chart/renku/values.yaml

---------

Co-authored-by: Tasko Olevski <16360283+olevski@users.noreply.github.com>
Co-authored-by: Samuel Gaist <samuel.gaist@idiap.ch>

* feat(ssh-proxy): dependencies audit in CI (#4629)

* feat(ssh-proxy-session): add ssh-proxy-session-keys generator image

* feat(ssh-proxy-session): generate static proxy-to-session keys via hook job

* refactor(ssh-proxy-session): generate proxy-to-session keys in platform-init

* feat(ssh-proxy-session): load session host/auth keys from config

* refactor(ssh-proxy): remove b64 decode fallback from load_private_key_from_file

* ci(ssh-proxy): audit Cargo.lock for known vulnerabilities

* ci(ssh-proxy): track cargo dependencies with dependabot

* docs(ssh-proxy): document the dependency audit cadence

* refactor(ssh-proxy): use secret service for authz (#4643)

* Add ssh subdomain annotation

* Fix whitespace in ssh-proxy deployment.yaml

* Fix release label in ssh-proxy pod

* Add feature flag for ssh-proxy deployment

- also pass some information into the data service

* Move ssh session keys into default data-service environment

* Update build pack images to 0.11.0

* chore: expose SSH_ENABLED variable the the UI pod (#4673)

* refactor(ssh-proxy): fix the proxy-to-session target, gate the keygen, default to port 22 (#4675)

## Summary

Cleanup of the SSH proxy: the proxy-to-session target is now fixed rather than configurable, the hop-2 keygen is gated behind `sshProxy.enabled`, the proxy runs two hardened replicas, and the public entry defaults to port **22** so `ssh <session>@ssh.<host>` works with no `-p`. Stacked on #4586.

## Motivation and context

- The hop-2 target is fixed by the amalthea contract. The session Service port is `80` and the login user is `renku`. Exposing `sshProxy.targetPort`/`targetUser` in values invites an operator to change a value that silently breaks every session; the proxy's own fallback was also stale at `2222`.
- The `platform-init` hook generated the two hop-2 Secrets unconditionally, even when the proxy is disabled.
- A single replica with no PodDisruptionBudget is not HA for an internet-facing entry point.
- The entry defaulted to `2222`, so the copy-paste command needed a non-standard port. data-services already omits `:22` from the `ssh://` URL, so defaulting to 22 makes the plain command correct.

## Behavior

- `ssh <session>@ssh.<host>` connects on the default port 22; `sshProxy.service.port` remains operator-overridable.
- With `sshProxy.enabled=false`, the hop-2 key Secrets are no longer generated.
- Two proxy replicas are spread across nodes and a PDB keeps one available during node drains.
- The external ingress NetworkPolicy admits only the proxy's pod port (2222) instead of all ports.

## Changes

- `renku-ssh-proxy/src/config.rs`: hop-2 default target port `2222 → 80`.
- `helm-chart/renku/templates/ssh-proxy/deployment.yaml`: drop the `RENKU_SSH_PROXY_TARGET_PORT`/`_TARGET_USER` env; default to preferred hostname pod anti-affinity (overridable via `sshProxy.affinity`).
- `helm-chart/renku/templates/ssh-proxy/pdb.yaml` (new): `policy/v1` PDB, `maxUnavailable: 50%`, created only when enabled and `replicaCount > 1`.
- `helm-chart/renku/values.yaml`: remove `targetPort`/`targetUser`; `replicaCount: 1 → 2`; `service.port: 2222 → 22`.
- `helm-chart/renku/templates/network-policies.yaml`: pin `allow-external-to-ssh-proxy` to pod port `2222`.
- `helm-chart/renku/templates/setup-job-platform-init.yaml` + `scripts/platform-init/platform-init.py`: pass `SSH_PROXY_ENABLED` into the hook and guard `ensure_ssh_proxy_session_keys` on it.

## Notes

- The NetworkPolicy pins the **pod** port `2222`, which assumes the CNI evaluates ingress policy post-DNAT (Calico/Cilium do). Verify on the first cluster that enables the proxy; a pre-DNAT CNI would need the Service port as well.

* feat(helm-chart): add label for networkpolicy selector

* chore: bump renku-ui to 4.34.0 (#4677)

* chore: address rust comments (#4688)

* chore: helm chart cleanup (#4680)

* feat: update data services to 0.88.0

* chore: update rust dependencies

* Remove directories dependency, since it has no usages

It's usage was removed in commit #b744c099

* Implement ci checks and adopt github workflow (#4700)

When running `nix flake check` all checks in `nix/check.nix` are run:

- the crate is compiled
- cargo audit
- cargo fmt --check
- cargo clippy
- cargo deny

---------

Co-authored-by: bethcg <127967525+bethcg@users.noreply.github.com>
Co-authored-by: Ralf Grubenmann <ralf.grubenmann@sdsc.ethz.ch>
Co-authored-by: Salim Kayal <salim.kayal@gmail.com>
Co-authored-by: Tasko Olevski <16360283+olevski@users.noreply.github.com>
Co-authored-by: Samuel Gaist <samuel.gaist@idiap.ch>
Co-authored-by: Lorenzo Cavazzi <43481553+lorenzo-cavazzi@users.noreply.github.com>
Co-authored-by: Salim Kayal <salim.kayal@idiap.ch>
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.

4 participants