Release / verify (push) Canceled after 0s
CI / checks (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / backend (push) Canceled after 0s
Release / test (push) Canceled after 0s
Release / publish (backend) (push) Canceled after 0s
Release / publish (frontend) (push) Canceled after 0s
Release / publish (proxy) (push) Canceled after 0s
Release / publish (updater) (push) Canceled after 0s
- **Management-Tunnel** (System › Management-Tunnel): Firewalls hinter NAT/CGNAT verwalten. Ein optionaler WireGuard-Hub auf dem OPNexus-Server (Compose-Profil `overlay`, Container `wg-hub` aus dem Backend-Image mit `NET_ADMIN`) nimmt die Firewalls entgegen, die sich nach außen einwählen; OPNexus erreicht deren API über einen kleinen CONNECT-Proxy des Hubs, der nur ins Tunnel-Netz verbindet. Je Firewall eine Tunnel-Adresse und ein eigener Pre-Shared Key, Anleitung mit kopierbaren Werten, Live-Status (Handshake, Datenmenge). Der Hub nimmt keine neuen Verbindungen aus dem Tunnel an. Ende-zu-Ende gegen eine echte OPNsense 26.7 geprüft (Handshake, API, Zertifikat festsetzen). Neue Tabellen `overlay_settings`, `overlay_peers` (Migration `0015`); das Backend-Image enthält jetzt `wireguard-tools`, `iproute2`, `iptables`.
49 lines
1.8 KiB
Python
49 lines
1.8 KiB
Python
"""Management-Overlay (WireGuard-Hub) für Firewalls hinter NAT.
|
|
|
|
Revision ID: 0015
|
|
Revises: 0014
|
|
Create Date: 2026-10-20
|
|
"""
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
from sqlalchemy.dialects import postgresql
|
|
|
|
revision = "0015"
|
|
down_revision = "0014"
|
|
branch_labels = None
|
|
depends_on = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
inspector = sa.inspect(op.get_bind())
|
|
if not inspector.has_table("overlay_settings"):
|
|
op.create_table(
|
|
"overlay_settings",
|
|
sa.Column("id", sa.Integer(), nullable=False),
|
|
sa.Column("enabled", sa.Boolean(), nullable=False),
|
|
sa.Column("endpoint", sa.String(length=255), nullable=True),
|
|
sa.Column("subnet", sa.String(length=18), nullable=False),
|
|
sa.Column("listen_port", sa.Integer(), nullable=False),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
)
|
|
if not inspector.has_table("overlay_peers"):
|
|
op.create_table(
|
|
"overlay_peers",
|
|
sa.Column("id", postgresql.UUID(as_uuid=True), nullable=False),
|
|
sa.Column("name", sa.String(length=80), nullable=False),
|
|
sa.Column("ip", sa.String(length=15), nullable=False),
|
|
sa.Column("public_key", sa.String(length=44), nullable=True),
|
|
sa.Column("host_id", postgresql.UUID(as_uuid=True), nullable=True),
|
|
sa.Column("created_at", sa.DateTime(timezone=True), server_default=sa.text("now()"), nullable=False),
|
|
sa.ForeignKeyConstraint(["host_id"], ["opnsense_hosts.id"], ondelete="SET NULL"),
|
|
sa.PrimaryKeyConstraint("id"),
|
|
sa.UniqueConstraint("name"),
|
|
sa.UniqueConstraint("ip"),
|
|
sa.UniqueConstraint("public_key"),
|
|
)
|
|
|
|
|
|
def downgrade() -> None:
|
|
op.execute("DROP TABLE IF EXISTS overlay_peers")
|
|
op.execute("DROP TABLE IF EXISTS overlay_settings")
|