[enh] Add RBL check

This commit is contained in:
ljf 2020-04-07 01:53:05 +02:00
parent fc50478382
commit d8feb1b72a
2 changed files with 91 additions and 1 deletions

View file

@ -1,9 +1,42 @@
#!/usr/bin/env python #!/usr/bin/env python
import os import os
import dns.resolver
from moulinette.utils.network import download_text
from yunohost.diagnosis import Diagnoser from yunohost.diagnosis import Diagnoser
DEFAULT_BLACKLIST = [
('zen.spamhaus.org' , 'Spamhaus SBL, XBL and PBL' ),
('dnsbl.sorbs.net' , 'SORBS aggregated' ),
('safe.dnsbl.sorbs.net' , "'safe' subset of SORBS aggregated"),
('ix.dnsbl.manitu.net' , 'Heise iX NiX Spam' ),
('babl.rbl.webiron.net' , 'Bad Abuse' ),
('cabl.rbl.webiron.net' , 'Chronicly Bad Abuse' ),
('truncate.gbudb.net' , 'Exclusively Spam/Malware' ),
('dnsbl-1.uceprotect.net' , 'Trapserver Cluster' ),
('cbl.abuseat.org' , 'Net of traps' ),
('dnsbl.cobion.com' , 'used in IBM products' ),
('psbl.surriel.com' , 'passive list, easy to unlist' ),
('dnsrbl.org' , 'Real-time black list' ),
('db.wpbl.info' , 'Weighted private' ),
('bl.spamcop.net' , 'Based on spamcop users' ),
('dyna.spamrats.com' , 'Dynamic IP addresses' ),
('spam.spamrats.com' , 'Manual submissions' ),
('auth.spamrats.com' , 'Suspicious authentications' ),
('dnsbl.inps.de' , 'automated and reported' ),
('bl.blocklist.de' , 'fail2ban reports etc.' ),
('srnblack.surgate.net' , 'feeders' ),
('all.s5h.net' , 'traps' ),
('rbl.realtimeblacklist.com' , 'lists ip ranges' ),
('b.barracudacentral.org' , 'traps' ),
('hostkarma.junkemailfilter.com', 'Autotected Virus Senders' ),
('rbl.megarbl.net' , 'Curated Spamtraps' ),
('ubl.unsubscore.com' , 'Collected Opt-Out Addresses' ),
('0spam.fusionzero.com' , 'Spam Trap' ),
]
class MailDiagnoser(Diagnoser): class MailDiagnoser(Diagnoser):
@ -14,6 +47,7 @@ class MailDiagnoser(Diagnoser):
def run(self): def run(self):
# Is outgoing port 25 filtered somehow ? # Is outgoing port 25 filtered somehow ?
self.logger_debug("Running outgoing 25 port check")
if os.system('/bin/nc -z -w2 yunohost.org 25') == 0: if os.system('/bin/nc -z -w2 yunohost.org 25') == 0:
yield dict(meta={"test": "ougoing_port_25"}, yield dict(meta={"test": "ougoing_port_25"},
status="SUCCESS", status="SUCCESS",
@ -23,9 +57,22 @@ class MailDiagnoser(Diagnoser):
status="ERROR", status="ERROR",
summary="diagnosis_mail_ougoing_port_25_blocked") summary="diagnosis_mail_ougoing_port_25_blocked")
# Is Reverse DNS well configured ?
# Mail blacklist using dig requests (c.f. ljf's code) # Are IPs blacklisted ?
self.logger_debug("Running RBL detection")
blacklisted_details = tuple(self.check_blacklisted(self.get_public_ip(4)))
blacklisted_details += tuple(self.check_blacklisted(self.get_public_ip(6)))
if blacklisted_details:
yield dict(meta={},
status="ERROR",
summary=("diagnosis_mail_blacklist_nok", {}),
details=blacklisted_details)
else:
yield dict(meta={},
status="SUCCESS",
summary=("diagnosis_mail_blacklist_ok", {}))
# SMTP reachability (c.f. check-smtp to be implemented on yunohost's remote diagnoser) # SMTP reachability (c.f. check-smtp to be implemented on yunohost's remote diagnoser)
@ -37,6 +84,46 @@ class MailDiagnoser(Diagnoser):
# check for unusual failed sending attempt being refused in the logs ? # check for unusual failed sending attempt being refused in the logs ?
def check_blacklisted(self, ip):
""" Check with dig onto blacklist DNS server
"""
if ip is None:
return
for blacklist, description in DEFAULT_BLACKLIST:
# Determine if we are listed on this RBL
try:
rev = dns.reversename.from_address(ip)
query = str(rev.split(3)[0]) + '.' + blacklist
# TODO add timeout lifetime
dns.resolver.query(query, "A")
except (dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.resolver.NoAnswer,
dns.exception.Timeout):
continue
# Try to get the reason
reason = "not explained"
try:
reason = str(dns.resolver.query(query, "TXT")[0])
except Exception:
pass
yield ('diagnosis_mail_blacklisted_by',
(ip, blacklist, reason))
def get_public_ip(self, protocol=4):
# TODO we might call this function from another side
assert protocol in [4, 6], "Invalid protocol version, it should be either 4 or 6 and was '%s'" % repr(protocol)
url = 'https://ip%s.yunohost.org' % ('6' if protocol == 6 else '')
try:
return download_text(url, timeout=30).strip()
except Exception as e:
self.logger_debug("Could not get public IPv%s : %s" % (str(protocol), str(e)))
return None
def main(args, env, loggers): def main(args, env, loggers):
return MailDiagnoser(args, env, loggers).diagnose() return MailDiagnoser(args, env, loggers).diagnose()

View file

@ -186,6 +186,9 @@
"diagnosis_swap_ok": "The system has {total} of swap!", "diagnosis_swap_ok": "The system has {total} of swap!",
"diagnosis_mail_ougoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.", "diagnosis_mail_ougoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.",
"diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.",
"diagnosis_mail_blacklist_ok": "Your server public IP are not listed on email blacklist.",
"diagnosis_mail_blacklist_nok": "Your server public IPs are listed on email blacklist.",
"diagnosis_mail_blacklisted_by": "{0} is listed on {1}. Reason: {2}",
"diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!", "diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!",
"diagnosis_regenconf_manually_modified": "Configuration file <code>{file}</code> appears to have been manually modified.", "diagnosis_regenconf_manually_modified": "Configuration file <code>{file}</code> appears to have been manually modified.",
"diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! YunoHost will stop updating this file automatically... But beware that YunoHost upgrades could contain important recommended changes. If you want to, you can inspect the differences with <cmd>yunohost tools regen-conf {category} --dry-run --with-diff</cmd> and force the reset to the recommended configuration with <cmd>yunohost tools regen-conf {category} --force</cmd>", "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! YunoHost will stop updating this file automatically... But beware that YunoHost upgrades could contain important recommended changes. If you want to, you can inspect the differences with <cmd>yunohost tools regen-conf {category} --dry-run --with-diff</cmd> and force the reset to the recommended configuration with <cmd>yunohost tools regen-conf {category} --force</cmd>",