diff --git a/README.md b/README.md index 9e43fee6..3afe2eaf 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,7 @@ Available Commands: import Import a specified container. list List containers (running and stopped). pkg Manipulate binary packages within targeted container(s). See pkg(8). + rdr Redirect host port to container port. restart Restart a running container. service Manage services within targeted container(s). start Start a stopped container. @@ -117,13 +118,21 @@ set skip on lo table persist nat on $ext_if from to any -> ($ext_if) -## rdr example +## static rdr example ## rdr pass inet proto tcp from any to any port {80, 443} -> 10.17.89.45 +# Enable dynamic rdr (see below) +rdr-anchor "rdr/*" + block in all pass out quick modulate state antispoof for $ext_if inet pass in inet proto tcp from any to any port ssh flags S/SA keep state + +# make sure you also open up ports that you are going to use for dynamic rdr +# pass in inet proto tcp from any to any port : flags S/SA keep state +# pass in inet proto udp from any to any port : flags S/SA keep state + ``` * Make sure to change the `ext_if` variable to match your host system interface. @@ -150,6 +159,24 @@ container at `10.17.89.45`. Finally, enable and (re)start the firewall: +## dynamic rdr + +The `rdr-anchor "rdr/*"` enables dynamic rdr rules to be setup using the +`bastille rdr` command at runtime - eg. + +``` + bastille rdr tcp 2001 22 # Redirects tcp port 2001 on host to 22 on jail + bastille rdr udp 2053 53 # Same for udp + bastille rdr list # List dynamic rdr rules + bastille rdr clear # Clear dynamic rdr rules +``` + + Note that if you are rediirecting ports where the host is also listening + (eg. ssh) you should make sure that the host service is not listening on + the cloned interface - eg. for ssh set sshd_flags in rc.conf + +## Enable pf rules + ```shell ishmael ~ # sysrc pf_enable="YES" ishmael ~ # service pf restart @@ -722,6 +749,28 @@ ishmael ~ # bastille cp ALL /tmp/resolv.conf-cf etc/resolv.conf /tmp/resolv.conf-cf -> /usr/local/bastille/jails/unbound0/root/etc/resolv.conf ``` +bastille-rdr +------------ + +`bastille rdr` allows you to configure dynamic rdr rules for your containers +without modifying pf.conf (assuming you are using the `bastille0` interface +for a private network and have enabled `rdr-anchor 'rdr/*'` in /etc/pf.conf +as described in the Networking section). + +```shell + # bastille rdr --help + Usage: bastille rdr TARGET [clear] | [list] | [tcp ] | [udp ] + # bastille rdr dev1 tcp 2001 22 + # bastille rdr dev1 list + rdr on em0 inet proto tcp from any to any port = 2001 -> 10.17.89.1 port 22 + # bastille rdr dev1 udp 2053 53 + # bastille rdr dev1 list + rdr on em0 inet proto tcp from any to any port = 2001 -> 10.17.89.1 port 22 + rdr on em0 inet proto udp from any to any port = 2053 -> 10.17.89.1 port 53 + # bastille rdr dev1 clear + nat cleared +``` + bastille update --------------- The `update` command targets a release instead of a container. Because every diff --git a/docs/chapters/networking.rst b/docs/chapters/networking.rst index 90f9f222..db3a7f3a 100644 --- a/docs/chapters/networking.rst +++ b/docs/chapters/networking.rst @@ -103,14 +103,21 @@ Create the firewall rules: table persist nat on $ext_if from to any -> ($ext_if) - ## rdr example + ## static rdr example ## rdr pass inet proto tcp from any to any port {80, 443} -> 10.17.89.45 + + ## dynamic rdr anchor (see below) + rdr-anchor "rdr/*" block in all pass out quick modulate state antispoof for $ext_if inet pass in inet proto tcp from any to any port ssh flags S/SA modulate state + # If you are using dynamic rdr also need to ensure that the external port + # range you are using is open + # pass in inet proto tcp any to any port : + - Make sure to change the `ext_if` variable to match your host system interface. - Make sure to include the last line (`port ssh`) or you'll end up locked out. @@ -121,7 +128,7 @@ to containers are: nat on $ext_if from to any -> ($ext_if) - ## rdr example + ## static rdr example ## rdr pass inet proto tcp from any to any port {80, 443} -> 10.17.89.45 The `nat` routes traffic from the loopback interface to the external @@ -131,6 +138,23 @@ The `rdr pass ...` will redirect traffic from the host firewall on port X to the ip of Container Y. The example shown redirects web traffic (80 & 443) to the containers at `10.17.89.45`. + ## dynamic rdr anchor (see below) + rdr-anchor "rdr/*" + +The `rdr-anchor "rdr/*"` enables dynamic rdr rules to be setup using the +`bastille rdr` command at runtime - eg. + + bastille rdr tcp 2001 22 # Redirects tcp port 2001 on host to 22 on jail + bastille rdr udp 2053 53 # Same for udp + bastille rdr list # List dynamic rdr rules + bastille rdr clear # Clear dynamic rdr rules + + Note that if you are redirecting ports where the host is also listening + (eg. ssh) you should make sure that the host service is not listening on + the cloned interface - eg. for ssh set sshd_flags in rc.conf + + sshd_flags="-o ListenAddress=" + Finally, start up the firewall: .. code-block:: shell diff --git a/docs/chapters/subcommands/rdr.rst b/docs/chapters/subcommands/rdr.rst new file mode 100644 index 00000000..5ee2cd6a --- /dev/null +++ b/docs/chapters/subcommands/rdr.rst @@ -0,0 +1,28 @@ +=== +rdr +=== + +`bastille rdr` allows you to configure dynamic rdr rules for your containers +without modifying pf.conf (assuming you are using the `bastille0` interface +for a private network and have enabled `rdr-anchor 'rdr/*'` in /etc/pf.conf +as described in the Networking section). + +Note: you need to be careful if host services are configured to run +on all interfaces as this will include the jail interface - you should +sepcify the interface they run on in rc.conf (or other config files) + +.. code-block:: shell + + # bastille rdr --help + Usage: bastille rdr TARGET [clear] | [list] | [tcp ] | [udp ] + # bastille rdr dev1 tcp 2001 22 + # bastille rdr dev1 list + rdr on em0 inet proto tcp from any to any port = 2001 -> 10.17.89.1 port 22 + # bastille rdr dev1 udp 2053 53 + # bastille rdr dev1 list + rdr on em0 inet proto tcp from any to any port = 2001 -> 10.17.89.1 port 22 + rdr on em0 inet proto udp from any to any port = 2053 -> 10.17.89.1 port 53 + # bastille rdr dev1 clear + nat cleared + + diff --git a/usr/local/bin/bastille b/usr/local/bin/bastille index 7fb3e68b..07fbc89f 100755 --- a/usr/local/bin/bastille +++ b/usr/local/bin/bastille @@ -93,6 +93,7 @@ Available Commands: import Import a specified container. list List containers (running and stopped). pkg Manipulate binary packages within targeted container(s). See pkg(8). + rdr Redirect host port to container port. restart Restart a running container. service Manage services within targeted container(s). start Start a stopped container. @@ -130,7 +131,7 @@ esac # Filter out all non-commands case "${CMD}" in -cmd|convert|cp|create|destroy|export|import|list|pkg|restart|start|stop|sysrc|template|verify) +cmd|convert|cp|create|destroy|export|import|list|pkg|rdr|restart|start|stop|sysrc|template|verify) ;; update|upgrade) ;; diff --git a/usr/local/share/bastille/rdr.sh b/usr/local/share/bastille/rdr.sh new file mode 100644 index 00000000..bca00a6d --- /dev/null +++ b/usr/local/share/bastille/rdr.sh @@ -0,0 +1,118 @@ +#!/bin/sh +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +. /usr/local/share/bastille/colors.pre.sh +. /usr/local/etc/bastille/bastille.conf + +usage() { + echo -e "${COLOR_RED}Usage: bastille rdr TARGET [clear] | [list] | [tcp ] | [udp ]${COLOR_RESET}" + exit 1 +} + +# Handle special-case commands first. +case "$1" in +help|-h|--help) + usage + ;; +esac + +if [ $# -lt 2 ]; then + usage +fi + +TARGET="${1}" +shift + +# Can only redirect to single jail +if [ "${TARGET}" = 'ALL' ]; then + echo -e "${COLOR_RED}Can only redirect to single jail${COLOR_RESET}" + exit 1 +fi + +# Check jail name valid +JAIL_NAME=$(jls -j "${TARGET}" name 2>/dev/null) +if [ -z "${JAIL_NAME}" ]; then + echo -e "${COLOR_RED}Jail not found: ${TARGET}${COLOR_RESET}" + exit 1 +fi + +# Check jail ip4 address valid +JAIL_IP=$(jls -j "${TARGET}" ip4.addr 2>/dev/null) +if [ -z "${JAIL_IP}" -o "${JAIL_IP}" = "-" ]; then + echo -e "${COLOR_RED}Jail IP not found: ${TARGET}${COLOR_RESET}" + exit 1 +fi + +# Check rdr-anchor is setup in pf.conf +if !(pfctl -sn | grep rdr-anchor | grep 'rdr/\*' >/dev/null); then + echo -e "${COLOR_RED}rdr-anchor not found in pf.conf${COLOR_RESET}" + exit 1 +fi + +# Check ext_if is setup in pf.conf +EXT_IF=$(grep '^[[:space:]]*ext_if[[:space:]]*=' /etc/pf.conf) +if [ -z "${JAIL_NAME}" ]; then + echo -e "${COLOR_RED}ext_if not defined in pf.conf${COLOR_RESET}" + exit 1 +fi + +while [ $# -gt 0 ]; do + case "$1" in + list) + pfctl -a "rdr/${JAIL_NAME}" -Psn 2>/dev/null + shift + ;; + clear) + pfctl -a "rdr/${JAIL_NAME}" -Fn + shift + ;; + tcp) + if [ $# -lt 3 ]; then + usage + fi + ( pfctl -a "rdr/${JAIL_NAME}" -Psn; + printf '%s\nrdr on $ext_if inet proto tcp to port %d -> %s port %d\n' "$EXT_IF" "$2" "$JAIL_IP" "$3" ) \ + | pfctl -a "rdr/${JAIL_NAME}" -f- + shift 3 + ;; + udp) + if [ $# -lt 3 ]; then + usage + fi + ( pfctl -a "rdr/${JAIL_NAME}" -Psn; + printf '%s\nrdr on $ext_if inet proto udp to port %d -> %s port %d\n' "$EXT_IF" "$2" "$JAIL_IP" "$3" ) \ + | pfctl -a "rdr/${JAIL_NAME}" -f- + shift 3 + ;; + *) + usage + ;; + esac +done + + +