Multilogcheck

A Unix/Linux szerverek üzemeltetése wikiből
A lap korábbi változatát látod, amilyen KornAndras (vitalap | szerkesztései) 2006. október 27., 01:48-kor történt szerkesztése után volt.

(eltér) ←Régebbi változat | Aktuális változat (eltér) | Újabb változat→ (eltér)

Ígéretemhez híven álljon itt akkor esettanulmányként a multilogcheck script, amit a logcheck kiváltására írtam (ha valakinek kell, vigye - GPL-es :).

Főbb tulajdonságai:

  • az svlogd vagy a multilog posztprocesszoraként szeret futni, tipikus indítása pl (svlogd config):
s500000
n15
-*
+*PAM_unix[*]: (ssh)*
+*:*:*:* ssh*
!tryto -pP multilogcheck sshd
  • így garantáltan megkapja az összes naplóüzenetet
    • kivéve esetleg rendszerösszeomlás utáni újraindításkor, az előző rotáció és a crash közöttieket
  • vigyázat, a tryto-ban időlimit is van - ha három percnél hosszabb feldolgozásra számítunk, növeljük meg
  • a nem ignorált sorokhoz legyártja az ignore-regexpeket, hogy ne kézzel kelljen
  • nem túl hordozható (bár könnyű "portolni")
  • egy loghoz két, a figyelmen kívül hagyandó sorokra illeszkedő reguláris kifejezéseket tartalmazó file-t használ fel:
    • egy szolgáltatásspecifikusat és
    • egy szolgáltatás- és hoszt-specifikusat ("példányspecifikusat").
  • a fenti példában egy sshd-logot elemzünk, a hosztspecifikus ignore-file nevét a hosztnévből találja ki
    • külön megadni pl. webes virtualhost esetén van értelme
    • vagy ha több példányban is futtatjuk ugyanazt a szolgáltatást
  • konfiguráció (az első paraméterről elnevezett file-ból olvassa be):
    • IGNOREFILE: a szolgáltatásspecifikusan ignorálandó regexpek listáját tartalmazó file neve
    • HOSTIGNOREFILE: ugyanez az adott szolgáltatáspéldányhoz
      • igazából kényelmi funkció, hogy a parancssorban is megadható, mert amúgy a configfile-ból is beolvashatná - de akkor esetleg sok configfile kéne
    • SENDMAILTO: kik kapják az értesítést a gyanús üzenetekről
    • PREFILTERS: olyan parancs-pipeline, amin mindenképpen át kell szűrni a logot, még a regexp-illesztés előtt - pl. "adnsresfilter"
    • MAILFILTERS: olyan parancs-pipeline, ami olvashatóbbá teszi a gyanúsnak talált sorokat - pl. "sort|uniq -c|sort -n"; emailben így mennek el
    • OUTFILTERS: olyan parancs-pipeline, amin csak a standard kimenetünket szűrjük át - pl. "gzip -9"
    • IGNOREFILTERS: olyan parancs-pipeline, ami ignore-regexpeket csinál a bejövő sorokból, pl. dátumra illeszkedő regexpre cseréli a konkrét dátumokat

Függőségek:

  • ssed ("super" sed, bár talán a 4.0-ás GNU sed is jó már; a bővített reguláris kifejezések miatt kell)
  • debianutils (az mktemp miatt)
  • mailx
  • gzip
  • libadns1-bin (az adnsresfilter miatt)
  • daemontools (a tai64nlocal miatt; csak akkor kell, ha vannak tai64n időbélyegeket tartalmazó naplóink)

A működés blokkdiagramja:

 /-------\   /------------\   /--------------\   /-------------\   /------------\
 | stdin |-->| PREFILTERS |-->| IGNORE files |-->| MAILFILTERS |-->| SENDMAILTO |
 \-------/   \-----+------/   \------+-------/   \-------------/   \------+-----/
                   |                 |                                    ^
                   V                 V          /---------------\         |
             /-----+------\          \---->-----| IGNOREFILTERS |---->----/
             | OUTFILTERS |                     \---------------/
             \-----+------/
                   V
               /---+----\
               | stdout |
               \--------/

Vagyis:

  1. Jön a log a standard inputon.
  2. Átszűrjük a PREFILTERS szűrőkön, így létrejön egy TEMPFILE (jobb lett volna elkerülni a tempfile-ok használatát, de csak hosszas tee-s trükközéssel lehetne, ha egyáltalán - talán majd egyszer)
  3. Az IGNOREFILE-ban és a HOSTIGNOREFILE-ban szereplő regexpek egyikére sem illeszkedő sorokat a MAILFILE-ba írjuk (ez is egy tempfile)
  4. MAILFILE-t átszűrjük MAILFILTERS-en, az eredmény TEMPFILE2-be kerül
  5. Elküldjük a SENDMAILTO-ban megadott címekre TEMPFILE2 tartalmát, valamint azt, amit MAILFILE tartalmából IGNOREFILTERS csinál
  6. Átszűrjük TEMPFILE-t OUTFILTERS-en, és az eredményt kiírjuk az stdoutra (ez lesz a rotált log, amit az svlogd elment)

Lássuk magát a scriptet:

#!/bin/zsh
#
# Usage: multilogcheck configfile [hostname]
#
# Reads standard input and generates mail and output as specified in
# configfile.
#
# 1. apply PREFILTERS to stdin, creating TEMPFILE.
# 2. remove lines matched by any of the regexps in IGNOREFILE and
#    HOSTIGNOREFILE from TEMPFILE, creating MAILFILE.
# 3. apply MAILFILTERS to MAILFILE, creating TEMPFILE2
# 4. mail the contents of TEMPFILE2 and the output of IGNOREFILTERS applied
#    to MAILFILE to SENDMAILTO
# 5. write to stdout the output of OUTFILTERS applied to TEMPFILE.

#
# Sample configuration:
#
IGNOREFILE=""
SENDMAILTO=root
PREFILTERS="adnsresfilter -w -t1000"
MAILFILTERS="tai64nlocal|adnsresfilter -w -t1000"
OUTFILTERS="gzip -9"
HOSTNAME=""
HOSTIGNOREFILE=""
IGNOREFILTERS=/usr/local/sbin/multilogcheck-default-ignorefilter
DATE=$(date +'%Y/%m/%d %H:%M')

CONFIGFILE="$1"
HOSTNAME="${2-${HOST:-$(hostname)}}"

BASEDIR=/etc/multilogcheck
IGNOREDIR=$BASEDIR/ignore.d
HOSTDIR=$BASEDIR/ignore.d.$HOSTNAME

export TMPDIR=/dev/shm

if [[ ! -r "$CONFIGFILE" ]]; then
	CONFIGFILE=$BASEDIR/$CONFIGFILE
	if [[ ! -r "$CONFIGFILE" ]]; then
		NOCONFIG=1
	fi
fi

[[ -r "$CONFIGFILE" ]] && . "$CONFIGFILE"

[[ -r "$IGNOREFILE" ]] || IGNOREFILE=$IGNOREDIR/"$(basename "$CONFIGFILE")"
[[ -r "$HOSTIGNOREFILE" ]] || HOSTIGNOREFILE="$HOSTDIR/$(basename "$CONFIGFILE")"

SUBJECT="$HOSTNAME multilogcheck $1 $DATE using $IGNOREFILE $HOSTIGNOREFILE"

TEMPFILE=$(tryto mktemp -t multilogcheck.$$.XXXXXXX) || exit 1
TEMPFILE2=$(tryto mktemp -t multilogcheck.$$.XXXXXXX) || exit 1
MAILFILE=$(tryto mktemp -t multilogcheck.$$.XXXXXXX) || exit 1

[[ -r "$IGNOREFILE" ]] || IGNOREFILE=/dev/null
[[ -r "$HOSTIGNOREFILE" ]] || HOSTIGNOREFILE=/dev/null

eval ${=PREFILTERS} >>$TEMPFILE
egrep -a -v -f "$IGNOREFILE" -f "$HOSTIGNOREFILE" <$TEMPFILE >>$MAILFILE

if [[ -s $MAILFILE ]]; then
	eval ${=MAILFILTERS} <$MAILFILE >>$TEMPFILE2
	if [[ -s $TEMPFILE2 ]]; then
		(
			cat $TEMPFILE2
			echo
			echo "Ignore patterns from the above:"
			echo
			eval ${=IGNOREFILTERS} <$MAILFILE
		) | mail -s "$SUBJECT" ${=SENDMAILTO}
	fi
fi
eval ${=OUTFILTERS} <$TEMPFILE
rm -f $TEMPFILE $TEMPFILE2 $MAILFILE
exit 0

A scriptben hivatkozott multilogcheck-default-ignorefilter tartalma:

#!/bin/sh
ssed -r '
        s/([][{}()\.*$^+?|])/\\\1/g
        s/^@[0-9a-f]* //
        s/(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) [ 0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9] /.* /
        s/^\.\* //
        /grsec/s/([[:alnum:]]+):[0-9]+/\1:[0-9]+/g
        s/port [0-9]+/port [0-9]+/
        s/\\[[0-9]+\\]/\\[[0-9]+\\]/g
        / pid [0-9]+/s/ pid [0-9]+([: ])/ pid [0-9]+\1/
        / delivery [0-9]+/s/ delivery [0-9]+([: ])/ delivery [0-9]+\1/
' \
| sort -u

A sedben:

  • az első sor a regexp metakarakterek elé szúr be backslasht
  • a második a sor eleji tai64n timestampet törli
  • a harmadik a dátumot cseréli .*-ra (lehetne a dátumra illeszkedő regexpre is, de annak az illesztése utána valószínűleg lassúbb lenne, és általában nem kell az a pontosság)
  • a negyedik hatékonyabbá teszi a kész regexpet azzal, hogy ha a sor elején csak ".* " maradt, akkor azt törli, mert redundáns (a szóköz szigorúan véve nem)
  • az ötödik a (régi) grsecurity logjaiban cseréli a "usernév:uid" ill. a "programnév:pid" jellegű konstrukciókat olyanra, ahol az uid/pid nem konkrét, hanem számokra illeszkedő regexp
  • a hatodik a "port 1234" jellegű konstrukciókat cseréli "port [0-9]+"-ra (pl. ssh logokban a sor végén ott a kliensport, és az általában mindegy)
  • a hetedik a szögletes zárójelben levő számokat cseréli arra, hogy "\[[0-9]+\]" (pl. syslogokban a PID ilyen)
  • a nyolcadik és a kilencedik a qmail logjaiból irtja ki a felesleges specifikumokat
Személyes eszközök