Skip to content

Commit

Permalink
Support IP and Hostname trusted addresses (#2619)
Browse files Browse the repository at this point in the history
Problem: Our initial implementation of rewriting client IP only supported CIDRs for the trusted addresses field.

Solution: Add support for IP addresses and hostnames
  • Loading branch information
sjberman authored Sep 30, 2024
1 parent d6a478d commit f24cca7
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 97 deletions.
57 changes: 28 additions & 29 deletions apis/v1alpha1/nginxproxy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,6 @@ 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.
Expand Down Expand Up @@ -154,11 +140,11 @@ type RewriteClientIP struct {
// If no addresses are provided, NGINX will not rewrite the client IP information.
// Sets NGINX directive set_real_ip_from: https://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from
// This field is required if mode is set.
// +kubebuilder:validation:MaxItems=16
// +listType=map
// +listMapKey=type
//
// +optional
// +listType=map
// +listMapKey=type
// +kubebuilder:validation:MaxItems=16
TrustedAddresses []Address `json:"trustedAddresses,omitempty"`
}

Expand All @@ -179,27 +165,40 @@ const (
RewriteClientIPModeXForwardedFor RewriteClientIPModeType = "XForwardedFor"
)

// 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"
)

// Address is a struct that specifies address type and value.
type Address struct {
// Type specifies the type of address.
// Default is "cidr" which specifies that the address is a CIDR block.
//
// +optional
// +kubebuilder:default:=cidr
Type AddressType `json:"type,omitempty"`
Type AddressType `json:"type"`

// Value specifies the address value.
//
// +optional
Value string `json:"value,omitempty"`
Value string `json:"value"`
}

// AddressType specifies the type of address.
// +kubebuilder:validation:Enum=cidr
// +kubebuilder:validation:Enum=CIDR;IPAddress;Hostname
type AddressType string

const (
// AddressTypeCIDR specifies that the address is a CIDR block.
// kubebuilder:validation:Pattern=`^[\.a-zA-Z0-9:]*(\/([0-9]?[0-9]?[0-9]))$`
AddressTypeCIDR AddressType = "cidr"
// CIDRAddressType specifies that the address is a CIDR block.
CIDRAddressType AddressType = "CIDR"

// IPAddressType specifies that the address is an IP address.
IPAddressType AddressType = "IPAddress"

// HostnameAddressType specifies that the address is a Hostname.
HostnameAddressType AddressType = "Hostname"
)
41 changes: 41 additions & 0 deletions charts/nginx-gateway-fabric/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,47 @@
"required": [],
"type": "string"
},
"rewriteClientIP": {
"description": "RewriteClientIP defines configuration for rewriting the client IP to the original client's IP.",
"properties": {
"mode": {
"enum": [
"ProxyProtocol",
"XForwardedFor"
],
"required": [],
"type": "string"
},
"setIPRecursively": {
"required": [],
"type": "boolean"
},
"trustedAddresses": {
"items": {
"properties": {
"type": {
"enum": [
"CIDR",
"IPAddress",
"Hostname"
],
"required": [],
"type": "string"
},
"value": {
"required": [],
"type": "string"
}
},
"required": []
},
"required": [],
"type": "array"
}
},
"required": [],
"type": "object"
},
"telemetry": {
"description": "Telemetry specifies the OpenTelemetry configuration.",
"properties": {
Expand Down
23 changes: 23 additions & 0 deletions charts/nginx-gateway-fabric/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,29 @@ nginx:
# - ipv4
# - ipv6
# - dual
# rewriteClientIP:
# type: object
# description: RewriteClientIP defines configuration for rewriting the client IP to the original client's IP.
# properties:
# mode:
# type: string
# enum:
# - ProxyProtocol
# - XForwardedFor
# setIPRecursively:
# type: boolean
# trustedAddresses:
# type: array
# items:
# properties:
# type:
# type: string
# enum:
# - CIDR
# - IPAddress
# - Hostname
# value:
# type: string
# telemetry:
# type: object
# description: Telemetry specifies the OpenTelemetry configuration.
Expand Down
12 changes: 7 additions & 5 deletions config/crd/bases/gateway.nginx.org_nginxproxies.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,18 @@ spec:
and value.
properties:
type:
default: cidr
description: |-
Type specifies the type of address.
Default is "cidr" which specifies that the address is a CIDR block.
description: Type specifies the type of address.
enum:
- cidr
- CIDR
- IPAddress
- Hostname
type: string
value:
description: Value specifies the address value.
type: string
required:
- type
- value
type: object
maxItems: 16
type: array
Expand Down
12 changes: 7 additions & 5 deletions deploy/crds.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -690,16 +690,18 @@ spec:
and value.
properties:
type:
default: cidr
description: |-
Type specifies the type of address.
Default is "cidr" which specifies that the address is a CIDR block.
description: Type specifies the type of address.
enum:
- cidr
- CIDR
- IPAddress
- Hostname
type: string
value:
description: Value specifies the address value.
type: string
required:
- type
- value
type: object
maxItems: 16
type: array
Expand Down
14 changes: 7 additions & 7 deletions internal/mode/static/state/dataplane/configuration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2201,7 +2201,7 @@ func TestBuildConfiguration(t *testing.T) {
SetIPRecursively: helpers.GetPointer(true),
TrustedAddresses: []ngfAPI.Address{
{
Type: ngfAPI.AddressTypeCIDR,
Type: ngfAPI.CIDRAddressType,
Value: "1.1.1.1/32",
},
},
Expand Down Expand Up @@ -3651,7 +3651,7 @@ func TestBuildRewriteIPSettings(t *testing.T) {
Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeProxyProtocol),
TrustedAddresses: []ngfAPI.Address{
{
Type: ngfAPI.AddressTypeCIDR,
Type: ngfAPI.CIDRAddressType,
Value: "10.9.9.4/32",
},
},
Expand All @@ -3678,7 +3678,7 @@ func TestBuildRewriteIPSettings(t *testing.T) {
Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeXForwardedFor),
TrustedAddresses: []ngfAPI.Address{
{
Type: ngfAPI.AddressTypeCIDR,
Type: ngfAPI.CIDRAddressType,
Value: "76.89.90.11/24",
},
},
Expand All @@ -3705,19 +3705,19 @@ func TestBuildRewriteIPSettings(t *testing.T) {
Mode: helpers.GetPointer(ngfAPI.RewriteClientIPModeXForwardedFor),
TrustedAddresses: []ngfAPI.Address{
{
Type: ngfAPI.AddressTypeCIDR,
Type: ngfAPI.CIDRAddressType,
Value: "5.5.5.5/12",
},
{
Type: ngfAPI.AddressTypeCIDR,
Type: ngfAPI.CIDRAddressType,
Value: "1.1.1.1/26",
},
{
Type: ngfAPI.AddressTypeCIDR,
Type: ngfAPI.CIDRAddressType,
Value: "2.2.2.2/32",
},
{
Type: ngfAPI.AddressTypeCIDR,
Type: ngfAPI.CIDRAddressType,
Value: "3.3.3.3/24",
},
},
Expand Down
30 changes: 20 additions & 10 deletions internal/mode/static/state/graph/nginxproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,23 +172,33 @@ func validateRewriteClientIP(npCfg *ngfAPI.NginxProxy) field.ErrorList {
}

for _, addr := range rewriteClientIP.TrustedAddresses {
valuePath := trustedAddressesPath.Child("value")

switch addr.Type {
case ngfAPI.AddressTypeCIDR:
if err := k8svalidation.IsValidCIDR(trustedAddressesPath, addr.Value); err != nil {
allErrs = append(
allErrs,
field.Invalid(trustedAddressesPath.Child(addr.Value),
addr,
err.ToAggregate().Error(),
),
)
case ngfAPI.CIDRAddressType:
if err := k8svalidation.IsValidCIDR(valuePath, addr.Value); err != nil {
allErrs = append(allErrs, err...)
}
case ngfAPI.IPAddressType:
if err := k8svalidation.IsValidIP(valuePath, addr.Value); err != nil {
allErrs = append(allErrs, err...)
}
case ngfAPI.HostnameAddressType:
if errs := k8svalidation.IsDNS1123Subdomain(addr.Value); len(errs) > 0 {
for _, e := range errs {
allErrs = append(allErrs, field.Invalid(valuePath, addr.Value, e))
}
}
default:
allErrs = append(
allErrs,
field.NotSupported(trustedAddressesPath.Child("type"),
addr.Type,
[]string{string(ngfAPI.AddressTypeCIDR)},
[]string{
string(ngfAPI.CIDRAddressType),
string(ngfAPI.IPAddressType),
string(ngfAPI.HostnameAddressType),
},
),
)
}
Expand Down
Loading

0 comments on commit f24cca7

Please sign in to comment.