rem

.co

Gentoo: Updating and Cleaning

| Comments

Keeping your Gentoo Linux server up to date isn’t as straightforward as let’s say an Ubuntu box, where you would just run $ apt-get update && apt-get upgrade && apt-get clean for example.

Gentoo is far too flexible for a one size fits all approach. The commands outlined below come pretty close for daily use though:

2013: Blogging With Octopress

| Comments

Well. Here we are!

New year, new approach to blogging.

In the past year I haven’t managed to push out a lot of blog articles though I’ve been working with loads of new, interesting technologies and approaches.

Being a programmer, I blame the software I use to blog. In my case that’s the TYPO3 installation at rem.co. It’s recently been updated to TYPO3 6.0, and I haven’t gotten around to fixing all the bugs that appeared after such a major upgrade.

Moreover, that site uses the ancient tt_news plugin as a blog substitute, which makes blogging a bit… troublesome to put it mildly. Especially “advanced” stuff like including code snippets and multiple layouts in one article.

Then Octopress enters the stage, combining a lot of my favorite technologies to provide a somewhat nerdy approach to blogging.

Fail2Ban PhpMyAdmin Script

| Comments

While examining my webserver statistics, I noticed that quite a lot 404’s are being served on most of my domains to scan bots that are trying to find exploits in possible running PHPMyAdmin configurations. Though harmless if you keep a clean ship with a decently configured PHPMyAdmin and the latest updates like I do, I still decided I couldn’t let this behaviour unanswered. So I took action, and wrote a small fail2ban filter that permanently drops all traffic from the IP addresses these scans originate from, like I do with every address that misbehaves in any way.

The regex used won’t capture all attempts, but with my configuration only 1 hit is enough to get you banned (the scripts these scans call are main.php and config.inc.php, which aren’t to be called directly, especially not when they fail with a 404 like these), and all scanning attempts I’ve seen so far cycle through at least 20 different combinations.

Well, enough talk, here is the filter.d file:

filter.d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 # Fail2Ban configuration file
 #
 # Author: Remco Overdijk
 #
 # $Revision: 4 $
 #

 [Definition]

 # Option:  failregex
 # Notes.:  regex to match the 404'ed PMA file in the logfile. The
 #          host must be matched by a group named 'host'. The tag '<HOST>' can
 #          be used for standard IP/hostname matching and is only an alias for
 #          (?:::f{4,6}:)?(?P<host>\S+)
 # Values:  TEXT
 #
 failregex = <HOST> -.*'GET .*(php|pma|PMA|p/m/a|db|sql|admin).*/(config/config\.inc|main)\.php.*'.*404.*

 # Option:  ignoreregex
 # Notes.:  regex to ignore. If this regex matches, the line is ignored.
 # Values:  TEXT
 #
 ignoreregex =

And this is of course accompanied by a bit in jail.conf:

jail.conf
1
2
3
4
5
6
7
8
9
[apache-pma]

 enabled = true
 filter = apache-pma
 action = iptables-allports[name=pma]
 mail-whois[name=pma, dest=<YOURADDRHERE>]
 logpath = /var/log/apache2/access_log
 bantime = -1
 maxretry = 1

Works for me, another 20 additional IPs/day onto the shitlist!

Update:

It seems another variation of these scans are hitting the NIC’s quite often; One for Zen Cart to be more precise. You can easily add support countering this scanner as well, simply by expanding the failregex with this line:

^<HOST> -.*'GET .*(cart|boutique|catalog|butik|shop|zen|store).*/install\.txt.*'.*404.*

You can put multiple regexes within one failregex, just put each one on a new line.

IPMI Graphs in Munin

| Comments

It is possible to monitor fan speeds and temperatures on Dell Poweredge servers under Linux. You can achieve this by reading out the IPMI data that is available on the system.

I used the steps on this website to buffer the data gathered by IPMI to use in Cacti.

However, in addition to Cacti I also use Munin to monitor various system parameters. Wouldn’t it be nice to incorporate graphs for fan speeds and temperatures in Munin? I thought so, so I developed a way to do this.

In this case the solution consists out of 2 munin plugins and a PHP script that formats the IPMI values for graphing use. This could also be done directly in the plugins, but since I’m not such an awk and sed expert I chose to let a PHP script handle the transformation, so I’d be done sooner (time is money my friends!).

First up, the PHP script:

/usr/bin/ipmi.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/php
 <?php
 $handle = fopen('/dev/shm/dell.ipmi','r');
 while(!feof($handle)) {
 $line = fgets($handle);
 if(preg_match('/AmbientTemp/',$line)) echo 'tambi.value '.fgets($handle);
 if(preg_match('/CPU1Temp/',$line)) echo 'tcpu1.value '.fgets($handle);
 if(preg_match('/CPU2Temp/',$line)) echo 'tcpu2.value '.fgets($handle);
 if(preg_match('/Fan1/',$line)) echo 'fan1.value '.fgets($handle);
 if(preg_match('/Fan2/',$line)) echo 'fan2.value '.fgets($handle);
 if(preg_match('/Fan3/',$line)) echo 'fan3.value '.fgets($handle);
 if(preg_match('/Fan4/',$line)) echo 'fan4.value '.fgets($handle);
 if(preg_match('/Fan5/',$line)) echo 'fan5.value '.fgets($handle);
 if(preg_match('/Fan6/',$line)) echo 'fan6.value '.fgets($handle);
 if(preg_match('/PlanarTemp/',$line)) echo 'tplan.value '.fgets($handle);
 if(preg_match('/RiserTemp/',$line)) echo 'trise.value '.fgets($handle);
 }
 fclose($handle);
 ?>

With the SHM values now being prefixed with matching Munin values, we can graph them in 2 plugins:

/etc/munin/plugins/ipmitemps
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/bin/sh
 if [ '$1' = 'autoconf' ]; then
 if [ -r /dev/shm/dell.ipmi ]; then
 echo yes
 exit 0
 else
 echo no
 exit 1
 fi
 fi

 if [ '$1' = 'config' ]; then
 echo 'graph_title Dell 2850 IPMI Temperatures'
 echo 'graph_order tcpu1 tcpu2 tambi tplan trise'
 echo 'graph_vlabel Temperature (Deg C)'
 echo 'graph_scale yes'
 echo 'graph_info This graph shows sensor temperatures'
 echo 'graph_category system'
 echo 'tcpu1.label CPU1'
 echo 'tcpu1.warning 80'
 echo 'tcpu1.critical 90'
 echo 'tcpu1.info XEON CPU 1 Temperature'
 echo 'tcpu2.label CPU2'
 echo 'tcpu2.warning 80'
 echo 'tcpu2.critical 90'
 echo 'tcpu2.info XEON CPU 2 Temperature'
 echo 'tambi.label Ambient'
 echo 'tambi.warning 80'
 echo 'tambi.critical 90'
 echo 'tambi.info Ambient Temperature'
 echo 'tplan.label Planar'
 echo 'tplan.warning 80'
 echo 'tplan.critical 90'
 echo 'tplan.info Planar Temperature'
 echo 'trise.label Riser'
 echo 'trise.warning 80'
 echo 'trise.critical 90'
 echo 'trise.info Riser Temperature'
 exit 0
 fi
 /usr/bin/ipmi.php

and

/etc/munin/plugins/ipmifans
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 #!/bin/sh

 if [ '$1' = 'autoconf' ]; then
 if [ -r /dev/shm/dell.ipmi ]; then
 echo yes
 exit 0
 else
 echo no
 exit 1
 fi
 fi

 if [ '$1' = 'config' ]; then

 echo 'graph_title Dell 2850 IPMI Fanspeeds'
 echo 'graph_order fan1 fan2 fan3 fan4 fan5 fan6'
 echo 'graph_vlabel Fanspeed (RPM)'
 echo 'graph_scale yes'
 echo 'graph_info This graph shows fanspeeds'
 echo 'graph_category system'
 echo 'fan1.label Fan1'
 echo 'fan1.info Fan1 speed'
 echo 'fan2.label Fan2'
 echo 'fan2.info Fan2 speed'
 echo 'fan3.label Fan3'
 echo 'fan3.info Fan3 speed'
 echo 'fan4.label Fan4'
 echo 'fan4.info Fan4 speed'
 echo 'fan5.label Fan5'
 echo 'fan5.info Fan5 speed'
 echo 'fan6.label Fan6'
 echo 'fan6.info Fan6 speed'
 exit 0
 fi
 /usr/bin/ipmi.php

Restart your munin-node, run the munin crons, and voila, graphs! With these settings they will look like this:

insert image

You can modify the params to change the graphs ofcourse.

Cisco VoIP Oplossing Voor Ziggo Telefonie

| Comments

This article is only available in Dutch.

Sinds een recente verhuizing beschik ik thuis over een Ziggo Alles-in-1 Plus pakket, met internet, tv, én telefonie.

Daarvoor maakte ik gebruik van een Cisco VoIP netwerk op basis van een externe SIP provider. Natuurlijk wilde ik mijn Cisco netwerk blijven gebruiken, maar dan wel op basis van de Ziggo telefonie aansluiting.

Helaas maakt Ziggo gebruik van het PacketCable protocol over EuroDocsis, in plaats van SIP. Daarnaast heeft het Motorola SurfBoard modem dat bij het Ziggo abonnement geleverd wordt geen SIP interface voor het LAN, maar beschikt over 2 POTS poorten op RJ11 connectoren.

Alsof dat nog niet genoeg ellende is maakt Ziggo geen gebruik van DTMF ETSI signalering voor onder andere nummerweergave, maar in plaats daarvan het veel minder bekende Bellcore FSK.

Goed, tot zover de met technische termen bezaaide probleemvorming. Tijd voor een oplossing om je mooie VoIP apparatuur aan de gang te krijgen! Er zijn naar mijn ervaring bijzonder weinig how-to’s / handleidingen of tutorials te vinden op het internet om een dergelijke setup te maken, en de informatie over het Ziggo netwerk is zo mogelijk nog beperkter, vandaar deze uitgebreide uitleg om voor eens en altijd van dat probleem af te zijn!

De basis

Als basis voor deze tutorial gebruik ik de volgende hardware:

Naast deze hardware heb ik nog een tweede router (voor o.a. Wireless) en de nodige switching apparatuur draaien, maar dat maakt voor het doel van deze tutorial weinig uit.

Een schematische weergave van de aan elkaar geknoopte hardware:

insert image

Stap 1: De hardware verbindingen

De meest ideale situatie zou zijn om de (digitale) VoIP toestellen digitaal via SIP of een ander protocol met Ziggo te verbinden om zo de beste gesprekskwaliteit en de laagste latency te krijgen. Helaas is dit dus niet mogelijk in combinatie met het Motorola modem, en zijn we veroordeeld tot het gebruik van analoge lijnen. Gelukkig beschikt de Cisco 1760 router over de mogelijkheid om aangesloten te worden op PSTN lijnen doormiddel van een Voice Interface Card. Om van deze mogelijkheid gebruik te maken moet je router over een aantal zaken beschikken:

  • De mogelijkheid om VIC’s te plaatsen
  • Een PVDM kaart waarop een DSP chip aanwezig is om van je router een Voice Gateway te maken. (In dit voorbeeld wordt een PVDM-256k-4= gebruikt)
  • De IOS versie op de router dient over het IP Voice feature pack te beschikken. (In dit voorbeeld wordt IOS (C1700-ADVENTERPRISEK9-M) Version 12.4(15)T5 (fc4) gebruikt, die daar naast Cisco Unified Call Manager ook over beschikt)

De VIC die je vervolgens nodig hebt is van het Foreign Exchange Office (FXO) type. Andere typen als Ear&Mouth (E/M) en Foreign Exchange Subscriber (FXS) zijn voor dit doeleinde NIET geschikt, en kunnen tevens niet gelijktijdig op dezelfde router gebruikt worden. Maak dus niet dezelfde fout als ik door een VIC-2E/M aan te schaffen omdat deze vrij goedkoop zijn, in de hoop dat het werkt.. want het werkt niet! Helaas is de FXO kaart de duurste van de collectie, zeker als je nummerweergave wilt. Een overzicht van alle beschikbare FXO kaarten kun je vinden bij Cisco. Mijn advies is om de VIC2-2FXO te nemen, aangezien deze over alle benodigde opties beschikt (helaas wel de duurste kaart met 2 poorten).

Goed, PVDM en VIC in de router, opstarten, en de analoge telefoonlijnen met RJ11 pluggen van de 2 VIC poorten naar de 2 poorten op het modem verbinden en we zijn klaar om te configureren!

Stap 2: Router configuratie

Gebruik je favoriete protocol om in te loggen op je router (RS232, ssh of telnet), zet de router in enable mode, en check of je PVDM en VIC actief zijn en werken en check gelijk op welke poorten ze zitten. De VIC2-2FXO zit in dit voorbeeld in slot 2, en beschikt over voice-ports 2/0 en 2/1. Ga vervolgens verder met de configuratie van het geheel via conf t. Ik ga er van uit dat de basis van je router goed is ingesteld, en ga dus alleen in op de voice settings.

voice card

1
2
3
trunk group pots
 hunt-scheme round-robin
 !

We maken een hunt group aan voor de analoge lijnen, zodat de router zelf bepaalt welke lijn vrij is en deze voor inkomende/uitgaande oproepen gebruikt.

voice ports

1
2
3
4
5
6
7
8
9
10
11
12
voice-port 2/0
 trunk-group pots
 cptone JP
 connection plar 1337
 caller-id enable
 !
 voice-port 2/1
 trunk-group pots
 cptone JP
 connection plar 1337
 caller-id enable
 !

Configureer beide voice-ports, en voeg ze aan de trunk group toe. Zet caller-id aan. De cptone parameter geeft aan wat voor landinstellingen de PSTN lijn gebruikt. Normaal gesproken zou dit NL zijn voor gebruik in Nederland, maar aangezien normale PSTN lijnen hier DTMF ETSI gebruiken werkt dat niet voor onze doeleinden. Daarom gebruiken we JP (Japan), aangezien daar FSK gebruikt wordt. Het klinkt wat vreemd, maar werkt perfect. Tenslotte voegen we een "private line automatic ringdown" (PLAR) toe op de lijnen. Hiermee zorgen we dat wanneer een oproep binnenkomt op de lijn, deze automatisch wordt doorgeschakeld naar de opgegeven PLAR lijn. De opgegeven PLAR lijn is in dit geval een Overlay voor de ephone DN’s die we later zullen configureren. In het kort komt het erop neer dat zodra de lijn gebeld wordt de Overlay wordt aangeroepen, en alle toestellen die gekoppeld zijn aan de DN zullen overgaan.

codecs

1
2
3
4
voice class codec 1
 codec preference 1 g711ulaw
 codec preference 2 g711alaw
 !

Niet zo belangrijk voor dit voorbeeld, maar toch is het handig om in te stellen welke codecs gebruikt worden binnen het VoIP netwerk (bellen tussen de toestellen onderling). de PVDM DSP handelt verder de codecs voor de analoge lijnen af.

dial-peers

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
dial-peer voice 1 pots
 trunkgroup pots
 preference 7
 destination-pattern 0[1-9]........
 incoming called-number .
 no digit-strip
 forward-digits all
 !
 dial-peer voice 2 pots
 trunkgroup pots
 destination-pattern 00[1-9]..........
 incoming called-number .
 no digit-strip
 forward-digits all
 !
 dial-peer voice 3 pots
 trunkgroup pots
 destination-pattern 1233
 incoming called-number .
 no digit-strip
 forward-digits all
 !

Voeg dial-peers toe voor alle inkomende en uitgaande belpatronen. Voor het gemak gebruiken alle dial-peers hier hetzelfde inkomende patroon (.), om het binnenkomende gesprek rechtstreeks zonder modificatie door te zetten naar de PLAR. De dial-peers dienen uiteraard net als de lijnen gekoppeld te worden aan de trunk group. De destination-patterns geven aan welke telefoonnummers geldig zijn, en indien een gedraaid nummer voldoet aan één van de patterns wordt het gesprek doorgezet naar de trunk, en zal dus gebruik maken van één van de PSTN lijnen. De opgegeven patterns zijn alles behalve compleet, maar dekken in ieder geval de volgende situaties:

  • Een 10 cijferig telefoonnummer, beginnend met een 0, gevolgd door een 1 tot 9 en vervolgens nog 8 random cijfers. (Bijv. 0131212121)
  • Een 13 cijferig internationaal telefoonnummer, beginnend met 00, gevolgd door een 1 tot 9 en nog 10 random cijfers. (Bijv. 0031131212121)
  • Het nummer van de Ziggo voicemail, 1233.

Standaard stript de dial-peer opgegeven dialpatterns van het nummer af alvorens deze door te geven, dus gebruiken we no digit-strip om dit te voorkomen. Tenslotte zorgen we dat alle overige gedraaide nummers (right-justified) worden doorgezet naar de PSTN lijn.

telephony service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
telephony-service
 load 7960-7940 P000308000400
 load 7971 SCCP70.8-2-2SR1S
 load 7970 SCCP70.8-2-2SR1S
 max-ephones 5
 max-dn 5
 ip source-address 10.0.0.5 port 2000
 auto assign 1 to 5
 calling-number initiator
 system message ccme1 ready..
 url services http://10.0.0.35:8080/CSFEnterprise-war/
 cnf-file perphone
 network-locale NL
 time-zone 28
 time-format 24
 date-format dd-mm-yy
 voicemail 1233
 max-conferences 4 gain -6
 moh rick.au
 transfer-system full-consult dss
 create cnf-files version-stamp 7960 Apr 17 2009 09:02:53
 !

Configureer je VoIP telephony service naar eigen inzicht. Bovenstaande configuratie is slechts een voorbeeld van mijn eigen configuratie. Zaken waar je op moet letten zijn of je loads goed gespecificeerd zijn (denk ook aan de tftp statements), het voicemail nummer (in dit geval extern naar Ziggo) voor wanneer op de Berichten knop op de telefoon wordt gedrukt, en de land instellingen.

DN’s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ephone-dn 1
 number 1234
 label Woonkamer
 description 013xxxxxxx
 name Woonkamer
 call-forward busy 1235
 call-forward noan 1235 timeout 120
 ephone-hunt login
 !
 ephone-dn 2
 number 1235
 label Kantoor
 description 013xxxxxxx
 name Kantoor
 call-forward busy 1234
 call-forward noan 1234 timeout 120
 ephone-hunt login
 !
 ephone-dn 3
 number 1337
 label Extern
 description Extern Overlay
 name Extern
 !

Voeg een ephone DN toe voor ieder toestel dat je aan gaat sluiten, met een eigen nummer (dit nummer en label is zichtbaar op het toestel, en wordt gebruikt om onderling met elkaar te bellen). Ik heb de DN’s zo ingesteld dat wanneer ze niet beantwoord worden/bezet zijn automatisch de andere DN wordt aangeroepen. Voeg tenslotte de Overlay DN toe die in de voice-ports is gespecificeerd, en geef het een label zodat je op je telefoons kunt zien dat een oproep van de Externe lijn afkomstig is.

ephones

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ephone 3
 mac-address 000D.EDAB.5055
 speed-dial 1 xxxxxxxxxx label "xxxx"
 type 7940
 button 1o2,3
 pin 1235
 !
 ephone 4
 conference admin
 mac-address 0015.2BD2.01C7
 username "remco" password xxxxxxxx
 speed-dial 1 xxxxxxxxxx label "xxxx"
 speed-dial 2 xxxxxxxxxx label "xxxx"
 speed-dial 3 xxxxxxxxxx label "xxxx"
 type 7971
 button 1o1,3
 pin 1234
 !

Voeg ephones toe voor ieder toestel dat je hebt. Configuratie hangt natuurlijk af van het toestel dat je hebt en hoe je wil dat hij reageert. Voeg users en speeddials toe naar wens. De enige cruciale data is het MAC adres (anders weet de router niet welk toestel aan de ephone te koppelen), en de button configuratie.

De button configuratie zorgt er namelijk voor dat een lijn aan het toestel gekoppeld wordt, inclusief de Overlay DN. zo heeft ephone 3 een button 1o2,3 . Dit houdt in dat DN’s 2 en 3 in overlay mode onder button 1 van dit toestel zitten. De router zoekt automatisch de meest unieke lijn uit, en presenteert deze DN onder de knop. Aangezien Overlay DN 3 op meerdere toestellen wordt gebruikt is deze het minst uniek, en zal enkel op de achtergrond aanwezig zijn. ephone 4 maakt op button 1 gebruik van DN’s 1 en 3. Op deze manier presenteren beide toestellen hun eigen nummer, maar luisteren op de achtergrond wel mee naar de Overlay DN, en zullen dus gelijktijdig overgaan zodra een gesprek binnenkomt op de trunk en deze wordt doorgezet naar de Overlay DN.

Stap 3: klaar!

Save je configuratie door een copy run start te doen, en je zou aan de gang moeten kunnen!

  • Onderling bellen kan door de unieke DN van een ephone te bellen (1234 en 1235 in dit geval)
  • Extern bellen kan door een telefoonnummer te draaien dat gespecificeerd is in één van de dial-peers
  • Voicemail is gekoppeld onder de berichtenknop van een toestel.
  • Binnenkomende gesprekken zullen op alle toestellen die gebruik maken van de Overlay DN overgaan, inclusief nummerweergave!

Veel succes!

Iptables: Creating Persistent Bans From Fail2Ban

| Comments

On my servers I use the nifty program Fail2Ban to perform logbased automatic firewalling of ‘bad’ ip’s.

The idea behind this is easy: Some IP performs an action I don’t approve of. This can be any number of things, e.g. requesting pages in Apache that are commonly accessed by bots and/or scanners, or trying to log in to SSH with accounts that do not exist on the system. This bad behavior gets logged, and Fail2Ban keeps tabs on those logs, and using a number of rules it determines if a host is ‘bad’ enough to temporarily or permanently ban all access to the server. It does so by adding a few chains to Iptables (one for each thing it checks for), and dynamically adding/removing IP’s to/from these chains.

This all works perfectly. However, there’s one issue; When Iptables gets reloaded, it restores its default rules, removing the Fail2Ban chains and all the rules they contain, even if the ip’s in the chain were marked as permanent.

I created a workaround for this problem, consisting of two simple steps:

  • When a ‘bad’ ip gets banned, it’s added to the Iptables chain, but also written to a file, containing all collected ‘bad’ ip’s. (I use /etc/shitlist for this purpose).

  • Whenever Iptables gets reloaded, I run a PHP script that checks the /etc/shitlist file for ‘safe’ and duplicate ip’s, and writes all other ip’s to the permanent Blocklist chain. (The checking for ‘safe’ ip’s might be a bit unneeded, but with my Fail2Ban rules it’s possible that one of my own ip’s gets banned for 10 minutes if a SSH login attempt fails for 5 times. Though it’s a temporary ban, the ip will still get written to the shitlist, and would end up in the permanent Blocklist).

To make this work, I made the following changes:

Every jail in Fail2Ban uses an action.d script to perform (un)banning. I defaulted all actions to an action script called iptables-allports.conf. Basically this action drops everything in Iptables if a package originates from the ‘bad’ IP.

I updated the ban action such that:

1
2
actionban = iptables -I fail2ban-<name> 1 -s <ip> -j DROP
echo <ip> >> /etc/shitlist

After that I created a PHP script that updates Iptables with the ip’s contained in the shitlist:

shitlist.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php
/** script that loads a shitlist file into iptables */
//CONFIG 
$shitlists = array("/root/list.txt","/etc/shitlist");
$chain = "Blocklist";
$safelist = array("x.x.x.x", "y.y.y.y", "z.z.z.z");

echo "Reading current IPTABLES state\r\n";
$data = shell_exec('iptables -S '.$chain);
$iparr = explode(' ',$data);
$j = 0;
$ref = array();
for($i=0;$i<sizeof($iparr);$i++) {
  if(substr_count($iparr[$i],".")==3) {
      $ref[$j] = $iparr[$i];
       $j++;
   }
}
sort($ref);
$total = 0;
foreach($shitlists as $shitlist) {
  echo "Reading shitlist at $shitlist\r\n";
  //READ FILE
  $fh = fopen($shitlist,'r');
  if($fh) {
      $itt = 0;
      $iparr = array();
      while(!feof($fh)) {
          $ip = trim(fgets($fh));
          if(strlen($ip)>6) {
              if(array_search($ip,$iparr)===false&&array_search($ip,$safelist)===false&&array_search($ip."/32",$ref)===false) {
                  $iparr[] = $ip;
                  echo "Now adding $ip to $chain\r\n";
                  $ins = 18+$itt;
                  shell_exec("iptables -I ".$chain." ".$ins." -s ".$ip."/32 -j DROP");
                  $itt++;
              }
          }
      }
      fclose($fh);
      echo "Finished adding $itt ip's from list $shitlist to chain $chain . Bye!\r\n";
      $total = $total + $itt;
  } else {
      echo "Could not open shitlist file $shitlist . Skipping this list\r\n";
  }
}
echo "Finished adding $total ip's to chain $chain from ".sizeof($shitlists)." shitlists.\r\n";
?> 

You can run the script from the commandline (as root!) simply by stating php shitlist.php, or add it to the startup script of your Iptables installation.

Hope this helps keeping your NIC’s available for VALID traffic!

Update:

Made some changes to the script to check for already existing bans, to keep your chains clean!

Update 2:

Little tweak to the script so it now loads an array of lists, in case you have various sources.