Usernetes разворачивает Kubernetes без root на хосте (писали о нем еще в 2020) — чтобы побег из контейнера никуда не вёл. На днях проект перешёл с Gen2 на Gen3.
Gen2 умел ровно один режим: Kubernetes-in-Docker поверх Rootless Docker/Podman/nerdctl — как rootless kind, только с поддержкой нескольких хостов.
Gen3 — надмножество: этот режим остался дефолтным, а сверху приехал Kubernetes-in-Kubernetes, где внутренний кластер живёт во внешнем в виде подов с hostUsers: false (KEP-127, UserNamespaces GA в 1.36).
Что тут важно для c точки зрения ИБ:
- node-поды не используют privileged: true. Вместо него — все namespaced capabilities, procMount: Unmasked и unconfined seccomp/AppArmor, а изоляцию держит user namespace. Тот же приём применён к kube-proxy внутреннего кластера: privileged выкинут в пользу NET_ADMIN и NET_RAW
- при этом namespace обязан разрешать профиль privileged в Pod Security admission — снаружи картина максимально пугающая, хотя это ровно тот случай, ради которого userns и создавали
- kubelet выдаёт каждому userns-поду свой диапазон в 65536 UID/GID, причём /etc/subuid хостов не участвует вообще — тенанты не пересекаются по UID даже на одной ноде
Цена вопроса: containerd >= 2.1 с отдельным runtime handler и cgroup_writable = true (включать его на дефолтном runc авторы прямо не советуют — заденет все непривилегированные поды), ядро >= 6.3 ради idmapped mounts для tmpfs, и сам режим помечен как экспериментальный. Зато отлично видно, до чего доросли user namespaces: целый кластер с kubelet, containerd и etcd уезжает внутрь пода — и без privileged =)
Gen2 умел ровно один режим: Kubernetes-in-Docker поверх Rootless Docker/Podman/nerdctl — как rootless kind, только с поддержкой нескольких хостов.
Gen3 — надмножество: этот режим остался дефолтным, а сверху приехал Kubernetes-in-Kubernetes, где внутренний кластер живёт во внешнем в виде подов с hostUsers: false (KEP-127, UserNamespaces GA в 1.36).
Что тут важно для c точки зрения ИБ:
- node-поды не используют privileged: true. Вместо него — все namespaced capabilities, procMount: Unmasked и unconfined seccomp/AppArmor, а изоляцию держит user namespace. Тот же приём применён к kube-proxy внутреннего кластера: privileged выкинут в пользу NET_ADMIN и NET_RAW
- при этом namespace обязан разрешать профиль privileged в Pod Security admission — снаружи картина максимально пугающая, хотя это ровно тот случай, ради которого userns и создавали
- kubelet выдаёт каждому userns-поду свой диапазон в 65536 UID/GID, причём /etc/subuid хостов не участвует вообще — тенанты не пересекаются по UID даже на одной ноде
Цена вопроса: containerd >= 2.1 с отдельным runtime handler и cgroup_writable = true (включать его на дефолтном runc авторы прямо не советуют — заденет все непривилегированные поды), ядро >= 6.3 ради idmapped mounts для tmpfs, и сам режим помечен как экспериментальный. Зато отлично видно, до чего доросли user namespaces: целый кластер с kubelet, containerd и etcd уезжает внутрь пода — и без privileged =)