Skip to content

sluice v0.10.0

Latest

Choose a tag to compare

@github-actions github-actions released this 17 Jul 12:20
bbb6567

Hardening, supply-chain, and fleet-operations release: fleet-wide egress auditing, a preventive
egress byte cap, pinned/verified replay, and a broad sweep of fail-closed and portability fixes.
All local, self-hosted, no telemetry.

Added

  • Fleet-wide egress auditsluice egress --verify --all / --export --all walk every box's append-only audit log and hash chain with the engine down (no daemon needed). (#67)
  • SLUICE_EGRESS_HARD_CAP_BYTES=N — a preventive in-box egress cap (via xt_quota) that kills an over-cap transfer mid-flight; fails closed when the kernel module is absent. (#67)
  • Per-host egress budget (SLUICE_EGRESS_MAX_BYTES) metered on what left the box; the direct-IP SLUICE_ALLOW_IPS lane is now metered before the ESTABLISHED accept. (#67)
  • sluice lock --pin writes a committable sluice.pin replay manifest (base pinned by digest + exact versions); SLUICE_PIN=1 performs a verified pinned replay build. (#67)
  • Versioned JSON contracts (sluice.version / doctor / box v1); ls --json embeds last_receipt; in-box DNS-tunnel audit and bump upload controls. (#67)
  • sluice egress gains a summary header ("N reached, N blocked, <bytes>"), an audit-log footer nudging --export/--verify, and egress --verify --json for CI integrity gating; egress --json gains "window":"boot". (#43)
  • Denied raw-IP (direct CONNECT/URL) requests are now ledgered in the receipt and egress --json, alongside an always-on fw_dropped total. (#72)
  • New managed-policy directives: max-allow-ips-bytes, max-hard-cap-bytes, strict-unknown, require-signed-base. (#76, #81)
  • sluice doctor readout: trailing verdict line, always-on hardening row, git-tracked-mask warning, report-only managed-policy preflight (doctor --json adds policy.effective_allowlist + policy.refusals), SLUICE_MOUNTS validation, and rootless-podman host-limit checks. (#48, #46, #45, #72, #71)
  • NOTICE file + README Acknowledgments crediting the upstream base image, its packages, and the test framework. (#72)
  • Four capability demo GIFs across the README and docs: a malicious npm dependency contained at import, an exfil killed mid-flight by the hard cap, a fleet audit with the daemon down, and an org policy refusing a metadata-SSRF config.

Changed

  • The credential prelaunch hook now runs before every session (run / shell / rebuild, warm boxes included) so short-lived tokens re-mint each run instead of expiring with the container. (#72)
  • sluice ls ALLOW count reads the live allowlist instead of the stale build-time label. (#72)
  • A config-less first run now stops after scaffolding instead of building + running the detected command under SLUICE_YES=1. (#72)
  • sluice diff is now gated by the managed policy and config validators like every other box bring-up path. (#76)
  • sluice learn drops a laundering host under forbid-laundering / SLUICE_STRICT_LAUNDERING instead of warn-and-keeping it onto the hot-reloaded allowlist. (#76)
  • SLUICE_ALLOW_IPS rejects a hostname entry with a clear message (hostnames belong in SLUICE_ALLOW_DOMAINS) instead of silently sinking it to the DNS dead-end. (#79)
  • sluice lock --scan exit code is a documented contract (0 clean / 3 gate-tripped / 4 scanner-failed), normalized across grype and trivy; --check/--diff/--enforce reject unknown flags. (#44)
  • The SBOM (lock --sbom / --pin) enumerates the full global-npm dependency closure (was top-level only), so a CVE in a transitive npm dep now reaches --scan. (#77)
  • A signed base image is resolved to a content digest once; cosign verify and the Dockerfile FROM pin the same digest (closes a same-tag-republish gap on the :latest signed-base path). (#77)
  • -b <box> doctor resolves the engine leniently, reporting instead of dying on a bad SLUICE_RUNTIME. (#55)
  • Agent presets: gemini drops the inert GOOGLE_API_KEY, qwen forwards DASHSCOPE_API_KEY, and stale/dead allowlist hosts were pruned from claude/amp. (#55, #65, #49)

Fixed

  • Portability (build-breaking): hashing routes through _sha256 (prefers sha256sum, falls back to shasum -a 256), unbricking every build/run on Alpine, *-slim, and most CI images where shasum is absent. (#78)
  • Data-loss: sluice apply/diff exclude masked paths from the overlay write-back, so a masked .env no longer truncates the real host secret to 0 bytes. (#75)
  • Denied raw-IP requests are matched with an interval-free regex, fixing silent under-reporting on mawk builds without --re-interval (e.g. Debian). (#73)
  • Rootless podman keeps host repo ownership via --userns=keep-id; podman's localhost/ image-name prefix is stripped so ls/ps/prune/receipt slugs resolve. (#69, #71)
  • Fail-closed audit reads: ls --egress, learn, and doctor report unknown (? / null) instead of a false all-clear when the in-box audit log can't be read. (#61, #59)
  • set -f around unquoted config word-splits in the launcher and the in-container entrypoint, so a glob char in a SLUICE_* value can't pathname-expand; wildcard allowlist entries stay literal during policy evaluation. (#54, #52, #45)
  • sluice learn no longer aborts when the run-offset file is missing — it falls back to the full log. (#53)
  • SLUICE_SECCOMP=audit writes its generated profile to the stable per-box state dir instead of leaking one /tmp file per run. (#54)
  • The overlay-workspace seed writes its apply-safety manifest only on a fully successful copy, so a partial seed can't delete untouched host files on apply. (#54)
  • egress --verify handles an unterminated final record and skips blank lines (full hash-chain coverage) and rejects typo'd flags. (#42, #55)
  • install.sh re-points origin to SLUICE_REPO on a re-run and quotes a spaced $HOME fpath dir. (#55)
  • lock: LC_ALL=C sorts kill phantom cross-locale drift; same-version rebuilds and multi-version packages classify correctly; --sbom/--json stdout is byte-clean on a cold build. (#44, #42)

Security

  • Closed egress allowlist fail-opens: the leading-dot wildcard form (.storage.googleapis.com) that sluice learn writes now matches forbid-laundering / strict-laundering, and SLUICE_ALLOW_IPS refuses masks broader than /8 (was only /0). (#51)
  • sluice init single-quotes every repo-derived value written into the generated config, closing an RCE via a hostile Procfile / make-target / package.json script. (#50)
  • Closed five managed-policy trust-boundary bypasses: deny-ip refuses an SLUICE_ALLOW_IPS entry that overlaps the denied CIDR in either direction (supernet included, e.g. a /24 over a /32 metadata deny); a malformed ceiling arg is a hard refusal, not a fail-open no-op. (#76)
  • sluice learn fails closed on an unreachable managed policy, aligning it with the run path.

Internal

CI / supply-chain hardening (advisory actionlint / zizmor / gitleaks lanes, checksum-pinned grype + container-structure-test installs, SHA-pinned action bumps), a lane-membership drop-guard and a preflight ship-readiness workflow, added regression coverage, docs-drift fixes, and a consolidated Terraform Linux test-runner VM (sluice-vm.sh, Kata opt-in). (#57, #60, #62, #63, #64, #65, #66, #68, #70, #74, #80, #81)


Install

brew install Pyronewbic/tap/sluice        # or: brew upgrade sluice
# or:  curl -fsSL https://raw.githubusercontent.com/Pyronewbic/Sluice/main/install.sh | sh

Full changelog: v0.9.0...v0.10.0