diff --git a/karldns/security.py b/karldns/security.py index 06654e7..bccc14d 100644 --- a/karldns/security.py +++ b/karldns/security.py @@ -15,6 +15,7 @@ from .settings import DdnsSettings DASHBOARD_UNLOCK_COOKIE = "ddns_dashboard_unlock" DASHBOARD_UNLOCK_DEFAULT_SECONDS = 24 * 60 * 60 DASHBOARD_UNLOCK_STAY_SIGNED_IN_SECONDS = 30 * 24 * 60 * 60 +BEARER_SECRET_PREFIX = "karl_" PRIVATE_PROXY_NETWORKS = tuple( ip_network(network) for network in ( @@ -62,6 +63,10 @@ def hash_lookup_token(token: str) -> str: return hashlib.sha256(token.encode("utf-8")).hexdigest() +def new_bearer_secret() -> str: + return f"{BEARER_SECRET_PREFIX}{secrets.token_urlsafe(32)}" + + def sign_dashboard_unlock_cookie( hostname: str, management_slug: str, diff --git a/karldns/store/accounts.py b/karldns/store/accounts.py index 5b3e83b..d2127be 100644 --- a/karldns/store/accounts.py +++ b/karldns/store/accounts.py @@ -1,17 +1,16 @@ from __future__ import annotations -import secrets from datetime import UTC, datetime -from ..security import hash_lookup_token, hash_secret, verify_secret +from ..security import hash_lookup_token, hash_secret, new_bearer_secret, verify_secret from .types import AccountRow class AccountsMixin: def create_account(self, hostname: str, username: str | None = None, *, accepted: bool = True) -> AccountRow: now = datetime.now(UTC).isoformat() - update_slug = secrets.token_urlsafe(32) - management_slug = secrets.token_urlsafe(32) + update_slug = new_bearer_secret() + management_slug = new_bearer_secret() management_hash = hash_lookup_token(management_slug) accepted_at = now if accepted else None with self.connect() as conn: @@ -103,13 +102,13 @@ class AccountsMixin: conn.execute("DELETE FROM records WHERE hostname = ?", (hostname,)) def rotate_update_slug(self, hostname: str) -> AccountRow | None: - update_slug = secrets.token_urlsafe(32) + update_slug = new_bearer_secret() with self.connect() as conn: conn.execute("UPDATE accounts SET update_slug = ? WHERE hostname = ?", (update_slug, hostname)) return self.get_account_by_hostname(hostname) def rotate_management_slug(self, hostname: str) -> AccountRow | None: - management_slug = secrets.token_urlsafe(32) + management_slug = new_bearer_secret() management_hash = hash_lookup_token(management_slug) with self.connect() as conn: conn.execute("UPDATE accounts SET management_hash = ? WHERE hostname = ?", (management_hash, hostname)) diff --git a/karldns/store/schema.py b/karldns/store/schema.py index a32d6b5..e93d00a 100644 --- a/karldns/store/schema.py +++ b/karldns/store/schema.py @@ -1,10 +1,9 @@ from __future__ import annotations -import secrets import sqlite3 from pathlib import Path -from ..security import hash_lookup_token +from ..security import hash_lookup_token, new_bearer_secret BUSY_TIMEOUT_MS = 10_000 CACHE_SIZE_KIB = 20_000 @@ -160,11 +159,11 @@ def initialize_schema(conn: sqlite3.Connection) -> None: if schema_version < 2: conn.execute("UPDATE accounts SET accepted_at = created_at WHERE accepted_at IS NULL") for row in conn.execute("SELECT hostname FROM accounts WHERE update_slug IS NULL OR update_slug = ''"): - conn.execute("UPDATE accounts SET update_slug = ? WHERE hostname = ?", (secrets.token_urlsafe(32), row[0])) + conn.execute("UPDATE accounts SET update_slug = ? WHERE hostname = ?", (new_bearer_secret(), row[0])) for row in conn.execute("SELECT hostname FROM accounts WHERE management_hash IS NULL OR management_hash = ''"): conn.execute( "UPDATE accounts SET management_hash = ? WHERE hostname = ?", - (hash_lookup_token(secrets.token_urlsafe(32)), row[0]), + (hash_lookup_token(new_bearer_secret()), row[0]), ) _migrate_accounts_schema(conn) conn.execute("CREATE UNIQUE INDEX IF NOT EXISTS idx_accounts_update_slug ON accounts(update_slug)") diff --git a/karldns/web/templates_management.py b/karldns/web/templates_management.py index fccb63f..5d2f6dc 100644 --- a/karldns/web/templates_management.py +++ b/karldns/web/templates_management.py @@ -162,7 +162,6 @@ def _render_pending_management_page( "management_slug": management_slug, } update_url = service.fritz_update_url(account_for_url) - registration_url = service.registration_url({"management_slug": management_slug}) dashboard_url = service.dashboard_url({"management_slug": management_slug}) hostname = escape(raw_hostname) message_html = _management_message(message) @@ -188,15 +187,13 @@ def _render_pending_management_page(
Save
This page is private. You can revisit it while the setup is pending. After registration, the dashboard hides secrets by default.
-Public: hostname and CNAME target. Secret: update URL, register URL, and private dashboard.
+Save the update URL and dashboard link before you register. After registration, the dashboard hides secrets by default.
+Public: Domainnamen. Secret: Update-URL and private dashboard.