Grandstream GXP1610 Reboot-o-matic

Standard

I wrote a nice little script to fix a problem I’ve been having with my work VOIP phone. It would lose connection but the screen wouldn’t let you know it had. I didn’t notice that it hadn’t been connected for *two* weeks until someone left a voicemail.

The phone did have a built in SSH interface, which had a reboot command, so I tried using ssh -i key admin@phone < commands.txt to feed it a bunch of commands. I had to pipe a text command to ssh because it doesn’t use a ‘real’ shell on the phone, just Grandstream’s proprietary command interface, which doesn’t accept commands directly when called from ssh.

This was okay, it restarted the phone but it would also reset the password back to ‘admin’. Not very secure really… So I looked for a way to reboot it using the web interface. I fired up Firefox’s network debugger and started to reverse engineer how commands were processed.

I worked out what I needed to do to login as an admin, and then issue the reboot command. It worked. Nice.

#!/bin/bash
sid=$(curl -k -s -c /tmp/cookies.txt -d"password=hunter2" https://192.168.1.50/cgi-bin/dologin --referer https://192.168.1.50 | sed -r 's|.*"sid": "([0-9a-z]+)".*|\1|' )
curl -k -s -b /tmp/cookies.txt -d"request=REBOOT&amp;sid=${sid}" https://192.168.1.50/cgi-bin/api-sys_operation --referer https://192.168.1.50
rm /tmp/cookies.txt

Really though, I wanted a better solution, sure I could reboot the phone every day to make sure it’s working but what would be awesome would be if my script could check to see if the phone was connected to my SIP account, and if it wasn’t, or there was some kind of error, it could reboot it or at least tell me there was an error.

So I wrote version 1 of my script and got it working, when the SIP connection isn’t registered, it will restart the phone.

#!/bin/bash
# Grandstream GXP1610 Reboot-o-matic v1
# Authored on 12/10/2020
# by jcx
# https://jcx.life
# Licence: GPLv3 (or at your option, any later version.)
# Usage: gsreboot [IP/Hostname]
sid=$(curl -k -s -c /tmp/cookies.txt -d"password=hunter2" https://${1}/cgi-bin/dologin --referer https://${1} | sed -r 's|.*"sid": "([0-9a-z]+)".*|\1|' )
status=$(curl -k -s -b /tmp/cookies.txt -d"request=vendor_fullname:P35:PAccountRegisteredServer1:PAccountRegistered1" https://${1}/cgi-bin/api.values.get --referer https://${1} | sed -r 's|.*"PAccountRegistered1": "([0-9a-z]+)".*|\1|' )
if [ ${status} != 1 ]
then
echo "Requesting reboot on ${1} ..."
date
curl -k -s -b /tmp/cookies.txt -d"request=REBOOT&amp;sid=${sid}" https://${1}/cgi-bin/api-sys_operation --referer https://${1}
fi
rm /tmp/cookies.txt

Now I just need some way to automate it, which is where cron comes in. Cron will run a command however often you like, so I just set it to every 5 minutes to do a check, and now I won’t miss any more important work phone calls.

*/5 * * * * /usr/local/bin/gsreboot.sh 192.168.1.50

Okay, so the first version of the script I wrote, while it works, it isn’t very elegant. It didn’t really report any error messages and wasn’t user-configurable so I’ve rewritten it (v2!) and now it supports some options, and has more sensible error messages.

#!/bin/bash
#################################
#
# Grandstream GXP1610 Reboot-o-matic v2
#
# Authored on 12/10/2020
# by jcx
# https://jcx.life
#
# Licence: GPLv3 (or at your option, any later version)
#
#################################
# Please edit the password below to be the admin account on your GXP1610.
# I defined it here so that you don't need to use it on the command line.
# You shouldn't need to make any other changes.
#################################
 
password="hunter2"
 
#### Begin Script
 
if [ -z ${1} ]
    then
    echo "----------------"
    echo "gsreboot2: GXP1610 Reboot-o-matic v2 by jcx (https://jcx.life) licenced under GPLv3"
    echo "Usage: gsreboot2.sh [IP/Hostname] [Protocol: http/https] [Ignore Certificate Errors: Y/N]"
    echo "Example: gsreboot2.sh 192.168.1.50 https Y"
    echo "----------------"
    echo "This will connect to the Grandstream phone on 192.168.1.50"
    echo "using https and will ignore any certificate errors."
    echo "Use with cron every 5 minutes, as it takes the phone about 3 minutes to boot."
    echo "Don't forget to change the password at the top of the script!"
    exit
fi
 
if [ -f "/tmp/gsreboot2.txt" ]
    then
    rm "/tmp/gsreboot2.txt"
fi
 
if [ -z ${2} ]
    then 
    proto="http"
fi
if [ "${2}" = "https" ]
    then
    proto="https"
fi
if [ "${2}" = "http" ]
    then
    proto="http"
fi
 
if [ -z ${3} ]
    then 
    certignore=""
fi
if [ "${3}" = "Y" ]
    then 
    certignore="-k "
fi
if [ "${3}" = "N" ]
    then
    certignore=""
fi
 
sid=$(curl ${certignore}-s --connect-timeout 10 -c /tmp/gsreboot2.txt -d"password=${password}" ${proto}://${1}/cgi-bin/dologin --referer ${proto}://${1} | sed -r 's|.*"sid": "([0-9a-z]+)".*|\1|' )
status=$(curl ${certignore}-s --connect-timeout 10 -b /tmp/gsreboot2.txt -d"request=vendor_fullname:P35:PAccountRegisteredServer1:PAccountRegistered1" ${proto}://${1}/cgi-bin/api.values.get --referer ${proto}://${1} | sed -r 's|.*"PAccountRegistered1": "([0-9a-z]+)".*|\1|' )
if [ "${status}" = "0" ]
    then
    echo "VOIP account not registered..."
    echo "Requesting reboot on ${1} ..."
    request=$(curl ${certignore}-s --connect-timeout 10 -b /tmp/gsreboot2.txt -d"request=REBOOT&amp;sid=${sid}" ${proto}://${1}/cgi-bin/api-sys_operation --referer ${proto}://${1} | sed -r 's|.*"body": "([0-9a-z]+)".*|\1|' )
    if [ "${request}" = "savereboot" ]
        then
        echo "Reboot request has been acknowledged."
    fi
    if [ -f "/tmp/gsreboot2.txt" ]
        then
        rm "/tmp/gsreboot2.txt"
    fi
    exit
fi
 
if [ "${status}" != "1" ]
    then
    echo "Error: Cannot determine status of VOIP account."
fi
 
###
# Enable this code if you want output on success... disabled by default because it works with cron.
###
 
#if [ "${status}" = "1" ]
#    then
#    echo "Success! Your VOIP account is active. No reboot required."
#fi
 
if [ -f "/tmp/gsreboot2.txt" ]
    then
    rm "/tmp/gsreboot2.txt"
fi

Its grown from 18 lines of code to 109(!). This isn’t bad, considering before I wrote these scripts, I’d never written anything in shell / bash script before. So I replaced the entry in my crontab to run the new script every 5 minutes.

*/5 * * * * /usr/local/bin/gsreboot2.sh 192.168.1.50 https Y

Below is what it looks like in my email client on my Linux box when the SIP account is not registered and it needs a reboot.

Date: Mon, 12 Oct 2020 00:10:07 +0100
From: Cron Daemon <root@localhost>
To: jcx@localhost
Subject: Cron <jcx@localhost> /usr/local/bin/gsreboot2.sh 192.168.1.50 https Y
VOIP account not registered...
Requesting reboot on 192.168.1.50 ...
Reboot request has been acknowledged.

If the script encounters an error, it will email an error response. This looks like the following:

Date: Mon, 12 Oct 2020 00:35:07 +0100
From: Cron Daemon <root@localhost>
To: jcx@localhost
Subject: Cron <jcx@localhost> /usr/local/bin/gsreboot2.sh 192.168.1.50 https Y
Error: Cannot determine status of VOIP account.

I hope this proves useful to you, it certainly has to me. Not only because my phone will always be connected but I also learnt how to do some basic shell scripting. Have a great day!

Setting Up Auto Mounting Encrypted Raid Disks

Standard

This is a little guide (currently under construction) for how I handle encrypted disks on Linux. This won’t be the ultimate ‘tin foil hat’ guide, as the attack vector this is intended to protect from is physical theft of the hardware, so that the data can’t be accessed from elsewhere. It obviously will not handle a targeted hacking attempt or the $5 wrench method, but I believe it gives security and convenience to a level appropriate for me.

xkcd 538: describing the $5 wrench method of breaking security.

The reason this started is because my physical health is deteriorating and getting up to enter a password at the console on every reboot is tiresome. Therefore I came up with a new way of handling encrypted drives to not only increase security but also make things a bit more convenient.

Of course before following any of these instructions, you should be aware of my standard disclaimer.

Caution – You need to secure the location of where you store your key. If you fail to secure your key with an appropriate mechanism, this entire exercise is fruitless.

Examples include: IP restricting access to your key provisioning system, using a strong username and password, using an easy to revoke token based storage mechanism, verifying HTTPs transfer certificates and countless others.

Included below is a method similar to what I use to secure where I store my keys.

Create a keyfile

dd bs=256 count=1 if=/dev/random | base64 > data-keyfile

Upload the keyfile somewhere, for example a HTTPS server with a valid certificate, or S3 or Azure key storage, and then make a script to download the key from where you put it. If you’re storing your key on a HTTPS server, here is an example htaccess file to secure access to the directory to specific IPs and a user/password section to further increase security. This works with Apache 2.4 but the syntax may be different for later versions.

order deny,allow
deny from all
allow from 192.168.1.100

Options -Indexes
AuthType Basic
AuthName "Restricted Access"
AuthUserFile "/secure/path/to/htpasswd"
Require valid-user

Once you have uploaded it somewhere don’t forget to delete the original source file securely from your system (for example with shred).

#!/bin/sh
set -e
# Request the file from somewhere, maybe blob storage, asure, S3 or HTTPS Server, then pipe it through `base64 -d` to decode it from base64
curl -s --basic --user username:password "https://example.net/data-keyfile" | base64 -d

Then move the script somewhere and give it the right permissions

# Ensure the owner of this file is "root"
chown root:root /etc/luks/get-key.sh
# Allow only the owner (root) to read and execute the script
chmod 0500 /etc/luks/get-key.sh

Create the raid

# if all drives are already blank and ready to be added. Replace drives as appropriate.
mdadm --create /dev/md2 -l 1 -n 2 /dev/sdc1 /dev/sdd1
# if you need to create a 'degraded' array with a drive missing.
mdadm --create /dev/md2 -l 1 -n 2 /dev/sdc1 missing

Then encrypt the array

# Encrypt the disk
# Replace md2 with the correct array!
/etc/luks/get-key.sh | cryptsetup -d - -v luksFormat /dev/md2

# Open the encrypted volume, with the name "data"
# Replace md2 with the correct array!
/etc/luks/get-key.sh | cryptsetup -d - -v luksOpen /dev/md2 data

# Create a filesystem on the encrypted volume
mkfs.ext4 -F /dev/mapper/data

# Close the encrypted volume
cryptsetup -v luksClose data

Find the encrypted partitions UUID

$ lsblk --fs
NAME    FSTYPE      LABEL           UUID
[..]
sdc
└─sdc1         linux_raid_mem server:1 a38cbabe-0f12-3643-f3232-998822c5d42
  └─md2        crypto_LUKS             a17db19d-5037-4cbb-b50b-c85e3e074864 

Then create a script to run on boot to automount

#!/bin/bash
if [ -b "/dev/mapper/data" ]
	then
		if [[ $(findmnt -M "/disks/data") ]]; then
		:
		else
    		echo "Not mounted but unlocked... trying to mount..."
	mount -t ext4 -o errors=remount-ro /dev/mapper/data /disks/data
		fi
	else
		curl -s --basic --user username:password "https://example.net/data-keyfile" | base64 -d | /sbin/cryptsetup -d - -v luksOpen /dev/disk/by-uuid/a17db19d-5037-4cbb-b50b-c85e3e074864 data
		mount -t ext4 -o errors=remount-ro /dev/mapper/data /disks/data
fi

if [[ $(findmnt -M "/disks/data") ]]; then
# Anything you want to run after the disks are mounted
		echo "All disks mounted, starting services..."
		echo "Starting samba..."
		systemctl start smbd
fi

and add it to root’s crontab on reboot.

# m h  dom mon dow   command
@reboot sleep 30 && /etc/luks/start-crypto.sh

Don’t forget to disable any services you don’t want to run until the encrypted drives are mounted, for example samba

systemctl disable smbd

Create the mount point

mkdir /disks/data

And finally a script to stop encrypted drives (if required)

#!/bin/bash
echo "Stopping Samba..."
systemctl stop smbd

if [[ $(findmnt -M  "/disks/data") ]]; then
    echo "/disks/data is mounted, trying to unmount..."
	umount /dev/mapper/data
    echo "Attempting to close luks on /dev/mapper/data ..."
	if [ -b /dev/mapper/data ]
		then
		/sbin/cryptsetup -d - -v luksClose data
	fi
else
	if [ -b /dev/mapper/data ]
	then
    	echo "/disks/data is not mounted, but is unlocked, will attempt to close ...."
	/sbin/cryptsetup -d - -v luksClose data
	else
	echo "/disks/data is not unlocked or mounted, nothing to do."
	fi
fi

This work was inspired by an article on https://withblue.ink/2020/01/19/auto-mounting-encrypted-drives-with-a-remote-key-on-linux.html by Alessandro Segala and adapted/changed to meet my requirements.

Debugging Windows 10 at Startup

Standard

It’s almost impossible to be able to hit F8 during Windows 10’s start up. The “official” way to get into the boot menu is to let Windows 10 start and get to the login screen, hold the shift key and click “Restart”. The problem with this is, what if you can’t get to the login screen?

Many times I’ve had a simple issue that could be fixed in Safe Mode or using the basic graphics mode available from the boot menu. I’ve found a method that makes this debugging easy and gives you plenty of time to be able to press F8 if you need to on boot, without taking too much time away from the actual boot. It’s a user configurable timeout too, so you can set it to what you want.

Of course before following any of these instructions, you should be aware of my standard disclaimer.

Firstly, enable the Legacy Bootloader, by opening an administrative command prompt.

bcdedit /set "{current}" bootmenupolicy legacy

This will enable the old style operating system selector from Windows 7. Next you set it to display the menu with the following command.

bcdedit /set {bootmgr} displaybootmenu yes

Finally you control how long the timeout is. The default 30 seconds is quite a long time to wait if you don’t press any key, so I use the timeout of 5 seconds, which gives me ample time if I need to get into the advanced boot options menu, but it doesn’t slow down the boot that much if I don’t.

bcdedit /set {bootmgr} timeout 5

That’s it! If you ever need to debug a simple start up issue, you don’t have to find your rescue CD, or reset during boot to launch “startup repair”. It’s saved my skin so many times already 🙂

Audio Terminal Bell (Software Bell) in Xubuntu with xfce-terminal

Standard

I have wanted a software audio based terminal bell in linux for years. Similar to in PuTTY on Windows you can chose any arbritary wav sound file as your terminal bell sound, I wanted this functionality on Linux, and I have wasted lots of time over the years trying to get this working. I haven’t had much luck… until today!

I was setting up a new Xubuntu 18.04LTS machine and was going through the preferences in xfce-terminal and noticed it had an option for “Audible Bell” in the advanced features menu. I turned it on and it didn’t work, but it prompted me to try and find a solution again.

Here’s the commands I used to get it working.

sudo apt-get install gnome-session-canberra sox
xfconf-query -c xsettings -p /Net/EnableEventSounds -s true
xfconf-query -c xsettings -p /Net/EnableInputFeedbackSounds -s true
xfconf-query -c xsettings -p /Net/SoundThemeName -s "freedesktop"

Then you need to add the following to the end of your .profile file in your home directory (~/.profile)

GTK_MODULES="$GTK_MODULES:canberra-gtk-module"
export GTK_MODULES

Then add the following to /etc/pulse/default.pa

# audible bell
load-sample-lazy x11-bell /usr/share/sounds/freedesktop/stereo/bell.oga
load-module module-x11-bell sample=x11-bell

Then restart pulseaudio with

pulseaudio -k

Make sure your “System Sounds” is turned up in the Volume Control applet and finally make sure the following appears in ~/.config/xfce4/terminal/terminalrc under [Configuration]

MiscBell=TRUE

You can also set this under “Preferences/Advanced/Audible Bell”. You will probably need to logout and logon again, but other than that everything should work. You can change the sound to a .oga file of your choice by changing the path of the sound in the load-sample-lazy command above.

jcx’s Standard “New Drive” Procedure

Standard

Don’t forget: This advice is posted with my Standard Disclaimer. Please read that before proceeding.

01. If the drive is not a new sealed retail drive, check SMART values are okay before testing. (smartctl -a /dev/sdX), save to compare later.

02. Run SMART short test (smartctl -t short /dev/sdX)

03. Run SMART conveyance test (if supported) smartctl -t conveyance /dev/sdX

04. Some drives will only update some SMART attributes if offline data collection is enabled. To make sure, turn offline testing every 4 hours with smartctl -o on and then start an offline test (smartctl -t offline /dev/sdX)

05. Verify drive passed all these tests (smartctl -l xselftest,selftest /dev/sdX). Create a second log file for comparison later.

06. Zero the drive with dd (dd if=/dev/zero of=/dev/sdX bs=4M status=progress).

[!] Keep an eye on the drive temperature (if supported) with smartctl -l scttemp the logged historical data will vary between manufacturers. [/!]

07. After ZEROing the drive, it will have reallocated any bad sectors from the factory. Compare the SMART values to the ones you took at the beginning, paying close attention to Pending and/or Reallocated Sectors.

08. Check the drive’s error log (smartctl -l xerror,error /dev/sdX) to see if there are any reports of non-fatal errors which haven’t resulted in a bad sector.

09. Next comes a full write test. You can either use the long test, using badblocks which will perform 4 full read and 4 full write tests. (badblocks -wvsb 4096 /dev/sdX) This can take tens of hours. If you are in a hurry, you can also use a single pass test. (nwipe?)

10. After completion again check the SMART values (smartctl -a /dev/sdX) and compare them to the original readings.

[!] Some drives also support extended device statistics (smartctl -l devstat /dev/sdX) which can show you some non-standard stuff. [/!]

11. If the drive passes these tests without error it will usually be a good drive. The surface has been written and read from several times to ensure there’s no major problems.

12. If you want to stress test the drive mechanism (actuator/heads) you can use fio to perform small reads/writes at random locations across the entire disk surface. Be sure to keep an eye on the temperatures while doing this. If the drive isn’t properly cooled you might run into problems. It is probably reasonable to run this test for around two or three hours.