Files
rdp-proxy/backend/migrations/000014_vpn_ip_tunnel_control_plane.up.sql
T
2026-04-28 22:29:50 +03:00

126 lines
6.0 KiB
SQL

CREATE TABLE IF NOT EXISTS vpn_connections (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
name TEXT NOT NULL,
target_endpoint JSONB NOT NULL DEFAULT '{}'::JSONB,
protocol_family TEXT NOT NULL DEFAULT 'generic',
credential_ref TEXT,
mode TEXT NOT NULL DEFAULT 'single_active',
desired_state TEXT NOT NULL DEFAULT 'disabled',
allowed_node_policy JSONB NOT NULL DEFAULT '{"mode":"explicit","node_ids":[]}'::JSONB,
routing_usage JSONB NOT NULL DEFAULT '[]'::JSONB,
route_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
qos_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
placement_policy JSONB NOT NULL DEFAULT '{}'::JSONB,
status TEXT NOT NULL DEFAULT 'disabled',
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
updated_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT vpn_connections_mode_check
CHECK (mode IN ('single_active')),
CONSTRAINT vpn_connections_desired_state_check
CHECK (desired_state IN ('enabled', 'disabled')),
CONSTRAINT vpn_connections_status_check
CHECK (status IN ('disabled', 'enabled', 'connecting', 'active', 'degraded', 'failed')),
CONSTRAINT vpn_connections_protocol_family_check
CHECK (protocol_family IN ('generic', 'wireguard', 'ipsec', 'openvpn'))
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_vpn_connections_cluster_org_name
ON vpn_connections(cluster_id, organization_id, lower(name));
CREATE INDEX IF NOT EXISTS idx_vpn_connections_cluster_org_state
ON vpn_connections(cluster_id, organization_id, desired_state, status);
CREATE TABLE IF NOT EXISTS vpn_connection_allowed_nodes (
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
role_preference TEXT NOT NULL DEFAULT 'candidate',
status TEXT NOT NULL DEFAULT 'active',
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (vpn_connection_id, node_id),
CONSTRAINT vpn_connection_allowed_nodes_membership_fk
FOREIGN KEY (cluster_id, node_id)
REFERENCES cluster_memberships(cluster_id, node_id)
ON DELETE CASCADE,
CONSTRAINT vpn_connection_allowed_nodes_preference_check
CHECK (role_preference IN ('candidate', 'standby', 'preferred')),
CONSTRAINT vpn_connection_allowed_nodes_status_check
CHECK (status IN ('active', 'disabled'))
);
CREATE INDEX IF NOT EXISTS idx_vpn_connection_allowed_nodes_cluster_node
ON vpn_connection_allowed_nodes(cluster_id, node_id, status);
CREATE TABLE IF NOT EXISTS vpn_connection_route_policies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
route_type TEXT NOT NULL,
destination TEXT NOT NULL,
action TEXT NOT NULL DEFAULT 'allow',
service_type TEXT,
priority INTEGER NOT NULL DEFAULT 100,
policy JSONB NOT NULL DEFAULT '{}'::JSONB,
status TEXT NOT NULL DEFAULT 'active',
created_by_user_id UUID REFERENCES users(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT vpn_connection_route_policies_route_type_check
CHECK (route_type IN ('cidr', 'dns_suffix', 'service', 'resource')),
CONSTRAINT vpn_connection_route_policies_action_check
CHECK (action IN ('allow', 'deny')),
CONSTRAINT vpn_connection_route_policies_status_check
CHECK (status IN ('active', 'disabled'))
);
CREATE INDEX IF NOT EXISTS idx_vpn_connection_route_policies_connection
ON vpn_connection_route_policies(vpn_connection_id, status, priority);
CREATE INDEX IF NOT EXISTS idx_vpn_connection_route_policies_cluster_org
ON vpn_connection_route_policies(cluster_id, organization_id, route_type, status);
CREATE TABLE IF NOT EXISTS vpn_connection_leases (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
vpn_connection_id UUID NOT NULL REFERENCES vpn_connections(id) ON DELETE CASCADE,
cluster_id UUID NOT NULL REFERENCES clusters(id) ON DELETE CASCADE,
owner_node_id UUID NOT NULL REFERENCES nodes(id) ON DELETE CASCADE,
lease_generation BIGINT NOT NULL,
fencing_token TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
acquired_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
renewed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL,
released_at TIMESTAMPTZ,
fenced_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'::JSONB,
CONSTRAINT vpn_connection_leases_membership_fk
FOREIGN KEY (cluster_id, owner_node_id)
REFERENCES cluster_memberships(cluster_id, node_id)
ON DELETE CASCADE,
CONSTRAINT vpn_connection_leases_status_check
CHECK (status IN ('active', 'released', 'expired', 'fenced')),
CONSTRAINT vpn_connection_leases_expiry_check
CHECK (expires_at > acquired_at)
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_vpn_connection_leases_generation
ON vpn_connection_leases(vpn_connection_id, lease_generation);
CREATE UNIQUE INDEX IF NOT EXISTS idx_vpn_connection_leases_single_active
ON vpn_connection_leases(vpn_connection_id)
WHERE status = 'active';
CREATE INDEX IF NOT EXISTS idx_vpn_connection_leases_owner
ON vpn_connection_leases(cluster_id, owner_node_id, status, expires_at);
CREATE INDEX IF NOT EXISTS idx_vpn_connection_leases_connection_time
ON vpn_connection_leases(vpn_connection_id, acquired_at DESC);