Mercurial > hgrepos > FreeBSD > ports > sysutils > local-bsdtools
diff sbin/fjail @ 128:3dcae0e91769
Move all admin scripts into the "sbin" folder
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Thu, 17 Oct 2019 09:09:13 +0200 |
| parents | bin/fjail@52523a19797e |
| children | 6be3742d21f7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sbin/fjail Thu Oct 17 09:09:13 2019 +0200 @@ -0,0 +1,304 @@ +#!/bin/sh +# -*- indent-tabs-mode: nil; -*- +: 'A very minimal BSD Jail management tool. + +:Author: Franz Glasner +:Copyright: (c) 2019 Franz Glasner. + All rights reserved. +:License: BSD 3-Clause "New" or "Revised" License. + See LICENSE for details. + If you cannot find LICENSE see + <https://opensource.org/licenses/BSD-3-Clause> +:ID: @(#)@@PKGORIGIN@@ $HGid$ + +' + +set -eu + +VERSION="@@VERSION@@" + +USAGE=' +USAGE: fjail [ OPTIONS ] COMMAND [ COMMAND OPTIONS ] [ ARG ... ] + +OPTIONS: + + -V Print the program name and version number to stdout and exit + + -h Print this help message to stdout and exit + +COMMANDS: + + datasets [-u] PARENT CHILD + + Create ZFS datasets to be used within a jail + + PARENT must exist already and CHILD must not exist. + + -u Do not automatically mount newly created datasets + + privs MOUNTPOINT + + Adjust some Unix privileges to mounted jail datasets + + populate MOUNTPOINT BASETXZ + + Populate the jail directory in MOUNTPOINT with the base system in BASETXZ + + copy SOURCE-DATASET DEST-DATASET + + Copy a tree of ZFS datasets with "zfs send -R" and "zfs receive". + Note that the destination dataset must not exist already. + + -u Do not automatically mount received datasets + +ENVIRONMENT: + + All environment variables that affect "zfs" are effective also. + +DESCRIPTION: + + All commands with the exception of "populate" require ZFS as + filesystem. +' + + +# Reset to standard umask +umask 0022 + + +# +# "datasets" -- create the ZFS dataset tree +# +# command_datasets [ -u ] parent-dataset child-dataset +# +# -u do not automatically mount newly created datasets +# +command_datasets() { + # parent ZFS dataset -- child ZFS dataset name + local _pds _cds + # and its mount point + local _pmp _get _dummy + # full name of the dataset + local _ds + # dynamic ZFS options + local _zfsopts + + _zfsopts="" + while getopts "u" _opt ; do + case ${_opt} in + u) + # do not mount newly created datasets + _zfsopts="${_zfsopts} -u" + ;; + \?|:) + return 2; + ;; + esac + done + shift $((OPTIND-1)) + OPTIND=1 + + _pds="$1" + if [ -z "${_pds}" ]; then + echo "ERROR: no parent dataset given" >&2 + return 2 + fi + _get=$(zfs get -H mountpoint "${_pds}" 2>/dev/null) || { echo "ERROR: dataset \`${_pds}' does not exist" >&2; return 1; } + IFS=$'\t' read _dummy _dummy _pmp _dummy <<EOF +${_get} +EOF + case "${_pmp}" in + none) + echo "ERROR: dataset \`${_pds}' has no mountpoint" >&2 + return 1 + ;; + legacy) + echo "ERROR: dataset \`${_pds}' has a \`${_mp}' mountpoint" >&2 + return 1 + ;; + *) + # VOID + ;; + esac + _cds="$2" + if [ -z "${_cds}" ]; then + echo "ERROR: no child dataset given" >&2 + return 2 + fi + _ds="${_pds}/${_cds}" + echo "Resulting new root dataset is \`${_ds}' at mountpoint \`${_pmp}/${_cds}'" + if zfs get -H mountpoint "${_ds}" >/dev/null 2>/dev/null; then + echo "ERROR: dataset \`${_ds}' does already exist" >&2 + return 1 + fi + zfs create ${_zfsopts} -o atime=off "${_ds}" + zfs create ${_zfsopts} -o sync=disabled -o setuid=off "${_ds}/tmp" + zfs create ${_zfsopts} "${_ds}/usr" + zfs create ${_zfsopts} "${_ds}/var" + zfs create ${_zfsopts} -o exec=off -o setuid=off "${_ds}/var/audit" + zfs create ${_zfsopts} -o exec=off -o setuid=off "${_ds}/var/cache" + zfs create ${_zfsopts} -o exec=off -o setuid=off -o compression=off "${_ds}/var/cache/pkg" + zfs create ${_zfsopts} -o exec=off -o setuid=off -o compression=off "${_ds}/var/crash" + zfs create ${_zfsopts} -o exec=off -o setuid=off "${_ds}/var/db" + zfs create ${_zfsopts} -o exec=on -o setuid=off "${_ds}/var/db/pkg" + zfs create ${_zfsopts} -o readonly=on -o exec=off -o setuid=off "${_ds}/var/empty" + zfs create ${_zfsopts} -o exec=off -o setuid=off -o primarycache=metadata "${_ds}/var/log" + zfs create ${_zfsopts} -o exec=off -o setuid=off -o atime=on "${_ds}/var/mail" + zfs create ${_zfsopts} -o sync=disabled -o exec=off -o setuid=off -o compression=off -o primarycache=all "${_ds}/var/run" + zfs create ${_zfsopts} -o sync=disabled -o setuid=off "${_ds}/var/tmp" +} + + +# +# "populate" -- populate the datasets with content from a FreeBSD base.txz +# +# command_populate mountpoint basetxz +# +command_populate() { + # MOUNTPOINT -- base.txz + local _mp _basetxz + + _mp="$1" + _basetxz="$2" + + if [ -z "${_mp}" ]; then + echo "ERROR: no mountpoint given" >&2 + return 2 + fi + if [ -z "${_basetxz}" ]; then + echo "ERROR: no base.txz given" >&2 + return 2 + fi + if [ ! -d "${_mp}" ]; then + echo "ERROR: mountpoint \`${_mp}' does not exist" >&2 + return 1 + fi + if [ ! -r "${_basetxz}" ]; then + echo "ERROR: file \`${_basetxz}' is not readable" >&2 + return 1 + fi + + tar -C "${_mp}" --exclude=./var/empty -xJp -f "${_basetxz}" || { echo "ERROR: tar encountered errors" >&2; return 1; } +} + + +# +# "copy" -- ZFS copy of datasets +# +# command_copy source-dataset destination-dataset +# +command_copy() { + # source dataset -- destination dataset + local _source _dest + # dynamic ZFS options + local _zfsopts + + _zfsopts="" + while getopts "u" _opt ; do + case ${_opt} in + u) + # do not mount newly created datasets + _zfsopts="${_zfsopts} -u" + ;; + \?|:) + return 2; + ;; + esac + done + shift $((OPTIND-1)) + OPTIND=1 + + _source="$1" + if [ -z "${_source}" ]; then + echo "ERROR: no source dataset given" >&2 + return 2 + fi + _dest="$2" + if [ -z "${_dest}" ]; then + echo "ERROR: no source dataset given" >&2 + return 2 + fi + zfs send -R -n -v ${_source} || { echo "ERROR: ZFS operation failed in no-op mode" >&2; return 1; } + zfs send -R "${_source}" | zfs receive ${_zfsopts} "${_dest}" || { echo "ERROR: ZFS operation failed" >&2; return 1; } +} + + +# +# "privs" -- adjust privileges +# +# To be used when all ZFS datasets are mounted. +# +command_privs() { + # mountpoint + local _mp _d + + _mp="$1" + if [ -z "${_mp}" ]; then + echo "ERROR: no mountpoint given" >&2 + return 2 + fi + if [ ! -d "${_mp}" ]; then + echo "ERROR: directory \`${_mp}' does not exist" >&2 + return 1 + fi + for _d in tmp var/tmp ; do + chmod 01777 "${_mp}/${_d}" + done + chown root:mail "${_mp}/var/mail" + chmod 0775 "${_mp}/var/mail" +} + + +# +# Global option handling +# +while getopts "Vh" _opt ; do + case ${_opt} in + V) + echo "fjail v${VERSION} (rv:@@HGREVISION@@)" + exit 0 + ;; + h) + echo "${USAGE}" + exit 0 + ;; + \?) + exit 2; + ;; + *) + echo "ERROR: option handling failed" >&2 + exit 2 + ;; + esac +done + +# +# Reset the Shell's option handling system to prepare for handling +# command-local options. +# +shift $((OPTIND-1)) +OPTIND=1 + +test $# -gt 0 || { echo "ERROR: no command given" >&2; exit 2; } + +command="$1" +shift + +case "${command}" in + datasets) + command_datasets "$@" + ;; + privs) + command_privs "$@" + ;; + populate) + command_populate "$@" + ;; + copy) + command_copy "$@" + ;; + *) + echo "ERROR: unknown command \`${command}'" >&2 + exit 2 + ;; +esac
