Skip to content

feat: support a CA bundle for the control plane connection - #447

Open
shreemaan-abhishek wants to merge 8 commits into
masterfrom
feat/cp-ca-bundle
Open

feat: support a CA bundle for the control plane connection#447
shreemaan-abhishek wants to merge 8 commits into
masterfrom
feat/cp-ca-bundle

Conversation

@shreemaan-abhishek

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

Copy link
Copy Markdown
Contributor

Closes #446

What this PR does

GatewayProxy.spec.provider.controlPlane.tlsVerify offers only two states today:

  1. tlsVerify: false — no verification.
  2. tlsVerify: true — verify against the system trust store only, which works only if the control plane's certificate chains to a publicly trusted CA.

There is no way to supply a custom CA, so for the common case of a self-signed or private-CA control plane a user who wants verification on has no path to make it succeed. The only escape from the resulting connection error is tlsVerify: false — which risks turning the insecure opt-out into copy-paste boilerplate, and undercuts the secure default being introduced in #438.

This adds the missing third state — tlsVerify: true + a CA bundle:

apiVersion: apisix.apache.org/v1alpha1
kind: GatewayProxy
spec:
  provider:
    type: ControlPlane
    controlPlane:
      endpoints:
        - https://api7-ee-3-gateway-admin.default.svc:9180
      tlsVerify: true
      caBundle: |
        -----BEGIN CERTIFICATE-----
        MIID...
        -----END CERTIFICATE-----
      auth:
        type: AdminKey
        adminKey:
          valueFrom:
            secretKeyRef:
              name: admin-key
              key: token

Design notes

Inline PEM, not a Secret/ConfigMap ref (one of the open questions in #446). A CA certificate is public material, so a Secret buys no confidentiality here, and an inline field is what Kubernetes itself uses for the same job (WebhookClientConfig.CABundle). It also keeps the change to the data path: no new watch, index, or RBAC rule, and rotating the bundle is an edit of the GatewayProxy the controller already reconciles on. A caBundleRef can be layered on later without breaking this field.

Invalid CA material fails fast, in two places. A CEL rule rejects a non-PEM caBundle at admission, and the translator parses it with x509.CertPool.AppendCertsFromPEM and returns an error before any config is pushed — so a typo surfaces as a clear message instead of an opaque TLS failure at connect time.

Interaction with tlsVerify. The bundle replaces the system trust store when verification is on, and is ignored when it is off — the controller logs that case rather than silently doing nothing. It is still sent, so flipping tlsVerify back on needs no other change.

Wire compatibility. The bundle reaches the ADC server as caCert in the task options, omitempty so that a GatewayProxy without a CA bundle produces byte-for-byte the request an older ADC server already accepts. Logging carries only hasCaCert / hasCaBundle booleans, never the material itself.

Dependency

The ADC-side blocker flagged in #446resolved. api7/adc#537 was closed in favour of api7/adc#552, which shipped in ADC v0.29.0 using the same caCert task option this PR sends. This PR pins 0.29.0 in the Makefile.

Sync

Paired open source PR, same change: apache/apisix-ingress-controller#2826. The Go hunks are identical between the two repos; this side additionally updates config/crd-nocel, which has no upstream counterpart.

Changes

  • api/v1alpha1/gatewayproxy_types.go: caBundle on ControlPlaneProvider, plus the CEL validation rule.
  • api/adc/types.go: Config.CaBundle; MarshalJSON reports hasCaBundle rather than the PEM.
  • internal/adc/translator/gatewayproxy.go: validate the PEM, warn when tlsVerify is off, set it on the config.
  • internal/adc/client/executor.go: carry it to the ADC server as caCert.
  • Regenerated CRD, hand-added the property to the crd-nocel bundle (no CEL rule there, by design), API reference updated by hand to match.

Tests

  • internal/adc/translator/gatewayproxy_test.go (new): the bundle reaches Config, stays empty when unset, is rejected when not PEM, and survives tlsVerify: false.
  • internal/adc/client/executor_test.go (new): caCert is absent from the request body without a bundle and present with one, with tlsSkipVerify still false.
go build ./...                              ok
go test ./internal/adc/... ./api/...        ok
go vet ./api/... ./internal/adc/...         ok
golangci-lint run                           0 issues

Both CRD variants were also exercised against a real API server via envtest: config/crd/bases admits a PEM bundle and rejects not-a-certificate with caBundle must be a PEM-encoded certificate, and the config/crd-nocel bundle installs cleanly and round-trips caBundle. Those checks are not committed, since this package has no envtest specs today and adding the first one would make go test ./internal/controller require the kubebuilder assets.

Summary by CodeRabbit

  • New Features

    • Added support for a custom PEM-encoded CA bundle (caBundle) to verify control-plane TLS certificates.
    • CA bundles are included in requests when TLS verification is enabled and have no effect when disabled.
  • Bug Fixes

    • Rejects malformed or incomplete certificate bundles.
    • Prevents certificate contents from appearing in request logs.
  • Documentation / Tests

    • Updated API and CRD documentation.
    • Added coverage for CA bundle validation, translation, and request handling.

tlsVerify offered only two states: verify against the system trust
store, or do not verify at all. A control plane using a self-signed or
private-CA certificate has no way to satisfy the first, so the only
escape from the connection error is tlsVerify: false -- which turns the
insecure opt-out into copy-paste boilerplate.

Add the missing third state: an optional PEM-encoded caBundle on
GatewayProxy.spec.provider.controlPlane, carried through the translated
config to the ADC server, which verifies the control plane against it
in place of the system trust store.

Unusable CA material is rejected up front -- by a CEL rule at admission
and by a PEM parse in the translator -- rather than surfacing later as
an opaque TLS failure.
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an optional PEM CA bundle to GatewayProxy control-plane configuration, validates and translates it into ADC configuration, sends it in ADC requests, and logs only its presence.

Changes

Control-plane CA bundle support

Layer / File(s) Summary
CA bundle API and CRD contract
api/v1alpha1/gatewayproxy_types.go, config/crd/..., docs/en/latest/reference/api-reference.md
Adds the optional caBundle field, PEM validation, CRD schema entries, and API documentation.
GatewayProxy translation and validation
internal/adc/translator/gatewayproxy.go, internal/adc/translator/gatewayproxy_test.go
Validates PEM certificate material and maps the configured bundle into Config.CaBundle. Tests cover empty, valid, multiple-certificate, invalid, and disabled-verification cases.
ADC configuration and request contract
api/adc/types.go, internal/adc/client/executor.go, internal/adc/client/executor_test.go, Makefile
Adds CA bundle state to ADC configuration, sends it as caCert, simplifies PUT request construction, tests request serialization and TLS verification, and updates the default ADC version to 0.29.0.

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

Sequence Diagram(s)

sequenceDiagram
  participant GatewayProxy
  participant TranslateGatewayProxyToConfig
  participant HTTPADCExecutor
  participant ADCServer
  GatewayProxy->>TranslateGatewayProxyToConfig: provide caBundle and tlsVerify
  TranslateGatewayProxyToConfig->>TranslateGatewayProxyToConfig: validate PEM certificates
  TranslateGatewayProxyToConfig->>HTTPADCExecutor: set Config.CaBundle
  HTTPADCExecutor->>ADCServer: send caCert in PUT request
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
E2e Test Quality Review ⚠️ Warning The feature diff adds only translator and executor unit tests; it changes no test/e2e files and committed E2E tests contain no control-plane caBundle scenario. This violates the blocking full-flow... Add an E2E test that creates a GatewayProxy with a private-CA control plane, verifies successful ADC sync with tlsVerify enabled, and checks invalid caBundle admission or translation failure.
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR adds inline CA bundle support, validates and propagates it through the CRD, translator, and ADC request, and updates documentation and tests [#446].
Out of Scope Changes check ✅ Passed All changes support the CA bundle feature, including the ADC version update required for the transmitted caCert field.
Security Check ✅ Passed PASS: No introduced finding in categories 1-7; logs expose only CA presence, Config.MarshalJSON omits Token and CA data, and TlsVerify maps to TlsSkipVerify as !tlsVerify.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding CA bundle support for the control plane connection.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cp-ca-bundle

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

@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 `@api/v1alpha1/gatewayproxy_types.go`:
- Line 123: The caBundle validation currently accepts certificate-looking
content without enforcing a strictly valid PEM certificate bundle. Update the
validation logic around the GatewayProxy caBundle handling in
internal/adc/translator/gatewayproxy.go:60-64 to parse every PEM block, reject
trailing garbage and non-certificate blocks, and accept only valid certificates;
then update the caBundle XValidation annotation in
api/v1alpha1/gatewayproxy_types.go:123 and regenerate the corresponding CRD
validation in config/crd/bases/apisix.apache.org_gatewayproxies.yaml:168-170.
🪄 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: a05e8c5e-fca5-4cea-88a4-9b2035151478

📥 Commits

Reviewing files that changed from the base of the PR and between d8fab8a and 867db94.

📒 Files selected for processing (9)
  • api/adc/types.go
  • api/v1alpha1/gatewayproxy_types.go
  • config/crd-nocel/apisix.apache.org_v2.yaml
  • config/crd/bases/apisix.apache.org_gatewayproxies.yaml
  • docs/en/latest/reference/api-reference.md
  • internal/adc/client/executor.go
  • internal/adc/client/executor_test.go
  • internal/adc/translator/gatewayproxy.go
  • internal/adc/translator/gatewayproxy_test.go

Comment thread api/v1alpha1/gatewayproxy_types.go Outdated
@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

conformance test report - apisix-standalone mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-08-26T10:00:21Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.6.0
implementation:
  contact:
  - https://github.com/apache/apisix-ingress-controller/issues
  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
    - HTTPRouteInvalidBackendRefUnknownKind
    - HTTPRouteInvalidCrossNamespaceBackendRef
    - HTTPRouteInvalidNonExistentBackendRef
    - HTTPRouteListenerHostnameMatching
    - HTTPRouteMultipleGateways
    - HTTPRouteNoBackendRefs
    statistics:
      Failed: 0
      Passed: 30
      Skipped: 7
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - BackendTLSPolicy
    - BackendTLSPolicySANValidation
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRoute303RedirectStatusCode
    - HTTPRoute307RedirectStatusCode
    - HTTPRoute308RedirectStatusCode
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteCORS
    - HTTPRouteNamedRouteRule
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
    - HTTPRouteRetry
    - HTTPRouteRetryBackendTimeout
    - HTTPRouteRetryConnectionError
    - ListenerSet
  name: GATEWAY-HTTP
  summary: Core tests partially succeeded with 7 test skips. Extended tests partially
    succeeded with 1 test skips.
- core:
    result: partial
    skippedTests:
    - GRPCRouteListenerHostnameMatching
    statistics:
      Failed: 0
      Passed: 14
      Skipped: 1
  extended:
    result: success
    statistics:
      Failed: 0
      Passed: 1
      Skipped: 0
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
  name: GATEWAY-GRPC
  summary: Core tests partially succeeded with 1 test skips. Extended tests succeeded.
- core:
    result: partial
    skippedTests:
    - TLSRouteHostnameIntersection
    - TLSRouteInvalidBackendRefNonexistent
    - TLSRouteInvalidBackendRefUnknownKind
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 16
      Skipped: 4
  extended:
    result: partial
    skippedTests:
    - TLSRouteTerminateSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 3
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - TLSRouteModeTerminate
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
    - TLSRouteModeMixed
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 4 test skips. Extended tests partially
    succeeded with 1 test skips.
succeededProvisionalTests:
- GatewayOptionalAddressValue

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

conformance test report - apisix mode

apiVersion: gateway.networking.k8s.io/v1
date: "2026-08-26T10:00:42Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.6.0
implementation:
  contact:
  - https://github.com/apache/apisix-ingress-controller/issues
  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
    - HTTPRouteInvalidBackendRefUnknownKind
    - HTTPRouteInvalidCrossNamespaceBackendRef
    - HTTPRouteInvalidNonExistentBackendRef
    - HTTPRouteListenerHostnameMatching
    - HTTPRouteMultipleGateways
    - HTTPRouteNoBackendRefs
    statistics:
      Failed: 0
      Passed: 30
      Skipped: 7
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - BackendTLSPolicy
    - BackendTLSPolicySANValidation
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRoute303RedirectStatusCode
    - HTTPRoute307RedirectStatusCode
    - HTTPRoute308RedirectStatusCode
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteCORS
    - HTTPRouteNamedRouteRule
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
    - HTTPRouteRetry
    - HTTPRouteRetryBackendTimeout
    - HTTPRouteRetryConnectionError
    - ListenerSet
  name: GATEWAY-HTTP
  summary: Core tests partially succeeded with 7 test skips. Extended tests partially
    succeeded with 1 test skips.
- core:
    result: partial
    skippedTests:
    - GRPCRouteListenerHostnameMatching
    statistics:
      Failed: 0
      Passed: 14
      Skipped: 1
  extended:
    result: success
    statistics:
      Failed: 0
      Passed: 1
      Skipped: 0
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
  name: GATEWAY-GRPC
  summary: Core tests partially succeeded with 1 test skips. Extended tests succeeded.
- core:
    result: partial
    skippedTests:
    - TLSRouteHostnameIntersection
    - TLSRouteInvalidBackendRefNonexistent
    - TLSRouteInvalidBackendRefUnknownKind
    - TLSRouteSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 16
      Skipped: 4
  extended:
    result: partial
    skippedTests:
    - TLSRouteTerminateSimpleSameNamespace
    statistics:
      Failed: 0
      Passed: 3
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - TLSRouteModeTerminate
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
    - TLSRouteModeMixed
  name: GATEWAY-TLS
  summary: Core tests partially succeeded with 4 test skips. Extended tests partially
    succeeded with 1 test skips.
succeededProvisionalTests:
- GatewayOptionalAddressValue

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

conformance test report

apiVersion: gateway.networking.k8s.io/v1
date: "2026-08-26T10:20:20Z"
gatewayAPIChannel: experimental
gatewayAPIVersion: v1.6.0
implementation:
  contact:
  - https://github.com/apache/apisix-ingress-controller/issues
  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
    - HTTPRouteMultipleGateways
    - HTTPRouteNoBackendRefs
    result: failure
    skippedTests:
    - HTTPRouteHTTPSListener
    statistics:
      Failed: 3
      Passed: 33
      Skipped: 1
  extended:
    result: partial
    skippedTests:
    - HTTPRouteRedirectPortAndScheme
    statistics:
      Failed: 0
      Passed: 12
      Skipped: 1
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - HTTPRouteBackendProtocolWebSocket
    - HTTPRouteDestinationPortMatching
    - HTTPRouteHostRewrite
    - HTTPRouteMethodMatching
    - HTTPRoutePathRewrite
    - HTTPRoutePortRedirect
    - HTTPRouteQueryParamMatching
    - HTTPRouteRequestMirror
    - HTTPRouteResponseHeaderModification
    - HTTPRouteSchemeRedirect
    unsupportedFeatures:
    - BackendTLSPolicy
    - BackendTLSPolicySANValidation
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - HTTPRoute303RedirectStatusCode
    - HTTPRoute307RedirectStatusCode
    - HTTPRoute308RedirectStatusCode
    - HTTPRouteBackendProtocolH2C
    - HTTPRouteBackendRequestHeaderModification
    - HTTPRouteBackendTimeout
    - HTTPRouteCORS
    - HTTPRouteNamedRouteRule
    - HTTPRouteParentRefPort
    - HTTPRoutePathRedirect
    - HTTPRouteRequestMultipleMirrors
    - HTTPRouteRequestPercentageMirror
    - HTTPRouteRequestTimeout
    - HTTPRouteRetry
    - HTTPRouteRetryBackendTimeout
    - HTTPRouteRetryConnectionError
    - ListenerSet
  name: GATEWAY-HTTP
  summary: Core tests failed with 3 test failures. Extended tests partially succeeded
    with 1 test skips.
- core:
    failedTests:
    - GatewayModifyListeners
    result: failure
    statistics:
      Failed: 1
      Passed: 14
      Skipped: 0
  extended:
    result: success
    statistics:
      Failed: 0
      Passed: 1
      Skipped: 0
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
  name: GATEWAY-GRPC
  summary: Core tests failed with 1 test failures. Extended tests succeeded.
- core:
    failedTests:
    - GatewayModifyListeners
    - TLSRouteHostnameIntersection
    - TLSRouteInvalidBackendRefNonexistent
    - TLSRouteInvalidBackendRefUnknownKind
    - TLSRouteSimpleSameNamespace
    result: failure
    statistics:
      Failed: 5
      Passed: 15
      Skipped: 0
  extended:
    failedTests:
    - TLSRouteTerminateSimpleSameNamespace
    result: failure
    statistics:
      Failed: 1
      Passed: 3
      Skipped: 0
    supportedFeatures:
    - GatewayAddressEmpty
    - GatewayPort8080
    - TLSRouteModeTerminate
    unsupportedFeatures:
    - GatewayBackendClientCertificate
    - GatewayFrontendClientCertificateValidation
    - GatewayFrontendClientCertificateValidationInsecureFallback
    - GatewayHTTPListenerIsolation
    - GatewayHTTPSListenerDetectMisdirectedRequests
    - GatewayInfrastructurePropagation
    - GatewayStaticAddresses
    - ListenerSet
    - TLSRouteModeMixed
  name: GATEWAY-TLS
  summary: Core tests failed with 5 test failures. Extended tests failed with 1 test
    failures.
succeededProvisionalTests:
- GatewayOptionalAddressValue

@shreemaan-abhishek shreemaan-abhishek self-assigned this Jul 27, 2026
x509.CertPool skips PEM blocks it cannot decode, so a bundle whose second
certificate is broken passed validation here and failed later at the ADC
server, which parses the whole bundle. Reject it up front instead.
@shreemaan-abhishek

Copy link
Copy Markdown
Contributor Author

Pushed a follow-up from the review of the ADC-side PR (api7/adc#537): the CA bundle is now validated by parsing every certificate in it.

x509.CertPool.AppendCertsFromPEM returns true as long as one block parses and silently skips the rest, so a bundle whose second certificate is broken passed here and then failed at the ADC server — which does parse the whole bundle — as an opaque sync error. That is exactly the late, unclear failure this field was meant to avoid, so the translator now rejects it up front, with the same semantics on both sides.

Test cases added for a header with no certificate, an unparseable body, a private key in place of a certificate, and one good plus one broken certificate; a multi-certificate bundle is still accepted.

Both call sites pass http.MethodPut, and the new test made unparam
report it. Set the method inside instead of threading it through.
# Conflicts:
#	api/adc/types.go
#	internal/adc/client/executor.go
#	internal/adc/client/executor_test.go
Comment thread api/v1alpha1/gatewayproxy_types.go Outdated
// Set it when the control plane uses a self-signed or private CA certificate.
// It has no effect when tlsVerify is false.
// +optional
CaBundle string `json:"caBundle,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Update the Helm-bundled CRD before exposing this field. api7/api7-helm-chart still has no caBundle property in charts/ingress-controller/crds/apisix-crds.yaml. With that standard installation the API server prunes this unknown field, so the controller never receives it. Please add the paired Helm chart PR and release dependency.

// CaCert is the PEM-encoded CA certificate (or bundle) the ADC server verifies
// the control plane against. Older ADC servers ignore it, and omitempty keeps
// requests without a CA bundle byte for byte what they were.
CaCert string `json:"caCert,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[P1] Ship an ADC version that honors caCert. This repository still pins ADC 0.27.1 and the API7 Helm chart pins 0.26.0, while api7/adc#537 is open and unreleased; both released sidecars accept this unknown option but ignore it. The CRD can therefore be accepted while private-CA verification still fails in every normal install. Please merge and release the ADC change, bump the pinned/chart image, and exercise this TLS path end to end before merging this PR.

The CA bundle reaches the ADC server as caCert, which lands in 0.29.0
(api7/adc#552). Older sidecars accept the option and ignore it, so the
CRD field would be set while verification still fails.
@shreemaan-abhishek

Copy link
Copy Markdown
Contributor Author

Follow-up: pinned ADC 0.29.0, keeping this in step with apache/apisix-ingress-controller#2826.

Worth recording, since the PR body said otherwise: api7/adc#537 was closed, not merged. The server-side support landed via api7/adc#552 (feat(server): support custom tls config per endpoint) and shipped in v0.29.0. It uses the same caCert task option this PR already sends, so no controller-side change was needed — only the pin.

Verified against the published image rather than assumed: ghcr.io/api7/adc:0.29.0 in ingress mode, an HTTPS backend with a private-CA certificate, replaying the exact body buildHTTPRequest produces. Without caCert it fails with unable to verify the first certificate; with it the handshake completes and the backend's own 404 surfaces. The previously pinned 0.27.1 behaves identically with and without the option.

@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 `@Makefile`:
- Line 33: Update the Makefile download rule that uses ADC_VERSION so Windows
selects the ADC .zip asset and extracts it with the appropriate
Windows-compatible command, while Linux and macOS continue using .tar.gz.
Preserve support for ADC_VERSION=dev and ensure the platform-specific archive
naming and extraction paths remain consistent.
🪄 Autofix

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: d8113fc8-5cf9-41a8-a0a7-7753cdb2a7e4

📥 Commits

Reviewing files that changed from the base of the PR and between 6d905a8 and b4bd118.

📒 Files selected for processing (1)
  • Makefile

Comment thread Makefile

DASHBOARD_VERSION ?= dev
ADC_VERSION ?= 0.27.1
ADC_VERSION ?= 0.29.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 5 'ADC_VERSION|kind-load-adc-image|ghcr.io/api7/adc' \
  Makefile .github/workflows

curl -fsSL https://api.github.com/repos/api7/adc/releases/tags/v0.29.0 |
  python -c '
import json
import sys

release = json.load(sys.stdin)
assert release["tag_name"] == "v0.29.0"
for asset in release["assets"]:
    print(asset["name"])
'

Repository: api7/api7-ingress-controller

Length of output: 10521


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Makefile platform variables and ADC targets ---'
rg -n -C 8 '^(GOOS|GOARCH)|ADC_BIN|^adc:|adc_\$\{ADC_VERSION\}|CA.?bundle|ca.?bundle' Makefile

printf '%s\n' '--- Workflow ADC and CA-bundle references ---'
rg -n -i -C 6 'ADC_VERSION|adc(\.js|-temp)?|ca.?bundle' .github/workflows

Repository: api7/api7-ingress-controller

Length of output: 11607


Handle Windows ADC archives in the download rule.

CI intentionally uses ADC_VERSION=dev in the E2E workflows. The v0.29.0 Linux and macOS assets match the .tar.gz download rule, but its Windows assets use .zip. If Windows is supported, select the correct archive format and extraction command.

🤖 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 `@Makefile` at line 33, Update the Makefile download rule that uses ADC_VERSION
so Windows selects the ADC .zip asset and extracts it with the appropriate
Windows-compatible command, while Linux and macOS continue using .tar.gz.
Preserve support for ADC_VERSION=dev and ensure the platform-specific archive
naming and extraction paths remain consistent.

Puts an openresty in front of the admin API to stand in for a control
plane published over TLS, with a certificate signed by a CA generated
per test -- the case caBundle exists for, since nothing in the system
trust store can verify it.

Three specs: the route is programmed when the bundle is trusted, the
sync fails on certificate verification when it is absent, and the API
server rejects a caBundle that is not a certificate.

The certificates are generated locally rather than with the scaffold's
GenerateMACert, which gives the CA and the leaf the same subject; OpenSSL
reads that as self-issued and rejects it without ever chaining to the CA.
@shreemaan-abhishek

Copy link
Copy Markdown
Contributor Author

Added in 99612161 (ported from apache/apisix-ingress-controller#2826) — test/e2e/crds/v1alpha1/gatewayproxy_tls.go, labelled apisix.apache.org, v1alpha1, gatewayproxy so it runs in the existing matrix.

The admin API only listens on plain HTTP, so the spec puts an openresty in front of it to stand in for a control plane published over TLS. Its certificate is signed by a CA generated per test, which is exactly the case caBundle exists for: nothing in the system trust store can verify it. That image is already pulled and loaded by make kind-load-images, so no new dependency.

Three specs:

spec asserts
syncs to a private-CA control plane when caBundle is trusted route is programmed and serves 200 — the sync got through TLS verification
fails to sync when caBundle is missing controller logs unable to verify the first certificate, and the route is never programmed
rejects a caBundle that is not a certificate the API server rejects it via the CEL rule

Run locally against kind, both provider modes:

PROVIDER_TYPE=apisix-standalone make e2e-test TEST_FOCUS='control plane TLS'
  Ran 3 of 237 Specs in 97.522 seconds -- SUCCESS! 3 Passed | 0 Failed

PROVIDER_TYPE=apisix make e2e-test TEST_FOCUS='control plane TLS'
  Ran 3 of 236 Specs in 115.785 seconds -- SUCCESS! 3 Passed | 0 Failed

Writing it turned up something worth flagging separately: the spec generates its certificates locally instead of using scaffold.GenerateMACert, because that helper gives the CA and the leaf the same subject (O=Acme Co, no CN) — ssl.go:150 and ssl.go:180. OpenSSL reads such a leaf as self-issued and fails with DEPTH_ZERO_SELF_SIGNED_CERT without ever chaining to the CA. My first run failed on exactly that, with the controller sending everything correctly:

"config": {...,"tlsVerify":true,"hasCaBundle":true}
"error": ... HTTP 500: {"message":"Error: self-signed certificate; ..."}

It goes unnoticed today because the current callers verify with Go's x509 (webhook caBundle) or use the CA only as a client-auth trust anchor, neither of which trips that OpenSSL check. Happy to fix the helper in a follow-up if you'd like — I kept it out of this PR to avoid touching the certificates other specs depend on.

@shreemaan-abhishek

Copy link
Copy Markdown
Contributor Author

Merged master in and re-ran CI.

Note on the one red job, e2e-test (apisix-standalone, networking.k8s.io): it fails on TCPRoute Base should route TCP traffic to backend service, which is unrelated to this PR. That test is broken on master here, and the fix is already open as #464 (backport of apache/apisix-ingress-controller#2836, merged upstream on 2026-08-06). The same job fails on #467 too. It will stay red on this branch until #464 lands.

Every job covering this PR's specs (apisix.apache.org, both provider modes) is green.

…envelope

Follows the value/valueFrom shape adminKey already uses on the same
struct, so a valueFrom source can be added later without breaking
existing resources. Only the inline value is supported today.

The PEM check moves from a provider-level CEL rule onto the field.
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.

Support configuring a CA bundle for the control plane connection (GatewayProxy)

3 participants