From 095380a8d03be3a0454626e8b150712f125d814d Mon Sep 17 00:00:00 2001 From: cryx Date: Sat, 23 May 2009 13:09:53 +0000 Subject: First chunk of ZFS support in ezjail. ezjail is now capable of managing jails in seperate ZFS filesystems and to manage basejail and newjail in seperate ZFS filesystems too. It is possible to mix non-ZFS jails with ZFS jails as well as using ZFS jails with basejail/newjail in a non-ZFS filesystem. To create a zfs jail you need an existing ZFS pool, ZFS needs to be enabled in /etc/rc.conf and you have to set at least ezjail_jailzfs in ezjail.conf. To let ezjail manage basejail/newjail in ZFS filesystems to, you have to enable ezjail_use_zfs in ezjail.conf. To use ZFS support in ezjail, you have to use at least FreeBSD 7-STABLE form after the commit of ZFS version 13 (commited Wed May 20 23:34:59 2009 UTC, http://svn.freebsd.org/viewvc/base?view=revision&revision=192498) of FreeBSD 8-CURRENT. Prior versions of ZFS are _not_ supported. Creating a ZFS based jail is as easy as using 'ezjail-admin create -c zfs '. Using zfs send/receive for archiving is not yet implemented. Converting non-ZFS basejail/newjail setups into ZFS setups is not handled by ezjail, converting non-ZFS jails into ZFS jails is not yet handled by ezjail but will be possible in the future. WARNING: ZFS is considered to be an experimental feature in FreeBSD. ZFS support in ezjail is work in progress. --- ezjail-admin | 129 +++++++++++++++++++++++++++++++++++++++++++++-------- ezjail.conf.sample | 7 +++ 2 files changed, 118 insertions(+), 18 deletions(-) diff --git a/ezjail-admin b/ezjail-admin index 579b758..5c67d38 100755 --- a/ezjail-admin +++ b/ezjail-admin @@ -39,7 +39,7 @@ case `uname -p` in amd64) ezjail_dirlist="${ezjail_dirlist} usr/lib32"; ezjail_b # Synopsis messages ezjail_usage_ezjailadmin="${ezjail_admin} v3.0\nUsage: ${ezjail_admin} [archive|config|console|create|delete|install|list|restore|update] {params}" ezjail_usage_install="Usage: ${ezjail_admin} install [-mMpPsS] [-h host] [-r release]" -ezjail_usage_create="Usage: ${ezjail_admin} create [-xbi] [-f flavour] [-r jailroot] [-s size] [-c bde|eli] [-C args] [-a archive] jailname jailip" +ezjail_usage_create="Usage: ${ezjail_admin} create [-xbi] [-f flavour] [-r jailroot] [-s size] [-c bde|eli|zfs] [-C args] [-a archive] jailname jailip" ezjail_usage_delete="Usage: ${ezjail_admin} delete [-w] jailname" ezjail_usage_update="Usage: ${ezjail_admin} update [-s sourcetree] [-p] (-b|-i|-u|-P)" ezjail_usage_config="Usage: ${ezjail_admin} config [-r run|norun] [-n newname] [-i attach|detach|fsck] jailname" @@ -200,6 +200,13 @@ ezjail_splitworld() { # This mkdir is important, since cpio will create intermediate # directories with permission 0700 which is bad + if [ "${ezjail_use_zfs}" = "YES" ]; then + echo "ZFS: create the basejail" + echo "/sbin/zfs create -p -o mountpoint=${ezjail_jaildir} ${ezjail_zfs_properties} ${ezjail_jailzfs}" + /sbin/zfs create -p -o mountpoint=${ezjail_jaildir} ${ezjail_zfs_properties} ${ezjail_jailzfs} + /sbin/zfs create -p ${ezjail_jailzfs}/basejail + /sbin/zfs snapshot ${ezjail_jailzfs}/basejail@`date -v -7d +"%C%y%m%d_%H:%M:%S"` + fi mkdir -p "${ezjail_jailbase}/usr" for dir in ${ezjail_dirlist}; do find ${dir} | cpio -d -p -v "${ezjail_jailbase}" || exerr "Error: Installation of ${dir} failed." @@ -208,8 +215,15 @@ ezjail_splitworld() { mkdir basejail # Try to remove the old template jail - [ -d "${ezjail_jailtemplate}" ] && chflags -R noschg "${ezjail_jailtemplate}" && rm -rf "${ezjail_jailtemplate}" - mv "${ezjail_jailfull}" "${ezjail_jailtemplate}" + if [ "${ezjail_use_zfs}" = "YES" ]; then + echo "ZFS: cleanup old template jail" + [ -d "${ezjail_jailtemplate}" ] && zfs destroy -R ${ezjail_jailzfs}/newjail && rm -rf "${ezjail_jailtemplate}" + cd ${ezjail_jaildir} + zfs rename ${ezjail_jailzfs}/fulljail ${ezjail_jailzfs}/newjail + else + [ -d "${ezjail_jailtemplate}" ] && chflags -R noschg "${ezjail_jailtemplate}" && rm -rf "${ezjail_jailtemplate}" + mv "${ezjail_jailfull}" "${ezjail_jailtemplate}" + fi # If the default flavour example has not yet been copied, do it now [ -d "${ezjail_flavours}/default" ] || mkdir -p "${ezjail_flavours}" && cp -p -R "${ezjail_examples}/default" "${ezjail_flavours}" @@ -313,6 +327,20 @@ parse_gbde_attach_args () { return ${_exit} } +check_for_zfs () { + . "/etc/rc.conf" + if [ "${ezjail_use_zfs}" = "YES" ] && [ "${zfs_enable}" != "YES" ]; then + echo "You have to enable ZFS in /etc/rc.conf" + exit + fi + + _zpoolstatus=`/sbin/zpool list -H -o health ${ezjail_jailzfs%%/*} 2> /dev/null` + if [ ! "${_zpoolstatus}" = "ONLINE" ]; then + echo "Your zpool does not exist or is not online." + exit + fi +} + ############################# # End of function definitions # @@ -320,6 +348,8 @@ parse_gbde_attach_args () { # check for command [ $# -gt 0 ] || exerr ${ezjail_usage_ezjailadmin} +check_for_zfs + case "$1" in ######################## ezjail-admin CREATE ######################## create) @@ -343,12 +373,18 @@ create) # we need at least a name and an ip for new jail [ "${ezjail_name}" -a "${ezjail_ip}" -a $# -eq 2 ] || exerr ${ezjail_usage_create} + + # show the user the type of image used + echo "TYPE: $ezjail_imagetype" # check for sanity of settings concerning the image feature - [ -z "${ezjail_imagetype}" -o "${ezjail_exists}" -o "${ezjail_imagesize}" ] || exerr "Error: Image jails need an image size." + if [ "${ezjail_imagetype}" != "zfs" ]; then + [ -z "${ezjail_imagetype}" -o "${ezjail_exists}" -o "${ezjail_imagesize}" ] || exerr "Error: Image jails need an image size." + fi + # check for a sane image type - case ${ezjail_imagetype} in ""|simple|bde|eli) ;; *) exerr ${ezjail_usage_create};; esac + case ${ezjail_imagetype} in ""|simple|bde|eli|zfs) ;; *) exerr ${ezjail_usage_create};; esac # check for a sane image size and split it up in blocks if [ "${ezjail_imagesize}" ]; then @@ -422,13 +458,16 @@ create) # Location of our image file ezjail_image="${ezjail_image}.img" + + # zfs does not use image files + [ "${ezjail_imagetype}" = "zfs" ] && unset ezjail_image # Prepare crypto jail so that an attacker cannot guess which blocks # have been written case ${ezjail_imagetype} in bde|eli) ezjail_sourcedevice="/dev/random";; simple) ezjail_sourcedevice="/dev/zero";; esac - # If NOT exist, create image - if [ -z "${ezjail_exists}" ]; then + # If NOT exist and imagetype not ZFS, create image + if [ -z "${ezjail_exists}" ] && [ ! ${ezjail_imagetype} = "zfs" ]; then [ -e "${ezjail_image}" ] && exerr "Error: A file exists at ${ezjail_image}.\n Won't overwrite an existing image." # Now create jail disc image @@ -475,9 +514,18 @@ create) simple) ezjail_device=${ezjail_imagedevice} ;; + zfs) + echo "ZFS: create the jail filesystem" + if [ ${ezjail_imagesize} ]; then + ezjail_zfs_jail_properies="-o quota=${ezjail_imagesize} -o compression=lzjb" + fi + [ -d "${ezjail_jaildir}/${ezjail_hostname}" ] && exerr "Error: Could not create jail root mount point ${ezjail_rootdir}" + /sbin/zfs create -p -o mountpoint=${ezjail_rootdir} ${ezjail_zfs_jail_properies} ${ezjail_jailzfs}/${ezjail_hostname} + ;; + esac - if [ -z "${ezjail_exists}" ]; then + if [ -z "${ezjail_exists}" ] && [ ! ${ezjail_imagetype} = "zfs" ]; then # Format memory image newfs -U "/dev/${ezjail_device}" || detach_images || exerr "Error: Could not newfs /dev/${ezjail_device}." # Create mount point and mount @@ -500,7 +548,16 @@ create) [ $? -eq 0 ] || detach_images || exerr "Error: Could not extract archive from ${ezjail_fromarchive}." elif [ -z "${ezjail_exists}" ]; then # now take a copy of our template jail - mkdir -p "${ezjail_rootdir}" && cd "${ezjail_jailtemplate}" && find . | cpio -p -v "${ezjail_rootdir}" > /dev/null + if [ "${ezjail_imagetype}" = "zfs" ] && [ "${ezjail_use_zfs}" = "YES" ]; then + # create ZFS filesystem first when using ZFS + /sbin/zfs snapshot ${ezjail_jailzfs}/newjail@_createnewjailtmp + /sbin/zfs send ${ezjail_jailzfs}/newjail@_createnewjailtmp | zfs receive -F ${ezjail_jailzfs}/${ezjail_hostname} + /sbin/zfs destroy ${ezjail_jailzfs}/${ezjail_hostname}@_createnewjailtmp + /sbin/zfs destroy ${ezjail_jailzfs}/newjail@_createnewjailtmp + else + mkdir -p "${ezjail_rootdir}" && cd "${ezjail_jailtemplate}" && find . | cpio -p -v "${ezjail_rootdir}" > /dev/null + fi + [ $? -eq 0 ] || detach_images || exerr "Error: Could not copy template jail." fi @@ -510,8 +567,9 @@ create) # if the automount feature is not disabled, this fstab entry for new jail # will be obeyed echo -n > /etc/fstab.${ezjail_safename} - [ "${ezjail_imagetype}" ] && \ - echo ${ezjail_devicelink} ${ezjail_rootdir} ufs rw 0 0 >> "/etc/fstab.${ezjail_safename}" + if [ "${ezjail_imagetype}" ] && [ ! "${ezjail_imagetype}" = "zfs" ] ; then + echo ${ezjail_devicelink} ${ezjail_rootdir} ufs rw 0 0 >> "/etc/fstab.${ezjail_safename}" + fi echo ${ezjail_jailbase} ${ezjail_rootdir}/basejail nullfs ro 0 0 >> "/etc/fstab.${ezjail_safename}" # now, where everything seems to have gone right, create control file in @@ -576,7 +634,7 @@ create) [ $? -eq 0 ] && echo -e "Warning: Some services already seem to be listening on all IP, (including ${ezjail_ip})\n This may cause some confusion, here they are:\n${ezjail_listener}" IFS=${TIFS} - [ "${ezjail_imagetype}" ] && echo "Note: To administrate your image jail, attach it using the '${ezjail_admin} config -i attach ${ezjail_hostname}' command." + [ "${ezjail_imagetype}" ] && [ "${ezjail_imagetype}" != "zfs" ] && echo "Note: To administrate your image jail, attach it using the '${ezjail_admin} config -i attach ${ezjail_hostname}' command." ;; ######################## ezjail-admin DELETE ######################## delete) @@ -626,7 +684,15 @@ delete) # if wiping the jail was requested, remove it if [ "${ezjail_wipeme}" ]; then - [ "${ezjail_image}" ] && rm -f "${ezjail_image}" "${ezjail_image%.img}.device" + case ${ezjail_imagetype} in + simple|bde|eli) + [ "${ezjail_image}" ] && rm -f "${ezjail_image}" "${ezjail_image%.img}.device" + ;; + zfs) + echo "ZFS: delete the jails ZFS" + /sbin/zfs destroy -r ${ezjail_jailzfs}/${ezjail_hostname} + ;; + esac rm -rf "${ezjail_rootdir}" fi @@ -674,6 +740,10 @@ setup|update) # Check if some action was requested [ "${ezjail_installaction}" ] || exerr "Error: No install action has been chosen.\n Please note that ezjails behaviour changed. Rebuilding the world no longer is default.\n Run '${ezjail_admin} update -b' to build and install a world from source or '${ezjail_admin} update -i' to install an already built world." + if [ "${ezjail_use_zfs}" = "YES" ]; then + zfs create -p -o mountpoint=${ezjail_jaildir} ${ezjail_jailzfs} + fi + if [ "${ezjail_installaction}" = "none" ]; then # check, whether ezjail has been setup correctly. existence of # ezjail_jailbase is our indicator @@ -683,6 +753,9 @@ setup|update) # If ran from cron be kind to freebsds update servers and sleep first [ -z "$TERM" -o "$TERM" = "dumb" ] && sleep $(( ${RANDOM} % 3600 )) + if [ "${ezjail_use_zfs}" = "YES" ]; then + zfs snapshot ${ezjail_jailzfs}/basejail@`date -v -7d +"%C%y%m%d_%H:%M:%S"` + fi freebsd-update -b ${ezjail_jailbase} fetch install else # Bump the user for some of the most common errors @@ -692,8 +765,14 @@ setup|update) # Normally fulljail should be renamed by past ezjail-admin commands. # However those may have failed - [ -d "${ezjail_jailfull}" ] && chflags -R noschg "${ezjail_jailfull}" && rm -rf "${ezjail_jailfull}" - mkdir -p "${ezjail_jailfull}" || exerr "Error: Cannot create temporary Jail directory." + if [ "${ezjail_use_zfs}" = "YES" ]; then + echo "ZFS: manage basejail and newjail" + [ -d "${ezjail_jailfull}" ] && /sbin/zfs destroy -R "${ezjail_jailzfs}/fulljail" && rm -rf "${ezjail_jailfull}" + /sbin/zfs create -p "${ezjail_jailzfs}/fulljail" || exerr "Error: Cannot create temporary Jail directory." + else + [ -d "${ezjail_jailfull}" ] && chflags -R noschg "${ezjail_jailfull}" && rm -rf "${ezjail_jailfull}" + mkdir -p "${ezjail_jailfull}" || exerr "Error: Cannot create temporary Jail directory." + fi # make and setup our world, then split basejail and newjail cd "${ezjail_sourcetree}" && env DESTDIR="${ezjail_jailfull}" make ${ezjail_installaction} || exerr "Error: The command 'make ${ezjail_installaction}' failed.\n Refer to the error report(s) above." @@ -751,8 +830,14 @@ install) # Normally fulljail should be renamed by past ezjail-admin commands. # However those may have failed - [ -d "${ezjail_jailfull}" ] && chflags -R noschg "${ezjail_jailfull}" && rm -rf "${ezjail_jailfull}" - mkdir -p "${ezjail_jailfull}" || exerr "Error: Cannot create temporary jail directory." + if [ "${ezjail_use_zfs}" = "YES" ]; then + echo "ZFS: manage basejail and newjail" + [ -d "${ezjail_jailfull}" ] && /sbin/zfs destroy -R "${ezjail_jailzfs}/fulljail" && rm -rf "${ezjail_jailfull}" + /sbin/zfs create -p "${ezjail_jailzfs}/fulljail" || exerr "Error: Cannot create temporary Jail directory." + else + [ -d "${ezjail_jailfull}" ] && chflags -R noschg "${ezjail_jailfull}" && rm -rf "${ezjail_jailfull}" + mkdir -p "${ezjail_jailfull}" || exerr "Error: Cannot create temporary Jail directory." + fi DESTDIR=${ezjail_jailfull} rm -rf "${ezjail_jailtemp}" @@ -1108,6 +1193,12 @@ config) else unset ezjail_new_image fi + + if [ "${ezjail_imagetype}" = "zfs" ]; then + # ZFS: using the zfs rename feature to rename the filesystem, remounting is done by ZFS + zfs rename ${ezjail_jailzfs}/${ezjail_hostname} ${ezjail_jailzfs}/${ezjail_new_hostname} + fi + # adjust softlink if [ -L "${ezjail_softlink}" ]; then @@ -1116,7 +1207,9 @@ config) fi # rename rootdir - mv "${ezjail_rootdir}" "${ezjail_new_rootdir}" + if [ ! "${ezjail_imagetype}" = "zfs" ]; then + mv "${ezjail_rootdir}" "${ezjail_new_rootdir}" + fi # rename fstab echo -n > "/etc/fstab.${ezjail_new_safename}" diff --git a/ezjail.conf.sample b/ezjail.conf.sample index ebc904a..46c08bc 100755 --- a/ezjail.conf.sample +++ b/ezjail.conf.sample @@ -46,3 +46,10 @@ # ezjail_devfs_ruleset="devfsrules_jail" # ezjail_procfs_enable="YES" # ezjail_fdescfs_enable="YES" + +# Setting this to YES will start to manage the basejail and newjail in ZFS +# ezjail_use_zfs="YES" +# The name of the ZFS ezjail should create jails on, it will be mounted at the ezjail_jaildir +# ezjail_jailzfs="tank/ezjail" +# ADVANCED, be very careful! +# ezjail_zfs_properties="-o compression=lzjb -o atime=off" -- cgit v1.2.3