189 lines
7.5 KiB
TypeScript
189 lines
7.5 KiB
TypeScript
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 | null>): 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:-<unset>}\" >&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",
|
|
];
|
|
}
|