Skip to content

keyprovider: decide direct TPM2 ESYS provider follow-up #1

Description

@justinjoy

Summary

Store RBAC permission data in a standalone encrypted file (separate from the DuckDB telemetry store) with a pluggable key-provider abstraction. Only two provider backends are supported:

  1. systemd-creds (default, no library linkage — invoked as a subprocess)
  2. tpm2-tss with the mbedtls crypto backend (for environments that need direct TPM 2.0 binding without systemd)

No OpenSSL. No other backends (no libsecret, no passphrase prompt, no PKCS#11, no cloud KMS) are in scope for this issue.

Motivation

  • RBAC data is small, slow-changing, and not joined with telemetry — no benefit from placing it inside DuckDB.
  • DuckDB's native at-rest encryption requires the key to appear as a literal inside an ATTACH SQL statement, which complicates key hygiene and has no dedicated C API.
  • An independent encrypted RBAC file allows key material to be unwrapped only at load time, kept in locked memory, and zeroed immediately after use.
  • wyrelog's GPL-3.0-or-later + Commercial dual license requires permissive or compatible dependencies; OpenSSL is excluded by project policy.

Scope

File format

  • Container: libsodium crypto_secretstream_xchacha20poly1305 (AEAD, chunked)
  • Atomic writes: temp file + fsync + rename
  • File mode: 0600, dedicated UID/GID
  • Header carries format version and provider identifier (systemd-creds | tpm2) so a file can be opened by the correct unwrap path without configuration lookup

KeyProvider interface (C)

typedef struct _WyrelogKeyProvider WyrelogKeyProvider;

gboolean wyrelog_key_provider_unwrap (WyrelogKeyProvider *provider,
                                      guint8             *dek_out,     /* 32 bytes */
                                      GError            **error);

gboolean wyrelog_key_provider_wrap   (WyrelogKeyProvider *provider,
                                      const guint8       *dek_in,      /* 32 bytes */
                                      GError            **error);
  • DEK buffers allocated via sodium_malloc + sodium_mlock
  • sodium_memzero on free, no logging of key material or SQL/command lines containing keys

Backend: systemd-creds

  • No library linkage; wyrelog spawns systemd-creds encrypt/decrypt with the DEK on stdin/stdout
  • On TPM-equipped hosts systemd transparently seals to TPM2; otherwise falls back to the host key
  • Minimum systemd version documented (249+)

Backend: tpm2-tss + mbedtls

  • Build option --with-crypto=mbed on tpm2-tss
  • Seal DEK under the owner hierarchy SRK; optional PCR policy (documented but off by default to avoid operational surprises across kernel/firmware updates)
  • Unseal path uses the ESYS API; sealed blob stored next to the RBAC file

Meson wiring

  • New dependencies: libsodium (unconditional), tpm2-tss ESYS (optional, feature-flagged -Dtpm2=enabled)
  • Build matrix adds one job per provider
  • THIRD_PARTY_NOTICES.md seeded with libsodium (ISC), mbedtls (Apache-2.0), and tpm2-tss (BSD-2-Clause) entries

Out of scope

  • Passphrase / libsecret / PKCS#11 / cloud KMS providers
  • OpenSSL-based TPM stacks (tpm2-openssl, tpm2-tss-engine)
  • Key rotation UX beyond a single wyrelog rbac rotate-key command that re-encrypts in place
  • Placing RBAC inside DuckDB

Acceptance criteria

  • WyrelogKeyProvider interface and two concrete backends land behind build flags
  • RBAC file reader/writer uses libsodium secretstream with atomic rename
  • Unit tests cover round-trip for each provider; tpm2 test gated on a CI job with a software TPM (swtpm)
  • DEK lifetime is bounded — verified by test that memory-dumps the RBAC manager after close() and asserts no 32-byte key remains
  • THIRD_PARTY_NOTICES.md present and CI verifies it is updated when dependencies change
  • Documentation: operator guide for choosing a provider and rotating keys

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions