ejabberd icon indicating copy to clipboard operation
ejabberd copied to clipboard

Docker: support running without `NET_BIND_SERVICE`

Open stratself opened this issue 1 month ago • 3 comments

Environment

  • ejabberd version: 25.10
  • Erlang version: unknown
  • OS: Alpine Linux
  • Installed from: container (ghcr.io/processone/ejabberd:latest)

Configuration

docker-compose.yml:

version: 3.9

networks:
  chat-network:
    external: true

services:
  ejabberd:
    image: ghcr.io/processone/ejabberd:latest
    container_name: ejabberd
    environment:
      - CTL_ON_START=stats registeredusers
    networks: [ chat-network ]
    volumes:
      - ./ejabberd.yml:/opt/ejabberd/conf/ejabberd.yml:ro
      - ./database:/opt/ejabberd/database
      - ./logs:/opt/ejabberd/logs
      - ./upload:/opt/ejabberd/upload
      - ./erlang.cookie:/opt/ejabberd/.erlang.cookie
      - ./modules:/opt/ejabberd/.ejabberd-modules

    # == RELEVANT CONFIGS ==
    security_opt:
      no-new-privileges: true
    cap_drop:
      - ALL
    # cap_add:
      # - NET_BIND_SERVICE

For ejabberd.yml, all listen modules are configured to listen on 5222, 5223, 5269, 5270, 5280, 5380, and 5480. Nothing is listening on ports below 1024.

Errors

From podman logs -f ejabberd:

erlexec: Error 1 executing '/opt/ejabberd-25.10/erts-15.2.7.2/bin/beam.smp'.

Bug description

I'm selfhosting ejabberd using podman, and would like to harden the setup by removing all unnecessary capabilities. One of those ways is by using these flags that result in the compose file as shown above:

    security_opt:
      no-new-privileges: true
    cap_drop:
      - ALL

However, this errors out as shown above, and the container process was unable to start. I was able to trace it down to a lack of the NET_BIND_SERVICE capability, and after adding those lines to the setup, it works:

    security_opt:
      no-new-privileges: true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE

The NET_BIND_SERVICE allows running on privileged ports (ports <1024). However, as explained above, all my ports are in the 5200+ ranges, which means this capability is unneeded.

Therefore, I'd like to be able to run the docker/binary without this capability in place, which helps dropping unnecessary privileges. Thanks in advance.

stratself avatar Nov 14 '25 10:11 stratself

It would be great if you update your issue description providing all the details required to reproduce the problem. Right now there's just a part of a YAML file that I don't know where to put.

badlop avatar Nov 18 '25 14:11 badlop

Rewritten to match bug report template

stratself avatar Nov 19 '25 05:11 stratself

Thanks, now that allows to reproduce the scenario. I attempted many different approaches, but didn't find any clue about where the problem is. I post here what I tried, in case it gives some idea.


Let's check it works perfectly by default:

podman run --rm -it ghcr.io/processone/ejabberd:25.10

...
2025-11-19 15:14:11.077 [info] ejabberd 25.10.0 is started in the node :ejabberd@localhost in 1.48s

The minimal steps required to produce the problem:

$ podman run --rm -it --cap-drop NET_BIND_SERVICE ghcr.io/processone/ejabberd:25.10
erlexec: Error 1 executing '/opt/ejabberd-25.10/erts-15.2.7.2/bin/beam.smp'.

Removing all listened ports in ejabberd.yml, it got down just to the ports used for erlang network distribution:

$ netstat -ntulp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:39269           0.0.0.0:*               LISTEN      2/beam.smp
tcp        0      0 :::4369                 :::*                    LISTEN      32/epmd
tcp        0      0 0.0.0.0:4369            0.0.0.0:*               LISTEN      32/epmd

This can be reduced to just one port by setting ERL_DIST_PORT=5210 in ejabberdctl.cfg:

$ netstat -ntulp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:5210            0.0.0.0:*               LISTEN      2/beam.smp

Funnily, setting INET_DIST_INTERFACE=127.0.0.1 in ejabberdctl.cfg produces two error lines:

ec2a2ed9fe58218c1bfbe3121b3c20d57e3658be79bcf7672474a1fe02a0153b
[main] | erlexec: Error 1 executing '/opt/ejabberd-25.10/erts-15.2.7.2/bin/beam.smp'.
[main] | erlexec: Error 1 executing '/opt/ejabberd-25.10/erts-15.2.7.2/bin/beam.smp'.

Looking at ejabberdctl script, this tries to start the erlang BEAM with this command line:

exec /opt/ejabberd-25.10/erts-15.2.7.2/bin/erl
    -sname ejabberd@localhost
    -boot ../releases/25.10.0/start_clean
    -boot_var RELEASE_LIB ../lib
    +K false
    +P 250000
    -erl_epmd_port 5210
    -start_epmd false
    -mnesia dir "/opt/ejabberd/database"
    -s ejabberd
    -noinput

None of those options imply any usage of any port.


ejabberd:25.10 container image uses Erlang/OTP 27.3. I tried an ejabberd image using erlang 28.1, and the problem remains.


I tried to reproduce the problem using just Erlang, but could not.

Erlang in desktop with capsh:

sudo capsh --drop=CAP_NET_BIND_SERVICE -- -c erl
Erlang/OTP 27 [erts-15.2.7] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]

Eshell V15.2.7 (press Ctrl+G to abort, type help(). for help)
1>

Erlang in container:

$ podman run --rm -it  --cap-drop NET_BIND_SERVICE  docker.io/library/erlang:28.1.0.0-alpine
Erlang/OTP 28 [erts-16.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [jit:ns]

Eshell V16.1 (press Ctrl+G to abort, type help(). for help)
1>

ejabberd start script with caps:

$ sudo capsh --drop=CAP_NET_BIND_SERVICE -- -c "./bin/ejabberdctl live"

$ sudo capsh --drop=CAP_NET_BIND_SERVICE -- -c "exec /home/badlop/git/ejabberd/_build/prod/rel/ejabberd/erts-16.1/bin/erl -sname ejabberd@localhost +K true +P 250000 -s ejabberd -noinput"
2025-11-19 15:56:24.615210+01:00 [info] Loading configuration from ejabberd.yml
...

badlop avatar Nov 19 '25 15:11 badlop