Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using separate interfaces with rdr #664

Open
lgrn opened this issue Jan 9, 2024 · 6 comments
Open

Using separate interfaces with rdr #664

lgrn opened this issue Jan 9, 2024 · 6 comments

Comments

@lgrn
Copy link

lgrn commented Jan 9, 2024

I'm trying out Bastille, and I have a setup where I'm using two different interfaces: one regular ethernet (em0) for regular outgoing traffic, and one "vpn-interface" (let's call it vpn0) for things like monitoring to my BSD host.

As I understand it, setting ext_if in pf.conf is not only for convenience in rules using that variable, but is also assumed by Bastille to be the interface that traffic will be hitting when doing redirects (rdr). The problem for me is that I don't want to create a redirect for my ext_if, but for vpn0. So instead of:

rdr pass on em0 inet proto tcp from any to any port = 2221 -> 10.1.1.1 port 2221

I would actually want it to say something like this (vpn0):

rdr pass on vpn0 inet proto tcp from any to any port = 2221 -> 10.1.1.1 port 2221

I can't find any way to set an interface when using bastille rdr, so I'm assuming this functionality doesn't exist and always assumes ext_if -- but is it possible in any sane way? Should I not use bastille rdr at all, and instead and just set something up in pf.conf, and if so any suggestions on what?

I don't think simply changing ext_if is a good idea, because this vpn0 interface does not have regular Internet access.

@adriel-tech
Copy link
Contributor

adriel-tech commented Jan 15, 2024

Bastille rdr does not work on secondary interfaces. What I've been doing for a few years is what you are suggesting here.
An example I manually add rdr rules to pf.conf passing on my mesh vpn interface.

rdr pass on $zt_if inet proto tcp from any to $zt_if port 445 -> 10.10.10.11 port 445

Assuming this is a cloud VM or something, I would suggest leaving the default ext_if alone and manually setting all the RDRs in pf.conf for your vpn interface, which is what I do. If this server is on a lan, you can use bastille rdr as normal for lan access but manually do RDRs for your VPN. Having an interface option to rdr would be a nice feature though.

@tschettervictor
Copy link
Collaborator

tschettervictor commented Dec 9, 2024

Can you try out this block of code? Just back up your old rdr.sh file and replace it with this one.

This should allow you to specify an interface as the first arguement.

bastille rdr em0 tcp 8080 80
@adriel-tech
@lgrn

#!/bin/sh
#
# Copyright (c) 2018-2024, Christer Edwards <[email protected]>
# All rights reserved.
#
# 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/common.sh
. /usr/local/etc/bastille/bastille.conf

usage() {
    error_exit "Usage: bastille rdr TARGET [clear|list|(tcp|udp host_port jail_port [log ['(' logopts ')'] ] )]"
}

# Handle special-case commands first.
case "$1" in
help|-h|--help)
    usage
    ;;
esac

if [ $# -lt 2 ]; then
    usage
fi

bastille_root_check

TARGET="${1}"
JAIL_NAME=""
JAIL_IP=""
JAIL_IP6=""
EXT_IF=""
shift

check_jail_validity() {
    # Can only redirect to single jail
    if [ "${TARGET}" = 'ALL' ]; then
        error_exit "Can only redirect to a single jail."
    fi

    # Check if jail name is valid
    JAIL_NAME=$(/usr/sbin/jls -j "${TARGET}" name 2>/dev/null)
    if [ -z "${JAIL_NAME}" ]; then
        error_exit "Jail not found: ${TARGET}"
    fi

    # Check if jail ip4 address (ip4.addr) is valid (non-VNET only)
    if [ "$(bastille config $TARGET get vnet)" != 'enabled' ]; then
        JAIL_IP=$(/usr/sbin/jls -j "${TARGET}" ip4.addr 2>/dev/null)
        if [ -z "${JAIL_IP}" -o "${JAIL_IP}" = "-" ]; then
            error_exit "Jail IP not found: ${TARGET}"
        fi
    fi
    # Check if jail ip6 address (ip6.addr) is valid (non-VNET only)
    if [ "$(bastille config $TARGET get vnet)" != 'enabled' ]; then
        if [ "$(bastille config $TARGET get ip6)" != 'disable' ] && [ "$(bastille config $TARGET get ip6)" != 'not set' ]; then
            JAIL_IP6=$(/usr/sbin/jls -j "${TARGET}" ip6.addr 2>/dev/null)
        fi
    fi


    # Check if rdr-anchor is defined in pf.conf
    if ! (pfctl -sn | grep rdr-anchor | grep 'rdr/\*' >/dev/null); then
        error_exit "rdr-anchor not found in pf.conf"
    fi

    # Check if ext_if is defined in pf.conf
    if [ -n "${bastille_pf_conf}" ]; then
        if [ -z "${EXT_IF}" ]; then
            EXT_IF=$(grep "^[[:space:]]*${bastille_network_pf_ext_if}[[:space:]]*=" ${bastille_pf_conf})
            if [ -z "${EXT_IF}" ]; then
              error_exit "bastille_network_pf_ext_if (${bastille_network_pf_ext_if}) not defined in pf.conf"
            fi
        fi
    fi
}

# function: write rule to rdr.conf
persist_rdr_rule() {
if ! grep -qs "$IF_NAME $1 $2 $3" "${bastille_jailsdir}/${JAIL_NAME}/rdr.conf"; then
    echo "$IF_NAME $1 $2 $3" >> "${bastille_jailsdir}/${JAIL_NAME}/rdr.conf"
fi
}

persist_rdr_log_rule() {
proto=$1;host_port=$2;jail_port=$3;
shift 3;
log=$@;
if ! grep -qs "$IF_NAME $proto $host_port $jail_port $log" "${bastille_jailsdir}/${JAIL_NAME}/rdr.conf"; then
    echo "$IF_NAME $proto $host_port $jail_port $log" >> "${bastille_jailsdir}/${JAIL_NAME}/rdr.conf"
fi
}


# function: load rdr rule via pfctl
load_rdr_rule() {
( pfctl -a "rdr/${JAIL_NAME}" -Psn;
  printf '%s\nrdr pass on $%s inet proto %s to port %s -> %s port %s\n' "$EXT_IF" "${bastille_network_pf_ext_if}" "$1" "$2" "$JAIL_IP" "$3" ) \
      | pfctl -a "rdr/${JAIL_NAME}" -f-
if [ -n "$JAIL_IP6" ]; then
  ( pfctl -a "rdr/${JAIL_NAME}" -Psn;
  printf '%s\nrdr pass on $%s inet proto %s to port %s -> %s port %s\n' "$EXT_IF" "${bastille_network_pf_ext_if}" "$1" "$2" "$JAIL_IP6" "$3" ) \
    | pfctl -a "rdr/${JAIL_NAME}" -f-
fi
}

# function: load rdr rule with log via pfctl
load_rdr_log_rule() {
proto=$1;host_port=$2;jail_port=$3;
shift 3;
log=$@
( pfctl -a "rdr/${JAIL_NAME}" -Psn;
  printf '%s\nrdr pass %s on $%s inet proto %s to port %s -> %s port %s\n' "$EXT_IF" "$log" "${bastille_network_pf_ext_if}" "$proto" "$host_port" "$JAIL_IP" "$jail_port" ) \
      | pfctl -a "rdr/${JAIL_NAME}" -f-
if [ -n "$JAIL_IP6" ]; then
  ( pfctl -a "rdr/${JAIL_NAME}" -Psn;
  printf '%s\nrdr pass %s on $%s inet proto %s to port %s -> %s port %s\n' "$EXT_IF" "$log" "${bastille_network_pf_ext_if}" "$proto" "$host_port" "$JAIL_IP6" "$jail_port" ) \
    | pfctl -a "rdr/${JAIL_NAME}" -f-
fi

}

while [ $# -gt 0 ]; do
    if echo "${1}" | ifconfig | grep -o "${1}"; then
      IF_NAME="${1}"
      EXT_IF=ext_if="${1}"
      shift
    fi
    case "$1" in
        list)
            if [ "${TARGET}" = 'ALL' ]; then
                for JAIL_NAME in $(ls "${bastille_jailsdir}" | sed "s/\n//g"); do
                    echo "${JAIL_NAME} redirects:"
                    pfctl -a "rdr/${JAIL_NAME}" -Psn 2>/dev/null
                done
            else
                check_jail_validity
                pfctl -a "rdr/${JAIL_NAME}" -Psn 2>/dev/null
            fi
            shift
            ;;
        clear)
            if [ "${TARGET}" = 'ALL' ]; then
                for JAIL_NAME in $(ls "${bastille_jailsdir}" | sed "s/\n//g"); do
                    echo "${JAIL_NAME} redirects:"
                    pfctl -a "rdr/${JAIL_NAME}" -Fn
                done
            else
                check_jail_validity
                pfctl -a "rdr/${JAIL_NAME}" -Fn
            fi
            shift
            ;;
        tcp|udp)
            if [ $# -lt 3 ]; then
                usage
            elif [ $# -eq 3 ]; then
                check_jail_validity
                persist_rdr_rule $1 $2 $3
                load_rdr_rule $1 $2 $3
                shift 3
            else
                case "$4" in
                    log)
                        proto=$1
                        host_port=$2
                        jail_port=$3
                        shift 3
                        if [ $# -gt 3 ]; then
                            for last in "$@"; do
                                true
                            done
                            if [ $2 == "(" ] && [ $last == ")" ] ; then
                                check_jail_validity
                                persist_rdr_log_rule $proto $host_port $jail_port "$@"
                                load_rdr_log_rule $proto $host_port $jail_port "$@"
                                shift $#
                            else
                                usage
                            fi
                        elif [ $# -eq 1 ]; then
                            check_jail_validity
                            persist_rdr_log_rule $proto $host_port $jail_port "$@"
                            load_rdr_log_rule $proto $host_port $jail_port "$@"
                            shift 1
                        else
                            usage
                        fi
                        ;;
                    *)
                        usage
                        ;;
                esac
            fi
            ;;
        *)
            usage
            ;;
    esac
done

@tschettervictor
Copy link
Collaborator

tschettervictor commented Dec 9, 2024

Seems to enter all the correct stuff. But test and see if it does the redirects properly.
I'm still getting an error about DIOCGETRULES, but as far as I can tell, that's an age old problem with pf.

https://lists.freebsd.org/pipermail/freebsd-questions/2020-March/288131.html

@tschettervictor
Copy link
Collaborator

tschettervictor commented Dec 9, 2024

My "rdr.conf" file now looks like this.

vtnet0 tcp 8080 800
lo0 tcp 8080 80

And no errors when starting and stopping a jail.

@tschettervictor
Copy link
Collaborator

tschettervictor commented Dec 9, 2024

root@webmin:~ # pfctl -a rdr/jail1 -Psn
rdr pass on vtnet0 inet proto tcp from any to any port = 8080 -> 10.0.0.111 port 800
rdr pass on lo0 inet proto tcp from any to any port = 8080 -> 10.0.0.111 port 80

And my rules...

@tschettervictor
Copy link
Collaborator

#765

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants