From b0e440d652160e1d544ae7fbb10338ac08f9b86d Mon Sep 17 00:00:00 2001 From: Viyurz Date: Tue, 1 Oct 2024 13:44:53 +0200 Subject: [PATCH] [manage.py] Add setNftables() & sudoRun() --- manage.py | 23 ++++++++++-- nftables.conf.mako | 90 ++++++++++++++++++++++++++++++++++++++++++++++ setup-podman.sh | 3 +- 3 files changed, 112 insertions(+), 4 deletions(-) create mode 100755 nftables.conf.mako diff --git a/manage.py b/manage.py index c769ce5..a1eac72 100755 --- a/manage.py +++ b/manage.py @@ -1,6 +1,7 @@ #!/usr/bin/python3 import os, sys, re +import filecmp from glob import glob from mako.template import Template import subprocess @@ -74,6 +75,16 @@ def setCertPerms(service): setPerms(pkeyFile, 640) +def setNftables(): + renderFile("nftables.conf.mako") + if not filecmp.cmp("nftables.conf.rendered", "/etc/nftables.conf"): + print("nftables.conf changed, copying new version.") + sudoRun(["cp", "nftables.conf.rendered", "/etc/nftables.conf"]) + sudoRun(["systemctl", "restart", "nftables"]) + else: + print("nftables.conf unchanged.") + + def setOwner(path, uid=None, gid=None): stat = os.stat(path) if not uid: @@ -83,9 +94,7 @@ def setOwner(path, uid=None, gid=None): if stat.st_uid != uid or stat.st_gid != gid: print(f"Changing ownership of {path} to {uid}:{gid} from {stat.st_uid}:{stat.st_gid}.") - child = subprocess.Popen(["sudo", "chown", f"{uid}:{gid}", path], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - if re.search('^\\[sudo\\] password for .+', child.stdout.read().strip()): - child.communicate(input=input().encode())[0] + sudoRun(["chown", f"{uid}:{gid}", path]) else: print(f"Ownership of {path} already set to {uid}:{gid}.") @@ -119,6 +128,12 @@ def setupProj(project): upProj(project) +def sudoRun(args): + child = subprocess.Popen(["sudo"] + args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if re.search('^\\[sudo\\] password for .+', child.stdout.read().strip()): + child.communicate(input=input().encode())[0] + + def upProj(project): print(f"Creating & starting stack for project {project}.") if subprocess.run(["podman", "container", "exists", project]).returncode == 0: @@ -172,6 +187,8 @@ def main(): target_projects = target_projects.split(' ') print(f"Target projects: {target_projects}") + setNftables() + match action: case '1' | 'S': for project in target_projects: diff --git a/nftables.conf.mako b/nftables.conf.mako new file mode 100755 index 0000000..e1d15cc --- /dev/null +++ b/nftables.conf.mako @@ -0,0 +1,90 @@ +#!/usr/sbin/nft -f + +flush ruleset + +table inet nat { + chain prerouting { + type nat hook prerouting priority dstnat; + iif eth0 tcp dport ${env['ports']['syncthing_relaysrv']} redirect to :22067 + iif eth0 tcp dport 25 redirect to :${env['ports']['mailserver_smtp']} + iif eth0 tcp dport 465 redirect to :${env['ports']['mailserver_smtps']} + iif eth0 tcp dport 993 redirect to :${env['ports']['mailserver_imaps']} + } +} + +table inet filter { + set blackhole_ipv4 { + type ipv4_addr + timeout 30s + flags dynamic + } + + set blackhole_ipv6 { + type ipv6_addr + timeout 30s + flags dynamic + } + + chain input { + type filter hook input priority 0; policy drop; + + iif lo accept + + # Block all IPs in blackhole + ip saddr @blackhole_ipv4 set update ip saddr @blackhole_ipv4 drop + ip6 saddr @blackhole_ipv6 set update ip6 saddr @blackhole_ipv6 drop + + ct state invalid drop + ct state { established, related } accept + + <%text> + # Prevent DDoS + # Rate limiting + meta nfproto ipv4 meter ratelimit4 \ + { ip saddr limit rate over 75/second burst 15 packets } \ + add @blackhole_ipv4 { ip saddr } counter + meta nfproto ipv6 meter ratelimit6 \ + { ip6 saddr limit rate over 75/second burst 15 packets } \ + add @blackhole_ipv6 { ip6 saddr } counter + # Max concurrent connections + meta nfproto ipv4 meter connlimit4 \ + { ip saddr ct count over 100 } add @blackhole_ipv4 { ip saddr } counter + meta nfproto ipv6 meter connlimit6 \ + { ip6 saddr ct count over 100 } add @blackhole_ipv6 { ip6 saddr } counter + + + # Allow ICMP + meta l4proto icmp accept + meta l4proto ipv6-icmp accept + + # HTTP/S + tcp dport { http, https } accept + + # SSH + tcp dport ssh accept + + # SMTP/IMAP + tcp dport { ${env['ports']['mailserver_smtp']}, ${env['ports']['mailserver_smtps']}, ${env['ports']['mailserver_imaps']} } accept + + # Syncthing + tcp dport { ${env['ports']['syncthing_tcp']}, 22067 } accept + udp dport ${env['ports']['syncthing_udp']} accept + + # Coturn + tcp dport { ${env['ports']['coturn_listening']}, ${env['ports']['coturn_tls_listening']} } accept + udp dport { ${env['ports']['coturn_listening']}, ${env['ports']['coturn_tls_listening']}, ${env['ports']['coturn_relay_min']}-${env['ports']['coturn_relay_max']} } accept + + } + + chain forward { + type filter hook forward priority 0; policy accept; + } + + chain output { + type filter hook output priority 0; policy accept; + + # Don't waste resources responding to blocked IPs + ip daddr @blackhole_ipv4 reject + ip6 daddr @blackhole_ipv6 reject + } +} diff --git a/setup-podman.sh b/setup-podman.sh index d3e2155..eaa77eb 100755 --- a/setup-podman.sh +++ b/setup-podman.sh @@ -6,7 +6,7 @@ if [[ $(whoami) == "root" ]]; then fi -sudo apt install -y aardvark-dns dbus-user-session passt podman podman-compose python3-mako uidmap +sudo apt install -y aardvark-dns dbus-user-session nftables passt podman podman-compose python3-mako uidmap for unit in podman.service podman.socket podman-auto-update.service podman-auto-update.timer podman-clean-transient.service podman-restart.service; do @@ -36,5 +36,6 @@ for key in "${!sysctl_vars[@]}"; do done +systemctl enable --now nftables systemctl --user daemon-reload systemctl --user restart podman