From b2b57f87948ffe44675c07ace6282386e41237ab Mon Sep 17 00:00:00 2001 From: Dmitry Dodzin Date: Thu, 7 Mar 2024 15:34:59 +0200 Subject: [PATCH] Use the same output rules for both meshed and not meshed networks (#2264) * Use the same output rules for both meshed and not meshed networks * Tidy Up * Changelog * More Tidy * Update failing test * Don't parallel iptable updates * Update Changelog --- changelog.d/2255.fixed.md | 1 + mirrord/agent/src/steal/ip_tables.rs | 13 +- mirrord/agent/src/steal/ip_tables/mesh.rs | 110 +++-------- mirrord/agent/src/steal/ip_tables/output.rs | 87 ++++++++ .../agent/src/steal/ip_tables/prerouting.rs | 186 ++++++++++++++++++ mirrord/agent/src/steal/ip_tables/redirect.rs | 184 +---------------- mirrord/agent/src/steal/ip_tables/standard.rs | 78 +++----- 7 files changed, 335 insertions(+), 324 deletions(-) create mode 100644 changelog.d/2255.fixed.md create mode 100644 mirrord/agent/src/steal/ip_tables/output.rs create mode 100644 mirrord/agent/src/steal/ip_tables/prerouting.rs diff --git a/changelog.d/2255.fixed.md b/changelog.d/2255.fixed.md new file mode 100644 index 00000000000..073861a230f --- /dev/null +++ b/changelog.d/2255.fixed.md @@ -0,0 +1 @@ +Fix incoming network interception via port-forward when "stealing" traffic with a mesh like linkerd or istio (Using the same `OUTPUT` iptable rules for both meshed and not meshed networks) diff --git a/mirrord/agent/src/steal/ip_tables.rs b/mirrord/agent/src/steal/ip_tables.rs index 7f462391a34..9078169895d 100644 --- a/mirrord/agent/src/steal/ip_tables.rs +++ b/mirrord/agent/src/steal/ip_tables.rs @@ -13,7 +13,8 @@ use crate::{ steal::ip_tables::{ flush_connections::FlushConnections, mesh::{MeshRedirect, MeshVendorExt}, - redirect::{PreroutingRedirect, Redirect}, + prerouting::PreroutingRedirect, + redirect::Redirect, standard::StandardRedirect, }, }; @@ -51,6 +52,8 @@ mod iptables { pub(crate) mod chain; pub(crate) mod flush_connections; pub(crate) mod mesh; +pub(crate) mod output; +pub(crate) mod prerouting; pub(crate) mod redirect; pub(crate) mod standard; @@ -504,9 +507,7 @@ mod tests { mock.expect_insert_rule() .with( str::starts_with("MIRRORD_OUTPUT_"), - eq( - "-o lo -m owner --uid-owner 2102 -m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420", - ), + eq("-o lo -m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420"), eq(2), ) .times(1) @@ -523,9 +524,7 @@ mod tests { mock.expect_remove_rule() .with( str::starts_with("MIRRORD_OUTPUT_"), - eq( - "-o lo -m owner --uid-owner 2102 -m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420", - ), + eq("-o lo -m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420"), ) .times(1) .returning(|_, _| Ok(())); diff --git a/mirrord/agent/src/steal/ip_tables/mesh.rs b/mirrord/agent/src/steal/ip_tables/mesh.rs index dfb48384d25..033149ebf96 100644 --- a/mirrord/agent/src/steal/ip_tables/mesh.rs +++ b/mirrord/agent/src/steal/ip_tables/mesh.rs @@ -3,22 +3,15 @@ use std::sync::{Arc, LazyLock}; use async_trait::async_trait; use fancy_regex::Regex; use mirrord_protocol::{MeshVendor, Port}; -use nix::unistd::getgid; -use tracing::warn; use crate::{ error::Result, steal::ip_tables::{ - chain::IPTableChain, - redirect::{PreroutingRedirect, Redirect}, - IPTables, IPTABLE_MESH, + output::OutputRedirect, prerouting::PreroutingRedirect, redirect::Redirect, IPTables, + IPTABLE_MESH, }, }; -/// [`Regex`] used to select the `owner` rule from the list of `iptables` rules. -static UID_LOOKUP_REGEX: LazyLock = - LazyLock::new(|| Regex::new(r"-m owner --uid-owner \d+").unwrap()); - static LINKERD_SKIP_PORTS_LOOKUP_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"-p tcp -m multiport --dports ([\d:,]+)").unwrap()); @@ -26,73 +19,45 @@ static ISTIO_SKIP_PORTS_LOOKUP_REGEX: LazyLock = LazyLock::new(|| Regex::new(r"-p tcp -m tcp --dport ([\d:,]+)").unwrap()); pub(crate) struct MeshRedirect { - preroute: PreroutingRedirect, - managed: IPTableChain, - own_packet_filter: String, + prerouteing: PreroutingRedirect, + output: OutputRedirect, } impl MeshRedirect where IPT: IPTables, { - const ENTRYPOINT: &'static str = "OUTPUT"; - pub fn create(ipt: Arc, vendor: MeshVendor) -> Result { - let preroute = PreroutingRedirect::create(ipt.clone())?; - let own_packet_filter = Self::get_own_packet_filter(&ipt, &vendor)?; + let prerouteing = PreroutingRedirect::create(ipt.clone())?; for port in Self::get_skip_ports(&ipt, &vendor)? { - preroute.add_rule(&format!("-m multiport -p tcp ! --dports {port} -j RETURN"))?; + prerouteing.add_rule(&format!("-m multiport -p tcp ! --dports {port} -j RETURN"))?; } - let managed = IPTableChain::create(ipt, IPTABLE_MESH.to_string())?; - - let gid = getgid(); - managed.add_rule(&format!("-m owner --gid-owner {gid} -p tcp -j RETURN"))?; + let output = OutputRedirect::create(ipt, IPTABLE_MESH.to_string())?; Ok(MeshRedirect { - preroute, - managed, - own_packet_filter, + prerouteing, + output, }) } - pub fn load(ipt: Arc, vendor: MeshVendor) -> Result { - let own_packet_filter = Self::get_own_packet_filter(&ipt, &vendor)?; - let preroute = PreroutingRedirect::load(ipt.clone())?; - let managed = IPTableChain::load(ipt, IPTABLE_MESH.to_string())?; + pub fn load(ipt: Arc, _vendor: MeshVendor) -> Result { + let prerouteing = PreroutingRedirect::load(ipt.clone())?; + let output = OutputRedirect::load(ipt, IPTABLE_MESH.to_string())?; Ok(MeshRedirect { - preroute, - managed, - own_packet_filter, + prerouteing, + output, }) } - fn get_own_packet_filter(ipt: &IPT, vendor: &MeshVendor) -> Result { - let chain_name = vendor.output_chain(); - - let own_packet_filter = ipt - .list_rules(chain_name)? - .iter() - .find_map(|rule| UID_LOOKUP_REGEX.find(rule).ok().flatten()) - .map(|m| format!("-o lo {}", m.as_str())) - .unwrap_or_else(|| { - warn!( - "Couldn't find --uid-owner of meshed chain {chain_name:?} falling back on \"-o lo\" rule", - ); - - "-o lo".to_owned() - }); - - Ok(own_packet_filter) - } - fn get_skip_ports(ipt: &IPT, vendor: &MeshVendor) -> Result> { + let chain_name = vendor.input_chain(); let lookup_regex = vendor.skip_ports_regex(); let skipped_ports = ipt - .list_rules(vendor.input_chain())? + .list_rules(chain_name)? .iter() .filter_map(|rule| { lookup_regex @@ -114,53 +79,37 @@ where IPT: IPTables + Send + Sync, { async fn mount_entrypoint(&self) -> Result<()> { - self.preroute.mount_entrypoint().await?; - - self.managed.inner().add_rule( - Self::ENTRYPOINT, - &format!("-j {}", self.managed.chain_name()), - )?; + self.prerouteing.mount_entrypoint().await?; + self.output.mount_entrypoint().await?; Ok(()) } async fn unmount_entrypoint(&self) -> Result<()> { - self.preroute.unmount_entrypoint().await?; - - self.managed.inner().remove_rule( - Self::ENTRYPOINT, - &format!("-j {}", self.managed.chain_name()), - )?; + self.prerouteing.unmount_entrypoint().await?; + self.output.unmount_entrypoint().await?; Ok(()) } async fn add_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()> { - self.preroute + self.prerouteing + .add_redirect(redirected_port, target_port) + .await?; + self.output .add_redirect(redirected_port, target_port) .await?; - - let redirect_rule = format!( - "{} -m tcp -p tcp --dport {redirected_port} -j REDIRECT --to-ports {target_port}", - self.own_packet_filter - ); - - self.managed.add_rule(&redirect_rule)?; Ok(()) } async fn remove_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()> { - self.preroute + self.prerouteing + .remove_redirect(redirected_port, target_port) + .await?; + self.output .remove_redirect(redirected_port, target_port) .await?; - - let redirect_rule = format!( - "{} -m tcp -p tcp --dport {redirected_port} -j REDIRECT --to-ports {target_port}", - self.own_packet_filter - ); - - self.managed.remove_rule(&redirect_rule)?; Ok(()) } @@ -214,6 +163,7 @@ impl MeshVendorExt for MeshVendor { #[cfg(test)] mod tests { use mockall::predicate::*; + use nix::unistd::getgid; use super::*; use crate::steal::ip_tables::{MockIPTables, IPTABLE_PREROUTING}; @@ -283,7 +233,7 @@ mod tests { mock.expect_insert_rule() .with( eq(IPTABLE_MESH.as_str()), - eq("-o lo -m owner --uid-owner 2102 -m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420"), + eq("-o lo -m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420"), eq(2), ) .times(1) diff --git a/mirrord/agent/src/steal/ip_tables/output.rs b/mirrord/agent/src/steal/ip_tables/output.rs new file mode 100644 index 00000000000..19ef3758753 --- /dev/null +++ b/mirrord/agent/src/steal/ip_tables/output.rs @@ -0,0 +1,87 @@ +use std::sync::Arc; + +use async_trait::async_trait; +use mirrord_protocol::Port; +use nix::unistd::getgid; +use tracing::warn; + +use crate::{ + error::Result, + steal::ip_tables::{chain::IPTableChain, IPTables, Redirect}, +}; + +pub(crate) struct OutputRedirect { + pub(crate) managed: IPTableChain, +} + +impl OutputRedirect +where + IPT: IPTables, +{ + const ENTRYPOINT: &'static str = "OUTPUT"; + + pub fn create(ipt: Arc, chain_name: String) -> Result { + let managed = IPTableChain::create(ipt, chain_name)?; + + let gid = getgid(); + managed + .add_rule(&format!("-m owner --gid-owner {gid} -p tcp -j RETURN")) + .inspect_err(|_| { + warn!("Unable to create iptable rule with \"--gid-owner {gid}\" filter") + })?; + + Ok(OutputRedirect { managed }) + } + + pub fn load(ipt: Arc, chain_name: String) -> Result { + let managed = IPTableChain::create(ipt, chain_name)?; + + Ok(OutputRedirect { managed }) + } +} + +/// This wrapper adds a new rule to the NAT OUTPUT chain to redirect "localhost" traffic as well +/// Note: OUTPUT chain is only traversed for packets produced by local applications +#[async_trait] +impl Redirect for OutputRedirect +where + IPT: IPTables + Send + Sync, +{ + async fn mount_entrypoint(&self) -> Result<()> { + self.managed.inner().add_rule( + Self::ENTRYPOINT, + &format!("-j {}", self.managed.chain_name()), + )?; + + Ok(()) + } + + async fn unmount_entrypoint(&self) -> Result<()> { + self.managed.inner().remove_rule( + Self::ENTRYPOINT, + &format!("-j {}", self.managed.chain_name()), + )?; + + Ok(()) + } + + async fn add_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()> { + let redirect_rule = format!( + "-o lo -m tcp -p tcp --dport {redirected_port} -j REDIRECT --to-ports {target_port}" + ); + + self.managed.add_rule(&redirect_rule)?; + + Ok(()) + } + + async fn remove_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()> { + let redirect_rule = format!( + "-o lo -m tcp -p tcp --dport {redirected_port} -j REDIRECT --to-ports {target_port}" + ); + + self.managed.remove_rule(&redirect_rule)?; + + Ok(()) + } +} diff --git a/mirrord/agent/src/steal/ip_tables/prerouting.rs b/mirrord/agent/src/steal/ip_tables/prerouting.rs new file mode 100644 index 00000000000..486b0ca1b51 --- /dev/null +++ b/mirrord/agent/src/steal/ip_tables/prerouting.rs @@ -0,0 +1,186 @@ +use std::{ops::Deref, sync::Arc}; + +use async_trait::async_trait; +use mirrord_protocol::Port; + +use crate::{ + error::Result, + steal::ip_tables::{chain::IPTableChain, IPTables, Redirect, IPTABLE_PREROUTING}, +}; + +pub(crate) struct PreroutingRedirect { + managed: IPTableChain, +} + +impl PreroutingRedirect +where + IPT: IPTables, +{ + const ENTRYPOINT: &'static str = "PREROUTING"; + + pub fn create(ipt: Arc) -> Result { + let managed = IPTableChain::create(ipt, IPTABLE_PREROUTING.to_string())?; + + Ok(PreroutingRedirect { managed }) + } + + pub fn load(ipt: Arc) -> Result { + let managed = IPTableChain::load(ipt, IPTABLE_PREROUTING.to_string())?; + + Ok(PreroutingRedirect { managed }) + } +} + +#[async_trait] +impl Redirect for PreroutingRedirect +where + IPT: IPTables + Send + Sync, +{ + async fn mount_entrypoint(&self) -> Result<()> { + self.managed.inner().add_rule( + Self::ENTRYPOINT, + &format!("-j {}", self.managed.chain_name()), + )?; + + Ok(()) + } + + async fn unmount_entrypoint(&self) -> Result<()> { + self.managed.inner().remove_rule( + Self::ENTRYPOINT, + &format!("-j {}", self.managed.chain_name()), + )?; + + Ok(()) + } + + async fn add_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()> { + let redirect_rule = + format!("-m tcp -p tcp --dport {redirected_port} -j REDIRECT --to-ports {target_port}"); + + self.managed.add_rule(&redirect_rule)?; + + Ok(()) + } + + async fn remove_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()> { + let redirect_rule = + format!("-m tcp -p tcp --dport {redirected_port} -j REDIRECT --to-ports {target_port}"); + + self.managed.remove_rule(&redirect_rule)?; + + Ok(()) + } +} + +impl Deref for PreroutingRedirect +where + IPT: IPTables, +{ + type Target = IPTableChain; + + fn deref(&self) -> &Self::Target { + &self.managed + } +} + +#[cfg(test)] +mod tests { + use mockall::predicate::*; + + use super::*; + use crate::steal::ip_tables::MockIPTables; + + #[tokio::test] + async fn add_redirect() { + let mut mock = MockIPTables::new(); + + mock.expect_create_chain() + .with(eq(IPTABLE_PREROUTING.as_str())) + .times(1) + .returning(|_| Ok(())); + + mock.expect_insert_rule() + .with( + eq(IPTABLE_PREROUTING.as_str()), + eq("-m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420"), + eq(1), + ) + .times(1) + .returning(|_, _, _| Ok(())); + + mock.expect_remove_chain() + .with(eq(IPTABLE_PREROUTING.as_str())) + .times(1) + .returning(|_| Ok(())); + + let prerouting = PreroutingRedirect::create(Arc::new(mock)).expect("Unable to create"); + + assert!(prerouting.add_redirect(69, 420).await.is_ok()); + } + + #[tokio::test] + async fn add_redirect_twice() { + let mut mock = MockIPTables::new(); + + mock.expect_create_chain() + .with(eq(IPTABLE_PREROUTING.as_str())) + .times(1) + .returning(|_| Ok(())); + + mock.expect_insert_rule() + .with( + eq(IPTABLE_PREROUTING.as_str()), + eq("-m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420"), + eq(1), + ) + .times(1) + .returning(|_, _, _| Ok(())); + + mock.expect_insert_rule() + .with( + eq(IPTABLE_PREROUTING.as_str()), + eq("-m tcp -p tcp --dport 169 -j REDIRECT --to-ports 1420"), + eq(2), + ) + .times(1) + .returning(|_, _, _| Ok(())); + + mock.expect_remove_chain() + .with(eq(IPTABLE_PREROUTING.as_str())) + .times(1) + .returning(|_| Ok(())); + + let prerouting = PreroutingRedirect::create(Arc::new(mock)).expect("Unable to create"); + + assert!(prerouting.add_redirect(69, 420).await.is_ok()); + assert!(prerouting.add_redirect(169, 1420).await.is_ok()); + } + + #[tokio::test] + async fn remove_redirect() { + let mut mock = MockIPTables::new(); + + mock.expect_create_chain() + .with(eq(IPTABLE_PREROUTING.as_str())) + .times(1) + .returning(|_| Ok(())); + + mock.expect_remove_rule() + .with( + eq(IPTABLE_PREROUTING.as_str()), + eq("-m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420"), + ) + .times(1) + .returning(|_, _| Ok(())); + + mock.expect_remove_chain() + .with(eq(IPTABLE_PREROUTING.as_str())) + .times(1) + .returning(|_| Ok(())); + + let prerouting = PreroutingRedirect::create(Arc::new(mock)).expect("Unable to create"); + + assert!(prerouting.remove_redirect(69, 420).await.is_ok()); + } +} diff --git a/mirrord/agent/src/steal/ip_tables/redirect.rs b/mirrord/agent/src/steal/ip_tables/redirect.rs index c7d662534f7..d18aeb1d7ea 100644 --- a/mirrord/agent/src/steal/ip_tables/redirect.rs +++ b/mirrord/agent/src/steal/ip_tables/redirect.rs @@ -1,13 +1,8 @@ -use std::{ops::Deref, sync::Arc}; - use async_trait::async_trait; use enum_dispatch::enum_dispatch; use mirrord_protocol::Port; -use crate::{ - error::Result, - steal::ip_tables::{chain::IPTableChain, IPTables, IPTABLE_PREROUTING}, -}; +use crate::error::Result; #[async_trait] #[enum_dispatch] @@ -21,180 +16,3 @@ pub(crate) trait Redirect { /// Remove port redirection async fn remove_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()>; } - -pub(crate) struct PreroutingRedirect { - managed: IPTableChain, -} - -impl PreroutingRedirect -where - IPT: IPTables, -{ - const ENTRYPOINT: &'static str = "PREROUTING"; - - pub fn create(ipt: Arc) -> Result { - let managed = IPTableChain::create(ipt, IPTABLE_PREROUTING.to_string())?; - - Ok(PreroutingRedirect { managed }) - } - - pub fn load(ipt: Arc) -> Result { - let managed = IPTableChain::load(ipt, IPTABLE_PREROUTING.to_string())?; - - Ok(PreroutingRedirect { managed }) - } -} - -#[async_trait] -impl Redirect for PreroutingRedirect -where - IPT: IPTables + Send + Sync, -{ - async fn mount_entrypoint(&self) -> Result<()> { - self.managed.inner().add_rule( - Self::ENTRYPOINT, - &format!("-j {}", self.managed.chain_name()), - )?; - - Ok(()) - } - - async fn unmount_entrypoint(&self) -> Result<()> { - self.managed.inner().remove_rule( - Self::ENTRYPOINT, - &format!("-j {}", self.managed.chain_name()), - )?; - - Ok(()) - } - - async fn add_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()> { - let redirect_rule = - format!("-m tcp -p tcp --dport {redirected_port} -j REDIRECT --to-ports {target_port}"); - - self.managed.add_rule(&redirect_rule)?; - - Ok(()) - } - - async fn remove_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()> { - let redirect_rule = - format!("-m tcp -p tcp --dport {redirected_port} -j REDIRECT --to-ports {target_port}"); - - self.managed.remove_rule(&redirect_rule)?; - - Ok(()) - } -} - -impl Deref for PreroutingRedirect -where - IPT: IPTables, -{ - type Target = IPTableChain; - - fn deref(&self) -> &Self::Target { - &self.managed - } -} - -#[cfg(test)] -mod tests { - use mockall::predicate::*; - - use super::*; - use crate::steal::ip_tables::MockIPTables; - - #[tokio::test] - async fn add_redirect() { - let mut mock = MockIPTables::new(); - - mock.expect_create_chain() - .with(eq(IPTABLE_PREROUTING.as_str())) - .times(1) - .returning(|_| Ok(())); - - mock.expect_insert_rule() - .with( - eq(IPTABLE_PREROUTING.as_str()), - eq("-m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420"), - eq(1), - ) - .times(1) - .returning(|_, _, _| Ok(())); - - mock.expect_remove_chain() - .with(eq(IPTABLE_PREROUTING.as_str())) - .times(1) - .returning(|_| Ok(())); - - let prerouting = PreroutingRedirect::create(Arc::new(mock)).expect("Unable to create"); - - assert!(prerouting.add_redirect(69, 420).await.is_ok()); - } - - #[tokio::test] - async fn add_redirect_twice() { - let mut mock = MockIPTables::new(); - - mock.expect_create_chain() - .with(eq(IPTABLE_PREROUTING.as_str())) - .times(1) - .returning(|_| Ok(())); - - mock.expect_insert_rule() - .with( - eq(IPTABLE_PREROUTING.as_str()), - eq("-m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420"), - eq(1), - ) - .times(1) - .returning(|_, _, _| Ok(())); - - mock.expect_insert_rule() - .with( - eq(IPTABLE_PREROUTING.as_str()), - eq("-m tcp -p tcp --dport 169 -j REDIRECT --to-ports 1420"), - eq(2), - ) - .times(1) - .returning(|_, _, _| Ok(())); - - mock.expect_remove_chain() - .with(eq(IPTABLE_PREROUTING.as_str())) - .times(1) - .returning(|_| Ok(())); - - let prerouting = PreroutingRedirect::create(Arc::new(mock)).expect("Unable to create"); - - assert!(prerouting.add_redirect(69, 420).await.is_ok()); - assert!(prerouting.add_redirect(169, 1420).await.is_ok()); - } - - #[tokio::test] - async fn remove_redirect() { - let mut mock = MockIPTables::new(); - - mock.expect_create_chain() - .with(eq(IPTABLE_PREROUTING.as_str())) - .times(1) - .returning(|_| Ok(())); - - mock.expect_remove_rule() - .with( - eq(IPTABLE_PREROUTING.as_str()), - eq("-m tcp -p tcp --dport 69 -j REDIRECT --to-ports 420"), - ) - .times(1) - .returning(|_, _| Ok(())); - - mock.expect_remove_chain() - .with(eq(IPTABLE_PREROUTING.as_str())) - .times(1) - .returning(|_| Ok(())); - - let prerouting = PreroutingRedirect::create(Arc::new(mock)).expect("Unable to create"); - - assert!(prerouting.remove_redirect(69, 420).await.is_ok()); - } -} diff --git a/mirrord/agent/src/steal/ip_tables/standard.rs b/mirrord/agent/src/steal/ip_tables/standard.rs index aa7d6711187..15155c2f3bb 100644 --- a/mirrord/agent/src/steal/ip_tables/standard.rs +++ b/mirrord/agent/src/steal/ip_tables/standard.rs @@ -2,56 +2,41 @@ use std::sync::Arc; use async_trait::async_trait; use mirrord_protocol::Port; -use nix::unistd::getgid; -use tracing::warn; use crate::{ error::Result, steal::ip_tables::{ - chain::IPTableChain, redirect::PreroutingRedirect, IPTables, Redirect, IPTABLE_STANDARD, + output::OutputRedirect, prerouting::PreroutingRedirect, IPTables, Redirect, + IPTABLE_STANDARD, }, }; pub(crate) struct StandardRedirect { - preroute: PreroutingRedirect, - managed: IPTableChain, - own_packet_filter: String, + prerouteing: PreroutingRedirect, + output: OutputRedirect, } impl StandardRedirect where IPT: IPTables, { - const ENTRYPOINT: &'static str = "OUTPUT"; - pub fn create(ipt: Arc) -> Result { - let preroute = PreroutingRedirect::create(ipt.clone())?; - let managed = IPTableChain::create(ipt, IPTABLE_STANDARD.to_string())?; - let own_packet_filter = "-o lo".to_owned(); - - let gid = getgid(); - managed - .add_rule(&format!("-m owner --gid-owner {gid} -p tcp -j RETURN")) - .inspect_err(|_| { - warn!("Unable to create iptable rule with \"--gid-owner {gid}\" filter") - })?; + let prerouteing = PreroutingRedirect::create(ipt.clone())?; + let output = OutputRedirect::create(ipt, IPTABLE_STANDARD.to_string())?; Ok(StandardRedirect { - preroute, - managed, - own_packet_filter, + prerouteing, + output, }) } pub fn load(ipt: Arc) -> Result { - let preroute = PreroutingRedirect::load(ipt.clone())?; - let managed = IPTableChain::create(ipt, IPTABLE_STANDARD.to_string())?; - let own_packet_filter = "-o lo".to_owned(); + let prerouteing = PreroutingRedirect::load(ipt.clone())?; + let output = OutputRedirect::load(ipt, IPTABLE_STANDARD.to_string())?; Ok(StandardRedirect { - preroute, - managed, - own_packet_filter, + prerouteing, + output, }) } } @@ -64,52 +49,37 @@ where IPT: IPTables + Send + Sync, { async fn mount_entrypoint(&self) -> Result<()> { - self.preroute.mount_entrypoint().await?; - - self.managed.inner().add_rule( - Self::ENTRYPOINT, - &format!("-j {}", self.managed.chain_name()), - )?; + self.prerouteing.mount_entrypoint().await?; + self.output.mount_entrypoint().await?; Ok(()) } async fn unmount_entrypoint(&self) -> Result<()> { - self.preroute.unmount_entrypoint().await?; - - self.managed.inner().remove_rule( - Self::ENTRYPOINT, - &format!("-j {}", self.managed.chain_name()), - )?; + self.prerouteing.unmount_entrypoint().await?; + self.output.unmount_entrypoint().await?; Ok(()) } async fn add_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()> { - self.preroute + self.prerouteing + .add_redirect(redirected_port, target_port) + .await?; + self.output .add_redirect(redirected_port, target_port) .await?; - let redirect_rule = format!( - "{} -m tcp -p tcp --dport {redirected_port} -j REDIRECT --to-ports {target_port}", - self.own_packet_filter - ); - - self.managed.add_rule(&redirect_rule)?; Ok(()) } async fn remove_redirect(&self, redirected_port: Port, target_port: Port) -> Result<()> { - self.preroute + self.prerouteing + .remove_redirect(redirected_port, target_port) + .await?; + self.output .remove_redirect(redirected_port, target_port) .await?; - - let redirect_rule = format!( - "{} -m tcp -p tcp --dport {redirected_port} -j REDIRECT --to-ports {target_port}", - self.own_packet_filter - ); - - self.managed.remove_rule(&redirect_rule)?; Ok(()) }