From 7b1e1fef9cc325303ae9107b9352019dc3a0e67b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Mon, 16 Jun 2025 09:32:25 +0200 Subject: [PATCH 01/21] update dependencies --- Cargo.lock | 421 ++++++++++++++++++++++++----------------------------- flake.lock | 12 +- 2 files changed, 193 insertions(+), 240 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 303233b639..4b8075201e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aead" @@ -105,9 +105,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.18" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ "anstyle", "anstyle-parse", @@ -120,33 +120,33 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] name = "anstyle-parse" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.8" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" dependencies = [ "anstyle", "once_cell_polyfill", @@ -205,7 +205,7 @@ checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "synstructure", ] @@ -217,7 +217,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -239,7 +239,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -250,7 +250,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -460,9 +460,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" -version = "1.7.3" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" [[package]] name = "base64urlsafedata" @@ -554,9 +554,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.17.0" +version = "3.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" [[package]] name = "byteorder" @@ -613,9 +613,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.25" +version = "1.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" +checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" dependencies = [ "jobserver", "libc", @@ -633,9 +633,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -708,9 +708,9 @@ checksum = "bba18ee93d577a8428902687bcc2b6b45a56b1981a1f6d779731c86cc4c5db18" [[package]] name = "clap" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" dependencies = [ "clap_builder", "clap_derive", @@ -718,9 +718,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" dependencies = [ "anstream", "anstyle", @@ -730,21 +730,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "cmac" @@ -759,9 +759,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "compact_jwt" @@ -1021,7 +1021,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1045,7 +1045,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1056,7 +1056,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1253,7 +1253,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1274,7 +1274,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1284,7 +1284,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1297,7 +1297,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1317,7 +1317,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "unicode-xid", ] @@ -1356,7 +1356,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1579,9 +1579,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "libz-rs-sys", @@ -1712,7 +1712,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -1758,9 +1758,9 @@ dependencies = [ [[package]] name = "getopts" -version = "0.2.21" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +checksum = "cba6ae63eb948698e300f645f87c70f76630d505f23b8907cf1e193ee85048c1" dependencies = [ "unicode-width", ] @@ -1774,7 +1774,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1899,9 +1899,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" dependencies = [ "allocator-api2", "equivalent", @@ -1914,7 +1914,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -2081,9 +2081,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.6" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" dependencies = [ "http", "hyper", @@ -2127,9 +2127,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" dependencies = [ "base64 0.22.1", "bytes", @@ -2331,7 +2331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "serde", ] @@ -2525,9 +2525,9 @@ dependencies = [ [[package]] name = "lettre" -version = "0.11.16" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ffd14fa289730e3ad68edefdc31f603d56fe716ec38f2076bb7410e09147c2" +checksum = "cb2a0354e9ece2fcdcf9fa53417f6de587230c0c248068eb058fa26c4a753179" dependencies = [ "async-trait", "base64 0.22.1", @@ -2553,9 +2553,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.172" +version = "0.2.173" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb" [[package]] name = "libgit2-sys" @@ -2587,9 +2587,9 @@ dependencies = [ [[package]] name = "libz-rs-sys" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6489ca9bd760fe9642d7644e827b0c9add07df89857b0416ee15c1cc1a3b8c5a" +checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" dependencies = [ "zlib-rs", ] @@ -2715,9 +2715,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "mime" @@ -2743,9 +2743,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] @@ -2757,7 +2757,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -2766,7 +2766,7 @@ name = "model_derive" version = "0.0.0" dependencies = [ "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -2920,7 +2920,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3054,7 +3054,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3241,9 +3241,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6" +checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", "thiserror 2.0.12", @@ -3252,9 +3252,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5" +checksum = "bb056d9e8ea77922845ec74a1c4e8fb17e7c218cc4fc11a15c5d25e189aa40bc" dependencies = [ "pest", "pest_generator", @@ -3262,24 +3262,23 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841" +checksum = "87e404e638f781eb3202dc82db6760c8ae8a1eeef7fb3fa8264b2ef280504966" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "pest_meta" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0" +checksum = "edd1101f170f5903fde0914f899bb503d9ff5271d7ba76bbb70bea63690cc0d5" dependencies = [ - "once_cell", "pest", "sha2", ] @@ -3415,7 +3414,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3495,12 +3494,12 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.33" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" +checksum = "6837b9e10d61f45f987d50808f83d1ee3d206c66acf650c3e4ae2e1f6ddedf55" dependencies = [ "proc-macro2", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3556,7 +3555,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.101", + "syn 2.0.103", "tempfile", ] @@ -3570,7 +3569,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -3763,13 +3762,33 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" dependencies = [ "bitflags 2.9.1", ] +[[package]] +name = "ref-cast" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + [[package]] name = "regex" version = "1.11.1" @@ -3816,9 +3835,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.19" +version = "0.12.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" +checksum = "eabf4c97d9130e2bf606614eb937e86edac8292eaa6f422f995d7e8de1eb1813" dependencies = [ "base64 0.22.1", "bytes", @@ -3835,13 +3854,11 @@ dependencies = [ "hyper-rustls", "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", "mime", "mime_guess", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", @@ -3940,7 +3957,7 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils", - "syn 2.0.101", + "syn 2.0.103", "walkdir", ] @@ -3968,9 +3985,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -4096,6 +4113,18 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -4214,7 +4243,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4286,15 +4315,16 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +checksum = "bf65a400f8f66fb7b0552869ad70157166676db75ed8181f8104ea91cf9d0b42" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", "indexmap 2.9.0", + "schemars", "serde", "serde_derive", "serde_json", @@ -4304,14 +4334,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +checksum = "81679d9ed988d5e9a5e6531dc3f2c28efbd639cbd1dfb628df08edea6004da77" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4445,12 +4475,9 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "slug" @@ -4464,9 +4491,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ "serde", ] @@ -4530,7 +4557,7 @@ dependencies = [ "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.15.3", + "hashbrown 0.15.4", "hashlink", "indexmap 2.9.0", "ipnetwork", @@ -4561,7 +4588,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4584,7 +4611,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.101", + "syn 2.0.103", "tokio", "url", ] @@ -4793,7 +4820,7 @@ checksum = "ac94fea04bf721f57ed7f421e64d3a04858e15708d00e8aa814cad7507427503" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4815,7 +4842,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4837,9 +4864,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" dependencies = [ "proc-macro2", "quote", @@ -4863,7 +4890,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4948,7 +4975,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -4959,17 +4986,16 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "thread_local" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -5064,7 +5090,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -5114,15 +5140,15 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.9" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" [[package]] name = "toml_edit" -version = "0.22.26" +version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap 2.9.0", "toml_datetime", @@ -5174,7 +5200,7 @@ dependencies = [ "prost-build", "prost-types", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -5292,20 +5318,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -5348,7 +5374,7 @@ checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -5483,9 +5509,9 @@ checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" [[package]] name = "unicode-xid" @@ -5560,7 +5586,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.101", + "syn 2.0.103", "uuid", ] @@ -5679,9 +5705,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -5720,7 +5746,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "wasm-bindgen-shared", ] @@ -5755,7 +5781,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5960,7 +5986,7 @@ dependencies = [ "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.2", + "windows-strings", ] [[package]] @@ -5971,7 +5997,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -5982,24 +6008,24 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-registry" -version = "0.4.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" +checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" dependencies = [ + "windows-link", "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", + "windows-strings", ] [[package]] @@ -6011,15 +6037,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-strings" version = "0.4.2" @@ -6080,29 +6097,13 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", + "windows_i686_gnullvm", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] -[[package]] -name = "windows-targets" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -6115,12 +6116,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -6133,12 +6128,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -6151,24 +6140,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" -[[package]] -name = "windows_i686_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" - [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -6181,12 +6158,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -6199,12 +6170,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnu" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -6217,12 +6182,6 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -6235,17 +6194,11 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" dependencies = [ "memchr", ] @@ -6332,7 +6285,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "synstructure", ] @@ -6353,7 +6306,7 @@ checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6373,7 +6326,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", "synstructure", ] @@ -6394,7 +6347,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6427,7 +6380,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.103", ] [[package]] @@ -6476,9 +6429,9 @@ dependencies = [ [[package]] name = "zlib-rs" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "868b928d7949e09af2f6086dfc1e01936064cc7a819253bce650d4e2a2d63ba8" +checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" [[package]] name = "zopfli" diff --git a/flake.lock b/flake.lock index 41ce99af79..77a8613232 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1748929857, - "narHash": "sha256-lcZQ8RhsmhsK8u7LIFsJhsLh/pzR9yZ8yqpTzyGdj+Q=", + "lastModified": 1749794982, + "narHash": "sha256-Kh9K4taXbVuaLC0IL+9HcfvxsSUx8dPB5s5weJcc9pc=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c2a03962b8e24e669fb37b7df10e7c79531ff1a4", + "rev": "ee930f9755f58096ac6e8ca94a1887e0534e2d81", "type": "github" }, "original": { @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1749004659, - "narHash": "sha256-zaZrcC5UwHPGkgfnhTPx5sZfSSnUJdvYHhgex10RadQ=", + "lastModified": 1750041667, + "narHash": "sha256-/8F9L6T9w/Fx1D6L+BtWIXg5m9F6jwOFg6uhZpKnM/0=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "c52e346aedfa745564599558a096e88f9a5557f9", + "rev": "d72bd8c9fda03c9834ea89d7a5a21c7880b79277", "type": "github" }, "original": { From de279b081383b79497c368c5bc010bab4ecd3728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Mon, 16 Jun 2025 09:32:41 +0200 Subject: [PATCH 02/21] move file to correct module --- .../defguard_core/src/enterprise/{firewall.rs => firewall/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename crates/defguard_core/src/enterprise/{firewall.rs => firewall/mod.rs} (100%) diff --git a/crates/defguard_core/src/enterprise/firewall.rs b/crates/defguard_core/src/enterprise/firewall/mod.rs similarity index 100% rename from crates/defguard_core/src/enterprise/firewall.rs rename to crates/defguard_core/src/enterprise/firewall/mod.rs From 0ae03a9957dbd408ea888abac54ae9e34016ef3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Mon, 16 Jun 2025 11:15:05 +0200 Subject: [PATCH 03/21] allow using IpAddr in models --- crates/model_derive/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/model_derive/src/lib.rs b/crates/model_derive/src/lib.rs index 0602281931..299ba260a4 100644 --- a/crates/model_derive/src/lib.rs +++ b/crates/model_derive/src/lib.rs @@ -123,6 +123,8 @@ pub fn derive(input: TokenStream) -> TokenStream { if field_type == "secret" { // FIXME: don't hard-code struct name cs_aliased_fields.push_str("?: SecretString\""); + } else if field_type == "ip" { + cs_aliased_fields.push_str(": IpAddr\""); } else { cs_aliased_fields.push_str(": _\""); } @@ -153,6 +155,9 @@ pub fn derive(input: TokenStream) -> TokenStream { } else if tokens == "secret" { // FIXME: hard-coded struct name return Some(quote! { &self.#name as &Option }); + } else if tokens == "ip" { + // FIXME: hard-coded struct name + return Some(quote! { &self.#name as &IpAddr }); } else { return Some(quote! { &self.#name }); } From 0e4d2d06e2ba8082a90ffd93e6b3271186ddf4dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Mon, 16 Jun 2025 11:15:56 +0200 Subject: [PATCH 04/21] add table for SNAT bindings --- .../migrations/20250616071627_add_user_snat.down.sql | 1 + .../migrations/20250616071627_add_user_snat.up.sql | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 crates/defguard_core/migrations/20250616071627_add_user_snat.down.sql create mode 100644 crates/defguard_core/migrations/20250616071627_add_user_snat.up.sql diff --git a/crates/defguard_core/migrations/20250616071627_add_user_snat.down.sql b/crates/defguard_core/migrations/20250616071627_add_user_snat.down.sql new file mode 100644 index 0000000000..c98ffc6336 --- /dev/null +++ b/crates/defguard_core/migrations/20250616071627_add_user_snat.down.sql @@ -0,0 +1 @@ +DROP TABLE user_snat_binding; diff --git a/crates/defguard_core/migrations/20250616071627_add_user_snat.up.sql b/crates/defguard_core/migrations/20250616071627_add_user_snat.up.sql new file mode 100644 index 0000000000..8e0974e7d7 --- /dev/null +++ b/crates/defguard_core/migrations/20250616071627_add_user_snat.up.sql @@ -0,0 +1,9 @@ +CREATE TABLE user_snat_binding ( + id bigserial PRIMARY KEY, + user_id bigint NOT NULL, + location_id bigint NOT NULL, + public_ip inet NOT NULL, + FOREIGN KEY(user_id) REFERENCES "user"(id) ON DELETE CASCADE, + FOREIGN KEY(location_id) REFERENCES "wireguard_network"(id) ON DELETE CASCADE, + CONSTRAINT user_location UNIQUE (user_id, location_id) +); From 7a6a18bef2b80733434f1fb7d2a500bf1d288290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Mon, 16 Jun 2025 13:14:58 +0200 Subject: [PATCH 05/21] add bruno API collection --- .../List all bindings for location.bru | 19 +++++++++++++++++++ .../User SNAT Bindings/folder.bru | 8 ++++++++ bruno/defguard_api/bruno.json | 9 +++++++++ bruno/defguard_api/environments/local.bru | 7 +++++++ 4 files changed, 43 insertions(+) create mode 100644 bruno/defguard_api/User SNAT Bindings/List all bindings for location.bru create mode 100644 bruno/defguard_api/User SNAT Bindings/folder.bru create mode 100644 bruno/defguard_api/bruno.json create mode 100644 bruno/defguard_api/environments/local.bru diff --git a/bruno/defguard_api/User SNAT Bindings/List all bindings for location.bru b/bruno/defguard_api/User SNAT Bindings/List all bindings for location.bru new file mode 100644 index 0000000000..f7b2f9ba29 --- /dev/null +++ b/bruno/defguard_api/User SNAT Bindings/List all bindings for location.bru @@ -0,0 +1,19 @@ +meta { + name: List all bindings for location + type: http + seq: 1 +} + +get { + url: {{base_url}}/{{api_prefix}}/network/:network_id/snat + body: none + auth: inherit +} + +params:path { + network_id: 1 +} + +headers { + Authorization: Bearer {{api_token}} +} diff --git a/bruno/defguard_api/User SNAT Bindings/folder.bru b/bruno/defguard_api/User SNAT Bindings/folder.bru new file mode 100644 index 0000000000..42940d6205 --- /dev/null +++ b/bruno/defguard_api/User SNAT Bindings/folder.bru @@ -0,0 +1,8 @@ +meta { + name: User SNAT Bindings + seq: 1 +} + +auth { + mode: inherit +} diff --git a/bruno/defguard_api/bruno.json b/bruno/defguard_api/bruno.json new file mode 100644 index 0000000000..40a66fbe2f --- /dev/null +++ b/bruno/defguard_api/bruno.json @@ -0,0 +1,9 @@ +{ + "version": "1", + "name": "defguard_api", + "type": "collection", + "ignore": [ + "node_modules", + ".git" + ] +} \ No newline at end of file diff --git a/bruno/defguard_api/environments/local.bru b/bruno/defguard_api/environments/local.bru new file mode 100644 index 0000000000..fa6745f644 --- /dev/null +++ b/bruno/defguard_api/environments/local.bru @@ -0,0 +1,7 @@ +vars { + base_url: http://localhost:8000 + api_prefix: api/v1 +} +vars:secret [ + api_token +] From 579dbaedbce0eff53237c9675daa19c7fa746b59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Mon, 16 Jun 2025 13:15:17 +0200 Subject: [PATCH 06/21] set up basic list endpoint --- crates/defguard_core/src/enterprise/mod.rs | 1 + .../src/enterprise/snat/error.rs | 4 ++ .../src/enterprise/snat/handlers.rs | 34 ++++++++++++++ .../defguard_core/src/enterprise/snat/mod.rs | 18 ++++++++ crates/defguard_core/src/lib.rs | 45 ++++++++++++------- 5 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 crates/defguard_core/src/enterprise/snat/error.rs create mode 100644 crates/defguard_core/src/enterprise/snat/handlers.rs create mode 100644 crates/defguard_core/src/enterprise/snat/mod.rs diff --git a/crates/defguard_core/src/enterprise/mod.rs b/crates/defguard_core/src/enterprise/mod.rs index dc3e9660f5..54634b8fe1 100644 --- a/crates/defguard_core/src/enterprise/mod.rs +++ b/crates/defguard_core/src/enterprise/mod.rs @@ -7,6 +7,7 @@ pub mod handlers; pub mod ldap; pub mod license; pub mod limits; +pub mod snat; mod utils; use license::{get_cached_license, validate_license}; diff --git a/crates/defguard_core/src/enterprise/snat/error.rs b/crates/defguard_core/src/enterprise/snat/error.rs new file mode 100644 index 0000000000..743376ac86 --- /dev/null +++ b/crates/defguard_core/src/enterprise/snat/error.rs @@ -0,0 +1,4 @@ +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum UserSnatBindingError {} diff --git a/crates/defguard_core/src/enterprise/snat/handlers.rs b/crates/defguard_core/src/enterprise/snat/handlers.rs new file mode 100644 index 0000000000..e891302ab6 --- /dev/null +++ b/crates/defguard_core/src/enterprise/snat/handlers.rs @@ -0,0 +1,34 @@ +use axum::extract::{Path, State}; +use reqwest::StatusCode; +use serde_json::json; +use sqlx::query_as; +use std::net::IpAddr; + +use crate::{ + appstate::AppState, + auth::AdminRole, + db::Id, + enterprise::snat::UserSnatBinding, + handlers::{ApiResponse, ApiResult}, +}; + +pub async fn list_snat_bindings( + _role: AdminRole, + Path(location_id): Path, + State(appstate): State, +) -> ApiResult { + debug!("Listing SNAT bindings for WireGuard location {location_id}"); + + let bindings = query_as!( + UserSnatBinding::, + "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1", + location_id + ) + .fetch_all(&appstate.pool) + .await?; + + Ok(ApiResponse { + json: json!(bindings), + status: StatusCode::OK, + }) +} diff --git a/crates/defguard_core/src/enterprise/snat/mod.rs b/crates/defguard_core/src/enterprise/snat/mod.rs new file mode 100644 index 0000000000..ebef2cb64f --- /dev/null +++ b/crates/defguard_core/src/enterprise/snat/mod.rs @@ -0,0 +1,18 @@ +use std::net::IpAddr; + +use crate::db::{Id, NoId}; +use model_derive::Model; +use serde::Serialize; + +pub mod error; +pub mod handlers; + +#[derive(Debug, Model, Serialize)] +#[table(user_snat_binding)] +pub struct UserSnatBinding { + pub id: I, + pub user_id: Id, + pub location_id: Id, + #[model(ip)] + pub public_ip: IpAddr, +} diff --git a/crates/defguard_core/src/lib.rs b/crates/defguard_core/src/lib.rs index c528b2b100..bf4613c461 100644 --- a/crates/defguard_core/src/lib.rs +++ b/crates/defguard_core/src/lib.rs @@ -14,23 +14,26 @@ use axum::{ }; use db::models::device::DeviceType; use defguard_web_ui::{index, svg, web_asset}; -use enterprise::handlers::{ - acl::{ - apply_acl_aliases, apply_acl_rules, create_acl_alias, create_acl_rule, delete_acl_alias, - delete_acl_rule, get_acl_alias, get_acl_rule, list_acl_aliases, list_acl_rules, - update_acl_alias, update_acl_rule, - }, - api_tokens::{add_api_token, delete_api_token, fetch_api_tokens, rename_api_token}, - audit_stream::{ - create_audit_stream, delete_audit_stream, get_audit_stream, modify_audit_stream, - }, - check_enterprise_info, - enterprise_settings::{get_enterprise_settings, patch_enterprise_settings}, - openid_login::{auth_callback, get_auth_info}, - openid_providers::{ - add_openid_provider, delete_openid_provider, get_current_openid_provider, - test_dirsync_connection, +use enterprise::{ + handlers::{ + acl::{ + apply_acl_aliases, apply_acl_rules, create_acl_alias, create_acl_rule, + delete_acl_alias, delete_acl_rule, get_acl_alias, get_acl_rule, list_acl_aliases, + list_acl_rules, update_acl_alias, update_acl_rule, + }, + api_tokens::{add_api_token, delete_api_token, fetch_api_tokens, rename_api_token}, + audit_stream::{ + create_audit_stream, delete_audit_stream, get_audit_stream, modify_audit_stream, + }, + check_enterprise_info, + enterprise_settings::{get_enterprise_settings, patch_enterprise_settings}, + openid_login::{auth_callback, get_auth_info}, + openid_providers::{ + add_openid_provider, delete_openid_provider, get_current_openid_provider, + test_dirsync_connection, + }, }, + snat::handlers::list_snat_bindings, }; use events::ApiEvent; use handlers::{ @@ -576,6 +579,16 @@ pub fn build_webapp( .route("/network/{network_id}/token", get(create_network_token)) .route("/network/{network_id}/stats/users", get(devices_stats)) .route("/network/{network_id}/stats", get(network_stats)) + .route("/network/{location_id}/snat", get(list_snat_bindings)) + // .route("/network/{location_id}/snat", post(create_snat_binding)) + // .route( + // "/network/{location_id}/snat/{binding_id}", + // put(modify_snat_binding), + // ) + // .route( + // "/network/{location_id}/snat/{binding_id}", + // delete(delete_snat_binding), + // ) .layer(Extension(gateway_state)), ); From 3c6cd53a796c5dd2fd5a4da3c16f22ff0c26b02e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Mon, 16 Jun 2025 15:27:34 +0200 Subject: [PATCH 07/21] add basic binding management API --- .../src/enterprise/snat/error.rs | 27 +++++- .../src/enterprise/snat/handlers.rs | 88 ++++++++++++++++++- .../defguard_core/src/enterprise/snat/mod.rs | 35 ++++++++ crates/defguard_core/src/lib.rs | 22 ++--- 4 files changed, 158 insertions(+), 14 deletions(-) diff --git a/crates/defguard_core/src/enterprise/snat/error.rs b/crates/defguard_core/src/enterprise/snat/error.rs index 743376ac86..6944a411e4 100644 --- a/crates/defguard_core/src/enterprise/snat/error.rs +++ b/crates/defguard_core/src/enterprise/snat/error.rs @@ -1,4 +1,29 @@ use thiserror::Error; +use crate::error::WebError; + #[derive(Debug, Error)] -pub enum UserSnatBindingError {} +pub enum UserSnatBindingError { + #[error("Binding not found")] + BindingNotFound, + #[error("Database error")] + DbError { source: sqlx::Error }, +} + +impl From for UserSnatBindingError { + fn from(value: sqlx::Error) -> Self { + match value { + sqlx::Error::RowNotFound => Self::BindingNotFound, + _ => Self::DbError { source: value }, + } + } +} + +impl From for WebError { + fn from(value: UserSnatBindingError) -> Self { + match value { + UserSnatBindingError::BindingNotFound => WebError::ObjectNotFound(value.to_string()), + UserSnatBindingError::DbError { source } => WebError::DbError(source.to_string()), + } + } +} diff --git a/crates/defguard_core/src/enterprise/snat/handlers.rs b/crates/defguard_core/src/enterprise/snat/handlers.rs index e891302ab6..a0ae5c1eb7 100644 --- a/crates/defguard_core/src/enterprise/snat/handlers.rs +++ b/crates/defguard_core/src/enterprise/snat/handlers.rs @@ -1,4 +1,7 @@ -use axum::extract::{Path, State}; +use axum::{ + extract::{Path, State}, + Json, +}; use reqwest::StatusCode; use serde_json::json; use sqlx::query_as; @@ -6,7 +9,7 @@ use std::net::IpAddr; use crate::{ appstate::AppState, - auth::AdminRole, + auth::{AdminRole, SessionInfo}, db::Id, enterprise::snat::UserSnatBinding, handlers::{ApiResponse, ApiResult}, @@ -14,10 +17,13 @@ use crate::{ pub async fn list_snat_bindings( _role: AdminRole, + session: SessionInfo, Path(location_id): Path, State(appstate): State, ) -> ApiResult { - debug!("Listing SNAT bindings for WireGuard location {location_id}"); + let current_user = session.user.username; + + debug!("User {current_user} listing SNAT bindings for WireGuard location {location_id}"); let bindings = query_as!( UserSnatBinding::, @@ -32,3 +38,79 @@ pub async fn list_snat_bindings( status: StatusCode::OK, }) } + +#[derive(Debug, Deserialize)] +pub struct NewUserSnatBinding { + user_id: Id, + public_ip: IpAddr, +} + +pub async fn create_snat_binding( + _role: AdminRole, + session: SessionInfo, + Path(location_id): Path, + State(appstate): State, + Json(data): Json, +) -> ApiResult { + let current_user = session.user.username; + + debug!("User {current_user} creating new SNAT binding for WireGuard location {location_id} with {data:?}"); + + let snat_binding = UserSnatBinding::new(data.user_id, location_id, data.public_ip); + + let binding = snat_binding.save(&appstate.pool).await?; + + Ok(ApiResponse { + json: json!(binding), + status: StatusCode::CREATED, + }) +} + +#[derive(Debug, Deserialize)] +pub struct EditUserSnatBinding { + public_ip: IpAddr, +} + +pub async fn modify_snat_binding( + _role: AdminRole, + session: SessionInfo, + Path((location_id, user_id)): Path<(Id, Id)>, + State(appstate): State, + Json(data): Json, +) -> ApiResult { + let current_user = session.user.username; + + debug!("User {current_user} updating SNAT binding for user {user_id} and WireGuard location {location_id} with {data:?}"); + + // fetch existing binding + let mut snat_binding = + UserSnatBinding::find_binding(&appstate.pool, location_id, user_id).await?; + + // update public IP + snat_binding.update_ip(data.public_ip); + snat_binding.save(&appstate.pool).await?; + + Ok(ApiResponse { + json: json!(snat_binding), + status: StatusCode::OK, + }) +} + +pub async fn delete_snat_binding( + _role: AdminRole, + session: SessionInfo, + Path((location_id, user_id)): Path<(Id, Id)>, + State(appstate): State, +) -> ApiResult { + let current_user = session.user.username; + + debug!("User {current_user} deleting SNAT binding for user {user_id} and WireGuard location {location_id}"); + + // fetch existing binding + let snat_binding = UserSnatBinding::find_binding(&appstate.pool, location_id, user_id).await?; + + // delete binding + snat_binding.delete(&appstate.pool).await?; + + Ok(ApiResponse::default()) +} diff --git a/crates/defguard_core/src/enterprise/snat/mod.rs b/crates/defguard_core/src/enterprise/snat/mod.rs index ebef2cb64f..0b6b784509 100644 --- a/crates/defguard_core/src/enterprise/snat/mod.rs +++ b/crates/defguard_core/src/enterprise/snat/mod.rs @@ -1,8 +1,10 @@ use std::net::IpAddr; use crate::db::{Id, NoId}; +use error::UserSnatBindingError; use model_derive::Model; use serde::Serialize; +use sqlx::{query_as, PgExecutor}; pub mod error; pub mod handlers; @@ -16,3 +18,36 @@ pub struct UserSnatBinding { #[model(ip)] pub public_ip: IpAddr, } + +impl UserSnatBinding { + pub fn new(user_id: Id, location_id: Id, public_ip: IpAddr) -> Self { + Self { + id: NoId, + user_id, + location_id, + public_ip, + } + } +} + +impl UserSnatBinding { + pub async fn find_binding<'e, E>( + executor: E, + location_id: Id, + user_id: Id, + ) -> Result + where + E: PgExecutor<'e>, + { + let binding = query_as!(Self, + "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1 AND user_id = $2", + location_id, user_id + ).fetch_one(executor).await?; + + Ok(binding) + } + + pub fn update_ip(&mut self, new_public_ip: IpAddr) { + self.public_ip = new_public_ip; + } +} diff --git a/crates/defguard_core/src/lib.rs b/crates/defguard_core/src/lib.rs index bf4613c461..6085996c16 100644 --- a/crates/defguard_core/src/lib.rs +++ b/crates/defguard_core/src/lib.rs @@ -33,7 +33,9 @@ use enterprise::{ test_dirsync_connection, }, }, - snat::handlers::list_snat_bindings, + snat::handlers::{ + create_snat_binding, delete_snat_binding, list_snat_bindings, modify_snat_binding, + }, }; use events::ApiEvent; use handlers::{ @@ -580,15 +582,15 @@ pub fn build_webapp( .route("/network/{network_id}/stats/users", get(devices_stats)) .route("/network/{network_id}/stats", get(network_stats)) .route("/network/{location_id}/snat", get(list_snat_bindings)) - // .route("/network/{location_id}/snat", post(create_snat_binding)) - // .route( - // "/network/{location_id}/snat/{binding_id}", - // put(modify_snat_binding), - // ) - // .route( - // "/network/{location_id}/snat/{binding_id}", - // delete(delete_snat_binding), - // ) + .route("/network/{location_id}/snat", post(create_snat_binding)) + .route( + "/network/{location_id}/snat/{user_id}", + put(modify_snat_binding), + ) + .route( + "/network/{location_id}/snat/{user_id}", + delete(delete_snat_binding), + ) .layer(Extension(gateway_state)), ); From 80ad39b7b433729926bc28bb2d16cc0e638280e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Mon, 16 Jun 2025 15:29:02 +0200 Subject: [PATCH 08/21] delete API docs --- .../List all bindings for location.bru | 19 ------------------- .../User SNAT Bindings/folder.bru | 8 -------- bruno/defguard_api/bruno.json | 9 --------- bruno/defguard_api/environments/local.bru | 7 ------- 4 files changed, 43 deletions(-) delete mode 100644 bruno/defguard_api/User SNAT Bindings/List all bindings for location.bru delete mode 100644 bruno/defguard_api/User SNAT Bindings/folder.bru delete mode 100644 bruno/defguard_api/bruno.json delete mode 100644 bruno/defguard_api/environments/local.bru diff --git a/bruno/defguard_api/User SNAT Bindings/List all bindings for location.bru b/bruno/defguard_api/User SNAT Bindings/List all bindings for location.bru deleted file mode 100644 index f7b2f9ba29..0000000000 --- a/bruno/defguard_api/User SNAT Bindings/List all bindings for location.bru +++ /dev/null @@ -1,19 +0,0 @@ -meta { - name: List all bindings for location - type: http - seq: 1 -} - -get { - url: {{base_url}}/{{api_prefix}}/network/:network_id/snat - body: none - auth: inherit -} - -params:path { - network_id: 1 -} - -headers { - Authorization: Bearer {{api_token}} -} diff --git a/bruno/defguard_api/User SNAT Bindings/folder.bru b/bruno/defguard_api/User SNAT Bindings/folder.bru deleted file mode 100644 index 42940d6205..0000000000 --- a/bruno/defguard_api/User SNAT Bindings/folder.bru +++ /dev/null @@ -1,8 +0,0 @@ -meta { - name: User SNAT Bindings - seq: 1 -} - -auth { - mode: inherit -} diff --git a/bruno/defguard_api/bruno.json b/bruno/defguard_api/bruno.json deleted file mode 100644 index 40a66fbe2f..0000000000 --- a/bruno/defguard_api/bruno.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "version": "1", - "name": "defguard_api", - "type": "collection", - "ignore": [ - "node_modules", - ".git" - ] -} \ No newline at end of file diff --git a/bruno/defguard_api/environments/local.bru b/bruno/defguard_api/environments/local.bru deleted file mode 100644 index fa6745f644..0000000000 --- a/bruno/defguard_api/environments/local.bru +++ /dev/null @@ -1,7 +0,0 @@ -vars { - base_url: http://localhost:8000 - api_prefix: api/v1 -} -vars:secret [ - api_token -] From 5295b94ed304772cb09693b737f5cfaa60ccc9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Tue, 17 Jun 2025 09:45:11 +0200 Subject: [PATCH 09/21] add API docs --- .../src/enterprise/snat/error.rs | 7 ++ .../src/enterprise/snat/handlers.rs | 96 ++++++++++++++++++- .../defguard_core/src/enterprise/snat/mod.rs | 4 +- crates/defguard_core/src/lib.rs | 12 ++- 4 files changed, 115 insertions(+), 4 deletions(-) diff --git a/crates/defguard_core/src/enterprise/snat/error.rs b/crates/defguard_core/src/enterprise/snat/error.rs index 6944a411e4..83ee044ab5 100644 --- a/crates/defguard_core/src/enterprise/snat/error.rs +++ b/crates/defguard_core/src/enterprise/snat/error.rs @@ -1,3 +1,4 @@ +use reqwest::StatusCode; use thiserror::Error; use crate::error::WebError; @@ -6,6 +7,8 @@ use crate::error::WebError; pub enum UserSnatBindingError { #[error("Binding not found")] BindingNotFound, + #[error("Binding already exists")] + BindingAlreadyExists, #[error("Database error")] DbError { source: sqlx::Error }, } @@ -14,6 +17,9 @@ impl From for UserSnatBindingError { fn from(value: sqlx::Error) -> Self { match value { sqlx::Error::RowNotFound => Self::BindingNotFound, + sqlx::Error::Database(err) if err.constraint() == Some("user_location") => { + Self::BindingAlreadyExists + } _ => Self::DbError { source: value }, } } @@ -23,6 +29,7 @@ impl From for WebError { fn from(value: UserSnatBindingError) -> Self { match value { UserSnatBindingError::BindingNotFound => WebError::ObjectNotFound(value.to_string()), + UserSnatBindingError::BindingAlreadyExists => WebError::Http(StatusCode::CONFLICT), UserSnatBindingError::DbError { source } => WebError::DbError(source.to_string()), } } diff --git a/crates/defguard_core/src/enterprise/snat/handlers.rs b/crates/defguard_core/src/enterprise/snat/handlers.rs index a0ae5c1eb7..cf8dfa358b 100644 --- a/crates/defguard_core/src/enterprise/snat/handlers.rs +++ b/crates/defguard_core/src/enterprise/snat/handlers.rs @@ -3,9 +3,11 @@ use axum::{ Json, }; use reqwest::StatusCode; +use serde::{Deserialize, Serialize}; use serde_json::json; use sqlx::query_as; use std::net::IpAddr; +use utoipa::ToSchema; use crate::{ appstate::AppState, @@ -15,6 +17,25 @@ use crate::{ handlers::{ApiResponse, ApiResult}, }; +/// List all SNAT bindings for a WireGuard location +#[utoipa::path( + get, + path = "/api/v1/network/{location_id}/snat", + tag = "SNAT", + params( + ("location_id" = Id, Path, description = "WireGuard location ID") + ), + responses( + (status = 200, description = "List of SNAT bindings", body = Vec), + (status = 401, description = "Unauthorized"), + (status = 403, description = "Forbidden - Admin role required"), + (status = 500, description = "Internal server error") + ), + security( + ("cookie" = []), + ("api_token" = []) + ) +)] pub async fn list_snat_bindings( _role: AdminRole, session: SessionInfo, @@ -39,12 +60,37 @@ pub async fn list_snat_bindings( }) } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize, ToSchema)] pub struct NewUserSnatBinding { + /// User ID to bind to the public IP user_id: Id, + /// Public IP address for SNAT + #[schema(value_type = String)] public_ip: IpAddr, } +/// Create a new SNAT binding for a user in a WireGuard location +#[utoipa::path( + post, + path = "/api/v1/network/{location_id}/snat", + tag = "SNAT", + params( + ("location_id" = Id, Path, description = "WireGuard location ID") + ), + request_body = NewUserSnatBinding, + responses( + (status = 201, description = "SNAT binding created successfully", body = UserSnatBinding), + (status = 400, description = "Bad request - Invalid input data"), + (status = 401, description = "Unauthorized"), + (status = 403, description = "Forbidden - Admin role required"), + (status = 409, description = "Conflict - Binding already exists"), + (status = 500, description = "Internal server error") + ), + security( + ("cookie" = []), + ("api_token" = []) + ) +)] pub async fn create_snat_binding( _role: AdminRole, session: SessionInfo, @@ -66,11 +112,36 @@ pub async fn create_snat_binding( }) } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Serialize, ToSchema)] pub struct EditUserSnatBinding { + /// New public IP address for SNAT + #[schema(value_type = String)] public_ip: IpAddr, } +/// Modify an existing SNAT binding for a user in a WireGuard location +#[utoipa::path( + put, + path = "/api/v1/network/{location_id}/snat/{user_id}", + tag = "SNAT", + params( + ("location_id" = Id, Path, description = "WireGuard location ID"), + ("user_id" = Id, Path, description = "User ID") + ), + request_body = EditUserSnatBinding, + responses( + (status = 200, description = "SNAT binding updated successfully", body = UserSnatBinding), + (status = 400, description = "Bad request - Invalid input data"), + (status = 401, description = "Unauthorized"), + (status = 403, description = "Forbidden - Admin role required"), + (status = 404, description = "Not found - SNAT binding does not exist"), + (status = 500, description = "Internal server error") + ), + security( + ("cookie" = []), + ("api_token" = []) + ) +)] pub async fn modify_snat_binding( _role: AdminRole, session: SessionInfo, @@ -96,6 +167,27 @@ pub async fn modify_snat_binding( }) } +/// Delete an existing SNAT binding for a user in a WireGuard location +#[utoipa::path( + delete, + path = "/api/v1/network/{location_id}/snat/{user_id}", + tag = "SNAT", + params( + ("location_id" = Id, Path, description = "WireGuard location ID"), + ("user_id" = Id, Path, description = "User ID") + ), + responses( + (status = 200, description = "SNAT binding deleted successfully"), + (status = 401, description = "Unauthorized"), + (status = 403, description = "Forbidden - Admin role required"), + (status = 404, description = "Not found - SNAT binding does not exist"), + (status = 500, description = "Internal server error") + ), + security( + ("cookie" = []), + ("api_token" = []) + ) +)] pub async fn delete_snat_binding( _role: AdminRole, session: SessionInfo, diff --git a/crates/defguard_core/src/enterprise/snat/mod.rs b/crates/defguard_core/src/enterprise/snat/mod.rs index 0b6b784509..e6ffc7825b 100644 --- a/crates/defguard_core/src/enterprise/snat/mod.rs +++ b/crates/defguard_core/src/enterprise/snat/mod.rs @@ -5,17 +5,19 @@ use error::UserSnatBindingError; use model_derive::Model; use serde::Serialize; use sqlx::{query_as, PgExecutor}; +use utoipa::ToSchema; pub mod error; pub mod handlers; -#[derive(Debug, Model, Serialize)] +#[derive(Debug, Model, Serialize, ToSchema)] #[table(user_snat_binding)] pub struct UserSnatBinding { pub id: I, pub user_id: Id, pub location_id: Id, #[model(ip)] + #[schema(value_type = String)] pub public_ip: IpAddr, } diff --git a/crates/defguard_core/src/lib.rs b/crates/defguard_core/src/lib.rs index 6085996c16..9273effe08 100644 --- a/crates/defguard_core/src/lib.rs +++ b/crates/defguard_core/src/lib.rs @@ -184,6 +184,7 @@ pub(crate) fn server_config() -> &'static DefGuardConfig { pub(crate) const KEY_LENGTH: usize = 32; mod openapi { + use crate::enterprise::snat::handlers as snat; use db::{ models::device::{ModifyDevice, UserDevice}, AddDevice, UserDetails, UserInfo, @@ -244,6 +245,12 @@ mod openapi { network::delete_network, network::list_networks, network::network_details, + // /network/{location_id}/snat + snat::list_snat_bindings, + snat::create_snat_binding, + snat::modify_snat_binding, + snat::delete_snat_binding, + ), components( schemas( @@ -275,13 +282,16 @@ Available actions: - list all devices or user devices - CRUD mechanism for handling devices. "), - (name = "nework", description = " + (name = "network", description = " Endpoints that allow to control your networks. Available actions: - list all wireguard networks - CRUD mechanism for handling devices. "), + (name = "SNAT", description = " +Endpoints that allow you to control user SNAT bindings for your locations. + "), ) )] pub struct ApiDoc; From d8d171bec6b58532bca6e99ac194b1e1de6d6153 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Tue, 17 Jun 2025 09:59:13 +0200 Subject: [PATCH 10/21] update protos --- proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto b/proto index 20fe30dfa1..f0c030a972 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit 20fe30dfa1c2985bb7a6afe1c74dd9a709e034c6 +Subproject commit f0c030a9725a0fd055efa69b58e0d7ee42654583 From 348caa8b17aa744a1ae7d620d6e83c592cf4cb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Tue, 17 Jun 2025 10:42:54 +0200 Subject: [PATCH 11/21] send snat bindings to the gateway --- .../src/enterprise/firewall/mod.rs | 94 ++++++++++++++++++- .../defguard_core/src/enterprise/snat/mod.rs | 19 +++- 2 files changed, 106 insertions(+), 7 deletions(-) diff --git a/crates/defguard_core/src/enterprise/firewall/mod.rs b/crates/defguard_core/src/enterprise/firewall/mod.rs index 570400ef9e..10433c633f 100644 --- a/crates/defguard_core/src/enterprise/firewall/mod.rs +++ b/crates/defguard_core/src/enterprise/firewall/mod.rs @@ -11,6 +11,7 @@ use super::{ AclAliasDestinationRange, AclRule, AclRuleDestinationRange, AclRuleInfo, PortRange, Protocol, }, + snat::UserSnatBinding, utils::merge_ranges, }; use crate::{ @@ -19,6 +20,7 @@ use crate::{ grpc::proto::enterprise::firewall::{ ip_address::Address, port::Port as PortInner, FirewallConfig, FirewallPolicy, FirewallRule, IpAddress, IpRange, IpVersion, Port, PortRange as PortRangeProto, + SnatBinding as SnatBindingProto, }, }; @@ -64,9 +66,11 @@ pub async fn generate_firewall_rules_from_acls( // get relevant users for determining source IPs let users = get_source_users(allowed_users, &denied_users); + // prepare a list of user IDs + let user_ids: Vec = users.iter().map(|user| user.id).collect(); // get network IPs for devices belonging to those users - let user_device_ips = get_user_device_ips(&users, location_id, &mut *conn).await?; + let user_device_ips = get_user_device_ips(&user_ids, location_id, &mut *conn).await?; // separate IPv4 and IPv6 user-device addresses let user_device_ips = user_device_ips .iter() @@ -321,13 +325,10 @@ fn get_source_users(allowed_users: Vec>, denied_users: &[User]) -> /// Fetches all IPs of devices belonging to specified users within a given location's VPN subnet. /// We specifically only fetch user devices since network devices are handled separately. async fn get_user_device_ips<'e, E: sqlx::PgExecutor<'e>>( - users: &[User], + user_ids: &[Id], location_id: Id, executor: E, ) -> Result>, SqlxError> { - // prepare a list of user IDs - let user_ids: Vec = users.iter().map(|user| user.id).collect(); - // fetch network IPs query_scalar!( "SELECT wireguard_ips \"wireguard_ips: Vec\" \ @@ -610,6 +611,87 @@ fn merge_port_ranges(port_ranges: Vec) -> Vec { .collect() } +/// Converts user SNAT bindings into SNAT config to be sent to a gateway as part of `FirewallConfig`. +/// +/// To generate the final SNAT binding we need to find all user devices +/// and get their IPs to generate a list of source addresses for a firewall rule. +async fn generate_user_snat_bindings_for_location( + location_id: Id, + conn: &mut PgConnection, +) -> Result, SqlxError> { + debug!("Generating SNAT bindings for location {location_id}"); + + let user_snat_bindings = UserSnatBinding::all_for_location(&mut *conn, location_id).await?; + + // check if there are any bindings configured for this location + if user_snat_bindings.is_empty() { + debug!("No user SNAT bindings configured for location {location_id}"); + return Ok(Vec::new()); + } + + // iniitalize output list + let mut bindings = Vec::new(); + + // process each user SNAT binding + for user_binding in user_snat_bindings { + let user_id = user_binding.user_id; + + debug!( + "Processing SNAT binding for user {user_id} with public IP {}", + user_binding.public_ip + ); + + // determine IP protocol version based on public IP + let is_ipv4 = user_binding.public_ip.is_ipv4(); + + // fetch all device IPs for this specific user in the location + let user_device_ips = get_user_device_ips(&[user_id], location_id, &mut *conn).await?; + + // separate IPv4 and IPv6 user-device addresses + let (user_device_ips_v4, user_device_ips_v6) = user_device_ips + .iter() + .flatten() + .partition(|ip| ip.is_ipv4()); + + // convert device IPs into source addresses for a firewall rule + let source_addrs = if is_ipv4 { + get_source_addrs(user_device_ips_v4, Vec::new(), IpVersion::Ipv4) + } else { + get_source_addrs(user_device_ips_v6, Vec::new(), IpVersion::Ipv6) + }; + + if source_addrs.is_empty() { + debug!( + "No compatible device IPs found for user {user_id} in location {location_id} with public IP {}, skipping SNAT binding", user_binding.public_ip + ); + continue; + } + + // create the SNAT binding proto + let snat_binding = SnatBindingProto { + id: user_binding.id, + source_addrs, + public_ip: user_binding.public_ip.to_string(), + comment: Some(format!("User {user_id} SNAT binding {}", user_binding.id)), + }; + + debug!( + "Created SNAT binding for user {user_id} in location {location_id}: {snat_binding:?}", + ); + + // add to output list + bindings.push(snat_binding); + } + + debug!( + "Generated {} SNAT bindings for location {}", + bindings.len(), + location_id + ); + + Ok(bindings) +} + impl WireguardNetwork { /// Fetches all active ACL rules for a given location. /// Filters out rules which are disabled, expired or have not been deployed yet. @@ -677,9 +759,11 @@ impl WireguardNetwork { }; let firewall_rules = generate_firewall_rules_from_acls(self.id, location_acls, &mut *conn).await?; + let snat_bindings = generate_user_snat_bindings_for_location(self.id, &mut *conn).await?; let firewall_config = FirewallConfig { default_policy: default_policy.into(), rules: firewall_rules, + snat_bindings, }; debug!("Firewall config generated for location {self}: {firewall_config:?}"); diff --git a/crates/defguard_core/src/enterprise/snat/mod.rs b/crates/defguard_core/src/enterprise/snat/mod.rs index e6ffc7825b..38b20042ef 100644 --- a/crates/defguard_core/src/enterprise/snat/mod.rs +++ b/crates/defguard_core/src/enterprise/snat/mod.rs @@ -42,13 +42,28 @@ impl UserSnatBinding { E: PgExecutor<'e>, { let binding = query_as!(Self, - "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1 AND user_id = $2", - location_id, user_id + "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1 AND user_id = $2", + location_id, user_id ).fetch_one(executor).await?; Ok(binding) } + pub async fn all_for_location<'e, E>( + executor: E, + location_id: Id, + ) -> Result, sqlx::Error> + where + E: PgExecutor<'e>, + { + let bindings = query_as!(Self, + "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1", + location_id + ).fetch_all(executor).await?; + + Ok(bindings) + } + pub fn update_ip(&mut self, new_public_ip: IpAddr) { self.public_ip = new_public_ip; } From a06e17c998142c6e585a0c3ffff74706a4e37e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Wed, 18 Jun 2025 08:24:18 +0200 Subject: [PATCH 12/21] update query data --- ...24d6ab73bee88929ad223c46bb9b2892150f3.json | 40 ++++++++++++++++++ ...462deb43863778ea17a47696912f412714741.json | 40 ++++++++++++++++++ ...2d7dd30dac5aa72e015c19931c84010ebfcf5.json | 17 ++++++++ ...801b381b112d54971c86beaad5fd679fbd5ac.json | 41 +++++++++++++++++++ ...ce4ea9b463d7e71533e9c6312939a3d77b49e.json | 38 +++++++++++++++++ ...e87000f305499668455d7e4d949f7d0a7be9a.json | 14 +++++++ ...5529ffe33c7290a63adfbaaa416efeffefac3.json | 24 +++++++++++ 7 files changed, 214 insertions(+) create mode 100644 .sqlx/query-0c18e9d0f192e36ed65569c0ef124d6ab73bee88929ad223c46bb9b2892150f3.json create mode 100644 .sqlx/query-0effde2f87ca6a7d9ce34daedb6462deb43863778ea17a47696912f412714741.json create mode 100644 .sqlx/query-6175d2d008ccf7860ebe9f1b2e12d7dd30dac5aa72e015c19931c84010ebfcf5.json create mode 100644 .sqlx/query-876e1659850a050155f3938231e801b381b112d54971c86beaad5fd679fbd5ac.json create mode 100644 .sqlx/query-87868c21e47dd3b55ba1aefb690ce4ea9b463d7e71533e9c6312939a3d77b49e.json create mode 100644 .sqlx/query-8d8416a6cc1f0bae02e126ca398e87000f305499668455d7e4d949f7d0a7be9a.json create mode 100644 .sqlx/query-cda76abbdfed425c3b06c5b64115529ffe33c7290a63adfbaaa416efeffefac3.json diff --git a/.sqlx/query-0c18e9d0f192e36ed65569c0ef124d6ab73bee88929ad223c46bb9b2892150f3.json b/.sqlx/query-0c18e9d0f192e36ed65569c0ef124d6ab73bee88929ad223c46bb9b2892150f3.json new file mode 100644 index 0000000000..f0693f9a64 --- /dev/null +++ b/.sqlx/query-0c18e9d0f192e36ed65569c0ef124d6ab73bee88929ad223c46bb9b2892150f3.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "location_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "public_ip: IpAddr", + "type_info": "Inet" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "0c18e9d0f192e36ed65569c0ef124d6ab73bee88929ad223c46bb9b2892150f3" +} diff --git a/.sqlx/query-0effde2f87ca6a7d9ce34daedb6462deb43863778ea17a47696912f412714741.json b/.sqlx/query-0effde2f87ca6a7d9ce34daedb6462deb43863778ea17a47696912f412714741.json new file mode 100644 index 0000000000..f9349763ad --- /dev/null +++ b/.sqlx/query-0effde2f87ca6a7d9ce34daedb6462deb43863778ea17a47696912f412714741.json @@ -0,0 +1,40 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"user_id\",\"location_id\",\"public_ip\" \"public_ip: IpAddr\" FROM \"user_snat_binding\" WHERE id = $1", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "location_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "public_ip: IpAddr", + "type_info": "Inet" + } + ], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "0effde2f87ca6a7d9ce34daedb6462deb43863778ea17a47696912f412714741" +} diff --git a/.sqlx/query-6175d2d008ccf7860ebe9f1b2e12d7dd30dac5aa72e015c19931c84010ebfcf5.json b/.sqlx/query-6175d2d008ccf7860ebe9f1b2e12d7dd30dac5aa72e015c19931c84010ebfcf5.json new file mode 100644 index 0000000000..c95aa77bf7 --- /dev/null +++ b/.sqlx/query-6175d2d008ccf7860ebe9f1b2e12d7dd30dac5aa72e015c19931c84010ebfcf5.json @@ -0,0 +1,17 @@ +{ + "db_name": "PostgreSQL", + "query": "UPDATE \"user_snat_binding\" SET \"user_id\" = $2,\"location_id\" = $3,\"public_ip\" = $4 WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8", + "Int8", + "Int8", + "Inet" + ] + }, + "nullable": [] + }, + "hash": "6175d2d008ccf7860ebe9f1b2e12d7dd30dac5aa72e015c19931c84010ebfcf5" +} diff --git a/.sqlx/query-876e1659850a050155f3938231e801b381b112d54971c86beaad5fd679fbd5ac.json b/.sqlx/query-876e1659850a050155f3938231e801b381b112d54971c86beaad5fd679fbd5ac.json new file mode 100644 index 0000000000..51f0e042d1 --- /dev/null +++ b/.sqlx/query-876e1659850a050155f3938231e801b381b112d54971c86beaad5fd679fbd5ac.json @@ -0,0 +1,41 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1 AND user_id = $2", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "location_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "public_ip: IpAddr", + "type_info": "Inet" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8" + ] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "876e1659850a050155f3938231e801b381b112d54971c86beaad5fd679fbd5ac" +} diff --git a/.sqlx/query-87868c21e47dd3b55ba1aefb690ce4ea9b463d7e71533e9c6312939a3d77b49e.json b/.sqlx/query-87868c21e47dd3b55ba1aefb690ce4ea9b463d7e71533e9c6312939a3d77b49e.json new file mode 100644 index 0000000000..0db9a2aa7c --- /dev/null +++ b/.sqlx/query-87868c21e47dd3b55ba1aefb690ce4ea9b463d7e71533e9c6312939a3d77b49e.json @@ -0,0 +1,38 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT id, \"user_id\",\"location_id\",\"public_ip\" \"public_ip: IpAddr\" FROM \"user_snat_binding\"", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + }, + { + "ordinal": 1, + "name": "user_id", + "type_info": "Int8" + }, + { + "ordinal": 2, + "name": "location_id", + "type_info": "Int8" + }, + { + "ordinal": 3, + "name": "public_ip: IpAddr", + "type_info": "Inet" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false + ] + }, + "hash": "87868c21e47dd3b55ba1aefb690ce4ea9b463d7e71533e9c6312939a3d77b49e" +} diff --git a/.sqlx/query-8d8416a6cc1f0bae02e126ca398e87000f305499668455d7e4d949f7d0a7be9a.json b/.sqlx/query-8d8416a6cc1f0bae02e126ca398e87000f305499668455d7e4d949f7d0a7be9a.json new file mode 100644 index 0000000000..01f2bb1f99 --- /dev/null +++ b/.sqlx/query-8d8416a6cc1f0bae02e126ca398e87000f305499668455d7e4d949f7d0a7be9a.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM \"user_snat_binding\" WHERE id = $1", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Int8" + ] + }, + "nullable": [] + }, + "hash": "8d8416a6cc1f0bae02e126ca398e87000f305499668455d7e4d949f7d0a7be9a" +} diff --git a/.sqlx/query-cda76abbdfed425c3b06c5b64115529ffe33c7290a63adfbaaa416efeffefac3.json b/.sqlx/query-cda76abbdfed425c3b06c5b64115529ffe33c7290a63adfbaaa416efeffefac3.json new file mode 100644 index 0000000000..b606660ad7 --- /dev/null +++ b/.sqlx/query-cda76abbdfed425c3b06c5b64115529ffe33c7290a63adfbaaa416efeffefac3.json @@ -0,0 +1,24 @@ +{ + "db_name": "PostgreSQL", + "query": "INSERT INTO \"user_snat_binding\" (\"user_id\",\"location_id\",\"public_ip\") VALUES ($1,$2,$3) RETURNING id", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "id", + "type_info": "Int8" + } + ], + "parameters": { + "Left": [ + "Int8", + "Int8", + "Inet" + ] + }, + "nullable": [ + false + ] + }, + "hash": "cda76abbdfed425c3b06c5b64115529ffe33c7290a63adfbaaa416efeffefac3" +} From d3a4aac1c717d4366c44b7440df75d10c428ef7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Thu, 26 Jun 2025 10:53:24 +0200 Subject: [PATCH 13/21] make snat API endpoints enterprise-only --- .../defguard_core/src/enterprise/snat/handlers.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/defguard_core/src/enterprise/snat/handlers.rs b/crates/defguard_core/src/enterprise/snat/handlers.rs index cf8dfa358b..309d45f115 100644 --- a/crates/defguard_core/src/enterprise/snat/handlers.rs +++ b/crates/defguard_core/src/enterprise/snat/handlers.rs @@ -13,7 +13,7 @@ use crate::{ appstate::AppState, auth::{AdminRole, SessionInfo}, db::Id, - enterprise::snat::UserSnatBinding, + enterprise::{handlers::LicenseInfo, snat::UserSnatBinding}, handlers::{ApiResponse, ApiResult}, }; @@ -37,7 +37,8 @@ use crate::{ ) )] pub async fn list_snat_bindings( - _role: AdminRole, + _license: LicenseInfo, + _admin_role: AdminRole, session: SessionInfo, Path(location_id): Path, State(appstate): State, @@ -92,7 +93,8 @@ pub struct NewUserSnatBinding { ) )] pub async fn create_snat_binding( - _role: AdminRole, + _license: LicenseInfo, + _admin_role: AdminRole, session: SessionInfo, Path(location_id): Path, State(appstate): State, @@ -143,7 +145,8 @@ pub struct EditUserSnatBinding { ) )] pub async fn modify_snat_binding( - _role: AdminRole, + _license: LicenseInfo, + _admin_role: AdminRole, session: SessionInfo, Path((location_id, user_id)): Path<(Id, Id)>, State(appstate): State, @@ -189,7 +192,8 @@ pub async fn modify_snat_binding( ) )] pub async fn delete_snat_binding( - _role: AdminRole, + _license: LicenseInfo, + _admin_role: AdminRole, session: SessionInfo, Path((location_id, user_id)): Path<(Id, Id)>, State(appstate): State, From 1ab5839e6251f244176149341d3719e461374700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Thu, 26 Jun 2025 11:54:58 +0200 Subject: [PATCH 14/21] trigger firewall update on snat binding change --- .../src/enterprise/snat/handlers.rs | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/crates/defguard_core/src/enterprise/snat/handlers.rs b/crates/defguard_core/src/enterprise/snat/handlers.rs index 309d45f115..7ec69d8cfc 100644 --- a/crates/defguard_core/src/enterprise/snat/handlers.rs +++ b/crates/defguard_core/src/enterprise/snat/handlers.rs @@ -12,7 +12,7 @@ use utoipa::ToSchema; use crate::{ appstate::AppState, auth::{AdminRole, SessionInfo}, - db::Id, + db::{GatewayEvent, Id, WireguardNetwork}, enterprise::{handlers::LicenseInfo, snat::UserSnatBinding}, handlers::{ApiResponse, ApiResult}, }; @@ -108,6 +108,18 @@ pub async fn create_snat_binding( let binding = snat_binding.save(&appstate.pool).await?; + // trigger firewall config update on relevant gateways + let mut conn = appstate.pool.acquire().await?; + if let Some(location) = WireguardNetwork::find_by_id(&appstate.pool, location_id).await? { + if let Some(firewall_config) = location.try_get_firewall_config(&mut conn).await? { + debug!("Sending firewall config update for location {location} affected by adding new SNAT binding"); + appstate.send_wireguard_event(GatewayEvent::FirewallConfigChanged( + location_id, + firewall_config, + )); + } + } + Ok(ApiResponse { json: json!(binding), status: StatusCode::CREATED, @@ -164,6 +176,18 @@ pub async fn modify_snat_binding( snat_binding.update_ip(data.public_ip); snat_binding.save(&appstate.pool).await?; + // trigger firewall config update on relevant gateways + let mut conn = appstate.pool.acquire().await?; + if let Some(location) = WireguardNetwork::find_by_id(&appstate.pool, location_id).await? { + if let Some(firewall_config) = location.try_get_firewall_config(&mut conn).await? { + debug!("Sending firewall config update for location {location} affected by adding new SNAT binding"); + appstate.send_wireguard_event(GatewayEvent::FirewallConfigChanged( + location_id, + firewall_config, + )); + } + } + Ok(ApiResponse { json: json!(snat_binding), status: StatusCode::OK, @@ -208,5 +232,17 @@ pub async fn delete_snat_binding( // delete binding snat_binding.delete(&appstate.pool).await?; + // trigger firewall config update on relevant gateways + let mut conn = appstate.pool.acquire().await?; + if let Some(location) = WireguardNetwork::find_by_id(&appstate.pool, location_id).await? { + if let Some(firewall_config) = location.try_get_firewall_config(&mut conn).await? { + debug!("Sending firewall config update for location {location} affected by adding new SNAT binding"); + appstate.send_wireguard_event(GatewayEvent::FirewallConfigChanged( + location_id, + firewall_config, + )); + } + } + Ok(ApiResponse::default()) } From f8c75fb4f521ed34257b58b6eb0ca9dfb46fafbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Tue, 1 Jul 2025 17:54:14 +0200 Subject: [PATCH 15/21] update protos --- proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto b/proto index f0c030a972..f042045ec6 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit f0c030a9725a0fd055efa69b58e0d7ee42654583 +Subproject commit f042045ec68fab166bfccb707a93adccfb10bc9d From f1cdae270b5d5294f02334ec72f30616dbfe12dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Wed, 2 Jul 2025 09:01:46 +0200 Subject: [PATCH 16/21] update protos --- proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto b/proto index f042045ec6..c0aef68395 160000 --- a/proto +++ b/proto @@ -1 +1 @@ -Subproject commit f042045ec68fab166bfccb707a93adccfb10bc9d +Subproject commit c0aef68395720f46a7f038b6766de3bb30e02930 From a850652205283853f15fc88ccb2901c7eb778518 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Wed, 2 Jul 2025 09:01:54 +0200 Subject: [PATCH 17/21] update dependencies --- Cargo.lock | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++--- flake.lock | 6 +++--- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ee7323d68..9dffd6c33f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3836,9 +3836,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.21" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8cea6b35bcceb099f30173754403d2eba0a5dc18cea3630fccd88251909288" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64 0.22.1", "bytes", @@ -6120,7 +6120,7 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", @@ -6155,6 +6155,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -6167,6 +6173,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -6179,12 +6191,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -6197,6 +6221,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -6209,6 +6239,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -6221,6 +6257,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -6233,6 +6275,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.7.11" diff --git a/flake.lock b/flake.lock index 69fe2db4c8..b227bc8e14 100644 --- a/flake.lock +++ b/flake.lock @@ -48,11 +48,11 @@ ] }, "locked": { - "lastModified": 1751338093, - "narHash": "sha256-/yd9nPcTfUZPFtwjRbdB5yGLdt3LTPqz6Ja63Joiahs=", + "lastModified": 1751423951, + "narHash": "sha256-AowKhJGplXRkAngSvb+32598DTiI6LOzhAnzgvbCtYM=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "6cfb7821732dac2d3e2dea857a5613d3b856c20c", + "rev": "1684ed5b15859b655caf41b467d046e29a994d04", "type": "github" }, "original": { From c461f8c2bfb060cd2491d602c5ef672d31ce4393 Mon Sep 17 00:00:00 2001 From: Maciek <19913370+wojcik91@users.noreply.github.com> Date: Wed, 2 Jul 2025 10:05:38 +0200 Subject: [PATCH 18/21] Update crates/defguard_core/src/enterprise/firewall/mod.rs Co-authored-by: Aleksander <170264518+t-aleksander@users.noreply.github.com> --- crates/defguard_core/src/enterprise/firewall/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/defguard_core/src/enterprise/firewall/mod.rs b/crates/defguard_core/src/enterprise/firewall/mod.rs index f287f35a2e..e49d6cab83 100644 --- a/crates/defguard_core/src/enterprise/firewall/mod.rs +++ b/crates/defguard_core/src/enterprise/firewall/mod.rs @@ -797,7 +797,7 @@ async fn generate_user_snat_bindings_for_location( return Ok(Vec::new()); } - // iniitalize output list + // initialize output list let mut bindings = Vec::new(); // process each user SNAT binding From 97a6b1f04a391d521ac5af163ffec95b6af3f396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Wed, 2 Jul 2025 10:11:25 +0200 Subject: [PATCH 19/21] move snat model definitions --- .../src/enterprise/db/models/mod.rs | 1 + .../src/enterprise/db/models/snat.rs | 69 +++++++++++++++++++ .../src/enterprise/firewall/mod.rs | 6 +- .../src/enterprise/snat/handlers.rs | 2 +- .../defguard_core/src/enterprise/snat/mod.rs | 68 ------------------ 5 files changed, 75 insertions(+), 71 deletions(-) create mode 100644 crates/defguard_core/src/enterprise/db/models/snat.rs diff --git a/crates/defguard_core/src/enterprise/db/models/mod.rs b/crates/defguard_core/src/enterprise/db/models/mod.rs index 36ae9de03e..18af2791d2 100644 --- a/crates/defguard_core/src/enterprise/db/models/mod.rs +++ b/crates/defguard_core/src/enterprise/db/models/mod.rs @@ -3,3 +3,4 @@ pub mod activity_log_stream; pub mod api_tokens; pub mod enterprise_settings; pub mod openid_provider; +pub mod snat; diff --git a/crates/defguard_core/src/enterprise/db/models/snat.rs b/crates/defguard_core/src/enterprise/db/models/snat.rs new file mode 100644 index 0000000000..08fcd0db92 --- /dev/null +++ b/crates/defguard_core/src/enterprise/db/models/snat.rs @@ -0,0 +1,69 @@ +use std::net::IpAddr; + +use crate::{ + db::{Id, NoId}, + enterprise::snat::error::UserSnatBindingError, +}; +use model_derive::Model; +use serde::Serialize; +use sqlx::{query_as, PgExecutor}; +use utoipa::ToSchema; + +#[derive(Debug, Model, Serialize, ToSchema)] +#[table(user_snat_binding)] +pub struct UserSnatBinding { + pub id: I, + pub user_id: Id, + pub location_id: Id, + #[model(ip)] + #[schema(value_type = String)] + pub public_ip: IpAddr, +} + +impl UserSnatBinding { + pub fn new(user_id: Id, location_id: Id, public_ip: IpAddr) -> Self { + Self { + id: NoId, + user_id, + location_id, + public_ip, + } + } +} + +impl UserSnatBinding { + pub async fn find_binding<'e, E>( + executor: E, + location_id: Id, + user_id: Id, + ) -> Result + where + E: PgExecutor<'e>, + { + let binding = query_as!(Self, + "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1 AND user_id = $2", + location_id, user_id + ).fetch_one(executor).await?; + + Ok(binding) + } + + pub async fn all_for_location<'e, E>( + executor: E, + location_id: Id, + ) -> Result, sqlx::Error> + where + E: PgExecutor<'e>, + { + let bindings = query_as!(Self, + "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1", + location_id + ).fetch_all(executor).await?; + + Ok(bindings) + } + + pub fn update_ip(&mut self, new_public_ip: IpAddr) { + self.public_ip = new_public_ip; + } +} diff --git a/crates/defguard_core/src/enterprise/firewall/mod.rs b/crates/defguard_core/src/enterprise/firewall/mod.rs index e49d6cab83..6786e25d79 100644 --- a/crates/defguard_core/src/enterprise/firewall/mod.rs +++ b/crates/defguard_core/src/enterprise/firewall/mod.rs @@ -11,12 +11,14 @@ use super::{ AclAliasDestinationRange, AclRule, AclRuleDestinationRange, AclRuleInfo, PortRange, Protocol, }, - snat::UserSnatBinding, utils::merge_ranges, }; use crate::{ db::{models::error::ModelError, Device, Id, User, WireguardNetwork}, - enterprise::{db::models::acl::AliasKind, is_enterprise_enabled}, + enterprise::{ + db::models::{acl::AliasKind, snat::UserSnatBinding}, + is_enterprise_enabled, + }, grpc::proto::enterprise::firewall::{ ip_address::Address, port::Port as PortInner, FirewallConfig, FirewallPolicy, FirewallRule, IpAddress, IpRange, IpVersion, Port, PortRange as PortRangeProto, diff --git a/crates/defguard_core/src/enterprise/snat/handlers.rs b/crates/defguard_core/src/enterprise/snat/handlers.rs index 7ec69d8cfc..64f6478b23 100644 --- a/crates/defguard_core/src/enterprise/snat/handlers.rs +++ b/crates/defguard_core/src/enterprise/snat/handlers.rs @@ -13,7 +13,7 @@ use crate::{ appstate::AppState, auth::{AdminRole, SessionInfo}, db::{GatewayEvent, Id, WireguardNetwork}, - enterprise::{handlers::LicenseInfo, snat::UserSnatBinding}, + enterprise::{db::models::snat::UserSnatBinding, handlers::LicenseInfo}, handlers::{ApiResponse, ApiResult}, }; diff --git a/crates/defguard_core/src/enterprise/snat/mod.rs b/crates/defguard_core/src/enterprise/snat/mod.rs index 38b20042ef..00d363536e 100644 --- a/crates/defguard_core/src/enterprise/snat/mod.rs +++ b/crates/defguard_core/src/enterprise/snat/mod.rs @@ -1,70 +1,2 @@ -use std::net::IpAddr; - -use crate::db::{Id, NoId}; -use error::UserSnatBindingError; -use model_derive::Model; -use serde::Serialize; -use sqlx::{query_as, PgExecutor}; -use utoipa::ToSchema; - pub mod error; pub mod handlers; - -#[derive(Debug, Model, Serialize, ToSchema)] -#[table(user_snat_binding)] -pub struct UserSnatBinding { - pub id: I, - pub user_id: Id, - pub location_id: Id, - #[model(ip)] - #[schema(value_type = String)] - pub public_ip: IpAddr, -} - -impl UserSnatBinding { - pub fn new(user_id: Id, location_id: Id, public_ip: IpAddr) -> Self { - Self { - id: NoId, - user_id, - location_id, - public_ip, - } - } -} - -impl UserSnatBinding { - pub async fn find_binding<'e, E>( - executor: E, - location_id: Id, - user_id: Id, - ) -> Result - where - E: PgExecutor<'e>, - { - let binding = query_as!(Self, - "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1 AND user_id = $2", - location_id, user_id - ).fetch_one(executor).await?; - - Ok(binding) - } - - pub async fn all_for_location<'e, E>( - executor: E, - location_id: Id, - ) -> Result, sqlx::Error> - where - E: PgExecutor<'e>, - { - let bindings = query_as!(Self, - "SELECT id, user_id, location_id, \"public_ip\" \"public_ip: IpAddr\" FROM user_snat_binding WHERE location_id = $1", - location_id - ).fetch_all(executor).await?; - - Ok(bindings) - } - - pub fn update_ip(&mut self, new_public_ip: IpAddr) { - self.public_ip = new_public_ip; - } -} From 8e3187837b414d997823d43858ab11889e643f82 Mon Sep 17 00:00:00 2001 From: Maciek <19913370+wojcik91@users.noreply.github.com> Date: Wed, 2 Jul 2025 10:12:20 +0200 Subject: [PATCH 20/21] Update crates/defguard_core/src/enterprise/firewall/mod.rs Co-authored-by: Adam --- crates/defguard_core/src/enterprise/firewall/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/defguard_core/src/enterprise/firewall/mod.rs b/crates/defguard_core/src/enterprise/firewall/mod.rs index 6786e25d79..a4b3d63a0d 100644 --- a/crates/defguard_core/src/enterprise/firewall/mod.rs +++ b/crates/defguard_core/src/enterprise/firewall/mod.rs @@ -854,7 +854,7 @@ async fn generate_user_snat_bindings_for_location( } debug!( - "Generated {} SNAT bindings for location {}", + "Generated {} SNAT bindings for location {location_id}", bindings.len(), location_id ); From 075db19cb15685042fa854dec08c5083c20f5bc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20W=C3=B3jcik?= Date: Wed, 2 Jul 2025 10:32:41 +0200 Subject: [PATCH 21/21] remove unused variable --- crates/defguard_core/src/enterprise/firewall/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/defguard_core/src/enterprise/firewall/mod.rs b/crates/defguard_core/src/enterprise/firewall/mod.rs index a4b3d63a0d..513aa064fe 100644 --- a/crates/defguard_core/src/enterprise/firewall/mod.rs +++ b/crates/defguard_core/src/enterprise/firewall/mod.rs @@ -856,7 +856,6 @@ async fn generate_user_snat_bindings_for_location( debug!( "Generated {} SNAT bindings for location {location_id}", bindings.len(), - location_id ); Ok(bindings)