mirror of
https://github.com/chillout2k/ExOTA-Milter.git
synced 2025-12-14 02:30:17 +00:00
Compare commits
5 Commits
aa8dc27402
...
a8c5aae039
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8c5aae039 | ||
| 78e82b5157 | |||
| 2cec47322b | |||
| d9c7b08072 | |||
| 2cc2d0b47e |
10
README.md
10
README.md
@ -86,7 +86,7 @@ X-MS-Exchange-CrossTenant-Id: <UUID-of-tenant>
|
|||||||
At last the **ExOTA-Milter** needs an additional policy (JSON file), that provides a mapping of *sender-domain <-> tenant-id* and if DKIM-signatures must be taken under consideration or not. The JSON policy file itself looks like this:
|
At last the **ExOTA-Milter** needs an additional policy (JSON file), that provides a mapping of *sender-domain <-> tenant-id* and if DKIM-signatures must be taken under consideration or not. The JSON policy file itself looks like this:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"lalalulu.onmicrosoft.com": {
|
"yad.onmicrosoft.com": {
|
||||||
"tenant_id": "1234abcd-18c5-45e8-88de-123456789abc",
|
"tenant_id": "1234abcd-18c5-45e8-88de-123456789abc",
|
||||||
"dkim_enabled": true
|
"dkim_enabled": true
|
||||||
}
|
}
|
||||||
@ -131,12 +131,12 @@ Prerequisites: `docker-compose` installed
|
|||||||
* Create the policy file `data/policy.json` with following content:
|
* Create the policy file `data/policy.json` with following content:
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
"lalalulu.onmicrosoft.com": {
|
"yad.onmicrosoft.com": {
|
||||||
"tenant_id": "1234abcd-18c5-45e8-88de-123456789abc",
|
"tenant_id": "1234abcd-18c5-45e8-88de-123456789abc",
|
||||||
"dkim_enabled": true
|
"dkim_enabled": true
|
||||||
},
|
},
|
||||||
"asdf2.onmicrosoft.com": {
|
"example.com": {
|
||||||
"tenant_id": "asdftasdfa",
|
"tenant_id": "abcd1234-18c5-45e8-88de-987654321cba",
|
||||||
"dkim_enabled": false
|
"dkim_enabled": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,6 +157,8 @@ services:
|
|||||||
MILTER_TRUSTED_AUTHSERVID: 'my-auth-serv-id'
|
MILTER_TRUSTED_AUTHSERVID: 'my-auth-serv-id'
|
||||||
MILTER_X509_ENABLED: 'some_value'
|
MILTER_X509_ENABLED: 'some_value'
|
||||||
MILTER_X509_TRUSTED_CN: 'mail.protection.outlook.com'
|
MILTER_X509_TRUSTED_CN: 'mail.protection.outlook.com'
|
||||||
|
MILTER_ADD_HEADER: 'some_value'
|
||||||
|
MILTER_AUTHSERVID: 'my-auth-serv-id'
|
||||||
volumes:
|
volumes:
|
||||||
- "./data/:/data/:ro"
|
- "./data/:/data/:ro"
|
||||||
- "./socket/:/socket/:rw"
|
- "./socket/:/socket/:rw"
|
||||||
|
|||||||
@ -37,6 +37,10 @@ g_milter_policy_file = '/data/policy.json'
|
|||||||
g_milter_x509_enabled = False
|
g_milter_x509_enabled = False
|
||||||
# ENV[MILTER_X509_TRUSTED_CN]
|
# ENV[MILTER_X509_TRUSTED_CN]
|
||||||
g_milter_x509_trusted_cn = 'mail.protection.outlook.com'
|
g_milter_x509_trusted_cn = 'mail.protection.outlook.com'
|
||||||
|
# ENV[MILTER_ADD_HEADER]
|
||||||
|
g_milter_add_header = False
|
||||||
|
# ENV[MILTER_AUTHSERVID]
|
||||||
|
g_milter_authservid = None
|
||||||
|
|
||||||
# Another globals
|
# Another globals
|
||||||
g_policy_backend = None
|
g_policy_backend = None
|
||||||
@ -63,23 +67,37 @@ class ExOTAMilter(Milter.Base):
|
|||||||
)
|
)
|
||||||
logging.debug(self.mconn_id + " reset_milter()")
|
logging.debug(self.mconn_id + " reset_milter()")
|
||||||
|
|
||||||
|
def smfir_reject(self, **kwargs):
|
||||||
|
message = g_milter_reject_message
|
||||||
|
if 'message' in kwargs:
|
||||||
|
message = kwargs['message']
|
||||||
|
self.setreply('550','5.7.1', message)
|
||||||
|
return Milter.REJECT
|
||||||
|
|
||||||
|
def smfir_tmpfail(self, **kwargs):
|
||||||
|
message = g_milter_tmpfail_message
|
||||||
|
if 'message' in kwargs:
|
||||||
|
message = kwargs['message']
|
||||||
|
self.setreply('450','4.7.1', message)
|
||||||
|
return Milter.TEMPFAIL
|
||||||
|
|
||||||
|
def smfir_continue(self):
|
||||||
|
return Milter.CONTINUE
|
||||||
|
|
||||||
# Not registered/used callbacks
|
# Not registered/used callbacks
|
||||||
@Milter.nocallback
|
@Milter.nocallback
|
||||||
def hello(self, heloname):
|
def hello(self, heloname):
|
||||||
return Milter.CONTINUE
|
return self.smfir_continue()
|
||||||
@Milter.nocallback
|
@Milter.nocallback
|
||||||
def eoh(self):
|
def eoh(self):
|
||||||
return Milter.CONTINUE
|
return self.smfir_continue()
|
||||||
@Milter.nocallback
|
@Milter.nocallback
|
||||||
def body(self, chunk):
|
def body(self, chunk):
|
||||||
return Milter.CONTINUE
|
return self.smfir_continue()
|
||||||
|
|
||||||
def connect(self, IPname, family, hostaddr):
|
def connect(self, IPname, family, hostaddr):
|
||||||
self.client_ip = hostaddr[0]
|
self.client_ip = hostaddr[0]
|
||||||
logging.debug(self.mconn_id +
|
return self.smfir_continue()
|
||||||
"/CONNECT client_ip=[" + self.client_ip + "]:" + str(hostaddr[1])
|
|
||||||
)
|
|
||||||
return Milter.CONTINUE
|
|
||||||
|
|
||||||
# Mandatory callback
|
# Mandatory callback
|
||||||
def envfrom(self, mailfrom, *str):
|
def envfrom(self, mailfrom, *str):
|
||||||
@ -92,12 +110,12 @@ class ExOTAMilter(Milter.Base):
|
|||||||
else:
|
else:
|
||||||
self.conn_reused = True
|
self.conn_reused = True
|
||||||
logging.debug(self.mconn_id + "/FROM client_ip={0}".format(self.client_ip))
|
logging.debug(self.mconn_id + "/FROM client_ip={0}".format(self.client_ip))
|
||||||
return Milter.CONTINUE
|
return self.smfir_continue()
|
||||||
|
|
||||||
# Mandatory callback
|
# Mandatory callback
|
||||||
def envrcpt(self, to, *str):
|
def envrcpt(self, to, *str):
|
||||||
logging.debug(self.mconn_id + "/RCPT 5321.rcpt={0}".format(to))
|
logging.debug(self.mconn_id + "/RCPT 5321.rcpt={0}".format(to))
|
||||||
return Milter.CONTINUE
|
return self.smfir_continue()
|
||||||
|
|
||||||
def header(self, name, hval):
|
def header(self, name, hval):
|
||||||
logging.debug(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.debug(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
@ -113,8 +131,7 @@ class ExOTAMilter(Milter.Base):
|
|||||||
logging.error(self.mconn_id + "/" + str(self.getsymval('i')) + "/HDR " +
|
logging.error(self.mconn_id + "/" + str(self.getsymval('i')) + "/HDR " +
|
||||||
"Could not determine domain-part of 5322.from=" + self.hdr_from
|
"Could not determine domain-part of 5322.from=" + self.hdr_from
|
||||||
)
|
)
|
||||||
self.setreply('450','4.7.1', g_milter_tmpfail_message)
|
return self.smfir_reject()
|
||||||
return Milter.TEMPFAIL
|
|
||||||
self.hdr_from_domain = m.group(1)
|
self.hdr_from_domain = m.group(1)
|
||||||
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/HDR: 5322.from={0}, 5322.from_domain={1}".format(
|
"/HDR: 5322.from={0}, 5322.from_domain={1}".format(
|
||||||
@ -150,11 +167,11 @@ class ExOTAMilter(Milter.Base):
|
|||||||
logging.debug(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.debug(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/HDR: Ignoring authentication results of {0}".format(ar.authserv_id)
|
"/HDR: Ignoring authentication results of {0}".format(ar.authserv_id)
|
||||||
)
|
)
|
||||||
except:
|
except Exception as e:
|
||||||
logging.error(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/HDR: AR-parse exception: " + traceback.format_exc()
|
"/HDR: AR-parse exception: {0}".format(str(e))
|
||||||
)
|
)
|
||||||
return Milter.CONTINUE
|
return self.smfir_continue()
|
||||||
|
|
||||||
# EOM is mandatory as well and thus always called by MTA
|
# EOM is mandatory as well and thus always called by MTA
|
||||||
def eom(self):
|
def eom(self):
|
||||||
@ -167,8 +184,7 @@ class ExOTAMilter(Milter.Base):
|
|||||||
logging.info(self.mconn_id + "/" + str(self.getsymval('i'))
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i'))
|
||||||
+ "/EOM: No trusted x509 client CN found - action=reject"
|
+ "/EOM: No trusted x509 client CN found - action=reject"
|
||||||
)
|
)
|
||||||
self.setreply('550','5.7.1', g_milter_tmpfail_message)
|
return self.smfir_reject()
|
||||||
return Milter.REJECT
|
|
||||||
else:
|
else:
|
||||||
if g_milter_x509_trusted_cn.lower() == cert_subject.lower():
|
if g_milter_x509_trusted_cn.lower() == cert_subject.lower():
|
||||||
self.x509_client_valid = True
|
self.x509_client_valid = True
|
||||||
@ -179,15 +195,13 @@ class ExOTAMilter(Milter.Base):
|
|||||||
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/EOM Untrusted x509 client CN {0} - action=reject".format(cert_subject)
|
"/EOM Untrusted x509 client CN {0} - action=reject".format(cert_subject)
|
||||||
)
|
)
|
||||||
self.setreply('550','5.7.1', g_milter_tmpfail_message)
|
return self.smfir_reject()
|
||||||
return Milter.REJECT
|
|
||||||
|
|
||||||
if self.hdr_from is None:
|
if self.hdr_from is None:
|
||||||
logging.error(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.error(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/EOM exception: could not determine 5322.from header - action=reject"
|
"/EOM exception: could not determine 5322.from header - action=reject"
|
||||||
)
|
)
|
||||||
self.setreply('550','5.7.1', g_milter_tmpfail_message)
|
return self.smfir_reject()
|
||||||
return Milter.REJECT
|
|
||||||
|
|
||||||
# Get policy for 5322.from_domain
|
# Get policy for 5322.from_domain
|
||||||
policy = None
|
policy = None
|
||||||
@ -200,23 +214,20 @@ class ExOTAMilter(Milter.Base):
|
|||||||
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/EOM {0}".format(e.message)
|
"/EOM {0}".format(e.message)
|
||||||
)
|
)
|
||||||
self.setreply('550','5.7.1', g_milter_reject_message)
|
return self.smfir_reject()
|
||||||
return Milter.REJECT
|
|
||||||
|
|
||||||
if self.hdr_tenant_id is None:
|
if self.hdr_tenant_id is None:
|
||||||
logging.error(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.error(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/EOM exception: could not determine X-MS-Exchange-CrossTenant-Id - action=reject"
|
"/EOM exception: could not determine X-MS-Exchange-CrossTenant-Id - action=reject"
|
||||||
)
|
)
|
||||||
self.setreply('550','5.7.1', g_milter_reject_message)
|
return self.smfir_reject()
|
||||||
return Milter.REJECT
|
|
||||||
if self.hdr_tenant_id_count > 1:
|
if self.hdr_tenant_id_count > 1:
|
||||||
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/EOM: More than one tenant-IDs for {0} found - action=reject".format(
|
"/EOM: More than one tenant-IDs for {0} found - action=reject".format(
|
||||||
self.hdr_from_domain
|
self.hdr_from_domain
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.setreply('550','5.7.1', g_milter_reject_message)
|
return self.smfir_reject()
|
||||||
return Milter.REJECT
|
|
||||||
if self.hdr_tenant_id == policy.get_tenant_id():
|
if self.hdr_tenant_id == policy.get_tenant_id():
|
||||||
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/EOM: tenant_id={0} status=match".format(self.hdr_tenant_id)
|
"/EOM: tenant_id={0} status=match".format(self.hdr_tenant_id)
|
||||||
@ -227,8 +238,7 @@ class ExOTAMilter(Milter.Base):
|
|||||||
self.hdr_tenant_id
|
self.hdr_tenant_id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.setreply('550','5.7.1', g_milter_reject_message)
|
return self.smfir_reject()
|
||||||
return Milter.REJECT
|
|
||||||
|
|
||||||
if g_milter_dkim_enabled and policy.is_dkim_enabled():
|
if g_milter_dkim_enabled and policy.is_dkim_enabled():
|
||||||
logging.debug(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.debug(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
@ -256,15 +266,12 @@ class ExOTAMilter(Milter.Base):
|
|||||||
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/EOM: No DKIM authentication results (AR headers) found - action=reject"
|
"/EOM: No DKIM authentication results (AR headers) found - action=reject"
|
||||||
)
|
)
|
||||||
self.setreply('550','5.7.1', g_milter_reject_message)
|
return self.smfir_reject()
|
||||||
return Milter.REJECT
|
|
||||||
if self.dkim_valid == False:
|
if self.dkim_valid == False:
|
||||||
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/EOM: DKIM authentication failed - action=reject"
|
"/EOM: DKIM authentication failed - action=reject"
|
||||||
)
|
)
|
||||||
self.setreply('550','5.7.1', g_milter_reject_message)
|
return self.smfir_reject()
|
||||||
return Milter.REJECT
|
|
||||||
|
|
||||||
if g_milter_dkim_enabled:
|
if g_milter_dkim_enabled:
|
||||||
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/EOM: Tenant authentication successful (dkim_enabled={0})".format(
|
"/EOM: Tenant authentication successful (dkim_enabled={0})".format(
|
||||||
@ -275,18 +282,33 @@ class ExOTAMilter(Milter.Base):
|
|||||||
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
"/EOM: Tenant successfully authenticated"
|
"/EOM: Tenant successfully authenticated"
|
||||||
)
|
)
|
||||||
return Milter.CONTINUE
|
if g_milter_add_header:
|
||||||
|
try:
|
||||||
|
self.addheader("X-ExOTA-Authentication-Results",
|
||||||
|
"{0};\n exota=pass header.d={1} dkim={2} x509_client_trust={3}".format(
|
||||||
|
g_milter_authservid, self.hdr_from_domain, policy.is_dkim_enabled(),
|
||||||
|
g_milter_x509_enabled
|
||||||
|
)
|
||||||
|
)
|
||||||
|
logging.debug(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
|
"/EOM: AR-header added"
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logging.info(self.mconn_id + "/" + str(self.getsymval('i')) +
|
||||||
|
"/EOM: addheader(AR) failed: {0}".format(str(e))
|
||||||
|
)
|
||||||
|
return self.smfir_continue()
|
||||||
|
|
||||||
def abort(self):
|
def abort(self):
|
||||||
# Client disconnected prematurely
|
# Client disconnected prematurely
|
||||||
logging.debug(self.mconn_id + "/ABORT")
|
logging.debug(self.mconn_id + "/ABORT")
|
||||||
return Milter.CONTINUE
|
return self.smfir_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")
|
logging.debug(self.mconn_id + "/CLOSE")
|
||||||
return Milter.CONTINUE
|
return self.smfir_continue()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
if 'LOG_LEVEL' in os.environ:
|
if 'LOG_LEVEL' in os.environ:
|
||||||
@ -305,6 +327,7 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
if 'MILTER_NAME' in os.environ:
|
if 'MILTER_NAME' in os.environ:
|
||||||
g_milter_name = os.environ['MILTER_NAME']
|
g_milter_name = os.environ['MILTER_NAME']
|
||||||
|
logging.info("ENV[MILTER_NAME]: {0}".format(g_milter_name))
|
||||||
if 'MILTER_SOCKET' in os.environ:
|
if 'MILTER_SOCKET' in os.environ:
|
||||||
g_milter_socket = os.environ['MILTER_SOCKET']
|
g_milter_socket = os.environ['MILTER_SOCKET']
|
||||||
logging.info("ENV[MILTER_SOCKET]: {0}".format(g_milter_socket))
|
logging.info("ENV[MILTER_SOCKET]: {0}".format(g_milter_socket))
|
||||||
@ -332,6 +355,17 @@ if __name__ == "__main__":
|
|||||||
if 'MILTER_POLICY_SOURCE' in os.environ:
|
if 'MILTER_POLICY_SOURCE' in os.environ:
|
||||||
g_milter_policy_source = os.environ['MILTER_POLICY_SOURCE']
|
g_milter_policy_source = os.environ['MILTER_POLICY_SOURCE']
|
||||||
logging.info("ENV[MILTER_POLICY_SOURCE]: {0}".format(g_milter_policy_source))
|
logging.info("ENV[MILTER_POLICY_SOURCE]: {0}".format(g_milter_policy_source))
|
||||||
|
if 'MILTER_ADD_HEADER' in os.environ:
|
||||||
|
g_milter_add_header = True
|
||||||
|
if 'MILTER_AUTHSERVID' in os.environ:
|
||||||
|
g_milter_authservid = os.environ['MILTER_AUTHSERVID']
|
||||||
|
if not re.match(r'^\S+$', g_milter_authservid):
|
||||||
|
logging.error("ENV[MILTER_AUTHSERVID] is invalid: {0}".format(g_milter_authservid))
|
||||||
|
logging.info("ENV[MILTER_AUTHSERVID]: {0}".format(g_milter_authservid))
|
||||||
|
else:
|
||||||
|
logging.error("ENV[MILTER_AUTHSERVID] is mandatory!")
|
||||||
|
sys.exit(1)
|
||||||
|
logging.info("ENV[MILTER_ADD_HEADER]: {0}".format(g_milter_add_header))
|
||||||
if g_milter_policy_source == 'file':
|
if g_milter_policy_source == 'file':
|
||||||
if 'MILTER_POLICY_FILE' in os.environ:
|
if 'MILTER_POLICY_FILE' in os.environ:
|
||||||
g_milter_policy_file = os.environ['MILTER_POLICY_FILE']
|
g_milter_policy_file = os.environ['MILTER_POLICY_FILE']
|
||||||
@ -355,6 +389,8 @@ if __name__ == "__main__":
|
|||||||
timeout = 600
|
timeout = 600
|
||||||
# Register to have the Milter factory create instances of your class:
|
# Register to have the Milter factory create instances of your class:
|
||||||
Milter.factory = ExOTAMilter
|
Milter.factory = ExOTAMilter
|
||||||
|
flags = Milter.ADDHDRS
|
||||||
|
Milter.set_flags(flags)
|
||||||
logging.info("Startup " + g_milter_name +
|
logging.info("Startup " + g_milter_name +
|
||||||
"@socket: " + g_milter_socket
|
"@socket: " + g_milter_socket
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
import re
|
import re
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
class ExOTAPolicyException(Exception):
|
class ExOTAPolicyException(Exception):
|
||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
@ -30,14 +31,16 @@ class ExOTAPolicy():
|
|||||||
"Policy must have a 'tenant_id' attribute!"
|
"Policy must have a 'tenant_id' attribute!"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if policy_dict['tenant_id'] == '':
|
try:
|
||||||
|
UUID(policy_dict['tenant_id'])
|
||||||
|
except ValueError as e:
|
||||||
raise ExOTAPolicyInvalidException(
|
raise ExOTAPolicyInvalidException(
|
||||||
"'tenant_id' must not be empty!"
|
"Invalid 'tenant_id': {0}".format(str(e))
|
||||||
)
|
) from e
|
||||||
if re.match(r'^.*\s+.*$', policy_dict['tenant_id']):
|
except Exception as e:
|
||||||
raise ExOTAPolicyInvalidException(
|
raise ExOTAPolicyInvalidException(
|
||||||
"'tenant_id' must not contain whitespace characters!"
|
"Invalid 'tenant_id': {0}".format(traceback.format_exc())
|
||||||
)
|
) from e
|
||||||
if 'dkim_enabled' not in policy_dict:
|
if 'dkim_enabled' not in policy_dict:
|
||||||
raise ExOTAPolicyInvalidException(
|
raise ExOTAPolicyInvalidException(
|
||||||
"Policy must have a 'dkim_enabled' attribute!"
|
"Policy must have a 'dkim_enabled' attribute!"
|
||||||
|
|||||||
@ -19,6 +19,8 @@ export MILTER_DKIM_ENABLED=yepp
|
|||||||
export MILTER_TRUSTED_AUTHSERVID=my-auth-serv-id
|
export MILTER_TRUSTED_AUTHSERVID=my-auth-serv-id
|
||||||
export MILTER_X509_ENABLED=yepp
|
export MILTER_X509_ENABLED=yepp
|
||||||
export MILTER_X509_TRUSTED_CN=mail.protection.outlook.com
|
export MILTER_X509_TRUSTED_CN=mail.protection.outlook.com
|
||||||
|
export MILTER_ADD_HEADER=yepp
|
||||||
|
export MILTER_AUTHSERVID=my-auth-serv-id
|
||||||
```
|
```
|
||||||
|
|
||||||
# Shell-1: start ExOTA-Milter
|
# Shell-1: start ExOTA-Milter
|
||||||
|
|||||||
@ -27,7 +27,7 @@ if mt.getreply(conn) ~= SMFIR_CONTINUE then
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- HEADER
|
-- HEADER
|
||||||
if mt.header(conn, "From", '"Blah Blubb" <O365ConnectorValidation@lalalulu.onmicrosoft.com>') ~= nil then
|
if mt.header(conn, "From", '"Blah Blubb" <O365ConnectorValidation@yad.onmicrosoft.com>') ~= nil then
|
||||||
error "mt.header(From) failed"
|
error "mt.header(From) failed"
|
||||||
end
|
end
|
||||||
if mt.header(conn, "X-MS-Exchange-CrossTenant-Id", "1234abcd-18c5-45e8-88de-123456789abc") ~= nil then
|
if mt.header(conn, "X-MS-Exchange-CrossTenant-Id", "1234abcd-18c5-45e8-88de-123456789abc") ~= nil then
|
||||||
@ -36,16 +36,19 @@ end
|
|||||||
--if mt.header(conn, "X-MS-Exchange-CrossTenant-Id", "4321abcd-18c5-45e8-88de-blahblubb") ~= nil then
|
--if mt.header(conn, "X-MS-Exchange-CrossTenant-Id", "4321abcd-18c5-45e8-88de-blahblubb") ~= nil then
|
||||||
-- error "mt.header(Subject) failed"
|
-- error "mt.header(Subject) failed"
|
||||||
--end
|
--end
|
||||||
if mt.header(conn, "Authentication-Results", "another-wrong-auth-serv-id;\n dkim=fail header.d=lalalulu.onmicrosoft.com header.s=selector1-lalalulu-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
|
if mt.header(conn, "Authentication-Results", "another-wrong-auth-serv-id;\n dkim=fail header.d=yad.onmicrosoft.com header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
|
||||||
error "mt.header(Subject) failed"
|
error "mt.header(Subject) failed"
|
||||||
end
|
end
|
||||||
if mt.header(conn, "Authentication-Results", "wrong-auth-serv-id;\n dkim=pass header.d=lalalulu.onmicrosoft.com header.s=selector1-lalalulu-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
|
if mt.header(conn, "Authentication-Results", "wrong-auth-serv-id;\n dkim=pass header.d=yad.onmicrosoft.com header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
|
||||||
error "mt.header(Subject) failed"
|
error "mt.header(Subject) failed"
|
||||||
end
|
end
|
||||||
if mt.header(conn, "Authentication-Results", "my-auth-serv-id;\n dkim=pass header.d=lalalulu.onmicrosoft.com header.s=selector1-lalalulu-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
|
if mt.header(conn, "Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then
|
||||||
error "mt.header(Subject) failed"
|
error "mt.header(Subject) failed"
|
||||||
end
|
end
|
||||||
if mt.header(conn, "Authentication-Results", "my-auth-serv-id;\n dkim=fail header.d=lalalulu.onmicrosoft.com header.s=selector2-asdf header.b=mmmjFpv8") ~= nil then
|
if mt.header(conn, "Authentication-Results", "my-auth-serv-id;\n dkim=pass header.d=yad.onmicrosoft.com header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
|
||||||
|
error "mt.header(Subject) failed"
|
||||||
|
end
|
||||||
|
if mt.header(conn, "Authentication-Results", "my-auth-serv-id;\n dkim=fail header.d=yad.onmicrosoft.com header.s=selector2-asdf header.b=mmmjFpv8") ~= nil then
|
||||||
error "mt.header(Subject) failed"
|
error "mt.header(Subject) failed"
|
||||||
end
|
end
|
||||||
if mt.header(conn, "Authentication-Results", "some-validating-host;\n dkim=pass header.d=paypal.de header.s=pp-dkim1 header.b=PmTtUzer;\n dmarc=pass (policy=reject) header.from=paypal.de;\n spf=pass (some-validating-host: domain of service@paypal.de designates 173.0.84.226 as permitted sender) smtp.mailfrom=service@paypal.de") ~= nil then
|
if mt.header(conn, "Authentication-Results", "some-validating-host;\n dkim=pass header.d=paypal.de header.s=pp-dkim1 header.b=PmTtUzer;\n dmarc=pass (policy=reject) header.from=paypal.de;\n spf=pass (some-validating-host: domain of service@paypal.de designates 173.0.84.226 as permitted sender) smtp.mailfrom=service@paypal.de") ~= nil then
|
||||||
@ -63,10 +66,10 @@ elseif mt.getreply(conn) == SMFIR_REPLYCODE then
|
|||||||
mt.echo("EOM-reject")
|
mt.echo("EOM-reject")
|
||||||
end
|
end
|
||||||
|
|
||||||
if not mt.eom_check(conn, MT_HDRADD, "X-SOS-Milter") then
|
if not mt.eom_check(conn, MT_HDRADD, "X-ExOTA-Authentication-Results") then
|
||||||
mt.echo("no header added")
|
mt.echo("no header added")
|
||||||
else
|
else
|
||||||
mt.echo("X-SOS-Milter header added -> LDAP-Domain with broken SPF")
|
mt.echo("X-ExOTA-Authentication-Results header added")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- DISCONNECT
|
-- DISCONNECT
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"lalalulu.onmicrosoft.com": {
|
"yad.onmicrosoft.com": {
|
||||||
"tenant_id": "1234abcd-18c5-45e8-88de-123456789abc",
|
"tenant_id": "1234abcd-18c5-45e8-88de-123456789abc",
|
||||||
"dkim_enabled": true
|
"dkim_enabled": true
|
||||||
},
|
},
|
||||||
"asdf2.onmicrosoft.com": {
|
"example.com": {
|
||||||
"tenant_id": "asdftasdfa",
|
"tenant_id": "abcd1234-18c5-45e8-88de-987654321cba",
|
||||||
"dkim_enabled": true
|
"dkim_enabled": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user