When you start a new Linux server like using Fedora F44 cloud image, you think it is already clean and very safe, but actually it have many active services running inside that you do not need at all. Every service that is active have socket and port, and this is like open window in your house that bad hacker can use to come inside and steal your things. We need to close all of this windows and lock them very strong so nobody can break in, and we can doing this easily with systemd mask and making sandbox for our applications.
If you only use normal disable command for service, it is not enough safety. When you disable some service, other service can still start it if they need it for dependency, or some process can trigger it to wake up again. So we must use mask command because mask is much more stronger than disable. When you run mask, systemd will make a symlink of that service pointing to /dev/null, which means the service is totally dead and nothing can start it anymore, even if some other program try to call it.
Here is the step by step to find and mask the services that you do not need on your server.
First step is you must mask the network discovery and printing services because you do not need them on a cloud server. You do not connect printer to your cloud server, right? So we run this command:
sudo systemctl mask avahi-daemon.service avahi-daemon.socket
sudo systemctl mask cups.service cups.socket
Avahi daemon is for finding local devices like printers or other computers in same local network, which is totally useless for cloud server and only make noise. CUPS is the printing service, and it is very famous for having security bugs in the past, so we mask them both so they can never start again.
Second step is to mask the automatic bug reporting tools. Fedora has this tool called ABRT that watch for system crash and send reports. It is good for desktop but on server it run many daemon in background and watch journal files which can slow down things. We mask them with this command:
sudo systemctl mask abrt-journal-core.service abrt-oops.service abrt-vmcore.service
Third step is for things that only laptop or desktop computer need. If you have server, you do not have mobile network card or double graphics card. So you must mask these two service also:
sudo systemctl mask ModemManager.service
sudo systemctl mask switcheroo-control.service
ModemManager is looking for 3G or 4G LTE USB dongle to connect to internet, and switcheroo is for switching between Intel GPU and Nvidia GPU on laptops. These have no business running on your cloud server.
Now after we mask them, we must check what is still running and listening on our network ports. We can check this with a very simple command:
sudo ss -tulpn | grep LISTEN
This command will show you all things that is waiting for connection. The “t” is for TCP, “u” is for UDP, “l” is for listening sockets, “p” is to show the program name, and “n” is to show number instead of service name. If you see some program there that you do not know, you must search on google and investigate why it is there, and then you can mask it if it is not important.
After you have only the services that you really need, like for example Nginx web server, you must make them very safe inside a virtual box. This is called per-unit sandboxing in systemd. If some hacker find bug in Nginx and try to hack it, sandboxing will stop them from touching the rest of your operating system.
To do this, we use the edit command of systemctl. This command opens a blank file where we can override the security settings of Nginx. Run this command:
sudo systemctl edit nginx.service
When the text editor opens, you must write these lines inside, make sure it is exactly like this:
[Service]
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/nginx /var/lib/nginx /run
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictNamespaces=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
Now I will explain what every line of this config is doing so you understand why your server becomes safer.
- NoNewPrivileges=true is very important because it stops the process from getting more power. Even if the process run some script that try to get root access using sudo or setuid, the kernel will say no and stop it.
- ProtectSystem=strict makes almost all system folders like /usr, /boot, and /etc become read-only. This means if hacker try to modify system files or install bad programs there, they cannot write anything because the system block it.
- ProtectHome=true makes the /home folder and /root folder completely invisible to Nginx. Nginx does not need to see your personal user files, so we hide them.
- ReadWritePaths is because Nginx still needs to write some files like logs and cache. We only allow it to write in /var/log/nginx, /var/lib/nginx, and /run. Everywhere else is completely blocked for writing.
- PrivateTmp=true creates a unique and empty /tmp folder just for Nginx. Other processes on the server cannot see what Nginx puts in its temp folder, and Nginx cannot see other processes temp files.
- PrivateDevices=true stops Nginx from talking to physical hardware devices like your hard drive directly or USB ports. It only sees virtual empty devices.
- ProtectKernelTunables=true stops Nginx from changing the settings of the Linux kernel via /proc/sys or /sys.
- ProtectKernelModules=true makes it impossible for Nginx to load or unload Linux kernel modules. Hacker cannot install malicious rootkits this way.
- ProtectControlGroups=true makes the control groups settings read-only so Nginx cannot change resource limits of the system.
- RestrictNamespaces=true stops Nginx from creating new namespaces which can be used to bypass security boundaries.
- LockPersonality=true stops changing the execution domain of the system, which is old feature that hackers sometimes use for exploits.
- MemoryDenyWriteExecute=true stops Nginx from making memory space that is both writable and executable at same time. This is very good because it stops many memory buffer overflow hacks.
- RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX tells the system that Nginx can only use IPv4, IPv6, and local Unix sockets. It cannot use weird old network protocols that might have security bugs.
After you save this file and close the editor, you must tell systemd to load the new configuration and restart Nginx service:
sudo systemctl daemon-reload
sudo systemctl restart nginx.service
If you want to see if your work made a difference, you can use systemd-analyze tool to check the security of your service. Run this command:
systemd-analyze security nginx.service
Before we added these sandbox lines, the security score of Nginx on Fedora was probably around 9.2, which systemd says is UNSAFE. But after you add these lines, the score will drop down to about 4, which systemd says is OK. This is much better and makes your server way more difficult for bad people to compromise. You can repeat this edit pattern for other services you run like database or other daemons.
To make sure your server is safe, you must always look at what services are active and turn off everything you do not use. Using systemd mask is a very simple and powerful way to make your attack surface small, and writing sandboxing options for your remaining services makes a big wall of security. I hope this guide helps you to make your Linux server more secure.
