.timezone = _TIMEZONE_MODE_INVALID,
.userns_mode = _USER_NAMESPACE_MODE_INVALID,
- .userns_chown = -1,
+ .userns_ownership = _USER_NAMESPACE_OWNERSHIP_INVALID,
.uid_shift = UID_INVALID,
.uid_range = UID_INVALID,
/* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either
* both fields shall be initialized or neither. */
- if (s->userns_mode == USER_NAMESPACE_PICK)
- s->userns_chown = true;
- else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0)
- s->userns_chown = false;
-
- if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
+ if (s->userns_mode >= 0 && s->userns_ownership < 0)
+ s->userns_ownership = s->userns_mode == USER_NAMESPACE_PICK ? USER_NAMESPACE_OWNERSHIP_CHOWN : USER_NAMESPACE_OWNERSHIP_OFF;
+ if (s->userns_ownership >= 0 && s->userns_mode < 0)
s->userns_mode = USER_NAMESPACE_NO;
*ret = TAKE_PTR(s);
};
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO);
+
+DEFINE_CONFIG_PARSE_ENUM(config_parse_userns_ownership, user_namespace_ownership, UserNamespaceOwnership, "Failed to parse user namespace ownership mode");
+
+static const char *const user_namespace_ownership_table[_USER_NAMESPACE_OWNERSHIP_MAX] = {
+ [USER_NAMESPACE_OWNERSHIP_OFF] = "off",
+ [USER_NAMESPACE_OWNERSHIP_CHOWN] = "chown",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(user_namespace_ownership, UserNamespaceOwnership);
+
+int config_parse_userns_chown(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ UserNamespaceOwnership *ownership = data;
+ int r;
+
+ assert(rvalue);
+ assert(ownership);
+
+ /* Compatibility support for UserNamespaceChown=, whose job has been taken over by UserNamespaceOwnership= */
+
+ r = parse_boolean(rvalue);
+ if (r < 0) {
+ log_syntax(unit, LOG_WARNING, filename, line, r, "Failed to parse user namespace ownership mode, ignoring: %s", rvalue);
+ return 0;
+ }
+
+ *ownership = r ? USER_NAMESPACE_OWNERSHIP_CHOWN : USER_NAMESPACE_OWNERSHIP_OFF;
+ return 0;
+}
_USER_NAMESPACE_MODE_INVALID = -EINVAL,
} UserNamespaceMode;
+typedef enum UserNamespaceOwnership {
+ USER_NAMESPACE_OWNERSHIP_OFF,
+ USER_NAMESPACE_OWNERSHIP_CHOWN,
+ _USER_NAMESPACE_OWNERSHIP_MAX,
+ _USER_NAMESPACE_OWNERSHIP_INVALID = -1,
+} UserNamespaceOwnership;
+
typedef enum ResolvConfMode {
RESOLV_CONF_OFF,
RESOLV_CONF_COPY_HOST, /* /etc/resolv.conf */
VolatileMode volatile_mode;
CustomMount *custom_mounts;
size_t n_custom_mounts;
- int userns_chown;
+ UserNamespaceOwnership userns_ownership;
/* [Network] */
int private_network;
CONFIG_PARSER_PROTOTYPE(config_parse_resolv_conf);
CONFIG_PARSER_PROTOTYPE(config_parse_link_journal);
CONFIG_PARSER_PROTOTYPE(config_parse_timezone);
+CONFIG_PARSER_PROTOTYPE(config_parse_userns_chown);
+CONFIG_PARSER_PROTOTYPE(config_parse_userns_ownership);
const char *resolv_conf_mode_to_string(ResolvConfMode a) _const_;
ResolvConfMode resolv_conf_mode_from_string(const char *s) _pure_;
const char *timezone_mode_to_string(TimezoneMode a) _const_;
TimezoneMode timezone_mode_from_string(const char *s) _pure_;
+const char *user_namespace_ownership_to_string(UserNamespaceOwnership a) _const_;
+UserNamespaceOwnership user_namespace_ownership_from_string(const char *s) _pure_;
+
int parse_link_journal(const char *s, LinkJournal *ret_mode, bool *ret_try);
void device_node_array_free(DeviceNode *node, size_t n);
static sd_bus_message *arg_property_message = NULL;
static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO;
static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U;
-static bool arg_userns_chown = false;
+static UserNamespaceOwnership arg_userns_ownership = _USER_NAMESPACE_OWNERSHIP_INVALID;
static int arg_kill_signal = 0;
static CGroupUnified arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_UNKNOWN;
static SettingsMask arg_settings_mask = 0;
" -U --private-users=pick Run within user namespace, autoselect UID/GID range\n"
" --private-users[=UIDBASE[:NUIDS]]\n"
" Similar, but with user configured UID/GID range\n"
- " --private-users-chown Adjust OS tree ownership to private UID/GID range\n\n"
+ " --private-users-ownership=MODE\n"
+ " Adjust ('chown') or map ('map') OS tree ownership\n"
+ " to private UID/GID range\n\n"
"%3$sNetworking:%4$s\n"
" --private-network Disable network in container\n"
" --network-interface=INTERFACE\n"
CustomMount *m = &arg_custom_mounts[i];
if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) {
- if (arg_userns_chown)
+ if (arg_userns_ownership != USER_NAMESPACE_OWNERSHIP_OFF)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "--private-users-chown may not be combined with custom root mounts.");
- else if (arg_uid_shift == UID_INVALID)
+ "--private-users-ownership=own not be combined with custom root mounts.");
+ if (arg_uid_shift == UID_INVALID)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--private-users with automatic UID shift may not be combined with custom root mounts.");
}
ARG_CHDIR,
ARG_PIVOT_ROOT,
ARG_PRIVATE_USERS_CHOWN,
+ ARG_PRIVATE_USERS_OWNERSHIP,
ARG_NOTIFY_READY,
ARG_ROOT_HASH,
ARG_ROOT_HASH_SIG,
{ "port", required_argument, NULL, 'p' },
{ "property", required_argument, NULL, ARG_PROPERTY },
{ "private-users", optional_argument, NULL, ARG_PRIVATE_USERS },
- { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN },
+ { "private-users-chown", optional_argument, NULL, ARG_PRIVATE_USERS_CHOWN }, /* obsolete */
+ { "private-users-ownership",required_argument, NULL, ARG_PRIVATE_USERS_OWNERSHIP},
{ "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
{ "settings", required_argument, NULL, ARG_SETTINGS },
{ "chdir", required_argument, NULL, ARG_CHDIR },
arg_uid_range = UINT32_C(0x10000);
} else if (streq(optarg, "pick")) {
/* pick: User namespacing on, UID range is picked randomly */
- arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_chown = true,
- * is implied by USER_NAMESPACE_PICK,
+ arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_ownership is
+ * implied by USER_NAMESPACE_PICK
* further down. */
arg_uid_shift = UID_INVALID;
arg_uid_range = UINT32_C(0x10000);
case 'U':
if (userns_supported()) {
- arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_chown = true,
- * is implied by USER_NAMESPACE_PICK,
+ arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_ownership is
+ * implied by USER_NAMESPACE_PICK
* further down. */
arg_uid_shift = UID_INVALID;
arg_uid_range = UINT32_C(0x10000);
break;
case ARG_PRIVATE_USERS_CHOWN:
- arg_userns_chown = true;
+ arg_userns_ownership = USER_NAMESPACE_OWNERSHIP_CHOWN;
+
+ arg_settings_mask |= SETTING_USERNS;
+ break;
+
+ case ARG_PRIVATE_USERS_OWNERSHIP:
+ if (streq(optarg, "help")) {
+ DUMP_STRING_TABLE(user_namespace_ownership, UserNamespaceOwnership, _USER_NAMESPACE_OWNERSHIP_MAX);
+ return 0;
+ }
+
+ arg_userns_ownership = user_namespace_ownership_from_string(optarg);
+ if (arg_userns_ownership < 0)
+ return log_error_errno(arg_userns_ownership, "Cannot parse --user-namespace-ownership= value: %s", optarg);
arg_settings_mask |= SETTING_USERNS;
break;
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--boot cannot be used without namespacing.");
}
- if (arg_userns_mode == USER_NAMESPACE_PICK)
- arg_userns_chown = true;
+ if (arg_userns_ownership < 0)
+ arg_userns_ownership =
+ arg_userns_mode == USER_NAMESPACE_PICK ? USER_NAMESPACE_OWNERSHIP_CHOWN :
+ USER_NAMESPACE_OWNERSHIP_OFF;
if (arg_start_mode == START_BOOT && arg_kill_signal <= 0)
arg_kill_signal = SIGRTMIN+3;
if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported())
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--private-users= is not supported, kernel compiled without user namespace support.");
- if (arg_userns_chown && arg_read_only)
+ if (arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_CHOWN && arg_read_only)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "--read-only and --private-users-chown may not be combined.");
+ "--read-only and --private-users-ownership=chown may not be combined.");
- /* We don't support --private-users-chown together with any of the volatile modes since we couldn't
- * change the read-only part of the tree (i.e. /usr) anyway, or because it would trigger a massive
- * copy-up (in case of overlay) making the entire exercise pointless. */
- if (arg_userns_chown && arg_volatile_mode != VOLATILE_NO)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--volatile= and --private-users-chown may not be combined.");
+ /* We don't support --private-users-ownership=chown together with any of the volatile modes since we
+ * couldn't change the read-only part of the tree (i.e. /usr) anyway, or because it would trigger a
+ * massive copy-up (in case of overlay) making the entire exercise pointless. */
+ if (arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_CHOWN && arg_volatile_mode != VOLATILE_NO)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--volatile= and --private-users-ownership=chown may not be combined.");
/* If --network-namespace-path is given with any other network-related option (except --private-network),
* we need to error out, to avoid conflicts between different network options. */
assert(directory);
- if (arg_userns_mode == USER_NAMESPACE_NO || !arg_userns_chown)
+ if (arg_userns_mode == USER_NAMESPACE_NO || arg_userns_ownership != USER_NAMESPACE_OWNERSHIP_CHOWN)
return 0;
r = path_patch_uid(directory, arg_uid_shift, arg_uid_range);
arg_userns_mode = settings->userns_mode;
arg_uid_shift = settings->uid_shift;
arg_uid_range = settings->uid_range;
- arg_userns_chown = settings->userns_chown;
+ arg_userns_ownership = settings->userns_ownership;
}
}