Harden pickle loading against RCE (RestrictedUnpickler) - #441
Merged
Merged
Conversation
Domain objects (Slices/Reservations/Units/Delegations/POAs) are stored as pickles in Postgres and pickled slivers ride Kafka, so plain pickle.loads means anyone able to write the store/bus can execute arbitrary code on load. Add fabric_cf/actor/security/restricted_unpickler.py with restricted_loads(), which blocks the standard pickle RCE/exfil gadget modules and callables (os/subprocess/sys/socket/ctypes/importlib, builtins.eval/exec/compile/open/ __import__, ...) in find_class, while allowing the FABRIC domain packages and the ordinary stdlib types (datetime/uuid/logging/enum/ipaddress/networkx/...) that legitimate pickled objects contain. Interim hardening only -- the on-disk/wire format is unchanged. Unlisted modules are audit-logged and allowed by default (so a too-narrow allowlist cannot break loading of real data); set FABRIC_PICKLE_ENFORCE=1 to switch to strict allowlist enforcement after the audit logs are confirmed clean. Route all 15 pickle.loads call sites (actor/server/substrate databases, container database, proxy) through restricted_loads. Adds infra-free unit tests covering legit round-trips and blocked os.system/eval/subprocess gadgets.
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.
Summary
Domain objects (Slices, Reservations, Units, Delegations, POAs) are stored as pickles in Postgres, and pickled slivers ride Kafka. Plain
pickle.loadson such data means anyone able to write the store or the bus can execute arbitrary code on load (pickle can import and call any global).This adds interim hardening — the on-disk / wire format is unchanged — that closes the practical remote-code-execution vectors.
Changes
fabric_cf/actor/security/restricted_unpickler.py—restricted_loads()+RestrictedUnpickler:find_class:os,subprocess,sys,socket,ssl,shutil,ctypes,importlib,pty,marshal,requests/urllib/http, … andbuiltins.eval/exec/compile/open/__import__/breakpoint/input.fabric_cf,fim,fabric_mb,fabrictestbed,fss_utils) and the ordinary stdlib types that legitimate pickled objects contain (datetime,uuid,logging,enum,collections,ipaddress,networkx, …) — the allowlist was derived empirically by pickling real domain/sliver objects.FABRIC_PICKLE_ENFORCE=1to switch to strict allowlist enforcement once the audit logs are confirmed clean.pickle.loadscall sites throughrestricted_loads(actor/server/substrate databases, container database, proxy).fabric_cf/actor/test/unit/test_restricted_unpickler.py— infra-free tests: legit round-trips (dict/datetime/uuid/Capacities/Labels) and blockedos.system/eval/subprocessgadgets.Verification
compileallpasses; the 5 wired modules import cleanly (no circular import — the helper depends only on stdlib).os.system(resolved asposix.system) andevalpayloads raiseUnpicklingErrorwhile legit objects round-trip.Rollout note
Because unlisted modules are allowed-and-logged by default, this is safe to deploy as-is. On
rel-2.0.1, grep actor logs forrestricted_unpickler: allowing unlisted pickle globalto discover any legitimate module missing from the allowlist, add them, and only then setFABRIC_PICKLE_ENFORCE=1for full allowlist enforcement.Merge notes (targets
rel-2.0.1)Touches
actor_database.py/server_actor_database.py/substrate_actor_database.py, which are also modified by the lock-idiom PR (#437) and (foractor_database.py) the DB-error PR (#440). Thepickle.loads -> restricted_loadsedits are on different lines, so conflicts should be trivial. Recommended order: #439 (CI) -> #437 (locks) -> #440 (db errors) -> this.Theme 3 (interim) of the improvement sweep. A full pickle-to-versioned-JSON migration remains a separate, larger project tracked in IMPROVEMENTS.md.