Skip to content

Remove cap_net_bind_service from the caddy binary - #466

Open
i5okie wants to merge 1 commit into
caddyserver:masterfrom
i5okie:fix/remove-setcap
Open

Remove cap_net_bind_service from the caddy binary#466
i5okie wants to merge 1 commit into
caddyserver:masterfrom
i5okie:fix/remove-setcap

Conversation

@i5okie

@i5okie i5okie commented Aug 21, 2026

Copy link
Copy Markdown

Claude Code was used for the diagnosis, testing, and this description; I have verified the change.

Removes setcap cap_net_bind_service=+ep from the caddy binary, as proposed in #461.

The +ep file capability does more than grant a capability — it makes execve fail with EPERM when the capability bounding set is empty. So the image doesn't start at all under --cap-drop ALL, and it fails as root too. This isn't a rootless-only issue.

Tested on OpenShift 4 (CRI-O), stock restricted-v2 SCC:

CapPrm:  0000000000000000
CapEff:  0000000000000000
CapBnd:  0000000000000000
CapAmb:  0000000000000000

uid=1004530000 gid=0(root) groups=0(root)
ip_unprivileged_port_start=1024

Running caddy:2.11.4-alpine there:

exec /usr/bin/caddy: operation not permitted

restricted-v2 sets requiredDropCapabilities: [ALL] and won't allow adding capabilities back, so there's no way to work around this downstream. Copying the binary to /tmp (which strips file caps) and running the copy starts and serves normally — which is what this change does properly.

Verified against the built image:

before after
--cap-drop ALL, root ✗ EPERM ✓ starts
--cap-drop ALL, non-root ✗ EPERM ✓ starts
non-root binds :80, default sysctl

One breaking case, as #461 notes: running as non-root and binding below 1024 on a host where net.ipv4.ip_unprivileged_port_start is above 0. Docker 20.10+ and containerd 2.0+ set it to 0, so most users are unaffected; anyone else can use --cap-add NET_BIND_SERVICE or listen above 1024. Root is unaffected either way.

Worth flagging that last sysctl line above: CRI-O still reports 1024, so the privileged-port problem isn't universally solved by runtimes yet. Separate concern from this change though — a Caddyfile on 8080 handles it.

libcap is now unused in the alpine image and could come out too. I've left it in to keep this minimal, happy to add it if you'd prefer.

This supersedes #428, which is now closed — this turned out to be the real blocker there, not the rootless packaging.

Closes #461

The `+ep` file capability makes execve fail with EPERM when the
capability bounding set is empty, so the image cannot start at all
under `--cap-drop ALL` — as root or as any other user.

Most runtimes now set net.ipv4.ip_unprivileged_port_start=0, which
makes the capability redundant. Where they don't, `--cap-add
NET_BIND_SERVICE` or a port above 1024 covers it.

Closes caddyserver#461
@i5okie i5okie mentioned this pull request Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop declaring setcap cap_net_bind_service on the caddy binary

1 participant