Some checks failed
Deploy server-up (dev) / deploy (push) Failing after 3s
- 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
77 lines
2.6 KiB
Python
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
|