$30 off During Our Annual Pro Sale. View Details »

[Alpha] PodでUserNamespaceを使おう

Junya Taniai
November 15, 2022
240

[Alpha] PodでUserNamespaceを使おう

Kubernetes Novice Tokyo #22 ( https://k8s-novice-jp.connpass.com/event/262918/?utm_campaign=event_reminder&utm_source=notifications&utm_medium=email&utm_content=detail_btn ) のセッション資料です。
Kubernetes 1.25でAlphaで導入されたUser NamespaceをPodで使う方法やメリットについて紹介しています。

セッション動画はこちらです。
https://www.youtube.com/watch?v=_XwvFrMykWo

Junya Taniai

November 15, 2022
Tweet

Transcript

  1. 2022/11/15 1
    [Alpha] PodでUserNamespaceを使おう

    View Slide

  2. 2022/11/15 2
    ⾃⼰紹介
    ⾕合 純也(たにあい じゅんや)
    ⻑崎県五島市富江町出⾝
    ・所属︓株式会社イットーソフトウエア
    システム基盤技術部 クラウド技術課
    ・業務︓Kubernetesやクラウドに関するR&D
    ・趣味︓Kubernetes CustomController開発
    jnytnai0530
    junya0530
    jnytnai0613

    View Slide

  3. 2022/11/15 3
    https://zenn.dev/junya0530/articles/97b8926799b755
    https://zenn.dev/junya0530/articles/d47b1b58c265c8

    View Slide

  4. 2022/11/15 4
    これから話すこと
    本セッションでは明⽇から使えるTipsや、運⽤事例、最先端な取り
    組みなどは⼀切お話ししません。
    Kubernetes v1.25でAlphaで導⼊された推し機能であるUser
    Namespaceを布教するためのセッションです。

    View Slide

  5. 2022/11/15 5
    Agenda
    1. KubernetesでのUser Namespaceの扱い
    2. 実装を覗いてみよう
    3. 制限事項
    4. 何が嬉しいの?
    5. 有効化⽅法
    6. Demo
    7. 今後の展望

    View Slide

  6. 2022/11/15 6
    1. KubernetesでのUser Namespaceの扱い
    ノードのUID
    0 262143
    196608
    0 65535
    PodのUID
    KubernetesではノードのUIDおよびGID範囲内の⼀部(range 65536)をPodのUID/GID 0〜65535にマッピングする。
    つまり、ノード上では⾮rootで動いているが、Pod内ではrootで動いているように⾒せかけることが可能。
    UID範囲外の操作をしようとすると、権限不⾜が発⽣する。

    View Slide

  7. 2022/11/15 7
    Agenda
    1. KubernetesでのUser Namespaceの扱い
    2. 実装を覗いてみよう
    3. 制限事項
    4. 何が嬉しいの?
    5. 有効化⽅法
    6. Demo
    7. 今後の展望

    View Slide

  8. 8
    2. 実装を覗いてみよう
    ノードのUID/GIDをPodのUser Namespceにマッピングする流れは以下の通りです。
    1. NewMainKubelet関数で、Kubelet構造体をruntimeHelperに指定してruntimeを⽣成。
    この時Kubelet構造体はポインタを渡す。
    2. Kubelet構造体にusernsManagerフィールドを設定。
    usernsManagerはUIDとGIDを⽣成する際に必要なbitArrayなどが含まれる。
    3. Podがデプロイされたら、 determineEffectiveSecurityContext関数が呼ばれ、1で⽣成したruntime. runtimeHelperを
    引数にruntimeutil.NamespacesForPod関数を呼び出す。
    4. NamespacesForPod関数からruntimeHelper.GetOrCreateUserNamespaceMappings関数を呼び出す。
    5. GetOrCreateUserNamespaceMappings関数からcreateUserNs関数→ allocateOne関数→ findAvailable関数の順で呼び
    出す。
    6. findAvailable関数でUIDとGID計算⽤の値を算出
    7. $(UIDとGID計算⽤の値 * 65536)の式にて、 UIDとGID算出
    8. ノードの/var/lib/kubelet/pods/$(PodUUID)/usernsファイルにマッピング情報を書き出す
    9. マッピング情報をNamespaceOption構造体に設定して、Podを起動する。
    kubelet
    起動時
    Pod
    デプロイ時

    View Slide

  9. Kubelet
    2022/11/15
    2. 実装を覗いてみよう
    NewMainKubelet()
    runtime usernsManager
    Kubelet.
    determineEffective
    SecurityContext()
    usernsManager.
    allocateOne()
    usernsManager.
    bitArray.
    findAvailable()
    UIDとGIDの計算に
    使⽤する値を⽣成
    UIDとGIDの計算に
    使⽤する値のreturn
    kl.usernsManager.
    GetOrCreateUserName
    spaceMappings()
    UIDとGIDを
    計算してreturn
    /var/lib/kubelet/pods/
    $(PodUUID)/userns
    UIDとGIDのマッピング
    状況を書き込み
    usernsManager.
    createUserNs()
    UIDとGIDのマッピング
    状況のreturn
    NamespaceOption
    のreturn
    Podの起動
    上記処理の流れはいくつか関数を省略しています。
    詳細なコードリーディング結果は、以下のZennスクラップを
    ご確認ください。
    - [kubelet] User Namespaceのコードリーディング
    https://zenn.dev/junya0530/scraps/c75784ea5b8a5b
    9

    View Slide

  10. 2022/11/15 10
    Agenda
    1. KubernetesでのUser Namespaceの扱い
    2. 実装を覗いてみよう
    3. 制限事項
    4. 何が嬉しいの?
    5. 有効化⽅法
    6. Demo
    7. 今後の展望

    View Slide

  11. 2022/11/15 11
    3. 制限事項
    User Namespaceは便利︕ただし現時点では制限があります。
    1. Alphaで使⽤可能なVolume
    • Configmap
    • Secret
    • downwardAPI
    • emptyDir
    • Projected
    2. ノード上でUser Namespaceを利⽤可能なPodはMax1024個
    // Limit the total number of pods using userns in this node to this value.
    // This is an alpha limitation that will probably be lifted later.
    const maxPods = 1024
    https://github.com/kubernetes/kubernetes/blob/641197dc9562d8affd428d05046a8921b3a367e0/pkg/kubelet/userns_manager.go#L43-L45

    View Slide

  12. 2022/11/15 12
    Agenda
    1. KubernetesでのUser Namespaceの扱い
    2. 実装を覗いてみよう
    3. 制限事項
    4. 何が嬉しいの?
    5. 有効化⽅法
    6. Demo
    7. 今後の展望

    View Slide

  13. 2022/11/15 13
    ここだけの話、実は簡単にコンテナか
    らノードの乗っ取りできます。
    4. 何が嬉しいの? ~その1

    View Slide

  14. 2022/11/15 14
    $ cat << EOT | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
    name: escapepod-priv
    spec:
    hostPID: true
    containers:
    - image: ubuntu
    name: escapepod-priv
    command: ["sleep", "infinity"]
    securityContext:
    privileged: true
    EOT
    pod/escapepod-priv created
    ü パターン1
    hostPID = trueかつ、privileged = true
    $ kubectl exec escapepod-priv -it -- bash
    root@escapepod-priv:/# hostname
    escapepod-priv
    root@escapepod-priv:/# nsenter -t 1 -a bash
    root@v1252-worker:/# hostname
    v1252-worker
    4. 何が嬉しいの? ~その1

    View Slide

  15. 2022/11/15 15
    $ cat << EOT | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
    name: escapepod-hostpath
    spec:
    hostNetwork: true
    hostPID: true
    hostIPC: true
    containers:
    - name: escapepod-hostpath
    image: busybox
    command: [ "tail", "-f", "/dev/null" ]
    securityContext:
    privileged: true
    volumeMounts:
    - mountPath: /host
    name: noderoot
    volumes:
    - name: noderoot
    hostPath:
    path: /
    EOT
    pod/escapepod-hostpath created
    ü パターン2
    hostNetwork = true、 hostPID = true、 hostIPC = true、privileged: true、かつノードの / ディレクトリをhostpathでmount
    $ kubectl exec escapepod-hostpath –it -- sh
    / # cd /host
    /host # chroot .
    root@v1252-worker:/# hostname
    v1252-worker
    4. 何が嬉しいの? ~その1

    View Slide

  16. 2022/11/15 16
    これUser Namespaceだったら防げます。
    4. 何が嬉しいの? ~その1

    View Slide

  17. 2022/11/15 17
    if spec.SecurityContext.HostNetwork {
    allErrs = append(allErrs, field.Forbidden(fldPath.Child("hostNetwork"), "when `pod.Spec.HostUsers` is false"))
    }
    if spec.SecurityContext.HostPID {
    allErrs = append(allErrs, field.Forbidden(fldPath.Child("HostPID"), "when `pod.Spec.HostUsers` is false"))
    }
    if spec.SecurityContext.HostIPC {
    allErrs = append(allErrs, field.Forbidden(fldPath.Child("HostIPC"), "when `pod.Spec.HostUsers` is false"))
    }
    validation.goのvalidateHostUsers関数にて、以下のようにhost*フィールドとhostPathが禁⽌されており、乗っ取りに必要な権限がすべて剥奪され
    ている。ちなみprivilegedはUserNamespace内での権限となるため、特に規制はかかっていない。
    for i, vol := range spec.Volumes {
    switch {
    case vol.EmptyDir != nil:
    case vol.Secret != nil:
    case vol.DownwardAPI != nil:
    case vol.ConfigMap != nil:
    case vol.Projected != nil:
    default:
    allErrs = append(allErrs, field.Forbidden(fldPath.Child("volumes").Index(i),
    "volume type not supported when `pod.Spec.HostUsers` is false"))
    }
    }
    https://github.com/kubernetes/kubernetes/blob/641197dc9562d8affd428d05046a8921b3a367e0/pkg/apis/core/validation/validation.go#L3138-L3147
    https://github.com/kubernetes/kubernetes/blob/641197dc9562d8affd428d05046a8921b3a367e0/pkg/apis/core/validation/validation.go#L3121-L3131
    4. 何が嬉しいの? ~その1

    View Slide

  18. 2022/11/15 18
    コンテナの乗っ取りを防ぐことで、以下のノードのroot権限奪取による脆弱性について緩和が可能。
    • CVE-2019-5736
    • CVE-2021-25741
    • CVE-2017-1002101
    • CVE-2021-30465
    • CVE-2016-8867
    • CVE-2018-15664
    今年2022年の以下の脆弱性もUser Namespaceを使⽤することで緩和可能である可能性がある。
    • CVE-2022-0492
    • CVE-2022-23648
    4. 何が嬉しいの? ~その1

    View Slide

  19. 2022/11/15 19
    User Namespaceを使⽤することで、強い権限をPod内のUser Namespace内に限定または、無効とすることが
    できる。
    例えば、以下のcapabilityはPod外では無効になります。
    • CAP_SYS_MODULE
    ポッドに付与されても無視され、 Podはカーネルモジュールをロードすることができません。
    • CAP_SYS_ADMIN
    PodのUser Namespaceに限定され、外では無効となります。
    4. 何が嬉しいの? ~その2

    View Slide

  20. 2022/11/15 20
    $ cat << EOT | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    :
    spec:
    hostUsers: false
    containers:
    - image: nginx
    name: nginx
    EOT
    pod/nginx created
    $ kubectl exec nginx -it – bash
    root@nginx:/# cat /proc/self/uid_map
    0 196608 65536
    root@nginx:/# cat /proc/self/gid_map
    0 196608 65536
    ノード上は⾮rootでもPod内ではrootなので、rootで起動が必要なcontainerも起動可能
    ※パラメータの意味は後述します。
    4. 何が嬉しいの? ~その3

    View Slide

  21. 2022/11/15 21
    Agenda
    1. KubernetesでのUser Namespaceの扱い
    2. 実装を覗いてみよう
    3. 制限事項
    4. 何が嬉しいの?
    5. 有効化⽅法
    6. Demo
    7. 今後の展望

    View Slide

  22. 2022/11/15 22
    5. 有効化⽅法
    前提条件:
    • CRIがCRI-O v1.25以上であること
    (containerdはv1.7からuser namespaceをサポート予定) ※ 1 containerd現バージョンは1.6.9
    有効化
    - kube-apiserverと、ワーカーノードのkubeletでFeatureGate UserNamespacesStatelessPodsSupportを
    有効化します。
    spec:
    containers:
    - command:
    - kube-apiserver
    - --feature-gates=UserNamespacesStatelessPodsSupport=true
    /etc/kubernetes/manifests/kube-apiserver.yaml
    KUBELET_KUBEADM_ARGS=“--container-runtime=remote
    --container-runtime-endpoint=unix:///var/run/crio/crio.sock
    --pod-infra-container-image=registry.k8s.io/pause:3.8
    --feature-gates=UserNamespacesStatelessPodsSupport=true"
    /var/lib/kubelet/kubeadm-flags.env

    View Slide

  23. 2022/11/15 23
    Podの.spec.hostUsersをfalseにすることでPod内でUser Namespaceを使⽤することが可能となる。
    ※1
    $ cat << EOT | kubectl apply -f -
    apiVersion: v1
    kind: Pod
    metadata:
    name: userns
    spec:
    hostUsers: false
    containers:
    - name: userns
    image: debian
    command: ["sleep", "infinity"]
    EOT
    pod/userns created
    5. 有効化⽅法

    View Slide

  24. 2022/11/15 24
    ※1
    $ sudo cat /var/lib/kubelet/pods/$(kubectl get po userns -o jsonpath='{.metadata.uid}')/userns
    {"uidMappings":[{"hostId":131072,"containerId":0,"length":65536}],
    "gidMappings":[{"hostId":131072,"containerId":0,"length":65536}]}
    以下の場合、UIDの範囲が65536なので、WorkerNode上のUID 131072〜196607が、PodのUID 0〜65535に
    マッピングされていることが分かります。
    Podで確認しても同様の結果が得られる。
    $ kubectl exec userns -it – bash
    root@userns:/# id
    uid=0(root) gid=0(root) groups=0(root)
    root@userns:/# cat /proc/self/uid_map
    0 131072 65536
    root@userns:/# cat /proc/self/gid_map
    0 131072 65536
    5. 有効化⽅法

    View Slide

  25. 2022/11/15 25
    Agenda
    1. KubernetesでのUser Namespaceの扱い
    2. 実装を覗いてみよう
    3. 制限事項
    4. 何が嬉しいの?
    5. 有効化⽅法
    6. Demo
    7. 今後の展望

    View Slide

  26. 2022/11/15 26
    Demo
    6. Demo
    以下をDemoします。
    • PodでのUser Namespaceの利⽤
    • DeploymentでのUser Namespaceの利⽤およびPod再起動時の挙動
    • 乗っ取りの回避

    View Slide

  27. 2022/11/15 27
    Agenda
    1. KubernetesでのUser Namespaceの扱い
    2. 実装を覗いてみよう
    3. 制限事項
    4. 何が嬉しいの?
    5. 有効化⽅法
    6. Demo
    7. 今後の展望

    View Slide

  28. 2022/11/15 28
    1. Volume
    今現状、以下のVolumeしかサポートされていない。
    • Configmap
    • Secret
    • downwardAPI
    • emptyDir
    • Projected
    他のVlumeはBeta, GAとなるにつれて他のVolumeも使⽤可能となる予定です。
    2. ノードのUID枯渇対応
    現状では、User Namespaceを使⽤可能なPod数をノード毎に1024に制限し、UID枯渇に対応するようにしている。
    ただし、この対応が正しいのかはまだ結論が出ていない。
    以下の対応が検討されている。
    • PodのUser NamespaceがノードのUID全体を使⽤しないように制限を設ける
    • kubeletではなく、CRIにUIDマッピング⽅法を委ね、UID範囲を選択可能とする
    7. 今後の展望

    View Slide

  29. 2022/11/15 29
    参考資料
    https://kubernetes.io/docs/concepts/workloads/pods/user-namespaces/
    https://kubernetes.io/docs/tasks/configure-pod-container/user-namespaces/
    https://github.com/kubernetes/enhancements/tree/217d790720c5aef09b8bd4d6ca96284a0affe6c2/keps/
    sig-node/127-user-namespaces
    https://github.com/kubernetes/kubernetes/pull/111090
    https://github.com/kubernetes/enhancements/issues/127

    View Slide