From ddb2fa0d4b8578587b688a9e916f4822e5a75ec2 Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Tue, 15 Jun 2021 05:11:44 +0000 Subject: [PATCH] Hallelujah --- Makefile | 18 ++-- backend/api.js | 29 +++++-- backend/sandbox.js | 18 +++- backend/util.js | 4 - docker/admin/Dockerfile | 2 +- docker/app/Dockerfile | 2 +- docker/base/Dockerfile | 2 +- docker/ci/Dockerfile | 2 +- docker/packaging/Dockerfile | 2 +- docker/runtime/Dockerfile | 2 +- docker/shared/admin-pid1.bash | 8 +- system/src/riju-system-privileged.c | 129 +++++++++++++++------------- 12 files changed, 121 insertions(+), 97 deletions(-) diff --git a/Makefile b/Makefile index dc34ee2..1a607c6 100644 --- a/Makefile +++ b/Makefile @@ -73,32 +73,24 @@ endif IMAGE_HASH := -e RIJU_IMAGE_HASH="$$(docker inspect riju:$(LANG_TAG) | jq '.[0].Config.Labels["riju.image-hash"]' -r)" -shell: # I= [L=] [E=1] [P1|P2=] : Launch Docker image with shell +shell: # I= [L=] [E[E]=1] [P1|P2=] : Launch Docker image with shell @: $${I} ifneq (,$(filter $(I),admin ci)) @mkdir -p $(HOME)/.aws $(HOME)/.docker $(HOME)/.ssh $(HOME)/.terraform.d - docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src -v /var/run/docker.sock:/var/run/docker.sock -v $(HOME)/.aws:/var/riju/.aws -v $(HOME)/.docker:/var/riju/.docker -v $(HOME)/.ssh:/var/riju/.ssh -v $(HOME)/.terraform.d:/var/riju/.terraform.d -e AWS_REGION -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e DOCKER_USERNAME -e DOCKER_PASSWORD -e DEPLOY_SSH_PRIVATE_KEY -e DOCKER_REPO -e S3_BUCKET -e DOMAIN -e VOLUME_MOUNT=$(VOLUME_MOUNT) $(SHELL_PORTS) $(SHELL_ENV) $(IMAGE_HASH) --network host riju:$(I) $(BASH_CMD) + docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src -v /var/run/docker.sock:/var/run/docker.sock -v $(HOME)/.aws:/var/run/riju/.aws -v $(HOME)/.docker:/var/run/riju/.docker -v $(HOME)/.ssh:/var/run/riju/.ssh -v $(HOME)/.terraform.d:/var/run/riju/.terraform.d -e AWS_REGION -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e DOCKER_USERNAME -e DOCKER_PASSWORD -e DEPLOY_SSH_PRIVATE_KEY -e DOCKER_REPO -e S3_BUCKET -e DOMAIN -e VOLUME_MOUNT=$(VOLUME_MOUNT) $(SHELL_PORTS) $(SHELL_ENV) $(IMAGE_HASH) --network host riju:$(I) $(BASH_CMD) else ifeq ($(I),app) - docker run -it --rm --hostname $(I) $(SHELL_PORTS) $(SHELL_ENV) $(IMAGE_HASH) riju:$(I) $(BASH_CMD) + docker run -it --rm --hostname $(I) -v /var/run/riju:/var/run/riju -v /var/run/docker.sock:/var/run/docker.sock $(SHELL_PORTS) $(SHELL_ENV) $(IMAGE_HASH) riju:$(I) $(BASH_CMD) else ifneq (,$(filter $(I),base lang)) ifeq ($(I),lang) @: $${L} endif - docker run -it --rm --hostname $(LANG_TAG) -v $(VOLUME_MOUNT):/src --label riju-install-target=yes $(SHELL_PORTS) $(SHELL_ENV) $(IMAGE_HASH) riju:$(LANG_TAG) $(BASH_CMD) + docker run -it --rm --hostname $(LANG_TAG) -v $(VOLUME_MOUNT):/src $(SHELL_PORTS) $(SHELL_ENV) $(IMAGE_HASH) riju:$(LANG_TAG) $(BASH_CMD) else ifeq ($(I),runtime) - docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src -v /var/run/docker.sock:/var/run/docker.sock $(SHELL_PORTS) $(SHELL_ENV) $(IMAGE_HASH) riju:$(I) $(BASH_CMD) + docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src -v /var/run/riju:/var/run/riju -v /var/run/docker.sock:/var/run/docker.sock $(SHELL_PORTS) $(SHELL_ENV) $(IMAGE_HASH) riju:$(I) $(BASH_CMD) else docker run -it --rm --hostname $(I) -v $(VOLUME_MOUNT):/src $(SHELL_PORTS) $(SHELL_ENV) $(IMAGE_HASH) riju:$(I) $(BASH_CMD) endif -## This is equivalent to 'make pkg' in a fresh packaging container -## followed by 'make install' in a persistent runtime container. - -repkg: script # L= T= : Build fresh .deb and install into live container - @: $${L} $${T} - $(MAKE_QUIETLY) shell I=packaging CMD="make pkg L=$(L) T=$(T)" - ctr="$$(docker container ls -f label="riju-install-target=yes" -l -q)"; test "$${ctr}" || (echo "no valid container is live"; exit 1); docker exec "$${ctr}" make install L=$(L) T=$(T) - ### Build packaging scripts script: # L= T= : Generate a packaging script diff --git a/backend/api.js b/backend/api.js index a39fa23..6030037 100644 --- a/backend/api.js +++ b/backend/api.js @@ -48,7 +48,6 @@ export class Session { }; privilegedSession = () => util.privilegedSession(this.context); - privilegedWait = () => util.privilegedWait(this.context); privilegedExec = (cmdline) => util.privilegedExec(this.context, bash(cmdline)); privilegedPty = (cmdline) => @@ -64,13 +63,6 @@ export class Session { this.container = { pty: containerPty, }; - containerPty.on("data", (data) => - this.send({ - event: "serviceLog", - service: "container", - output: data.toString("utf8"), - }) - ); containerPty.on("close", (code, signal) => this.send({ event: "serviceFailed", @@ -85,7 +77,26 @@ export class Session { error: `${err}`, }) ); - await this.run(this.privilegedWait(this.context)); + let buffer = ""; + await new Promise((resolve) => { + containerPty.on("data", (data) => { + buffer += data; + let idx; + while ((idx = buffer.indexOf("\r\n")) !== -1) { + const line = buffer.slice(0, idx); + buffer = buffer.slice(idx + 2); + if (line === "riju: container ready") { + resolve(); + } else { + this.send({ + event: "serviceLog", + service: "container", + output: line + "\n", + }) + } + } + }); + }); if (this.config.setup) { await this.run(this.privilegedExec(this.config.setup)); } diff --git a/backend/sandbox.js b/backend/sandbox.js index 4f8e470..e0fc67a 100644 --- a/backend/sandbox.js +++ b/backend/sandbox.js @@ -11,7 +11,6 @@ import { privilegedExec, privilegedPty, privilegedSession, - privilegedWait, quote, run, } from "./util.js"; @@ -38,7 +37,22 @@ async function main() { const session = pty.spawn(sessionArgs[0], sessionArgs.slice(1), { name: "xterm-color", }); - await run(privilegedWait({ uuid }), log); + let buffer = ""; + await new Promise((resolve) => { + session.on("data", (data) => { + buffer += data; + let idx; + while ((idx = buffer.indexOf("\r\n")) !== -1) { + const line = buffer.slice(0, idx); + buffer = buffer.slice(idx + 2); + if (line === "riju: container ready") { + resolve(); + } else { + console.error(line); + } + } + }); + }); const args = privilegedPty( { uuid }, bash( diff --git a/backend/util.js b/backend/util.js index 5a205a2..52a2b2a 100644 --- a/backend/util.js +++ b/backend/util.js @@ -51,10 +51,6 @@ export function privilegedSession({ uuid, lang }) { return [rijuSystemPrivileged, "session", uuid, lang]; } -export function privilegedWait({ uuid }) { - return [rijuSystemPrivileged, "wait", uuid]; -} - export function privilegedExec({ uuid }, args) { return [rijuSystemPrivileged, "exec", uuid].concat(args); } diff --git a/docker/admin/Dockerfile b/docker/admin/Dockerfile index 12ee1dc..ea76193 100644 --- a/docker/admin/Dockerfile +++ b/docker/admin/Dockerfile @@ -6,5 +6,5 @@ RUN /tmp/install.bash WORKDIR /src COPY docker/shared/my_init /usr/local/sbin/ COPY docker/shared/admin-pid1.bash /usr/local/sbin/pid1.bash -ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--", "/usr/local/sbin/pid1.bash"] +ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--skip-runit", "--", "/usr/local/sbin/pid1.bash"] CMD ["bash"] diff --git a/docker/app/Dockerfile b/docker/app/Dockerfile index 7b8a710..b970056 100644 --- a/docker/app/Dockerfile +++ b/docker/app/Dockerfile @@ -24,7 +24,7 @@ COPY langs ./langs/ FROM riju:runtime -ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--"] +ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--skip-runit","--"] RUN useradd -p '!' -m -l -s /usr/bin/bash riju COPY --chown=riju:riju --from=build /src ./ RUN chown root:riju system/out/*-privileged && chmod a=,g=rx,u=rwxs system/out/*-privileged diff --git a/docker/base/Dockerfile b/docker/base/Dockerfile index 9d23860..60e18e5 100644 --- a/docker/base/Dockerfile +++ b/docker/base/Dockerfile @@ -8,5 +8,5 @@ RUN runuser -u riju -- mkdir /home/riju/src WORKDIR /home/riju/src COPY docker/shared/my_init /usr/local/sbin/ -ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--"] +ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--skip-runit", "--"] CMD ["bash"] diff --git a/docker/ci/Dockerfile b/docker/ci/Dockerfile index 19389e9..8098cb4 100644 --- a/docker/ci/Dockerfile +++ b/docker/ci/Dockerfile @@ -6,5 +6,5 @@ RUN /tmp/install.bash WORKDIR /src COPY docker/shared/my_init /usr/local/sbin/ COPY docker/shared/admin-pid1.bash /usr/local/sbin/pid1.bash -ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--", "/usr/local/sbin/pid1.bash"] +ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--skip-runit", "--", "/usr/local/sbin/pid1.bash"] CMD ["bash"] diff --git a/docker/packaging/Dockerfile b/docker/packaging/Dockerfile index 899afb8..ba528b1 100644 --- a/docker/packaging/Dockerfile +++ b/docker/packaging/Dockerfile @@ -5,5 +5,5 @@ RUN /tmp/install.bash WORKDIR /src COPY docker/shared/my_init docker/packaging/pid1.bash /usr/local/sbin/ -ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--", "/usr/local/sbin/pid1.bash"] +ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--skip-runit", "--", "/usr/local/sbin/pid1.bash"] CMD ["bash"] diff --git a/docker/runtime/Dockerfile b/docker/runtime/Dockerfile index 0ac0212..ac1118d 100644 --- a/docker/runtime/Dockerfile +++ b/docker/runtime/Dockerfile @@ -4,7 +4,7 @@ COPY docker/runtime/install.bash /tmp/ RUN /tmp/install.bash COPY docker/shared/my_init docker/runtime/pid1.bash /usr/local/sbin/ -ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--", "/usr/local/sbin/pid1.bash"] +ENTRYPOINT ["/usr/local/sbin/my_init", "--quiet", "--skip-runit", "--", "/usr/local/sbin/pid1.bash"] WORKDIR /src CMD ["bash"] diff --git a/docker/shared/admin-pid1.bash b/docker/shared/admin-pid1.bash index 6fee836..e126a75 100755 --- a/docker/shared/admin-pid1.bash +++ b/docker/shared/admin-pid1.bash @@ -7,10 +7,10 @@ tee -a /etc/hosts >/dev/null <<< "127.0.0.1 $(hostname)" groupadd -g "$(stat -c %g "$PWD")" -o -p '!' -r riju useradd -u "$(stat -c %u "$PWD")" -g "$(stat -c %g "$PWD")" -o -p '!' -m -N -l -s /usr/bin/bash -G sudo riju -runuser -u riju -- ln -sT /var/riju/.aws /home/riju/.aws -runuser -u riju -- ln -sT /var/riju/.docker /home/riju/.docker -runuser -u riju -- ln -sT /var/riju/.ssh /home/riju/.ssh -runuser -u riju -- ln -sT /var/riju/.terraform.d /home/riju/.terraform.d +runuser -u riju -- ln -sT /var/run/riju/.aws /home/riju/.aws +runuser -u riju -- ln -sT /var/run/riju/.docker /home/riju/.docker +runuser -u riju -- ln -sT /var/run/riju/.ssh /home/riju/.ssh +runuser -u riju -- ln -sT /var/run/riju/.terraform.d /home/riju/.terraform.d runuser -u riju -- touch /home/riju/.sudo_as_admin_successful runuser -u riju -- tee -a /home/riju/.bashrc >/dev/null <<"EOF" diff --git a/system/src/riju-system-privileged.c b/system/src/riju-system-privileged.c index e7546e0..50ace35 100644 --- a/system/src/riju-system-privileged.c +++ b/system/src/riju-system-privileged.c @@ -1,4 +1,5 @@ #define _GNU_SOURCE +#include #include #include #include @@ -8,6 +9,7 @@ #include #include #include +#include #include #include @@ -21,7 +23,6 @@ void die_with_usage() { die("usage:\n" " riju-system-privileged session UUID LANG\n" - " riju-system-privileged wait UUID\n" " riju-system-privileged exec UUID CMDLINE...\n" " riju-system-privileged pty UUID CMDLINE..."); } @@ -43,72 +44,88 @@ char *parseLang(char *lang) { return lang; } -void session(char *uuid, char *lang) -{ - char *image, *container, *hostname; - if (asprintf(&image, "riju:lang-%s", lang) < 0) - die("asprintf failed"); - if (asprintf(&container, "riju-session-%s", uuid) < 0) - die("asprintf failed"); - if (asprintf(&hostname, "HOSTNAME=%s", lang) < 0) - die("asprintf failed"); - char *argv[] = { - "docker", - "run", - "--rm", "-it", - "-e", "HOME=/home/riju", - "-e", hostname, - "-e", "LANG=C.UTF-8", - "-e", "LC_ALL=C.UTF-8", - "-e", "LOGNAME=riju", - "-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/bin", - "-e", "PWD=/home/riju/src", - "-e", "SHELL=/usr/bin/bash", - "-e", "TERM=xterm-256color", - "-e", "TMPDIR=/tmp", - "-e", "USER=riju", - "-e", "USERNAME=riju", - "--hostname", lang, - "--name", container, - image, "cat", NULL, - }; - execvp(argv[0], argv); - die("execvp failed"); -} - void wait_alarm(int signum) { (void)signum; die("container did not come up within 1 second"); } -void wait(char *uuid) +void session(char *uuid, char *lang) { - char *cmdline; - if (asprintf(&cmdline, "docker inspect riju-session-%s >/dev/null 2>&1", uuid) < 0) + char *image, *container, *hostname, *volume, *fifo; + if (asprintf(&image, "riju:lang-%s", lang) < 0) die("asprintf failed"); - struct timespec ts; + if (asprintf(&container, "riju-session-%s", uuid) < 0) + die("asprintf failed"); + if (asprintf(&hostname, "HOSTNAME=%s", lang) < 0) + die("asprintf failed"); + int rv = mkdir("/var/run/riju/sentinels", 0700); + if (rv < 0 && errno != EEXIST) + die("mkdir failed"); + char tmpdir[] = "/var/run/riju/sentinels/XXXXXX"; + if (mkdtemp(tmpdir) == NULL) + die("mkdtemp failed"); + if (asprintf(&volume, "%s:/var/run/riju/sentinel", tmpdir) < 0) + die("asprintf failed"); + if (asprintf(&fifo, "%s/fifo", tmpdir) < 0) + die("asprintf failed"); + if (mknod(fifo, 0700 | S_IFIFO, 0) < 0) + die("mknod failed"); + pid_t pid = fork(); + if (pid < 0) + die("fork failed"); + else if (pid == 0) { + char *argv[] = { + "docker", + "run", + "--rm", + "-v", volume, + "-e", "HOME=/home/riju", + "-e", hostname, + "-e", "LANG=C.UTF-8", + "-e", "LC_ALL=C.UTF-8", + "-e", "LOGNAME=riju", + "-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/bin", + "-e", "PWD=/home/riju/src", + "-e", "SHELL=/usr/bin/bash", + "-e", "TERM=xterm-256color", + "-e", "TMPDIR=/tmp", + "-e", "USER=riju", + "-e", "USERNAME=riju", + "--user", "root", + "--hostname", lang, + "--name", container, + image, "cat", "/var/run/riju/sentinel/fifo", NULL, + }; + execvp(argv[0], argv); + die("execvp failed"); + } + struct timespec ts; // 10ms ts.tv_sec = 0; ts.tv_nsec = 1000 * 1000 * 10; signal(SIGALRM, wait_alarm); alarm(1); + int fd; while (1) { - FILE *proc = popen(cmdline, "r"); - if (proc == NULL) - die("popen failed"); - char buf[1024]; - while (fgets(buf, 1024, proc) != NULL); - if (ferror(proc)) - die("fgets failed"); - int status = pclose(proc); - if (status < 0) - die("pclose failed"); - if (WEXITSTATUS(status) == 0) + fd = open(fifo, O_WRONLY); + if (fd >= 0) break; + if (errno != ENXIO) + die("open failed"); int rv = nanosleep(&ts, NULL); - if (rv != 0 && rv != EINTR) + if (rv != 0 && errno != EINTR) die("nanosleep failed"); } + signal(SIGALRM, SIG_IGN); + if (unlink(fifo) < 0) + die("unlink failed"); + if (rmdir(tmpdir) < 0) + die("rmdir failed"); + printf("riju: container ready\n"); // magic string + if (waitpid(pid, NULL, 0) <= 0) + die("waitpid failed"); + if (close(fd) < 0) + die("close failed"); } void exec(char *uuid, int argc, char **cmdline, bool pty) @@ -119,6 +136,7 @@ void exec(char *uuid, int argc, char **cmdline, bool pty) char *argvPrefix[] = { "docker", "exec", + "--user", "riju", pty ? "-it" : "-i", container, }; @@ -134,8 +152,8 @@ void exec(char *uuid, int argc, char **cmdline, bool pty) int main(int argc, char **argv) { - if (setuid(0) != 0) - die("setuid failed"); + if (seteuid(0) != 0) + die("seteuid failed"); if (argc < 2) die_with_usage(); if (!strcmp(argv[1], "session")) { @@ -146,13 +164,6 @@ int main(int argc, char **argv) session(uuid, lang); return 0; } - if (!strcmp(argv[1], "wait")) { - if (argc != 3) - die_with_usage(); - char *uuid = parseUUID(argv[2]); - wait(uuid); - return 0; - } if (!strcmp(argv[1], "exec")) { if (argc < 4) die_with_usage();