noisebridge-wiki/modules/security.nix
Jet 8cfede9f57
Some checks failed
CI / check (push) Has been cancelled
CI / deploy (push) Has been cancelled
feat: init
2026-03-17 04:07:44 -07:00

121 lines
4.6 KiB
Nix

{ config, pkgs, ... }:
{
networking.firewall = {
enable = true;
# SSH is NOT public — only accessible via Tailscale (trustedInterfaces)
allowedTCPPorts = [
80 # HTTP (Caddy ACME + redirect)
443 # HTTPS
];
logReversePathDrops = true;
# Kernel-level DDoS protection via iptables
# These rules fire BEFORE Caddy even sees the packet, so they're very cheap.
extraCommands = ''
# SYN flood protection
# Limit new TCP connections to 30/sec per source IP (burst 50).
# Legitimate browsers open ~6 connections; scrapers open hundreds.
iptables -N RATE_LIMIT 2>/dev/null || iptables -F RATE_LIMIT
iptables -A RATE_LIMIT -m hashlimit \
--hashlimit-name syn_flood \
--hashlimit-above 30/sec \
--hashlimit-burst 50 \
--hashlimit-mode srcip \
--hashlimit-htable-expire 300000 \
-j DROP
iptables -A RATE_LIMIT -j RETURN
# Hook into INPUT chain for new TCP SYN packets to HTTP/HTTPS
iptables -C INPUT -p tcp --syn -m multiport --dports 80,443 -j RATE_LIMIT 2>/dev/null || \
iptables -I INPUT -p tcp --syn -m multiport --dports 80,443 -j RATE_LIMIT
# Connection limit
# Max 200 concurrent connections per source IP to HTTP/HTTPS.
# A single browser uses ~6-10; a scraper farm uses thousands.
iptables -C INPUT -p tcp -m multiport --dports 80,443 -m connlimit --connlimit-above 200 --connlimit-mask 32 -j DROP 2>/dev/null || \
iptables -I INPUT -p tcp -m multiport --dports 80,443 -m connlimit --connlimit-above 200 --connlimit-mask 32 -j DROP
# Same for IPv6
ip6tables -N RATE_LIMIT 2>/dev/null || ip6tables -F RATE_LIMIT
ip6tables -A RATE_LIMIT -m hashlimit \
--hashlimit-name syn_flood_v6 \
--hashlimit-above 30/sec \
--hashlimit-burst 50 \
--hashlimit-mode srcip \
--hashlimit-htable-expire 300000 \
-j DROP
ip6tables -A RATE_LIMIT -j RETURN
ip6tables -C INPUT -p tcp --syn -m multiport --dports 80,443 -j RATE_LIMIT 2>/dev/null || \
ip6tables -I INPUT -p tcp --syn -m multiport --dports 80,443 -j RATE_LIMIT
ip6tables -C INPUT -p tcp -m multiport --dports 80,443 -m connlimit --connlimit-above 200 --connlimit-mask 64 -j DROP 2>/dev/null || \
ip6tables -I INPUT -p tcp -m multiport --dports 80,443 -m connlimit --connlimit-above 200 --connlimit-mask 64 -j DROP
'';
# Clean up custom chains on stop
extraStopCommands = ''
iptables -D INPUT -p tcp --syn -m multiport --dports 80,443 -j RATE_LIMIT 2>/dev/null || true
iptables -D INPUT -p tcp -m multiport --dports 80,443 -m connlimit --connlimit-above 200 --connlimit-mask 32 -j DROP 2>/dev/null || true
iptables -F RATE_LIMIT 2>/dev/null || true
iptables -X RATE_LIMIT 2>/dev/null || true
ip6tables -D INPUT -p tcp --syn -m multiport --dports 80,443 -j RATE_LIMIT 2>/dev/null || true
ip6tables -D INPUT -p tcp -m multiport --dports 80,443 -m connlimit --connlimit-above 200 --connlimit-mask 64 -j DROP 2>/dev/null || true
ip6tables -F RATE_LIMIT 2>/dev/null || true
ip6tables -X RATE_LIMIT 2>/dev/null || true
'';
};
services.openssh = {
enable = true;
settings = {
PasswordAuthentication = false;
KbdInteractiveAuthentication = false;
PermitRootLogin = "prohibit-password";
X11Forwarding = false;
MaxAuthTries = 3;
};
# Do NOT open firewall — SSH only over Tailscale
openFirewall = false;
};
# Fail2ban for HTTP abuse (not SSH — SSH isn't public)
services.fail2ban = {
enable = true;
maxretry = 5;
bantime = "1h";
bantime-increment = {
enable = true;
maxtime = "48h";
};
};
boot.kernel.sysctl = {
# Reverse path filtering
"net.ipv4.conf.all.rp_filter" = 1;
"net.ipv4.conf.default.rp_filter" = 1;
# Ignore broadcast pings
"net.ipv4.icmp_echo_ignore_broadcasts" = 1;
# Don't accept or send redirects
"net.ipv4.conf.all.accept_redirects" = 0;
"net.ipv6.conf.all.accept_redirects" = 0;
"net.ipv4.conf.all.send_redirects" = 0;
# Reject source-routed packets
"net.ipv4.conf.all.accept_source_route" = 0;
"net.ipv6.conf.all.accept_source_route" = 0;
# SYN flood protection (kernel-level SYN cookies)
"net.ipv4.tcp_syncookies" = 1;
"net.ipv4.tcp_max_syn_backlog" = 4096;
# Reduce TIME_WAIT accumulation from abusive connections
"net.ipv4.tcp_fin_timeout" = 15;
# Connection tracking table size (default 65536 is too small under DDoS)
"net.netfilter.nf_conntrack_max" = 262144;
};
}