Παρασκευή 6 Αυγούστου 2021

Get Telegram notifications for APC UPS using SNMP traps in Linux

In this post I am going to explain how I used SNMP traps on a Linux Debian system in order to get some telegram notifications. I used them in order to be informed in case our company UPS systems runs on battery.

First of all, you have to setup and test a Telegram Notification script.
I used the one here: https://github.com/samsulmaarif/telegram-notify , it is very easy to install and use.

The binary is installed in: /usr/local/bin/telegram-notify

We assume that you already have a telegram bot running.
The two things you 've got to find are your bot api-key and chat id or Channel id.
In case you are not yet using a telegram bot, there are plenty of guides showing how to setup one and how to get your user-id.

In order to get SNMP traps from your UPS Network management card, you should first enable them in the "Notifications". Just enter your Linux hostname as a trap receiver.


First install snmpd and snmptrapd

sudo apt install snmpd snmptrapd 


Edit your config files:

The only change I did to this file is the agentAddress.
/etc/snmp/snmpd.conf

agentAddress udp:161,udp6:161


Then, edit the configuration for the traps. The matching SNMPv1 OIDs for the online/offline battery status of an APC UPS are SNMPv2-SMI::enterprises.318.0.5 and SNMPv2-SMI::enterprises.318.0.9.

So, when each trap is received, my local script /usr/local/ups-telegram.sh  is ran, follwed by a string onbattery/offbattery used to identify the status.


/etc/snmp/snmptrapd.conf 

authcommunity log,execute,net public
snmpTrapdAddr udp:162
traphandle SNMPv2-SMI::enterprises.318.0.5 /usr/local/ups-telegram.sh "onbattery"  
traphandle SNMPv2-SMI::enterprises.318.0.9 /usr/local/ups-telegram.sh "offbattery"

Traps are enabled only for the matching OIDs. In order to de tests, you may use "traphandle default" instead of using specific OIDs.

Restart the services:

sudo service snmpd restart
sudo service snmptrapd restart


test the traps locally:

snmptrap -v 2c -c public localhost "" SNMPv2-SMI::enterprises.318.0.5
snmptrap -v 2c -c public localhost "" SNMPv2-SMI::enterprises.318.0.9

if everyting is fine, you should see some SNMP trap logs at your syslog.

In the second part, we will configure our "action" script. The variable $1 is passed from the snmptrapd.conf file.
In order to get the Telegram Notifications, I used a modified offical Example handler script:

ups-telegram.sh

#!/bin/bash

userid="myuserid"
apikey="myapikey"

read host
read ip
vars=
count=1

while read oid val
do
count=$[count+1]
if [ "$vars" = "" ]
then
vars="$oid = $val"
else
vars="$vars, $oid = $val"
fi
done

UPS_TIME="/tmp/UPS_TIME.tmp"

#echo a $1 trap from host=$host at IP $ip vars=$vars>>/var/log/messages
if [ "$1" = "onbattery" ]; then
starttime="$(TZ=UTC0 printf '%(%s)T\n' '-1')"
echo "$starttime" > "$UPS_TIME"

/usr/local/bin/telegram-notify --user "$userid" --key
"$apikey" --text "$host - On
battery power in response to an input power problem" --error

fi
if [ "$1" = "offbattery" ]; then
starttime=$(head -n 1 $UPS_TIME)
rm -f "$UPS_TIME"
elapsedseconds=$(( $(TZ=UTC0 printf '%(%s)T\n' '-1') - starttime ))

/usr/local/bin/telegram-notify --user "$userid" --key
"$apikey" --text "$host - No longer
on battery power - was on battery for $elapsedseconds secs" --success
fi

exit

 

Τρίτη 13 Ιουλίου 2021

Configure Cisco Jabber DNS and Single Domain using Response Policy Zone (RPZ) and a single BIND9 DNS server

Cisco recommends dual DNS: public (external) and local (internal) DNS in order that Mobile and Remote Access can work more efficiently.

Pinpoint entries (a zone created for a single host only) as suggested by Cisco could be a solution, but our DNS server is authoritative for the parent domain, so this wouldn't work.

With the follwing setup and by using RPZ, jabber requests coming from the internet, are forwarded to Expressway-Edge and requests coming from the internal network are forwarded to Expressway-Core.


Public (External) Records needed:

_collab-edge._tls.example.com      SRV 10 10 8443 expe1.example.com.

Local (Internal) Records needed:

_cisco-uds._tcp.example.com        SRV    10 10 8443 cucm1.example.com
_cuplogin._tcp.example.com         SRV    10 10 8443 cup1.example.com


Cisco states that a client first searches for internal DNS records:
For example, Adam McKenzie's services domain is example.com when he starts the client. The client then issues the following query:

    _cisco-uds._tcp.example.com
    _cuplogin._tcp.example.com
    _collab-edge._tls.example.com


We will be using RPZ, so that the client gets different DNS results depending on which DNS is using for recursion (Public or Private).
Our DNS server is authoritative for all our domains and recursive allowing queries only from internal network (allow-recursion { ournets; };).

First, create the RPZ zone file to your bind9 recursor. This is where all the overrides will be placed.
I copied the file from https://deteque.com/m3aawg-bind-training/ and modified it according to our needs.

db.rpz.local

$TTL 3600

@               IN SOA  localhost. need.to.know.only. (
                       201702135 ; Serial number
                       60        ; Refresh every minute
                       60        ; Retry every minute
                       432000    ; Expire in 5 days
                       60 )      ; negative caching ttl 1 minute
                IN NS   LOCALHOST.
;_collab-edge._tls.example.com IN CNAME *. ; return NODATA for internal queries - Probably not needed
_cisco-uds._tcp.example.com          SRV     10 10 8443 cucm1.example.com. ;internal only
_cuplogin._tcp.example.com           SRV     10 10 8443 cup1.example.com. ;internal only


In my Debian Buster BIND configuration, I use seperate config files in named.conf:

include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
include "/etc/bind/named.conf.log";


If you use a single config file, then put the following parts in the same file, usually named.conf.
Enable logging of RPZ requests, it is a goood thing to know what's going on:

named.conf.log

        channel rpzlog {
                file "/var/log/rpz.log" versions 14 size 1m;
                print-time yes;
                print-category yes;
                print-severity yes;
                severity info;
        };
        category rpz { rpzlog; };


In your domain main zone file, among your other records, you have to add the following record.
This should be available publicly so that remote clients can access your Cisco Expressway Edge.
In my case, this zone is also transferred to our domain slaves.

example.com zone:

_collab-edge._tls       SRV     10 10 8443 expe1.example.com. ; VCS Expressway or Cisco Expressway-E server


Then, add the zone in your config file:

named.conf.local

zone "rpz.local" {
        type master;
        file "db.rpz.local";
        allow-update { none; };
        allow-transfer { none; };
};


-----------
Finally, enable the Response Policy Zone:

named.conf.options:

        response-policy {
                zone "rpz.local";
        };


Restart Bind and reload the zone

sudo /etc/init.d/bind9 restart
sudo rndc reload rpz.local


Do some tests from some clients and check the rpz.log file!
When querying locally for _cisco-uds._tcp.example.com, you should get a result and see some Local-Data rewrite for your queries.
When querying remotely for _cisco-uds._tcp.example.com, you should get no answer.

Πέμπτη 14 Ιανουαρίου 2021

Allow Adobe Flash for certain sites after 2021 EOL announcement

You can still use Adobe Flash as of January 2021 by "whitelisting" certain sites. I mostly use it to manage some old Enterprise Appliances. All you have to do is edit your mms.cfg file, located in:  

C:\Windows\System32\Macromed\Flash 

or

C:\Windows\SysWOW64\Macromed\Flash

So, the file should look be edited as following:

EOLUninstallDisable=1
AutoUpdateDisable=1
EnableAllowList=1
AllowListRootMovieOnly=1
AllowListURLPattern=*://mydomain.example.com

According to the Adobe Flash Admin guide, AllowListUrlPattern syntax is the follwing:

AllowListUrlPattern = <scheme> ://<host>:<port>/<path>
<scheme> = ‘*’ | ‘http’ | ‘https’
<host> = <any char except ‘.’ and ‘*’>
<port> (optional) = <any valid port number>
<path> = ‘/’ <any chars>
 
With EnableAllowList=1 set, administrators can then specify a discrete URL or pattern to allow

Πέμπτη 22 Οκτωβρίου 2015

Migrating a linux machine from LVM to normal partitions

Before shutting down your system, copy the output of your current fstab in order to mount your partitions:

# cat /etc/fstab
Boot from a live cd (I used an ubuntu 14.04)
You 've got to mount somewhere the original filesystem in order to copy it, preferably as read-only:
Assuming our old disk is /dev/sda with 3 partitions (all of them LVM):
# mkdir /mnt/olddisk /mnt/olddisk/oldboot /mnt/olddisk/oldroot /mnt/olddisk/oldvar 
# mount -o ro /dev/mapper/vg1-lv_XXX /mnt/olddisk/oldboot
# mount -o ro /dev/mapper/vg1-lv_XXY /mnt/olddisk/oldroot
# mount -o ro /dev/mapper/vg1-lv_XXZ  /mnt/olddisk/oldvar
Your old fstab should look like this
# cat /mnt/olddisk/oldroot/etc/fstab
/dev/mapper/vg1-lv_XXX   /                       ext3    defaults        1 1
/dev/mapper/vg1-lv_XXY  /boot                   ext3    defaults        1 2
/dev/mapper/vg1-lv_XXZ  /var                  ext3    defaults        1 2
tmpfs                   /dev/shm                tmpfs   defaults        0 0
devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
sysfs                   /sys                    sysfs   defaults        0 0
proc                    /proc                   proc    defaults        0 0
/dev/mapper/vg1-lv_swap      swap                    swap    defaults        0 
You should first check the inode size of your old disk because after migrating a disk with inode size 128 to a disk with inode size 256 which is the default now, grub won't install, you will get an error (The file /boot/grub/stage1 not read correctly, Error 2: Bad File or Directory Type)!
# tune2fs -l /dev/sda1 | grep -i 'inode size'
Inode size:           128
Fdisk your new disk (/dev/sdb)
I used 2 partitions, sdb1 for the the whole filesystem and of course a small swap sdb2 partition
# fdisk /dev/sdb
After creating both partitions, give sda1 the bootable flag (a) and change the system id to sdb2 (t, 82).

Format the partitions:
# mkfs.ext3 /dev/sdb1 -I 128
# mkswap /dev/sdb2
Label the partition makes it more convenient for configuring grub and fstab later:
I labeled it "root"
# e2label /dev/sdb1 root
Mount and prepare the filesystem on new disk:
# mkdir /mnt/newdisk
# mount /dev/sdb1 /mnt/newdisk
# cd /mnt/newdisk; mkdir {dev,mnt,boot,opt,usr,var,tmp,home,data}
Create and configure permissions on /proc, /dev and /sys folders:
# mkdir /mnt/newdisk/{proc,dev,sys}
# chmod -w /mnt/newdisk/proc
# chmod go+w /mnt/newdisk/dev
# chmod +t /mnt/newdisk/dev
copy all data from all olddisk mountpoints excluding /dev, /proc and /sys
# rsync -avz /mnt/olddisk/oldboot /mnt/newdisk --exclude dev --exclude proc --exclude selinux --exclude sys
# rsync -avz /mnt/olddisk/oldroot /mnt/newdisk --exclude dev --exclude proc --exclude selinux --exclude sys
....
Adjust some important permissions:
# chmod +t /mnt/newdisk/tmp
# chmod go+rw /mnt/newdisk/tmp
Installing grub on new disk requires chrooting:
# mount -o bind /dev /mnt/newdisk/dev
# chroot /mnt/newdisk
# grub
> root (hd1,0)
> setup (hd1)
> quit
Note that hd1 is /dev/sdb1 in our case

Fix grub:
# nano /boot/grub/grub.conf
change the line containing the image(s):
 /boot/vmlinuz-3.2.0-4-686-pae ro root=/dev/mapper/vg1-lv_XXX
  to
  /boot/vmlinuz-3.2.0-4-686-pae ro root=LABEL=root
or by UUID:
/boot/vmlinuz-3.2.0-4-686-pae ro root=UUID=a53bee3f-3a18-45b3-9b7a-8e8e0f2b3e1b 
you can get your UUID by:
# blkid /dev/sda1
Configure fstab:
# nano /etc/fstab
/dev/sda1    /                       ext3    defaults        1 1
tmpfs                   /dev/shm                tmpfs   defaults        0 0
devpts                  /dev/pts                devpts  gid=5,mode=620  0 0
sysfs                   /sys                    sysfs   defaults        0 0
proc                    /proc                   proc    defaults        0 0
/dev/sda2       swap                    swap    defaults        0 0
I removed all LVM mounts, and configured one mount for my root filesystem, and a second one for swap
You could use disk UUID instead of /dev/sdax:
UUID=a53bee3f-3a18-45b3-9b7a-8e8e0f2b3e1b /                       ext3    defaults        1 1
UUID=83a41156-85aa-4c40-a6db-b20d42fcb444       swap                    swap    defaults        0 0
Exit chroot after saving your fstab:
# exit

Δευτέρα 9 Μαρτίου 2015

Getting a SIP call whenever an IFTTT recipe is triggered

In my previous post I explained how to run a custom script triggered by an IFTTT reipe.
In this post I will explain how to get a SIP call when an IFTTT action is triggered.

I assume you have created the recipe, linked Dropbox and configured incrontab as explained here.

Requirements:
-A cli SIP client, pjsua (from the pjsip library) is perfect for this job
-expect package (in order to "talk" to pjsua with spawn)
-festival package (this includes text2wave for text-to-speech)

I didn't find pjsip as a package for Debian, so I had to download it from http://www.pjsip.org and compile it manually, it's straightforward (configure-make dep-make).
pjsua is located inside the /pjsip-apps folder so you may either copy it somewhere convenient or create a soflink in order to run it.

Create your pjsip conf file (this may depend on your SIP provider) and paste:

--null-audio
--registrar sip:your.sip.provider.com
--realm=*
--id sip:sipuser@your.sip.provider.com
--username sipuser
--password yourpass
--auto-play
--max-calls 1

You may at first check that you can make a SIP call:
pjsua sip:1234567890@your.sip.provider.com --config-file /home/bob/pjsip.conf 

Then edit your script.sh file: (I found this script somewhere on the web and did slight modifications to it)

#! /bin/bash

EXPECT=/usr/bin/expect
SOUNDFILE=/tmp/alert.wav
TEXT2WAVE=/usr/bin/text2wave
DURATION=20
MESSAGE="Monitoring Alert"
PJSUACONFIG=/home/bob/pjsip.conf
CALLNUMB=1234567890

locked=false
 while [[ $locked == false ]]; do
     if [[ ! -f /tmp/caller.lock ]]; then
         touch /tmp/caller.lock
         locked=true
     else
         sleep 5
     fi
 done

# Generating the message
$TEXT2WAVE -o $SOUNDFILE -f 8000 << EOF
$MESSAGE
EOF

$EXPECT << EOF
spawn pjsua sip:$CALLNUMB@your.sip.provider.com --config-file $PJSUACONFIG --play-file $SOUNDFILE --duration $DURATION
expect "VAD re-enabled"
sleep 1
send "q\n"
EOF

# Cleaning up
rm $SOUNDFILE

# Removing the lock file and IFTTT file
rm /tmp/caller.lock
rm /home/bob/Dropbox/IFTTT/runme*

That's it!


Using IFTTT recipe to run a shell script

Requirements:

-an IFTTT account
-a Dropbpx account linked to your IFTTT account
-a server running linux (mine runs Debian linux).
- incrontab package, available on most linux distros.

First of all, install Dropbox on your linux box. You may find instructions here:
https://www.dropbox.com/install?os=lnx
Run Dropbox and create a folder named IFTTT on your home Dropbox folder.

mkdir ~/Dropbox/IFTTT

Then, you have to create your IFTTT recipe at ifttt.com. You may use whatever "this" statement you wish. On "that" statement you chose Dropbox, and create a file. Give a name to your file, for my example I use "runme". In the content you may put whatever you want. In Dropbox folder path you use IFTTT.

Create your script file on your home folder named "script.sh" and make it executable: chmod +x script.sh. Make sure your script executes properly:

./script.sh

Add the user that has both rights to run the script and to write into the Dropbox folder in the incrontab allow list:

echo bob >/etc/incron.allow

Then modify your incrontab. This package continuously checks a folder for changes. In my example it will check my IFTTT folder for modifications.

incrontab -e

inside your incrontab file paste:

/home/bob/Dropbox/IFTTT/ IN_MODIFY,IN_CREATE,IN_MOVED_TO /home/bob/script.sh

Checking that incrond is working:

Open a new terminal window and run:
sudo tail -f /var/log/syslog | grep incrond

Then, create a file inside your /home/bob/Dropbox/IFTTT folder:

touch /home/bob/Dropbox/IFTTT/lala

You will instantly see on your logs:

Mar  9 15:50:03 linuxbox2 incrond[361]: (bob) CMD (/home/bob/script.sh)

This means that your script just triggered and will trigger every time your recipe runs!