Skip to content

Admin API: JIT-elevated SSH endpoint - #5006

Open
RickARodriguez wants to merge 1 commit into
masterfrom
admin-ssh-endpoint-clean
Open

Admin API: JIT-elevated SSH endpoint#5006
RickARodriguez wants to merge 1 commit into
masterfrom
admin-ssh-endpoint-clean

Conversation

@RickARodriguez

@RickARodriguez RickARodriguez commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Which issue this PR addresses:

Fixes ARO-26233https://redhat.atlassian.net/browse/ARO-26233

What this PR does / why we need it:

Adds an admin RP endpoint POST /admin/{resourceID}/ssh/newelevated that mints
per-request SSH credentials for the portal binary's SSH reverse proxy. The
response mirrors the portal binary's ssh.New response byte-for-byte
({command, password} JSON), and the RP writes a PortalDocument with
Portal.SSH{Master} and a 60s TTL; the portal binary's SSH PasswordCallback
then authorises the follow-up TCP connect against that stored document.

Elevation is JIT-gated by the ACIS manifest that fronts /newelevated (same
pattern as /kubeconfig/newelevated, #4949); the RP does not do an
in-process AAD group check. This gives SREs SSH parity with the portal UI via
Geneva Actions. The portal SSH host public key is loaded once at frontend
startup from the portal keyvault (secret portal-sshkey, RSA PKCS#1); on
failure the endpoint returns 503 and the RP still starts.

Test plan for issue:

  • Unit: TestAdminOpenShiftClusterSSHNewElevated — 8 subtests covering
    master 0/1/2, missing host key → 503, wrong Content-Type → 415, master out of
    range → 400, and Cosmos write failure → 500.
  • Manual (local dev RP): live mint → HTTP 200 with {command, password};
    PortalDocument written to Cosmos (ttl=60, portal.ssh.master,
    authenticated unset); IfxAudit adminOp event = Success; password is not
    logged; portal SSH host key loaded from keyvault.
  • Manual e2e (dev cluster): the minted credential is consumed by the
    unchanged portal SSH reverse proxy (pkg/portal/ssh) and yields a real
    interactive shell on master-0.

Is there any documentation that needs to be updated for this PR?

No — this mirrors the existing portal SSH flow (same PortalDocument shape, same
proxy consumer); there is no new operator-facing doc surface. Tech-debt/N-A.

How do you know this will function as expected in production?

  • The endpoint emits the same PortalDocument shape the portal SSH proxy
    already consumes, so the data path is exercised by existing, unchanged code.
  • Every call emits an IfxAudit adminOp event for the admin audit trail.
  • Failure modes are handled explicitly: missing portal host key → 503 (RP still
    starts), invalid master → 400, Cosmos write failure → 500. The SSH bearer
    password is never written to logs.

Authoring assisted by GitHub Copilot.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new admin RP endpoint to mint short-lived, JIT-elevated SSH credentials that are consumed by the existing portal SSH reverse proxy via a persisted PortalDocument (TTL=60s) in Cosmos.

Changes:

  • Adds POST /admin/{resourceID}/ssh/newelevated and wires it into the authenticated admin routes.
  • Loads/pins the portal SSH host public key once at frontend startup from the portal Key Vault, returning 503 on endpoint usage if unavailable.
  • Adds unit tests covering success/error paths for the new SSH minting endpoint.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
pkg/frontend/frontend.go Loads portal SSH host public key at startup and registers the new admin SSH route.
pkg/frontend/admin_openshiftcluster_ssh.go Implements the SSH credential minting handler and Key Vault host key loader.
pkg/frontend/admin_openshiftcluster_ssh_test.go Adds unit coverage for the new /ssh/newelevated admin endpoint.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pkg/frontend/admin_openshiftcluster_ssh.go
Comment thread pkg/frontend/admin_openshiftcluster_ssh.go
Comment thread pkg/frontend/admin_openshiftcluster_ssh.go
Copilot AI review requested due to automatic review settings July 28, 2026 07:22
@RickARodriguez

RickARodriguez commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 6f9814e addressing the review feedback:

  • Cluster existence is now checked before minting (404 if the OpenShiftCluster doc is absent), mirroring the admin kubeconfig endpoint — no more stray PortalDocuments for typo'd/deleted resource IDs.
  • Empty caller identity (missing/blank ARM SystemData) now fails fast with 400 instead of minting a credential the portal SSH proxy can never match.
  • Error target for out-of-range master corrected to master (top-level field), not properties.master.

Unit tests updated accordingly (added cluster-not-found 404 and missing-identity 400 cases); all 9 subtests green locally.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread pkg/frontend/admin_openshiftcluster_ssh.go Outdated
Copilot AI review requested due to automatic review settings July 28, 2026 07:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Adds an admin RP endpoint that mints per-request SSH credentials for the
portal binary's SSH reverse proxy:

  POST /admin/{resourceID}/ssh/newelevated

The response body mirrors the portal binary's ssh.New response
byte-for-byte ({command, password} JSON), so tooling that consumes
either endpoint sees the same shape. The RP writes a PortalDocument
with Portal.SSH{Master} and a 60s TTL; the portal binary's SSH
PasswordCallback then authorises the follow-up TCP connect against
that stored document.

Elevation is JIT-gated by the ACIS manifest that fronts /newelevated
(same pattern as /kubeconfig/newelevated added in PR #4949). The RP
does NOT do an in-process AAD group check.

Before minting, the handler confirms the target cluster document exists
(404 otherwise, mirroring the kubeconfig endpoint) and validates the
caller-identity local-part against a shell-safe character set (400
otherwise), so a typo'd resource ID or a malformed identity can't leave
a stray PortalDocument or inject into the returned command.

The portal SSH host public key is loaded once at frontend startup from
the portal keyvault (secret "portal-sshkey", RSA PKCS#1). On failure
the endpoint returns 503 and RP still starts.

Jira: ARO-26233
Signed-off-by: Richard Rodriguez <rckarod@gmail.com>
@RickARodriguez
RickARodriguez force-pushed the admin-ssh-endpoint-clean branch from 6f369a7 to fa002cb Compare July 28, 2026 07:48
@RickARodriguez

Copy link
Copy Markdown
Collaborator Author

Re: the 3 failing checks — all pre-existing, not from this PR.

npm-audit-api, npm-audit-portal/v2, and CI (Build_And_Test_RP_And_Portal) are the same fleet-wide npm-audit breakage — high-severity vulnerabilities in the portal/v2 and api npm dependencies. The ADO Build_And_Test_RP_And_Portal job fails at Dockerfile.ci-rp Step 9 (npm ci && npm audit --audit-level high --omit=dev), i.e. the exact same audit; it never reaches the Go build/test. The same checks fail on unrelated PRs (e.g. #5003, #5005).

This is tracked and fixed by #5007 ("Fix npm audit CI failure"), which bumps the vulnerable deps and edits Dockerfile.ci-rp. Once #5007 merges, rebasing this branch on master clears all three.

This PR touches only 3 Go files and is green on every code gate: golangci-lint, validate-go, generate-check, vendor-check, all unit_tests shards (incl. pkg-frontend), CodeQL, and the prow image/e2e jobs.

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.

2 participants