From 4bc721965ff272ad59c6e8a130a544e1e2c01b34 Mon Sep 17 00:00:00 2001 From: Matthew Macy Date: Wed, 11 Dec 2019 11:58:37 -0800 Subject: [PATCH] Add FreeBSD jail support hooks Add the 'zfs jail/unjail' subcommands along with the relevant documentation from FreeBSD. This feature is not supported on Linux and still requires the match kernel ioctls which will be included when the FreeBSD platform code is integrated. Reviewed-by: Jorgen Lundman Reviewed-by: Brian Behlendorf Signed-off-by: Matt Macy Signed-off-by: Ryan Moeller Closes #9686 --- cmd/zfs/Makefile.am | 4 + cmd/zfs/zfs_main.c | 91 +++++++++++++++++++- include/libzfs_impl.h | 2 +- lib/libzfs/os/linux/libzfs_mount_os.c | 4 +- man/man8/Makefile.am | 2 + man/man8/zfs-jail.8 | 119 ++++++++++++++++++++++++++ man/man8/zfs-unjail.8 | 1 + man/man8/zfs.8 | 14 +++ man/man8/zfsprops.8 | 17 +++- module/zcommon/zfs_prop.c | 5 ++ module/zfs/dsl_dir.c | 4 +- 11 files changed, 254 insertions(+), 9 deletions(-) create mode 100644 man/man8/zfs-jail.8 create mode 120000 man/man8/zfs-unjail.8 diff --git a/cmd/zfs/Makefile.am b/cmd/zfs/Makefile.am index 49ad6f21f..c824bf61e 100644 --- a/cmd/zfs/Makefile.am +++ b/cmd/zfs/Makefile.am @@ -15,3 +15,7 @@ zfs_LDADD = \ $(top_builddir)/lib/libuutil/libuutil.la \ $(top_builddir)/lib/libzfs/libzfs.la \ $(top_builddir)/lib/libzfs_core/libzfs_core.la + +if BUILD_FREEBSD +zfs_LDADD += -L/usr/local/lib -lintl -lgeom -ljail +endif diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c index 219433590..6b1777907 100644 --- a/cmd/zfs/zfs_main.c +++ b/cmd/zfs/zfs_main.c @@ -122,6 +122,11 @@ static int zfs_do_project(int argc, char **argv); static int zfs_do_version(int argc, char **argv); static int zfs_do_redact(int argc, char **argv); +#ifdef __FreeBSD__ +static int zfs_do_jail(int argc, char **argv); +static int zfs_do_unjail(int argc, char **argv); +#endif + /* * Enable a reasonable set of defaults for libumem debugging on DEBUG builds. */ @@ -176,6 +181,8 @@ typedef enum { HELP_CHANGE_KEY, HELP_VERSION, HELP_REDACT, + HELP_JAIL, + HELP_UNJAIL } zfs_help_t; typedef struct zfs_command { @@ -240,6 +247,11 @@ static zfs_command_t command_table[] = { { "unload-key", zfs_do_unload_key, HELP_UNLOAD_KEY }, { "change-key", zfs_do_change_key, HELP_CHANGE_KEY }, { "redact", zfs_do_redact, HELP_REDACT }, + +#ifdef __FreeBSD__ + { "jail", zfs_do_jail, HELP_JAIL }, + { "unjail", zfs_do_unjail, HELP_UNJAIL }, +#endif }; #define NCOMMAND (sizeof (command_table) / sizeof (command_table[0])) @@ -391,6 +403,10 @@ get_usage(zfs_help_t idx) case HELP_REDACT: return (gettext("\tredact " " ...")); + case HELP_JAIL: + return (gettext("\tjail \n")); + case HELP_UNJAIL: + return (gettext("\tunjail \n")); } abort(); @@ -734,7 +750,7 @@ zfs_mount_and_share(libzfs_handle_t *hdl, const char *dataset, zfs_type_t type) */ if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type, B_FALSE) && zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON) { - if (geteuid() != 0) { + if (zfs_mount_delegation_check()) { (void) fprintf(stderr, gettext("filesystem " "successfully created, but it may only be " "mounted by root\n")); @@ -6970,7 +6986,6 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual) const char *cmdname = (op == OP_SHARE) ? "unshare" : "unmount"; ino_t path_inode; - /* * Search for the given (major,minor) pair in the mount table. */ @@ -7233,8 +7248,12 @@ unshare_unmount(int op, int argc, char **argv) nomem(); while ((node = uu_avl_walk_next(walk)) != NULL) { - uu_avl_remove(tree, node); + const char *mntarg = NULL; + uu_avl_remove(tree, node); +#ifndef __FreeBSD__ + mntarg = node->un_zhp->zfs_name; +#endif switch (op) { case OP_SHARE: if (zfs_unshareall_bytype(node->un_zhp, @@ -7244,7 +7263,7 @@ unshare_unmount(int op, int argc, char **argv) case OP_MOUNT: if (zfs_unmount(node->un_zhp, - node->un_zhp->zfs_name, flags) != 0) + mntarg, flags) != 0) ret = 1; break; } @@ -8350,3 +8369,67 @@ main(int argc, char **argv) return (ret); } + +#ifdef __FreeBSD__ +#include +#include +/* + * Attach/detach the given dataset to/from the given jail + */ +/* ARGSUSED */ +static int +zfs_do_jail_impl(int argc, char **argv, boolean_t attach) +{ + zfs_handle_t *zhp; + int jailid, ret; + + /* check number of arguments */ + if (argc < 3) { + (void) fprintf(stderr, gettext("missing argument(s)\n")); + usage(B_FALSE); + } + if (argc > 3) { + (void) fprintf(stderr, gettext("too many arguments\n")); + usage(B_FALSE); + } + + jailid = jail_getid(argv[1]); + if (jailid < 0) { + (void) fprintf(stderr, gettext("invalid jail id or name\n")); + usage(B_FALSE); + } + + zhp = zfs_open(g_zfs, argv[2], ZFS_TYPE_FILESYSTEM); + if (zhp == NULL) + return (1); + + ret = (zfs_jail(zhp, jailid, attach) != 0); + + zfs_close(zhp); + return (ret); +} + +/* + * zfs jail jailid filesystem + * + * Attach the given dataset to the given jail + */ +/* ARGSUSED */ +static int +zfs_do_jail(int argc, char **argv) +{ + return (zfs_do_jail_impl(argc, argv, B_TRUE)); +} + +/* + * zfs unjail jailid filesystem + * + * Detach the given dataset from the given jail + */ +/* ARGSUSED */ +static int +zfs_do_unjail(int argc, char **argv) +{ + return (zfs_do_jail_impl(argc, argv, B_FALSE)); +} +#endif diff --git a/include/libzfs_impl.h b/include/libzfs_impl.h index e9fd4cf41..276241e93 100644 --- a/include/libzfs_impl.h +++ b/include/libzfs_impl.h @@ -237,7 +237,7 @@ extern proto_table_t proto_table[PROTO_END]; extern int do_mount(const char *src, const char *mntpt, char *opts, int flags); extern int do_unmount(const char *mntpt, int flags); -extern int zfs_can_user_mount(void); +extern int zfs_mount_delegation_check(void); extern int zfs_share_proto(zfs_handle_t *zhp, zfs_share_proto_t *proto); extern int unshare_one(libzfs_handle_t *hdl, const char *name, const char *mountpoint, zfs_share_proto_t proto); diff --git a/lib/libzfs/os/linux/libzfs_mount_os.c b/lib/libzfs/os/linux/libzfs_mount_os.c index af1cafd28..c04eb2161 100644 --- a/lib/libzfs/os/linux/libzfs_mount_os.c +++ b/lib/libzfs/os/linux/libzfs_mount_os.c @@ -361,7 +361,7 @@ do_unmount(const char *mntpt, int flags) } int -zfs_can_user_mount(void) +zfs_mount_delegation_check(void) { - return (geteuid() == 0); + return ((geteuid() != 0) ? EACCES : 0); } diff --git a/man/man8/Makefile.am b/man/man8/Makefile.am index 33e502ed2..a4cd9f567 100644 --- a/man/man8/Makefile.am +++ b/man/man8/Makefile.am @@ -17,6 +17,7 @@ dist_man_MANS = \ zfs-groupspace.8 \ zfs-hold.8 \ zfs-inherit.8 \ + zfs-jail.8 \ zfs-list.8 \ zfs-load-key.8 \ zfs-mount.8 \ @@ -35,6 +36,7 @@ dist_man_MANS = \ zfs-share.8 \ zfs-snapshot.8 \ zfs-unallow.8 \ + zfs-unjail.8 \ zfs-unload-key.8 \ zfs-unmount.8 \ zfs-upgrade.8 \ diff --git a/man/man8/zfs-jail.8 b/man/man8/zfs-jail.8 new file mode 100644 index 000000000..8274179bb --- /dev/null +++ b/man/man8/zfs-jail.8 @@ -0,0 +1,119 @@ +.\" +.\" CDDL HEADER START +.\" +.\" The contents of this file are subject to the terms of the +.\" Common Development and Distribution License (the "License"). +.\" You may not use this file except in compliance with the License. +.\" +.\" You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +.\" or http://www.opensolaris.org/os/licensing. +.\" See the License for the specific language governing permissions +.\" and limitations under the License. +.\" +.\" When distributing Covered Code, include this CDDL HEADER in each +.\" file and include the License file at usr/src/OPENSOLARIS.LICENSE. +.\" If applicable, add the following below this CDDL HEADER, with the +.\" fields enclosed by brackets "[]" replaced with your own identifying +.\" information: Portions Copyright [yyyy] [name of copyright owner] +.\" +.\" CDDL HEADER END +.\" +.\" +.\" Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. +.\" Copyright 2011 Joshua M. Clulow +.\" Copyright (c) 2011, 2019 by Delphix. All rights reserved. +.\" Copyright (c) 2011, Pawel Jakub Dawidek +.\" Copyright (c) 2012, Glen Barber +.\" Copyright (c) 2012, Bryan Drewery +.\" Copyright (c) 2013, Steven Hartland +.\" Copyright (c) 2013 by Saso Kiselkov. All rights reserved. +.\" Copyright (c) 2014, Joyent, Inc. All rights reserved. +.\" Copyright (c) 2014 by Adam Stevko. All rights reserved. +.\" Copyright (c) 2014 Integros [integros.com] +.\" Copyright (c) 2014, Xin LI +.\" Copyright (c) 2014-2015, The FreeBSD Foundation, All Rights Reserved. +.\" Copyright (c) 2016 Nexenta Systems, Inc. All Rights Reserved. +.\" Copyright 2019 Richard Laager. All rights reserved. +.\" Copyright 2018 Nexenta Systems, Inc. +.\" Copyright 2019 Joyent, Inc. +.\" +.Dd December 9, 2019 +.Dt ZFS-JAIL 8 +.Os FreeBSD +.Sh NAME +.Nm zfs Ns Pf - Cm jail +.Nd Attaches and detaches ZFS filesystems from FreeBSD jails. +.No A Tn ZFS +dataset can be attached to a jail by using the +.Qq Nm Cm jail +subcommand. You cannot attach a dataset to one jail and the children of the +same dataset to another jail. You can also not attach the root file system +of the jail or any dataset which needs to be mounted before the zfs rc script +is run inside the jail, as it would be attached unmounted until it is +mounted from the rc script inside the jail. To allow management of the +dataset from within a jail, the +.Sy jailed +property has to be set and the jail needs access to the +.Pa /dev/zfs +device. The +.Sy quota +property cannot be changed from within a jail. See +.Xr jail 8 +for information on how to allow mounting +.Tn ZFS +datasets from within a jail. +.Pp +.No A Tn ZFS +dataset can be detached from a jail using the +.Qq Nm Cm unjail +subcommand. +.Pp +After a dataset is attached to a jail and the jailed property is set, a jailed +file system cannot be mounted outside the jail, since the jail administrator +might have set the mount point to an unacceptable value. +.Sh SYNOPSIS +.Nm +.Cm jail +.Ar jailid Ns | Ns Ar jailname filesystem +.Nm +.Cm unjail +.Ar jailid Ns | Ns Ar jailname filesystem +.Sh DESCRIPTION +.Bl -tag -width "" +.It Xo +.Nm +.Cm jail +.Ar jailid filesystem +.Xc +.Pp +Attaches the specified +.Ar filesystem +to the jail identified by JID +.Ar jailid . +From now on this file system tree can be managed from within a jail if the +.Sy jailed +property has been set. To use this functuinality, the jail needs the +.Va allow.mount +and +.Va allow.mount.zfs +parameters set to 1 and the +.Va enforce_statfs +parameter set to a value lower than 2. +.Pp +See +.Xr jail 8 +for more information on managing jails and configuring the parameters above. +.It Xo +.Nm +.Cm unjail +.Ar jailid filesystem +.Xc +.Pp +Detaches the specified +.Ar filesystem +from the jail identified by JID +.Ar jailid . +.El +.Sh SEE ALSO +.Xr jail 8 , +.Xr zfsprops 8 diff --git a/man/man8/zfs-unjail.8 b/man/man8/zfs-unjail.8 new file mode 120000 index 000000000..04cc05a00 --- /dev/null +++ b/man/man8/zfs-unjail.8 @@ -0,0 +1 @@ +zfs-jail.8 \ No newline at end of file diff --git a/man/man8/zfs.8 b/man/man8/zfs.8 index 1f100ab9e..62b7f1f8a 100644 --- a/man/man8/zfs.8 +++ b/man/man8/zfs.8 @@ -22,10 +22,17 @@ .\" Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. .\" Copyright 2011 Joshua M. Clulow .\" Copyright (c) 2011, 2019 by Delphix. All rights reserved. +.\" Copyright (c) 2011, Pawel Jakub Dawidek +.\" Copyright (c) 2012, Glen Barber +.\" Copyright (c) 2012, Bryan Drewery +.\" Copyright (c) 2013, Steven Hartland .\" Copyright (c) 2013 by Saso Kiselkov. All rights reserved. .\" Copyright (c) 2014, Joyent, Inc. All rights reserved. .\" Copyright (c) 2014 by Adam Stevko. All rights reserved. .\" Copyright (c) 2014 Integros [integros.com] +.\" Copyright (c) 2014, Xin LI +.\" Copyright (c) 2014-2015, The FreeBSD Foundation, All Rights Reserved. +.\" Copyright (c) 2016 Nexenta Systems, Inc. All Rights Reserved. .\" Copyright 2019 Richard Laager. All rights reserved. .\" Copyright 2018 Nexenta Systems, Inc. .\" Copyright 2019 Joyent, Inc. @@ -267,6 +274,13 @@ Unload a key for the specified dataset, removing the ability to access the datas Execute ZFS administrative operations programmatically via a Lua script-language channel program. .El +.Ss Jails +.Bl -tag -width "" +.It Xr zfs-jail 8 +Attaches a filesystem to a jail. +.It Xr zfs-unjail 8 +Detaches a filesystem from a jail. +.El .Sh EXIT STATUS The .Nm diff --git a/man/man8/zfsprops.8 b/man/man8/zfsprops.8 index 7318361af..da3280a9f 100644 --- a/man/man8/zfsprops.8 +++ b/man/man8/zfsprops.8 @@ -22,10 +22,17 @@ .\" Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. .\" Copyright 2011 Joshua M. Clulow .\" Copyright (c) 2011, 2019 by Delphix. All rights reserved. +.\" Copyright (c) 2011, Pawel Jakub Dawidek +.\" Copyright (c) 2012, Glen Barber +.\" Copyright (c) 2012, Bryan Drewery +.\" Copyright (c) 2013, Steven Hartland .\" Copyright (c) 2013 by Saso Kiselkov. All rights reserved. .\" Copyright (c) 2014, Joyent, Inc. All rights reserved. .\" Copyright (c) 2014 by Adam Stevko. All rights reserved. .\" Copyright (c) 2014 Integros [integros.com] +.\" Copyright (c) 2016 Nexenta Systems, Inc. All Rights Reserved. +.\" Copyright (c) 2014, Xin LI +.\" Copyright (c) 2014-2015, The FreeBSD Foundation, All Rights Reserved. .\" Copyright 2019 Richard Laager. All rights reserved. .\" Copyright 2018 Nexenta Systems, Inc. .\" Copyright 2019 Joyent, Inc. @@ -1709,9 +1716,17 @@ are equivalent to the and .Sy noxattr mount options. +.It Sy jailed Ns = Ns Cm off | on +Controls whether the dataset is managed from a jail. See the +.Qq Sx Jails +section in +.Xr zfs 8 +for more information. Jails are a FreeBSD feature and are not relevant on +other platforms. The default value is +.Cm off . .It Sy zoned Ns = Ns Sy on Ns | Ns Sy off Controls whether the dataset is managed from a non-global zone. Zones are a -Solaris feature and are not relevant on Linux. The default value is +Solaris feature and are not relevant on other platforms. The default value is .Sy off . .El .Pp diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 20f92c6a8..506a9bdfe 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -379,8 +379,13 @@ zfs_prop_init(void) zprop_register_index(ZFS_PROP_READONLY, "readonly", 0, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "on | off", "RDONLY", boolean_table); +#ifdef __FreeBSD__ + zprop_register_index(ZFS_PROP_ZONED, "jailed", 0, PROP_INHERIT, + ZFS_TYPE_FILESYSTEM, "on | off", "JAILED", boolean_table); +#else zprop_register_index(ZFS_PROP_ZONED, "zoned", 0, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off", "ZONED", boolean_table); +#endif zprop_register_index(ZFS_PROP_VSCAN, "vscan", 0, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, "on | off", "VSCAN", boolean_table); zprop_register_index(ZFS_PROP_NBMAND, "nbmand", 0, PROP_INHERIT, diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c index a4c64d63d..909b67630 100644 --- a/module/zfs/dsl_dir.c +++ b/module/zfs/dsl_dir.c @@ -743,6 +743,7 @@ dsl_enforce_ds_ss_limits(dsl_dir_t *dd, zfs_prop_t prop, cred_t *cr) uint64_t obj; dsl_dataset_t *ds; uint64_t zoned; + const char *zonedstr; ASSERT(prop == ZFS_PROP_FILESYSTEM_LIMIT || prop == ZFS_PROP_SNAPSHOT_LIMIT); @@ -763,7 +764,8 @@ dsl_enforce_ds_ss_limits(dsl_dir_t *dd, zfs_prop_t prop, cred_t *cr) if (dsl_dataset_hold_obj(dd->dd_pool, obj, FTAG, &ds) != 0) return (ENFORCE_ALWAYS); - if (dsl_prop_get_ds(ds, "zoned", 8, 1, &zoned, NULL) || zoned) { + zonedstr = zfs_prop_to_name(ZFS_PROP_ZONED); + if (dsl_prop_get_ds(ds, zonedstr, 8, 1, &zoned, NULL) || zoned) { /* Only root can access zoned fs's from the GZ */ enforce = ENFORCE_ALWAYS; } else {