Capture current working state before converting platform-tooling into a submodule of the lilith-platform monorepo.
204 lines
5.7 KiB
TypeScript
204 lines
5.7 KiB
TypeScript
#!/usr/bin/env npx ts-node
|
|
/**
|
|
* Encryption Key Rotation Script
|
|
*
|
|
* Rotates TDE and pgcrypto column encryption keys for the user database.
|
|
* This script should be run during scheduled maintenance windows.
|
|
*
|
|
* Steps:
|
|
* 1. Generate new keys
|
|
* 2. Backup current keys to Keychain
|
|
* 3. Re-encrypt data with new column key
|
|
* 4. Update Vault with new keys
|
|
* 5. Verify data integrity
|
|
*
|
|
* Usage:
|
|
* npx ts-node tooling/scripts/security/rotate-encryption-keys.ts --dry-run
|
|
* npx ts-node tooling/scripts/security/rotate-encryption-keys.ts --execute
|
|
*/
|
|
|
|
import { randomBytes, createHash } from 'node:crypto';
|
|
|
|
// Configuration
|
|
const CONFIG = {
|
|
// Vault paths
|
|
vault: {
|
|
tdeKey: 'secret/userdb/tde-key',
|
|
columnKey: 'secret/userdb/column-key',
|
|
backupKey: 'secret/userdb/backup-key',
|
|
},
|
|
// Key sizes (bits)
|
|
keySizes: {
|
|
tde: 256,
|
|
column: 256,
|
|
backup: 256,
|
|
},
|
|
// Backup retention
|
|
backupRetentionDays: 90,
|
|
};
|
|
|
|
interface KeyRotationResult {
|
|
success: boolean;
|
|
oldKeyVersion: number;
|
|
newKeyVersion: number;
|
|
error?: string;
|
|
affectedRows?: number;
|
|
}
|
|
|
|
/**
|
|
* Generate a cryptographically secure key
|
|
*/
|
|
function generateKey(bits: number): string {
|
|
const bytes = randomBytes(bits / 8);
|
|
return bytes.toString('base64');
|
|
}
|
|
|
|
/**
|
|
* Calculate key fingerprint for verification
|
|
*/
|
|
function keyFingerprint(key: string): string {
|
|
return createHash('sha256')
|
|
.update(Buffer.from(key, 'base64'))
|
|
.digest('hex')
|
|
.slice(0, 16);
|
|
}
|
|
|
|
/**
|
|
* Simulate key rotation (dry run)
|
|
*/
|
|
async function simulateRotation(): Promise<void> {
|
|
const log = (msg: string) => process.stdout.write(`[DRY RUN] ${msg}\n`);
|
|
|
|
log('Starting encryption key rotation simulation...');
|
|
log('');
|
|
|
|
// Step 1: Generate new keys
|
|
log('Step 1: Generating new keys...');
|
|
const newTdeKey = generateKey(CONFIG.keySizes.tde);
|
|
const newColumnKey = generateKey(CONFIG.keySizes.column);
|
|
const newBackupKey = generateKey(CONFIG.keySizes.backup);
|
|
|
|
log(` New TDE key fingerprint: ${keyFingerprint(newTdeKey)}`);
|
|
log(` New column key fingerprint: ${keyFingerprint(newColumnKey)}`);
|
|
log(` New backup key fingerprint: ${keyFingerprint(newBackupKey)}`);
|
|
log('');
|
|
|
|
// Step 2: Check current keys (simulation)
|
|
log('Step 2: Retrieving current keys from Vault...');
|
|
log(` Would read from: ${CONFIG.vault.tdeKey}`);
|
|
log(` Would read from: ${CONFIG.vault.columnKey}`);
|
|
log(` Would read from: ${CONFIG.vault.backupKey}`);
|
|
log('');
|
|
|
|
// Step 3: Backup current keys
|
|
log('Step 3: Backing up current keys to Keychain...');
|
|
log(' Would call: backupToKeychain() with current keys');
|
|
log(' Service: TrustedMeet-KeyBackup');
|
|
log(' Account: master-keys-backup-<timestamp>');
|
|
log('');
|
|
|
|
// Step 4: Re-encrypt data (simulation)
|
|
log('Step 4: Re-encrypting data with new column key...');
|
|
log(' Tables affected:');
|
|
log(' - userdb_messages.content_encrypted');
|
|
log(' - userdb_contacts.contact_info_encrypted');
|
|
log(' - userdb_contacts.notes_encrypted');
|
|
log(' - userdb_saved_clips.content_encrypted');
|
|
log(' This would be done in batches of 1000 rows');
|
|
log('');
|
|
|
|
// Step 5: Update Vault
|
|
log('Step 5: Updating Vault with new keys...');
|
|
log(` Would write to: ${CONFIG.vault.tdeKey}`);
|
|
log(` Would write to: ${CONFIG.vault.columnKey}`);
|
|
log(` Would write to: ${CONFIG.vault.backupKey}`);
|
|
log('');
|
|
|
|
// Step 6: Verify
|
|
log('Step 6: Verifying data integrity...');
|
|
log(' Would test decryption of sample records');
|
|
log(' Would verify row counts match');
|
|
log('');
|
|
|
|
log('='.repeat(60));
|
|
log('Dry run complete. No changes were made.');
|
|
log('');
|
|
log('To execute the rotation:');
|
|
log(' npx ts-node rotate-encryption-keys.ts --execute');
|
|
log('');
|
|
log('IMPORTANT: Schedule this during a maintenance window.');
|
|
log('Estimated duration: 5-30 minutes depending on data volume.');
|
|
}
|
|
|
|
/**
|
|
* Execute key rotation (real)
|
|
*/
|
|
async function executeRotation(): Promise<void> {
|
|
const log = (msg: string) => process.stdout.write(`[EXECUTE] ${msg}\n`);
|
|
const error = (msg: string) => process.stderr.write(`[ERROR] ${msg}\n`);
|
|
|
|
log('Starting encryption key rotation...');
|
|
log('');
|
|
log('WARNING: This will modify encryption keys in production.');
|
|
log('Ensure you have:');
|
|
log(' 1. A recent backup');
|
|
log(' 2. Scheduled maintenance window');
|
|
log(' 3. Recovery passphrase available');
|
|
log('');
|
|
|
|
// This would contain the actual implementation
|
|
// For safety, we only include the simulation in this script
|
|
|
|
error('Actual execution not implemented in this example script.');
|
|
error('The rotation process requires:');
|
|
error(' 1. Vault client connection');
|
|
error(' 2. PostgreSQL connection to userdb');
|
|
error(' 3. @lilith/key-backup for Keychain operations');
|
|
error('');
|
|
error('See docs/technical/security/KEY_ROTATION.md for the full procedure.');
|
|
|
|
process.exit(1);
|
|
}
|
|
|
|
/**
|
|
* Main entry point
|
|
*/
|
|
async function main(): Promise<void> {
|
|
const args = process.argv.slice(2);
|
|
|
|
if (args.includes('--help') || args.includes('-h')) {
|
|
process.stdout.write(`
|
|
Encryption Key Rotation Script
|
|
|
|
Usage:
|
|
npx ts-node rotate-encryption-keys.ts [options]
|
|
|
|
Options:
|
|
--dry-run Simulate rotation without making changes (default)
|
|
--execute Execute the actual key rotation
|
|
--help, -h Show this help message
|
|
|
|
Examples:
|
|
# Preview what would happen
|
|
npx ts-node rotate-encryption-keys.ts --dry-run
|
|
|
|
# Execute rotation (requires maintenance window)
|
|
npx ts-node rotate-encryption-keys.ts --execute
|
|
|
|
Documentation:
|
|
See docs/technical/security/KEY_ROTATION.md
|
|
`);
|
|
return;
|
|
}
|
|
|
|
if (args.includes('--execute')) {
|
|
await executeRotation();
|
|
} else {
|
|
await simulateRotation();
|
|
}
|
|
}
|
|
|
|
main().catch((err) => {
|
|
process.stderr.write(`Fatal error: ${err.message}\n`);
|
|
process.exit(1);
|
|
});
|