Fix postrouting rules for vpns, port forwards

This commit is contained in:
asonix 2021-05-07 11:52:58 -05:00
parent 25e8b65394
commit 750b584b8a
3 changed files with 113 additions and 47 deletions

View file

@ -158,16 +158,16 @@ pub(crate) async fn forward_postrouting(
proto: Proto,
internal_ip: Ipv4Addr,
internal_mask: u8,
internal_port: u16,
external_ip: Ipv4Addr,
external_port: u16,
destination_ip: Ipv4Addr,
) -> Result<(), anyhow::Error> {
forward_postrouting_snat(
proto,
internal_ip,
internal_mask,
internal_port,
external_ip,
external_port,
destination_ip,
|cmd| cmd.arg("-I"),
)
@ -178,16 +178,16 @@ pub(crate) async fn delete_forward_postrouting(
proto: Proto,
internal_ip: Ipv4Addr,
internal_mask: u8,
internal_port: u16,
external_ip: Ipv4Addr,
external_port: u16,
destination_ip: Ipv4Addr,
) -> Result<(), anyhow::Error> {
forward_postrouting_snat(
proto,
internal_ip,
internal_mask,
internal_port,
external_ip,
external_port,
destination_ip,
|cmd| cmd.arg("-D"),
)
@ -198,8 +198,8 @@ async fn forward_postrouting_snat(
proto: Proto,
internal_ip: Ipv4Addr,
internal_mask: u8,
internal_port: u16,
external_ip: Ipv4Addr,
external_port: u16,
destination_ip: Ipv4Addr,
func: impl Fn(&mut Command) -> &mut Command,
) -> Result<(), anyhow::Error> {
@ -215,7 +215,7 @@ async fn forward_postrouting_snat(
"-m",
proto.as_iptables_str(),
"--dport",
&external_port.to_string(),
&internal_port.to_string(),
"-m",
"conntrack",
"--ctstate",

View file

@ -109,34 +109,37 @@ pub(crate) async fn unset(interfaces: &Interfaces, rule: Rule) -> Result<(), any
dest_port,
)
.await?;
for iface in &interfaces.internal {
iptables::delete_forward_postrouting(
rule.proto,
iface.ip,
iface.mask,
interfaces.external.ip,
rule.port,
dest_ip,
)
.await?;
}
for iface in &interfaces.tunnel {
if interfaces
for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) {
let is_nat_iface = interfaces
.nats
.iter()
.any(|nat_iface| *nat_iface == iface.interface)
{
.any(|nat_iface| *nat_iface == iface.interface);
let has_nat_subnet = interfaces.nats.iter().any(|nat_iface| {
*nat_iface != iface.interface
&& interfaces
.internal
.iter()
.chain(interfaces.tunnel.iter())
.any(|other_iface| {
*nat_iface == other_iface.interface
&& other_iface.ip == iface.ip
&& other_iface.mask == iface.mask
})
});
if is_nat_iface {
iptables::delete_forward_prerouting(
rule.proto, iface.ip, iface.mask, rule.port, dest_ip, dest_port,
)
.await?;
} else {
} else if !has_nat_subnet {
iptables::delete_forward_postrouting(
rule.proto,
iface.ip,
iface.mask,
dest_port,
interfaces.external.ip,
rule.port,
dest_ip,
)
.await?;
@ -192,34 +195,37 @@ pub(crate) async fn apply(interfaces: &Interfaces, rule: Rule) -> Result<(), any
dest_port,
)
.await?;
for iface in &interfaces.internal {
iptables::forward_postrouting(
rule.proto,
iface.ip,
iface.mask,
interfaces.external.ip,
rule.port,
dest_ip,
)
.await?;
}
for iface in &interfaces.tunnel {
if interfaces
for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) {
let is_nat_iface = interfaces
.nats
.iter()
.any(|nat_iface| *nat_iface == iface.interface)
{
.any(|nat_iface| *nat_iface == iface.interface);
let has_nat_subnet = interfaces.nats.iter().any(|nat_iface| {
*nat_iface != iface.interface
&& interfaces
.internal
.iter()
.chain(interfaces.tunnel.iter())
.any(|other_iface| {
*nat_iface == other_iface.interface
&& other_iface.ip == iface.ip
&& other_iface.mask == iface.mask
})
});
if is_nat_iface {
iptables::forward_prerouting(
rule.proto, iface.ip, iface.mask, rule.port, dest_ip, dest_port,
)
.await?;
} else {
} else if !has_nat_subnet {
iptables::forward_postrouting(
rule.proto,
iface.ip,
iface.mask,
dest_port,
interfaces.external.ip,
rule.port,
dest_ip,
)
.await?;

View file

@ -198,6 +198,52 @@ fn filter(interfaces: &Interfaces) -> String {
// FORWARD: Forwarding traffic across interfaces
for nat_iface in &interfaces.nats {
if let Some(iface) = interfaces
.internal
.iter()
.chain(interfaces.tunnel.iter())
.find(|iface| iface.interface == *nat_iface)
{
filter += &format!(
"-A FORWARD -i {natif} -d {natip}/{natmask} -j ACCEPT\n",
natif = iface.interface,
natip = iface.ip,
natmask = iface.mask,
);
}
for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) {
let is_nat_iface = *nat_iface == iface.interface;
let has_nat_subnet = interfaces
.internal
.iter()
.chain(interfaces.tunnel.iter())
.any(|other_iface| {
*nat_iface == other_iface.interface
&& other_iface.ip == iface.ip
&& other_iface.mask == iface.mask
});
if !is_nat_iface && !has_nat_subnet {
// Accept packets over NAT'd tunnel interfaces from pre-existing connections
filter += &format!(
"-A FORWARD -i {natif} -o {intif} -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT\n",
natif = nat_iface,
intif = iface.interface,
);
// Reject packets over NAT'd tunnel interfaces
filter += &format!(
"-A FORWARD -i {natif} -o {intif} -j REJECT --reject-with icmp-port-unreachable\n",
natif = nat_iface,
intif = iface.interface,
);
}
}
}
// Accept packets over tunnel interfaces
for iface in &interfaces.tunnel {
filter += &format!(
@ -273,13 +319,27 @@ fn nat(interfaces: &Interfaces) -> String {
);
for nat_iface in &interfaces.nats {
for internal in &interfaces.internal {
nat += &format!(
"-A POSTROUTING -s {intip}/{intmask} -o {natiface} -j MASQUERADE\n",
intip = internal.ip,
intmask = internal.mask,
natiface = nat_iface,
);
for iface in interfaces.internal.iter().chain(interfaces.tunnel.iter()) {
let is_nat_iface = *nat_iface == iface.interface;
let has_nat_subnet = interfaces
.internal
.iter()
.chain(interfaces.tunnel.iter())
.any(|other_iface| {
*nat_iface == other_iface.interface
&& other_iface.ip == iface.ip
&& other_iface.mask == iface.mask
});
if !is_nat_iface && !has_nat_subnet {
nat += &format!(
"-A POSTROUTING -s {intip}/{intmask} -o {natiface} -j MASQUERADE\n",
intip = iface.ip,
intmask = iface.mask,
natiface = nat_iface,
);
}
}
}