Skip to content

Commit 34b6db7

Browse files
sjbermanKate Osborn
and
Kate Osborn
committed
Restrict policies to non-duplicate routes
Problem: Some NGINX directives are not applied or enforced when configured in an internal location. This occurs when redirecting or rewriting a request from an external location to an internal location. Solution: Only accept a policy if the Route it targets is the only Route that matches the hostname, port, and path combination. If other Routes overlap, the policy will be rejected. This allows us to apply policy configuration to the external location instead of the internal locations. We would limit the policies we accept rather than limiting which Routes we accept. This is possible because, with the policy restriction, a policy cannot be applied to a Route that shares an external location with another Route. However, for the otel module, we still require some internal location directives to be specified, so the policy generator has been refactored to account for this. Finally, revert named locations back to internal locations. As part of this process, we've learned that named locations do not behave as expected. Co-authored-by: Kate Osborn <[email protected]>
1 parent 99a4532 commit 34b6db7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2148
-1159
lines changed

internal/mode/static/handler.go

+2-5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import (
2424
ngxConfig "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config"
2525
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/file"
2626
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/runtime"
27-
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/policies"
2827
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state"
2928
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
3029
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/graph"
@@ -65,8 +64,6 @@ type eventHandlerConfig struct {
6564
serviceResolver resolver.ServiceResolver
6665
// generator is the nginx config generator.
6766
generator ngxConfig.Generator
68-
// policyConfigGenerator is the config generator for NGF policies.
69-
policyConfigGenerator policies.ConfigGenerator
7067
// k8sClient is a Kubernetes API client
7168
k8sClient client.Client
7269
// logLevelSetter is used to update the logging level.
@@ -196,7 +193,7 @@ func (h *eventHandlerImpl) HandleEventBatch(ctx context.Context, logger logr.Log
196193
return
197194
case state.EndpointsOnlyChange:
198195
h.version++
199-
cfg := dataplane.BuildConfiguration(ctx, graph, h.cfg.serviceResolver, h.cfg.policyConfigGenerator, h.version)
196+
cfg := dataplane.BuildConfiguration(ctx, graph, h.cfg.serviceResolver, h.version)
200197

201198
h.setLatestConfiguration(&cfg)
202199

@@ -207,7 +204,7 @@ func (h *eventHandlerImpl) HandleEventBatch(ctx context.Context, logger logr.Log
207204
)
208205
case state.ClusterStateChange:
209206
h.version++
210-
cfg := dataplane.BuildConfiguration(ctx, graph, h.cfg.serviceResolver, h.cfg.policyConfigGenerator, h.version)
207+
cfg := dataplane.BuildConfiguration(ctx, graph, h.cfg.serviceResolver, h.version)
211208

212209
h.setLatestConfiguration(&cfg)
213210

internal/mode/static/manager.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ import (
4949
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/config"
5050
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/metrics/collectors"
5151
ngxcfg "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config"
52+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies"
53+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies/clientsettings"
54+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies/observability"
5255
ngxvalidation "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/validation"
5356
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/file"
5457
ngxruntime "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/runtime"
55-
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/policies"
56-
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/policies/clientsettings"
57-
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/policies/observability"
5858
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state"
5959
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/resolver"
6060
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/validation"
@@ -232,7 +232,6 @@ func StartManager(cfg config.Config) error {
232232
usageSecret: usageSecret,
233233
gatewayCtlrName: cfg.GatewayCtlrName,
234234
updateGatewayClassStatus: cfg.UpdateGatewayClassStatus,
235-
policyConfigGenerator: policyManager,
236235
})
237236

238237
objects, objectLists := prepareFirstEventBatchPreparerArgs(
@@ -287,17 +286,15 @@ func StartManager(cfg config.Config) error {
287286
func createPolicyManager(
288287
mustExtractGVK kinds.MustExtractGVK,
289288
validator validation.GenericValidator,
290-
) *policies.Manager {
289+
) *policies.CompositeValidator {
291290
cfgs := []policies.ManagerConfig{
292291
{
293292
GVK: mustExtractGVK(&ngfAPI.ClientSettingsPolicy{}),
294293
Validator: clientsettings.NewValidator(validator),
295-
Generator: clientsettings.Generate,
296294
},
297295
{
298296
GVK: mustExtractGVK(&ngfAPI.ObservabilityPolicy{}),
299297
Validator: observability.NewValidator(validator),
300-
Generator: observability.Generate,
301298
},
302299
}
303300

internal/mode/static/nginx/config/generator.go

+18-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package config
33
import (
44
"path/filepath"
55

6+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies"
7+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies/clientsettings"
8+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies/observability"
69
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/file"
710
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/dataplane"
811
)
@@ -85,7 +88,12 @@ func (g GeneratorImpl) Generate(conf dataplane.Configuration) []file.File {
8588
files = append(files, generatePEM(id, pair.Cert, pair.Key))
8689
}
8790

88-
files = append(files, g.generateHTTPConfig(conf)...)
91+
policyGenerator := policies.NewCompositeGenerator(
92+
clientsettings.NewGenerator(),
93+
observability.NewGenerator(conf.Telemetry),
94+
)
95+
96+
files = append(files, g.generateHTTPConfig(conf, policyGenerator)...)
8997

9098
files = append(files, generateConfigVersion(conf.Version))
9199

@@ -127,20 +135,23 @@ func generateCertBundleFileName(id dataplane.CertBundleID) string {
127135
return filepath.Join(secretsFolder, string(id)+".crt")
128136
}
129137

130-
func (g GeneratorImpl) generateHTTPConfig(conf dataplane.Configuration) []file.File {
138+
func (g GeneratorImpl) generateHTTPConfig(
139+
conf dataplane.Configuration,
140+
generator policies.Generator,
141+
) []file.File {
131142
fileBytes := make(map[string][]byte)
132143

133-
for _, execute := range g.getExecuteFuncs() {
144+
for _, execute := range g.getExecuteFuncs(generator) {
134145
results := execute(conf)
135146
for _, res := range results {
136147
fileBytes[res.dest] = append(fileBytes[res.dest], res.data...)
137148
}
138149
}
139150

140151
files := make([]file.File, 0, len(fileBytes))
141-
for filepath, bytes := range fileBytes {
152+
for fp, bytes := range fileBytes {
142153
files = append(files, file.File{
143-
Path: filepath,
154+
Path: fp,
144155
Content: bytes,
145156
Type: file.TypeRegular,
146157
})
@@ -149,10 +160,10 @@ func (g GeneratorImpl) generateHTTPConfig(conf dataplane.Configuration) []file.F
149160
return files
150161
}
151162

152-
func (g GeneratorImpl) getExecuteFuncs() []executeFunc {
163+
func (g GeneratorImpl) getExecuteFuncs(generator policies.Generator) []executeFunc {
153164
return []executeFunc{
154165
executeBaseHTTPConfig,
155-
executeServers,
166+
newExecuteServersFunc(generator),
156167
g.executeUpstreams,
157168
executeSplitClients,
158169
executeMaps,

internal/mode/static/nginx/config/http/config.go

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package http
22

3+
const InternalRoutePathPrefix = "/_ngf-internal"
4+
35
// Server holds all configuration for an HTTP server.
46
type Server struct {
57
SSL *SSL
68
ServerName string
79
Locations []Location
8-
Includes []string
10+
Includes []Include
911
Port int32
1012
IsDefaultHTTP bool
1113
IsDefaultSSL bool
@@ -18,19 +20,28 @@ type IPFamily struct {
1820
IPv6 bool
1921
}
2022

23+
type LocationType string
24+
25+
const (
26+
InternalLocationType LocationType = "internal"
27+
ExternalLocationType LocationType = "external"
28+
RedirectLocationType LocationType = "redirect"
29+
)
30+
2131
// Location holds all configuration for an HTTP location.
2232
type Location struct {
2333
Path string
2434
ProxyPass string
2535
HTTPMatchKey string
26-
HTTPMatchVar string
36+
Type LocationType
2737
ProxySetHeaders []Header
2838
ProxySSLVerify *ProxySSLVerify
2939
Return *Return
3040
ResponseHeaders ResponseHeaders
3141
Rewrites []string
32-
Includes []string
42+
Includes []Include
3343
GRPC bool
44+
Redirects bool
3445
}
3546

3647
// Header defines an HTTP header to be passed to the proxied server.
@@ -101,7 +112,7 @@ type Map struct {
101112
Parameters []MapParameter
102113
}
103114

104-
// Parameter defines a Value and Result pair in a Map.
115+
// MapParameter defines a Value and Result pair in a Map.
105116
type MapParameter struct {
106117
Value string
107118
Result string
@@ -118,3 +129,9 @@ type ServerConfig struct {
118129
Servers []Server
119130
IPFamily IPFamily
120131
}
132+
133+
// Include defines a file that's included via the include directive.
134+
type Include struct {
135+
Name string
136+
Content []byte
137+
}

internal/mode/static/nginx/config/maps_template.go

+5
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,9 @@ map $http_upgrade $connection_upgrade {
2424
default upgrade;
2525
'' close;
2626
}
27+
28+
## Returns just the path from the original request URI.
29+
map $request_uri $request_uri_path {
30+
"~^(?P<path>[^?]*)(\?.*)?$" $path;
31+
}
2732
`

internal/mode/static/nginx/config/maps_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ func TestExecuteMaps(t *testing.T) {
8686
"map ${http_my_set_header} $my_set_header_header_var {": 0,
8787
"map $http_host $gw_api_compliant_host {": 1,
8888
"map $http_upgrade $connection_upgrade {": 1,
89+
"map $request_uri $request_uri_path {": 1,
8990
}
9091

9192
mapResult := executeMaps(conf)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package clientsettings
2+
3+
import (
4+
"fmt"
5+
"text/template"
6+
7+
ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
8+
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
9+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/http"
10+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies"
11+
)
12+
13+
var tmpl = template.Must(template.New("client settings policy").Parse(clientSettingsTemplate))
14+
15+
const clientSettingsTemplate = `
16+
{{- if .Body }}
17+
{{- if .Body.MaxSize }}
18+
client_max_body_size {{ .Body.MaxSize }};
19+
{{- end }}
20+
{{- if .Body.Timeout }}
21+
client_body_timeout {{ .Body.Timeout }};
22+
{{- end }}
23+
{{- end }}
24+
{{- if .KeepAlive }}
25+
{{- if .KeepAlive.Requests }}
26+
keepalive_requests {{ .KeepAlive.Requests }};
27+
{{- end }}
28+
{{- if .KeepAlive.Time }}
29+
keepalive_time {{ .KeepAlive.Time }};
30+
{{- end }}
31+
{{- if .KeepAlive.Timeout }}
32+
{{- if and .KeepAlive.Timeout.Server .KeepAlive.Timeout.Header }}
33+
keepalive_timeout {{ .KeepAlive.Timeout.Server }} {{ .KeepAlive.Timeout.Header }};
34+
{{- else if .KeepAlive.Timeout.Server }}
35+
keepalive_timeout {{ .KeepAlive.Timeout.Server }};
36+
{{- end }}
37+
{{- end }}
38+
{{- end }}
39+
`
40+
41+
// Generator generates nginx configuration based on a clientsettings policy.
42+
type Generator struct{}
43+
44+
// NewGenerator returns a new instance of Generator.
45+
func NewGenerator() *Generator {
46+
return &Generator{}
47+
}
48+
49+
// GenerateForServer generates policy configuration for the server block.
50+
func (g Generator) GenerateForServer(pols []policies.Policy, _ http.Server) policies.GenerateResultFiles {
51+
return generate(pols)
52+
}
53+
54+
// GenerateForServer generates policy configuration for a normal location block.
55+
func (g Generator) GenerateForLocation(pols []policies.Policy, _ http.Location) policies.GenerateResultFiles {
56+
return generate(pols)
57+
}
58+
59+
// GenerateForServer generates policy configuration for a normal location block.
60+
func (g Generator) GenerateForInternalLocation(pols []policies.Policy) policies.GenerateResultFiles {
61+
return generate(pols)
62+
}
63+
64+
func generate(pols []policies.Policy) policies.GenerateResultFiles {
65+
files := make(policies.GenerateResultFiles, 0, len(pols))
66+
67+
for _, pol := range pols {
68+
csp, ok := pol.(*ngfAPI.ClientSettingsPolicy)
69+
if !ok {
70+
continue
71+
}
72+
73+
files = append(files, policies.File{
74+
Name: fmt.Sprintf("ClientSettingsPolicy_%s_%s.conf", csp.Namespace, csp.Name),
75+
Content: helpers.MustExecuteTemplate(tmpl, csp.Spec),
76+
})
77+
}
78+
79+
return files
80+
}

internal/mode/static/policies/clientsettings/generator_test.go renamed to internal/mode/static/nginx/config/policies/clientsettings/generator_test.go

+41-10
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import (
77

88
ngfAPI "github.com/nginxinc/nginx-gateway-fabric/apis/v1alpha1"
99
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
10-
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/policies"
11-
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/policies/clientsettings"
12-
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/policies/policiesfakes"
10+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/http"
11+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies"
12+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies/clientsettings"
1313
)
1414

1515
func TestGenerate(t *testing.T) {
@@ -153,21 +153,52 @@ func TestGenerate(t *testing.T) {
153153
t.Run(test.name, func(t *testing.T) {
154154
g := NewWithT(t)
155155

156-
cfgString := string(clientsettings.Generate(test.policy, nil))
156+
generator := clientsettings.NewGenerator()
157+
158+
resFiles := generator.GenerateForServer([]policies.Policy{test.policy}, http.Server{})
159+
g.Expect(resFiles).To(HaveLen(1))
160+
161+
for _, str := range test.expStrings {
162+
g.Expect(string(resFiles[0].Content)).To(ContainSubstring(str))
163+
}
164+
165+
resFiles = generator.GenerateForLocation([]policies.Policy{test.policy}, http.Location{})
166+
g.Expect(resFiles).To(HaveLen(1))
157167

158168
for _, str := range test.expStrings {
159-
g.Expect(cfgString).To(ContainSubstring(str))
169+
g.Expect(string(resFiles[0].Content)).To(ContainSubstring(str))
170+
}
171+
172+
resFiles = generator.GenerateForInternalLocation([]policies.Policy{test.policy})
173+
g.Expect(resFiles).To(HaveLen(1))
174+
175+
for _, str := range test.expStrings {
176+
g.Expect(string(resFiles[0].Content)).To(ContainSubstring(str))
160177
}
161178
})
162179
}
163180
}
164181

165-
func TestGeneratePanics(t *testing.T) {
182+
func TestGenerateNoPolicies(t *testing.T) {
166183
g := NewWithT(t)
167184

168-
generate := func() {
169-
clientsettings.Generate(&policiesfakes.FakePolicy{}, nil)
170-
}
185+
generator := clientsettings.NewGenerator()
186+
187+
resFiles := generator.GenerateForServer([]policies.Policy{}, http.Server{})
188+
g.Expect(resFiles).To(BeEmpty())
189+
190+
resFiles = generator.GenerateForServer([]policies.Policy{&ngfAPI.ObservabilityPolicy{}}, http.Server{})
191+
g.Expect(resFiles).To(BeEmpty())
192+
193+
resFiles = generator.GenerateForLocation([]policies.Policy{}, http.Location{})
194+
g.Expect(resFiles).To(BeEmpty())
195+
196+
resFiles = generator.GenerateForLocation([]policies.Policy{&ngfAPI.ObservabilityPolicy{}}, http.Location{})
197+
g.Expect(resFiles).To(BeEmpty())
198+
199+
resFiles = generator.GenerateForInternalLocation([]policies.Policy{})
200+
g.Expect(resFiles).To(BeEmpty())
171201

172-
g.Expect(generate).To(Panic())
202+
resFiles = generator.GenerateForInternalLocation([]policies.Policy{&ngfAPI.ObservabilityPolicy{}})
203+
g.Expect(resFiles).To(BeEmpty())
173204
}

internal/mode/static/policies/clientsettings/validator.go renamed to internal/mode/static/nginx/config/policies/clientsettings/validator.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/conditions"
99
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/helpers"
1010
"github.com/nginxinc/nginx-gateway-fabric/internal/framework/kinds"
11-
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/policies"
11+
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/nginx/config/policies"
1212
staticConds "github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/conditions"
1313
"github.com/nginxinc/nginx-gateway-fabric/internal/mode/static/state/validation"
1414
)

0 commit comments

Comments
 (0)