ADMIN · DATABASE SECURITY

Database security

Four overlapping layers so audit logs, user data, and secrets cannot be modified offline — even by someone with direct DB access.

The four layers

LayerWhat it protectsMechanism
1 · FilesystemDisk theft, snapshot theftLUKS2 on /var/lib/postgresql (optional, install-time choice)
2 · Field encryptionDB dumps, row-level theftAES-256-GCM per-field; master key at /etc/meridian/secrets/master.key
3 · Row hash chainSilent offline modificationHMAC-SHA-256 chained row_hash on 8 sensitive tables
4 · Direct-SQL lockdownAccidental or malicious ad-hoc queriesPG bound to localhost only · SCRAM-SHA-256 · meridian-app role scoping

Layer 1 · LUKS filesystem encryption

Run separately via scripts/setup_luks.sh. See LUKS setup.

Layer 2 · Field encryption

Sensitive values are stored as AES-256-GCM ciphertext with per-row nonces. Columns currently protected:

The 32-byte master key lives at /etc/meridian/secrets/master.key (0400 meridian:meridian). The app derives per-domain subkeys (HMAC-based) so leakage of one context doesn't compromise the others.

Rotating the master key

sudo meridian-nip secrets rotate-master
# App re-encrypts all vault rows under a new key_version.
# Old key marked retired; usable for decrypt-only reads during migration.

Layer 3 · Row-hash tamper evidence

Eight tables carry a row_hash BYTEA column: audit_events · license · license_activations · license_verifications · cert_events · approvals · impersonations · update_history.

On every insert, the app computes

row_hash = HMAC-SHA-256(
  key     = load('/etc/meridian/secrets/row_hmac.key'),
  message = canonical_row_fields || previous_row_hash
)

The previous_row_hash term chains each row to the one before it. Any offline modification breaks the chain at the modified row and every subsequent row.

How tampering is detected

What it catches

Any of these break the chain at the modified row. The scan reports the exact ctid and table; combined with PG's WAL (if archive_mode is on, which it is by default), forensic replay is possible.

Layer 4 · Direct-SQL access restriction

A shell admin with sudo -u postgres psql access can technically run arbitrary SQL, but any modification to a tamper-evident table is detected within 24 hours — and logged with before/after hashes so you know exactly what changed.

Master key lifecycle

Initial generation

Done automatically by install.sh via openssl rand -out ... 32. Permissions are locked to 0400 meridian:meridian.

Backup

The key files are not included in routine backups by default. Include them only when creating a DR-grade bundle:

sudo /opt/meridian/scripts/backup.sh --include-keys --output /secure/offsite/

Without the keys, any backup tarball is useless against a future restore. With the keys, the bundle is full DR-grade and must be stored with the same care as the original host.

Loss recovery

There is no recovery path from master key loss. The database is permanently unreadable. Your only recourse is to restore from a --include-keys backup made before the loss. This is why the installer prints a bold warning after key generation.

What customers should NOT do

Incident response: integrity alert received

  1. Do not restart or redeploy. Preserve the host as-is.
  2. Pull an audit snapshot: sudo meridian-nip audit export --since "2h ago" --output /tmp/audit.json
  3. Run the integrity scanner with verbose mode: sudo meridian-nip integrity scan --verbose
  4. Cross-reference the mismatched row with PG WAL (/var/lib/meridian/backups/wal/) to identify the exact modification.
  5. Escalate per your internal IR playbook.
MERIDIAN 1.0.0 · DOCUMENTATION
meridiannip.com ↗