Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/container-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,9 @@ jobs:

- name: Verify bundled scanner
run: |
docker run --rm codex-security:ci --version
docker run --rm codex-security:ci bulk-scan --help
docker run --rm codex-security:ci info --json
docker run --rm --cap-drop ALL --security-opt no-new-privileges codex-security:ci --version
docker run --rm --cap-drop ALL --security-opt no-new-privileges codex-security:ci bulk-scan --help
docker run --rm --cap-drop ALL --security-opt no-new-privileges codex-security:ci info --json

- name: Validate hardened customer Compose configuration
env:
Expand Down Expand Up @@ -133,7 +133,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
if output="$(docker run --rm -t codex-security:ci bulk-scan 2>&1)"; then
if output="$(docker run --rm -t --cap-drop ALL --security-opt no-new-privileges codex-security:ci bulk-scan 2>&1)"; then
echo "The customer container must require a repository CSV." >&2
exit 1
else
Expand Down
16 changes: 10 additions & 6 deletions docker/codex-security-seccomp.json
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,6 @@
"signalfd4",
"sigprocmask",
"sigreturn",
"socketcall",
"socketpair",
"splice",
"stat",
Expand Down Expand Up @@ -378,24 +377,29 @@
"action": "SCMP_ACT_ALLOW"
},
{
"names": ["process_vm_readv", "process_vm_writev", "ptrace"],
"names": ["socket"],
"action": "SCMP_ACT_ALLOW",
"includes": { "minKernel": "4.8" }
"comment": "AF_UNIX(1)/AF_INET(2)/AF_INET6(10) only: local IPC + HTTPS for git clone and the OpenAI API. Everything else (notably AF_NETLINK=16, AF_PACKET=17) is denied by defaultAction; the previous 3-rule form here only blocked AF_ALG=38/AF_VSOCK=40 and left NETLINK/PACKET/everything-else allowed.",
"args": [{ "index": 0, "value": 1, "op": "SCMP_CMP_EQ" }]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve AF_NETLINK for Bubblewrap setup

When Codex starts its Linux Bubblewrap sandbox with network isolation, Bubblewrap needs an AF_NETLINK socket to configure the loopback interface, but this allowlist now rejects that socket before RTM_NEWLINK/RTM_NEWADDR can run. The hardened-sandbox workflow in .github/workflows/container-ci.yml lines 122-126 explicitly recognizes this failure and survives only by manually rerunning with use_legacy_landlock; the normal scan path contains no equivalent retry or feature override, so container scans that execute sandboxed commands can fail rather than run.

Useful? React with 👍 / 👎.

Comment on lines +382 to +383

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Block legacy socketcall before claiming a domain allowlist

On the explicitly supported SCMP_ARCH_X86 subarchitecture, a 32-bit executable can create sockets through the multiplexed socketcall syscall, which remains unconditionally allowed at line 326, so these argument filters on the direct socket syscall are bypassed. A bundled or repository-supplied i386 executable can therefore still open AF_NETLINK and other domains that this hardening intends to deny; remove or constrain the legacy path as part of the allowlist.

Useful? React with 👍 / 👎.

},
{
"names": ["socket"],
"action": "SCMP_ACT_ALLOW",
"args": [{ "index": 0, "value": 38, "op": "SCMP_CMP_LT" }]
"args": [{ "index": 0, "value": 2, "op": "SCMP_CMP_EQ" }]
},
{
"names": ["socket"],
"action": "SCMP_ACT_ALLOW",
"args": [{ "index": 0, "value": 39, "op": "SCMP_CMP_EQ" }]
"args": [{ "index": 0, "value": 10, "op": "SCMP_CMP_EQ" }]
},
{
"names": ["socket"],
"action": "SCMP_ACT_ALLOW",
"args": [{ "index": 0, "value": 40, "op": "SCMP_CMP_GT" }]
"comment": "AF_NETLINK(16) restricted to NETLINK_ROUTE(0): Bubblewrap's Linux sandbox needs a NETLINK_ROUTE socket to run RTM_NEWLINK/RTM_NEWADDR when it configures the loopback interface inside its own (already-isolated) network namespace during codex sandbox startup.",
"args": [
{ "index": 0, "value": 16, "op": "SCMP_CMP_EQ" },
{ "index": 2, "value": 0, "op": "SCMP_CMP_EQ" }
]
},
{
"names": ["personality"],
Expand Down
18 changes: 18 additions & 0 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

set -eu

# Fail closed instead of silently degrading: docker/codex-security-seccomp.json
# allows mount/pivot_root/unshare/setns/clone unconditionally, on the assumption
# (previously only a comment) that the caller runs this image with
# --cap-drop ALL --security-opt no-new-privileges (as compose.yaml does). If
# someone runs the raw image without that contract, refuse to start rather than
# let the seccomp profile's namespace/mount allowlist apply with capabilities
# or new-privilege escalation still available.
caps=$(awk '/^CapEff:/{print $2}' /proc/self/status 2>/dev/null || true)
if [ "$caps" != "0000000000000000" ]; then
printf '%s\n' "codex-security: refusing to start without --cap-drop ALL (effective capabilities are not empty: ${caps:-unknown})." >&2
exit 3
fi
nnp=$(awk '/^NoNewPrivs:/{print $2}' /proc/self/status 2>/dev/null || true)
if [ "$nnp" != "1" ]; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add hardening flags to bare-image CI runs

When the image is invoked by the bare docker run commands in .github/workflows/container-ci.yml lines 92-94 and 136, NoNewPrivs remains 0, so this new check exits with status 3 before --version, help, or discovery validation can run. The first failure aborts the container CI job, while the discovery test also explicitly expects status 2; update those invocations to supply the required security options or otherwise preserve the smoke-test behavior.

Useful? React with 👍 / 👎.

printf '%s\n' "codex-security: refusing to start without --security-opt no-new-privileges." >&2
exit 3
fi

if [ "${1:-}" = bulk-scan ]; then
case "${2:-}" in
--help|-h)
Expand Down