Skip to content

Commit

Permalink
Netavark: nftables support
Browse files Browse the repository at this point in the history
Adds an nftables firewall backend and tests for said backend.
Implements basic forwarding, port forwarding, and teardown for
all relevant rules. Heavily based on our existing iptables driver
but with a number of improvements (we live in a dedicated table,
so this should play much more nicely with other tools using the
firewall; IPv4 and IPv6 share a table and almost all code; and
rule structure is a bit simpler because we do have our own table
and don't have to worry about cluttering up the FORWARD chain,
we'll the the only ones using it.

This implementation presently does not support isolation; that
will be added in a followon.

Fixes #816

Signed-off-by: Matthew Heon <[email protected]>
  • Loading branch information
mheon committed Jan 8, 2024
1 parent 86ce937 commit c19a317
Show file tree
Hide file tree
Showing 8 changed files with 2,172 additions and 69 deletions.
44 changes: 44 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ sha2 = "0.10.8"
netlink-packet-utils = "0.5.2"
netlink-packet-route = "0.18.1"
netlink-packet-core = "0.7.0"
nftables = "0.2.4"
fs2 = "0.4.3"
netlink-sys = "0.8.5"
tokio = { version = "1.35", features = ["rt", "rt-multi-thread", "signal", "fs"] }
Expand Down
26 changes: 26 additions & 0 deletions src/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ pub enum NetavarkError {
DHCPProxy(tonic::Status),

List(NetavarkErrorList),

Nftables(nftables::helper::NftablesError),

SubnetParse(ipnet::AddrParseError),
AddrParse(std::net::AddrParseError),
}

/// Internal struct for JSON output
Expand Down Expand Up @@ -160,6 +165,9 @@ impl fmt::Display for NetavarkError {
Ok(())
}
}
NetavarkError::Nftables(e) => write!(f, "nftables error: {e}"),
NetavarkError::SubnetParse(e) => write!(f, "parsing IP subnet error: {e}"),
NetavarkError::AddrParse(e) => write!(f, "parsing IP address error: {e}"),
}
}
}
Expand Down Expand Up @@ -213,3 +221,21 @@ impl From<tonic::Status> for NetavarkError {
NetavarkError::DHCPProxy(err)
}
}

impl From<nftables::helper::NftablesError> for NetavarkError {
fn from(err: nftables::helper::NftablesError) -> Self {
NetavarkError::Nftables(err)
}
}

impl From<ipnet::AddrParseError> for NetavarkError {
fn from(err: ipnet::AddrParseError) -> Self {
NetavarkError::SubnetParse(err)
}
}

impl From<std::net::AddrParseError> for NetavarkError {
fn from(err: std::net::AddrParseError) -> Self {
NetavarkError::AddrParse(err)
}
}
69 changes: 68 additions & 1 deletion src/firewall/firewalld.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::network::internal_types;
use crate::network::internal_types::{PortForwardConfig, TearDownNetwork, TeardownPortForward};
use crate::network::types::PortMapping;
use core::convert::TryFrom;
use log::{debug, info};
use log::{debug, info, warn};
use std::collections::HashMap;
use std::vec::Vec;
use zbus::{
Expand Down Expand Up @@ -679,3 +679,70 @@ fn make_port_tuple(port: &PortMapping, addr: &str) -> (String, String, String, S
to_return
}
}

/// Check if firewalld is running.
/// Not used within the firewalld driver, but by other drivers that may need to
/// interact with firewalld.
pub fn is_firewalld_running(conn: &Connection) -> bool {
conn.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"GetNameOwner",
&"org.fedoraproject.FirewallD1",
)
.is_ok()
}

/// If possible, add a firewalld rule to allow traffic.
/// Ignore all errors, beyond possibly logging them.
/// Not used within the firewalld driver, but by other drivers that may need to
/// interact with firewalld.
pub fn add_firewalld_if_possible(net: &ipnet::IpNet) {
let conn = match Connection::system() {
Ok(conn) => conn,
Err(_) => return,
};
if !is_firewalld_running(&conn) {
return;
}
debug!("Adding firewalld rules for network {}", net.to_string());

match add_source_subnets_to_zone(&conn, "trusted", &[*net]) {
Ok(_) => {}
Err(e) => warn!(
"Error adding subnet {} from firewalld trusted zone: {}",
net.to_string(),
e
),
}
}

/// If possible, remove a firewalld rule to allow traffic.
/// Ignore all errors, beyond possibly logging them.
/// Not used within the firewalld driver, but by other drivers that may need to
/// interact with firewalld.
pub fn rm_firewalld_if_possible(net: &ipnet::IpNet) {
let conn = match Connection::system() {
Ok(conn) => conn,
Err(_) => return,
};
if !is_firewalld_running(&conn) {
return;
}
debug!("Removing firewalld rules for IPs {}", net.to_string());
match conn.call_method(
Some("org.fedoraproject.FirewallD1"),
"/org/fedoraproject/FirewallD1",
Some("org.fedoraproject.FirewallD1.zone"),
"removeSource",
&("trusted", net.to_string()),
) {
Ok(_) => {}
Err(e) => warn!(
"Error removing subnet {} from firewalld trusted zone: {}",
net.to_string(),
e
),
};
}
67 changes: 2 additions & 65 deletions src/firewall/iptables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ use crate::network::internal_types::{
};
use iptables;
use iptables::IPTables;
use log::{debug, warn};
use zbus::blocking::Connection;

pub(crate) const MAX_HASH_SIZE: usize = 13;

Expand Down Expand Up @@ -64,7 +62,7 @@ impl firewall::FirewallDriver for IptablesDriver {

create_network_chains(chains)?;

add_firewalld_if_possible(&network);
firewalld::add_firewalld_if_possible(&network);
}
}
Ok(())
Expand Down Expand Up @@ -106,7 +104,7 @@ impl firewall::FirewallDriver for IptablesDriver {
}

if tear.complete_teardown {
rm_firewalld_if_possible(&network)
firewalld::rm_firewalld_if_possible(&network)
}
}
}
Expand Down Expand Up @@ -216,64 +214,3 @@ impl firewall::FirewallDriver for IptablesDriver {
Result::Ok(())
}
}

/// Check if firewalld is running
fn is_firewalld_running(conn: &Connection) -> bool {
conn.call_method(
Some("org.freedesktop.DBus"),
"/org/freedesktop/DBus",
Some("org.freedesktop.DBus"),
"GetNameOwner",
&"org.fedoraproject.FirewallD1",
)
.is_ok()
}

/// If possible, add a firewalld rule to allow traffic.
/// Ignore all errors, beyond possibly logging them.
fn add_firewalld_if_possible(net: &ipnet::IpNet) {
let conn = match Connection::system() {
Ok(conn) => conn,
Err(_) => return,
};
if !is_firewalld_running(&conn) {
return;
}
debug!("Adding firewalld rules for network {}", net.to_string());

match firewalld::add_source_subnets_to_zone(&conn, "trusted", &[*net]) {
Ok(_) => {}
Err(e) => warn!(
"Error adding subnet {} from firewalld trusted zone: {}",
net.to_string(),
e
),
}
}

// If possible, remove a firewalld rule to allow traffic.
// Ignore all errors, beyond possibly logging them.
fn rm_firewalld_if_possible(net: &ipnet::IpNet) {
let conn = match Connection::system() {
Ok(conn) => conn,
Err(_) => return,
};
if !is_firewalld_running(&conn) {
return;
}
debug!("Removing firewalld rules for IPs {}", net.to_string());
match conn.call_method(
Some("org.fedoraproject.FirewallD1"),
"/org/fedoraproject/FirewallD1",
Some("org.fedoraproject.FirewallD1.zone"),
"removeSource",
&("trusted", net.to_string()),
) {
Ok(_) => {}
Err(e) => warn!(
"Error removing subnet {} from firewalld trusted zone: {}",
net.to_string(),
e
),
};
}
5 changes: 2 additions & 3 deletions src/firewall/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use zbus::blocking::Connection;
pub mod firewalld;
pub mod fwnone;
pub mod iptables;
pub mod nft;
pub mod state;
mod varktables;

Expand Down Expand Up @@ -108,9 +109,7 @@ pub fn get_supported_firewall_driver(
}
FirewallImpl::Nftables => {
info!("Using nftables firewall driver");
Err(NetavarkError::msg(
"nftables support presently not available",
))
nft::new()
}
FirewallImpl::Fwnone => {
info!("Not using firewall");
Expand Down
Loading

0 comments on commit c19a317

Please sign in to comment.