Files
edgeguard-native/management-ui/src/i18n/locales/de/common.json
Debian b031725dfe feat(routes): Static-Routes-Management + Live-View (Networks-Tab)
Migration 0019: static_routes (id, destination, gateway, dev, metric,
table_name, active, comment).

internal/services/staticroutes/:
  - CRUD-Repo
  - Generator schreibt /etc/edgeguard/routes.conf (pipe-format) und
    triggert `sudo systemctl restart edgeguard-routes.service`
  - LiveAll() ruft `ip -j route show table all` und parsed JSON

internal/handlers/routes.go:
  GET /api/v1/routes           — managed (DB)
  POST/PUT/DELETE              — CRUD (re-render + apply on mutate)
  GET /api/v1/routes/live      — kernel-state via ip(8)

postinst:
  - /usr/sbin/edgeguard-apply-routes (root-owned shell-script). Liest
    routes.conf, flusht `proto 250` (= edgeguard), setzt neue Routen
    mit proto 250. Andere Quellen (kernel/dhcp/manuell) bleiben
    unangetastet.
  - /etc/systemd/system/edgeguard-routes.service (Type=oneshot,
    After=network-online.target). Beim Boot automatisch via
    multi-user.target.
  - /etc/iproute2/rt_protos.d/edgeguard.conf — Symbol "edgeguard" =
    250 damit `ip route show proto edgeguard` funktioniert.
    (Debian 13 hat kein /etc/iproute2 default → .d-Pattern statt
    rt_protos-Anhängen.)
  - sudoers: edgeguard ALL=(root) NOPASSWD: /usr/bin/systemctl
    restart edgeguard-routes.service

UI: Networks-Page jetzt mit Tabs (Interfaces + Routen). Routes-Tab
hat zwei Cards:
  - Live-Routen (read-only, 30s refresh, `proto edgeguard` farblich
    hervorgehoben)
  - Verwaltete Routen (CRUD-Tabelle, Add/Edit-Modal mit destination/
    gateway/dev/metric/table/active/comment)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 23:50:26 +02:00

761 lines
33 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"app": {
"title": "EdgeGuard",
"subtitle": "Native Reverse-Proxy / VPN / Firewall"
},
"nav": {
"dashboard": "Dashboard",
"domains": "Domains",
"backends": "Backends",
"routing": "Routing",
"networks": "Netzwerk-Interfaces",
"ipAddresses": "IP-Adressen",
"ssl": "SSL-Zertifikate",
"vpn": "VPN",
"wireguard": "WireGuard",
"forwardProxy": "Forward-Proxy",
"dns": "DNS",
"ntp": "Zeit (NTP)",
"firewall": "Firewall",
"firewallLive": "Firewall-Log",
"cluster": "Cluster",
"logs": "Logs",
"backups": "Backups",
"license": "Lizenz",
"settings": "Einstellungen",
"section": {
"overview": "Übersicht",
"routing": "Routing",
"network": "Netzwerk",
"security": "Sicherheit",
"system": "System"
}
},
"fw": {
"title": "Firewall",
"intro": "Fortigate-Style: Regeln aus Zonen × Adress-Objekten/Gruppen × Services/Service-Gruppen × Action. NAT separat. Top-down, first-match.",
"tabs": {
"rules": "Regeln",
"nat": "NAT",
"live": "Live-Log",
"zones": "Zonen",
"addrObj": "Adress-Objekte",
"addrGrp": "Adress-Gruppen",
"services": "Services",
"svcGrp": "Service-Gruppen"
},
"zone": {
"name": "Name",
"description": "Beschreibung",
"builtin": "vordefiniert",
"builtinHint": "Vordefinierte Zonen können nicht gelöscht werden — Renderer und Anti-Lockout-Regeln verlassen sich darauf.",
"builtinNameLocked": "Name vordefiniert — kann nicht geändert werden, weil bestehende Regeln und Interfaces ihn referenzieren.",
"namePattern": "Nur Kleinbuchstaben, Ziffern, _ und -; muss mit Buchstaben beginnen, max. 32 Zeichen.",
"add": "Zone hinzufügen",
"edit": "Zone bearbeiten",
"deleteConfirm": "Zone {{name}} wirklich löschen?"
},
"ao": {
"name": "Name", "kind": "Typ", "value": "Wert", "description": "Beschreibung",
"add": "Adress-Objekt hinzufügen", "edit": "Adress-Objekt bearbeiten",
"deleteConfirm": "Adress-Objekt {{name}} wirklich löschen?"
},
"ag": {
"name": "Name", "members": "Mitglieder", "description": "Beschreibung",
"add": "Adress-Gruppe hinzufügen", "edit": "Adress-Gruppe bearbeiten",
"selectMembers": "Adress-Objekte wählen",
"deleteConfirm": "Adress-Gruppe {{name}} wirklich löschen?"
},
"svc": {
"name": "Name", "proto": "Protokoll", "ports": "Ports",
"portStart": "Port (Start)", "portEnd": "Port (Ende)",
"description": "Beschreibung", "builtinHint": "Vordefiniert — nicht editierbar",
"add": "Service hinzufügen", "edit": "Service bearbeiten",
"deleteConfirm": "Service {{name}} wirklich löschen?"
},
"sg": {
"name": "Name", "members": "Mitglieder", "description": "Beschreibung",
"add": "Service-Gruppe hinzufügen", "edit": "Service-Gruppe bearbeiten",
"selectMembers": "Services wählen",
"deleteConfirm": "Service-Gruppe {{name}} wirklich löschen?"
},
"rule": {
"name": "Name", "priority": "Priority", "enabled": "Aktiv", "log": "Logging",
"action": "Aktion", "src": "Quelle", "dst": "Ziel", "service": "Service",
"srcZone": "Quell-Zone", "dstZone": "Ziel-Zone",
"srcKind": "Quell-Typ", "dstKind": "Ziel-Typ",
"object": "Adress-Objekt", "group": "Adress-Gruppe",
"serviceKind": "Service-Typ", "serviceGroup": "Service-Gruppe",
"comment": "Kommentar",
"add": "Regel hinzufügen", "edit": "Regel bearbeiten",
"deleteConfirm": "Diese Regel wirklich löschen?"
},
"nat": {
"name": "Name", "priority": "Priority", "kind": "Typ", "enabled": "Aktiv",
"match": "Match", "target": "Ziel",
"inZone": "Eingangs-Zone", "outZone": "Ausgangs-Zone", "proto": "Protokoll",
"matchSrcCidr": "Source-CIDR (Match)", "matchDstCidr": "Dest-CIDR (Match)",
"matchDstCidrHint": "leer = jede dest-IP (z.B. öffentliche IP der Box)",
"dportStart": "Port (Start)", "dportEnd": "Port (Ende)",
"targetAddr": "Ziel-Adresse", "targetPortStart": "Ziel-Port (Start)", "targetPortEnd": "Ziel-Port (Ende)",
"comment": "Kommentar",
"add": "NAT-Regel hinzufügen", "edit": "NAT-Regel bearbeiten",
"deleteConfirm": "Diese NAT-Regel wirklich löschen?"
},
"sys": {
"title": "System-Regeln (immer aktiv)",
"chain": "Chain", "match": "Match", "action": "Aktion", "note": "Hinweis",
"policy": "Default-Policy",
"policyValue": "Eingang DROP — alles muss explizit erlaubt werden.",
"order": "Auswertung",
"orderValue": "System-Regeln zuerst, danach Operator-Regeln top-down (priority asc, first-match).",
"lockout": "Anti-Lockout",
"lockoutValue": "SSH (22) und Management-UI (443) sind immer erreichbar — können auch vom Operator nicht versehentlich gesperrt werden."
}
},
"networks": {
"title": "Netzwerk",
"intro": "Interfaces (Ethernet/VLAN/Bond/Bridge) und Static-Routes. Live-Discovery + deklarative Konfiguration aus der DB.",
"tabs": {
"interfaces": "Interfaces",
"routes": "Routen"
},
"systemDiscovered": "System-Interfaces (read-only)",
"addInterface": "Interface hinzufügen",
"editInterface": "Interface bearbeiten",
"name": "Name",
"type": "Typ",
"parent": "Parent-Interface",
"selectParent": "Parent wählen",
"vlan": "VLAN",
"vlanId": "VLAN-ID",
"composition": "Zusammensetzung",
"members": "Member-Interfaces",
"selectMembers": "Physische Interfaces wählen",
"membersRequired": "Mindestens ein Member-Interface erforderlich",
"membersHintBridge": "Eine Bridge bündelt mehrere physische Ports auf L2 — typisch zwei Ports für einen Software-Switch.",
"membersHintBond": "Ein Bond aggregiert mehrere physische Ports zu einem logischen Link (LACP / active-backup).",
"role": "Zone",
"roleHint": "Zonen kommen aus Firewall → Zonen. Eigene Zonen (z.B. iot, guest) lassen sich dort anlegen.",
"mtu": "MTU",
"active": "Aktiv",
"description": "Beschreibung",
"actions": "Aktionen",
"deleteConfirm": "Interface {{name}} wirklich löschen?"
},
"ips": {
"title": "IP-Adressen",
"intro": "Adressen, die das Betriebssystem zeigt (Read-only oben) plus die Adressen, die EdgeGuard zusätzlich verwaltet — inklusive VIPs für Cluster-Failover.",
"systemDiscovered": "Adressen am Kernel (read-only)",
"managedTitle": "Verwaltete Adressen",
"family": "Familie",
"addAddress": "Adresse hinzufügen",
"editAddress": "Adresse bearbeiten",
"interface": "Interface",
"selectInterface": "Interface wählen",
"address": "Adresse",
"prefix": "Prefix",
"vip": "VIP",
"vipFlag": "Als VIP markieren",
"vipPriority": "VIP-Priorität (Cluster-Failover)",
"active": "Aktiv",
"description": "Beschreibung",
"actions": "Aktionen",
"deleteConfirm": "Adresse {{addr}} wirklich löschen?"
},
"auth": {
"loginTitle": "Anmelden",
"email": "E-Mail",
"password": "Passwort",
"login": "Anmelden",
"logout": "Abmelden",
"loginFailed": "Anmeldung fehlgeschlagen",
"loggedInAs": "Angemeldet als"
},
"setup": {
"title": "Erst-Einrichtung",
"intro": "Lege den Admin-Account an, gib die öffentliche FQDN an und optional einen Lizenzschlüssel. Ohne Lizenz startet eine 30-Tage-Trial.",
"adminEmail": "Admin-E-Mail",
"adminPassword": "Admin-Passwort",
"passwordRule": "Mindestens 12 Zeichen.",
"fqdn": "Öffentliche FQDN",
"acmeEmail": "ACME-/Let's-Encrypt-E-Mail",
"licenseKey": "Lizenzschlüssel (optional)",
"submit": "Setup abschließen",
"successTitle": "Setup abgeschlossen",
"successHint": "Du wirst zur Anmeldung weitergeleitet."
},
"dashboard": {
"title": "Dashboard",
"welcomeHint": "Übersicht aller laufenden EdgeGuard-Komponenten."
},
"domains": {
"title": "Domains",
"intro": "Verwalte FQDNs, die HAProxy terminiert. Optionales Primary-Backend als Catch-all; Pfad-Routing via Routing-Regeln.",
"addDomain": "Domain hinzufügen",
"editDomain": "Domain bearbeiten",
"name": "Name",
"active": "Aktiv",
"primaryBackend": "Primary-Backend",
"primaryBackendHint": "Catch-all-Backend für Requests, die kein Routing-Regel-Match haben. Optional — leer lassen, wenn alles über Routing-Regeln läuft.",
"selectBackend": "Backend wählen",
"noBackend": "kein Backend",
"httpToHttps": "HTTP→HTTPS",
"hsts": "HSTS",
"notes": "Notizen",
"actions": "Aktionen",
"edit": "Bearbeiten",
"delete": "Löschen",
"deleteConfirm": "Domain {{name}} wirklich löschen?"
},
"backends": {
"title": "Backends",
"intro": "Upstream-Pools (Backend = N Server). HAProxy verteilt laut LB-Algorithmus; Health-Check-Pfad aktiviert HTTP-Probes alle 5s pro Server.",
"addBackend": "Backend-Pool hinzufügen",
"editBackend": "Backend-Pool bearbeiten",
"name": "Name",
"scheme": "Schema",
"target": "Ziel",
"healthCheck": "Health-Check-Pfad",
"active": "Aktiv",
"usedBy": "Genutzt von",
"noDomain": "keine Domain",
"attachedDomains": "Domains",
"attachedDomainsHint": "Domains, die dieses Backend als Primary verwenden. Auswahl umkonfiguriert die Domains direkt — gleiche Quelle wie der Backend-Picker im Domain-Modal.",
"selectDomains": "Domains wählen",
"lbAlgo": "Load-Balancing",
"lbAlgoHint": "roundrobin = gleichmäßig, leastconn = an den Server mit wenigsten Verbindungen, source = sticky per Client-IP (für stateful Apps ohne shared session).",
"websocket": "WebSocket-Support",
"websocketHint": "An: erlaubt langlebige WebSocket-/Long-Poll-Verbindungen (z. B. Proxmox-Console, SSH-WS, AsyncAPI) — Tunnel-Idle 1h statt 60s. Aus: strikte HTTP-Timeouts.",
"servers": "Server",
"noServers": "kein Server",
"nServers": "{{n}} Server",
"serversIn": "Server in „{{name}}\"",
"serverHintCreate": "Speichern legt nur den Pool an. Server kommen im nächsten Schritt — Pool öffnen → „Server hinzufügen\".",
"actions": "Aktionen",
"deleteConfirm": "Backend-Pool {{name}} wirklich löschen? Alle Server im Pool werden mitentfernt.",
"server": {
"intro": "Upstream-Server im Pool. Reihenfolge in HAProxy egal — der LB-Algorithmus entscheidet.",
"add": "Server hinzufügen",
"edit": "Server bearbeiten",
"name": "Server-Name",
"address": "Adresse",
"port": "Port",
"target": "Endpoint",
"weight": "Gewicht",
"weightHint": "0256. Höher = mehr Traffic. 100 = Standard.",
"backup": "Backup",
"backupHint": "Backup-Server werden nur angesprochen, wenn alle primären Server (non-backup) down sind.",
"empty": "Noch keine Server im Pool. „Server hinzufügen\" startet damit.",
"deleteConfirm": "Server {{name}} wirklich löschen?"
}
},
"routing": {
"title": "Routing-Regeln",
"intro": "Pfad-Präfix → Backend-Mapping pro Domain. Niedrige Priority gewinnt; Catch-all per Domain.primary_backend.",
"addRule": "Regel hinzufügen",
"editRule": "Regel bearbeiten",
"domain": "Domain",
"pathPrefix": "Pfad-Präfix",
"backend": "Backend",
"priority": "Priorität",
"active": "Aktiv",
"actions": "Aktionen",
"selectDomain": "Domain wählen",
"selectBackend": "Backend wählen",
"deleteConfirm": "Diese Routing-Regel wirklich löschen?"
},
"cluster": {
"title": "Cluster",
"intro": "{{count}} Node(s) registriert. Multi-Node-Cluster (KeyDB Active-Active + PG Streaming Replication) folgt in einem späteren Release.",
"id": "Node-ID",
"fqdn": "FQDN",
"role": "Rolle",
"joinedAt": "Beigetreten",
"self": "diese Node"
},
"ssl": {
"title": "SSL-Zertifikate",
"intro": "TLS-Zertifikate verwalten — entweder per Let's Encrypt automatisch ausstellen oder eigene PEMs hochladen. HAProxy lädt nach jeder Änderung automatisch neu.",
"tabLE": "Let's Encrypt",
"tabUpload": "Eigenes Zertifikat",
"leIntro": "Domain wählen, Issue klicken — EdgeGuard löst HTTP-01 über die ACME-Webroot, schreibt das PEM nach /etc/edgeguard/tls/ und reloaded HAProxy.",
"uploadIntro": "Eigenes Zertifikat hochladen. Format: PEM-encoded. Cert + optional Chain + Private Key. EdgeGuard prüft die Cert/Key-Übereinstimmung vor dem Schreiben.",
"uploadHint": "Tipp: bei Let's-Encrypt-Renewals nicht hier hochladen — den LE-Tab nutzen.",
"domain": "Domain",
"selectDomain": "Domain wählen oder eintippen",
"domainExtra": "Inkludiert Management-FQDN (aus Setup), Cluster-Knoten und Operator-Domains. Du kannst auch eine andere Domain tippen, sofern DNS schon auf die Box zeigt.",
"fqdnHintMgmt": "Management-FQDN",
"fqdnHintCluster": "Cluster · {{role}}",
"issuer": "Issuer",
"status": "Status",
"expiresIn": "Gültig noch",
"expiredAgo": "abgelaufen vor {{days}} Tagen",
"actions": "Aktionen",
"issueButton": "Zertifikat anfordern",
"uploadButton": "Hochladen",
"issueSuccess": "Zertifikat ausgestellt + installiert.",
"uploadSuccess": "Zertifikat hochgeladen + installiert.",
"deleteConfirm": "Zertifikat für {{domain}} löschen? HAProxy fällt für diese Domain auf das Default-Cert zurück.",
"installedTitle": "Installierte Zertifikate",
"certPem": "Zertifikat (PEM)",
"chainPem": "Chain (PEM, optional)",
"keyPem": "Private Key (PEM)"
},
"settings": {
"title": "Einstellungen",
"intro": "System-Information und Setup-Status. Bearbeitbare Werte folgen in einem späteren Release.",
"systemInfo": "System",
"version": "Version",
"status": "Status",
"setupInfo": "Setup",
"adminEmail": "Admin-E-Mail",
"fqdn": "FQDN",
"setupCompleted": "Setup abgeschlossen"
},
"update": {
"available": "Update verfügbar: Version {{version}}",
"multiPackageHint": "{{count}} Pakete werden aktualisiert.",
"applyNow": "Jetzt installieren",
"confirmTitle": "Update jetzt installieren?",
"confirmDesc": "Pakete werden auf Version {{version}} aktualisiert. edgeguard-api + scheduler restarten (~2-5s), HAProxy/nft/WG/Squid/Unbound/Chrony laufen durch.",
"checkNow": "Auf Updates prüfen",
"checkDone": "Update verfügbar",
"noUpdate": "Keine neuen Updates",
"checkFailed": "Update-Check fehlgeschlagen",
"running": "Update läuft …",
"waitHint": "Bitte warten — die Seite lädt automatisch neu sobald die neue Version live ist.",
"success": "Update auf {{version}} abgeschlossen.",
"failed": "Update fehlgeschlagen",
"stepDownload": "Pakete laden",
"stepInstall": "Installation",
"stepRestart": "Service-Restart",
"stepVerify": "Verifizierung"
},
"wg": {
"title": "WireGuard",
"intro": "VPN-Tunnel über WireGuard. Server-Modus = wir lauschen für Peers; Client-Modus = wir verbinden zu einem festen Upstream. Privater Schlüssel liegt verschlüsselt in der DB.",
"tabs": { "servers": "Server-Tunnel", "clients": "Client-Tunnel" },
"serverIntro": "Server-Tunnel hosten ein Peer-Roster — z.B. Mitarbeiter-Geräte oder Niederlassungen. Pro Peer bekommt der Operator eine .conf zum Download (oder QR-Code für Mobile).",
"clientIntro": "Client-Tunnel verbinden EdgeGuard zu einem fremden WireGuard-Server (z.B. HQ-Datacenter). Allowed-IPs steuert, welcher Traffic durch den Tunnel geroutet wird.",
"iface": {
"name": "Name",
"namePattern": "wg gefolgt von Kleinbuchstaben/Ziffern/-, max. 15 Zeichen",
"nameExtra": "Empfehlung: wg0, wg1, wg-hq …",
"address": "Adresse (CIDR)",
"addressExtra": "Tunnel-IP der Box, z.B. 10.99.0.1/24 für /24-Pool",
"listenPort": "Listen-Port",
"publicKey": "Public-Key",
"privateKey": "Private-Key (paste)",
"privateKeyExtra": "Nur ausfüllen wenn nicht generieren — base64 32 Byte. Wird verschlüsselt gespeichert.",
"peerEndpoint": "Peer-Endpoint",
"peerPublicKey": "Peer Public-Key",
"peerPSK": "Pre-Shared-Key (PSK)",
"peerPSKExtra": "Optional, zusätzliche Schicht",
"allowedIPs": "Allowed IPs",
"allowedIPsExtra": "Was durch den Tunnel geroutet wird. Default = full-tunnel.",
"keepalive": "Persistent Keepalive (sec)",
"mtu": "MTU",
"zone": "Firewall-Zone",
"description": "Beschreibung",
"addServer": "Server-Tunnel hinzufügen",
"editServer": "Server-Tunnel bearbeiten",
"addClient": "Client-Tunnel hinzufügen",
"editClient": "Client-Tunnel bearbeiten",
"upstream": "Upstream-Peer",
"deleteConfirm": "Tunnel {{name}} wirklich löschen? wg-quick wird gestoppt.",
"keys": "Schlüssel",
"generateExtra": "Wenn an: Server erzeugt ein neues Curve25519-Keypair beim Speichern.",
"generateOn": "Server generiert",
"generateOff": "Manuell paste",
"editKeyWarning": "Achtung: neue Schlüssel = bestehende Peer-Configs ungültig. Nur ändern wenn explizit gewollt."
},
"peers": {
"button": "Peers",
"drawerTitle": "Peer-Roster"
},
"peer": {
"name": "Name",
"publicKey": "Public-Key",
"publicKeyExtra": "Wird vom Peer-Gerät erzeugt; hier nur paste-bar wenn der Peer schon ein Key-Pair hat.",
"allowedIPs": "Allowed IPs",
"allowedIPsExtra": "Welche Tunnel-IPs darf dieser Peer benutzen. Typisch /32 = eine IP.",
"keepalive": "Keepalive (sec)",
"keepaliveExtra": "0 = aus. Empfohlen 25 hinter NAT.",
"lastHandshake": "Letzter Handshake",
"never": "nie",
"description": "Beschreibung",
"add": "Peer hinzufügen",
"edit": "Peer bearbeiten",
"deleteConfirm": "Peer {{name}} wirklich entfernen?",
"keys": "Schlüssel",
"generateExtra": "Wenn an: Server erzeugt für diesen Peer ein Keypair und kann die Config / QR-Code ausliefern. Wenn aus: nur den Public-Key paste-en — keine Config-Download möglich.",
"pskExtra": "Wenn an: Server generiert einen 32-Byte PSK für diesen Peer.",
"pskOn": "PSK generieren",
"pskOff": "kein PSK",
"downloadConf": "wg-quick.conf herunterladen",
"qrTitle": "WireGuard-QR",
"qrHint": "Mit der WireGuard-App (iOS/Android) scannen: \"Tunnel hinzufügen\" → \"QR-Code scannen\". Endpoint im Download-Conf bitte vor Verwendung anpassen.",
"online": "Online",
"offline": "Offline",
"traffic": "Traffic"
}
},
"dashboard": {
"title": "Dashboard",
"welcomeHint": "EdgeGuard-Übersicht — Health, Counts, Live-Status der wichtigsten Dienste.",
"kpi": {
"domains": "Domains",
"backends": "Backends",
"ifaces": "Interfaces",
"fwRules": "FW-Regeln",
"natRules": "NAT-Regeln",
"wg": "WG-Verbindungen"
},
"wgCard": {
"title": "WireGuard",
"empty": "Noch kein WG-Tunnel angelegt."
},
"firewallCard": {
"title": "Firewall",
"zones": "Zonen",
"activeRules": "{{rules}} aktive Regeln · {{nat}} NAT"
},
"sslCard": {
"title": "SSL-Zertifikate",
"total": "Verwaltete Zertifikate",
"expiringSoon": "{{count}} läuft bald ab (< 30 Tage)",
"allFresh": "Alle Zertifikate haben > 30 Tage Restlaufzeit."
},
"clusterCard": {
"title": "Cluster",
"nodes": "Knoten"
},
"routingCard": {
"title": "Routing",
"domains": "Domains",
"backends": "Backends",
"attached": "{{count}}/{{total}} Domains haben einen Primary-Backend"
},
"systemCard": {
"title": "System",
"version": "Version",
"api": "API",
"ifaces": "Interfaces",
"wg": "WireGuard"
},
"servicesCard": {
"title": "Service-Status (live, 10s)"
},
"activityCard": {
"title": "Letzte Aktivität (Audit-Log)",
"empty": "Noch keine Aktivität — Mutationen werden hier protokolliert."
},
"haproxyCard": {
"title": "HAProxy-Backends (live)",
"empty": "Keine Backend-Stats erreichbar (HAProxy down oder admin.sock-permission)."
},
"resCard": {
"load": "Load",
"memory": "Memory",
"disk": "Disk /",
"free": "frei",
"conntrack": "Conntrack",
"uptime": "Uptime"
}
},
"ntp": {
"title": "Zeitserver (Chrony)",
"intro": "Chrony als Time-Sync-Daemon (NTP). Quellen oben, Listen-/Serve-Konfig im Settings-Tab. Wenn 'serve_clients' aktiv und LAN-IPs gebound sind, wird die Box selbst zum NTP-Server für das LAN.",
"tabs": { "pools": "Quellen", "settings": "Settings" },
"pool": {
"kind": "Typ",
"kindPool": "pool — DNS-Round-Robin (mehrere Server aus A-Records)",
"kindServer": "server — einzelner Host",
"address": "Adresse / Host",
"addressExtra": "FQDN (für pool: 0.de.pool.ntp.org) oder IP.",
"iburst": "iburst",
"prefer": "prefer",
"minpoll": "min-poll",
"maxpoll": "max-poll",
"options": "Optionen",
"description": "Beschreibung",
"add": "Quelle hinzufügen",
"edit": "Quelle bearbeiten",
"deleteConfirm": "NTP-Quelle {{addr}} wirklich löschen?"
},
"settings": {
"intro": "Globale Chrony-Settings. Save reloaded chrony automatisch.",
"serveClients": "Als NTP-Server für Clients arbeiten",
"serveClientsExtra": "Wenn aus: chrony agiert nur als Client (port 0). Wenn an + Listen-IP: bindet UDP/123.",
"listenAddresses": "Listen-Adressen",
"listenAddressesPlaceholder": "IPs wählen (oder eintippen)",
"listenAddressesExtra": "Auf welchen IPs chrony :123/UDP bindet. 127.0.0.1+::1 = nur lokal; LAN-IPs öffnen für LAN-Clients (FW-Rule wird automatisch generiert).",
"allowACL": "Allow-ACL (CIDRs)",
"allowACLExtra": "Wer darf NTP-Time anfragen.",
"makestepSecs": "makestep secs",
"makestepSecsExtra": "Erlaube step (statt slew) wenn offset > N sec.",
"makestepLimit": "makestep limit",
"rtcsync": "RTC mit System-Time syncen",
"rtcsyncExtra": "Hardware-Clock alle 11 min synchron halten — nach Reboot ist die Zeit grob korrekt.",
"leapsectz": "Leap-Sec TZ",
"leapsectzExtra": "Optional, z.B. 'right/UTC' für leap-sec über tzdata."
}
},
"dns": {
"title": "DNS (Unbound)",
"intro": "Unbound-Resolver auf :53. Lokale Zonen (authoritativ aus DNS-Records) und Forward-Zonen (per stub-zone weiter zu fremden Resolvern). Default-Forwarder für alles andere.",
"tabs": { "zones": "Zonen", "settings": "Resolver-Settings" },
"zone": {
"name": "Zone-Name",
"nameExtra": "FQDN ohne führenden/abschließenden Punkt — z.B. internal.netcell-it.de",
"type": "Typ",
"typeLocal": "local — authoritativ (records hier)",
"typeForward": "forward — stub-zone zu fremdem Resolver",
"forwardTo": "Upstream-Resolver",
"forwardToExtra": "Komma-separierte IP-Liste — z.B. '10.0.0.53, 8.8.8.8'",
"description": "Beschreibung",
"records": "Records …",
"add": "Zone hinzufügen",
"edit": "Zone bearbeiten",
"deleteConfirm": "Zone {{name}} mit allen Records wirklich löschen?"
},
"record": {
"name": "Name",
"nameExtra": "Relativ zur Zone (z.B. 'mailcow') oder FQDN mit abschließendem Punkt.",
"type": "Typ",
"value": "Wert",
"valueExtra": "RDATA in Textform: A → IP, CNAME → FQDN, MX → 'priority host', TXT → 'string'.",
"ttl": "TTL (sec)",
"drawerTitle": "DNS-Records",
"add": "Record hinzufügen",
"edit": "Record bearbeiten",
"deleteConfirm": "Record {{name}} wirklich löschen?"
},
"settings": {
"intro": "Globale Resolver-Settings. Änderungen hier reloaden Unbound automatisch.",
"listenAddresses": "Listen-Adressen",
"listenAddressesPlaceholder": "IPs wählen (oder eintippen)",
"listenAddressesRequired": "Mindestens eine Adresse erforderlich.",
"listenAddressesExtra": "Mehrfachauswahl aus den IPs die der Kernel kennt. 127.0.0.1 + ::1 = nur lokal; weitere LAN-Iface-IPs (z.B. 10.10.20.3) öffnen den Resolver für LAN-Clients. Eigene IPs lassen sich auch eintippen (Enter).",
"listenPort": "Port",
"upstreamForwards": "Default-Forwarders",
"upstreamForwardsExtra": "Wo geht alles hin was nicht lokal ist. Default 1.1.1.1 + 9.9.9.9.",
"accessACL": "Access-ACL (CIDRs)",
"accessACLExtra": "Wer darf diesen Resolver benutzen.",
"dnssec": "DNSSEC validieren",
"qnameMin": "QName-Minimisation (privacy)",
"cacheMin": "Cache min-TTL",
"cacheMax": "Cache max-TTL"
}
},
"fwd": {
"title": "Forward-Proxy (Squid)",
"intro": "Squid-basierter Forward-Proxy auf :3128. ACLs werden top-down nach Priority ausgewertet — first-match wins. Wenn keine Regel passt, gewinnt der Default: nur localnet (10/8, 172.16/12, 192.168/16) darf raus.",
"helpTitle": "Tipp zur ACL-Reihenfolge",
"helpBody": "Höhere Priority = wird zuerst geprüft. Beispiel: 'deny .badsite.com' (priority 200) vor 'allow .com' (priority 100). Werte können Listen sein (mehrere Zeilen), Regex je nach acl_type.",
"name": "Name",
"nameExtra": "Squid-konformer Bezeichner — Kleinbuchstaben + _, kein Leerzeichen.",
"aclType": "Typ",
"aclTypeExtra": "Was Squid prüft (Quelle, Domain, Port, …).",
"value": "Wert",
"valueExtra": "Format hängt vom Typ ab — IPs/CIDRs für src/dst, Domain mit führendem . für dstdomain (.example.com matcht auch sub.example.com), Regex für *_regex-Typen.",
"action": "Aktion",
"priority": "Priority",
"priorityExtra": "Höher = wird zuerst geprüft.",
"comment": "Kommentar",
"add": "ACL hinzufügen",
"edit": "ACL bearbeiten",
"deleteConfirm": "ACL {{name}} wirklich löschen?"
},
"common": {
"yes": "Ja",
"no": "Nein",
"save": "Speichern",
"cancel": "Abbrechen",
"loading": "Lädt …",
"error": "Fehler",
"edit": "Bearbeiten",
"delete": "Löschen",
"deleteConfirm": "Wirklich löschen?",
"search": "Suchen …",
"totalRows": "{{count}} Einträge",
"active": "Aktiv",
"inactive": "Inaktiv",
"noData": "Keine Einträge",
"actions": "Aktionen",
"add": "Hinzufügen",
"download": "Download",
"copy": "Kopieren",
"copied": "Kopiert"
},
"license": {
"title": "Lizenz",
"status": "Status",
"product": "Produkt",
"key": "Lizenz-Schlüssel",
"noKey": "Kein Schlüssel hinterlegt",
"validUntil": "Gültig bis",
"expired": "Abgelaufen",
"daysLeft": "noch {{days}} Tage",
"lastVerifiedAt": "Letzte Verifizierung",
"verifiedBy": "Verifiziert von",
"limits": "Limits",
"unlimited": "Unbegrenzt",
"features": "Features",
"reverify": "Erneut prüfen",
"reverified": "Lizenz erfolgreich verifiziert",
"enterKey": "Schlüssel eingeben",
"replaceKey": "Schlüssel ersetzen",
"enterKeyHint": "Lizenz-Schlüssel aus dem Self-Service-Portal von license.netcell-it.com einfügen.",
"activate": "Aktivieren",
"saved": "Lizenz gespeichert und verifiziert",
"savedButVerifyFailed": "Schlüssel gespeichert, aber Server-Verifizierung fehlgeschlagen",
"clearKey": "Schlüssel entfernen",
"cleared": "Lizenz entfernt — System fällt auf Trial zurück",
"confirmClear": "Lizenz-Schlüssel wirklich entfernen?",
"confirmClearHint": "System fällt auf Trial-Modus zurück, sobald der Schlüssel gelöscht wird.",
"lastVerifyFailed": "Letzte Server-Verifizierung fehlgeschlagen",
"trialExpiring": "Trial läuft in {{days}} Tag(en) ab",
"trialExpiringHint": "Lizenz aktivieren, bevor die Trial-Periode endet."
},
"licenseBanner": {
"expired": "Lizenz abgelaufen oder ungültig.",
"trialExpiring": "Trial läuft in {{days}} Tag(en) ab.",
"verifyFailed": "Lizenz-Verifizierung fehlgeschlagen",
"cta": "Jetzt aktivieren →",
"openPage": "Lizenz-Seite öffnen →"
},
"routes": {
"liveTitle": "Live-Routen (Kernel)",
"liveIntro": "Aktueller Zustand aus `ip -j route show table all`. proto edgeguard markiert von EdgeGuard verwaltete Routen — andere Quellen (kernel/static/dhcp) bleiben unberührt.",
"liveEmpty": "Keine Routen im Kernel.",
"managedTitle": "Verwaltete Routen",
"managedIntro": "Statische Routen, die EdgeGuard via `ip route … proto edgeguard` beim Boot setzt. Änderung hier triggert sofort `systemctl restart edgeguard-routes.service`.",
"add": "Route hinzufügen",
"addTitle": "Statische Route anlegen",
"editTitle": "Statische Route bearbeiten",
"empty": "Keine verwalteten Routen.",
"confirmDelete": "Route nach {{dest}} wirklich löschen?",
"refreshTooltip": "Live-Routen neu laden",
"destExtra": "CIDR — z.B. 10.0.5.0/24 oder 0.0.0.0/0 für Default-Route.",
"gatewayExtra": "Optional. Leer = on-link via dev.",
"devExtra": "Output-Interface. Bei Gateway-only kann ip das auflösen, explizit ist aber stabiler.",
"metricExtra": "Niedriger gewinnt. Default 100. Standard-Linux-Defaults: dhcp 1024, kernel-link 0.",
"tableExtra": "Routing-Table. main = Standard. Custom-Tables via /etc/iproute2/rt_tables.",
"col": {
"destination": "Ziel",
"gateway": "Gateway",
"dev": "Interface",
"metric": "Metric",
"table": "Table",
"active": "Aktiv",
"comment": "Kommentar",
"proto": "Protokoll",
"scope": "Scope",
"src": "Quell-IP"
}
},
"backups": {
"title": "Backups",
"intro": "Sicherungen der PostgreSQL-Datenbank + /var/lib/edgeguard (Setup, License, JWT, ACME-Account). Täglicher Auto-Job + manueller Trigger.",
"scopeTitle": "Was wird gesichert?",
"scopeDesc": "DB-Dump (pg_dump --clean), setup.json, license_key, license.cache, .jwt_fingerprint, acme-account/. Konfig-Dateien (haproxy.cfg, nft, …) sind aus der DB regenerierbar und werden NICHT mitgesichert.",
"runNow": "Backup jetzt erstellen",
"created": "Backup erstellt: {{file}}",
"failed": "Backup fehlgeschlagen",
"deleted": "Backup gelöscht",
"download": "Download",
"restore": "Wiederherstellen",
"restoreOk": "Restore starten",
"restoreDone": "Restore abgeschlossen — Seite wird neu geladen.",
"restoreFailed": "Restore fehlgeschlagen",
"restoreRunning": "Restore läuft …",
"restoreHint": "edgeguard-api startet automatisch neu; die UI lädt nach Restart neu.",
"confirmRestoreTitle": "Backup wiederherstellen?",
"confirmRestoreDesc": "DB-Inhalt wird durch {{file}} ersetzt + State-Dateien zurückgespielt. edgeguard-api + scheduler restarten. Aktuelle Änderungen seit dem Backup gehen verloren.",
"step": {
"extract": "Tar entpacken",
"psql": "DB-Restore (psql)",
"render": "Configs re-rendern",
"restart": "Services neu starten"
},
"downloadTooltip": "tar.gz herunterladen",
"refreshTooltip": "Liste neu laden",
"confirmDelete": "Backup {{file}} wirklich löschen?",
"empty": "Noch keine Backups. Klicke „Backup jetzt erstellen\" oder warte den nächsten Auto-Tick ab.",
"failedTag": "FEHLER",
"col": {
"time": "Zeit",
"file": "Datei",
"kind": "Typ",
"status": "Status",
"size": "Größe",
"duration": "Dauer"
}
},
"logs": {
"title": "System-Logs",
"intro": "Aggregierter Blick auf alle Service-Journals + audit_log. Multi-Source-Auswahl, Level-Filter, Freitext-Suche, Zeit-Range, Auto-Refresh (5s).",
"autoOn": "Auto",
"autoOff": "Manuell",
"refresh": "Aktualisieren",
"refreshTooltip": "Einmalig neu laden",
"export": "CSV",
"exportTooltip": "Aktuelle Tabelle als CSV exportieren",
"exportEmpty": "Keine Einträge zum Exportieren",
"found": "{{n}} Einträge",
"limit": "Limit",
"empty": "Keine Einträge gefunden. Quellen-Auswahl ändern oder Zeit-Range erweitern.",
"col": {
"time": "Zeit",
"source": "Quelle",
"level": "Level",
"message": "Nachricht"
},
"filter": {
"sources": "Quellen wählen (alle wenn leer)",
"levels": "Level filtern",
"grep": "Volltext-Suche"
}
},
"fwlog": {
"title": "Firewall-Log (Live)",
"intro": "Pakete, die in nft-Regeln mit aktivem Log-Flag matchen, fließen via NFLOG → ulogd2 → JSONL hierher. WebSocket-Stream zeigt Live-Events; Ring-Buffer (1000) hält die letzten Treffer auch nach Reconnect.",
"start": "Live-Log starten",
"stop": "Stop",
"notStartedTitle": "Live-Log ist aus",
"notStartedDesc": "Standardmäßig pausiert — Klick zum Verbinden lässt Events live einfließen.",
"live": "Live",
"disconnected": "verbinde …",
"pause": "Pause",
"resume": "Fortsetzen",
"queued": "wartend",
"clear": "Leeren",
"clearTooltip": "Tabelle leeren (Server-Ringbuffer bleibt unverändert)",
"export": "CSV",
"exportTooltip": "Aktuelle Tabelle als CSV exportieren",
"exportEmpty": "Keine Events zum Exportieren",
"connError": "WebSocket-Fehler — versuche erneut",
"empty": "Noch keine Events. Aktiviere bei einer Firewall-Regel den Log-Schalter, dann fließen Treffer hier hinein.",
"connecting": "Verbinde …",
"col": {
"time": "Zeit",
"action": "Aktion",
"rule": "Rule",
"proto": "Proto",
"src": "Quelle",
"dst": "Ziel",
"iface": "Interface",
"size": "Größe"
},
"filter": {
"action": "Action filtern",
"proto": "Proto filtern",
"src": "Quell-IP",
"dst": "Ziel-IP",
"rule": "Rule-ID"
}
}
}