snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
This commit is contained in:
75
zerver/management/commands/fetch_tor_exit_nodes.py
Normal file
75
zerver/management/commands/fetch_tor_exit_nodes.py
Normal file
@@ -0,0 +1,75 @@
|
||||
import os
|
||||
from argparse import ArgumentParser
|
||||
from typing import Any
|
||||
|
||||
import orjson
|
||||
from django.conf import settings
|
||||
from typing_extensions import override
|
||||
from urllib3.util import Retry
|
||||
|
||||
from zerver.lib.management import ZulipBaseCommand, abort_cron_during_deploy, abort_unless_locked
|
||||
from zerver.lib.outgoing_http import OutgoingSession
|
||||
|
||||
|
||||
class TorDataSession(OutgoingSession):
|
||||
def __init__(self, max_retries: int) -> None:
|
||||
Retry.DEFAULT_BACKOFF_MAX = 64
|
||||
retry = Retry(
|
||||
total=max_retries,
|
||||
backoff_factor=2.0,
|
||||
status_forcelist={ # Retry on these
|
||||
429, # The formal rate-limiting response code
|
||||
500, # Server error
|
||||
502, # Bad gateway
|
||||
503, # Service unavailable
|
||||
},
|
||||
)
|
||||
super().__init__(role="tor_data", timeout=3, max_retries=retry)
|
||||
|
||||
|
||||
class Command(ZulipBaseCommand):
|
||||
help = """Fetch the list of TOR exit nodes, and write the list of IP addresses
|
||||
to a file for access from Django for rate-limiting purposes.
|
||||
|
||||
Does nothing unless RATE_LIMIT_TOR_TOGETHER is enabled.
|
||||
"""
|
||||
|
||||
@override
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
parser.add_argument(
|
||||
"--max-retries",
|
||||
type=int,
|
||||
default=10,
|
||||
help="Number of times to retry fetching data from TOR",
|
||||
)
|
||||
|
||||
@override
|
||||
@abort_cron_during_deploy
|
||||
@abort_unless_locked
|
||||
def handle(self, *args: Any, **options: Any) -> None:
|
||||
if not settings.RATE_LIMIT_TOR_TOGETHER:
|
||||
return
|
||||
|
||||
session = TorDataSession(max_retries=options["max_retries"])
|
||||
response = session.get("https://check.torproject.org/exit-addresses")
|
||||
response.raise_for_status()
|
||||
|
||||
# Format:
|
||||
# ExitNode 4273E6D162ED2717A1CF4207A254004CD3F5307B
|
||||
# Published 2021-11-02 11:01:07
|
||||
# LastStatus 2021-11-02 23:00:00
|
||||
# ExitAddress 176.10.99.200 2021-11-02 23:17:02
|
||||
exit_nodes: set[str] = set()
|
||||
for line in response.text.splitlines():
|
||||
if line.startswith("ExitAddress "):
|
||||
exit_nodes.add(line.split()[1])
|
||||
|
||||
# Write to a tmpfile to ensure we can't read a partially-written file
|
||||
with open(settings.TOR_EXIT_NODE_FILE_PATH + ".tmp", "wb") as f:
|
||||
f.write(orjson.dumps(list(exit_nodes)))
|
||||
|
||||
# Do an atomic rename into place
|
||||
os.rename(
|
||||
settings.TOR_EXIT_NODE_FILE_PATH + ".tmp",
|
||||
settings.TOR_EXIT_NODE_FILE_PATH,
|
||||
)
|
||||
Reference in New Issue
Block a user