aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-10-01 14:43:51 +0900
committerOphestra <cat@gensokyo.uk>2026-10-01 14:43:51 +0900
commitf8cab362c0b5d96608bcdc58cc51a596ed69bbe3 (patch)
treed6848b0eabd8be22543292301e32919d6253baf0 /internal
parent3be6a3f79aa5871a1d0ec701f367966dcf8e1a36 (diff)
internal/workflows: document act_runner setup
This requires a lot of specialised setup to run, so document that here for future reference. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal')
-rw-r--r--internal/workflows/doc.go133
1 files changed, 128 insertions, 5 deletions
diff --git a/internal/workflows/doc.go b/internal/workflows/doc.go
index 6a9dc5e6..0c7e94b9 100644
--- a/internal/workflows/doc.go
+++ b/internal/workflows/doc.go
@@ -1,10 +1,133 @@
//go:build !workflows
-// The workflows program manages the .gitea directory to avoid having to write
-// yaml. It is never invoked directly or built for the distribution tarball.
-//
-// This program is an internal detail of the hakurei project and is not usable
-// on its own. It is not covered by the compatibility promise.
+/*
+The workflows program manages the .gitea directory to avoid having to write
+yaml. It is never invoked directly or built for the distribution tarball.
+
+This program is an internal detail of the hakurei project and is not usable
+on its own. It is not covered by the compatibility promise.
+
+# Configuring act_runner
+
+It is unreasonable and impractical to bootstrap Rosa OS each time a job runs.
+This section documents an ideal configuration for exposing the cmd/mbf CI
+service to all job containers, while avoiding some issues with the container
+setup documented upstream.
+
+Microsoft Github workflows expect a docker socket in the job container. This is
+problematic because docker has a hard time running inside a docker container.
+The Gitea act_runner simply bind mounts whatever socket it sees into the
+container. With a regular docker daemon, this allows not only a simple container
+escape, but also privilege escalation as unconstrained root in the init
+namespace. To mitigate this, set up an unprivileged podman daemon and expose its
+socket to the container instead.
+
+On Alpine Linux, this is achieved by:
+
+ # Configuration for /etc/init.d/podman
+
+ # See podman-system-service(1) for service description
+ # and available options.
+ #podman_opts="--time 0"
+
+ # API endpoint in URI form. Leave empty to use defaults.
+ podman_uri="unix:///srv/act_runner/podman"
+
+ # Setting root user will start rootful service.
+ # Use any other user for rootless mode.
+ podman_user="act_runner"
+
+A symlink must then be created at /var/run/docker.sock pointing to the socket
+owned by the unprivileged daemon. This should be done in a /etc/local.d script.
+
+The container can be specified via docker-compose:
+
+ version: "3.8"
+ services:
+ runner:
+ restart: always
+ image: docker.io/gitea/act_runner:nightly
+ environment:
+ CONFIG_FILE: /config.yaml
+ GITEA_INSTANCE_URL: "https://git.gensokyo.uk"
+ GITEA_RUNNER_LABELS: "rosa:docker://docker.gitea.com/runner-images:ubuntu-latest"
+
+ GITEA_RUNNER_REGISTRATION_TOKEN: "${REGISTRATION_TOKEN}"
+ GITEA_RUNNER_NAME: "${RUNNER_NAME}"
+ volumes:
+ - ./config.yaml:/config.yaml
+ - ./data:/data
+ - ./podman:/var/run/docker.sock
+
+Before starting the container, configure act_runner via config.yaml:
+
+ container:
+ network: host
+ options: -e PAGER=cat -e MBF_CACHE_DIR=/rosa -e MBF_POISON_OPEN=1
+ -v /var/lib/rosa:/rosa
+ --device=/dev/kvm
+ valid_volumes:
+ - /var/lib/rosa
+
+where /var/lib/rosa is the absolute pathname of the cache directory in the init
+namespace. Setting MBF_POISON_OPEN enables cmd/mbf to run as root. It is also
+a good idea here to set runner.capacity to reflect the capacity of the guest, so
+jobs can be consumed quicker.
+
+Build a statically-linked cmd/mbf:
+
+ go build -trimpath \
+ -ldflags='-s -w
+ -buildid=
+ -linkmode external
+ -extldflags=-static' \
+ ./cmd/mbf
+
+Create a directory named "bin" in the cache directory and install the resulting
+binary.
+
+The cmd/mbf CI service should generally run as a separate user. To achieve this,
+change the socket permission to 0777 after the service starts. On Alpine Linux,
+this can be achieved by the init script:
+
+ #!/sbin/openrc-run
+ supervisor=supervise-daemon
+
+ name="Rosa OS cmd/mbf CI backend"
+ description="Listening service that services CI workloads from act_runner"
+
+ command=/var/lib/rosa/bin/mbf
+ command_args="-d /var/lib/rosa ci daemon"
+ command_user="${mbf_user:=mbf}"
+
+ output_logger="logger"
+ error_logger="logger"
+
+ depend() {
+ need sysfs
+ }
+
+ start_post() {
+ while [ ! -e /var/lib/rosa/ci ]; do sleep 0.1; done
+ chmod 0777 /var/lib/rosa/ci
+ }
+
+It is often a good idea to populate the cache from a mirror service before the
+first workflow job is started and re-populate it after every cmd/mbf update.
+
+# Security
+
+The design of Microsoft Github workflows is inherently insecure: it requires
+internet access just to fetch actions, and these actions all expect internet
+access as well. Gitea, being a drop-in replacement, retains the same problems.
+
+The setup documented above already avoids the obvious container escape via the
+bind-mounted docker socket, but it is still easily possible to escape as the
+context of the unconstrained runner user. For this reason, the runner must
+reside in a hardened guest. Since internet access cannot be disabled, extra care
+must be taken when setting up the host firewall to prevent access to internal
+systems from the untrusted guest.
+*/
package workflows
//go:generate go run -tags=workflows .