Force TCP socket disconnect on imap login failure?
Paul Kudla (SCOM.CA Internet Services Inc.)
paul at scom.ca
Tue May 24 08:54:07 UTC 2022
for what its worth this is a python script that i use for the database
driven iptables updater for my asterisk server
again same ideas but it gets the job done.
It's a lot of work to get stuff like this going but may help point
someone in the right directions balance wise pending on there system /
network setup.
The django script is intelligent as it looks at the ip addresses already
blacklisted and updates the list adding or subtracting ip address
changes within the database
can answer in more detail, mainly for reference.
example iptables output :
# /sbin/iptables -L INPUT -n | more
Chain INPUT (policy ACCEPT)
target prot opt source destination
ACCEPT all -- 92.204.135.144 0.0.0.0/0
ACCEPT all -- 104.205.0.0/16 0.0.0.0/0
ACCEPT all -- 174.95.0.0/16 0.0.0.0/0
ACCEPT all -- 174.94.0.0/16 0.0.0.0/0
ACCEPT all -- 174.93.0.0/16 0.0.0.0/0
ACCEPT all -- 174.92.0.0/16 0.0.0.0/0
ACCEPT all -- 174.91.0.0/16 0.0.0.0/0
ACCEPT all -- 174.90.0.0/16 0.0.0.0/0
ACCEPT all -- 174.89.0.0/16 0.0.0.0/0
ACCEPT all -- 174.88.0.0/16 0.0.0.0/0
ACCEPT all -- 209.171.88.0/24 0.0.0.0/0
ACCEPT all -- 72.12.174.230 0.0.0.0/0
ACCEPT all -- 72.136.0.0/16 0.0.0.0/0
ACCEPT all -- 10.0.0.0/8 0.0.0.0/0
ACCEPT all -- 67.171.153.140 0.0.0.0/0
ACCEPT all -- 99.235.148.110 0.0.0.0/0
ACCEPT all -- 67.69.69.0/24 0.0.0.0/0
ACCEPT all -- 204.237.0.0/16 0.0.0.0/0
ACCEPT all -- 65.39.148.0/25 0.0.0.0/0
ACCEPT all -- 72.143.119.178 0.0.0.0/0
ACCEPT all -- 99.244.67.244 0.0.0.0/0
ACCEPT all -- 69.60.225.80 0.0.0.0/0
ACCEPT all -- 198.200.68.0/24 0.0.0.0/0
ACCEPT all -- 185.58.85.0/24 0.0.0.0/0
ACCEPT all -- 172.97.0.0/16 0.0.0.0/0
ACCEPT all -- 184.151.0.0/16 0.0.0.0/0
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:5038
DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
DROP all -- 213.175.208.0/24 0.0.0.0/0
DROP all -- 50.24.0.0/24 0.0.0.0/0
DROP all -- 20.98.78.0/24 0.0.0.0/0
DROP all -- 116.106.197.0/24 0.0.0.0/0
DROP all -- 45.95.169.0/24 0.0.0.0/0
DROP all -- 193.253.211.0/24 0.0.0.0/0
DROP all -- 65.49.20.0/24 0.0.0.0/0
DROP all -- 107.189.1.0/24 0.0.0.0/0
DROP all -- 107.189.3.0/24 0.0.0.0/0
DROP all -- 209.141.51.0/24 0.0.0.0/0
DROP all -- 75.119.155.0/24 0.0.0.0/0
DROP all -- 45.133.1.0/24 0.0.0.0/0
DROP all -- 185.166.84.0/24 0.0.0.0/0
DROP all -- 116.105.218.0/24 0.0.0.0/0
DROP all -- 216.37.36.0/24 0.0.0.0/0
DROP all -- 216.245.220.0/24 0.0.0.0/0
DROP all -- 205.185.121.0/24 0.0.0.0/0
based on django model(s)
#IP Blacklistings
class IpBlock(models.Model):
id = models.AutoField(primary_key=True)
ipaddress = models.CharField(verbose_name='IP Address',
max_length=40, null=True, blank=False,unique=False)
action = models.CharField(max_length=15,
choices=ip_action_choices, verbose_name='Firewall', default = 'D',
null=True, blank=True)
syslog = models.TextField(verbose_name='Last Syslog',
max_length=1000, null=True, blank=True, default = '')
whois = models.TextField(verbose_name='Whois', max_length=1500,
null=True, blank=True, default = '')
asterisk = models.BooleanField('Asterisk', default = False )
last_datetime = models.DateTimeField(verbose_name='Date Last Updated
Server', null=True, blank=True, default = timezone.now)
accountid = models.ForeignKey(Contacts,verbose_name='Reference',
default = '2594',null=False, blank=True,related_name = 'blacklist_soldto')
syslog2 = models.TextField(verbose_name='Last Syslog',
max_length=1000, null=True, blank=True, default = 'Denied due to
Unauthorized Use')
last_program = models.CharField(verbose_name='Last Program',
max_length=20, null=True, blank=True, default = '')
class Meta:
ordering = ['ipaddress',]
db_table = u'blocked_ip'
verbose_name = u"Currently Blocked IP's"
verbose_name_plural = u"Currently Blocked Ip's"
class IpCount(models.Model):
ipaddress = models.GenericIPAddressField(verbose_name='IP Address',
max_length=17,blank=False,primary_key=True, unique=True)
counthour = models.IntegerField(verbose_name='Current IP Count
This Hour', null=True, blank=True, default='0')
counttotal = models.IntegerField(verbose_name='Total IP Count This
Month', null=True, blank=True, default='0')
asterisk_counthour = models.IntegerField(verbose_name='Asterisk IP
Count This Hour', null=True, blank=True, default='0')
asterisk_counttotal = models.IntegerField(verbose_name='Asterisk IP
Count This Month', null=True, blank=True, default='0')
syslog = models.TextField(verbose_name='Syslog (What Hacked Me
Last)', max_length=1000, null=True, blank=True, default = '')
whois = models.TextField(verbose_name='Whois', max_length=1500,
null=True, blank=True, default = '')
last_datetime = models.DateTimeField(verbose_name='Date Last Updated
Server', null=True, blank=True, default = timezone.now)
last_program = models.CharField(verbose_name='Last Program',
max_length=20, null=True, blank=True, default = '')
action = models.BooleanField('Marked As Bad', default = False )
class Meta:
ordering = ['ipaddress',]
db_table = u'ip_count'
verbose_name = u"Current IP Count"
verbose_name_plural = u"Current IP Counts"
---------------------------------------------------------------------------
#!/usr/bin/env python2
#update.cidr
#Modified for iptables
#iptables -A FORWARD -s 8.8.8.8 -j DROP
#iptables -I INPUT -s 30.30.0.0/255.255.0.0 -j DROP
import sys
import os
import string
import psycopg2
import commands
def DROPIP(ipaddress) :
command = '/sbin/iptables -A INPUT -s %s -j DROP' %str(ipaddress)
yyerror = commands.getoutput(command)
command = '/sbin/iptables -A OUTPUT -s %s -j DROP' %str(ipaddress)
yyerror = commands.getoutput(command)
command = '/sbin/iptables -A FORWARD -s %s -j DROP' %str(ipaddress)
yyerror = commands.getoutput(command)
print yyerror
def ACCEPTIP(ipaddress) :
command = '/sbin/iptables -I INPUT -s %s -j ACCEPT' %str(ipaddress)
yyerror = commands.getoutput(command)
command = '/sbin/iptables -I OUTPUT -s %s -j ACCEPT'
%str(ipaddress)
yyerror = commands.getoutput(command)
command = '/sbin/iptables -I FORWARD -s %s -j ACCEPT'
%str(ipaddress)
yyerror = commands.getoutput(command)
def DELETEIP(ipaddress) :
#Drop the drops
command = '/sbin/iptables -D INPUT -s %s -j DROP' %str(ipaddress)
yyerror = commands.getoutput(command)
command = '/sbin/iptables -D OUTPUT -s %s -j DROP' %str(ipaddress)
yyerror = commands.getoutput(command)
command = '/sbin/iptables -D FORWARD -s %s -j DROP' %str(ipaddress)
yyerror = commands.getoutput(command)
command = '/sbin/iptables -D INPUT -s %s -j ACCEPT' %str(ipaddress)
yyerror = commands.getoutput(command)
command = '/sbin/iptables -D OUTPUT -s %s -j ACCEPT'
%str(ipaddress)
yyerror = commands.getoutput(command)
command = '/sbin/iptables -D FORWARD -s %s -j ACCEPT'
%str(ipaddress)
yyerror = commands.getoutput(command)
#ipaddress = '179.126.80.0/24'
#DELETEIP(ipaddress)
#sys.exit()
#Am I already running
command = '/bin/ps -axww | grep python'
yyerror = commands.getoutput(command)
#print yyerror
count = 0
yyerror = yyerror.split('\n')
#print yyerror
for nn in range (0,len(yyerror)) :
yy = yyerror[nn]
if 'iptables.update' in yy :
count = count + 1
#print
#print count
if count >= 2 :
print 'Already Updating ..... '
print 'Exiting ......'
sys.exit()
print 'Connecting to DB ...'
conn = psycopg2.connect(host='10.220.0.2', port = 5433,
database='scom_billing', user='', password='')
pg = conn.cursor()
print 'Connected to DB'
print 'Getting Current IP List ....'
command = ("""select action,ipaddress from blocked_ip where asterisk =
true or action = 'A' order by action """ ) #Go get any unassigned orders
pg.execute(command)
firewalldata = pg.fetchall()
#iptables --line-numbers -n --list
#Go get the existing firewall list
command = '/sbin/iptables -L INPUT -n'
print command
data = commands.getoutput(command)
data = data.split('\n')
currentlist = []
print len(data)
#Update list for DROP or ACCEPT and ip block
for nn in range (0,len(data)) :
y = str(data[nn])
#print
#print y
#print
if 'Chain INPUT (policy ACCEPT)' in y or 'target prot opt
source destination' in y :
print 'Skipping ...'
print
else : #Process the line
#print 'Processing this Line'
try :
if 'DROP' in y :
status = 'D'
else :
status = 'A'
ip = y.split('-- ')
#print ip
ip = ip[1]
#print ip
ip = ip.split(' ')
#print ip
ip = ip[0]
#print ip
#print 'appending to list'
currentlist.append(status)
currentlist.append(ip)
except :
print 'Bad Data Skipping ...'
print
print
print'Full list Currently In Firewall ...'
#print currentlist
#sys.exit()
print 'Got the list ... Working'
print
print
blacklist = [] #This is the converted list to iptable compatable formats
for x in range (0,len(firewalldata)) : #data = ipdata from db
#Internal Sample - ['A', '10.220.0.0/16']
#DB Sample - ('A', '67.55.27.171')
y = firewalldata[x]
#print 'firewall data %s' %str(y)
#print
#print
#sys.exit()
ipaddress = str(y[1])
#print 'DB Ip Address %s' %str(ipaddress)
if ipaddress <> 'ALL' :
done = 0
#print 'IP In : %s' %str(ipaddress)
#Modify ipaddress for cidr mapping
if ipaddress.count('.') == 1 : #10.
ipaddress = ipaddress + '0.0.0/8'
done = 1
if ipaddress.count('.') == 2 and done == 0 : #10.0.
ipaddress = ipaddress + '0.0/16'
done = 1
if ipaddress.count('.') == 3 and
ipaddress[len(ipaddress)-1] == '.' and done == 0 : #10.0.0.
ipaddress = ipaddress + '0/24'
#print 'IP Out: %s' %str(ipaddress)
#Now process the tables ie update/delete/change the entries
blacklist.append(str(y[0])) #set the status
blacklist.append(str(ipaddress) ) #Set the ip block to
manage
#print 'Current List In Scom Blacklistings'
#print badlist
print 'Processing .... My IP Black List Entries'
for n in range (0,len(blacklist),2) : #0 - action,1 - ip block
blacklistaction = str(blacklist[n])
blacklistip = str(blacklist[n+1])
#Now go check the iptable list to see if i have an entry
#print 'Processing Entry %s for IP %s with Action %s'
%(str(n),blacklistip,blacklistaction)
#print len(currentlist)
try :
nn = currentlist.index(blacklistip)
nn = nn-1
#Is this current black list ip currently in the iptables?
iptablesaction = str(currentlist[nn])
iptablesip = str( currentlist[nn+1] )
#Do i have a matching ip block?
if blacklistip == iptablesip : #We found a matching bl
entry already in iptables.
if blacklistaction == iptablesaction : #Rule is
good as is skip
#print 'Found A Current Rule that
matches, skipping ... %s' %str(blacklistip)
del currentlist[nn+1]
del currentlist[nn]
elif ipblacklistaction <> iptablesaction : #We
have a matching block but have to update the list
DELETEIP(str(iptablesip)) #Drop the
existing ip from the tables (precautionary)
if blacklistaction == 'A' :
#print 'Adding to Accept
IPTABLES List'
ACCEPTIP(str(ipblacklistip))
elif blacklistaction == 'D' :
#print 'Adding to Drop IPTABLES
List'
DROPIP(str(ipblacklistip))
print 'Updated Mismatch IPTABLES for %s
...' %str(ipblacklistip)
del currentlist[nn+1]
del currentlist[nn]
except :
#e = sys.exc_info()[0]
#print e
#We did not find anything in the tables, add new entry
print 'Pricessing Entry : %s ' %str(n)
if blacklistaction == 'A' :
print 'Adding to Accept IPTABLES List %s'
%str(blacklistip)
ACCEPTIP(blacklistip)
elif blacklistaction == 'D' :
print 'Adding to Drop IPTABLES List %s'
%str(blacklistip)
DROPIP(blacklistip)
#print 'Updated IPTABLES with new entry %s with Action
: %s' %(blacklistip,blacklistaction)
#Ok the blacklist is god again, see if there are any left over iptables
rules that we need to delete
print len(currentlist)
if len(currentlist) <> 0 :
print 'Cleaning up %s extra iptables ....' %str(len(currentlist))
for nn in range (0,len(currentlist),2) :
iptablesip = str( currentlist[nn+1] )
print 'Deleting %s from iptables' %str(iptablesip)
DELETEIP(str(iptablesip))
sys.exit()
------------------------------------------------------------------------------
Happy Tuesday !!!
Thanks - paul
Paul Kudla
Scom.ca Internet Services <http://www.scom.ca>
004-1009 Byron Street South
Whitby, Ontario - Canada
L1N 4S3
Toronto 416.642.7266
Main 1.866.411.7266
Fax 1.888.892.7266
Email paul at scom.ca
On 5/24/2022 3:36 AM, Jan Hugo Prins wrote:
> Just a few comments.
>
> - The below commands drops ALL future connections to the IMAP ports and
> not just the one from that specific IP address.
> - It all depends on the ordering of the rest of your iptables rules. A
> lot of iptables setups have an accept related / established in the top
> of the INPUT chain and then indeed the traffic will continue as long as
> the connection is established. If you put a correct drop rule in the top
> of your iptables INPUT chain it will block all traffic including any
> related/established.
>
> Fail2Ban is able to insert such a drop rule in the top of the INPUT
> chain and thereby block all further tries.
> This is exactly how I have setup my fail2ban and it works.
>
> The first few lines of my iptables input chain look like this:
>
> 29M 2249M f2b-dovecot tcp -- * * 0.0.0.0/0
> 0.0.0.0/0 multiport dports 110,143,993,995
> 9969K 2545M f2b-sasl tcp -- * * 0.0.0.0/0
> 0.0.0.0/0 multiport dports 25,465
> 9691K 2788M ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0
> 134M 257G ACCEPT all -- * * 0.0.0.0/0
> 0.0.0.0/0 state RELATED,ESTABLISHED
>
> Jan Hugo Prins
>
>
> On 5/23/22 23:16, Hippo Man wrote:
>> OOPS! I incorrectly copied and pasted the iptables command in my
>> previous message. Here is the correct iptables command:
>>
>> iptables -I INPUT -p tcp -m multiport --destination-port 143,993 -d
>> aaa.bbb.ccc.ddd -j DROP
>>
>> This command successfully blocks *future* connections to ports 143 and
>> 993 from that IP address, but as I mentioned, it doesn't kill the
>> currently open connection.
>>
>> --
>> hippoman at gmail.com
>> Take a hippopotamus to lunch today.
>>
>>
>> On Mon, May 23, 2022 at 4:54 PM Hippo Man <hippoman at gmail.com> wrote:
>>
>> Thank you, but fail2ban doesn't do what I need. Here is why ...
>>
>> I have used fail2ban and also my own homegrown log monitor program
>> for this purpose. In both cases, I can detect the failed imap
>> logins and then cause the following command to be run ...
>>
>> iptables -I INPUT -p tcp --destination-port aaa.bbb.ccc.ddd -j DROP
>>
>> However, this does not drop connections that are existing and
>> already open. It will only drop *future* connections from that IP
>> address to port 143.
>>
>> This is why I want to kill the existing connection. Even after
>> that "iptables" command is issued, the entity which is connected
>> to the imap port can continue to send more and more imap commands.
>>
>> If I can drop the TCP connection as soon as an imap login fails
>> and also issue that kind of "iptables" command, then the client
>> would have to reconnect in order to retry other login attempts.
>> Those future connections would then be successfully blocked by
>> that iptables rule.
>>
>> And even if I issue a "tcpdrop" command instead of just the
>> "iptables" command, it doesn't kill the already-open connection.
>> It just force-blocks future connections.
>>
>> I'm thinking of patching the dovecot source code to create a
>> personal version which immediately disconnects from the socket
>> after login failure. Of course, I would prefer not to do that, if
>> there is another way to accomplish this.
>>
>> --
>> hippoman at gmail.com
>> Take a hippopotamus to lunch today.
>>
>>
>> On Mon, May 23, 2022 at 4:24 PM Jan Hugo Prins <jhp at jhprins.org>
>> wrote:
>>
>> Look at fail2ban.
>> Should be able to do that for you.
>>
>> Jan Hugo
>>
>>
>> On 5/23/22 21:11, Lloyd Zusman wrote:
>>> I'm running dovecot 2.2.13 under Debian 8.
>>>
>>> I'd like to force an immediate TCP socket disconnect after
>>> any imap login attempt that fails.
>>>
>>> Right now, if invalid credentials are supplied during an imap
>>> login, the client can keep retrying logins with different
>>> credentials. However, I want to prevent that from occurring
>>> by causing the socket connection to be closed as soon as
>>> there is any failed login attempt.
>>>
>>> I haven't been able to find any |dovecot| configuration
>>> setting which could control this behavior, but I'm hoping
>>> that I just missed something.
>>>
>>> Thank you very much for any suggestions.
>>>
>>> --
>>> hippoman at gmail.com
>>> Take a hippopotamus to lunch today.
>>
>
>
> --
> This message has been scanned for viruses and
> dangerous content by *MailScanner* <http://www.mailscanner.info/>, and is
> believed to be clean.
More information about the dovecot
mailing list