diff --git a/bundles/iptables/files/00-defaults b/bundles/iptables/files/00-defaults new file mode 100644 index 0000000..af1ca28 --- /dev/null +++ b/bundles/iptables/files/00-defaults @@ -0,0 +1 @@ +iptables_both -A INPUT -p tcp --dport 22 -j ACCEPT diff --git a/bundles/iptables/files/iptables-enforce b/bundles/iptables/files/iptables-enforce new file mode 100644 index 0000000..1437601 --- /dev/null +++ b/bundles/iptables/files/iptables-enforce @@ -0,0 +1,85 @@ +#!/bin/bash + +% if not node.metadata.get('iptables', {}).get('enabled', True): +exit 0 +% endif + +lock_try=0 +while ! mkdir /run/bw-iptables.lock >/dev/null 2>&1 +do + ((lock_try++)) + if (( lock_try == 10 )) + then + echo 'FATAL: iptables-enforce: Could not get lock!' >&2 + exit 1 + fi + sleep 1 +done +trap 'rmdir /run/bw-iptables.lock' EXIT + +iptables_both() +{ + iptables "$@" + ip6tables "$@" +} + +iptables_both -P INPUT DROP +iptables_both -P OUTPUT ACCEPT +iptables_both -P FORWARD DROP +iptables_both -F +iptables_both -X +iptables_both -t nat -F +iptables_both -t nat -X +iptables_both -t nat -Z +iptables_both -t filter -F +iptables_both -t filter -X +iptables_both -t filter -Z +iptables_both -t mangle -F +iptables_both -t mangle -X +iptables_both -t mangle -Z + +# Workaround for CVE-2019-11477, CVE-2019-11478 and CVE-2019-11479 +# https://www.openwall.com/lists/oss-security/2019/06/17/5 +# https://people.canonical.com/~ubuntu-security/cve/2019/CVE-2019-11477.html +iptables_both -I INPUT -p tcp -m tcpmss --mss 1:500 -j DROP + +# Dummy rules to make sure the conntrack table(s) will be updated. +iptables_both -I INPUT -m state --state NEW,ESTABLISHED,RELATED +iptables_both -I OUTPUT -m state --state NEW,ESTABLISHED,RELATED +iptables_both -I FORWARD -m state --state NEW,ESTABLISHED,RELATED + +# open up local loopback +iptables_both -A INPUT -i lo -j ACCEPT + +# Set Up counting rules +% for ip in sorted(ipv4): +iptables -A INPUT -d ${ip} +iptables -A OUTPUT -s ${ip} +% endfor + +% for ip in sorted(ipv6): +ip6tables -A INPUT -d ${ip} +ip6tables -A OUTPUT -s ${ip} +% endfor + +iptables -A INPUT -p ICMP --icmp-type timestamp-request -j DROP +iptables -A INPUT -p ICMP --icmp-type timestamp-reply -j DROP +# allow ICMP -- answers for IPv4 are covered by conntrack +iptables -A INPUT -p icmp -j ACCEPT + +# ICMP6 is used for so many things, we should under no circumstances +# ignore it and thus should not rely on any conntrack heuristics. +ip6tables -A INPUT -p ipv6-icmp -j ACCEPT + +# Allow incoming answers. Install this first (before the larger ruleset +# from /etc/network/iptables-rules.d/), so that iptables can match/exit +# early. +iptables_both -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT + +shopt -s nullglob +for i in /etc/iptables-rules.d/* +do + . "$i" +done + +cat /etc/sysctl.d/*.conf /etc/sysctl.conf | sysctl -e -p - diff --git a/bundles/iptables/files/iptables-enforce.service b/bundles/iptables/files/iptables-enforce.service new file mode 100644 index 0000000..6eeac90 --- /dev/null +++ b/bundles/iptables/files/iptables-enforce.service @@ -0,0 +1,11 @@ +[Unit] +Description=Run iptables-enforce after networkd startup +Requires=network-online.target +After=network-online.target + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/iptables-enforce + +[Install] +WantedBy=multi-user.target diff --git a/bundles/iptables/items.py b/bundles/iptables/items.py new file mode 100644 index 0000000..3befc26 --- /dev/null +++ b/bundles/iptables/items.py @@ -0,0 +1,60 @@ +directories = { + '/etc/iptables-rules.d': { + 'purge': True, + }, +} + +files = { + '/etc/systemd/system/iptables-enforce.service': { + 'triggers': { + 'action:systemd-reload', + }, + }, + '/usr/local/sbin/iptables-enforce': { + 'content_type': 'mako', + 'context': repo.libs.tools.resolve_identifier(repo, node.name), + 'mode': '0700', + 'triggers': { + 'action:iptables_enforce', + }, + }, + '/etc/iptables-rules.d/00-defaults': { + 'triggers': { + 'action:iptables_enforce', + }, + }, +} + +for bundle, rules in node.metadata.get('iptables', {}).get('bundle_rules', {}).items(): + files[f'/etc/iptables-rules.d/20-{bundle}'] = { + # We must never use sorted() here. Bundles might rely on their order. + 'content': '\n'.join(rules), + 'triggers': { + 'action:iptables_enforce', + }, + } + +if 'custom_rules' in node.metadata.get('iptables', {}): + files[f'/etc/iptables-rules.d/40-custom'] = { + 'content': '\n'.join(node.metadata['iptables']['custom_rules']), + 'triggers': { + 'action:iptables_enforce', + }, + } + + +actions = { + 'iptables_enforce': { + 'command': '/usr/local/sbin/iptables-enforce', + 'triggered': True, + }, +} + +svc_systemd = { + 'iptables-enforce': { + 'running': None, + 'needs': { + 'file:/etc/systemd/system/iptables-enforce.service', + }, + }, +} diff --git a/nodes/home/router.py b/nodes/home/router.py index bd7c534..c0ac875 100644 --- a/nodes/home/router.py +++ b/nodes/home/router.py @@ -1,6 +1,7 @@ nodes['home.router'] = { 'hostname': '172.19.138.10', 'bundles': { + 'iptables', 'pppd', }, 'groups': set(), @@ -24,6 +25,14 @@ nodes['home.router'] = { 'backups': { 'exclude_from_backups': True, }, + 'iptables': { + 'custom_rules': [ + 'iptables_both -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT', + 'iptables_both -A FORWARD -i enp1s0.23 -o enp1s0.42 -j REJECT', + 'iptables_both -A FORWARD -i enp1s0.23 -j ACCEPT', + 'iptables_both -A FORWARD -i enp1s0.42 -j ACCEPT', + ], + }, 'pppd': { 'username': vault.decrypt('encrypt$gAAAAABfruZ5AZbgJ3mfMLWqIMx8o4bBRMJsDPD1jElh-vWN_gnhiuZVjrQ1-7Y6zDXNkxXiyhx8rxc2enmvo26axd7EBI8FqknCptXAPruVtDZrBCis4TE='), 'password': vault.decrypt('encrypt$gAAAAABfruaXEDkaFksFMU8g97ydWyJF8p2KcSDJJBlzaOLDsLL6oCDYjG1kMPVESOzqjn8ThtSht1uZDuMCstA-sATmLS-EWQ=='),