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
51 changes: 28 additions & 23 deletions crates/defguard/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use defguard_core::{
limits::update_counts,
},
events::{ApiEvent, BidiStreamEvent},
gateway_config,
grpc::{GatewayEvent, WorkerState, run_grpc_server},
init_dev_env, init_vpn_location, run_web_server,
setup_logs::CoreSetupLogLayer,
Expand Down Expand Up @@ -83,6 +84,29 @@ async fn main() -> Result<(), anyhow::Error> {
)
.await;

if config.openid_signing_key.is_some() {
info!("Using RSA OpenID signing key");
} else {
info!("Using HMAC OpenID signing key");
}

// initialize global settings struct
initialize_current_settings(&pool).await?;

debug!("Checking enterprise license status");
match License::load_or_renew(&pool).await {
Ok(license) => {
set_cached_license(license);
}
Err(err) => {
warn!(
"There was an error while loading the license, error: {err}. The enterprise \
features will be disabled."
);
set_cached_license(None);
}
}

// handle optional subcommands
if let Some(command) = &config.cmd {
match command {
Expand All @@ -93,21 +117,16 @@ async fn main() -> Result<(), anyhow::Error> {
let token = init_vpn_location(&pool, args).await?;
println!("{token}");
}
Command::GatewayConfig(args) => {
let config = gateway_config(&pool, args).await?;
println!("{config:#?}");
}
}

// return early
return Ok(());
}

if config.openid_signing_key.is_some() {
info!("Using RSA OpenID signing key");
} else {
info!("Using HMAC OpenID signing key");
}

// initialize global settings struct
initialize_current_settings(&pool).await?;

// Both flags must be provided together
if let Err(msg) = config.validate_adopt_flags() {
anyhow::bail!("{msg}");
Expand Down Expand Up @@ -208,20 +227,6 @@ async fn main() -> Result<(), anyhow::Error> {

update_counts(&pool).await?;

debug!("Checking enterprise license status");
match License::load_or_renew(&pool).await {
Ok(license) => {
set_cached_license(license);
}
Err(err) => {
warn!(
"There was an error while loading the license, error: {err}. The enterprise \
features will be disabled."
);
set_cached_license(None);
}
}

let (proxy_control_tx, proxy_control_rx) = channel::<ProxyControlMessage>(100);
let proxy_secret_key = settings.secret_key_required()?;
let proxy_manager = ProxyManager::new(
Expand Down
8 changes: 8 additions & 0 deletions crates/defguard_common/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ pub enum Command {
about = "Add a new VPN location and return a gateway token. Used for automated setup."
)]
InitVpnLocation(InitVpnLocationArgs),
#[command(about = "Output the gateway gRPC configuration payload for a VPN location by ID.")]
GatewayConfig(GatewayConfigArgs),
}

#[derive(Args, Debug, Clone)]
Expand All @@ -215,6 +217,12 @@ pub struct InitVpnLocationArgs {
pub id: Option<i64>,
}

#[derive(Args, Debug, Clone)]
pub struct GatewayConfigArgs {
#[arg(long)]
pub location_id: i64,
}

impl DefGuardConfig {
#[must_use]
pub fn new() -> Self {
Expand Down
48 changes: 46 additions & 2 deletions crates/defguard_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use defguard_certs::CertificateAuthority;
use defguard_common::{
VERSION,
auth::claims::{Claims, ClaimsType},
config::{DefGuardConfig, InitVpnLocationArgs, server_config},
config::{DefGuardConfig, GatewayConfigArgs, InitVpnLocationArgs, server_config},
db::{
init_db,
models::{
Expand All @@ -28,6 +28,7 @@ use defguard_common::{
},
types::proxy::ProxyControlMessage,
};
use defguard_proto::gateway::Configuration;
use defguard_version::server::DefguardVersionLayer;
use defguard_web_ui::{index, svg, web_asset};
use events::ApiEvent;
Expand Down Expand Up @@ -76,6 +77,7 @@ use crate::{
auth::failed_login::FailedLoginMap,
db::AppEvent,
enterprise::{
firewall::try_get_location_firewall_config,
handlers::{
acl::{
alias::{
Expand Down Expand Up @@ -167,7 +169,9 @@ use crate::{
},
worker::{create_job, create_worker_token, job_status, list_workers, remove_worker},
},
location_management::sync_location_allowed_devices,
location_management::{
allowed_peers::get_location_allowed_peers, sync_location_allowed_devices,
},
version::IncompatibleComponents,
};

Expand Down Expand Up @@ -929,6 +933,46 @@ pub async fn init_vpn_location(
Ok(token)
}

pub async fn gateway_config(
pool: &PgPool,
args: &GatewayConfigArgs,
) -> Result<Configuration, anyhow::Error> {
let location_id = args.location_id;

let mut conn = pool.acquire().await?;

// fetch specified location
let location = match WireguardNetwork::find_by_id(&mut *conn, location_id).await {
Ok(Some(network)) => network,
Ok(None) => return Err(anyhow!("Location {location_id} not found")),
Err(err) => {
return Err(anyhow!(
"Failed to retrieve location {location_id} with error: {err}"
));
}
};

// get peers
let peers = get_location_allowed_peers(&location, &mut *conn)
.await
.map_err(|err| anyhow!("Failed to get peers for location {location} with error: {err}"))?;

// prepare firewall config
let maybe_firewall_config = try_get_location_firewall_config(&location, &mut conn)
.await
.map_err(|err| {
anyhow!("Failed to prepare firewall config for location {location} with error: {err}")
})?;

// generate config
let mut config = Configuration::new(&location, peers, maybe_firewall_config);

// overwrite private key just in case
config.prvkey = "REDACTED".into();

Comment thread
j-chmielewski marked this conversation as resolved.
Ok(config)
Comment thread
j-chmielewski marked this conversation as resolved.
}

pub fn is_valid_phone_number(number: &str) -> bool {
PHONE_NUMBER_REGEX.is_match(number)
}
Expand Down
28 changes: 5 additions & 23 deletions crates/defguard_gateway_manager/src/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl GatewayHandler {
let peers = get_location_allowed_peers(&network, &self.pool).await?;

let maybe_firewall_config = try_get_location_firewall_config(&network, &mut conn).await?;
let payload = Some(core_response::Payload::Config(gen_config(
let payload = Some(core_response::Payload::Config(Configuration::new(
&network,
peers,
maybe_firewall_config,
Expand Down Expand Up @@ -1000,23 +1000,6 @@ fn try_protos_into_stats_message(
))
}

fn gen_config<I>(
network: &WireguardNetwork<I>,
peers: Vec<Peer>,
maybe_firewall_config: Option<FirewallConfig>,
) -> Configuration {
Configuration {
name: network.name.clone(),
port: network.port.cast_unsigned(),
prvkey: network.prvkey.clone(),
addresses: network.address().iter().map(ToString::to_string).collect(),
peers,
firewall_config: maybe_firewall_config,
mtu: network.mtu.cast_unsigned(),
fwmark: network.fwmark as u32,
}
}

#[cfg(test)]
mod tests {
use std::{collections::HashMap, net::IpAddr, str::FromStr, sync::Arc};
Expand All @@ -1034,13 +1017,12 @@ mod tests {
setup_pool,
};
use defguard_core::grpc::GatewayEvent;
use defguard_proto::gateway::{Peer, PeerStats, core_response};
use defguard_proto::gateway::{Configuration, Peer, PeerStats, core_response};
use sqlx::postgres::{PgConnectOptions, PgPoolOptions};
use tokio::sync::{broadcast, mpsc::unbounded_channel, watch};

use super::{
FirewallConfig, GatewayHandler, GatewayUpdatesHandler, gen_config,
try_protos_into_stats_message,
FirewallConfig, GatewayHandler, GatewayUpdatesHandler, try_protos_into_stats_message,
};

fn test_network(location_mfa_mode: LocationMfaMode) -> WireguardNetwork<Id> {
Expand Down Expand Up @@ -1145,7 +1127,7 @@ mod tests {

#[test]
fn gen_config_maps_network_fields() {
let config = gen_config(
let config = Configuration::new(
&build_network(),
vec![Peer {
pubkey: "peer-public-key".to_string(),
Expand Down Expand Up @@ -1186,7 +1168,7 @@ mod tests {

#[test]
fn gen_config_preserves_absent_firewall_config_and_empty_peers() {
let config = gen_config(&build_network(), Vec::new(), None);
let config = Configuration::new(&build_network(), Vec::new(), None);

assert!(config.peers.is_empty());
assert!(config.firewall_config.is_none());
Expand Down
26 changes: 25 additions & 1 deletion crates/defguard_proto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use defguard_common::{
db::{
Id,
models::{
Device, DeviceConfig, User,
Device, DeviceConfig, User, WireguardNetwork,
vpn_client_session::VpnClientMfaMethod,
wireguard::{LocationMfaMode, ServiceLocationMode},
},
Expand All @@ -30,6 +30,11 @@ use proxy::{CoreError, MfaMethod};
use serde::Serialize;
use tonic::Status;

use crate::{
enterprise::firewall::FirewallConfig,
gateway::{Configuration, Peer},
};

// Client MFA methods
impl fmt::Display for MfaMethod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down Expand Up @@ -152,3 +157,22 @@ impl From<ServiceLocationMode> for proxy::ServiceLocationMode {
}
}
}

impl Configuration {
pub fn new(
location: &WireguardNetwork<Id>,
peers: Vec<Peer>,
maybe_firewall_config: Option<FirewallConfig>,
) -> Self {
Self {
name: location.name.clone(),
port: location.port.cast_unsigned(),
prvkey: location.prvkey.clone(),
addresses: location.address().iter().map(ToString::to_string).collect(),
peers,
firewall_config: maybe_firewall_config,
mtu: location.mtu.cast_unsigned(),
fwmark: location.fwmark as u32,
}
}
}
3 changes: 0 additions & 3 deletions migrations/20251125072923_network_gateways.down.sql

This file was deleted.

20 changes: 0 additions & 20 deletions migrations/20251125072923_network_gateways.up.sql

This file was deleted.

3 changes: 0 additions & 3 deletions migrations/20251218140442_[2.0.0]_core_ca.down.sql

This file was deleted.

3 changes: 0 additions & 3 deletions migrations/20251218140442_[2.0.0]_core_ca.up.sql

This file was deleted.

Loading
Loading