/** * Data migration: encrypts existing plaintext WireGuard private keys * and pre-shared keys in the database. * * This migration is safe to run multiple times (idempotent): * already-encrypted values (prefix "enc:") are skipped. * * Requires DB_ENCRYPTION_KEY to be set in the environment. * If not set, migration logs a warning and exits without modifying data. */ import { encrypt } from "../lib/crypto.js"; const migrate_name = "wireguard_encrypt_keys"; export async function up(knex) { const key = process.env.DB_ENCRYPTION_KEY; if (!key) { console.warn(`[${migrate_name}] DB_ENCRYPTION_KEY not set — skipping encryption migration. Keys remain as plaintext.`); return; } console.log(`[${migrate_name}] Encrypting existing WireGuard keys...`); // --- wg_interface: encrypt private_key --- const ifaces = await knex("wg_interface").select("id", "private_key"); let ifaceCount = 0; for (const iface of ifaces) { if (!iface.private_key || iface.private_key.startsWith("enc:")) continue; await knex("wg_interface").where("id", iface.id).update({ private_key: encrypt(iface.private_key), }); ifaceCount++; } console.log(`[${migrate_name}] wg_interface: ${ifaceCount} rows encrypted.`); // --- wg_client: encrypt private_key + pre_shared_key --- const clients = await knex("wg_client").select("id", "private_key", "pre_shared_key"); let clientCount = 0; for (const client of clients) { const updates = {}; if (client.private_key && !client.private_key.startsWith("enc:")) { updates.private_key = encrypt(client.private_key); } if (client.pre_shared_key && !client.pre_shared_key.startsWith("enc:")) { updates.pre_shared_key = encrypt(client.pre_shared_key); } if (Object.keys(updates).length > 0) { await knex("wg_client").where("id", client.id).update(updates); clientCount++; } } console.log(`[${migrate_name}] wg_client: ${clientCount} rows encrypted.`); console.log(`[${migrate_name}] Done.`); } export async function down(knex) { // Intentionally a no-op: decrypting back to plaintext would require the key, // and rolling back encryption is a security risk. console.warn(`[${migrate_name}] down() is a no-op. Keys remain encrypted.`); }