Traffic auf einem clamav-mirror limitieren 1


Ich betreibe einen Mirror für clamav. Einige clients laden aber jedesmal die main.cvd anstatt der diffs. Daraus folgt ein täglicher Traffic von bist zu 150 MB pro Tag und client. Also habe ich mich auf die Sucher nach einer Lösung gemacht, um den monatlichen Traffic von ~2TB ein wenig zu reduzieren.

Mein apache sendet eh die Logs an syslog. Ich musste nur noch syslog so anpassen, dass die zwei wichtigen Teile eines Logeintrages (IP und download-size) in eine mysql-datenbank geschrieben werden.

Ein kleines bash-script liest all ip aus der Datenbank mit einem Traffic > 50MB*Tag (jeder Client kann 50 MB / Tag downloaden ehe die IP geblockt wird) und trägt die IP in die Firewall ein. Der zulässig Traffic kann einfach angepasst werden..

  1. iptables
  2. mysql
  3. apache
  4. syslog
  5. shell-script
  6. cron
  7. Statistik
  8. Files

Alle Files aus diesem Beitrag stehen am Ende zum Download zur Verfügung.

1. iptables
Trag folgendes in die Firewall ein, um einen recent-jail nutzen zu können. $MIRROR_IP muss dazu nur entsprechend gesetzt werden.
iptables -N DenyAccessClamAV

iptables -A INPUT -d $MIRROR_IP -p tcp -m multiport --dports 80,443 -m recent --update --seconds 86400 --name DenyAccessClamAV --hitcount 1 -j DenyAccessClamAV

iptables -A DenyAccessClamAV -p tcp -d $MIRROR_IP --dport 80 -i eth0 -m state --state NEW -m recent --update --hitcount 2 -j DROP

Die Einstellungen für recent müssen nicht angepasst werden, da die Einträge durch ein shell-script verwaltet werden. Ich habe ‘update’ und ‘seonds’ nur eingetragen, damit das ganze noch halbwegs läuft, wenn es mal Probleme mit dem Script geben sollte.

2. mysql einrichten
login into mysql as root and run:

CREATE DATABASE systemlog;
CREATE USER 'systemlog'@'localhost' IDENTIFIED BY 'MYPASSWORD';
GRANT ALL ON systemlog.* TO 'systemlog'@'localhost' IDENTIFIED BY 'MYPASSWORD';
FLUSH PRIVILEGES;
QUIT;

Importiere die table-struktur:
mysql systemlog < clamavtraffic.sql

3. setup apache
Logformat definieren:
LogFormat "%v %h %l %u %t \"%r\" %>s %B \"%{Referer}i\" \"%{User-Agent}i\"" clamav

Der vhost für den Mirror muss das passende Logformat verwenden und das access-log an syslog senden:
CustomLog "| /bin/logger -t apache2" clamav

You can use this logformat generally, so all apache-access-logs are send through syslog. The syslog-handling is explained below.

restart apache

4. syslog
Schreibe IP und Traffic in eine Datenbank – user, password, und database bitte anpassen.

destination db_traffic {
program("/usr/bin/mysql -usystemlog -pMYPASSWORD systemlog"
template("INSERT INTO clamavtraffic VALUES (inet_aton('${.apache.client_ip}') ,${.apache.content_length},CURRENT_TIMESTAMP,1 ) ON duplicate KEY UPDATE bytes=bytes+${.apache.content_length}, hits=hits+1;\n")
template-escape(yes));
};

Es soll nur der Traffic für database.clamav.net in die Datenbank geschrieben werden. Und natürlich auch nur, wenn der Download erfolgreich war (=apache-code 200):

filter f_mirror-traffic {
match ("200" value(".apache.request_status"));
};

Parse nur Messages von apache.

filter f_apache2 {
program('apache2'); # apache use /bin/logger -t apache2
};

Parse apache-log-messages und teile das ganze auf.

parser p_apache-access {
csv-parser(columns(
".apache.domain",
".apache.client_ip",
".apache.ident_name",
".apache.user_name",
".apache.timestamp",
".apache.timestamp2",
".apache.request_url",
".apache.request_status",
".apache.content_length",
".apache.referer",
".apache.user_agent")
flags(escape-double-char,strip-whitespace)
delimiters(" ")
quote-pairs('""\[\]') );
};

Und hier das log-statement:

log {
source(src);
filter(f_apache2);
parser(p_apache-access);
filter(f_mirror-traffic);
destination (db_traffic);
};

Wenn zusätzlich ein normales access-log geschrieben werden soll, füge einfach eine zweite Destination ein und passe das log-statement an:

destination d_apache-logs {
file("/var/log/httpd/${.apache.domain}/${YEAR}${MONTH}${DAY}-access.log"
template("${.apache.client_ip} ${.apache.ident_name} ${.apache.user_name} [${.apache.timestamp}] \"${.apache.request_url}\" ${.apache.request_status} ${.apache.content_length} \"${.apache.referer}\" \"${.apache.user_agent}\"\n")
template_escape(yes)
perm(0644));
};
log {
source(src);
filter(f_apache2);
parser(p_apache-access);
destination (d_apache-logs);
filter(f_mirror-traffic);
destination (db_traffic);
};

restart syslog

5. shell-script
Zum Schluss brauchen wir nur noch ein kleines shell-script um die IP mit zu viel Traffic aus der Datenbank zu lesen und in die Firewall einzutragen:

#!/bin/bash
# Program to handle traffic on a clamav-mirror
#
# Created: 12/27/2012
# Version: 1.0
# Author: Florian Schaal (info@schaal-24.de)

# Copyright (c) 2012 Florian Schaal (info@schaal-24.de.) All rights reserved.
#
# This plugin is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License.
# See http://www.fsf.org/licensing/licenses/gpl.html
#
# 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.
#
#
MAX_MB_DAY=50 # max. daily traffic per ip
DB=systemlog # sql-database
WHITELIST=”78.46.84.243 78.46.84.244 176.9.89.165 176.9.33.188″ # never block whitelisted IP
HTACCESS=/srv/www/clamav.net/httpdocs/.htaccess # add “Deny from” from .htaccess direct to the firewall

# binaries
IPTABLESBIN=/usr/local/sbin/iptables
SQLBIN=/usr/bin/mysql

# calculate allowed traffic
DOM=`date +%e|cut -d” ” -f2`
let MAX_TRAFFIC=$DOM*$MAX_MB_DAY # set allowed_traffic to day of month * MAX_MB_DAY

# get ip from database with traffic >= MAX_TRAFFIC
$SQLBIN $DB -e “select INET_NTOA(source) as source from clamavtraffic where bytes >= $MAX_TRAFFIC*1048576 order by bytes;”|grep -v source|while read ip; do
# add ip to xt_recent
echo +$ip > /proc/net/xt_recent/DenyAccessClamAV
done

# block clients from .htaccess
grep “Deny from” $HTACCESS | grep -v “env=”| cut -d” ” -f3|while read ip; do
echo +$ip > /proc/net/xt_recent/DenyAccessClamAV
done

# remove whitelisted ip from firewall (checking during ban will increase the runtime, so just remove them)
for ip in $(echo $WHITELIST); do
echo -$ip > /proc/net/xt_recent/DenyAccessClamAV
done

6. cron
Das Script muss natürlich auch regelmäßig laufen. Ich mache das über cron
*/1 * * * * /root/scripts/raffic-clamav.sh &> /dev/null

Damit jeder Client ein tägliches Limit hat, muss die IP auch wieder aus dem Jail gelöscht werden. Hier wieder über cron:
00 0 * * * echo / > /proc/net/xt_recent/DenyAccessClamAV

Das shell-script ermittelt den täglichen Traffic anhand des Datums. Dazu muss die Tabelle am anfang des Monats geleert werden. Also einfach soetwas in cron eintragen:
00 0 1 * * /usr/bin/mysql systemlog -e "TRUNCATE TABLE clamavtraffic;"

You should run cron as root. If you run cron as non-root and/or have no ~/.my.cnf create ~/.my.cnf or use
mysql -u user -ppassword systemlog -e "TRUNCATE TABLE clamavtraffic;"

7. Statistik
Zur Auswertung lassen sich bspw. alle IP mit einem Traffic > 50 MB / Tag mit mysql anzeigen:

use systemlog;
select INET_NTOA(source) as source,last_hit,bytes/1048576 as traffic_mb from clamavtraffic where bytes >= 50*1048576 order by bytes desc;
+----------------+---------------------+------------+
| source | last_hit | traffic_mb |
+----------------+---------------------+------------+
| 208.125.63.xxx | 2013-01-01 11:14:58 | 117.3044 |
| 66.42.247.xxx | 2013-01-01 11:13:51 | 87.9783 |
| 200.13.242.xxx | 2013-01-01 11:55:03 | 67.5427 |
| 98.90.105.xxx | 2013-01-01 11:58:36 | 58.6522 |
| 69.174.87.xxx | 2013-01-01 11:56:35 | 58.6522 |
| 216.70.185.xxx | 2013-01-01 11:30:32 | 50.9471 |
| 113.97.35.xxx | 2013-01-01 11:41:58 | 50.9471 |
| 50.82.231.xxx | 2013-01-01 11:44:08 | 50.9471 |
+----------------+---------------------+------------+
8 rows in set (0.06 sec)

8. Files
clamavtraffic.sql
syslog-ng.conf
traffic-clamav.sh


Kommentar erstellen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Ein Gedanke zu “Traffic auf einem clamav-mirror limitieren