OpenShift operator that manages OAuth Server and OAuth API Server for cluster authentication.
This operator manages the authentication infrastructure for OpenShift clusters:
- OAuth Server (
openshift-authenticationnamespace) - Handles user authentication via identity providers (HTPasswd, LDAP, OIDC, Request Header, etc.) - OAuth API Server (
openshift-oauth-apiservernamespace) - Manages OAuth API resources and client registrations - Controllers - Each controller watches specific cluster resources and reconciles authentication state
- Token Encryption - Manages encryption of OAuth tokens at rest with key rotation support
- Identity Provider Configuration - Syncs IdP configs from
openshift-configto the OAuth server
The operator runs in openshift-authentication-operator namespace and orchestrates both OAuth components.
External OIDC Mode: Recent development focuses on external OIDC support. When external OIDC is enabled (via Authentication.config.openshift.io/cluster spec), the operator disables the default OAuth stack and configures the cluster to use an external OIDC provider instead. See pkg/controllers/externaloidc/ for implementation. Future feature development should be focused on external OIDC support, and the existing OAuth stack is considered feature complete.
The following feature gates control External OIDC behavior and conditionally enable controllers/resources:
ExternalOIDC— enables External OIDC mode itselfExternalOIDCWithUIDAndExtraClaimMappings— enables additional claim mappings (UID, extra claims) for OIDC providersExternalOIDCWithUpstreamParity— enables upstream-parity features for OIDC authenticationExternalOIDCExternalClaimsSourcing— when enabled, preserves the oauth-apiserver under OIDC (instead of deleting it) and enables external claims sourcing
# Build and verify
make build
make verify
make verify-bindata
# Testing
make test-unit # Fast unit tests
make test-e2e # E2E tests (1h)
make run-e2e-test WHAT=TestCustomRoute # Specific test
# NEVER run encryption tests in parallel
make test-e2e-encryption # Serial only, 4h timeout
# Update embedded assets
make update-bindata # After modifying bindata/ YAML files
make verify-bindata # Verify bindata is current
# Dependencies
go get <module>@<version>
go mod tidy && go mod vendor
make verify
# If carrying patches on vendored code
make update-deps-overrides- Go - See
go.modfor version - OpenShift library-go - Controller factory, resourceapply, operatorhelpers
- OpenShift api - Authentication/OAuth CRDs
- klog/v2 - Structured logging
- Use informers and listers - Never make direct API calls in controller sync loops
- Run
make update-bindataafter updating vendored CRDs that are copied intobindata/(e.g. the RoleBindingRestriction CRD). Editing YAML files directly inbindata/does not require this step — they are embedded via//go:embed - Use
resourceapplyhelpers -ApplyDeployment(),ApplyService(), etc. from library-go - Propagate
context.Context- When a function accepts or has access to acontext.Context, pass it through to downstream calls that accept one. Never discard a context or substitutecontext.Background()/context.TODO()when a context is already available - Return errors from sync() to trigger automatic retry
- Use table-driven tests for unit tests
- Log with klog - Use structured logging with klog.V() levels
- Reference files with line numbers - Format:
pkg/controllers/deployment/controller.go:123
- Adding new controllers - Must register in
pkg/operator/starter.goand add RBAC to manifests - Changing RBAC permissions - Update
manifests/files - Modifying encryption logic - Impacts security and requires encryption test validation
- Changing operator namespaces - Multiple namespaces involved (see below)
- Adding new CRD dependencies - Check vendoring and bindata requirements
- Never commit secrets or credentials to the repo
- Never modify
vendor/directly without updatingdeps.diff- Usego get,go mod tidy, andgo mod vendor. If carrying patches on vendored code, runmake update-deps-overrides - Never skip encryption tests - They're serial and slow (4h) but critical
- Never make direct API calls in controllers - Always use informers/listers for performance
- Never modify bindata/ CRDs sourced from vendor without running
make update-bindata - Never run encryption tests in parallel - They must run serially (
-p 1 -parallel 1) - Never use generic "helpful assistant" code - Follow OpenShift operator patterns
All controllers follow the library-go factory pattern:
import (
"github.com/openshift/library-go/pkg/controller/factory"
"github.com/openshift/library-go/pkg/operator/v1helpers"
)
func NewMyController(
operatorClient v1helpers.OperatorClient,
kubeInformers informers.SharedInformerFactory,
recorder events.Recorder,
) factory.Controller {
c := &myController{
operatorClient: operatorClient,
lister: kubeInformers.Apps().V1().Deployments().Lister(),
}
return factory.New().
WithInformers(kubeInformers.Apps().V1().Deployments().Informer()).
WithSync(c.sync).
ToController("MyController", recorder)
}
func (c *myController) sync(ctx context.Context, syncCtx factory.SyncContext) error {
// Use listers, never direct API calls
deployment, err := c.lister.Deployments("namespace").Get("name")
if err != nil {
return err // Return error to trigger retry
}
// Use resourceapply for declarative updates
_, _, err = resourceapply.ApplyDeployment(
ctx,
c.client,
recorder,
requiredDeployment,
expectedGeneration,
)
return err
}pkg/operator/starter.go # Register new controllers here
pkg/controllers/<name>/ # Controller implementations
pkg/operator/configobservation/ # Config observers
pkg/operator/workload/ # OAuth API Server workload
bindata/oauth-openshift/ # OAuth server manifests (14 YAML files)
bindata/oauth-apiserver/ # OAuth API server manifests (10 YAML files)
manifests/ # Operator RBAC and deployment
test/e2e/ # E2E tests
test/library/ # Shared test utilities
openshift-authentication-operator- This operator runs hereopenshift-authentication- OAuth server runs hereopenshift-oauth-apiserver- OAuth API server runs hereopenshift-config- User-provided configuration (secrets/configmaps for IdPs, TLS certs, CA bundles, etc.)openshift-config-managed- Operator-managed configuration
- Create
pkg/controllers/<name>/<name>_controller.go - Implement factory pattern with
sync()method - Register in
pkg/operator/starter.go - Add RBAC to
manifests/if needed - Add unit tests in
*_test.go
YAML files under bindata/ are embedded via //go:embed — edit them directly, no generation step needed. The only exception is CRDs copied from vendor/ (e.g. the RoleBindingRestriction CRD):
# After updating vendored CRDs
make update-bindata
make verify-bindata// Unit test - co-located with source
func TestMyController_Sync(t *testing.T) {
tests := []struct {
name string
setup func(*fakeClient)
wantErr bool
}{
// Table-driven test cases
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Test implementation
})
}
}- This operator manages cluster authentication - security is critical
- OAuth tokens encrypted at rest with key rotation support
- Never log secrets or credentials
- Validate all external IdP configurations