diff --git a/Cargo.lock b/Cargo.lock index c65dda20..3b7558a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -724,9 +724,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" dependencies = [ "atomic-waker", "bytes", diff --git a/Dockerfile b/Dockerfile index 16ba3ccb..d27a3ccb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM rust:1-slim as builder +FROM rust:1-slim AS builder RUN apt-get update && apt-get -y install protobuf-compiler libnftnl-dev libmnl-dev WORKDIR /app diff --git a/flake.lock b/flake.lock index 19484013..b227bc8e 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1746904237, - "narHash": "sha256-3e+AVBczosP5dCLQmMoMEogM57gmZ2qrVSrmq9aResQ=", + "lastModified": 1751271578, + "narHash": "sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU+tt4YY=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d89fc19e405cb2d55ce7cc114356846a0ee5e956", + "rev": "3016b4b15d13f3089db8a41ef937b13a9e33a8df", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1747190175, - "narHash": "sha256-s33mQ2s5L/2nyllhRTywgECNZyCqyF4MJeM3vG/GaRo=", + "lastModified": 1751423951, + "narHash": "sha256-AowKhJGplXRkAngSvb+32598DTiI6LOzhAnzgvbCtYM=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "58160be7abad81f6f8cb53120d5b88c16e01c06d", + "rev": "1684ed5b15859b655caf41b467d046e29a994d04", "type": "github" }, "original": { diff --git a/proto b/proto index f0c030a9..c0aef683 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit f0c030a9725a0fd055efa69b58e0d7ee42654583 +Subproject commit c0aef68395720f46a7f038b6766de3bb30e02930 diff --git a/src/enterprise/firewall/api.rs b/src/enterprise/firewall/api.rs index 67960927..9fb4c4af 100644 --- a/src/enterprise/firewall/api.rs +++ b/src/enterprise/firewall/api.rs @@ -4,7 +4,7 @@ use std::fs::{File, OpenOptions}; #[cfg(target_os = "linux")] use nftnl::Batch; -use super::{FirewallError, FirewallRule, Policy}; +use super::{FirewallError, FirewallRule, Policy, SnatBinding}; #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] const DEV_PF: &str = "/dev/pf"; @@ -42,11 +42,15 @@ pub(crate) trait FirewallManagementApi { /// Clean up the firewall rules. fn cleanup(&mut self) -> Result<(), FirewallError>; - /// Add fireall `rules`. + /// Add firewall rules. fn add_rules(&mut self, rules: Vec) -> Result<(), FirewallError>; - /// Set masquerade status. - fn set_masquerade_status(&mut self, enabled: bool) -> Result<(), FirewallError>; + /// Setup Network Address Translation using POSTROUTING chain rules + fn setup_nat( + &mut self, + masquerade_enabled: bool, + snat_bindings: &[SnatBinding], + ) -> Result<(), FirewallError>; /// Begin rule transaction. fn begin(&mut self) -> Result<(), FirewallError>; diff --git a/src/enterprise/firewall/dummy/mod.rs b/src/enterprise/firewall/dummy/mod.rs deleted file mode 100644 index 9129f91f..00000000 --- a/src/enterprise/firewall/dummy/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -use super::{ - api::{FirewallApi, FirewallManagementApi}, - FirewallError, FirewallRule, Policy, -}; - -impl FirewallManagementApi for FirewallApi { - fn setup( - &mut self, - _default_policy: Policy, - _priority: Option, - ) -> Result<(), FirewallError> { - Ok(()) - } - - fn cleanup(&mut self) -> Result<(), FirewallError> { - Ok(()) - } - - fn set_masquerade_status(&mut self, _enabled: bool) -> Result<(), FirewallError> { - Ok(()) - } - - fn add_rules(&mut self, _rules: Vec) -> Result<(), FirewallError> { - Ok(()) - } - - fn begin(&mut self) -> Result<(), FirewallError> { - Ok(()) - } - - fn commit(&mut self) -> Result<(), FirewallError> { - Ok(()) - } -} diff --git a/src/enterprise/firewall/mod.rs b/src/enterprise/firewall/mod.rs index 5f564697..532dd9f0 100644 --- a/src/enterprise/firewall/mod.rs +++ b/src/enterprise/firewall/mod.rs @@ -1,8 +1,6 @@ pub mod api; -#[cfg(test)] -mod dummy; mod iprange; -#[cfg(all(not(test), target_os = "linux"))] +#[cfg(target_os = "linux")] mod nftables; #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] mod packetfilter; @@ -204,10 +202,19 @@ pub(crate) struct FirewallRule { pub ipv4: bool, // FIXME: is that really needed? } +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct SnatBinding { + pub id: i64, + pub source_addrs: Vec
, + pub public_ip: IpAddr, + pub comment: Option, +} + #[derive(Debug, Clone, PartialEq)] pub(crate) struct FirewallConfig { pub rules: Vec, pub default_policy: Policy, + pub snat_bindings: Vec, } impl FirewallConfig { @@ -216,6 +223,7 @@ impl FirewallConfig { ) -> Result { debug!("Parsing following received firewall proto configuration: {config:?}"); let mut rules = Vec::new(); + let mut snat_bindings = Vec::new(); let default_policy = Policy::from_proto(config.default_policy.try_into().map_err(|err| { FirewallError::TypeConversionError(format!("Invalid default policy: {err:?}")) @@ -224,6 +232,7 @@ impl FirewallConfig { "Default firewall policy defined: {default_policy:?}. Proceeding to parsing rules..." ); + // parse received firewall rules for rule in config.rules { debug!("Parsing the following received Defguard ACL proto rule: {rule:?}"); let mut source_addrs = Vec::new(); @@ -287,9 +296,37 @@ impl FirewallConfig { rules.push(firewall_rule); } + // parse received SNAT bindings + for binding in config.snat_bindings { + debug!("Parsing the following received SNAT binding proto: {binding:?}"); + + let mut source_addrs = Vec::new(); + for addr in binding.source_addrs { + source_addrs.push(Address::from_proto(&addr)?); + } + + let public_ip = binding.public_ip.parse().map_err(|err| { + FirewallError::TypeConversionError(format!( + "Invalid public IP address format: {err}" + )) + })?; + + let snat_binding = SnatBinding { + id: binding.id, + source_addrs, + public_ip, + comment: binding.comment, + }; + + debug!("Parsed received proto SNAT binding as: {snat_binding:?}"); + + snat_bindings.push(snat_binding); + } + Ok(Self { rules, default_policy, + snat_bindings, }) } } diff --git a/src/enterprise/firewall/nftables/mod.rs b/src/enterprise/firewall/nftables/mod.rs index d5e5574b..a5bd53ec 100644 --- a/src/enterprise/firewall/nftables/mod.rs +++ b/src/enterprise/firewall/nftables/mod.rs @@ -4,13 +4,13 @@ use std::sync::atomic::{AtomicU32, Ordering}; use netfilter::{ allow_established_traffic, apply_filter_rules, drop_table, ignore_unrelated_traffic, - init_firewall, send_batch, set_masq, + init_firewall, send_batch, set_nat_rules, }; use nftnl::Batch; use super::{ api::{FirewallApi, FirewallManagementApi}, - Address, FirewallError, FirewallRule, Policy, Port, Protocol, + Address, FirewallError, FirewallRule, Policy, Port, Protocol, SnatBinding, }; static SET_ID_COUNTER: AtomicU32 = AtomicU32::new(0); @@ -194,27 +194,20 @@ impl FirewallManagementApi for FirewallApi { Ok(()) } - // Allows for changing the default policy of the firewall. - // fn set_firewall_default_policy(&mut self, policy: Policy) -> Result<(), FirewallError> { - // debug!("Setting default firewall policy to: {policy:?}"); - // if let Some(batch) = &mut self.batch { - // set_default_policy(policy, batch, &self.ifname)?; - // } else { - // return Err(FirewallError::TransactionNotStarted); - // } - // debug!("Set firewall default policy to {policy:?}"); - // Ok(()) - // } + fn setup_nat( + &mut self, + masquerade_enabled: bool, + snat_bindings: &[SnatBinding], + ) -> Result<(), FirewallError> { + debug!("Setting up POSTROUTING chain rules with masquerade status: {masquerade_enabled} and SNAT bindings: {snat_bindings:?}"); - /// Allows for changing the masquerade status of the firewall. - fn set_masquerade_status(&mut self, enabled: bool) -> Result<(), FirewallError> { - debug!("Setting masquerade status to: {enabled:?}"); if let Some(batch) = &mut self.batch { - set_masq(&self.ifname, enabled, batch)?; + set_nat_rules(batch, &self.ifname, masquerade_enabled, snat_bindings)?; } else { return Err(FirewallError::TransactionNotStarted); } - debug!("Set masquerade status to: {enabled:?}"); + + debug!("Finished POSTROUTING chain rules setup"); Ok(()) } diff --git a/src/enterprise/firewall/nftables/netfilter.rs b/src/enterprise/firewall/nftables/netfilter.rs index 7174d9b0..6cef7be1 100644 --- a/src/enterprise/firewall/nftables/netfilter.rs +++ b/src/enterprise/firewall/nftables/netfilter.rs @@ -9,14 +9,14 @@ use ipnetwork::IpNetwork; #[cfg(test)] use ipnetwork::{Ipv4Network, Ipv6Network}; use nftnl::{ - expr::{Expression, InterfaceName}, + expr::{Expression, Immediate, InterfaceName, Nat, NatType, Register}, nft_expr, nftnl_sys, set::{Set, SetKey}, Batch, Chain, FinalizedBatch, ProtoFamily, Rule, Table, }; use super::{get_set_id, Address, FilterRule, Policy, Port, Protocol, State}; -use crate::enterprise::firewall::{iprange::IpAddrRange, FirewallError}; +use crate::enterprise::firewall::{iprange::IpAddrRange, FirewallError, SnatBinding}; const FILTER_TABLE: &str = "filter"; const NAT_TABLE: &str = "nat"; @@ -148,6 +148,26 @@ fn add_protocol_to_set( Ok(()) } +fn add_rule_comment(rule: &mut Rule, comment: &str) -> Result<(), FirewallError> { + debug!("Adding comment to nftables expression: {comment:?}"); + // Since we are interoping with C, truncate the string to 255 *bytes* (not UTF-8 characters) + // 256 is the maximum length of a comment string in nftables, leave 1 byte for the null terminator + let maybe_truncated_str = if comment.len() > 255 { + warn!("Comment string {comment} is too long, truncating to 255 bytes"); + &comment[..=255] + } else { + comment + }; + let comment = &CString::new(maybe_truncated_str).map_err(|e| { + FirewallError::NetlinkError(format!( + "Failed to create CString from string {comment}. Error: {e:?}" + )) + })?; + rule.set_comment(comment); + debug!("Added comment to nftables expression: {comment:?}"); + Ok(()) +} + impl FirewallRule for FilterRule<'_> { fn to_chain_rule<'a>( &self, @@ -408,22 +428,7 @@ impl FirewallRule for FilterRule<'_> { // comment if let Some(comment_string) = &self.comment { - debug!("Adding comment to nftables expression: {comment_string:?}"); - // Since we are interoping with C, truncate the string to 255 *bytes* (not UTF-8 characters) - // 256 is the maximum length of a comment string in nftables, leave 1 byte for the null terminator - let maybe_truncated_str = if comment_string.len() > 255 { - warn!("Comment string {comment_string} is too long, truncating to 255 bytes"); - &comment_string[..=255] - } else { - comment_string.as_str() - }; - let comment = &CString::new(maybe_truncated_str).map_err(|e| { - FirewallError::NetlinkError(format!( - "Failed to create CString from string {comment_string}. Error: {e:?}" - )) - })?; - rule.set_comment(comment); - debug!("Added comment to nftables expression: {comment_string:?}"); + add_rule_comment(&mut rule, comment_string)?; } else { debug!("No comment provided for nftables expression"); } @@ -435,18 +440,14 @@ impl FirewallRule for FilterRule<'_> { } } -#[derive(Debug, Default)] -struct NatRule { - src_ip: Option, - dest_ip: Option, - oifname: Option, - iifname: Option, +#[derive(Debug)] +struct MasqueradeRule { + oifname: String, negated_oifname: bool, - negated_iifname: bool, counter: bool, } -impl FirewallRule for NatRule { +impl FirewallRule for MasqueradeRule { fn to_chain_rule<'a>( &self, chain: &'a Chain, @@ -454,49 +455,125 @@ impl FirewallRule for NatRule { ) -> Result, FirewallError> { let mut rule = Rule::new(chain); - if let Some(src_ip) = self.src_ip { - if src_ip.is_ipv4() { - rule.add_expr(&nft_expr!(payload ipv4 saddr)); - } else { - rule.add_expr(&nft_expr!(payload ipv6 saddr)); - } - rule.add_expr(&nft_expr!(cmp == src_ip)); + rule.add_expr(&nft_expr!(meta oifname)); + let exact = InterfaceName::Exact(CString::new(self.oifname.as_str()).unwrap()); + if self.negated_oifname { + rule.add_expr(&nft_expr!(cmp != exact)); + } else { + rule.add_expr(&nft_expr!(cmp == exact)); } - if let Some(dest_ip) = self.dest_ip { - if dest_ip.is_ipv4() { - rule.add_expr(&nft_expr!(payload ipv4 daddr)); - } else { - rule.add_expr(&nft_expr!(payload ipv6 daddr)); - } - rule.add_expr(&nft_expr!(cmp == dest_ip)); + if self.counter { + rule.add_expr(&nft_expr!(counter)); } - if let Some(iifname) = &self.iifname { - rule.add_expr(&nft_expr!(meta iifname)); - let exact = InterfaceName::Exact(CString::new(iifname.as_str()).unwrap()); - if self.negated_iifname { - rule.add_expr(&nft_expr!(cmp != exact)); + rule.add_expr(&nft_expr!(masquerade)); + + Ok(rule) + } +} + +#[derive(Debug)] +struct SnatRule<'a> { + src_ips: &'a [Address], + public_ip: &'a IpAddr, + oifname: String, + negated_oifname: bool, + counter: bool, + ipv4: bool, + comment: Option, +} + +impl FirewallRule for SnatRule<'_> { + fn to_chain_rule<'a>( + &self, + chain: &'a Chain, + batch: &mut Batch, + ) -> Result, FirewallError> { + let mut rule = Rule::new(chain); + + if !self.src_ips.is_empty() { + if self.ipv4 { + let set = new_anon_set::(chain.get_table(), ProtoFamily::Inet, true)?; + batch.add(&set, nftnl::MsgType::Add); + + for ip in self.src_ips { + add_address_to_set(set.as_ptr(), ip)?; + } + + // ip saddr {x.x.x.x, x.x.x.x} + set.elems_iter().for_each(|elem| { + batch.add(&elem, nftnl::MsgType::Add); + }); + + rule.add_expr(&nft_expr!(meta nfproto)); + rule.add_expr(&nft_expr!(cmp == libc::NFPROTO_IPV4 as u8)); + rule.add_expr(&nft_expr!(payload ipv4 saddr)); + + rule.add_expr(&nft_expr!(lookup & set)); } else { - rule.add_expr(&nft_expr!(cmp == exact)); + let set = new_anon_set::(chain.get_table(), ProtoFamily::Inet, true)?; + batch.add(&set, nftnl::MsgType::Add); + + for ip in self.src_ips { + add_address_to_set(set.as_ptr(), ip)?; + } + + // ip6 saddr {x.x.x.x, x.x.x.x} + set.elems_iter().for_each(|elem| { + batch.add(&elem, nftnl::MsgType::Add); + }); + + rule.add_expr(&nft_expr!(meta nfproto)); + rule.add_expr(&nft_expr!(cmp == libc::NFPROTO_IPV6 as u8)); + rule.add_expr(&nft_expr!(payload ipv6 saddr)); + + rule.add_expr(&nft_expr!(lookup & set)); } + debug!( + "Added source IP addresses match to nftables expression: {:?}", + self.src_ips + ); } - if let Some(oifname) = &self.oifname { - rule.add_expr(&nft_expr!(meta oifname)); - let exact = InterfaceName::Exact(CString::new(oifname.as_str()).unwrap()); - if self.negated_oifname { - rule.add_expr(&nft_expr!(cmp != exact)); - } else { - rule.add_expr(&nft_expr!(cmp == exact)); - } + rule.add_expr(&nft_expr!(meta oifname)); + let exact = InterfaceName::Exact(CString::new(self.oifname.as_str()).unwrap()); + if self.negated_oifname { + rule.add_expr(&nft_expr!(cmp != exact)); + } else { + rule.add_expr(&nft_expr!(cmp == exact)); } if self.counter { rule.add_expr(&nft_expr!(counter)); } - rule.add_expr(&nft_expr!(masquerade)); + // determine if public IP is IPv4 or IPv6 and store the address in a register + let family = match self.public_ip { + IpAddr::V4(ipv4_addr) => { + rule.add_expr(&Immediate::new(*ipv4_addr, Register::Reg1)); + ProtoFamily::Ipv4 + } + IpAddr::V6(ipv6_addr) => { + rule.add_expr(&Immediate::new(*ipv6_addr, Register::Reg1)); + ProtoFamily::Ipv6 + } + }; + let snat_expr = Nat { + nat_type: NatType::SNat, + family, + ip_register: Register::Reg1, + port_register: None, + }; + + rule.add_expr(&snat_expr); + + // comment + if let Some(comment_string) = &self.comment { + add_rule_comment(&mut rule, comment_string)?; + } else { + debug!("No comment provided for nftables expression"); + } Ok(rule) } @@ -591,35 +668,54 @@ pub(super) fn drop_chain( Ok(()) } -/// Applies masquerade on the specified interface for the outgoing packets -pub(super) fn set_masq( - ifname: &str, - enabled: bool, +/// Applies NAT rules on the specified interface for the outgoing packets +pub(super) fn set_nat_rules( batch: &mut Batch, + ifname: &str, + masquerade_enabled: bool, + snat_bindings: &[SnatBinding], ) -> Result<(), FirewallError> { + // cleanup existing POSTROUTING chain rules let table = Tables::Defguard(ProtoFamily::Inet).to_table(ifname); batch.add(&table, nftnl::MsgType::Add); drop_chain(&Chains::Postrouting, batch, ifname)?; + // initialize new POSTROUTING chain let mut nat_chain = Chains::Postrouting.to_chain(&table); nat_chain.set_hook(nftnl::Hook::PostRouting, POSTROUTING_PRIORITY); nat_chain.set_policy(nftnl::Policy::Accept); nat_chain.set_type(nftnl::ChainType::Nat); batch.add(&nat_chain, nftnl::MsgType::Add); - let nat_rule = NatRule { - oifname: Some(LOOPBACK_IFACE.to_string()), + // add SNAT bindings + for binding in snat_bindings { + let snat_rule = SnatRule { + oifname: LOOPBACK_IFACE.to_string(), + negated_oifname: true, + counter: true, + src_ips: &binding.source_addrs, + public_ip: &binding.public_ip, + ipv4: binding.public_ip.is_ipv4(), + comment: binding.comment.clone(), + } + .to_chain_rule(&nat_chain, batch)?; + + batch.add(&snat_rule, nftnl::MsgType::Add); + } + + // add MASQUERADE rule + let masquerade_rule = MasqueradeRule { + oifname: LOOPBACK_IFACE.to_string(), negated_oifname: true, counter: true, - ..Default::default() } .to_chain_rule(&nat_chain, batch)?; - if enabled { - batch.add(&nat_rule, nftnl::MsgType::Add); + if masquerade_enabled { + batch.add(&masquerade_rule, nftnl::MsgType::Add); } else { - batch.add(&nat_rule, nftnl::MsgType::Del); + batch.add(&masquerade_rule, nftnl::MsgType::Del); } Ok(()) diff --git a/src/enterprise/firewall/packetfilter/api.rs b/src/enterprise/firewall/packetfilter/api.rs index baad67cd..a3d7bcb7 100644 --- a/src/enterprise/firewall/packetfilter/api.rs +++ b/src/enterprise/firewall/packetfilter/api.rs @@ -72,8 +72,12 @@ impl FirewallManagementApi for FirewallApi { Ok(()) } - /// Set masquerade status. - fn set_masquerade_status(&mut self, _enabled: bool) -> Result<(), FirewallError> { + /// Setup Network Address Translation using POSTROUTING chain rules + fn setup_nat( + &mut self, + masquerade_enabled: bool, + snat_bindings: &[SnatBinding], + ) -> Result<(), FirewallError> { Ok(()) } diff --git a/src/enterprise/firewall/packetfilter/mod.rs b/src/enterprise/firewall/packetfilter/mod.rs index 81cccdce..6495ceb6 100644 --- a/src/enterprise/firewall/packetfilter/mod.rs +++ b/src/enterprise/firewall/packetfilter/mod.rs @@ -92,5 +92,4 @@ impl FirewallApi { } } -#[cfg(not(test))] mod api; diff --git a/src/gateway.rs b/src/gateway.rs index 039e3e9e..ce664717 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -30,7 +30,7 @@ use crate::{ config::Config, enterprise::firewall::{ api::{FirewallApi, FirewallManagementApi}, - FirewallConfig, FirewallRule, + FirewallConfig, FirewallRule, SnatBinding, }, error::GatewayError, execute_command, mask, @@ -262,7 +262,8 @@ impl Gateway { fn has_firewall_config_changed(&self, new_fw_config: &FirewallConfig) -> bool { if let Some(current_config) = &self.firewall_config { return current_config.default_policy != new_fw_config.default_policy - || self.have_firewall_rules_changed(&new_fw_config.rules); + || self.have_firewall_rules_changed(&new_fw_config.rules) + || self.have_snat_bindings_changed(&new_fw_config.snat_bindings); } true @@ -300,6 +301,38 @@ impl Gateway { } } + /// Checks whether SNAT bindings have changed. + fn have_snat_bindings_changed(&self, new_bindings: &[SnatBinding]) -> bool { + debug!("Checking if SNAT bindings have changed"); + if let Some(current_config) = &self.firewall_config { + let current_bindings = ¤t_config.snat_bindings; + if current_bindings.len() != new_bindings.len() { + debug!("Number of SNAT bindings is different, so the bindings have changed"); + return true; + } + + for binding in new_bindings { + if !current_bindings.contains(binding) { + debug!("Found a new SNAT binding: {binding:?}. Bindings have changed."); + return true; + } + } + + for binding in current_bindings { + if !new_bindings.contains(binding) { + debug!("Found a removed SNAT binding: {binding:?}. Bindings have changed."); + return true; + } + } + + debug!("SNAT bindings are the same. Bindings have not changed. My bindings: {current_bindings:?}, new bindings: {new_bindings:?}"); + false + } else { + debug!("There are new SNAT bindings in the new configuration, but we don't have any in the current one. Bindings have changed."); + true + } + } + /// Process and apply firewall configuration changes. /// - If the main config changed (default policy), reconfigure the whole firewall. /// - If only the rules changed, apply the new rules. Currently also reconfigures the whole firewall but that @@ -317,9 +350,8 @@ impl Gateway { self.firewall_api.begin()?; self.firewall_api .setup(fw_config.default_policy, self.config.fw_priority)?; - if self.config.masquerade { - self.firewall_api.set_masquerade_status(true)?; - } + self.firewall_api + .setup_nat(self.config.masquerade, &fw_config.snat_bindings)?; self.firewall_api.add_rules(fw_config.rules.clone())?; self.firewall_api.commit()?; self.firewall_config = Some(fw_config.clone()); @@ -331,9 +363,7 @@ impl Gateway { debug!("Received firewall configuration is empty, cleaning up firewall rules..."); self.firewall_api.begin()?; self.firewall_api.cleanup()?; - if self.config.masquerade { - self.firewall_api.set_masquerade_status(true)?; - } + self.firewall_api.setup_nat(self.config.masquerade, &[])?; self.firewall_api.commit()?; self.firewall_config = None; debug!("Cleaned up firewall rules"); @@ -597,7 +627,7 @@ impl Gateway { #[cfg(target_os = "linux")] if !self.config.disable_firewall_management && self.config.masquerade { self.firewall_api.begin()?; - self.firewall_api.set_masquerade_status(true)?; + self.firewall_api.setup_nat(self.config.masquerade, &[])?; self.firewall_api.commit()?; } } @@ -852,11 +882,13 @@ mod tests { let config1 = FirewallConfig { rules: vec![rule1.clone(), rule2.clone()], default_policy: Policy::Allow, + snat_bindings: vec![], }; let config_empty = FirewallConfig { rules: Vec::new(), default_policy: Policy::Allow, + snat_bindings: vec![], }; #[cfg(target_os = "macos")] @@ -916,16 +948,19 @@ mod tests { let config1 = FirewallConfig { rules: Vec::new(), default_policy: Policy::Allow, + snat_bindings: vec![], }; let config2 = FirewallConfig { rules: Vec::new(), default_policy: Policy::Deny, + snat_bindings: vec![], }; let config3 = FirewallConfig { rules: Vec::new(), default_policy: Policy::Allow, + snat_bindings: vec![], }; #[cfg(target_os = "macos")] @@ -971,6 +1006,7 @@ mod tests { ipv4: true, }], default_policy: Policy::Allow, + snat_bindings: vec![], }; gateway.firewall_config = Some(config1); assert!(gateway.has_firewall_config_changed(&config4)); @@ -988,6 +1024,7 @@ mod tests { ipv4: false, }], default_policy: Policy::Allow, + snat_bindings: vec![], }; gateway.firewall_config = Some(config4); assert!(gateway.has_firewall_config_changed(&config5));