Archived
Clarify optional router credentials
This commit is contained in:
1 parent
67477ab40f
commit
f865bc28c7
3 files changed
+29
-21
No files matched your search
@@ -144,13 +144,15 @@ export DDNS_TTL=60
|
|||||||
|
|
||||||
## FRITZ!Box DynDNS Setup
|
## FRITZ!Box DynDNS Setup
|
||||||
|
|
||||||
Create a hostname in the web UI. RouterPulse shows the exact values for:
|
Create a hostname in the web UI. For the normal accountless flow, the generated `Update-URL` already contains the secret update slug. Put that URL into the router and use the generated hostname as `Domainnamen`.
|
||||||
|
|
||||||
|
RouterPulse also shows optional compatibility fields for routers that insist on separate credentials:
|
||||||
|
|
||||||
- `Update-URL`
|
- `Update-URL`
|
||||||
- `Domainnamen`
|
- `Domainnamen`
|
||||||
- `CNAME target`
|
- `CNAME target`
|
||||||
- `Benutzername`
|
- `Benutzername` (optional)
|
||||||
- `Kennwort`
|
- `Kennwort` (optional)
|
||||||
|
|
||||||
The generated FRITZ!Box URL uses the native placeholders:
|
The generated FRITZ!Box URL uses the native placeholders:
|
||||||
|
|
||||||
@@ -158,7 +160,7 @@ The generated FRITZ!Box URL uses the native placeholders:
|
|||||||
https://ddns.example.net/u/<random-update-slug>?myip=<ipaddr>&myipv6=<ip6addr>
|
https://ddns.example.net/u/<random-update-slug>?myip=<ipaddr>&myipv6=<ip6addr>
|
||||||
```
|
```
|
||||||
|
|
||||||
This URL is enough for routers that only expose a single update URL field. RouterPulse also shows a `One-box update URL` that embeds `hostname`, `username`, and `password` as query parameters for clients that require credential-style DynDNS URLs but do not expose separate credential fields. Treat both URLs as secrets.
|
This URL is enough for routers that only expose a single update URL field and do not send separate credentials. RouterPulse also shows a `One-box update URL` that embeds `hostname`, `username`, and `password` as query parameters for clients that require credential-style DynDNS URLs but do not expose separate credential fields. Treat both URLs as secrets.
|
||||||
|
|
||||||
The compatibility endpoint is also available:
|
The compatibility endpoint is also available:
|
||||||
|
|
||||||
|
|||||||
+13
-13
@@ -463,8 +463,8 @@ def make_app(settings: DdnsSettings | None = None) -> FastAPI:
|
|||||||
[
|
[
|
||||||
_copy_row("Domainnamen:", str(rotated["hostname"]), "Public"),
|
_copy_row("Domainnamen:", str(rotated["hostname"]), "Public"),
|
||||||
_copy_row("One-box update URL:", service.credentialed_update_url(rotated), "Secret"),
|
_copy_row("One-box update URL:", service.credentialed_update_url(rotated), "Secret"),
|
||||||
_copy_row("Benutzername:", str(rotated["username"]), "Public"),
|
_copy_row("Benutzername (optional):", str(rotated["username"]), "Public"),
|
||||||
_copy_row("Kennwort:", str(rotated["password"]), "Secret"),
|
_copy_row("Kennwort (optional):", str(rotated["password"]), "Secret"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -820,12 +820,12 @@ def _credentials_panel(service: DdnsService, created_account: dict[str, str] | N
|
|||||||
return f"""
|
return f"""
|
||||||
<section class="section success-section">
|
<section class="section success-section">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="eyebrow">Credentials generated</p>
|
<p class="eyebrow">Hostname generated</p>
|
||||||
<h2>{html.escape(hostname)}</h2>
|
<h2>{html.escape(hostname)}</h2>
|
||||||
<p class="section-copy">Save this now. The password, private status page, and one-box update URL are only shown on this screen. To use your own DNS name, create a CNAME from that name to this hostname.</p>
|
<p class="section-copy">Use the Update-URL directly. The separate Benutzername and Kennwort values are optional compatibility fields for routers that require them. Save this now: the password, private status page, and one-box update URL are only shown on this screen.</p>
|
||||||
{_secret_notice()}
|
{_secret_notice()}
|
||||||
<div class="d-flex gap-2 flex-wrap my-3">
|
<div class="d-flex gap-2 flex-wrap my-3">
|
||||||
{_copy_button("Copy FRITZ!Box fields", fritzbox_fields)}
|
{_copy_button("Copy complete FRITZ!Box fields", fritzbox_fields)}
|
||||||
{_copy_button("Copy one-box update URL", credentialed_update_url)}
|
{_copy_button("Copy one-box update URL", credentialed_update_url)}
|
||||||
{_copy_button("Copy CNAME record", cname_record)}
|
{_copy_button("Copy CNAME record", cname_record)}
|
||||||
</div>
|
</div>
|
||||||
@@ -834,8 +834,8 @@ def _credentials_panel(service: DdnsService, created_account: dict[str, str] | N
|
|||||||
{_copy_row("One-box update URL:", credentialed_update_url, "Secret")}
|
{_copy_row("One-box update URL:", credentialed_update_url, "Secret")}
|
||||||
{_copy_row("Domainnamen:", hostname, "Public")}
|
{_copy_row("Domainnamen:", hostname, "Public")}
|
||||||
{_copy_row("CNAME target:", hostname, "Public")}
|
{_copy_row("CNAME target:", hostname, "Public")}
|
||||||
{_copy_row("Benutzername:", username, "Public")}
|
{_copy_row("Benutzername (optional):", username, "Public")}
|
||||||
{_copy_row("Kennwort:", password, "Secret")}
|
{_copy_row("Kennwort (optional):", password, "Secret")}
|
||||||
{_copy_row("Private status page:", management_url, "Secret")}
|
{_copy_row("Private status page:", management_url, "Secret")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -904,7 +904,7 @@ def _secret_notice() -> str:
|
|||||||
return """
|
return """
|
||||||
<div class="alert alert-info my-3 py-2 small" role="note">
|
<div class="alert alert-info my-3 py-2 small" role="note">
|
||||||
<strong>Public:</strong> Domainnamen and CNAME target can be shared in DNS.
|
<strong>Public:</strong> Domainnamen and CNAME target can be shared in DNS.
|
||||||
<strong>Secret:</strong> Update-URL, one-box update URL, Kennwort, private status page, and domain claim links control the hostname.
|
<strong>Secret:</strong> Update-URL, one-box update URL, optional Kennwort, private status page, and domain claim links control the hostname.
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -956,11 +956,11 @@ def _render_management_page(service: DdnsService, settings: DdnsSettings, manage
|
|||||||
<div class="row g-4 align-items-start">
|
<div class="row g-4 align-items-start">
|
||||||
<div class="col-lg-6">
|
<div class="col-lg-6">
|
||||||
<p class="eyebrow">Router settings</p>
|
<p class="eyebrow">Router settings</p>
|
||||||
<h2>FRITZ!Box fields</h2>
|
<h2>Router update settings</h2>
|
||||||
<p class="section-copy">Use the Update-URL directly if your router only has one URL field. The username and password are shown only when generated or rotated. If you use your own DNS name, point it at this hostname with a CNAME.</p>
|
<p class="section-copy">Use the Update-URL directly if your router only has one URL field. Separate Benutzername and Kennwort values are optional compatibility fields and are only shown when generated or rotated. If you use your own DNS name, point it at this hostname with a CNAME.</p>
|
||||||
{_secret_notice()}
|
{_secret_notice()}
|
||||||
<div class="d-flex gap-2 flex-wrap mt-3">
|
<div class="d-flex gap-2 flex-wrap mt-3">
|
||||||
{_copy_button("Copy FRITZ!Box fields", fritzbox_fields)}
|
{_copy_button("Copy complete FRITZ!Box fields", fritzbox_fields)}
|
||||||
{_copy_button("Copy CNAME record", cname_record)}
|
{_copy_button("Copy CNAME record", cname_record)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -968,8 +968,8 @@ def _render_management_page(service: DdnsService, settings: DdnsSettings, manage
|
|||||||
{_copy_row("Update-URL:", update_url, "Secret")}
|
{_copy_row("Update-URL:", update_url, "Secret")}
|
||||||
{_copy_row("Domainnamen:", hostname, "Public")}
|
{_copy_row("Domainnamen:", hostname, "Public")}
|
||||||
{_copy_row("CNAME target:", hostname, "Public")}
|
{_copy_row("CNAME target:", hostname, "Public")}
|
||||||
{_copy_row("Benutzername:", username, "Public")}
|
{_copy_row("Benutzername (optional):", username, "Public")}
|
||||||
{_copy_row("Kennwort:", "unchanged; only shown when generated", "Secret")}
|
{_copy_row("Kennwort (optional):", "unchanged; only shown when generated", "Secret")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+10
-4
@@ -344,7 +344,7 @@ def test_admin_can_rotate_links_and_password(tmp_path: Path) -> None:
|
|||||||
assert "Router password rotated" in password.text
|
assert "Router password rotated" in password.text
|
||||||
assert "One-box update URL:" in password.text
|
assert "One-box update URL:" in password.text
|
||||||
assert "/nic/update?" in password.text
|
assert "/nic/update?" in password.text
|
||||||
assert "Kennwort:" in password.text
|
assert "Kennwort (optional):" in password.text
|
||||||
|
|
||||||
|
|
||||||
def test_self_service_managed_hostname_generates_random_account(tmp_path: Path) -> None:
|
def test_self_service_managed_hostname_generates_random_account(tmp_path: Path) -> None:
|
||||||
@@ -364,7 +364,9 @@ def test_self_service_managed_hostname_generates_random_account(tmp_path: Path)
|
|||||||
assert "Update-URL:" in response.text
|
assert "Update-URL:" in response.text
|
||||||
assert "One-box update URL:" in response.text
|
assert "One-box update URL:" in response.text
|
||||||
assert "CNAME target:" in response.text
|
assert "CNAME target:" in response.text
|
||||||
assert "Copy FRITZ!Box fields" in response.text
|
assert "Benutzername (optional):" in response.text
|
||||||
|
assert "Kennwort (optional):" in response.text
|
||||||
|
assert "Copy complete FRITZ!Box fields" in response.text
|
||||||
assert "Copy CNAME record" in response.text
|
assert "Copy CNAME record" in response.text
|
||||||
assert "Public:" in response.text
|
assert "Public:" in response.text
|
||||||
assert "Secret:" in response.text
|
assert "Secret:" in response.text
|
||||||
@@ -391,8 +393,10 @@ def test_magic_hostname_without_login_generates_update_and_management_links(tmp_
|
|||||||
assert "Update-URL:" in response.text
|
assert "Update-URL:" in response.text
|
||||||
assert "One-box update URL:" in response.text
|
assert "One-box update URL:" in response.text
|
||||||
assert "CNAME target:" in response.text
|
assert "CNAME target:" in response.text
|
||||||
|
assert "Benutzername (optional):" in response.text
|
||||||
|
assert "Kennwort (optional):" in response.text
|
||||||
assert "Private status page:" in response.text
|
assert "Private status page:" in response.text
|
||||||
assert "Copy FRITZ!Box fields" in response.text
|
assert "Copy complete FRITZ!Box fields" in response.text
|
||||||
assert "Copy CNAME record" in response.text
|
assert "Copy CNAME record" in response.text
|
||||||
assert response.text.count("Secret") >= 3
|
assert response.text.count("Secret") >= 3
|
||||||
assert response.text.count("Public") >= 3
|
assert response.text.count("Public") >= 3
|
||||||
@@ -420,7 +424,9 @@ def test_magic_management_link_can_delete_hostname(tmp_path: Path) -> None:
|
|||||||
assert management.status_code == 200
|
assert management.status_code == 200
|
||||||
assert "home.ddns.example.net" in management.text
|
assert "home.ddns.example.net" in management.text
|
||||||
assert "CNAME target:" in management.text
|
assert "CNAME target:" in management.text
|
||||||
assert "Copy FRITZ!Box fields" in management.text
|
assert "Benutzername (optional):" in management.text
|
||||||
|
assert "Kennwort (optional):" in management.text
|
||||||
|
assert "Copy complete FRITZ!Box fields" in management.text
|
||||||
assert "Copy CNAME record" in management.text
|
assert "Copy CNAME record" in management.text
|
||||||
assert "Public:" in management.text
|
assert "Public:" in management.text
|
||||||
assert "Secret:" in management.text
|
assert "Secret:" in management.text
|
||||||
|
|||||||
Reference in new issue
Block a user