Files
pikasTech-unidesk/scripts/src/platform-infra/policy.ts
T
2026-06-26 17:28:34 +00:00

171 lines
7.6 KiB
TypeScript

// 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<Record<string, unknown>> {
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);
}