use std::collections::HashSet; use anyhow::Result; use iptables; use log::*; use tokio::{ select, sync::watch, time::{self, Duration}, }; use crate::{config::RuntimeConfigFirewall, fw, messages}; pub struct FirewallActor { pub ipt: iptables::IPTables, last_ports: messages::PublicExposedPorts, refresh: Duration, rx_ports: watch::Receiver, } impl FirewallActor { pub fn new( config: Option, rxp: &watch::Receiver, ) -> Result> { match config { None => Ok(None), Some(c) => { let ctx = Self { ipt: iptables::new(false)?, last_ports: messages::PublicExposedPorts::new(), refresh: c.refresh_time, rx_ports: rxp.clone(), }; fw::setup(&ctx.ipt)?; Ok(Some(ctx)) } } } pub async fn listen(&mut self) -> Result<()> { let mut interval = time::interval(self.refresh); loop { // 1. Wait for an event let new_ports = select! { Some(ports) = self.rx_ports.recv() => Some(ports), _ = interval.tick() => None, else => return Ok(()) // Sender dropped, terminate loop. }; // 2. Update last ports if needed if let Some(p) = new_ports { self.last_ports = p; } // 3. Update firewall rules match self.do_fw_update().await { Ok(()) => debug!("Successfully updated firewall rules"), Err(e) => error!("An error occured while updating firewall rules. {}", e), } } } pub async fn do_fw_update(&self) -> Result<()> { let curr_opened_ports = fw::get_opened_ports(&self.ipt)?; let diff_tcp = self .last_ports .tcp_ports .difference(&curr_opened_ports.tcp_ports) .copied() .collect::>(); let diff_udp = self .last_ports .udp_ports .difference(&curr_opened_ports.udp_ports) .copied() .collect::>(); let ports_to_open = messages::PublicExposedPorts { tcp_ports: diff_tcp, udp_ports: diff_udp, }; fw::open_ports(&self.ipt, ports_to_open)?; return Ok(()) } }