refactor: load ssh key from entry password and streamline session cache

This commit is contained in:
v 2026-10-23 10:53:58 +03:00
parent 8e283ad670
commit bceb1080e5
2 changed files with 22 additions and 58 deletions

View file

@ -1,9 +1,6 @@
import sys
import xml.etree.ElementTree as ET
import base64
import os
import gzip
import zlib
dump_file = sys.argv[1]
cache_file = sys.argv[2]
@ -15,9 +12,16 @@ vault_addr = sys.argv[6]
tree = ET.parse(dump_file)
root = tree.getroot()
# Исключаем чтение старых версий паролей и удаленных записей
for h in root.findall(".//History"):
h.clear()
for g in root.findall(".//Group"):
name = g.find("./Name")
if name is not None and name.text == "Recycle Bin":
g.clear()
entries = {}
# Чтение актуальных записей (первое вхождение отсекает старый мусор из History)
for entry in root.findall(".//Entry"):
title = ""
username = ""
@ -42,64 +46,23 @@ for entry in root.findall(".//Entry"):
"notes": notes
}
# Извлечение приватного SSH-ключа в RAM tmpfs
# Прямое извлечение SSH-ключа из текстового поля записи Atlas Core Key
ssh_loaded = False
# 1. Поиск во вложениях KeePass XML
for b in root.findall(".//Binary"):
raw_text = (b.text or "").strip()
if len(raw_text) < 50:
continue
try:
raw_bytes = base64.b64decode(raw_text)
key_bytes = None
if raw_bytes.startswith(b"\x1f\x8b"):
key_bytes = gzip.decompress(raw_bytes)
elif b"-----BEGIN" in raw_bytes:
key_bytes = raw_bytes
elif len(raw_bytes) > 4:
# Срез 4-байтового заголовка длины Qt (qCompress)
try:
key_bytes = zlib.decompress(raw_bytes[4:])
except Exception:
try:
key_bytes = zlib.decompress(raw_bytes)
except Exception:
key_bytes = raw_bytes
else:
key_bytes = raw_bytes
if key_bytes and (b"PRIVATE KEY" in key_bytes or b"-----BEGIN" in key_bytes):
with open(key_file, "wb") as kf:
kf.write(key_bytes.strip() + b"\n")
for title in ("Atlas Core Key", "AtlasCoreKey", "Atlas Core"):
entry_data = entries.get(title, {})
for field in ("password", "notes"):
val = entry_data.get(field, "")
if "-----BEGIN" in val or "PRIVATE KEY" in val:
with open(key_file, "w") as kf:
kf.write(val.strip() + "\n")
os.chmod(key_file, 0o600)
ssh_loaded = True
break
except Exception:
pass
if ssh_loaded:
break
# 2. Поиск в текстовых полях записей Atlas Core
if not ssh_loaded:
for title, data in entries.items():
if "atlas" in title.lower() and "key" in title.lower():
for field in ("password", "notes"):
val = data.get(field, "")
if "-----BEGIN" in val or "PRIVATE KEY" in val:
with open(key_file, "w") as kf:
kf.write(val.strip() + "\n")
os.chmod(key_file, 0o600)
ssh_loaded = True
break
if ssh_loaded:
break
# Гарантия наличия файла для блокировки повторных запросов пароля
if not ssh_loaded:
sys.stderr.write("Внимание: приватный SSH-ключ не найден среди вложений KDBX.\n")
with open(key_file, "wb") as kf:
pass
os.chmod(key_file, 0o600)
sys.stderr.write("Внимание: приватный SSH-ключ не найден в полях Password/Notes записи Atlas Core Key.\n")
with open(cache_file, "w") as f:
f.write(f"# TTL_SECONDS={ttl_sec}\n")

View file

@ -8,8 +8,8 @@ load_secrets_to_ram() {
ensure_prerequisites
# Валидация сессии строго по кэшу в RAM
if [[ -f "${RAM_CACHE}" ]]; then
# Валидация сессии по наличию непустых кэша секретов и SSH-ключа
if [[ -f "${RAM_CACHE}" ]] && [[ -f "${RAM_KEY}" ]] && [[ -s "${RAM_KEY}" ]]; then
local created age cached_ttl
created=$(stat -c %Y "${RAM_CACHE}" 2>/dev/null || echo 0)
age=$(( now - created ))
@ -40,6 +40,7 @@ load_secrets_to_ram() {
log_info "YubiKey обнаружен. Приготовьтесь к аппаратному подтверждению (1 касание)..."
fi
# Атомарный 1-Touch экспорт всей базы KDBX в RAM (ровно 1 касание YubiKey на всю сессию)
local tmp_dump="/dev/shm/.kdbx_dump_${now}_$$.xml"
rm -f "${tmp_dump}"
touch "${tmp_dump}"