Feat/docker deployment - #1
Merged
Merged
Conversation
The suite left tables in place between tests. `app` is a module-level singleton, so the in-memory SQLite engine is reused for the whole session and rows created by one test's fixtures collided with the next test's inserts, failing with "UNIQUE constraint failed: #control.setup". Restore the missing db.drop_all() teardown, and make ldap_test_app depend on test_app so the tables exist before it runs. Also assert on db.engine.url, not just the configured URI. The config value is what we asked for; the engine is what create_all/drop_all actually operate on. #control and #task are mapped models, so a drop_all() against the production server would drop the live experiment tables. The existing check would pass if an engine had already been built from another URI. 13 passed, previously 7 passed with 6 errors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MvZQkduzotzQy2F5LaaBnE
The login form was an unthrottled gateway to the LDAP directory. Since
directories lock accounts after a few failures, anyone could lock out
every account in the lab.
Add two independent limits on POST /login:
per IP generous, because lab members may share one NAT'd address
per username the important one. Accounts lock individually, so an
attacker rotating addresses could still lock one person
out. Keying on the submitted username holds regardless of
source.
Only failed attempts count, so normal users never consume their own
quota. No default limits are applied: the control table and activity
monitor poll their endpoints every few seconds and would otherwise get
429s.
Counters live in per-worker memory, which needs no extra service but
means the effective limit is roughly 4x the configured value with 4
workers. Set the limits so value x workers stays below the directory's
lockout threshold. RATELIMIT_STORAGE_URI can point at a shared store to
make them exact, falling back to memory if it is unreachable.
Also harden the session cookie: HttpOnly, SameSite=Lax, configurable
lifetime, and an opt-in Secure flag. ProxyFix is applied only when
TRUST_PROXY_HEADERS is set, since trusting X-Forwarded-For while directly
reachable would let clients forge their address and skip the limit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MvZQkduzotzQy2F5LaaBnE
Replaces a hand-built venv and gunicorn invocation with `docker compose up -d`, so the app can be moved to another machine without knowing Python, and comes back on its own after a crash or a reboot. Dockerfile: single-stage python:3.12-slim. No compiler is needed since every dependency is pure Python or ships manylinux wheels. Dependencies are installed before the code is copied, so a code-only rebuild reuses the layer cache and takes about two seconds. Runs as a non-root user. The health check uses urllib rather than curl, which slim does not ship, and targets /login because / only redirects. Entry point is `main:app`, which works because main.py imports app at module scope. Importing it skips main()'s database pre-flight check, which is wanted here: a transient database blip should serve errors rather than crash-loop the container. .dockerignore keeps the live .env out of the image and excludes the recursive build/lib tree, cutting the build context from 314 MB to under 2 KB. Tests are deliberately kept in the image so the deployed artefact can check itself. Compose serves plain HTTP on a configurable HOST_PORT, published on the local network. restart: unless-stopped covers crashes and reboots. FLASK_CONFIG is pinned to production so the Werkzeug debugger cannot be switched on by a stale .env, and FLASK_ENV is deliberately never set because it is an authentication bypass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MvZQkduzotzQy2F5LaaBnE
DEPLOY.md is the operator runbook: deploying on a new machine, everyday commands, what recovers what and what does not, updating after a code change, migrating an existing gunicorn install, and what every .env value is and who to ask for it. It records two traps that are easy to misdiagnose. Docker never restarts a container a human stopped, and it counts `docker kill` as manual, so the obvious way to test the restart policy appears to prove it broken. And `ufw` does not filter Docker-published ports, because Docker's rules are evaluated first, so a rule that looks active does nothing. docs/docker.md explains how the setup works and why, with diagrams: what is in the image, why dependencies are copied before the code, how credentials get in without being baked in, how a request reaches the app, and which failures recover themselves. docs/setup.md previously carried an aspirational Docker section describing files that never existed, with a wrong port and unnecessary packages. Two conflicting guides is the worst outcome for whoever inherits this, so it now points at the real one. mkdocs.yml gains the markdown extensions the new pages need, including mermaid rendering. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MvZQkduzotzQy2F5LaaBnE
This repository is public and its docs publish to GitHub Pages. Notes that name real hosts or describe how a particular site's network is arranged should not be committed. private/ is handed over directly, the same way .env is. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MvZQkduzotzQy2F5LaaBnE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.