support configuring multiple bind hosts; default to dual localhost.

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk 2024-06-02 22:36:00 +00:00
parent f09e0dc137
commit 1a4736d40b
2 changed files with 67 additions and 54 deletions

View file

@ -27,46 +27,43 @@ pub fn check(config: &Config) -> Result<(), Error> {
)); ));
} }
if config.address.is_loopback() && cfg!(unix) { config.get_bind_addrs().iter().for_each(|addr| {
debug!( if addr.ip().is_loopback() && cfg!(unix) {
"Found loopback listening address {}, running checks if we're in a container.", debug!("Found loopback listening address {addr}, running checks if we're in a container.",);
config.address
);
#[cfg(unix)] #[cfg(unix)]
if Path::new("/proc/vz").exists() /* Guest */ && !Path::new("/proc/bz").exists() if Path::new("/proc/vz").exists() /* Guest */ && !Path::new("/proc/bz").exists()
/* Host */ /* Host */
{ {
error!( error!(
"You are detected using OpenVZ with a loopback/localhost listening address of {}. If you are using \ "You are detected using OpenVZ with a loopback/localhost listening address of {addr}. If you are \
OpenVZ for containers and you use NAT-based networking to communicate with the host and guest, this \ using OpenVZ for containers and you use NAT-based networking to communicate with the host and \
will NOT work. Please change this to \"0.0.0.0\". If this is expected, you can ignore.", guest, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, you can \
config.address ignore.",
); );
} }
#[cfg(unix)] #[cfg(unix)]
if Path::new("/.dockerenv").exists() { if Path::new("/.dockerenv").exists() {
error!( error!(
"You are detected using Docker with a loopback/localhost listening address of {}. If you are using a \ "You are detected using Docker with a loopback/localhost listening address of {addr}. If you are \
reverse proxy on the host and require communication to conduwuit in the Docker container via \ using a reverse proxy on the host and require communication to conduwuit in the Docker container \
NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \ via NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is \
you can ignore.", expected, you can ignore.",
config.address );
); }
}
#[cfg(unix)] #[cfg(unix)]
if Path::new("/run/.containerenv").exists() { if Path::new("/run/.containerenv").exists() {
error!( error!(
"You are detected using Podman with a loopback/localhost listening address of {}. If you are using a \ "You are detected using Podman with a loopback/localhost listening address of {addr}. If you are \
reverse proxy on the host and require communication to conduwuit in the Podman container via \ using a reverse proxy on the host and require communication to conduwuit in the Podman container \
NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is expected, \ via NAT-based networking, this will NOT work. Please change this to \"0.0.0.0\". If this is \
you can ignore.", expected, you can ignore.",
config.address );
); }
} }
} });
// rocksdb does not allow max_log_files to be 0 // rocksdb does not allow max_log_files to be 0
if config.rocksdb_max_log_files == 0 && cfg!(feature = "rocksdb") { if config.rocksdb_max_log_files == 0 && cfg!(feature = "rocksdb") {

View file

@ -1,7 +1,7 @@
use std::{ use std::{
collections::BTreeMap, collections::BTreeMap,
fmt::{self, Write as _}, fmt::{self, Write as _},
net::{IpAddr, Ipv6Addr, SocketAddr}, net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
path::PathBuf, path::PathBuf,
}; };
@ -36,13 +36,20 @@ struct ListeningPort {
ports: Either<u16, Vec<u16>>, ports: Either<u16, Vec<u16>>,
} }
#[derive(Deserialize, Clone, Debug)]
#[serde(transparent)]
struct ListeningAddr {
#[serde(with = "either::serde_untagged")]
addrs: Either<IpAddr, Vec<IpAddr>>,
}
/// all the config options for conduwuit /// all the config options for conduwuit
#[derive(Clone, Debug, Deserialize)] #[derive(Clone, Debug, Deserialize)]
#[allow(clippy::struct_excessive_bools)] #[allow(clippy::struct_excessive_bools)]
pub struct Config { pub struct Config {
/// [`IpAddr`] conduwuit will listen on (can be IPv4 or IPv6) /// [`IpAddr`] conduwuit will listen on (can be IPv4 or IPv6)
#[serde(default = "default_address")] #[serde(default = "default_address")]
pub address: IpAddr, address: ListeningAddr,
/// default TCP port(s) conduwuit will listen on /// default TCP port(s) conduwuit will listen on
#[serde(default = "default_port")] #[serde(default = "default_port")]
port: ListeningPort, port: ListeningPort,
@ -471,22 +478,27 @@ impl Config {
#[must_use] #[must_use]
pub fn get_bind_addrs(&self) -> Vec<SocketAddr> { pub fn get_bind_addrs(&self) -> Vec<SocketAddr> {
match &self.port.ports { let mut addrs = Vec::new();
Left(port) => { for host in &self.get_bind_hosts() {
// Left is only 1 value, so make a vec with 1 value only for port in &self.get_bind_ports() {
let port_vec = [port]; addrs.push(SocketAddr::new(*host, *port));
}
}
port_vec addrs
.iter() }
.copied()
.map(|port| SocketAddr::from((self.address, *port))) fn get_bind_hosts(&self) -> Vec<IpAddr> {
.collect::<Vec<_>>() match &self.address.addrs {
}, Left(addr) => vec![*addr],
Right(ports) => ports Right(addrs) => addrs.clone(),
.iter() }
.copied() }
.map(|port| SocketAddr::from((self.address, port)))
.collect::<Vec<_>>(), fn get_bind_ports(&self) -> Vec<u16> {
match &self.port.ports {
Left(port) => vec![*port],
Right(ports) => ports.clone(),
} }
} }
@ -875,7 +887,11 @@ impl fmt::Display for Config {
fn true_fn() -> bool { true } fn true_fn() -> bool { true }
fn default_address() -> IpAddr { Ipv6Addr::LOCALHOST.into() } fn default_address() -> ListeningAddr {
ListeningAddr {
addrs: Right(vec![Ipv4Addr::LOCALHOST.into(), Ipv6Addr::LOCALHOST.into()]),
}
}
fn default_port() -> ListeningPort { fn default_port() -> ListeningPort {
ListeningPort { ListeningPort {