Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/vole.git

Write over SSH with a registered key:

git clone ssh://git@rodent.software:2222/vole.git

A free and open-source forge that lives in your Git repo.

Ship the runner's unit and let a deployment create a runner machine

d0d4239f3f7236197c87a752d7965de238430f98 · Keenan Nemetz · 3d ago · parent dbae2e01

Ship the runner's unit and let a deployment create a runner machine

The package installs one binary that is the forge, the runner, and the CLI,
so it now carries a second unit. It is not enabled, and it refuses to start
with a message until an operator fills in /etc/vole/runner.env: who runs
builds on a machine is that machine owner's decision.

The unit runs as a dedicated account, because rootless podman needs a home
directory and a range of subordinate ids, and because a job should not find
the forge's own account on the same disk. The arguments live in that one
file rather than in the unit, split at spaces by systemd, so a single unit
serves every machine.

Each Terraform configuration takes runner=true and creates that machine
beside the forge: its own firewall, no backups (its checkouts and caches are
copies), the same key for a shell, and Cloud-init that installs the package,
creates the account and its subuids, generates the runner's key, pins the
instance's host key once, and enables the unit. The runner_setup output
prints the three commands that let it take work, filled in with the
addresses this apply created.

The first-boot file cannot register the runner: the allowlist is per
repository, and at first boot there are no repositories. That step stays
with an instance admin, and the docs say so.

packaging/systemd/vole-runner.service +55

@@ -0,0 +1,55 @@
1 [Unit]
2 Description=Vole CI runner
3 Documentation=https://github.com/rodent-software/vole
4 After=network-online.target
5 Wants=network-online.target
6
7 # An instance that is down, or a name that does not resolve yet, is not a reason
8 # to give up on the machine: the runner waits and tries again.
9 StartLimitIntervalSec=0
10
11 [Service]
12 Type=simple
13
14 # A dedicated unprivileged account with a home directory and a subuid and subgid
15 # range, so the container runtime is rootless. docs/deploy/runner.md has the two
16 # commands that set it up. The instance's own account is not this one: a job that
17 # escapes its container should not find the forge's data on the same machine.
18 User=ci-runner
19 Group=ci-runner
20 Environment=HOME=/var/lib/vole-runner
21 WorkingDirectory=/var/lib/vole-runner
22 RuntimeDirectory=vole-runner
23 RuntimeDirectoryMode=0700
24 Environment=XDG_RUNTIME_DIR=/run/vole-runner
25
26 # The arguments live in a file so one unit serves every machine: --remote,
27 # --label, --dir, --known-hosts, --container-network. See the example shipped as
28 # /etc/vole/runner.env.
29 EnvironmentFile=-/etc/vole/runner.env
30 ExecStartPre=/bin/sh -c 'test -n "$VOLE_RUNNER_ARGS" || { echo "runner is not configured: set VOLE_RUNNER_ARGS in /etc/vole/runner.env" >&2; exit 1; }'
31 ExecStart=/usr/bin/vole runner $VOLE_RUNNER_ARGS
32
33 Restart=on-failure
34 RestartSec=10s
35 TimeoutStopSec=60s
36
37 # Rootless podman creates a cgroup per container, which it can only do if this
38 # service's own cgroup is delegated to it.
39 Delegate=yes
40
41 # A core dump of this process is a copy of the runner's SSH key, which leases jobs
42 # and reports their results.
43 LimitCORE=0
44 LimitNOFILE=65536
45 UMask=0027
46 NoNewPrivileges=true
47 PrivateTmp=true
48
49 # Deliberately less sandboxed than vole.service: the isolation a job gets is its
50 # container, and the runtime needs user namespaces and a writable home to build
51 # one. Nothing here reaches the instance's data, because it is not on this
52 # machine.
53
54 [Install]
55 WantedBy=multi-user.target