Hi list,
The attached patch implements support for systemd socket activation.
For people who don't know what that is: systemd is an "init" system for Linux. Socket activation means that systemd binds all the sockets in advance, and only spawns Tor once somebody attempts to connect.
More information here: http://0pointer.de/blog/projects/socket-activation.html
I rarely use Tor, so there's no reason to have it running all the time (wasting battery on my laptop), but it's also annoying to launch it manually every time. Socket activation is ideal for this use case.
There are 3 changes to the startup process: 1. Before loading the configuration, Tor identifies all sockets passed in by systemd and creates pending_socket_t objects. I considered reusing connection_t, but that seemed to require way more modification to the code. 2. After parsing configuration, when Tor would otherwise create new listeners, it first tries to match up the address/port to existing pending sockets. If a pending socket does not match, it opens a new one as usual. 3. After configuration parsing is done, Tor closes all remaining unmatched systemd sockets and logs a warning for each one.
This infrastructure can also be used to support "launch-on-demand" with launchd on OS X, but I have no experience with that.
Known problems: * TCP and UDP sockets work, but Unix sockets are not currently yet implemented. * It's impossible to support hibernation as is for systemd sockets -- the systemd daemon still keeps a reference to the listener socket even after we close it, and it's impossible to re-bind the port later. * Closing unmatched sockets is a bad idea for the same reason: systemd still keeps it open and connections hang forever. Perhaps a better solution is to keep the socket and simply reject all connections, ditto hibernating connections?
I have added the source of sd-daemon.c into Tor -- it's easier to manage this way and we don't introduce any new library dependencies (it's 804 LoC total). This approach is also encouraged by systemd itself. The code turns into a no-op when built on Windows.
Full patch is attached, also available as individual commits from my GitHub clone (branch "systemd"): https://github.com/intgr/tor/tree/systemd
Regards, Marti
Marti Raudsepp:
Hi list,
The attached patch implements support for systemd socket activation.
For people who don't know what that is: systemd is an "init" system for Linux. Socket activation means that systemd binds all the sockets in advance, and only spawns Tor once somebody attempts to connect.
More information here: http://0pointer.de/blog/projects/socket-activation.html
I rarely use Tor, so there's no reason to have it running all the time (wasting battery on my laptop), but it's also annoying to launch it manually every time. Socket activation is ideal for this use case.
Hi Marti,
Could you open a ticket on trac?
https://trac.torproject.org/projects/tor/newticket
If you'd like to use the cypherpunks account, feel free - no need to make an account if you don't want to do so.
It seems to me that this requires a bit of documentation - for example - do you expect to run Tor as say, some-tor-user as usual (what is it on Fedora, anyway?)? How do you expect UDP to be handled as Tor does not actually support UDP (excepting DNSPort, of course). Do you expect this to be used by Tor only in client mode? Or do you expect it to be used by bridges or relays? It seems that socket activation is generally a reasonable idea - though I expect it will simply result in a Tor running nearly all of the time, no? If nothing else, I suspect it will often allow the control port controllers to easily connect - even if Tor isn't started. If anything, we may have an easy way to ensure that any authorized Tor controller may start a Tor, which is actually quite nice!
I have a bunch of questions that come to mind about anti-forensics - for example, if we restart Tor, what happens to data sitting in buffers in memory? The data stays queued, right? How do we ensure that Tor, if started for SOCKS will allow only the right configurations? Will this need to be enabled on all Tor Gnu/Linux builds or should we only enable this code for systemd supporting systems and only with a configuration option, even if we have it compiled into Tor?
Comments on the patch follow:
src/ext/README should include information on the author as well as the original location of the source.
I suspect we want to ensure that we only include mqueue.h if we have it.
There is a mix of !defined() and defined() - eg:
defined(DISABLE_SYSTEMD) !defined(SD_DAEMON_DISABLE_MQ)
I tend to think that we should assert positively what we want and what we expect - if we don't have it, we shouldn't make it confusing as to what is or isn't built.
I tend to prefer code like:
if (NULL != e) {
to code like:
if (!e) {
I also tend to be more specific with my ints - rather than int, why not uint32_t? Furthermore - will the stats leave behind a trace? What priviledges are required or set for /dev/mqueue I wonder?
Shouldn't 'foosddaemonhfoo' be a bit more... Torish? :)
Will all logging be redirected into the Tor logging subsystem?
This code seems to be optimized for your use path:
+ s = get_pending_socket(type, socktype, &addr, usePort); + if (SOCKET_OK(s)) + { + is_bound = 1; + } + else + { + log_notice(LD_NET, "Opening %s on %s", + conn_type_to_string(type), fmt_addrport(&addr, usePort)); + + s = tor_open_socket(tor_addr_family(&addr), socktype, is_tcp ? IPPROTO_TCP: IPPROTO_UDP); + if (!SOCKET_OK(s)) { + log_warn(LD_NET,"Socket creation failed: %s", + tor_socket_strerror(tor_socket_errno(-1))); + goto err;
what happens if we never take your path? Is there a performance hit?
When you apply this patch and enable debugging, do you see any warnings? On what platform did you test it where you expect it to work? How about other platforms where it shouldn't work but also shouldn't do harm?
Nick and Roger probably have stronger opinions that matter more but I just happened to arrive at the scene first...
All the best, Jacob
Hi!
Thanks for the comments. Sorry if my reply is long-winded, but you left me no other choice. :)
On Sun, May 19, 2013 at 6:55 PM, Jacob Appelbaum jacob@appelbaum.net wrote:
Could you open a ticket on trac?
Done: https://trac.torproject.org/projects/tor/ticket/8908
It seems to me that this requires a bit of documentation
Sure. Sorry, I should have included some info about how to use this thing :)
Since systemd has no way of knowing which ports Tor actually needs, there needs to be another systemd unit file called tor.socket (in addition to the regular tor.service)
Given for example a torrc: SocksPort 9050 ControlPort 9051 TransPort 9040 DNSPort 9053
You need to have an accompanying tor.socket file with: [Socket] ListenStream=127.0.0.1:9050 ListenStream=127.0.0.1:9051 ListenStream=127.0.0.1:9040 ListenDatagram=127.0.0.1:9053 [Install] WantedBy=sockets.target
Normally you'd put it in /etc/systemd/system. Then run "systemctl daemon-reload; systemctl enable tor.socket"
The matching of listening ports to services is done in Tor based on socket type, bind address and port.
If torrc has some ports that tor.socket doesn't define, then those aren't involved in socket activation. But when Tor starts (e.g. started manually or by activation on other ports), it will bind the remaining ports as usual.
In other cases, if the two config files don't match up, Tor will log a warning (and might fail to start up). Fortunately systemd provides easy access to log messages (via "systemctl status tor")
do you expect to run Tor as say, some-tor-user as usual (what is it on Fedora, anyway?)?
Yeah, it gets started as usual. The user is "tor" on Arch Linux, not sure about Fedora. This is unrelated to socket activation.
An extra benefit of socket activation is that systemd can bind privileged ports like 443 on behalf of Tor, even if Tor starts as an unprivileged user.
How do you expect UDP to be handled as Tor does not actually support UDP (excepting DNSPort, of course)
DNSPort is the use case I had in mind. If a ListenDatagram socket doesn't match up to a DNSPort in torrc then a warning is logged and the socket is closed.
Do you expect this to be used by Tor only in client mode? Or do you expect it to be used by bridges or relays? It seems that socket activation is generally a reasonable idea - though I expect it will simply result in a Tor running nearly all of the time, no?
That's up to the user, really. I agree it's not very useful for running a relay, but it doesn't hurt either.
I expect distros to ship a tor.socket file that matches their default torrc configuration (just 9050) and use that by default. If the user wants to customize it, they have to keep the two in sync, or use the old way of launching it.
If it's installed with "systemctl enable tor.service" then it gets started at boot. "systemctl enable tor.socket" will enable socket activation.
If nothing else, I suspect it will often allow the control port controllers to easily connect - even if Tor isn't started.
Yeah, I tried and it works nicely with Vidalia.
If anything, we may have an easy way to ensure that any authorized Tor controller may start a Tor, which is actually quite nice!
Hmm, making sure that the starter is actually authorized can get complicated. I'd prefer not having to write that dirty code. :)
I have a bunch of questions that come to mind about anti-forensics - for example, if we restart Tor, what happens to data sitting in buffers in memory? The data stays queued, right?
During a restart all the client connections are dropped and their buffers are presumably deallocated. Only the listen sockets persist.
But new inbound connects will be queued and any data sent on those will be buffered in the kernel's TCP buffers as usual, until Tor catches up and handles it. However, the data will stay in buffers longer during startup. Not sure if this is a concern or not.
systemd itself doesn't buffer data, it only grabs the listening socket and passes it on.
How do we ensure that Tor, if started for SOCKS will allow only the right configurations?
Sorry, I don't understand this question.
Will this need to be enabled on all Tor Gnu/Linux builds or should we only enable this code for systemd supporting systems and only with a configuration option, even if we have it compiled into Tor?
I think it's easiest to enable it always. There are no extra dependencies and it doesn't do anything if it's not started by systemd.
src/ext/README should include information on the author as well as the original location of the source.
Will do.
---
I suspect we want to ensure that we only include mqueue.h if we have it.
...
What priviledges are required or set for /dev/mqueue I wonder?
This code is already disabled, I added this to sd-daemon.h: #define SD_DAEMON_DISABLE_MQ 1
Now that you mention it, I realized that this doesn't work -- sd-daemon.h gets included too late. Will fix.
There is a mix of !defined() and defined()
...
Shouldn't 'foosddaemonhfoo' be a bit more... Torish? :)
Well this code (sd-daemon.c and .h) comes from systemd upstream; I think we shouldn't modify it unless really necessary.
Or if we do want to modify it, we should just delete all the parts that we don't use. Should I do that?
I tend to prefer code like: if (NULL != e) { to code like: if (!e) {
I also tend to be more specific with my ints - rather than int, why not uint32_t?
I tried to follow patterns already used in the connection.c file. File descriptors, socket types, booleans and return codes seem to be using type "int". Should I change them all or...?
Furthermore - will the stats leave behind a trace?
The stats?
Will all logging be redirected into the Tor logging subsystem?
Erm, I'm using Tor's log_* functions, so yes.
This code seems to be optimized for your use path:
- s = get_pending_socket(type, socktype, &addr, usePort);
...
s = tor_open_socket(tor_addr_family(&addr), socktype, is_tcp ?
what happens if we never take your path? Is there a performance hit?
Well this code is only executed when the configuration gets loaded (at startup/reload), once for each *Port definition. And the overhead is pretty small if the pending_sockets list is empty (when not using socket activation).
Doing it the other way around seems silly (try to bind the socket, then go down the other path if it returned EADDRINUSE) and it seems wouldn't work for UDP.
When you apply this patch and enable debugging, do you see any warnings?
Do you mean "Log debug syslog", or what kind of debugging?
Apart from the fact that hibernation is broken (as stated in the initial mail), I don't see any warnings.
On what platform did you test it where you expect it to work? How about other platforms where it shouldn't work but also shouldn't do harm?
I tested this on Linux (Arch Linux x86_64)
On other platforms sd_listen_fds() will return 0, in which case it works just as if socket activation isn't used.
Unfortunately I don't have a development environment on any other platform, but I'm fairly confident it will work.
Regards, Marti
On Sun, May 19, 2013 at 3:26 PM, Marti Raudsepp marti@juffo.org wrote:
Hi!
Thanks for the comments. Sorry if my reply is long-winded, but you left me no other choice. :)
On Sun, May 19, 2013 at 6:55 PM, Jacob Appelbaum jacob@appelbaum.net wrote:
Could you open a ticket on trac?
I just commented over there. Thanks for the patch! Let's move the discussion over onto the tracker.
best wishes,
On Sun, May 19, 2013 at 6:55 PM, Jacob Appelbaum jacob@appelbaum.net wrote:
On what platform did you test it where you expect it to work? How about other platforms where it shouldn't work but also shouldn't do harm?
Actually it looks some sd-daemon.c header includes can cause problems on Windows, particularly <unistd.h>, <netinet/in.h> and <sys/un.h>.
I guess it really is a better idea to trim the sd-daemon.* files down to bare minimum and enclose it all in #ifdef __linux__
Regards, Marti