proxy
81 TopicsX-Forwarded-For Log Filter for Windows Servers
For those that don't know what X-Forwarded-For is, then you might as well close your browser because this post likely will mean nothing to you… A Little Background Now, if you are still reading this, then you likely are having issues with determining the origin client connections to your web servers. When web requests are passed through proxies, load balancers, application delivery controllers, etc, the client no longer has a direct connection with the destination server and all traffic looks like it's coming from the last server in the chain. In the following diagram, Proxy2 is the last hop in the chain before the request hits the destination server. Relying on connection information alone, the server thinks that all connections come from Proxy2, not from the Client that initiated the connection. The only one in the chain here who knows who the client really is (as determined by it's client IP Address, is Proxy1. The problem is that application owners rely on source client information for many reasons ranging from analyzing client demographics to targeting Denial of Service attacks. That's where the X-Forwarded-For header comes in. It is non-RFC standard HTTP request header that is used for identifying the originating IP address of a client connecting to a web server through a proxy. The format of the header is: X-Forwarded-For: client, proxy1, proxy, … X-Forwarded-For header logging is supported in Apache (with mod_proxy) but Microsoft IIS does not have a direct way to support the translation of the X-Forwarded-For value into the client ip (c-ip) header value used in its webserver logging. Back in September, 2005 I wrote an ISAPI filter that can be installed within IIS to perform this transition. This was primarily for F5 customers but I figured that I might as well release it into the wild as others would find value out of it. Recently folks have asked for 64 bit versions (especially with the release of Windows 2008 Server). This gave me the opportunity to brush up on my C skills. In addition to building targets for 64 bit windows, I went ahead and added a few new features that have been asked for. Proxy Chain Support The original implementation did not correctly parse the "client, proxy1, proxy2,…" format and assumed that there was a single IP address following the X-Forwarded-For header. I've added code to tokenize the values and strip out all but the first token in the comma delimited chain for inclusion in the logs. Header Name Override Others have asked to be able to change the header name that the filter looked for from "X-Forwarded-For" to some customized value. In some cases they were using the X-Forwarded-For header for another reason and wanted to use iRules to create a new header that was to be used in the logs. I implemented this by adding a configuration file option for the filter. The filter will look for a file named F5XForwardedFor.ini in the same directory as the filter with the following format: [SETTINGS] HEADER=Alternate-Header-Name The value of "Alternate-Header-Name" can be changed to whatever header you would like to use. Download I've updated the original distribution file so that folks hitting my previous blog post would get the updates. The following zip file includes 32 and 64 bit release versions of the F5XForwardedFor.dll that you can install under IIS6 or IIS7. Installation Follow these steps to install the filter. Download and unzip the F5XForwardedFor.zip distribution. Copy the F5XForwardedFor.dll file from the x86\Release or x64\Release directory (depending on your platform) into a target directory on your system. Let's say C:\ISAPIFilters. Ensure that the containing directory and the F5XForwardedFor.dll file have read permissions by the IIS process. It's easiest to just give full read access to everyone. Open the IIS Admin utility and navigate to the web server you would like to apply it to. For IIS6, Right click on your web server and select Properties. Then select the "ISAPI Filters" tab. From there click the "Add" button and enter "F5XForwardedFor" for the Name and the path to the file "c:\ISAPIFilters\F5XForwardedFor.dll" to the Executable field and click OK enough times to exit the property dialogs. At this point the filter should be working for you. You can go back into the property dialog to determine whether the filter is active or an error occurred. For II7, you'll want to select your website and then double click on the "ISAPI Filters" icon that shows up in the Features View. In the Actions Pane on the right select the "Add" link and enter "F5XForwardedFor" for the name and "C:\ISAPIFilters\F5XForwardedFor.dll" for the Executable. Click OK and you are set to go. I'd love to hear feedback on this and if there are any other feature request, I'm wide open to suggestions. The source code is included in the download distribution so if you make any changes yourself, let me know! Good luck and happy filtering! -Joe13KViews0likes14CommentsWhat is a Proxy?
The term ‘Proxy’ is a contraction that comes from the middle English word procuracy, a legal term meaning to act on behalf of another. You may have heard of a proxy vote. Where you submit your choice and someone else votes the ballot on your behalf. In networking and web traffic, a proxy is a device or server that acts on behalf of other devices. It sits between two entities and performs a service. Proxies are hardware or software solutions that sit between the client and the server and does something to requests and sometimes responses. The first kind of proxy we’ll discuss is a half proxy. With a Half-Proxy, a client will connect to the proxy and the proxy will establish the session with the servers. The proxy will then respond back to the client with the information. After that initial connection is set up, the rest of the traffic with go right through the proxy to the back-end resources. The proxy may do things like L4 port switching, routing or NAT’ing but at this point it is not doing anything intelligent other than passing traffic. Basically, the half-proxy sets up a call and then the client and server does their thing. Half-proxies are also good for Direct Server Return (DSR). For protocols like streaming protocols, you’ll have the initial set up but instead of going through the proxy for the rest of the connections, the server will bypass the proxy and go straight to the client. This is so you don’t waste resources on the proxy for something that can be done directly server to client. A Full Proxy on the other hand, handles all the traffic. A full proxy creates a client connection along with a separate server connection with a little gap in the middle. The client connects to the proxy on one end and the proxy establishes a separate, independent connection to the server. This is bi-directionally on both sides. There is never any blending of connections from the client side to the server side – the connections are independent. This is what we mean when we say BIG-IP is a full proxy architecture. The full proxy intelligence is in that OSI Gap. With a half-proxy, it is mostly client side traffic on the way in during a request and then does what it needs…with a full proxy you can manipulate, inspect, drop, do what you need to the traffic on both sides and in both directions. Whether a request or response, you can manipulate traffic on the client side request, the server side request, the server side response or client side response. You get a lot more power with a full proxy than you would with a half proxy. With BIG-IP (a full proxy) on the server side it can be used as a reverse proxy. When clients make a request from the internet, they terminate on the reverse proxy sitting in front of application servers. Reverse proxies are good for traditional load balancing, optimization, SSL offloading, server side caching, and security functionality. If you know certain clients or IP spaces are acceptable, you can whitelist them. Same with known malicious sources or bad ranges/clients, you can blacklist them. You can do it at the IP layer (L4) or you can go up the stack to Layer 7 and control an http/s request. Or add a BIG-IP ASM policy on there. As it inspects the protocol traffic if it sees some anomaly that is not native to the application like a SQL injection, you can block it. On the client side, BIG-IP can also be a forward proxy. In this case, the client connects to the BIG-IP on an outbound request and the proxy acts on behalf of the client to the outside world. This is perfect for things like client side caching (grabbing a video and storing locally), filtering (blocking certain time-wasting sites or malicious content) along with privacy (masking internal resources) along with security. You can also have a services layer, like an ICAP server, where you can pass traffic to an inspection engine prior to hitting the internet. You can manipulate client side traffic out to the internet, server side in from the internet, handle locally on the platform or or pass off to a third party services entity. A full proxy is your friend in an application delivery environment. If you'd like to learn more about Proxies, check out the resources below including the Lightboard Lesson: What is a Proxy? ps Related: Lightboard Lessons: What is a Proxy? Encrypted malware vs. F5's full proxy architecture The Concise Guide to Proxies The Full-Proxy Data Center Architecture Three things your proxy can't do unless it's a full-proxy Back to Basics: The Many Modes of Proxies9.9KViews0likes0CommentsProxyPass v10/v11
Problem this snippet solves: iRule to replace the functionality of Apache Webserver ProxyPass and ProxyPassReverse functions. Allows you to do hostname and path name modifications as HTTP traffic passes through the LTM. This optimized version requires TMOS v10 or higher. If you are using APM for authentication on the virtual server, please use the ProxyPass_for_use_with_APM iRule instead. (Full documentation follows the iRule.) For use with TMOS v9, see ProxyPass v8.2 Note: In 10.1+ you can use an internal data group to store the ProxyPass configuration. Ignore the comments to the contrary in the instructions as these were for pre-10.1 releases. Please post questions or fixes for this iRule in the iRules forum to get the fastest response. Thanks Aaron. Introduction Sometimes it is desirable to have a different look and feel to your website on the outside than you have in the inside. You may want www.company.com/usa/ to internally go to the server usa.company.com. You may want support.company.com to go internally to abc123.company.com/web/support. This can create a few issues - sometimes the web server expects to see a certain hostname (i.e. for name-based virtual hosting) or will use the internal hostname and/or path when sending a redirect to the clients. These problems can be overcome with the Apache webserver using the ProxyPass module which can translate the URL on the way into the server, and the ProxyPassReverse module which can un-translate header fields such as Location in the case of a redirect. Now you can accomplish this with an iRule. The ProxyPass iRule translates incoming requests in a flexible manner and untranslates the Location, Content-Location, and/or URI headers in the response back to the client. ProxyPass also rewrites the domain and path in cookies set by the server. Page Content Modification In addition, this rule will perform basic page modification as needed (this feature is disabled by default but can be enabled in the RULE_INIT event). Using the example from the introduction, if the page content contains a link to http://abc123.company.com/web/support/viewticket.html, the iRule will modify that to be http://support.company.com/viewticket.html. Here are some examples: < a href=http://www.domain.com/path/file.html> will be modified assuming the incoming request was matched by the ProxyPass iRule and the inside hostname was www.domain.com and the inside path was /path. < a href="page2.html"> will not need to be modified as this is a URL already relative to the path. Even relative URLs such as < a href="../page2.html"> will work as long as it does not try to go above the top-level directory defined by the ProxyPass rules. You must have a stream profile defined on any virtual servers the rule is applied to in order to enable the page modification feature. Virtual Server The first step to using the ProxyPass iRule is to define the rule on your BIG-IP and associate it with one or more Virtual Servers. Note that each virtual server MUST have an HTTP profile defined (doesn't matter which one). I also highly recommend applying a OneConnect profile, especially if you will be choosing pools with ProxyPass. It also must have a stream profile associated with it if you want to uncomment the page modification code. The rule will work on HTTP sites as well as HTTPS sites where the SSL is terminated on the BIG-IP (i.e. a client-side SSL profile is defined). Data Groups You can apply the ProxyPass rule to any virtual server that you want to do translations on. But just applying the rule will have no effect unless you define the translations you want done. This is done by defining specific Data Groups. The ProxyPass iRule uses Data Groups which are created and managed by going to Local Traffic / Virtual Servers / iRules on the left menu bar in the BIG-IP GUI. Then choose the "Data Group List" tab at the top of the screen. Here you can create the data groups used by this rule. For 10.0.x, the data groups must be External, type "String", and Read-only. For 10.1 and higher, the data group can be internal or external with name=value pairings. In order to use this rule on a virtual server you must apply the rule to the virtual server and create a data group named ProxyPassXYZ where "XYZ" needs to be the name of the virtual server. If both of these conditions are not met then the rule will not work for that virtual server. ProxyPassXYZ Data Groups If your virtual server is named XYZ and has the ProxyPass iRule associated with it, it will look for a data group named ProxyPassXYZ. Assuming that class is found, for each new HTTP request, the rule will find the one row that matches the hostname/path used in the request. For example, the data group may contain 4 entries (each line below is one string in the data group): "www.usa.company.com/support" := "support.company-internal.com:8080/usa", "www.usa.company.com/" := "www.company-internal.com:8080/usa", "www.japan.company.com/" := "www.company-internal.com:8080/japan", "/" := "www.company-internal.com:8080/others", A request need not match any entries - if no entries match then the iRule will have no effect. But each request will only match at most one entry and that will be the entry with the most specific left-hand-side. Entries with hostnames specified on the left-hand-side will be matched before entries without hostnames. If multiple entries match then the entry with the longest path name on the left-hand-side will be used. The example above lists entries from most-specific to least-specific, just as the rule will process them, but in your actual data group the order of the entries does not matter. In the example above, requests to http://www.usa.company.com/support will match the first entry and have the host header changed to "support.company-internal.com:8080" and the URI will be rewritten so that the string /support at the beginning of the URI will be changed to /usa. Furthermore, requests to http://www.usa.company.com/ will match the second entry as long as the URI does not begin with "/support" in which case it would match the first entry. In that case the Host header will be changed to "www.company-internal.com:8080" and whatever URI the client sends in will be prepended with "/usa". Likewise all requests to http://www.japan.company.com/ will match the third entry and have the Host header changed to "www.company-internal.com:8080" and the URIs would be prepended with "/japan". Finally, all other requests that hit this example virtual server would match the least-specific rule which is simply "/" -- all URIs begin with "/" and thus all requests will match the fourth entry if they did not match any others. Remember that a catch-all entry is not required, but in this example we want to prepend the URI of all other requests with "/others". Note that the ProxyPass iRule does not actually alter the destination of the requests by default. In the examples above all of the requests would go to the default pool regardless of the entries they match. The hostnames and ports specified in the right-hand entry is only used to modify the Host header. To alter the destination pool see the next section. Dynamic Pool Selection You may also specify an alternate pool as a second item in the right-hand value of the entry. This is optional any items in the list without a pool name will just use the default pool associated with the virtual server. For example: "/support" := "support.company.com/ SupportPool", "/downloads" := "downloads.company.com/ DownloadPool", If the pool name is not valid the user will get an error and you should see an error message in /var/log/ltm. Dynamic SNAT You can optionally define a ProxyPassSNATs data group. This will allow you to use different SNAT IP based on which pool you send traffic to. The ProxyPassSNATs data group is shared by all virtual servers but will only have an effect if the selected pool is listed in the data group. The format of this data group (internal string type) is: Pool1 W.X.Y.Z Pool2 automap Dynamic ServerSSL Profiles You can optionally define a ProxyPassSSLProfiles data group and apply a generic serverssl profile to the virtual server. This will allow you to use different serverssl profiles based on which pool you send traffic to. The ProxyPassSSLProfiles data group is shared by all virtual servers but will only have an effect if the selected pool is listed in the data group and a generic serverssl is applied to the virtual server. The format of this data group (internal string type) is: Pool1 ServerSSLProfile1 Pool2 ServerSSLProfile2 Regular Expressions New in v10: you may also use regular expressions and backreferences when building your rule set. "/" := "=www.company.com/(.*?)/=$1.company.com/=", As you see, instead of a regular server-side entry, we have "=regex=replace=". Basically, in order for the regex to be run, the left-hand side must match the client host/path (just "/" in this case, which will always match unless something more specific matches). Once this happens, the client host and URI are combined into a form similar to www.domain.com/path and the regular expression is run a against it. If the regular expression does not match, ProxyPass does not alter the request. If it does match, the clientside path becomes the match string and the serverside path becomes the replace string. Within this replace string you can use $1 through $9 as back-references to grouped items in the original regular expression. So, the example above, entry is equivalent to all of these entries: "www.company.com/sales" := "sales.company.com/", "www.company.com/support" := "support.company.com/", "www.company.com/employment" := "employment.company.com/", Debugging You can debug your ProxyPass rules by setting the static::ProxyPassDebug variable at the top of the rule to 1 (or 2 for more verbose debugging). Once you do this you can SSH to the BIG-IP and run the command "tail -f /var/log/ltm" to see what ProxyPass is doing to your requests. Code : # ProxyPass iRule, Version 10.9 # Nov 26 2012. Date # THIS VERSION REQUIRES TMOS v10 or higher. Use ProxyPass v8.2 for TMOS 9.x. # This version does not work with APM-enabled virtual servers, please # download ProxyPass 10.2APM for this use case. # Created by Kirk Bauer # https://devcentral.f5.com/s/wiki/default.aspx/iRules/ProxyPassV10.html # (please see end of iRule for additional credits) # Purpose: # iRule to replace the functionality of Apache Webserver ProxyPass and # ProxyPassReverse functions. It allows you to perform host name and path name # modifications as HTTP traffic passes through the LTM. In other words, you # can have different hostnames and directory names on the client side as you # do on the server side and ProxyPass handles the necessary translations. # NOTE: You should not need to modify this iRule in any way except the settings # in the RULE_INIT event. Just apply the iRule to the virtual server and # define the appropriate Data Group and you are done. If you do make any # changes to this iRule, please send your changes and reasons to me so that # I may understand how ProxyPass is being used and possibly incorporate your # changes into the core release. # Configuration Requirements # 1) The ProxyPass iRule needs to be applied to an HTTP virtual server or # an HTTPS virtual server with a clientssl profile applied to it. # 2) A data group (LTM -> iRules -> Data Groups tab) must be defined with # the name "ProxyPassVIRTUAL" where VIRTUAL is the name of the virtual server # (case-sensitive!). See below for the format of this data group (class). # For 10.0.x, you must use an EXTERNAL data group. # 3) You must define a default pool on the virtual server unless you specify # a pool in every entry in the data group. # 4) If you are using ProxyPass to select alternate pools, you must define # a OneConnect profile in most cases! # 5) ProxyPass does not rewrite links embedded within pages by default, just # headers. If you want to change this, edit the $static::RewriteResponsePayload variable in RULE_INIT # and apply the default stream profile to the virtual server. # Data Group Information # For 10.0.x, you must define an external data group (type=String, read-only) which loads # from a file on your BIG-IP. For 10.1 and higher you can use an internal string data group with name=value pairings. # The format of the file is as follows: # "clientside" := "serverside", # or # "clientside" := "serverside poolname", # The clientside and serverside fields must contain a URI (at least a "/") and # may also contain a hostname. Here are some examples: # "/clientdir" := "/serverdir", # "www.host.com/clientdir" := "internal.company.com/serverdir", # "www.host.com/" := "internal.company.com/serverdir/", # Notes: # 1) You can optionally define a ProxyPassSNATs data group to SNAT based # on the pool selected. # 2) You can optionally define a ProxyPassSSLProfiles data group to select # a serverssl profile based on the pool selected. # 3) You can also use regular expressions which is documented on DevCentral. when RULE_INIT { # Enable to debug ProxyPass translations via log messages in /var/log/ltm # (2 = verbose, 1 = essential, 0 = none) set static::ProxyPassDebug 0 # Enable to rewrite page content (try a setting of 1 first) # (2 = attempt to rewrite host/path and just /path, 1 = attempt to rewrite host/path) set static::RewriteResponsePayload 0 } when CLIENT_ACCEPTED { # Get the default pool name. This is used later to explicitly select # the default pool for requests which don't have a pool specified in # the class. set default_pool [LB::server pool] # The name of the Data Group (aka class) we are going to use. # Parse just the virtual server name by stripping off the folders (if present) set clname "ProxyPass[URI::basename [virtual name]]" if { $static::ProxyPassDebug > 1 } { log local0. "[virtual name]: [IP::client_addr]:[TCP::client_port] -> [IP::local_addr]:[TCP::local_port]" } } when HTTP_REQUEST { # "bypass" tracks whether or not we made any changes inbound so we # can skip changes on the outbound traffic for greater efficiency. set bypass 1 # Initialize other local variables used in this rule set orig_uri "[HTTP::uri]" set orig_host "[HTTP::host]" set log_prefix "VS=[virtual name], Host=$orig_host, URI=$orig_uri" set clientside "" set serverside "" set newpool "" set ppass "" if {! [class exists $clname]} { log local0. "$log_prefix: Data group $clname not found, exiting." pool $default_pool return } else { set ppass [class match -element "$orig_host$orig_uri" starts_with $clname] if {$ppass eq ""} { # Did not find with hostname, look for just path set ppass [class match -element "$orig_uri" starts_with $clname] } if {$ppass eq ""} { # No entries found if { $static::ProxyPassDebug > 0 } { log local0. "$log_prefix: No rule found, using default pool $default_pool and exiting" } pool $default_pool return } } # Store each entry in the data group line into a local variable set clientside [getfield $ppass " " 1] set serverside [string trimleft [getfield $ppass " " 2 ] "{" ] set newpool [string trimright [getfield $ppass " " 3 ] "}" ] # If serverside is in the form =match=replace=, apply regex if {$serverside starts_with "="} { set regex [getfield $serverside "=" 2] set rewrite [getfield $serverside "=" 3] if {[regexp -nocase $regex "$orig_host$orig_uri" 0 1 2 3 4 5 6 7 8 9]}{ # The clientside becomes the matched string and the serverside the substitution set clientside $0 set serverside [eval set X $rewrite] } else { pool $default_pool return } } if {$clientside starts_with "/"} { # No virtual hostname specified, so use the Host header instead set host_clientside $orig_host set path_clientside $clientside } else { # Virtual host specified in entry, split the host and path set host_clientside [getfield $clientside "/" 1] set path_clientside [substr $clientside [string length $host_clientside]] } # At this point $host_clientside is the client hostname, and $path_clientside # is the client-side path as specified in the data group set host_serverside [getfield $serverside "/" 1] set path_serverside [substr $serverside [string length $host_serverside]] if {$host_serverside eq ""} { set host_serverside $host_clientside } # At this point $host_serverside is the server hostname, and $path_serverside # is the server-side path as specified in the data group # In order for directory redirects to work properly we have to be careful with slashes if {$path_clientside equals "/"} { # Make sure serverside path ends with / if clientside path is "/" if {!($path_serverside ends_with "/")} { append path_serverside "/" } } else { # Otherwise, neither can end in a / (unless serverside path is just "/") if {!($path_serverside equals "/")} { if {$path_serverside ends_with "/"} { set path_serverside [string trimright $path_serverside "/"] } if {$path_clientside ends_with "/"} { set path_clientside [string trimright $path_clientside "/"] } } } if { $static::ProxyPassDebug } { log local0. "$log_prefix: Found Rule, Client Host=$host_clientside, Client Path=$path_clientside, Server Host=$host_serverside, Server Path=$path_serverside" } # If you go to http://www.domain.com/dir, and /dir is a directory, the web # server will redirect you to http://www.domain.com/dir/. The problem is, with ProxyPass, if the client-side # path is http://www.domain.com/dir, but the server-side path is http://www.domain.com/, the server will NOT # redirect the client (it isn't going to redirect you to http://www.domain.com//!). Here is the problem with # that. If there is an image referenced on the page, say logo.jpg, the client doesn't realize /dir is a directory # and as such it will try to load http://www.domain.com/logo.jpg and not http://www.domain.com/dir/logo.jpg. So # ProxyPass has to handle the redirect in this case. This only really matters if the server-side path is "/", # but since we have the code here we might as well offload all of the redirects that we can (that is whenever # the client path is exactly the client path specified in the data group but not "/"). if {$orig_uri eq $path_clientside} { if {([string index $path_clientside end] ne "/") and not ($path_clientside contains ".") } { set is_https 0 if {[PROFILE::exists clientssl] == 1} { set is_https 1 } # Assumption here is that the browser is hitting http://host/path which is a virtual path and we need to do the redirect for them if {$is_https == 1} { HTTP::redirect "https://$orig_host$orig_uri/" if { $static::ProxyPassDebug } { log local0. "$log_prefix: Redirecting to https://$orig_host$orig_uri/" } } else { HTTP::redirect "http://$orig_host$orig_uri/" if { $static::ProxyPassDebug } { log local0. "$log_prefix: Redirecting to http://$orig_host$orig_uri/" } } return } } if {$host_clientside eq $orig_host} { if {$orig_uri starts_with $path_clientside} { set bypass 0 # Take care of pool selection if {$newpool eq ""} { pool $default_pool if { $static::ProxyPassDebug > 1 } { log local0. "$log_prefix: Using default pool $default_pool" } set newpool $default_pool } else { pool $newpool if { $static::ProxyPassDebug > 0 } { log local0. "$log_prefix: Using parsed pool $newpool (make sure you have OneConnect enabled)" } } } } # If we did not match anything, skip the rest of this event if {$bypass} { return } # The following code will look up SNAT addresses from # the data group "ProxyPassSNATs" and apply them. # # The format of the entries in this list is as follows: # # # # All entries are separated by spaces, and both items # are required. set class_exists_cmd "class exists ProxyPassSNATs" if {! [eval $class_exists_cmd]} { return } set snat [findclass $newpool ProxyPassSNATs " "] if {$snat eq ""} { # No snat found, skip rest of this event return } if { $static::ProxyPassDebug > 0 } { log local0. "$log_prefix: SNAT address $snat assigned for pool $newpool" } snat $snat } when HTTP_REQUEST_SEND { # If we didn't match anything, skip the rest of this event if {$bypass} { return } # The following code does the actual rewrite on its way TO # the backend server. It replaces the URI with the newly # constructed one and masks the "Host" header with the FQDN # the backend pool server wants to see. # # If a new pool or custom SNAT are to be applied, these are # done here as well. If a SNAT is used, an X-Forwarded-For # header is attached to send the original requesting IP # through to the server. if {$host_clientside eq $orig_host} { if {$orig_uri starts_with $path_clientside} { if { $static::ProxyPassDebug > 1 } { log local0. "$log_prefix: New Host=$host_serverside, New Path=$path_serverside[substr $orig_uri [string length $path_clientside]]" } clientside { # Rewrite the URI HTTP::uri $path_serverside[substr $orig_uri [string length $path_clientside]] # Rewrite the Host header HTTP::header replace Host $host_serverside # Now alter the Referer header if necessary if { [HTTP::header exists "Referer"] } { set protocol [URI::protocol [HTTP::header Referer]] if {$protocol ne ""} { set client_path [findstr [HTTP::header "Referer"] $host_clientside [string length $host_clientside]] if {$client_path starts_with $path_clientside} { if { $static::ProxyPassDebug > 1 } { log local0. "$log_prefix: Changing Referer header: [HTTP::header Referer] to $protocol://$host_serverside$path_serverside[substr $client_path [string length $path_clientside]]" } HTTP::header replace "Referer" "$protocol://$host_serverside$path_serverside[substr $client_path [string length $path_clientside]]" } } } } } } # If we're rewriting the response content, prevent the server from using #compression in its response by removing the Accept-Encoding header #from the request. LTM does not decompress response content before #applying the stream profile. This header is only removed if we're #rewriting response content. clientside { if { $static::RewriteResponsePayload } { if { [HTTP::header exists "Accept-Encoding"] } { HTTP::header remove "Accept-Encoding" if { $static::ProxyPassDebug > 1} { log local0. "$log_prefix: Removed Accept-Encoding header" } } } HTTP::header insert "X-Forwarded-For" "[IP::remote_addr]" } } when HTTP_RESPONSE { if { $static::ProxyPassDebug > 1 } { log local0. "$log_prefix: [HTTP::status] response from [LB::server]" } if {$bypass} { # No modification is necessary if we didn't change anything inbound so disable the stream filter if it was enabled # Check if we're rewriting the response if {$static::RewriteResponsePayload} { if { $static::ProxyPassDebug > 1 } { log local0. "$log_prefix: Rewriting response content enabled, but disabled on this response." } # Need to explicity disable the stream filter if it's not needed for this response # Hide the command from the iRule parser so it won't generate a validation error #when not using a stream profile set stream_disable_cmd "STREAM::disable" # Execute the STREAM::disable command. Use catch to handle any errors. Save the result to $result if { [catch {eval $stream_disable_cmd} result] } { # There was an error trying to disable the stream profile. log local0. "$log_prefix: Error disabling stream filter ($result). If you enable static::RewriteResponsePayload, then you should add a stream profile to the VIP. Else, set static::RewriteResponsePayload to 0 in this iRule." } } # Exit from this event. return } # Check if we're rewriting the response if {$static::RewriteResponsePayload} { # Configure and enable the stream filter to rewrite the response payload # Hide the command from the iRule parser so it won't generate a validation error #when not using a stream profile if {$static::RewriteResponsePayload > 1} { set stream_expression_cmd "STREAM::expression \"@$host_serverside$path_serverside@$host_clientside$path_clientside@ @$path_serverside@$path_clientside@\"" } else { set stream_expression_cmd "STREAM::expression \"@$host_serverside$path_serverside@$host_clientside$path_clientside@\"" } set stream_enable_cmd "STREAM::enable" if { $static::ProxyPassDebug > 1 } { log local0. "$log_prefix: \$stream_expression_cmd: $stream_expression_cmd, \$stream_enable_cmd: $stream_enable_cmd" } # Execute the STREAM::expression command. Use catch to handle any errors. Save the result to $result if { [catch {eval $stream_expression_cmd} result] } { # There was an error trying to set the stream expression. log local0. "$log_prefix: Error setting stream expression ($result). If you enable static::RewriteResponsePayload, then you should add a stream profile to the VIP. Else, set static::RewriteResponsePayload to 0 in this iRule." } else { # No error setting the stream expression, so try to enable the stream filter # Execute the STREAM::enable command. Use catch to handle any errors. Save the result to $result if { [catch {eval $stream_enable_cmd} result] } { # There was an error trying to enable the stream filter. log local0. "$log_prefix: error enabling stream filter ($result)" } else { if { $static::ProxyPassDebug > 1 } { log local0. "$log_prefix: Successfully configured and enabled stream filter" } } } } # Fix Location, Content-Location, and URI headers foreach header {"Location" "Content-Location" "URI"} { set protocol [URI::protocol [HTTP::header $header]] if { $static::ProxyPassDebug > 1 } { log local0. "$log_prefix: Checking $header=[HTTP::header $header], \$protocol=$protocol" } if {$protocol ne ""} { set server_path [findstr [HTTP::header $header] $host_serverside [string length $host_serverside]] if {$server_path starts_with $path_serverside} { if { $static::ProxyPassDebug } { log local0. "$log_prefix: Changing response header $header: [HTTP::header $header] with $protocol://$host_clientside$path_clientside[substr $server_path [string length $path_serverside]]" } HTTP::header replace $header $protocol://$host_clientside$path_clientside[substr $server_path [string length $path_serverside]] } } } # Rewrite any domains/paths in Set-Cookie headers if {[HTTP::header exists "Set-Cookie"]}{ array unset cookielist foreach cookievalue [HTTP::header values "Set-Cookie"] { set cookiename [getfield $cookievalue "=" 1] set namevalue "" set newcookievalue "" foreach element [split $cookievalue ";"] { set element [string trim $element] if {$namevalue equals ""} { set namevalue $element } else { if {$element contains "="} { set elementname [getfield $element "=" 1] set elementvalue [getfield $element "=" 2] if {[string tolower $elementname] eq "domain"} { set elementvalue [string trimright $elementvalue "."] if {$host_serverside ends_with $elementvalue} { if {$static::ProxyPassDebug > 1} { log local0. "$log_prefix: Modifying cookie $cookiename domain from $elementvalue to $host_clientside" } set elementvalue $host_clientside } append elementvalue "." } if {[string tolower $elementname] eq "path"} { if {$elementvalue starts_with $path_serverside} { if {$static::ProxyPassDebug > 1} { log local0. "$log_prefix: Modifying cookie $cookiename path from $elementvalue to $path_clientside[substr $elementvalue [string length $path_serverside]]" } set elementvalue $path_clientside[substr $elementvalue [string length $path_serverside]] } } append newcookievalue "; $elementname=$elementvalue" } else { append newcookievalue "; $element" } } } set cookielist($cookiename) "$namevalue$newcookievalue" } HTTP::header remove "Set-Cookie" foreach cookiename [array names cookielist] { HTTP::header insert "Set-Cookie" $cookielist($cookiename) if {$static::ProxyPassDebug > 1} { log local0. "$log_prefix: Inserting cookie: $cookielist($cookiename)" } } } } # Only uncomment this event if you need extra debugging for content rewriting. # This event can only be uncommented if the iRule is used with a stream profile. #when STREAM_MATCHED { #if { $static::ProxyPassDebug } { #log local0. "$log_prefix: Rewriting match: [STREAM::match]" #} #} # The following code will look up SSL profile rules from # the Data Group ProxyPassSSLProfiles" and apply # them. # # The format of the entries in this list is as follows: # # # # All entries are separated by spaces, and both items # are required. The virtual server also will need to # have any serverssl profile applied to it for this to work. when SERVER_CONNECTED { if {$bypass} { return } set class_exists_cmd "class exists ProxyPassSSLProfiles" if {! [eval $class_exists_cmd]} { return } set pool [LB::server pool] set profilename [findclass $pool ProxyPassSSLProfiles " "] if {$profilename eq ""} { if { [PROFILE::exists serverssl] == 1} { # Hide this command from the iRule parser (in case no serverssl profile is applied) set disable "SSL::disable serverside" catch {eval $disable} } return } if { $static::ProxyPassDebug > 0 } { log local0. "$log_prefix: ServerSSL profile $profilename assigned for pool $pool" } if { [PROFILE::exists serverssl] == 1} { # Hide these commands from the iRule parser (in case no serverssl profile is applied) set profile "SSL::profile $profilename" catch {eval $profile} set enable "SSL::enable serverside" catch {eval $enable} } else { log local0. "$log_prefix: ServerSSL profile must be defined on virtual server to enable server-side encryption!" } } # ProxyPass Release History #v10.9: Nov 26, 2012: Used URI::basename to get the virtual server name. Thanks to Opher Shachar for the suggestion. #Replaced indentations with tabs intead of spaces to save on characters #v10.8: Oct 25, 2012: Updated the class name to remove the folder(s) (if present) from the virtual server name. # This assumes the ProxyPass data group is in the same partition as the iRule. #v10.7: Oct 24, 2012: Changed array set cookielist {} to array unset cookielist as the former does not clear the array. # Thanks to rhuyerman@schubergphilis.com and Simon Kowallik for pointing out the issue and this wiki page with details: http://wiki.tcl.tk/724 #v10.6: Oct 14, 2012: Updated how the protocol is parsed from URLs in request and response headers to fix errant matches #v10.5: Feb 2, 2012: Removed extra stream profile $result reference for debug logging. #v10.4: Nov 23, 2011: Removed an extra colon in sever HTTP::header replace commands to prevent duplicate headers from being inserted #v10.3: Sep 27, 2010: Moved rewrite code to HTTP_REQUEST_SEND to work with WebAccelerator # Fixed bug with cookie rewrites when cookie value contained an "=" #v10.2: Jun 04, 2010: Can handle individual file mappings thanks to Michael Holmes from AZDOE # Also fixed bug with directory slash logic #v10.1: Oct 24, 2009: Now CMP-friendly! (NOTE: use ProxyPass v8.2 for TMOS v9.x) #v10.0: May 15, 2009: Optimized for external classes in v10 only (use v8.2 for TMOS v9.x) # Added support for regular expressions and backreferences for the translations. # v8.2: Jun 04, 2010: Fixed bug with directory slash logic # v8.1: May 15, 2009: Added internal redirects back in (removing them was a mistake) # v8.0: May 13, 2009: pulled in changes submitted by Aaron Hooley (hooleylists gmail com) # TMOS v10 support added. Cookie domain/path rewriting added. # v7.0: May 6, 2008: added optional serverssl contributed by Joel Moses # v6.0: Jan 15, 2008: Small efficiency change # v5.0: Jul 27, 2007: Added Referer header conversions # v4.0: Jul 27, 2007: Added optional debugging flag # v3.0: Jul 20, 2007: Added SNAT support contributed by Adam Auerbach # v2.0: May 28, 2007: Added internal directory redirects and optional stream profile # v1.0: Feb 20, 2007: Initial Release Tested this on version: 10.07.8KViews0likes27CommentsProxy Protocol Initiator
Problem this snippet solves: iRule Support for BIG-IP sending Proxy header to serverside pool member. (BIG-IP as Proxy Protocol Initiator) Implements v1 of PROXY protocol at: http://www.haproxy.org/download/1.5/doc/proxy-protocol.txt How to use this snippet: Add iRule to Virtual Server. Back-end server should accept Proxy header. Code : when CLIENT_ACCEPTED { set proxyheader "PROXY " if {[IP::version] eq 4} { append proxyheader "TCP4 " } else { append proxyheader "TCP6 " } append proxyheader "[IP::remote_addr] [IP::local_addr] [TCP::remote_port] [TCP::local_port]\r\n" } when SERVER_CONNECTED { TCP::respond $proxyheader } ### Alternate Optimized Version ### when CLIENT_ACCEPTED { set proxyheader "PROXY TCP[IP::version] [IP::remote_addr] [IP::local_addr] [TCP::remote_port] [TCP::local_port]\r\n" } when SERVER_CONNECTED { TCP::respond $proxyheader } Tested this on version: 11.67.1KViews0likes17CommentsProxy Protocol Receiver
Problem this snippet solves: iRule for BIG-IP to receive PROXY protocol (v1 and v2) header in TCP Payload and remove it before forwarding remaining TCP Payload to server side pool member. How to use this snippet: Enable iRule on virtual server where upstream proxy will be sending PROXY header. Testing done using proxied IPv6 and IPv4 HTTP connections from HAProxy using Proxy Protocol v1 and v2. Use of client or server SSL profiles slated for testing and validation since ELB promotes use of Proxy Protocol as a solution for customers that don't want to have ELB terminate HTTPS traffic but do want servers to see original IP addresses. Code : #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 # v2.1 - Fix for skipping bytes in v2 code when RULE_INIT { set static::allowProxyV1 0 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" binary scan [TCP::payload] @14S address_size set skip_bytes [expr 16 + $address_size] TCP::payload replace 0 $skip_bytes "" } elseif {$v2_addressFamilyTransportProtocol == 21} { binary scan [TCP::payload] @16H4H4H4H4H4H4H4H4 v2_v6sourceAddress1 v2_v6sourceAddress2 v2_v6sourceAddress3 v2_v6sourceAddress4 v2_v6sourceAddress5 v2_v6sourceAddress6 v2_v6sourceAddress7 v2_v6sourceAddress8 binary scan [TCP::payload] @32H4H4H4H4H4H4H4H4 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" binary scan [TCP::payload] @14S address_size set skip_bytes [expr 16 + $address_size] TCP::payload replace 0 $skip_bytes "" } 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]; skipping" binary scan [TCP::payload] @14S address_size set skip_bytes [expr 16 + $address_size] TCP::payload replace 0 $skip_bytes "" binary scan [TCP::payload] H* local_remainder } 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 } Tested this on version: 12.05.1KViews0likes9CommentsThree things your proxy can’t do unless it’s a full-proxy
Proxies are one of the more interesting (in my no-doubt biased opinion) “devices” in the network. They’re the basis for caching, load balancing, app security, and even app acceleration services. They’re also a bridge between dev and ops and the network, being commonplace to all three groups and environments in most data center architectures. But not all proxies are built on the same architectural principles, which means not all proxies are created equal. A large number of proxies are half-proxies while others are full-proxies, and the differences between them are what mean the difference between what you can and cannot do with them. In fact, there are three very important things you can do with a full-proxy that you can’t do with a regular old proxy. Before we jump into those three things, let’s review the differences between them, shall we? Half-Proxy Half-proxy is a description of the way in which a proxy, reverse or forward, handles connections. Basically it’s describing the notion that the proxy only mediates connections on the client side. So it only proxies half the communication between the client and the app. The most important thing to recognize about a half-proxy is that it has only one network stack that it shares across both client and server. Full-Proxy By contrast, a full-proxy maintains two distinct network stacks – one on the client side, one of the app side – and fully proxies both sides, hence the name. While a full-proxy can be configured to act like a half-proxy, its value is in its typical configuration, which is to maintain discrete connections to both the client and the server. It is this dual-stack approach that enables a full-proxy to provide capabilities that a half-proxy with its single network stack simply cannot. The Three Things A full-proxy completely understands the protocols for which it proxies and is itself both an endpoint and an originator for those protocols and connections. This also means the full-proxy can have its own TCP connection behavior for each network stack such as buffering, retransmits, and TCP options. With a full-proxy each connection is unique; each can have its own TCP connection behavior. This means that a client connecting to the full-proxy device would likely have different connection behavior than the full-proxy might use for communicating with servers. Full-proxies can look at incoming requests and outbound responses and can manipulate both if the solution allows it. #1 Optimize client side and server side Because it can maintain separate network stacks and characteristics, a full-proxy can optimize each side for its unique needs. The TCP options needed to optimize for performance on the client side’s lower-speed, higher-latency network connection – particularly when mobile devices are being served – are almost certainly very different than those needed to optimize for performance on the server side’s high-speed, low latency data center network connection. A full-proxy can optimize both at the same time and thus provide the best performance possible in all situations. A half-proxy, with its single network stack, is forced to optimize for the average of its connections, which certainly means one side or the other is left with less than optimal performance. #2 Act as a protocol gateway Protocol gateways are an important tool in the architect’s toolbox particularly when transitioning from one version of an application protocol to another, like HTTP/1 to HTTP/2 or SPDY. Because a full proxy maintains those two unique connections, it can accept HTTP/2 on the client side, for example, but speak HTTP/1 to the server (app). That’s because a full-proxy terminates the client connection (the proxy is the server) and initiatives a different connection to the server (the proxy is the client). The protocol used on the client side doesn’t restrict the choice of protocols on the server side. Realistically, any protocol transition that makes sense (and even those that don’t) can be managed with a full-proxy. A programmable full-proxy ensures that even if its an uncommon (and thus not universally supported) that you can code up a gateway yourself without expending effort on reinventing the proxy-wheel. #3 Terminate SSL/TLS Technically this is a specialized case of a protocol gateway but the ascendancy of HTTP/S (and the urgency with which we are encouraged to deploy SSL Everywhere and Encrypt All The Things) makes me treat this as its own case. Basically terminating SSL/TLS is a critical capability in modern and emerging architectures because of the need to inspect and direct HTTP-based traffic (like REST API calls) based on information within the HTTP protocol that would otherwise be invisible thanks to encryption. The ability to terminate SSL/TLS means the proxy becomes the secure endpoint to which clients connect (and ultimately trust). Termination means the proxy is responsible for decrypting requests and encrypting responses and is thus able to “see” into the messages and use the data therein to make routing and load balancing decisions. So the next time you’re looking at a proxy, don’t forget to find out whether it’s a full proxy or not. Because without a full-proxy, you’re limiting your ability to really take advantage of its capabilities and reaping the benefits it can offer modern and emerging application architectures.2.8KViews1like3CommentsWhat is server offload and why do I need it?
One of the tasks of an enterprise architect is to design a framework atop which developers can implement and deploy applications consistently and easily. The consistency is important for internal business continuity and reuse; common objects, operations, and processes can be reused across applications to make development and integration with other applications and systems easier. Architects also often decide where functionality resides and design the base application infrastructure framework. Application server, identity management, messaging, and integration are all often a part of such architecture designs. Rarely does the architect concern him/herself with the network infrastructure, as that is the purview of “that group”; the “you know who I’m talking about” group. And for the most part there’s no need for architects to concern themselves with network-oriented architecture. Applications should not need to know on which VLAN they will be deployed or what their default gateway might be. But what architects might need to know – and probably should know – is whether the network infrastructure supports “server offload” of some application functions or not, and how that can benefit their enterprise architecture and the applications which will be deployed atop it. WHAT IT IS Server offload is a generic term used by the networking industry to indicate some functionality designed to improve the performance or security of applications. We use the term “offload” because the functionality is “offloaded” from the server and moved to an application network infrastructure device instead. Server offload works because the application network infrastructure is almost always these days deployed in front of the web/application servers and is in fact acting as a broker (proxy) between the client and the server. Server offload is generally offered by load balancers and application delivery controllers. You can think of server offload like a relay race. The application network infrastructure device runs the first leg and then hands off the baton (the request) to the server. When the server is finished, the application network infrastructure device gets to run another leg, and then the race is done as the response is sent back to the client. There are basically two kinds of server offload functionality: Protocol processing offload Protocol processing offload includes functions like SSL termination and TCP optimizations. Rather than enable SSL communication on the web/application server, it can be “offloaded” to an application network infrastructure device and shared across all applications requiring secured communications. Offloading SSL to an application network infrastructure device improves application performance because the device is generally optimized to handle the complex calculations involved in encryption and decryption of secured data and web/application servers are not. TCP optimization is a little different. We say TCP session management is “offloaded” to the server but that’s really not what happens as obviously TCP connections are still opened, closed, and managed on the server as well. Offloading TCP session management means that the application network infrastructure is managing the connections between itself and the server in such a way as to reduce the total number of connections needed without impacting the capacity of the application. This is more commonly referred to as TCP multiplexing and it “offloads” the overhead of TCP connection management from the web/application server to the application network infrastructure device by effectively giving up control over those connections. By allowing an application network infrastructure device to decide how many connections to maintain and which ones to use to communicate with the server, it can manage thousands of client-side connections using merely hundreds of server-side connections. Reducing the overhead associated with opening and closing TCP sockets on the web/application server improves application performance and actually increases the user capacity of servers. TCP offload is beneficial to all TCP-based applications, but is particularly beneficial for Web 2.0 applications making use of AJAX and other near real-time technologies that maintain one or more connections to the server for its functionality. Protocol processing offload does not require any modifications to the applications. Application-oriented offload Application-oriented offload includes the ability to implement shared services on an application network infrastructure device. This is often accomplished via a network-side scripting capability, but some functionality has become so commonplace that it is now built into the core features available on application network infrastructure solutions. Application-oriented offload can include functions like cookie encryption/decryption, compression, caching, URI rewriting, HTTP redirection, DLP (Data Leak Prevention), selective data encryption, application security functionality, and data transformation. When network-side scripting is available, virtually any kind of pre or post-processing can be offloaded to the application network infrastructure and thereafter shared with all applications. Application-oriented offload works because the application network infrastructure solution is mediating between the client and the server and it has the ability to inspect and manipulate the application data. The benefits of application-oriented offload are that the services implemented can be shared across multiple applications and in many cases the functionality removes the need for the web/application server to handle a specific request. For example, HTTP redirection can be fully accomplished on the application network infrastructure device. HTTP redirection is often used as a means to handle application upgrades, commonly mistyped URIs, or as part of the application logic when certain conditions are met. Application security offload usually falls into this category because it is application – or at least application data – specific. Application security offload can include scanning URIs and data for malicious content, validating the existence of specific cookies/data required for the application, etc… This kind of offload improves server efficiency and performance but a bigger benefit is consistent, shared security across all applications for which the service is enabled. Some application-oriented offload can require modification to the application, so it is important to design such features into the application architecture before development and deployment. While it is certainly possible to add such functionality into the architecture after deployment, it is always easier to do so at the beginning. WHY YOU NEED IT Server offload is a way to increase the efficiency of servers and improve application performance and security. Server offload increases efficiency of servers by alleviating the need for the web/application server to consume resources performing tasks that can be performed more efficiently on an application network infrastructure solution. The two best examples of this are SSL encryption/decryption and compression. Both are CPU intense operations that can consume 20-40% of a web/application server’s resources. By offloading these functions to an application network infrastructure solution, servers “reclaim” those resources and can use them instead to execute application logic, serve more users, handle more requests, and do so faster. Server offload improves application performance by allowing the web/application server to concentrate on what it is designed to do: serve applications and putting the onus for performing ancillary functions on a platform that is more optimized to handle those functions. Server offload provides these benefits whether you have a traditional client-server architecture or have moved (or are moving) toward a virtualized infrastructure. Applications deployed on virtual servers still use TCP connections and SSL and run applications and therefore will benefit the same as those deployed on traditional servers. I am wondering why not all websites enabling this great feature GZIP? 3 Really good reasons you should use TCP multiplexing SOA & Web 2.0: The Connection Management Challenge Understanding network-side scripting I am in your HTTP headers, attacking your application Infrastructure 2.0: As a matter of fact that isn't what it means2.7KViews0likes1CommentHow to delete proxy sys db configurations on ASM?
Hello Everyone, I'm having an issue here where I'd like to totally remove the Proxy Configurations.As what happens here is that the ASM tries to resolve <null> as a domain so the update fails. We are using TMOS v14.1 Thanks in advance!Solved2.4KViews0likes5CommentsLightboard Lessons: Microsoft AD FS Web Application Proxy Using F5 BIG-IP
Many users and organizations want the flexibility and convenience of identity federation and Single Sign-On (SSO) from the corporate network to intranet, extranet, and cloud applications. Users want touse their corporate login information to access applications outside of the organization...for example, login from the Microsoft/Windows environment and have seamless access to Office 365 and external applications like Salesforce and Concur. Microsoft utlilizes a WebApplication Proxy (WAP) that acts as agateway product to allow external users to access internal applications (behind the firewall), like Active Directory Federation Services (AD FS) for example. The F5 BIG-IP can be used to providehigh availability, performance, and scalability for both AD FS and AD FS Proxy servers using theLocal Traffic Manager (LTM) module. The same BIG-IP can also be usedto secure AD FS traffic without the need for AD FS Proxy servers by using the Access Policy Manager (APM) module. Check out the video below to learn more! Related Resources: Deploying F5 with Microsoft Active Directory Federation Services Active Directory Federation Services Content Map1.8KViews0likes0CommentsLDAP Proxy
Problem this snippet solves: Summary: An LDAP proxy used send read/write requests to different pools. For anyone that is interested, I recently was posed with the problem of proxying ldap requests. The requirement was to send read and write requests to different pools. As any familiar with ldap knows, you need to send a bind request to authenticate. The following will transparently resend the bind requests to the newly selected server prior to sending the new read/write request. Other functionality could be added to this, such as more verification of the ldap fields to ensure a valid request is being made. The expr commands could be optimized with proper bracing. See the TCL expr wiki page for details Code : class ldap_writes { 6 8 10 12 } # v11 compatabile version when RULE_INIT { # Read Pool set static::readPool sun_ldap_read # Write Pool set static::writePool sun_ldap_write # Turn on debugging set static::ldap_debug 0 # A lookup table for debugging array set static::msg_types { 0 "bind request" 1 "bind response" 2 "unbind request" 3 "search request" 4 "search response" 6 "modify request" 7 "modify response" 8 "add request" 9 "add response" 10 "delete request" 11 "delete response" 12 "modifydn request" 13 "modifydn response" 14 "compare request" 15 "compare response" 16 "abandon request" 23 "extended request" 24 "extended response" } } when CLIENT_ACCEPTED { set rebind 0 set binding "" set replayop "" set writing 0 TCP::collect } when CLIENT_DATA { # Grab the current payload collected set payload [TCP::payload] # Pull the first 2 bytes. binary scan $payload H2c ber_t ber_len # The first byte is the tag signifying an LDAP message, # Always is hex 30, if that is not so reject if { $ber_t ne "30" } { reject return } # The second byte is one of two values: # a) The length of the packet minus the above # defining byte and the length byte # OR # b) an octet describing how many subsequent bytes # hold the packet length # In either case the message type (what we are after) # follows the message id field which too can be a variable # number of bytes. set len_bytes 0 if { [expr {[expr {$ber_len + 0x100} % 0x100]} & 128] > 0 } { set len_bytes [expr {[expr ($ber_len + 0x100) % 0x100]} & 127] } # How many bytes is the message id binary scan $payload x[expr {3 + $len_bytes}]c msgid_bytes # The message type is then 4 bytes + number length bytes + number of # message id bytes offset. binary scan $payload x[expr {4 + $len_bytes + $msgid_bytes}]c msgtype # msgtype - BER encoded value, bits 1-5 are the actual # type, 6 is the data type, 7-8 are the data class # Here we only care about the lower 5 bits set msgtype [expr {$msgtype & 31}] if {$static::ldap_debug and [catch { log local0. "message type is: $static::msg_types($msgtype) $msgtype" } ] } { log local0. "Bad message type: $msgtype" reject } # Each connection should start with a bind request # We'll save this packet for later rebinding when we # flip between servers if { $msgtype == 0 } { if {$static::ldap_debug} {log local0. "Bind Request with: ldap_read"} set writing 0 set rebind 0 set binding $payload LB::detach pool $static::readPool # If we come across a write request and are currently not # sending data to the write pool, detach, and set the rebind # flag so we can send the bind packet before we actually send # our write request } elseif {[class match -- $msgtype equals ldap_writes] and $writing != 1} { if {$static::ldap_debug} {log local0. "Rebinding with: ldap_write"} set rebind 1 set writing 1 set replayop $payload TCP::payload replace 0 [TCP::payload length] $binding LB::detach pool $static::writePool # If we come across a read request while we are bound to a write server # we need to detach and rebind with a read server from the read pool } elseif {![class match -- $msgtype equals ldap_writes] and $writing == 1} { if {$static::ldap_debug} {log local0. "Rebinding with: ldap_read"} set rebind 1 set writing 0 set replayop $payload TCP::payload replace 0 [TCP::payload length] $binding LB::detach pool $static::readPool } TCP::release TCP::collect } when SERVER_CONNECTED { # A change in the type of request has been detected # requiring a rebind, we've sent the bind now we need to # wait for the response before we send the actual request if { $rebind == 1 } { TCP::collect } } when SERVER_DATA { if { $rebind == 1 } { set rebind 0 # See above for details on this block. Stupid iRules, no proc grrrr set payload [TCP::payload] # Pull the first 2 bytes. binary scan $payload H2c ber_t ber_len set len_bytes 0 if { [expr {[expr {($ber_len + 0x100) % 0x100}]} & 128] > 0 } { set len_bytes [expr {[expr ($ber_len + 0x100) % 0x100]} & 127] } binary scan $payload x[expr {3 + $len_bytes}]c msgid_bytes binary scan $payload x[expr {4 + $len_bytes + $msgid_bytes}]c msgtype set msgtype [expr {$msgtype & 31}] # If the msgtype we have here is for a bind response just discard # it as we don't need to send it to the client if {$msgtype == 1 } { TCP::payload replace 0 [TCP::payload length] "" } # Now send the actual read or write op to the server # It should now have processed the bind TCP::respond $replayop } TCP::release } # v10 CMP compatible version when RULE_INIT { # Read Pool set static::readPool sun_ldap_read # Write Pool set static::writePool sun_ldap_write # Turn on debugging set static::ldap_debug 0 # A lookup table for debugging array set static::msg_types { 0 "bind request" 1 "bind response" 2 "unbind request" 3 "search request" 4 "search response" 6 "modify request" 7 "modify response" 8 "add request" 9 "add response" 10 "delete request" 11 "delete response" 12 "modifydn request" 13 "modifydn response" 14 "compare request" 15 "compare response" 16 "abandon request" 23 "extended request" 24 "extended response" } } when CLIENT_ACCEPTED { set rebind 0 set binding "" set replayop "" set writing 0 TCP::collect } when CLIENT_DATA { # Grab the current payload collected set payload [TCP::payload] # Pull the first 2 bytes. binary scan $payload H2c ber_t ber_len # The first byte is the tag signifying an LDAP message, # Always is hex 30, if that is not so reject if { $ber_t ne "30" } { reject return } # The second byte is one of two values: # a) The length of the packet minus the above # defining byte and the length byte # OR # b) an octet describing how many subsequent bytes # hold the packet length # In either case the message type (what we are after) # follows the message id field which too can be a variable # number of bytes. set len_bytes 0 if { [expr [expr ($ber_len + 0x100) % 0x100] & 128] > 0 } { set len_bytes [expr [expr ($ber_len + 0x100) % 0x100] & 127] } # How many bytes is the message id binary scan $payload x[expr 3 + $len_bytes]c msgid_bytes # The message type is then 4 bytes + number length bytes + number of # message id bytes offset. binary scan $payload x[expr 4 + $len_bytes + $msgid_bytes]c msgtype # msgtype - BER encoded value, bits 1-5 are the actual # type, 6 is the data type, 7-8 are the data class # Here we only care about the lower 5 bits set msgtype [expr $msgtype & 31] if {$static::ldap_debug and [catch { log local0. "message type is: $static::msg_types($msgtype) $msgtype" } ] } { log local0. "Bad message type: $msgtype" reject } # Each connection should start with a bind request # We'll save this packet for later rebinding when we # flip between servers if { $msgtype == 0 } { if {$static::ldap_debug} {log local0. "Bind Request with: ldap_read"} set writing 0 set rebind 0 set binding $payload LB::detach pool $static::readPool # If we come across a write request and are currently not # sending data to the write pool, detach, and set the rebind # flag so we can send the bind packet before we actually send # our write request } elseif {[matchclass $msgtype equals $::ldap_writes] and $writing != 1} { if {$static::ldap_debug} {log local0. "Rebinding with: ldap_write"} set rebind 1 set writing 1 set replayop $payload TCP::payload replace 0 [TCP::payload length] $binding LB::detach pool $static::writePool # If we come across a read request while we are bound to a write server # we need to detach and rebind with a read server from the read pool } elseif {![matchclass $msgtype equals $::ldap_writes] and $writing == 1} { if {$static::ldap_debug} {log local0. "Rebinding with: ldap_read"} set rebind 1 set writing 0 set replayop $payload TCP::payload replace 0 [TCP::payload length] $binding LB::detach pool $static::readPool } TCP::release TCP::collect } when SERVER_CONNECTED { # A change in the type of request has been detected # requiring a rebind, we've sent the bind now we need to # wait for the response before we send the actual request if { $rebind == 1 } { TCP::collect } } when SERVER_DATA { if { $rebind == 1 } { set rebind 0 # See above for details on this block. Stupid iRules, no proc grrrr set payload [TCP::payload] # Pull the first 2 bytes. binary scan $payload H2c ber_t ber_len set len_bytes 0 if { [expr [expr ($ber_len + 0x100) % 0x100] & 128] > 0 } { set len_bytes [expr [expr ($ber_len + 0x100) % 0x100] & 127] } binary scan $payload x[expr 3 + $len_bytes]c msgid_bytes binary scan $payload x[expr 4 + $len_bytes + $msgid_bytes]c msgtype set msgtype [expr $msgtype & 31] # If the msgtype we have here is for a bind response just discard # it as we don't need to send it to the client if {$msgtype == 1 } { TCP::payload replace 0 [TCP::payload length] "" } # Now send the actual read or write op to the server # It should now have processed the bind TCP::respond $replayop } TCP::release } # v9 compatible version when RULE_INIT { # Read Pool set ::readPool sun_ldap_read # Write Pool set ::writePool sun_ldap_write # Turn on debugging set ::debug 0 # A lookup table for debugging array set ::msg_types { 0 "bind request" 1 "bind response" 2 "unbind request" 3 "search request" 4 "search response" 6 "modify request" 7 "modify response" 8 "add request" 9 "add response" 10 "delete request" 11 "delete response" 12 "modifydn request" 13 "modifydn response" 14 "compare request" 15 "compare response" 16 "abandon request" 23 "extended request" 24 "extended response" } } when CLIENT_ACCEPTED { set rebind 0 set binding "" set replayop "" set writing 0 TCP::collect } when CLIENT_DATA { # Grab the current payload collected set payload [TCP::payload] # Pull the first 2 bytes. binary scan $payload H2c ber_t ber_len # The first byte is the tag signifying an LDAP message, # Always is hex 30, if that is not so reject if { $ber_t ne "30" } { reject return } # The second byte is one of two values: # a) The length of the packet minus the above # defining byte and the length byte # OR # b) an octet describing how many subsequent bytes # hold the packet length # In either case the message type (what we are after) # follows the message id field which too can be a variable # number of bytes. set len_bytes 0 if { [expr [expr ($ber_len + 0x100) % 0x100] & 128] > 0 } { set len_bytes [expr [expr ($ber_len + 0x100) % 0x100] & 127] } # How many bytes is the message id binary scan $payload x[expr 3 + $len_bytes]c msgid_bytes # The message type is then 4 bytes + number length bytes + number of # message id bytes offset. binary scan $payload x[expr 4 + $len_bytes + $msgid_bytes]c msgtype # msgtype - BER encoded value, bits 1-5 are the actual # type, 6 is the data type, 7-8 are the data class # Here we only care about the lower 5 bits set msgtype [expr $msgtype & 31] if {$::debug and [catch { log local0. "message type is: $::msg_types($msgtype) $msgtype" } ] } { log local0. "Bad message type: $msgtype" reject } # Each connection should start with a bind request # We'll save this packet for later rebinding when we # flip between servers if { $msgtype == 0 } { if {$::debug} {log local0. "Bind Request with: ldap_read"} set writing 0 set rebind 0 set binding $payload LB::detach pool $::readPool # If we come across a write request and are currently not # sending data to the write pool, detach, and set the rebind # flag so we can send the bind packet before we actually send # our write request } elseif {[matchclass $msgtype equals $::ldap_writes] and $writing != 1} { if {$::debug} {log local0. "Rebinding with: ldap_write"} set rebind 1 set writing 1 set replayop $payload TCP::payload replace 0 [TCP::payload length] $binding LB::detach pool $::writePool # If we come across a read request while we are bound to a write server # we need to detach and rebind with a read server from the read pool } elseif {![matchclass $msgtype equals $::ldap_writes] and $writing == 1} { if {$::debug} {log local0. "Rebinding with: ldap_read"} set rebind 1 set writing 0 set replayop $payload TCP::payload replace 0 [TCP::payload length] $binding LB::detach pool $::readPool } TCP::release TCP::collect } when SERVER_CONNECTED { # A change in the type of request has been detected # requiring a rebind, we've sent the bind now we need to # wait for the response before we send the actual request if { $rebind == 1 } { TCP::collect } } when SERVER_DATA { if { $rebind == 1 } { set rebind 0 # See above for details on this block. Stupid iRules, no proc grrrr set payload [TCP::payload] # Pull the first 2 bytes. binary scan $payload H2c ber_t ber_len set len_bytes 0 if { [expr [expr ($ber_len + 0x100) % 0x100] & 128] > 0 } { set len_bytes [expr [expr ($ber_len + 0x100) % 0x100] & 127] } binary scan $payload x[expr 3 + $len_bytes]c msgid_bytes binary scan $payload x[expr 4 + $len_bytes + $msgid_bytes]c msgtype set msgtype [expr $msgtype & 31] # If the msgtype we have here is for a bind response just discard # it as we don't need to send it to the client if {$msgtype == 1 } { TCP::payload replace 0 [TCP::payload length] "" } # Now send the actual read or write op to the server # It should now have processed the bind TCP::respond $replayop } TCP::release }1.7KViews1like7Comments