Skip to content

yichenshen/dbus-auth-proxy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

dbus-auth-proxy

dbus-auth-proxy is a proxy that overrides the user id in the AUTH EXTERNAL message of a dbus request. The proxy is intended to be used in containers running in rootless mode (e.g. podman rootless containers) to access the system dbus when the application's dbus client sets a UID in AUTH EXTERNAL.

It produces a unix socket that mimics the system dbus unix socket. This socket should then be mounted in the rootless container in question at /run/dbus/system_bus_socket so that dbus requests flow through the proxy and their AUTH EXTERNAL message will be fixed.

The Issue

If you ever run podman in rootless mode, you might notice having trouble with dbus on some clients. For example, as for writing, this will fail with an auth error:

podman run -it --rm -v /run/dbus:/run/dbus:rw --security-opt label=disable alpine
apk add dbus
dbus-send --system --print-reply --dest=org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.DBus.Introspectable.Introspect

A more practical example: if you run Home Assistant in a rootless podman, you'll most likely have trouble with bluetooth:

DBus authentication error; make sure the DBus socket is available and the user has the correct permissions: authentication failed: REJECTED: ['EXTERNAL']

This has been reported in a number of places:

Running

If you're not interested in the details, you can simply run this proxy in a container alongside the container you want dbus for.

cp dbus-auth-proxy.container ~/.config/containers/systemd/
systemctl --user daemon-reload
systemctl --user start dbus-auth-proxy

This will create a named volume called dbus-socket, and the container will register a system_bus_socket into this volume. You then simply mount this volume in place of /run/dbus/ on the container you want dbus for.

podman run -it --rm -v dbus-socket:/run/dbus:rw --security-opt label=disable alpine
apk add dbus
dbus-send --system --print-reply --dest=org.freedesktop.systemd1 /org/freedesktop/systemd1 org.freedesktop.DBus.Introspectable.Introspect

This should now work and list you the dbus endpoints. Doing the same for the Home Assistant container should also fix the bluetooth problem.

Details: Dbus Authentication

Note that this is not a dbus permission policy problem. It actually lies with how user ids are changed across container=>host and how that interacts with the AUTH EXTERNAL mechanism of dbus.

AUTH EXTERNAL

When connecting to dbus, there are a number of authentication mechanisms. For a local connection via unix sockets (to /run/dbus/system_bus_socket), it's usually simplest to use AUTH EXTERNAL. This means it relies on an external verifier, in this case the kernel, to verify which user is connecting. Here, a unix socket connection has a SO_PEERCRED option that tells you the user id of the connecting client. AUTH EXTERNAL basically uses that to detemine the user that's connecting and apply permission policies.

AUTH EXTERNAL has an extra optional mechanism. You can also supply a user id in the auth sequence itself. If you do so, then dbus will check this against then sockets user id, and only if they match, continue with the rest of the interaction. This isn't strictly neccessary, since the kernel already verified the user id, but you can specify it anyway for an additional check.

The above issue is usually when the supplied user id does not match the socket user id. Not supplying the user id in the dbus library is what the blog post above is trying to patch to fix the problem.

Container User ID

But why doesn't the user id match? This has to do with podman rootless containers. In a rootless container, the process inside the container runs as root by default (uid: 0). The container itself however runs as a non-root user, like uid: 1000. When socket connections are made from the container to the host, the uid is translated from the container namespace to the host namespace. This means that if you open a connection from a container to the dbus socket, inside the container you'll see the sockets uid to be 0. Outside the container it'll appear as 1000. When dbus does AUTH EXTERNAL, it'll thus see this socket as coming from your non-root user, uid: 1000, and will only confer it permissions for that user. This is expected and desired.

The trouble starts with clients in the container opting to use the additional user id check. In the container if the client decides to write it's own user id, it'll write uid: 0 into the AUTH EXTERNAL sequence. This is just data through the connection, and so when it crosses the container/host boundary it won't be transformed unlike the underlying socket metadata (podman won't know this is supposed to be a user id to begin with). Thus, when it arrives at dbus, it have receives AUTH EXTERNAL for uid: 0 but the socket's user id is 1000. It will reject the connection due to a mismatch.

The github issue above fixes this with --userns=keep-id. This runs the container process under uid: 1000, and thus avoids the problem because the client inside will now use 1000 for AUTH EXTERNAL. For some containers though, this creates another problem. Because the process in the container is now not running under root (uid: 0), it has less permissions inside the container. For Home Assistant for example, HACS will need root permissions for pip install, and running with --userns=keep-id will cause it to fail.

Proxying and Transforming

The pupose of the dbus-auth-proxy is to have an non-invasive way to get around this without having to patch the client or force it to run without root in the container.

It addresses the problem by intercepting the AUTH EXTERNAL message and fixing the user id. Essentially, dbus-auth-proxy creates it's own unix socket that mimics /run/dbus/system_bus_socket. When it receives an AUTH EXTERNAL message, it'll verify that the connection's user id is the same as it's own (meaning that dbus-auth-proxy is not granting the client any new credentials it doesn't already have). Then it fixes the AUTH EXTERNAL message's user id if supplied to it's own user id.

Then, what we do this to run dbus-auth-proxy in a container with --userns=keep-id. Afterwards, we mount the proxy socket it produces as the system dbus socket into the target container. This way, when the target container's dbus client connects with AUTH EXTERNAL, dbus-auth-proxy will get the message instead and fix the user id to it's own user id, which is the right one (because it runs with --userns=keep-id). Then it just forwards the rest of the data bi-directionally.

License

See the LICENSE file for license rights and limitations (MIT).

About

A proxy for dbus that overrides the user id of the AUTH message.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors