Add Linux namespace delegation support

This allows ZFS datasets to be delegated to a user/mount namespace
Within that namespace, only the delegated datasets are visible
Works very similarly to Zones/Jailes on other ZFS OSes

As a user:
```
 $ unshare -Um
 $ zfs list
no datasets available
 $ echo $$
1234
```

As root:
```
 # zfs list
NAME                            ZONED  MOUNTPOINT
containers                      off    /containers
containers/host                 off    /containers/host
containers/host/child           off    /containers/host/child
containers/host/child/gchild    off    /containers/host/child/gchild
containers/unpriv               on     /unpriv
containers/unpriv/child         on     /unpriv/child
containers/unpriv/child/gchild  on     /unpriv/child/gchild

 # zfs zone /proc/1234/ns/user containers/unpriv
```

Back to the user namespace:
```
 $ zfs list
NAME                             USED  AVAIL     REFER  MOUNTPOINT
containers                       129M  47.8G       24K  /containers
containers/unpriv                128M  47.8G       24K  /unpriv
containers/unpriv/child          128M  47.8G      128M  /unpriv/child
```

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Will Andrews <will.andrews@klarasystems.com>
Signed-off-by: Allan Jude <allan@klarasystems.com>
Signed-off-by: Mateusz Piotrowski <mateusz.piotrowski@klarasystems.com>
Co-authored-by: Allan Jude <allan@klarasystems.com>
Co-authored-by: Mateusz Piotrowski <mateusz.piotrowski@klarasystems.com>
Sponsored-by: Buddy <https://buddy.works>
Closes #12263
This commit is contained in:
Will Andrews
2021-02-21 10:19:43 -06:00
committed by Brian Behlendorf
parent a1aa8f14c8
commit 4ed5e25074
33 changed files with 1166 additions and 15 deletions
+1 -1
View File
@@ -44,7 +44,7 @@
#include <inttypes.h>
#endif /* HAVE_INTTYPES */
typedef int zoneid_t;
typedef uint_t zoneid_t;
typedef int projid_t;
/*
+11 -1
View File
@@ -33,7 +33,17 @@
extern "C" {
#endif
#define GLOBAL_ZONEID 0
#ifdef __FreeBSD__
#define GLOBAL_ZONEID 0
#else
/*
* Hardcoded in the kernel's root user namespace. A "better" way to get
* this would be by using ioctl_ns(2), but this would need to be performed
* recursively on NS_GET_PARENT and then NS_GET_USERNS. Also, that's only
* supported since Linux 4.9.
*/
#define GLOBAL_ZONEID 4026531837U
#endif
extern zoneid_t getzoneid(void);
+31 -1
View File
@@ -23,10 +23,40 @@
* Use is subject to license terms.
*/
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <zone.h>
zoneid_t
getzoneid(void)
{
return (GLOBAL_ZONEID);
char path[PATH_MAX];
char buf[128] = { '\0' };
char *cp;
int c = snprintf(path, sizeof (path), "/proc/self/ns/user");
/* This API doesn't have any error checking... */
if (c < 0)
return (0);
ssize_t r = readlink(path, buf, sizeof (buf) - 1);
if (r < 0)
return (0);
cp = strchr(buf, '[');
if (cp == NULL)
return (0);
cp++;
unsigned long n = strtoul(cp, NULL, 10);
if (n == ULONG_MAX && errno == ERANGE)
return (0);
zoneid_t z = (zoneid_t)n;
return (z);
}
+1 -1
View File
@@ -1081,7 +1081,7 @@
</function-decl>
</abi-instr>
<abi-instr address-size='64' path='os/linux/zone.c' language='LANG_C99'>
<typedef-decl name='zoneid_t' type-id='95e97e5e' id='4da03624'/>
<typedef-decl name='zoneid_t' type-id='3502e3ff' id='4da03624'/>
<function-decl name='getzoneid' mangled-name='getzoneid' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='getzoneid'>
<return type-id='4da03624'/>
</function-decl>
+8 -1
View File
@@ -433,6 +433,7 @@
<elf-symbol name='zfs_unmountall' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_unshare' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_unshareall' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_userns' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_userspace' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_valid_proplist' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='zfs_version_kernel' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
@@ -1537,7 +1538,7 @@
</function-decl>
</abi-instr>
<abi-instr address-size='64' path='lib/libspl/os/linux/zone.c' language='LANG_C99'>
<typedef-decl name='zoneid_t' type-id='95e97e5e' id='4da03624'/>
<typedef-decl name='zoneid_t' type-id='3502e3ff' id='4da03624'/>
<function-decl name='getzoneid' mangled-name='getzoneid' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='getzoneid'>
<return type-id='4da03624'/>
</function-decl>
@@ -4414,6 +4415,12 @@
<function-decl name='zfs_version_kernel' mangled-name='zfs_version_kernel' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zfs_version_kernel'>
<return type-id='26a90f95'/>
</function-decl>
<function-decl name='zfs_userns' mangled-name='zfs_userns' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zfs_userns'>
<parameter type-id='9200a744' name='zhp'/>
<parameter type-id='80f4b756' name='nspath'/>
<parameter type-id='95e97e5e' name='attach'/>
<return type-id='95e97e5e'/>
</function-decl>
</abi-instr>
<abi-instr address-size='64' path='lib/libzutil/os/linux/zutil_device_path_os.c' language='LANG_C99'>
<function-decl name='zfs_append_partition' mangled-name='zfs_append_partition' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='zfs_append_partition'>
+6
View File
@@ -299,6 +299,9 @@ libzfs_error_description(libzfs_handle_t *hdl)
case EZFS_VDEV_NOTSUP:
return (dgettext(TEXT_DOMAIN, "operation not supported "
"on this type of vdev"));
case EZFS_NOT_USER_NAMESPACE:
return (dgettext(TEXT_DOMAIN, "the provided file "
"was not a user namespace file"));
case EZFS_UNKNOWN:
return (dgettext(TEXT_DOMAIN, "unknown error"));
default:
@@ -485,6 +488,9 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
case ZFS_ERR_BADPROP:
zfs_verror(hdl, EZFS_BADPROP, fmt, ap);
break;
case ZFS_ERR_NOT_USER_NAMESPACE:
zfs_verror(hdl, EZFS_NOT_USER_NAMESPACE, fmt, ap);
break;
default:
zfs_error_aux(hdl, "%s", strerror(error));
zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
+69
View File
@@ -19,6 +19,9 @@
* CDDL HEADER END
*/
/*
* Copyright (c) 2021 Klara, Inc.
*/
#include <alloca.h>
#include <errno.h>
@@ -207,3 +210,69 @@ zfs_version_kernel(void)
ret[read - 1] = '\0';
return (ret);
}
/*
* Add or delete the given filesystem to/from the given user namespace.
*/
int
zfs_userns(zfs_handle_t *zhp, const char *nspath, int attach)
{
libzfs_handle_t *hdl = zhp->zfs_hdl;
zfs_cmd_t zc = {"\0"};
char errbuf[1024];
unsigned long cmd;
int ret;
if (attach) {
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot add '%s' to namespace"),
zhp->zfs_name);
} else {
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot remove '%s' from namespace"),
zhp->zfs_name);
}
switch (zhp->zfs_type) {
case ZFS_TYPE_VOLUME:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"volumes can not be namespaced"));
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
case ZFS_TYPE_SNAPSHOT:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"snapshots can not be namespaced"));
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
case ZFS_TYPE_BOOKMARK:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"bookmarks can not be namespaced"));
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
case ZFS_TYPE_VDEV:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"vdevs can not be namespaced"));
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
case ZFS_TYPE_INVALID:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"invalid zfs_type_t: ZFS_TYPE_INVALID"));
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
case ZFS_TYPE_POOL:
zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
"pools can not be namespaced"));
return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
case ZFS_TYPE_FILESYSTEM:
zfs_fallthrough;
}
assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
zc.zc_objset_type = DMU_OST_ZFS;
zc.zc_cleanup_fd = open(nspath, O_RDONLY);
if (zc.zc_cleanup_fd < 0) {
return (zfs_error(hdl, EZFS_NOT_USER_NAMESPACE, errbuf));
}
cmd = attach ? ZFS_IOC_USERNS_ATTACH : ZFS_IOC_USERNS_DETACH;
if ((ret = zfs_ioctl(hdl, cmd, &zc)) != 0)
zfs_standard_error(hdl, errno, errbuf);
return (ret);
}
+1 -1
View File
@@ -939,7 +939,7 @@
</function-decl>
</abi-instr>
<abi-instr address-size='64' path='os/linux/zone.c' language='LANG_C99'>
<typedef-decl name='zoneid_t' type-id='95e97e5e' id='4da03624'/>
<typedef-decl name='zoneid_t' type-id='3502e3ff' id='4da03624'/>
<function-decl name='getzoneid' mangled-name='getzoneid' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='getzoneid'>
<return type-id='4da03624'/>
</function-decl>