Helm and WebAssembly
Helm — the package manager for Kubernetes — added first-class WebAssembly (Wasm) support in its v4.0.0 release (November 2025). This note explains why that happened, how it works, and what it means for the broader story of Wasm inside Kubernetes.
If you already know Helm but think of WebAssembly as “the thing that runs code in browsers,” this note bridges those two worlds.
Quick background: what Helm actually executes
Helm charts are not just static YAML. They contain Go templates — files in the templates/ directory that the Helm engine renders by combining them with user-supplied values before sending the resulting Kubernetes manifests to the API server.
The rendering engine gives templates access to:
- The Go
text/templatestandard library — conditionals, loops, pipelines. - ~50 functions from the Sprig library — string manipulation, math, date formatting, cryptographic helpers (
genCA,genSignedCert,encryptAES), and more. Sprig is a third-party Go library that provides generic template functions beyond what the Go standard library offers. - Helm-specific functions:
include— renders another template inline.required— fails rendering if a value is missing.lookup— queries the live Kubernetes API at render time (disabled duringhelm templatebut active duringhelm install/helm upgrade).getHostByName— performs DNS lookups (opt-in via--enable-dns).
Helm deliberately excludes the Sprig functions env and expandenv (which would read host environment variables) for security reasons. That exclusion is a hint at the underlying tension.
Beyond templates, Helm supports hooks — Kubernetes Jobs or other resources annotated with helm.sh/hook that run at specific lifecycle points (pre-install, post-install, pre-delete, etc.). A hook can execute arbitrary container images on the cluster.
What happens during helm install
- Helm loads the chart archive (
.tgz) and resolves dependencies. - CRDs (Custom Resource Definitions) in the
crds/directory are installed first (plain YAML, no templating allowed). - The template engine renders every file in
templates/against the merged values. - During rendering,
lookupcalls hit the Kubernetes API server with whatever RBAC (Role-Based Access Control) permissions the Helm client holds. - Rendered manifests are sorted by resource type, then by name.
- Resources are created or updated on the cluster.
- Hooks fire at their designated lifecycle points.
The security problem
The attack surface
Helm’s template rendering runs inside the Helm client process — typically on an operator’s workstation or in a CI/CD (Continuous Integration / Continuous Delivery) pipeline. The rendered output is pure YAML, but the rendering process itself has capabilities that extend beyond text generation:
| Capability | Risk |
|---|---|
lookup queries the Kubernetes API | A chart can read Secrets, ConfigMaps, or any resource the client’s kubeconfig allows |
getHostByName performs DNS lookups | A chart can probe internal DNS to map the network |
| Sprig crypto functions generate keys and certificates | A chart can create cryptographic material and embed it in manifests |
| Hooks run arbitrary containers | A post-install hook can run any image with any command on the cluster |
A chart from a trusted vendor is fine. A chart from an unknown source — pulled from a public Helm repository, forked from a GitHub project, or vendored by a colleague — is running code you did not write, with your credentials.
Supply chain risks
Helm charts have a deep dependency tree. A parent chart can declare sub-charts in Chart.yaml, each of which can have its own sub-charts. The lookup function, hooks, and template logic in any transitive dependency execute with the same privileges as the top-level chart.
Helm does support chart provenance — .tgz.prov files containing a SHA-256 checksum and an OpenPGP signature. You can verify a chart with helm verify or helm install --verify. There is also a helm-sigstore plugin for publishing provenance to Sigstore (an open-source project for software signing and transparency logs). But provenance proves who packaged the chart, not what the chart does at render time. A legitimately signed chart can still contain a lookup call that exfiltrates cluster state.
The old plugin model made it worse
Before Helm 4, plugins were subprocess-based: Helm spawned an external process (a Go binary, a Python script, a shell script) with full access to the host filesystem, network, and environment variables. A malicious or compromised plugin could do anything the user could do.
WebAssembly background
See WebAssembly for the full picture: sandbox model, capability-based security, WASI (the system interface), Wasm runtimes (Wasmtime, Wazero, Wasmer, V8), and the Component Model.
The key property Helm cares about: a Wasm module starts with zero capabilities and can only call functions the host explicitly provides. This is the opposite of a subprocess, which inherits everything from the OS and must be restricted. For Helm, this means a plugin can only access the filesystem, network, or Helm APIs that the operator explicitly grants in plugin.yaml.
The connection: Wasm as a sandbox for Helm plugins
Helm 4’s plugin architecture
Helm 4 (released November 12, 2025) redesigned the plugin system with two runtimes:
| Runtime | Identifier | How it works |
|---|---|---|
| Subprocess | subprocess | Spawns an external process. Full host access. Backward-compatible with Helm 3 plugins. |
| Wasm | extism/v1 | Runs a .wasm module inside a sandboxed Wasm runtime. Capability-based security. |
Helm uses Extism (an open-source Wasm plugin framework by Dylibso) as its Wasm runtime, which means plugins can be written in any language that compiles to Wasm and run inside Helm with controlled, auditable permissions. Extism handles module instantiation, memory management, and data marshalling — see Extism for the full architecture.
Plugin types in Helm 4
All three plugin categories support the Wasm runtime:
- CLI plugins (
cli/v1) — add new subcommands to thehelmCLI. - Getter plugins (
getter/v1) — fetch charts from custom storage backends (S3, GCS, internal registries). - PostRenderer plugins (
postrenderer/v1) — modify rendered manifests after template generation but before they reach the Kubernetes API. This is particularly relevant for security: a post-renderer can inject sidecars, enforce labels, or strip dangerous fields.
The sandbox controls
The plugin.yaml file for a Wasm plugin includes a runtimeConfig section that declares exactly what the module can do:
apiVersion: v1
type: postrenderer/v1
name: my-secure-postrenderer
version: 0.1.0
runtime: extism/v1
runtimeConfig:
# Memory: max pages (1 page = 64 KiB). Default: 4 pages (256 KiB).
maxPages: 16 # 1 MiB
# Network: which hosts the plugin can reach. Default: none.
allowedHosts:
- "registry.internal.example.com"
# Filesystem: temp directory access. Default: false.
fileSystem:
createTempDir: false
# Host functions: which Helm APIs the plugin can call.
hostFunctions:
- "helm_get_values"
# HTTP response size cap. Default: 4 KiB.
maxHttpResponseBytes: 8192
# Execution timeout in milliseconds.
timeout: 5000
# Free-form config passed to the plugin.
config:
policyURL: "https://policies.example.com/v1"
# Entry function name. Default: "helm_plugin_main".
entryFuncName: "helm_plugin_main"This is a declarative security manifest: before installing a plugin, an operator can read the plugin.yaml and see exactly what capabilities it requests. Compare this to a subprocess plugin, which is a black box with full host access.
What this solves (and what it does not)
| Problem | Wasm plugins help? |
|---|---|
Malicious plugin reads ~/.kube/config or ~/.aws/credentials | Yes — no filesystem access unless createTempDir is enabled, and even then it is a temp directory, not the host filesystem |
| Plugin exfiltrates data to an external server | Yes — allowedHosts restricts network access to an explicit allowlist |
| Plugin consumes unbounded memory or CPU | Yes — maxPages and timeout enforce resource limits |
Malicious chart template uses lookup to read Secrets | No — template rendering still happens in the Helm engine, not in a Wasm sandbox |
| Hook runs a malicious container image | No — hooks execute on the cluster, outside the Helm client |
The Wasm sandbox applies to plugins, not to chart template rendering itself. Template rendering still uses the Go template engine inside the Helm process. The lookup function and hook execution remain governed by Kubernetes RBAC, not by Wasm sandboxing.
Broader Wasm-in-Kubernetes story
See Wasm in the Kubernetes ecosystem for the full picture: SpinKube (Wasm as an alternative compute runtime), wasmCloud, OPA policy compilation to Wasm, and Envoy proxy-wasm filters.
Helm 4 is one of several CNCF (Cloud Native Computing Foundation) projects adopting Wasm. The common thread is capability-based security: start with zero capabilities and explicitly grant only what is needed.
Practical implications
Helm 4 Wasm plugins — production-ready (2026)
If you are writing a new Helm plugin, the Wasm runtime is the recommended path for security-sensitive environments. You can write plugins in Rust, Go (via TinyGo — a Go compiler targeting microcontrollers and Wasm), or any language with an Extism PDK (Plugin Development Kit).
When would you use Wasm plugins in Helm?
- You distribute plugins to teams that must audit what the plugin can do before installing it (the
runtimeConfiginplugin.yamlis the audit surface). - You run Helm in CI/CD pipelines where subprocess plugins are a security risk (any process spawned by the pipeline agent has access to pipeline secrets).
- You want cross-platform plugins: a single
.wasmbinary runs on Linux, macOS, and Windows without separate builds. - You write post-renderers that enforce organizational policy on rendered manifests and want to guarantee the post-renderer itself cannot exfiltrate data.
What Wasm does NOT solve in Helm
Wasm sandboxing applies to plugins, not to chart template rendering. The core security risks of Helm — lookup reading cluster state, hooks running arbitrary containers, deep dependency trees — remain. Mitigating those requires:
- RBAC: restrict the permissions of the ServiceAccount or kubeconfig used by Helm.
- Chart review: audit the
templates/directory of every chart and sub-chart before installation. - Provenance verification: use
helm install --verifywith trusted signing keys. - Admission controllers: use OPA/Gatekeeper or Kyverno (a Kubernetes-native policy engine that validates, mutates, and generates resources) to reject dangerous manifests regardless of how they were generated.
See also
- WebAssembly — sandbox model, WASI, runtimes, and the Kubernetes Wasm ecosystem
- Extism — the Wasm plugin framework Helm 4 uses (architecture, PDKs, security model)
- Knative — references Helm in the context of Kubernetes operator installation
- Kubernetes Networking — covers service mesh integration, which is where Envoy Wasm filters operate
- Kubelet — the node agent that SpinKube’s Runtime Class Manager configures for Wasm shim support
- CertManager — a common Helm chart dependency; its templates use many of the Sprig functions discussed here
- Karpenter — node provisioning; relevant when scaling nodes that support Wasm runtime classes