Run as a non-root user

Since 4.3.0, every Mugnsoft component can run as a Linux service under its own account instead of root. It is opt-in: without RunUser, a component runs as root as before.

What each component needs

All default ports are above 1024, so none of them needs a privilege to listen. The service unit grants exactly the capabilities below and removes all the others.

Component Capabilities Used for
Webserver, Webserver-Front none —
Integrator none —
Monitor probe CAP_NET_RAW Ping and MTR monitors, which open raw ICMP sockets
Sentinel Agent CAP_NET_RAW Packet capture: bandwidth and protocol detection
CAP_SYS_PTRACE, CAP_DAC_READ_SEARCH Linking sockets and disk I/O to processes of other users (/proc/<pid>/fd, /proc/<pid>/io), and monitoring log files the account cannot read

A port below 1024 in the component’s .json adds CAP_NET_BIND_SERVICE automatically.

The account owns the component folder, which is where everything is written: configuration, keys, databases, logs, exports and downloaded webdrivers. The Linux host needs systemd 229 or later (RHEL 8, Debian 9, Ubuntu 16.04 and later); on an older init system, capabilities are not applied.

Why Sentinel capabilities are worth a look

CAP_SYS_PTRACE and CAP_DAC_READ_SEARCH are powerful: they let the agent read other users’ process details and any file on the host. They are what process discovery relies on. If you do not need topology across users, remove them as shown in Change what a service may do: the processes of other users then appear without a name.

New installation

Install under /opt/mugnsoft: the account must be able to reach the program, which a private home folder such as /home/<you> usually prevents.

With the install script, add --run-as. The account is created if it does not exist:


bash mugnsoft-install.sh monitor --webserver 10.0.0.5:8050 --shared-internal 3f9c...e1 --run-as mugnsoft

From an extracted package, as root:


cd /opt/mugnsoft/monitor                  # or webserver, integrator, discovery_agent, webserver-front
./install.sh --run-as mugnsoft 10.0.0.5:8050
./monitor start

Or with your own account, set in the component’s .json:


useradd --system --user-group --home-dir /opt/mugnsoft/monitor --no-create-home --shell /usr/sbin/nologin mugnsoft
sed -i 's/"RunUser": ""/"RunUser": "mugnsoft"/' monitor.json
./monitor install 10.0.0.5:8050 && ./monitor start

install must still run as root, because it creates the service. It checks that the account exists, gives it the folder, and restricts the .json file (it holds _sharedInternal) to mode 600. The commands that write files as root (register, migrate, repair-records, run) give them back to the account when they finish.

Move an existing installation

Do not run uninstall for this: it deletes the component’s configuration and databases. Change the service in place, here for a monitor probe:


cd /opt/mugnsoft/monitor
systemctl stop MugnsoftMonitor
id mugnsoft || useradd --system --user-group --home-dir "$PWD" --no-create-home --shell /usr/sbin/nologin mugnsoft
grep -q '"RunUser"' monitor.json && sed -i 's/"RunUser": *"[^"]*"/"RunUser": "mugnsoft"/' monitor.json \
  || sed -i '0,/{/s//{\n    "RunUser": "mugnsoft",/' monitor.json
chown -R mugnsoft: . && chmod 600 monitor.json
mkdir -p /etc/systemd/system/MugnsoftMonitor.service.d
cat > /etc/systemd/system/MugnsoftMonitor.service.d/10-run-as.conf <<'EOF'
[Service]
User=mugnsoft
NoNewPrivileges=true
CapabilityBoundingSet=CAP_NET_RAW
AmbientCapabilities=CAP_NET_RAW
EOF
systemctl daemon-reload
systemctl start MugnsoftMonitor

For another component, replace the names and use its line from the table above. With no capability, write CapabilityBoundingSet= and leave out AmbientCapabilities.

Check it


systemctl status MugnsoftMonitor
ps -o user,comm -C monitor                # the user column shows mugnsoft
grep Cap /proc/$(pidof monitor)/status    # CapEff 0000000000002000 is CAP_NET_RAW
find /opt/mugnsoft/monitor ! -user mugnsoft   # lists nothing

Run in the foreground

To debug, stop the service and run the program under the account:


systemctl stop MugnsoftMonitor
sudo -u mugnsoft ./monitor run

The capabilities of the service do not apply here. If ping monitors or packet capture must work in this mode, grant them on the program with setcap cap_net_raw+ep ./monitor. Replacing the program clears them, so apply this again after an upgrade.

Change what a service may do

Add a drop-in rather than editing the unit, so that it survives a reinstall. Each example is one case; a service only needs the lines for what you use. systemctl edit MugnsoftMonitor opens the right file:


[Service]
# Container monitors reaching the local Docker socket
SupplementaryGroups=docker

# Action scripts that call sudo. Remove the limits they would otherwise run into
NoNewPrivileges=false
CapabilityBoundingSet=~

# A Sentinel Agent limited to packet capture
CapabilityBoundingSet=
CapabilityBoundingSet=CAP_NET_RAW
AmbientCapabilities=
AmbientCapabilities=CAP_NET_RAW

Things to know about the account:

  • Your scripts run as this account too. Action and script monitors inherit its rights, including the capabilities above. sudo is refused by default, because of NoNewPrivileges.
  • Files outside the folder: give the account read access to what its monitors read, such as a private key for an SSH monitor or a log file the Sentinel Agent does not reach. An SSH key file must be owned by this account.
  • EUM journeys and PDF reports start a browser. Its profile goes under the account’s home folder, which is the component folder when install.sh created the account. Chrome’s sandbox stays on for a non-root account, whereas root runs need --no-sandbox: on a host that blocks unprivileged user namespaces (some AppArmor profiles), allow them for the browser.

Windows and containers

On Windows, the services run as LocalSystem. RunUser does not apply: a custom account is not a supported setup in this release.

The Docker images and Helm chart of the monitor probe already run as an unprivileged account: see Install on Kubernetes.

Troubleshooting

Symptom Cause and fix
RunUser "x" does not exist at install Create the account (the message gives the command), or use install.sh --run-as
status=203/EXEC in systemctl status; install warned that the account cannot enter a folder A parent folder is closed to the account, typically a home folder. Move the component to /opt/mugnsoft, then chown -R it
The service starts then stops; permission denied in journalctl -u <service> Files created as root. chown -R <account>: <folder>. With RunUser set, register hands its files back, so this mostly follows a manual copy
Ping or MTR monitors fail with operation not permitted No CAP_NET_RAW: check grep Cap /proc/<pid>/status, and that the unit or a drop-in grants it
Bandwidth stays at 0 on a Sentinel Agent See Packet capture
Processes of other users have no name on a Sentinel Agent CAP_SYS_PTRACE or CAP_DAC_READ_SEARCH was removed
A port below 1024 is refused after you changed it The unit was generated for the old port. Add AmbientCapabilities=CAP_NET_BIND_SERVICE and the same value in CapabilityBoundingSet= with a drop-in

See also

Translations