The following patch enables one-hop intro point and rendezvous connections for onion service servers. It is compatible with current tor clients and relays.
Use the following torrc lines to make intro point and rendezvous point connections one-hop on the server:
This is an alternate design and implementation of the "Stream latency is reduced on a 4-hop circuit" feature of #prop252. (It reduces the path length, but does it in a different way to the proposal, by building a one-hop path to hidden/onion service introduction points and rendezvous points.)
These one-hop introduction point and rendezvous paths are compatible with the existing hidden service implementation, and it works on the existing tor network without any changes to older relays or clients.
Some sites wish to scale up public ".onion" sites within the next 6 months, using the existing Tor network and Tor clients. This patch enables their use case, until #prop252 is implemented and deployed widely enough.
The one-hop circuits that were created by this code weren't marked as one-hop, which caused a info-level bug log message in the pathbias code.
I've pushed a fixup to the branch hs-route-len-v2.
There's also a branch with the same changes based on Tor 0.2.6.10 at hs-route-len-02610.
I think this code needs a redesign (and a proposal) before it is merged, to:
get community consensus on the tradeoffs between efficiency, features, and splitting the hidden service anonymity set
collapse the two experimental options into a single option like OnionServerOneHop 1, which would do the equivalent of __OnionSrvIntroRouteLength 1 __OnionSrvRendRouteLength 0
(I also dislike that one-hop paths need to have one of these settings as 1, and the other as 0. This should be handled internally and not exposed to operators.)
Trac: Severity: N/Ato Normal Status: needs_review to needs_revision
when RendezvousSingleOnionServiceNonAnonymousServer is changed at runtime, tor attempts to re-use introduction points and introduction point circuits after a HUP. It should close the circuits and discard the intro points.
The proposal has been updated and posted to tor-dev.
The proposal is available in the branch “feature-17178-rsos” at https://github.com/teor2345/torspec.git as
torspec/proposals/ideas/xxx-rend-single-onion.txt
This can be tested with the chutney branch "feature-17178-rsos” at https://github.com/teor2345/chutney.git using the command:
src/test/test-network.sh --flavor rsos-min
Hello teor, what can I do here to help? What's the current status here? I can spend a few hours this week on this ticket.
The code works and has been tested using chutney.
It doesn't behave correctly when RendezvousSingleOnionServiceNonAnonymousServer Is changed at runtime (using torrc and a HUP, or over the control port). The current code attempts to re-use introduction points and introduction point circuits after a HUP. But if the value of RendezvousSingleOnionServiceNonAnonymousServer has changed, the circuits are the wrong length. Tor should close the circuits and discard the intro points (this needs to be coded), then post a fresh descriptor (this likely already happens anyway after a config change).
There aren't any specific unit tests, but any existing unit tests pass.
Hello teor, what can I do here to help? What's the current status here? I can spend a few hours this week on this ticket.
The code works and has been tested using chutney.
It doesn't behave correctly when RendezvousSingleOnionServiceNonAnonymousServer Is changed at runtime (using torrc and a HUP, or over the control port). The current code attempts to re-use introduction points and introduction point circuits after a HUP. But if the value of RendezvousSingleOnionServiceNonAnonymousServer has changed, the circuits are the wrong length. Tor should close the circuits and discard the intro points (this needs to be coded), then post a fresh descriptor (this likely already happens anyway after a config change).
This can be done yes, but it's some moderate engineering complexity. Are we sure we want RendezvousSingleOnionServiceNonAnonymousServer to be hotpluggable like that? We think HS operators would enjoy that, or can we just fail and warn the user if RSOS was enabled after a HUP?
And also there is the opposite direction. What happens if RSOS is disabled after a HUP? Then you need to kill all 1-hop circuits and make 3-hop ones? Do we want people to think it's so easy to switch between these two modes?
Hello teor, what can I do here to help? What's the current status here? I can spend a few hours this week on this ticket.
The code works and has been tested using chutney.
It doesn't behave correctly when RendezvousSingleOnionServiceNonAnonymousServer Is changed at runtime (using torrc and a HUP, or over the control port). The current code attempts to re-use introduction points and introduction point circuits after a HUP. But if the value of RendezvousSingleOnionServiceNonAnonymousServer has changed, the circuits are the wrong length. Tor should close the circuits and discard the intro points (this needs to be coded), then post a fresh descriptor (this likely already happens anyway after a config change).
This can be done yes, but it's some moderate engineering complexity. Are we sure we want RendezvousSingleOnionServiceNonAnonymousServer to be hotpluggable like that? We think HS operators would enjoy that, or can we just fail and warn the user if RSOS was enabled after a HUP?
That's a really good point. The existing design deliberately doesn't support mixing HS and RSOS for security reasons. (See below.)
And also there is the opposite direction. What happens if RSOS is disabled after a HUP? Then you need to kill all 1-hop circuits and make 3-hop ones?
If we were to close all connections, it would have to happen on both RSOS to HS, and HS to RSOS. But that's not secure.
Do we want people to think it's so easy to switch between these two modes?
You're right, we really can't allow hot-plugging securely and safely.
I think we need secure defaults here:
a Tor instance can only run all RSOS, or all HS at one time (RendezvousSingleOnionServiceNonAnonymousServer is global)
after an onion service is launched, a Tor instance can only run all RSOS, or all HS, until Tor is restarted (RendezvousSingleOnionServiceNonAnonymousServer is persistent on first RSOS/HS launch)
These rules protect operators from inadvertent disclosure by either simultaneously or sequentially running onion services with 1 and 3 hop paths on the same Tor instance. But they also support the ephemeral use-case, where the operator launches Tor, sets or unsets RendezvousSingleOnionServiceNonAnonymousServer, then launches an onion service.
I have implemented 1, but I haven't implemented 2 yet. To implement it, Tor needs to check: if RendezvousSingleOnionServiceNonAnonymousServer is changed, and there are any onion services active (excluding deleted/inactive services, if this is a feature of (ephemeral) services), then reject the change and warn the user. Tell them to restart if they really want to change it.
teorIs it OK to make operators restart their tor instance to switch to/from (Rendezvous) Single Onion Services?nickmIMO absolutely yes; and in fact... the same datadirectory probably shouldn't let you do that without some kind of a "yes I know what I'm doing"err,the same hidden service directory
Hello, please see branch feature-17178-rsos in my repo for the code that blocks
RSOS hotplugging. That was easy to do.
Now if we want to do more fancy stuff like "don't allow transitioning between
RSOS and normal HS even after restart", then we need to do more stuff (like put
a notice in the hidden service dir that RSOS was enabled). I'm wondering if we
should get in this trouble.
BTW, I noticed there is no code that blocks cannibalization of rendezvous
circuits. I remember that this was a problem in the past, since you ended up
cannibalizing 3-hop circuits all the time. Is this something we don't care
anymore, or maybe it was never a problem?
Hello, please see branch feature-17178-rsos in my repo for the code that blocks
RSOS hotplugging. That was easy to do.
Now if we want to do more fancy stuff like "don't allow transitioning between
RSOS and normal HS even after restart", then we need to do more stuff (like put
a notice in the hidden service dir that RSOS was enabled). I'm wondering if we
should get in this trouble.
Ideally, we should mark every key that is used for a HS or RSOS on first use, and refuse to use it for the other flavour/purpose unless an option like PermitNonAnonymousMultiUseOnionServiceKeys is set.
But I don't like the idea of modifying keys. So instead, we could write a file to the directory that says whether the keys were last used for an anonymous or non-anonymous service.
I think the security benefits are worth the extra complexity, and I can't see how to make it work without an extra file in the onion service directory.
(As a comparison, Tor2Web will only run with a different binary (with a compilation flag) and specific torrc option.)
BTW, I noticed there is no code that blocks cannibalization of rendezvous
circuits. I remember that this was a problem in the past, since you ended up
cannibalizing 3-hop circuits all the time. Is this something we don't care
anymore, or maybe it was never a problem?
I improved the code so the one-hop flag is set on intro and rendezvous circuits. (The previous code modified the path length calculation.)
Circuits with the one-hop flag never cannibalize existing circuits, and aren't themselves cannibalized.
It makes for much simpler code, and the flag is set where the circuit is initiated, so it's more readable, too. (And setting the one-hop flag makes the rest of the code treat the circuits like it does other one-hop circuits, which is also a win.)
Hello, please see branch feature-17178-rsos in my repo for the code that blocks
RSOS hotplugging. That was easy to do.
Now if we want to do more fancy stuff like "don't allow transitioning between
RSOS and normal HS even after restart", then we need to do more stuff (like put
a notice in the hidden service dir that RSOS was enabled). I'm wondering if we
should get in this trouble.
Ideally, we should mark every key that is used for a HS or RSOS on first use, and refuse to use it for the other flavour/purpose unless an option like PermitNonAnonymousMultiUseOnionServiceKeys is set.
But I don't like the idea of modifying keys. So instead, we could write a file to the directory that says whether the keys were last used for an anonymous or non-anonymous service.
I think the security benefits are worth the extra complexity, and I can't see how to make it work without an extra file in the onion service directory.
(As a comparison, Tor2Web will only run with a different binary (with a compilation flag) and specific torrc option.)
OK, I coded this feature and pushed it in branch feature-17178-rsos. Let me know if you like it.
I still feel a bit guilty for adding another 100 lines of rarely-used code to rendservice.c, but...
Also, I was not sure what to do about ephemeral hidden services who don't have an HS directory.
Hello, please see branch feature-17178-rsos in my repo for the code that blocks
RSOS hotplugging. That was easy to do.
Now if we want to do more fancy stuff like "don't allow transitioning between
RSOS and normal HS even after restart", then we need to do more stuff (like put
a notice in the hidden service dir that RSOS was enabled). I'm wondering if we
should get in this trouble.
Ideally, we should mark every key that is used for a HS or RSOS on first use, and refuse to use it for the other flavour/purpose unless an option like PermitNonAnonymousMultiUseOnionServiceKeys is set.
But I don't like the idea of modifying keys. So instead, we could write a file to the directory that says whether the keys were last used for an anonymous or non-anonymous service.
I think the security benefits are worth the extra complexity, and I can't see how to make it work without an extra file in the onion service directory.
OK, I coded this feature and pushed it in branch feature-17178-rsos. Let me know if you like it.
Thanks, I'll have a look at it this week.
I still feel a bit guilty for adding another 100 lines of rarely-used code to rendservice.c, but...
Also, I was not sure what to do about ephemeral hidden services who don't have an HS directory.
Ephemeral hidden services don't need this feature:
On HUP: RSOS / HS path lengths can't change, and
On restart: ephemeral hidden service keys aren't persistent (so there is no need to mark them as anonymous / non-anonymous).
If operators retrieve their own ephemeral keys, I think they can manage anonymity as well.
code looks good, but I wonder if we should make it generic, so we can use it with single onion services as well, by using more generic terms, like SOS and "non-anonymous onion service":
There's a warning for ephemeral RSOS, but I think it's actually an acceptable use case. The keys aren't persistent, so there's no issue with reuse in anonymous/non-anonymous modes. Maybe make it a log_info and remove the "can't be"?
log_warn(LD_GENERAL, "Ephemeral HS can't be started as RSOS.");
Running the changes:
chutney verify works using chutney rsos-min network in the chutney feature-17178-rsos branch
changing from RSOS to HS or HS to RSOS and then doing a HUP fails.
changing from RSOS to HS and then relaunching tor fails.
Changing from HS to RSOS in the torrc and relaunching doesn't fail. I think this is OK, because we need an upgrade path for hidden service operators to deliberately upgrade to a rendezvous single onion service if they want to. And I don't like the idea of making operators touch a file in the filesystem to use RSOS.
We already have an extensive warning the first time we start up with RSOS and poison the directory. Is this sufficient for the HS -> RSOS case?
[warn] RendezvousSingleOnionServiceNonAnonymousServer (RSOS) is set. Every hidden/onion service on this instance is NON-ANONYMOUS. Clients remain location-anonymous, but may be statistically distinguishable. If RSOS is disabled, Tor will refuse to launch RSOS hidden services to protect against misconfiguration errors. This setting is for experimental use only.
code looks good, but I wonder if we should make it generic, so we can use it with single onion services as well, by using more generic terms, like SOS and "non-anonymous onion service":
{{{
#define SOS_POISON_FNAME "non_anonymous_onion_service"
}}}
There's a warning for ephemeral RSOS, but I think it's actually an acceptable use case. The keys aren't persistent, so there's no issue with reuse in anonymous/non-anonymous modes. Maybe make it a log_info and remove the "can't be"?
{{{
log_warn(LD_GENERAL, "Ephemeral HS can't be started as RSOS.");
}}}
OK I implemented both of the above changes and pushed them in my branch feature-17178-rsos.
Can someone test that ephemeral HSes play nicely with the RSOS feature in this latest branch? Will do it myself soon if someone beats me to it.
Please see my branch feature-17178-rsos for a unittest on the poisoning functionality.
Also, I feel a bit uneasy about code like this:
+ if (!options->RendezvousSingleOnionServiceNonAnonymousServer) {+ service->next_upload_time += crypto_rand_int(2*rendpostperiod);+ }
It's a bit like we are treating location-hidden services as a special case, whereas we should probably have it be the default case. I don't mind the specific snippet above, but maybe we could functionify the RSOS option check to also make it a bit nicer (since it's huge and unreadable). Maybe we could put it in a function service_has_no_location_hiding() (or some nicer name please).
Similarly I don't like:
#ifndef NON_ANONYMOUS_MODE_ENABLED- tor_assert(!(circuit->build_state->onehop_tunnel));+ if (!get_options()->RendezvousSingleOnionServiceNonAnonymousServer) {+ tor_assert(!(circuit->build_state->onehop_tunnel));+ } #endif
these asserts used to make the code look terrible, and now they are worse. The number of negative clauses in those asserts makes it even more confusing. Do you think we could functionify those asserts similar to assert_circuit_ok()? Also, shouldn't we assert that if we are in RSOS mode, it is a one hop tunnel?
}}}
It's a bit like we are treating location-hidden services as a special case, whereas we should probably have it be the default case. I don't mind the specific snippet above, but maybe we could functionify the RSOS option check to also make it a bit nicer (since it's huge and unreadable). Maybe we could put it in a function service_has_no_location_hiding() (or some nicer name please).
For this particular case, refactored into rend_reveal_startup_time(), which applies to RSOS and will apply to SOS.
Similarly I don't like:
{{{
#ifndef NON_ANONYMOUS_MODE_ENABLED
}
#endif
}}}
these asserts used to make the code look terrible, and now they are worse. The number of negative clauses in those asserts makes it even more confusing. Do you think we could functionify those asserts similar to assert_circuit_ok()?
Refactored into assert_circ_onehop_ok(), which uses the new function rend_allow_direct_connection(). rend_allow_direct_connection() is true when in Tor2web or RSOS modes. (But won't be true for SOS, as they accept incoming client extend requests rather than making requests.)
Also, shouldn't we assert that if we are in RSOS mode, it is a one hop tunnel?
No, the security property we're asserting is that standard hidden services (and clients) must make 3-hop paths for everything except directory connections. This keeps their location hidden.
When we don't care about location hiding (RSOS, Tor2web), then there may be other reasons to make 3-hop paths. For example, RSOS make 3-hop paths to HSDirs to avoid denial of service attacks.
it's different than the assert that it replaced. I would suggest to spread it out and maybe add some comments? Or is it something trivial that I don't get?
Hmm, I got it now. Maybe this whole comment is moot.
How come assert_circ_onehop_ok() is a macro? Having it as a macro makes it look kinda ugly and also it's hidden in a header file.
Because the assertions correspond to the lines in the source file where the macro is used.
Otherwise, I could modify it to be a macro that calls a function that takes __FILE__ and __LINE__. We do that with one existing function already.
Also, I don't entirely understand:
{{{
tor_assert((is_dir) || rend_allow_direct_connection((options)) ||
(circ)->build_state->onehop_tunnel == 0);
}}}
it's different than the assert that it replaced. I would suggest to spread it out and maybe add some comments? Or is it something trivial that I don't get?
Hmm, I got it now. Maybe this whole comment is moot.
For the record, it's an abstraction of the different checks for valid one-hop connections:
If you really don't want to, that's fine but the tor_asprintf doesn't need to use %s for it. Same for PATH_SEPARATOR. (tor_asprintf(&fname, "%s" PATH_SEPARATOR ...))
Nitpick: You can use tor_free(poison_fname); once after file_status() is called. Avoid two of them.
commit ff63c64c9cdebb7ea50354a3e72cb57758f9f939
Hrm that commit simply return 0. Can't we flag the HS that it's actually in RSOS mode? By that I mean, can we have two ephemeral HS, one in RSOS and the other one not ?
commit 1e0b54feb5629eb85e9b365db684e1df8073a516
rend_allow_direct_connection() comment mentions: "Returns true in Tor2web and RSOS modes.". But the code return 1 if one of them is enabled, not both. So I'm guessing typo here.
commit 80a041b9740fa69126f40ddc1c8bba9555c8a08b
In rend_client_get_random_intro_impl(), this is added:
This should be a static const char *. It's not required but imo we should use type as much as we can which is much more helpful on the compiler side.
#define RSOS_POISON_FNAME "non_anonymous_hidden_service_rsos"}}} If you really don't want to, that's fine but the `tor_asprintf` doesn't need to use `%s` for it. Same for `PATH_SEPARATOR`. (`tor_asprintf(&fname, "%s" PATH_SEPARATOR ...)`)
Fixed in 30969ca2abec80ebacb585767a88915a53293c01.
Nitpick: You can use tor_free(poison_fname); once after file_status() is called. Avoid two of them.
Fixed in 30969ca2abec80ebacb585767a88915a53293c01.
commit ff63c64c9cdebb7ea50354a3e72cb57758f9f939
Hrm that commit simply return 0. Can't we flag the HS that it's actually in RSOS mode? By that I mean, can we have two ephemeral HS, one in RSOS and the other one not ?
No, we decided that was a bad design, as the non-anonymous RSOS could expose the anonymous HS.
Whenever RSOS is set, it applies to all configured services.
commit 1e0b54feb5629eb85e9b365db684e1df8073a516
rend_allow_direct_connection() comment mentions: "Returns true in Tor2web and RSOS modes.". But the code return 1 if one of them is enabled, not both. So I'm guessing typo here.
One of the glorious ambiguities of English. I meant: "Returns true in Tor2web mode and returns true in RSOS mode."
Fixed in 70f44487b8ecdeef09681f02068ac30b6643a0e5.
commit 80a041b9740fa69126f40ddc1c8bba9555c8a08b
In rend_client_get_random_intro_impl(), this is added:
{{{
new_extend_info = extend_info_from_node(node, 0);
new_extend_info = extend_info_from_node(node,
rend_allow_direct_connection(options));
}}}
This is somehow worrying me. I get the Tor2Web mode but what if I use my HS server as a client, I loose anonymity? Am I seeing that right?
Same goes in find_rp_for_intro(), if the HS is somehow compiled with NON_ANONYMOUS_MODE_ENABLED (Tor2Web), it goes to the RP/IP with one hop?
I think that you're right, if you use a Tor2web client with a HS, or run a standard tor client as a RSOS, you lose anonymity.
So let's prevent that. See bae2a4de61d6d440840411fb992ffc72ad04c660.
For the record, these code changes only control how the node's address is selected.
(In legacy/trac#17840 (moved), we change extend_info_from_node() to select addresses based on ReachableAddresses, ClientUseIPv4/6 and ClientPreferIPv6OR/DirPort.)
It's the code like this that makes the connections one-hop:
{{{
if (rend_allow_direct_connection(options)) {
flags = flags | CIRCLAUNCH_ONEHOP_TUNNEL;
}
I think that you're right, if you use a Tor2web client with a HS, or run a standard tor client as a RSOS, you lose anonymity.
So let's prevent that. See bae2a4de61d6d440840411fb992ffc72ad04c660.
Ok I see that this is extra protection since somehow a client using an RSOS tor instance is distinguishable in some ways? (comment in or.h):
+ * location-anonymous. However, client use of a RSOS may be statistically+ * distinguishable.
As a last change, I would document that part in the manpage for the option RendezvousSingleOnionServiceNonAnonymousServer. It changes things a bit because now the server that host a RSOS needs a second tor for any other client usage which is not that easy to have in a standard Linux distro (systemd and all only manage one single tor). As long as the user as a way to learn that before setting it up, it's fine.
I think that you're right, if you use a Tor2web client with a HS, or run a standard tor client as a RSOS, you lose anonymity.
So let's prevent that. See bae2a4de61d6d440840411fb992ffc72ad04c660.
Ok I see that this is extra protection since somehow a client using an RSOS tor instance is distinguishable in some ways? (comment in or.h):
{{{
location-anonymous. However, client use of a RSOS may be statistically
distinguishable.
}}}
As a last change, I would document that part in the manpage for the option RendezvousSingleOnionServiceNonAnonymousServer. It changes things a bit because now the server that host a RSOS needs a second tor for any other client usage which is not that easy to have in a standard Linux distro (systemd and all only manage one single tor). As long as the user as a way to learn that before setting it up, it's fine.
Thanks!
I added the manual page changes for RSOS and Tor2webMode in 4093eeb. I also fixed the option checking so that it takes into account the compile-time --enable-tor2web-mode setting, and not just the options at runtime.
find a better name (I started a discussion on tor-onions, and asked alianthus to help us find a name that would make sense to the wider public),
fix legacy/trac#17788 (moved) so that RSOS (and relays) won't extend to their own addresses, or and RSOS, HS, and relays won't extend to private addresses.
Trac: Milestone: Tor: 0.2.8.x-final to Tor: 0.2.9.x-final