dns
23 TopicsF5 BIG-IP iRule based TCL DNS server
Short Description This code is for the purpose if there is a need to return a custom DNS reply not from the main DNS server but from the F5 Virtual Server. Problem solved by this Code Snippet The code is meant for small lab or dev environments as F5 has F5 DNS/GTM for replying to DNS requests with Intelligent Load Balancing, DNS Express Memory cache etc. How to use this Code Snippet Modify the irule code to configure the custom domain you want replies from the irule code and not the real DNS server. Add the irule to a DNS UDP LB Code Snippet Meta Information Version: 17.1 Coding Language: TCL Code You can find the code and further documentation in my GitHub repository: irule_dns/irule1 at main · Nikoolayy1/irule_dns (github.com)103Views0likes0CommentsDNS lite with topology selection
Problem this snippet solves: 19/12/2017 : add lowercase conversion of the requested name to support case insensitive request. BIGIP DNS is a really great product to load balance DNS requests to the best Datacenter. This codes is not a solution to replace the DNS product but to provide a solution to manage small DNS needs. This code isn't full RFC compliant but provide an internal DNS server supporting only A and AAAA query types. The goal of this code was to permit to manage datacenter HA for a single application (exchange in my case). This code search for the preferred network in a data group based on the DNS client IP source. Then, If the request is www.example.com and there is a LTM pool named p_gtm_lite_www.example.com (with virtual servers as members), the active pool members are compared to the preferred network. If no pool member matches the preferred network, the last pool member is selected (no LB is enabled) If the pool does not exist, a NXDOMAIN is answered. How to use this snippet: create a Datagroup named gtm_lite_topology ltm data-group internal gtm_lite_topology { records { 0.0.0.0 { data 192.168.2.0/24 } 192.168.1.0/24 { data 192.168.1.0/24 } } type ip } create a LTM pool with VS as members. don't forget to enable LTM monitor. Note: The pool name must be lowercase. ltm pool p_gtm_lite_www.example.com { members { vs2:https { address 192.168.2.237 } vs1:https { address 192.168.1.237 } } } change domain name in variable static::domain Change the max dns members in response in variable static::dns_max_response Code : when RULE_INIT { set static::DNS_TTL 30 set static::dns_max_response 2 } when CLIENT_ACCEPTED { # Initiate response flags to ANSWER; 16 bits with first set to 1 # qr(1) opcode(0000) AA(1) TC(0) RD(0) RA(0) Z(000) RCODE(0000) ################################################### # DNS RCODE Assignment as defined in RFC 2929 # RCODE Name Description Reference # 0 NoError No Error [RFC 1035] # 1 FormErr Format Error [RFC 1035] # 2 ServFail Server Failure [RFC 1035] # 3 NXDomain Non-Existent Domain [RFC 1035] # 4 NotImp Not Implemented [RFC 1035] # 5 Refused Query Refused [RFC 1035] # 6 YXDomain Name Exists when it should not [RFC 2136] # 7 YXRRSet RR Set Exists when it should not [RFC 2136] # 8 NXRRSet RR Set that should exist does not [RFC 2136] # 9 NotAuth Server Not Authoritative for zone [RFC 2136] # 10 NotZone Name not contained in zone [RFC 2136] # 16 BADVERS Bad OPT Version [RFC 2671] # 16 BADSIG TSIG Signature Failure [RFC 2845] # 17 BADKEY Key not recognized [RFC 2845] # 18 BADTIME Signature out of time window [RFC 2845] # 19 BADMODE Bad TKEY Mode [RFC 2930] # 20 BADNAME Duplicate key name [RFC 2930] # 21 BADALG Algorithm not supported [RFC 2930] set RESPONSE_FLAGS "1000010000000000" # Initiate response resources. each resource type is created in a list allowing multiple resources of same type. set QUESTION_LIST [list]; set ANSWER_LIST [list]; set AUTHORITY_LIST [list]; set ADDITIONAL_LIST [list] # Decode UDP DNS request. If all variables are not set, drop the request if {[binary scan [UDP::payload] SB16SSSSH* ID REQUEST_FLAGS QDCOUNT ANCOUNT NSCOUNT ARCOUNT RESOURCES] < 7} { drop; return } # If ANSWER resource exists or different than 1 question, drop the request if {$ANCOUNT > 0 || $QDCOUNT != 1} { # change RCODE to value 5 (0101) : Refused set RESPONSE_FLAGS [string replace $RESPONSE_FLAGS 12 15 "0101"] } else { # According to RFC, a security-aware name server MUST copy the CD bit from a query into the corresponding response set RESPONSE_FLAGS [string replace $RESPONSE_FLAGS 7 7 [string index $REQUEST_FLAGS 7]] set RESPONSE_FLAGS [string replace $RESPONSE_FLAGS 11 11 [string index $REQUEST_FLAGS 11]] ################################################### ###### extract query name, type and class from resources # DNS Question format as defined in RFC 1035 # - NAME : 255 octets or less containing a sequence of labels (63 octets or less). # - TYPE: two octets containing one of the RR TYPE codes. # - CLASS: two octets containing one of the RR CLASS codes. set QNAME_LIST {} set index 1 set BIN_RESOURCES [binary format H* $RESOURCES] # Read the first label length binary scan $BIN_RESOURCES c label_length while {$index < [string length $BIN_RESOURCES]} { # Read label and the next label length binary scan $BIN_RESOURCES @${index}A${label_length}c label_value next_length lappend QNAME_LIST "$label_value" set index [expr {$index + 1 + $label_length}] set label_length $next_length if {$label_length == 0} { binary scan $BIN_RESOURCES @${index}SS TYPE CLASS #binary scan $BIN_RESOURCES H[expr {$index * 2}] QNAME_HEX # pointer C00C means 12 bytes from ID --> start of Question label set QNAME_HEX "C00C" # extract Question record to insert it in the response binary scan $BIN_RESOURCES H[expr {$index * 2 + 8}] QUESTION_HEX # append Question record in Question RR list lappend QUESTION_LIST $QUESTION_HEX # Join all labels with dot separator. set QNAME [string tolower [join $QNAME_LIST "."]] break } } unset index if {$TYPE == 12 && $QNAME ends_with ".in-addr.arpa"} { if {[scan $QNAME "%d.%d.%d.%d" d c b a] == 4 && [IP::addr "$a.$b.$c.$d" equals [IP::local_addr]] } { set data "" foreach label [split [string tolower [info hostname]] "."] { binary scan [binary format cA* [string length $label] $label] H* tmp append data $tmp } append data "00" log local0. $data binary scan [binary format H*SSISH* $QNAME_HEX ${TYPE} ${CLASS} ${static::DNS_TTL} [expr {[string length $data] / 2}] ${data}] H* HEX_RESOURCE_RECORD ################################################### # Add the resource record to the Answer RR list lappend ANSWER_LIST $HEX_RESOURCE_RECORD log local0. "[IP::client_addr] / $QNAME : [info hostname] added to list" } } elseif {[catch { set pool_members [active_members -list "p_gtm_lite_$QNAME"] }]} { log local0. "error $QNAME record not exists" # change RCODE to value 3 (0011) : NXDomain set RESPONSE_FLAGS [string replace $RESPONSE_FLAGS 12 15 "0011"] } else { #retreive prefered network from Topology datagroup set PREFERED_NETWORK [class match -value [IP::client_addr] equals gtm_lite_topology] set member_addr_list [list] # sort pool member list to put preferred member first foreach pmember $pool_members { if {$PREFERED_NETWORK equals "" || [IP::addr [set pmember_addr [lindex $pmember 0]] equals $PREFERED_NETWORK]} { set member_addr_list [linsert $member_addr_list 0 $pmember_addr] } else { lappend member_addr_list $pmember_addr } } ################################################### # Create answer(s) based on the requested record type # # DNS Resource record format as defined in RFC 1035 # - NAME : 255 octets or less containing a sequence of labels (63 octets or less) or offset pointer. pointer C00C means 12 bytes from ID --> start of Question label # - TYPE: two octets containing one of the RR TYPE codes. # - CLASS: two octets containing one of the RR CLASS codes. # - TTL: 32 bit signed integer # - RDLENGTH: unsigned 16 bit integer that specifies the length in octets of the RDATA field # - RDATA: a variable length string of octets that describes the resource. The format of this information varies according to the TYPE and CLASS of the resource record. switch $TYPE { 1 { # If query type is A foreach item $member_addr_list { if {[llength $ANSWER_LIST] < ${static::dns_max_response}} { #convert IPv4 to hexadecimal if {[scan $item "%d.%d.%d.%d" a b c d] == 4} { set data [format %02x%02x%02x%02x $a $b $c $d] binary scan [binary format H*SSISH* $QNAME_HEX ${TYPE} ${CLASS} ${static::DNS_TTL} [expr {[string length $data] / 2}] ${data}] H* HEX_RESOURCE_RECORD ################################################### # Add the resource record to the Answer RR list lappend ANSWER_LIST $HEX_RESOURCE_RECORD log local0. "[IP::client_addr] / $QNAME : $item added to list" } } else {break} } } 28 { # If query type is AAAA foreach item $member_addr_list { if {[llength $ANSWER_LIST] < ${static::dns_max_response}} { #convert IPv6 to hexadecimal (add missing 0, remove colons) if {$item contains ":"} { if {$item contains "::"} { set ipv6_begin "" foreach val [split [getfield $item "::" 1] ":"] {append ipv6_begin [format %04x 0x$val]} set ipv6_end "" foreach val [split [getfield $item "::" 2] ":"] { append ipv6_end [format %04x 0x$val]} set data "$ipv6_begin[format %0[expr {32 - [string length $ipv6_begin$ipv6_end]}]x 0]$ipv6_end" } else { set data "" foreach val [split $item ":"] {append data [format %04x 0x$val]} } binary scan [binary format H*SSISH* $QNAME_HEX ${TYPE} ${CLASS} ${static::DNS_TTL} [expr {[string length $data] / 2}] ${data}] H* HEX_RESOURCE_RECORD ################################################### # Add the resource record to the Answer RR list lappend ANSWER_LIST $HEX_RESOURCE_RECORD log local0. "[IP::client_addr] / $QNAME : $item added to list" } } else {break} } } } } } ################################################### # DNS Message format as defined in RFC 1035 # - Header : Header section # - ID: A 16 bit identifier assigned by the program that generates any kind of query. # - Flags: Flags containing: #- QR: 1 bit : A one bit field that specifies whether this message is a query (0), or a response (1). #- Opcode: 4 bits : A four bit field that specifies kind of query in this message #- AA: 1 bit : Authoritative Answer #- TC: 1 bit : TrunCation - specifies that this message was truncated due to length greater than that permitted on the transmission channel. #- RD: 1 bit : Recursion Desired #- RA: 1 bit : Recursion Available #- Z: 3 bits : Reserved for future use. Must be zero in all queries and responses. #- RCODE : 4 bits : Response code - this 4 bit field is set as part of responses. 0 for NOERROR # - QDCOUNT: unsigned 16 bit integer specifying the number of entries in the question section. # - ANCOUNT: unsigned 16 bit integer specifying the number of entries in the answer section. # - NSCOUNT: unsigned 16 bit integer specifying the number of entries in the authority records section. # - ARCOUNT: unsigned 16 bit integer specifying the number of entries in the additional records section. # - Question: a possibly empty list of concatenated resource records (RRs) # - Answer: a possibly empty list of concatenated resource records (RRs) # - Authority: a possibly empty list of concatenated resource records (RRs) # - Additional: a possibly empty list of concatenated resource records (RRs) drop UDP::respond [binary format SB16SSSSH* ${ID} $RESPONSE_FLAGS [llength $QUESTION_LIST] [llength $ANSWER_LIST] [llength $AUTHORITY_LIST] [llength $ADDITIONAL_LIST] [join ${QUESTION_LIST} ""][join ${ANSWER_LIST} ""][join ${AUTHORITY_LIST} ""][join ${ADDITIONAL_LIST} ""]] } Tested this on version: 11.6385Views0likes0CommentsExport GTM/DNS Configuration in CSV - tmsh cli script
Problem this snippet solves: This is a simple cli script used to collect all the WideIP, LB Method, Status, State, Pool Name, Pool LB, Pool Members, Pool Fall back, Last Resort pool info in CSV format. A sample output would be like below, One can customize the code to extract other fields available too. Check out my other codeshare of LTM report. Note: The codeshare may get multiple version, use the latest version alone. The reason to keep the other versions is for end users to understand & compare, thus helping them to modify to their own requirements. Hope it helps. How to use this snippet: Login to the GTM/DNS, create your script by running the below commands and paste the code provided in snippet, tmsh create cli script gtm-config-parser Delete the proc blocks, so it looks something like below, create script gtm-config-parser { ## PASTE THE CODE HERE ## } and paste the code provided in the snippet. Note: When you paste it, the indentation may be realigned, it shouldn't cause any errors, but the list output would show improperly aligned. Feel free to delete the tab spaces in the code snippet & paste it while creating, so indentation is aligned properly. And you can run the script like below, tmsh run cli script gtm-config-parser > /var/tmp/gtm-config-parser-output.csv And get the output from the saved file, open it on excel. Format it & use it for audit & reporting. cat /var/tmp/gtm-config-parser-output.csv Feel free to add more elements as per your requirements. For version 13.x & higher, there requires a small change in the code. Refer the comments section. Thanks to @azblaster Code : proc script::run {} { puts "WIP,LB-MODE,WIP-STATUS,WIP-STATE,POOL-NAME,POOL-LB,POOL-MEMBERS,POOL-FB,LASTRESORT-POOL" foreach { obj } [tmsh::get_config gtm wideip all-properties] { set wipname [tmsh::get_name $obj] set wippools [tmsh::get_field_value $obj pools] set lbmode [tmsh::get_field_value $obj "pool-lb-mode"] set lastresort [tmsh::get_field_value $obj "last-resort-pool"] foreach { status } [tmsh::get_status gtm wideip $wipname] { set wipstatus [tmsh::get_field_value $status "status.availability-state"] set wipstate [tmsh::get_field_value $status "status.enabled-state"] } foreach wippool $wippools { set pool_name [tmsh::get_name $wippool] set pool_configs [tmsh::get_config /gtm pool $pool_name all-properties] foreach pool_config $pool_configs { set pool_lb [tmsh::get_field_value $pool_config "load-balancing-mode"] set pool_fb [tmsh::get_field_value $pool_config "fallback-mode"] if { [catch { set member_name [tmsh::get_field_value $pool_config "members" ]} err] } { set pool_member $err } else { set pool_member "" set member_name [tmsh::get_field_value $pool_config "members"] foreach member $member_name { append pool_member "[lindex $member 1] " } } puts "$wipname,$lbmode,$wipstatus,$wipstate,$pool_name,$pool_lb,$pool_member,$pool_fb,$lastresort" } } } } Tested this on version: 11.63.7KViews2likes6CommentsExport GTM/DNS Virtual Servers Configuration in CSV - tmsh cli script
Problem this snippet solves: This is a simple cli script used to collect all the virtual-servers name, its destination created in a server or ltm server. A sample output would be like below, How to use this snippet: This is similar to my other share - https://devcentral.f5.com/s/articles/Export-GTM-DNS-Configuration-in-CSV-tmsh-cli-script Login to the GTM/DNS, create your script by running the below commands and paste the code provided in snippet, tmsh create cli script gtm-vs Delete the proc blocks, so it looks something like below, create script gtm-vs { ## PASTE THE CODE HERE ## } and paste the code provided in the snippet. Note: When you paste it, the indentation may be realigned, it shouldn't cause any errors, but the list output would show improperly aligned. Feel free to delete the tab spaces in the code snippet & paste it while creating, so indentation is aligned properly. And you can run the script like below, tmsh run cli script gtm-vs > /var/tmp/gtm-vs-output.csv And get the output from the saved file, open it on excel. Format it & use it for audit & reporting. cat /var/tmp/gtm-vs-output.csv Feel free to add more elements as per your requirements. Code : proc script::run {} { puts "Server,Virtual-Server,Destination" foreach { obj } [tmsh::get_config gtm server] { set server [tmsh::get_name $obj] foreach { vss } [tmsh::get_config gtm server $server virtual-servers] { set vs_set [tmsh::get_field_value $vss virtual-servers] foreach vs $vs_set { set vs_name [tmsh::get_name $vs] puts $server,$vs_name,[tmsh::get_field_value $vs destination] } } } } Tested this on version: 13.11.5KViews3likes2CommentsBIG-IP DNS Express - Private Zone Blocker
Problem this snippet solves: With BIG-IP DNS; you cannot enable/disable configured DNS Express Zones on a per-listener basis. This makes scenarios where a single BIG-IP DNS system has listeners exposed to internal networks with RFC1918 addresses and public Internet networks. DNS Express doesn't support DNS Views, in short. This iRule allows you to configure a datagroup containing "disabled_zones" and the iRule will validate if the query matches a zone listed in disabled_zones. If it gets a match, it simply returns nothing. Additionally, the iRule examines all responses and checks that resource records in the response do not contain RFC1918 addresses and if it finds them, it removes those Resource Records. All code in the "DNS_RESPONSE" event can be commented or deleted if this behavior isn't desired. How to use this snippet: enable iRule on DNS listener (most likely a listener available only to private network clients) and configure "disabled_zones" data group as shown in example. Log lines can be deleted or commented out once proper operation of the rule is confirmed and understood or retained for purposes of auditing queries that are dropped/blocked. Code : when DNS_REQUEST { log "Got request from: [IP::remote_addr] for [DNS::question name]" if {[class match [DNS::question name] ends_with disabled_zones]}{ log "Query for [DNS::question name] is for a disabled zone - Dropping" DNS::return } } when DNS_RESPONSE { log "Got Response = [DNS::answer]" set rrs [DNS::answer] set privateresponse 0 foreach rr $rrs { log "DNS Response rr: $rr" if {[DNS::type $rr] == "A"}{ if {[class match [DNS::rdata $rr] equals private_net]}{ set privateresponse 1 DNS::answer remove $rr } log "DNS RR data: [DNS::rdata $rr]" } } if {$privateresponse}{ log "DNS response contains private addresses" } } Tested this on version: 13.0502Views0likes1CommentDNS Blackhole
Problem this snippet solves: The blackhole requirement is to intercept DNS requests for prohibited FQDNs, not sent those to BIND for recursive look-up, return a DNS response with an A record to an LTM virtual server, and have a LTM virtual server with a second iRule that will log the request and serve a static page. The solution uses an iRule to the listener virtual server. This virtual server processes all GTM/BIND traffic. Incoming requests are matched against an external data group that contains a list of prohibited FQDNs. This data group file can be edited directly in the GUI at System - File Management - Data Group File List (line terminator should be LF only, not CR-LF). Alternately, the file can be edited manually and re-loaded by doing a "tmsh load sys config verify" then "tmsh load sys config". The blackhole iRule will log all requests for prohibited FQDNs and return a DNS response that matches an LTM virtual server. The blackhole iRule only provides valid responses for A records, however all blackhole DNS requests are logged. How to use this snippet: Create the list of FQDNs for the BIG-IP external data group. Example format is below. File can be stored in /config/Blackhole_Class, although v11.1 provides a method to upload via GUI which is recommended. The second field is the reason why the site was added to the Blackhole class. ".3322.org" := "virus", ".3322.com" := "malware", ".3322.net" := "phishing", Make the external file accessible On Internal-GTM's GUI, go to System - File Management - Data Group File List - Import. File Name: upload Blackhole_Class file Name: Blackhole_Class File Contents: String Key / Value Pair Separator: := Create data group as external file: On Internal-GTM's GUI, go to Local Traffic - iRules - Data Group List - Create Name: Blackhole_Class Type: (External File) Path/Filename: Blackhole_Class File Contents: string Access Mode: Read Only Create the Blackhole iRule On Internal-GTM's GUI, go to Local Traffic - iRules - Create. Name: DNS_blackhole (can be renamed) iRule Source is attached below. Apply the Blackhole iRule to the GTM listener virtual server. Go to Local Traffic - Virtual Servers. Click on the Virtual Server created automatically for the GTM listener. Name will be of form: vs_10_1_1_152_53_gtm, where the first 4 numbers are the IP address of the Listener. Go to the resources tab and assign the DNS_Blackhole iRule created above. Create iFile for the Organization's Logo that will be used with the block page. Download a copy of the image to be used Rename file to corp-logo.gif (optional) Go to System - File Management - iFile List - Import Name: corp-logo.gif Go to Local Traffic - iRules - iFile List - Create Name: corp-logo.gif File name: Create iRule to log client requests and send an HTML page to notify customers they have violated the Blackhole. This iRule should be copied from the attached file. Go to Local Traffic - iRules - Create Name: DNS_blackhole_block_page iRule Source attached below. Create virtual server for client requests. The IP address should match the DNS response defined in ::blackhole_reply in the DNS_Blackhole iRule. Go to Local Traffic - Virtual Servers - Create Name: DNS_blackhole_block_page Destination: 10.1.1.80 (update per local requirements) Port: 80 HTTP profile: http iRules: DNS_blackhole_block_page Code : # DNS Blackhole # This iRule interrogates all queries that arrive on the GTM listener. If the query matches # a list of prohibited FQDNs, a standardized response is given and the request is logged. # This response IP address could be a honeypot server or an LTM virtual server. # Blackhole functionality can be used to prevent malware, virus C2 servers, adware, or other sites. # # Author: Hugh O'Donnell, F5 Consulting # # Usage: # 1) apply to GTM listener virtual server that is defined at Local Traffic - Virtual Servers # 2) create a string data group called "Blackhole_Class". The FQDNs must start with a period. # 3) update the answer static variable with the IP address to return # # Known Issues: # 1) Only A and AAAA records are returned for blackhole requests. The response for other request # types will be logged and returned with no answer. # # Revision history: # 12-11-2011: Initial Revision when RULE_INIT { # Set IPV4 address that is returned for Blackhole matches for A records set static::blackhole_reply_IPV4 "10.1.1.26" # Set IPV6 address that is returned for Blackhole matches for AAAA records set static::blackhole_reply_IPV6 "2001:19b8:101:2::f5f5:1d" # Set TTL used for all Blackhole replies set static::blackhole_ttl "300" } when DNS_REQUEST { # debugging statement see all questions and request details # log -noname local0. "Client: [IP::client_addr] Question:[DNS::question name] Type:[DNS::question type] Class:[DNS::question class] Origin:[DNS::origin]" # Blackhole_Match is used to track when a Query matches the blackhole list # Ensure it is always set to 0 or false at beginning of the DNS request set Blackhole_Match 0 # Blackhole_Type is used to track why this FQDN was added to the Blackhole_Class set Blackhole_Type "" # When the FQDN from the DNS Query is checked against the Blackhole class, the FQDN must start with a # period. This ensures we match a FQDN and all names to the left of it. This prevents against # malware that dynamically prepends characters to the domain name in order to bypass exact matches if {!([DNS::question name] == ".")} { set fqdn_name .[DNS::question name] } if { [class match $fqdn_name ends_with Blackhole_Class] } { # Client made a DNS request for a Blackhole site. set Blackhole_Match 1 set Blackhole_Type [class match -value $fqdn_name ends_with Blackhole_Class ] # Prevent processing by GTM, DNS Express, BIND and GTM Listener's pool. # Want to ensure we don't request a prohibited site and allow their server to identify or track the GTM source IP. DNS::return } } when DNS_RESPONSE { # debugging statement to see all questions and request details # log -noname local0. "Request: $fqdn_name Answer: [DNS::answer] Origin:[DNS::origin] Status: [DNS::header rcode] Flags: RD [DNS::header rd] RA [DNS::header ra]" if { $Blackhole_Match } { # This DNS request was for a Blackhole FQDN. Take different actions based on the request type. switch [DNS::question type] { "A" { # Clear out any DNS responses and insert the custom response. RA header = recursive answer DNS::answer clear DNS::answer insert "[DNS::question name]. $static::blackhole_ttl [DNS::question class] [DNS::question type] $static::blackhole_reply_IPV4" DNS::header ra "1" # log example: Apr 3 14:54:23 local/tmm info tmm[4694]: # Blackhole: 10.1.1.148#4902 requested foo.com query type: A class IN A-response: 10.1.1.60 log -noname local0. "Blackhole: [IP::client_addr]#[UDP::client_port] requested [DNS::question name] query type: [DNS::question type] class [DNS::question class] A-response: $static::blackhole_reply_IPV4 BH type: $Blackhole_Type" } "AAAA" { # Clear out any DNS responses and insert the custom response. RA header = recursive answer DNS::answer clear DNS::answer insert "[DNS::question name]. $static::blackhole_ttl [DNS::question class] [DNS::question type] $static::blackhole_reply_IPV6" DNS::header ra "1" # log example: Apr 3 14:54:23 local/tmm info tmm[4694]: # Blackhole: 10.1.1.148#4902 requested foo.com query type: A class IN AAAA-response: 2001:19b8:101:2::f5f5:1d log -noname local0. "Blackhole: [IP::client_addr]#[UDP::client_port] requested [DNS::question name] query type: [DNS::question type] class [DNS::question class] AAAA-response: $static::blackhole_reply_IPV6 BH type: $Blackhole_Type" } default { # For other record types, e.g. MX, NS, TXT, etc, provide a blank NOERROR response DNS::last_act reject # log example: Apr 3 14:54:23 local/tmm info tmm[4694]: # Blackhole: 10.1.1.148#4902 requested foo.com query type: A class IN unable to respond log -noname local0. "Blackhole: [IP::client_addr]#[UDP::client_port] requested [DNS::question name] query type: [DNS::question type] class [DNS::question class] unable to respond BH type: $Blackhole_Type" } } } } # DNS Blackhole Block Page # # This iRule presents an HTML page to the user and logs details of the HTML request. # It is used in conjuction with the DNS Blackhole iRule to ensure users are aware that # their request was blocked by the Blackhole and actions to take if this block was in error. # # Author: Hugh O'Donnell, F5 Consulting # # Usage: # 1) Add the corporate logo that will be displayed to System - File Management - iFile List # 2) Make the iFile accessible to be used in this iRule by giving associating the iFile name corp_logo_gif # with the iFile at: Local Traffic - iRules - iFile List. # 3) Ensure list of FQDNs in the DNS Blackhole is in a data group called "Blackhole_Class". This can be # verified at Local Traffic - iRules - Data Group List. # 4) Apply this iRule to the port 80 virtual server that the Blackhole iRule sends out, which is specified in variables # static::blackhole_reply_IPV4 and static::blackhole_reply_IPV6 in v11.1+ version of Blackhole iRule when HTTP_REQUEST { # the static HTML pages include the logo that is referenced in HTML as corp-logo.gif # intercept requests for this and reply with the image that is stored in an iFile defined in RULE_INIT below if {[HTTP::uri] ends_with "/_maintenance-page/corp-logo.gif" } { # Present HTTP::respond 200 content $static::corp_logo_gif } else { # Request for Blackhole webpage. Identify what type of block was in place switch -glob [class match -value ".[HTTP::host]" ends_with Blackhole_Class ] { "virus" { set block_reason "Virus site" } "phishing" { set block_reason "Phishing site" } "generic" { set block_reason "Unacceptable Usage" } default { set block_reason "Denied Per Policy - Other Sites" } } # Log details about the blackhole request to the remote syslog server log -noname local0. "Blackhole: From [IP::client_addr]:[TCP::client_port] \ to [IP::local_addr]:[TCP::local_port], [HTTP::request_num], \ [HTTP::method],[HTTP::uri],[HTTP::version], [HTTP::host], [HTTP::header value Referer], \ [HTTP::header User-Agent], [HTTP::header names],[HTTP::cookie names], BH category: $block_reason," # Send an HTML page to the user. The page is defined in the RULE_INIT event below HTTP::respond 200 content "$static::block_page [HTTP::host][HTTP::uri] $static::after_url $block_reason $static::after_block_reason " } } when RULE_INIT { # load the logo that was stored as an iFile set static::corp_logo_gif [ifile get "/Common/corp-logo.gif"] # Beginning of the block page set static::block_page " Web Access Denied - Enterprise Network Operations Center <!-- .mainbody { background-color: #C0C0C0; color: #000000; font-family: Verdana, Geneva, sans-serif; font-size: 12px; margin: 0px; padding: 20px 0px 20px 0px; position: relative; text-align: center; width: 100%; } .bdywrpr { width:996px; height:auto; text-align:left; margin:0 auto; z-index:1; position: relative; } #banner-wrapper { width: 950px; padding: 0px; margin: 0px; overflow:hidden; background-color: #000033; background-repeat: no-repeat; } #banner-image { float: left; margin-left: auto; margin-right: auto; padding: 3px 0px 2px 7px; width: 950px; } #textbody { background-color: #FFFFFF; color: #000000; font-family: Verdana, Geneva, sans-serif; font-size: 13px; width: 950px; padding:0px; text-align:justify; margin: 0px; } --> Access has been denied. URL: " set static::after_url " Your request was denied because it is blacklisted in DNS. Blacklist category: " set static::after_block_reason " The Internet Gateways are for official use only. Misuse violates policy. If you believe that this site is categorized incorrectly, and that you have a valid business reason for access to this site please contact your manager for approval and the Enterprise Network Operations Center via E-mail: enoc@example.com Please use the Web Access Request Form and include a business justification. Only e-mail that originates from valid internal e-mail addresses will be processed. If you do not have a valid e-mail address, your manager will need to submit a request on your behalf. Generated by bigip1.f5.com. " }1.5KViews0likes1CommentGTM Translation
Problem this snippet solves: When GTM responds with the IP address of the virtual server, there are different NAT translations that must be performed depending on where the request is coming from. This iRule looks at 4 classes (DataGroups) to determine which (If any) of the NAT translations it must perform on the response. This iRule differs from the pinhole example by only examining the response and the 'client' IP address. The hostname requested is ignored so there is only 1 event that needs to fire. Note that although developed for GTM, this is an LTM iRule and is attached to the LTM VS that is created by the GTM when you configure a listener address How to use this snippet: These data-groups are required but MAY be empty if no translations are necessary. local_address_class (type: address) - Holds a list of networks and their type (string internal or 3dp) gte_translate_3dp (type: address) - Translations for 3dp networks gte_translate_internet (type: address) - Translations for the internet gte_translate_internal (type: address) - Translations for internal addresses Code : timing on # gte_translate_external # # Hamish Marson (hamish@travellingkiwi.com) 2011 # # When GTM responds with the IP address of the virtual server, there are different NAT translations that # must be performed depending on where the request is coming from. This iRule looks at 4 classes (DataGroups) # to determine which (If any) of the NAT translations it must perform on the response. # # CLASS Type Description # local_address_class Address - Holds a list of networks and their type. Type is a string internal or 3dp # gte_translate_3dp Address - Translations for 3dp networks # gte_translate_internet Address - Translations for The Internet # gte_translate_internal Address - Translations for Internal addresses # # The translation dataGroups MUST exist. But MAY be empty if no translations are requirted (e.g. For Internal) # # Extensions... Current.y hardcoded for 3 types of address (internal, 3dp or (default) internet. This could be # extended by building the translation class name from the value returned from local_address_class. However extra # error checking would also be required to take into account mis-configuration of the local_address_class dataGroup # to make non-existant dataGroup names. # when DNS_RESPONSE { set GTE_CLASS_INTERNET "gte_translate_internet" set GTE_CLASS_INTERNAL "gte_translate_internal" set GTE_CLASS_3DP "gte_translate_3dp" set LOCAL_CLASS_ADDRESS "local_address_class" set gteHSL [HSL::open -proto UDP -pool hsl-log-01] set gteLogPrefix "<190>[virtual]:gte_translate_external:[IP::client_addr]:0:" # Grab the answer from GTM set rrs [DNS::answer] #HSL::send $gteHSL "$gteLogPrefix: checking DNS::answers" foreach rr $rrs { #HSL::send $gteHSL "$gteLogPrefix: checking RR type ==> [DNS::type $rr]" # Lookup the IP address returned in the GTE_CLASS if { [DNS::type $rr] == "A" } { set dstip [DNS::rdata $rr] #HSL::send $gteHSL "$gteLogPrefix: checking A data ==> [DNS::rdata $rr] ($dstip)" set gteAddClass [class match -value [IP::client_addr] equals $LOCAL_CLASS_ADDRESS ] switch -exact $gteAddClass { internal { set transIP [class match -value $dstip equals $GTE_CLASS_INTERNAL] } 3dp { set transIP [class match -value $dstip equals $GTE_CLASS_3DP] } default { set transIP [class match -value $dstip equals $GTE_CLASS_INTERNET] } } if { $transIP ne "" } { HSL::send $gteHSL "$gteLogPrefix: translate ($gteAddClass) $dstip => $transIP" DNS::rdata $rr $transIP return } else { HSL::send $gteHSL "$gteLogPrefix: no match for ($gteAddClass) $dstip" return } } } }744Views0likes6CommentsDNS Request and Response Filtering using URL DB and IPI subscriptions
Problem this snippet solves: This iRule solution is applied to a DNS resolver or a catch all (0.0.0.0/0:53) DNS virtual server where the BIG-IP is a default gateway. It allows the BIG-IP to explicitly or transparently intercept all DNS Requests and Responses from the client and apply security filtering controls. This solution is suited to almost any outbound DNS scenario where you need to protect the client from accessing malicious threats or undesirable content intentionally or unintentionally. One example where you may find this solution handy, is on a Guest or BYOD network where you need a transparent method of adding security when you don’t have control of the client. How to use this snippet: DNS Interception: Protecting the Client Code : # Author: Brett Smith @f5.com # # This iRule is applied to a DNS resolver (DNS Profile required) or a catch all (0.0.0.0/0:53) DNS virtual server where the BIG-IP is a default GW. # # Configurable Parameters for DNS_REQUEST filtering: # 1. DNS Request Filtering On or Off - Enable or Disable all DNS_REQUEST filtering. # 2. URL Categories e.g. Adult_Content, Hacking. If the DNS Question (FQDN - e.g. playboy.com) matches a category in the data group (default: dns_request_url_categories_dg), # NXDOMAIN will be returned in the response. To obtain a list of possible URL Categories and their descriptions, run: tmsh list sys url-db url-category { description } # 3. DNS Question Type e.g. A, AAAA, ANY etc. Only the Question Types configured in the data group (default: dns_request_question_type_dg) will be filtered. # 4. FQDN/TLD Whitelist e.g. f5.com or .gov. Any FQDN/Domain in the whitelist data group will bypass DNS_REQUEST filtering regardless of the Question Type or URL Category. # # Configurable Parameters for DNS_RESPONSE filtering: # 1. DNS Response Filtering On or Off - Enable or Disable all DNS_RESPONSE filtering. # 2. IP/Subnet Whitelist e.g 192.168.0.0/16 or 1.1.1.1. Any IP or IP Subnet in the whitelist data group will bypass DNS_RESPONSE filtering. # 3. IPI Threat Categories e.g. Spam Sources, Phishing. If the DNS RDATA (A & AAAA only) matches a category in the data group, NXDOMAIN will be returned in the response. # # Global Parameters # 1. Logging Control - Off, Level 1 (NXDOMAIN and Whitelist Matching) and Level 2 (All DNS Requests & Responses) when RULE_INIT { # DNS Request filtering - URL DB control. # 0 = DNS Request filter off, 1 = DNS Request filter on. set static::dns_request_filter 1 # URL categories data group. Any categories (Bot_Networks, etc) in this DG will be blocked based on the DNS question name. set static::dns_request_url_categories_dg "dns_request_url_categories_dg" # DNS request questions type data group. Question types (A,AAAA,etc) in the DNS request that match the DG will be sent for URL category filtering. set static::dns_request_question_type_dg "dns_request_question_type_dg" # FQDN whitelist data group. Any FQDN/Domain in this DG will bypass DNS request URL category filtering. set static::dns_request_fqdn_whitelist_dg "dns_request_fqdn_whitelist_dg" # DNS Response filtering - IP intelligence (reputation) control. # 0 = DNS Response filter off, 1 = DNS Response filter on. set static::dns_response_filter 1 # IP address whitelist data group. Any IP or IP Subnet in this DG will bypass DNS response filtering regardless of the IP intelligence (reputation) value. # If a AAAA record is contained in the Resource Record (RR), it is also returned in the DNS response. set static::dns_response_ip4_whitelist_dg "dns_response_ip_whitelist_dg" # IP intelligence categories data group. If the DNS RDATA (A & AAAA only) matches a category in the data group, NXDOMAIN will be returned in the response. set static::dns_response_ipi_categories_dg "dns_response_ipi_categories_dg" # Debug logging control. # 0 = logging off, 1 = informational logging, 2 = debug logging set static::debug_dns 2 } when DNS_REQUEST { if { $static::debug_dns >= 2 } { log local0. "Client IP: [IP::client_addr], Question: [DNS::question name], Type: [DNS::question type]" } # If DNS Request filtering is enabled, filter the request. if { $static::dns_request_filter } { # If the Question Name (eg. f5.com) is in the Whitelist, bypass URL filtering. if { ![class match [DNS::question name] ends_with $static::dns_request_fqdn_whitelist_dg] } { # If the Question Type matches, filter the request. if { [class match [DNS::question type] equals $static::dns_request_question_type_dg] } { # Determine the URL Category for the Question Name. set url_category [lindex [CATEGORY::lookup http://[DNS::question name]] 0] # If the URL Category is matched, return NXDOMAIN to the client. if { [class match $url_category ends_with $static::dns_request_url_categories_dg] } { # URL Category matched - Log. if { $static::debug_dns >= 1 } { log local0. "Client IP: [IP::client_addr], Question: [DNS::question name], Type: [DNS::question type], URL Category: $url_category - Respond with NXDOMAIN" } # Return NXDOMAIN to the client. DNS::answer clear DNS::header opcode QUERY DNS::header rcode NXDOMAIN DNS::return } } } else { # FQDN/TLD Whitelist matched - Log. if { $static::debug_dns >= 1 } { log local0. "Client IP: [IP::client_addr], Question: [DNS::question name], Type: [DNS::question type], FQDN/TLD Whitelist: Match Found - Bypass Request Filtering" } } } } when DNS_RESPONSE { # If Response Filter is enabled, filter the response. if { $static::dns_response_filter } { set threat_categories "" if { [DNS::ptype] eq "ANSWER" } { # Loop through each Resource Record(s). foreach rr [DNS::answer] { switch [DNS::type $rr] { "A" { # If the IP is not in the whitelist, filter the response. if { ![class match [DNS::rdata $rr] equals $static::dns_response_ip4_whitelist_dg] } { set threat_categories [IP::reputation [DNS::rdata $rr]] # If a Threat Category is returned and matches, filter the request. if { [class match $threat_categories contains $static::dns_response_ipi_categories_dg] } { # Threat Category matched - Log. if { $static::debug_dns >= 1 } { log local0. "Client IP: [IP::client_addr], Question: [DNS::question name], Type: A, Threat Category: $threat_categories - Respond with NXDOMAIN" } # Return NXDOMAIN to the client. DNS::answer clear DNS::header opcode QUERY DNS::header rcode NXDOMAIN } } else { # IP Whitelist matched - Log. if { $static::debug_dns >= 1 } { log local0. "Client IP: [IP::client_addr], Question: [DNS::question name], Type: [DNS::question type], IP Whitelist: Match Found - Bypass Response Filtering" } } } "AAAA" { # As IP::reputation doesn't support IPv6, use the Threat Category response from the A record. i.e. If the A record is bad assume the AAAA record is also bad. if { [class match $threat_categories contains $static::dns_response_ipi_categories_dg] } { # Threat Category matched - Log. if { $static::debug_dns >= 1 } { log local0. "Client IP: [IP::client_addr], Question: [DNS::question name], Type: AAAA, Threat Category: $threat_categories - Respond with NXDOMAIN" } # Return NXDOMAIN to the client. DNS::answer clear DNS::header opcode QUERY DNS::header rcode NXDOMAIN } } } } } } # Log the DNS response if { $static::debug_dns >= 2 } { log local0. "Client IP: [IP::client_addr], Answer: [DNS::answer], RCODE: [DNS::header rcode]" } } #Data Groups: create ltm data-group internal dns_request_url_categories_dg type string modify ltm data-group internal dns_request_url_categories_dg records add {"Adult_Content"} modify ltm data-group internal dns_request_url_categories_dg records add {"Advanced_Malware_Command_and_Control"} modify ltm data-group internal dns_request_url_categories_dg records add {"Advanced_Malware_Payloads"} modify ltm data-group internal dns_request_url_categories_dg records add {"Bot_Networks"} modify ltm data-group internal dns_request_url_categories_dg records add {"Compromised_Websites"} modify ltm data-group internal dns_request_url_categories_dg records add {"Elevated_Exposure"} modify ltm data-group internal dns_request_url_categories_dg records add {"Emerging_Exploits"} modify ltm data-group internal dns_request_url_categories_dg records add {"Files_Containing_Passwords"} modify ltm data-group internal dns_request_url_categories_dg records add {"Hacking"} modify ltm data-group internal dns_request_url_categories_dg records add {"Keyloggers"} modify ltm data-group internal dns_request_url_categories_dg records add {"Malicious_Embedded_Link"} modify ltm data-group internal dns_request_url_categories_dg records add {"Malicious_Embedded_iFrame"} modify ltm data-group internal dns_request_url_categories_dg records add {"Malicious_Web_Sites"} modify ltm data-group internal dns_request_url_categories_dg records add {"Militancy_and_Extremist"} modify ltm data-group internal dns_request_url_categories_dg records add {"Mobile_Malware"} modify ltm data-group internal dns_request_url_categories_dg records add {"Newly_Registered_Websites"} modify ltm data-group internal dns_request_url_categories_dg records add {"Nudity"} modify ltm data-group internal dns_request_url_categories_dg records add {"Peer-to-Peer_File_Sharing"} modify ltm data-group internal dns_request_url_categories_dg records add {"Phishing_and_Other_Frauds"} modify ltm data-group internal dns_request_url_categories_dg records add {"Proxy_Avoidance"} modify ltm data-group internal dns_request_url_categories_dg records add {"Sex"} modify ltm data-group internal dns_request_url_categories_dg records add {"Spyware"} modify ltm data-group internal dns_request_url_categories_dg records add {"Tasteless"} modify ltm data-group internal dns_request_url_categories_dg records add {"Web_and_Email_Spam"} create ltm data-group internal dns_request_question_type_dg type string modify ltm data-group internal dns_request_question_type_dg records add {"A"} modify ltm data-group internal dns_request_question_type_dg records add {"AAAA"} modify ltm data-group internal dns_request_question_type_dg records add {"ANY"} modify ltm data-group internal dns_request_question_type_dg records add {"CNAME"} modify ltm data-group internal dns_request_question_type_dg records add {"MX"} create ltm data-group internal dns_request_fqdn_whitelist_dg type string modify ltm data-group internal dns_request_fqdn_whitelist_dg records add {"f5.com"} create ltm data-group internal dns_response_ip_whitelist_dg type ip modify ltm data-group internal dns_response_ip_whitelist_dg records add {"10.0.0.0/8"} modify ltm data-group internal dns_response_ip_whitelist_dg records add {"172.16.0.0/12"} modify ltm data-group internal dns_response_ip_whitelist_dg records add {"192.168.0.0/16"} create ltm data-group internal dns_response_ipi_categories_dg type string modify ltm data-group internal dns_response_ipi_categories_dg records add {"BotNets"} modify ltm data-group internal dns_response_ipi_categories_dg records add {"Networks"} modify ltm data-group internal dns_response_ipi_categories_dg records add {"Denial of Service"} modify ltm data-group internal dns_response_ipi_categories_dg records add {"Illegal"} modify ltm data-group internal dns_response_ipi_categories_dg records add {"Infected Sources"} modify ltm data-group internal dns_response_ipi_categories_dg records add {"Phishing"} modify ltm data-group internal dns_response_ipi_categories_dg records add {"Proxy"} modify ltm data-group internal dns_response_ipi_categories_dg records add {"Scanners"} modify ltm data-group internal dns_response_ipi_categories_dg records add {"Spam Sources"} modify ltm data-group internal dns_response_ipi_categories_dg records add {"Web Attacks"} modify ltm data-group internal dns_response_ipi_categories_dg records add {"Windows Exploits"} Tested this on version: 11.51KViews1like2CommentsDNS Black Hole Response Page
Problem this snippet solves: DNS Black Hole response Page Code : # Author: Hugh O.Donnell, F5 Consulting when HTTP_REQUEST { # the static HTML pages include the logo that is referenced in HTML as corp-logo.gif # intercept requests for this and reply with the image that is stored in an iFile defined in RULE_INIT below if {[HTTP::uri] ends_with "/_maintenance-page/corp-logo.png" } { # Present HTTP::respond 200 content $static::corp_logo } else { # Request for Blackhole webpage. Identify what type of block was in place switch -glob [class match -value ".]HTTP::host[" ends_with Blackhole_Class ] { "virus" { set block_reason "Virus site" } "phishing" { set block_reason "Phishing site" } "generic" { set block_reason "Unacceptable Usage" } default { set block_reason "Denied Per Policy - Other Sites" } } # Log details about the blackhole request to the remote syslog server log -noname local0. "Blackhole: From [IP::client_addr]:[TCP::client_port] \ to [IP::local_addr]:[TCP::local_port], [HTTP::request_num], \ [HTTP::method],[HTTP::uri],[HTTP::version], [HTTP::host], [HTTP::header value Referer], \ [HTTP::header User-Agent], [HTTP::header names],[HTTP::cookie names], BH category: $block_reason," # Send an HTML page to the user. The page is defined in the RULE_INIT event below HTTP::respond 200 content "$static::block_page [HTTP::host][HTTP::uri] $static::after_url $block_reason $static::after_block_reason " } } when RULE_INIT { # load the logo that was stored as an iFile set static::corp_logo [ifile get "/Common/f5ball"] # Beginning of the block page set static::block_page " Web Access Denied - Enterprise Network Operations Center <!-- .mainbody { background-color: #C0C0C0; color: #000000; font-family: Verdana, Geneva, sans-serif; font-size: 12px; margin: 0px; padding: 20px 0px 20px 0px; position: relative; text-align: center; width: 100%; } .bdywrpr { width:996px; height:auto; text-align:left; margin:0 auto; z-index:1; position: relative; } #banner-wrapper { width: 950px; padding: 0px; margin: 0px; overflow:hidden; background-color: #FFFFFF; background-repeat: no-repeat; } #banner-image { float: left; margin-left: auto; margin-right: auto; padding: 3px 0px 2px 7px; width: 950px; } #textbody { background-color: #FFFFFF; color: #000000; font-family: Verdana, Geneva, sans-serif; font-size: 13px; width: 950px; padding:0px; text-align:justify; margin: 0px; } --> Access has been denied. URL: " set static::after_url " Your request was denied because it is blacklisted in DNS. Blacklist category: " set static::after_block_reason " The Internet Gateways are for official use only. Misuse violates policy. If you believe that this site is categorized incorrectly, and that you have a valid business reason for access to this site please contact your manager for approval and the Enterprise Network Operations Center via E-mail: enoc@example.com Please use the Web Access Request Form and include a business justification. Only e-mail that originates from valid internal e-mail addresses will be processed. If you do not have a valid e-mail address, your manager will need to submit a request on your behalf. Generated by bigip1.f5.com. " }359Views0likes1Comment