mirror of
https://github.com/chillout2k/sos-milter.git
synced 2025-12-11 01:30:19 +00:00
connection reusing; testing docs
This commit is contained in:
parent
6656e7b81a
commit
7bfc3884d8
@ -8,8 +8,7 @@ import random
|
|||||||
import re
|
import re
|
||||||
import dns.resolver
|
import dns.resolver
|
||||||
from ldap3 import (
|
from ldap3 import (
|
||||||
Server, Connection, NONE, ALL, set_config_parameter, ALL_ATTRIBUTES,
|
Server, Connection, NONE, set_config_parameter
|
||||||
ALL_OPERATIONAL_ATTRIBUTES, MODIFY_REPLACE, HASHED_NONE, HASHED_SALTED_SHA,
|
|
||||||
)
|
)
|
||||||
from ldap3.core.exceptions import LDAPException
|
from ldap3.core.exceptions import LDAPException
|
||||||
|
|
||||||
@ -20,7 +19,6 @@ g_milter_reject_message = 'Security policy violation!'
|
|||||||
g_milter_tmpfail_message = 'Service temporarily not available! Please try again later.'
|
g_milter_tmpfail_message = 'Service temporarily not available! Please try again later.'
|
||||||
g_re_domain = re.compile(r'^.*@(\S+)$', re.IGNORECASE)
|
g_re_domain = re.compile(r'^.*@(\S+)$', re.IGNORECASE)
|
||||||
g_re_spf_regex = re.compile(r'.*', re.IGNORECASE)
|
g_re_spf_regex = re.compile(r'.*', re.IGNORECASE)
|
||||||
g_re_expected_txt_data = ''
|
|
||||||
g_loglevel = logging.INFO
|
g_loglevel = logging.INFO
|
||||||
g_milter_mode = 'test'
|
g_milter_mode = 'test'
|
||||||
g_ignored_next_hops = {}
|
g_ignored_next_hops = {}
|
||||||
@ -31,6 +29,10 @@ g_ldap_bindpw = ''
|
|||||||
class SOSMilter(Milter.Base):
|
class SOSMilter(Milter.Base):
|
||||||
# Each new connection is handled in an own thread
|
# Each new connection is handled in an own thread
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.client_ip = None
|
||||||
self.is_null_sender = False
|
self.is_null_sender = False
|
||||||
self.env_from = None
|
self.env_from = None
|
||||||
self.env_from_domain = None
|
self.env_from_domain = None
|
||||||
@ -42,6 +44,7 @@ class SOSMilter(Milter.Base):
|
|||||||
self.mconn_id = g_milter_name + ': ' + ''.join(
|
self.mconn_id = g_milter_name + ': ' + ''.join(
|
||||||
random.choice(string.ascii_lowercase + string.digits) for _ in range(8)
|
random.choice(string.ascii_lowercase + string.digits) for _ in range(8)
|
||||||
)
|
)
|
||||||
|
logging.debug(self.mconn_id + " RESET")
|
||||||
|
|
||||||
# Not registered/used callbacks
|
# Not registered/used callbacks
|
||||||
@Milter.nocallback
|
@Milter.nocallback
|
||||||
@ -64,6 +67,18 @@ class SOSMilter(Milter.Base):
|
|||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
def envfrom(self, mailfrom, *str):
|
def envfrom(self, mailfrom, *str):
|
||||||
|
# Instance member values remain within reused SMTP-connections!
|
||||||
|
if self.client_ip is not None:
|
||||||
|
# Milter connection reused!
|
||||||
|
logging.debug(self.mconn_id + "/FROM connection reused!")
|
||||||
|
self.reset()
|
||||||
|
self.client_ip = self.getsymval('{client_addr}')
|
||||||
|
if self.client_ip is None:
|
||||||
|
logging.error(self.mconn_id + " FROM exception: could not retrieve milter-macro ({client_addr})!")
|
||||||
|
self.setreply('450','4.7.1', g_milter_tmpfail_message)
|
||||||
|
return Milter.TEMPFAIL
|
||||||
|
else:
|
||||||
|
logging.debug(self.mconn_id + "/FROM client_ip={0}".format(self.client_ip))
|
||||||
try:
|
try:
|
||||||
# DSNs/bounces are not relevant
|
# DSNs/bounces are not relevant
|
||||||
if(mailfrom == '<>'):
|
if(mailfrom == '<>'):
|
||||||
@ -90,7 +105,7 @@ class SOSMilter(Milter.Base):
|
|||||||
try:
|
try:
|
||||||
g_ldap_conn.search(os.environ['LDAP_SEARCH_BASE'],
|
g_ldap_conn.search(os.environ['LDAP_SEARCH_BASE'],
|
||||||
filter,
|
filter,
|
||||||
attributes=[ALL_ATTRIBUTES]
|
attributes=[]
|
||||||
)
|
)
|
||||||
if len(g_ldap_conn.entries) != 0:
|
if len(g_ldap_conn.entries) != 0:
|
||||||
self.is_env_from_domain_in_ldap = True
|
self.is_env_from_domain_in_ldap = True
|
||||||
@ -105,9 +120,7 @@ class SOSMilter(Milter.Base):
|
|||||||
try:
|
try:
|
||||||
dns_response = dns.resolver.resolve(self.env_from_domain, 'TXT')
|
dns_response = dns.resolver.resolve(self.env_from_domain, 'TXT')
|
||||||
except dns.resolver.NoAnswer as e:
|
except dns.resolver.NoAnswer as e:
|
||||||
logging.warning(self.mconn_id +
|
logging.warning(self.mconn_id + " /FROM " + e.msg)
|
||||||
" /FROM " + e.msg
|
|
||||||
)
|
|
||||||
# accept message if DNS-resolver fails
|
# accept message if DNS-resolver fails
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
except dns.resolver.NXDOMAIN as e:
|
except dns.resolver.NXDOMAIN as e:
|
||||||
@ -185,7 +198,7 @@ class SOSMilter(Milter.Base):
|
|||||||
"Passing message due to ignored next-hop=" + self.next_hop
|
"Passing message due to ignored next-hop=" + self.next_hop
|
||||||
)
|
)
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
if self.is_env_from_domain_in_ldap:
|
if self.is_env_from_domain_in_ldap and g_milter_mode != 'reject':
|
||||||
logging.info(self.mconn_id + '/' + self.queue_id + "/EOM " +
|
logging.info(self.mconn_id + '/' + self.queue_id + "/EOM " +
|
||||||
"5321_from_domain={0} (LDAP) has a broken SPF-record!".format(self.env_from_domain)
|
"5321_from_domain={0} (LDAP) has a broken SPF-record!".format(self.env_from_domain)
|
||||||
)
|
)
|
||||||
@ -199,11 +212,11 @@ class SOSMilter(Milter.Base):
|
|||||||
"addheader() failed: " + traceback.format_exc()
|
"addheader() failed: " + traceback.format_exc()
|
||||||
)
|
)
|
||||||
ex = str(
|
ex = str(
|
||||||
" SPF-record (-all) of 5321_from_domain="
|
"SPF-record (-all) of 5321_from_domain="
|
||||||
+ self.env_from_domain + " does not permit us to relay this message!"
|
+ self.env_from_domain + " does not permit us to relay this message!"
|
||||||
)
|
)
|
||||||
logging.info(self.mconn_id + '/' + self.queue_id + "/EOM " +
|
logging.info(self.mconn_id + '/' + self.queue_id + "/EOM " +
|
||||||
"mode=" + g_milter_mode + ' ' + ex
|
"mode=" + g_milter_mode + ' client=' + self.client_ip + ' ' + ex
|
||||||
)
|
)
|
||||||
if g_milter_mode == 'reject':
|
if g_milter_mode == 'reject':
|
||||||
self.setreply('550','5.7.1',
|
self.setreply('550','5.7.1',
|
||||||
@ -218,11 +231,13 @@ class SOSMilter(Milter.Base):
|
|||||||
|
|
||||||
def abort(self):
|
def abort(self):
|
||||||
# Client disconnected prematurely
|
# Client disconnected prematurely
|
||||||
|
logging.debug(self.mconn_id + "/ABORT")
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
# Always called, even when abort is called.
|
# Always called, even when abort is called.
|
||||||
# Clean up any external resources here.
|
# Clean up any external resources here.
|
||||||
|
logging.debug(self.mconn_id + "/CLOSE")
|
||||||
return Milter.CONTINUE
|
return Milter.CONTINUE
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
23
tests/README.md
Normal file
23
tests/README.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# prepare testing env
|
||||||
|
```
|
||||||
|
export TLD=de
|
||||||
|
export SLD=domain
|
||||||
|
export MILTER_MODE=reject
|
||||||
|
export MILTER_SOCKET=inet:12345
|
||||||
|
export LOG_LEVEL=debug
|
||||||
|
export SPF_REGEX="^.*include:_spf\.blah\.blub.*$"
|
||||||
|
export LDAP_ENABLED=yepp
|
||||||
|
export LDAP_SERVER_URI="ldap://ldap-master-staging.int.${SLD}.${TLD}"
|
||||||
|
export LDAP_SEARCH_BASE="ou=domains,dc=${SLD},dc=${TLD}"
|
||||||
|
export LDAP_QUERY_FILTER='(dc=%d)'
|
||||||
|
export IGNORED_NEXT_HOPS=test.next-host
|
||||||
|
```
|
||||||
|
|
||||||
|
# start milter
|
||||||
|
`python3 app/sos-milter.py`
|
||||||
|
|
||||||
|
# execute `miltertest`
|
||||||
|
First of all install the `miltertest` binary. Under debian based distros
|
||||||
|
it´s located in the `opendkim-tools` package.
|
||||||
|
|
||||||
|
`miltertest -v -D socket=inet:12345@127.0.0.111 -s tests/miltertest.lua`
|
||||||
@ -1,8 +1,8 @@
|
|||||||
-- https://mopano.github.io/sendmail-filter-api/constant-values.html#com.sendmail.milter.MilterConstants
|
-- https://mopano.github.io/sendmail-filter-api/constant-values.html#com.sendmail.milter.MilterConstants
|
||||||
-- http://www.opendkim.org/miltertest.8.html
|
-- http://www.opendkim.org/miltertest.8.html
|
||||||
|
|
||||||
--conn = mt.connect("inet:8020@10.42.50.2")
|
-- socket must be defined as miltertest global variable (-D)
|
||||||
conn = mt.connect("inet:12345@127.0.0.1")
|
conn = mt.connect(socket)
|
||||||
if conn == nil then
|
if conn == nil then
|
||||||
error "mt.connect() failed"
|
error "mt.connect() failed"
|
||||||
end
|
end
|
||||||
@ -10,7 +10,42 @@ end
|
|||||||
mt.set_timeout(3)
|
mt.set_timeout(3)
|
||||||
|
|
||||||
-- 5321.FROM + MACROS
|
-- 5321.FROM + MACROS
|
||||||
mt.macro(conn, SMFIC_MAIL, "i", "test-id",'{rcpt_host}', "test.next-hostx")
|
mt.macro(conn, SMFIC_MAIL, '{client_addr}', "127.128.129.130", "i", "TestQueueId",'{rcpt_host}', "test.next-host")
|
||||||
|
if mt.mailfrom(conn, "dominik@dc-it-con.de") ~= nil then
|
||||||
|
error "mt.mailfrom() failed"
|
||||||
|
end
|
||||||
|
if mt.getreply(conn) ~= SMFIR_CONTINUE then
|
||||||
|
error "mt.mailfrom() unexpected reply"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- 5321.RCPT
|
||||||
|
if mt.rcptto(conn, "info@dc-it-con.de") ~= nil then
|
||||||
|
error "mt.rcptto() failed"
|
||||||
|
end
|
||||||
|
if mt.getreply(conn) ~= SMFIR_CONTINUE then
|
||||||
|
error "mt.rcptto() unexpected reply"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- EOM
|
||||||
|
if mt.eom(conn) ~= nil then
|
||||||
|
error "mt.eom() failed"
|
||||||
|
end
|
||||||
|
mt.echo("EOM: " .. mt.getreply(conn))
|
||||||
|
if mt.getreply(conn) == SMFIR_CONTINUE then
|
||||||
|
mt.echo("EOM-continue")
|
||||||
|
elseif mt.getreply(conn) == SMFIR_REPLYCODE then
|
||||||
|
mt.echo("EOM-reject")
|
||||||
|
end
|
||||||
|
|
||||||
|
if not mt.eom_check(conn, MT_HDRADD, "X-SOS-Milter") then
|
||||||
|
mt.echo("no header added")
|
||||||
|
else
|
||||||
|
mt.echo("X-SOS-Milter header added -> LDAP-Domain with broken SPF")
|
||||||
|
end
|
||||||
|
|
||||||
|
--asdf
|
||||||
|
-- 5321.FROM + MACROS
|
||||||
|
mt.macro(conn, SMFIC_MAIL, '{client_addr}', "127.128.129.130", "i", "TestQueueId",'{rcpt_host}', "test.next-hostx")
|
||||||
if mt.mailfrom(conn, "dominik@dc-it-con.de") ~= nil then
|
if mt.mailfrom(conn, "dominik@dc-it-con.de") ~= nil then
|
||||||
error "mt.mailfrom() failed"
|
error "mt.mailfrom() failed"
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user