# 13. Security Considerations

> Privilege isolation, attack surface, Linux capabilities, and secure operation

---

LLMS index: [llms.txt](/llms.txt)

---

<!-- Generated by scripts/generate-haproxy-docs.py from pinned upstream text. -->

HAProxy is designed to run with very limited privileges. The standard way to use it is to isolate it
into a chroot jail and to drop its privileges to a non-root user without any permissions inside this
jail so that if any future vulnerability were to be discovered, its compromise would not affect the
rest of the system.

In order to perform a chroot, it first needs to be started as a root user. It is pointless to build
hand-made chroots to start the process there, these ones are painful to build, are never properly
maintained and always contain way more bugs than the main file-system. And in case of compromise,
the intruder can use the purposely built file-system. Unfortunately many administrators confuse
"start as root" and "run as root", resulting in the uid change to be done prior to starting haproxy,
and reducing the effective security restrictions.

HAProxy will need to be started as root in order to:

- adjust the file descriptor limits
- bind to privileged port numbers
- bind to a specific network interface
- transparently listen to a foreign address
- isolate itself inside the chroot jail
- drop to another non-privileged UID

HAProxy may require to be run as root in order to:

- bind to an interface for outgoing connections
- bind to privileged source ports for outgoing connections
- transparently bind to a foreign address for outgoing connections

Most users will never need the "run as root" case. But the "start as root" covers most usages.

A safe configuration will have:

- a chroot statement pointing to an empty location without any access permissions. This can be
  prepared this way on the UNIX command line:

```shell
# mkdir /var/empty && chmod 0 /var/empty || echo "Failed"
```

and referenced like this in the HAProxy configuration's global section:

```text
chroot /var/empty
```

- both a uid/user and gid/group statements in the global section:

```text
user haproxy
group haproxy
```

- a stats socket whose mode, uid and gid are set to match the user and/or group allowed to access
  the CLI so that nobody may access it:

```text
stats socket /var/run/haproxy.stat uid hatop gid hatop mode 600
```

## 13.1. Linux capabilities support {#section-13-1}

Since version v2.9 haproxy supports Linux capabilities. If the binary is compiled with
USE_LINUX_CAP=1, it is able to preserve capabilities given in 'setcap' keyword during switching from
root user to a non-root.

Since version v3.1 haproxy also checks if capabilities given in 'setcap' keyword were set in its
binary file Permitted set by administrator (capget syscall). If this a case it performs transition
of these capabilities in its process Effective set (capset syscall), while running as a non-root
user.

This was done to avoid all potential use cases when haproxy starts and runs as root: transparent
proxy mode, binding to privileged ports.

'setcap' keyword supports following network capabilities:

- cap_net_admin: transparent proxying, binding socket to a specific network interface, using
  set-mark action;
- cap_net_raw (subset of cap_net_admin): transparent proxying;
- cap_net_bind_service: binding socket to a specific network interface;
- cap_sys_admin: creating socket in a specific network namespace.

Haproxy never does the transition of these capabilities from its Permitted set to the Effective, if
they are not listed as 'setcap' argument. See more information about 'setcap' keyword and supported
capabilities in the chapter 3.1 Process management and security in the Configuration guide.

Administrator may add needed capabilities in the haproxy binary file Permitted set with the
following command:

Example:

```haproxy
# setcap cap_net_admin,cap_net_bind_service=p /usr/local/sbin/haproxy
```

Added capabilities will be seen in process Permitted set after its start. If the same capabilities
are the arguments of 'setcap' keyword, they could be also seen in the process Effective set. This
could be check with the following command:

Example:

```haproxy
# grep Cap /proc/<haproxy PID>/status
```

    CapInh: 0000000000000000
    CapPrm: 0000000000001400
    CapEff: 0000000000001400
    CapBnd: 000001ffffffffff
    CapAmb: 0000000000000000

See more details about setcap and capabilities sets in Linux man pages (capabilities(7)).

In some use cases like transparent proxying or creating socket in a specific network namespace,
configuration file parser detects that cap_net_raw or cap_sys_admin or some other supported
capabilities are needed. Then, during the initialization stage, haproxy process checks, if these
capabilities could be put in its Effective set. If it's not possible due to capget or capset syscall
failure (restrictions set on syscalls by some security modules like SELinux, Seccomp, etc), process
emits diagnostic warnings (start with -dD).

Due to support of many different platforms with different system settings, it's impossible for the
parser to deduce from the configuration file, if binding to privileged ports will be done. So, in
the case of insufficient privileges (run as non-root) process will terminate only with an alert
message like below. It's up to a user to recheck its configuration and haproxy binary capabilities
set.

Example:

```shell
$ haproxy -dD -f haproxy.cfg
...
[ALERT]    (96797): Binding [haproxy.cfg:36] for frontend fe: cannot bind socket (Permission denied) for [0.0.0.0:80]
[ALERT]    (96797): [haproxy.main()] Some protocols failed to start their listeners! Exiting.
```
