Unsock
shim library to automatically change AF_INET sockets to AF_UNIX, etc.
Install / Use
/learn @kohlschutter/UnsockREADME
unsock: shim to automatically change AF_INET sockets to AF_UNIX, etc.
unsock
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.
Mode of operation
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).
Building and running
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.
Security and permissions
Choosing a directory for UNSOCK_DIR
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.
Socket file permissions
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.
Disabling AF_INET6
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 (...)
Disabling unintercepted AF_INET
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 (...)
Usage Examples
nc
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.
java
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 ...
noVNC
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.
iperf
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.
Python HTTP server
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.
Control files, other socket domains like AF_VSOCK and AF_TIPC.
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 to bind on an AF_VSOCK address.
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.
Create a control file to connect to a VSOCK socket via a Firecracker-style Unix domain socket
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.
Create a control file to connect TIPC sockets
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 littl
