wlan-setup/ is a Go service that will replace the nginx + njs backend of
wlan.renner.uno (switched over in a follow-up once the first image exists).
- Page, /whoami, /healthz; /{android,apple,windows}/install and /callback:
authentik OAuth code flow (client wlan-setup, client_secret_basic), user
check against X-authentik-username, per-device app password
wlan-<platform>-... (29 days), profile per platform: Android Passpoint
(application/x-wifi-config), Apple .mobileconfig with Passpoint, Windows
CarrierProvisioning with Passpoint.
- Windows: the profile is signed with XML-DSig in the form of Microsoft's
samples (enveloped, C14N 1.0, rsa-sha1, chain in KeyInfo) by the key in
/etc/wlan-signing, kept for 5 minutes under a single-use token, and the
browser is sent to ms-settings:wifi-provisioning?uri=.../windows/profile/<token>.
- Offline tests against a fake authentik; goxmldsig verifies the signature
in the tests only.
- CI: wlan-setup-test (gofmt, vet, test -race) on PRs; build-ci-images
builds gitea.renner.uno/gitea_admin/wlan-setup:YYYY-MM-DD-HHMM on main;
Flux image automation follows the newest tag.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
124 lines
5.6 KiB
HTML
124 lines
5.6 KiB
HTML
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>WLAN einrichten</title>
|
|
<style>
|
|
:root { --bg: #ffffff; --fg: #1d1d1f; --muted: #5f6368; --card: #f4f5f7; --accent: #0b63ce; --code: #e9ebef; }
|
|
@media (prefers-color-scheme: dark) {
|
|
:root { --bg: #121314; --fg: #e8eaed; --muted: #9aa0a6; --card: #1e1f22; --accent: #7cacf8; --code: #2a2b2f; }
|
|
}
|
|
* { box-sizing: border-box; }
|
|
body { margin: 0; background: var(--bg); color: var(--fg); font: 16px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
|
|
main { max-width: 42rem; margin: 0 auto; padding: 1.5rem 1rem 3rem; }
|
|
h1 { font-size: 1.6rem; margin: 0 0 .25rem; }
|
|
h2 { font-size: 1.15rem; margin: 0 0 .5rem; }
|
|
p { margin: .5rem 0; }
|
|
.muted { color: var(--muted); }
|
|
section { background: var(--card); border-radius: 12px; padding: 1rem 1.1rem; margin: 1rem 0; }
|
|
section.current { outline: 2px solid var(--accent); }
|
|
a.button, button { display: inline-block; background: var(--accent); color: #fff; border: 0; border-radius: 8px; padding: .6rem 1rem; font: inherit; text-decoration: none; cursor: pointer; }
|
|
@media (prefers-color-scheme: dark) { a.button, button { color: #121314; } }
|
|
ol { padding-left: 1.3rem; margin: .5rem 0; }
|
|
pre { background: var(--code); border-radius: 8px; padding: .75rem; overflow-x: auto; white-space: pre-wrap; word-break: break-all; font-size: .85rem; margin: .5rem 0; }
|
|
details > summary { cursor: pointer; color: var(--accent); margin: 1.5rem 0 .5rem; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<main>
|
|
<h1>WLAN einrichten</h1>
|
|
<p>WLAN <strong>Renner UniFi</strong> für dein Konto <strong id="user">…</strong>. Android, iPhone, iPad, Mac und Windows bekommen ein eigenes WLAN-App-Passwort; Linux meldet sich mit deinem authentik-Passwort an.</p>
|
|
|
|
<div id="detected"></div>
|
|
|
|
<details id="others">
|
|
<summary>Andere Geräte</summary>
|
|
<div id="rest"></div>
|
|
</details>
|
|
|
|
<template id="t-android">
|
|
<section data-os="android">
|
|
<h2>Android</h2>
|
|
<p><a class="button" href="/android/install">Auf Android installieren</a></p>
|
|
<p class="muted">Das Gerät bekommt ein eigenes WLAN-App-Passwort in deinem authentik-Konto; dort gelöscht, ist das Gerät aus dem WLAN.</p>
|
|
</section>
|
|
</template>
|
|
|
|
<template id="t-apple">
|
|
<section data-os="apple">
|
|
<h2>iPhone, iPad, Mac</h2>
|
|
<p><a class="button" href="/apple/install">Profil laden</a></p>
|
|
<ol>
|
|
<li>iPhone/iPad: Einstellungen → „Profil geladen“ → Installieren. Mac: Systemeinstellungen → Allgemein → Geräteverwaltung → Profil doppelklicken.</li>
|
|
<li>Das Gerät verbindet sich danach selbst mit „Renner UniFi“.</li>
|
|
</ol>
|
|
<p class="muted">Das Gerät bekommt ein eigenes WLAN-App-Passwort in deinem authentik-Konto; dort gelöscht, ist das Gerät aus dem WLAN.</p>
|
|
</section>
|
|
</template>
|
|
|
|
<template id="t-windows">
|
|
<section data-os="windows">
|
|
<h2>Windows</h2>
|
|
<p><a class="button" href="/windows/install">Auf Windows installieren</a></p>
|
|
<p>Windows öffnet die Einstellungen und fragt, ob das WLAN-Profil hinzugefügt werden soll: „Hinzufügen“ wählen.</p>
|
|
<p class="muted">Das Gerät bekommt ein eigenes WLAN-App-Passwort in deinem authentik-Konto; dort gelöscht, ist das Gerät aus dem WLAN.</p>
|
|
</section>
|
|
</template>
|
|
|
|
<template id="t-linux">
|
|
<section data-os="linux">
|
|
<h2>Linux (NetworkManager)</h2>
|
|
<p>Im Terminal ausführen, danach fragt NetworkManager nach dem Passwort:</p>
|
|
<pre id="nmcli"></pre>
|
|
<p><button type="button" id="copy-nmcli">Befehl kopieren</button></p>
|
|
</section>
|
|
</template>
|
|
</main>
|
|
<script>
|
|
(function () {
|
|
var ua = navigator.userAgent;
|
|
var os = "other";
|
|
if (/Android/i.test(ua)) os = "android";
|
|
else if (/iPhone|iPad|iPod|Macintosh/i.test(ua)) os = "apple";
|
|
else if (/Windows/i.test(ua)) os = "windows";
|
|
else if (/Linux|X11/i.test(ua)) os = "linux";
|
|
|
|
var detected = document.getElementById("detected");
|
|
var rest = document.getElementById("rest");
|
|
["android", "apple", "windows", "linux"].forEach(function (name) {
|
|
var node = document.getElementById("t-" + name).content.cloneNode(true);
|
|
if (name === os) { node.querySelector("section").classList.add("current"); detected.appendChild(node); }
|
|
else rest.appendChild(node);
|
|
});
|
|
if (os === "other") document.getElementById("others").open = true;
|
|
|
|
function quote(s) { return "'" + s.replace(/'/g, "'\\''") + "'"; }
|
|
function render(user) {
|
|
document.getElementById("user").textContent = user || "(dein Benutzername)";
|
|
document.getElementById("nmcli").textContent =
|
|
"nmcli connection add type wifi con-name 'Renner UniFi' ssid 'Renner UniFi' " +
|
|
"wifi-sec.key-mgmt wpa-eap 802-1x.eap ttls 802-1x.phase2-auth pap " +
|
|
"802-1x.anonymous-identity anonymous 802-1x.identity " + quote(user || "BENUTZERNAME") + " " +
|
|
"802-1x.domain-suffix-match radius.renner.uno 802-1x.system-ca-certs yes " +
|
|
"802-1x.password-flags 2 && nmcli --ask connection up 'Renner UniFi'";
|
|
}
|
|
render("");
|
|
fetch("/whoami", { credentials: "same-origin" })
|
|
.then(function (r) { return r.ok ? r.text() : ""; })
|
|
.then(function (t) { render(t.trim()); })
|
|
.catch(function () {});
|
|
|
|
document.getElementById("copy-nmcli").addEventListener("click", function () {
|
|
var text = document.getElementById("nmcli").textContent;
|
|
var btn = this;
|
|
navigator.clipboard.writeText(text).then(function () {
|
|
btn.textContent = "Kopiert";
|
|
setTimeout(function () { btn.textContent = "Befehl kopieren"; }, 2000);
|
|
});
|
|
});
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|