From def90aaed15da94cf0ca2eca03feee0ff14a1fb8 Mon Sep 17 00:00:00 2001 From: David Blane <32327139+dblane-digicatapult@users.noreply.github.com> Date: Tue, 20 Feb 2024 15:14:29 +0000 Subject: [PATCH] Merge Upstream (#3) * Make init/de-init less intrusive (#284) This adds the flag "-m" to be minimally intrusive. Also allows de-init to use "-p pf_file" to specify the firewall rulesets (this way it's symmetrical to init). Rename "-f pf_file" to "-p pf_file" in init (as "-f" was already taken in de-init), but keep "-f" as an alias for the time being. Add flag "-s" to init (do not alter syslogd configuration). Some minor cleanup of variable names while there. Fixes #85 * Find bridge interfaces by interface group (#282) This allows users to give their bridges custom names. We might make use of this as well in the future (think $POT_BRIDGE_NAME). * New global config to isolate vnet pots (#283) This new global setting called `POT_ISOLATE_VNET_POTS` sets bridge member epaira interfaces to be private, preventing them from forwarding traffic to each other. This helps with overall security, but (primarily) makes sure that pots in larger nomad clusters don't talk to each other using direct communication instead of published (natted) endpoints. This could be a more fine-grained per pot setting in the future, in our setups we only ever needed a global setting decided by the infrastructure operator (so, e.g., in the nomad cluster, everything uses this setting, whereas in the more static part forming the infrastructure the nomad cluster relies on, direct communication between pots is wanted) and changing it per pot would be a disadvantage - hence this implementation. * Fix typo in set-status.sh * Prepare 0.16.0 (#285) --------- Co-authored-by: grembo --- CHANGELOG.md | 6 ++- bin/pot | 2 +- etc/pot/pot.conf.sample | 3 ++ etc/pot/pot.default.conf | 4 ++ share/doc/pot/conf.py | 4 +- share/pot/common.sh | 18 ++++++++ share/pot/de-init.sh | 39 +++++++++++------ share/pot/init.sh | 86 ++++++++++++++++++++++---------------- share/pot/network.sh | 6 +-- share/pot/set-attribute.sh | 18 -------- share/pot/set-status.sh | 2 +- share/pot/start.sh | 31 ++++++++++---- tests/network1.sh | 7 ++++ 13 files changed, 145 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24778362..178b3948 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,16 +4,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.16.0] 2023-12-30 ### Added - tinirc: Write tinirc's pid to /tmp/tinirc.pid (#277) - set-attr/stop: Add attributes exec_stop and stop_timeout (#275) +- init/de-init: Add flag "-m" to be minimally intrusive, add flag -p to specify pf file (#284) +- init: Add flag -s to not alter syslogd settings, deprecate flag -f pf_file, as it is replaced by -p (#284) +- vnet: Add global configuration POT_ISOLATE_VNET_POTS to prevent direct traffic between VNET pots (#283) ### Fixed - tinirc: Overwrite tinirc on start instead of appending to an existing file (#277) - start: Fix setting of nullfs attribute - set-status: Ignore status files that predate system boot (#278) - set-status: Forward verbosity flags (#279) +- network: Find bridge interfaces by interface group, this allows custom bridge names (#282) ## [0.15.6] 2023-09-29 ### Added diff --git a/bin/pot b/bin/pot index 61d9b650..b3afcde6 100755 --- a/bin/pot +++ b/bin/pot @@ -34,7 +34,7 @@ export IFCONFIG_FORMAT=addr:default # Environment initialization and initial checks # shellcheck disable=SC2034 -_POT_VERSION=0.15.6 +_POT_VERSION=0.16.0 _POT_PATHNAME="$(realpath "$0")" _POT_PREFIX="$(dirname "${_POT_PATHNAME}")" _POT_INCLUDE="$( realpath "${_POT_PREFIX}/../share/pot")" diff --git a/etc/pot/pot.conf.sample b/etc/pot/pot.conf.sample index 4dec3b6a..510b10c0 100644 --- a/etc/pot/pot.conf.sample +++ b/etc/pot/pot.conf.sample @@ -43,6 +43,9 @@ # POT_NETWORK_vlan20=192.168.100.0/24 # POT_NETWORK_vlan50=10.50.50.0/24 +# Do not allow bridge-based pots to forward traffic to each other +# POT_ISOLATE_VNET_POTS=true + # DNS on the Internal Virtual Network # name of the pot running the DNS diff --git a/etc/pot/pot.default.conf b/etc/pot/pot.default.conf index 8e53528b..769ab20b 100644 --- a/etc/pot/pot.default.conf +++ b/etc/pot/pot.default.conf @@ -46,6 +46,10 @@ POT_DNS_NAME=dns # IP of the DNS POT_DNS_IP=10.192.0.2 +# If set to true, isolate pot vnet bridge members +# (by using `ifconfig private `, see ifconfig(8)) +POT_ISOLATE_VNET_POTS=false + # If not empty, this script will be called by pot and the pf rules # returned on stdout will be loaded into "pot-rdr/anchor" instead # of those which pot would usually create. This also skips diff --git a/share/doc/pot/conf.py b/share/doc/pot/conf.py index e2e86b68..2cb98a2a 100644 --- a/share/doc/pot/conf.py +++ b/share/doc/pot/conf.py @@ -56,9 +56,9 @@ # built documents. # # The short X.Y version. -version = "0.15.6" +version = "0.16.0" # The full version, including alpha/beta/rc tags. -release = "0.15.6" +release = "0.16.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/share/pot/common.sh b/share/pot/common.sh index f9f65d38..f7e482df 100644 --- a/share/pot/common.sh +++ b/share/pot/common.sh @@ -182,6 +182,24 @@ _get_system_uptime() { sysctl -n kern.boottime | sed -e 's/.*[^u]sec = \([0-9]*\).*$/\1/' } +# check if the argument is a valid boolean value +# if valid, it returns true and it echo a normalized version of the boolean value (YES/NO) +# if not valid, it return false +_normalize_true_false() { + case $1 in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]) + echo YES + return 0 # true + ;; + [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]) + echo NO + return 0 # true + ;; + *) + return 1 # false + esac +} + # validate some values of the configuration files # $1 quiet / no _error messages are emitted _conf_check() diff --git a/share/pot/de-init.sh b/share/pot/de-init.sh index bb3d57ef..03e19cc6 100644 --- a/share/pot/de-init.sh +++ b/share/pot/de-init.sh @@ -5,32 +5,43 @@ de-init-help() { cat <<-"EOH" - pot de-init [-hvf] + pot de-init [-hmvf] [-p pf_file] + -f force : stop all running pots + -p pf_file : remove anchors to this file (empty to skip), + defaults to result of `sysrc -n pf_rules` -h print this help + -m minimal modifications (alias for `-p ''`) + WARNING: Still destroys POT_ZFS_ROOT -v verbose - -f force : stop all running pots EOH } pot-de-init() { - local _pots _p _force _zopt + local _pots _p _force _zopt _pf_file _force= _zopt= + _pf_file="$(sysrc -n pf_rules)" OPTIND=1 - while getopts "hvf" _o ; do + while getopts "fhmvp:" _o ; do case "$_o" in + f) + _force="force" + ;; h) de-init-help ${EXIT} 0 ;; + m) + _pf_file="" + ;; + p) + _pf_file="$OPTARG" + ;; v) _POT_VERBOSITY=$(( _POT_VERBOSITY + 1)) _zopt="-v" ;; - f) - _force="force" - ;; ?) de-init-help ${EXIT} 1 @@ -59,13 +70,15 @@ pot-de-init() _info "Deinstall pot ($POT_ZFS_ROOT)" zfs destroy -r $_zopt "${POT_ZFS_ROOT}" fi - # Remove pf entries - pf_file="$(sysrc -n pf_rules)" - sed -i '' '/^nat-anchor pot-nat$/d' "$pf_file" - sed -i '' '/^rdr-anchor "pot-rdr\/\*"$/d' "$pf_file" - # Final message echo "zfs datasets have been removed" - echo "pf configuration file should be clean (please check $pf_file)" + # Remove pf entries if needed + if [ -n "$_pf_file" ]; then + sed -i '' '/^nat-anchor pot-nat$/d' "$_pf_file" + sed -i '' '/^rdr-anchor "pot-rdr\/\*"$/d' "$_pf_file" + echo "pf configuration file should be clean" + echo " - please check $_pf_file and reload it" + fi + # Final message echo "check your rc.conf for potential leftovers variable like:" echo ' syslogd_flags' echo ' pot_enable' diff --git a/share/pot/init.sh b/share/pot/init.sh index 14fd32de..ea9adbc1 100644 --- a/share/pot/init.sh +++ b/share/pot/init.sh @@ -8,27 +8,39 @@ init-help() { cat <<-"EOH" - pot init [-hv] [-f pf_file] - -f pf_file : write pot anchors to this file (empty to skip), - defaults to result of `sysrc -n pf_rules` + pot init [-hmsv] [-p pf_file] -h print this help + -m minimal modifications (alias for `-sp ''`) + -p pf_file : write pot anchors to this file (empty to skip), + defaults to result of `sysrc -n pf_rules` + -f pf_file : alias for -p pf_file (deprecated) + -s do not alter syslogd config -v verbose EOH } pot-init() { - local pf_file dataset - pf_file="$(sysrc -n pf_rules)" + local _pf_file _dataset _skip_alter_syslog + _pf_file="$(sysrc -n pf_rules)" + _skip_alter_syslog= OPTIND=1 - while getopts "hvf:" _o ; do + while getopts "hmsvf:p:" _o ; do case "$_o" in - f) pf_file="$OPTARG" + f|p) + _pf_file="$OPTARG" ;; h) init-help ${EXIT} 0 ;; + m) + _pf_file="" + _skip_alter_syslog="YES" + ;; + s) + _skip_alter_syslog="YES" + ;; v) _POT_VERBOSITY=$(( _POT_VERBOSITY + 1)) ;; @@ -73,14 +85,14 @@ pot-init() chown root:"${POT_GROUP:-pot}" "${POT_FS_ROOT}" || ${EXIT} 1 # create mandatory datasets - for dataset in bases jails fscomp; do - if ! _zfs_dataset_valid "${POT_ZFS_ROOT}/$dataset" ; then - _debug "creating ${POT_ZFS_ROOT}/$dataset" - zfs create "${POT_ZFS_ROOT}/$dataset" || ${EXIT} 1 + for _dataset in bases jails fscomp; do + if ! _zfs_dataset_valid "${POT_ZFS_ROOT}/$_dataset" ; then + _debug "creating ${POT_ZFS_ROOT}/$_dataset" + zfs create "${POT_ZFS_ROOT}/$_dataset" || ${EXIT} 1 fi - if ! _zfs_mounted "${POT_ZFS_ROOT}/$dataset"; then - _debug "mounting ${POT_ZFS_ROOT}/$dataset" - zfs mount "${POT_ZFS_ROOT}/$dataset" || ${EXIT} 1 + if ! _zfs_mounted "${POT_ZFS_ROOT}/$_dataset"; then + _debug "mounting ${POT_ZFS_ROOT}/$_dataset" + zfs mount "${POT_ZFS_ROOT}/$_dataset" || ${EXIT} 1 fi done if ! _zfs_exist "${POT_ZFS_ROOT}/cache" "${POT_CACHE}" ; then @@ -91,10 +103,12 @@ pot-init() fi # create the bridges folder mkdir -p "${POT_FS_ROOT}/bridges" - # create mandatory directories for logs - mkdir -p /usr/local/etc/syslog.d - mkdir -p /usr/local/etc/newsyslog.conf.d - mkdir -p /var/log/pot + if [ "$_skip_alter_syslog" != "YES" ]; then + # create mandatory directories for logs + mkdir -p /usr/local/etc/syslog.d + mkdir -p /usr/local/etc/newsyslog.conf.d + mkdir -p /var/log/pot + fi if ! _is_pot_tmp_dir ; then _error "The POT_TMP directory has not been created - aborting" @@ -127,31 +141,33 @@ pot-init() fi done - if [ -w /etc/rc.conf ]; then - echo "Creating a backup of your /etc/rc.conf" - cp -v /etc/rc.conf /etc/rc.conf.bkp-pot + if [ "$_skip_alter_syslog" != "YES" ]; then + if [ -w /etc/rc.conf ]; then + echo "Creating a backup of your /etc/rc.conf" + cp -v /etc/rc.conf /etc/rc.conf.bkp-pot + fi + # add proper syslogd flags and restart it + sysrc -q syslogd_flags="-b 127.0.0.1 -b $POT_GATEWAY -a $POT_NETWORK" + # service syslogd restart fi - # add proper syslogd flags and restart it - sysrc -q syslogd_flags="-b 127.0.0.1 -b $POT_GATEWAY -a $POT_NETWORK" - # service syslogd restart # Add pot anchors if needed - if [ -n "$pf_file" ]; then - if [ -r "$pf_file" ] && [ "$(grep -c '^nat-anchor pot-nat$' "$pf_file" )" -eq 1 ] && [ "$(grep -c '^rdr-anchor "pot-rdr/\*"$' "$pf_file" )" -eq 1 ] ; then + if [ -n "$_pf_file" ]; then + if [ -r "$_pf_file" ] && [ "$(grep -c '^nat-anchor pot-nat$' "$_pf_file" )" -eq 1 ] && [ "$(grep -c '^rdr-anchor "pot-rdr/\*"$' "$_pf_file" )" -eq 1 ] ; then _debug "pf already properly configured" else - if [ -w "$pf_file" ]; then - echo "Creating a backup of your $pf_file" - cp -v "$pf_file" "$pf_file".bkp-pot + if [ -w "$_pf_file" ]; then + echo "Creating a backup of your $_pf_file" + cp -v "$_pf_file" "$_pf_file".bkp-pot # delete incomplete/broken ancory entries - just in case - sed -i '' '/^nat-anchor pot-nat$/d' "$pf_file" - sed -i '' '/^rdr-anchor "pot-rdr\/\*"$/d' "$pf_file" + sed -i '' '/^nat-anchor pot-nat$/d' "$_pf_file" + sed -i '' '/^rdr-anchor "pot-rdr\/\*"$/d' "$_pf_file" else - touch "$pf_file" + touch "$_pf_file" fi - echo "auto-magically editing your $pf_file" - printf "%s\n" 0a "nat-anchor pot-nat" "rdr-anchor \"pot-rdr/*\"" . x | ex "$pf_file" - echo "Please, check that your PF configuration file $pf_file is still valid!" + echo "auto-magically editing your $_pf_file" + printf "%s\n" 0a "nat-anchor pot-nat" "rdr-anchor \"pot-rdr/*\"" . x | ex "$_pf_file" + echo "Please, check that your PF configuration file $_pf_file is still valid and reload it!" fi else _debug "pf configuration skipped" diff --git a/share/pot/network.sh b/share/pot/network.sh index c3b63ff6..c28a01e3 100644 --- a/share/pot/network.sh +++ b/share/pot/network.sh @@ -10,7 +10,7 @@ _pot_bridge() _pot_bridge_ipv4() { local _bridges - _bridges=$( ifconfig | grep ^bridge | cut -f1 -d':' ) + _bridges=$( ifconfig -g bridge ) if [ -z "$_bridges" ]; then return fi @@ -26,7 +26,7 @@ _pot_bridge_ipv4() _pot_bridge_ipv6() { local _bridges - _bridges=$( ifconfig | grep ^bridge | cut -f1 -d':' ) + _bridges=$( ifconfig -g bridge ) if [ -z "$_bridges" ]; then return fi @@ -43,7 +43,7 @@ _private_bridge() { local _bridges _bridge _bridge_ip _bridge="$1" - _bridges=$( ifconfig | grep ^bridge | cut -f1 -d':' ) + _bridges=$( ifconfig -g bridge ) if [ -z "$_bridges" ]; then return fi diff --git a/share/pot/set-attribute.sh b/share/pot/set-attribute.sh index 65973fc0..6d276f42 100644 --- a/share/pot/set-attribute.sh +++ b/share/pot/set-attribute.sh @@ -16,24 +16,6 @@ set-attribute-help() EOH } -# check if the argument is a valid boolean value -# if valid, it returns true and it echo a normalized version of the boolean value (YES/NO) -# if not valid, it return false -_normalize_true_false() { - case $1 in - [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]) - echo YES - return 0 # true - ;; - [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]) - echo NO - return 0 # true - ;; - *) - return 1 # false - esac -} - # $1 pot name # $2 attribute name # $3 value diff --git a/share/pot/set-status.sh b/share/pot/set-status.sh index b8dd6865..50422116 100644 --- a/share/pot/set-status.sh +++ b/share/pot/set-status.sh @@ -33,7 +33,7 @@ _get_status() _uptime=$(_get_system_uptime) if [ "$_uptime" -gt "$_mod_time" ]; then - >&2 _debug "Ignoring oudated status file $_status_file of pot $_pname" + >&2 _debug "Ignoring outdated status file $_status_file of pot $_pname" return fi diff --git a/share/pot/start.sh b/share/pot/start.sh index 056b475c..159fb874 100644 --- a/share/pot/start.sh +++ b/share/pot/start.sh @@ -172,7 +172,7 @@ _js_create_epair() # $3 epairb interface _js_vnet() { - local _pname _bridge _epaira _epairb _ip + local _pname _bridge _epaira _epairb _ip _param _pname=$1 if ! _is_vnet_ipv4_up ; then _info "Internal network not found! Calling vnet-start to fix the issue" @@ -182,9 +182,14 @@ _js_vnet() _epaira=$2 _epairb=$3 ifconfig "$_epaira" up - ifconfig "$_bridge" addm "$_epaira" + _param=$(_save_params "addm" "$_epaira") + if [ "$(_normalize_true_false "$POT_ISOLATE_VNET_POTS")" = "YES" ]; then + _param="$_param"$(_save_params "private" "$_epaira") + fi + eval "set -- $_param" + ifconfig "$_bridge" "$@" _ip=$( _get_ip_var "$_pname" ) - ## if norcscript - write a ad-hoc one + ## if norcscript - write an ad-hoc one if [ "$(_get_conf_var "$_pname" "pot.attr.no-rc-script")" = "YES" ]; then cat >>"${POT_FS_ROOT}/jails/$_pname/m/tmp/tinirc" <<-EOT if ! ifconfig ${_epairb} >/dev/null 2>&1; then @@ -213,7 +218,7 @@ _js_vnet() # $4 stack (ipv6 or dual) _js_vnet_ipv6() { - local _pname _bridge _epaira _epairb _ip + local _pname _bridge _epaira _epairb _ip _param _pname=$1 if ! _is_vnet_ipv6_up ; then _info "Internal network not found! Calling vnet-start to fix the issue" @@ -223,7 +228,12 @@ _js_vnet_ipv6() _epaira=$2 _epairb=$3 ifconfig "$_epaira" up - ifconfig "$_bridge" addm "$_epaira" + _param=$(_save_params "addm" "$_epaira") + if [ "$(_normalize_true_false "$POT_ISOLATE_VNET_POTS")" = "YES" ]; then + _param="$_param"$(_save_params "private" "$_epaira") + fi + eval "set -- $_param" + ifconfig "$_bridge" "$@" if [ "$(_get_conf_var "$_pname" "pot.attr.no-rc-script")" = "YES" ]; then cat >>"${POT_FS_ROOT}/jails/$_pname/m/tmp/tinirc" <<-EOT if ! ifconfig ${_epairb} >/dev/null 2>&1; then @@ -253,7 +263,8 @@ _js_vnet_ipv6() # $3 epairb interface _js_private_vnet() { - local _pname _bridge_name _bridge _epaira _epairb _ip _net_size _gateway + local _pname _bridge_name _bridge _epaira _epairb _ip _net_size + local _gateway _param _pname=$1 _bridge_name="$( _get_conf_var "$_pname" bridge )" if ! _is_vnet_ipv4_up "$_bridge_name" ; then @@ -264,12 +275,18 @@ _js_private_vnet() _epaira=$2 _epairb=$3 ifconfig "$_epaira" up + _param=$(_save_params "addm" "$_epaira") + if [ "$(_normalize_true_false "$POT_ISOLATE_VNET_POTS")" = "YES" ]; then + _param="$_param"$(_save_params "private" "$_epaira") + fi + eval "set -- $_param" + ifconfig "$_bridge" "$@" ifconfig "$_bridge" addm "$_epaira" _ip=$( _get_ip_var "$_pname" ) _net_size="$(_get_bridge_var "$_bridge_name" net)" _net_size="${_net_size##*/}" _gateway="$(_get_bridge_var "$_bridge_name" gateway)" - ## if norcscript - write a ad-hoc one + ## if norcscript - write an ad-hoc one if [ "$(_get_conf_var "$_pname" "pot.attr.no-rc-script")" = "YES" ]; then cat >>"${POT_FS_ROOT}/jails/$_pname/m/tmp/tinirc" <<-EOT if ! ifconfig ${_epairb} >/dev/null 2>&1; then diff --git a/tests/network1.sh b/tests/network1.sh index 1f6c7220..d7722ff5 100755 --- a/tests/network1.sh +++ b/tests/network1.sh @@ -59,6 +59,13 @@ bridge2: flags=8843 metric 0 mtu 1500 id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp maxaddr 2000 timeout 1200 root id 00:00:00:00:00:00 priority 32768 ifcost 0 port 0 +EOF-- + return 0 # true + elif [ "$1" = "-g" ] && [ "$2" = "bridge" ]; then + cat << EOF-- +bridge0 +bridge1 +bridge2 EOF-- return 0 # true elif [ "$1" = "bridge0" ]; then