From 9ccfaf429f29b08272287a31dae75db3e183c7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Ciarcin=CC=81ski?= Date: Wed, 18 Mar 2026 10:16:08 +0100 Subject: [PATCH 1/3] OPNsense plugin for Gateway 2.0 --- Cargo.lock | 39 +++++----- Cargo.toml | 8 +-- build.rs | 2 +- opnsense/Makefile | 2 +- .../etc/inc/plugins.inc.d/defguardgateway.inc | 13 ++-- .../DefguardGateway/forms/general.xml | 71 ++++++++++++++----- .../DefguardGateway/DefguardGateway.xml | 45 +++++++++--- .../views/OPNsense/DefguardGateway/index.volt | 31 ++++++-- .../OPNsense/DefguardGateway/config.toml | 53 +++++++++----- src/config.rs | 4 +- 10 files changed, 182 insertions(+), 86 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d9974c7..689bb9e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -161,7 +161,7 @@ dependencies = [ "memchr", "serde", "serde_derive", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -695,7 +695,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tokio-stream", - "toml 1.0.6+spec-1.1.0", + "toml 1.0.7+spec-1.1.0", "tonic", "tonic-prost", "tonic-prost-build", @@ -735,7 +735,7 @@ dependencies = [ [[package]] name = "defguard_certs" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=9c6cbd5108470f9c8dc9b4ee740a9a08f071468c#9c6cbd5108470f9c8dc9b4ee740a9a08f071468c" +source = "git+https://github.com/DefGuard/defguard.git?rev=b6921e0f510eae1114844c3df5f5a74c23a75e46#b6921e0f510eae1114844c3df5f5a74c23a75e46" dependencies = [ "base64", "chrono", @@ -750,7 +750,7 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=9c6cbd5108470f9c8dc9b4ee740a9a08f071468c#9c6cbd5108470f9c8dc9b4ee740a9a08f071468c" +source = "git+https://github.com/DefGuard/defguard.git?rev=b6921e0f510eae1114844c3df5f5a74c23a75e46#b6921e0f510eae1114844c3df5f5a74c23a75e46" dependencies = [ "axum", "http", @@ -3612,20 +3612,20 @@ dependencies = [ "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] name = "toml" -version = "1.0.6+spec-1.1.0" +version = "1.0.7+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399b1124a3c9e16766831c6bba21e50192572cdd98706ea114f9502509686ffc" +checksum = "dd28d57d8a6f6e458bc0b8784f8fdcc4b99a437936056fa122cb234f18656a96" dependencies = [ "serde_core", "serde_spanned", - "toml_datetime 1.0.0+spec-1.1.0", + "toml_datetime 1.0.1+spec-1.1.0", "toml_parser", - "winnow", + "winnow 1.0.0", ] [[package]] @@ -3639,27 +3639,27 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.0.0+spec-1.1.0" +version = "1.0.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +checksum = "9b320e741db58cac564e26c607d3cc1fdc4a88fd36c879568c07856ed83ff3e9" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" -version = "1.0.9+spec-1.1.0" +version = "1.0.10+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +checksum = "7df25b4befd31c4816df190124375d5a20c6b6921e2cad937316de3fccd63420" dependencies = [ - "winnow", + "winnow 1.0.0", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.0.7+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "f17aaa1c6e3dc22b1da4b6bba97d066e354c7945cac2f7852d4e4e7ca7a6b56d" [[package]] name = "tonic" @@ -4080,7 +4080,6 @@ dependencies = [ "anyhow", "derive_builder", "rustversion", - "time", "vergen-lib", ] @@ -4535,6 +4534,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" + [[package]] name = "wireguard-nt" version = "0.5.0" diff --git a/Cargo.toml b/Cargo.toml index 19d8561c..8baf5f85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,9 +7,9 @@ edition = "2024" axum = "0.8" base64 = "0.22" chrono = "0.4" -clap = { version = "4.5", features = ["derive", "env"] } -defguard_certs = { git = "https://github.com/DefGuard/defguard.git", rev = "9c6cbd5108470f9c8dc9b4ee740a9a08f071468c" } -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "9c6cbd5108470f9c8dc9b4ee740a9a08f071468c" } +clap = { version = "4.6", features = ["derive", "env"] } +defguard_certs = { git = "https://github.com/DefGuard/defguard.git", rev = "b6921e0f510eae1114844c3df5f5a74c23a75e46" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "b6921e0f510eae1114844c3df5f5a74c23a75e46" } defguard_wireguard_rs = "0.9" env_logger = "0.11" gethostname = "1.0" @@ -56,7 +56,7 @@ x25519-dalek = { version = "2.0", features = ["getrandom", "static_secrets"] } [build-dependencies] tonic-prost-build = "0.14" -vergen-git2 = { version = "9.1", features = ["build"] } +vergen-git2 = "9.1" [profile.release] codegen-units = 1 diff --git a/build.rs b/build.rs index e9fddc48..5731502f 100644 --- a/build.rs +++ b/build.rs @@ -2,7 +2,7 @@ use vergen_git2::{Emitter, Git2Builder}; fn main() -> Result<(), Box> { // set VERGEN_GIT_SHA env variable based on git commit hash - let git2 = Git2Builder::default().branch(true).sha(true).build()?; + let git2 = Git2Builder::default().sha(true).build()?; Emitter::default().add_instructions(&git2)?.emit()?; tonic_prost_build::configure() diff --git a/opnsense/Makefile b/opnsense/Makefile index ffe89d87..74861b78 100644 --- a/opnsense/Makefile +++ b/opnsense/Makefile @@ -1,5 +1,5 @@ PLUGIN_NAME= defguard-gateway -PLUGIN_VERSION= 1.0.1 +PLUGIN_VERSION= 2.0.0 PLUGIN_COMMENT= Gateway service for Defguard PLUGIN_MAINTAINER= defguard@community.net diff --git a/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc b/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc index d7d800b2..5584a3b4 100644 --- a/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc +++ b/opnsense/src/etc/inc/plugins.inc.d/defguardgateway.inc @@ -7,7 +7,7 @@ function defguardgateway_services() $pidfile = (string) (new OPNsense\DefguardGateway\DefguardGateway())->general->PidFile; - if (isset($config['OPNsense']['defguardgateway']['general']['enabled']) && $config['OPNsense']['defguardgateway']['general']['enabled'] == 1) { + if (isset($config['OPNsense']['defguardgateway']['general']['Enabled']) && $config['OPNsense']['defguardgateway']['general']['Enabled'] == 1) { $services[] = [ "description" => "Defguard Gateway", "configd" => [ @@ -41,24 +41,23 @@ function defguardgateway_interfaces() function defguardgateway_devices() { - $names = []; - $interface = (new OPNsense\DefguardGateway\DefguardGateway())->general ->IfName; + $interface = empty((string) $interface) ? 'wg0' : (string) $interface; $devices[] = [ "configurable" => false, - "pattern" => "^wg", + "pattern" => sprintf("^%s$", preg_quote($interface, '/')), "type" => "wireguard", "volatile" => true, "names" => [ - (string) $interface => [ + $interface => [ "descr" => sprintf( "%s (Defguard Gateway)", - (string) $interface + $interface ), "ifdescr" => "WireGuard interface used by Defguard Gateway", - "name" => (string) $interface, + "name" => $interface, ], ], ]; 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 d2e0e761..3b9b81e2 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 @@ -6,30 +6,26 @@ Check to enable Defguard Gateway service. - defguardgateway.general.Token - + defguardgateway.general.LogLevel + text - Required: Token obtained from Defguard Core after network creation. - - - defguardgateway.general.GrpcUrl - - text - Required: URL of Defguard Core's gRPC service. - - - defguardgateway.general.GrpcCertPath - - text - Required if custom SSL CA has been enabled in Defguard Core; more details here: https://docs.defguard.net/admin-and-features/setting-up-your-instance/grpc-ssl-communication#custom-ssl-ca-and-certificates. + Set the application log level used when syslog is disabled. + Default value: info defguardgateway.general.Name text - Name that will be displayed in Defguard + Name that will be displayed in Defguard. Gateway OPNsense + + defguardgateway.general.GrpcPort + + text + Port used by the gateway gRPC server. + Default value: 50066 + defguardgateway.general.UseSyslog @@ -61,15 +57,15 @@ defguardgateway.general.IfName text - Specify the WireGuard interface name + Specify the WireGuard interface name. It must start with wg. Default value: wg0 defguardgateway.general.StatsPeriod text - Specify the stats period in seconds - Default value: 60. + Specify how often interface statistics are sent, in seconds. + Default value: 30 defguardgateway.general.Userspace @@ -101,4 +97,41 @@ text Command to run after bringing down the interface. + + defguardgateway.general.HealthPort + + text + Optional HTTP port exposing the gateway health endpoint. + + + defguardgateway.general.Masquerade + + checkbox + Automatically apply outbound masquerading rules in the firewall. + + + defguardgateway.general.FwPriority + + text + Optional priority for the Defguard forward chain. + + + defguardgateway.general.DisableFirewallManagement + + checkbox + Disable Defguard-managed firewall changes for incompatible hardware or custom setups. + + + defguardgateway.general.HttpBindAddress + + text + Optional IPv4 or IPv6 address used by the health endpoint. + + + defguardgateway.general.CertDir + + text + Directory where the gateway stores generated gRPC certificates. + Default value: /etc/defguard/certs + 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 da480934..6a1ac31f 100644 --- a/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml +++ b/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml @@ -11,20 +11,17 @@ 0 Y - + + info Y - please add authorization token - - - Y - please specify Defguard Core gRPC URL - - - N - + N + + 50066 + Y + 0 Y @@ -44,10 +41,12 @@ Y wg0 + /^wg[0-9]*$/ + please specify a valid interface name starting with wg Y - 60 + 30 N @@ -61,6 +60,30 @@ N + + N + + + 0 + Y + + + N + + + 0 + Y + + + N + N + N + please specify a valid IP address + + + /etc/defguard/certs + Y + diff --git a/opnsense/src/opnsense/mvc/app/views/OPNsense/DefguardGateway/index.volt b/opnsense/src/opnsense/mvc/app/views/OPNsense/DefguardGateway/index.volt index 52c5d88f..a36d2125 100644 --- a/opnsense/src/opnsense/mvc/app/views/OPNsense/DefguardGateway/index.volt +++ b/opnsense/src/opnsense/mvc/app/views/OPNsense/DefguardGateway/index.volt @@ -12,10 +12,15 @@ $( document ).ready(function() { updateServiceControlUI("defguardgateway"); ajaxCall(url="/api/defguardgateway/service/reconfigure", sendData={}, callback=function(data, status) { $("#responseMsg").html(data["status"]); - }); - ajaxCall(url="/api/defguardgateway/service/restart", sendData={}, callback=function(data, status) { - updateServiceControlUI("defguardgateway"); - }); + if (status !== "success" || data["status"] !== "ok") { + return; + } + + ajaxCall(url="/api/defguardgateway/service/restart", sendData={}, callback=function(data, status) { + $("#responseMsg").html(data['status']); + updateServiceControlUI("defguardgateway"); + }); + }); }); }); @@ -30,7 +35,22 @@ $( document ).ready(function() { $("#startAct").removeClass("fa fa-spinner fa-pulse"); $("#responseMsg").html(data['status']); updateServiceControlUI("defguardgateway"); - }); + }); + }); + }); + + $("#stopAct").click(function () { + stdDialogConfirm( + '{{ lang._("Confirm gateway stop") }}', + '{{ lang._("Do you want to stop Defguard Gateway?") }}', + '{{ lang._("Yes") }}', '{{ lang._("Cancel") }}', function () { + $("#stopAct").addClass("fa fa-spinner"); + $("#responseMsg").removeClass("hidden"); + ajaxCall(url="/api/defguardgateway/service/stop", sendData={}, callback=function(data, status) { + $("#stopAct").removeClass("fa fa-spinner fa-pulse"); + $("#responseMsg").html(data['status']); + updateServiceControlUI("defguardgateway"); + }); }); }); }); @@ -45,4 +65,5 @@ $( document ).ready(function() {
+
diff --git a/opnsense/src/opnsense/service/templates/OPNsense/DefguardGateway/config.toml b/opnsense/src/opnsense/service/templates/OPNsense/DefguardGateway/config.toml index 2407d4ec..5299b27a 100644 --- a/opnsense/src/opnsense/service/templates/OPNsense/DefguardGateway/config.toml +++ b/opnsense/src/opnsense/service/templates/OPNsense/DefguardGateway/config.toml @@ -1,47 +1,62 @@ -# Required: defguard server gRPC endpoint URL -# NOTE: must replace default with actual value -grpc_url = "{{ OPNsense.defguardgateway.general.GrpcUrl|default("") }}" +# Log level used when syslog is disabled +log_level = "{{ OPNsense.defguardgateway.general.LogLevel|default("info") }}" # Optional: gateway name which will be displayed in defguard web UI -name = "{{ OPNsense.defguardgateway.general.Name|default("Gateway A") }}" -# Required: use userspace WireGuard implementation (e.g. wireguard-go) +{% if not helpers.empty('OPNsense.defguardgateway.general.Name') %} +name = "{{ OPNsense.defguardgateway.general.Name }}" +{% endif %} +# Gateway gRPC server port +grpc_port = {{ OPNsense.defguardgateway.general.GrpcPort|default(50066) }} +# Use userspace WireGuard implementation (e.g. wireguard-go) {% if OPNsense.defguardgateway.general.Userspace == "1" %} userspace = true {% else %} userspace = false {% endif %} -# Optional: path to TLS cert file -{% if not helpers.empty('OPNsense.defguardgateway.general.GrpcCertPath') %} -grpc_ca = "{{ OPNsense.defguardgateway.general.GrpcCertPath }}" -{% endif %} -# Required: how often should interface stat updates be sent to defguard server (in seconds) -stats_period = {{ OPNsense.defguardgateway.general.StatsPeriod|default(60) }} -# Required: name of WireGuard interface +# How often interface statistics are sent to Defguard Core (in seconds) +stats_period = {{ OPNsense.defguardgateway.general.StatsPeriod|default(30) }} +# Name of WireGuard interface ifname = "{{ OPNsense.defguardgateway.general.IfName|default("wg0") }}" # Optional: write PID to this file pidfile = "{{ OPNsense.defguardgateway.general.PidFile|default("/var/run/defguard_gateway.pid") }}" -# Required: enable logging to syslog +# Enable logging to syslog {% if OPNsense.defguardgateway.general.UseSyslog == "1" %} use_syslog = true {% else %} use_syslog = false {% endif %} -# Required: which syslog facility to use +# Syslog facility syslog_facility = "{{ OPNsense.defguardgateway.general.SyslogFacility|default("LOG_USER") }}" -# Required: which socket to use for logging +# Syslog socket path syslog_socket = "{{ OPNsense.defguardgateway.general.SyslogSocket|default("/var/run/log") }}" - {% if not helpers.empty('OPNsense.defguardgateway.general.PreUp') %} pre_up = "{{ OPNsense.defguardgateway.general.PreUp }}" {% endif %} - {% if not helpers.empty('OPNsense.defguardgateway.general.PreDown') %} pre_down = "{{ OPNsense.defguardgateway.general.PreDown }}" {% endif %} - {% if not helpers.empty('OPNsense.defguardgateway.general.PostUp') %} post_up = "{{ OPNsense.defguardgateway.general.PostUp }}" {% endif %} - {% if not helpers.empty('OPNsense.defguardgateway.general.PostDown') %} post_down = "{{ OPNsense.defguardgateway.general.PostDown }}" {% endif %} +{% if not helpers.empty('OPNsense.defguardgateway.general.HealthPort') %} +health_port = {{ OPNsense.defguardgateway.general.HealthPort }} +{% endif %} +{% if OPNsense.defguardgateway.general.Masquerade == "1" %} +masquerade = true +{% else %} +masquerade = false +{% endif %} +{% if not helpers.empty('OPNsense.defguardgateway.general.FwPriority') %} +fw_priority = {{ OPNsense.defguardgateway.general.FwPriority }} +{% endif %} +{% if OPNsense.defguardgateway.general.DisableFirewallManagement == "1" %} +disable_firewall_management = true +{% else %} +disable_firewall_management = false +{% endif %} +{% if not helpers.empty('OPNsense.defguardgateway.general.HttpBindAddress') %} +http_bind_address = "{{ OPNsense.defguardgateway.general.HttpBindAddress }}" +{% endif %} +cert_dir = "{{ OPNsense.defguardgateway.general.CertDir|default("/etc/defguard/certs") }}" diff --git a/src/config.rs b/src/config.rs index 55b5f567..6aa8ad6f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -4,7 +4,7 @@ use clap::Parser; use serde::Deserialize; use toml; -use crate::error::GatewayError; +use crate::{VERSION, error::GatewayError}; fn default_log_level() -> String { String::from("info") @@ -16,7 +16,7 @@ fn default_syslog_socket() -> PathBuf { #[derive(Debug, Parser, Clone, Deserialize)] #[clap(about = "Defguard VPN gateway service")] -#[command(version)] +#[command(version = VERSION)] pub struct Config { #[arg(long, short = 'l', env = "DEFGUARD_LOG_LEVEL", default_value = "info")] #[serde(default = "default_log_level")] From 6513375199393dc59fe2a8c56a8a6e7f8a39584f Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 18 Mar 2026 10:42:34 +0100 Subject: [PATCH 2/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../app/models/OPNsense/DefguardGateway/DefguardGateway.xml | 3 +++ 1 file changed, 3 insertions(+) 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 6a1ac31f..00c40beb 100644 --- a/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml +++ b/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml @@ -21,6 +21,9 @@ 50066 Y + 1 + 65535 + please specify a valid TCP/UDP port between 1 and 65535 0 From 00ecfe803a859af98b3c840669736caa523c9d16 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 18 Mar 2026 10:43:20 +0100 Subject: [PATCH 3/3] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../app/models/OPNsense/DefguardGateway/DefguardGateway.xml | 3 +++ 1 file changed, 3 insertions(+) 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 00c40beb..5c0a8ee8 100644 --- a/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml +++ b/opnsense/src/opnsense/mvc/app/models/OPNsense/DefguardGateway/DefguardGateway.xml @@ -65,6 +65,9 @@ N + 1 + 65535 + please specify a valid port number (1-65535) 0