Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pr-kubernetes-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
# May 19, 2025: ~20 minutes
- cluster-name: 'cluster-four'
go-test-args: '-v -timeout=25m'
go-test-run-regex: '^TestKgateway$$/^ExtProc$$|^TestKgateway$$/^ExtAuth$$|^TestKgateway$$/^TCPRouteServices$$|^TestKgateway$$/^PolicySelector$$'
go-test-run-regex: '^TestKgateway$$/^ExtProc$$|^TestKgateway$$/^ExtAuth$$|^TestKgateway$$/^TCPRouteServices$$|^TestKgateway$$/^PolicySelector$$|^TestKgateway$$/^CSRF$$'
localstack: 'false'
# May 19, 2025: ~20 minutes
- cluster-name: 'cluster-ai'
Expand Down
43 changes: 43 additions & 0 deletions api/applyconfiguration/api/v1alpha1/csrfpolicy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions api/applyconfiguration/api/v1alpha1/trafficpolicyspec.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions api/applyconfiguration/internal/internal.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions api/applyconfiguration/utils.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions api/v1alpha1/traffic_policy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ type TrafficPolicySpec struct {
// Cors specifies the CORS configuration for the policy.
// +optional
Cors *CorsPolicy `json:"cors,omitempty"`

// Csrf specifies the Cross-Site Request Forgery (CSRF) policy for this traffic policy.
// +optional
Csrf *CsrfPolicy `json:"csrf,omitempty"`
}

// TransformationPolicy config is used to modify envoy behavior at a route level.
Expand Down Expand Up @@ -342,3 +346,28 @@ type CorsPolicy struct {
// +kubebuilder:pruning:PreserveUnknownFields
*gwv1.HTTPCORSFilter `json:",inline"`
}

// CsrfPolicy can be used to set percent of requests for which the CSRF filter is enabled,
// enable shadow-only mode where policies will be evaluated and tracked, but not enforced and
// add additional source origins that will be allowed in addition to the destination origin.
type CsrfPolicy struct {
// Specifies the percentage of requests for which the CSRF filter is enabled.
// If both PercentageEnabled and PercentageShadowed are set, the PercentageEnabled flag will take precedence.
// +required
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=100
PercentageEnabled *uint32 `json:"percentageEnabled,omitempty"`

// Specifies that CSRF policies will be evaluated and tracked, but not enforced.
// This is intended to be used when PercentageEnabled is 0 and will be ignored otherwise.
// If both PercentageEnabled and PercentageShadowed are set, the PercentageEnabled flag will take precedence.
// +optional
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=100
PercentageShadowed *uint32 `json:"percentageShadowed,omitempty"`

// Specifies additional source origins that will be allowed in addition to the destination origin.
// Only exact matches are supported.
// +optional
AdditionalOrigins []string `json:"additionalOrigins,omitempty"`
}
35 changes: 35 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,25 @@ spec:
type: integer
type: object
x-kubernetes-preserve-unknown-fields: true
csrf:
properties:
additionalOrigins:
items:
type: string
type: array
percentageEnabled:
format: int32
maximum: 100
minimum: 0
type: integer
percentageShadowed:
format: int32
maximum: 100
minimum: 0
type: integer
required:
- percentageEnabled
type: object
extAuth:
properties:
contextExtensions:
Expand Down
93 changes: 93 additions & 0 deletions internal/kgateway/extensions2/plugins/trafficpolicy/csrf_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package trafficpolicy

import (
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
envoy_csrf_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/csrf/v3"
envoy_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
"google.golang.org/protobuf/proto"

"github.com/kgateway-dev/kgateway/v2/api/v1alpha1"
)

const (
csrfExtensionFilterName = "envoy.filters.http.csrf"
csrfFilterEnabledKey = "envoy.csrf.filter_enabled"
csrfShadowEnabledKey = "envoy.csrf.shadow_enabled"
)

type CsrfIR struct {
csrfPolicy *envoy_csrf_v3.CsrfPolicy
}

func (c *CsrfIR) Equals(other *CsrfIR) bool {
if c == nil && other == nil {
return true
}
if c == nil || other == nil {
return false
}

return proto.Equal(c.csrfPolicy, other.csrfPolicy)
}

// csrfForSpec translates the CSRF spec into and onto the IR policy
func csrfForSpec(spec v1alpha1.TrafficPolicySpec, out *trafficPolicySpecIr) error {
if spec.Csrf == nil {
return nil
}

csrfPolicy := &envoy_csrf_v3.CsrfPolicy{}

// Set filter enabled percentage
if spec.Csrf.PercentageEnabled != nil {
csrfPolicy.FilterEnabled = &envoy_core_v3.RuntimeFractionalPercent{
DefaultValue: &envoy_type_v3.FractionalPercent{
Numerator: *spec.Csrf.PercentageEnabled,
Denominator: envoy_type_v3.FractionalPercent_HUNDRED,
},
RuntimeKey: csrfFilterEnabledKey,
}
}

// Set shadow enabled percentage if specified
if spec.Csrf.PercentageShadowed != nil {
csrfPolicy.ShadowEnabled = &envoy_core_v3.RuntimeFractionalPercent{
DefaultValue: &envoy_type_v3.FractionalPercent{
Numerator: *spec.Csrf.PercentageShadowed,
Denominator: envoy_type_v3.FractionalPercent_HUNDRED,
},
RuntimeKey: csrfShadowEnabledKey,
}
}

// Add additional origins if specified
if len(spec.Csrf.AdditionalOrigins) > 0 {
csrfPolicy.AdditionalOrigins = make([]*envoy_matcher_v3.StringMatcher, len(spec.Csrf.AdditionalOrigins))
for i, origin := range spec.Csrf.AdditionalOrigins {
csrfPolicy.GetAdditionalOrigins()[i] = &envoy_matcher_v3.StringMatcher{
MatchPattern: &envoy_matcher_v3.StringMatcher_Exact{
Exact: origin,
},
}
}
}

out.csrf = &CsrfIR{
csrfPolicy: csrfPolicy,
}
return nil
}

// csrfFilter returns a default csrf filter with the filter enabled percentage set to 0 to be added to the filter
// chain.
func csrfFilter() *envoy_csrf_v3.CsrfPolicy {
return &envoy_csrf_v3.CsrfPolicy{
FilterEnabled: &envoy_core_v3.RuntimeFractionalPercent{
DefaultValue: &envoy_type_v3.FractionalPercent{
Numerator: 0,
Denominator: envoy_type_v3.FractionalPercent_HUNDRED,
},
},
}
}
Loading
Loading