addCapabilitiesToRetain({"CAP_BPF"}) bpf = newBPFFilter({ipv4MaxItems=8192, ipv6MaxItems=8192, qnamesMaxItems=4096}) setDefaultBPFFilter(bpf) setKey("[REDACTED]") controlSocket("127.0.0.1:5199") setSecurityPollSuffix("") setMaxTCPClientThreads(2) setTCPRecvTimeout(30) setTCPSendTimeout(5) setTCPDownstreamMaxIdleTime(5) setMaxTCPConnectionDuration(0) setMaxTCPQueriesPerConnection(0) setMaxTCPConnectionsPerClient([REDACTED]) pc = newPacketCache(500000, { numberOfShards = 32, keepStaleData = true, skipOptions = {11, 12}, }) getPool(""):setCache(pc) setStaleCacheEntriesTTL(3600) setRingBuffersSize(50000, 20) setRingBuffersOptions({ recordResponses = true }) webserver("127.0.0.1:8083") setWebserverConfig({ password = "[REDACTED]", apiKey = "[REDACTED]", acl = "127.0.0.1", apiRequiresAuthentication = true, statsRequireAuthentication = true, maxConcurrentConnections = 64, }) newServer({ address = "127.0.0.1:5300", name = "PDNS", sockets = 2, udpTimeout = 5, maxCheckFailures = 3, rise = 2, maxInFlight = 20, }) setACL({ "0.0.0.0/0", "::/0" }) local certFile = "/etc/ssl/dnsbunker_rapidssl/fullchain.pem" local keyFile = "/etc/ssl/dnsbunker_rapidssl/privkey.pem" local dohOpts = { trustForwardedForHeader = true, sendCacheControlHeaders = true, idleTimeout = 180, } addDOHLocal("127.0.0.1:8053", nil, nil, "/dns-query", dohOpts) local dotOpts = { minTLSVersion = "tls1.2", ticketsKeysRotationDelay = 3600, numberOfTicketsKeys = 2, sessionTimeout = 3600, maxInFlight = 100, } addTLSLocal("185.250.250.61:853", certFile, keyFile, dotOpts) addTLSLocal("[2a0a:51c1:a:ea::]:853", certFile, keyFile, dotOpts) addDOQLocal("185.250.250.61:853", certFile, keyFile, { reusePort = true, idleTimeout = 30 }) addDOQLocal("[2a0a:51c1:a:ea::]:853", certFile, keyFile, { reusePort = true, idleTimeout = 30 }) addAction(PayloadSizeRule("smaller", 12), DropAction()) addAction(PayloadSizeRule("greater", 1232), DropAction()) addAction(NotRule(OpcodeRule(DNSOpcode.Query)), RCodeAction(DNSRCode.NOTIMP)) addAction(NotRule(QClassRule(DNSClass.IN)), RCodeAction(DNSRCode.REFUSED)) addAction(QTypeRule(DNSQType.ANY), SetExtendedDNSErrorAction(21, "ANY not supported, see RFC 8482")) addAction(QTypeRule(DNSQType.ANY), RCodeAction(DNSRCode.NOTIMP)) addAction(OrRule({QTypeRule(DNSQType.AXFR), QTypeRule(DNSQType.IXFR)}), RCodeAction(DNSRCode.REFUSED)) addAction(NotRule(RDRule()), RCodeAction(DNSRCode.REFUSED)) addAction(EDNSVersionRule(0), ERCodeAction(DNSRCode.BADVERS)) addAction(MaxQPSIPRule([REDACTED], 32, 64, [REDACTED], 60, 30, 1), RCodeAction(DNSRCode.REFUSED)) setDynBlocksAction(DNSAction.Refused) local dbr_host = dynBlockRulesGroup() dbr_host:excludeRange("127.0.0.0/8") dbr_host:excludeRange("::1/128") dbr_host:setMasks(32, 64, 0) dbr_host:setQueryRate([REDACTED], 20, "Query rate exceeded", 60, DNSAction.Drop) dbr_host:setResponseByteRate([REDACTED], 10, "Response BW exceeded", 60, DNSAction.Drop) addResponseAction(AndRule({RCodeRule(DNSRCode.NXDOMAIN), RecordsTypeCountRule(DNSSection.Answer, DNSQType.CNAME, 1, 65535)}), SetTagResponseAction("stats", "blocked"), {name = "stats-blocked"}) addCacheHitResponseAction(AndRule({RCodeRule(DNSRCode.NXDOMAIN), RecordsTypeCountRule(DNSSection.Answer, DNSQType.CNAME, 1, 65535)}), SetTagResponseAction("stats", "blocked"), {name = "stats-blocked-cached"}) addAction(QNameSuffixRule("check.dnsbunker.org"), SetSkipCacheAction(), {name="check-nocache"}) local CHECK_ME = {"185.250.250.61", "2a0a:51c1:a:ea::"} for _, p in ipairs({{"doh", "DoH"}, {"dot", "DoT"}, {"doq", "DoQ"}}) do addAction(AndRule({QNameRule(p[1] .. ".check.dnsbunker.org"), IncomingProtocolRule(p[2])}), SpoofAction(CHECK_ME, {ttl=30}), {name="check-" .. p[1]}) end local function v6sel() return NetmaskGroupRule({"::/0"}) end addAction(AndRule({QNameRule("v6.check.dnsbunker.org"), v6sel()}), SpoofAction(CHECK_ME, {ttl=30}), {name="check-v6"}) addAction(AndRule({QNameRule("v4.check.dnsbunker.org"), NotRule(v6sel())}), SpoofAction(CHECK_ME, {ttl=30}), {name="check-v4"}) addAction(v6sel(), NoneAction(), {name="stats-v6"}) addAction(NotRule(v6sel()), NoneAction(), {name="stats-v4"}) declareMetric("blocked-pro", "counter", "Sperren durch HaGeZi Pro") declareMetric("blocked-tif", "counter", "Sperren durch HaGeZi TIF") declareMetric("blocked-local", "counter", "Sperren durch die eigene Sperrliste") declareMetric("blocked-other", "counter", "Sperren ohne erkennbare Liste") local BLOCKED_LISTS = {pro = "blocked-pro", tif = "blocked-tif", ["local"] = "blocked-local"} function countBlockedList(dr) local packet = dr:getContent() local overlay = newDNSPacketOverlay(packet) local metric = "blocked-other" for i = 0, overlay:getRecordsCountInSection(DNSSection.Answer) - 1 do local rec = overlay:getRecord(i) if rec.type == DNSQType.CNAME and rec.place == DNSSection.Answer then local len = string.byte(packet, rec.contentOffset + 1) if len and len > 0 and len < 64 then metric = BLOCKED_LISTS[string.sub(packet, rec.contentOffset + 2, rec.contentOffset + 1 + len)] or metric end end end incMetric(metric) return DNSResponseAction.None, "" end addResponseAction(AndRule({RCodeRule(DNSRCode.NXDOMAIN), RecordsTypeCountRule(DNSSection.Answer, DNSQType.CNAME, 1, 65535)}), LuaResponseAction(countBlockedList), {name = "perlist-blocked"}) addCacheHitResponseAction(AndRule({RCodeRule(DNSRCode.NXDOMAIN), RecordsTypeCountRule(DNSSection.Answer, DNSQType.CNAME, 1, 65535)}), LuaResponseAction(countBlockedList), {name = "perlist-blocked-cached"}) local DNSCRYPT_PROVIDER = "2.dnscrypt-cert.dnsbunker.org" local DNSCRYPT_KEY = "/etc/dnsdist/dnscrypt/provider.key" local DNSCRYPT_DIR = "/run/dnsdist" local DNSCRYPT_ROTATE, DNSCRYPT_VALID, DNSCRYPT_SLOTS = 43200, 86400, 3 local function dnscryptSlot(serial) local n = math.floor(serial / DNSCRYPT_ROTATE) % DNSCRYPT_SLOTS + 1 return DNSCRYPT_DIR .. "/dnscrypt-" .. n .. ".cert", DNSCRYPT_DIR .. "/dnscrypt-" .. n .. ".key" end local dnscryptCerts, dnscryptKeys = {}, {} for n = 1, DNSCRYPT_SLOTS do local c, k = DNSCRYPT_DIR .. "/dnscrypt-" .. n .. ".cert", DNSCRYPT_DIR .. "/dnscrypt-" .. n .. ".key" local fc, fk = io.open(c, "rb"), io.open(k, "rb") if fc and fk then table.insert(dnscryptCerts, c) table.insert(dnscryptKeys, k) end if fc then fc:close() end if fk then fk:close() end end for _, a in ipairs({"185.250.250.61:8443", "[2a0a:51c1:a:ea::]:8443"}) do addDNSCryptBind(a, DNSCRYPT_PROVIDER, dnscryptCerts, dnscryptKeys) end local dnscryptLast = nil local dnscryptFilterDetached = false local function dnscryptMaintenance(now) if not dnscryptFilterDetached then for i = 0, getBindCount() - 1 do local b = getBind(i) if b:toString():sub(-5) == ":8443" then b:detachFilter() end end dnscryptFilterDetached = true end local newest = 0 for i = 0, getDNSCryptBindCount() - 1 do local ctx = getDNSCryptBind(i) for _, pair in pairs(ctx:getCertificatePairs()) do local cert = pair:getCertificate() if cert:getTSEnd() <= now then ctx:markInactive(cert:getSerial()) ctx:removeInactiveCertificate(cert:getSerial()) elseif cert:getTSStart() + 60 > newest then newest = cert:getTSStart() + 60 end end end if dnscryptLast == nil then dnscryptLast = newest end if now - dnscryptLast >= DNSCRYPT_ROTATE then local certFile, keyFile = dnscryptSlot(now) generateDNSCryptCertificate(DNSCRYPT_KEY, certFile, keyFile, now, now - 60, now + DNSCRYPT_VALID, DNSCryptExchangeVersion.VERSION2) for i = 0, getDNSCryptBindCount() - 1 do getDNSCryptBind(i):loadNewCertificate(certFile, keyFile) end dnscryptLast = now end end addAction(AndRule({QNameRule("dnscrypt.check.dnsbunker.org"), OrRule({IncomingProtocolRule("DNSCryptUDP"), IncomingProtocolRule("DNSCryptTCP")})}), SpoofAction(CHECK_ME, {ttl=30}), {name="check-dnscrypt"}) function maintenance() dbr_host:apply() dnscryptMaintenance(os.time()) end