Фантастически полезная вещь для анализа логов вебсервера.
Очень быстро позволяют отловить медленные выполняющиеся запросы, найти использованную уязвимость, определить файлы с самым большим количеством трафика, отфильтровать ддос-ботов, выбрать самые популярные файлы для перемещения на SSD-кеш, собирать гео-статистику в live режиме, а так же железным аргументом дерзко попячить гордое щячло любителей денвера.
Скрипты написаны на python, соответственно, работать должны везде.
Брать по адресу http://code.google.com/p/wtop/
Главная утилита - logrep.
wtop лично мне не пригодился.
Руководство к logrep: http://code.google.com/p/wtop/wiki/UsingLogrep
Примеры использования: http://code.google.com/p/wtop/wiki/LogrepCookbook
Самое замечательное в logrep - это возможность применять к выводимым значениям операторы.
Т.е. мы можем полностью сформировать вывод скрипта, состоящий из заданных нами параметров и с примененными к ним необходимыми операторами.
Полный список выходных параметров, которыми мы можем оперировать, указан в руководстве под строкой AVAILABLE FIELDS.
Ключ -o позволяет указать используемые в output значения, разделенные табуляцией. К каждому выводимому зачению можно применить оператор.
count(*) avg(FIELD) mean average min(FIELD) lowest seen value max(FIELD) highest seen value sum(FIELD) summation of all values var(FIELD) population variance dev(FIELD) deviation (square root of variance)
Ключ -s позволяет нам сортировать список, используя параметры вида -s "LIMIT:FIELDS:DIRECTION",
где LIMIT - это общее количество выводимых строк, FIELDS - выводимые значения, а DIRECTION - направление сортировки: 'd' - descending (по умолчанию), 'a' - acending.
Т.к. изначально скрипт создавался для работы с CustomLog типа combined вебсервера Apache, нам необходимо добавить в формат лога nginx переменную $request_time.
Формат лога для nginx
log_format mp4 '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent "$http_referer" ' '"$http_user_agent" $request_time' ;
Сортировка по top10 URL:
# logrep -o 'url,count(*)' -s '10:2' /home/log/httpd/yziltz.com/access_log /videos/ztod.com/546fg/168nzrr/1.mp4 324 /videos/wtfpass/104ghd/560wyqe/1.mp4 259 /videos/evilangel/1004hf/457elux/1.mp4 205 /videos/ztod.com/546fg/84rqek/1.mp4 203 /videos/wtfpass/104ghd/505dtve/1.mp4 192 /videos/realitykings/986fk/314qiga/1.mp4 173 /videos/wtfpass/104ghd/18ifvr/1.mp4 153 /videos/brazzers/758gf/350ihij/1.mp4 143 /videos/wtfpass/104ghd/533jdzo/1.mp4 125 /videos/bangbros/754hj/100geoo/1.mp4 122
где параметрами -o являются
url - URL файла count(*) - количество всех уникальных вхождений URL,
а параметрами -s
10 - это количество выводимых файлов, 2 - сортировка по полю #2.
В нашем случае поле #2 - это count(*).
Тип сортировки не указан и по умолчанию это 'd' - descending.
Сортировка по top10 URL с отображением дополнительных параметров:
Добавим новые значения в список параметров:
# logrep -o 'url,count(*),avg(msec),sum(bytes)' -s '10:2' /home/log/httpd/yziltz.com/access_log /videos/evilangel/1004hf/457elux/1.mp4 800 63330.12 1265011232 /videos/wtfpass/104ghd/18ifvr/1.mp4 446 55150.93 199966883 /videos/naughty_america/634fs/6kryk/1.mp4 413 66149.11 422437989 /videos/naughty_america/634fs/83nbna/1.mp4 361 41608.23 284347235 /videos/wtfpass/104ghd/606pfgb/1.mp4 352 35669.83 129798205 /videos/wtfpass/104ghd/216pcko/1.mp4 330 50201.86 563510318 /videos/ztod.com/546fg/168nzrr/1.mp4 325 48295.78 238183106 /videos/wtfpass/104ghd/560wyqe/1.mp4 311 121607.30 4415056002 /videos/wtfpass/104ghd/387jkmy/1.mp4 309 52073.58 677026365 /videos/wtfpass/104ghd/505dtve/1.mp4 240 233122.70 1272250219
Дополнительными параметрами будут avg(msec) - среднее время отдачи файла и sum(bytes) - общий трафик, пришедшийся на этот файл.
А теперь добавим фильтр -f, в котором выведем результаты только за последний час (3600 секунд).
Для этого мы применим unixtime как самый простой способ.
# CUR_DATE=`date +"%s"`; HOUR_OFFSET=`echo "$CUR_DATE - 3600" | bc`; logrep -f "ts<$HOUR_OFFSET" -o 'url,count(*),avg(msec),sum(bytes)' -s '10:2' /home/log/httpd/yziltz.com/access_log /videos/ztod.com/546fg/168nzrr/1.mp4 325 48295.78 238183106 /videos/wtfpass/104ghd/560wyqe/1.mp4 267 117390.16 2869373722 /videos/evilangel/1004hf/457elux/1.mp4 225 152913.27 954288822 /videos/ztod.com/546fg/84rqek/1.mp4 203 55998.36 188930211 /videos/wtfpass/104ghd/505dtve/1.mp4 196 216440.77 1093232840 /videos/realitykings/986fk/314qiga/1.mp4 173 203926.49 973211068 /videos/wtfpass/104ghd/18ifvr/1.mp4 161 114550.71 145899888 /videos/wtfpass/104ghd/533jdzo/1.mp4 147 182660.09 1100896430 /videos/bangbros/754hj/100geoo/1.mp4 145 175528.83 609455138 /videos/brazzers/758gf/350ihij/1.mp4 143 213783.88 144107563
Саппорт webazilla.com поставили клиенту скрипт резервирования баз mysql.
Конструкция WHICH_WHICH='which' заслуживает второго киселя и дополнительных 15 минут прогулки,
ветвление глубиной в 8 уровней - осиновый кол в серде, а акты непроизвольной обфускации смогут быть купированы только отрубанием верхних конечностей.
#!/bin/sh # MySQL backup creation/rotation scenario # =============== Config Section ====================< BDIR='/home/backup' DATADIR='/home/mysql' DBA_PASSWORD='' # >========== End Of Config Section ================== # non-user serviceable settings DATE=`date "+%Y-%m-%d"` DATE_TOUCH=`date "+%Y%m%d0001"` BEXT='.sql' GZEXT='.gz' TAREXT='.tar' FULLBACKPREFIX='mysql-data' NICENUM=19 CHMODBDIR='700' CHMODSTORE='400' BACKMTIME_COMPRESSED=8 BACKMTIME_STORED=14 BACKMINIMUMDAYS=3 TARGET_BACKUP='backup' TARGET_CHECK='check' # paths export PATH="/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/local/mysql/bin" WHICH_WHICH='which' WHICH_FIND=`$WHICH_WHICH find` || 'find' WHICH_BASENAME=`$WHICH_WHICH basename` || 'basename' WHICH_NICE=`$WHICH_WHICH nice` || 'nice' WHICH_MYSQLDUMP=`$WHICH_WHICH mysqldump` || 'mysqldump' WHICH_TAR=`$WHICH_WHICH tar` || 'tar' WHICH_GZIP=`$WHICH_WHICH gzip` || 'gzip' WHICH_CHMOD=`$WHICH_WHICH chmod` || 'chmod' WHICH_TOUCH=`$WHICH_WHICH touch` || 'touch' WHICH_MKDIR=`$WHICH_WHICH mkdir` || 'mkdir' WHICH_ECHO=`$WHICH_WHICH echo` || 'echo' WHICH_UNAME=`$WHICH_WHICH uname` || 'uname' # initialization OSMARKER=`$WHICH_UNAME -s` TARGET="$1" [ "$TARGET" = "" ] && TARGET=$TARGET_BACKUP db_list='' db_list_notrecent='' db_cnt=0 db_cnt_notrecent=0 # collecting database list if [ -d "$DATADIR" ]; then for f in `$WHICH_FIND "$DATADIR" -maxdepth 1 -type d`; do if [ "$f" != "$DATADIR" ]; then db_list=$db_list" "`$WHICH_BASENAME $f` db_cnt=$(($db_cnt+1)) fi done fi # section: backup if [ "$TARGET" = "$TARGET_BACKUP" ]; then # preparing backup directory [ ! -d $BDIR ] && ( $WHICH_MKDIR "$BDIR" && $WHICH_CHMOD $CHMODBDIR "$BDIR" ) [ ! -d $BDIR ] && return 1 # database backup loop for db_name in ${db_list}; do if [ $db_name!='' ]; then db_file_name="$BDIR/$db_name.$DATE$BEXT" gz_file_name="$db_file_name$GZEXT" $WHICH_NICE -n $NICENUM $WHICH_MYSQLDUMP -q -p$DBA_PASSWORD $db_name > "$db_file_name" if [ -s "$db_file_name" ]; then $WHICH_NICE -n $NICENUM $WHICH_GZIP -f -S "$GZEXT" "$db_file_name" if [ -s "$gz_file_name" ]; then $WHICH_CHMOD $CHMODSTORE "$gz_file_name" $WHICH_TOUCH -c -t $DATE_TOUCH "$gz_file_name" # performing rotation for listfile_name in `$WHICH_FIND "$BDIR" -type f -iname "$db_name.*$BEXT$GZEXT" -mtime +$BACKMTIME_COMPRESSED`; do if [ -s "$listfile_name" ]; then [ "$listfile_name" != "$gz_file_name" ] && rm "$listfile_name" else rm "$listfile_name" fi done else [ -f "$gz_file_name" ] && rm "$gz_file_name" fi else [ -f "$db_file_name" ] && rm "$db_file_name" fi fi done # full backup (just to be sure) store_file_name="$BDIR/$FULLBACKPREFIX.$DATE$TAREXT" $WHICH_NICE -n $NICENUM $WHICH_TAR cf "$store_file_name" "$DATADIR" if [ -s "$store_file_name" ]; then $WHICH_CHMOD $CHMODSTORE "$store_file_name" $WHICH_TOUCH -c -t $DATE_TOUCH "$store_file_name" for listfile_name in `$WHICH_FIND "$BDIR" -type f -iname "$FULLBACKPREFIX.*$TAREXT" -mtime +$BACKMTIME_STORED`; do [ "$listfile_name" != "$store_file_name" ] && rm "$listfile_name" done fi # section: check elif [ "$TARGET" = "$TARGET_CHECK" ]; then check_result='' check_status=1 meolder_border=$(($BACKMINIMUMDAYS+1)) if [ `$WHICH_FIND $0 \( -type f -or -type l \) \( -atime +$meolder_border -or -mtime +$meolder_border \)` ]; then # collecting list of databases which are not recent if [ -d "$DATADIR" ]; then atime_border=$(($BACKMINIMUMDAYS+1)) if [ $OSMARKER = "FreeBSD" ]; then atime_crit='Btime' else atime_crit='atime' fi for f in `$WHICH_FIND "$DATADIR" -maxdepth 1 -type d -$atime_crit +$atime_border`; do if [ "$f" != "$DATADIR" ]; then db_list_notrecent=$db_list_notrecent" "`$WHICH_BASENAME $f` db_cnt_notrecent=$(($db_cnt_notrecent+1)) fi done db_cnt_new=$(($db_cnt-$db_cnt_notrecent)) check_result="OK - healthy backups, databases: $db_cnt_new/$db_cnt (new/total)" check_status=0 else check_result="WARNING - Database directory does not exist: '$DATADIR'" check_status=1 fi if [ -d "$BDIR" ]; then if [ $OSMARKER = "Linux" ]; then mtime_first=1 mtime_last=$BACKMINIMUMDAYS else mtime_first=2 mtime_last=$(($BACKMINIMUMDAYS+1)) fi for db_name in ${db_list_notrecent}; do if [ $db_name!='' ]; then cnt_healthy_backups=0 mtime_check=$mtime_first while [ "$mtime_check" -le "$mtime_last" ]; do search_result=`$WHICH_FIND "$BDIR" -type f -iname "$db_name.*$BEXT$GZEXT" \! -size 0 -mtime $mtime_check` [ "$search_result" != "" ] && cnt_healthy_backups=$(($cnt_healthy_backups+1)) mtime_check=$(($mtime_check+1)) done if [ "$cnt_healthy_backups" -lt "$BACKMINIMUMDAYS" ]; then cnt_missing=$(($BACKMINIMUMDAYS-$cnt_healthy_backups)) check_result="CRITICAL - missing $cnt_missing backup(s) of database '$db_name'" check_status=2 break fi fi done else check_result="WARNING - Backup directory does not exist: '$BDIR'" check_status=1 fi else check_result="OK - check has not been performed, it's new installation" check_status=0 fi $WHICH_ECHO $check_result exit $check_status fi
/etc/rsyslog.d/logclient.conf
# UDP *.* @123.123.123.123:514
Создадим директорию для логов
# mkdir -p /var/log/remote/ # chmod 700 /var/log/remote/
/etc/rsyslog.d/logserver.conf
# log settings for host # loading UDP listener module $ModLoad imudp # bind listener to specified IP $UDPServerAddress 123.123.123.123 # allow remote client $AllowedSender UDP, 321.321.321.321 # template for all remote clients $template RemoteHost,"/var/log/remote/%HOSTNAME%.log" # ruleset for remote clients $RuleSet remote *.* ?RemoteHost # applying remote ruleset to UDP listener $InputUDPServerBindRuleset remote # run listener $UDPServerRun 514
Автоматическая установка модуля ustats, предназначенного для отображения статистики upstream'ов для nginx.
Версия под Debian wheezy. Должен быть установлен пакет build-essential.
Активируется по добавлению в конфиг виртуального хоста
location /ustats { ustats memsize=2m; ustats_refresh_interval 6000; ustats_html_table_width 95; ustats_html_table_height 95; }
Сам скрипт:
#!/bin/bash SRC_DIR="/opt/src/ustats" mkdir -p $SRC_DIR git clone https://github.com/0xc0dec/ustats.git $SRC_DIR cd $SRC_DIR git pull rm -rf $SRC_DIR/nginx-src mkdir -p $SRC_DIR/nginx-src cd $SRC_DIR/nginx-src apt-get source nginx-full apt-get build-dep nginx-full -y nginx_src=`find $SRC_DIR/nginx-src -maxdepth 1 -type d | tail -n 1` cd $nginx_src cat $SRC_DIR/nginx.patch | patch -p1 --dry-run OUT=$? if [[ $OUT == 0 ]]; then cat $SRC_DIR/nginx.patch | patch -p1 cp -a $SRC_DIR/ustats src/http/modules/ sed 's#--prefix=/etc/nginx \\#--prefix=/etc/nginx --add-module=src/http/modules/ustats \\#g' -i debian/rules debuild -us -uc -b fi
#!/bin/bash DOMAIN="" JAIL_IP="" CLIENT_DB="" if [[ -z $CLIENT_DB ]]; then echo "Please fill ALL \$VARIABLES !!!"; exit 1; fi echo "nameserver 8.8.8.8" > /etc/resolv.conf apt-get update apt-get install -y vim bzip2 wget http://kyxap.org.ua/rc/vimrc -O ~/.vimrc update-rc.d -f exim4 disable killall -9 exim4 modprobe ppp_mppe modprobe iptable_nat echo "modprobe ppp_mppe modprobe iptable_nat /opt/scripts/firewall.sh &" > /etc/rc.local mkdir /home/jails cd /home/jails wget http://cgiproxy.hqhost.net/nph-proxy.cgi/000000A/http/dist.hqhost.net/kyxap/dedikit-default-i386.tar.bz2 tar xf dedikit-default-i386.tar.bz2 mv dedikit-default-i386 $CLIENT_DB echo "/home/jails/$CLIENT_DB/initjail.sh &" >> /etc/rc.local # NEED TO CONTINUE! sed "s/mx1.hqhost-dedicated.net/$DOMAIN/g" -i /home/jails/$CLIENT_DB/start.sh echo "you neet to tune manually: 1. host/jail ssh 2. set root jail password 3. set mysql jail password 4. set correct smmta owner instead wrong 100 uid: smmta="/var/run/sendmail/mta /var/spool/mqueue /var/lib/sendmail /etc/mail /etc/mail/tls /etc/mail/sasl /etc/mail/sasl/Sendmail.conf.2 /etc/mail/m4 /etc/mail/smrsh /etc/mail/default-auth-info /etc/mail/access.db /etc/mail/aliases.db /etc/mail/virtusertable.db" for i in "$smmta"; do chown smmta:smmsp $i; done 5. rm -r /etc/awstats; ln -s /opt/dedikit/autogen/logprocessor_awstats/logprocessor_awstats /etc/awstats 6. set backup 7. set firewall 8. change registration email to client email "
#!/usr/bin/perl use strict; my $email = '[email protected]'; open my $PROC, "/proc/loadavg" or die $!; while (<$PROC>) { /(^\d{0,3}).\d{0,}\s(.*)/; my $la = $1; if($la gt 10) { system("top -b -n 2 | /usr/sbin/sendmail -sVPS_LA $email"); } } close $PROC or die $!;
Полный доступ для WHITELIST и только избранные порты для остальных.
#!/usr/bin/env bash iptables="echo /sbin/iptables" WHITELIST=( 10.0.0.0/8 ) TCP_PORTS=( 22 1514 1515 1516 55000 5601 ) UDP_PORTS=( 1514 ) TCP=${TCP_PORTS[@]} UDP=${UDP_PORTS[@]} #### START #### # flushing firewall $iptables -F $iptables -X # allow $iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # allow loopback $iptables -A INPUT -s 127.0.0.1 -j ACCEPT # allow smtp, http $iptables -A INPUT -p tcp -m multiport --dports ${TCP// /,} -j ACCEPT $iptables -A INPUT -p udp -m multiport --dports ${UDP// /,} -j ACCEPT # start whitelisting for i in ${WHITELIST[@]}; do $iptables -A INPUT -s $i -j ACCEPT done # block other $iptables -A INPUT -j DROP