This article is more than one year old. Older articles may contain outdated content. Check that the information in the page has not become incorrect since its publication.

Kubernetesにおけるフォレンジックコンテナチェックポイント処理

Authors: Adrian Reber (Red Hat)

フォレンジックコンテナチェックポイント処理はCheckpoint/Restore In Userspace (CRIU)に基づいており、コンテナがチェックポイントされていることを認識することなく、実行中のコンテナのステートフルコピーを作成することができます。 コンテナのコピーは、元のコンテナに気づかれることなく、サンドボックス環境で複数回の分析やリストアが可能です。 フォレンジックコンテナチェックポイント処理はKubernetes v1.25でalpha機能として導入されました。

どのように機能しますか?

CRIUを使用してコンテナのチェックポイントやリストアを行うことが可能です。 CRIUはruncやcrun、CRI-O、containerdと統合されており、Kubernetesで実装されているフォレンジックコンテナチェックポイント処理は、既存のCRIU統合を使用します。

なぜ重要なのか?

CRIUと対応する統合機能を使用することで、後でフォレンジック分析を行うために、ディスク上で実行中のコンテナに関する全ての情報と状態を取得することが可能です。 フォレンジック分析は、疑わしいコンテナを停止したり影響を与えることなく検査するために重要となる場合があります。 コンテナが本当に攻撃を受けている場合、攻撃者はコンテナを検査する処理を検知するかもしれません。 チェックポイントを取得しサンドボックス環境でコンテナを分析することは、元のコンテナや、おそらく攻撃者にも検査を認識されることなく、コンテナを検査することができる可能性があります。

フォレンジックコンテナチェックポイント処理のユースケースに加えて、内部状態を失うことなく、あるノードから他のノードにコンテナを移行することも可能です。 特に初期化時間の長いステートフルコンテナの場合、チェックポイントからリストアすることは再起動後の時間が節約されるか、起動時間がより早くなる可能性があります。

コンテナチェックポイント処理を利用するには?

機能はフィーチャーゲートで制限されているため、新しい機能を使用する前にContainerCheckpointを有効にしてください。

ランタイムがコンテナチェックポイント処理をサポートしている必要もあります。

  • containerd: サポートは現在検討中です。詳細はcontainerdプルリクエスト#6965を見てください。
  • CRI-O: v1.25はフォレンジックコンテナチェックポイント処理をサポートしています。

CRI-Oでの使用例

CRI-Oとの組み合わせでフォレンジックコンテナチェックポイント処理を使用するためには、ランタイムをコマンドラインオプション--enable-criu-support=trueで起動する必要があります。 Kubernetesでは、ContainerCheckpointフィーチャーゲートを有効にしたクラスターを実行する必要があります。 チェックポイント処理の機能はCRIUによって提供されているため、CRIUをインストールすることも必要となります。 通常、runcやcrunはCRIUに依存しているため、自動的にインストールされます。

執筆時点ではチェックポイント機能はCRI-OやKubernetesにおいてalpha機能としてみなされており、セキュリティ影響がまだ検討中であることに言及することも重要です。

コンテナとPodが実行されると、チェックポイントを作成することが可能になります。 チェックポイント処理kubeletレベルでのみ公開されています。 コンテナをチェックポイントするためには、コンテナが実行されているノード上でcurlを実行し、チェックポイントをトリガーします。

curl -X POST "https://localhost:10250/checkpoint/namespace/podId/container"

default名前空間内のcountersと呼ばれるPod内のcounterと呼ばれるコンテナに対し、kubelet APIエンドポイントが次の場所で到達可能です。

curl -X POST "https://localhost:10250/checkpoint/default/counters/counter"

厳密には、kubeletの自己署名証明書を許容し、kubeletチェックポイントAPIの使用を認可するために、下記のcurlコマンドのオプションが必要です。

--insecure --cert /var/run/kubernetes/client-admin.crt --key /var/run/kubernetes/client-admin.key

このkubelet APIが実行されると、CRI-Oからチェックポイントの作成をリクエストします。 CRI-Oは低レベルランタイム(例えばrunc)からチェックポイントをリクエストします。 そのリクエストを確認すると、runcは実際のチェックポイントを行うためにcriuツールを呼び出します。

チェックポイント処理が終了すると、チェックポイントは/var/lib/kubelet/checkpoints/checkpoint-<pod-name>_<namespace-name>-<container-name>-<timestamp>.tarで利用可能になります。

その後、そのtarアーカイブを使用してコンテナを別の場所にリストアできます。

Kubernetesの外部でチェックポイントしたコンテナをリストアする(CRI-Oを使用)

チェックポイントtarアーカイブを使用すると、CRI-Oのサンドボックスインスタンス内のKubernetesの外部にコンテナをリストア可能です。 リストア中のより良いユーザエクスペリエンスのために、main CRI-O GitHubブランチからCRI-Oのlatestバージョンを使用することを推奨します。 CRI-O v1.25を使用している場合、コンテナを開始する前にKubernetesが作成する特定のディレクトリを手動で作成する必要があります。

Kubernetesの外部にコンテナをリストアするための最初のステップは、crictlを使用してPodサンドボックスを作成することです。

crictl runp pod-config.json

次に、さきほどチェックポイントしたコンテナを新しく作成したPodサンドボックスにリストアします。

crictl create <POD_ID> container-config.json pod-config.json

container-config.jsonのレジストリでコンテナイメージを指定する代わりに、前に作成したチェックポイントアーカイブへのパスを指定する必要があります。

{
  "metadata": {
      "name": "counter"
  },
  "image":{
      "image": "/var/lib/kubelet/checkpoints/<checkpoint-archive>.tar"
  }
}

次に、そのコンテナを開始するためにcrictl start <CONTAINER_ID>を実行すると、さきほどチェックポイントしたコンテナのコピーが実行されているはずです。

Kubernetes内でチェックポイントしたコンテナをリストアする

先ほどチェックポイントしたコンテナをKubernetes内で直接リストアするためには、レジストリにプッシュできるイメージにチェックポイントアーカイブを変換する必要があります。

ローカルのチェックポイントアーカイブを変換するための方法として、buildahを使用した下記のステップが考えられます。

newcontainer=$(buildah from scratch)
buildah add $newcontainer /var/lib/kubelet/checkpoints/checkpoint-<pod-name>_<namespace-name>-<container-name>-<timestamp>.tar /
buildah config --annotation=io.kubernetes.cri-o.annotations.checkpoint.name=<container-name> $newcontainer
buildah commit $newcontainer checkpoint-image:latest
buildah rm $newcontainer

出来上がったイメージは標準化されておらず、CRI-Oとの組み合わせでのみ動作します。 このイメージはalphaにも満たないフォーマットであると考えてください。 このようなチェックポイントイメージのフォーマットを標準化するための議論が進行中です。 これはまだ標準化されたイメージフォーマットではなく、CRI-Oを--enable-criu-support=trueで起動した場合のみ動作することを忘れないでください。 CRIUサポートでCRI-Oを起動することのセキュリティ影響はまだ明確ではなく、そのため、イメージフォーマットだけでなく機能も気を付けて使用するべきです。

さて、そのイメージをコンテナイメージレジストリにプッシュする必要があります。 例えば以下のような感じです。

buildah push localhost/checkpoint-image:latest container-image-registry.example/user/checkpoint-image:latest

このチェックポイントイメージ(container-image-registry.example/user/checkpoint-image:latest)をリストアするために、イメージはPodの仕様(Specification)に記載する必要があります。 以下はマニフェストの例です。

apiVersion: v1
kind: Pod
metadata:
  namePrefix: example-
spec:
  containers:
  - name: <container-name>
    image: container-image-registry.example/user/checkpoint-image:latest
  nodeName: <destination-node>

Kubernetesは新しいPodをノード上にスケジュールします。 そのノード上のKubeletは、registry/user/checkpoint-image:latestとして指定されたイメージをもとに、コンテナを作成し開始するようにコンテナランタイム(この例ではCRI-O)に指示をします。 CRI-Oはregistry/user/checkpoint-image:latestがコンテナイメージでなく、チェックポイントデータへの参照であることを検知します。 その時、コンテナを作成し開始する通常のステップの代わりに、CRI-Oはチェックポイントデータをフェッチし、指定されたチェックポイントからコンテナをリストアします。

Pod内のアプリケーションはチェックポイントを取得しなかったかのように実行し続けます。 コンテナ内では、アプリケーションはチェックポイントからリストアされず通常起動したコンテナのような見た目や動作をします。

これらのステップで、あるノードで動作しているPodを、別のノードで動作している新しい同等のPodに置き換えることができ、そのPod内のコンテナの状態を失うことはないです。

どのように参加すればよいですか?

SIG Nodeにはいくつかの手段でアクセスすることができます。

さらなる読み物

コンテナチェックポイントの分析方法に関する詳細は後続のブログForensic container analysisを参照してください。