diff options
author | Florian Klink <flokli@flokli.de> | 2024-04-23 11:57:43 +0300 |
---|---|---|
committer | Florian Klink <flokli@flokli.de> | 2024-06-05 08:41:36 +0200 |
commit | a0f6bc5b7faa0d557179a7c4ed4c8d0facb9afa4 (patch) | |
tree | a16e5b4d951c793bbd7f6ef62a66d15e7c4605c6 /src/rpc | |
parent | a2c1de646bce4a96cf8dc526f82bd88bcf3dde70 (diff) | |
download | garage-a0f6bc5b7faa0d557179a7c4ed4c8d0facb9afa4.tar.gz garage-a0f6bc5b7faa0d557179a7c4ed4c8d0facb9afa4.zip |
add rpc_public_addr_subnet config option
In case `rpc_public_addr` is not set, but autodiscovery is used, this
allows filtering the list of automatically discovered IPs to a specific
subnet.
For example, if nodes should pick *their* IP inside a specific subnet,
but you don't want to explicitly write the IP down (as it's dynamic, or
you want to share configs across nodes), you can use this option.
Diffstat (limited to 'src/rpc')
-rw-r--r-- | src/rpc/Cargo.toml | 1 | ||||
-rw-r--r-- | src/rpc/system.rs | 41 |
2 files changed, 36 insertions, 6 deletions
diff --git a/src/rpc/Cargo.toml b/src/rpc/Cargo.toml index 43d5568e..4c8cafd9 100644 --- a/src/rpc/Cargo.toml +++ b/src/rpc/Cargo.toml @@ -24,6 +24,7 @@ bytes.workspace = true bytesize.workspace = true gethostname.workspace = true hex.workspace = true +ipnet.workspace = true tracing.workspace = true rand.workspace = true itertools.workspace = true diff --git a/src/rpc/system.rs b/src/rpc/system.rs index 0e78060b..d94d4eec 100644 --- a/src/rpc/system.rs +++ b/src/rpc/system.rs @@ -844,12 +844,20 @@ impl NodeStatus { } } -fn get_default_ip() -> Option<IpAddr> { +/// Obtain the list of currently available IP addresses on all non-loopback +/// interfaces, optionally filtering them to be inside a given IpNet. +fn get_default_ip(filter_ipnet: Option<ipnet::IpNet>) -> Option<IpAddr> { pnet_datalink::interfaces() - .iter() - .find(|e| e.is_up() && !e.is_loopback() && !e.ips.is_empty()) - .and_then(|e| e.ips.first()) - .map(|a| a.ip()) + .into_iter() + // filter down and loopback interfaces + .filter(|i| i.is_up() && !i.is_loopback()) + // get all IPs + .flat_map(|e| e.ips) + // optionally, filter to be inside filter_ipnet + .find(|ipn| { + filter_ipnet.is_some_and(|ipnet| ipnet.contains(&ipn.ip())) || filter_ipnet.is_none() + }) + .map(|ipn| ipn.ip()) } fn get_rpc_public_addr(config: &Config) -> Option<SocketAddr> { @@ -877,7 +885,28 @@ fn get_rpc_public_addr(config: &Config) -> Option<SocketAddr> { } } None => { - let addr = get_default_ip().map(|ip| SocketAddr::new(ip, config.rpc_bind_addr.port())); + // `No rpc_public_addr` specified, try to discover one, optionally filtering by `rpc_public_addr_subnet`. + let filter_subnet: Option<ipnet::IpNet> = config + .rpc_public_addr_subnet + .as_ref() + .and_then(|filter_subnet_str| match filter_subnet_str.parse::<ipnet::IpNet>() { + Ok(filter_subnet) => { + let filter_subnet_trunc = filter_subnet.trunc(); + if filter_subnet_trunc != filter_subnet { + warn!("`rpc_public_addr_subnet` changed after applying netmask, continuing with {}", filter_subnet.trunc()); + } + Some(filter_subnet_trunc) + } + Err(e) => { + panic!( + "Cannot parse rpc_public_addr_subnet {} from config file: {}. Bailing out.", + filter_subnet_str, e + ); + } + }); + + let addr = get_default_ip(filter_subnet) + .map(|ip| SocketAddr::new(ip, config.rpc_bind_addr.port())); if let Some(a) = addr { warn!("Using autodetected rpc_public_addr: {}. Consider specifying it explicitly in configuration file if possible.", a); } |