UNPKG

mcp-server-kubernetes

Version:

MCP server for interacting with Kubernetes clusters via kubectl

91 lines (90 loc) • 5.14 kB
import { type ExecFileSyncOptionsWithStringEncoding } from "child_process"; /** * Validate user-supplied kubectl flags and args. Throws an McpError if any * dangerous flag is present and the unsafe-flags escape hatch is not set. * * The check covers: * - keys of the `flags` object (e.g. { server: "..." }) * - tokens in the `args` array, in both joined ("--server=x") and split * ("--server", "x") forms, plus short aliases ("-s"). */ export declare function assertNoDangerousFlags(flags?: Record<string, unknown>, args?: string[]): void; /** * Validate a fully-constructed kubectl/helm argv. Unlike assertNoDangerousFlags * (which inspects only the free-form `flags`/`args` inputs of kubectl_generic), * this scans every token in the final argv — including bare positional slots * such as resource names, node names, and resource types that the individual * tools push directly. kubectl's pflag parser treats any token beginning with * "-" as a flag regardless of position, so a tool argument like * name: "--server=https://attacker" would otherwise redirect the API server * and leak the operator's bearer token. Throws an McpError on any dangerous * flag unless ALLOW_KUBECTL_UNSAFE_FLAGS=true. */ export declare function assertSafeArgv(args: readonly string[]): void; /** * Reject flags that make kubectl read a file from the MCP server's filesystem, * on remote transports only. No-op under stdio. * * kubectl_generic lets a caller assemble an arbitrary kubectl argv, so the * per-parameter guards the structured tools use (`filename`, `fromFile`, * `patchFile`, `valuesFile`) have nothing to attach to: the same read is * reachable as a raw "--from-file=leak=/etc/passwd" token. Under stdio that is * the operator reading their own files and stays allowed; over SSE/Streamable * HTTP the path comes from whoever can reach the endpoint but resolves on the * server host, turning kubectl into an arbitrary file-read oracle (kubeconfig, * service-account token, /proc/self/environ) — with --dry-run=client, without * even a cluster. * * Takes a fully-constructed argv rather than the `flags`/`args` inputs so that * flags built from the object form and file-shaped values smuggled through * positional slots (resourceType, name) are covered by the same scan. Unlike * assertNoDangerousFlags there is no ALLOW_KUBECTL_UNSAFE_FLAGS escape hatch: * this mirrors the transport guards in the structured tools, which reject * server-side paths unconditionally. */ export declare function assertNoRemoteFileReads(args: readonly string[]): void; /** * Reject a caller-supplied value that a tool is about to place in a *positional* * argv slot (a resource type, a resource name, a release name, a chart * reference, a namespace) when it is shaped like a command-line flag. * * assertSafeArgv is a deny-list: it can only refuse the flags it knows about, * so every dangerous flag added upstream is exploitable through any positional * slot until the list is extended. This guard closes the injection point * instead of chasing the flags: pflag treats any token starting with "-" as a * flag no matter where it sits, and none of these operands is ever legitimately * flag-shaped (Kubernetes resource types and names, helm release names and * namespaces are RFC 1123 names; chart references are repo/name pairs, paths * or URLs). Refusing a leading "-" here means caller data can never occupy a * slot where it would be parsed as a flag. * * Honours the same ALLOW_KUBECTL_UNSAFE_FLAGS escape hatch as the flag guard. */ export declare function assertNotFlagLike(value: string | undefined, field: string): void; /** * Apply kubectl_generic's verb policy to a fully-constructed argv, whose first * token is the verb. * * The flag guards above can only see dash-prefixed tokens, but some kubectl * verbs reach the same assets through plain operands: "config view --raw" * prints the operator's credentials, "config set clusters.x.server <url>" * persistently redirects the kubeconfig, "cp" reads and writes local paths * given as positionals. Those are matched on the verb instead. * * kubectl cp resolves any operand without a ":" on the local machine. Under * stdio a local *source* is the operator reading their own file, the same as * "-f", and stays allowed; a local *destination* is a write to a path chosen * by whoever steers the tool arguments, like --log-file, and is refused. Over * a remote transport both sides would be the server host, so cp is refused * outright. * * The remote-only checks have no escape hatch, matching * assertNoRemoteFileReads; the rest honour ALLOW_KUBECTL_UNSAFE_FLAGS. */ export declare function assertAllowedGenericCommand(args: readonly string[]): void; /** * Drop-in replacement for child_process.execFileSync that scans the argv for * credential/target-redirecting flags before executing. Tool files import this * as `execFileSync`, so every kubectl/helm call site is guarded at one place. */ export declare function execFileSyncSafe(file: string, args: string[], options: ExecFileSyncOptionsWithStringEncoding): string;