ZFS dataset management for unprivileged users via a Unix domain socket.
A host-side agent runs with ZFS privileges and executes validated zfs create requests on behalf of clients that lack ZFS tools or privileges – typically containers. Used in ftm-lakehouse.
Linux only (peer authentication relies on SO_PEERCRED), Python 3.10+, no
dependencies.
pip install zfs-agent
Run the agent on the host (as a user that may run zfs create, typically root):
zfs-agent --socket /run/zfs.sock --pool tank/data --owner 1000:1000 --allowed-uid 1000
--poolrestricts requests to datasets below this path (env:ZFS_POOL)--ownerchowns new dataset mountpoints to thisuid:gid(env:ZFS_OWNER)--allowed-uidonly accepts connections from this UID, verified viaSO_PEERCRED; defaults to the agent's own UID (env:ZFS_ALLOWED_UID)--log-levelsets the agent's log verbosity, defaultINFO(env:ZFS_LOG_LEVEL). Only the CLI configures logging; imported as a library, the package logs throughloggingwithout attaching handlers.
Clients may only set ZFS properties from a built-in allowlist of tuning
knobs (compression, recordsize, atime, quota, …). Set
ZFS_EXTRA_PROPS on the agent to add more, comma separated:
ZFS_EXTRA_PROPS=canmount,readonly zfs-agent --socket /run/zfs.sock --pool tank/data
The effective allowlist is logged at startup.
Create datasets from the client side (e.g. inside a container that mounts the socket):
from zfs_agent.client import zfs_create_socket
zfs_create_socket("/run/zfs.sock", "tank/data/my_dataset", compression="zstd")Or set ZFS_SOCKET=/run/zfs.sock in the environment and let the dispatcher choose between socket and local zfs create:
from zfs_agent import zfs_create
zfs_create("tank/data/my_dataset", compression="zstd")- The socket is created mode
0600, owned by the allowed UID. - The peer's UID is verified via
SO_PEERCREDbefore the request is read. - Dataset names are validated (no path traversal, restricted characters) and must live under the configured pool.
- ZFS properties are checked against an allowlist. Properties such as
mountpoint,sharenfsorsetuidwould otherwise let a client steer what the privileged side touches. - Requests are size capped and time limited, and a malformed one is answered with an error rather than taking the agent down.
- The only supported action is
create.
make test # unit tests
make test-docker # integration test (privileged container, ZFS on the host)
AGPLv3+