diff --git a/crates/defguard/src/main.rs b/crates/defguard/src/main.rs index 246f1c0ea6..1be7cd7b87 100644 --- a/crates/defguard/src/main.rs +++ b/crates/defguard/src/main.rs @@ -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, @@ -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 { @@ -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}"); @@ -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::(100); let proxy_secret_key = settings.secret_key_required()?; let proxy_manager = ProxyManager::new( diff --git a/crates/defguard_common/src/config.rs b/crates/defguard_common/src/config.rs index 13a5cea975..c496b21b90 100644 --- a/crates/defguard_common/src/config.rs +++ b/crates/defguard_common/src/config.rs @@ -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)] @@ -215,6 +217,12 @@ pub struct InitVpnLocationArgs { pub id: Option, } +#[derive(Args, Debug, Clone)] +pub struct GatewayConfigArgs { + #[arg(long)] + pub location_id: i64, +} + impl DefGuardConfig { #[must_use] pub fn new() -> Self { diff --git a/crates/defguard_core/src/lib.rs b/crates/defguard_core/src/lib.rs index b9f3a7fb31..252dc6c471 100644 --- a/crates/defguard_core/src/lib.rs +++ b/crates/defguard_core/src/lib.rs @@ -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::{ @@ -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; @@ -76,6 +77,7 @@ use crate::{ auth::failed_login::FailedLoginMap, db::AppEvent, enterprise::{ + firewall::try_get_location_firewall_config, handlers::{ acl::{ alias::{ @@ -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, }; @@ -929,6 +933,46 @@ pub async fn init_vpn_location( Ok(token) } +pub async fn gateway_config( + pool: &PgPool, + args: &GatewayConfigArgs, +) -> Result { + 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(); + + Ok(config) +} + pub fn is_valid_phone_number(number: &str) -> bool { PHONE_NUMBER_REGEX.is_match(number) } diff --git a/crates/defguard_gateway_manager/src/handler.rs b/crates/defguard_gateway_manager/src/handler.rs index 86e71964a9..d971c4af2a 100644 --- a/crates/defguard_gateway_manager/src/handler.rs +++ b/crates/defguard_gateway_manager/src/handler.rs @@ -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, @@ -1000,23 +1000,6 @@ fn try_protos_into_stats_message( )) } -fn gen_config( - network: &WireguardNetwork, - peers: Vec, - maybe_firewall_config: Option, -) -> 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}; @@ -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 { @@ -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(), @@ -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()); diff --git a/crates/defguard_proto/src/lib.rs b/crates/defguard_proto/src/lib.rs index aa70796b42..ae1a525952 100644 --- a/crates/defguard_proto/src/lib.rs +++ b/crates/defguard_proto/src/lib.rs @@ -20,7 +20,7 @@ use defguard_common::{ db::{ Id, models::{ - Device, DeviceConfig, User, + Device, DeviceConfig, User, WireguardNetwork, vpn_client_session::VpnClientMfaMethod, wireguard::{LocationMfaMode, ServiceLocationMode}, }, @@ -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 { @@ -152,3 +157,22 @@ impl From for proxy::ServiceLocationMode { } } } + +impl Configuration { + pub fn new( + location: &WireguardNetwork, + peers: Vec, + maybe_firewall_config: Option, + ) -> 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, + } + } +} diff --git a/migrations/20251125072923_network_gateways.down.sql b/migrations/20251125072923_network_gateways.down.sql deleted file mode 100644 index 5e727c02c8..0000000000 --- a/migrations/20251125072923_network_gateways.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -DROP TRIGGER gateway ON gateway; -DROP FUNCTION row_change(); -DROP TABLE gateway; diff --git a/migrations/20251125072923_network_gateways.up.sql b/migrations/20251125072923_network_gateways.up.sql deleted file mode 100644 index 3db149fd6e..0000000000 --- a/migrations/20251125072923_network_gateways.up.sql +++ /dev/null @@ -1,20 +0,0 @@ -CREATE TABLE gateway ( - id bigserial PRIMARY KEY, - network_id bigint NOT NULL, - url text NOT NULL, - hostname text NULL, - connected_at timestamp without time zone NULL, - disconnected_at timestamp without time zone NULL, - FOREIGN KEY(network_id) REFERENCES wireguard_network(id) -); -CREATE FUNCTION row_change() RETURNS trigger AS $$ -BEGIN - PERFORM pg_notify(TG_TABLE_NAME || '_change', - json_build_object('operation', TG_OP, 'old', row_to_json(OLD), 'new', row_to_json(NEW))::text - ); - RETURN NULL; -END; -$$ LANGUAGE plpgsql; -CREATE TRIGGER gateway - AFTER INSERT OR UPDATE OR DELETE ON gateway - FOR ROW EXECUTE FUNCTION row_change(); diff --git a/migrations/20251218140442_[2.0.0]_core_ca.down.sql b/migrations/20251218140442_[2.0.0]_core_ca.down.sql deleted file mode 100644 index 22ee3452f2..0000000000 --- a/migrations/20251218140442_[2.0.0]_core_ca.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE settings - DROP COLUMN ca_key_der, - DROP COLUMN ca_cert_der; diff --git a/migrations/20251218140442_[2.0.0]_core_ca.up.sql b/migrations/20251218140442_[2.0.0]_core_ca.up.sql deleted file mode 100644 index 3db71200ff..0000000000 --- a/migrations/20251218140442_[2.0.0]_core_ca.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE settings - ADD COLUMN ca_key_der BYTEA DEFAULT NULL, - ADD COLUMN ca_cert_der BYTEA DEFAULT NULL; diff --git a/migrations/20251218140442_[2.0.0]_initial.down.sql b/migrations/20251218140442_[2.0.0]_initial.down.sql new file mode 100644 index 0000000000..744d1d59f3 --- /dev/null +++ b/migrations/20251218140442_[2.0.0]_initial.down.sql @@ -0,0 +1,137 @@ +-- Drop wizard state introduced in 2.0.0. +DROP TABLE wizard; +DROP TYPE active_wizard; + +-- Remove database-backed mail templates. +DROP TABLE mail_context; + +DROP INDEX api_token_token_hash_idx; + +DROP INDEX device_user_id_device_type_id_idx; + +DROP INDEX wireguard_network_device_network_id_device_id_idx; + +UPDATE activity_log_event +SET ip = '0.0.0.0'::inet +WHERE ip IS NULL; + +ALTER TABLE activity_log_event ALTER COLUMN ip SET NOT NULL; + +ALTER TABLE wireguard_network_device + ADD COLUMN preshared_key text NULL, + ADD COLUMN is_authorized bool NOT NULL DEFAULT false, + ADD COLUMN authorized_at timestamp without time zone NULL; + +-- Rollback is lossy: only preshared_key is repopulated from an active +-- session with a non-null preshared_key per (device_id, location_id); +-- is_authorized and authorized_at are recreated with default/NULL values and +-- are not reconstructed. +UPDATE wireguard_network_device AS network_device +SET preshared_key = latest_active_session.preshared_key +FROM ( + SELECT DISTINCT ON (session.device_id, session.location_id) + session.device_id, + session.location_id, + session.preshared_key + FROM vpn_client_session AS session + WHERE session.state IN ('new', 'connected') + AND session.preshared_key IS NOT NULL + ORDER BY session.device_id, session.location_id, session.created_at DESC, session.id DESC +) AS latest_active_session +WHERE network_device.device_id = latest_active_session.device_id + AND network_device.wireguard_network_id = latest_active_session.location_id; + +-- Remove VPN session tracking and proxy management structures. +DROP TABLE vpn_session_stats; +DROP TABLE vpn_client_session; +DROP TYPE vpn_client_mfa_method; +DROP TYPE vpn_client_session_state; + +DROP TABLE proxy; + +DROP TRIGGER gateway ON gateway; +DROP FUNCTION row_change(); +DROP TABLE gateway; + +-- Restore ACL naming and flags from before 2.0.0. +ALTER TABLE aclrule RENAME COLUMN addresses TO destination; +ALTER TABLE aclrule RENAME COLUMN all_locations TO all_networks; + +ALTER TABLE aclalias RENAME COLUMN addresses TO destination; + +ALTER TABLE aclrule + DROP COLUMN any_address, + DROP COLUMN any_port, + DROP COLUMN any_protocol, + DROP COLUMN use_manual_destination_settings, + DROP COLUMN allow_all_groups, + DROP COLUMN deny_all_groups; + +ALTER TABLE aclalias + DROP COLUMN any_address, + DROP COLUMN any_port, + DROP COLUMN any_protocol; + +-- Remove 2.0.0 OpenID provider extensions. +ALTER TABLE openidprovider DROP COLUMN kind; +DROP TYPE openid_provider_kind; + +-- Remove 2.0.0 WireGuard network defaults. +ALTER TABLE wireguard_network + DROP COLUMN mtu, + DROP COLUMN fwmark, + DROP COLUMN allow_all_groups; + +-- Remove 2.0.0 setup and settings columns. +ALTER TABLE settings DROP CONSTRAINT fk_default_admin; + +ALTER TABLE settings + DROP COLUMN ca_key_der, + DROP COLUMN ca_cert_der, + DROP COLUMN ca_expiry, + DROP COLUMN defguard_url, + DROP COLUMN default_admin_group_name, + DROP COLUMN authentication_period_days, + DROP COLUMN mfa_code_timeout_seconds, + DROP COLUMN public_proxy_url, + DROP COLUMN default_admin_id, + DROP COLUMN secret_key, + DROP COLUMN openid_signing_key, + DROP COLUMN enable_stats_purge, + DROP COLUMN stats_purge_frequency_hours, + DROP COLUMN stats_purge_threshold_days, + DROP COLUMN enrollment_token_timeout_hours, + DROP COLUMN enrollment_send_welcome_email, + DROP COLUMN password_reset_token_timeout_hours, + DROP COLUMN enrollment_session_timeout_minutes, + DROP COLUMN password_reset_session_timeout_minutes; + +-- Restore the legacy peer stats structures used before 2.0.0. +CREATE TABLE wireguard_peer_stats ( + id bigserial PRIMARY KEY, + device_id bigint NOT NULL, + collected_at timestamp without time zone NOT NULL DEFAULT current_timestamp, + network bigint NOT NULL, + endpoint text, + upload bigint NOT NULL, + download bigint NOT NULL, + latest_handshake timestamp without time zone NOT NULL, + allowed_ips text, + FOREIGN KEY (device_id) REFERENCES device(id) ON DELETE CASCADE +); + +CREATE INDEX peer_stats_device_id_collected_at + ON wireguard_peer_stats (device_id, network, collected_at DESC, latest_handshake DESC NULLS LAST); + +CREATE OR REPLACE VIEW wireguard_peer_stats_view AS + SELECT + device_id, + greatest(upload - lag(upload, 1, upload) OVER (PARTITION BY device_id, network ORDER BY collected_at), 0) upload, + greatest(download - lag(download, 1, download) OVER (PARTITION BY device_id, network ORDER BY collected_at), 0) download, + latest_handshake - (lag(latest_handshake, 1, latest_handshake) OVER (PARTITION BY device_id, network ORDER BY collected_at)) latest_handshake_diff, + latest_handshake, + collected_at, + network, + endpoint, + allowed_ips + FROM wireguard_peer_stats; diff --git a/migrations/20251218140442_[2.0.0]_initial.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql new file mode 100644 index 0000000000..c78136f2aa --- /dev/null +++ b/migrations/20251218140442_[2.0.0]_initial.up.sql @@ -0,0 +1,374 @@ +-- Settings and network defaults introduced for the 2.0.0 setup flow. +ALTER TABLE settings + ADD COLUMN ca_key_der bytea DEFAULT NULL, + ADD COLUMN ca_cert_der bytea DEFAULT NULL, + ADD COLUMN ca_expiry timestamp without time zone NULL, + ADD COLUMN defguard_url text NOT NULL DEFAULT 'http://localhost:8000', + ADD COLUMN default_admin_group_name text NOT NULL DEFAULT 'admin', + ADD COLUMN authentication_period_days integer NOT NULL DEFAULT 7, + ADD COLUMN mfa_code_timeout_seconds integer NOT NULL DEFAULT 60, + ADD COLUMN public_proxy_url text NOT NULL DEFAULT 'http://localhost:8080', + ADD COLUMN default_admin_id bigint NULL, + ADD COLUMN secret_key text, + ADD COLUMN openid_signing_key text, + ADD COLUMN enable_stats_purge boolean NOT NULL DEFAULT true, + ADD COLUMN stats_purge_frequency_hours int4 NOT NULL DEFAULT 24, + ADD COLUMN stats_purge_threshold_days int4 NOT NULL DEFAULT 30, + ADD COLUMN enrollment_token_timeout_hours int4 NOT NULL DEFAULT 24, + ADD COLUMN enrollment_send_welcome_email boolean NOT NULL DEFAULT true, + ADD COLUMN password_reset_token_timeout_hours int4 NOT NULL DEFAULT 24, + ADD COLUMN enrollment_session_timeout_minutes int4 NOT NULL DEFAULT 10, + ADD COLUMN password_reset_session_timeout_minutes int4 NOT NULL DEFAULT 10; + +ALTER TABLE activity_log_event ALTER COLUMN ip DROP NOT NULL; + +ALTER TABLE settings + ADD CONSTRAINT fk_default_admin + FOREIGN KEY (default_admin_id) REFERENCES "user"(id) + ON DELETE SET NULL; + +ALTER TABLE wireguard_network + ADD COLUMN mtu integer NOT NULL DEFAULT 1420, + ADD COLUMN fwmark bigint NOT NULL DEFAULT 0, + ADD COLUMN allow_all_groups boolean NOT NULL DEFAULT false; + +-- External OpenID providers gain a provider kind discriminator. +CREATE TYPE openid_provider_kind AS ENUM ( + 'Custom', + 'Google', + 'Microsoft', + 'Okta', + 'JumpCloud', + 'Zitadel' +); + +ALTER TABLE openidprovider + ADD COLUMN kind openid_provider_kind NOT NULL DEFAULT 'Custom'::openid_provider_kind; + +-- ACL rules and aliases move to the new "any_*" flags and addresses naming. +ALTER TABLE aclalias + ADD COLUMN any_address boolean NOT NULL DEFAULT false, + ADD COLUMN any_port boolean NOT NULL DEFAULT false, + ADD COLUMN any_protocol boolean NOT NULL DEFAULT false; + +-- Backfill explicit any_* flags so migrated 1.6 aliases keep treating empty +-- destination, port, and protocol inputs as "match any". +WITH alias_destination_input_state AS ( + SELECT + alias.id, + COALESCE(cardinality(alias.destination), 0) = 0 AS has_no_destination_addresses, + NOT EXISTS ( + SELECT 1 + FROM aclaliasdestinationrange AS alias_range + WHERE alias_range.alias_id = alias.id + ) AS has_no_destination_ranges, + COALESCE(cardinality(alias.ports), 0) = 0 AS has_no_ports, + COALESCE(cardinality(alias.protocols), 0) = 0 AS has_no_protocols + FROM aclalias AS alias +) +UPDATE aclalias AS alias +SET + any_address = ( + state.has_no_destination_addresses + AND state.has_no_destination_ranges + ), + any_port = state.has_no_ports, + any_protocol = state.has_no_protocols +FROM alias_destination_input_state AS state +WHERE state.id = alias.id; + +ALTER TABLE aclalias RENAME COLUMN destination TO addresses; + +ALTER TABLE aclrule + ADD COLUMN any_address boolean NOT NULL DEFAULT false, + ADD COLUMN any_port boolean NOT NULL DEFAULT false, + ADD COLUMN any_protocol boolean NOT NULL DEFAULT false, + ADD COLUMN use_manual_destination_settings boolean NOT NULL DEFAULT true, + ADD COLUMN allow_all_groups boolean NOT NULL DEFAULT false, + ADD COLUMN deny_all_groups boolean NOT NULL DEFAULT false; + +-- Preserve migrated 1.6 rule behavior by separating destination aliases from +-- component aliases: destination aliases define alias-driven destinations, +-- while component aliases only count when they provide concrete inputs. +WITH rule_alias_destination_input_state AS ( + SELECT + rule_alias.rule_id, + BOOL_OR(alias.kind = 'destination') AS has_destination_aliases, + BOOL_OR(alias.kind = 'component' AND NOT alias.any_address) AS has_component_alias_addresses, + BOOL_OR(alias.kind = 'component' AND NOT alias.any_port) AS has_component_alias_ports, + BOOL_OR(alias.kind = 'component' AND NOT alias.any_protocol) AS has_component_alias_protocols + FROM aclrulealias AS rule_alias + JOIN aclalias AS alias ON alias.id = rule_alias.alias_id + GROUP BY rule_alias.rule_id +), +-- Rule-local destination inputs must still be checked after alias detection so +-- legacy rules keep manual settings whenever they stored addresses, ranges, +-- ports, or protocols directly on the rule. +rule_destination_input_state AS ( + SELECT + rule.id, + COALESCE(cardinality(rule.destination), 0) = 0 AS has_no_destination_addresses, + NOT EXISTS ( + SELECT 1 + FROM aclruledestinationrange AS rule_range + WHERE rule_range.rule_id = rule.id + ) AS has_no_destination_ranges, + COALESCE(cardinality(rule.ports), 0) = 0 AS has_no_ports, + COALESCE(cardinality(rule.protocols), 0) = 0 AS has_no_protocols, + COALESCE(alias_state.has_destination_aliases, false) AS has_destination_aliases, + NOT COALESCE(alias_state.has_component_alias_addresses, false) AS has_no_component_alias_addresses, + NOT COALESCE(alias_state.has_component_alias_ports, false) AS has_no_component_alias_ports, + NOT COALESCE(alias_state.has_component_alias_protocols, false) AS has_no_component_alias_protocols + FROM aclrule AS rule + LEFT JOIN rule_alias_destination_input_state AS alias_state ON alias_state.rule_id = rule.id +) +UPDATE aclrule AS rule +SET + any_address = ( + state.has_no_destination_addresses + AND state.has_no_destination_ranges + AND state.has_no_component_alias_addresses + ), + any_port = ( + state.has_no_ports + AND state.has_no_component_alias_ports + ), + any_protocol = ( + state.has_no_protocols + AND state.has_no_component_alias_protocols + ), + -- Only switch migrated 1.6 rules away from manual destination settings + -- when destination aliases were the sole source of destination inputs. + use_manual_destination_settings = NOT ( + state.has_no_destination_addresses + AND state.has_no_destination_ranges + AND state.has_no_ports + AND state.has_no_protocols + AND state.has_no_component_alias_addresses + AND state.has_no_component_alias_ports + AND state.has_no_component_alias_protocols + AND state.has_destination_aliases + ), + allow_all_groups = false, + deny_all_groups = false +FROM rule_destination_input_state AS state +WHERE state.id = rule.id; + +-- Rename after backfills because these queries must read the legacy 1.6 column +-- names while deriving the new flags and destination-mode settings. +ALTER TABLE aclrule RENAME COLUMN destination TO addresses; +ALTER TABLE aclrule RENAME COLUMN all_networks TO all_locations; + +-- Gateway and proxy management are introduced in their final 2.0.0 form. +CREATE TABLE gateway ( + id bigserial PRIMARY KEY, + location_id bigint NOT NULL, + connected_at timestamp without time zone NULL, + disconnected_at timestamp without time zone NULL, + certificate_expiry timestamp without time zone NULL, + version text, + name text NOT NULL, + certificate text, + address text NOT NULL DEFAULT '127.0.0.1', + port integer NOT NULL DEFAULT 50051, + modified_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + enabled boolean NOT NULL DEFAULT true, + modified_by text NOT NULL, + CONSTRAINT gateway_network_id_fkey + FOREIGN KEY (location_id) REFERENCES wireguard_network(id) ON DELETE CASCADE +); + +CREATE FUNCTION row_change() RETURNS trigger AS $$ +BEGIN + PERFORM pg_notify(TG_TABLE_NAME || '_change', + json_build_object('operation', TG_OP, 'old', row_to_json(OLD), 'new', row_to_json(NEW))::text + ); + RETURN NULL; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER gateway + AFTER INSERT OR UPDATE OR DELETE ON gateway + FOR ROW EXECUTE FUNCTION row_change(); + +CREATE TABLE proxy ( + id bigserial PRIMARY KEY, + name text NOT NULL, + address text NOT NULL, + port integer NOT NULL, + connected_at timestamp without time zone NULL, + disconnected_at timestamp without time zone NULL, + certificate_expiry timestamp without time zone NULL, + version text, + modified_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + certificate text, + modified_by text NOT NULL, + enabled boolean NOT NULL DEFAULT true, + CONSTRAINT unique_address_port UNIQUE (address, port) +); + +-- VPN client session tracking replaces the legacy peer stats model. +CREATE TYPE vpn_client_session_state AS ENUM ( + 'new', + 'connected', + 'disconnected' +); + +CREATE TYPE vpn_client_mfa_method AS ENUM ( + 'totp', + 'email', + 'oidc', + 'biometric', + 'mobileapprove' +); + +CREATE TABLE vpn_client_session ( + id bigserial PRIMARY KEY, + location_id bigint NOT NULL, + user_id bigint NOT NULL, + device_id bigint NOT NULL, + created_at timestamp without time zone NOT NULL DEFAULT current_timestamp, + connected_at timestamp without time zone NULL, + disconnected_at timestamp without time zone NULL, + mfa_method vpn_client_mfa_method NULL, + preshared_key text NULL, + state vpn_client_session_state NOT NULL DEFAULT 'new', + FOREIGN KEY (location_id) REFERENCES wireguard_network(id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE, + FOREIGN KEY (device_id) REFERENCES device(id) ON DELETE CASCADE +); +CREATE INDEX idx_vpn_client_session_user_id ON vpn_client_session(user_id); +CREATE INDEX idx_vpn_client_session_device_id ON vpn_client_session(device_id); +CREATE INDEX idx_vpn_client_session_location_id ON vpn_client_session(location_id); +CREATE INDEX idx_vpn_client_session_state ON vpn_client_session(state); +CREATE INDEX idx_vpn_client_session_created_at ON vpn_client_session(created_at DESC); +CREATE INDEX idx_vpn_client_session_connected_at ON vpn_client_session(connected_at DESC); +CREATE UNIQUE INDEX vpn_client_session_active_location_device_unique + ON vpn_client_session(location_id, device_id) + WHERE state IN ('new', 'connected'); + +ALTER TABLE wireguard_network_device + DROP COLUMN preshared_key, + DROP COLUMN is_authorized, + DROP COLUMN authorized_at; + +CREATE INDEX wireguard_network_device_network_id_device_id_idx + ON wireguard_network_device (wireguard_network_id, device_id); + +CREATE INDEX device_user_id_device_type_id_idx + ON device (user_id, device_type, id); + +CREATE TABLE vpn_session_stats ( + id bigserial PRIMARY KEY, + session_id bigint NOT NULL, + gateway_id bigint NOT NULL, + collected_at timestamp without time zone NOT NULL, + latest_handshake timestamp without time zone NOT NULL, + endpoint text NOT NULL, + total_upload bigint NOT NULL, + total_download bigint NOT NULL, + upload_diff bigint NOT NULL, + download_diff bigint NOT NULL, + FOREIGN KEY (session_id) REFERENCES vpn_client_session(id) ON DELETE CASCADE, + FOREIGN KEY (gateway_id) REFERENCES gateway(id) ON DELETE CASCADE +); +CREATE INDEX idx_vpn_session_stats_session_id ON vpn_session_stats(session_id); +CREATE INDEX idx_vpn_session_stats_gateway_id ON vpn_session_stats(gateway_id); +CREATE INDEX idx_vpn_session_stats_collected_at ON vpn_session_stats(collected_at DESC); +CREATE INDEX idx_vpn_session_stats_latest_handshake ON vpn_session_stats(latest_handshake DESC); +CREATE INDEX idx_vpn_session_stats_session_collected ON vpn_session_stats(session_id, collected_at DESC); +CREATE INDEX idx_vpn_session_stats_session_latest_handshake + ON vpn_session_stats(session_id, latest_handshake DESC); + +-- Remove legacy peer stats structures superseded by VPN session tracking. +DROP VIEW wireguard_peer_stats_view; +DROP TABLE wireguard_peer_stats; + +-- Mail template content is moved to the database. +CREATE TABLE mail_context ( + template text NOT NULL, + section text NOT NULL, + language_tag text NOT NULL, + text text NOT NULL, + enabled bool NOT NULL DEFAULT true +); + +INSERT INTO mail_context (template, section, language_tag, text) VALUES + ('desktop-start', 'title', 'en_US', 'You''re receiving this email to configure a new desktop client.'), + ('desktop-start', 'subtitle', 'en_US', 'Please paste this URL and token in your desktop client:'), + ('desktop-start', 'label_url', 'en_US', 'URL'), + ('desktop-start', 'label_token', 'en_US', 'Token'), + ('desktop-start', 'configure', 'en_US', 'Configure your desktop client'), + ('desktop-start', 'click', 'en_US', 'Click the button or use link below'), + ('new-account', 'title', 'en_US', 'New account has been created for you'), + ('new-account', 'subtitle', 'en_US', 'To start the enrollment process, please use credentials below.'), + ('new-account', 'download', 'en_US', 'Download the official Defguard desktop client for your system.'), + ('new-account', 'after_install', 'en_US', 'After installation, please add a Defguard instance by entering:'), + ('new-account', 'label_url', 'en_US', 'URL'), + ('new-account', 'label_token', 'en_US', 'Token'), + ('new-account', 'token_info', 'en_US', 'The token is valid for 24 hours. Once the enrollment process starts, you have 10 minutes to complete it.'), + ('new-account', 'label_enroll', 'en_US', 'Enroll with desktop client'), + ('new-account', 'label_mobile', 'en_US', 'Mobile application'), + ('new-account', 'scan_qr', 'en_US', 'Scan QR code below to activate Defguard mobile application.'), + ('new-account', 'mobile_install', 'en_US', 'If you haven''t installed the mobile app, click one of the buttons below.'), + ('new-account', 'download_google', 'en_US', 'Download from Google Play'), + ('new-account', 'download_apple', 'en_US', 'Download from Apple Store'), + ('new-device', 'title', 'en_US', 'A new device has been added to your account:'), + ('new-device', 'label_device', 'en_US', 'Device name'), + ('new-device', 'label_pubkey', 'en_US', 'Public key'), + ('mfa-code', 'title', 'en_US', 'Hello,'), + ('mfa-code', 'subtitle', 'en_US', 'It seems like you are trying to login to Defguard. Here is the code you need to access your account.'), + ('mfa-code', 'code_is_valid', 'en_US', 'The code is valid for 1 minute'), + ('user-import-blocked', 'title', 'en_US', 'User import blocked'), + ('user-import-blocked', 'notification_text', 'en_US', 'Import of an external user was blocked because it would exceed your current license capacity.'), + ('mfa-activation', 'title', 'en_US', 'Hello,'), + ('mfa-activation', 'subtitle', 'en_US', 'You are activating Multi-Factor Authentication using email verification codes.'), + ('mfa-activation', 'code_is_valid', 'en_US', 'The code is valid for:'), + ('enrollment-admin-notification', 'title', 'en_US', 'Dear,'), + ('enrollment-admin-notification', 'message', 'en_US', 'just completed their enrollment process.'), + ('enrollment-admin-notification', 'goodday', 'en_US', 'Have a good day!'), + ('gateway-disconnect', 'title', 'en_US', 'Defguard Gateway has just disconnected.'), + ('gateway-disconnect', 'subtitle', 'en_US', 'Please login to your gateway server and see the logs.'), + ('gateway-disconnect', 'gateway_label', 'en_US', 'Gateway name:'), + ('gateway-disconnect', 'ip_address_label', 'en_US', 'Gateway IP address:'), + ('gateway-disconnect', 'location_label', 'en_US', 'VPN location:'), + ('gateway-reconnect', 'title', 'en_US', 'Defguard Gateway has just reconnected.'), + ('gateway-reconnect', 'gateway_label', 'en_US', 'Gateway name:'), + ('gateway-reconnect', 'ip_address_label', 'en_US', 'Gateway IP address:'), + ('gateway-reconnect', 'location_label', 'en_US', 'VPN location:'), + ('mfa-configured', 'title', 'en_US', 'Hello,'), + ('mfa-configured', 'subtitle', 'en_US', 'A Multi-Factor Authentication (MFA) has been activated in your account.'), + ('mfa-configured', 'mfa_method_label', 'en_US', 'MFA method:'), + ('new-device-login', 'title', 'en_US', 'Your account was just logged into from a new device.'), + ('new-device-login', 'label_device', 'en_US', 'Device name:'), + ('new-device-login', 'label_date', 'en_US', 'Date:'), + ('new-device-oidc-login', 'title', 'en_US', 'Your account was just logged into a system using OpenID Connect authorization'), + ('new-device-oidc-login', 'subtitle', 'en_US', 'You can deauthorize all applications that have access to your account from the web vault under (My Profile > Apps).'), + ('new-device-oidc-login', 'label_profile', 'en_US', 'Profile URL:'), + ('new-device-oidc-login', 'label_oauth2client', 'en_US', 'System name:'), + ('password-reset', 'title', 'en_US', 'Password reset'), + ('password-reset', 'subtitle', 'en_US', 'If you wish to reset your password, please copy and paste the following URL in your browser:'), + ('password-reset-done', 'title', 'en_US', 'Password reset'), + ('password-reset-done', 'subtitle', 'en_US', 'Your password has been successfully changed.'), + ('test', 'title', 'en_US', 'This is test email from Defguard system.'), + ('test', 'subtitle', 'en_US', 'If you received it, your SMTP configuration is correct.'), + ('support-data', 'title', 'en_US', 'Support data'), + ('support-data', 'subtitle', 'en_US', 'Support data can be found in the attachment.'); + +CREATE UNIQUE INDEX api_token_token_hash_idx + ON api_token (token_hash); + +-- Wizard state is centralized outside of settings. +CREATE TYPE active_wizard AS ENUM ('none', 'initial', 'auto_adoption', 'migration'); + +CREATE TABLE wizard ( + is_singleton boolean NOT NULL DEFAULT true PRIMARY KEY CHECK (is_singleton), + active_wizard active_wizard NOT NULL DEFAULT 'none', + completed boolean NOT NULL DEFAULT false, + initial_setup_state jsonb, + auto_adoption_state jsonb, + migration_wizard_state jsonb +); + +INSERT INTO wizard (is_singleton, active_wizard, completed, initial_setup_state) +VALUES (TRUE, 'none'::active_wizard, FALSE, jsonb_build_object('step', 'welcome')); diff --git a/migrations/20260107092015_[2.0.0]_mtu_fwmark.down.sql b/migrations/20260107092015_[2.0.0]_mtu_fwmark.down.sql deleted file mode 100644 index 2368ce0c65..0000000000 --- a/migrations/20260107092015_[2.0.0]_mtu_fwmark.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE wireguard_network - DROP COLUMN mtu, - DROP COLUMN fwmark; diff --git a/migrations/20260107092015_[2.0.0]_mtu_fwmark.up.sql b/migrations/20260107092015_[2.0.0]_mtu_fwmark.up.sql deleted file mode 100644 index faf70e0128..0000000000 --- a/migrations/20260107092015_[2.0.0]_mtu_fwmark.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE wireguard_network - ADD COLUMN mtu integer NOT NULL DEFAULT 1420, - ADD COLUMN fwmark bigint NOT NULL DEFAULT 0; diff --git a/migrations/20260113094304_[2.0.0]_gateway_certificates_management.down.sql b/migrations/20260113094304_[2.0.0]_gateway_certificates_management.down.sql deleted file mode 100644 index 2b52bcba79..0000000000 --- a/migrations/20260113094304_[2.0.0]_gateway_certificates_management.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE gateway - DROP COLUMN has_certificate, - DROP COLUMN certificate_expiry; diff --git a/migrations/20260113094304_[2.0.0]_gateway_certificates_management.up.sql b/migrations/20260113094304_[2.0.0]_gateway_certificates_management.up.sql deleted file mode 100644 index 6021175475..0000000000 --- a/migrations/20260113094304_[2.0.0]_gateway_certificates_management.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE gateway - ADD COLUMN has_certificate boolean NOT NULL DEFAULT false, - ADD COLUMN certificate_expiry timestamp without time zone NULL; diff --git a/migrations/20260113114719_[2.0.0]_proxy_management.down.sql b/migrations/20260113114719_[2.0.0]_proxy_management.down.sql deleted file mode 100644 index 06d501493f..0000000000 --- a/migrations/20260113114719_[2.0.0]_proxy_management.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE proxy; diff --git a/migrations/20260113114719_[2.0.0]_proxy_management.up.sql b/migrations/20260113114719_[2.0.0]_proxy_management.up.sql deleted file mode 100644 index c82036abf8..0000000000 --- a/migrations/20260113114719_[2.0.0]_proxy_management.up.sql +++ /dev/null @@ -1,9 +0,0 @@ -CREATE TABLE proxy ( - id bigserial PRIMARY KEY, - name text NOT NULL, - address text NOT NULL, - port integer NOT NULL, - public_address text NOT NULL, - connected_at timestamp without time zone NULL, - disconnected_at timestamp without time zone NULL -); diff --git a/migrations/20260115123545_[2.0.0]_openid_provider_kind.down.sql b/migrations/20260115123545_[2.0.0]_openid_provider_kind.down.sql deleted file mode 100644 index 9451ea998e..0000000000 --- a/migrations/20260115123545_[2.0.0]_openid_provider_kind.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE openidprovider DROP COLUMN kind; -DROP TYPE openid_provider_kind; diff --git a/migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql b/migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql deleted file mode 100644 index 20dee5a954..0000000000 --- a/migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE TYPE openid_provider_kind AS ENUM ( - 'Custom', - 'Google', - 'Microsoft', - 'Okta', - 'JumpCloud', - 'Zitadel' -); - -ALTER TABLE openidprovider ADD COLUMN kind openid_provider_kind NOT NULL DEFAULT 'Custom'::openid_provider_kind; diff --git a/migrations/20260116095450_[2.0.0]_proxy_certificates.down.sql b/migrations/20260116095450_[2.0.0]_proxy_certificates.down.sql deleted file mode 100644 index 01200febf8..0000000000 --- a/migrations/20260116095450_[2.0.0]_proxy_certificates.down.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE proxy DROP COLUMN has_certificate; -ALTER TABLE proxy DROP COLUMN certificate_expiry; -ALTER TABLE proxy DROP CONSTRAINT unique_address_port; -ALTER TABLE settings DROP COLUMN ca_expiry; diff --git a/migrations/20260116095450_[2.0.0]_proxy_certificates.up.sql b/migrations/20260116095450_[2.0.0]_proxy_certificates.up.sql deleted file mode 100644 index de514147d4..0000000000 --- a/migrations/20260116095450_[2.0.0]_proxy_certificates.up.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE proxy ADD COLUMN has_certificate BOOLEAN NOT NULL DEFAULT FALSE; -ALTER TABLE proxy ADD COLUMN certificate_expiry TIMESTAMP WITHOUT TIME ZONE NULL; -ALTER TABLE proxy ADD CONSTRAINT unique_address_port UNIQUE (address, port); -ALTER TABLE settings ADD COLUMN ca_expiry TIMESTAMP WITHOUT TIME ZONE NULL; diff --git a/migrations/20260119121935_[2.0.0]_vpn_client_sessions.down.sql b/migrations/20260119121935_[2.0.0]_vpn_client_sessions.down.sql deleted file mode 100644 index 8e23639f41..0000000000 --- a/migrations/20260119121935_[2.0.0]_vpn_client_sessions.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -DROP TABLE vpn_session_stats; -DROP TABLE vpn_client_session; -DROP TYPE vpn_client_session_state; diff --git a/migrations/20260119121935_[2.0.0]_vpn_client_sessions.up.sql b/migrations/20260119121935_[2.0.0]_vpn_client_sessions.up.sql deleted file mode 100644 index 90ddb3d0fc..0000000000 --- a/migrations/20260119121935_[2.0.0]_vpn_client_sessions.up.sql +++ /dev/null @@ -1,46 +0,0 @@ -CREATE TYPE vpn_client_session_state AS ENUM ( - 'new', - 'connected', - 'disconnected' -); - -CREATE TABLE vpn_client_session ( - id bigserial PRIMARY KEY, - location_id bigint NOT NULL, - user_id bigint NOT NULL, - device_id bigint NOT NULL, - created_at timestamp without time zone NOT NULL DEFAULT current_timestamp, - connected_at timestamp without time zone NULL, - disconnected_at timestamp without time zone NULL, - mfa_mode location_mfa_mode NOT NULL, - state vpn_client_session_state NOT NULL DEFAULT 'new', - FOREIGN KEY (location_id) REFERENCES wireguard_network(id) ON DELETE CASCADE, - FOREIGN KEY(user_id) REFERENCES "user"(id) ON DELETE CASCADE, - FOREIGN KEY (device_id) REFERENCES device(id) ON DELETE CASCADE -); -CREATE INDEX idx_vpn_client_session_user_id ON vpn_client_session(user_id); -CREATE INDEX idx_vpn_client_session_device_id ON vpn_client_session(device_id); -CREATE INDEX idx_vpn_client_session_location_id ON vpn_client_session(location_id); -CREATE INDEX idx_vpn_client_session_state ON vpn_client_session(state); -CREATE INDEX idx_vpn_client_session_created_at ON vpn_client_session(created_at DESC); -CREATE INDEX idx_vpn_client_session_connected_at ON vpn_client_session(connected_at DESC); - -CREATE TABLE vpn_session_stats ( - id bigserial PRIMARY KEY, - session_id bigint NOT NULL, - gateway_id bigint NOT NULL, - collected_at timestamp without time zone NOT NULL, - latest_handshake timestamp without time zone NOT NULL, - endpoint text NOT NULL, - total_upload bigint NOT NULL, - total_download bigint NOT NULL, - upload_diff bigint NOT NULL, - download_diff bigint NOT NULL, - FOREIGN KEY (session_id) REFERENCES vpn_client_session(id) ON DELETE CASCADE, - FOREIGN KEY (gateway_id) REFERENCES gateway(id) ON DELETE CASCADE -); -CREATE INDEX idx_vpn_session_stats_session_id ON vpn_session_stats(session_id); -CREATE INDEX idx_vpn_session_stats_gateway_id ON vpn_session_stats(gateway_id); -CREATE INDEX idx_vpn_session_stats_collected_at ON vpn_session_stats(collected_at DESC); -CREATE INDEX idx_vpn_session_stats_latest_handshake ON vpn_session_stats(latest_handshake DESC); -CREATE INDEX idx_vpn_session_stats_session_collected ON vpn_session_stats(session_id, collected_at DESC); diff --git a/migrations/20260121122631_[2.0.0]_Add_component_versions.down.sql b/migrations/20260121122631_[2.0.0]_Add_component_versions.down.sql deleted file mode 100644 index d45223bf16..0000000000 --- a/migrations/20260121122631_[2.0.0]_Add_component_versions.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE proxy DROP COLUMN version; -ALTER TABLE gateway DROP COLUMN version; diff --git a/migrations/20260121122631_[2.0.0]_Add_component_versions.up.sql b/migrations/20260121122631_[2.0.0]_Add_component_versions.up.sql deleted file mode 100644 index 461804fd0f..0000000000 --- a/migrations/20260121122631_[2.0.0]_Add_component_versions.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE proxy ADD COLUMN version TEXT; -ALTER TABLE gateway ADD COLUMN version TEXT; diff --git a/migrations/20260127085637_[2.0.0]_gateway_common_name.down.sql b/migrations/20260127085637_[2.0.0]_gateway_common_name.down.sql deleted file mode 100644 index 3cde267aba..0000000000 --- a/migrations/20260127085637_[2.0.0]_gateway_common_name.down.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE gateway DROP COLUMN name; diff --git a/migrations/20260127085637_[2.0.0]_gateway_common_name.up.sql b/migrations/20260127085637_[2.0.0]_gateway_common_name.up.sql deleted file mode 100644 index 9d1887dd3a..0000000000 --- a/migrations/20260127085637_[2.0.0]_gateway_common_name.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE gateway ADD COLUMN name TEXT NOT NULL; diff --git a/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql b/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql deleted file mode 100644 index fd8d3a00ac..0000000000 --- a/migrations/20260127094513_[2.0.0]_aclalias_any.down.sql +++ /dev/null @@ -1,15 +0,0 @@ -ALTER TABLE aclrule - DROP COLUMN any_address, - DROP COLUMN any_port, - DROP COLUMN any_protocol, - DROP COLUMN use_manual_destination_settings, - DROP COLUMN allow_all_groups, - DROP COLUMN deny_all_groups; -ALTER TABLE aclrule RENAME COLUMN addresses TO destination; -ALTER TABLE aclrule RENAME COLUMN all_locations TO all_networks; - -ALTER TABLE aclalias - DROP COLUMN any_address, - DROP COLUMN any_port, - DROP COLUMN any_protocol; -ALTER TABLE aclalias RENAME COLUMN addresses TO destination; diff --git a/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql b/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql deleted file mode 100644 index a652c451b6..0000000000 --- a/migrations/20260127094513_[2.0.0]_aclalias_any.up.sql +++ /dev/null @@ -1,31 +0,0 @@ --- add new toggle columns -ALTER TABLE aclalias - ADD COLUMN any_address boolean NOT NULL DEFAULT false, - ADD COLUMN any_port boolean NOT NULL DEFAULT false, - ADD COLUMN any_protocol boolean NOT NULL DEFAULT false; - --- set values for new columns based on existing data -UPDATE aclalias SET - any_address = array_length(destination, 1) IS NULL, - any_port = array_length(ports, 1) IS NULL, - any_protocol = array_length(protocols, 1) IS NULL; - --- rename destination column to avoid confusion -ALTER TABLE aclalias RENAME COLUMN destination TO addresses; - --- do the same for the aclrule table itself -ALTER TABLE aclrule - ADD COLUMN any_address boolean NOT NULL DEFAULT false, - ADD COLUMN any_port boolean NOT NULL DEFAULT false, - ADD COLUMN any_protocol boolean NOT NULL DEFAULT false, - ADD COLUMN use_manual_destination_settings boolean NOT NULL DEFAULT true, - ADD COLUMN allow_all_groups boolean NOT NULL DEFAULT false, - ADD COLUMN deny_all_groups boolean NOT NULL DEFAULT false; - -UPDATE aclrule SET - any_address = array_length(destination, 1) IS NULL, - any_port = array_length(ports, 1) IS NULL, - any_protocol = array_length(protocols, 1) IS NULL; - -ALTER TABLE aclrule RENAME COLUMN destination TO addresses; -ALTER TABLE aclrule RENAME COLUMN all_networks TO all_locations; diff --git a/migrations/20260128082943_[2.0.0]_add_session_mfa_method.down.sql b/migrations/20260128082943_[2.0.0]_add_session_mfa_method.down.sql deleted file mode 100644 index 7bb999b470..0000000000 --- a/migrations/20260128082943_[2.0.0]_add_session_mfa_method.down.sql +++ /dev/null @@ -1,9 +0,0 @@ --- Restore MFA mode column --- This will not restore a correct MFA mode, but it souldn't be an issue outside of development environments -ALTER TABLE vpn_client_session ADD COLUMN mfa_mode location_mfa_mode NOT NULL DEFAULT 'disabled'; - --- Drop MFA method column -ALTER TABLE vpn_client_session DROP COLUMN mfa_method; - --- Drop MFA method enum -DROP TYPE vpn_client_mfa_method; diff --git a/migrations/20260128082943_[2.0.0]_add_session_mfa_method.up.sql b/migrations/20260128082943_[2.0.0]_add_session_mfa_method.up.sql deleted file mode 100644 index 5675edc2a5..0000000000 --- a/migrations/20260128082943_[2.0.0]_add_session_mfa_method.up.sql +++ /dev/null @@ -1,15 +0,0 @@ --- Add enum for MFA methods -CREATE TYPE vpn_client_mfa_method AS ENUM ( - 'totp', - 'email', - 'oidc', - 'biometric', - 'mobileapprove' -); - --- Add MFA method column to VPN client session -ALTER TABLE vpn_client_session ADD COLUMN mfa_method vpn_client_mfa_method NULL; - --- Remove unnecessary MFA type from VPN client session -ALTER TABLE vpn_client_session DROP COLUMN mfa_mode; - diff --git a/migrations/20260128121256_[2.0.0]_initial_setup_wizard.down.sql b/migrations/20260128121256_[2.0.0]_initial_setup_wizard.down.sql deleted file mode 100644 index f01bf61d2d..0000000000 --- a/migrations/20260128121256_[2.0.0]_initial_setup_wizard.down.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE settings - DROP COLUMN initial_setup_completed, - DROP COLUMN defguard_url, - DROP COLUMN default_admin_group_name, - DROP COLUMN authentication_period_days, - DROP COLUMN mfa_code_timeout_seconds; diff --git a/migrations/20260128121256_[2.0.0]_initial_setup_wizard.up.sql b/migrations/20260128121256_[2.0.0]_initial_setup_wizard.up.sql deleted file mode 100644 index d2c4a7a173..0000000000 --- a/migrations/20260128121256_[2.0.0]_initial_setup_wizard.up.sql +++ /dev/null @@ -1,6 +0,0 @@ -ALTER TABLE settings -ADD COLUMN initial_setup_completed BOOLEAN NOT NULL DEFAULT FALSE, -ADD COLUMN defguard_url TEXT NOT NULL DEFAULT 'http://localhost:8000', -ADD COLUMN default_admin_group_name TEXT NOT NULL DEFAULT 'admin', -ADD COLUMN authentication_period_days INTEGER NOT NULL DEFAULT 7, -ADD COLUMN mfa_code_timeout_seconds INTEGER NOT NULL DEFAULT 60; diff --git a/migrations/20260202100252_[2.0.0]_drop_legacy_stats_table.down.sql b/migrations/20260202100252_[2.0.0]_drop_legacy_stats_table.down.sql deleted file mode 100644 index bab1a6b5f0..0000000000 --- a/migrations/20260202100252_[2.0.0]_drop_legacy_stats_table.down.sql +++ /dev/null @@ -1,29 +0,0 @@ --- Restore legacy stats table -CREATE TABLE wireguard_peer_stats ( - id bigserial PRIMARY KEY, - device_id bigint NOT NULL, - collected_at timestamp without time zone NOT NULL DEFAULT current_timestamp, - network bigint NOT NULL, - endpoint text, - upload bigint NOT NULL, - download bigint NOT NULL, - latest_handshake timestamp without time zone NOT NULL, - allowed_ips text, - FOREIGN KEY (device_id) REFERENCES device(id) ON DELETE CASCADE -); -CREATE INDEX peer_stats_device_id_collected_at on wireguard_peer_stats (device_id, network, collected_at DESC, latest_handshake DESC NULLS LAST); - - --- Restore stats view -CREATE OR REPLACE VIEW wireguard_peer_stats_view AS - SELECT - device_id, - greatest(upload - lag(upload, 1, upload) OVER (PARTITION BY device_id, network ORDER BY collected_at), 0) upload, - greatest(download - lag(download, 1, download) OVER (PARTITION BY device_id, network ORDER BY collected_at), 0) download, - latest_handshake - (lag(latest_handshake, 1, latest_handshake) OVER (PARTITION BY device_id, network ORDER BY collected_at)) latest_handshake_diff, - latest_handshake, - collected_at, - network, - endpoint, - allowed_ips - FROM wireguard_peer_stats; diff --git a/migrations/20260202100252_[2.0.0]_drop_legacy_stats_table.up.sql b/migrations/20260202100252_[2.0.0]_drop_legacy_stats_table.up.sql deleted file mode 100644 index ebdfaaf7ab..0000000000 --- a/migrations/20260202100252_[2.0.0]_drop_legacy_stats_table.up.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Remove stats view -DROP VIEW wireguard_peer_stats_view; - --- Drop stats table -DROP TABLE wireguard_peer_stats; - diff --git a/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.down.sql b/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.down.sql deleted file mode 100644 index 0c72a349a5..0000000000 --- a/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE proxy ADD COLUMN public_address TEXT NOT NULL; -ALTER TABLE settings DROP COLUMN public_proxy_url; diff --git a/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.up.sql b/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.up.sql deleted file mode 100644 index 7463094e94..0000000000 --- a/migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE proxy DROP COLUMN public_address; -ALTER TABLE settings ADD COLUMN public_proxy_url TEXT NOT NULL DEFAULT 'http://localhost:8080'; diff --git a/migrations/20260205094812_[2.0.0]_initial_wizard_step_persistence.down.sql b/migrations/20260205094812_[2.0.0]_initial_wizard_step_persistence.down.sql deleted file mode 100644 index 83b8d141ab..0000000000 --- a/migrations/20260205094812_[2.0.0]_initial_wizard_step_persistence.down.sql +++ /dev/null @@ -1,7 +0,0 @@ -ALTER TABLE settings DROP CONSTRAINT fk_default_admin; - -ALTER TABLE settings -DROP COLUMN initial_setup_step, -DROP COLUMN default_admin_id; - -DROP TYPE initial_setup_step; diff --git a/migrations/20260205094812_[2.0.0]_initial_wizard_step_persistence.up.sql b/migrations/20260205094812_[2.0.0]_initial_wizard_step_persistence.up.sql deleted file mode 100644 index 0a08e70692..0000000000 --- a/migrations/20260205094812_[2.0.0]_initial_wizard_step_persistence.up.sql +++ /dev/null @@ -1,19 +0,0 @@ -CREATE TYPE initial_setup_step AS ENUM ( - 'welcome', - 'admin_user', - 'general_configuration', - 'ca', - 'ca_summary', - 'edge_component', - 'confirmation', - 'finished' -); - -ALTER TABLE settings -ADD COLUMN initial_setup_step initial_setup_step NOT NULL DEFAULT 'welcome', -ADD COLUMN default_admin_id BIGINT NULL; - -ALTER TABLE settings -ADD CONSTRAINT fk_default_admin -FOREIGN KEY (default_admin_id) REFERENCES "user"(id) -ON DELETE SET NULL; diff --git a/migrations/20260205123151_[2.0.0]_proxy_modified.down.sql b/migrations/20260205123151_[2.0.0]_proxy_modified.down.sql deleted file mode 100644 index 1b632e4f86..0000000000 --- a/migrations/20260205123151_[2.0.0]_proxy_modified.down.sql +++ /dev/null @@ -1,4 +0,0 @@ -ALTER TABLE proxy - DROP CONSTRAINT IF EXISTS proxy_modified_by_fkey, - DROP COLUMN IF EXISTS modified_by, - DROP COLUMN IF EXISTS modified_at; diff --git a/migrations/20260205123151_[2.0.0]_proxy_modified.up.sql b/migrations/20260205123151_[2.0.0]_proxy_modified.up.sql deleted file mode 100644 index f4fbc5b65d..0000000000 --- a/migrations/20260205123151_[2.0.0]_proxy_modified.up.sql +++ /dev/null @@ -1,5 +0,0 @@ -ALTER TABLE proxy - ADD COLUMN modified_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, - ADD COLUMN modified_by bigint NOT NULL DEFAULT 1, - ADD CONSTRAINT proxy_modified_by_fkey - FOREIGN KEY (modified_by) REFERENCES "user"(id); diff --git a/migrations/20260209080417_[2.0.0]_proxy_certificate.down.sql b/migrations/20260209080417_[2.0.0]_proxy_certificate.down.sql deleted file mode 100644 index d2c880529d..0000000000 --- a/migrations/20260209080417_[2.0.0]_proxy_certificate.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE proxy ADD COLUMN has_certificate boolean; -UPDATE proxy SET has_certificate = (certificate IS NOT NULL); -ALTER TABLE proxy DROP COLUMN certificate; diff --git a/migrations/20260209080417_[2.0.0]_proxy_certificate.up.sql b/migrations/20260209080417_[2.0.0]_proxy_certificate.up.sql deleted file mode 100644 index 2d671af774..0000000000 --- a/migrations/20260209080417_[2.0.0]_proxy_certificate.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE proxy - DROP COLUMN has_certificate, - ADD COLUMN certificate text; diff --git a/migrations/20260209083940_[2.0.0]_mjml.down.sql b/migrations/20260209083940_[2.0.0]_mjml.down.sql deleted file mode 100644 index 7893473927..0000000000 --- a/migrations/20260209083940_[2.0.0]_mjml.down.sql +++ /dev/null @@ -1 +0,0 @@ -DROP TABLE mail_context; diff --git a/migrations/20260209083940_[2.0.0]_mjml.up.sql b/migrations/20260209083940_[2.0.0]_mjml.up.sql deleted file mode 100644 index 1895a451f5..0000000000 --- a/migrations/20260209083940_[2.0.0]_mjml.up.sql +++ /dev/null @@ -1,33 +0,0 @@ -CREATE TABLE mail_context ( - template TEXT NOT NULL, - section TEXT NOT NULL, - language_tag TEXT NOT NULL, - text TEXT NOT NULL, - CONSTRAINT template_section_language UNIQUE (template, section, language_tag) -); -INSERT INTO mail_context (template, section, language_tag, text) VALUES - ('desktop-start', 'title', 'en_US', 'You''re receiving this email to configure a new desktop client.'), - ('desktop-start', 'subtitle', 'en_US', 'Please paste this URL and token in your desktop client:'), - ('desktop-start', 'label_url', 'en_US', 'URL'), - ('desktop-start', 'label_token', 'en_US', 'Token'), - ('desktop-start', 'configure', 'en_US', 'Configure your desktop client'), - ('desktop-start', 'click', 'en_US', 'Click the button or use link below'), - ('new-account', 'title', 'en_US', 'New account has been created for you'), - ('new-account', 'subtitle', 'en_US', 'To start the enrollment process, please use credentials below.'), - ('new-account', 'download', 'en_US', 'Download the official Defguard desktop client for your system.'), - ('new-account', 'after_install', 'en_US', 'After installation, please add a Defguard instance by entering:'), - ('new-account', 'label_url', 'en_US', 'URL'), - ('new-account', 'label_token', 'en_US', 'Token'), - ('new-account', 'token_info', 'en_US', 'The token is valid for 24 hours. Once the enrollment process starts, you have 10 minutes to complete it.'), - ('new-account', 'label_enroll', 'en_US', 'Enroll with desktop client'), - ('new-account', 'label_mobile', 'en_US', 'Mobile application'), - ('new-account', 'scan_qr', 'en_US', 'Scan QR code below to activate Defguard mobile application.'), - ('new-account', 'mobile_install', 'en_US', 'If you haven''t installed the mobile app, click one of the buttons below.'), - ('new-account', 'download_google', 'en_US', 'Download from Google Play'), - ('new-account', 'download_apple', 'en_US', 'Download from Apple Store'), - ('new-device', 'title', 'en_US', 'A new device has been added to your account:'), - ('new-device', 'label_device', 'en_US', 'Device name'), - ('new-device', 'label_pubkey', 'en_US', 'Public key'), - ('mfa-code', 'title', 'en_US', 'Hello,'), - ('mfa-code', 'subtitle', 'en_US', 'It seems like you are trying to login to Defguard. Here is the code you need to access your account.'), - ('mfa-code', 'code_is_valid', 'en_US', 'The code is valid for'); diff --git a/migrations/20260213090000_[2.0.0]_gateway_certificate_serial.down.sql b/migrations/20260213090000_[2.0.0]_gateway_certificate_serial.down.sql deleted file mode 100644 index 66425c97dc..0000000000 --- a/migrations/20260213090000_[2.0.0]_gateway_certificate_serial.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE gateway - DROP COLUMN certificate, - ADD COLUMN has_certificate boolean NOT NULL DEFAULT false; diff --git a/migrations/20260213090000_[2.0.0]_gateway_certificate_serial.up.sql b/migrations/20260213090000_[2.0.0]_gateway_certificate_serial.up.sql deleted file mode 100644 index b728d7a16c..0000000000 --- a/migrations/20260213090000_[2.0.0]_gateway_certificate_serial.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE gateway - DROP COLUMN has_certificate, - ADD COLUMN certificate TEXT; diff --git a/migrations/20260218054705_[2.0.0]_gateway_cascade_delete.down.sql b/migrations/20260218054705_[2.0.0]_gateway_cascade_delete.down.sql deleted file mode 100644 index d438bcd26b..0000000000 --- a/migrations/20260218054705_[2.0.0]_gateway_cascade_delete.down.sql +++ /dev/null @@ -1,7 +0,0 @@ -ALTER TABLE gateway -DROP CONSTRAINT gateway_network_id_fkey; - -ALTER TABLE gateway -ADD CONSTRAINT gateway_network_id_fkey -FOREIGN KEY (network_id) -REFERENCES wireguard_network(id); diff --git a/migrations/20260218054705_[2.0.0]_gateway_cascade_delete.up.sql b/migrations/20260218054705_[2.0.0]_gateway_cascade_delete.up.sql deleted file mode 100644 index bf0b321e3b..0000000000 --- a/migrations/20260218054705_[2.0.0]_gateway_cascade_delete.up.sql +++ /dev/null @@ -1,8 +0,0 @@ -ALTER TABLE gateway -DROP CONSTRAINT gateway_network_id_fkey; - -ALTER TABLE gateway -ADD CONSTRAINT gateway_network_id_fkey -FOREIGN KEY (network_id) -REFERENCES wireguard_network(id) -ON DELETE CASCADE; diff --git a/migrations/20260219113714_[2.0.0]_update_gateway_model.down.sql b/migrations/20260219113714_[2.0.0]_update_gateway_model.down.sql deleted file mode 100644 index 0c22a6865d..0000000000 --- a/migrations/20260219113714_[2.0.0]_update_gateway_model.down.sql +++ /dev/null @@ -1,11 +0,0 @@ -ALTER TABLE gateway - DROP CONSTRAINT modified_by_fkey, - DROP COLUMN enabled, - DROP COLUMN address, - DROP COLUMN port, - DROP COLUMN modified_at, - DROP COLUMN modified_by, - ADD COLUMN hostname TEXT NOT NULL DEFAULT 'gateway', - ADD COLUMN url text NOT NULL DEFAULT 'http://127.0.0.1:50051'; - -ALTER TABLE gateway RENAME COLUMN location_id TO network_id; diff --git a/migrations/20260219113714_[2.0.0]_update_gateway_model.up.sql b/migrations/20260219113714_[2.0.0]_update_gateway_model.up.sql deleted file mode 100644 index e535125cc5..0000000000 --- a/migrations/20260219113714_[2.0.0]_update_gateway_model.up.sql +++ /dev/null @@ -1,14 +0,0 @@ -ALTER TABLE gateway - DROP COLUMN url, - DROP COLUMN hostname, - -- FIXME: remove the default once we squash alpha migrations - ADD COLUMN address text NOT NULL DEFAULT '127.0.0.1', - -- FIXME: remove the default once we squash alpha migrations - ADD COLUMN port integer NOT NULL DEFAULT 50051, - ADD COLUMN modified_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, - -- FIXME: remove the default once we squash alpha migrations - ADD COLUMN modified_by bigint NOT NULL DEFAULT 1, - ADD COLUMN enabled bool NOT NULL DEFAULT true, - ADD CONSTRAINT modified_by_fkey FOREIGN KEY (modified_by) REFERENCES "user"(id); - -ALTER TABLE gateway RENAME COLUMN network_id TO location_id; diff --git a/migrations/20260223075953_[2.0.0]_mjml_part_2.down.sql b/migrations/20260223075953_[2.0.0]_mjml_part_2.down.sql deleted file mode 100644 index c9bb12325c..0000000000 --- a/migrations/20260223075953_[2.0.0]_mjml_part_2.down.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE mail_context - DROP COLUMN enabled, - ADD CONSTRAINT template_section_language UNIQUE (template, section, language_tag); diff --git a/migrations/20260223075953_[2.0.0]_mjml_part_2.up.sql b/migrations/20260223075953_[2.0.0]_mjml_part_2.up.sql deleted file mode 100644 index 73e4a440ba..0000000000 --- a/migrations/20260223075953_[2.0.0]_mjml_part_2.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE mail_context - ADD COLUMN enabled BOOL NOT NULL DEFAULT true, - DROP CONSTRAINT template_section_language; diff --git a/migrations/20260223095641_[2.0.0]_mjml_user_import.down.sql b/migrations/20260223095641_[2.0.0]_mjml_user_import.down.sql deleted file mode 100644 index 017421bcb9..0000000000 --- a/migrations/20260223095641_[2.0.0]_mjml_user_import.down.sql +++ /dev/null @@ -1 +0,0 @@ -DELETE from mail_context WHERE template = 'user-import-blocked'; diff --git a/migrations/20260223095641_[2.0.0]_mjml_user_import.up.sql b/migrations/20260223095641_[2.0.0]_mjml_user_import.up.sql deleted file mode 100644 index d976ef13ca..0000000000 --- a/migrations/20260223095641_[2.0.0]_mjml_user_import.up.sql +++ /dev/null @@ -1,3 +0,0 @@ -INSERT INTO mail_context (template, section, language_tag, text) VALUES - ('user-import-blocked', 'title', 'en_US', 'User import blocked'), - ('user-import-blocked', 'notification_text', 'en_US', 'Import of an external user was blocked because it would exceed your current license capacity.'); diff --git a/migrations/20260225142454_[2.0.0]_migration_wizard.down.sql b/migrations/20260225142454_[2.0.0]_migration_wizard.down.sql deleted file mode 100644 index f3d8b8826d..0000000000 --- a/migrations/20260225142454_[2.0.0]_migration_wizard.down.sql +++ /dev/null @@ -1,25 +0,0 @@ --- Restore settings columns -ALTER TABLE settings - ADD COLUMN initial_setup_completed BOOLEAN NOT NULL DEFAULT FALSE, - ADD COLUMN initial_setup_step initial_setup_step NOT NULL DEFAULT 'welcome'; - --- Copy data back from wizard to settings -UPDATE settings SET - initial_setup_completed = w.completed, - initial_setup_step = COALESCE((w.initial_setup_state->>'step')::initial_setup_step, 'welcome') -FROM wizard w WHERE w.is_singleton = TRUE AND settings.id = 1; - --- Reverse proxy modified_by: convert back to bigint FK --- NOTE: name-to-id conversion is lossy; existing rows will have NULL modified_by. -ALTER TABLE proxy - ALTER COLUMN modified_by TYPE bigint USING NULL, - ADD CONSTRAINT proxy_modified_by_fkey FOREIGN KEY (modified_by) REFERENCES "user"(id); - --- Reverse gateway modified_by: same as proxy -ALTER TABLE gateway - ALTER COLUMN modified_by TYPE bigint USING NULL, - ADD CONSTRAINT modified_by_fkey FOREIGN KEY (modified_by) REFERENCES "user"(id); - --- Drop wizard table and enum -DROP TABLE wizard; -DROP TYPE active_wizard; diff --git a/migrations/20260225142454_[2.0.0]_migration_wizard.up.sql b/migrations/20260225142454_[2.0.0]_migration_wizard.up.sql deleted file mode 100644 index fe55ac02b3..0000000000 --- a/migrations/20260225142454_[2.0.0]_migration_wizard.up.sql +++ /dev/null @@ -1,40 +0,0 @@ --- Create the active_wizard enum type -CREATE TYPE active_wizard AS ENUM ('none', 'initial', 'auto_adoption', 'migration'); - --- Create wizard table: active_wizard and completed as columns, each wizard --- type has its own JSONB column for step tracking state -CREATE TABLE wizard ( - is_singleton BOOLEAN NOT NULL DEFAULT TRUE PRIMARY KEY CHECK (is_singleton), - active_wizard active_wizard NOT NULL DEFAULT 'none', - completed BOOLEAN NOT NULL DEFAULT FALSE, - initial_setup_state JSONB, - auto_adoption_state JSONB, - migration_wizard_state JSONB -); - --- Migrate initial_setup data from settings into wizard -INSERT INTO wizard (is_singleton, active_wizard, completed, initial_setup_state) -SELECT TRUE, 'none'::active_wizard, s.initial_setup_completed, - jsonb_build_object('step', s.initial_setup_step::text) -FROM settings s WHERE s.id = 1; - --- Drop wizard-related columns from settings -ALTER TABLE settings - DROP COLUMN initial_setup_completed, - DROP COLUMN initial_setup_step; - --- Proxy modified_by: convert from user id (bigint FK) to user name (text) -ALTER TABLE proxy ADD COLUMN modified_by_name text; -UPDATE proxy SET modified_by_name = u.first_name || ' ' || u.last_name - FROM "user" u WHERE u.id = proxy.modified_by; -ALTER TABLE proxy DROP CONSTRAINT proxy_modified_by_fkey, DROP COLUMN modified_by; -ALTER TABLE proxy RENAME COLUMN modified_by_name TO modified_by; -ALTER TABLE proxy ALTER COLUMN modified_by SET NOT NULL; - --- Gateway modified_by: convert from user id (bigint FK) to user name (text) -ALTER TABLE gateway ADD COLUMN modified_by_name text; -UPDATE gateway SET modified_by_name = u.first_name || ' ' || u.last_name - FROM "user" u WHERE u.id = gateway.modified_by; -ALTER TABLE gateway DROP CONSTRAINT modified_by_fkey, DROP COLUMN modified_by; -ALTER TABLE gateway RENAME COLUMN modified_by_name TO modified_by; -ALTER TABLE gateway ALTER COLUMN modified_by SET NOT NULL; diff --git a/migrations/20260227091211_[2.0.0]_settings_in_db.down.sql b/migrations/20260227091211_[2.0.0]_settings_in_db.down.sql deleted file mode 100644 index 81a6d9b726..0000000000 --- a/migrations/20260227091211_[2.0.0]_settings_in_db.down.sql +++ /dev/null @@ -1,11 +0,0 @@ -ALTER TABLE settings - DROP COLUMN secret_key, - DROP COLUMN openid_signing_key, - DROP COLUMN webauthn_rp_id, - DROP COLUMN disable_stats_purge, - DROP COLUMN stats_purge_frequency_hours, - DROP COLUMN stats_purge_threshold_days, - DROP COLUMN enrollment_token_timeout_hours, - DROP COLUMN password_reset_token_timeout_hours, - DROP COLUMN enrollment_session_timeout_minutes, - DROP COLUMN password_reset_session_timeout_minutes; diff --git a/migrations/20260227091211_[2.0.0]_settings_in_db.up.sql b/migrations/20260227091211_[2.0.0]_settings_in_db.up.sql deleted file mode 100644 index 1f51954c46..0000000000 --- a/migrations/20260227091211_[2.0.0]_settings_in_db.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -ALTER TABLE settings - ADD COLUMN secret_key text, - ADD COLUMN openid_signing_key text, - ADD COLUMN webauthn_rp_id text, - ADD COLUMN disable_stats_purge boolean NOT NULL DEFAULT false, - ADD COLUMN stats_purge_frequency_hours int4 NOT NULL DEFAULT 24, - ADD COLUMN stats_purge_threshold_days int4 NOT NULL DEFAULT 30, - ADD COLUMN enrollment_token_timeout_hours int4 NOT NULL DEFAULT 24, - ADD COLUMN password_reset_token_timeout_hours int4 NOT NULL DEFAULT 24, - ADD COLUMN enrollment_session_timeout_minutes int4 NOT NULL DEFAULT 10, - ADD COLUMN password_reset_session_timeout_minutes int4 NOT NULL DEFAULT 10; diff --git a/migrations/20260302142347_[2.0.0]_proxy_enabled.down.sql b/migrations/20260302142347_[2.0.0]_proxy_enabled.down.sql deleted file mode 100644 index c1bb0c3a9f..0000000000 --- a/migrations/20260302142347_[2.0.0]_proxy_enabled.down.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE proxy DROP COLUMN enabled; diff --git a/migrations/20260302142347_[2.0.0]_proxy_enabled.up.sql b/migrations/20260302142347_[2.0.0]_proxy_enabled.up.sql deleted file mode 100644 index acc3c24452..0000000000 --- a/migrations/20260302142347_[2.0.0]_proxy_enabled.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE proxy ADD COLUMN enabled bool NOT NULL DEFAULT true; diff --git a/migrations/20260312072940_[2.0.0]_rp_id_stats_purge.down.sql b/migrations/20260312072940_[2.0.0]_rp_id_stats_purge.down.sql deleted file mode 100644 index 1e38fa5370..0000000000 --- a/migrations/20260312072940_[2.0.0]_rp_id_stats_purge.down.sql +++ /dev/null @@ -1,11 +0,0 @@ -ALTER TABLE settings - ADD COLUMN webauthn_rp_id text; - -UPDATE settings - SET enable_stats_purge = NOT enable_stats_purge; - -ALTER TABLE settings - ALTER COLUMN enable_stats_purge SET DEFAULT false; - -ALTER TABLE settings - RENAME COLUMN enable_stats_purge TO disable_stats_purge; diff --git a/migrations/20260312072940_[2.0.0]_rp_id_stats_purge.up.sql b/migrations/20260312072940_[2.0.0]_rp_id_stats_purge.up.sql deleted file mode 100644 index 1f8bf162f5..0000000000 --- a/migrations/20260312072940_[2.0.0]_rp_id_stats_purge.up.sql +++ /dev/null @@ -1,11 +0,0 @@ -ALTER TABLE settings - RENAME COLUMN disable_stats_purge TO enable_stats_purge; - -ALTER TABLE settings - ALTER COLUMN enable_stats_purge SET DEFAULT true; - -ALTER TABLE settings - DROP COLUMN webauthn_rp_id; - -UPDATE settings - SET enable_stats_purge = NOT enable_stats_purge; diff --git a/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.down.sql b/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.down.sql deleted file mode 100644 index 3dadba730c..0000000000 --- a/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE wireguard_network -DROP COLUMN allow_all_groups; diff --git a/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.up.sql b/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.up.sql deleted file mode 100644 index 3be846103c..0000000000 --- a/migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE wireguard_network -ADD COLUMN allow_all_groups boolean NOT NULL DEFAULT false; diff --git a/migrations/20260317100000_[2.0.0]_activity_log_optional_ip.down.sql b/migrations/20260317100000_[2.0.0]_activity_log_optional_ip.down.sql deleted file mode 100644 index 32e7a33db6..0000000000 --- a/migrations/20260317100000_[2.0.0]_activity_log_optional_ip.down.sql +++ /dev/null @@ -1,5 +0,0 @@ -UPDATE activity_log_event -SET ip = '0.0.0.0'::inet -WHERE ip IS NULL; - -ALTER TABLE activity_log_event ALTER COLUMN ip SET NOT NULL; diff --git a/migrations/20260317100000_[2.0.0]_activity_log_optional_ip.up.sql b/migrations/20260317100000_[2.0.0]_activity_log_optional_ip.up.sql deleted file mode 100644 index 0614b13db2..0000000000 --- a/migrations/20260317100000_[2.0.0]_activity_log_optional_ip.up.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE activity_log_event ALTER COLUMN ip DROP NOT NULL; diff --git a/migrations/20260317120000_[2.0.0]_vpn_client_session_preshared_key.down.sql b/migrations/20260317120000_[2.0.0]_vpn_client_session_preshared_key.down.sql deleted file mode 100644 index 5fd9557e6a..0000000000 --- a/migrations/20260317120000_[2.0.0]_vpn_client_session_preshared_key.down.sql +++ /dev/null @@ -1,27 +0,0 @@ -ALTER TABLE wireguard_network_device - ADD COLUMN preshared_key text NULL, - ADD COLUMN is_authorized bool NOT NULL DEFAULT false, - ADD COLUMN authorized_at timestamp without time zone NULL; - --- Rollback is lossy: only preshared_key is repopulated from an active --- session with a non-null preshared_key per (device_id, location_id); --- is_authorized and authorized_at are recreated with default/NULL values and --- are not reconstructed. -UPDATE wireguard_network_device AS network_device -SET preshared_key = latest_active_session.preshared_key -FROM ( - SELECT DISTINCT ON (session.device_id, session.location_id) - session.device_id, - session.location_id, - session.preshared_key - FROM vpn_client_session AS session - WHERE session.state IN ('new', 'connected') - AND session.preshared_key IS NOT NULL - ORDER BY session.device_id, session.location_id, session.created_at DESC, session.id DESC -) AS latest_active_session -WHERE network_device.device_id = latest_active_session.device_id - AND network_device.wireguard_network_id = latest_active_session.location_id; - -DROP INDEX IF EXISTS vpn_client_session_active_location_device_unique; - -ALTER TABLE vpn_client_session DROP COLUMN preshared_key; diff --git a/migrations/20260317120000_[2.0.0]_vpn_client_session_preshared_key.up.sql b/migrations/20260317120000_[2.0.0]_vpn_client_session_preshared_key.up.sql deleted file mode 100644 index ceac7eef68..0000000000 --- a/migrations/20260317120000_[2.0.0]_vpn_client_session_preshared_key.up.sql +++ /dev/null @@ -1,13 +0,0 @@ --- Add session-level preshared_key, enforce the active-session invariant, --- then drop device-level preshared_key/auth fields. --- WARNING: rollback is lossy for dropped wireguard_network_device columns. -ALTER TABLE vpn_client_session ADD COLUMN preshared_key text NULL; - -CREATE UNIQUE INDEX vpn_client_session_active_location_device_unique - ON vpn_client_session(location_id, device_id) - WHERE state IN ('new', 'connected'); - -ALTER TABLE wireguard_network_device - DROP COLUMN preshared_key, - DROP COLUMN is_authorized, - DROP COLUMN authorized_at; diff --git a/migrations/20260323081850_[2.0.0]_more_mjml.down.sql b/migrations/20260323081850_[2.0.0]_more_mjml.down.sql deleted file mode 100644 index 97c3f7002f..0000000000 --- a/migrations/20260323081850_[2.0.0]_more_mjml.down.sql +++ /dev/null @@ -1 +0,0 @@ --- Nothing here diff --git a/migrations/20260323081850_[2.0.0]_more_mjml.up.sql b/migrations/20260323081850_[2.0.0]_more_mjml.up.sql deleted file mode 100644 index d47bb0274f..0000000000 --- a/migrations/20260323081850_[2.0.0]_more_mjml.up.sql +++ /dev/null @@ -1,34 +0,0 @@ -INSERT INTO mail_context (template, section, language_tag, text) VALUES - ('mfa-activation', 'title', 'en_US', 'Hello,'), - ('mfa-activation', 'subtitle', 'en_US', 'You are activating Multi-Factor Authentication using email verification codes.'), - ('mfa-activation', 'code_is_valid', 'en_US', 'The code is valid for:'), - ('enrollment-admin-notification', 'title', 'en_US', 'Dear,'), - ('enrollment-admin-notification', 'message', 'en_US', 'just completed their enrollment process.'), - ('enrollment-admin-notification', 'goodday', 'en_US', 'Have a good day!'), - ('gateway-disconnect', 'title', 'en_US', 'Defguard Gateway has just disconnected.'), - ('gateway-disconnect', 'subtitle', 'en_US', 'Please login to your gateway server and see the logs.'), - ('gateway-disconnect', 'gateway_label', 'en_US', 'Gateway name:'), - ('gateway-disconnect', 'ip_address_label', 'en_US', 'Gateway IP address:'), - ('gateway-disconnect', 'location_label', 'en_US', 'VPN location:'), - ('gateway-reconnect', 'title', 'en_US', 'Defguard Gateway has just disconnected.'), - ('gateway-reconnect', 'gateway_label', 'en_US', 'Gateway name:'), - ('gateway-reconnect', 'ip_address_label', 'en_US', 'Gateway IP address:'), - ('gateway-reconnect', 'location_label', 'en_US', 'VPN location:'), - ('mfa-configured', 'title', 'en_US', 'Hello,'), - ('mfa-configured', 'subtitle', 'en_US', 'A Multi-Factor Authentication (MFA) has been activated in your account.'), - ('mfa-configured', 'mfa_method_label', 'en_US', 'MFA method:'), - ('new-device-login', 'title', 'en_US', 'Your account was just logged into from a new device.'), - ('new-device-login', 'label_device', 'en_US', 'Device name:'), - ('new-device-login', 'label_date', 'en_US', 'Date:'), - ('new-device-oidc-login', 'title', 'en_US', 'Your account was just logged into a system using OpenID Connect authorization'), - ('new-device-oidc-login', 'subtitle', 'en_US', 'You can deauthorize all applications that have access to your account from the web vault under (My Profile > Apps).'), - ('new-device-oidc-login', 'label_profile', 'en_US', 'Profile URL:'), - ('new-device-oidc-login', 'label_oauth2client', 'en_US', 'System name:'), - ('password-reset', 'title', 'en_US', 'Password reset'), - ('password-reset', 'subtitle', 'en_US', 'If you wish to reset your password, please copy and paste the following URL in your browser:'), - ('password-reset-done', 'title', 'en_US', 'Password reset'), - ('password-reset-done', 'subtitle', 'en_US', 'Your password has been successfully changed.'), - ('test', 'title', 'en_US', 'This is test email from Defguard system.'), - ('test', 'subtitle', 'en_US', 'If you received it, your SMTP configuration is correct.'), - ('support-data', 'title', 'en_US', 'Support data'), - ('support-data', 'subtitle', 'en_US', 'Support data can be found in the attachment.'); diff --git a/migrations/20260323083929_[2.0.0]_enrollment_settings.down.sql b/migrations/20260323083929_[2.0.0]_enrollment_settings.down.sql deleted file mode 100644 index ffe792ce73..0000000000 --- a/migrations/20260323083929_[2.0.0]_enrollment_settings.down.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE settings - DROP COLUMN enrollment_send_welcome_email; diff --git a/migrations/20260323083929_[2.0.0]_enrollment_settings.up.sql b/migrations/20260323083929_[2.0.0]_enrollment_settings.up.sql deleted file mode 100644 index ebe6934a7b..0000000000 --- a/migrations/20260323083929_[2.0.0]_enrollment_settings.up.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE settings - ADD COLUMN enrollment_send_welcome_email BOOLEAN NOT NULL DEFAULT TRUE;