# D-Bus Permission Model mcdbus does not implement its own access control. By design, it delegates all security decisions to the operating system's existing D-Bus permission stack. This means the same mechanisms that protect D-Bus from any other client also protect it from mcdbus. This guide covers four independent layers that can be composed to build the level of isolation your deployment requires. They are all optional, all independent, and all stack on top of each other. ## Layer 1: systemd Service Sandboxing The single most effective thing you can do. Running mcdbus as a systemd service gives you process-level isolation using Linux namespaces, seccomp, and capability dropping -- all with a handful of declarative directives. An example unit file is provided at [docs/examples/mcdbus.service](examples/mcdbus.service). ### What it does The unit file uses `DynamicUser=yes`, which creates an ephemeral Unix user for each service invocation. No home directory, no persistent UID, no leftover state. Combined with filesystem and network restrictions, the mcdbus process gets a minimal sandbox: - **Filesystem:** `ProtectSystem=strict` makes `/usr`, `/boot`, and `/efi` read-only. `ProtectHome=yes` hides `/home`, `/root`, and `/run/user` entirely. `PrivateTmp=yes` gives mcdbus its own `/tmp` that is invisible to other processes. - **Network:** `RestrictAddressFamilies=AF_UNIX` limits socket creation to Unix domain sockets. D-Bus only needs `AF_UNIX`, so this blocks any TCP/UDP/raw socket activity. mcdbus cannot open network connections. - **Capabilities:** `CapabilityBoundingSet=` (empty) drops every Linux capability. mcdbus cannot change file ownership, load kernel modules, bind to privileged ports, or do anything else that requires elevated privileges. - **Syscalls:** `SystemCallFilter=@system-service` restricts the process to the set of syscalls that a typical well-behaved service needs. Things like `mount`, `reboot`, and `kexec_load` are blocked. - **Memory:** `MemoryDenyWriteExecute=yes` prevents mapping memory as both writable and executable. Stops most classes of code injection. ### Deploying it ```bash sudo cp docs/examples/mcdbus.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now mcdbus.service ``` ### Testing it systemd ships a built-in security auditor: ```bash systemd-analyze security mcdbus.service ``` This produces a score from 0.0 (fully exposed) to 10.0 (fully locked down). The example unit file should score above 7.0. The output also lists each directive and its impact, so you can see exactly what is and is not restricted. Check the journal for any sandbox-related denials: ```bash journalctl -u mcdbus.service -f ``` ### Environment variables The unit file sets `MCDBUS_TIMEOUT=30` by default. You can override this and add other mcdbus environment variables via a drop-in: ```bash sudo systemctl edit mcdbus.service ``` ```ini [Service] Environment=MCDBUS_TIMEOUT=60 Environment=MCDBUS_REQUIRE_ELICITATION=1 ``` ## Layer 2: D-Bus Bus Policy D-Bus itself has a message filtering layer that operates at the bus daemon (or bus broker) level. This is independent of what mcdbus does -- it controls which messages any client can send or receive based on the sender's Unix identity. An example policy file is provided at [docs/examples/mcdbus.conf](examples/mcdbus.conf). ### How it works D-Bus policy files are XML documents dropped into `/etc/dbus-1/system.d/` (for the system bus) or `/etc/dbus-1/session.d/` (for the session bus). Both `dbus-daemon` and `dbus-broker` read them. The policy engine evaluates rules top to bottom. The last matching rule wins. The general pattern is: 1. Default deny everything for the mcdbus user/group. 2. Selectively allow specific destinations, interfaces, and methods. ### Filter attributes Each `` or `` element can filter on: - `send_destination` -- the bus name being called (e.g. `org.freedesktop.Notifications`) - `send_interface` -- the interface being invoked - `send_member` -- the specific method name - `send_type` -- message type (`method_call`, `signal`, etc.) You can combine these. An `` with both `send_destination` and `send_interface` only permits calls to that specific interface on that specific service. ### Deploying it For the system bus: ```bash sudo cp docs/examples/mcdbus.conf /etc/dbus-1/system.d/ sudo systemctl reload dbus.service # dbus-daemon # or: sudo systemctl reload dbus-broker.service ``` For the session bus, copy to `/etc/dbus-1/session.d/` instead. Session bus policies apply to all users. If you only want to restrict a specific user, use `user="mcdbus"` in the policy context. ### Testing it Denied messages show up in the bus daemon's journal: ```bash # dbus-broker journalctl -u dbus-broker.service --since "5 min ago" | grep -i deny # dbus-daemon journalctl -u dbus.service --since "5 min ago" | grep -i deny ``` You can also use `dbus-monitor` to watch traffic in real time: ```bash dbus-monitor --system "destination='org.freedesktop.UPower'" ``` ## Layer 3: PolicyKit (polkit) polkit provides per-action authorization. Where D-Bus bus policy controls which messages can be *sent*, polkit controls whether a specific *action* is *authorized* for a specific user. Many system services (systemd, NetworkManager, UPower, udisks2) check polkit before performing privileged operations, regardless of what the D-Bus bus policy says. An example rules file is provided at [docs/examples/50-mcdbus.rules](examples/50-mcdbus.rules). ### How it works polkit actions are identified by reverse-domain strings like `org.freedesktop.systemd1.manage-units`. When a D-Bus service receives a method call that requires authorization, it asks polkit whether the calling user is allowed to perform that action. polkit rules are JavaScript files in `/etc/polkit-1/rules.d/`. They are evaluated in filename order (hence the `50-` prefix). Each rule function receives an `action` and a `subject` and returns one of: - `polkit.Result.YES` -- allow without prompting - `polkit.Result.AUTH_ADMIN` -- require admin password - `polkit.Result.NO` -- deny ### What the example does The provided rules file checks if the calling user is in the `mcdbus` Unix group. If so, it allows a curated set of read-only actions. Everything else falls through to the system defaults (which typically require admin authentication or deny outright). ### Creating the group ```bash sudo groupadd mcdbus sudo usermod -aG mcdbus $(whoami) # Log out and back in for the group membership to take effect ``` If you are running mcdbus under systemd with `DynamicUser=yes`, the dynamic user will not be in any supplementary groups by default. You can either: - Use `SupplementaryGroups=mcdbus` in the unit file, or - Use `User=mcdbus` with a real system user instead of `DynamicUser=yes` ### Deploying it ```bash sudo cp docs/examples/50-mcdbus.rules /etc/polkit-1/rules.d/ sudo systemctl restart polkit.service ``` ### Testing it List all registered polkit actions: ```bash pkaction ``` Check whether a specific action would be allowed for a user: ```bash pkcheck --action-id org.freedesktop.systemd1.manage-units \ --process $$ --allow-user-interaction ``` The `--allow-user-interaction` flag lets polkit prompt for a password if the rules say `AUTH_ADMIN`. Without it, `AUTH_ADMIN` results are treated as denial. ## Layer 4: xdg-dbus-proxy `xdg-dbus-proxy` is a Flatpak utility that creates a filtered D-Bus socket. You point it at the real bus socket, tell it which services to expose, and it creates a new socket that only passes through matching messages. The client (mcdbus) connects to the proxy socket and never sees the rest of the bus. An example wrapper script is provided at [docs/examples/mcdbus-proxy.sh](examples/mcdbus-proxy.sh). ### Policy levels xdg-dbus-proxy supports three levels of access per service: - `--see=NAME` -- the service appears in `ListNames` and can be introspected, but method calls are blocked. Read-only visibility. - `--talk=NAME` -- full bidirectional communication with the service. Method calls, property reads/writes, and signals all pass through. - `--own=NAME` -- the client can register (own) the given bus name. mcdbus does not need this. You can also filter at the interface and path level: ```bash --talk=org.freedesktop.Notifications --call=org.freedesktop.UPower=org.freedesktop.DBus.Properties.GetAll@/org/freedesktop/UPower/* ``` ### Installing it xdg-dbus-proxy ships with Flatpak: ```bash # Arch sudo pacman -S xdg-dbus-proxy # Debian/Ubuntu sudo apt install xdg-dbus-proxy # Fedora sudo dnf install xdg-dbus-proxy ``` ### How the wrapper works The script: 1. Creates a temporary directory for the proxy socket. 2. Starts `xdg-dbus-proxy` in the background, pointing at `$DBUS_SESSION_BUS_ADDRESS`. 3. Waits for the proxy socket to appear. 4. Sets `DBUS_SESSION_BUS_ADDRESS` to the proxy socket. 5. `exec`s mcdbus, so mcdbus runs with PID 1 semantics and gets signals correctly. 6. Cleans up the proxy socket and process on exit via a trap. ### Deploying it ```bash chmod +x docs/examples/mcdbus-proxy.sh ./docs/examples/mcdbus-proxy.sh ``` Or integrate it into the systemd unit file: ```ini [Service] ExecStart=/usr/local/bin/mcdbus-proxy.sh ``` ## Choosing Layers All four layers are independent. You can use any combination. **Layer 1 (systemd sandboxing)** is always recommended. It costs nothing to deploy and provides broad process-level isolation. Even if mcdbus or dbus-fast had a vulnerability, the sandbox limits what an attacker could do with it. **Layer 2 (D-Bus bus policy)** is useful when you want to restrict which services mcdbus can talk to at the bus level. This is the coarsest filter but also the most reliable -- it operates inside the bus daemon itself, so there is no way for the client to bypass it. **Layer 3 (polkit)** matters when the services mcdbus talks to perform their own polkit authorization checks. Most system services do this. If you want mcdbus to be able to read battery status but not manage systemd units, polkit rules are where you express that. **Layer 4 (xdg-dbus-proxy)** is the most granular option. It is useful for session bus filtering (where D-Bus bus policy is less commonly configured) and for deployments where you want per-interface or per-path control. The trade-off is that it adds a proxy process. For most deployments, Layer 1 alone is sufficient. For high-security environments or shared systems, stacking Layers 1 + 2 + 3 provides defense in depth. Layer 4 is there when you need fine-grained session bus filtering that the other layers cannot express. ### Quick reference | Layer | Scope | Granularity | Bus | |-------|-------|-------------|-----| | systemd | Process isolation | Filesystem, network, syscalls | Both | | D-Bus policy | Message filtering | Service, interface, method | Both | | polkit | Action authorization | Per-action, per-user/group | System (mostly) | | xdg-dbus-proxy | Socket filtering | Service, interface, path | Session (mostly) |