Experimente mit “dnsdist”

Ich betreibe ja “aus Spaß” einen authoritativen Bind9 Nameserver. (Fragt nicht.) Jetzt kam der Gedanke auf, dass man den doch “besser absichern” könnte, sowas wie sshguard oder fail2ban für Bind9.

Eine kurze Recherche später war “dnsdist” gefunden, es war als Paket meiner Distribution verfügbar und los geht der Spaß. Viele Fehlversuche später (Das Config-File ist nicht eben “menschenfreundlich”.) hab ich das Teil deinstalliert und fahre wieder den nackten Bind9. Nicht dass dnsdist schlecht wäre, nie nicht, aber es ist wie in einer Beziehung: Man passt zueinander oder eben nicht.

Nevertheless, falls es vllt. mal irgendjemandem hilft, hier ist eine funktionierende Config mit Log nach /var/log/dnsdist.log und einem Bind9 Backend, das auf localhost Port 5353 lauscht. Wie immer alles ohne Garantie und Gewähr, das war einfach ein kurzes Spaßprojekt. Schaut auf Owner, Group und Rechte des Configfiles, macht Gebrauch von “dnsdist –check-config -C /path/to/dnsdist.conf”. Das Übliche halt. Und BITTE checkt, ob ihr damit nicht versehentlich einen Open-Resolver baut, das ist wichtig.

-- Log queries (5 parameters)
-- Parameters: filename, binary, append, buffered, verboseOnly
addAction(AllRule(), LogAction("/var/log/dnsdist.log", false, true, false, false))

-- Log responses (5 parameters)
-- Parameters: filename, binary, append, buffered, verboseOnly
addResponseAction(AllRule(), LogResponseAction("/var/log/dnsdist.log", false, true, false, false))

-- Log cache hits (5 parameters)
-- Parameters: filename, binary, append, buffered, verboseOnly
addCacheHitResponseAction(AllRule(), LogResponseAction("/var/log/dnsdist.log", false, true, false, false))

-- Log self-answered repsonse (5 parameters)
-- Parameters: filename, binary, append, buffered, verboseOnly
addSelfAnsweredResponseAction(AllRule(), LogResponseAction("/var/log/dnsdist.log", false, true, false, false))

-- 1. Listener & Backend
addLocal('0.0.0.0:53', { reusePort=true, tcpFastOpenQueueSize=100, interface="eth0" })
newServer({address="127.0.0.1:5353", name="bind-main", healthCheckMode='lazy'})
setACL({'0.0.0.0/0'}) -- Opens the door; security is handled below

-- 2. Initialize Dynamic Blocking & Whitelist
local dbr = dynBlockRulesGroup()
dbr:excludeRange({"127.0.0.1/8"}) -- WHITELIST

-- 3. Static Security Rules (Applied to EVERY query immediately)
addAction(QTypeRule(DNSQType.ANY), TCAction()) -- Force ANY to TCP

local cnDomains = newSuffixMatchNode()
cnDomains:add("cn")
addAction(SuffixMatchNodeRule(cnDomains), DropAction())

-- 4. Dynamic Rules (Fail2ban style)
dbr:setQueryRate(20, 10, "Exceeded query rate", 60)
dbr:setRCodeRate(DNSRCode.NXDOMAIN, 5, 10, "Exceeded NXDOMAIN rate", 60)
dbr:setRCodeRate(DNSRCode.SERVFAIL, 2, 10, "Exceeded SERVFAIL rate", 60)
dbr:setRCodeRate(DNSRCode.REFUSED, 1, 30, "Exceeded REFUSED rate", 60)

-- 5. Maintenance Loop
function maintenance()
dbr:apply()
end

Mattermost im Tor-Netzwerk hinter einem nginx Reverse-Proxy

Richtig, ich betreibe eine kleine Mattermost-Instanz zum Austausch von Gedanken im Security-Umfeld. Bevor jemand fragt: Wir tragen alle weiße Hüte.

Jedenfalls: Meine Mattermost-Instanz im regulären Internet läuft fein, auch die Tor-Config war rasch erledigt, allerdings bin ich dann über diesen Fehler gestolpert: “Error: Please check connection, Mattermost unreachable. If issue persists, ask administrator to check WebSocket port”

Nach einer kurzen Recherche war das Problem rasch gefunden, mein davor geschnallter Reverse-Proxy (nginx) hatte ein kleines Problem mit den Websockets.

Wie auch immer, hier ist eine funktionierende nginx-Config (Keine Garantien auf irgendwas, das ist ein Experiment.), vielleicht hilft das mal jemandem von euch.

upstream backend-onion {
  # mattermost listening port is 8065 in my config
  server localhost:8065;
  keepalive 32;
}

server {
  # 8080 is the tor hidden service port in my config
  listen 127.0.0.1:8080 default_server;
  server_name localhost;
  port_in_redirect off;

  location = /favicon.ico {
    log_not_found off;
    access_log off;
  }

  location ~ /api/v[0-9]+/(users/)?websocket$ {
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    client_max_body_size 50M;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Frame-Options SAMEORIGIN;
    proxy_set_header CLIENT_HOST $remote_addr;
    proxy_set_header Origin "";
    proxy_buffers 256 16k;
    proxy_buffer_size 16k;
    client_body_timeout 60;
    send_timeout 300;
    lingering_timeout 5;
    proxy_connect_timeout 90;
    proxy_send_timeout 300;
    proxy_read_timeout 90s;
    proxy_http_version 1.1;
    proxy_pass http://backend-onion;
  }

  location / {
    client_max_body_size 50M;
    proxy_set_header Connection "";
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Frame-Options SAMEORIGIN;
    proxy_buffers 256 16k;
    proxy_buffer_size 16k;
    proxy_read_timeout 600s;
    proxy_cache mattermost_cache;
    proxy_cache_revalidate on;
    proxy_cache_min_uses 2;
    proxy_cache_use_stale timeout;
    proxy_cache_lock on;
    proxy_http_version 1.1;
    proxy_pass http://backend-onion;
  }
}