mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
dynamic subnet in port forward
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -z "$sip" ] || [ -z "$dip" ] || [ -z "$sport" ] || [ -z "$dport" ]; then
|
||||
if [ -z "$sip" ] || [ -z "$dip" ] || [ -z "$dprefix" ] || [ -z "$sport" ] || [ -z "$dport" ]; then
|
||||
>&2 echo 'missing required env var'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NAME="F$(echo "$sip:$sport -> $dip:$dport" | sha256sum | head -c 15)"
|
||||
NAME="F$(echo "$sip:$sport -> $dip/$dprefix:$dport" | sha256sum | head -c 15)"
|
||||
|
||||
for kind in INPUT FORWARD ACCEPT; do
|
||||
if ! iptables -C $kind -j "${NAME}_${kind}" 2> /dev/null; then
|
||||
@@ -40,10 +40,10 @@ iptables -t nat -A ${NAME}_PREROUTING -d "$sip" -p udp --dport "$sport" -j DNAT
|
||||
iptables -t nat -A ${NAME}_OUTPUT -d "$sip" -p tcp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
iptables -t nat -A ${NAME}_OUTPUT -d "$sip" -p udp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
|
||||
iptables -t nat -A ${NAME}_PREROUTING -s "$dip/24" -d "$sip" -p tcp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
iptables -t nat -A ${NAME}_PREROUTING -s "$dip/24" -d "$sip" -p udp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$dip/24" -d "$dip" -p tcp --dport "$dport" -j MASQUERADE
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$dip/24" -d "$dip" -p udp --dport "$dport" -j MASQUERADE
|
||||
iptables -t nat -A ${NAME}_PREROUTING -s "$dip/$dprefix" -d "$sip" -p tcp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
iptables -t nat -A ${NAME}_PREROUTING -s "$dip/$dprefix" -d "$sip" -p udp --dport "$sport" -j DNAT --to-destination "$dip:$dport"
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$dip/$dprefix" -d "$dip" -p tcp --dport "$dport" -j MASQUERADE
|
||||
iptables -t nat -A ${NAME}_POSTROUTING -s "$dip/$dprefix" -d "$dip" -p udp --dport "$dport" -j MASQUERADE
|
||||
|
||||
iptables -A ${NAME}_FORWARD -d $dip -p tcp --dport $dport -m state --state NEW -j ACCEPT
|
||||
iptables -A ${NAME}_FORWARD -d $dip -p udp --dport $dport -m state --state NEW -j ACCEPT
|
||||
|
||||
@@ -78,6 +78,7 @@ pub fn forward_api<C: Context>() -> ParentHandler<C> {
|
||||
struct ForwardMapping {
|
||||
source: SocketAddrV4,
|
||||
target: SocketAddrV4,
|
||||
target_prefix: u8,
|
||||
rc: Weak<()>,
|
||||
}
|
||||
|
||||
@@ -91,6 +92,7 @@ impl PortForwardState {
|
||||
&mut self,
|
||||
source: SocketAddrV4,
|
||||
target: SocketAddrV4,
|
||||
target_prefix: u8,
|
||||
) -> Result<Arc<()>, Error> {
|
||||
if let Some(existing) = self.mappings.get_mut(&source) {
|
||||
if existing.target == target {
|
||||
@@ -104,18 +106,19 @@ impl PortForwardState {
|
||||
} else {
|
||||
// Different target, need to remove old and add new
|
||||
if let Some(mapping) = self.mappings.remove(&source) {
|
||||
unforward(mapping.source, mapping.target).await?;
|
||||
unforward(mapping.source, mapping.target, mapping.target_prefix).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let rc = Arc::new(());
|
||||
forward(source, target).await?;
|
||||
forward(source, target, target_prefix).await?;
|
||||
self.mappings.insert(
|
||||
source,
|
||||
ForwardMapping {
|
||||
source,
|
||||
target,
|
||||
target_prefix,
|
||||
rc: Arc::downgrade(&rc),
|
||||
},
|
||||
);
|
||||
@@ -133,7 +136,7 @@ impl PortForwardState {
|
||||
|
||||
for source in to_remove {
|
||||
if let Some(mapping) = self.mappings.remove(&source) {
|
||||
unforward(mapping.source, mapping.target).await?;
|
||||
unforward(mapping.source, mapping.target, mapping.target_prefix).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -154,7 +157,9 @@ impl Drop for PortForwardState {
|
||||
let mappings = std::mem::take(&mut self.mappings);
|
||||
tokio::spawn(async move {
|
||||
for (_, mapping) in mappings {
|
||||
unforward(mapping.source, mapping.target).await.log_err();
|
||||
unforward(mapping.source, mapping.target, mapping.target_prefix)
|
||||
.await
|
||||
.log_err();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -165,6 +170,7 @@ enum PortForwardCommand {
|
||||
AddForward {
|
||||
source: SocketAddrV4,
|
||||
target: SocketAddrV4,
|
||||
target_prefix: u8,
|
||||
respond: oneshot::Sender<Result<Arc<()>, Error>>,
|
||||
},
|
||||
Gc {
|
||||
@@ -244,9 +250,10 @@ impl PortForwardController {
|
||||
PortForwardCommand::AddForward {
|
||||
source,
|
||||
target,
|
||||
target_prefix,
|
||||
respond,
|
||||
} => {
|
||||
let result = state.add_forward(source, target).await;
|
||||
let result = state.add_forward(source, target, target_prefix).await;
|
||||
respond.send(result).ok();
|
||||
}
|
||||
PortForwardCommand::Gc { respond } => {
|
||||
@@ -270,12 +277,14 @@ impl PortForwardController {
|
||||
&self,
|
||||
source: SocketAddrV4,
|
||||
target: SocketAddrV4,
|
||||
target_prefix: u8,
|
||||
) -> Result<Arc<()>, Error> {
|
||||
let (send, recv) = oneshot::channel();
|
||||
self.req
|
||||
.send(PortForwardCommand::AddForward {
|
||||
source,
|
||||
target,
|
||||
target_prefix,
|
||||
respond: send,
|
||||
})
|
||||
.map_err(err_has_exited)?;
|
||||
@@ -305,6 +314,7 @@ impl PortForwardController {
|
||||
struct InterfaceForwardRequest {
|
||||
external: u16,
|
||||
target: SocketAddrV4,
|
||||
target_prefix: u8,
|
||||
filter: DynInterfaceFilter,
|
||||
rc: Arc<()>,
|
||||
}
|
||||
@@ -312,7 +322,7 @@ struct InterfaceForwardRequest {
|
||||
#[derive(Clone)]
|
||||
struct InterfaceForwardEntry {
|
||||
external: u16,
|
||||
filter: BTreeMap<DynInterfaceFilter, (SocketAddrV4, Weak<()>)>,
|
||||
filter: BTreeMap<DynInterfaceFilter, (SocketAddrV4, u8, Weak<()>)>,
|
||||
// Maps source SocketAddr -> strong reference for the forward created in PortForwardController
|
||||
forwards: BTreeMap<SocketAddrV4, Arc<()>>,
|
||||
}
|
||||
@@ -343,12 +353,12 @@ impl InterfaceForwardEntry {
|
||||
let mut keep = BTreeSet::<SocketAddrV4>::new();
|
||||
|
||||
for (iface, info) in ip_info.iter() {
|
||||
if let Some(target) = self
|
||||
if let Some((target, target_prefix)) = self
|
||||
.filter
|
||||
.iter()
|
||||
.filter(|(_, (_, rc))| rc.strong_count() > 0)
|
||||
.filter(|(_, (_, _, rc))| rc.strong_count() > 0)
|
||||
.find(|(filter, _)| filter.filter(iface, info))
|
||||
.map(|(_, (target, _))| *target)
|
||||
.map(|(_, (target, target_prefix, _))| (*target, *target_prefix))
|
||||
{
|
||||
if let Some(ip_info) = &info.ip_info {
|
||||
for addr in ip_info.subnets.iter().filter_map(|net| {
|
||||
@@ -360,7 +370,9 @@ impl InterfaceForwardEntry {
|
||||
}) {
|
||||
keep.insert(addr);
|
||||
if !self.forwards.contains_key(&addr) {
|
||||
let rc = port_forward.add_forward(addr, target).await?;
|
||||
let rc = port_forward
|
||||
.add_forward(addr, target, target_prefix)
|
||||
.await?;
|
||||
self.forwards.insert(addr, rc);
|
||||
}
|
||||
}
|
||||
@@ -379,6 +391,7 @@ impl InterfaceForwardEntry {
|
||||
InterfaceForwardRequest {
|
||||
external,
|
||||
target,
|
||||
target_prefix,
|
||||
filter,
|
||||
mut rc,
|
||||
}: InterfaceForwardRequest,
|
||||
@@ -395,15 +408,16 @@ impl InterfaceForwardEntry {
|
||||
let entry = self
|
||||
.filter
|
||||
.entry(filter)
|
||||
.or_insert_with(|| (target, Arc::downgrade(&rc)));
|
||||
.or_insert_with(|| (target, target_prefix, Arc::downgrade(&rc)));
|
||||
if entry.0 != target {
|
||||
entry.0 = target;
|
||||
entry.1 = Arc::downgrade(&rc);
|
||||
entry.1 = target_prefix;
|
||||
entry.2 = Arc::downgrade(&rc);
|
||||
}
|
||||
if let Some(existing) = entry.1.upgrade() {
|
||||
if let Some(existing) = entry.2.upgrade() {
|
||||
rc = existing;
|
||||
} else {
|
||||
entry.1 = Arc::downgrade(&rc);
|
||||
entry.2 = Arc::downgrade(&rc);
|
||||
}
|
||||
|
||||
self.update(ip_info, port_forward).await?;
|
||||
@@ -416,7 +430,7 @@ impl InterfaceForwardEntry {
|
||||
ip_info: &OrdMap<GatewayId, NetworkInterfaceInfo>,
|
||||
port_forward: &PortForwardController,
|
||||
) -> Result<(), Error> {
|
||||
self.filter.retain(|_, (_, rc)| rc.strong_count() > 0);
|
||||
self.filter.retain(|_, (_, _, rc)| rc.strong_count() > 0);
|
||||
|
||||
self.update(ip_info, port_forward).await
|
||||
}
|
||||
@@ -474,6 +488,7 @@ pub struct ForwardTable(pub BTreeMap<u16, ForwardTarget>);
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct ForwardTarget {
|
||||
pub target: SocketAddrV4,
|
||||
pub target_prefix: u8,
|
||||
pub filter: String,
|
||||
}
|
||||
|
||||
@@ -487,12 +502,13 @@ impl From<&InterfaceForwardState> for ForwardTable {
|
||||
entry
|
||||
.filter
|
||||
.iter()
|
||||
.filter(|(_, (_, rc))| rc.strong_count() > 0)
|
||||
.map(|(filter, (target, _))| {
|
||||
.filter(|(_, (_, _, rc))| rc.strong_count() > 0)
|
||||
.map(|(filter, (target, target_prefix, _))| {
|
||||
(
|
||||
entry.external,
|
||||
ForwardTarget {
|
||||
target: *target,
|
||||
target_prefix: *target_prefix,
|
||||
filter: format!("{:#?}", filter),
|
||||
},
|
||||
)
|
||||
@@ -573,6 +589,7 @@ impl InterfacePortForwardController {
|
||||
external: u16,
|
||||
filter: DynInterfaceFilter,
|
||||
target: SocketAddrV4,
|
||||
target_prefix: u8,
|
||||
) -> Result<Arc<()>, Error> {
|
||||
let rc = Arc::new(());
|
||||
let (send, recv) = oneshot::channel();
|
||||
@@ -581,6 +598,7 @@ impl InterfacePortForwardController {
|
||||
InterfaceForwardRequest {
|
||||
external,
|
||||
target,
|
||||
target_prefix,
|
||||
filter,
|
||||
rc,
|
||||
},
|
||||
@@ -609,10 +627,15 @@ impl InterfacePortForwardController {
|
||||
}
|
||||
}
|
||||
|
||||
async fn forward(source: SocketAddrV4, target: SocketAddrV4) -> Result<(), Error> {
|
||||
async fn forward(
|
||||
source: SocketAddrV4,
|
||||
target: SocketAddrV4,
|
||||
target_prefix: u8,
|
||||
) -> Result<(), Error> {
|
||||
Command::new("/usr/lib/startos/scripts/forward-port")
|
||||
.env("sip", source.ip().to_string())
|
||||
.env("dip", target.ip().to_string())
|
||||
.env("dprefix", target_prefix.to_string())
|
||||
.env("sport", source.port().to_string())
|
||||
.env("dport", target.port().to_string())
|
||||
.invoke(ErrorKind::Network)
|
||||
@@ -620,11 +643,16 @@ async fn forward(source: SocketAddrV4, target: SocketAddrV4) -> Result<(), Error
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn unforward(source: SocketAddrV4, target: SocketAddrV4) -> Result<(), Error> {
|
||||
async fn unforward(
|
||||
source: SocketAddrV4,
|
||||
target: SocketAddrV4,
|
||||
target_prefix: u8,
|
||||
) -> Result<(), Error> {
|
||||
Command::new("/usr/lib/startos/scripts/forward-port")
|
||||
.env("UNDO", "1")
|
||||
.env("sip", source.ip().to_string())
|
||||
.env("dip", target.ip().to_string())
|
||||
.env("dprefix", target_prefix.to_string())
|
||||
.env("sport", source.port().to_string())
|
||||
.env("dport", target.port().to_string())
|
||||
.invoke(ErrorKind::Network)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use color_eyre::eyre::eyre;
|
||||
@@ -693,7 +693,24 @@ impl NetServiceData {
|
||||
(
|
||||
internal,
|
||||
filter.clone(),
|
||||
ctrl.forward.add(external, filter, internal).await?,
|
||||
ctrl.forward
|
||||
.add(
|
||||
external,
|
||||
filter,
|
||||
internal,
|
||||
net_ifaces
|
||||
.iter()
|
||||
.find_map(|(_, i)| {
|
||||
i.ip_info.as_ref().and_then(|i| {
|
||||
i.subnets.iter().find(|i| {
|
||||
i.contains(&IpAddr::from(*internal.ip()))
|
||||
})
|
||||
})
|
||||
})
|
||||
.map(|s| s.prefix_len())
|
||||
.unwrap_or(32),
|
||||
)
|
||||
.await?,
|
||||
)
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Stdio;
|
||||
use std::sync::{Arc, LazyLock, OnceLock};
|
||||
|
||||
use clap::Parser;
|
||||
@@ -39,6 +40,7 @@ pub static CONTAINER_TOOL: LazyLock<&'static str> = LazyLock::new(|| {
|
||||
if *PREFER_DOCKER.get_or_init(|| false) {
|
||||
if std::process::Command::new("which")
|
||||
.arg("docker")
|
||||
.stdout(Stdio::null())
|
||||
.status()
|
||||
.map_or(false, |o| o.success())
|
||||
{
|
||||
|
||||
@@ -435,7 +435,22 @@ pub async fn add_forward(
|
||||
ctx: TunnelContext,
|
||||
AddPortForwardParams { source, target }: AddPortForwardParams,
|
||||
) -> Result<(), Error> {
|
||||
let rc = ctx.forward.add_forward(source, target).await?;
|
||||
let prefix = ctx
|
||||
.net_iface
|
||||
.peek(|i| {
|
||||
i.iter()
|
||||
.find_map(|(_, i)| {
|
||||
i.ip_info.as_ref().and_then(|i| {
|
||||
i.subnets
|
||||
.iter()
|
||||
.find(|s| s.contains(&IpAddr::from(*target.ip())))
|
||||
})
|
||||
})
|
||||
.cloned()
|
||||
})
|
||||
.map(|s| s.prefix_len())
|
||||
.unwrap_or(32);
|
||||
let rc = ctx.forward.add_forward(source, target, prefix).await?;
|
||||
ctx.active_forwards.mutate(|m| {
|
||||
m.insert(source, rc);
|
||||
});
|
||||
|
||||
@@ -185,7 +185,21 @@ impl TunnelContext {
|
||||
|
||||
let mut active_forwards = BTreeMap::new();
|
||||
for (from, to) in peek.as_port_forwards().de()?.0 {
|
||||
active_forwards.insert(from, forward.add_forward(from, to).await?);
|
||||
let prefix = net_iface
|
||||
.peek(|i| {
|
||||
i.iter()
|
||||
.find_map(|(_, i)| {
|
||||
i.ip_info.as_ref().and_then(|i| {
|
||||
i.subnets
|
||||
.iter()
|
||||
.find(|s| s.contains(&IpAddr::from(*to.ip())))
|
||||
})
|
||||
})
|
||||
.cloned()
|
||||
})
|
||||
.map(|s| s.prefix_len())
|
||||
.unwrap_or(32);
|
||||
active_forwards.insert(from, forward.add_forward(from, to, prefix).await?);
|
||||
}
|
||||
|
||||
Ok(Self(Arc::new(TunnelContextSeed {
|
||||
|
||||
Reference in New Issue
Block a user