mirror of
https://github.com/ryan4yin/nix-config.git
synced 2026-11-13 12:13:36 +01:00
docs(nixpak): consolidate runtime validation notes
Merge the three runtime-validation files into one concise record, update the interactive browser and Zoom results, and document the intentional Chromium host-loopback and Brave/Edge no-media exceptions. Tighten the remaining cookie-crypto workaround and the smoke-test prerequisites. Assisted-by: DeepSeek Harness (deepseek-flash)
This commit is contained in:
1 parent
ccffddc864
commit
87b7a23cf3
8 files changed
+115
-242
No files matched your search
+10
-6
@@ -326,9 +326,11 @@ isolation. Source review and socket metadata—not reads of private credentials
|
||||
- All nine desktop wrappers use shared nixpak policies: cleared environment, filtered session D-Bus
|
||||
and selected mounts. The [permission inventory](hardening/README.md#app-permissions) lists each
|
||||
app's file, network, credential and media grants.
|
||||
- Only Firefox/Chromium share host networking. Other apps isolate loopback with pasta but can still
|
||||
reach the LAN/Internet. Browser media and browserpass are enabled only for
|
||||
Firefox/Chromium/Chrome; browserpass exposes password-store/GPG data and agent operations.
|
||||
- Only Firefox/Chromium share host networking. Chromium is an intentional local-development
|
||||
exception; all other apps isolate loopback with pasta but can still reach the LAN/Internet.
|
||||
Browser media and browserpass are enabled only for Firefox/Chromium/Chrome. Brave Origin and Edge
|
||||
are backup browsers with no media grants. browserpass exposes password-store/GPG data and agent
|
||||
operations.
|
||||
- Nixpak supplements Chromium's renderer sandbox, which remains enabled. Zoom/WeChat retain their
|
||||
FHS runtimes inside the outer boundary; inner mounts cannot recover hidden host resources.
|
||||
- Desktop access has limits: GPU drivers remain exposed, PulseAudio permits recording, read-only
|
||||
@@ -339,9 +341,11 @@ isolation. Source review and socket metadata—not reads of private credentials
|
||||
browser receives broad GNOME Keyring authority. Firefox uses its own NSS storage.
|
||||
- **Rollout note (WA-045):** v12 is verified on a disposable profile, but if the portal is
|
||||
unavailable Chromium may fall back to weaker v10 storage; portal initialization is monitored.
|
||||
- Runtime evidence is partial: Brave/Edge headless export times out; interactive login, media, IME
|
||||
and file workflows require acceptance. See the
|
||||
[validation records](hardening/README.md#validation).
|
||||
- Policy/build passes for all nine wrappers. Interactive validation passed for Firefox, Chrome,
|
||||
Chromium, Brave Origin, Edge, QQ, Telegram and Zoom; the Chromium smoke test also validates
|
||||
renderer sandboxing, environment clearing, bind scope and profile persistence. WeChat's
|
||||
interactive Wayland check remains pending. See the
|
||||
[runtime validation](hardening/tests/runtime-validation.md).
|
||||
|
||||
#### Agent-first diagnostics by host role
|
||||
|
||||
|
||||
+6
-6
@@ -83,12 +83,12 @@ A known limitation with no fix yet.
|
||||
|
||||
An unfinished feature or gap.
|
||||
|
||||
| ID | What & where | Why | Removal condition | Added | Revisit | Status |
|
||||
| ------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------ | ---------- |
|
||||
| WA-013 | NixOS tests — `outputs/README.md` | full-host tests still rebuild custom packages (no private cache) and need the host ssh key to decrypt agenix; the shared-firewall security-exporters VM test now runs in CI (KVM); `outputs/x86_64-linux/nixos-tests/idols-ruby.nix` is exposed as `packages.x86_64-linux.ruby-nixos-tests` but is not in `checks` or CI | Resolve when a private cache exists and tests can run per-service | 2024-03-13 | quarterly | active |
|
||||
| WA-027 | NFS golden store `no_root_squash` — `hosts/12kingdoms-youko/nfs-golden.nix` | the existing PVC subdirectories are root-owned, so enabling `root_squash` would break running workloads until they are migrated; the CSI node plugin is hostNetwork and sources from the allowlisted node IPs | Drop when the PVC subdirectories are migrated and `root_squash` is enabled | 2026-10-07 | next maintenance window | active |
|
||||
| WA-044 | transmission AppArmor lock permission — `hosts/12kingdoms-youko/homelab-services/transmission.nix` | nixpkgs' transmission 4.1.4 profile grants `rw` on the daemon's config dir but not `k`, while 4.1.4 takes an fcntl lock on `~/.config/transmission-daemon/lock`; the daemon exits with "Another process is already using". A `local/bin.transmission-daemon` include adds `rwk` for that lock file only | Drop once the nixpkgs profile includes the lock permission | 2026-10-31 | on the next nixpkgs bump | active |
|
||||
| WA-045 | Chromium cookie crypto — `hardening/nixpaks/modules/chromium-browser.nix` | Chromium-family browsers use the app-scoped Secret portal and write v12 cookies. If the portal is unavailable, Chromium may fall back to weak v10 storage. Brave/Edge headless export also times out. | Drop once the v10 fallback is eliminated and the Brave/Edge headless checks pass; monitor portal initialization and interactive browser checks | 2026-10-31 | on Chromium updates | active gap |
|
||||
| ID | What & where | Why | Removal condition | Added | Revisit | Status |
|
||||
| ------ | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ---------- | ------------------------ | ---------- |
|
||||
| WA-013 | NixOS tests — `outputs/README.md` | full-host tests still rebuild custom packages (no private cache) and need the host ssh key to decrypt agenix; the shared-firewall security-exporters VM test now runs in CI (KVM); `outputs/x86_64-linux/nixos-tests/idols-ruby.nix` is exposed as `packages.x86_64-linux.ruby-nixos-tests` but is not in `checks` or CI | Resolve when a private cache exists and tests can run per-service | 2024-03-13 | quarterly | active |
|
||||
| WA-027 | NFS golden store `no_root_squash` — `hosts/12kingdoms-youko/nfs-golden.nix` | the existing PVC subdirectories are root-owned, so enabling `root_squash` would break running workloads until they are migrated; the CSI node plugin is hostNetwork and sources from the allowlisted node IPs | Drop when the PVC subdirectories are migrated and `root_squash` is enabled | 2026-10-07 | next maintenance window | active |
|
||||
| WA-044 | transmission AppArmor lock permission — `hosts/12kingdoms-youko/homelab-services/transmission.nix` | nixpkgs' transmission 4.1.4 profile grants `rw` on the daemon's config dir but not `k`, while 4.1.4 takes an fcntl lock on `~/.config/transmission-daemon/lock`; the daemon exits with "Another process is already using". A `local/bin.transmission-daemon` include adds `rwk` for that lock file only | Drop once the nixpkgs profile includes the lock permission | 2026-10-31 | on the next nixpkgs bump | active |
|
||||
| WA-045 | Chromium cookie crypto — `hardening/nixpaks/modules/chromium-browser.nix` | Chromium-family browsers use the app-scoped Secret portal and write v12 cookies. If the portal is unavailable, Chromium may fall back to weak v10 storage. | Drop once the v10 fallback is eliminated; monitor portal initialization and the `Local State.os_crypt.portal.prev_init_success` flag | 2026-10-31 | on Chromium updates | active gap |
|
||||
|
||||
## Related
|
||||
|
||||
|
||||
+16
-14
@@ -1,6 +1,6 @@
|
||||
# Linux Hardening
|
||||
|
||||
Deployed and interactively validated on `ai` (2026-10-31). Policy and limits:
|
||||
Deployed and interactively validated on the Linux desktop host (2026-10-31). Policy and limits:
|
||||
[SECURITY.md](../SECURITY.md).
|
||||
|
||||
## Shared boundaries
|
||||
@@ -10,7 +10,9 @@ Deployed and interactively validated on `ai` (2026-10-31). Policy and limits:
|
||||
- Read-only `/nix/store`; GPU and display; fonts and themes; LAN and Internet.
|
||||
- Portals for file chooser, URI opening, notifications, input methods and document exports.
|
||||
- No whole host home, system D-Bus, SSH agent, USB/serial, raw camera or raw PipeWire socket.
|
||||
- Host loopback only for Firefox/Chromium; all other apps use pasta-isolated loopback.
|
||||
- Host loopback only for Firefox/Chromium; all other apps use pasta-isolated loopback. Chromium is
|
||||
an intentional development exception. Brave Origin and Edge are backup browsers and receive no
|
||||
media grants.
|
||||
|
||||
## App permissions
|
||||
|
||||
@@ -50,21 +52,21 @@ All browsers get read-only NSS certificates, printing, native notifications and
|
||||
capture.
|
||||
- PulseAudio grants recording authority. Read-only GPG sockets still permit signing and decryption.
|
||||
- pasta isolates loopback, not LAN access. Chromecast discovery is unverified.
|
||||
- Brave/Edge renderer and permission checks pass, but headless page export still times out.
|
||||
- Brave Origin and Edge intentionally have no PulseAudio, Camera, ScreenCast or MPRIS grants. They
|
||||
are backup browsers, not media browsers.
|
||||
|
||||
## Validation
|
||||
|
||||
- Passed: `just fmt`, `just test`, `ai-niri` build and `shoukei-niri` eval.
|
||||
- Deployed on `ai`; interactive startup and tray checks passed for Chrome, Firefox, QQ, Telegram and
|
||||
Zoom.
|
||||
- WeChat uses the native deb package from nixpkgs-patched, with nixpak as the only bwrap layer. The
|
||||
build passes; interactive Wayland validation is pending.
|
||||
- Chromium/Chrome: renderer sandbox, mounts, rendering and disposable-profile persistence passed.
|
||||
Chromium reaches host loopback; Chrome cannot.
|
||||
- Firefox: disposable headless screenshot passed.
|
||||
- Brave/Edge: renderer and permission checks passed; headless page export timed out.
|
||||
- Evidence: [browsers](tests/browser-runtime.md),
|
||||
[Firefox/QQ/Telegram](tests/unified-app-runtime.md), [Zoom](tests/zoom-runtime.md).
|
||||
- Passed: `just fmt`, `just test`, `ai-niri` build, `shoukei-niri` eval and all wrapper builds.
|
||||
- Interactive validation passed for Firefox, Chrome, Chromium, Brave Origin, Edge, QQ, Telegram and
|
||||
Zoom. Chromium's host-loopback/personal-file exception and the Brave/Edge no-media policy are
|
||||
intentional.
|
||||
- Chromium smoke test passed: renderer sandbox, environment clearing, bind scope, disposable-profile
|
||||
persistence and loopback policy.
|
||||
- WeChat uses the native nixpkgs-patched package with nixpak as the only bwrap layer. Build/policy
|
||||
pass; interactive Wayland validation is still pending.
|
||||
- Evidence and repeat commands: [runtime validation](tests/runtime-validation.md),
|
||||
[policy test](tests/policy.nix).
|
||||
|
||||
## Code map
|
||||
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
# Browser runtime verification
|
||||
|
||||
- Run from the repository root as the Linux desktop user, with user D-Bus; no sudo or activation.
|
||||
- Tests use disposable profiles only, synthetic environment canaries, and no environment dumps.
|
||||
- **Keep HOME under `/var/tmp`:** the sandbox's private `/tmp` masks a HOME created there. Rendering
|
||||
reads a host-prepared HTML file through the profile bind; persistence requires a nonempty
|
||||
`Local State` file in the disposable host HOME.
|
||||
|
||||
## Reproduce
|
||||
|
||||
Build against the locked flake inputs:
|
||||
|
||||
```bash
|
||||
mkdir -p browser-validation
|
||||
for app in chromium google-chrome brave-origin microsoft-edge; do
|
||||
APP="$app" nix build --impure --out-link "browser-validation/$app" --expr '
|
||||
let
|
||||
flake = builtins.getFlake (toString ./.);
|
||||
pkgs = import flake.inputs.nixpkgs {
|
||||
system = builtins.currentSystem;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
in pkgs.callPackage
|
||||
(./hardening/nixpaks + "/${builtins.getEnv "APP"}.nix")
|
||||
{
|
||||
mkNixPak = flake.inputs.nixpak.lib.nixpak {
|
||||
inherit pkgs;
|
||||
inherit (pkgs) lib;
|
||||
};
|
||||
}
|
||||
'
|
||||
done
|
||||
```
|
||||
|
||||
Run each test separately so a failing browser does not hide the other results:
|
||||
|
||||
```bash
|
||||
python3 hardening/tests/verify-chromium.py browser-validation/chromium/bin/chromium
|
||||
python3 hardening/tests/verify-chromium.py browser-validation/google-chrome/bin/google-chrome
|
||||
python3 hardening/tests/verify-chromium.py browser-validation/brave-origin/bin/brave-origin
|
||||
python3 hardening/tests/verify-chromium.py browser-validation/microsoft-edge/bin/microsoft-edge
|
||||
```
|
||||
|
||||
- Each launch has a 35-second limit; failures exit nonzero. Temporary HOME is cleaned up.
|
||||
- Intermediate JSON records process checks; final JSON appears only after all checks pass.
|
||||
|
||||
## Current measured results
|
||||
|
||||
Host: `ai`, x86_64 Linux. All four rebuilt after the shared policy/media opt-in changes and tested
|
||||
with `/var/tmp` HOME, explicit Fontconfig configuration, CJK fonts, and narrow NVIDIA mappings.
|
||||
|
||||
| Browser | Process/security checks | HTML rendering | Profile persistence | Host loopback | Overall |
|
||||
| ---------------------- | ----------------------- | --------------- | ------------------- | ------------- | -------- |
|
||||
| Chromium 156.0.8078.73 | Pass | Pass | Pass | Pass: allowed | **Pass** |
|
||||
| Chrome 156.0.8078.73 | Pass | Pass | Pass | Pass: blocked | **Pass** |
|
||||
| Brave Origin 1.98.60 | Pass | **35s timeout** | Not reached | Not reached | **Fail** |
|
||||
| Edge 156.0.4314.42 | Pass | **35s timeout** | Not reached | Not reached | **Fail** |
|
||||
|
||||
Measured evidence:
|
||||
|
||||
- All four build and have live renderers with `Seccomp=2`, `NoNewPrivs=1`; no sandbox-disabling
|
||||
flags are present.
|
||||
- Actual bwrap arguments pass user/PID namespace, effective network flag, clear environment, new
|
||||
session, parent-death, and private `/dev/shm` checks. No broad HOME/`/home`/`/dev`, SSH,
|
||||
system-bus, raw video, X11, GVfs, or direct PipeWire binds. Brave/Edge also have no browserpass
|
||||
credential or PulseAudio binds.
|
||||
- All nine canaries are absent: `NIXPAK_VALIDATION_CANARY`, `SSH_AUTH_SOCK`, `GPG_AGENT_INFO`, and
|
||||
upper/lowercase `HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY`.
|
||||
- Chromium/Chrome render the prepared file without Fontconfig errors and persist `Local State`. Host
|
||||
HTTP control succeeds; Chromium fetches its marker. Chrome reports `ERR_CONNECTION_REFUSED`,
|
||||
returns no marker, and sends zero requests to the host listener.
|
||||
- Brave/Edge stall on `--headless=new --dump-dom` despite `--timeout=10000`. Cause unresolved;
|
||||
system D-Bus/UPower warnings also occur in passing browsers. No upstream-package control was run,
|
||||
so this does not establish a wrapper regression.
|
||||
|
||||
## Current permission checks
|
||||
|
||||
- **Firefox/Chromium/Chrome:** evaluated media opt-in enables PulseAudio, camera/screencast portals,
|
||||
MPRIS, and inhibition. **Brave/Edge:** all disabled; live socket-bind checks pass.
|
||||
- Chromium/Chrome main processes enable `WaylandWindowDecorations,WebRtcPipeWireCamera`. Brave/Edge
|
||||
retain Wayland decorations and have **no camera feature**; all flag checks pass.
|
||||
- Firefox/Chromium share host networking and allow personal files; Chrome/Brave/Edge use pasta
|
||||
isolation without personal-file access. Firefox parity is evaluated, not runtime-tested here.
|
||||
- GPU nodes remain available for webpage rendering. Interactive audio/camera/capture, hardware
|
||||
acceleration, CJK/IME, portals, downloads, and browserpass remain unverified by this headless
|
||||
test.
|
||||
- Inventory: [hardening README](../README.md). Security limits: [SECURITY.md](../../SECURITY.md).
|
||||
|
||||
## Cookie crypto
|
||||
|
||||
- Chromium-family wrappers enable `SecretPortalKeyProviderUseForEncryption` and receive only
|
||||
`org.freedesktop.portal.Secret.RetrieveSecret`; they do **not** get
|
||||
`--talk=org.freedesktop.secrets`.
|
||||
- A disposable-profile probe under `/var/tmp` served an HTTP `Set-Cookie`, then inspected the
|
||||
profile DB: the cookie prefix was `v12` (`763132`) and
|
||||
`Local State.os_crypt.portal.prev_init_success` was true.
|
||||
- If the portal is unavailable, Chromium can fall back to v10. That is weak; monitor portal startup
|
||||
Portal startup is a monitored degradation, not a fail-closed guarantee.
|
||||
- Firefox uses NSS (`key4.db`/`logins.json`) and `cookies.sqlite`, not Chrome OSCrypt. Set a Firefox
|
||||
Primary Password if saved-login at-rest protection matters.
|
||||
- Sources:
|
||||
[feature defaults](https://github.com/chromium/chromium/blob/156.0.8078.73/chrome/browser/browser_features.cc),
|
||||
[v12 provider](https://github.com/chromium/chromium/blob/156.0.8078.73/components/os_crypt/async/browser/secret_portal_key_provider.cc),
|
||||
[prefix decryption](https://github.com/chromium/chromium/blob/156.0.8078.73/components/os_crypt/async/common/encryptor.cc).
|
||||
|
||||
## Check these files
|
||||
|
||||
```bash
|
||||
ruff format --check hardening/tests/verify-chromium.py
|
||||
ruff check hardening/tests/verify-chromium.py
|
||||
prettier --check hardening/tests/browser-runtime.md
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Manual Chrome media smoke test
|
||||
|
||||
Use the official WebRTC samples. They require no account and do not upload the stream; the preview
|
||||
stays local. Chrome uses pasta-isolated loopback, so a test server on the host is intentionally not
|
||||
reachable.
|
||||
|
||||
- Camera + microphone: <https://webrtc.github.io/samples/src/content/getusermedia/gum/>
|
||||
- Click **Start**, accept Chrome and portal prompts, verify local video preview and audio level.
|
||||
- Screen sharing: <https://webrtc.github.io/samples/src/content/getusermedia/getdisplaymedia/>
|
||||
- Click **Start**, choose a window/screen, verify preview, then **Stop**.
|
||||
- Files: use any site's upload control, then download the same file; confirm the file chooser and
|
||||
Downloads work.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Runtime validation
|
||||
|
||||
Nixpak wrappers are accepted in three layers:
|
||||
|
||||
1. **Policy/build:** `just test` evaluates every wrapper and [`policy.nix`](policy.nix) rejects
|
||||
broad mounts, system D-Bus, keyring access and device grants outside each app's declared scope.
|
||||
2. **Disposable smoke test:** [`verify-chromium.py`](verify-chromium.py) launches a Chromium-family
|
||||
wrapper with a temporary HOME and validates renderer sandboxing, environment clearing, bind
|
||||
scope, profile persistence and loopback policy.
|
||||
3. **Interactive desktop check:** launch the real app, use its normal file/network/device workflows,
|
||||
and confirm the sandbox does not break required integration.
|
||||
|
||||
## Current status
|
||||
|
||||
| App | Policy/build | Runtime |
|
||||
| ---------------------- | ------------ | ----------------------------------------------------- |
|
||||
| Firefox | Pass | Interactive pass |
|
||||
| Chromium | Pass | Automated smoke + interactive pass |
|
||||
| Chrome | Pass | Interactive pass |
|
||||
| Brave Origin | Pass | Interactive pass; backup browser without media grants |
|
||||
| Edge | Pass | Interactive pass; backup browser without media grants |
|
||||
| Zoom | Pass | Interactive pass |
|
||||
| QQ / Telegram | Pass | Interactive startup/tray pass |
|
||||
| WeChat (native nixpak) | Pass | Interactive Wayland validation pending |
|
||||
|
||||
Intentional exceptions:
|
||||
|
||||
- **Chromium** keeps host loopback and personal files for local development. Firefox has the same
|
||||
exception. Chrome, Brave Origin and Edge use pasta-isolated loopback.
|
||||
- **Brave Origin and Edge are backup browsers.** They intentionally have no PulseAudio, Camera,
|
||||
ScreenCast or MPRIS grants.
|
||||
|
||||
## Browser smoke test
|
||||
|
||||
Build a wrapper from the locked inputs:
|
||||
|
||||
```bash
|
||||
APP=chromium nix build --impure --out-link "browser-validation/$APP" --expr '
|
||||
let
|
||||
flake = builtins.getFlake (toString ./.);
|
||||
pkgs = import flake.inputs.nixpkgs {
|
||||
system = builtins.currentSystem;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
in
|
||||
pkgs.callPackage (./hardening/nixpaks + "/${builtins.getEnv "APP"}.nix") {
|
||||
mkNixPak = flake.inputs.nixpak.lib.nixpak { inherit pkgs; inherit (pkgs) lib; };
|
||||
}
|
||||
'
|
||||
python3 hardening/tests/verify-chromium.py "browser-validation/$APP/bin/$APP"
|
||||
```
|
||||
|
||||
The script writes its disposable HOME under `/var/tmp`; the sandbox's private `/tmp` would otherwise
|
||||
hide profile binds. Pasta-wrapped browsers also require a usable `/dev/net/tun` on the host. The
|
||||
script checks live renderer `Seccomp=2` and `NoNewPrivs=1`, absence of `--no-sandbox`, canary-free
|
||||
environments, effective bwrap arguments, profile persistence and loopback reachability.
|
||||
|
||||
Chromium-family browsers use the app-scoped Secret portal for v12 cookie encryption; they do not
|
||||
receive `org.freedesktop.secrets`. If the portal is unavailable, Chromium can fall back to weaker
|
||||
v10 storage; monitor `Local State.os_crypt.portal.prev_init_success`. Firefox uses NSS storage
|
||||
instead. Browserpass separately exposes the password store and GPG-agent socket to
|
||||
Firefox/Chromium/Chrome only.
|
||||
|
||||
## Zoom
|
||||
|
||||
Zoom keeps its upstream FHS runtime inside the outer nixpak boundary. The nested sandbox propagates
|
||||
`/.flatpak-info`, uses pasta-isolated networking, and binds only `DISPLAY`, `XAUTHORITY`,
|
||||
`/dev/video0`, `/dev/video1`, DRM sysfs and the declared app directories. X11 remains a
|
||||
shared-display trust boundary: ScreenCast portal permission does not prevent capture through the X11
|
||||
connection. Interactive login, audio, camera, screen sharing and file workflows passed.
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
just fmt
|
||||
just test
|
||||
git diff --check
|
||||
ruff format --check hardening/tests/verify-chromium.py
|
||||
ruff check hardening/tests/verify-chromium.py
|
||||
prettier --check hardening/tests/runtime-validation.md
|
||||
```
|
||||
@@ -1,35 +0,0 @@
|
||||
# Unified desktop-policy validation
|
||||
|
||||
Validation on the Linux `ai` host, using the pinned nixpkgs and nixpak inputs. These checks use
|
||||
disposable profiles and do not read existing browser or chat accounts.
|
||||
|
||||
| Check | Result | Scope |
|
||||
| --------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Firefox, QQ, Telegram derivations | Pass | All three wrappers evaluated and built after conversion to the shared desktop policy. |
|
||||
| Browser policy regression checks | Pass | `policy.nix` returned `true`; covers Firefox/Chromium capability and portal-call parity, host loopback, Chrome isolated networking and media grants, browserpass scope, and forbidden service/device access. |
|
||||
| Firefox headless screenshot | Pass | Exit status 0; `about:blank` produced a PNG with a fresh profile and HOME under `/var/tmp`. |
|
||||
| QQ startup | Partial | With a disposable HOME and `--ozone-platform=headless`, initialized private QQ data and stayed running for 12 seconds until explicitly terminated. Headless EGL/GPU initialization errors occurred. No account login or media function was tested. |
|
||||
| Telegram runtime | Not run | Evaluation and build only. |
|
||||
|
||||
The Firefox invocation used the built wrapper with
|
||||
`--headless --no-remote --profile "$HOME/.mozilla/test" --screenshot "$HOME/Downloads/smoke.png" about:blank`.
|
||||
The disposable environment retained only the launcher's PATH, USER, locale, Wayland display and
|
||||
session-bus/runtime-directory variables, with HOME and all XDG storage paths redirected to the
|
||||
temporary tree. Existing account profiles were not mounted. The temporary tree was removed after the
|
||||
process exited.
|
||||
|
||||
Use `/var/tmp`, not `/tmp`, for disposable host profiles. Nixpak emits the sandbox's private `/tmp`
|
||||
mount after filesystem binds; a profile prepared under host `/tmp` is hidden by that mount. An
|
||||
application that creates a fresh profile inside the resulting tmpfs can appear to work without
|
||||
testing persistence.
|
||||
|
||||
The common policy replaces the earlier direct camera-device and PipeWire-socket grants with explicit
|
||||
Camera and ScreenCast portal methods. QQ and Telegram retain their private XDG data and existing
|
||||
personal-file mappings, and opt into notifications, tray, MPRIS and accessibility. Firefox retains
|
||||
its `.mozilla` profile and MPRIS artwork mounts and shares browser capability definitions with
|
||||
Chromium.
|
||||
|
||||
Interactive validation remains necessary for camera capture, microphone input, video playback,
|
||||
desktop/window sharing, file chooser/save operations, IME, tray, notifications and media controls.
|
||||
The QQ headless startup is not evidence that those functions work. Browser encrypted-login behavior
|
||||
is covered in `browser-runtime.md`.
|
||||
@@ -2,7 +2,8 @@
|
||||
"""Runtime smoke test: verify a built nixpak Chromium without existing profiles.
|
||||
|
||||
Usage: python3 hardening/tests/verify-chromium.py /nix/store/.../bin/chromium
|
||||
Requires Linux procfs and an active user D-Bus session. Uses only stdlib.
|
||||
Requires Linux procfs and an active user D-Bus session. Pasta-wrapped browsers also
|
||||
need /dev/net/tun. Uses a disposable HOME under /var/tmp and only the standard library.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
# Zoom nested runtime verification
|
||||
|
||||
The Zoom 7.2.1.5760 candidate built and opened two `Zoom Workplace` X11 windows on the NVIDIA
|
||||
desktop. The final tested output was
|
||||
`/nix/store/yy6sxsgn5rqafvvgs3k53akwqjcfa2r7-nixpak-zoom-7.2.1.5760`.
|
||||
|
||||
The live main process had `NoNewPrivs: 1`, a network namespace different from the host, and
|
||||
`/.flatpak-info` with `name=us.zoom.Zoom`. Its ancestry included ZoomLauncher and the nested
|
||||
bubblewrap processes. Its `/dev` contained only standard synthetic devices, `dri`, `nvidia0`,
|
||||
`nvidiactl`, `nvidia-modeset`, and `nvidia-uvm`; no camera, USB or block devices were exposed.
|
||||
Zoom's main process reported `Seccomp: 0`; this test does not establish a renderer seccomp
|
||||
guarantee.
|
||||
|
||||
The upstream FHS runtime remains intact. Its automatic root mounts propagate directories, so the
|
||||
wrapper explicitly propagates the outer `/.flatpak-info` file for portal identification. Its `/dev`
|
||||
bind inherits the outer sandbox's filtered devices. The outer network uses pasta isolate mode,
|
||||
without host loopback sharing. X11 remains a display trust boundary: explicit ScreenCast portal
|
||||
permission does not prevent access through the X11 connection.
|
||||
|
||||
Initial display testing found that `/tmp` tmpfs shadowed the X11 socket and that NVIDIA OpenGL
|
||||
needed the four device mappings now in `desktop-base.nix`. Zoom therefore keeps the sandbox's
|
||||
private root `/tmp` instead of mounting another tmpfs over the socket, and explicitly passes
|
||||
`DISPLAY` and `XAUTHORITY`.
|
||||
|
||||
## Repeat without touching the normal Zoom profile
|
||||
|
||||
Build the candidate without activating a host, then substitute its output path below. The temporary
|
||||
home must be outside `/tmp` when testing policies that mount a `/tmp` tmpfs. Keep the current
|
||||
display and session bus environment.
|
||||
|
||||
```bash
|
||||
zoom_probe_dir=$(mktemp -d "$HOME/.cache/zoom-nixpak-probe.XXXXXX")
|
||||
mkdir -p "$zoom_probe_dir"/{.config,.cache,.local/share,.local/state,Documents,Downloads}
|
||||
HOME="$zoom_probe_dir" \
|
||||
XDG_CONFIG_HOME="$zoom_probe_dir/.config" \
|
||||
XDG_CACHE_HOME="$zoom_probe_dir/.cache" \
|
||||
XDG_DATA_HOME="$zoom_probe_dir/.local/share" \
|
||||
XDG_STATE_HOME="$zoom_probe_dir/.local/state" \
|
||||
timeout --kill-after=3 30 /nix/store/OUTPUT/bin/zoom
|
||||
```
|
||||
|
||||
While it runs, inspect the exact test process through `/proc/PID/root/dev`,
|
||||
`/proc/PID/root/.flatpak-info`, `/proc/PID/ns/net`, `/proc/PID/status`, and its parent processes. Do
|
||||
not sign in or join a meeting during this smoke test. The timeout terminates the owned launcher and
|
||||
its sandbox. Confirm the test windows and processes are gone afterward.
|
||||
|
||||
The disposable run created `zoomus.conf` under the private application config directory. Existing
|
||||
host `zoomus.conf` and `zoom.conf` are individually mounted when present. Application caches use the
|
||||
private application cache directory.
|
||||
|
||||
Still pending: login and meeting join, microphone and speaker routing, camera portal behavior on
|
||||
supported hardware, screencast selection and PipeWire FD handoff, recording/export paths, file
|
||||
chooser access, URL opening and IME. No successful camera, audio or screencast session is claimed by
|
||||
this smoke test.
|
||||
Reference in new issue
Block a user