node: Improve Tor configuration flexibility
*Breaking change* The `tor` configuration field is no longer supported. Users must remove that field from their config. Allows for a mixed mode, where regular addresses bypass the Tor proxy, while onion addresses go through it. When `onion.mode` is set to "forward", Tor connections are fowarded to the global proxy, and if it isn't set, to the OS's DNS resolution. For mixed mode, a global proxy simply isn't set, so that IP/DNS is not proxied.
This commit is contained in:
parent
cb2cbf014a
commit
ca7db1620c
|
|
@ -29,7 +29,6 @@ $ rad config
|
||||||
"db": {
|
"db": {
|
||||||
"journalMode": "rollback"
|
"journalMode": "rollback"
|
||||||
},
|
},
|
||||||
"tor": null,
|
|
||||||
"network": "main",
|
"network": "main",
|
||||||
"log": "INFO",
|
"log": "INFO",
|
||||||
"relay": "auto",
|
"relay": "auto",
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,6 @@ mod routes {
|
||||||
"connect": [],
|
"connect": [],
|
||||||
"externalAddresses": [],
|
"externalAddresses": [],
|
||||||
"db": { "journalMode": "rollback" },
|
"db": { "journalMode": "rollback" },
|
||||||
"tor": null,
|
|
||||||
"network": "main",
|
"network": "main",
|
||||||
"log": "INFO",
|
"log": "INFO",
|
||||||
"relay": "auto",
|
"relay": "auto",
|
||||||
|
|
|
||||||
|
|
@ -1232,7 +1232,7 @@ where
|
||||||
}
|
}
|
||||||
Entry::Vacant(e) => {
|
Entry::Vacant(e) => {
|
||||||
if let HostName::Ip(ip) = addr.host {
|
if let HostName::Ip(ip) = addr.host {
|
||||||
if !ip.is_loopback() && !self.config.is_proxy_ip(ip) {
|
if !address::is_local(&ip) {
|
||||||
if let Err(e) =
|
if let Err(e) =
|
||||||
self.db
|
self.db
|
||||||
.addresses_mut()
|
.addresses_mut()
|
||||||
|
|
@ -2335,8 +2335,8 @@ where
|
||||||
.map(|ka| (peer.nid, ka))
|
.map(|ka| (peer.nid, ka))
|
||||||
})
|
})
|
||||||
.filter(|(_, ka)| match AddressType::from(&ka.addr) {
|
.filter(|(_, ka)| match AddressType::from(&ka.addr) {
|
||||||
// Only consider Tor addresses if Tor is configured.
|
// Only consider onion addresses if configured.
|
||||||
AddressType::Onion => self.config.tor.is_some(),
|
AddressType::Onion => self.config.onion.is_some(),
|
||||||
AddressType::Dns | AddressType::Ipv4 | AddressType::Ipv6 => true,
|
AddressType::Dns | AddressType::Ipv4 | AddressType::Ipv6 => true,
|
||||||
})
|
})
|
||||||
.take(wanted)
|
.take(wanted)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ use netservices::{NetConnection, NetReader, NetWriter};
|
||||||
use reactor::{ResourceId, ResourceType, Timestamp};
|
use reactor::{ResourceId, ResourceType, Timestamp};
|
||||||
|
|
||||||
use radicle::collections::RandomMap;
|
use radicle::collections::RandomMap;
|
||||||
use radicle::node::config::TorConfig;
|
use radicle::node::config::AddressConfig;
|
||||||
use radicle::node::NodeId;
|
use radicle::node::NodeId;
|
||||||
use radicle::storage::WriteStorage;
|
use radicle::storage::WriteStorage;
|
||||||
|
|
||||||
|
|
@ -1071,42 +1071,37 @@ pub fn dial<G: Signer + Ecdh<Pk = NodeId>>(
|
||||||
signer: G,
|
signer: G,
|
||||||
config: &service::Config,
|
config: &service::Config,
|
||||||
) -> io::Result<WireSession<G>> {
|
) -> io::Result<WireSession<G>> {
|
||||||
let inet_addr: NetAddr<InetHost> = match config.tor {
|
let inet_addr: NetAddr<InetHost> = match &remote_addr.host {
|
||||||
// In Tor proxy mode, simply specify the proxy address for all connections,
|
HostName::Ip(ip) => NetAddr::new(InetHost::Ip(*ip), remote_addr.port),
|
||||||
// since we'll be routing all connections through the proxy.
|
HostName::Dns(dns) => NetAddr::new(InetHost::Dns(dns.clone()), remote_addr.port),
|
||||||
Some(TorConfig::Proxy { address: proxy }) => proxy.into(),
|
HostName::Tor(onion) => match config.onion {
|
||||||
// In transparent Tor mode, we treat `.onion` addresses as regular DNS names.
|
// In Tor proxy mode, simply specify the proxy address.
|
||||||
Some(TorConfig::Transparent) => {
|
Some(AddressConfig::Proxy { address }) => address.into(),
|
||||||
let host = match &remote_addr.host {
|
// In "forward" mode, if a global proxy is set, we use that, otherwise
|
||||||
HostName::Ip(ip) => InetHost::Ip(*ip),
|
// we treat `.onion` addresses as regular DNS names.
|
||||||
HostName::Dns(dns) => InetHost::Dns(dns.clone()),
|
Some(AddressConfig::Forward) => {
|
||||||
HostName::Tor(onion) => InetHost::Dns(onion.to_string()),
|
if let Some(proxy) = config.proxy {
|
||||||
_ => {
|
proxy.into()
|
||||||
return Err(io::Error::new(
|
} else {
|
||||||
io::ErrorKind::Unsupported,
|
NetAddr::new(InetHost::Dns(onion.to_string()), remote_addr.port)
|
||||||
"unsupported remote address type",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
NetAddr::new(host, remote_addr.port)
|
None => {
|
||||||
}
|
return Err(io::Error::new(
|
||||||
// Without any Tor configuration, refuse to connect to a `.onion` address.
|
io::ErrorKind::Unsupported,
|
||||||
None => {
|
"no configuration found for .onion addresses",
|
||||||
let host = match &remote_addr.host {
|
));
|
||||||
HostName::Ip(ip) => InetHost::Ip(*ip),
|
}
|
||||||
HostName::Dns(dns) => InetHost::Dns(dns.clone()),
|
},
|
||||||
_ => {
|
_ => {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::Unsupported,
|
io::ErrorKind::Unsupported,
|
||||||
"unsupported remote address type",
|
"unsupported remote address type",
|
||||||
))
|
))
|
||||||
}
|
|
||||||
};
|
|
||||||
NetAddr::new(host, remote_addr.port)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Whether to tunnel regular connections through the proxy.
|
// Whether to tunnel regular connections through the proxy.
|
||||||
let force_proxy = matches!(config.tor, Some(TorConfig::Proxy { .. }));
|
let force_proxy = config.proxy.is_some();
|
||||||
// Nb. This timeout is currently not used by the underlying library due to the
|
// Nb. This timeout is currently not used by the underlying library due to the
|
||||||
// `socket2` library not supporting non-blocking connect with timeout.
|
// `socket2` library not supporting non-blocking connect with timeout.
|
||||||
let connection = net::TcpStream::connect_nonblocking(inet_addr, DEFAULT_DIAL_TIMEOUT)?;
|
let connection = net::TcpStream::connect_nonblocking(inet_addr, DEFAULT_DIAL_TIMEOUT)?;
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use localtime::LocalDuration;
|
||||||
|
|
||||||
use crate::node;
|
use crate::node;
|
||||||
use crate::node::policy::{Policy, Scope};
|
use crate::node::policy::{Policy, Scope};
|
||||||
use crate::node::{address, db, Address, Alias, NodeId};
|
use crate::node::{db, Address, Alias, NodeId};
|
||||||
|
|
||||||
/// Target number of peers to maintain connections to.
|
/// Target number of peers to maintain connections to.
|
||||||
pub const TARGET_OUTBOUND_PEERS: usize = 8;
|
pub const TARGET_OUTBOUND_PEERS: usize = 8;
|
||||||
|
|
@ -247,17 +247,18 @@ pub enum Relay {
|
||||||
Auto,
|
Auto,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tor configuration.
|
/// Proxy configuration.
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
#[serde(rename_all = "camelCase", tag = "mode")]
|
#[serde(rename_all = "camelCase", tag = "mode")]
|
||||||
pub enum TorConfig {
|
pub enum AddressConfig {
|
||||||
/// Connect via SOCKS5 proxy.
|
/// Proxy connections to this address type.
|
||||||
Proxy {
|
Proxy {
|
||||||
/// Tor proxy address.
|
/// Proxy address.
|
||||||
address: net::SocketAddr,
|
address: net::SocketAddr,
|
||||||
},
|
},
|
||||||
/// Treat Tor onion addresses as DNS names.
|
/// Forward address to the next layer. Either this is the global proxy,
|
||||||
Transparent,
|
/// or the operating system, via DNS.
|
||||||
|
Forward,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Database configuration.
|
/// Database configuration.
|
||||||
|
|
@ -290,9 +291,12 @@ pub struct Config {
|
||||||
/// Database config.
|
/// Database config.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub db: DbConfig,
|
pub db: DbConfig,
|
||||||
/// Tor configuration.
|
/// Global proxy.
|
||||||
#[serde(default)]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub tor: Option<TorConfig>,
|
pub proxy: Option<net::SocketAddr>,
|
||||||
|
/// Onion address config.
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
pub onion: Option<AddressConfig>,
|
||||||
/// Peer-to-peer network.
|
/// Peer-to-peer network.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub network: Network,
|
pub network: Network,
|
||||||
|
|
@ -334,7 +338,8 @@ impl Config {
|
||||||
external_addresses: vec![],
|
external_addresses: vec![],
|
||||||
db: DbConfig::default(),
|
db: DbConfig::default(),
|
||||||
network: Network::default(),
|
network: Network::default(),
|
||||||
tor: None,
|
proxy: None,
|
||||||
|
onion: None,
|
||||||
relay: Relay::default(),
|
relay: Relay::default(),
|
||||||
limits: Limits::default(),
|
limits: Limits::default(),
|
||||||
workers: DEFAULT_WORKERS,
|
workers: DEFAULT_WORKERS,
|
||||||
|
|
@ -359,15 +364,6 @@ impl Config {
|
||||||
self.peer(id).is_some()
|
self.peer(id).is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the given IP address is our proxy.
|
|
||||||
pub fn is_proxy_ip(&self, ip: net::IpAddr) -> bool {
|
|
||||||
match self.tor {
|
|
||||||
None => false,
|
|
||||||
Some(TorConfig::Proxy { address }) => address.ip() == ip,
|
|
||||||
Some(TorConfig::Transparent) => address::is_local(&ip),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Are we a relay node? This determines what we do with gossip messages from other peers.
|
/// Are we a relay node? This determines what we do with gossip messages from other peers.
|
||||||
pub fn is_relay(&self) -> bool {
|
pub fn is_relay(&self) -> bool {
|
||||||
match self.relay {
|
match self.relay {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue