APM AD Site Awareness

Problem this snippet solves:

This example iCall will keep an APM Active Directory AAA object and pool in lockstep with a particular Site construct in an AD domain. It's a periodic script and should probably be run no more than every 30 seconds (although it's fairly lightweight in operation). It requires two configuration entries to be set:

  • ad_site: The name of the Active Directory site whose DCs you want to track (not case-sensitive).
  • ad_aaa: The name of the AAA object to keep in sync; the domain name and pool will be read from the object.

If you change the AD sitename configured in the iCall script, it will repopulate the existing object with the new site's DCs -- making it very easy to retarget your domain controller traffic. It will also notice when DCs are added or removed from an existing site. If someone adds a non-site entry manually to the configuration, it'll remove it.

How to use this snippet:

Implementation Details

This iCall script requires v11.4 or higher.

Code :

sys icall script /Common/f5.ad_aaa_maintainer {
    app-service none
    definition {

#################################################
## ad_aaa object maintainer
## version 1.0 -jm
## - works best with a periodic run of no
##   greater than 30 seconds
##
## CONFIGURATION
##

## Active Directory Site Name
# Set the name of the AD Site whose domain controllers
# you want to track for the AAA object. This name is not
# case sensitive.
set ad_site "seattle"

## Active Directory AAA Object
# Set the name of the AAA object whose pool members you
# wish to maintain.
set ad_aaa "olympus-f5"

##
## MAIN
##
tmsh::log_level notice
tmsh::stateless enabled

# Get the domain and pool from the AAA object.
set ad_domain [tmsh::get_field_value [lindex [tmsh::get_config /apm aaa active-directory $ad_aaa] 0] "domain"]
if { $ad_domain == "" } {
tmsh::log_level alert
tmsh::log "ad_aaa_maintainer: ERROR! This AD AAA object is not configured correctly or does not exist. Please verify your iCall settings."
exit 1
}
set ad_pool [tmsh::get_field_value [lindex [tmsh::get_config /apm aaa active-directory $ad_aaa] 0] "pool"]
if { $ad_pool == "" } {
tmsh::log_level alert
tmsh::log "ad_aaa_maintainer: ERROR! This AD AAA object is not configured as a pool-type object. Please verify your iCall settings."
exit 1
}

# Find the site's members from the Kerberos SRV records.
set ad_ipaddrs [exec -keepnewline -- /usr/bin/dig +short -q _kerberos._tcp.${ad_site}._sites.${ad_domain}. SRV | /bin/cut -c9- | /bin/sort | /bin/sed "s/.\$//"]

# Create an array of site DCs discovered from DNS.
foreach name ${ad_ipaddrs} {
set addr [exec -- /usr/bin/dig +short -q $name A]
set dns_member($name) $addr
}

# If nothing was discovered from DNS, then error out -- someone has
# removed our site. Otherwise, create an array of DCs from the
# AAA object configuration.
if { [array size dns_member] > 0 } {
set ad_config_serverlist [tmsh::get_field_value [lindex [tmsh::get_config /apm aaa active-directory $ad_aaa] 0] "domain-controllers"]
foreach member $ad_config_serverlist {
set config_member([lindex $member 1]) [lindex [lindex $member 2] 1]
}

# Compare the two arrays and find differences.
set ilist1 [array names dns_member]
set ilist2 [array names config_member]
foreach idx $ilist1 {

# If we don't have a config entry for a DNS-discovered DC,
# then it must have joined the site. Add it.
if {![info exists config_member($idx)]} {
lappend retn_list $idx
tmsh::log "ad_aaa_maintainer: Domain Controller joined site $ad_site. Added $idx ip $dns_member($idx)."  
tmsh::modify /apm aaa active-directory $ad_aaa domain-controllers add "{ $idx { ip $dns_member($idx) } }"
tmsh::modify /ltm pool $ad_pool members add "{ $dns_member($idx):any }"
continue
}

# If an existing config entry has a different value for IP
# address than the DNS entry tells us, change it to the new
# value.
if {$dns_member($idx) != $config_member($idx)} {
tmsh::log "ad_aaa_maintainer: Domain Controller changed addresses in site $ad_site. Modified AAA object $ad_aaa : $idx ip $dns_member($idx) - was $config_member($idx)."  
tmsh::modify /apm aaa active-directory $ad_aaa domain-controllers modify "{ $idx { ip $dns_member($idx) } }"
tmsh::modify /ltm pool $ad_pool members remove "{ $config_member($idx):any }"
tmsh::modify /ltm pool $ad_pool members add "{ $dns_member($idx):any }"
lappend retn_list $idx
}
}

# If we have an entry in the config that doesn't appear in
# DNS then the DC has either left the site or someone is adding
# things to the configuration. Remove it from the AAA object
# and pool.
foreach idx $ilist2 {
if {![info exists dns_member($idx)]} {
tmsh::log "ad_aaa_maintainer: Domain Controller left site $ad_site. Deleted $idx from AAA object $ad_aaa."
tmsh::modify /apm aaa active-directory $ad_aaa domain-controllers delete "{ $idx }"
tmsh::modify /ltm pool $ad_pool members delete "{ $config_member($idx):any }"
}
}
} else {
tmsh::log_level alert
tmsh::log "ad_aaa_maintainer: ERROR! No pool members detected. Please check your sitename for errors."
exit 1 
}
##
#################################################
    }
    description none
    events none
}

sys icall handler periodic f5.ad_aaa_task {
interval 30
script f5.ad_aaa_maintainer
}
Published Mar 09, 2015
Version 1.0