pve-firewall supports several subcommands for starting / stopping the firewall, as well as debugging. proxmox-firewall currently only ran in foreground when executed and had no real means of dumping the generated ruleset other than running it with log level trace and capturing the log output.
The commands introduced in this patch series are: * start - runs proxmox-firewall in foreground * skeleton - prints the rule skeleton, included in the binary * compile - prints the commands generated from the firewall configuration files For now, start retains the exact same behavior as the binary had before introducing subcommands, so calling it with start as subcommand is equivalent to invoking it without the start subcommand before. The skeleton and compile subcommands can be used to dump the nftables rules generated by proxmox-firewall. They print the ruleset in the format expected by nft directly to STDOUT, so it can be piped to the nft binary directly: $ proxmox-firewall skeleton | nft -f - $ proxmox-firewall compile | nft -j -f - start always prints its logs to the journal, all other commands print the logs to stderr. Since there isn't really a reason anymore to run proxmox-firewall in the foreground via start (there are now specific commands for debugging), this should be fine. Signed-off-by: Stefan Hanreich <s.hanre...@proxmox.com> --- debian/control | 1 + debian/proxmox-firewall.service | 2 +- proxmox-firewall/Cargo.toml | 2 + proxmox-firewall/src/bin/proxmox-firewall.rs | 103 +++++++++++++++++-- 4 files changed, 97 insertions(+), 11 deletions(-) diff --git a/debian/control b/debian/control index be6e584..15d9cad 100644 --- a/debian/control +++ b/debian/control @@ -7,6 +7,7 @@ Build-Depends: cargo:native, librust-anyhow-1+default-dev, librust-insta-1+default-dev (>= 1.21-~~), librust-insta-1+json-dev (>= 1.21-~~), + librust-pico-args-0.5+default-dev, librust-proxmox-log-0.2+default-dev (>= 0.2.9-~~), librust-proxmox-sys-0.6+default-dev, librust-proxmox-ve-config-dev (>= 0.2.3-~~), diff --git a/debian/proxmox-firewall.service b/debian/proxmox-firewall.service index ececa75..398670f 100644 --- a/debian/proxmox-firewall.service +++ b/debian/proxmox-firewall.service @@ -4,7 +4,7 @@ Wants=pve-cluster.service pvefw-logger.service After=pvefw-logger.service pve-cluster.service network.target systemd-modules-load.service [Service] -ExecStart=/usr/libexec/proxmox/proxmox-firewall +ExecStart=/usr/libexec/proxmox/proxmox-firewall start Type=simple [Install] diff --git a/proxmox-firewall/Cargo.toml b/proxmox-firewall/Cargo.toml index a7031a3..3302060 100644 --- a/proxmox-firewall/Cargo.toml +++ b/proxmox-firewall/Cargo.toml @@ -13,6 +13,8 @@ license = "AGPL-3" [dependencies] anyhow = "1" +pico-args = "0.5" + serde = { version = "1", features = [ "derive" ] } serde_json = "1" diff --git a/proxmox-firewall/src/bin/proxmox-firewall.rs b/proxmox-firewall/src/bin/proxmox-firewall.rs index 70dca73..273daec 100644 --- a/proxmox-firewall/src/bin/proxmox-firewall.rs +++ b/proxmox-firewall/src/bin/proxmox-firewall.rs @@ -2,7 +2,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::time::{Duration, Instant}; -use anyhow::{Context, Error}; +use anyhow::{bail, format_err, Context, Error}; +use pico_args::Arguments; use proxmox_firewall::config::{FirewallConfig, PveFirewallConfigLoader, PveNftConfigLoader}; use proxmox_firewall::firewall::Firewall; @@ -10,6 +11,18 @@ use proxmox_log as log; use proxmox_log::{LevelFilter, Logger}; use proxmox_nftables::{client::NftError, NftClient}; +const HELP: &str = r#" +USAGE: + proxmox-firewall <COMMAND> + +COMMANDS: + help Prints this help message. + skeleton Prints the firewall rule skeleton as accepted by 'nft -f -' + compile Compile and print firewall rules as accepted by 'nft -j -f -' + start Execute proxmox-firewall service in foreground +"#; + + const RULE_BASE: &str = include_str!("../../resources/proxmox-firewall.nft"); const FORCE_DISABLE_FLAG_FILE: &str = "/run/proxmox-nftables-firewall-force-disable"; @@ -27,10 +40,13 @@ fn remove_firewall() -> Result<(), std::io::Error> { Ok(()) } -fn handle_firewall() -> Result<(), Error> { +fn create_firewall_instance() -> Result<Firewall, Error> { let config = FirewallConfig::new(&PveFirewallConfigLoader::new(), &PveNftConfigLoader::new())?; + Ok(Firewall::new(config)) +} - let firewall = Firewall::new(config); +fn handle_firewall() -> Result<(), Error> { + let firewall = create_firewall_instance()?; if !firewall.is_enabled() { return remove_firewall().with_context(|| "could not remove firewall tables".to_string()); @@ -55,15 +71,19 @@ fn handle_firewall() -> Result<(), Error> { Ok(()) } -fn init_logger() -> Result<(), Error> { - Logger::from_env("PVE_LOG", LevelFilter::WARN) - .journald() - .init() -} +fn init_logger(command: Command) -> Result<(), Error> { + let mut logger = Logger::from_env("PVE_LOG", LevelFilter::WARN); -fn main() -> Result<(), Error> { - init_logger()?; + if command == Command::Start { + logger = logger.journald(); + } else { + logger = logger.stderr_pve(); + } + + logger.init() +} +fn run_firewall() -> Result<(), Error> { let term = Arc::new(AtomicBool::new(false)); signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&term))?; @@ -97,3 +117,66 @@ fn main() -> Result<(), Error> { remove_firewall() .with_context(|| "Could not remove firewall rules") } + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Command { + Compile, + Help, + Skeleton, + Start, +} + +impl std::str::FromStr for Command { + type Err = Error; + + fn from_str(value: &str) -> Result<Self, Self::Err> { + Ok(match value { + "help" => Command::Help, + "compile" => Command::Compile, + "skeleton" => Command::Skeleton, + "start" => Command::Start, + cmd => { + bail!("{cmd} is not a valid command") + }, + }) + } +} + +fn run_command(command: Command) -> Result<(), Error> { + init_logger(command)?; + + match command { + Command::Help => { + println!("{}", HELP); + Ok(()) + }, + Command::Compile => { + let commands = create_firewall_instance()?.full_host_fw()?; + let json = serde_json::to_string_pretty(&commands)?; + + println!("{json}"); + Ok(()) + }, + Command::Skeleton => { + println!("{}", RULE_BASE); + Ok(()) + }, + Command::Start => run_firewall(), + } +} + +fn main() -> Result<(), Error> { + let mut args = Arguments::from_env(); + + let parsed_command = args.subcommand()? + .ok_or_else(|| format_err!("no subcommand specified"))? + .parse(); + + if let Ok(command) = parsed_command { + run_command(command) + } else { + eprintln!("Invalid command specified!\n{}", HELP); + std::process::exit(1); + } + +} -- 2.39.5 _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel