What changed, and why it matters
This commit simply removes a previously added feature: support for Unix domain sockets. It reverts the code back to using only standard TCP network sockets. There is no indication in the commit itself that this is a security fix, and the change does not appear to introduce or remove any vulnerability. It is a feature rollback.
No security action required. Treat as a normal feature reversion. If Unix socket support is operationally needed, track the project's issue tracker or release notes for a future re-implementation.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit reverts an earlier change that added Unix domain socket (UDS) support via the abstract_socket crate. It removes the abstract_socket dependency, deletes the ElectrumAddr wrapper type and public_addr configuration option, and switches socket handling in src/daemon.rs, src/electrum.rs, src/metrics.rs, src/p2p.rs, and src/server.rs back to std::net::SocketAddr, TcpListener, TcpStream, and standard tiny_http HTTP serving. The documentation section on Unix sockets is also removed. No security-relevant bug is described or visible in the diff.
Changed components
Cargo.toml dependency listdoc/config.mdinternal/config_specification.tomlsrc/config.rssrc/daemon.rssrc/electrum.rssrc/metrics.rssrc/p2p.rssrc/server.rsInspect captured patch +29 / −184
diff --git a/Cargo.lock b/Cargo.lock
index f052edf..3eb8b5f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,15 +2,6 @@
# It is not intended for manual editing.
version = 4
-[[package]]
-name = "abstract_socket"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a070310b2037a33c2e4cbf89718ef7169fb0a031757b7b3c46f9c15321517dd9"
-dependencies = [
- "libc",
-]
-
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -417,7 +408,6 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
name = "electrs"
version = "0.11.1"
dependencies = [
- "abstract_socket",
"anyhow",
"bitcoin",
"bitcoin-test-data",
diff --git a/Cargo.toml b/Cargo.toml
index f14a8c9..1090c4d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,7 +22,6 @@ metrics_process = ["prometheus/process"]
spec = "internal/config_specification.toml"
[dependencies]
-abstract_socket = "0.1"
anyhow = "1.0"
bitcoin = { version = "0.32.8", features = ["serde", "rand-std"] }
bitcoin_slices = { version = "0.11.0", features = ["bitcoin", "sha2"] }
diff --git a/doc/config.md b/doc/config.md
index 39ec18f..3784b20 100644
--- a/doc/config.md
+++ b/doc/config.md
@@ -3,8 +3,6 @@
This applies only if you do **not** use some other automated systems such as Debian packages.
If you use automated systems, refer to their documentation first!
-Note that all instances of socket addresses support Unix domain sockets, read about them below.
-
### Bitcoind configuration
Pruning must be turned **off** for `electrs` to work.
@@ -84,20 +82,6 @@ You would need to either use a webserver to provide SSL (see _SSL connection_ be
Electrs will listen by default on `127.0.0.1:50001`, which means it will only serve clients in the local machine. This is configured via the `electrum_rpc_addr` setting and if you wish to connect from another machine, you need to change it to `0.0.0.0:50001`. This is less secure though, and the recommended way to access Electrs remotely is to keep listening on `127.0.0.1` and tunnel to your server.
-## Unix domain sockets
-
-Electrs supports binding and connecting to Unix domain sockets which can provide improved speed and security. However not all other services support this now. Still, using it is recommended if you can.
-
-Aside from avoiding the overhead of OS having to handle TCP packets, Unix sockets can provide improved access control and authentication using filesystem permissions. For instance, if you're running electrs as `electrs` user, an electrum client as `electrum` user and an untrusted software as `untrusted` user then with TCP the `untrusted` software, should it become malicious, can attack the connection between the Electrum client and electrs by binding the address before electrs has the chance to bind it. A Unix socket bound in a directory only writable by the `electrs` user makes this impossible. Further, if you're worried about DoS attacks, you can restrict the socket permissions to trusted clients only.
-
-This feature is currently mainly implemented for use with SSH-tunnelled clients/servers but it should start working right away when other software adds support. Here's a list of known software that might start supporting Unix sockets:
-
-* `bitcoind` has several issues and PRs concerning Unix sockets
-* `prometheus` has an issue requesting Unix sockets open
-* Briefly discussed in an Electrum issue with positive attitude
-
-There is also [a library that can translate between the sockets using the `LD_PRELOAD` hack](https://github.com/kohlschutter/unsock), if you want to try forcing software to use the Unix socket before it's supported natively.
-
## Extra configuration suggestions
### SSL connection
diff --git a/internal/config_specification.toml b/internal/config_specification.toml
index 7b7dc7e..f4ae65e 100644
--- a/internal/config_specification.toml
+++ b/internal/config_specification.toml
@@ -142,11 +142,6 @@ type = "String"
doc = "The banner to be shown in the Electrum console"
default = "concat!(\"Welcome to electrs \", env!(\"CARGO_PKG_VERSION\"), \" (Electrum Rust Server)!\").to_owned()"
-[[param]]
-name = "public_addr"
-type = "crate::config::ElectrumAddr"
-doc = "Sets the publicly advertised value of the server address. By default the electrum_rpc_addr is used; this setting can be used to correct it if the port is remapped by NAT or if the socket is tunnelled to a machine with a different address. This may be also used when Electrum RPC is bound to Unix domain socket. The format is the standard Electrum connection string host:port:connection where connection is either t for TCP or s for TLS."
-
[[param]]
name = "log_filters"
type = "String"
diff --git a/src/config.rs b/src/config.rs
index e7b1277..b844dda 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,4 +1,3 @@
-use abstract_socket::{SocketAddr, ToSocketAddrs};
use bitcoin::p2p::Magic;
use bitcoin::Network;
use bitcoincore_rpc::Auth;
@@ -6,6 +5,8 @@ use dirs_next::home_dir;
use std::ffi::{OsStr, OsString};
use std::fmt;
+use std::net::SocketAddr;
+use std::net::ToSocketAddrs;
use std::path::PathBuf;
use std::str::FromStr;
@@ -87,77 +88,6 @@ impl ResolvAddr {
}
}
-/// Electrum address
-///
-/// This is parsed the same as Electrum connection string but used in public advert instead.
-#[derive(Debug, Clone, Deserialize)]
-#[serde(try_from = "String")]
-pub struct ElectrumAddr {
- pub host: String,
- pub port: u16,
- pub is_tls: bool,
-}
-
-impl ElectrumAddr {
- fn from_tcp(addr: &SocketAddr) -> Option<Self> {
- match addr {
- SocketAddr::Net(addr) => {
- let host = addr.ip().to_string();
- let addr = ElectrumAddr {
- host,
- port: addr.port(),
- is_tls: false,
- };
- Some(addr)
- }
- _ => None,
- }
- }
-}
-
-impl TryFrom<String> for ElectrumAddr {
- type Error = anyhow::Error;
-
- fn try_from(string: String) -> Result<Self, Self::Error> {
- string.parse()
- }
-}
-
-impl std::str::FromStr for ElectrumAddr {
- type Err = anyhow::Error;
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- use anyhow::Context;
-
- let (host_port, conn_type) = s
- .rsplit_once(':')
- .ok_or_else(|| anyhow::anyhow!("missing colons in the Electrum address"))?;
- let is_tls = match conn_type {
- "t" => false,
- "s" => true,
- invalid => anyhow::bail!("invalid connection type {}", invalid),
- };
- let (host, port) = host_port
- .rsplit_once(':')
- .ok_or_else(|| anyhow::anyhow!("the Electrum address contains only one colon"))?;
- let port = port
- .parse()
- .context("cannot parse the port of Electrum address")?;
-
- Ok(ElectrumAddr {
- host: host.to_owned(),
- port,
- is_tls,
- })
- }
-}
-
-impl ::configure_me::parse_arg::ParseArgFromStr for ElectrumAddr {
- fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
- write!(writer, "an Electrum network address in the form host:port:connection_type where connection_type is either t for TCP or s for TLS")
- }
-}
-
/// This newtype implements `ParseArg` for `Network`.
#[derive(Deserialize)]
pub struct BitcoinNetwork(Network);
@@ -216,7 +146,6 @@ pub struct Config {
pub disable_electrum_rpc: bool,
pub server_banner: String,
pub magic: Magic,
- pub public_addr: Option<ElectrumAddr>,
}
pub struct SensitiveAuth(pub Auth);
@@ -408,10 +337,6 @@ impl Config {
std::process::exit(0);
}
- let public_addr = config
- .public_addr
- .or(ElectrumAddr::from_tcp(&electrum_rpc_addr));
-
let config = Config {
network: config.network,
db_path: config.db_dir,
@@ -434,7 +359,6 @@ impl Config {
disable_electrum_rpc: config.disable_electrum_rpc,
server_banner: config.server_banner,
magic,
- public_addr,
};
eprintln!(
"Starting electrs {} on {} {} with {:?}",
diff --git a/src/daemon.rs b/src/daemon.rs
index 20eb959..40df169 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -140,7 +140,7 @@ impl Daemon {
}
let p2p = Mutex::new(Connection::connect(
- config.daemon_p2p_addr.clone(),
+ config.daemon_p2p_addr,
metrics,
config.magic,
)?);
diff --git a/src/electrum.rs b/src/electrum.rs
index de25e88..e6ef9c6 100644
--- a/src/electrum.rs
+++ b/src/electrum.rs
@@ -13,6 +13,7 @@ use serde_json::{self, json, Value};
use std::collections::{hash_map::Entry, HashMap};
use std::fmt;
use std::iter::FromIterator;
+use std::net::SocketAddr;
use std::str::FromStr;
use crate::{
@@ -150,7 +151,7 @@ pub struct Rpc {
daemon: Daemon,
signal: Signal,
banner: String,
- addr: Option<crate::config::ElectrumAddr>,
+ addr: SocketAddr,
}
impl Rpc {
@@ -174,7 +175,7 @@ impl Rpc {
daemon,
signal,
banner: config.server_banner.clone(),
- addr: config.public_addr.clone(),
+ addr: config.electrum_rpc_addr,
})
}
@@ -505,23 +506,13 @@ impl Rpc {
}
fn features(&self) -> Result<Value> {
- let hosts = if let Some(addr) = &self.addr {
- if addr.is_tls {
- json!({ &addr.host: {
- "ssl_port": addr.port
- }})
- } else {
- json!({ &addr.host: {
- "tcp_port": addr.port
- }})
- }
- } else {
- json!({})
- };
-
Ok(json!({
"genesis_hash": self.tracker.chain().get_block_hash(0),
- "hosts": hosts,
+ "hosts": {
+ self.addr.ip().to_string(): {
+ "tcp_port": self.addr.port()
+ }
+ },
"protocol_max": PROTOCOL_VERSION,
"protocol_min": PROTOCOL_VERSION,
"pruning": null,
diff --git a/src/metrics.rs b/src/metrics.rs
index c9916f7..9d6ef8b 100644
--- a/src/metrics.rs
+++ b/src/metrics.rs
@@ -1,7 +1,5 @@
#[cfg(feature = "metrics")]
mod metrics_impl {
- use abstract_socket::SocketAddr;
-
use anyhow::{Context, Result};
#[cfg(feature = "metrics_process")]
@@ -10,6 +8,8 @@ mod metrics_impl {
use prometheus::{self, Encoder, HistogramOpts, HistogramVec, Registry, TEXT_FORMAT};
use tiny_http::{Header as HttpHeader, Response, Server};
+ use std::net::SocketAddr;
+
use crate::thread::spawn;
pub struct Metrics {
@@ -17,11 +17,7 @@ mod metrics_impl {
}
impl Metrics {
- pub fn new(addr: &SocketAddr) -> Result<Self> {
- use std::net::TcpListener;
- #[cfg(target_family = "unix")]
- use std::os::unix::net::UnixListener;
-
+ pub fn new(addr: SocketAddr) -> Result<Self> {
let reg = Registry::new();
#[cfg(feature = "metrics_process")]
@@ -31,14 +27,7 @@ mod metrics_impl {
let result = Self { reg };
let reg = result.reg.clone();
- let listener: tiny_http::Listener = match addr {
- SocketAddr::Net(addr) => TcpListener::bind(addr).map(Into::into),
- #[cfg(target_family = "unix")]
- SocketAddr::Uds(ref addr) => UnixListener::bind_addr(addr).map(Into::into),
- }
- .with_context(|| format!("failed to bind address {}", addr))?;
-
- let server = match Server::from_listener(listener, None) {
+ let server = match Server::http(addr) {
Ok(server) => server,
Err(err) => bail!("failed to start HTTP server on {}: {}", addr, err),
};
@@ -128,12 +117,12 @@ pub use metrics_impl::{Gauge, Histogram, Metrics};
mod metrics_fake {
use anyhow::Result;
- use abstract_socket::SocketAddr;
+ use std::net::SocketAddr;
pub struct Metrics {}
impl Metrics {
- pub fn new(_addr: &SocketAddr) -> Result<Self> {
+ pub fn new(_addr: SocketAddr) -> Result<Self> {
debug!("metrics collection is disabled");
Ok(Self {})
}
diff --git a/src/p2p.rs b/src/p2p.rs
index 3786f52..0787f36 100644
--- a/src/p2p.rs
+++ b/src/p2p.rs
@@ -1,4 +1,3 @@
-use abstract_socket::{SocketAddr, Stream};
use anyhow::{Context, Result};
use bitcoin::blockdata::block::Header as BlockHeader;
use bitcoin::consensus::Encodable;
@@ -22,7 +21,7 @@ use bitcoin_slices::{bsl, Parse};
use crossbeam_channel::{bounded, select, Receiver, Sender};
use std::io::Write;
-use std::net::{IpAddr, Ipv4Addr};
+use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use crate::types::SerBlock;
@@ -137,7 +136,7 @@ impl Connection {
}
pub(crate) fn connect(address: SocketAddr, metrics: &Metrics, magic: Magic) -> Result<Self> {
- let recv_conn = Stream::connect(&address)
+ let recv_conn = TcpStream::connect(address)
.with_context(|| format!("p2p failed to connect: {:?}", address))?;
let mut send_conn = recv_conn
.try_clone()
@@ -317,7 +316,7 @@ impl Connection {
}
fn build_version_message() -> NetworkMessage {
- let addr = (IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0).into();
+ let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0);
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time error")
diff --git a/src/server.rs b/src/server.rs
index e2eb27f..f50662f 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -1,4 +1,3 @@
-use abstract_socket::{Listener, Stream};
use anyhow::{Context, Result};
use crossbeam_channel::{select, unbounded, Sender};
use rayon::prelude::*;
@@ -7,7 +6,7 @@ use std::{
collections::hash_map::HashMap,
io::{BufRead, BufReader, Write},
iter::once,
- net::Shutdown,
+ net::{Shutdown, TcpListener, TcpStream},
};
use crate::{
@@ -21,11 +20,11 @@ use crate::{
struct Peer {
id: usize,
client: Client,
- stream: Stream,
+ stream: TcpStream,
}
impl Peer {
- fn new(id: usize, stream: Stream) -> Self {
+ fn new(id: usize, stream: TcpStream) -> Self {
let client = Client::default();
Self { id, client, stream }
}
@@ -63,38 +62,13 @@ pub fn run() -> Result<()> {
fn serve() -> Result<()> {
let config = Config::from_args();
- let metrics = Metrics::new(&config.monitoring_addr)?;
+ let metrics = Metrics::new(config.monitoring_addr)?;
let (server_tx, server_rx) = unbounded();
if !config.disable_electrum_rpc {
- let listener = Listener::bind(&config.electrum_rpc_addr)?;
+ let listener = TcpListener::bind(config.electrum_rpc_addr)?;
info!("serving Electrum RPC on {}", listener.local_addr()?);
- let electrum_rpc_addr = config.electrum_rpc_addr.clone();
- spawn("accept_loop", move || {
- let result = accept_loop(listener, server_tx);
- // The loop is actually supposed to be infinite so `Ok` is not OK.
- if result.is_ok() {
- error!("The `Incoming` iterator ended unexpectedly");
- }
- #[cfg(unix)]
- if let abstract_socket::SocketAddr::Uds(uds_addr) = electrum_rpc_addr {
- if let Some(path) = uds_addr.as_pathname() {
- match std::fs::remove_file(path) {
- // Not found means something else removed the socket already, so it is not
- // an error because we got the desired outcome but it's still concerning
- // that something is messing with our socket.
- Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
- warn!("The socket file was removed by something else, this may indicate broken setup");
- }
- Ok(()) => (),
- Err(error) => {
- error!("Failed to remove the socket: {}", error);
- }
- }
- }
- }
- result
- }); // detach accepting thread
+ spawn("accept_loop", || accept_loop(listener, server_tx)); // detach accepting thread
};
let server_batch_size = metrics.histogram_vec(
@@ -184,7 +158,7 @@ struct Event {
}
enum Message {
- New(Stream),
+ New(TcpStream),
Request(String),
Done,
}
@@ -235,7 +209,7 @@ fn handle_peer_events(
}
}
-fn accept_loop(listener: Listener, server_tx: Sender<Event>) -> Result<()> {
+fn accept_loop(listener: TcpListener, server_tx: Sender<Event>) -> Result<()> {
for (peer_id, conn) in listener.incoming().enumerate() {
let stream = conn.context("failed to accept")?;
let tx = server_tx.clone();
@@ -250,7 +224,7 @@ fn accept_loop(listener: Listener, server_tx: Sender<Event>) -> Result<()> {
Ok(())
}
-fn recv_loop(peer_id: usize, stream: &Stream, server_tx: Sender<Event>) -> Result<()> {
+fn recv_loop(peer_id: usize, stream: &TcpStream, server_tx: Sender<Event>) -> Result<()> {
let msg = Message::New(stream.try_clone()?);
server_tx.send(Event { peer_id, msg })?;
Why this scored 12/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.