Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kubernetes Meetup Tokyo #67 - KEP-3619: Fine-gr...

Kubernetes Meetup Tokyo #67 - KEP-3619: Fine-grained SupplementalGroups Control / k8sjp67-kep-3619

Kubernetes v1.31でアルファ機能としてリリースしたKEP-3619: Fine-Grained SupplementalGroups Controlという機能やその背景について共有します。

Shingo Omura

October 09, 2024
Tweet

More Decks by Shingo Omura

Other Decks in Technology

Transcript

  1. @everpeace Shingo OMURA / @everpeace ▶ Multi/Hybrid Cloud ML Platform

    Engineer ▶ ex-Woven By Toyota, ex-PFN ▶ 主な活動SIG: sig-node, sig-scheduling
  2. @everpeace おさらい: Supplementary Groups とは $ id uid=1000(codespace) gid=1000(codespace) groups=1000(codespace),106(ssh),107(docker),...

    User ID (UID) Primary Group ID (GID) Supplementary Group IDs 今日はコレの話! 📰Linuxではgroupsの先頭はprimary groupが入っていることが想定されていますが 各種container runtimeがそうなっていなくてCVEが出たことも → "Vulnerability in Linux containers ‒ investigation and mitigation" TL;DR; setgidで完全にprimary groupを抜けることができてnegative permission (特定のグループのpermissionが000)をbypass可能
  3. @everpeace おさらい: Supplementary Groupsは PodSecurityContextで指定できる spec: securityContext: runAsUser:1000 runAsGroup:1000 supplementalGroups:[60000]

    containers: - name: ctr securityContext: # runAsUser, runAsGroupはあるけど、supplementalGroupsはない 👈あまり知られていない&セキュリティ的に不安な  挙動あり(KEP-3619の解決したい課題)
  4. @everpeace 🤔 SupplementalGroupsのあまり知られていない挙動 FROM ubuntu RUN useradd -m -u 1000

    alice && groupadd -g 50000 group-in-image \ && gpasswd -a alice group-in-image uid=1000(alice) gid=1000(alice) groups=1000(alice),50000(group-in-image)     ,60000 spec: securityContext: runAsUser: 1000 # alice supplementalGroups: [60000] containers: - image: image-above command: ["id"] • コンテナイメージ(の/etc/group)で alice(1000)がgroup-in-image(50000) に所属していて • Manifest上で containerのUIDがalice(1000)だと GID 50000に一切触れていなくても 🤔 group-in-image(50000)が暗黙的に マージされてしまう 🤔 つまり、イメージ焼くだけでManifestに定義され ていない自分の好きなGIDを追加できる 👉/etc/groupの情報が暗黙的にマージされてしまう
  5. @everpeace 😿 何が困るのか? FROM ubuntu RUN useradd -m -u 1000

    alice && groupadd -g 50000 group-in-image \ && gpasswd -a alice group-in-image uid=1000(alice) gid=1000(alice) groups=1000(alice),50000(group-in-image)     ,60000 spec: securityContext: runAsUser: 1000 # alice supplementalGroups: [60000] containers: - image: image-above command: ["id"] 😿 Policy Engine等の静的検査で検出できない (Pod Specの宣言性の破壊) 😿 HostPathボリューム, NFSボリューム等の グループパーミッションを意図的にbypassできる (k/k#112879) 😿 Kubernetes API上ではこの挙動は 変更不可能(だった) 👉/etc/groupの情報が暗黙的にマージされてしまう 🤔 group-in-image(50000)が暗黙的に マージされてしまう 🤔 つまり、イメージ焼くだけでManifestに定義され ていない自分の好きなGIDを追加できる
  6. @everpeace KubernetesにおけるContainerのUser Identities決定の流れ CRI Runtime (containerd, CRI-O, etc.) → CRIのSecurityContextを読んでOCI

    Runtime Specに変換してOCI Runtimeを呼ぶ CRI: LinuxContainerSecurityContext # CRI (protobuf) security_context { run_as_user: 1000, run_as_group: 1000, supplemental_groups: [60000] } OCI Runtime (runc, crun, youki, etc.) → OCI Runtime Specを読んで実際にコンテナを作る OCI Runtime Spec: process.user # OCI runtime spec (json) "user": {"uid": 1000, "gid": 1000, "additionalGids": [50000, 60000]} Kubelet → Specを読んでCRIを呼ぶ(Image ConfigはCRIのImage Service経由で読む) OCI Image Spec: Configuration.User (DockerfileのUSER) PodSecurityContext (ContainerSecurityContext) # Dockerfile USER 1000:1000 # OCI Image Configuration # application/vnd.oci.image.config.v1+json "config": {"User": "1000:1000"} # kind: Pod spec: securityContext: runAsUser: 1000 # alice runAsGroup: 1000 # alice supplementalGroups: [60000]
  7. @everpeace Kubelet → Specを読んでCRIを呼ぶ(Image ConfigはCRIのImage Service経由で読む) /etc/groupからsupplementary groupを追加しているのはCRI Runtime CRI

    Runtime (containerd, CRI-O, etc.) → CRIのSecurityContextを読んでOCI Runtime Specに変換してOCI Runtimeを呼ぶ CRI: LinuxContainerSecurityContext # CRI (protobuf) security_context { run_as_user: 1000, run_as_group: 1000, supplemental_groups: [60000] } OCI Runtime (runc, crun, youki, etc.) → OCI Runtime Specを読んで実際にコンテナを作る OCI Runtime Spec: process.user # OCI runtime spec (json) "user": {"uid": 1000, "gid": 1000, "additionalGids": [50000, 60000]} OCI Image Spec: Configuration.User (DockerfileのUSER) PodSecurityContext (ContainerSecurityContext) # Dockerfile USER 1000:1000 # OCI Image Configuration # application/vnd.oci.image.config.v1+json "config": {"User": "1000:1000"} # kind: Pod spec: securityContext: runAsUser: 1000 # alice runAsGroup: 1000 # alice supplementalGroups: [60000] 💡CRIまでは暗黙的な  GIDは追加されていない 💡OCI Runtime Specで暗黙的な GIDが追加されている!
  8. @everpeace OCI Image Spec: Configuration.Userの仕様 (v1.1.0) 💡 Kubernetesはこの仕様に厳密に従っていない点に注意 ◦ Dockerfileで"USER

    1000:1000"&空SecurityContextでも/etc/group読む 💡 containerd(docker cli, ctr経由)はこの仕様に準拠している 🤔/etc/groupも必要 では? ✏ちゃんと書いてある!!!
  9. @everpeace CRI: LinuxContainerSecurityContextの仕様 (KEP-3619以前, 仕様はPod側とほぼ同様) ✏ちゃんと書いてある!!! 💡OCI Image Configuration Specとねじれている

    (runAsGroupが指定されているかどうかに関わらず/etc/groupのグループを含むと書かれている) 💡この仕様はもともとDockerの仕様に習ったもので意図的ではなかった&このKEPが提案されるまでこの  仕様の再検討はされなかったとのこと( kubernetes/website#46921 )
  10. @everpeace 仕様のねじれのまとめ OCI Image Spec: Configuration.User USER 1000:1000 # /etc/group

    読まない USER 1000 # /etc/group 読む (USER指定なし) # /etc/group 読む          # runtimeのdefault uid # が適用(通常はuid=0) SecurityContext securityContext: {} # /etc/group 読む # uid/gidはImage Config見る securityContext: runAsUser: 1000 # /etc/group 読む # gidはImage Config見る securityContext: runAsUser: 1000 # /etc/group 読む runAsGroup: 1000 😿 Kubernetesではどうやっても/etc/group の反映を回避できない(KEP-3619以前)
  11. @everpeace 提案1: SupplementalGroupsPolicy APIの追加 securityContext: runAsUser: 1000 # alice runAsGroup:

    1000 # alice supplementalGroups:[60000] # default=Merge supplementalGroupsPolicy: Merge|Strict uid=1000(alice) gid=1000(alice) groups=1000(alice),50000(group-in-image),60000 uid=1000(alice) gid=1000(alice) groups=1000(alice),60000 Merge Strict • ねじれている仕様をOCIの仕様に寄せる変更はBreaking Changeになる • Kubernetes自体はOCI Image Configに完全準拠しないのは妥協できる 💡 Kubernetes API上で挙動を制御するAPIを導入しよう(Breaking Changeでない) 📝 これに付随するCRIの変更もありますが割愛
  12. @everpeace 提案2: ContainerStatus.Userの追加 # kind: Pod status: containerStatuses: - user:

    linux: uid: 1000 gid: 1000 supplementalGroups: [1000, 60000] 💡 実際にどのidentityが割り当てられたか確認できる 👀 このフィールドは初期値(OCI Runtime Specに渡された値)なので注意 ◦ 十分な権限があればプロセス自身でuid/gidは動的に変更可能なことも注意 📝 これに付随するCRIの変更もありますが割愛
  13. @everpeace 提案3: Node.Status.Featuresの追加 # kind: Node status: features: supplementalGroupsPolicy: true|false

    # ↓こちらはRuntimeHandler(aka: RuntimeClass, OCI Runtime)に依存したfeatureなので混同注意 runtimeHandlers:[{"name": "", "features": …}, …] 💡 Nodeで実行されているCRI RuntimeがSupplementalGroupsPolicyを サポートしているのかどうか確認できる 📝 これに付随するCRIの変更もありますが割愛
  14. @everpeace KubernetesにおけるContainerのUser Identities決定の流れ(with KEP-3619) CRI Runtime (containerd, CRI-O, etc.) →

    CRIのSecurityContextを読んでOCI Runtime Specに変換してOCI Runtimeを呼ぶ CRI: LinuxContainerSecurityContext # CRI (protobuf) security_context { run_as_user: 1000, run_as_group: 1000, supplemental_groups: [60000], supplementalGroupsPolicy: "Strict" } OCI Runtime (runc, crun, youki, etc.) → OCI Runtime Specを読んで実際にコンテナを作る OCI Runtime Spec: process.user # OCI runtime spec (json) "user": {"uid": 1000, "gid": 1000, "additionalGids": [50000, 60000]} Kubelet → Specを読んでCRIを呼ぶ(Image ConfigはCRIのImage Service経由で読む) OCI Image Spec: Configuration.User (DockerfileのUSER) PodSecurityContext (ContainerSecurityContext) # Dockerfile USER 1000:1000 # OCI Image Configuration # application/vnd.oci.image.config.v1+json "config": {"User": "1000:1000"} # PodSpec spec: securityContext: runAsUser: 1000 # alice runAsGroup: 1000 # alice supplementalGroups: [60000] supplementalGroupsPolicy: Strict
  15. @everpeace KEP-3619の実装(変更)箇所 k/k kubelet kube-apiserver cri-api • Pod, Node APIの追加

    • CRI APIの追加 • Plumbing • Testの追加(API, e2e tests) • critestへのTestケースの追加 k-sigs/cri-tools critest Container Runtimes CRI-O containerd • 実際のSupplementalGroupsPolicy=Merge|Strictの実装 (/etc/group読む読まない) • Testの追加 📝APIフィールド一個追加するだけ  ですが思ったよりも大変でした 💦
  16. @everpeace KEP-3619のステータス • v1.31でAlpha featureとしてリリース済み🎉 ◦ Kubernetes 1.31: Fine-grained SupplementalGroups

    control • feature gate: SupplementalGroupsPolicy (kube-apiserver, kubelet) • サポートしているCRI Runtime ◦ Containerd: v2 以降 (正確には2.0.0-rc.4以降) ◦ CRI-O: v1.31.0 以降 📝 CRI RuntimeがSupplementalGroupsPolicy未対応な場合、 StrictポリシーはSilentに無効化されるので注意が必要 (Beta昇格時にはErrorになる予定: k/enhancements#4895)
  17. @everpeace v1.31以前でもこの機能を使いたいけど Workaroundはあるのか? → YES OCI Image Spec Configuration.User (DockerfileのUSER)

    PodSecurityContext ContainerSecurityContext Kubelet CRI LinuxContainer SecurityContext CRI Runtime OCI Runtime Spec process.user OCI Runtime Kubelet & CRI Runtimeが Kubernetes & CRIの仕様で OCI Runtime Spec まで変換してしまうので OCI Runtime (RuntimeClass) でなんとか するしかない
  18. @everpeace KEP-3619の今後 • Beta promotionに向けて議論中(k/enhancements#4895) ◦ containerd v2がリリースされてからのほうが良いのでv1.33以降になる予定 ◦ sig-nodeには他にもcontainerd

    v2(CRIの新機能)に依存するKEPがあるが Betaへのgraduation criteriaが標準化されていなくてMLにて横断的に議論中 (containerd v2に依存しているのに既にBetaに昇格してしまったものも存在) • Beta向けのProduction Readiness Review対応が必要 ◦ feature gateのenabled->disabled->enabledみたいな事をした場合の挙動 ◦ componentのupgrade->downgrade->upgradeみたいなことをした場合の挙動 ◦ Rollout時のSLI ◦ Rollback signal(Rollout 失敗をどう検出するか?) ◦ etc.
  19. @everpeace KEPをリリースするためのTips • いきなりKEPじゃなくてまずはIssue/相談が吉 (in Slack or Community Meeting) •

    いろんなKEPを読む(特に関連SIGのは参考になります) • KEP, リリースサイクル, 開発関連のドキュメントはできるかぎり読んでおくとスムーズ ◦ kubernetes.dev(/docs,/resources/release/), kubernetes/communityのdeveloper guide(特にsig-release, sig-architecture), kubernetes/enhancements • リリースサイクルを意識して予定を立てておく ◦ リリースサイクルは4ヶ月だけどCode Freezeは1か月程度前なので正味3ヶ月 ◦ リリースサイクル内にはいくつも締め切りがあるので事前に把握しておきましょう (Production Readiness, Enhancement Freeze, Code/Test Freeze, Doc Freeze, Feature Blog Freeze, etc.) • レビューが遅延しないように工夫する ◦ Reviewer, Approverレベルの味方を見つけておくと吉 ◦ Reviewerの脳内キャッシュからこぼれないようにする(レビューの打ち返しはできるだけ素早く) ◦ Reviewが遅い場合は、Community MeetingでPromoteしたり、Rudeにならない程度にメンションしたり • 作業はとにかくどんどん並行で進める ◦ 依存するPRがあってもよいので明示しつつどんどんPR投げてreviewしてもらったほうが早い ←今回は@thockin ,@haircommander さんがサポートくれました 👍
  20. @everpeace KEP-3619に特有(CRI Runtimeをまたぐ場合)のTips • CRI APIのVersion Skew Policyを把握しておく ◦ k8s.io/cri-apiのバージョンがkubeletのN-3

    minor versionまでをサポート ◦ 新しい機能は古いバージョンのCRI Runtimeでは"must be gracefully degraded" • CRIのFeature Developmentプロセスを把握しておく ◦ k/kのPRではCRI Runtimeは手元でprototype (k8s.io/cri-apiだけ更新してマージすることはしない) ◦ k/kのPRがマージされてrc.N or beta.Nが出たらCRI Runtime側の依存をbumpしてマージ • CRI RuntimeのリリースサイクルはKubernetesと独立であることを把握 ◦ Kubernetesのリリースに合わせてCRI Runtimeも利用可能にしたい場合は特に • cri-tools 等のRepoにも更新いれるの忘れずに(critest, crictl) ◦ CRI Runtime側はcritestを使ってテストしてたりする • CRI Runtime側にもreviewer, approverさん味方をつけておくと吉 ◦ Containerd: @TokunagaKohei さんにとても助けていただきました 👍 ◦ CRI-O: Kubernetes側のreviewerと同じ人( @haircommander さん)が積極的にreviwして くれたのでスムーズでした 👍
  21. @everpeace 手元でKEP-3619を試したい 👉 CRI-O & SupplementalGroupsPolicy=TrueなKindクラスタで ◦ node.status.featureのチェック ◦ SupplementalGroupsPolicy=StrictなPodを作ってidコマンドのチェック

    ◦ pod.status.containerStatuses[].userのチェック をします 📝 kubectl, kind, docker, yqが必要です $ curl -sL https://bit.ly/k8sjp67-try-kep-3619 | bash