Skip to content

Instantly share code, notes, and snippets.

@nfnty
Last active February 20, 2020 01:40
Show Gist options
  • Save nfnty/1dec1910a5ce4ad01fee29595e9e5ba8 to your computer and use it in GitHub Desktop.
Save nfnty/1dec1910a5ce4ad01fee29595e9e5ba8 to your computer and use it in GitHub Desktop.
powerdns-recursor lua-dns-script
{
"BlacklistSuffixes": [
],
"WhitelistSuffixes": [
"bitcointalk.org."
],
"WhitelistFullmatches": [
"s3.amazonaws.com.",
"yui.yahooapis.com."
],
"LocalSuffixes": [
"test."
],
"Addresses": [
{
"Subnet4": "10.1.0.1/24",
"Gateway4": "10.1.0.1",
"Subnet6": "fd00:0:0:1::/64",
"Gateway6": "fd00:0:0:1::1",
"Zone": "client"
},
{
"Subnet4": "10.2.0.1/24",
"Gateway4": "10.2.0.1",
"Subnet6": "fd00:0:0:2::/64",
"Gateway6": "fd00:0:0:2::1",
"Zone": "admin"
}
],
"Zones": {
"gateway": {
"printer.test.": {"28": "fd00:0:0:3::2", "1": "10.3.0.2"}
},
"client": {
"gateway.test.": {"28": "gateway", "1": "gateway"},
"printer.test.": {"28": "fd00:0:0:3::2", "1": "10.3.0.2"}
},
"admin": {
"gateway.test.": {"28": "gateway", "1": "gateway"},
"printer.test.": {"28": "fd00:0:0:3::2", "1": "10.3.0.2"}
}
}
}
-- luacheck: globals addNTA
-- Local
addNTA('test.')
-- luacheck: globals preresolve pdns newNMG newCA newDS newDN pdnslog
local json = require('json')
local PATH_BLACKLISTS = '/mnt/blacklists'
local PATH_ETC = '/etc/powerdns'
local function json_decode(path)
local fileobject = io.open(path, 'r')
local decoded = json.decode(fileobject:read('*all'))
fileobject:close()
return decoded
end
local CONFIG = json_decode(PATH_ETC .. '/config.json')
local DS_LOCAL = newDS()
DS_LOCAL:add(CONFIG['LocalSuffixes'])
local function scandir_files(path_dir)
local paths_file = {}
for path_file in io.popen('find "' .. path_dir .. '" -type f'):lines() do
paths_file[#paths_file+1] = path_file
end
return paths_file
end
local function array_contains(array, value)
for _, value_a in ipairs(array) do
if value_a == value then
return true
end
end
return false
end
local function blacklists_parse(path)
local ds_blacklist = newDS()
local ds_whitelist = newDS()
ds_whitelist:add(CONFIG['WhitelistSuffixes'])
local dn_line
for _, path_file in ipairs(scandir_files(path)) do
for line in io.lines(path_file) do
if line ~= nil and line ~= '' then
dn_line = newDN(line)
if ds_whitelist:check(dn_line) then
pdnslog(json.encode({
message='whitelist suffix',
path=path_file,
domain=dn_line:toString(),
}))
elseif array_contains(CONFIG['WhitelistFullmatches'], dn_line:toString()) then
pdnslog(json.encode({
message='whitelist fullmatch',
path=path_file,
domain=dn_line:toString(),
}))
else
ds_blacklist:add(dn_line)
end
end
end
end
for _, suffix in ipairs(CONFIG['BlacklistSuffixes']) do
ds_blacklist:add(suffix)
end
return ds_blacklist
end
local DS_BLACKLIST = blacklists_parse(PATH_BLACKLISTS)
local function log_dq(dq, message)
pdnslog(json.encode({
message=message,
qname=dq.qname:toString(),
qtype=dq.qtype,
rcode=dq.rcode,
isTcp=dq.isTcp,
remoteaddr=dq.remoteaddr:toString(),
localaddr=dq.localaddr:toString(),
variable=dq.variable,
followupFunction=dq.followupFunction,
followCNAMERecords=dq.followCNAMERecords,
getFakeAAAARecords=dq.getFakeAAAARecords,
getFakePTRRecords=dq.getFakePTRRecords,
udpQueryResponse=dq.udpQueryResponse,
}))
end
function preresolve(dq)
-- Zone
local zone, ca_gateway4, ca_gateway6, nmg_cidr
for _, dict in ipairs(CONFIG['Addresses']) do
ca_gateway4, ca_gateway6 = newCA(dict['Gateway4']), newCA(dict['Gateway6'])
nmg_cidr = newNMG()
nmg_cidr:addMasks({dict['Subnet4'], dict['Subnet6']})
if dq.remoteaddr:equal(ca_gateway4) or dq.remoteaddr:equal(ca_gateway6) then
zone = CONFIG['Zones']['gateway']
break
elseif nmg_cidr:match(dq.remoteaddr) then
zone = CONFIG['Zones'][dict['Zone']]
break
end
end
if not zone then
dq.variable = true
dq.rcode = pdns.NXDOMAIN
log_dq(dq, 'no zone')
return true
end
-- Blacklist
if DS_BLACKLIST:check(dq.qname) then
dq.rcode = pdns.NXDOMAIN
log_dq(dq, 'blacklisted')
return true
end
-- Local
if DS_LOCAL:check(dq.qname) then
-- No cache
dq.variable = true
-- Retrieve records
local records = zone[dq.qname:toString()]
if records == nil then
dq.rcode = pdns.NXDOMAIN
log_dq(dq, 'local unknown')
return true
end
-- Normal answer
dq.rcode = 0
-- Retrieve record
local record = records[tostring(dq.qtype)]
if record == nil then
log_dq(dq, 'local unknown record')
return true
end
-- Special answer for gateway record
if record == 'gateway' then
if dq.qtype == pdns.A then
dq:addAnswer(dq.qtype, ca_gateway4:toString(), 3600)
return true
elseif dq.qtype == pdns.AAAA then
dq:addAnswer(dq.qtype, ca_gateway6:toString(), 3600)
return true
end
end
-- Answer
dq:addAnswer(dq.qtype, record, 3600)
return true
end
-- No answer
return false
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment