Unix socket as backend

Hi! I am trying to use a unix socket as server in backend.

I’ve seen it is supported in frontend but I have not seen whether is or not supported in the backend.

I did an internet search and found a forum (link here) with some stating it is supported and others it is not.

I tried it and I have no Haproxy error stating it is not supported or anything like that, but it doesn’t work.

My haproxy configuration is with http:

frontend rpilibcam
    bind-process 2-3
    bind :8090 tfo ssl crt /etc/haproxy/certs/ process 2-3 alpn h2,http/1.1 curves X25519:P-256:secp384r1
    bind abns@haproxy-clt7 accept-proxy tfo ssl crt /etc/haproxy/certs/ process 2-3 alpn h2,http/1.1 curves X25519:P-256:secp384r1
    mode http
    option forwardfor
    default_backend rpicam_8089

backend rpicam_8089
    mode http
    option forwardfor
    retry-on all-retryable-errors
    #server rpicam 127.0.0.1:8089
    server rpicam unix@ustreamer.sock

And this is the one with tcp:

frontend rpilibcam
    bind-process 2-3
    bind :8090 ssl crt /etc/haproxy/certs/ process 2-3 curves X25519:secp521r1:P-256:secp384r1 tfo
    bind abns@haproxy-clt7 accept-proxy ssl crt /etc/haproxy/certs process 2-3 curves X25519:P-256:secp384r1
    mode tcp
    option tcp-smart-accept
    default_backend rpicam_8089

backend rpicam_8089
    mode tcp
    option tcp-smart-connect
    retry-on all-retryable-errors
    #server rpicam 127.0.0.1:8089
    #server rpicam /run/ustreamer.sock
    server rpicam /run/ustreamer.sock

The application I am trying to use is Ustreamer(link here) which is a video streaming application with systemd socket activation.

I expect the user to connect to 8090 and watch the video streaming haproxy encrypts from the unix socket where the aplication listens for activation and transmits.

The aplication can be accessed with this command the developer provided me so to prove the application is working:

curl --unix /run/ustreamer.sock http://127.0.0.1:8080/state
{"ok": true, "result": { "encoder": {"type": "CPU", "quality": 80}, "source": {"resolution": {"width": 1296, "height": 972}, "online": true, "desired_fps": 6, "captured_fps": 2}, "stream": {"queued_fps": 0, "clients": 0, "clients_stat": {}}}}

Further information, I tested haproxy with IP instead of Unix socket and both work. My issue seems to be conecting the unix socket with Haproxy if that is possible.

So do you know if unix socket server is supported in the backend? And if so, do you know what is missing?

Thanks in advance!

Further system information below:
haproxy -v

HA-Proxy version 2.2.9-2+deb11u2 2021/09/05 - https://haproxy.org/
Status: long-term supported branch - will stop receiving fixes around Q2 2025.
Known bugs: http://www.haproxy.org/bugs/bugs-2.2.9.html
Running on: Linux 5.10.92-v8+ #1514 SMP PREEMPT Mon Jan 17 17:39:38 GMT 2022 aarch64

uname -a

Linux rpi4 5.10.92-v8+ #1514 SMP PREEMPT Mon Jan 17 17:39:38 GMT 2022 aarch64 GNU/Linux
root@rpi4:/home/pi# cat /etc/debian_version
11.2

apt show haproxy

Package: haproxy
Version: 2.2.9-2+deb11u2
Priority: optional
Section: net
Maintainer: Debian HAProxy Maintainers <team+haproxy@tracker.debian.org>
Installed-Size: 3,839 kB
Pre-Depends: dpkg (>= 1.17.14), init-system-helpers (>= 1.54~)
Depends: libc6 (>= 2.17), libcrypt1 (>= 1:4.1.0), libgcc-s1 (>= 3.0), liblua5.3-0, libpcre2-8-0 (>= 10.22), libssl1.1 (>= 1.1.1), libsystemd0, zlib1g (>= 1:1.1.4), adduser, lsb-base (>= 3.0-6)
Suggests: vim-haproxy, haproxy-doc
Homepage: http://www.haproxy.org/
Tag: implemented-in::c, interface::daemon, network::hiavailability,
 network::load-balancing, network::server, network::service,
 protocol::http, protocol::tcp, role::program, use::filtering,
 use::proxying
Download-Size: 1,846 kB
APT-Manual-Installed: yes
APT-Sources: http://deb.debian.org/debian bullseye/main arm64 Packages
Description: fast and reliable load balancing reverse proxy
 HAProxy is a TCP/HTTP reverse proxy which is particularly suited for high
 availability environments. It features connection persistence through HTTP
 cookies, load balancing, header addition, modification, deletion both ways. It
 has request blocking capabilities and provides interface to display server
 status.

It is absolutely supported, but people often forget 2 things when working unix sockets:

  • privileges: just like every other file, it has file permissions, this is especially important when haproxy downgrades it’s privileges to a dedicated unprivileged user
  • chroot: if you need to access a file, you can’t limit haproxy to a chroot directory that doesn’t have that file

Hi @lukastribus thanks for your answer! I searched for documentation/forums related to your answer and found an old thread with the same issue (link here). The solution from that post was resumed in 3 lines:

1 - move the socket you want to use on backend server to haproxy chroot directory
2 - Add “user haproxy group haproxy” to frontend
3 - The same line to server backend

I tried to do that it but I guess I found either a bug or something that changed since that time as you can see below:

  1. I checked the socket I wanted to use as backend server was created (I guess as expected with the user which runs the application that creates the socket on haproxy chroot directory)
root@rpi4:/lib/systemd/system# ls -lrth /var/lib/haproxy/
total 4.0K
drwxr-xr-x 2 root      root      4.0K Jul 13  2021 dev
srwxrwxr-x 1 ustreamer ustreamer    0 Feb 16 21:47 ustreamer.sock
  1. I moved(already done in the first step) the socket I triend to use as backend server to haproxy chroot directory as defined in the haproxy.cfg, in my case /var/lib/haproxy/http

  2. I added to frontend bind:

user haproxy group haproxy
  1. I tried to add the same to the backend server section:
user haproxy group haproxy

But unfortunately I couldn’t restart haproxy due to it complained saying it doesn’t recognize the word “user” from the backend:

# systemctl restart haproxy; systemctl status haproxy; journalctl -xe
Job for haproxy.service failed because the control process exited with error code.
See "systemctl status haproxy.service" and "journalctl -xe" for details.
● haproxy.service - HAProxy Load Balancer
     Loaded: loaded (/lib/systemd/system/haproxy.service; enabled; vendor preset: enabled)
     Active: activating (auto-restart) (Result: exit-code) since Wed 2022-02-16 22:10:03 -03; 14ms ago
       Docs: man:haproxy(1)
             file:/usr/share/doc/haproxy/configuration.txt.gz
    Process: 9069 ExecStartPre=/usr/sbin/haproxy -f $CONFIG -c -q $EXTRAOPTS (code=exited, status=1/FAILURE)
        CPU: 35ms
░░
░░ The job identifier is 5877.
Feb 16 22:10:03 rpi4 haproxy[9069]: [ALERT] 046/221003 (9069) : parsing [/etc/haproxy/haproxy.cfg:176] : 'server rpicam' unknown keyword 'user'. Registered keywords:
Feb 16 22:10:03 rpi4 haproxy[9069]:     [ SSL] allow-0rtt [dflt_ok]
Feb 16 22:10:03 rpi4 haproxy[9069]:     [ SSL] alpn <arg> [dflt_ok]
Feb 16 22:10:03 rpi4 haproxy[9069]:     [ SSL] ca-file <arg> [dflt_ok]
Feb 16 22:10:03 rpi4 haproxy[9069]:     [ SSL] check-alpn <arg> [dflt_ok]
Feb 16 22:10:03 rpi4 haproxy[9069]:     [ SSL] check-sni <arg> [dflt_ok]
Feb 16 22:10:03 rpi4 haproxy[9069]:     [ SSL] check-ssl [dflt_ok]
Feb 16 22:10:03 rpi4 haproxy[9069]:     [ SSL] ciphers <arg> [dflt_ok]
Feb 16 22:10:03 rpi4 haproxy[9069]:     [ SSL] ciphersuites <arg> [dflt_ok]

And here is the frontend/backend configuration:

userlist rpicamcredentials
   user MYUSER password MYHASHEDPASS

frontend rpilibcam
    bind-process 1
    bind :8090 tfo ssl crt /etc/haproxy/certs/ process 1 alpn h2,http/1.1 curves X25519:P-256:secp384r1 user haproxy group haproxy
    bind abns@haproxy-clt7 accept-proxy tfo ssl crt /etc/haproxy/certs/ process 1 alpn h2,http/1.1 curves X25519:P-256:secp384r1 user haproxy group haproxy
    mode http
    option forwardfor
    http-request redirect scheme https unless { ssl_fc }
    http-request auth unless { http_auth(rpicamcredentials) }
    default_backend rpicam_8089

backend rpicam_8089
    mode http
    option forwardfor
    retry-on all-retryable-errors
    #server rpicam 127.0.0.1:8089
    server rpicam /var/lib/haproxy/ustreamer.sock user haproxy group haproxy

Removing from the server backend line “user haproxy group haproxy” allows haproxy to start but It doesn’t work and from the forum I referred above, I need to add it to both in order to make it work. :frowning:

Thanks!

Hello,

no, so first of all, don’t blindly configure user and group everywhere without understanding, you don’t need that at all actually. The other examples you found are about haproxy listening on unix sockets, which is not your case.

You never need user/group settings on IP socket or abns sockets, because they cannot even be applied, user and permission settings are about files (a unix socket is a file, and abns or IP socket is not).

The is no backend user/group setting either, this doesn’t make sense.

You DO need to check permission of the unix socket and understand it. But ustreamer.sock is world readable, so you are already ok.

I hope you didn’t just move the file. You had the application reconfigured with the new path, right? You cannot just move a unix socket around, if the application is listening on the old path.

You can hard link the socket from the chroot location, or tell the application to listen in there.

That’s the wrong path. Assuming your chroot is /var/lib/haproxy/ (but you omitted the chroot configuration so I can only guess), then your path is /ustreamer.sock:

server rpicam /ustreamer.sock

1 Like

Thank you @lukastribus ! It is working now, I had multiple problems you pointed me:

  • The usage of “user” and “group” was not correct
  • The reference of the socket under Haproxy chroot directory was not correct
  • And the last one that I don’t know if it is setted as expected was the socket permissions I created in the systemd socket file. I had to give read and write permissions to “others” unix group eventhough haproxy is on the group that the systemd socket and systemd service files runs(in my case the group is “ustreamer”):

root@rpi4:/home/pi# cat /lib/systemd/system/ustreamer.socket
[Unit]
Description=Ustreamer socket unit
PartOf=ustreamer.service

[Socket]
#Para que funcione con Haproxy, lo ponemos en su chroot path
ListenStream=/var/lib/haproxy/ustreamer.sock
SocketUser=ustreamer
#SocketMode=006 Funcionó
SocketMode=006

[Install]
WantedBy=sockets.target

As seen haproxy is on the group user that executes both the systemd socket and service files:

root@rpi4:/home/pi# id haproxy
uid=117(haproxy) gid=126(haproxy) groups=126(haproxy),44(video),995(ustreamer)

For anyone looking for the final working haproxy file:

frontend rpilibcam
    bind-process 2-3
    bind :8090 tfo ssl crt /etc/haproxy/certs/ process 2-3 alpn h2,http/1.1 curves X25519:P-256:secp384r1
    #bind abns@haproxy-clt7 tfo ssl crt /etc/haproxy/certs/ process 2-3 alpn h2,http/1.1 curves X25519:P-256:secp384r1
    mode http
    option forwardfor
    http-request redirect scheme https unless { ssl_fc }
    http-request auth unless { http_auth(rpicamcredentials) }
    use_backend rpicam_8089 if { path /stream }

backend rpicam_8089
    mode http
    option forwardfor
    retry-on all-retryable-errors
    server rpicam /ustreamer.sock tfo

For this to work make sure that:

  1. You have haproxy chroot setted like i.e.:
    chroot /var/lib/haproxy
  2. Set the server to point to the socket under chroot correctly; that means you have to use the relative path with a slash in front of it. “/your_unix_socket.sock”
  3. The systemd socket file permissions allow others to read and write “SocketMode=006” (I don’t know if this is correct or there is any security issue, but this is the only way I worked for me. I tried “SocketMode=770” but it didn’t work. In order to avoid lower security issues, I hardened the systemd socket and service file with many restrictive options. I also restricted the server path to allow only the one I want with the line “if { path /stream }”.

As always with all my questions, thanks to @lukastribus for all!

1 Like

Right, of course you need both read and write permission from haproxy, because the traffic is bidirectional.