xdg-dbus-proxy builds a filtered D-Bus socket that gives a sandboxed client a deliberately narrow view of the real bus. The example permits access to the bus daemon, permits one service to be contacted, and permits portal calls only under portal object paths. The proxy listens on its own Unix socket, so the client can be pointed at that socket instead of the real bus.
Allow about 15 minutes. You need the xdg-dbus-proxy package, a shell, and a D-Bus address that the proxy can reach. This guide uses the installed package version 0.1.5-1ubuntu0.2, whose command reports 0.1.5. No root privileges are needed for a proxy and socket in a directory you own.
Security boundary: Filtering is only useful if the client cannot bypass the proxy and connect to the original bus. Treat the proxy socket and the real bus address as separate pieces of the sandbox design. Do not publish the real address to an untrusted client.
Confirm which executable will run and record its version:
$ command -v xdg-dbus-proxy
/usr/bin/xdg-dbus-proxy
$ xdg-dbus-proxy --version
xdg-dbus-proxy 0.1.5
The command takes one or more ADDRESS PATH pairs. ADDRESS is the bus address, such as unix:path=/run/user/1000/bus. PATH is the new Unix socket where clients will connect. Options belonging to a proxy must come after its pair.
Checkpoint: run xdg-dbus-proxy --help. You should see general options such as --fd and --args, followed by proxy options including --filter, --see, --talk, --own, --call and --broadcast.
Make a directory that the intended client can access, then choose a socket name inside it:
$ proxy_dir=$(mktemp -d)
$ proxy_socket="$proxy_dir/session-bus.sock"
$ printf '%s\n' "$proxy_socket"
/tmp/tmp.abc123/session-bus.sock
The random directory is useful for a short test because it is not an existing file and is normally accessible only to you. In a service or sandbox, use the directory layout and ownership rules that service already expects. The proxy creates the socket when it starts; the parent directory must already exist.
Do not put the socket in a shared directory without checking its permissions. A different process that can connect to the socket may be able to use the access your policy grants.
Set the real bus address in a shell variable, then start the proxy. The following policy is intentionally narrow:
$ bus_address='unix:path=/run/user/1000/bus'
$ xdg-dbus-proxy "$bus_address" "$proxy_socket" \
--filter \
--talk=org.freedesktop.DBus \
--talk=ca.desrt.dconf \
--call=org.freedesktop.portal.*=* \
--broadcast=org.freedesktop.portal.*=@/org/freedesktop/portal/*
Replace the address with the address for the bus you actually intend to proxy. The process stays in the foreground. Press Ctrl-C to stop it while testing.
--filter enables policy enforcement. With filtering enabled, the initial policy allows the client to talk to the bus itself and its own unique name, while other clients are hidden. The two --talk options add ordinary method-call and signal access for the named services. The wildcard suffix matches the name and deeper names such as org.freedesktop.portal.Documents, but not an unrelated prefix such as org.freedesktop.portals.
The portal rules then narrow the broad service permission: --call=...=* permits calls with any method, while only object paths below /org/freedesktop/portal/ are allowed for broadcast signals. A rule has the form METHOD optionally followed by @PATH. Use a fully qualified method, an interface, or * for the method part; append /* to a path when a subtree is intended.
Checkpoint: in another terminal, check for the socket:
$ test -S "$proxy_socket" && printf '%s\n' 'proxy socket is ready'
proxy socket is ready
Choose policy levels by the operation the client must perform:
--see=NAME makes a well-known name visible for discovery and ownership queries, without granting ordinary calls.--talk=NAME includes SEE and permits method calls and signals for the name. It also permits StartServiceByName.--own=NAME includes TALK and permits requesting, releasing and listing queued ownership of the name.Use the lowest level that meets the client requirement. Granting OWN merely because a client needs to inspect a service is a policy expansion, not a harmless convenience. A name may end in .* when a family of well-known names is required.
Remember that these policies affect the current owners of well-known names. The manual also describes unique-name policy as sticky: once a client has received a higher policy through a name it owned, releasing that name does not immediately lower the unique name's policy. Design around that behaviour instead of expecting a release to revoke access during a live proxy session.
The proxy does not apply every rule in both directions. Filtering applies to outgoing signals and method calls, and to incoming broadcast signals. Replies to an outstanding method call are allowed once, whether they are method returns or errors. Other unsolicited replies are not generally opened up by a call rule.
This distinction catches a common configuration mistake: adding a --broadcast rule will not authorise a client to call the service, and adding a --call rule will not subscribe it to every signal. Grant both directions only when the client needs both.
When a client receives a message from another peer, the peer's unique name becomes visible so the client can track its lifetime through NameOwnerChanged. A method call to, or a broadcast match from, a name also raises that name's effective basic policy to at least TALK for the client. Keep this behaviour in mind when reviewing a policy that relies on names remaining hidden.
Stop the foreground proxy with Ctrl-C. If it exits unexpectedly, the socket path may remain as a filesystem entry even though no proxy is listening. Check before starting a new instance:
$ test -S "$proxy_socket" && printf '%s\n' 'socket path exists'
socket path exists
$ ss -xl | grep -F -- "$proxy_socket" || true
If no process owns the socket, remove only the test socket and its temporary directory:
$ rm -f -- "$proxy_socket"
$ rmdir -- "$proxy_dir"
Check the variables before running those removal commands. They are recovery for the temporary example, not a general cleanup recipe. Never substitute a service directory or a path you have not inspected.
Supervisors can use --fd=FD to receive a readiness notification and to stop the proxy when that descriptor closes. The option is useful when another process must wait for the socket before launching a client. The proxy also accepts NUL-separated arguments through --args=FD; the option can be repeated for multiple argument sources. Keep these descriptors under the supervisor's control rather than exposing them to the sandboxed client.
For a simple foreground test, you do not need either option. Add them when your process manager can create and close file descriptors deliberately, and verify the manager's descriptor numbering before deploying the unit.
xdg-dbus-proxy --version reports the installed version you reviewed.--filter appears before the policy options for that address pair.