#!/bin/bash # postinst for edgeguard-api — creates system user, filesystem layout, # initialises PostgreSQL (role + db + migrations), enables systemd # units. Each step idempotent; safe to re-run on every upgrade. set -e export LC_ALL=C export LANG=C EG_USER="edgeguard" EG_HOME="/var/lib/edgeguard" case "$1" in configure) # ── System user ────────────────────────────────────────────── if ! getent passwd "$EG_USER" >/dev/null; then adduser --system --group --home "$EG_HOME" \ --shell /usr/sbin/nologin --no-create-home \ --gecos "EdgeGuard daemon" "$EG_USER" fi # haproxy admin.sock liegt mit mode 0660 root:haproxy. Damit # edgeguard-api das Backend-Stat-CSV lesen kann (Dashboard), # in die haproxy-group adden — best-effort, nicht failen wenn # das Paket noch nicht installiert ist. if getent group haproxy >/dev/null; then usermod -a -G haproxy "$EG_USER" || true fi # unbound-Gruppe: unbound-control-Socket ist root:unbound srw-rw---- # → edgeguard muss in der Gruppe sein um stats_noreset + flush aufrufen # zu können (kein sudo nötig, Gruppe reicht). if getent group unbound >/dev/null; then usermod -a -G unbound "$EG_USER" || true fi # freeradius: edgeguard muss die gerenderten clients.conf/authorize # ins freeradius-lesbare Verzeichnis schreiben (setgid-Dir unten). if getent group freeradius >/dev/null; then usermod -a -G freeradius "$EG_USER" || true fi # systemd-journal + adm: damit edgeguard-api `journalctl -u …` # ohne sudo lesen kann — wird für /api/v1/logs gebraucht # (zentrale Log-Übersicht über alle Services). if getent group systemd-journal >/dev/null; then usermod -a -G systemd-journal "$EG_USER" || true fi if getent group adm >/dev/null; then usermod -a -G adm "$EG_USER" || true fi # ── Directories ────────────────────────────────────────────── # /etc/edgeguard und Service-Subdirs müssen für die Service-User # (squid, unbound, haproxy laufen NICHT als edgeguard) traversier- # bzw lesbar sein. 0755 statt 0750 — kein Geheimnis ist hier # gespeichert, alles sind Renderer-Outputs. for d in /etc/edgeguard /etc/edgeguard/haproxy /etc/edgeguard/squid \ /etc/edgeguard/wireguard /etc/edgeguard/unbound \ /etc/edgeguard/kea \ /etc/edgeguard/nftables.d; do install -d -m 0755 -o "$EG_USER" -g "$EG_USER" "$d" done # Sensitive Verzeichnisse bleiben 0750 (TLS-Keys, ACME-State). for d in /etc/edgeguard/tls /var/lib/edgeguard /var/log/edgeguard \ /var/lib/edgeguard/acme; do install -d -m 0750 -o "$EG_USER" -g "$EG_USER" "$d" done # ACME-Account-Dir 0700 — hält den lego-Account-Schlüssel, # gehört nur edgeguard. install -d -m 0700 -o "$EG_USER" -g "$EG_USER" /var/lib/edgeguard/acme-account # Cluster-TLS-Dir 0700 — Cluster-CA-Key + peer.key sind sehr # sensitiv (CA-Compromise = vollständiger Cluster-Bruch). Wird # beim API-Boot mit Material gefüllt; postinst legt nur den # Dir an damit der API-User schreiben kann. install -d -m 0700 -o "$EG_USER" -g "$EG_USER" /var/lib/edgeguard/cluster-tls # Backup-Verzeichnis: edgeguard-owned damit der scheduler / # API-Prozess schreiben kann. Mode 0750 — Backups enthalten # einen pg_dump der edgeguard-DB + license_key, also nicht # world-readable. install -d -m 0750 -o "$EG_USER" -g "$EG_USER" /var/backups/edgeguard # node.conf — node-lokale Cluster-Identität (NIEMALS in Sync # zwischen Peers). API auto-befüllt das beim ersten Boot mit # NODE_ID + Hostname + erster non-loopback-IPv4. Operator # darf danach editieren (z.B. MGMT_IP korrigieren wenn Box # mehrere Interfaces hat). if [ ! -f /etc/edgeguard/node.conf ]; then : > /etc/edgeguard/node.conf chown "$EG_USER":"$EG_USER" /etc/edgeguard/node.conf chmod 0644 /etc/edgeguard/node.conf fi # ── sudoers: HAProxy reload + (later) systemd-networkd reload # Damit edgeguard-api nach einer SSL- oder Netzwerk-Mutation # selbst reloaden kann ohne root zu sein. NOPASSWD ist auf # genau dieses Kommando beschränkt. cat > /etc/sudoers.d/edgeguard <<'SUDOERS' edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl reload haproxy.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl reload haproxy.service edgeguard ALL=(root) NOPASSWD: /usr/sbin/nft -f /etc/edgeguard/nftables.d/ruleset.nft edgeguard ALL=(root) NOPASSWD: /usr/sbin/nft list tables edgeguard ALL=(root) NOPASSWD: /usr/sbin/nft list table inet edgeguard edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl start wg-quick@*.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl restart wg-quick@*.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl stop wg-quick@*.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl enable wg-quick@*.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl disable wg-quick@*.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl start wg-quick@*.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl restart wg-quick@*.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl stop wg-quick@*.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl enable wg-quick@*.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl disable wg-quick@*.service edgeguard ALL=(root) NOPASSWD: /usr/bin/wg show all dump edgeguard ALL=(root) NOPASSWD: /usr/bin/wg show * # WireGuard Live-Reload ohne Tunnel-Abbruch: wg syncconf + wg-quick strip edgeguard ALL=(root) NOPASSWD: /usr/bin/wg syncconf * edgeguard ALL=(root) NOPASSWD: /usr/bin/wg-quick strip * # WireGuard symlink: /etc/wireguard/ ist root:root 700; edgeguard-api # legt Symlinks an damit wg-quick@ die Configs findet. edgeguard ALL=(root) NOPASSWD: /bin/ln -sf /etc/edgeguard/wireguard/* /etc/wireguard/* edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl reload squid.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl reload squid.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl reload unbound.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl reload unbound.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl restart unbound.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl restart unbound.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl reload chrony.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl reload chrony.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl restart chrony.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl restart chrony.service # DHCP (Kea): Service-Lifecycle an dhcp_settings.enabled gekoppelt. edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl restart kea-dhcp4-server.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl restart kea-dhcp4-server.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl stop kea-dhcp4-server.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl stop kea-dhcp4-server.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl enable kea-dhcp4-server.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl enable kea-dhcp4-server.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl disable kea-dhcp4-server.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl disable kea-dhcp4-server.service # RADIUS (FreeRADIUS): Service-Lifecycle an radius_settings.enabled gekoppelt. edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl restart freeradius.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl restart freeradius.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl stop freeradius.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl stop freeradius.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl enable freeradius.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl enable freeradius.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl disable freeradius.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl disable freeradius.service edgeguard ALL=(root) NOPASSWD: /usr/bin/apt-get update -qq edgeguard ALL=(root) NOPASSWD: /usr/bin/apt-get update # Auto-Update-Toggle (Settings → Automatische Updates) schreibt/entfernt # exakt diese Conf-Datei. Restrict auf den genauen Pfad damit edgeguard # NICHT beliebig in /etc/apt/apt.conf.d schreiben darf. edgeguard ALL=(root) NOPASSWD: /usr/bin/tee /etc/apt/apt.conf.d/52edgeguard-auto-updates edgeguard ALL=(root) NOPASSWD: /bin/rm -f /etc/apt/apt.conf.d/52edgeguard-auto-updates # Backup-Pfad: pg_dump als postgres-User. Whitelist exakt mit # --clean --if-exists --no-owner --no-acl + dem festen DB-Namen. edgeguard ALL=(postgres) NOPASSWD: /usr/bin/pg_dump --clean --if-exists --no-owner --no-acl edgeguard # Static-Routes: API ruft `sudo systemctl restart edgeguard-routes.service` # nach jedem Mutate, damit das apply-Skript die neue routes.conf anwendet. edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl restart edgeguard-routes.service # Network-Interfaces: API ruft `sudo systemctl restart edgeguard-interfaces.service` # nach jedem Mutate (Create/Update/Delete) von VLAN/bridge/bond-Interfaces. edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl restart edgeguard-interfaces.service # IP-Addresses: API ruft `sudo systemctl restart edgeguard-ipaddresses.service` # nach jedem Mutate (Create/Update/Delete). edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl restart edgeguard-ipaddresses.service # Self-Upgrade-Pfad (handlers/system.go → /system/upgrade). Whitelist # nur die exakte Unit-Form, damit edgeguard NICHT beliebige systemd- # Units anlegen darf. edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl reset-failed edgeguard-upgrade.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemd-run --unit=edgeguard-upgrade.service --description=EdgeGuard self-upgrade --collect bash /var/lib/edgeguard/upgrade.sh # Backup-Restore: gleiche Pattern wie Upgrade — Skript landet immer # unter /var/lib/edgeguard/restore.sh, Unit-Form ist fix. edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl reset-failed edgeguard-restore.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemd-run --unit=edgeguard-restore.service --description=EdgeGuard self-restore --collect bash /var/lib/edgeguard/restore.sh # Replication-Repair: Resync der Logical-Replication-Subscription vom # Primary (Config-Drift-Banner → "Resync erzwingen"). Skript landet immer # unter /var/lib/edgeguard/repair-replication.sh, Unit-Form ist fix. edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl reset-failed edgeguard-repair-replication.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemd-run --unit=edgeguard-repair-replication.service --description=EdgeGuard replication repair --collect bash /var/lib/edgeguard/repair-replication.sh # Keepalived reload: VIP-Settings-Änderung triggert keepalived-Reload. edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl reload-or-restart keepalived.service edgeguard ALL=(root) NOPASSWD: /bin/systemctl reload-or-restart keepalived.service # VIP-Schwenk-Test: dediziertes Script mit interner Input-Validierung. edgeguard ALL=(root) NOPASSWD: /usr/lib/edgeguard/vip-cmd.sh # CrowdSec management edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli decisions list -o json edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli decisions add * edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli decisions delete * edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli alerts list * edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli alerts delete * edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli bouncers list -o json edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli bouncers delete * edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli machines list -o json edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli machines delete * edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli collections list -o json edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli collections install * edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli collections remove * edgeguard ALL=(root) NOPASSWD: /usr/bin/cscli version # WAF agent service control edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl start edgeguard-waf.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl stop edgeguard-waf.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl enable edgeguard-waf.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl disable edgeguard-waf.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl restart edgeguard-waf.service # CrowdSec service toggle (start/stop/enable/disable) edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl start crowdsec.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl stop crowdsec.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl enable crowdsec.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl disable crowdsec.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl start crowdsec-firewall-bouncer.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl stop crowdsec-firewall-bouncer.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl enable crowdsec-firewall-bouncer.service edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl disable crowdsec-firewall-bouncer.service SUDOERS # ── Distro-Conf-Includes für die per-Service Renderer ───────── # Squid + Unbound lesen ihre Distro-Default-Conf, die wir per # Symlink/Drop-in auf unsere managed conf zeigen lassen müssen. # Renderer können das nicht selbst (kein Schreibrecht in /etc/squid # bzw. /etc/unbound/unbound.conf.d/), daher hier einmalig. # Plus: Squid + Unbound laufen als eigene User (squid/unbound), # nicht edgeguard. Damit sie unsere Conf lesen können, müssen # die Conf-Dirs world-readable sein (Configs ohne Secrets). install -d -m 0755 -o "$EG_USER" -g "$EG_USER" /etc/edgeguard/squid /etc/edgeguard/unbound # Squid: ersetzt die Distro-Datei durch Symlink (Backup .distro-bak) if [ -f /etc/squid/squid.conf ] && [ ! -L /etc/squid/squid.conf ]; then mv /etc/squid/squid.conf /etc/squid/squid.conf.distro-bak fi ln -sfn /etc/edgeguard/squid/squid.conf /etc/squid/squid.conf # Unbound: Drop-in im conf.d-Verzeichnis. Wir schreiben direkt # rein (statt /etc/edgeguard/unbound/...) weil das AppArmor- # Profil unbound nur /etc/unbound erlaubt. Datei gehört dem # edgeguard-User damit der Renderer sie überschreiben kann. install -d /etc/unbound/unbound.conf.d # Vorgänger-Symlink (aus früheren Versionen) wegräumen. if [ -L /etc/unbound/unbound.conf.d/edgeguard.conf ]; then rm /etc/unbound/unbound.conf.d/edgeguard.conf fi if [ ! -f /etc/unbound/unbound.conf.d/edgeguard.conf ]; then : > /etc/unbound/unbound.conf.d/edgeguard.conf fi chown "$EG_USER":"$EG_USER" /etc/unbound/unbound.conf.d/edgeguard.conf chmod 0644 /etc/unbound/unbound.conf.d/edgeguard.conf # Chrony: gleicher Pattern wie Unbound — Drop-in im conf.d, der # vom distro-default chrony.conf included wird. Datei gehört # edgeguard damit der Renderer sie überschreiben kann. install -d /etc/chrony/conf.d if [ -L /etc/chrony/conf.d/edgeguard.conf ]; then rm /etc/chrony/conf.d/edgeguard.conf fi if [ ! -f /etc/chrony/conf.d/edgeguard.conf ]; then : > /etc/chrony/conf.d/edgeguard.conf fi chown "$EG_USER":"$EG_USER" /etc/chrony/conf.d/edgeguard.conf chmod 0644 /etc/chrony/conf.d/edgeguard.conf # Kea DHCPv4: managed config unter /etc/edgeguard/kea, vom Distro- # Pfad symlinked (Distro-Original wird gesichert). Der Dienst läuft # NUR, wenn der Operator DHCP aktiviert (Default AUS) — kein # rogue DHCP-Server nach Install. Der Renderer enabled+restartet # kea-dhcp4-server, sobald dhcp_settings.enabled=true gesetzt wird. install -d /etc/kea if [ -f /etc/kea/kea-dhcp4.conf ] && [ ! -L /etc/kea/kea-dhcp4.conf ]; then mv /etc/kea/kea-dhcp4.conf /etc/kea/kea-dhcp4.conf.distro-bak fi ln -sfn /etc/edgeguard/kea/kea-dhcp4.conf /etc/kea/kea-dhcp4.conf systemctl disable --now kea-dhcp4-server.service 2>/dev/null || true # FreeRADIUS: managed clients.conf + authorize unter /etc/edgeguard/ # freeradius (setgid → Dateien erben Gruppe freeradius, 0640 → der # freeradius-Dienst kann lesen). Symlinks von den Distro-Pfaden. # Dienst default AUS (kein RADIUS bis Operator aktiviert). if getent group freeradius >/dev/null; then install -d -m 2750 -o "$EG_USER" -g freeradius /etc/edgeguard/freeradius else install -d -m 0750 -o "$EG_USER" -g "$EG_USER" /etc/edgeguard/freeradius fi if [ -d /etc/freeradius/3.0 ]; then if [ -e /etc/freeradius/3.0/clients.conf ] && [ ! -L /etc/freeradius/3.0/clients.conf ]; then mv /etc/freeradius/3.0/clients.conf /etc/freeradius/3.0/clients.conf.distro-bak fi ln -sfn /etc/edgeguard/freeradius/clients.conf /etc/freeradius/3.0/clients.conf if [ -e /etc/freeradius/3.0/mods-config/files/authorize ] && [ ! -L /etc/freeradius/3.0/mods-config/files/authorize ]; then mv /etc/freeradius/3.0/mods-config/files/authorize /etc/freeradius/3.0/mods-config/files/authorize.distro-bak fi install -d /etc/freeradius/3.0/mods-config/files ln -sfn /etc/edgeguard/freeradius/authorize /etc/freeradius/3.0/mods-config/files/authorize fi systemctl disable --now freeradius.service 2>/dev/null || true chmod 0440 /etc/sudoers.d/edgeguard # ── Sysctl-Profil für Edge-Gateway (NAT + HAProxy + Forwarding) ── # Voraussetzung für NAT/DNAT/Masquerade + sinnvolle Defaults # für eine high-throughput Forwarding-Box. Edit nicht von Hand # — Re-install vom Package überschreibt die Datei. Eigene # Tweaks gehören in eine Datei mit höherer Nummer als 99. rm -f /etc/sysctl.d/99-edgeguard-forward.conf # Vorgänger cat > /etc/sysctl.d/99-edgeguard.conf <<'SYSCTL' # ── Managed by edgeguard ──────────────────────────────────────────── # Lade-Reihenfolge: 99-* überschreibt distro-Defaults. Eigene # Operator-Tweaks: /etc/sysctl.d/99-zzz-local.conf (lexikografisch # später) — nicht in DIESE Datei! # ─── Forwarding (NAT/DNAT/Masquerade) ─────────────────────────────── net.ipv4.ip_forward = 1 net.ipv6.conf.all.forwarding = 1 net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.default.send_redirects = 0 net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 net.ipv4.conf.all.accept_source_route = 0 net.ipv6.conf.all.accept_source_route = 0 # ─── Reverse-Path-Filter (anti-spoof, loose-Modus für asymmetrisches # Routing wie Multi-WAN / WireGuard split) ───────────────────── net.ipv4.conf.all.rp_filter = 2 net.ipv4.conf.default.rp_filter = 2 # ─── Conntrack — Edge-Box trackt viele parallele Sessions ───────── net.netfilter.nf_conntrack_max = 524288 net.netfilter.nf_conntrack_tcp_timeout_established = 86400 net.netfilter.nf_conntrack_tcp_timeout_time_wait = 30 net.netfilter.nf_conntrack_tcp_timeout_close_wait = 30 net.netfilter.nf_conntrack_buckets = 131072 # ─── TCP/IP-Stack-Tuning für HAProxy + viele Backends ───────────── net.core.somaxconn = 65535 net.core.netdev_max_backlog = 16384 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.core.rmem_default = 262144 net.core.wmem_default = 262144 net.ipv4.tcp_rmem = 4096 87380 16777216 net.ipv4.tcp_wmem = 4096 65536 16777216 net.ipv4.tcp_max_syn_backlog = 65535 net.ipv4.tcp_fin_timeout = 15 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_keepalive_time = 300 net.ipv4.tcp_keepalive_probes = 5 net.ipv4.tcp_keepalive_intvl = 30 net.ipv4.tcp_mtu_probing = 1 net.ipv4.tcp_slow_start_after_idle = 0 net.ipv4.tcp_no_metrics_save = 1 net.ipv4.ip_local_port_range = 10240 65535 # ─── Modern congestion control + queueing (BBR + fq) ────────────── # Wenn der Kernel BBR nicht hat, fällt Linux still auf cubic zurück. net.ipv4.tcp_congestion_control = bbr net.core.default_qdisc = fq # ─── Anti-DoS / Hardening ───────────────────────────────────────── net.ipv4.tcp_syncookies = 1 net.ipv4.icmp_echo_ignore_broadcasts = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 net.ipv4.conf.all.log_martians = 1 kernel.kptr_restrict = 2 kernel.dmesg_restrict = 1 # ─── Memory ─────────────────────────────────────────────────────── vm.swappiness = 10 vm.dirty_ratio = 20 vm.dirty_background_ratio = 5 SYSCTL sysctl --system >/dev/null 2>&1 || true # ── Firewall-Logging via ulogd2 (NFLOG group 0) ────────────── # nft-Renderer emittiert `log prefix "edgeguard:" group 0` # für jede Rule mit log=true. ulogd2 subscribed auf netlink-group # 0 und schreibt JSON-Lines nach /var/log/edgeguard/firewall.jsonl. # Das UI tailt das File für Live-Log + Historie. install -d -m 0755 /var/log/edgeguard install -d -m 0755 -o "$EG_USER" -g "$EG_USER" /var/log/edgeguard if [ ! -f /var/log/edgeguard/firewall.jsonl ]; then : > /var/log/edgeguard/firewall.jsonl fi # ulogd2 läuft als root (eigener Daemon); File muss von ihm # schreibbar UND von edgeguard-API lesbar sein. chown root:"$EG_USER" /var/log/edgeguard/firewall.jsonl chmod 0640 /var/log/edgeguard/firewall.jsonl cat > /etc/ulogd.conf <<'ULOGD' # Managed by edgeguard — re-installation overwrites this file. # NFLOG group 0 → JSON-Lines pro Paket nach /var/log/edgeguard/firewall.jsonl # Format-Felder: oob.time.sec, oob.prefix (rule-id), src_ip, dst_ip, # src_port, dst_port, ip.protocol, raw.pktlen, oob.in/oob.out (iface). [global] logfile="/var/log/ulogd.log" loglevel=5 plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_inppkt_NFLOG.so" plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_IFINDEX.so" plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_IP2STR.so" plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_HWHDR.so" plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_raw2packet_BASE.so" plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_output_JSON.so" stack=fw1:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,mac2str1:HWHDR,json1:JSON [fw1] group=0 # qthreshold=1 + qtimeout=1: Kernel batched sonst Pakete bis 1024 oder # bis 1s. Mit 1/1 → jedes Paket SOFORT raus, kein Sammeln. Wichtig # damit die Live-Log-UI in real-time fließt statt in Bursts. qthreshold=1 qtimeout=1 [json1] sync=1 file="/var/log/edgeguard/firewall.jsonl" timestamp=1 boolean_label=1 ULOGD chmod 0644 /etc/ulogd.conf # Logrotate-Profil für firewall.jsonl — Default: daily, 14 days # comprimiert, copytruncate damit ulogd kein Reopen-Signal braucht. cat > /etc/logrotate.d/edgeguard-firewall <<'LOGROTATE' /var/log/edgeguard/firewall.jsonl { daily rotate 14 missingok notifempty compress delaycompress copytruncate create 0640 root edgeguard } LOGROTATE chmod 0644 /etc/logrotate.d/edgeguard-firewall # ulogd2 enablen + restarten (idempotent). Wenn das Paket nicht # da ist (Dependency-Konflikt o.ä.), nur warnen — die Firewall # läuft auch ohne Logger. if systemctl list-unit-files ulogd2.service >/dev/null 2>&1; then systemctl enable ulogd2.service >/dev/null 2>&1 || true systemctl restart ulogd2.service || \ echo "postinst: ulogd2.service restart failed (firewall logs disabled until fixed)" >&2 else echo "postinst: ulogd2.service not installed — install ulogd2 to enable firewall log" >&2 fi # ── Static-routes apply-script + systemd unit ──────────────── # Verwaltet aus /etc/edgeguard/routes.conf. `proto edgeguard` # markiert die Routen damit das flush keine fremden Routen # tötet (kernel/dhcp/static manuell gesetzt). cat > /usr/sbin/edgeguard-apply-routes <<'APPLYROUTES' #!/bin/bash # Managed by edgeguard — DO NOT EDIT. # Reads /etc/edgeguard/routes.conf (pipe-format) and applies via ip(8). set -e CONF=/etc/edgeguard/routes.conf # Existierende edgeguard-Routen weg, bevor wir neue setzen. Andere # Quellen (kernel/dhcp/manuell ohne proto) bleiben intakt. ip route flush proto 250 2>/dev/null || true [ -f "$CONF" ] || exit 0 while IFS='|' read -r dest gw dev metric table; do [ -z "$dest" ] && continue case "$dest" in '#'*) continue;; esac args=("$dest") [ -n "$gw" ] && args+=("via" "$gw") [ -n "$dev" ] && args+=("dev" "$dev") [ -n "$metric" ] && args+=("metric" "$metric") if [ -n "$table" ] && [ "$table" != "main" ]; then args+=("table" "$table") fi args+=("proto" "250") if ! ip route add "${args[@]}"; then echo "edgeguard-routes: failed to add: ip route add ${args[*]}" >&2 # weitermachen — eine fehlende Route soll nicht alle anderen # blockieren. fi done < "$CONF" APPLYROUTES chmod 0755 /usr/sbin/edgeguard-apply-routes chown root:root /usr/sbin/edgeguard-apply-routes # rt_protos-Eintrag für `proto edgeguard` (Symbolname statt # numerisch). Debian 13 hat /etc/iproute2 nicht als Default, # also conf.d-Pattern: /etc/iproute2/rt_protos.d/edgeguard.conf # überlagert die /usr/share/iproute2/rt_protos. install -d /etc/iproute2/rt_protos.d echo "250 edgeguard" > /etc/iproute2/rt_protos.d/edgeguard.conf chmod 0644 /etc/iproute2/rt_protos.d/edgeguard.conf cat > /etc/systemd/system/edgeguard-routes.service <<'ROUTESUNIT' [Unit] Description=EdgeGuard static-routes apply After=network-online.target Wants=network-online.target [Service] Type=oneshot ExecStart=/usr/sbin/edgeguard-apply-routes RemainAfterExit=yes [Install] WantedBy=multi-user.target ROUTESUNIT systemctl daemon-reload systemctl enable edgeguard-routes.service >/dev/null 2>&1 || true # Initialer Apply — leere /etc/edgeguard/routes.conf ist ok # (Skript exitet einfach ohne irgendwas zu tun). systemctl start edgeguard-routes.service 2>/dev/null || true # Initialer Stub damit `cat` im Skript nicht klagt if [ ! -f /etc/edgeguard/routes.conf ]; then : > /etc/edgeguard/routes.conf chown "$EG_USER":"$EG_USER" /etc/edgeguard/routes.conf fi # ── Network-Interfaces apply-script + systemd unit ─────────── # Verwaltet aus /etc/edgeguard/interfaces.conf. VLAN/bridge/bond- # Interfaces die der Operator anlegt werden sofort via # `systemctl restart edgeguard-interfaces.service` erstellt. # Ethernet + WireGuard werden vom OS bzw. wg-quick verwaltet, # nicht von diesem Skript. Diff-Ansatz analog ip-addresses. cat > /usr/sbin/edgeguard-apply-interfaces <<'APPLYIFACES' #!/bin/bash # Managed by edgeguard — DO NOT EDIT. CONF=/etc/edgeguard/interfaces.conf APPLIED=/var/lib/edgeguard/interfaces-applied.conf create_iface() { local typ="$1" name="$2" parent="$3" vlan_id="$4" mtu="$5" members="$6" case "$typ" in vlan) ip link set "$parent" up 2>/dev/null || true ip link add link "$parent" name "$name" type vlan id "$vlan_id" || return 1 ;; bridge) ip link add "$name" type bridge || return 1 if [ -n "$members" ]; then echo "$members" | tr ',' '\n' | while read -r m; do [ -n "$m" ] && ip link set "$m" master "$name" 2>/dev/null || true done fi ;; bond) ip link add "$name" type bond || return 1 if [ -n "$members" ]; then echo "$members" | tr ',' '\n' | while read -r m; do [ -n "$m" ] && { ip link set "$m" down 2>/dev/null; ip link set "$m" master "$name" 2>/dev/null || true; } done fi ;; *) return 0 ;; esac [ -n "$mtu" ] && ip link set "$name" mtu "$mtu" 2>/dev/null || true ip link set "$name" up 2>/dev/null || true } # Entferne Interfaces die im letzten Apply waren, aber jetzt nicht mehr if [ -f "$APPLIED" ]; then while IFS='|' read -r typ name rest; do [ -z "$typ" ] && continue case "$typ" in '#'*) continue;; esac if ! awk -F'|' -v n="$name" 'NR>2 && $2==n {found=1} END{exit !found}' "$CONF" 2>/dev/null; then ip link set "$name" down 2>/dev/null || true ip link del "$name" 2>/dev/null || true fi done < "$APPLIED" fi [ -f "$CONF" ] || { : > "$APPLIED"; exit 0; } # Erstelle/aktualisiere Interfaces aus Config while IFS='|' read -r typ name parent vlan_id mtu members; do [ -z "$typ" ] && continue case "$typ" in '#'*) continue;; esac if ip link show "$name" >/dev/null 2>&1; then # Typ-Änderung: altes Interface löschen und neu erstellen existing_type=$(ip -d link show "$name" 2>/dev/null | awk '/^[[:space:]]/{print $1; exit}') case "$typ" in vlan) expected="vlan" ;; bridge) expected="bridge_slave" ;; # bridge selbst hat keinen eigenen type-String bond) expected="bond_slave" ;; *) expected="" ;; esac current_type=$(ip -d link show "$name" 2>/dev/null | grep -oE 'vlan |bridge |bond ' | head -1 | tr -d ' ') if [ -n "$current_type" ] && [ "$current_type" != "$typ" ]; then ip link set "$name" down 2>/dev/null || true ip link del "$name" 2>/dev/null || true if ! create_iface "$typ" "$name" "$parent" "$vlan_id" "$mtu" "$members"; then echo "edgeguard-interfaces: failed to recreate $typ $name" >&2 fi else [ -n "$mtu" ] && ip link set "$name" mtu "$mtu" 2>/dev/null || true [ "$typ" = "vlan" ] && [ -n "$parent" ] && ip link set "$parent" up 2>/dev/null || true ip link set "$name" up 2>/dev/null || true fi else if ! create_iface "$typ" "$name" "$parent" "$vlan_id" "$mtu" "$members"; then echo "edgeguard-interfaces: failed to create $typ $name" >&2 fi fi done < "$CONF" cp "$CONF" "$APPLIED" APPLYIFACES chmod 0755 /usr/sbin/edgeguard-apply-interfaces chown root:root /usr/sbin/edgeguard-apply-interfaces cat > /etc/systemd/system/edgeguard-interfaces.service <<'IFACESUNIT' [Unit] Description=EdgeGuard managed network interfaces apply After=network-online.target Wants=network-online.target StartLimitIntervalSec=0 [Service] Type=oneshot ExecStart=/usr/sbin/edgeguard-apply-interfaces RemainAfterExit=yes [Install] WantedBy=multi-user.target IFACESUNIT systemctl daemon-reload systemctl enable edgeguard-interfaces.service >/dev/null 2>&1 || true # Initialer Apply — leere /etc/edgeguard/interfaces.conf ist ok systemctl start edgeguard-interfaces.service 2>/dev/null || true if [ ! -f /etc/edgeguard/interfaces.conf ]; then : > /etc/edgeguard/interfaces.conf chown "$EG_USER":"$EG_USER" /etc/edgeguard/interfaces.conf fi # ── IP-Addresses apply-script + systemd unit ───────────────── # Verwaltet aus /etc/edgeguard/ip-addresses.conf. Adressen die # der Operator über die GUI hinzufügt/entfernt werden sofort via # `systemctl restart edgeguard-ipaddresses.service` angewendet. # Diff-Ansatz: /var/lib/edgeguard/ip-addresses-applied.conf hält # den zuletzt angewendeten Stand; alte Adressen werden entfernt, # neue hinzugefügt — manuell gebundene Adressen bleiben intakt. cat > /usr/sbin/edgeguard-apply-ipaddresses <<'APPLYIP' #!/bin/bash # Managed by edgeguard — DO NOT EDIT. set -e CONF=/etc/edgeguard/ip-addresses.conf APPLIED=/var/lib/edgeguard/ip-addresses-applied.conf # Entferne Adressen die im letzten Apply-Lauf waren, aber jetzt nicht mehr if [ -f "$APPLIED" ]; then while IFS='|' read -r dev addrpfx; do [ -z "$dev" ] && continue case "$dev" in '#'*) continue;; esac # Nur entfernen wenn nicht mehr in der neuen Config if ! grep -qxF "${dev}|${addrpfx}" "$CONF" 2>/dev/null; then ip addr del "$addrpfx" dev "$dev" 2>/dev/null || true fi done < "$APPLIED" fi [ -f "$CONF" ] || { : > "$APPLIED"; exit 0; } # Füge neue Adressen hinzu while IFS='|' read -r dev addrpfx; do [ -z "$dev" ] && continue case "$dev" in '#'*) continue;; esac ip addr add "$addrpfx" dev "$dev" 2>/dev/null || true done < "$CONF" # Aktuelle Config als "applied" speichern cp "$CONF" "$APPLIED" APPLYIP chmod 0755 /usr/sbin/edgeguard-apply-ipaddresses chown root:root /usr/sbin/edgeguard-apply-ipaddresses cat > /etc/systemd/system/edgeguard-ipaddresses.service <<'IPADDRUNIT' [Unit] Description=EdgeGuard managed IP addresses apply After=network-online.target edgeguard-interfaces.service Wants=network-online.target StartLimitIntervalSec=0 [Service] Type=oneshot ExecStart=/usr/sbin/edgeguard-apply-ipaddresses RemainAfterExit=yes [Install] WantedBy=multi-user.target IPADDRUNIT systemctl daemon-reload systemctl enable edgeguard-ipaddresses.service >/dev/null 2>&1 || true systemctl start edgeguard-ipaddresses.service 2>/dev/null || true if [ ! -f /etc/edgeguard/ip-addresses.conf ]; then : > /etc/edgeguard/ip-addresses.conf chown "$EG_USER":"$EG_USER" /etc/edgeguard/ip-addresses.conf fi # ── Keepalived notify-scripts installieren ─────────────────── # Die Skripte liegen im Package unter /usr/lib/edgeguard/ und # werden von Keepalived als notify_master / notify_backup / check # aufgerufen. Kein Auto-Promote — keepalived-master.sh loggt nur. install -d -m 0755 /usr/lib/edgeguard for script in keepalived-check.sh keepalived-master.sh keepalived-backup.sh keepalived-gw-check.sh; do if [ -f "/usr/lib/edgeguard/${script}" ]; then chmod 0755 "/usr/lib/edgeguard/${script}" fi done # ── VIP-Schwenk-Script ─────────────────────────────────────── # Führt `ip addr add/del` für VIP-Failover-Tests aus. Läuft via # sudo (Whitelist in sudoers). Interne Input-Validierung verhindert # Command-Injection trotz breiter sudoers-Regel. cat > /usr/lib/edgeguard/vip-cmd.sh <<'VIPCMD' #!/bin/bash # vip-cmd.sh {add|del} {address/prefix} {device} # Wird von edgeguard-api via sudo für VIP-Schwenk-Tests aufgerufen. # Nach `ip addr add` werden squid/unbound/haproxy reloaded damit sie # die neu aktive VIP sofort binden. set -e action="$1" addrpfx="$2" dev="$3" case "$action" in add|del) ;; *) echo "vip-cmd: ungültige action '$action'" >&2; exit 1 ;; esac [[ -z "$addrpfx" || -z "$dev" ]] && { echo "vip-cmd: fehlende Parameter" >&2; exit 1; } [[ "$addrpfx" =~ ^[0-9a-fA-F.:\/]+$ ]] || { echo "vip-cmd: ungültige Adresse '$addrpfx'" >&2; exit 1; } [[ "$dev" =~ ^[a-zA-Z0-9._-]+$ ]] || { echo "vip-cmd: ungültiges Device '$dev'" >&2; exit 1; } ip addr "$action" "$addrpfx" dev "$dev" if [ "$action" = "add" ]; then for svc in squid.service unbound.service haproxy.service; do systemctl is-active --quiet "$svc" && systemctl reload "$svc" 2>/dev/null || true done fi VIPCMD chmod 0755 /usr/lib/edgeguard/vip-cmd.sh # ── Self-signed default cert so HAProxy starts cleanly ─────── # HAProxy `bind :443 ssl crt /etc/edgeguard/tls/` needs at least # one PEM in the directory to come up. Operator runs certbot # later; until then, browsers see an unverified cert which is # the expected first-boot UX. DEFAULT_PEM="/etc/edgeguard/tls/_default.pem" if [ ! -f "$DEFAULT_PEM" ]; then HOSTNAME_FQDN="$(hostname -f 2>/dev/null || hostname)" TMP_KEY="$(mktemp)" TMP_CRT="$(mktemp)" openssl req -x509 -nodes -newkey rsa:2048 \ -keyout "$TMP_KEY" -out "$TMP_CRT" \ -days 3650 \ -subj "/CN=$HOSTNAME_FQDN" \ -addext "subjectAltName = DNS:$HOSTNAME_FQDN,DNS:localhost" \ >/dev/null 2>&1 cat "$TMP_CRT" "$TMP_KEY" > "$DEFAULT_PEM" chown "$EG_USER:$EG_USER" "$DEFAULT_PEM" chmod 0640 "$DEFAULT_PEM" rm -f "$TMP_KEY" "$TMP_CRT" fi # ── Pre-flight: validate embedded migration set ────────────── # Catches duplicate version prefixes BEFORE we touch the DB, # so a broken upgrade can't half-apply migrations and leave # the cluster wedged (mail-gateway 2026-05-08 incident). if ! /usr/bin/edgeguard-ctl migrate check; then echo "postinst: embedded migrations failed validation — aborting" >&2 exit 1 fi # ── PostgreSQL: ensure role + database exist ───────────────── # Requires postgresql-16 (or -17) running locally — guaranteed # by Depends. Idempotent — re-runs on upgrade are no-ops. if ! /usr/bin/edgeguard-ctl initdb; then echo "postinst: edgeguard-ctl initdb failed — aborting" >&2 exit 1 fi # ── Apply pending schema migrations ────────────────────────── if ! sudo -n -u "$EG_USER" /usr/bin/edgeguard-ctl migrate up; then echo "postinst: edgeguard-ctl migrate up failed — aborting" >&2 exit 1 fi # ── Replikation reconcilen (Publication / Grants / Subscription) ── # Ersetzt die frühere feste ADD-TABLE-Logik (die network_interfaces/ # ip_addresses bei JEDEM Upgrade fälschlich wieder in die Publication # zog — beide sind node-lokal). edgeguard-ctl erkennt die Rolle selbst # und bringt idempotent den Soll-Zustand: # Publisher : fehlende Shared-Tables ADD, node-lokale (localOnlyTables) # DROP, GRANT SELECT für den Replikations-User (deckt per # Migration neu hinzugekommene Tabellen ab — sonst hängt # deren tablesync in 'd'). # Subscriber: neue Tabellen leeren + REFRESH PUBLICATION. # Standalone: No-Op. # Läuft als root → buildPsqlCmd nutzt `sudo -u postgres psql` (Superuser). # Best-effort: ein Reconcile-Fehler darf das Upgrade nie abbrechen. /usr/bin/edgeguard-ctl cluster-reconcile-replication 2>&1 || true # ── CrowdSec IDS installation ───────────────────────────────────────── # Install CrowdSec if not present. We use the official CrowdSec APT repo. # Only done on initial install (not on upgrade) to avoid overwriting # user configuration. if [ "$1" = "configure" ] && [ -z "$2" ]; then if ! command -v cscli >/dev/null 2>&1; then echo "postinst: installing CrowdSec..." # Add CrowdSec repo key install -d -m 0755 /etc/apt/keyrings curl -s https://packagecloud.io/crowdsec/crowdsec/gpgkey | \ gpg --dearmor -o /etc/apt/keyrings/crowdsec_crowdsec-archive-keyring.gpg 2>/dev/null || true # Add repo (bookworm compat for Trixie) echo "deb [signed-by=/etc/apt/keyrings/crowdsec_crowdsec-archive-keyring.gpg] https://packagecloud.io/crowdsec/crowdsec/debian/ bookworm main" \ > /etc/apt/sources.list.d/crowdsec.list apt-get update -qq 2>/dev/null || true DEBIAN_FRONTEND=noninteractive apt-get install -y -qq crowdsec crowdsec-firewall-bouncer-nftables 2>/dev/null || true # Install default collections if command -v cscli >/dev/null 2>&1; then cscli collections install crowdsecurity/linux crowdsecurity/haproxy 2>/dev/null || true fi fi fi # ── CrowdSec HAProxy-Acquisition auf journald zeigen ────────── # EdgeGuards HAProxy loggt nach journald (log /dev/log local0/1) — # es gibt KEINE /var/log/haproxy.log. `cscli setup` rät aber auf eine # datei-basierte Quelle → CrowdSec liest nichts → die HTTP-/CVE- # Szenarien laufen leer (WAF-artiger Web-Schutz tot). Wir setzen die # Acquisition deterministisch auf die haproxy.service-Journal-Unit # (analog zur sshd-Quelle). Läuft auf JEDEM configure (self-healing # auch für Bestandsinstalls). Admin-Custom bleibt unangetastet: nur # überschreiben, wenn Datei fehlt / noch die kaputte Datei-Default # (/var/log/haproxy.log) enthält / von uns stammt. if command -v cscli >/dev/null 2>&1; then CS_HAPROXY_ACQUIS="/etc/crowdsec/acquis.d/setup.haproxy.yaml" if [ ! -f "$CS_HAPROXY_ACQUIS" ] \ || grep -q '/var/log/haproxy.log' "$CS_HAPROXY_ACQUIS" 2>/dev/null \ || grep -q 'Managed by EdgeGuard' "$CS_HAPROXY_ACQUIS" 2>/dev/null; then install -d -m 0755 /etc/crowdsec/acquis.d cat > "$CS_HAPROXY_ACQUIS" <<'ACQUIS' # Managed by EdgeGuard. HAProxy loggt nach journald (log /dev/log local0/1), # es gibt keine /var/log/haproxy.log. journalctl-Quelle analog zur sshd-Acquis. source: journalctl journalctl_filter: - "_SYSTEMD_UNIT=haproxy.service" labels: type: haproxy ACQUIS # SIGHUP-Reload lädt die Acquisition neu; Restart als Fallback. systemctl reload crowdsec 2>/dev/null \ || systemctl restart crowdsec 2>/dev/null || true fi fi # ── Render initial service configs ─────────────────────────── # Writes /etc/edgeguard/haproxy/haproxy.cfg + nftables.d/ # ruleset.nft from the (just-migrated, empty) PG state. # # haproxy bekommt --no-reload (drop-in unten zeigt erst danach # auf unsere cfg; wir restarten explizit); nftables muss aber # aktiv reloadet werden, sonst läuft das Kernel-Set bei Template- # Änderungen (z.B. neue anti-lockout-Ports) hinterher. if ! sudo -n -u "$EG_USER" /usr/bin/edgeguard-ctl render-config --only=haproxy --no-reload; then echo "postinst: edgeguard-ctl render-config (haproxy) failed — aborting" >&2 exit 1 fi if ! sudo -n -u "$EG_USER" /usr/bin/edgeguard-ctl render-config --only=nftables; then echo "postinst: edgeguard-ctl render-config (nftables) failed — aborting" >&2 exit 1 fi # Keepalived-Config rendern (optional — nur wenn VIPs konfiguriert) # /etc/keepalived/ wird vom keepalived-Paket als root angelegt; # edgeguard braucht Schreibrecht damit der Config-Generator den # atomic-write (tempfile → rename) durchführen kann. install -d -m 0755 /etc/keepalived chown "$EG_USER":"$EG_USER" /etc/keepalived # ── keepalived systemd drop-in: Boot-Race-Fix ────────────────── # keepalived referenziert VIP-Devices (vlanXXX), die erst von # edgeguard-interfaces.service angelegt werden. Ohne Ordering startet # keepalived vor den VLANs (beide nur After=network-online.target) → # "interface vlanX doesn't exist" → permanenter CONFIG-Crash OHNE # Auto-Recovery (keepalived bleibt nach Reboot tot). After=/Wants= # wartet auf die Interfaces; Restart=on-failure ist das Sicherheitsnetz. install -d /etc/systemd/system/keepalived.service.d cat > /etc/systemd/system/keepalived.service.d/10-edgeguard.conf <<'KEEPALIVEDDROPIN' [Unit] After=edgeguard-interfaces.service Wants=edgeguard-interfaces.service StartLimitIntervalSec=0 [Service] Restart=on-failure RestartSec=3 KEEPALIVEDDROPIN systemctl daemon-reload sudo -n -u "$EG_USER" /usr/bin/edgeguard-ctl render-config --only=keepalived || true if [ -f /etc/keepalived/keepalived.conf ]; then systemctl enable keepalived >/dev/null 2>&1 || true systemctl is-active --quiet keepalived \ && systemctl reload keepalived \ || systemctl start keepalived || true fi # ── HAProxy systemd drop-in: read EdgeGuard config ─────────── # Keeps the distro /etc/haproxy/haproxy.cfg untouched (it's a # conffile of the haproxy package). Drop-in is reversible by # removing the file + daemon-reload. install -d /etc/systemd/system/haproxy.service.d if [ -f /etc/edgeguard/systemd/haproxy-edgeguard.conf ]; then install -m 0644 /etc/edgeguard/systemd/haproxy-edgeguard.conf \ /etc/systemd/system/haproxy.service.d/edgeguard.conf fi # ── OWASP Core Rule Set (CRS) für edgeguard-waf ───────────────────── # Nur beim Erstinstall — kein Überschreiben bei Upgrades damit # admins angepasste crs-setup.conf behalten können. WAF_CRS_DIR="/usr/share/edgeguard/waf/crs" if [ "$1" = "configure" ] && [ -z "$2" ] && [ ! -d "$WAF_CRS_DIR/rules" ]; then CRS_VERSION="4.7.0" echo "postinst: installing OWASP CRS v${CRS_VERSION}..." install -d -m 0755 "$WAF_CRS_DIR/rules" CRS_TMP="$(mktemp -d)" if curl -sL --max-time 60 \ "https://github.com/coreruleset/coreruleset/archive/refs/tags/v${CRS_VERSION}.tar.gz" \ -o "${CRS_TMP}/crs.tar.gz" 2>/dev/null; then tar xzf "${CRS_TMP}/crs.tar.gz" -C "${CRS_TMP}" 2>/dev/null || true CRS_SRC="${CRS_TMP}/coreruleset-${CRS_VERSION}" if [ -f "${CRS_SRC}/crs-setup.conf.example" ]; then install -m 0644 "${CRS_SRC}/crs-setup.conf.example" "${WAF_CRS_DIR}/crs-setup.conf" install -m 0644 "${CRS_SRC}/rules/"* "${WAF_CRS_DIR}/rules/" 2>/dev/null || true chown -R "$EG_USER":"$EG_USER" "$WAF_CRS_DIR" echo "postinst: OWASP CRS v${CRS_VERSION} installed to ${WAF_CRS_DIR}" else echo "postinst: CRS download succeeded but layout unexpected — skipping" >&2 fi else echo "postinst: CRS download failed (no internet?) — WAF usable without CRS" >&2 fi rm -rf "${CRS_TMP}" fi # ── systemd: pick up new units + restart haproxy with our cfg systemctl daemon-reload systemctl restart haproxy.service || true systemctl enable edgeguard-api.service edgeguard-scheduler.service edgeguard-waf.service || true # restart (NICHT enable --now): das neue Unit-File könnte ein # geänderter ReadWritePaths-Block sein (Befund 1.0.87: # /var/lib/apt fehlte → apt-get update aus dem Update-Banner- # Handler schrieb in read-only Filesystem und failte still). # daemon-reload allein lädt die neue Unit nur für NÄCHSTE Starts; # der laufende Daemon behält das alte Sandbox-Snapshot bis zum # restart. Bei Erst-Install ist nichts running, dann ist das # ein normaler Start. systemctl restart edgeguard-api.service edgeguard-scheduler.service edgeguard-waf.service || true # WireGuard-Sicherung: enabled-aber-inaktive wg-quick@-Interfaces # nach dem API-Restart starten. Der API-Renderer kann wg-quick@ # kurz flippen; falls er dabei fehlschlägt, stellt dieser Block # das Interface wieder her ohne manuellen Eingriff. while IFS= read -r wg_unit; do [ -n "$wg_unit" ] || continue systemctl is-active --quiet "$wg_unit" || systemctl start "$wg_unit" 2>/dev/null || true done < <(systemctl list-unit-files 'wg-quick@*.service' --state=enabled --no-legend 2>/dev/null | awk '{print $1}') ;; abort-upgrade|abort-remove|abort-deconfigure) ;; *) echo "postinst called with unknown argument \`$1'" >&2 exit 1 ;; esac #DEBHELPER# exit 0