Skip to content

Conversation

@askosyrskiy
Copy link

@askosyrskiy askosyrskiy commented Jan 4, 2026

Summary

Fixes #14741

This PR adds label-based exclusion for Gateway API resources to prevent validation and watching of HTTPRoutes with unsupported features (e.g., Gateway API v1.3+ CORS filter).

Note: This PR was simplified to use a boolean flag with a hardcoded label instead of configurable label selectors, making it much easier to use.

Problem

Users running Gateway API v1.3 with features not yet supported by Linkerd (which supports v1.2) experience:

  1. Webhook validation failures - HTTPRoutes with CORS filters are rejected during creation
  2. High CPU usage - Policy controller continuously fails to deserialize v1.3 HTTPRoutes

Solution

This PR implements a dual-layer filtering system with a simple boolean flag:

1. Webhook objectSelector

  • Splits ValidatingWebhookConfiguration into two webhooks:
    • linkerd-policy-validator.linkerd.io - validates policy.linkerd.io resources
    • linkerd-policy-validator-gateway.linkerd.io - validates gateway.networking.k8s.io resources with optional objectSelector
  • When enabled, prevents HTTPRoutes labeled with config.linkerd.io/policy-validation: disabled from being sent to the admission webhook

2. Watch label selector

  • Adds --skip-labeled-gateway-api-routes CLI flag to policy controller
  • Applies hardcoded label selector to all Gateway API route watches (HTTPRoute, GRPCRoute, TLSRoute, TCPRoute)
  • Prevents labeled HTTPRoutes from being watched and deserialized

Usage

Step 1: Enable the feature

Set the boolean flag in your Linkerd configuration:

policyValidator:
  skipLabeledGatewayAPIRoutes: true

Step 2: Label routes to skip

Add the label to HTTPRoutes you want to exclude:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-cors-route
  labels:
    config.linkerd.io/policy-validation: disabled
spec:
  # v1.3 features like CORS filter

That's it! No complex label selector configuration needed.

Implementation Details

The feature uses a hardcoded label config.linkerd.io/policy-validation: disabled:

  • Simple configuration: Just a boolean flag (skipLabeledGatewayAPIRoutes: false)
  • No regex parsing: Helm templates use simple conditionals with hardcoded values
  • Clean CLI: --skip-labeled-gateway-api-routes=true instead of passing label selector strings
  • Explicit naming: Makes it clear we're skipping labeled routes, not all Gateway API routes

Changes

  • policy-controller/runtime/src/args.rs: Add skip_labeled_gateway_api_routes boolean argument
  • charts/linkerd-control-plane/values.yaml: Add skipLabeledGatewayAPIRoutes boolean flag
  • charts/linkerd-control-plane/templates/destination.yaml: Pass boolean flag as CLI argument
  • charts/linkerd-control-plane/templates/destination-rbac.yaml: Add objectSelector with hardcoded label

Testing

  • ✅ Rust code compiles (cargo check)
  • ✅ Helm templates render correctly with flag enabled/disabled
  • ✅ Webhook objectSelector only appears when flag is enabled
  • ✅ Policy controller receives correct CLI flag
  • ✅ Split webhook configuration works correctly

Backward Compatibility

  • Default behavior unchanged (skipLabeledGatewayAPIRoutes: false)
  • All existing resources continue to be validated and watched
  • No breaking changes

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 noreply@anthropic.com

Fixes linkerd#14741

Users can now exclude Gateway API HTTPRoutes (and other Gateway API
resources) from validation and watching by adding labels to them.
This prevents issues when using Gateway API v1.3+ features (like CORS
filter) that Linkerd doesn't yet support.

Changes:
- Add objectSelector to policy validator webhook for Gateway API resources
- Split ValidatingWebhookConfiguration into separate webhooks for
  policy.linkerd.io and gateway.networking.k8s.io resources
- Add --gateway-api-label-selector CLI flag to policy controller
- Apply label selector to all Gateway API route watches (HTTPRoute,
  GRPCRoute, TLSRoute, TCPRoute)

This prevents both:
1. Webhook validation failures blocking HTTPRoute creation
2. High CPU usage from policy controller continuously failing to
   deserialize unsupported Gateway API resources

Usage:
Label HTTPRoutes to exclude them:
  config.linkerd.io/policy-validation: disabled

Configure Linkerd with:
  policyValidator.objectSelector (for webhook filtering)
  policyValidator.gatewayAPILabelSelector (for watch filtering)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Alexey Skosyrskiy <askosyrskiy@metropolis.io>
@askosyrskiy askosyrskiy force-pushed the fix/14741-gateway-api-label-exclusion branch from 516c13a to 4c0ba52 Compare January 4, 2026 02:47
@askosyrskiy
Copy link
Author

Note: This is a Workaround

This PR provides a workaround for users who need to use Gateway API v1.3+ features (like the CORS filter) alongside Linkerd, which currently supports Gateway API v1.2.

Why a workaround?

While the ideal solution would be to upgrade Linkerd's Gateway API support to v1.3 or v1.4 (see #14836), there are significant challenges that make this upgrade non-trivial:

  1. Breaking changes in Gateway API versions
  2. Compatibility concerns with existing deployments
  3. Extensive testing required across multiple Gateway implementations
  4. API migration complexity for users

When to use this workaround

This label-based exclusion is appropriate for users who:

  • Are using another Gateway controller (e.g., Contour, Istio) that supports Gateway API v1.3+
  • Want to use newer Gateway API features not yet supported by Linkerd
  • Need Gateway API resources managed by external controllers to coexist with Linkerd's policy system

Future direction

Once #14836 (Gateway API upgrade) is completed, this exclusion mechanism will still be useful for:

  • Testing new Gateway API features before Linkerd supports them
  • Mixed environments with multiple Gateway controllers
  • Gradual migration scenarios

This workaround allows users to move forward with Gateway API v1.3+ features today while Linkerd works toward full native support.

Combine objectSelector and gatewayAPILabelSelector into one configuration.
Users now only need to set gatewayAPILabelSelector, which is automatically
used for both webhook validation and policy controller watches.

This reduces configuration complexity and follows the DRY principle.

Signed-off-by: Alexey Skosyrskiy <askosyrskiy@metropolis.io>
The policy validator webhook was split into two webhooks:
- linkerd-policy-validator.linkerd.io (for policy.linkerd.io resources)
- linkerd-policy-validator-gateway.linkerd.io (for gateway.networking.k8s.io resources)

Update the healthcheck to expect 2 webhooks for the policy validator
ValidatingWebhookConfiguration instead of 1.

Signed-off-by: Alexey Skosyrskiy <askosyrskiy@metropolis.io>
This simplifies the configuration interface introduced in commits
4c0ba52 and d4d1719 by replacing string-based label selector
configuration with a simple boolean flag and hardcoded label.

Problem

The previous implementation required users to configure a label
selector string (e.g., "config.linkerd.io/policy-validation!=disabled"):
- Made configuration more complex than necessary
- Required regex parsing in Helm templates to extract label key/value
- Used string-based CLI interface requiring label selector syntax
- Provided flexibility that users are unlikely to need

Solution

Replace gatewayAPILabelSelector string with skipLabeledGatewayAPIRoutes
boolean flag and hardcode the label config.linkerd.io/policy-validation:
disabled throughout the stack:

Helm values:
- Before: gatewayAPILabelSelector: "config.linkerd.io/policy-validation!=disabled"
- After: skipLabeledGatewayAPIRoutes: false

Webhook template (destination-rbac.yaml):
- Removed regex parsing: regexReplaceAll to extract label parts
- Hardcoded objectSelector with label key and value

Policy controller CLI (destination.yaml):
- Before: --gateway-api-label-selector=config.linkerd.io/policy-validation!=disabled
- After: --skip-labeled-gateway-api-routes=true

Rust code (args.rs):
- Before: gateway_api_label_selector: Option<String>
- After: skip_labeled_gateway_api_routes: bool
- Label selector hardcoded internally when flag is true

The new name skipLabeledGatewayAPIRoutes is explicit and self-documenting,
making it clear we're skipping labeled routes, not all Gateway API routes.

Validation

- Verified Rust code compiles: cargo check passes
- Verified Helm templates render correctly with flag enabled/disabled
- Confirmed webhook objectSelector only appears when flag is enabled
- Confirmed policy controller receives correct CLI flag
- Net change: 4 files modified, 22 insertions, 25 deletions

Fixes linkerd#14741

Signed-off-by: Alexey Skosyrskiy <askosyrskiy@metropolis.io>
Signed-off-by: Alexey Skosyrskiy <askosyrskiy@metropolis.io>
@askosyrskiy askosyrskiy force-pushed the fix/14741-gateway-api-label-exclusion branch from 49be25c to 021335f Compare January 6, 2026 18:29
@askosyrskiy askosyrskiy marked this pull request as ready for review January 6, 2026 18:30
@askosyrskiy askosyrskiy requested a review from a team as a code owner January 6, 2026 18:30
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.

linkerd control plane - policy controller high cpu usage when idle

1 participant