Skip to content

Feat/docker deployment - #1

Merged
alexevag merged 5 commits into
mainfrom
feat/docker-deployment
Sep 2, 2026
Merged

Feat/docker deployment#1
alexevag merged 5 commits into
mainfrom
feat/docker-deployment

Conversation

@alexevag

@alexevag alexevag commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

No description provided.

alexevag and others added 5 commits September 2, 2026 14:19
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
@alexevag
alexevag merged commit 8bf7f82 into main Sep 2, 2026
2 checks passed
@alexevag
alexevag deleted the feat/docker-deployment branch September 2, 2026 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant