refactor
This commit is contained in:
@@ -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
|
||||||
@@ -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 <uid> -m <rootfs路径> -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
|
||||||
@@ -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 <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <grp.h>
|
|
||||||
#include <pwd.h>
|
|
||||||
#include <sched.h>
|
|
||||||
#include <seccomp.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/capability.h>
|
|
||||||
#include <sys/mount.h>
|
|
||||||
#include <sys/prctl.h>
|
|
||||||
#include <sys/resource.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#include <sys/sysmacros.h>
|
|
||||||
#include <sys/utsname.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <linux/capability.h>
|
|
||||||
#include <linux/limits.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef CAPABILITIES_H
|
||||||
|
#define CAPABILITIES_H
|
||||||
|
|
||||||
|
int capabilities(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef CONTAINER_H
|
||||||
|
#define CONTAINER_H
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/sysmacros.h>
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/limits.h>
|
||||||
|
|
||||||
|
#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
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#ifndef CONTAINER_SECCOMP_H
|
||||||
|
#define CONTAINER_SECCOMP_H
|
||||||
|
|
||||||
|
int syscalls(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef HOSTNAME_H
|
||||||
|
#define HOSTNAME_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
int choose_hostname(char *buff, size_t len);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef MOUNT_H
|
||||||
|
#define MOUNT_H
|
||||||
|
|
||||||
|
#include "container.h"
|
||||||
|
|
||||||
|
int mounts(struct child_config *config);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/capability.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Binary file not shown.
+142
@@ -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;
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -0,0 +1,32 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <time.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Binary file not shown.
+149
@@ -0,0 +1,149 @@
|
|||||||
|
#include "container.h"
|
||||||
|
#include "mount.h"
|
||||||
|
#include "cgroup.h"
|
||||||
|
#include "capabilities.h"
|
||||||
|
#include "container_seccomp.h"
|
||||||
|
#include "userns.h"
|
||||||
|
#include "hostname.h"
|
||||||
|
|
||||||
|
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 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;
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
+89
@@ -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;
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
@@ -0,0 +1,50 @@
|
|||||||
|
#include "container.h"
|
||||||
|
#include "container_seccomp.h"
|
||||||
|
#include <seccomp.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -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;
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -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 "========================================="
|
||||||
Reference in New Issue
Block a user