From 273193cd15b5447cc377ab0ced8549a276aad38b Mon Sep 17 00:00:00 2001 From: Seth Ornstein Date: Wed, 7 Jun 2017 15:20:18 -0400 Subject: [PATCH] documentation of new lua commands, stripped down dnsdist.conf, improved example script in pdns/zzz-gca-example directory --- zzz-gca-example/build-dnsdist2.sh | 47 +++ zzz-gca-example/dig-test-nocookie.sh | 4 + zzz-gca-example/dig-test-rpz.sh | 4 + .../dnsdist-Lua additions-June_7_2017.abw | 212 ++++++++++ .../dnsdist-Lua additions-June_7_2017.pdf | Bin 0 -> 62627 bytes zzz-gca-example/dnsdist-check-config.sh | 17 + zzz-gca-example/dnsdist-debug.conf | 378 ++++++++++++++++++ zzz-gca-example/dnsdist.conf | 252 ++++++++++++ zzz-gca-example/dnsdist2-debug.sh | 19 + zzz-gca-example/dnsdist2.sh | 19 + zzz-gca-example/make-dnsdist2.sh | 5 + zzz-gca-example/protobuf-server2.sh | 14 + 12 files changed, 971 insertions(+) create mode 100755 zzz-gca-example/build-dnsdist2.sh create mode 100755 zzz-gca-example/dig-test-nocookie.sh create mode 100755 zzz-gca-example/dig-test-rpz.sh create mode 100644 zzz-gca-example/dnsdist-Lua additions-June_7_2017.abw create mode 100644 zzz-gca-example/dnsdist-Lua additions-June_7_2017.pdf create mode 100755 zzz-gca-example/dnsdist-check-config.sh create mode 100644 zzz-gca-example/dnsdist-debug.conf create mode 100644 zzz-gca-example/dnsdist.conf create mode 100755 zzz-gca-example/dnsdist2-debug.sh create mode 100755 zzz-gca-example/dnsdist2.sh create mode 100755 zzz-gca-example/make-dnsdist2.sh create mode 100755 zzz-gca-example/protobuf-server2.sh diff --git a/zzz-gca-example/build-dnsdist2.sh b/zzz-gca-example/build-dnsdist2.sh new file mode 100755 index 0000000000..279b530da7 --- /dev/null +++ b/zzz-gca-example/build-dnsdist2.sh @@ -0,0 +1,47 @@ +#!/bin/bash -e +echo "" +echo "from: http://dnsdist.org/download/" +echo "" +echo "clone from: git clone https://github.com/PowerDNS/pdns.git" +echo "--or from our copy--" +echo "https://github.com/GlobalCyberAlliance/pdns.git" +echo "" + +echo "cd ../pdns/dnsdistdist" +echo "" +cd ../pdns/dnsdistdist +echo "" +echo "autoreconf -i" +echo "" +autoreconf -i +echo "" +echo "NOTE: configure with libsodium enabled to allow cache test to succeed - Seth - Global Cyber Alliance" +echo "./configure --enable-libsodium" +./configure --enable-libsodium +echo "" +echo "do a \"make clean\" incase this is not the first time through" +echo "" +make clean +echo "" +echo "now do a make" +echo "" +echo "make" +echo "" +make +echo "" +echo "test out the cache code" +echo "" +cd "../../regression-tests.dnsdist" +echo "" +echo "test_Caching" +DNSDISTBIN=../pdns/dnsdistdist/dnsdist ./runtests test_Caching +echo "" +echo "test_CacheHitResponses" +DNSDISTBIN=../pdns/dnsdistdist/dnsdist ./runtests test_CacheHitResponses +echo "" +echo "you can now do \"make install\" if desired." +echo "" +echo "finished" + + + diff --git a/zzz-gca-example/dig-test-nocookie.sh b/zzz-gca-example/dig-test-nocookie.sh new file mode 100755 index 0000000000..b5f88fae7f --- /dev/null +++ b/zzz-gca-example/dig-test-nocookie.sh @@ -0,0 +1,4 @@ +echo "test dnsdist with nocookie option to allow cache hits" +echo "dig @127.0.0.1 -p 5200 +nocookie google.com" +echo "" +dig @127.0.0.1 -p 5200 +nocookie google.com diff --git a/zzz-gca-example/dig-test-rpz.sh b/zzz-gca-example/dig-test-rpz.sh new file mode 100755 index 0000000000..0c1188b5df --- /dev/null +++ b/zzz-gca-example/dig-test-rpz.sh @@ -0,0 +1,4 @@ +echo "test dnsdist with rpz bad entry" +echo "dig @127.0.0.1 -p 5200 1jw2mr4fmky.net" +echo "" +dig @127.0.0.1 -p 5200 1jw2mr4fmky.net diff --git a/zzz-gca-example/dnsdist-Lua additions-June_7_2017.abw b/zzz-gca-example/dnsdist-Lua additions-June_7_2017.abw new file mode 100644 index 0000000000..6ddc68ed7a --- /dev/null +++ b/zzz-gca-example/dnsdist-Lua additions-June_7_2017.abw @@ -0,0 +1,212 @@ + + + + + + + + + + + +Wed Jun 7 15:14:33 2017 + +AbiWord +SethO +Wed Jun 7 11:06:40 2017 + +application/x-abiword + + + + + + + + + + +
+

Proposed new Lua commands for DNSDIST

+

June 7, 2017

+

Seth Ornstein

+

Global Cyber Alliance

+

sornstein@globalcyberalliance.org

+

+

+

To obtain a copy of the source for the modified DNSDIST version:

+

+

git clone -b dnsdist-mod2 https://github.com/GlobalCyberAlliance/pdns.git

+

+

Example scripts and configuration file are located in: pdns/zzz-gca-examples

+

+

+

Lua additional functions that act on the DNSQuestion parameter dq.

+

+

They are expected to be used inside of a Lua function that is setup for use in the dnsdist configuration file by the addLuaAction function. They are used to store text label and value pairs in the DNSQuestion dq and accessed by the DNSResponse Lua functions below.

+

+

To store a text label and value pair in the DNSQuestion:

+

+

dq:setTag(“LabelText”, “ValueText”)

+

+

To store values as a table in the DNSQuestion structure:

+

+

dq:setTagArray(exampleTable)

+

+

+

Lua additional functions that act on the DNSResponse parameter dr.

+

+

These are expected to be located inside of a Lua function that is setup for use in the dnsdist configuration file by the RemoteLogAction function as the “alterFunction”. They are used to obtain the text label and value pairs that were stored in the DNSQuestion Lua functions above.

+

+

To read matching value from DNSQuestion structure:

+

+

dr:getTagMatch(“LabelText”)

+

+

To read text values as an array from DNSQuestion structure:

+

+

dr:getTagArray()

+

+

+

+

+

+

+

+

Lua additional functions that act on the DNSDistProtoBufMessage parameter pbMsg.

+

+

These are expected to be located inside of a Lua function that is setup for use in the dnsdist configuration file by the RemoteLogAction function as the “alterFunction”. They are used to modify the protobuf message that is being sent to the protobuf server.

+

+

To store text values in the protobuf “tags” field:

+

+

pbMsg:setTag(“LabelText”, “ValueText”)

+

+

To store text values as a table in protobuf tag fields

+

+

pbMsg:setTagArray(exampleTable)

+

+

+

To change the protobuf message from a ‘query’ to a ‘response’.

+

The variable is the dns name the client requested to be looked up.

+

A zero is inserted in the ‘query time’ protobuf field.

+

+

pbMsg:setProtobufResponseType(“example.com”)

+

+

+

To change the protobuf message from a ‘query’ to a ‘response’ and set ‘query time’.

+

The variable is the dns name the client requested to be looked up.

+

The second variable is the query time in seconds.

+

The third variable is the fractional micro-seconds.

+

+

pbMsg:setProtobufResponseTypeQT(“example.com”, os.time(), 123456)

+

+

Building the modified dnsdist with the new Lua functions:

+

+

1. Open a console in pdns/zzz-gca-examples and run ./build-dnsdist2.sh

+

This builds dnsdist with libsodium enabled for cache testing.

+

+

+

Running the test scripts:

+

+

1. Open a console in pdns/zzz-gca-examples and run ./protobuf-server2.sh

+

+

2. Open a console in pdns/zzz-gca-examples and run ./dnsdist2.sh

+

+

3. Open a console in pdns/zzz-gca-examples and run ./dig-test-rpz.sh

+

+

4. Verify that the protobuf server console shows a response with tags indicating RPZ.

+

+

5. Open a console in pdns/zzz-gca-examples and run ./dig-test-nocookie.sh

+

+

6. Verify that the protobuf server console shows a response with tags indicating FWD.

+

+

7. Run ./dig-test-nocookie.sh a second time.

+

+

8. Verify that the protobuf server console shows a response with tags indicating CACHE.

+

+

+

+

+

+

Protobuf server sample output:

+

+

Note that the unused “Tags” protobuf field now has the entries passed by the pbMsg:setTag and pbMsg:setTagArray functions. The tag data is separated by commas and the order is label followed by value for each ‘Tag’ in the protobuf ‘Tags’ field. The values in the protobuf ‘Tags’ field were set by the pbMsg:setTagArray function in luaLogBL in dnsdist.conf

+

+

+

Typical response from the protobuf server from a RPZ ‘hit’ :

+

+

Note that since this protobuf was originally a ‘query’ and not a ‘response’ due to dnsdist’s treatment of returning a NXDOMAIN response to the client without having examined the cache or forwarded the request to a DNS server. Also since the function pbMsg:setProtobufResponseType was used the ‘Query time’ field has the zero time.

+

+

[2017-06-07 12:11:45.745800] Response of size 56: 127.0.0.1 -> 127.0.0.1 (UDP), id: 15891, uuid: dc7c809436b74bc48b7dc07b49f3831d

+

- Question: 1, 1, 1jw2mr4fmky.net.

+

- Query time: 1969-12-31 19:00:00.0

+

- Response Code: 3, RRs: 1, Tags: lua-time,12:11:45-06/07/17,Test1,One Two Three,lua-ver,Lua 5.1,Test2,Four Five Six,Trans,RPZ,RPZ-Info,reject-example,From,127.0.0.1:56144,TCP,false

+

- 1, 1, 1jw2mr4fmky.net., 123, 127.0.0.1

+

+

+

Typical response from the protobuf server from a forwarded message:

+

+

Note that the protobuf ‘Tags’ field has the label ‘Trans’ and the value ‘FWD’, which was set using the pbMsg:setTagArray function in luaLogForward in dnsdist.conf.

+

+

[2017-06-07 12:12:50.409444] Response of size 87: 127.0.0.1 -> 127.0.0.1 (UDP), id: 52616, uuid: 3ec48935be3846609cc175136693608f

+

- Question: 1, 1, google.com.

+

- Query time: 2017-06-07 12:12:50.390094

+

- Response Code: 0, RRs: 3, Tags: Trans,FWD

+

- 1, 1, google.com., 299, 216.58.217.78

+

- 1, 1, google.com., 299, 216.58.217.78

+

- 1, 1, google.com., 299, 216.58.217.78

+

+

+

Typical response from the protobuf server from a cache message:

+

+

Note that the protobuf ‘Tags’ field has the label ‘Trans’ and the value ‘CACHE’, which was set using the pbMsg:setTagArray function in luaLogForward in dnsdist.conf.

+

+

[2017-06-07 12:12:55.598121] Response of size 87: 127.0.0.1 -> 127.0.0.1 (UDP), id: 29215, uuid: d16852328b394d49ac0519fd7a2b6a25

+

- Question: 1, 1, google.com.

+

- Query time: 2017-06-07 12:12:55.598087

+

- Response Code: 0, RRs: 3, Tags: Trans,CACHE

+

- 1, 1, google.com., 294, 216.58.217.78

+

- 1, 1, google.com., 294, 216.58.217.78

+

- 1, 1, google.com., 294, 216.58.217.78

+

+

+

+

Additional scripts:

+

+

make-dnsdist.sh - make script for dnsdist

+

+

dnsdist-check-config.sh - quick checking of dnsdist.conf configuration file.

+

+

dnsdist2-debug.sh - run dnsdist with configuration file with debugging statements.

+

+

dnsdist-debug.conf - configuration file for use with dnsdist2-debug.sh

+

Note that you need to set lines 20 to 25 to true to enable the text debugging.

+

+

+

Source code files modified in DNSDIST:

+

+

+

dnsdist-lua.cc

+

+

dnsdist-lua2.cc

+

+

dnsdist.hh

+

+

protobuf.cc

+

+

protobuf.hh

+

+

+

To locate the modified source code search for the words Seth or GCA

+

+

ie. grep -i Seth * or grep -i GCA *

+

+

+

+

+

+

+
+
+

+
+
diff --git a/zzz-gca-example/dnsdist-Lua additions-June_7_2017.pdf b/zzz-gca-example/dnsdist-Lua additions-June_7_2017.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fa63dbf1f33a5682539ba59399e47bd048c5977d GIT binary patch literal 62627 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 maximum number of entries stored in the cache (required) +-- 86400 -> maximum lifetime of an entry in the cache (seconds) +-- 0 -> minimum TTL an entry should have to be considered for insertion in the cache (seconds) +-- 60 -> TTL used for a Server Failure or a Refused response (seconds) +-- 60 -> TTL that will be used when a stale cache entry is returned (seconds) + warnlog(string.format("Script starting ----------------- %s ", "*** setup cache *** ")) +-- ----------------------------------------------------------------------------------------------- + + pc = newPacketCache(10000, 86400, 0, 60, 60) -- new cache + getPool("masterpool"):setCache(pc) -- masterpool cache + + + setStaleCacheEntriesTTL(3600) -- If no backends working, use cached data + + +-- ----------------------------------------------------------------------------------------------- +-- listen on local port 5200 + warnlog(string.format("Script starting ----------------- %s ", "*** listen on port 0.0.0.0:5200 for DNS requests ***")) +-- ----------------------------------------------------------------------------------------------- + + setLocal("0.0.0.0:5200") + + + rlBlkLst = newRemoteLogger('127.0.0.1:60000') -- rpz hit protobuf handler for local address, port 60,000 + rlCache = newRemoteLogger('127.0.0.1:60000') -- cache hit protobuf handler for local address, port 60,000 + rlFwd = newRemoteLogger('127.0.0.1:60000') -- forward protobuf handler for local address, port 60,000 + + +-- ----------------------------------------------------------------------------------------------- +-- maintenance() function called every second + + function maintenance() + + if ((maintCounter % 60) == 0) then -- do this once a minute + print(string.format("\n maintenance() - %s", os.date("%X-%x"))) + + local tableStat = getStatisticsCounters() -- display statistics + for k, v in pairs( tableStat ) do + print(string.format(" %-23s %d ", k, v)) + end + + + end + maintCounter = maintCounter + 1 + end + +-- ----------------------------------------------------------------------------------------------- +-- luaCheckBL() - check for rpz hit +-- if in blacklist then spoof response +-- else forward normally to masterpool + warnlog(string.format("Script starting ----------------- %s ", "*** luaCheckBL() *** ")) +-- ----------------------------------------------------------------------------------------------- + + function luaCheckBL(dq) + + if (bDebugCheckBL) + then + print(string.format("luaCheckBL -> qname.: %s ", dq.qname:toString())) + + print(string.format("luaCheckBL -> qtype.: %d ", dq.qtype)) + print(string.format("luaCheckBL -> from..: %s ", dq.remoteaddr:toStringWithPort())) + print(string.format("luaCheckBL -> opcode: %d ", dq.opcode)) + print(string.format("luaCheckBL -> rcode.: %d ", dq.rcode)) + print(string.format("luaCheckBL -> qclass: %d ", dq.qclass)) + print(string.format("luaCheckBL -> DO....: %s ", tostring(dq:getDO()))) + print(string.format("luaCheckBL -> Len...: %d ", dq.len)) + print(string.format("luaCheckBL -> Size..: %d ", dq.size)) + print(string.format("luaCheckBL -> TCP...: %s ", tostring(dq.tcp))) + end + + + local tKey = dq.qname:toString() -- get dns name client requested to be looked up + if(tKey ~= nil) + then + local tKey2 = string.sub(tKey, 1, string.len(tKey) - 1) -- get rid of final period at end of dnsname + if (bDebugCheckBL) + then + print(string.format("luaCheckBL -> tKey2.: %s ", tKey2)) + end + if(tKey2 == strTestDns1) + then + dq:setTag("Trans", "RPZ") -- label this transaction as rpz for protobuf - NEW LUA COMMAND - 5/22/2017 + dq:setTag("RPZ-Info", "reject-example") -- store blacklist extra data in dq for protobuf later -- NEW LUA COMMAND - 5/22/2017 + dq:setTag("lua-time", os.date("%X-%x")) -- an example of storing extra data -- NEW LUA COMMAND - 5/22/2017 + dq:setTag("lua-ver", _VERSION) -- another example of storing extra data -- NEW LUA COMMAND - 5/22/2017 + dq:setTag("From", dq.remoteaddr:toStringWithPort()) -- store blacklist extra data in dq for protobuf later -- NEW LUA COMMAND - 5/22/2017 + dq:setTag("TCP", tostring(dq.tcp)) -- store blacklist extra data in dq for protobuf later -- NEW LUA COMMAND - 5/22/2017 + + local tableTags = {} -- create a table as an experiment + tableTags["TestLabel1"] = "Test Value One" -- add transaction type to table + tableTags["TestLabel2"] = "Test Value Two" -- add transaction type to table + dq:setTagArray(tableTags) -- store table in dq for protobuf later -- NEW LUA COMMAND - 6/2/2017 + + if (bDebugCheckBL) + then + print(string.format("luaCheckBL -> RpzHit: %s **********", tValue)) + print(string.format("--")) + end + + return DNSAction.None, "" -- continue to the next rule + + end + end + + if (bDebugCheckBL) + then + print(string.format("luaCheckBL -> return DNSAction.Pool to masterpool ")) + print(string.format("--")) + end + + return DNSAction.Pool, "masterpool" -- use the specified pool to forward this query + + end + +-- ----------------------------------------------------------------------------------------------- +-- declare a Lua action functino to alter the protobuf when a BlackList (RPZ) hit occurs + warnlog(string.format("Script starting ----------------- %s ", "*** luaLogBL() -> 127.0.0.1:60000 ***")) +-- ----------------------------------------------------------------------------------------------- + +function luaLogBL(dr, pbMsg) -- this is the lua code that executes for a request + + + if (bDebugLogBL) + then + print(string.format("luaLogBL -> qname: %s qtype: %d from: %s TCP: %s ", dr.qname:toString(), dr.qtype, dr.remoteaddr:toStringWithPort(), tostring(dr.tcp))) + print(string.format("luaLogBL -> pb: %s ", pbMsg:toDebugString())) + end + + + if (bDebugLogBL) + then + print(string.format("luaLogBL -> dr:getTagArray() ")) + end + + local tableTags = dr:getTagArray() -- get array of tags inserted by setTag() - NEW LUA COMMAND - 5/24/2017 + + if (bDebugLogBL) + then + for k, v in pairs( tableTags ) do + print(string.format("\t Label: %-15s Value: %s ", k, v)) + end + end + + if (bDebugLogBL) + then + print(string.format("luaLogBL-> Test adding to table tableTags")) + tableTags["dude1"] = "test1" -- test adding extra entries to table + tableTags["dude2"] = "test2" -- test adding extra entries to table + tableTags["dude3"] = "test3" -- test adding extra entries to table + tableTags["dude4"] = "test4" -- test adding extra entries to table + end + + if (bDebugLogBL) + then + print(string.format("luaLogBL-> setTagArray(tableTags)")) + end + + pbMsg:setTagArray(tableTags) -- store tableTags in the 'tags' field of the protobuf - NEW LUA COMMAND - 5/24/2017 + + if (bDebugLogBL) + then + print(string.format("luaLogBL-> setResponseCode(dnsdist.NXDOMAIN)")) + end + + + pbMsg:setResponseCode(dnsdist.NXDOMAIN) -- set protobuf response code to be NXDOMAIN + + + if (bDebugLogBL) + then + print(string.format("luaLogBL-> get dns name")) + end + + + local strReqName = dr.qname:toString() -- get request dns name + + + if (bDebugLogBL) + then + print(string.format("luaLogBL-> strReqName = %s", strReqName)) + end + + pbMsg:setProtobufResponseType(strReqName) -- set protobuf to look like a response and not a query, no query time -- NEW LUA COMMAND + -- strReqName - The DNS name that was sent by the client to be looked up. + +-- pbMsg:setProtobufResponseTypeQT(strReqName, os.time(), 0) -- set protobuf to look like a response and not a query, insert query time -- NEW LUA COMMAND + -- strReqName - The DNS name that was sent by the client to be looked up. + -- timestamp - Timestamp for protobuf field + -- timestamp - microseconds + + if (bDebugLogBL) + then + print(string.format("--")) + end + + +end + + + +-- ----------------------------------------------------------------------------------------------- +-- declare a Lua action function to alter the protobuf when a normal forwarding happens + warnlog(string.format("Script starting ----------------- %s ", "*** luaLogForward() ***")) +-- ----------------------------------------------------------------------------------------------- + +function luaLogForward(dr, pbMsg) + + + if (bDebugLogForward) + then + print(string.format("luaLogForward -> qname: %s qtype: %d from: %s TCP: %s ", dr.qname:toString(), dr.qtype, dr.remoteaddr:toStringWithPort(), tostring(dr.tcp))) + print(string.format("luaLogForward -> opcode: %d ", dr.opcode)) + print(string.format("luaLogForward -> rcode.: %d ", dr.rcode)) + print(string.format("luaLogForward -> qclass: %d ", dr.qclass)) + print(string.format("luaLogForward -> len...: %d ", dr.len)) + print(string.format("luaLogForward -> pb: %s ", pbMsg:toDebugString())) + end + + + if (bDebugLogForward) + then + print(string.format("luaLogForward -> Creating a table with transaction type. ")) + end + + local tableTags = {} -- create a table + tableTags["Trans"] = "FWD" -- add transaction type to table + + if (bDebugLogForward) + then + print(string.format("luaLogBL-> setTagArray(tableTags)")) + end + + pbMsg:setTagArray(tableTags) -- store tableTags in the 'tags' field of the protobuf - NEW LUA COMMAND - 5/24/2017 + + + + if (bDebugLogForward) + then + print(string.format("--")) + end + +end + + + + +-- ----------------------------------------------------------------------------------------------- +-- declare a Lua action function to alter the protobuf when a Cache hit occurs + warnlog(string.format("Script starting ----------------- %s ", "*** luaLogCache() ***")) +-- ----------------------------------------------------------------------------------------------- + +function luaLogCache(dr, pbMsg) -- this is the lua code that executes after a cache hit + + + if (bDebugLogCache) + then + print(string.format("luaLogCache -> qname: %s qtype: %d from: %s TCP: %s ", dr.qname:toString(), dr.qtype, dr.remoteaddr:toStringWithPort(), tostring(dr.tcp))) + end + + if (bDebugLogCache) + then + print(string.format("luaLogForward -> Creating a table with transaction type. ")) + end + + local tableTags = {} -- create a table + tableTags["Trans"] = "CACHE" -- add transaction type to table + + if (bDebugLogCache) + then + print(string.format("luaLogBL-> setTagArray(tableTags)")) + end + + pbMsg:setTagArray(tableTags) -- store tableTags in the 'tags' field of the protobuf - NEW LUA COMMAND - 5/24/2017 + + + + if (bDebugLogForward) + then + print(string.format("--")) + end + + + if (bDebugLogCache) + then + print(string.format("--")) + end + +end + + + + +-- ---------------------------------------------------------------------------------------------- + -- put this here so blacklist sends out protobuf...... +function luaRetNXDOMAIN(dq) + + if (bDebugRetNXDOMAIN) + then + print(string.format("luaRetNXDOMAIN() - return NXDOMAIN to client")) + print(string.format("--")) + end + return DNSAction.Nxdomain, "" -- return NXDOMAIN response to client +end + + + + +-- ----------------------------------------------------------------------------------------------- +-- Rules + warnlog(string.format("Script starting ----------------- %s ", "*** setting rules *** ")) +-- ----------------------------------------------------------------------------------------------- + + + addLuaAction(AllRule(), luaCheckBL) -- first, check blacklist, if match process next rule below, else send to "masterpool" + + addAction(AllRule(), RemoteLogAction(rlBlkLst, luaLogBL)) -- then send out protobuf for rpz hit + + addLuaAction(AllRule(), luaRetNXDOMAIN) -- then send nxdomain response back to the client. + + addAction(AllRule(), PoolAction("masterpool")) -- direct requests that are not RPZ to pool "masterpool" + + + addCacheHitResponseAction(AllRule(), RemoteLogResponseAction(rlCache, luaLogCache)) -- used to send out protobuf on cache hit + + + addResponseAction(AllRule(), RemoteLogResponseAction(rlFwd, luaLogForward)) -- used to send out protobuf on forward (normal) out + +-- ----------------------------------------------------------------------------------------------- +-- finished setting up script +-- ----------------------------------------------------------------------------------------------- + + warnlog(string.format("Script finished ----------------- %s ", os.date("%X-%x"))) + diff --git a/zzz-gca-example/dnsdist.conf b/zzz-gca-example/dnsdist.conf new file mode 100644 index 0000000000..a79521a6af --- /dev/null +++ b/zzz-gca-example/dnsdist.conf @@ -0,0 +1,252 @@ +-- ----------------------------------------------------------------------------------------------- +-- dnsdist2.conf - with NO text debugging comments....... +-- see dnsdist2-debug.conf for a copy with text debugging. +-- Seth Ornstein - sornstein@globalcyberalliance.org +-- 6/7/2017 +-- for use in testing out new Lua commands for dnsdist +-- git clone -b dnsdist-mod2 https://github.com/GlobalCyberAlliance/pdns.git +-- ----------------------------------------------------------------------------------------------- + + warnlog(string.format("Script starting ----------------- %s ", "dnsdist2.conf")) + + warnlog(string.format("Script starting ----------------- %s ", os.date("%X-%x"))) + + warnlog(string.format("Script starting ----------------- Lua Version: %s - (should be 5.1)", _VERSION)) + + + + strTestDns1 = "1jw2mr4fmky.net" -- test #1 dns lookup name - (reject) + + maintCounter = 0 -- maintainance counter + + bDebugCheckBL = false -- true if debugging luaCheckBL + bDebugLogBL = false -- true if debugging luaLogBL + bDebugLogForward = false -- true if debugging luaLogForward + bDebugLogCache = false -- true if debugging luaLogCache + bDebugRetNXDOMAIN = false -- true if debugging luaRetNXDOMAIN + + + +-- ----------------------------------------------------------------------------------------------- + +-- ----------------------------------------------------------------------------------------------- +-- setup servers to use + warnlog(string.format("Script starting ----------------- %s ", "*** setup servers to use ***")) +-- ----------------------------------------------------------------------------------------------- + +--newServer({address="64.6.64.6:53", name="Verisign_1", pool="masterpool"}) -- verisign dns server #1 +--newServer({address="64.6.65.6:53", name="Verisign_2", pool="masterpool"}) -- verisign dns server #2 +newServer({address="8.8.8.8:53", name="Google_1", pool="masterpool"}) -- google dns server #1 +newServer({address="8.8.4.4:53", name="Google_2", pool="masterpool"}) -- google dns server #2 +--newServer({address="208.67.222.222:53", name="Opendns_1", pool="masterpool"}) -- opendns dns server #1 +--newServer({address="208.67.220.220:53", name="Opendns_2", pool="masterpool"}) -- opendns dns server #2 + + +-- ----------------------------------------------------------------------------------------------- +-- set up the cache +-- 10000 -> maximum number of entries stored in the cache (required) +-- 86400 -> maximum lifetime of an entry in the cache (seconds) +-- 0 -> minimum TTL an entry should have to be considered for insertion in the cache (seconds) +-- 60 -> TTL used for a Server Failure or a Refused response (seconds) +-- 60 -> TTL that will be used when a stale cache entry is returned (seconds) + warnlog(string.format("Script starting ----------------- %s ", "*** setup cache *** ")) +-- ----------------------------------------------------------------------------------------------- + + pc = newPacketCache(10000, 86400, 0, 60, 60) -- new cache + getPool("masterpool"):setCache(pc) -- masterpool cache + + + setStaleCacheEntriesTTL(3600) -- If no backends working, use cached data + + +-- ----------------------------------------------------------------------------------------------- +-- listen on local port 5200 + warnlog(string.format("Script starting ----------------- %s ", "*** listen on port 0.0.0.0:5200 for DNS requests ***")) +-- ----------------------------------------------------------------------------------------------- + + setLocal("0.0.0.0:5200") + + + rlBlkLst = newRemoteLogger('127.0.0.1:60000') -- rpz hit protobuf handler for local address, port 60,000 + rlCache = newRemoteLogger('127.0.0.1:60000') -- cache hit protobuf handler for local address, port 60,000 + rlFwd = newRemoteLogger('127.0.0.1:60000') -- forward protobuf handler for local address, port 60,000 + + +-- ----------------------------------------------------------------------------------------------- +-- maintenance() function called every second + + function maintenance() + + if ((maintCounter % 60) == 0) then -- do this once a minute + print(string.format("\n maintenance() - %s", os.date("%X-%x"))) + + local tableStat = getStatisticsCounters() -- display statistics + for k, v in pairs( tableStat ) do + print(string.format(" %-23s %d ", k, v)) + end + + + end + maintCounter = maintCounter + 1 + end + +-- ----------------------------------------------------------------------------------------------- +-- luaCheckBL() - check for rpz hit +-- if in blacklist then spoof response +-- else forward normally to masterpool + warnlog(string.format("Script starting ----------------- %s ", "*** luaCheckBL() *** ")) +-- ----------------------------------------------------------------------------------------------- + + function luaCheckBL(dq) + + + + local tKey = dq.qname:toString() -- get dns name client requested to be looked up + if(tKey ~= nil) + then + local tKey2 = string.sub(tKey, 1, string.len(tKey) - 1) -- get rid of final period at end of dnsname + if(tKey2 == strTestDns1) + then + dq:setTag("Trans", "RPZ") -- label this transaction as rpz for protobuf - NEW LUA COMMAND - 5/22/2017 + dq:setTag("RPZ-Info", "reject-example") -- store blacklist extra data in dq for protobuf later -- NEW LUA COMMAND - 5/22/2017 + + local tableTags = {} -- create a table as an experiment + tableTags["TestLabel1"] = "Test Value One" -- add transaction type to table + tableTags["TestLabel2"] = "Test Value Two" -- add transaction type to table + dq:setTagArray(tableTags) -- store table in dq for protobuf later -- NEW LUA COMMAND - 6/2/2017 + + + return DNSAction.None, "" -- continue to the next rule + + end + end + + + return DNSAction.Pool, "masterpool" -- use the specified pool to forward this query + + end + +-- ----------------------------------------------------------------------------------------------- +-- declare a Lua action functino to alter the protobuf when a BlackList (RPZ) hit occurs + warnlog(string.format("Script starting ----------------- %s ", "*** luaLogBL() -> 127.0.0.1:60000 ***")) +-- ----------------------------------------------------------------------------------------------- + +function luaLogBL(dr, pbMsg) -- this is the lua code that executes for a request + + + + + + local tableTags = dr:getTagArray() -- get array of tags inserted by setTag() - NEW LUA COMMAND - 5/24/2017 + + + + + pbMsg:setTagArray(tableTags) -- store tableTags in the 'tags' field of the protobuf - NEW LUA COMMAND - 5/24/2017 + + + + pbMsg:setResponseCode(dnsdist.NXDOMAIN) -- set protobuf response code to be NXDOMAIN + + + + + local strReqName = dr.qname:toString() -- get request dns name + + + + pbMsg:setProtobufResponseType(strReqName) -- set protobuf to look like a response and not a query, no query time -- NEW LUA COMMAND + -- strReqName - The DNS name that was sent by the client to be looked up. + +-- pbMsg:setProtobufResponseTypeQT(strReqName, os.time(), 0) -- set protobuf to look like a response and not a query, insert query time -- NEW LUA COMMAND + -- strReqName - The DNS name that was sent by the client to be looked up. + -- timestamp - Timestamp for protobuf field + -- timestamp - microseconds + + + +end + + + +-- ----------------------------------------------------------------------------------------------- +-- declare a Lua action function to alter the protobuf when a normal forwarding happens + warnlog(string.format("Script starting ----------------- %s ", "*** luaLogForward() ***")) +-- ----------------------------------------------------------------------------------------------- + +function luaLogForward(dr, pbMsg) + + + + + local tableTags = {} -- create a table + tableTags["Trans"] = "FWD" -- add transaction type to table + + + pbMsg:setTagArray(tableTags) -- store tableTags in the 'tags' field of the protobuf - NEW LUA COMMAND - 5/24/2017 + + + + +end + + + + +-- ----------------------------------------------------------------------------------------------- +-- declare a Lua action function to alter the protobuf when a Cache hit occurs + warnlog(string.format("Script starting ----------------- %s ", "*** luaLogCache() ***")) +-- ----------------------------------------------------------------------------------------------- + +function luaLogCache(dr, pbMsg) -- this is the lua code that executes after a cache hit + + + + local tableTags = {} -- create a table + tableTags["Trans"] = "CACHE" -- add transaction type to table + + + pbMsg:setTagArray(tableTags) -- store tableTags in the 'tags' field of the protobuf - NEW LUA COMMAND - 5/24/2017 + + +end + + + + +-- ---------------------------------------------------------------------------------------------- + -- put this here so blacklist sends out protobuf...... +function luaRetNXDOMAIN(dq) + + return DNSAction.Nxdomain, "" -- return NXDOMAIN response to client +end + + + + +-- ----------------------------------------------------------------------------------------------- +-- Rules + warnlog(string.format("Script starting ----------------- %s ", "*** setting rules *** ")) +-- ----------------------------------------------------------------------------------------------- + + + addLuaAction(AllRule(), luaCheckBL) -- first, check blacklist, if match process next rule below, else send to "masterpool" + + addAction(AllRule(), RemoteLogAction(rlBlkLst, luaLogBL)) -- then send out protobuf for rpz hit + + addLuaAction(AllRule(), luaRetNXDOMAIN) -- then send nxdomain response back to the client. + + addAction(AllRule(), PoolAction("masterpool")) -- direct requests that are not RPZ to pool "masterpool" + + + addCacheHitResponseAction(AllRule(), RemoteLogResponseAction(rlCache, luaLogCache)) -- used to send out protobuf on cache hit + + + addResponseAction(AllRule(), RemoteLogResponseAction(rlFwd, luaLogForward)) -- used to send out protobuf on forward (normal) out + +-- ----------------------------------------------------------------------------------------------- +-- finished setting up script +-- ----------------------------------------------------------------------------------------------- + + warnlog(string.format("Script finished ----------------- %s ", os.date("%X-%x"))) + diff --git a/zzz-gca-example/dnsdist2-debug.sh b/zzz-gca-example/dnsdist2-debug.sh new file mode 100755 index 0000000000..1f20163059 --- /dev/null +++ b/zzz-gca-example/dnsdist2-debug.sh @@ -0,0 +1,19 @@ +echo "test-dnsdist2.sh - test dnsdist - debugging configuration" +echo "" +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SLASH="/" +CFG_FILE="dnsdist-debug.conf" +CONFIG_FILE=$DIR$SLASH$CFG_FILE +echo "current directory: " $DIR +echo "" +echo "configuration file: " $CONFIG_FILE +echo "" +echo "cd ../pdns/dnsdistdist" +echo "" +cd ../pdns/dnsdistdist +echo "" +###echo "listen on port 5200 for requests" +###echo "" +###./dnsdist --config=$CONFIG_FILE --local=0.0.0.0:5200 + +./dnsdist --config=$CONFIG_FILE diff --git a/zzz-gca-example/dnsdist2.sh b/zzz-gca-example/dnsdist2.sh new file mode 100755 index 0000000000..410d4de28e --- /dev/null +++ b/zzz-gca-example/dnsdist2.sh @@ -0,0 +1,19 @@ +echo "test-dnsdist2.sh - test dnsdist" +echo "" +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +SLASH="/" +CFG_FILE="dnsdist.conf" +CONFIG_FILE=$DIR$SLASH$CFG_FILE +echo "current directory: " $DIR +echo "" +echo "configuration file: " $CONFIG_FILE +echo "" +echo "cd ../pdns/dnsdistdist" +echo "" +cd ../pdns/dnsdistdist +echo "" +###echo "listen on port 5200 for requests" +###echo "" +###./dnsdist --config=$CONFIG_FILE --local=0.0.0.0:5200 + +./dnsdist --config=$CONFIG_FILE diff --git a/zzz-gca-example/make-dnsdist2.sh b/zzz-gca-example/make-dnsdist2.sh new file mode 100755 index 0000000000..f4f952988c --- /dev/null +++ b/zzz-gca-example/make-dnsdist2.sh @@ -0,0 +1,5 @@ +echo "-------------- cd ../pdns/dnsdistdist -----------" +cd ../pdns/dnsdistdist +echo "-------------- make--------------------------------" +make + diff --git a/zzz-gca-example/protobuf-server2.sh b/zzz-gca-example/protobuf-server2.sh new file mode 100755 index 0000000000..6bfc39340a --- /dev/null +++ b/zzz-gca-example/protobuf-server2.sh @@ -0,0 +1,14 @@ +echo "protobuf-server2 for testing dnsdist Seth Global Cyber Alliance 6/7/2017" + +cd ../contrib/ + +echo "" +echo "----------------------------" +echo "listening on 127.0.0.1:60000" +echo "----------------------------" +echo "" + +./ProtobufLogger.py 127.0.0.1 60000 + + + -- 2.47.2