Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions crates/defguard_core/src/handlers/wireguard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ pub struct WireguardNetworkData {
pub service_location_mode: ServiceLocationMode,
}

const MIN_PEER_DISCONNECT_THRESHOLD_WITH_MFA: i32 = 120;

impl WireguardNetworkData {
pub(crate) fn parse_allowed_ips(&self) -> Vec<IpNetwork> {
self.allowed_ips
Expand Down Expand Up @@ -103,6 +105,20 @@ impl WireguardNetworkData {
Ok(subnets)
}

pub(crate) fn validate_peer_disconnect_threshold(&self) -> Result<(), WebError> {
if self.location_mfa_mode == LocationMfaMode::Disabled {
return Ok(());
}

if self.peer_disconnect_threshold >= MIN_PEER_DISCONNECT_THRESHOLD_WITH_MFA {
return Ok(());
}

Err(WebError::BadRequest(format!(
"peer_disconnect_threshold must be at least {MIN_PEER_DISCONNECT_THRESHOLD_WITH_MFA} when location MFA is enabled"
)))
}

pub(crate) async fn validate_location_mfa_mode<'e, E: sqlx::PgExecutor<'e>>(
&self,
executor: E,
Expand Down Expand Up @@ -216,6 +232,7 @@ pub(crate) async fn create_network(
});
}

data.validate_peer_disconnect_threshold()?;
data.validate_location_mfa_mode(&appstate.pool).await?;

let allowed_ips = data.parse_allowed_ips();
Expand Down Expand Up @@ -324,6 +341,7 @@ pub(crate) async fn modify_network(
});
}

data.validate_peer_disconnect_threshold()?;
data.validate_location_mfa_mode(&appstate.pool).await?;

let mut network = find_network(network_id, &appstate.pool).await?;
Expand Down
118 changes: 118 additions & 0 deletions crates/defguard_core/tests/integration/api/wireguard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ use super::common::{
authenticate_admin, exceed_enterprise_limits, make_network, make_test_client, setup_pool,
};

const INVALID_MFA_PEER_DISCONNECT_THRESHOLD: i32 = 119;
const MINIMUM_MFA_PEER_DISCONNECT_THRESHOLD: i32 = 120;

#[sqlx::test]
async fn test_network(_: PgPoolOptions, options: PgConnectOptions) {
let pool = setup_pool(options).await;
Expand Down Expand Up @@ -319,6 +322,121 @@ async fn test_location_mfa_mode_validation_modify(_: PgPoolOptions, options: PgC
assert_eq!(response.status(), StatusCode::OK);
}

#[sqlx::test]
async fn test_peer_disconnect_threshold_validation_create(
_: PgPoolOptions,
options: PgConnectOptions,
) {
let pool = setup_pool(options).await;

let (mut client, _client_state) = make_test_client(pool).await;
authenticate_admin(&mut client).await;

let mut location_data = WireguardNetworkData {
name: "test_location_disabled".into(),
address: "10.1.1.0/24".into(),
endpoint: "10.1.1.1".parse().unwrap(),
port: 55555,
allowed_ips: Some("10.1.1.0/24, 10.2.0.1/16, 10.10.10.54/32".into()),
dns: None,
mtu: DEFAULT_WIREGUARD_MTU,
fwmark: 0,
allow_all_groups: false,
allowed_groups: vec!["admin".into()],
keepalive_interval: DEFAULT_KEEPALIVE_INTERVAL,
peer_disconnect_threshold: INVALID_MFA_PEER_DISCONNECT_THRESHOLD,
acl_enabled: false,
acl_default_allow: false,
location_mfa_mode: LocationMfaMode::Disabled,
service_location_mode: ServiceLocationMode::Disabled,
};

let response = client
.post("/api/v1/network")
.json(&location_data)
.send()
.await;
assert_eq!(response.status(), StatusCode::CREATED);

location_data.name = "test_location_internal".into();
location_data.location_mfa_mode = LocationMfaMode::Internal;
let response = client
.post("/api/v1/network")
.json(&location_data)
.send()
.await;
assert_eq!(response.status(), StatusCode::BAD_REQUEST);

location_data.name = "test_location_internal_boundary".into();
location_data.peer_disconnect_threshold = MINIMUM_MFA_PEER_DISCONNECT_THRESHOLD;
let response = client
.post("/api/v1/network")
.json(&location_data)
.send()
.await;
assert_eq!(response.status(), StatusCode::CREATED);
}

#[sqlx::test]
async fn test_peer_disconnect_threshold_validation_modify(
_: PgPoolOptions,
options: PgConnectOptions,
) {
let pool = setup_pool(options).await;

let (mut client, _client_state) = make_test_client(pool).await;
authenticate_admin(&mut client).await;

let mut location_data = WireguardNetworkData {
name: "test_location".into(),
address: "10.1.1.0/24".into(),
endpoint: "10.1.1.1".parse().unwrap(),
port: 55555,
allowed_ips: Some("10.1.1.0/24, 10.2.0.1/16, 10.10.10.54/32".into()),
dns: None,
mtu: DEFAULT_WIREGUARD_MTU,
fwmark: 0,
allow_all_groups: false,
allowed_groups: vec!["admin".into()],
keepalive_interval: DEFAULT_KEEPALIVE_INTERVAL,
peer_disconnect_threshold: INVALID_MFA_PEER_DISCONNECT_THRESHOLD,
acl_enabled: false,
acl_default_allow: false,
location_mfa_mode: LocationMfaMode::Disabled,
service_location_mode: ServiceLocationMode::Disabled,
};

let response = client
.post("/api/v1/network")
.json(&location_data)
.send()
.await;
assert_eq!(response.status(), StatusCode::CREATED);

let response = client
.put("/api/v1/network/1")
.json(&location_data)
.send()
.await;
assert_eq!(response.status(), StatusCode::OK);

location_data.location_mfa_mode = LocationMfaMode::Internal;
let response = client
.put("/api/v1/network/1")
.json(&location_data)
.send()
.await;
assert_eq!(response.status(), StatusCode::BAD_REQUEST);

location_data.peer_disconnect_threshold = MINIMUM_MFA_PEER_DISCONNECT_THRESHOLD;
let response = client
.put("/api/v1/network/1")
.json(&location_data)
.send()
.await;
assert_eq!(response.status(), StatusCode::OK);
}

#[sqlx::test]
async fn test_device(_: PgPoolOptions, options: PgConnectOptions) {
let pool = setup_pool(options).await;
Expand Down
12 changes: 6 additions & 6 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"humanize-duration": "^3.33.2",
"ipaddr.js": "^2.3.0",
"lodash-es": "^4.17.23",
"motion": "^12.36.0",
"motion": "^12.37.0",
"qrcode.react": "^4.2.0",
"qs": "^6.15.0",
"radashi": "^12.7.2",
Expand Down
26 changes: 13 additions & 13 deletions web/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading