Add Tor support for outbound connections via SOCKS#778
Add Tor support for outbound connections via SOCKS#778jharveyb wants to merge 1 commit intolightningdevkit:mainfrom
Conversation
|
👋 Thanks for assigning @tankyleo as a reviewer! |
|
🔔 1st Reminder Hey @tnull! This PR has been waiting for your review. |
|
@jharveyb Thanks for pointing out these quirks. From my reading, we are still onside, so I would expect everything to work on on the current main branch. For the outbound connection case, we could add an While we don't have native support for inbound tor connections yet, |
|
this is the patch I have in mind @tnull curious your thoughts. diff --git a/lightning-net-tokio/src/lib.rs b/lightning-net-tokio/src/lib.rs
index eec0e424e..104fd8595 100644
--- a/lightning-net-tokio/src/lib.rs
+++ b/lightning-net-tokio/src/lib.rs
@@ -378,12 +378,12 @@ where
/// futures are freed, though, because all processing futures are spawned with tokio::spawn, you do
/// not need to poll the provided future in order to make progress.
pub fn setup_outbound<PM: Deref + 'static + Send + Sync + Clone>(
- peer_manager: PM, their_node_id: PublicKey, stream: StdTcpStream,
+ peer_manager: PM, their_node_id: PublicKey, stream: StdTcpStream, remote_addr_override: Option<SocketAddress>,
) -> impl std::future::Future<Output = ()>
where
PM::Target: APeerManager<Descriptor = SocketDescriptor>,
{
- let remote_addr = get_addr_from_stream(&stream);
+ let remote_addr = remote_addr_override.or(get_addr_from_stream(&stream));
let (reader, mut write_receiver, read_receiver, us) = Connection::new(stream);
#[cfg(test)]
let last_us = Arc::clone(&us);
@@ -469,7 +469,7 @@ where
if let Ok(Ok(stream)) =
time::timeout(Duration::from_secs(CONNECT_OUTBOUND_TIMEOUT), connect_fut).await
{
- Some(setup_outbound(peer_manager, their_node_id, stream))
+ Some(setup_outbound(peer_manager, their_node_id, stream, None))
} else {
None
}
@@ -488,12 +488,12 @@ where
PM::Target: APeerManager<Descriptor = SocketDescriptor>,
{
let connect_fut = async {
- tor_connect(addr, tor_proxy_addr, entropy_source).await.map(|s| s.into_std().unwrap())
+ tor_connect(addr.clone(), tor_proxy_addr, entropy_source).await.map(|s| s.into_std().unwrap())
};
if let Ok(Ok(stream)) =
time::timeout(Duration::from_secs(TOR_CONNECT_OUTBOUND_TIMEOUT), connect_fut).await
{
- Some(setup_outbound(peer_manager, their_node_id, stream))
+ Some(setup_outbound(peer_manager, their_node_id, stream, Some(addr)))
} else {
None
}
@@ -1015,7 +1015,7 @@ mod tests {
// 127.0.0.1.
let (conn_a, conn_b) = make_tcp_connection();
- let fut_a = super::setup_outbound(Arc::clone(&a_manager), b_pub, conn_a);
+ let fut_a = super::setup_outbound(Arc::clone(&a_manager), b_pub, conn_a, None);
let fut_b = super::setup_inbound(b_manager, conn_b);
tokio::time::timeout(Duration::from_secs(10), a_connected.recv()).await.unwrap();
@@ -1085,7 +1085,7 @@ mod tests {
// Call connection setup inside new tokio tasks.
let manager_reference = Arc::clone(&a_manager);
tokio::spawn(async move { super::setup_inbound(manager_reference, conn_a).await });
- tokio::spawn(async move { super::setup_outbound(a_manager, b_pub, conn_b).await });
+ tokio::spawn(async move { super::setup_outbound(a_manager, b_pub, conn_b, None).await });
}
#[tokio::test(flavor = "multi_thread")] |
tnull
left a comment
There was a problem hiding this comment.
Cool, thanks for taking a look!
Already looks pretty good, but here are a few comments after the first pass. Do you think there is a good way to add test coverage for this to CI?
Hmm, could make sense, but maybe we should only expose the override field on the Tor-specific API? |
The general |
Cherry-picks the approach from upstream ldk-node PR lightningdevkit#778 but implements the SOCKS5 protocol directly in connection.rs instead of depending on lightning-net-tokio::tor_connect_outbound (which requires rust-lightning main, not available in v0.2.0). Changes: - Add tor_proxy_address field to Config - Add set_tor_proxy_address() to NodeBuilder and ArcedNodeBuilder - Add SOCKS5 connect function with Tor stream isolation (RFC 1929) - Route OnionV3 peer connections through the SOCKS5 proxy - Add base32 encoder for deriving .onion hostnames from pubkeys - Expose set_tor_proxy_address in UDL bindings Based on: lightningdevkit#778 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement SOCKS5 protocol in connection.rs to route outbound peer connections through a Tor proxy. This enables LDK nodes to connect to peers at .onion addresses. Changes: - Add tor_socks5_connect() with full SOCKS5 handshake (RFC 1928/1929) - Support Tor stream isolation via random password auth per connection - Add set_tor_proxy_address() on NodeBuilder (FFI-compatible via UDL) - Route OnionV3 addresses through SOCKS5, clearnet through direct TCP - Include base32 encoder for OnionV3 address derivation Based on the approach in upstream ldk-node PR lightningdevkit#778, but with a self-contained SOCKS5 implementation that doesn't depend on unreleased lightning_net_tokio::tor_connect_outbound(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement SOCKS5 protocol in connection.rs to route outbound peer connections through a Tor proxy. This enables LDK nodes to connect to peers at .onion addresses. Changes: - Add tor_socks5_connect() with full SOCKS5 handshake (RFC 1928/1929) - Support Tor stream isolation via random password auth per connection - Add set_tor_proxy_address() on NodeBuilder (FFI-compatible via UDL) - Route OnionV3 addresses through SOCKS5, clearnet through direct TCP - Include base32 encoder for OnionV3 address derivation Based on the approach in upstream ldk-node PR lightningdevkit#778, but with a self-contained SOCKS5 implementation that doesn't depend on unreleased lightning_net_tokio::tor_connect_outbound(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement SOCKS5 protocol in connection.rs to route outbound peer connections through a Tor proxy. This enables LDK nodes to connect to peers at .onion addresses. Changes: - Add tor_socks5_connect() with full SOCKS5 handshake (RFC 1928/1929) - Support Tor stream isolation via random password auth per connection - Add set_tor_proxy_address() on NodeBuilder (FFI-compatible via UDL) - Route OnionV3 addresses through SOCKS5, clearnet through direct TCP - Include base32 encoder for OnionV3 address derivation Based on the approach in upstream ldk-node PR lightningdevkit#778, but with a self-contained SOCKS5 implementation that doesn't depend on unreleased lightning_net_tokio::tor_connect_outbound(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement SOCKS5 protocol in connection.rs to route outbound peer connections through a Tor proxy. This enables LDK nodes to connect to peers at .onion addresses. Changes: - Add tor_socks5_connect() with full SOCKS5 handshake (RFC 1928/1929) - Support Tor stream isolation via random password auth per connection - Add set_tor_proxy_address() on NodeBuilder (FFI-compatible via UDL) - Route OnionV3 addresses through SOCKS5, clearnet through direct TCP - Include base32 encoder for OnionV3 address derivation Based on the approach in upstream ldk-node PR lightningdevkit#778, but with a self-contained SOCKS5 implementation that doesn't depend on unreleased lightning_net_tokio::tor_connect_outbound(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@jharveyb upstream PR merged here lightningdevkit/rust-lightning#4372 should fix the issues you raised above |
1ce0c38 to
fa8801d
Compare
37d30a8 to
46b2f60
Compare
Thanks for that! Updated here, added the |
46b2f60 to
728b909
Compare
| /// | ||
| /// **Note**: If unset, connecting to peer OnionV3 addresses will fail. | ||
| pub fn set_tor_config(&mut self, tor_config: TorConfig) -> Result<&mut Self, BuildError> { | ||
| match tor_config.proxy_address { |
There was a problem hiding this comment.
Maybe we should move this check to the build step so it happens also when set via Builder::from_config? Not sure if we even need this setter then?
There was a problem hiding this comment.
I'm fine with also having the check there, though I'll note that there are checks in other setters, like set_announcement_addresses() and set_listening_addresses(), that aren't mirrored in build_with_store_internal() right now.
I'd prefer to keep the setter (since I'm using the others as well), but can remove it if needed.
|
🔔 1st Reminder Hey @tankyleo! This PR has been waiting for your review. |
|
Unfortunately also needs a rebase now. |
tankyleo
left a comment
There was a problem hiding this comment.
LGTM, just would like to make even clearer to people a tor config does not mean we route IP connections via tor.
src/builder.rs
Outdated
| /// Configures the [`Node`] instance to use a Tor SOCKS proxy for some (or all) outbound connections. | ||
| /// The proxy address must not itself be an onion address. |
There was a problem hiding this comment.
I think it would be worth underscoring again here that setting this proxy only enables connecting to Onion V3 addresses; it is not used to connect to clearnet addresses.
There was a problem hiding this comment.
Thanks, forgot to update comments after dropping the logic for clearnet addresses.
src/builder.rs
Outdated
| /// Configures the [`Node`] instance to use a Tor SOCKS proxy for some (or all) outbound connections. | ||
| /// The proxy address must not itself be an onion address. |
There was a problem hiding this comment.
Likewise here, maybe something along "...to use a Tor SOCKS proxy to connect to Onion V3 addresses"?
There was a problem hiding this comment.
Using the same comment for the setter in both places; lmk if you think I should revise further!
src/config.rs
Outdated
| /// Setting [`TorConfig`] enables connecting to Tor-only peers. Please refer to [`TorConfig`] | ||
| /// for further information. |
There was a problem hiding this comment.
"Setting [TorConfig] only enables connecting to OnionV3 peers, it does not route IP connections via the tor network" ?
728b909 to
11a9d8a
Compare
|
Rebased + updated docs. |
Addresses #178 . Builds on lightningdevkit/rust-lightning#4305 .
As mentioned there, I see two drawbacks with this patch as is:
https://github.com/lightningdevkit/rust-lightning/blob/f9ad3450b7d8b722b440f0a5e3d9be8bd7a696ae/lightning-net-tokio/src/lib.rs#L332
Which affects
list_peers()output:{ "pubkey": { "pubkey": "03864ef025fde8fb587d989186ce6a4a186895ee44a926bfc370e2c366597a3f8f" }, "socket_addr": { "address": "3.33.236.230:9735" }, "is_connected": true }, { "pubkey": { "pubkey": "03fe47fdfea0f25fad0013498e8d6cec348ae3d673841ec25ee94f87c21af16ed8" }, "socket_addr": { "address": "127.0.0.1:9050" }, "is_connected": true }But more significantly, AFAICT affects our setup messages with our peer:
https://github.com/lightningdevkit/rust-lightning/blob/f9ad3450b7d8b722b440f0a5e3d9be8bd7a696ae/lightning/src/ln/peer_handler.rs#L1905
The
filter_addresses()call here means we won't include the proxy address, but we also won't include that peer's onion address, which we may want to do?I'm testing this actively now and haven't hit any issues yet, though I haven't had much traffic with that peer either.