feat(hardening): login rate limiting, per-service memory limits, pinned images
- slowapi limiter on POST /auth/login (10/min per IP, Persian error msg); edge-level coarse limits stay in Traefik - mem_limit on every main-compose service (~4.4GB worst case, protects the 8GB shared host during rebuilds) - monitoring images pinned to verified tags (prom v3.4.1, grafana 11.6.3, loki/promtail 3.5.0, node-exporter v1.9.1, postgres-exporter v0.17.1, cadvisor v0.49.1); exporter image name fixed to postgres-exporter
This commit is contained in:
@@ -13,7 +13,7 @@ Error mapping (documented in docs/casdoor-auth.md):
|
||||
import logging
|
||||
|
||||
import httpx
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi import APIRouter, Depends, Request
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -23,6 +23,7 @@ from app.db.engine import get_db
|
||||
from app.routers.users import _user_projects, user_to_dict
|
||||
from app.services import analytics
|
||||
from app.utils.errors import ServiceUnavailableError, UnauthorizedError
|
||||
from app.utils.ratelimit import limiter
|
||||
|
||||
logger = logging.getLogger("procurement.auth")
|
||||
|
||||
@@ -35,7 +36,8 @@ class LoginRequest(BaseModel):
|
||||
|
||||
|
||||
@router.post("/login")
|
||||
async def login(payload: LoginRequest, db: AsyncSession = Depends(get_db)):
|
||||
@limiter.limit("10/minute", error_message="تلاشهای ورود بیش از حد مجاز — کمی بعد دوباره امتحان کنید")
|
||||
async def login(request: Request, payload: LoginRequest, db: AsyncSession = Depends(get_db)):
|
||||
"""Exchange username/password for a Casdoor JWT (OAuth password grant)."""
|
||||
url = f"{settings.CASDOOR_ENDPOINT}/api/login/oauth/access_token"
|
||||
form = {
|
||||
|
||||
Reference in New Issue
Block a user