Skip to content

feat: secure-by-default RunOptions + cpus/pids_limit + k8s strings#2

Merged
oddur merged 3 commits intomainfrom
feat/secure-defaults
Apr 26, 2026
Merged

feat: secure-by-default RunOptions + cpus/pids_limit + k8s strings#2
oddur merged 3 commits intomainfrom
feat/secure-defaults

Conversation

@oddur
Copy link
Copy Markdown
Owner

@oddur oddur commented Apr 26, 2026

v0.5.0. See commit message for full breakdown.

TL;DR — yoink containers now ship hardened by default (cap_drop=ALL, no-new-privileges, read_only, pids_limit=1024). Operators opt out per knob. New cpus and pids_limit fields, k8s-style strings (500m, 2Gi, 512Mi). TUI container detail gains a 'security & limits' panel.

Migration: services that rely on root or writable rootfs need explicit opt-outs in their fragments before bumping. Audit via yoink up --dry-run — every service that needs to flip will show as an Update.

oddur added 3 commits April 26, 2026 22:58
Compose's defaults are dev-friendly, not prod-friendly: every
container runs with the full Linux cap set, a writable rootfs, no
pids cgroup limit, no protection against setuid escalation. yoink
now flips those.

New defaults applied to every service yoink creates:
  cap_drop:     ["ALL"]                    (was [])
  security_opt: ["no-new-privileges:true"] (was [])
  read_only:    true                       (was false)
  pids_limit:   1024                       (NEW field, was unlimited)

Operators opt out per knob:
  cap_drop: []                # restore full cap set
  security_opt: []            # allow setuid escalation
  read_only: false            # writable rootfs
  pids_limit: null            # unlimited

Two new fields:
- `cpus`: k8s-style absolute cores' worth of CPU time. Accepts
  bare numbers ("2", "1.5") + millicores ("500m", "1500m").
  None = uncapped. Translates to bollard's nano_cpus internally.
  NOT a percentage of host CPU — `cpus: "2"` on a 32-core box
  caps this container at 2 cores.
- `pids_limit`: i64 cgroup limit, default 1024. Bounds fork-bomb
  exploits without throwing off normal workloads (Rust async, Node,
  Redis, Caddy all stay well under 50). JVM with large thread pools
  may need to bump or disable.

Memory parser now also accepts k8s-style binary suffixes
(`Ki`/`Mi`/`Gi`/`Ti`) alongside the existing `k`/`m`/`g`.

The TUI's container detail (`i` from a row) gains a "security &
limits" panel showing memory/cpus/pids/user/cap_drop/cap_add/
security_opt/read_only at a glance, so operators can spot when
something's accidentally lax.

ContainerDetail (in docker_ops) gains user/memory_bytes/nano_cpus/
pids_limit/cap_drop/cap_add/security_opt/read_only fields populated
from `docker inspect`'s host_config.

spec_hash now includes cpus + pids_limit so any change re-deploys.
Existing containers will look "drifted" on the first reconcile after
upgrade — expected one-time forced redeploy.

Tests: parse_cpus accepts "2", "1.5", "500m"; rejects 0/-1/garbage.
parse_memory accepts "Mi"/"Gi"/"Ki" (case-insensitive). Default
RunOptions now produces a hardened HostConfig; explicit opt-outs
produce the lax shape.

Migration note: services that rely on root inside the container
(otel) or a writable rootfs (pgadmin) need explicit opt-outs in
their service fragments before bumping to v0.5.0. Audit with
`yoink up --dry-run` before merging — every service that needs to
flip will show as an Update with a new spec_hash.
Yoink streams raw container output; `hl` (https://github.com/pamburus/hl)
gives the human-friendly JSON/logfmt highlighting most operators want.
Also document the new `H` keybind from v0.4.0 (deploy history pane,
`r` on a stopped row to roll back).
The feature has been in the TUI logs pane since the OSS extraction —
`tui/app.rs:2461` spawns `hl` with the right flags + falls back to
raw output when `hl` isn't on PATH. Just calling it out in the README
so operators know it exists.
@oddur oddur force-pushed the feat/secure-defaults branch from 9c5f395 to f08f0ed Compare April 26, 2026 21:02
@oddur oddur merged commit 238685d into main Apr 26, 2026
6 checks passed
@oddur oddur deleted the feat/secure-defaults branch April 26, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant