Skip to content

Commit e2a5322

Browse files
committed
fix kind in camel-k template
1 parent 94e7dd9 commit e2a5322

4 files changed

Lines changed: 59 additions & 23 deletions

File tree

Containerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ FROM registry.fedoraproject.org/fedora:44
22

33
# ── System packages ──────────────────────────────────────────────────────────
44
RUN dnf install -y --setopt=retries=5 \
5-
git git-lfs curl wget jq zip unzip findutils procps-ng hostname \
5+
git git-lfs curl wget jq yq zip unzip findutils procps-ng hostname \
66
diffutils less iproute iptables openssh-server \
77
podman fuse-overlayfs e2fsprogs maven nodejs npm gh \
88
gcc gcc-c++ make java-devel podman-compose golang \

README.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ Ephemeral, microVM-isolated dev containers for AI-assisted development. Each con
2929
- **MCP whitelist** — only explicitly whitelisted MCP servers are proxied into containers (see `MCP_WHITELIST` in `scripts/dev-proxy.py`)
3030
- **Selective key mounting** — only specific key files are mounted into containers (container SSH pubkey, read-only GitHub PAT); host-only keys like `id_ed25519_dev_automation` never enter containers. The Bob API key is injected via `podman secret` (never volume-mounted)
3131

32+
### The `kind` profile weakens in-VM isolation
33+
34+
Containers using the `kind` profile (currently only camel-k) auto-create a Kubernetes cluster with **rootful podman inside the VM**. This is required, not a shortcut: the `kindest/node` image runs systemd, which needs a root-owned cgroup that the unprivileged `dev` user cannot create — this microVM has no systemd/cgroup delegation, so rootless Kind cannot boot here. (Also, the node's kubelet needs a real block device for its rootfs, so Kind/registry storage is pinned to the bounded podman disk rather than the virtiofs root.)
35+
36+
**Consequence — for these containers, treat the `dev`-vs-root boundary *inside the VM* as gone.** Kind's kubeconfig is cluster-admin, so the agent has a practical, non-exploit path to VM-root (cluster-admin → privileged pod → node container, which runs as VM-root). That means:
37+
38+
- The per-container disk caps and root-owned config (guest nftables firewall, `/etc`, `/opt`, the Bob API key file) are no longer protected from the agent.
39+
- **Residual host risk:** a VM-root agent can write to the container's *uncapped* writable rootfs layer on the host, which could fill the host disk (DoS). Kind/registry image storage itself stays capped (pinned to the bounded podman disk).
40+
41+
**What is _not_ affected:** the KVM boundary still fully contains the VM. Your host filesystem, Google Vertex credentials, and the GitHub-write SSH key never enter the VM, so they remain protected even against a VM-root agent. Non-`kind` containers keep the full rootless posture described above.
42+
3243
## Prerequisites
3344

3445
- Clone this repo to `~/sandboxing`: `git clone git@github.com:michalvavrik/ai-sandboxing.git ~/sandboxing`
@@ -416,7 +427,7 @@ The `profiles` field in `project-templates.conf` is a comma-separated list that
416427
|---------|--------|
417428
| `java` | Maven cache overlay from host `~/.m2/repository` |
418429
| `go` | Sets GOPATH, GOBIN, adds `~/go/bin` to PATH |
419-
| `kind` | Auto-creates a Kind cluster with local registry (`localhost:5001`) on first start, 12 GiB podman storage |
430+
| `kind` | Auto-creates a **rootful** Kind cluster with local registry (`localhost:5001`) on first start; 20 GiB podman storage. **Weakens in-VM isolation — see [the `kind` profile security note](#the-kind-profile-weakens-in-vm-isolation).** |
420431

421432
```bash
422433
# camel-k (profiles: go,kind):
@@ -468,7 +479,7 @@ Host krun MicroVM
468479
└── dev-sandbox-disks/ └── bounded loopback disks (ext4, root:600 inside VM)
469480
├── <name>.img (workspace) ├── /mnt/bounded → /workspace, /home/dev, /tmp, /var
470481
└── <name>-podman.img └── /mnt/podman → rootless Podman storage
471-
(6 GiB java, 12 GiB go) (Testcontainers / Kind nodes)
482+
(6 GiB default, 20 GiB kind) (Testcontainers / rootful Kind nodes)
472483
```
473484

474485
### Bob Shell credential isolation

entrypoint.sh

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -153,37 +153,61 @@ STCONF
153153
runuser -u dev -- bash -c 'cd /run/user/1000 && XDG_RUNTIME_DIR=/run/user/1000 podman system service --time=0 &'
154154
fi
155155

156-
# ── Kind cluster (auto-create on first start when 'kind' profile is set) ────
156+
# ── Kind cluster (ROOTFUL — auto-create on first start when 'kind' profile set) ──
157+
# Runs as VM-root: root's CAP_DAC_OVERRIDE gets past the root-owned 555 /sys/fs/cgroup
158+
# so the kindest/node systemd can create its cgroups. The unprivileged dev user cannot
159+
# (this microVM has no systemd/cgroup delegation, so rootless Kind is impossible here).
160+
# Root podman storage is pinned to the bounded podman disk (loop-backed ext4): this caps
161+
# Kind/registry image growth AND gives kubelet/cAdvisor a real block device for the node
162+
# rootfs — a virtiofs root fails with "no partition info for device /dev/root".
163+
# SECURITY: kind-profile containers therefore run Kubernetes as VM-root, and the agent
164+
# can reach VM-root via the cluster (cluster-admin -> privileged pod -> node). See the
165+
# "kind profile" security note in README. The KVM boundary still contains everything.
157166
if _has_profile kind && command -v kind &>/dev/null; then
158-
_kind_wait=0
159-
while ! runuser -u dev -- podman info &>/dev/null && (( _kind_wait < 30 )); do
160-
sleep 1; _kind_wait=$(( _kind_wait + 1 ))
161-
done
162-
163-
if runuser -u dev -- podman info &>/dev/null; then
164-
if ! runuser -u dev -- bash -c 'export KIND_EXPERIMENTAL_PROVIDER=podman; kind get clusters 2>/dev/null' | grep -q "dev-k8s"; then
165-
echo "Creating Kind cluster with local registry..."
166-
runuser -u dev -- bash -c '
167-
export KIND_EXPERIMENTAL_PROVIDER=podman
168-
podman run -d --restart=always -p 127.0.0.1:5001:5000 --name kind-registry registry:2 2>/dev/null || true
169-
cat <<KINDCFG | kind create cluster --name dev-k8s --wait 120s --config=-
167+
# Kubernetes needs higher limits than the krun defaults (real guest kernel — settable).
168+
sysctl -w fs.inotify.max_user_instances=512 fs.inotify.max_user_watches=1048576 \
169+
kernel.pid_max=32768 >/dev/null 2>&1 || true
170+
171+
mkdir -p /mnt/podman/root-storage /mnt/podman/root-run
172+
cat > /etc/containers/kind-root-storage.conf <<'ROOTSTORE'
173+
[storage]
174+
driver = "overlay"
175+
graphroot = "/mnt/podman/root-storage"
176+
runroot = "/mnt/podman/root-run"
177+
ROOTSTORE
178+
export CONTAINERS_STORAGE_CONF=/etc/containers/kind-root-storage.conf
179+
export KIND_EXPERIMENTAL_PROVIDER=podman
180+
181+
# Returns kind's exit code, so success/failure is detected correctly.
182+
_dev_kind_create() {
183+
podman run -d --restart=always -p 127.0.0.1:5001:5000 \
184+
--name kind-registry docker.io/library/registry:2 2>/dev/null || true
185+
kind create cluster --name dev-k8s --wait 120s --config=- <<KINDCFG
170186
kind: Cluster
171187
apiVersion: kind.x-k8s.io/v1alpha4
172188
containerdConfigPatches:
173189
- |-
174190
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5001"]
175191
endpoint = ["http://kind-registry:5001"]
176192
KINDCFG
177-
podman network connect kind kind-registry 2>/dev/null || true
178-
' && echo "Kind cluster ready. Registry at localhost:5001" \
179-
|| echo "WARNING: Kind cluster creation failed — create manually with: kind create cluster --name dev-k8s" >&2
193+
}
194+
195+
if ! kind get clusters 2>/dev/null | grep -q "dev-k8s"; then
196+
echo "Creating rootful Kind cluster with local registry..."
197+
if _dev_kind_create; then
198+
podman network connect kind kind-registry 2>/dev/null || true
199+
install -o dev -g dev -d /home/dev/.kube
200+
kind get kubeconfig --name dev-k8s > /home/dev/.kube/config
201+
chown dev:dev /home/dev/.kube/config
202+
echo "Kind cluster ready. Registry at localhost:5001"
180203
else
181-
runuser -u dev -- bash -c 'podman start dev-k8s-control-plane kind-registry 2>/dev/null' || true
182-
echo "Kind cluster already exists."
204+
echo "WARNING: Kind cluster creation failed — create manually (as root): KIND_EXPERIMENTAL_PROVIDER=podman kind create cluster --name dev-k8s" >&2
183205
fi
184206
else
185-
echo "WARNING: podman not ready, skipping Kind cluster setup" >&2
207+
podman start dev-k8s-control-plane kind-registry 2>/dev/null || true
208+
echo "Kind cluster already exists."
186209
fi
210+
unset CONTAINERS_STORAGE_CONF
187211
fi
188212

189213
# ── Maven cache (fuse-overlayfs as dev user) ────────────────────────────────

scripts/dev-common.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ _dev_has_profile() {
3131
_dev_podman_storage_gib() {
3232
local _dev_profiles="${1:-java}"
3333
if _dev_has_profile "$_dev_profiles" "kind"; then
34-
echo 12
34+
# Shared by dev's rootless storage and root's Kind storage (see entrypoint.sh).
35+
echo 20
3536
else
3637
echo 6
3738
fi

0 commit comments

Comments
 (0)