diff --git a/apis/v1alpha1/clientsettingspolicy_types.go b/apis/v1alpha1/clientsettingspolicy_types.go index 6948f3339a..a03df366e6 100644 --- a/apis/v1alpha1/clientsettingspolicy_types.go +++ b/apis/v1alpha1/clientsettingspolicy_types.go @@ -5,7 +5,6 @@ import ( gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) -// +genclient // +kubebuilder:object:root=true // +kubebuilder:storageversion // +kubebuilder:subresource:status diff --git a/apis/v1alpha1/nginxgateway_types.go b/apis/v1alpha1/nginxgateway_types.go index f6c16f6a42..ad1e57b4c0 100644 --- a/apis/v1alpha1/nginxgateway_types.go +++ b/apis/v1alpha1/nginxgateway_types.go @@ -2,7 +2,6 @@ package v1alpha1 import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -// +genclient // +kubebuilder:object:root=true // +kubebuilder:storageversion // +kubebuilder:subresource:status diff --git a/apis/v1alpha1/observabilitypolicy_types.go b/apis/v1alpha1/observabilitypolicy_types.go index 2366a1fff5..d43403f10f 100644 --- a/apis/v1alpha1/observabilitypolicy_types.go +++ b/apis/v1alpha1/observabilitypolicy_types.go @@ -5,7 +5,6 @@ import ( gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) -// +genclient // +kubebuilder:object:root=true // +kubebuilder:deprecatedversion:warning="The 'v1alpha1' version of ObservabilityPolicy API is deprecated, please migrate to 'v1alpha2'." // +kubebuilder:subresource:status diff --git a/apis/v1alpha1/policy_methods.go b/apis/v1alpha1/policy_methods.go index 71cc93ede8..3b291cfa07 100644 --- a/apis/v1alpha1/policy_methods.go +++ b/apis/v1alpha1/policy_methods.go @@ -43,3 +43,15 @@ func (p *UpstreamSettingsPolicy) GetPolicyStatus() v1alpha2.PolicyStatus { func (p *UpstreamSettingsPolicy) SetPolicyStatus(status v1alpha2.PolicyStatus) { p.Status = status } + +func (p *WAFPolicy) GetTargetRefs() []v1alpha2.LocalPolicyTargetReference { + return []v1alpha2.LocalPolicyTargetReference{p.Spec.TargetRef} +} + +func (p *WAFPolicy) GetPolicyStatus() v1alpha2.PolicyStatus { + return p.Status +} + +func (p *WAFPolicy) SetPolicyStatus(status v1alpha2.PolicyStatus) { + p.Status = status +} diff --git a/apis/v1alpha1/register.go b/apis/v1alpha1/register.go index 7deb5bfb5c..6967ab3271 100644 --- a/apis/v1alpha1/register.go +++ b/apis/v1alpha1/register.go @@ -42,6 +42,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &SnippetsFilterList{}, &UpstreamSettingsPolicy{}, &UpstreamSettingsPolicyList{}, + &WAFPolicy{}, + &WAFPolicyList{}, ) // AddToGroupVersion allows the serialization of client types like ListOptions. metav1.AddToGroupVersion(scheme, SchemeGroupVersion) diff --git a/apis/v1alpha1/snippetsfilter_types.go b/apis/v1alpha1/snippetsfilter_types.go index decb6190a4..e58b15ea21 100644 --- a/apis/v1alpha1/snippetsfilter_types.go +++ b/apis/v1alpha1/snippetsfilter_types.go @@ -5,7 +5,6 @@ import ( v1 "sigs.k8s.io/gateway-api/apis/v1" ) -// +genclient // +kubebuilder:object:root=true // +kubebuilder:storageversion // +kubebuilder:subresource:status diff --git a/apis/v1alpha1/upstreamsettingspolicy_types.go b/apis/v1alpha1/upstreamsettingspolicy_types.go index 158776491e..eaac7b45fd 100644 --- a/apis/v1alpha1/upstreamsettingspolicy_types.go +++ b/apis/v1alpha1/upstreamsettingspolicy_types.go @@ -5,7 +5,6 @@ import ( gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" ) -// +genclient // +kubebuilder:object:root=true // +kubebuilder:storageversion // +kubebuilder:subresource:status diff --git a/apis/v1alpha1/wafpolicy_types.go b/apis/v1alpha1/wafpolicy_types.go new file mode 100644 index 0000000000..8dbe2153ab --- /dev/null +++ b/apis/v1alpha1/wafpolicy_types.go @@ -0,0 +1,303 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" +) + +// +kubebuilder:object:root=true +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:resource:categories=nginx-gateway-fabric +// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp` +// +kubebuilder:metadata:labels="gateway.networking.k8s.io/policy=inherited" + +// WAFPolicy is an Inherited Attached Policy. It provides a way to configure NGINX App Protect Web Application Firewall +// for Gateways and Routes. +type WAFPolicy struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired state of the WAFPolicy. + Spec WAFPolicySpec `json:"spec"` + + // Status defines the state of the WAFPolicy. + Status gatewayv1alpha2.PolicyStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// WAFPolicyList contains a list of WAFPolicies. +type WAFPolicyList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []WAFPolicy `json:"items"` +} + +// WAFPolicySpec defines the desired state of a WAFPolicy. +type WAFPolicySpec struct { + // PolicySource defines the source location and configuration for the compiled WAF policy bundle. + // + // +optional + PolicySource *WAFPolicySource `json:"policySource,omitempty"` + + // TargetRef identifies an API object to apply the policy to. + // Object must be in the same namespace as the policy. + // Support: Gateway, HTTPRoute, GRPCRoute. + // + // +kubebuilder:validation:XValidation:message="TargetRef Kind must be one of: Gateway, HTTPRoute, or GRPCRoute",rule="(self.kind=='Gateway' || self.kind=='HTTPRoute' || self.kind=='GRPCRoute')" + // +kubebuilder:validation:XValidation:message="TargetRef Group must be gateway.networking.k8s.io.",rule="(self.group=='gateway.networking.k8s.io')" + //nolint:lll + TargetRef gatewayv1alpha2.LocalPolicyTargetReference `json:"targetRef"` + + // SecurityLogs defines the security logging configuration for app_protect_security_log directives. + // Multiple logging configurations can be specified to send logs to different destinations. + // + // +optional + // +kubebuilder:validation:MaxItems=32 + SecurityLogs []WAFSecurityLog `json:"securityLogs,omitempty"` +} + +// WAFPolicySource defines the source location and configuration for fetching WAF policy bundles. +type WAFPolicySource struct { + // AuthSecret is the Secret containing authentication credentials for the WAF policy source. + // + // +optional + AuthSecret *WAFPolicyAuthSecret `json:"authSecret,omitempty"` + + // Validation defines the validation methods for policy integrity verification. + // + // +optional + Validation *WAFPolicyValidation `json:"validation,omitempty"` + + // Polling defines the polling configuration for automatic WAF policy change detection. + // + // +optional + Polling *WAFPolicyPolling `json:"polling,omitempty"` + + // Retry defines the retry configuration for WAF policy fetch failures. + // + // +optional + Retry *WAFPolicyRetry `json:"retry,omitempty"` + + // Timeout for policy downloads. + // + // +optional + Timeout *Duration `json:"timeout,omitempty"` + + // FileLocation defines the location of the WAF policy file. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + FileLocation string `json:"fileLocation"` +} + +// WAFPolicyAuthSecret is the Secret containing authentication credentials for the WAF policy source. +// It must live in the same Namespace as the policy. +type WAFPolicyAuthSecret struct { + // Name is the name of the Secret containing authentication credentials for the WAF policy source. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:validation:Pattern=`^[a-zA-Z0-9_-]+$` + Name string `json:"name"` +} + +// WAFPolicyValidation defines the validation methods for policy integrity verification. +type WAFPolicyValidation struct { + // Methods specifies the validation methods to use for policy integrity verification. + // Currently supported: ["checksum"] + // + // +optional + // +listType=set + Methods []WAFPolicyValidationMethod `json:"methods,omitempty"` +} + +// WAFPolicyValidationMethod defines the supported validation methods. +// +// +kubebuilder:validation:Enum=checksum +type WAFPolicyValidationMethod string + +const ( + // WAFPolicyValidationChecksum validates policy integrity using checksum verification. + WAFPolicyValidationChecksum WAFPolicyValidationMethod = "checksum" +) + +// WAFPolicyPolling defines the polling configuration for automatic WAF policy change detection. +type WAFPolicyPolling struct { + // Enabled indicates whether polling is enabled for automatic WAF policy change detection. + // When enabled, NGINX Gateway Fabric will periodically check for policy changes using checksum validation. + // + // +optional + // +kubebuilder:default=false + Enabled *bool `json:"enabled,omitempty"` + + // Interval is the polling interval to check for WAF policy changes. + // Must be a valid duration string (e.g., "5m", "30s", "1h"). + // Defaults to "5m" if polling is enabled. + // + // +optional + // +kubebuilder:default="5m" + Interval *Duration `json:"interval,omitempty"` + + // ChecksumLocation specifies the location of the checksum file for the policy bundle. + // If not specified, defaults to .sha256 + // + // +optional + // +kubebuilder:validation:MaxLength=2048 + ChecksumLocation *string `json:"checksumLocation,omitempty"` +} + +// WAFPolicyRetry defines the retry configuration for WAF policy fetch failures. +type WAFPolicyRetry struct { + // Attempts is the number of retry attempts for fetching the WAF policy. + // Set to 0 to disable retries. + // + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:default=3 + Attempts *int32 `json:"attempts,omitempty"` + + // Backoff defines the backoff strategy for retry attempts. + // Supported values: "exponential", "linear" + // + // +optional + // +kubebuilder:default="exponential" + Backoff *WAFPolicyRetryBackoff `json:"backoff,omitempty"` + + // MaxDelay is the maximum delay between retry attempts. + // Must be a valid duration string (e.g., "5m", "30s"). + // + // +optional + // +kubebuilder:default="5m" + MaxDelay *Duration `json:"maxDelay,omitempty"` +} + +// WAFPolicyRetryBackoff defines the supported backoff strategies. +// +// +kubebuilder:validation:Enum=exponential;linear +type WAFPolicyRetryBackoff string + +const ( + // WAFPolicyRetryBackoffExponential uses exponential backoff for retry delays. + WAFPolicyRetryBackoffExponential WAFPolicyRetryBackoff = "exponential" + // WAFPolicyRetryBackoffLinear uses linear backoff for retry delays. + WAFPolicyRetryBackoffLinear WAFPolicyRetryBackoff = "linear" +) + +// WAFSecurityLog defines the security logging configuration for app_protect_security_log directives. +// LogProfile and LogProfileBundle are mutually exclusive per security log entry. +// +// +kubebuilder:validation:XValidation:message="only one of logProfile or logProfileBundle may be set",rule="!(has(self.logProfile) && has(self.logProfileBundle))" +// +kubebuilder:validation:XValidation:message="at least one of logProfile or logProfileBundle must be set",rule="has(self.logProfile) || has(self.logProfileBundle)" +// +//nolint:lll +type WAFSecurityLog struct { + // Name is the name of the security log configuration. + // + // +optional + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=63 + // +kubebuilder:validation:Pattern=`^[a-zA-Z0-9]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$` + Name *string `json:"name,omitempty"` + + // LogProfile defines the built-in logging profile to use. + // + // +optional + LogProfile *LogProfile `json:"logProfile,omitempty"` + + // LogProfileBundle defines a custom logging profile bundle, similar to policy bundle. + // + // +optional + LogProfileBundle *WAFPolicySource `json:"logProfileBundle,omitempty"` + + // Destination defines where the security logs should be sent. + Destination SecurityLogDestination `json:"destination"` +} + +// SecurityLogDestination defines the destination for security logs. +// +// +kubebuilder:validation:XValidation:message="destination.file must be nil if the destination.type is not file",rule="!(has(self.file) && self.type != 'file')" +// +kubebuilder:validation:XValidation:message="destination.file must be specified for file destination.type",rule="!(!has(self.file) && self.type == 'file')" +// +kubebuilder:validation:XValidation:message="destination.syslog must be nil if the destination.type is not syslog",rule="!(has(self.syslog) && self.type != 'syslog')" +// +kubebuilder:validation:XValidation:message="destination.syslog must be specified for syslog destination.type",rule="!(!has(self.syslog) && self.type == 'syslog')" +// +//nolint:lll +type SecurityLogDestination struct { + // File defines the file destination configuration. + // Only valid when type is "file". + // + // +optional + File *SecurityLogFile `json:"file,omitempty"` + + // Syslog defines the syslog destination configuration. + // Only valid when type is "syslog". + // + // +optional + Syslog *SecurityLogSyslog `json:"syslog,omitempty"` + + // Type identifies the type of security log destination. + // + // +unionDiscriminator + // +kubebuilder:default=stderr + Type SecurityLogDestinationType `json:"type"` +} + +// SecurityLogDestinationType defines the supported security log destination types. +// +// +kubebuilder:validation:Enum=stderr;file;syslog +type SecurityLogDestinationType string + +const ( + // SecurityLogDestinationTypeStderr outputs logs to container stderr. + SecurityLogDestinationTypeStderr SecurityLogDestinationType = "stderr" + // SecurityLogDestinationTypeFile writes logs to a specified file path. + SecurityLogDestinationTypeFile SecurityLogDestinationType = "file" + // SecurityLogDestinationTypeSyslog sends logs to a syslog server via TCP. + SecurityLogDestinationTypeSyslog SecurityLogDestinationType = "syslog" +) + +// SecurityLogFile defines the file destination configuration for security logs. +type SecurityLogFile struct { + // Path is the file path where security logs will be written. + // Must be accessible to the waf-enforcer container. + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=256 + // +kubebuilder:validation:Pattern=`^/.*$` + Path string `json:"path"` +} + +// SecurityLogSyslog defines the syslog destination configuration for security logs. +type SecurityLogSyslog struct { + // Server is the syslog server address in the format "host:port". + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + // +kubebuilder:validation:Pattern=`^[a-zA-Z0-9.-]+:[0-9]+$` + Server string `json:"server"` +} + +// LogProfile defines the built-in logging profiles available in NGINX App Protect. +// +// +kubebuilder:validation:Enum=log_default;log_all;log_illegal;log_blocked;log_grpc_all;log_grpc_blocked;log_grpc_illegal +// +//nolint:lll +type LogProfile string + +const ( + // LogProfileDefault is the default logging profile. + LogProfileDefault LogProfile = "log_default" + // LogProfileAll logs all requests (blocked and passed). + LogProfileAll LogProfile = "log_all" + // LogProfileIllegal logs illegal requests. + LogProfileIllegal LogProfile = "log_illegal" + // LogProfileBlocked logs only blocked requests. + LogProfileBlocked LogProfile = "log_blocked" + // LogProfileGRPCAll logs all gRPC requests. + LogProfileGRPCAll LogProfile = "log_grpc_all" + // LogProfileGRPCBlocked logs blocked gRPC requests. + LogProfileGRPCBlocked LogProfile = "log_grpc_blocked" + // LogProfileGRPCIllegal logs illegal gRPC requests. + LogProfileGRPCIllegal LogProfile = "log_grpc_illegal" +) diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 65b3b76c30..c4a8b430ac 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -404,6 +404,61 @@ func (in *ObservabilityPolicySpec) DeepCopy() *ObservabilityPolicySpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecurityLogDestination) DeepCopyInto(out *SecurityLogDestination) { + *out = *in + if in.File != nil { + in, out := &in.File, &out.File + *out = new(SecurityLogFile) + **out = **in + } + if in.Syslog != nil { + in, out := &in.Syslog, &out.Syslog + *out = new(SecurityLogSyslog) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityLogDestination. +func (in *SecurityLogDestination) DeepCopy() *SecurityLogDestination { + if in == nil { + return nil + } + out := new(SecurityLogDestination) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecurityLogFile) DeepCopyInto(out *SecurityLogFile) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityLogFile. +func (in *SecurityLogFile) DeepCopy() *SecurityLogFile { + if in == nil { + return nil + } + out := new(SecurityLogFile) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecurityLogSyslog) DeepCopyInto(out *SecurityLogSyslog) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecurityLogSyslog. +func (in *SecurityLogSyslog) DeepCopy() *SecurityLogSyslog { + if in == nil { + return nil + } + out := new(SecurityLogSyslog) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Snippet) DeepCopyInto(out *Snippet) { *out = *in @@ -695,3 +750,256 @@ func (in *UpstreamSettingsPolicySpec) DeepCopy() *UpstreamSettingsPolicySpec { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WAFPolicy) DeepCopyInto(out *WAFPolicy) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WAFPolicy. +func (in *WAFPolicy) DeepCopy() *WAFPolicy { + if in == nil { + return nil + } + out := new(WAFPolicy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WAFPolicy) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WAFPolicyAuthSecret) DeepCopyInto(out *WAFPolicyAuthSecret) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WAFPolicyAuthSecret. +func (in *WAFPolicyAuthSecret) DeepCopy() *WAFPolicyAuthSecret { + if in == nil { + return nil + } + out := new(WAFPolicyAuthSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WAFPolicyList) DeepCopyInto(out *WAFPolicyList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]WAFPolicy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WAFPolicyList. +func (in *WAFPolicyList) DeepCopy() *WAFPolicyList { + if in == nil { + return nil + } + out := new(WAFPolicyList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *WAFPolicyList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WAFPolicyPolling) DeepCopyInto(out *WAFPolicyPolling) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.Interval != nil { + in, out := &in.Interval, &out.Interval + *out = new(Duration) + **out = **in + } + if in.ChecksumLocation != nil { + in, out := &in.ChecksumLocation, &out.ChecksumLocation + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WAFPolicyPolling. +func (in *WAFPolicyPolling) DeepCopy() *WAFPolicyPolling { + if in == nil { + return nil + } + out := new(WAFPolicyPolling) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WAFPolicyRetry) DeepCopyInto(out *WAFPolicyRetry) { + *out = *in + if in.Attempts != nil { + in, out := &in.Attempts, &out.Attempts + *out = new(int32) + **out = **in + } + if in.Backoff != nil { + in, out := &in.Backoff, &out.Backoff + *out = new(WAFPolicyRetryBackoff) + **out = **in + } + if in.MaxDelay != nil { + in, out := &in.MaxDelay, &out.MaxDelay + *out = new(Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WAFPolicyRetry. +func (in *WAFPolicyRetry) DeepCopy() *WAFPolicyRetry { + if in == nil { + return nil + } + out := new(WAFPolicyRetry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WAFPolicySource) DeepCopyInto(out *WAFPolicySource) { + *out = *in + if in.AuthSecret != nil { + in, out := &in.AuthSecret, &out.AuthSecret + *out = new(WAFPolicyAuthSecret) + **out = **in + } + if in.Validation != nil { + in, out := &in.Validation, &out.Validation + *out = new(WAFPolicyValidation) + (*in).DeepCopyInto(*out) + } + if in.Polling != nil { + in, out := &in.Polling, &out.Polling + *out = new(WAFPolicyPolling) + (*in).DeepCopyInto(*out) + } + if in.Retry != nil { + in, out := &in.Retry, &out.Retry + *out = new(WAFPolicyRetry) + (*in).DeepCopyInto(*out) + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + *out = new(Duration) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WAFPolicySource. +func (in *WAFPolicySource) DeepCopy() *WAFPolicySource { + if in == nil { + return nil + } + out := new(WAFPolicySource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WAFPolicySpec) DeepCopyInto(out *WAFPolicySpec) { + *out = *in + if in.PolicySource != nil { + in, out := &in.PolicySource, &out.PolicySource + *out = new(WAFPolicySource) + (*in).DeepCopyInto(*out) + } + in.TargetRef.DeepCopyInto(&out.TargetRef) + if in.SecurityLogs != nil { + in, out := &in.SecurityLogs, &out.SecurityLogs + *out = make([]WAFSecurityLog, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WAFPolicySpec. +func (in *WAFPolicySpec) DeepCopy() *WAFPolicySpec { + if in == nil { + return nil + } + out := new(WAFPolicySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WAFPolicyValidation) DeepCopyInto(out *WAFPolicyValidation) { + *out = *in + if in.Methods != nil { + in, out := &in.Methods, &out.Methods + *out = make([]WAFPolicyValidationMethod, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WAFPolicyValidation. +func (in *WAFPolicyValidation) DeepCopy() *WAFPolicyValidation { + if in == nil { + return nil + } + out := new(WAFPolicyValidation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WAFSecurityLog) DeepCopyInto(out *WAFSecurityLog) { + *out = *in + if in.Name != nil { + in, out := &in.Name, &out.Name + *out = new(string) + **out = **in + } + if in.LogProfile != nil { + in, out := &in.LogProfile, &out.LogProfile + *out = new(LogProfile) + **out = **in + } + if in.LogProfileBundle != nil { + in, out := &in.LogProfileBundle, &out.LogProfileBundle + *out = new(WAFPolicySource) + (*in).DeepCopyInto(*out) + } + in.Destination.DeepCopyInto(&out.Destination) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WAFSecurityLog. +func (in *WAFSecurityLog) DeepCopy() *WAFSecurityLog { + if in == nil { + return nil + } + out := new(WAFSecurityLog) + in.DeepCopyInto(out) + return out +} diff --git a/apis/v1alpha2/nginxproxy_types.go b/apis/v1alpha2/nginxproxy_types.go index bf77daf511..ee6b64d03a 100644 --- a/apis/v1alpha2/nginxproxy_types.go +++ b/apis/v1alpha2/nginxproxy_types.go @@ -7,7 +7,6 @@ import ( "github.com/nginx/nginx-gateway-fabric/apis/v1alpha1" ) -// +genclient // +kubebuilder:object:root=true // +kubebuilder:storageversion // +kubebuilder:resource:categories=nginx-gateway-fabric,scope=Namespaced @@ -42,7 +41,7 @@ type NginxProxySpec struct { // Default is "dual", meaning the server will use both IPv4 and IPv6. // // +optional - // +kubebuilder:default:=dual + // +kubebuilder:default=dual IPFamily *IPFamilyType `json:"ipFamily,omitempty"` // Telemetry specifies the OpenTelemetry configuration. // @@ -568,7 +567,7 @@ type Image struct { // PullPolicy describes a policy for if/when to pull a container image. // // +optional - // +kubebuilder:default:=IfNotPresent + // +kubebuilder:default=IfNotPresent PullPolicy *PullPolicy `json:"pullPolicy,omitempty"` } @@ -592,7 +591,7 @@ type ServiceSpec struct { // ServiceType describes ingress method for the Service. // // +optional - // +kubebuilder:default:=LoadBalancer + // +kubebuilder:default=LoadBalancer ServiceType *ServiceType `json:"type,omitempty"` // ExternalTrafficPolicy describes how nodes distribute service traffic they @@ -600,7 +599,7 @@ type ServiceSpec struct { // and LoadBalancer IPs. // // +optional - // +kubebuilder:default:=Local + // +kubebuilder:default=Local ExternalTrafficPolicy *ExternalTrafficPolicy `json:"externalTrafficPolicy,omitempty"` // LoadBalancerIP is a static IP address for the load balancer. Requires service type to be LoadBalancer. diff --git a/apis/v1alpha2/observabilitypolicy_types.go b/apis/v1alpha2/observabilitypolicy_types.go index d46b64ddd3..258ca5c92f 100644 --- a/apis/v1alpha2/observabilitypolicy_types.go +++ b/apis/v1alpha2/observabilitypolicy_types.go @@ -7,7 +7,6 @@ import ( ngfAPIv1alpha1 "github.com/nginx/nginx-gateway-fabric/apis/v1alpha1" ) -// +genclient // +kubebuilder:object:root=true // +kubebuilder:storageversion // +kubebuilder:subresource:status diff --git a/config/crd/bases/gateway.nginx.org_wafpolicies.yaml b/config/crd/bases/gateway.nginx.org_wafpolicies.yaml new file mode 100644 index 0000000000..c0421251b2 --- /dev/null +++ b/config/crd/bases/gateway.nginx.org_wafpolicies.yaml @@ -0,0 +1,689 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + labels: + gateway.networking.k8s.io/policy: inherited + name: wafpolicies.gateway.nginx.org +spec: + group: gateway.nginx.org + names: + categories: + - nginx-gateway-fabric + kind: WAFPolicy + listKind: WAFPolicyList + plural: wafpolicies + singular: wafpolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + WAFPolicy is an Inherited Attached Policy. It provides a way to configure NGINX App Protect Web Application Firewall + for Gateways and Routes. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of the WAFPolicy. + properties: + policySource: + description: PolicySource defines the source location and configuration + for the compiled WAF policy bundle. + properties: + authSecret: + description: AuthSecret is the Secret containing authentication + credentials for the WAF policy source. + properties: + name: + description: Name is the name of the Secret containing authentication + credentials for the WAF policy source. + maxLength: 253 + minLength: 1 + pattern: ^[a-zA-Z0-9_-]+$ + type: string + required: + - name + type: object + fileLocation: + description: FileLocation defines the location of the WAF policy + file. + maxLength: 256 + minLength: 1 + type: string + polling: + description: Polling defines the polling configuration for automatic + WAF policy change detection. + properties: + checksumLocation: + description: |- + ChecksumLocation specifies the location of the checksum file for the policy bundle. + If not specified, defaults to .sha256 + maxLength: 2048 + type: string + enabled: + default: false + description: |- + Enabled indicates whether polling is enabled for automatic WAF policy change detection. + When enabled, NGINX Gateway Fabric will periodically check for policy changes using checksum validation. + type: boolean + interval: + default: 5m + description: |- + Interval is the polling interval to check for WAF policy changes. + Must be a valid duration string (e.g., "5m", "30s", "1h"). + Defaults to "5m" if polling is enabled. + pattern: ^[0-9]{1,4}(ms|s|m|h)?$ + type: string + type: object + retry: + description: Retry defines the retry configuration for WAF policy + fetch failures. + properties: + attempts: + default: 3 + description: |- + Attempts is the number of retry attempts for fetching the WAF policy. + Set to 0 to disable retries. + format: int32 + minimum: 0 + type: integer + backoff: + default: exponential + description: |- + Backoff defines the backoff strategy for retry attempts. + Supported values: "exponential", "linear" + enum: + - exponential + - linear + type: string + maxDelay: + default: 5m + description: |- + MaxDelay is the maximum delay between retry attempts. + Must be a valid duration string (e.g., "5m", "30s"). + pattern: ^[0-9]{1,4}(ms|s|m|h)?$ + type: string + type: object + timeout: + description: Timeout for policy downloads. + pattern: ^[0-9]{1,4}(ms|s|m|h)?$ + type: string + validation: + description: Validation defines the validation methods for policy + integrity verification. + properties: + methods: + description: |- + Methods specifies the validation methods to use for policy integrity verification. + Currently supported: ["checksum"] + items: + description: WAFPolicyValidationMethod defines the supported + validation methods. + enum: + - checksum + type: string + type: array + x-kubernetes-list-type: set + type: object + required: + - fileLocation + type: object + securityLogs: + description: |- + SecurityLogs defines the security logging configuration for app_protect_security_log directives. + Multiple logging configurations can be specified to send logs to different destinations. + items: + description: |- + WAFSecurityLog defines the security logging configuration for app_protect_security_log directives. + LogProfile and LogProfileBundle are mutually exclusive per security log entry. + properties: + destination: + description: Destination defines where the security logs should + be sent. + properties: + file: + description: |- + File defines the file destination configuration. + Only valid when type is "file". + properties: + path: + description: |- + Path is the file path where security logs will be written. + Must be accessible to the waf-enforcer container. + maxLength: 256 + minLength: 1 + pattern: ^/.*$ + type: string + required: + - path + type: object + syslog: + description: |- + Syslog defines the syslog destination configuration. + Only valid when type is "syslog". + properties: + server: + description: Server is the syslog server address in + the format "host:port". + maxLength: 253 + minLength: 1 + pattern: ^[a-zA-Z0-9.-]+:[0-9]+$ + type: string + required: + - server + type: object + type: + default: stderr + description: Type identifies the type of security log destination. + enum: + - stderr + - file + - syslog + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: destination.file must be nil if the destination.type + is not file + rule: '!(has(self.file) && self.type != ''file'')' + - message: destination.file must be specified for file destination.type + rule: '!(!has(self.file) && self.type == ''file'')' + - message: destination.syslog must be nil if the destination.type + is not syslog + rule: '!(has(self.syslog) && self.type != ''syslog'')' + - message: destination.syslog must be specified for syslog destination.type + rule: '!(!has(self.syslog) && self.type == ''syslog'')' + logProfile: + description: LogProfile defines the built-in logging profile + to use. + enum: + - log_default + - log_all + - log_illegal + - log_blocked + - log_grpc_all + - log_grpc_blocked + - log_grpc_illegal + type: string + logProfileBundle: + description: LogProfileBundle defines a custom logging profile + bundle, similar to policy bundle. + properties: + authSecret: + description: AuthSecret is the Secret containing authentication + credentials for the WAF policy source. + properties: + name: + description: Name is the name of the Secret containing + authentication credentials for the WAF policy source. + maxLength: 253 + minLength: 1 + pattern: ^[a-zA-Z0-9_-]+$ + type: string + required: + - name + type: object + fileLocation: + description: FileLocation defines the location of the WAF + policy file. + maxLength: 256 + minLength: 1 + type: string + polling: + description: Polling defines the polling configuration for + automatic WAF policy change detection. + properties: + checksumLocation: + description: |- + ChecksumLocation specifies the location of the checksum file for the policy bundle. + If not specified, defaults to .sha256 + maxLength: 2048 + type: string + enabled: + default: false + description: |- + Enabled indicates whether polling is enabled for automatic WAF policy change detection. + When enabled, NGINX Gateway Fabric will periodically check for policy changes using checksum validation. + type: boolean + interval: + default: 5m + description: |- + Interval is the polling interval to check for WAF policy changes. + Must be a valid duration string (e.g., "5m", "30s", "1h"). + Defaults to "5m" if polling is enabled. + pattern: ^[0-9]{1,4}(ms|s|m|h)?$ + type: string + type: object + retry: + description: Retry defines the retry configuration for WAF + policy fetch failures. + properties: + attempts: + default: 3 + description: |- + Attempts is the number of retry attempts for fetching the WAF policy. + Set to 0 to disable retries. + format: int32 + minimum: 0 + type: integer + backoff: + default: exponential + description: |- + Backoff defines the backoff strategy for retry attempts. + Supported values: "exponential", "linear" + enum: + - exponential + - linear + type: string + maxDelay: + default: 5m + description: |- + MaxDelay is the maximum delay between retry attempts. + Must be a valid duration string (e.g., "5m", "30s"). + pattern: ^[0-9]{1,4}(ms|s|m|h)?$ + type: string + type: object + timeout: + description: Timeout for policy downloads. + pattern: ^[0-9]{1,4}(ms|s|m|h)?$ + type: string + validation: + description: Validation defines the validation methods for + policy integrity verification. + properties: + methods: + description: |- + Methods specifies the validation methods to use for policy integrity verification. + Currently supported: ["checksum"] + items: + description: WAFPolicyValidationMethod defines the + supported validation methods. + enum: + - checksum + type: string + type: array + x-kubernetes-list-type: set + type: object + required: + - fileLocation + type: object + name: + description: Name is the name of the security log configuration. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z0-9]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$ + type: string + required: + - destination + type: object + x-kubernetes-validations: + - message: only one of logProfile or logProfileBundle may be set + rule: '!(has(self.logProfile) && has(self.logProfileBundle))' + - message: at least one of logProfile or logProfileBundle must be + set + rule: has(self.logProfile) || has(self.logProfileBundle) + maxItems: 32 + type: array + targetRef: + description: |- + TargetRef identifies an API object to apply the policy to. + Object must be in the same namespace as the policy. + Support: Gateway, HTTPRoute, GRPCRoute. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + x-kubernetes-validations: + - message: 'TargetRef Kind must be one of: Gateway, HTTPRoute, or + GRPCRoute' + rule: (self.kind=='Gateway' || self.kind=='HTTPRoute' || self.kind=='GRPCRoute') + - message: TargetRef Group must be gateway.networking.k8s.io. + rule: (self.group=='gateway.networking.k8s.io') + required: + - targetRef + type: object + status: + description: Status defines the state of the WAFPolicy. + properties: + ancestors: + description: |- + Ancestors is a list of ancestor resources (usually Gateways) that are + associated with the policy, and the status of the policy with respect to + each ancestor. When this policy attaches to a parent, the controller that + manages the parent and the ancestors MUST add an entry to this list when + the controller first sees the policy and SHOULD update the entry as + appropriate when the relevant ancestor is modified. + + Note that choosing the relevant ancestor is left to the Policy designers; + an important part of Policy design is designing the right object level at + which to namespace this status. + + Note also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations MUST + use the ControllerName field to uniquely identify the entries in this list + that they are responsible for. + + Note that to achieve this, the list of PolicyAncestorStatus structs + MUST be treated as a map with a composite key, made up of the AncestorRef + and ControllerName fields combined. + + A maximum of 16 ancestors will be represented in this list. An empty list + means the Policy is not relevant for any ancestors. + + If this slice is full, implementations MUST NOT add further entries. + Instead they MUST consider the policy unimplementable and signal that + on any related resources such as the ancestor that would be referenced + here. For example, if this list was full on BackendTLSPolicy, no + additional Gateways would be able to reference the Service targeted by + the BackendTLSPolicy. + items: + description: |- + PolicyAncestorStatus describes the status of a route with respect to an + associated Ancestor. + + Ancestors refer to objects that are either the Target of a policy or above it + in terms of object hierarchy. For example, if a policy targets a Service, the + Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and + the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most + useful object to place Policy status on, so we recommend that implementations + SHOULD use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. + + In the context of policy attachment, the Ancestor is used to distinguish which + resource results in a distinct application of this policy. For example, if a policy + targets a Service, it may have a distinct result per attached Gateway. + + Policies targeting the same resource may have different effects depending on the + ancestors of those resources. For example, different Gateways targeting the same + Service may have different capabilities, especially if they have different underlying + implementations. + + For example, in BackendTLSPolicy, the Policy attaches to a Service that is + used as a backend in a HTTPRoute that is itself attached to a Gateway. + In this case, the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. + + Note that a parent is also an ancestor, so for objects where the parent is the + relevant object for status, this struct SHOULD still be used. + + This struct is intended to be used in a slice that's effectively a map, + with a composite key made up of the AncestorRef and the ControllerName. + properties: + ancestorRef: + description: |- + AncestorRef corresponds with a ParentRef in the spec that this + PolicyAncestorStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + conditions: + description: Conditions describes the status of the Policy with + respect to the given Ancestor. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + required: + - ancestorRef + - controllerName + type: object + maxItems: 16 + type: array + required: + - ancestors + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {}