Skip to content

Commit 8a5bc79

Browse files
committed
address review comments: use gwapiv1.Fraction for CSRF rollout fields
- Replace `filterEnabled`/`shadowEnabled` (`*int32`) with `enforcedFraction`/ `shadowFraction` (`gwapiv1.Fraction`), matching how EG already models Envoy's RuntimeFractionalPercent (Tracing.SamplingFraction, RetryBudget.Percent), and translate them with the shared fractionalpercent helper. - Give the CSRF filter its own explicit order instead of sharing CORS's, so it runs after cors (preflights are answered by the cors filter) and before the authn/authz filters. - Preallocate the additionalOrigins slice to satisfy the prealloc linter. - Regenerate CRDs, deepcopy, helm golden files, API reference and testdata. - Move the release note to the new release-notes/current fragment layout so it lands in v1.9.0 instead of the already-cut v1.8.0-rc.1. Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com>
1 parent 0810f2b commit 8a5bc79

22 files changed

Lines changed: 358 additions & 101 deletions

File tree

api/v1alpha1/csrf_types.go

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55

66
package v1alpha1
77

8+
import (
9+
gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
10+
)
11+
812
// CSRF defines the configuration for the Cross-Site Request Forgery (CSRF) filter.
913
// The CSRF filter checks that the Origin header in HTTP requests matches the destination,
1014
// preventing cross-origin mutating requests (POST, PUT, DELETE, PATCH) from being processed.
@@ -15,25 +19,24 @@ package v1alpha1
1519
// host or host:port values, not full URLs. For example, use "www.example.com"
1620
// instead of "https://www.example.com".
1721
type CSRF struct {
18-
// FilterEnabled specifies the percentage of requests for which the CSRF filter is enabled.
19-
// When set, only the given percentage of requests will have CSRF protection enforced.
20-
// Defaults to 100 (fully enabled) if not specified.
22+
// EnforcedFraction represents the fraction of requests for which the CSRF
23+
// policy is enforced. Requests that are not selected are allowed through
24+
// without any origin validation.
25+
// Defaults to 100% (all requests are enforced) if not specified.
2126
//
2227
// +optional
23-
// +kubebuilder:validation:Minimum=0
24-
// +kubebuilder:validation:Maximum=100
25-
FilterEnabled *int32 `json:"filterEnabled,omitempty"`
28+
EnforcedFraction *gwapiv1.Fraction `json:"enforcedFraction,omitempty"`
2629

27-
// ShadowEnabled specifies the percentage of requests for which the CSRF filter is in
28-
// shadow/dry-run mode. In this mode, the filter evaluates requests and tracks whether
29-
// they would be allowed or rejected, but does not enforce the policy.
30-
// This is useful for rolling out CSRF protection gradually while monitoring the impact.
31-
// Only takes effect when FilterEnabled is not set or is 0.
30+
// ShadowFraction represents the fraction of requests for which the CSRF
31+
// policy is evaluated in shadow (dry-run) mode. In this mode, the filter
32+
// evaluates requests and tracks whether they would be allowed or rejected in
33+
// the `csrf.request_invalid` and `csrf.request_valid` stats, but does not
34+
// enforce the policy. This is useful for rolling out CSRF protection
35+
// gradually while monitoring the impact.
36+
// Only takes effect for requests that are not selected by EnforcedFraction.
3237
//
3338
// +optional
34-
// +kubebuilder:validation:Minimum=0
35-
// +kubebuilder:validation:Maximum=100
36-
ShadowEnabled *int32 `json:"shadowEnabled,omitempty"`
39+
ShadowFraction *gwapiv1.Fraction `json:"shadowFraction,omitempty"`
3740

3841
// AdditionalOrigins specifies additional origins that are allowed to make requests,
3942
// beyond the destination origin. These are checked against the Origin header (host:port only,

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_securitypolicies.yaml

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -700,9 +700,10 @@ spec:
700700
properties:
701701
additionalOrigins:
702702
description: |-
703-
AdditionalOrigins specifies additional origins that are allowed to make requests.
704-
These are checked against the Origin header and if matched, the request is allowed.
705-
Each origin can be an exact, prefix, suffix, or regex match using StringMatch.
703+
AdditionalOrigins specifies additional origins that are allowed to make requests,
704+
beyond the destination origin. These are checked against the Origin header (host:port only,
705+
not the full URL) and if matched, the request is allowed.
706+
Each origin supports Exact, Prefix, Suffix, and RegularExpression matching.
706707
items:
707708
description: |-
708709
StringMatch defines how to match any strings.
@@ -727,7 +728,55 @@ spec:
727728
required:
728729
- value
729730
type: object
731+
maxItems: 16
730732
type: array
733+
enforcedFraction:
734+
description: |-
735+
EnforcedFraction represents the fraction of requests for which the CSRF
736+
policy is enforced. Requests that are not selected are allowed through
737+
without any origin validation.
738+
Defaults to 100% (all requests are enforced) if not specified.
739+
properties:
740+
denominator:
741+
default: 100
742+
format: int32
743+
minimum: 1
744+
type: integer
745+
numerator:
746+
format: int32
747+
minimum: 0
748+
type: integer
749+
required:
750+
- numerator
751+
type: object
752+
x-kubernetes-validations:
753+
- message: numerator must be less than or equal to denominator
754+
rule: self.numerator <= self.denominator
755+
shadowFraction:
756+
description: |-
757+
ShadowFraction represents the fraction of requests for which the CSRF
758+
policy is evaluated in shadow (dry-run) mode. In this mode, the filter
759+
evaluates requests and tracks whether they would be allowed or rejected in
760+
the `csrf.request_invalid` and `csrf.request_valid` stats, but does not
761+
enforce the policy. This is useful for rolling out CSRF protection
762+
gradually while monitoring the impact.
763+
Only takes effect for requests that are not selected by EnforcedFraction.
764+
properties:
765+
denominator:
766+
default: 100
767+
format: int32
768+
minimum: 1
769+
type: integer
770+
numerator:
771+
format: int32
772+
minimum: 0
773+
type: integer
774+
required:
775+
- numerator
776+
type: object
777+
x-kubernetes-validations:
778+
- message: numerator must be less than or equal to denominator
779+
rule: self.numerator <= self.denominator
731780
type: object
732781
extAuth:
733782
description: ExtAuth defines the configuration for External Authorization.

charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -699,9 +699,10 @@ spec:
699699
properties:
700700
additionalOrigins:
701701
description: |-
702-
AdditionalOrigins specifies additional origins that are allowed to make requests.
703-
These are checked against the Origin header and if matched, the request is allowed.
704-
Each origin can be an exact, prefix, suffix, or regex match using StringMatch.
702+
AdditionalOrigins specifies additional origins that are allowed to make requests,
703+
beyond the destination origin. These are checked against the Origin header (host:port only,
704+
not the full URL) and if matched, the request is allowed.
705+
Each origin supports Exact, Prefix, Suffix, and RegularExpression matching.
705706
items:
706707
description: |-
707708
StringMatch defines how to match any strings.
@@ -726,7 +727,55 @@ spec:
726727
required:
727728
- value
728729
type: object
730+
maxItems: 16
729731
type: array
732+
enforcedFraction:
733+
description: |-
734+
EnforcedFraction represents the fraction of requests for which the CSRF
735+
policy is enforced. Requests that are not selected are allowed through
736+
without any origin validation.
737+
Defaults to 100% (all requests are enforced) if not specified.
738+
properties:
739+
denominator:
740+
default: 100
741+
format: int32
742+
minimum: 1
743+
type: integer
744+
numerator:
745+
format: int32
746+
minimum: 0
747+
type: integer
748+
required:
749+
- numerator
750+
type: object
751+
x-kubernetes-validations:
752+
- message: numerator must be less than or equal to denominator
753+
rule: self.numerator <= self.denominator
754+
shadowFraction:
755+
description: |-
756+
ShadowFraction represents the fraction of requests for which the CSRF
757+
policy is evaluated in shadow (dry-run) mode. In this mode, the filter
758+
evaluates requests and tracks whether they would be allowed or rejected in
759+
the `csrf.request_invalid` and `csrf.request_valid` stats, but does not
760+
enforce the policy. This is useful for rolling out CSRF protection
761+
gradually while monitoring the impact.
762+
Only takes effect for requests that are not selected by EnforcedFraction.
763+
properties:
764+
denominator:
765+
default: 100
766+
format: int32
767+
minimum: 1
768+
type: integer
769+
numerator:
770+
format: int32
771+
minimum: 0
772+
type: integer
773+
required:
774+
- numerator
775+
type: object
776+
x-kubernetes-validations:
777+
- message: numerator must be less than or equal to denominator
778+
rule: self.numerator <= self.denominator
730779
type: object
731780
extAuth:
732781
description: ExtAuth defines the configuration for External Authorization.

internal/gatewayapi/securitypolicy.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1777,13 +1777,13 @@ func (t *Translator) buildCORS(cors *egv1a1.CORS) *ir.CORS {
17771777
}
17781778

17791779
func (t *Translator) buildCSRF(csrf *egv1a1.CSRF) *ir.CSRF {
1780-
var additionalOrigins []*ir.StringMatch
1780+
additionalOrigins := make([]*ir.StringMatch, 0, len(csrf.AdditionalOrigins))
17811781
for _, origin := range csrf.AdditionalOrigins {
17821782
additionalOrigins = append(additionalOrigins, irStringMatch("csrf", origin))
17831783
}
17841784
return &ir.CSRF{
1785-
FilterEnabled: csrf.FilterEnabled,
1786-
ShadowEnabled: csrf.ShadowEnabled,
1785+
EnforcedFraction: csrf.EnforcedFraction,
1786+
ShadowFraction: csrf.ShadowFraction,
17871787
AdditionalOrigins: additionalOrigins,
17881788
}
17891789
}

internal/gatewayapi/testdata/securitypolicy-with-csrf.in.yaml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ securityPolicies:
1010
kind: HTTPRoute
1111
name: httproute-1
1212
csrf:
13-
filterEnabled: 80
14-
shadowEnabled: 20
13+
enforcedFraction:
14+
numerator: 80
15+
shadowFraction:
16+
numerator: 20
1517
additionalOrigins:
1618
- type: Exact
1719
value: www.example.com

internal/gatewayapi/testdata/securitypolicy-with-csrf.out.yaml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,6 @@ securityPolicies:
103103
namespace: default
104104
spec:
105105
csrf:
106-
filterEnabled: 80
107-
shadowEnabled: 20
108106
additionalOrigins:
109107
- type: Exact
110108
value: www.example.com
@@ -114,6 +112,10 @@ securityPolicies:
114112
value: .trusted.com
115113
- type: RegularExpression
116114
value: .*\.partner\.com$
115+
enforcedFraction:
116+
numerator: 80
117+
shadowFraction:
118+
numerator: 20
117119
targetRef:
118120
group: gateway.networking.k8s.io
119121
kind: HTTPRoute
@@ -212,8 +214,6 @@ xdsIR:
212214
prefix: /
213215
security:
214216
csrf:
215-
filterEnabled: 80
216-
shadowEnabled: 20
217217
additionalOrigins:
218218
- distinct: false
219219
exact: www.example.com
@@ -227,6 +227,10 @@ xdsIR:
227227
- distinct: false
228228
name: csrf
229229
safeRegex: .*\.partner\.com$
230+
enforcedFraction:
231+
numerator: 80
232+
shadowFraction:
233+
numerator: 20
230234
readyListener:
231235
address: 0.0.0.0
232236
ipFamily: IPv4

internal/ir/xds.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,11 +1216,11 @@ type CORS struct {
12161216
//
12171217
// +k8s:deepcopy-gen=true
12181218
type CSRF struct {
1219-
// FilterEnabled is the percentage of requests for which CSRF is enforced (0-100).
1219+
// EnforcedFraction is the fraction of requests for which CSRF is enforced.
12201220
// nil means 100%.
1221-
FilterEnabled *int32 `json:"filterEnabled,omitempty" yaml:"filterEnabled,omitempty"`
1222-
// ShadowEnabled is the percentage of requests evaluated in shadow/dry-run mode (0-100).
1223-
ShadowEnabled *int32 `json:"shadowEnabled,omitempty" yaml:"shadowEnabled,omitempty"`
1221+
EnforcedFraction *gwapiv1.Fraction `json:"enforcedFraction,omitempty" yaml:"enforcedFraction,omitempty"`
1222+
// ShadowFraction is the fraction of requests evaluated in shadow/dry-run mode.
1223+
ShadowFraction *gwapiv1.Fraction `json:"shadowFraction,omitempty" yaml:"shadowFraction,omitempty"`
12241224
// AdditionalOrigins specifies additional origins that are allowed.
12251225
AdditionalOrigins []*StringMatch `json:"additionalOrigins,omitempty" yaml:"additionalOrigins,omitempty"`
12261226
}

internal/ir/zz_generated.deepcopy.go

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/xds/translator/csrf.go

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ import (
1313
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
1414
csrfv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/csrf/v3"
1515
hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
16-
xdstype "github.com/envoyproxy/go-control-plane/envoy/type/v3"
1716
"google.golang.org/protobuf/types/known/anypb"
1817

1918
"github.com/envoyproxy/gateway/internal/ir"
2019
"github.com/envoyproxy/gateway/internal/xds/types"
20+
"github.com/envoyproxy/gateway/internal/xds/utils/fractionalpercent"
2121
)
2222

2323
const csrfFilterName = "envoy.filters.http.csrf"
@@ -70,10 +70,7 @@ func (*csrf) patchHCM(
7070
func buildHCMCSRFFilter() (*hcmv3.HttpFilter, error) {
7171
csrfProto := &csrfv3.CsrfPolicy{
7272
FilterEnabled: &corev3.RuntimeFractionalPercent{
73-
DefaultValue: &xdstype.FractionalPercent{
74-
Numerator: 0,
75-
Denominator: xdstype.FractionalPercent_HUNDRED,
76-
},
73+
DefaultValue: fractionalpercent.FromIn32(0),
7774
},
7875
}
7976

@@ -145,27 +142,21 @@ func (*csrf) patchResources(*types.ResourceVersionTable, []*ir.HTTPRoute) error
145142

146143
// buildXdsCSRFPolicy builds the full Envoy CSRF policy from the IR.
147144
func buildXdsCSRFPolicy(csrf *ir.CSRF) *csrfv3.CsrfPolicy {
148-
// Default to 100% enabled.
149-
filterEnabledNumerator := uint32(100)
150-
if csrf.FilterEnabled != nil {
151-
filterEnabledNumerator = uint32(*csrf.FilterEnabled) //nolint:gosec
145+
// Default to 100% enforced.
146+
enforcedFraction := fractionalpercent.FromIn32(100)
147+
if csrf.EnforcedFraction != nil {
148+
enforcedFraction = fractionalpercent.FromFraction(csrf.EnforcedFraction)
152149
}
153150

154151
policy := &csrfv3.CsrfPolicy{
155152
FilterEnabled: &corev3.RuntimeFractionalPercent{
156-
DefaultValue: &xdstype.FractionalPercent{
157-
Numerator: filterEnabledNumerator,
158-
Denominator: xdstype.FractionalPercent_HUNDRED,
159-
},
153+
DefaultValue: enforcedFraction,
160154
},
161155
}
162156

163-
if csrf.ShadowEnabled != nil {
157+
if csrf.ShadowFraction != nil {
164158
policy.ShadowEnabled = &corev3.RuntimeFractionalPercent{
165-
DefaultValue: &xdstype.FractionalPercent{
166-
Numerator: uint32(*csrf.ShadowEnabled), //nolint:gosec
167-
Denominator: xdstype.FractionalPercent_HUNDRED,
168-
},
159+
DefaultValue: fractionalpercent.FromFraction(csrf.ShadowFraction),
169160
}
170161
}
171162

0 commit comments

Comments
 (0)