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
5 changes: 3 additions & 2 deletions crates/defguard/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use defguard_common::{
settings::{initialize_current_settings, update_current_settings},
},
},
gateway_event::GatewayCommand,
messages::peer_stats_update::PeerStatsUpdate,
types::proxy::ProxyControlMessage,
};
Expand All @@ -30,7 +31,7 @@ use defguard_core::{
},
events::{ApiEvent, BidiStreamEvent},
gateway_config,
grpc::{GatewayEvent, WorkerState, run_grpc_server},
grpc::{WorkerState, run_grpc_server},
init_dev_env, init_vpn_location, run_web_server,
setup_logs::CoreSetupLogLayer,
utility_thread::run_utility_thread,
Expand Down Expand Up @@ -210,7 +211,7 @@ async fn main() -> Result<(), anyhow::Error> {
// setup communication channels for services
let (webhook_tx, webhook_rx) = unbounded_channel::<AppEvent>();
// RX is discarded here since it can be derived from TX later on
let (gateway_tx, _gateway_rx) = broadcast::channel::<GatewayEvent>(256);
let (gateway_tx, _gateway_rx) = broadcast::channel::<GatewayCommand>(256);
let (event_logger_tx, event_logger_rx) = unbounded_channel::<EventLoggerMessage>();
let (peer_stats_tx, peer_stats_rx) = unbounded_channel::<PeerStatsUpdate>();

Expand Down
66 changes: 66 additions & 0 deletions crates/defguard_common/src/gateway_event.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//! Gateway command types and helpers for communicating with the gateway manager service.
//!
//! [`GatewayCommand`] is the primary type sent from core to the gateway manager over
//! an in-process broadcast channel. The gateway manager converts each command to the
//! appropriate protobuf wire message before forwarding it to the gateway daemon.

use tokio::sync::broadcast::Sender;
use tracing::{debug, error};

use crate::{
db::{
Id,
models::{
Device, WireguardNetwork,
device::{DeviceInfo, DeviceNetworkInfo},
},
},
gateway_types::{FirewallConfig, WireguardPeer},
};

/// A command sent from core to the gateway manager service.
///
/// Each variant instructs the gateway daemon to update its WireGuard state or
/// firewall configuration. Native Rust types are used throughout; conversion to
/// protobuf wire types happens at the serialization boundary in the gateway manager.
#[derive(Clone, Debug)]
pub enum GatewayCommand {
NetworkCreated(Id, WireguardNetwork<Id>),
NetworkModified(
Id,
WireguardNetwork<Id>,
Vec<WireguardPeer>,
Option<FirewallConfig>,
),
NetworkDeleted(Id, String),
DeviceCreated(DeviceInfo),
DeviceModified(DeviceInfo),
DeviceDeleted(DeviceInfo),
FirewallConfigChanged(Id, FirewallConfig),
FirewallDisabled(Id),
VpnSessionAuthorized(Id, Device<Id>, DeviceNetworkInfo),
VpnSessionDeauthorized(Id, Device<Id>),
}

/// Sends a [`GatewayCommand`] to the gateway manager service.
///
/// In API handler context prefer `AppState::send_gateway_command`.
pub fn send_gateway_command(command: GatewayCommand, gateway_tx: &Sender<GatewayCommand>) {
debug!("Sending the following command to Gateway Manager: {command:?}");
if let Err(err) = gateway_tx.send(command) {
error!("Error sending gateway command: {err}");
}
}

/// Sends multiple [`GatewayCommand`]s to the gateway manager service.
///
/// In API handler context prefer `AppState::send_multiple_gateway_commands`.
pub fn send_multiple_gateway_commands(
commands: Vec<GatewayCommand>,
gateway_tx: &Sender<GatewayCommand>,
) {
debug!("Sending {} gateway commands", commands.len());
for command in commands {
send_gateway_command(command, gateway_tx);
}
}
113 changes: 113 additions & 0 deletions crates/defguard_common/src/gateway_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//! Native Rust types for data carried in [`crate::gateway_event::GatewayCommand`] variants.
//!
//! These are domain types; conversion to protobuf wire types happens at the
//! serialization boundary (gateway manager) via `From` impls in `defguard_proto`.

/// A WireGuard peer entry to be configured on a gateway.
#[derive(Clone, Debug, PartialEq)]
pub struct WireguardPeer {
pub pubkey: String,
pub allowed_ips: Vec<String>,
pub preshared_key: Option<String>,
pub keepalive_interval: Option<u32>,
}

/// Default firewall action applied to traffic that does not match any rule.
#[derive(Clone, Debug, Default, PartialEq)]
pub enum FirewallPolicy {
#[default]
Unspecified,
Allow,
Deny,
}

/// IP protocol version a firewall rule applies to.
#[derive(Clone, Debug, Default, PartialEq)]
pub enum IpVersion {
#[default]
Unspecified,
Ipv4,
Ipv6,
}

/// Network protocol matched by a firewall rule.
#[derive(Clone, Debug, PartialEq)]
pub enum Protocol {
Unspecified,
Icmp,
Tcp,
Udp,
}

impl From<i32> for Protocol {
fn from(v: i32) -> Self {
match v {
1 => Self::Icmp,
6 => Self::Tcp,
17 => Self::Udp,
_ => Self::Unspecified,
}
}
}

/// An inclusive range of IP addresses.
#[derive(Clone, Debug, PartialEq)]
pub struct IpRange {
pub start: String,
pub end: String,
}

/// An IP address, range, or subnet.
#[derive(Clone, Debug, PartialEq)]
pub enum IpAddress {
/// Single IP address string (e.g. `"10.0.0.1"`).
Ip(String),
/// Inclusive IP range.
IpRange(IpRange),
/// IP subnet in CIDR notation (e.g. `"10.0.0.0/24"`).
IpSubnet(String),
}

/// An inclusive range of port numbers.
#[derive(Clone, Debug, PartialEq)]
pub struct PortRange {
pub start: u32,
pub end: u32,
}

/// A single port or an inclusive port range matched by a firewall rule.
#[derive(Clone, Debug, PartialEq)]
pub enum Port {
Single(u32),
Range(PortRange),
}

/// A single ACL-derived firewall rule to be enforced on a gateway.
#[derive(Clone, Debug, PartialEq)]
pub struct FirewallRule {
pub id: i64,
pub source_addrs: Vec<IpAddress>,
pub destination_addrs: Vec<IpAddress>,
pub destination_ports: Vec<Port>,
pub protocols: Vec<Protocol>,
pub verdict: FirewallPolicy,
pub comment: Option<String>,
pub ip_version: IpVersion,
}

/// Source NAT binding that rewrites the source IP of matching VPN traffic.
#[derive(Clone, Debug, PartialEq)]
pub struct SnatBinding {
pub id: i64,
pub source_addrs: Vec<IpAddress>,
pub public_ip: String,
pub comment: Option<String>,
}

/// Full firewall configuration to be applied to a gateway location.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FirewallConfig {
pub default_policy: FirewallPolicy,
pub rules: Vec<FirewallRule>,
pub snat_bindings: Vec<SnatBinding>,
}
2 changes: 2 additions & 0 deletions crates/defguard_common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ pub mod auth;
pub mod config;
pub mod csv;
pub mod db;
pub mod gateway_event;
pub mod gateway_types;
pub mod globals;
pub mod hex;
pub mod messages;
Expand Down
24 changes: 12 additions & 12 deletions crates/defguard_core/src/appstate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
db::{AppEvent, WebHook},
error::WebError,
events::ApiEvent,
grpc::{GatewayEvent, send_multiple_wireguard_events, send_wireguard_event},
grpc::{GatewayCommand, send_gateway_command, send_multiple_gateway_commands},
version::IncompatibleComponents,
};

Expand All @@ -29,7 +29,7 @@ const X_DEFGUARD_EVENT: &str = "x-defguard-event";
pub struct AppState {
pub pool: PgPool,
tx: UnboundedSender<AppEvent>,
pub wireguard_tx: Sender<GatewayEvent>,
pub gateway_tx: Sender<GatewayCommand>,
pub web_reload_tx: tokio::sync::broadcast::Sender<()>,
pub failed_logins: Arc<Mutex<FailedLoginMap>>,
key: Key,
Expand Down Expand Up @@ -86,16 +86,16 @@ impl AppState {
}
}

/// Sends given `GatewayEvent` to be handled by gateway GRPC server.
/// Convenience wrapper around [`send_wireguard_event`]
pub fn send_wireguard_event(&self, event: GatewayEvent) {
send_wireguard_event(event, &self.wireguard_tx);
/// Sends given `GatewayCommand` to be handled by gateway manager service.
/// Convenience wrapper around [`send_gateway_command`]
pub fn send_gateway_command(&self, command: GatewayCommand) {
send_gateway_command(command, &self.gateway_tx);
}

/// Sends multiple events to be handled by gateway GRPC server.
/// Convenience wrapper around [`send_multiple_wireguard_events`]
pub fn send_multiple_wireguard_events(&self, events: Vec<GatewayEvent>) {
send_multiple_wireguard_events(events, &self.wireguard_tx);
/// Sends multiple commands to be handled by gateway manager service.
/// Convenience wrapper around [`send_multiple_gateway_commands`]
pub fn send_multiple_gateway_commands(&self, commands: Vec<GatewayCommand>) {
send_multiple_gateway_commands(commands, &self.gateway_tx);
}

/// Sends event to the main event router
Expand All @@ -118,7 +118,7 @@ impl AppState {
pool: PgPool,
tx: UnboundedSender<AppEvent>,
rx: UnboundedReceiver<AppEvent>,
wireguard_tx: Sender<GatewayEvent>,
gateway_tx: Sender<GatewayCommand>,
web_reload_tx: tokio::sync::broadcast::Sender<()>,
key: Key,
failed_logins: Arc<Mutex<FailedLoginMap>>,
Expand All @@ -132,7 +132,7 @@ impl AppState {
Self {
pool,
tx,
wireguard_tx,
gateway_tx,
web_reload_tx,
failed_logins,
key,
Expand Down
10 changes: 5 additions & 5 deletions crates/defguard_core/src/enterprise/db/models/acl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use crate::{
ApiAclRule, EditAclRule, alias::EditAclAlias, destination::EditAclDestination,
},
},
grpc::GatewayEvent,
grpc::GatewayCommand,
};

#[derive(Debug, Error)]
Expand Down Expand Up @@ -547,15 +547,15 @@ impl AclRule {
match try_get_location_firewall_config(&location, &mut transaction).await? {
Some(firewall_config) => {
debug!("Sending firewall update event for location {location}");
appstate.send_wireguard_event(GatewayEvent::FirewallConfigChanged(
appstate.send_gateway_command(GatewayCommand::FirewallConfigChanged(
location.id,
firewall_config,
));
}
None => {
debug!(
"No firewall config generated for location {location}. Not sending a \
gateway event"
gateway command"
);
}
}
Expand Down Expand Up @@ -1828,15 +1828,15 @@ impl AclAlias {
match try_get_location_firewall_config(&location, &mut transaction).await? {
Some(firewall_config) => {
debug!("Sending firewall update event for location {location}");
appstate.send_wireguard_event(GatewayEvent::FirewallConfigChanged(
appstate.send_gateway_command(GatewayCommand::FirewallConfigChanged(
location.id,
firewall_config,
));
}
None => {
debug!(
"No firewall config generated for location {location}. Not sending a \
gateway event"
gateway command"
);
}
}
Expand Down
Loading
Loading