Ok so if you’re running Sonatype Nexus Repository 3 Community Edition and want Keycloak login, you’ve probably already hit this wall: Nexus CE just doesnt have native OIDC support at all. The one community plugin that used to fix this, flytreeleft/nexus3-keycloak-plugin, has been dead since 2021. First instinct was to write a custom Shiro Realm for it, but that means chasing Nexus’s internal auth APIs across every upgrade forever, so I didnt want to go there.
Turns out Nexus already ships something called RutAuthRealm thats been there this whole time. It just trusts whatever HTTP header you configure as an already-authenticated identity — old, obscure, basically nobody uses it for OIDC. But it means you dont have to write any Nexus-side code at all. Put a real OIDC proxy in front of it that does the actual Keycloak handshake, have it inject that header after, and Nexus never even knows OIDC exists.
The part thats actually missing from that setup is keeping Nexus’s role assignments in sync with Keycloak group membership, since an auth proxy doesnt do that on its own. Thats what I built, called it Cambium.
What it does:
cambium sync— daemon that reconciles Keycloak group/role membership into Nexus roles. Keeps its own manifest so it doesnt clobber roles you assigned manually.cambium ropc-proxy— separate shim for CLI stuff.docker login,npm login,pipcant follow a browser OIDC redirect, and neither oauth2-proxy nor Envoy’s OIDC filter does password-grant token exchange. This terminates Basic Auth, exchanges the credentials against Keycloak’s token endpoint, and forwards to Nexus with the identity injected as the RutAuth header. Credentials never get logged, the cache is keyed by a hash not plaintext.
Two Keycloak API things that actually took me a while to figure out, might save someone else the trouble: GET /groups only returns top-level groups, subGroups array is always empty no matter what, so if youre not also recursing into /groups/{id}/children you will silently miss anyone sitting in a nested subgroup. And separately, users with a role granted directly with zero group membership (mostly service accounts in my case) dont show up from the group side at all — needed a totally separate discovery path just for that.
Its at v1.0.0 now, CI green, tested against something with actual complexity (multiple groups/subgroups, some direct-role service accounts), not just a toy one-realm setup. One real limitation worth being upfront about: its single-instance only for now, theres a flock enforcing that on startup, no leader election yet, so dont scale it past 1 replica.
One more thing worth saying plainly: I designed the architecture and made every real decision here — why RutAuth over a custom realm, why a separate ROPC shim, how the sync avoids clobbering manual role assignments — but Im coming at this from DevOps, not as a full-time Rust dev, so I used AI tooling for a lot of the implementation. I can walk through and defend every design choice in it.
Repo’s here: GitHub - Rhizomo/cambium: An OIDC/Keycloak translator for Sonatype Nexus Repository 3 — no custom auth realm, no jar patching. Pairs an OIDC proxy + Nexus's built-in RutAuth header trust with a Keycloak->Nexus role-sync bridge. · GitHub (Apache-2.0). If youre dealing with the same Nexus-has-no-OIDC problem, or hit a similar wall with some other tool that only trusts a header, issues and PRs welcome.