fix(unifi): open webinterface via https
All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 5m46s
All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 5m46s
This commit is contained in:
parent
9008e39fe4
commit
8f62df90d8
8 changed files with 133 additions and 13 deletions
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -1,3 +1,19 @@
|
||||||
|
# v0.8.18-beta — UniFi opent via HTTPS
|
||||||
|
|
||||||
|
De UniFi-container was wel correct op zijn HTTPS-poort gepubliceerd, maar
|
||||||
|
Server Up maakte er altijd een `http://`-link van. Daardoor leek de app
|
||||||
|
onbereikbaar terwijl de browser met het verkeerde protocol verbinding maakte.
|
||||||
|
|
||||||
|
- **UniFi declareert HTTPS expliciet.** Het template vermeldt voortaan zowel
|
||||||
|
het protocol als de interne webpoort 8443.
|
||||||
|
- **De juiste hostpoort wordt gekozen.** Server Up koppelt de interne webpoort
|
||||||
|
aan de ingestelde hostpoort, ook wanneer een databasepoort eerder in het
|
||||||
|
Compose-bestand staat.
|
||||||
|
- **Bestaande installaties werken mee.** Ontbrekende installatiemetadata wordt
|
||||||
|
uit het actuele bronsjabloon aangevuld; opnieuw installeren is niet nodig.
|
||||||
|
- **Eigen IP-adressen blijven correct.** Een UniFi-stack met een eigen IP opent
|
||||||
|
als `https://<app-ip>:8443`.
|
||||||
|
|
||||||
# v0.8.17-beta — Elf nieuwe apps voor beheer, automatisering en monitoring
|
# v0.8.17-beta — Elf nieuwe apps voor beheer, automatisering en monitoring
|
||||||
|
|
||||||
De app-store bevatte nog weinig lichte infrastructuurapps die zowel op een
|
De app-store bevatte nog weinig lichte infrastructuurapps die zowel op een
|
||||||
|
|
|
||||||
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
0.8.17-beta
|
0.8.18-beta
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,11 @@
|
||||||
"amd64",
|
"amd64",
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
"architecture_note": "Op een Raspberry Pi is een 64-bits besturingssysteem vereist. Voor een Pi 4 is de database bewust vastgezet op MongoDB 4.4.29."
|
"architecture_note": "Op een Raspberry Pi is een 64-bits besturingssysteem vereist. Voor een Pi 4 is de database bewust vastgezet op MongoDB 4.4.29.",
|
||||||
|
"web": {
|
||||||
|
"scheme": "https",
|
||||||
|
"port": 8443
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"variables": [
|
"variables": [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -910,18 +910,15 @@ def api_stack_credentials(name):
|
||||||
items.append({"kind": soort, "title": f.get("title") or f["name"],
|
items.append({"kind": soort, "title": f.get("title") or f["name"],
|
||||||
"value": waarde, "secret": bool(f.get("secret"))})
|
"value": waarde, "secret": bool(f.get("secret"))})
|
||||||
|
|
||||||
# Het adres waarop de app draait: eigen IP, anders de gepubliceerde poort.
|
# Het adres waarop de app draait. Sommige apps (waaronder UniFi) luisteren
|
||||||
adres = ""
|
# uitsluitend via HTTPS; het oude vaste http://-adres maakte hun link stuk.
|
||||||
try:
|
try:
|
||||||
poorten = compose_transform.published_ports(docker.read_compose(d))
|
compose_text = docker.read_compose(d)
|
||||||
except Exception:
|
except Exception:
|
||||||
poorten = []
|
compose_text = ""
|
||||||
if meta.get("ip"):
|
bron_meta = boilerplates.metadata(bron) if bron else {}
|
||||||
adres = f"http://{meta['ip']}"
|
adres = _stack_webadres(meta, bron_meta, compose_text,
|
||||||
elif poorten:
|
request.host.split(":")[0])
|
||||||
eerste = str(poorten[0]).strip('"').split(":")
|
|
||||||
if len(eerste) >= 2 and eerste[-2].isdigit():
|
|
||||||
adres = f"http://{request.host.split(':')[0]}:{eerste[-2]}"
|
|
||||||
|
|
||||||
if items:
|
if items:
|
||||||
audit.log("stacks", "credentials_view", "ok", ref=name,
|
audit.log("stacks", "credentials_view", "ok", ref=name,
|
||||||
|
|
@ -933,6 +930,43 @@ def api_stack_credentials(name):
|
||||||
self_signup=not items)
|
self_signup=not items)
|
||||||
|
|
||||||
|
|
||||||
|
def _stack_webadres(meta: dict, bron_meta: dict, compose_text: str,
|
||||||
|
host: str) -> str:
|
||||||
|
"""Bouw het browseradres uit template-metadata en Compose-poorten.
|
||||||
|
|
||||||
|
De bronmetadata blijft bewust een fallback: daarmee krijgen ook stacks die
|
||||||
|
vóór deze wijziging zijn geïnstalleerd meteen het juiste HTTPS-adres.
|
||||||
|
"""
|
||||||
|
web = meta.get("web") if isinstance(meta.get("web"), dict) else {}
|
||||||
|
if not web and isinstance(bron_meta.get("web"), dict):
|
||||||
|
web = bron_meta["web"]
|
||||||
|
schema = str(web.get("scheme") or "http").lower()
|
||||||
|
if schema not in ("http", "https"):
|
||||||
|
schema = "http"
|
||||||
|
try:
|
||||||
|
doelpoort = int(web.get("port") or 0)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
doelpoort = 0
|
||||||
|
|
||||||
|
eigen_ip = str(meta.get("ip") or "").strip()
|
||||||
|
if eigen_ip:
|
||||||
|
poort = doelpoort
|
||||||
|
host = eigen_ip
|
||||||
|
else:
|
||||||
|
poorten = [p for p in compose_transform.poorten_per_service(compose_text)
|
||||||
|
if p.get("proto") == "tcp"]
|
||||||
|
gekozen = next((p for p in poorten
|
||||||
|
if doelpoort and p.get("container") == doelpoort), None)
|
||||||
|
gekozen = gekozen or (poorten[0] if poorten else None)
|
||||||
|
poort = (gekozen or {}).get("host") or (gekozen or {}).get("container") or 0
|
||||||
|
|
||||||
|
if not host or (not eigen_ip and not poort):
|
||||||
|
return ""
|
||||||
|
standaard = (schema == "http" and poort == 80) or (
|
||||||
|
schema == "https" and poort == 443)
|
||||||
|
return f"{schema}://{host}{'' if not poort or standaard else f':{poort}'}"
|
||||||
|
|
||||||
|
|
||||||
@app.route("/api/stacks/<name>/compose")
|
@app.route("/api/stacks/<name>/compose")
|
||||||
def api_stack_compose(name):
|
def api_stack_compose(name):
|
||||||
lib, d = _stack_dir(name)
|
lib, d = _stack_dir(name)
|
||||||
|
|
@ -1177,6 +1211,7 @@ def api_stack_reconfigure(name):
|
||||||
"network": net_name, "ip": net_ip, "ips": net_ips,
|
"network": net_name, "ip": net_ip, "ips": net_ips,
|
||||||
"app_networks": app_netten,
|
"app_networks": app_netten,
|
||||||
"service_ports": poorten_meta,
|
"service_ports": poorten_meta,
|
||||||
|
"web": boilerplates.metadata(bron).get("web") or {},
|
||||||
"connected": bool(app_netten)})
|
"connected": bool(app_netten)})
|
||||||
|
|
||||||
docker.compose_down(d, log_fn=lf, name=name)
|
docker.compose_down(d, log_fn=lf, name=name)
|
||||||
|
|
@ -2098,6 +2133,7 @@ def api_store_install():
|
||||||
"service_ports": poorten_meta,
|
"service_ports": poorten_meta,
|
||||||
"categories": meta.get("categories") or [],
|
"categories": meta.get("categories") or [],
|
||||||
"tags": meta.get("tags") or [],
|
"tags": meta.get("tags") or [],
|
||||||
|
"web": meta.get("web") or {},
|
||||||
# Zonder de gebruikte waarden valt het installatieformulier
|
# Zonder de gebruikte waarden valt het installatieformulier
|
||||||
# later niet opnieuw te openen om iets te wijzigen.
|
# later niet opnieuw te openen om iets te wijzigen.
|
||||||
"format": "boilerplate" if is_bp else "compose",
|
"format": "boilerplate" if is_bp else "compose",
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ def metadata(d: Path) -> dict:
|
||||||
"depends_on": _afhankelijkheden(md.get("depends_on")),
|
"depends_on": _afhankelijkheden(md.get("depends_on")),
|
||||||
"architectures": _architecturen(md.get("architectures")),
|
"architectures": _architecturen(md.get("architectures")),
|
||||||
"architecture_note": str(md.get("architecture_note") or "").strip(),
|
"architecture_note": str(md.get("architecture_note") or "").strip(),
|
||||||
|
"web": _webconfig(md.get("web")),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -85,6 +86,22 @@ _ARCH_ALIASES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _webconfig(waarde) -> dict:
|
||||||
|
"""Normaliseer het webadres dat een app-template zelf declareert."""
|
||||||
|
if not isinstance(waarde, dict):
|
||||||
|
return {}
|
||||||
|
schema = str(waarde.get("scheme") or "").strip().lower()
|
||||||
|
if schema not in ("http", "https"):
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
poort = int(waarde.get("port") or 0)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return {}
|
||||||
|
if not 1 <= poort <= 65535:
|
||||||
|
return {}
|
||||||
|
return {"scheme": schema, "port": poort}
|
||||||
|
|
||||||
|
|
||||||
def _architectuur(waarde: str) -> str:
|
def _architectuur(waarde: str) -> str:
|
||||||
return _ARCH_ALIASES.get(str(waarde or "").strip().lower(), "")
|
return _ARCH_ALIASES.get(str(waarde or "").strip().lower(), "")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -397,7 +397,7 @@ def test_unifi_heeft_pi_geschikte_en_correct_geinitialiseerde_mongodb():
|
||||||
assert "clusterMonitor" in script
|
assert "clusterMonitor" in script
|
||||||
assert 'role: "dbOwner"' in script
|
assert 'role: "dbOwner"' in script
|
||||||
assert bp.metadata(app)["architectures"] == ["amd64", "arm64"]
|
assert bp.metadata(app)["architectures"] == ["amd64", "arm64"]
|
||||||
|
assert bp.metadata(app)["web"] == {"scheme": "https", "port": 8443}
|
||||||
|
|
||||||
AMD64_ALLEEN = {
|
AMD64_ALLEEN = {
|
||||||
"cal-com", "comfyui", "dawarich", "focalboard", "formbricks",
|
"cal-com", "comfyui", "dawarich", "focalboard", "formbricks",
|
||||||
|
|
|
||||||
|
|
@ -145,3 +145,28 @@ def test_render_behoudt_uitvoerbaar_recht_van_tekstscript(tmp_path):
|
||||||
dest = tmp_path / "out"
|
dest = tmp_path / "out"
|
||||||
bp.render_to_dir(src, dest, {"service_name": "test"})
|
bp.render_to_dir(src, dest, {"service_name": "test"})
|
||||||
assert (dest / "init.sh").stat().st_mode & stat.S_IXUSR
|
assert (dest / "init.sh").stat().st_mode & stat.S_IXUSR
|
||||||
|
|
||||||
|
|
||||||
|
def test_webmetadata_wordt_genormaliseerd(tmp_path):
|
||||||
|
src = _maak_boilerplate(tmp_path, "services: {}\n")
|
||||||
|
pad = src / "template.json"
|
||||||
|
data = json.loads(pad.read_text(encoding="utf-8"))
|
||||||
|
data["metadata"]["web"] = {"scheme": "HTTPS", "port": "8443"}
|
||||||
|
pad.write_text(json.dumps(data), encoding="utf-8")
|
||||||
|
|
||||||
|
assert bp.metadata(src)["web"] == {"scheme": "https", "port": 8443}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("web", [
|
||||||
|
{"scheme": "ftp", "port": 21},
|
||||||
|
{"scheme": "https", "port": 70000},
|
||||||
|
{"scheme": "https", "port": "niet-een-poort"},
|
||||||
|
])
|
||||||
|
def test_ongeldige_webmetadata_wordt_genegeerd(tmp_path, web):
|
||||||
|
src = _maak_boilerplate(tmp_path, "services: {}\n")
|
||||||
|
pad = src / "template.json"
|
||||||
|
data = json.loads(pad.read_text(encoding="utf-8"))
|
||||||
|
data["metadata"]["web"] = web
|
||||||
|
pad.write_text(json.dumps(data), encoding="utf-8")
|
||||||
|
|
||||||
|
assert bp.metadata(src)["web"] == {}
|
||||||
|
|
|
||||||
|
|
@ -339,3 +339,25 @@ def test_voorvertoning_gebruikt_de_ingevulde_waarden(client, env):
|
||||||
# Het geheim hoort al in de .env-voorvertoning te staan, niet in compose.
|
# Het geheim hoort al in de .env-voorvertoning te staan, niet in compose.
|
||||||
assert "MijnWachtwoord1" not in d["compose_preview"]
|
assert "MijnWachtwoord1" not in d["compose_preview"]
|
||||||
assert "MijnWachtwoord1" in d["env_preview"]
|
assert "MijnWachtwoord1" in d["env_preview"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_webadres_gebruikt_https_en_de_juiste_containerpoort(client):
|
||||||
|
import app as app_module
|
||||||
|
compose = ("services:\n database:\n image: mongo\n"
|
||||||
|
" ports:\n - \"27017:27017\"\n"
|
||||||
|
" unifi:\n image: unifi\n"
|
||||||
|
" ports:\n - \"9443:8443\"\n - \"8080:8080\"\n")
|
||||||
|
bron = {"web": {"scheme": "https", "port": 8443}}
|
||||||
|
|
||||||
|
assert app_module._stack_webadres({}, bron, compose, "server.local") == \
|
||||||
|
"https://server.local:9443"
|
||||||
|
assert app_module._stack_webadres({"ip": "192.168.1.50"}, bron, compose,
|
||||||
|
"server.local") == \
|
||||||
|
"https://192.168.1.50:8443"
|
||||||
|
|
||||||
|
|
||||||
|
def test_webadres_zonder_metadata_blijft_http(client):
|
||||||
|
import app as app_module
|
||||||
|
compose = "services:\n app:\n image: nginx\n ports:\n - \"8123:80\"\n"
|
||||||
|
assert app_module._stack_webadres({}, {}, compose, "server.local") == \
|
||||||
|
"http://server.local:8123"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue