Unix domain sockets (AF_UNIX
) are Berkeley (BSD-style) sockets that are accessible
as paths in the file system. Unlike AF_INET
sockets, they may be given user and group
ownership and access rights, which makes them an excellent choice to connect services
that run on the same host.
Unfortunately, not all programs support Unix domain sockets out of the box. This is where unsock comes in:
unsock is a shim library that intercepts Berkeley socket calls that
use AF_INET
sockets and automatically rewrites them such that they use AF_UNIX
sockets instead,
without having to modify the target program's source code.
Moreover, with the help of a custom control file in place of a real AF_UNIX
domain socket,
unsock allows communicating over all sorts of sockets, such as AF_VSOCK
and AF_TIPC
.
Using unsock not only makes systems more secure (by not having to expose internal communication
as AF_INET
sockets), it also helps improve performance by removing inter-protocol proxies from
the equation — programs can now talk directly to each other.
unsock specifically also simplifies communication between a virtual machine and its host, by
allowing communication to go through AF_VSOCK
sockets even if the programs were designed for
IPv4-communication only. As a bonus feature, unsock simplifies communication with
Firecracker-style
multiplexing sockets.
Being a shared library that is inserted into a process using LD_PRELOAD
, unsock intercepts
standard C library calls like connect(2)
, bind(2)
, accept(2)
, etc. The calls are analyzed
and, if necessary, modified transparently such that the calling process does not notice (or at least
only minimally) that an exchange took place.
Since socket file descriptors are first created on a per-protocol bassis using socket(2)
, should
an address family need to be changed, that socket file descriptor is replaced transparently using
a correct one. dup3(2)
is used to re-assign the file descriptor number on the fly, so no
additional housekeeping is necessary.
AF_INET
socket addresses are converted to a configurable path on the file system, under which
either AF_UNIX
sockets reside, or special control files with instructions how to reach the desired
socket destination (for details see below).
unsock's behavior can be modified using several environment parameters, which are outlined below.
The shared library binary doubles as a simple configuration tool to create the special control files (for details see below).
In order to build, you need a working C compiler (available under cc
), Linux headers, and for
tests an nc
command that supports UNIX sockets. If you're on Alpine Linux, just run
./init
To create the shared library libunsock.so
, on a Linux machine just run
make
To run some tests on the created library use
make test
To install the library on the system (by default to /usr/local/lib/
) use
sudo make install
To launch a target process with unsock, add libunsock.so to the environment variable
LD_PRELOAD, and set the environment variable UNSOCK_DIR to the absolute path of the directory
where unsock's AF_UNIX
sockets are stored, for example as follows:
UNSOCK_DIR=/tmp/unsockets/ LD_PRELOAD=/usr/local/lib/libunsock.so *some-process* *(some-args ...)*
This will ensure that all connections to 127.175.0.0
are intercepted and routed to unix domain
sockets in /tmp/unsockets
. The socket files are named (port).socket
, e.g., 1234.socket
for
port 1234.
Use UNSOCK_ADDR
to configure which IP addresses are redirected. You can either specify a single
IP-address (e.g., 1.2.3.4), or an IP-range identified by a bitmask (e.g., 1.2.3.4/24). Specifying
a bitmask of 32 is identical to omitting the bitmask. Specifying a bitmask of 0 means "all" IPv4
addresses, whereas the IP address itself is used to flag incoming connections from other protocols:
UNSOCK_ADDR=127.0.0.1/8 UNSOCK_DIR=/tmp/unsockets/ LD_PRELOAD=/usr/local/lib/libunsock.so some-process some-args ...
If UNSOCK_ADDR
is omitted, only connections/binds to 127.175.0.0/32
are intercepted and converted.
In the examples, for simplicity, we use /tmp/unsockets
for UNSOCK_DIR
.
Note that you should use a different directory in production, preferably one that has read/write permissions restricted to the user/group that uses the socket.
You also don't necessarily need one directory; you can have separate directories for different processes.
By default, unsock does not modify permissions for created socket files. However, you may specify
an octal value for UNSOCK_MODE
to run chmod whenver an unsock socket file is created, e.g.:
# Make available to all
UNSOCK_MODE=777 (...)
# Make available only to user
UNSOCK_MODE=700 (...)
Since you may not be able to change group ownership from any process, you can strategically move
UNSOCK_DIR
to a directory that has a certain group ownership and still get some security even
with UNSOCK_MODE=777
.
Some processes may try binding/connnecting via IPv6. unsock will not prevent that, unless you
specify the following environment variable setting, which will block any attempts to create
AF_INET6
sockets:
UNSOCK_BLOCK_INET6=1 (...)
You can also prevent unintercepted AF_INET
connections (those that are not translated via
unsock
), by specifying the following environment variable:
UNSOCK_BLOCK_INET=1 (...)
Make nc
listen on Unix domain socket /tmp/unsockets/7000.sock instead of using TCP port 7000:
UNSOCK_DIR=/tmp/unsockets/ LD_PRELOAD=/usr/local/lib/libunsock.so nc -l 127.175.0.0 7000
Listen on all IPv4 addresses; connections are coming from 127.175.0.3
:
UNSOCK_ADDR=127.175.0.3/0 UNSOCK_DIR=/tmp/unsockets/ LD_PRELOAD=/usr/local/lib/libunsock.so nc 127.0.0.1 7000
Listen on all IP addresses between 127.1.0.0 and 127.1.0.255; connection to 127.0.0.1 is via TCP:
UNSOCK_ADDR=127.1.0.3/24 UNSOCK_DIR=/tmp/unsockets/ LD_PRELOAD=/usr/local/lib/libunsock.so nc 127.0.0.1 7000
NOTE: busybox nc is a bit picky when accepting connections. You may need to specify
UNSOCK_BLOCK_INET6=1 UNSOCK_PORT=7000 UNSOCK_ADDR=127.0.0.1/0 UNSOCK_DIR=/tmp/unsockets/ \
LD_PRELOAD=/usr/local/lib/libunsock.so nc -l -p 7000
to fix the bound + incoming addresses and ports.
Make Java connect to UNIX sockets even without special support. Obviously, this is no replacement for proper libraries like junixsocket, but may be useful sometimes.
UNSOCK_ADDR=127.0.0.1/0 UNSOCK_DIR=/tmp/unsockets/ LD_PRELOAD=/usr/local/lib/libunsock.so java -jar ...
unsock + noVNC can be used to expose a VNC server to the Web via nginx, using Unix domain sockets for all internal ports.
see doc/novnc.md for details.
unsock allows to run iperf over arbitrary sockets (e.g., AF_UNIX
, AF_VSOCK
and AF_TIPC
), not
just IP.
see doc/iperf.md for details.
unsock allows to run the HTTP server over arbitrary sockets (e.g., AF_UNIX
, AF_VSOCK
and
AF_TIPC
), not just IP.
see doc/python-http.md for details.
unsock can also connect to other types of sockets. If the *.sock
file in UNSOCK_DIR
is not a
unix domain socket but a regular file with a magic header, the contents of the file control the
actual target of the connection. See struct unsock_socket_info
in unsock.h for
details of the file format.
Some control file configurations can be created by calling libunsock.so
as an executable, along
with some environment variables being set, including UNSOCK_FILE
pointing to the control file:
Create a control file under /tmp/unsockets/1234.sock
that points to AF_VSOCK
port 5678 with
CID "any" (-1
).
UNSOCK_FILE=/tmp/unsockets/1234.sock UNSOCK_VSOCK_PORT=5678 /usr/local/lib/libunsock.so
The command will fail if the file already exists.
The Firecracker hypervisor exposes a multiplexed Unix domain socket, over which one can connect to
VSOCK ports in the guest system. When using libunsock.so
with such a control file, the connection
is transparent, so no manual CONNECT port/OK
logic is necesssary.
Create a control file under /tmp/unsockets/1024.sock
that points to the AF_UNIX
socket at
/path/to/firecracker/vsock
which is a Firecracker multiplexing server. Connecting to
/tmp/unsockets/1024.sock
will actually try to connect to the guest's VSOCK port 5678.
UNSOCK_FILE=/tmp/unsockets/1024.sock UNSOCK_FC_SOCK=/path/to/firecracker/vsock \
UNSOCK_VSOCK_PORT=5678 /usr/local/lib/libunsock.so
The command will fail if the file already exists. You should specify an absolute path for
UNSOCK_FC_SOCK
. If it's a relative path, it must actually exist since it is resolved to an
absolute path for the control file.
TIPC knows several addressing types, but it comes down to specifying five values, address type and scope, and then three integer values depending on address type.
unsock does not discern these values, so the naming may be a little off, but it works.
To create a TIPC service address (addrtype=2; service range would be 1, and node id would be 3)
at cluster scope (scope=2; node scope would be 3), with service type 128 (values less than 64 are
reserved), instance ID of 99 ("lower" address) and domain of 0 (= global lookup; the "upper" address
of a service range) accessible via AF_INET
port 8000, run the following command:
UNSOCK_FILE=/tmp/unsockets/8000.sock UNSOCK_TIPC_ADDRTYPE=2 UNSOCK_TIPC_SCOPE=2 \
UNSOCK_TIPC_TYPE=128 UNSOCK_TIPC_LOWER=99 UNSOCK_TIPC_UPPER=0 /usr/local/lib/libunsock.so
To actually use TIPC, make sure the tipc
kernel module is loaded, and you have a bearer medium
set up, e.g.:
sudo modprobe tipc
sudo apk add iproute2-rdma
sudo tipc bearer enable media eth device eth0
Some programs expect AF_INET
socket addresses to be returned upon accept
. Set the following
environment variable to modify any non-AF_INET
address to look like one:
UNSOCK_ACCEPT_CONVERT_ALL=1 (...)
You can also selectively convert AF_VSOCK
only (UNSOCK_ACCEPT_CONVERT_VSOCK=1
).
To create a library for debugging (libunsock-debug.so
), which outputs some error messages, use
make DEBUG=1
and change LD_PRELOAD
accordingly.
To run some built-in tests that exercise the library, run
make test
or
make DEBUG=1 test
This library is supported on Linux only, and just lightly tested with some scenarios. However, it should already work for many real-world use cases. If it doesn't work for you, feel free to file a bug report, optionally with a pull request.
Only AF_INET
is intercepted; AF_INET6
is not intercepted. Binding on localhost
may attempt
binding on an IPv6 address and therefore may not give you the results you expect. You can block
AF_INET6
by specifying UNSOCK_BLOCK_INET6=1
.
Because unsock simply redirects libc calls, processes may technically work around the wrapper, for
example by using syscall(2)
or other means of invoking kernel methods directly or via a helper
process.
Socket files are not removed upon close(2)
(unsock tries to delete bound sockets upon
shutdown(2)
). However, when binding, stale socket files are automatically removed to prevent an
"address in use" error.
The absolute path specified with UNSOCK_DIR
must be of a certain maximum length (less than 96),
otherwise the process will terminate with an error message.
When the directory specified with UNSOCK_DIR
does not exist, it is created using a mode of 0755
,
unless umask dictates a stricter mode.
The abstract namespace for Unix domain sockets is not supported.
When using recvfrom(2)
, data sent from other AF_UNIX
sockets that are not under the control of
unsock, is treated as if it was received from 127.175.0.0
(or the address configured with
UNSOCK_ADDR
), port 0, which means that replying to that address is currently not possible.
AF_INET
-based sockets have several socket options thay may not be supported by AF_UNIX
.
While unsock already has several checks for common options, some are still missing. Use
the debug build to add some logging when debugging these cases.
In order to determine the socket file in UNSOCK_DIR
, the address part of the AF_INET
address
is currently not taken into consideration (only the port number is), which may lead to unexpected
results. Use a narrowly specified UNSOCK_ADDR
to compensate.
Currently, only little-endian architectures are tested/supported.
- Add support for non-
AF_UNIX
connections (via control files posing as unix domain socket files) - Add support for Firecracker-style
CONNECT
proxies forAF_VSOCK
communication. - Add very basic tooling to create the corresponding control files for
VSOCK
andTIPC
sockets - Allow unintercepted
AF_INET
/AF_INET6
traffic; by default, only127.175.0.0
is intercepted. - Add
UNSOCK_ADDR
environment variable to configure which IP address/address range is intercepted. - Add
UNSOCK_PORT
,UNSOCK_MODE
,UNSOCK_BLOCK_INET6
,UNSOCK_BLOCK_INET
. - Update build scripts, examples
- Initial release
It would be relatively simple to intercept custom address families that are not yet supported in the kernel. This could accelerate development of new protocols.
It would be relatively simple to add code that intercepts calls to certain IP address ranges and employ a third-party routing software for such connections. For example, a library like BoringTun could provide WireGuard-compatible connections for a specific process, without requiring additional configuration or kernel support.
Traffic could be logged, similar to what socket_wrapper
does (see below).
Samba has the Socket Wrapper, which
serves a similar purpose. It is limited to AF_UNIX
sockets and does not use dup3
to exchange
file descriptors, therefore it needs to intercept many unrelated function calls for housekeeping.
ip2unix converts IPv4 and IPv6 sockets to AF_UNIX, on a per-rule basis. An internal mapping (instead of using dup3) is maintained. Also has some systemd integration for IP-based socket activation.
Containers libkrun has kernel patches that may
transparently turn AF_INET
sockets into AF_VSOCK
.
See patches AF_TSI and tsi_hijack.
A Java/JNI library that allows the use of Unix domain sockets (AF_UNIX
) and others like AF_TIPC
and AF_VSOCK
, from Java and other JVM languages. Works with GraalVM, too.
junixsocket on GitHub; junixsocket project website
Copyright 2022 Christian Kohlschuetter [email protected]
SPDX-License-Identifier: Apache-2.0
See NOTICE and LICENSE for license details.