From 04f081d5def7775552d59fee6b3662b7178f84f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Tue, 11 Aug 2026 11:12:29 +0200 Subject: [PATCH 1/3] port opnsense plugin fix --- .../src/etc/inc/plugins.inc.d/defguardgateway.inc | 9 ++++++++- .../DefguardGateway/Api/ServiceController.php | 9 +++++++++ .../OPNsense/DefguardGateway/forms/general.xml | 14 ++++++++++++++ .../OPNsense/DefguardGateway/DefguardGateway.xml | 12 ++++++++++++ 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc b/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc index 5584a3b4..3696a19f 100644 --- a/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc +++ b/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc @@ -81,5 +81,12 @@ function defguardgateway_firewall($fw) // $fw->registerAnchor('defguard/*', 'nat', 1, 'head'); // $fw->registerAnchor('defguard/*', 'rdr', 1, 'head'); - $fw->registerAnchor('defguard/*', 'fw', 1, 'head', true); + // IMPORTANT: never register this anchor as 'quick'. On FreeBSD 15+ + // (OPNsense 26.7+), a 'quick' anchor short-circuits the parent ruleset + // even when no inner rule matches, silently skipping OPNsense's own + // firewall rules. + $model = new OPNsense\DefguardGateway\DefguardGateway(); + $placement = (string) $model->general->FwAnchorPlacement ?: 'tail'; + $quick = (string) $model->general->FwAnchorQuick === '1'; + $fw->registerAnchor('defguard/*', 'fw', 1, $placement, $quick); } diff --git a/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/Api/ServiceController.php b/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/Api/ServiceController.php index 2c340eb0..553ba3a5 100644 --- a/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/Api/ServiceController.php +++ b/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/Api/ServiceController.php @@ -14,4 +14,13 @@ class ServiceController extends ApiMutableServiceControllerBase protected static $internalServiceClass = "\OPNsense\DefguardGateway\DefguardGateway"; protected static $internalServiceTemplate = "OPNsense/DefguardGateway"; protected static $internalServiceEnabled = "general.Enabled"; + + /** + * The 'defguard/*' anchor is hooked into the ruleset by defguardgateway_firewall(). + * Changes only take effect once the filter is reloaded. + */ + protected function invokeFirewallReload() + { + return true; + } } diff --git a/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/forms/general.xml b/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/forms/general.xml index 3b9b81e2..29709dad 100644 --- a/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/forms/general.xml +++ b/opnsense/src/opnsense/mvc/app/controllers/OPNsense/DefguardGateway/forms/general.xml @@ -134,4 +134,18 @@ Directory where the gateway stores generated gRPC certificates. Default value: /etc/defguard/certs + + defguardgateway.general.FwAnchorPlacement + + dropdown + Where in the pf ruleset the Defguard anchor is inserted. Tail (default) evaluates OPNsense rules first; Head places Defguard rules before OPNsense rules. + true + + + defguardgateway.general.FwAnchorQuick + + checkbox + Make the Defguard anchor 'quick'. WARNING: on OPNsense 26.7+ (FreeBSD 15.1+) this short-circuits the parent ruleset even when no inner rule matches, silently bypassing OPNsense's own firewall rules. Leave disabled unless you fully understand the risk. + true + diff --git a/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml b/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml index 5c0a8ee8..d7e0562c 100644 --- a/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml +++ b/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml @@ -90,6 +90,18 @@ /etc/defguard/certs Y + + tail + Y + + Tail (evaluate after OPNsense rules) + Head (evaluate before OPNsense rules) + + + + 0 + Y + From ee0276ceca06cc3a80ce2d0a6e7fe70e94063e2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Tue, 11 Aug 2026 11:12:36 +0200 Subject: [PATCH 2/3] update nftnl --- Cargo.lock | 8 +++++--- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 558b9cf5..7c027c23 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2314,8 +2314,9 @@ dependencies = [ [[package]] name = "nftnl" -version = "0.9.2" -source = "git+https://github.com/DefGuard/nftnl-rs.git?rev=abd34b08c53b88a47346da6362e360d522cf8ca2#abd34b08c53b88a47346da6362e360d522cf8ca2" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f44c7df2060dc8fc8388ca2717f1a2a7eb2193d1f6dc357671a93f0cc97fc67" dependencies = [ "bitflags 2.13.1", "log", @@ -2326,7 +2327,8 @@ dependencies = [ [[package]] name = "nftnl-sys" version = "0.6.4" -source = "git+https://github.com/DefGuard/nftnl-rs.git?rev=abd34b08c53b88a47346da6362e360d522cf8ca2#abd34b08c53b88a47346da6362e360d522cf8ca2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cffa28b4c81eea6a8a4fd454634d425e1a559250a50a926de4517dd9607fe9a4" dependencies = [ "cfg-if", "libc", diff --git a/Cargo.toml b/Cargo.toml index 476954b2..43e69d16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,7 @@ tracing = "0.1" tracing-subscriber = "0.3" [target.'cfg(target_os = "linux")'.dependencies] -nftnl = { git = "https://github.com/DefGuard/nftnl-rs.git", rev = "abd34b08c53b88a47346da6362e360d522cf8ca2" } +nftnl = "0.9" mnl = "0.3" [dev-dependencies] From f7c0224c83bee3e4a807a43848a387d6af88df06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Tue, 11 Aug 2026 11:12:49 +0200 Subject: [PATCH 3/3] cleanup --- src/enterprise/firewall/nftables/netfilter.rs | 3 ++- src/enterprise/firewall/packetfilter/api.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/enterprise/firewall/nftables/netfilter.rs b/src/enterprise/firewall/nftables/netfilter.rs index ba7d3e24..a551aefb 100644 --- a/src/enterprise/firewall/nftables/netfilter.rs +++ b/src/enterprise/firewall/nftables/netfilter.rs @@ -160,7 +160,8 @@ fn add_rule_comment(rule: &mut Rule, comment: &str) -> Result<(), FirewallError> "Failed to create CString from string {comment}. Error: {e:?}" )) })?; - rule.set_comment(comment); + rule.set_comment(comment) + .map_err(|err| FirewallError::NetlinkError(err.into()))?; debug!("Added comment to nftables expression: {comment:?}"); Ok(()) } diff --git a/src/enterprise/firewall/packetfilter/api.rs b/src/enterprise/firewall/packetfilter/api.rs index 96f7b78e..af541bfe 100644 --- a/src/enterprise/firewall/packetfilter/api.rs +++ b/src/enterprise/firewall/packetfilter/api.rs @@ -55,7 +55,7 @@ impl FirewallManagementApi for FirewallApi { for rule in rules { if let Err(err) = self.add_rule(rule, ticket, pool_ticket, anchor) { - error!("Firewall rule {} can't be added", &rule.id); + error!("Firewall rule {} can't be added", rule.id); debug!("Rollback pf transaction"); // Rule cannot be added, so rollback. let result = unsafe { ioctl(self.fd(), DIOCXROLLBACK, &raw mut ioc_trans) };