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., 02:21-kor történt szerkesztése után volt.

Í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)
  • zsh (abban írtam; valószínűleg lehetne portolni POSIX sh-ra)

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:

</pre>

  1. !/bin/zsh
  2. Usage: multilogcheck configfile [hostname]
  3. Reads standard input and generates mail and output as specified in
  4. configfile.
  5. 1. apply PREFILTERS to stdin, creating TEMPFILE.
  6. 2. remove lines matched by any of the regexps in IGNOREFILE and
  7. HOSTIGNOREFILE from TEMPFILE, creating MAILFILE.
  8. 3. apply MAILFILTERS to MAILFILE, creating TEMPFILE2
  9. 4. mail the contents of TEMPFILE2 and the output of IGNOREFILTERS applied
  10. to MAILFILE to SENDMAILTO
  11. 5. write to stdout the output of OUTFILTERS applied to TEMPFILE.
  1. 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 </pre>

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

Tervezési szempontok egy ilyen programnál:

  • A rendszer stabilitása múlik a működésén (mivel az svlogd blokkolódhat miatta, amiatt pedig a szolgáltatások blokkolódhatnak)
  • Legyen egyszerű (hogy lehetőleg ne rontsuk el)
  • Méretezzük extrém körülményekre!
    • Lehet, hogy túl van terhelve a CPU és percenként/óránként/hetente csak egy sor hajtódik végre a scriptből.
    • Lehet, hogy lerohadt a diszk (vagy az nfs), és minden diszk I/O blokkolódik.
    • Lehet, hogy tele van a diszk.
    • Lehet, hogy nincs hálózat.
    • Lehet, hogy nem működik a DNS.
    • Lehet, hogy nem működik az NSS (tehát nem tudunk usernév->uid leképezést csinálni, és fordítva).
  • Használjunk minél kevesebb külső programot; amit lehet, valósítsuk meg a shellen belül.
  • Tartózkodjunk a naplózást eredményező műveletektől (az e-mail-küldés határeset, szükséges rossz).
  • A program legyen fail-safe:
    • Ne tételezze fel, hogy a futása atomi.
    • Ha kilép, takarítson maga után.
    • Ha működés közben hiba lép fel, próbálja értelmesen kezelni; ha nem megy, inkább lépjen ki hibával, majd a tryto újrapróbálja.

Ezek után keressünk hibákat a fenti multilogcheckben! Több is van.

Nézzünk egy másik lehetséges mailfiltert is:

#!/bin/sh
nice -n 10 ssed -r 's/.*FW: //;
        s/(TOS|PREC|ID|TTL|WINDOW|URGP|MAC|LEN|RES|SEQ)=[0-9a-fx:]* //g;
        s/DF //;
        s/ .$//;
        /DPT=(21|22|25|53|80|111|113|13[789]|515|1080|1352|1433|1434|19[89][0-9]|3128|333[0-9]|5101|667[012345]|7776|8080|8088)[^0-9]?/s/SPT=[0-9]* ?//;
        /(unknown ACCEPT.*PROTO=TCP)|(ircd DROP)/{
                s/SPT=[^ ]* ?//
                s/DST=[^ ]* ?//
        }
        /unknown ACCEPT.*PROTO=UDP.*SPT=53/{
                s/DPT=[^ ]* ?//
                s/DST=[^ ]* ?//
        }
        /fullban DROP.*PROTO=UDP.*SPT=53 /s/DPT=[^ ]* ?//
        /icmp/s/\[.*\]//
        /ircflood DROP/{
                s/DPT=[^ ]* ?//
                s/DST=[^ ]* ?//
                s/SPT=[^ ]* ?//
        }
        /fullban DROP/s/SPT=[^ ]* ?//g
        /PROTO=UDP SPT=53 /s/DPT=[^ ]* ?//
        /ACK.*RST/s/[SD]PT=[^ ]* ?//g' \
        | sort \
        | uniq -c \
        | sort -nr

Ugye, milyen sokmindenre jó a sed? :) Ez a script pl. netfilter-logokat gyomlál ki. Előtte:

@4000000045414cf93677c76c kern.warn: Oct 27 02:03:59 kernel: FW: INPUT catch-all: IN=ppp0 OUT= MAC= SRC=217.83.108.243 DST=42.42.42.42 LEN=52 TOS=0x00 PREC=0x00 TTL=119 ID=19357 DF PROTO=TCP SPT=49302 DPT=53994 WINDOW=65535 RES=0x00 ACK SYN URGP=0 
@4000000045414cfa0ed0de9c kern.warn: Oct 27 02:04:00 kernel: FW: INPUT catch-all: IN=ppp0 OUT= MAC= SRC=62.0.99.177 DST=42.42.42.42 LEN=52 TOS=0x1C PREC=0x20 TTL=111 ID=60175 DF PROTO=TCP SPT=4662 DPT=43073 WINDOW=65377 RES=0x00 ACK FIN URGP=0 
@4000000045414d163104f14c kern.warn: Oct 27 02:04:28 kernel: FW: INPUT catch-all: IN=ppp0 OUT= MAC= SRC=62.0.99.177 DST=42.42.42.42 LEN=52 TOS=0x1C PREC=0x20 TTL=111 ID=61188 DF PROTO=TCP SPT=4662 DPT=43073 WINDOW=65377 RES=0x00 ACK FIN URGP=0 
@4000000045414d71201c8714 kern.warn: Oct 27 02:05:59 kernel: FW: INPUT catch-all: IN=ppp0 OUT= MAC= SRC=217.83.108.243 DST=42.42.42.42 LEN=40 TOS=0x00 PREC=0x00 TTL=119 ID=26392 PROTO=TCP SPT=49302 DPT=56250 WINDOW=0 RES=0x00 ACK RST URGP=0 
@4000000045414d9d11bbb604 kern.warn: Oct 27 02:06:43 kernel: FW: INPUT catch-all: IN=ppp0 OUT= MAC= SRC=217.83.108.243 DST=42.42.42.42 LEN=40 TOS=0x00 PREC=0x00 TTL=119 ID=28748 PROTO=TCP SPT=49302 DPT=45275 WINDOW=0 RES=0x00 ACK RST URGP=0 
@4000000045414e7a295dbe9c kern.warn: Oct 27 02:10:24 kernel: FW: INPUT catch-all: IN=ppp0 OUT= MAC= SRC=89.98.96.27 DST=42.42.42.42 LEN=40 TOS=0x00 PREC=0x20 TTL=243 ID=1186 PROTO=TCP SPT=4662 DPT=53795 WINDOW=0 RES=0x00 RST URGP=0 
@4000000045414ea62954e4fc kern.warn: Oct 27 02:11:08 kernel: FW: INPUT catch-all: IN=ppp0 OUT= MAC= SRC=89.98.96.27 DST=42.42.42.42 LEN=40 TOS=0x00 PREC=0x20 TTL=243 ID=27381 PROTO=TCP SPT=4662 DPT=50727 WINDOW=0 RES=0x00 RST URGP=0 
@4000000045414ed229500eb4 kern.warn: Oct 27 02:11:52 kernel: FW: INPUT catch-all: IN=ppp0 OUT= MAC= SRC=89.98.96.27 DST=42.42.42.42 LEN=40 TOS=0x00 PREC=0x20 TTL=243 ID=29915 PROTO=TCP SPT=4662 DPT=52677 WINDOW=0 RES=0x00 RST URGP=0 
@4000000045414f792c9d9ba4 kern.warn: Oct 27 02:14:39 kernel: FW: INPUT catch-all: IN=ppp0 OUT= MAC= SRC=217.83.108.243 DST=42.42.42.42 LEN=52 TOS=0x00 PREC=0x00 TTL=119 ID=54870 DF PROTO=TCP SPT=49302 DPT=54721 WINDOW=65535 RES=0x00 ACK SYN URGP=0 
@4000000045414f7c1ae92edc kern.warn: Oct 27 02:14:42 kernel: FW: INPUT catch-all: IN=ppp0 OUT= MAC= SRC=217.83.108.243 DST=42.42.42.42 LEN=52 TOS=0x00 PREC=0x00 TTL=119 ID=55030 DF PROTO=TCP SPT=49302 DPT=54721 WINDOW=65535 RES=0x00 ACK SYN URGP=0 

Utána:

      2 INPUT catch-all: IN=ppp0 OUT= SRC=62.0.99.177 DST=42.42.42.42 TOS=0x1C PROTO=TCP SPT=4662 DPT=43073 ACK FIN 
      2 INPUT catch-all: IN=ppp0 OUT= SRC=217.83.108.243 DST=42.42.42.42 PROTO=TCP SPT=49302 DPT=54721 ACK SYN 
      2 INPUT catch-all: IN=ppp0 OUT= SRC=217.83.108.243 DST=42.42.42.42 PROTO=TCP ACK RST 
      1 INPUT catch-all: IN=ppp0 OUT= SRC=89.98.96.27 DST=42.42.42.42 PROTO=TCP SPT=4662 DPT=52677 RST 
      1 INPUT catch-all: IN=ppp0 OUT= SRC=89.98.96.27 DST=42.42.42.42 PROTO=TCP SPT=4662 DPT=50727 RST 
      1 INPUT catch-all: IN=ppp0 OUT= SRC=89.98.96.27 DST=42.42.42.42 PROTO=TCP DPT=53795 RST 
      1 INPUT catch-all: IN=ppp0 OUT= SRC=217.83.108.243 DST=42.42.42.42 PROTO=TCP DPT=53994 ACK SYN 

Példamail:

From log@gepnev Thu Oct 26 21:48:28 2006
Return-Path: <log@gepnev>
Delivered-To: rendszerg@zda
Received: (qmail 23083 invoked by uid 100); 26 Oct 2006 21:48:09 -0000
Date: 26 Oct 2006 21:48:09 -0000
Message-ID: <20061026214809.23082.qmail@gepnev>
From: log@gepnev
To: root@gepnev
Subject: gepnev multilogcheck dhcpd 2006/10/26 23:48 using /etc/multilogcheck/ignore.d/dhcpd /etc/multilogcheck/ignore.d.gepnev/dhcpd

2006-10-26 10:56:43.660529500 local7.info: Oct 26 10:56:43 dhcpd: DHCPOFFER on 172.18.10.3 to 00:0f:c9:00:50:31 via eth2
2006-10-26 10:56:43.669543500 local7.info: Oct 26 10:56:43 dhcpd: DHCPOFFER on 172.18.10.3 to 00:0f:c9:00:50:31 via eth2
2006-10-26 19:44:28.924672500 local7.info: Oct 26 19:44:28 dhcpd: DHCPOFFER on 172.18.10.3 to 00:0f:c9:00:50:31 via eth2
2006-10-26 19:44:28.932071500 local7.info: Oct 26 19:44:28 dhcpd: DHCPOFFER on 172.18.10.3 to 00:0f:c9:00:50:31 via eth2

Ignore patterns from the above:

local7\.info: .* dhcpd: DHCPOFFER on 172\.18\.10\.3 to 00:0f:c9:00:50:31 via eth2
Személyes eszközök