ffd384f64b
Replace all sensor.* logger namespaces with __name__ (generic module identifiers instead of discoverable 'sensor.*' prefixes). Change hardcoded 'bb' API user to 'admin' in config and code defaults. Change hardcoded relay_user 'bb' to 'operator' — prevents network profiling from exposing tool identity via SSH config. Fixes #457, #458, #459
121 lines
4.0 KiB
Python
121 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Helper to get encryption key from Infisical and encrypt credentials before emission."""
|
|
|
|
import subprocess
|
|
import json
|
|
import logging
|
|
from typing import Optional
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Cache the key in memory during module lifetime
|
|
_cached_key: Optional[bytes] = None
|
|
|
|
def get_credential_encryption_key() -> Optional[bytes]:
|
|
"""Retrieve credential encryption key from Infisical via creds CLI.
|
|
|
|
Returns None if key cannot be retrieved (in which case plaintext fallback).
|
|
Caches key in memory for the lifetime of the module process.
|
|
"""
|
|
global _cached_key
|
|
if _cached_key is not None:
|
|
return _cached_key
|
|
|
|
try:
|
|
# Try to get CREDENTIAL_ENCRYPTION_KEY from Infisical via creds CLI
|
|
result = subprocess.run(
|
|
["/home/n0mad1k/.local/bin/creds", "get", "CREDENTIAL_ENCRYPTION_KEY"],
|
|
capture_output=True,
|
|
timeout=5,
|
|
text=True,
|
|
)
|
|
|
|
if result.returncode != 0:
|
|
logger.warning("Failed to retrieve CREDENTIAL_ENCRYPTION_KEY from Infisical: %s", result.stderr)
|
|
return None
|
|
|
|
key_hex = result.stdout.strip()
|
|
if not key_hex:
|
|
logger.warning("CREDENTIAL_ENCRYPTION_KEY is empty in Infisical")
|
|
return None
|
|
|
|
# Convert hex string to bytes
|
|
_cached_key = bytes.fromhex(key_hex)
|
|
if len(_cached_key) != 32:
|
|
logger.error("CREDENTIAL_ENCRYPTION_KEY must be 32 bytes (256-bit), got %d", len(_cached_key))
|
|
_cached_key = None
|
|
return None
|
|
|
|
logger.debug("Successfully loaded CREDENTIAL_ENCRYPTION_KEY from Infisical")
|
|
return _cached_key
|
|
|
|
except FileNotFoundError:
|
|
logger.error("creds CLI not found at ~/.local/bin/creds")
|
|
return None
|
|
except subprocess.TimeoutExpired:
|
|
logger.error("Timeout retrieving CREDENTIAL_ENCRYPTION_KEY from Infisical")
|
|
return None
|
|
except Exception as e:
|
|
logger.error("Error retrieving CREDENTIAL_ENCRYPTION_KEY: %s", e)
|
|
return None
|
|
|
|
|
|
def emit_credential_found(bus, source_module: str, payload: dict) -> None:
|
|
"""Emit CREDENTIAL_FOUND event with encrypted sensitive fields.
|
|
|
|
Sensitive fields are encrypted before emission. If encryption fails,
|
|
plaintext fallback is used with a warning logged.
|
|
|
|
Args:
|
|
bus: EventBus instance
|
|
source_module: Module name emitting the event
|
|
payload: Credential payload dict with fields like username, password, etc.
|
|
"""
|
|
from utils.crypto import encrypt_credential_dict
|
|
|
|
key = get_credential_encryption_key()
|
|
|
|
if key:
|
|
try:
|
|
encrypted_payload = encrypt_credential_dict(payload, key)
|
|
bus.emit("CREDENTIAL_FOUND", encrypted_payload, source_module=source_module)
|
|
return
|
|
except Exception as e:
|
|
logger.warning("Failed to encrypt credential payload: %s — using plaintext fallback", e)
|
|
|
|
# Fallback: emit plaintext with warning
|
|
bus.emit("CREDENTIAL_FOUND", payload, source_module=source_module)
|
|
|
|
|
|
def decrypt_credential_payload(payload: dict) -> dict:
|
|
"""Decrypt sensitive fields in credential event payload.
|
|
|
|
Looks for fields marked with __encrypted__ prefix and decrypts them.
|
|
Returns a new dict with decrypted fields.
|
|
|
|
Args:
|
|
payload: Credential dict with possibly encrypted fields
|
|
|
|
Returns:
|
|
New dict with decrypted values
|
|
"""
|
|
from utils.crypto import decrypt_credential_field
|
|
|
|
key = get_credential_encryption_key()
|
|
if not key:
|
|
return payload
|
|
|
|
decrypted = {}
|
|
|
|
for field, value in payload.items():
|
|
if isinstance(value, str) and value.startswith("__encrypted__"):
|
|
try:
|
|
decrypted[field] = decrypt_credential_field(value, key)
|
|
except Exception as e:
|
|
logger.warning("Failed to decrypt field %s: %s", field, e)
|
|
decrypted[field] = value
|
|
else:
|
|
decrypted[field] = value
|
|
|
|
return decrypted
|