use std::collections::HashSet;
use anyhow::Result;
use iptables;
use log::*;
use tokio::{
select,
sync::watch,
time::{
Duration,
self,
}};
use crate::config::RuntimeConfigFirewall;
use crate::fw;
use crate::messages;
pub struct FirewallActor {
pub ipt: iptables::IPTables,
last_ports: messages::PublicExposedPorts,
refresh: Duration,
rx_ports: watch::Receiver<messages::PublicExposedPorts>,
}
impl FirewallActor {
pub async fn new(config: Option<RuntimeConfigFirewall>, rxp: &watch::Receiver<messages::PublicExposedPorts>) -> Result<Option<Self>> {
if config.is_none() {
return Ok(None);
}
let config = config.unwrap();
let ctx = Self {
ipt: iptables::new(false)?,
last_ports: messages::PublicExposedPorts::new(),
refresh: config.refresh_time,
rx_ports: rxp.clone(),
};
fw::setup(&ctx.ipt)?;
return 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::<HashSet<u16>>();
let diff_udp = self.last_ports.udp_ports.difference(&curr_opened_ports.udp_ports).copied().collect::<HashSet<u16>>();
let ports_to_open = messages::PublicExposedPorts {
tcp_ports: diff_tcp,
udp_ports: diff_udp
};
fw::open_ports(&self.ipt, ports_to_open)?;
return Ok(());
}
}