62 lines
2.2 KiB
JavaScript
62 lines
2.2 KiB
JavaScript
|
|
/**
|
||
|
|
* 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.`);
|
||
|
|
}
|