It's pretty useful in test suites where you don't want to open a public TCP port (even on localhost), for obvious security reasons but also because it's hard to pick an unused port without conflicting with other tests running at the same time. Example test using this: https://gitlab.com/nbdkit/nbdkit/-/blob/master/tests/test-cu...
you don't want to open a public TCP port (even on localhost), for obvious security reasons
Can someone explain this a bit more? It's my understanding that all localhost traffic is confined to the host, and so only available to the host itself. If something malicious could access traffic on localhost, wouldn't that mean you've already lost?
A control socket for a local daemon should only be accessible by authorised users or root. TCP sockets don’t help you with that, but Unix domain sockets are subject to regular file system access control.
On a multi-user machine, as a regular user, serving over TCP immediately allows any other logged-on users to access it, which can be unacceptable in some circumstances.
It's possible to bind a server so that the OS chooses which port it listens on for you, and if you set up your test infrastructure to communicate that port to the things that need to connect to said server, you can happily run several tests in parallel. This is how Firefox's networking tests run in parallel, despite many of them requiring spawning an HTTP server to connect to.
What you say is only true if your firewall is configured to do so. By default (without any firewall), all traffic is exposed to every interface I believe.
This is not correct. Binding a socket to localhost (UDP, or TCP listener, or SCTP listener, ...) will prevent traffic from other network interfaces using it, even on the same machine.
virtus ~ # nft list ruleset
virtus ~ # nc -vvv -l -s 127.0.0.1 -p 1234
Listening on localhost 1234
virtus ~ # ip -4 addr show scope global dev enp3s0
2: enp3s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
inet 10.20.1.75/24 brd 10.20.1.255 scope global enp3s0
valid_lft forever preferred_lft forever
virtus ~ # nc -vvv 10.20.1.75 1234
nc: connect to 10.20.1.75 port 1234 (tcp) failed: Connection refused
The security consideration with binding to localhost is that every process in the same network namespace can connect to that socket, regardless of what user the process is running as, unless you use netfilter to isolate what users can connect to where. Even then any process running as the given user will be permitted. A UNIX domain socket bound to a filesystem path can be constrained by filesystem ACLs and chroots, without having to touch netfilter at all.
It's more of a coordination problem. Even if the web server could be persuaded to use port 0 (most will barf at this), I now have to find out what port was assigned by the kernel and communicate that to the test client. Unix domain sockets just make this whole problem go away.
Also running out of ports is a thing! There are only 64K ports (with many not being usable) and a CI machine might be running many tests in parallel. Unix domain sockets give me an infinite space of ports, since they use filenames.
It isn't infinite space. The buffer for filenames is only 256 bytes, so 255 including the NUL terminator. But still, that is a ton more socket names than ports.
You can have arbitrarily complex names because filesystems are a tree, not a flat namespace. The limit you'll hit is either inodes or kernel memory, neither of which will run out all that easily.
Comments
curl can do this: https://curl.se/libcurl/c/CURLOPT_UNIX_SOCKET_PATH.html
It's pretty useful in test suites where you don't want to open a public TCP port (even on localhost), for obvious security reasons but also because it's hard to pick an unused port without conflicting with other tests running at the same time. Example test using this: https://gitlab.com/nbdkit/nbdkit/-/blob/master/tests/test-cu...
Can someone explain this a bit more? It's my understanding that all localhost traffic is confined to the host, and so only available to the host itself. If something malicious could access traffic on localhost, wouldn't that mean you've already lost?
A control socket for a local daemon should only be accessible by authorised users or root. TCP sockets don’t help you with that, but Unix domain sockets are subject to regular file system access control.
On a multi-user machine, as a regular user, serving over TCP immediately allows any other logged-on users to access it, which can be unacceptable in some circumstances.
It's true, but on a CI machine you may have other tests running in parallel. Even perhaps tests being run by other users/tenants.
Unix domain sockets can be confined to a randomly generated directory under /tmp and locked down with file permissions.
It's possible to bind a server so that the OS chooses which port it listens on for you, and if you set up your test infrastructure to communicate that port to the things that need to connect to said server, you can happily run several tests in parallel. This is how Firefox's networking tests run in parallel, despite many of them requiring spawning an HTTP server to connect to.
What you say is only true if your firewall is configured to do so. By default (without any firewall), all traffic is exposed to every interface I believe.
EDIT: I stand corrected
This is not correct. Binding a socket to localhost (UDP, or TCP listener, or SCTP listener, ...) will prevent traffic from other network interfaces using it, even on the same machine.
The security consideration with binding to localhost is that every process in the same network namespace can connect to that socket, regardless of what user the process is running as, unless you use netfilter to isolate what users can connect to where. Even then any process running as the given user will be permitted. A UNIX domain socket bound to a filesystem path can be constrained by filesystem ACLs and chroots, without having to touch netfilter at all.If you use 0 for the port, the Linux kernel will assign you a random port.
It's more of a coordination problem. Even if the web server could be persuaded to use port 0 (most will barf at this), I now have to find out what port was assigned by the kernel and communicate that to the test client. Unix domain sockets just make this whole problem go away.
Also running out of ports is a thing! There are only 64K ports (with many not being usable) and a CI machine might be running many tests in parallel. Unix domain sockets give me an infinite space of ports, since they use filenames.
It isn't infinite space. The buffer for filenames is only 256 bytes, so 255 including the NUL terminator. But still, that is a ton more socket names than ports.
You can have arbitrarily complex names because filesystems are a tree, not a flat namespace. The limit you'll hit is either inodes or kernel memory, neither of which will run out all that easily.
That's not just Linux, that's a more global IP sockets thing.