aboutsummaryrefslogtreecommitdiff
path: root/doc/dev/notes/BridgeIssues.md
blob: d5c0f496b06611d82a971c46d3cd3e1b0a6479c0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# Adding Bridges and Pluggable Transports to Arti

This document will go over the general issues that we face when building
client-side support for bridges and pluggable transports in Arti.


## Tor's anticensorship features: a lower-level perspective

Here's what you need to know about bridges.

Fundamentally, a "**Bridge**" is a relay that we use as the first hop
for our circuits _because it is configured by the user_, not because it is
listed in the main network directory.[^1]

A "Bridge" can either be reached by the regular Tor (cells over TLS)
protocol, or by some different censorship-resistant "**transport**"
protocol.[^2]

Users configure a single bridge by listing some of the following:
  * A set of supported transports that can be used.  If this set is
    empty, the client just uses the default transport.
  * A set of IP:Port addresses that can be used to reach the bridge.
    (With some transports, the transport itself figures out how to
    contact the bridge, and this set is empty or ignored.)
  * A set of identities to expect for the bridge.  (Note that C Tor
    allows this set to be empty; Arti will not, since it tends to
    create severe implementation headaches.)
  * For each transport, a set of transport-specific parameters.  (These
    might, for example, be additional protocol-specific authentication
    keys.)

Users can turn bridge usage on and off.  This is a single boolean that
does not require deleting their entire list of bridges.

Users can configure a large number of bridges; if they do, then we want
to pick randomly from among them and favor just a few, in the same way
that we do when choosing guard relays.  We want to reuse our `GuardMgr`
code for this.  (Doing so, however, may require a bit of refactoring,
since the current `GuardMgr` selects `Relay`s from a `NetDir`, and we'll
have to select `Bridge`s from some kind of underlying `BridgeSet`.)

Since bridges are not listed in the main network directory, we can't use
the directory to look up their **onion keys** (the ones we use to build
multihop circuits).  Instead, we have to connect to the bridge and ask
the bridge for a **router descriptor**—a self-signed document describing
the bridge and its supported keys.  Descriptors are only valid for a
while.

Some transports are implemented as external processes, using a
"**managed pluggable transport**" mechanism.  In this design, the Tor
client program is responsible for launching and monitoring external
binaries that provide transports over SOCKS4 or SOCKS5.  The [protocol]
for communicating with these binaries uses stdin, stdout, and the
environment.  To use these binaries as transports, the client treats
them as SOCKS4 or SOCKS5 proxies, and encodes per-connection arguments
in the authentication fields of the SOCKS handshakes.

A single managed PT binary can implement multiple transports: if it
does, each one gets its own local proxy address.

[protocol]: https://gitlab.torproject.org/tpo/core/torspec/-/blob/main/pt-spec.txt

## Architectural implications

With those issues in mind, let's go through the parts of the
implementation that are simple.

We'll need to extend the definition of `ChanTarget` to include the
additional information that bridges need: which protocol to use, and
protocol-specific information.  We might want a separate trait for
`ChanTarget`s that can have this information, since relays will never
want to look at it, and in fact will require that it is absent.

We'll want to extend the `tor-chanmgr` crate to know about more ways to
launch channels.  It will probably have a registry of known transport
mechanisms (including the default transport) and know how to connect to
each one.

We'll need to implement `tor-ptmgr` crate that launches and monitors
managed pluggable transport binaries.  It should have the ability to
launch and shut down PTs on demand, not just because they are
configured.  (In other words, if no bridge wants a given transport, we
shouldn't run that transport.)

We'll need to teach `tor-guardmgr` to be able to take its input from a
configured set of bridges rather than from a `NetDir`.  This needs to be
a separate "guard selection", since we want to be able to switch back
and forth between using bridges and not using bridges.

In `ChanMgr` and `GuardMgr`, we'll need a way to identify bridges. This
will be interesting, since bridges can be configured only with a single
identity that is _not_ their Ed25519 identity.  (In `GuardMgr`, we might
have as little as an `RsaIdentity`.  In `ChanMgr`, we will have more
identity information, but only _after_ the channel handshake is
successful.)  If the same identity is listed twice with different
addresses and transports, we may need to treat them as different
bridges.[^3] We may need to assign configured bridges a local unique ID,
and use that identify which bridge is which in ChanMgr.  We may need a
flexible matching approach in our `GuardMgr` code to see which
remembered guard is equivalent to which configured bridge.

We'll need to download and cache bridge's router descriptors as needed.
This is different from downloading regular directory information in
several ways:
   * We can only download a bridge's descriptor _from that bridge_.
   * We need to be able to download a bridge's descriptor _even when we
     have no directory_.
   * When using bridges, we _only use bridges_ as our directory caches:
     never fallback directories.

Let's try to, to the extent possible, to  put all of the client-side
bridge and pluggable
transport code behind Cargo features (`bridge-client` and `pt-client`,
maybe), so that we can disable them for Relays and for
resource-constrained clients that don't want them.

## Challenges with implementing anticensorship in Arti

Now that we've been through all of that, here are some of the challenges
and open questions that we need to solve as we implement these
anticensorship features in Arti.

### Problem 1: The directory infrastructure and logic

Our existing directory code doesn't know about bridges.  We'll need to
think carefully about the logic that drives guard selection and
directory downloads.

We'll need an additional directory state where we try to make sure we
fetch bridge descriptors.  This has to happen after bridges are
selected.  There needs to be feedback between the `GuardMgr` and the
`DirMgr` here: the `GuardMgr` can't hand out bridges for multi-hop
circuits until it knows descriptors for them; the `DirMgr` can't fetch
any bridge descriptors until it knows what the `GuardMgr` wants.

(The `DirMgr` also needs to keep bridge descriptors separate from regular
relays, to avoid leaking whether we've used a given bridge when using it
as a relay, and vice versa.)


### Problem 2: Circuits through bridges

Our `CircMgr` can build one-hop directory circuits through any kind of
`ChanTarget`.  But right now it can only build multihop circuits by
first looking up the `Relay` object for the first hop in the `NetDir`.

Here we have two options: We can make bridges with known descriptors
into `Relay`s, or we can adjust `CircMgr` so that any `CircTarget` can
start a multihop circuit.

We'll also want a meaningful way to know if a bridge is in the same
family as a `Relay`, which presents its own challenges.

### Problem 3: Discarding unused channels and circuits

When a user turns bridges on and off, or changes the set of configured
bridges, we can easily have the `ChanMgr` and the `CircMgr` drop all of
their existing channels and circuits.  That will cause these channels
and circuits to close once there are no longer any streams using them,
which is all well and good.

But the user may want channels and circuits to close sooner!  People
sometimes get worried when an they flip a "anticensorship" switch and
their non-resistant channels and circuits don't close immediately.

That's a challenge in our current `ChanMgr`/`CircMgr` API, since we
don't actually keep track of the channels and circuits that we no longer
track in those managers.  We might instead need to keep weak references
to deprecated channels and circuits.  But doing _that_ might require new
`WeakChannel` and `WeakCircuit` types in `tor-proto`.

### Problem 4: Channel equivalency, bridge identity

If a bridge's configured addresses or transports are changed, then
existing channels to that bridge may no longer be used.

If a bridge has multiple transports, we might need to remember which
ones work and which ones don't.

What's more, we might not always know an Ed25519 identity for a bridge:
this will mess with our guard and channel code, both of which assume
that all known relays have an Ed25519 identity.

### Problem 5: Tuning, tuning, tuning

Our existing code has some constants and consensus values that are tuned
for the main network.  We'll need to revisit them for bridges.  Notably,
we'll need to reconsider our required guard parallelism, our recommended
guard parallelism, our willingness to retry a guard that seems not to be
working, our timeouts, our happy-eyeballs parameters, and more.


### Problem 6: Existing bridge-line format

We would like to have backward compatibility with Tor's current bridge
configuration mechanism, which uses a line format something like this:

```
[TransportId] 1.2.3.4:9100 RsaIdentity [Param1=Val1] [Param2=Val2] ...
```

We need to support this indefinitely, though it has a number of design
problems, since its usage is established basically everywhere.
Nonetheless, we may want to look into alternatives, so that we could:

  * Have more identity types
  * Make addresses optional
  * Use a type better suited for encoding binary data.


## APIs to design

These are some APIs to sketch out as next steps.

* Extended ChanTarget/CircTarget API

* Protocol or TransportId API

* Revised GuardMgr interfaces

* TransportRegistry (part of ChanMgr, knows how to connect via different
  protocols.  Takes an `ExtendedChanTarget`; returns a `Result<Channel>`)

* PtMgr (handles managed pluggable transports)

* Whatever the heck is going on inside DirMgr and between
  DirMgr/GuardMgr now.



----

[^1]: In fact, bridges are typically _not_ listed in the main network
   directory: if they were, a censor could easily block their IP addresses.

[^2]: In practice, all of our transports are implemented as extra layers
   over which we tunnel our regular cells-over-TLS protocol.  This is a
   deliberate choice: Even when the transport provides authenticity and
   and confidentiality on its own

[^3]: This is an uncommon case in C Tor, and we might not want to
   support it.