In a past article we discussed using systemd timer units to schedule jobs and their pros and cons over crond. In today's article we are examining another unit file called path units. Systemd path units allow you to monitor files and directories (paths) for an event. Once the event triggers, systemd can execute a script via a service unit. Let's create an example configuration.
Creating a Systemd Path Unit
In this example we will create a path unit to send an email alert if the /etc/passwd
file changes on our system. In order to create a functional path unit we need three files.
- A script that sends the email alert
- Service unit to launch the script when an event is observed
- Path unit to monitor the file or directory
Create a Script for Email Alerts
Let's start by creating a simple script that will send an email alert to our administrators. Create a file called email-alert.sh
in the /usr/local/bin/
directory with the following contents.
#!/bin/bash
mail -S sendwait -s "ETC PASSWD CHANGED ON $(hostname)" myadmin@example.com < /etc/passwd
NOTE: This assumes your system is configured correctly to send email and you have mailx installed.
Now we have to make the script executable by running chmod, like so:
$ chmod +x email-alert.sh
Create Service Unit File to Execute Script
The service unit will be responsible for running the script when the path unit observes changes to the file. In this example, we are creating the service unit with only the basic options for simplicity.
Create a file called passwd-mon.service
in the /etc/systemd/system/
directory with the following contents:
[Unit]
Description="Run script to send email alert"
[Service]
ExecStart=/usr/local/bin/email-alert.sh
There are many other options for a service unit which is outside the scope of this tutorial.
Create a Path Unit File
The last file needed to complete our configuration is the path unit file itself. The unit file needs to be created in the same directory as it's corresponding service file, /etc/systemd/system
in this case. It should also have the same name, but with a .path extension.
Create a file called passwd-mon.path
in the /etc/systemd/system/
directory with the following contents.
[Unit]
Description="Monitor the /etc/passwd file for changes"
[Path]
PathModified=/etc/passwd
Unit=passwd-mon.service
[Install]
WantedBy=multi-user.target
The contents of the[Path]
section is the meat and potatoes of the path unit. PathModified
tells the unit to watch for changes to the file. The Unit
declaration tells it which service to activate if a change occurs. WantedBy
basically sets a dependency resulting in our timer starting when the system has reached the multi-user target (runlevel 3).
Available Path Options for Monitoring Files & Directories
We chose to use PathModified
for our path unit, but other options exist. Here is an overview of the other options available.
- PathExists - If file/directory exists activate the unit.
- PathExistsGlob - If at least one file/directory exists that matches the specified globbing pattern.
- PathChanged - If file/directory changes activate the unit.
- PathModified - Similar to PathChanged, but watches for simple writes.
- DirectoryNotEmpty - If directory contains at least one file activate the unit.
There are three additional options that can be used in the [Path]
stanza.
- Unit - Unit to activate when above path events trigger.
- MakeDirectory - Create directory to be monitored (boolean)
- DirectoryMode - Use specified mode to create directories (octal notation).
Verify the Syntax of Your Unit Files
You can use the systemd-analyze command with the verify option to check the correctness of your unit files. It will help point out any syntax errors.
$ sudo systemd-analyze verify /etc/systemd/system/passwd-mon.*
If you have any fatal errors, they will be highlighted red.
Start Your systemd Path Unit
You are now ready to start your path unit and begin monitoring your files. You can start it with systemctl, just like a normal service.
$ sudo systemctl start passwd-mon.path
Now we check the status and ensure it is running without any issues.
$ sudo systemctl status passwd-mon.path
● passwd-mon.path - "Monitor the /etc/passwd file for changes"
Loaded: loaded (/etc/systemd/system/passwd-mon.path; disabled; vendor preset: disabled)
Active: active (waiting) since Sat 2020-01-25 13:30:20 EST; 6s ago
Jan 25 13:30:20 putor systemd[1]: Started "Monitor the /etc/passwd file for changes".
Enabling Your systemd Path Unit at Boot
Enabling your path unit to start on boot is the done the same way you enable services at boot, like so:
$sudo systemctl enable passwd-mon.path
Testing Your New Path Unit
Now we have our new path unit running and monitoring our file. If a user is added, deleted, or other changes made to a users account (change home dir, default shell, etc) the /etc/passwd
file will be updated. The path unit will detect the change to the file, which in turn will trigger the service unit.
Let's add a user and see what happens.
[savona@putor ~]$ date
Sat 25 Jan 2020 02:27:12 PM EST
[savona@putor ~]$ sudo useradd test
We can see in the logs that the service unit executed successfully.
Jan 25 14:27:19 putor systemd[1]: Started "Run script to send email alert".
Jan 25 14:27:19 putor systemd[1]: passwd-mon.service: Succeeded.
We can also see in the postfix logs, that the email sent successfully.
Jan 25 14:27:19 putor postfix/pickup[1491]: D25F02E15BB: uid=0 from=<root>
Jan 25 14:27:19 putor postfix/cleanup[4293]: D25F02E15BB: message-id=<20200125192719.D25F02E15BB@putor>
Jan 25 14:27:19 putor postfix/qmgr[1492]: D25F02E15BB: from=<root@putor>, size=3826, nrcpt=1 (queue active)
Jan 25 14:27:20 putor postfix/smtp[4294]: D25F02E15BB: to=<*********@********.net>, relay=mail.******.net[*.*.*.*]:465, delay=0.64, delays=0.05/0.01/0.43/0.14, dsn=2.0.0, status=sent (250 OK id=1ivR5U-002kSc-Cq)
Jan 25 14:27:20 putor postfix/qmgr[1492]: D25F02E15BB: removed
Looks like everything went off without a hitch.
Checking Your Path Unit Logs with Journalctl
One of the advantages of using systemd path (or timer) units is that they operate like normal services. This means you can control them with systemctl
. In addition their logs are available in the systemd journal via journalctl
.
To check the logs generated by your path unit, use journalctl
with the -u option.
[savona@putor ~]$ sudo journalctl -u passwd-mon.path
-- Logs begin at Fri 2019-11-08 00:08:01 EST, end at Sun 2020-01-26 02:43:59 EST. --
Jan 25 13:10:57 putor systemd[1]: Started "Monitor the /etc/passwd file for changes".
Jan 25 13:30:00 putor systemd[1]: passwd-mon.path: Succeeded.
...OUTPUT TRUNCATED...
You can also use journalctl
to check on your .service unit as well. For more information on using journalctl
read "Viewing logs with journaltcl".
Conclusion
In this example we set up a systemd path unit to monitor the /etc/passwd
file for changes. We created a service unit that fires off a script to send an email when the file changes.
In all of my testing this seems to work great. However, keep in mind that although I chose to use the /etc/passwd
file as an example, I would not recommend this as a security feature. There are products out there designed for file integrity checking. For example, aide is a file integrity checker designed specifically for that purpose. It is far better equipped to be used as a security tool.
There are some real world examples of when these would come in handy. For example, I run a script every 5 minutes (via cron) that looks in a folder for a file it can process. I would probably be better off using a path unit for that job.