diff --git a/crates/defguard_core/src/enterprise/firewall/mod.rs b/crates/defguard_core/src/enterprise/firewall/mod.rs
index afe826623e..442750b8a0 100644
--- a/crates/defguard_core/src/enterprise/firewall/mod.rs
+++ b/crates/defguard_core/src/enterprise/firewall/mod.rs
@@ -238,7 +238,11 @@ async fn get_manual_destination_rules(
}
// prepare destination addresses
- let (dest_addrs_v4, dest_addrs_v6) = process_destination_addrs(&addresses, &address_ranges);
+ let (dest_addrs_v4, dest_addrs_v6) = if any_address {
+ (Vec::new(), Vec::new())
+ } else {
+ process_destination_addrs(&addresses, &address_ranges)
+ };
// prepare destination ports
let destination_ports = if any_port {
@@ -319,8 +323,11 @@ async fn get_predefined_destination_rules(
let alias_destination_ranges = destination.get_destination_ranges(&mut *conn).await?;
// combine destination addrs
- let (dest_addrs_v4, dest_addrs_v6) =
- process_alias_destination_addrs(&destination.addresses, &alias_destination_ranges);
+ let (dest_addrs_v4, dest_addrs_v6) = if destination.any_address {
+ (Vec::new(), Vec::new())
+ } else {
+ process_alias_destination_addrs(&destination.addresses, &alias_destination_ranges)
+ };
// process alias ports
let destination_ports = if destination.any_port {
@@ -430,6 +437,7 @@ fn create_rules(
debug!("ALLOW rule generated from ACL: {rule:?}");
Some(rule)
};
+
// prepare DENY rule
// it should specify only the destination addrs to block all remaining traffic
let deny = FirewallRule {
diff --git a/crates/defguard_core/src/enterprise/firewall/tests/destination.rs b/crates/defguard_core/src/enterprise/firewall/tests/destination.rs
index fd8e62bab7..19f4d68add 100644
--- a/crates/defguard_core/src/enterprise/firewall/tests/destination.rs
+++ b/crates/defguard_core/src/enterprise/firewall/tests/destination.rs
@@ -1,11 +1,21 @@
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
-use defguard_proto::enterprise::firewall::{IpAddress, IpRange, ip_address::Address};
+use defguard_common::db::{NoId, models::WireguardNetwork, setup_pool};
+use defguard_proto::enterprise::firewall::{
+ FirewallPolicy, IpAddress, IpRange, ip_address::Address,
+};
+use rand::thread_rng;
+use sqlx::postgres::{PgConnectOptions, PgPoolOptions};
use crate::enterprise::{
- db::models::acl::AclRuleDestinationRange, firewall::process_destination_addrs,
+ db::models::acl::{
+ AclAlias, AclAliasDestinationRange, AclRule, AclRuleDestinationRange, AliasKind, RuleState,
+ },
+ firewall::{process_destination_addrs, try_get_location_firewall_config},
};
+use super::{create_acl_rule, create_test_users_and_devices, set_test_license_business};
+
#[test]
fn test_process_destination_addrs_v4() {
// Test data with mixed IPv4 and IPv6 networks
@@ -115,3 +125,194 @@ fn test_process_destination_addrs_v6() {
let ipv4_only = process_destination_addrs(&["192.168.1.0/24".parse().unwrap()], &[]);
assert!(ipv4_only.1.is_empty());
}
+
+#[sqlx::test]
+async fn test_any_address_overwrites_manual_destination(
+ _: PgPoolOptions,
+ options: PgConnectOptions,
+) {
+ set_test_license_business();
+ let pool = setup_pool(options).await;
+
+ let mut rng = thread_rng();
+
+ let location = WireguardNetwork {
+ id: NoId,
+ acl_enabled: true,
+ address: vec!["10.0.0.0/16".parse().unwrap()],
+ ..Default::default()
+ }
+ .save(&pool)
+ .await
+ .unwrap();
+
+ create_test_users_and_devices(&mut rng, &pool, vec![&location]).await;
+
+ let acl_rule = AclRule {
+ id: NoId,
+ name: "any destination rule".to_string(),
+ state: RuleState::Applied,
+ allow_all_users: true,
+ any_address: true,
+ addresses: vec!["192.168.1.0/24".parse().unwrap()],
+ use_manual_destination_settings: true,
+ ..Default::default()
+ };
+
+ create_acl_rule(
+ &pool,
+ acl_rule,
+ vec![location.id],
+ Vec::new(),
+ Vec::new(),
+ Vec::new(),
+ Vec::new(),
+ Vec::new(),
+ Vec::new(),
+ vec![(
+ IpAddr::V4(Ipv4Addr::new(192, 168, 1, 10)),
+ IpAddr::V4(Ipv4Addr::new(192, 168, 1, 20)),
+ )],
+ Vec::new(),
+ )
+ .await;
+
+ let mut conn = pool.acquire().await.unwrap();
+ let generated_firewall_rules = try_get_location_firewall_config(&location, &mut conn)
+ .await
+ .unwrap()
+ .unwrap()
+ .rules;
+
+ let expected_source_addrs = [
+ IpAddress {
+ address: Some(Address::IpRange(IpRange {
+ start: "10.0.1.1".to_string(),
+ end: "10.0.1.2".to_string(),
+ })),
+ },
+ IpAddress {
+ address: Some(Address::IpRange(IpRange {
+ start: "10.0.2.1".to_string(),
+ end: "10.0.2.2".to_string(),
+ })),
+ },
+ ];
+
+ assert_eq!(generated_firewall_rules.len(), 2);
+
+ let allow_rule = &generated_firewall_rules[0];
+ assert_eq!(allow_rule.verdict, i32::from(FirewallPolicy::Allow));
+ assert_eq!(allow_rule.source_addrs, expected_source_addrs);
+ assert!(allow_rule.destination_addrs.is_empty());
+
+ let deny_rule = &generated_firewall_rules[1];
+ assert_eq!(deny_rule.verdict, i32::from(FirewallPolicy::Deny));
+ assert!(deny_rule.source_addrs.is_empty());
+ assert!(deny_rule.destination_addrs.is_empty());
+}
+
+#[sqlx::test]
+async fn test_any_address_overwrites_destination_alias_addrs(
+ _: PgPoolOptions,
+ options: PgConnectOptions,
+) {
+ set_test_license_business();
+ let pool = setup_pool(options).await;
+
+ let mut rng = thread_rng();
+
+ let location = WireguardNetwork {
+ id: NoId,
+ acl_enabled: true,
+ address: vec!["10.0.0.0/16".parse().unwrap()],
+ ..Default::default()
+ }
+ .save(&pool)
+ .await
+ .unwrap();
+
+ create_test_users_and_devices(&mut rng, &pool, vec![&location]).await;
+
+ let destination_alias = AclAlias {
+ id: NoId,
+ name: "any destination alias".to_string(),
+ kind: AliasKind::Destination,
+ any_address: true,
+ any_port: true,
+ any_protocol: true,
+ addresses: vec!["10.1.0.0/24".parse().unwrap()],
+ ..Default::default()
+ }
+ .save(&pool)
+ .await
+ .unwrap();
+
+ AclAliasDestinationRange {
+ id: NoId,
+ alias_id: destination_alias.id,
+ start: IpAddr::V4(Ipv4Addr::new(10, 2, 0, 10)),
+ end: IpAddr::V4(Ipv4Addr::new(10, 2, 0, 20)),
+ }
+ .save(&pool)
+ .await
+ .unwrap();
+
+ let acl_rule = AclRule {
+ id: NoId,
+ name: "any destination alias rule".to_string(),
+ state: RuleState::Applied,
+ allow_all_users: true,
+ use_manual_destination_settings: false,
+ ..Default::default()
+ };
+
+ create_acl_rule(
+ &pool,
+ acl_rule,
+ vec![location.id],
+ Vec::new(),
+ Vec::new(),
+ Vec::new(),
+ Vec::new(),
+ Vec::new(),
+ Vec::new(),
+ Vec::new(),
+ vec![destination_alias.id],
+ )
+ .await;
+
+ let mut conn = pool.acquire().await.unwrap();
+ let generated_firewall_rules = try_get_location_firewall_config(&location, &mut conn)
+ .await
+ .unwrap()
+ .unwrap()
+ .rules;
+
+ let expected_source_addrs = [
+ IpAddress {
+ address: Some(Address::IpRange(IpRange {
+ start: "10.0.1.1".to_string(),
+ end: "10.0.1.2".to_string(),
+ })),
+ },
+ IpAddress {
+ address: Some(Address::IpRange(IpRange {
+ start: "10.0.2.1".to_string(),
+ end: "10.0.2.2".to_string(),
+ })),
+ },
+ ];
+
+ assert_eq!(generated_firewall_rules.len(), 2);
+
+ let allow_rule = &generated_firewall_rules[0];
+ assert_eq!(allow_rule.verdict, i32::from(FirewallPolicy::Allow));
+ assert_eq!(allow_rule.source_addrs, expected_source_addrs);
+ assert!(allow_rule.destination_addrs.is_empty());
+
+ let deny_rule = &generated_firewall_rules[1];
+ assert_eq!(deny_rule.verdict, i32::from(FirewallPolicy::Deny));
+ assert!(deny_rule.source_addrs.is_empty());
+ assert!(deny_rule.destination_addrs.is_empty());
+}
diff --git a/crates/defguard_core/src/enterprise/firewall/tests/mod.rs b/crates/defguard_core/src/enterprise/firewall/tests/mod.rs
index 40e6d233ba..78c8ca896d 100644
--- a/crates/defguard_core/src/enterprise/firewall/tests/mod.rs
+++ b/crates/defguard_core/src/enterprise/firewall/tests/mod.rs
@@ -1801,6 +1801,7 @@ async fn test_alias_kinds(_: PgPoolOptions, options: PgConnectOptions) {
addresses: vec!["192.168.1.0/24".parse().unwrap()],
allow_all_users: true,
use_manual_destination_settings: true,
+ any_address: false,
..Default::default()
}
.save(&pool)
diff --git a/web/src/pages/CERulePage/CERulePage.tsx b/web/src/pages/CERulePage/CERulePage.tsx
index 9ac7a5a167..57dd5b4522 100644
--- a/web/src/pages/CERulePage/CERulePage.tsx
+++ b/web/src/pages/CERulePage/CERulePage.tsx
@@ -30,6 +30,7 @@ import type {
import { AppText } from '../../shared/defguard-ui/components/AppText/AppText';
import { Button } from '../../shared/defguard-ui/components/Button/Button';
import { ButtonsGroup } from '../../shared/defguard-ui/components/ButtonsGroup/ButtonsGroup';
+import { Checkbox } from '../../shared/defguard-ui/components/Checkbox/Checkbox';
import { CheckboxIndicator } from '../../shared/defguard-ui/components/CheckboxIndicator/CheckboxIndicator';
import { Chip } from '../../shared/defguard-ui/components/Chip/Chip';
import { Divider } from '../../shared/defguard-ui/components/Divider/Divider';
@@ -253,8 +254,22 @@ const Content = ({ rule: initialRule }: Props) => {
return [];
}, [networkDevices]);
- const [_restrictionsPresent, _setRestrictionsPresent] = useState(false);
- // const [manualDestination, setManualDestination] = useState(false);
+ const [restrictUsers, setRestrictUsers] = useState(() =>
+ isPresent(initialRule)
+ ? initialRule.deny_all_users || initialRule.denied_users.length > 0
+ : false,
+ );
+ const [restrictGroups, setRestrictGroups] = useState(() =>
+ isPresent(initialRule)
+ ? initialRule.deny_all_groups || initialRule.denied_groups.length > 0
+ : false,
+ );
+ const [restrictDevices, setRestrictDevices] = useState(() =>
+ isPresent(initialRule)
+ ? initialRule.deny_all_network_devices ||
+ initialRule.denied_network_devices.length > 0
+ : false,
+ );
const formSchema = useMemo(
() =>
@@ -417,13 +432,18 @@ const Content = ({ rule: initialRule }: Props) => {
},
onSubmit: async ({ value }) => {
const toSend = cloneDeep(value);
- // FIXME: When restrictions section is reworked
- toSend.deny_all_network_devices = false;
- toSend.deny_all_users = false;
- toSend.deny_all_groups = false;
- toSend.denied_network_devices = [];
- toSend.denied_groups = [];
- toSend.denied_users = [];
+ if (!restrictUsers) {
+ toSend.deny_all_users = false;
+ toSend.denied_users = [];
+ }
+ if (!restrictGroups) {
+ toSend.deny_all_groups = false;
+ toSend.denied_groups = [];
+ }
+ if (!restrictDevices) {
+ toSend.deny_all_network_devices = false;
+ toSend.denied_network_devices = [];
+ }
if (isPresent(initialRule)) {
await editRule({
...toSend,
@@ -797,66 +817,172 @@ const Content = ({ rule: initialRule }: Props) => {
)}
- {/* {`If needed, you may exclude specific users, groups, or devices from accessing this location.`} {`Choose who or what should be blocked from accessing this location.`}