Skip to content

Commit e11a235

Browse files
sjbermanciarams87
andcommitted
Add WAFPolicy CRD (#3496)
Problem: As a user of NGF with an NGINX One subscription I want a method to configure WAF protection on my Gateways and Routes So that I can enable the NAP WAF feature for the applications that need it. Solution: Define the WafPolicy CRD. Co-authored-by: Ciara Stacke <[email protected]>
1 parent 0a8caf8 commit e11a235

12 files changed

+1318
-11
lines changed

apis/v1alpha1/clientsettingspolicy_types.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
66
)
77

8-
// +genclient
98
// +kubebuilder:object:root=true
109
// +kubebuilder:storageversion
1110
// +kubebuilder:subresource:status

apis/v1alpha1/nginxgateway_types.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package v1alpha1
22

33
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
44

5-
// +genclient
65
// +kubebuilder:object:root=true
76
// +kubebuilder:storageversion
87
// +kubebuilder:subresource:status

apis/v1alpha1/observabilitypolicy_types.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
66
)
77

8-
// +genclient
98
// +kubebuilder:object:root=true
109
// +kubebuilder:deprecatedversion:warning="The 'v1alpha1' version of ObservabilityPolicy API is deprecated, please migrate to 'v1alpha2'."
1110
// +kubebuilder:subresource:status

apis/v1alpha1/policy_methods.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,15 @@ func (p *UpstreamSettingsPolicy) GetPolicyStatus() v1alpha2.PolicyStatus {
4343
func (p *UpstreamSettingsPolicy) SetPolicyStatus(status v1alpha2.PolicyStatus) {
4444
p.Status = status
4545
}
46+
47+
func (p *WAFPolicy) GetTargetRefs() []v1alpha2.LocalPolicyTargetReference {
48+
return []v1alpha2.LocalPolicyTargetReference{p.Spec.TargetRef}
49+
}
50+
51+
func (p *WAFPolicy) GetPolicyStatus() v1alpha2.PolicyStatus {
52+
return p.Status
53+
}
54+
55+
func (p *WAFPolicy) SetPolicyStatus(status v1alpha2.PolicyStatus) {
56+
p.Status = status
57+
}

apis/v1alpha1/register.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
4242
&SnippetsFilterList{},
4343
&UpstreamSettingsPolicy{},
4444
&UpstreamSettingsPolicyList{},
45+
&WAFPolicy{},
46+
&WAFPolicyList{},
4547
)
4648
// AddToGroupVersion allows the serialization of client types like ListOptions.
4749
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)

apis/v1alpha1/snippetsfilter_types.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
v1 "sigs.k8s.io/gateway-api/apis/v1"
66
)
77

8-
// +genclient
98
// +kubebuilder:object:root=true
109
// +kubebuilder:storageversion
1110
// +kubebuilder:subresource:status

apis/v1alpha1/upstreamsettingspolicy_types.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
66
)
77

8-
// +genclient
98
// +kubebuilder:object:root=true
109
// +kubebuilder:storageversion
1110
// +kubebuilder:subresource:status

apis/v1alpha1/wafpolicy_types.go

Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
package v1alpha1
2+
3+
import (
4+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
5+
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
6+
)
7+
8+
// +kubebuilder:object:root=true
9+
// +kubebuilder:storageversion
10+
// +kubebuilder:subresource:status
11+
// +kubebuilder:resource:categories=nginx-gateway-fabric
12+
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
13+
// +kubebuilder:metadata:labels="gateway.networking.k8s.io/policy=inherited"
14+
15+
// WAFPolicy is an Inherited Attached Policy. It provides a way to configure NGINX App Protect Web Application Firewall
16+
// for Gateways and Routes.
17+
type WAFPolicy struct {
18+
metav1.TypeMeta `json:",inline"`
19+
metav1.ObjectMeta `json:"metadata,omitempty"`
20+
21+
// Spec defines the desired state of the WAFPolicy.
22+
Spec WAFPolicySpec `json:"spec"`
23+
24+
// Status defines the state of the WAFPolicy.
25+
Status gatewayv1alpha2.PolicyStatus `json:"status,omitempty"`
26+
}
27+
28+
// +kubebuilder:object:root=true
29+
30+
// WAFPolicyList contains a list of WAFPolicies.
31+
type WAFPolicyList struct {
32+
metav1.TypeMeta `json:",inline"`
33+
metav1.ListMeta `json:"metadata,omitempty"`
34+
Items []WAFPolicy `json:"items"`
35+
}
36+
37+
// WAFPolicySpec defines the desired state of a WAFPolicy.
38+
type WAFPolicySpec struct {
39+
// PolicySource defines the source location and configuration for the compiled WAF policy bundle.
40+
//
41+
// +optional
42+
PolicySource *WAFPolicySource `json:"policySource,omitempty"`
43+
44+
// TargetRef identifies an API object to apply the policy to.
45+
// Object must be in the same namespace as the policy.
46+
// Support: Gateway, HTTPRoute, GRPCRoute.
47+
//
48+
// +kubebuilder:validation:XValidation:message="TargetRef Kind must be one of: Gateway, HTTPRoute, or GRPCRoute",rule="(self.kind=='Gateway' || self.kind=='HTTPRoute' || self.kind=='GRPCRoute')"
49+
// +kubebuilder:validation:XValidation:message="TargetRef Group must be gateway.networking.k8s.io.",rule="(self.group=='gateway.networking.k8s.io')"
50+
//nolint:lll
51+
TargetRef gatewayv1alpha2.LocalPolicyTargetReference `json:"targetRef"`
52+
53+
// SecurityLogs defines the security logging configuration for app_protect_security_log directives.
54+
// Multiple logging configurations can be specified to send logs to different destinations.
55+
//
56+
// +optional
57+
// +kubebuilder:validation:MaxItems=32
58+
SecurityLogs []WAFSecurityLog `json:"securityLogs,omitempty"`
59+
}
60+
61+
// WAFPolicySource defines the source location and configuration for fetching WAF policy bundles.
62+
type WAFPolicySource struct {
63+
// AuthSecret is the Secret containing authentication credentials for the WAF policy source.
64+
//
65+
// +optional
66+
AuthSecret *WAFPolicyAuthSecret `json:"authSecret,omitempty"`
67+
68+
// Validation defines the validation methods for policy integrity verification.
69+
//
70+
// +optional
71+
Validation *WAFPolicyValidation `json:"validation,omitempty"`
72+
73+
// Polling defines the polling configuration for automatic WAF policy change detection.
74+
//
75+
// +optional
76+
Polling *WAFPolicyPolling `json:"polling,omitempty"`
77+
78+
// Retry defines the retry configuration for WAF policy fetch failures.
79+
//
80+
// +optional
81+
Retry *WAFPolicyRetry `json:"retry,omitempty"`
82+
83+
// Timeout for policy downloads.
84+
//
85+
// +optional
86+
Timeout *Duration `json:"timeout,omitempty"`
87+
88+
// FileLocation defines the location of the WAF policy file.
89+
//
90+
// +kubebuilder:validation:MinLength=1
91+
// +kubebuilder:validation:MaxLength=256
92+
FileLocation string `json:"fileLocation"`
93+
}
94+
95+
// WAFPolicyAuthSecret is the Secret containing authentication credentials for the WAF policy source.
96+
// It must live in the same Namespace as the policy.
97+
type WAFPolicyAuthSecret struct {
98+
// Name is the name of the Secret containing authentication credentials for the WAF policy source.
99+
//
100+
// +kubebuilder:validation:MinLength=1
101+
// +kubebuilder:validation:MaxLength=253
102+
// +kubebuilder:validation:Pattern=`^[a-zA-Z0-9_-]+$`
103+
Name string `json:"name"`
104+
}
105+
106+
// WAFPolicyValidation defines the validation methods for policy integrity verification.
107+
type WAFPolicyValidation struct {
108+
// Methods specifies the validation methods to use for policy integrity verification.
109+
// Currently supported: ["checksum"]
110+
//
111+
// +optional
112+
// +listType=set
113+
Methods []WAFPolicyValidationMethod `json:"methods,omitempty"`
114+
}
115+
116+
// WAFPolicyValidationMethod defines the supported validation methods.
117+
//
118+
// +kubebuilder:validation:Enum=checksum
119+
type WAFPolicyValidationMethod string
120+
121+
const (
122+
// WAFPolicyValidationChecksum validates policy integrity using checksum verification.
123+
WAFPolicyValidationChecksum WAFPolicyValidationMethod = "checksum"
124+
)
125+
126+
// WAFPolicyPolling defines the polling configuration for automatic WAF policy change detection.
127+
type WAFPolicyPolling struct {
128+
// Enabled indicates whether polling is enabled for automatic WAF policy change detection.
129+
// When enabled, NGINX Gateway Fabric will periodically check for policy changes using checksum validation.
130+
//
131+
// +optional
132+
// +kubebuilder:default=false
133+
Enabled *bool `json:"enabled,omitempty"`
134+
135+
// Interval is the polling interval to check for WAF policy changes.
136+
// Must be a valid duration string (e.g., "5m", "30s", "1h").
137+
// Defaults to "5m" if polling is enabled.
138+
//
139+
// +optional
140+
// +kubebuilder:default="5m"
141+
Interval *Duration `json:"interval,omitempty"`
142+
143+
// ChecksumLocation specifies the location of the checksum file for the policy bundle.
144+
// If not specified, defaults to <fileLocation>.sha256
145+
//
146+
// +optional
147+
// +kubebuilder:validation:MaxLength=2048
148+
ChecksumLocation *string `json:"checksumLocation,omitempty"`
149+
}
150+
151+
// WAFPolicyRetry defines the retry configuration for WAF policy fetch failures.
152+
type WAFPolicyRetry struct {
153+
// Attempts is the number of retry attempts for fetching the WAF policy.
154+
// Set to 0 to disable retries.
155+
//
156+
// +optional
157+
// +kubebuilder:validation:Minimum=0
158+
// +kubebuilder:default=3
159+
Attempts *int32 `json:"attempts,omitempty"`
160+
161+
// Backoff defines the backoff strategy for retry attempts.
162+
// Supported values: "exponential", "linear"
163+
//
164+
// +optional
165+
// +kubebuilder:default="exponential"
166+
Backoff *WAFPolicyRetryBackoff `json:"backoff,omitempty"`
167+
168+
// MaxDelay is the maximum delay between retry attempts.
169+
// Must be a valid duration string (e.g., "5m", "30s").
170+
//
171+
// +optional
172+
// +kubebuilder:default="5m"
173+
MaxDelay *Duration `json:"maxDelay,omitempty"`
174+
}
175+
176+
// WAFPolicyRetryBackoff defines the supported backoff strategies.
177+
//
178+
// +kubebuilder:validation:Enum=exponential;linear
179+
type WAFPolicyRetryBackoff string
180+
181+
const (
182+
// WAFPolicyRetryBackoffExponential uses exponential backoff for retry delays.
183+
WAFPolicyRetryBackoffExponential WAFPolicyRetryBackoff = "exponential"
184+
// WAFPolicyRetryBackoffLinear uses linear backoff for retry delays.
185+
WAFPolicyRetryBackoffLinear WAFPolicyRetryBackoff = "linear"
186+
)
187+
188+
// WAFSecurityLog defines the security logging configuration for app_protect_security_log directives.
189+
// LogProfile and LogProfileBundle are mutually exclusive per security log entry.
190+
//
191+
// +kubebuilder:validation:XValidation:message="only one of logProfile or logProfileBundle may be set",rule="!(has(self.logProfile) && has(self.logProfileBundle))"
192+
// +kubebuilder:validation:XValidation:message="at least one of logProfile or logProfileBundle must be set",rule="has(self.logProfile) || has(self.logProfileBundle)"
193+
//
194+
//nolint:lll
195+
type WAFSecurityLog struct {
196+
// Name is the name of the security log configuration.
197+
//
198+
// +optional
199+
// +kubebuilder:validation:MinLength=1
200+
// +kubebuilder:validation:MaxLength=63
201+
// +kubebuilder:validation:Pattern=`^[a-zA-Z0-9]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$`
202+
Name *string `json:"name,omitempty"`
203+
204+
// LogProfile defines the built-in logging profile to use.
205+
//
206+
// +optional
207+
LogProfile *LogProfile `json:"logProfile,omitempty"`
208+
209+
// LogProfileBundle defines a custom logging profile bundle, similar to policy bundle.
210+
//
211+
// +optional
212+
LogProfileBundle *WAFPolicySource `json:"logProfileBundle,omitempty"`
213+
214+
// Destination defines where the security logs should be sent.
215+
Destination SecurityLogDestination `json:"destination"`
216+
}
217+
218+
// SecurityLogDestination defines the destination for security logs.
219+
//
220+
// +kubebuilder:validation:XValidation:message="destination.file must be nil if the destination.type is not file",rule="!(has(self.file) && self.type != 'file')"
221+
// +kubebuilder:validation:XValidation:message="destination.file must be specified for file destination.type",rule="!(!has(self.file) && self.type == 'file')"
222+
// +kubebuilder:validation:XValidation:message="destination.syslog must be nil if the destination.type is not syslog",rule="!(has(self.syslog) && self.type != 'syslog')"
223+
// +kubebuilder:validation:XValidation:message="destination.syslog must be specified for syslog destination.type",rule="!(!has(self.syslog) && self.type == 'syslog')"
224+
//
225+
//nolint:lll
226+
type SecurityLogDestination struct {
227+
// File defines the file destination configuration.
228+
// Only valid when type is "file".
229+
//
230+
// +optional
231+
File *SecurityLogFile `json:"file,omitempty"`
232+
233+
// Syslog defines the syslog destination configuration.
234+
// Only valid when type is "syslog".
235+
//
236+
// +optional
237+
Syslog *SecurityLogSyslog `json:"syslog,omitempty"`
238+
239+
// Type identifies the type of security log destination.
240+
//
241+
// +unionDiscriminator
242+
// +kubebuilder:default=stderr
243+
Type SecurityLogDestinationType `json:"type"`
244+
}
245+
246+
// SecurityLogDestinationType defines the supported security log destination types.
247+
//
248+
// +kubebuilder:validation:Enum=stderr;file;syslog
249+
type SecurityLogDestinationType string
250+
251+
const (
252+
// SecurityLogDestinationTypeStderr outputs logs to container stderr.
253+
SecurityLogDestinationTypeStderr SecurityLogDestinationType = "stderr"
254+
// SecurityLogDestinationTypeFile writes logs to a specified file path.
255+
SecurityLogDestinationTypeFile SecurityLogDestinationType = "file"
256+
// SecurityLogDestinationTypeSyslog sends logs to a syslog server via TCP.
257+
SecurityLogDestinationTypeSyslog SecurityLogDestinationType = "syslog"
258+
)
259+
260+
// SecurityLogFile defines the file destination configuration for security logs.
261+
type SecurityLogFile struct {
262+
// Path is the file path where security logs will be written.
263+
// Must be accessible to the waf-enforcer container.
264+
//
265+
// +kubebuilder:validation:MinLength=1
266+
// +kubebuilder:validation:MaxLength=256
267+
// +kubebuilder:validation:Pattern=`^/.*$`
268+
Path string `json:"path"`
269+
}
270+
271+
// SecurityLogSyslog defines the syslog destination configuration for security logs.
272+
type SecurityLogSyslog struct {
273+
// Server is the syslog server address in the format "host:port".
274+
//
275+
// +kubebuilder:validation:MinLength=1
276+
// +kubebuilder:validation:MaxLength=253
277+
// +kubebuilder:validation:Pattern=`^[a-zA-Z0-9.-]+:[0-9]+$`
278+
Server string `json:"server"`
279+
}
280+
281+
// LogProfile defines the built-in logging profiles available in NGINX App Protect.
282+
//
283+
// +kubebuilder:validation:Enum=log_default;log_all;log_illegal;log_blocked;log_grpc_all;log_grpc_blocked;log_grpc_illegal
284+
//
285+
//nolint:lll
286+
type LogProfile string
287+
288+
const (
289+
// LogProfileDefault is the default logging profile.
290+
LogProfileDefault LogProfile = "log_default"
291+
// LogProfileAll logs all requests (blocked and passed).
292+
LogProfileAll LogProfile = "log_all"
293+
// LogProfileIllegal logs illegal requests.
294+
LogProfileIllegal LogProfile = "log_illegal"
295+
// LogProfileBlocked logs only blocked requests.
296+
LogProfileBlocked LogProfile = "log_blocked"
297+
// LogProfileGRPCAll logs all gRPC requests.
298+
LogProfileGRPCAll LogProfile = "log_grpc_all"
299+
// LogProfileGRPCBlocked logs blocked gRPC requests.
300+
LogProfileGRPCBlocked LogProfile = "log_grpc_blocked"
301+
// LogProfileGRPCIllegal logs illegal gRPC requests.
302+
LogProfileGRPCIllegal LogProfile = "log_grpc_illegal"
303+
)

0 commit comments

Comments
 (0)