From a13b9f682945ef47d442cecdbfaf40f2f49bdf56 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:07:53 -0700 Subject: [PATCH 1/5] zfs: properly jail dataset --- usr/local/share/bastille/zfs.sh | 82 ++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/usr/local/share/bastille/zfs.sh b/usr/local/share/bastille/zfs.sh index 97a7dea1..5b7a63c1 100644 --- a/usr/local/share/bastille/zfs.sh +++ b/usr/local/share/bastille/zfs.sh @@ -53,6 +53,8 @@ EOF zfs_jail_dataset() { + local jail_config="${bastille_jailsdir}/${JAIL}/jail.conf" + # Exit if MOUNT or DATASET is empty if [ -z "${MOUNT}" ] || [ -z "${DATASET}" ]; then usage @@ -65,30 +67,48 @@ zfs_jail_dataset() { if grep -hoqsw "${DATASET}" ${bastille_jailsdir}/*/zfs.conf; then error_exit "[ERROR]: Dataset already assigned." fi + # Validate jail state - check_target_is_stopped "${_jail}" || if [ "${AUTO}" -eq 1 ]; then - bastille stop "${_jail}" + check_target_is_stopped "${JAIL}" || if [ "${AUTO}" -eq 1 ]; then + bastille stop "${JAIL}" else error_notify "Jail is running." error_exit "Use [-a|--auto] to auto-stop the jail." fi # Add necessary config variables to jail - bastille config ${_jail} set enforce_statfs 1 >/dev/null - bastille config ${_jail} set allow.mount >/dev/null - bastille config ${_jail} set allow.mount.devfs >/dev/null - bastille config ${_jail} set allow.mount.zfs >/dev/null + bastille config ${JAIL} set enforce_statfs 1 >/dev/null + bastille config ${JAIL} set allow.mount >/dev/null + bastille config ${JAIL} set allow.mount.devfs >/dev/null + bastille config ${JAIL} set allow.mount.zfs >/dev/null + + # Enable ZFS inside jail + sysrc -f "${bastille_jailsdir}/${JAIL}/root/etc/rc.conf" zfs_enable="YES" + + # Jail the dataset + zfs set mountpoint="${MOUNT}" "${DATASET}" + zfs set jailed=on "${DATASET}" # Add dataset to zfs.conf - echo "${DATASET} ${MOUNT}" >> "${bastille_jailsdir}/${_jail}/zfs.conf" + echo "${DATASET} ${MOUNT}" >> "${bastille_jailsdir}/${JAIL}/zfs.conf" + + # Add config to jail.conf + sed -i '' '/^}$/d' "${jail_config}" + cat << EOF >> "${jail_config}" + # Jailed dataset: ${DATASET} + exec.created += "zfs jail ${JAIL} ${DATASET}"; +} +EOF if [ "${AUTO}" -eq 1 ]; then - bastille start "${_jail}" + bastille start "${JAIL}" fi } zfs_unjail_dataset() { + local jail_config="${bastille_jailsdir}/${JAIL}/jail.conf" + # Exit if DATASET is empty if [ -z "${DATASET}" ]; then usage @@ -98,57 +118,65 @@ zfs_unjail_dataset() { fi # Validate jail state - check_target_is_stopped "${_jail}" || if [ "${AUTO}" -eq 1 ]; then - bastille stop "${_jail}" + check_target_is_stopped "${JAIL}" || if [ "${AUTO}" -eq 1 ]; then + bastille stop "${JAIL}" else error_notify "Jail is running." error_exit "Use [-a|--auto] to auto-stop the jail." fi + # Unjail the dataset + zfs set jailed=off "${DATASET}" + zfs umount "${DATASET}" + # Remove dataset from zfs.conf - if ! grep -hoqsw "${DATASET}" ${bastille_jailsdir}/${_jail}/zfs.conf; then + if ! grep -hoqsw "${DATASET}" ${bastille_jailsdir}/${JAIL}/zfs.conf; then error_exit "[ERROR]: Dataset not present in zfs.conf." else - sed -i '' "\#.*${DATASET}.*#d" "${bastille_jailsdir}/${_jail}/zfs.conf" + sed -i '' "\#.*${DATASET}.*#d" "${bastille_jailsdir}/${JAIL}/zfs.conf" fi + # Remove config from jail.conf + sed -i '' "\#.*Jailed dataset: ${DATASET}.*#d" "${jail_config}" + sed -i '' "\#.*zfs jail ${JAIL} ${DATASET}.*#d" "${jail_config}" + if [ "${AUTO}" -eq 1 ]; then - bastille start "${_jail}" + bastille start "${JAIL}" fi } zfs_snapshot() { # shellcheck disable=SC2140 - zfs snapshot -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}"@"${TAG}" + zfs snapshot -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}"@"${TAG}" _return=$? } zfs_rollback() { # shellcheck disable=SC2140 - zfs rollback -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}"@"${TAG}" + zfs rollback -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}"@"${TAG}" # shellcheck disable=SC2140 - zfs rollback -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}/root"@"${TAG}" + zfs rollback -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}/root"@"${TAG}" _return=$? } zfs_destroy_snapshot() { # shellcheck disable=SC2140 - zfs destroy ${OPT_DESTROY} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}"@"${TAG}" + zfs destroy ${OPT_DESTROY} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}"@"${TAG}" _return=$? } zfs_set_value() { - zfs set "${ATTRIBUTE}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}" + zfs set "${ATTRIBUTE}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}" _return=$? } zfs_get_value() { - zfs get "${ATTRIBUTE}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}" + zfs get "${ATTRIBUTE}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}" _return=$? } zfs_disk_usage() { - zfs list -t all -o name,used,avail,refer,mountpoint,compress,ratio -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}" + zfs list -t all -o name,used,avail,refer,mountpoint,compress,ratio -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}" _return=$? } @@ -162,10 +190,10 @@ snapshot_checks() { # Verify rollback snapshots if [ "${SNAP_ROLLBACK}" -eq 1 ]; then if [ -n "${TAG}" ]; then - SNAP_TAG_CHECK="$(zfs list -H -t snapshot -o name ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail} | grep -o "${TAG}$" | tail -n 1)" + SNAP_TAG_CHECK="$(zfs list -H -t snapshot -o name ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL} | grep -o "${TAG}$" | tail -n 1)" else - TAG="$(zfs list -H -t snapshot -o name ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail} | grep -o "bastille_${_jail}_.*$" | tail -n 1)" - SNAP_TAG_CHECK=$(echo ${TAG} | grep -wo "bastille_${_jail}_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}") + TAG="$(zfs list -H -t snapshot -o name ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL} | grep -o "bastille_${JAIL}_.*$" | tail -n 1)" + SNAP_TAG_CHECK=$(echo ${TAG} | grep -wo "bastille_${JAIL}_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}") fi if [ -z "${SNAP_TAG_CHECK}" ]; then error_continue "[ERROR]: Snapshot not found: ${TAG}" @@ -177,10 +205,10 @@ snapshot_checks() { # Generate a relatively short but unique name for the snapshots based on the current date/jail name. elif [ "${AUTO_TAG}" -eq 1 ]; then DATE=$(date +%F-%H%M%S) - TAG="bastille_${_jail}_${DATE}" + TAG="bastille_${JAIL}_${DATE}" # Check for the generated snapshot name. SNAP_GEN_CHECK="" - SNAP_GEN_CHECK=$(echo ${TAG} | grep -wo "bastille_${_jail}_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}") + SNAP_GEN_CHECK=$(echo ${TAG} | grep -wo "bastille_${JAIL}_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}") if [ -z "${SNAP_GEN_CHECK}" ]; then error_notify "[ERROR]: Failed to validate snapshot name." fi @@ -296,9 +324,9 @@ if [ -z "${bastille_zfs_zpool}" ]; then error_exit "[ERROR]: ZFS zpool not defined." fi -for _jail in ${JAILS}; do +for JAIL in ${JAILS}; do - info "\n[${_jail}]:" + info "\n[${JAIL}]:" case "${ACTION}" in destroy|destroy_snap|destroy_snapshot) From 1db5aa617d287065b77cb3742c02c38c85e59503 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:08:17 -0700 Subject: [PATCH 2/5] stop: remove zfs jail dataset block --- usr/local/share/bastille/stop.sh | 8 -------- 1 file changed, 8 deletions(-) diff --git a/usr/local/share/bastille/stop.sh b/usr/local/share/bastille/stop.sh index f62017dd..d7fcb019 100644 --- a/usr/local/share/bastille/stop.sh +++ b/usr/local/share/bastille/stop.sh @@ -124,14 +124,6 @@ for _jail in ${JAILS}; do bastille limits "${_jail}" clear fi - # Unmount any jailed ZFS datasets - if [ -s "${bastille_jailsdir}/${_jail}/zfs.conf" ]; then - while read _dataset _mount; do - jexec -l -U root "${_jail}" zfs umount "${_dataset}" - zfs unjail "${_jail}" "${_dataset}" - done < "${bastille_jailsdir}/${_jail}/zfs.conf" - fi - # Stop jail jail ${OPTION} -f "${bastille_jailsdir}/${_jail}/jail.conf" -r "${_jail}" From fb37dbebfa140bc3be8921ccbb2a0e3de9777caa Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:08:46 -0700 Subject: [PATCH 3/5] start: remove zfs jail dataset block --- usr/local/share/bastille/start.sh | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/usr/local/share/bastille/start.sh b/usr/local/share/bastille/start.sh index bb9dcc98..3a68a09f 100644 --- a/usr/local/share/bastille/start.sh +++ b/usr/local/share/bastille/start.sh @@ -184,16 +184,6 @@ for _jail in ${JAILS}; do # Start jail jail ${OPTION} -f "${bastille_jailsdir}/${_jail}/jail.conf" -c "${_jail}" - # Add ZFS jailed datasets - if [ -s "${bastille_jailsdir}/${_jail}/zfs.conf" ]; then - while read _dataset _mount; do - zfs set jailed=on "${_dataset}" - zfs jail ${_jail} "${_dataset}" - jexec -l -U root "${_jail}" zfs set mountpoint="${_mount}" "${_dataset}" - jexec -l -U root "${_jail}" zfs mount "${_dataset}" 2>/dev/null - done < "${bastille_jailsdir}/${_jail}/zfs.conf" - fi - # Add rctl limits if [ -s "${bastille_jailsdir}/${_jail}/rctl.conf" ]; then while read _limits; do @@ -218,4 +208,4 @@ for _jail in ${JAILS}; do # Delay between jail action sleep "${DELAY_TIME}" -done \ No newline at end of file +done From 076a069604aaa0fd0697c3a53b634e7859dddd63 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:21:30 -0700 Subject: [PATCH 4/5] docs: zfs: remove template section --- docs/chapters/zfs-support.rst | 37 ++++------------------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/docs/chapters/zfs-support.rst b/docs/chapters/zfs-support.rst index ca6046c4..2944ac83 100644 --- a/docs/chapters/zfs-support.rst +++ b/docs/chapters/zfs-support.rst @@ -85,8 +85,8 @@ and being able to fully manage it from within the jail. To add a dataset to a jail, we can run ``bastille zfs TARGET jail pool/dataset /path/inside/jail``. -This will mount ``pool/dataset`` into the jail at ``/path/inside/jail`` when the -jail is started, and unmount and unjail it when the jail is stopped. +This will assign ``pool/dataset`` to the jail and mount it +at ``/path/inside/jail``. You can manually change the path where the dataset will be mounted by ``bastille edit TARGET zfs.conf`` and adjusting the path after you have added it, @@ -105,34 +105,5 @@ simple. To remove a dataset from being jailed, we can run ``bastille zfs TARGET unjail pool/dataset``. -Template Approach -^^^^^^^^^^^^^^^^^ - -While it is possible to "jail" a dataset using a template, it is a bit more -"hacky" than the above apporach. -Below is a template that you can use that will add the necessary bits to the -``jail.conf`` file to "jail" a dataset. - -.. code-block:: shell - - ARG JAIL_NAME - ARG DATASET - ARG MOUNT - - CONFIG set allow.mount - CONFIG set allow.mount.devfs - CONFIG set allow.mount.zfs - CONFIG set enforce_statfs 1 - - CONFIG set "exec.created += '/sbin/zfs jail ${JAIL_NAME} ${DATASET}'" - CONFIG set "exec.start += '/sbin/zfs set mountpoint=${MOUNT} ${DATASET}'" - - RESTART - - CONFIG set "exec.prestop += 'jexec -l -U root ${JAIL_NAME} /sbin/zfs umount ${DATASET}'" - CONFIG set "exec.prestop += '/sbin/zfs unjail ${JAIL_NAME} ${DATASET}'" - - RESTART - -This template can be applied using ``bastille template TARGET project/template --arg DATASET=zpool/dataset --arg MOUNT=/path/inside/jail``. -We do not need the ``JAIL_NAME`` arg, as it will be auto-filled from the supplied ``TARGET`` name. +NOTE: You must unjail any jailed datasets before attempting to destroy +a jail. From 3803a5da26c0f5007b7971c1822fc5b0775f9c07 Mon Sep 17 00:00:00 2001 From: tschettervictor Date: Thu, 6 Nov 2025 11:40:03 -0700 Subject: [PATCH 5/5] start: validate mountpoint for jailed datasets --- usr/local/share/bastille/start.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/usr/local/share/bastille/start.sh b/usr/local/share/bastille/start.sh index 3a68a09f..cee7f5a9 100644 --- a/usr/local/share/bastille/start.sh +++ b/usr/local/share/bastille/start.sh @@ -181,6 +181,17 @@ for _jail in ${JAILS}; do fi fi + # Validate jailed datasets mountpoint + if [ -s "${bastille_jailsdir}/${_jail}/zfs.conf" ]; then + while read dataset mount; do + if [ "$(zfs get -H -o value mountpoint ${dataset})" != "${mount}" ]; then + zfs set jailed=off "${dataset}" + zfs set mountpoint="${mount}" "${dataset}" + zfs set jailed=on "${dataset}" + fi + done < "${bastille_jailsdir}/${_jail}/zfs.conf" + fi + # Start jail jail ${OPTION} -f "${bastille_jailsdir}/${_jail}/jail.conf" -c "${_jail}"