Proxy Protocol iRule support for L4 routes

Overview

F5 BIG-IP Next for Kubernetes supports Proxy Protocol iRules for Layer 4 (L4) routes. This lets you pass client connection information — such as IP addresses and port numbers — to backend servers when traffic passes through a load balancer.

This feature supports both TCP and UDP protocols and uses the Kubernetes Gateway API.


Before you begin

Before you configure Proxy Protocol iRule support, make sure you have:

  • A working BIG-IP Next for Kubernetes deployment

  • Access to the Kubernetes Gateway API

  • Permissions to create and apply Kubernetes resources in your cluster


How it works

TCP routes

By default, BIG-IP Next for Kubernetes creates a FastL4 profile for TCP L4 routes. FastL4 does not support the TCP::respond iRule command in the SERVER_CONNECTED event that Proxy Protocol iRules require.

When you attach a Proxy Protocol iRule to a TCP L4 route, BIG-IP Next for Kubernetes checks the iRule for the TCP::respond command.

Note: If the TCP::respond command is present and the acceleration mode is set to disabled, BIG-IP Next for Kubernetes switches the virtual server from FastL4 to a standard TCP profile. This lets the iRule run as expected.

If the TCP::respond command is not present, BIG-IP Next for Kubernetes applies a FastL4 profile instead.

When you attach more than one iRule to a route, BIG-IP Next for Kubernetes treats the route as standard if any of the iRules contain the TCP::respond command.

UDP routes

Proxy Protocol iRules work with UDP L4 routes without any changes. BIG-IP Next for Kubernetes creates a standard UDP virtual server, which supports the iRule as expected.

Configure Proxy Protocol iRule support for L4 routes

The steps below show you how to set up Proxy Protocol iRule support for TCP and UDP L4 routes.

Step 1: Create the iRule

Create an F5BigCneIrule resource that contains the Proxy Protocol iRule logic.

apiVersion: k8s.f5net.com/v1
kind: F5BigCneIrule
metadata:
  name: proxy-irule
  namespace: spk-app
spec:
  iRule: |
    # Proxy Protocol iRule
    # v1.0 - 21/02/2023
    proc encode { version protocol src_ip dst_ip src_port dst_port} {
      if {$version eq 4} {
        # block containing the protocol signature : 12 bytes
        # source IPv6 address : 32 bytes
        # Destination IPv6 address : 32 bytes
        # add extra 0 to prevent issues with :: at the beginning or end of address
        set count [expr {8 - [string length $ipv6_compressed] + \
          [string length [string map {: {}} $ipv6_compressed]]}]
        set ipv6_temp "0[string map [list :: :[string repeat 0:0 $count]] $ipv6_compressed]"
        # Destination port : 2 bytes
        switch -- $protocol {
          6 {
            append proxy_addr [binary format SS $src_port $dst_port]
            set fam_proto "[expr {$version == 4 ? 1 : 2}]1"
          }
          17 {
            append proxy_addr [binary format SS $src_port $dst_port]
            set fam_proto "[expr {$version == 4 ? 1 : 2}]2"
          }
          default {
            set fam_proto "[expr {$version == 4 ? 1 : 2}]0"
          }
        }
      }
    }
    when CLIENT_ACCEPTED {
      if { [IP::protocol] == 6 } {
        set proxy_hdr_v2 [call encode [IP::version] [IP::protocol] \
            [IP::remote_addr] [IP::local_addr] [TCP::remote_port] [TCP::local_port]]
      } else {
        set proxy_hdr_v2 [call encode [IP::version] [IP::protocol] \
            [IP::remote_addr] [IP::local_addr] [UDP::remote_port] [UDP::local_port]]
      }
    }
    when SERVER_CONNECTED {
      if { [IP::protocol] == 6 } {
        TCP::respond $proxy_hdr_v2
      } else {
        UDP::respond $proxy_hdr_v2
      }
    }

Step 2: Create the Gateway

Create a Gateway resource that defines the entry point for your traffic.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: tcp-gw
  namespace: spk-app
spec:
  addresses:
    - type: IPAddress
      value: 10.151.0.151
    - type: IPAddress
      value: 10.152.0.152
    - type: IPAddress
      value: 10.153.0.153
  gatewayClassName: gateway-class
  listeners:
    - allowedRoutes:
        kinds:
          - kind: L4Route
        namespaces:
          from: Same
      name: ubuntu
      port: 8000
      protocol: TCP

Step 3: Create the L4 route

Create an L4Route resource that links your backend service to the Gateway.

apiVersion: gateway.networking.k8s.io/v1
kind: L4Route
metadata:
  name: ubuntu
  namespace: spk-app
spec:
  protocol: TCP
  pvaAccelerationMode: "disabled"
  parentRefs:
    - name: tcp-gw
      sectionName: ubuntu
  rules:
    - backendRefs:
        - name: ubuntu
          port: 80

Step 4: Apply the policy

Create a NetPolicy resource to attach the iRule to the route.

apiVersion: gateway.k8s.f5.com/v1alpha1
kind: NetPolicy
metadata:
  name: proxy-net-policy
  namespace: spk-app
spec:
  extensionRefs:
    - name: proxy-irule
  targetRefs:
    - name: tcp-gw
      sectionName: ubuntu

Step 5: Create the backend service

Create a Service resource for your backend application.

apiVersion: v1
kind: Service
metadata:
  labels:
    app: ubuntu
  name: ubuntu
  namespace: spk-app
spec:
  internalTrafficPolicy: Cluster
  ipFamilies:
    - IPv4
  ipFamilyPolicy: SingleStack
  ports:
    - name: tcp
      port: 80
      protocol: TCP
      targetPort: 80
  selector:
    app: ubuntu
  sessionAffinity: None
  type: ClusterIP

Step 6: Deploy the backend application

Create a Deployment resource for your backend application.

Note: This code is only a sample of the application deployment. You must already have an application with the proxy protocol option enabled for deployment.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ubuntu
spec:
  selector:
    matchLabels:
      app: ubuntu
  replicas: 1
  template:
    metadata:
      labels:
        app: ubuntu
    spec:
      containers:
        - name: ubuntu
          image: ubuntu:latest
          ports:
            - containerPort: 80
          command:
            - "/bin/bash"
            - "-c"
          args:
            - sysctl -w net.ipv6.conf.all.disable_ipv6=1 &&
              sysctl -w net.ipv6.conf.default.disable_ipv6=1 &&
              sysctl -w net.ipv6.conf.lo.disable_ipv6=1 &&
              apt update &&
              apt install -y iproute2 iputils-ping tcpdump net-tools lighttpd netcat-openbsd curl wget &&
              service lighttpd start &&
              sleep 365d
          securityContext:
            privileged: true

Note: Proxy Protocol is not enabled by default on lighttpd. You must update your deployment manually to enable Proxy Protocol on lighttpd. Following is an example of updates to be made to the lighttpd.conf file to enable proxy protocol:

server.modules += ( "mod_accesslog", )
accesslog.filename = "/var/log/lighttpd/access.log"

server.modules += ( "mod_extforward" )
extforward.hap-PROXY = "enable"
extforward.forwarder = ( "all" => "trust" )
# "proxy" indicates that the trusted proxy sends a PROXY protocol header

Verify your configuration

After you apply all resources, verify that your configuration works as expected:

  1. Check that all resources are running in the spk-app namespace.

  2. Send test traffic to the Gateway IP address on port 8000.

  3. Confirm that the backend server receives the Proxy Protocol header with the correct client IP address and port.