Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
12 changes: 12 additions & 0 deletions apis/v1alpha1/policy_methods.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,15 @@ func (p *UpstreamSettingsPolicy) GetPolicyStatus() gatewayv1.PolicyStatus {
func (p *UpstreamSettingsPolicy) SetPolicyStatus(status gatewayv1.PolicyStatus) {
p.Status = status
}

func (p *SnippetsPolicy) GetTargetRefs() []gatewayv1.LocalPolicyTargetReference {
return p.Spec.TargetRefs
}

func (p *SnippetsPolicy) GetPolicyStatus() gatewayv1.PolicyStatus {
return p.Status
}

func (p *SnippetsPolicy) SetPolicyStatus(status gatewayv1.PolicyStatus) {
p.Status = status
}
2 changes: 2 additions & 0 deletions apis/v1alpha1/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&SnippetsFilterList{},
&UpstreamSettingsPolicy{},
&UpstreamSettingsPolicyList{},
&SnippetsPolicy{},
&SnippetsPolicyList{},
)
// AddToGroupVersion allows the serialization of client types like ListOptions.
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
Expand Down
69 changes: 69 additions & 0 deletions apis/v1alpha1/snippetspolicy_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
Copyright 2025 The NGINX Gateway Fabric Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
)

// +genclient
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
// +kubebuilder:metadata:labels="gateway.networking.k8s.io/policy=direct"
// +kubebuilder:resource:categories=nginx-gateway-fabric,shortName=snippetspolicy
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`

// SnippetsPolicy provides a way to inject NGINX snippets into the configuration on Gateway level.
type SnippetsPolicy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// Spec defines the desired state of the SnippetsPolicy.
Spec SnippetsPolicySpec `json:"spec"`

// Status defines the current state of the SnippetsPolicy.
Status gatewayv1.PolicyStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

// SnippetsPolicyList contains a list of SnippetsPolicies.
type SnippetsPolicyList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []SnippetsPolicy `json:"items"`
}

// SnippetsPolicySpec defines the desired state of the SnippetsPolicy.
type SnippetsPolicySpec struct {
// TargetRefs identifies API object(s) to apply the policy to.
// +kubebuilder:validation:MinItems=1
// +kubebuilder:validation:MaxItems=16
// +kubebuilder:validation:XValidation:message="TargetRefs Kind must be Gateway",rule="self.all(t, t.kind == 'Gateway')"
// +kubebuilder:validation:XValidation:message="TargetRefs Group must be gateway.networking.k8s.io",rule="self.all(t, t.group == 'gateway.networking.k8s.io')"
//nolint:lll
TargetRefs []gatewayv1.LocalPolicyTargetReference `json:"targetRefs"`

// Snippets is a list of snippets to be injected into the NGINX configuration.
// +kubebuilder:validation:MaxItems=3
// +kubebuilder:validation:XValidation:message="Only one snippet allowed per context",rule="self.all(s1, self.exists_one(s2, s1.context == s2.context))"
// +kubebuilder:validation:XValidation:message="http.server.location context is not supported in SnippetsPolicy",rule="!self.exists(s, s.context == 'http.server.location')"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why we can't support location at the Gateway level. It will just apply to all locations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

location-level directives usually express per-path or per-request behavior. Applying them at the Gateway level would implicitly affect all attached Routes, which may make validation and NGINX behavior harder to reason about. Keeping this logic closer to Route-level constructs while using the Gateway for shared defaults seems like a reasonable trade-off, but open to discussion.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other policies that we support, when applied at the Gateway level, are intended to apply to all Routes. That's the main reason why we attach at the Gateway level. Users who have been attaching SnippetsFilters to their Routes want a way to define a config for all Routes so they don't have to keep duplicating SnippetsFilters.

There is obviously going to be the risk of overlapping configs, validation issues, and confusion in how things are applied. Unfortunately, that's the nature of using Snippets, and we will need to warn users about this in our docs when we update them for this feature.

//nolint:lll
Snippets []Snippet `json:"snippets"`
}
84 changes: 84 additions & 0 deletions apis/v1alpha1/zz_generated.deepcopy.go

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

3 changes: 2 additions & 1 deletion charts/nginx-gateway-fabric/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ The following table lists the configurable parameters of the NGINX Gateway Fabri
| `nginx.usage.resolver` | The nameserver used to resolve the NGINX Plus usage reporting endpoint. Used with NGINX Instance Manager. | string | `""` |
| `nginx.usage.secretName` | The name of the Secret containing the JWT for NGINX Plus usage reporting. Must exist in the same namespace that the NGINX Gateway Fabric control plane is running in (default namespace: nginx-gateway). | string | `"nplus-license"` |
| `nginx.usage.skipVerify` | Disable client verification of the NGINX Plus usage reporting server certificate. | bool | `false` |
| `nginxGateway` | The nginxGateway section contains configuration for the NGINX Gateway Fabric control plane deployment. | object | `{"affinity":{},"autoscaling":{"enable":false},"config":{"logging":{"level":"info"}},"configAnnotations":{},"extraVolumeMounts":[],"extraVolumes":[],"gatewayClassAnnotations":{},"gatewayClassName":"nginx","gatewayControllerName":"gateway.nginx.org/nginx-gateway-controller","gwAPIExperimentalFeatures":{"enable":false},"gwAPIInferenceExtension":{"enable":false,"endpointPicker":{"disableTLS":false,"skipVerify":true}},"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric","tag":"edge"},"kind":"deployment","labels":{},"leaderElection":{"enable":true,"lockName":""},"lifecycle":{},"metrics":{"enable":true,"port":9113,"secure":false},"name":"","nodeSelector":{},"podAnnotations":{},"productTelemetry":{"enable":true},"readinessProbe":{"enable":true,"initialDelaySeconds":3,"port":8081},"replicas":1,"resources":{},"service":{"annotations":{},"labels":{}},"serviceAccount":{"annotations":{},"imagePullSecret":"","imagePullSecrets":[],"name":""},"snippetsFilters":{"enable":false},"terminationGracePeriodSeconds":30,"tolerations":[],"topologySpreadConstraints":[]}` |
| `nginxGateway` | The nginxGateway section contains configuration for the NGINX Gateway Fabric control plane deployment. | object | `{"affinity":{},"autoscaling":{"enable":false},"config":{"logging":{"level":"info"}},"configAnnotations":{},"extraVolumeMounts":[],"extraVolumes":[],"gatewayClassAnnotations":{},"gatewayClassName":"nginx","gatewayControllerName":"gateway.nginx.org/nginx-gateway-controller","gwAPIExperimentalFeatures":{"enable":false},"gwAPIInferenceExtension":{"enable":false,"endpointPicker":{"disableTLS":false,"skipVerify":true}},"image":{"pullPolicy":"Always","repository":"ghcr.io/nginx/nginx-gateway-fabric","tag":"edge"},"kind":"deployment","labels":{},"leaderElection":{"enable":true,"lockName":""},"lifecycle":{},"metrics":{"enable":true,"port":9113,"secure":false},"name":"","nodeSelector":{},"podAnnotations":{},"productTelemetry":{"enable":true},"readinessProbe":{"enable":true,"initialDelaySeconds":3,"port":8081},"replicas":1,"resources":{},"service":{"annotations":{},"labels":{}},"serviceAccount":{"annotations":{},"imagePullSecret":"","imagePullSecrets":[],"name":""},"snippetsFilters":{"enable":false},"snippetsPolicies":{"enable":false},"terminationGracePeriodSeconds":30,"tolerations":[],"topologySpreadConstraints":[]}` |
| `nginxGateway.affinity` | The affinity of the NGINX Gateway Fabric control plane pod. | object | `{}` |
| `nginxGateway.autoscaling` | Autoscaling configuration for the NGINX Gateway Fabric control plane. | object | `{"enable":false}` |
| `nginxGateway.autoscaling.enable` | Enable or disable Horizontal Pod Autoscaler for the control plane. | bool | `false` |
Expand Down Expand Up @@ -290,6 +290,7 @@ The following table lists the configurable parameters of the NGINX Gateway Fabri
| `nginxGateway.serviceAccount.imagePullSecrets` | A list of secret names containing docker registry credentials for the control plane. Secrets must exist in the same namespace as the helm release. | list | `[]` |
| `nginxGateway.serviceAccount.name` | The name of the service account of the NGINX Gateway Fabric control plane pods. Used for RBAC. | string | Autogenerated if not set or set to "" |
| `nginxGateway.snippetsFilters.enable` | Enable SnippetsFilters feature. SnippetsFilters allow inserting NGINX configuration into the generated NGINX config for HTTPRoute and GRPCRoute resources. | bool | `false` |
| `nginxGateway.snippetsPolicies.enable` | Enable SnippetsPolicies feature. SnippetsPolicies allow inserting NGINX configuration into the generated NGINX config for Gateway resources. | bool | `false` |
| `nginxGateway.terminationGracePeriodSeconds` | The termination grace period of the NGINX Gateway Fabric control plane pod. | int | `30` |
| `nginxGateway.tolerations` | Tolerations for the NGINX Gateway Fabric control plane pod. | list | `[]` |
| `nginxGateway.topologySpreadConstraints` | The topology spread constraints for the NGINX Gateway Fabric control plane pod. | list | `[]` |
Expand Down
6 changes: 6 additions & 0 deletions charts/nginx-gateway-fabric/templates/clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ rules:
{{- if .Values.nginxGateway.snippetsFilters.enable }}
- snippetsfilters
{{- end }}
{{- if .Values.nginxGateway.snippetsPolicies.enable }}
- snippetspolicies
{{- end }}
verbs:
- list
- watch
Expand All @@ -145,6 +148,9 @@ rules:
{{- if .Values.nginxGateway.snippetsFilters.enable }}
- snippetsfilters/status
{{- end }}
{{- if .Values.nginxGateway.snippetsPolicies.enable }}
- snippetspolicies/status
{{- end }}
verbs:
- update
{{- if .Values.nginxGateway.gwAPIInferenceExtension.enable }}
Expand Down
3 changes: 3 additions & 0 deletions charts/nginx-gateway-fabric/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,9 @@ spec:
{{- if .Values.nginxGateway.snippetsFilters.enable }}
- --snippets-filters
{{- end }}
{{- if .Values.nginxGateway.snippetsPolicies.enable }}
- --snippets-policies
{{- end }}
{{- if .Capabilities.APIVersions.Has "security.openshift.io/v1/SecurityContextConstraints" }}
- --nginx-scc={{ include "nginx-gateway.scc-name" . }}-nginx
{{- end}}
Expand Down
14 changes: 14 additions & 0 deletions charts/nginx-gateway-fabric/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,20 @@
"title": "snippetsFilters",
"type": "object"
},
"snippetsPolicies": {
"properties": {
"enable": {
"default": false,
"description": "Enable SnippetsPolicies feature. SnippetsPolicies allow inserting NGINX configuration into the generated NGINX\nconfig for Gateway resources.",
"required": [],
"title": "enable",
"type": "boolean"
}
},
"required": [],
"title": "snippetsPolicies",
"type": "object"
},
"terminationGracePeriodSeconds": {
"default": 30,
"description": "The termination grace period of the NGINX Gateway Fabric control plane pod.",
Expand Down
5 changes: 5 additions & 0 deletions charts/nginx-gateway-fabric/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ nginxGateway:
# config for HTTPRoute and GRPCRoute resources.
enable: false

snippetsPolicies:
# -- Enable SnippetsPolicies feature. SnippetsPolicies allow inserting NGINX configuration into the generated NGINX
# config for Gateway resources.
enable: false

# -- The nginx section contains the configuration for all NGINX data plane deployments
# installed by the NGINX Gateway Fabric control plane.
nginx:
Expand Down
13 changes: 12 additions & 1 deletion cmd/gateway/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ func createControllerCommand() *cobra.Command {
usageReportCASecretFlag = "usage-report-ca-secret" //nolint:gosec // not credentials
usageReportEnforceInitialReportFlag = "usage-report-enforce-initial-report"
snippetsFiltersFlag = "snippets-filters"
snippetsPoliciesFlag = "snippets-policies"
nginxSCCFlag = "nginx-scc"
)

Expand Down Expand Up @@ -156,7 +157,8 @@ func createControllerCommand() *cobra.Command {

disableProductTelemetry bool

snippetsFilters bool
snippetsFilters bool
snippetsPolicies bool

plus bool
nginxDockerSecrets = stringSliceValidatingValue{
Expand Down Expand Up @@ -282,6 +284,7 @@ func createControllerCommand() *cobra.Command {
Values: flagValues,
},
SnippetsFilters: snippetsFilters,
SnippetsPolicies: snippetsPolicies,
NginxDockerSecretNames: nginxDockerSecrets.values,
AgentTLSSecretName: agentTLSSecretName.value,
NGINXSCCName: nginxSCCName.value,
Expand Down Expand Up @@ -512,6 +515,14 @@ func createControllerCommand() *cobra.Command {
"generated NGINX config for HTTPRoute and GRPCRoute resources.",
)

cmd.Flags().BoolVar(
&snippetsPolicies,
snippetsPoliciesFlag,
false,
"Enable SnippetsPolicies feature. SnippetsPolicies allow inserting NGINX configuration into the "+
"generated NGINX config for Gateway resources.",
)

cmd.Flags().Var(
&nginxSCCName,
nginxSCCFlag,
Expand Down
10 changes: 10 additions & 0 deletions cmd/gateway/commands_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func TestControllerCmdFlagValidation(t *testing.T) {
"--usage-report-client-ssl-secret=client-secret",
"--usage-report-enforce-initial-report",
"--snippets-filters",
"--snippets-policies",
"--nginx-scc=nginx-sscc-name",
"--nginx-one-dataplane-key-secret=dataplane-key-secret",
"--nginx-one-telemetry-endpoint-host=telemetry-endpoint-host",
Expand Down Expand Up @@ -417,6 +418,15 @@ func TestControllerCmdFlagValidation(t *testing.T) {
},
wantErr: true,
},
{
name: "snippets-policies is not a bool",
expectedErrPrefix: `invalid argument "not-a-bool" for "--snippets-policies" flag: strconv.ParseBool:` +
` parsing "not-a-bool": invalid syntax`,
args: []string{
"--snippets-policies=not-a-bool",
},
wantErr: true,
},
{
name: "nginx-scc is set to empty string",
args: []string{
Expand Down
Loading