This the second part of our blog series "Things that security auditors will nag about and why you shouldn't ignore them" In these articles, Nixu's security consultants explain issues that often come up when assessing the security of web applications, platforms and other computer systems.
One issue that pops up in security audits once in a while is running services as root. While it is easy and you don't need to mess around with file and directory privileges, it also increases your attack surface. In this blog, I will explain you why you should not run every service as root and how to do it.
Don't be the root of all evil
The reason for using non-root accounts for running services is to minimize access in case of vulnerabilities. If a remotely exploitable vulnerability is found on your software, and the software is running as root, the attacker can get root privileges on your server. Using a non-privileged account will limit the damage, if the vulnerability gets exploited.
How to run services as non-privileged users
The amount of steps required depends on the service. If the service already supports running as a non-root user but for some reason it hasn't been configured to do so, this is a bit simpler.
As with any bigger changes, you should try the changes in a testing environment first to avoid breaking things!
1. Check if services are running as unique non-root users. Using nobody or daemon user and group that are default on some distros is not recommended.
- Check the process list (ps axuf) for running services,
- or go through the configuration files and look for User and Group directives
2. If some services are running as root, create a separate user account and group for each.
- Adding a group 'myservice' in Linux: groupadd -r myservice
- Adding a user 'myservice' in Linux: useradd myservice -r -g myservice /path/to/servicefiles -s /sbin/nologin
3. If there was a separate user account already, check that it has an invalid login shell to prevent logging in with that user.
- Running grep myservice /etc/passwd should contain /usr/sbin/nologin, /dev/null or another invalid shell.
- If the user has /bin/sh, /bin/bash or another valid shell, change it with the chsh command.
4. If the service supports non-root user and group already in the configuration files, edit the configuration files to use the new user and group. Restart or reload the service to make the changes effective.
5. Lock the user account to prevent login and su-ing to that user.
- Run passwd -l myservice
6. Set up directory and file read/write permissions to required configuration files and working directories.
- Here you should refer to any Center of Internet Security (CIS) Benchmarks, if there is one available for your server software. For example in Apache and BIND, the recommendation is to have configuration files owned by root to reduce unauthorized modifications.
- There are exceptions to this, if some users or automatic processes need to modify certain files or directories, or files are created run-time.
7. Set up proper read and write access to the log files created by the service. Ensure the service user account can write to logs (append-only is even better) but other users cannot. Also restrict unnecessary read access.
- Remember to set up remote logging, too!
8. If the service does not specify User and Group in its own configuration files directly, you can modify the system startup scripts. This depends on the Linux distribution you are using.
- Systemd: Systemd is a popular init system in many modern distros. In systemd unit files, you can use the directives User=myservice, Group=myservice to specify the correct user and group to use and use it for dropping privileges.
- The commands start-stop-daemon or runuser can be helpful in other init systems.
9. If you can't use systemd or don't know what init daemon will be used by the users of your software, implement dropping privileges yourself. The usual approach is to use root for starting a master process, which then forks additional processes with dropped privileges, as explained in Michael Boelen's article How and why Linux daemons drop privileges
10.As an alternative to dropping privileges, you can assign required Linux capabilities with setcap to avoid being root in the first place. Capabilities are flags that specify, what the application can do. For example:
- To be able to bind to ports < 1024, you need the CAP_NET_BIND_SERVICE capability. If you are happy with running in high ports, this is not necessary.
- Depending on what your service does, you might need other capabilities. Check the Additional Reading section for more Sven Vermeulen's informative blog posts explaining Linux capabilities.
- Remember that many of the capabilities grant very wide permissions. In addition, capabilities on a binary are system-wide: they have no target files, binaries or ports. Use capabilities sparingly to avoid privilege escalation.
Additional hardening steps include, for example, disabling root login with SSH and configuring the firewall to allow access only to necessary open services.
If the service in question is 3rd party software, contact the vendor for a feature request. They can implement dropping privileges correctly and ensure something does not break. Running as root is a weakness so it's reasonable to ask for a modification.
Q&A - In this special case I can run as root, can I?
Q: I patch regularly so I don't need to worry about vulnerabilities and exploits. Is it ok to run services as root now?
A: Zero days do happen, and it is all too easy to find vulnerable servers with Shodan, Zoomeye and similar search engines. If the vendor is releasing patches for example once a week, there's plenty of time for exploiting. It's not just about having vulnerabilities in a specific services: the bug might lie in the init system or network stack of the operating system, for example. Even if you had an update available, you might be forced to wait for the maintenance window. So even if you're Sir Patch-a-lot, it's still not recommended to run services as root.
Q: I don't store any confidential information on my server. So even if someone got root access, they would not get any useful information!
A: Someone gaining unauthorized root access might still want to use your server as a malware or spam server, launching DDoS attacks, hiding their trails or other criminal activities. And if your server is connected to a network with other hosts, an attacker could gain access to them via your compromised server. You wouldn't want that, would you?
Q: I'm running these services in a totally isolated environment. I don't see the point of all that extra configuration.
A: OK, you might be in the beginning of developing something or trialling a software in your dedicated virtual machine. For simplicity, running the services as root might be OK then. Just keep it far away from your production setup. It is a whole nother story if you are talking about non-networking equipment or exceptional physical security such as of three armed men guarding the door. Know your risks. As a general rule, don't run as root.
- Center for Internet Security (CIS) Benchmarks
- How and why Linux daemons drop privileges, Michael Boelen
- systemd.exec — Execution environment configuration
- Capabilities(7) - Linux man page
- Capabilties, a short intro, by Sven Vermeulen
- Restricting and granting capabilities, Sven Vermeulen
- Overview of Linux capabilities, part 1, Sven Vermeulen
- Overview of Linux capabilities, part 2, Sven Vermeulen
- Overview of Linux capabilities, part 3, Sven Vermeulen