Closed sideeffect42 closed 1 year ago
The ejabberd source code just calls Erlang/OTP function gen_tcp:listen providing the file path, and looking at the erlang documentation, there's no way to modify the permissions you mention. See listen_option and local_address.
update ownership/mode after starting ejabberd: theoretically possible but somewhat fiddly.
In this sense, I wrote this dirty and experimental patch that updates the file permissions after creating it. It requires some testing...
Example configuration in ejabberd.yml:
unix_socket_domain_details:
mode: 775
owner: 117
group: 135
Patch against ejabberd git:
@sideeffect42: Have you tried the @badlop patch?
I have just given @badlop's patch a spin on a fresh build of 23.04.
It seems to work like what I would expect. My thoughts on the current implementation:
www-data
for use by a reverse proxy and another one owned by root
for admin API access.owner
/group
options should accept names, too.mode
option should maybe be of type econf:octal
like the mode options in mod_http_upload
.file:change_owner
and file:change_group
will fail with eperm
if the owner/group is not the ejabberd service user, unless CAP_CHOWN
is set (on Linux).
Currently this results in quite a cryptic error message.umask(777)
before creating the socket (if the mode
option is used).
As it is there is a short time frame in which an attacker might connect to the socket before its permissions are set correctly.Great ideas!. Here is a new patch with several improvements:
owner
/group
options should accept names, too.
--> unfortunately, Erlang/OTP's file:change_owner function only supports integersmode
option should maybe be of type econf:octal
--> Donefile:change_owner
and file:change_group
will fail with eperm
--> I've added a error handlingumask(777)
before creating the socket
--> At a quick look, I found no way to accomplish this or something similarConfigure like this:
listen:
-
port: "unix://tmp/asd/socket"
unix_socket:
mode: "0775"
owner: 117
group: 135
New patch:
I also didn't found any code that allow mapping from user -> id (there is no mention of getpwuid
in erlang source code that is usually used for that), and i also not found a way to change umask for drivers so not sure if we will be able to solve those two points
BTW, I noticed that I only added the relevant code for TCP listeners, I guess it should be added to UDP too, right?
As it is there is a short time frame in which an attacker might connect to the socket before its permissions are set correctly.
Just to be sure, could you check that possibility exists? For this debugging, you can add the erlang code: timer:sleep(30000),
that will add a delay of 30 seconds, so that you can attempt to break into the server before the proper permissions are set. For example:
set_unix_socket_domain_file_details(<<"unix:", Path/binary>>, Opts) ->
timer:sleep(30000),
Usd = maps:get(unix_socket, Opts),
...
BTW, I noticed that I only added the relevant code for TCP listeners, I guess it should be added to UDP too, right?
I'm not sure, but having it is better than needing it.
As it is there is a short time frame in which an attacker might connect to the socket before its permissions are set correctly.
Just to be sure, could you check that possibility exists? For this debugging, you can add the erlang code:
timer:sleep(30000),
that will add a delay of 30 seconds, so that you can attempt to break into the server before the proper permissions are set.
On my system I can "acquire API output" using ths following test script and config:
#!/bin/sh
set -x
prefix=/usr/local
ejabberdctl=${prefix}/sbin/ejabberdctl
socket_path=/tmp/ejabberd-admin.sock
user=ejabberd
EJABBERD_CONFIG_PATH=${PWD}/ejabberd.test.yml
export EJABBERD_CONFIG_PATH
"${ejabberdctl}" stop
"${ejabberdctl}" stopped
(umask 0000 && HOME=$(eval "cd ~${user}" && pwd -P) capsh --iab=^cap_chown,^cap_setpcap,^cap_net_bind_service --user="${user}" --addamb=cap_chown,cap_net_bind_service -- -c "${ejabberdctl} start")
su -s /bin/bash nobody -c "x='';\
curl --silent --parallel --unix-socket ${socket_path} http://127.0.0.1/api/registered_vhosts\$x{1..39999}\
"
ejabberd.test.yml
:
fqdn: xmpp.example.net
language: en
hosts:
- example.net
listen:
-
port: unix:/tmp/ejabberd-admin.sock
transport: tcp
module: ejabberd_http
unix_socket:
#owner: 0
#group: 0
mode: '0700'
request_handlers:
/api: mod_http_api
api_permissions:
"admin access":
who: all
what:
- "*"
- "!stop"
- "!start"
It might not work on every try but I was having pretty good luck even without adding a timer:sleep()
.
Thanks! Right, your script produces the problem many times. And adding the sleep it's 100% reproducible.
I guess the most simple solution would be to tell the admin to set the socket path in a directory where only the proper users and groups can view the directory content. That way, even if Erlang doesn't set the proper permissions, nobody can read the files there...
But it would be better if ejabberd does it automatically:
This patch implements that idea. The provisional path for a given socket file is deterministic, and there's a DEBUG line that prints it, so you can point the script to that path.
With that patch, the script doesn't get access to the API, even when adding a sleep in ejabberd to allow curl access the provisional file.
Is your feature request related to a problem? Please describe.
I would like to have some functionality of ejabberd to be fronted by a reverse proxy. Since the reverse proxy and ejabberd are running on the same machine it would be practical if the reverse proxy could talk to ejabberd via a UNIX socket.
Unfortunately, the sockets created by the
listen
option are owned by theejabberd
service user and have mode0755
(umask 0022
). As a result (on Linux) only theejabberd
user can connect to the socket.Describe the solution you'd like
I would like there to be configuration options to set the ownership and mode of the socket which is created.
Describe alternatives you've considered
Possible alternative solutions:
set the umask: setting the umask before starting ejabberd alters the sockets' mode, but it will also affect all the other files ejabberd creates. For the reverse proxy to be able to connect to the socket I would need to
umask 0000
which seems like a bad idea.update ownership/mode after starting ejabberd: theoretically possible but somewhat fiddly. Patching init script, maybe?