mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-28 09:11:11 -07:00
What the image holds ready before any extension package exists, so that
the first one starts inside it.
forgefirm-sandbox (new recipe, on both images):
- the account pool: ffx0 to ffx31, uid and gid 800 to 831, one group
each, /nonexistent, /bin/false, locked. Below 1000 on purpose: the
forgefirm-users render replaces only the accounts from 1000 up, so an
account reset leaves the pool alone and the read-only rootfs never
needs an account made at run time. The image's dynamic system ids
count down from 999 and stop at 997.
- an rcS script at S30: cgroup v2 mounted at /sys/fs/cgroup, the cpu,
memory, and pids controllers handed down to /sys/fs/cgroup/ffx, and
ffx marked idle-class (cpu.idle; the kernel refuses a cpu.weight on
top of it, so none is written). The firmware's processes stay in the
root group. `status` reports both halves and exits nonzero when
either is missing.
- /etc/forgefirm/ffx.nft, loaded by the same script, before the network
starts in rc5: table inet ffx, an output-hook filter with policy
accept that sends uid 800-831 to chain pool; pool looks the uid up in
the verdict map `allow`, then answers TCP with a reset (a drop would
leave a connect to time out) and drops the rest (the sender sees
EPERM), both counted. The map is the one way through: a uid mapped to
a chain of that package's destinations. Loading the file again
replaces the table, allowlists included: it fails closed.
nftables comes in as its runtime dependency, trimmed in the distro config
to the binary and its library with JSON output: no interactive shell, no
Python binding. gmp and jansson were on the image; libmnl and libnftnl are
new. The release rootfs goes from 34.5 to 34.1 MiB free.
scripts/sandbox-rules-test.py, and the workflow sandbox-ci that runs it:
the rule file loaded into a network namespace of its own and sent at from
real uids. Root, 799, and 832 are not touched; 800, 815, and 831 are
refused on 127.0.0.1 and ::1 at once, UDP with EPERM, and a receiver hears
nobody from the pool; an allow chain opens one port on one address to one
uid and nothing else; a reload closes it.
exthost.platform (new suite module exthost.py): the platform proven on a
probe process, not read off a config. In a probe group under ffx, as the
last pool uid: held to cpu.max, stopped by cgroup.freeze and running again
after, stopped at pids.max, killed by the group's own OOM at memory.max
while forgectrl keeps its pid. The 32 accounts as the boot's render left
them. Pool uids 800 and 831 refused TCP to forgectrl on loopback (both
ports, IPv4 and IPv6), to the LAN address, and to the Grbl port, at once,
UDP EPERM, with the rules' counters moving by at least the attempts, while
root reaches the same listeners. A root probe under landlock loses /etc
and TCP connects and keeps /usr; a seccomp filter returns EPERM for the
filtered call. The probe group is removed whatever happens.
Proven. The rules test passes with nft 1.0.9, the image's version, and
three controls each fail it: the range one uid short, the TCP reject
turned to accept, the delete-table line removed. The unit suite passes
(422) with no undefined name. Image 20260920211625 carries all of it (read
back from both rootfs images: 32 accounts in passwd, group, and shadow,
S30forgefirm-sandbox, the rule file and the script byte-identical, nft
with its libraries and no Python binding). On the bench reference, that
image: exthost.platform PASS (5.0 percent of the core under a 5 percent
cpu.max, 0 us frozen and 87358 us thawed over 1.5 s each, 5 of 12 forks
then EAGAIN, rc -9 with oom_kill 1 at a 24 MiB memory.max, the counters
[0, 0] to [12, 4], landlock ABI 6), and forgefirm-sandbox status reports
both halves in place.
Acceptance. exthost.platform gates the platform; sandbox-ci gates the rule
file. The recipe, the rules, and the distro option are layer content, in
the platform identity of every fingerprint.
74 lines
3.6 KiB
Plaintext
74 lines
3.6 KiB
Plaintext
# SPDX-License-Identifier: MIT
|
|
|
|
require conf/distro/include/fslc-base.inc
|
|
|
|
DISTRO = "forgefirm"
|
|
DISTRO_NAME = "OpenGlow/ForgeFIRM"
|
|
DISTRO_VERSION = "0.0.0"
|
|
|
|
DISTRO_FEATURES:remove = " \
|
|
3g alsa avahi bluetooth bluez5 ext2 irda nfc nfs pci pcmcia \
|
|
pulseaudio vulkan wayland x11 zeroconf "
|
|
|
|
# The name the image ships in /etc/hostname. forgefirm-hostname replaces
|
|
# it at S38 in rcS with forgefirm-<xxxx>, from the machine's MAC address;
|
|
# this is what the few seconds before that show, and the fallback on a
|
|
# machine whose MAC address cannot be read.
|
|
hostname:pn-base-files = "forgefirm"
|
|
|
|
# No mDNS. The panel is reached at the machine's address, which every
|
|
# network resolves; the machine's own name (forgefirm-<xxxx>, from
|
|
# forgefirm-hostname) goes out in the DHCP request, so a network with
|
|
# dynamic DNS publishes it as well. The avahi and zeroconf features stay
|
|
# removed above: zeroconf would install packagegroup-base-zeroconf (the
|
|
# daemon plus libnss-mdns), and avahi would switch other recipes' avahi
|
|
# options on.
|
|
|
|
# TLS: forgectrl serves HTTPS on 443 with a self-signed certificate
|
|
# through libmicrohttpd and ulfius, and links GnuTLS itself. GnuTLS is
|
|
# trimmed to the library:
|
|
# no seccomp the distro default adds libseccomp for the gnutls test
|
|
# programs; the library does not use it
|
|
# no libidn internationalized host names in certificates; the
|
|
# machine's own certificate is plain ASCII (saves libidn2)
|
|
# no p11-kit PKCS#11 tokens and the system trust store; the machine
|
|
# presents its own certificate and verifies no peer
|
|
# through GnuTLS (saves p11-kit and its modules)
|
|
# libtasn1 stays a shared library: the bundled copy is the same
|
|
# code without poky's CVE patches
|
|
# no tpm, fips, dane, and no certificate compression (brotli, zlib,
|
|
# zstd), none of which the panel uses
|
|
PACKAGECONFIG:pn-gnutls = "libtasn1"
|
|
|
|
# opengl stays: forgectrl's camera demosaic runs as GLES2 fragment
|
|
# shaders on the GC880 (etnaviv), reached through surfaceless EGL with
|
|
# no display stack. Mesa is trimmed to exactly that: the etnaviv
|
|
# gallium driver, GLES/EGL/GBM, no GLX, no X11/Wayland platforms
|
|
# (both remain removed above), so the cost is the libraries and the one
|
|
# driver, not a graphics stack.
|
|
PACKAGECONFIG:pn-mesa = "opengl gles egl gbm gallium etnaviv"
|
|
# udev's hardware database is USB and PCI vendor/product identities (7.7 MB);
|
|
# the board has neither bus.
|
|
BAD_RECOMMENDATIONS += "eudev-hwdb"
|
|
|
|
# System logger: rsyslog replaces busybox syslogd/klogd. Every ForgeFIRM
|
|
# process logs through it and it is the only log writer (one directory
|
|
# per logger under /data/log/forgefirm; per-logger levels and the remote
|
|
# target come from the machine settings). Trimmed to what the image uses:
|
|
# the local socket and kernel inputs, file output, plain UDP/TCP
|
|
# forwarding, and rainerscript filters - no TLS, database, HTTP, or
|
|
# signing modules (rootfs must fit the 200 MiB factory slot).
|
|
VIRTUAL-RUNTIME_base-utils-syslog = "rsyslog"
|
|
PACKAGECONFIG:pn-rsyslog = "rsyslogd rsyslogrt klog inet regexp"
|
|
|
|
# nftables: the extension sandbox's rule loader (forgefirm-sandbox). The nft
|
|
# binary and its library, with JSON output so a program can read back what
|
|
# the kernel holds; no interactive shell (readline) and no Python binding.
|
|
# gmp and jansson are on the image already.
|
|
PACKAGECONFIG:pn-nftables = "json"
|
|
|
|
# Local (file://) source checksums leave out a workstation's Python bytecode
|
|
# caches: the forgetest recipe fetches its package directory whole, and a
|
|
# host test run must not move a task hash without a source change.
|
|
BB_SIGNATURE_LOCAL_DIRS_EXCLUDE = "CVS .bzr .git .hg .osc .p4 .repo .svn __pycache__ .pytest_cache"
|