Skip to content

fix: enforce ReferenceGrant on cross-namespace Consumer SecretRef#432

Open
shreemaan-abhishek wants to merge 2 commits into
masterfrom
fix/consumer-cross-ns-secretref-grant
Open

fix: enforce ReferenceGrant on cross-namespace Consumer SecretRef#432
shreemaan-abhishek wants to merge 2 commits into
masterfrom
fix/consumer-cross-ns-secretref-grant

Conversation

@shreemaan-abhishek

@shreemaan-abhishek shreemaan-abhishek commented Jul 17, 2026

Copy link
Copy Markdown

Fixes api7/rfcs#138 (Sec Review FINDING-004).

What this fixes

A v1alpha1 Consumer credential's secretRef carries a caller-settable namespace. ConsumerReconciler.processSpec honored it and read the Secret with the controller's cluster-wide secrets RBAC, with no ReferenceGrant check, then TranslateConsumerV1alpha1 copied the entire foreign Secret's data into the ADC consumer credential and pushed it to the data plane.

A user who can only create consumers in namespace A could name namespace-B/victim-secret and bind that namespace's auth material (key-auth keys, basic-auth passwords, JWT secrets, HMAC keys) to a Consumer identity they own. The caller never had get secrets in namespace B; the controller does. Classic cross-namespace confused deputy.

The route controllers already gate cross-namespace refs via checkReferenceGrant; the Consumer path was the one auth pathway that skipped it. The v2 ApisixConsumer API is namespace-locked (LocalObjectReference), so only v1alpha1 was affected.

The fix

Enforce checkReferenceGrant before dereferencing a cross-namespace Consumer secretRef, matching the route controllers. Same-namespace refs are unaffected (the check short-circuits). Without a permitting grant the reconcile refuses the reference and surfaces the error on the Consumer status, so the foreign Secret is never loaded or bound.

Tests

internal/controller/consumer_controller_test.go covers cross-namespace denied (no grant), cross-namespace allowed (matching grant), and same-namespace allowed.

go test ./internal/controller/ -run TestProcessSpec

Sync

Paired open-source fix: apache/apisix-ingress-controller#2805 (identical change). Per the two-PR sync rule, the sync is complete when both merge.

Summary by CodeRabbit

  • New Features
    • Added explicit authorization enforcement for cross-namespace Secret references.
    • Cross-namespace secret access is now allowed only when an appropriate ReferenceGrant is present.
    • Unauthorized references are rejected and surfaced via consumer status/validation, while same-namespace Secret references continue to work as before.
  • Tests
    • Added and extended unit tests and webhook validator tests covering allowed/denied cross-namespace scenarios and “no warnings” behavior with grants.

A v1alpha1 Consumer credential secretRef carries an attacker-settable
namespace. The reconciler honored it and read the Secret with the
controller's cluster-wide RBAC, no ReferenceGrant check, binding a
foreign namespace's auth material to the consumer identity (confused
deputy). The route controllers already gate cross-namespace refs via
checkReferenceGrant; apply the same gate in the Consumer reconciler and
refuse the reference when no grant permits it.
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Consumer reconciliation and webhook validation now require a matching ReferenceGrant before resolving cross-namespace credential Secrets. Tests cover rejected references, granted references, and same-namespace references.

Changes

Consumer Secret Reference Authorization

Layer / File(s) Summary
ReferenceGrant validation and controller coverage
internal/controller/consumer_controller.go, internal/controller/consumer_controller_test.go
processSpec checks cross-namespace Secret references with checkReferenceGrant before loading them; controller tests cover denial without a grant, success with a grant, and same-namespace access without a grant.
Webhook ReferenceGrant validation coverage
internal/webhook/v1/consumer_webhook_test.go
Webhook test setup registers Gateway API types and covers permitted cross-namespace warnings, denied references without a grant, and warning-free validation with a grant.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Security Check ❌ Error internal/webhook/v1/consumer_webhook.go still GETs cross-namespace Secrets in extractCredentialKey before any ReferenceGrant check, so the foreign Secret is dereferenced without trust validation. Gate validateDuplicateKeyAuthCredentials/extractCredentialKey with the same ReferenceGrant check, or reuse processSpec so no Secret GET occurs until the ref is permitted.
E2e Test Quality Review ⚠️ Warning Tests are fake-client unit tests, not E2E; the PR lacks coverage of the full admission/reconcile flow required by the check. Add envtest/e2e coverage for create→admission→reconcile with a real API server and ReferenceGrant objects; keep unit tests as supporting coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: enforcing ReferenceGrant for cross-namespace Consumer SecretRef handling.
Linked Issues check ✅ Passed The changes implement ReferenceGrant gating in reconciliation and webhook paths, while preserving same-namespace refs and adding matching tests.
Out of Scope Changes check ✅ Passed The edits are focused on the ReferenceGrant fix and its tests, with no unrelated functional changes evident.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/consumer-cross-ns-secretref-grant

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

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

conformance test report - apisix mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-17T11:01:49Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.
- core:
    failedTests:
    - HTTPRouteInvalidBackendRefUnknownKind
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

conformance test report - apisix-standalone mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-17T11:00:53Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    result: partial
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 0
      Passed: 32
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests partially succeeded with 1 test skips. Extended tests partially
    succeeded with 1 test skips.
- core:
    result: partial
    skippedTests:
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 10
      Skipped: 1
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 1 test skips.
- core:
    result: success
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests succeeded.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

conformance test report

apiVersion: gateway.networking.k8s.io/v1
date: "2026-07-17T11:18:27Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.3.0
implementation:
  contact: null
  organization: APISIX
  project: apisix-ingress-controller
  url: https://github.com/apache/apisix-ingress-controller.git
  version: v2.0.0
kind: ConformanceReport
mode: default
profiles:
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    statistics:
      Failed: 1
      Passed: 11
      Skipped: 0
  name: GATEWAY-GRPC
  summary: Core tests failed with 1 test failures.
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 1
      Passed: 31
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 11
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - GatewayHTTPListenerIsolation
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
  name: GATEWAY-HTTP
  summary: Core tests failed with 1 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    failedTests:
    - GatewayModifyListeners
    - TLSRouteSimpleSameNamespace
    result: failure
    statistics:
      Failed: 2
      Passed: 9
      Skipped: 0
  name: GATEWAY-TLS
  summary: Core tests failed with 2 test failures.

…ment

The Consumer admission validator shares processSpec via
PrepareConsumerForValidation, so the ReferenceGrant gate now also
fail-closes at admission. Update the two cross-namespace webhook tests to
permit the reference via a ReferenceGrant, and add a test asserting a
cross-namespace secretRef without a grant is denied at admission.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@internal/webhook/v1/consumer_webhook_test.go`:
- Around line 167-172: Update the ValidateCreate test around
buildConsumerValidator so its fake client fails when Get requests
auth/jwt-secret, preventing the foreign Secret from being served before
ReferenceGrant validation. Preserve the existing error assertion containing “not
permitted by any ReferenceGrant”.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 86667401-87d4-4f82-9437-00651858b187

📥 Commits

Reviewing files that changed from the base of the PR and between 7b25520 and cea1094.

📒 Files selected for processing (1)
  • internal/webhook/v1/consumer_webhook_test.go

Comment on lines +167 to +172
secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "jwt-secret", Namespace: "auth"}}
validator := buildConsumerValidator(t, secret)

_, err := validator.ValidateCreate(context.Background(), consumer)
require.Error(t, err)
require.Contains(t, err.Error(), "not permitted by any ReferenceGrant")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
ast-grep outline internal/webhook/v1/consumer_webhook.go --match ConsumerCustomValidator --view expanded
rg -n -C4 'func \(v \*ConsumerCustomValidator\) collectWarnings|checker\.Secret|adcValidator\.Validate|processSpec' \
  internal/webhook/v1/consumer_webhook.go internal/webhook/v1/reference/checker.go

Repository: api7/api7-ingress-controller

Length of output: 4073


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the test around the cited lines
sed -n '1,260p' internal/webhook/v1/consumer_webhook_test.go

echo
echo '--- consumer_webhook.go (relevant slice) ---'
sed -n '115,150p' internal/webhook/v1/consumer_webhook.go

echo
echo '--- reference/checker.go (relevant slice) ---'
sed -n '1,240p' internal/webhook/v1/reference/checker.go

Repository: api7/api7-ingress-controller

Length of output: 13339


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '60,95p' internal/webhook/v1/consumer_webhook.go

echo
echo '--- adc admission validator references ---'
rg -n -C3 'type adcAdmissionValidator|func \(v \*adcAdmissionValidator\) Validate|ReferenceGrant|Check.*Grant|Secret\(' internal/webhook/v1 -g '*.go'

Repository: api7/api7-ingress-controller

Length of output: 18985


Block the foreign Secret lookup (internal/webhook/v1/consumer_webhook_test.go:167-172)

Because ValidateCreate collects warnings first, the fake client can still serve auth/jwt-secret before the ReferenceGrant denial. Use a client that fails on Get for that Secret and keep the not permitted by any ReferenceGrant assertion.

🤖 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 `@internal/webhook/v1/consumer_webhook_test.go` around lines 167 - 172, Update
the ValidateCreate test around buildConsumerValidator so its fake client fails
when Get requests auth/jwt-secret, preventing the foreign Secret from being
served before ReferenceGrant validation. Preserve the existing error assertion
containing “not permitted by any ReferenceGrant”.

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant