From 642c78ffee70510cf3c2d9899d20e671b5bc60d2 Mon Sep 17 00:00:00 2001 From: tschettervictor Date: Sat, 13 Dec 2025 15:10:02 -0700 Subject: [PATCH] networking: require subnet for VNET jails --- docs/chapters/networking.rst | 16 ++-- usr/local/share/bastille/clone.sh | 134 +++++++++++++++++----------- usr/local/share/bastille/create.sh | 53 +++++++---- usr/local/share/bastille/network.sh | 22 +++++ 4 files changed, 150 insertions(+), 75 deletions(-) diff --git a/docs/chapters/networking.rst b/docs/chapters/networking.rst index cf40ad9c..6420c716 100644 --- a/docs/chapters/networking.rst +++ b/docs/chapters/networking.rst @@ -107,8 +107,10 @@ Bastille includes a number of IP options for IPv4 networking. The IP address specified above can be any of the following options. * An IP in your local subnet should be chosen if you create your jail using - ``-V``, ``-B`` or ``-P`` (VNET jail). It is also preferable to add the - subnet mask (/24 or whaterver your subnet is) to the IP. + ``-V``, ``-B`` or ``-P`` (VNET jail). + + Note: It is mandatory to add the subnet mask (/24 or whaterver your subnet is) + to the IP for any types of VNET jail. See below... * DHCP, SYNCDHCP, or 0.0.0.0 will configure your jail to use DHCP to obtain an address from your router. This should only be used with VNET jails. @@ -130,9 +132,10 @@ The IP address specified above can be any of the following options. resolves to. This is an advanced option and should only be used if you know what you are doing. -Note that jails support specifying an IP without the subnet (/24 or whatever -yours is) but we highly recommend setting it, especially on VNET jails. Not -doing so can cause issues in some rare cases. +Standard (non-VNET) jails support specifying an IP without the subnet (/24 or whatever +yours is), but for VNET jails it is mandatory. If none is supplied, it will +default to /24. This is because FreeBSD does not support adding an IP to an interface +without a subnet. IPv6 Network ^^^^^^^^^^^^ @@ -146,7 +149,8 @@ IPv6 address when creating a jail to use IPv6. The IP address specified above can be any of the following options. -* A valid IPv6 address including the subnet. +* A valid IPv6 address including the subnet. If not subnet is given, it + will defalut to /64. * SLAAC will configure your jail to use router advertisement to obtain an address from your router. This should only be used with VNET jails. diff --git a/usr/local/share/bastille/clone.sh b/usr/local/share/bastille/clone.sh index fcedcb40..bb9516a8 100644 --- a/usr/local/share/bastille/clone.sh +++ b/usr/local/share/bastille/clone.sh @@ -49,6 +49,7 @@ EOF # Handle options. AUTO=0 LIVE=0 +VNET_JAIL=0 while [ "$#" -gt 0 ]; do case "${1}" in -h|--help|help) @@ -111,34 +112,56 @@ clone_validate_jail_name() { validate_ip() { - local _ip="${1}" - local _ip6="$(echo ${_ip} | grep -E '^(([a-fA-F0-9:]+$)|([a-fA-F0-9:]+\/[0-9]{1,3}$)|SLAAC)')" + local ip="${1}" + local ip4="$(echo ${ip} | awk -F"/" '{print $1}')" + local ip6="$(echo ${ip} | grep -E '^(([a-fA-F0-9:]+$)|([a-fA-F0-9:]+\/[0-9]{1,3}$)|SLAAC)')" + local subnet="$(echo ${ip} | awk -F"/" '{print $2}')" - if [ -n "${_ip6}" ]; then - if [ "${_ip6}" = "SLAAC" ] && [ "$(bastille config ${TARGET} get vnet)" != "enabled" ]; then - error_exit "[ERROR]: Unsupported IP option for standard jail: (${_ip6})." + if [ -n "${ip6}" ]; then + if [ "${ip6}" = "SLAAC" ] && [ "$(bastille config ${TARGET} get vnet)" != "enabled" ]; then + error_exit "[ERROR]: Unsupported IP option for standard jail: (${ip6})." fi - info "\nValid: (${_ip6})." - IP6_ADDR="${_ip6}" - elif [ "${_ip}" = "inherit" ] || [ "${_ip}" = "ip_hostname" ]; then + if [ "${VNET_JAIL}" -eq 1 ]; then + if [ -z "${subnet}" ]; then + subnet="64" + ip6="${ip6}/${subnet}" + elif echo "${subnet}" | grep -Eq '^[0-9]+$'; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + elif [ "${subnet}" -lt 1 ] || [ "${subnet}" -gt 128 ]; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + fi + fi + info "\nValid: (${ip6})." + IP6_ADDR="${ip6}" + elif [ "${ip4}" = "inherit" ] || [ "${ip4}" = "ip_hostname" ]; then if [ "$(bastille config ${TARGET} get vnet)" = "enabled" ]; then - error_exit "[ERROR]: Unsupported IP option for VNET jail: (${_ip})." + error_exit "[ERROR]: Unsupported IP option for VNET jail: (${ip4})." else - info "\nValid: (${_ip})." - IP4_ADDR="${_ip}" - IP6_ADDR="${_ip}" + info "\nValid: (${ip4})." + IP4_ADDR="${ip4}" + IP6_ADDR="${ip4}" fi - elif [ "${_ip}" = "0.0.0.0" ] || [ "${_ip}" = "DHCP" ] || [ "${_ip}" = "SYNCDHCP" ]; then + elif [ "${ip4}" = "0.0.0.0" ] || [ "${ip4}" = "DHCP" ] || [ "${ip4}" = "SYNCDHCP" ]; then if [ "$(bastille config ${TARGET} get vnet)" = "enabled" ]; then - info "\nValid: (${_ip})." - IP4_ADDR="${_ip}" + info "\nValid: (${ip4})." + IP4_ADDR="${ip4}" else - error_exit "[ERROR]: Unsupported IP option for standard jail: (${_ip})." + error_exit "[ERROR]: Unsupported IP option for standard jail: (${ip4})." fi else + if [ "${VNET_JAIL}" -eq 1 ]; then + if [ -z "${subnet}" ]; then + subnet="24" + ip4="${ip4}/${subnet}" + elif echo "${subnet}" | grep -Eq '^[0-9]+$'; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + elif [ "${subnet}" -lt 1 ] || [ "${subnet}" -gt 32 ]; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + fi + fi local IFS - if echo "${_ip}" | grep -Eq '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))?$'; then - TEST_IP=$(echo "${_ip}" | cut -d / -f1) + if echo "${ip4}" | grep -Eq '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))?$'; then + TEST_IP=$(echo "${ip4}" | cut -d / -f1) IFS=. set ${TEST_IP} for quad in 1 2 3 4; do @@ -149,14 +172,14 @@ validate_ip() { if ifconfig | grep -qwF "${TEST_IP}"; then warn "\nWarning: IP address already in use (${TEST_IP})." - IP4_ADDR="${_ip}" + IP4_ADDR="${ip4}" else - info "\nValid: (${_ip})." - IP4_ADDR="${_ip}" + info "\nValid: (${ip4})." + IP4_ADDR="${ip4}" fi else - error_continue "Invalid: (${_ip})." + error_continue "Invalid: (${ip4})." fi fi } @@ -173,58 +196,63 @@ validate_ips() { update_jailconf() { - # Update jail.conf - JAIL_CONFIG="${bastille_jailsdir}/${NEWNAME}/jail.conf" + local jail_config="${bastille_jailsdir}/${NEWNAME}/jail.conf" - if [ -f "${JAIL_CONFIG}" ]; then - if ! grep -qw "path = ${bastille_jailsdir}/${NEWNAME}/root;" "${JAIL_CONFIG}"; then - sed -i '' "s|host.hostname = ${TARGET};|host.hostname = ${NEWNAME};|" "${JAIL_CONFIG}" - sed -i '' "s|exec.consolelog = .*;|exec.consolelog = ${bastille_logsdir}/${NEWNAME}_console.log;|" "${JAIL_CONFIG}" - sed -i '' "s|path = .*;|path = ${bastille_jailsdir}/${NEWNAME}/root;|" "${JAIL_CONFIG}" - sed -i '' "s|mount.fstab = .*;|mount.fstab = ${bastille_jailsdir}/${NEWNAME}/fstab;|" "${JAIL_CONFIG}" - sed -i '' "s|^${TARGET}.*{$|${NEWNAME} {|" "${JAIL_CONFIG}" + if grep -qw "vnet;" "${jail_config}"; then + VNET_JAIL=1 + else + VNET_JAIL=0 + fi + + if [ -f "${jail_config}" ]; then + if ! grep -qw "path = ${bastille_jailsdir}/${NEWNAME}/root;" "${jail_config}"; then + sed -i '' "s|host.hostname = ${TARGET};|host.hostname = ${NEWNAME};|" "${jail_config}" + sed -i '' "s|exec.consolelog = .*;|exec.consolelog = ${bastille_logsdir}/${NEWNAME}_console.log;|" "${jail_config}" + sed -i '' "s|path = .*;|path = ${bastille_jailsdir}/${NEWNAME}/root;|" "${jail_config}" + sed -i '' "s|mount.fstab = .*;|mount.fstab = ${bastille_jailsdir}/${NEWNAME}/fstab;|" "${jail_config}" + sed -i '' "s|^${TARGET}.*{$|${NEWNAME} {|" "${jail_config}" fi fi - if grep -qw "vnet;" "${JAIL_CONFIG}"; then + if [ "${VNET_JAIL}" -eq 1 ]; then validate_netconf update_jailconf_vnet else - _ip4="$(bastille config ${TARGET} get ip4.addr | sed 's/,/ /g')" - _ip6="$(bastille config ${TARGET} get ip6.addr | sed 's/,/ /g')" - _interface="$(bastille config ${TARGET} get interface)" + ip4="$(bastille config ${TARGET} get ip4.addr | sed 's/,/ /g')" + ip6="$(bastille config ${TARGET} get ip6.addr | sed 's/,/ /g')" + interface="$(bastille config ${TARGET} get interface)" # Remove old style interface naming in place of new if|ip style - if [ "${_interface}" != "not set" ]; then - sed -i '' "/.*interface = .*/d" "${JAIL_CONFIG}" + if [ "${interface}" != "not set" ]; then + sed -i '' "/.*interface = .*/d" "${jail_config}" fi # IP4 - if [ "${_ip4}" != "not set" ]; then - for _ip in ${_ip4}; do - if echo ${_ip} | grep -q "|"; then - _ip="$(echo ${_ip} | awk -F"|" '{print $2}')" + if [ "${ip4}" != "not set" ]; then + for ip in ${ip4}; do + if echo ${ip} | grep -q "|"; then + ip="$(echo ${ip} | awk -F"|" '{print $2}')" fi - if [ "${_interface}" != "not set" ]; then - sed -i '' "s#.*ip4.addr = .*# ip4.addr = ${_interface}|${IP4_ADDR};#" "${JAIL_CONFIG}" + if [ "${interface}" != "not set" ]; then + sed -i '' "s#.*ip4.addr = .*# ip4.addr = ${interface}|${IP4_ADDR};#" "${jail_config}" else - sed -i '' "\#ip4.addr = .*# s#${_ip}#${IP4_ADDR}#" "${JAIL_CONFIG}" + sed -i '' "\#ip4.addr = .*# s#${ip}#${IP4_ADDR}#" "${jail_config}" fi - sed -i '' "\#ip4.addr += .*# s#${_ip}#127.0.0.1#" "${JAIL_CONFIG}" + sed -i '' "\#ip4.addr += .*# s#${ip}#127.0.0.1#" "${jail_config}" done fi # IP6 - if [ "${_ip6}" != "not set" ]; then - for _ip in ${_ip6}; do - if echo ${_ip} | grep -q "|"; then - _ip="$(echo ${_ip} | awk -F"|" '{print $2}')" + if [ "${ip6}" != "not set" ]; then + for ip in ${ip6}; do + if echo ${ip} | grep -q "|"; then + ip="$(echo ${ip} | awk -F"|" '{print $2}')" fi - if [ "${_interface}" != "not set" ]; then - sed -i '' "s#.*${_interface} = .*# ip6.addr = ${_interface}|${IP6_ADDR};/" "${JAIL_CONFIG}" + if [ "${interface}" != "not set" ]; then + sed -i '' "s#.*${interface} = .*# ip6.addr = ${interface}|${IP6_ADDR};/" "${jail_config}" else - sed -i '' "\#ip6.addr = .*# s#${_ip}#${IP6_ADDR}#" "${JAIL_CONFIG}" + sed -i '' "\#ip6.addr = .*# s#${ip}#${IP6_ADDR}#" "${jail_config}" fi - sed -i '' "\#ip6.addr += .*# s#${_ip}#::1#" "${JAIL_CONFIG}" + sed -i '' "\#ip6.addr += .*# s#${ip}#::1#" "${jail_config}" done fi fi diff --git a/usr/local/share/bastille/create.sh b/usr/local/share/bastille/create.sh index 94df0b45..917ee1c5 100644 --- a/usr/local/share/bastille/create.sh +++ b/usr/local/share/bastille/create.sh @@ -114,29 +114,50 @@ validate_release() { validate_ip() { local ip="${1}" + local ip4="$(echo ${ip} | awk -F"/" '{print $1}')" local ip6="$(echo ${ip} | grep -E '^(([a-fA-F0-9:]+$)|([a-fA-F0-9:]+\/[0-9]{1,3}$)|SLAAC)')" + local subnet="$(echo ${ip} | awk -F"/" '{print $2}')" if [ -n "${ip6}" ]; then + if [ "${VNET_JAIL}" -eq 1 ]; then + if [ -z "${subnet}" ]; then + subnet="64" + ip6="${ip6}/${subnet}" + elif echo "${subnet}" | grep -Eq '^[0-9]+$'; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + elif [ "${subnet}" -lt 1 ] || [ "${subnet}" -gt 128 ]; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + fi + fi info "\nValid: (${ip6})." - # This is only used in this function to set IPX_DEFINITION local ipx_addr="ip6.addr" else - if [ "${ip}" = "inherit" ] || [ "${ip}" = "ip_hostname" ]; then + if [ "${ip4}" = "inherit" ] || [ "${ip4}" = "ip_hostname" ]; then if [ "${VNET_JAIL}" -eq 1 ]; then - error_exit "[ERROR]: Unsupported IP option for VNET jail: (${ip})." + error_exit "[ERROR]: Unsupported IP option for VNET jail: (${ip4})." else - info "\nValid: (${ip})." + info "\nValid: (${ip4})." fi - elif [ "${ip}" = "DHCP" ] || [ "${ip}" = "SYNCDHCP" ] || [ "${ip}" = "0.0.0.0" ]; then + elif [ "${ip4}" = "DHCP" ] || [ "${ip4}" = "SYNCDHCP" ] || [ "${ip4}" = "0.0.0.0" ]; then if [ "${VNET_JAIL}" -eq 0 ]; then - error_exit "[ERROR]: Unsupported IP option for non-VNET jail: (${ip})." + error_exit "[ERROR]: Unsupported IP option for non-VNET jail: (${ip4})." else - info "\nValid: (${ip})." + info "\nValid: (${ip4})." fi else + if [ "${VNET_JAIL}" -eq 1 ]; then + if [ -z "${subnet}" ]; then + subnet="24" + ip4="${ip4}/${subnet}" + elif echo "${subnet}" | grep -Eq '^[0-9]+$'; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + elif [ "${subnet}" -lt 1 ] || [ "${subnet}" -gt 32 ]; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + fi + fi local IFS - if echo "${ip}" | grep -Eq '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))?$'; then - TEST_IP=$(echo "${ip}" | cut -d / -f1) + if echo "${ip4}" | grep -Eq '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))?$'; then + TEST_IP=$(echo "${ip4}" | cut -d / -f1) IFS=. set ${TEST_IP} for quad in 1 2 3 4; do @@ -145,9 +166,9 @@ validate_ip() { fi done ipx_addr="ip4.addr" - info "\nValid: (${ip})." + info "\nValid: (${ip4})." else - error_continue "Invalid: (${ip})." + error_continue "Invalid: (${ip4})." fi fi fi @@ -201,7 +222,7 @@ validate_ip() { IP6_ADDR="${ip}" fi else - error_exit "[ERROR]: Unsupported IP option for standard jail: (${ip})." + error_exit "[ERROR]: Unsupported IP option for standard jail: (${ip})." fi else if [ "${ipx_addr}" = "ip4.addr" ]; then @@ -966,10 +987,10 @@ elif [ "${VNET_JAIL_PASSTHROUGH}" -eq 1 ]; then VNET_INTERFACE_TYPE="passthrough" fi -NAME="$1" -RELEASE="$2" -IP="$3" -INTERFACE="$4" +NAME="${1}" +RELEASE="${2}" +IP="${3}" +INTERFACE="${4}" info "\nAttempting to create jail: ${NAME}" diff --git a/usr/local/share/bastille/network.sh b/usr/local/share/bastille/network.sh index 73db036e..7949d33e 100644 --- a/usr/local/share/bastille/network.sh +++ b/usr/local/share/bastille/network.sh @@ -178,15 +178,37 @@ fi validate_ip() { local ip="${1}" + local ip4="$(echo ${ip} | awk -F"/" '{print $1}')" local ip6="$( echo "${ip}" 2>/dev/null | grep -E '^(([a-fA-F0-9:]+$)|([a-fA-F0-9:]+\/[0-9]{1,3}$)|SLAAC)' )" + local subnet="$(echo ${ip} | awk -F"/" '{print $2}')" if [ -n "${ip6}" ]; then + if [ "${STANDARD}" -eq 0 ]; then + if [ -z "${subnet}" ]; then + subnet="64" + ip6="${ip6}/${subnet}" + elif echo "${subnet}" | grep -Eq '^[0-9]+$'; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + elif [ "${subnet}" -lt 1 ] || [ "${subnet}" -gt 128 ]; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + fi + fi info "\nValid: (${ip6})." IP6_ADDR="${ip6}" elif [ "${ip}" = "0.0.0.0" ] || [ "${ip}" = "DHCP" ] || [ "${ip}" = "SYNCDHCP" ]; then info "\nValid: (${ip})." IP4_ADDR="${ip}" else + if [ "${STANDARD}" -eq 0 ]; then + if [ -z "${subnet}" ]; then + subnet="24" + ip4="${ip4}/${subnet}" + elif echo "${subnet}" | grep -Eq '^[0-9]+$'; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + elif [ "${subnet}" -lt 1 ] || [ "${subnet}" -gt 32 ]; then + error_exit "[ERROR]: Invalid subnet: /${subnet}" + fi + fi local IFS if echo "${ip}" 2>/dev/null | grep -Eq '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))?$'; then TEST_IP=$(echo "${ip}" | cut -d / -f1)