fix: sync scoped ssh runtime deploy env
This commit is contained in:
@@ -12,6 +12,8 @@ assertCondition(source.includes("downloadRemoteFile(options, remoteArchive, loca
|
||||
assertCondition(source.includes("runRemoteScriptBackground(options, remoteScript"), "remote docker save must run as a background job");
|
||||
assertCondition(source.includes('runRemoteScriptBackground(options, deployScript, Math.max(options.timeoutMs, 420_000), "d601-k3s-deploy")'), "D601 k3s deploy must use background polling");
|
||||
assertCondition(source.includes('"ssh",\n options.providerId,\n "download"'), "download helper must route through UniDesk ssh download");
|
||||
assertCondition(source.includes('"--chunk-bytes",\n "96000"'), "artifact ssh download must use the largest bounded chunk size");
|
||||
assertCondition(source.includes("UNIDESK_SSH_CLIENT_TOKEN") && source.includes("UNIDESK_SSH_CLIENT_ROUTE_ALLOWLIST"), "dev frontend artifact deploy must sync scoped ssh runtime keys");
|
||||
|
||||
console.log(JSON.stringify({
|
||||
ok: true,
|
||||
@@ -19,6 +21,8 @@ console.log(JSON.stringify({
|
||||
assertions: [
|
||||
"no docker-save stdout stream over ssh",
|
||||
"compose artifact uses verified ssh download",
|
||||
"remote docker save and k3s deploy use background polling"
|
||||
"remote docker save and k3s deploy use background polling",
|
||||
"artifact downloads use the largest bounded ssh chunk size",
|
||||
"dev frontend artifact deploy syncs scoped ssh runtime keys"
|
||||
]
|
||||
}, null, 2));
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
type DeployJsonServiceContract,
|
||||
} from "./deploy-json-contract";
|
||||
import { d601K3sGuardShellLines } from "./d601-k3s-guard";
|
||||
import { composeRuntimeEnvValue } from "./runtime-env";
|
||||
|
||||
export type ArtifactRegistryAction = "plan" | "render" | "status" | "health" | "install" | "deploy-backend-core" | "deploy-service";
|
||||
type ArtifactDeployEnvironment = "prod" | "dev";
|
||||
@@ -1676,6 +1677,8 @@ function downloadRemoteFile(options: ArtifactRegistryOptions, remotePath: string
|
||||
"ssh",
|
||||
options.providerId,
|
||||
"download",
|
||||
"--chunk-bytes",
|
||||
"96000",
|
||||
remotePath,
|
||||
localPath,
|
||||
], repoRoot, { timeoutMs });
|
||||
@@ -2455,13 +2458,18 @@ function verifyLocalArtifactLabels(
|
||||
}
|
||||
|
||||
function d601DevFrontendAuthPatchScript(config: UniDeskConfig): string {
|
||||
const sshClientToken = composeRuntimeEnvValue("UNIDESK_SSH_CLIENT_TOKEN");
|
||||
if (sshClientToken === null) throw new Error("UNIDESK_SSH_CLIENT_TOKEN must be present in .state/docker-compose.env before deploying dev frontend");
|
||||
const sshClientRouteAllowlist = composeRuntimeEnvValue("UNIDESK_SSH_CLIENT_ROUTE_ALLOWLIST") ?? "G14,G14:*,D601,D601:*";
|
||||
const secretData = {
|
||||
AUTH_USERNAME: base64(config.auth.username),
|
||||
AUTH_PASSWORD: base64(config.auth.password),
|
||||
SESSION_SECRET: base64(config.auth.sessionSecret),
|
||||
UNIDESK_SSH_CLIENT_TOKEN: base64(sshClientToken),
|
||||
};
|
||||
const configData = {
|
||||
SESSION_TTL_SECONDS: String(config.auth.sessionTtlSeconds),
|
||||
UNIDESK_SSH_CLIENT_ROUTE_ALLOWLIST: sshClientRouteAllowlist,
|
||||
};
|
||||
return [
|
||||
d601K3sGuardScript(),
|
||||
|
||||
@@ -10,6 +10,7 @@ import { startJob } from "./jobs";
|
||||
import { coreInternalFetch } from "./microservices";
|
||||
import { codeQueueSourceImportPreflight, codeQueueSourceSubdir } from "./code-queue-source-guard";
|
||||
import { d601K3sGuardShellLines, d601NativeKubeconfig } from "./d601-k3s-guard";
|
||||
import { composeRuntimeEnvValue } from "./runtime-env";
|
||||
import {
|
||||
compareDeployJsonExecutorMirrors,
|
||||
deployJsonCommitImage,
|
||||
@@ -1513,13 +1514,18 @@ function devK3sPrepullImages(service: UniDeskMicroserviceConfig): string[] {
|
||||
}
|
||||
|
||||
function syncDevFrontendAuthScript(config: UniDeskConfig): string {
|
||||
const sshClientToken = composeRuntimeEnvValue("UNIDESK_SSH_CLIENT_TOKEN");
|
||||
if (sshClientToken === null) throw new Error("UNIDESK_SSH_CLIENT_TOKEN must be present in .state/docker-compose.env before deploying dev frontend");
|
||||
const sshClientRouteAllowlist = composeRuntimeEnvValue("UNIDESK_SSH_CLIENT_ROUTE_ALLOWLIST") ?? "G14,G14:*,D601,D601:*";
|
||||
const data = {
|
||||
AUTH_USERNAME: Buffer.from(config.auth.username, "utf8").toString("base64"),
|
||||
AUTH_PASSWORD: Buffer.from(config.auth.password, "utf8").toString("base64"),
|
||||
SESSION_SECRET: Buffer.from(config.auth.sessionSecret, "utf8").toString("base64"),
|
||||
UNIDESK_SSH_CLIENT_TOKEN: Buffer.from(sshClientToken, "utf8").toString("base64"),
|
||||
};
|
||||
const runtimeConfig = {
|
||||
SESSION_TTL_SECONDS: String(config.auth.sessionTtlSeconds),
|
||||
UNIDESK_SSH_CLIENT_ROUTE_ALLOWLIST: sshClientRouteAllowlist,
|
||||
};
|
||||
return [
|
||||
"set -euo pipefail",
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { rootPath } from "./config";
|
||||
|
||||
export function canonicalComposeEnvFile(): string {
|
||||
return rootPath(".state", "docker-compose.env");
|
||||
}
|
||||
|
||||
function unquoteEnvValue(value: string): string {
|
||||
const trimmed = value.trim();
|
||||
if (trimmed.length < 2) return trimmed;
|
||||
const first = trimmed[0];
|
||||
const last = trimmed[trimmed.length - 1];
|
||||
if ((first !== "\"" && first !== "'") || last !== first) return trimmed;
|
||||
return trimmed.slice(1, -1);
|
||||
}
|
||||
|
||||
export function readEnvFileValues(path: string): Map<string, string> {
|
||||
const values = new Map<string, string>();
|
||||
if (!existsSync(path)) return values;
|
||||
for (const line of readFileSync(path, "utf8").split(/\r?\n/u)) {
|
||||
if (line.trim().length === 0 || line.trimStart().startsWith("#")) continue;
|
||||
const index = line.indexOf("=");
|
||||
if (index <= 0) continue;
|
||||
const key = line.slice(0, index).trim();
|
||||
if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(key)) continue;
|
||||
values.set(key, unquoteEnvValue(line.slice(index + 1)));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
export function composeRuntimeEnvValue(key: string, env: NodeJS.ProcessEnv = process.env): string | null {
|
||||
const fromProcess = env[key]?.trim();
|
||||
if (fromProcess !== undefined && fromProcess.length > 0) return fromProcess;
|
||||
const fromFile = readEnvFileValues(canonicalComposeEnvFile()).get(key)?.trim();
|
||||
return fromFile === undefined || fromFile.length === 0 ? null : fromFile;
|
||||
}
|
||||
Reference in New Issue
Block a user