/* SPDX-License-Identifier: LGPL-2.1+ */
#if HAVE_BLKID
-#include <blkid.h>
#endif
#include <errno.h>
#include <getopt.h>
-#include <grp.h>
#include <linux/fs.h>
#include <linux/loop.h>
-#include <pwd.h>
-#include <sched.h>
#if HAVE_SELINUX
#include <selinux/selinux.h>
#endif
-#include <signal.h>
-#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/file.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include "terminal-util.h"
#include "tmpfile-util.h"
#include "umask-util.h"
+#include "unit-name.h"
#include "user-util.h"
#include "util.h"
return log_oom();
printf("%1$s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n"
- "Spawn a command or OS in a light-weight container.\n\n"
+ "%5$sSpawn a command or OS in a light-weight container.%6$s\n\n"
" -h --help Show this help\n"
" --version Print version string\n"
" -q --quiet Do not show status information\n"
"\nSee the %2$s for details.\n"
, program_invocation_short_name
, link
- , ansi_underline(), ansi_normal());
+ , ansi_underline(), ansi_normal()
+ , ansi_highlight(), ansi_normal()
+ );
return 0;
}
e = getenv(var);
if (!e) {
- static bool warned = false;
-
+ /* $UNIFIED_CGROUP_HIERARCHY has been renamed to $SYSTEMD_NSPAWN_UNIFIED_HIERARCHY. */
var = "UNIFIED_CGROUP_HIERARCHY";
e = getenv(var);
- if (e && !warned) {
- log_info("$UNIFIED_CGROUP_HIERARCHY has been renamed to $SYSTEMD_NSPAWN_UNIFIED_HIERARCHY.");
- warned = true;
- }
}
if (!isempty(e)) {
return 0;
}
+static int parse_capability_spec(const char *spec, uint64_t *ret_mask) {
+ uint64_t mask = 0;
+ int r;
+
+ for (;;) {
+ _cleanup_free_ char *t = NULL;
+
+ r = extract_first_word(&spec, &t, ",", 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse capability %s.", t);
+ if (r == 0)
+ break;
+
+ if (streq(t, "help")) {
+ for (int i = 0; i < capability_list_length(); i++) {
+ const char *name;
+
+ name = capability_to_name(i);
+ if (name)
+ puts(name);
+ }
+
+ return 0; /* quit */
+ }
+
+ if (streq(t, "all"))
+ mask = (uint64_t) -1;
+ else {
+ r = capability_from_name(t);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse capability %s.", t);
+
+ mask |= 1ULL << r;
+ }
+ }
+
+ *ret_mask = mask;
+ return 1; /* continue */
+}
+
static int parse_share_ns_env(const char *name, unsigned long ns_flag) {
int r;
};
int c, r;
- const char *p;
uint64_t plus = 0, minus = 0;
bool mask_all_settings = false, mask_no_settings = false;
arg_settings_mask |= SETTING_MACHINE_ID;
break;
- case 'S':
- r = free_and_strdup(&arg_slice, optarg);
+ case 'S': {
+ _cleanup_free_ char *mangled = NULL;
+
+ r = unit_name_mangle_with_suffix(optarg, NULL, UNIT_NAME_MANGLE_WARN, ".slice", &mangled);
if (r < 0)
return log_oom();
+ free_and_replace(arg_slice, mangled);
arg_settings_mask |= SETTING_SLICE;
break;
+ }
case 'M':
if (isempty(optarg))
case ARG_CAPABILITY:
case ARG_DROP_CAPABILITY: {
- p = optarg;
- for (;;) {
- _cleanup_free_ char *t = NULL;
-
- r = extract_first_word(&p, &t, ",", 0);
- if (r < 0)
- return log_error_errno(r, "Failed to parse capability %s.", t);
- if (r == 0)
- break;
-
- if (streq(t, "all")) {
- if (c == ARG_CAPABILITY)
- plus = (uint64_t) -1;
- else
- minus = (uint64_t) -1;
- } else {
- r = capability_from_name(t);
- if (r < 0)
- return log_error_errno(r, "Failed to parse capability %s.", t);
-
- if (c == ARG_CAPABILITY)
- plus |= 1ULL << r;
- else
- minus |= 1ULL << r;
- }
- }
+ uint64_t m;
+ r = parse_capability_spec(optarg, &m);
+ if (r <= 0)
+ return r;
+ if (c == ARG_CAPABILITY)
+ plus |= m;
+ else
+ minus |= m;
arg_settings_mask |= SETTING_CAPABILITY;
break;
}
-
case ARG_NO_NEW_PRIVILEGES:
r = parse_boolean(optarg);
if (r < 0)
/* If we're not using OCI, proceed with mangled capabilities (so we don't error out)
* in order to maintain the same behavior as systemd < 242. */
if (capability_quintet_mangle(&q))
- log_warning("Some capabilities will not be set because they are not in the current bounding set.");
+ log_full(arg_quiet ? LOG_DEBUG : LOG_WARNING,
+ "Some capabilities will not be set because they are not in the current bounding set.");
}
"/",
arg_custom_mounts,
arg_n_custom_mounts,
- false,
- 0,
0,
arg_selinux_apifs_context,
- true);
+ MOUNT_NON_ROOT_ONLY | MOUNT_IN_USERNS);
if (r < 0)
return r;
return r;
directory = "/run/systemd/nspawn-root";
-
- } else if (!dissected_image) {
- /* Turn directory into bind mount (we need that so that we can move the bind mount to root
- * later on). */
- r = mount_verbose(LOG_ERR, directory, directory, NULL, MS_BIND|MS_REC, NULL);
- if (r < 0)
- return r;
}
r = setup_pivot_root(
r = setup_volatile_mode(
directory,
arg_volatile_mode,
- arg_userns_mode != USER_NAMESPACE_NO,
arg_uid_shift,
- arg_uid_range,
arg_selinux_apifs_context);
if (r < 0)
return r;
+ r = mount_custom(
+ directory,
+ arg_custom_mounts,
+ arg_n_custom_mounts,
+ arg_uid_shift,
+ arg_selinux_apifs_context,
+ MOUNT_ROOT_ONLY);
+ if (r < 0)
+ return r;
+
+ /* Make sure we always have a mount that we can move to root later on. */
+ if (!path_is_mount_point(directory, NULL, 0)) {
+ r = mount_verbose(LOG_ERR, directory, directory, NULL, MS_BIND|MS_REC, NULL);
+ if (r < 0)
+ return r;
+ }
+
if (dissected_image) {
/* Now we know the uid shift, let's now mount everything else that might be in the image. */
r = dissected_image_mount(dissected_image, directory, arg_uid_shift,
* inside the container that create a new mount namespace.
* See https://github.com/systemd/systemd/issues/3860
* Further submounts (such as /dev) done after this will inherit the
- * shared propagation mode. */
+ * shared propagation mode.
+ *
+ * IMPORTANT: Do not overmount the root directory anymore from now on to
+ * enable moving the root directory mount to root later on.
+ * https://github.com/systemd/systemd/issues/3847#issuecomment-562735251
+ */
r = mount_verbose(LOG_ERR, NULL, directory, NULL, MS_SHARED|MS_REC, NULL);
if (r < 0)
return r;
directory,
arg_custom_mounts,
arg_n_custom_mounts,
- arg_userns_mode != USER_NAMESPACE_NO,
arg_uid_shift,
- arg_uid_range,
arg_selinux_apifs_context,
- false);
+ MOUNT_NON_ROOT_ONLY);
if (r < 0)
return r;
if ((arg_settings_mask & SETTING_CAPABILITY) == 0) {
uint64_t plus, minus;
+ uint64_t network_minus = 0;
/* Note that we copy both the simple plus/minus caps here, and the full quintet from the
* Settings structure */
if (settings_private_network(settings))
plus |= UINT64_C(1) << CAP_NET_ADMIN;
else
- minus |= UINT64_C(1) << CAP_NET_ADMIN;
+ network_minus |= UINT64_C(1) << CAP_NET_ADMIN;
}
if (!arg_settings_trusted && plus != 0) {
if (settings->capability != 0)
log_warning("Ignoring Capability= setting, file %s is not trusted.", path);
- } else
+ } else {
+ arg_caps_retain &= ~network_minus;
arg_caps_retain |= plus;
+ }
arg_caps_retain &= ~minus;
goto finish;
}
- r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, &loop);
+ r = loop_device_make_by_path(arg_image, arg_read_only ? O_RDONLY : O_RDWR, LO_FLAGS_PARTSCAN, &loop);
if (r < 0) {
log_error_errno(r, "Failed to set up loopback block device: %m");
goto finish;