Key Encryption
This commit is contained in:
@@ -19,6 +19,7 @@ def init_db() -> None:
|
||||
from ..models import metric as _metric # noqa: F401
|
||||
from ..models import settings as _settings # noqa: F401
|
||||
from ..models import interface_stat as _ifs # noqa: F401
|
||||
from ..models import vault as _vault # noqa: F401
|
||||
|
||||
Base.metadata.create_all(bind=engine)
|
||||
_ensure_columns()
|
||||
|
||||
@@ -57,9 +57,19 @@ def decode_token(token: str) -> dict[str, Any]:
|
||||
|
||||
|
||||
# --- Симметричное шифрование секретов устройств -----------------------------
|
||||
# Производный ключ из SECRET_KEY (для dev). В prod — KMS / Vault.
|
||||
# Двухслойная схема:
|
||||
# v1: Fernet от SHA256(SECRET_KEY) — устаревший формат (без префикса для
|
||||
# обратной совместимости). Только дешифрация. Запись новых v1 запрещена.
|
||||
# v2: AES-256-GCM от DEK из services.vault, префикс "v2:".
|
||||
#
|
||||
# encrypt_secret() требует, чтобы vault был инициализирован и разблокирован.
|
||||
# decrypt_secret() умеет читать оба формата.
|
||||
# Подробнее см. services/vault.py.
|
||||
|
||||
def _fernet() -> Fernet:
|
||||
SECRET_PREFIX_V2 = "v2:"
|
||||
|
||||
|
||||
def _legacy_fernet() -> Fernet:
|
||||
import base64
|
||||
import hashlib
|
||||
|
||||
@@ -69,8 +79,30 @@ def _fernet() -> Fernet:
|
||||
|
||||
|
||||
def encrypt_secret(value: str) -> str:
|
||||
return _fernet().encrypt(value.encode()).decode()
|
||||
"""Шифрует секрет DEK'ом из vault. Если vault ещё не инициализирован
|
||||
(свежий апгрейд с 0.6.x) — fallback на legacy Fernet, чтобы существующие
|
||||
сценарии не ломались. После /api/v1/vault/init все секреты пишутся как v2.
|
||||
"""
|
||||
from ..services.vault import vault_service # локальный импорт против цикла
|
||||
initialized = vault_service.is_initialized_cached()
|
||||
if initialized is False:
|
||||
# legacy v1: пишем Fernet-токен без префикса
|
||||
return _legacy_fernet().encrypt(value.encode()).decode()
|
||||
# initialized is True (или None — тогда поверим, что инициализирован, и
|
||||
# если на самом деле нет — VaultNotInitialized всплывёт; админу всё равно
|
||||
# пора создавать мастер-пароль).
|
||||
return vault_service.encrypt_secret(value)
|
||||
|
||||
|
||||
def decrypt_secret(token: str) -> str:
|
||||
return _fernet().decrypt(token.encode()).decode()
|
||||
"""Дешифрует секрет. Поддерживает v2 (vault) и legacy v1 (SECRET_KEY)."""
|
||||
if token.startswith(SECRET_PREFIX_V2):
|
||||
from ..services.vault import vault_service
|
||||
return vault_service.decrypt_secret_v2(token)
|
||||
# Legacy: Fernet-токен без префикса.
|
||||
return _legacy_fernet().decrypt(token.encode()).decode()
|
||||
|
||||
|
||||
def is_legacy_secret(token: str) -> bool:
|
||||
"""True, если секрет ещё не мигрирован на v2 (Fernet от SECRET_KEY)."""
|
||||
return not token.startswith(SECRET_PREFIX_V2)
|
||||
|
||||
Reference in New Issue
Block a user