fix(exchange): preserve old archives during server key rotation
All checks were successful
State Corp Backend CI/CD / Quality gate (push) Successful in 3m19s
State Corp Backend CI/CD / Build linux/amd64 images once (push) Successful in 2m54s
State Corp Backend CI/CD / Refresh and release internal main (push) Has been skipped
State Corp Backend CI/CD / Release customer main (push) Has been skipped
State Corp Backend CI/CD / Release dev (push) Successful in 54s
All checks were successful
State Corp Backend CI/CD / Quality gate (push) Successful in 3m19s
State Corp Backend CI/CD / Build linux/amd64 images once (push) Successful in 2m54s
State Corp Backend CI/CD / Refresh and release internal main (push) Has been skipped
State Corp Backend CI/CD / Release customer main (push) Has been skipped
State Corp Backend CI/CD / Release dev (push) Successful in 54s
This commit is contained in:
@@ -412,6 +412,30 @@ class ExchangePackageImportService:
|
||||
|
||||
return header, bin_bytes[header_end:]
|
||||
|
||||
@classmethod
|
||||
def _decryption_token(cls, header: dict[str, Any]) -> str:
|
||||
token = str(getattr(settings, "EXCHANGE_SHARED_TOKEN", "") or "").strip()
|
||||
if not token:
|
||||
raise ExchangeImportError("EXCHANGE_SHARED_TOKEN не настроен")
|
||||
|
||||
previous_token = str(
|
||||
getattr(settings, "EXCHANGE_PREVIOUS_SHARED_TOKEN", "") or ""
|
||||
).strip()
|
||||
previous_id = str(
|
||||
getattr(settings, "EXCHANGE_PREVIOUS_KEY_ID", "") or ""
|
||||
).strip()
|
||||
current_id = str(getattr(settings, "EXCHANGE_KEY_ID", "") or "").strip()
|
||||
if previous_token or previous_id:
|
||||
if not previous_token or not previous_id or previous_id == current_id:
|
||||
raise ExchangeImportError(
|
||||
"Некорректно настроен предыдущий ключ расшифровки обмена"
|
||||
)
|
||||
if header.get("key_id") == previous_id:
|
||||
return previous_token
|
||||
# Preserve the existing current-key contract, including old containers
|
||||
# without key_id. Never try the previous secret as a decryption fallback.
|
||||
return token
|
||||
|
||||
@classmethod
|
||||
def _decrypt_payload(
|
||||
cls,
|
||||
@@ -419,10 +443,7 @@ class ExchangePackageImportService:
|
||||
header: dict[str, Any],
|
||||
encrypted_payload: bytes,
|
||||
) -> dict[str, Any]:
|
||||
token = str(getattr(settings, "EXCHANGE_SHARED_TOKEN", "") or "").strip()
|
||||
if not token:
|
||||
raise ExchangeImportError("EXCHANGE_SHARED_TOKEN не настроен")
|
||||
|
||||
token = cls._decryption_token(header)
|
||||
raw_key = hashlib.sha256(token.encode("utf-8")).digest()
|
||||
nonce = cls._decode_base64_field(header, "nonce")
|
||||
aad = cls._decode_base64_field(header, "aad")
|
||||
|
||||
@@ -19,6 +19,9 @@ BACKUP_ENCRYPTION_KEY = os.getenv("BACKUP_ENCRYPTION_KEY", "")
|
||||
BACKUP_KEY_ID = os.getenv("BACKUP_KEY_ID", "default")
|
||||
EXCHANGE_SHARED_TOKEN = os.getenv("EXCHANGE_SHARED_TOKEN", "")
|
||||
EXCHANGE_KEY_ID = os.getenv("EXCHANGE_KEY_ID", "dev-shared-token")
|
||||
# Read old encrypted archives after rotation; never accepted as HTTP credentials.
|
||||
EXCHANGE_PREVIOUS_SHARED_TOKEN = os.getenv("EXCHANGE_PREVIOUS_SHARED_TOKEN", "")
|
||||
EXCHANGE_PREVIOUS_KEY_ID = os.getenv("EXCHANGE_PREVIOUS_KEY_ID", "")
|
||||
|
||||
warnings.filterwarnings(
|
||||
"ignore",
|
||||
|
||||
Reference in New Issue
Block a user