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
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,28 @@ This information is used to manage AWS resources for each ingress objects of the
- Support for `ipv4` and `ipv6` target group IP address types for ALB and NLB
- set default target group IP address type using `--target-group-ip-address-type=ipv6`
- IPv6 targets require dualstack load balancers (`--ip-addr-type=dualstack`)
- Support for NLB target group attributes (Network Load Balancers only)
- Proxy Protocol v2: enable with `--nlb-proxy-protocol-v2` (default: false)
- Preserve Client IP: configure with `--nlb-preserve-client-ip` (default: true, matching AWS NLB default)

## Upgrade

### <v0.20 to >=v0.20

Version `v0.20` adds support for NLB target group attributes:

- **Proxy Protocol v2**: Enable with `--nlb-proxy-protocol-v2-enabled` flag (default: false, disabled)
- Enables Proxy Protocol v2 for Network Load Balancer target groups
- Only applies to NLBs; ALBs do not support this feature

- **Preserve Client IP**: Disable with `--nlb-preserve-client-ip-disabled` flag (default: false, enabled)
- Preserves client IP address in NLB target group connections
- Defaults to true (enabled), matching AWS NLB default behavior
- Set the flag to disable this feature
- **Breaking change**: Previously this attribute was not explicitly set. Updating to v0.20 will set `preserve_client_ip.enabled=true` on all NLB target groups. For setups that require it disabled, use `--nlb-preserve-client-ip-disabled` flag

These attributes are only applied to Network Load Balancer target groups. Application Load Balancers are not affected.

### <v0.19 to >=v0.19

Version `v0.19` adds support for IPv6 target group IP address type. When using IPv6 targets, ensure your load balancer is configured as dualstack (`--ip-addr-type=dualstack` or `alb.ingress.kubernetes.io/ip-address-type: dualstack`). IPv4-only load balancers cannot route to IPv6 targets and will fail with a clear error message.
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.19
v0.20
28 changes: 25 additions & 3 deletions aws/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ type Adapter struct {
httpRedirectToHTTPS bool
nlbCrossZone bool
nlbHTTPEnabled bool
nlbProxyProtocolV2Enabled bool
nlbPreserveClientIPEnabled bool
customFilter string
internalDomains []string
denyInternalDomains bool
Expand Down Expand Up @@ -135,9 +137,11 @@ const (
// DefaultNLBCrossZone specifies the default configuration for cross
// zone load balancing: https://docs.aws.amazon.com/elasticloadbalancing/latest/network/network-load-balancers.html#load-balancer-attributes
// It it is safe to change as per https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-elasticloadbalancingv2-loadbalancer-loadbalancerattribute.html#aws-properties-elasticloadbalancingv2-loadbalancer-loadbalancerattribute-properties
DefaultNLBCrossZone = false
DefaultNLBHTTPEnabled = false
DefaultTargetGroupProtocolVersion = "HTTP1"
DefaultNLBCrossZone = false
DefaultNLBHTTPEnabled = false
DefaultTargetGroupProtocolVersion = "HTTP1"
DefaultNLBProxyProtocolV2Enabled = false
DefaultNLBPreserveClientIPEnabled = true

nameTag = "Name"
LoadBalancerTypeApplication = "application"
Expand Down Expand Up @@ -498,6 +502,20 @@ func (a *Adapter) WithNLBHTTPEnabled(nlbHTTPEnabled bool) *Adapter {
return a
}

// WithNLBProxyProtocolV2 returns the receiver adapter after setting the
// nlbProxyProtocolV2Enabled config.
func (a *Adapter) WithNLBProxyProtocolV2(nlbProxyProtocolV2Enabled bool) *Adapter {
a.nlbProxyProtocolV2Enabled = nlbProxyProtocolV2Enabled
return a
}

// WithNLBPreserveClientIP returns the receiver adapter after setting the
// nlbPreserveClientIPEnabled config.
func (a *Adapter) WithNLBPreserveClientIP(nlbPreserveClientIPEnabled bool) *Adapter {
a.nlbPreserveClientIPEnabled = nlbPreserveClientIPEnabled
return a
}

// WithCustomFilter returns the receiver adapter after setting a custom filter expression
func (a *Adapter) WithCustomFilter(customFilter string) *Adapter {
a.customFilter = customFilter
Expand Down Expand Up @@ -851,6 +869,8 @@ func (a *Adapter) CreateStack(ctx context.Context, certificateARNs []string, sch
httpRedirectToHTTPS: a.httpRedirectToHTTPS,
nlbCrossZone: a.nlbCrossZone,
nlbZoneAffinity: a.nlbZoneAffinity,
nlbProxyProtocolV2Enabled: a.nlbProxyProtocolV2Enabled,
nlbPreserveClientIPEnabled: a.nlbPreserveClientIPEnabled,
http2: http2,
tags: a.stackTags,
internalDomains: a.internalDomains,
Expand Down Expand Up @@ -914,6 +934,8 @@ func (a *Adapter) UpdateStack(ctx context.Context, stackName string, certificate
httpRedirectToHTTPS: a.httpRedirectToHTTPS,
nlbCrossZone: a.nlbCrossZone,
nlbZoneAffinity: a.nlbZoneAffinity,
nlbProxyProtocolV2Enabled: a.nlbProxyProtocolV2Enabled,
nlbPreserveClientIPEnabled: a.nlbPreserveClientIPEnabled,
http2: http2,
tags: a.stackTags,
internalDomains: a.internalDomains,
Expand Down
2 changes: 2 additions & 0 deletions aws/cf.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ type stackSpec struct {
albLogsS3Prefix string
wafWebAclId string
nlbZoneAffinity string
nlbProxyProtocolV2Enabled bool
nlbPreserveClientIPEnabled bool
cwAlarms CloudWatchAlarmList
httpRedirectToHTTPS bool
nlbCrossZone bool
Expand Down
27 changes: 21 additions & 6 deletions aws/cf_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -490,13 +490,28 @@ func newTargetGroup(spec *stackSpec, targetPortParameter string) *cloudformation
protocolVersion = cloudformation.String(spec.targetGroupProtocolVersion)
}

targetGroup := &cloudformation.ElasticLoadBalancingV2TargetGroup{
TargetGroupAttributes: &cloudformation.ElasticLoadBalancingV2TargetGroupTargetGroupAttributeList{
{
Key: cloudformation.String("deregistration_delay.timeout_seconds"),
Value: cloudformation.String(fmt.Sprintf("%d", spec.deregistrationDelayTimeoutSeconds)),
},
// Build target group attributes
attrsList := cloudformation.ElasticLoadBalancingV2TargetGroupTargetGroupAttributeList{
{
Key: cloudformation.String("deregistration_delay.timeout_seconds"),
Value: cloudformation.String(fmt.Sprintf("%d", spec.deregistrationDelayTimeoutSeconds)),
},
}

// Add NLB-specific attributes
if spec.loadbalancerType == LoadBalancerTypeNetwork {
attrsList = append(attrsList, cloudformation.ElasticLoadBalancingV2TargetGroupTargetGroupAttribute{
Key: cloudformation.String("proxy_protocol_v2.enabled"),
Value: cloudformation.String(fmt.Sprintf("%v", spec.nlbProxyProtocolV2Enabled)),
})
attrsList = append(attrsList, cloudformation.ElasticLoadBalancingV2TargetGroupTargetGroupAttribute{
Key: cloudformation.String("preserve_client_ip.enabled"),
Value: cloudformation.String(fmt.Sprintf("%v", spec.nlbPreserveClientIPEnabled)),
})
}

targetGroup := &cloudformation.ElasticLoadBalancingV2TargetGroup{
TargetGroupAttributes: &attrsList,
HealthCheckIntervalSeconds: cloudformation.Ref(parameterTargetGroupHealthCheckIntervalParameter).Integer(),
HealthCheckPath: cloudformation.Ref(parameterTargetGroupHealthCheckPathParameter).String(),
HealthCheckPort: cloudformation.Ref(parameterTargetGroupHealthCheckPortParameter).String(),
Expand Down
66 changes: 66 additions & 0 deletions aws/cf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -932,3 +932,69 @@ func TestShouldDelete(t *testing.T) {
}

}

func TestNLBTargetGroupAttributes(t *testing.T) {
for _, ti := range []struct {
name string
givenSpec stackSpec
expectedAttrCount int
}{
{
name: "ALB-with-nlb-flags-ignored",
givenSpec: stackSpec{
loadbalancerType: LoadBalancerTypeApplication,
nlbProxyProtocolV2Enabled: true,
nlbPreserveClientIPEnabled: true,
deregistrationDelayTimeoutSeconds: 30,
},
expectedAttrCount: 1, // only deregistration delay
},
{
name: "NLB-with-proxy-protocol-v2",
givenSpec: stackSpec{
loadbalancerType: LoadBalancerTypeNetwork,
nlbProxyProtocolV2Enabled: true,
nlbPreserveClientIPEnabled: false,
deregistrationDelayTimeoutSeconds: 30,
},
expectedAttrCount: 3, // deregistration delay + proxy protocol v2 + preserve client ip
},
{
name: "NLB-with-preserve-client-ip",
givenSpec: stackSpec{
loadbalancerType: LoadBalancerTypeNetwork,
nlbProxyProtocolV2Enabled: false,
nlbPreserveClientIPEnabled: true,
deregistrationDelayTimeoutSeconds: 30,
},
expectedAttrCount: 3, // deregistration delay + proxy protocol v2 + preserve client ip
},
{
name: "NLB-with-both-attributes",
givenSpec: stackSpec{
loadbalancerType: LoadBalancerTypeNetwork,
nlbProxyProtocolV2Enabled: true,
nlbPreserveClientIPEnabled: true,
deregistrationDelayTimeoutSeconds: 30,
},
expectedAttrCount: 3, // deregistration delay + both NLB attributes
},
{
name: "NLB-with-no-extra-attributes",
givenSpec: stackSpec{
loadbalancerType: LoadBalancerTypeNetwork,
nlbProxyProtocolV2Enabled: false,
nlbPreserveClientIPEnabled: false,
deregistrationDelayTimeoutSeconds: 30,
},
expectedAttrCount: 3, // deregistration delay + both NLB attributes (set to false)
},
} {
t.Run(ti.name, func(t *testing.T) {
tg := newTargetGroup(&ti.givenSpec, "TargetPort")
assert.NotNil(t, tg)
assert.NotNil(t, tg.TargetGroupAttributes)
assert.Equal(t, ti.expectedAttrCount, len(*tg.TargetGroupAttributes), "target group attributes count mismatch")
})
}
}
8 changes: 8 additions & 0 deletions controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ var (
nlbZoneAffinity string
nlbCrossZone bool
nlbHTTPEnabled bool
nlbProxyProtocolV2Enabled bool
nlbPreserveClientIPDisabled bool
ingressAPIVersion string
internalDomains []string
targetAccessMode string
Expand Down Expand Up @@ -184,6 +186,10 @@ func loadSettings() error {
Default("false").BoolVar(&nlbCrossZone)
kingpin.Flag("nlb-http-enabled", "Enable HTTP (port 80) for Network Load Balancers. By default this is disabled as NLB can't provide HTTP -> HTTPS redirect.").
Default("false").BoolVar(&nlbHTTPEnabled)
kingpin.Flag("nlb-proxy-protocol-v2-enabled", "Enable Proxy Protocol v2 for Network Load Balancers. By default this is disabled. This setting only applies to 'network' Load Balancers.").
Default("false").BoolVar(&nlbProxyProtocolV2Enabled)
kingpin.Flag("nlb-preserve-client-ip-disabled", "Disable preserve client IP address for Network Load Balancers. By default this is enabled. This setting only applies to 'network' Load Balancers.").
Default("false").BoolVar(&nlbPreserveClientIPDisabled)
kingpin.Flag("deny-internal-domains", "Sets a rule on ALB's Listeners that denies requests with the Host header as a internal domain. Domains can be set with the -internal-domains flag.").
Default("false").BoolVar(&denyInternalDomains)
kingpin.Flag("internal-domains", "Define the internal domains to be blocked when -deny-internal-domains is set to true. Set it multiple times for multiple domains. The maximum size of each name is 128 characters. The following wildcard characters are supported: * (matches 0 or more characters) and ? (matches exactly 1 character).").
Expand Down Expand Up @@ -349,6 +355,8 @@ func main() {
WithNLBCrossZone(nlbCrossZone).
WithNLBZoneAffinity(nlbZoneAffinity).
WithNLBHTTPEnabled(nlbHTTPEnabled).
WithNLBProxyProtocolV2(nlbProxyProtocolV2Enabled).
WithNLBPreserveClientIP(!nlbPreserveClientIPDisabled).
WithCustomFilter(customFilter).
WithStackTags(additionalStackTags).
WithInternalDomains(internalDomains).
Expand Down
30 changes: 15 additions & 15 deletions kubernetes/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,22 +88,22 @@ var (
// Ingress is the ingress-controller's business object. It is used to
// store Kubernetes ingress and routegroup resources.
type Ingress struct {
ResourceType IngressType
Namespace string
Name string
Shared bool
HTTP2 bool
ResourceType IngressType
Namespace string
Name string
Shared bool
HTTP2 bool
ClusterLocal bool
CertificateARN string
Hostname string
Scheme string
SecurityGroup string
SSLPolicy string
HasSSLPolicyAnnotation bool
IPAddressType string
LoadBalancerType string
WAFWebACLID string
Hostnames []string
CertificateARN string
Hostname string
Scheme string
SecurityGroup string
SSLPolicy string
HasSSLPolicyAnnotation bool
IPAddressType string
LoadBalancerType string
WAFWebACLID string
Hostnames []string
}

// String returns a string representation of the Ingress instance containing the type, namespace and the resource name.
Expand Down
8 changes: 4 additions & 4 deletions kubernetes/adapter_ssl_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ func TestHasSSLPolicyAnnotation(t *testing.T) {
customSSLPolicy := "ELBSecurityPolicy-TLS-1-2-2017-01"

tests := []struct {
name string
annotations map[string]string
expectedSSLPolicy string
expectedHasAnnotation bool
name string
annotations map[string]string
expectedSSLPolicy string
expectedHasAnnotation bool
}{
{
name: "no SSL policy annotation - uses default",
Expand Down
Loading
Loading