feat(security): isolate bounded work and support required auth actions
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# Disposable resource-bounded operations
|
||||
|
||||
`security.bounded_process.run_bounded_operation` runs a trusted, importable,
|
||||
module-level `bytes -> bytes` function in a fresh interpreter. Core owns the
|
||||
process lifecycle, not the business parser. Owners retain authorization,
|
||||
sessions, provider reads, idempotency and persistence in the parent and pass
|
||||
only explicit bounded data. Never accept the operation, module or source path
|
||||
from a client. Use `security.worker_payload` for typed values; it does not use
|
||||
pickle, arbitrary constructors or JSON object hooks.
|
||||
|
||||
The runner requires POSIX process groups, `waitid(WNOWAIT)` and resource limits.
|
||||
Unsupported controls fail closed; there is no in-process fallback. The child
|
||||
uses `-I -B`, a fixed minimal environment, `/` as working directory, closed
|
||||
inherited descriptors and a new process session. Limits are installed before
|
||||
the owning module is imported. Installed dependencies must support isolated
|
||||
Python imports; development `PYTHONPATH` alone is insufficient.
|
||||
|
||||
`ProcessLimits` specifies wall-clock seconds (including child startup), CPU
|
||||
seconds, virtual address space, input/output pipe bytes and maximum regular-file
|
||||
size. Defaults are 10 seconds wall/CPU, 256 MiB address space, 8 MiB input and
|
||||
output, and no regular-file output. Wall/CPU limits are at most 600 seconds,
|
||||
memory 64 MiB–8 GiB, pipe limits 1 byte–256 MiB, and file size 0–2 GiB. Owners
|
||||
must document their tighter functional limits; a transport cap does not replace
|
||||
row, archive expansion, item-count or artifact limits.
|
||||
|
||||
Admission is non-queuing. `GOVOPLAN_ISOLATED_PROCESS_CONCURRENCY` defaults to 1
|
||||
(range 1–16) and applies across these operations **within each API/worker
|
||||
process**. Multiply capacity and memory budgets by the number of API/worker
|
||||
processes when sizing an installation. This is not a fleet-wide semaphore,
|
||||
cgroup quota, filesystem/network sandbox or permission to run arbitrary code.
|
||||
`RLIMIT_FSIZE` is per file, not a total disk quota. Owners creating staged files
|
||||
must enforce cumulative quotas and clean up their own private directories.
|
||||
|
||||
Owners preparing bounded local snapshots can enter
|
||||
`bounded_operation_admission()` before preparation and pass its token as
|
||||
`admission=` to the runner. This reuses shared capacity rather than reserving a
|
||||
second slot. Tokens belong to their active context, thread and process; expired,
|
||||
cross-thread and overlapping reuse fail. Preparation exceptions release the
|
||||
slot without launching a child. Never hold admission while waiting for a user;
|
||||
parent-side preparation still requires explicit I/O and byte bounds.
|
||||
|
||||
The parent concurrently drains stdout/stderr while writing input. Output is
|
||||
bounded during reading, stderr is discarded and capped at 64 KiB, and raw child
|
||||
tracebacks are never returned. Every success, exception, timeout, cancellation
|
||||
and callback failure kills the owned process group before reaping its leader,
|
||||
including descendants which close their inherited pipes. A module-level child
|
||||
handler must return bytes; it must not print logs/progress to stdout.
|
||||
|
||||
The optional `cancelled` callback runs in the parent at most roughly every
|
||||
50 ms while waiting. It must be fast and must not return an awaitable. It may
|
||||
also service a module-owned bounded progress protocol; exceptions terminate
|
||||
the child and propagate. There is no fabricated progress for killed work.
|
||||
`ProcessBudgetError.code` distinguishes busy, cancelled, timeout, CPU, memory,
|
||||
input/output limits, unavailable controls and worker failure. Owners map these
|
||||
to their existing structured diagnostics and recovery semantics.
|
||||
|
||||
The private typed-data codec supports null, booleans, strings, bytes, integers,
|
||||
floats, Decimal, UUID, date/time/datetime, lists, tuples and string-keyed maps.
|
||||
It rejects unsupported objects, malformed/trailing bytes, duplicate keys,
|
||||
excess depth (64) and node counts (1,000,000). Operation DTOs remain owner
|
||||
contracts and require owner validation. Do not persist this private wire format
|
||||
or use it as a public API.
|
||||
|
||||
Tests use real child processes for catastrophic regex, memory exhaustion,
|
||||
noisy output, exact limits, cancellation, closed-pipe hangs and descendant
|
||||
cleanup. These are local process regression tests, not production concurrent
|
||||
load certification. Operators still need target Linux/cgroup, cancellation,
|
||||
worker-count, memory and disk-quota evidence before raising concurrency.
|
||||
|
||||
## Deutsche Betriebszusammenfassung
|
||||
|
||||
Rechenintensive, vertrauenswürdige Moduloperationen laufen in einem frischen
|
||||
Prozess mit harten Laufzeit-, CPU-, Speicher- und Ausgabegrenzen. Berechtigungen,
|
||||
Sitzungen, Zugangsdaten und Datenbankänderungen bleiben im Hauptprozess. Fehlende
|
||||
Betriebssystemkontrollen führen zu einer Diagnose, nicht zu ungeschützter
|
||||
Ausführung. `GOVOPLAN_ISOLATED_PROCESS_CONCURRENCY` begrenzt die gemeinsame
|
||||
Zulassung je API-/Worker-Prozess, standardmäßig auf 1. Mehrere Prozesse haben
|
||||
jeweils eigene Grenzen; systemweite Speicher- und Festplattenquoten müssen
|
||||
Betreiber zusätzlich konfigurieren und auf der Zielinstallation prüfen. Die
|
||||
Schnittstelle ist keine Sandbox für beliebigen Code. Modul-Dokumentation nennt
|
||||
die jeweiligen fachlichen Grenzen, Fortschritts- und Wiederholungsregeln.
|
||||
Reference in New Issue
Block a user