Merge nucleic/gentle-willow-quail-cjp3 into dev
This commit is contained in:
@@ -44,6 +44,10 @@ echo "$ROLE" > "$R/etc/naros/role"
|
||||
# Capability manifest (NAROS.md §6.3). Base fields here; toolchain entries are appended
|
||||
# by the tiers that install them (agent-tier hook, N2). Versions of Nucleic packages are
|
||||
# queryable via dpkg, listed here for one-stop reads.
|
||||
#
|
||||
# kernel_tag is the suffix naros-identity's preloaded shim appends to uname(2)'s release
|
||||
# (§2.3) — recorded so a reader can tell the narOS marker apart from the host kernel's own
|
||||
# version, and strip it. narOS builds no kernel; the tag is identity, not a kernel build.
|
||||
nash_ver="$(chroot "$R" dpkg-query -W -f '${Version}' nash 2>/dev/null || echo null)"
|
||||
[ "$nash_ver" = null ] || nash_ver="\"$nash_ver\""
|
||||
cat > "$R/etc/naros/manifest.json" <<EOF
|
||||
@@ -55,6 +59,7 @@ cat > "$R/etc/naros/manifest.json" <<EOF
|
||||
"variant": "$VARIANT",
|
||||
"flavor": "$FLAVOR",
|
||||
"arch": "$NAROS_ARCH",
|
||||
"kernel_tag": "-naros$NAROS_VERSION",
|
||||
"debian": { "suite": "$NAROS_SUITE", "snapshot": "$NAROS_SNAPSHOT" },
|
||||
"nash": $nash_ver,
|
||||
"toolchains": {},
|
||||
|
||||
@@ -4,4 +4,5 @@
|
||||
nash
|
||||
naros-init
|
||||
naros
|
||||
naros-identity
|
||||
naros-keyring
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
nash
|
||||
naros-init
|
||||
naros
|
||||
naros-identity
|
||||
naros-keyring
|
||||
nucleic-linux-agent
|
||||
nucleic-a11y-agent
|
||||
|
||||
@@ -5,4 +5,5 @@
|
||||
nash
|
||||
naros-init
|
||||
naros
|
||||
naros-identity
|
||||
naros-keyring
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
Package: naros-identity
|
||||
Version: @VERSION@
|
||||
Architecture: @ARCH@
|
||||
Maintainer: Nucleic <[email protected]>
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Depends: libc6
|
||||
Description: narOS kernel identity shim (NAROS.md §2.3)
|
||||
narOS ships no kernel of its own — containers share the host's and the VM tiers
|
||||
boot an externally-fetched vmlinux — so uname(2) reports a kernel with no narOS
|
||||
in it, and every consumer that derives an OS string from it (notably the
|
||||
`OS Version:` line agent harnesses build from os.type()+os.release()) misses the
|
||||
identity that /etc/os-release carries. This package preloads a small interposer
|
||||
via /etc/ld.so.preload that appends the narOS release tag to utsname.release,
|
||||
the way a distro kernel package does. sysname stays "Linux" so build tooling
|
||||
that switches on it is unaffected; NAROS_UNAME_PASSTHROUGH=1 disables the tag
|
||||
for callers that resolve /lib/modules/`uname -r`.
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/sh
|
||||
# Register the identity shim in /etc/ld.so.preload (NAROS.md §2.3).
|
||||
#
|
||||
# Idempotent, and additive rather than authoritative: the file is rewritten preserving any
|
||||
# other entries, so this package never owns unrelated preloads. The write goes through a
|
||||
# temp file + rename because /etc/ld.so.preload is read by the loader on EVERY exec — a
|
||||
# partially written list would be observed by whatever runs during the write.
|
||||
set -e
|
||||
|
||||
LIB=/usr/lib/naros/libnaros-uname.so
|
||||
PRELOAD=/etc/ld.so.preload
|
||||
TMP="$PRELOAD.naros-tmp"
|
||||
|
||||
case "$1" in
|
||||
configure)
|
||||
# Belt and braces: never point the loader at a library that is not on disk.
|
||||
if [ ! -f "$LIB" ]; then
|
||||
echo "naros-identity: $LIB missing, not registering preload" >&2
|
||||
exit 0
|
||||
fi
|
||||
if [ -f "$PRELOAD" ] && grep -qxF "$LIB" "$PRELOAD"; then
|
||||
exit 0
|
||||
fi
|
||||
if [ -f "$PRELOAD" ]; then
|
||||
cat "$PRELOAD" > "$TMP"
|
||||
else
|
||||
: > "$TMP"
|
||||
fi
|
||||
echo "$LIB" >> "$TMP"
|
||||
chmod 0644 "$TMP"
|
||||
mv "$TMP" "$PRELOAD"
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/bin/sh
|
||||
# Deregister the identity shim from /etc/ld.so.preload before its files are removed
|
||||
# (NAROS.md §2.3), so the loader never names a library that is no longer on disk.
|
||||
#
|
||||
# Runs on remove/deconfigure only: on `upgrade` the entry must persist, since the
|
||||
# replacement .so lands at the same path and the new postinst is a no-op.
|
||||
set -e
|
||||
|
||||
LIB=/usr/lib/naros/libnaros-uname.so
|
||||
PRELOAD=/etc/ld.so.preload
|
||||
TMP="$PRELOAD.naros-tmp"
|
||||
|
||||
case "$1" in
|
||||
remove | deconfigure)
|
||||
[ -f "$PRELOAD" ] || exit 0
|
||||
# grep exits 1 when nothing survives the filter; that is the empty case, not an error.
|
||||
grep -vxF "$LIB" "$PRELOAD" > "$TMP" || true
|
||||
if [ -s "$TMP" ]; then
|
||||
chmod 0644 "$TMP"
|
||||
mv "$TMP" "$PRELOAD"
|
||||
else
|
||||
# An empty ld.so.preload is legal but pointless — drop the file entirely.
|
||||
rm -f "$TMP" "$PRELOAD"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -0,0 +1,25 @@
|
||||
# Stage the prebuilt narOS kernel identity shim (os/src/naros-identity, built per-arch
|
||||
# into dist/bin by that dir's build.sh / the CI binaries job).
|
||||
#
|
||||
# /etc/ld.so.preload is written by the postinst rather than shipped in files/: dpkg
|
||||
# unpacks a package's files in no guaranteed order, so a shipped preload file could land
|
||||
# before the .so it names and make every binary exec'd for the rest of that transaction —
|
||||
# including dpkg's own maintainer scripts — emit a loader warning. The postinst runs
|
||||
# after the whole package is on disk, so the reference is never dangling.
|
||||
# The /lib/modules alias unit rides along with a static enable symlink (rather than a
|
||||
# `systemctl enable` in the postinst) so it takes effect inside the mmdebstrap chroot,
|
||||
# where no systemd is running — the same pattern nucleic-linux-agent uses.
|
||||
stage() {
|
||||
local dest="$1" arch="$2"
|
||||
local lib="$OS_DIR/dist/bin/libnaros-uname-$arch.so"
|
||||
local unit="$OS_DIR/src/naros-identity/naros-identity-modules.service"
|
||||
if [ ! -f "$lib" ]; then
|
||||
echo "prebuilt shim missing: dist/bin/libnaros-uname-$arch.so" > "$dest/.skip-reason"
|
||||
return 1
|
||||
fi
|
||||
install -D -m 0644 "$lib" "$dest/usr/lib/naros/libnaros-uname.so"
|
||||
install -D -m 0644 "$unit" "$dest/usr/lib/systemd/system/naros-identity-modules.service"
|
||||
install -d "$dest/etc/systemd/system/sysinit.target.wants"
|
||||
ln -sf /usr/lib/systemd/system/naros-identity-modules.service \
|
||||
"$dest/etc/systemd/system/sysinit.target.wants/naros-identity-modules.service"
|
||||
}
|
||||
@@ -4,7 +4,7 @@ Architecture: all
|
||||
Maintainer: Nucleic <[email protected]>
|
||||
Section: metapackages
|
||||
Priority: optional
|
||||
Depends: nash, nash-default-shell, naros-init, naros, ca-certificates, curl, git, openssh-client, iproute2
|
||||
Depends: nash, nash-default-shell, naros-init, naros, naros-identity, ca-certificates, curl, git, openssh-client, iproute2
|
||||
Recommends: naros-keyring
|
||||
Description: narOS base tier (NAROS.md §4)
|
||||
The minimal narOS surface: nash forced as the default shell, naros-init, the
|
||||
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the narOS kernel identity shim into os/dist/bin/libnaros-uname-<arch>.so
|
||||
# (NAROS.md §2.3), the artifact os/packages/naros-identity/stage.sh packages.
|
||||
#
|
||||
# build.sh [arch] arch: arm64|amd64 (default: this host's)
|
||||
#
|
||||
# glibc, dynamically linked, on purpose: the shim only ever loads into glibc processes
|
||||
# via /etc/ld.so.preload, and a static or musl build could not interpose them. CI builds
|
||||
# arm64 with the gcc-aarch64-linux-gnu cross toolchain rather than zig (which the musl
|
||||
# static binaries in this tree use) because a glibc shared object is exactly what the
|
||||
# stock cross-gcc is for.
|
||||
set -euo pipefail
|
||||
|
||||
here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
OS_DIR="$(cd "$here/../.." && pwd)"
|
||||
VERSION="$(cat "$OS_DIR/VERSION")"
|
||||
|
||||
case "${1:-$(dpkg --print-architecture 2>/dev/null || uname -m)}" in
|
||||
arm64 | aarch64) arch=arm64 ;;
|
||||
amd64 | x86_64) arch=amd64 ;;
|
||||
*) echo "unsupported arch: ${1:-}" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
# Cross only when the target differs from the host; a native build wants plain cc.
|
||||
host="$(uname -m)"
|
||||
cc=cc
|
||||
if [ "$arch" = arm64 ] && [ "$host" != aarch64 ]; then cc=aarch64-linux-gnu-gcc; fi
|
||||
if [ "$arch" = amd64 ] && [ "$host" != x86_64 ]; then cc=x86_64-linux-gnu-gcc; fi
|
||||
command -v "$cc" > /dev/null || { echo "compiler not found: $cc" >&2; exit 2; }
|
||||
|
||||
out="$OS_DIR/dist/bin/libnaros-uname-$arch.so"
|
||||
mkdir -p "$(dirname "$out")"
|
||||
"$cc" -shared -fPIC -O2 -Wall -Wextra \
|
||||
-DNAROS_KERNEL_TAG="\"-naros$VERSION\"" \
|
||||
-o "$out" "$here/uname.c"
|
||||
# Match strip to the compiler: the host's strip cannot touch a cross-built object.
|
||||
strip_bin=strip
|
||||
[ "$cc" = cc ] || strip_bin="${cc%gcc}strip"
|
||||
"$strip_bin" "$out" 2> /dev/null || true
|
||||
|
||||
echo "built $out (tag -naros$VERSION, $cc)"
|
||||
@@ -0,0 +1,31 @@
|
||||
[Unit]
|
||||
Description=narOS: alias /lib/modules for the identity-tagged kernel release
|
||||
Documentation=https://github.com/abkslm/nucleic/blob/main/docs/NAROS.md
|
||||
# /lib/modules/`uname -r` is a real path: kmod, depmod and udev all resolve modules
|
||||
# through it. Since naros-identity tags utsname.release (NAROS.md §2.3), the tagged name
|
||||
# has no directory and module autoloading would fail on the tiers that load modules at all
|
||||
# — the VM tiers, which boot an external kernel whose modules the payload bakes in under
|
||||
# the UNTAGGED release. Symlinking the tagged name onto the real directory fixes every
|
||||
# consumer at once, which env-var plumbing into each caller could not.
|
||||
#
|
||||
# Inert on the container tiers: naros-init is PID 1 there, so no unit ever runs, and
|
||||
# containers never load modules anyway.
|
||||
DefaultDependencies=no
|
||||
Before=systemd-modules-load.service sysinit.target
|
||||
After=systemd-remount-fs.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
# Only $(...) substitutions, never bare $var: systemd expands $NAME in ExecStart itself,
|
||||
# so shell variables here would arrive empty.
|
||||
#
|
||||
# Both guards matter. The first requires the real module directory to exist, so a kernel
|
||||
# that ships no modules is left alone. The second requires the tagged path to be ABSENT,
|
||||
# which is what makes this safe when the tag is not in effect (shim not installed, or
|
||||
# passthrough): there tagged == real, the path exists, and we must not replace a real
|
||||
# module directory with a symlink to itself.
|
||||
ExecStart=/bin/sh -c '[ -d "/lib/modules/$(NAROS_UNAME_PASSTHROUGH=1 uname -r)" ] && [ ! -e "/lib/modules/$(uname -r)" ] && ln -sfnT "/lib/modules/$(NAROS_UNAME_PASSTHROUGH=1 uname -r)" "/lib/modules/$(uname -r)"; true'
|
||||
|
||||
[Install]
|
||||
WantedBy=sysinit.target
|
||||
@@ -0,0 +1,73 @@
|
||||
/* narOS kernel identity shim (NAROS.md §2.3).
|
||||
*
|
||||
* narOS does not build its own kernel: containers share the host's (on Cloudflare
|
||||
* Containers that is a Firecracker microVM kernel, whose release string carries a
|
||||
* `-cloudflare-firecracker` suffix) and the VM tiers boot an externally-fetched
|
||||
* vmlinux (scripts/fetch-kernel.sh). So there is no CONFIG_LOCALVERSION to set, and
|
||||
* every uname(2)-derived identity — including the `OS Version:` line agent harnesses
|
||||
* put in their environment block, which is os.type() + os.release() — reports the
|
||||
* host kernel with no narOS in it at all, no matter what /etc/os-release says.
|
||||
*
|
||||
* This interposer, preloaded via /etc/ld.so.preload, appends the narOS release tag to
|
||||
* utsname.release the way a distro kernel package does (Debian's own kernels report
|
||||
* `6.1.0-18-amd64`), so the identity is true at the syscall layer rather than only in
|
||||
* files a caller has to know to read.
|
||||
*
|
||||
* Deliberately narrow: `sysname` stays "Linux". It is the single most-switched-on
|
||||
* uname field in build tooling — autoconf's config.guess, CMAKE_SYSTEM_NAME, node-gyp,
|
||||
* the Go and rustup installers all compare it against "Linux" and fall through to
|
||||
* "unsupported platform" otherwise. `version` is likewise untouched.
|
||||
*
|
||||
* Truth comes from syscall(SYS_uname) rather than dlsym(RTLD_NEXT): a library in
|
||||
* /etc/ld.so.preload is loaded into *every* dynamically-linked process on the system,
|
||||
* including early boot and dpkg's own maintainer scripts, so it must not depend on
|
||||
* libdl being resolvable or risk recursing through an interposed symbol.
|
||||
*
|
||||
* Escape hatch: NAROS_UNAME_PASSTHROUGH=1 returns the kernel's answer verbatim. This
|
||||
* exists because /lib/modules/`uname -r` is a real path — kmod, depmod and udev resolve
|
||||
* modules through it, so the VM tiers need the untagged release to find their external
|
||||
* kernel's modules (see nucleic-modsetup.service, which also symlinks the tagged name
|
||||
* onto the real one so autoloading works without the env var).
|
||||
*
|
||||
* Statically linked binaries, and Go programs that issue the raw syscall themselves,
|
||||
* bypass this by construction — the tag is an identity marker, never a security or
|
||||
* correctness boundary.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Baked at build time from os/VERSION, e.g. "-naros26.07". */
|
||||
#ifndef NAROS_KERNEL_TAG
|
||||
#define NAROS_KERNEL_TAG "-naros"
|
||||
#endif
|
||||
|
||||
static int naros_passthrough(void)
|
||||
{
|
||||
const char *v = getenv("NAROS_UNAME_PASSTHROUGH");
|
||||
return v != NULL && *v != '\0' && strcmp(v, "0") != 0;
|
||||
}
|
||||
|
||||
int uname(struct utsname *buf)
|
||||
{
|
||||
long rc = syscall(SYS_uname, buf);
|
||||
if (rc != 0 || buf == NULL)
|
||||
return (int)rc;
|
||||
if (naros_passthrough())
|
||||
return 0;
|
||||
/* Idempotent: a re-exec through another preloading process must not stack tags. */
|
||||
if (strstr(buf->release, NAROS_KERNEL_TAG) != NULL)
|
||||
return 0;
|
||||
|
||||
size_t have = strnlen(buf->release, sizeof(buf->release));
|
||||
size_t tag = sizeof(NAROS_KERNEL_TAG) - 1;
|
||||
/* utsname.release is a fixed 65-byte field; leave it untagged rather than truncate
|
||||
* the real kernel release, which callers parse for version comparisons. */
|
||||
if (have + tag + 1 > sizeof(buf->release))
|
||||
return 0;
|
||||
memcpy(buf->release + have, NAROS_KERNEL_TAG, tag + 1);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user