chiark / gitweb /
nspawn: introduce --capability=all for retaining all capabilities
[elogind.git] / src / nspawn / nspawn.c
index 759f9c1aef046729ced5bfdadac8ab3066dda296..0b25334fe9eab93444502fcc07c4d35f5e15c5c8 100644 (file)
@@ -119,6 +119,8 @@ static char **arg_bind_ro = NULL;
 static char **arg_setenv = NULL;
 static bool arg_quiet = false;
 static bool arg_share_system = false;
+static bool arg_register = true;
+static bool arg_keep_unit = false;
 
 static int help(void) {
 
@@ -150,6 +152,9 @@ static int help(void) {
                "                            the container\n"
                "     --bind-ro=PATH[:PATH]  Similar, but creates a read-only bind mount\n"
                "     --setenv=NAME=VALUE    Pass an environment variable to PID 1\n"
+               "     --register=BOOLEAN     Register container as machine\n"
+               "     --keep-unit            Do not register a scope for the machine, reuse\n"
+               "                            the service unit nspawn is running in\n"
                "  -q --quiet                Do not show status information\n",
                program_invocation_short_name);
 
@@ -169,7 +174,9 @@ static int parse_argv(int argc, char *argv[]) {
                 ARG_BIND,
                 ARG_BIND_RO,
                 ARG_SETENV,
-                ARG_SHARE_SYSTEM
+                ARG_SHARE_SYSTEM,
+                ARG_REGISTER,
+                ARG_KEEP_UNIT
         };
 
         static const struct option options[] = {
@@ -193,6 +200,8 @@ static int parse_argv(int argc, char *argv[]) {
                 { "selinux-apifs-context", required_argument, NULL, 'L'                 },
                 { "quiet",                 no_argument,       NULL, 'q'                 },
                 { "share-system",          no_argument,       NULL, ARG_SHARE_SYSTEM    },
+                { "register",              required_argument, NULL, ARG_REGISTER        },
+                { "keep-unit",             no_argument,       NULL, ARG_KEEP_UNIT       },
                 {}
         };
 
@@ -255,17 +264,23 @@ static int parse_argv(int argc, char *argv[]) {
                         break;
 
                 case 'M':
-                        if (!hostname_is_valid(optarg)) {
-                                log_error("Invalid machine name: %s", optarg);
-                                return -EINVAL;
-                        }
+                        if (isempty(optarg)) {
+                                free(arg_machine);
+                                arg_machine = NULL;
+                        } else {
 
-                        free(arg_machine);
-                        arg_machine = strdup(optarg);
-                        if (!arg_machine)
-                                return log_oom();
+                                if (!hostname_is_valid(optarg)) {
+                                        log_error("Invalid machine name: %s", optarg);
+                                        return -EINVAL;
+                                }
 
-                        break;
+                                free(arg_machine);
+                                arg_machine = strdup(optarg);
+                                if (!arg_machine)
+                                        return log_oom();
+
+                                break;
+                        }
 
                 case 'Z':
                         arg_selinux_context = optarg;
@@ -285,25 +300,29 @@ static int parse_argv(int argc, char *argv[]) {
                         size_t length;
 
                         FOREACH_WORD_SEPARATOR(word, length, optarg, ",", state) {
+                                _cleanup_free_ char *t;
                                 cap_value_t cap;
-                                char *t;
 
                                 t = strndup(word, length);
                                 if (!t)
                                         return log_oom();
 
-                                if (cap_from_name(t, &cap) < 0) {
-                                        log_error("Failed to parse capability %s.", t);
-                                        free(t);
-                                        return -EINVAL;
+                                if (streq(t, "all")) {
+                                        if (c == ARG_CAPABILITY)
+                                                arg_retain = (uint64_t) -1;
+                                        else
+                                                arg_retain = 0;
+                                } else {
+                                        if (cap_from_name(t, &cap) < 0) {
+                                                log_error("Failed to parse capability %s.", t);
+                                                return -EINVAL;
+                                        }
+
+                                        if (c == ARG_CAPABILITY)
+                                                arg_retain |= 1ULL << (uint64_t) cap;
+                                        else
+                                                arg_retain &= ~(1ULL << (uint64_t) cap);
                                 }
-
-                                free(t);
-
-                                if (c == ARG_CAPABILITY)
-                                        arg_retain |= 1ULL << (uint64_t) cap;
-                                else
-                                        arg_retain &= ~(1ULL << (uint64_t) cap);
                         }
 
                         break;
@@ -390,6 +409,20 @@ static int parse_argv(int argc, char *argv[]) {
                         arg_share_system = true;
                         break;
 
+                case ARG_REGISTER:
+                        r = parse_boolean(optarg);
+                        if (r < 0) {
+                                log_error("Failed to parse --register= argument: %s", optarg);
+                                return r;
+                        }
+
+                        arg_register = r;
+                        break;
+
+                case ARG_KEEP_UNIT:
+                        arg_keep_unit = true;
+                        break;
+
                 case '?':
                         return -EINVAL;
 
@@ -398,6 +431,19 @@ static int parse_argv(int argc, char *argv[]) {
                 }
         }
 
+        if (arg_share_system)
+                arg_register = false;
+
+        if (arg_boot && arg_share_system) {
+                log_error("--boot and --share-system may not be combined.");
+                return -EINVAL;
+        }
+
+        if (arg_keep_unit && cg_pid_get_owner_uid(0, NULL) >= 0) {
+                log_error("--keep-unit may not be used when invoked from a user session.");
+                return -EINVAL;
+        }
+
         return 1;
 }
 
@@ -636,6 +682,9 @@ static int setup_boot_id(const char *dest) {
 
         assert(dest);
 
+        if (arg_share_system)
+                return 0;
+
         /* Generate a new randomized boot ID, so that each boot-up of
          * the container gets a new one */
 
@@ -861,6 +910,9 @@ static int setup_kmsg(const char *dest, int kmsg_socket) {
 
 static int setup_hostname(void) {
 
+        if (arg_share_system)
+                return 0;
+
         if (sethostname(arg_machine, strlen(arg_machine)) < 0)
                 return -errno;
 
@@ -1043,28 +1095,50 @@ static int register_machine(pid_t pid) {
         _cleanup_bus_unref_ sd_bus *bus = NULL;
         int r;
 
+        if (!arg_register)
+                return 0;
+
         r = sd_bus_default_system(&bus);
         if (r < 0) {
                 log_error("Failed to open system bus: %s", strerror(-r));
                 return r;
         }
 
-        r = sd_bus_call_method(
-                        bus,
-                        "org.freedesktop.machine1",
-                        "/org/freedesktop/machine1",
-                        "org.freedesktop.machine1.Manager",
-                        "CreateMachine",
-                        &error,
-                        NULL,
-                        "sayssusa(sv)",
-                        arg_machine,
-                        SD_BUS_MESSAGE_APPEND_ID128(arg_uuid),
-                        "nspawn",
-                        "container",
-                        (uint32_t) pid,
-                        strempty(arg_directory),
-                        !isempty(arg_slice), "Slice", "s", arg_slice);
+        if (arg_keep_unit) {
+                r = sd_bus_call_method(
+                                bus,
+                                "org.freedesktop.machine1",
+                                "/org/freedesktop/machine1",
+                                "org.freedesktop.machine1.Manager",
+                                "RegisterMachine",
+                                &error,
+                                NULL,
+                                "sayssus",
+                                arg_machine,
+                                SD_BUS_MESSAGE_APPEND_ID128(arg_uuid),
+                                "nspawn",
+                                "container",
+                                (uint32_t) pid,
+                                strempty(arg_directory));
+        } else {
+                r = sd_bus_call_method(
+                                bus,
+                                "org.freedesktop.machine1",
+                                "/org/freedesktop/machine1",
+                                "org.freedesktop.machine1.Manager",
+                                "CreateMachine",
+                                &error,
+                                NULL,
+                                "sayssusa(sv)",
+                                arg_machine,
+                                SD_BUS_MESSAGE_APPEND_ID128(arg_uuid),
+                                "nspawn",
+                                "container",
+                                (uint32_t) pid,
+                                strempty(arg_directory),
+                                !isempty(arg_slice), "Slice", "s", arg_slice);
+        }
+
         if (r < 0) {
                 log_error("Failed to register machine: %s", bus_error_message(&error, r));
                 return r;
@@ -1080,6 +1154,9 @@ static int terminate_machine(pid_t pid) {
         const char *path;
         int r;
 
+        if (!arg_register)
+                return 0;
+
         r = sd_bus_default_system(&bus);
         if (r < 0) {
                 log_error("Failed to open system bus: %s", strerror(-r));
@@ -1125,15 +1202,37 @@ static int terminate_machine(pid_t pid) {
         return 0;
 }
 
-static bool audit_enabled(void) {
-        int fd;
+static int reset_audit_loginuid(void) {
+        _cleanup_free_ char *p = NULL;
+        int r;
+
+        if (arg_share_system)
+                return 0;
+
+        r = read_one_line_file("/proc/self/loginuid", &p);
+        if (r == -EEXIST)
+                return 0;
+        if (r < 0) {
+                log_error("Failed to read /proc/self/loginuid: %s", strerror(-r));
+                return r;
+        }
+
+        /* Already reset? */
+        if (streq(p, "4294967295"))
+                return 0;
+
+        r = write_string_file("/proc/self/loginuid", "4294967295");
+        if (r < 0) {
+                log_error("Failed to reset audit login UID. This probably means that your kernel is too\n"
+                          "old and you have audit enabled. Note that the auditing subsystem is known to\n"
+                          "be incompatible with containers on old kernels. Please make sure to upgrade\n"
+                          "your kernel or to off auditing with 'audit=0' on the kernel command line before\n"
+                          "using systemd-nspawn. Sleeping for 5s... (%s)\n", strerror(-r));
 
-        fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
-        if (fd >= 0) {
-                close_nointr_nofail(fd);
-                return true;
+                sleep(5);
         }
-        return false;
+
+        return 0;
 }
 
 int main(int argc, char *argv[]) {
@@ -1146,7 +1245,6 @@ int main(int argc, char *argv[]) {
         _cleanup_close_pipe_ int kmsg_socket_pair[2] = { -1, -1 };
         _cleanup_fdset_free_ FDSet *fds = NULL;
         _cleanup_free_ char *kdbus_domain = NULL;
-        const char *ns;
 
         log_parse_environment();
         log_open();
@@ -1199,13 +1297,6 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
-        if (arg_boot && audit_enabled()) {
-                log_warning("The kernel auditing subsystem is known to be incompatible with containers.\n"
-                            "Please make sure to turn off auditing with 'audit=0' on the kernel command\n"
-                            "line before using systemd-nspawn. Sleeping for 5s...\n");
-                sleep(5);
-        }
-
         if (path_equal(arg_directory, "/")) {
                 log_error("Spawning container on root directory not supported.");
                 goto finish;
@@ -1248,12 +1339,26 @@ int main(int argc, char *argv[]) {
                 goto finish;
         }
 
-        ns = strappenda("machine-", arg_machine);
-        kdbus_fd = bus_kernel_create_domain(ns, &kdbus_domain);
-        if (r < 0)
-                log_debug("Failed to create kdbus domain: %s", strerror(-r));
-        else
-                log_debug("Successfully created kdbus domain as %s", kdbus_domain);
+
+        if (access("/dev/kdbus/control", F_OK) >= 0) {
+
+                if (arg_share_system) {
+                        kdbus_domain = strdup("/dev/kdbus");
+                        if (!kdbus_domain) {
+                                log_oom();
+                                goto finish;
+                        }
+                } else {
+                        const char *ns;
+
+                        ns = strappenda("machine-", arg_machine);
+                        kdbus_fd = bus_kernel_create_domain(ns, &kdbus_domain);
+                        if (r < 0)
+                                log_debug("Failed to create kdbus domain: %s", strerror(-r));
+                        else
+                                log_debug("Successfully created kdbus domain as %s", kdbus_domain);
+                }
+        }
 
         if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_NONBLOCK|SOCK_CLOEXEC, 0, kmsg_socket_pair) < 0) {
                 log_error("Failed to create kmsg socket pair: %m");
@@ -1350,6 +1455,9 @@ int main(int argc, char *argv[]) {
                                 goto child_fail;
                         }
 
+                        if (reset_audit_loginuid() < 0)
+                                goto child_fail;
+
                         if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) {
                                 log_error("PR_SET_PDEATHSIG failed: %m");
                                 goto child_fail;
@@ -1438,7 +1546,8 @@ int main(int argc, char *argv[]) {
 
                         umask(0022);
 
-                        loopback_setup();
+                        if (arg_private_network)
+                                loopback_setup();
 
                         if (drop_capabilities() < 0) {
                                 log_error("drop_capabilities() failed: %m");
@@ -1644,7 +1753,7 @@ int main(int argc, char *argv[]) {
                 } else if (status.si_code == CLD_KILLED ||
                            status.si_code == CLD_DUMPED) {
 
-                        log_error("Container %s terminated by signal %s.", arg_machine,  signal_to_string(status.si_status));
+                        log_error("Container %s terminated by signal %s.", arg_machine, signal_to_string(status.si_status));
                         r = EXIT_FAILURE;
                         break;
                 } else {