1
0
mirror of https://github.com/systemd/systemd.git synced 2024-12-26 03:22:00 +03:00

nspawn: replace boolean --private-user-chown by enum

This replaces --private-user-chown by an enum value
--private-user-ownership=off|chown. Changes otherwise very little.

This is mostly preparation for a follow-up commit adding a new "map"
mode, using kernel 5.12 UID mapping mounts.

Note that this does alter codeflow a bit: the new enum already knows
three different values instead of the old true/false pair. Besides "off"
and "chown" it knows -EINVAL, i.e. whenever the value wsn't set
explicitly. This value is changed to "off" or "chown" before use, thus
retaining compat to the status quo before, except it won't override
explicit configuration anymore. Thus, if you explicitly request
--private-user=pick you can now combine it wiht an explicit
--private-user-ownership=off if you like, which will give you a
container that runs under its own UID set, but the files will be owned
by the original image. Makes not much sense besids maybe debugging, but
if requested explicitly I think it's OK to implement.
This commit is contained in:
Lennart Poettering 2021-04-27 15:26:26 +02:00
parent 33eac552ab
commit 6c045a9998
4 changed files with 99 additions and 31 deletions

View File

@ -67,7 +67,8 @@ Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0
Files.Inaccessible, config_parse_inaccessible, 0, 0 Files.Inaccessible, config_parse_inaccessible, 0, 0
Files.Overlay, config_parse_overlay, 0, 0 Files.Overlay, config_parse_overlay, 0, 0
Files.OverlayReadOnly, config_parse_overlay, 1, 0 Files.OverlayReadOnly, config_parse_overlay, 1, 0
Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown) Files.PrivateUsersChown, config_parse_userns_chown, 0, offsetof(Settings, userns_ownership)
Files.PrivateUsersOwnership, config_parse_userns_ownership, 0, offsetof(Settings, userns_ownership)
Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network) Network.Private, config_parse_tristate, 0, offsetof(Settings, private_network)
Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces) Network.Interface, config_parse_strv, 0, offsetof(Settings, network_interfaces)
Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan) Network.MACVLAN, config_parse_strv, 0, offsetof(Settings, network_macvlan)

View File

@ -33,7 +33,7 @@ Settings *settings_new(void) {
.timezone = _TIMEZONE_MODE_INVALID, .timezone = _TIMEZONE_MODE_INVALID,
.userns_mode = _USER_NAMESPACE_MODE_INVALID, .userns_mode = _USER_NAMESPACE_MODE_INVALID,
.userns_chown = -1, .userns_ownership = _USER_NAMESPACE_OWNERSHIP_INVALID,
.uid_shift = UID_INVALID, .uid_shift = UID_INVALID,
.uid_range = UID_INVALID, .uid_range = UID_INVALID,
@ -84,12 +84,9 @@ int settings_load(FILE *f, const char *path, Settings **ret) {
/* Make sure that if userns_mode is set, userns_chown is set to something appropriate, and vice versa. Either /* 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. */ * both fields shall be initialized or neither. */
if (s->userns_mode == USER_NAMESPACE_PICK) if (s->userns_mode >= 0 && s->userns_ownership < 0)
s->userns_chown = true; s->userns_ownership = s->userns_mode == USER_NAMESPACE_PICK ? USER_NAMESPACE_OWNERSHIP_CHOWN : USER_NAMESPACE_OWNERSHIP_OFF;
else if (s->userns_mode != _USER_NAMESPACE_MODE_INVALID && s->userns_chown < 0) if (s->userns_ownership >= 0 && s->userns_mode < 0)
s->userns_chown = false;
if (s->userns_chown >= 0 && s->userns_mode == _USER_NAMESPACE_MODE_INVALID)
s->userns_mode = USER_NAMESPACE_NO; s->userns_mode = USER_NAMESPACE_NO;
*ret = TAKE_PTR(s); *ret = TAKE_PTR(s);
@ -863,3 +860,42 @@ static const char *const timezone_mode_table[_TIMEZONE_MODE_MAX] = {
}; };
DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(timezone_mode, TimezoneMode, TIMEZONE_AUTO); 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;
}

View File

@ -36,6 +36,13 @@ typedef enum UserNamespaceMode {
_USER_NAMESPACE_MODE_INVALID = -EINVAL, _USER_NAMESPACE_MODE_INVALID = -EINVAL,
} UserNamespaceMode; } 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 { typedef enum ResolvConfMode {
RESOLV_CONF_OFF, RESOLV_CONF_OFF,
RESOLV_CONF_COPY_HOST, /* /etc/resolv.conf */ RESOLV_CONF_COPY_HOST, /* /etc/resolv.conf */
@ -185,7 +192,7 @@ typedef struct Settings {
VolatileMode volatile_mode; VolatileMode volatile_mode;
CustomMount *custom_mounts; CustomMount *custom_mounts;
size_t n_custom_mounts; size_t n_custom_mounts;
int userns_chown; UserNamespaceOwnership userns_ownership;
/* [Network] */ /* [Network] */
int private_network; int private_network;
@ -255,6 +262,8 @@ CONFIG_PARSER_PROTOTYPE(config_parse_cpu_affinity);
CONFIG_PARSER_PROTOTYPE(config_parse_resolv_conf); CONFIG_PARSER_PROTOTYPE(config_parse_resolv_conf);
CONFIG_PARSER_PROTOTYPE(config_parse_link_journal); CONFIG_PARSER_PROTOTYPE(config_parse_link_journal);
CONFIG_PARSER_PROTOTYPE(config_parse_timezone); 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_; const char *resolv_conf_mode_to_string(ResolvConfMode a) _const_;
ResolvConfMode resolv_conf_mode_from_string(const char *s) _pure_; ResolvConfMode resolv_conf_mode_from_string(const char *s) _pure_;
@ -262,6 +271,9 @@ ResolvConfMode resolv_conf_mode_from_string(const char *s) _pure_;
const char *timezone_mode_to_string(TimezoneMode a) _const_; const char *timezone_mode_to_string(TimezoneMode a) _const_;
TimezoneMode timezone_mode_from_string(const char *s) _pure_; 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); int parse_link_journal(const char *s, LinkJournal *ret_mode, bool *ret_try);
void device_node_array_free(DeviceNode *node, size_t n); void device_node_array_free(DeviceNode *node, size_t n);

View File

@ -194,7 +194,7 @@ static char **arg_property = NULL;
static sd_bus_message *arg_property_message = NULL; static sd_bus_message *arg_property_message = NULL;
static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO; static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO;
static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U; 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 int arg_kill_signal = 0;
static CGroupUnified arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_UNKNOWN; static CGroupUnified arg_unified_cgroup_hierarchy = CGROUP_UNIFIED_UNKNOWN;
static SettingsMask arg_settings_mask = 0; static SettingsMask arg_settings_mask = 0;
@ -352,7 +352,9 @@ static int help(void) {
" -U --private-users=pick Run within user namespace, autoselect UID/GID range\n" " -U --private-users=pick Run within user namespace, autoselect UID/GID range\n"
" --private-users[=UIDBASE[:NUIDS]]\n" " --private-users[=UIDBASE[:NUIDS]]\n"
" Similar, but with user configured UID/GID range\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" "%3$sNetworking:%4$s\n"
" --private-network Disable network in container\n" " --private-network Disable network in container\n"
" --network-interface=INTERFACE\n" " --network-interface=INTERFACE\n"
@ -449,10 +451,10 @@ static int custom_mount_check_all(void) {
CustomMount *m = &arg_custom_mounts[i]; CustomMount *m = &arg_custom_mounts[i];
if (path_equal(m->destination, "/") && arg_userns_mode != USER_NAMESPACE_NO) { 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), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--private-users-chown may not be combined with custom root mounts."); "--private-users-ownership=own not be combined with custom root mounts.");
else if (arg_uid_shift == UID_INVALID) if (arg_uid_shift == UID_INVALID)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--private-users with automatic UID shift may not be combined with custom root mounts."); "--private-users with automatic UID shift may not be combined with custom root mounts.");
} }
@ -685,6 +687,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_CHDIR, ARG_CHDIR,
ARG_PIVOT_ROOT, ARG_PIVOT_ROOT,
ARG_PRIVATE_USERS_CHOWN, ARG_PRIVATE_USERS_CHOWN,
ARG_PRIVATE_USERS_OWNERSHIP,
ARG_NOTIFY_READY, ARG_NOTIFY_READY,
ARG_ROOT_HASH, ARG_ROOT_HASH,
ARG_ROOT_HASH_SIG, ARG_ROOT_HASH_SIG,
@ -752,7 +755,8 @@ static int parse_argv(int argc, char *argv[]) {
{ "port", required_argument, NULL, 'p' }, { "port", required_argument, NULL, 'p' },
{ "property", required_argument, NULL, ARG_PROPERTY }, { "property", required_argument, NULL, ARG_PROPERTY },
{ "private-users", optional_argument, NULL, ARG_PRIVATE_USERS }, { "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 }, { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL },
{ "settings", required_argument, NULL, ARG_SETTINGS }, { "settings", required_argument, NULL, ARG_SETTINGS },
{ "chdir", required_argument, NULL, ARG_CHDIR }, { "chdir", required_argument, NULL, ARG_CHDIR },
@ -1217,8 +1221,8 @@ static int parse_argv(int argc, char *argv[]) {
arg_uid_range = UINT32_C(0x10000); arg_uid_range = UINT32_C(0x10000);
} else if (streq(optarg, "pick")) { } else if (streq(optarg, "pick")) {
/* pick: User namespacing on, UID range is picked randomly */ /* pick: User namespacing on, UID range is picked randomly */
arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_chown = true, arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_ownership is
* is implied by USER_NAMESPACE_PICK, * implied by USER_NAMESPACE_PICK
* further down. */ * further down. */
arg_uid_shift = UID_INVALID; arg_uid_shift = UID_INVALID;
arg_uid_range = UINT32_C(0x10000); arg_uid_range = UINT32_C(0x10000);
@ -1267,8 +1271,8 @@ static int parse_argv(int argc, char *argv[]) {
case 'U': case 'U':
if (userns_supported()) { if (userns_supported()) {
arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_chown = true, arg_userns_mode = USER_NAMESPACE_PICK; /* Note that arg_userns_ownership is
* is implied by USER_NAMESPACE_PICK, * implied by USER_NAMESPACE_PICK
* further down. */ * further down. */
arg_uid_shift = UID_INVALID; arg_uid_shift = UID_INVALID;
arg_uid_range = UINT32_C(0x10000); arg_uid_range = UINT32_C(0x10000);
@ -1279,7 +1283,20 @@ static int parse_argv(int argc, char *argv[]) {
break; break;
case ARG_PRIVATE_USERS_CHOWN: 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; arg_settings_mask |= SETTING_USERNS;
break; break;
@ -1715,8 +1732,10 @@ static int verify_arguments(void) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--boot cannot be used without namespacing."); return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--boot cannot be used without namespacing.");
} }
if (arg_userns_mode == USER_NAMESPACE_PICK) if (arg_userns_ownership < 0)
arg_userns_chown = true; 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) if (arg_start_mode == START_BOOT && arg_kill_signal <= 0)
arg_kill_signal = SIGRTMIN+3; arg_kill_signal = SIGRTMIN+3;
@ -1750,15 +1769,15 @@ static int verify_arguments(void) {
if (arg_userns_mode != USER_NAMESPACE_NO && !userns_supported()) 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."); 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), 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 /* We don't support --private-users-ownership=chown together with any of the volatile modes since we
* change the read-only part of the tree (i.e. /usr) anyway, or because it would trigger a massive * couldn't change the read-only part of the tree (i.e. /usr) anyway, or because it would trigger a
* copy-up (in case of overlay) making the entire exercise pointless. */ * massive copy-up (in case of overlay) making the entire exercise pointless. */
if (arg_userns_chown && arg_volatile_mode != VOLATILE_NO) if (arg_userns_ownership == USER_NAMESPACE_OWNERSHIP_CHOWN && arg_volatile_mode != VOLATILE_NO)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--volatile= and --private-users-chown may not be combined."); 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), /* 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. */ * we need to error out, to avoid conflicts between different network options. */
@ -2795,7 +2814,7 @@ static int recursive_chown(const char *directory, uid_t shift, uid_t range) {
assert(directory); 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; return 0;
r = path_patch_uid(directory, arg_uid_shift, arg_uid_range); r = path_patch_uid(directory, arg_uid_shift, arg_uid_range);
@ -4240,7 +4259,7 @@ static int merge_settings(Settings *settings, const char *path) {
arg_userns_mode = settings->userns_mode; arg_userns_mode = settings->userns_mode;
arg_uid_shift = settings->uid_shift; arg_uid_shift = settings->uid_shift;
arg_uid_range = settings->uid_range; arg_uid_range = settings->uid_range;
arg_userns_chown = settings->userns_chown; arg_userns_ownership = settings->userns_ownership;
} }
} }