Skip to content

fix(gateway): reject identical main and sandbox vhosts#2146

Draft
mehara-rothila wants to merge 4 commits into
wso2:feature/operation-level-epfrom
mehara-rothila:fix/same-vhost-route-collision
Draft

fix(gateway): reject identical main and sandbox vhosts#2146
mehara-rothila wants to merge 4 commits into
wso2:feature/operation-level-epfrom
mehara-rothila:fix/same-vhost-route-collision

Conversation

@mehara-rothila

@mehara-rothila mehara-rothila commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Purpose

A RestApi with a sandbox upstream whose vhost resolves to the same value as the main vhost was rejected by the RuntimeDeployConfig transform path but accepted by the Envoy xDS translator. Because per-API translation errors are only logged and swallowed, the deployment returned 201 Created while the policy xDS received no configuration for the API. With the policy engine running fail-closed, every request to the API then returned 500 at runtime, with no signal to the API author beyond a swallowed log line.

Resolves #2145

Goals

Reject the same-vhost collision consistently and at deploy time, returning a clear 400 to the author instead of accepting a misconfiguration that silently fails at runtime. Ensure the two xDS builders can never disagree about whether a configuration is valid, since that disagreement was the root cause.

Approach

The collision is now caught at every layer it can arise:

  • Vhost resolution, which already froze router defaults into fully-omitted vhosts on create, now also fills blank or individually-omitted fields, and runs on the update (PUT) path in addition to create. Validation therefore always compares the effective vhosts, whether they come from explicit values, partial configurations, or router defaults.
  • The API validator rejects identical main and sandbox vhosts (when a sandbox upstream is configured) with a 400 at deploy time. The comparison is case-insensitive and whitespace-trimmed, since Envoy matches domains case-insensitively.
  • Both xDS builders (the Envoy translator and the RuntimeDeployConfig transform path) now apply the same case-insensitive guard, kept identical to each other, so they reject any remaining collision consistently and the inconsistent main-xDS / policy-xDS state can no longer occur.
  • A gateway configured with identical main and sandbox vhost defaults logs a startup warning instead of failing to boot, so main-only APIs keep working while sandbox-enabled APIs are rejected per API.

User stories

As an API developer, when I deploy an API whose main and sandbox vhosts collide, I want it rejected immediately with a clear error, rather than accepted and then failing every request at runtime.

Documentation

N/A. This changes internal validation and xDS-translation behavior only; there is no user-facing API or product-documentation impact.

Automation tests

  • Unit tests

    Added validator cases (identical, case-insensitively identical, distinct, no-sandbox-upstream, absent vhosts); vhost-resolution cases (omitted/blank sandbox and blank main filled from defaults, existing sentinel/nil behaviour unchanged); and translator cases (explicit identical, case-only, and defaults-derived collisions all rejected). All existing unit tests in the affected packages (pkg/config, pkg/utils, pkg/xds, pkg/transform) pass.

  • Integration tests

    No new integration tests; the change is covered by unit tests at the validator, the resolver, and both xDS builders. The existing integration suite is unaffected.

Security checks

Samples

Before the fix, this configuration deployed with 201 and then returned 500 on every request:

apiVersion: gateway.api-platform.wso2.com/v1alpha1
kind: RestApi
metadata:
  name: same-vhost-demo-v1.0
spec:
  displayName: Same-Vhost-Demo
  version: v1.0
  context: /same-vhost-demo/$version
  vhosts:
    main: collide.local
    sandbox: collide.local
  upstream:
    main:
      url: http://backend:8080/main-be
    sandbox:
      url: http://backend:8080/sb-be
  operations:
    - method: GET
      path: /endpoint

After the fix, the same deployment is rejected at admission:

spec.vhosts: Main and sandbox vhosts must differ when a sandbox upstream is configured

Related PRs

None.

Test environment

  • Go (version per gateway/gateway-controller/go.mod)
  • Reproduced and verified on the gateway integration Docker stack (Linux containers) via Rancher Desktop on a Windows host
  • Controller storage backend: SQLite (default)

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 42714d33-24d9-4994-b880-4ee92a7d2352

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR ensures main and sandbox vhosts do not resolve to the same effective hostname. It resolves gateway-default vhost sentinels to concrete router defaults, validates REST API spec.vhosts (case- and whitespace-insensitive) when a sandbox upstream exists, adds the same guard to the XDS translator, and emits a startup warning if router defaults collide. Unit tests cover sentinel resolution, validator behavior, and translator rejection cases.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Service as REST API Service
    participant Resolve as ResolveVhostSentinels
    participant Validator as APIValidator
    participant Translator as XDS Translator
    participant Controller as Gateway Controller

    Client->>Service: Submit/Update RestAPI
    Service->>Resolve: ResolveVhostSentinels(cfg, routerCfg)
    Resolve-->>Service: Return config with concrete vhosts
    Service->>Validator: Validate resolved config (spec.vhosts)
    alt vhosts equal (case/whitespace-insensitive) and sandbox present
        Validator-->>Client: 400 Bad Request (spec.vhosts validation error)
    else validation passed
        Service->>Translator: translateAPIConfig(resolvedConfig)
        alt translator detects same effective vhost
            Translator-->>Service: error ("same vhost")
            Service-->>Client: 400 Bad Request (translation error)
        else translation succeeds
            Translator->>Controller: Apply XDS config
            Controller-->>Client: 201 Created
        end
    end
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: rejecting identical main and sandbox vhosts to prevent route collisions, which is the primary objective of this PR.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from issue #2145: validation rejects colliding vhosts, both xDS paths guard against collisions, vhost resolution fills omitted fields, and comprehensive tests verify all scenarios.
Out of Scope Changes check ✅ Passed All changes are directly scoped to preventing same-vhost collisions between main and sandbox upstreams. No unrelated modifications or extraneous changes are present.
Description check ✅ Passed The pull request description provides comprehensive coverage of all required sections with clear explanations of the problem, solution, testing approach, and deployment details.

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

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@mehara-rothila mehara-rothila force-pushed the fix/same-vhost-route-collision branch 3 times, most recently from 206b216 to 9fa9d4b Compare June 11, 2026 05:10
@mehara-rothila

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

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.

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 `@gateway/gateway-controller/pkg/xds/translator.go`:
- Around line 834-838: The comparison between effectiveMainVHost and
effectiveSandboxVHost should mirror validator semantics by normalizing case and
surrounding whitespace before comparing; update the equality check in the
translator logic that currently compares effectiveMainVHost ==
effectiveSandboxVHost to instead compare trimmed, case-insensitive versions
(e.g., use strings.TrimSpace and strings.EqualFold) so that values differing
only by case or extra whitespace are treated as equal and the same
route-conflict error is triggered.
🪄 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: 8c1ac97a-09a4-4e6d-aee2-6b26e053448e

📥 Commits

Reviewing files that changed from the base of the PR and between 46a4edc and 9fa9d4b.

📒 Files selected for processing (8)
  • gateway/gateway-controller/cmd/controller/main.go
  • gateway/gateway-controller/pkg/config/api_validator.go
  • gateway/gateway-controller/pkg/config/api_validator_test.go
  • gateway/gateway-controller/pkg/service/restapi/service.go
  • gateway/gateway-controller/pkg/utils/api_deployment.go
  • gateway/gateway-controller/pkg/utils/api_deployment_test.go
  • gateway/gateway-controller/pkg/xds/translator.go
  • gateway/gateway-controller/pkg/xds/translator_test.go

Comment thread gateway/gateway-controller/pkg/xds/translator.go
Identical main and sandbox vhosts collide on the same route key, but only one xDS path rejected them while the other accepted them, so the API deployed (201) then returned 500. Both paths now reject the collision and the validator returns a 400 at deploy time; equal vhost defaults log a startup warning instead of failing to boot.
@mehara-rothila mehara-rothila force-pushed the fix/same-vhost-route-collision branch from 9fa9d4b to 1a1e03e Compare June 11, 2026 08:04
@mehara-rothila

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

Review feedback: the latent collision must not be storable. The xDS builders are unchanged since no sandbox upstream means no sandbox routes.
@mehara-rothila mehara-rothila force-pushed the fix/same-vhost-route-collision branch from 8cf393e to ef47b59 Compare June 12, 2026 07:06
A templated vhost that renders blank slipped past validation and the collision only surfaced later in xDS. Re-resolving after RenderSpec fills the blank with the router default before the same-vhost check, giving a clean 400.
Also clarifies the equal-default startup warning, which overstated that all REST deploys are rejected (explicit distinct vhosts still pass). From code review.
@mehara-rothila mehara-rothila changed the base branch from main to feature/operation-level-ep June 12, 2026 13:00
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.

[Bug]: xDS paths disagree on same-vhost configs

1 participant