Joseph Zikusooka ~ Zik

A software engineer specializing in open source technologies | Very experienced in building and configuring UNIX/Linux systems and servers. Passionate about developing software applications and hardware for the smart home | Currently serving as the CEO of Jambula Labs and the project leader at JambulaTV, a smart home automation and entertainment platform - https://jambulatv.com | This blog focuses on the following areas: Linux How-Tos and Tutorials ::: IT Security News ::: Free and Libre Open Source Software ::: Smart Home Software ::: Digital Innovations in East Africa https://mastodon.social/@jzik | https://github.com/zikusooka

Year: 2021

How to monitor the presence of nearby WiFi devices using Icinga2

Introduction

I recently published a Python based plugin for Icinga and Nagios monitoring platforms that monitors the presence of wireless devices in the vicinity.

I really like the stability and reliability Icinga provides when it comes to monitoring the state of hosts or services. There are so many software applications that let you do this, including one I talked about a while back called trackerjacker: https://github.com/calebmadrigal/trackerjacker

Why this way?

At first trackerjacker seemed like what I needed to do this sort of thing, but after several months of testing one issue persisted. The performance hit whenever I started the process was huge. The server, would frequently slow down, as packet capturing and monitoring of WiFi devices took place. So I decided to ditch this tool, and write my own plugin.

I am already using icinga on the server, so I figured, rather than write another app for polling, why not use the existing and well performing icinga2 monitoring software. After all, I had done this with bluetooth devices earlier and presence detection was working so well.

Pre-requisites

You are running a Linux based server with Icinga2 monitoring software installed, configured and working properly. There are several articles about how to install and configure icinga2 including the official documentation at:
https://icinga.com/docs/icinga-2/latest/doc/01-about/

The heavy lifting i.e. scanning is done by the Wireshark based CLI tool ‘tshark’. Therefore, ensure that Wireshark is installed on your system. Also, a separate wireless interface that supports monitor mode is required

To capture packets as a non-root user, run the following command in your terminal:
sudo setcap cap_net_raw,cap_net_admin+eip $(which dumpcap)

If you’re still having trouble capturing packets as a non-root user, check out the following Wireshark wiki page:
https://wiki.wireshark.org/CaptureSetup/CapturePrivileges#Most_UNIXes

You will need to identify the MAC address of each wireless device you plan on monitoring. For phones for example, this information is in the Settings >> General >> About >> WiFi Address for iOS. For Android, go to Settings >> About >> Status >> Wi-Fi MAC address

NOTE: Some phones like the iPhone may have MAC randomization enabled, which makes it difficult to reliably monitor their presence status

Setup Icinga2 plugin

Download icinga2 monitoring plugin named ‘check_tshark’ from my Github page at:
https://github.com/zikusooka/icinga-nagios-plugins

Alternatively, you can clone the entire repository and check out my other plugins too:

git clone https://github.com/zikusooka/icinga-nagios-plugins

Copy the icinga2 plugin i.e. check_tshark to your icinga2 monitoring plugins directory e.g. /usr/lib64/nagios/plugins/

cp -v check_tshark /usr/lib64/nagios/plugins/

Configure Icinga2

Add new check and event command objects to icinga2 i.e. Add and save the following snippets to the file:
/etc/icinga2/conf.d/commands.conf

object CheckCommand "tshark" {
        import "plugin-check-command",

        command = [ PluginDir + "/check_tshark" ]

        arguments = {
                "-i"="$tshark_interface$"
                "-t"="$tshark_timeout$"
                "-a"="$tshark_address$"
        }
}
object EventCommand "wifi-tshark-status-event" {
  import "plugin-event-command"

  command = [ SysconfDir + "/icinga2/scripts/wifi-tshark-status-event.sh" ]

  env = {
    "HOSTALIAS" = "$host.display_name$",
    "HOSTADDRESS" = "$address$",
    "HOSTSTATE" = "$host.state$",
    "HOSTSTATEID" = "$host.state_id$",
    "HOSTSTATETYPE" = "$host.state_type$",
    "HOSTOUTPUT" = "$host.output$",
    "HOSTDISPLAYNAME" = "$host.display_name$",
    "LASTHOSTSTATE" = "$host.last_state$",
    "LASTEHOSTSTATEID" = "$host.last_state_id$",
    "LASTHOSTSTATETYPE" = "$host.last_state_type$",
    "LASTHOSTSTATECHANGE" = "$host.last_state_change$",
    "LASTHOSTCHECK" = "$host.last_check$",
    "LONGDATETIME" = "$icinga.long_date_time$",
  }

}

Add new host templates to icinga2 i.e. Add and save the following snippets to the file:
/etc/icinga2/conf.d/templates.conf

template Host "event-tshark-status" {
  max_check_attempts = 6
  check_interval = 30s
  retry_interval = 5s

  check_command = "tshark"

  enable_event_handler = 1
  event_command = "wifi-tshark-status-event"

  enable_flapping = 1
  flapping_ignore_states = ["Critical"]
}

Finally add the wireless devices you want to monitor to the hosts in icinga2 i.e. Add and save the following snippets to the file:
/etc/icinga2/conf.d/hosts.conf

NOTE: In addition to the MAC address, make sure you specify the WiFi interface used for monitoring

object Host "Phone_Zik" {
  import "event-tshark-status"
  vars.tshark_interface = "wlan1"
  vars.tshark_timeout = 10
  vars.tshark_address = "xx:xx:xx:xx:xx:xx"
}

object Host "Phone_Shushu" {
  import "event-tshark-status"
  vars.tshark_interface = "wlan1"
  vars.tshark_timeout = 10
  vars.tshark_address = "xx:xx:xx:xx:xx:xx"
}

Optional: Add Alert notification using MQTT

In order to send alerts whenever a WiFi device appears or disappears, you will need to set up a MQTT broker server. I personally use mosquitto on Fedora Linux. For detailed instructions on how to setup mosquitto on Fedora Linux, check out this how to: https://nullr0ute.com/2016/05/getting-started-with-mqtt-using-the-mosquitto-broker-on-fedora/

Since, we have already enabled event handling for our monitored devices in icinga2, all that is left is to add an event script that will be triggered by the HOST state of either UP or DOWN. Here’s an example:

Create the following script and make it executable:

cat > /etc/icinga2/scripts/wifi-tshark-status-event.sh <<ET
#!/bin/sh
# Variables
TSHARK_MAC_ADDRESS=$(echo "$HOSTOUTPUT" | grep -oP "(?<=\[).+?(?=\])")
MQTT_TOPIC_PRESENCE_TSHARK=Home/presence/$TSHARK_MAC_ADDRESS
MQTT_PUBLISH_CMD=/usr/bin/mosquitto_pub
MQTT_PUBLISH_OPTS="--quiet -h 127.0.0.1 -p 8883"
# Quit if type of state alert is 'SOFT'
[[ "$HOSTSTATETYPE" = "SOFT" ]] && exit 0

# Publish status via MQTT
if [[ "$HOSTSTATE" = "UP" ]];
then
$MQTT_PUBLISH_CMD $MQTT_PUBLISH_OPTS -t "$MQTT_TOPIC_PRESENCE_TSHARK" -m "Home"

else
$MQTT_PUBLISH_CMD $MQTT_PUBLISH_OPTS -t "$MQTT_TOPIC_PRESENCE_TSHARK" -m "Away"
fi
ET
 
chmod 755 /etc/icinga2/scripts/wifi-tshark-status-event.sh

To check your configuration and that all syntax is correct; run the following command:

icinga2 daemon -C

If all’s OK, restart icinga2 as follows:

systemctl restart icinga2

Conclusion

Next, log into your icinga2 web interface (if you have this setup) and ensure your devices are reporting correctly.

In order to monitor alerting via MQTT run the following command in a terminal:

mosquitto_sub -v -h JambulaTV -p 8883 -t "JambulaTV/#" --insecure  | grep presence

That’s all for now. Until next time!

How to automatically backup your data to an external USB disk on Linux

Introduction

One of the most critical roles that any Systems Administrator must perform on a regular basis is that of running backups for an organization. These days, most big and medium sized offices use Network Attached Storage servers to quickly perform backups on their networks. However, for small offices with limited IT budgets, a DIY solution which uses a simple USB external hard drive is a viable option.

In this setup, the resident “IT guy” is assigned the task of backing up data. This could be from a handful of desktops/laptops, or a central backup server. However, the problem with this setup is that they often will forget to plug in the disk or even initiate the manual backup process, leading to a very unreliable backup scheme.

In this article, I will guide you on how to set up a cheaper, reliable and automated backup scheme. All the operator has to do is to plug in the USB external hard disk any time, and the backup process commences immediately.

This solution takes advantage of a bash script that is triggered once the USB hard disk is inserted into the computer desktop or laptop’s USB ports. The trigger mechanism is handled by udev rules via a systemd unit as will be shown below.

Prerequisites

Operating system: Linux
Desktop: GNOME – mainly for notification purposes
Username: erica (Must have permissions to run ‘sudo’)
Data: /home
Partition is NOT encrypted. See ‘cryptsetup’ tool

Create the following backup script. Save it and make it executable:

vim /opt/backup_2_usb_disk.sh
chmod 755 /opt/backup_2_usb_disk.sh

#!/bin/sh
# Variables
NORMALUSER=erica
VOLUMENAME=BACKUP
USB_DISK_PARTITION=$USB_DISK_PARTITION1
MOUNTPOINT=/run/media/$NORMALUSER/$VOLUMENAME
RSYNC_OPTIONS="-aAXvH”
# Excludes 4 /home
EXCLUDE_FILE_4_HOME_DIR=/tmp/exclude_dirs_in_home_dir
EXCLUDE_DIRECTORIES_IN_HOME_DIR="Videos"
NOTIFY_CMD=/usr/bin/notify-send 
NOTIFY_ICON=/usr/share/icons/gnome/scalable/devices/media-zip-symbolic.svg
NOTIFY_TITLE=""


# Functions
notice () {
su -l $NORMALUSER -c "$NOTIFY_CMD -i $NOTIFY_ICON $NOTIFY_TITLE '$@'"
}

disable_automount () {
su -l $NORMALUSER -c "dbus-launch gsettings set org.gnome.desktop.media-handling automount false"
su -l $NORMALUSER -c "dbus-launch gsettings set org.gnome.desktop.media-handling autorun-never true"
}

sync_data () {
sudo rsync $RSYNC_OPTIONS --delete "$1" "$2"
}

mount_partition () {
# Create mountpoint if it does not exist
[ -d $MOUNTPOINT ] || mkdir -p $MOUNTPOINT
# Mount drive
mount $USB_DISK_PARTITION $MOUNTPOINT
STATUS_MOUNT=$?
if [ "$STATUS_MOUNT" != "0" ];
then
notice "ERROR: mount $USB_DISK_PARTITION $MOUNTPOINT failed"
exit 1
fi
}

backup_thinkpad () {
# Create backupdir directory
[ -d $MOUNTPOINT ] || mkdir $MOUNTPOINT

# Start of backup
notice "Backup Started"

# /home
echo $EXCLUDE_DIRECTORIES_IN_HOME_DIR | tr " " "\n" > $EXCLUDE_FILE_4_HOME_DIR
sleep 3
rsync $RSYNC_OPTIONS --delete --exclude-from=$EXCLUDE_FILE_4_HOME_DIR /home/ $MOUNTPOINT/home/
rm -f $EXCLUDE_FILE_4_HOME_DIR

# other partitions
}

umount_partition () {
umount $USB_DISK_PARTITION
STATUS_UMOUNT=$?
if [ "$STATUS_UMOUNT" != "0" ];
then
notice "ERROR: umount $USB_DISK_PARTITION failed"
exit 1
fi
}

enable_automount () {
su -l $NORMALUSER -c "dbus-launch gsettings set org.gnome.desktop.media-handling automount true"
su -l $NORMALUSER -c "dbus-launch gsettings set org.gnome.desktop.media-handling autorun-never false"
}


# Process the following routines
disable_automount
mount_partition
backup_thinkpad 
umount_partition
enable_automount 

# Notice of completion
sleep 10
notice "Backup completed successfully"

Start/Stop service

Create the systemd unit file:
/etc/systemd/system/backup-usb-disk.service and add the following:

[Unit]
Description=USB Portable Drive

[Service]
User=root
Type=oneshot
Environment=DISPLAY=:0
ExecStart=/opt/backup_2_usb_disk.sh

[Install]
WantedBy=multi-user.target

NOTES:
Reload systemd daemon i.e. ‘systemctl –system daemon-reload’
It is not necessary to enable or start the above unit at this time
since that will be done by the udev rule below.

UDEV rules

Create the udev rules file: /etc/udev/rules.d/97-backup-usb-disk.rules and add the following:

ACTION=="add", ATTRS{idVendor}=="0bc2", ATTRS{idProduct}=="2400", SYMLINK+="disk/by-id/$env{ID_NAME}_$env{ID_SERIAL}", RUN+="/usr/bin/systemctl start backup-usb-disk.service"

ACTION=="remove", ATTRS{idVendor}=="0bc2", ATTRS{idProduct}=="2400", RUN+="/usr/bin/systemctl stop backup-usb-disk.service"

NOTES:
To find your USB disk Vendor and Product ID, run the command:

udevadm info -a -p $(udevadm info -q path -n /dev/sdX) | grep -B2 -i -e idvendor -e idproduct


Change ‘X’ to suit your setup
Run the command ‘udevadm control –reload’ to reload the rules

Conclusion

That’s it! Now when ever, the USB external disk is plugged into the USB port, the backup script will be triggered. You can extend the above mentioned backup script to not only back up other partitions beyond just the /home in our example, but also email the operator whenever the process completes.

Scroll to top