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
|
I've assigned @tnull 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 |
src/config.rs
Outdated
| /// Tor daemon SOCKS proxy address. | ||
| pub proxy_address: SocketAddress, | ||
|
|
||
| /// If set, all outbound peer connections will be made via the Tor SOCKS proxy. |
There was a problem hiding this comment.
Hmm, I do wonder if we want to expose this right now, as it could be misleading given that all other connections (like Esplora/Eleectrum syncing, RGS updates, etc) would still go over clearnet? Maybe we should make this clear in the TorConfig docs more generally, and only expose this flag once we're positive we can switch to tunnel everything over Tor?
There was a problem hiding this comment.
Mm, good point - and incoming traffic from other nodes would also still be over clearnet.
I'll remove the flag + make the comments for TorConfig more explicit + remove the flag. It can also be a follow-up if we really want it.
src/connection.rs
Outdated
| pub(crate) fn new(peer_manager: Arc<PeerManager>, logger: L) -> Self { | ||
| pub(crate) fn new( | ||
| peer_manager: Arc<PeerManager>, tor_proxy_config: Option<TorConfig>, | ||
| ephemeral_random_data: [u8; 32], logger: L, |
There was a problem hiding this comment.
Let's just have this take a KeysManager reference and rederive RandomBytes when needed?
There was a problem hiding this comment.
Sure - is there a reason that isn't done for PeerManager? out of curiousity
src/lib.rs
Outdated
| pub use builder::NodeBuilder as Builder; | ||
| use chain::ChainSource; | ||
| #[cfg(feature = "uniffi")] | ||
| use config::TorConfig; |
There was a problem hiding this comment.
Please just move this to the exports in the beginning of src/ffi/types.rs IIUC that should suffice to make uniffi happy.
46b2f60 to
728b909
Compare
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.