Files
overlord/Overlord-Server/src/certGenerator.ts
T

162 lines
3.4 KiB
TypeScript
Raw Normal View History

2026-03-05 00:04:26 -06:00
import { spawn } from "child_process";
import { existsSync, mkdirSync, unlinkSync } from "fs";
2026-03-05 00:04:26 -06:00
import { dirname } from "path";
interface CertOptions {
certPath: string;
keyPath: string;
commonName?: string;
daysValid?: number;
additionalIPs?: string[];
}
export function certificatesExist(certPath: string, keyPath: string): boolean {
return existsSync(certPath) && existsSync(keyPath);
}
export async function generateSelfSignedCert(
options: CertOptions,
): Promise<void> {
const {
certPath,
keyPath,
commonName = "localhost",
daysValid = 3650,
additionalIPs = [],
} = options;
const certDir = dirname(certPath);
if (!existsSync(certDir)) {
mkdirSync(certDir, { recursive: true });
}
console.log("[TLS] Generating self-signed certificate...");
console.log(`[TLS] Common Name: ${commonName}`);
console.log(`[TLS] Valid for: ${daysValid} days`);
const sanConfig = `
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = req_ext
x509_extensions = v3_ca
[dn]
C=US
ST=State
L=City
O=Overlord
OU=IT
CN=${commonName}
[req_ext]
subjectAltName = @alt_names
[v3_ca]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
[alt_names]
DNS.1 = ${commonName}
DNS.2 = localhost
DNS.3 = *.local
IP.1 = 127.0.0.1
IP.2 = ::1
${additionalIPs.map((ip, i) => `IP.${i + 3} = ${ip}`).join("\n")}
`;
const configPath = `${certDir}/openssl.cnf`;
try {
await Bun.write(configPath, sanConfig);
await execCommand("openssl", [
"req",
"-x509",
"-newkey",
"rsa:2048",
"-nodes",
"-keyout",
keyPath,
"-out",
certPath,
"-days",
daysValid.toString(),
"-config",
configPath,
]);
try {
unlinkSync(configPath);
2026-03-05 00:04:26 -06:00
} catch {}
console.log(`[TLS] ✓ Certificate generated successfully`);
console.log(`[TLS] Certificate: ${certPath}`);
console.log(`[TLS] Private Key: ${keyPath}`);
} catch (error) {
console.error("[TLS] Failed to generate certificate:", error);
throw new Error(`Certificate generation failed: ${error}`);
}
}
function execCommand(command: string, args: string[]): Promise<string> {
return new Promise((resolve, reject) => {
const proc = spawn(command, args);
let stdout = "";
let stderr = "";
proc.stdout?.on("data", (data) => {
stdout += data.toString();
});
proc.stderr?.on("data", (data) => {
stderr += data.toString();
});
proc.on("close", (code) => {
if (code === 0) {
resolve(stdout);
} else {
reject(new Error(`Command failed with code ${code}: ${stderr}`));
}
});
proc.on("error", (err) => {
reject(new Error(`Failed to execute ${command}: ${err.message}`));
});
});
}
export async function isOpenSSLAvailable(): Promise<boolean> {
try {
await execCommand("openssl", ["version"]);
return true;
} catch {
return false;
}
}
export function getLocalIPs(): string[] {
try {
const os = require("os");
const interfaces = os.networkInterfaces();
const ips: string[] = [];
for (const name of Object.keys(interfaces)) {
for (const iface of interfaces[name]) {
if (iface.family === "IPv4" && !iface.internal) {
ips.push(iface.address);
}
}
}
return ips;
} catch {
return [];
}
}