I am sure that if you managed a Linux system for a while, you probably have dealt with Unix sockets—special files that act like sockets. You probably also run into permission issues when dealing with these socket files.
In this post, I’ll describe some methods of dealing with these permission issues, and a situation in which each might apply.
Method 1: Unix Groups
If your Unix socket only needs to be accessed by one client, then the easiest
approach is probably to leverage the group part of Unix permissions. If the
group of the Unix socket is set to a group that contains the user the client
is running under, and the group is granted
rw permission, then the client
will be able to connect to the Unix socket. (Note that this assumes the
client is able to access all parent directories.)
The most common situation that I use this method for is the connection
between a reverse proxy server, such as nginx, and a web application server,
uwsgi or a FastCGI server.
This is most easily done if the program supports creating sockets as
and configuring the permissions on the socket before dropping
spawn-fcgi can be run as
root and supports configuring the
-U), group (
-G), and mode (
-M) of the socket before switching
users. You can then set the group to
nginx (if your nginx runs with this
group) and mode to
660 and nginx will be able to access the socket.
uwsgi is similar, although the group of the socket has to be the same as
the group to switch to after dropping privileges. This may pose a security
issue if your application is complex, but most of the time you could simply
uwsgi --gid nginx and solve the problem.
Sometimes, the program itself doesn’t support this, and you may need to
use external mechanisms to launch it with the correct group. Certain
launchers may not support setting the group, and in such cases, the
command may prove helpful. Note that
sg will prompt for a password unless
the user it is running as is part of the group specified on the command line.
Method 2: Proxy Server
Sometimes, the service needs to be accessed by multiple clients and configuring the group ID may not be helpful. In such cases, it might be better to use another process to proxy the connection instead, running with the desired permissions.
To create socket
/tmp/target.sock with user
660 which forwards to
/tmp/source.sock can be done with this command:
socat UNIX-CONNECT:/tmp/source.sock UNIX-LISTEN:/tmp/target.sock,user=quantum,group=example,mode=660,fork
Since you can create multiple sockets that refer to the same resource, you can
create as many sockets as you need, all with different permissions, as long
as you run
socat under a user that can connect to the socket.
Note that the
fork option is important, or otherwise
socat will just
handle one connection before exiting.
Method 3: ACLs
socat doesn’t cut it. For example, you may be dealing with low
latency things like audio, or you are handling many clients. In such a case,
you want the exact same underlying socket to be available to multiple
different clients all with different users and groups.
In such a case, you can use access control lists, which allow you to specify
exactly what access each user and group has to a file. For example, to grant
quantum and group
example access to
/tmp/example.sock, you can
setfacl command as follows:
setfacl -m u:quantum:rw /tmp/example.sock setfacl -m g:example:rw /tmp/example.sock
The downside of this method is that you usually have to run a bunch of
commands after the process creates the socket, which usually requires some
sort of wait loop for the process to start or a race-prone call to
This also does nothing when you can’t change the path of the socket, and
the directory’s permissions do not allow access by other users. Note that
whether users will be able to access files in a directory at all is
determined by the executable bit (
x) on the directory. This is separate
from being able to get the list of files, which is determined by the read
Method 4: Directory Permissions and File Bind Mounts
And this brings us to the craziest scenario, which I ran into a while ago.
I was running a virtual machine on my Linux desktop, and I wanted to use
QEMU’s JACK audio support to output audio to PipeWire (with the JACK
drop-in support). I was using libvirt which ran the virtual machine under
libvirt-qemu user and thus was unable to access the PipeWire socket
For security reasons, I do not wish to run QEMU under my user, and I
absolutely cannot allow other users any access to
may them to attack my SSH agent.
Note that PipeWire creates the socket with mode
666 and all access
control is done by the
700 mode on
So ideally, I would like an alternative path to the PipeWire socket that QEMU could access.
An obvious thing to try would be symbolic links, but that would never work because symlinks are resolved by the user, which then needs access to the destination.
So instead, I came up with a crazy approach: use a mount point. Normally,
we associate mounting with directories, but we could in fact bind mount
a single file. So, if we create
access, bind mount
and set the environment variable
Then, PipeWire should be able to access the socket despite running under
a different user!
The obvious solution with
root access is thus:
# mkdir /example # chown libvirt-qemu: /example # chmod 700 /example # touch /example/pipewire-0 # mount --bind /run/user/1000/pipewire-0 /example/pipewire-0
However, I do not wish to run this as
root. So instead, I created an
/etc/fstab allowing me to perform this mount as myself:
/run/user/1000/pipewire-0 /example/pipewire-0 none bind,rw,user,noauto 0 0
The important options here are
user, which allows non-
root users to
perform the mount operation, and
noauto, which tells the system to not
mount this at boot. Then, as the user
quantum, I would be allowed to
/example/pipewire-0. Also note that the user
quantum would need
write access to
/example, which can be granted with the group approach
or with an ACL.
/run/user/1000/pipewire-0 is created, we simply have to run
/run/user/1000/pipewire-0 is created by
systemd, I could use
the following override unit to handle the mounting of
[Socket] ExecStartPost=/bin/mount /example/pipewire-0 ExecStopPre=/bin/umount /example/pipewire-0
And this works perfectly, without any overhead created by
pauses in audio resulting in popping noises), or creating any security holes
(other than exposing PipeWire to the virtual machine user).