1
0
Fork 0

Commit initial: récupération et tri rapide

This commit is contained in:
Chl 2019-07-23 22:28:09 +02:00
commit a52829f96c
104 changed files with 11892 additions and 0 deletions

15
Makefile-dnssec-nsec3 Normal file
View file

@ -0,0 +1,15 @@
# For NSEC3 records, we need 8 random bytes, which means a 16 hexa string
SALT := $(shell dd if=/dev/random bs=13 count=1 2>/dev/null | hexdump -v -e '"%02x"' | cut -c 1-16 )
# There's no easy way to know if bind has been reloaded
# after the .signed file has been generated so it will
# always reload actually.
reload: db.*.signed
service bind9 reload
# Ou nsdc rebuild && nsdc reload pour NSD
db.%.signed: db.%
@echo Signing requires a lot of entropy in /dev/random, do not hesitate to load the machine...
# 5356800 seconds = two months of validity
#dnssec-signzone -e +5356800 $^
dnssec-signzone -e +7776000 -o $* -K ../keys/ -3 $(SALT) $^

13
apt-conf-proxy.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/sh
# À utiliser en mettant la ligne suivante dans le fichier : /etc/apt/apt.conf.d/44proxy
# Acquire::http::Proxy-Auto-Detect "/usr/local/share/scripts-admin/apt-conf-proxy.sh";
PROXY_HOST=apt-proxy.example.net
PROXY_PORT=3142
if nc -zw1 $PROXY_HOST $PROXY_PORT 2>/dev/null >/dev/null; then
echo http://$PROXY_HOST:$PROXY_PORT/
else
echo DIRECT
fi

View file

@ -0,0 +1,12 @@
#!/bin/sh
# Création de la chaîne si elle n'existe pas
iptables-save | grep auth2ban >/dev/null 2>&1 || ( iptables -N auth2ban ; iptables -I INPUT 2 -j auth2ban )
# Vidange
iptables -F auth2ban
# Remplissage
tail -n 3000 /var/log/syslog | grep "did not issue MAIL/EXPN/VRFY/ETRN during connection" | sed -n 's/.*\[\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\)\] .*/\1/p' | sort | uniq -c | grep -v "^[[:space:]]*1 " | awk '{ print $2 }' | while read LINE; do
iptables -A auth2ban -s "$LINE" -j DROP
done

View file

@ -0,0 +1,83 @@
#!/bin/sh
# This pre-receive hook is meant to be put in the main
# repository of the project to detect if a commit follow
# the rule for database updates.
# For example, with the issue #123, second script :
# - adding the file scripts/script_123-2.sql
# - putting the filename in the commit msg for easier retrieval
# When encountering non-UTF8 messages commit, sed may fail.
LANG=C
# Small attemp at making this script portable...
PATH_SCRUTINIZED="scripts"
# We fail on all uncaught errors
# and it helps us transmit error status outside
# of the loops
set -e
while read LINE; do
oldrev="$( echo $LINE | cut -f 1 -d ' ' )"
newrev="$( echo $LINE | cut -f 2 -d ' ' )"
refname="$( echo $LINE | cut -f 3 -d ' ' )"
# We ignore refs/tags and refs/remotes
if ! echo "$refname" | grep "^refs/heads/" >/dev/null; then
continue
fi
# In case oldrev is "000000..."
if [ "$oldrev" = "0000000000000000000000000000000000000000" ]; then
period="$newrev"
else
period="$oldrev..$newrev"
fi
# We loop over each commit to check if they
# put a script file to the scripts/ directory
# without having the following format in the
# first line of the commit message :
# [NNNN-NN-SQL] blabla... bla...
git log --pretty=oneline "$period" -- "$PATH_SCRUTINIZED" | while read COMMIT; do
# Commit metadata extraction
commitsha="$( echo "$COMMIT" | sed 's/^\([^ ]\+\) \(.*\)$/\1/' )"
commitmsg="$( echo "$COMMIT" | sed 's/^\([^ ]\+\) \(.*\)$/\2/' )"
# Listing of files modified by commit
# (git diff-tree will escape tab and other shell-risky characters, but not spaces)
IFS="$( printf "\t\n" )"
for filename in $( git diff-tree --no-commit-id --name-only --root -r "$commitsha" ); do
if echo "$filename" | grep "^$PATH_SCRUTINIZED/script"; then
# Check the filename is well-formed
if ! echo "$filename" | egrep "^$PATH_SCRUTINIZED/script_[0-9]{4,5}-[0-9]{2}.(sql|php)$" >/dev/null; then
echo "check-scripts: nom de fichier non conforme : $filename"
exit 1
fi
# Check the filename matches the commit message
mantis_number="$( echo "$filename" | sed -n 's#.*/script_\([0-9]\+\)-\([0-9]\+\).\(sql\|php\)#\1#p' )"
script_number="$( echo "$filename" | sed -n 's#.*/script_\([0-9]\+\)-\([0-9]\+\).\(sql\|php\)#\2#p' )"
extension="$( echo "$filename" | sed -n 's#.*/script_\([0-9]\+\)-\([0-9]\+\).\(sql\|php\)#\3#p' | tr "a-z" "A-Z" )"
if ! echo $commitmsg | grep "\[$mantis_number-$script_number-$extension\] " >/dev/null; then
echo "check-scripts: message de commit non conforme au script (filename : $filename) ($commitsha) : $commitmsg"
exit 1
fi
fi
done
# Inversely, for every commit message with the correct format, we
# check the matching file exists
if echo "$commitmsg" | egrep "^\[[0-9-]+[A-Z]{3}\]" >/dev/null; then
# Check the filename matches the commit message
mantis_number="$( echo "$commitmsg" | sed -n 's#^\[\([0-9]\{4,5\}\)-\([0-9]\{2\}\)-\(SQL\|PHP\)\] .*#\1#p' )"
script_number="$( echo "$commitmsg" | sed -n 's#^\[\([0-9]\{4,5\}\)-\([0-9]\{2\}\)-\(SQL\|PHP\)\] .*#\2#p' )"
extension="$( echo "$commitmsg" | sed -n 's#^\[\([0-9]\{4,5\}\)-\([0-9]\{2\}\)-\(SQL\|PHP\)\] .*#\3#p' | tr "A-Z" "a-z" )"
if [ -z "$mantis_number" ] || ! git diff-tree --no-commit-id --name-only --root -r "$commitsha" | grep "^$PATH_SCRUTINIZED/script_$mantis_number-$script_number.$extension$" >/dev/null; then
echo "check-scripts: aucun script SQL/PHP correspondant au message de commit ($commitsha) : $commitmsg"
exit 1
fi
fi
done
done

View file

@ -0,0 +1,58 @@
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
if git rev-parse --verify HEAD >/dev/null 2>&1
then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# If you want to allow non-ASCII filenames set this variable to true.
allownonascii=$(git config --bool hooks.allownonascii)
# Redirect output to stderr.
exec 1>&2
# Cross platform projects tend to avoid non-ASCII filenames; prevent
# them from being added to the repository. We exploit the fact that the
# printable range starts at the space character and ends with tilde.
if [ "$allownonascii" != "true" ] &&
# Note that the use of brackets around a tr range is ok here, (it's
# even required, for portability to Solaris 10's /usr/bin/tr), since
# the square bracket bytes happen to fall in the designated range.
test $(git diff --cached --name-only --diff-filter=A -z $against |
LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
then
cat <<\EOF
Error: Attempt to add a non-ASCII file name.
This can cause problems if you want to work with people on other platforms.
To be portable it is advisable to rename the file.
If you know what you are doing you can disable this check using:
git config hooks.allownonascii true
EOF
exit 1
fi
# Stop at any uncatched non-zero status
set -e
# If there are whitespace errors, print the offending file names and fail.
git diff-index --check --cached $against --
# PHP checks
echo "Checking PHP syntax (linting)..."
git diff-index --diff-filter=ACMRT --cached --name-only HEAD -- | egrep '\.php$|\.inc$' | xargs --no-run-if-empty -d "\n" -n 1 php -l
echo "Checking PHP CodeStyle..."
git diff-index --diff-filter=ACMRT --cached --name-only HEAD -- | egrep '\.php$|\.inc$' | xargs --no-run-if-empty -d "\n" phpcs --standard=phpcs.xml --extensions=php --ignore=autoload.php --ignore=bootstrap/cache/

View file

@ -0,0 +1,11 @@
#!/bin/sh
# Stop on the first error
set -ex
export GIT_WORK_TREE=$GIT_DIR/..
# Theoretically, everything has been checked in the pre-receive hook
# and no local modification should go missing (at worst, there's the
# reflog)
git reset --hard

View file

@ -0,0 +1,14 @@
#!/bin/sh
# Stop on the first error
set -ex
export GIT_WORK_TREE=$GIT_DIR/..
# Check for diff between the index and the staging area
git diff-index --quiet --cached HEAD --
# Check for diff between the working tree and the staging area
git diff-files --quiet
# No abandoned files
test -z "$( cd "$GIT_WORK_TREE" && GIT_WORK_TREE="$PWD" GIT_DIR="$GIT_WORK_TREE/.git" git --git-dir=.git ls-files --others )"

View file

@ -0,0 +1,11 @@
fr : Ces hooks permettent de mettre à jour un site web statique
directement via git push.
Il faut autoriser le "in place" dans le dépôt Git distant :
[receive]
denyCurrentBranch = ignore
en : Those hooks allow to update a static website directly with
a git push.
You need to allow 'in place' in the remote git repository :
[receive]
denyCurrentBranch = ignore

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,122 @@
# Sample udhcpd configuration file (/etc/udhcpd.conf)
# The start and end of the IP lease block
start 192.168.10.20 #default: 192.168.0.20
end 192.168.10.254 #default: 192.168.0.254
# The interface that udhcpd will use
interface wlan0 #default: eth0
# The maximim number of leases (includes addressesd reserved
# by OFFER's, DECLINE's, and ARP conficts
#max_leases 254 #default: 254
# If remaining is true (default), udhcpd will store the time
# remaining for each lease in the udhcpd leases file. This is
# for embedded systems that cannot keep time between reboots.
# If you set remaining to no, the absolute time that the lease
# expires at will be stored in the dhcpd.leases file.
#remaining yes #default: yes
# The time period at which udhcpd will write out a dhcpd.leases
# file. If this is 0, udhcpd will never automatically write a
# lease file. (specified in seconds)
#auto_time 7200 #default: 7200 (2 hours)
# The amount of time that an IP will be reserved (leased) for if a
# DHCP decline message is received (seconds).
#decline_time 3600 #default: 3600 (1 hour)
# The amount of time that an IP will be reserved (leased) for if an
# ARP conflct occurs. (seconds
#conflict_time 3600 #default: 3600 (1 hour)
# How long an offered address is reserved (leased) in seconds
#offer_time 60 #default: 60 (1 minute)
# If a lease to be given is below this value, the full lease time is
# instead used (seconds).
#min_lease 60 #defult: 60
# The location of the leases file
#lease_file /var/lib/misc/udhcpd.leases #defualt: /var/lib/misc/udhcpd.leases
# The location of the pid file
#pidfile /var/run/udhcpd.pid #default: /var/run/udhcpd.pid
# Everytime udhcpd writes a leases file, the below script will be called.
# Useful for writing the lease file to flash every few hours.
#notify_file #default: (no script)
#notify_file dumpleases # <--- useful for debugging
# The following are bootp specific options, setable by udhcpd.
#siaddr 192.168.0.22 #default: 0.0.0.0
#sname zorak #default: (none)
#boot_file /var/nfs_root #default: (none)
# The remainer of options are DHCP options and can be specifed with the
# keyword 'opt' or 'option'. If an option can take multiple items, such
# as the dns option, they can be listed on the same line, or multiple
# lines. The only option with a default is 'lease'.
#Examles
#opt dns 8.8.8.8 4.4.4.4
# FDN open DNS resolvers
opt dns 80.67.169.12 80.67.169.40
option subnet 255.255.255.0
opt router 192.168.10.2
option lease 864000 # 10 days of seconds
# Currently supported options, for more info, see options.c
#opt subnet
#opt timezone
#opt router
#opt timesrv
#opt namesrv
#opt dns
#opt logsrv
#opt cookiesrv
#opt lprsrv
#opt bootsize
#opt domain
#opt swapsrv
#opt rootpath
#opt ipttl
#opt mtu
#opt broadcast
#opt wins
#opt lease
#opt ntpsrv
#opt tftp
#opt bootfile
#opt wpad
# Static leases map
#static_lease 00:60:08:11:CE:4E 192.168.0.54
#static_lease 00:60:08:11:CE:3E 192.168.0.44

28
mini-ap-wifi/launch-wifi-ap.sh Executable file
View file

@ -0,0 +1,28 @@
#!/bin/sh
ifconfig wlan0 192.168.10.2 netmask 255.255.255.0
echo 1 >/proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -j MASQUERADE
udhcpd -f -S launch-wifi-ap-udhcpd.conf &
DHCPD_PID="$!"
hostapd ~/launch-wifi-ap-hostapd.conf &
HOSTAPD_PID="$!"
sleep 5
echo
echo
echo "Appuyer sur Entree pour quitter..."
read LINE
kill -2 "$DHCPD_PID"
kill -2 "$HOSTAPD_PID"
# Nettoyage
iptables -t nat -D POSTROUTING -s 192.168.10.0/24 -j MASQUERADE
echo 0 >/proc/sys/net/ipv4/ip_forward
ifconfig wlan0 down

263
nagios/check_apache_access_log.pl Executable file
View file

@ -0,0 +1,263 @@
#!/usr/bin/perl
# This perl script is an adaptation of logtail2 to parse
# Apache access logs.
# I know it's generally preferrable to require and call
# the original instead of forking and risking of not maintaining
# it but logtail is fairly simple and, since logtail2 is not always
# installed and somtimes not even packaged in distributions,
# it simplify deployment.
# TODO: call logtail2 if available ?
# Copyright (C) 2003 Jonathan Middleton <jjm@ixtab.org.uk
# Copyright (C) 2001 Paul Slootman <paul@debian.org>
# This file is part of Logcheck.
# Logcheck is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# Logcheck is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with Logcheck; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
use strict;
use warnings;
use Getopt::Long;
use File::Basename;
use Digest::MD5 qw(md5_hex);
my ($size, $logfile, $offsetfile, @listingLogfiles, @listingOffsetfiles, $key, $firstTimeReading);
# (problems with including utils.pm)
my %ERRORS=('OK'=>0,'WARNING'=>1,'CRITICAL'=>2,'UNKNOWN'=>3,'DEPENDENT'=>4);
my $TMP_DIR = '/tmp';
my %opts = ();
my %outputCpt = (
'2XX' => 0,
'3XX' => 0,
'403' => 0,
'404' => 0,
'4XX' => 0,
'500' => 0,
'5XX' => 0,
'others' => 0, # other (1XX) and strange things
);
my ($outputTotalCpt) = 0;
my ($outputTotalBandwith) = 0;
# process args and switches
my ($TEST_MODE) = 0;
# When we discover a file for the first time,
# we don't do the count and just mark the position
# for the next time
my ($COUNT_FIRST_READ_CATCHING_UP) = 0;
GetOptions(
'file=s' => \@listingLogfiles,
'even-catch-up' => \$COUNT_FIRST_READ_CATCHING_UP,
'test-mode' => \$TEST_MODE,
);
sub print_from_offset {
my ($filename, $offset) = @_;
# this subroutine prints the contents of the file named $filename,
# starting offset $offset.
#print "print_from_offset $filename, $offset\n";
unless (open(LOGFILE, $filename)) {
print "File $logfile cannot be read: $!\n";
exit $ERRORS{UNKNOWN};
}
seek(LOGFILE, $offset, 0);
while (<LOGFILE>) {
if ($_ =~ /^([[:xdigit:].:]+) (.+) (.+) (\[[[:alnum:]\/:]+ \+[[:digit:]]{4}\]) (".*") ([[:digit:]]{3}) ([[:digit:]]+) "(.*)" "(.*)"$/) {
#We ignore some IP address
next if ($1 eq '::1' or $1 eq '127.0.0.1' or $1 eq '2a01:e35:2ef3:b360::abac:22' or $1 eq '192.168.0.34');
if ($6 >= 200 && $6 < 300) {
++$outputCpt{'2XX'};
} elsif ($6 >= 300 && $6 < 400) {
++$outputCpt{'3XX'};
} elsif ($6 == 403) {
++$outputCpt{'403'};
} elsif ($6 == 404) {
++$outputCpt{'404'};
} elsif ($6 >= 400 && $6 < 500) {
++$outputCpt{'4XX'};
} elsif ($6 == 500) {
++$outputCpt{'500'};
} elsif ($6 >= 500 && $6 < 600) {
++$outputCpt{'5XX'};
} else {
++$outputCpt{'others'};
}
$outputTotalBandwith += $7;
} else {
++$outputCpt{'others'};
}
}
$size = tell LOGFILE;
close LOGFILE;
return $size;
}
sub mtime {
my ($filename) = @_;
my $mtime = 0;
unless (-e $filename && ($mtime = ((stat($filename))[8])) ) {
print STDERR "Cannot get $filename mtime: $!\n";
exit 65;
}
return $mtime;
}
sub inode {
my ($filename) = @_;
my $inode = 0;
unless (-e $filename && ($inode = ((stat($filename))[1])) ) {
print STDERR "Cannot get $filename inode: $!\n";
exit 65;
}
return $inode;
}
sub get_directory_contents {
my ($filename) = @_;
my $dirname = dirname($filename);
unless (opendir(DIR, $dirname)) {
print STDERR "Cannot open directory $dirname: $!\n";
exit 65;
}
my @direntries = readdir(DIR);
closedir DIR;
return @direntries;
}
sub determine_rotated_logfile {
my ($filename,$inode) = @_;
my $rotated_filename;
# this subroutine tries to guess to where a given log file was
# rotated. Its magic is mainly taken from logcheck's logoutput()
# function with dateext magic added.
#print "determine_rotated_logfile $filename $inode\n";
for my $codefile (glob("/usr/share/logtail/detectrotate/*.dtr")) {
my $func = do $codefile;
if (!$func) {
print STDERR "cannot compile $codefile: $!";
exit 68;
}
$rotated_filename = $func->($filename);
last if $rotated_filename;
}
#if ($rotated_filename) {
# print "rotated_filename $rotated_filename (". inode($rotated_filename). ")\n";
#} else {
# print "no rotated file found\n";
#}
if ($rotated_filename && -e "$rotated_filename" && inode($rotated_filename) == $inode) {
return $rotated_filename;
} else {
return "";
}
}
foreach $logfile (@listingLogfiles) {
my ($inode, $ino, $offset) = (0, 0, 0);
if (! -f $logfile) {
print "File $logfile cannot be read: $!\n";
exit $ERRORS{UNKNOWN};
}
# We generate a unique offset filename
$offsetfile = $TMP_DIR . '/nagios-logtail.' . md5_hex($logfile) . '.offset';
$firstTimeReading = 0;
if (! -f $offsetfile) {
open(OFFSET, ">", $offsetfile);
chmod 0600, $offsetfile;
if (($ino,$size) = (stat($logfile))[1,7]) {
# Unless we care about the historic before the
# first call of this script, we just skip it.
if ($COUNT_FIRST_READ_CATCHING_UP) {
$size = 0;
}
print OFFSET "$ino\n$size\n";
}
close OFFSET;
}
if ($offsetfile) {
# If offset file exists, open and parse it.
if (open(OFFSET, $offsetfile)) {
$_ = <OFFSET>;
if (defined $_) {
chomp $_;
$inode = $_;
$_ = <OFFSET>;
if (defined $_) {
chomp $_;
$offset = $_;
}
}
}
# determine log file inode and size
unless (($ino,$size) = (stat($logfile))[1,7]) {
print "Cannot get $logfile file size: $!\n";
exit $ERRORS{UNKNOWN};
}
if ($inode == $ino) {
# inode is still the same
next if $offset == $size; # short cut
if ($offset > $size) {
$offset = 0;
print "(warning: possible tampering on $logfile) "
}
}
if ($inode != $ino) {
# this is the interesting case: inode has changed.
# So the file might have been rotated. We need to print the
# entire file.
# Additionally, we might want to see whether we can find the
# previous instance of the file and to process it from here.
#print "inode $inode, ino $ino\n";
my $rotatedfile = determine_rotated_logfile($logfile,$inode);
if ( $rotatedfile && ! $firstTimeReading) {
print_from_offset($rotatedfile,$offset);
}
# print the actual file from beginning
$offset = 0;
}
}
$size = print_from_offset($logfile,$offset);
# update offset, unless test mode
unless ($TEST_MODE) {
unless (open(OFFSET, ">", $offsetfile)) {
print STDERR "File $offsetfile cannot be created. Check your permissions: $!\n";
exit 73;
}
print OFFSET "$ino\n$size\n";
close OFFSET;
}
}
# printing results
print "OK|";
foreach $key (sort keys %outputCpt) {
print "'" . $key . "'=" . $outputCpt{$key} . ";;;; "
}
print "'total bandwidth'=" . $outputTotalBandwith . "B;;;;\n";
exit $ERRORS{'OK'};

View file

@ -0,0 +1 @@
DATATYPE = GAUGE,GAUGE,GAUGE,GAUGE,GAUGE,GAUGE,GAUGE,GAUGE,GAUGE,GAUGE,GAUGE,GAUGE,GAUGE,COUNTER,COUNTER,GAUGE

View file

@ -0,0 +1,155 @@
<?php
#
# The MIT License (MIT)
#
# Copyright (c) 2016 Steffen Schoch - dsb it services GmbH & Co. KG
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
#
# Feel free to contact me via email: schoch@dsb-its.net
#
# 2016-01-## schoch - 1.0 - Init...
// Process-Informationen
$opt[1] = " --vertical-label \"Anzahl\" --title \"Apache Server-Status for $hostname\" --lower-limit 0 ";
$ds_name[1] = 'Server-Status';
// Scoreboard to var1 - var11
$def[1] = "DEF:var1=$RRDFILE[16]:$DS[16]:AVERAGE " ;
$def[1] .= "DEF:var2=$RRDFILE[13]:$DS[13]:AVERAGE " ;
$def[1] .= "DEF:var3=$RRDFILE[10]:$DS[10]:AVERAGE " ;
$def[1] .= "DEF:var4=$RRDFILE[12]:$DS[12]:AVERAGE " ;
$def[1] .= "DEF:var5=$RRDFILE[8]:$DS[8]:AVERAGE " ;
$def[1] .= "DEF:var6=$RRDFILE[4]:$DS[4]:AVERAGE " ;
$def[1] .= "DEF:var7=$RRDFILE[3]:$DS[3]:AVERAGE " ;
$def[1] .= "DEF:var8=$RRDFILE[9]:$DS[9]:AVERAGE " ;
$def[1] .= "DEF:var9=$RRDFILE[6]:$DS[6]:AVERAGE " ;
$def[1] .= "DEF:var10=$RRDFILE[7]:$DS[7]:AVERAGE " ;
$def[1] .= "DEF:var11=$RRDFILE[5]:$DS[5]:AVERAGE " ;
// WAIT
$def[1] .= "AREA:var1#ff0000:\"Waiting for connection \":STACK ";
$def[1] .= "GPRINT:var1:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var1:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var1:MAX:\"%4.0lf max\\n\" " ;
// START
$def[1] .= "AREA:var2#FF8000:\"Starting up \":STACK ";
$def[1] .= "GPRINT:var2:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var2:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var2:MAX:\"%4.0lf max\\n\" " ;
// READ
$def[1] .= "AREA:var3#ffff00:\"Reading Request \":STACK ";
$def[1] .= "GPRINT:var3:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var3:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var3:MAX:\"%4.0lf max\\n\" " ;
// SEND
$def[1] .= "AREA:var4#00FF80:\"Sending Reply \":STACK ";
$def[1] .= "GPRINT:var4:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var4:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var4:MAX:\"%4.0lf max\\n\" " ;
// KEEPALIVE
$def[1] .= "AREA:var5#00FFFF:\"Keepalive (read) \":STACK ";
$def[1] .= "GPRINT:var5:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var5:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var5:MAX:\"%4.0lf max\\n\" " ;
// DNS
$def[1] .= "AREA:var6#0080FF:\"DNS Lookup \":STACK ";
$def[1] .= "GPRINT:var6:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var6:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var6:MAX:\"%4.0lf max\\n\" " ;
// CLOSE
$def[1] .= "AREA:var7#0000FF:\"Closing connection \":STACK ";
$def[1] .= "GPRINT:var7:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var7:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var7:MAX:\"%4.0lf max\\n\" " ;
// LOGGING
$def[1] .= "AREA:var8#8000FF:\"Logging \":STACK ";
$def[1] .= "GPRINT:var8:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var8:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var8:MAX:\"%4.0lf max\\n\" " ;
// GRACEFUL
$def[1] .= "AREA:var9#FF00FF:\"Gracefully finishing \":STACK ";
$def[1] .= "GPRINT:var9:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var9:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var9:MAX:\"%4.0lf max\\n\" " ;
// IDLE
$def[1] .= "AREA:var10#FF80FF:\"Idle cleanup of worker \":STACK ";
$def[1] .= "GPRINT:var10:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var10:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var10:MAX:\"%4.0lf max\\n\" " ;
// FREE
$def[1] .= "AREA:var11#D0D0D0:\"Open slot with no current process \":STACK ";
$def[1] .= "GPRINT:var11:LAST:\"%4.0lf last\" " ;
$def[1] .= "GPRINT:var11:AVERAGE:\"%4.0lf avg\" " ;
$def[1] .= "GPRINT:var11:MAX:\"%4.0lf max\\n\" " ;
// Draw last line
if($this->MACRO['TIMET'] != ""){
$def[1] .= "VRULE:".$this->MACRO['TIMET']."#000000:\"Last Service Check \\n\" ";
}
// Request per Second
$opt[2] = " --vertical-label \"Anzahl\" --title \"Apache Requests per Second for $hostname\" --lower-limit 0 ";
$ds_name[2] = 'Server-Status';
$def[2] = "DEF:var12=$RRDFILE[11]:$DS[11]:AVERAGE " ;
$def[2] .= "LINE1:var12#ffae2d:\"Requests per Second \" ";
$def[2] .= "GPRINT:var12:LAST:\"%4.0lf last\" " ;
$def[2] .= "GPRINT:var12:AVERAGE:\"%4.0lf avg\" " ;
$def[2] .= "GPRINT:var12:MAX:\"%4.0lf max\\n\" " ;
// Bytes per Second and Bytes per Request
$opt[3] = " --vertical-label \"Anzahl\" --title \"Apache Bytes per ... for $hostname\" --lower-limit 0 ";
$ds_name[3] = 'Server-Status';
$def[3] = "DEF:var13=$RRDFILE[2]:$DS[2]:AVERAGE " ;
$def[3] .= "DEF:var14=$RRDFILE[1]:$DS[1]:AVERAGE " ;
$def[3] .= "LINE1:var13#db60f7:\"Bytes per Second \" ";
$def[3] .= "GPRINT:var13:LAST:\"%4.0lf last\" " ;
$def[3] .= "GPRINT:var13:AVERAGE:\"%4.0lf avg\" " ;
$def[3] .= "GPRINT:var13:MAX:\"%4.0lf max\\n\" " ;
$def[3] .= "LINE1:var14#5fe27b:\"Bytes per Request \" ";
$def[3] .= "GPRINT:var14:LAST:\"%4.0lf last\" " ;
$def[3] .= "GPRINT:var14:AVERAGE:\"%4.0lf avg\" " ;
$def[3] .= "GPRINT:var14:MAX:\"%4.0lf max\\n\" " ;
$opt[4] = " --vertical-label \"Anzahl\" --title \"Apache Total access and kBytes for $hostname\" --lower-limit 0 ";
$ds_name[4] = 'Server-Status';
$def[4] = "DEF:var15=$RRDFILE[14]:$DS[14]:AVERAGE "
. "DEF:var16=$RRDFILE[15]:$DS[15]:AVERAGE "
. rrd::area('var16', '#004400')
. rrd::line1('var16', '#003300', 'Total_kBytes')
. rrd::gprint('var16', array('LAST', 'AVERAGE', 'MAX'), "%7.2lf %SkB/s")
. rrd::line1('var15', '#999999', 'Total_Accesses')
. rrd::gprint('var15', array('LAST', 'AVERAGE', 'MAX'), "%7.2lf %SHits/s");

View file

@ -0,0 +1,328 @@
#!/usr/bin/perl -w
#
# The MIT License (MIT)
#
# Copyright (c) 2016 Steffen Schoch - dsb it services GmbH & Co. KG
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
#
# Feel free to contact me via email: schoch@dsb-its.net
#
# 2018-02-06, sni: 1.2 - fixed some bugs and typos, add some output - Thank you!
# 2016-08-03, schoch: 1.1 - changes for new apache version 2.4.x
# 2016-01-##, schoch: 1.0 - Init...
use strict;
use warnings;
use Data::Dumper;
use Getopt::Long qw(:config bundling); # insert debug for much more infos ;)
# define constants
use constant {
VERSION => '1.0',
# a simple inf variant - works for me ;-)
MAXINT => ~0,
NEGMAXINT => -1 * ~0,
STAT_OK => 0,
STAT_WARNING => 1,
STAT_CRITICAL => 2,
STAT_UNKNOWN => 3
};
# check arguments
my $options = { # define defaults here
'hostname' => 'localhost',
'verbose' => 0,
'wget' => '/usr/bin/wget',
'wget-options' => '-q --no-check-certificate -O-',
};
my $goodOpt = GetOptions(
'v+' => \$options->{'verbose'},
'verbose+' => \$options->{'verbose'},
'V' => \$options->{'version'},
'h' => \$options->{'help'},
'version' => \$options->{'version'},
'help' => \$options->{'help'},
'H=s' => \$options->{'hostname'},
'hostname=s' => \$options->{'hostname'},
'wc=s' => \@{$options->{'warncrit'}},
'warncrit=s' => \@{$options->{'warncrit'}},
'wget' => \$options->{'wget'},
'woptions=s' => \$options->{'wget-options'},
'u=s' => \$options->{'url'},
'url=s' => \$options->{'url'},
);
helpShort() unless $goodOpt;
helpLong() if $options->{'help'};
if($options->{'version'}) {
print 'Version: ', VERSION, "\n";
exit STAT_UNKNOWN;
}
print Data::Dumper->Dump([$options], ['options'])
if $options->{'verbose'};
# warncrit - get start and end for each pair
my $warncrit = {};
foreach my $item (@{$options->{'warncrit'}}) {
if($item =~ m/^([^,]+),([^,]+),([^,]+)$/o) {
$warncrit->{$1} = {'w' => $2, 'c' => $3};
} else {
mydie('Don\'t understand ' . $item);
}
}
print Data::Dumper->Dump([$warncrit], ['warncrit'])
if $options->{'verbose'};
# read stdin complete
local $/;
# which url to use? --url can overwrite the auto-creation
my $url = $options->{'url'}
? $options->{'url'}
: 'http://' . $options->{'hostname'} . '/server-status?auto';
printf "Url: %s\n", $url
if $options->{'verbose'};
# open server info
open PH, sprintf('%s %s %s |',
$options->{'wget'},
$options->{'wget-options'},
$url
) or mydie('Can not open server-status: ' . $!);
# read and cut data
my %lineData = map { (split /:\s*/)[0..1] } split /\n/, <PH>;
close PH;
print Data::Dumper->Dump([\%lineData], ['server-status'])
if $options->{'verbose'};
# Search for "Scoreboard" and analyze...
my $data = {};
if(exists $lineData{'Scoreboard'}) {
$data->{$1}++ while $lineData{'Scoreboard'} =~ m/(.)/og;
} else {
# Not found...
print 'No useful data found';
exit STAT_UNKNOWN;
}
print Data::Dumper->Dump([$data], ['scoreboard'])
if $options->{'verbose'};
# Sum up Scoreboard entries
my $sum = 0;
foreach(keys %$data) {
$sum += $data->{$_};
}
# print result
my $result = $lineData{'ServerMPM'} ? sprintf('MPM=%s', $lineData{'ServerMPM'}) : '';
my $perfData = '';
my @statList = qw(_ S R W K D C L G I .);
my $stats = {
'_' => 'Wait',
'S' => 'Start',
'R' => 'Read',
'W' => 'Send',
'K' => 'Keepalive',
'D' => 'DNS',
'C' => 'Close',
'L' => 'Logging',
'G' => 'Graceful',
'I' => 'Idle',
'.' => 'Free'
};
foreach my $item (@statList) {
$result .= ', ' if $result;
$perfData .= ' ' if $perfData;
$result .= sprintf '%s=%d', $stats->{$item}, ($data->{$item} or 0);
$perfData .= sprintf '%s=%d', $stats->{$item}, ($data->{$item} or 0);
}
$result .= ' ===> Total=' . $sum;
# add server rates - if exisiting (apache => 2.4)
if(
exists $lineData{'ReqPerSec'}
and exists $lineData{'BytesPerSec'}
and exists $lineData{'BytesPerReq'}
) {
$result .= sprintf ' RATES %s=%s, %s=%s, %s=%s',
(map { $_, $lineData{$_} } qw(ReqPerSec BytesPerSec BytesPerReq));
$perfData .= sprintf ' %s=%s %s=%s %s=%s',
(map { $_, $lineData{$_} } qw(ReqPerSec BytesPerSec BytesPerReq));
$perfData .= sprintf ' Total_Accesses=%sc', $lineData{"Total Accesses"};
$perfData .= sprintf ' Total_kBytes=%s', $lineData{"Total kBytes"};
}
# check for warning and critical
my $status = STAT_OK;
foreach my $field (keys %$warncrit) {
printf "checking warn/crit for \"%s\"...\n", $field
if $options->{'verbose'};
# value = if one letter scoreboard, else one of lineData (0 if not found)
my $fieldValue =
$field =~ m/^.$/o ? $data->{$field} || 0 : $lineData{$field} || 0;
printf " value: \"%s\"\n", $fieldValue
if $options->{'verbose'};
my $fieldStatus = checkStatus($fieldValue, $warncrit->{$field});
printf " result: %d\n", $fieldStatus
if $options->{'verbose'};
# last if CRITICAL, save WARNING, ignore OK
if($fieldStatus == STAT_CRITICAL) {
$status = STAT_CRITICAL;
last;
} elsif($fieldStatus == STAT_WARNING) {
$status = STAT_WARNING;
}
}
printf "Check overall status: %d\n", $status
if $options->{'verbose'};
# print result
printf "APACHE SERVER STATUS %s - %s|%s\n",
$status == 0 ? 'OK' : $status == 1
? 'WARNING' : $status == 2
? 'CRITICAL' : 'UNKNOWN',
$result, $perfData;
exit $status;
########### Functions #########################################################
# short help
sub helpShort {
print 'check_apache_serverstatus.pl -H <ip address> [-h] [-v]', "\n",
'[--wc=<field,warning,critical>] [--wget=<path to wget>] ', "\n",
'[--woption=<aditional wget options>] [-u <alternative url>]', "\n";
exit STAT_UNKNOWN;
}
# long help
sub helpLong {
print 'check_apache_serverstatus.pl (', VERSION, ")\n",
'Steffen Schoch <schoch@dsb-its.net>', "\n", "\n",
<<END;
check_apache_serverstatus.pl -H <ip address> [-h] [-v]
[--wc=<field,warning,critical>] [--wget=<path to wget>]
[--woption=<aditional wget options>] [-u <alternative url>]
Check apache server-status and builds performance data. Uses
wget to connect to the apache webserver.
Options:
-h, --help
Print help
-V, --version
Print version
-H, --hostname
Host name or IP address - will be used as
http://<hostname>/server-status. You can overwrite this
url by using -u/--url.
-v, --verbose
Be much more verbose.
--wget
Path to wget. Could also be used to use lynx or something
else instead of wget. Output must be send to stdout.
--woptions
Arguments passed to wget.
-u, --url
Use this url to connect to the apache server-status. Usefull
if the auto generated url out of the hostname is not correct.
--wc=<field,warning,critical>, --warncrit=<field,warning,critical>
Field could be any of the letters of the apache scoreboard or
of the other keys returned by server-status. Can be set multiple
times if you want to check more than one field.
END
exit STAT_UNKNOWN;
}
# die with STAT_UNKNOWN
sub mydie {
print @_, "\n";
exit STAT_UNKNOWN;
}
# checks if value is in defined limits for warning and critical
# see https://nagios-plugins.org/doc/guidelines.html for more details
# ARG1: value
# ARG2: hash with c and w limit
# RET: Nagios-State for this value
sub checkStatus {
my $value = shift;
my $limits = shift;
# first check critical - if not crit, then check warning. If not must be ok
for my $type (qw(c w)) {
printf " checking type %s = %s\n",
$type eq 'c' ? 'critcal' : 'warning',
$limits->{$type}
if $options->{'verbose'};
# Get min/max values, range is inside or outside?
my $inOrOut = 'out';
my $min;
my $max;
if($limits->{$type} =~ m/^(\@?)((~|\d*(\.\d+)?)?:)?(~|\d*(\.\d+)?)?$/o) {
# save min, max and inOrOut
$inOrOut = 'in' if $1;
$min = $3 || 0;
$max = $5 =~ m/^(.+)$/o ? $1 : MAXINT; # $max could be 0...
# neg infinity if ~
($min, $max) = map { $_ eq '~' ? NEGMAXINT : $_ } ($min, $max);
} else {
# Don't understand...
myexit('--> Strange range found: ', $limits->{$type});
}
printf " inside or outside: %s min: %s max: %s\n",
$inOrOut, $min, $max
if $options->{'verbose'};
# check for value outside range. Break if match, else check for inside.
if($inOrOut eq 'out') {
if(!($min < $value && $value < $max)) {
return $type eq 'c' ? STAT_CRITICAL : STAT_WARNING;
}
} elsif($inOrOut eq 'in') {
if($min <= $value && $value <= $max) {
return $type eq 'c' ? STAT_CRITICAL : STAT_WARNING;
}
}
}
# must be OK...
return STAT_OK;
}

View file

@ -0,0 +1,89 @@
#!/bin/sh
WARNING_RANGE="9:9"
CRITICAL_RANGE="8:10"
# Note needed in this version of the script
#set -e
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
. $PROGPATH/utils.sh
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 -h
$0 [-w warning_range] [-c critical_range]
Valeurs par défaut:
warning_range: $WARNING_RANGE
critical_range: $CRITICAL_RANGE
EOF
}
#
# Gestion des paramètres
#
while getopts hw:c:W:C: f; do
case "$f" in
'h')
usage
exit
;;
'w')
WARNING_RANGE="$OPTARG"
;;
'c')
CRITICAL_RANGE="$OPTARG"
;;
\?)
usage
exit 1
;;
esac
done
#
# Lancement de la commande
#
# Note : on lance les traitements "sûrs" (décompte) ensuite pour
# bien capturer un éventuel échec de la commande
# principale.
# En outre, grep retourne un code d'erreur si aucune
# occurrence n'est trouvée.
RESULT="$( asterisk -rx "dahdi show channels" 2>&1 )"
# Si la commande ne s'est pas correctement executée,
# on renvoie unknown
if [ "$?" -ne 0 ]; then
echo "UNKNOWN : error at command launch : $RESULT"
exit $STATE_UNKNOWN
fi
# Décompte
RESULT="$( printf "%s" "$RESULT" | tail -n +2 | sed 's/^.\{77\}[[:space:]]*//' | grep -c "In Service" )"
# Ventilation selon valeur
RETURN_STATUS=$STATE_OK
RETURN_OUTPUT="OK"
if check_range "$RESULT" "$CRITICAL_RANGE"; then
RETURN_STATUS=$STATE_CRITICAL
RETURN_OUTPUT="CRITICAL"
elif check_range "$RESULT" "$WARNING_RANGE"; then
RETURN_STATUS=$STATE_WARNING
RETURN_OUTPUT="WARNING"
fi
# Affichage final
printf "%s | val=%d;%s;%s\n" "$RETURN_OUTPUT" "$RESULT" "$WARNING_RANGE" "$CRITICAL_RANGE"
exit $RETURN_STATUS

View file

@ -0,0 +1,89 @@
#!/bin/sh
WARNING_RANGE="4:4"
CRITICAL_RANGE="3:5"
# Note needed in this version of the script
#set -e
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
. $PROGPATH/utils.sh
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 -h
$0 [-w warning_range] [-c critical_range]
Valeurs par défaut:
warning_range: $WARNING_RANGE
critical_range: $CRITICAL_RANGE
EOF
}
#
# Gestion des paramètres
#
while getopts hw:c:W:C: f; do
case "$f" in
'h')
usage
exit
;;
'w')
WARNING_RANGE="$OPTARG"
;;
'c')
CRITICAL_RANGE="$OPTARG"
;;
\?)
usage
exit 1
;;
esac
done
#
# Lancement de la commande
#
# Note : on lance les traitements "sûrs" (décompte) ensuite pour
# bien capturer un éventuel échec de la commande
# principale.
# En outre, grep retourne un code d'erreur si aucune
# occurrence n'est trouvée.
RESULT="$( asterisk -rx "dahdi show status" 2>&1 )"
# Si la commande ne s'est pas correctement executée,
# on renvoie unknown
if [ "$?" -ne 0 ]; then
echo "UNKNOWN : error at command launch : $RESULT"
exit $STATE_UNKNOWN
fi
# Décompte
RESULT="$( printf "%s" "$RESULT" | tail -n +2 | sed 's/^.\{41\}[[:space:]]*\([^[:space:]]\+\)[[:space:]]\+.*/\1/' | grep -c "OK" )"
# Ventilation selon valeur
RETURN_STATUS=$STATE_OK
RETURN_OUTPUT="OK"
if check_range "$RESULT" "$CRITICAL_RANGE"; then
RETURN_STATUS=$STATE_CRITICAL
RETURN_OUTPUT="CRITICAL"
elif check_range "$RESULT" "$WARNING_RANGE"; then
RETURN_STATUS=$STATE_WARNING
RETURN_OUTPUT="WARNING"
fi
# Affichage final
printf "%s | val=%d;%s;%s\n" "$RETURN_OUTPUT" "$RESULT" "$WARNING_RANGE" "$CRITICAL_RANGE"
exit $RETURN_STATUS

View file

@ -0,0 +1,184 @@
#!/bin/sh
# Note needed in this version of the script
#set -e
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
. $PROGPATH/utils.sh
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 -h
$0 [-w warning_monitored_offline_range] [-c critical_monitored_offline_range] [-W warning_total_peers] [-C critical_total_peers]
EOF
}
#
# Gestion des paramètres
#
while getopts hw:c:b:B: f; do
case "$f" in
'h')
usage
exit
;;
#'a')
# WARNING_MONITORED_ONLINE_RANGE="$OPTARG"
# ;;
#'A')
# CRITICAL_MONITORED_ONLINE_RANGE="$OPTARG"
# ;;
'b')
WARNING_MONITORED_OFFLINE_RANGE="$OPTARG"
;;
'B')
CRITICAL_MONITORED_OFFLINE_RANGE="$OPTARG"
;;
'w')
WARNING_TOTAL_RANGE="$OPTARG"
;;
'c')
CRITICAL_TOTAL_RANGE="$OPTARG"
;;
\?)
usage
exit 1
;;
esac
done
#
# Lancement de la commande
#
# Note : on lance les traitements "sûrs" (décompte) ensuite pour
# bien capturer un éventuel échec de la commande
# principale.
# En outre, grep retourne un code d'erreur si aucune
# occurrence n'est trouvée.
RAW_OUTPUT="$( asterisk -rx "sip show peers" 2>&1 )"
# Si la commande ne s'est pas correctement executée,
# on renvoie unknown
if [ "$?" -ne 0 ]; then
echo "UNKNOWN : error at command launch : $RAW_OUTPUT"
exit $STATE_UNKNOWN
fi
# Décompte
RESULT="$( printf "%s" "$RAW_OUTPUT" | tail -n 1 | sed 's/^\([0-9]\+\) sip peers \[Monitored: \([0-9]\+\) online, \([0-9]\+\) offline Unmonitored: \([0-9]\+\) online, \([0-9]\+\) offline\]/\1\t\2\t\3\t\4\t\5/g' )"
VALUE_TOTAL_PEERS="$( printf "%s" "$RESULT" | cut -f 1 )"
VALUE_MONITORED_ONLINE_PEERS="$( printf "%s" "$RESULT" | cut -f 2 )"
VALUE_MONITORED_OFFLINE_PEERS="$( printf "%s" "$RESULT" | cut -f 3 )"
VALUE_UNMONITORED_ONLINE_PEERS="$( printf "%s" "$RESULT" | cut -f 4 )"
VALUE_UNMONITORED_OFFLINE_PEERS="$( printf "%s" "$RESULT" | cut -f 5 )"
# On extrait les lignes qui n'ont pas OK comme statut. Explication des sed :
# - on supprime la première et la dernière ligne
# - on supprime celles qui ont 'OK' en 95 position
# - on ne garde que le premier champ
# - on regroupe sur une ligne en séparant par des virgules
PROBLEMATIC_LINES="$( printf "%s" "$RAW_OUTPUT" | sed '1d;$d' | sed '/.\{94\}OK/d' | sed 's/[[:space:]].*//' | sed -e :a -e 'N; s/\n/, /; ta' )"
# Ventilation selon valeur
RETURN_STATUS=$STATE_OK
RETURN_OUTPUT="OK"
# Warning checks
# - total
if [ -n "$WARNING_TOTAL_RANGE" ]; then
check_range "$VALUE_TOTAL_PEERS" "$WARNING_TOTAL_RANGE"
RET="$?"
if [ "$RET" -eq "2" ]; then
echo "ERROR with WARNING_TOTAL_RANGE"
exit $STATE_UNKNOWN
elif [ "$RET" -eq "0" ]; then
TMP="$VALUE_TOTAL_PEERS total peers"
if [ "$RETURN_STATUS" -ne "$STATE_WARNING" ]; then
RETURN_OUTPUT="$TMP"
else
RETURN_OUTPUT="$RETURN_OUTPUT, $TMP"
fi
RETURN_STATUS=$STATE_WARNING
fi
fi
# - monitored offline
if [ -n "$WARNING_MONITORED_OFFLINE_RANGE" ]; then
check_range "$VALUE_MONITORED_OFFLINE_PEERS" "$WARNING_MONITORED_OFFLINE_RANGE"
RET="$?"
if [ "$RET" -eq "2" ]; then
echo "ERROR with WARNING_MONITORED_OFFLINE_RANGE"
exit $STATE_UNKNOWN
elif [ "$RET" -eq "0" ]; then
TMP="$VALUE_MONITORED_OFFLINE_PEERS monitored offline peers"
if [ "$RETURN_STATUS" -ne "$STATE_WARNING" ]; then
RETURN_OUTPUT="$TMP"
else
RETURN_OUTPUT="$RETURN_OUTPUT, $TMP"
fi
RETURN_STATUS=$STATE_WARNING
fi
fi
# Critical checks (copy-paste from warning + regexp s/CRITICAL/CRITICAL/g)
# - total
if [ -n "$CRITICAL_TOTAL_RANGE" ]; then
check_range "$VALUE_TOTAL_PEERS" "$CRITICAL_TOTAL_RANGE"
RET="$?"
if [ "$RET" -eq "2" ]; then
echo "ERROR with CRITICAL_TOTAL_RANGE"
exit $STATE_UNKNOWN
elif [ "$RET" -eq "0" ]; then
TMP="$VALUE_TOTAL_PEERS total peers"
if [ "$RETURN_STATUS" -ne "$STATE_CRITICAL" ]; then
RETURN_OUTPUT="$TMP"
else
RETURN_OUTPUT="$RETURN_OUTPUT, $TMP"
fi
RETURN_STATUS=$STATE_CRITICAL
fi
fi
# - monitored offline
if [ -n "$CRITICAL_MONITORED_OFFLINE_RANGE" ]; then
check_range "$VALUE_MONITORED_OFFLINE_PEERS" "$CRITICAL_MONITORED_OFFLINE_RANGE"
RET="$?"
if [ "$RET" -eq "2" ]; then
echo "ERROR with CRITICAL_MONITORED_OFFLINE_RANGE"
exit $STATE_UNKNOWN
elif [ "$RET" -eq "0" ]; then
TMP="$VALUE_MONITORED_OFFLINE_PEERS monitored offline peers"
if [ "$RETURN_STATUS" -ne "$STATE_CRITICAL" ]; then
RETURN_OUTPUT="$TMP"
else
RETURN_OUTPUT="$RETURN_OUTPUT, $TMP"
fi
RETURN_STATUS=$STATE_CRITICAL
fi
fi
# Affichage final
# Petit ajout dans l'indication
RETURN_OUTPUT_ADDENDUM=""
if [ -n "$PROBLEMATIC_LINES" ]; then
RETURN_OUTPUT_ADDENDUM="$( printf " (not ok peers : %s)" "$PROBLEMATIC_LINES" )"
fi
printf "%s%s | total=%d;%s;%s monitored_online=%d;%s;%s monitored_offline=%d;%s;%s unmonitored_online=%d;%s;%s unmonitored_offline=%d;%s;%s\n" "$RETURN_OUTPUT" "$RETURN_OUTPUT_ADDENDUM" \
"$VALUE_TOTAL_PEERS" "$WARNING_TOTAL_RANGE" "$CRITICAL_TOTAL_RANGE" \
"$VALUE_MONITORED_ONLINE_PEERS" "$WARNING_MONITORED_ONLINE_RANGE" "$CRITICAL_MONITORED_ONLINE_RANGE" \
"$VALUE_MONITORED_OFFLINE_PEERS" "$WARNING_MONITORED_OFFLINE_RANGE" "$CRITICAL_MONITORED_OFFLINE_RANGE" \
"$VALUE_UNMONITORED_ONLINE_PEERS" "$WARNING_UNMONITORED_ONLINE_RANGE" "$CRITICAL_UNMONITORED_ONLINE_RANGE" \
"$VALUE_UNMONITORED_OFFLINE_PEERS" "$WARNING_UNMONITORED_OFFLINE_RANGE" "$CRITICAL_UNMONITORED_OFFLINE_RANGE"
exit $RETURN_STATUS

434
nagios/check_bind9.pl Executable file
View file

@ -0,0 +1,434 @@
#!/usr/bin/perl
#
# host_check_bind.pl - Nagios BIND9 Monitoring Plugin - Host Check
#
# Indicate compatibility with the Nagios embedded perl interpreter
# nagios: +epn
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
my $COPYRIGHT = q{Copyright (C) 2010};
my $VERSION = q{Version 1.0.0};
my $AUTHOR = q{David Goh <david@goh.id.au> - http://goh.id.au/~david/};
my $SOURCE = q{GIT: http://github.com/thorfi/nagios-bind9-plugin};
my $LICENSE =
q{Licensed as GPLv3 or later - http://www.gnu.org/licenses/gpl.html};
# Force all stderr to stdout
*STDERR = *STDOUT;
#use strict;
#use warnings;
use Carp ();
use English;
use Fcntl qw(:seek);
use Getopt::Long;
use IO::Handle;
use IO::File;
# nagios exit codes
#0 OK UP
#1 WARNING UP or DOWN/UNREACHABLE*
#2 CRITICAL DOWN/UNREACHABLE
#3 UNKNOWN DOWN/UNREACHABLE
# Note: If the use_aggressive_host_checking option is enabled, return codes of
# 1 will result in a host state of DOWN or UNREACHABLE. Otherwise return codes
# of 1 will result in a host state of UP.
my $NAGIOS_EXIT_OK = 0;
my $NAGIOS_EXIT_WARNING = 1;
my $NAGIOS_EXIT_CRITICAL = 2;
my $NAGIOS_EXIT_UNKNOWN = 3;
my %OPTIONS = (
q{ps-path} => q{ps},
q{pid-path} => q{/var/run/named.pid},
q{rndc-path} => q{rndc},
q{rndc-args} => q{},
q{sudo-path} => q{},
q{stats-path} => q{/var/run/named.stats},
q{stats-seek} => 20480,
q{timeout} => 30,
);
# Options to supply to ps command
# These commands are presumed to result in lines with the PID in
# column two (whitespace separated).
# Column two will be expected to match the whitespace trimmed contents
# of the --pid-path
my %PS_OPTS_FOR_OS = (
q{darwin} => q{auxww},
q{freebsd} => q{auxww},
q{linux} => q{auxww},
q{hpux} => q{-ef},
q{irix} => q{-ef},
q{openbsd} => q{auxww},
q{solaris} => q{-ef},
q{sunos} => q{auxww},
);
my $PS_OPTIONS = $PS_OPTS_FOR_OS{$OSNAME} || q{auxww};
my $print_help_sref = sub {
print qq{Usage: $PROGRAM_NAME
--pid-path: /path/to/named.pid (Default: $OPTIONS{'pid-path'})
--stats-path: /path/to/named.stats (Default: $OPTIONS{'stats-path'})
--ps-path: /path/to/bin/ps (Default: $OPTIONS{'ps-path'})
--rndc-path: /path/to/sbin/rndc (Default: $OPTIONS{'rndc-path'})
--sudo-path: /path/to/bin/sudo (Default: None)
--stats-seek: bytes to seek backwards to read last stats (Default: $OPTIONS{'stats-seek'})
--rndc-args: additional args to rndc (Default: None)
--timeout: seconds to wait before dying (Default: $OPTIONS{'timeout'})
--version: print version and exit
--help: print this help and exit
$PROGRAM_NAME is a Nagios Plugin which checks that BIND9 is working
by checking the contents of --pid-path and checking that that process
is alive.
If --rndc-args is set, the argument will have any semicolons, ampersands,
angle brackets and pipe characters removed, and then bit split on whitespace
and supplied as individual arguments in between 'rndc' and 'stats'
If --pid-path is set to empty string or a non-existent file, no check
will be done.
It also calls rndc status, rndc stats, and reports the latest statistics found
in --stats-path as well as gathered from rndc status
If --sudo-path is specified then it will be used to call rndc
};
if ( not defined $PS_OPTS_FOR_OS{$OSNAME} ) {
print qq{
Unknown \$OSNAME $OSNAME, please report to $AUTHOR with OSNAME and ps options
that will result in lines with the PID in column two (whitespace separated).
Column two of the output from ps will be expected to match the whitespace
trimmed contents of --pid-path
};
}
print qq{
$COPYRIGHT
$VERSION
$AUTHOR
$SOURCE
$LICENSE
};
};
my $print_version_sref = sub {
print qq{$VERSION - $COPYRIGHT - $AUTHOR};
};
my $getopt_result = GetOptions(
"pid-path=s" => \$OPTIONS{'pid-path'},
"rndc-path=s" => \$OPTIONS{'rndc-path'},
"sudo-path=s" => \$OPTIONS{'sudo-path'},
"stats-path=s" => \$OPTIONS{'stats-path'},
"stats-seek=i" => \$OPTIONS{'stats-seek'},
"rndc-args=s" => \$OPTIONS{'rndc-args'},
"temp-path=s" => \$OPTIONS{'temp-path'},
"timeout=i" => \$OPTIONS{'timeout'},
"version" => sub { $print_version_sref->(); exit $NAGIOS_EXIT_UNKNOWN; },
"help" => sub { $print_help_sref->(); exit $NAGIOS_EXIT_UNKNOWN; },
);
if ( not $getopt_result ) {
print qq{Error: Options failure\n};
$print_help_sref->();
exit $NAGIOS_EXIT_UNKNOWN;
}
$SIG{'ALRM'} = sub {
Carp::cluck(q{BIND9 plugin timed out});
exit $NAGIOS_EXIT_WARNING;
};
alarm $OPTIONS{'timeout'};
my @RNDC_ARGV = ();
if ( length $OPTIONS{'sudo-path'} ) {
push @RNDC_ARGV, $OPTIONS{'sudo-path'};
}
push @RNDC_ARGV, $OPTIONS{'rndc-path'};
if ( length $OPTIONS{'rndc-args'} ) {
my $args = $OPTIONS{'rndc-args'};
$args =~ s/[;<>|&]//g;
push @RNDC_ARGV, split /\s+/, $args;
}
my @RNDC_STATS = ( @RNDC_ARGV, q{stats}, );
my @RNDC_STATUS = ( @RNDC_ARGV, q{status}, );
my @STATS_KEYS = qw(
success referral nxrrset nxdomain recursion failure duplicate dropped
);
my %STATS_ENDMAP = (
'queries resulted in successful answer' => 'success',
'queries resulted in referral answer' => 'referral',
'queries resulted in non authoritative answer' => 'referral',
'queries resulted in nxrrset' => 'nxrrset',
'queries resulted in NXDOMAIN' => 'nxdomain',
'queries caused recursion' => 'recursion',
'queries resulted in SERVFAIL' => 'failure',
'duplicate queries received' => 'duplicate',
'queries dropped' => 'dropped',
);
# Regular expressions used to
# parse rndc stats data
my $STATS_RESET_RE = quotemeta q{+++ Statistics Dump +++};
my $STATS_STARTS_RE =
q{^(} . ( join q{|}, map { quotemeta $_ } @STATS_KEYS ) . q{)};
my $STATS_ENDS_RE =
q{(} . ( join q{|}, map { quotemeta $_ } keys %STATS_ENDMAP ) . q{)$};
my @STATUS_KEYS = qw(
cpus
workers
zones
debug
xfers_running
xfers_deferred
soa_running
udp_running
udp_soft_limit
udp_hard_limit
tcp_running
tcp_hard_limit
);
my @PERFKEYS = ( @STATS_KEYS, @STATUS_KEYS, );
my %PERFDATA = map { ( $_ => 0, ) } @PERFKEYS;
sub slurp_command {
my $fh = new IO::Handle;
open $fh, q{-|}, @_ or die qq{$!};
return $fh->getlines();
}
my $BIND_PID;
if ( $OPTIONS{'pid-path'} ) {
my $path = $OPTIONS{'pid-path'};
if ( -f $path ) {
my $fh = new IO::File $path, q{r};
if ( not defined $fh ) {
print qq{BIND9 PID file at $path failed to open\n};
exit $NAGIOS_EXIT_CRITICAL;
}
my $fh_line = $fh->getline();
if ( not defined $fh_line ) {
print qq{BIND9 PID file $path is empty\n};
exit $NAGIOS_EXIT_CRITICAL;
}
$BIND_PID = $fh_line;
$BIND_PID =~ s/^\s+//;
$BIND_PID =~ s/\s+$//;
if ( $BIND_PID !~ m/^\d+$/ ) {
print qq{BIND9 PID file $path did not contain a number\n};
exit $NAGIOS_EXIT_CRITICAL;
}
my @ps_lines = slurp_command( $OPTIONS{'ps-path'}, $PS_OPTIONS );
my $ps_found = 0;
for my $ps_line (@ps_lines) {
$ps_line =~ s/^\s+//;
my @bits = split /\s+/, $ps_line;
if ( ( int @bits ) < 2 ) {
next;
}
if ( $bits[1] eq $BIND_PID ) {
$ps_found = 1;
last;
}
}
if ( not $ps_found ) {
print
qq{BIND9 PID file $path contains not-running PID $BIND_PID\n};
exit $NAGIOS_EXIT_CRITICAL;
}
}
else {
print qq{BIND9 PID file $path not found\n};
exit $NAGIOS_EXIT_CRITICAL;
}
}
my $exit_message = q{};
# Run rndc stats to put latest data in the stats-path
system @RNDC_STATS;
# and slurp the latest data from stats-path
my $stats_fh = new IO::File $OPTIONS{'stats-path'}, q{r};
if ( not defined $stats_fh ) {
$exit_message .= qq{Failed to open --stats-path };
$exit_message .= $OPTIONS{'stats-path'};
$exit_message .= qq{: $!.};
}
else {
# We have a stats file, so seek backwards in it and read it out.
$stats_fh->seek( -$OPTIONS{'stats-seek'}, SEEK_END );
my $found_stats_start = 0;
while ( my $stats_line = $stats_fh->getline() ) {
chomp $stats_line;
$stats_line =~ s/^\s+//;
$stats_line =~ s/\s+$//;
if ( $stats_line =~ m/$STATS_RESET_RE/i ) {
# Reset the stats, we have a new block
$found_stats_start = 1;
for my $k (@STATS_KEYS) {
$PERFDATA{$k} = 0;
}
next;
}
if ( $stats_line =~ m/$STATS_STARTS_RE/i ) {
my $k = $1;
my @bits = split /\s+/, $stats_line;
my $number = $bits[-1];
if ( $number =~ m/^\d+$/ ) {
$PERFDATA{$k} += $number;
}
next;
}
if ( $stats_line =~ m/$STATS_ENDS_RE/i ) {
my $k = $STATS_ENDMAP{$1};
if ( not defined $k ) {
next;
}
my @bits = split /\s+/, $stats_line;
my $number = $bits[0];
if ( $number =~ m/^\d+$/ ) {
$PERFDATA{$k} += $number;
}
next;
}
}
if ( not $found_stats_start ) {
$exit_message .= q{Failed to find statistics block in --stats-path };
$exit_message .= $OPTIONS{'stats-path'};
$exit_message .= q{.};
}
}
my $found_status_data = 0;
# Run rndc status to slurp the bind9 status info
for my $status_line ( slurp_command(@RNDC_STATUS) ) {
if ( $status_line =~ m/CPUs found: (\d+)/i ) {
$PERFDATA{'cpus'} = $1;
}
elsif ( $status_line =~ m/worker threads: (\d+)/i ) {
$PERFDATA{'workers'} = $1;
}
elsif ( $status_line =~ m/number of zones: (\d+)/i ) {
$PERFDATA{'zones'} = $1;
}
elsif ( $status_line =~ m/debug level: (\d+)/i ) {
$PERFDATA{'debug'} = $1;
}
elsif ( $status_line =~ m/xfers running: (\d+)/i ) {
$PERFDATA{'xfers_running'} = $1;
}
elsif ( $status_line =~ m/xfers deferred: (\d+)/i ) {
$PERFDATA{'xfers_deferred'} = $1;
}
elsif ( $status_line =~ m/soa queries in progress: (\d+)/i ) {
$PERFDATA{'soa_running'} = $1;
}
elsif ( $status_line =~ m/recursive clients: (\d+)\/(\d+)\/(\d+)/i ) {
$PERFDATA{'udp_running'} = $1;
$PERFDATA{'udp_soft_limit'} = $2;
$PERFDATA{'udp_hard_limit'} = $3;
}
elsif ( $status_line =~ m/tcp clients: (\d+)\/(\d+)/i ) {
$PERFDATA{'tcp_running'} = $1;
$PERFDATA{'tcp_hard_limit'} = $2;
}
else {
# Skip if we didn't match anything
next;
}
# If we did match something, say so
$found_status_data = 1;
}
if ( not $found_status_data ) {
$exit_message .= q{Failed to find status data in: '};
$exit_message .= $OPTIONS{'rndc-path'};
if ( length $OPTIONS{'rndc-args'} ) {
$exit_message .= q{ };
$exit_message .= $OPTIONS{'rndc-args'};
}
$exit_message .= q{ status'.};
}
my $exit_code = $NAGIOS_EXIT_OK;
if ( length $exit_message > 0 ) {
$exit_code = $NAGIOS_EXIT_WARNING;
$exit_message =~ s/[\r\n]/ /g;
}
else {
$exit_message = 'OK';
}
print qq{BIND9 $exit_message ;};
if ( defined $BIND_PID ) {
print qq{ PID $BIND_PID ;};
}
print q{ Running:};
print qq{ $PERFDATA{'udp_running'}/$PERFDATA{'udp_soft_limit'}};
print qq{/$PERFDATA{'udp_hard_limit'} UDP,};
print qq{ $PERFDATA{'tcp_running'}/$PERFDATA{'tcp_hard_limit'} TCP,};
print qq{ $PERFDATA{'xfers_running'} xfers;};
print qq{ $PERFDATA{'xfers_deferred'} deferred xfers;};
print qq{ $PERFDATA{'zones'} zones ;};
print qq{ |};
# Generate perfdata in PNP4Nagios format
# http://docs.pnp4nagios.org/pnp-0.6/perfdata_format
# Stats keys are all 'Counter' data
for my $k (@STATS_KEYS) {
print q{ } , $k , q{=} , $PERFDATA{$k} , q{c};
}
my %EXTRAS = (
q{debug} => ';1', # Warning if in debug
);
if ( $PERFDATA{'udp_soft_limit'} + $PERFDATA{'udp_hard_limit'} > 0 ) {
# Running at soft limit is warning, running at hard limit is critical
$EXTRAS{'udp_running'} = q{;}
. ( $PERFDATA{'udp_soft_limit'} || q{} ) . q{;}
. ( $PERFDATA{'udp_hard_limit'} || q{} );
}
if ( $PERFDATA{'tcp_hard_limit'} ) {
# Running at hard limit is critical
$EXTRAS{'tcp_running'} = q{;;} . $PERFDATA{'tcp_hard_limit'};
}
for my $k (@STATUS_KEYS) {
print q{ } , $k , q{=} , $PERFDATA{$k};
if ( defined $EXTRAS{$k} ) {
print $EXTRAS{$k};
}
}
exit $exit_code;

199
nagios/check_crl Normal file
View file

@ -0,0 +1,199 @@
#!/usr/bin/perl -w
#
#
# check_crl -f <filename> -w <warn> -c <crit>
#
# Script to check the "Next Update" time of a revocation list within the apache
# webserver (users.crl).
# Warn and crit are the number of days left before the expiration date is reached.
#
# Changes and Modifications
# =========================
# 23.05.2007 - 1.0.0 R. Kaiser autinform
# Created
#
use Time::Local;
use POSIX;
use strict;
use Getopt::Long;
use vars qw($opt_c $opt_w $opt_f $opt_h $opt_V);
use vars qw($PROGNAME);
use vars qw($REVISION);
use lib "/usr/lib/nagios/plugins" ;
use utils qw($TIMEOUT %ERRORS &print_revision &support &usage);
# Programname and version
$PROGNAME = "check_crl";
$REVISION = "\$Revision: 1.0.0 \$";
# Definition of my defaults
my $def_warn=10;
my $def_crit=4;
sub print_help ();
sub print_usage ();
sub zeit_wandeln_in_sek ();
Getopt::Long::Configure('bundling');
GetOptions
("V" => \$opt_V, "version" => \$opt_V,
"h" => \$opt_h, "help" => \$opt_h,
"f=s" => \$opt_f, "file=s" => \$opt_f,
"w=i" => \$opt_w, "warning=i" => \$opt_w,
"c=i" => \$opt_c, "critical=i" => \$opt_c);
if ($opt_V) {
print_revision($PROGNAME,$REVISION);
exit $ERRORS{'OK'};
}
if ($opt_h) {print_help(); exit 0;}
($opt_f) || ($opt_f = shift) || usage("File not specified\n");
my $datei = $1 if ($opt_f =~ /^([\/-_.A-Za-z0-9]+)$/);
($datei) || usage("Invalid filename: $opt_f\n");
($opt_w) || ($opt_w = shift) || ($opt_w = $def_warn);
my $warn = $1 if ($opt_w =~ /^([0-9]{1,4})$/);
($warn) || usage("Invalid warning threshold: $opt_w\n");
($opt_c) || ($opt_c = shift) || ($opt_c = $def_crit);
my $crit = $1 if ($opt_c =~ /^([0-9]{1,4})$/);
($crit) || usage("Invalid critical threshold: $opt_c\n");
# verify warning is less than critical
unless ( $warn > $crit ) {
usage("days left: warning ($opt_w) should be greater than critical ($opt_c)\n");
}
# check file access
unless ( -r $datei ) {
usage("File ($datei) not found or not accessable.\n");
}
# end of params checking
my $state = "OK";
my $answer = undef;
my $res = undef;
my @lines = undef;
my $datum = undef;
my $monat= undef;
my $timesec = undef;
# Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
print "No Answer from Client\n";
exit $ERRORS{"UNKNOWN"};
};
alarm($TIMEOUT);
########## Action
# Get the "Next Update" line of the crl.
my $crl_zeit = qx(/usr/bin/openssl crl -noout -text -in $datei | /bin/grep " Next Update:");
$crl_zeit =~ s/^ +//g; # remove leading blanks
$crl_zeit =~ s/\n$//; # remove trailing linefeed
$crl_zeit =~ s/ / /g; # remove multiple blanks
my ($nix1, $nix2, $mon, $tag, $zeit, $jahr, $dattyp) = split (/ /, $crl_zeit);
# change month from string to number
my $mon_liste = "JanFebMarAprMayJunJulAugSepOctNovDec";
$monat = (index($mon_liste, $mon) / 3) + 1;
# change to seconds since 01.01.1970
$timesec = zeit_wandeln_in_sek();
# get current time and check the difference
my $act_time = time();
my $SekDiff = $timesec - $act_time;
my $SekRest = $SekDiff;
# make the difference human readable
my $Tage = int($SekRest / (24 * 3600));
$SekRest = $SekRest - ($Tage * 24 * 3600);
my $Stunden = int($SekRest / 3600);
$SekRest = $SekRest - ($Stunden * 3600);
my $Minuten = int($SekRest / 60);
$SekRest = $SekRest - ($Minuten * 60);
#Turn off alarm
alarm(0);
# and now build the answer
my $txt_Tage = "Tage";
my $txt_Stun = "Stunden";
my $txt_Minu = "Minuten";
my $txt_Seku = "Sekunden";
$txt_Tage = "Tag" if ( $Tage == 1 );
$txt_Stun = "Stunde" if ( $Stunden == 1 );
$txt_Minu = "Minute" if ( $Minuten == 1 );
$txt_Seku = "Sekunde" if ( $SekRest == 1 );
$answer = "CRL Restzeit: $Tage $txt_Tage, $Stunden $txt_Stun, $Minuten $txt_Minu und $SekRest $txt_Seku.\n";
# check the time left with warn and crit
if ( $SekDiff <= ($warn * 24 * 3600) ) {
$state = "WARNING";
}
if ( $SekDiff <= ($crit * 24 * 3600) ) {
$state = "CRITICAL";
}
print $state." ".$answer;
exit $ERRORS{$state};
############################################################################
sub zeit_wandeln_in_sek () {
# Den Monat fuer Perl anpassen.
$monat = $monat - 1;
# Die Zeitangabe auseinander nehmen.
my ($stunde,$minute,$sekunde) = split /\:/, $zeit;
if ( $dattyp eq "GMT" ) {
my $timesec=timegm($sekunde,$minute,$stunde,$tag,$monat,$jahr);
}
elsif ( $dattyp eq "LOC" ) {
my $timesec=timelocal($sekunde,$minute,$stunde,$tag,$monat,$jahr);
}
else {
$timesec=0;
}
}
###
sub print_usage () {
print_revision($PROGNAME,$REVISION);
print "Usage: $PROGNAME -f <filename> [-w <warn> -c <crit>]\n";
}
###
sub print_help () {
print "Checking the expiration date (Next Update) of a revocation list.
";
print_usage();
print "
-f, --filename=STRING
name and location of the revocation list file
-w, --warning=INTEGER
Number of days left (Defaults: $def_warn)
-c, --critical=INTEGER
Number of days left (Defaults: $def_crit)
";
}

204
nagios/check_crl.sh Normal file
View file

@ -0,0 +1,204 @@
#!/bin/sh
# Small script to survey CRL
# GPL v3+
# Default values
# Warning : 10 days - 10 years
RANGE_WARNING="864000:315360000"
# Critical : 4 days
RANGE_CRITICAL="345600:"
# Output
OUTPUT_EXIT_STATUS=0
OUTPUT_DETAIL_WARNING=""
OUTPUT_DETAIL_CRITICAL=""
# Stop at the first non-catched error
set -e
#
# Help function
#
usage() {
cat <<EOF
Usage :
$0 [-w warning_range] [-c critical_range] -f file.crl [[-w...] -f file.crl ] ...
Ranges are in seconds.
Note: Since the file is checked against the lastest ranges given, order
of the arguments are important.
Default values:
warning_range: $RANGE_WARNING
critical_range: $RANGE_CRITICAL
EOF
}
# TODO: manage non-integer values
# Args :
# - value
# - range warning
# - range critical
# Return:
# 0: ok
# 8: syntax error in range
# 9: higher threshold lower than lower threshold
check_range_syntax() {
local REGEXP LOWER_THRESHOLD HIGHER_THRESHOLD
# Check syntax
REGEXP='@\?\(-\?[0-9]\+:\|~:\|:\|\)\(-\?[0-9]\+\|~\|\)'
test -n "$( echo "$1" | sed -n "/^$REGEXP$/p" )" || return 8
# Check that lower limit is lower than higher limit :)
LOWER_THRESHOLD=$( echo "$1" | sed -n "s/$REGEXP/\1/p" | sed 's/:$//' )
HIGHER_THRESHOLD=$( echo "$1" | sed -n "s/$REGEXP/\2/p" )
if test -z "$LOWER_THRESHOLD"; then
LOWER_THRESHOLD=0
fi
if test -z "$HIGHER_THRESHOLD"; then
HIGHER_THRESHOLD='~'
fi
if [ "$LOWER_THRESHOLD" != "~" ]; then
if [ "$HIGHER_THRESHOLD" != "~" ] && [ "$LOWER_THRESHOLD" -gt "$HIGHER_THRESHOLD" ]; then
return 9
fi
fi
printf "%s\t%s" "$LOWER_THRESHOLD" "$HIGHER_THRESHOLD"
return 0
}
# Args :
# - value
# - range
# Return :
# 0: ok
# 1: not in range
# 8-15: see check_range_syntax()
# 16: function call problem
check_range() {
local VALUE LINE RET LOWER_THRESHOLD HIGHER_THRESHOLD
# ranges can be empty
test -n "$1" || return 16
VALUE="$1"
RANGE="$2"
# Analyze range
LINE=$( check_range_syntax "$RANGE" )
RET="$?"
test $RET -eq 0 || return $RET
LOWER_THRESHOLD="$( echo "$LINE" | cut -f 1 )"
HIGHER_THRESHOLD="$( echo "$LINE" | cut -f 2 )"
# Check value
if [ $( echo "$RANGE" | grep -c "^@" ) -eq 0 ]; then
# Normal comparison
if [ "$LOWER_THRESHOLD" != "~" ] && [ "$VALUE" -lt "$LOWER_THRESHOLD" ]; then
return 1
fi
if [ "$HIGHER_THRESHOLD" != "~" ] && [ "$VALUE" -gt "$HIGHER_THRESHOLD" ]; then
return 1
fi
else
# Invert range (inside, inclusive)
if [ "$LOWER_THRESHOLD" = '~' ] || [ "$VALUE" -ge "$LOWER_THRESHOLD" ]; then
if [ "$HIGHER_THRESHOLD" = '~' ] || [ "$VALUE" -le "$HIGHER_THRESHOLD" ]; then
return 1
fi
fi
fi
return 0
}
# Some early checks
if ! which openssl >/dev/null 2>&1 ; then
echo "UNKNOWN 'openssl' not found."
exit 1
fi
#
# Parameters management
#
while getopts hw:c:f: OPT; do
case "$OPT" in
'h')
usage
exit
;;
'w')
if check_range_syntax "$OPTARG" >/dev/null; then
RANGE_WARNING="$OPTARG"
else
echo "UNKNOWN: invalid range."
exit 3
fi
;;
'c')
if check_range_syntax "$OPTARG" >/dev/null; then
RANGE_CRITICAL="$OPTARG"
else
echo "UNKNOWN: invalid range."
exit 3
fi
;;
'f')
# I'm not very proud of this one : aesthetically speaking, treatments
# should not be done during params management :)
CRL_FILE="$OPTARG"
if [ ! -f "$CRL_FILE" ]; then
echo "UNKNOWN: inexistent file."
exit 3
fi
# Extract time left, in seconds
EXPIRATION_DATE="$( openssl crl -noout -text -in "$CRL_FILE" | sed -n "s/^[[:space:]]\+Next Update: \(.*\)$/\1/p" )"
if [ -z "$EXPIRATION_DATE" ]; then
echo "UNKNOWN: couldn't get expiration date."
exit 3
fi
TIME_LEFT=$(( $( date +%s ) - $( date --date="$EXPIRATION_DATE" +%s ) ))
# Check time left against range
if ! check_range "$TIME_LEFT" "$RANGE_CRITICAL"; then
OUTPUT_EXIT_STATUS=2
OUTPUT_DETAIL_CRITICAL="$OUTPUT_DETAIL_CRITICAL crl:$CRL_FILE"
elif ! check_range "$CPT" "$RANGE_WARNING"; then
if [ "$OUTPUT_EXIT_STATUS" -eq 0 ]; then
OUTPUT_EXIT_STATUS=1
fi
OUTPUT_DETAIL_WARNING="$OUTPUT_DETAIL_WARNING crl:$CRL_FILE"
fi
;;
\?)
usage
exit 1
;;
esac
done
case "$OUTPUT_EXIT_STATUS" in
'0')
printf "OK"
;;
'1')
printf "WARNING %s" "$OUTPUT_DETAIL_WARNING"
;;
'2')
printf "CRITICAL %s" "$OUTPUT_DETAIL_CRITICAL"
;;
*)
printf "UNKNOWN"
;;
esac
# on supprime les retours à la ligne
exit $RETURN_STATUS

118
nagios/check_dane_tlsa.sh Executable file
View file

@ -0,0 +1,118 @@
#!/bin/sh
# Not needed in this version of the script
set -e
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
. $PROGPATH/utils.sh
#
# Help function
#
usage() {
cat <<EOF
Usage :
$0 -h
$0 -H host -p port [-P protocol (tcp/udp)] [-S starttls-scheme (smtp)]
EOF
}
#
# Parameters management
#
CHECKED_PROTOCOL="tcp" # it's almost always TCP so we don't bother to ask
OPENSSL_OPTIONS=""
while getopts hH:p:P:S: f; do
case "$f" in
'h')
usage
exit
;;
'H')
CHECKED_HOSTNAME="$OPTARG"
;;
'p')
CHECKED_PORT="$( printf "%d" "$OPTARG" )"
;;
'P')
CHECKED_PROTOCOL="$OPTARG"
;;
'S')
OPENSSL_OPTIONS="$OPENSSL_OPTIONS -starttls $OPTARG"
;;
\?)
usage
exit 1
;;
esac
done
if [ -z "$CHECKED_HOSTNAME" ]; then echo "ERROR empty parameter 'hostname'"; exit $STATE_UNKNOWN; fi
if [ -z "$CHECKED_PORT" ]; then echo "ERROR empty parameter 'port'"; exit $STATE_UNKNOWN; fi
# We get all TLSA record for the host
# FIXME: make a loop
# return example : "1 1 1 4A2403E87DBC4354570C5FDE24348EAED50B7791E4E2C3FC1D79B487 DDB9CC2C"
REQUEST_RECORD="_$CHECKED_PORT._$CHECKED_PROTOCOL.$CHECKED_HOSTNAME"
TLSA_RECORD="$( dig "$REQUEST_RECORD" TLSA +short | sed 's/^\([0-3]\)[[:space:]]\([01]\)[[:space:]]\([01]\)[[:space:]]/\1;\2;\3;/' | sed 's/[[:space:]]//g' )"
if [ -z "$TLSA_RECORD" ]; then echo "ERROR no TLSA record at $REQUEST_RECORD"; exit $STATE_CRITICAL; fi
if [ -z "$( echo "$TLSA_RECORD" | sed -n '/^.;/p' )" ]; then echo "ERROR record malformed or too modern for this plugin"; exit $STATE_CRITICAL; fi
CPT=0
OUTPUT_STATUS=$STATE_OK
OUTPUT_DETAILS=""
MATCH_NUMBER=0
for LINE in $TLSA_RECORD; do
CERTIFICATE_USAGE="$( echo "$LINE" | cut -d ';' -f 1 )"
SELECTOR="$( echo "$LINE" | cut -d ';' -f 2 )"
MATCHING_TYPE="$( echo "$LINE" | cut -d ';' -f 3 )"
DATA="$( echo "$LINE" | cut -d ';' -f 4 )"
# PKI verification (TODO: to implement)
if [ "$CERTIFICATE_USAGE" -eq "0" ] || [ "$CERTIFICATE_USAGE" -eq "1" ]; then
OUTPUT_DETAILS="$OUTPUT_DETAILS (PKI check not implemented)"
fi
# Pointer
if [ "$CERTIFICATE_USAGE" -eq "1" ] || [ "$CERTIFICATE_USAGE" -eq "3" ]; then
# Check the certificate directly
case "$MATCHING_TYPE" in
'0')
# Entire info in base64
# TODO
;;
'1')
# SHA-256
OPENSSL_RESULT="$( openssl s_client $OPENSSL_OPTIONS -connect $CHECKED_HOSTNAME:$CHECKED_PORT -servername $CHECKED_HOSTNAME </dev/null 2>/dev/null | openssl x509 -pubkey -noout | openssl rsa -pubin -outform DER 2>/dev/null | openssl dgst -sha256 | tr a-z A-Z | sed 's/.*[[:space:]]//' )"
;;
'2')
# SHA-512
OPENSSL_RESULT="$( openssl s_client $OPENSSL_OPTIONS -connect $CHECKED_HOSTNAME:$CHECKED_PORT -servername $CHECKED_HOSTNAME </dev/null 2>/dev/null | openssl x509 -pubkey -noout | openssl rsa -pubin -outform DER 2>/dev/null | openssl dgst -sha512 | tr a-z A-Z | sed 's/.*[[:space:]]//' )"
;;
esac
if [ "$( echo "$DATA" | tr a-z A-Z )" = "$OPENSSL_RESULT" ]; then
OUTPUT_DETAILS="$OUTPUT_DETAILS $( echo $LINE | sed 's/^\(.\{8\}\).*\(.\{3\}\)$/\1...\2/' )-ok"
MATCH_NUMBER=$(( $MATCH_NUMBER + 1 ))
else
OUTPUT_DETAILS="$OUTPUT_DETAILS $( echo $LINE | sed 's/^\(.\{8\}\).*\(.\{3\}\)$/\1...\2/' )-NO"
fi
else
# Check one of the trust anchor
# TODO: to implement
OUTPUT_DETAILS="$OUTPUT_DETAILS (trust anchor check not implemented)"
fi
done
if [ "$MATCH_NUMBER" -eq 0 ]; then
echo "ERROR no match $OUTPUT_DETAILS"
exit $STATE_CRITICAL
else
echo "OK $OUTPUT_DETAILS"
exit $STATE_OK
fi

View file

@ -0,0 +1,132 @@
#!/bin/sh
# Petit script custom pour vérifier que, dans une
# architecture maître-esclave, certains fichiers de conf.
# ou certaines arborescences ont bien été déployées
# sur les 2
# GPL v3+
# Default values
RSYNC_OPTIONS="-n -aHAX --delete --out-format %n"
SERVERS_LIST=""
DEBUG_MODE=""
# Output
OUTPUT_EXIT_STATUS=0
OUTPUT_DETAIL_WARNING=""
OUTPUT_DETAIL_CRITICAL=""
OUTPUT_PERFDATA=""
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
# Stop at the first non-catched error
set -e
# Include check_range()
. $PROGPATH/utils.sh
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 -s srv1 [-s srv2] [-s ...] rep1/ rep2/ file3 ...
Returns OK ($STATE_OK) if rsync doesn't find any file to transfer,
CRITICAL ($STATE_CRITICAL) if it does.
No soft warning. Soft warnings are for pussies.
Wildcards can be used (or any rsync/bash trick, at your own risk...)
Don't forget the final '/' for directories.
For example :
$0 -s slave1.example.com -s slave2.example.com /etc/apache/ /etc/crontab /etc/libapache2-mod-jk/workers.properties
The options used for rsync are :
$RSYNC_OPTIONS
EOF
}
check_range_syntax() {
check_range 0 "$1" >/dev/null 2>&1
if [ "$?" -eq "2" ]; then
return 1
fi
return 0
}
# Some early checks
if ! which find >/dev/null 2>&1 ; then
echo "UNKNOWN 'find' not found."
exit 1
fi
#
# Gestion des paramètres
#
while getopts hs:v f; do
case "$f" in
'h')
usage
exit
;;
's')
# Yeah, I don't manage server name with space in it... Sorry...
# If you find a elegant way to do it, please mail !
SERVERS_LIST="$SERVERS_LIST $OPTARG"
;;
'v')
DEBUG_MODE="-v"
;;
\?)
usage
exit 1
;;
esac
done
shift $( expr $OPTIND - 1 )
TARGETS_LIST="$@"
if [ -z "$SERVERS_LIST" ]; then
OUTPUT_EXIT_STATUS=$STATE_UNKNOWN
OUTPUT_DETAIL_UNKNOWN="$OUTPUT_DETAIL_UNKNOWN (no server specified)"
else
for SERVER in $SERVERS_LIST; do
for TARGET in $TARGETS_LIST; do
if [ $( rsync $RSYNC_OPTIONS "$TARGET" "$SERVER:$TARGET" 2>&1 | wc -l ) -gt 0 ]; then
OUTPUT_EXIT_STATUS=$STATE_CRITICAL
OUTPUT_DETAIL_CRITICAL="$OUTPUT_DETAIL_CRITICAL $SERVER:$TARGET"
if [ -n "$DEBUG_MODE" ]; then
echo rsync $RSYNC_OPTIONS "$TARGET" "$SERVER:$TARGET"
rsync $RSYNC_OPTIONS "$TARGET" "$SERVER:$TARGET"
fi
fi
done
done
fi
case "$OUTPUT_EXIT_STATUS" in
'0')
printf "OK"
;;
'1')
printf "WARNING %s" "$OUTPUT_DETAIL_WARNING"
;;
'2')
printf "CRITICAL %s" "$OUTPUT_DETAIL_CRITICAL"
;;
*)
printf "UNKNOWN %s" "$OUTPUT_DETAIL_UNKNOWN"
;;
esac
# (pas de perfdata dans ce script)
#printf "|%s\n" "$OUTPUT_PERFDATA"
printf "\n"
# on supprime les retours à la ligne
exit $OUTPUT_EXIT_STATUS

111
nagios/check_drupal_multi.sh Executable file
View file

@ -0,0 +1,111 @@
#!/bin/sh
# This script's purpose is to launch check_drupal with URL and
# password stored in a listing file.
#
# memento :
# drush en nagios
# drush vset nagios_page_path NAGIOS_PATH
# drush vset nagios_ua NAGIOS_PASSWORD
# Default options
NAGIOS_PLUGIN="/usr/lib/nagios/plugins/check_drupal"
DATABASE="/etc/nagios-plugins/check_drupal_multi.lst"
# Par défaut, on arrête le script à la première erreur non "catchée"
set -e
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
. $PROGPATH/utils.sh
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 [ -C check_drupal path] [ -d listing_file ] [ -H host_name ] [ -t default time_out_seconds ]
$0 -h
-h : ce message d'aide
Listing_file is a text file with 3 fields, separated by tabulation.
1 : hostname
2 : UID
3 : path (optional)
4 : timeout (optional)
Example :
www.localdomain.com xxxyyyzzz
www.otherdomain.com zzzyyyxxx hiddennagios 30
Default options :
Database : $DATABASE
check_drupal path : $NAGIOS_PLUGIN
EOF
}
# Début du code
# gestion des options de lancement
while getopts C:d:H:t:h f; do
case $f in
'C')
NAGIOS_PLUGIN="$OPTARG"
;;
'd')
DATABASE="$OPTARG"
;;
'H')
NAGIOS_HOST="$OPTARG"
;;
't')
NAGIOS_DEFAULT_TIMEOUT="$OPTARG"
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
#(code inutile, mais que je garde parce qu'on ne sait jamais)
#shift $( expr $OPTIND - 1 )
#DATA="$1"
# Petite vérif.
if [ ! -f "$DATABASE" ]; then
echo "UNKNOWN: no check_drupal_multi listing found."
exit $STATE_UNKNOWN
fi
if [ ! -f "$NAGIOS_PLUGIN" ]; then
echo "UNKNOWN: check_drupal could not be found."
exit $STATE_UNKNOWN
fi
# Lookup for host in database
DATABASE_LINE="$( grep -w "^$NAGIOS_HOST" "$DATABASE" | head -n 1 )"
NAGIOS_DRUPAL_UID="$( printf "$DATABASE_LINE" | cut -f 2 )"
NAGIOS_DRUPALPATH="$( printf "$DATABASE_LINE" | cut -f 3 )"
NAGIOS_TIMEOUT="$( printf "$DATABASE_LINE" | cut -f 4 )"
if [ -z "$NAGIOS_TIMEOUT" ]; then
NAGIOS_TIMEOUT="$NAGIOS_DEFAULT_TIMEOUT"
fi
# Check we actually got a host
if [ -z "$NAGIOS_DRUPAL_UID" ]; then
echo "UNKNOWN: Host not found in listing file."
exit $STATE_UNKNOWN
fi
# Launch the actual plugin with config
$NAGIOS_PLUGIN -H "$NAGIOS_HOST" -U "$NAGIOS_DRUPAL_UID" \
$( test -n "$NAGIOS_DRUPALPATH" && printf "%s %s" "-P" "$NAGIOS_DRUPALPATH" ) \
$( test -n "$NAGIOS_TIMEOUT" && printf "%s %s" "-t" "$NAGIOS_TIMEOUT" )

160
nagios/check_file_age.sh Executable file
View file

@ -0,0 +1,160 @@
#!/bin/sh
# Petit script custom pour vérifier l'âge de certains fichiers
# GPL v3+
# Default values
RANGE_WARNING_AGE="7"
RANGE_CRITICAL_AGE="30"
RANGE_WARNING_FILES_NUMBER="1:"
RANGE_CRITICAL_FILES_NUMBER="1:"
FIND_BASEDIR="/"
# Output
OUTPUT_EXIT_STATUS=0
OUTPUT_DETAIL_WARNING=""
OUTPUT_DETAIL_CRITICAL=""
OUTPUT_PERFDATA=""
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
# Stop at the first non-catched error
set -e
# Include check_range()
. $PROGPATH/utils.sh
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 [-w age_warning_limit] [-c age_critical_limit] [-W files_number_warning_range] [-C files_number_critical_range] -b find-basedir -f find-name-regexp [[-w...] -f find-name-regexp] ...
Note: Since the file(s) are checked against the lastest ranges given, order
of the arguments are important.
Default values:
age_warning_range (in days): $RANGE_WARNING_AGE
age_critical_range (in days): $RANGE_CRITICAL_AGE
files_number_warning_range : $RANGE_WARNING_FILES_NUMBER
files_number_critical_range : $RANGE_CRITICAL_FILES_NUMBER
find-basedir: $FIND_BASEDIR
The check is only done on the couple crit-crit and warn-warn.
No combination of crit/warn is made (for example, critical number of
files at age warning range).
EOF
}
check_range_syntax() {
check_range 0 "$1" >/dev/null 2>&1
if [ "$?" -eq "2" ]; then
return 1
fi
return 0
}
# Some early checks
if ! which find >/dev/null 2>&1 ; then
echo "UNKNOWN 'find' not found."
exit 1
fi
#
# Gestion des paramètres
#
while getopts hw:c:W:C:b:f: f; do
case "$f" in
'h')
usage
exit
;;
'w')
RANGE_WARNING_AGE="$( printf "%d" "$OPTARG" )"
;;
'c')
RANGE_CRITICAL_AGE="$( printf "%d" "$OPTARG" )"
;;
'W')
if check_range_syntax "$OPTARG" >/dev/null; then
RANGE_WARNING_FILES_NUMBER="$OPTARG"
else
echo "UNKNOWN: invalid range."
exit 3
fi
;;
'C')
if check_range_syntax "$OPTARG" >/dev/null; then
RANGE_CRITICAL_FILES_NUMBER="$OPTARG"
else
echo "UNKNOWN: invalid range."
exit 3
fi
;;
'b')
# TODO : vérifier que le répertoire existe ?
FIND_BASEDIR="$OPTARG"
;;
'f')
# Ce n'est pas très propre, mais on gère tout ici plutôt que de remplir
# un buffer et de le traiter ensuite
FIND_NAME_REGEXP="$OPTARG"
# mémo : 'label'=value[UOM];[warn];[crit];[min];[max]
#OUTPUT_PERFDATA=$( printf "%s'port%d'=%d;%s;%s;0;" \
# "$( test -n "$OUTPUT_PERFDATA" && echo "$OUTPUT_PERFDATA " )" \
# "$PORT_NUMBER" \
# "$CPT" \
# "$RANGE_WARNING" \
# "$RANGE_CRITICAL" )
FILES_NUMBER_AT_WARN_AGE="$( find "$FIND_BASEDIR" -name "$FIND_NAME_REGEXP" -type f -mtime "-$RANGE_WARNING_AGE" -printf "a\n" | wc -l )"
FILES_NUMBER_AT_CRIT_AGE="$( find "$FIND_BASEDIR" -name "$FIND_NAME_REGEXP" -type f -mtime "-$RANGE_CRITICAL_AGE" -printf "a\n" | wc -l )"
if check_range "$FILES_NUMBER_AT_CRIT_AGE" "$RANGE_CRITICAL_FILES_NUMBER" ; then
OUTPUT_EXIT_STATUS=2
OUTPUT_DETAIL_CRITICAL="$OUTPUT_DETAIL_CRITICAL basedir:$FIND_BASEDIR($FILES_NUMBER_AT_CRIT_AGE files at less than $RANGE_CRITICAL_AGE days)"
elif check_range "$FILES_NUMBER_AT_WARN_AGE" "$RANGE_WARNING_FILES_NUMBER"; then
if [ "$OUTPUT_EXIT_STATUS" -eq 0 ]; then
OUTPUT_EXIT_STATUS=1
fi
OUTPUT_DETAIL_WARNING="$OUTPUT_DETAIL_WARNING basedir:$FIND_BASEDIR($FILES_NUMBER_AT_WARN_AGE files at less than $RANGE_WARNING_AGE days)"
fi
;;
\?)
usage
exit 1
;;
esac
done
case "$OUTPUT_EXIT_STATUS" in
'0')
printf "OK"
;;
'1')
printf "WARNING %s" "$OUTPUT_DETAIL_WARNING"
;;
'2')
printf "CRITICAL %s" "$OUTPUT_DETAIL_CRITICAL"
;;
*)
printf "UNKNOWN"
;;
esac
# (pas de perfdata dans ce script)
#printf "|%s\n" "$OUTPUT_PERFDATA"
printf "\n"
# on supprime les retours à la ligne
exit $OUTPUT_EXIT_STATUS

112
nagios/check_first_ip.sh Executable file
View file

@ -0,0 +1,112 @@
#!/bin/sh
# Petit script pour vérifier qu'une IP souhaitée est en tête des IP configurées
# GPL v3+ (copyright chl-dev@bugness.org)
# Initialisation
IGNORED_ADDRESS="127.0.0. ::1 fe80:"
# Output
OUTPUT_EXIT_STATUS=0
OUTPUT_DETAIL_WARNING=""
OUTPUT_DETAIL_CRITICAL=""
OUTPUT_PERFDATA=""
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
# Stop at the first non-catched error
set -e
# Include check_range()
. $PROGPATH/utils.sh
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 [-x xxxxx] [-x yyyy] [-4 nnn.nnn.nnn.nnn] [-6 nnnn:nnnn:nnnn::nnnn]
WARNING: order of arguments are important.
-x : address (or start of address) which must be ignored. Often '127.0' and 'fe80'.
-4 : IPv4 address which must be first
-6 : IPv6 address which must be first
Default values:
-x "$IGNORED_ADDRESS"
EOF
}
#
# Gestion des paramètres
#
while getopts h4:6:x: f; do
case "$f" in
'h')
usage
exit
;;
'x')
IGNORED_ADDRESS="$OPTARG"
;;
'4')
IPV4="$OPTARG";
LISTING="$( ip -4 addr show | sed -n 's/^[[:space:]]*inet \([0-9.]\+\)\/[0-9].*/\1/p' )"
for i in $IGNORED_ADDRESS; do
LISTING="$( echo "$LISTING" | grep -v "^$i" || true )"
done
FIRST="$( echo "$LISTING" | head -n 1 )"
if [ "$FIRST" != "$IPV4" ]; then
OUTPUT_EXIT_STATUS=2
OUTPUT_DETAIL_CRITICAL="$OUTPUT_DETAIL_CRITICAL ('$FIRST' instead of '$IPV4')"
else
OUTPUT_DETAIL_OK="$OUTPUT_DETAIL_OK ('$FIRST')"
fi
;;
'6')
IPV6="$OPTARG";
LISTING="$( ip -6 addr show | sed -n 's/^[[:space:]]*inet6 \([a-f0-9:]\+\)\/[0-9].*/\1/p' )"
for i in $IGNORED_ADDRESS; do
LISTING="$( echo "$LISTING" | grep -v "^$i" || true )"
done
FIRST="$( echo "$LISTING" | head -n 1 )"
if [ "$FIRST" != "$IPV6" ]; then
OUTPUT_EXIT_STATUS=2
OUTPUT_DETAIL_CRITICAL="$OUTPUT_DETAIL_CRITICAL ('$FIRST' instead of '$IPV6')"
else
OUTPUT_DETAIL_OK="$OUTPUT_DETAIL_OK ('$FIRST')"
fi
;;
\?)
usage
exit 1
;;
esac
done
case "$OUTPUT_EXIT_STATUS" in
'0')
printf "OK %s" "$OUTPUT_DETAIL_OK"
;;
'1')
printf "WARNING %s" "$OUTPUT_DETAIL_WARNING"
;;
'2')
printf "CRITICAL %s" "$OUTPUT_DETAIL_CRITICAL"
;;
*)
printf "UNKNOWN"
;;
esac
# (pas de perfdata dans ce script)
#printf "|%s\n" "$OUTPUT_PERFDATA"
printf "\n"
# on supprime les retours à la ligne
exit $OUTPUT_EXIT_STATUS

View file

@ -0,0 +1,198 @@
#!/bin/sh
# Petit script custom pour vérifier le nombre de connexions sur netstat
# GPL v3+ (copyright chl-dev@bugness.org)
# Default values
RANGE_WARNING="1:50"
RANGE_CRITICAL="1:100"
# Output
OUTPUT_EXIT_STATUS=0
OUTPUT_DETAIL_WARNING=""
OUTPUT_DETAIL_CRITICAL=""
OUTPUT_PERFDATA=""
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
# Stop at the first non-catched error
set -e
# Include check_range()
. $PROGPATH/utils.sh
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 [-w warning_range] [-c critical_range] -p port [[-w...] -p port] ...
Example :
./check_netstat_connectioncount.sh -w 50 -c 100 -p 80
Note: Since the port is checked against the lastest ranges given, order
of the arguments is important. Ex:
./check_netstat_connectioncount.sh -w 1:5 -c 1:10 -p 22 -p listen-unix:X11 -w 1:50 -c 1:100 -p 80 -p 443
Special values for 'port' :
all
all-ipv4
all-ipv6
listen
listen-ipv4
listen-ipv6
listen-unix
listen-unix:PATTERN
Default values:
warning_range: $RANGE_WARNING
critical_range: $RANGE_CRITICAL
EOF
}
check_range_syntax() {
check_range 0 "$1" >/dev/null 2>&1
if [ "$?" -eq "2" ]; then
return 1
fi
return 0
}
# Some early checks
for i in netstat ss; do
if which "$i" >/dev/null 2>&1 ; then
COMMAND_SYS="$i"
if [ "$COMMAND_SYS" = "ss" ]; then
OUTPUT_COLUMN=5
else
OUTPUT_COLUMN=4
fi
break
fi
done
if [ -z "$COMMAND_SYS" ]; then
echo "UNKNOWN 'netstat' and 'ss' not found."
exit 1
fi
#
# Gestion des paramètres
#
while getopts hw:c:p: f; do
case "$f" in
'h')
usage
exit
;;
'w')
if check_range_syntax "$OPTARG" >/dev/null; then
RANGE_WARNING="$OPTARG"
else
echo "UNKNOWN: invalid range."
exit 3
fi
;;
'c')
if check_range_syntax "$OPTARG" >/dev/null; then
RANGE_CRITICAL="$OPTARG"
else
echo "UNKNOWN: invalid range."
exit 3
fi
;;
'p')
# Ce n'est pas très propre, mais on gère tout ici plutôt que de remplir
# un buffer et de le traiter ensuite
# Note : grep renvoie un code d'erreur 1 s'il n'y a pas de résultat,
# d'où l'ajout d'un || true sur lui uniquement.
LABEL="$OPTARG"
case "$OPTARG" in
'all')
CPT="$( $COMMAND_SYS -taun | tail -n +2 | wc -l )"
PORT_NUMBER='all'
;;
'all-ipv4')
CPT="$( $COMMAND_SYS -taun4 | tail -n +2 | wc -l )"
PORT_NUMBER='all-ipv4'
;;
'all-ipv6')
CPT="$( $COMMAND_SYS -taun6 | tail -n +2 | wc -l )"
PORT_NUMBER='all-ipv6'
;;
'listen')
CPT="$( $COMMAND_SYS -tlun | tail -n +2 | wc -l )"
PORT_NUMBER='listen'
;;
'listen-ipv4')
CPT="$( $COMMAND_SYS -tlun4 | tail -n +2 | wc -l )"
PORT_NUMBER='listen-ipv4'
;;
'listen-ipv6')
CPT="$( $COMMAND_SYS -tlun6 | tail -n +2 | wc -l )"
PORT_NUMBER='listen-ipv6'
;;
'listen-unix')
CPT="$( $COMMAND_SYS -xl | tail -n +2 | wc -l )"
PORT_NUMBER='listen-unix'
;;
'listen-unix:'*)
CPT="$( $COMMAND_SYS -xl | tail -n +2 | grep "$( echo "$OPTARG" | sed 's/^listen-unix://' )" | wc -l )"
PORT_NUMBER=$OPTARG # risque de bug côté superviseur ?
;;
*)
PORT_NUMBER=$( printf "%d" "$OPTARG" )
LABEL="port$PORT_NUMBER"
CPT="$( $COMMAND_SYS -taun | sed 's/[[:space:]]\+/\t/g' | cut -f "$OUTPUT_COLUMN" | ( grep -c ":$PORT_NUMBER$" || true ) )"
;;
esac
# mémo : 'label'=value[UOM];[warn];[crit];[min];[max]
OUTPUT_PERFDATA=$( printf "%s'%s'=%d;%s;%s;0;" \
"$( test -n "$OUTPUT_PERFDATA" && echo "$OUTPUT_PERFDATA " )" \
"$LABEL" \
"$CPT" \
"$RANGE_WARNING" \
"$RANGE_CRITICAL" )
if check_range "$CPT" "$RANGE_CRITICAL"; then
OUTPUT_EXIT_STATUS=2
OUTPUT_DETAIL_CRITICAL="$OUTPUT_DETAIL_CRITICAL Port:$PORT_NUMBER($CPT conn.)"
elif check_range "$CPT" "$RANGE_WARNING"; then
if [ "$OUTPUT_EXIT_STATUS" -eq 0 ]; then
OUTPUT_EXIT_STATUS=1
fi
OUTPUT_DETAIL_WARNING="$OUTPUT_DETAIL_WARNING Port:$PORT_NUMBER($CPT conn.)"
fi
;;
\?)
usage
exit 1
;;
esac
done
case "$OUTPUT_EXIT_STATUS" in
'0')
printf "OK ($COMMAND_SYS)"
;;
'1')
printf "WARNING ($COMMAND_SYS) %s" "$OUTPUT_DETAIL_WARNING"
;;
'2')
printf "CRITICAL ($COMMAND_SYS) %s" "$OUTPUT_DETAIL_CRITICAL"
;;
*)
printf "UNKNOWN"
;;
esac
printf "|%s\n" "$OUTPUT_PERFDATA"
# on supprime les retours à la ligne
exit $OUTPUT_EXIT_STATUS

View file

@ -0,0 +1,68 @@
#!/bin/sh
# Petit script custom pour relever les compteurs des tables
# de voisins (neighbour) ipv4 (ARP) et ipv6 (Neighbour discorvery)
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 -h
EOF
}
#
# Gestion des paramètres
#
while getopts h f; do
case "$f" in
'h')
usage
exit
;;
'e')
EXCLUSIONS="$( printf "%s\n%s" "$EXCLUSIONS" "$OPTARG" )"
;;
'w')
WARNING_LEVEL=$( printf "%d" "$OPTARG" )
;;
'c')
CRITICAL_LEVEL=$( printf "%d" "$OPTARG" )
;;
\?)
usage
exit 1
;;
esac
done
if ! which ip >/dev/null 2>&1 || [ "$( ip ntable show | wc -l )" -lt 2 ]; then
echo "UNKNOWN souci avec 'ip ntable show'."
exit 1
fi
# On lance la commande
printf "OK|"
# Dans l'ordre :
# - la commande...
# - tout sur 1 ligne
# - 1 ligne = 1 couple interface - protocole
# - on picore les quelques éléments intéressants (uniquement refcnt pour le moment)
# - on remet tout sur une ligne
# - pas d'espace en fin de ligne
ip ntable show \
| tr '\n' '@' \
| sed 's/@@/\n/g' \
| sed -n 's/^\(inet6\?\).*dev\s\+\(\S\+\).*\(refcnt\)\s\+\([0-9]\+\).*/\1_\2\3=\4/gp' \
| tr '\n' ' ' \
| sed 's/\s\+$/\n/'
exit 0

17
nagios/check_network_volume.sh Executable file
View file

@ -0,0 +1,17 @@
#!/bin/sh
printf "OK |"
for i in $( cat /proc/net/dev | sed -n 's/^[[:space:]]*\([a-z0-9\.]\+\):.*/\1/p' ); do
if [ $( echo "$i" | egrep -c '(lo|bond|vmbr)' ) -gt 0 ]; then
continue
fi
VOLUME_RECU="$( cat /proc/net/dev | sed -n "s/^[[:space:]]*$i:[[:space:]]*//p" | sed 's/[[:space:]]\+/ /g' | cut -d " " -f 1 )"
VOLUME_EMIS="$( cat /proc/net/dev | sed -n "s/^[[:space:]]*$i:[[:space:]]*//p" | sed 's/[[:space:]]\+/ /g' | cut -d " " -f 9 )"
PAQUETS_RECUS="$( cat /proc/net/dev | sed -n "s/^[[:space:]]*$i:[[:space:]]*//p" | sed 's/[[:space:]]\+/ /g' | cut -d " " -f 2 )"
PAQUETS_EMIS="$( cat /proc/net/dev | sed -n "s/^[[:space:]]*$i:[[:space:]]*//p" | sed 's/[[:space:]]\+/ /g' | cut -d " " -f 10 )"
printf " %s_packets_in=%d %s_packets_out=%d %s_volume_in=%d %s_volume_out=%d" "$i" "$PAQUETS_RECUS" "$i" "$PAQUETS_EMIS" "$i" "$VOLUME_RECU" "$i" "$VOLUME_EMIS"
done
printf "\n"

127
nagios/check_nrpe_stunnel.sh Executable file
View file

@ -0,0 +1,127 @@
#!/bin/sh
# This script's purpose is to launch check_nrpe through stunnel
# in a transparent way
# - Nagios/Shinken launch "check_nrpe_stunnel -H host -c command"
# - this script looks up in a database (-d option) to redirect the
# call to the local port of the stunnel
#
# This script needs :
# - nrpe daemon on the remote host
# - stunnel in server mode on the remote host
# - stunnel in client mode on the local (nagios/shinken) host
# - a database (cf help message)
# TODO : copy stunnel conf.
# Default options
NAGIOS_CHECKNRPE="/usr/lib/nagios/plugins/check_nrpe"
DATABASE="/etc/nagios/check_nrpe_stunnel.lst"
# Par défaut, on arrête le script à la première erreur non "catchée"
set -e
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
. $PROGPATH/utils.sh
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 -d <database> -C <check_nrpe path> -H <host> -c <command> ... (cf. check_nrpe_options)
$0 -h
-h : ce message d'aide
Database is a text file with two fields, separated by tabulation. The first
is the local port for stunnel to join the host in the second field.
Example :
5678 bdd.localdomain
5679 proxmox.localdomain
5680 backup.localdomain
(for examples of how to setup stunnel, cf. comments in this script)
Default options :
Database : $DATABASE
check_nrpe path : $NAGIOS_CHECKNRPE
EOF
}
# Début du code
# gestion des options de lancement
while getopts d:H:c:C:nut:a: f; do
case $f in
'a')
NAGIOS_ARGLIST="$OPTARG"
;;
'c')
NAGIOS_COMMAND="$OPTARG"
;;
'C')
NAGIOS_CHECKNRPE="$OPTARG"
;;
'd')
DATABASE="$OPTARG"
;;
'H')
NAGIOS_HOST="$OPTARG"
;;
'n')
NAGIOS_NOSSL="1"
;;
't')
NAGIOS_TIMEOUT="$OPTARG"
;;
'u')
NAGIOS_SOCKET_NONCRITICALTIMEOUT="1"
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
#(code inutile, mais que je garde parce qu'on ne sait jamais)
#shift $( expr $OPTIND - 1 )
#DATA="$1"
# Petite vérif.
if [ ! -f "$DATABASE" ]; then
echo "UNKNOWN: no check_nrpe_stunnel database found."
exit $STATE_UNKNOWN
fi
if [ ! -f "$NAGIOS_CHECKNRPE" ]; then
echo "UNKNOWN: check_nrpe_stunnel could not find check_nrpe (-C flag)."
exit $STATE_UNKNOWN
fi
# Lookup for host in database
STUNNEL_PORT="$( grep -w "^$NAGIOS_HOST" "$DATABASE" | head -n 1 | cut -f 2 )"
if [ -z "$STUNNEL_PORT" ]; then
echo "UNKNOWN: Host not found in check_nrpe_stunnel database."
exit $STATE_UNKNOWN
fi
$NAGIOS_CHECKNRPE -H localhost -p $STUNNEL_PORT \
$( test -n "$NAGIOS_SOCKET_NONCRITICALTIMEOUT" && printf "%s" "-u" ) \
$( test -n "$NAGIOS_NOSSL" && printf "%s" "-n" ) \
$( test -n "$NAGIOS_TIMEOUT" && printf "%s %d" "-t" "$NAGIOS_TIMEOUT" ) \
$( test -n "$NAGIOS_COMMAND" && printf "%s %s" "-c" "$NAGIOS_COMMAND" ) \
$( test -n "$NAGIOS_ARGLIST" && printf "%s %s" "-a" "$NAGIOS_ARGLIST" )

49
nagios/check_postfix_log.sh Executable file
View file

@ -0,0 +1,49 @@
#!/bin/sh
TMPFILE=$( mktemp )
LOG=/var/log/mail.log
LOGTAIL_OFFSET=/tmp/logtail-maillog.offset
EXIT_STATUS=0
# Alors, petite explication
# Dans l'ordre :
# - logtail sur le log de Postfix
# - pflogsumm pour avoir les stats
# - head : on ne prend que les premières stats
# - sed (.*) : on dégage tout ce qui est entre parenthèse (les labels
# doivent être identiques d'un appel à l'autre, les valeurs contenues
# dans les parenthèses ne me semblent pas essentielles)
# - sed -n : on convertit l'affichage de 12345 label en 'label'=12345
# - sed smtpd : on rajoute le préfixe 'smtpd' aux labels idoines
# - sed km/000 : on multiplie par 1000/1000000 les valeurs suffixées par k ou m
# - tr : on met tout sur une même ligne
/usr/sbin/logtail2 -f "$LOG" -o "$LOGTAIL_OFFSET" \
| /usr/sbin/pflogsumm -h 0 -u 0 --smtpd_stats --zero_fill \
| head -n 30 \
| sed 's/[[:space:]]*(.*).*//g' \
| sed -n "s/^[[:space:]]*\([0-9]\+[kmg]\?\)[[:space:]]\+\([a-z \/\.]\+\).*/\'\2\'=\1/p" \
| sed 's/\(connections\|hosts\/domains\|avg. connect time\)/smtpd \1/' \
| sed -e 's/k$/000/' -e 's/m$/000000/' \
| tr "\n" " " \
>"$TMPFILE"
# Si la commande précédente s'est bien passée, on
# renvoie OK et les perfdata générées plus haut
if [ "$?" -eq "0" ] && [ -s "$TMPFILE" ]; then
printf "OK | "
cat "$TMPFILE"
printf "\n"
else
# Sinon, KO et warning (pas "critical", on est là juste pour des stats)
printf "KO \n"
EXIT_STATUS=1
fi
# Nettoyage du fichier temporaire
rm -f "$TMPFILE"
# Petit accès de parano
chmod 600 "$LOGTAIL_OFFSET"
exit $EXIT_STATUS

View file

@ -0,0 +1,96 @@
#!/bin/sh
COMMANDE="/usr/sbin/postqueue -p"
WARNING_REQ=10
CRITICAL_REQ=15
WARNING_SIZE=20000000
CRITICAL_SIZE=50000000
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 -h
$0 [-w warning_nb_mails] [-c critical_nb_mails] [-W warning_size] [-C critical_size]
Valeurs par défaut:
warning_nb_mails: $WARNING_REQ
critical_nb_mails: $CRITICAL_REQ
warning_size: $WARNING_SIZE
critical_size: $CRITICAL_SIZE
EOF
}
#
# Gestion des paramètres
#
while getopts hw:c:W:C: f; do
case "$f" in
'h')
usage
exit
;;
'w')
WARNING_REQ="$OPTARG"
;;
'c')
CRITICAL_REQ="$OPTARG"
;;
'W')
WARNING_SIZE="$OPTARG"
;;
'C')
CRITICAL_SIZE="$OPTARG"
;;
\?)
usage
exit 1
;;
esac
done
#
# Lancement de la commande
#
RESULT=$( $COMMANDE | grep -v "^[^A-Za-z0-9]" | grep -v "^[[:space:]]" | awk '{ print $2 }' )
# ... et analyse de la sortie
if [ "x$RESULT" = "xqueue" ]; then
# Queue vide
REQ=0
SIZE=0
else
REQ=$( echo "$RESULT" | wc -w )
SIZE=0
for i in $RESULT; do
SIZE=$(( $SIZE + $i ))
done
fi
#
# Analyse des résultats
#
if [ "$SIZE" -ge "$CRITICAL_SIZE" ] || [ "$REQ" -ge "$CRITICAL_REQ" ]; then
RETURN_STATUS=2
printf "CRITICAL"
else
if [ "$SIZE" -ge "$WARNING_SIZE" ] || [ "$REQ" -ge "$WARNING_REQ" ]; then
RETURN_STATUS=1
printf "WARNING"
else
RETURN_STATUS=0
printf "OK"
fi
fi
printf " | req=%d;%d;%d size=%dB;%d;%d\n" "$REQ" "$WARNING_REQ" "$CRITICAL_REQ" "$SIZE" "$WARNING_SIZE" "$CRITICAL_SIZE"
exit $RETURN_STATUS

92
nagios/check_pve_mem.pl Executable file
View file

@ -0,0 +1,92 @@
#!/usr/bin/perl -w
use strict;
use Socket;
use PVE::Utils;
use PVE::Cluster;
use PVE::ConfigServer;
use PVE::AtomicFile;
use Getopt::Std;
use File::Path;
use Data::Dumper;
my $cinfo = PVE::Cluster::clusterinfo();
my $secret = PVE::Utils::load_auth_secret();
my $ticket = PVE::Utils::create_auth_ticket ($secret, 'root', 'root');
my @listcrit;
my @listwarn;
my @listok;
my $warning_level = 80;
my $critical_level = 90;
my $return_status = 0;
my @listperfdata;
# Récupération des options
my %options=();
getopts("hw:c:", \%options);
if (defined $options{h}) {
print STDERR "Ce script sert à vérifier l'occupation mémoire des machines hôtes du cluster Proxmox.\n";
print STDERR "USAGE: script [-c critical_level] [-w warning_level]\n";
exit 0;
}
if (defined $options{w}) {
$warning_level = $options{w};
}
if (defined $options{c}) {
$critical_level = $options{c};
}
foreach my $ni (@{$cinfo->{nodes}}) {
my $state = '-';
my $conn;
my $status;
my $mem;
eval {
$conn = PVE::ConfigClient::connect ($ticket, $cinfo, $ni->{cid});
if ($status = $conn->ping()->result) {
$state = 'A';
}
};
my $err = $@;
if ($err) {
push(@listcrit, $ni->{cid} . ":(down)");
} else {
$mem = int (0.5 + ($status->{meminfo}->{mbmemused}*100/$status->{meminfo}->{mbmemtotal}));
if ($mem >= $critical_level) {
push(@listcrit, $ni->{cid} . ":$mem");
} else {
if ($mem >= $warning_level) {
push(@listwarn, $ni->{cid} . ":$mem");
} else {
push(@listok, $ni->{cid} . ":$mem");
}
}
push (@listperfdata, $ni->{name} . "_mem="
. $status->{meminfo}->{mbmemused} . "MB;"
. ($status->{meminfo}->{mbmemtotal} * $warning_level / 100) . ";"
. ($status->{meminfo}->{mbmemtotal} * $critical_level / 100) . ";"
. "0;"
. $status->{meminfo}->{mbmemtotal});
}
}
if (scalar(@listcrit) != 0) {
print "CRITICAL : " . join(", ", @listcrit);
$return_status = 2;
} else {
if (scalar(@listwarn) != 0) {
print "WARNING : " . join(", ", @listwarn);
$return_status = 1;
} else {
print "OK : " . join(", ", @listok);
}
}
print "|" . join(" ", @listperfdata) . "\n";
exit $return_status;

View file

@ -0,0 +1,48 @@
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define COMMAND_MAX_LENGTH 256
int main(int argc, char **argv) {
int warning_level = 80;
int critical_level = 90;
int help_needed = 0;
int opt;
char command[COMMAND_MAX_LENGTH];
/* Vérification du setuid root */
if (setuid(0) != 0) {
perror("setuid root error.\n");
exit(EXIT_FAILURE);
}
/* Analyse des options */
while ((opt = getopt (argc, argv, "w:c:")) != -1) {
switch (opt)
{
case 'w':
warning_level = atoi(optarg);
break;
case 'c':
critical_level = atoi(optarg);
break;
case '?':
help_needed = 1;
break;
}
}
/* Lancement de la commande */
if (help_needed != 0) {
return system("/usr/local/sbin/check_pve_mem.pl -h");
}
if ( ! snprintf(command, COMMAND_MAX_LENGTH, "/usr/local/sbin/check_pve_mem.pl -w %d -c %d", warning_level, critical_level)) {
exit(EXIT_FAILURE);
}
exit(WEXITSTATUS(system(command)));
}

90
nagios/check_quota.pl Normal file
View file

@ -0,0 +1,90 @@
#!/usr/bin/perl
# TODO : intégrer avec utils.pm / Nagios::Plugin du CPAN
# TODO : gérer les niveaux via des arguments en ligne de commande
use strict;
use Cyrus::IMAP::Shell;
use Data::Dumper;
my $configFile = '/root/.cyrus_auth';
my $configWarningLevel = 90;
my $configCriticalLevel = 97;
my %cyrusConfig;
my $var;
my $value;
my $username;
my $taux_occupation;
my @donnees;
my @quota;
my @listingMailboxes;
my @listingWarning;
my @listingCritical;
# Lecture du fichier de configuration
open CONFIG, $configFile;
while (<CONFIG>) {
chomp; # no newline
s/^#.*//; # no comments
s/^\s+//; # no leading white
s/\s+$//; # no trailing white
next unless length; # anything left?
my ($var, $value) = split(/\s*=\s*/, $_, 2);
$cyrusConfig{$var} = $value;
}
# Connexion
my $conn = Cyrus::IMAP::Admin->new($cyrusConfig{'host'}) or die "Echec de connexion.";
# Authentification
$conn->authenticate(
"-user" => $cyrusConfig{'user'},
"-password" => $cyrusConfig{'password'},
"-mechanism" => 'LOGIN',
) or die "Echec à l'authentification.";
# Boucle sur chacune des boîtes
@listingMailboxes = $conn->listmailbox('user.%');
if ($conn->error) {
print $conn->error;
exit;
}
for (@listingMailboxes) {
# listingMailboxes est un tableau multi-dimensionnel
# On essaie de récupérer proprement les tableaux internes
@donnees = @$_;
$username = @donnees[0];
# Récupération du quota
@quota = $conn->listquota($username);
# fin de boucle si vide (= pas de quota) (ou erreur peut-être...)
next unless @quota;
if (shift(@quota) != 'STORAGE') {
die "Type de quota inconnu.";
}
# Calcul du taux d'occupation et éventuel ajout aux
# listes warning/critical
$taux_occupation = 100 * $quota[0][0] / $quota[0][1];
if ( $taux_occupation > $configCriticalLevel ) {
push (@listingCritical, $username . sprintf("(%.4f)", $taux_occupation));
} else {
if ( $taux_occupation > $configWarningLevel ) {
push (@listingWarning, $username . sprintf("(%.4f)", $taux_occupation));
}
}
}
if (@listingCritical) {
print "CRITICAL : " . scalar(@listingCritical) . " au dessus de " . $configCriticalLevel . "% : " . join ", ", @listingCritical;
exit(2);
}
if (@listingWarning) {
print "WARNING : " . scalar(@listingWarning) . " au dessus de " . $configWarningLevel . "% : " . join ", ", @listingWarning;
exit(1);
}
print "OK";
exit(0);

618
nagios/check_rbl Normal file
View file

@ -0,0 +1,618 @@
#!perl
# nagios: -epn
package main;
# check_rbl is a Nagios plugin to check if an SMTP server is black- or
# white- listed
#
# See the INSTALL file for installation instructions
#
# Copyright (c) 2014, Matteo Corti <matteo@corti.li>
# Copyright (c) 2007, ETH Zurich.
# Copyright (c) 2010, Elan Ruusamae <glen@delfi.ee>.
#
# This module is free software; you can redistribute it and/or modify it
# under the terms of GNU general public license (gpl) version 3.
# See the LICENSE file for details.
use strict;
use warnings;
use 5.00800;
our $VERSION = '1.3.8';
use Data::Validate::Domain qw(is_hostname);
use Data::Validate::IP qw(is_ipv4 is_ipv6);
use IO::Select;
use Net::DNS;
use Readonly;
use English qw(-no_match_vars);
use Data::Dumper;
my $plugin_module = load_module( 'Monitoring::Plugin', 'Nagios::Plugin' );
my $plugin_threshold_module =
load_module( 'Monitoring::Plugin::Threshold', 'Nagios::Plugin::Threshold' );
my $plugin_getopt_module =
load_module( 'Monitoring::Plugin::Getopt', 'Nagios::Plugin::Getopt' );
# Check which version of the monitoring plugins is available
sub load_module {
my @names = @_;
my $loaded_module;
for my $name (@names) {
my $file = $name;
# requires need either a bare word or a file name
$file =~ s{::}{/}gsxm;
$file .= '.pm';
eval { ## no critic (ErrorHandling::RequireCheckingReturnValueOfEval)
require $file;
$name->import();
};
if ( !$EVAL_ERROR ) {
$loaded_module = $name;
last;
}
}
if ( !$loaded_module ) {
#<<<
print 'CHECK_RBL: plugin not found: ' . join( ', ', @names ) . "\n"; ## no critic (RequireCheckedSyscall)
#>>>
exit 2;
}
return $loaded_module;
}
Readonly our $DEFAULT_TIMEOUT => 15;
Readonly our $DEFAULT_RETRIES => 4;
Readonly our $DEFAULT_WORKERS => 20;
Readonly our $DEFAULT_QUERY_TIMEOUT => 15;
# IMPORTANT: Nagios plugins could be executed using embedded perl in this case
# the main routine would be executed as a subroutine and all the
# declared subroutines would therefore be inner subroutines
# This will cause all the global lexical variables not to stay shared
# in the subroutines!
#
# All variables are therefore declared as package variables...
#
## no critic (ProhibitPackageVars)
our ( @listed, @timeouts, $options, $plugin, $threshold, $timeouts_string, );
##############################################################################
# Usage : debug("some message string")
# Purpose : write a message if the debugging option was specified
# Returns : n/a
# Arguments : message : message string
# Throws : n/a
# Comments : n/a
# See also : n/a
sub debug {
# arguments
my $message = shift;
if ( !defined $message ) {
$plugin->nagios_exit( $plugin_module->UNKNOWN,
q{Internal error: not enough parameters for 'debug'} );
}
if ( $options && $options->debug() ) {
## no critic (RequireCheckedSyscall)
print "[DBG] $message\n";
}
return;
}
##############################################################################
# Usage : verbose("some message string", $optional_verbosity_level);
# Purpose : write a message if the verbosity level is high enough
# Returns : n/a
# Arguments : message : message string
# level : options verbosity level
# Throws : n/a
# Comments : n/a
# See also : n/a
sub verbose {
# arguments
my $message = shift;
my $level = shift;
if ( !defined $message ) {
$plugin->nagios_exit( $plugin_module->UNKNOWN,
q{Internal error: not enough parameters for 'verbose'} );
}
if ( !defined $level ) {
$level = 0;
}
if ( $level < $options->verbose ) {
if ( !print $message ) {
$plugin->nagios_exit( $plugin_module->UNKNOWN,
'Error: cannot write to STDOUT' );
}
}
return;
}
# the script is declared as a package so that it can be unit tested
# but it should not be used as a module
if ( !caller ) {
run();
}
##############################################################################
# Usage : my $res = init_dns_resolver( $retries )
# Purpose : Initializes a new DNS resolver
# Arguments : retries : number of retries
# Returns : The newly created resolver
# See also : Perl Net::DNS
sub init_dns_resolver {
my $retries = shift;
my $res = Net::DNS::Resolver->new();
if ( $res->can('force_v4') ) {
$res->force_v4(1);
}
if ($retries) {
$res->retry($retries);
}
return $res;
}
##############################################################################
# Usage : mdns(\@addresses, $callback)
# Purpose : Perform multiple DNS lookups in parallel
# Returns : n/a
# See also : Perl Net::DNS module mresolv in examples
#
# Resolves all IPs in C<@addresses> in parallel.
# If answer is found C<$callback> is called with arguments as: $name, $host.
#
# Author: Elan Ruusamae <glen@delfi.ee>, (c) 1999-2010
sub mdns {
my ( $data, $callback ) = @_;
# number of requests to have outstanding at any time
my $workers = $options ? $options->workers() : 1;
# timeout per query (seconds)
my $timeout = $options ? $options->get('query-timeout') : $DEFAULT_TIMEOUT;
my $res = init_dns_resolver( $options ? $options->retry() : 0 );
my $sel = IO::Select->new();
my $eof = 0;
my @addrs = @{$data};
my %addrs;
while (1) {
#----------------------------------------------------------------------
# Read names until we've filled our quota of outstanding requests.
#----------------------------------------------------------------------
while ( !$eof && $sel->count() < $workers ) {
my $name = shift @addrs;
if ( !defined $name ) {
debug('reading...EOF.');
$eof = 1;
last;
}
debug("reading...$name");
my $sock = $res->bgsend($name);
if ( !defined $sock ) {
verbose 'DNS query error: ' . $res->errorstring;
verbose "Skipping $name";
}
else {
# we store in a hash the query we made, as parsing it back from
# response gives different ip for ips with multiple hosts
$addrs{$sock} = $name;
$sel->add($sock);
debug( "name = $name, outstanding = " . $sel->count() );
}
}
#----------------------------------------------------------------------
# Wait for any replies. Remove any replies from the outstanding pool.
#----------------------------------------------------------------------
my @ready;
my $timed_out = 1;
debug('waiting for replies');
@ready = $sel->can_read($timeout);
while (@ready) {
$timed_out = 0;
debug( 'replies received: ' . scalar @ready );
foreach my $sock (@ready) {
debug('handling a reply');
my $addr = $addrs{$sock};
delete $addrs{$sock};
$sel->remove($sock);
my $ans = $res->bgread($sock);
debug Dumper $ans;
my $host;
if ($ans) {
foreach my $rr ( $ans->answer ) {
debug('Processing answer');
## no critic(ProhibitDeepNests)
if ( !( $rr->type eq 'A' ) ) {
next;
}
$host = $rr->address;
debug("host = $host");
# take just the first answer
last;
}
}
else {
debug( 'no answer: ' . $res->errorstring() );
}
if ( defined $host ) {
debug("callback( $addr, $host )");
}
else {
debug("callback( $addr, <undefined> )");
}
&{$callback}( $addr, $host );
}
@ready = $sel->can_read(0);
}
#----------------------------------------------------------------------
# If we timed out waiting for replies, remove all entries from the
# outstanding pool.
#----------------------------------------------------------------------
if ($timed_out) {
debug('timeout: clearing the outstanding pool.');
foreach my $sock ( $sel->handles() ) {
my $addr = $addrs{$sock};
delete $addrs{$sock};
$sel->remove($sock);
# callback for hosts that timed out
&{$callback}( $addr, q{} );
}
}
debug( 'outstanding = ' . $sel->count() . ", eof = $eof" );
#----------------------------------------------------------------------
# We're done if there are no outstanding queries and we've read EOF.
#----------------------------------------------------------------------
last if ( $sel->count() == 0 ) && $eof;
}
return;
}
##############################################################################
# Usage : validate( $hostname );
# Purpose : check if an IP address or host name is valid
# Returns : the IP address corresponding to $hostname
# Arguments : n/a
# Throws : an UNKNOWN error if the argument is not valid
# Comments : n/a
# See also : n/a
sub validate {
my $hostname = shift;
my $ip = $hostname;
debug("validate($hostname, $ip)");
if ( !is_ipv4($hostname) && !is_ipv6($hostname) ) {
if ( is_hostname($hostname) ) {
mdns(
[$hostname],
sub {
my ( $addr, $host ) = @_;
$ip = $host;
}
);
if ( !$ip ) {
$plugin->nagios_exit( $plugin_module->UNKNOWN,
'Cannot resolve ' . $hostname );
}
}
if ( !$ip ) {
$plugin->nagios_exit( $plugin_module->UNKNOWN,
'Cannot resolve ' . $options->host );
}
}
return $ip;
}
##############################################################################
# Usage : run();
# Purpose : main method
# Returns : n/a
# Arguments : n/a
# Throws : n/a
# Comments : n/a
# See also : n/a
sub run {
################################################################################
# Initialization
$plugin = $plugin_module->new( shortname => 'CHECK_RBL' );
my $time = time;
########################
# Command line arguments
$options = $plugin_getopt_module->new(
usage => 'Usage: %s [OPTIONS]',
version => $VERSION,
url => 'http://matteocorti.github.io/check_rbl/',
blurb => 'Check SMTP black- or white- listing status',
);
$options->arg(
spec => 'critical|c=i',
help => 'Number of blacklisting servers for a critical warning',
required => 0,
default => 1,
);
$options->arg(
spec => 'warning|w=i',
help => 'Number of blacklisting servers for a warning',
required => 0,
default => 1,
);
$options->arg(
spec => 'debug|d',
help => 'Prints debugging information',
required => 0,
default => 0,
);
$options->arg(
spec => 'server|s=s@',
help => 'RBL server',
required => 1,
);
$options->arg(
spec => 'host|H=s',
help => 'SMTP server to check',
required => 1,
);
$options->arg(
spec => 'retry|r=i',
help => 'Number of times to try a DNS query (default is 4) ',
required => 0,
default => $DEFAULT_RETRIES,
);
$options->arg(
spec => 'workers=i',
help => 'Number of parallel checks',
required => 0,
default => $DEFAULT_WORKERS,
);
$options->arg(
spec => 'whitelistings|wl',
help => 'Check whitelistings instead of blacklistings',
required => 0,
default => 0,
);
$options->arg(
spec => 'query-timeout=i',
help => 'Timeout of the RBL queries',
required => 0,
default => $DEFAULT_QUERY_TIMEOUT,
);
$options->getopts();
###############
# Sanity checks
if ( $options->critical < $options->warning ) {
$plugin->nagios_exit( $plugin_module->UNKNOWN,
'critical has to be greater or equal warning' );
}
my $ip = validate( $options->host );
my @servers = @{ $options->server };
verbose 'Using ' . $options->timeout . " as global script timeout\n";
alarm $options->timeout;
################
# Set the limits
# see https://nagios-plugins.org/doc/guidelines.html#THRESHOLDFORMAT
$threshold = $plugin_threshold_module->set_thresholds(
warning => $options->warning - 1,
critical => $options->critical - 1,
);
################################################################################
my $nservers = scalar @servers;
verbose 'Checking ' . $options->host . " ($ip) on $nservers server(s)\n";
# build address lists
my @addrs;
foreach my $server (@servers) {
( my $local_ip = $ip ) =~
s/(\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3}) [.] (\d{1,3})/$4.$3.$2.$1.$server/mxs;
push @addrs, $local_ip;
}
mdns(
\@addrs,
sub {
my ( $addr, $host ) = @_;
if ( defined $host ) {
debug("callback( $addr, $host )");
}
else {
debug("callback( $addr, <undefined> )");
}
# extract RBL we checked
$addr =~ s/^(?:\d+[.]){4}//mxs;
if ( defined $host ) {
if ( $host eq q{} ) {
push @timeouts, $addr;
}
else {
verbose "listed in $addr as $host\n";
if ( !$options->get('whitelistings') ) {
push @listed, $addr;
}
}
}
else {
verbose "not listed in $addr\n";
if ( $options->get('whitelistings') ) {
push @listed, $addr;
}
}
}
);
my $total = scalar @listed;
my $status;
if ( $options->get('whitelistings') ) {
$status =
$options->host
. " NOT WHITELISTED on $total "
. ( ( $total == 1 ) ? 'server' : 'servers' )
. " of $nservers";
}
else {
$status =
$options->host
. " BLACKLISTED on $total "
. ( ( $total == 1 ) ? 'server' : 'servers' )
. " of $nservers";
}
# append timeout info, but do not account these in status
if (@timeouts) {
$timeouts_string = scalar @timeouts;
$status =
" ($timeouts_string server"
. ( ( $timeouts_string > 1 ) ? 's' : q{} )
. ' timed out: '
. join( ', ', @timeouts ) . ')';
}
if ( $total > 0 ) {
$status .= " (@listed)";
}
$plugin->add_perfdata(
label => 'servers',
value => $total,
uom => q{},
threshold => $threshold,
);
$plugin->add_perfdata(
label => 'time',
value => time - $time,
uom => q{s},
);
$plugin->nagios_exit( $threshold->get_status($total), $status );
return;
}
1;

View file

@ -0,0 +1,50 @@
#!/bin/sh
# Little Nagios check to save the Sendmail mailstats
CATEGORY=""
OUTPUT_PERFDATA=""
# Stop at the first non-catched error
set -e
# We use the "idiom" suggested by http://www.etalabs.net/sh_tricks.html
# based on "done <<EOF $( command ) EOF"
# to circumvent the creation of a subshell for the while loop
# where the $OUTPUT_PERFDATA variable would be local and we
# would not get the value outside of the loop.
while read LINETYPE msgsfr bytes_from msgsto bytes_to msgsrej msgsdis msgsqur mailer; do
case "$LINETYPE" in
"MSP"|"MTA")
CATEGORY="$LINETYPE"
;;
"T")
# for the sake of Icinga, print the elements in alphabetical order
for i in msgsfr bytes_from msgsto bytes_to msgsrej msgsdis msgsqur mailer; do
OUTPUT_PERFDATA="$( printf "%s %s_msg_%s=%d" "$OUTPUT_PERFDATA" "$CATEGORY" "$i" "$( eval echo "$"$i )" )"
done
;;
"C")
# for the sake of Icinga, print the elements in alphabetical order
for i in msgsfr bytes_from; do
OUTPUT_PERFDATA="$( printf "%s %s_tcp_%s=%d" "$OUTPUT_PERFDATA" "$CATEGORY" "$i" "$( eval echo "$"$i )" )"
done
;;
esac
done <<EOF
$(mailstats -P 2>/dev/null)
EOF
# if not all data is present, we return an unknown status
# (remove this block if you don't mind)
if ! echo "$OUTPUT_PERFDATA" | grep "MTA_msg" >/dev/null 2>&1 \
|| ! echo "$OUTPUT_PERFDATA" | grep "MSP_msg" >/dev/null 2>&1 \
|| ! echo "$OUTPUT_PERFDATA" | grep "MTA_tcp" >/dev/null 2>&1 \
|| ! echo "$OUTPUT_PERFDATA" | grep "MSP_tcp" >/dev/null 2>&1 \
; then
echo "UNKNOWN"
exit 3
fi
# Remove starting space to OUTPUT_PERFDATA
printf "OK|%s\n" "$( echo $OUTPUT_PERFDATA )"

96
nagios/check_sendmail_queue.sh Executable file
View file

@ -0,0 +1,96 @@
#!/bin/sh
# Replace COMMANDE by mailq if you want both new and old messages listed
COMMANDE="/usr/sbin/sendmail -bp"
WARNING_REQ=10
CRITICAL_REQ=15
WARNING_SIZE=20000000
CRITICAL_SIZE=50000000
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 -h
$0 [-w warning_nb_mails] [-c critical_nb_mails] [-W warning_size] [-C critical_size]
Valeurs par défaut:
warning_nb_mails: $WARNING_REQ
critical_nb_mails: $CRITICAL_REQ
warning_size: $WARNING_SIZE
critical_size: $CRITICAL_SIZE
EOF
}
#
# Gestion des paramètres
#
while getopts hw:c:W:C: f; do
case "$f" in
'h')
usage
exit
;;
'w')
WARNING_REQ="$OPTARG"
;;
'c')
CRITICAL_REQ="$OPTARG"
;;
'W')
WARNING_SIZE="$OPTARG"
;;
'C')
CRITICAL_SIZE="$OPTARG"
;;
\?)
usage
exit 1
;;
esac
done
#
# Lancement de la commande
#
RESULT=$( $COMMANDE | grep -v "^[^A-Za-z0-9]" | grep -v "^[[:space:]]" | awk '{ print $2 }' )
# ... et analyse de la sortie
if [ -z "$RESULT" ]; then
# Queue vide
REQ=0
SIZE=0
else
REQ=$( echo "$RESULT" | wc -w )
SIZE=0
for i in $RESULT; do
SIZE=$(( $SIZE + $i ))
done
fi
#
# Analyse des résultats
#
if [ "$SIZE" -ge "$CRITICAL_SIZE" ] || [ "$REQ" -ge "$CRITICAL_REQ" ]; then
RETURN_STATUS=2
printf "CRITICAL ($REQ mail(s) in queue for $SIZE byte(s))"
else
if [ "$SIZE" -ge "$WARNING_SIZE" ] || [ "$REQ" -ge "$WARNING_REQ" ]; then
RETURN_STATUS=1
printf "WARNING ($REQ mail(s) in queue for $SIZE byte(s))"
else
RETURN_STATUS=0
printf "OK ($REQ mail(s) in queue for $SIZE byte(s))"
fi
fi
printf " | req=%d;%d;%d size=%dB;%d;%d\n" "$REQ" "$WARNING_REQ" "$CRITICAL_REQ" "$SIZE" "$WARNING_SIZE" "$CRITICAL_SIZE"
exit $RETURN_STATUS

152
nagios/check_shinken-config.sh Executable file
View file

@ -0,0 +1,152 @@
#!/bin/sh
# Petit script custom pour vérifier le nombre de checks
# dans chaque catégorie
# GPL v3+
# Stop at the first non-catched error
set -e
# Include check_range() and STATE_* vars
. "$( dirname "$0" )/utils.sh"
# Default values
RANGE_WARNING="1:50"
RANGE_CRITICAL="1:100"
# Output
OUTPUT_EXIT_STATUS=$STATE_OK
OUTPUT_DETAIL_WARNING=""
OUTPUT_DETAIL_CRITICAL=""
OUTPUT_PERFDATA=""
# Some preconf.
# TODO: check to make it available as an command line option, if
# not security-ill
SHINKEN_ARBITER_PATH="/usr/local/bin/shinken-arbiter"
SHINKEN_ARBITER_OPTIONS="-v -c /etc/shinken/nagios.cfg -c /etc/shinken/shinken-specific.cfg"
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 [-w warning_range] [-c critical_range] -t category/type [[-w...] -t type] ...
Note: Since the category/type is checked against the lastest ranges given, order
of the arguments are important.
Default values:
warning_range: $RANGE_WARNING
critical_range: $RANGE_CRITICAL
EOF
}
check_range_syntax() {
check_range 0 "$1" >/dev/null 2>&1
if [ "$?" -eq "2" ]; then
return 1
fi
return 0
}
# Some early checks
if ! which $SHINKEN_ARBITER_PATH >/dev/null 2>&1 ; then
echo "UNKNOWN 'shinken-arbiter' not found in path : $PATH"
exit $STATE_UNKNOWN
fi
# Get the values
LISTING="$( $SHINKEN_ARBITER_PATH $SHINKEN_ARBITER_OPTIONS | sed -n 's/^\tChecked \([0-9]\+\) \(.\+\)$/\2\t\1/p' | sort )"
OUTPUT_PERFDATA_REMNANT="$( printf "%s" "$LISTING" | sed 's/\t/=/g' | tr '\n' ' ' )"
#
# Gestion des paramètres
#
while getopts hw:c:t: f; do
case "$f" in
'h')
usage
exit
;;
'w')
if check_range_syntax "$OPTARG" >/dev/null; then
RANGE_WARNING="$OPTARG"
else
echo "UNKNOWN: invalid range."
exit 3
fi
;;
'c')
if check_range_syntax "$OPTARG" >/dev/null; then
RANGE_CRITICAL="$OPTARG"
else
echo "UNKNOWN: invalid range."
exit 3
fi
;;
't')
# Ce n'est pas très propre, mais on gère tout ici plutôt que de remplir
# un buffer et de le traiter ensuite
CATEGORY="$OPTARG"
CPT=$( printf "%s\n" "$LISTING" | grep -- "^$CATEGORY[[:space:]]" | cut -f 2 )
if [ -z "$CPT" ]; then
printf "UNKNOWN: unknown category $CATEGORY"
exit $STATE_UNKNOWN
fi
# mémo : 'label'=value[UOM];[warn];[crit];[min];[max]
OUTPUT_PERFDATA=$( printf "%s'%s'=%d;%s;%s;0;" \
"$( test -n "$OUTPUT_PERFDATA" && echo "$OUTPUT_PERFDATA " )" \
"$CATEGORY" \
"$CPT" \
"$RANGE_WARNING" \
"$RANGE_CRITICAL" )
# We remove the item from the "remnants"
OUTPUT_PERFDATA_REMNANT="$( printf "%s" "$OUTPUT_PERFDATA_REMNANT" | sed "s/[[:space:]]*$CATEGORY=[0-9]\+[[:space:]]*/ /" )"
# Check value against critical & warning range
if check_range "$CPT" "$RANGE_CRITICAL"; then
OUTPUT_EXIT_STATUS=$STATE_CRITICAL
OUTPUT_DETAIL_CRITICAL="$OUTPUT_DETAIL_CRITICAL Cat:$CATEGORY($CPT)"
elif check_range "$CPT" "$RANGE_WARNING"; then
if [ "$OUTPUT_EXIT_STATUS" -eq 0 ]; then
OUTPUT_EXIT_STATUS=$STATE_WARNING
fi
OUTPUT_DETAIL_WARNING="$OUTPUT_DETAIL_WARNING Cat:$CATEGORY($CPT)"
fi
;;
\?)
usage
exit 1
;;
esac
done
OUTPUT_PERFDATA="$OUTPUT_PERFDATA $OUTPUT_PERFDATA_REMNANT"
case "$OUTPUT_EXIT_STATUS" in
"$STATE_OK")
printf "OK"
;;
"$STATE_WARNING")
printf "WARNING %s" "$OUTPUT_DETAIL_WARNING"
;;
"$STATE_CRITICAL")
printf "CRITICAL %s" "$OUTPUT_DETAIL_CRITICAL"
;;
*)
printf "UNKNOWN"
;;
esac
printf "|%s\n" "$OUTPUT_PERFDATA"
# on supprime les retours à la ligne
exit $OUTPUT_EXIT_STATUS

99
nagios/check_temperature.sh Executable file
View file

@ -0,0 +1,99 @@
#!/bin/sh
# Petit script custom pour relever la température des processeurs
#
# On ne tient pas compte des indications "high" et "critical" émises
# par sensors, mais seulement de celles fournies en argument comme
# seuils "warning" et "critical"
# Config par défaut
WARNING_LEVEL=60
CRITICAL_LEVEL=80
#
# Fonction d'aide
#
usage() {
cat <<EOF
Usage :
$0 -h
$0 [-w warning_temp] [-c critical_temp]
Valeurs par défaut:
warning_temp: $WARNING_LEVEL
critical_temp: $CRITICAL_LEVEL
EOF
}
#
# Gestion des paramètres
#
while getopts hw:c:e: f; do
case "$f" in
'h')
usage
exit
;;
'e')
EXCLUSIONS="$( printf "%s\n%s" "$EXCLUSIONS" "$OPTARG" )"
;;
'w')
WARNING_LEVEL=$( printf "%d" "$OPTARG" )
;;
'c')
CRITICAL_LEVEL=$( printf "%d" "$OPTARG" )
;;
\?)
usage
exit 1
;;
esac
done
if ! which sensors >/dev/null 2>&1 ; then
echo "UNKNOWN 'sensors' not found."
exit 1
fi
# On lance la commande
# note: on tronque les flottants en entiers.
DATA=$( /usr/bin/sensors | sed -n "s/^\([A-Za-z0-9 ]\+\):[[:space:]]*+\?\(-\?[0-9]\+\)\.\?[0-9]*.C.*$/'\1'=\2;$WARNING_LEVEL;$CRITICAL_LEVEL;;/p" )
if [ -z "$DATA" ]; then
echo "UNKNOWN no cpu found"
exit 3
fi
# ...puis on lance la boucle principale
# note: en faisant sensors | while read line; on entre dans un sous-process shell et les variables deviennent donc locales.
# D'où cette bidouille à base de redirection.
# TODO: tester la portabilité (bash ok, dash ok)
RETURN_STATUS=0
RETURN_COMMENT="OK"
while read LINE; do
TEMPERATURE="$( echo $LINE | sed 's/.*=\([0-9]\+\);.*/\1/' )"
SONDE="$( echo $LINE | sed "s/^'\([^']\+\)'=.*/\1/" )"
# Si la température est au-dessus du niveau critique, et que la sonde
# ne fait pas partie des exclues => alerte
if [ "$TEMPERATURE" -gt "$CRITICAL_LEVEL" ] && ! echo "$EXCLUSIONS" | grep "^$SONDE$" >/dev/null; then
RETURN_STATUS=2
RETURN_COMMENT="CRITICAL"
fi
# Idem ci-dessus + vérification que l'on n'a pas déjà levé une alerte
# (si c'était le cas, on serait déjà en Warning, voir en critical)
if [ "$TEMPERATURE" -gt "$WARNING_LEVEL" ] && [ "$RETURN_STATUS" -eq "0" ] && ! echo "$EXCLUSIONS" | grep "^$SONDE$" >/dev/null; then
RETURN_STATUS=1
RETURN_COMMENT="WARNING"
fi
done <<EOF
$DATA
EOF
# on supprime les retours à la ligne
RETURN_PERF_DATA=$( echo $DATA )
printf "$RETURN_COMMENT %s|%s\n" "$( echo $DATA | sed 's/;[^ ]*/C/g' )" "$RETURN_PERF_DATA"
exit $RETURN_STATUS

220
nagios/check_whois Executable file
View file

@ -0,0 +1,220 @@
#!/bin/sh
# Little custom script to check expiration date of DNS domains
# Default values
#RANGE_WARNING=":2592000" # 30 days
#RANGE_CRITICAL=":691200" # 8 days
THRESHOLD_WARNING="2592000" # 30 days
THRESHOLD_CRITICAL="691200" # 8 days
TIMESTAMP_NOW="$( date +%s )"
# Output
OUTPUT_EXIT_STATUS=0
OUTPUT_DETAIL_WARNING=""
OUTPUT_DETAIL_CRITICAL=""
OUTPUT_PERFDATA=""
PROGPATH=$( echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,' )
REVISION="0.1"
# Stop at the first non-catched error
set -e
# Include check_range()
. $PROGPATH/utils.sh
# No real need for this script but then you have to copy the status
# Not really clean...
#STATE_OK=0
#STATE_WARNING=1
#STATE_CRITICAL=2
#STATE_UNKNOWN=3
#STATE_DEPENDENT=4
#
# Help function
#
usage() {
cat <<EOF
Usage :
$0 [-w warning_threshold] [-c critical_threshold] domain1 [domain2] ...
Thresholds in seconds (or suffix by 'd' for days).
Default values:
warning_threshold : $THRESHOLD_WARNING
critical_threshold : $THRESHOLD_CRITICAL
EOF
}
#
# Days/Months/Hours to seconds convert function
#
convert_to_seconds() {
local UNIT VALUE
UNIT="$( echo "$1" | sed 's/.*\(.\)$/\1/;q' )"
VALUE="$( echo "$1" | sed 's/^\([0-9]*\).*/\1/;q' )"
case "$UNIT" in
'd')
# Unit is days
echo "$( expr "$VALUE" \* 86400 )"
;;
*)
echo "$1"
;;
esac
}
#
# Check if arg is an integer
# (copied from jilles @ http://stackoverflow.com/questions/806906/how-do-i-test-if-a-variable-is-a-number-in-bash )
#
is_int() {
case "$1" in
''|*[!0-9]*) return 1;;
*) return 0;;
esac
}
#
# Extract the date fs a timestamp rom the whois response
# (print 1 line max.)
# (exit status = 1 if problem)
#
extract_date_from_whois() {
local EXPIRATION_DATE
# We put each format on a dedicated line
# + filter only the first line
# Note : some sed implementations seems not to offer
# case-insensitive option, so we try not to
# use it for the moment.
# 1 - kernel.org, icann.org
# 2 - coca-cola.com (we try to ignore lines starting with space...)
# 3 - facebook.com (...but if there's nothing else, take it anyway)
# 4 - some databases with simple display (.se, .si)
# Add your own filter here :)
EXPIRATION_DATE="$( sed -n \
-e 's/^Registry Expiry Date: //p' \
-e '/^[^[:space:]]/s/.*\(xpiry\|xpiration\) Date: //p' \
-e 's/.*\(xpiry\|xpiration\) Date: //p' \
-e 's/^expires\?:[[:space:]]*//p' \
| sed 'q'
)"
# Small protection against injection
if [ -n "$( echo "$EXPIRATION_DATE" | LANG=C sed -n '/^[a-zA-Z0-9:\-]\{1,64\}$/p' )" ]; then
date --date="$EXPIRATION_DATE" +%s
fi
}
# Some early checks
if ! which whois >/dev/null 2>&1; then
echo "UNKNOWN: 'whois' command not found."
exit 1
fi
#
# Parameters management
#
while [ "$#" -gt 0 ]; do
while getopts hw:c: f; do
case "$f" in
'h')
usage
exit
;;
'w')
THRESHOLD_WARNING="$( convert_to_seconds "$OPTARG" )"
;;
'c')
THRESHOLD_CRITICAL="$( convert_to_seconds "$OPTARG" )"
;;
\?)
usage
exit 1
;;
esac
done
shift $( expr $OPTIND - 1 )
# Little checks
if ! is_int "$THRESHOLD_WARNING" || ! is_int "$THRESHOLD_CRITICAL"; then
echo "UNKNOWN invalid parameter : one of the threshold is not an integer."
exit $STATE_UNKNOWN
fi
# End of the options, we get the domain name
if [ -z "$1" ]; then
# No more options but no domain specified ? Weird but well...
break
fi
DOMAIN="$1"
shift
# get the expiration date for this domain
EXPIRATION_DATE="$( whois "$DOMAIN" | extract_date_from_whois )"
if [ -z "$EXPIRATION_DATE" ]; then
# We couldn't get the date :-(
if [ "$OUTPUT_EXIT_STATUS" -eq "$STATE_OK" ]; then
OUTPUT_EXIT_STATUS="$STATE_UNKNOWN"
fi
OUTPUT_DETAIL_UNKNOWN="$OUTPUT_DETAIL_UNKNOWN $DOMAIN:could_not_get_date"
break
fi
# Dispatch in the OK/Warning/Critical boxes
OUTPUT_DOMAIN_DETAIL="$DOMAIN:$( date --date=@$EXPIRATION_DATE +%FT%T%z)"
if [ "$EXPIRATION_DATE" -le "$( expr "$TIMESTAMP_NOW" + "$THRESHOLD_CRITICAL" )" ]; then
# Domain is critical
OUTPUT_EXIT_STATUS="$STATE_CRITICAL"
OUTPUT_DETAIL_CRITICAL="$OUTPUT_DETAIL_CRITICAL $OUTPUT_DOMAIN_DETAIL"
elif [ "$EXPIRATION_DATE" -le "$( expr "$TIMESTAMP_NOW" + "$THRESHOLD_WARNING" )" ]; then
# Domain is warning
OUTPUT_DETAIL_WARNING="$OUTPUT_DETAIL_WARNING $OUTPUT_DOMAIN_DETAIL"
# we don't change if the status is already Critical
# (but we take precedence over Unknown)
if [ "$OUTPUT_EXIT_STATUS" -ne "$STATE_CRITICAL" ]; then
OUTPUT_EXIT_STATUS="$STATE_WARNING"
fi
else
# Domain is Ok
OUTPUT_DETAIL_OK="$OUTPUT_DETAIL_OK $OUTPUT_DOMAIN_DETAIL"
fi
done
# final output
case "$OUTPUT_EXIT_STATUS" in
"$STATE_OK")
echo "OK $OUTPUT_DETAIL_OK"
;;
"$STATE_WARNING")
echo "WARNING $OUTPUT_DETAIL_WARNING"
;;
"$STATE_CRITICAL")
echo "CRITICAL $OUTPUT_DETAIL_CRITICAL"
;;
"$STATE_UNKNOWN")
echo "UNKNOWN $OUTPUT_DETAIL_UNKNOWN"
;;
*)
echo "WTF"
;;
esac
exit "$OUTPUT_EXIT_STATUS"

244
nagios/check_wp.php Normal file
View file

@ -0,0 +1,244 @@
#!/usr/bin/php
<?php
###################################
#
# WordPress Updates Monitoring
# written by Martin Scharm
# see http://binfalse.de
#
###################################
define ("OOK", 0);
define ("WRN", 1);
define ("ERR", 2);
define ("MMH", 3);
$instdir = null;
$domain = null;
$website = null;
$check_core = true;
$check_plugins = true;
$check_themes = true;
$verify_cert = true;
$help = false;
$version = "1.0";
$err = array ();
$perfdata = array();
for ($i = 1; $i < count ($argv); $i++)
{
switch ($argv[$i])
{
case "--domain":
$domain = $argv[++$i];
break;
case "--dir":
$instdir = $argv[++$i];
break;
case "--web":
$website = $argv[++$i];
break;
case "--no-core":
$check_core = false;
break;
case "--no-plugins":
$check_plugins = false;
break;
case "--no-theme":
$check_themes = false;
break;
case "--insec-cert":
$verify_cert = false;
break;
default:
$err[] = $argv[$i]."?";
$help = true;
}
}
if (!$website && !$instdir)
$err[] = "no installation directory and no website, don't know what to check...";
if (!$check_core && !$check_plugins && !$check_themes)
$err[] = "--no-core and --no-plugins and --no-theme? you must be kidding...";
if ($instdir && !file_exists ($instdir.'/wp-load.php'))
$err[] = "your installation is way to old or your installation path isn't correct...";
if ($help || count ($err))
{
if (count ($err))
echo implode (" | ", $err)."\n";
echo "Okay, let me help you... btw. this is version $version\n";
echo "Valid arguments:\n";
echo "\t--domain DOMAIN\tcheck for DOMAIN (required for multidomain installations)\n";
echo "\t--dir DIRECTORY\twordpress installation directory can be found in DIRECTORY\n";
echo "\t--web WEBSITE\tcheck _only_ the website WEBSITE (will just check the core version for updates, based on meta name generator)\n";
echo "\t--insec-cert\tdon't verify SSL cert (in combination with --web)\n";
echo "\t--no-core\tdon't check the core\n";
echo "\t--no-plugins\tdon't check the plugins\n";
echo "\t--no-theme\tdon't check the themes\n";
echo "\t-h | --help\thelp me please\n\n";
echo "that's it for the moment...\n";
exit (MMH);
}
if ($website)
{
// just check the website...
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $website);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
if (!$verify_cert)
{
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
}
$data = curl_exec($ch);
curl_close($ch);
preg_match('/meta[^>]*generator[^>]*wordpress\s+([0-9.]+)/i', $data, $matches);
if (count ($matches) < 2 || !$matches[1])
{
echo "no version in web found...\n";
exit (WRN);
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://api.wordpress.org/core/version-check/1.2/?version=" . $matches[1]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_NOSIGNAL, 1);
$data = curl_exec($ch);
curl_close($ch);
$data = explode ("\n", $data);
if (version_compare ($data[3], $matches[1]) > 0)
{
echo "Your core is out of date! " . $matches[1] . " -> " . $data[3] . "\n";
exit (ERR);
}
// that's it
echo "Running " . $matches[1] . " is fine.\n";
exit (OOK);
}
// ok lets check a local installation!
if ($domain) // multihost
$_SERVER['HTTP_HOST'] = $domain;
// include wp stuff, don't need to to reinvent the wheel...
require_once($instdir.'/wp-load.php');
// let wordpress prepare it's tests
wp_version_check();
wp_update_plugins();
wp_update_themes();
// if it's pre 2.9 get_site_transient might be missing... pretty old my friend!
if (!function_exists ("get_site_transient"))
{
echo "OMG. Time to get some updates!!!";
exit (ERR);
}
$ret = OOK;
$suppl = "";
$msg = array ();
// check the core of your wordpress
if ($check_core)
{
$core = get_site_transient('update_core');
if (isset ($core->updates) && version_compare ($core->updates[0]->current, $core->version_checked) > 0)
{
$msg[] = "Core is out-of-date!";
$suppl .= "Core: " . $core->version_checked . " -> " . $core->updates[0]->current . "; ";
$ret = max ($ret, ERR);
}
else
$msg[] = "Core is up-to-date.";
}
else
$msg[] = "Skipping core checks!";
// check the plugins
if ($check_plugins)
{
$plugin_msg = array ();
$plugins = get_site_transient('update_plugins');
if (isset ($plugins->response))
{
foreach($plugins->response as $name => $update)
{
$plugin_msg[] = $update->slug . ": " . $plugins->checked[$name] ." -> " . $update->new_version;
}
}
if (count ($plugin_msg))
{
$s = "s are";
if (count ($plugin_msg) == 1)
$s = " is";
$msg[] = count ($plugin_msg) . " plugin" . $s . " out-of-date!";
$suppl .= implode ("; ", $plugin_msg) . "; ";
$ret = max ($ret, ERR);
}
else
$msg[] = "Plugins are up-to-date.";
}
else
$msg[] = "Skipping plugin checks!";
// check the themes
if ($check_themes)
{
$themes_msg = array ();
$themes = get_site_transient('update_themes');
if (isset ($themes->response))
{
foreach($themes->response as $name => $update)
{
$themes_msg[] = $name . ": " . $themes->checked[$name] ." -> " . $update["new_version"];
}
}
if (count ($themes_msg))
{
$s = "s are";
if (count ($plugin_msg) == 1)
$s = " is";
$msg[] = count ($themes_msg) . " theme" . $s . " out-of-date!";
$suppl .= implode ("; ", $themes_msg) . "; ";
$ret = max ($ret, ERR);
}
else
$msg[] = "Themes are up-to-date.";
}
else
$msg[] = "Skipping theme checks!";
// Get the perfdata
$count_posts = wp_count_posts();
$perfdata['posts_published'] = sprintf('%s=%s', 'posts_published', $count_posts->publish);
$comments_count = wp_count_comments();
$perfdata['comments'] = sprintf('%s=%s', 'comments', $comments_count->total_comments);
$count_users = count_users();
$perfdata['users'] = sprintf('%s=%s', 'users', $count_users['total_users']);
// collect our info
if ($ret == OOK)
echo "Well done! ";
else
echo "Need attention! ";
echo implode (" - ", $msg) . " - " . $suppl . "|" . implode(' ', $perfdata) . "\n";
exit ($ret);

View file

@ -0,0 +1,313 @@
#!/usr/bin/perl
# $Id: check_zone_rrsig_expiration,v 1.14 2015/10/12 16:54:20 wessels Exp $
#
# check_zone_rrsig_expiration
#
# nagios plugin to check expiration times of RRSIG records. Reminds
# you if its time to re-sign your zone.
# Copyright (c) 2008, The Measurement Factory, Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# Neither the name of The Measurement Factory nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# usage
#
# define command {
# command_name check-zone-rrsig
# command_line /usr/local/libexec/nagios-local/check_zone_rrsig -Z $HOSTADDRESS$
# }
#
# define service {
# name dns-rrsig-service
# check_command check-zone-rrsig
# ...
# }
#
# define host {
# use dns-zone
# host_name zone.example.com
# alias ZONE example.com
# }
#
# define service {
# use dns-rrsig-service
# host_name zone.example.com
# }
use warnings;
use strict;
use Getopt::Std;
use Net::DNS::Resolver;
use Time::HiRes qw ( gettimeofday tv_interval);
use Time::Local;
use List::Util qw ( shuffle );
# options
# -Z zone Zone to test
# -t seconds DNS query timeout
# -d debug
# -C days Critical if expiring in this many days
# -W days Warning if expiring in this many days
# -T type Query type (default SOA)
# -4 use IPv4 only
# -U size EDNS0 UDP packet size (default 4096)
my %opts = (t=>30, C=>2, W=>3, T=>'SOA', U=>4096);
getopts('4Z:dt:W:C:T:U:', \%opts);
usage() unless $opts{Z};
usage() if $opts{h};
my $zone = $opts{Z};
$zone =~ s/^zone\.//;
my $data;
my $start;
my $stop;
my @refs = qw (
a.root-servers.net
b.root-servers.net
c.root-servers.net
d.root-servers.net
e.root-servers.net
f.root-servers.net
g.root-servers.net
h.root-servers.net
i.root-servers.net
j.root-servers.net
k.root-servers.net
l.root-servers.net
m.root-servers.net
);
$start = [gettimeofday()];
do_recursion();
do_queries();
$stop = [gettimeofday()];
do_analyze();
sub do_recursion {
my $done = 0;
my $res = Net::DNS::Resolver->new;
do {
print STDERR "\nRECURSE\n" if $opts{d};
my $pkt;
foreach my $ns (shuffle @refs) {
print STDERR "sending query for $zone $opts{T} to $ns\n" if $opts{d};
$res->nameserver($ns);
$res->udp_timeout($opts{t});
$res->recurse(0);
$res->dnssec(1);
$res->udppacketsize($opts{U});
$res->force_v4(1) if $opts{'4'};
$pkt = $res->send($zone, $opts{T});
last if $pkt;
}
critical("No response to seed query") unless $pkt;
critical($pkt->header->rcode . " from " . $pkt->answerfrom)
unless ($pkt->header->rcode eq 'NOERROR');
@refs = ();
foreach my $rr ($pkt->authority) {
next unless $rr->type eq 'NS';
print STDERR $rr->string, "\n" if $opts{d};
push (@refs, $rr->nsdname);
next unless names_equal($rr->name, $zone);
add_nslist_to_data($pkt);
$done = 1;
}
} while (! $done);
}
sub do_queries {
my $n;
do {
$n = 0;
foreach my $ns (keys %$data) {
next if $data->{$ns}->{done};
print STDERR "\nQUERY $ns\n" if $opts{d};
my $pkt = send_query($zone, $opts{T}, $ns);
add_nslist_to_data($pkt);
$data->{$ns}->{queries}->{$opts{T}} = $pkt;
print STDERR "done with $ns\n" if $opts{d};
$data->{$ns}->{done} = 1;
$n++;
}
} while ($n);
}
sub do_analyze {
my $nscount = 0;
my $NOW = time;
my %MAX_EXP_BY_TYPE;
foreach my $ns (keys %$data) {
print STDERR "\nANALYZE $ns\n" if $opts{d};
my $pkt = $data->{$ns}->{queries}->{$opts{T}};
critical("No response from $ns") unless $pkt;
print STDERR $pkt->string if $opts{d};
critical($pkt->header->rcode . " from $ns")
unless ($pkt->header->rcode eq 'NOERROR');
critical("$ns is lame") unless $pkt->header->ancount;
foreach my $rr ($pkt->answer) {
next unless $rr->type eq 'RRSIG';
my $exp = sigrr_exp_epoch($rr);
my $T = $rr->typecovered;
if (!defined($MAX_EXP_BY_TYPE{$T}->{exp}) || $exp > $MAX_EXP_BY_TYPE{$T}->{exp}) {
$MAX_EXP_BY_TYPE{$T}->{exp} = $exp;
$MAX_EXP_BY_TYPE{$T}->{ns} = $ns;
}
}
$nscount++;
}
warning("No nameservers found. Is '$zone' a zone?") if ($nscount < 1);
warning("No RRSIGs found") unless %MAX_EXP_BY_TYPE;
my $min_exp = undef;
my $min_ns = undef;
my $min_type = undef;
foreach my $T (keys %MAX_EXP_BY_TYPE) {
printf STDERR ("%s RRSIG expires in %.1f days\n", $T, ($MAX_EXP_BY_TYPE{$T}->{exp}-$NOW)/86400) if $opts{d};
if (!defined($min_exp) || $MAX_EXP_BY_TYPE{$T}->{exp} < $min_exp) {
$min_exp = $MAX_EXP_BY_TYPE{$T}->{exp};
$min_ns = $MAX_EXP_BY_TYPE{$T}->{ns};
$min_type = $T;
}
}
critical("$min_ns has expired RRSIGs") if ($min_exp < $NOW);
if ($min_exp - $NOW < ($opts{C}*86400)) {
my $ND = sprintf "%3.1f days", ($min_exp-$NOW)/86400;
critical("$min_type RRSIG expires in $ND at $min_ns")
}
if ($min_exp - $NOW < ($opts{W}*86400)) {
my $ND = sprintf "%3.1f days", ($min_exp-$NOW)/86400;
warning("$min_type RRSIG expires in $ND at $min_ns")
}
success("No RRSIGs expiring in the next $opts{W} days");
}
sub sigrr_exp_epoch {
my $rr = shift;
die unless $rr->type eq 'RRSIG';
my $exp = $rr->sigexpiration;
die "bad exp time '$exp'"
unless $exp =~ /^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/;
my $exp_epoch = timegm($6,$5,$4,$3,$2-1,$1);
return $exp_epoch;
}
sub add_nslist_to_data {
my $pkt = shift;
foreach my $ns (get_nslist($pkt)) {
next if defined $data->{$ns}->{done};
print STDERR "adding NS $ns\n" if $opts{d};
$data->{$ns}->{done} |= 0;
}
}
sub success {
output('OK', shift);
exit(0);
}
sub warning {
output('WARNING', shift);
exit(1);
}
sub critical {
output('CRITICAL', shift);
exit(2);
}
sub output {
my $state = shift;
my $msg = shift;
$stop = [gettimeofday()] unless $stop;
my $latency = tv_interval($start, $stop);
printf "ZONE %s: %s; (%.2fs) |time=%.6fs;;;0.000000\n",
$state,
$msg,
$latency,
$latency;
}
sub usage {
print STDERR "usage: $0 -Z zone -d -t timeout -W days -C days\n";
print STDERR "\t-Z zone zone to test\n";
print STDERR "\t-T type query type (default SOA)\n";
print STDERR "\t-d debug\n";
print STDERR "\t-t seconds timeout on DNS queries\n";
print STDERR "\t-W days warning threshhold\n";
print STDERR "\t-C days critical threshold\n";
print STDERR "\t-4 use IPv4 only\n";
print STDERR "\t-U bytes EDNS0 UDP buffer size (default 4096)\n";
exit 3;
}
sub send_query {
my $qname = shift;
my $qtype = shift;
my $server = shift;
my $res = Net::DNS::Resolver->new;
$res->nameserver($server) if $server;
$res->udp_timeout($opts{t});
$res->retry(2);
$res->recurse(0);
$res->dnssec(1);
$res->udppacketsize($opts{U});
$res->force_v4(1) if $opts{'4'};
my $pkt = $res->send($qname, $qtype);
unless ($pkt) {
$res->usevc(1);
$res->tcp_timeout($opts{t});
$pkt = $res->send($qname, $qtype);
}
return $pkt;
}
sub get_nslist {
my $pkt = shift;
return () unless $pkt;
return () unless $pkt->authority;
my @nslist;
foreach my $rr ($pkt->authority) {
next unless ($rr->type eq 'NS');
next unless names_equal($rr->name, $zone);
push(@nslist, lc($rr->nsdname));
}
return @nslist;
}
sub names_equal {
my $a = shift;
my $b = shift;
$a =~ s/\.$//;
$b =~ s/\.$//;
lc($a) eq lc($b);
}

View file

@ -0,0 +1,5 @@
# Commande de check (essentiellement des perfs data) sur les logs de Postfix
command[check_postfix_log]=/usr/local/share/scripts-admin/nagios/check_postfix_log.sh
# Commande de check sur les mails en attente
command[check_postfix_postqueue]=/usr/local/share/scripts-admin/nagios/check_postfix_postqueue.sh -w 2 -c 5 -W 21000000 -C 30000000

View file

@ -0,0 +1,22 @@
# Commande de check (essentiellement des perfs data) sur les logs de Postfix
# TODO : à adapter (probablement refaire)
#command[check_postfix_log]=/usr/local/share/scripts-admin/nagios/check_postfix_log.sh
# Commande de check sur les mails en attente
command[check_mail_queue]=/usr/local/share/scripts-admin/nagios/check_sendmail_queue.sh -w 1 -c 5 -W 21000000 -C 30000000
# mailstats de Sendmail
command[check_sendmail_mailstats]=/usr/local/share/scripts-admin/nagios/check_sendmail-mailstats.sh
# Vérification des blacklists DNS (Spamhaus, etc.)
# Pour l'instant, on ne fait que spamhaus. À voir au fur et à mesure pour les autres.
# Exemple de liste : https://github.com/matteocorti/check_rbl
# 'domain' march'pas...
#command[check_rbl_domain]=perl /usr/local/share/scripts-admin/nagios/check_rbl -H example.net -t 15 -s cbl.anti-spam.org.cn -s cblplus.anti-spam.org.cn -s cblless.anti-spam.org.cn -s cdl.anti-spam.org.cn -s cbl.abuseat.org -s dnsbl.cyberlogic.net -s bl.deadbeef.com -s t1.dnsbl.net.au -s spamtrap.drbl.drand.net -s spamsources.fabel.dk -s 0spam.fusionzero.com -s mail-abuse.blacklist.jippg.org -s korea.services.net -s spamguard.leadmon.net -s ix.dnsbl.manitu.net -s relays.nether.net -s no-more-funn.moensted.dk -s psbl.surriel.com -s dyna.spamrats.com -s noptr.spamrats.com -s spam.spamrats.com -s dnsbl.sorbs.net -s dul.dnsbl.sorbs.net -s old.spam.dnsbl.sorbs.net -s problems.dnsbl.sorbs.net -s safe.dnsbl.sorbs.net -s spam.dnsbl.sorbs.net -s bl.spamcannibal.org -s bl.spamcop.net -s pbl.spamhaus.org -s sbl.spamhaus.org -s xbl.spamhaus.org -s ubl.unsubscore.com -s dnsbl-1.uceprotect.net -s dnsbl-2.uceprotect.net -s dnsbl-3.uceprotect.net -s db.wpbl.info
command[check_rbl_ipv4]=perl /usr/local/share/scripts-admin/nagios/check_rbl -H mail.example.net -t 15 -s cbl.anti-spam.org.cn -s cblplus.anti-spam.org.cn -s cblless.anti-spam.org.cn -s cdl.anti-spam.org.cn -s cbl.abuseat.org -s dnsbl.cyberlogic.net -s bl.deadbeef.com -s t1.dnsbl.net.au -s spamtrap.drbl.drand.net -s spamsources.fabel.dk -s 0spam.fusionzero.com -s mail-abuse.blacklist.jippg.org -s korea.services.net -s spamguard.leadmon.net -s ix.dnsbl.manitu.net -s relays.nether.net -s no-more-funn.moensted.dk -s psbl.surriel.com -s dyna.spamrats.com -s noptr.spamrats.com -s spam.spamrats.com -s dnsbl.sorbs.net -s dul.dnsbl.sorbs.net -s old.spam.dnsbl.sorbs.net -s problems.dnsbl.sorbs.net -s safe.dnsbl.sorbs.net -s spam.dnsbl.sorbs.net -s bl.spamcannibal.org -s bl.spamcop.net -s pbl.spamhaus.org -s sbl.spamhaus.org -s xbl.spamhaus.org -s ubl.unsubscore.com -s dnsbl-1.uceprotect.net -s dnsbl-2.uceprotect.net -s dnsbl-3.uceprotect.net -s db.wpbl.info
command[check_rbl_ipv6]=perl /usr/local/share/scripts-admin/nagios/check_rbl -H 2a01:abcd::abac:19 -t 15 -s cbl.anti-spam.org.cn -s cblplus.anti-spam.org.cn -s cblless.anti-spam.org.cn -s cdl.anti-spam.org.cn -s cbl.abuseat.org -s dnsbl.cyberlogic.net -s bl.deadbeef.com -s t1.dnsbl.net.au -s spamtrap.drbl.drand.net -s spamsources.fabel.dk -s 0spam.fusionzero.com -s mail-abuse.blacklist.jippg.org -s korea.services.net -s spamguard.leadmon.net -s ix.dnsbl.manitu.net -s relays.nether.net -s no-more-funn.moensted.dk -s psbl.surriel.com -s dyna.spamrats.com -s noptr.spamrats.com -s spam.spamrats.com -s dnsbl.sorbs.net -s dul.dnsbl.sorbs.net -s old.spam.dnsbl.sorbs.net -s problems.dnsbl.sorbs.net -s safe.dnsbl.sorbs.net -s spam.dnsbl.sorbs.net -s bl.spamcannibal.org -s bl.spamcop.net -s pbl.spamhaus.org -s sbl.spamhaus.org -s xbl.spamhaus.org -s ubl.unsubscore.com -s dnsbl-1.uceprotect.net -s dnsbl-2.uceprotect.net -s dnsbl-3.uceprotect.net -s db.wpbl.info
# Le noyau va (a priori) choisir l'IP source des connexions de Sendmail
# dans l'ordre dans lequel elles sont configurées. On vérifie que l'IPv4
# 192.168.2.25 est bien en tête.
command[check_first_ip]=/usr/local/share/scripts-admin/nagios/check_first_ip.sh -4 192.168.2.25

View file

@ -0,0 +1,7 @@
# Commandes de base pour serveur Linux
command[check_disks]=/usr/lib/nagios/plugins/check_disk -w 10% -c 5% -W 50% -K 5% -l -X tmpfs -X devpts -X usbfs
command[check_load]=/usr/lib/nagios/plugins/check_load -w 1,1,1 -c 3,2,2
command[check_network_volume]=/usr/local/share/scripts-admin/nagios/check_network_volume.sh
# Petite commande temporaire pour étudier souci neighbour table overflow
command[check_network-neighbour-table]=/usr/local/share/scripts-admin/nagios/check_network-neighbour-table.sh

View file

@ -0,0 +1,2 @@
# Vérification de l'age de certains fichiers (en général, des sauvegardes)
command[check_file_age]=/usr/local/share/scripts-admin/nagios/check_file_age.sh -b /var/backups/pfsense -W 3:20 -f '*'

View file

@ -0,0 +1,9 @@
# Vérification de la similitude des fichiers statiques sur les serveurs esclaves
# Pour utiliser, rajouter dans /etc/sudoers.d/nagios :
# nagios ALL=(ALL) NOPASSWD:/usr/local/sbin/check_differences_via_rsync.sh
command[check_config_replication]=sudo /usr/local/share/scripts-admin/nagios/check_differences_via_rsync.sh -s client-project-prod-srv2.example.net /etc/apache2/ /etc/libapache2-mod-jk/workers.properties /var/lib/tomcat7-multiinstances/*/webapps/ /root/patchs/ /etc/default/tomcat7_*
# Verif sur réplication fichiers
# À l'inverse, cette commande doit être lancée côté serveurs esclaves
# pour vérifier que la réplication des uploads se fait bien.
command[check_file_age_replication]=/usr/lib/nagios/plugins/check_file_age -w 900 -c 36000 -f /srv/app/.nagios-check-file-age

View file

@ -0,0 +1,10 @@
# Vérification de MAJ de la distribution
#
# Sous Debian, ajouter la ligne :
# APT::Periodic::Update-Package-Lists "1";
# si possible dans un fichier nommé /etc/apt/apt.conf.d/02periodic
command[check_updates]=/usr/lib/nagios/plugins/check_apt
# Sous CentOS, pour l'instant simple ajout de la ligne de /etc/crontab :
# 13 4,13,21 * * * root /usr/bin/yum -q -e 0 check-update
#command[check_updates]=/usr/lib64/nagios/plugins/check_updates -t 270 -w 3 -c 20 -a "--cacheonly"

View file

@ -0,0 +1,2 @@
# Commande de check de la température des procs
command[check_temperature]=/usr/local/share/scripts-admin/nagios/check_temperature.sh -w 65 -c 80

View file

@ -0,0 +1,7 @@
# Commande pour vérifier l'état SMART des disques physiques
command[check_ide_smart_sda]=/usr/lib64/nagios/plugins/check_ide_smart -d /dev/sda
command[check_ide_smart_sdb]=/usr/lib64/nagios/plugins/check_ide_smart -d /dev/sdb
#command[check_ide_smart_sdc]=/usr/lib64/nagios/plugins/check_ide_smart -d /dev/sdc
#command[check_ide_smart_sdd]=/usr/lib64/nagios/plugins/check_ide_smart -d /dev/sdd
#command[check_ide_smart_sde]=/usr/lib64/nagios/plugins/check_ide_smart -d /dev/sde
#command[check_ide_smart_sdf]=/usr/lib64/nagios/plugins/check_ide_smart -d /dev/sdf

2
nagios/etc/48_raid.cfg Normal file
View file

@ -0,0 +1,2 @@
# Check sur RAID Fusion MPT
command[check_raid]=/usr/lib/nagios/plugins/check_raid.pl

View file

@ -0,0 +1,2 @@
# Commande pour vérifier l'état du RAID logiciel
command[check_raid_linux]=/usr/lib/nagios/plugins/check_raid_linux.pl

View file

@ -0,0 +1,3 @@
# Commande basique pour check_openmanage
# Cf http://folk.uio.no/trondham/software/check_openmanage.html
command[check_openmanage]=/usr/lib/nagios/plugins/check_openmanage --perfdata

View file

@ -0,0 +1,6 @@
# SMTP (SSL ou STARTTLS)
#command[check_certificate]=/usr/lib/nagios/plugins/check_smtp -H 127.0.0.1 --certificate=10,3
# IMAPS (SSL)
#command[check_certificate]=/usr/lib/nagios/plugins/check_imap -H 127.0.0.1 -p 993 --certificate=30,5 -w 2 -c 30
# TCP de base (HTTPS par exemple
command[check_certificate]=/usr/lib/nagios/plugins/check_tcp -H 127.0.0.1 -p 443 --certificate=10,5 -w 2 -c 30

View file

@ -0,0 +1,3 @@
# Commande de check sur le nombre de connexions TCP et UDP
command[check_netstat_connectioncount]=/usr/local/share/scripts-admin/nagios/check_netstat_connectioncount.sh -w 1:3 -c 1:5 -p 22
#command[check_netstat_connectioncount]=/usr/local/share/scripts-admin/nagios/check_netstat_connectioncount.sh -w 1:3 -c 1:5 -p 22 -w 1:100 -c 1:200 -p80 -p 443

View file

@ -0,0 +1,2 @@
# Vérification sur le serveur local NTP
command[check_ntp_peer]=/usr/lib/nagios/plugins/check_ntp_peer -H 127.0.0.1 -w 2 -c 60 --jwarn=2 --jcrit=5 --twarn=3:10 --tcrit=2:20 --swarn=4 --scrit=6

View file

@ -0,0 +1,3 @@
# check_apache_serverstatus.pl récupéré depuis https://github.com/SteScho/apache_serverstatus
command[check_apache]=/usr/local/share/scripts-admin/nagios/check_apache_serverstatus.pl -H localhost --warncrit=.,3:,1: --wc=BytesPerSec,100000,200000
command[check_apache_access_log]=/usr/local/share/scripts-admin/nagios/check_apache_access_log.pl -f /var/log/apache2/access.log

View file

@ -0,0 +1 @@
command[check_wordpress]=php /usr/local/share/scripts-admin/nagios/check_wp.php --dir /var/www/wordpress

25
nagios/etc/README.txt Normal file
View file

@ -0,0 +1,25 @@
Petits exemples de fichiers récupérés dans les /etc/nagios/nrpe.d/ à droite à gauche.
Je les regroupe ici juste pour éviter d'avoir à les rechercher sur chacun des serveurs : ce ne sont pas des sauvegardes, ni des modèles immuables. Bien penser à adapter ces appels NRPE à chaque serveur (et lire la doc des plugins utilisés :)
Pour info, le nrpe_local :
######################################
# Do any local nrpe configuration here
######################################
allowed_hosts=2a01:cb04:a52:f900::abac:22
ssl_version=TLSv1.2+
ssl_cipher_list=HIGH
ssl_cacert_file=/etc/ssl/nagios/bugness-monitoring-ca.pem
ssl_cert_file=/etc/ssl/nagios/TODO.bugness.org.crt
ssl_privatekey_file=/etc/ssl/nagios/TODO.bugness.org.key
# SSL USE CLIENT CERTS
# This options determines client certificate usage.
# Values: 0 = Don't ask for or require client certificates (default)
# 1 = Ask for client certificates
# 2 = Require client certificates
ssl_client_certs=2
# Note Bugness: quoi qu'on fasse, ça semble être désactivé. Mais on le spécifie quand même
ssl_use_adh=0

View file

@ -0,0 +1,10 @@
--- nagios-plugins-1.4.15/plugins/check_disk.c 2010-07-27 22:47:16.000000000 +0200
+++ check_disk.c 2012-06-05 21:17:26.650825165 +0200
@@ -314,6 +314,7 @@
me->me_mountdir, total, available, available_to_root, used, fsp.fsu_files, fsp.fsu_ffree);
dused_pct = calculate_percent( used, used + available ); /* used + available can never be > uintmax */
+ total = used + available;
dfree_pct = 100 - dused_pct;
dused_units = used*fsp.fsu_blocksize/mult;

123
nagios/utils.sh Normal file
View file

@ -0,0 +1,123 @@
#! /bin/sh
# Note chl-dev@bugness.org
# Fichier pris sur la dernière version de nagios-plugins au 2012-12-10
# https://github.com/nagios-plugins/nagios-plugins
# car la version dispo sur Debian n'a pas encore la fonction check_range()
# À supprimer dès qu'envisageable.
STATE_OK=0
STATE_WARNING=1
STATE_CRITICAL=2
STATE_UNKNOWN=3
STATE_DEPENDENT=4
if test -x /usr/bin/printf; then
ECHO=/usr/bin/printf
else
ECHO=echo
fi
print_revision() {
echo "$1 v$2 (nagios-plugins 1.4.16)"
$ECHO "The nagios plugins come with ABSOLUTELY NO WARRANTY. You may redistribute\ncopies of the plugins under the terms of the GNU General Public License.\nFor more information about these matters, see the file named COPYING.\n" | sed -e 's/\n/ /g'
}
support() {
$ECHO "Send email to nagios-users@lists.sourceforge.net if you have questions\nregarding use of this software. To submit patches or suggest improvements,\nsend email to nagiosplug-devel@lists.sourceforge.net.\nPlease include version information with all correspondence (when possible,\nuse output from the --version option of the plugin itself).\n" | sed -e 's/\n/ /g'
}
#
# check_range takes a value and a range string, returning successfully if an
# alert should be raised based on the range. Range values are inclusive.
# Values may be integers or floats.
#
# Example usage:
#
# Generating an exit code of 1:
# check_range 5 2:8
#
# Generating an exit code of 0:
# check_range 1 2:8
#
check_range() {
local v range yes no err decimal start end cmp match
v="$1"
range="$2"
# whether to raise an alert or not
yes=0
no=1
err=2
# regex to match a decimal number
decimal="-?([0-9]+\.?[0-9]*|[0-9]*\.[0-9]+)"
# compare numbers (including decimals), returning true/false
cmp() { awk "BEGIN{ if ($1) exit(0); exit(1)}"; }
# returns successfully if the string in the first argument matches the
# regex in the second
match() { echo "$1" | grep -E -q -- "$2"; }
# make sure value is valid
if ! match "$v" "^$decimal$"; then
echo "${0##*/}: check_range: invalid value" >&2
unset -f cmp match
return "$err"
fi
# make sure range is valid
if ! match "$range" "^@?(~|$decimal)(:($decimal)?)?$"; then
echo "${0##*/}: check_range: invalid range" >&2
unset -f cmp match
return "$err"
fi
# check for leading @ char, which negates the range
if match $range '^@'; then
range=${range#@}
yes=1
no=0
fi
# parse the range string
if ! match "$range" ':'; then
start=0
end="$range"
else
start="${range%%:*}"
end="${range#*:}"
fi
# do the comparison, taking positive ("") and negative infinity ("~")
# into account
if [ "$start" != "~" ] && [ "$end" != "" ]; then
if cmp "$start <= $v" && cmp "$v <= $end"; then
unset -f cmp match
return "$no"
else
unset -f cmp match
return "$yes"
fi
elif [ "$start" != "~" ] && [ "$end" = "" ]; then
if cmp "$start <= $v"; then
unset -f cmp match
return "$no"
else
unset -f cmp match
return "$yes"
fi
elif [ "$start" = "~" ] && [ "$end" != "" ]; then
if cmp "$v <= $end"; then
unset -f cmp match
return "$no"
else
unset -f cmp match
return "$yes"
fi
else
unset -f cmp match
return "$no"
fi
}

View file

@ -0,0 +1,123 @@
# Ici se trouvent réunies les fonctions et la configuration utiles au script
# de gestion des mailing-lists
# Config
LDAP_HOST="ldap.example.net"
LDAP_ROOT="dc=example,dc=net"
CONFIG_IDENTIFIANTS_SMBLDAP="/etc/smbldap-tools/smbldap_bind.conf"
. $CONFIG_IDENTIFIANTS_SMBLDAP
LDAP_PASSWD="$masterPw"
LDAP_BIND_DN="$masterDN"
# Liste, sur stdout, les mailing-lists dont fait déjà parti l'adresse
# email fournie en argument.
# args: email
#
# note : effectué en connexion anonyme. A priori, les droits sont suffisants.
afficher_abonnements_en_cours() {
ldapsearch -x -h "$LDAP_HOST" -b "ou=mailing-lists,$LDAP_ROOT" -LLL "(rfc822MailMember=$1)" cn | sed -n 's/^cn: \(.*\)/\1/p' | sort
}
# Affiche la liste des mailing-lists
#
# note : effectué en connexion anonyme. A priori, les droits sont suffisants.
afficher_listing_mailinglists() {
ldapsearch -x -h "$LDAP_HOST" -b "ou=mailing-lists,$LDAP_ROOT" -LLL cn | sed -n 's/^cn: \(.*\)/\1/p' | sort
}
# Affiche la liste des abonnés d'une mailing-list
# args: mailing-list
#
# note : effectué en connexion anonyme. A priori, les droits sont suffisants.
afficher_abonnes_mailinglist() {
ldapsearch -x -h "$LDAP_HOST" -b "cn=$1,ou=mailing-lists,$LDAP_ROOT" -LLL rfc822MailMember | sed -n 's/^rfc822MailMember: //p'
}
# Supprime l'email d'une mailing-list
# args: email, mailing-list
supprimer_abonnement() {
# Ecriture du mot de passe
LDAP_PASSWDFILE=$( mktemp )
printf "%s" "$LDAP_PASSWD" >$LDAP_PASSWDFILE
# Requete
cat <<EOF | ldapmodify -x -h $LDAP_HOST -D "$LDAP_BIND_DN" -y "$LDAP_PASSWDFILE"
dn: cn=$2,ou=mailing-lists,$LDAP_ROOT
changetype: modify
delete: rfc822MailMember
rfc822MailMember: $1
EOF
RETOUR="$?"
# Effacement du mot de passe
rm -f "$LDAP_PASSWDFILE"
return $RETOUR
}
# Ajoute l'email à une mailing-list
# args: email, mailing-list
creer_abonnement() {
# Ecriture du mot de passe
LDAP_PASSWDFILE=$( mktemp )
printf "%s" "$LDAP_PASSWD" >$LDAP_PASSWDFILE
# Requête
cat <<EOF | ldapmodify -x -h $LDAP_HOST -D "$LDAP_BIND_DN" -y "$LDAP_PASSWDFILE"
dn: cn=$2,ou=mailing-lists,$LDAP_ROOT
changetype: modify
add: rfc822MailMember
rfc822MailMember: $1
EOF
RETOUR="$?"
# Effacement du mot de passe
rm -f "$LDAP_PASSWDFILE"
return $RETOUR
}
# Détruit une mailing-list
# args: mailing-list
supprimer_mailinglist() {
# Ecriture du mot de passe
LDAP_PASSWDFILE=$( mktemp )
printf "%s" "$LDAP_PASSWD" >$LDAP_PASSWDFILE
# Requete
cat <<EOF | ldapmodify -x -h $LDAP_HOST -D "$LDAP_BIND_DN" -y "$LDAP_PASSWDFILE"
dn: cn=$1,ou=mailing-lists,$LDAP_ROOT
changetype: delete
EOF
RETOUR="$?"
# Effacement du mot de passe
rm -f "$LDAP_PASSWDFILE"
return $RETOUR
}
# Crée une mailing-list
# args: mailing-list
creer_mailinglist() {
# Ecriture du mot de passe
LDAP_PASSWDFILE=$( mktemp )
printf "%s" "$LDAP_PASSWD" >$LDAP_PASSWDFILE
# Requete
cat <<EOF | ldapmodify -x -h $LDAP_HOST -D "$LDAP_BIND_DN" -y "$LDAP_PASSWDFILE"
dn: cn=$1,ou=mailing-lists,$LDAP_ROOT
changetype: add
cn: $1
objectClass: nisMailAlias
objectClass: top
EOF
RETOUR="$?"
# Effacement du mot de passe
rm -f "$LDAP_PASSWDFILE"
return $RETOUR
}

114
oldies/sauvegarde_pfsense.sh Executable file
View file

@ -0,0 +1,114 @@
#!/bin/sh
# Ce script sert à la sauvegarde de notre firewall pfSense
# Très inspiré du script http://www.scribd.com/doc/92371725/pfsense
# Configuration
BASE_DIR="$HOME/backups"
PREFIXE="sauv_pfsense_"
SUFFIXE=$( date +%Y%m%d-%H%M )
DUREE_DE_VIE=0
USERNAME="backuppc"
PASSWORD="toto123"
COOKIES_FILE=$( mktemp )
CSRF_MAGIC=""
# Arrêt à la première erreur non-catchée
set -e
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 [-b base_dir] [-p prefixe] [-s suffixe] [-d duree_de_vie] hostname/IP
$0 -h
-b base_dir : répertoire de sauvegarde (defaut: $BASE_DIR),
-p prefixe : prefixe des fichiers de sauvegarde (defaut: $PREFIXE),
-s suffixe : suffixe des fichiers de sauvegarde (default basé sur la date, ex: $SUFFIXE),
-d duree_de_vie : age en nb de jours a partir duquel les anciennes archives sont supprimees. Si 0, pas de suppression. (defaut: $DUREE_DE_VIE),
hostname/IP : nom d'hôte ou IP du firewall pfsense à sauvegarder.
EOF
}
clean_up_and_exit() {
test -f "$COOKIES_FILE" && rm -f "$COOKIES_FILE"
exit $1
}
# Début du code
# gestion des options de lancement
while getopts b:p:s:d:h o; do
case $o in
'b')
BASE_DIR="$OPTARG"
;;
'p')
PREFIXE="$OPTARG"
;;
's')
SUFFIXE="$OPTARG"
;;
'd')
DUREE_DE_VIE="$OPTARG"
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
shift $( expr $OPTIND - 1 )
HOSTNAME_PFSENSE="$1"
BASE_NAME=$( printf "%s" $( basename "$REPDRUPAL" ) | tr -c "a-zA-Z" "-" )
if [ -z "$HOSTNAME_PFSENSE" ]; then
echo "ERREUR: veuillez indiquer un hostname (ou une IP)." >&2
clean_up_and_exit 1
fi
if [ ! -d "$BASE_DIR" ]; then
echo "ERREUR: repertoire de destination '$BASE_DIR' inexistant." >&2
clean_up_and_exit 1
fi
# Récupération du CSRF
CSRF_MAGIC="$( wget -q -O - --keep-session-cookies --save-cookies "$COOKIES_FILE" https://$HOSTNAME_PFSENSE/index.php | sed -n "s/.*<input type='hidden' name='__csrf_magic' value=\"\([^\"]\+\)\".*/\1/p" || echo "" )"
if [ -z "$CSRF_MAGIC" ]; then
echo "ERREUR: erreur lors de la récupération du __csrf_magic." >&2
clean_up_and_exit 1
fi
# Authentification
if ! wget -q -O /dev/null --keep-session-cookies --save-cookies "$COOKIES_FILE" --load-cookies "$COOKIES_FILE" --post-data "__csrf_magic=$CSRF_MAGIC&usernamefld=$USERNAME&passwordfld=$PASSWORD&login=Login" https://$HOSTNAME_PFSENSE/index.php; then
echo "ERREUR: echec a la premiere connexion." >&2
clean_up_and_exit 1
fi
# On vérifie que l'on est bien connecté
if [ $( wget -q -O - --keep-session-cookies --save-cookies "$COOKIES_FILE" --load-cookies "$COOKIES_FILE" https://$HOSTNAME_PFSENSE/diag_backup.php | grep -c "Diagnostics: Backup/restore" ) -lt 1 ]; then
echo "ERREUR: impossible d'accéder à la page de backup." >&2
clean_up_and_exit 1
fi
#Config. seule
wget -q -O "$BASE_DIR/$PREFIXE$BASE_NAME$HOSTNAME_PFSENSE-conf_$SUFFIXE.xml" --keep-session-cookies --save-cookies "$COOKIES_FILE" --load-cookies "$COOKIES_FILE" --post-data "Submit=Download%20configuration&donotbackuprrd=on" https://$HOSTNAME_PFSENSE/diag_backup.php
#Config + données RRD
wget -q -O "$BASE_DIR/$PREFIXE$BASE_NAME$HOSTNAME_PFSENSE-confrrd_$SUFFIXE.xml" --keep-session-cookies --save-cookies "$COOKIES_FILE" --load-cookies "$COOKIES_FILE" --post-data "Submit=Download%20configuration" https://$HOSTNAME_PFSENSE/diag_backup.php
# Suppression des vieux backups
if [ "$DUREE_DE_VIE" -ne "0" ]; then
find $BASE_DIR -name "$PREFIXE$BASE_NAME$HOSTNAME_PFSENSE-conf_*.xml" -mtime "+$DUREE_DE_VIE" -print0 | xargs -n 200 -r -0 rm -f
find $BASE_DIR -name "$PREFIXE$BASE_NAME$HOSTNAME_PFSENSE-confrrd_*.xml" -mtime "+$DUREE_DE_VIE" -print0 | xargs -n 200 -r -0 rm -f
fi
clean_up_and_exit 0

25
oldies/sauvegarde_samba.sh Executable file
View file

@ -0,0 +1,25 @@
#!/bin/sh
# Configuration
# Le fichier sera nommé BASE_DIR/PREFIXEbasenameSUFFIXE
BASE_DIR=/var/backups/bdd-samba
PREFIXE=sauv_samba_
SUFFIXE=_$( date +%Y%m%d-%H%M ).tar.gz
DUREE_DE_VIE=100
# Vérifications initiales
if [ ! -d "$BASE_DIR" ]; then
echo "ERREUR : répertoire de sauvegarde inexistant : $BASE_DIR ." >&2
exit 1
fi
# Sauvegarde
cd /var/lib
tar -czf "$BASE_DIR/$PREFIXE"example.net"$SUFFIXE" samba
# Suppression des anciennes sauvegardes
if [ "$1" = "--delete-olds" ]; then
find $BASE_DIR -name "$PREFIXE*" -mtime +$DUREE_DE_VIE -print0 | xargs -n 200 -r -0 rm -f
fi

View file

@ -0,0 +1,242 @@
#!/bin/sh
# Ce script aide à la création d'un compte LDAP
# Il s'occupe :
# - création LDAP/SMB
# - mot de passe
# - création de la boîte Cyrus
# - ajout aux listes d'emails (salaries@example.net, ...)
# Ce script nécessite :
# - des identifiants admin sur les listes emails (utilisation des identifiants smbldap pour l'instant. Pas glop.)
# - des identifiants admin sur Cyrus. Ces identifiants seront contenus dans le fichier $CONFIG_IDENTIFIANTS_CYRUS, au format :
# user=LOGIN
# password=PASS
# Config
LDAP_HOST="ldap.example.net"
LDAP_ROOT="dc=example,dc=net"
CYRUS_HOST="messagerie.example.net"
CONFIG_EMAIL_DOMAIN="example.net"
CONFIG_DEFAULT_GID=513
CONFIG_DEFAULT_QUOTA=1048576
CONFIG_IDENTIFIANTS_SMBLDAP="/etc/smbldap-tools/smbldap_bind.conf"
CONFIG_IDENTIFIANTS_CYRUS="/root/.cyrus_auth"
# Petite mise en bouche
cat <<EOF
Script de création d'un compte utilisateur dans l'annuaire LDAP et
dans Cyrus (IMAP/POP3), puis d'inscription aux mailing-lists.
Dernière modification : 2010-09-30 (chl)
(interruption possible via Ctrl-C)
EOF
# Fonctions (désolé pour la lisibilité, 'faut les définir au-dessus des appels)
check_string() {
# Vérifie que la chaîne ne contient pas "trop" de caractères
# spéciaux (accepte accents, espaces et tirets)
# TODO
test "$1" != ""
}
check_email() {
# Vérifie la syntaxe et l'unicité de l'adresse e-mail
# Recherche dans LDAP d'une adresse identique
RACINE_EMAIL=$( echo "$1" | sed 's/@.*//g' )
if [ $( ldapsearch -x -h "$LDAP_HOST" -b "ou=users,$LDAP_ROOT" -LLL "mailLocalAddress=$RACINE_EMAIL" | grep -c "^dn: " ) -gt 0 ]; then
# Listing du(es) login(s) utilisant cette adresse
TMP=$( ldapsearch -x -h "$LDAP_HOST" -b "ou=users,$LDAP_ROOT" -LLL "mailLocalAddress=$RACINE_EMAIL" uid | sed -n 's/^uid: //p' )
echo "ERREUR: adresse email déjà utilisée par :" $TMP "." >&2
return 1
fi
#TODO : vérif syntaxe
test "$1" != ""
}
check_login() {
# n'accepte que les minuscules et les chiffres (doit commencer par une lettre)
# locale "C" pour que [a-z] ne contienne pas à, é, ...
OLDLANG="$LANG"
LANG="C"
RETURN=0
# Passage à la regexp de la mort
if [ $( printf "%s" "$1" | egrep -c "^[a-z][a-z0-9]{2,7}$" ) -ne 1 ]; then
RETURN=1
# Si le login est vide, on reste clément et rien n'est affiché
test -n "$1" && echo "ERREUR: login non valide (regexp: ^[a-z][a-z0-9]{2,7}$ )" >&2
fi
LANG="$OLDLANG"
test "$RETURN" -eq 0 || return 1
# Vérification que le login (=uid LDAP) n'est pas déjà utilisé
if [ $( ldapsearch -x -h "$LDAP_HOST" -b "ou=users,$LDAP_ROOT" -LLL "uid=$1" | grep -c "^dn: " ) -gt 0 ]; then
echo "ERREUR: login déjà existant." >&2
return 1
fi
}
check_unix_homedir() {
# Rien à faire (?)
test "$1" != "" # renvoie false si $1 vide
}
generate_default_email() {
# Initiale du prénom + nom
RETURN=$( printf "%.01s%s@%s" "$1" "$2" "$CONFIG_EMAIL_DOMAIN" | tr "A-Z" "a-z" )
# Suppression des caractères spéciaux et sortie de la fonction
LANG=C echo "$RETURN" | tr -dc "a-z0-9-.@"
}
generate_default_login() {
# on se base lâchement sur generate_default_email :)
# (TODO : bugs divers si prénom + nom < 3 caractères...)
printf "%.03s" $( generate_default_email "$1" "$2" )
}
generate_default_unix_homedir() {
# On renvoie simplement /home/LOGIN
echo "/home/$1"
}
# Enfin : le code...
# 1ère étape : on demande interactivement toutes les infos
# et on crée le compte + mot de passe
#Prénom
while ! check_string "$PRENOM"; do
printf "Prénom : "; read PRENOM
done
#Nom
while ! check_string "$NOM"; do
printf "Nom : "; read NOM
done
#login
DEFAULT_LOGIN=$( generate_default_login "$PRENOM" "$NOM" )
while ! check_login "$LOGIN"; do
printf "Login [%s] : " "$DEFAULT_LOGIN"; read LOGIN
test "$LOGIN" = "" && LOGIN="$DEFAULT_LOGIN"
done
#email
DEFAULT_EMAIL=$( generate_default_email "$PRENOM" "$NOM" )
while ! check_email "$EMAIL"; do
# Prompt avec proposition
printf "Email [%s] : " "$DEFAULT_EMAIL"; read EMAIL
# Si l'utilisateur laisse le champ vide : utilisation de la proposition
test "$EMAIL" = "" && EMAIL="$DEFAULT_EMAIL"
done
#Home directory (UNIX)
DEFAULT_UNIX_HOMEDIR=$( generate_default_unix_homedir "$LOGIN" )
while ! check_unix_homedir "$UNIX_HOMEDIR"; do
printf "Homedir [%s] : " "$DEFAULT_UNIX_HOMEDIR"; read UNIX_HOMEDIR
test "$UNIX_HOMEDIR" = "" && UNIX_HOMEDIR="$DEFAULT_UNIX_HOMEDIR"
done
# Création du compte avec smbldap-useradd + demande du mot de passe
echo "Création du compte dans LDAP... (éviter le Ctrl-C à partir de maintenant)"
if ! smbldap-useradd -a -g $CONFIG_DEFAULT_GID -M "$EMAIL" -N "$PRENOM" -S "$NOM" -d "$UNIX_HOMEDIR" "$LOGIN"; then
echo "ERREUR: smbldap-useradd a renvoyé une erreur. Arrêt du script $0." >&2
return 1
fi
echo "Compte LDAP créé."
echo
# mot de passe
echo "Veuillez renseigner le mot de passe..."
TMP=""
while test "$TMP" = "" && ! smbldap-passwd "$LOGIN"; do
echo "WARNING: problème sur le mot de passe." >&2
printf "Voulez-vous réessayer ? (O/n)"
read TMP
if [ $( echo "$TMP" | egrep -ic "^o$" ) -eq 1 ]; then
TMP=""
fi
done
echo
# 2ème étape : création de la boîte mail dans Cyrus
#
# Tout se fait via un script Perl parce que les outils d'admin
# de Cyrus sont pas franchement faciles à scripter...
printf "Créer la boîte mail dans Cyrus ? (O/n)"
read TMP
if [ -n "$TMP" ] && [ $( echo "$TMP" | egrep -ic "^o$" ) -eq 0 ]; then
echo "Ah bon ?! Ben à la prochaine alors." >&2
return 0
fi
RACINE_EMAIL=$( echo "$EMAIL" | sed 's/@.*//' )
cat <<EOF | perl -MCyrus::IMAP::Admin
# Connexion
my \$conn = Cyrus::IMAP::Admin->new('$CYRUS_HOST') or die "Echec de connexion.";
# Lecture du fichier de configuration
open CONFIG, '$CONFIG_IDENTIFIANTS_CYRUS';
while (<CONFIG>) {
chomp; # no newline
s/^#.*//; # no comments
s/^\s+//; # no leading white
s/\s+$//; # no trailing white
next unless length; # anything left?
my (\$var, \$value) = split(/\s*=\s*/, \$_, 2);
\$cyrus_config{\$var} = \$value;
}
# Authentification
\$conn->authenticate(
"-user" => \$cyrus_config{'user'},
"-password" => \$cyrus_config{'password'},
"-mechanism" => 'LOGIN',
) or die "Echec à l'authentification.";
# Création de la boîte Cyrus
\$conn->create('user.$RACINE_EMAIL') or die "Echec à la création de la boîte ('va falloir aller voir à la main si qqchose a été fait...)";
# Spécification du quota
\$conn->setquota('user.$RACINE_EMAIL', 'STORAGE', '$CONFIG_DEFAULT_QUOTA') or die "Echec lors de la mise en place du quota";
EOF
if [ "$?" -eq 0 ]; then
echo "Boîte créée."
else
echo "ERREUR: erreur à la création de la boîte. Le compte LDAP existe néamoins. Voir avec Cyrus. Abandon." >&2
return 1
fi
echo
# 3ème étape : les listes mails
echo "Ajout aux mailing-lists..."
if ! ./script_ml-gestion-abonne.sh -a salaries -i "$EMAIL"; then
cat <<EOF >&2
NOTICE: apparemment, il y a eu un pépin à la gestion des mailing-lists.
Le compte est néanmoins créé, on vous laisse vous débrouiller avec
./script_ml-gestion-abonne.sh ou PHPLDAPAdmin.
Cordialement.
EOF
return 1
fi
# 4ème étape : envoi du mail de bienvenue
# TODO
echo "Fin du script."

View file

@ -0,0 +1,136 @@
#!/bin/sh
RACINE_MULTIINSTANCES="/var/lib/tomcat7-multiinstances"
RACINE_PATCHS="$HOME/patchs"
WAR_FILE=""
WEBAPP_NAME="ROOT"
RELANCE_AUTO_TOMCAT=1
JUST_TEST_THE_PATCHS=0
set -e
# Fonctions
# fonction d'aide
usage() {
cat <<EOF
$0 [options] -w package.war instance1 instance2 ...
$0 -h
Ce script déploie un war dans les instances Tomcat indiquées
puis les patchs en utilisant le fichier homonyme suffixé en .diff
Par ex:
$0 -w myapp-1.1.1.war tomcat7_003_titi tomcat7_008_toto
va déployer 'myapp-1.1.1.war' dans la webapp ROOT des 2 instances, puis
va appliquer les patchs tomcat7_003_titi.diff et tomcat7_008_toto.diff
qui se trouvent dans '$RACINE_PATCHS'.
Ce script commence par tester chacun des patchs sur un dézippage temporaire
du war, puis crée une copie par instance, la patche, éteint Tomcat, remplace
l'ancienne webapp par la nouvelle copie patchée et reboote tomcat.
Options :
-w : war à déployer
-d : répertoire racine du multiinstances (par défaut : '$RACINE_MULTIINSTANCES')
-D : répertoire contenant les patchs (par défaut : '$RACINE_PATCHS')
-n : nom de la webapp à écraser (par défaut : '$WEBAPP_NAME')
-T : juste tester les patchs sur le war, ne pas faire le déploiement
-h : this help screen
EOF
}
# Début du code
# gestion des options de lancement
while getopts d:D:n:hTw: o; do
case $o in
'd')
RACINE_MULTIINSTANCES="$OPTARG"
;;
'D')
RACINE_PATCHS="$OPTARG"
;;
'n')
WEBAPP_NAME="$OPTARG"
;;
'T')
JUST_TEST_THE_PATCHS=1
;;
'w')
# Utilisation de readlink pour avoir un chemin absolu,
# vu qu'on bouge pas mal entre les répertoires :-/
WAR_FILE="$( readlink -f "$OPTARG" )"
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
shift $( expr $OPTIND - 1 )
#REPDRUPAL="$1"
# Petites vérifs
if [ ! -f "$WAR_FILE" ]; then
echo "ERREUR: fichier '$WAR_FILE' introuvable." >&2
exit 1
fi
# Vérification des patchs
echo "INFO: vérification des patchs..."
# on dézippe le war dans un dossier temporaire
TEMP_REP=$( mktemp -d )
cd "$TEMP_REP"
unzip -q "$WAR_FILE"
# ... et on boucle sur chacun de patchs demandés
for INSTANCE in $@; do
echo "$INSTANCE"
if [ ! -f $RACINE_PATCHS/$INSTANCE.diff ]; then
echo "ERREUR: patch inexistant pour l'instance '$INSTANCE'." >&2
exit 1
fi
if ! patch --dry-run -p1 < $RACINE_PATCHS/$INSTANCE.diff; then
echo "ERREUR: patch non applicable sur instance $INSTANCE." >&2
exit 1
fi
done
cd /
rm -rf "$TEMP_REP"
# Si on se contentait de tester les patchs, on s'arrête là
[ "$JUST_TEST_THE_PATCHS" -eq 1 ] && exit 0
# Déploiement
echo "INFO: déploiement..."
for INSTANCE in $@; do
echo "$INSTANCE"
# Dézippage du war dans un dossier temporaire
cd "$RACINE_MULTIINSTANCES/$INSTANCE"
mkdir ROOT_new
cd ROOT_new
unzip -q "$WAR_FILE"
# Patch de la copie dézippée
patch -p1 < $RACINE_PATCHS/$INSTANCE.diff
# Relance de tomcat
if [ "$RELANCE_AUTO_TOMCAT" -ne 0 ]; then
service $INSTANCE stop
sleep 3
rm -rf "../webapps/$WEBAPP_NAME"
mv -i ../ROOT_new "../webapps/$WEBAPP_NAME"
service $INSTANCE start
else
# TODO : affichage des commandes ?
true
fi
done

128
oldies/script_inotify-rsync.sh Executable file
View file

@ -0,0 +1,128 @@
#!/bin/sh
# Configuration
DIR_SOURCE=""
DIR_DESTINATION=""
MAX_WAIT=60
RSYNC_OPTIONS="-q -aH --delete"
INOTIFYWAIT_OPTIONS="-qq -r -e close_write -e attrib -e move -e create -e delete -e unmount"
#RSYNC_OPTIONS="-v -aH --delete"
#INOTIFYWAIT_OPTIONS="-r -e close_write -e attrib -e move -e create -e delete -e unmount"
# On essaie de diminuer la priorité de la chose
# Aucune importance si les utilitaires ne sont pas dispos
renice 15 -p $$ >/dev/null 2>&1
ionice -c 3 -p $$ >/dev/null 2>&1
# Arrêt à la moindre erreur non-catchée
set -e
# Fonctions
# Little helper to centralize syslog calls
loglog() {
LEVEL="$1"
shift
logger -s -t inotify-rsync -p user.$LEVEL $@
}
# affiche le message d'aide
usage() {
cat <<EOF
$0 [-i inotifywait_options] [-r rsync_options] [-t max_wait] -s source_directory -d rsync_destination
$0 -h
-s : specify the rsync source (needs to be a local directory)
-d : specify the rsync destination (can be anythin rsync accepts)
-i : inotifywait options (minus the -t option)
(default : "$INOTIFYWAIT_OPTIONS")
-r : rsync options
(default : "$RSYNC_OPTIONS")
-t : Even without event (or in case of missed event), rsync will be launched every "max_wait" seconds
(0 = wait forever) (default: $MAX_WAIT)
-h : this help screen
EOF
}
# Début du code
# gestion des options de lancement
while getopts s:d:r:w:h o; do
case $o in
's')
DIR_SOURCE="$OPTARG"
;;
'd')
DIR_DESTINATION="$OPTARG"
;;
'i')
INOTIFYWAIT_OPTIONS="$OPTARG"
;;
'r')
RSYNC_OPTIONS="$OPTARG"
;;
'w')
MAX_WAIT="$OPTARG"
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
#shift $( expr $OPTIND - 1 )
#REPDRUPAL="$1"
# Some checks
if [ -z "$DIR_SOURCE" ] || [ ! -d "$DIR_SOURCE" ]; then
loglog err "ERROR: no source specified or directory inexistent." >&2
exit 1
fi
if [ -z "$DIR_DESTINATION" ]; then
loglog err "ERROR: no destination specified." >&2
exit 1
fi
while true; do
# rsync can send error code != 0, we'll have to check
# what this code means before failing
set +e
rsync $RSYNC_OPTIONS "$DIR_SOURCE" "$DIR_DESTINATION"
RSYNC_RETURN_CODE="$?"
set -e
while [ "$RSYNC_RETURN_CODE" -ne "0" ]; do
for i in 24; do
if [ "$RSYNC_RETURN_CODE" -eq "$i" ]; then
# If the return code is "acceptable", we go on
loglog debug "DEBUG: rsync returned code '$RSYNC_RETURN_CODE'"
break 2
fi
done
# The return code wasn't in the "acceptable" list => log + exit
loglog err "ERROR: rsync returned unexpected error code : $RSYNC_RETURN_CODE. Script stopped."
exit $RSYNC_RETURN_CODE
done
# Wait for another change
# inotifywait can send error code != 0, we'll have to check
# what this code means before failing
set +e
inotifywait $INOTIFYWAIT_OPTIONS -t "$MAX_WAIT" "$DIR_SOURCE"
INOTIFYWAIT_RETURN_CODE="$?"
set -e
if [ "$INOTIFYWAIT_RETURN_CODE" -ne "0" ]; then
if [ "$INOTIFYWAIT_RETURN_CODE" -ne "2" ]; then
loglog err "ERROR: inotifywait returned unexpected error code : $INOTIFYWAIT_RETURN_CODE. Script stopped."
exit $INOTIFYWAIT_RETURN_CODE
fi
fi
done

View file

@ -0,0 +1,138 @@
#! /bin/sh
### BEGIN INIT INFO
# Provides: rsync-inotify
# Required-Start: $syslog $local_fs $network
# Required-Stop: $syslog $local_fs $network
# Should-Start: $named
# Should-Stop: $named
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Rsync-inotify launcher
# Description: This script launch the rsync-inotify script on the upload
# of the XXX instances project.
### END INIT INFO
# Do NOT "set -e"
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="Rsync-Inotify for XXX instances"
NAME=inotify-rsync
DAEMON=/root/script_inotify-rsync.sh
# J'arrive pas à le faire passer correctement à start-stop-daemon :-(
#DAEMON_ARGS='-r "-q -aH --delete --password-file=/root/.inotify-rsync-password" -s /srv/XXX/ -d "rsync://XXX-storage-master@srv2.example.net/XXX-storage/"'
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/inotify-rsync.sh
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
if [ -n "$( pgrep -f "$DAEMON" )" ]; then
return 1
fi
start-stop-daemon --start -b --make-pidfile --iosched idle --nicelevel 10 --exec "$DAEMON" --pidfile "$PIDFILE" --test > /dev/null \
|| return 1
start-stop-daemon --start -b --make-pidfile --iosched idle --nicelevel 10 --exec "$DAEMON" --pidfile "$PIDFILE" -- \
-r "-q -aH --delete --password-file=/root/.inotify-rsync-password" -s /srv/XXX/ -d "rsync://storage-master@srv2.example.net/XXX-storage/" \
|| return 2
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
# Petite vérification supplémentaire :
# comme on ne semble pas pouvoir utiliser l'option --exec avec le --stop
# de start-stop-daemon, on vérifie que le PID dans PID_FILE correspond bien
# notre process
PID_CHECK="$( pgrep -f "$DAEMON" )"
if [ -z "$PID_CHECK" ] || [ "$PID_CHECK" != "$( cat "$PIDFILE" 2>/dev/null )" ]; then
return 2
fi
# start-stop-daemon ne semble pas arrêter proprement le inotifywait qui reste
# zombifié jusqu'à son timeout. Du coup, on commence par pkill
pkill --signal INT -P "$PID_CHECK" || return 2
if [ -n "$( pgrep -f "$DAEMON" )" ]; then
start-stop-daemon --stop --quiet --retry=INT/5/KILL/5 --pidfile "$PIDFILE"
fi
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
restart|force-reload)
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
exit 3
;;
esac
:

View file

@ -0,0 +1,54 @@
#!/bin/sh
BASE_REP="/backuppc/backuppc/pc"
MAX_NBLIGNES_INCR=30
LISTING_FULL=$(printf "%s\n\t%s\t%s\n" "Volumes des dernières sauvegardes complètes :" "Nb fichiers" "Taille" )
LISTING_INCR=$(printf "%s\n\t%s\t%s\n" "Volumes des $MAX_NBLIGNES_INCR dernières sauvegardes incrémentales :" "Nb fichiers" "Taille" )
somme_ligne() {
TOTAL_FILES=0
TOTAL_SIZE=0
# note : c'est un peu sale.
# Explications : on fait une boucle basée sur un pipe, les variables dans cette boucle
# deviennent donc locales à cette boucle.
# 'faut que je retrouve comment en sortir proprement
cat - | while read line; do
echo $(( $TOTAL_FILES + $(printf "%s" "$line" | awk 'BEGIN { FS = "\t" } ; { print $5 }' ) ))
TOTAL_FILES=$(( $TOTAL_FILES + $( printf "%s" "$line" | awk 'BEGIN { FS = "\t" } ; { print $5 }' ) ))
TOTAL_SIZE=$(( $TOTAL_SIZE + $( printf "%s" "$line" | awk 'BEGIN { FS = "\t" } ; { print $6 }' ) ))
printf "%d\t%d\n" "$TOTAL_FILES" "$TOTAL_SIZE"
done | tail -n 1
}
cd "$BASE_REP"
for REP in *; do
# On ne prend que les dossiers qui contiennent un fichier non-vide "backups"
if [ ! -d "$REP" ]; then
continue
fi
if [ ! -s "$REP/backups" ]; then
continue
fi
# Listing full
LISTING_FULL="$( printf "%s\n%s\t%s" "$LISTING_FULL" "$REP" "$( cat "$REP/backups" | egrep "^[0-9]+[[:space:]]+full" | tail -n 1 | somme_ligne )" )"
# Listing incrémental
#décompte nb sauv.
NBLIGNES_INCR="$( cat "$REP/backups" | egrep "^[0-9]+[[:space:]]+incr" | wc -l )"
if [ "$NBLIGNES_INCR" -gt $MAX_NBLIGNES_INCR ]; then
NBLIGNES_INCR=$MAX_NBLIGNES_INCR
fi
LISTING_INCR="$( printf "%s\n%s (%dj)\t%s" "$LISTING_INCR" "$REP" "$NBLIGNES_INCR" "$( cat "$REP/backups" | egrep "^[0-9]+[[:space:]]+incr" | tail -n $MAX_NBLIGNES_INCR | somme_ligne )" )"
done
echo "$LISTING_FULL"
echo
echo "$LISTING_INCR"

View file

@ -0,0 +1,177 @@
#!/bin/sh
# Ce script sert à gérer les abonnements d'une adresse email
#
# exemple d'utilisation :
# - supprimer une adresse de toutes les mailings-list (suppression d'un compte)
# - ajouter interactivement une adresse à certaines ML avec un choix par
# défaut (salaries, ...)
#
# note: dans l'autre sens (ie. pour gérer une mailing-list donnée), il vaut
# mieux passer par phpldapadmin ou similaire, c'est plus pratique.
#
# Known bug: ne pas utiliser avec les mailings-lists ou adresses emails
# dont le nom contient un espace.
# Inclusion de la conf. + fonctions de base
. ./lib_gestion-mailinglists.sh
# Initialisation des variables "globales"
MODE_INTERACTIF="0"
MODE_LISTING="0"
AJOUTS=""
SUPPRESSIONS=""
LOGFILE=$( mktemp )
LISTING_MAILING_LISTS=$( afficher_listing_mailinglists )
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 [-i] [-r liste] [-r ALL] [-a liste] [-a ...] mail
$0 -h
-i : mode interactif.
-a liste : ajoute le mail à la liste. En mode interactif, propose par défaut cette liste au prompt.
-r liste : supprime le mail de la liste. ALL = suppression de toutes les listes. En mode interactif, supprime les listes de la proposition.
-l : liste les mailings-lists dans lesquelles le mail apparaît.
Note: les ajouts prennent le pas sur les suppressions.
EOF
}
terminaison() {
if [ "$1" = "" ] || [ "$1" -eq 0 ]; then
rm -f "$LOGFILE"
RETOUR=0
else
if [ -s "$LOGFILE" ]; then
printf "NOTICE: log dispo : %s\n" "$LOGFILE" >&2
else
rm -f "$LOGFILE"
fi
RETOUR="$1"
fi
exit "$RETOUR"
}
# Début du code
# gestion des options de lancement
while getopts a:r:ihl f; do
case $f in
'a')
AJOUTS=$( echo $AJOUTS $OPTARG )
;;
'r')
SUPPRESSIONS=$( echo $SUPPRESSIONS $OPTARG )
;;
'i')
MODE_INTERACTIF=1
;;
'l')
MODE_LISTING=1
;;
'h')
usage
terminaison 0
;;
\?)
usage >&2
terminaison 1
;;
esac
done
shift $( expr $OPTIND - 1 )
MAIL="$1"
# Petite vérif.
if [ "$MAIL" = "" ]; then
echo "ERREUR: aucune adresse email fournie." >&2
usage
terminaison 1
fi
# Mode listing
if [ "$MODE_LISTING" -gt 0 ]; then
afficher_abonnements_en_cours "$MAIL"
terminaison 0
fi
# Les mailings-lists demandées existent-elles ?
TMP=0
for ML in $AJOUTS $SUPPRESSIONS; do
if [ $( printf "%s\nALL\n" "$LISTING_MAILING_LISTS" | egrep -c "^$ML$" ) -ne 1 ]; then
echo "ERREUR: la mailing-list ne semble pas exister : $ML" >&2
TMP=1
fi
done
test "$TMP" -eq 0 || terminaison 1
# Construction de la liste finale des abonnements
if [ $( echo "$SUPPRESSIONS" | grep -wc "ALL" ) -gt 0 ]; then
# Suppression de tous les abonnements (paramètre "-r ALL")
SUPPRESSIONS=$( afficher_abonnements_en_cours "$MAIL" )
fi
LISTE_FINALE=$( (afficher_abonnements_en_cours "$MAIL" | grep -wFv "$( echo "$SUPPRESSIONS" | sed 's/ \+/\n/g' )" ; echo "$AJOUTS" | sed 's/ \+/\n/g') | grep -v "^$" | sort -u )
# Entrée dans le "mode interactif"
BOUCLE_INTERACTIVE=$MODE_INTERACTIF # simple copie cosmétique, histoire de garder le paramètre jusqu'au bout (sait-on jamais :)
while [ "$BOUCLE_INTERACTIVE" -eq 1 ]; do
# Listing des mailing-lists disponibles
echo "Listing des mailing-lists : "
printf "%s\n" "$LISTING_MAILING_LISTS" | sed 's/^/ /'
# Prompt pour la modification du listing
LISTE_FINALE=$( echo $LISTE_FINALE ) # ça permet de convertir facilement en une liste mono-ligne :)
printf "Indiquer les listes auxquelles l'email %s doit être abonné, ligne vide pour terminer\n(. pour ignorer la proposition et annuler les entrées précédentes)\n[%s]\n" "$MAIL" "$LISTE_FINALE"
LISTE_USER=$( while read LINE; do if [ "$LINE" = "" ]; then exit 0; else echo "$LINE"; fi; done )
# écrasement de LISTE_FINALE si l'utilisateur a entré qqchose
if [ "$LISTE_USER" != "" ]; then
LISTE_FINALE=""
fi
# On vérifie chaque entrée (un peu redondant avec la boucle de vérif. précédente)
BOUCLE_INTERACTIVE=0
for ML in $LISTE_USER; do
# Si c'est un "." qui a été rentré, on efface la liste finale
if [ "$ML" = "." ]; then
LISTE_FINALE=""
# sinon, on vérifie simplement que la mailing-list existe
elif [ $( printf "%s\n" "$LISTING_MAILING_LISTS" | egrep -c "^$ML$" ) -ne 1 ]; then
# Si elle n'existe pas: nouveau tour gratuit
echo "ERREUR: la mailing-list ne semble pas exister : $ML" >&2
BOUCLE_INTERACTIVE=1
else
# On concatène (astuce avec echo pour enlever les espaces inutiles)
LISTE_FINALE=$( echo $LISTE_FINALE $ML )
fi
done
done
# - les suppressions
for ML in $( afficher_abonnements_en_cours "$MAIL" | grep -wFv "$( echo "$LISTE_FINALE" | sed 's/ \+/\n/g' )" ); do
printf "Suppression de %s de la mailing-list %s.\n" "$MAIL" "$ML"
supprimer_abonnement "$MAIL" "$ML" >"$LOGFILE" 2>&1 || terminaison 1
done
# - les créations d'abonnement
for ML in $( echo "$LISTE_FINALE" | sed 's/ \+/\n/g' | grep -wFv "$( afficher_abonnements_en_cours "$MAIL" )" ); do
printf "Ajout de %s à la mailing-list %s.\n" "$MAIL" "$ML"
creer_abonnement "$MAIL" "$ML" >"$LOGFILE" 2>&1 || terminaison 1
done
# Fin du script - nettoyage
terminaison 0

View file

@ -0,0 +1,214 @@
#!/bin/sh
# Ce script sert à créer/supprimer une mailing-list
#
# exemple d'utilisation :
# - créer une mailing-list et lui ajouter des abonnés,
# - retirer des abonnés d'une mailing-list,
# - supprimer une mailing-list
#
# note: dans l'autre sens (ie. gérer les abonnements d'une adresse donnée),
# il existe le script_ml-gestion-abonne.sh
#
# Known bug: ne pas utiliser avec les mailings-lists ou adresses emails
# dont le nom contient un espace.
# Inclusion de la conf. + fonctions de base
. ./lib_gestion-mailinglists.sh
# Initialisation des variables "globales"
MODE_INTERACTIF="0"
MODE_LISTING_ABONNES="0"
CREATION_SUPPRESSION="0" # 0: neutre, -1: suppression, 1: creation
AJOUTS=""
SUPPRESSIONS=""
LOGFILE=$( mktemp )
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 [-i] [ -c | -d ] [-r email] [-r ALL] [-a email] [-a ...] mail
$0 -L
$0 -h
-i : mode interactif.
-c : crée la mailing-list.
-d : supprime la mailing-list.
-a mail : ajoute le mail à la liste. En mode interactif, propose par défaut cet
email au prompt.
-r mail : supprime le mail de la liste. ALL = suppression de tous les abonnés.
En mode interactif, supprime les listes de la proposition.
-l : liste les abonnés de la mailing-list.
-L : liste les mailing-list
Note: les ajouts prennent le pas sur les suppressions.
EOF
}
terminaison() {
if [ "$1" = "" ] || [ "$1" -eq 0 ]; then
rm -f "$LOGFILE"
RETOUR=0
else
if [ -s "$LOGFILE" ]; then
printf "NOTICE: log dispo : %s\n" "$LOGFILE" >&2
else
rm -f "$LOGFILE"
fi
RETOUR="$1"
fi
exit "$RETOUR"
}
# Début du code
# gestion des options de lancement
while getopts cda:r:ihlL f; do
case $f in
'c')
CREATION_SUPPRESSION=1
;;
'd')
CREATION_SUPPRESSION=-1
;;
'a')
AJOUTS=$( echo $AJOUTS $OPTARG )
;;
'r')
SUPPRESSIONS=$( echo $SUPPRESSIONS $OPTARG )
;;
'i')
MODE_INTERACTIF=1
;;
'l')
MODE_LISTING_ABONNES=1
;;
'L')
afficher_listing_mailinglists
terminaison 0
;;
'h')
usage
terminaison 0
;;
\?)
usage >&2
terminaison 1
;;
esac
done
shift $( expr $OPTIND - 1 )
MAILINGLIST="$1"
# Petite vérif.
if [ "$MAILINGLIST" = "" ]; then
echo "ERREUR: aucune mailing-list n'a été indiquée." >&2
usage
terminaison 1
fi
# Mode listing des abonnes d'une mailing-linst
if [ "$MODE_LISTING_ABONNES" -gt 0 ]; then
# On vérifie quand même que la mailing-list existe...
if [ $( afficher_listing_mailinglists | grep -wc "^$MAILINGLIST$" ) -ne 1 ]; then
echo "ERREUR: mailing-list inexistante." >&2
terminaison 1
fi
# Listing des abonnés de la mailing-list
afficher_abonnes_mailinglist "$MAILINGLIST"
terminaison 0
fi
# La mailing-list demandée existe-elle ?
if [ $( printf "%s\n" "$( afficher_listing_mailinglists )" | egrep -c "^$MAILINGLIST$" ) -ne 1 ]; then
# Si elle n'existe pas, doit-on la créer ?
if [ $CREATION_SUPPRESSION -lt 1 ]; then
echo "ERREUR: la mailing-list ne semble pas exister : $MAILINGLIST" >&2
terminaison 1
else
# On crée la mailing-list
printf "Création de la mailing-list..."
if creer_mailinglist "$MAILINGLIST" >"$LOGFILE" 2>&1; then
printf " [OK]\n"
else
printf "\nProblèmes à la création de la mailing-list. Cf log.\n" >&2
terminaison 1
fi
fi
else
# Si elle existe, doit-on la supprimer ?
if [ "$CREATION_SUPPRESSION" -lt 0 ]; then
# On la supprime et on arrête là le script
printf "Suppression de la mailing-list..."
if supprimer_mailinglist "$MAILINGLIST" >"$LOGFILE" 2>&1; then
printf "[OK]\n";
terminaison 0
else
printf "\nProblèmes à la suppression de la mailing-list. Cf log.\n" >&2
terminaison 1
fi
fi
fi
# Construction de la liste finale des abonnements
if [ $( echo "$SUPPRESSIONS" | grep -wc "ALL" ) -gt 0 ]; then
# Suppression de tous les abonnés (paramètre "-r ALL")
SUPPRESSIONS=$( afficher_abonnes_mailinglist "$MAILINGLIST" )
fi
LISTE_FINALE=$( (afficher_abonnes_mailinglist "$MAILINGLIST" | grep -wFv "$( echo "$SUPPRESSIONS" | sed 's/ \+/\n/g' )" ; echo "$AJOUTS" | sed 's/ \+/\n/g') | grep -v "^$" | sort -u )
# Entrée dans le "mode interactif"
BOUCLE_INTERACTIVE=$MODE_INTERACTIF # simple copie cosmétique, histoire de garder le paramètre jusqu'au bout (sait-on jamais :)
while [ "$BOUCLE_INTERACTIVE" -eq 1 ]; do
# Prompt pour la modification du listing
LISTE_FINALE=$( echo $LISTE_FINALE ) # ça permet de convertir facilement en une liste mono-ligne :)
printf "Indiquer les emails qui seront abonnés à la liste '%s', ligne vide pour terminer\n(. pour ignorer la proposition et annuler les entrées précédentes)\n[%s]\n" "$MAILINGLIST" "$LISTE_FINALE"
LISTE_USER=$( while read LINE; do if [ "$LINE" = "" ]; then exit 0; else echo "$LINE"; fi; done )
# écrasement de LISTE_FINALE si l'utilisateur a entré qqchose
if [ "$LISTE_USER" != "" ]; then
LISTE_FINALE=""
fi
# On vérifie chaque entrée (un peu redondant avec la boucle de vérif. précédente)
BOUCLE_INTERACTIVE=0
for MAIL in $LISTE_USER; do
# Si c'est un "." qui a été rentré, on efface la liste finale
if [ "$MAIL" = "." ]; then
LISTE_FINALE=""
else
# sinon, on concatène (astuce avec echo pour enlever les espaces inutiles)
LISTE_FINALE=$( echo $LISTE_FINALE $MAIL )
fi
done
done
# - les suppressions
for MAIL in $( afficher_abonnes_mailinglist "$MAILINGLIST" | grep -wFv "$( echo "$LISTE_FINALE" | sed 's/ \+/\n/g' )" ); do
printf "Suppression de %s de la mailing-list %s.\n" "$MAIL" "$MAILINGLIST"
supprimer_abonnement "$MAIL" "$MAILINGLIST" >"$LOGFILE" 2>&1 || terminaison 1
done
# - les créations d'abonnement
for MAIL in $( echo "$LISTE_FINALE" | sed 's/ \+/\n/g' | grep -wFv "$( afficher_abonnes_mailinglist "$MAILINGLIST" )" ); do
printf "Ajout de %s à la mailing-list %s.\n" "$MAIL" "$MAILINGLIST"
creer_abonnement "$MAIL" "$MAILINGLIST" >"$LOGFILE" 2>&1 || terminaison 1
done
# Fin du script - nettoyage
terminaison 0

View file

@ -0,0 +1,591 @@
#!/bin/sh
# Ce script sert à créer ou détruire :
# - une instance PostgreSQL (PGDATA).
# Il peut également gérer un serveur esclave en définissant la
# variable $ESCLAVE . Dans ce cas là, mieux vaut avoir une authentification
# par clef (les opérations se font par ssh)
#
# Attention, ce script affiche les identifiants à l'écran.
# Prérequis :
# - PostgreSQL
# Si utilisation d'un esclave :
# - une authentification pour l'utilisateur root local vers root@$ESCLAVE par clef,
# - une authentification pour l'utilisateur postgres local vers postgres@$ESCLAVE par clef.
# Trucs à nettoyer (si quelque chose part en vrille, ou simplement
# pour désinstaller ):
# - TODO
# Version de PostgreSQL : 9.1 seule testée
VERSION_POSTGRESQL="9.1"
# Mettre ci-dessous le nom d'hôte ou l'IP du serveur esclave
# (utilisé par la config PG + les connexions SSH pour l'exécution
# des commandes distantes de ce script)
ESCLAVE=""
# idem pour le maître (vu depuis l'esclave en cas de réseau privé)
# (utilisé pour la config PG)
MAITRE=""
# Initialisation des variables globales
MODE_CREATION=0
MODE_DESTRUCTION=0
POSTGRESQL_REPLICATION_USER_NAME="repliquser"
POSTGRESQL_LOCALE="fr_FR.utf8"
POSTGRESQL_PORT=""
# Par défaut, on arrête le script à la première erreur non "catchée"
set -e
# On s'exécute dans un dossier accessible à l'utilisateur postgres
cd /tmp
# Fonctions
# affiche le message d'aide
usage() {
cat <<EOF
$0 -c cluster-name [ -e <hostname de l'esclave> -m <hostname du maître> ] [ -p <numéro de port> ]
$0 -d cluster-name [ -e <hostname de l'esclave> -m <hostname du maître> ]
$0 -h
-c xxx : création d'une instance/cluster nommée 'xxx'
-d xxx : destruction de l'instance nommée 'xxx'
-p nnn : numéro de port à affecter au nouveau cluster (par défaut, 'auto')
# Ce script peut générer la configuration nécessaire au streaming replication
-e esclave : nom d'hôte ou IP de l'esclave
-m maitre : nom d'hôte ou IP du serveur maître
-h : ce message d'aide
EOF
}
# Arguments
# $1 : IPv4, IPv6 ou nom d'hôte
# Cette fonction suffixe d'un /32 ou /128
# la chaîne si celle-ci est une adresse IP
add_mask_ip() {
test -n "$1" || exit 1
if [ $( echo "$1" | egrep -c "^([[:digit:]]{1,3}\.){3}[[:digit:]]{1,3}$" ) -gt 0 ]; then
printf "$1/32"
elif [ $( echo "$1" | egrep -c "^[[:xdigit:]:]*$" ) -gt 0 ]; then
printf "$1/128"
else
printf "$1"
fi
}
# Arguments
# $1 : IP esclave
# $2 : version PostgreSQL
# $3 : nom de l'instance
generate_patch_postgresqlconf_master() {
test -n "$1" && test -n "$2" && test -n "$3" || exit 1
cat <<EOF
@@ -56,6 +56,8 @@
# - Connection Settings -
+# Script MI PostgreSQL : écoute sur toutes les interfaces nécessaire pour la streaming replication
+listen_addresses = '*'
#listen_addresses = 'localhost' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost', '*' = all
@@ -150,6 +152,8 @@ shared_buffers = 24MB # min 128kB
# - Settings -
+# Script MI PostgreSQL : serveur maître
+wal_level = hot_standby
#wal_level = minimal # minimal, archive, or hot_standby
# (change requires restart)
#fsync = on # turns forced synchronization on or off
@@ -178,9 +182,10 @@ shared_buffers = 24MB # min 128kB
# - Archiving -
-#archive_mode = off # allows archiving to be done
+# Script MI PostgreSQL : Serveur maître: activation de l'archivage et de l'envoi vers l'esclave
+archive_mode = on # allows archiving to be done
# (change requires restart)
-#archive_command = '' # command to use to archive a logfile segment
+archive_command = 'scp -q -B -C %p postgres@$1:/var/lib/postgresql/$2/$3/archives_from_master/%f' # command to use to archive a logfile segment
#archive_timeout = 0 # force a logfile segment switch after this
# number of seconds; 0 disables
@@ -193,10 +198,12 @@
# These settings are ignored on a standby server
+max_wal_senders = 3
#max_wal_senders = 0 # max number of walsender processes
# (change requires restart)
#wal_sender_delay = 1s # walsender cycle time, 1-10000 milliseconds
-#wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables
+# Script MI PostgreSQL : serveur maître: conservation des WAL
+wal_keep_segments = 32 # in logfile segments, 16MB each; 0 disables
#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
#replication_timeout = 60s # in milliseconds; 0 disables
#synchronous_standby_names = '' # standby servers that provide sync rep
@@ -207,6 +213,9 @@ shared_buffers = 24MB # min 128kB
# These settings are ignored on a master server
+# Script MI PostgreSQL : mis en place sur le maître comme sur l'esclave, pour homogénéiser
+# comme il est ignoré sur le maître, ça ne pose pas de souci :)
+hot_standby = on
#hot_standby = off # "on" allows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s # max delay before canceling queries
EOF
}
# Arguments
# $1 : nom de l'instance
generate_patch_postgresql_pghbaconf() {
test -n "$1" || exit 1
cat <<EOF
@@ -87,11 +87,11 @@ local all postgres peer
# TYPE DATABASE USER ADDRESS METHOD
# "local" is for Unix domain socket connections only
-local all all peer
+local $1 $1 md5
# IPv4 local connections:
-host all all 127.0.0.1/32 md5
+host $1 $1 127.0.0.1/32 md5
# IPv6 local connections:
-host all all ::1/128 md5
+host $1 $1 ::1/128 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
#local replication postgres peer
EOF
}
# Pas d'arguments
generate_patch_postgresqlconf_slave() {
cat <<EOF
@@ -56,6 +56,8 @@
# - Connection Settings -
+# Script MI PostgreSQL : écoute sur toutes les interfaces nécessaire pour la streaming replication
+listen_addresses = '*'
#listen_addresses = 'localhost' # what IP address(es) to listen on;
# comma-separated list of addresses;
# defaults to 'localhost', '*' = all
@@ -150,6 +152,8 @@ shared_buffers = 24MB # min 128kB
# - Settings -
+# Script MI PostgreSQL : serveur esclave
+wal_level = hot_standby
#wal_level = minimal # minimal, archive, or hot_standby
# (change requires restart)
#fsync = on # turns forced synchronization on or off
@@ -193,10 +198,12 @@
# These settings are ignored on a standby server
+max_wal_senders = 3
#max_wal_senders = 0 # max number of walsender processes
# (change requires restart)
#wal_sender_delay = 1s # walsender cycle time, 1-10000 milliseconds
-#wal_keep_segments = 0 # in logfile segments, 16MB each; 0 disables
+# Script MI PostgreSQL : serveur maître: conservation des WAL
+wal_keep_segments = 32 # in logfile segments, 16MB each; 0 disables
#vacuum_defer_cleanup_age = 0 # number of xacts by which cleanup is delayed
#replication_timeout = 60s # in milliseconds; 0 disables
#synchronous_standby_names = '' # standby servers that provide sync rep
@@ -207,6 +213,9 @@ shared_buffers = 24MB # min 128kB
# These settings are ignored on a master server
+# Script MI PostgreSQL : mis en place sur le maître comme sur l'esclave, pour homogénéiser
+# comme il est ignoré sur le maître, ça ne pose pas de souci :)
+hot_standby = on
#hot_standby = off # "on" allows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s # max delay before canceling queries
EOF
}
# Arguments:
# $1 : IP maître
# $2 : port maître (idem esclave, en fait)
# $3 : utilisateur dédié à la réplication
# $4 : mot de passe dudit utilisateur
# $5 : version de PostgreSQL
# $6 : nom de l'instance
# $7 : chemin complet de pg_archivecleanup (si vide, désactivé)
generate_file_postgresqlrecoveryconf_slave() {
local ARCHIVE_CLEANUP_COMMAND
test -n "$1" && test -n "$2" && test -n "$3" && test -n "$4" && test -n "$5" && test -n "$6" || exit 1
if ! test -n "$7"; then
ARCHIVE_CLEANUP_COMMAND="#archive_cleanup_command = 'pg_archivecleanup /var/lib/postgresql/$5/$6/archives_from_master %r'"
else
ARCHIVE_CLEANUP_COMMAND="archive_cleanup_command = '$7 /var/lib/postgresql/$5/$6/archives_from_master %r'"
fi
cat <<EOF
# Note that recovery.conf must be in \$PGDATA directory.
# Specifies whether to start the server as a standby. In streaming replication,
# this parameter must be set to on.
standby_mode = 'on'
# Specifies a connection string which is used for the standby server to connect
# with the primary.
primary_conninfo = 'host=$1 port=$2 user=$3 password=$4'
# Specifies a trigger file whose presence should cause streaming replication to
# end (i.e., failover).
# Cf pg_ctlcluster promote pour une alternative
trigger_file = '/var/lib/postgresql/trigger-postgresql'
# Specifies a command to load archive segments from the WAL archive. If
# wal_keep_segments is a high enough number to retain the WAL segments
# required for the standby server, this may not be necessary. But
# a large workload can cause segments to be recycled before the standby
# is fully synchronized, requiring you to start again from a new base backup.
restore_command = 'cp /var/lib/postgresql/$5/$6/archives_from_master/%f "%p"'
# Specifies a command to clean up obsolete archive logs
$ARCHIVE_CLEANUP_COMMAND
EOF
}
# Arguments:
# $1 : Nom de la bdd / de l'utilisateur
# $2 : mot de passe de l'utilisateur
generate_script_postgresql_creation_db_et_user() {
local NOM_INSTANCE POSTGRESQL_PASSWORD
test -n "$1" && test -n "$2" || exit 1
NOM_INSTANCE="$1"
POSTGRESQL_PASSWORD="$2"
cat <<EOF
-- Création de l'utilisateur principal
CREATE USER "$NOM_INSTANCE" WITH PASSWORD '$POSTGRESQL_PASSWORD' CONNECTION LIMIT 50;
-- Restriction des privilèges par défaut
-- Cette partie du script vient en grande partie de :
-- http://wiki.postgresql.org/wiki/Shared_Database_Hosting
-- Connexion à template1
\c template1
-- Révocation sur la base template1
REVOKE ALL ON DATABASE template1 FROM public;
REVOKE ALL ON SCHEMA public FROM public;
GRANT ALL ON SCHEMA public TO postgres;
-- Ce passage-là est plus parano, mais à étudier avant suppression
REVOKE ALL ON pg_user FROM public;
REVOKE ALL ON pg_roles FROM public;
REVOKE ALL ON pg_group FROM public;
REVOKE ALL ON pg_authid FROM public;
REVOKE ALL ON pg_auth_members FROM public;
REVOKE ALL ON pg_database FROM public;
REVOKE ALL ON pg_tablespace FROM public;
REVOKE ALL ON pg_settings FROM public;
-- Création de la nouvelle base de données
CREATE DATABASE "$NOM_INSTANCE" WITH OWNER = "$NOM_INSTANCE" ENCODING = 'UTF8';
REVOKE ALL ON DATABASE "$NOM_INSTANCE" FROM public;
\c $NOM_INSTANCE
REVOKE ALL ON SCHEMA public FROM public;
GRANT ALL ON SCHEMA public TO "$NOM_INSTANCE";
EOF
}
# Arguments:
# $1 : nom de l'utilisateur dédié à la réplication
# $2 : mot de passe dudit utilisateur
generate_script_postgresql_creation_replication_user() {
test -n "$1" && test -n "$2" || exit 1
POSTGRESQL_REPLICATION_USER_NAME="$1"
POSTGRESQL_REPLICATION_PASSWORD="$2"
cat <<EOF
-- Création de l'utilisateur utilisé par l'esclave pour la streaming replication
CREATE USER $POSTGRESQL_REPLICATION_USER_NAME WITH PASSWORD '$POSTGRESQL_REPLICATION_PASSWORD' REPLICATION;
EOF
}
# Arguments
# none
log_alias() {
# Cette fonction est utilisée pour "capturer" les sorties des différentes
# commandes (hors messages générés par le script lui-même)
# Note : elle peut être modifiée pour envoyer les messages vers un fichier
# de log temporaire. Ça serait plus propre.
sed 's/^/ /'
}
fancylog() {
BEGINNING="$( printf "%s" "$1" | head -n 1 | sed -n "s/^\([A-Z]\+\):.*/\1/p" )"
if [ -n "$BEGINNING" ] && [ -t 1 ] && tput setaf 1 >/dev/null 2>&1; then
case "$BEGINNING" in
"ERREUR"|"ERROR")
tput setaf 1;;
"WARN"|"WARNING")
tput setaf 3;;
"INFO"|"DEBUG")
tput setaf 2;;
esac
printf "%s" "$BEGINNING"
tput op
printf "%s\n" "$( printf "%s" "$1" | sed '1 s/^[A-Z]\+:/:/' )"
else
printf "%s\n" "$1"
fi
}
# Début du code
# gestion des options de lancement
while getopts c:d:e:m:p:h f; do
case $f in
'c')
MODE_CREATION=1
NOM_INSTANCE="$OPTARG"
;;
'd')
MODE_DESTRUCTION=1
NOM_INSTANCE="$OPTARG"
;;
'e')
ESCLAVE="$OPTARG"
;;
'm')
MAITRE="$OPTARG"
;;
'p')
if [ "$OPTARG" = "auto" ]; then
unset POSTGRESQL_PORT
else
POSTGRESQL_PORT="$( printf "%d" "$OPTARG" )"
fi
;;
'h')
usage
exit 0
;;
\?)
usage >&2
exit 1
;;
esac
done
#(code inutile, mais que je garde parce qu'on ne sait jamais)
#shift $( expr $OPTIND - 1 )
#DATA="$1"
if [ -n "$MAITRE" ] && [ -n "$ESCLAVE" ]; then
MAITRE_WITH_MASK=$( add_mask_ip "$MAITRE" )
ESCLAVE_WITH_MASK=$( add_mask_ip "$ESCLAVE" )
fi
# Petite vérif.
case $(( $MODE_CREATION + $MODE_DESTRUCTION )) in
'0')
fancylog "ERREUR: veuillez choisir entre création et destruction." >&2
usage >&2
exit 1
;;
'2')
fancylog "ERREUR: tu veux créer et détruire en même temps, petit malin ?" >&2
exit 1
;;
esac
# Petites vérifications préliminaires :
fancylog "INFO: petites vérifications préliminaires..."
# - vérifications sur le nom du clustername fourni
OLD_LC_ALL="$LC_ALL"
export LC_ALL=C
export LANG=C
if [ $( printf "$NOM_INSTANCE" | egrep -c "^[a-z][a-z0-9_]{0,14}$" ) -ne 1 ]; then
fancylog "ERREUR: clustername vide ou contenant des caractères interdits : regexp : [a-z][a-z0-9_]{0,14}" >&2
exit 1
fi
export LC_ALL="$OLD_LC_ALL"
export LANG="$OLD_LC_ALL"
# - est-ce que l'auth. SSH par clef fonctionne pour root et postgres ?
if [ -n "$ESCLAVE" ]; then
# - vérification de l'auth. par clefs SSH
if ! ssh -o BatchMode=yes root@$ESCLAVE /bin/true; then
fancylog "ERREUR: auth. par clef SSH non configurée pour l'utilisateur 'root'."
exit 1
fi
if ! su -c "ssh -o BatchMode=yes postgres@$ESCLAVE /bin/true" postgres; then
fancylog "ERREUR: auth. par clef SSH non configurée pour l'utilisateur 'postgres'."
exit 1
fi
# - vérification de la présence de $MAITRE
if [ ! -n "$MAITRE" ]; then
fancylog "ERREUR: serveur esclave indiqué, mais pas de serveur maître (option -m)." >&2
exit 1
fi
fi
# - est-ce qu'on est bien sur une Debian 6 ou 7
# (c'est surtout pour éviter les mauvaises surprises, ce script n'ayant pas été testé ailleurs)
if [ ! -f "/etc/debian_version" ] || [ $( egrep -c "^(6.|7.)" /etc/debian_version ) -ne 1 ]; then
fancylog "ERREUR: mauvaise version de Debian détectée. Arrêt par prudence. Vérifiez le code de ce script avant de forcer." >&2
exit 1
fi
if [ -n "$ESCLAVE" ] && [ "$( ssh root@$ESCLAVE 'egrep -c "^(6.|7.)" /etc/debian_version' )" -ne 1 ]; then
fancylog "ERREUR: ESCLAVE: mauvaise version de Debian détectée. Arrêt par prudence. Vérifiez le code de ce script avant de forcer." >&2
exit 1
fi
# - est-ce que la version attendue de PostgreSQL est bien installée ?
if ! dpkg -s "postgresql-$VERSION_POSTGRESQL" >/dev/null 2>&1; then
fancylog "ERREUR: PostgreSQL $VERSION_POSTGRESQL ne semble pas installée." >&2
exit 1
fi
if [ -n "$ESCLAVE" ] && ! ssh root@$ESCLAVE dpkg -s "postgresql-$VERSION_POSTGRESQL" >/dev/null 2>&1; then
fancylog "ERREUR: PostgreSQL $VERSION_POSTGRESQL ne semble pas installée." >&2
exit 1
fi
# Fin des vérifications
fancylog "INFO: fin des vérifications. Lancement des opérations."
if [ "$MODE_CREATION" -eq 1 ]; then
POSTGRESQL_PASSWORD=$( dd if=/dev/random 2>/dev/null bs=1 count=10 status=noxfer | base64 | sed 's#[/=]##g' )
POSTGRESQL_REPLICATION_PASSWORD=$( dd if=/dev/random 2>/dev/null bs=1 count=20 status=noxfer | base64 | sed 's#[/=]##g' )
# Création de l'instance dédiée
fancylog "INFO: création du cluster PG primaire..."
if [ -z "$POSTGRESQL_PORT" ]; then
pg_createcluster --locale=$POSTGRESQL_LOCALE --start-conf=auto "$VERSION_POSTGRESQL" "$NOM_INSTANCE"
# Détermination du port généré
# TODO: récupérer ça via pg_lsclusters serait plus "propre"
POSTGRESQL_PORT=$( grep "^port" /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/postgresql.conf | sed 's/^port[[:space:]]*=[[:space:]]*\([0-9]\+\).*$/\1/' )
else
pg_createcluster --locale=$POSTGRESQL_LOCALE --start-conf=auto --port "$POSTGRESQL_PORT" "$VERSION_POSTGRESQL" "$NOM_INSTANCE"
fi
fancylog "INFO: modification du pg_hba.conf..."
if ! generate_patch_postgresql_pghbaconf "$NOM_INSTANCE" | patch -s --dry-run /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf; then
fancylog "ERREUR: modification du fichier pg_hba.conf en échec. Arrêt."
exit 1
fi
generate_patch_postgresql_pghbaconf "$NOM_INSTANCE" | patch -s /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf
fancylog "INFO: démarrage de l'instance PostgreSQL..."
pg_ctlcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE" start
# Lancement du script fourre-tout
fancylog "INFO: création de l'utilisateur et de la base de données, et affinage des droits d'accès..."
generate_script_postgresql_creation_db_et_user "$NOM_INSTANCE" "$POSTGRESQL_PASSWORD" "$POSTGRESQL_REPLICATION_USER_NAME" "$POSTGRESQL_REPLICATION_PASSWORD" | su -c "psql -p $POSTGRESQL_PORT -v ON_ERROR_STOP=1" postgres
if [ -n "$ESCLAVE" ]; then
fancylog "INFO: création de l'utilisateur de réplication..."
generate_script_postgresql_creation_replication_user "$POSTGRESQL_REPLICATION_USER_NAME" "$POSTGRESQL_REPLICATION_PASSWORD" | su -c "psql -p $POSTGRESQL_PORT -v ON_ERROR_STOP=1" postgres
# configuration en tant que maître
fancylog "INFO: modification de la configuration BDD de l'instance primaire..."
if ! generate_patch_postgresqlconf_master "$ESCLAVE" "$VERSION_POSTGRESQL" "$NOM_INSTANCE" | patch -s --dry-run /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/postgresql.conf; then
fancylog "ERREUR: modification du fichier postgresql.conf en échec. Arrêt."
exit 1
fi
generate_patch_postgresqlconf_master "$ESCLAVE" "$VERSION_POSTGRESQL" "$NOM_INSTANCE" | patch -s /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/postgresql.conf
# On rajoute les lignes pour la connexion streaming replication dans le pg_hba.conf
echo "host replication $POSTGRESQL_REPLICATION_USER_NAME $ESCLAVE_WITH_MASK md5" >>/etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf
echo "host replication $POSTGRESQL_REPLICATION_USER_NAME $MAITRE_WITH_MASK md5" >>/etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf
# Création sur l'esclave
# Note: on commence par créer l'arborescence de l'esclave pour
# avoir le répertoire de destination des archives logs
# le plus tôt possible (en tout cas, avant le pg_stop_backup()
# qui envoie forcément un WAL)
fancylog "INFO: création du cluster PG secondaire..."
ssh "root@$ESCLAVE" "cd /tmp; pg_createcluster --locale='$POSTGRESQL_LOCALE' -p '$POSTGRESQL_PORT' --start-conf=auto '$VERSION_POSTGRESQL' '$NOM_INSTANCE'"
fancylog "INFO: création du dossier de réception des archives logs et arret du backup..."
su -c "mkdir '/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/archives_from_master'" postgres
su -c "ssh postgres@$ESCLAVE mkdir '/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/archives_from_master'" postgres
fancylog "INFO: redémarrage du serveur primaire..."
pg_ctlcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE" restart
# rsync du PGDATA vers l'esclave
fancylog "INFO: écrasement du PGDATA secondaire par celui de l'instance primaire..."
echo "SELECT pg_start_backup('script_mi_pg', true);" | su -c "psql -p $POSTGRESQL_PORT -v ON_ERROR_STOP=1" postgres
# on attend un peu, il y a parfois des fichiers WAL qui "vanished" pendant le rsync
sleep 1
# On exclut :
# - postmaster.pid : raison évidente :)
# - postmaster.opts : a priori, le contenu est identique mais par prudence...
# - server.key/server.crt : rsync génère une erreur sur la date de modification des symlinks :-/
# - recovery.* : le recovery.done du maitre référence l'esclave, et vice versa pour le recovery.conf de l'esclave
# - archives_from_master : cela écraserait tout éventuel WAL déjà envoyé
# - lost+found : au cas où la partition existe déjà (pg_createcluster se bloque mais ça permet de récupérer cette ligne de commande ailleurs :)
# A étudier : pas de --delete ?
# test -c car problème de synchro bizarre
su -c "rsync -aHc --exclude=/postmaster.pid --exclude=/postmaster.opts --exclude=/server.key --exclude=/server.crt --exclude=/recovery.conf --exclude=/recovery.done --exclude=/archives_from_master --exclude=lost+found /var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/ postgres@$ESCLAVE:/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/" postgres
echo "SELECT pg_stop_backup();" | su -c "psql -p $POSTGRESQL_PORT -v ON_ERROR_STOP=1" postgres
# configuration de l'esclave
fancylog "INFO: configuration de l'instance PG secondaire..."
if ! ssh root@$ESCLAVE dpkg -s "postgresql-contrib-$VERSION_POSTGRESQL" >/dev/null 2>&1; then
fancylog "WARNING: Le paquet postgresql-contrib-$VERSION_POSTGRESQL ne semble pas installé sur le serveur esclave, archive_cleanup_command sera désactivé." >&2
fancylog "NOTICE: cf fichier /var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/recovery.conf en cas d'installation après-coup." >&2
ARCHIVE_CLEANUP_COMMAND=""
else
# TODO: à rendre plus portable ?
ARCHIVE_CLEANUP_COMMAND="/usr/lib/postgresql/$VERSION_POSTGRESQL/bin/pg_archivecleanup"
fi
generate_file_postgresqlrecoveryconf_slave "$MAITRE" "$POSTGRESQL_PORT" "$POSTGRESQL_REPLICATION_USER_NAME" "$POSTGRESQL_REPLICATION_PASSWORD" "$VERSION_POSTGRESQL" "$NOM_INSTANCE" "$ARCHIVE_CLEANUP_COMMAND" | su -c "ssh postgres@$ESCLAVE 'cat - >/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/recovery.conf'" postgres
generate_file_postgresqlrecoveryconf_slave "$ESCLAVE" "$POSTGRESQL_PORT" "$POSTGRESQL_REPLICATION_USER_NAME" "$POSTGRESQL_REPLICATION_PASSWORD" "$VERSION_POSTGRESQL" "$NOM_INSTANCE" "$ARCHIVE_CLEANUP_COMMAND" | su -c "cat - >/var/lib/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/recovery.done" postgres
generate_patch_postgresqlconf_slave | ssh "root@$ESCLAVE" patch -s /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/postgresql.conf
# (note: même fichier pg_hba.conf pour tout le monde, homogénéisation avec peu de risques il me semble)
scp -q /etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf root@$ESCLAVE:/etc/postgresql/$VERSION_POSTGRESQL/$NOM_INSTANCE/pg_hba.conf
# démarrage des instances maître et esclave
fancylog "INFO: démarrage de l'instance PostgreSQL esclave..."
ssh root@$ESCLAVE pg_ctlcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE" start
# Vérification de la réplication
# TODO (via numéro de transaction (requête) ? processus/connexion ?)
fi
# Fin
fancylog "INFO: Création terminée. Instance PostgreSQL allumée."
fancylog "INFO: pour mémo :"
fancylog "INFO: BDD: $NOM_INSTANCE, port: $POSTGRESQL_PORT, login: $NOM_INSTANCE, mdp: $POSTGRESQL_PASSWORD"
TMP="$MAITRE"
if [ -z "$MAITRE" ]; then
TMP="localhost"
fi
fancylog "INFO: pgpass: $TMP:$POSTGRESQL_PORT:$NOM_INSTANCE:$NOM_INSTANCE:$POSTGRESQL_PASSWORD"
fi
if [ "$MODE_DESTRUCTION" -eq 1 ]; then
fancylog "INFO: suppression de l'instance locale..."
# Note : en guise de garde-fou, on ne met pas l'option --stop
pg_dropcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE"
if [ -n "$ESCLAVE" ]; then
fancylog "INFO: suppression de l'instance esclave..."
ssh root@$ESCLAVE pg_dropcluster "$VERSION_POSTGRESQL" "$NOM_INSTANCE"
fi
fi

View file

@ -0,0 +1,453 @@
#!/bin/sh
# Ce script sert à créer ou détruire une instance Tomcat
# en créant un CATALINA_BASE indépendant.
# L'usage habituel se fait sur une Debian 7 "Wheezy" avec
# tomcat, libapache2-mod-jk et apache2 installé.
# Prérequis :
# - Debian Wheezy (Squeeze non testée)
# - tomcat7 (6 non testée)
# - libapache2-mod-jk avec le workers.properties du module
# Trucs à nettoyer (si quelque chose part en vrille, ou simplement
# pour désinstaller ):
# - supprimer tout le répertoire REP_TOMCAT_MULTIINSTANCE
# - supprimer /etc/default/tomcatN_XXX
# - supprimer /etc/init.d/tomcatN_XXX
# - supprimer les utilisateurs tomcatN_YYYY
# - nettoyer les clusters PostgreSQL
# Version de Tomcat : 6 ou 7
# TODO : voir si on peut le passer en paramètre pour gérer
# Tomcat 6 & 7 sur la même machine
VERSION_TOMCAT="7"
# Quelques constantes internes
MAX_INSTANCES=999
START_RANGE_TOMCATUID=8000
START_RANGE_TOMCAT_PORT_SYS=15000
START_RANGE_TOMCAT_PORT_HTTP=18000
START_RANGE_TOMCAT_PORT_AJP=19000
# Initialisation des variables globales
MODE_CREATION=0
MODE_DESTRUCTION=0
TOMCAT_ENABLE_SHUTDOWN_PORT=""
OPTION_WORKERS_PROPERTIES=""
OPTION_VHOST_APACHE_SERVERNAME=""
NB_CHIFFRES_DANS_NUM_INSTANCE=$( printf "%d" "$MAX_INSTANCES" | wc -c )
REP_TOMCAT_MULTIINSTANCE="/var/lib/tomcat$VERSION_TOMCAT-multiinstances"
# On récupère également le nom de l'utilisateur et du groupe de Tomcat
if [ ! -f /etc/default/tomcat$VERSION_TOMCAT ]; then
echo "ERREUR: /etc/default/tomcat$VERSION_TOMCAT inaccessible." >&2
exit 1
fi
. /etc/default/tomcat$VERSION_TOMCAT
# Par défaut, on arrête le script à la première erreur non "catchée"
set -e
# Fonctions
# Arguments
# $1 : Version de Tomcat
# $2 : nom du worker
# $3 : port AJP du worker
ajout_worker_properties() {
test -n "$1" && test -n "$2" && test -n "$3" || exit 1
# On se fiche un peu de la version de Tomcat, en fait :)
case "$1" in
6|7)
cat <<EOF
# Script MI Tomcat : ajout du worker $2
worker.$2.port = $3
worker.$2.host = localhost
worker.$2.type = ajp13
EOF
;;
*)
echo "ERREUR: version de Tomcat non-gérée." >&2
exit 1
;;
esac
}
# Arguments
# $1 : Version de Tomcat
# $2 : port système (d'habitude 8005)
# $3 : port connecteur HTTP (8080, d'habitude)
# $4 : port connecteur AJP (8009, d'habitude)
generate_patch_tomcat_server_xml() {
test -n "$1" && test -n "$2" && test -n "$3" && test -n "$4" && test -n "$5" || exit 1
case "$1" in
6|7)
generate_patch_tomcat_server_xml_6 "$2" "$3" "$4" "$5"
;;
*)
echo "ERREUR: version de Tomcat non-gérée." >&2
exit 1
;;
esac
}
# Arguments
# $1 : port système (d'habitude 8005)
# $2 : port connecteur HTTP (8080, d'habitude)
# $3 : port connecteur AJP (8009, d'habitude)
generate_patch_tomcat_server_xml_6() {
local NOM_WORKER TOMCAT_PORT_SYS TOMCAT_PORT_HTTP TOMCAT_PORT_AJP
test -n "$1" && test -n "$2" && test -n "$3" && test -n "$4" || exit 1
NOM_WORKER="$1"
TOMCAT_PORT_SYS="$2"
TOMCAT_PORT_HTTP="$3"
TOMCAT_PORT_AJP="$4"
cat <<EOF
--- server.xml
+++ server.xml
@@ -19,7 +19,7 @@
define subcomponents such as "Valves" at this level.
Documentation at /docs/config/server.html
-->
-<Server port="8005" shutdown="SHUTDOWN">
+<Server port="$TOMCAT_PORT_SYS" shutdown="SHUTDOWN">
<!-- Security listener. Documentation at /docs/config/listeners.html
<Listener className="org.apache.catalina.security.SecurityListener" />
-->
@@ -69,10 +69,12 @@
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL HTTP/1.1 Connector on port 8080
-->
- <Connector port="8080" protocol="HTTP/1.1"
+ <!--
+ <Connector port="$TOMCAT_PORT_HTTP" protocol="HTTP/1.1"
connectionTimeout="20000"
URIEncoding="UTF-8"
redirectPort="8443" />
+ -->
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
@@ -91,9 +93,7 @@
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
- <!--
- <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
- -->
+ <Connector port="$TOMCAT_PORT_AJP" protocol="AJP/1.3" redirectPort="8443" address="127.0.0.1" />
<!-- An Engine represents the entry point (within Catalina) that processes
@@ -105,7 +105,7 @@
<!-- You should set jvmRoute to support load-balancing via AJP ie :
<Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
-->
- <Engine name="Catalina" defaultHost="localhost">
+ <Engine name="Catalina" defaultHost="localhost" jvmRoute="$NOM_WORKER">
<!--For clustering, please take a look at documentation at:
/docs/cluster-howto.html (simple how to)
EOF
}
# Petites fonctions pour éviter de faire des concatenations
# hasardeuses un peu partout
get_tomcat_user() {
case $VERSION_TOMCAT in
'6')
printf "%s" "$TOMCAT6_USER"
;;
'7')
printf "%s" "$TOMCAT7_USER"
;;
esac
}
get_tomcat_group() {
# TODO : une idée à creuser serait de créer un groupe tomcat incluant tomcat6 et tomcat7
case $VERSION_TOMCAT in
'6')
printf "%s" "$TOMCAT6_GROUP"
;;
'7')
printf "%s" "$TOMCAT7_GROUP"
;;
esac
}
# affiche le message d'aide
usage() {
cat <<EOF
$0 -c suffixe
$0 -d numero_instance
$0 -h
-c xxxx : création d'une instance avec le suffixe 'xxxx' (15 caract. max)
-d NNN : destruction de l'instance numéro NNN
-S : activation du port d'extinction de Tomcat
(déconseillé en production, ne gêne pas le gestionnaire de démon de Debian :
http://tomcat.apache.org/tomcat-7.0-doc/config/server.html#Common_Attributes
http://wiki.apache.org/tomcat/FAQ/Security#Q2 )
Ce script peut également modifier le fichier workers.properties
pour y ajouter les infos du worker créé.
-w /etc/.../workers.properties : Modification du workers.properties
-h : ce message d'aide
EOF
}
# Début du code
# gestion des options de lancement
while getopts c:d:Sw:h f; do
case $f in
'c')
MODE_CREATION=1
SUFFIX="$OPTARG"
OLD_LC_ALL="$LC_ALL"
export LC_ALL=C
export LANG=C
if [ $( printf "$SUFFIX" | egrep -c "^[a-z][a-z0-9_]{0,14}$" ) -ne 1 ]; then
echo "ERREUR: suffixe vide ou contenant des caractères interdits : regexp : [a-z][a-z0-9_]{0,14}" >&2
exit 1
fi
export LC_ALL="$OLD_LC_ALL"
export LANG="$OLD_LC_ALL"
;;
'd')
MODE_DESTRUCTION=1
NUM_INSTANCE=$( printf "%d" "$OPTARG" )
;;
'h')
usage
exit 0
;;
'S')
TOMCAT_ENABLE_SHUTDOWN_PORT="1"
;;
'w')
OPTION_WORKERS_PROPERTIES="$OPTARG"
if [ ! -f "$OPTION_WORKERS_PROPERTIES" ]; then
echo "ERREUR: fichier workers.properties inexistant : $OPTION_WORKERS_PROPERTIES" >&2
exit 1
fi
;;
\?)
usage >&2
exit 1
;;
esac
done
#(code inutile pour ce script, mais que je garde parce qu'on ne sait jamais)
#shift $( expr $OPTIND - 1 )
#DATA="$1"
# Petite vérif.
case $(( $MODE_CREATION + $MODE_DESTRUCTION )) in
'0')
echo "ERREUR: veuillez choisir entre création et destruction." >&2
usage >&2
exit 1
;;
'2')
echo "ERREUR: tu veux créer et détruire en même temps, petit malin ?" >&2
exit 1
;;
esac
# - est-ce qu'on est bien sur une Debian 6 ou 7
# (c'est surtout pour éviter les mauvaises surprises, ce script n'ayant pas été testé ailleurs)
if [ ! -f "/etc/debian_version" ] || [ $( egrep -c "^(6.|7.)" /etc/debian_version ) -ne 1 ]; then
echo "ERREUR: mauvaise version de Debian détectée. Arrêt par prudence. Vérifiez le code de ce script avant de forcer." >&2
exit 1
fi
# - est-ce que Tomcat est bien installé ?
if ! dpkg -l tomcat$VERSION_TOMCAT >/dev/null 2>&1; then
echo "ERREUR: tomcat$VERSION_TOMCAT ne semble pas avoir été installé." >&2
exit 1
fi
# - on vérifie que le /etc/default/tomcatN contient bien les infos qui nous intéresse
if [ ! -n $( get_tomcat_user ) ] || [ ! -n $( get_tomcat_group) ]; then
echo "ERREUR: TOMCAT_USER ou TOMCAT_GROUP indeterminé." >&2
exit 1
fi
# Fin des vérifications
if [ "$MODE_CREATION" -eq 1 ]; then
# Détermination du numéro d'instance :
# On cherche un numéro inutilisé ni par une instance Tomcat, ni par une instance Postgres
for NUM_INSTANCE in $( seq 1 $MAX_INSTANCES ); do
# Finalement, on base tout sur le nom de l'instance : ça
# nous permet de retrouver facilement le répertoire de l'instance
# lors de la suppression (le HOME de tomcat7 est sur CATALINA_HOME,
# pas sur CATALINA_BASE :'(
NOM_INSTANCE=$( printf "%s_%0$NB_CHIFFRES_DANS_NUM_INSTANCE""d_%s" "$( get_tomcat_user )" "$NUM_INSTANCE" "$SUFFIX" )
USERID_TOMCAT=$(( $START_RANGE_TOMCATUID + $NUM_INSTANCE ))
USERNAME_TOMCAT="$NOM_INSTANCE"
SCRIPTNAME_TOMCAT="$NOM_INSTANCE"
WORKERNAME_TOMCAT="worker_$NOM_INSTANCE"
if [ ! -d "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE" ] && ! getent passwd "$USERID_TOMCAT" >/dev/null 2>&1 && ! getent passwd "$USERNAME_TOMCAT" >/dev/null 2>&1; then
echo "INFO: Numéro d'instance libre trouvé : $NUM_INSTANCE"
break
fi
done
if [ "$NUM_INSTANCE" -eq "$MAX_INSTANCES" ]; then
echo "ERREUR: plus de numéro d'instance disponible." >&2
exit 1
fi
# Vérifier que le REP_TOMCAT_MULTIINSTANCE pré-existe.
# TODO review : finalement pas une bonne idée de le faire créer par le script ?
if [ ! -d "$REP_TOMCAT_MULTIINSTANCE" ]; then
# (finalement, on le crée nous même...)
echo "WARNING: le répertoire '$REP_TOMCAT_MULTIINSTANCE' n'existe pas (normal si première exécution). Création..."
mkdir -p "$REP_TOMCAT_MULTIINSTANCE"
chown -R "root:$( get_tomcat_group )" "$REP_TOMCAT_MULTIINSTANCE"
chmod 750 "$REP_TOMCAT_MULTIINSTANCE"
fi
# Copie du script d'init et du /etc/default de Tomcat
# Note : pour les scripts d'init, j'aurais aimé faire de simples liens symboliques vers
# un script générique. Malheureusement, ça pose de gros soucis pour LSB (insserv
# semble avoir les liens symboliques en horreur) et on aurait dû tout faire à la
# main, d'où plus d'inconvénients que d'avantages.
echo "INFO: création de l'utilisateur système dédié..."
useradd -u "$USERID_TOMCAT" -s /bin/false -g $( get_tomcat_group ) -d $( getent passwd $( get_tomcat_user ) | cut -d : -f 6 ) "$USERNAME_TOMCAT"
echo "INFO: création du script de lancement..."
cat /etc/init.d/tomcat$VERSION_TOMCAT | \
sed "s/^\(#[[:space:]]*Provides[[:space:]]*:[[:space:]]*\)tomcat[0-9]\+[[:space:]]*$/\1$SCRIPTNAME_TOMCAT/" | \
sed 's/^NAME=tomcat[0-9]\+$/NAME=$( basename "$0" | sed "s\/^[SK][0-9]\+\/\/" )/' \
>/etc/init.d/$SCRIPTNAME_TOMCAT
chmod +x /etc/init.d/$SCRIPTNAME_TOMCAT
echo "INFO: création du fichier de paramétrage dans /etc/default..."
cat /etc/default/tomcat$VERSION_TOMCAT | sed "s/^\(TOMCAT[0-9]\+_USER\)=tomcat[0-9]\+$/\1=$USERNAME_TOMCAT/" >/etc/default/$SCRIPTNAME_TOMCAT
echo "# Script MI Tomcat : Spécification des CATALINA_HOME et CATALINA_BASE" >>/etc/default/$SCRIPTNAME_TOMCAT
echo "CATALINA_HOME=/usr/share/tomcat$VERSION_TOMCAT" >>/etc/default/$SCRIPTNAME_TOMCAT
echo "CATALINA_BASE=$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE" >>/etc/default/$SCRIPTNAME_TOMCAT
# Activation du script d'init
echo "INFO: activation du script d'init..."
update-rc.d $SCRIPTNAME_TOMCAT defaults
# Instanciation du CATALINA_BASE
# Copie du /var/lib/tomcatN (notre template, en gros)
echo "INFO: génération du CATALINA_BASE ( $REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE )..."
mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE"
cp -a /var/lib/tomcat$VERSION_TOMCAT/* "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/"
rm "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf"
rm "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/logs"
rm "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/work"
mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf"
mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/logs"
mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/work"
cp -r /var/lib/tomcat$VERSION_TOMCAT/conf/* "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf/"
# Parfois, le work est vide. Solution temporaire : on émet un warning
if [ "$( ls /var/lib/tomcat$VERSION_TOMCAT/work/* 2>/dev/null | wc -l )" -gt 0 ]; then
cp -r /var/lib/tomcat$VERSION_TOMCAT/work/* "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/work/"
else
echo "INFO: le répertoire /work/ du template est vide. A priori, c'est normal."
fi
chown -R $USERNAME_TOMCAT:$( get_tomcat_group ) "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE"
# Réglage des ports TCP/IP
generate_patch_tomcat_server_xml "$VERSION_TOMCAT" \
"$WORKERNAME_TOMCAT" \
$( test -n "$TOMCAT_ENABLE_SHUTDOWN_PORT" && echo $(( $START_RANGE_TOMCAT_PORT_SYS + $NUM_INSTANCE )) || echo "-1" ) \
$(( $START_RANGE_TOMCAT_PORT_HTTP + $NUM_INSTANCE )) \
$(( $START_RANGE_TOMCAT_PORT_AJP + $NUM_INSTANCE )) | \
patch $REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf/server.xml
# Génération du fichier de config de logrotate
echo "INFO: génération du logrotate..."
cat /etc/logrotate.d/tomcat$VERSION_TOMCAT | \
sed "s#^/var/log/tomcat[0-9]\+/catalina.out #$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/logs/catalina.out #" | \
sed "s/create 640 tomcat[0-9]\+ adm/create 640 $USERNAME_TOMCAT adm/" \
>/etc/logrotate.d/$SCRIPTNAME_TOMCAT
# partie annexe n°1 : workers.properties
if [ -f "$OPTION_WORKERS_PROPERTIES" ]; then
echo "INFO: Modification du workers.properties..."
# Ajout du worker à la worker.list
TMP_SUFFIX=$( date +%Y%m%d-%H%M%S )
sed -i.$TMP_SUFFIX -e "s/^worker.list=\(.\+\)$/worker.list=\1,$WORKERNAME_TOMCAT/g" -e "s/^worker.list=$/worker.list=$WORKERNAME_TOMCAT/g" "$OPTION_WORKERS_PROPERTIES"
echo "NOTICE: une sauvegarde $OPTION_WORKERS_PROPERTIES.$TMP_SUFFIX a été créée, n'hésitez pas à la supprimer."
# Ajouts de la définition du worker
ajout_worker_properties "$VERSION_TOMCAT" "$WORKERNAME_TOMCAT" $(( $START_RANGE_TOMCAT_PORT_AJP + $NUM_INSTANCE )) >> "$OPTION_WORKERS_PROPERTIES"
fi
# Fin
echo "INFO: Création terminée. Serveur Tomcat éteint."
echo "INFO: Pour connecter mod-jk au worker, voici la ligne à ajouter au vhost : JkMount /* $WORKERNAME_TOMCAT"
echo "INFO: Pour allumer l'instance Tomcat : service $SCRIPTNAME_TOMCAT start"
fi
if [ "$MODE_DESTRUCTION" -eq 1 ]; then
USERID_TOMCAT=$(( $START_RANGE_TOMCATUID + $NUM_INSTANCE ))
#echo "NOTICE: le script est en mode -e. Il s'arrête donc à la première erreur. Le cas échéant, il faudra terminer le nettoyage à la main."
# On recherche le nom de l'instance
USERNAME_TOMCAT=$( getent passwd $USERID_TOMCAT | cut -d : -f 1 )
NOM_INSTANCE="$USERNAME_TOMCAT"
SCRIPTNAME_TOMCAT="$NOM_INSTANCE"
WORKERNAME_TOMCAT="worker_$NOM_INSTANCE"
if [ ! -n "$USERNAME_TOMCAT" ]; then
echo "ERREUR: l'utilisateur correspondant à l'uid $USERID_TOMCAT est introuvable." >&2
exit 1
fi
if service "$SCRIPTNAME_TOMCAT" status >/dev/null 2>&1; then
echo "ERREUR: l'instance tomcat tourne toujours. Ne pas oublier le service $SCRIPTNAME_TOMCAT stop" >&2
exit 1
fi
# Suppression utilisateur et répertoire dédié
echo "INFO: suppression de l'utilisateur..."
userdel $USERNAME_TOMCAT
echo "INFO: suppression du répertoire dédié..."
rm -rf "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE"
echo "INFO: suppresion fichier default..."
rm -f "/etc/default/$SCRIPTNAME_TOMCAT"