Skip to content

Instantly share code, notes, and snippets.

@woyczek
Last active June 20, 2018 09:42
Show Gist options
  • Save woyczek/5b85f11ab43dced63353716f6744616f to your computer and use it in GitHub Desktop.
Save woyczek/5b85f11ab43dced63353716f6744616f to your computer and use it in GitHub Desktop.
NTP monitoring thru HTTP proxies

Goal

to provide two tools for date/time monitoring:

  • ntpq daemon quality analysis
  • date/time queries thru HTTP headers for time reliancy

Usage remarks

This piece of sofware may work with minimal modifications on AIX, as it was intended to work on this platform on first write. It NEEDS a $http_proxy variable set to work for distant HTTP queries, as it requests this proxy for all http headers.

Examples

to check date/time from distant HTTP queries: get_http_date.sh -d -v

to check NTP Quality get_http_date.sh -d -N

to check quality and send mails only on error to exploit mailbox get_http_date.sh -N -M

Dependencies

  • bc -- calculator
  • host -- hostname/ips query tools
  • perl-HTTP-Date -- Date manipulation perl module HTTP::Date
#!/bin/sh
#
# Nom: menu.sh
# Auteur : Gaetan RYCKEBOER
# Role : superviser l'horloge locale par rapport a un pool de serveurs internet
# Date : 06.02.2013
# Version : 1.19
#
# Root : non
#
#
# Historique
# Version | Date | Ref. | Qui | Description
# v1.0 | 20.08.10 | | GRY | Creation
# v1.1 | 23.08.10 | | GRY | send email
# v1.2 | 27.08.10 | | GRY | Supervision NTP
# v1.3 | 30.08.10 | | GRY | Better NTP monitoring
# v1.4 | 01.09.10 | | GRY | Add "crossover" ntp
# v1.5 | 08.09.10 | | GRY | better reach monitoring
# v1.6 | 08.09.10 | | GRY | Refactor ntpq per line checks
# v1.7 | 09.09.10 | | GRY | Remove completely out clock sources
# v1.8 | 11.09.10 | | GRY | Ajout courriel en cas d'erreur
# v1.9 | 11.09.10 | | GRY | Suppression des alertes NTP si reach trop faible
# v1.10 | 14.09.10 | | GRY | Ajout de la variable "flash"
# v1.11 | 20.09.10 | | GRY | Si reach > 0, pas d'erreur "unreach". On suppose que c'est transitoire.
# v1.12 | 30.10.10 | | GRY | Ajout supervision demon NTP uniquement
# v1.13 | 13.12.10 | | GRY | Incompatibilite syntaxe anciens ksh [ '==' ]
# v1.14 | 04.01.11 | | GRY | Incompatibilite syntaxe anciens ksh [ '==' ]
# v1.15 | 03.05.11 | | GRY | Factorisation gestion demon ntp
# v1.16 | 26.07.12 | | GRY | Fix [ " " ]
# v1.17 | 18.01.13 | | GRY | exit 1 on GetHTTPDate and shift
# v1.18 | 06.02.13 | | GRY | Help + remove yahoo
# v1.19 | 20.06.18 | | GRY | Change \n to end of line, fix ntpq clocks ID, english translation
#
# Affichage des details
VERBOSE=false
# Affichage des lignes
DEBUG=false
# Ce qui sera envoye par courriel
RETOUR="";
# ERR=ERROR or ERR="" selon la gravite des erreurs
ERR=""
# Version du script
VERSION=$(egrep '^# Version :' $0 | cut -d ':' -f 2 | tr -d ' ')
# Emplacement des binaires
NTPQx="/usr/bin/ntpq /usr/sbin/ntpq"
NTPDx="/usr/bin/ntpd /usr/sbin/xntpd /usr/bin/xntpd /usr/sbin/ntpd"
for i in $NTPDx ; do
[ -x "$i" ] && NTPD=$i
done
for i in $NTPQx ; do
[ -x "$i" ] && NTPQ=$i
done
###########
# Some constants
URL_LIST="www.google.com www.viamichelin.com www.ibm.com www.pool.ntp.org"
HTTP_PROXY=$http_proxy # Local proxy varibale. Must be set.
MailFrom="From: \"EXPLOIT\" <[email protected]>"
MaxSecOffset=5 # Max number of seconds between HTTP check and local time
MinNTPReach=1 # Minimal number of servers to be reached
export LANG=C;export LC_ALL=C
HTTP_PROXY_PORT=$(echo $HTTP_PROXY | cut -d / -f 3| cut -d : -f 2)
HTTP_PROXY=$(echo $HTTP_PROXY | cut -d / -f 3| cut -d : -f 1)
##### Goal ####
# to provide two tools for date/time monitoring:
# - ntpq daemon quality analysis
# - date/time queries thru HTTP headers for time reliancy
#
##### Usage remarks ####
# This piece of sofware may work with minimal modifications on AIX, as it was intended to work on this platform on first write.
# It NEEDS a $http_proxy variable set to work for distant HTTP queries, as it requests this proxy for all http headers.
#
##### Examples ####
# to check date/time from distant HTTP queries:
# get_http_date.sh -d -v
#
# to check NTP Quality
# get_http_date.sh -d -N
#
# to check quality and send mails only on error to exploit mailbox
# get_http_date.sh -N -M
#
##### Dependencies ####
# - bc -- calculator
# - host -- hostname/ips query tools
# - perl-HTTP-Date -- Date manipulation perl module HTTP::Date
#
###########
Help() {
echo "$(basename $0) version $VERSION"
echo >&2 "Usage: $0 [-d] [-D] [-m mailto@domain] [-M mailto@domain] [-n] [-v]"
echo >&2 " $0 [-d] [-D] [-h]
-d : verbose
-D : debug
-m : send result by email
-M : idem but only on error
-n : NTP daemon monitoring
-N : NTP clocks quality monitoring
-v : HTTP check of time and date
-h : help / aide"
}
ValidateNTP() {
NTPDisabled=0
NTPSpare=0
NTPValides=0
NTPOff=0
NTPReach=0
$DEBUG && set -x
NTPError=""
if ValidateNTPDaemon ; then
NTPServers=$($NTPQ -np | egrep -v ' reach +delay +offse' |grep -v '=========' 2>/dev/null | sed 's/^\(.\)/[\1] /' )
NTPHead=$($NTPQ -np | egrep ' reach +delay +offse')
#####################################################################################################
# Nouvelle boucle par ligne du ntpq
$VERBOSE && RETOUR="$RETOUR
Ntpq :"
NTPServers2="sta$NTPHead - hostname"
for line in $(echo "$NTPServers" | sed 's/\[ \]/[@]/;s/ /%/g' ) ; do
line="$(echo $line | sed 's/%/ /g')"
status=$(echo $line | awk '{print $1}'|tr '@' ' ')
host=$(echo $line | awk '{print $2}')
# Specifique AIX
##hostname=$(host $host 2>/dev/null| tail -n 1 | cut -d ' ' -f 1| cut -d '.' -f -2)
hostname=$((host $host 2>i/dev/null||echo $host )|tail -n 1|sed 's/.*name pointer \(.*\)/\1/')
#hostname=$(dig +short -x $host 2>/dev/null)
refid=$(echo $line | awk '{print $3}')
stratum=$(echo $line | awk '{print $4}')
type=$(echo $line | awk '{print $5}')
when=$(echo $line | awk '{print $6}')
poll=$(echo $line | awk '{print $7}')
reach=$(echo $line | awk '{print $8}')
delay=$(echo $line | awk '{print $9}')
offset=$(echo $line | awk '{print $10}')
disp=$(echo $line | awk '{print $11}')
NTPServers2="$NTPServers2
$(echo "$line" | sed "s/^\[@\]/[ ]/;s/\(^... $host .*\)/\\1 - $( echo $hostname| cut -d '.' -f -2)/")"
# Les serveurs valides
if echo "$status" | egrep "[\*o#]" >/dev/null 2>&1 ; then
((NTPValides=NTPValides+1))
if [ "$type" = "l" -o "$type" = "-" ] ; then
NTPError="$NTPError
ERROR : The reference clock is localhost"
ERR=ERROR
ERRlocal=1
fi
fi
# Serveurs de spare
echo "$status" | egrep "[\+]" >/dev/null 2>&1 && \
((NTPSpare=NTPSpare+1))
# Serveurs desactives par le crossover
if echo "$status" | egrep "[xX]" >/dev/null 2>&1 ; then
((NTPDisabled=NTPDisabled+1))
NTPError="$NTPError
ERROR : the server $hostname ($host) is declared 'invalid' $status"
ERR=ERROR
fi
if echo "$type" | grep "-" >/dev/null 2>&1 ; then
$VERBOSE && NTPError="$NTPError
INFO : $hostname ($host) configuration is slightly out of phase Type: '-'"
else
# conversion la colonne "reach" en binaire, et on compte les "1".
NTPReachBin=$(printf "%08d" $(echo "obase=2;ibase=8;$reach" | bc ))
let NTPReachNum=$(echo "$NTPReachBin" | tr '1' '
' | wc -l | tr -d ' ')-1
$VERBOSE && RETOUR="$RETOUR
reach : $(printf "%30s - %3d" $hostname $reach) - #${NTPReachBin} - $NTPReachNum"
# Moins de 6/8 reponses : on alerte
if [ "$NTPReachNum" -lt "$MinNTPReach" ] ; then
ERR=ERROR
NTPError="$NTPError
ERROR : the clock serverl $hostname ($host) is not reliable
: reach=$(printf '%3d' $reach) means the following sequence '#$(echo $NTPReachBin|tr '0' '.' |tr '1' 'X' )' ; $NTPReachNum responses"
# On n'alerte que si le serveur est totalement injoignable
else
let NTPReach=$NTPReach+1
fi
# Serveurs injoignables / cf disp et reach
if echo "$status" | egrep "^\[ \]" >/dev/null 2>&1 ; then
((NTPOff=NTPOff+1))
if [ "$type" != 'l' ] ; then
NTPError="$NTPError
WARN : The clock of $hostname ($host) is not suitable for synchronization $status"
# Dans ce cas, on affiche aussi la colonne reach
NTPError="$NTPError
DEBUG : reach=$(printf '%3d' $reach) means the following sequence '#$(echo $NTPReachBin|tr '0' '.' |tr '1' 'X' )' ; $NTPReachNum responses"
fi
fi
fi
done
###############################
# Serveurs de reference
[ "$NTPValides" -lt 1 ] && NTPError="$NTPError
ERROR : no reference clock server" && ERR=ERROR
# Serveurs de secours
[ "$NTPSpare" -lt 1 ] && NTPError="$NTPError
WARN : Not enough backup servers"
# Serveurs inactifs
[ "$NTPOff" -gt 1 ] && NTPError="$NTPError
WARN : some servers are not reliable"
# Serveurs desactives
[ "$NTPDisabled" -gt 3 ] && NTPError="$NTPError
WARN : Too many 'bad clock' servers"
# Serveurs joignables
[ "$NTPReach" -lt 3 ] && NTPError="$NTPError
WARN : Not reachable servers enough"
#####################################################################################################
if [ "" != "$NTPError" ] ; then
# Recuperons la liste des ID
NTPError="$NTPError
DEBUG : FLASH"
# Recuperons la liste des ID
for assID in $($NTPQ -nc 'as' | grep -v '====' | grep -vi 'ind assID' | tr -s ' ' | cut -d ' ' -f 3 ) ; do
# Pour chaque ID on recupere le flash et l'IP
NTPQLIST=$($NTPQ -nc "rv $assID" )
#NTPQLIST=$($NTPQ -nc "rv $assID" )
IP=$(echo "$NTPQLIST"|egrep 'srcadr=' | sed 's/.*srcadr=\([^,]*\),.*/\1/')
FLASH=$(echo "$NTPQLIST"|egrep 'flash=' | sed 's/.* flash=\([^,<]*\)\(<.*>\),.*/\1 - \2/')
NTPError="$NTPError
: $IP - $FLASH"
done
if [ "$ERR" = "ERROR" ] ; then
RETOUR="$RETOUR
ERROR : Probleme sur NTP"
else
RETOUR="$RETOUR
WARN : Probleme sur NTP"
fi
RETOUR="$RETOUR
$NTPError
Joignables:$NTPReach Valide:$NTPValides Secours:$NTPSpare Inutilisables:$NTPOff Desactives:$NTPDisabled"
RETOUR="$RETOUR
min.......:3 min...:1 min....:1 .............:- max.......:0
"
RETOUR="$RETOUR
$NTPServers2
"
#ERR=ERROR
else $VERBOSE && RETOUR="$RETOUR
$NTPServers2
"
fi
else
#RETOUR="$RETOUR
#ERROR : Le demon NTP n'est pas demarre"
#ERR=ERROR
:
fi
}
ValidateNTPDaemon() {
NTPError=""
if ps -ef | grep -v grep | grep $NTPD >/dev/null ; then
:
true
else
RETOUR="$RETOUR
ERROR : NTP daemon is not running"
ERR=ERROR
false
fi
}
GetHTTPDate() {
$DEBUG && set -x
( sleep 1 ; echo "HEAD / HTTP/1.1" ; echo "Host: $1" ; echo ; echo "quit" ; sleep 1 ) | \
telnet $HTTP_PROXY $HTTP_PROXY_PORT 2>/dev/null| grep Date: |\
awk '{print $2" "$3" "$4" "$5" "$6" "$7}' | perl -pe 'use HTTP::Date;$_=str2time($_)+1'
# On est oblige d'ajouter une seconde de "sleep 1" pour la fin de telnet
}
ValidateDateTime() {
Diff=0
NbDiff=0
SvrDiff=0
DateSVR=0
$DEBUG && set -x
for HostName in $URL_LIST ; do
DateHTTP=$( GetHTTPDate "$HostName" )
LocalDate=$( date -u | perl -pe 'use HTTP::Date;$_=str2time($_)')
let DateDiff=$DateHTTP-$LocalDate ;
$VERBOSE && RETOUR="$RETOUR
$HostName : $DateDiff" >&2
# Si le décalage est trop important ET que le serveur HTTP a répondu correctemet,
if [ "$( echo $DateDiff | tr -d '-' )" -gt "$MaxSecOffset" -a "$DateHTTP" -gt 10 ] ; then
# le decalage est trop important
let NbDiff=$NbDiff+1
RETOUR="$RETOUR
WARN : Clock server $HostName is out of synchronization with $(hostname) ($DateDiff seconds)"
DateSVR=$DateDiff
fi
done
if [ "$NbDiff" -gt 1 ] ; then
# Plus de un serveur en decalage
RETOUR="$RETOUR
ERROR : System clock is out of phase from $NbDiff sources."
ERR=ERROR
RETOUR="$RETOUR
stopsrc -s xntpd ; date -u -a $DateSVR ; sleep 3000 ; startsrc -s xntpd"
fi
}
##########################################################################################
while getopts :m:M:dhnNDv o
do case "$o" in
m) MailTo="$OPTARG";;
N) ValidateNTP;;
n) ValidateNTPDaemon;;
v) ValidateDateTime;;
d) VERBOSE=true;;
D) DEBUG=true;;
M) MailErr="$OPTARG";;
[?]|h) Help
exit 1;;
esac
done
let T=$OPTIND-1
shift $T
# shift $OPTIND
$DEBUG && echo $NTPQ $NTPD
##########
if [ ! "$MailErr" = "" ] && [ "$ERR" = "ERROR" ] ; then
if [ "$MailTo" = "" ] ; then
MailTo="$MailErr"
else
MailTo="$MailTo,$MailErr"
fi
fi
if [ ! "$MailTo" = "" ] && [ ! "$RETOUR" = "" ] ; then
echo "From: $MailFrom
To: $(echo "$MailTo" | sed 's/,/\
Cc: /g' )
Content-Type: text/html
Subject: [NTP] ${ERR} out of phase synchronization - $(hostname)
<p>Clock monitoring for serveur <b>$(hostname)</b>
<p>See : <a href=\"http://wikidoc.foo.bar/wiki/NTP\">NTP wiki documention</a>
<li>$(date)
<li>$(date -u)
<li>$TZ
</ul>
<hr noshade>
<pre>
$RETOUR
</pre>
<hr noshade>
Monitoring robot (version $VERSION)
<html>
" | tee /tmp/toto.ntp | /usr/lib/sendmail -ba -f "$MailFrom" -t
fi
if [ "$RETOUR" = "" -a "$ERR" = "" ] ; then
$VERBOSE && echo OK.
$VERBOSE && echo "$RETOUR"
else
[ "$MailTo" = "" ] && echo "$RETOUR
Version : $VERSION"
if echo "$RETOUR$ERR" | grep ERROR ; then
exit 1
fi
fi
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment