From 4305c2eac0c6afc2dbbf9bdd7397de95870e08a7 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Sat, 7 Mar 2026 14:49:26 +0100 Subject: [PATCH 01/14] squash 2.0.0 migrations --- .../20251218140442_[2.0.0]_core_ca.down.sql | 116 ++++++++- .../20251218140442_[2.0.0]_core_ca.up.sql | 241 +++++++++++++++++- ...20260107092015_[2.0.0]_mtu_fwmark.down.sql | 3 - .../20260107092015_[2.0.0]_mtu_fwmark.up.sql | 3 - ...]_gateway_certificates_management.down.sql | 3 - ....0]_gateway_certificates_management.up.sql | 3 - ...13114719_[2.0.0]_proxy_management.down.sql | 1 - ...0113114719_[2.0.0]_proxy_management.up.sql | 9 - ...3545_[2.0.0]_openid_provider_kind.down.sql | 2 - ...123545_[2.0.0]_openid_provider_kind.up.sql | 10 - ...095450_[2.0.0]_proxy_certificates.down.sql | 4 - ...16095450_[2.0.0]_proxy_certificates.up.sql | 4 - ...21935_[2.0.0]_vpn_client_sessions.down.sql | 3 - ...9121935_[2.0.0]_vpn_client_sessions.up.sql | 46 ---- ...31_[2.0.0]_Add_component_versions.down.sql | 2 - ...2631_[2.0.0]_Add_component_versions.up.sql | 2 - ...85637_[2.0.0]_gateway_common_name.down.sql | 1 - ...7085637_[2.0.0]_gateway_common_name.up.sql | 1 - ...260127094513_[2.0.0]_aclalias_any.down.sql | 15 -- ...20260127094513_[2.0.0]_aclalias_any.up.sql | 31 --- ...43_[2.0.0]_add_session_mfa_method.down.sql | 9 - ...2943_[2.0.0]_add_session_mfa_method.up.sql | 15 -- ...1256_[2.0.0]_initial_setup_wizard.down.sql | 6 - ...121256_[2.0.0]_initial_setup_wizard.up.sql | 6 - ...2_[2.0.0]_drop_legacy_stats_table.down.sql | 29 --- ...252_[2.0.0]_drop_legacy_stats_table.up.sql | 6 - ...0.0]_public_proxy_url_in_settings.down.sql | 2 - ...2.0.0]_public_proxy_url_in_settings.up.sql | 2 - ...]_initial_wizard_step_persistence.down.sql | 7 - ....0]_initial_wizard_step_persistence.up.sql | 19 -- ...0205123151_[2.0.0]_proxy_modified.down.sql | 4 - ...260205123151_[2.0.0]_proxy_modified.up.sql | 5 - ...9080417_[2.0.0]_proxy_certificate.down.sql | 3 - ...209080417_[2.0.0]_proxy_certificate.up.sql | 3 - .../20260209083940_[2.0.0]_mjml.down.sql | 1 - migrations/20260209083940_[2.0.0]_mjml.up.sql | 33 --- ...2.0.0]_gateway_certificate_serial.down.sql | 3 - ..._[2.0.0]_gateway_certificate_serial.up.sql | 3 - ...05_[2.0.0]_gateway_cascade_delete.down.sql | 7 - ...4705_[2.0.0]_gateway_cascade_delete.up.sql | 8 - ...3714_[2.0.0]_update_gateway_model.down.sql | 11 - ...113714_[2.0.0]_update_gateway_model.up.sql | 14 - ...0260223075953_[2.0.0]_mjml_part_2.down.sql | 3 - .../20260223075953_[2.0.0]_mjml_part_2.up.sql | 3 - ...23095641_[2.0.0]_mjml_user_import.down.sql | 1 - ...0223095641_[2.0.0]_mjml_user_import.up.sql | 3 - ...25142454_[2.0.0]_migration_wizard.down.sql | 25 -- ...0225142454_[2.0.0]_migration_wizard.up.sql | 40 --- ...0227091211_[2.0.0]_settings_in_db.down.sql | 12 - ...260227091211_[2.0.0]_settings_in_db.up.sql | 12 - ...60302142347_[2.0.0]_proxy_enabled.down.sql | 1 - ...0260302142347_[2.0.0]_proxy_enabled.up.sql | 1 - 52 files changed, 354 insertions(+), 443 deletions(-) delete mode 100644 migrations/20260107092015_[2.0.0]_mtu_fwmark.down.sql delete mode 100644 migrations/20260107092015_[2.0.0]_mtu_fwmark.up.sql delete mode 100644 migrations/20260113094304_[2.0.0]_gateway_certificates_management.down.sql delete mode 100644 migrations/20260113094304_[2.0.0]_gateway_certificates_management.up.sql delete mode 100644 migrations/20260113114719_[2.0.0]_proxy_management.down.sql delete mode 100644 migrations/20260113114719_[2.0.0]_proxy_management.up.sql delete mode 100644 migrations/20260115123545_[2.0.0]_openid_provider_kind.down.sql delete mode 100644 migrations/20260115123545_[2.0.0]_openid_provider_kind.up.sql delete mode 100644 migrations/20260116095450_[2.0.0]_proxy_certificates.down.sql delete mode 100644 migrations/20260116095450_[2.0.0]_proxy_certificates.up.sql delete mode 100644 migrations/20260119121935_[2.0.0]_vpn_client_sessions.down.sql delete mode 100644 migrations/20260119121935_[2.0.0]_vpn_client_sessions.up.sql delete mode 100644 migrations/20260121122631_[2.0.0]_Add_component_versions.down.sql delete mode 100644 migrations/20260121122631_[2.0.0]_Add_component_versions.up.sql delete mode 100644 migrations/20260127085637_[2.0.0]_gateway_common_name.down.sql delete mode 100644 migrations/20260127085637_[2.0.0]_gateway_common_name.up.sql delete mode 100644 migrations/20260127094513_[2.0.0]_aclalias_any.down.sql delete mode 100644 migrations/20260127094513_[2.0.0]_aclalias_any.up.sql delete mode 100644 migrations/20260128082943_[2.0.0]_add_session_mfa_method.down.sql delete mode 100644 migrations/20260128082943_[2.0.0]_add_session_mfa_method.up.sql delete mode 100644 migrations/20260128121256_[2.0.0]_initial_setup_wizard.down.sql delete mode 100644 migrations/20260128121256_[2.0.0]_initial_setup_wizard.up.sql delete mode 100644 migrations/20260202100252_[2.0.0]_drop_legacy_stats_table.down.sql delete mode 100644 migrations/20260202100252_[2.0.0]_drop_legacy_stats_table.up.sql delete mode 100644 migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.down.sql delete mode 100644 migrations/20260204093536_[2.0.0]_public_proxy_url_in_settings.up.sql delete mode 100644 migrations/20260205094812_[2.0.0]_initial_wizard_step_persistence.down.sql delete mode 100644 migrations/20260205094812_[2.0.0]_initial_wizard_step_persistence.up.sql delete mode 100644 migrations/20260205123151_[2.0.0]_proxy_modified.down.sql delete mode 100644 migrations/20260205123151_[2.0.0]_proxy_modified.up.sql delete mode 100644 migrations/20260209080417_[2.0.0]_proxy_certificate.down.sql delete mode 100644 migrations/20260209080417_[2.0.0]_proxy_certificate.up.sql delete mode 100644 migrations/20260209083940_[2.0.0]_mjml.down.sql delete mode 100644 migrations/20260209083940_[2.0.0]_mjml.up.sql delete mode 100644 migrations/20260213090000_[2.0.0]_gateway_certificate_serial.down.sql delete mode 100644 migrations/20260213090000_[2.0.0]_gateway_certificate_serial.up.sql delete mode 100644 migrations/20260218054705_[2.0.0]_gateway_cascade_delete.down.sql delete mode 100644 migrations/20260218054705_[2.0.0]_gateway_cascade_delete.up.sql delete mode 100644 migrations/20260219113714_[2.0.0]_update_gateway_model.down.sql delete mode 100644 migrations/20260219113714_[2.0.0]_update_gateway_model.up.sql delete mode 100644 migrations/20260223075953_[2.0.0]_mjml_part_2.down.sql delete mode 100644 migrations/20260223075953_[2.0.0]_mjml_part_2.up.sql delete mode 100644 migrations/20260223095641_[2.0.0]_mjml_user_import.down.sql delete mode 100644 migrations/20260223095641_[2.0.0]_mjml_user_import.up.sql delete mode 100644 migrations/20260225142454_[2.0.0]_migration_wizard.down.sql delete mode 100644 migrations/20260225142454_[2.0.0]_migration_wizard.up.sql delete mode 100644 migrations/20260227091211_[2.0.0]_settings_in_db.down.sql delete mode 100644 migrations/20260227091211_[2.0.0]_settings_in_db.up.sql delete mode 100644 migrations/20260302142347_[2.0.0]_proxy_enabled.down.sql delete mode 100644 migrations/20260302142347_[2.0.0]_proxy_enabled.up.sql diff --git a/migrations/20251218140442_[2.0.0]_core_ca.down.sql b/migrations/20251218140442_[2.0.0]_core_ca.down.sql index 22ee3452f2..f8d8ad0b00 100644 --- a/migrations/20251218140442_[2.0.0]_core_ca.down.sql +++ b/migrations/20251218140442_[2.0.0]_core_ca.down.sql @@ -1,3 +1,117 @@ +DROP TABLE wizard; +DROP TYPE active_wizard; + +DROP TABLE mail_context; + +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; + +ALTER TABLE gateway DROP CONSTRAINT gateway_location_id_fkey; + +ALTER TABLE gateway + ADD COLUMN url text NOT NULL DEFAULT 'http://127.0.0.1:50051', + ADD COLUMN hostname text NULL; + +UPDATE gateway +SET + url = 'http://' || address || ':' || port, + hostname = name; + +ALTER TABLE gateway RENAME COLUMN location_id TO network_id; + +ALTER TABLE gateway + DROP COLUMN name, + DROP COLUMN address, + DROP COLUMN port, + DROP COLUMN certificate, + DROP COLUMN certificate_expiry, + DROP COLUMN version, + DROP COLUMN enabled, + DROP COLUMN modified_at, + DROP COLUMN modified_by; + +ALTER TABLE gateway + ADD CONSTRAINT gateway_network_id_fkey + FOREIGN KEY (network_id) REFERENCES wireguard_network(id); + +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; + +ALTER TABLE openidprovider DROP COLUMN kind; +DROP TYPE openid_provider_kind; + +ALTER TABLE wireguard_network + DROP COLUMN mtu, + DROP COLUMN fwmark; + +ALTER TABLE settings DROP CONSTRAINT fk_default_admin; + ALTER TABLE settings DROP COLUMN ca_key_der, - DROP COLUMN ca_cert_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 auth_cookie_timeout_days, + 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; + +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]_core_ca.up.sql b/migrations/20251218140442_[2.0.0]_core_ca.up.sql index 3db71200ff..acfdf38d33 100644 --- a/migrations/20251218140442_[2.0.0]_core_ca.up.sql +++ b/migrations/20251218140442_[2.0.0]_core_ca.up.sql @@ -1,3 +1,240 @@ ALTER TABLE settings - ADD COLUMN ca_key_der BYTEA DEFAULT NULL, - ADD COLUMN ca_cert_der BYTEA DEFAULT NULL; + 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 auth_cookie_timeout_days int4 NOT NULL DEFAULT 7, + 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; + +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; + +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; + +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; + +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; + +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; + +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; + +ALTER TABLE gateway DROP CONSTRAINT gateway_network_id_fkey; +ALTER TABLE gateway RENAME COLUMN network_id TO location_id; + +ALTER TABLE gateway + ADD COLUMN name text, + ADD COLUMN address text, + ADD COLUMN port integer, + ADD COLUMN certificate text, + ADD COLUMN certificate_expiry timestamp without time zone NULL, + ADD COLUMN version text, + ADD COLUMN enabled bool NOT NULL DEFAULT true, + ADD COLUMN modified_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + ADD COLUMN modified_by text NOT NULL DEFAULT 'System'; + +UPDATE gateway +SET + name = COALESCE(NULLIF(hostname, ''), 'Gateway ' || id::text), + address = COALESCE( + NULLIF(split_part(regexp_replace(url, '^https?://', ''), ':', 1), ''), + '127.0.0.1' + ), + port = COALESCE( + NULLIF( + regexp_replace(split_part(regexp_replace(url, '^https?://', ''), ':', 2), '/.*$', ''), + '' + )::integer, + 50051 + ); + +ALTER TABLE gateway + ALTER COLUMN name SET NOT NULL, + ALTER COLUMN address SET NOT NULL, + ALTER COLUMN port SET NOT NULL; + +ALTER TABLE gateway + DROP COLUMN url, + DROP COLUMN hostname; + +ALTER TABLE gateway + ADD CONSTRAINT gateway_location_id_fkey + FOREIGN KEY (location_id) REFERENCES wireguard_network(id) + ON DELETE CASCADE; + +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, + version text, + enabled boolean NOT NULL DEFAULT true, + certificate text, + certificate_expiry timestamp without time zone NULL, + modified_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + modified_by text NOT NULL DEFAULT 'System', + CONSTRAINT unique_address_port UNIQUE (address, port) +); + +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, + 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); + +DROP VIEW wireguard_peer_stats_view; +DROP TABLE wireguard_peer_stats; + +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.'); + +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 f2d1a94866..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 1 minute'); 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 c74dfd7d0d..0000000000 --- a/migrations/20260227091211_[2.0.0]_settings_in_db.down.sql +++ /dev/null @@ -1,12 +0,0 @@ -ALTER TABLE settings - DROP COLUMN auth_cookie_timeout_days, - 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 64858bd93f..0000000000 --- a/migrations/20260227091211_[2.0.0]_settings_in_db.up.sql +++ /dev/null @@ -1,12 +0,0 @@ -ALTER TABLE settings - ADD COLUMN auth_cookie_timeout_days int4 NOT NULL DEFAULT 7, - 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; From cb2bdff64835730659319530cfff5d26340edf84 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Sat, 7 Mar 2026 15:07:35 +0100 Subject: [PATCH 02/14] reorder proxy and gateway columns --- migrations/20251218140442_[2.0.0]_core_ca.up.sql | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/migrations/20251218140442_[2.0.0]_core_ca.up.sql b/migrations/20251218140442_[2.0.0]_core_ca.up.sql index acfdf38d33..f6c4ea4a89 100644 --- a/migrations/20251218140442_[2.0.0]_core_ca.up.sql +++ b/migrations/20251218140442_[2.0.0]_core_ca.up.sql @@ -75,14 +75,14 @@ ALTER TABLE gateway DROP CONSTRAINT gateway_network_id_fkey; ALTER TABLE gateway RENAME COLUMN network_id TO location_id; ALTER TABLE gateway - ADD COLUMN name text, - ADD COLUMN address text, - ADD COLUMN port integer, - ADD COLUMN certificate text, ADD COLUMN certificate_expiry timestamp without time zone NULL, ADD COLUMN version text, - ADD COLUMN enabled bool NOT NULL DEFAULT true, + ADD COLUMN name text, + ADD COLUMN certificate text, + ADD COLUMN address text DEFAULT '127.0.0.1', + ADD COLUMN port integer DEFAULT 50051, ADD COLUMN modified_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, + ADD COLUMN enabled bool NOT NULL DEFAULT true, ADD COLUMN modified_by text NOT NULL DEFAULT 'System'; UPDATE gateway @@ -121,12 +121,12 @@ CREATE TABLE proxy ( port integer NOT NULL, connected_at timestamp without time zone NULL, disconnected_at timestamp without time zone NULL, - version text, - enabled boolean NOT NULL DEFAULT true, - certificate text, 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 DEFAULT 'System', + enabled boolean NOT NULL DEFAULT true, CONSTRAINT unique_address_port UNIQUE (address, port) ); From f657c0c5196205a6083e4b36e144c7483ae9bb34 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 9 Mar 2026 13:27:31 +0100 Subject: [PATCH 03/14] rename squashed migration --- ...]_core_ca.down.sql => 20251218140442_[2.0.0]_initial.down.sql} | 0 ....0.0]_core_ca.up.sql => 20251218140442_[2.0.0]_initial.up.sql} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename migrations/{20251218140442_[2.0.0]_core_ca.down.sql => 20251218140442_[2.0.0]_initial.down.sql} (100%) rename migrations/{20251218140442_[2.0.0]_core_ca.up.sql => 20251218140442_[2.0.0]_initial.up.sql} (100%) diff --git a/migrations/20251218140442_[2.0.0]_core_ca.down.sql b/migrations/20251218140442_[2.0.0]_initial.down.sql similarity index 100% rename from migrations/20251218140442_[2.0.0]_core_ca.down.sql rename to migrations/20251218140442_[2.0.0]_initial.down.sql diff --git a/migrations/20251218140442_[2.0.0]_core_ca.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql similarity index 100% rename from migrations/20251218140442_[2.0.0]_core_ca.up.sql rename to migrations/20251218140442_[2.0.0]_initial.up.sql From 3551d4cc94e704211e6d3278b16c7c4e75843ee9 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 10 Mar 2026 08:46:28 +0100 Subject: [PATCH 04/14] squash additional migrations --- .../20251125072923_network_gateways.down.sql | 3 - .../20251125072923_network_gateways.up.sql | 20 ------ .../20251218140442_[2.0.0]_initial.down.sql | 30 +------- .../20251218140442_[2.0.0]_initial.up.sql | 70 ++++++++----------- ...0227091211_[2.0.0]_settings_in_db.down.sql | 11 --- ...260227091211_[2.0.0]_settings_in_db.up.sql | 11 --- 6 files changed, 32 insertions(+), 113 deletions(-) delete mode 100644 migrations/20251125072923_network_gateways.down.sql delete mode 100644 migrations/20251125072923_network_gateways.up.sql delete mode 100644 migrations/20260227091211_[2.0.0]_settings_in_db.down.sql delete mode 100644 migrations/20260227091211_[2.0.0]_settings_in_db.up.sql 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]_initial.down.sql b/migrations/20251218140442_[2.0.0]_initial.down.sql index 5b58d4bbe5..7ef062f7e5 100644 --- a/migrations/20251218140442_[2.0.0]_initial.down.sql +++ b/migrations/20251218140442_[2.0.0]_initial.down.sql @@ -10,33 +10,9 @@ DROP TYPE vpn_client_session_state; DROP TABLE proxy; -ALTER TABLE gateway DROP CONSTRAINT gateway_location_id_fkey; - -ALTER TABLE gateway - ADD COLUMN url text NOT NULL DEFAULT 'http://127.0.0.1:50051', - ADD COLUMN hostname text NULL; - -UPDATE gateway -SET - url = 'http://' || address || ':' || port, - hostname = name; - -ALTER TABLE gateway RENAME COLUMN location_id TO network_id; - -ALTER TABLE gateway - DROP COLUMN name, - DROP COLUMN address, - DROP COLUMN port, - DROP COLUMN certificate, - DROP COLUMN certificate_expiry, - DROP COLUMN version, - DROP COLUMN enabled, - DROP COLUMN modified_at, - DROP COLUMN modified_by; - -ALTER TABLE gateway - ADD CONSTRAINT gateway_network_id_fkey - FOREIGN KEY (network_id) REFERENCES wireguard_network(id); +DROP TRIGGER gateway ON gateway; +DROP FUNCTION row_change(); +DROP TABLE gateway; ALTER TABLE aclrule DROP COLUMN any_address, diff --git a/migrations/20251218140442_[2.0.0]_initial.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql index 65e75ff787..0bbb99b095 100644 --- a/migrations/20251218140442_[2.0.0]_initial.up.sql +++ b/migrations/20251218140442_[2.0.0]_initial.up.sql @@ -70,48 +70,36 @@ SET ALTER TABLE aclrule RENAME COLUMN destination TO addresses; ALTER TABLE aclrule RENAME COLUMN all_networks TO all_locations; -ALTER TABLE gateway DROP CONSTRAINT gateway_network_id_fkey; -ALTER TABLE gateway RENAME COLUMN network_id TO location_id; - -ALTER TABLE gateway - ADD COLUMN certificate_expiry timestamp without time zone NULL, - ADD COLUMN version text, - ADD COLUMN name text, - ADD COLUMN certificate text, - ADD COLUMN address text DEFAULT '127.0.0.1', - ADD COLUMN port integer DEFAULT 50051, - ADD COLUMN modified_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, - ADD COLUMN enabled bool NOT NULL DEFAULT true, - ADD COLUMN modified_by text NOT NULL DEFAULT 'System'; - -UPDATE gateway -SET - name = COALESCE(NULLIF(hostname, ''), 'Gateway ' || id::text), - address = COALESCE( - NULLIF(split_part(regexp_replace(url, '^https?://', ''), ':', 1), ''), - '127.0.0.1' - ), - port = COALESCE( - NULLIF( - regexp_replace(split_part(regexp_replace(url, '^https?://', ''), ':', 2), '/.*$', ''), - '' - )::integer, - 50051 - ); - -ALTER TABLE gateway - ALTER COLUMN name SET NOT NULL, - ALTER COLUMN address SET NOT NULL, - ALTER COLUMN port SET NOT NULL; +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 +); -ALTER TABLE gateway - DROP COLUMN url, - DROP COLUMN hostname; +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; -ALTER TABLE gateway - ADD CONSTRAINT gateway_location_id_fkey - FOREIGN KEY (location_id) REFERENCES wireguard_network(id) - ON DELETE CASCADE; +CREATE TRIGGER gateway + AFTER INSERT OR UPDATE OR DELETE ON gateway + FOR ROW EXECUTE FUNCTION row_change(); CREATE TABLE proxy ( id bigserial PRIMARY KEY, @@ -124,7 +112,7 @@ CREATE TABLE proxy ( version text, modified_at timestamp without time zone NOT NULL DEFAULT CURRENT_TIMESTAMP, certificate text, - modified_by text NOT NULL DEFAULT 'System', + modified_by text NOT NULL, enabled boolean NOT NULL DEFAULT true, CONSTRAINT unique_address_port UNIQUE (address, port) ); 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; From 96509b1c07be776f0f00635b120a0072fe9782ef Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Tue, 10 Mar 2026 12:45:49 +0100 Subject: [PATCH 05/14] comments --- migrations/20251218140442_[2.0.0]_initial.down.sql | 8 ++++++++ migrations/20251218140442_[2.0.0]_initial.up.sql | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/migrations/20251218140442_[2.0.0]_initial.down.sql b/migrations/20251218140442_[2.0.0]_initial.down.sql index 7ef062f7e5..3f366a7432 100644 --- a/migrations/20251218140442_[2.0.0]_initial.down.sql +++ b/migrations/20251218140442_[2.0.0]_initial.down.sql @@ -1,8 +1,11 @@ +-- Drop wizard state introduced in 2.0.0. DROP TABLE wizard; DROP TYPE active_wizard; +-- Remove database-backed mail templates. DROP TABLE mail_context; +-- Remove VPN session tracking and proxy management structures. DROP TABLE vpn_session_stats; DROP TABLE vpn_client_session; DROP TYPE vpn_client_mfa_method; @@ -14,6 +17,7 @@ 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 DROP COLUMN any_address, DROP COLUMN any_port, @@ -32,13 +36,16 @@ ALTER TABLE aclalias ALTER TABLE aclalias RENAME COLUMN addresses TO destination; +-- 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; +-- Remove 2.0.0 setup and settings columns. ALTER TABLE settings DROP CONSTRAINT fk_default_admin; ALTER TABLE settings @@ -62,6 +69,7 @@ ALTER TABLE settings 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, diff --git a/migrations/20251218140442_[2.0.0]_initial.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql index 0bbb99b095..353d8a8603 100644 --- a/migrations/20251218140442_[2.0.0]_initial.up.sql +++ b/migrations/20251218140442_[2.0.0]_initial.up.sql @@ -1,3 +1,4 @@ +-- 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, @@ -28,6 +29,7 @@ ALTER TABLE wireguard_network ADD COLUMN mtu integer NOT NULL DEFAULT 1420, ADD COLUMN fwmark bigint NOT NULL DEFAULT 0; +-- External OpenID providers gain a provider kind discriminator. CREATE TYPE openid_provider_kind AS ENUM ( 'Custom', 'Google', @@ -40,6 +42,7 @@ CREATE TYPE openid_provider_kind AS ENUM ( 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, @@ -70,6 +73,7 @@ SET 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, @@ -117,6 +121,7 @@ CREATE TABLE proxy ( 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', @@ -172,9 +177,11 @@ CREATE INDEX idx_vpn_session_stats_collected_at ON vpn_session_stats(collected_a 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); +-- 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, @@ -212,6 +219,7 @@ 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.'); +-- Wizard state is centralized outside of settings. CREATE TYPE active_wizard AS ENUM ('none', 'initial', 'auto_adoption', 'migration'); CREATE TABLE wizard ( From c716a6eb2ac5c8d4e4015c68c410fc2480b1bc62 Mon Sep 17 00:00:00 2001 From: Maciek <19913370+wojcik91@users.noreply.github.com> Date: Wed, 11 Mar 2026 14:30:09 +0100 Subject: [PATCH 06/14] ACL 2.0 migration (#2277) * expand ACL backfill logic to migrate legacy rules * add helper command for debugging gateway config * remove commented out code --- crates/defguard/src/main.rs | 51 +++++----- crates/defguard_common/src/config.rs | 8 ++ crates/defguard_core/src/lib.rs | 48 ++++++++- .../defguard_gateway_manager/src/handler.rs | 19 +--- crates/defguard_proto/src/lib.rs | 26 ++++- .../20251218140442_[2.0.0]_initial.down.sql | 10 +- .../20251218140442_[2.0.0]_initial.up.sql | 99 +++++++++++++++++-- 7 files changed, 204 insertions(+), 57 deletions(-) diff --git a/crates/defguard/src/main.rs b/crates/defguard/src/main.rs index fbc0e5f7c0..deeb7989f4 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?; - let has_auto_adopt_flags = config.adopt_edge.is_some() || config.adopt_gateway.is_some(); let wizard = Wizard::init(&pool, has_auto_adopt_flags).await?; let mut ini_server_config = true; @@ -207,20 +226,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 e9f39ab14c..4adf6c275f 100644 --- a/crates/defguard_common/src/config.rs +++ b/crates/defguard_common/src/config.rs @@ -196,6 +196,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)] @@ -220,6 +222,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 4a4fc9ffb0..c7b28141cd 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::{ @@ -31,6 +31,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; @@ -79,6 +80,7 @@ use crate::{ auth::failed_login::FailedLoginMap, db::AppEvent, enterprise::{ + firewall::try_get_location_firewall_config, handlers::{ acl::{ alias::{ @@ -169,7 +171,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, }; @@ -936,6 +940,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 rerieve 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 ce705fdb5c..3983ab1c0b 100644 --- a/crates/defguard_gateway_manager/src/handler.rs +++ b/crates/defguard_gateway_manager/src/handler.rs @@ -143,7 +143,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, @@ -790,20 +790,3 @@ fn try_protos_into_stats_message( latest_handshake, )) } - -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, - } -} diff --git a/crates/defguard_proto/src/lib.rs b/crates/defguard_proto/src/lib.rs index 84e78505c1..5b21cc2a6e 100644 --- a/crates/defguard_proto/src/lib.rs +++ b/crates/defguard_proto/src/lib.rs @@ -23,7 +23,7 @@ use defguard_common::{ db::{ Id, models::{ - Device, DeviceConfig, User, + Device, DeviceConfig, User, WireguardNetwork, vpn_client_session::VpnClientMfaMethod, wireguard::{LocationMfaMode, ServiceLocationMode}, }, @@ -33,6 +33,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 { @@ -159,3 +164,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/20251218140442_[2.0.0]_initial.down.sql b/migrations/20251218140442_[2.0.0]_initial.down.sql index 3f366a7432..4c4f7f4b72 100644 --- a/migrations/20251218140442_[2.0.0]_initial.down.sql +++ b/migrations/20251218140442_[2.0.0]_initial.down.sql @@ -18,6 +18,11 @@ 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, @@ -26,16 +31,11 @@ ALTER TABLE aclrule 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; - -- Remove 2.0.0 OpenID provider extensions. ALTER TABLE openidprovider DROP COLUMN kind; DROP TYPE openid_provider_kind; diff --git a/migrations/20251218140442_[2.0.0]_initial.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql index 353d8a8603..16227efb8a 100644 --- a/migrations/20251218140442_[2.0.0]_initial.up.sql +++ b/migrations/20251218140442_[2.0.0]_initial.up.sql @@ -48,11 +48,31 @@ ALTER TABLE aclalias ADD COLUMN any_port boolean NOT NULL DEFAULT false, ADD COLUMN any_protocol boolean NOT NULL DEFAULT false; -UPDATE aclalias +-- 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 = array_length(destination, 1) IS NULL, - any_port = array_length(ports, 1) IS NULL, - any_protocol = array_length(protocols, 1) IS NULL; + 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; @@ -64,12 +84,75 @@ ALTER TABLE aclrule ADD COLUMN allow_all_groups boolean NOT NULL DEFAULT false, ADD COLUMN deny_all_groups boolean NOT NULL DEFAULT false; -UPDATE aclrule +-- 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 = array_length(destination, 1) IS NULL, - any_port = array_length(ports, 1) IS NULL, - any_protocol = array_length(protocols, 1) IS NULL; + 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; From 3aad6961977d1b1f65bbc85b04c71f2c45fd02b9 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 16 Mar 2026 11:02:20 +0100 Subject: [PATCH 07/14] squash rp_id migration --- migrations/20251218140442_[2.0.0]_initial.down.sql | 2 -- migrations/20251218140442_[2.0.0]_initial.up.sql | 3 +-- .../20260312072940_[2.0.0]_rp_id_stats_purge.down.sql | 11 ----------- .../20260312072940_[2.0.0]_rp_id_stats_purge.up.sql | 11 ----------- 4 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 migrations/20260312072940_[2.0.0]_rp_id_stats_purge.down.sql delete mode 100644 migrations/20260312072940_[2.0.0]_rp_id_stats_purge.up.sql diff --git a/migrations/20251218140442_[2.0.0]_initial.down.sql b/migrations/20251218140442_[2.0.0]_initial.down.sql index 4c4f7f4b72..89f4b3d93b 100644 --- a/migrations/20251218140442_[2.0.0]_initial.down.sql +++ b/migrations/20251218140442_[2.0.0]_initial.down.sql @@ -60,8 +60,6 @@ ALTER TABLE settings DROP COLUMN default_admin_id, 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, diff --git a/migrations/20251218140442_[2.0.0]_initial.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql index 16227efb8a..6cb5834deb 100644 --- a/migrations/20251218140442_[2.0.0]_initial.up.sql +++ b/migrations/20251218140442_[2.0.0]_initial.up.sql @@ -11,8 +11,7 @@ ALTER TABLE settings ADD COLUMN default_admin_id bigint NULL, 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 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, 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; From 23f214b21d072664924147436a5837e8b558bd7f Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 16 Mar 2026 11:04:18 +0100 Subject: [PATCH 08/14] squash wireguard_network migration --- migrations/20251218140442_[2.0.0]_initial.down.sql | 3 ++- migrations/20251218140442_[2.0.0]_initial.up.sql | 3 ++- ...2110000_[2.0.0]_wireguard_network_allow_all_groups.down.sql | 2 -- ...312110000_[2.0.0]_wireguard_network_allow_all_groups.up.sql | 2 -- 4 files changed, 4 insertions(+), 6 deletions(-) delete mode 100644 migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.down.sql delete mode 100644 migrations/20260312110000_[2.0.0]_wireguard_network_allow_all_groups.up.sql diff --git a/migrations/20251218140442_[2.0.0]_initial.down.sql b/migrations/20251218140442_[2.0.0]_initial.down.sql index 89f4b3d93b..ecccba2942 100644 --- a/migrations/20251218140442_[2.0.0]_initial.down.sql +++ b/migrations/20251218140442_[2.0.0]_initial.down.sql @@ -43,7 +43,8 @@ DROP TYPE openid_provider_kind; -- Remove 2.0.0 WireGuard network defaults. ALTER TABLE wireguard_network DROP COLUMN mtu, - DROP COLUMN fwmark; + DROP COLUMN fwmark, + DROP COLUMN allow_all_groups; -- Remove 2.0.0 setup and settings columns. ALTER TABLE settings DROP CONSTRAINT fk_default_admin; diff --git a/migrations/20251218140442_[2.0.0]_initial.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql index 6cb5834deb..640a0b2975 100644 --- a/migrations/20251218140442_[2.0.0]_initial.up.sql +++ b/migrations/20251218140442_[2.0.0]_initial.up.sql @@ -26,7 +26,8 @@ ALTER TABLE settings ALTER TABLE wireguard_network ADD COLUMN mtu integer NOT NULL DEFAULT 1420, - ADD COLUMN fwmark bigint NOT NULL DEFAULT 0; + 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 ( 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; From b076d31c34eb3ba8621a6fb6dddfc4b42fc915a2 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Mon, 16 Mar 2026 11:10:04 +0100 Subject: [PATCH 09/14] fix squashed down migration --- migrations/20251218140442_[2.0.0]_initial.down.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/migrations/20251218140442_[2.0.0]_initial.down.sql b/migrations/20251218140442_[2.0.0]_initial.down.sql index ecccba2942..3af68e3703 100644 --- a/migrations/20251218140442_[2.0.0]_initial.down.sql +++ b/migrations/20251218140442_[2.0.0]_initial.down.sql @@ -61,6 +61,7 @@ ALTER TABLE settings 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, From cd4a220a797b19afb9858d00daa12936c872b04d Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 26 Mar 2026 08:01:37 +0100 Subject: [PATCH 10/14] squash new migrations --- .../20251218140442_[2.0.0]_initial.down.sql | 31 ++++++++++++ .../20251218140442_[2.0.0]_initial.up.sql | 47 ++++++++++++++++++- migrations/20260209083940_[2.0.0]_mjml.up.sql | 33 ------------- ..._[2.0.0]_activity_log_optional_ip.down.sql | 5 -- ...00_[2.0.0]_activity_log_optional_ip.up.sql | 1 - ..._vpn_client_session_preshared_key.down.sql | 27 ----------- ...0]_vpn_client_session_preshared_key.up.sql | 13 ----- .../20260323081850_[2.0.0]_more_mjml.down.sql | 1 - .../20260323081850_[2.0.0]_more_mjml.up.sql | 34 -------------- ...83929_[2.0.0]_enrollment_settings.down.sql | 2 - ...3083929_[2.0.0]_enrollment_settings.up.sql | 2 - 11 files changed, 77 insertions(+), 119 deletions(-) delete mode 100644 migrations/20260209083940_[2.0.0]_mjml.up.sql delete mode 100644 migrations/20260317100000_[2.0.0]_activity_log_optional_ip.down.sql delete mode 100644 migrations/20260317100000_[2.0.0]_activity_log_optional_ip.up.sql delete mode 100644 migrations/20260317120000_[2.0.0]_vpn_client_session_preshared_key.down.sql delete mode 100644 migrations/20260317120000_[2.0.0]_vpn_client_session_preshared_key.up.sql delete mode 100644 migrations/20260323081850_[2.0.0]_more_mjml.down.sql delete mode 100644 migrations/20260323081850_[2.0.0]_more_mjml.up.sql delete mode 100644 migrations/20260323083929_[2.0.0]_enrollment_settings.down.sql delete mode 100644 migrations/20260323083929_[2.0.0]_enrollment_settings.up.sql diff --git a/migrations/20251218140442_[2.0.0]_initial.down.sql b/migrations/20251218140442_[2.0.0]_initial.down.sql index 3af68e3703..68903f86ca 100644 --- a/migrations/20251218140442_[2.0.0]_initial.down.sql +++ b/migrations/20251218140442_[2.0.0]_initial.down.sql @@ -5,6 +5,36 @@ DROP TYPE active_wizard; -- Remove database-backed mail templates. DROP TABLE mail_context; +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; @@ -65,6 +95,7 @@ ALTER TABLE settings 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; diff --git a/migrations/20251218140442_[2.0.0]_initial.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql index 640a0b2975..e6e57dc02f 100644 --- a/migrations/20251218140442_[2.0.0]_initial.up.sql +++ b/migrations/20251218140442_[2.0.0]_initial.up.sql @@ -15,10 +15,13 @@ ALTER TABLE settings 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) @@ -228,6 +231,7 @@ CREATE TABLE vpn_client_session ( 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, @@ -239,6 +243,14 @@ CREATE INDEX idx_vpn_client_session_location_id ON vpn_client_session(location_i 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 TABLE vpn_session_stats ( id bigserial PRIMARY KEY, @@ -300,7 +312,40 @@ INSERT INTO mail_context (template, section, language_tag, text) VALUES ('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.'); + ('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 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.'); -- Wizard state is centralized outside of settings. CREATE TYPE active_wizard AS ENUM ('none', 'initial', 'auto_adoption', 'migration'); 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/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; From e464bf3b5788f087da85061668714c4747caaf9e Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 26 Mar 2026 08:40:57 +0100 Subject: [PATCH 11/14] add indices --- migrations/20251218140442_[2.0.0]_initial.down.sql | 6 ++++++ migrations/20251218140442_[2.0.0]_initial.up.sql | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/migrations/20251218140442_[2.0.0]_initial.down.sql b/migrations/20251218140442_[2.0.0]_initial.down.sql index 68903f86ca..744d1d59f3 100644 --- a/migrations/20251218140442_[2.0.0]_initial.down.sql +++ b/migrations/20251218140442_[2.0.0]_initial.down.sql @@ -5,6 +5,12 @@ 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; diff --git a/migrations/20251218140442_[2.0.0]_initial.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql index e6e57dc02f..61966bea1e 100644 --- a/migrations/20251218140442_[2.0.0]_initial.up.sql +++ b/migrations/20251218140442_[2.0.0]_initial.up.sql @@ -252,6 +252,12 @@ ALTER TABLE wireguard_network_device 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, @@ -347,6 +353,9 @@ INSERT INTO mail_context (template, section, language_tag, text) VALUES ('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'); From 52d2edfdd3f8257218084a9624e6375c29f55f39 Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 26 Mar 2026 08:41:28 +0100 Subject: [PATCH 12/14] cargo fmt --- crates/defguard_gateway_manager/src/handler.rs | 3 +-- crates/defguard_proto/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/defguard_gateway_manager/src/handler.rs b/crates/defguard_gateway_manager/src/handler.rs index fc838df640..d971c4af2a 100644 --- a/crates/defguard_gateway_manager/src/handler.rs +++ b/crates/defguard_gateway_manager/src/handler.rs @@ -1022,8 +1022,7 @@ mod tests { use tokio::sync::{broadcast, mpsc::unbounded_channel, watch}; use super::{ - FirewallConfig, GatewayHandler, GatewayUpdatesHandler, - try_protos_into_stats_message, + FirewallConfig, GatewayHandler, GatewayUpdatesHandler, try_protos_into_stats_message, }; fn test_network(location_mfa_mode: LocationMfaMode) -> WireguardNetwork { diff --git a/crates/defguard_proto/src/lib.rs b/crates/defguard_proto/src/lib.rs index da6110bab7..ae1a525952 100644 --- a/crates/defguard_proto/src/lib.rs +++ b/crates/defguard_proto/src/lib.rs @@ -18,12 +18,12 @@ pub mod enterprise { use defguard_common::{ csv::AsCsv, db::{ + Id, models::{ + Device, DeviceConfig, User, WireguardNetwork, vpn_client_session::VpnClientMfaMethod, wireguard::{LocationMfaMode, ServiceLocationMode}, - Device, DeviceConfig, User, WireguardNetwork, }, - Id, }, }; use proxy::{CoreError, MfaMethod}; From fd836912f7478e1b97dd0bd72fa658b244faeb0a Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 26 Mar 2026 08:50:37 +0100 Subject: [PATCH 13/14] additional index on session stats --- migrations/20251218140442_[2.0.0]_initial.up.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migrations/20251218140442_[2.0.0]_initial.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql index 61966bea1e..acc44100da 100644 --- a/migrations/20251218140442_[2.0.0]_initial.up.sql +++ b/migrations/20251218140442_[2.0.0]_initial.up.sql @@ -277,6 +277,8 @@ 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; From 27958b80bdddf7ecce2b1948d5f356be724cb67d Mon Sep 17 00:00:00 2001 From: Jacek Chmielewski Date: Thu, 26 Mar 2026 09:23:21 +0100 Subject: [PATCH 14/14] fix typos --- crates/defguard_core/src/lib.rs | 2 +- migrations/20251218140442_[2.0.0]_initial.up.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/defguard_core/src/lib.rs b/crates/defguard_core/src/lib.rs index e23c48a93d..252dc6c471 100644 --- a/crates/defguard_core/src/lib.rs +++ b/crates/defguard_core/src/lib.rs @@ -947,7 +947,7 @@ pub async fn gateway_config( Ok(None) => return Err(anyhow!("Location {location_id} not found")), Err(err) => { return Err(anyhow!( - "Failed to rerieve location {location_id} with error: {err}" + "Failed to retrieve location {location_id} with error: {err}" )); } }; diff --git a/migrations/20251218140442_[2.0.0]_initial.up.sql b/migrations/20251218140442_[2.0.0]_initial.up.sql index acc44100da..c78136f2aa 100644 --- a/migrations/20251218140442_[2.0.0]_initial.up.sql +++ b/migrations/20251218140442_[2.0.0]_initial.up.sql @@ -332,7 +332,7 @@ INSERT INTO mail_context (template, section, language_tag, text) VALUES ('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', '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:'),