From 3395491d4bf45d9f1c0a9dcb1cbf749735c1f3a3 Mon Sep 17 00:00:00 2001 From: "re:fi.64" Date: Tue, 21 Jul 2026 20:43:19 -0500 Subject: [PATCH] nix/spindle/alpine: refactor out common setup/runtime scripts These are things that other images can make use of as well. --- flake.nix | 11 ++- nix/pkgs/spindle-alpine-image.nix | 123 +++------------------------- nix/pkgs/spindle-image-helpers.nix | 127 +++++++++++++++++++++++++++++ nix/pkgs/spindle-static-git.nix | 14 ++++ 4 files changed, 159 insertions(+), 116 deletions(-) create mode 100644 nix/pkgs/spindle-image-helpers.nix create mode 100644 nix/pkgs/spindle-static-git.nix diff --git a/flake.nix b/flake.nix index f1416159..673d8be4 100644 --- a/flake.nix +++ b/flake.nix @@ -219,6 +219,13 @@ packages = mkPackageSet pkgs; staticPackages = mkPackageSet pkgs.pkgsStatic; crossPackages = mkPackageSet pkgs.pkgsCross.gnu64.pkgsStatic; + + spindle-image-helpers = let + shuttle = (mkPackageSet linuxPkgs).shuttle-static; + in + linuxPkgs.callPackage ./nix/pkgs/spindle-image-helpers.nix { + inherit shuttle; + }; in { inherit (packages) @@ -303,11 +310,9 @@ version = "${branch}.0"; arch = "x86_64"; cdn = "https://dl-cdn.alpinelinux.org/alpine/v${branch}/releases/${arch}"; - - shuttle = (mkPackageSet linuxPkgs).shuttle-static; in linuxPkgs.callPackage ./nix/pkgs/spindle-alpine-image.nix { - inherit arch shuttle; + inherit arch spindle-image-helpers; repositories = [ "https://dl-cdn.alpinelinux.org/alpine/v${branch}/main" "https://dl-cdn.alpinelinux.org/alpine/v${branch}/community" diff --git a/nix/pkgs/spindle-alpine-image.nix b/nix/pkgs/spindle-alpine-image.nix index 46701877..b33e61e0 100644 --- a/nix/pkgs/spindle-alpine-image.nix +++ b/nix/pkgs/spindle-alpine-image.nix @@ -3,9 +3,8 @@ runCommand, writeText, squashfsTools, - shuttle, + spindle-image-helpers, binutils, - publicsuffix-list, rootfs, kernel, initramfs, @@ -17,17 +16,7 @@ bash = pkgsStatic.bashNonInteractive; curl = pkgsStatic.curlMinimal; jq = pkgsStatic.jq; - git = - (pkgsStatic.gitMinimal.override { - inherit curl; - pythonSupport = false; - withManual = false; - nlsSupport = false; - }).overrideAttrs (old: { - doCheck = false; - doInstallCheck = false; - configureFlags = (old.configureFlags or []) ++ ["ac_cv_lib_curl_curl_global_init=yes"]; - }); + git = pkgsStatic.callPackage ./spindle-static-git.nix {}; # we don't include gnused, xxd etc. here because busybox has them # we want to keep the image this image small! guestTools = [nix bash git curl jq]; @@ -50,8 +39,10 @@ mountpoint -q /run || mount -t tmpfs -o mode=0755 run /run mountpoint -q /tmp || mount -t tmpfs -o mode=1777 tmp /tmp - # setup xdg runtime dir, podman eg. needs it - install -d -m 0700 -o spindle-workflow -g spindle-workflow /run/user/970 + modprobe vmw_vsock_virtio_transport + # shuttle's cache enqueue listener binds a guest-local (CID 1) vsock + modprobe vsock_loopback + modprobe ext4 # cgroup2 setup, normally we would do this with rc-service # but minirootfs does not ship with those so we set it up ourselves. @@ -64,26 +55,9 @@ # the initramfs mdev leaves these 0660, which breaks non-root workflows chmod 666 /dev/null /dev/zero /dev/full /dev/random /dev/urandom /dev/tty /dev/ptmx 2>/dev/null - modprobe vmw_vsock_virtio_transport - # shuttle's cache enqueue listener binds a guest-local (CID 1) vsock - modprobe vsock_loopback - modprobe ext4 - - if [ -b /dev/vdb ]; then - # setup disk backed nix store - mount -t ext4 /dev/vdb /workspace - install -d -o spindle-workflow -g spindle-workflow /workspace /workspace/repo - install -d /workspace/.nix/rw-store /workspace/.nix/rw-store-work /workspace/.nix/build - mount -t overlay overlay \ - -o lowerdir=/nix/store,upperdir=/workspace/.nix/rw-store,workdir=/workspace/.nix/rw-store-work \ - /nix/store - fi - - ip link set lo up - ip link set eth0 up - ip addr add 10.0.3.15/24 dev eth0 - ip route add default via 10.0.3.2 hostname -F /etc/hostname + + spindle-system-init ''; inittab = writeText "inittab" '' @@ -99,31 +73,8 @@ export NIX_REMOTE=daemon ''; - # mirror nix/microvm/base.nix and nix/modules/shuttle.nix - nixConf = writeText "nix.conf" '' - experimental-features = nix-command flakes - trusted-users = root spindle-workflow - allowed-users = spindle-workflow - post-build-hook = /usr/libexec/spindle-post-build-hook - # keep build sandboxes on the /workspace disk, not the RAM-backed root tmpfs - build-dir = /workspace/.nix/build - !include /run/spindle/nix.conf - ''; - apkRepositories = writeText "apk-repositories" (builtins.concatStringsSep "\n" repositories + "\n"); - postBuildHook = writeText "spindle-post-build-hook" '' - #!/bin/sh - set -f - - if [ -z "''${OUT_PATHS:-}" ]; then - exit 0 - fi - - # OUT_PATHS is intentionally split into individual store paths - exec /usr/bin/shuttle enqueue-built-paths $OUT_PATHS - ''; - imageSpecJSON = writeText "spec.json" ( builtins.toJSON { inherit arch; @@ -173,73 +124,19 @@ in mkdir -p rootfs/lib/modules cp -a modloop/modules/* rootfs/lib/modules/ - install -D -m 0755 ${shuttle}/bin/shuttle rootfs/usr/bin/shuttle + ${spindle-image-helpers.setupRootfs} rootfs install -D -m 0755 ${setupScript} rootfs/sbin/spindle-setup install -D -m 0644 ${inittab} rootfs/etc/inittab install -D -m 0644 ${profileScript} rootfs/etc/profile.d/01-spindle.sh - install -D -m 0644 ${nixConf} rootfs/etc/nix/nix.conf - install -D -m 0755 ${postBuildHook} rootfs/usr/libexec/spindle-post-build-hook # install dependencies - # we only copy binaries + libexec for minimal deps so the image size doesn't - # increase so much (if we copy the whole guestTools closure for example, it - # doubles the disk size) - mkdir -p rootfs/nix/store rootfs/usr/local/bin - for pkg in ${toString guestTools}; do - for bin in "$pkg/bin/"*; do - [[ -e "$bin" ]] || continue - name=$(basename "$bin") - # we resolve symlinks as to copy the actual binaries - if [[ -L "$bin" ]]; then - real=$(readlink "$bin") - else - real="$bin" - fi - # handle symlinks properly - if [[ "$real" != /nix/store* ]]; then - ln -vsf "$real" "rootfs/usr/local/bin/$name" - else - cp -v "$real" "rootfs/usr/local/bin/$name" - fi - done - # libexec has binaries used by packages even if statically compiled - if [[ -d "$pkg/libexec" ]]; then - mkdir -p "rootfs$pkg" - cp -av "$pkg/libexec" "rootfs$pkg/" - fi - done - # this is necessary for nix to work, it is not a library but nix hardcodes - # it in it's binary - cp -rv ${publicsuffix-list} rootfs/nix/store/ + ${spindle-image-helpers.installGuestTools} rootfs ${toString guestTools} # scripts commonly hardcode #!/bin/bash ln -sf ${bash}/bin/bash rootfs/bin/bash - echo "spindle-microvm" > rootfs/etc/hostname - printf 'nameserver 127.0.0.1\n' > rootfs/etc/resolv.conf install -D -m 0644 ${apkRepositories} rootfs/etc/apk/repositories - echo "spindle-workflow:x:970:970:spindle workflow:/workspace:/bin/sh" >> rootfs/etc/passwd - echo "spindle-workflow:x:970:" >> rootfs/etc/group - echo "spindle-workflow:!::0:::::" >> rootfs/etc/shadow - mkdir -p rootfs/workspace - - # subordinate id ranges so the workflow user can run rootless containers - # (podman/buildah): without these, user-namespace id mapping falls back to a - # single 970->0 map and any layer that chowns to another uid fails. the range - # is well clear of 970 and the 30000-block nixbld users. - echo "spindle-workflow:100000:65536" >> rootfs/etc/subuid - echo "spindle-workflow:100000:65536" >> rootfs/etc/subgid - - # setup nix build users for the daemon - members="" - for i in $(seq 1 8); do - echo "nixbld$i:x:$((30000 + i)):30000:nix build user $i:/var/empty:/sbin/nologin" >> rootfs/etc/passwd - echo "nixbld$i:!::0:::::" >> rootfs/etc/shadow - members="$members''${members:+,}nixbld$i" - done - echo "nixbld:x:30000:$members" >> rootfs/etc/group - mkdir -p "$out" mksquashfs rootfs "$out/store-disk" -comp zstd -Xcompression-level 19 -noappend -no-xattrs -all-root -quiet \ -p '/sbin/apk m 4755 0 0' # suid apk so spindle-workflow can use it without having to doas or smth diff --git a/nix/pkgs/spindle-image-helpers.nix b/nix/pkgs/spindle-image-helpers.nix new file mode 100644 index 00000000..dba184e1 --- /dev/null +++ b/nix/pkgs/spindle-image-helpers.nix @@ -0,0 +1,127 @@ +{ + shuttle, + writeScript, + writeShellScript, + writeText, + publicsuffix-list, +}: let + nixConf = writeText "nix.conf" '' + # mirror nix/microvm/base.nix and nix/modules/shuttle.nix + experimental-features = nix-command flakes + trusted-users = root spindle-workflow + allowed-users = spindle-workflow + post-build-hook = /usr/libexec/spindle-post-build-hook + # keep build sandboxes on the /workspace disk, not the RAM-backed root tmpfs + build-dir = /workspace/.nix/build + !include /run/spindle/nix.conf + ''; + + postBuildHook = writeText "spindle-post-build-hook" '' + #!/bin/sh + set -f + + if [ -z "''${OUT_PATHS:-}" ]; then + exit 0 + fi + + # OUT_PATHS is intentionally split into individual store paths + exec shuttle enqueue-built-paths $OUT_PATHS + ''; + + systemInit = writeScript "spindle-system-init.sh" '' + #!/bin/sh + # setup xdg runtime dir, podman eg. needs it + install -d -m 0700 -o spindle-workflow -g spindle-workflow /run/user/970 + + if [ -b /dev/vdb ]; then + # setup disk backed nix store + mount -t ext4 /dev/vdb /workspace + install -d -o spindle-workflow -g spindle-workflow /workspace /workspace/repo + install -d /workspace/.nix/rw-store /workspace/.nix/rw-store-work /workspace/.nix/build + mount -t overlay overlay \ + -o lowerdir=/nix/store,upperdir=/workspace/.nix/rw-store,workdir=/workspace/.nix/rw-store-work \ + /nix/store + fi + + busybox ip link set lo up + busybox ip link set eth0 up + busybox ip addr add 10.0.3.15/24 dev eth0 + busybox ip route add default via 10.0.3.2 + ''; +in { + setupRootfs = writeShellScript "setup-rootfs" '' + set -eu + + rootfs="$1" + shift + + install -D -m 0755 ${shuttle}/bin/shuttle "$rootfs"/usr/bin/shuttle + install -D -m 0644 ${nixConf} "$rootfs"/etc/nix/nix.conf + install -D -m 0755 ${postBuildHook} "$rootfs"/usr/libexec/spindle-post-build-hook + install -D -m 0755 ${systemInit} "$rootfs"/sbin/spindle-system-init + + # this is necessary for nix to work, it is not a library but nix hardcodes + # it in it's binary + mkdir -p "$rootfs"/nix/store + cp -rv ${publicsuffix-list} "$rootfs"/nix/store/ + + echo "spindle-microvm" > "$rootfs"/etc/hostname + printf 'nameserver 127.0.0.1\n' > "$rootfs"/etc/resolv.conf + + echo "spindle-workflow:x:970:970:spindle workflow:/workspace:/bin/sh" >> "$rootfs"/etc/passwd + echo "spindle-workflow:x:970:" >> "$rootfs"/etc/group + echo "spindle-workflow:!::0:::::" >> "$rootfs"/etc/shadow + mkdir -p "$rootfs"/workspace + + # subordinate id ranges so the workflow user can run rootless containers + # (podman/buildah): without these, user-namespace id mapping falls back to a + # single 970->0 map and any layer that chowns to another uid fails. the range + # is well clear of 970 and the 30000-block nixbld users. + echo "spindle-workflow:100000:65536" >> "$rootfs"/etc/subuid + echo "spindle-workflow:100000:65536" >> "$rootfs"/etc/subgid + + # setup nix build users for the daemon + members="" + for i in $(seq 1 8); do + echo "nixbld$i:x:$((30000 + i)):30000:nix build user $i:/var/empty:/sbin/nologin" >> "$rootfs"/etc/passwd + echo "nixbld$i:!::0:::::" >> "$rootfs"/etc/shadow + members="$members''${members:+,}nixbld$i" + done + echo "nixbld:x:30000:$members" >> "$rootfs"/etc/group + ''; + + installGuestTools = writeShellScript "install-guest-tools" '' + set -eu + + rootfs="$1" + shift + + # we only copy binaries + libexec for minimal deps so the image size doesn't + # increase so much (if we copy the whole guestTools closure for example, it + # doubles the disk size) + mkdir -p "$rootfs"/nix/store "$rootfs"/usr/local/bin + for pkg in "$@"; do + for bin in "$pkg/bin/"*; do + [[ -e "$bin" ]] || continue + name=$(basename "$bin") + # we resolve symlinks as to copy the actual binaries + if [[ -L "$bin" ]]; then + real=$(readlink "$bin") + else + real="$bin" + fi + # handle symlinks properly + if [[ "$real" != /nix/store* ]]; then + ln -vsf "$real" "$rootfs/usr/local/bin/$name" + else + cp -v "$real" "$rootfs/usr/local/bin/$name" + fi + done + # libexec has binaries used by packages even if statically compiled + if [[ -d "$pkg/libexec" ]]; then + mkdir -p "$rootfs$pkg" + cp -av "$pkg/libexec" "$rootfs$pkg/" + fi + done + ''; +} diff --git a/nix/pkgs/spindle-static-git.nix b/nix/pkgs/spindle-static-git.nix new file mode 100644 index 00000000..98326a9f --- /dev/null +++ b/nix/pkgs/spindle-static-git.nix @@ -0,0 +1,14 @@ +{ + curlMinimal, + gitMinimal, +}: +(gitMinimal.override { + curl = curlMinimal; + pythonSupport = false; + withManual = false; + nlsSupport = false; +}).overrideAttrs (old: { + doCheck = false; + doInstallCheck = false; + configureFlags = (old.configureFlags or []) ++ ["ac_cv_lib_curl_curl_global_init=yes"]; +}) -- 2.51.2