Pinhole/Pinpoint DNS
Resident superman and F5er Joel Moses dropped a solution in the wiki several weeks back utilizing an iRule to intercept dns requests and respond with different answers than the name servers would provide. As he states in the wiki entry, one of the use cases for this is to mimic the static host functionality in the BIG-IP Access Policy Manager. This is useful for platforms like iOS that don't support the modification of their internal host files, as well as preventing the need for OSX users from needing to manage their host files manually. I had a need for exactly this use case in my development environment, but a couple things led me to a slightly different approach. One, I don't have the DNS Features license on my BIG-IP Edge Gateway device. Two, the solution Joel crafted is far more robust than I needed. Granted, the DNS services module is far more efficient than my solution as it is completely built-in to TMOS, but for the very limited traffic and clientbase I'll have for this temporary solution, an iRule with binary manipulation makes sense. Overview You can front any dns solution with an iRule like this, but in this case, I created a udp virtual server on port 53 and attached the iRule and the pool of dns services for those requests not matched in the iRule. Then in my APM network access configuration, I make the dns server the virtual I just created. The iRule If you're only going to respond from the iRule with one IP address, F5er Nat (natty76 in the community) posted a pre-CMP version in the wiki quite a while back. This formed the foundation of the iRule I used, just changing the names, ips, and updating for CMP by using the static namespace instead of global variables. The mad science in this iRule is getting the fqdn and the ip in the right format for binary conversion. For www.f5.com, Nat provided the example shown below (updated to static namespace.) # Name = www f5 com set static::answer "0377777702663503636f6d00" # Address = 65.197.145.23 set static::answer "${static::answer}41c59117" Unpacking that a little, the characters and numbers are converted to hex. The IP is simple, just a by-octet conversion from decimal to hexadecimal. For the name, the dns field format requires the character number in advance of the characters themselves, so in hex, www is represented: lencharacters - 03 77 77 77 and with spaces removed, 03777777. This is repeated for each part of the fqdn, and when it is finished, the string needs to be padded with a double zero. If you don't like to make your eyes bleed converting ascii to hex, I wrote a python script to do this for you. Just supply an fqdn and an IP for that name, and it'll output the strings you need for the iRule. #!/usr/bin/python __author__ = 'rahm' def fqdn2hex(fqdn): fqdn_split = fqdn.split('.') hexstring = '' matchstring = '' for x in fqdn_split: hexstring += hex(len(x))[2:].zfill(2) hexstring += x.encode('hex') matchstring += '\\x' + str(len(x)).zfill(2) matchstring += x hexstring += '00' return hexstring, matchstring def ip2hex(ip): ip = ip.split('.') ip = ''.join((hex(int(i))[2:].zfill(2) for i in ip)) return ip import sys if len(sys.argv) != 3: fqdn = raw_input('Enter the FQDN you want to convert to a hex string for DNS response: ') ip = raw_input('Enter the IP you want to convert to a hex string for DNS response: ') else: fqdn = sys.argv[1] ip = sys.argv[2] h_fqdn, m_fqdn = fqdn2hex(fqdn) h_ip = ip2hex(ip) print "\n\n%s, %s, %s" % (fqdn, h_fqdn, m_fqdn) print "\n%s: %s" % (ip, h_ip) This script when run results in the following output: C:\PycharmProjects\scripts>python fqdn2hex.py test.devcentral.local 172.16.31.100 test.devcentral.local, 04746573740a64657663656e7472616c056c6f63616c00, \x04test\x10devcentral\x05local 172.16.31.100: ac101f64 C:\PycharmProjects\scripts>python fqdn2hex.py this.is.fun.com 192.168.50.250 this.is.fun.com, 04746869730269730366756e03636f6d00, \x04this\x02is\x03fun\x03com 192.168.50.250: c0a832fa Now that the data I need to build the iRule is properly formatted, here's the iRule: when RULE_INIT { # Header generation (in hexadecimal) # qr(1) opcode(0000) AA(1) TC(0) RD(1) RA(1) Z(000) RCODE(0000) Question(0001) Answer(0001) No DNS(0000) No Addition(0000) set static::hdr "85800001000100000000" # name_a test.devcentral.local set static::nma "04746573740a64657663656e7472616c056c6f63616c00" # name_b this.is.fun.com set static::nmb "04746869730269730366756e03636f6d00" # Type = A(2)IN(2)TTL(4)Len(2) set static::ans "00010001000151800004" # test.devcentral.local ip = 172.16.31.100 set static::ipa "ac101f64" # this.is.fun.com ip = 192.168.50.250 set static::ipb "0a32019d" } when CLIENT_DATA { binary scan [UDP::payload] H4@12A*@12H* id dname question set dname [string tolower [getfield $dname \x00 1 ] ] switch -glob $dname { "\x04test\x10devcentral\x05local" { set payload [binary format H* ${id}${static::hdr}${question}${static::nma}${static::ans}${static::ipa} ] drop UDP::respond $payload } "\x04this\x02is\x03fun\x03com" { set payload [binary format H* ${id}${static::hdr}${question}${static::nmb}${static::ans}${static::ipb} ] drop UDP::respond $payload } default { #log local0. "does not match" } } } To add new names to the ones I've already included, I would add another static::nm_ and static::ip_ variable with the output from the script for those values, and then duplicate one of the switch conditions, updating that condition with the appropriate match string from the script, then update the variables to the newly created ones. After getting everything setup, it was time to test: C:\PycharmProjects\scripts>nslookup DNS request timed out. timeout was 2 seconds. Default Server: UnKnown Address: x.x.x.x > server x.x.x.x Default Server: [x.x.x.x] Address: x.x.x.x > test.devcentral.local Server: [x.x.x.x] Address: x.x.x.x Name: test.devcentral.local Addresses: 172.16.31.100 172.16.31.100 Looks like all is well! Conclusion Whether you go with a more robust and production-ready solution like Joel's, or meet a quick temporary need with a solution like this, it's great to have the Swiss-army knife toolset that BIG-IP and iRules provide. Technorati Tags: iRules,dns,pinhole dns,python421Views0likes0Comments