zilencer: Add flow for a server to reclaim its registration.

If the server controls the registration's hostname, it can reclaim its
registration credentials. This is useful, because self-hosted admins
frequently lose the credentials when moving their Zulip server to a
different machine / deployment method.

The flow is the following:
1. The host sends a POST request to
   /api/v1/remotes/server/register/takeover.
2. The bouncer responds with a signed token.
3. The host prepares to serve this token at /api/v1/zulip-services/verify and
   sends a POST to /remotes/server/register/verify_challenge endpoint of
   the bouncer.
4. Upon receiving the POST request, the bouncer GETS
   https://{hostname}/api/v1/zulip-services/verify, verifies the secret and
   responds to the original POST with the registration credentials.
5. The host can now save these credentials to it zulip-secrets.conf file
   and thus regains its push notifications registration.

Includes a global rate limit on the usage of the /verify_challenge
endpoint, as it causes us to make outgoing requests.
This commit is contained in:
Mateusz Mandera
2024-11-19 23:16:01 +01:00
committed by Tim Abbott
parent a8625df748
commit 4e22a79e6a
12 changed files with 702 additions and 11 deletions

View File

@@ -1,4 +1,5 @@
import logging
import secrets
from collections.abc import Mapping
from typing import Any
from urllib.parse import urljoin
@@ -489,3 +490,19 @@ def maybe_enqueue_audit_log_upload(realm: Realm) -> None:
if uses_notification_bouncer():
event = {"type": "push_bouncer_update_for_realm", "realm_id": realm.id}
queue_event_on_commit("deferred_work", event)
SELF_HOSTING_REGISTRATION_TAKEOVER_CHALLENGE_TOKEN_REDIS_KEY = (
"self_hosting_domain_takeover_challenge_verify"
)
def prepare_for_registration_takeover_challenge(verification_secret: str) -> str:
access_token = secrets.token_urlsafe(32)
data_to_store = {"verification_secret": verification_secret, "access_token": access_token}
redis_client.set(
redis_utils.REDIS_KEY_PREFIX + SELF_HOSTING_REGISTRATION_TAKEOVER_CHALLENGE_TOKEN_REDIS_KEY,
orjson.dumps(data_to_store),
ex=10,
)
return access_token