D-Bus Proxy
Most containers either get the whole session bus or nothing. podbox sits in
the middle: a small proxy (xdg-dbus-proxy, run as a companion systemd unit)
forwards only the D-Bus services you allow. Notifications and link-opening
work through their portal interfaces; everything else stays unreachable
unless you add it under [dbus].
How it works
When [dbus] talk/own rules are configured, or notify/xdg_open need
portal access:
podbox enablewrites an additional file:~/.config/containers/systemd/<name>-proxy.service- The generated
.containerquadlet gains:Requires=<name>-proxy.service After=<name>-proxy.service - Instead of
Volume=%t/bus:%t/bus, the container gets the proxy socket:Volume=%t/podbox/<name>-dbus.sock:/run/podbox/dbus.sock:ro Environment=DBUS_SESSION_BUS_ADDRESS=unix:path=/run/podbox/dbus.sock - The proxy service runs
xdg-dbus-proxy, which forwards only the explicitly allowed D-Bus services to the container.
Configuration
1[dbus]2talk = [3 "org.freedesktop.Notifications",4 "org.mpris.MediaPlayer2.*",5]6own = [7 "org.mpris.MediaPlayer2.podbox_app",8]| Key | Type | Description |
|---|---|---|
talk | string[] | D-Bus services the container can call (two-way communication) |
own | string[] | D-Bus services the container can register on the host bus |
Wildcards (*) are supported per the xdg-dbus-proxy filtering rules.
Warning: adding
org.freedesktop.portal.*(or anyorg.freedesktop.portal.*/org.freedesktop.impl.portal.*name) totalkre-grants the full portal bus surface, including host-privileged interfaces likeDynamicLauncher,Screenshot,ScreenCastandSettings. Prefer the built-in interface-scoped rules described below;podboxprints a warning when it sees a portal-familytalkentry.
Portal access model
The portal preset (applied by default when [dbus] has no explicit rules)
does not add org.freedesktop.portal.* to the talk list. Instead, the
generated proxy exposes org.freedesktop.portal.Desktop through
interface-scoped rules, one per enabled capability:
| Capability | Rule granted |
|---|---|
integration.notify | --call=org.freedesktop.portal.Desktop=org.freedesktop.portal.Notification.*@/org/freedesktop/portal/desktop |
integration.xdg_open | --call=org.freedesktop.portal.Desktop=org.freedesktop.portal.OpenURI.*@/org/freedesktop/portal/desktop |
| either | --call=org.freedesktop.portal.Desktop=org.freedesktop.portal.Request.*@/org/freedesktop/portal/desktop/request/* (async Request pattern, incl. Request.Close) |
| either | --broadcast=org.freedesktop.portal.Desktop=org.freedesktop.portal.Request.*@/org/freedesktop/portal/desktop/request/* (Request.Response result signals) |
| either | --call=org.freedesktop.portal.Desktop=org.freedesktop.DBus.Introspectable.*@/org/freedesktop/portal/* (read-only introspection, needed by GIO clients to parse call arguments) |
Because xdg-dbus-proxy treats any granted method on a name as TALK for
that name, these rules expose exactly those portal interfaces — nothing
else. A disabled capability contributes no rules.
Behavior matrix
integration.dbus | [dbus] config | What the container gets |
|---|---|---|
false | any | No D-Bus access |
true | default (empty), no capabilities on | Unfiltered Volume=%t/bus:%t/bus |
true | notify/xdg_open on, or preset / talk / own set | Proxied via xdg-dbus-proxy with those rules plus interface-scoped portal rules for the enabled capabilities |
true | preset = "", empty talk + own | Unfiltered Volume=%t/bus:%t/bus |
Generated proxy unit
When rules are present, a companion systemd service is generated at
~/.config/containers/systemd/<name>-proxy.service:
1[Unit]2Description=D-Bus Proxy for podbox container <name>3PartOf=<name>.service4 5[Service]6Type=simple7RuntimeDirectory=podbox8ExecStart=/usr/bin/xdg-dbus-proxy \9 unix:path=%t/bus \10 %t/podbox/<name>-dbus.sock \11 --talk=org.freedesktop.Notifications \12 --talk=org.mpris.MediaPlayer2.* \13 --call=org.freedesktop.portal.Desktop=org.freedesktop.portal.Notification.*@/org/freedesktop/portal/desktop \14 --call=org.freedesktop.portal.Desktop=org.freedesktop.portal.OpenURI.*@/org/freedesktop/portal/desktop \15 --call=org.freedesktop.portal.Desktop=org.freedesktop.portal.Request.*@/org/freedesktop/portal/desktop/request/* \16 --call=org.freedesktop.portal.Desktop=org.freedesktop.DBus.Introspectable.*@/org/freedesktop/portal/* \17 --broadcast=org.freedesktop.portal.Desktop=org.freedesktop.portal.Request.*@/org/freedesktop/portal/desktop/request/* \18 --own=org.mpris.MediaPlayer2.podbox_app19Restart=on-failure20 21[Install]22WantedBy=<name>.serviceStopping the container stops the proxy; restarting restarts it
(PartOf=<name>.service).
Requirements
xdg-dbus-proxymust be installed on the host system (packagexdg-dbus-proxy, commonly shipped with Flatpak)integration.dbus = true(the master switch)- A D-Bus session bus socket must be present on the host (auto-detected)
Verification
Test an allowed service
gdbus call --session \ --dest org.freedesktop.Notifications \ --object-path /org/freedesktop/Notifications \ --method org.freedesktop.Notifications.Notify \ "podbox" 0 "" "Hello" "Proxied message." [] {} 5000Should succeed and show a host notification.
Test isolation
gdbus call --session \ --dest org.freedesktop.systemd1 \ --object-path /org/freedesktop/systemd1 \ --method org.freedesktop.DBus.Peer.PingShould fail with access denied — the proxy blocks unapproved services.
Portal surface audit
Audited from inside a container against the proxied socket. Allowed:
| Interface | Result |
|---|---|
org.freedesktop.portal.Notification.AddNotification | () (host notification shown) |
org.freedesktop.portal.OpenURI.OpenURI | request handle returned |
org.freedesktop.DBus.Introspectable.Introspect | introspection XML (needed by GIO clients) |
Denied interfaces (all return AccessDenied)
| Interface |
|---|
Screenshot.Screenshot |
ScreenCast.CreateSession |
RemoteDesktop.CreateSession |
InputCapture.CreateSession |
Settings.Read |
Documents.Add |
Account.GetUserInformation |
GameMode.QueryStatus |
Lockdown.GetDisabled |
Print.PreparePrint |
Wallpaper.SetWallpaperURI |
DynamicLauncher.RequestInstallToken |
FileChooser.OpenFile |
Denial happens at the proxy before anything reaches the host — screen capture, screenshots, file access, and launcher install stay unreachable unless the matching capability is enabled.