From 406c17701c6151900a67ae220601a19e259a8087 Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Mon, 7 May 2018 09:03:56 +0100 Subject: [PATCH] Refactor the firewall script Make it more generic, dropping ai-specific bits here and there. Provide a standard command-line interface including --help and --version options. Move default filter setup to a standalone code snippet. Rename package to just 'firewall'. Bump version to 0.2 to highlight the difference with the original ai-firewall codebase. --- .gitignore | 5 + .gitlab-ci.yml | 37 ++++++ COPYING | 2 +- Makefile | 28 ++-- README | 62 --------- README.md | 58 +++++++++ conf-dist/filter.d/00base | 66 ++++++++++ conf-dist/filter.d/01docker | 8 ++ conf-dist/filter.d/01fail2ban | 7 +- conf-dist/nat.d/01docker | 8 ++ debian/ai-firewall.default | 4 - debian/ai-firewall.init | 35 ----- debian/ai-firewall.postinst | 5 - debian/changelog | 18 +-- debian/compat | 2 +- debian/control | 6 +- debian/firewall.service | 10 ++ debian/rules | 6 +- examples/filter.d/00ring0 | 16 --- examples/filter.d/dns | 2 - examples/filter.d/ftp | 5 - examples/filter.d/jabber | 7 - examples/filter.d/mail | 2 - examples/filter.d/monitoring | 9 -- examples/filter.d/tinc | 3 - examples/filter.d/www | 14 -- firewall | 238 ++++++++++++++-------------------- 27 files changed, 319 insertions(+), 344 deletions(-) create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml delete mode 100644 README create mode 100644 README.md create mode 100644 conf-dist/filter.d/00base create mode 100644 conf-dist/filter.d/01docker create mode 100644 conf-dist/nat.d/01docker delete mode 100644 debian/ai-firewall.default delete mode 100644 debian/ai-firewall.init delete mode 100755 debian/ai-firewall.postinst create mode 100644 debian/firewall.service delete mode 100644 examples/filter.d/00ring0 delete mode 100644 examples/filter.d/dns delete mode 100644 examples/filter.d/ftp delete mode 100644 examples/filter.d/jabber delete mode 100644 examples/filter.d/mail delete mode 100644 examples/filter.d/monitoring delete mode 100644 examples/filter.d/tinc delete mode 100644 examples/filter.d/www diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6bf7bd7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*-stamp +*.debhelper +*.substvars +debian/firewall +debian/files diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..f1f481b --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,37 @@ + +stages: + - build_src + - build_pkg + - upload + +build:src: + stage: build_src + image: "ai/build:stretch" + script: "build-dsc" + artifacts: + paths: + - build-deb/ + only: + - master + +build:pkg: + stage: build_pkg + image: "ai/build:stretch" + script: "build-deb" + dependencies: + - build:src + artifacts: + paths: + - output-deb/ + only: + - master + +upload:pkg: + stage: upload + image: "ai/pkg:base" + script: "upload-packages -r ai3" + dependencies: + - build:pkg + only: + - master + diff --git a/COPYING b/COPYING index fa96ea8..37d284e 100644 --- a/COPYING +++ b/COPYING @@ -1,4 +1,4 @@ -Copyright (C) 2012, Autistici/Inventati <info@inventati.org>. +Copyright (C) 2012-2018, Autistici/Inventati <info@inventati.org>. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/Makefile b/Makefile index c4099dc..5db5c90 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,30 @@ prefix = /usr +sbindir = $(prefix)/sbin sysconfdir = /etc +fwconfdir = $(sysconfdir)/firewall +sharedir = $(prefix)/share/firewall INSTALL = install +TABLES = filter nat mangle all: clean: install: - $(INSTALL) -d $(DESTDIR)$(prefix)/bin - $(INSTALL) -m 755 firewall $(DESTDIR)$(prefix)/bin/firewall - $(INSTALL) -d $(DESTDIR)$(sysconfdir)/firewall - $(INSTALL) -d $(DESTDIR)$(sysconfdir)/firewall/filter.d - $(INSTALL) -d $(DESTDIR)$(sysconfdir)/firewall/nat.d - $(INSTALL) -d $(DESTDIR)$(sysconfdir)/firewall/mangle.d - $(INSTALL) -m 644 README $(DESTDIR)$(sysconfdir)/firewall/README - (for f in ./conf-dist/filter.d/* ; do \ - $(INSTALL) -m 644 $$f $(DESTDIR)$(sysconfdir)/firewall/filter.d ; done) + $(INSTALL) -d $(DESTDIR)$(sbindir) + $(INSTALL) -d $(DESTDIR)$(sharedir) + $(INSTALL) -d $(DESTDIR)$(fwconfdir) + $(INSTALL) -m 755 firewall $(DESTDIR)$(sbindir)/firewall + (for t in $(TABLES); do \ + $(INSTALL) -d $(DESTDIR)$(fwconfdir)/$$t.d ; \ + $(INSTALL) -d $(DESTDIR)$(sharedir)/$$t.d ; \ + if [ -d conf-dist/$$t.d ]; then \ + for f in conf-dist/$$t.d/* ; do \ + $(INSTALL) -m 644 $$f $(DESTDIR)$(sharedir)/$$t.d ; \ + b=$$(basename $$f) ; \ + ln -s $(sharedir)/$$t.d/$$b $(DESTDIR)$(fwconfdir)/$$t.d/$$b ; \ + done ; \ + fi ; \ + done) diff --git a/README b/README deleted file mode 100644 index fa0280c..0000000 --- a/README +++ /dev/null @@ -1,62 +0,0 @@ - -ai-firewall -=========== - -A shell-based DSL for quick and easy configuration of an iptables -firewall, primarily targeted at individual servers, supporting both -IPv4 and IPv6. - -ai-firewall will perform some basic setup and then execute -application-specific configuration snippets from the /etc/firewall -tree. This setup allows packages to plug into the firewall setup by -simply deploying a snippet in /etc/firewall. - -The configuration is loaded from the directories below /etc/firewall, -every iptables table (such as 'filter', 'nat', and 'mangle') is -configured independently from its own subdirectory named after itself, -with a '.d' extension. Individual files from each directory are loaded -in lexicographical order (like run-parts, for instance). - - -Configuration syntax --------------------- - -Configuration files are simple shell scripts. Rules are generated by -invoking the following predefined helper functions: - - - create_chain <CHAIN_NAME> - - Create a new chain with the specified name. - - - add_rule <IPTABLES_ARGS> - add_rule4 <IPTABLES_ARGS> - add_rule6 <IPTABLES_ARGS> - - This function will generate a full iptables rule exactly as - specified. The first form will generate the rule for IPv4 and - IPv6, the other two are protocol-specific. - - An example: - - add_rule -A bad-host -s 1.2.3.4 -j DROP - - - add_to_chain <CHAIN_NAME> <IPTABLES_ARGS> - - A shortcut for 'add_rule -A <CHAIN_NAME> <IPTABLES_ARGS>'. - - - add_user_port <PROTOCOL> <PORT> [<TARGET>] - - Allow incoming traffic to the specified protocol / port. - - - add_user_ports <PROTOCOL> <PORT_SPEC> - - Allow incoming traffic to the specified ports. PORT_SPEC - should be a comma-separated list of destination ports. - - - diff --git a/README.md b/README.md new file mode 100644 index 0000000..bab619a --- /dev/null +++ b/README.md @@ -0,0 +1,58 @@ +firewall +===== + +A shell-based DSL for quick and easy configuration of an iptables +firewall, primarily targeted at individual servers, supporting both +IPv4 and IPv6. Instead of parameterizing the hell out of iptables like +more sophisticated solutions (think Shorewall), it provides helpers to +write iptables configs from shell snippets. These helpers make it easy +to maintain IPv4 and IPv6 rules in sync. + +The main driver script will perform some basic setup and then execute +application-specific configuration snippets from the */etc/firewall* +tree. This setup allows packages to plug into the firewall setup by +simply deploying a snippet in /etc/firewall. + +The configuration is loaded from the directories below /etc/firewall, +every iptables table (such as *filter*, *nat*, and *mangle*) is +configured independently from its own subdirectory named after the +table, with a `.d` extension. Individual files from each directory are +loaded in lexicographical order using *run-parts(8)*. + +# Configuration + +Configuration files are simple shell scripts. Rules are generated by +invoking the following predefined helper functions: + +#### `create_chain` *CHAIN_NAME* + +Create a new chain with the specified name. + +#### `add_rule` *IPTABLES_ARGS* +#### `add_rule4` *IPTABLES_ARGS* +#### `add_rule6` *IPTABLES_ARGS* + +This function will generate a full iptables rule exactly as +specified. The first form will generate the rule for IPv4 and +IPv6, the other two are protocol-specific. + +An example: + +``` +add_rule -A bad-host -s 1.2.3.4 -j DROP +``` + +#### `add_to_chain` *CHAIN_NAME* *IPTABLES_ARGS* + +A shortcut for `add_rule -A CHAIN_NAME IPTABLES_ARGS`. + +#### `allow_port` *PROTOCOL* *PORT* *[IPTABLES_ARGS]* + +Allow incoming traffic to the specified protocol / port. +*IPTABLES_ARGS* is just a placeholder for any number of arbitrary +iptables options (the default is simply `-j ALLOW`). + +#### `allow_ports` *PROTOCOL* *PORT_SPEC* *[IPTABLES_ARGS]* + +Allow incoming traffic to the specified ports. *PORT_SPEC* +should be a comma-separated list of destination ports. diff --git a/conf-dist/filter.d/00base b/conf-dist/filter.d/00base new file mode 100644 index 0000000..7e182ab --- /dev/null +++ b/conf-dist/filter.d/00base @@ -0,0 +1,66 @@ +# Set up basic rules for the 'filter' table. +# +# This snippet should run before the others. + +# Set up a chain that will drop noisy unwanted traffic +# without even logging it. +create_chain drop-noise +add_rule -A drop-noise -p tcp --dport 113 -j REJECT +add_rule -A drop-noise -p tcp -m multiport --dports 139,445 -j DROP +add_rule -A drop-noise -p udp -m multiport --dports 137,138,500 -j DROP +# Be kind and allow old-style traceroutes. +add_rule -A drop-noise -p udp --dport 33434:33500 -j REJECT + +# base-input chain. +create_chain base-input + +# Enable everything from lo and ring0. +add_rule -A base-input -i lo -j ACCEPT +add_rule4 -A base-input -i ring0 -s 172.16.1.0/24 -j ACCEPT + +# Some IPv6-specific ICMP setup. +add_rule6 -A base-input -m rt --rt-type 0 --rt-segsleft 0 -j DROP +for icmp6type in 133 134 135 136 ; do + add_rule6 -A base-input -p ipv6-icmp -m icmp6 \ + --icmpv6-type ${icmp6type} -m hl --hl-eq 255 -j ACCEPT +done + +# Standard conntrack stuff. +add_rule -A base-input -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT +add_rule6 -A base-input -s fe80::/10 -p ipv6-icmp -m icmp6 \ + --icmpv6-type 129 -j ACCEPT +add_rule -A base-input -m conntrack --ctstate INVALID -j DROP + +# Enable 6to4 protocol. +#add_rule4 -A base-input -p ipv6 -j ACCEPT + +# Allow useful ICMPs (but rate-limit incoming echo requests). +for icmptype in 3 4 11 12 ; do + add_rule4 -A base-input -p icmp -m icmp \ + --icmp-type ${icmptype} -j ACCEPT +done +for icmp6type in 1 2 3 4 128 ; do + add_rule6 -A base-input -p ipv6-icmp -m icmp6 \ + --icmpv6-type ${icmp6type} -j ACCEPT +done +add_rule4 -A base-input -p icmp -m icmp --icmp-type 8 \ + -m limit --limit 3/s -j ACCEPT +add_rule6 -A base-input -p ipv6-icmp -m icmp6 --icmp-type 128 \ + -m limit --limit 3/s -j ACCEPT + +# IPv6 autodiscovery. +#add_rule6 -A base-input -s fe80::/10 -d fe80::/10 -p udp -m udp \ +# --sport 547 --dport 546 -j ACCEPT + +# user-input +create_chain user-input + +# Always allow SSH access, just in case someone forgets to add it +# with a user-defined ruleset file. +allow_port tcp 22 + +# Setup the INPUT chain. +# It is split into stages: base-input, user-input +add_rule -A INPUT -j base-input +add_rule -A INPUT -j drop-noise +add_rule -A INPUT -j user-input diff --git a/conf-dist/filter.d/01docker b/conf-dist/filter.d/01docker new file mode 100644 index 0000000..e66ef1f --- /dev/null +++ b/conf-dist/filter.d/01docker @@ -0,0 +1,8 @@ +# Preserve docker-related firewall rules (IPv4-only). +iptables-save -t filter \ + | grep \ + -e '^:DOCKER' \ + -e '^-A DOCKER' \ + -e '-[io] docker[0-9]' \ + -e '-j DOCKER' \ + | (while read line; do add_rule4 "${line}"; done) diff --git a/conf-dist/filter.d/01fail2ban b/conf-dist/filter.d/01fail2ban index 329ccad..57437a7 100644 --- a/conf-dist/filter.d/01fail2ban +++ b/conf-dist/filter.d/01fail2ban @@ -1,12 +1,9 @@ - # The following snippet saves the existing fail2ban rules and -# reproduces them identically in the output. -if [ -x /sbin/iptables-save ]; then - /sbin/iptables-save | (while read line ; do +# reproduces them identically in the output (IPv4-only). +iptables-save -t filter | (while read line ; do case "${line}" in ":fail2ban-"*|"-A fail2ban-"*|*"-j fail2ban-"*) add_rule4 "${line}" ;; esac done) -fi diff --git a/conf-dist/nat.d/01docker b/conf-dist/nat.d/01docker new file mode 100644 index 0000000..bb71ede --- /dev/null +++ b/conf-dist/nat.d/01docker @@ -0,0 +1,8 @@ +# Preserve docker-related firewall rules (IPv4-only). +iptables-save -t nat \ + | grep \ + -e '^:DOCKER' \ + -e '^-A DOCKER' \ + -e '-[io] docker[0-9]' \ + -e '-j DOCKER' \ + | (while read line; do add_rule4 "${line}"; done) diff --git a/debian/ai-firewall.default b/debian/ai-firewall.default deleted file mode 100644 index 3cc5534..0000000 --- a/debian/ai-firewall.default +++ /dev/null @@ -1,4 +0,0 @@ - -# Remove comment to enable. -#ENABLED=true - diff --git a/debian/ai-firewall.init b/debian/ai-firewall.init deleted file mode 100644 index 02d8f6b..0000000 --- a/debian/ai-firewall.init +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# -# Start/stop the A/I firewall. -# -### BEGIN INIT INFO -# Provides: ai-firewall -# Required-Start: $network $local_fs -# Required-Stop: -# Should-Start: -# Should-Stop: -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: A/I Firewall -# Description: A/I Firewall -### END INIT INFO - -ENABLED=false - -test -e /etc/default/ai-firewall && . /etc/default/ai-firewall - -if [ "${ENABLED}" != true ]; then - exit 0 -fi - -case "$1" in -start|restart) - echo -n "Starting firewall... " - /usr/bin/firewall start - echo "ok" - ;; -stop) - ;; -esac - -exit 0 diff --git a/debian/ai-firewall.postinst b/debian/ai-firewall.postinst deleted file mode 100755 index 42faf6a..0000000 --- a/debian/ai-firewall.postinst +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -#DEBHELPER# - -exit 0 diff --git a/debian/changelog b/debian/changelog index b7c4e72..349465c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,17 +1,5 @@ -ai-firewall (0.1-2) unstable; urgency=low +firewall (0.2) unstable; urgency=medium - * Use debhelper for postinst script. + * New package. - -- Autistici/Inventati <debian@autistici.org> Sun, 22 Jun 2014 19:12:21 +0000 - -ai-firewall (0.1-1) unstable; urgency=low - - * Update debian package to start service at boot - - -- Autistici/Inventati <debian@autistici.org> Sun, 22 Jun 2014 18:30:41 +0000 - -ai-firewall (0.1) unstable; urgency=low - - * First packaged release. - - -- Autistici/Inventati <debian@autistici.org> Sat, 13 Sep 2012 14:49:37 +0000 + -- Autistici/Inventati <debian@autistici.org> Mon, 07 May 2018 08:37:01 +0100 diff --git a/debian/compat b/debian/compat index 7f8f011..f599e28 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -7 +10 diff --git a/debian/control b/debian/control index a1d8135..21bd66c 100644 --- a/debian/control +++ b/debian/control @@ -1,13 +1,13 @@ -Source: ai-firewall +Source: firewall Section: net Priority: extra Maintainer: Autistici/Inventati <debian@autistici.org> Build-Depends: debhelper (>= 7), cdbs Standards-Version: 3.8.0.1 -Package: ai-firewall +Package: firewall Architecture: all -Depends: ${misc:Depends}, python, iptables +Depends: ${misc:Depends}, iptables Description: A/I Firewall Script Automatically maintain local firewalls for A/I servers. diff --git a/debian/firewall.service b/debian/firewall.service new file mode 100644 index 0000000..c16f2e1 --- /dev/null +++ b/debian/firewall.service @@ -0,0 +1,10 @@ +[Unit] +Description=Set up firewall + +[Service] +Type=oneshot +EnvironmentFile=-/etc/default/firewall +ExecStart=/usr/sbin/firewall + +[Install] +WantedBy=multi-user.target diff --git a/debian/rules b/debian/rules index ab9566f..9cdd140 100755 --- a/debian/rules +++ b/debian/rules @@ -1,8 +1,8 @@ #!/usr/bin/make -f # -*- makefile -*- -DEB_MAKE_INSTALL_TARGET = install DESTDIR=$(cdbs_make_curdestdir) +export DH_OPTIONS -include /usr/share/cdbs/1/rules/debhelper.mk -include /usr/share/cdbs/1/class/makefile.mk +%: + dh $@ diff --git a/examples/filter.d/00ring0 b/examples/filter.d/00ring0 deleted file mode 100644 index 0cad906..0000000 --- a/examples/filter.d/00ring0 +++ /dev/null @@ -1,16 +0,0 @@ - -# Create a RING0 chain that will accept requests from IP addresses -# in the ring0, and drop everything else. -create_chain RING0 -if [ -e /etc/ai/hosts ]; then - ai_hosts=${AI_HOSTS:-/etc/ai/hosts} - RING0_IP4=$(resolve_addrs_from_file ${ai_hosts} ipv4) - RING0_IP6=$(resolve_addrs_from_file ${ai_hosts} ipv6) - for ip in ${RING0_IP4} ; do - add_rule4 -A RING0 -s ${ip} -j ACCEPT - done - for ip in ${RING0_IP6} ; do - add_rule6 -A RING0 -s ${ip} -j ACCEPT - done -fi -add_rule -A RING0 -j DROP diff --git a/examples/filter.d/dns b/examples/filter.d/dns deleted file mode 100644 index b2469ef..0000000 --- a/examples/filter.d/dns +++ /dev/null @@ -1,2 +0,0 @@ -add_user_port udp 53 -add_user_port tcp 53 diff --git a/examples/filter.d/ftp b/examples/filter.d/ftp deleted file mode 100644 index 7b85f71..0000000 --- a/examples/filter.d/ftp +++ /dev/null @@ -1,5 +0,0 @@ - -add_user_port tcp 21 - -# This is the port range for PASV transfers. -add_user_port tcp 15000:19000 diff --git a/examples/filter.d/jabber b/examples/filter.d/jabber deleted file mode 100644 index d086380..0000000 --- a/examples/filter.d/jabber +++ /dev/null @@ -1,7 +0,0 @@ - -add_user_ports tcp 5222,5223,5269,5280 - -# STUN. -add_user_ports tcp 3478,5349 -add_user_port udp 3478 - diff --git a/examples/filter.d/mail b/examples/filter.d/mail deleted file mode 100644 index b7ee9dd..0000000 --- a/examples/filter.d/mail +++ /dev/null @@ -1,2 +0,0 @@ -add_user_ports tcp 25,110,143,465,587,993,995 - diff --git a/examples/filter.d/monitoring b/examples/filter.d/monitoring deleted file mode 100644 index d91ec3f..0000000 --- a/examples/filter.d/monitoring +++ /dev/null @@ -1,9 +0,0 @@ - -# Create a chain with probe IP ranges. -create_chain MONITORING -add_rule -A MONITORING -s 131.114.114.0/24 -j ACCEPT -add_rule -A MONITORING -s 46.4.206.80/28 -j ACCEPT -add_rule -A MONITORING -j DROP - -add_user_port tcp 3900 -j MONITORING - diff --git a/examples/filter.d/tinc b/examples/filter.d/tinc deleted file mode 100644 index fc1ee68..0000000 --- a/examples/filter.d/tinc +++ /dev/null @@ -1,3 +0,0 @@ -for proto in udp tcp ; do - add_user_ports ${proto} 655,656,657 -j RING0 -done diff --git a/examples/filter.d/www b/examples/filter.d/www deleted file mode 100644 index da3394d..0000000 --- a/examples/filter.d/www +++ /dev/null @@ -1,14 +0,0 @@ - -# Standard HTTP/HTTPS ports. -add_user_ports tcp 80,443 - -# Block outgoing connections from the users' FastCGI runners -# (i.e. the PHP scripts) to only HTTP. -USERS_GROUP=2000 -create_chain user-output-cgi -add_rule -A user-output-cgi -p tcp --syn --dport 80 -j ACCEPT -add_rule -A user-output-cgi -p tcp --syn --dport 443 -j ACCEPT -add_rule -A user-output-cgi -m log --log-prefix \"users-cgi: \" \ - -m limit --limit 3/s -j LOG -add_rule -A user-output-cgi -j REJECT -add_rule -A OUTPUT -m owner --gid-owner ${USERS_GROUP} -j user-output-cgi diff --git a/firewall b/firewall index 3ab2f80..55fe385 100755 --- a/firewall +++ b/firewall @@ -1,14 +1,15 @@ #!/bin/bash -# The following configuration variables can be overridden from the -# environment. Useful in combination with /etc/default or some -# equivalent mechanism. -FW_DIR="${FW_DIR:-/etc/firewall}" -DO_LOG="${DO_LOG:-1}" -LOG_RATE="${LOG_RATE:-5/min}" +# Directory containing the configuration snippets. +CONFIG_DIR="${CONFIG_DIR:-/etc/firewall}" +# List of tables to manage. TABLES="filter nat mangle" +# Use a safe PATH. +PATH=/bin:/sbin:/usr/bin:/usr/sbin +export PATH + # Resolve a host with the given proto (ipv4 or ipv6). resolve_addr() { local addr="$1" @@ -20,17 +21,6 @@ resolve_addr() { python -c "import socket ; print '\n'.join(x[4][0] for x in socket.getaddrinfo('${addr}', 0, socket.${af}, socket.SOCK_STREAM))" 2>/dev/null || true } -# Resolve all hostnames in a file. -resolve_addrs_from_file() { - local filename="$1" - local proto="$2" - awk '{print $1}' < ${filename} \ - | (while read name ; do \ - test -n "${name}" && resolve_addr ${name}.investici.org ${proto} ; \ - done) - return 0 -} - # Add a rule valid for IPv4 and IPv6. add_rule() { add_rule4 "$*" @@ -39,14 +29,12 @@ add_rule() { # Add an IPv4-only rule. add_rule4() { - local args="$*" - echo "${args}" 1>&4 + echo "$*" 1>&4 } # Add an IPv6-only rule. add_rule6() { - local args="$*" - echo "${args}" 1>&6 + echo "$*" 1>&6 } # Create a new iptables chain. @@ -67,7 +55,7 @@ add_to_chain() { # Add a rule to the 'user-input' chain allowing traffic to a # specific port/protocol. -add_user_port() { +allow_port() { local proto="$1" local port="$2" shift 2 @@ -80,7 +68,7 @@ add_user_port() { # Add a rule to the 'user-input' chain allowing traffic to # a set of multiple ports. -add_user_ports() { +allow_ports() { local proto="$1" local ports="$2" shift 2 @@ -108,126 +96,61 @@ generate() { add_rule "*${table_name}" - # Initialize the table. + # Initialize the table with default policies. case ${table_name} in + filter) + create_chain INPUT DROP + create_chain OUTPUT ACCEPT + create_chain FORWARD ACCEPT + ;; nat) - generate_nat + create_chain PREROUTING ACCEPT + create_chain OUTPUT ACCEPT + create_chain POSTROUTING ACCEPT ;; - filter) - generate_filter + mangle) + create_chain PREROUTING ACCEPT + create_chain INPUT ACCEPT + create_chain FORWARD ACCEPT + create_chain OUTPUT ACCEPT + create_chain POSTROUTING ACCEPT ;; - esac + esac # Load user-defined rulesets. - for file in ${conf_root}/* - do - test -f ${file} || continue - case $(basename "${file}") in - *~|.*) - ;; - *) - . ${file} - ;; - esac - done + if [ -d ${conf_root} ]; then + for file in $(run-parts --list ${conf_root}); do + . ${file} + done + fi add_rule COMMIT } -# Initialize the 'nat' table. -generate_nat() { - create_chain PREROUTING ACCEPT - create_chain OUTPUT ACCEPT - create_chain POSTROUTING ACCEPT -} - -# Initialize the 'filter' table. -generate_filter() { - create_chain INPUT DROP - create_chain OUTPUT ACCEPT - create_chain FORWARD ACCEPT - - # Set up a chain that will drop noisy unwanted traffic - # without even logging it. - create_chain drop-noise - add_rule -A drop-noise -p tcp --dport 113 -j REJECT - add_rule -A drop-noise -p tcp -m multiport --dports 139,445 -j DROP - add_rule -A drop-noise -p udp -m multiport --dports 137,138,500 -j DROP - # Be kind and allow old-style traceroutes. - add_rule -A drop-noise -p udp --dport 33434:33500 -j REJECT - - # base-input chain. - create_chain base-input - - # Enable everything from lo and ring0. - add_rule -A base-input -i lo -j ACCEPT - add_rule4 -A base-input -i ring0 -s 172.16.1.0/24 -j ACCEPT - - # Some IPv6-specific ICMP setup. - add_rule6 -A base-input -m rt --rt-type 0 --rt-segsleft 0 -j DROP - for icmp6type in 133 134 135 136 ; do - add_rule6 -A base-input -p ipv6-icmp -m icmp6 \ - --icmpv6-type ${icmp6type} -m hl --hl-eq 255 -j ACCEPT - done - - # Standard conntrack stuff. - add_rule -A base-input -m state --state RELATED,ESTABLISHED -j ACCEPT - add_rule6 -A base-input -s fe80::/10 -p ipv6-icmp -m icmp6 \ - --icmpv6-type 129 -j ACCEPT - add_rule -A base-input -m state --state INVALID -j DROP - - # Enable 6to4 protocols. - add_rule -A base-input -p ipv6 -j ACCEPT - - # Allow useful ICMPs (but rate-limit incoming echo requests). - add_rule4 -A base-input -p icmp -m icmp --icmp-type 8 -m limit \ - --limit 3/s -j ACCEPT - for icmptype in 3 4 11 12 ; do - add_rule4 -A base-input -p icmp -m icmp \ - --icmp-type ${icmptype} -j ACCEPT - done - for icmp6type in 1 2 3 4 128 ; do - add_rule6 -A base-input -p ipv6-icmp -m icmp6 \ - --icmpv6-type ${icmp6type} -j ACCEPT - done - - # IPv6 autodiscovery. - #add_rule6 -A base-input -s fe80::/10 -d fe80::/10 -p udp -m udp \ - # --sport 547 --dport 546 -j ACCEPT - - # user-input - create_chain user-input - - # Always allow SSH access, just in case someone forgets to add it - # with a user-defined ruleset file. - add_user_port tcp 22 - - # Setup the INPUT chain. - # It is split into stages: base-input, user-input - add_rule -A INPUT -j base-input - add_rule -A INPUT -j drop-noise - add_rule -A INPUT -j user-input - - # Logging. - if [ "${DO_LOG}" -eq 1 ]; then - create_chain log-deny - add_rule -A log-deny -j LOG --log-prefix 'deny: ' - add_rule -A INPUT -j log-deny -m limit --limit "${LOG_RATE}" --limit-burst 5 - fi -} - -load() { +load_firewall() { set -e set -u - v4rules=$(mktemp ${TMP:-/tmp}/ip4t.XXXXXX) - v6rules=$(mktemp ${TMP:-/tmp}/ip6t.XXXXXX) - trap "rm -f ${v4rules} ${v6rules} 2>/dev/null; trap - EXIT; exit 0" EXIT + now=$(date) + tmpfiles= + v4rules=/dev/null + v6rules=/dev/null + if [ ${enable_ipv4} -eq 1 ]; then + v4rules=$(mktemp ${TMP:-/tmp}/ip4t.XXXXXX) + tmpfiles="${tmpfiles} ${v4rules}" + echo "# firewall(IPv4). Autogenerated on ${now}" > ${v4rules} + fi + if [ ${enable_ipv6} -eq 1 ]; then + v6rules=$(mktemp ${TMP:-/tmp}/ip6t.XXXXXX) + tmpfiles="${tmpfiles} ${v6rules}" + echo "# firewall(IPv6). Autogenerated on ${now}" > ${v6rules} + fi + trap "rm -f ${tmpfiles} 2>/dev/null; trap - EXIT; exit 0" EXIT # Setup the various tables. Note that IPv6 only has the # 'filter' table. for table in ${TABLES} ; do - table_dir=${FW_DIR}/${table}.d + table_dir=${CONFIG_DIR}/${table}.d case "$table" in filter) run_with_fds ${v4rules} ${v6rules} \ @@ -241,29 +164,58 @@ load() { done if [ ${dry_run} -eq 1 ]; then - echo "/sbin/iptables-restore <${v4rules}" - cat ${v4rules} - echo "/sbin/ip6tables-restore <${v6rules}" - cat ${v6rules} + [ ${enable_ipv4} -eq 0 ] || cat ${v4rules} + [ ${enable_ipv6} -eq 0 ] || cat ${v6rules} else - /sbin/iptables-restore <${v4rules} - /sbin/ip6tables-restore <${v6rules} + [ ${enable_ipv4} -eq 0 ] || iptables-restore <${v4rules} + [ ${enable_ipv6} -eq 0 ] || ip6tables-restore <${v6rules} fi } +usage() { + cat <<EOF +Usage: $0 [<OPTIONS>] +Known options: + -4 Generate only the IPv4 configuration. + -6 Generate only the IPv6 configuration. + -n, --dry-run Do not apply iptables config, just output the results. + -h, --help Print this help message. + +EOF +} + dry_run=0 -if [ "$1" = "-n" ]; then - dry_run=1 +enable_ipv4=1 +enable_ipv6=1 + +while [ $# -gt 0 ]; do + case "$1" in + -4) + enable_ipv6=0 + ;; + -6) + enable_ipv4=0 + ;; + -n|--dry-run) + dry_run=1 + ;; + -h|--help) + usage + exit 0 + ;; + --version) + echo "firewall v0.2" + exit 0 + ;; + *) + echo "Unknown argument '$1'" >&2 + usage >&2 + exit 2 + ;; + esac shift -fi +done -case "$1" in - start|load|reload) - load - ;; - *) - echo "Usage: $0 {start|reload}" 1>&2 - ;; -esac +load_firewall exit 0 -- GitLab