Add security improvements: refresh tokens, passkeys, CSP, audit logging, TOTP encryption
- Shorten access token to 30min with 7-day refresh token flow - Add WebAuthn passkey registration and login with TOTP fallback - Add Content-Security-Policy header - Add audit logging middleware to /volume1/docker/nas-dashboard/audit.log - Block /volume1/docker/ in files endpoint - Encrypt TOTP secret at rest with Fernet (derived from SECRET_KEY) - New deps: py_webauthn, cryptography
This commit is contained in:
@@ -10,7 +10,10 @@ import auth as auth_module
|
||||
|
||||
import asyncio
|
||||
import httpx
|
||||
from config import TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, CORS_ORIGINS
|
||||
import logging
|
||||
import time
|
||||
import jwt
|
||||
from config import TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, CORS_ORIGINS, SECRET_KEY, ALGORITHM, VOLUME_ROOT
|
||||
|
||||
limiter = Limiter(key_func=get_remote_address)
|
||||
app = FastAPI(title="NAS Dashboard")
|
||||
@@ -35,6 +38,38 @@ async def security_headers(request: Request, call_next):
|
||||
response.headers["X-Frame-Options"] = "DENY"
|
||||
response.headers["X-XSS-Protection"] = "1; mode=block"
|
||||
response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
|
||||
response.headers["Content-Security-Policy"] = "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self' wss://nas.jimmygan.com; frame-ancestors 'none'"
|
||||
return response
|
||||
|
||||
# Audit logger
|
||||
import os
|
||||
_audit_log_path = os.path.join(VOLUME_ROOT, "docker/nas-dashboard/audit.log")
|
||||
os.makedirs(os.path.dirname(_audit_log_path), exist_ok=True)
|
||||
_audit_logger = logging.getLogger("audit")
|
||||
_audit_logger.setLevel(logging.INFO)
|
||||
_audit_handler = logging.FileHandler(_audit_log_path)
|
||||
_audit_handler.setFormatter(logging.Formatter("%(message)s"))
|
||||
_audit_logger.addHandler(_audit_handler)
|
||||
_audit_logger.propagate = False
|
||||
|
||||
@app.middleware("http")
|
||||
async def audit_log(request: Request, call_next):
|
||||
start = time.time()
|
||||
response = await call_next(request)
|
||||
duration = int((time.time() - start) * 1000)
|
||||
user = "-"
|
||||
try:
|
||||
auth_header = request.headers.get("authorization", "")
|
||||
if auth_header.startswith("Bearer "):
|
||||
payload = jwt.decode(auth_header[7:], SECRET_KEY, algorithms=[ALGORITHM])
|
||||
user = payload.get("sub", "-")
|
||||
except Exception:
|
||||
pass
|
||||
_audit_logger.info(
|
||||
"%s %s %s %s %s %d %dms",
|
||||
time.strftime("%Y-%m-%dT%H:%M:%S"), request.client.host, user,
|
||||
request.method, request.url.path, response.status_code, duration,
|
||||
)
|
||||
return response
|
||||
|
||||
async def monitor_containers():
|
||||
|
||||
Reference in New Issue
Block a user