Key Encryption

This commit is contained in:
2026-05-18 01:33:23 +05:00
parent a552e55c29
commit c3422f7c67
11 changed files with 831 additions and 16 deletions
+37 -1
View File
@@ -3,14 +3,16 @@ from __future__ import annotations
from contextlib import asynccontextmanager
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from fastapi import FastAPI
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from loguru import logger
from .api.router import api_router
from .core.bootstrap import init_db
from .core.config import get_settings
from .core.db import SessionLocal
from .services.vault import VaultLocked, VaultNotInitialized, vault_service
def _job_firmware_check() -> None:
@@ -36,6 +38,14 @@ def _job_probe_devices() -> None:
fetch_identity, fetch_interface_stats, fetch_resource, parse_uptime,
)
from datetime import datetime, timedelta, timezone
# Если vault уже инициализирован, но заблокирован — пропускаем итерацию:
# без DEK не получится расшифровать password_enc устройств в формате v2.
# До инициализации (legacy-режим) опрос продолжается со старым ключом из SECRET_KEY.
if vault_service.is_initialized_cached() is True and not vault_service.is_unlocked():
logger.info("probe_devices: vault locked, пропускаем итерацию")
return
db = SessionLocal()
try:
for d in db.query(Device).all():
@@ -160,6 +170,18 @@ async def lifespan(_: FastAPI):
logger.info("Starting ROSzetta API ({} env)", settings.app_env)
init_db()
# Прогреваем кеш «vault initialized?» — нужен encrypt_secret() для legacy-fallback
# и probe-джобе, чтобы решать skip/run без обращения к БД.
try:
_db = SessionLocal()
try:
initialized = vault_service.refresh_initialized_cache(_db)
logger.info("Vault initialized={}, unlocked={}", initialized, vault_service.is_unlocked())
finally:
_db.close()
except Exception as exc: # pragma: no cover
logger.warning("vault init-cache refresh failed: {}", exc)
# FTP-сервер для приёма push-бэкапов от MikroTik
try:
from .services.backup_ftp_server import start_server
@@ -225,6 +247,20 @@ def create_app() -> FastAPI:
allow_methods=["*"],
allow_headers=["*"],
)
@app.exception_handler(VaultLocked)
async def _vault_locked(_: Request, exc: VaultLocked) -> JSONResponse:
# 423 Locked — стандартный HTTP-код для «ресурс заперт»; фронт ловит его и
# показывает форму ввода мастер-пароля.
return JSONResponse(status_code=423, content={"detail": str(exc), "code": "vault_locked"})
@app.exception_handler(VaultNotInitialized)
async def _vault_uninit(_: Request, exc: VaultNotInitialized) -> JSONResponse:
return JSONResponse(
status_code=412,
content={"detail": str(exc), "code": "vault_not_initialized"},
)
app.include_router(api_router)
return app