export const d601NativeKubeconfig = "/etc/rancher/k3s/k3s.yaml"; export const d601RequiredNodeName = "d601"; export type D601K3sGuardStatus = "pass" | "refused" | "blocked"; export interface D601K3sTargetObservation { kubeconfig: string; expectedKubeconfig?: string; currentContext: string | null; apiServer: string | null; nodeNames: string[]; commandsOk: boolean; combinedText: string; } export interface D601K3sDefaultKubectlDiagnostic { checked: boolean; currentContext: string | null; apiServer: string | null; refusalSignals: string[]; status: "clean" | "stale-forbidden-default" | "unavailable"; summary: string; } export interface D601K3sGuardClassification { status: D601K3sGuardStatus; refusal: boolean; refusalSignals: string[]; kubeconfig: string; expectedKubeconfig: string; currentContext: string | null; apiServer: string | null; nodeNames: string[]; nodeCount: number; requiredNodeName: string; requiredNodePresent: boolean; commandsOk: boolean; defaultKubectlDiagnostic?: D601K3sDefaultKubectlDiagnostic; summary: string; } export interface D601K3sGuardShellOptions { passOutput?: "stdout" | "stderr" | "quiet"; } function uniqueSignals(signals: Array): string[] { return [...new Set(signals.filter((signal): signal is string => signal !== null))]; } export function d601ForbiddenKubeSignals(text: string): string[] { return uniqueSignals([ /docker-desktop/iu.test(text) ? "docker-desktop" : null, /desktop-control-plane/iu.test(text) ? "desktop-control-plane" : null, /127\.0\.0\.1:11700/u.test(text) ? "127.0.0.1:11700" : null, ]); } export function classifyD601DefaultKubectlDiagnostic(input: { currentContext: string | null; apiServer: string | null; combinedText: string; commandsOk: boolean; }): D601K3sDefaultKubectlDiagnostic { const refusalSignals = d601ForbiddenKubeSignals(input.combinedText); if (!input.commandsOk) { return { checked: true, currentContext: input.currentContext, apiServer: input.apiServer, refusalSignals, status: "unavailable", summary: "Default kubectl diagnostic could not read context/server; this does not block the explicit D601 target.", }; } if (refusalSignals.length > 0) { return { checked: true, currentContext: input.currentContext, apiServer: input.apiServer, refusalSignals, status: "stale-forbidden-default", summary: "Default kubectl resolves to a forbidden local control-plane signal; explicit D601 KUBECONFIG remains the deploy target.", }; } return { checked: true, currentContext: input.currentContext, apiServer: input.apiServer, refusalSignals, status: "clean", summary: "Default kubectl diagnostic did not show Docker Desktop control-plane signals.", }; } export function classifyD601K3sTarget( observation: D601K3sTargetObservation, defaultKubectlDiagnostic?: D601K3sDefaultKubectlDiagnostic, ): D601K3sGuardClassification { const expectedKubeconfig = observation.expectedKubeconfig ?? d601NativeKubeconfig; const refusalSignals = d601ForbiddenKubeSignals(observation.combinedText); const requiredNodePresent = observation.nodeNames.includes(d601RequiredNodeName); const wrongKubeconfig = observation.kubeconfig !== expectedKubeconfig; const refusal = refusalSignals.length > 0; const status: D601K3sGuardStatus = refusal ? "refused" : !observation.commandsOk || wrongKubeconfig || !requiredNodePresent ? "blocked" : "pass"; const defaultStale = defaultKubectlDiagnostic?.status === "stale-forbidden-default"; const summary = refusal ? "Refusing D601 k3s operation because the explicit target kubeconfig resolved to a forbidden Docker Desktop control-plane signal." : wrongKubeconfig ? `D601 k3s guard blocked: expected explicit KUBECONFIG=${expectedKubeconfig}.` : !observation.commandsOk ? "D601 k3s guard blocked: explicit target kubeconfig could not read context, server, and nodes." : !requiredNodePresent ? `D601 k3s guard blocked: explicit target kubeconfig did not report node ${d601RequiredNodeName}.` : defaultStale ? "D601 native k3s guard passed with explicit KUBECONFIG; stale default kubectl context was observed only as a diagnostic." : "D601 native k3s guard passed with explicit KUBECONFIG."; return { status, refusal, refusalSignals, kubeconfig: observation.kubeconfig, expectedKubeconfig, currentContext: observation.currentContext, apiServer: observation.apiServer, nodeNames: observation.nodeNames, nodeCount: observation.nodeNames.length, requiredNodeName: d601RequiredNodeName, requiredNodePresent, commandsOk: observation.commandsOk, ...(defaultKubectlDiagnostic === undefined ? {} : { defaultKubectlDiagnostic }), summary, }; } function shellQuote(value: string): string { return `'${value.replace(/'/gu, "'\\''")}'`; } export function d601K3sGuardShellLines(kubeconfig = d601NativeKubeconfig, options: D601K3sGuardShellOptions = {}): string[] { const passOutput = options.passOutput ?? "stdout"; const passLine = passOutput === "quiet" ? ":" : passOutput === "stderr" ? "printf 'd601_native_k3s_guard=pass kubeconfig=%s context=%s server=%s node=%s\\n' \"$required_kubeconfig\" \"$context\" \"$server\" \"$required_node\" >&2" : "printf 'd601_native_k3s_guard=pass kubeconfig=%s context=%s server=%s node=%s\\n' \"$required_kubeconfig\" \"$context\" \"$server\" \"$required_node\""; return [ `export KUBECONFIG=${shellQuote(kubeconfig)}`, "d601_k3s_guard() {", ` required_kubeconfig=${shellQuote(kubeconfig)}`, ` required_node=${shellQuote(d601RequiredNodeName)}`, " if [ \"${KUBECONFIG:-}\" != \"$required_kubeconfig\" ]; then", " printf 'd601_native_k3s_guard=blocked reason=wrong-kubeconfig expected=%s actual=%s\\n' \"$required_kubeconfig\" \"${KUBECONFIG:-}\" >&2", " return 1", " fi", " if ! command -v kubectl >/dev/null 2>&1; then", " echo 'd601_native_k3s_guard=blocked reason=kubectl-missing' >&2", " return 1", " fi", " if ! context=$(kubectl config current-context 2>&1); then", " printf 'd601_native_k3s_guard=blocked reason=context-read-failed detail=%s\\n' \"$context\" >&2", " return 1", " fi", " if ! server=$(kubectl config view --minify -o 'jsonpath={.clusters[0].cluster.server}' 2>&1); then", " printf 'd601_native_k3s_guard=blocked reason=server-read-failed detail=%s\\n' \"$server\" >&2", " return 1", " fi", " if ! nodes=$(kubectl get nodes -o 'jsonpath={range .items[*]}{.metadata.name}{\"\\n\"}{end}' 2>&1); then", " printf 'd601_native_k3s_guard=blocked reason=nodes-read-failed detail=%s\\n' \"$nodes\" >&2", " return 1", " fi", " combined=$(printf '%s\\n%s\\n%s\\n' \"$context\" \"$server\" \"$nodes\")", " if printf '%s\\n' \"$combined\" | grep -Eiq 'docker-desktop|desktop-control-plane|127\\.0\\.0\\.1:11700'; then", " printf 'd601_native_k3s_guard=refused reason=forbidden-control-plane context=%s server=%s nodes=%s\\n' \"$context\" \"$server\" \"$(printf '%s' \"$nodes\" | tr '\\n' ',')\" >&2", " return 1", " fi", " if ! printf '%s\\n' \"$nodes\" | grep -Fx \"$required_node\" >/dev/null; then", " printf 'd601_native_k3s_guard=blocked reason=missing-d601-node context=%s server=%s nodes=%s\\n' \"$context\" \"$server\" \"$(printf '%s' \"$nodes\" | tr '\\n' ',')\" >&2", " return 1", " fi", ` ${passLine}`, "}", "d601_k3s_guard", ]; }