【Kubernetes v1.36】User NamespacesがついにGA:コンテナroot権限のリスクを根本から解消する設定術

Kubernetes

この記事の概要

Kubernetes v1.36(コードネーム “Haru”、2026年4月22日リリース)で、User Namespaces機能がついにStable(GA)になりました。
spec.hostUsers: false の一行を追加するだけで、コンテナ内のrootユーザーをホスト上の非特権ユーザーにマッピングでき、コンテナ脱獄(container breakout)時のダメージを劇的に抑えられます。
本記事では仕組みの解説から実際の設定手順まで、インフラエンジニア視点で解説します。

コンテナの「root」はホストのrootと同じじゃない——はずだった

「Kubernetesのコンテナ内でrootで動かすのは危ない」——これはよく言われるセキュリティベストプラクティスです。しかし実態はどうでしょうか。

従来のKubernetesでは、コンテナ内のUID 0(root)はホストOSのUID 0と 同じユーザー として扱われていました。
つまり、コンテナ脱獄(runCやoverlayfsの脆弱性による脱出)が発生した瞬間、攻撃者はホスト上で root権限を取得 できてしまいます。

有名どころだけでも、以下のCVEがこのリスクを突いてきました:

  • CVE-2019-5736(runC container escape)
  • CVE-2021-25741(symlink exchange attack)
  • CVE-2022-0492(cgroup namespace escape)

これらはすべて「コンテナ内rootがホストrootと同一」という前提に依拠した攻撃です。
User Namespacesはこの前提そのものを崩します。

なぜ従来の対策では不十分だったのか

securityContext.runAsNonRoot: true を設定すればいいんじゃないの?」と思われるかもしれません。
確かにこれはコンテナ内でrootとして動作することを禁止しますが、いくつかの問題があります。

まず、既存アプリケーションの多くはroot起動を前提としている点です。
nginxやPostgreSQLなど、古典的なソフトウェアはroot起動→権限降格のパターンを採用しており、runAsNonRoot を強制すると設定変更や再ビルドが必要になります。

また、runAsNonRoot はあくまでもプロセスの動作UID制限です。
もし脆弱性を突かれてUID 0相当の権限を奪われた場合、ホストへの影響を防ぐ手段がありません。

User Namespacesはレイヤーが違います。
カーネルのnamespace機能を使い、コンテナ内のUID/GIDとホスト上のUID/GIDを完全に分離します。
コンテナ内のUID 0(root)は、ホスト上ではUID 100000のような非特権ユーザーにしか見えません。
コンテナ脱獄に成功しても、攻撃者はホスト上で何もできない状態になります。

コンテナ内                      ホスト上
UID 0 (root)      ──マッピング──>  UID 100000 (非特権)
UID 1 (daemon)    ──マッピング──>  UID 100001 (非特権)
UID 65535         ──マッピング──>  UID 165535 (非特権)

これが「根本解決」と表現される理由です。

実際にUser Namespacesを有効化してみる

前提条件の確認

User Namespacesを使うには、以下の環境が必要です。

コンポーネント 要件
Kubernetes v1.36以降(GAとして安定稼働)
Linux Kernel 6.3以上(idmap mounts対応)
containerd 2.0以上(推奨)/ 1.7は制限あり
CRI-O 1.30以上
ファイルシステム /var/lib/kubelet/pods/ がidmap mounts対応

EKSを使っている場合、2026年5月時点ではAmazon Linux 2023(AL2023)のカーネルが6.1系なため、まだKernel 6.3要件を満たしていない可能性があります。
利用前にノードのカーネルバージョンを必ず確認してください。

# ノードのカーネルバージョン確認
kubectl get nodes -o custom-columns='NAME:.metadata.name,KERNEL:.status.nodeInfo.kernelVersion'

# 出力例
NAME KERNEL
ip-10-0-1-100 6.1.131-133.201.amzn2023.x86_64 # ← 6.3未満のため非対応
ip-10-0-1-200 6.6.87-1.2.amzn2023.x86_64 # ← 対応

GKEのCOS(Container-Optimized OS)ノードは比較的新しいカーネルを採用しており、対応済みノードが多い状況です。

実装例:PodへのUser Namespaces適用

設定は非常にシンプルです。Podスペックに hostUsers: false を追加するだけです。

apiVersion: v1
kind: Pod
metadata:
  name: secure-nginx
  labels:
    app: nginx
spec:
  hostUsers: false   # ← User Namespacesを有効化
  containers:
    - name: nginx
      image: nginx:1.27
      ports:
        - containerPort: 80
      securityContext:
        readOnlyRootFilesystem: true

Deploymentに適用する場合は spec.template.spec に追加します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: secure-app
  template:
    metadata:
      labels:
        app: secure-app
    spec:
      hostUsers: false   # ← ここに追加
      containers:
        - name: app
          image: myapp:latest
          ports:
            - containerPort: 8080

動作確認:コンテナ内外でのUID確認

# コンテナ内でのUID確認
kubectl exec -it secure-nginx -- id
# uid=0(root) gid=0(root) groups=0(root)   ← コンテナ内ではrootに見える

# ホストノードにSSHして確認
# (コンテナ内rootがホスト上でどのUIDに見えるか)
cat /proc/$(pidof nginx)/status | grep NStgid
# NStgid: 100000 ← ホスト上ではUID 100000(非特権)

制限事項(同時使用不可の設定)

# ❌ User Namespaces有効時に使えない設定
spec:
  hostUsers: false
  hostNetwork: true     # 使用不可
  hostPID: true         # 使用不可
  hostIPC: true         # 使用不可
  containers:
    - name: app
      volumeDevices:    # raw block volumes 使用不可
        - name: data
          devicePath: /dev/sda

KyvernoでCluster全体に強制適用する

個別Podに設定するのが現実的でない場合、KyvernoのMutating Policyを使ってCluster全体に自動適用できます。

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-user-namespaces
spec:
  rules:
    - name: set-host-users-false
      match:
        any:
          - resources:
              kinds:
                - Pod
      mutate:
        patchStrategicMerge:
          spec:
            hostUsers: false

ただし、hostNetwork: true を使うPod(DaemonSetなど)は事前に除外設定を入れておく必要があります。

実務での活用シナリオ

外部向けWebアプリ・マルチテナントクラスタでとくに効果的です。
外部公開アプリは脆弱性が見つかるリスクが高く、万が一のコンテナ脱獄時もホストへのダメージを最小化できます。
複数チームが同一クラスタを使うケースでは、あるチームの侵害が他チームのノードに波及するリスクを大幅に低減します。
CI/CDのビルドコンテナも権限が広がりやすいため、分離することでセキュリティを強化できます。

コンテナセキュリティのあり方が変わる——まずはステージングから試そう

Kubernetes v1.36でUser NamespacesがGAになったことで、今後はこの設定がセキュリティのデファクトになっていくことが予想されます。
CIS Kubernetes Benchmarkや各社のセキュリティガイドラインにも近い将来組み込まれるでしょう。

ただし、すぐに本番環境全体に展開するのは禁物です。
まずは以下のステップで段階的に導入することをおすすめします。

Step 1:ノードのカーネルバージョンとcontainerdバージョンを確認する
Step 2:開発・ステージング環境の低リスクなPodから hostUsers: false を試す
Step 3:動作確認後、Kyverno等でClusterポリシーとして展開する

「コンテナ内rootはホストrootではない」——これが当たり前になる時代がやってきました。User Namespacesはその第一歩です。

まとめ

Kubernetes v1.36でGAになったUser Namespacesは、コンテナセキュリティの根本的な改善をもたらします。主なポイントをまとめます。

  • コンテナ内のUID 0がホスト上の非特権UID(100000番台等)にマッピングされる
  • コンテナ脱獄(CVE-2019-5736等)が発生しても、ホストへの影響がない
  • spec.hostUsers: false の一行で有効化でき、設定変更は最小限
  • 前提条件としてLinux Kernel 6.3以上、containerd 2.0以上が必要
  • hostNetworkhostPIDhostIPC との同時使用は不可

既存ワークロードへの影響調査を兼ねて、まずステージング環境で試してみてください。

参考リンク

コメント