server-up/tests/test_networks.py
Ramon 8b29553a71
Some checks failed
Deploy server-up (dev) / deploy (push) Failing after 3s
v0.5.00-beta - containerbeheer, eigen IP-adressen, veiliger installeren
- Beheer per container: de stackkaart klapt uit naar de losse containers, met
  start/stop/herstart, logs en live CPU-/geheugengebruik per container. De
  containernaam wordt getoetst aan compose_ps van díé stack, zodat de route
  geen willekeurige container op de host kan raken.
- Stacks met een eigen IP-adres (macvlan/ipvlan): netwerkbeheer onder
  Instellingen → Netwerken, netwerkkeuze in de installatiemodal met voorstel
  voor het eerstvolgende vrije adres, en automatische omzetting van het
  gerenderde compose-bestand (poortmappings eruit, ipv4_address erin). De
  templates in apps/ blijven ongewijzigd.
- Toegekende IP's worden vastgehouden in .serverup.json en getoond op de
  stackkaart, zodat een volgende installatie ze niet opnieuw uitdeelt.
- Vrije poort voorstellen bij installeren: next_free_port() bestond al maar
  werd nergens gebruikt. Bezette poorten worden in de UI gemeld.
- Genereerknop voor velden die op een geheim wijzen (token/password/secret),
  lokaal gegenereerd via crypto.getRandomValues.
- Compose valideren met `docker compose config` vóór het wegschrijven, zowel
  bij de editor als na het renderen bij installatie. Ontbreekt de compose-CLI,
  dan blokkeert dat een installatie niet.
- Uitloggen in de zijbalk; gebruikersbeheer en wachtwoord wijzigen onder
  Instellingen → Beveiliging.
- docs/netwerken.md (incl. de shim-interface die de host nodig heeft om zijn
  eigen macvlan-containers te bereiken) en docs/beveiliging.md toegevoegd.
- CHANGELOG bijgewerkt; testsuite uitgebreid naar 112 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C7oLCRYzY5ixJ5Sv8Y8EFb
2026-07-26 14:51:32 +02:00

77 lines
2.6 KiB
Python

"""Validatie en IP-toewijzing voor macvlan/ipvlan-netwerken."""
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "server-up"))
from core import networks
GELDIG = {"name": "lan", "driver": "macvlan", "parent": "eth0",
"subnet": "192.168.1.0/24", "gateway": "192.168.1.1",
"ip_range": "192.168.1.240/28"}
def test_geldige_definitie():
assert networks.validate(GELDIG) == ""
@pytest.mark.parametrize("wijziging,fragment", [
({"name": "../boos"}, "netwerknaam"),
({"name": ""}, "netwerknaam"),
({"driver": "bridge"}, "macvlan"),
({"parent": "eth0; rm -rf /"}, "host-interface"),
({"subnet": "geen-subnet"}, "subnet"),
({"gateway": "10.9.9.1"}, "gateway ligt buiten"),
({"gateway": "nogal-fout"}, "gateway-adres"),
({"ip_range": "10.9.9.0/24"}, "range ligt buiten"),
({"ip_range": "onzin"}, "IP-range"),
])
def test_ongeldige_definities(wijziging, fragment):
net = {**GELDIG, **wijziging}
assert fragment in networks.validate(net)
def test_next_free_ip_respecteert_range_en_gateway(monkeypatch):
monkeypatch.setattr(networks, "used_ips", lambda naam: set())
# .240/28 loopt van .240 t/m .255; .240 is het netwerkadres, dus .241 eerst.
assert networks.next_free_ip(GELDIG) == "192.168.1.241"
def test_next_free_ip_slaat_bezette_adressen_over(monkeypatch):
monkeypatch.setattr(networks, "used_ips",
lambda naam: {"192.168.1.241", "192.168.1.242"})
assert networks.next_free_ip(GELDIG) == "192.168.1.243"
def test_next_free_ip_houdt_rekening_met_reserveringen(monkeypatch):
monkeypatch.setattr(networks, "used_ips", lambda naam: set())
assert networks.next_free_ip(GELDIG, {"192.168.1.241"}) == "192.168.1.242"
def test_next_free_ip_zonder_range_gebruikt_subnet(monkeypatch):
monkeypatch.setattr(networks, "used_ips", lambda naam: set())
net = {**GELDIG, "ip_range": ""}
assert networks.next_free_ip(net) == "192.168.1.2" # .1 is de gateway
def test_next_free_ip_leeg_als_alles_bezet(monkeypatch):
bezet = {f"192.168.1.{i}" for i in range(240, 256)}
monkeypatch.setattr(networks, "used_ips", lambda naam: bezet)
assert networks.next_free_ip(GELDIG) == ""
@pytest.mark.parametrize("ip,verwacht", [
("192.168.1.240", True),
("192.168.2.1", False),
("geen-ip", False),
])
def test_ip_in_network(ip, verwacht):
assert networks.ip_in_network(ip, GELDIG) is verwacht
def test_remove_weigert_ongeldige_naam():
ok, msg = networks.remove("../etc")
assert ok is False
assert "netwerknaam" in msg