Repository navigation
Conversation
…t#16911) * OCPBUGS-71237: Persist console sessions across pod restarts Console sessions were tied to console pods — when pods restarted, all user sessions were terminated and users had to re-login. This change introduces session persistence via encrypted browser cookies. For OpenShift OAuth: a recovery cookie stores the encrypted access token. After pod restart, the new pod decrypts it and rebuilds the session. For OIDC: the existing refresh token cookie enables recovery. After pod restart, the new pod reads the refresh token from the cookie, exchanges it with the IdP for fresh tokens, and rebuilds the session. Large refresh tokens (>4KB) fall back to a server-side reference ID to avoid exceeding browser cookie size limits. Session cookies are per-pod (appended with POD_NAME) to handle multi-pod deployments. Previous encryption keys are supported for graceful key rotation during rolling updates. The logout handler clears session and recovery cookies before writing the HTTP response to ensure Set-Cookie headers are not silently dropped by Go's ResponseWriter. * OCPBUGS-71237: Address review findings - Fix concurrent map access on byRefreshTokenID by adding synchronized SetRefreshTokenID/GetRefreshTokenByID/DeleteRefreshTokenID methods - Route UpdateCookieRefreshToken through setRefreshTokenCookie for consistent size-aware cookie storage - Validate previous session key files together — log warning and skip both if either is unreadable instead of silently using an incomplete pair Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> (cherry picked from commit efcbe8b)
…tate The session-persistence tests call performLogin() to log in from scratch, but the Playwright project config injects storageState (admin cookies) into every browser context. This causes page.goto(baseURL) to land on the dashboard instead of the OAuth login page, so the login element assertion always times out. Clear the storageState for this describe block so tests start with an unauthenticated browser context, matching the pattern already used by auth-multiuser-login.spec.ts. Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 0cab36d)
|
Pipeline controller notification This PR uses the pipeline controller for second-stage tests. Selection and triggering follow the repository configuration. Use |
|
@jhadvig: This pull request references Jira Issue OCPBUGS-71237, which is invalid:
Comment The bug has been updated to refer to the pull request using the external bug tracker. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: jhadvig The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
|
@jhadvig: This pull request references Jira Issue OCPBUGS-132619, which is invalid:
Comment The bug has been updated to refer to the pull request using the external bug tracker. DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/jira refresh |
|
@jhadvig: This pull request references Jira Issue OCPBUGS-132619, which is valid. 7 validation(s) were run on this bug
DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
release-5.0 already has prettier enforcement enabled for the e2e folder (backported separately), which wasn't yet active on main when the cherry-picked commits were authored. Reformat only, no logic changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
OCPBUGS-132619 Session Persistence - Verification ResultsTested on a live cluster (
Test details
All tests passed 👍 |
|
/verified by @jhadvig |
|
@jhadvig: This PR has been marked as verified by DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository. |
|
/pipeline required |
|
Scheduling tests matching the |
|
@jhadvig: all tests passed! Full PR test history. Your PR dashboard. DetailsInstructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here. |
Leo6Leo
left a comment
There was a problem hiding this comment.
the backport itself looks good, but it carries a logout bug from main that I reproduced on a live cluster
Bug (inherited from main): logout recreates the session it just deleted.
pkg/auth/oauth2/auth_openshift.go, logout():
DeleteSessiondeletes the server-side session and emits an expiringSet-Cookiefor the session-token cookie.getLoginStatethen runs. The server lookup is nil, OpenShift auth has no refresh token, so it falls intorecoverSession.recoverSessionreadsopenshift-recovery-tokenfrom the request (still present; only the response header cleared it), callsAddSession, creates a fresh server-side session and emits a secondSet-Cookiefor the same session-token cookie name/path, with a value.- Revocation runs against the recovered login state, then 204.
Reproduced on a 5.1.0-ec.1 cluster as well (console built from a commit that includes #16911; operator providing shared keys; single pod served the whole sequence):
POST /api/console/logout→ 204, no error logged.- The session's
OAuthAccessTokenwas deleted (revocation ran). - 28 ms later, a request through the console proxy still returned 200 as
kube:admin. It kept returning 200 for ~2 min (API server token cache), then returned a KubernetesStatus401 body, i.e. the console was still proxying for that browser's cookies. A correct logout produces the console's own empty 401 from the auth middleware, not a proxied one. - Side effect observed with the UI open: once the cache expired, the frontend's 401 handling triggered a second logout and then silently re-authenticated via the still-valid OAuth server session. A kube:admin user may never notice they were logged out.
Do we wanna create a separate issue and have a new PR targeting it? If so, I can give LGTM to this PR
The reproduction report is generated with the assistance of Claude.
|
@Leo6Leo please create a separate issue to track the logout bug 🙏 |
Backport of #16911 and #17121 to
release-5.0. Clean cherry-pick, no conflicts.Analysis / Root cause:
Console sessions are stored entirely in per-pod process memory. When a pod restarts (upgrades, scaling, OOM, operator reconciliation), all sessions are lost and users must re-authenticate. This affects all users but is most disruptive for clusters using external IdPs (htpasswd, LDAP) where re-authentication requires full credential entry.
Two architectural limitations cause this:
Jira: https://redhat.atlassian.net/browse/OCPBUGS-71237
Related: https://redhat.atlassian.net/browse/OCPBUGS-58468
Companion operator PR: openshift/console-operator#1204
Solution description:
Phase 1: Shared encryption keys
Accept file-based encryption keys (provided by the console-operator via
session-secretSecret) instead of generating random keys per process. Falls back to random generation when no key files are provided (backward compat with older operators).Phase 2: Cookie-based session recovery
For OIDC auth (refresh tokens available):
For OpenShift auth (no refresh tokens — OAuth server doesn't support
refresh_tokengrant):openshift-recovery-tokencookie stores encrypted access token + expiryrecoverSession()reads the token, validates expiry locally, creates a new server-side sessionSecurity hardening
refreshTokenIDfield removedAlso includes #17121, a follow-up fix for the e2e session-persistence tests failing due to pre-loaded storageState.
Screenshots / screen recording:
Test setup:
Deploy cluster with htpasswd IdP, login, restart console pods, verify session persists.
Test cases:
Browser conformance:
Additional info:
Reviewers and assignees:
🤖 Generated with Claude Code