Forum Discussion
How to select a pool based on client's hostname?
I need to write an iRule that will select a pool based on the requesting client's DNS hostname. I know an iRule can perform a reverse DNS lookup to get the client's hostname from the source IP address, but I'm not sure how to put it all together.
This sample shows a reverse DNS lookup:
https://devcentral.f5.com/wiki/iRules.Block_requests_by_reverse_DNS_record.ashx
However, being new to iRules I'm not sure the best way to work this into a script to fit my needs. One problem is that the sample code seems to be dependent on HTTP, however I am not concerned with the application layer. My iRule needs to work regardless of the application protocol.
This example assumes there are two data groups (non-prod-list and prod-list), each containing the hostnames of several servers. Does anyone think this will fly? Certainly it has problems.
when CLIENT_ACCEPTED {
Trigger a name lookup for new connections
set do_lookup 1
Check if we haven't done a lookup already on this connection
if { $do_lookup }{
Start a name resolution on the client IP address
NAME::lookup -ptr [IP::client_addr]
}
}
when NAME_RESOLVED {
set ptr [string tolower [NAME::response]]
if { $ptr starts_with $::non-prod-list } {
pool pool_a }
elseif { $ptr starts_with $::prod-list } {
pool pool_b }
}
else pool default_pool
}
- hoolioCirrostratusIf you're on 10.2.1 or higher you should use RESOLV::lookup as it's a simpler command which doesn't require coding in the NAME_RESOLVED event. Also, change the data group lookup from
Check the config options for tmm.resolv.retry and tmm.resolv.timeout on the RESOLV::lookup wiki page! https://devcentral.f5.com/wiki/iRules.resolv__lookup.ashx when RULE_INIT { Log debug to /var/log/ltm? 1=yes, 0=no. set static::dns_debug 1 Use a DNS virtual server for redundancy set static::dns_server my_dns_vs Less optimally, hardcode a DNS server IP address set static::dns_server 4.2.2.2 Data group name which maps the domain names to a pool name The logical data group format is expected to be: Name=domain1.org, value=prod_pool Name=domain1.com, value=non_prod_pool The exact format of the data group depends on the LTM version See these articles for details: v11 - https://devcentral.f5.com/Tutorials/TechTips/tabid/63/articleType/ArticleView/articleId/1086510/v11-iRules-Data-Group-Updates.aspx v10 - https://devcentral.f5.com/Tutorials/TechTips/tabid/63/articleType/ArticleView/articleId/1086448/iRules-Data-Group-Formatting-Rules.aspx set static::ptr_to_pool_dg "ptr_to_pool_dg" } when CLIENT_ACCEPTED { Get PTR for client's IP address set ptr [RESOLV::lookup @$static::dns_server -ptr [IP::client_addr]] if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Resolved $ptr"} Check if a pointer record was returned if {$ptr eq ""}{ No PTR, so use the VS default pool if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: No PTR!"} pool [LB::server pool] } else { Check the data group named ptr_to_pool_dg to get the pool name set pool [class match -value -- $ptr ends_with $static::ptr_to_pool_dg] if {$pool eq ""}{ No match for domain, so use the VS default pool if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: No pool found for $ptr in $static::ptr_to_pool_dg"} pool [LB::server pool] } else { Try to assign the matched pool, but use the default pool if the assignment fails if {[catch {pool $pool} result]}{ if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Error assigning pool $pool. Using default pool [LB::server pool]. Error: $error"} pool [LB::server pool] } } } }
- Ken_B_50116Cirrostratus
Excellent, thanks for the suggestions. My LTMs are running 10.2.1 at this time, so will follow your suggestions. I will review the code and do some testing and post back with the results.
Thanks again!
- Ken_B_50116CirrostratusWhat if I forget the datagroup idea and just use text matching like this:
- hoolioCirrostratusActually, per BZ360270, the -ptr result isn't cached like other resolution responses, so it makes sense to manually cache the result in the session table. I'm working on a revised iRule now.
- Ken_B_50116CirrostratusPosted By hoolio on 06/12/2012 10:27 AM
- hoolioCirrostratusWhat if I forget the datagroup idea and just use text matching like this...
switch -glob -- [string tolower $ptr] { "*wtsappprod" { pool prod_pool } "*wtsapp" { pool preprod_pool } }
- hoolioCirrostratusHere's an untested version that implements caching of the PTR records in the session table. Let me know if you see any issues testing this.
Look up the client IP to map its pointer record suffix to a pool name Use a data group to map domain names to pool names Manually cache pointer records in the session cache as there is a bug (BZ360270) which prevents TMM from caching PTR records as it should. Check the config options for tmm.resolv.retry and tmm.resolv.timeout on the RESOLV::lookup wiki page! https://devcentral.f5.com/wiki/iRules.resolv__lookup.ashx when RULE_INIT { Log debug to /var/log/ltm? 1=yes, 0=no. set static::dns_debug 1 Ideally, use a DNS virtual server to perfor lookups against for redundancy set static::dns_server my_dns_vs ...or less optimally, hardcode a DNS server IP address set static::dns_server 4.2.2.2 Time (in seconds) to cache successful PTR lookups. If the entry is not accessed in this time period a new resolution attempt will be made. This is a workaround for BZ360270 which notes that RESOLV::lookup -ptr lookups are not cached like other records are set static::cache_time 3600 Data group name which maps the domain names to a pool name The logical data group format is expected to be: Name=domain1.org, value=prod_pool Name=domain1.com, value=non_prod_pool The exact format of the data group depends on the LTM version See these articles for details: v11 - https://devcentral.f5.com/Tutorials/TechTips/tabid/63/articleType/ArticleView/articleId/1086510/v11-iRules-Data-Group-Updates.aspx v10 - https://devcentral.f5.com/Tutorials/TechTips/tabid/63/articleType/ArticleView/articleId/1086448/iRules-Data-Group-Formatting-Rules.aspx set static::ptr_to_pool_dg "ptr_to_pool_dg" If the pointer to pool mapping data group does not exist, log an error if {not [class exists $static::ptr_to_pool_dg]}{ log local0.emerg "Required data group $static::ptr_to_pool_dg does not exist! Using VS default pool" } } when CLIENT_ACCEPTED { If the pointer to pool mapping data group does not exist, log an error and exit if {not [class exists $static::ptr_to_pool_dg]}{ log "[IP::client_addr]:[TCP::client_port]: Required data group $static::ptr_to_pool_dg does not exist! Using VS default pool [LB::server pool]" return } Get pointer record for client IP address. Check the session cache first using ptr_1.1.1.1 as the key, where 1.1.1.1 is the client IP address set ptr [table lookup "ptr_[IP::client_addr]"] Check if a pointer record was returned if {$ptr eq ""}{ No cached ptr record so try a new resolution if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: No cached ptr, attempting resolution"} set ptr [RESOLV::lookup @$static::dns_server -ptr [IP::client_addr]] if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Resolved $ptr"} Check if a pointer record was returned if {$ptr eq ""}{ No PTR, so use the VS default pool if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: No ptr from resolution, using default pool, [LB::server pool]"} pool [LB::server pool] Stop processing this event in this rule as there is no PTR return } else { New resolution returned a result so cache it in the session table using ptr_1.1.1.1 as the key where 1.1.1.1 is the client IP address if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Adding new ptr to cache for $static::cache_time seconds: $ptr"} table set "ptr_[IP::client_addr]" $ptr $static::cache_time indefinite } } We have a valid PTR either from cache or new resolution. Check the data group named ptr_to_pool_dg to get the pool name set pool [class match -value -- $ptr ends_with $static::ptr_to_pool_dg] if {$pool eq ""}{ No match for domain, so use the VS default pool if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: No pool found for $ptr in $static::ptr_to_pool_dg"} pool [LB::server pool] } else { Try to assign the matched pool, but use the default pool if the assignment fails if {[catch {pool $pool} result]}{ if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Error assigning pool $pool. Using default pool [LB::server pool]. Error: $error"} pool [LB::server pool] } } }
- Ken_B_50116CirrostratusAgain, thanks for taking the time to work up that code. For the sake of simplicity, I'm going to try a version that avoids needing any data groups. This iRule is focused on matching the hostname and not the domain name, or the end of the FQDN.
when RULE_INIT { Log debug to /var/log/ltm? 1=yes, 0=no. set static::dns_debug 0 set static::dns_server 10.10.9.8 } when CLIENT_ACCEPTED { Get PTR for client's IP address set ptr [RESOLV::lookup @$static::dns_server -ptr [IP::client_addr]] if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: Resolved $ptr"} Check if a pointer record was returned if {$ptr eq ""}{ No PTR, so use the VS default pool if {$static::dns_debug}{log local0. "[IP::client_addr]:[TCP::client_port]: No PTR!"} pool [LB::server pool] } else { switch -glob -- [string tolower $ptr] { "wtscern*" { pool pool_Multum_prod } "wtsc4prod*" { pool pool_Multum_prod } "wtsc4test*" { pool pool_Multum_nonprod } "wtsc4cert*" { pool pool_Multum_nonprod } "wtsc4edu*" { pool pool_Multum_nonprod } "vmqa*" { pool pool_Multum_nonprod } } } }
- Ken_B_50116CirrostratusOh, clearly this is important too:
set static::cache_time 3600
- Ken_B_50116CirrostratusJust a follow up on this. I did end up using my code above, which relies on some key elements that Aaron provided. The script runs well and testing has shown that clients whose hostnames meet certain criteria are sent to the correct pool. Thanks again for the assistance. Hoepfully this code can be of use to others in the future.
Recent Discussions
Related Content
* Getting Started on DevCentral
* Community Guidelines
* Community Terms of Use / EULA
* Community Ranking Explained
* Community Resources
* Contact the DevCentral Team
* Update MFA on account.f5.com