症状
错误文本本身就带着分类信息:listen EACCES: permission denied 0.0.0.0:443。EACCES 是权限拒绝,不是端口占用冲突。最快的鉴别方法是换 8443 端口再跑一次:如果能绑定,说明代码没问题,是身份的问题。用 id 和 grep Cap /proc/self/status 检查,非特权 uid 且 CapEff 缺少 CAP_NET_BIND_SERVICE 位(第 12 位)时,内核必然拒绝绑定 1024 以下端口。
诊断思路
先确认进程身份与能力集,排除 root 之外的普通用户;再用 ss -ltnp 确认端口确实空闲——空列表排除 EADDRINUSE,问题回到能力层;最后检查平台策略层:SELinux 处于 enforcing 模式,或容器的 securityContext 限制,都会独立于 Unix 权限拒绝绑定。本地 3000 端口正常、容器或虚拟机里 PORT=443 就失败,也指向部署环境的能力边界而非应用代码。
根因
Node 进程在没有 CAP_NET_BIND_SERVICE 的情况下尝试绑定 1024 以下端口。与表示端口被占用的 EADDRINUSE 不同,EACCES 表示策略边界:内核在当前安全上下文里拒绝该身份的绑定请求。
修复与验证
首选方案是应用监听 8443 等非特权端口,由 nginx 或负载均衡终结 TLS;必须直接绑定时,用 setcap cap_net_bind_service=+ep 精确授予 node 二进制该能力,或在容器 securityContext 的 capabilities.add 里声明 NET_BIND_SERVICE,也可以 root 启动后在 listen 之后 setuid 降权。注意 Node 升级后 setcap 会失效,因为能力绑定的是文件身份。验证时以生产用户重启服务确认 443 的监听日志,再用 curl https://localhost 完成完整握手与响应,并用 capsh –print 核对能力仍然只加了这一项。千万不要为了绕过 EACCES 让整个服务以 root 运行。
代码示例
Error: listen EACCES: permission denied 0.0.0.0:443
at Server.setupListenHandle [as _listen2] (node:net:1874:18)
id
grep Cap /proc/self/status
internet -> nginx :443 (root, master) -> app :3000 (app user)
sudo setcap cap_net_bind_service=+ep $(readlink -f $(which node))
securityContext:
capabilities:
add: ['NET_BIND_SERVICE']