Skip to content

Correct the guidance when binding a privileged host port fails - #2269

Open
daddyrusher wants to merge 1 commit into
apple:mainfrom
daddyrusher:fix/privileged-port-error-message
Open

daddyrusher wants to merge 1 commit into
apple:mainfrom
daddyrusher:fix/privileged-port-error-message

Conversation

@daddyrusher

Copy link
Copy Markdown

What this changes

When publishing a port below 1024 on an explicit host address, the forwarder gets EACCES from the kernel and we rewrite it into a friendlier message. The rewritten text tells the user that root is required. That advice cannot be followed: sudo container run fails with unauthorized request - uid mismatch [client_euid=0] [server_euid=501], since the API server runs as the invoking user.

This replaces the advice with options that actually work, and names the host address that was refused.

Before:

Permission denied while binding to host port 80. Binding to ports below 1024 requires root privileges.

After:

Permission denied while binding to host port 80 on 127.0.0.1. macOS restricts ports below 1024 when an explicit host address is given. Use -p 80:<container-port> without a host address, or a host port of 1024 or above. Running with sudo does not help: the API server runs as your user.

Why the old text was wrong

The restriction is a macOS one and it only applies when an explicit address is given. Wildcard binds are exempt. This is reproducible with plain sockets, no container involved:

import socket
s = socket.socket(); s.bind(("0.0.0.0", 80)); s.listen()      # fine
s = socket.socket(); s.bind(("127.0.0.1", 80)); s.listen()    # PermissionError

The cutoff sits exactly at 1024, and it is not specific to loopback. A LAN address behaves the same way:

address        port   result
0.0.0.0          80   OK
0.0.0.0        1023   OK
127.0.0.1        80   EACCES
127.0.0.1      1023   EACCES
127.0.0.1      1024   OK
192.168.x.y      80   EACCES
192.168.x.y    1024   OK

So the EACCES we catch is a correct refusal from the OS. Only the advice we print on top of it needed fixing. Gaining root is not an option here, so the message should point at the paths that work instead.

Verification

Built with make all and run against the locally built services on macOS 26.5, Apple silicon.

$ bin/container run --rm -p 80:80 nginx
... nginx/1.31.5 ... start worker processes
$ curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:80/
200

$ bin/container run -d --name c1 -p 127.0.0.1:8080:80 nginx
c1
$ curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/
200

$ bin/container run --rm -p 127.0.0.1:80:80 nginx
Error: failed to bootstrap container (cause: "internalError: "failed to bootstrap container ... (cause: "invalidArgument: "Permission denied while binding to host port 80 on 127.0.0.1. macOS restricts ports below 1024 when an explicit host address is given. Use -p 80:<container-port> without a host address, or a host port of 1024 or above. Running with sudo does not help: the API server runs as your user."")"")

make APP_ROOT=test-data all test passes.

Notes

No automated test is included. There is no existing coverage for error message text in the project, and a test that binds port 80 on CI seemed like a bad idea. Happy to add something if you would prefer it.

One thing I noticed but did not touch: the error is thrown as .invalidArgument, which reads oddly for a permission failure and contributes to the nested wrapping in the CLI output. That felt like a separate change, so I left it alone.

Relates to #1985. That report asks for -p 127.0.0.1:80:80 to work for a non-root user, which this does not do. I left the issue open since that is a different question.

@daddyrusher
daddyrusher force-pushed the fix/privileged-port-error-message branch from 02c306d to de380ba Compare September 13, 2026 10:19
@daddyrusher daddyrusher changed the title Privileged port error message Correct the guidance when binding a privileged host port fails Sep 13, 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.

1 participant