-- --- Dynamic block rules local dbr = dynBlockRulesGroup() dbr:setQueryRate(300, 10, "Exceeded query rate", 60) dbr:setRCodeRate(DNSRCode.NXDOMAIN, 50, 10, "Exceeded NXD rate", 60) dbr:setRCodeRate(DNSRCode.SERVFAIL, 30, 10, "Exceeded ServFail rate", 60) dbr:setQTypeRate(DNSQType.ANY, 1, 10, "Exceeded ANY rate", 60) dbr:setResponseByteRate(8000, 10, "Exceeded resp BW rate", 60) function maintenance() dbr:apply() end dbr:apply() -- --- Basic query mitigations addAction(QTypeRule(DNSQType.ANY), RCodeAction(DNSRCode.REFUSED)) -- Block ANY queries addAction(AndRule{QClassRule(3), QNameRule("version.bind")}, DropAction()) -- Block version.bind -- Max QPS per client addAction(MaxQPSIPRule(25, 24), DelayAction(50)) -- gentle delay instead of drop addAction(MaxQPSIPRule(25, 64), DelayAction(100)) -- longer window, slightly longer delay addAction(AndRule{MaxQPSIPRule(10), TCPRule(false)}, TCAction()) -- Drop queries to local TLDs local sldsToDrop = newSuffixMatchNode() sldsToDrop:add("lan.") addAction(SuffixMatchNodeRule(sldsToDrop), DropAction()) -- Create a node for all IANA TLDs local tldNode = newSuffixMatchNode() local tldsFile = io.open("/etc/dnsdist/tlds-alpha-by-domain.txt", "r") if tldsFile then for line in tldsFile:lines() do if not line:match("^#") and line ~= "" then -- SuffixMatchNode expects lower-case domain with trailing dot tldNode:add(line:lower() .. ".") end end tldsFile:close() end -- --- Upstream servers -- Public resolvers for official TLDs local cloudflare = newServer({address="1.1.1.1", name="Cloudflare", pool="tldPool"}) local cloudflarealt = newServer({address="1.0.0.1", name="CloudflareALT", pool="tldPool"}) local google = newServer({address="8.8.8.8", name="Google", pool="tldPool"}) local googlealt = newServer({address="8.8.4.4", name="GoogleALT", pool="tldPool"}) cloudflare:setUp() cloudflarealt:setUp() google:setUp() googlealt:setUp() -- Your local upstream local localUpstream = newServer({address="127.0.0.1:5353", name="HSD"}) localUpstream:setUp() -- If domain matches official TLDs -> use tldPool addAction(SuffixMatchNodeRule(tldNode), PoolAction("tldPool")) -- Cacheing pc = newPacketCache(10000, {maxTTL=86400, minTTL=0, temporaryFailureTTL=60, staleTTL=60, dontAge=false}) getPool(""):setCache(pc) getPool("tldPool"):setCache(pc) -- --- Local listeners addLocal('0.0.0.0:53', { reusePort = true, tcpFastOpenQueueSize=4096 }) addTLSLocal('0.0.0.0', '/etc/letsencrypt/live/hnsdoh.com/fullchain.pem', '/etc/letsencrypt/live/hnsdoh.com/privkey.pem', { reusePort = true }) -- HTTPS certificates added with Caddy addDOHLocal("127.0.0.1:8053", nil, nil, {"/", "/dns-query"}, { reusePort = true, customResponseHeaders = {["Access-Control-Allow-Origin"]="*"} }) -- Uncomment for IPv6 support -- addLocal('[::]:53', { reusePort = true, ednsUDPSize = 1232 }) -- addTLSLocal('[::]', '/etc/letsencrypt/live/hnsdoh.com/fullchain.pem', -- '/etc/letsencrypt/live/hnsdoh.com/privkey.pem', { reusePort = true }) -- addACL('[::]/0') -- Allow public access addACL('0.0.0.0/0') -- --- DOH front-end redirect dohFE = getDOHFrontend(0) if dohFE then map = { newDOHResponseMapEntry("^/$", 307, "https://welcome.hnsdoh.com") } dohFE:setResponsesMap(map) end -- --- Control socket & key setKey("csl2icaGACsP3+M9tx55c8+dBxVCnlnqAHEC92P55eo=") controlSocket('127.0.0.1:5199') -- webserver("0.0.0.0:5000") -- setWebserverConfig({password="woodburn", apiKey="woodburn", acl="0.0.0.0/0"})