0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-22 17:51:53 +00:00

Flock: telnet to machines

This commit is contained in:
Maria Matejka 2024-09-27 21:38:56 +02:00
parent d36ad6af6a
commit 56aff8ef39
5 changed files with 210 additions and 132 deletions

View File

@ -8,6 +8,7 @@
#include <poll.h> #include <poll.h>
#include <sched.h> #include <sched.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/mount.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
@ -73,6 +74,39 @@ container_poweroff(int fd, int sig)
exit(0); exit(0);
} }
static void
container_zombie(void)
{
zombie = 0;
log(L_INFO "Zombie elimination routine invoked.");
while (1) {
int status;
pid_t p = waitpid(-1, &status, WNOHANG);
if (p < 0)
{
if (errno != ECHILD)
log(L_ERR "Zombie elimination failed: %m");
return;
}
if (p == 0)
return;
const char *coreinfo = WCOREDUMP(status) ? " (core dumped)" : "";
if (WIFEXITED(status))
log(L_INFO "Process %d ended with status %d%s", p, WEXITSTATUS(status), coreinfo);
else if (WIFSIGNALED(status))
log(L_INFO "Process %d exited by signal %d (%s)%s", p, WTERMSIG(status), strsignal(WTERMSIG(status)), coreinfo);
else
log(L_ERR "Process %d exited with a strange status %d", p, status);
}
}
#define SYSCALL(x, ...) ({ int e = x(__VA_ARGS__); if (e < 0) die("Failed to run %s at %s:%d: %m", #x, __FILE__, __LINE__); e; })
static void static void
container_mainloop(int fd) container_mainloop(int fd)
{ {
@ -82,6 +116,10 @@ container_mainloop(int fd)
signal(SIGINT, container_poweroff_sighandler); signal(SIGINT, container_poweroff_sighandler);
signal(SIGCHLD, container_child_sighandler); signal(SIGCHLD, container_child_sighandler);
/* Remounting proc to reflect the new PID namespace */
SYSCALL(mount, "none", "/", NULL, MS_REC | MS_PRIVATE, NULL);
SYSCALL(mount, "proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC, NULL);
/* TODO: mount overlayfs and chroot */ /* TODO: mount overlayfs and chroot */
while (1) while (1)
{ {
@ -95,13 +133,31 @@ container_mainloop(int fd)
int res = ppoll(&pfd, 1, NULL, &newmask); int res = ppoll(&pfd, 1, NULL, &newmask);
if ((res < 0) && (errno != EINTR))
log(L_INFO "ppoll returned -1: %m");
if (poweroff) if (poweroff)
container_poweroff(fd, poweroff); container_poweroff(fd, poweroff);
if (zombie)
container_zombie();
if (pfd.revents & POLLIN) if (pfd.revents & POLLIN)
{ {
byte buf[128]; int sfd = -1;
ssize_t sz = read(fd, buf, sizeof buf); byte buf[128], cbuf[CMSG_SPACE(sizeof sfd)];
struct iovec v = {
.iov_base = buf,
.iov_len = sizeof buf,
};
struct msghdr m = {
.msg_iov = &v,
.msg_iovlen = 1,
.msg_control = &cbuf,
.msg_controllen = sizeof cbuf,
};
int sz = recvmsg(fd, &m, 0);
if (sz < 0) if (sz < 0)
{ {
log(L_ERR "error reading data from control socket: %m"); log(L_ERR "error reading data from control socket: %m");
@ -116,11 +172,47 @@ container_mainloop(int fd)
container_poweroff(fd, 0); container_poweroff(fd, 0);
break; break;
} case 0x21:
} ASSERT_DIE(buf[2] == 0xf6);
struct cmsghdr *c = CMSG_FIRSTHDR(&m);
memcpy(&sfd, CMSG_DATA(c), sizeof sfd);
/* TODO: check for telnet socket */ int e = fork();
log(L_INFO "woken up, res %d (%m)!", res); if (e < 0) bug("Cannot fork: %m");
if (e == 0) {
int fd = accept(sfd, NULL, 0);
if (fd < 0)
{
log(L_ERR "failed to accept telnet connection: %m");
exit(1);
}
log(L_INFO "telnet connected");
close(0);
close(1);
close(2);
dup2(fd, 0);
dup2(fd, 1);
/* Unblock signals */
sigset_t newmask;
sigemptyset(&newmask);
sigprocmask(SIG_SETMASK, &newmask, NULL);
/* Exec the telnet */
e = execl("/usr/sbin/telnetd", "telnetd", "-E", "/bin/bash", NULL);
log(L_ERR "failed to execl: %m");
exit(42);
}
close(sfd);
break;
default:
log(L_ERR "unknown command on control socket: %d", buf[1]);
break;
}
}
} }
} }
@ -356,6 +448,14 @@ crt_err(sock *s, int err UNUSED)
crt->ccc = NULL; crt->ccc = NULL;
} }
int
container_ctl_fd(const char *name)
{
uint h = mem_hash(name, strlen(name));
struct container_runtime *crt = HASH_FIND(hcf.hash, CRT, name, h);
return (crt && crt->s) ? crt->s->fd : -1;
}
static void static void
container_created(callback *cb) container_created(callback *cb)
{ {

View File

@ -186,16 +186,21 @@ hcs_parse(struct cbor_parser_context *ctx, const byte *buf, s64 size)
break; break;
case 3: /* telnet listener open */ case 3: /* telnet listener open */
if ((ctx->type != 7) || (ctx->value != 22)) if ((ctx->type == 7) && (ctx->value == 22))
CBOR_PARSER_ERROR("Expected null, got %u-%u", ctx->type, ctx->value); {
/* TODO: allow this also for machines */
log(L_INFO "Requested telnet open");
hexp_get_telnet(ctx->sock, NULL); hexp_get_telnet(ctx->sock, NULL);
ctx->major_state = 1; ctx->major_state = 1;
break; break;
}
else if (ctx->type != 3)
CBOR_PARSER_ERROR("Expected null or string, got %u-%u", ctx->type, ctx->value);
ASSERT_DIE(!ctx->target_buf);
ctx->cfg.cf.name = ctx->target_buf = lp_alloc(ctx->lp, ctx->value + 1);
ctx->target_len = ctx->value;
break;
case 4: /* telnet listener close */ case 4: /* telnet listener close */
if ((ctx->type != 7) || (ctx->value != 22)) if ((ctx->type != 7) || (ctx->value != 22))
@ -364,6 +369,11 @@ hcs_parse(struct cbor_parser_context *ctx, const byte *buf, s64 size)
/* Read completely! */ /* Read completely! */
switch (ctx->major_state) switch (ctx->major_state)
{ {
case 3:
hexp_get_telnet(ctx->sock, ctx->cfg.cf.name);
ctx->major_state = 1;
break;
case 5: case 5:
/* Actually not this one */ /* Actually not this one */
CBOR_PARSER_ERROR("NOT IMPLEMENTED YET"); CBOR_PARSER_ERROR("NOT IMPLEMENTED YET");

View File

@ -105,6 +105,12 @@ def container_stop(hypervisor: str, name: str):
for k,v in msg(hypervisor, { 4: { 0: name, }}).items(): for k,v in msg(hypervisor, { 4: { 0: name, }}).items():
print(k,v) print(k,v)
@handler
def container_telnet(hypervisor: str, name: str):
for k,v in msg(hypervisor, { 1: name}).items():
assert(k == -2)
os.execlp("telnet", "telnet", "localhost", str(v))
try: try:
binname = sys.argv.pop(0) binname = sys.argv.pop(0)
except Exception as e: except Exception as e:

View File

@ -48,6 +48,7 @@ union flock_machine_config {
void hypervisor_container_request(sock *s, const char *name, const char *basedir, const char *workdir); void hypervisor_container_request(sock *s, const char *name, const char *basedir, const char *workdir);
void hypervisor_container_shutdown(sock *s, const char *name); void hypervisor_container_shutdown(sock *s, const char *name);
int container_ctl_fd(const char *name);
extern event reboot_event, poweroff_event; extern event reboot_event, poweroff_event;
extern event_list shutdown_event_list; extern event_list shutdown_event_list;

View File

@ -127,6 +127,8 @@ static struct hypervisor_exposed {
pool *p; pool *p;
sock *s; sock *s;
struct birdloop *loop; struct birdloop *loop;
const char *port_name;
sock *port_sreq;
} he; } he;
/** /**
@ -136,7 +138,6 @@ static struct hypervisor_exposed {
static void hexp_received_telnet(void *); static void hexp_received_telnet(void *);
struct hexp_received_telnet { struct hexp_received_telnet {
event e; event e;
struct hexp_telnet_port *p;
int fd; int fd;
u16 port; u16 port;
}; };
@ -165,6 +166,7 @@ hypervisor_telnet_connected(sock *sk, uint size UNUSED)
{ {
log(L_INFO "telnet connected"); log(L_INFO "telnet connected");
close(fd); close(fd);
sk_close(sk);
return 1; return 1;
} }
@ -214,20 +216,12 @@ hypervisor_exposed_parent_rx(sock *sk, uint size UNUSED)
u16 port = ntohs(*((u16 *) &buf[3])); u16 port = ntohs(*((u16 *) &buf[3]));
log(L_INFO "RX %d bytes, fd %d, port %u", e, sfd, port); log(L_INFO "RX %d bytes, fd %d, port %u", e, sfd, port);
sock *skl = sk_new(sk->pool);
skl->type = SK_MAGIC;
skl->rx_hook = hypervisor_telnet_connected;
skl->fd = sfd;
if (sk_open(skl, sk->loop) < 0)
bug("Telnet listener: sk_open failed");
struct hexp_received_telnet *hrt = mb_allocz(he.p, sizeof *hrt); struct hexp_received_telnet *hrt = mb_allocz(he.p, sizeof *hrt);
*hrt = (struct hexp_received_telnet) { *hrt = (struct hexp_received_telnet) {
.e = { .e = {
.hook = hexp_received_telnet, .hook = hexp_received_telnet,
.data = hrt, .data = hrt,
}, },
.p = sk->data,
.port = port, .port = port,
.fd = sfd, .fd = sfd,
}; };
@ -413,86 +407,20 @@ hypervisor_exposed_fork(void)
* Hypervisor's mapping between external ports and names * Hypervisor's mapping between external ports and names
*/ */
#define HEXP_TELNET_KEY(tp) tp->name, tp->hash
#define HEXP_TELNET_NEXT(tp) tp->next
#define HEXP_TELNET_EQ(a,h,b,i) ((h) == (i)) && (!(a) && !(b) || !strcmp(a,b))
#define HEXP_TELNET_FN(a,h) h
#define TLIST_PREFIX hexp_telnet_requestor
#define TLIST_TYPE struct hexp_telnet_requestor
#define TLIST_ITEM n
struct hexp_telnet_requestor {
TLIST_DEFAULT_NODE;
sock *s;
struct cbor_parser_context *ctx;
};
#define TLIST_WANT_ADD_TAIL
#include "lib/tlists.h"
static void static void
hexp_sock_err(sock *s, int err UNUSED) hexp_sock_err(sock *s, int err UNUSED)
{ {
struct hexp_telnet_requestor *req = s->data; ASSERT_DIE(s == he.port_sreq);
s->data = req->ctx; he.port_name = NULL;
he.port_sreq = NULL;
hexp_telnet_requestor_rem_node(hexp_telnet_requestor_enlisted(req), req);
}
struct hexp_telnet_port {
struct hexp_telnet_port *next;
const char *name;
uint hash;
uint port;
TLIST_LIST(hexp_telnet_requestor) requestors;
int fd;
};
static struct hexp_telnet {
pool *pool;
HASH(struct hexp_telnet_port) port_hash;
} hexp_telnet;
static void
hexp_init_telnet(void)
{
pool *p = rp_new(hcs_pool, hcs_pool->domain, "Hypervisor exposed telnets");
hexp_telnet.pool = p;
HASH_INIT(hexp_telnet.port_hash, p, 6);
}
static void
hexp_have_telnet(sock *s, struct hexp_telnet_port *p)
{
struct linpool *lp = lp_new(s->pool);
struct cbor_writer *cw = cbor_init(s->tbuf, s->tbsize, lp);
cbor_open_block_with_length(cw, 1);
cbor_add_int(cw, -2);
cbor_add_int(cw, p->port);
sk_send(s, cw->pt);
rfree(lp);
} }
void void
hexp_get_telnet(sock *s, const char *name) hexp_get_telnet(sock *s, const char *name)
{ {
if (!hexp_telnet.pool) ASSERT_DIE(!he.port_name);
hexp_init_telnet(); he.port_name = name ?: "";
he.port_sreq = s;
uint h = name ? mem_hash(name, strlen(name)) : 0;
struct hexp_telnet_port *p = HASH_FIND(hexp_telnet.port_hash, HEXP_TELNET, name, h);
if (p && p->port)
return hexp_have_telnet(s, p);
else if (!p)
{
he.s->data = p = mb_alloc(hcs_pool, sizeof *p);
*p = (struct hexp_telnet_port) {
.name = name,
.hash = h,
.fd = -1,
};
HASH_INSERT(hexp_telnet.port_hash, HEXP_TELNET, p);
uint8_t buf[64]; uint8_t buf[64];
linpool *lp = lp_new(s->pool); linpool *lp = lp_new(s->pool);
@ -501,53 +429,86 @@ hexp_get_telnet(sock *s, const char *name)
cbor_add_int(cw, 1); cbor_add_int(cw, 1);
cw->cbor[cw->pt++] = 0xf6; cw->cbor[cw->pt++] = 0xf6;
struct iovec v = { int e = write(he.s->fd, buf, cw->pt);
.iov_base = buf,
.iov_len = cw->pt,
};
struct msghdr m = {
.msg_iov = &v,
.msg_iovlen = 1,
};
int e = sendmsg(he.s->fd, &m, 0);
if (e != cw->pt) if (e != cw->pt)
bug("sendmsg error handling not implemented, got %d (%m)", e); bug("write error handling not implemented, got %d (%m)", e);
rfree(lp); rfree(lp);
}
s->err_paused = hexp_sock_err; s->err_paused = hexp_sock_err;
sk_pause_rx(s->loop, s); sk_pause_rx(s->loop, s);
struct hexp_telnet_requestor *req = mb_allocz(hcs_pool, sizeof *req);
req->s = s;
req->ctx = s->data;
s->data = req;
hexp_telnet_requestor_add_tail(&p->requestors, req);
} }
static void hexp_received_telnet(void *_data) static void hexp_received_telnet(void *_data)
{ {
struct hexp_received_telnet *hrt = _data; struct hexp_received_telnet *hrt = _data;
ASSERT_DIE(!hrt->p->port); ASSERT_DIE(he.port_name);
hrt->p->port = hrt->port; const char *name = he.port_name;
hrt->p->fd = hrt->fd; he.port_name = NULL;
sock *s = he.port_sreq;
he.port_sreq = NULL;
if (name[0])
{
/* Transferring the received listening socket to the container */
int fd = container_ctl_fd(name);
/* TODO: unduplicate this code */
byte outbuf[128]; byte outbuf[128];
linpool *lp = lp_new(hcs_pool); linpool *lp = lp_new(hcs_pool);
struct cbor_writer *cw = cbor_init(outbuf, sizeof outbuf, lp); struct cbor_writer *cw = cbor_init(outbuf, sizeof outbuf, lp);
cbor_open_block_with_length(cw, 1); cbor_open_block_with_length(cw, 1);
cbor_add_int(cw, -2); cbor_add_int(cw, -2);
write_item(cw, 7, 22);
struct iovec v = {
.iov_base = outbuf,
.iov_len = cw->pt,
};
byte cbuf[CMSG_SPACE(sizeof hrt->fd)];
struct msghdr m = {
.msg_iov = &v,
.msg_iovlen = 1,
.msg_control = &cbuf,
.msg_controllen = sizeof cbuf,
};
struct cmsghdr *c = CMSG_FIRSTHDR(&m);
c->cmsg_level = SOL_SOCKET;
c->cmsg_type = SCM_RIGHTS;
c->cmsg_len = CMSG_LEN(sizeof hrt->fd);
memcpy(CMSG_DATA(c), &hrt->fd, sizeof hrt->fd);
int e = sendmsg(fd, &m, 0);
if (e < 0)
log(L_ERR "Failed to send socket: %m");
close(hrt->fd);
}
else
{
/* Opening listener here */
sock *skl = sk_new(hcs_pool);
skl->type = SK_MAGIC;
skl->rx_hook = hypervisor_telnet_connected;
skl->data = skl;
skl->fd = hrt->fd;
if (sk_open(skl, hcs_loop) < 0)
bug("Telnet listener: sk_open failed");
}
if (s)
{
linpool *lp = lp_new(hcs_pool);
struct cbor_writer *cw = cbor_init(s->tbuf, s->tbsize, lp);
cbor_open_block_with_length(cw, 1);
cbor_add_int(cw, -2);
cbor_add_int(cw, hrt->port); cbor_add_int(cw, hrt->port);
WALK_TLIST_DELSAFE(hexp_telnet_requestor, r, &hrt->p->requestors) sk_send(s, cw->pt);
{ sk_resume_rx(hcs_loop, s);
sk_resume_rx(r->s->loop, r->s); rfree(lp);
memcpy(r->s->tbuf, outbuf, cw->pt);
sk_send(r->s, cw->pt);
hexp_telnet_requestor_rem_node(&hrt->p->requestors, r);
} }
birdloop_enter(he.loop); birdloop_enter(he.loop);