fix: make rootless Docker home writable
CI / tests-and-images (pull_request) Has been cancelled
Runner smoke test / runner-smoke (pull_request) Has been cancelled

This commit is contained in:
agent committed 2026-10-12 22:18:51 +03:00
1 parent e6b099cf31
commit 00922e6c09
5 files changed
+39 -7

No files matched your search

+5
View File
@@ -20,6 +20,11 @@ dedicated ext4 loop-backed Docker data filesystem. The runner account's home
directory is root-owned mode `0711`, so CI cannot rename the image or mountpoint
entries. Docker config remains under `.config`; registration state and the
runner workspace live in `.config/act-runner`, which is runner-owned. The
rootless Docker setup and user service use `.config/act-runner` as writable
`HOME`, `.config` as `XDG_CONFIG_HOME`, and
`.config/act-runner/.local/share` as `XDG_DATA_HOME`. This keeps Docker config
and its systemd user unit in the existing `.config` tree without granting
write access to the protected home.
rootless Docker daemon
and runner user are separate from production Docker; `codex` is not added to
sudo or Docker groups. Jobs do not receive the rootless Docker socket, the
+4
View File
@@ -95,6 +95,10 @@ UFW rule details, and protected file metadata remain unverified.
and runtime state remain in runner-owned subdirectories. It reuses legacy
`0755` config directories, creates the image through a pinned file descriptor,
rejects path replacement, and requires mount options `nosuid,nodev`.
The reported installation blocker is that rootless setup receives this
protected directory as `HOME`; the fix prepared in this PR assigns writable
runner-owned HOME/XDG paths and checks them before setup. It has not been
merged or run on the VPS.
- Gitea repository runner inventory could not be read by the current API
principal (403). The owner has not supplied runner state, and no runner
Online status or runner-hosted workflow is claimed. The manual
+4 -1
View File
@@ -2,6 +2,9 @@
set -Eeuo pipefail
systemctl is-active --quiet akyldash-act-runner.service
runner_uid=$(id -u akyldash-runner)
runuser -u akyldash-runner -- env HOME=/var/lib/akyldash-runner \
runuser -u akyldash-runner -- env \
HOME=/var/lib/akyldash-runner/.config/act-runner \
XDG_CONFIG_HOME=/var/lib/akyldash-runner/.config \
XDG_DATA_HOME=/var/lib/akyldash-runner/.config/act-runner/.local/share \
DOCKER_HOST="unix:///run/user/$runner_uid/docker.sock" \
/usr/bin/docker info --format '{{.ServerVersion}}' >/dev/null
+14 -5
View File
@@ -4,6 +4,8 @@ set -Eeuo pipefail
readonly runner_user=akyldash-runner
readonly runner_home=/var/lib/akyldash-runner
readonly runner_runtime_home="$runner_home/.config/act-runner"
readonly runner_xdg_config_home="$runner_home/.config"
readonly runner_xdg_data_home="$runner_runtime_home/.local/share"
readonly runner_store="$runner_home/docker-data"
readonly runner_image="$runner_home/docker-store.img"
readonly runner_mount_unit_name="$(systemd-escape --path --suffix=mount /var/lib/akyldash-runner/docker-data)"
@@ -92,6 +94,8 @@ runner_ensure_private_directory "$runner_home/.config/systemd" "$runner_user" "$
runner_ensure_private_directory "$runner_home/.config/systemd/user" "$runner_user" "$runner_user" || die 'runner user systemd directory failed validation'
runner_ensure_private_directory "$runner_home/.config/systemd/user/docker.service.d" "$runner_user" "$runner_user" || die 'runner Docker override directory failed validation'
runner_ensure_private_directory "$runner_runtime_home" "$runner_user" "$runner_user" || die 'runner runtime home failed directory validation'
runner_ensure_private_directory "$runner_runtime_home/.local" "$runner_user" "$runner_user" || die 'runner XDG data parent failed directory validation'
runner_ensure_private_directory "$runner_xdg_data_home" "$runner_user" "$runner_user" || die 'runner XDG data directory failed directory validation'
if [[ -e $runner_home/.runner || -L $runner_home/.runner ]]; then
[[ -f $runner_home/.runner && ! -L $runner_home/.runner ]] || die 'legacy runner registration state is not a regular file'
if [[ ! -e $runner_runtime_home/.runner && ! -L $runner_runtime_home/.runner ]]; then
@@ -154,6 +158,8 @@ Slice=user-$runner_uid.slice
User=$runner_user
Group=$runner_user
Environment=HOME=$runner_runtime_home
Environment=XDG_CONFIG_HOME=$runner_xdg_config_home
Environment=XDG_DATA_HOME=$runner_xdg_data_home
WorkingDirectory=$runner_runtime_home
Environment=XDG_RUNTIME_DIR=/run/user/$runner_uid
Environment=DOCKER_HOST=unix:///run/user/$runner_uid/docker.sock
@@ -172,7 +178,7 @@ ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectKernelLogs=yes
RestrictSUIDSGID=yes
ReadWritePaths=$runner_runtime_home /var/log/akyldash-runner /run/user/$runner_uid
ReadWritePaths=$runner_runtime_home $runner_xdg_config_home /var/log/akyldash-runner /run/user/$runner_uid
StandardOutput=append:/var/log/akyldash-runner/act_runner.log
StandardError=append:/var/log/akyldash-runner/act_runner.log
@@ -205,14 +211,17 @@ runner_install_file "$(dirname "$0")/akyldash-runner-restart.service" /etc/syste
loginctl enable-linger "$runner_user"
systemctl daemon-reload
systemctl start "user@$runner_uid.service"
rootless_docker_unit="$runner_home/.config/systemd/user/docker.service"
rootless_docker_unit="$runner_xdg_config_home/systemd/user/docker.service"
rootless_env=(HOME="$runner_runtime_home" XDG_CONFIG_HOME="$runner_xdg_config_home" XDG_DATA_HOME="$runner_xdg_data_home" XDG_RUNTIME_DIR="/run/user/$runner_uid" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$runner_uid/bus")
runuser -u "$runner_user" -- env "${rootless_env[@]}" sh -c 'test -w "$HOME" && test -w "$XDG_CONFIG_HOME" && test -w "$XDG_DATA_HOME"' || die 'rootless Docker HOME and XDG directories must be writable by the runner'
runuser -u "$runner_user" -- env "${rootless_env[@]}" systemctl --user set-environment "HOME=$runner_runtime_home" "XDG_CONFIG_HOME=$runner_xdg_config_home" "XDG_DATA_HOME=$runner_xdg_data_home"
if [[ -e $rootless_docker_unit || -L $rootless_docker_unit ]]; then
[[ -f $rootless_docker_unit && ! -L $rootless_docker_unit && $(stat -c %U "$rootless_docker_unit") == "$runner_user" ]] || die 'existing rootless Docker unit has an unexpected type or owner'
grep -Fq 'dockerd-rootless.sh' "$rootless_docker_unit" || die 'existing rootless Docker unit differs from the expected service'
else
runuser -u "$runner_user" -- env HOME="$runner_home" XDG_RUNTIME_DIR="/run/user/$runner_uid" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$runner_uid/bus" dockerd-rootless-setuptool.sh install
runuser -u "$runner_user" -- env "${rootless_env[@]}" dockerd-rootless-setuptool.sh install
fi
runuser -u "$runner_user" -- env HOME="$runner_home" XDG_RUNTIME_DIR="/run/user/$runner_uid" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$runner_uid/bus" systemctl --user daemon-reload
runuser -u "$runner_user" -- env HOME="$runner_home" XDG_RUNTIME_DIR="/run/user/$runner_uid" DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$runner_uid/bus" systemctl --user enable --now docker.service
runuser -u "$runner_user" -- env "${rootless_env[@]}" systemctl --user daemon-reload
runuser -u "$runner_user" -- env "${rootless_env[@]}" systemctl --user enable --now docker.service
printf '\nRunner runtime installed. Register it interactively, then enable its services as described in docs/operations/CI_RUNNER.md.\n'
+12 -1
View File
@@ -19,8 +19,19 @@ assert_absent 'var/run/docker.sock|metrics:|health_check:|production.env|review_
grep -Fq 'docker-ce-rootless-extras' "$installer"
grep -Fq 'runner_ensure_image "$runner_image" "$runner_image_bytes" 0 711' "$installer"
grep -Fq 'runner_lock_home "$runner_home"' "$installer"
grep -Fq 'test -w "$HOME" && test -w "$XDG_CONFIG_HOME" && test -w "$XDG_DATA_HOME"' "$installer"
grep -Fq 'rootless_env=(HOME="$runner_runtime_home" XDG_CONFIG_HOME="$runner_xdg_config_home" XDG_DATA_HOME="$runner_xdg_data_home"' "$installer"
grep -Fq 'Environment=XDG_CONFIG_HOME=$runner_xdg_config_home' "$installer"
grep -Fq 'Environment=XDG_DATA_HOME=$runner_xdg_data_home' "$installer"
grep -Fq 'HOME=/var/lib/akyldash-runner/.config/act-runner' infra/runner/act-runner-healthcheck.sh
grep -Fq 'XDG_CONFIG_HOME=/var/lib/akyldash-runner/.config' infra/runner/act-runner-healthcheck.sh
grep -Fq 'XDG_DATA_HOME=/var/lib/akyldash-runner/.config/act-runner/.local/share' infra/runner/act-runner-healthcheck.sh
if grep -Fq 'env HOME="$runner_home"' "$installer"; then
echo 'Protected runner home must not be passed as rootless Docker HOME' >&2
exit 1
fi
grep -Fq 'die '\''stop akyldash-act-runner.service after all jobs finish before rerunning this installer' "$installer"
grep -Fq 'ReadWritePaths=$runner_runtime_home /var/log/akyldash-runner /run/user/$runner_uid' "$installer"
grep -Fq 'ReadWritePaths=$runner_runtime_home $runner_xdg_config_home /var/log/akyldash-runner /run/user/$runner_uid' "$installer"
grep -Fq 'os.O_NOFOLLOW | os.O_CLOEXEC' infra/runner/secure-runner-image.py
grep -Fq 'os.posix_fallocate(image_fd, 0, int(bytes_text))' infra/runner/secure-runner-image.py
grep -Fq 'pass_fds=(image_fd,)' infra/runner/secure-runner-image.py