checkpoint-restore/checkpointctl

GitHub: checkpoint-restore/checkpointctl

checkpointctl 是一个用于深入分析和比较 Podman、CRI-O 及 Kubernetes 容器检查点归档文件的命令行工具。

Stars: 152 | Forks: 38

checkpointctl logo

# checkpointctl - 用于深入分析容器检查点的工具 [![运行测试](https://static.pigsec.cn/wp-content/uploads/repos/cas/96/961ecbbbf6b9c6da2ef90dc16b38cd41fe9f3dde0881cf199a780512e101a580.svg)](https://github.com/checkpoint-restore/checkpointctl/actions/workflows/tests.yml) [![codecov](https://codecov.io/gh/checkpoint-restore/checkpointctl/graph/badge.svg?token=P1SXZ8HZ32)](https://codecov.io/gh/checkpoint-restore/checkpointctl) 诸如 *Podman* 和 *CRI-O* 之类的容器引擎具备检查点(checkpoint)容器的功能。与检查点相关的所有数据都会收集在检查点归档文件中。借助 `checkpointctl` 这个工具,可以展示有关这些检查点归档文件的信息。 有关如何借助 [CRIU][criu] 创建检查点的详细信息,请访问: * [Kubernetes 中的取证容器检查点][forensic] * [Podman 检查点][podman] ## 用法 ### `show` 子命令 要展示检查点归档文件的概览,只需使用 `checkpointctl show`: ``` $ checkpointctl show /tmp/dump.tar Displaying container checkpoint data from /tmp/dump.tar CONTAINER IMAGE ID RUNTIME CREATED ENGINE CHKPT SIZE ROOT FS DIFF SIZE --------- ----- -- ------- ------- ------ ---------- ----------------- looper docker.io/library/busybox:latest 8b5c2ca15082 crun 2021-09-28T10:03:56Z Podman 130.8 KiB 204 B ``` 对于由 Kubernetes 使用 *CRI-O* 创建的检查点归档文件,输出结果如下所示: ``` $ checkpointctl show /var/lib/kubelet/checkpoints/checkpoint-checkpoint-test_default-checkpoint-test-2026-03-13T21:43:06Z.tar Displaying container checkpoint data from /var/lib/kubelet/checkpoints/checkpoint-checkpoint-test_default-checkpoint-test-2026-03-13T21:43:06Z.tar CONTAINER IMAGE ID RUNTIME CREATED ENGINE CHKPT SIZE ROOT FS DIFF SIZE --------- ----- -- ------- ------- ------ ---------- ----------------- checkpoint-test docker.io/library/busybox:latest 68bb2fa6a176 crun 2026-03-13T21:42:59.05135916Z CRI-O 314.6 KiB 3.5 KiB ``` ### `inspect` 子命令 要检索有关容器检查点的底层信息,请使用 `checkpointctl inspect` 命令: ``` Displaying container checkpoint tree view from /tmp/ubuntu_looper.tar.gz awesome_booth ├── Image: docker.io/library/ubuntu:latest ├── ID: 2076a99c49c959375c6ae406818a2c841744a019d30221f134db657232f133f5 ├── Runtime: crun ├── Created: 2026-03-16T08:00:17Z ├── Engine: Podman ├── Network Interfaces │ └── podman (eth0) │ ├── IP: 10.88.0.4/16 │ ├── MAC: 2e:e1:db:6b:68:93 │ └── Gateway: 10.88.0.1 ├── Checkpoint size: 1018.3 KiB │ └── Memory pages size: 952.0 KiB ├── Root FS diff size: 2.0 KiB └── Process tree └── [1] bash └── [4] su └── [5] bash └── [6] sleep ``` 有关支持的功能标志(flag)的完整列表,请使用 `checkpointctl inspect --help`。 ### `diff` 子命令 要比较两个容器检查点并分析它们之间的变化,请使用 checkpointctl `diff` 命令。 ``` $ checkpointctl diff --show-unchanged --ps-tree-cmd --sockets ./cp1.tar cp2.tar ╔════════════════════════════════════════════════════════════════╗ ║ Checkpoint Diff ║ ╠════════════════════════════════════════════════════════════════╣ ║ Container: optimistic_mclean ║ ║ Image: docker.io/library/python:alpine ║ ║ ID: 6ebe04dda2005962a8fb4e971caedfda5e03e22c882e2334... ║ ╚════════════════════════════════════════════════════════════════╝ Checkpoint A: Created: 2026-04-18T10:15:41+01:00 Size: 15186908 bytes Checkpoint B: Created: 2026-04-18T10:15:41+01:00 Size: 15212641 bytes ┌─ Memory Changes ─────────────────────────────────────────────┐ │ ↑ Increased by 0.02 MB └──────────────────────────────────────────────────────────────┘ ┌─ Process Tree ───────────────────────────────────────────────┐ │ Process tree │ └── [= PID 1] sleep infinity │ └── [= PID 2] python3 -m http.server 8080 └──────────────────────────────────────────────────────────────┘ ┌─ Socket Changes ──────────────────────────────────────────────────────┐ │ PID PROTO STATE LOCAL PEER │ + 2 TCP ESTABLISHED 10.88.0.3:8080 10.88.0.1:39838 │ = 2 TCP LISTEN 0.0.0.0:8080 - └───────────────────────────────────────────────────────────────────────┘ Summary: Checkpoint comparison for container optimistic_mclean Sockets: +1 -0 Memory: +0.02 MB ``` ### `memparse` 子命令 要对容器检查点进行内存分析,你可以使用 `checkpointctl memparse` 命令。 ``` $ checkpointctl memparse /tmp/jira.tar.gz --pid=1 | less Displaying memory pages content for Process ID 1 from checkpoint: /tmp/jira.tar.gz ADDRESS HEXADECIMAL ASCII ------------------------------------------------------------------------------------- 00005633bb080000 f3 0f 1e fa 48 83 ec 08 48 8b 05 d1 4f 00 00 48 |....H...H...O..H| 00005633bb080010 85 c0 74 02 ff d0 48 83 c4 08 c3 00 00 00 00 00 |..t...H.........| 00005633bb080020 ff 35 b2 4e 00 00 f2 ff 25 b3 4e 00 00 0f 1f 00 |.5.N....%.N.....| 00005633bb080030 f3 0f 1e fa 68 00 00 00 00 f2 e9 e1 ff ff ff 90 |....h...........| * 00005633bb0800a0 f3 0f 1e fa 68 07 00 00 00 f2 e9 71 ff ff ff 90 |....h......q....| 00005633bb0800b0 f3 0f 1e fa 68 08 00 00 00 f2 e9 61 ff ff ff 90 |....h......a....| 00005633bb0800c0 f3 0f 1e fa 68 09 00 00 00 f2 e9 51 ff ff ff 90 |....h......Q....| 00005633bb0800d0 f3 0f 1e fa 68 0a 00 00 00 f2 e9 41 ff ff ff 90 |....h......A....| 00005633bb0800e0 f3 0f 1e fa 68 0b 00 00 00 f2 e9 31 ff ff ff 90 |....h......1....| ``` 这是一个针对 PostgreSQL 容器进行内存分析的示例。在本例中,我们启动了一个密码设置为 'mysecret' 的 PostgreSQL 容器。然后,我们为该容器创建一个检查点,并使用 `memparse` 来查找存储的密码。 ``` $ sudo podman run --name postgres -e POSTGRES_PASSWORD=mysecret -d postgres $ sudo podman container checkpoint -l --export=/tmp/postgres.tar.gz $ sudo checkpointctl memparse --pid 1 /tmp/postgres.tar.gz | grep -B 1 -A 1 mysecret 000055dd725c1e60 50 4f 53 54 47 52 45 53 5f 50 41 53 53 57 4f 52 |POSTGRES_PASSWOR| 000055dd725c1e70 44 3d 6d 79 73 65 63 72 65 74 00 00 00 00 00 00 |D=mysecret......| 000055dd725c1e80 00 00 00 00 00 00 00 00 31 00 00 00 00 00 00 00 |........1.......| ``` 这是另一个针对 Web 应用容器的内存分析场景。我们启动了一个存在漏洞的 Web 应用容器,执行了任意代码执行攻击,在不停止容器运行的情况下创建了一个用于取证分析的检查点,最后分析该检查点的内存以识别注入的代码。 ``` # 启动易受攻击的 web 应用程序 $ sudo podman run --name dsvw -p 1234:8000 -d quay.io/rst0git/dsvw # 执行任意代码执行攻击:$(echo secret) $ curl "http://localhost:1234/?domain=www.google.com%3B%20echo%20secret" nslookup: can't resolve '(null)': Name does not resolve Name: www.google.com Address 1: 142.250.187.228 lhr25s34-in-f4.1e100.net Address 2: 2a00:1450:4009:820::2004 lhr25s34-in-x04.1e100.net secret # 创建用于取证分析的 checkpoint 并保持容器运行 $ sudo podman container checkpoint --leave-running -l -e /tmp/dsvw.tar # 分析 checkpoint 内存以识别攻击者注入的代码 $ sudo checkpointctl memparse --pid 1 /tmp/dsvw.tar | grep 'echo secret' 00007faac5711f60 6f 6d 3b 20 65 63 68 6f 20 73 65 63 72 65 74 00 |om; echo secret.| ``` 对于较大的进程,建议将进程内存页的内容写入文件,而不是直接输出到标准输出。 要概览检查点内的进程内存大小,请在不带任何参数的情况下运行 `checkpointctl memparse`。 ``` $ sudo checkpointctl memparse /tmp/jira.tar.gz Displaying processes memory sizes from /tmp/jira.tar.gz PID PROCESS NAME MEMORY SIZE SHARED MEMORY SIZE --- ------------ ----------- ------------------ 1 tini 100.0 KiB 0 B 2 java 553.5 MiB 0 B ``` 在本示例中,鉴于 java 进程体积较大,最好将其输出写入文件中。 ``` $ sudo checkpointctl memparse --pid=2 /tmp/jira.tar.gz --output=/tmp/java-memory-pages.txt Writing memory pages content for process ID 2 from checkpoint: /tmp/jira.tar.gz to file: /tmp/java-memory-pages.txt... ``` 请注意,将大量内存页写入文件可能需要几分钟的时间。 ### `build` 子命令 在 Kubernetes 中从检查点恢复容器,需要将检查点归档文件转换为 OCI 镜像。 `build` 命令通过从检查点中提取必要的元数据,并创建一个带有适当注解的兼容 OCI 的镜像,从而简化了这一过程。 示例: ``` checkpointctl build ./checkpoint.tar quay.io/foo/bar:latest buildah push quay.io/foo/bar:latest ``` 在这个示例中,`checkpointctl build` 命令将 `checkpoint.tar` 归档文件转换为了 兼容 OCI 的镜像,并将其标记为 `quay.io/foo/bar:latest`。随后的 `buildah push` 命令 会将新创建的 OCI 镜像上传到容器镜像仓库,从而使其可用于部署。 ### `plugin` 子命令 `plugin` 子命令用于管理通过额外子命令扩展 checkpointctl 功能的外部插件。插件是存放在 PATH 中的独立可执行文件,其名称需匹配 `checkpointctl-` 的格式。 ``` $ checkpointctl plugin list Available plugins: hello My custom plugin description /usr/local/bin/checkpointctl-hello ``` 这种插件架构允许在不增加核心工具二进制文件大小的情况下扩展 checkpointctl 的功能。包含较大依赖项的插件 可以单独分发,并且可以使用任何编程语言来实现。 #### 创建插件 要创建一个插件,请创建一个名为 `checkpointctl-` 的可执行文件: ``` #!/bin/sh # 支持 --plugin-description 帮助文本(必须以 code 42 退出) if [ "$1" = "--plugin-description" ]; then echo "Say hello" exit 42 fi echo "Hello, world!" ``` 将其保存为 `checkpointctl-hello` 并存放在你 PATH 路径下的目录中,然后赋予其可执行权限。随后,它就可以作为 `checkpointctl hello` 来使用了。 注意:必须使用退出代码 42 来指示该插件支持 `--plugin-description` 标志。任何其他的退出代码都将导致 系统使用默认描述。 ## 从源代码安装 1. 克隆仓库。 git clone https://github.com/checkpoint-restore/checkpointctl.git 2. 安装依赖项。 在 Fedora、CentOS 及相关发行版上: sudo yum install -y make asciidoctor 在 Debian、Ubuntu 及相关发行版上: sudo apt-get install -y make asciidoctor Linux 发行版通常提供旧版本的 golang 编译器。我们建议从[官方压缩包](https://go.dev/doc/install)安装 Go。 3. 构建。 make 4. checkpointctl 编译完成后,只需键入以下命令即可将其安装到系统中 sudo make install 此命令接受以下变量: * **DESTDIR**,用于指定所有组件将要放置在其下的全局根目录(默认为空); * **PREFIX**,用于指定每个已安装组件路径的额外前缀(默认为 `/usr/local`); * **BINDIR**,用于指定 checkpointctl 工具的安装位置(默认为 `$(PREFIX)/bin`); 因此,要将所有内容安装在 `/some/new/place` 下,请使用以下命令: make DESTDIR=/some/new/place install ## 启用自动补全 现在你需要确保在所有的 shell 会话中都能加载自动补全脚本。 你可以通过以下两种方式来实现: ### 用户级别 ``` echo 'source <(checkpointctl completion bash)' >>~/.bashrc ``` ### 系统级别 ``` checkpointctl completion bash | sudo tee /etc/bash_completion.d/checkpointctl > /dev/null sudo chmod a+r /etc/bash_completion.d/checkpointctl ``` 这两种方法是等效的。在重新加载你的 shell 之后,自动补全应该就可以工作了。 要在当前的 shell 会话中启用 bash 自动补全,请加载 `~/.bashrc` 文件: ``` source ~/.bashrc ``` ## 卸载 可以使用以下命令来清理先前安装的 checkpointctl 实例。 ``` make uninstall ``` 请注意,如果在安装过程中使用了某些变量(例如 **DESTDIR**、**BINDIR**), 在执行卸载操作时*必须*传入相同的变量。 ## 许可证与版权 除非在特定文件的头部另有说明,否则本项目的所有代码均 遵循 Apache 2.0 许可证发布。 变更的作者仍保留其代码的版权 (无需转让版权)。作者和贡献者列表可以 从 git 提交历史中获取,在某些情况下,也可以从文件头部获取。
标签:CRIU, EVTX分析, 分析工具, 子域名突变, 容器, 数字取证, 日志审计, 系统运维, 自动化脚本