import { execSync, spawnSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { CA_CERT, CERTS_DIR, HOST, SERVER_CERT, SERVER_KEY } from './config.mjs'; export function certHosts(host) { const hosts = new Set([host, '127.0.0.1', 'localhost']); for (const iface of Object.values(os.networkInterfaces())) { for (const addr of iface || []) { if (addr.family === 'IPv4' && !addr.internal) { hosts.add(addr.address); } } } return [...hosts]; } export function ensureCerts(host = HOST) { fs.mkdirSync(CERTS_DIR, { recursive: true }); const hosts = certHosts(host); const marker = path.join(CERTS_DIR, 'hosts.txt'); const hostList = hosts.join('\n'); if ( fs.existsSync(SERVER_CERT) && fs.existsSync(SERVER_KEY) && fs.existsSync(CA_CERT) && fs.existsSync(marker) && fs.readFileSync(marker, 'utf8') === hostList ) { return; } const cnf = `${CERTS_DIR}/openssl.cnf`; const altNames = hosts .map((h, i) => (/^\d+\./.test(h) ? `IP.${i + 1} = ${h}` : `DNS.${i + 1} = ${h}`)) .join('\n'); fs.writeFileSync( cnf, `[req] distinguished_name = req_distinguished_name x509_extensions = v3_req prompt = no [req_distinguished_name] CN = ${host} [v3_req] subjectAltName = @alt_names basicConstraints = CA:FALSE keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = serverAuth [alt_names] ${altNames} ` ); execSync( `openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \ -keyout "${CERTS_DIR}/ca-key.pem" -out "${CA_CERT}" \ -subj "/CN=JTL S3 Backup CA/O=JTL/C=DE"`, { stdio: 'pipe' } ); execSync( `openssl req -newkey rsa:2048 -nodes \ -keyout "${SERVER_KEY}" -out "${CERTS_DIR}/server.csr" \ -config "${cnf}"`, { stdio: 'pipe' } ); execSync( `openssl x509 -req -in "${CERTS_DIR}/server.csr" \ -CA "${CA_CERT}" -CAkey "${CERTS_DIR}/ca-key.pem" -CAcreateserial \ -out "${SERVER_CERT}" -days 3650 -extensions v3_req -extfile "${cnf}"`, { stdio: 'pipe' } ); fs.writeFileSync(marker, hostList); } export function installCaTrust(container = process.env.MSSQL_DOCKER_CONTAINER || 'mssql') { if (!fs.existsSync(CA_CERT)) { ensureCerts(); } const copied = spawnSync('docker', ['cp', CA_CERT, `${container}:/tmp/jtlsrv-s3-ca.pem`], { stdio: 'pipe', }); if (copied.status !== 0) { return false; } const installed = spawnSync( 'docker', [ 'exec', '-u', 'root', container, 'bash', '-lc', 'cp /tmp/jtlsrv-s3-ca.pem /usr/local/share/ca-certificates/jtlsrv-s3.crt && update-ca-certificates', ], { stdio: 'pipe' } ); return installed.status === 0; }