From a3914c63375596f3ef9327a7c88999eb3195373e Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Sun, 12 Apr 2026 10:29:52 -0700 Subject: [PATCH 1/3] fix(devcontainer): support bubblewrap inside docker Co-authored-by: Codex --- .devcontainer/Dockerfile | 4 +- .devcontainer/Dockerfile.secure | 3 + .devcontainer/README.md | 20 +- .devcontainer/devcontainer.json | 12 +- .devcontainer/devcontainer.secure.json | 7 + .devcontainer/seccomp-bubblewrap.json | 352 +++++++++++++++++++++++++ 6 files changed, 394 insertions(+), 4 deletions(-) create mode 100644 .devcontainer/seccomp-bubblewrap.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 8c3a8597795..98e751ba721 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -8,10 +8,12 @@ RUN apt-get update && \ add-apt-repository --yes universe # now install build deps +# Devcontainers run as a non-root user, so enable bubblewrap's setuid mode. RUN apt-get update && \ apt-get install -y --no-install-recommends \ build-essential curl git ca-certificates \ - pkg-config libcap-dev clang musl-tools libssl-dev just && \ + pkg-config libcap-dev clang musl-tools libssl-dev just bubblewrap && \ + chmod u+s /usr/bin/bwrap && \ rm -rf /var/lib/apt/lists/* # Ubuntu 24.04 ships with user 'ubuntu' already created with UID 1000. diff --git a/.devcontainer/Dockerfile.secure b/.devcontainer/Dockerfile.secure index aaff9eb2d39..f5f4d016d93 100644 --- a/.devcontainer/Dockerfile.secure +++ b/.devcontainer/Dockerfile.secure @@ -10,6 +10,7 @@ ENV TZ="$TZ" SHELL ["/bin/bash", "-o", "pipefail", "-c"] +# Devcontainers run as a non-root user, so enable bubblewrap's setuid mode. RUN apt-get update \ && apt-get install -y --no-install-recommends \ build-essential \ @@ -37,6 +38,8 @@ RUN apt-get update \ ipset \ iptables \ aggregate \ + bubblewrap \ + && chmod u+s /usr/bin/bwrap \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/.devcontainer/README.md b/.devcontainer/README.md index a5e114d37a4..18201abbb99 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -7,13 +7,15 @@ We provide two container paths: ## Codex contributor profile -Use `devcontainer.json` when you are developing Codex itself. This is the same lightweight arm64 container that already exists in the repo. +Use `devcontainer.json` when you are developing Codex itself. This is the same lightweight arm64 container that already exists in the repo, with Docker security options that allow Codex's Linux bubblewrap sandbox to run inside the container. ## Secure customer profile Use `devcontainer.secure.json` when you want a stricter runtime profile for running Codex inside a project container: - installs the Codex CLI plus common build tools +- installs bubblewrap in setuid mode for Codex's Linux sandbox +- uses `.devcontainer/seccomp-bubblewrap.json`, a Docker seccomp profile based on Docker's default profile with the namespace and mount syscalls bubblewrap needs - enables firewall startup with an allowlist-driven outbound policy - blocks IPv6 by default so the allowlist cannot be bypassed over AAAA routes - requires `NET_ADMIN` and `NET_RAW` so the firewall can be installed at startup @@ -35,7 +37,19 @@ To build the contributor image locally for x64 and then run it with the repo mou ```shell CODEX_DOCKER_IMAGE_NAME=codex-linux-dev docker build --platform=linux/amd64 -t "$CODEX_DOCKER_IMAGE_NAME" ./.devcontainer -docker run --platform=linux/amd64 --rm -it -e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64 -v "$PWD":/workspace -w /workspace/codex-rs "$CODEX_DOCKER_IMAGE_NAME" +docker run --platform=linux/amd64 --rm -it \ + --cap-add=SYS_ADMIN \ + --cap-add=SYS_CHROOT \ + --cap-add=NET_ADMIN \ + --cap-add=SETUID \ + --cap-add=SETGID \ + --cap-add=SYS_PTRACE \ + --security-opt "seccomp=$PWD/.devcontainer/seccomp-bubblewrap.json" \ + --security-opt apparmor=unconfined \ + -e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64 \ + -v "$PWD":/workspace \ + -w /workspace/codex-rs \ + "$CODEX_DOCKER_IMAGE_NAME" ``` Note that `/workspace/target` will contain the binaries built for your host platform, so we include `-e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64` in the `docker run` command so that the binaries built inside your container are written to a separate directory. @@ -43,3 +57,5 @@ Note that `/workspace/target` will contain the binaries built for your host plat For arm64, specify `--platform=linux/arm64` instead for both `docker build` and `docker run`. Currently, the contributor `Dockerfile` works for both x64 and arm64 Linux, though you need to run `rustup target add x86_64-unknown-linux-musl` yourself to install the musl toolchain for x64. + +The capability, seccomp, and AppArmor options are required when you want Codex's bubblewrap sandbox to run inside Docker as the non-root devcontainer user. Without them, Docker's default runtime profile can block bubblewrap's namespace setup before Codex's own seccomp filter is installed. diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 1bed79c3ca3..433978129fe 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,17 @@ /* Force VS Code to run the container as arm64 in case your host is x86 (or vice-versa). */ - "runArgs": ["--platform=linux/arm64"], + "runArgs": [ + "--platform=linux/arm64", + "--cap-add=SYS_ADMIN", + "--cap-add=SYS_CHROOT", + "--cap-add=NET_ADMIN", + "--cap-add=SETUID", + "--cap-add=SETGID", + "--cap-add=SYS_PTRACE", + "--security-opt=seccomp=${localWorkspaceFolder}/.devcontainer/seccomp-bubblewrap.json", + "--security-opt=apparmor=unconfined" + ], "containerEnv": { "RUST_BACKTRACE": "1", diff --git a/.devcontainer/devcontainer.secure.json b/.devcontainer/devcontainer.secure.json index a7f1f6c6546..7d26e3d7f17 100644 --- a/.devcontainer/devcontainer.secure.json +++ b/.devcontainer/devcontainer.secure.json @@ -12,6 +12,13 @@ } }, "runArgs": [ + "--cap-add=SYS_ADMIN", + "--cap-add=SYS_CHROOT", + "--cap-add=SETUID", + "--cap-add=SETGID", + "--cap-add=SYS_PTRACE", + "--security-opt=seccomp=${localWorkspaceFolder}/.devcontainer/seccomp-bubblewrap.json", + "--security-opt=apparmor=unconfined", "--cap-add=NET_ADMIN", "--cap-add=NET_RAW" ], diff --git a/.devcontainer/seccomp-bubblewrap.json b/.devcontainer/seccomp-bubblewrap.json new file mode 100644 index 00000000000..527e5bf193c --- /dev/null +++ b/.devcontainer/seccomp-bubblewrap.json @@ -0,0 +1,352 @@ +{ + "defaultAction": "SCMP_ACT_ERRNO", + "defaultErrnoRet": 1, + "archMap": [ + { + "architecture": "SCMP_ARCH_X86_64", + "subArchitectures": ["SCMP_ARCH_X86", "SCMP_ARCH_X32"] + }, + { + "architecture": "SCMP_ARCH_AARCH64", + "subArchitectures": ["SCMP_ARCH_ARM"] + }, + { + "architecture": "SCMP_ARCH_MIPS64", + "subArchitectures": ["SCMP_ARCH_MIPS", "SCMP_ARCH_MIPS64N32"] + }, + { + "architecture": "SCMP_ARCH_MIPS64N32", + "subArchitectures": ["SCMP_ARCH_MIPS", "SCMP_ARCH_MIPS64"] + }, + { + "architecture": "SCMP_ARCH_MIPSEL64", + "subArchitectures": ["SCMP_ARCH_MIPSEL", "SCMP_ARCH_MIPSEL64N32"] + }, + { + "architecture": "SCMP_ARCH_MIPSEL64N32", + "subArchitectures": ["SCMP_ARCH_MIPSEL", "SCMP_ARCH_MIPSEL64"] + }, + { + "architecture": "SCMP_ARCH_S390X", + "subArchitectures": ["SCMP_ARCH_S390"] + }, + { + "architecture": "SCMP_ARCH_RISCV64", + "subArchitectures": null + } + ], + "syscalls": [ + { + "names": [ + "accept", "accept4", "access", "adjtimex", "alarm", "bind", "brk", "capget", + "capset", "chdir", "chmod", "chown", "chown32", "clock_adjtime", "clock_adjtime64", "clock_getres", + "clock_getres_time64", "clock_gettime", "clock_gettime64", "clock_nanosleep", "clock_nanosleep_time64", "close", "close_range", "connect", + "copy_file_range", "creat", "dup", "dup2", "dup3", "epoll_create", "epoll_create1", "epoll_ctl", + "epoll_ctl_old", "epoll_pwait", "epoll_pwait2", "epoll_wait", "epoll_wait_old", "eventfd", "eventfd2", "execve", + "execveat", "exit", "exit_group", "faccessat", "faccessat2", "fadvise64", "fadvise64_64", "fallocate", + "fanotify_mark", "fchdir", "fchmod", "fchmodat", "fchown", "fchown32", "fchownat", "fcntl", + "fcntl64", "fdatasync", "fgetxattr", "flistxattr", "flock", "fork", "fremovexattr", "fsetxattr", + "fstat", "fstat64", "fstatat64", "fstatfs", "fstatfs64", "fsync", "ftruncate", "ftruncate64", + "futex", "futex_time64", "futex_waitv", "futimesat", "getcpu", "getcwd", "getdents", "getdents64", + "getegid", "getegid32", "geteuid", "geteuid32", "getgid", "getgid32", "getgroups", "getgroups32", + "getitimer", "getpeername", "getpgid", "getpgrp", "getpid", "getppid", "getpriority", "getrandom", + "getresgid", "getresgid32", "getresuid", "getresuid32", "getrlimit", "get_robust_list", "getrusage", "getsid", + "getsockname", "getsockopt", "get_thread_area", "gettid", "gettimeofday", "getuid", "getuid32", "getxattr", + "inotify_add_watch", "inotify_init", "inotify_init1", "inotify_rm_watch", "io_cancel", "ioctl", "io_destroy", "io_getevents", + "io_pgetevents", "io_pgetevents_time64", "ioprio_get", "ioprio_set", "io_setup", "io_submit", "ipc", "kill", + "landlock_add_rule", "landlock_create_ruleset", "landlock_restrict_self", "lchown", "lchown32", "lgetxattr", "link", "linkat", + "listen", "listxattr", "llistxattr", "_llseek", "lremovexattr", "lseek", "lsetxattr", "lstat", + "lstat64", "madvise", "membarrier", "memfd_create", "memfd_secret", "mincore", "mkdir", "mkdirat", + "mknod", "mknodat", "mlock", "mlock2", "mlockall", "mmap", "mmap2", "mprotect", + "mq_getsetattr", "mq_notify", "mq_open", "mq_timedreceive", "mq_timedreceive_time64", "mq_timedsend", "mq_timedsend_time64", "mq_unlink", + "mremap", "msgctl", "msgget", "msgrcv", "msgsnd", "msync", "munlock", "munlockall", + "munmap", "name_to_handle_at", "nanosleep", "newfstatat", "_newselect", "open", "openat", "openat2", + "pause", "pidfd_open", "pidfd_send_signal", "pipe", "pipe2", "pkey_alloc", "pkey_free", "pkey_mprotect", + "poll", "ppoll", "ppoll_time64", "prctl", "pread64", "preadv", "preadv2", "prlimit64", + "process_mrelease", "pselect6", "pselect6_time64", "pwrite64", "pwritev", "pwritev2", "read", "readahead", + "readlink", "readlinkat", "readv", "recv", "recvfrom", "recvmmsg", "recvmmsg_time64", "recvmsg", + "remap_file_pages", "removexattr", "rename", "renameat", "renameat2", "restart_syscall", "rmdir", "rseq", + "rt_sigaction", "rt_sigpending", "rt_sigprocmask", "rt_sigqueueinfo", "rt_sigreturn", "rt_sigsuspend", "rt_sigtimedwait", "rt_sigtimedwait_time64", + "rt_tgsigqueueinfo", "sched_getaffinity", "sched_getattr", "sched_getparam", "sched_get_priority_max", "sched_get_priority_min", "sched_getscheduler", "sched_rr_get_interval", + "sched_rr_get_interval_time64", "sched_setaffinity", "sched_setattr", "sched_setparam", "sched_setscheduler", "sched_yield", "seccomp", "select", + "semctl", "semget", "semop", "semtimedop", "semtimedop_time64", "send", "sendfile", "sendfile64", + "sendmmsg", "sendmsg", "sendto", "setfsgid", "setfsgid32", "setfsuid", "setfsuid32", "setgid", + "setgid32", "setgroups", "setgroups32", "setitimer", "setpgid", "setpriority", "setregid", "setregid32", + "setresgid", "setresgid32", "setresuid", "setresuid32", "setreuid", "setreuid32", "setrlimit", "set_robust_list", + "setsid", "setsockopt", "set_thread_area", "set_tid_address", "setuid", "setuid32", "setxattr", "shmat", + "shmctl", "shmdt", "shmget", "shutdown", "sigaltstack", "signalfd", "signalfd4", "sigprocmask", + "sigreturn", "socketcall", "socketpair", "splice", "stat", "stat64", "statfs", "statfs64", + "statx", "symlink", "symlinkat", "sync", "sync_file_range", "syncfs", "sysinfo", "tee", + "tgkill", "time", "timer_create", "timer_delete", "timer_getoverrun", "timer_gettime", "timer_gettime64", "timer_settime", + "timer_settime64", "timerfd_create", "timerfd_gettime", "timerfd_gettime64", "timerfd_settime", "timerfd_settime64", "times", "tkill", + "truncate", "truncate64", "ugetrlimit", "umask", "uname", "unlink", "unlinkat", "utime", + "utimensat", "utimensat_time64", "utimes", "vfork", "vmsplice", "wait4", "waitid", "waitpid", + "write", "writev" + ], + "action": "SCMP_ACT_ALLOW" + }, + { + "names": [ + "clone", "clone3", "fsconfig", "fsmount", "fsopen", "fspick", "mount", "mount_setattr", + "move_mount", "open_tree", "pivot_root", "setdomainname", "sethostname", "setns", "umount", "umount2", + "unshare" + ], + "action": "SCMP_ACT_ALLOW", + "comment": "Allow bubblewrap to create user namespaces and construct the sandbox mount tree inside Docker." + }, + { + "names": ["process_vm_readv", "process_vm_writev", "ptrace"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "minKernel": "4.8" + } + }, + { + "names": ["socket"], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 40, + "op": "SCMP_CMP_NE" + } + ] + }, + { + "names": ["personality"], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 0, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": ["personality"], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 8, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": ["personality"], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 131072, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": ["personality"], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 131080, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": ["personality"], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 4294967295, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": ["sync_file_range2", "swapcontext"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": ["ppc64le"] + } + }, + { + "names": [ + "arm_fadvise64_64", "arm_sync_file_range", "sync_file_range2", "breakpoint", "cacheflush", "set_tls" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": ["arm", "arm64"] + } + }, + { + "names": ["arch_prctl"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": ["amd64", "x32"] + } + }, + { + "names": ["modify_ldt"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": ["amd64", "x32", "x86"] + } + }, + { + "names": ["s390_pci_mmio_read", "s390_pci_mmio_write", "s390_runtime_instr"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": ["s390", "s390x"] + } + }, + { + "names": ["riscv_flush_icache"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "arches": ["riscv64"] + } + }, + { + "names": ["open_by_handle_at"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_DAC_READ_SEARCH"] + } + }, + { + "names": [ + "bpf", "clone", "clone3", "fanotify_init", "fsconfig", "fsmount", "fsopen", "fspick", + "lookup_dcookie", "mount", "mount_setattr", "move_mount", "open_tree", "perf_event_open", "quotactl", "quotactl_fd", + "setdomainname", "sethostname", "setns", "syslog", "umount", "umount2", "unshare" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYS_ADMIN"] + } + }, + { + "names": ["clone"], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 0, + "value": 2114060288, + "op": "SCMP_CMP_MASKED_EQ" + } + ], + "excludes": { + "caps": ["CAP_SYS_ADMIN"], + "arches": ["s390", "s390x"] + } + }, + { + "names": ["clone"], + "action": "SCMP_ACT_ALLOW", + "args": [ + { + "index": 1, + "value": 2114060288, + "op": "SCMP_CMP_MASKED_EQ" + } + ], + "comment": "s390 parameter ordering for clone is different", + "includes": { + "arches": ["s390", "s390x"] + }, + "excludes": { + "caps": ["CAP_SYS_ADMIN"] + } + }, + { + "names": ["reboot"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYS_BOOT"] + } + }, + { + "names": ["chroot"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYS_CHROOT"] + } + }, + { + "names": ["delete_module", "init_module", "finit_module"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYS_MODULE"] + } + }, + { + "names": ["acct"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYS_PACCT"] + } + }, + { + "names": [ + "kcmp", "pidfd_getfd", "process_madvise", "process_vm_readv", "process_vm_writev", "ptrace" + ], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYS_PTRACE"] + } + }, + { + "names": ["iopl", "ioperm"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYS_RAWIO"] + } + }, + { + "names": ["settimeofday", "stime", "clock_settime", "clock_settime64"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYS_TIME"] + } + }, + { + "names": ["vhangup"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYS_TTY_CONFIG"] + } + }, + { + "names": ["get_mempolicy", "mbind", "set_mempolicy"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYS_NICE"] + } + }, + { + "names": ["syslog"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_SYSLOG"] + } + }, + { + "names": ["bpf"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_BPF"] + } + }, + { + "names": ["perf_event_open"], + "action": "SCMP_ACT_ALLOW", + "includes": { + "caps": ["CAP_PERFMON"] + } + } + ] +} From 995f1fecbcf83082d6449475a225e61ff0410881 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Sun, 12 Apr 2026 10:34:17 -0700 Subject: [PATCH 2/3] fix(devcontainer): scope bubblewrap setup to secure profile Co-authored-by: Codex --- .devcontainer/Dockerfile | 4 +--- .devcontainer/README.md | 18 +++--------------- .devcontainer/devcontainer.json | 12 +----------- 3 files changed, 5 insertions(+), 29 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 98e751ba721..8c3a8597795 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -8,12 +8,10 @@ RUN apt-get update && \ add-apt-repository --yes universe # now install build deps -# Devcontainers run as a non-root user, so enable bubblewrap's setuid mode. RUN apt-get update && \ apt-get install -y --no-install-recommends \ build-essential curl git ca-certificates \ - pkg-config libcap-dev clang musl-tools libssl-dev just bubblewrap && \ - chmod u+s /usr/bin/bwrap && \ + pkg-config libcap-dev clang musl-tools libssl-dev just && \ rm -rf /var/lib/apt/lists/* # Ubuntu 24.04 ships with user 'ubuntu' already created with UID 1000. diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 18201abbb99..9cfff2aea9a 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -7,7 +7,7 @@ We provide two container paths: ## Codex contributor profile -Use `devcontainer.json` when you are developing Codex itself. This is the same lightweight arm64 container that already exists in the repo, with Docker security options that allow Codex's Linux bubblewrap sandbox to run inside the container. +Use `devcontainer.json` when you are developing Codex itself. This is the same lightweight arm64 container that already exists in the repo. ## Secure customer profile @@ -37,19 +37,7 @@ To build the contributor image locally for x64 and then run it with the repo mou ```shell CODEX_DOCKER_IMAGE_NAME=codex-linux-dev docker build --platform=linux/amd64 -t "$CODEX_DOCKER_IMAGE_NAME" ./.devcontainer -docker run --platform=linux/amd64 --rm -it \ - --cap-add=SYS_ADMIN \ - --cap-add=SYS_CHROOT \ - --cap-add=NET_ADMIN \ - --cap-add=SETUID \ - --cap-add=SETGID \ - --cap-add=SYS_PTRACE \ - --security-opt "seccomp=$PWD/.devcontainer/seccomp-bubblewrap.json" \ - --security-opt apparmor=unconfined \ - -e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64 \ - -v "$PWD":/workspace \ - -w /workspace/codex-rs \ - "$CODEX_DOCKER_IMAGE_NAME" +docker run --platform=linux/amd64 --rm -it -e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64 -v "$PWD":/workspace -w /workspace/codex-rs "$CODEX_DOCKER_IMAGE_NAME" ``` Note that `/workspace/target` will contain the binaries built for your host platform, so we include `-e CARGO_TARGET_DIR=/workspace/codex-rs/target-amd64` in the `docker run` command so that the binaries built inside your container are written to a separate directory. @@ -58,4 +46,4 @@ For arm64, specify `--platform=linux/arm64` instead for both `docker build` and Currently, the contributor `Dockerfile` works for both x64 and arm64 Linux, though you need to run `rustup target add x86_64-unknown-linux-musl` yourself to install the musl toolchain for x64. -The capability, seccomp, and AppArmor options are required when you want Codex's bubblewrap sandbox to run inside Docker as the non-root devcontainer user. Without them, Docker's default runtime profile can block bubblewrap's namespace setup before Codex's own seccomp filter is installed. +The secure profile's capability, seccomp, and AppArmor options are required when you want Codex's bubblewrap sandbox to run inside Docker as the non-root devcontainer user. Without them, Docker's default runtime profile can block bubblewrap's namespace setup before Codex's own seccomp filter is installed. diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 433978129fe..1bed79c3ca3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,17 +8,7 @@ /* Force VS Code to run the container as arm64 in case your host is x86 (or vice-versa). */ - "runArgs": [ - "--platform=linux/arm64", - "--cap-add=SYS_ADMIN", - "--cap-add=SYS_CHROOT", - "--cap-add=NET_ADMIN", - "--cap-add=SETUID", - "--cap-add=SETGID", - "--cap-add=SYS_PTRACE", - "--security-opt=seccomp=${localWorkspaceFolder}/.devcontainer/seccomp-bubblewrap.json", - "--security-opt=apparmor=unconfined" - ], + "runArgs": ["--platform=linux/arm64"], "containerEnv": { "RUST_BACKTRACE": "1", From 5cc7c332aa76f8af4d65c098850789d412e45066 Mon Sep 17 00:00:00 2001 From: viyatb-oai Date: Sun, 12 Apr 2026 10:38:26 -0700 Subject: [PATCH 3/3] fix(devcontainer): simplify docker seccomp setup Co-authored-by: Codex --- .devcontainer/README.md | 4 +- .devcontainer/devcontainer.secure.json | 2 +- .devcontainer/seccomp-bubblewrap.json | 352 ------------------------- 3 files changed, 3 insertions(+), 355 deletions(-) delete mode 100644 .devcontainer/seccomp-bubblewrap.json diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 9cfff2aea9a..3b07f2b4408 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -15,7 +15,7 @@ Use `devcontainer.secure.json` when you want a stricter runtime profile for runn - installs the Codex CLI plus common build tools - installs bubblewrap in setuid mode for Codex's Linux sandbox -- uses `.devcontainer/seccomp-bubblewrap.json`, a Docker seccomp profile based on Docker's default profile with the namespace and mount syscalls bubblewrap needs +- disables Docker's outer seccomp and AppArmor profiles so bubblewrap can construct Codex's inner sandbox - enables firewall startup with an allowlist-driven outbound policy - blocks IPv6 by default so the allowlist cannot be bypassed over AAAA routes - requires `NET_ADMIN` and `NET_RAW` so the firewall can be installed at startup @@ -46,4 +46,4 @@ For arm64, specify `--platform=linux/arm64` instead for both `docker build` and Currently, the contributor `Dockerfile` works for both x64 and arm64 Linux, though you need to run `rustup target add x86_64-unknown-linux-musl` yourself to install the musl toolchain for x64. -The secure profile's capability, seccomp, and AppArmor options are required when you want Codex's bubblewrap sandbox to run inside Docker as the non-root devcontainer user. Without them, Docker's default runtime profile can block bubblewrap's namespace setup before Codex's own seccomp filter is installed. +The secure profile's capability, seccomp, and AppArmor options are required when you want Codex's bubblewrap sandbox to run inside Docker as the non-root devcontainer user. Without them, Docker's default runtime profile can block bubblewrap's namespace setup before Codex's own seccomp filter is installed. This keeps the Docker relaxation explicit in the profile that is meant to run Codex inside a project container, while the default contributor profile stays lightweight. diff --git a/.devcontainer/devcontainer.secure.json b/.devcontainer/devcontainer.secure.json index 7d26e3d7f17..f52686986c8 100644 --- a/.devcontainer/devcontainer.secure.json +++ b/.devcontainer/devcontainer.secure.json @@ -17,7 +17,7 @@ "--cap-add=SETUID", "--cap-add=SETGID", "--cap-add=SYS_PTRACE", - "--security-opt=seccomp=${localWorkspaceFolder}/.devcontainer/seccomp-bubblewrap.json", + "--security-opt=seccomp=unconfined", "--security-opt=apparmor=unconfined", "--cap-add=NET_ADMIN", "--cap-add=NET_RAW" diff --git a/.devcontainer/seccomp-bubblewrap.json b/.devcontainer/seccomp-bubblewrap.json deleted file mode 100644 index 527e5bf193c..00000000000 --- a/.devcontainer/seccomp-bubblewrap.json +++ /dev/null @@ -1,352 +0,0 @@ -{ - "defaultAction": "SCMP_ACT_ERRNO", - "defaultErrnoRet": 1, - "archMap": [ - { - "architecture": "SCMP_ARCH_X86_64", - "subArchitectures": ["SCMP_ARCH_X86", "SCMP_ARCH_X32"] - }, - { - "architecture": "SCMP_ARCH_AARCH64", - "subArchitectures": ["SCMP_ARCH_ARM"] - }, - { - "architecture": "SCMP_ARCH_MIPS64", - "subArchitectures": ["SCMP_ARCH_MIPS", "SCMP_ARCH_MIPS64N32"] - }, - { - "architecture": "SCMP_ARCH_MIPS64N32", - "subArchitectures": ["SCMP_ARCH_MIPS", "SCMP_ARCH_MIPS64"] - }, - { - "architecture": "SCMP_ARCH_MIPSEL64", - "subArchitectures": ["SCMP_ARCH_MIPSEL", "SCMP_ARCH_MIPSEL64N32"] - }, - { - "architecture": "SCMP_ARCH_MIPSEL64N32", - "subArchitectures": ["SCMP_ARCH_MIPSEL", "SCMP_ARCH_MIPSEL64"] - }, - { - "architecture": "SCMP_ARCH_S390X", - "subArchitectures": ["SCMP_ARCH_S390"] - }, - { - "architecture": "SCMP_ARCH_RISCV64", - "subArchitectures": null - } - ], - "syscalls": [ - { - "names": [ - "accept", "accept4", "access", "adjtimex", "alarm", "bind", "brk", "capget", - "capset", "chdir", "chmod", "chown", "chown32", "clock_adjtime", "clock_adjtime64", "clock_getres", - "clock_getres_time64", "clock_gettime", "clock_gettime64", "clock_nanosleep", "clock_nanosleep_time64", "close", "close_range", "connect", - "copy_file_range", "creat", "dup", "dup2", "dup3", "epoll_create", "epoll_create1", "epoll_ctl", - "epoll_ctl_old", "epoll_pwait", "epoll_pwait2", "epoll_wait", "epoll_wait_old", "eventfd", "eventfd2", "execve", - "execveat", "exit", "exit_group", "faccessat", "faccessat2", "fadvise64", "fadvise64_64", "fallocate", - "fanotify_mark", "fchdir", "fchmod", "fchmodat", "fchown", "fchown32", "fchownat", "fcntl", - "fcntl64", "fdatasync", "fgetxattr", "flistxattr", "flock", "fork", "fremovexattr", "fsetxattr", - "fstat", "fstat64", "fstatat64", "fstatfs", "fstatfs64", "fsync", "ftruncate", "ftruncate64", - "futex", "futex_time64", "futex_waitv", "futimesat", "getcpu", "getcwd", "getdents", "getdents64", - "getegid", "getegid32", "geteuid", "geteuid32", "getgid", "getgid32", "getgroups", "getgroups32", - "getitimer", "getpeername", "getpgid", "getpgrp", "getpid", "getppid", "getpriority", "getrandom", - "getresgid", "getresgid32", "getresuid", "getresuid32", "getrlimit", "get_robust_list", "getrusage", "getsid", - "getsockname", "getsockopt", "get_thread_area", "gettid", "gettimeofday", "getuid", "getuid32", "getxattr", - "inotify_add_watch", "inotify_init", "inotify_init1", "inotify_rm_watch", "io_cancel", "ioctl", "io_destroy", "io_getevents", - "io_pgetevents", "io_pgetevents_time64", "ioprio_get", "ioprio_set", "io_setup", "io_submit", "ipc", "kill", - "landlock_add_rule", "landlock_create_ruleset", "landlock_restrict_self", "lchown", "lchown32", "lgetxattr", "link", "linkat", - "listen", "listxattr", "llistxattr", "_llseek", "lremovexattr", "lseek", "lsetxattr", "lstat", - "lstat64", "madvise", "membarrier", "memfd_create", "memfd_secret", "mincore", "mkdir", "mkdirat", - "mknod", "mknodat", "mlock", "mlock2", "mlockall", "mmap", "mmap2", "mprotect", - "mq_getsetattr", "mq_notify", "mq_open", "mq_timedreceive", "mq_timedreceive_time64", "mq_timedsend", "mq_timedsend_time64", "mq_unlink", - "mremap", "msgctl", "msgget", "msgrcv", "msgsnd", "msync", "munlock", "munlockall", - "munmap", "name_to_handle_at", "nanosleep", "newfstatat", "_newselect", "open", "openat", "openat2", - "pause", "pidfd_open", "pidfd_send_signal", "pipe", "pipe2", "pkey_alloc", "pkey_free", "pkey_mprotect", - "poll", "ppoll", "ppoll_time64", "prctl", "pread64", "preadv", "preadv2", "prlimit64", - "process_mrelease", "pselect6", "pselect6_time64", "pwrite64", "pwritev", "pwritev2", "read", "readahead", - "readlink", "readlinkat", "readv", "recv", "recvfrom", "recvmmsg", "recvmmsg_time64", "recvmsg", - "remap_file_pages", "removexattr", "rename", "renameat", "renameat2", "restart_syscall", "rmdir", "rseq", - "rt_sigaction", "rt_sigpending", "rt_sigprocmask", "rt_sigqueueinfo", "rt_sigreturn", "rt_sigsuspend", "rt_sigtimedwait", "rt_sigtimedwait_time64", - "rt_tgsigqueueinfo", "sched_getaffinity", "sched_getattr", "sched_getparam", "sched_get_priority_max", "sched_get_priority_min", "sched_getscheduler", "sched_rr_get_interval", - "sched_rr_get_interval_time64", "sched_setaffinity", "sched_setattr", "sched_setparam", "sched_setscheduler", "sched_yield", "seccomp", "select", - "semctl", "semget", "semop", "semtimedop", "semtimedop_time64", "send", "sendfile", "sendfile64", - "sendmmsg", "sendmsg", "sendto", "setfsgid", "setfsgid32", "setfsuid", "setfsuid32", "setgid", - "setgid32", "setgroups", "setgroups32", "setitimer", "setpgid", "setpriority", "setregid", "setregid32", - "setresgid", "setresgid32", "setresuid", "setresuid32", "setreuid", "setreuid32", "setrlimit", "set_robust_list", - "setsid", "setsockopt", "set_thread_area", "set_tid_address", "setuid", "setuid32", "setxattr", "shmat", - "shmctl", "shmdt", "shmget", "shutdown", "sigaltstack", "signalfd", "signalfd4", "sigprocmask", - "sigreturn", "socketcall", "socketpair", "splice", "stat", "stat64", "statfs", "statfs64", - "statx", "symlink", "symlinkat", "sync", "sync_file_range", "syncfs", "sysinfo", "tee", - "tgkill", "time", "timer_create", "timer_delete", "timer_getoverrun", "timer_gettime", "timer_gettime64", "timer_settime", - "timer_settime64", "timerfd_create", "timerfd_gettime", "timerfd_gettime64", "timerfd_settime", "timerfd_settime64", "times", "tkill", - "truncate", "truncate64", "ugetrlimit", "umask", "uname", "unlink", "unlinkat", "utime", - "utimensat", "utimensat_time64", "utimes", "vfork", "vmsplice", "wait4", "waitid", "waitpid", - "write", "writev" - ], - "action": "SCMP_ACT_ALLOW" - }, - { - "names": [ - "clone", "clone3", "fsconfig", "fsmount", "fsopen", "fspick", "mount", "mount_setattr", - "move_mount", "open_tree", "pivot_root", "setdomainname", "sethostname", "setns", "umount", "umount2", - "unshare" - ], - "action": "SCMP_ACT_ALLOW", - "comment": "Allow bubblewrap to create user namespaces and construct the sandbox mount tree inside Docker." - }, - { - "names": ["process_vm_readv", "process_vm_writev", "ptrace"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "minKernel": "4.8" - } - }, - { - "names": ["socket"], - "action": "SCMP_ACT_ALLOW", - "args": [ - { - "index": 0, - "value": 40, - "op": "SCMP_CMP_NE" - } - ] - }, - { - "names": ["personality"], - "action": "SCMP_ACT_ALLOW", - "args": [ - { - "index": 0, - "value": 0, - "op": "SCMP_CMP_EQ" - } - ] - }, - { - "names": ["personality"], - "action": "SCMP_ACT_ALLOW", - "args": [ - { - "index": 0, - "value": 8, - "op": "SCMP_CMP_EQ" - } - ] - }, - { - "names": ["personality"], - "action": "SCMP_ACT_ALLOW", - "args": [ - { - "index": 0, - "value": 131072, - "op": "SCMP_CMP_EQ" - } - ] - }, - { - "names": ["personality"], - "action": "SCMP_ACT_ALLOW", - "args": [ - { - "index": 0, - "value": 131080, - "op": "SCMP_CMP_EQ" - } - ] - }, - { - "names": ["personality"], - "action": "SCMP_ACT_ALLOW", - "args": [ - { - "index": 0, - "value": 4294967295, - "op": "SCMP_CMP_EQ" - } - ] - }, - { - "names": ["sync_file_range2", "swapcontext"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "arches": ["ppc64le"] - } - }, - { - "names": [ - "arm_fadvise64_64", "arm_sync_file_range", "sync_file_range2", "breakpoint", "cacheflush", "set_tls" - ], - "action": "SCMP_ACT_ALLOW", - "includes": { - "arches": ["arm", "arm64"] - } - }, - { - "names": ["arch_prctl"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "arches": ["amd64", "x32"] - } - }, - { - "names": ["modify_ldt"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "arches": ["amd64", "x32", "x86"] - } - }, - { - "names": ["s390_pci_mmio_read", "s390_pci_mmio_write", "s390_runtime_instr"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "arches": ["s390", "s390x"] - } - }, - { - "names": ["riscv_flush_icache"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "arches": ["riscv64"] - } - }, - { - "names": ["open_by_handle_at"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_DAC_READ_SEARCH"] - } - }, - { - "names": [ - "bpf", "clone", "clone3", "fanotify_init", "fsconfig", "fsmount", "fsopen", "fspick", - "lookup_dcookie", "mount", "mount_setattr", "move_mount", "open_tree", "perf_event_open", "quotactl", "quotactl_fd", - "setdomainname", "sethostname", "setns", "syslog", "umount", "umount2", "unshare" - ], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYS_ADMIN"] - } - }, - { - "names": ["clone"], - "action": "SCMP_ACT_ALLOW", - "args": [ - { - "index": 0, - "value": 2114060288, - "op": "SCMP_CMP_MASKED_EQ" - } - ], - "excludes": { - "caps": ["CAP_SYS_ADMIN"], - "arches": ["s390", "s390x"] - } - }, - { - "names": ["clone"], - "action": "SCMP_ACT_ALLOW", - "args": [ - { - "index": 1, - "value": 2114060288, - "op": "SCMP_CMP_MASKED_EQ" - } - ], - "comment": "s390 parameter ordering for clone is different", - "includes": { - "arches": ["s390", "s390x"] - }, - "excludes": { - "caps": ["CAP_SYS_ADMIN"] - } - }, - { - "names": ["reboot"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYS_BOOT"] - } - }, - { - "names": ["chroot"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYS_CHROOT"] - } - }, - { - "names": ["delete_module", "init_module", "finit_module"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYS_MODULE"] - } - }, - { - "names": ["acct"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYS_PACCT"] - } - }, - { - "names": [ - "kcmp", "pidfd_getfd", "process_madvise", "process_vm_readv", "process_vm_writev", "ptrace" - ], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYS_PTRACE"] - } - }, - { - "names": ["iopl", "ioperm"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYS_RAWIO"] - } - }, - { - "names": ["settimeofday", "stime", "clock_settime", "clock_settime64"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYS_TIME"] - } - }, - { - "names": ["vhangup"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYS_TTY_CONFIG"] - } - }, - { - "names": ["get_mempolicy", "mbind", "set_mempolicy"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYS_NICE"] - } - }, - { - "names": ["syslog"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_SYSLOG"] - } - }, - { - "names": ["bpf"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_BPF"] - } - }, - { - "names": ["perf_event_open"], - "action": "SCMP_ACT_ALLOW", - "includes": { - "caps": ["CAP_PERFMON"] - } - } - ] -}