Bug 1386233 - haproxy requires fowner capability when non-root user is used for a socket file
Summary: haproxy requires fowner capability when non-root user is used for a socket file
Keywords:
Status: CLOSED ERRATA
Alias: None
Product: Red Hat Enterprise Linux 7
Classification: Red Hat
Component: selinux-policy
Version: 7.4
Hardware: All
OS: Linux
medium
medium
Target Milestone: rc
: ---
Assignee: Lukas Vrabec
QA Contact: Milos Malik
URL:
Whiteboard:
Depends On:
Blocks: 1420851
TreeView+ depends on / blocked
 
Reported: 2016-10-18 12:58 UTC by Zdenek Pytela
Modified: 2020-09-10 09:52 UTC (History)
10 users (show)

Fixed In Version: selinux-policy-3.13.1-133.el7
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2017-08-01 15:15:11 UTC
Target Upstream Version:


Attachments (Terms of Use)


Links
System ID Private Priority Status Summary Last Updated
Red Hat Knowledge Base (Solution) 3047061 0 None None None 2017-05-22 13:26:55 UTC
Red Hat Product Errata RHBA-2017:1861 0 normal SHIPPED_LIVE selinux-policy bug fix update 2017-08-01 17:50:24 UTC

Description Zdenek Pytela 2016-10-18 12:58:42 UTC
Description of problem:
The haproxy daemon can be configured with a socket file owned by a non-root user and permissions of 0600 to give another process exclusive access to this socket in order to configure and monitor the haproxy service. With such a configuration, the service fails to start.

Version-Release number of selected component (if applicable):
haproxy-1.5.14-3.el7.x86_64
selinux-policy-3.13.1-60.el7_2.9.noarch


How reproducible:
always


Steps to Reproduce:
1. Add a line like this to default haproxy configuration:
    stats socket /var/run/haproxy.sock level admin user lb-admin mode 755

2. systemctl start haproxy; sleep 2; systemctl status haproxy -l

Actual results:
Oct 18 14:38:53 rhel7 haproxy-systemd-wrapper[11465]: haproxy-systemd-wrapper: exit, haproxy RC=15
Oct 18 14:38:53 rhel7 systemd[1]: haproxy.service: main process exited, code=exited, status=15/n/a
Oct 18 14:38:53 rhel7 systemd[1]: Stopped HAProxy Load Balancer.
Oct 18 14:38:53 rhel7 systemd[1]: Unit haproxy.service entered failed state.
Oct 18 14:38:53 rhel7 systemd[1]: haproxy.service failed.
Oct 18 14:38:57 rhel7 systemd[1]: Started HAProxy Load Balancer.
Oct 18 14:38:57 rhel7 systemd[1]: Starting HAProxy Load Balancer...
Oct 18 14:38:57 rhel7 haproxy-systemd-wrapper[11541]: haproxy-systemd-wrapper: executing /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
Oct 18 14:38:57 rhel7 haproxy-systemd-wrapper[11541]: [ALERT] 291/145544 (13356) : Starting frontend GLOBAL: cannot change UNIX socket ownership [/var/run/haproxy.sock]
Oct 18 14:38:57 rhel7 haproxy-systemd-wrapper[11541]: haproxy-systemd-wrapper: exit, haproxy RC=256

alternatively, if the socket file exists:
Oct 18 14:38:57 rhel7 haproxy-systemd-wrapper[11541]: [ALERT] 291/143857 (11543) : Starting frontend GLOBAL: error when trying to preserve previous UNIX socket [/var/run/haproxy.sock]


Expected results:
* haproxy.service - HAProxy Load Balancer
   Loaded: loaded (/usr/lib/systemd/system/haproxy.service; disabled; vendor preset: disabled)
   Active: active (running) since Tue 2016-10-18 14:40:44 CEST; 2s ago
 Main PID: 11608 (haproxy-systemd)
   CGroup: /system.slice/haproxy.service
           |-11608 /usr/sbin/haproxy-systemd-wrapper -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid
           |-11609 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds
           `-11611 /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds

Oct 18 14:40:44 rhel7 systemd[1]: Started HAProxy Load Balancer.
Oct 18 14:40:44 rhel7 systemd[1]: Starting HAProxy Load Balancer...
Oct 18 14:40:44 rhel7 haproxy-systemd-wrapper[11608]: haproxy-systemd-wrapper: executing /usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -Ds


Additional info:
ausearch -i -m avc,user_avc,selinux_err,user_selinux_err -ts today
----
type=SYSCALL msg=audit(10/14/16 11:01:25.317:838) : arch=x86_64 syscall=chmod success=yes exit=0 a0=0x7ffd0f227450 a1=0755 a2=0xffffffff a3=0x0 items=0 ppid=4038 pid=4039 auid=unset uid=root gid=root euid=root suid=root fsuid=root egid=root sgid=root fsgid=root tty=(none) ses=unset comm=haproxy exe=/usr/sbin/haproxy subj=system_u:system_r:haproxy_t:s0 key=(null)
type=AVC msg=audit(10/14/16 11:01:25.317:838) : avc:  denied  { fowner } for  pid=4039 comm=haproxy capability=fowner  scontext=system_u:system_r:haproxy_t:s0 tcontext=system_u:system_r:haproxy_t:s0 tclass=capability
----

sesearch -s haproxy_t -t haproxy_t -c capability --allow
----
Found 2 semantic av rules:
   allow haproxy_t haproxy_t : capability { chown dac_override kill setgid setuid net_bind_service net_admin net_raw sys_chroot sys_nice sys_resource } ;
   allow haproxy_t haproxy_t : capability net_bind_service ;
----
^^ fowner cap is missing

Making haproxy_t a permissive domain helps to start the service.

Comment 1 Daniel Borek 2016-10-18 13:19:03 UTC
This strace output shows what haproxy is up to when it fails:
464   00:52:09.832823 chown("/var/run/haproxy.sock.464.tmp", 188, 4294967295) = 0 <0.000081>
464   00:52:09.833261 chmod("/var/run/haproxy.sock.464.tmp", 0755) = -1 EPERM (Operation not permitted) <0.000466> <----- here
464   00:52:09.833829 unlink("/var/run/haproxy.sock.464.tmp") = 0 <0.000041>
464   00:52:09.833950 close(4)          = 0 <0.000105>
464   00:52:09.834119 unlink("/var/run/haproxy.sock.464.bak") = -1 ENOENT (No such file or directory) <0.000040>
464   00:52:09.834293 write(2, "[ALERT] 291/005149 (464) : ", 27) = 27 <0.000589>
464   00:52:09.835018 write(2, "Starting frontend GLOBAL: cannot change UNIX socket ownership [/var/run/haproxy.sock]\n", 86) = 86 <0.001536>

And this is where it happens in src/proto_uxst.c:

        if (!ext && path[0] && 
            (((listener->bind_conf->ux.uid != -1 || listener->bind_conf->ux.gid != -1) &&
              (chown(tempname, listener->bind_conf->ux.uid, listener->bind_conf->ux.gid) == -1)) ||
             (listener->bind_conf->ux.mode != 0 && chmod(tempname, listener->bind_conf->ux.mode) == -1))) {
                err |= ERR_FATAL | ERR_ALERT;
                msg = "cannot change UNIX socket ownership";
                goto err_unlink_temp;
        }

Comment 4 Milos Malik 2016-10-18 15:19:32 UTC
Accidentally, I found out that file context pattern for the /var/run/haproxy.sock socket is incorrect:

# semanage fcontext -l | grep '/var/run/haproxy.*sock'
/var/run/haproxy\.sock.*                           regular file       system_u:object_r:haproxy_var_run_t:s0 
# ls -l /var/run/haproxy.sock 
srwxr-xr-x. 1 haproxy root 0 Oct 18 16:40 /var/run/haproxy.sock
# matchpathcon /var/run/haproxy.sock 
/var/run/haproxy.sock	system_u:object_r:var_run_t:s0
# rm -f /var/run/haproxy.sock
# matchpathcon /var/run/haproxy.sock 
/var/run/haproxy.sock	system_u:object_r:haproxy_var_run_t:s0
# 

The file context pattern matches only regular files, but it should match sockets:

# semanage fcontext -l | grep '/var/run/haproxy.*sock'
/var/run/haproxy\.sock.*                           socket       system_u:object_r:haproxy_var_run_t:s0 
#

Comment 5 Zdenek Pytela 2016-10-20 07:20:59 UTC
Milosi,

Once the daemon manages to start, the socket file context looks like the correct one, either it is in /var/run or /var/lib/haproxy directory:

  $ ls -lZ /var/run/haproxy.sock /var/lib/haproxy/haproxy2.sock
srwxr-xr-x. root root system_u:object_r:haproxy_var_lib_t:s0 /var/lib/haproxy/haproxy2.sock
srwxr-xr-x. root root system_u:object_r:haproxy_var_run_t:s0 /var/run/haproxy.sock

Not quite sure if it is related, but the files are actually created in the /run directory which is a tmpfs linked to from /var/run. The policy does not seem to contain any reference to ^/run.

Comment 6 Milos Malik 2016-10-20 08:07:36 UTC
RHEL-6 and RHEL-7 support a feature called  file context equivalences which define that /run will be treated the same way as /var/run:

# semanage fcontext -l | grep -A 100 -i equivalence
SELinux Distribution fcontext Equivalence 

/usr/local/lib64 = /usr/lib
/etc/systemd/system = /usr/lib/systemd/system
/run/systemd/system = /usr/lib/systemd/system
/run/systemd/generator = /usr/lib/systemd/system
/var/home = /home
/var/roothome = /root
/usr/lib64 = /usr/lib
/var/lib/xguest/home = /home
/var/named/chroot/lib64 = /usr/lib
/var/named/chroot/usr/lib64 = /usr/lib
/run = /var/run
/usr/local/lib32 = /usr/lib
/lib64 = /usr/lib
/lib = /usr/lib
/run/lock = /var/lock
#

This feature is heavily used by RHSCL components.

Comment 7 Terry Bowling 2016-11-28 20:40:41 UTC
Could we confirm if this is a simple selinux policy that needs corrected?  Or does it need updated to use the file context equivalence as noted in cmt6?

Comment 8 Lukas Vrabec 2016-11-28 23:29:09 UTC
(In reply to Terry Bowling from comment #7)
> Could we confirm if this is a simple selinux policy that needs corrected? 
> Or does it need updated to use the file context equivalence as noted in cmt6?

According to comment#4 we should update haproxy context file policy.

Thanks guys for investigation.

Comment 9 Nils Fredrik Gjerull 2017-02-01 10:08:48 UTC
I set a non-root group on the haproxy socket file and are also affected by this. This prevents HAProxy from being reloaded and can also prevent haproxy from starting. I sometimes have to manually remove the socket file before I can start HAProxy.

This is in my audit.log.
---
type=AVC msg=audit(1485174334.322:170648): avc:  denied  { link } for  pid=62485 comm="haproxy" name="haproxy.sock" dev="tmpfs" ino=57340817 scontext=system_u:system_r:haproxy_t:s0 tcontext=system_u:object_r:var_run_t:s0 tclass=sock_file

type=AVC msg=audit(1485175066.360:171290): avc:  denied  { fsetid } for  pid=65435 comm="haproxy" capability=4  scontext=system_u:system_r:haproxy_t:s0 tcontext=system_u:system_r:haproxy_t:s0 tclass=capability
---

The first denial is caused by having the wrong file context (var_run_t in stead of haproxy_var_run_t), but the second I am not sure.

Perhaps the second one is a missing permission for the HAProxy process?

Comment 15 errata-xmlrpc 2017-08-01 15:15:11 UTC
Since the problem described in this bug report should be
resolved in a recent advisory, it has been closed with a
resolution of ERRATA.

For information on the advisory, and where to find the updated
files, follow the link below.

If the solution does not work for you, open a new bug report.

https://access.redhat.com/errata/RHBA-2017:1861


Note You need to log in before you can comment on or make changes to this bug.