Nous collectons les journaux du pare-feu Mikrotik dans une base de données

Bon après-midi

Je veux vous dire avec quelle facilité et naturellement vous pouvez configurer un serveur de collecte de métadonnées de trafic réseau pour les routeurs Mikrotik.

Objectif: L'objectif sera de stocker les journaux de pare-feu «mâchés» dans une base de données pour une analyse plus approfondie.

Moyens: Toute nouvelle distribution Linux avec rsyslogd v8 et supérieur convient à la mise en œuvre, peut-être que la syntaxe proposée fonctionnera également sur v7. Nous avons également besoin d'un SGBD, j'ai choisi mariadb. La croissance de la base de données variera par rapport au nombre de règles enregistrées, car la taille du lecteur est à votre discrétion, dans mon cas, 30 à 40 règles sont enregistrées, ce qui représente environ 1 200 000 lignes par jour. Au cours du mois d'utilisation de la base de données, index compris, elle est passée à 3,8 Go.

Mécanique: le routeur envoie un journal au serveur distant via UDP. À l'aide d'expressions régulières, le serveur rsyslog nettoie les chaînes d'informations inutiles, génère une insertion SQL et l'envoie au SGBD. Le SGBD, à l'aide d'un déclencheur avant l'insertion, effectue un nettoyage et une séparation supplémentaires des champs qui n'ont pas pu être analysés dans rsyslog.

Configurer RSYSLOG


Modification du fichier /etc/rsyslog.conf
Ajoutez-y les lignes suivantes:

module(load="ommysql") module(load="imudp") input(type="imudp" port="514") 

Ainsi, nous chargeons les modules nécessaires et ouvrons le port 514 UDP.

La ligne de journal de Mikrotik ressemble à ceci:

 20180927155341 BLOCKSMKNETS forward: in:ether6 - LocalTORF out:VLAN55 - RT_INET, src-mac 00:15:17:31:b8:d7, proto TCP (SYN), 192.168.0.234:2457->192.168.6.14:65535, len 60 

Comme vous pouvez le voir, beaucoup d'extra pour le stockage dans la base de données et une sélection claire sera difficile.
En théorie, je dois ajouter de telles données:

 20180927155341 ether6 VLAN5 192.168.0.234 2457 192.168.6.14 65535 00:15:17:31:b8:d7 TCP SYN forward BLOCKSMKNETS 60 

Je n'ai pas pu obtenir une telle ligne en utilisant un seul rsyslog. Les habitués de Rsyslog utilisent POSIX ERE / BRE, il n'y a donc aucun moyen d'appliquer des fonctionnalités comme lookahead ou lookbehind.

Il existe un outil qui vous permet de déboguer les habitués, essayez-le, vous pouvez peut-être séparer le port de l'adresse, ainsi que le nom de l'interface de in: et out:. Gardez juste à l'esprit que certains protocoles sport et dport sont manquants.

En général, ma sortie était la suivante:

 20180927155341 in:ether6 out:VLAN5 192.168.0.234:2457 192.168.6.14:65535 00:15:17:31:b8:d7 TCP (SYN) forward BLOCKSMKNETS 60 

Il existe de la documentation sur la façon de cuisiner les habitués de rsyslog.

Dans la forme finale, le fichier de configuration pour recevoir le journal de Mikrotik /etc/rsyslog.d/20-remote.conf ressemblera à ceci:

 $template tpl_traflog,"insert into traflog.traffic (datetime, inif, outif, src, dst, smac, proto, flags, chain, logpref, len) values ('%timereported:::date-mysql%', '%msg:R,ERE,0,DFLT,0:in:[a-zA-Z]+[0-9]+|in:<[a-zA-Z]+-[a-zA-Z]+>--end%', '%msg:R,ERE,0,BLANK,0:out:[a-zA-Z]+[0-9]+|out:<[a-zA-Z]+-[a-zA-Z]+>--end%', '%msg:R,ERE,0,DFLT,0:([0-9]+\.){3}[0-9]+[:]?([0-9]+)?--end%', '%msg:R,ERE,0,DFLT,1:([0-9]+\.){3}[0-9]+[:]?([0-9]+)?--end%', '%msg:R,ERE,0,BLANK:([0-f]+:){5}[0-f]+--end%', '%msg:R,ERE,0,BLANK:\b[AX]{3,4}\b--end%', '%msg:R,ERE,0,BLANK:\([AZ]+\)|\(([AZ]+\,){1,3}[AZ]+\)--end%', '%msg:R,ERE,0,DFLT:[ax]+--end%', '%msg:F,32:2%', '%msg:R,ERE,0,DFLT:[0-9]+$--end%' )",SQL if ($fromhost-ip == '192.168.0.230') and ($syslogtag contains "firewall") then {action(type="ommysql" server="localhost" serverport="3306" db="traflog" uid="rsyslogger" pwd="rsyslogger" template="tpl_traflog") stop} 

Dans la première ligne, la description du modèle (modèle) est une ligne de code SQL pour le transférer vers le SGBD.
La deuxième ligne est la condition lorsque l'action se produira, c'est-à-dire l'enregistrement dans le SGBD.
La condition ressemble à ceci: si la source du journal = 192.168.0.230 ( if ($fromhost-ip == '192.168.0.230') ) Et si la ligne msg contient "pare-feu" (et ($ syslogtag contient "pare-feu")), alors en utilisant le module ommysql avec les paramètres de connexion ( then {action(type="ommysql" server="localhost" serverport="3306" db="traflog" uid="rsyslogger" pwd="..." ) nous appelons le modèle tpl_traflog ( template="tpl_traflog") ), et après cela, nous arrêtons le traitement de la ligne ( stop} ).

Il est possible que dans votre cas, quelque chose se passe mal, cela peut être dû aux noms des interfaces ou des préfixes de journal, peut-être autre chose. Pour le débogage, nous allons procéder comme suit, commenter la deuxième ligne, ajouter un nouveau modèle et deux nouvelles conditions:

 $template tpl_traflog_test,"%timereported:::date-mysql% %msg:R,ERE,0,DFLT,0:in:[a-zA-Z]+[0-9]+|in:<[a-zA-Z]+-[a-zA-Z]+>--end% %msg:R,ERE,0,BLANK,0:out:[a-zA-Z]+[0-9]+|out:<[a-zA-Z]+-[a-zA-Z]+>--end% %msg:R,ERE,0,DFLT,0:([0-9]+\.){3}[0-9]+[:]?([0-9]+)?--end% %msg:R,ERE,0,DFLT,1:([0-9]+\.){3}[0-9]+[:]?([0-9]+)?--end% %msg:R,ERE,0,BLANK:([0-f]+:){5}[0-f]+--end% %msg:R,ERE,0,BLANK:\b[AX]{3,4}\b--end% %msg:R,ERE,0,BLANK:\([AZ]+\)|\(([AZ]+\,){1,3}[AZ]+\)--end% %msg:R,ERE,0,DFLT:[ax]+--end% %msg:F,32:2% %msg:R,ERE,0,DFLT:[0-9]+$--end%\n" if ($fromhost-ip == '192.168.0.230') then {action(type="omfile" file="/var/log/remote/192.168.0.230.log" )} if ($fromhost-ip == '192.168.0.230') then {action(type="omfile" file="/var/log/remote/192.168.0.230.log" template="tpl_traflog_test" ) stop} 

Redémarrez l'enregistreur.

Le modèle tpl_traflog_test est similaire à tpl_traflog, mais sans SQL INSERT.

La première condition ajoute la ligne non traitée% msg% au fichier /var/log/remote/192.168.0.230.log, car le modèle n'est pas spécifié.

La deuxième condition ajoute la ligne traitée au même fichier.
Il sera donc plus pratique de comparer.
Ensuite, préparez la base de données.

Nous préparons une DB


Nous allons baisser le paramètre SGBD, tout est standard ici.

Nous démarrons la console mysql et exécutons le code suivant:

 --   create database traflog character set utf8 collate utf8_bin; use traflog; --  create table traffic (id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, datetime DATETIME, inif VARCHAR(20), outif VARCHAR(20), src VARCHAR(21), sport INT(5), dst VARCHAR(21), dport INT(5), smac VARCHAR(17), proto VARCHAR(4), flags VARCHAR(11), chain VARCHAR(8), logpref VARCHAR(24), len INT(5)) ENGINE=MYISAM; --  create user rsyslogger@localhost identified by '...'; grant all privileges on traflog.* to rsyslogger@localhost; 

La table est prête, l'utilisateur l'est.

Ajoutez maintenant un déclencheur, il fera ce que l'enregistreur n'a pas réussi à séparer l'adresse du port, nettoyez les noms des interfaces, supprimez les crochets du drapeau:

 --   traffic DELIMITER // create TRIGGER delim_ip_port_flags BEFORE insert ON traffic FOR EACH ROW begin set NEW.inif = REGEXP_REPLACE ((NEW.inif), 'in:', '' ); set NEW.outif = REGEXP_REPLACE ((NEW.outif), 'out:', '' ); set NEW.sport = REGEXP_REPLACE ((NEW.src), '([0-9]+\.){3}[0-9]+:|([0-9]+\.){3}[0-9]+', '' ); set NEW.src = REGEXP_REPLACE ((NEW.src), ':[0-9]+', '' ); set NEW.dport = REGEXP_REPLACE ((NEW.dst), '([0-9]+\.){3}[0-9]+:|([0-9]+\.){3}[0-9]+', '' ); set NEW.dst = REGEXP_REPLACE ((NEW.dst), ':[0-9]+', '' ); set NEW.flags = REGEXP_REPLACE ((NEW.flags), '\\(|\\)', '' ); end // delimiter ; 

REGEXP_REPLACE recherche le deuxième paramètre après le point décimal (saison régulière) et le remplace par le troisième paramètre, dans notre cas, il n'y a rien entre guillemets, par conséquent, il supprime simplement ce qu'il trouve.

Faisons un insert de test, semblable à la façon dont l'enregistreur fera:

 --   insert into traffic (datetime, inif, outif, src, dst, smac, proto, chain, logpref) values (20180730075437, 'in:ether6', 'out:VLAN55', '192.168.0.234:4997', '192.168.6.18:65535', '00:15:17:31:b8:d7', 'TCP', '(SYN)', 'forward', 'BLOCKSMKNETS'); 

Voyons ce qui s'est passé:

 select * from tarffic; 

Si tout est correct, passez à autre chose. Sinon, recherchez l'erreur.

Ajoutez au moins un index. Je ne suis pas un maître dans la création d'index, mais si je comprends bien, dans mysql, il est plus correct d'utiliser des index avec différents champs correspondants pour différentes requêtes, car une requête ne peut utiliser qu'un seul index (ou je me trompe?). Si vous comprenez, faites-le à votre discrétion.
Je dois souvent faire des demandes avec un préfixe spécifique, j'ai donc ajouté cet index:
 --  create index traffic_index on traffic (datetime,logpref,src); 

C'est fait.

Vous devez maintenant commencer à envoyer sur le routeur, ajouter les paramètres et l'action du serveur de journalisation distant, ajouter l'option de journalisation à l'une des règles de pare-feu, ajouter un préfixe de 24 caractères maximum.

Dans la console Mikrotik, cela ressemble à ceci:

 /system logging action set 3 remote=192.168.0.94 src-address=192.168.0.230 add name=remote2 remote=192.168.0.19 syslog-facility=local6 target=remote /system logging add action=remote topics=error,account,critical,event,info add action=remote2 topics=firewall /ip firewall filter ... add action=drop chain=input comment="drop ssh brute forcers" dst-port=22,8291 log=yes log-prefix=DROP_SSH_BRUTE protocol=tcp src-address-list=ssh_blacklist ... 

Où 192.168.0.230 est l'adresse du routeur, 192.168.0.19 est l'adresse du serveur de journaux pour les journaux de pare-feu et 192.168.0.94 est un autre serveur de journaux, j'ai des journaux système Mikrotik qui s'y trouvent, nous n'en avons pas besoin maintenant. Notre configuration est distante2.

Ensuite, voyez ce qui se trouve dans le fichier:

 tail -f /var/log/remote/192.168.0.230.log 

Les lignes du routeur doivent être éparpillées dans le fichier, à moins bien sûr que votre règle ne soit déclenchée assez souvent.

Si certains champs sont manquants, c'est-à-dire que la séquence datetime, inif, outif, src, dst, smac, proto, flags, chain, logpref, len n'est pas suivie, vous pouvez essayer de modifier le paramètre dans les modèles de débogage de l'enregistreur, remplacer BLANK par DLFT. Ensuite, au lieu du vide de n'importe quel champ, certaines lettres apparaîtront, je ne me souviens pas déjà lesquelles. Si cela se produit, alors quelque chose ne va pas avec le programme régulier et vous devez le réparer.

Si tout s'est déroulé comme il se doit, désactivez les conditions de test et le modèle.

Vous devez également exécuter la configuration par défaut dans /etc/rsyslog.d/ ci-dessous, je l'ai renommé en 50-default.conf afin que les journaux distants ne soient pas versés dans le journal système / var / log / message
Redémarrez l'enregistreur.

Attendons un peu que notre base de données soit pleine. Ensuite, nous pouvons commencer la sélection.

Quelques requêtes pour un exemple:

Pour voir la taille de la base de données et le nombre de lignes:
 MariaDB [traflog]> select table_schema as "database", round(sum(data_length + index_length)/1024/1024,2) as "size Mb", TABLE_ROWS as "count rows" from information_schema.tables group by table_schema; +--------------------+---------+------------+ | database | size Mb | count rows | +--------------------+---------+------------+ | information_schema | 0.17 | NULL | | traflog | 3793.39 | 21839553 | +--------------------+---------+------------+ 2 rows in set (0.48 sec) 

Près de 4 Go ont augmenté en un mois, mais cela dépend du nombre et des propriétés des règles de pare-feu enregistrées

Nombre de préfixes enregistrés
Le nombre de préfixes enregistrés n'est pas égal au nombre de règles, certaines règles fonctionnent avec un seul préfixe, mais combien de préfixes au total? et combien de règles ont été élaborées pour eux?:

 MariaDB [traflog]> select logpref,count(logpref) from traffic group by logpref order by count(logpref) desc; +----------------------+----------------+ | logpref | count(logpref) | +----------------------+----------------+ | ACCEPT_TORF_INET | 14582602 | | ACCEPT_SMK_PPP | 1085791 | | DROP_FORWARD_INVALID | 982374 | | REJECT_BNK01 | 961503 | | ACCEPT_MMAX_TORF | 802455 | | ACCEPT_TORF_PPP | 736803 | | SMTP_DNAT | 689533 | | ACCEPT_SMK_INET | 451411 | | ACCEPT_INET_TORF | 389857 | | BLOCK_SMKNETS | 335424 | | DROP_SMTP_BRUTE | 285850 | | ACCEPT_ROZN_TORF | 154811 | | ACCEPT_TORF_MMAX | 148393 | | DROP_ETHALL_ETHALL | 80679 | | ACCEPT_SMTP | 48921 | | DROP_SMTP_DDOS | 32190 | | RDP_DNAT | 28757 | | ACCEPT_TORF_ROZN | 18456 | | SIP_DNAT | 15494 | | 1CWEB_DNAT | 6406 | | BLOCKSMKNETS | 5789 | | DROP_SSH_BRUTE | 3162 | | POP_DNAT | 1997 | | DROP_RDP_BRUTE | 442 | | DROP_BNK01 | 291 | | DROPALL | 138 | | ACCEPT_RTP_FORWARD | 90 | | REJECT_SMTP_BRUTE | 72 | | L2TP_INPUT_ACCEPT | 33 | +----------------------+----------------+ 29 rows in set (2 min 51.03 sec) 

ACCEPT_TORF_INET est le leader, par ce préfixe vous pouvez trouver tous ceux qui sont allés sur Internet à partir de notre réseau local, les protocoles et les ports sont enregistrés, le temps viendra et l'accès sera fermé pour certains. Il existe des données de référence pour les futurs travaux sur les bogues.

Chef de lance smtp
Voyons qui aujourd'hui a essayé d'accéder au serveur smtp:

 MariaDB [traflog]> select src,count(dport) from traffic where logpref='SMTP_DNAT' and datetime > '2018101600000000' group by src order by count(dport) desc limit 10; +----------------+--------------+ | src | count(dport) | +----------------+--------------+ | 191.96.249.92 | 12440 | | 191.96.249.24 | 4556 | | 191.96.249.61 | 4537 | | 185.255.31.122 | 3119 | | 178.57.79.250 | 226 | | 185.36.81.174 | 216 | | 185.234.219.32 | 211 | | 89.248.162.145 | 40 | | 45.125.66.157 | 32 | | 188.165.124.31 | 21 | +----------------+--------------+ 10 rows in set, 1 warning (21.36 sec) 

De toute évidence, le nœud 191.96.249.92 est le gagnant aujourd'hui. Voyons dans quelles règles enregistrées il est toujours apparu:

 MariaDB [traflog]> select src,dport,count(dport),logpref from traffic where src='191.96.249.92' group by logpref order by count(dport) desc; +---------------+-------+--------------+-----------------+ | src | dport | count(dport) | logpref | +---------------+-------+--------------+-----------------+ | 191.96.249.92 | 25 | 226989 | SMTP_DNAT | | 191.96.249.92 | 25 | 170714 | DROP_SMTP_BRUTE | | 191.96.249.92 | 25 | 2907 | DROP_SMTP_DDOS | | 191.96.249.92 | 25 | 2061 | ACCEPT_SMTP | +---------------+-------+--------------+-----------------+ 4 rows in set (10 min 44.21 sec) 

Celui-ci est spécialisé uniquement dans smtp, ~ 1% des hits pour avoir essayé de deviner le mot de passe ou pour avoir envoyé des ordures, le reste est allé aux bains.

La demande a pris 10 minutes, c'est beaucoup, les index actuels ne lui conviennent pas, ou vous pouvez reformuler la demande, mais maintenant nous n'en parlerons pas.

À l'avenir, il est prévu de visser l'interface Web avec des requêtes et des formulaires standard.
Le vecteur est donné, j'espère que cet article vous sera utile.

Merci à tous!

Références:

Documentation Rsyslog
Documentation mysql
Documentation de journalisation Mikrotik

Merci à la communauté LOR pour les conseils.

UPD.1
Ajout du champ des drapeaux à la base de données, vous pouvez maintenant suivre la durée de la connexion en attrapant SYN, FIN.
Correction de quelques bugs dans les habitués de rsyslog, ainsi que des déclencheurs mysql.

Curieusement, la règle par défaut defconf: drop invalid supprime tous les derniers paquets de connexions TCP, par conséquent, tous les nœuds qui tentent de fermer la connexion en science échouent, envoyant plusieurs FIN. Est-ce bien?

J'ai ajouté une règle permettant la traversée TCP avec les drapeaux ACK, FIN.

Sous le spoiler SQL, une procédure qui affiche l'heure des connexions TCP au cours des cinq dernières minutes
connections_list ()
 DROP PROCEDURE IF EXISTS connections_list; DELIMITER // CREATE PROCEDURE connections_list() BEGIN DECLARE logid BIGINT UNSIGNED; DECLARE done INT DEFAULT FALSE; DECLARE datefin DATETIME; DECLARE datesyn DATETIME; DECLARE conntime TIME; DECLARE connsport INT; DECLARE conndport INT; DECLARE connsrc VARCHAR(21); DECLARE conndst VARCHAR(21); DECLARE cur CURSOR FOR SELECT id,datetime,src,sport,dst,dport FROM conn_syn_fin WHERE flags='SYN'; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=TRUE; DROP TABLE IF EXISTS conn_syn_fin; DROP TABLE IF EXISTS connless; CREATE temporary TABLE connless(datestart DATETIME,dateend DATETIME,duration TIME,src VARCHAR(21),sport INT,dst VARCHAR(21),dport INT); CREATE temporary TABLE conn_syn_fin (SELECT * from traffic WHERE datetime > now() - interval 5 minute and src in (select src from traffic where datetime > now() - interval 5 minute and logpref='TCP_FIN' and flags like '%FIN%') and (flags like '%SYN%' or flags like '%FIN%') order by id); OPEN cur; read_loop: LOOP FETCH cur INTO logid,datesyn,connsrc,connsport,conndst,conndport; IF done THEN LEAVE read_loop; END IF; set datefin=(SELECT datetime FROM conn_syn_fin WHERE id>logid and src=connsrc and sport=connsport and flags like '%FIN%' and dst=conndst and dport=conndport limit 1); set conntime=(SELECT timediff(datefin,datesyn)); INSERT INTO connless (datestart,dateend,duration,src,sport,dst,dport) value (datesyn,datefin,conntime,connsrc,connsport,conndst,conndport); END LOOP; CLOSE cur; select * from connless; END; // DELIMITER ; 


À la suite de la procédure, deux tables temporaires seront créées.
La table conn_syn_fin contient des entrées de journal avec les drapeaux SYN et FIN, puis une recherche est effectuée à l'aide du curseur dans cette table.
Le tableau connless contient une liste de connexions, ouvertes et terminées, terminées ont une durée d'ouverture, respectivement, non.
Notez le temps d'échantillonnage moins cinq minutes de l'heure actuelle. Ma demande est lente. Lentement, il passe par la recherche du curseur, traite environ 10 enregistrements par seconde, a essayé de l'accélérer dans tous les sens, mais le temps d'exécution est toujours le même.
Notez également que cette procédure est uniquement à des fins de démonstration. Si vous devez faire une sélection pour un src / sport / dst / dport spécifique, il est préférable de faire une procédure distincte similaire à celle-ci. Si vous êtes un maître SQL, vous pouvez mieux écrire votre requête.

appeler connections_list ();
 MariaDB [traflog]> call connections_list(); +---------------------+---------------------+----------+---------------+-------+-----------------+-------+ | datestart | dateend | duration | src | sport | dst | dport | +---------------------+---------------------+----------+---------------+-------+-----------------+-------+ | 2019-03-20 14:12:19 | 2019-03-20 14:13:14 | 00:00:55 | 192.168.0.81 | 41868 | 87.250.250.207 | 443 | | 2019-03-20 14:12:25 | NULL | NULL | 192.168.0.65 | 49311 | 52.5.23.125 | 443 | | 2019-03-20 14:12:31 | 2019-03-20 14:12:51 | 00:00:20 | 192.168.0.104 | 54433 | 217.69.139.42 | 443 | | 2019-03-20 14:12:31 | 2019-03-20 14:12:51 | 00:00:20 | 192.168.0.104 | 54434 | 217.69.139.42 | 443 | | 2019-03-20 14:12:32 | NULL | NULL | 192.168.0.119 | 37977 | 209.85.233.95 | 443 | ... | 2019-03-20 14:17:12 | NULL | NULL | 192.168.0.119 | 39331 | 91.213.158.131 | 443 | | 2019-03-20 14:17:13 | NULL | NULL | 192.168.0.90 | 63388 | 87.240.185.236 | 443 | +---------------------+---------------------+----------+---------------+-------+-----------------+-------+ 399 rows in set (33.17 sec) Query OK, 0 rows affected (33.18 sec) 



Une fois la procédure terminée, les tables temporaires conn_syn_fin et connless restent, vous pouvez les regarder plus en détail si vous trouvez quelque chose de suspect ou non fiable. Après avoir démarré la procédure, les anciennes tables sont supprimées et de nouvelles apparaissent. Écrivez si vous trouvez une erreur.

Source: https://habr.com/ru/post/fr427159/


All Articles