Understanding Systemd.exec - Part 1: How to Secure Applications Running in Systemd?

posted Originally published at dev.to 5 min read

Hello, I'm Ganesh. I'm building git-lrc, an AI code reviewer that runs on every commit. It is free, unlimited, and source-available on GitHub. Star Us to help devs discover the project. Do give it a try and share your feedback for improving the product.

If you are running applications with systemd and want to restrict how they interact with Linux kernel features (like filesystem, processes, and capabilities), you can use the systemd.exec configuration options.

In this article I will be going to explain how to use these options to secure your application.

What are systemd.exec options?

Systemd.exec options are a set of configuration options that are used to configure how a systemd service interacts with the Linux kernel.

This feature is mainly used to provide security for the applications running in systemd.

Why we need this option in first place?

  • Hardening services (Postgres, Nginx, workers, etc) which are mostly open to the internet
  • Reducing blast radius after compromise
  • Lightweight (no container overhead)

How is this different from Docker?

  • Works on a single process/service.
  • Applies restrictions on top of the host.
  • Uses kernel features (namespaces, cgroups, capabilities)
  • Shares the same root filesystem (by default)
  • No packaging, no images

What is the real usecase?

Let's take an example of service.

/etc/systemd/system/myapp.service

Inside this file, we can control:

  • Where the app runs
  • Which user it runs as
  • What it can access
  • How secure / isolated it is
  • What resources it can use

Most common options

  1. Run a service as non-root (security)

Set User= Group= to a non-privileged user (e.g. postgres, www-data).
This will prevent the service from running as root, which can lead to security vulnerabilities.

For example:

[Service]
User=postgres
Group=postgres
  1. Restrict filesystem access

Set ProtectSystem= ProtectHome= ReadWritePaths= ReadOnlyPaths= to control filesystem access.
This will make service only read/ write specifed directory.

Example:

[Service]
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/postgresql
ReadOnlyPaths=/var/lib/postgresql/16/bin
  1. Isolate runtime environment (like lightweight container)

Set PrivateTmp=yes NoNewPrivileges=yes to isolate runtime environment.
As there is no container like docker this will be running in the host but under private tmp and no new privileges.

Example:

[Service]
PrivateTmp=yes
NoNewPrivileges=yes
  1. Limit resources (avoid abuse / crashes)

Set CPUQuota= MemoryMax= MemoryHigh= MemoryLimit= MemoryAccounting= to limit resources.
If we want specific service to use only limited resources, we can use these options.

Example:

[Service]
CPUQuota=50%
MemoryMax=512M
  1. Provide controlled writable directories Set ReadWritePaths= ReadOnlyPaths= to control filesystem access. This will make service only read/ write specifed directory.

Example:

[Service]
ReadWritePaths=/var/lib/postgresql
ReadOnlyPaths=/var/lib/postgresql/16/bin
  1. Drop dangerous privileges

Set CapabilityBoundingSet= to drop dangerous privileges.
This will make service only able to run commands with specific privileges.

Example:

[Service]
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
  1. Run temporary / ephemeral services Set RuntimeDirectory= StateDirectory= LogsDirectory= to run temporary / ephemeral services. This will run the service in a temporary / ephemeral directory.

Example:

[Service]
RuntimeDirectory=myapp
StateDirectory=myapp
LogsDirectory=myapp
  1. Limit process / thread count Set TasksMax= to limit process / thread count. This will make service only able to run with specific process / thread count.

Example:

[Service]
TasksMax=100
  1. Restrict access to system calls Set SystemCallFilter= SystemCallArchitectures= to restrict access to system calls. This will make service only able to use specific system calls.

Example:

[Service]
SystemCallFilter=open,read,write,close
SystemCallArchitectures=amd64
  1. Security measures for Docker Engine Set Delegate=yes to run Docker Engine with specific privileges. This will make Docker Engine only able to run with specific privileges.

Example:

[Service]
Delegate=yes

Conclusion

We could able to use systemd.exec options to secure application running in systemd.

But Until we integrate with real application and test these options, we can't be sure about the security.

In the next article we will see how service react with integrations of these options.

Reference Man Page Systemd : https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html

git-lrc

Any feedback or contributors are welcome! It’s online, source-available, and ready for anyone to use.
⭐ Star it on GitHub: https://github.com/HexmosTech/git-lrc

More Posts

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelskiverified - Mar 19

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

Understanding Interface in Go

Ganesh Kumar - Mar 31

How to send sensor data to a gateway using PainlessMesh in ESP8266?

Ganesh Kumar - Apr 19

How to Send Data from PainlessMesh to the Cloud Using MQTT in an ESP8266?

Ganesh Kumar - Apr 13
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

7 comments
3 comments
2 comments

Contribute meaningful comments to climb the leaderboard and earn badges!