Remove all DROP iptables rules from bash script - linux

I want to delete all iptables DROP rules from a bash script.
This is my script:
#!/bin/bash
# Remove all DROPs.
######################################################################
#_____________________________________________________________________
iptables="/sbin/iptables"
######################################################################
#_____________________________________________________________________
echo "[*] Removing all DROPs ..."
IFS_OLD=$IFS
IFS=$'\n'
rule_list=$(${iptables} -S | grep 'DROP$')
for drop_rule in ${rule_list}; do
undrop_rule=$(printf -- "${drop_rule}\n" | sed 's#^-A#-D#')
printf "[-] ${iptables} ${undrop_rule}\n"
${iptables} -v ${undrop_rule}
[ $? == 1 ] && echo "[E] Unable to delete DROP rule." && exit 1
done
IFS=$IFS_OLD
######################################################################
#_____________________________________________________________________
printf '\n\n'
${iptables} -S
######################################################################
#_____________________________________________________________________
But the output is:
[*] Removing all DROPs ...
[-] /sbin/iptables -D INPUT -s 209.126.1.2/32 -i eth0 -j DROP
all opt -- in * out * 0.0.0.0/0 -> 0.0.0.0/0
iptables: Bad rule (does a matching rule exist in that chain?).
[E] Unable to delete DROP rule.
Why ?
If I run manually the command:
/sbin/iptables -D INPUT -s 209.126.1.2/32 -i eth0 -j DROP
it work.
Thanks, BuDuS.

In your script, your IFS is still set to a newline by the time the iptables is executed. Hence bash can't word-split your command arguments.
I would set back IFS=$IFS_OLD as soon as the modified value is not needed anymore, which is probably after the assignement to rule_list.

Related

Bash Script Command Not Executing

I need help with the following Bash v4.1.2 script.
#!/bin/bash
IP=$1
IPTABLES=/sbin/iptables
$IPTABLES -I INPUT -s $IP -j DROP
echo $IPTABLES -I INPUT -s $IP -j DROP |wall
The variables, IP and IPTABLES, get populated in the echo but the line above is not executed. The echo outputs...
/sbin/iptables -I INPUT -s 1.2.3.4 -j DROP
...which is syntactically correct and works if executed manually.
I don't know Bash so I'm struggling to debug this elementary script. I see some scenarios where commands are left bare as I have mine and some that are wrapped in $() (with and without quotes). I've also tried using backticks and quoting various parts of the command. The echo piped through wall only exists for debugging.
I found a basically identical post at Bash script commands not working in cron. My script is not running from cron though.
=== EDIT ===
Added for #Barmar
[root#server tmp]# bash -x /bin/netfilter-drop.sh
+ IP=1.2.3.4
+ IPTABLES=/sbin/iptables
+ /sbin/iptables -I INPUT -s 1.2.3.4 -j DROP
+ wall
+ echo /sbin/iptables -I INPUT -s 1.2.3.4 -j DROP
[root#server tmp]#
Broadcast message from root#server (Thu Dec 29 12:46:44 2016):
/sbin/iptables -I INPUT -s 1.2.3.4 -j DROP
^C
[root#server tmp]#
I had initially only given sudo access to run the posted Bash script. The problem was not the script, rather it was permissions. I needed to give additional sudo access to run iptables in my sudoers. Fixed.

Iptables remove specific rules by comment

I need to delete some rules with same comment.
For example I have rules with comment = "test it", so i can get list of them like this:
sudo iptables -t nat -L | grep 'test it'
But how can i delete all PREROUTING rules with comment 'test it'?
UPD:
As #hek2mgl said, i can do something like this:
sudo bash -c "iptables-save > iptables.backup"
sed -i '/PREROUTING.*--comment.* "test it"/d' iptables.backup
sudo iptables-restore < iptables.backup
sudo rm iptables.backup
But between save and restore could be changes in iptables, so after restore there will be problems =/
You can use the following command:
iptables-save | sed -r '/PREROUTING.*comment.*test it/s/-A/iptables -D/e'
iptables-save will return iptables commands that can be executed to return the current state of the firewall after a reboot or whatever.
Meaning it will contain lines like:
...
-A PREROUTING -p tcp -m tcp --dport 25 -j ACCEPT -m comment --comment "test it"
...
The sed command searches for lines containing PREROUTING.*comment.*test it (should be good enough) and prepends the term iptablesplus replaces -A by -D since -D deletes a rule. The result of the replacement operation get's then executed using the e command. The e command is a GNU extension to sed.
Note: If you want to print the command in addition to simply executing it you can use s/-A/iptables -D/pe.
Yet another way to Remove by comment:
NOWRULES=$(iptables --line-number -nL INPUT | grep comment_here | awk '{print $1}' | tac)
for rul in $NOWRULES; do /sbin/iptables -D INPUT $rul; sleep 0.1; done
The best way to remove comment-based rules from iptables is:
iptables-save | grep -v COMMENT | iptables-restore
it cleans all rules with matching comment. As for me, I use this method to add ruleset that needs to be completely removed later.
If you neeed only PREROUTING chain to be touched, add some prefix or suffix to your comment like preroute_COMMENT upon rule creation to make difference inside COMMENT identified ruleset

Adding my current IP to whitelist on iptables?

I'm pretty new to setting up game server but I want to block rcon to every ip except the ones that are whitelisted.
First I'm gonna use this trhough SSH:
iptables -A INPUT -p tcp --destination-port 27015 -j LOG --log-prefix "SRCDS-RCON " -m limit --limit 1/m --limit-burst 1
iptables -A INPUT -p tcp --destination-port 27015 -j DROP
After that I want that when a user runs a bash script or something similar, it detects the user IP and add it to the whitelist automatically.
How can I do this?
Assuming :
the bash script is run on the server
the users logs in using ssh
You could create an ipset :
First, add this rule in iptables :
iptables -A INPUT -i eth0 -m set --match-set whitelist src -p tcp --destination-port 27015 -j ACCEPT
Then create a set :
sudo ipset -N whilelist iphash
Finally, add a script like this, using SSH_CONNECTION environment variable :
#!/bin/bash
USER_IP=$(echo $SSH_CONNECTION | cut -f1 -d' ')
sudo ipset -A whitelist $USER_IP
You could even add those two lines at the end of /root/.bash_profile so it gets done automagically when someone connects as root.
However, this assumes your friends are connecting as root via ssh. Since this is not desirable, you could use a temporary directory to hold the ip addresses, and add a cron job to fill the ipset hash :
Create /etc/cron.d/check_ipset with :
* * * * * root /usr/local/bin/check_ipset
Create /usr/local/bin/check_ipset (and chmod 700) :
#!/bin/bash
for i in `cat /tmp/ipset_pending | sort -u`; do
ipset -A whitelist $i
done
cat /dev/null > /tmp/ipset_pending
Add this to every user's .bash_profile :
...
echo $SSH_CONNECTION | cut -f1 -d' ' >> /tmp/ipset_pending
...
Didn't test, so YMMV, but this should be close enough.

Using variables to direct output to different files

I am trying to use variables to direct program output to different locations based on some user config settings.
if [ -f "vars/debug.var" ]; then
DUMP=''
else
DUMP='&> logs/dump.log'
fi
...
ping -I eth0 -c 10 www.google.com $DUMP
...
So, if the file debug.var exists, DUMP is an empty string, but if it does not exist I want to pipe the output to the dump.log file.
I have tried a lot of different combinations of the variable and command and nothing has worked out... I keep getting the error
ping: unknown host &>
Anybody have an idea? Or is it just not possible?
eval "ping -I eth0 -c 10 www.google.com $DUMP"
Instead of messing around with eval (which is a security hole)
if [ -f "vars/debug.var" ]; then
: # nothing
else
exec &> logs/dump.log
fi
ping -I eth0 -c 10 www.google.com
What exec does is allowing you to redirect output as needed, and you can do that in a sub-shell (...) as well to limit the scope of the re-direct.
Here is another safer variant that allows you to have arbitrary paths.
DU='&>'
MP='logs/ * /du mp.log'
eval "xargs ping -I eth0 -c 3 www.google.com $DU \"\$MP\""
Or you can use an array:
DUMP=('&>' 'logs/ * /du mp.log')
eval "xargs ping -I eth0 -c 3 www.google.com $DUMP \"\${DUMP[1]}\""
And you can quote it like this to save 1 character
eval "xargs ping -I eth0 -c 3 www.google.com $DUMP "'"${DUMP[1]}"'

bash - add additional IF statement

I have a script which parses a varnish varnishncsa log file. The purpose of the script is that if anyone accesses a certain url on the server, it adds their ip address to iptables to lock them out.
In my script I have a statement which ignores my static office ip address (so that I dont lock myself out of the server).
I am trying to add more ip addresses to exclude them from being locked out, but when I do, it seems to break the script.
#!/bin/bash
for address in `cat /var/log/brute.txt | grep -v -f /var/log/applied_brute.txt`; do
/bin/echo $address >> /var/log/applied_brute.txt
if [ "$address" != "my.of.fi.ce.ip" ]; then
IPTABLE=`echo $address | awk '{ print "/sbin/iptables -A INPUT -s "$0" -j DROP -m state --state NEW,ESTABLISHED,RELATED\n"}'`
fi
echo $IPTABLE
$IPTABLE
done
unset address
unset IPTABLE
What I would like is where the statement
if [ "$address" != "my.of.fi.ce.ip" ]; then
to add a few more ip addresses to it.
How about:
#!/bin/bash
for address in `grep -v -f /var/log/applied_brute.txt < /var/log/brute.txt`; do
echo $address >> /var/log/applied_brute.txt
if ! grep -q -F -x $address /etc/my-office-addresses.txt; then
IPTABLE="/sbin/iptables -A INPUT -s "$address" -j DROP -m state --state NEW,ESTABLISHED,RELATED"
echo $IPTABLE
$IPTABLE
fi
done
Store your office addresses in /etc/my-office-addresses.txt
grep options used:
-F : Fixed strings (treat pattern literally, not as regex. This option is not really required in this case, since the input data in all the files used is assumed to be in standard format.)
-x : line match (address = 192.168.0.1 would have matched line = 192.168.0.100 , if this option is missed.)
-q : Do not print result to stdout.
You could use grep or fgrep and have
if fgrep -q "$address" /etc/files-of-addresses-to-avoid ; then
# $address should be avoided
else
# $address should not be avoided
fi
(Perhaps you want /var/log/brute.txt instead of /etc/files-of-addresses-to-avoid etc...)
You may be interested by fail2ban which does what you want to achieve.
If you have a very limited amount of ip addresses, you can use a AND:
if [ "$address" != "my.of.fi.ce.ip" -a "$address" != "my.other.of.fi.ce.ip" -a "$address" != "my.la.st.ip" ]; then
how about creating an Array of local ip addresses, and then go through them in a inner "for loop".

Resources