What’s Going On Here?¶
This is an attempt to produce a computing environment which is connected to the Internet but has no idea how that magic is happening. (Or, well, as little of an idea as possible.) In particular, we aim to connect a Linux userland over Tor without the machine even having an IP address, and without any (easy?) way to discover the addresses of its physical neighbors.
Concretely, this project aims to mitigate end-node vulnerabilities (e.g. sandbox escapes, information disclosures) by ensuring that as little information as possible is available to arbitrary code running on the end-node. It is a variant of the isolating proxies design documented by Tor; it seeks to further minimize the attack surface available to a compromised machine.
We’re going to use two computers to do this. One we’ll allow access to the Internet via traditional networking; this gateway node will run Tor. The other, client machine will also run Tor but will reach the Tor network (and the Internet beyond) over Tor, as provided by the gateway, over a non-traditional link: a serial link carrying a Tor “orconn”. Because there seems to be some confusion, perhaps some clarification is in order. This setup differs substantially from SLiRP and other IP-in-X encapsulation techniques; there is no symmetric notion of identifier for the client and gateway nodes, though of course the client node has to use some identifier when it opens streams, and for that it will continue to use DNS and IP addresses. (As the Tor network also has a notion of hidden services, the client node can, at its discretion, establish one or more cryptographic identities for itself and allow “inbound” connections over its outbound streams.)
Why Network Without A Network Address?¶
Traditionally, one reaches the Internet by symmetry, by being assigned an IP address. [1] That seems natural enough, given the goal, but it is not necessarily what one wants. IP addresses are (more often than not) more than sufficient to narrow down one’s location.
In addition to its usual mode of being a SOCKS proxy, Tor already has some support for such whole-network asymmetry with transparent proxies as well as the isolating proxies extension mentioned earlier. However, even these require that one runs a network stack.
Network stacks are huge. They are vast bodies of bizarre historical relics,
such as Linux’s tune-ables /proc/sys/net/ipv4/conf/*/arp_filter
and
.../arp_announce
; by default Linux will cheerfully respond to ARP requests
for any address assigned to any interface. Meaning: if you run your Tor
transparent gateway with on your network’s border gateway node, the supposedly
anonymous, funneled-into-Tor network either can probe for or get told your
external IP address. Not ideal. [2]
By contrast, the only vulnerabilities that might be faced here are those within the Tor daemon itself, a much smaller and hopefully more audit-able chunk of code.
Tor Issues Making This Nontrivial¶
Unfortunately, we’re going to have to work around some apparently intentional information disclosure in Tor and deal with the fact that our use case remains niche even within the niche of Tor.
Until https://gitlab.torproject.org/tpo/core/tor/-/issues/9498 is resolved, even private (i.e., not published in BridgeDB) Tor bridges will reveal their public address(es?) to their clients. This is probably not seen as an issue given the usual way that bridges are meant to be used.
Until https://gitweb.torproject.org/torspec.git/tree/proposals/188-bridge-guards.txt is implemented, any bridge client can direct that bridge to attempt to connect to any Tor relay (or possibly any IP address?) as a nominal next-hop. Therefore, a conspiring client and relay operator can trivially reveal the IP address of the bridge the client is using. This is again probably not considered a threat to client users because usually the client is not also running a bridge; I am sort of surprised it’s not fixed anyway, despite the bridge enumeration risk. See also https://gitlab.torproject.org/legacy/trac/-/issues/7144 and https://gitlab.torproject.org/tpo/core/tor/-/issues/9500 and https://gitlab.torproject.org/tpo/core/tor/-/issues/40093 .
And, for I assume good reasons, Tor tries to stop you from exiting back into the Tor network. So we’re going to have to subvert that.
Therefore, what we’re going to do is manually construct a rather ludicrous path:
Our gateway will run Tor as an ordinary onion proxy rather than bridge. (For this demo, the gateway is not, itself, configured to use bridges; it could be, but that is an orthogonal concern.)
We will have the gateway connect a serial port to a public Tor bridge via Tor, using
socat
. That is, the bridge will see us connecting from a Tor exit node that we will reach by the usual 3-hop circuit (from the gateway).The client node will also run Tor, set to use
localhost
as its bridge, using the key information of the public bridge.We will have
socat
on the client respond to those loop-back connections and bridge to the serial port.
So, until Tor resolves the above issues, this is going to result in a six-hop network path when we reach out (ideally it would just be four), nine hops when the client reaches (or is reached as) a hidden service, or twelve if two such clients connect (yikes).
Demo How-To¶
The ingredients for this demo are
a Raspberry Pi 1 (the lack of WiFi and BT radios are important, so that ACE cannot be used to scan the wireless neighborhood)
an old laptop running Tor
two FTDI USB UART chips, wired in crossover/null-modem configuration
The
socat
commandA public Tor bridge. For this demo, I picked one from https://gitweb.torproject.org/tor-browser.git/tree/tor-config/tor_obfs4.conf since we’re not using the bridge to hide our identity and we don’t depend on local network reachability.
Tor itself, of course
Serial Link¶
You’ll want a UART interface that can achieve relatively high baud rates and understands flow control. This is a taller order than might be imagined! I’ve had good luck with FTDI FT232RL chips and bad luck with the cheaper Silab CP210x [3]. YMMV.
Just as a reminder, a minimal crossover cable with “both levels” of handshaking pins connects ground to ground and crosses within each of the TXD/RXD, RTS/CTS [4], and DTR/DCD pair. RI and DSR can be left disconnected.
Gateway¶
On the gateway, we want to run something like the following in a loop:
socat /dev/ttyUSB0,b3000000,flock-ex,cfmakeraw,crtscts,hupcl=1 SOCKS4:localhost:${BRIDGE},socksport=9050
In detail, that bridges /dev/ttyUSB0
and the Tor bridge, over the gateway’s
Tor proxy (using its SOCKS interface). We set the serial link to use 3 Mbaud
(as fast as I have found reliable, YMMV), lock the port exclusively, disable
interpretation of the raw bytes, use RTS/CTS flow control, and have the port
signal us to quit when DCD goes low. Ideally socat
would close and re-open
the connection, rather than exit, but AFAICT it doesn’t understand fork
-ing
in this use case; looping the command is sufficient workaround.
Client¶
And on the client, we will run the reverse:
socat TCP-LISTEN:9001,reuseaddr,fork /dev/ttyUSB0,b3000000,flock-ex,cfmakeraw,crtscts,hupcl=1
Here we can use socat
’s fork
functionality and don’t necessarily need
this in a loop, but it can’t hurt to do that, too.
And for Tor, we have to tell it to use our bridge, so /etc/tor/torrc
contains (with variables expanded):
UseBridges 1
Bridge obfs4 127.0.0.1:9001 ${BRIDGE_KEY} ${BRIDGE_EXTRA}
Other Tor options (ControlPort
, HiddenServiceDir
, &c) are of course
quite useful.
Success¶
Now, to check that everything’s working, try running something like this on the client:
torify lynx --source google.com
It’s possible to configure apt
to use the Tor proxy by default, too, so the
client can even keep itself up to date easily enough. In
/etc/apt/apt.conf.d/90proxy
, write:
Acquire::Queue-Mode "access";
Acquire::http::proxy "socks5h://localhost:9050";
The Queue-Mode
cuts down on the number of concurrent connections and,
experimentally, improves reliability. I presume this is some side-effect of
the very high bandwidth-delay product of our link and Tor’s stream
multiplexing.
We can ask the kernel to verify that all of this happens without a route-able IP address:
pi@raspberrypi:~ $ ip -o a s
1: lo inet 127.0.0.1/8 scope host lo\ valid_lft forever preferred_lft forever
1: lo inet6 ::1/128 scope host \ valid_lft forever preferred_lft forever
pi@raspberrypi:~ $
Paranoid Device Construction¶
Thus far I’ve described the procedure for getting things working assuming that both ends are already perfectly functional Linux machines. What if we don’t have a client yet? We don’t really want to connect it to our network, replete with possibly traceable information flying about, as that might get written down somewhere.
The simplest approach is to grab a distribution that already has tor
and
socat
its installation media, but, failing that, it’s easy enough to use
the serial link itself to move files. (You can, of course, fetch those files
over Tor assuming you’re already set up.) stty
and cat
are your friends
here and will let you (slowly) bootstrap up just about anything you like. If
you’re going to move larger files this way, I highly recommend gkermit
or
even the full ckermit
program.