mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-15 04:09:46 +00:00
Merge pull request #2596 from brauner/2018-09-05/attach_id
utils: allow lxc-attach to set uid / gid
This commit is contained in:
commit
88fbc01082
@ -60,6 +60,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
<arg choice="opt">--clear-env</arg>
|
||||
<arg choice="opt">-v, --set-var <replaceable>variable</replaceable></arg>
|
||||
<arg choice="opt">--keep-var <replaceable>variable</replaceable></arg>
|
||||
<arg choice="opt">-u, --uid <replaceable>uid</replaceable></arg>
|
||||
<arg choice="opt">-g, --gid <replaceable>gid</replaceable></arg>
|
||||
<arg choice="opt">-- <replaceable>command</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
@ -282,6 +284,30 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<option>--u, --uid <replaceable>uid</replaceable></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Executes the <replaceable>command</replaceable> with user ID
|
||||
<replaceable>uid</replaceable> inside the container.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<option>--g, --gid <replaceable>gid</replaceable></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Executes the <replaceable>command</replaceable> with group ID
|
||||
<replaceable>gid</replaceable> inside the container.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</refsect1>
|
||||
|
@ -53,6 +53,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
<arg choice="opt">-d</arg>
|
||||
<arg choice="opt">-f <replaceable>config_file</replaceable></arg>
|
||||
<arg choice="opt">-s KEY=VAL</arg>
|
||||
<arg choice="opt">-u, --uid <replaceable>uid</replaceable></arg>
|
||||
<arg choice="opt">-g, --gid <replaceable>gid</replaceable></arg>
|
||||
<arg choice="opt">-- <replaceable>command</replaceable></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
@ -139,6 +141,30 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<option>--u, --uid <replaceable>uid</replaceable></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Executes the <replaceable>command</replaceable> with user ID
|
||||
<replaceable>uid</replaceable> inside the container.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term>
|
||||
<option>--g, --gid <replaceable>gid</replaceable></option>
|
||||
</term>
|
||||
<listitem>
|
||||
<para>
|
||||
Executes the <replaceable>command</replaceable> with group ID
|
||||
<replaceable>gid</replaceable> inside the container.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--</option></term>
|
||||
<listitem>
|
||||
|
@ -749,6 +749,8 @@ static int attach_child_main(struct attach_clone_payload *payload)
|
||||
int fd, lsm_fd, ret;
|
||||
uid_t new_uid;
|
||||
gid_t new_gid;
|
||||
uid_t ns_root_uid = 0;
|
||||
gid_t ns_root_gid = 0;
|
||||
lxc_attach_options_t* options = payload->options;
|
||||
struct lxc_proc_context_info* init_ctx = payload->init_ctx;
|
||||
bool needs_lsm = (options->namespaces & CLONE_NEWNS) &&
|
||||
@ -836,33 +838,40 @@ static int attach_child_main(struct attach_clone_payload *payload)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
/* Set {u,g}id. */
|
||||
new_uid = 0;
|
||||
new_gid = 0;
|
||||
if (options->namespaces & CLONE_NEWUSER) {
|
||||
/* Check whether nsuid 0 has a mapping. */
|
||||
ns_root_uid = get_ns_uid(0);
|
||||
|
||||
/* Ignore errors, we will fall back to root in that case (/proc was not
|
||||
* mounted etc.).
|
||||
*/
|
||||
if (options->namespaces & CLONE_NEWUSER)
|
||||
lxc_attach_get_init_uidgid(&new_uid, &new_gid);
|
||||
/* Check whether nsgid 0 has a mapping. */
|
||||
ns_root_gid = get_ns_gid(0);
|
||||
|
||||
if (options->uid != (uid_t)-1)
|
||||
new_uid = options->uid;
|
||||
/* If there's no mapping for nsuid 0 try to retrieve the nsuid
|
||||
* init was started with.
|
||||
*/
|
||||
if (ns_root_uid == LXC_INVALID_UID)
|
||||
lxc_attach_get_init_uidgid(&ns_root_uid, &ns_root_gid);
|
||||
|
||||
if (options->gid != (gid_t)-1)
|
||||
new_gid = options->gid;
|
||||
if (ns_root_uid == LXC_INVALID_UID)
|
||||
goto on_error;
|
||||
|
||||
/* Try to set the {u,g}id combination. */
|
||||
if (new_uid != 0 || new_gid != 0 || options->namespaces & CLONE_NEWUSER) {
|
||||
ret = lxc_switch_uid_gid(new_uid, new_gid);
|
||||
if (ret < 0)
|
||||
if (!lxc_switch_uid_gid(ns_root_uid, ns_root_gid))
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
ret = lxc_setgroups(0, NULL);
|
||||
if (ret < 0 && errno != EPERM)
|
||||
if (!lxc_setgroups(0, NULL) && errno != EPERM)
|
||||
goto on_error;
|
||||
|
||||
/* Set {u,g}id. */
|
||||
if (options->uid != LXC_INVALID_UID)
|
||||
new_uid = options->uid;
|
||||
else
|
||||
new_uid = ns_root_uid;
|
||||
|
||||
if (options->gid != LXC_INVALID_GID)
|
||||
new_gid = options->gid;
|
||||
else
|
||||
new_gid = ns_root_gid;
|
||||
|
||||
if ((init_ctx->container && init_ctx->container->lxc_conf &&
|
||||
init_ctx->container->lxc_conf->no_new_privs) ||
|
||||
(options->attach_flags & LXC_ATTACH_NO_NEW_PRIVS)) {
|
||||
@ -952,6 +961,16 @@ static int attach_child_main(struct attach_clone_payload *payload)
|
||||
TRACE("Prepared terminal file descriptor %d", payload->terminal_slave_fd);
|
||||
}
|
||||
|
||||
/* Avoid unnecessary syscalls. */
|
||||
if (new_uid == ns_root_uid)
|
||||
new_uid = LXC_INVALID_UID;
|
||||
|
||||
if (new_gid == ns_root_gid)
|
||||
new_gid = LXC_INVALID_GID;
|
||||
|
||||
if (!lxc_switch_uid_gid(new_uid, new_gid))
|
||||
goto on_error;
|
||||
|
||||
/* We're done, so we can now do whatever the user intended us to do. */
|
||||
_exit(payload->exec_function(payload->exec_payload));
|
||||
|
||||
|
@ -1388,6 +1388,8 @@ static int chown_cgroup_wrapper(void *data)
|
||||
}
|
||||
|
||||
destuid = get_ns_uid(arg->origuid);
|
||||
if (destuid == LXC_INVALID_UID)
|
||||
destuid = 0;
|
||||
|
||||
for (i = 0; arg->hierarchies[i]; i++) {
|
||||
char *fullpath;
|
||||
|
@ -104,12 +104,10 @@ static int do_child(void *vargv)
|
||||
char **argv = (char **)vargv;
|
||||
|
||||
/* Assume we want to become root */
|
||||
ret = lxc_switch_uid_gid(0, 0);
|
||||
if (ret < 0)
|
||||
if (!lxc_switch_uid_gid(0, 0))
|
||||
return -1;
|
||||
|
||||
ret = lxc_setgroups(0, NULL);
|
||||
if (ret < 0)
|
||||
if (!lxc_setgroups(0, NULL))
|
||||
return -1;
|
||||
|
||||
ret = unshare(CLONE_NEWNS);
|
||||
|
@ -340,4 +340,7 @@ extern int __build_bug_on_failed;
|
||||
#define PTR_TO_INTMAX(p) ((intmax_t)((intptr_t)(p)))
|
||||
#define INTMAX_TO_PTR(u) ((void *)((intptr_t)(u)))
|
||||
|
||||
#define LXC_INVALID_UID ((uid_t)-1)
|
||||
#define LXC_INVALID_GID ((gid_t)-1)
|
||||
|
||||
#endif /* __LXC_MACRO_H */
|
||||
|
@ -1046,10 +1046,11 @@ static int do_start(void *data)
|
||||
{
|
||||
int ret;
|
||||
char path[PATH_MAX];
|
||||
bool have_cap_setgid;
|
||||
uid_t new_uid;
|
||||
gid_t new_gid;
|
||||
struct lxc_list *iterator;
|
||||
uid_t nsuid = 0;
|
||||
gid_t nsgid = 0;
|
||||
int devnull_fd = -1;
|
||||
struct lxc_handler *handler = data;
|
||||
|
||||
@ -1117,22 +1118,20 @@ static int do_start(void *data)
|
||||
* privilege over our namespace.
|
||||
*/
|
||||
if (!lxc_list_empty(&handler->conf->id_map)) {
|
||||
uid_t nsuid = (handler->conf->root_nsuid_map != NULL)
|
||||
? 0
|
||||
: handler->conf->init_uid;
|
||||
gid_t nsgid = (handler->conf->root_nsgid_map != NULL)
|
||||
? 0
|
||||
: handler->conf->init_gid;
|
||||
if (!handler->conf->root_nsuid_map)
|
||||
nsuid = handler->conf->init_uid;
|
||||
|
||||
ret = lxc_switch_uid_gid(nsuid, nsgid);
|
||||
if (ret < 0)
|
||||
if (!handler->conf->root_nsgid_map)
|
||||
nsgid = handler->conf->init_gid;
|
||||
|
||||
if (!lxc_switch_uid_gid(nsuid, nsgid))
|
||||
goto out_warn_father;
|
||||
|
||||
/* Drop groups only after we switched to a valid gid in the new
|
||||
* user namespace.
|
||||
*/
|
||||
ret = lxc_setgroups(0, NULL);
|
||||
if (ret < 0 && (handler->am_root || errno != EPERM))
|
||||
if (!lxc_setgroups(0, NULL) &&
|
||||
(handler->am_root || errno != EPERM))
|
||||
goto out_warn_father;
|
||||
|
||||
ret = prctl(PR_SET_DUMPABLE, prctl_arg(1), prctl_arg(0),
|
||||
@ -1355,25 +1354,27 @@ static int do_start(void *data)
|
||||
new_uid = handler->conf->init_uid;
|
||||
new_gid = handler->conf->init_gid;
|
||||
|
||||
/* If we are in a new user namespace we already dropped all groups when
|
||||
* we switched to root in the new user namespace further above. Only
|
||||
* drop groups if we can, so ensure that we have necessary privilege.
|
||||
*/
|
||||
#if HAVE_LIBCAP
|
||||
have_cap_setgid = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE);
|
||||
#else
|
||||
have_cap_setgid = false;
|
||||
#endif
|
||||
if (lxc_list_empty(&handler->conf->id_map) && have_cap_setgid) {
|
||||
ret = lxc_setgroups(0, NULL);
|
||||
if (ret < 0)
|
||||
goto out_warn_father;
|
||||
}
|
||||
/* Avoid unnecessary syscalls. */
|
||||
if (new_uid == nsuid)
|
||||
new_uid = LXC_INVALID_UID;
|
||||
|
||||
ret = lxc_switch_uid_gid(new_uid, new_gid);
|
||||
if (ret < 0)
|
||||
if (new_gid == nsgid)
|
||||
new_gid = LXC_INVALID_GID;
|
||||
|
||||
if (!lxc_switch_uid_gid(new_uid, new_gid))
|
||||
goto out_warn_father;
|
||||
|
||||
/* If we are in a new user namespace we already dropped all groups when
|
||||
* we switched to root in the new user namespace further above. Only
|
||||
* drop groups if we can, so ensure that we have necessary privilege.
|
||||
*/
|
||||
if (lxc_list_empty(&handler->conf->id_map))
|
||||
#if HAVE_LIBCAP
|
||||
if (lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE))
|
||||
#endif
|
||||
if (!lxc_setgroups(0, NULL))
|
||||
goto out_warn_father;
|
||||
|
||||
ret = lxc_ambient_caps_down();
|
||||
if (ret < 0) {
|
||||
ERROR("Failed to clear ambient capabilities");
|
||||
|
@ -50,12 +50,10 @@ int lxc_rsync_exec_wrapper(void *data)
|
||||
int ret;
|
||||
struct rsync_data_char *args = data;
|
||||
|
||||
ret = lxc_switch_uid_gid(0, 0);
|
||||
if (ret < 0)
|
||||
if (!lxc_switch_uid_gid(0, 0))
|
||||
return -1;
|
||||
|
||||
ret = lxc_setgroups(0, NULL);
|
||||
if (ret < 0)
|
||||
if (!lxc_setgroups(0, NULL))
|
||||
return -1;
|
||||
|
||||
return lxc_rsync_exec(args->src, args->dest);
|
||||
@ -117,12 +115,10 @@ int lxc_rsync(struct rsync_data *data)
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = lxc_switch_uid_gid(0, 0);
|
||||
if (ret < 0)
|
||||
if (!lxc_switch_uid_gid(0, 0))
|
||||
return -1;
|
||||
|
||||
ret = lxc_setgroups(0, NULL);
|
||||
if (ret < 0)
|
||||
if (!lxc_setgroups(0, NULL))
|
||||
return -1;
|
||||
|
||||
src = lxc_storage_get_path(orig->dest, orig->type);
|
||||
|
@ -72,6 +72,8 @@ static const struct option my_longopts[] = {
|
||||
{"set-var", required_argument, 0, 'v'},
|
||||
{"pty-log", required_argument, 0, 'L'},
|
||||
{"rcfile", required_argument, 0, 'f'},
|
||||
{"uid", required_argument, 0, 'u'},
|
||||
{"gid", required_argument, 0, 'g'},
|
||||
LXC_COMMON_OPTIONS
|
||||
};
|
||||
|
||||
@ -122,6 +124,8 @@ Options :\n\
|
||||
multiple times.\n\
|
||||
-f, --rcfile=FILE\n\
|
||||
Load configuration file FILE\n\
|
||||
-u, --uid=UID Execute COMMAND with UID inside the container\n\
|
||||
-g, --gid=GID Execute COMMAND with GID inside the container\n\
|
||||
",
|
||||
.options = my_longopts,
|
||||
.parser = my_parser,
|
||||
@ -187,6 +191,14 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg)
|
||||
case 'f':
|
||||
args->rcfile = arg;
|
||||
break;
|
||||
case 'u':
|
||||
if (lxc_safe_uint(arg, &args->uid) < 0)
|
||||
return -1;
|
||||
break;
|
||||
case 'g':
|
||||
if (lxc_safe_uint(arg, &args->gid) < 0)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -333,6 +345,12 @@ int main(int argc, char *argv[])
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (my_args.uid)
|
||||
attach_options.uid = my_args.uid;
|
||||
|
||||
if (my_args.gid)
|
||||
attach_options.gid = my_args.gid;
|
||||
|
||||
if (command.program)
|
||||
ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, &pid);
|
||||
else
|
||||
|
@ -544,7 +544,34 @@ uid_t get_ns_uid(uid_t orig)
|
||||
}
|
||||
}
|
||||
|
||||
nsid = 0;
|
||||
nsid = LXC_INVALID_UID;
|
||||
|
||||
found:
|
||||
fclose(f);
|
||||
free(line);
|
||||
return nsid;
|
||||
}
|
||||
|
||||
gid_t get_ns_gid(gid_t orig)
|
||||
{
|
||||
char *line = NULL;
|
||||
size_t sz = 0;
|
||||
gid_t nsid, hostid, range;
|
||||
FILE *f = fopen("/proc/self/gid_map", "r");
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
while (getline(&line, &sz, f) != -1) {
|
||||
if (sscanf(line, "%u %u %u", &nsid, &hostid, &range) != 3)
|
||||
continue;
|
||||
|
||||
if (hostid <= orig && hostid + range > orig) {
|
||||
nsid += orig - hostid;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
|
||||
nsid = LXC_INVALID_GID;
|
||||
|
||||
found:
|
||||
fclose(f);
|
||||
@ -1324,33 +1351,41 @@ int lxc_preserve_ns(const int pid, const char *ns)
|
||||
return open(path, O_RDONLY | O_CLOEXEC);
|
||||
}
|
||||
|
||||
int lxc_switch_uid_gid(uid_t uid, gid_t gid)
|
||||
bool lxc_switch_uid_gid(uid_t uid, gid_t gid)
|
||||
{
|
||||
if (setgid(gid) < 0) {
|
||||
SYSERROR("Failed to switch to gid %d.", gid);
|
||||
return -errno;
|
||||
}
|
||||
NOTICE("Switched to gid %d.", gid);
|
||||
int ret = 0;
|
||||
|
||||
if (setuid(uid) < 0) {
|
||||
SYSERROR("Failed to switch to uid %d.", uid);
|
||||
return -errno;
|
||||
if (gid != LXC_INVALID_GID) {
|
||||
ret = setgid(gid);
|
||||
if (ret < 0) {
|
||||
SYSERROR("Failed to switch to gid %d", gid);
|
||||
return false;
|
||||
}
|
||||
NOTICE("Switched to gid %d", gid);
|
||||
}
|
||||
NOTICE("Switched to uid %d.", uid);
|
||||
|
||||
return 0;
|
||||
if (uid != LXC_INVALID_UID) {
|
||||
ret = setuid(uid);
|
||||
if (ret < 0) {
|
||||
SYSERROR("Failed to switch to uid %d", uid);
|
||||
return false;
|
||||
}
|
||||
NOTICE("Switched to uid %d", uid);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Simple covenience function which enables uniform logging. */
|
||||
int lxc_setgroups(int size, gid_t list[])
|
||||
bool lxc_setgroups(int size, gid_t list[])
|
||||
{
|
||||
if (setgroups(size, list) < 0) {
|
||||
SYSERROR("Failed to setgroups().");
|
||||
return -errno;
|
||||
SYSERROR("Failed to setgroups()");
|
||||
return false;
|
||||
}
|
||||
NOTICE("Dropped additional groups.");
|
||||
NOTICE("Dropped additional groups");
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int lxc_get_unused_loop_dev_legacy(char *loop_name)
|
||||
|
@ -328,6 +328,10 @@ inline static bool am_host_unpriv(void)
|
||||
* parse /proc/self/uid_map to find what @orig maps to
|
||||
*/
|
||||
extern uid_t get_ns_uid(uid_t orig);
|
||||
/*
|
||||
* parse /proc/self/gid_map to find what @orig maps to
|
||||
*/
|
||||
extern gid_t get_ns_gid(gid_t orig);
|
||||
|
||||
extern bool dir_exists(const char *path);
|
||||
|
||||
@ -354,9 +358,11 @@ extern int lxc_preserve_ns(const int pid, const char *ns);
|
||||
/* Check whether a signal is blocked by a process. */
|
||||
extern bool task_blocks_signal(pid_t pid, int signal);
|
||||
|
||||
/* Switch to a new uid and gid. */
|
||||
extern int lxc_switch_uid_gid(uid_t uid, gid_t gid);
|
||||
extern int lxc_setgroups(int size, gid_t list[]);
|
||||
/* Switch to a new uid and gid.
|
||||
* If LXC_INVALID_{G,U}ID is passed then the set{g,u}id() will not be called.
|
||||
*/
|
||||
extern bool lxc_switch_uid_gid(uid_t uid, gid_t gid);
|
||||
extern bool lxc_setgroups(int size, gid_t list[]);
|
||||
|
||||
/* Find an unused loop device and associate it with source. */
|
||||
extern int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags);
|
||||
|
Loading…
Reference in New Issue
Block a user