You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
We've been running dex on EKS for a while and ended up building an internal controller to handle connector and client
management. I wanted to share what we built and ask whether there's appetite for something like this upstream.
What we built and why
The goal was developer self-service: app teams shouldn't need to file a ticket or touch a central dex config file to
onboard their application to SSO. They should be able to declare what they need as part of their own Helm chart and
have it just work.
Our controller (sso-controller) makes that possible today. When an app team wants SSO, they add a namespaced DexClient CR and an ExternalSecret to their chart - both live in their own namespace alongside the rest of their app.
On merge, ArgoCD picks it up, the controller reconciles it, and their connector and OAuth2 client are registered with
dex.
The full GitOps loop looks like this:
App team adds DexCR + ExternalSecret to their chart (credentials pulled from External Vault via
External Secrets Operator)
PR merged -> ArgoCD syncs the namespace
Controller picks up the new CR, copies secrets to the dex namespace, writes connector and client entries into the
dex ConfigMap
Dex pod restarts with the updated config
It works, and it's fully integrated into our platform. But there are a few things that bother us about it:
Pod restarts on every change. A new app onboarding or a secret rotation causes a rolling restart of dex.
Secret values land in the ConfigMap. We copy the actual credential values in rather than referencing them, which
means plaintext secrets in a ConfigMap instead of proper Kubernetes Secret objects.
Not true GitOps The ExternalSecret and DexCR CR are clean declarativeresources in git. But then the controller has to step outside that model and mutate a central ConfigMap that isn't owned by any single application. When something goes wrong it's hard to debug.
It only covers our connector type (Azure AD via OIDC). Adding other providers means extending the
controller ourselves and maintaining that mapping indefinitely.
It's a separate thing to maintain that stays in sync with dex internals by parsing and rewriting config.yaml.
Why not the gRPC API?
I'm aware of CreateConnector/UpdateConnector behind the api_connectors_crud feature flag. The flag being off by
default and the API being marked experimental made us hesitant, but the bigger gap is that connector config (including
secrets) goes directly in the request payload. There's no way to say "get the clientSecret from this Kubernetes Secret"
you have to resolve it first and send the value, which puts us back to the same secrets-in-plaintext problem.
What we'd want instead
A built-in reconciler that watches namespaced CRDs and manages connectors and clients without restarts. The
self-service story stays the same - app teams declare what they need in their own namespace and it gets reconciled
automatically - but the implementation is cleaner end-to-end:
No central ConfigMap that every app's controller is racing to mutate
No secret copying across namespaces - the connector CR references a Secret in its own namespace and dex reads it at
reconcile time
No pod restarts - getConnector already checks ResourceVersion and lazy-reloads from storage on mismatch, so the
infrastructure for live updates is already there
The declared state in git actually matches what's running, with CR status conditions surfacing sync errors back to
app teams via kubectl
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
We've been running dex on EKS for a while and ended up building an internal controller to handle connector and client
management. I wanted to share what we built and ask whether there's appetite for something like this upstream.
What we built and why
The goal was developer self-service: app teams shouldn't need to file a ticket or touch a central dex config file to
onboard their application to SSO. They should be able to declare what they need as part of their own Helm chart and
have it just work.
Our controller (
sso-controller) makes that possible today. When an app team wants SSO, they add a namespacedDexClientCR and an ExternalSecret to their chart - both live in their own namespace alongside the rest of their app.On merge, ArgoCD picks it up, the controller reconciles it, and their connector and OAuth2 client are registered with
dex.
The full GitOps loop looks like this:
DexCR+ExternalSecretto their chart (credentials pulled from External Vault viaExternal Secrets Operator)
dex ConfigMap
It works, and it's fully integrated into our platform. But there are a few things that bother us about it:
means plaintext secrets in a ConfigMap instead of proper Kubernetes Secret objects.
DexCRCR are clean declarativeresources in git. But then the controller has to step outside that model and mutate a central ConfigMap that isn't owned by any single application. When something goes wrong it's hard to debug.controller ourselves and maintaining that mapping indefinitely.
Why not the gRPC API?
I'm aware of
CreateConnector/UpdateConnectorbehind theapi_connectors_crudfeature flag. The flag being off bydefault and the API being marked experimental made us hesitant, but the bigger gap is that connector config (including
secrets) goes directly in the request payload. There's no way to say "get the clientSecret from this Kubernetes Secret"
What we'd want instead
A built-in reconciler that watches namespaced CRDs and manages connectors and clients without restarts. The
self-service story stays the same - app teams declare what they need in their own namespace and it gets reconciled
automatically - but the implementation is cleaner end-to-end:
reconcile time
getConnectoralready checksResourceVersionand lazy-reloads from storage on mismatch, so theinfrastructure for live updates is already there
app teams via
kubectlSomething like:
The important properties we'd want:
staticConnectors/staticClients- base config untouchedWe have a working internal implementation and would be willing to contribute it upstream if there's interest. Happy to
write a full DEP.
Beta Was this translation helpful? Give feedback.
All reactions