76 lines
2.2 KiB
TypeScript
76 lines
2.2 KiB
TypeScript
/*
|
|
* Veskslop daemon — self-signed TLS for the web UI.
|
|
*
|
|
* Browser media APIs need a secure context. The daemon generates one
|
|
* self-signed certificate (openssl) covering localhost and the host's LAN
|
|
* addresses, and serves the UI over HTTPS. WEBUI_TLS=0 disables this.
|
|
*/
|
|
|
|
import { execFileSync } from "node:child_process";
|
|
import { existsSync, mkdirSync } from "node:fs";
|
|
import { networkInterfaces } from "node:os";
|
|
import { join } from "node:path";
|
|
|
|
export interface TlsPaths {
|
|
key: string;
|
|
cert: string;
|
|
}
|
|
|
|
function localAddresses(): string[] {
|
|
const names = ["DNS:localhost", "IP:127.0.0.1"];
|
|
|
|
for (const list of Object.values(networkInterfaces())) {
|
|
for (const info of list ?? []) {
|
|
if (info.family === "IPv4" && !info.internal) names.push(`IP:${info.address}`);
|
|
}
|
|
}
|
|
|
|
return names;
|
|
}
|
|
|
|
export function ensureTls(storeDir: string): TlsPaths | undefined {
|
|
if (process.env.WEBUI_TLS === "0") return undefined;
|
|
|
|
const keyOverride = process.env.WEBUI_TLS_KEY;
|
|
const certOverride = process.env.WEBUI_TLS_CERT;
|
|
if (keyOverride && certOverride) {
|
|
return existsSync(keyOverride) && existsSync(certOverride)
|
|
? { key: keyOverride, cert: certOverride }
|
|
: undefined;
|
|
}
|
|
|
|
const dir = join(storeDir, "tls");
|
|
const key = join(dir, "key.pem");
|
|
const cert = join(dir, "cert.pem");
|
|
if (existsSync(key) && existsSync(cert)) return { key, cert };
|
|
|
|
try {
|
|
mkdirSync(dir, { recursive: true });
|
|
execFileSync(
|
|
"openssl",
|
|
[
|
|
"req",
|
|
"-x509",
|
|
"-newkey",
|
|
"rsa:2048",
|
|
"-nodes",
|
|
"-keyout",
|
|
key,
|
|
"-out",
|
|
cert,
|
|
"-days",
|
|
"825",
|
|
"-subj",
|
|
"/CN=veskslop",
|
|
"-addext",
|
|
`subjectAltName=${localAddresses().join(",")}`
|
|
],
|
|
{ stdio: "ignore" }
|
|
);
|
|
return { key, cert };
|
|
} catch (error) {
|
|
console.warn("[webui] self-signed certificate generation failed:", (error as Error).message);
|
|
return undefined;
|
|
}
|
|
}
|