A simple script to patch a Linux server

Want to patch your system with a simple script?  I made a very, very, very, very simple Bash script that can be thrown into cron (I have mine set to run monthly), which will just download and install any available updates from yum.  If any kernel packages were installed, the script will reboot the server to get those kernel packages applied:


# Just yum updates

yum clean all
yum -y update

# Reboot if we get a kernel installed
if grep "$(date '+%b %d')" /var/log/yum.log | grep -E "(Installed|Updated): kernel-([0-9].+|headers)"; then
logger "Kernel updated, server rebooted"
shutdown -r now

So much more can be put into this, but for just updating any packages that are available and rebooting the server when kernel packages are installed, it works.

The script works on CentOS 7 and should work on older releases of this distro.  I haven’t tested the script on Debian/Ubuntu, but I assume it would work on those distros as well.

Using Fabric and the .succeeded and .failed attributes

At work, I’ve written a Fabric script that would allow me to check if a user existed across multiple systems.  The script also has the capability to add a missing user and update an existing users’s password.  To make the script easier to read and use, I’ll put in enhancements every now and then.

Recently, I’ve been wanting to improve the logic in my Fabric script.  Specifically, I wanted the script to determine if a user did not exist on a system, add the user.  So, I figured I’d try using Fabric’s .succeeded and .failed attributes when executing a task using run.  I tried the following if statement:

if run("id " + username || echo "ERROR").succeeded:

    do things

I thought that the .succeeded attribute would not work if the script determined the user was not found.  I was wrong.  It turns out that it doesn’t matter if a && or || operator is being used; the .succeeded or .failed attributes will work based off the success or failure of the entire run command and not what occurs after the && and || operators.  The code above demonstrates that if no user was found, the system would produce the “ERROR” string. Since Fabric saw this as a command being successfully completed, any code in the if block would execute.

After some Googling and reading the Fabric documentation, I ended up using the .return_code attribute to determine if a run command on the system was successful.  Here’s an example:

if run("id " + username).return_code == 0:

return True

Granted, I don’t need to use the .return_code attribute because Fabric can still determine truthiness of the command without it.  I’ve included the attribute into my Fabric script as I plan on implementing further logic to work with the error codes that the useradd command provides.

What I learned using Sensu and sudo

I’ve been working on project that would have a Sensu plugin check a directory, every 60 seconds, to see if any symlinks existed.  If there were none, the Sensu plugin was configured to generate a critical alert.  If any symlink was found (broken or not) then no alert was generated.

But I had some difficulty getting the plugin to work properly.  Even though symlinks would exited in the directory the plugin was configured to look at, Sensu would always throw a critical alert.  I spent some time troubleshooting the problem and learned quite a few things:

  • Sensu uses its own build of Ruby, located in /opt/sensu/embedded/bin/ruby.  When I first started troubleshooting, I wasn’t aware of this and assumed Sensu was using the version of Ruby that was already installed on the server.  The version of Ruby between what the server was using and what was Sensu was packaged with were different, so I made sure to use the /opt/sensu/embedded/bin/ruby path during the troubleshooting process.
  • My Sensu plugin script made use of Bash’s ‘find’ command to look for symlinks.  Due to how the permissions were set up for the directory I wanted the plugin to look at, Sensu would experience permission problems when the plugin was executed.  As Sensu runs the plugins under its own user (sensu), I found that the ‘find’ command would not work.  I was able to get around this by specifying the Ruby plugin script in a /etc/sudoers.d/sensu file and placing the following data in it (this is not a complete version of the file – it is only the data needed to ensure the plugin worked properly):

    Defaults:sensu !requiretty
    sensu   ALL = NOPASSWD: /path/to/sensu/plugins/my_plugin.rb
    sensu   ALL = NOPASSWD: /bin/find

    *You might be able to add these to /etc/sudoers for the sensu user but I haven’t personally tested that.

After finding the above, making the necessary changes and testing those changes, my plugin script began to work!

Did you know…

…that you can configure yum to use a proxy?

A proxy can be specified globally to yum in /etc/yum.conf, or you can specify a proxy for each repository in /etc/yum.repos.d/ .

If you want to disable the global proxy setting for a specific repository, use ‘proxy=_none_’ in your repository file in /etc/yum.repos.d/ .  If no proxy is specified, it will inherit the global proxy setting.