Replace WireGuard prototype with native host service and scoped access

This commit is contained in:
Mikei386 committed 2026-10-01 23:00:43 +02:00
1 parent 37b5394df1
commit 11f16ef4a3
16 files changed
+667 -136

No files matched your search

+21 -92
View File
@@ -1,101 +1,30 @@
"""Local management via fixed SSH target or container-private Unix RPC."""
from auth import initial_record
import base64
import hashlib
"""Authenticated Deck administration through a native host Unix socket only."""
import json
import os
from pathlib import Path
import secrets
import subprocess
import threading
import time
ROOT = Path(__file__).resolve().parent.parent
SSH = ['ssh', '-i', str(Path.home()/'.ssh/athena_key'), '-o', 'BatchMode=yes', '-o', 'ConnectTimeout=5', '-o', 'StrictHostKeyChecking=yes', 'root@192.168.1.212']
from network.rpc import request
class NetworkClient:
def __init__(self):
self.disabled = os.environ.get('DECK_NETWORK_MODE') == 'disabled'
self.local = os.environ.get('DECK_NETWORK_SOCKET') == '1'
self.job = None
self.lock = threading.Lock()
self.cached = None
self.cached_at = 0
def call(self, action, values=None, ingress='management'):
if self.disabled:
raise ValueError('WireGuard ist in dieser eigenständigen Installation nicht angebunden.')
data = dict(values or {}, action=action)
if self.local:
from network.rpc import request
data.update(ingress=ingress, proxy_token=os.environ.get('DECK_PROXY_TOKEN', ''))
return request(data)
result = subprocess.run(SSH + ['docker exec -i athena-deck-network python3 -m network.rpc'], input=json.dumps(data), text=True, capture_output=True, timeout=35)
try:
value = json.loads(result.stdout)
except ValueError:
raise ValueError('Deck-Netzwerkmodul auf Athena nicht erreichbar oder noch nicht installiert.') from None
if result.returncode or ('error' in value and 'installed' not in value):
raise ValueError(value.get('error', 'Netzwerkaktion fehlgeschlagen.'))
self.cached = None
return value
def status(self, ingress='management'):
if self.disabled:
return dict(installed=False, install_supported=False, state='disabled-module', ingress=ingress, error='Eigenständige Installation ohne WireGuard-Modul. Zugriff ist per SSH-Tunnel möglich. Vorhandene Gateways werden nicht verändert.')
self.native=os.environ.get('DECK_NETWORK_MODE')=='native'
self.disabled=not self.native
self.local=False
self.job=None;self.lock=threading.Lock();self.cached=None;self.cached_at=0
def call(self,action,values=None,ingress='management'):
if self.disabled:raise ValueError('Nativen WireGuard-Systemdienst zuerst auf dem Debian-Host einrichten.')
token=Path(os.environ.get('DECK_PROXY_TOKEN_FILE','/run/athena-deck-network/proxy-token')).read_text().strip()
result=request(dict(values or {},action=action,ingress=ingress,proxy_token=token),path='/run/athena-deck-network/control.sock')
self.cached=None
return result
def status(self,ingress='management'):
if self.disabled:return dict(installed=False,install_supported=False,state='disabled-module',ingress=ingress,backend='native-systemd',error='Nativer WireGuard-Systemdienst ist noch nicht angebunden. Einrichtung siehe Installationsanleitung.')
with self.lock:
if self.job and self.job['state'] == 'running':
return dict(installed=False, state='installing', job=self.job.copy(), ingress=ingress)
if self.cached is not None and time.monotonic()-self.cached_at < 3:
return dict(self.cached, ingress=ingress, job=self.job)
try:
result = self.call('status', ingress=ingress)
result['reachable'] = True
except (ValueError, OSError, subprocess.SubprocessError):
result = dict(installed=False, reachable=False, state='unavailable', error='Netzwerkmodul noch nicht installiert oder Athena nicht erreichbar.')
self.cached = result
self.cached_at = time.monotonic()
return dict(result, ingress=ingress, job=self.job)
def install(self, password, api_token=None):
if self.disabled:
raise ValueError('WireGuard-Installation ist für diese eigenständige Instanz deaktiviert.')
if self.local:
raise ValueError('Die Server-Instanz ist bereits installiert.')
if not isinstance(password, str) or not 16 <= len(password) <= 256:
raise ValueError('Bitte ein eigenes Deck-Passwort mit mindestens 16 Zeichen setzen.')
auth = initial_record(password,api_token)
with self.lock:
if self.job and self.job['state'] == 'running':
raise ValueError('Installation läuft bereits.')
self.job = dict(state='running', message='Eigener Deck-Container wird gebaut und gestartet. Das kann mehrere Minuten dauern.')
threading.Thread(target=self._install, args=(auth,), daemon=True).start()
return dict(job=self.job.copy())
def _install(self, auth):
try:
# Import the source allowlist without executing the installer entrypoint.
from network.install_remote import FILES
payload = dict(auth=auth, files={name:base64.b64encode((ROOT/name).read_bytes()).decode() for name in FILES})
script = (ROOT/'network/install_remote.py').read_text()
# Remote command contains only fixed trusted program text, never user input.
import shlex
command = 'python3 -c ' + shlex.quote(script)
result = subprocess.run(SSH+[command], input=json.dumps(payload), text=True, capture_output=True, timeout=900)
value = json.loads(result.stdout)
if result.returncode or ('error' in value and 'installed' not in value):
raise ValueError(value.get('error', 'Installation fehlgeschlagen.'))
# Confirm helper readiness; don't report a successful installation from docker run alone.
for _ in range(20):
try:
self.call('status')
break
except (ValueError, OSError, subprocess.SubprocessError):
time.sleep(1)
else:
raise ValueError('Container angelegt, aber Netzwerk-Helper nicht bereit. Installation prüfen.')
self.job = dict(state='complete', message='Netzwerkmodul installiert. Jetzt eine eigene WireGuard-Konfiguration importieren.')
except Exception as exc:
self.job = dict(state='failed', message=str(exc) if isinstance(exc, ValueError) and not isinstance(exc, json.JSONDecodeError) else 'Installation fehlgeschlagen; SSH und Docker prüfen.')
finally:
self.cached = None
if self.cached is not None and time.monotonic()-self.cached_at<3:return dict(self.cached,ingress=ingress)
try:result=self.call('status',ingress=ingress);result['reachable']=True
except (ValueError,OSError,json.JSONDecodeError):result=dict(installed=False,install_supported=False,reachable=False,state='unavailable',error='Nativer WireGuard-Systemdienst nicht erreichbar.')
self.cached=result;self.cached_at=time.monotonic()
return dict(result,ingress=ingress)
def install(self,password=None,api_token=None):
raise ValueError('WireGuard wird nativ mit deploy/setup_network_helper.py eingerichtet. Kein Netzwerk-Container und keine zweite Deck-Instanz.')