Add an option to allow calling unshare() just before starting guest execution. The option allows unsharing one or more of the mount namespace, the network namespace, and the IPC namespace. This is useful to restrict the ability of QEMU to cause damage to the system should it be compromised.
An example of using this would be to have QEMU open a QMP socket at startup and unshare the network namespace. The instance of QEMU could still be controlled by the QMP socket since that belongs in the original namespace, but if QEMU were compromised it wouldn't be able to open any new connections, even to other processes on the same machine. Signed-off-by: Ross Lagerwall <ross.lagerw...@citrix.com> --- os-posix.c | 34 ++++++++++++++++++++++++++++++++++ qemu-options.hx | 14 ++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/os-posix.c b/os-posix.c index b9c2343..cfc5c38 100644 --- a/os-posix.c +++ b/os-posix.c @@ -45,6 +45,7 @@ static struct passwd *user_pwd; static const char *chroot_dir; static int daemonize; static int daemon_pipe; +static int unshare_flags; void os_setup_early_signal_handling(void) { @@ -160,6 +161,28 @@ void os_parse_cmd_args(int index, const char *optarg) fips_set_state(true); break; #endif +#ifdef CONFIG_SETNS + case QEMU_OPTION_unshare: + { + char *flag; + char *opts = g_strdup(optarg); + + while ((flag = qemu_strsep(&opts, ",")) != NULL) { + if (!strcmp(flag, "mount")) { + unshare_flags |= CLONE_NEWNS; + } else if (!strcmp(flag, "net")) { + unshare_flags |= CLONE_NEWNET; + } else if (!strcmp(flag, "ipc")) { + unshare_flags |= CLONE_NEWIPC; + } else { + fprintf(stderr, "Unknown unshare option: %s\n", flag); + exit(1); + } + } + g_free(opts); + } + break; +#endif } } @@ -201,6 +224,16 @@ static void change_root(void) } +static void unshare_namespaces(void) +{ + if (unshare_flags) { + if (unshare(unshare_flags) < 0) { + perror("could not unshare"); + exit(1); + } + } +} + void os_daemonize(void) { if (daemonize) { @@ -266,6 +299,7 @@ void os_setup_post(void) } change_root(); + unshare_namespaces(); change_process_uid(); if (daemonize) { diff --git a/qemu-options.hx b/qemu-options.hx index 3728e9b..5cfcc51 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3972,6 +3972,20 @@ Immediately before starting guest execution, chroot to the specified directory. Especially useful in combination with -runas. ETEXI +#ifdef CONFIG_SETNS +DEF("unshare", HAS_ARG, QEMU_OPTION_unshare, \ + "-unshare [mount][,net][,ipc]\n" \ + " unshare namespaces just before starting the VM\n", + QEMU_ARCH_ALL) +#endif +STEXI +@item -unshare @code{[mount][,net][,ipc]} +@findex -unshare +Immediately before starting guest execution, unshare the specified namespaces. +The namespaces that can be unshared are the mount namespace, the network +namespace and the IPC namespace. +ETEXI + #ifndef _WIN32 DEF("runas", HAS_ARG, QEMU_OPTION_runas, \ "-runas user change to user id user just before starting the VM\n", -- 2.9.5