DEV Community

Rogerio Taques
Rogerio Taques

Posted on

Automated server security package watcher in PHP

Hi ๐Ÿ‘‹,

I'm back and in this new article, we'll see how to set up a simple yet useful script that watches a (virtual private) server, notifying you (by email) always any security packages are available to be installed.

Also, as a bonus, the script will include a watcher for disk usage rate and whether the server needs a reboot, after any core update. ๐Ÿ™Œ This's a very useful way to automate the server monitoring that works like a charm (at least for me ๐Ÿ˜…)!

All the code examples given here are gonna be written in PHP, however, you should be able to implement it using any other server-side language of your choice. So, feel free to use any other language you feel more comfortable with. If you choose to use a different one, lemme know and I'll be more than happy to also share it here. ๐Ÿš€

If you're in a hurry (or lazy of reading till the end), just get the sample file here. This will give you the file ready to be used. Just don't forget to change the variables' contents.

.
.
.
.
.
.

Wow, glad to see you prefer to keep reading it! ๐Ÿ˜‡

Great! So, let's jump to the funny part ... the code! ๐Ÿ˜‰

The entire script will be written in a single file, let's create a PHP file (I'm calling it sec-watcher.php, but you name it as your wish) and add our first lines:

<?php 

/**
 * sec-watcher.php
 * Server security packages watcher.
 */

Enter fullscreen mode Exit fullscreen mode

We may want to reuse this file across multiple servers, tho, let's put some variables to hold settings that can be easily changed (or automatically replaced in a CI/CD workflow) when deploying this to our servers.


# Who is sending the notification
$sender = 'admin@domain.tld';

# To who the notification will be sent.
# Wanna notify more people? Simply put multiple addresses
# separated by commas (,).
$notify = 'you@domain.tld';

# Give a name to identify your server. Usually the hostname.
$server = 'myserver.com'; 

# What mounted disk should be checked.
# Use '/dev/disk1s1' when on macOS.
$mountedDisk = '/dev/sda'; 

# What's the maximum usage before sending a notification
# for disk over-usage (don't go too low or too high here).
$diskUsageBoundary = 80; # %

Enter fullscreen mode Exit fullscreen mode

Don't forget to replace these variable contents (e.g admin@domain.tld, you@domain.tld, and myserver.com) with values that make sense to you, right? ๐Ÿ˜‰

Next, let's check whether our server needs a reboot. Linux distributions create a file that indicates when the system needs a reboot. We can check for the existence of that file to identify if our machine needs it.

// If this file exist, a reboot is required
$reboot = file_exists('/var/run/reboot-required');
Enter fullscreen mode Exit fullscreen mode

Now, let's double-check if our server is running out of space on its disk. For that, we'll need to execute the command df and process its return.

We'll be looking for a specific pattern in the results that will contain the disk-usage rate and this will be emailed to you always the value is beyond your defined boundary.

// Gather info about disk usage
$res = exec("df -h | grep {$mountedDisk}");
$res = preg_replace('/\s{2,}/', ' ', $res);

$intUsedRate = 0;
$usedRate    = null;

if (!empty($res) && $res) {
    list($mountPoint, $totalSize, $usedSpace, $freeSpace, $usedRate) = explode(' ', $res);
    $intUsedRate = (int) preg_replace('/[^0-9]/i', '', $usedRate);
}

// Clear used rate when not passed the boundary
if ($diskUsageBoundary > $intUsedRate) {
    $usedRate = null;
}
Enter fullscreen mode Exit fullscreen mode

Finally, let's gather the necessary information to determine whether our server has pending security packages to be installed.

Similarly to what happens when a reboot is required, Linux distributions have a special file in the system which is updated with the summary aways the system has packages to be installed, including the security ones.

The approach here will be watching for that file to figure out what we want to know.

// Gather info about available updates
$matches = [];
$note    = @file_get_contents('/var/lib/update-notifier/updates-available');
$note    = preg_replace("/\n/", " ", $note);
Enter fullscreen mode Exit fullscreen mode

Great! With all the information we need in place, let's make sure our script really needs to email us ...

// Set a flag to control whether to send or not send a notification
$shouldNotify = (bool) (preg_match("/\d+\supdates?/i", $note, $matches) || !empty($usedRate) || $reboot);
Enter fullscreen mode Exit fullscreen mode

Awesome! Our script is almost done.

All we need to do now is test if the notification should be sent or not sent and perform it to finalize. Here you go ...


// Notify when needed
if ($shouldNotify) {
    $headers = "From: {$sender} \n";

    $body = [
        "This is an automated message. \n",
    ];

    $count = (int) preg_replace("/[^0-9]/i", '', $matches[0]);

    if ($count) {
        $body[] = "{$count} pending security updates has been found.";
    }

    if (!empty($usedRate)) {
        $body[] = "{$mountedDisk} is {$usedRate} used. Needs an urgent review.";
        $body[] = "As general it is like: ";

        exec("df -h", $out);
        $body[] = "\t" . implode("\n\t", $out);
    }

    if ($reboot) {
        $body[] = "** A reboot is required. **";
    }

    $body[] = "Please review the '{$server}' server ASAP.";

    // echo implode("\n", $body) . "\n";
    mail($notify, "Server '{$server}' Pending Review", implode("\n", $body) . "\n", $headers, "-f {$sender}");
}
Enter fullscreen mode Exit fullscreen mode

And just one more line to communicate we've done: ๐Ÿ˜…

echo "Daily updates review complete. \n";
Enter fullscreen mode Exit fullscreen mode

If you prefer, you can get the file here ๐Ÿ‘ˆ

Buuuuut, hold on. The job is not done yet!

.
.
.
.
.

We'll need to properly define the file permissions and schedule a CRON to run that file on a regular basis (let's say once a day?) ...

Upload the file to a directory of your preference on your server (remember, it doesn't need to be accessible from the web, right?).

Once you've uploaded it, we'll need to give its ownership to the root user and also instruct the system that our current (not privileged user) can run it.

So, from a terminal window (connected to your server, via SSH), run the following commands:

# This will change the ownership
$ sudo chown root:root /path/to/file/sec-watcher.php
Enter fullscreen mode Exit fullscreen mode

Then, open the sudo config file (with visudo, please) and add an instruction to enable our user to call our file without the need for a password ...

$ sudo visudo
Enter fullscreen mode Exit fullscreen mode

When the visudo file is open, add this line:

YOUR-USER-HERE   ALL=(ALL) NOPASSWD: /home/YOUR-USER-HERE/path/to/file/sec-watcher.php
Enter fullscreen mode Exit fullscreen mode

Don't forget to replace YOUR-USER-HERE and /path/to/file/ with the user you are used to connecting to your server and the actual path to the directory you've chosen.

Finally ... yes, truly ๐Ÿ˜… let's finish up by scheduling a CRON job that will call our file on a regular basis. Run the following command in your terminal window (that one connected to your server via SSH):

$ crontab -e
Enter fullscreen mode Exit fullscreen mode

And add the following line ...

30 6 * * *  PHP -q /home/YOUR-USER-HERE/path/to/file/sec-watcher.php >/dev/null 2>&1
Enter fullscreen mode Exit fullscreen mode

Save the file with CTRL + X and type Y.

Don't forget to replace YOUR-USER-HERE and /path/to/file/ with the user you are used to connecting to your server and the actual path to the directory you've chosen.

That's it! ๐Ÿ˜Ž Now, you just need to welcome the email notifications coming to your inbox, always your server has a package pending to be updated or whenever its disk is getting closer to be full!

I hope it can help you!

Happy coding. ๐Ÿค˜๐Ÿš€

Top comments (6)

Collapse
 
lubosm profile image
Lubos M

Rogerio, do you know ubuntu/debian package "unattended-upgrades" ? :)

Collapse
 
rogeriotaques profile image
Rogerio Taques

Hi Lubos, ๐Ÿ‘‹

Thanks for your comment!

Yep, I'm aware of that and I agree that in many cases it can be really helpful. :) However, I've had some issues when using that in the past, so I chose to do such upgrades manually, nowadays.

That's why I found having an automated notification to letting me know always the server needs my immediate attention is a useful solution to be implemented! ๐Ÿค˜

Collapse
 
josipcodes profile image
Josip Opaฤiฤ‡

Dude, this is seriously good article, kudos!

Collapse
 
rogeriotaques profile image
Rogerio Taques

Thanks, Josip! Iโ€™m glad you liked it. ๐Ÿคฉ

Collapse
 
manishfoodtechs profile image
manish srivastava

Great article.

Collapse
 
rogeriotaques profile image
Rogerio Taques

Thanks, Manish! ๐Ÿค—