aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/workflows
diff options
context:
space:
mode:
authorOphestra <cat@gensokyo.uk>2026-10-04 01:05:10 +0900
committerOphestra <cat@gensokyo.uk>2026-10-06 19:41:06 +0900
commita7383510fb05abc98b992240cb76ad4b6c598956 (patch)
tree90881e9d6952e050128f1378d66452c7d733d012 /internal/workflows
parentb452e1047ccd3e1826da16416430e6638270155b (diff)
test/sandbox: migrate tests
This significantly improves performance, removing overhead of nix, python, and virtualisation. Running this in an unprivileged container required patching the kernel, but since special runner setup was already needed, that was an acceptable tradeoff. Signed-off-by: Ophestra <cat@gensokyo.uk>
Diffstat (limited to 'internal/workflows')
-rw-r--r--internal/workflows/doc.go18
-rw-r--r--internal/workflows/step.go3
-rw-r--r--internal/workflows/test.go45
3 files changed, 50 insertions, 16 deletions
diff --git a/internal/workflows/doc.go b/internal/workflows/doc.go
index e34c59c8..a8f18ab4 100644
--- a/internal/workflows/doc.go
+++ b/internal/workflows/doc.go
@@ -20,7 +20,8 @@ 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.
+socket to the container instead. Since mountinfo always use credentials from the
+init user namespace, subordinate user and group ID must always be 100000.
On Alpine Linux, this is achieved by:
@@ -67,6 +68,7 @@ Before starting the container, configure act_runner via config.yaml:
-v /var/lib/rosa:/rosa
--security-opt='unmask=/proc/*'
--cap-add=SYS_ADMIN
+ --cap-add=SYS_PTRACE
--device=/dev/kvm
--device=/dev/fuse
valid_volumes:
@@ -76,8 +78,9 @@ 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. Removing mount points covering /proc enables
-testing of cmd/hakurei. Exposing the fuse device and adding capability SYS_ADMIN
-enables testing of cmd/sharefs.
+testing of cmd/hakurei. Exposing the fuse device and adding capability
+CAP_SYS_ADMIN enables testing of cmd/sharefs. Adding capability CAP_SYS_PTRACE
+enables dumping seccomp filters via ptrace on the patched kernel.
Build a statically-linked cmd/mbf:
@@ -120,6 +123,15 @@ this can be achieved by the init script:
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.
+# Configuring the kernel
+
+In order to attach to the container process, the sysctl kernel.yama.ptrace_scope
+must be set to 0. After which, apply the patch test/sandbox/seccomp.patch to
+your kernel sources, compile and install the new kernel. Refer to
+https://wiki.alpinelinux.org/wiki/Custom_Kernel if the guest runs Alpine Linux.
+If running podman or docker as root, the patch is not required. Do not apply
+this patch on a system meant to be secure.
+
# Security
The design of Microsoft Github workflows is inherently insecure: it requires
diff --git a/internal/workflows/step.go b/internal/workflows/step.go
index 83d9c5f3..199f82d9 100644
--- a/internal/workflows/step.go
+++ b/internal/workflows/step.go
@@ -76,11 +76,12 @@ func newTestsuite(name, prefix string) Step {
// newPackages returns a job for installing the specified packages with
// best-effort caching. Package names must not contain spaces.
-func newPackages(rev int, packages ...string) Step {
+func newPackages(rev int, repos []string, packages ...string) Step {
return Step{
Name: "Install packages",
Uses: "awalsh128/cache-apt-pkgs-action@v1",
With: []KV[any]{
+ {"add-repository", strings.Join(repos, " ")},
{"packages", strings.Join(packages, " ")},
{"version", rev},
{"execute_install_scripts", true},
diff --git a/internal/workflows/test.go b/internal/workflows/test.go
index 723cf463..c81574ea 100644
--- a/internal/workflows/test.go
+++ b/internal/workflows/test.go
@@ -8,7 +8,7 @@ var _ = (&Workflow{
Jobs: Map[Job]{
{"hakurei", Job{
- Name: "Hakurei",
+ Name: "Hakurei (legacy)",
On: "nix",
Steps: []Step{
@@ -19,7 +19,7 @@ var _ = (&Workflow{
}},
{"race", Job{
- Name: "Hakurei (race detector)",
+ Name: "Hakurei (legacy with race instrument)",
On: "nix",
Steps: []Step{
@@ -31,23 +31,46 @@ var _ = (&Workflow{
{"sandbox", Job{
Name: "Sandbox",
- On: "nix",
+ On: "rosa",
Steps: []Step{
+ fixup,
checkout,
- newNixOSTest("sandbox"),
- newUploadArtifact("test output", "sandbox-vm-output"),
+ toolchain,
+ newPackages(0, []string{"ppa:savoury1/pipewire"},
+ "libmount-dev",
+ "sway",
+ "xwayland",
+ "xdg-dbus-proxy",
+ "pipewire",
+ ),
+
+ newCIRequest("distribution", "dist -o result", "dist"),
+ install,
+ newTestsuite("sandbox", ""),
},
}},
{"sandbox-race", Job{
- Name: "Sandbox (race detector)",
- On: "nix",
+ Name: "Sandbox (with race instrument)",
+ On: "rosa",
Steps: []Step{
+ fixup,
checkout,
- newNixOSTest("sandbox-race"),
- newUploadArtifact("test output", "sandbox-race-vm-output"),
+ toolchain,
+
+ newPackages(0, []string{"ppa:savoury1/pipewire"},
+ "libmount-dev",
+ "sway",
+ "xwayland",
+ "xdg-dbus-proxy",
+ "pipewire",
+ ),
+
+ newCIRequest("distribution", "race -o result", "dist"),
+ install,
+ newTestsuite("sandbox", ""),
},
}},
@@ -59,7 +82,7 @@ var _ = (&Workflow{
fixup,
checkout,
toolchain,
- newPackages(0, "fuse3", "fsmark"),
+ newPackages(0, nil, "fuse3", "fsmark"),
newCIRequest("distribution", "dist -o result", "dist"),
install,
@@ -90,8 +113,6 @@ var _ = (&Workflow{
Needs: []string{
"hakurei",
"race",
- "sandbox",
- "sandbox-race",
},
Steps: []Step{