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
|
# 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
|
# by the tiers that install them (agent-tier hook, N2). Versions of Nucleic packages are
|
||||||
# queryable via dpkg, listed here for one-stop reads.
|
# 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="$(chroot "$R" dpkg-query -W -f '${Version}' nash 2>/dev/null || echo null)"
|
||||||
[ "$nash_ver" = null ] || nash_ver="\"$nash_ver\""
|
[ "$nash_ver" = null ] || nash_ver="\"$nash_ver\""
|
||||||
cat > "$R/etc/naros/manifest.json" <<EOF
|
cat > "$R/etc/naros/manifest.json" <<EOF
|
||||||
@@ -55,6 +59,7 @@ cat > "$R/etc/naros/manifest.json" <<EOF
|
|||||||
"variant": "$VARIANT",
|
"variant": "$VARIANT",
|
||||||
"flavor": "$FLAVOR",
|
"flavor": "$FLAVOR",
|
||||||
"arch": "$NAROS_ARCH",
|
"arch": "$NAROS_ARCH",
|
||||||
|
"kernel_tag": "-naros$NAROS_VERSION",
|
||||||
"debian": { "suite": "$NAROS_SUITE", "snapshot": "$NAROS_SNAPSHOT" },
|
"debian": { "suite": "$NAROS_SUITE", "snapshot": "$NAROS_SNAPSHOT" },
|
||||||
"nash": $nash_ver,
|
"nash": $nash_ver,
|
||||||
"toolchains": {},
|
"toolchains": {},
|
||||||
|
|||||||
@@ -4,4 +4,5 @@
|
|||||||
nash
|
nash
|
||||||
naros-init
|
naros-init
|
||||||
naros
|
naros
|
||||||
|
naros-identity
|
||||||
naros-keyring
|
naros-keyring
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
nash
|
nash
|
||||||
naros-init
|
naros-init
|
||||||
naros
|
naros
|
||||||
|
naros-identity
|
||||||
naros-keyring
|
naros-keyring
|
||||||
nucleic-linux-agent
|
nucleic-linux-agent
|
||||||
nucleic-a11y-agent
|
nucleic-a11y-agent
|
||||||
|
|||||||
@@ -5,4 +5,5 @@
|
|||||||
nash
|
nash
|
||||||
naros-init
|
naros-init
|
||||||
naros
|
naros
|
||||||
|
naros-identity
|
||||||
naros-keyring
|
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]>
|
Maintainer: Nucleic <[email protected]>
|
||||||
Section: metapackages
|
Section: metapackages
|
||||||
Priority: optional
|
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
|
Recommends: naros-keyring
|
||||||
Description: narOS base tier (NAROS.md §4)
|
Description: narOS base tier (NAROS.md §4)
|
||||||
The minimal narOS surface: nash forced as the default shell, naros-init, the
|
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