检测当前是否在容器中运行 — 源码 sandbox.rs:108-153 为什么要检测容器?因为: 1. 如果已经在 Docker 里,再用 unshare 做命名空间隔离可能不支持 2. 容器里的权限模型不同,某些操作需要特殊处理 3. 需要告知用户当前的安全环境 检测方法有 5 种,不依赖任何单一信号——纵深检测:
()
| 151 | |
| 152 | |
| 153 | def detect_container_environment() -> ContainerEnvironment: |
| 154 | """检测当前是否在容器中运行 — 源码 sandbox.rs:108-153 |
| 155 | |
| 156 | 为什么要检测容器?因为: |
| 157 | 1. 如果已经在 Docker 里,再用 unshare 做命名空间隔离可能不支持 |
| 158 | 2. 容器里的权限模型不同,某些操作需要特殊处理 |
| 159 | 3. 需要告知用户当前的安全环境 |
| 160 | |
| 161 | 检测方法有 5 种,不依赖任何单一信号——纵深检测: |
| 162 | """ |
| 163 | markers = [] |
| 164 | |
| 165 | # 信号 1: /.dockerenv 文件存在 |
| 166 | # Docker 会在容器根目录创建这个空文件 |
| 167 | if os.path.exists("/.dockerenv"): |
| 168 | markers.append("/.dockerenv") |
| 169 | |
| 170 | # 信号 2: /run/.containerenv 文件存在 |
| 171 | # Podman 使用这个标记 |
| 172 | if os.path.exists("/run/.containerenv"): |
| 173 | markers.append("/run/.containerenv") |
| 174 | |
| 175 | # 信号 3: 环境变量 |
| 176 | # 检查 CONTAINER, DOCKER, PODMAN, KUBERNETES_SERVICE_HOST |
| 177 | for key, value in os.environ.items(): |
| 178 | normalized = key.lower() |
| 179 | if normalized in ("container", "docker", "podman", "kubernetes_service_host"): |
| 180 | if value: # 非空 |
| 181 | markers.append(f"env:{key}={value}") |
| 182 | |
| 183 | # 信号 4: /proc/1/cgroup 内容 |
| 184 | # 在容器中,PID 1 的 cgroup 路径会包含容器运行时的标识 |
| 185 | try: |
| 186 | with open("/proc/1/cgroup", "r") as f: |
| 187 | cgroup_content = f.read() |
| 188 | for needle in ["docker", "containerd", "kubepods", "podman", "libpod"]: |
| 189 | if needle in cgroup_content: |
| 190 | markers.append(f"/proc/1/cgroup:{needle}") |
| 191 | except (FileNotFoundError, PermissionError): |
| 192 | pass # macOS 和 Windows 没有 /proc |
| 193 | |
| 194 | # 去重 + 排序(确保确定性) |
| 195 | markers = sorted(set(markers)) |
| 196 | |
| 197 | return ContainerEnvironment( |
| 198 | in_container=len(markers) > 0, |
| 199 | markers=markers, |
| 200 | ) |
| 201 | |
| 202 | |
| 203 | def command_exists(command: str) -> bool: |
no test coverage detected