Skip to content

OPRUN-4601: use resource-based RBAC for lifecycle-server auth#1290

Open
perdasilva wants to merge 1 commit intoopenshift:mainfrom
perdasilva:lifecycle-server-auth
Open

OPRUN-4601: use resource-based RBAC for lifecycle-server auth#1290
perdasilva wants to merge 1 commit intoopenshift:mainfrom
perdasilva:lifecycle-server-auth

Conversation

@perdasilva
Copy link
Copy Markdown
Contributor

@perdasilva perdasilva commented May 6, 2026

Summary

  • Replace the controller-runtime metrics auth filter (filters.WithAuthenticationAndAuthorization) with a custom resource-based authorization middleware for the lifecycle-server API
  • The metrics filter performed nonResourceURL-based SubjectAccessReviews, which meant the default system:discovery ClusterRole (granting GET on /api/*) allowed any authenticated user to bypass authorization
  • The new middleware creates SubjectAccessReviews with ResourceAttributes (apiGroup: lifecycle.olm.openshift.io, resource: lifecycles, verb: get) so access requires an explicit ClusterRoleBinding to the new lifecycle-server-consumer ClusterRole

Changes

  • pkg/lifecycle-server/auth.go — New auth middleware using DelegatingAuthenticatorConfig + DelegatingAuthorizerConfig with resource-based AttributesRecord
  • pkg/lifecycle-server/auth_test.go — 7 unit tests covering all auth code paths (401, 403, 500, 200, DecisionNoOpinion, attributes verification, reason non-leakage)
  • cmd/lifecycle-server/start.go — Swap metrics filter for new NewAuthFilter

Test plan

  • go build ./cmd/lifecycle-server/... compiles
  • go test ./pkg/lifecycle-server/... — all tests pass
  • go vet ./pkg/lifecycle-server/... ./cmd/lifecycle-server/... — clean
  • Deploy to cluster and verify: unauthenticated requests get 401, authenticated requests without the consumer ClusterRoleBinding get 403, requests with the binding get 200

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added authentication and authorization middleware for lifecycle server API requests, enforcing Kubernetes token-based auth and RBAC checks.
  • Refactor

    • Simplified middleware wiring to streamline request handling and error paths.
  • Tests

    • Added unit tests for unauthenticated, unauthorized, error, and allowed request scenarios.
  • Chores

    • Updated Go module dependencies and metadata.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 76bf222f-265a-4240-854f-3ee452710765

📥 Commits

Reviewing files that changed from the base of the PR and between cd520b2 and 11ba9c5.

📒 Files selected for processing (4)
  • cmd/lifecycle-server/start.go
  • go.mod
  • pkg/lifecycle-server/auth.go
  • pkg/lifecycle-server/auth_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • cmd/lifecycle-server/start.go
  • pkg/lifecycle-server/auth.go

Walkthrough

Adds a new authentication and authorization HTTP middleware to the lifecycle server: introduces server.NewAuthFilter (TokenReview authenticator + SubjectAccessReview authorizer), wires it into server startup to wrap the API handler for lifecycles, and adds unit tests plus related go.mod updates. (≤50 words)

Changes

Lifecycle Server Authentication Middleware

Layer / File(s) Summary
Constants & Configuration
pkg/lifecycle-server/auth.go
Defines APIGroup = "lifecycle.olm.openshift.io" and Resource = "lifecycles" and imports authn/authz packages.
Core Implementation
pkg/lifecycle-server/auth.go
Adds NewAuthFilter(cfg *rest.Config, httpClient *http.Client, log logr.Logger) (func(http.Handler) http.Handler, error) which builds TokenReview-based authenticator, SubjectAccessReview-based authorizer, retry/backoff and returns middleware enforcing authn/authz (maps outcomes to 401/403/500).
Server Wiring
cmd/lifecycle-server/start.go
Replaces previous filter factory with authFilter, err := server.NewAuthFilter(restCfg, httpClient, log) and applies middleware via apiHandler := authFilter(baseHandler), updating imports and init error handling.
Tests
pkg/lifecycle-server/auth_test.go
Adds tests with fakeAuthenticator and fakeAuthorizer covering unauthenticated (401), authentication error (500), forbidden/no-op (403), allowed (200), attribute recording, and authorization error (500).
Dependencies
go.mod
Adds k8s.io/apiserver v0.35.4 to direct requires and updates related indirect Kubernetes module entries (e.g., k8s.io/cli-runtime v0.35.0 and others).

Sequence Diagram

sequenceDiagram
    participant Client
    participant AuthMiddleware as Auth Middleware
    participant Authenticator as TokenReview Authenticator
    participant Authorizer as SubjectAccessReview Authorizer
    participant Handler as Inner Handler

    Client->>AuthMiddleware: HTTP Request
    AuthMiddleware->>Authenticator: AuthenticateRequest()
    alt Authentication Error
        Authenticator-->>AuthMiddleware: Error
        AuthMiddleware-->>Client: 500 Internal Server Error
    else Unauthenticated
        Authenticator-->>AuthMiddleware: No User
        AuthMiddleware-->>Client: 401 Unauthorized
    else Authenticated
        Authenticator-->>AuthMiddleware: User Identity
        AuthMiddleware->>Authorizer: Authorize(user, verb="get", apiGroup="lifecycle.olm.openshift.io", resource="lifecycles")
        alt Authorization Error
            Authorizer-->>AuthMiddleware: Error
            AuthMiddleware-->>Client: 500 Internal Server Error
        else Denied or NoOpinion
            Authorizer-->>AuthMiddleware: Denied / NoOpinion
            AuthMiddleware-->>Client: 403 Forbidden
        else Allowed
            Authorizer-->>AuthMiddleware: Allowed
            AuthMiddleware->>Handler: Forward request
            Handler-->>Client: Handler response
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 11 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (11 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main change: replacing authentication/authorization approach with resource-based RBAC for the lifecycle-server.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed The PR adds 7 test functions in auth_test.go using standard Go testing.T. All test names are static and descriptive with no dynamic information that could change between runs.
Test Structure And Quality ✅ Passed Auth tests use standard Go testing (not Ginkgo). 7 tests each test single behavior. Meaningful assertions with messages. Uses test doubles. Follows repo patterns.
Microshift Test Compatibility ✅ Passed No Ginkgo e2e tests were added. The new auth_test.go file is a standard Go unit test file using testing.T, not Ginkgo patterns. The custom check only applies to Ginkgo e2e tests.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No Ginkgo e2e tests were added in this PR. The new test file (auth_test.go) uses standard Go testing package for unit tests only, with no infrastructure assumptions. Check not applicable.
Topology-Aware Scheduling Compatibility ✅ Passed PR adds auth middleware in Go code only. No deployment manifests or scheduling constraints (affinity, topology spread, replicas) are introduced or modified. Check not applicable.
Ote Binary Stdout Contract ✅ Passed No stdout contract violations found. All logging uses klog (stderr by default), error handling writes to stderr, and Cobra output defaults to stderr without SetOut() overrides.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed PR adds only standard Go unit tests (testing.T), not Ginkgo e2e tests. Custom check applies only to Ginkgo e2e tests. Not applicable to this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci Bot requested review from fgiudici and oceanc80 May 6, 2026 09:21
@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 6, 2026

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: perdasilva

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 6, 2026
@perdasilva perdasilva force-pushed the lifecycle-server-auth branch from 7fc0232 to cd520b2 Compare May 6, 2026 13:03
@perdasilva perdasilva changed the title feat: use resource-based RBAC for lifecycle-server auth OPRUN-4601: use resource-based RBAC for lifecycle-server auth May 6, 2026
@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label May 6, 2026
@openshift-ci-robot
Copy link
Copy Markdown

openshift-ci-robot commented May 6, 2026

@perdasilva: This pull request references OPRUN-4601 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Summary

  • Replace the controller-runtime metrics auth filter (filters.WithAuthenticationAndAuthorization) with a custom resource-based authorization middleware for the lifecycle-server API
  • The metrics filter performed nonResourceURL-based SubjectAccessReviews, which meant the default system:discovery ClusterRole (granting GET on /api/*) allowed any authenticated user to bypass authorization
  • The new middleware creates SubjectAccessReviews with ResourceAttributes (apiGroup: lifecycle.olm.openshift.io, resource: lifecycles, verb: get) so access requires an explicit ClusterRoleBinding to the new lifecycle-server-consumer ClusterRole

Changes

  • pkg/lifecycle-server/auth.go — New auth middleware using DelegatingAuthenticatorConfig + DelegatingAuthorizerConfig with resource-based AttributesRecord
  • pkg/lifecycle-server/auth_test.go — 7 unit tests covering all auth code paths (401, 403, 500, 200, DecisionNoOpinion, attributes verification, reason non-leakage)
  • cmd/lifecycle-server/start.go — Swap metrics filter for new NewAuthFilter
  • tmp-manifests/0000_50_olm_09-lifecycle-server.rbac.yaml — Add lifecycle-server-consumer ClusterRole for API consumers
  • tmp-manifests/0000_50_olm_08-lifecycle-controller.rbac.yaml — Grant lifecycle-controller access to the lifecycle API

Test plan

  • go build ./cmd/lifecycle-server/... compiles
  • go test ./pkg/lifecycle-server/... — all tests pass
  • go vet ./pkg/lifecycle-server/... ./cmd/lifecycle-server/... — clean
  • Deploy to cluster and verify: unauthenticated requests get 401, authenticated requests without the consumer ClusterRoleBinding get 403, requests with the binding get 200

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

  • Added authentication and authorization middleware for lifecycle server API requests, enforcing Kubernetes token-based auth and RBAC checks.

  • Refactor

  • Internalized middleware integration to simplify request handling and improve error paths.

  • Tests

  • Added unit tests covering unauthenticated, unauthorized, error, and allowed request scenarios.

  • Chores

  • Updated build dependencies and module metadata.

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/lifecycle-server/auth.go (1)

21-24: ⚡ Quick win

Keep these constants unexported.

APIGroup and Resource look internal to this middleware and the tests can still use them without exporting. Lowercasing them avoids accidentally growing the server package API surface.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/lifecycle-server/auth.go` around lines 21 - 24, Rename the exported
constants APIGroup and Resource to unexported identifiers (e.g., apiGroup and
resource) in the auth.go file and update all internal references to those
symbols (including unit tests in the same package) so the package API surface
doesn't grow unintentionally; ensure no external package code relies on
APIGroup/Resource and adjust tests or move them into the same package if
necessary.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/lifecycle-server/auth.go`:
- Around line 86-92: The SAR call is missing the resource Name so
resourceNames-scoped RBAC can't match; in the authz.Authorize invocation that
constructs authorizer.AttributesRecord (the block that sets User: res.User,
Verb: "get", APIGroup: APIGroup, Resource: Resource, ResourceRequest: true),
extract the package name from the request path (the {package} segment of
/api/{version}/lifecycles/{package} using req.URL.Path parsing or the router
vars) and set AttributesRecord.Name to that package string before calling
authz.Authorize so the authorization is evaluated against the specific lifecycle
resource.

---

Nitpick comments:
In `@pkg/lifecycle-server/auth.go`:
- Around line 21-24: Rename the exported constants APIGroup and Resource to
unexported identifiers (e.g., apiGroup and resource) in the auth.go file and
update all internal references to those symbols (including unit tests in the
same package) so the package API surface doesn't grow unintentionally; ensure no
external package code relies on APIGroup/Resource and adjust tests or move them
into the same package if necessary.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 269ceef6-45fa-480f-9ce0-030e8cb99009

📥 Commits

Reviewing files that changed from the base of the PR and between 7fc0232 and cd520b2.

📒 Files selected for processing (4)
  • cmd/lifecycle-server/start.go
  • go.mod
  • pkg/lifecycle-server/auth.go
  • pkg/lifecycle-server/auth_test.go

Comment thread pkg/lifecycle-server/auth.go
@joelanford
Copy link
Copy Markdown
Member

PR description says:

  • tmp-manifests/0000_50_olm_09-lifecycle-server.rbac.yaml — Add lifecycle-server-consumer ClusterRole for API consumers
  • tmp-manifests/0000_50_olm_08-lifecycle-controller.rbac.yaml — Grant lifecycle-controller access to the lifecycle API

But I don't see those in the changeset. iirc, the manifests directory is generated, and I had to trace where to put the new lifecycle-controller manifests to ensure the generation step puts them in the CVO manifests directory.

@perdasilva
Copy link
Copy Markdown
Contributor Author

PR description says:

  • tmp-manifests/0000_50_olm_09-lifecycle-server.rbac.yaml — Add lifecycle-server-consumer ClusterRole for API consumers
  • tmp-manifests/0000_50_olm_08-lifecycle-controller.rbac.yaml — Grant lifecycle-controller access to the lifecycle API

But I don't see those in the changeset. iirc, the manifests directory is generated, and I had to trace where to put the new lifecycle-controller manifests to ensure the generation step puts them in the CVO manifests directory.

PR description says:

  • tmp-manifests/0000_50_olm_09-lifecycle-server.rbac.yaml — Add lifecycle-server-consumer ClusterRole for API consumers
  • tmp-manifests/0000_50_olm_08-lifecycle-controller.rbac.yaml — Grant lifecycle-controller access to the lifecycle API

But I don't see those in the changeset. iirc, the manifests directory is generated, and I had to trace where to put the new lifecycle-controller manifests to ensure the generation step puts them in the CVO manifests directory.

sorry about that - I've updated the description. Those were either wrong or breadcrumbs for the client-side RBAC requirements

@openshift-ci
Copy link
Copy Markdown
Contributor

openshift-ci Bot commented May 6, 2026

@perdasilva: all tests passed!

Full PR test history. Your PR dashboard.

Details

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 kubernetes-sigs/prow repository. I understand the commands that are listed here.

Replace the controller-runtime metrics auth filter with a custom
resource-based authorization middleware. The metrics filter performed
nonResourceURL-based SubjectAccessReviews, which meant the default
system:discovery ClusterRole (granting GET on /api/*) allowed any
authenticated user to access the lifecycle-server API.

The new middleware creates SubjectAccessReviews with ResourceAttributes
(apiGroup: lifecycle.olm.openshift.io, resource: lifecycles, verb: get)
so access requires an explicit ClusterRoleBinding to the new
lifecycle-server-consumer ClusterRole.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Per G. da Silva <pegoncal@redhat.com>
@perdasilva perdasilva force-pushed the lifecycle-server-auth branch from cd520b2 to 11ba9c5 Compare May 7, 2026 07:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants