Go-live hardening: server_tokens, resource limits, pinned images, CVE fixes

- Add server_tokens off to nginx (suppress version leakage)
- Add deploy.resources.limits to all containers (db: 512M, api: 256M, nginx: 64M)
- Pin image tags: mysql:8.0 → 8.0.45, nginx:alpine → 1.29.6-alpine
- Fix CVEs: cryptography 43.0.3 → 46.0.5 (HIGH), python-jose 3.3.0 → 3.4.0 (CRITICAL)
- Add .limit(500) to GET /api/flock and GET /api/admin/users

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 00:16:28 -07:00
parent 59f9685e2b
commit 392a48dfb9
5 changed files with 23 additions and 5 deletions

View File

@@ -2,8 +2,8 @@ fastapi==0.115.0
uvicorn==0.32.0
sqlalchemy==2.0.36
pymysql==1.1.1
cryptography==43.0.3
cryptography==46.0.5
pydantic==2.9.2
python-jose[cryptography]==3.3.0
python-jose[cryptography]==3.4.0
passlib[bcrypt]==1.7.4
bcrypt==4.0.1

View File

@@ -18,7 +18,7 @@ def list_users(
_: User = Depends(get_current_admin),
db: Session = Depends(get_db),
):
return db.scalars(select(User).order_by(User.created_at)).all()
return db.scalars(select(User).order_by(User.created_at).limit(500)).all()
@router.post("/users", response_model=UserOut, status_code=201)

View File

@@ -22,6 +22,7 @@ def list_flock_history(
select(FlockHistory)
.where(FlockHistory.user_id == current_user.id)
.order_by(FlockHistory.date.desc())
.limit(500)
)
return db.scalars(q).all()

View File

@@ -4,8 +4,13 @@ services:
# MYSQL_ROOT_PASSWORD and MYSQL_PASSWORD should each be 20+ random characters.
# Generate with: openssl rand -hex 16
db:
image: mysql:8.0
image: mysql:8.0.45
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
env_file: .env
logging:
driver: json-file
@@ -33,6 +38,11 @@ services:
api:
build: ./backend
restart: unless-stopped
deploy:
resources:
limits:
cpus: '1.0'
memory: 256M
env_file: .env
logging:
driver: json-file
@@ -58,8 +68,13 @@ services:
# ── Nginx ────────────────────────────────────────────────────────────────────
nginx:
image: nginx:alpine
image: nginx:1.29.6-alpine
restart: unless-stopped
deploy:
resources:
limits:
cpus: '0.5'
memory: 64M
logging:
driver: json-file
options:

View File

@@ -9,6 +9,8 @@ http {
sendfile on;
# ── Gzip compression ──────────────────────────────────────────────────────
server_tokens off;
gzip on;
gzip_types text/plain text/css application/javascript application/json;
gzip_min_length 1000;