// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. policy module for scripts/src/platform-infra.ts. // Moved mechanically from scripts/src/platform-infra.ts:1853-1973 for #903. import { createHash, randomBytes } from "node:crypto"; import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, isAbsolute, join } from "node:path"; import type { UniDeskConfig } from "../config"; import { rootPath } from "../config"; import { startJob } from "../jobs"; import type { RenderedCliResult } from "../output"; import { pk01CaddyMergeManagedBlocksPython, renderCaddyManagedBlock, renderSimpleReverseProxyCaddySiteBlock } from "../pk01-caddy"; import { capture, compactCapture, parseJsonOutput, prepareFrpcSecret, shQuote } from "../platform-infra-public-service"; import { yamlBooleanField, yamlFieldLabel, yamlIntegerField } from "../platform-infra-ops-library"; import { fingerprintSecretValues, parseEnvFile, readEnvSourceFile, readTextFile, redactRepoPath, requiredEnvValue } from "../secrets"; import type { Sub2ApiConfig, Sub2ApiTargetConfig } from "./entry"; import type { DisclosureOptions, PolicyCheck } from "./options"; import { readSub2ApiConfig } from "./config"; import { serviceName } from "./entry"; import { isExternalTarget, resolveTarget } from "./manifest"; import { escapeRegExp, hasAllowAllNetworkPolicy, hasDeploymentReplicas } from "./secrets-and-egress"; import { boolField, validateExternalPendingScript } from "./utils"; import { validateExternalActiveScript, validateScript } from "./validate-script"; export async function validate(config: UniDeskConfig, options: DisclosureOptions): Promise> { const sub2api = readSub2ApiConfig(); const target = resolveTarget(sub2api, options.targetId); const script = target.databaseMode === "external-pending" ? validateExternalPendingScript(sub2api, target) : target.databaseMode === "external-active" ? validateExternalActiveScript(sub2api, target) : validateScript(sub2api, target); const result = await capture(config, target.route, ["sh"], script); const parsed = parseJsonOutput(result.stdout); if (options.raw) { return { ok: result.exitCode === 0 && boolField(parsed, "ok", false), action: "platform-infra-sub2api-validate", target: { id: target.id, route: target.route, namespace: target.namespace, }, remote: compactCapture(result, { full: true }), parsed, }; } return { ok: result.exitCode === 0 && boolField(parsed, "ok", false), action: "platform-infra-sub2api-validate", target: { id: target.id, route: target.route, namespace: target.namespace, }, summary: parsed, remote: compactCapture(result, { full: options.full || result.exitCode !== 0 }), }; } export function policyChecks(sub2api: Sub2ApiConfig, yaml: string, target: Sub2ApiTargetConfig): PolicyCheck[] { const cleanup = sub2api.defaults.cleanup; const redisService = sub2api.runtime.redis.serviceName; const checks: PolicyCheck[] = [ { name: "no-ingress", ok: !/^\s*kind:\s*Ingress\s*$/mu.test(yaml), detail: "Sub2API must not be exposed through Kubernetes Ingress.", }, { name: "no-nodeport-or-loadbalancer", ok: !/^\s*type:\s*(NodePort|LoadBalancer)\s*$/mu.test(yaml), detail: "Services must remain ClusterIP/internal-only.", }, { name: "no-host-network", ok: hostNetworkPolicyOk(yaml, target), detail: target.egressProxy?.hostNetwork === true ? "Only the YAML-declared egress proxy Deployment may join the host network." : "Pods must not join the host network unless explicitly allowed by YAML for the egress proxy.", }, { name: "no-host-port", ok: !/^\s*hostPort:\s*[0-9]+\s*$/mu.test(yaml), detail: "Pods must not expose host ports.", }, { name: "no-cpu-memory-resources", ok: !/^\s*(cpu|memory):\s*/mu.test(yaml), detail: "Issue #220 requires no Kubernetes CPU/memory requests or limits.", }, { name: "no-resource-quota-or-limit-range", ok: !/^\s*kind:\s*(ResourceQuota|LimitRange)\s*$/mu.test(yaml), detail: "The platform-infra namespace must not receive quota/default limit objects for this deployment.", }, { name: "expected-namespace", ok: new RegExp(`^\\s*name:\\s*${escapeRegExp(target.namespace)}\\s*$`, "mu").test(yaml), detail: `Manifest declares namespace ${target.namespace}.`, }, { name: "allow-all-network-policy", ok: hasAllowAllNetworkPolicy(yaml, target.namespace), detail: `Manifest must include NetworkPolicy/allow-all in ${target.namespace} to keep kube-router from blocking Sub2API cross-pod traffic.`, }, ]; if (isExternalTarget(target)) { checks.push( { name: "external-db-no-local-postgres", ok: !/^\s*kind:\s*StatefulSet\s*$/mu.test(yaml) && !new RegExp(`\\b${escapeRegExp(cleanup.externalDbState.postgresStatefulSetName)}\\b`, "u").test(yaml), detail: "External DB targets must not deploy a local PostgreSQL StatefulSet, Service, or PVC.", }); } if (target.databaseMode === "external-pending") { checks.push( { name: "pending-db-replicas-match-yaml", ok: hasDeploymentReplicas(yaml, serviceName, target.appReplicas) && hasDeploymentReplicas(yaml, redisService, target.redisReplicas), detail: `External-pending predeployment renders YAML-declared app/Redis replicas ${target.appReplicas}/${target.redisReplicas}.`, }, ); } else if (target.databaseMode === "external-active") { checks.push({ name: "external-active-replicas-match-yaml", ok: hasDeploymentReplicas(yaml, serviceName, target.appReplicas) && hasDeploymentReplicas(yaml, redisService, target.redisReplicas), detail: `External-active targets render YAML-declared app/Redis replicas ${target.appReplicas}/${target.redisReplicas} against the external PostgreSQL runtime.`, }); } else { checks.push({ name: "bundled-db-present", ok: /^\s*kind:\s*StatefulSet\s*$/mu.test(yaml) && new RegExp(`\\b${escapeRegExp(cleanup.externalDbState.postgresStatefulSetName)}\\b`, "u").test(yaml), detail: "Bundled active targets render the local PostgreSQL StatefulSet.", }); } if (target.redisMode === "local-ephemeral") { checks.push({ name: "local-redis-ephemeral", ok: !new RegExp(`\\b${escapeRegExp(cleanup.redisPersistentState.pvcName)}\\b`, "u").test(yaml) && /^\s*emptyDir:\s*\{\}\s*$/mu.test(yaml), detail: "Local Redis is an ephemeral cache and must not allocate persistent Redis storage.", }); } if (target.egressProxy?.enabled) { checks.push({ name: "egress-proxy-yaml-controlled", ok: hasDeploymentReplicas(yaml, target.egressProxy.deploymentName, 1) && new RegExp(`^\\s*name:\\s*${escapeRegExp(target.egressProxy.serviceName)}\\s*$`, "mu").test(yaml), detail: "Egress HTTP proxy client must be rendered as a YAML-controlled platform-infra Deployment and ClusterIP Service.", }); } return checks; } function hostNetworkPolicyOk(yaml: string, target: Sub2ApiTargetConfig): boolean { const matches = [...yaml.matchAll(/^\s*hostNetwork:\s*true\s*$/gmu)]; if (matches.length === 0) return true; const proxy = target.egressProxy; if (proxy === null || !proxy.enabled || !proxy.hostNetwork || matches.length !== 1) return false; const egressProxyDeployment = new RegExp( `kind:\\s*Deployment[\\s\\S]*?name:\\s*${escapeRegExp(proxy.deploymentName)}[\\s\\S]*?hostNetwork:\\s*true`, "u", ); return egressProxyDeployment.test(yaml); }