From 4cb721cb139dbd7958b78fb1637e37fc30f11dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Mon, 14 Jul 2025 12:11:04 +0200 Subject: [PATCH 01/25] bump version to 1.5.0 --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b7558a2..edc53f75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -445,7 +445,7 @@ dependencies = [ [[package]] name = "defguard-gateway" -version = "1.4.0" +version = "1.5.0" dependencies = [ "axum 0.8.4", "base64", diff --git a/Cargo.toml b/Cargo.toml index 8f7e50b6..c39680fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "defguard-gateway" -version = "1.4.0" +version = "1.5.0" edition = "2021" [dependencies] From 7750a6379a8741cbf2872dd8f7b94d8d734500bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Mon, 14 Jul 2025 14:10:19 +0200 Subject: [PATCH 02/25] add missing import --- src/enterprise/firewall/packetfilter/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/enterprise/firewall/packetfilter/api.rs b/src/enterprise/firewall/packetfilter/api.rs index a3d7bcb7..48d9e202 100644 --- a/src/enterprise/firewall/packetfilter/api.rs +++ b/src/enterprise/firewall/packetfilter/api.rs @@ -7,7 +7,7 @@ use super::{ }; use crate::enterprise::firewall::{ api::{FirewallApi, FirewallManagementApi}, - FirewallError, Policy, + FirewallError, Policy, SnatBinding, }; impl FirewallManagementApi for FirewallApi { From 7d2bd6b61065c7b059130984363803778e8d9e6c Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Fri, 25 Jul 2025 16:50:58 +0200 Subject: [PATCH 03/25] Add AMI building to the release pipeline (#181) * gateway ami 1 * build ami for multiple regions * revert workflow changes * fix * tokyo region --- .github/workflows/release.yml | 24 ++++++++++++++ images/ami/gateway.pkr.hcl | 62 +++++++++++++++++++++++++++++++++++ images/ami/gateway.sh | 13 ++++++++ 3 files changed, 99 insertions(+) create mode 100644 images/ami/gateway.pkr.hcl create mode 100644 images/ami/gateway.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c3e7fb87..3f959c7e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -98,6 +98,10 @@ jobs: target: ${{ matrix.target }} override: true + - name: Setup `packer` + uses: hashicorp/setup-packer@main + id: setup + - name: Build release binary uses: actions-rs/cargo@v1 with: @@ -144,6 +148,26 @@ jobs: asset_name: defguard-gateway_${{ env.VERSION }}_${{ matrix.target }}.deb asset_content_type: application/octet-stream + - name: Run `packer init` + if: matrix.build == 'linux' && matrix.arch == 'amd64' + id: init + run: "packer init ./images/ami/gateway.pkr.hcl" + + - name: Build AMI images for multiple regions + if: matrix.build == 'linux' && matrix.arch == 'amd64' + run: | + regions=(us-east-1 eu-west-1 ap-northeast-1) + for region in "${regions[@]}"; do + echo "Building AMI for region: $region" + echo "Running packer validate for $region..." + packer validate --var "package_version=${{ env.VERSION }}" --var "region=$region" ./images/ami/gateway.pkr.hcl + echo "Building AMI image for $region..." + packer build -color=false -on-error=abort --var "package_version=${{ env.VERSION }}" --var "region=$region" ./images/ami/gateway.pkr.hcl + done + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + - name: Build RPM package if: matrix.build == 'linux' uses: defGuard/fpm-action@main diff --git a/images/ami/gateway.pkr.hcl b/images/ami/gateway.pkr.hcl new file mode 100644 index 00000000..431edbea --- /dev/null +++ b/images/ami/gateway.pkr.hcl @@ -0,0 +1,62 @@ +packer { + required_plugins { + amazon = { + version = ">= 1.2.8" + source = "github.com/hashicorp/amazon" + } + } +} + +variable "package_version" { + type = string +} + +variable "region" { + type = string + default = "eu-north-1" +} + +variable "instance_type" { + type = string + default = "t3.micro" +} + +source "amazon-ebs" "defguard-gateway" { + ami_name = "defguard-gateway-${var.package_version}-amd64" + instance_type = var.instance_type + region = var.region + source_ami_filter { + filters = { + name = "ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + most_recent = true + owners = ["099720109477"] + } + ssh_username = "ubuntu" +} + +build { + name = "defguard-gateway" + sources = [ + "source.amazon-ebs.defguard-gateway" + ] + + provisioner "file" { + source = "defguard-gateway_${var.package_version}_x86_64-unknown-linux-gnu.deb" + destination = "/tmp/defguard-gateway.deb" + } + + provisioner "shell" { + script = "./images/ami/gateway.sh" + } + + provisioner "shell" { + inline = ["rm /home/ubuntu/.ssh/authorized_keys"] + } + + provisioner "shell" { + inline = ["sudo rm /root/.ssh/authorized_keys"] + } +} diff --git a/images/ami/gateway.sh b/images/ami/gateway.sh new file mode 100644 index 00000000..c0b769c9 --- /dev/null +++ b/images/ami/gateway.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +set -e + +echo "Updating apt repositories..." +sudo apt update + +echo "Installing Defguard Gateway package..." +sudo dpkg -i /tmp/defguard-gateway.deb + +echo "Cleaning up..." +sudo rm -f /tmp/defguard-gateway.deb + +echo "Defguard Gateway installation completed successfully." From 0a25e408dc84006011b28ad904fe1d70cae06317 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 5 Aug 2025 12:24:09 +0200 Subject: [PATCH 04/25] Fix pointer warnings, update dependencies (#184) --- Cargo.lock | 392 +++++++----------- Cargo.toml | 20 +- build.rs | 23 +- src/enterprise/firewall/packetfilter/api.rs | 12 +- src/enterprise/firewall/packetfilter/calls.rs | 82 ++-- src/enterprise/firewall/packetfilter/mod.rs | 6 +- src/gateway.rs | 6 +- 7 files changed, 220 insertions(+), 321 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index edc53f75..39d7b879 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,28 +82,6 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -[[package]] -name = "async-stream" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" -dependencies = [ - "async-stream-impl", - "futures-core", - "pin-project-lite", -] - -[[package]] -name = "async-stream-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "async-trait" version = "0.1.88" @@ -127,40 +105,13 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" -[[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core 0.4.5", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "itoa", - "matchit 0.7.3", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower 0.5.2", - "tower-layer", - "tower-service", -] - [[package]] name = "axum" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ - "axum-core 0.5.2", + "axum-core", "axum-macros", "bytes", "form_urlencoded", @@ -171,7 +122,7 @@ dependencies = [ "hyper", "hyper-util", "itoa", - "matchit 0.8.4", + "matchit", "memchr", "mime", "percent-encoding", @@ -183,32 +134,12 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "tokio", - "tower 0.5.2", + "tower", "tower-layer", "tower-service", "tracing", ] -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", -] - [[package]] name = "axum-core" version = "0.5.2" @@ -281,9 +212,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ "jobserver", "libc", @@ -304,9 +235,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.5.40" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" dependencies = [ "clap_builder", "clap_derive", @@ -314,9 +245,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" dependencies = [ "anstream", "anstyle", @@ -326,9 +257,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.40" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck", "proc-macro2", @@ -375,9 +306,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -447,7 +378,7 @@ dependencies = [ name = "defguard-gateway" version = "1.5.0" dependencies = [ - "axum 0.8.4", + "axum", "base64", "clap", "defguard_wireguard_rs", @@ -467,7 +398,8 @@ dependencies = [ "tokio-stream", "toml", "tonic", - "tonic-build", + "tonic-prost", + "tonic-prost-build", "vergen-git2", "x25519-dalek", ] @@ -475,7 +407,8 @@ dependencies = [ [[package]] name = "defguard_wireguard_rs" version = "0.7.5" -source = "git+https://github.com/DefGuard/wireguard-rs.git?rev=v0.7.5#d090d2249e5bb3d4154f07de098387e2ab69bfdc" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2d2f56ffaf56903a51b128c6f6730b8b344fab0d0be0f5db0b65dcccbb7334" dependencies = [ "base64", "libc", @@ -734,19 +667,13 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.10.0", + "indexmap", "slab", "tokio", "tokio-util", "tracing", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.15.4" @@ -852,9 +779,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "bytes", "futures-channel", @@ -986,22 +913,23 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.3" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ - "autocfg", - "hashbrown 0.12.3", + "equivalent", + "hashbrown", ] [[package]] -name = "indexmap" -version = "2.10.0" +name = "io-uring" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" dependencies = [ - "equivalent", - "hashbrown 0.15.4", + "bitflags", + "cfg-if", + "libc", ] [[package]] @@ -1113,12 +1041,6 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" -[[package]] -name = "matchit" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" - [[package]] name = "matchit" version = "0.8.4" @@ -1362,7 +1284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.10.0", + "indexmap", ] [[package]] @@ -1433,20 +1355,11 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - [[package]] name = "prettyplease" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" dependencies = [ "proc-macro2", "syn", @@ -1463,9 +1376,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" dependencies = [ "bytes", "prost-derive", @@ -1473,9 +1386,9 @@ dependencies = [ [[package]] name = "prost-build" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +checksum = "ac6c3320f9abac597dcbc668774ef006702672474aad53c6d596b62e487b40b1" dependencies = [ "heck", "itertools", @@ -1486,6 +1399,8 @@ dependencies = [ "prettyplease", "prost", "prost-types", + "pulldown-cmark", + "pulldown-cmark-to-cmark", "regex", "syn", "tempfile", @@ -1493,9 +1408,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" dependencies = [ "anyhow", "itertools", @@ -1506,48 +1421,47 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.13.5" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" dependencies = [ "prost", ] [[package]] -name = "quote" -version = "1.0.40" +name = "pulldown-cmark" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" dependencies = [ - "proc-macro2", + "bitflags", + "memchr", + "unicase", ] [[package]] -name = "r-efi" -version = "5.3.0" +name = "pulldown-cmark-to-cmark" +version = "21.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "e5b6a0769a491a08b31ea5c62494a8f144ee0987d86d670a8af4df1e1b7cde75" +dependencies = [ + "pulldown-cmark", +] [[package]] -name = "rand" -version = "0.8.5" +name = "quote" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ - "libc", - "rand_chacha", - "rand_core", + "proc-macro2", ] [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "r-efi" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand_core" @@ -1603,9 +1517,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc_version" @@ -1618,22 +1532,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustls" -version = "0.23.28" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7160e3e10bf4535308537f3c4e1641468cd0e485175d6163087c0393c7d46643" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "log", "once_cell", @@ -1656,15 +1570,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - [[package]] name = "rustls-pki-types" version = "1.12.0" @@ -1676,9 +1581,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -1757,9 +1662,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ "itoa", "memchr", @@ -1779,9 +1684,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" dependencies = [ "serde", ] @@ -1806,9 +1711,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -1827,12 +1732,12 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1991,19 +1896,21 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", "signal-hook-registry", + "slab", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2040,9 +1947,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -2053,47 +1960,43 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" dependencies = [ "serde", ] [[package]] -name = "toml_edit" -version = "0.22.27" +name = "toml_parser" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" dependencies = [ - "indexmap 2.10.0", - "serde", - "serde_spanned", - "toml_datetime", "winnow", ] [[package]] name = "tonic" -version = "0.12.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +checksum = "308e1db96abdccdf0a9150fb69112bf6ea72640e0bd834ef0c4a618ccc8c8ddc" dependencies = [ - "async-stream", "async-trait", - "axum 0.7.9", + "axum", "base64", "bytes", "flate2", @@ -2106,14 +2009,13 @@ dependencies = [ "hyper-util", "percent-encoding", "pin-project", - "prost", "rustls-native-certs", - "rustls-pemfile", "socket2", + "sync_wrapper", "tokio", "tokio-rustls", "tokio-stream", - "tower 0.4.13", + "tower", "tower-layer", "tower-service", "tracing", @@ -2121,36 +2023,41 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.12.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +checksum = "18262cdd13dec66e8e3f2e3fe535e4b2cc706fab444a7d3678d75d8ac2557329" dependencies = [ "prettyplease", "proc-macro2", - "prost-build", - "prost-types", "quote", "syn", ] [[package]] -name = "tower" -version = "0.4.13" +name = "tonic-prost" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "2d8b5b7a44512c59f5ad45e0c40e53263cbbf4426d74fe6b569e04f1d4206e9c" dependencies = [ - "futures-core", - "futures-util", - "indexmap 1.9.3", - "pin-project", - "pin-project-lite", - "rand", - "slab", - "tokio", - "tokio-util", - "tower-layer", - "tower-service", - "tracing", + "bytes", + "prost", + "tonic", +] + +[[package]] +name = "tonic-prost-build" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "114cca66d757d72422ef8cccf8be3065321860ac9fa4be73aab37a8a20a9a805" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn", + "tempfile", + "tonic-build", ] [[package]] @@ -2161,9 +2068,12 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", + "indexmap", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -2219,6 +2129,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -2353,7 +2269,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -2374,10 +2290,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -2486,12 +2403,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" -dependencies = [ - "memchr", -] +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" [[package]] name = "wit-bindgen-rt" @@ -2544,26 +2458,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zerofrom" version = "0.1.6" @@ -2618,9 +2512,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "bdbb9122ea75b11bf96e7492afb723e8a7fbe12c67417aa95e7e3d18144d37cd" dependencies = [ "yoke", "zerofrom", diff --git a/Cargo.toml b/Cargo.toml index c39680fc..213fc8b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,25 +7,29 @@ edition = "2021" axum = { version = "0.8", features = ["macros"] } base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } -defguard_wireguard_rs = { git = "https://github.com/DefGuard/wireguard-rs.git", rev = "v0.7.5" } +defguard_wireguard_rs = "0.7.5" env_logger = "0.11" gethostname = "1.0" ipnetwork = "0.21" libc = { version = "0.2", default-features = false } log = "0.4" -prost = "0.13" +prost = "0.14" serde = { version = "1.0", features = ["derive"] } syslog = "7.0" thiserror = "2.0" tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] } tokio-stream = { version = "0.1", features = [] } -toml = { version = "0.8", default-features = false, features = ["parse"] } -tonic = { version = "0.12", default-features = false, features = [ +toml = { version = "0.9", default-features = false, features = [ + "parse", + "serde", +] } +tonic = { version = "0.14", default-features = false, features = [ "codegen", "gzip", - "prost", "tls-native-roots", + "tls-ring", ] } +tonic-prost = "0.14" [target.'cfg(target_os = "linux")'.dependencies] nftnl = { git = "https://github.com/DefGuard/nftnl-rs.git", rev = "1a1147271f43b9d7182a114bb056a5224c35d38f" } @@ -36,15 +40,15 @@ nix = { version = "0.30", default-features = false, features = ["ioctl"] } [dev-dependencies] tokio = { version = "1", features = ["io-std", "io-util"] } -tonic = { version = "0.12", default-features = false, features = [ +tonic = { version = "0.14", default-features = false, features = [ "codegen", - "prost", + "router", "transport", ] } x25519-dalek = { version = "2.0", features = ["getrandom", "static_secrets"] } [build-dependencies] -tonic-build = { version = "0.12" } +tonic-prost-build = "0.14" vergen-git2 = { version = "1.0", features = ["build"] } [profile.release] diff --git a/build.rs b/build.rs index 74db20ad..e9fddc48 100644 --- a/build.rs +++ b/build.rs @@ -5,18 +5,17 @@ fn main() -> Result<(), Box> { let git2 = Git2Builder::default().branch(true).sha(true).build()?; Emitter::default().add_instructions(&git2)?.emit()?; - // compiling protos using path on build time - let mut config = tonic_build::Config::new(); - // enable optional fields - config.protoc_arg("--experimental_allow_proto3_optional"); - tonic_build::configure().compile_protos_with_config( - config, - &[ - "proto/wireguard/gateway.proto", - "proto/enterprise/firewall/firewall.proto", - ], - &["proto/wireguard", "proto/enterprise/firewall"], - )?; + tonic_prost_build::configure() + // enable optional fields + .protoc_arg("--experimental_allow_proto3_optional") + // compiling protos using path on build time + .compile_protos( + &[ + "proto/wireguard/gateway.proto", + "proto/enterprise/firewall/firewall.proto", + ], + &["proto/wireguard", "proto/enterprise/firewall"], + )?; println!("cargo:rerun-if-changed=proto"); Ok(()) } diff --git a/src/enterprise/firewall/packetfilter/api.rs b/src/enterprise/firewall/packetfilter/api.rs index 48d9e202..ffa26c25 100644 --- a/src/enterprise/firewall/packetfilter/api.rs +++ b/src/enterprise/firewall/packetfilter/api.rs @@ -34,7 +34,7 @@ impl FirewallManagementApi for FirewallApi { let mut ioc_trans = IocTrans::new(elements.as_mut_slice()); // This will create an anchor if it doesn't exist. unsafe { - pf_begin(self.fd(), &mut ioc_trans)?; + pf_begin(self.fd(), &raw mut ioc_trans)?; } let ticket = elements[0].ticket; @@ -46,7 +46,7 @@ impl FirewallManagementApi for FirewallApi { debug!("Rollback pf transaction."); // Rule cannot be added, so rollback. unsafe { - pf_rollback(self.fd(), &mut ioc_trans)?; + pf_rollback(self.fd(), &raw mut ioc_trans)?; return Err(FirewallError::TransactionFailed(err.to_string())); } } @@ -57,7 +57,7 @@ impl FirewallManagementApi for FirewallApi { debug!("Rollback pf transaction."); // Rule cannot be added, so rollback. unsafe { - pf_rollback(self.fd(), &mut ioc_trans)?; + pf_rollback(self.fd(), &raw mut ioc_trans)?; return Err(FirewallError::TransactionFailed(err.to_string())); } } @@ -66,7 +66,7 @@ impl FirewallManagementApi for FirewallApi { // Commit transaction. debug!("Commit pf transaction."); unsafe { - pf_commit(self.file.as_raw_fd(), &mut ioc_trans).unwrap(); + pf_commit(self.file.as_raw_fd(), &raw mut ioc_trans).unwrap(); } Ok(()) @@ -75,8 +75,8 @@ impl FirewallManagementApi for FirewallApi { /// Setup Network Address Translation using POSTROUTING chain rules fn setup_nat( &mut self, - masquerade_enabled: bool, - snat_bindings: &[SnatBinding], + _masquerade_enabled: bool, + _snat_bindings: &[SnatBinding], ) -> Result<(), FirewallError> { Ok(()) } diff --git a/src/enterprise/firewall/packetfilter/calls.rs b/src/enterprise/firewall/packetfilter/calls.rs index e81b7312..e4f2054e 100644 --- a/src/enterprise/firewall/packetfilter/calls.rs +++ b/src/enterprise/firewall/packetfilter/calls.rs @@ -115,14 +115,14 @@ impl AddrWrap { #[must_use] fn with_interface(ifname: &str) -> Self { let mut uninit = MaybeUninit::::zeroed(); - let self_ptr = uninit.as_mut_ptr(); let len = ifname.len().min(IFNAMSIZ - 1); unsafe { - (*self_ptr).v.ifname[..len].copy_from_slice(&ifname.as_bytes()[..len]); + let self_ptr = &mut *uninit.as_mut_ptr(); + self_ptr.v.ifname[..len].copy_from_slice(&ifname.as_bytes()[..len]); // Probably, this is needed only for pfctl to omit displaying number of bits. // FIXME: Fill all bytes for IPv6. - (*self_ptr).v.a.mask[..4].fill(255); - (*self_ptr).r#type = AddrType::DynIftl; + self_ptr.v.a.mask[..4].fill(255); + self_ptr.r#type = AddrType::DynIftl; } unsafe { uninit.assume_init() } @@ -211,7 +211,7 @@ struct TailQueue { impl TailQueue { fn init(&mut self) { self.tqh_first = ptr::null_mut(); - self.tqh_last = &mut self.tqh_first; + self.tqh_last = &raw mut self.tqh_first; } } @@ -332,13 +332,14 @@ impl Pool { /// Insert `PoolAddr` at the end of the list. Take ownership of the given `PoolAddr`. pub(super) fn insert_pool_addr(&mut self, mut pool_addr: PoolAddr) { // TODO: Traverse tail queue; for now assume empty tail queue. - if !self.list.tqh_first.is_null() { - panic!("Expected one entry in PoolAddr TailQueue."); - } - self.list.tqh_first = &mut pool_addr; - self.list.tqh_last = &mut pool_addr.entries.tqe_next; + assert!( + self.list.tqh_first.is_null(), + "Expected one entry in PoolAddr TailQueue." + ); + self.list.tqh_first = &raw mut pool_addr; + self.list.tqh_last = &raw mut pool_addr.entries.tqe_next; pool_addr.entries.tqe_next = ptr::null_mut(); - pool_addr.entries.tqe_prev = &mut self.list.tqh_first; + pool_addr.entries.tqe_prev = &raw mut self.list.tqh_first; } } @@ -554,51 +555,52 @@ pub(super) struct Rule { impl Rule { pub(super) fn from_pf_rule(pf_rule: &PacketFilterRule) -> Self { let mut uninit = MaybeUninit::::zeroed(); - let self_ptr = uninit.as_mut_ptr(); unsafe { + let self_ptr = &mut *uninit.as_mut_ptr(); + if let Some(from) = pf_rule.from { - (*self_ptr).src = RuleAddr::new(from, pf_rule.from_port); + self_ptr.src = RuleAddr::new(from, pf_rule.from_port); } if let Some(to) = pf_rule.to { - (*self_ptr).dst = RuleAddr::new(to, pf_rule.to_port); + self_ptr.dst = RuleAddr::new(to, pf_rule.to_port); } if let Some(interface) = &pf_rule.interface { let len = interface.len().min(IFNAMSIZ - 1); - (*self_ptr).ifname[..len].copy_from_slice(&interface.as_bytes()[..len]); + self_ptr.ifname[..len].copy_from_slice(&interface.as_bytes()[..len]); } if let Some(label) = &pf_rule.label { let len = label.len().min(PF_RULE_LABEL_SIZE - 1); - (*self_ptr).label[..len].copy_from_slice(&label.as_bytes()[..len]); + self_ptr.label[..len].copy_from_slice(&label.as_bytes()[..len]); } // Don't use routing tables. #[cfg(any(target_os = "freebsd", target_os = "netbsd"))] { - (*self_ptr).rtableid = -1; + self_ptr.rtableid = -1; } #[cfg(target_os = "macos")] { - (*self_ptr).rtableid = 0; + self_ptr.rtableid = 0; } - (*self_ptr).action = pf_rule.action; - (*self_ptr).direction = pf_rule.direction; - (*self_ptr).log = pf_rule.log; - (*self_ptr).quick = pf_rule.quick; + self_ptr.action = pf_rule.action; + self_ptr.direction = pf_rule.direction; + self_ptr.log = pf_rule.log; + self_ptr.quick = pf_rule.quick; - (*self_ptr).keep_state = pf_rule.state; + self_ptr.keep_state = pf_rule.state; let af = pf_rule.address_family(); - (*self_ptr).af = af; + self_ptr.af = af; #[cfg(target_os = "macos")] { - (*self_ptr).rpool.af = af; + self_ptr.rpool.af = af; } - (*self_ptr).proto = pf_rule.proto as u8; - (*self_ptr).flags = pf_rule.tcp_flags; - (*self_ptr).flagset = pf_rule.tcp_flags_set; + self_ptr.proto = pf_rule.proto as u8; + self_ptr.flags = pf_rule.tcp_flags; + self_ptr.flagset = pf_rule.tcp_flags_set; - (*self_ptr).rpool.list.init(); + self_ptr.rpool.list.init(); uninit.assume_init() } @@ -658,13 +660,13 @@ impl IocRule { #[must_use] pub(super) fn with_rule(anchor: &str, rule: Rule) -> Self { let mut uninit = MaybeUninit::::zeroed(); - let self_ptr = uninit.as_mut_ptr(); // Copy anchor name. let len = anchor.len().min(MAXPATHLEN - 1); unsafe { - (*self_ptr).anchor[..len].copy_from_slice(&anchor.as_bytes()[..len]); - (*self_ptr).rule = rule; + let self_ptr = &mut *uninit.as_mut_ptr(); + self_ptr.anchor[..len].copy_from_slice(&anchor.as_bytes()[..len]); + self_ptr.rule = rule; } unsafe { uninit.assume_init() } @@ -689,12 +691,12 @@ impl IocPoolAddr { #[must_use] pub(super) fn new(anchor: &str) -> Self { let mut uninit = MaybeUninit::::zeroed(); - let self_ptr = uninit.as_mut_ptr(); // Copy anchor name. let len = anchor.len().min(MAXPATHLEN - 1); unsafe { - (*self_ptr).anchor[..len].copy_from_slice(&anchor.as_bytes()[..len]); + let self_ptr = &mut *uninit.as_mut_ptr(); + self_ptr.anchor[..len].copy_from_slice(&anchor.as_bytes()[..len]); } unsafe { uninit.assume_init() } @@ -704,10 +706,10 @@ impl IocPoolAddr { #[must_use] pub(super) fn with_pool_addr(addr: PoolAddr, ticket: c_uint) -> Self { let mut uninit = MaybeUninit::::zeroed(); - let self_ptr = uninit.as_mut_ptr(); unsafe { - (*self_ptr).ticket = ticket; - (*self_ptr).addr = addr; + let self_ptr = &mut *uninit.as_mut_ptr(); + self_ptr.ticket = ticket; + self_ptr.addr = addr; } unsafe { uninit.assume_init() } @@ -726,13 +728,13 @@ impl IocTransElement { #[must_use] pub(super) fn new(ruleset: RuleSet, anchor: &str) -> Self { let mut uninit = MaybeUninit::::zeroed(); - let self_ptr = uninit.as_mut_ptr(); // Copy anchor name. let len = anchor.len().min(MAXPATHLEN - 1); unsafe { - (*self_ptr).rs_num = ruleset; - (*self_ptr).anchor[..len].copy_from_slice(&anchor.as_bytes()[..len]); + let self_ptr = &mut *uninit.as_mut_ptr(); + self_ptr.rs_num = ruleset; + self_ptr.anchor[..len].copy_from_slice(&anchor.as_bytes()[..len]); } unsafe { uninit.assume_init() } diff --git a/src/enterprise/firewall/packetfilter/mod.rs b/src/enterprise/firewall/packetfilter/mod.rs index 6495ceb6..ad439b13 100644 --- a/src/enterprise/firewall/packetfilter/mod.rs +++ b/src/enterprise/firewall/packetfilter/mod.rs @@ -42,7 +42,7 @@ impl FirewallApi { let mut ioc = IocPoolAddr::new(anchor); unsafe { - pf_begin_addrs(self.fd(), &mut ioc)?; + pf_begin_addrs(self.fd(), &raw mut ioc)?; } Ok(ioc.ticket) @@ -58,7 +58,7 @@ impl FirewallApi { let mut ioc = IocRule::with_rule(anchor, Rule::from_pf_rule(&rule)); ioc.ticket = ticket; ioc.pool_ticket = pool_ticket; - if let Err(err) = unsafe { pf_add_rule(self.fd(), &mut ioc) } { + if let Err(err) = unsafe { pf_add_rule(self.fd(), &raw mut ioc) } { error!("Packet filter rule {rule} can't be added."); return Err(err.into()); } @@ -82,7 +82,7 @@ impl FirewallApi { ioc.action = Change::None; ioc.ticket = ticket; ioc.pool_ticket = pool_ticket; - if let Err(err) = unsafe { pf_add_rule(self.fd(), &mut ioc) } { + if let Err(err) = unsafe { pf_add_rule(self.fd(), &raw mut ioc) } { error!("Packet filter rule {rule} can't be added."); return Err(err.into()); } diff --git a/src/gateway.rs b/src/gateway.rs index ce664717..9c515d2e 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -413,7 +413,9 @@ impl Gateway { } // process received firewall config unless firewall management is disabled - if !self.config.disable_firewall_management { + if self.config.disable_firewall_management { + debug!("Firewall management is disabled. Skipping updating firewall configuration"); + } else { let new_firewall_configuration = if let Some(firewall_config) = new_configuration.firewall_config { Some(FirewallConfig::from_proto(firewall_config)?) @@ -422,8 +424,6 @@ impl Gateway { }; self.process_firewall_changes(new_firewall_configuration.as_ref())?; - } else { - debug!("Firewall management is disabled. Skipping updating firewall configuration"); } Ok(()) From 4a6d87b7247554ccf2feccad798ed47844fa3a64 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Wed, 6 Aug 2025 09:55:44 +0200 Subject: [PATCH 05/25] Merge adjacent IP subnets into ranges for nftables (#182) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * restore merging of ips * merge adjacent subnets for nft (#185) * merge adjacent elements * update new tests * linter fixes * linter fix * review fixes --------- Co-authored-by: Maciek <19913370+wojcik91@users.noreply.github.com> Co-authored-by: Maciej Wójcik Co-authored-by: Maciej Wójcik --- src/enterprise/firewall/iprange.rs | 17 + src/enterprise/firewall/mod.rs | 99 +++++ src/enterprise/firewall/nftables/mod.rs | 408 ++++++++++++++++-- src/enterprise/firewall/nftables/netfilter.rs | 83 +--- 4 files changed, 489 insertions(+), 118 deletions(-) diff --git a/src/enterprise/firewall/iprange.rs b/src/enterprise/firewall/iprange.rs index 33eab39d..b1b4acd6 100644 --- a/src/enterprise/firewall/iprange.rs +++ b/src/enterprise/firewall/iprange.rs @@ -76,6 +76,23 @@ impl IpAddrRange { Self::V6(_) => true, } } + + /// Returns the start of the range. + pub fn start(&self) -> IpAddr { + match self { + Self::V4(range) => IpAddr::V4(*range.start()), + Self::V6(range) => IpAddr::V6(*range.start()), + } + } + + /// Returns the end of the range. + /// If the range is empty, returns the start of the range. + pub fn end(&self) -> IpAddr { + match self { + Self::V4(range) => IpAddr::V4(*range.end()), + Self::V6(range) => IpAddr::V6(*range.end()), + } + } } impl Iterator for IpAddrRange { diff --git a/src/enterprise/firewall/mod.rs b/src/enterprise/firewall/mod.rs index 5f564697..ef4304d2 100644 --- a/src/enterprise/firewall/mod.rs +++ b/src/enterprise/firewall/mod.rs @@ -26,6 +26,24 @@ pub(crate) enum Address { } impl Address { + // FIXME: remove after merging nft hotfix into dev + #[allow(dead_code)] + pub fn first(&self) -> IpAddr { + match self { + Address::Network(network) => network.ip(), + Address::Range(range) => range.start(), + } + } + + // FIXME: remove after merging nft hotfix into dev + #[allow(dead_code)] + pub fn last(&self) -> IpAddr { + match self { + Address::Network(network) => max_address(network), + Address::Range(range) => range.end(), + } + } + pub fn from_proto(ip: &proto::enterprise::firewall::IpAddress) -> Result { match &ip.address { Some(proto::enterprise::firewall::ip_address::Address::Ip(ip)) => { @@ -322,3 +340,84 @@ pub enum FirewallError { #[error("Firewall transaction failed: {0}")] TransactionFailed(String), } + +/// Get the max address in a network. +/// +/// - In IPv4 this is the broadcast address. +/// - In IPv6 this is just the last address in the network. +pub fn max_address(network: &IpNetwork) -> IpAddr { + match network { + IpNetwork::V4(network) => { + let addr = network.ip().to_bits(); + let mask = network.mask().to_bits(); + + IpAddr::V4(Ipv4Addr::from(addr | !mask)) + } + IpNetwork::V6(network) => { + let addr = network.ip().to_bits(); + let mask = network.mask().to_bits(); + + IpAddr::V6(Ipv6Addr::from(addr | !mask)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_max_address_ipv4_24() { + let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.0/24").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 255))); + } + + #[test] + fn test_max_address_ipv4_16() { + let network = IpNetwork::V4(Ipv4Network::from_str("10.1.0.0/16").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(10, 1, 255, 255))); + } + + #[test] + fn test_max_address_ipv4_8() { + let network = IpNetwork::V4(Ipv4Network::from_str("172.16.0.0/8").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(172, 255, 255, 255))); + } + + #[test] + fn test_max_address_ipv4_32() { + let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.1/32").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))); + } + + #[test] + fn test_max_address_ipv6_64() { + let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::/64").unwrap()); + let max = max_address(&network); + assert_eq!( + max, + IpAddr::V6(Ipv6Addr::from_str("2001:db8::ffff:ffff:ffff:ffff").unwrap()) + ); + } + + #[test] + fn test_max_address_ipv6_128() { + let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::1/128").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V6(Ipv6Addr::from_str("2001:db8::1").unwrap())); + } + + #[test] + fn test_max_address_ipv6_48() { + let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8:1234::/48").unwrap()); + let max = max_address(&network); + assert_eq!( + max, + IpAddr::V6(Ipv6Addr::from_str("2001:db8:1234:ffff:ffff:ffff:ffff:ffff").unwrap()) + ); + } +} diff --git a/src/enterprise/firewall/nftables/mod.rs b/src/enterprise/firewall/nftables/mod.rs index d5e5574b..614b9831 100644 --- a/src/enterprise/firewall/nftables/mod.rs +++ b/src/enterprise/firewall/nftables/mod.rs @@ -1,6 +1,9 @@ pub mod netfilter; -use std::sync::atomic::{AtomicU32, Ordering}; +use std::{ + net::{IpAddr, Ipv4Addr, Ipv6Addr}, + sync::atomic::{AtomicU32, Ordering}, +}; use netfilter::{ allow_established_traffic, apply_filter_rules, drop_table, ignore_unrelated_traffic, @@ -10,8 +13,10 @@ use nftnl::Batch; use super::{ api::{FirewallApi, FirewallManagementApi}, + iprange::IpAddrRangeError, Address, FirewallError, FirewallRule, Policy, Port, Protocol, }; +use crate::enterprise::firewall::iprange::IpAddrRange; static SET_ID_COUNTER: AtomicU32 = AtomicU32::new(0); @@ -51,29 +56,110 @@ struct FilterRule<'a> { negated_iifname: bool, } +/// Merges any contiguous subets or addres ranges into an address range. +/// +/// This reflects the way `nft` CLI handles such cases. +/// Otherwise first address in any subnet after the first is not matched. +/// For example if we use `172.30.0.2/31, 172.30.0.4/31` as `saddr` in a rule, +/// then 172.30.0.4 will not be matched. +fn merge_addrs(addrs: Vec
) -> Result, IpAddrRangeError> { + debug!("Merging any contiguous subnets and ranges found within address list: {addrs:?}"); + + if addrs.is_empty() { + debug!("No addresses provided, returning empty vector."); + return Ok(Vec::new()); + } + + let mut merged_addrs = Vec::new(); + let mut current_address = None; + + // we can assume addresses coming from the core + // are already sorted and non-overlapping + for next_address in addrs { + match ¤t_address { + None => { + debug!("Initializing current address with: {next_address:?}"); + current_address = Some(next_address); + } + Some(previous_address) => { + let previous_range_start = previous_address.first(); + let previous_range_end = previous_address.last(); + let next_ip = next_ip(previous_range_end); + + let next_range_start = next_address.first(); + let next_range_end = next_address.last(); + + // check if range is adjacent to current address + if next_range_start == next_ip { + // replace current address with a combined range + debug!("Merging {next_address:?} with {current_address:?}"); + current_address = Some(Address::Range(IpAddrRange::new( + previous_range_start, + next_range_end, + )?)); + } else { + // push previous address to result and replace with next address + merged_addrs.push(previous_address.clone()); + current_address = Some(next_address); + }; + } + } + } + + // push last remaining address to results + if let Some(address) = current_address { + debug!("Pushing last remaining address into results: {address:?}"); + merged_addrs.push(address) + } + + debug!("Prepared addresses: {merged_addrs:?}"); + + Ok(merged_addrs) +} + +/// Returns the next IP address in sequence, handling overflow via wrapping +fn next_ip(ip: IpAddr) -> IpAddr { + match ip { + IpAddr::V4(ipv4) => { + let ip_u32 = ipv4.to_bits(); + let next_ip_u32 = ip_u32.wrapping_add(1); + IpAddr::V4(Ipv4Addr::from(next_ip_u32)) + } + IpAddr::V6(ipv6) => { + let ip_u128 = ipv6.to_bits(); + let next_ip_u128 = ip_u128.wrapping_add(1); + IpAddr::V6(Ipv6Addr::from(next_ip_u128)) + } + } +} + impl FirewallApi { fn add_rule(&mut self, rule: FirewallRule) -> Result<(), FirewallError> { debug!("Applying the following Defguard ACL rule: {rule:?}"); - let mut rules = Vec::new(); let batch = if let Some(ref mut batch) = self.batch { batch } else { return Err(FirewallError::TransactionNotStarted); }; + let mut filter_rules = Vec::new(); debug!( "The rule will be split into multiple nftables rules based on the specified \ destination ports and protocols." ); + + let source_addrs = merge_addrs(rule.source_addrs)?; + let dest_addrs = merge_addrs(rule.destination_addrs)?; + if rule.destination_ports.is_empty() { debug!( "No destination ports specified, applying single aggregate nftables rule for \ every protocol." ); let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, - protocols: rule.protocols, + src_ips: &source_addrs, + dest_ips: &dest_addrs, + protocols: rule.protocols.clone(), action: rule.verdict, counter: true, defguard_rule_id: rule.id, @@ -81,47 +167,35 @@ impl FirewallApi { comment: rule.comment.clone(), ..Default::default() }; - rules.push(rule); + filter_rules.push(rule); } else if !rule.protocols.is_empty() { debug!( "Destination ports and protocols specified, applying individual nftables rules \ for each protocol." ); - for protocol in rule.protocols { + for protocol in rule.protocols.clone() { debug!("Applying rule for protocol: {protocol:?}"); + let mut filter_rule = FilterRule { + src_ips: &source_addrs, + dest_ips: &dest_addrs, + protocols: vec![protocol], + action: rule.verdict, + counter: true, + defguard_rule_id: rule.id, + v4: rule.ipv4, + comment: rule.comment.clone(), + ..Default::default() + }; if protocol.supports_ports() { debug!("Protocol supports ports, rule."); - let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, - dest_ports: &rule.destination_ports, - protocols: vec![protocol], - action: rule.verdict, - counter: true, - defguard_rule_id: rule.id, - v4: rule.ipv4, - comment: rule.comment.clone(), - ..Default::default() - }; - rules.push(rule); + filter_rule.dest_ports = &rule.destination_ports; } else { debug!( "Protocol does not support ports, applying nftables rule and ignoring \ destination ports." ); - let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, - protocols: vec![protocol], - action: rule.verdict, - counter: true, - defguard_rule_id: rule.id, - v4: rule.ipv4, - comment: rule.comment.clone(), - ..Default::default() - }; - rules.push(rule); } + filter_rules.push(filter_rule); } } else { debug!( @@ -131,8 +205,8 @@ impl FirewallApi { for protocol in [Protocol::Tcp, Protocol::Udp] { debug!("Applying nftables rule for protocol: {protocol:?}"); let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, + src_ips: &source_addrs, + dest_ips: &dest_addrs, dest_ports: &rule.destination_ports, protocols: vec![protocol], action: rule.verdict, @@ -142,11 +216,11 @@ impl FirewallApi { comment: rule.comment.clone(), ..Default::default() }; - rules.push(rule); + filter_rules.push(rule); } } - apply_filter_rules(rules, batch, &self.ifname)?; + apply_filter_rules(filter_rules, batch, &self.ifname)?; debug!( "Applied firewall rules for Defguard ACL rule ID: {}", @@ -257,3 +331,265 @@ impl FirewallManagementApi for FirewallApi { } } } + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use ipnetwork::IpNetwork; + + use super::*; + + #[test] + fn test_sorting() { + let mut addrs = vec![ + Address::Network(IpNetwork::from_str("10.10.10.11/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.12/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.10/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.11/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.10/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.12/32").unwrap()), + ]; + + addrs.sort_by(|a, b| { + a.first() + .partial_cmp(&b.first()) + .unwrap_or(std::cmp::Ordering::Equal) + }); + + assert_eq!( + addrs, + vec![ + Address::Network(IpNetwork::from_str("10.10.10.10/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.11/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.12/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.10/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.11/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.12/32").unwrap()), + ] + ); + + let _prepared_addrs = merge_addrs(addrs).unwrap(); + } + + #[test] + fn test_merge_addrs_empty() { + let addrs: Vec
= vec![]; + let result = merge_addrs(addrs).unwrap(); + assert!(result.is_empty()); + } + + #[test] + fn test_merge_addrs_single_address() { + let addrs = vec![Address::Network( + IpNetwork::from_str("192.168.1.10/32").unwrap(), + )]; + let result = merge_addrs(addrs.clone()).unwrap(); + + assert_eq!(result, addrs); + } + + #[test] + fn test_merge_addrs_adjacent_ranges() { + let addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.20").unwrap(), + ) + .unwrap(), + ), + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.21").unwrap(), + IpAddr::from_str("192.168.1.30").unwrap(), + ) + .unwrap(), + ), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("192.168.1.30").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_adjacent_single_addresses() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.10/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.11/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.12/32").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("192.168.1.12").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_non_adjacent_ranges() { + let addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.20").unwrap(), + ) + .unwrap(), + ), + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.30").unwrap(), + IpAddr::from_str("192.168.1.40").unwrap(), + ) + .unwrap(), + ), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 2); + if let Address::Range(range1) = &result[0] { + assert_eq!(range1.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range1.end(), IpAddr::from_str("192.168.1.20").unwrap()); + } else { + panic!("Expected Address::Range"); + } + if let Address::Range(range2) = &result[1] { + assert_eq!(range2.start(), IpAddr::from_str("192.168.1.30").unwrap()); + assert_eq!(range2.end(), IpAddr::from_str("192.168.1.40").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_mixed_networks_and_ranges() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.10/32").unwrap()), + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.11").unwrap(), + IpAddr::from_str("192.168.1.15").unwrap(), + ) + .unwrap(), + ), + Address::Network(IpNetwork::from_str("192.168.1.16/32").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("192.168.1.16").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_non_adjacent_singles() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.10/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.11/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.15/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.20/32").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + // These should result in 3 separate ranges: 10-11, 15, 20 + let expected_addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.11").unwrap(), + ) + .unwrap(), + ), + Address::Network(IpNetwork::from_str("192.168.1.15/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.20/32").unwrap()), + ]; + assert_eq!(result, expected_addrs); + } + + #[test] + fn test_merge_addrs_ipv6() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("2001:db8::1/128").unwrap()), + Address::Network(IpNetwork::from_str("2001:db8::2/128").unwrap()), + Address::Network(IpNetwork::from_str("2001:db8::3/128").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("2001:db8::1").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("2001:db8::3").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_next_ip_ipv4() { + assert_eq!( + next_ip(IpAddr::from_str("192.168.1.10").unwrap()), + IpAddr::from_str("192.168.1.11").unwrap() + ); + + // Test overflow + assert_eq!( + next_ip(IpAddr::from_str("255.255.255.255").unwrap()), + IpAddr::from_str("0.0.0.0").unwrap() + ); + } + + #[test] + fn test_next_ip_ipv6() { + assert_eq!( + next_ip(IpAddr::from_str("2001:db8::1").unwrap()), + IpAddr::from_str("2001:db8::2").unwrap() + ); + + // Test overflow + assert_eq!( + next_ip(IpAddr::from_str("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap()), + IpAddr::from_str("::").unwrap() + ); + } + + #[test] + fn test_merge_addrs_gap() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.1/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.100/32").unwrap()), + ]; + let result = merge_addrs(addrs.clone()).unwrap(); + + // Should not merge since there's a gap + assert_eq!(result, addrs); + + let addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.20").unwrap(), + ) + .unwrap(), + ), + Address::Network(IpNetwork::from_str("192.168.1.100/32").unwrap()), + ]; + let result = merge_addrs(addrs.clone()).unwrap(); + + // Should not merge since there's a gap + assert_eq!(result, addrs); + } +} diff --git a/src/enterprise/firewall/nftables/netfilter.rs b/src/enterprise/firewall/nftables/netfilter.rs index 7174d9b0..b5d8505c 100644 --- a/src/enterprise/firewall/nftables/netfilter.rs +++ b/src/enterprise/firewall/nftables/netfilter.rs @@ -1,13 +1,8 @@ -#[cfg(test)] -use std::str::FromStr; use std::{ ffi::{CStr, CString}, net::{IpAddr, Ipv4Addr, Ipv6Addr}, }; -use ipnetwork::IpNetwork; -#[cfg(test)] -use ipnetwork::{Ipv4Network, Ipv6Network}; use nftnl::{ expr::{Expression, InterfaceName}, nft_expr, nftnl_sys, @@ -16,7 +11,7 @@ use nftnl::{ }; use super::{get_set_id, Address, FilterRule, Policy, Port, Protocol, State}; -use crate::enterprise::firewall::{iprange::IpAddrRange, FirewallError}; +use crate::enterprise::firewall::{iprange::IpAddrRange, max_address, FirewallError}; const FILTER_TABLE: &str = "filter"; const NAT_TABLE: &str = "nat"; @@ -809,27 +804,6 @@ fn socket_recv<'a>( } } -/// Get the max address in a network. -/// -/// - In IPv4 this is the broadcast address. -/// - In IPv6 this is just the last address in the network. -fn max_address(network: &IpNetwork) -> IpAddr { - match network { - IpNetwork::V4(network) => { - let addr = network.ip().to_bits(); - let mask = network.mask().to_bits(); - - IpAddr::V4(Ipv4Addr::from(addr | !mask)) - } - IpNetwork::V6(network) => { - let addr = network.ip().to_bits(); - let mask = network.mask().to_bits(); - - IpAddr::V6(Ipv6Addr::from(addr | !mask)) - } - } -} - fn new_anon_set( table: &Table, family: ProtoFamily, @@ -991,59 +965,4 @@ mod tests { increment_bytes(&mut ip); assert_eq!(ip, [0, 0, 0, 0, 0, 0, 0, 0]); } - - #[test] - fn test_max_address_ipv4_24() { - let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.0/24").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 255))); - } - - #[test] - fn test_max_address_ipv4_16() { - let network = IpNetwork::V4(Ipv4Network::from_str("10.1.0.0/16").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(10, 1, 255, 255))); - } - - #[test] - fn test_max_address_ipv4_8() { - let network = IpNetwork::V4(Ipv4Network::from_str("172.16.0.0/8").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(172, 255, 255, 255))); - } - - #[test] - fn test_max_address_ipv4_32() { - let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.1/32").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))); - } - - #[test] - fn test_max_address_ipv6_64() { - let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::/64").unwrap()); - let max = max_address(&network); - assert_eq!( - max, - IpAddr::V6(Ipv6Addr::from_str("2001:db8::ffff:ffff:ffff:ffff").unwrap()) - ); - } - - #[test] - fn test_max_address_ipv6_128() { - let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::1/128").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V6(Ipv6Addr::from_str("2001:db8::1").unwrap())); - } - - #[test] - fn test_max_address_ipv6_48() { - let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8:1234::/48").unwrap()); - let max = max_address(&network); - assert_eq!( - max, - IpAddr::V6(Ipv6Addr::from_str("2001:db8:1234:ffff:ffff:ffff:ffff:ffff").unwrap()) - ); - } } From fa0012ea9caabdd7f43ac2735cb1edeff4e12447 Mon Sep 17 00:00:00 2001 From: Maciek <19913370+wojcik91@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:52:27 +0200 Subject: [PATCH 06/25] Merge adjacent IP subnets into ranges for nftables (#182) (#187) * restore merging of ips * merge adjacent subnets for nft (#185) * merge adjacent elements * update new tests * linter fixes * linter fix * review fixes --------- Co-authored-by: Aleksander <170264518+t-aleksander@users.noreply.github.com> --- src/enterprise/firewall/iprange.rs | 17 + src/enterprise/firewall/mod.rs | 99 +++++ src/enterprise/firewall/nftables/mod.rs | 408 ++++++++++++++++-- src/enterprise/firewall/nftables/netfilter.rs | 83 +--- 4 files changed, 489 insertions(+), 118 deletions(-) diff --git a/src/enterprise/firewall/iprange.rs b/src/enterprise/firewall/iprange.rs index 33eab39d..b1b4acd6 100644 --- a/src/enterprise/firewall/iprange.rs +++ b/src/enterprise/firewall/iprange.rs @@ -76,6 +76,23 @@ impl IpAddrRange { Self::V6(_) => true, } } + + /// Returns the start of the range. + pub fn start(&self) -> IpAddr { + match self { + Self::V4(range) => IpAddr::V4(*range.start()), + Self::V6(range) => IpAddr::V6(*range.start()), + } + } + + /// Returns the end of the range. + /// If the range is empty, returns the start of the range. + pub fn end(&self) -> IpAddr { + match self { + Self::V4(range) => IpAddr::V4(*range.end()), + Self::V6(range) => IpAddr::V6(*range.end()), + } + } } impl Iterator for IpAddrRange { diff --git a/src/enterprise/firewall/mod.rs b/src/enterprise/firewall/mod.rs index 532dd9f0..4d93ad4c 100644 --- a/src/enterprise/firewall/mod.rs +++ b/src/enterprise/firewall/mod.rs @@ -24,6 +24,24 @@ pub(crate) enum Address { } impl Address { + // FIXME: remove after merging nft hotfix into dev + #[allow(dead_code)] + pub fn first(&self) -> IpAddr { + match self { + Address::Network(network) => network.ip(), + Address::Range(range) => range.start(), + } + } + + // FIXME: remove after merging nft hotfix into dev + #[allow(dead_code)] + pub fn last(&self) -> IpAddr { + match self { + Address::Network(network) => max_address(network), + Address::Range(range) => range.end(), + } + } + pub fn from_proto(ip: &proto::enterprise::firewall::IpAddress) -> Result { match &ip.address { Some(proto::enterprise::firewall::ip_address::Address::Ip(ip)) => { @@ -359,3 +377,84 @@ pub enum FirewallError { #[error("Firewall transaction failed: {0}")] TransactionFailed(String), } + +/// Get the max address in a network. +/// +/// - In IPv4 this is the broadcast address. +/// - In IPv6 this is just the last address in the network. +pub fn max_address(network: &IpNetwork) -> IpAddr { + match network { + IpNetwork::V4(network) => { + let addr = network.ip().to_bits(); + let mask = network.mask().to_bits(); + + IpAddr::V4(Ipv4Addr::from(addr | !mask)) + } + IpNetwork::V6(network) => { + let addr = network.ip().to_bits(); + let mask = network.mask().to_bits(); + + IpAddr::V6(Ipv6Addr::from(addr | !mask)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_max_address_ipv4_24() { + let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.0/24").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 255))); + } + + #[test] + fn test_max_address_ipv4_16() { + let network = IpNetwork::V4(Ipv4Network::from_str("10.1.0.0/16").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(10, 1, 255, 255))); + } + + #[test] + fn test_max_address_ipv4_8() { + let network = IpNetwork::V4(Ipv4Network::from_str("172.16.0.0/8").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(172, 255, 255, 255))); + } + + #[test] + fn test_max_address_ipv4_32() { + let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.1/32").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))); + } + + #[test] + fn test_max_address_ipv6_64() { + let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::/64").unwrap()); + let max = max_address(&network); + assert_eq!( + max, + IpAddr::V6(Ipv6Addr::from_str("2001:db8::ffff:ffff:ffff:ffff").unwrap()) + ); + } + + #[test] + fn test_max_address_ipv6_128() { + let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::1/128").unwrap()); + let max = max_address(&network); + assert_eq!(max, IpAddr::V6(Ipv6Addr::from_str("2001:db8::1").unwrap())); + } + + #[test] + fn test_max_address_ipv6_48() { + let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8:1234::/48").unwrap()); + let max = max_address(&network); + assert_eq!( + max, + IpAddr::V6(Ipv6Addr::from_str("2001:db8:1234:ffff:ffff:ffff:ffff:ffff").unwrap()) + ); + } +} diff --git a/src/enterprise/firewall/nftables/mod.rs b/src/enterprise/firewall/nftables/mod.rs index a5bd53ec..94af5e00 100644 --- a/src/enterprise/firewall/nftables/mod.rs +++ b/src/enterprise/firewall/nftables/mod.rs @@ -1,6 +1,9 @@ pub mod netfilter; -use std::sync::atomic::{AtomicU32, Ordering}; +use std::{ + net::{IpAddr, Ipv4Addr, Ipv6Addr}, + sync::atomic::{AtomicU32, Ordering}, +}; use netfilter::{ allow_established_traffic, apply_filter_rules, drop_table, ignore_unrelated_traffic, @@ -10,8 +13,10 @@ use nftnl::Batch; use super::{ api::{FirewallApi, FirewallManagementApi}, + iprange::IpAddrRangeError, Address, FirewallError, FirewallRule, Policy, Port, Protocol, SnatBinding, }; +use crate::enterprise::firewall::iprange::IpAddrRange; static SET_ID_COUNTER: AtomicU32 = AtomicU32::new(0); @@ -51,29 +56,110 @@ struct FilterRule<'a> { negated_iifname: bool, } +/// Merges any contiguous subets or addres ranges into an address range. +/// +/// This reflects the way `nft` CLI handles such cases. +/// Otherwise first address in any subnet after the first is not matched. +/// For example if we use `172.30.0.2/31, 172.30.0.4/31` as `saddr` in a rule, +/// then 172.30.0.4 will not be matched. +fn merge_addrs(addrs: Vec
) -> Result, IpAddrRangeError> { + debug!("Merging any contiguous subnets and ranges found within address list: {addrs:?}"); + + if addrs.is_empty() { + debug!("No addresses provided, returning empty vector."); + return Ok(Vec::new()); + } + + let mut merged_addrs = Vec::new(); + let mut current_address = None; + + // we can assume addresses coming from the core + // are already sorted and non-overlapping + for next_address in addrs { + match ¤t_address { + None => { + debug!("Initializing current address with: {next_address:?}"); + current_address = Some(next_address); + } + Some(previous_address) => { + let previous_range_start = previous_address.first(); + let previous_range_end = previous_address.last(); + let next_ip = next_ip(previous_range_end); + + let next_range_start = next_address.first(); + let next_range_end = next_address.last(); + + // check if range is adjacent to current address + if next_range_start == next_ip { + // replace current address with a combined range + debug!("Merging {next_address:?} with {current_address:?}"); + current_address = Some(Address::Range(IpAddrRange::new( + previous_range_start, + next_range_end, + )?)); + } else { + // push previous address to result and replace with next address + merged_addrs.push(previous_address.clone()); + current_address = Some(next_address); + }; + } + } + } + + // push last remaining address to results + if let Some(address) = current_address { + debug!("Pushing last remaining address into results: {address:?}"); + merged_addrs.push(address) + } + + debug!("Prepared addresses: {merged_addrs:?}"); + + Ok(merged_addrs) +} + +/// Returns the next IP address in sequence, handling overflow via wrapping +fn next_ip(ip: IpAddr) -> IpAddr { + match ip { + IpAddr::V4(ipv4) => { + let ip_u32 = ipv4.to_bits(); + let next_ip_u32 = ip_u32.wrapping_add(1); + IpAddr::V4(Ipv4Addr::from(next_ip_u32)) + } + IpAddr::V6(ipv6) => { + let ip_u128 = ipv6.to_bits(); + let next_ip_u128 = ip_u128.wrapping_add(1); + IpAddr::V6(Ipv6Addr::from(next_ip_u128)) + } + } +} + impl FirewallApi { fn add_rule(&mut self, rule: FirewallRule) -> Result<(), FirewallError> { debug!("Applying the following Defguard ACL rule: {rule:?}"); - let mut rules = Vec::new(); let batch = if let Some(ref mut batch) = self.batch { batch } else { return Err(FirewallError::TransactionNotStarted); }; + let mut filter_rules = Vec::new(); debug!( "The rule will be split into multiple nftables rules based on the specified \ destination ports and protocols." ); + + let source_addrs = merge_addrs(rule.source_addrs)?; + let dest_addrs = merge_addrs(rule.destination_addrs)?; + if rule.destination_ports.is_empty() { debug!( "No destination ports specified, applying single aggregate nftables rule for \ every protocol." ); let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, - protocols: rule.protocols, + src_ips: &source_addrs, + dest_ips: &dest_addrs, + protocols: rule.protocols.clone(), action: rule.verdict, counter: true, defguard_rule_id: rule.id, @@ -81,47 +167,35 @@ impl FirewallApi { comment: rule.comment.clone(), ..Default::default() }; - rules.push(rule); + filter_rules.push(rule); } else if !rule.protocols.is_empty() { debug!( "Destination ports and protocols specified, applying individual nftables rules \ for each protocol." ); - for protocol in rule.protocols { + for protocol in rule.protocols.clone() { debug!("Applying rule for protocol: {protocol:?}"); + let mut filter_rule = FilterRule { + src_ips: &source_addrs, + dest_ips: &dest_addrs, + protocols: vec![protocol], + action: rule.verdict, + counter: true, + defguard_rule_id: rule.id, + v4: rule.ipv4, + comment: rule.comment.clone(), + ..Default::default() + }; if protocol.supports_ports() { debug!("Protocol supports ports, rule."); - let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, - dest_ports: &rule.destination_ports, - protocols: vec![protocol], - action: rule.verdict, - counter: true, - defguard_rule_id: rule.id, - v4: rule.ipv4, - comment: rule.comment.clone(), - ..Default::default() - }; - rules.push(rule); + filter_rule.dest_ports = &rule.destination_ports; } else { debug!( "Protocol does not support ports, applying nftables rule and ignoring \ destination ports." ); - let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, - protocols: vec![protocol], - action: rule.verdict, - counter: true, - defguard_rule_id: rule.id, - v4: rule.ipv4, - comment: rule.comment.clone(), - ..Default::default() - }; - rules.push(rule); } + filter_rules.push(filter_rule); } } else { debug!( @@ -131,8 +205,8 @@ impl FirewallApi { for protocol in [Protocol::Tcp, Protocol::Udp] { debug!("Applying nftables rule for protocol: {protocol:?}"); let rule = FilterRule { - src_ips: &rule.source_addrs, - dest_ips: &rule.destination_addrs, + src_ips: &source_addrs, + dest_ips: &dest_addrs, dest_ports: &rule.destination_ports, protocols: vec![protocol], action: rule.verdict, @@ -142,11 +216,11 @@ impl FirewallApi { comment: rule.comment.clone(), ..Default::default() }; - rules.push(rule); + filter_rules.push(rule); } } - apply_filter_rules(rules, batch, &self.ifname)?; + apply_filter_rules(filter_rules, batch, &self.ifname)?; debug!( "Applied firewall rules for Defguard ACL rule ID: {}", @@ -250,3 +324,265 @@ impl FirewallManagementApi for FirewallApi { } } } + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use ipnetwork::IpNetwork; + + use super::*; + + #[test] + fn test_sorting() { + let mut addrs = vec![ + Address::Network(IpNetwork::from_str("10.10.10.11/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.12/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.10/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.11/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.10/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.12/32").unwrap()), + ]; + + addrs.sort_by(|a, b| { + a.first() + .partial_cmp(&b.first()) + .unwrap_or(std::cmp::Ordering::Equal) + }); + + assert_eq!( + addrs, + vec![ + Address::Network(IpNetwork::from_str("10.10.10.10/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.11/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.10.12/24").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.10/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.11/32").unwrap()), + Address::Network(IpNetwork::from_str("10.10.11.12/32").unwrap()), + ] + ); + + let _prepared_addrs = merge_addrs(addrs).unwrap(); + } + + #[test] + fn test_merge_addrs_empty() { + let addrs: Vec
= vec![]; + let result = merge_addrs(addrs).unwrap(); + assert!(result.is_empty()); + } + + #[test] + fn test_merge_addrs_single_address() { + let addrs = vec![Address::Network( + IpNetwork::from_str("192.168.1.10/32").unwrap(), + )]; + let result = merge_addrs(addrs.clone()).unwrap(); + + assert_eq!(result, addrs); + } + + #[test] + fn test_merge_addrs_adjacent_ranges() { + let addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.20").unwrap(), + ) + .unwrap(), + ), + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.21").unwrap(), + IpAddr::from_str("192.168.1.30").unwrap(), + ) + .unwrap(), + ), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("192.168.1.30").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_adjacent_single_addresses() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.10/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.11/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.12/32").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("192.168.1.12").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_non_adjacent_ranges() { + let addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.20").unwrap(), + ) + .unwrap(), + ), + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.30").unwrap(), + IpAddr::from_str("192.168.1.40").unwrap(), + ) + .unwrap(), + ), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 2); + if let Address::Range(range1) = &result[0] { + assert_eq!(range1.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range1.end(), IpAddr::from_str("192.168.1.20").unwrap()); + } else { + panic!("Expected Address::Range"); + } + if let Address::Range(range2) = &result[1] { + assert_eq!(range2.start(), IpAddr::from_str("192.168.1.30").unwrap()); + assert_eq!(range2.end(), IpAddr::from_str("192.168.1.40").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_mixed_networks_and_ranges() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.10/32").unwrap()), + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.11").unwrap(), + IpAddr::from_str("192.168.1.15").unwrap(), + ) + .unwrap(), + ), + Address::Network(IpNetwork::from_str("192.168.1.16/32").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("192.168.1.10").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("192.168.1.16").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_merge_addrs_non_adjacent_singles() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.10/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.11/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.15/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.20/32").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + // These should result in 3 separate ranges: 10-11, 15, 20 + let expected_addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.11").unwrap(), + ) + .unwrap(), + ), + Address::Network(IpNetwork::from_str("192.168.1.15/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.20/32").unwrap()), + ]; + assert_eq!(result, expected_addrs); + } + + #[test] + fn test_merge_addrs_ipv6() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("2001:db8::1/128").unwrap()), + Address::Network(IpNetwork::from_str("2001:db8::2/128").unwrap()), + Address::Network(IpNetwork::from_str("2001:db8::3/128").unwrap()), + ]; + let result = merge_addrs(addrs).unwrap(); + + assert_eq!(result.len(), 1); + if let Address::Range(range) = &result[0] { + assert_eq!(range.start(), IpAddr::from_str("2001:db8::1").unwrap()); + assert_eq!(range.end(), IpAddr::from_str("2001:db8::3").unwrap()); + } else { + panic!("Expected Address::Range"); + } + } + + #[test] + fn test_next_ip_ipv4() { + assert_eq!( + next_ip(IpAddr::from_str("192.168.1.10").unwrap()), + IpAddr::from_str("192.168.1.11").unwrap() + ); + + // Test overflow + assert_eq!( + next_ip(IpAddr::from_str("255.255.255.255").unwrap()), + IpAddr::from_str("0.0.0.0").unwrap() + ); + } + + #[test] + fn test_next_ip_ipv6() { + assert_eq!( + next_ip(IpAddr::from_str("2001:db8::1").unwrap()), + IpAddr::from_str("2001:db8::2").unwrap() + ); + + // Test overflow + assert_eq!( + next_ip(IpAddr::from_str("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap()), + IpAddr::from_str("::").unwrap() + ); + } + + #[test] + fn test_merge_addrs_gap() { + let addrs = vec![ + Address::Network(IpNetwork::from_str("192.168.1.1/32").unwrap()), + Address::Network(IpNetwork::from_str("192.168.1.100/32").unwrap()), + ]; + let result = merge_addrs(addrs.clone()).unwrap(); + + // Should not merge since there's a gap + assert_eq!(result, addrs); + + let addrs = vec![ + Address::Range( + IpAddrRange::new( + IpAddr::from_str("192.168.1.10").unwrap(), + IpAddr::from_str("192.168.1.20").unwrap(), + ) + .unwrap(), + ), + Address::Network(IpNetwork::from_str("192.168.1.100/32").unwrap()), + ]; + let result = merge_addrs(addrs.clone()).unwrap(); + + // Should not merge since there's a gap + assert_eq!(result, addrs); + } +} diff --git a/src/enterprise/firewall/nftables/netfilter.rs b/src/enterprise/firewall/nftables/netfilter.rs index 6cef7be1..2deb6b30 100644 --- a/src/enterprise/firewall/nftables/netfilter.rs +++ b/src/enterprise/firewall/nftables/netfilter.rs @@ -1,13 +1,8 @@ -#[cfg(test)] -use std::str::FromStr; use std::{ ffi::{CStr, CString}, net::{IpAddr, Ipv4Addr, Ipv6Addr}, }; -use ipnetwork::IpNetwork; -#[cfg(test)] -use ipnetwork::{Ipv4Network, Ipv6Network}; use nftnl::{ expr::{Expression, Immediate, InterfaceName, Nat, NatType, Register}, nft_expr, nftnl_sys, @@ -16,7 +11,7 @@ use nftnl::{ }; use super::{get_set_id, Address, FilterRule, Policy, Port, Protocol, State}; -use crate::enterprise::firewall::{iprange::IpAddrRange, FirewallError, SnatBinding}; +use crate::enterprise::firewall::{iprange::IpAddrRange, max_address, FirewallError, SnatBinding}; const FILTER_TABLE: &str = "filter"; const NAT_TABLE: &str = "nat"; @@ -905,27 +900,6 @@ fn socket_recv<'a>( } } -/// Get the max address in a network. -/// -/// - In IPv4 this is the broadcast address. -/// - In IPv6 this is just the last address in the network. -fn max_address(network: &IpNetwork) -> IpAddr { - match network { - IpNetwork::V4(network) => { - let addr = network.ip().to_bits(); - let mask = network.mask().to_bits(); - - IpAddr::V4(Ipv4Addr::from(addr | !mask)) - } - IpNetwork::V6(network) => { - let addr = network.ip().to_bits(); - let mask = network.mask().to_bits(); - - IpAddr::V6(Ipv6Addr::from(addr | !mask)) - } - } -} - fn new_anon_set( table: &Table, family: ProtoFamily, @@ -1087,59 +1061,4 @@ mod tests { increment_bytes(&mut ip); assert_eq!(ip, [0, 0, 0, 0, 0, 0, 0, 0]); } - - #[test] - fn test_max_address_ipv4_24() { - let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.0/24").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 255))); - } - - #[test] - fn test_max_address_ipv4_16() { - let network = IpNetwork::V4(Ipv4Network::from_str("10.1.0.0/16").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(10, 1, 255, 255))); - } - - #[test] - fn test_max_address_ipv4_8() { - let network = IpNetwork::V4(Ipv4Network::from_str("172.16.0.0/8").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(172, 255, 255, 255))); - } - - #[test] - fn test_max_address_ipv4_32() { - let network = IpNetwork::V4(Ipv4Network::from_str("192.168.1.1/32").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1))); - } - - #[test] - fn test_max_address_ipv6_64() { - let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::/64").unwrap()); - let max = max_address(&network); - assert_eq!( - max, - IpAddr::V6(Ipv6Addr::from_str("2001:db8::ffff:ffff:ffff:ffff").unwrap()) - ); - } - - #[test] - fn test_max_address_ipv6_128() { - let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8::1/128").unwrap()); - let max = max_address(&network); - assert_eq!(max, IpAddr::V6(Ipv6Addr::from_str("2001:db8::1").unwrap())); - } - - #[test] - fn test_max_address_ipv6_48() { - let network = IpNetwork::V6(Ipv6Network::from_str("2001:db8:1234::/48").unwrap()); - let max = max_address(&network); - assert_eq!( - max, - IpAddr::V6(Ipv6Addr::from_str("2001:db8:1234:ffff:ffff:ffff:ffff:ffff").unwrap()) - ); - } } From e41a55a7c94b087cc567fee5d225b543e6f3f3b8 Mon Sep 17 00:00:00 2001 From: Maciek <19913370+wojcik91@users.noreply.github.com> Date: Wed, 6 Aug 2025 17:36:32 +0200 Subject: [PATCH 07/25] bump version (#188) --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c65dda20..2d7c659a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -445,7 +445,7 @@ dependencies = [ [[package]] name = "defguard-gateway" -version = "1.4.0" +version = "1.4.1" dependencies = [ "axum 0.8.4", "base64", diff --git a/Cargo.toml b/Cargo.toml index 8f7e50b6..6ab6afd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "defguard-gateway" -version = "1.4.0" +version = "1.4.1" edition = "2021" [dependencies] From f1b2c5170a46e980000601e7b6c1a79891eebfe9 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Mon, 11 Aug 2025 09:55:49 +0200 Subject: [PATCH 08/25] add eu central region (#190) --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3f959c7e..0977caef 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -156,7 +156,7 @@ jobs: - name: Build AMI images for multiple regions if: matrix.build == 'linux' && matrix.arch == 'amd64' run: | - regions=(us-east-1 eu-west-1 ap-northeast-1) + regions=(us-east-1 eu-west-1 ap-northeast-1 eu-central-1) for region in "${regions[@]}"; do echo "Building AMI for region: $region" echo "Running packer validate for $region..." From 1a39f0e99326dd6718378664978b65d191122c61 Mon Sep 17 00:00:00 2001 From: Maciek <19913370+wojcik91@users.noreply.github.com> Date: Mon, 11 Aug 2025 13:42:35 +0200 Subject: [PATCH 09/25] sign Docker images using Cosign (#191) * setup image signing * run test * update readme * update dependencies * linter fixes * remove test branch --- .github/workflows/build-docker.yml | 31 ++++++++ Cargo.lock | 76 +++++++++---------- README.md | 29 +++++++ flake.lock | 12 +-- flake.nix | 1 + src/enterprise/firewall/nftables/netfilter.rs | 2 +- src/gateway.rs | 8 +- 7 files changed, 110 insertions(+), 49 deletions(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 3e5956d9..2210bfd0 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -21,6 +21,7 @@ jobs: - self-hosted - Linux - ${{ matrix.runner }} + strategy: matrix: cpu: [arm64, amd64, arm/v7] @@ -34,23 +35,31 @@ jobs: - cpu: arm/v7 runner: ARM tag: armv7 + + permissions: + contents: read + packages: write + steps: - name: Checkout uses: actions/checkout@v4 with: submodules: recursive + - name: Login to GitHub container registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: buildkitd-config-inline: | [registry."docker.io"] mirrors = ["dockerhub-proxy.teonite.net"] + - name: Build container uses: docker/build-push-action@v5 with: @@ -64,8 +73,18 @@ jobs: docker-manifest: runs-on: [self-hosted, Linux] + + permissions: + contents: read + packages: write + id-token: write # needed for signing the images with GitHub OIDC Token + needs: [build-docker] + steps: + - name: Install Cosign + uses: sigstore/cosign-installer@v3.9.2 + - name: Docker meta id: meta uses: docker/metadata-action@v5 @@ -74,12 +93,14 @@ jobs: ${{ env.GHCR_REPO }} flavor: ${{ inputs.flavor }} tags: ${{ inputs.tags }} + - name: Login to GitHub container registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Create and push manifests run: | tags='${{ env.GHCR_REPO }}:${{ github.sha }} ${{ steps.meta.outputs.tags }}' @@ -89,3 +110,13 @@ jobs: docker manifest create ${tag} ${{ env.GHCR_REPO }}:${{ github.sha }}-amd64 ${{ env.GHCR_REPO }}:${{ github.sha }}-arm64 ${{ env.GHCR_REPO }}:${{ github.sha }}-armv7 docker manifest push ${tag} done + + - name: Sign the images with GitHub OIDC Token + run: | + images='${{ env.GHCR_REPO }}:${{ github.sha }} ${{ steps.meta.outputs.tags }}' + cosign sign --yes ${images} + + - name: Verify image signatures + run: | + images='${{ env.GHCR_REPO }}:${{ github.sha }} ${{ steps.meta.outputs.tags }}' + cosign verify ${images} --certificate-oidc-issuer https://token.actions.githubusercontent.com --certificate-identity-regexp="https://github.com/DefGuard/gateway" -o text diff --git a/Cargo.lock b/Cargo.lock index 39d7b879..f387783c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -58,22 +58,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -212,9 +212,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.31" +version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ "jobserver", "libc", @@ -235,9 +235,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" dependencies = [ "clap_builder", "clap_derive", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" dependencies = [ "anstream", "anstyle", @@ -657,9 +657,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -676,9 +676,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "heck" @@ -995,9 +995,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libgit2-sys" @@ -1367,9 +1367,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "beef09f85ae72cea1ef96ba6870c51e6382ebfa4f0e85b643459331f3daa5be0" dependencies = [ "unicode-ident", ] @@ -1592,9 +1592,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -1613,9 +1613,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" dependencies = [ "bitflags", "core-foundation", @@ -1720,9 +1720,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -1991,9 +1991,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308e1db96abdccdf0a9150fb69112bf6ea72640e0bd834ef0c4a618ccc8c8ddc" +checksum = "67ac5a8627ada0968acec063a4746bf79588aa03ccb66db2f75d7dce26722a40" dependencies = [ "async-trait", "axum", @@ -2023,9 +2023,9 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18262cdd13dec66e8e3f2e3fe535e4b2cc706fab444a7d3678d75d8ac2557329" +checksum = "49e323d8bba3be30833707e36d046deabf10a35ae8ad3cae576943ea8933e25d" dependencies = [ "prettyplease", "proc-macro2", @@ -2035,9 +2035,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d8b5b7a44512c59f5ad45e0c40e53263cbbf4426d74fe6b569e04f1d4206e9c" +checksum = "b9c511b9a96d40cb12b7d5d00464446acf3b9105fd3ce25437cfe41c92b1c87d" dependencies = [ "bytes", "prost", @@ -2046,9 +2046,9 @@ dependencies = [ [[package]] name = "tonic-prost-build" -version = "0.14.0" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114cca66d757d72422ef8cccf8be3065321860ac9fa4be73aab37a8a20a9a805" +checksum = "8ef298fcd01b15e135440c4b8c974460ceca4e6a5af7f1c933b08e4d2875efa1" dependencies = [ "prettyplease", "proc-macro2", @@ -2512,9 +2512,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdbb9122ea75b11bf96e7492afb723e8a7fbe12c67417aa95e7e3d18144d37cd" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", diff --git a/README.md b/README.md index b7516f51..d1e0e17f 100644 --- a/README.md +++ b/README.md @@ -22,5 +22,34 @@ Find us on Matrix: [#defguard:teonite.com](https://matrix.to/#/#defguard:teonite Please review the [Contributing guide](https://defguard.gitbook.io/defguard/for-developers/contributing) for information on how to get started contributing to the project. You might also find our [environment setup guide](https://defguard.gitbook.io/defguard/for-developers/dev-env-setup) handy. +## Verifiability of releases + +We provide following ways to verify the authenticity and integrity of official releases: + +### Docker Image Verification with Cosign + +All official Docker images are signed using [Cosign](https://docs.sigstore.dev/cosign/overview/). To verify a Docker image: + +1. [Install](https://github.com/sigstore/cosign?tab=readme-ov-file#installation) cosign CLI + +2. Verify the image signature (replace with the tag you want to verify): + ```bash + cosign verify --certificate-identity-regexp="https://github.com/DefGuard/gateway" \ + --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ + ghcr.io/defguard/defguard: + ``` + +### Release Asset Verification + +All release assets (binaries, packages, etc.) include SHA256 checksums that are automatically generated and published with each GitHub release: + +1. Download the release asset and copy its corresponding checksum from the [releases page](https://github.com/DefGuard/gateway/releases) + +2. Verify the checksum: + ```bash + # Linux/macOS + echo known_sha256_checksum_of_the_file path/to/file | sha256sum --check + ``` + # Legal WireGuard is [registered trademarks](https://www.wireguard.com/trademark-policy/) of Jason A. Donenfeld. diff --git a/flake.lock b/flake.lock index b227bc8e..4c226f53 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1751271578, - "narHash": "sha256-P/SQmKDu06x8yv7i0s8bvnnuJYkxVGBWLWHaU+tt4YY=", + "lastModified": 1754725699, + "narHash": "sha256-iAcj9T/Y+3DBy2J0N+yF9XQQQ8IEb5swLFzs23CdP88=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3016b4b15d13f3089db8a41ef937b13a9e33a8df", + "rev": "85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1751423951, - "narHash": "sha256-AowKhJGplXRkAngSvb+32598DTiI6LOzhAnzgvbCtYM=", + "lastModified": 1754880555, + "narHash": "sha256-tG6l0wiX8V8IvG4HFYY8IYN5vpNAxQ+UWunjjpE6SqU=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "1684ed5b15859b655caf41b467d046e29a994d04", + "rev": "17c591a44e4eb77f05f27cd37e1cfc3f219c7fc4", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 08e0ab97..0d06e026 100644 --- a/flake.nix +++ b/flake.nix @@ -25,6 +25,7 @@ }; rustToolchain = pkgs.rust-bin.stable.latest.default.override { extensions = ["rust-analyzer" "rust-src" "rustfmt" "clippy"]; + targets = ["x86_64-unknown-linux-gnu" "armv7-unknown-linux-gnueabihf" "aarch64-unknown-linux-gnu" "x86_64-unknown-freebsd"]; }; in { devShells.default = pkgs.mkShell { diff --git a/src/enterprise/firewall/nftables/netfilter.rs b/src/enterprise/firewall/nftables/netfilter.rs index 2deb6b30..06e2d62d 100644 --- a/src/enterprise/firewall/nftables/netfilter.rs +++ b/src/enterprise/firewall/nftables/netfilter.rs @@ -904,7 +904,7 @@ fn new_anon_set( table: &Table, family: ProtoFamily, interval_set: bool, -) -> Result, FirewallError> +) -> Result, FirewallError> where T: SetKey, { diff --git a/src/gateway.rs b/src/gateway.rs index 9c515d2e..335e49ab 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -659,7 +659,7 @@ impl Gateway { #[cfg(test)] mod tests { - use std::net::Ipv4Addr; + use std::{net::Ipv4Addr, slice::from_ref}; #[cfg(not(any(target_os = "macos", target_os = "netbsd")))] use defguard_wireguard_rs::Kernel; @@ -916,7 +916,7 @@ mod tests { // Gateway has no firewall config, but new rules exist gateway.firewall_config = None; - assert!(gateway.have_firewall_rules_changed(&[rule1.clone()])); + assert!(gateway.have_firewall_rules_changed(from_ref(&rule1))); // Gateway has firewall config, with empty rules list gateway.firewall_config = Some(config1.clone()); @@ -924,7 +924,7 @@ mod tests { // Gateway has firewall config, new rules have different length gateway.firewall_config = Some(config1.clone()); - assert!(gateway.have_firewall_rules_changed(&[rule1.clone()])); + assert!(gateway.have_firewall_rules_changed(from_ref(&rule1))); // Gateway has firewall config, new rules have different content gateway.firewall_config = Some(config1.clone()); @@ -936,7 +936,7 @@ mod tests { // Gateway has empty firewall config, new rules exist gateway.firewall_config = Some(config_empty.clone()); - assert!(gateway.have_firewall_rules_changed(&[rule1.clone()])); + assert!(gateway.have_firewall_rules_changed(from_ref(&rule1))); // Both configs are empty gateway.firewall_config = Some(config_empty); From 26863ecb4e63fa0b279a4b7178049feb2bc06517 Mon Sep 17 00:00:00 2001 From: Maciek <19913370+wojcik91@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:33:24 +0200 Subject: [PATCH 10/25] setup AWS CodeBuild (#193) * run job on AWS * fix docs link * remove legacy docs workflow --- .github/workflows/ci.yml | 3 ++- .github/workflows/docs.yml | 40 -------------------------------------- README.md | 2 +- 3 files changed, 3 insertions(+), 42 deletions(-) delete mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16ba8185..ba41d349 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,8 @@ env: jobs: test: - runs-on: [self-hosted, Linux, X64] + runs-on: + - codebuild-defguard-gateway-runner-${{ github.run_id }}-${{ github.run_attempt }} container: rust:1 steps: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index 6c8f9745..00000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: rustdoc Github Pages -on: - push: - branches: - - main - -env: - CARGO_INCREMENTAL: 0 - CARGO_NET_RETRY: 10 - RUSTFLAGS: "-D warnings -W unreachable-pub" - RUSTUP_MAX_RETRIES: 10 - -jobs: - rustdoc: - runs-on: [self-hosted, Linux] - container: - image: rust:1 - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - submodules: recursive - - - name: Install Rust toolchain - run: rustup update --no-self-update stable - - - name: Install dependencies - run: apt-get update && apt-get -y install protobuf-compiler libnftnl-dev libmnl-dev - - - name: Build Docs - run: cargo doc --all --no-deps - - - name: Deploy Docs - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_branch: gh-pages - publish_dir: ./target/doc - force_orphan: true diff --git a/README.md b/README.md index d1e0e17f..dfca4e14 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ If you already have your defguard instance running you can set up a gateway by f ## Documentation -See the [documentation](https://defguard.gitbook.io) for more information. +See the [documentation](https://docs.defguard.net) for more information. ## Community and Support From ec0d602e4d8ae893161bfad36d8843eb86dda43e Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 18 Aug 2025 11:23:25 +0200 Subject: [PATCH 11/25] Fix deny.toml (#194) --- Cargo.lock | 50 +++++++++++++++++++++++++------------------------- deny.toml | 10 +++------- 2 files changed, 28 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f387783c..21053341 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -78,15 +78,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", @@ -194,9 +194,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" [[package]] name = "byteorder" @@ -212,9 +212,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.32" +version = "1.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" +checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" dependencies = [ "jobserver", "libc", @@ -235,9 +235,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.5.43" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" +checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" dependencies = [ "clap_builder", "clap_derive", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.43" +version = "4.5.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" +checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" dependencies = [ "anstream", "anstyle", @@ -257,9 +257,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck", "proc-macro2", @@ -393,7 +393,7 @@ dependencies = [ "prost", "serde", "syslog", - "thiserror 2.0.12", + "thiserror 2.0.15", "tokio", "tokio-stream", "toml", @@ -421,7 +421,7 @@ dependencies = [ "netlink-sys", "nix", "serde", - "thiserror 2.0.12", + "thiserror 2.0.15", "x25519-dalek", ] @@ -1367,9 +1367,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.96" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beef09f85ae72cea1ef96ba6870c51e6382ebfa4f0e85b643459331f3daa5be0" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -1760,9 +1760,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -1822,11 +1822,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.15", ] [[package]] @@ -1842,9 +1842,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" dependencies = [ "proc-macro2", "quote", diff --git a/deny.toml b/deny.toml index 05ea3fa6..2282829b 100644 --- a/deny.toml +++ b/deny.toml @@ -69,9 +69,7 @@ feature-depth = 1 #db-urls = ["https://github.com/rustsec/advisory-db"] # A list of advisory IDs to ignore. Note that ignored advisories will still # output a note when they are encountered. -ignore = [ - { id = "RUSTSEC-2024-0436", reason = "Unmaintained" }, -] +ignore = [{ id = "RUSTSEC-2024-0436", reason = "Unmaintained" }] # If this is true, then cargo deny will use the git executable to fetch advisory database. # If this is false, then it uses a built-in git library. # Setting this to true can be helpful if you have special authentication requirements that cargo-deny does not support. @@ -92,7 +90,7 @@ allow = [ "MPL-2.0", "BSD-3-Clause", "Unicode-3.0", - "Unicode-DFS-2016", # unicode-ident + "Unicode-DFS-2016", # unicode-ident "Zlib", "ISC", "BSL-1.0", @@ -108,9 +106,7 @@ allow = [ confidence-threshold = 0.8 # Allow 1 or more licenses on a per-crate basis, so that particular licenses # aren't accepted for every possible crate as with the normal allow list -exceptions = [ - { allow = ["AGPL-3.0"], crate = "defguard-gateway" }, -] +exceptions = [{ allow = ["AGPL-3.0-only"], crate = "defguard-gateway" }] # Some crates don't have (easily) machine readable licensing information, # adding a clarification entry for it allows you to manually specify the From 86def0d8ff0d4b1715107896b66d9cf725a3c052 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 22 Aug 2025 10:13:44 +0200 Subject: [PATCH 12/25] Version exchange and logging (#189) Uses defguard_version crate to exchange and log version and system information. --- Cargo.lock | 179 ++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 + deny.toml | 5 +- src/config.rs | 4 ++ src/error.rs | 7 ++ src/gateway.rs | 191 +++++++++++++++++++++++++++++++++---------------- src/main.rs | 9 +-- 7 files changed, 328 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21053341..a7c3ca72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -381,6 +381,7 @@ dependencies = [ "axum", "base64", "clap", + "defguard_version", "defguard_wireguard_rs", "env_logger", "gethostname", @@ -400,10 +401,26 @@ dependencies = [ "tonic", "tonic-prost", "tonic-prost-build", + "tracing", "vergen-git2", "x25519-dalek", ] +[[package]] +name = "defguard_version" +version = "0.0.0" +source = "git+https://github.com/DefGuard/defguard.git?rev=f61ce40927a4d21095ea53a691219d5ae46e3e4e#f61ce40927a4d21095ea53a691219d5ae46e3e4e" +dependencies = [ + "http", + "os_info", + "semver", + "thiserror 2.0.15", + "tonic", + "tower", + "tracing", + "tracing-subscriber", +] + [[package]] name = "defguard_wireguard_rs" version = "0.7.5" @@ -993,6 +1010,12 @@ dependencies = [ "libc", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.175" @@ -1041,6 +1064,15 @@ version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.8.4" @@ -1223,6 +1255,16 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -1265,6 +1307,24 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "os_info" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0e1ac5fde8d43c34139135df8ea9ee9465394b2d8d20f032d38998f64afffc3" +dependencies = [ + "log", + "plist", + "serde", + "windows-sys 0.52.0", +] + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "paste" version = "1.0.15" @@ -1325,6 +1385,19 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plist" +version = "1.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" +dependencies = [ + "base64", + "indexmap", + "quick-xml", + "serde", + "time", +] + [[package]] name = "portable-atomic" version = "1.11.1" @@ -1448,6 +1521,15 @@ dependencies = [ "pulldown-cmark", ] +[[package]] +name = "quick-xml" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9845d9dccf565065824e69f9f235fafba1587031eda353c1f1561cd6a6be78f4" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.40" @@ -1480,8 +1562,17 @@ checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -1492,9 +1583,15 @@ checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.5", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.5" @@ -1703,6 +1800,15 @@ dependencies = [ "serde", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1851,6 +1957,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "time" version = "0.3.41" @@ -2121,6 +2236,36 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", ] [[package]] @@ -2170,6 +2315,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2239,6 +2390,28 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-link" version = "0.1.3" diff --git a/Cargo.toml b/Cargo.toml index 213fc8b8..96915c03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "f61ce40927a4d21095ea53a691219d5ae46e3e4e" } axum = { version = "0.8", features = ["macros"] } base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } @@ -29,6 +30,7 @@ tonic = { version = "0.14", default-features = false, features = [ "tls-native-roots", "tls-ring", ] } +tracing = "0.1" tonic-prost = "0.14" [target.'cfg(target_os = "linux")'.dependencies] diff --git a/deny.toml b/deny.toml index 2282829b..4c2d088a 100644 --- a/deny.toml +++ b/deny.toml @@ -106,7 +106,10 @@ allow = [ confidence-threshold = 0.8 # Allow 1 or more licenses on a per-crate basis, so that particular licenses # aren't accepted for every possible crate as with the normal allow list -exceptions = [{ allow = ["AGPL-3.0-only"], crate = "defguard-gateway" }] +exceptions = [ + { allow = ["AGPL-3.0-only"], crate = "defguard-gateway" }, + { allow = ["AGPL-3.0-only"], crate = "defguard_version" } +] # Some crates don't have (easily) machine readable licensing information, # adding a clarification entry for it allows you to manually specify the diff --git a/src/config.rs b/src/config.rs index 8235fe46..87d42a5a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,9 @@ use crate::error::GatewayError; #[clap(about = "Defguard VPN gateway service")] #[command(version)] pub struct Config { + #[arg(long, short = 'l', env = "DEFGUARD_LOG_LEVEL", default_value = "info")] + pub log_level: String, + /// Token received from Defguard after completing the network wizard #[arg( long, @@ -113,6 +116,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + log_level: "info".to_string(), token: "TOKEN".into(), name: None, grpc_url: "http://localhost:50051".into(), diff --git a/src/error.rs b/src/error.rs index d05e5d45..aceb3c4f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,4 @@ +use defguard_version::{DefguardVersionError, SemverError}; use defguard_wireguard_rs::error::WireguardInterfaceError; use thiserror::Error; @@ -43,4 +44,10 @@ pub enum GatewayError { #[error("Firewall error: {0}")] FirewallError(#[from] FirewallError), + + #[error(transparent)] + DefguardVersionError(#[from] DefguardVersionError), + + #[error(transparent)] + SemverError(#[from] SemverError), } diff --git a/src/gateway.rs b/src/gateway.rs index 335e49ab..e5155881 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -1,3 +1,8 @@ +use defguard_version::{ + client::version_interceptor, parse_metadata, ComponentInfo, DefguardComponent, Version, +}; +use defguard_wireguard_rs::{net::IpAddrMask, WireguardInterfaceApi}; +use gethostname::gethostname; use std::{ collections::HashMap, fs::read_to_string, @@ -8,9 +13,6 @@ use std::{ }, time::{Duration, SystemTime}, }; - -use defguard_wireguard_rs::{net::IpAddrMask, WireguardInterfaceApi}; -use gethostname::gethostname; use tokio::{ select, sync::mpsc, @@ -25,6 +27,7 @@ use tonic::{ transport::{Certificate, Channel, ClientTlsConfig, Endpoint}, Request, Status, Streaming, }; +use tracing::{instrument, Instrument}; use crate::{ config::Config, @@ -69,14 +72,29 @@ impl From for InterfaceConfiguration { } } -#[derive(Clone)] -struct AuthInterceptor { +type InterceptorFn = Box) -> Result, Status> + Send + Sync>; + +/// Intercepts all grpc requests adding authentication and version metadata +struct RequestInterceptor { hostname: MetadataValue, token: MetadataValue, + version: defguard_version::Version, + version_interceptor_fn: InterceptorFn, } -impl AuthInterceptor { - fn new(token: &str) -> Result { +impl Clone for RequestInterceptor { + fn clone(&self) -> Self { + Self { + hostname: self.hostname.clone(), + token: self.token.clone(), + version: self.version.clone(), + version_interceptor_fn: Box::new(version_interceptor(self.version.clone())), + } + } +} + +impl RequestInterceptor { + fn new(token: &str, version: Version) -> Result { let token = MetadataValue::try_from(token)?; let hostname = MetadataValue::try_from( gethostname() @@ -84,12 +102,21 @@ impl AuthInterceptor { .expect("Unable to get current hostname during gRPC connection setup."), )?; - Ok(Self { hostname, token }) + Ok(Self { + hostname, + token, + version: version.clone(), + version_interceptor_fn: Box::new(version_interceptor(version)), + }) } } -impl Interceptor for AuthInterceptor { - fn call(&mut self, mut request: Request<()>) -> Result, Status> { +impl Interceptor for RequestInterceptor { + fn call(&mut self, request: Request<()>) -> Result, Status> { + // Apply version interceptor - adds version headers + let mut request = (self.version_interceptor_fn)(request)?; + + // Add auth headers let metadata = request.metadata_mut(); metadata.insert("authorization", self.token.clone()); metadata.insert("hostname", self.hostname.clone()); @@ -110,7 +137,8 @@ pub struct Gateway { #[cfg_attr(not(target_os = "linux"), allow(unused))] firewall_config: Option, pub connected: Arc, - client: GatewayServiceClient>, + client: GatewayServiceClient>, + core_info: Option, stats_thread: Option>, } @@ -131,6 +159,7 @@ impl Gateway { stats_thread: None, firewall_api, firewall_config: None, + core_info: None, }) } @@ -182,6 +211,7 @@ impl Gateway { } /// Starts tokio thread collecting stats and sending them to backend service via gRPC. + #[instrument(skip_all)] fn spawn_stats_thread(&mut self) -> UnboundedReceiverStream { if let Some(handle) = self.stats_thread.take() { debug!("Aborting previous stats thread before starting a new one"); @@ -192,63 +222,67 @@ impl Gateway { let wgapi = Arc::clone(&self.wgapi); let (tx, rx) = mpsc::unbounded_channel(); debug!("Spawning stats thread"); - let handle = spawn(async move { - // helper map to track if peer data is actually changing - // and avoid sending duplicate stats - let mut peer_map = HashMap::new(); - let mut interval = interval(period); - let mut id = 1; - 'outer: loop { - // wait until next iteration - interval.tick().await; - debug!("Sending active peer stats updates."); - let interface_data = wgapi.lock().unwrap().read_interface_data(); - match interface_data { - Ok(host) => { - let peers = host.peers; - debug!( - "Found {} peers configured on WireGuard interface", - peers.len() - ); - for peer in peers.into_values().filter(|p| { - p.last_handshake - .is_some_and(|lhs| lhs != SystemTime::UNIX_EPOCH) - }) { - let has_changed = peer_map - .get(&peer.public_key) - .is_none_or(|last_peer| *last_peer != peer); - if has_changed { - peer_map.insert(peer.public_key.clone(), peer.clone()); - id += 1; - if tx - .send(StatsUpdate { - id, - payload: Some(Payload::PeerStats((&peer).into())), - }) - .is_err() - { - debug!("Stats stream disappeared"); - break 'outer; + let handle = spawn( + async move { + // helper map to track if peer data is actually changing + // and avoid sending duplicate stats + let mut peer_map = HashMap::new(); + let mut interval = interval(period); + let mut id = 1; + 'outer: loop { + // wait until next iteration + interval.tick().await; + debug!("Sending active peer stats updates."); + let interface_data = wgapi.lock().unwrap().read_interface_data(); + match interface_data { + Ok(host) => { + let peers = host.peers; + debug!( + "Found {} peers configured on WireGuard interface", + peers.len() + ); + for peer in peers.into_values().filter(|p| { + p.last_handshake + .is_some_and(|lhs| lhs != SystemTime::UNIX_EPOCH) + }) { + let has_changed = peer_map + .get(&peer.public_key) + .is_none_or(|last_peer| *last_peer != peer); + if has_changed { + peer_map.insert(peer.public_key.clone(), peer.clone()); + id += 1; + if tx + .send(StatsUpdate { + id, + payload: Some(Payload::PeerStats((&peer).into())), + }) + .is_err() + { + debug!("Stats stream disappeared"); + break 'outer; + } + } else { + debug!( + "Stats for peer {} have not changed. Skipping.", + peer.public_key + ); } - } else { - debug!( - "Stats for peer {} have not changed. Skipping.", - peer.public_key - ); } } + Err(err) => error!("Failed to retrieve WireGuard interface stats: {err}"), } - Err(err) => error!("Failed to retrieve WireGuard interface stats: {err}"), + debug!("Sent peer stats updates for all peers."); } - debug!("Sent peer stats updates for all peers."); } - }); + .instrument(tracing::Span::current()), + ); self.stats_thread = Some(handle); UnboundedReceiverStream::new(rx) } + #[instrument(skip_all)] async fn handle_stats_thread( - mut client: GatewayServiceClient>, + mut client: GatewayServiceClient>, rx: UnboundedReceiverStream, ) { let status = client.stats(rx).await; @@ -429,6 +463,19 @@ impl Gateway { Ok(()) } + fn get_tracing_variables(&self) -> (String, String) { + let version = self + .core_info + .as_ref() + .map_or(String::from("?"), |info| info.version.to_string()); + let info = self + .core_info + .as_ref() + .map_or(String::from("?"), |info| info.system.to_string()); + + (version, info) + } + /// Continuously tries to connect to gRPC endpoint. Once the connection is established /// configures the interface, starts the stats thread, connects and returns the updates stream. async fn connect(&mut self) -> Streaming { @@ -451,6 +498,16 @@ impl Gateway { }; match (response, stream) { (Ok(response), Ok(stream)) => { + self.core_info = parse_metadata(response.metadata()); + let (version, info) = self.get_tracing_variables(); + let span = tracing::info_span!( + "core_configuration", + component = %DefguardComponent::Core, + version, + info + ); + let _guard = span.enter(); + if let Err(err) = self.configure(response.into_inner()) { error!("Interface configuration failed: {err}"); continue; @@ -477,7 +534,7 @@ impl Gateway { fn setup_client( config: &Config, - ) -> Result>, GatewayError> + ) -> Result>, GatewayError> { debug!("Preparing gRPC client configuration"); let tls = ClientTlsConfig::new(); @@ -497,14 +554,15 @@ impl Gateway { .keep_alive_while_idle(true) .tls_config(tls)?; let channel = endpoint.connect_lazy(); - - let auth_interceptor = AuthInterceptor::new(&config.token)?; - let client = GatewayServiceClient::with_interceptor(channel, auth_interceptor); + let version = Version::parse(VERSION)?; + let request_interceptor = RequestInterceptor::new(&config.token, version)?; + let client = GatewayServiceClient::with_interceptor(channel, request_interceptor); debug!("gRPC client configuration done"); Ok(client) } + #[instrument(skip_all)] async fn handle_updates(&mut self, updates_stream: &mut Streaming) { loop { match updates_stream.message().await { @@ -642,6 +700,14 @@ impl Gateway { debug!("Executing specified POST_UP command: {post_up}"); execute_command(post_up)?; } + let (version, info) = self.get_tracing_variables(); + let span = tracing::info_span!( + "core_grpc", + component = %DefguardComponent::Core, + version, + info, + ); + let _guard = span.enter(); let stats_stream = self.spawn_stats_thread(); let client = self.client.clone(); select! { @@ -717,6 +783,7 @@ mod tests { stats_thread: None, firewall_api, firewall_config: None, + core_info: None, }; // new config is the same @@ -908,6 +975,7 @@ mod tests { stats_thread: None, firewall_api: FirewallApi::new("test_interface").unwrap(), firewall_config: None, + core_info: None, }; // Gateway has no firewall config, new rules are empty @@ -980,6 +1048,7 @@ mod tests { stats_thread: None, firewall_api: FirewallApi::new("test_interface").unwrap(), firewall_config: None, + core_info: None, }; // Gateway has no config gateway.firewall_config = None; diff --git a/src/main.rs b/src/main.rs index ce5b2317..def54a2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,12 @@ use std::{fs::File, io::Write, process, sync::Arc}; use defguard_gateway::{ config::get_config, enterprise::firewall::api::FirewallApi, error::GatewayError, - execute_command, gateway::Gateway, init_syslog, server::run_server, + execute_command, gateway::Gateway, init_syslog, server::run_server, VERSION, }; +use defguard_version::Version; #[cfg(not(any(target_os = "macos", target_os = "netbsd")))] use defguard_wireguard_rs::Kernel; use defguard_wireguard_rs::{Userspace, WGApi}; -use env_logger::{init_from_env, Env, DEFAULT_FILTER_ENV}; use tokio::task::JoinSet; #[tokio::main] @@ -30,8 +30,9 @@ async fn main() -> Result<(), GatewayError> { return Err(error); } } else { - init_from_env(Env::default().filter_or(DEFAULT_FILTER_ENV, "info")); - } + let version = Version::parse(VERSION)?; + defguard_version::tracing::init(version, &config.log_level.to_string())? + }; if let Some(pre_up) = &config.pre_up { log::info!("Executing specified PRE_UP command: {pre_up}"); From eaea88fe7e5ab5efb0908c39516ae59678858505 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 25 Aug 2025 11:33:04 +0200 Subject: [PATCH 13/25] Scan images with Trivy (#195) --- .github/workflows/build-docker.yml | 12 +++- Cargo.lock | 105 +++++++++++++++-------------- Dockerfile | 2 +- 3 files changed, 66 insertions(+), 53 deletions(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 2210bfd0..3f4cd5f6 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -71,6 +71,16 @@ jobs: cache-from: type=gha cache-to: type=gha,mode=max + - name: Scan image with Trivy + uses: aquasecurity/trivy-action@0.32.0 + with: + image-ref: "${{ env.GHCR_REPO }}:${{ github.sha }}-${{ matrix.tag }}" + format: "table" + exit-code: "1" + ignore-unfixed: true + vuln-type: "os,library" + severity: "CRITICAL,HIGH,MEDIUM" + docker-manifest: runs-on: [self-hosted, Linux] @@ -110,7 +120,7 @@ jobs: docker manifest create ${tag} ${{ env.GHCR_REPO }}:${{ github.sha }}-amd64 ${{ env.GHCR_REPO }}:${{ github.sha }}-arm64 ${{ env.GHCR_REPO }}:${{ github.sha }}-armv7 docker manifest push ${tag} done - + - name: Sign the images with GitHub OIDC Token run: | images='${{ env.GHCR_REPO }}:${{ github.sha }} ${{ steps.meta.outputs.tags }}' diff --git a/Cargo.lock b/Cargo.lock index a7c3ca72..d0286c1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -194,9 +194,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.9.2" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" [[package]] name = "byteorder" @@ -212,9 +212,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.33" +version = "1.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" dependencies = [ "jobserver", "libc", @@ -223,9 +223,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -394,7 +394,7 @@ dependencies = [ "prost", "serde", "syslog", - "thiserror 2.0.15", + "thiserror 2.0.16", "tokio", "tokio-stream", "toml", @@ -414,7 +414,7 @@ dependencies = [ "http", "os_info", "semver", - "thiserror 2.0.15", + "thiserror 2.0.16", "tonic", "tower", "tracing", @@ -438,7 +438,7 @@ dependencies = [ "netlink-sys", "nix", "serde", - "thiserror 2.0.15", + "thiserror 2.0.16", "x25519-dalek", ] @@ -574,9 +574,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -762,13 +762,14 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", @@ -776,6 +777,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -909,9 +911,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -930,9 +932,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", "hashbrown", @@ -940,9 +942,9 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ "bitflags", "cfg-if", @@ -1002,9 +1004,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.3", "libc", @@ -1333,9 +1335,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "petgraph" @@ -1430,9 +1432,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "prettyplease" -version = "0.2.36" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", @@ -1523,9 +1525,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.1" +version = "0.38.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9845d9dccf565065824e69f9f235fafba1587031eda353c1f1561cd6a6be78f4" +checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89" dependencies = [ "memchr", ] @@ -1556,14 +1558,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", + "regex-automata 0.4.10", + "regex-syntax 0.8.6", ] [[package]] @@ -1577,13 +1579,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax 0.8.6", ] [[package]] @@ -1594,9 +1596,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "ring" @@ -1759,9 +1761,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -1906,15 +1908,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1928,11 +1930,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.15", + "thiserror-impl 2.0.16", ] [[package]] @@ -1948,9 +1950,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.15" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", @@ -2294,13 +2296,14 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -2576,9 +2579,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" [[package]] name = "wit-bindgen-rt" diff --git a/Dockerfile b/Dockerfile index d27a3ccb..2c2d714c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ WORKDIR /app COPY . . RUN cargo build --release -FROM debian:bookworm-slim +FROM debian:13-slim RUN apt-get update && apt-get -y --no-install-recommends install \ iproute2 wireguard-tools sudo ca-certificates iptables ebtables nftables && \ apt-get clean && rm -rf /var/lib/apt/lists/* From af57d71b4e238d2f8315c7730d2bc22217b732ff Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 26 Aug 2025 13:11:52 +0200 Subject: [PATCH 14/25] Better config parsing (#196) --- .github/workflows/ci.yml | 15 ++++++++++++--- src/config.rs | 16 ++++++++++++++-- src/enterprise/firewall/mod.rs | 1 + src/main.rs | 4 ++-- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba41d349..629c753d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: branches: - main - dev - - 'release/**' + - "release/**" paths-ignore: - "*.md" - "LICENSE" @@ -13,7 +13,7 @@ on: branches: - main - dev - - 'release/**' + - "release/**" paths-ignore: - "*.md" - "LICENSE" @@ -30,25 +30,34 @@ jobs: steps: - name: Debug run: echo ${{ github.ref_name }} + - name: Checkout uses: actions/checkout@v4 with: submodules: recursive + - name: Cache uses: Swatinem/rust-cache@v2 with: key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - name: Install dependencies run: apt-get update && apt-get -y install protobuf-compiler libnftnl-dev libmnl-dev + - name: Check format run: | rustup component add rustfmt cargo fmt -- --check + - name: Run clippy linter run: | rustup component add clippy cargo clippy --all-targets --all-features -- -D warnings + - name: Run cargo deny - uses: EmbarkStudios/cargo-deny-action@v2 + run: | + cargo install cargo-deny + cargo deny check + - name: Run tests run: cargo test --locked --no-fail-fast diff --git a/src/config.rs b/src/config.rs index 87d42a5a..00445ffa 100644 --- a/src/config.rs +++ b/src/config.rs @@ -6,11 +6,20 @@ use toml; use crate::error::GatewayError; +fn default_log_level() -> String { + String::from("info") +} + +fn default_syslog_socket() -> PathBuf { + PathBuf::from("/var/run/log") +} + #[derive(Debug, Parser, Clone, Deserialize)] #[clap(about = "Defguard VPN gateway service")] #[command(version)] pub struct Config { #[arg(long, short = 'l', env = "DEFGUARD_LOG_LEVEL", default_value = "info")] + #[serde(default = "default_log_level")] pub log_level: String, /// Token received from Defguard after completing the network wizard @@ -21,6 +30,7 @@ pub struct Config { env = "DEFGUARD_TOKEN", default_value = "" )] + #[serde(default)] pub token: String, #[arg(long, env = "DEFGUARD_GATEWAY_NAME")] @@ -34,6 +44,7 @@ pub struct Config { env = "DEFGUARD_GRPC_URL", default_value = "" )] + #[serde(default)] pub grpc_url: String, /// Use userspace WireGuard implementation e.g. wireguard-go @@ -66,6 +77,7 @@ pub struct Config { /// Syslog socket path #[arg(long, default_value = "/var/run/log")] + #[serde(default = "default_syslog_socket")] pub syslog_socket: PathBuf, /// Configuration file path @@ -116,7 +128,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - log_level: "info".to_string(), + log_level: "info".into(), token: "TOKEN".into(), name: None, grpc_url: "http://localhost:50051".into(), @@ -150,7 +162,7 @@ pub fn get_config() -> Result { if let Some(config_path) = cli_config.config_path { let config_toml = fs::read_to_string(config_path) .map_err(|err| GatewayError::InvalidConfigFile(err.to_string()))?; - let file_config: Config = toml::from_str(&config_toml) + let file_config = toml::from_str(&config_toml) .map_err(|err| GatewayError::InvalidConfigFile(err.message().to_string()))?; return Ok(file_config); } diff --git a/src/enterprise/firewall/mod.rs b/src/enterprise/firewall/mod.rs index 4d93ad4c..e078af43 100644 --- a/src/enterprise/firewall/mod.rs +++ b/src/enterprise/firewall/mod.rs @@ -382,6 +382,7 @@ pub enum FirewallError { /// /// - In IPv4 this is the broadcast address. /// - In IPv6 this is just the last address in the network. +#[must_use] pub fn max_address(network: &IpNetwork) -> IpAddr { match network { IpNetwork::V4(network) => { diff --git a/src/main.rs b/src/main.rs index def54a2e..b9149666 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,8 +31,8 @@ async fn main() -> Result<(), GatewayError> { } } else { let version = Version::parse(VERSION)?; - defguard_version::tracing::init(version, &config.log_level.to_string())? - }; + defguard_version::tracing::init(version, &config.log_level)?; + } if let Some(pre_up) = &config.pre_up { log::info!("Executing specified PRE_UP command: {pre_up}"); From e9f854b28adb1d3a6903a8ad462eb3985e5f080d Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 28 Aug 2025 16:22:58 +0200 Subject: [PATCH 15/25] Version check (#197) * implement core version check * fix semver version * use get_tracing_variables function from defguard_version crate * use the new defguard_version::client::ClientVersionInterceptor * update defguard_version dependency * use aws cached docker image * don't use cargo-deny gh action --- .github/workflows/ci.yml | 2 +- Cargo.lock | 3 +- Cargo.toml | 3 +- src/gateway.rs | 78 ++++++++++++++++------------------------ src/lib.rs | 3 +- src/version.rs | 18 ++++++++++ 6 files changed, 56 insertions(+), 51 deletions(-) create mode 100644 src/version.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 629c753d..3710f64c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: test: runs-on: - codebuild-defguard-gateway-runner-${{ github.run_id }}-${{ github.run_attempt }} - container: rust:1 + container: public.ecr.aws/docker/library/rust:1 steps: - name: Debug diff --git a/Cargo.lock b/Cargo.lock index d0286c1b..9336521d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -401,6 +401,7 @@ dependencies = [ "tonic", "tonic-prost", "tonic-prost-build", + "tower", "tracing", "vergen-git2", "x25519-dalek", @@ -409,7 +410,7 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=f61ce40927a4d21095ea53a691219d5ae46e3e4e#f61ce40927a4d21095ea53a691219d5ae46e3e4e" +source = "git+https://github.com/DefGuard/defguard.git?rev=a5709e7117103458ad8417d4437a8a369ca5bbce#a5709e7117103458ad8417d4437a8a369ca5bbce" dependencies = [ "http", "os_info", diff --git a/Cargo.toml b/Cargo.toml index 96915c03..b7d63934 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "f61ce40927a4d21095ea53a691219d5ae46e3e4e" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "a5709e7117103458ad8417d4437a8a369ca5bbce" } axum = { version = "0.8", features = ["macros"] } base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } @@ -32,6 +32,7 @@ tonic = { version = "0.14", default-features = false, features = [ ] } tracing = "0.1" tonic-prost = "0.14" +tower = "0.5.2" [target.'cfg(target_os = "linux")'.dependencies] nftnl = { git = "https://github.com/DefGuard/nftnl-rs.git", rev = "1a1147271f43b9d7182a114bb056a5224c35d38f" } diff --git a/src/gateway.rs b/src/gateway.rs index e5155881..4f3d78e8 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -1,5 +1,6 @@ use defguard_version::{ - client::version_interceptor, parse_metadata, ComponentInfo, DefguardComponent, Version, + client::ClientVersionInterceptor, get_tracing_variables, parse_metadata, ComponentInfo, + DefguardComponent, Version, }; use defguard_wireguard_rs::{net::IpAddrMask, WireguardInterfaceApi}; use gethostname::gethostname; @@ -23,10 +24,11 @@ use tokio_stream::wrappers::UnboundedReceiverStream; use tonic::{ codegen::InterceptedService, metadata::{Ascii, MetadataValue}, - service::Interceptor, + service::{Interceptor, InterceptorLayer}, transport::{Certificate, Channel, ClientTlsConfig, Endpoint}, Request, Status, Streaming, }; +use tower::ServiceBuilder; use tracing::{instrument, Instrument}; use crate::{ @@ -41,6 +43,7 @@ use crate::{ gateway_service_client::GatewayServiceClient, stats_update::Payload, update, Configuration, ConfigurationRequest, Peer, StatsUpdate, Update, }, + version::ensure_core_version_supported, VERSION, }; @@ -72,29 +75,23 @@ impl From for InterfaceConfiguration { } } -type InterceptorFn = Box) -> Result, Status> + Send + Sync>; - /// Intercepts all grpc requests adding authentication and version metadata -struct RequestInterceptor { +struct AuthInterceptor { hostname: MetadataValue, token: MetadataValue, - version: defguard_version::Version, - version_interceptor_fn: InterceptorFn, } -impl Clone for RequestInterceptor { +impl Clone for AuthInterceptor { fn clone(&self) -> Self { Self { hostname: self.hostname.clone(), token: self.token.clone(), - version: self.version.clone(), - version_interceptor_fn: Box::new(version_interceptor(self.version.clone())), } } } -impl RequestInterceptor { - fn new(token: &str, version: Version) -> Result { +impl AuthInterceptor { + fn new(token: &str) -> Result { let token = MetadataValue::try_from(token)?; let hostname = MetadataValue::try_from( gethostname() @@ -102,20 +99,12 @@ impl RequestInterceptor { .expect("Unable to get current hostname during gRPC connection setup."), )?; - Ok(Self { - hostname, - token, - version: version.clone(), - version_interceptor_fn: Box::new(version_interceptor(version)), - }) + Ok(Self { hostname, token }) } } -impl Interceptor for RequestInterceptor { - fn call(&mut self, request: Request<()>) -> Result, Status> { - // Apply version interceptor - adds version headers - let mut request = (self.version_interceptor_fn)(request)?; - +impl Interceptor for AuthInterceptor { + fn call(&mut self, mut request: Request<()>) -> Result, Status> { // Add auth headers let metadata = request.metadata_mut(); metadata.insert("authorization", self.token.clone()); @@ -126,6 +115,9 @@ impl Interceptor for RequestInterceptor { } type PubKey = String; +type GatewayClientType = GatewayServiceClient< + InterceptedService, ClientVersionInterceptor>, +>; pub struct Gateway { config: Config, @@ -137,7 +129,7 @@ pub struct Gateway { #[cfg_attr(not(target_os = "linux"), allow(unused))] firewall_config: Option, pub connected: Arc, - client: GatewayServiceClient>, + client: GatewayClientType, core_info: Option, stats_thread: Option>, } @@ -282,7 +274,7 @@ impl Gateway { #[instrument(skip_all)] async fn handle_stats_thread( - mut client: GatewayServiceClient>, + mut client: GatewayClientType, rx: UnboundedReceiverStream, ) { let status = client.stats(rx).await; @@ -463,19 +455,6 @@ impl Gateway { Ok(()) } - fn get_tracing_variables(&self) -> (String, String) { - let version = self - .core_info - .as_ref() - .map_or(String::from("?"), |info| info.version.to_string()); - let info = self - .core_info - .as_ref() - .map_or(String::from("?"), |info| info.system.to_string()); - - (version, info) - } - /// Continuously tries to connect to gRPC endpoint. Once the connection is established /// configures the interface, starts the stats thread, connects and returns the updates stream. async fn connect(&mut self) -> Streaming { @@ -499,7 +478,7 @@ impl Gateway { match (response, stream) { (Ok(response), Ok(stream)) => { self.core_info = parse_metadata(response.metadata()); - let (version, info) = self.get_tracing_variables(); + let (version, info) = get_tracing_variables(&self.core_info); let span = tracing::info_span!( "core_configuration", component = %DefguardComponent::Core, @@ -508,6 +487,10 @@ impl Gateway { ); let _guard = span.enter(); + // check core version and exit if it's not supported + let version = self.core_info.as_ref().map(|info| &info.version); + ensure_core_version_supported(version); + if let Err(err) = self.configure(response.into_inner()) { error!("Interface configuration failed: {err}"); continue; @@ -532,10 +515,7 @@ impl Gateway { } } - fn setup_client( - config: &Config, - ) -> Result>, GatewayError> - { + fn setup_client(config: &Config) -> Result { debug!("Preparing gRPC client configuration"); let tls = ClientTlsConfig::new(); // Use CA if provided, otherwise load certificates from system. @@ -554,9 +534,13 @@ impl Gateway { .keep_alive_while_idle(true) .tls_config(tls)?; let channel = endpoint.connect_lazy(); - let version = Version::parse(VERSION)?; - let request_interceptor = RequestInterceptor::new(&config.token, version)?; - let client = GatewayServiceClient::with_interceptor(channel, request_interceptor); + let version_interceptor = ClientVersionInterceptor::new(Version::parse(VERSION)?); + let auth_interceptor = AuthInterceptor::new(&config.token)?; + let channel = ServiceBuilder::new() + .layer(InterceptorLayer::new(version_interceptor)) + .layer(InterceptorLayer::new(auth_interceptor)) + .service(channel); + let client = GatewayServiceClient::new(channel); debug!("gRPC client configuration done"); Ok(client) @@ -700,7 +684,7 @@ impl Gateway { debug!("Executing specified POST_UP command: {post_up}"); execute_command(post_up)?; } - let (version, info) = self.get_tracing_variables(); + let (version, info) = get_tracing_variables(&self.core_info); let span = tracing::info_span!( "core_grpc", component = %DefguardComponent::Core, diff --git a/src/lib.rs b/src/lib.rs index 251696cf..66c5ef14 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod config; pub mod error; pub mod gateway; pub mod server; +mod version; pub mod proto { pub mod gateway { @@ -26,7 +27,7 @@ use syslog::{BasicLogger, Facility, Formatter3164}; pub mod enterprise; -pub const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "-", env!("VERGEN_GIT_SHA")); +pub const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "+", env!("VERGEN_GIT_SHA")); /// Masks object's field with "***" string. /// Used to log sensitive/secret objects. diff --git a/src/version.rs b/src/version.rs new file mode 100644 index 00000000..dcd9e985 --- /dev/null +++ b/src/version.rs @@ -0,0 +1,18 @@ +use defguard_version::Version; + +const MIN_CORE_VERSION: Version = Version::new(1, 5, 0); + +/// Ensures the core version meets minimum version requirements. +/// Terminates the process if it doesn't. +pub(crate) fn ensure_core_version_supported(core_version: Option<&Version>) { + let Some(core_version) = core_version else { + error!("Missing core component version information. This most likely means that core component uses unsupported version. Exiting."); + std::process::exit(1); + }; + if core_version < &MIN_CORE_VERSION { + error!("Core version {core_version} is not supported. Minimal supported core version is {MIN_CORE_VERSION}. Exiting."); + std::process::exit(1); + } + + info!("Core version {core_version} is supported"); +} From 1cb3e439490b9ccf2caec1cac4807466972649b4 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Fri, 29 Aug 2025 10:19:13 +0200 Subject: [PATCH 16/25] Fix version comparison (#199) Don't compare version build metadata --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/version.rs | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9336521d..c349c11f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,7 +410,7 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=a5709e7117103458ad8417d4437a8a369ca5bbce#a5709e7117103458ad8417d4437a8a369ca5bbce" +source = "git+https://github.com/DefGuard/defguard.git?rev=db678a95398e38b72bbb4ecef36a27caa427e48c#db678a95398e38b72bbb4ecef36a27caa427e48c" dependencies = [ "http", "os_info", diff --git a/Cargo.toml b/Cargo.toml index b7d63934..fd41f589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "a5709e7117103458ad8417d4437a8a369ca5bbce" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "db678a95398e38b72bbb4ecef36a27caa427e48c" } axum = { version = "0.8", features = ["macros"] } base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } diff --git a/src/version.rs b/src/version.rs index dcd9e985..754bc7f4 100644 --- a/src/version.rs +++ b/src/version.rs @@ -1,4 +1,4 @@ -use defguard_version::Version; +use defguard_version::{is_version_lower, Version}; const MIN_CORE_VERSION: Version = Version::new(1, 5, 0); @@ -6,10 +6,11 @@ const MIN_CORE_VERSION: Version = Version::new(1, 5, 0); /// Terminates the process if it doesn't. pub(crate) fn ensure_core_version_supported(core_version: Option<&Version>) { let Some(core_version) = core_version else { - error!("Missing core component version information. This most likely means that core component uses unsupported version. Exiting."); + error!("Missing core component version information. This most likely means that core component uses outdated version. Exiting."); std::process::exit(1); }; - if core_version < &MIN_CORE_VERSION { + + if is_version_lower(core_version, &MIN_CORE_VERSION) { error!("Core version {core_version} is not supported. Minimal supported core version is {MIN_CORE_VERSION}. Exiting."); std::process::exit(1); } From c39ffb92832f02397ffe7b3dd1e1085a0fe81acb Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Fri, 29 Aug 2025 10:54:36 +0200 Subject: [PATCH 17/25] switch ami to debian (#198) --- images/ami/gateway.pkr.hcl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/images/ami/gateway.pkr.hcl b/images/ami/gateway.pkr.hcl index 431edbea..dbc86156 100644 --- a/images/ami/gateway.pkr.hcl +++ b/images/ami/gateway.pkr.hcl @@ -27,14 +27,14 @@ source "amazon-ebs" "defguard-gateway" { region = var.region source_ami_filter { filters = { - name = "ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*" + name = "debian-13-amd64-*" root-device-type = "ebs" virtualization-type = "hvm" } most_recent = true - owners = ["099720109477"] + owners = ["136693071363"] } - ssh_username = "ubuntu" + ssh_username = "admin" } build { From b85e5338fe4a77e41446336b53e9a1f5eed4fba3 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 1 Sep 2025 13:11:53 +0200 Subject: [PATCH 18/25] Update tracing_subscriber (#200) --- Cargo.lock | 114 ++++++++++++++++------------------------------------- 1 file changed, 33 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c349c11f..29203d66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,9 +235,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.5.45" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318" +checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" dependencies = [ "clap_builder", "clap_derive", @@ -245,9 +245,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.44" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8" +checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" dependencies = [ "anstream", "anstyle", @@ -445,9 +445,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.4.0" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +checksum = "d630bccd429a5bb5a64b5e94f693bfc48c9f8566418fda4c494cc94f911f87cc" dependencies = [ "powerfmt", ] @@ -651,7 +651,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.3+wasi-0.2.4", ] [[package]] @@ -1069,11 +1069,11 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -1260,12 +1260,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -1322,12 +1321,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "paste" version = "1.0.15" @@ -1418,9 +1411,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -1565,17 +1558,8 @@ checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.10", - "regex-syntax 0.8.6", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -1586,15 +1570,9 @@ checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.6", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.6" @@ -1971,12 +1949,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.41" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +checksum = "8ca967379f9d8eb8058d86ed467d81d03e81acd45757e4ca341c24affbe8e8e3" dependencies = [ "deranged", - "itoa", "libc", "num-conv", "num_threads", @@ -1988,15 +1965,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" +checksum = "a9108bb380861b07264b950ded55a44a14a4adc68b9f5efd85aafc3aa4d40a68" [[package]] name = "time-macros" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +checksum = "7182799245a7264ce590b349d90338f1c1affad93d2639aed5f8f69c090b334c" dependencies = [ "num-conv", "time-core", @@ -2255,14 +2232,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -2387,35 +2364,13 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.3+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" dependencies = [ - "wit-bindgen-rt", + "wit-bindgen", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-link" version = "0.1.3" @@ -2585,13 +2540,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags", -] +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" [[package]] name = "writeable" From 71a570574e8683ac3f716af185f02a9d57663301 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Mon, 1 Sep 2025 16:30:33 +0200 Subject: [PATCH 19/25] Bump defguard-version version (#201) * bump * bump --- Cargo.lock | 3 ++- Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29203d66..2d5eea6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,8 +410,9 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=db678a95398e38b72bbb4ecef36a27caa427e48c#db678a95398e38b72bbb4ecef36a27caa427e48c" +source = "git+https://github.com/DefGuard/defguard.git?rev=be3f96ced072ede3ebde72f2f6c6063d2e7f7403#be3f96ced072ede3ebde72f2f6c6063d2e7f7403" dependencies = [ + "axum", "http", "os_info", "semver", diff --git a/Cargo.toml b/Cargo.toml index fd41f589..95561be4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "db678a95398e38b72bbb4ecef36a27caa427e48c" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "be3f96ced072ede3ebde72f2f6c6063d2e7f7403" } axum = { version = "0.8", features = ["macros"] } base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } From 5b2708303e644b424b3722f87ca909f2716b0269 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Tue, 2 Sep 2025 08:56:39 +0200 Subject: [PATCH 20/25] Fix ami building (#202) --- images/ami/gateway.pkr.hcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/ami/gateway.pkr.hcl b/images/ami/gateway.pkr.hcl index dbc86156..3b3bd353 100644 --- a/images/ami/gateway.pkr.hcl +++ b/images/ami/gateway.pkr.hcl @@ -53,7 +53,7 @@ build { } provisioner "shell" { - inline = ["rm /home/ubuntu/.ssh/authorized_keys"] + inline = ["rm /home/admin/.ssh/authorized_keys"] } provisioner "shell" { From 1ba0d5b04e56b5c8774cd119da4b86522216fccf Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Sep 2025 11:47:34 +0200 Subject: [PATCH 21/25] Build with never defguard_version (#203) --- .github/workflows/ci.yml | 3 - Cargo.lock | 154 ++++++++++++++++----------------------- Cargo.toml | 8 +- src/gateway.rs | 77 ++++++++++++++------ 4 files changed, 120 insertions(+), 122 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3710f64c..cc9a2889 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,9 +28,6 @@ jobs: container: public.ecr.aws/docker/library/rust:1 steps: - - name: Debug - run: echo ${{ github.ref_name }} - - name: Checkout uses: actions/checkout@v4 with: diff --git a/Cargo.lock b/Cargo.lock index 2d5eea6e..61b5739c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,7 +112,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" dependencies = [ "axum-core", - "axum-macros", "bytes", "form_urlencoded", "futures-util", @@ -160,17 +159,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "axum-macros" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "backtrace" version = "0.3.75" @@ -194,9 +182,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.9.3" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "byteorder" @@ -212,10 +200,11 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.34" +version = "1.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -235,9 +224,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "clap" -version = "4.5.46" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" +checksum = "7eac00902d9d136acd712710d71823fb8ac8004ca445a89e73a41d45aa712931" dependencies = [ "clap_builder", "clap_derive", @@ -245,9 +234,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.46" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" +checksum = "2ad9bbf750e73b5884fb8a211a9424a1906c1e156724260fdae972f31d70e1d6" dependencies = [ "anstream", "anstyle", @@ -257,9 +246,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.45" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck", "proc-macro2", @@ -394,7 +383,7 @@ dependencies = [ "prost", "serde", "syslog", - "thiserror 2.0.16", + "thiserror", "tokio", "tokio-stream", "toml", @@ -410,13 +399,14 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=be3f96ced072ede3ebde72f2f6c6063d2e7f7403#be3f96ced072ede3ebde72f2f6c6063d2e7f7403" +source = "git+https://github.com/DefGuard/defguard.git?rev=168bbd8e737e0aa920c6356947e1b6be99c9031b#168bbd8e737e0aa920c6356947e1b6be99c9031b" dependencies = [ "axum", "http", "os_info", "semver", - "thiserror 2.0.16", + "serde", + "thiserror", "tonic", "tower", "tracing", @@ -425,9 +415,9 @@ dependencies = [ [[package]] name = "defguard_wireguard_rs" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2d2f56ffaf56903a51b128c6f6730b8b344fab0d0be0f5db0b65dcccbb7334" +checksum = "093cede63322e14eede3916a6a5de2518788f438a6cdfc71d262c72d0ae865d0" dependencies = [ "base64", "libc", @@ -440,7 +430,7 @@ dependencies = [ "netlink-sys", "nix", "serde", - "thiserror 2.0.16", + "thiserror", "x25519-dalek", ] @@ -552,6 +542,12 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "find-msvc-tools" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650" + [[package]] name = "fixedbitset" version = "0.5.7" @@ -1064,9 +1060,9 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "matchers" @@ -1153,66 +1149,55 @@ checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" [[package]] name = "netlink-packet-core" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +checksum = "745d789fe0958caf7252f5e1e900ce5c09b6a5bf05c7bba02a9cc600866ce31e" dependencies = [ - "anyhow", - "byteorder", - "netlink-packet-utils", + "pastey", ] [[package]] name = "netlink-packet-generic" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd7eb8ad331c84c6b8cb7f685b448133e5ad82e1ffd5acafac374af4a5a308b" +checksum = "2f891b2e0054cac5a684a06628f59568f841c93da4e551239da6e518f539e775" dependencies = [ - "anyhow", - "byteorder", "netlink-packet-core", - "netlink-packet-utils", ] [[package]] name = "netlink-packet-route" -version = "0.22.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0e7987b28514adf555dc1f9a5c30dfc3e50750bbaffb1aec41ca7b23dcd8e4" +checksum = "3ec2f5b6839be2a19d7fa5aab5bc444380f6311c2b693551cb80f45caaa7b5ef" dependencies = [ - "anyhow", "bitflags", - "byteorder", "libc", "log", "netlink-packet-core", - "netlink-packet-utils", ] [[package]] name = "netlink-packet-utils" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +checksum = "3176f18d11a1ae46053e59ec89d46ba318ae1343615bd3f8c908bfc84edae35c" dependencies = [ - "anyhow", "byteorder", - "paste", - "thiserror 1.0.69", + "pastey", + "thiserror", ] [[package]] name = "netlink-packet-wireguard" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b25b050ff1f6a1e23c6777b72db22790fe5b6b5ccfd3858672587a79876c8f" +checksum = "598962d9067d3153a00106da10e7b8276cea68f396f4a22f5b4a079270d92e29" dependencies = [ - "anyhow", - "byteorder", "libc", "log", + "netlink-packet-core", "netlink-packet-generic", - "netlink-packet-utils", ] [[package]] @@ -1323,10 +1308,10 @@ dependencies = [ ] [[package]] -name = "paste" -version = "1.0.15" +name = "pastey" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" [[package]] name = "percent-encoding" @@ -1718,6 +1703,9 @@ name = "semver" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] [[package]] name = "serde" @@ -1899,33 +1887,13 @@ dependencies = [ "windows-sys 0.60.2", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - [[package]] name = "thiserror" version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.16", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "thiserror-impl", ] [[package]] @@ -1950,9 +1918,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca967379f9d8eb8058d86ed467d81d03e81acd45757e4ca341c24affbe8e8e3" +checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" dependencies = [ "deranged", "libc", @@ -1966,15 +1934,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9108bb380861b07264b950ded55a44a14a4adc68b9f5efd85aafc3aa4d40a68" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7182799245a7264ce590b349d90338f1c1affad93d2639aed5f8f69c090b334c" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -2087,9 +2055,9 @@ dependencies = [ [[package]] name = "tonic" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac5a8627ada0968acec063a4746bf79588aa03ccb66db2f75d7dce26722a40" +checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203" dependencies = [ "async-trait", "axum", @@ -2119,9 +2087,9 @@ dependencies = [ [[package]] name = "tonic-build" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49e323d8bba3be30833707e36d046deabf10a35ae8ad3cae576943ea8933e25d" +checksum = "4c40aaccc9f9eccf2cd82ebc111adc13030d23e887244bc9cfa5d1d636049de3" dependencies = [ "prettyplease", "proc-macro2", @@ -2131,9 +2099,9 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c511b9a96d40cb12b7d5d00464446acf3b9105fd3ce25437cfe41c92b1c87d" +checksum = "66bd50ad6ce1252d87ef024b3d64fe4c3cf54a86fb9ef4c631fdd0ded7aeaa67" dependencies = [ "bytes", "prost", @@ -2142,9 +2110,9 @@ dependencies = [ [[package]] name = "tonic-prost-build" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ef298fcd01b15e135440c4b8c974460ceca4e6a5af7f1c933b08e4d2875efa1" +checksum = "b4a16cba4043dc3ff43fcb3f96b4c5c154c64cbd18ca8dce2ab2c6a451d058a2" dependencies = [ "prettyplease", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 95561be4..c94c0985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,11 @@ version = "1.5.0" edition = "2021" [dependencies] -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "be3f96ced072ede3ebde72f2f6c6063d2e7f7403" } -axum = { version = "0.8", features = ["macros"] } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "168bbd8e737e0aa920c6356947e1b6be99c9031b" } +axum = "0.8" base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } -defguard_wireguard_rs = "0.7.5" +defguard_wireguard_rs = "0.7.6" env_logger = "0.11" gethostname = "1.0" ipnetwork = "0.21" @@ -32,7 +32,7 @@ tonic = { version = "0.14", default-features = false, features = [ ] } tracing = "0.1" tonic-prost = "0.14" -tower = "0.5.2" +tower = "0.5" [target.'cfg(target_os = "linux")'.dependencies] nftnl = { git = "https://github.com/DefGuard/nftnl-rs.git", rev = "1a1147271f43b9d7182a114bb056a5224c35d38f" } diff --git a/src/gateway.rs b/src/gateway.rs index 4f3d78e8..ce093adc 100644 --- a/src/gateway.rs +++ b/src/gateway.rs @@ -1,6 +1,6 @@ use defguard_version::{ - client::ClientVersionInterceptor, get_tracing_variables, parse_metadata, ComponentInfo, - DefguardComponent, Version, + client::ClientVersionInterceptor, get_tracing_variables, ComponentInfo, DefguardComponent, + Version, }; use defguard_wireguard_rs::{net::IpAddrMask, WireguardInterfaceApi}; use gethostname::gethostname; @@ -319,10 +319,16 @@ impl Gateway { } } - debug!("Defguard ACL rules are the same. Rules have not changed. My rules: {current_rules:?}, new rules: {new_rules:?}"); + debug!( + "Defguard ACL rules are the same. Rules have not changed. My rules: \ + {current_rules:?}, new rules: {new_rules:?}" + ); false } else { - debug!("There are new Defguard ACL rules in the new configuration, but we don't have any in the current one. Rules have changed."); + debug!( + "There are new Defguard ACL rules in the new configuration, but we don't have \ + any in the current one. Rules have changed." + ); true } } @@ -351,18 +357,24 @@ impl Gateway { } } - debug!("SNAT bindings are the same. Bindings have not changed. My bindings: {current_bindings:?}, new bindings: {new_bindings:?}"); + debug!( + "SNAT bindings are the same. Bindings have not changed. My bindings: \ + {current_bindings:?}, new bindings: {new_bindings:?}" + ); false } else { - debug!("There are new SNAT bindings in the new configuration, but we don't have any in the current one. Bindings have changed."); + debug!( + "There are new SNAT bindings in the new configuration, but we don't have any in \ + the current one. Bindings have changed." + ); true } } /// Process and apply firewall configuration changes. /// - If the main config changed (default policy), reconfigure the whole firewall. - /// - If only the rules changed, apply the new rules. Currently also reconfigures the whole firewall but that - /// should be temporary. + /// - If only the rules changed, apply the new rules. Currently also reconfigures the whole + /// firewall but that should be temporary. /// /// TODO: Reduce cloning here fn process_firewall_changes( @@ -372,7 +384,10 @@ impl Gateway { if let Some(fw_config) = fw_config { debug!("Received firewall configuration: {fw_config:?}"); if self.has_firewall_config_changed(fw_config) { - debug!("Received firewall configuration is different than current one. Reconfiguring firewall..."); + debug!( + "Received firewall configuration is different than current one. \ + Reconfiguring firewall..." + ); self.firewall_api.begin()?; self.firewall_api .setup(fw_config.default_policy, self.config.fw_priority)?; @@ -383,7 +398,10 @@ impl Gateway { self.firewall_config = Some(fw_config.clone()); info!("Reconfigured firewall with new configuration"); } else { - debug!("Received firewall configuration is the same as current one. Skipping reconfiguration."); + debug!( + "Received firewall configuration is the same as current one. Skipping \ + reconfiguration." + ); } } else { debug!("Received firewall configuration is empty, cleaning up firewall rules..."); @@ -417,7 +435,7 @@ impl Gateway { if self.is_interface_config_changed(&new_interface_configuration, &new_configuration.peers) { debug!( - "Received configuration is different than the current one. Reconfiguring interface..." + "Received configuration is different than the current one. Reconfiguring interface." ); self.wgapi .lock() @@ -435,7 +453,10 @@ impl Gateway { self.interface_configuration = Some(new_interface_configuration); self.replace_peers(new_configuration.peers); } else { - debug!("Received configuration is identical to the current one. Skipping interface reconfiguration."); + debug!( + "Received configuration is identical to the current one. Skipping interface \ + reconfiguration." + ); } // process received firewall config unless firewall management is disabled @@ -477,12 +498,12 @@ impl Gateway { }; match (response, stream) { (Ok(response), Ok(stream)) => { - self.core_info = parse_metadata(response.metadata()); + self.core_info = ComponentInfo::from_metadata(response.metadata()); let (version, info) = get_tracing_variables(&self.core_info); let span = tracing::info_span!( "core_configuration", component = %DefguardComponent::Core, - version, + version = version.to_string(), info ); let _guard = span.enter(); @@ -503,12 +524,18 @@ impl Gateway { break stream.into_inner(); } (Err(err), _) => { - error!("Couldn't retrieve gateway configuration from the core. Using gRPC URL: {}. Retrying in 10s. Error: {err}", - self.config.grpc_url); + error!( + "Couldn't retrieve gateway configuration from the core. Using gRPC URL: \ + {}. Retrying in 10s. Error: {err}", + self.config.grpc_url + ); } (_, Err(err)) => { - error!("Couldn't establish streaming connection to the core. Using gRPC URL: {}. Retrying in 10s. Error: {err}", - self.config.grpc_url); + error!( + "Couldn't establish streaming connection to the core. Using gRPC URL: \ + {}. Retrying in 10s. Error: {err}", + self.config.grpc_url + ); } } sleep(TEN_SECS).await; @@ -590,7 +617,10 @@ impl Gateway { } Some(update::Update::FirewallConfig(config)) => { if self.config.disable_firewall_management { - debug!("Received firewall config update, but firewall management is disabled. Skipping processing this update: {config:?}"); + debug!( + "Received firewall config update, but firewall management \ + is disabled. Skipping processing this update: {config:?}" + ); continue; } @@ -622,7 +652,10 @@ impl Gateway { } Some(update::Update::DisableFirewall(())) => { if self.config.disable_firewall_management { - debug!("Received firewall disable request, but firewall management is disabled. Skipping processing this update"); + debug!( + "Received firewall disable request, but firewall management \ + is disabled. Skipping processing this update" + ); continue; } @@ -675,7 +708,7 @@ impl Gateway { } info!( - "Trying to connect to {} and obtain the gateway configuration from Defguard...", + "Trying to connect to {} and obtain the gateway configuration from Defguard.", self.config.grpc_url ); loop { @@ -688,7 +721,7 @@ impl Gateway { let span = tracing::info_span!( "core_grpc", component = %DefguardComponent::Core, - version, + version = version.to_string(), info, ); let _guard = span.enter(); From db858533b62048f75d3dd7a9765e8cf9512ea4f5 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Fri, 5 Sep 2025 11:38:06 +0200 Subject: [PATCH 22/25] Update defguard-version version (#204) --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 61b5739c..f41b4430 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,7 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=168bbd8e737e0aa920c6356947e1b6be99c9031b#168bbd8e737e0aa920c6356947e1b6be99c9031b" +source = "git+https://github.com/DefGuard/defguard.git?rev=9aaa99f38eb9e4a07685d47779a8a43d0047b7b9#9aaa99f38eb9e4a07685d47779a8a43d0047b7b9" dependencies = [ "axum", "http", diff --git a/Cargo.toml b/Cargo.toml index c94c0985..958a3326 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "168bbd8e737e0aa920c6356947e1b6be99c9031b" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "9aaa99f38eb9e4a07685d47779a8a43d0047b7b9" } axum = "0.8" base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } From 5d3549f9b50251389f979441d3730b0b914ec08a Mon Sep 17 00:00:00 2001 From: Maciek <19913370+wojcik91@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:41:52 +0200 Subject: [PATCH 23/25] pre release 1.5 cleanup (#206) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Merge adjacent IP subnets into ranges for nftables (#182) * restore merging of ips * merge adjacent subnets for nft (#185) * merge adjacent elements * update new tests * linter fixes * linter fix * review fixes --------- Co-authored-by: Maciek <19913370+wojcik91@users.noreply.github.com> Co-authored-by: Maciej Wójcik Co-authored-by: Maciej Wójcik * bump version (#188) * update dependencies * update protos * update core dependency --------- Co-authored-by: Aleksander <170264518+t-aleksander@users.noreply.github.com> --- Cargo.lock | 92 ++++++++++++++++++++++++++++++++++-------------------- Cargo.toml | 2 +- flake.lock | 12 +++---- proto | 2 +- 4 files changed, 66 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f41b4430..210b9d7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -200,9 +200,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cc" -version = "1.2.35" +version = "1.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "590f9024a68a8c40351881787f1934dc11afd69090f5edb6831464694d836ea3" +checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54" dependencies = [ "find-msvc-tools", "jobserver", @@ -399,7 +399,7 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=9aaa99f38eb9e4a07685d47779a8a43d0047b7b9#9aaa99f38eb9e4a07685d47779a8a43d0047b7b9" +source = "git+https://github.com/DefGuard/defguard.git?rev=c8e1bc91e0410a91af0b124d2d2b57c5a7226517#c8e1bc91e0410a91af0b124d2d2b57c5a7226517" dependencies = [ "axum", "http", @@ -522,12 +522,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -544,9 +544,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e178e4fba8a2726903f6ba98a6d221e76f9c12c650d5dc0e6afdc50677b49650" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" [[package]] name = "fixedbitset" @@ -648,7 +648,7 @@ dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.3+wasi-0.2.4", + "wasi 0.14.5+wasi-0.2.4", ] [[package]] @@ -709,7 +709,7 @@ checksum = "a56f203cd1c76362b69e3863fd987520ac36cf70a8c92627449b2f64a8cf7d65" dependencies = [ "cfg-if", "libc", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -930,9 +930,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "206a8042aec68fa4a62e8d3f7aa4ceb508177d9324faf261e1959e495b7a1921" dependencies = [ "equivalent", "hashbrown", @@ -1048,9 +1048,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -1596,15 +1596,15 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -1668,18 +1668,18 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schannel" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] name = "security-framework" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80fb1d92c5028aa318b4b8bd7302a5bfcf48be96a37fc6fc790f806b0004ee0c" +checksum = "60b369d18893388b345804dc0007963c99b7d665ae71d275812d828c6f089640" dependencies = [ "bitflags", "core-foundation", @@ -1690,9 +1690,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.14.0" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", @@ -1876,15 +1876,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -2231,9 +2231,9 @@ checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "untrusted" @@ -2333,9 +2333,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.3+wasi-0.2.4" +version = "0.14.5+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4494f6290a82f5fe584817a676a34b9d6763e8d9d18204009fb31dceca98fd4" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.0+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +checksum = "03fa2761397e5bd52002cd7e73110c71af2109aca4e521a9f40473fe685b0a24" dependencies = [ "wit-bindgen", ] @@ -2346,6 +2355,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + [[package]] name = "windows-sys" version = "0.52.0" @@ -2373,6 +2388,15 @@ dependencies = [ "windows-targets 0.53.3", ] +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -2395,7 +2419,7 @@ version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -2510,9 +2534,9 @@ checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" [[package]] name = "wit-bindgen" -version = "0.45.0" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" +checksum = "5c573471f125075647d03df72e026074b7203790d41351cd6edc96f46bcccd36" [[package]] name = "writeable" diff --git a/Cargo.toml b/Cargo.toml index 958a3326..20fbf4e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "9aaa99f38eb9e4a07685d47779a8a43d0047b7b9" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "c8e1bc91e0410a91af0b124d2d2b57c5a7226517" } axum = "0.8" base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } diff --git a/flake.lock b/flake.lock index 4c226f53..abf59ab7 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1754725699, - "narHash": "sha256-iAcj9T/Y+3DBy2J0N+yF9XQQQ8IEb5swLFzs23CdP88=", + "lastModified": 1757347588, + "narHash": "sha256-tLdkkC6XnsY9EOZW9TlpesTclELy8W7lL2ClL+nma8o=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "85dbfc7aaf52ecb755f87e577ddbe6dbbdbc1054", + "rev": "b599843bad24621dcaa5ab60dac98f9b0eb1cabe", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1754880555, - "narHash": "sha256-tG6l0wiX8V8IvG4HFYY8IYN5vpNAxQ+UWunjjpE6SqU=", + "lastModified": 1757471515, + "narHash": "sha256-0+rSzNsYindDWjO9VVULKGjXlPsQV6IDjRU5G3SwI9U=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "17c591a44e4eb77f05f27cd37e1cfc3f219c7fc4", + "rev": "aecf31120156fe47a7d1992aa814052910178fca", "type": "github" }, "original": { diff --git a/proto b/proto index c0aef683..883487df 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit c0aef68395720f46a7f038b6766de3bb30e02930 +Subproject commit 883487df67d90fd14fae900737cd8b5ea6c10de3 From 3419d8ee8ee29c3cc5ae2d5285ebb240192a6938 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 11 Sep 2025 09:56:16 +0200 Subject: [PATCH 24/25] update defguard_version dependency (#209) --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 210b9d7c..51c5e6cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,7 +399,7 @@ dependencies = [ [[package]] name = "defguard_version" version = "0.0.0" -source = "git+https://github.com/DefGuard/defguard.git?rev=c8e1bc91e0410a91af0b124d2d2b57c5a7226517#c8e1bc91e0410a91af0b124d2d2b57c5a7226517" +source = "git+https://github.com/DefGuard/defguard.git?rev=8649a9ba225d7bd2066a09c9e1347705c34bd158#8649a9ba225d7bd2066a09c9e1347705c34bd158" dependencies = [ "axum", "http", diff --git a/Cargo.toml b/Cargo.toml index 20fbf4e3..967c7a08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ version = "1.5.0" edition = "2021" [dependencies] -defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "c8e1bc91e0410a91af0b124d2d2b57c5a7226517" } +defguard_version = { git = "https://github.com/DefGuard/defguard.git", rev = "8649a9ba225d7bd2066a09c9e1347705c34bd158" } axum = "0.8" base64 = "0.22" clap = { version = "4.5", features = ["derive", "env"] } From ca1fa38c19bf59633d1e3c0decd56462a18c59d1 Mon Sep 17 00:00:00 2001 From: Maciek <19913370+wojcik91@users.noreply.github.com> Date: Mon, 15 Sep 2025 08:44:41 +0200 Subject: [PATCH 25/25] Migrate docker builds to AWS (#210) * attempt to run docker build on AWS * add missing parameter * remove buildspec override * try adjusting token permissions * disable legacy mirror * try using registry cache * fix cache key * fix cache key * try out another arm image * cleanup * remove temporary override --- .github/workflows/build-docker.yml | 33 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 3f4cd5f6..1f21aa95 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -18,22 +18,23 @@ env: jobs: build-docker: runs-on: - - self-hosted - - Linux - - ${{ matrix.runner }} - + - codebuild-defguard-gateway-runner-${{ github.run_id }}-${{ github.run_attempt }} + image:${{ matrix.os }} + instance-size:${{ matrix.size }} strategy: matrix: - cpu: [arm64, amd64, arm/v7] include: - - cpu: arm64 - runner: ARM64 + - os: arm-3.0 + size: xlarge + cpu: arm64 tag: arm64 - - cpu: amd64 - runner: X64 + - os: ubuntu-7.0 + size: xlarge + cpu: amd64 tag: amd64 - - cpu: arm/v7 - runner: ARM + - os: arm-3.0 + size: xlarge + cpu: arm/v7 tag: armv7 permissions: @@ -55,10 +56,6 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - with: - buildkitd-config-inline: | - [registry."docker.io"] - mirrors = ["dockerhub-proxy.teonite.net"] - name: Build container uses: docker/build-push-action@v5 @@ -68,8 +65,10 @@ jobs: provenance: false push: true tags: "${{ env.GHCR_REPO }}:${{ github.sha }}-${{ matrix.tag }}" - cache-from: type=gha - cache-to: type=gha,mode=max + cache-from: | + type=registry,ref=${{ env.GHCR_REPO }}:cache-${{ matrix.tag }} + type=registry,ref=${{ env.GHCR_REPO }}:cache-${{ matrix.tag }}-${{ github.ref_name }} + cache-to: type=registry,mode=max,ref=${{ env.GHCR_REPO }}:cache-${{ matrix.tag }}-${{ github.ref_name }} - name: Scan image with Trivy uses: aquasecurity/trivy-action@0.32.0