Archived
Allow registration before first IP update
This commit is contained in:
1 parent
9f5cee8a0e
commit
c19e5170a0
5 files changed
+29
-20
No files matched your search
@@ -335,7 +335,7 @@ Example:
|
||||
curl -sS -X POST https://karldns.de/api/v1/hostnames
|
||||
```
|
||||
|
||||
The create response includes `registration_url`, `update_url`, and `dashboard_url`. Save the returned secrets, then `POST` the registration URL before using the update URL. Registration publishes the initial DNS record from the request IP; scripts may also pass `myip` or `myipv6` on the registration URL. Pending hostnames that are never registered are cleaned up automatically.
|
||||
The create response includes `registration_url`, `update_url`, and `dashboard_url`. Save the returned secrets, then `POST` the registration URL before using the update URL. Registration publishes an initial DNS record when a public request IP or explicit `myip`/`myipv6` value is available; otherwise the first router update publishes the record. Pending hostnames that are never registered are cleaned up automatically.
|
||||
|
||||
Use the dashboard secret to rotate the update URL:
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ async def activate_dashboard(
|
||||
management_slug,
|
||||
account,
|
||||
dashboard_csrf_token(management_slug, account),
|
||||
"Registration needs a public source IP. Try again from your home connection or use the API with myip.",
|
||||
"The supplied IP address is invalid. Leave IP fields empty and let the router send the first update.",
|
||||
client_ip(request, settings),
|
||||
),
|
||||
status_code=400,
|
||||
|
||||
+13
-12
@@ -72,21 +72,21 @@ class DdnsService:
|
||||
ipaddr: str | None,
|
||||
myipv6: str | None,
|
||||
ip6addr: str | None,
|
||||
) -> None:
|
||||
) -> bool:
|
||||
if not dns_backend_configured(self.settings):
|
||||
return
|
||||
return False
|
||||
source_ip = client_ip(request, self.settings)
|
||||
source_fallback = source_ip if _is_global_ip(source_ip) else ""
|
||||
try:
|
||||
ipv4, ipv6, update_ipv4, update_ipv6 = parse_update_ips(
|
||||
source_fallback,
|
||||
myip,
|
||||
ipaddr,
|
||||
myipv6,
|
||||
ip6addr,
|
||||
)
|
||||
except BadIpError as exc:
|
||||
raise BadIpError("registration needs a public source IP or myip/myipv6 parameter") from exc
|
||||
supplied_ip = any(value and value.strip() for value in (myip, ipaddr, myipv6, ip6addr))
|
||||
if not supplied_ip and not source_fallback:
|
||||
return False
|
||||
ipv4, ipv6, update_ipv4, update_ipv6 = parse_update_ips(
|
||||
source_fallback,
|
||||
myip,
|
||||
ipaddr,
|
||||
myipv6,
|
||||
ip6addr,
|
||||
)
|
||||
lock = self._lock_for(hostname)
|
||||
with lock:
|
||||
planned = self.store.preview_update(hostname, ipv4, ipv6, update_ipv4=update_ipv4, update_ipv6=update_ipv6)
|
||||
@@ -115,6 +115,7 @@ class DdnsService:
|
||||
raise
|
||||
result = self.store.commit_update_result(planned)
|
||||
self.store.log_update_event(hostname, result.ipv4, result.ipv6, UPDATED, "initial dns registered", source_ip)
|
||||
return True
|
||||
|
||||
def apply_update(
|
||||
self,
|
||||
|
||||
@@ -183,7 +183,7 @@ def _render_api_docs_page(settings: DdnsSettings, request_ip: str | None = None)
|
||||
<h2>Endpoints</h2>
|
||||
<ol class="simple-steps text-start">
|
||||
<li><strong>POST /api/v1/hostnames.</strong> Create a pending hostname and private setup values. The response includes <code>registration_url</code>.</li>
|
||||
<li><strong>GET or POST /api/v1/router/register/<karl_secret>.</strong> Register the hostname after secrets are saved. Uses the request IP, or <code>myip</code>/<code>myipv6</code>, for the initial DNS record.</li>
|
||||
<li><strong>GET or POST /api/v1/router/register/<karl_secret>.</strong> Register the hostname after secrets are saved. Publishes an initial DNS record when a public request IP or explicit <code>myip</code>/<code>myipv6</code> value is available; otherwise the first router update publishes it.</li>
|
||||
<li><strong>GET or POST /api/v1/router/update/<karl_secret>.</strong> Router-friendly update endpoint. Supports <code>myip</code>, <code>ipaddr</code>, <code>myipv6</code>, and <code>ip6addr</code>.</li>
|
||||
<li><strong>GET /api/v1/hostnames/<karl_secret>.</strong> Read hostname status and recent history.</li>
|
||||
<li><strong>POST /api/v1/hostnames/<karl_secret>/rotate-update-url.</strong> Replace the private router update URL.</li>
|
||||
|
||||
+13
-5
@@ -273,8 +273,9 @@ def test_registration_publishes_initial_dns_when_provider_is_configured(tmp_path
|
||||
assert published[0].ipv4 == "203.0.113.77"
|
||||
|
||||
|
||||
def test_registration_requires_ip_when_provider_source_ip_is_not_public(tmp_path: Path, monkeypatch) -> None:
|
||||
monkeypatch.setattr(ddns_module, "publish_dns", lambda result, settings: None)
|
||||
def test_registration_without_public_source_ip_waits_for_first_router_update(tmp_path: Path, monkeypatch) -> None:
|
||||
published = []
|
||||
monkeypatch.setattr(ddns_module, "publish_dns", lambda result, settings: published.append(result))
|
||||
app = make_app(
|
||||
DdnsSettings(
|
||||
database_path=tmp_path / "ddns.sqlite3",
|
||||
@@ -289,12 +290,19 @@ def test_registration_requires_ip_when_provider_source_ip_is_not_public(tmp_path
|
||||
client = TestClient(app)
|
||||
|
||||
body = client.post("/api/v1/hostnames").json()
|
||||
update_path = _path(body["update_url"])
|
||||
registered = client.post(_path(body["registration_url"]))
|
||||
status = client.get(f"/api/v1/hostnames/{body['dashboard_url'].rsplit('/', 1)[1]}")
|
||||
update = client.get(update_path, params={"myip": "203.0.113.80"})
|
||||
|
||||
assert registered.status_code == 400
|
||||
assert registered.text.startswith("badip registration needs a public source IP")
|
||||
assert status.json()["status"] == "pending"
|
||||
assert registered.status_code == 200
|
||||
assert registered.text == "good registered"
|
||||
assert status.json()["status"] == "active"
|
||||
assert status.json()["ipv4"] is None
|
||||
assert update.status_code == 200
|
||||
assert update.text == "good 203.0.113.80"
|
||||
assert len(published) == 1
|
||||
assert published[0].ipv4 == "203.0.113.80"
|
||||
|
||||
|
||||
def test_api_update_publishes_before_recording_ip(tmp_path: Path, monkeypatch) -> None:
|
||||
|
||||
Reference in new issue
Block a user