HTTP sideband policy checking

Problem this snippet solves:

iRule for HTTP sideband policy checking

Note: in v11, you can replicate this functionality natively using sideband connections.

What it does

  • For every HTTP request or every first HTTP request of each TCP connection, send HTTP request to external policy server.
  • HTTP request to external policy server can be any method
  • LTM waits for response from policy server before sending user’s request to destination. (Request from user -> request to policy server -> response from policy server -> request to original destination)

Limitation Only HTTP request to external policy server is supported. However, if external policy server supports name lookup or radius or LDAP, other LTM features may be used instead.

How to use

  • Apply iRule to HTTP virtual server
  • Customize iRule actions or variables under the following sections
    • Command to be evaluated in order to reach policy server(s)
    • Prepare policy checking HTTP request
    • To perform policy check for every request, reset "checked" to 0
    • Check if this is response from policy server or default pool
    • Verify result from policy server

Conclusion With iRule, LTM can provide sideband policy checking functionality.

Code :

when CLIENT_ACCEPTED {
    if { [IP::client_addr] != "127.127.127.127" } {
        set checked 0
        # Command to be evaluated in order to reach policy server(s)
        set sb_command "node 10.10.72.2 80"
    }
}
when HTTP_REQUEST {
    # Internal trigger step-2
    if { [IP::client_addr] == "127.127.127.127" } {
        log local0. "Triggering policy checking process"
        HTTP::respond 555 me "me"
        return
    }
    # Process HTTP request for Policy server.
    if { [HTTP::header exists "me"] } {
        eval $sb_command
        snat none
        log local0. "Sending request to policy server: [LB::server]"
        # save policy server information
        set sb_server [LB::server]
        return
    }
    if { ! $checked } {
        # if original method is POST or PUT, collect more data.
        switch [HTTP::method] {
            "POST" -
            "PUT" {
                HTTP::collect [HTTP::header Content-Length]
            }
            default {
                set request_original [HTTP::request]
                log local0. "Original request is [HTTP::method]"
                # use magic number
                snat 127.127.127.127
                HTTP::uri /magic_uri
                # Internal trigger step-1 (for non-POST/PUT)
                virtual [virtual]
            }
        }
        log local0. "Start policy checking process"
        # Prepare policy checking HTTP request
        #  + method
        set sb_method "POST"
        #  + uri
        set sb_uri "/let_me_check?srcip=[IP::client_addr]&see_uri_in_my_payload"
        #  + payload
        set sb_payload "[HTTP::uri]"
        #  + header
        set sb_header "me: me\r\nContent-Length: [string length $sb_payload]"
        #  = combine altogether
        set request_sideband "$sb_method $sb_uri HTTP/1.0\r\n$sb_header\r\n\r\n$sb_payload"
    } else {
        log local0. "Sending request to default pool"
        # To perform policy check for every request, reset "checked" to 0
        set checked 0
    }
}
when HTTP_REQUEST_DATA {
    # HTTP_REQUEST_DATA should only be called for original HTTP request recording
    set request_original [HTTP::request][HTTP::payload [HTTP::header Content-Length]]
    log local0. "Original request is [HTTP::method]. Collecting [HTTP::payload length] bytes payload"
    # use magic number
    snat 127.127.127.127
    HTTP::uri /magic_uri
    # Internal trigger step-1 (for POST/PUT)
    virtual [virtual]
    HTTP::release
}
when HTTP_RESPONSE {
    # Internal trigger step-3
    if { [IP::local_addr] == "127.127.127.127" } {
        HTTP::retry $request_sideband
        log local0. "Insert policy checking HTTP request"
        return
    }
    # Check if this is response from policy server or default pool
    # This has to be changed if policy server address is same as default pool
    if { [LB::server] eq $sb_server }{
        log local0. "Verifying response from policy server: [LB::server]"
        # Verify result from policy server
        # For example, if policy server returns 200, client is allowed to connect to default pool
        if { [HTTP::status] == 200 } {
            # Action to be performed when user passes the policy
            log local0. "User passes the policy"
            HTTP::retry $request_original
        } else {
            log local0. "User does not pass the policy"
            # Action to be performed when user does not pass the policy
            # For example, respond with 401 code.
            HTTP::respond 200 content \
                  "Sorry, you are not allowed to access this resource"
        }
        set checked 1
    } else {
        log local0. "Passing response to client"
    }
}
Published Mar 18, 2015
Version 1.0
No CommentsBe the first to comment