mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-09 21:20:48 +00:00
terminal: safely allocate pts devices from inside the container
This was a year long journey which seems to finally have come to an end. Closes: #1620. Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
This commit is contained in:
parent
f3bbb01f8a
commit
f797f05e6e
@ -131,3 +131,8 @@ This adds time namespace support to LXC.
|
|||||||
## seccomp\_allow\_deny\_syntax
|
## seccomp\_allow\_deny\_syntax
|
||||||
|
|
||||||
This adds the ability to use "denylist" and "allowlist" in seccomp v2 policies.
|
This adds the ability to use "denylist" and "allowlist" in seccomp v2 policies.
|
||||||
|
|
||||||
|
## devpts\_fd
|
||||||
|
|
||||||
|
This adds the ability to allocate a file descriptor for the devpts instance of
|
||||||
|
the container.
|
||||||
|
@ -43,6 +43,7 @@ static char *api_extensions[] = {
|
|||||||
"network_bridge_vlan",
|
"network_bridge_vlan",
|
||||||
"time_namespace",
|
"time_namespace",
|
||||||
"seccomp_allow_deny_syntax",
|
"seccomp_allow_deny_syntax",
|
||||||
|
"devpts_fd",
|
||||||
};
|
};
|
||||||
|
|
||||||
static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions);
|
static size_t nr_api_extensions = sizeof(api_extensions) / sizeof(*api_extensions);
|
||||||
|
@ -889,14 +889,14 @@ on_error:
|
|||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lxc_attach_terminal(struct lxc_conf *conf,
|
static int lxc_attach_terminal(const char *name, const char *lxcpath, struct lxc_conf *conf,
|
||||||
struct lxc_terminal *terminal)
|
struct lxc_terminal *terminal)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
lxc_terminal_init(terminal);
|
lxc_terminal_init(terminal);
|
||||||
|
|
||||||
ret = lxc_terminal_create(terminal);
|
ret = lxc_terminal_create(name, lxcpath, terminal);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return log_error(-1, "Failed to create terminal");
|
return log_error(-1, "Failed to create terminal");
|
||||||
|
|
||||||
@ -1091,7 +1091,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options->attach_flags & LXC_ATTACH_TERMINAL) {
|
if (options->attach_flags & LXC_ATTACH_TERMINAL) {
|
||||||
ret = lxc_attach_terminal(conf, &terminal);
|
ret = lxc_attach_terminal(name, lxcpath, conf, &terminal);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
ERROR("Failed to setup new terminal");
|
ERROR("Failed to setup new terminal");
|
||||||
free(cwd);
|
free(cwd);
|
||||||
|
@ -86,6 +86,7 @@ static const char *lxc_cmd_str(lxc_cmd_t cmd)
|
|||||||
[LXC_CMD_GET_INIT_PIDFD] = "get_init_pidfd",
|
[LXC_CMD_GET_INIT_PIDFD] = "get_init_pidfd",
|
||||||
[LXC_CMD_GET_LIMITING_CGROUP] = "get_limiting_cgroup",
|
[LXC_CMD_GET_LIMITING_CGROUP] = "get_limiting_cgroup",
|
||||||
[LXC_CMD_GET_LIMITING_CGROUP2_FD] = "get_limiting_cgroup2_fd",
|
[LXC_CMD_GET_LIMITING_CGROUP2_FD] = "get_limiting_cgroup2_fd",
|
||||||
|
[LXC_CMD_GET_DEVPTS_FD] = "get_devpts_fd",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cmd >= LXC_CMD_MAX)
|
if (cmd >= LXC_CMD_MAX)
|
||||||
@ -156,6 +157,11 @@ static int lxc_cmd_rsp_recv(int sock, struct lxc_cmd_rr *cmd)
|
|||||||
rsp->data = INT_TO_PTR(init_pidfd);
|
rsp->data = INT_TO_PTR(init_pidfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cmd->req.cmd == LXC_CMD_GET_DEVPTS_FD) {
|
||||||
|
int devpts_fd = move_fd(fd_rsp);
|
||||||
|
rsp->data = INT_TO_PTR(devpts_fd);
|
||||||
|
}
|
||||||
|
|
||||||
if (rsp->datalen == 0)
|
if (rsp->datalen == 0)
|
||||||
return log_debug(ret,
|
return log_debug(ret,
|
||||||
"Response data length for command \"%s\" is 0",
|
"Response data length for command \"%s\" is 0",
|
||||||
@ -447,6 +453,43 @@ static int lxc_cmd_get_init_pidfd_callback(int fd, struct lxc_cmd_req *req,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lxc_cmd_get_devpts_fd(const char *name, const char *lxcpath)
|
||||||
|
{
|
||||||
|
int ret, stopped;
|
||||||
|
struct lxc_cmd_rr cmd = {
|
||||||
|
.req = {
|
||||||
|
.cmd = LXC_CMD_GET_DEVPTS_FD,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
ret = lxc_cmd(name, &cmd, &stopped, lxcpath, NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
return log_debug_errno(-1, errno, "Failed to process devpts fd command");
|
||||||
|
|
||||||
|
if (cmd.rsp.ret < 0)
|
||||||
|
return log_debug_errno(-EBADF, errno, "Failed to receive devpts fd");
|
||||||
|
|
||||||
|
return PTR_TO_INT(cmd.rsp.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int lxc_cmd_get_devpts_fd_callback(int fd, struct lxc_cmd_req *req,
|
||||||
|
struct lxc_handler *handler,
|
||||||
|
struct lxc_epoll_descr *descr)
|
||||||
|
{
|
||||||
|
struct lxc_cmd_rsp rsp = {
|
||||||
|
.ret = 0,
|
||||||
|
};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!handler->conf || handler->conf->devpts_fd < 0)
|
||||||
|
rsp.ret = -EBADF;
|
||||||
|
ret = lxc_abstract_unix_send_fds(fd, &handler->conf->devpts_fd, 1, &rsp, sizeof(rsp));
|
||||||
|
if (ret < 0)
|
||||||
|
return log_error(LXC_CMD_REAP_CLIENT_FD, "Failed to send devpts fd");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* lxc_cmd_get_clone_flags: Get clone flags container was spawned with
|
* lxc_cmd_get_clone_flags: Get clone flags container was spawned with
|
||||||
*
|
*
|
||||||
@ -1505,6 +1548,7 @@ static int lxc_cmd_process(int fd, struct lxc_cmd_req *req,
|
|||||||
[LXC_CMD_GET_INIT_PIDFD] = lxc_cmd_get_init_pidfd_callback,
|
[LXC_CMD_GET_INIT_PIDFD] = lxc_cmd_get_init_pidfd_callback,
|
||||||
[LXC_CMD_GET_LIMITING_CGROUP] = lxc_cmd_get_limiting_cgroup_callback,
|
[LXC_CMD_GET_LIMITING_CGROUP] = lxc_cmd_get_limiting_cgroup_callback,
|
||||||
[LXC_CMD_GET_LIMITING_CGROUP2_FD] = lxc_cmd_get_limiting_cgroup2_fd_callback,
|
[LXC_CMD_GET_LIMITING_CGROUP2_FD] = lxc_cmd_get_limiting_cgroup2_fd_callback,
|
||||||
|
[LXC_CMD_GET_DEVPTS_FD] = lxc_cmd_get_devpts_fd_callback,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (req->cmd >= LXC_CMD_MAX)
|
if (req->cmd >= LXC_CMD_MAX)
|
||||||
|
@ -41,6 +41,7 @@ typedef enum {
|
|||||||
LXC_CMD_GET_INIT_PIDFD,
|
LXC_CMD_GET_INIT_PIDFD,
|
||||||
LXC_CMD_GET_LIMITING_CGROUP,
|
LXC_CMD_GET_LIMITING_CGROUP,
|
||||||
LXC_CMD_GET_LIMITING_CGROUP2_FD,
|
LXC_CMD_GET_LIMITING_CGROUP2_FD,
|
||||||
|
LXC_CMD_GET_DEVPTS_FD,
|
||||||
LXC_CMD_MAX,
|
LXC_CMD_MAX,
|
||||||
} lxc_cmd_t;
|
} lxc_cmd_t;
|
||||||
|
|
||||||
@ -132,5 +133,6 @@ __hidden extern int lxc_cmd_get_cgroup2_fd(const char *name, const char *lxcpath
|
|||||||
__hidden extern char *lxc_cmd_get_limiting_cgroup_path(const char *name, const char *lxcpath,
|
__hidden extern char *lxc_cmd_get_limiting_cgroup_path(const char *name, const char *lxcpath,
|
||||||
const char *subsystem);
|
const char *subsystem);
|
||||||
__hidden extern int lxc_cmd_get_limiting_cgroup2_fd(const char *name, const char *lxcpath);
|
__hidden extern int lxc_cmd_get_limiting_cgroup2_fd(const char *name, const char *lxcpath);
|
||||||
|
__hidden extern int lxc_cmd_get_devpts_fd(const char *name, const char *lxcpath);
|
||||||
|
|
||||||
#endif /* __commands_h */
|
#endif /* __commands_h */
|
||||||
|
@ -1472,13 +1472,16 @@ static const struct id_map *find_mapped_nsid_entry(const struct lxc_conf *conf,
|
|||||||
return retmap;
|
return retmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lxc_setup_devpts(struct lxc_conf *conf)
|
static int lxc_setup_devpts(struct lxc_handler *handler)
|
||||||
{
|
{
|
||||||
|
__do_close int devpts_fd = -EBADF;
|
||||||
int ret;
|
int ret;
|
||||||
char **opts;
|
char **opts;
|
||||||
char devpts_mntopts[256];
|
char devpts_mntopts[256];
|
||||||
char *mntopt_sets[5];
|
char *mntopt_sets[5];
|
||||||
char default_devpts_mntopts[256] = "gid=5,newinstance,ptmxmode=0666,mode=0620";
|
char default_devpts_mntopts[256] = "gid=5,newinstance,ptmxmode=0666,mode=0620";
|
||||||
|
struct lxc_conf *conf = handler->conf;
|
||||||
|
int sock = handler->data_sock[0];
|
||||||
|
|
||||||
if (conf->pty_max <= 0)
|
if (conf->pty_max <= 0)
|
||||||
return log_debug(0, "No new devpts instance will be mounted since no pts devices are requested");
|
return log_debug(0, "No new devpts instance will be mounted since no pts devices are requested");
|
||||||
@ -1521,6 +1524,16 @@ static int lxc_setup_devpts(struct lxc_conf *conf)
|
|||||||
return log_error_errno(-1, errno, "Failed to mount new devpts instance");
|
return log_error_errno(-1, errno, "Failed to mount new devpts instance");
|
||||||
DEBUG("Mount new devpts instance with options \"%s\"", *opts);
|
DEBUG("Mount new devpts instance with options \"%s\"", *opts);
|
||||||
|
|
||||||
|
devpts_fd = open_tree(-EBADF, "/dev/pts", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_EMPTY_PATH);
|
||||||
|
if (devpts_fd < 0) {
|
||||||
|
TRACE("Failed to create detached devpts mount");
|
||||||
|
ret = lxc_abstract_unix_send_fds(sock, NULL, 0, NULL, 0);
|
||||||
|
} else {
|
||||||
|
ret = lxc_abstract_unix_send_fds(sock, &devpts_fd, 1, NULL, 0);
|
||||||
|
}
|
||||||
|
if (ret < 0)
|
||||||
|
return log_error_errno(-1, errno, "Failed to send devpts fd to parent");
|
||||||
|
|
||||||
/* Remove any pre-existing /dev/ptmx file. */
|
/* Remove any pre-existing /dev/ptmx file. */
|
||||||
ret = remove("/dev/ptmx");
|
ret = remove("/dev/ptmx");
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -3327,7 +3340,7 @@ int lxc_setup(struct lxc_handler *handler)
|
|||||||
if (lxc_conf->autodev > 0)
|
if (lxc_conf->autodev > 0)
|
||||||
(void)lxc_setup_boot_id();
|
(void)lxc_setup_boot_id();
|
||||||
|
|
||||||
ret = lxc_setup_devpts(lxc_conf);
|
ret = lxc_setup_devpts(handler);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return log_error(-1, "Failed to setup new devpts instance");
|
return log_error(-1, "Failed to setup new devpts instance");
|
||||||
|
|
||||||
|
@ -292,6 +292,8 @@ struct lxc_conf {
|
|||||||
struct lxc_terminal console;
|
struct lxc_terminal console;
|
||||||
/* maximum pty devices allowed by devpts mount */
|
/* maximum pty devices allowed by devpts mount */
|
||||||
size_t pty_max;
|
size_t pty_max;
|
||||||
|
/* file descriptor for the container's /dev/pts mount */
|
||||||
|
int devpts_fd;
|
||||||
|
|
||||||
/* set to true when rootfs has been setup */
|
/* set to true when rootfs has been setup */
|
||||||
bool rootfs_setup;
|
bool rootfs_setup;
|
||||||
|
@ -608,6 +608,16 @@ static int do_lxcapi_init_pidfd(struct lxc_container *c)
|
|||||||
|
|
||||||
WRAP_API(int, lxcapi_init_pidfd)
|
WRAP_API(int, lxcapi_init_pidfd)
|
||||||
|
|
||||||
|
static int do_lxcapi_devpts_fd(struct lxc_container *c)
|
||||||
|
{
|
||||||
|
if (!c)
|
||||||
|
return ret_errno(EBADF);
|
||||||
|
|
||||||
|
return lxc_cmd_get_devpts_fd(c->name, c->config_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
WRAP_API(int, lxcapi_devpts_fd)
|
||||||
|
|
||||||
static bool load_config_locked(struct lxc_container *c, const char *fname)
|
static bool load_config_locked(struct lxc_container *c, const char *fname)
|
||||||
{
|
{
|
||||||
if (!c->lxc_conf)
|
if (!c->lxc_conf)
|
||||||
@ -5319,6 +5329,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath
|
|||||||
c->unfreeze = lxcapi_unfreeze;
|
c->unfreeze = lxcapi_unfreeze;
|
||||||
c->console = lxcapi_console;
|
c->console = lxcapi_console;
|
||||||
c->console_getfd = lxcapi_console_getfd;
|
c->console_getfd = lxcapi_console_getfd;
|
||||||
|
c->devpts_fd = lxcapi_devpts_fd;
|
||||||
c->init_pid = lxcapi_init_pid;
|
c->init_pid = lxcapi_init_pid;
|
||||||
c->init_pidfd = lxcapi_init_pidfd;
|
c->init_pidfd = lxcapi_init_pidfd;
|
||||||
c->load_config = lxcapi_load_config;
|
c->load_config = lxcapi_load_config;
|
||||||
|
@ -865,6 +865,15 @@ struct lxc_container {
|
|||||||
* \return pidfd of init process of the container.
|
* \return pidfd of init process of the container.
|
||||||
*/
|
*/
|
||||||
int (*init_pidfd)(struct lxc_container *c);
|
int (*init_pidfd)(struct lxc_container *c);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Retrieve a mount fd for the container's devpts instance.
|
||||||
|
*
|
||||||
|
* \param c Container.
|
||||||
|
*
|
||||||
|
* \return Mount fd of the container's devpts instance.
|
||||||
|
*/
|
||||||
|
int (*devpts_fd)(struct lxc_container *c);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -980,6 +980,7 @@ void lxc_end(struct lxc_handler *handler)
|
|||||||
|
|
||||||
lxc_terminal_delete(&handler->conf->console);
|
lxc_terminal_delete(&handler->conf->console);
|
||||||
lxc_delete_tty(&handler->conf->ttys);
|
lxc_delete_tty(&handler->conf->ttys);
|
||||||
|
close_prot_errno_disarm(handler->conf->devpts_fd);
|
||||||
|
|
||||||
/* The command socket is now closed, no more state clients can register
|
/* The command socket is now closed, no more state clients can register
|
||||||
* themselves from now on. So free the list of state clients.
|
* themselves from now on. So free the list of state clients.
|
||||||
@ -1959,6 +1960,14 @@ static int lxc_spawn(struct lxc_handler *handler)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = lxc_abstract_unix_recv_fds(data_sock1, &handler->conf->devpts_fd, 1, NULL, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
SYSERROR("Failed to receive devpts fd from child");
|
||||||
|
goto out_delete_net;
|
||||||
|
}
|
||||||
|
if (ret == 0)
|
||||||
|
handler->conf->devpts_fd = -EBADF;
|
||||||
|
|
||||||
/* Now all networks are created, network devices are moved into place,
|
/* Now all networks are created, network devices are moved into place,
|
||||||
* and the correct names and ifindices in the respective namespaces have
|
* and the correct names and ifindices in the respective namespaces have
|
||||||
* been recorded. The corresponding structs have now all been filled. So
|
* been recorded. The corresponding structs have now all been filled. So
|
||||||
|
@ -828,7 +828,7 @@ int lxc_terminal_create_log_file(struct lxc_terminal *terminal)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int lxc_terminal_create(struct lxc_terminal *terminal)
|
static int lxc_terminal_create_foreign(struct lxc_terminal *terminal)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -869,6 +869,59 @@ err:
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int lxc_terminal_create_native(const char *name, const char *lxcpath,
|
||||||
|
struct lxc_terminal *terminal)
|
||||||
|
{
|
||||||
|
__do_close int devpts_fd = -EBADF, ptx_fd = -EBADF, pty_fd = -EBADF;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
devpts_fd = lxc_cmd_get_devpts_fd(name, lxcpath);
|
||||||
|
if (devpts_fd < 0)
|
||||||
|
return log_error_errno(-1, errno, "Failed to receive devpts fd");
|
||||||
|
|
||||||
|
ptx_fd = openat(devpts_fd, "ptmx", O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||||
|
if (ptx_fd < 0)
|
||||||
|
return log_error_errno(-1, errno, "Failed to open terminal multiplexer device");
|
||||||
|
|
||||||
|
ret = grantpt(ptx_fd);
|
||||||
|
if (ret < 0)
|
||||||
|
return log_error_errno(-1, errno, "Failed to grant access to multiplexer device");
|
||||||
|
|
||||||
|
ret = unlockpt(ptx_fd);
|
||||||
|
if (ret < 0)
|
||||||
|
return log_error_errno(-1, errno, "Failed to unlock multiplexer device device");
|
||||||
|
|
||||||
|
pty_fd = ioctl(ptx_fd, TIOCGPTPEER, O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||||
|
if (pty_fd < 0)
|
||||||
|
return log_error_errno(-1, errno, "Failed to allocate new pty device");
|
||||||
|
|
||||||
|
ret = ttyname_r(terminal->pty, terminal->name, sizeof(terminal->name));
|
||||||
|
if (ret < 0)
|
||||||
|
return log_error_errno(-1, errno, "Failed to retrieve name of terminal pty");
|
||||||
|
|
||||||
|
terminal->ptx = move_fd(ptx_fd);
|
||||||
|
terminal->pty = move_fd(pty_fd);
|
||||||
|
ret = lxc_terminal_peer_default(terminal);
|
||||||
|
if (ret < 0) {
|
||||||
|
ERROR("Failed to allocate proxy terminal");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
lxc_terminal_delete(terminal);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
int lxc_terminal_create(const char *name, const char *lxcpath, struct lxc_terminal *terminal)
|
||||||
|
{
|
||||||
|
if (!lxc_terminal_create_native(name, lxcpath, terminal))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return lxc_terminal_create_foreign(terminal);
|
||||||
|
}
|
||||||
|
|
||||||
int lxc_terminal_setup(struct lxc_conf *conf)
|
int lxc_terminal_setup(struct lxc_conf *conf)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
@ -879,7 +932,7 @@ int lxc_terminal_setup(struct lxc_conf *conf)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = lxc_terminal_create(terminal);
|
ret = lxc_terminal_create_foreign(terminal);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -110,7 +110,8 @@ __hidden extern int lxc_terminal_allocate(struct lxc_conf *conf, int sockfd, in
|
|||||||
* - sets up SIGWINCH handler, winsz, and new terminal settings
|
* - sets up SIGWINCH handler, winsz, and new terminal settings
|
||||||
* (Handlers for SIGWINCH and I/O are not registered in a mainloop.)
|
* (Handlers for SIGWINCH and I/O are not registered in a mainloop.)
|
||||||
*/
|
*/
|
||||||
__hidden extern int lxc_terminal_create(struct lxc_terminal *console);
|
__hidden extern int lxc_terminal_create(const char *name, const char *lxcpath,
|
||||||
|
struct lxc_terminal *console);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* lxc_terminal_setup: Create a new terminal.
|
* lxc_terminal_setup: Create a new terminal.
|
||||||
|
Loading…
Reference in New Issue
Block a user