Repository navigation
root-w3bs-os.sh: W3BS OS (Alpine/OpenRC) counterpart of root-ubuntu.sh - #198
Merged
Merged
Conversation
…tu.sh Bootstrap and maintain a W3BS OS server as root, idempotently: accounts and groups, confinement, dotfiles, oh-my-zsh/mise, per-user pages and dev apps, remote shares, sandboxed tenants -- ported section by section from root-ubuntu.sh with the same flags, server.conf, state dir and managed-file rule. - packages through the w3bs CLI: w3bs dist-upgrade (w3bs os update once it exists), apk for base packages, w3bs install <catalog id> per account for moshcode/threatcrush/hqterm/sh1pt (curl installers only as fallback), per-account W3BS repository added with the image's pinned fingerprint - Caddy replaces nginx+certbot+acme.sh: per-user import files regenerated every minute, admin API on loopback, WEB_TLS=auto|internal|off - OpenRC first-class, systemd when it is the running init; crond jobs, zram-init, earlyoom, fail2ban, ufw (dual-stack), one ssh-agent per account - cgroup v2 per confined account (sweep + pam_exec hook), wheel as admin - key-only accounts get '*' not '!' (Alpine sshd has no PAM: '!' locks keys) - refuses non-Alpine unless --force; --with-sh1pt, --with/--without-moshcode Tested twice in a row as root in an OpenRC-booted w3bs.org/os:server container: second run "nothing changed", state snapshot identical, 36/36 assertions pass. test/root-w3bs-os.test.ts covers the parse, dry-run and helper paths (and shellcheck when installed). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ThreatCrush Security Scan41 finding(s) HIGH/CRITICAL: 15 | MEDIUM: 12 | LOW: 14
Snippets are redacted; ThreatCrush never prints matched credential material. |
ralyodio
added a commit
that referenced
this pull request
Oct 19, 2026
…/root for dotfiles (#201) Two flip-flops found while porting this script to W3BS OS (root-w3bs-os.sh, #198): - sysctl floors: run 1 raised the keys below the floor and wrote 62-profullstack-confine.conf; run 2 saw the raised running values, wrote nothing and DELETED the file, so the next boot lost the hardening. A key at the floor because of our own file now keeps its line (same for fs.suid_dumpable = 0). - dotfiles: run from /root (the documented way), run 2 found the .zshrc run 1 put there, took /root for the team's dotfiles and copied root's shell config into every account. The script's own directory now counts only when it is a git checkout. Regression tests for both; the suid test sets CONFINE_SYSCTL_CONF instead of rewriting the function. vitest root-ubuntu: 193 passed; no new shellcheck warnings. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
root-w3bs-os.shis the W3BS OS version ofroot-ubuntu.sh. W3BS OS is Alpine with musl, apk, OpenRC and busybox ash. The script was ported section by section, so it keeps the same structure, comment style, flags,server.conf, state dir (/var/lib/profullstack), theinstall_managedrule that leaves user-edited files alone, prompts that only appear on a TTY, and the subcommands (groups,confine,mount/umount/mounts/share,sandbox). It is idempotent: a second run reports "nothing changed".What changed from the Ubuntu script
w3bs dist-upgrade --yes(switches tow3bs os update --yesonce the CLI has it), then apk for base packages. A bootstrap step first installs the GNU tools the script relies on (coreutils, findutils, util-linux, shadow, runuser, …) and enables the community repo on the same branch as mainw3bs install moshcode.sh/moshcode,threatcrush.com/threatcrush,hqterm.sh/hqterm, plussh1pt.com/sh1ptwith--with-sh1pt. Runs per account, checked against w3bs.org/api/packages. The publisher's curl installer is only the fallback when w3bs is missing orW3BS_INSTALL=0.--without-moshcodeturns moshcode off (it is on by default)/etc/periodic/daily/w3bs-os-update, run by crond (AUTO_UPGRADE=0turns it off)IPV6=yes, adds 443/udp for HTTP/3, runlevelbootswapservice), zram-init in/etc/conf.d/zram-init, earlyoom in/etc/conf.d/earlyoom. Under systemd it uses zram-generator as on Ubuntu/etc/caddy/users/<login>.caddy, written byprofullstack-devapps(cron, every minute; it validates and rolls back a bad config). Admin API is on localhost only./etc/caddy/conf.d/*.caddyis for the operator and never touched.WEB_TLS=auto|internal|off. Ported features:/~user,user.domain,app.user.domain(static orreverse_proxy, websockets included), AI-crawler 403,.git404, robots.txt, Moshpit parking on the port-80 catch-all (Caddymap)# root-w3bs-os:<tag>markers, or timers when systemd is the running init~/.ssh/agent.sock, started at login (/etc/profile.d). Under systemd: same as Ubuntuuser-.slice.d)/sys/fs/cgroup/w3bs.confined/<login>(memory.high/max, pids.max, cpu.weight). A cron job moves processes in every minute, and where sshd uses PAM apam_exechook does it at login. Under systemd: same slices as UbuntuCONFINE_EXEMPT_GROUPS=wheel,sudo,admin). Without confinement,DEFAULT_GROUPSiswheelpasswd -lfor key-only accountsusermod -p '*'. Alpine's sshd is built without PAM, and then a!password means a locked account, so even a valid key is refused. The run summary also warns about any account that still has!--forceoverrides. Reads/etc/w3bs-os-release(ID,EDITION)Detecting init. The script uses systemd if it is PID 1, OpenRC if
/run/openrcexists, and otherwisenone. Withnone, packages, accounts and files still converge, but no services are started.Not ported
random_indexandadd_before_body. It is off by default anyway. IfSPONSOR_AD_SLOTis set, the script says so and nothing gets billed.threatcrush install-servicewrites a systemd unit, so it only runs where systemd is the running init. The threatcrush CLI itself is installed everywhere.openssh-server-pam,UsePAM yes).confinereports this as "written, not read".sandbox(incus) is ported but not tested here. Host-side install moved to apk with theincusdOpenRC service. The tenant instance is still Ubuntu, so its bootstrap is unchanged. It was not exercised in the docker test because incus inside docker is out of scope.Two-run test (dev3, OpenRC-booted
w3bs.org/os:server)Test image:
w3bs.org/os:server+openrc+/etc/w3bs-os-release(ID=w3bs-os,EDITION=docker-server), booted with/sbin/initand run asdocker run --privileged --cgroupns=private. Config came from/etc/cli-tools/server.conf:WEB_DOMAIN=dev.w3bs.test,WEB_TLS=internal,SKIP_TAILSCALE=1. Tailscale was skipped because joining needs an auth key.Extra check:
./root-w3bs-os.sh bob --skip-toolsputs bob inusersandconfined, sets his home to 0700 with the caddy ACL, andconfinelists him as confined. Inside a container,confinecorrectly reports that hidepid and cgroup caps are not available.vitest run test/root-w3bs-os.test.ts: 97 passed, 2 skipped. One skipped test needs root on a non-Alpine box; the other needscaddyon PATH. The tests cover syntax,--help, the sh→bash re-exec and the one-sentence message when piped into sh, refusal as non-root, the public-file invariants, theserver.confoverride sweep,read_w3bs_os_release,ensure_apk_repos, thew3bs_toolinstall/update/failure/fallback paths and the fingerprint lookup, the*password, everyWEB_TLSmode in the generated Caddyfile,sh -non the generated devapps script, the crontab and sysctl no-flip-flop rules, the unprivilegedgroups/confine/mount --dry-runcommands, and shellcheck at warning level.test/root-ubuntu.test.tsstill passes.Bugs found while testing (fixed here)
These also exist in root-ubuntu.sh, which this PR does not change:
/rootcopies root's shell config to everyone. After run 1,/roothas a.zshrc, so run 2 treats/rootas a dotfiles checkout and copies root's shell config into every account. This script now only treats the script directory as dotfiles if it is a git checkout.Specific to Alpine/OpenRC (fixed):
as_user.caddy validaterun as root created root-owned log files, which the caddy user then could not open, so Caddy failed to start. Validation now runs as the caddy user.kptr_restrict,protected_fifos,suid_dumpable) were restored./usr/share/w3bs-os/FINGERPRINT(orW3BS_REPO_FINGERPRINT, or the official installer). The fingerprint is never hard-coded.Not merged; for review.
🤖 Generated with Claude Code