feat: secure frontend and provider ingress
This commit is contained in:
+51
-14
@@ -48,14 +48,17 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
|
||||
const stateDir = rootPath(config.paths.stateDir);
|
||||
mkdirSync(stateDir, { recursive: true });
|
||||
const envFile = join(stateDir, "docker-compose.env");
|
||||
let logDir: string;
|
||||
let logPrefix: string;
|
||||
if (!freshLogPrefix && existsSync(envFile)) {
|
||||
const raw = readFileSync(envFile, "utf8");
|
||||
const logDir = raw.match(/^UNIDESK_LOG_DIR=(.*)$/m)?.[1]?.replace(/^"|"$/g, "") ?? rootPath(config.paths.logsDir);
|
||||
const logPrefix = raw.match(/^UNIDESK_LOG_PREFIX=(.*)$/m)?.[1]?.replace(/^"|"$/g, "") ?? localDateParts(new Date()).stamp;
|
||||
return { envFile, logDir, logPrefix };
|
||||
logDir = raw.match(/^UNIDESK_LOG_DIR=(.*)$/m)?.[1]?.replace(/^"|"$/g, "") ?? rootPath(config.paths.logsDir);
|
||||
logPrefix = raw.match(/^UNIDESK_LOG_PREFIX=(.*)$/m)?.[1]?.replace(/^"|"$/g, "") ?? localDateParts(new Date()).stamp;
|
||||
} else {
|
||||
const parts = localDateParts(new Date());
|
||||
logDir = resolve(rootPath(config.paths.logsDir, parts.day));
|
||||
logPrefix = parts.stamp;
|
||||
}
|
||||
const parts = localDateParts(new Date());
|
||||
const logDir = resolve(rootPath(config.paths.logsDir, parts.day));
|
||||
mkdirSync(logDir, { recursive: true });
|
||||
chmodSync(logDir, 0o777);
|
||||
const labels = JSON.stringify(config.providerGateway.labels);
|
||||
@@ -64,6 +67,7 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
|
||||
UNIDESK_CORE_PORT: String(config.network.core.port),
|
||||
UNIDESK_FRONTEND_PORT: String(config.network.frontend.port),
|
||||
UNIDESK_DATABASE_PORT: String(config.network.database.port),
|
||||
UNIDESK_PROVIDER_INGRESS_PORT: String(config.network.providerIngress.port),
|
||||
UNIDESK_DATABASE_USER: config.database.user,
|
||||
UNIDESK_DATABASE_PASSWORD: config.database.password,
|
||||
UNIDESK_DATABASE_NAME: config.database.name,
|
||||
@@ -71,18 +75,22 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean):
|
||||
UNIDESK_PROVIDER_ID: config.providerGateway.id,
|
||||
UNIDESK_PROVIDER_NAME: config.providerGateway.name,
|
||||
UNIDESK_PROVIDER_LABELS_JSON: labels,
|
||||
UNIDESK_AUTH_USERNAME: config.auth.username,
|
||||
UNIDESK_AUTH_PASSWORD: config.auth.password,
|
||||
UNIDESK_SESSION_SECRET: config.auth.sessionSecret,
|
||||
UNIDESK_SESSION_TTL_SECONDS: String(config.auth.sessionTtlSeconds),
|
||||
UNIDESK_HEARTBEAT_INTERVAL_MS: String(config.providerGateway.heartbeatIntervalMs),
|
||||
UNIDESK_HEARTBEAT_TIMEOUT_MS: "90000",
|
||||
UNIDESK_RECONNECT_BASE_MS: String(config.providerGateway.reconnectBaseMs),
|
||||
UNIDESK_RECONNECT_MAX_MS: String(config.providerGateway.reconnectMaxMs),
|
||||
UNIDESK_LOG_DIR: logDir,
|
||||
UNIDESK_LOG_PREFIX: parts.stamp,
|
||||
UNIDESK_LOG_PREFIX: logPrefix,
|
||||
UNIDESK_HOST_SSH_HOST: config.sshForwarding.host,
|
||||
UNIDESK_HOST_SSH_PORT: String(config.sshForwarding.port),
|
||||
UNIDESK_HOST_SSH_USER: config.sshForwarding.user,
|
||||
};
|
||||
writeFileSync(envFile, Object.entries(lines).map(([key, value]) => `${key}=${envValue(value)}`).join("\n") + "\n", "utf8");
|
||||
return { envFile, logDir, logPrefix: parts.stamp };
|
||||
return { envFile, logDir, logPrefix };
|
||||
}
|
||||
|
||||
export function composeConfig(config: UniDeskConfig): { runtimeEnv: ComposeRuntimeEnv; command: string[]; result: ReturnType<typeof runCommand> } {
|
||||
@@ -117,9 +125,8 @@ export function stopStack(config: UniDeskConfig): unknown {
|
||||
|
||||
function fixedPorts(config: UniDeskConfig): Array<{ name: string; port: number; listening: boolean }> {
|
||||
return [
|
||||
{ name: "backend-core", port: config.network.core.port, listening: isPortListening(config.network.core.port) },
|
||||
{ name: "frontend", port: config.network.frontend.port, listening: isPortListening(config.network.frontend.port) },
|
||||
{ name: "database", port: config.network.database.port, listening: isPortListening(config.network.database.port) },
|
||||
{ name: "provider-ingress", port: config.network.providerIngress.port, listening: isPortListening(config.network.providerIngress.port) },
|
||||
];
|
||||
}
|
||||
|
||||
@@ -166,21 +173,51 @@ async function probe(url: string): Promise<unknown> {
|
||||
}
|
||||
}
|
||||
|
||||
function dockerExecJson(container: string, code: string): unknown {
|
||||
const result = runCommand(["docker", "exec", container, "bun", "-e", code], repoRoot);
|
||||
if (result.exitCode !== 0) {
|
||||
return { ok: false, exitCode: result.exitCode, stdout: result.stdout.slice(-1200), stderr: result.stderr.slice(-1200) };
|
||||
}
|
||||
try {
|
||||
return JSON.parse(result.stdout.trim()) as unknown;
|
||||
} catch {
|
||||
return { ok: true, stdout: result.stdout.slice(-1200), stderr: result.stderr.slice(-1200) };
|
||||
}
|
||||
}
|
||||
|
||||
function dockerExec(config: UniDeskConfig, container: string, command: string[]): unknown {
|
||||
const result = runCommand(["docker", "exec", container, ...command], repoRoot);
|
||||
return { ok: result.exitCode === 0, exitCode: result.exitCode, stdout: result.stdout.slice(-1200), stderr: result.stderr.slice(-1200) };
|
||||
}
|
||||
|
||||
export async function stackStatus(config: UniDeskConfig): Promise<unknown> {
|
||||
const runtimeEnv = writeComposeEnv(config, false);
|
||||
const coreHealth = dockerExecJson("unidesk-backend-core", "fetch('http://127.0.0.1:8080/health').then(r=>r.json()).then(j=>console.log(JSON.stringify({ok:true,status:200,body:j}))).catch(e=>{console.log(JSON.stringify({ok:false,error:String(e)}));process.exit(1)})");
|
||||
const overview = dockerExecJson("unidesk-backend-core", "fetch('http://127.0.0.1:8080/api/overview').then(r=>r.json()).then(j=>console.log(JSON.stringify({ok:true,status:200,body:j}))).catch(e=>{console.log(JSON.stringify({ok:false,error:String(e)}));process.exit(1)})");
|
||||
return {
|
||||
runtimeEnv,
|
||||
ports: fixedPorts(config),
|
||||
publicPorts: fixedPorts(config),
|
||||
blockedPublicPorts: [
|
||||
{ name: "backend-core-rest", port: config.network.core.port, listening: isPortListening(config.network.core.port), expected: "not-listening" },
|
||||
{ name: "database", port: config.network.database.port, listening: isPortListening(config.network.database.port), expected: "not-listening" },
|
||||
],
|
||||
internalPorts: [
|
||||
{ name: "backend-core", containerPort: config.network.core.containerPort, hostPort: null },
|
||||
{ name: "database", containerPort: config.network.database.containerPort, hostPort: null },
|
||||
],
|
||||
containers: dockerContainers(config),
|
||||
health: {
|
||||
core: await probe(`http://127.0.0.1:${config.network.core.port}/health`),
|
||||
core: coreHealth,
|
||||
frontend: await probe(`http://127.0.0.1:${config.network.frontend.port}/health`),
|
||||
overview: await probe(`http://127.0.0.1:${config.network.core.port}/api/overview`),
|
||||
providerIngress: await probe(`http://127.0.0.1:${config.network.providerIngress.port}/health`),
|
||||
database: dockerExec(config, "unidesk-database", ["pg_isready", "-U", config.database.user, "-d", config.database.name]),
|
||||
overview,
|
||||
},
|
||||
urls: {
|
||||
core: `http://${config.network.publicHost}:${config.network.core.port}`,
|
||||
frontend: `http://${config.network.publicHost}:${config.network.frontend.port}`,
|
||||
database: `postgres://${config.database.user}:***@${config.network.publicHost}:${config.network.database.port}/${config.database.name}`,
|
||||
providerIngress: `ws://${config.network.publicHost}:${config.network.providerIngress.port}/ws/provider`,
|
||||
internalCore: `http://backend-core:${config.network.core.containerPort}`,
|
||||
internalDatabase: `postgres://${config.database.user}:***@database:${config.network.database.containerPort}/${config.database.name}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user