fix: better file cleanup

This commit is contained in:
Daniel Hougaard
2025-04-11 21:49:57 +04:00
parent 894633143d
commit 5ce738bba0
3 changed files with 63 additions and 28 deletions
@@ -0,0 +1,35 @@
import crypto from "crypto";
import fs from "fs/promises";
import os from "os";
import path from "path";
import { logger } from "@app/lib/logger";
const baseDir = path.join(os.tmpdir(), "temporary-signing");
const randomPath = () => `${crypto.randomBytes(32).toString("hex")}-`;
export const createTemporaryDirectory = async (name: string) => {
const tempDirPath = path.join(baseDir, `${name}-${randomPath()}-${randomPath()}`);
await fs.mkdir(tempDirPath, { recursive: true });
return tempDirPath;
};
export const removeTemporaryBaseDirectory = async () => {
await fs.rm(baseDir, { force: true, recursive: true }).catch((err) => {
logger.error(err, `Failed to remove temporary base directory [path=${baseDir}]`);
});
};
export const cleanTemporaryDirectory = async (dirPath: string) => {
await fs.rm(dirPath, { recursive: true, force: true }).catch((err) => {
logger.error(err, `Failed to cleanup temporary directory [path=${dirPath}]`);
});
};
export const writeToTemporaryFile = async (tempDirPath: string, data: string | Buffer) => {
await fs.writeFile(tempDirPath, data, { mode: 0o600 }).catch((err) => {
logger.error(err, `Failed to write to temporary file [path=${tempDirPath}]`);
throw err;
});
};
+23 -28
View File
@@ -1,13 +1,13 @@
import { execFile } from "child_process"; import { execFile } from "child_process";
import crypto from "crypto"; import crypto from "crypto";
import fs from "fs/promises"; import fs from "fs/promises";
import os from "os";
import path from "path"; import path from "path";
import { promisify } from "util"; import { promisify } from "util";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { cleanTemporaryDirectory, createTemporaryDirectory, writeToTemporaryFile } from "./signing-fns";
import { AsymmetricKeyAlgorithm, SigningAlgorithm, TAsymmetricSignVerifyFns } from "./types"; import { AsymmetricKeyAlgorithm, SigningAlgorithm, TAsymmetricSignVerifyFns } from "./types";
const execFileAsync = promisify(execFile); const execFileAsync = promisify(execFile);
@@ -30,11 +30,6 @@ const SHA256_DIGEST_LENGTH = 32;
const SHA384_DIGEST_LENGTH = 48; const SHA384_DIGEST_LENGTH = 48;
const SHA512_DIGEST_LENGTH = 64; const SHA512_DIGEST_LENGTH = 64;
const makeTempDir = async (name: string) => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), `${name}-${crypto.randomBytes(16).toString("hex")}-`));
return tempDir;
};
/** /**
* Service for cryptographic signing and verification operations using asymmetric keys * Service for cryptographic signing and verification operations using asymmetric keys
* *
@@ -124,14 +119,14 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
}; };
const $signRsaDigest = async (digest: Buffer, privateKey: Buffer, hashAlgorithm: SupportedHashAlgorithm) => { const $signRsaDigest = async (digest: Buffer, privateKey: Buffer, hashAlgorithm: SupportedHashAlgorithm) => {
const tempDir = await makeTempDir("kms-rsa-sign"); const tempDir = await createTemporaryDirectory("kms-rsa-sign");
const digestPath = path.join(tempDir, "digest.bin"); const digestPath = path.join(tempDir, "digest.bin");
const keyPath = path.join(tempDir, "private_key.pem");
const sigPath = path.join(tempDir, "signature.bin"); const sigPath = path.join(tempDir, "signature.bin");
const keyPath = path.join(tempDir, "key.pem");
try { try {
await fs.writeFile(digestPath, digest, { mode: 0o600 }); await writeToTemporaryFile(digestPath, digest);
await fs.writeFile(keyPath, privateKey, { mode: 0o600 }); await writeToTemporaryFile(keyPath, privateKey);
const { stderr } = await execFileAsync( const { stderr } = await execFileAsync(
"openssl", "openssl",
@@ -170,19 +165,19 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
return signature; return signature;
} finally { } finally {
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); await cleanTemporaryDirectory(tempDir);
} }
}; };
const $signEccDigest = async (digest: Buffer, privateKey: Buffer, hashAlgorithm: SupportedHashAlgorithm) => { const $signEccDigest = async (digest: Buffer, privateKey: Buffer, hashAlgorithm: SupportedHashAlgorithm) => {
const tempDir = await makeTempDir("ecc-sign"); const tempDir = await createTemporaryDirectory("ecc-sign");
const digestPath = path.join(tempDir, "digest.bin"); const digestPath = path.join(tempDir, "digest.bin");
const keyPath = path.join(tempDir, "private_key.pem"); const keyPath = path.join(tempDir, "key.pem");
const sigPath = path.join(tempDir, "signature.bin"); const sigPath = path.join(tempDir, "signature.bin");
try { try {
await fs.writeFile(digestPath, digest); await writeToTemporaryFile(digestPath, digest);
await fs.writeFile(keyPath, privateKey); await writeToTemporaryFile(keyPath, privateKey);
const { stderr } = await execFileAsync( const { stderr } = await execFileAsync(
"openssl", "openssl",
@@ -222,7 +217,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
return signature; return signature;
} finally { } finally {
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); await cleanTemporaryDirectory(tempDir);
} }
}; };
@@ -232,15 +227,15 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
publicKey: Buffer, publicKey: Buffer,
hashAlgorithm: SupportedHashAlgorithm hashAlgorithm: SupportedHashAlgorithm
) => { ) => {
const tempDir = await makeTempDir("ecc-signature-verification"); const tempDir = await createTemporaryDirectory("ecc-signature-verification");
const pubKeyFile = path.join(tempDir, "public-key.pem"); const publicKeyFile = path.join(tempDir, "public-key.pem");
const sigFile = path.join(tempDir, "signature.sig"); const sigFile = path.join(tempDir, "signature.sig");
const digestFile = path.join(tempDir, "digest.bin"); const digestFile = path.join(tempDir, "digest.bin");
try { try {
await fs.writeFile(pubKeyFile, publicKey, { mode: 0o600 }); await writeToTemporaryFile(publicKeyFile, publicKey);
await fs.writeFile(sigFile, signature, { mode: 0o600 }); await writeToTemporaryFile(sigFile, signature);
await fs.writeFile(digestFile, digest, { mode: 0o600 }); await writeToTemporaryFile(digestFile, digest);
await execFileAsync( await execFileAsync(
"openssl", "openssl",
@@ -250,7 +245,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
"-in", "-in",
digestFile, digestFile,
"-inkey", "-inkey",
pubKeyFile, publicKeyFile,
"-pubin", // Important for EC public keys "-pubin", // Important for EC public keys
"-sigfile", "-sigfile",
sigFile, sigFile,
@@ -272,7 +267,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
} }
return false; return false;
} finally { } finally {
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); await cleanTemporaryDirectory(tempDir);
} }
}; };
@@ -282,15 +277,15 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
publicKey: Buffer, publicKey: Buffer,
hashAlgorithm: SupportedHashAlgorithm hashAlgorithm: SupportedHashAlgorithm
) => { ) => {
const tempDir = await makeTempDir("kms-signature-verification"); const tempDir = await createTemporaryDirectory("kms-signature-verification");
const publicKeyFile = path.join(tempDir, "public-key.pub"); const publicKeyFile = path.join(tempDir, "public-key.pub");
const signatureFile = path.join(tempDir, "signature.sig"); const signatureFile = path.join(tempDir, "signature.sig");
const digestFile = path.join(tempDir, "digest.bin"); const digestFile = path.join(tempDir, "digest.bin");
try { try {
await fs.writeFile(publicKeyFile, publicKey, { mode: 0o600 }); await writeToTemporaryFile(publicKeyFile, publicKey);
await fs.writeFile(signatureFile, signature, { mode: 0o600 }); await writeToTemporaryFile(signatureFile, signature);
await fs.writeFile(digestFile, digest, { mode: 0o600 }); await writeToTemporaryFile(digestFile, digest);
await execFileAsync( await execFileAsync(
"openssl", "openssl",
@@ -320,7 +315,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
} }
return false; return false;
} finally { } finally {
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {}); await cleanTemporaryDirectory(tempDir);
} }
}; };
+5
View File
@@ -9,6 +9,7 @@ import { runMigrations } from "./auto-start-migrations";
import { initAuditLogDbConnection, initDbConnection } from "./db"; import { initAuditLogDbConnection, initDbConnection } from "./db";
import { keyStoreFactory } from "./keystore/keystore"; import { keyStoreFactory } from "./keystore/keystore";
import { formatSmtpConfig, initEnvConfig } from "./lib/config/env"; import { formatSmtpConfig, initEnvConfig } from "./lib/config/env";
import { removeTemporaryBaseDirectory } from "./lib/crypto/sign/signing-fns";
import { initLogger } from "./lib/logger"; import { initLogger } from "./lib/logger";
import { queueServiceFactory } from "./queue"; import { queueServiceFactory } from "./queue";
import { main } from "./server/app"; import { main } from "./server/app";
@@ -21,6 +22,8 @@ const run = async () => {
const logger = initLogger(); const logger = initLogger();
const envConfig = initEnvConfig(logger); const envConfig = initEnvConfig(logger);
await removeTemporaryBaseDirectory();
const db = initDbConnection({ const db = initDbConnection({
dbConnectionUri: envConfig.DB_CONNECTION_URI, dbConnectionUri: envConfig.DB_CONNECTION_URI,
dbRootCert: envConfig.DB_ROOT_CERT, dbRootCert: envConfig.DB_ROOT_CERT,
@@ -71,6 +74,7 @@ const run = async () => {
process.on("SIGINT", async () => { process.on("SIGINT", async () => {
await server.close(); await server.close();
await db.destroy(); await db.destroy();
await removeTemporaryBaseDirectory();
hsmModule.finalize(); hsmModule.finalize();
process.exit(0); process.exit(0);
}); });
@@ -79,6 +83,7 @@ const run = async () => {
process.on("SIGTERM", async () => { process.on("SIGTERM", async () => {
await server.close(); await server.close();
await db.destroy(); await db.destroy();
await removeTemporaryBaseDirectory();
hsmModule.finalize(); hsmModule.finalize();
process.exit(0); process.exit(0);
}); });