Encrypt connection credentials with an owner-bound AES-GCM envelope

Adds a v2 credential envelope next to the v1 tool-secret helpers:
AES-256-GCM, a master key derived once per process from
ENCRYPTION_SECRET_KEY, and a per-record key from HKDF over the owner's
id, which is also the associated data, so a blob moved onto another
user's row does not decrypt. The envelope names its key, so
ENCRYPTION_SECRET_KEY_PREVIOUS keeps old rows readable during a
rotation.

Log redaction now also covers token_info, tokens and client_info, and
the API warns at startup when the public default key is in use.
This commit is contained in:
arc53-machine committed 2026-09-28 17:03:02 +01:00
1 parent 45c4d928c4
commit 667d4bbab0
6 files changed
+275 -3

No files matched your search

+21
View File
@@ -184,6 +184,27 @@ if settings.AUTH_TYPE == "simple_jwt":
print(f"Generated Simple JWT Token: {SIMPLE_JWT_TOKEN}")
def _warn_default_encryption_key() -> None:
"""Say when stored credentials are sealed with the public default key."""
from docsgpt.security.encryption import is_default_encryption_key
if not is_default_encryption_key():
return
if settings.AUTH_TYPE:
logging.getLogger(__name__).warning(
"ENCRYPTION_SECRET_KEY is the public default: connecting services is refused until you set your "
"own value (then run `docsgpt connectors reencrypt`)."
)
else:
logging.getLogger(__name__).warning(
"ENCRYPTION_SECRET_KEY is the public default. Stored connector credentials are only as safe as "
"that key; set your own value before exposing this install."
)
_warn_default_encryption_key()
@app.route("/")
def home():
if request.remote_addr in ("0.0.0.0", "127.0.0.1", "localhost", "172.18.0.1"):