# vArmor: Kubernetes Container Hardening with AppArmor, BPF, Seccomp and an Envoy Egress Proxy

> vArmor is a Kubernetes operator that turns AppArmor, BPF LSM, Seccomp and an Envoy-based network proxy into policy-driven enforcers for container workloads, including AI agents. It is a risk-reduction tool for runc containers, not an isolation boundary that replaces hardware-virtualized runtimes.

**bytedance/vArmor** — vArmor is a cloud-native container hardening system that leverages AppArmor/BPF/Seccomp and NetworkProxy technologies to enforce access control from system calls to application protocols — protecting workloads including AI Agents.

- Repository: https://github.com/bytedance/vArmor
- Website: https://varmor.org
- Stars: 504 · Forks: 69
- Language: Go
- License: Apache-2.0
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/bytedance-varmor

## What vArmor Solves That Kubernetes RBAC and NetworkPolicy Cannot

Kubernetes gives you admission control, RBAC and NetworkPolicy. None of those constrain what a process inside a container does after it starts. A compromised process can still call arbitrary syscalls, execute binaries it fetched at runtime, and open outbound connections to any address the CNI permits. vArmor targets that gap. It is a cloud-native container hardening system, built by the Elkeid Team in ByteDance's endpoint security department, that applies Linux security modules and a network proxy to running workloads through a CRD API.

The intended audience is platform and security engineers running multi-tenant Kubernetes clusters where hardware-virtualized container runtimes are off the table for cost or technical reasons. The README names three further cases: hardening business-critical containers against privilege escalation and lateral movement, mitigating high-risk vulnerabilities that cannot be patched immediately, and constraining AI agents or LLM applications whose outbound calls should be limited to an approved set of endpoints.

That last case is where the project has moved furthest from classic LSM tooling. Prompt injection turns an agent's own tool-calling loop into an attack surface, and a syscall filter does not see an HTTP request. vArmor pairs kernel-level mandatory access control with protocol-level egress rules so that a hijacked agent cannot quietly exfiltrate data to an unlisted host.

## Enforcers: AppArmor, BPF LSM, Seccomp and the Envoy Sidecar

vArmor abstracts four enforcement mechanisms and lets you combine them in one policy. AppArmor and BPF LSM handle file access and process execution. Seccomp handles the syscall surface. NetworkProxy is an Envoy-based sidecar that transparently intercepts container egress at L4 (TCP), L7 (HTTP/HTTPS) and TLS SNI levels.

The NetworkProxy enforcer is the part that differs from anything a stock Kubernetes install offers. According to the README it supports TLS MITM termination for decrypted HTTPS inspection, per-domain HTTP header injection (the example given is API key injection), anti-Domain-Fronting protection, allow-list and deny-list modes, and audit logging. Policy updates propagate without pod restarts, which matters if you are tightening rules in response to an incident.

The repository layout reflects this split. There is a cmd/varmor entrypoint and a cmd/classifier entrypoint, a vArmor-ebpf submodule for the BPF programs, and manifests plus a Helm chart under config and manifests. The go.mod pulls in cilium/ebpf, libseccomp-golang, envoyproxy/go-control-plane, controller-runtime and the CRI API. That dependency set tells you what the operator actually does: it reconciles custom resources, talks to the container runtime, generates profiles, and programs an Envoy control plane.

The policy model is allow-by-default. Only explicitly declared behaviors are blocked, which the README frames as a way to limit performance impact and keep the system usable. The project also supports deny-by-default through allowlist profiles, and it ships built-in rules so that you do not need to author AppArmor profiles by hand. Auditing is available as an alternative to blocking, which is the right mode for a first rollout.

## Installing vArmor and Applying a First Policy

The README points to varmor.org for installation rather than embedding steps, so the values below come from the repository's Makefile. The Makefile derives the image tag from git describe --tags --match "v[0-9]*", strips any pre-release suffix for release images, and builds four images: varmor, classifier, proxyinit and envoy. The default registry is elkeid-ap-southeast-1.cr.volces.com and the default namespace is varmor.

These are the Makefile variables that control what gets built and where it is pushed:

```makefile
REGISTRY_AP ?= elkeid-ap-southeast-1.cr.volces.com
NAMESPACE ?= varmor
VARMOR_IMAGE_NAME := varmor
PROXY_IMAGE_NAME:= envoy
PROXY_IMAGE_TAG := v1.38-latest
```

The namespace and registry defaults matter because the Helm chart variables are derived from them:

```makefile
REPO_AP = $(REGISTRY_AP)/$(NAMESPACE)
CHART_VERSION := $(shell echo $(CHART_APP_VERSION) | sed 's/^v//')
```

The chart version is the app version with the leading v removed, so release v0.10.4 maps to chart version 0.10.4. Before installing, confirm which LSMs the node kernel exposes, since the available enforcers depend on it. The project supports AppArmor LSM and BPF LSM, and the kernel reports the active set through the security filesystem path described in the AppArmor and BPF LSM documentation the README links to.

After the operator is running, hardening a workload means creating a CRD object that selects the target pods and names the enforcers and rules. The README describes this as manipulating the CRD API rather than writing profiles directly. The exact schema lives at varmor.org under Policies and Rules; the repository's apis directory holds the Go type definitions. Start with audit mode rather than blocking, so that violations are logged while you learn what your workload actually does.

## Where vArmor Is the Wrong Tool

The README is unusually direct about this, and it is worth repeating rather than softening. runc plus vArmor does not provide an isolation level equivalent to hardware virtualization containers such as Kata Containers. If you need high-intensity isolation, the project itself tells you to use hardware-virtualized containers for compute isolation and CNI NetworkPolicy for network isolation.

So vArmor is a hardening layer, not a boundary replacement. It raises the cost of an exploit; it does not make a container escape impossible. The README frames the whole exercise as balancing risks and benefits, converting uncontrollable risks into controllable costs.

There are practical constraints beyond the isolation ceiling. AppArmor and BPF LSM both depend on kernel support, and a mixed-architecture cluster means your policies may behave differently across node pools. The NetworkProxy enforcer terminates TLS to inspect HTTPS, which means the proxy holds decrypted traffic and you need to trust that path; certificate handling for MITM is an operational burden, not a checkbox. Sidecar injection changes pod composition, which can interact badly with admission webhooks or resource quotas elsewhere in the cluster. And because the default model is allow-by-default, a policy that looks strict may still permit more than you expect until you switch to an allowlist profile.

## vArmor Versus Plain NetworkPolicy and Standalone AppArmor

The honest comparison is not against another container hardening operator. It is against the two things you would otherwise assemble by hand.

Plain Kubernetes NetworkPolicy operates at L3/L4 and is enforced by the CNI. It can say that a pod may reach a CIDR or a namespace. It cannot say that a pod may reach api.example.com and nothing else on port 443, because it does not parse TLS SNI or HTTP. The README states this directly: vArmor's NetworkProxy enforcer complements NetworkPolicy by adding L7 access control for HTTP and HTTPS, SNI-based domain filtering, per-domain header injection, anti-Domain-Fronting protection and audit logging. The difference in approach is that NetworkPolicy filters packets while NetworkProxy terminates and inspects sessions. If your requirement is only pod-to-pod reachability, NetworkPolicy is simpler and has no sidecar to operate.

The other alternative is writing AppArmor profiles and seccomp profiles yourself and shipping them through the container runtime or a profile manager. That gives you full control and no operator in the cluster. What you lose is the Kubernetes-native lifecycle: vArmor reconciles policies through CRDs, tracks which workloads they apply to, and updates enforcement without restarting pods. Hand-written profiles are static files that must be distributed and reloaded, and there is no built-in audit pipeline or rule library. If your cluster has a handful of long-lived workloads and a team comfortable with AppArmor syntax, that route is defensible. If policies need to change per workload and per incident, the operator model earns its keep.

## Licensing and the Cost of Staying Current

vArmor is licensed under Apache 2.0, with a stated exception: the README notes the project is Apache 2.0 "except fo" (the sentence is truncated in the repository's README rendering), and the badges at the top include a GPL license badge alongside the Apache 2.0 one. The likely reason is the vArmor-ebpf submodule, since eBPF code that links kernel headers commonly carries GPL obligations. Do not treat the badge pair as decoration: if you redistribute vArmor or build a product on it, confirm with your own counsel which components fall under which license, and check the LICENSE file and the submodule's own terms rather than the README summary.

Maintenance cost is the other number to budget. The last push to the default branch was on 2026-09-08, and the most recent release is v0.10.4 from 2026-08-13, preceded by v0.10.3 on 2026-07-02 and a v0.10.4-beta.1 on 2026-08-04. Releases arrive on a roughly monthly cadence, with beta tags ahead of stable ones. That cadence cuts both ways: you get fixes, and you get a moving target. The Makefile's image tagging scheme ties the chart version to the git tag, so an upgrade is a coordinated change of operator image, classifier image, proxyinit and the Envoy sidecar version (v1.38-latest in the Makefile). Pinning the Envoy tag to something other than a floating latest is worth doing on your side.

The upgrade path itself is the part the README does not document. There is no rollback procedure in the README, and no compatibility matrix stating which Kubernetes versions a given vArmor release supports. The go.mod pins k8s.io/api v0.31.1 and controller-runtime v0.19.0, which tells you what the current release was built against, not what it tolerates. Verify that against your cluster before scheduling an upgrade window.

## Conclusion

Adopt vArmor if you run runc-based workloads in Kubernetes and need syscall-level confinement plus L7 egress control without moving to hardware virtualization. Do not adopt it as a substitute for Kata Containers or as a network isolation layer for clusters that only need pod-to-pod policy. Before rolling it out, verify that your nodes ship an AppArmor or BPF LSM capable kernel, that the chart version you install matches the vArmor image tag the Makefile derives from git describe, and that your team accepts the operational cost of TLS MITM and sidecar injection in the target namespaces.

## FAQ

### What is vArmor and what does it protect?

vArmor is a cloud-native container hardening system that uses AppArmor LSM, BPF LSM, Seccomp and an Envoy-based NetworkProxy sidecar as enforcers. It strengthens container isolation, reduces the kernel attack surface and applies L4/L7 egress control, and the README lists AI agent and LLM workloads among its target scenarios.

### Does vArmor replace Kata Containers for isolation?

No. The README states that runc plus vArmor does not provide an isolation level equivalent to hardware virtualization containers such as Kata Containers, and it recommends hardware-virtualized containers for compute isolation and CNI NetworkPolicy for network isolation if you need that level of separation.

### How is vArmor installed on Kubernetes?

The README directs installation to varmor.org rather than listing steps inline. The repository's Makefile builds varmor, classifier, proxyinit and envoy images from a tag derived by git describe, and the Helm chart version is that tag with the leading v removed, so v0.10.4 maps to chart version 0.10.4.

### What is the difference between vArmor's NetworkProxy enforcer and Kubernetes NetworkPolicy?

NetworkPolicy operates at L3/L4 through the CNI, while the NetworkProxy enforcer intercepts egress at L4, L7 and TLS SNI, adding TLS MITM inspection, per-domain HTTP header injection, anti-Domain-Fronting protection and audit logging. The README describes NetworkProxy as complementing NetworkPolicy rather than replacing it.

### Which license does vArmor use?

The README states the project is licensed under Apache 2.0 with an exception, and the repository shows both an Apache 2.0 and a GPL license badge. The vArmor-ebpf submodule is a plausible source of the GPL component, so check the LICENSE file and the submodule terms directly.

## Sources

- [bytedance/vArmor on GitHub](https://github.com/bytedance/vArmor)
- [License: Apache-2.0](https://github.com/bytedance/vArmor/blob/main/LICENSE)
- [Project website](https://varmor.org)
- [README](https://github.com/bytedance/vArmor/blob/main/README.md)
- [Releases](https://github.com/bytedance/vArmor/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/bytedance-varmor
