User Tools

Site Tools


Sidebar


Tags Cloud
start

Last updates

Анализ логов nginx / apache с помощью скриптов wtop и logrep

Фантастически полезная вещь для анализа логов вебсервера.

Очень быстро позволяют отловить медленные выполняющиеся запросы, найти использованную уязвимость, определить файлы с самым большим количеством трафика, отфильтровать ддос-ботов, выбрать самые популярные файлы для перемещения на 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.

Формирование списка наиболее востребованных файлов для выноса на SSD-диск (для Nginx)

Т.к. изначально скрипт создавался для работы с 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
2013/06/01 08:20

Эпичный скрипт бекапа mysql

Саппорт 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
2013/05/23 17:54

Настройка rsyslog для удаленного протоколирования в Debian

Клиент

/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
2013/04/23 23:44

Скрипт установки модуля ustats для nginx в Debian

Автоматическая установка модуля 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
2013/04/22 23:15

Быстрая установка Dedikit из готового джейла в Debian

#!/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

"
2013/01/15 22:24

Скрипт проверки уровня LA

#!/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 $!;
2011/05/23 12:58

Простой фаервол, открывающий доступ только для белого списка.

Полный доступ для 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
2010/11/24 13:10
start.txt · Last modified: 2016/09/22 01:27 by kyxap