[Hardening] Bot HTTP server: default bind to 127.0.0.1 and cap /notify request size #111
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
The bot runs an internal aiohttp server for incoming calls from the backend (
/health,/notify,/record/*— routes registered atbot/questboard_bot/main.py:217-220, served viaweb.TCPSite(runner, settings.http_host, settings.http_port)at:291).Its bind address defaults to all interfaces:
http_host: str = "0.0.0.0"(bot/questboard_bot/config.py:27). Under Docker Compose this is safe — the bot service usesexposeonly, no published ports (docker-compose.yml:122-123). But anyone running the bot bare-metal (outside compose) gets an unauthenticated-surface-except-for-X-Bot-Key HTTP server listening on every interface by default. Secure-by-default says loopback.Additionally, the aiohttp app is created as bare
web.Application()(main.py:103) with no explicitclient_max_size. Note: aiohttp's default is already 1 MiB, so today's behavior is acceptable — but the bound is implicit and unpinned; make it explicit so a future aiohttp default change or a copy-paste refactor can't silently remove it, and so the intent is documented.Motivation
Defense in depth for self-hosters: loopback default bind, explicit request-size bound on
/notify(main.py:109-126) so a leakedBOT_API_KEY(or the pre-auth request-body read) can't be used for memory abuse with giant JSON payloads.Fix / Spec
bot/questboard_bot/config.py:27tohttp_host: str = "127.0.0.1".HTTP_HOST=0.0.0.0(matching the settings env prefix used by the bot's config; verify the exact env var name pydantic-settings expects) to the bot serviceenvironment:indocker-compose.yml, otherwise backend→bot notifications break because the backend reaches the bot over the Docker network. Verify with the compose stack after the change.web.Application(client_max_size=1024**2)atmain.py:103, with a short comment..env.example(bot section) and the relevant docs page (docs/OPERATIONS.mdor the bot README section).Acceptance criteria
/notifydelivery still works end-to-end (verify a notification or/healthreachability from the backend container).HTTP_HOSTenv: server binds 127.0.0.1 (assert via config test or netstat)./notifyPOST with a body over 1 MiB is rejected (413) instead of being read into memory.References
bot/questboard_bot/config.py:25-28(bind defaults)bot/questboard_bot/main.py:103(web.Application()),:109-126(/notifyhandler),:217-220(routes),:289-291(AppRunner/TCPSite)docker-compose.yml:114-123(bot service;exposeonly)Filed from the July 2026 full-project review.
Picking this up as part of a v3.3.0 push. Landing on branch
hardening/bottogether with #83, #85, and #94 (grouped by component to keep the diffs reviewable).Fixed on
main(commit38945bd, merged viacd315d6).http_hostdefault changed to127.0.0.1inconfig.py;docker-compose.ymloverrides withHTTP_HOST=0.0.0.0on the bot service so backend→bot notifications keep working over the Docker network (verified the config uses no env prefix, soHTTP_HOSTis the correct var). Body cap made explicit asweb.Application(client_max_size=1024**2)..env.exampledocuments the default and the compose override. Tests assert the loopback default with no env var and the explicitclient_max_size. Bot suite green (158 passed).