From eb228e79dbe41e87ec900b2dbf47bcb2a59e2e8f Mon Sep 17 00:00:00 2001 From: Fendy Date: Thu, 23 Jul 2026 15:19:56 +0800 Subject: [PATCH] refactor --- Makefile | 19 ++ README.md | 134 ++++++++ container-from-scratch.c | 597 ------------------------------------ include/capabilities.h | 6 + include/cgroup.h | 9 + include/container.h | 41 +++ include/container_seccomp.h | 6 + include/hostname.h | 8 + include/mount.h | 8 + include/userns.h | 9 + src/capabilities.c | 52 ++++ src/capabilities.o | Bin 0 -> 2880 bytes src/cgroup.c | 142 +++++++++ src/cgroup.o | Bin 0 -> 9664 bytes src/hostname.c | 32 ++ src/hostname.o | Bin 0 -> 3744 bytes src/main.c | 149 +++++++++ src/main.o | Bin 0 -> 6704 bytes src/mount.c | 89 ++++++ src/mount.o | Bin 0 -> 6480 bytes src/seccomp.c | 50 +++ src/seccomp.o | Bin 0 -> 4016 bytes src/userns.c | 68 ++++ src/userns.o | Bin 0 -> 4680 bytes test-container.sh | 52 ++++ 25 files changed, 874 insertions(+), 597 deletions(-) create mode 100644 Makefile create mode 100644 README.md delete mode 100644 container-from-scratch.c create mode 100644 include/capabilities.h create mode 100644 include/cgroup.h create mode 100644 include/container.h create mode 100644 include/container_seccomp.h create mode 100644 include/hostname.h create mode 100644 include/mount.h create mode 100644 include/userns.h create mode 100644 src/capabilities.c create mode 100644 src/capabilities.o create mode 100644 src/cgroup.c create mode 100644 src/cgroup.o create mode 100644 src/hostname.c create mode 100644 src/hostname.o create mode 100644 src/main.c create mode 100644 src/main.o create mode 100644 src/mount.c create mode 100644 src/mount.o create mode 100644 src/seccomp.c create mode 100644 src/seccomp.o create mode 100644 src/userns.c create mode 100644 src/userns.o create mode 100644 test-container.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cbf519a --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +CC = gcc +CFLAGS = -Wall -Werror -Iinclude +LDFLAGS = -lseccomp -lcap + +SRCS = src/main.c src/mount.c src/cgroup.c src/capabilities.c \ + src/seccomp.c src/userns.c src/hostname.c +OBJS = $(SRCS:.c=.o) +TARGET = container-from-scratch + +$(TARGET): $(OBJS) + $(CC) $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS) + +src/%.o: src/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +clean: + rm -f $(OBJS) $(TARGET) + +.PHONY: clean diff --git a/README.md b/README.md new file mode 100644 index 0000000..83ec5eb --- /dev/null +++ b/README.md @@ -0,0 +1,134 @@ +# Container From Scratch + +用纯 C 从零实现一个简易容器运行时,演示 Linux 容器核心原理。 + +## 功能 + +- **Namespace 隔离** — PID、UTS、NET、Mount、IPC、Cgroup、User +- **Cgroup 资源限制** — 内存 1GB、CPU shares、最大 64 进程 +- **Capability 权限收窄** — drop 20 个危险 capability +- **Seccomp 系统调用过滤** — 禁止 ptrace、keyctl 等危险 syscall + +## 依赖 + +```bash +# Alibaba Cloud Linux / RHEL +dnf install -y libseccomp-devel libcap-devel + +# Ubuntu / Debian +apt install -y libseccomp-dev libcap-dev +``` + +## 编译 + +```bash +make +``` + +清理: + +```bash +make clean +``` + +## 用法 + +``` +./container-from-scratch -u -m -c <命令> +``` + +| 参数 | 含义 | +|------|------| +| `-m` | 容器根文件系统目录(rootfs) | +| `-u` | 容器内进程的 UID | +| `-c` | 要执行的命令(必须放最后) | + +## 快速开始 + +### 1. 创建 rootfs + +```bash +mkdir -p rootfs +dnf install -y --installroot=$(pwd)/rootfs --releasever=3 bash coreutils procps-ng iproute hostname +``` + +### 2. 准备 /dev/null + +```bash +rm -f rootfs/dev/null +mknod -m 666 rootfs/dev/null c 1 3 +``` + +### 3. 运行容器 + +```bash +# 交互式 shell +./container-from-scratch -u 0 -m ./rootfs -c /bin/sh + +# 运行测试脚本 +./container-from-scratch -u 0 -m ./rootfs -c /test-container.sh +``` + +## 项目结构 + +``` +container-from-scratch/ +├── include/ +│ ├── container.h # 核心定义:child_config 结构体、系统头文件、常量 +│ ├── mount.h # mounts() — 文件系统隔离 +│ ├── cgroup.h # resources()、free_resources() — 资源限制 +│ ├── capabilities.h # capabilities() — 权限收窄 +│ ├── container_seccomp.h # syscalls() — 系统调用过滤 +│ ├── userns.h # userns()、handle_child_uid_map() — 用户命名空间 +│ └── hostname.h # choose_hostname() — 随机主机名 +├── src/ +│ ├── main.c # 主流程:参数解析、clone、waitpid +│ ├── mount.c # pivot_root、挂载 /proc、/dev +│ ├── cgroup.c # cgroup 目录创建与清理 +│ ├── capabilities.c # prctl + cap_set_flag drop 权限 +│ ├── seccomp.c # seccomp 规则定义与加载 +│ ├── userns.c # user namespace + UID/GID 映射 +│ └── hostname.c # 塔罗牌随机名生成 +├── rootfs/ # 容器根文件系统 +├── test-container.sh # 容器环境测试脚本 +├── Makefile +└── README.md +``` + +## 工作原理 + +``` +main() + ├── 解析参数(-m、-u、-c) + ├── 检查内核版本(>= 4.7) + ├── choose_hostname() → 生成随机主机名 + ├── resources() → 创建 cgroup,写入资源限制 + ├── clone() → 创建子进程(6 个 namespace flag) + │ + ├── [父进程] + │ ├── handle_child_uid_map() → 写入 uid_map/gid_map + │ └── waitpid() → 等待子进程退出 + │ + └── [子进程 — child()] + ├── sethostname() → 设置容器主机名(UTS namespace) + ├── mounts() → pivot_root + 挂载 /proc、/dev + ├── userns() → 创建 user namespace + 切换 UID + ├── capabilities() → drop 危险 capability + ├── syscalls() → 加载 seccomp 过滤器 + └── execve() → 执行目标命令 +``` + +## 容器核心机制 + +| 机制 | 作用 | 类比 | +|------|------|------| +| **Namespace** | 隔离视野(进程、网络、文件系统...) | 每个房间有独立的门牌号和家具 | +| **Cgroup** | 限制资源(内存、CPU、进程数) | 给每个房间限电限水 | +| **Capabilities** | 细分 root 权限 | 老板权限拆成"能开保险柜"、"能进机房" | +| **Seccomp** | 禁止危险系统调用 | 规定能用螺丝刀,不能用电钻 | + +四者叠加 = 容器隔离。 + +## License + +GPLv3 diff --git a/container-from-scratch.c b/container-from-scratch.c deleted file mode 100644 index 54a5362..0000000 --- a/container-from-scratch.c +++ /dev/null @@ -1,597 +0,0 @@ -/* -*- compile-command: "gcc -Wall -Werror -lcap -lseccomp contained.c -o contained" -*- */ -/* This code is licensed under the GPLv3. You can find its text here: - https://www.gnu.org/licenses/gpl-3.0.en.html */ - - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct child_config { - int argc; - uid_t uid; - int fd; - char *hostname; - char **argv; - char *mount_dir; -}; - -int capabilities() -{ - fprintf(stderr, "=> dropping capabilities..."); - int drop_caps[] = { - CAP_AUDIT_CONTROL, - CAP_AUDIT_READ, - CAP_AUDIT_WRITE, - CAP_BLOCK_SUSPEND, - CAP_DAC_READ_SEARCH, - CAP_FSETID, - CAP_IPC_LOCK, - CAP_MAC_ADMIN, - CAP_MAC_OVERRIDE, - CAP_MKNOD, - CAP_SETFCAP, - CAP_SYSLOG, - CAP_SYS_ADMIN, - CAP_SYS_BOOT, - CAP_SYS_MODULE, - CAP_SYS_NICE, - CAP_SYS_RAWIO, - CAP_SYS_RESOURCE, - CAP_SYS_TIME, - CAP_WAKE_ALARM - }; - size_t num_caps = sizeof(drop_caps) / sizeof(*drop_caps); - fprintf(stderr, "bounding..."); - for (size_t i = 0; i < num_caps; i++) { - if (prctl(PR_CAPBSET_DROP, drop_caps[i], 0, 0, 0)) { - fprintf(stderr, "prctl failed: %m\n"); - return 1; - } - } - fprintf(stderr, "inheritable..."); - cap_t caps = NULL; - if (!(caps = cap_get_proc()) - || cap_set_flag(caps, CAP_INHERITABLE, num_caps, drop_caps, CAP_CLEAR) - || cap_set_proc(caps)) { - fprintf(stderr, "failed: %m\n"); - if (caps) cap_free(caps); - return 1; - } - cap_free(caps); - fprintf(stderr, "done.\n"); - return 0; -} - -int pivot_root(const char *new_root, const char *put_old) -{ - return syscall(SYS_pivot_root, new_root, put_old); -} - -int mounts(struct child_config *config) -{ - fprintf(stderr, "=> remounting everything with MS_PRIVATE..."); - if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) { - fprintf(stderr, "failed! %m\n"); - return -1; - } - fprintf(stderr, "remounted.\n"); - - fprintf(stderr, "=> making a temp directory and a bind mount there..."); - char mount_dir[] = "/tmp/tmp.XXXXXX"; - if (!mkdtemp(mount_dir)) { - fprintf(stderr, "failed making a directory!\n"); - return -1; - } - - if (mount(config->mount_dir, mount_dir, NULL, MS_BIND | MS_PRIVATE, NULL)) { - fprintf(stderr, "bind mount failed!\n"); - return -1; - } - - char inner_mount_dir[] = "/tmp/tmp.XXXXXX/oldroot.XXXXXX"; - memcpy(inner_mount_dir, mount_dir, sizeof(mount_dir) - 1); - if (!mkdtemp(inner_mount_dir)) { - fprintf(stderr, "failed making the inner directory!\n"); - return -1; - } - fprintf(stderr, "done.\n"); - - fprintf(stderr, "=> pivoting root..."); - if (pivot_root(mount_dir, inner_mount_dir)) { - fprintf(stderr, "failed!\n"); - return -1; - } - fprintf(stderr, "done.\n"); - - char *old_root_dir = basename(inner_mount_dir); - char old_root[sizeof(inner_mount_dir) + 1] = { "/" }; - strcpy(&old_root[1], old_root_dir); - - fprintf(stderr, "=> unmounting %s...", old_root); - if (chdir("/")) { - fprintf(stderr, "chdir failed! %m\n"); - return -1; - } - if (umount2(old_root, MNT_DETACH)) { - fprintf(stderr, "umount failed! %m\n"); - return -1; - } - if (rmdir(old_root)) { - fprintf(stderr, "rmdir failed! %m\n"); - return -1; - } - fprintf(stderr, "done.\n"); - - fprintf(stderr, "=> mounting /proc..."); - if (mount("proc", "/proc", "proc", 0, NULL)) { - fprintf(stderr, "failed: %m\n"); - return -1; - } - fprintf(stderr, "done.\n"); - - fprintf(stderr, "=> mounting /dev..."); - if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,size=65536k")) { - fprintf(stderr, "failed: %m\n"); - return -1; - } - umask(0); - if (mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3))) { - fprintf(stderr, "mknod null failed: %m\n"); - } - if (mknod("/dev/zero", S_IFCHR | 0666, makedev(1, 5))) { - fprintf(stderr, "mknod zero failed: %m\n"); - } - if (mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9))) { - fprintf(stderr, "mknod urandom failed: %m\n"); - } - fprintf(stderr, "done.\n"); - - return 0; -} - - -#define SCMP_FAIL SCMP_ACT_ERRNO(EPERM) - -int syscalls() -{ - scmp_filter_ctx ctx = NULL; - fprintf(stderr, "=> filtering syscalls..."); - if (!(ctx = seccomp_init(SCMP_ACT_ALLOW)) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(chmod), 1, - SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(chmod), 1, - SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(fchmod), 1, - SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(fchmod), 1, - SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(fchmodat), 1, - SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(fchmodat), 1, - SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(unshare), 1, - SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(clone), 1, - SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(ioctl), 1, - SCMP_A1(SCMP_CMP_MASKED_EQ, TIOCSTI, TIOCSTI)) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(keyctl), 0) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(add_key), 0) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(request_key), 0) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(ptrace), 0) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(mbind), 0) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(migrate_pages), 0) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(move_pages), 0) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(set_mempolicy), 0) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(userfaultfd), 0) - || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(perf_event_open), 0) - || seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0) - || seccomp_load(ctx)) { - if (ctx) seccomp_release(ctx); - fprintf(stderr, "failed: %m\n"); - return 1; - } - seccomp_release(ctx); - fprintf(stderr, "done.\n"); - return 0; -} - -#define MEMORY "1073741824" -#define SHARES "256" -#define PIDS "64" -#define WEIGHT "10" -#define FD_COUNT 64 - -struct cgrp_control { - char control[256]; - struct cgrp_setting { - char name[256]; - char value[256]; - } **settings; -}; -struct cgrp_setting add_to_tasks = { - .name = "tasks", - .value = "0" -}; - -struct cgrp_control *cgrps[] = { - & (struct cgrp_control) { - .control = "memory", - .settings = (struct cgrp_setting *[]) { - & (struct cgrp_setting) { - .name = "memory.limit_in_bytes", - .value = MEMORY - }, - & (struct cgrp_setting) { - .name = "memory.kmem.limit_in_bytes", - .value = MEMORY - }, - &add_to_tasks, - NULL - } - }, - & (struct cgrp_control) { - .control = "cpu", - .settings = (struct cgrp_setting *[]) { - & (struct cgrp_setting) { - .name = "cpu.shares", - .value = SHARES - }, - &add_to_tasks, - NULL - } - }, - & (struct cgrp_control) { - .control = "pids", - .settings = (struct cgrp_setting *[]) { - & (struct cgrp_setting) { - .name = "pids.max", - .value = PIDS - }, - &add_to_tasks, - NULL - } - }, - NULL -}; -int resources(struct child_config *config) -{ - fprintf(stderr, "=> setting cgroups..."); - for (struct cgrp_control **cgrp = cgrps; *cgrp; cgrp++) { - char dir[PATH_MAX] = {0}; - fprintf(stderr, "%s...", (*cgrp)->control); - if (snprintf(dir, sizeof(dir), "/sys/fs/cgroup/%s/%s", - (*cgrp)->control, config->hostname) == -1) { - return -1; - } - if (mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR)) { - fprintf(stderr, "mkdir %s failed: %m\n", dir); - return -1; - } - for (struct cgrp_setting **setting = (*cgrp)->settings; *setting; setting++) { - char path[PATH_MAX] = {0}; - int fd = 0; - if (snprintf(path, sizeof(path), "%s/%s", dir, - (*setting)->name) == -1) { - fprintf(stderr, "snprintf failed: %m\n"); - return -1; - } - if ((fd = open(path, O_WRONLY)) == -1) { - fprintf(stderr, "opening %s failed: %m\n", path); - return -1; - } - if (write(fd, (*setting)->value, strlen((*setting)->value)) == -1) { - fprintf(stderr, "writing to %s failed: %m\n", path); - close(fd); - return -1; - } - close(fd); - } - } - fprintf(stderr, "done.\n"); - fprintf(stderr, "=> setting rlimit..."); - if (setrlimit(RLIMIT_NOFILE, - & (struct rlimit) { - .rlim_max = FD_COUNT, - .rlim_cur = FD_COUNT, - })) { - fprintf(stderr, "failed: %m\n"); - return 1; - } - fprintf(stderr, "done.\n"); - return 0; -} - -int free_resources(struct child_config *config) -{ - fprintf(stderr, "=> cleaning cgroups..."); - for (struct cgrp_control **cgrp = cgrps; *cgrp; cgrp++) { - char dir[PATH_MAX] = {0}; - char task[PATH_MAX] = {0}; - int task_fd = 0; - if (snprintf(dir, sizeof(dir), "/sys/fs/cgroup/%s/%s", - (*cgrp)->control, config->hostname) == -1 - || snprintf(task, sizeof(task), "/sys/fs/cgroup/%s/tasks", - (*cgrp)->control) == -1) { - fprintf(stderr, "snprintf failed: %m\n"); - return -1; - } - if ((task_fd = open(task, O_WRONLY)) == -1) { - fprintf(stderr, "opening %s failed: %m\n", task); - return -1; - } - if (write(task_fd, "0", 2) == -1) { - fprintf(stderr, "writing to %s failed: %m\n", task); - close(task_fd); - return -1; - } - close(task_fd); - if (rmdir(dir)) { - fprintf(stderr, "rmdir %s failed: %m", dir); - return -1; - } - } - fprintf(stderr, "done.\n"); - return 0; -} - -#define USERNS_OFFSET 10000 -#define USERNS_COUNT 2000 - -int handle_child_uid_map (pid_t child_pid, int fd) -{ - int uid_map = 0; - int has_userns = -1; - if (read(fd, &has_userns, sizeof(has_userns)) != sizeof(has_userns)) { - fprintf(stderr, "couldn't read from child!\n"); - return -1; - } - if (has_userns) { - char path[PATH_MAX] = {0}; - for (char **file = (char *[]) { "uid_map", "gid_map", 0 }; *file; file++) { - if (snprintf(path, sizeof(path), "/proc/%d/%s", child_pid, *file) - > sizeof(path)) { - fprintf(stderr, "snprintf too big? %m\n"); - return -1; - } - fprintf(stderr, "writing %s...", path); - if ((uid_map = open(path, O_WRONLY)) == -1) { - fprintf(stderr, "open failed: %m\n"); - return -1; - } - if (dprintf(uid_map, "0 %d %d\n", USERNS_OFFSET, USERNS_COUNT) == -1) { - fprintf(stderr, "dprintf failed: %m\n"); - close(uid_map); - return -1; - } - close(uid_map); - } - } - if (write(fd, & (int) { 0 }, sizeof(int)) != sizeof(int)) { - fprintf(stderr, "couldn't write: %m\n"); - return -1; - } - return 0; -} -int userns(struct child_config *config) -{ - fprintf(stderr, "=> trying a user namespace..."); - int has_userns = !unshare(CLONE_NEWUSER); - if (write(config->fd, &has_userns, sizeof(has_userns)) != sizeof(has_userns)) { - fprintf(stderr, "couldn't write: %m\n"); - return -1; - } - int result = 0; - if (read(config->fd, &result, sizeof(result)) != sizeof(result)) { - fprintf(stderr, "couldn't read: %m\n"); - return -1; - } - if (result) return -1; - if (has_userns) { - fprintf(stderr, "done.\n"); - } else { - fprintf(stderr, "unsupported? continuing.\n"); - } - fprintf(stderr, "=> switching to uid %d / gid %d...", config->uid, config->uid); - if (setgroups(1, & (gid_t) { config->uid }) || - setresgid(config->uid, config->uid, config->uid) || - setresuid(config->uid, config->uid, config->uid)) { - fprintf(stderr, "%m\n"); - return -1; - } - fprintf(stderr, "done.\n"); - return 0; -} -int child(void *arg) -{ - struct child_config *config = arg; - if (sethostname(config->hostname, strlen(config->hostname)) - || mounts(config) - || userns(config) - || capabilities() - || syscalls()) { - close(config->fd); - return -1; - } - if (close(config->fd)) { - fprintf(stderr, "close failed: %m\n"); - return -1; - } - if (execve(config->argv[0], config->argv, NULL)) { - fprintf(stderr, "execve failed! %m.\n"); - return -1; - } - return 0; -} - -int choose_hostname(char *buff, size_t len) -{ - static const char *suits[] = { "swords", "wands", "pentacles", "cups" }; - static const char *minor[] = { - "ace", "two", "three", "four", "five", "six", "seven", "eight", - "nine", "ten", "page", "knight", "queen", "king" - }; - static const char *major[] = { - "fool", "magician", "high-priestess", "empress", "emperor", - "hierophant", "lovers", "chariot", "strength", "hermit", - "wheel", "justice", "hanged-man", "death", "temperance", - "devil", "tower", "star", "moon", "sun", "judgment", "world" - }; - struct timespec now = {0}; - clock_gettime(CLOCK_MONOTONIC, &now); - size_t ix = now.tv_nsec % 78; - if (ix < sizeof(major) / sizeof(*major)) { - snprintf(buff, len, "%05lx-%s", now.tv_sec, major[ix]); - } else { - ix -= sizeof(major) / sizeof(*major); - snprintf(buff, len, - "%05lxc-%s-of-%s", - now.tv_sec, - minor[ix % (sizeof(minor) / sizeof(*minor))], - suits[ix / (sizeof(minor) / sizeof(*minor))]); - } - return 0; -} - -int main (int argc, char **argv) -{ - struct child_config config = {0}; - int err = 0; - int option = 0; - int sockets[2] = {0}; - pid_t child_pid = 0; - int last_optind = 0; - while ((option = getopt(argc, argv, "c:m:u:"))) { - switch (option) { - case 'c': - config.argc = argc - last_optind - 1; - config.argv = &argv[argc - config.argc]; - goto finish_options; - case 'm': - config.mount_dir = optarg; - break; - case 'u': - if (sscanf(optarg, "%d", &config.uid) != 1) { - fprintf(stderr, "badly-formatted uid: %s\n", optarg); - goto usage; - } - break; - default: - goto usage; - } - last_optind = optind; - } -finish_options: - if (!config.argc) goto usage; - if (!config.mount_dir) goto usage; - - fprintf(stderr, "=> validating Linux version..."); - struct utsname host = {0}; - if (uname(&host)) { - fprintf(stderr, "failed: %m\n"); - goto cleanup; - } - int major = -1; - int minor = -1; - if (sscanf(host.release, "%u.%u.", &major, &minor) != 2) { - fprintf(stderr, "weird release format: %s\n", host.release); - goto cleanup; - } - if (major < 4 || (major == 4 && minor < 7)) { - fprintf(stderr, "kernel too old: %s (need >= 4.7)\n", host.release); - goto cleanup; - } - if (strcmp("x86_64", host.machine)) { - fprintf(stderr, "expected x86_64: %s\n", host.machine); - goto cleanup; - } - fprintf(stderr, "%s on %s.\n", host.release, host.machine); - - char hostname[256] = {0}; - if (choose_hostname(hostname, sizeof(hostname))) - goto error; - config.hostname = hostname; - - if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, sockets)) { - fprintf(stderr, "socketpair failed: %m\n"); - goto error; - } - if (fcntl(sockets[0], F_SETFD, FD_CLOEXEC)) { - fprintf(stderr, "fcntl failed: %m\n"); - goto error; - } - config.fd = sockets[1]; - #define STACK_SIZE (1024 * 1024) - - char *stack = 0; - if (!(stack = malloc(STACK_SIZE))) { - fprintf(stderr, "=> malloc failed, out of memory?\n"); - goto error; - } - if (resources(&config)) { - err = 1; - goto clear_resources; - } - int flags = CLONE_NEWNS - | CLONE_NEWCGROUP - | CLONE_NEWPID - | CLONE_NEWIPC - | CLONE_NEWNET - | CLONE_NEWUTS; - if ((child_pid = clone(child, stack + STACK_SIZE, flags | SIGCHLD, &config)) == -1) { - fprintf(stderr, "=> clone failed! %m\n"); - err = 1; - goto clear_resources; - } - close(sockets[1]); - sockets[1] = 0; - close(sockets[1]); - sockets[1] = 0; - if (handle_child_uid_map(child_pid, sockets[0])) { - err = 1; - goto kill_and_finish_child; - } - - goto finish_child; -kill_and_finish_child: - if (child_pid) kill(child_pid, SIGKILL); -finish_child:; - int child_status = 0; - waitpid(child_pid, &child_status, 0); - err |= WEXITSTATUS(child_status); -clear_resources: - free_resources(&config); - free(stack); - - goto cleanup; -usage: - fprintf(stderr, "Usage: %s -u -1 -m . -c /bin/sh ~\n", argv[0]); -error: - err = 1; -cleanup: - if (sockets[0]) close(sockets[0]); - if (sockets[1]) close(sockets[1]); - return err; -} diff --git a/include/capabilities.h b/include/capabilities.h new file mode 100644 index 0000000..5e2f467 --- /dev/null +++ b/include/capabilities.h @@ -0,0 +1,6 @@ +#ifndef CAPABILITIES_H +#define CAPABILITIES_H + +int capabilities(void); + +#endif diff --git a/include/cgroup.h b/include/cgroup.h new file mode 100644 index 0000000..0d2bdd1 --- /dev/null +++ b/include/cgroup.h @@ -0,0 +1,9 @@ +#ifndef CGROUP_H +#define CGROUP_H + +#include "container.h" + +int resources(struct child_config *config); +int free_resources(struct child_config *config); + +#endif diff --git a/include/container.h b/include/container.h new file mode 100644 index 0000000..12e6cf8 --- /dev/null +++ b/include/container.h @@ -0,0 +1,41 @@ +#ifndef CONTAINER_H +#define CONTAINER_H + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define STACK_SIZE (1024 * 1024) +#define USERNS_OFFSET 10000 +#define USERNS_COUNT 2000 +#define FD_COUNT 64 + +struct child_config { + int argc; + uid_t uid; + int fd; + char *hostname; + char **argv; + char *mount_dir; +}; + +#endif diff --git a/include/container_seccomp.h b/include/container_seccomp.h new file mode 100644 index 0000000..32ba623 --- /dev/null +++ b/include/container_seccomp.h @@ -0,0 +1,6 @@ +#ifndef CONTAINER_SECCOMP_H +#define CONTAINER_SECCOMP_H + +int syscalls(void); + +#endif diff --git a/include/hostname.h b/include/hostname.h new file mode 100644 index 0000000..1805c0d --- /dev/null +++ b/include/hostname.h @@ -0,0 +1,8 @@ +#ifndef HOSTNAME_H +#define HOSTNAME_H + +#include + +int choose_hostname(char *buff, size_t len); + +#endif diff --git a/include/mount.h b/include/mount.h new file mode 100644 index 0000000..6775e92 --- /dev/null +++ b/include/mount.h @@ -0,0 +1,8 @@ +#ifndef MOUNT_H +#define MOUNT_H + +#include "container.h" + +int mounts(struct child_config *config); + +#endif diff --git a/include/userns.h b/include/userns.h new file mode 100644 index 0000000..9053f98 --- /dev/null +++ b/include/userns.h @@ -0,0 +1,9 @@ +#ifndef USERNS_H +#define USERNS_H + +#include "container.h" + +int userns(struct child_config *config); +int handle_child_uid_map(pid_t child_pid, int fd); + +#endif diff --git a/src/capabilities.c b/src/capabilities.c new file mode 100644 index 0000000..5be3ce4 --- /dev/null +++ b/src/capabilities.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include "capabilities.h" + +int capabilities(void) +{ + fprintf(stderr, "=> dropping capabilities..."); + int drop_caps[] = { + CAP_AUDIT_CONTROL, + CAP_AUDIT_READ, + CAP_AUDIT_WRITE, + CAP_BLOCK_SUSPEND, + CAP_DAC_READ_SEARCH, + CAP_FSETID, + CAP_IPC_LOCK, + CAP_MAC_ADMIN, + CAP_MAC_OVERRIDE, + CAP_MKNOD, + CAP_SETFCAP, + CAP_SYSLOG, + CAP_SYS_ADMIN, + CAP_SYS_BOOT, + CAP_SYS_MODULE, + CAP_SYS_NICE, + CAP_SYS_RAWIO, + CAP_SYS_RESOURCE, + CAP_SYS_TIME, + CAP_WAKE_ALARM + }; + size_t num_caps = sizeof(drop_caps) / sizeof(*drop_caps); + fprintf(stderr, "bounding..."); + for (size_t i = 0; i < num_caps; i++) { + if (prctl(PR_CAPBSET_DROP, drop_caps[i], 0, 0, 0)) { + fprintf(stderr, "prctl failed: %m\n"); + return 1; + } + } + fprintf(stderr, "inheritable..."); + cap_t caps = NULL; + if (!(caps = cap_get_proc()) + || cap_set_flag(caps, CAP_INHERITABLE, num_caps, drop_caps, CAP_CLEAR) + || cap_set_proc(caps)) { + fprintf(stderr, "failed: %m\n"); + if (caps) cap_free(caps); + return 1; + } + cap_free(caps); + fprintf(stderr, "done.\n"); + return 0; +} diff --git a/src/capabilities.o b/src/capabilities.o new file mode 100644 index 0000000000000000000000000000000000000000..06fd4a3c929e99aafb35035253851e1dfba53fa9 GIT binary patch literal 2880 zcmbVNPiS0482>igHZ>-7TdcLzKTq3AiuSqLpu~t}W4i4Vq=(AdgF^at_igfU|MB(} zs~{Fa3ri^E;Gu#C3q2@!sL(@NG;OIQ76dEwpx~tkaiu+^^^${7zu&x>-N|I%rQh=A zH}m`c%s2DR%zmDG_fTIVK}r&IfVv~aGH&knv!a`&AsVE-)B4Mqz1MK&HiC6p*RSki zTttkkVA^PwbMwy$_+qbsbI%I+YL9@g`vhFrAmE!V0=|1ez(q^IB?Mj0Ej%yahZhB0 z+b-azrv&_*67b6o0k^gaxc#(%-**c5b2o#toLhMk*vFCIIUdUnf(dqMy?NuU16C=hHyVCz+A6vYx8PU&(D$0QZBwCsqE=!B`i-C%R;;q? zSG>}=wYNGzer?7J{Ln2_JQ(PzQoZKc14M@>C&#Vf!$xu}7>-I+IS1WnQy} zCn|oyEx1y#f7BkcGWKX@9}y}JNql;g5+_rMU4#9b=eZ`&FozGa9eX#iC3U86a%fXB z)yF1+@71838|ePSUbLp1McWRv~N&V|M5J~-#Y8FXCsn!ZE%Qh`f zv3BNgF`+XBH?4F(-eIq*E5UI?BJ2#yXPPWta*B-lp?IK7H^xge-G ze3ku=AX&^fzeaQ?jykKiC2z**$H8O)KggeozfJOH{2h{4c@D#wWseqTh{r0AsivOj?KcVs8YCO)NvcDvGb6uAuuh#XB%x6i9qp#}^jmKYy zihnPL|5x(nyc{0@@c41)+~R4?!qgxFUIu*K;FZ6^w^^_Hy6N9$LQ-kk9+Up2d0P@= zdQk4G{a5r+?)0kPDft(r>V*2iSNSU*`IvJvMV2hcMVT<(j>q^j{lS^FV~TDeDHcD1 VPHV0ocfFVYIhnuP@r*jA|NlZJwrKzW literal 0 HcmV?d00001 diff --git a/src/cgroup.c b/src/cgroup.c new file mode 100644 index 0000000..a1ca0d6 --- /dev/null +++ b/src/cgroup.c @@ -0,0 +1,142 @@ +#include "container.h" +#include "cgroup.h" + +#define MEMORY "1073741824" +#define SHARES "256" +#define PIDS "64" +#define WEIGHT "10" + +struct cgrp_control { + char control[256]; + struct cgrp_setting { + char name[256]; + char value[256]; + } **settings; +}; + +static struct cgrp_setting add_to_tasks = { + .name = "tasks", + .value = "0" +}; + +static struct cgrp_control *cgrps[] = { + & (struct cgrp_control) { + .control = "memory", + .settings = (struct cgrp_setting *[]) { + & (struct cgrp_setting) { + .name = "memory.limit_in_bytes", + .value = MEMORY + }, + & (struct cgrp_setting) { + .name = "memory.kmem.limit_in_bytes", + .value = MEMORY + }, + &add_to_tasks, + NULL + } + }, + & (struct cgrp_control) { + .control = "cpu", + .settings = (struct cgrp_setting *[]) { + & (struct cgrp_setting) { + .name = "cpu.shares", + .value = SHARES + }, + &add_to_tasks, + NULL + } + }, + & (struct cgrp_control) { + .control = "pids", + .settings = (struct cgrp_setting *[]) { + & (struct cgrp_setting) { + .name = "pids.max", + .value = PIDS + }, + &add_to_tasks, + NULL + } + }, + NULL +}; + +int resources(struct child_config *config) +{ + fprintf(stderr, "=> setting cgroups..."); + for (struct cgrp_control **cgrp = cgrps; *cgrp; cgrp++) { + char dir[PATH_MAX] = {0}; + fprintf(stderr, "%s...", (*cgrp)->control); + if (snprintf(dir, sizeof(dir), "/sys/fs/cgroup/%s/%s", + (*cgrp)->control, config->hostname) == -1) { + return -1; + } + if (mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR)) { + fprintf(stderr, "mkdir %s failed: %m\n", dir); + return -1; + } + for (struct cgrp_setting **setting = (*cgrp)->settings; *setting; setting++) { + char path[PATH_MAX] = {0}; + int fd = 0; + if (snprintf(path, sizeof(path), "%s/%s", dir, + (*setting)->name) == -1) { + fprintf(stderr, "snprintf failed: %m\n"); + return -1; + } + if ((fd = open(path, O_WRONLY)) == -1) { + fprintf(stderr, "opening %s failed: %m\n", path); + return -1; + } + if (write(fd, (*setting)->value, strlen((*setting)->value)) == -1) { + fprintf(stderr, "writing to %s failed: %m\n", path); + close(fd); + return -1; + } + close(fd); + } + } + fprintf(stderr, "done.\n"); + fprintf(stderr, "=> setting rlimit..."); + if (setrlimit(RLIMIT_NOFILE, + & (struct rlimit) { + .rlim_max = FD_COUNT, + .rlim_cur = FD_COUNT, + })) { + fprintf(stderr, "failed: %m\n"); + return 1; + } + fprintf(stderr, "done.\n"); + return 0; +} + +int free_resources(struct child_config *config) +{ + fprintf(stderr, "=> cleaning cgroups..."); + for (struct cgrp_control **cgrp = cgrps; *cgrp; cgrp++) { + char dir[PATH_MAX] = {0}; + char task[PATH_MAX] = {0}; + int task_fd = 0; + if (snprintf(dir, sizeof(dir), "/sys/fs/cgroup/%s/%s", + (*cgrp)->control, config->hostname) == -1 + || snprintf(task, sizeof(task), "/sys/fs/cgroup/%s/tasks", + (*cgrp)->control) == -1) { + fprintf(stderr, "snprintf failed: %m\n"); + return -1; + } + if ((task_fd = open(task, O_WRONLY)) == -1) { + fprintf(stderr, "opening %s failed: %m\n", task); + return -1; + } + if (write(task_fd, "0", 2) == -1) { + fprintf(stderr, "writing to %s failed: %m\n", task); + close(task_fd); + return -1; + } + close(task_fd); + if (rmdir(dir)) { + fprintf(stderr, "rmdir %s failed: %m", dir); + return -1; + } + } + fprintf(stderr, "done.\n"); + return 0; +} diff --git a/src/cgroup.o b/src/cgroup.o new file mode 100644 index 0000000000000000000000000000000000000000..315746f9192a14776406b2ec342eb151671a02c7 GIT binary patch literal 9664 zcmeHMYiv|S6rQpI6{S}3h0luy!PjNGk60&lMXo!iA5X^Ghy_HS((cF6*ilWxk z8aA6Ig2BYZANtQ|@VAl(^%09P(UO?RA0v{O(6q)HQL9nvIWy;Mr?dA82|t$;c4zK4 z=X`VK%sJE9%S)MU56^3A5-d$(gP46{REYE)T3>e4qD3qgPdJsIoR=rlmJm+m9%73xyAz zH|Kq)nwfU0@bD?ka8%k=Mx~-6uMQ~>wzt-_KXHcTBwH(EN6_G?XhlZNYH)g{Z_$S{ zAmjVcDW!jOM9w@aiHAnQdw5;LhtTKx$rZzTWFSwg0~kuDl9?LcfgM=wFSD!VF02;1 z=4wYXlX7|0%#>U-EJMzV-huko%5kT1;(r!&XKav^p9j$)#U*!WPDCo1$-x&UXZluX zYlcZ<);#e%ELeIuU#(ZgOm39Ihxg+^H8bf{zOK+HM!oHP{9r=*((8$tdBaTx|@Qz?%L^vQ5;LFdbo-7L{t9*P0GL2&dX4eCc={MKYH!ulg z*c4cR7Ivq(JA6o-s?RV~<{SiN?t=1Ez4B=0tYobYDVK}_!&(2gz``VN2bAxyocQAM zcL^?65k=+i*})gi&h%+Di%X5k-|XwEPxK;4$?TB zB3eb^NQ_4l9d#MJ4AXixN8=L+{nsRPgPuTpZnSQ)(H3m1x9Ani{$9I~FXqE+zLf3U z8+whD4{gbH?dw*zb);4sfoYIO+W#JC8!`zRegDsw<|;qVr`5RQUA=vFu-o+;>d@4- zJGEx5_H?OhMga1|gf2a65$Mh50=ww;YfwXV*EZKwLTuT*dA+rA%VSU6ZM7xsl-*{flBr~}Belj_xv7xvbUR%dxWC=*uu^t=>TZl8 zzT`n^dhT)2)Zf~4^OXymUxRlFw*ZD8lb%X17bG4@fQlS@P>#Y7zA&hb zKR6G^;{>S1W#U))ZpPF};M=hXNeFbHUJ^pSVVyIu2%egBee@0F2KY#vo&~k{F@70p zfe^k4x>6tCFo0O{TZkv2Mcg91nOyE6JdLlyP>5#$x~QMN96=#&RE;+C0gV8mhiU;XP$AwM;0r6OEvO4A;@_R!)`)E?^{BUlXd#M*x7+K z5RYrXQx=&!@bC+K;DvLY{1Joy8-vdW>L{L;82s88d}$2+XUx1-jQ(9QcoJ}o1Fv7! z2ykzV(HE-p?JnWwa@nw)Rn?x&c9o00<-SrbTgZo=?-uN&hydH7z*H329tEzB0@p-= zYoow*QD8?D_yDf67mnC4=lQreA=jt56O+0Y|(#L`Apn zd0F^1QttD+;5Ugp6Cu0;|EmQB%LZQ)|1+ax1%e=0R#(fO`oj80!Scs5g7x{jas^;` z`fwfXfj)}KzfZw6ew-Vnq8iDMwa@&A31@%!ti?JTTq-V+*rGwAZV zevIM&+2D7H|8I>)=f55u1VR5X#>~H3!*A7au2XYekCHyeryp!kc4$2Myg3cO4fJ^4 zz8L&4;n?VTdyH_N_aIIR^0~&N=j~T9Jl`8UZz>vbI)*1fe{wNyx5J11Zy_AbbU&96 z&VGJC{FcU}$H$K0NgF)8-i*ehuh)y=*<Zm#QBgQs87h;tf`K5sK!@o-qLqopzUN)2BIHk`NZgq!|sB%Jqmg8bR0@#y}P zV|ZRRcsOrg)p+#0~F_31un$>WMORI@1bz#vJtfo(TeBWf8@1+i1O~#eA5O33clX14^zn_fr{rR||&-dXm1Lym#MH6ATC}a{z z8#v#)gdOZHhHfXcp|9HA-1EJHYlmKcC~VoyxuGlU&LDv2vU-5qSJ5khLa7{jHvGK0 zKM38f9y0KDXS;p3=+WF4(^z1Pv7zm&QKhMOfbxh3P*)eMc}$hG4nqGp9!?1sbktN)l z+nVcN3&V)%Ie`{zZ~ovk?KhCU`TwNH=m#?L>%#WjZh=AW>w3TQ4{RNb(A`Ox>Hjp@ zKW*4^{1|=;I#Ka|cbU@Qbz%JR2w;2OfArm)8!flfg*r(agJqA9X^))d-T@>AXo8Fp U9OF^m$)oH~lKR literal 0 HcmV?d00001 diff --git a/src/hostname.c b/src/hostname.c new file mode 100644 index 0000000..31a8d4a --- /dev/null +++ b/src/hostname.c @@ -0,0 +1,32 @@ +#include +#include +#include "hostname.h" + +int choose_hostname(char *buff, size_t len) +{ + static const char *suits[] = { "swords", "wands", "pentacles", "cups" }; + static const char *minor[] = { + "ace", "two", "three", "four", "five", "six", "seven", "eight", + "nine", "ten", "page", "knight", "queen", "king" + }; + static const char *major[] = { + "fool", "magician", "high-priestess", "empress", "emperor", + "hierophant", "lovers", "chariot", "strength", "hermit", + "wheel", "justice", "hanged-man", "death", "temperance", + "devil", "tower", "star", "moon", "sun", "judgment", "world" + }; + struct timespec now = {0}; + clock_gettime(CLOCK_MONOTONIC, &now); + size_t ix = now.tv_nsec % 78; + if (ix < sizeof(major) / sizeof(*major)) { + snprintf(buff, len, "%05lx-%s", now.tv_sec, major[ix]); + } else { + ix -= sizeof(major) / sizeof(*major); + snprintf(buff, len, + "%05lxc-%s-of-%s", + now.tv_sec, + minor[ix % (sizeof(minor) / sizeof(*minor))], + suits[ix / (sizeof(minor) / sizeof(*minor))]); + } + return 0; +} diff --git a/src/hostname.o b/src/hostname.o new file mode 100644 index 0000000000000000000000000000000000000000..567f07f35dad7a05d300d1fa0427e2f42bb6fbda GIT binary patch literal 3744 zcmeH~PiP!f9LIl~ZIh@;h{Z~zMF(kYqU^BQ8VMqGYe@5owp1bLK}aXNZ+E8InOSFM zlPFXR0dZZ#;6?G2lL%gV@K6e3LebWqHU}YiaZ$u53hE{6AE>|I?C)i_&+SF<=1V5O z-}m$0@ArE%Z}z>-YctQB>`5d*wFDf8wo(+pxU21FV>b&!FbH#^`kh$)DJ`lOwnep4 z7uRMQ)Qxs0wr|ZeMD_DeDQ64RPVa~-ea>1h{fg>!vHPQ_*2QLXzo_02n}0kh#_M9S zeulcxtIjmV>V@XPI!uoVGUC5H&1Uo7p~v1FKtCVqCp;nQUC?hgI~%s@uFb zj@-hsqo>|ELce178>)$@_5|lU-K2v0psm*28cCW;Vnd8?(j;pGU&hsLnZ)n6n@2pO zFRoI=|Lwni0;B24(n@MHgxJZUm+}jw3%*|h*D5+W$MV2-igv0TI5Lc67=m=m0b3dP z0d}J)+m;tW$zPH|2szsd96y3E3Zz$zY_MhEIuR_}QkLL+C5)V$1dJ%ke9FZNd1>LO z2s6vT@~|&2mz)wr{;~`(ixq(D`yPZ952N!%S7NPYKPcrPM3WC;+4Asj8M&1!Nt{8Y z9D;?3qGcZ4C&Mi@7clY4bC6gW zP8=HO>tDkU_hIC8QG1v^>?ekXU+`jg)a7fI%{h^`z;Mbeo`jTCDB7h|RbZbf2 z-wosF-hc1`B#DkZmv{u-AMp*=23xu};tkeiwq%mf{r-}0N5`nPn}oi$VDAXp10DAp zKSN}X;x)`;I?YqJ;b8k=-8rvo|s<0 ze#>IE3u@B=+GmOB!&UrIEkKj_-%Kk$-7XP(yF4MakE?DKuk0+xtA1ru^iaAq6{osh zM2DC@|1`%>sex?O^HcbFbh>%O~3!Uv9DYFWuAYbBbHn}{%>zxhw1hostname, strlen(config->hostname)) + || mounts(config) + || userns(config) + || capabilities() + || syscalls()) { + close(config->fd); + return -1; + } + if (close(config->fd)) { + fprintf(stderr, "close failed: %m\n"); + return -1; + } + if (execve(config->argv[0], config->argv, NULL)) { + fprintf(stderr, "execve failed! %m.\n"); + return -1; + } + return 0; +} + +int main(int argc, char **argv) +{ + struct child_config config = {0}; + int err = 0; + int option = 0; + int sockets[2] = {0}; + pid_t child_pid = 0; + int last_optind = 0; + while ((option = getopt(argc, argv, "c:m:u:"))) { + switch (option) { + case 'c': + config.argc = argc - last_optind - 1; + config.argv = &argv[argc - config.argc]; + goto finish_options; + case 'm': + config.mount_dir = optarg; + break; + case 'u': + if (sscanf(optarg, "%d", &config.uid) != 1) { + fprintf(stderr, "badly-formatted uid: %s\n", optarg); + goto usage; + } + break; + default: + goto usage; + } + last_optind = optind; + } +finish_options: + if (!config.argc) goto usage; + if (!config.mount_dir) goto usage; + + fprintf(stderr, "=> validating Linux version..."); + struct utsname host = {0}; + if (uname(&host)) { + fprintf(stderr, "failed: %m\n"); + goto cleanup; + } + int major = -1; + int minor = -1; + if (sscanf(host.release, "%u.%u.", &major, &minor) != 2) { + fprintf(stderr, "weird release format: %s\n", host.release); + goto cleanup; + } + if (major < 4 || (major == 4 && minor < 7)) { + fprintf(stderr, "kernel too old: %s (need >= 4.7)\n", host.release); + goto cleanup; + } + if (strcmp("x86_64", host.machine)) { + fprintf(stderr, "expected x86_64: %s\n", host.machine); + goto cleanup; + } + fprintf(stderr, "%s on %s.\n", host.release, host.machine); + + char hostname[256] = {0}; + if (choose_hostname(hostname, sizeof(hostname))) + goto error; + config.hostname = hostname; + + if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, sockets)) { + fprintf(stderr, "socketpair failed: %m\n"); + goto error; + } + if (fcntl(sockets[0], F_SETFD, FD_CLOEXEC)) { + fprintf(stderr, "fcntl failed: %m\n"); + goto error; + } + config.fd = sockets[1]; + + char *stack = 0; + if (!(stack = malloc(STACK_SIZE))) { + fprintf(stderr, "=> malloc failed, out of memory?\n"); + goto error; + } + if (resources(&config)) { + err = 1; + goto clear_resources; + } + int flags = CLONE_NEWNS + | CLONE_NEWCGROUP + | CLONE_NEWPID + | CLONE_NEWIPC + | CLONE_NEWNET + | CLONE_NEWUTS; + if ((child_pid = clone(child, stack + STACK_SIZE, flags | SIGCHLD, &config)) == -1) { + fprintf(stderr, "=> clone failed! %m\n"); + err = 1; + goto clear_resources; + } + close(sockets[1]); + sockets[1] = 0; + close(sockets[1]); + sockets[1] = 0; + if (handle_child_uid_map(child_pid, sockets[0])) { + err = 1; + goto kill_and_finish_child; + } + + goto finish_child; +kill_and_finish_child: + if (child_pid) kill(child_pid, SIGKILL); +finish_child:; + int child_status = 0; + waitpid(child_pid, &child_status, 0); + err |= WEXITSTATUS(child_status); +clear_resources: + free_resources(&config); + free(stack); + + goto cleanup; +usage: + fprintf(stderr, "Usage: %s -u -1 -m . -c /bin/sh ~\n", argv[0]); +error: + err = 1; +cleanup: + if (sockets[0]) close(sockets[0]); + if (sockets[1]) close(sockets[1]); + return err; +} diff --git a/src/main.o b/src/main.o new file mode 100644 index 0000000000000000000000000000000000000000..8d50b2579bd8ad9caf08b6e56b575ccd7847a650 GIT binary patch literal 6704 zcmb_gZ)_CD6`%7NV~oLu0CAgytdlr03OMc;9YLRjo?HoZe=Yp`k2Gs2@ATU= z2`_xw9w)?qYjJUDN%MccfL&FcpsZDuYQlJmjc>4i2K%bI7<6|SIvxxcg06<$Jo;B0 zy;JZ~__ytqwM}%K_SVBV?>#a9cL=#$Li3NPwc=dNS0~B?69KYgM!TR@w!Di>TDe4> zi_O4h-I)I#g=o59JaZdz6AUT?6I$_lcxLFOX^(yFEQe=RLR zumr>oAu2-b7Gkdu_X#m6L`{fCgm_Gd-xlJK5Ql|0BE;u~_>vIEgm^)SuM2Tph!aA* zB*ZBp-Vox95I+*)$3mPF;(`!sLL_kDSSFjfSfTly#Ih~UbzAAK1{eo(ny*f26?Gi% z4z8ZWn{*W~5Ivr748GtNd;of7b^HpdPXZ_LrE>bkOTSN-Ui8&VQ2D4SID75v!us%K zQ5>Bkg$Mbu0`EG}2wD<{bpBZwgZC36IIk5dCTh(iX;r;NNP|^YZxwHB^6{%?POCuO zZD*^lQ7&;CeRZKYy0BD$5=JY^>ch_2Uk3KFdXbf>s(KN|b%25Kvbx~UGBrra{fuA? z>=17Y%ucR@l}zDweu}oF2)tcLWF||%zlyg9X3v`SI0u2BjZ&_WOemkABT|>$0X3og;D_!{(VvHE+lN{;=sChEH_B;B9yFZ5&PuswM|IjQH&jP9A%apkaS6-JbkhU1#H zl}IFrY^bY{fPdtaVLBPbF>;2Eo=^h;GxD7<9LvZlo^303j#^Z@Ed%Tycv9Juczj0- z8QHftwRaD=J#3`Wv0xl9bh(OcftL`1Yo|{b-mq>uE27P&EibnML8~;5yq?S1X*Rc0 zu?wDJXO+B>x1G~pX~9S#d6t~17D5JG{kVa~m7apqvs>xOD+#41t?U{!tzGVrau#$B z9X!~tbRYWKzz$`1Z=x@;Tj}fV>+Rjw_n6ZCRL&gK2X!{l^Jrq9(wBI&Z%5!2AzPRb zJ9Cu8M%rWdw61L)gR62c;P}C|2CvRj8{5CzaBxFYoWwwd&OJeA3HdF&EX4e56V699 zq-rF8qeUsK1Jq%Nj^hNrOX{>ZFx6{uLhtFSsW@4?3PT`H>iZif7}&~&nwup*X>I`9 z2%q+qs&s^po_cs^1l}Ei?~1^259;x&5%{wa_|XXbn-RDUIOc)JPtO2g*$Dl71U?#p zpO3)H5%{YS_=O1kClUCY5jd9GdU>9T!0}~XPrn+0|0)9iV+8(51pe0u{PPH$q{IXS~3uHg;Yu4n0agXHak<+-HbLg92tS|8R2&7A3(hD+SjZW?MDpjh-_ zamH{Qk{xzT%geI5Px*;G?CH*Nl1iCSHuvvOxv19fWI8&P!$t+YUoMHM};3^Jr!nVgZLv83P&C6(8Q$q6%;Bd2uJ8#Xf}>ljAL zF;m=5TtAu}D!Z%2GO2V;LaQ-0)Z%S6#zs7KUaBY(G@tkABnB=NRpC*7|yqn>}`Da1KhWK6Z;rJgAgcj!iO8_tP zBNw*Mf^HT6bS#)a9;}U=e>MXD6T`Xx4>R5chNGYOUk#jxKJ#E4=8q%5a5+EC4Cnm( zi*YN%!};lyc-y*U%fm(-(mWl3=ikOhv7&k$CV86yvTUID)Gp6&qVN4LOicCp4TFH zeiqWd&h#%!`u70F_wgNuhyA}9;+bYVwtaDJnMo`=-)Rb{Z2{$D#OF^z9aEqs`{BfA4)uO zK0lFgxt`Y|@Qv)Lig|lL@@JQXKPchHB>W)>cO|?_!p}+g!xH`@hKJ+*F~j-(9%1pm zA@RuZz9->wyf-6o1$@AUcJaeok_jB=?@f|$Pv<>-5PHuE`XMgCZ$CZZ86zG^!0$$# zBnDlVBpf>!r0sm(uwc%zJtG0XntEJMPoDqwC2m5LH@=Ft8-EY7g zVqyPh*!pAAS`@?n9|f%5{vE?d3p5@V?jNq@>-qlGvyJPzaRIb(wlVlNMVG?s5ex4f eKx76Vr_kb|fI~z29bRw!OU!>gpjrMKUjJ{X9XV+L literal 0 HcmV?d00001 diff --git a/src/mount.c b/src/mount.c new file mode 100644 index 0000000..d3e10e7 --- /dev/null +++ b/src/mount.c @@ -0,0 +1,89 @@ +#include "container.h" +#include "mount.h" + +static int pivot_root(const char *new_root, const char *put_old) +{ + return syscall(SYS_pivot_root, new_root, put_old); +} + +int mounts(struct child_config *config) +{ + fprintf(stderr, "=> remounting everything with MS_PRIVATE..."); + if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL)) { + fprintf(stderr, "failed! %m\n"); + return -1; + } + fprintf(stderr, "remounted.\n"); + + fprintf(stderr, "=> making a temp directory and a bind mount there..."); + char mount_dir[] = "/tmp/tmp.XXXXXX"; + if (!mkdtemp(mount_dir)) { + fprintf(stderr, "failed making a directory!\n"); + return -1; + } + + if (mount(config->mount_dir, mount_dir, NULL, MS_BIND | MS_PRIVATE, NULL)) { + fprintf(stderr, "bind mount failed!\n"); + return -1; + } + + char inner_mount_dir[] = "/tmp/tmp.XXXXXX/oldroot.XXXXXX"; + memcpy(inner_mount_dir, mount_dir, sizeof(mount_dir) - 1); + if (!mkdtemp(inner_mount_dir)) { + fprintf(stderr, "failed making the inner directory!\n"); + return -1; + } + fprintf(stderr, "done.\n"); + + fprintf(stderr, "=> pivoting root..."); + if (pivot_root(mount_dir, inner_mount_dir)) { + fprintf(stderr, "failed!\n"); + return -1; + } + fprintf(stderr, "done.\n"); + + char *old_root_dir = basename(inner_mount_dir); + char old_root[sizeof(inner_mount_dir) + 1] = { "/" }; + strcpy(&old_root[1], old_root_dir); + + fprintf(stderr, "=> unmounting %s...", old_root); + if (chdir("/")) { + fprintf(stderr, "chdir failed! %m\n"); + return -1; + } + if (umount2(old_root, MNT_DETACH)) { + fprintf(stderr, "umount failed! %m\n"); + return -1; + } + if (rmdir(old_root)) { + fprintf(stderr, "rmdir failed! %m\n"); + return -1; + } + fprintf(stderr, "done.\n"); + + fprintf(stderr, "=> mounting /proc..."); + if (mount("proc", "/proc", "proc", 0, NULL)) { + fprintf(stderr, "failed: %m\n"); + return -1; + } + fprintf(stderr, "done.\n"); + + fprintf(stderr, "=> mounting /dev..."); + if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME, "mode=755,size=65536k")) { + fprintf(stderr, "failed: %m\n"); + return -1; + } + umask(0); + if (mknod("/dev/null", S_IFCHR | 0666, makedev(1, 3))) { + fprintf(stderr, "mknod null failed: %m\n"); + } + if (mknod("/dev/zero", S_IFCHR | 0666, makedev(1, 5))) { + fprintf(stderr, "mknod zero failed: %m\n"); + } + if (mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9))) { + fprintf(stderr, "mknod urandom failed: %m\n"); + } + fprintf(stderr, "done.\n"); + + return 0; +} diff --git a/src/mount.o b/src/mount.o new file mode 100644 index 0000000000000000000000000000000000000000..a41723ab3dfc37c36de3ff8e8755df4b3db1e52a GIT binary patch literal 6480 zcmb_gYit!o6rMhS0tG874MNsI|Gn&2O-RZ9g!AyMFm529%LLn0yxV$?vy)N^*v^q$V`qEt_^J2T&$ z`OcX$XXeh{?SV%h$ zv9|%90Mt($eI4|=Q1>FfP8}!RB`0>FwVdOel4o>!h zlP8U?L^Nb45{?eY*}lLzoI|)TaK5qeMBsa2bk_I7JbJrt0|Q3pv^0;{U|pG8sh=-9 z3x))HbgmwY&@eMHIvaYupniq65B)VTpoF0)m(mOC*nc+7XOw}=>94nU8LJ`{vItjZ& zGvgrunj;Vr6OCcbJU*++1G%8|k@kB_&DtYOVqE`7< z%TAC2R&y27HY`LUMmp3Qq9#_=)h*S^R;_86qg7VuReGgXRZ&$@vAC*QE31u0n$2dD zI_59X7i(4ef~q;VC?I$kpyX{`EAqCKz!L66icwFB}sX(Bk zxl$m;`3^1)a6 z;A?#F4L{Vc+R21Mn*n{dg$%~KQi5U%pqGn~h>(TD$8g?~TsCm0Wp!)FY? z9dxDt=NQiC^#kGRy#63u&g(GQ{gd%PSp8X1soYw&#p0^o332e%BxxjF|#!CD$;aKo}{X5|@p4Z6# zeELblc=G)4HdWcXc-|1lr_bqfDX;x`!& z=ikh5{EI>Q|Dq579)-V>_+Mi@oc}E!p7#|Vx$Yk^9=Z;bCIvr0c)Nm2{pSeBg2!_=;c7hhDm*ft`xp<;!*>`y1#HMX z|B&HR8GcgXf0O+Goas+v`sWB&{rpbhd6#%DF&^&cb%t|)M$;8p=Fg|ZGoEl9#{HR0 zxa!Yzh37Q!lrbLe&mx9%e^x3y7l>yy)93!IBV6_8NrmTE;t4Yz?oZ5zXS>29_r*@e z!}o;-@j^j=@R3qO6^YApUg)Wgm}53W>DXS`Dr>eCHFd|@;s`xtI;PN@Qz@a_39k`M z#9~$)`r--4(&1xpe#$X}ZJ>a|tk$NmjW^o=Ni^WW5c0b!QrF-M3_1Cl zBKcXeFYi}~p-BI|mJmyzg5xVBK;?P*6kVv`(tlY#4E?gsRVS&2s8iL&?>fX(|EJRU zMotQKl@`BChFiZaczFPk^TPGRv2wgzzu|1-xFjhi$uUd^$$|ZHJbve^Ylr>)lsEGG Y1u+zL{&?3OZu}wg-*iW1U+Vb30lPddBLDyZ literal 0 HcmV?d00001 diff --git a/src/seccomp.c b/src/seccomp.c new file mode 100644 index 0000000..9015cb2 --- /dev/null +++ b/src/seccomp.c @@ -0,0 +1,50 @@ +#include "container.h" +#include "container_seccomp.h" +#include +#include + +#define SCMP_FAIL SCMP_ACT_ERRNO(EPERM) + +int syscalls(void) +{ + scmp_filter_ctx ctx = NULL; + fprintf(stderr, "=> filtering syscalls..."); + if (!(ctx = seccomp_init(SCMP_ACT_ALLOW)) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(chmod), 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(chmod), 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(fchmod), 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(fchmod), 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(fchmodat), 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISUID, S_ISUID)) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(fchmodat), 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, S_ISGID, S_ISGID)) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(unshare), 1, + SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(clone), 1, + SCMP_A0(SCMP_CMP_MASKED_EQ, CLONE_NEWUSER, CLONE_NEWUSER)) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(ioctl), 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, TIOCSTI, TIOCSTI)) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(keyctl), 0) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(add_key), 0) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(request_key), 0) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(ptrace), 0) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(mbind), 0) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(migrate_pages), 0) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(move_pages), 0) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(set_mempolicy), 0) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(userfaultfd), 0) + || seccomp_rule_add(ctx, SCMP_FAIL, SCMP_SYS(perf_event_open), 0) + || seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, 0) + || seccomp_load(ctx)) { + if (ctx) seccomp_release(ctx); + fprintf(stderr, "failed: %m\n"); + return 1; + } + seccomp_release(ctx); + fprintf(stderr, "done.\n"); + return 0; +} diff --git a/src/seccomp.o b/src/seccomp.o new file mode 100644 index 0000000000000000000000000000000000000000..6003b926f56b11e4d3cb0cbe9b3146fe5ce66547 GIT binary patch literal 4016 zcmbtXUuYaf7@x}}X{`Bk7SY)Hu!=2dG26=pYb+SA#%uRMik4PIA?YT!XAUoyuzzYQ zXe#Z7hQ7p?;$t5~3W9{fLYs#)V&K|7*gp6mK1d#_1gsK$kSOB#eX}!nvzwg|^n<CL(M20UDiQnlV?{FwTFtLAoyN1B+T=6`8fd{T=WEXKSR5z1P|M z5|SUZQE#1kYt&o4f#k)v;R6D+5M!=(9rQ4UaehVfm%IGC&FF7Abi2ac-Xeg4`+MwN zcYBX|x2U(g9kop6x<3+Xd=frQVg$?K@j$`xJ9~E_8c=yS>P>osVWa zPrZxOJGULTchay4{%J~S6NIn%MbJX${y3;%+WwSK#fAik&;ThkKaM2B_g${MUq9kA(;gkU|5r-cGFFAwq`h>fQK8nti`_)xp46a(+t@-s4K%R%jx~O{Sfp~cu-OwoFJ*`OlnX2j<$Ko z=1IWtLHQQHM~-x6W}AjOTGMx1fWq*k4_rty`HkJr_u}>vP8opr)e z!WjL?_)EM5saI<;+*jO&{o(B9CMGy28b;1_zQe-=3P01G3xc$Fwz=J0M literal 0 HcmV?d00001 diff --git a/src/userns.c b/src/userns.c new file mode 100644 index 0000000..b5b2e68 --- /dev/null +++ b/src/userns.c @@ -0,0 +1,68 @@ +#include "container.h" +#include "userns.h" + +int handle_child_uid_map(pid_t child_pid, int fd) +{ + int uid_map = 0; + int has_userns = -1; + if (read(fd, &has_userns, sizeof(has_userns)) != sizeof(has_userns)) { + fprintf(stderr, "couldn't read from child!\n"); + return -1; + } + if (has_userns) { + char path[PATH_MAX] = {0}; + for (char **file = (char *[]) { "uid_map", "gid_map", 0 }; *file; file++) { + if (snprintf(path, sizeof(path), "/proc/%d/%s", child_pid, *file) + > sizeof(path)) { + fprintf(stderr, "snprintf too big? %m\n"); + return -1; + } + fprintf(stderr, "writing %s...", path); + if ((uid_map = open(path, O_WRONLY)) == -1) { + fprintf(stderr, "open failed: %m\n"); + return -1; + } + if (dprintf(uid_map, "0 %d %d\n", USERNS_OFFSET, USERNS_COUNT) == -1) { + fprintf(stderr, "dprintf failed: %m\n"); + close(uid_map); + return -1; + } + close(uid_map); + } + } + if (write(fd, & (int) { 0 }, sizeof(int)) != sizeof(int)) { + fprintf(stderr, "couldn't write: %m\n"); + return -1; + } + return 0; +} + +int userns(struct child_config *config) +{ + fprintf(stderr, "=> trying a user namespace..."); + int has_userns = !unshare(CLONE_NEWUSER); + if (write(config->fd, &has_userns, sizeof(has_userns)) != sizeof(has_userns)) { + fprintf(stderr, "couldn't write: %m\n"); + return -1; + } + int result = 0; + if (read(config->fd, &result, sizeof(result)) != sizeof(result)) { + fprintf(stderr, "couldn't read: %m\n"); + return -1; + } + if (result) return -1; + if (has_userns) { + fprintf(stderr, "done.\n"); + } else { + fprintf(stderr, "unsupported? continuing.\n"); + } + fprintf(stderr, "=> switching to uid %d / gid %d...", config->uid, config->uid); + if (setgroups(1, & (gid_t) { config->uid }) || + setresgid(config->uid, config->uid, config->uid) || + setresuid(config->uid, config->uid, config->uid)) { + fprintf(stderr, "%m\n"); + return -1; + } + fprintf(stderr, "done.\n"); + return 0; +} diff --git a/src/userns.o b/src/userns.o new file mode 100644 index 0000000000000000000000000000000000000000..2b0307e63a99191abc8ec49b3b2c682eb845dc87 GIT binary patch literal 4680 zcmbtWZ)_Ar6rbxAsFgn_il~6DV4)~%%MleT(8GGNCSn>A6g676ciZ;#?)Kc>6G}pi zsU>bM7yTf7Gx5(C5))%$P^b!Lh#ne3Y7EiDFC>Pf1dK&XavvJ%d$VtjVR{=9eC^!K z{N}yiy!YnK?!J|L=BcJggb9hTt?b%Npp3OFx;9SmVS+7S^Vu%LKX1H!sW--$|J}EL z&&>Eoj>2y>IfY8q zsXxd2a&p}8leG)WP_>k-8KsueK@7qubNZ+KQ~obwsCt}1fQDb?)ZDEth`hOqd_T_l ze%=^#>Qf6&|47MnUUH| z@xf46!IsX0JALuBYk)D5thEHGVFo0B^z2nU+|xYV@{ZHtr>cc+7i=L(v#z zX#S5Uz=7ZjjaG_J*3BVbM3!i-UqsMYc_WQ_9JBWhj#MN&Bu+YT%r#>vyO>Sq*La#^ znQ3jnv2$8#D3eX!-^z-abYIRautDlO3XYxXXiImrxy;QMoJ`&u&^+7L`ZI&wT3fD_ z?Qt?*CO@dPxw@`1yI|$D0W*`e(p`MHQ)^4Ze=AFqvAkG!1WByGV9Qp`bB2-8)QYa< zXn8Ydxdk(2A%EQhT@MV>cHYv#^SoOu6l}+{(%o9h&Vy4$V1X3|lFLtiw?DgUt+t_4kLw$>cxSw`b5s0bt$kZI({J{hw6K1ozDbMg z8{=y+pLgI7NMzqm7TFt%ESo>C<$WlC<$z&B+17+V3u6bGdKS!$Zf*h&e*&vI(S~mj#-i}oNTR^hNTQ6F`mCiWg9lv$iL%E2Mj76^>jhHH z7E6Be+yHhTGy5o-V=fwA$V^HzAglR4seWD#L3SZu$M#htq|M} z!G}X|hW97$>M1s4=F?fLkKgh>amVorG1p65j>87{4Xj^rHW1uaexJqFWvQ&~T11NX zZOC*i=33sMV;2kXe6T#na^X(V2=)QTK)QmCR~nxEG=5cpY$qJA#;p?m0^z7DSK%v! zW5v#gCU_1LjvT1JjOJe`_$r$JNxk65IzqV;bllGcFc$n#yl^0@pDlz7o<{>UwuEr> zb0IXrvpR$)uJG(2p6(Ey-4c$UhJt@6gdbnDD8kQ|f+^c4@yLE2QuMQ=|B0lJ_f+ir zIpJ!4zEpUM#8Z)Yi-dmNA~}mgrkkv_bnJl4kIdgruN0N1i*UhnCZJ*6gsb=02;q5A;WGf6xLBpx}g zk0pEw=!(3Zl<+$x{JexOmGDWz)%;u`T;%6=s_!OD42b&Nh!L*xtR!6Uh&->6c;q_N zID7CMqa)==Lczs*is|lf&NKU=_ne>~5+ldTn!0E0^_ZSEJ(KDEuFG`C4kjr(m$UM) zmbX1ihcBx2u4krR2L&{9I&Lv#>tKYOH*3EEY>`twQ<$>7wEt3N zL*YN_pgaZ*RXTAZ$$J#{BNB!~K_~hrBurNQf$U`r$HGqF&l9Hle~RpP3q|Tx|91l3 z===(JsX;_scz$Rr?8W(^KkD89F^vi;L5ujTyak$SkC=LP0I^p|aReulsPW^wu95vm M7XSbN literal 0 HcmV?d00001 diff --git a/test-container.sh b/test-container.sh new file mode 100644 index 0000000..fc3f663 --- /dev/null +++ b/test-container.sh @@ -0,0 +1,52 @@ +#!/bin/sh + +echo "=========================================" +echo " Container Environment Test" +echo "=========================================" + +echo "" +echo "[1] Process Info" +echo " PID: $$" +echo " User: $(id)" +echo " Home: $HOME" +echo " Shell: $SHELL" + +echo "" +echo "[2] Hostname" +echo " $(hostname)" + +echo "" +echo "[3] Process List" +ps aux 2>/dev/null || echo " ps not available, using /proc" +echo " PIDs in /proc:" +ls -d /proc/[0-9]* 2>/dev/null | head -20 + +echo "" +echo "[4] Network Interfaces" +ip addr 2>/dev/null || ifconfig 2>/dev/null || echo " no network tools, reading /proc/net" +cat /proc/net/dev 2>/dev/null | head -5 + +echo "" +echo "[5] Mount Points" +mount 2>/dev/null || cat /proc/mounts 2>/dev/null | head -20 + +echo "" +echo "[6] Filesystem Root" +ls / + +echo "" +echo "[7] Memory Limits" +cat /proc/meminfo 2>/dev/null | head -3 + +echo "" +echo "[8] Capabilities" +cat /proc/self/status 2>/dev/null | grep -i cap + +echo "" +echo "[9] Seccomp Mode" +cat /proc/self/status 2>/dev/null | grep Seccomp + +echo "" +echo "=========================================" +echo " Test Complete" +echo "========================================="