Forum Discussion

Chris_Atkins's avatar
Chris_Atkins
Icon for Nimbostratus rankNimbostratus
Feb 13, 2020

Questions about proxy protocol iRule

So we have a proxy in front of our f5, which is incapable of using x-forwarding-for to give us the original source IP. We were looking at this proxy protocol iRule to resolve that, as they can do that.

 

#PROXY Protocol Receiver iRule

# c.jenison at f5.com (Chad Jenison)

# v2.0 - Added support for PROXY Protocol v2, control for v1,v2 or lack of proxy via static:: variables set in RULE_INIT

when RULE_INIT {

  set static::allowProxyV1 1

  set static::allowProxyV2 1

  set static::allowNoProxy 0

}

 

when CLIENT_ACCEPTED {

  TCP::collect

}

 

when CLIENT_DATA {

  binary scan [TCP::payload 12] H* v2_protocol_sig

  if {$static::allowProxyV1 && [TCP::payload 0 5] eq "PROXY"} {

    set proxy_string [TCP::payload]

    set proxy_string_length [expr {[string first "\r" [TCP::payload]] + 2}]

    scan $proxy_string {PROXY TCP%s%s%s%s%s} tcpver srcaddr dstaddr srcport dstport

    log "Proxy Protocol v1 conn from [IP::client_addr]:[TCP::client_port] for an IPv$tcpver stream from Src: $srcaddr:$srcport to Dst: $dstaddr:$dstport"

    TCP::payload replace 0 $proxy_string_length ""

  } elseif {$static::allowProxyV2 && $v2_protocol_sig eq "0d0a0d0a000d0a515549540a"}{

    binary scan [TCP::payload] @12H* v2_proxyheaderremainder

    binary scan [TCP::payload] @12H2H* v2_verCommand v2_remainder

    if {$v2_verCommand == 21}{

      binary scan [TCP::payload] @13H2 v2_addressFamilyTransportProtocol

      if {$v2_addressFamilyTransportProtocol == 11} {

        binary scan [TCP::payload] @16ccccccccSS v2_sourceAddress1 v2_sourceAddress2 v2_sourceAddress3 v2_sourceAddress4 v2_destAddress1 v2_destAddress2 v2_destAddress3 v2_destAddress4 v2_sourcePort1 v2_destPort1

        set v2_sourceAddress "[expr {$v2_sourceAddress1 & 0xff}].[expr {$v2_sourceAddress2 & 0xff}].[expr {$v2_sourceAddress3 & 0xff}].[expr {$v2_sourceAddress4 & 0xff}]"

        set v2_destAddress "[expr {$v2_destAddress1 & 0xff}].[expr {$v2_destAddress2 & 0xff}].[expr {$v2_destAddress3 & 0xff}].[expr {$v2_destAddress4 & 0xff}]"

        set v2_sourcePort [expr {$v2_sourcePort1 & 0xffff}]

        set v2_destPort [expr {$v2_destPort1 & 0xffff}]

        log "Proxy Protocol v2 conn from [IP::client_addr]:[TCP::client_port] for an IPv4 Stream from Src: $v2_sourceAddress:$v2_sourcePort to Dst: $v2_destAddress:$v2_destPort"

        TCP::payload replace 0 28 ""

      } elseif {$v2_addressFamilyTransportProtocol == 21} {

        binary scan [TCP::payload] @16H4H4H4H4H4H4H4H v2_v6sourceAddress1 v2_v6sourceAddress2 v2_v6sourceAddress3 v2_v6sourceAddress4 v2_v6sourceAddress5 v2_v6sourceAddress6 v2_v6sourceAddress7 v2_v6sourceAddress8

        binary scan [TCP::payload] @32H4H4H4H4H4H4H4H v2_v6destAddress1 v2_v6destAddress2 v2_v6destAddress3 v2_v6destAddress4 v2_v6destAddress5 v2_v6destAddress6 v2_v6destAddress7 v2_v6destAddress8

        binary scan [TCP::payload] @48SS v2_v6sourcePort1 v2_v6destPort1

        set v2_v6sourcePort [expr {$v2_v6sourcePort1 & 0xffff}]

        set v2_v6destPort [expr {$v2_v6destPort1 & 0xffff}]

        set v2_v6sourceAddress "$v2_v6sourceAddress1:$v2_v6sourceAddress2:$v2_v6sourceAddress3:$v2_v6sourceAddress4:$v2_v6sourceAddress5:$v2_v6sourceAddress6:$v2_v6sourceAddress7:$v2_v6sourceAddress8"

        set v2_v6destAddress "$v2_v6destAddress1:$v2_v6destAddress2:$v2_v6destAddress3:$v2_v6destAddress4:$v2_v6destAddress5:$v2_v6destAddress6:$v2_v6destAddress7:$v2_v6destAddress8"

        log "Proxy Protocol v2 conn from from [IP::client_addr]:[TCP::client_port] for an IPv6 Stream from Src: $v2_v6sourceAddress:$v2_v6sourcePort to Dst: $v2_v6destAddress:$v2_v6destPort"

        TCP::payload replace 0 52 ""

      } else {

        log "v2_proxy conn from [IP::client_addr]:[TCP::client_port] - possible unknown/malformed transportProtocol or addressFamily"

        reject

      }

    } elseif {$v2_verCommand == 20}{

      log "Proxy Protocol v2 and LOCAL command from [IP::client_addr]:[TCP::client_port]; currently unhandled; connection reset"

      reject

    } else {

      log "Proxy Protocol Protocol Signature Detected from [IP::client_addr]:[TCP::client_port] but protocol version and command not legal; connection reset"

      reject

    }

  } elseif {$static::allowNoProxy} {

    log "Connection from [IP::client_addr]:[TCP::client_port] allowed despite lack of PROXY protocol header"

  } else {

    reject

    log "Connection rejected from [IP::client_addr]:[TCP::client_port] due to lack of PROXY protocol header"

  }

  TCP::release

}

 

 

I included the whole rule but likely just need ProxyV1. What I need to know is

-does this replace the source IP for the packet before nat or after

-does this take effect before the http profile does, and could then apply x-forwarded-for to the server side traffic

-if someone has a graphic of what order iRules/different virtual server settings are applied that would be helpful as well

 

Thanks

1 Reply

  • > What I need to know is

    > -does this replace the source IP for the packet before nat or after

    This irule extracts the information from the PROXY protocol line (v1 or V2) and then deletes the PROXY protocol line from the request (at the TCP Data stage). It does not modify the source/destination address in any way.

    > -does this take effect before the http profile does, and could then apply x-forwarded-for to the server side traffic

    Yes - CLIENT_DATA events occur before the HTTP profile.

    You can insert a X-Forwarded-For header into the request containing the source address as supplied by the upstream proxy device.

    Something like the following (only extracting Proxy V1 information, but the same change would work with v2 data - $v2_sourceAddress)

    #PROXY Protocol Receiver iRule
    # c.jenison at f5.com (Chad Jenison)
    # v2.0 - Added support for PROXY Protocol v2, control for v1,v2 or lack of proxy via static:: variables set in RULE_INIT
    when RULE_INIT {
      set static::allowProxyV1 1
      set static::allowProxyV2 1
      set static::allowNoProxy 0
    }
     
    when CLIENT_ACCEPTED {
      TCP::collect
    }
     
    when CLIENT_DATA {
      binary scan [TCP::payload 12] H* v2_protocol_sig
      if {$static::allowProxyV1 && [TCP::payload 0 5] eq "PROXY"} {
        set proxy_string [TCP::payload]
        set proxy_string_length [expr {[string first "\r" [TCP::payload]] + 2}]
        scan $proxy_string {PROXY TCP%s%s%s%s%s} tcpver srcaddr dstaddr srcport dstport
        log "Proxy Protocol v1 conn from [IP::client_addr]:[TCP::client_port] for an IPv$tcpver stream from Src: $srcaddr:$srcport to Dst: $dstaddr:$dstport"
        TCP::payload replace 0 $proxy_string_length ""
      } elseif {$static::allowNoProxy} {
        log "Connection from [IP::client_addr]:[TCP::client_port] allowed despite lack of PROXY protocol header"
      } else {
        reject
        log "Connection rejected from [IP::client_addr]:[TCP::client_port] due to lack of PROXY protocol header"
      }
      TCP::release
    }
     
    when HTTP_REQUEST {
      	HTTP::Header insert "X-Forwarded-For" {$srcaddr}
    }

    > -if someone has a graphic of what order iRules/different virtual server settings are applied that would be helpful as well

    Getting Started with iRules: Events & Priorities