Last active
August 29, 2015 14:07
-
-
Save grepwood/b65cd1a9869a161d6c23 to your computer and use it in GitHub Desktop.
Automatically unban IPs and force IP unbanning for APF
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <unistd.h> | |
/* If you're using Windows, OSX, or OpenVZ, it's recommended you get grepline | |
* You can find it here https://github.com/grepwood/grepline */ | |
/* Compile with GNU getline: | |
* cc count_ips.c -Wall -Wextra -pedantic -O2 -mtune=generic -DGNU_GETLINE -o count_ips | |
* | |
* Compile with grepline: | |
* cc grepline.c count_ips.c -Wall -Wextra -pedantic -O2 -mtune=generic -o count_ips */ | |
#ifndef GNU_GETLINE | |
# include "grepline.h" | |
#else | |
# define grepline getline | |
#endif /* GNU_GETLINE */ | |
/* If non-zero, file with given path exists */ | |
int file_exist(const char *filename) { | |
struct stat buffer; | |
return (!stat(filename, &buffer)); | |
} | |
/* This needs to be signed because "char" on its own | |
* defaults to "unsigned char" on PowerPC. On x86 it | |
* defaults to "signed char". This difference may cause | |
* trivial compile warnings. */ | |
signed char IsInterProtFourAddr(char * arg) { | |
signed char result = 1; | |
size_t len = strlen(arg); | |
unsigned char counter; | |
/* GCC 4.4 and older will moan and complain if | |
* you declare these counters as smaller types than | |
* the types for which they serve and indices */ | |
unsigned long dots; | |
unsigned long displacement[3]; | |
unsigned long octet[4]; | |
/* We have to account for a newline character */ | |
if(len < 8 || len > 16) { | |
result = -1; | |
return result; | |
} | |
/* If the line is a comment */ | |
if(arg[0] == '#') { | |
result = -2; | |
return result; | |
} | |
/* Valid IPv4 has 3 dots */ | |
for(counter = 0, dots = 0; counter < len; ++counter) { | |
if(arg[counter] == '.') { | |
displacement[dots] = counter+1; | |
++dots; | |
} | |
} | |
if(dots != 3) { | |
result = -4; | |
return result; | |
} | |
octet[0] = strtoul(arg,NULL,10); | |
octet[1] = strtoul(arg+displacement[0],NULL,10); | |
octet[2] = strtoul(arg+displacement[1],NULL,10); | |
octet[3] = strtoul(arg+displacement[2],NULL,10); | |
/* We all watched that episode of CSI with | |
* IPv4 octets way over 300, right? */ | |
for(counter = 0; counter < 4; ++counter) { | |
if(octet[counter] > 255) { | |
result = -8; | |
return result; | |
} | |
} | |
/* Let's make the IP alright to puts() */ | |
arg[len-1] = 0; | |
return result; | |
} | |
void usage(int exitcode) { | |
puts("Copyright 2014 Michael Dec <[email protected]>"); | |
puts("This program reads a text file line by line,"); | |
puts("counting IPv4 addresses inside of it."); | |
puts("It is especially geared towards dealing with"); | |
puts("/etc/apf/deny_hosts.rules\n"); | |
puts("-h this message"); | |
puts("-f file path"); | |
exit(exitcode); | |
} | |
int main(int argc, char * argv[]) { | |
int result = 0; | |
FILE * denyhosts_file = NULL; | |
char * line = NULL; | |
char * denyhosts_path = NULL; | |
unsigned long IP_Count = 0; | |
size_t wom; | |
char satisfied = 0; | |
int c; | |
while((c = getopt(argc, argv, "hf:")) != -1) { | |
switch(c) { | |
case 'h': | |
usage(-1); | |
break; | |
case 'f': | |
denyhosts_path = optarg; | |
satisfied += 1; | |
break; | |
default: | |
fprintf(stderr, "Unrecognised argument -%c\n", c); | |
usage(-2); | |
} | |
} | |
if (satisfied != 1) { | |
fputs("Insufficient parameters\n",stderr); | |
result = -4; | |
return result; | |
} | |
if(!file_exist(denyhosts_path)) { | |
result = -8; | |
fprintf(stderr, "%s does not exist\n", denyhosts_path); | |
return result; | |
} | |
denyhosts_file = fopen(denyhosts_path,"r"); | |
while(!feof(denyhosts_file)) { | |
grepline(&line,&wom,denyhosts_file); | |
if(IsInterProtFourAddr(line) == 1) { | |
++IP_Count; | |
puts(line); | |
} | |
} | |
fclose(denyhosts_file); | |
free(line); | |
printf("IPv4 addresses: %lu\n",IP_Count); | |
return result; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
APF_UNBANS="/root/firewall_unbans" | |
IPS_TO_FORGIVE=`ls -C1 $APF_UNBANS | wc -l` | |
COUNT_IP_EXE="/usr/local/bin/count_ips" | |
APF_BAN_LIST="/etc/apf/deny_hosts.rules" | |
COUNT_IP_EXE_URL="" | |
UNBAN_SCHEDULER="schedule_unbans.sh" | |
echo "Before you deploy this, you must either compile count_ips.c," | |
echo "move it to $COUNT_IP_EXE," | |
echo "or define COUNT_IP_EXE_URL with a valid URL to the executable." | |
echo "You also have to redefine UNBAN_SCHEDULER if you renamed the" | |
echo "script that schedules unbans." | |
echo "After you've done that, feel free to wipe these echos and the following exit" | |
exit | |
if [ ! -f "$COUNT_IP_EXE" ]; then | |
printf "Downloading IP parser... " | |
curl $COUNT_IP_EXE_URL > $COUNT_IP_EXE 2>/dev/null | |
echo "done!" | |
fi | |
if [ ! -x "$COUNT_IP_EXE" ]; then | |
printf "Granting exe rights to IP parser... " | |
chmod +x $COUNT_IP_EXE | |
echo "done!" | |
fi | |
printf "Unbanning all IPs from $APF_UNBANS... " | |
for((COUNTER=1; COUNTER <= $IPS_TO_FORGIVE; COUNTER++)); do | |
CURRENT_IP=`ls -C1 $APF_UNBANS | head -n$COUNTER | tail -n1` | |
apf -u $CURRENT_IP 2>/dev/null 1>/dev/null | |
rm $APF_UNBANS/$CURRENT_IP | |
done | |
echo "done!" | |
echo "Unbanned IPs: $IPS_TO_FORGIVE" | |
COUNTER=`ps aux | grep $UNBAN_SCHEDULER | grep -v grep | wc -l` | |
if [ "$COUNTER" -gt "0" ]; then | |
printf "Killing all sleeping IP unbans... " | |
kill -9 `ps aux | grep $UNBAN_SCHEDULER | grep -v grep | awk '{print $2}'` | |
echo "done!" | |
fi | |
echo "Ban status:" | |
$COUNT_IP_EXE -f $APF_BAN_LIST |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
COUNT_IP_EXE="/usr/local/bin/count_ips" | |
JOB_PID=$$ | |
FIREWALL_UNBANS="/root/firewall_unbans" | |
APF_BAN_LIST="/etc/apf/deny_hosts.rules" | |
COUNT_IP_EXE_URL="" | |
DELAY="7 days" | |
echo "Before you deploy this, you must either compile count_ips.c," | |
echo "move it to $COUNT_IP_EXE," | |
echo "or define COUNT_IP_EXE_URL with a valid URL to the executable." | |
echo "You may also want to redefine DELAY for your own purposes." | |
echo "After you've done that, feel free to wipe these echos and the following exit" | |
exit | |
if [ ! -f "$COUNT_IP_EXE" ]; then | |
printf "Downloading IP parser... " | |
curl $COUNT_IP_EXE_URL > $COUNT_IP_EXE 2>/dev/null | |
echo "done!" | |
fi | |
if [ ! -x "$COUNT_IP_EXE" ]; then | |
printf "Granting exe rights to IP parser... " | |
chmod +x $COUNT_IP_EXE | |
echo "done!" | |
fi | |
if [ ! -d "$FIREWALL_UNBANS" ]; then | |
printf "Creating pending unban directory... " | |
mkdir -p $FIREWALL_UNBANS | |
echo "done!" | |
fi | |
printf "Copying and counting IPs... " | |
$COUNT_IP_EXE -f $APF_BAN_LIST 2>cip.$JOB_PID 1>cip.$JOB_PID | |
if grep -q ^"$APF_BAN_LIST does not exist"$ cip.$JOB_PID ; then | |
echo "oops" | |
echo "APF is not installed." | |
rm -f cip.$JOB_PID | |
exit | |
fi | |
echo "done!" | |
if [ "`tail -n1 cip.$JOB_PID`" == "IPv4 addresses: 0" ]; then | |
echo "The block list is empty." | |
rm -f cip.$JOB_PID | |
exit | |
fi | |
AMOUNT_OF_IPS=`tail -n1 cip.$JOB_PID | awk '{print $3}'` | |
for((COUNTER=1, TODO_COUNTER=0, ON_THEIR_WAY=0; COUNTER <= $AMOUNT_OF_IPS; COUNTER++)); do | |
CURRENT_IP=`head -n$COUNTER cip.$JOB_PID | tail -n1` | |
JOB_EXISTS=`ls $FIREWALL_UNBANS | grep $CURRENT_IP | wc -l` | |
if [ "$JOB_EXISTS" -eq "0" ]; then | |
TIME_OF_IP=`grep "{trust}\ deny\ all\ to\/from\ $CURRENT_IP" /var/log/apf_log | tail -n1 | awk '{print $1" "$2" "$3}'` | |
TIME_OF_IP=`date -d "$TIME_OF_IP"` | |
EXPIRATION_DATE=`date -d "$TIME_OF_IP+$DELAY" +%s` | |
TIME_NOW=`date +%s` | |
TIME_TO_SLEEP=`expr $EXPIRATION_DATE - $TIME_NOW` | |
if [ "$TIME_TO_SLEEP" -lt "0" ]; then | |
apf -u $CURRENT_IP 2>/dev/null | |
else | |
touch $FIREWALL_UNBANS/$CURRENT_IP | |
echo "Unblocking $CURRENT_IP in the background, in $TIME_TO_SLEEP seconds" | |
(sleep $TIME_TO_SLEEP; apf -u $CURRENT_IP 2>/dev/null && rm -f $FIREWALL_UNBANS/$CURRENT_IP) > /dev/null & | |
fi | |
TODO_COUNTER=`expr $TODO_COUNTER + 1` | |
else | |
ON_THEIR_WAY=`expr $ON_THEIR_WAY + 1` | |
fi | |
done | |
rm -f cip.$JOB_PID | |
# Note that sometimes you will sometimes have a job for | |
# an IP that's not deleted yet. | |
echo "IPs processed: $AMOUNT_OF_IPS" | |
echo "New jobs dispatched: $TODO_COUNTER" | |
echo "IPs that already were on their way out: $ON_THEIR_WAY" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment