From 8f10788e556c43a535b07cb23bc4d75eac1d3e0b Mon Sep 17 00:00:00 2001 From: Thomas von Dein Date: Thu, 3 Dec 2020 19:01:05 +0100 Subject: [PATCH] added pf support --- README.md | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ jaildk | 85 ++++++++++++++++++++++++++++++++++-- 2 files changed, 208 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8a4da3a..150d59f 100644 --- a/README.md +++ b/README.md @@ -418,6 +418,132 @@ which uses custom ipfw rules: `exec.prestart = "/jail/bin/jaildk ipfw $name"` +Be aware, that the ipfw module will only be executed if the jail is +running so that we can properly determine the ip addresses of the +running jail. **Note**: this might change in the future. + +### Using pf + +Beside ipfw, Free supports +[pf](https://www.freebsd.org/doc/de_DE.ISO8859-1/books/handbook/firewalls-pf.html) +as well. You can use pf with `jaildk`. Unlike the ipfw module (see +above) it is a normal `install` module. That is it can be installed or +reloaded before the jail is running (i.e. like the mount module). + +In order to use `pf` with a jail, enable and configure it according to +the FreeBSD handbook linked above. It is recommended to include +general block, scrup, state rules, communication to and fro localhost +etc and just leave everything which is related to your jail. + +Just so that you know how such a global `/etc/pf.conf` file might look +like, here's a simple one: +```shell +# variables +ext = "em0" +me = "your ipv4 address here" +me5 = "your ipv6 address here/64" +loginports = "{ 22, 5222, 443 }" +icmp_types = "echoreq" + +# tables. look at the contents of a table: +# pfctl -t bad_hosts -T show +# remove an entry from a table: +# pfctl -t bad_hosts -T delete $ip +table persist + +# default policy +set block-policy drop + +# optimize according to rfc's +set optimization aggressive + +# normalisation +scrub in all +antispoof for $ext + +# allow localhost +pass quick on $local + +# additional default block rules w/ logging. to view the log: +# tcpdump -n -e -ttt -r /var/log/pflog +# to view live log: +# tcpdump -n -e -ttt -i pflog0 +block in log on $ext +block in log on $ext inet6 + +# whoever makes it into those tables: you loose +block quick from + +# allow outgoing established sessions +pass out keep state +pass out inet6 keep state + +# allow troubleshooting +pass in on $ext inet proto icmp all icmp-type $icmp_types keep state +pass in on $ext inet proto udp from any to any port 33433 >< 33626 keep state + +# allow all icmpv6 +pass in quick inet6 proto icmp6 all keep state + +# allow login but punish offenders +block quick from +pass in quick on $ext inet proto tcp from any to $me port $loginports \ + flags S/SAFR keep state \ + (max-src-conn-rate 10/60, \ + overload flush global) label ServicesTCP +pass in quick on $ext inet6 proto tcp from any to $me6 port $loginports \ + flags S/SAFR keep state \ + (max-src-conn-rate 10/60, \ + overload flush global) label ServicesTCP +``` + +Install the ruleset with `service pf start`. + +Now that everything is prepared you can create a pf.conf file for your +jail. Here's an example I use for a webserver jail, which includes a +git server: +```shell +ip = "jail ip4 addr" +ip6 = "jail ip6 addr" +loginports = "{ 22 }" +prodports = "{ 80, 443 }" +ext = "em0" + +# dynamic block list +table + +# restrict foreigners +block quick from +pass in quick on $ext inet proto tcp from any to $ip port $loginports \ + flags S/SAFR keep state \ + (max-src-conn-rate 10/60, \ + overload flush global) label ServicesTCP + +# allow production traffic v4 +pass in quick on $ext proto tcp from any to $ip port $prodports keep state + +# allow production traffic v6 +pass in quick inet6 proto tcp from any to $ip6 port $prodports keep state +``` + +That's it already. Now install the jail as usual. You can also install +the pf ruleset for the jail separately: + +`jaildk install myjail start -r pf` + +To take look at the rules, execute: + +`jaildk install myjail status -r pf` + +You can of course manipulate the ruleset manually. `jaildk` installs +rulesets into a jail specific anchor using the following naming +scheme: `/jail/`. So, for example to view the rules, execute: + +`pfctl -a /jail/myjail -s rules` + +Manipulate a jail specific table: + +`pfctl -a /jail/myjail -t blocked -T show` ## Getting help diff --git a/jaildk b/jaildk index 0939ca8..9e2c928 100755 --- a/jaildk +++ b/jaildk @@ -1,6 +1,6 @@ #!/bin/sh -version=1.08 +version=1.09 usage_jaildk() { beg=`tput -T ${TERM:-cons25} md` @@ -119,6 +119,58 @@ die_if_not_exist() { fi } +parse_jail_conf() { + # + # just in case we want or have to fetch variables out of + # /etc/jail.conf, this is the way to go. Call it like this: + # + # ip=`parse_jail_conf $jail ip4.addr` + # + # Output may be empty, so check before using. Multiple variables + # of the same type (like multiple ip addresses) will be returned + # comma separated. + jail=$1 + search=$2 + JAIL='' + list='' + + # fetch 20 lines after "^$jail {", ignore comments + egrep -A20 "^$jail" jail.conf | egrep -v "^ *#" | \ + # turn each line into an evaluable shell expression \ + sed -e 's/ *{//g' -e 's/}//g' -e 's/ *= */=/g' -e 's/;$//' | \ + # ignore empty lines \ + egrep -v '^$' | while read LINE; do + if echo "$LINE" | egrep -q "="; then + case $JAIL in + $jail) + var=`echo "$LINE" | cut -d= -f1` + opt=`echo "$LINE" | cut -d= -f2 | sed -e 's/^"//' -e 's/"$//'` + case $var in + $search) + if test -z "$list"; then + list="$opt" + else + list="$list,$opt" + fi + ;; + esac + ;; + *) + echo $list + return + ;; + esac + else + case $LINE in + \*) JAIL=any;; + *) JAIL="$LINE";; + esac + fi + done +} + + + usage_build() { fin "Usage: $0 build [] [-b ] [-v ] Mount to $j/build read-writable for maintenance. Options: @@ -171,6 +223,33 @@ jaildk_build() { esac } +jaildk_rc_pf() { + jail=$1 + mode=$2 + conf=$j/etc/$jail/pf.conf + + # FIXME: maybe we use parse_jail_conf() to fetch ip addresses, + # generate a config file containing pf macros, which the user + # needs to include in the jails pf.conf? On the other hand, + # there's not that much duplication in the config. So, maybe not. + if test -f $conf; then + case $mode in + start) + bold "Installing PF rules for jail $jail:" + pfctl -a /jail/$jail -f $conf -v + ;; + status) + bold "PF rules for jail $jail:" + pfctl -a /jail/$jail -s rules -v + ;; + stop) + bold "Removing PF rules for jail $jail:" + pfctl -a /jail/$jail -f $conf -v -F all + ;; + esac + fi +} + jaildk_rc_mtree() { jail=$1 mode=$2 @@ -1540,8 +1619,8 @@ jaildk_ipfw_delete() { JAILDIR=/jail # install modules -RCSCRIPTS_START="jaildk_rc_mount jaildk_rc_rcoff jaildk_rc_ports jaildk_rc_mtree" -RCSCRIPTS_STOP="jaildk_rc_rcoff jaildk_rc_mount jaildk_rc_ports" +RCSCRIPTS_START="jaildk_rc_mount jaildk_rc_rcoff jaildk_rc_ports jaildk_rc_mtree jaildk_rc_pf" +RCSCRIPTS_STOP="jaildk_rc_pf jaildk_rc_rcoff jaildk_rc_mount jaildk_rc_ports" # globals j=$JAILDIR