mirror of
https://github.com/systemd/systemd
synced 2025-11-17 07:44:46 +01:00
Compare commits
15 Commits
404d9bfb62
...
0885e4a6e7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0885e4a6e7 | ||
|
|
b1ba55a8a7 | ||
|
|
31c220d8f5 | ||
|
|
3f9db926e4 | ||
|
|
def01c7efe | ||
|
|
b430f2bc94 | ||
|
|
3fbf4ac24b | ||
|
|
cfabf3eb3b | ||
|
|
472161f368 | ||
|
|
097b6d3f66 | ||
|
|
bfb365d924 | ||
|
|
4207abb6e1 | ||
|
|
2348c56367 | ||
|
|
7912b1ebe5 | ||
|
|
70733160ee |
@ -1601,7 +1601,7 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
|
|||||||
|
|
||||||
<listitem><para>Binds the home directory of the specified user on the host into the container. Takes
|
<listitem><para>Binds the home directory of the specified user on the host into the container. Takes
|
||||||
the name of an existing user on the host as argument. May be used multiple times to bind multiple
|
the name of an existing user on the host as argument. May be used multiple times to bind multiple
|
||||||
users into the container. This does three things:</para>
|
users into the container. This does two things:</para>
|
||||||
|
|
||||||
<orderedlist>
|
<orderedlist>
|
||||||
<listitem><para>The user's home directory is bind mounted from the host into
|
<listitem><para>The user's home directory is bind mounted from the host into
|
||||||
@ -1616,7 +1616,7 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
|
|||||||
user/group databases.</para></listitem>
|
user/group databases.</para></listitem>
|
||||||
</orderedlist>
|
</orderedlist>
|
||||||
|
|
||||||
<para>The combination of the three operations above ensures that it is possible to log into the
|
<para>The combination of the two operations above ensures that it is possible to log into the
|
||||||
container using the same account information as on the host. The user is only mapped transiently,
|
container using the same account information as on the host. The user is only mapped transiently,
|
||||||
while the container is running, and the mapping itself does not result in persistent changes to the
|
while the container is running, and the mapping itself does not result in persistent changes to the
|
||||||
container (except maybe for log messages generated at login time, and similar). Note that in
|
container (except maybe for log messages generated at login time, and similar). Note that in
|
||||||
@ -1667,6 +1667,19 @@ After=sys-subsystem-net-devices-ens1.device</programlisting>
|
|||||||
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--bind-user-group=<replaceable>NAME</replaceable></option></term>
|
||||||
|
|
||||||
|
<listitem><para>When used with <option>--bind-user=</option>, includes the specified group as an
|
||||||
|
auxiliary group in the user records of users bound into the container. Takes a group name.</para>
|
||||||
|
|
||||||
|
<para>Note: This will not check whether the specified groups exist in the container.</para>
|
||||||
|
|
||||||
|
<para>This operation is only supported in combination with <option>--bind-user=</option>.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--inaccessible=</option></term>
|
<term><option>--inaccessible=</option></term>
|
||||||
|
|
||||||
|
|||||||
@ -526,6 +526,20 @@
|
|||||||
|
|
||||||
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--bind-user-group=<replaceable>NAME</replaceable></option></term>
|
||||||
|
|
||||||
|
<listitem><para>When used with <option>--bind-user=</option>, includes the specified group as an
|
||||||
|
auxiliary group in the user records of users bound into the virtual machine. Takes a group name.</para>
|
||||||
|
|
||||||
|
<para>Note: This will not check whether the specified groups exist in the virtual machine.</para>
|
||||||
|
|
||||||
|
<para>This operation is only supported in combination with <option>--bind-user=</option>.</para>
|
||||||
|
|
||||||
|
<xi:include href="version-info.xml" xpointer="v259"/></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
</variablelist>
|
</variablelist>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
|
|||||||
@ -6347,15 +6347,17 @@ ServerAddress=192.168.0.1/24</programlisting>
|
|||||||
<refsect1>
|
<refsect1>
|
||||||
<title>[BridgeVLAN] Section Options</title>
|
<title>[BridgeVLAN] Section Options</title>
|
||||||
<para>
|
<para>
|
||||||
The [BridgeVLAN] section manages the VLAN ID configurations of a bridge master or port, and accepts the
|
The [BridgeVLAN] section manages the VLAN ID configuration of a bridge master or enslaved device.
|
||||||
following keys. To make the settings in this section take an effect,
|
To make the settings in this section take an effect,
|
||||||
<varname>VLANFiltering=</varname> option has to be enabled on the bridge master, see the [Bridge]
|
<varname>VLANFiltering=</varname> option has to be enabled on the bridge master, see the [Bridge]
|
||||||
section in
|
section in
|
||||||
<citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
<citerefentry><refentrytitle>systemd.netdev</refentrytitle><manvolnum>5</manvolnum></citerefentry>, and
|
||||||
If at least one valid settings specified in this section in a .network file for an interface, all
|
each enslaved device needs to define the relevant VLAN IDs in its own [BridgeVLAN] section, which it
|
||||||
assigned VLAN IDs on the interface that are not configured in the .network file will be removed. If
|
has in common with the bridge master.
|
||||||
|
All assigned VLAN IDs on the interface that are not configured in the .network file will be removed. If
|
||||||
VLAN IDs on an interface need to be managed by other tools, then the settings in this section cannot
|
VLAN IDs on an interface need to be managed by other tools, then the settings in this section cannot
|
||||||
be used in the matching .network file.
|
be used in the matching .network file and <varname>VLANFiltering=</varname> needs to be disabled on
|
||||||
|
the bridge master.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<variablelist class='network-directives'>
|
<variablelist class='network-directives'>
|
||||||
|
|||||||
@ -1485,7 +1485,8 @@ ExecStart=/bin/echo $ONE $TWO $THREE</programlisting>
|
|||||||
<literal>too</literal>.
|
<literal>too</literal>.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>To pass a literal dollar sign, use <literal>$$</literal>.
|
<para>Unless for commands with the special executable prefix <literal>:</literal>,
|
||||||
|
to pass a literal dollar sign, use <literal>$$</literal>.
|
||||||
Variables whose value is not known at expansion time are treated
|
Variables whose value is not known at expansion time are treated
|
||||||
as empty strings. Note that the first argument (i.e. the program
|
as empty strings. Note that the first argument (i.e. the program
|
||||||
to execute) may not be a variable.</para>
|
to execute) may not be a variable.</para>
|
||||||
|
|||||||
@ -3,10 +3,6 @@
|
|||||||
"uid": 4711,
|
"uid": 4711,
|
||||||
"disposition": "regular",
|
"disposition": "regular",
|
||||||
"enforcePasswordPolicy": false,
|
"enforcePasswordPolicy": false,
|
||||||
"memberOf": [
|
|
||||||
"wheel",
|
|
||||||
"systemd-journal"
|
|
||||||
],
|
|
||||||
"shell": "/bin/bash",
|
"shell": "/bin/bash",
|
||||||
"privileged": {
|
"privileged": {
|
||||||
"hashedPassword": ["$1$kqp7NF1f$tNnQcshPX53CSfRKTQD0R1"]
|
"hashedPassword": ["$1$kqp7NF1f$tNnQcshPX53CSfRKTQD0R1"]
|
||||||
|
|||||||
@ -114,6 +114,26 @@ int path_is_extension_tree(ImageClass image_class, const char *path, const char
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int fd_is_os_tree(int fd) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(fd >= 0);
|
||||||
|
|
||||||
|
r = open_extension_release_at(
|
||||||
|
fd,
|
||||||
|
IMAGE_MACHINE,
|
||||||
|
/* extension= */ NULL,
|
||||||
|
/* relax_extension_release_check= */ false,
|
||||||
|
/* ret_path= */ NULL,
|
||||||
|
/* ret_fd= */ NULL);
|
||||||
|
if (r == -ENOENT)
|
||||||
|
return false;
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static int extension_release_strict_xattr_value(int extension_release_fd, const char *extension_release_dir_path, const char *filename) {
|
static int extension_release_strict_xattr_value(int extension_release_fd, const char *extension_release_dir_path, const char *filename) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
|||||||
@ -28,6 +28,7 @@ int path_is_extension_tree(ImageClass image_class, const char *path, const char
|
|||||||
static inline int path_is_os_tree(const char *path) {
|
static inline int path_is_os_tree(const char *path) {
|
||||||
return path_is_extension_tree(_IMAGE_CLASS_INVALID, path, NULL, false);
|
return path_is_extension_tree(_IMAGE_CLASS_INVALID, path, NULL, false);
|
||||||
}
|
}
|
||||||
|
int fd_is_os_tree(int fd);
|
||||||
|
|
||||||
int open_extension_release(const char *root, ImageClass image_class, const char *extension, bool relax_extension_release_check, char **ret_path, int *ret_fd);
|
int open_extension_release(const char *root, ImageClass image_class, const char *extension, bool relax_extension_release_check, char **ret_path, int *ret_fd);
|
||||||
int open_extension_release_at(int rfd, ImageClass image_class, const char *extension, bool relax_extension_release_check, char **ret_path, int *ret_fd);
|
int open_extension_release_at(int rfd, ImageClass image_class, const char *extension, bool relax_extension_release_check, char **ret_path, int *ret_fd);
|
||||||
|
|||||||
@ -148,19 +148,27 @@ int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid) {
|
|||||||
return TAKE_FD(pipefd[0]);
|
return TAKE_FD(pipefd[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int import_mangle_os_tree(const char *path) {
|
int import_mangle_os_tree_fd(int tree_fd, int userns_fd, ImportFlags flags) {
|
||||||
_cleanup_free_ char *child = NULL, *t = NULL, *joined = NULL;
|
_cleanup_free_ char *child = NULL, *t = NULL, *joined = NULL;
|
||||||
_cleanup_closedir_ DIR *d = NULL, *cd = NULL;
|
_cleanup_closedir_ DIR *d = NULL, *cd = NULL;
|
||||||
struct dirent *dent;
|
struct dirent *dent;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(path);
|
assert(tree_fd >= 0);
|
||||||
|
|
||||||
|
if (FLAGS_SET(flags, IMPORT_FOREIGN_UID) && userns_fd >= 0)
|
||||||
|
return import_mangle_os_tree_fd_foreign(tree_fd, userns_fd);
|
||||||
|
|
||||||
/* Some tarballs contain a single top-level directory that contains the actual OS directory tree. Try to
|
/* Some tarballs contain a single top-level directory that contains the actual OS directory tree. Try to
|
||||||
* recognize this, and move the tree one level up. */
|
* recognize this, and move the tree one level up. */
|
||||||
|
|
||||||
r = path_is_os_tree(path);
|
_cleanup_free_ char *path = NULL;
|
||||||
|
r = fd_get_path(tree_fd, &path);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to determine path of fd: %m");
|
||||||
|
|
||||||
|
r = fd_is_os_tree(tree_fd);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", path);
|
return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", path);
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
@ -170,9 +178,9 @@ int import_mangle_os_tree(const char *path) {
|
|||||||
|
|
||||||
log_debug("Directory tree '%s' is not recognizable as OS tree, checking whether to rearrange it.", path);
|
log_debug("Directory tree '%s' is not recognizable as OS tree, checking whether to rearrange it.", path);
|
||||||
|
|
||||||
d = opendir(path);
|
d = xopendirat(tree_fd, /* path= */ NULL, /* flags= */ 0);
|
||||||
if (!d)
|
if (!d)
|
||||||
return log_error_errno(r, "Failed to open directory '%s': %m", path);
|
return log_error_errno(errno, "Failed to open directory '%s': %m", path);
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
dent = readdir_no_dot(d);
|
dent = readdir_no_dot(d);
|
||||||
@ -191,29 +199,29 @@ int import_mangle_os_tree(const char *path) {
|
|||||||
errno = 0;
|
errno = 0;
|
||||||
dent = readdir_no_dot(d);
|
dent = readdir_no_dot(d);
|
||||||
if (dent) {
|
if (dent) {
|
||||||
if (errno != 0)
|
|
||||||
return log_error_errno(errno, "Failed to iterate through directory '%s': %m", path);
|
|
||||||
|
|
||||||
log_debug("Directory '%s' does not look like an OS tree, and has multiple children, leaving as it is.", path);
|
log_debug("Directory '%s' does not look like an OS tree, and has multiple children, leaving as it is.", path);
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (errno != 0)
|
||||||
|
return log_error_errno(errno, "Failed to iterate through directory '%s': %m", path);
|
||||||
|
|
||||||
|
_cleanup_close_ int child_fd = openat(dirfd(d), child, O_CLOEXEC|O_DIRECTORY|O_NOFOLLOW|O_NONBLOCK);
|
||||||
|
if (child_fd < 0) {
|
||||||
|
if (IN_SET(errno, ENOTDIR, ELOOP)) {
|
||||||
|
log_debug_errno(errno, "Child '%s' of directory '%s' is not a directory, leaving things as they are.", child, path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return log_debug_errno(errno, "Failed to open file '%s/%s': %m", path, child);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fstatat(dirfd(d), child, &st, AT_SYMLINK_NOFOLLOW) < 0)
|
if (fstat(child_fd, &st) < 0)
|
||||||
return log_debug_errno(errno, "Failed to stat file '%s/%s': %m", path, child);
|
return log_debug_errno(errno, "Failed to stat file '%s/%s': %m", path, child);
|
||||||
r = stat_verify_directory(&st);
|
|
||||||
if (r < 0) {
|
|
||||||
log_debug_errno(r, "Child '%s' of directory '%s' is not a directory, leaving things as they are.", child, path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
joined = path_join(path, child);
|
joined = path_join(path, child);
|
||||||
if (!joined)
|
if (!joined)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
r = path_is_os_tree(joined);
|
|
||||||
if (r == -ENOTDIR) {
|
r = fd_is_os_tree(child_fd);
|
||||||
log_debug("Directory '%s' does not look like an OS tree, and contains a single regular file only, leaving as it is.", path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", joined);
|
return log_error_errno(r, "Failed to determine whether '%s' is an OS tree: %m", joined);
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
@ -230,7 +238,7 @@ int import_mangle_os_tree(const char *path) {
|
|||||||
*
|
*
|
||||||
* Let's now rearrange things, moving everything in the inner directory one level up */
|
* Let's now rearrange things, moving everything in the inner directory one level up */
|
||||||
|
|
||||||
cd = xopendirat(dirfd(d), child, O_NOFOLLOW);
|
cd = take_fdopendir(&child_fd);
|
||||||
if (!cd)
|
if (!cd)
|
||||||
return log_error_errno(errno, "Can't open directory '%s': %m", joined);
|
return log_error_errno(errno, "Can't open directory '%s': %m", joined);
|
||||||
|
|
||||||
@ -238,7 +246,7 @@ int import_mangle_os_tree(const char *path) {
|
|||||||
|
|
||||||
/* Let's rename the child to an unguessable name so that we can be sure all files contained in it can be
|
/* Let's rename the child to an unguessable name so that we can be sure all files contained in it can be
|
||||||
* safely moved up and won't collide with the name. */
|
* safely moved up and won't collide with the name. */
|
||||||
r = tempfn_random(child, NULL, &t);
|
r = tempfn_random(child, /* extra= */ NULL, &t);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
r = rename_noreplace(dirfd(d), child, dirfd(d), t);
|
r = rename_noreplace(dirfd(d), child, dirfd(d), t);
|
||||||
@ -259,15 +267,68 @@ int import_mangle_os_tree(const char *path) {
|
|||||||
|
|
||||||
r = futimens(dirfd(d), (struct timespec[2]) { st.st_atim, st.st_mtim });
|
r = futimens(dirfd(d), (struct timespec[2]) { st.st_atim, st.st_mtim });
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
log_debug_errno(r, "Failed to adjust top-level timestamps '%s', ignoring: %m", path);
|
log_debug_errno(errno, "Failed to adjust top-level timestamps '%s', ignoring: %m", path);
|
||||||
|
|
||||||
r = fchmod_and_chown(dirfd(d), st.st_mode, st.st_uid, st.st_gid);
|
r = fchmod_and_chown(dirfd(d), st.st_mode, st.st_uid, st.st_gid);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to adjust top-level directory mode/ownership '%s': %m", path);
|
return log_error_errno(r, "Failed to adjust top-level directory mode/ownership '%s': %m", path);
|
||||||
|
|
||||||
log_info("Successfully rearranged OS tree.");
|
log_info("Successfully rearranged OS tree.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int import_mangle_os_tree(const char *path, int userns_fd, ImportFlags flags) {
|
||||||
|
assert(path);
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = open(path, O_DIRECTORY|O_CLOEXEC|O_PATH);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open '%s': %m", path);
|
||||||
|
|
||||||
|
return import_mangle_os_tree_fd(fd, userns_fd, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int import_mangle_os_tree_fd_foreign(
|
||||||
|
int tree_fd,
|
||||||
|
int userns_fd) {
|
||||||
|
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(tree_fd >= 0);
|
||||||
|
assert(userns_fd >= 0);
|
||||||
|
|
||||||
|
r = safe_fork_full(
|
||||||
|
"mangle-tree",
|
||||||
|
/* stdio_fds= */ NULL,
|
||||||
|
(int[]) { userns_fd, tree_fd }, 2,
|
||||||
|
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REOPEN_LOG|FORK_WAIT,
|
||||||
|
/* ret_pid= */ NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0) {
|
||||||
|
/* child */
|
||||||
|
|
||||||
|
r = namespace_enter(
|
||||||
|
/* pidns_fd= */ -EBADF,
|
||||||
|
/* mntns_fd= */ -EBADF,
|
||||||
|
/* netns_fd= */ -EBADF,
|
||||||
|
userns_fd,
|
||||||
|
/* root_fd= */ -EBADF);
|
||||||
|
if (r < 0) {
|
||||||
|
log_error_errno(r, "Failed to join user namespace: %m");
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = import_mangle_os_tree_fd(tree_fd, /* userns_fd= */ -EBADF, /* flags= */ 0);
|
||||||
|
if (r < 0) {
|
||||||
|
log_error_errno(r, "Failed to mangle OS tree in foreign UID mode: %m");
|
||||||
|
_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
_exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool import_validate_local(const char *name, ImportFlags flags) {
|
bool import_validate_local(const char *name, ImportFlags flags) {
|
||||||
|
|||||||
@ -37,7 +37,9 @@ typedef enum ImportFlags {
|
|||||||
int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid);
|
int import_fork_tar_c(int tree_fd, int userns_fd, PidRef *ret_pid);
|
||||||
int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid);
|
int import_fork_tar_x(int tree_fd, int userns_fd, PidRef *ret_pid);
|
||||||
|
|
||||||
int import_mangle_os_tree(const char *path);
|
int import_mangle_os_tree_fd(int tree_fd, int userns_fd, ImportFlags flags);
|
||||||
|
int import_mangle_os_tree(const char *path, int userns_fd, ImportFlags flags);
|
||||||
|
int import_mangle_os_tree_fd_foreign(int tree_fd, int userns_fd);
|
||||||
|
|
||||||
bool import_validate_local(const char *name, ImportFlags flags);
|
bool import_validate_local(const char *name, ImportFlags flags);
|
||||||
|
|
||||||
|
|||||||
@ -238,7 +238,7 @@ static int import_fs(int argc, char *argv[], void *userdata) {
|
|||||||
return log_error_errno(r, "Failed to copy directory: %m");
|
return log_error_errno(r, "Failed to copy directory: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
r = import_mangle_os_tree(dest);
|
r = import_mangle_os_tree(dest, /* userns_fd= */ -EBADF, /* flags= */ 0);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|||||||
@ -199,7 +199,7 @@ static int tar_import_finish(TarImport *i) {
|
|||||||
|
|
||||||
assert_se(d = i->temp_path ?: i->local);
|
assert_se(d = i->temp_path ?: i->local);
|
||||||
|
|
||||||
r = import_mangle_os_tree(d);
|
r = import_mangle_os_tree_fd(i->tree_fd, i->userns_fd, i->flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|||||||
@ -497,7 +497,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
|
|||||||
|
|
||||||
tar_pull_report_progress(i, TAR_FINALIZING);
|
tar_pull_report_progress(i, TAR_FINALIZING);
|
||||||
|
|
||||||
r = import_mangle_os_tree(i->local);
|
r = import_mangle_os_tree_fd(i->tree_fd, i->userns_fd, i->flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
@ -523,7 +523,7 @@ static void tar_pull_job_on_finished(PullJob *j) {
|
|||||||
|
|
||||||
tar_pull_report_progress(i, TAR_FINALIZING);
|
tar_pull_report_progress(i, TAR_FINALIZING);
|
||||||
|
|
||||||
r = import_mangle_os_tree(i->temp_path);
|
r = import_mangle_os_tree_fd(i->tree_fd, i->userns_fd, i->flags);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto finish;
|
goto finish;
|
||||||
|
|
||||||
|
|||||||
@ -1762,8 +1762,14 @@ int dhcp4_start_full(Link *link, bool set_ipv6_connectivity) {
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
assert(link);
|
assert(link);
|
||||||
|
assert(link->manager);
|
||||||
assert(link->network);
|
assert(link->network);
|
||||||
|
|
||||||
|
/* On stopping/restarting networkd, we may drop IPv6 connectivity (which depends on KeepConfiguration=
|
||||||
|
* setting). Do not (re)start DHCPv4 client in that case. See issue #39299. */
|
||||||
|
if (link->manager->state != MANAGER_RUNNING)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if (!link->dhcp_client)
|
if (!link->dhcp_client)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|||||||
@ -2225,7 +2225,7 @@ static int link_update_flags(Link *link, sd_netlink_message *message) {
|
|||||||
if (!had_carrier && link_has_carrier(link))
|
if (!had_carrier && link_has_carrier(link))
|
||||||
r = link_carrier_gained(link);
|
r = link_carrier_gained(link);
|
||||||
else if (had_carrier && !link_has_carrier(link))
|
else if (had_carrier && !link_has_carrier(link))
|
||||||
link_carrier_lost(link);
|
r = link_carrier_lost(link);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
|||||||
@ -5,12 +5,16 @@
|
|||||||
#include "sd-json.h"
|
#include "sd-json.h"
|
||||||
|
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
|
#include "chase.h"
|
||||||
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "format-util.h"
|
#include "format-util.h"
|
||||||
|
#include "io-util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "nspawn.h"
|
#include "nspawn.h"
|
||||||
#include "machine-bind-user.h"
|
#include "machine-bind-user.h"
|
||||||
#include "nspawn-bind-user.h"
|
#include "nspawn-bind-user.h"
|
||||||
|
#include "strv.h"
|
||||||
#include "user-record.h"
|
#include "user-record.h"
|
||||||
#include "group-record.h"
|
#include "group-record.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
@ -67,6 +71,40 @@ static int write_and_symlink(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int write_membership(const char *root, const char *user, const char *group) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(user);
|
||||||
|
assert(group);
|
||||||
|
|
||||||
|
_cleanup_free_ char *membership = strjoin(user, ":", group, ".membership");
|
||||||
|
if (!membership)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
_cleanup_free_ char *p = path_join("/run/host/userdb/", membership);
|
||||||
|
if (!p)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = chase_and_open(
|
||||||
|
p,
|
||||||
|
root,
|
||||||
|
CHASE_PREFIX_ROOT|CHASE_NO_AUTOFS,
|
||||||
|
O_WRONLY|O_CREAT|O_CLOEXEC,
|
||||||
|
/* ret_path= */ NULL);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to create %s: %m", p);
|
||||||
|
|
||||||
|
r = userns_chown_at(fd, /* fname= */ NULL, /* uid= */ 0, /* gid= */ 0, /* flags= */ 0);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to adjust access mode of '%s': %m", p);
|
||||||
|
|
||||||
|
r = loop_write(fd, "{}\n", SIZE_MAX);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to write empty JSON object into %s: %m", p);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int bind_user_setup(const MachineBindUserContext *c, const char *root) {
|
int bind_user_setup(const MachineBindUserContext *c, const char *root) {
|
||||||
static const UserRecordLoadFlags strip_flags = /* Removes privileged info */
|
static const UserRecordLoadFlags strip_flags = /* Removes privileged info */
|
||||||
USER_RECORD_LOAD_MASK_PRIVILEGED|
|
USER_RECORD_LOAD_MASK_PRIVILEGED|
|
||||||
@ -130,6 +168,12 @@ int bind_user_setup(const MachineBindUserContext *c, const char *root) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
STRV_FOREACH(u, stripped_group->members) {
|
||||||
|
r = write_membership(root, *u, stripped_group->group_name);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/* Third, write out user shadow data. i.e. extract privileged info from user record */
|
/* Third, write out user shadow data. i.e. extract privileged info from user record */
|
||||||
r = user_record_clone(d->payload_user, shadow_flags, &shadow_user);
|
r = user_record_clone(d->payload_user, shadow_flags, &shadow_user);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -161,6 +205,12 @@ int bind_user_setup(const MachineBindUserContext *c, const char *root) {
|
|||||||
0);
|
0);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
|
STRV_FOREACH(g, stripped_user->member_of) {
|
||||||
|
r = write_membership(root, stripped_user->user_name, *g);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@ -240,6 +240,7 @@ static MachineCredentialContext arg_credentials = {};
|
|||||||
static char **arg_bind_user = NULL;
|
static char **arg_bind_user = NULL;
|
||||||
static char *arg_bind_user_shell = NULL;
|
static char *arg_bind_user_shell = NULL;
|
||||||
static bool arg_bind_user_shell_copy = false;
|
static bool arg_bind_user_shell_copy = false;
|
||||||
|
static char **arg_bind_user_groups = NULL;
|
||||||
static bool arg_suppress_sync = false;
|
static bool arg_suppress_sync = false;
|
||||||
static char *arg_settings_filename = NULL;
|
static char *arg_settings_filename = NULL;
|
||||||
static Architecture arg_architecture = _ARCHITECTURE_INVALID;
|
static Architecture arg_architecture = _ARCHITECTURE_INVALID;
|
||||||
@ -283,6 +284,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_cpu_set, cpu_set_done);
|
|||||||
STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_bind_user_shell, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_bind_user_shell, freep);
|
||||||
|
STATIC_DESTRUCTOR_REGISTER(arg_bind_user_groups, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_settings_filename, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_settings_filename, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
|
||||||
@ -429,6 +431,8 @@ static int help(void) {
|
|||||||
" --bind-user=NAME Bind user from host to container\n"
|
" --bind-user=NAME Bind user from host to container\n"
|
||||||
" --bind-user-shell=BOOL|PATH\n"
|
" --bind-user-shell=BOOL|PATH\n"
|
||||||
" Configure the shell to use for --bind-user= users\n"
|
" Configure the shell to use for --bind-user= users\n"
|
||||||
|
" --bind-user-group=GROUP\n"
|
||||||
|
" Add an auxiliary group to --bind-user= users\n"
|
||||||
"\n%3$sInput/Output:%4$s\n"
|
"\n%3$sInput/Output:%4$s\n"
|
||||||
" --console=MODE Select how stdin/stdout/stderr and /dev/console are\n"
|
" --console=MODE Select how stdin/stdout/stderr and /dev/console are\n"
|
||||||
" set up for the container.\n"
|
" set up for the container.\n"
|
||||||
@ -660,6 +664,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
ARG_LOAD_CREDENTIAL,
|
ARG_LOAD_CREDENTIAL,
|
||||||
ARG_BIND_USER,
|
ARG_BIND_USER,
|
||||||
ARG_BIND_USER_SHELL,
|
ARG_BIND_USER_SHELL,
|
||||||
|
ARG_BIND_USER_GROUP,
|
||||||
ARG_SUPPRESS_SYNC,
|
ARG_SUPPRESS_SYNC,
|
||||||
ARG_IMAGE_POLICY,
|
ARG_IMAGE_POLICY,
|
||||||
ARG_BACKGROUND,
|
ARG_BACKGROUND,
|
||||||
@ -738,6 +743,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
{ "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL },
|
{ "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL },
|
||||||
{ "bind-user", required_argument, NULL, ARG_BIND_USER },
|
{ "bind-user", required_argument, NULL, ARG_BIND_USER },
|
||||||
{ "bind-user-shell", required_argument, NULL, ARG_BIND_USER_SHELL },
|
{ "bind-user-shell", required_argument, NULL, ARG_BIND_USER_SHELL },
|
||||||
|
{ "bind-user-group", required_argument, NULL, ARG_BIND_USER_GROUP },
|
||||||
{ "suppress-sync", required_argument, NULL, ARG_SUPPRESS_SYNC },
|
{ "suppress-sync", required_argument, NULL, ARG_SUPPRESS_SYNC },
|
||||||
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
{ "image-policy", required_argument, NULL, ARG_IMAGE_POLICY },
|
||||||
{ "background", required_argument, NULL, ARG_BACKGROUND },
|
{ "background", required_argument, NULL, ARG_BACKGROUND },
|
||||||
@ -1514,6 +1520,15 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ARG_BIND_USER_GROUP:
|
||||||
|
if (!valid_user_group_name(optarg, /* flags= */ 0))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid bind user auxiliary group name: %s", optarg);
|
||||||
|
|
||||||
|
if (strv_extend(&arg_bind_user_groups, optarg) < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case ARG_SUPPRESS_SYNC:
|
case ARG_SUPPRESS_SYNC:
|
||||||
r = parse_boolean_argument("--suppress-sync=", optarg, &arg_suppress_sync);
|
r = parse_boolean_argument("--suppress-sync=", optarg, &arg_suppress_sync);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
@ -1689,12 +1704,16 @@ static int verify_arguments(void) {
|
|||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "AmbientCapability= setting is not useful for boot mode.");
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "AmbientCapability= setting is not useful for boot mode.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Drop duplicate --bind-user= entries */
|
/* Drop duplicate --bind-user= and --bind-user-group= entries */
|
||||||
strv_uniq(arg_bind_user);
|
strv_uniq(arg_bind_user);
|
||||||
|
strv_uniq(arg_bind_user_groups);
|
||||||
|
|
||||||
if (arg_bind_user_shell && strv_isempty(arg_bind_user))
|
if (arg_bind_user_shell && strv_isempty(arg_bind_user))
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --bind-user-shell= without --bind-user=");
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --bind-user-shell= without --bind-user=");
|
||||||
|
|
||||||
|
if (!strv_isempty(arg_bind_user_groups) && strv_isempty(arg_bind_user))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --bind-user-group= without --bind-user=");
|
||||||
|
|
||||||
r = custom_mount_check_all();
|
r = custom_mount_check_all();
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -1733,7 +1752,7 @@ static int in_child_chown(void) {
|
|||||||
return IN_SET(arg_userns_mode, USER_NAMESPACE_PICK, USER_NAMESPACE_FIXED);
|
return IN_SET(arg_userns_mode, USER_NAMESPACE_PICK, USER_NAMESPACE_FIXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int userns_chown_at(int fd, const char *fname, uid_t uid, gid_t gid, int flags) {
|
int userns_chown_at(int fd, const char *fname, uid_t uid, gid_t gid, int flags) {
|
||||||
assert(fd >= 0 || fd == AT_FDCWD);
|
assert(fd >= 0 || fd == AT_FDCWD);
|
||||||
|
|
||||||
if (!in_child_chown())
|
if (!in_child_chown())
|
||||||
@ -1756,6 +1775,9 @@ static int userns_chown_at(int fd, const char *fname, uid_t uid, gid_t gid, int
|
|||||||
return -EOVERFLOW;
|
return -EOVERFLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isempty(fname))
|
||||||
|
flags |= AT_EMPTY_PATH;
|
||||||
|
|
||||||
return RET_NERRNO(fchownat(fd, strempty(fname), uid, gid, flags));
|
return RET_NERRNO(fchownat(fd, strempty(fname), uid, gid, flags));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4020,6 +4042,7 @@ static int outer_child(
|
|||||||
arg_bind_user_shell,
|
arg_bind_user_shell,
|
||||||
arg_bind_user_shell_copy,
|
arg_bind_user_shell_copy,
|
||||||
"/run/host/home",
|
"/run/host/home",
|
||||||
|
arg_bind_user_groups,
|
||||||
&bind_user_context);
|
&bind_user_context);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "shared-forward.h"
|
#include "shared-forward.h"
|
||||||
|
|
||||||
|
int userns_chown_at(int fd, const char *fname, uid_t uid, gid_t gid, int flags);
|
||||||
int userns_lchown(const char *p, uid_t uid, gid_t gid);
|
int userns_lchown(const char *p, uid_t uid, gid_t gid);
|
||||||
int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid_t gid);
|
int userns_mkdir(const char *root, const char *path, mode_t mode, uid_t uid, gid_t gid);
|
||||||
int make_run_host(const char *root);
|
int make_run_host(const char *root);
|
||||||
|
|||||||
@ -94,6 +94,7 @@ static int convert_user(
|
|||||||
const char *shell,
|
const char *shell,
|
||||||
bool shell_copy,
|
bool shell_copy,
|
||||||
const char *home_mount_directory,
|
const char *home_mount_directory,
|
||||||
|
char **groups,
|
||||||
UserRecord **ret_converted_user,
|
UserRecord **ret_converted_user,
|
||||||
GroupRecord **ret_converted_group) {
|
GroupRecord **ret_converted_group) {
|
||||||
|
|
||||||
@ -145,6 +146,7 @@ static int convert_user(
|
|||||||
SD_JSON_BUILD_PAIR("homeDirectory", SD_JSON_BUILD_STRING(h)),
|
SD_JSON_BUILD_PAIR("homeDirectory", SD_JSON_BUILD_STRING(h)),
|
||||||
SD_JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.NSpawn")),
|
SD_JSON_BUILD_PAIR("service", JSON_BUILD_CONST_STRING("io.systemd.NSpawn")),
|
||||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("shell", shell),
|
JSON_BUILD_PAIR_STRING_NON_EMPTY("shell", shell),
|
||||||
|
SD_JSON_BUILD_PAIR_STRV("memberOf", groups),
|
||||||
SD_JSON_BUILD_PAIR("privileged", SD_JSON_BUILD_OBJECT(
|
SD_JSON_BUILD_PAIR("privileged", SD_JSON_BUILD_OBJECT(
|
||||||
SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(u->hashed_password), "hashedPassword", SD_JSON_BUILD_VARIANT(hp)),
|
SD_JSON_BUILD_PAIR_CONDITION(!strv_isempty(u->hashed_password), "hashedPassword", SD_JSON_BUILD_VARIANT(hp)),
|
||||||
SD_JSON_BUILD_PAIR_CONDITION(!!ssh, "sshAuthorizedKeys", SD_JSON_BUILD_VARIANT(ssh))))));
|
SD_JSON_BUILD_PAIR_CONDITION(!!ssh, "sshAuthorizedKeys", SD_JSON_BUILD_VARIANT(ssh))))));
|
||||||
@ -212,6 +214,7 @@ int machine_bind_user_prepare(
|
|||||||
const char *bind_user_shell,
|
const char *bind_user_shell,
|
||||||
bool bind_user_shell_copy,
|
bool bind_user_shell_copy,
|
||||||
const char *bind_user_home_mount_directory,
|
const char *bind_user_home_mount_directory,
|
||||||
|
char **bind_user_groups,
|
||||||
MachineBindUserContext **ret) {
|
MachineBindUserContext **ret) {
|
||||||
|
|
||||||
_cleanup_(machine_bind_user_context_freep) MachineBindUserContext *c = NULL;
|
_cleanup_(machine_bind_user_context_freep) MachineBindUserContext *c = NULL;
|
||||||
@ -288,6 +291,7 @@ int machine_bind_user_prepare(
|
|||||||
bind_user_shell,
|
bind_user_shell,
|
||||||
bind_user_shell_copy,
|
bind_user_shell_copy,
|
||||||
bind_user_home_mount_directory,
|
bind_user_home_mount_directory,
|
||||||
|
bind_user_groups,
|
||||||
&cu, &cg);
|
&cu, &cg);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@ -28,4 +28,5 @@ int machine_bind_user_prepare(
|
|||||||
const char *bind_user_shell,
|
const char *bind_user_shell,
|
||||||
bool bind_user_shell_copy,
|
bool bind_user_shell_copy,
|
||||||
const char *bind_user_home_mount_directory,
|
const char *bind_user_home_mount_directory,
|
||||||
|
char **bind_user_groups,
|
||||||
MachineBindUserContext **ret);
|
MachineBindUserContext **ret);
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
#include "format-table.h"
|
#include "format-table.h"
|
||||||
#include "format-util.h"
|
#include "format-util.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
|
#include "io-util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "main-func.h"
|
#include "main-func.h"
|
||||||
#include "mkdir.h"
|
#include "mkdir.h"
|
||||||
@ -1196,6 +1197,31 @@ static int ssh_authorized_keys(int argc, char *argv[], void *userdata) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int write_membership(int dir_fd, const char *dir, const char *user, const char *group) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
|
||||||
|
assert(dir);
|
||||||
|
assert(user);
|
||||||
|
assert(group);
|
||||||
|
|
||||||
|
_cleanup_free_ char *membership = strjoin(user, ":", group, ".membership");
|
||||||
|
if (!membership)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
_cleanup_close_ int fd = openat(dir_fd, membership, O_WRONLY|O_CREAT|O_CLOEXEC, 0644);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to create %s/%s: %m", dir, membership);
|
||||||
|
|
||||||
|
r = loop_write(fd, "{}\n", SIZE_MAX);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to write empty JSON object into %s/%s: %m", dir, membership);
|
||||||
|
|
||||||
|
log_info("Installed %s/%s from credential.", dir, membership);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int load_credential_one(
|
static int load_credential_one(
|
||||||
int credential_dir_fd,
|
int credential_dir_fd,
|
||||||
const char *name,
|
const char *name,
|
||||||
@ -1430,27 +1456,15 @@ static int load_credential_one(
|
|||||||
|
|
||||||
if (ur)
|
if (ur)
|
||||||
STRV_FOREACH(g, ur->member_of) {
|
STRV_FOREACH(g, ur->member_of) {
|
||||||
_cleanup_free_ char *membership = strjoin(ur->user_name, ":", *g);
|
r = write_membership(*userdb_dir_fd, userdb_dir, ur->user_name, *g);
|
||||||
if (!membership)
|
if (r < 0)
|
||||||
return log_oom();
|
return r;
|
||||||
|
|
||||||
_cleanup_close_ int fd = openat(*userdb_dir_fd, membership, O_WRONLY|O_CREAT|O_CLOEXEC, 0644);
|
|
||||||
if (fd < 0)
|
|
||||||
return log_error_errno(errno, "Failed to create %s: %m", membership);
|
|
||||||
|
|
||||||
log_info("Installed %s/%s from credential.", userdb_dir, membership);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
STRV_FOREACH(u, gr->members) {
|
STRV_FOREACH(u, gr->members) {
|
||||||
_cleanup_free_ char *membership = strjoin(*u, ":", gr->group_name);
|
r = write_membership(*userdb_dir_fd, userdb_dir, *u, gr->group_name);
|
||||||
if (!membership)
|
if (r < 0)
|
||||||
return log_oom();
|
return r;
|
||||||
|
|
||||||
_cleanup_close_ int fd = openat(*userdb_dir_fd, membership, O_WRONLY|O_CREAT|O_CLOEXEC, 0644);
|
|
||||||
if (fd < 0)
|
|
||||||
return log_error_errno(errno, "Failed to create %s: %m", membership);
|
|
||||||
|
|
||||||
log_info("Installed %s/%s from credential.", userdb_dir, membership);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ur && user_record_disposition(ur) == USER_REGULAR) {
|
if (ur && user_record_disposition(ur) == USER_REGULAR) {
|
||||||
|
|||||||
@ -143,6 +143,7 @@ static bool arg_notify_ready = true;
|
|||||||
static char **arg_bind_user = NULL;
|
static char **arg_bind_user = NULL;
|
||||||
static char *arg_bind_user_shell = NULL;
|
static char *arg_bind_user_shell = NULL;
|
||||||
static bool arg_bind_user_shell_copy = false;
|
static bool arg_bind_user_shell_copy = false;
|
||||||
|
static char **arg_bind_user_groups = NULL;
|
||||||
|
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||||
@ -164,6 +165,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_tpm_state_path, freep);
|
|||||||
STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_bind_user, strv_freep);
|
||||||
STATIC_DESTRUCTOR_REGISTER(arg_bind_user_shell, freep);
|
STATIC_DESTRUCTOR_REGISTER(arg_bind_user_shell, freep);
|
||||||
|
STATIC_DESTRUCTOR_REGISTER(arg_bind_user_groups, strv_freep);
|
||||||
|
|
||||||
static int help(void) {
|
static int help(void) {
|
||||||
_cleanup_free_ char *link = NULL;
|
_cleanup_free_ char *link = NULL;
|
||||||
@ -227,6 +229,8 @@ static int help(void) {
|
|||||||
" --bind-user=NAME Bind user from host to virtual machine\n"
|
" --bind-user=NAME Bind user from host to virtual machine\n"
|
||||||
" --bind-user-shell=BOOL|PATH\n"
|
" --bind-user-shell=BOOL|PATH\n"
|
||||||
" Configure the shell to use for --bind-user= users\n"
|
" Configure the shell to use for --bind-user= users\n"
|
||||||
|
" --bind-user-group=GROUP\n"
|
||||||
|
" Add an auxiliary group to --bind-user= users\n"
|
||||||
"\n%3$sIntegration:%4$s\n"
|
"\n%3$sIntegration:%4$s\n"
|
||||||
" --forward-journal=FILE|DIR\n"
|
" --forward-journal=FILE|DIR\n"
|
||||||
" Forward the VM's journal to the host\n"
|
" Forward the VM's journal to the host\n"
|
||||||
@ -303,6 +307,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
ARG_NOTIFY_READY,
|
ARG_NOTIFY_READY,
|
||||||
ARG_BIND_USER,
|
ARG_BIND_USER,
|
||||||
ARG_BIND_USER_SHELL,
|
ARG_BIND_USER_SHELL,
|
||||||
|
ARG_BIND_USER_GROUP,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
@ -354,6 +359,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
{ "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
|
{ "notify-ready", required_argument, NULL, ARG_NOTIFY_READY },
|
||||||
{ "bind-user", required_argument, NULL, ARG_BIND_USER },
|
{ "bind-user", required_argument, NULL, ARG_BIND_USER },
|
||||||
{ "bind-user-shell", required_argument, NULL, ARG_BIND_USER_SHELL },
|
{ "bind-user-shell", required_argument, NULL, ARG_BIND_USER_SHELL },
|
||||||
|
{ "bind-user-group", required_argument, NULL, ARG_BIND_USER_GROUP },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -715,6 +721,15 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ARG_BIND_USER_GROUP:
|
||||||
|
if (!valid_user_group_name(optarg, /* flags= */ 0))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid bind user auxiliary group name: %s", optarg);
|
||||||
|
|
||||||
|
if (strv_extend(&arg_bind_user_groups, optarg) < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -722,12 +737,16 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Drop duplicate --bind-user= entries */
|
/* Drop duplicate --bind-user= and --bind-user-group= entries */
|
||||||
strv_uniq(arg_bind_user);
|
strv_uniq(arg_bind_user);
|
||||||
|
strv_uniq(arg_bind_user_groups);
|
||||||
|
|
||||||
if (arg_bind_user_shell && strv_isempty(arg_bind_user))
|
if (arg_bind_user_shell && strv_isempty(arg_bind_user))
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --bind-user-shell= without --bind-user=");
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --bind-user-shell= without --bind-user=");
|
||||||
|
|
||||||
|
if (!strv_isempty(arg_bind_user_groups) && strv_isempty(arg_bind_user))
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --bind-user-group= without --bind-user=");
|
||||||
|
|
||||||
if (argc > optind) {
|
if (argc > optind) {
|
||||||
arg_kernel_cmdline_extra = strv_copy(argv + optind);
|
arg_kernel_cmdline_extra = strv_copy(argv + optind);
|
||||||
if (!arg_kernel_cmdline_extra)
|
if (!arg_kernel_cmdline_extra)
|
||||||
@ -1840,6 +1859,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||||||
arg_bind_user_shell,
|
arg_bind_user_shell,
|
||||||
arg_bind_user_shell_copy,
|
arg_bind_user_shell_copy,
|
||||||
"/run/vmhost/home",
|
"/run/vmhost/home",
|
||||||
|
arg_bind_user_groups,
|
||||||
&bind_user_context);
|
&bind_user_context);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|||||||
@ -162,7 +162,7 @@ rm -f /tmp/core.{output,redirected}
|
|||||||
|
|
||||||
# Unprivileged stuff
|
# Unprivileged stuff
|
||||||
# Related issue: https://github.com/systemd/systemd/issues/26912
|
# Related issue: https://github.com/systemd/systemd/issues/26912
|
||||||
UNPRIV_CMD=(systemd-run --user --wait --pipe -M "testuser@.host" --)
|
UNPRIV_CMD=(systemd-run --user --wait --pipe -M "testuser@.host" -E SYSTEMD_PAGER --)
|
||||||
# Trigger a couple of coredumps as an unprivileged user
|
# Trigger a couple of coredumps as an unprivileged user
|
||||||
"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
|
"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
|
||||||
"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
|
"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user