Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for IPv6 #2190

Merged
merged 28 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5afe069
add support for ipv6
salonichf5 Jul 2, 2024
1fe7bb1
Update apis/v1alpha1/nginxproxy_types.go
salonichf5 Jul 8, 2024
caafe33
updates based on reviews
salonichf5 Jul 9, 2024
6c96e94
edit kind cluster creation with dual stack
salonichf5 Jul 9, 2024
0a5ead0
fix fieldalignment
salonichf5 Jul 9, 2024
8a930d7
updates based on reviews
salonichf5 Jul 10, 2024
2f03c33
update unit test
salonichf5 Jul 10, 2024
a19793c
update endpoint verification based on NGINX IP Family
salonichf5 Jul 11, 2024
5600726
update resolver
salonichf5 Jul 11, 2024
ec86322
update nginx proxy validator for ipFamily
salonichf5 Jul 11, 2024
5f6f583
add conditions to service
salonichf5 Jul 12, 2024
5b38ff9
improve verify IPFamily
salonichf5 Jul 12, 2024
b3dcc2c
Update internal/mode/static/state/resolver/resolver_test.go
salonichf5 Jul 12, 2024
c804274
Update internal/mode/static/nginx/config/upstreams_test.go
salonichf5 Jul 12, 2024
a578387
Update internal/mode/static/nginx/config/servers.go
salonichf5 Jul 12, 2024
624fd81
Update internal/mode/static/state/dataplane/configuration_test.go
salonichf5 Jul 12, 2024
de05457
Update internal/mode/static/state/graph/backend_refs.go
salonichf5 Jul 12, 2024
fe42fa5
Update internal/mode/static/state/graph/backend_refs.go
salonichf5 Jul 12, 2024
82584b2
Update internal/mode/static/state/graph/backend_refs.go
salonichf5 Jul 12, 2024
931c613
Update internal/mode/static/state/graph/backend_refs_test.go
salonichf5 Jul 12, 2024
2d1938c
Update internal/mode/static/state/graph/backend_refs_test.go
salonichf5 Jul 12, 2024
efe7fa1
address comments
salonichf5 Jul 12, 2024
4175da4
update compat doc and troubleshooting guide
salonichf5 Jul 14, 2024
e3c1b57
update doc
salonichf5 Jul 15, 2024
2ea4db6
update troubleshooting guide
salonichf5 Jul 15, 2024
b52b90d
update based on reviews
salonichf5 Jul 15, 2024
9ef4868
move service name creation out of function
salonichf5 Jul 16, 2024
e802e82
address comments
salonichf5 Jul 16, 2024
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
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ MANIFEST_DIR = $(CURDIR)/deploy/manifests
CHART_DIR = $(SELF_DIR)charts/nginx-gateway-fabric
NGINX_CONF_DIR = internal/mode/static/nginx/conf
NJS_DIR = internal/mode/static/nginx/modules/src
KIND_CONFIG_FILE = $(SELF_DIR)config/cluster/kind-cluster.yaml
NGINX_DOCKER_BUILD_PLUS_ARGS = --secret id=nginx-repo.crt,src=nginx-repo.crt --secret id=nginx-repo.key,src=nginx-repo.key
BUILD_AGENT=local
PLUS_ENABLED ?= false
Expand Down Expand Up @@ -160,7 +161,7 @@ deps: ## Add missing and remove unused modules, verify deps and download them to
.PHONY: create-kind-cluster
create-kind-cluster: ## Create a kind cluster
$(eval KIND_IMAGE=$(shell grep -m1 'FROM kindest/node' <$(SELF_DIR)tests/Dockerfile | awk -F'[ ]' '{print $$2}'))
kind create cluster --image $(KIND_IMAGE)
kind create cluster --image $(KIND_IMAGE) --config $(KIND_CONFIG_FILE)

.PHONY: delete-kind-cluster
delete-kind-cluster: ## Delete kind cluster
Expand Down
20 changes: 20 additions & 0 deletions apis/v1alpha1/nginxproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,28 @@ type NginxProxyList struct {
Items []NginxProxy `json:"items"`
}

// IPFamilyType specifies the IP family to be used by NGINX.
//
// +kubebuilder:validation:Enum=dual;ipv4;ipv6
type IPFamilyType string

const (
// Dual specifies that NGINX will use both IPv4 and IPv6.
Dual IPFamilyType = "dual"
// IPv4 specifies that NGINX will use only IPv4.
IPv4 IPFamilyType = "ipv4"
// IPv6 specifies that NGINX will use only IPv6.
IPv6 IPFamilyType = "ipv6"
)

// NginxProxySpec defines the desired state of the NginxProxy.
type NginxProxySpec struct {
// IPFamily specifies the IP family to be used by the NGINX.
// Default is "dual", meaning the server will use both IPv4 and IPv6.
//
// +optional
// +kubebuilder:default:=dual
IPFamily *IPFamilyType `json:"ipFamily,omitempty"`
// Telemetry specifies the OpenTelemetry configuration.
//
// +optional
Expand Down
5 changes: 5 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.

1 change: 1 addition & 0 deletions charts/nginx-gateway-fabric/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ nginx:
config:
{}
# disableHTTP2: false
# ipFamily: dual
# telemetry:
# exporter:
# endpoint: otel-collector.default.svc:4317
Expand Down
7 changes: 7 additions & 0 deletions config/cluster/kind-cluster.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
networking:
ipFamily: dual
apiServerAddress: 127.0.0.1
10 changes: 10 additions & 0 deletions config/crd/bases/gateway.nginx.org_nginxproxies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ spec:
DisableHTTP2 defines if http2 should be disabled for all servers.
Default is false, meaning http2 will be enabled for all servers.
type: boolean
ipFamily:
default: dual
description: |-
IPFamily specifies the IP family to be used by the NGINX.
Default is "dual", meaning the server will use both IPv4 and IPv6.
enum:
- dual
- ipv4
- ipv6
type: string
telemetry:
description: Telemetry specifies the OpenTelemetry configuration.
properties:
Expand Down
10 changes: 10 additions & 0 deletions deploy/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,16 @@ spec:
DisableHTTP2 defines if http2 should be disabled for all servers.
Default is false, meaning http2 will be enabled for all servers.
type: boolean
ipFamily:
default: dual
description: |-
IPFamily specifies the IP family to be used by the NGINX.
Default is "dual", meaning the server will use both IPv4 and IPv6.
enum:
- dual
- ipv4
- ipv6
type: string
telemetry:
description: Telemetry specifies the OpenTelemetry configuration.
properties:
Expand Down
14 changes: 14 additions & 0 deletions docs/developer/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,24 @@ This will build the docker images `nginx-gateway-fabric:<your-user>` and `nginx-

1. Create a `kind` cluster:

To create a `kind` cluster with dual (IPv4 and IPv6) enabled:

```makefile
make create-kind-cluster
```

To create a `kind` cluster with IPv6 or IPv4 only, edit kind cluster config located at `nginx-gateway-fabric/config/cluster/kind-cluster.yaml`:

```yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
networking:
ipFamily: ipv6
apiServerAddress: 127.0.0.1
```

2. Load the previously built images onto your `kind` cluster:

```shell
Expand Down
12 changes: 12 additions & 0 deletions internal/mode/static/nginx/config/http/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ type Server struct {
GRPC bool
}

// IPFamily holds the IP family configuration to be used by NGINX.
type IPFamily struct {
IPv4 bool
IPv6 bool
}

// Location holds all configuration for an HTTP location.
type Location struct {
Path string
Expand Down Expand Up @@ -106,3 +112,9 @@ type ProxySSLVerify struct {
TrustedCertificate string
Name string
}

// ServerConfig holds configuration for an HTTP server and IP family to be used by NGINX.
type ServerConfig struct {
Servers []Server
IPFamily IPFamily
}
19 changes: 18 additions & 1 deletion internal/mode/static/nginx/config/servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,14 @@ var grpcBaseHeaders = []http.Header{
func executeServers(conf dataplane.Configuration) []executeResult {
servers, httpMatchPairs := createServers(conf.HTTPServers, conf.SSLServers)

serverConfig := http.ServerConfig{
Servers: servers,
IPFamily: getIPFamily(conf.BaseHTTPConfig),
}

serverResult := executeResult{
dest: httpConfigFile,
data: helpers.MustExecuteTemplate(serversTemplate, servers),
data: helpers.MustExecuteTemplate(serversTemplate, serverConfig),
}

// create httpMatchPair conf
Expand All @@ -86,6 +91,18 @@ func executeServers(conf dataplane.Configuration) []executeResult {
return allResults
}

// getIPFamily returns whether the server should be configured for IPv4, IPv6, or both.
func getIPFamily(baseHTTPConfig dataplane.BaseHTTPConfig) http.IPFamily {
switch baseHTTPConfig.IPFamily {
case dataplane.IPv4:
return http.IPFamily{IPv4: true}
case dataplane.IPv6:
return http.IPFamily{IPv6: true}
salonichf5 marked this conversation as resolved.
Show resolved Hide resolved
}

return http.IPFamily{IPv4: true, IPv6: true}
}

func createAdditionFileResults(conf dataplane.Configuration) []executeResult {
uniqueAdditions := make(map[string][]byte)

Expand Down
22 changes: 21 additions & 1 deletion internal/mode/static/nginx/config/servers_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,52 @@ package config

const serversTemplateText = `
js_preload_object matches from /etc/nginx/conf.d/matches.json;
{{- range $s := . -}}
{{- range $s := .Servers -}}
{{ if $s.IsDefaultSSL -}}
server {
{{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }} ssl default_server;
{{- end }}
{{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }} ssl default_server;
{{- end }}

ssl_reject_handshake on;
}
{{- else if $s.IsDefaultHTTP }}
server {
{{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }} default_server;
{{- end }}
{{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }} default_server;
{{- end }}

default_type text/html;
return 404;
}
{{- else }}
server {
{{- if $s.SSL }}
{{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }} ssl;
{{- end }}
{{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }} ssl;
{{- end }}
ssl_certificate {{ $s.SSL.Certificate }};
ssl_certificate_key {{ $s.SSL.CertificateKey }};

if ($ssl_server_name != $host) {
return 421;
}
{{- else }}
{{- if $.IPFamily.IPv4 }}
listen {{ $s.Port }};
{{- end }}
{{- if $.IPFamily.IPv6 }}
listen [::]:{{ $s.Port }};
{{- end }}
{{- end }}

server_name {{ $s.ServerName }};
Expand Down
143 changes: 143 additions & 0 deletions internal/mode/static/nginx/config/servers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,117 @@ func TestExecuteServers(t *testing.T) {
}
}

func TestExecuteServersForIPFamily(t *testing.T) {
httpServers := []dataplane.VirtualServer{
{
IsDefault: true,
Port: 8080,
},
{
Hostname: "example.com",
Port: 8080,
},
}
sslServers := []dataplane.VirtualServer{
{
IsDefault: true,
Port: 8443,
},
{
Hostname: "example.com",
SSL: &dataplane.SSL{
KeyPairID: "test-keypair",
},
Port: 8443,
},
}
tests := []struct {
msg string
expectedHTTPConfig map[string]int
config dataplane.Configuration
}{
{
msg: "http and ssl servers with IPv4 IP family",
config: dataplane.Configuration{
HTTPServers: httpServers,
SSLServers: sslServers,
BaseHTTPConfig: dataplane.BaseHTTPConfig{
IPFamily: dataplane.IPv4,
},
},
expectedHTTPConfig: map[string]int{
"listen 8080 default_server;": 1,
"listen 8080;": 1,
"listen 8443 ssl default_server;": 1,
"listen 8443 ssl;": 1,
"server_name example.com;": 2,
"ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 1,
"ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 1,
"ssl_reject_handshake on;": 1,
},
},
{
msg: "http and ssl servers with IPv6 IP family",
config: dataplane.Configuration{
HTTPServers: httpServers,
SSLServers: sslServers,
BaseHTTPConfig: dataplane.BaseHTTPConfig{
IPFamily: dataplane.IPv6,
},
},
expectedHTTPConfig: map[string]int{
"listen [::]:8080 default_server;": 1,
"listen [::]:8080;": 1,
"listen [::]:8443 ssl default_server;": 1,
"listen [::]:8443 ssl;": 1,
"server_name example.com;": 2,
"ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 1,
"ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 1,
"ssl_reject_handshake on;": 1,
},
},
{
msg: "http and ssl servers with Dual IP family",
config: dataplane.Configuration{
HTTPServers: httpServers,
SSLServers: sslServers,
BaseHTTPConfig: dataplane.BaseHTTPConfig{
IPFamily: dataplane.Dual,
},
},
expectedHTTPConfig: map[string]int{
"listen 8080 default_server;": 1,
"listen 8080;": 1,
"listen 8443 ssl default_server;": 1,
"listen 8443 ssl;": 1,
"server_name example.com;": 2,
"ssl_certificate /etc/nginx/secrets/test-keypair.pem;": 1,
"ssl_certificate_key /etc/nginx/secrets/test-keypair.pem;": 1,
"ssl_reject_handshake on;": 1,
"listen [::]:8080 default_server;": 1,
"listen [::]:8080;": 1,
"listen [::]:8443 ssl default_server;": 1,
"listen [::]:8443 ssl;": 1,
},
},
}

for _, test := range tests {
t.Run(test.msg, func(t *testing.T) {
g := NewWithT(t)
results := executeServers(test.config)
g.Expect(results).To(HaveLen(2))
serverConf := string(results[0].data)
httpMatchConf := string(results[1].data)
g.Expect(httpMatchConf).To(Equal("{}"))

for expSubStr, expCount := range test.expectedHTTPConfig {
g.Expect(strings.Count(serverConf, expSubStr)).To(Equal(expCount))
}
})
}
}

func TestExecuteForDefaultServers(t *testing.T) {
testcases := []struct {
msg string
Expand Down Expand Up @@ -2515,3 +2626,35 @@ func TestAdditionFilename(t *testing.T) {
name := createAdditionFileName(dataplane.Addition{Identifier: "my-addition"})
g.Expect(name).To(Equal(includesFolder + "/" + "my-addition.conf"))
}

func TestGetIPFamily(t *testing.T) {
test := []struct {
msg string
baseHTTPConfig dataplane.BaseHTTPConfig
expected http.IPFamily
}{
{
msg: "ipv4",
baseHTTPConfig: dataplane.BaseHTTPConfig{IPFamily: dataplane.IPv4},
expected: http.IPFamily{IPv4: true, IPv6: false},
},
{
msg: "ipv6",
baseHTTPConfig: dataplane.BaseHTTPConfig{IPFamily: dataplane.IPv6},
expected: http.IPFamily{IPv4: false, IPv6: true},
},
{
msg: "dual",
baseHTTPConfig: dataplane.BaseHTTPConfig{IPFamily: dataplane.Dual},
expected: http.IPFamily{IPv4: true, IPv6: true},
},
}

for _, tc := range test {
t.Run(tc.msg, func(t *testing.T) {
g := NewWithT(t)
result := getIPFamily(tc.baseHTTPConfig)
g.Expect(result).To(Equal(tc.expected))
})
}
}
Loading
Loading