Compare commits

..

12 Commits

Author SHA1 Message Date
a82d27004f docs: ENV-options + tests refactoring 2023-06-05 21:55:04 +02:00
d395344401 kubernetes via kustomize 2023-06-05 19:40:36 +02:00
a25ee7fd11 refactoring + purge 2023-06-05 18:56:00 +02:00
686a5de1a0 docs: fix for docker-image uri 2023-01-31 00:40:11 +01:00
fadf6966be Make X-MS-Exchange-CrossTenant-Id header optional - take 2 2023-01-31 00:29:44 +01:00
3e9409717f docs + some OCI enhancements 2022-10-20 21:19:50 +02:00
Dominik Chilla
6c844f22e7
Merge pull request #47 from chillout2k/oci-patch
OCI build plan changed
2022-10-20 20:38:40 +02:00
Dominik Chilla
931570677b
OCI build plan changed 2022-10-20 20:38:20 +02:00
Dominik Chilla
7b60d6a845
Merge pull request #46 from chillout2k/devel
Make X-MS-Exchange-CrossTenant-Id header optional
2022-08-31 13:21:34 +02:00
ca703bee9c Make X-MS-Exchange-CrossTenant-Id header optional 2022-08-31 13:15:13 +02:00
Dominik Chilla
0dc507a425
Merge pull request #45 from chillout2k/devel
systemd install typo
2022-08-24 21:52:14 +02:00
010e9eadfa systemd install typo 2022-08-24 21:51:31 +02:00
23 changed files with 397 additions and 302 deletions

View File

@ -1,98 +1,122 @@
# How to install ExOTA-Milter # How to install ExOTA-Milter
#### Table of contents #### Table of contents
[docker-compose](#docker-compose) [docker-compose](#docker-compose)
[kubernetes](#kubernetes) [kubernetes](#kubernetes)
[systemd](#systemd) [systemd](#systemd)
## docker-compose <a name="docker-compose"/> ## docker-compose <a name="docker-compose"/>
``` ```
~/src/ExOTA-Milter/INSTALL/docker-compose$ docker-compose up ~/src/ExOTA-Milter/INSTALL/docker-compose$ docker-compose up
[+] Running 2/2 [+] Running 2/2
⠿ Network docker-compose_default Created 0.8s ⠿ Network docker-compose_default Created 0.8s
⠿ Container docker-compose-exota-milter-1 Created 0.1s ⠿ Container docker-compose-exota-milter-1 Created 0.1s
Attaching to docker-compose-exota-milter-1 Attaching to docker-compose-exota-milter-1
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,503: INFO 140529821924168 Logger initialized docker-compose-exota-milter-1 | 2022-06-06 21:54:04,503: INFO 140529821924168 Logger initialized
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,503: INFO 140529821924168 ENV[MILTER_NAME]: exota-milter docker-compose-exota-milter-1 | 2022-06-06 21:54:04,503: INFO 140529821924168 ENV[MILTER_NAME]: exota-milter
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,503: INFO 140529821924168 ENV[MILTER_SOCKET]: inet:4321@0.0.0.0 docker-compose-exota-milter-1 | 2022-06-06 21:54:04,503: INFO 140529821924168 ENV[MILTER_SOCKET]: inet:4321@0.0.0.0
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_REJECT_MESSAGE]: CUSTOMIZE THIS! - Security policy violation!! docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_REJECT_MESSAGE]: CUSTOMIZE THIS! - Security policy violation!!
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_TMPFAIL_MESSAGE]: Service temporarily not available! Please try again later. docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_TMPFAIL_MESSAGE]: Service temporarily not available! Please try again later.
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_TRUSTED_AUTHSERVID]: dkimauthservid docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_TRUSTED_AUTHSERVID]: dkimauthservid
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_DKIM_ALIGNMENT_REQUIRED]: True docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_DKIM_ALIGNMENT_REQUIRED]: True
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_DKIM_ENABLED]: True docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_DKIM_ENABLED]: True
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_X509_TRUSTED_CN]: mail.protection.outlook.com docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_X509_TRUSTED_CN]: mail.protection.outlook.com
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_X509_IP_WHITELIST]: ['127.0.0.1', '::1'] docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_X509_IP_WHITELIST]: ['127.0.0.1', '::1']
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_X509_ENABLED]: True docker-compose-exota-milter-1 | 2022-06-06 21:54:04,504: INFO 140529821924168 ENV[MILTER_X509_ENABLED]: True
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,505: INFO 140529821924168 ENV[MILTER_AUTHSERVID]: ThisAuthservID docker-compose-exota-milter-1 | 2022-06-06 21:54:04,505: INFO 140529821924168 ENV[MILTER_AUTHSERVID]: ThisAuthservID
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,505: INFO 140529821924168 ENV[MILTER_ADD_HEADER]: True docker-compose-exota-milter-1 | 2022-06-06 21:54:04,505: INFO 140529821924168 ENV[MILTER_ADD_HEADER]: True
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,505: INFO 140529821924168 ENV[MILTER_POLICY_SOURCE]: file docker-compose-exota-milter-1 | 2022-06-06 21:54:04,505: INFO 140529821924168 ENV[MILTER_POLICY_SOURCE]: file
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,505: INFO 140529821924168 ENV[MILTER_POLICY_FILE]: /data/exota-milter-policy.json docker-compose-exota-milter-1 | 2022-06-06 21:54:04,505: INFO 140529821924168 ENV[MILTER_POLICY_FILE]: /data/exota-milter-policy.json
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,511: INFO 140529821924168 JSON policy backend initialized docker-compose-exota-milter-1 | 2022-06-06 21:54:04,511: INFO 140529821924168 JSON policy backend initialized
docker-compose-exota-milter-1 | 2022-06-06 21:54:04,511: INFO 140529821924168 Startup exota-milter@socket: inet:4321@0.0.0.0 docker-compose-exota-milter-1 | 2022-06-06 21:54:04,511: INFO 140529821924168 Startup exota-milter@socket: inet:4321@0.0.0.0
``` ```
## kubernetes <a name="kubernetes"/> ## kubernetes <a name="kubernetes"/>
``` By default this example installs the Exota-milter workload into the `exota-milter` namespace, which must be created in advance:
~/src/ExOTA-Milter/INSTALL/kubernetes$ kubectl apply -f 01_config-map.yaml ```
configmap/exota-milter-policy-cmap created kubectl create ns exota-milter
~/src/ExOTA-Milter/INSTALL/kubernetes$ kubectl apply -f 02_deployment.yaml namespace/exota-milter created
deployment.apps/exota-milter created ```
Deploy stateless workload (type `Deployment`) with `kustomize`:
~/src/ExOTA-Milter/INSTALL/kubernetes$ kubectl apply -f 03_service.yaml ```
service/exota-milter created ~/src/ExOTA-Milter/INSTALL/kubernetes$ kubectl apply -k .
```
Check status of pods, replica-sets and service configmap/exota-milter-policy-cmap created
``` service/exota-milter created
~/src/ExOTA-Milter/INSTALL/kubernetes$ kubectl -n devel get all deployment.apps/exota-milter created
NAME READY STATUS RESTARTS AGE ```
pod/exota-milter-547dbccd8b-j69mn 1/1 Running 0 64s Check status of pods, replica-sets and cluster internal service:
pod/exota-milter-547dbccd8b-7hl6c 1/1 Running 0 64s ```
pod/exota-milter-547dbccd8b-k4ng8 1/1 Running 0 64s ~/src/ExOTA-Milter/INSTALL/kubernetes$ kubectl -n exota-milter get all
NAME READY STATUS RESTARTS AGE
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE pod/exota-milter-547dbccd8b-j69mn 1/1 Running 0 64s
service/exota-milter ClusterIP 10.43.78.163 <none> 4321/TCP 61s pod/exota-milter-547dbccd8b-7hl6c 1/1 Running 0 64s
pod/exota-milter-547dbccd8b-k4ng8 1/1 Running 0 64s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/exota-milter 3/3 3 3 64s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/exota-milter ClusterIP 10.43.78.163 <none> 4321/TCP 61s
NAME DESIRED CURRENT READY AGE
replicaset.apps/exota-milter-547dbccd8b 3 3 3 65s NAME READY UP-TO-DATE AVAILABLE AGE
``` deployment.apps/exota-milter 3/3 3 3 64s
Get logs of one of the pods:
``` NAME DESIRED CURRENT READY AGE
~/src/ExOTA-Milter/INSTALL/kubernetes$ kubectl -n devel logs exota-milter-547dbccd8b-7hl6c replicaset.apps/exota-milter-547dbccd8b 3 3 3 65s
2022-06-06 21:57:03,515: INFO Logger initialized ```
2022-06-06 21:57:03,515: INFO ENV[MILTER_NAME]: exota-milter Get logs of the pods:
2022-06-06 21:57:03,515: INFO ENV[MILTER_SOCKET]: inet:4321@127.0.0.1 ```
2022-06-06 21:57:03,515: INFO ENV[MILTER_REJECT_MESSAGE]: Security policy violation!! ~/src/ExOTA-Milter/INSTALL/kubernetes$ kubectl -n exota-milter logs -l app=exota-milter
2022-06-06 21:57:03,515: INFO ENV[MILTER_TMPFAIL_MESSAGE]: Service temporarily not available! Please try again later. 2022-06-06 21:57:03,515: INFO Logger initialized
2022-06-06 21:57:03,515: INFO ENV[MILTER_TRUSTED_AUTHSERVID]: dkimauthservid 2022-06-06 21:57:03,515: INFO ENV[MILTER_NAME]: exota-milter
2022-06-06 21:57:03,515: INFO ENV[MILTER_DKIM_ALIGNMENT_REQUIRED]: True 2022-06-06 21:57:03,515: INFO ENV[MILTER_SOCKET]: inet:4321@127.0.0.1
2022-06-06 21:57:03,515: INFO ENV[MILTER_DKIM_ENABLED]: True 2022-06-06 21:57:03,515: INFO ENV[MILTER_REJECT_MESSAGE]: Security policy violation!!
2022-06-06 21:57:03,515: INFO ENV[MILTER_X509_TRUSTED_CN]: mail.protection.outlook.com 2022-06-06 21:57:03,515: INFO ENV[MILTER_TMPFAIL_MESSAGE]: Service temporarily not available! Please try again later.
2022-06-06 21:57:03,515: INFO ENV[MILTER_X509_IP_WHITELIST]: ['127.0.0.1', '::1'] 2022-06-06 21:57:03,515: INFO ENV[MILTER_TRUSTED_AUTHSERVID]: dkimauthservid
2022-06-06 21:57:03,515: INFO ENV[MILTER_X509_ENABLED]: True 2022-06-06 21:57:03,515: INFO ENV[MILTER_DKIM_ALIGNMENT_REQUIRED]: True
2022-06-06 21:57:03,516: INFO ENV[MILTER_AUTHSERVID]: some-auth-serv-id 2022-06-06 21:57:03,515: INFO ENV[MILTER_DKIM_ENABLED]: True
2022-06-06 21:57:03,516: INFO ENV[MILTER_ADD_HEADER]: True 2022-06-06 21:57:03,515: INFO ENV[MILTER_X509_TRUSTED_CN]: mail.protection.outlook.com
2022-06-06 21:57:03,516: INFO ENV[MILTER_POLICY_SOURCE]: file 2022-06-06 21:57:03,515: INFO ENV[MILTER_X509_IP_WHITELIST]: ['127.0.0.1', '::1']
2022-06-06 21:57:03,516: INFO ENV[MILTER_POLICY_FILE]: /data/exota-milter-policy.json 2022-06-06 21:57:03,515: INFO ENV[MILTER_X509_ENABLED]: True
2022-06-06 21:57:03,516: INFO JSON policy backend initialized 2022-06-06 21:57:03,516: INFO ENV[MILTER_AUTHSERVID]: some-auth-serv-id
2022-06-06 21:57:03,516: INFO Startup exota-milter@socket: inet:4321@127.0.0.1 2022-06-06 21:57:03,516: INFO ENV[MILTER_ADD_HEADER]: True
``` 2022-06-06 21:57:03,516: INFO ENV[MILTER_POLICY_SOURCE]: file
2022-06-06 21:57:03,516: INFO ENV[MILTER_POLICY_FILE]: /data/exota-milter-policy.json
## systemd <a name="systemd"/> 2022-06-06 21:57:03,516: INFO JSON policy backend initialized
If you do not want to run the ExOTA-Milter in a containerized environment but directly as a systemd-unit/-service, first you´ll need to install all necessary python dependencies: 2022-06-06 21:57:03,516: INFO Startup exota-milter@socket: inet:4321@127.0.0.1
``` ```
~/src/ExOTA-Milter/INSTALL/systemd# sudo pip3 install -r ../../requirements.txt Remove workload from cluster:
Requirement already satisfied: authres==1.2.0 in /usr/local/lib/python3.8/dist-packages (from -r ../../requirements.txt (line 1)) (1.2.0) ```
Requirement already satisfied: pymilter==1.0.4 in /usr/local/lib/python3.8/dist-packages (from -r ../../requirements.txt (line 2)) (1.0.4) ~/src/ExOTA-Milter/INSTALL/kubernetes$ kubectl delete -k .
Requirement already satisfied: ldap3 in /usr/local/lib/python3.8/dist-packages (from -r ../../requirements.txt (line 3)) (2.9.1)
Requirement already satisfied: pyasn1>=0.4.6 in /usr/local/lib/python3.8/dist-packages (from ldap3->-r ../../requirements.txt (line 3)) (0.4.8) configmap "exota-milter-policy-cmap" deleted
``` service "exota-milter" deleted
Next you should be able to install the ExOTA-Milter as well as the systemd-stuff by running the `install.sh` script: deployment.apps "exota-milter" deleted
```
~/src/ExOTA-Milter/INSTALL/systemd$ sudo ./install.sh ~/src/ExOTA-Milter/INSTALL/kubernetes$ kubectl delete ns exota-milter
Created symlink /etc/systemd/system/multi-user.target.wants/exota-milter.service → /lib/systemd/system/exota-milter.service.
``` namespace "exota-milter" deleted
Use the `uninstall.sh` script to uninstall the ExOTA-Milter from your systemd environment. Files under `/etc/exota-milter/` (config and policy) are kept undeleted! ```
## systemd <a name="systemd"/>
If you do not want to run the ExOTA-Milter in a containerized environment but directly as a systemd-unit/-service, first you´ll need to install all necessary python and build dependencies. Start with build deps (examples refere to ubuntu/debian):
```
sudo apt install --no-install-recommends gcc libpython3-dev libmilter-dev python3-pip
```
Now install all python dependencies:
```
~/src/ExOTA-Milter/INSTALL/systemd# sudo pip3 install -r ../../requirements.txt
Requirement already satisfied: authres==1.2.0 in /usr/local/lib/python3.8/dist-packages (from -r ../../requirements.txt (line 1)) (1.2.0)
Requirement already satisfied: pymilter==1.0.4 in /usr/local/lib/python3.8/dist-packages (from -r ../../requirements.txt (line 2)) (1.0.4)
Requirement already satisfied: ldap3 in /usr/local/lib/python3.8/dist-packages (from -r ../../requirements.txt (line 3)) (2.9.1)
Requirement already satisfied: pyasn1>=0.4.6 in /usr/local/lib/python3.8/dist-packages (from ldap3->-r ../../requirements.txt (line 3)) (0.4.8)
```
At last uninstall all build dependencies, as they are not needed anymore:
```
apt purge gcc libpython3-dev libmilter-dev python3-pip
```
Next you should be able to install the ExOTA-Milter as well as the systemd-stuff by running the `install.sh` script:
```
~/src/ExOTA-Milter/INSTALL/systemd$ sudo ./install.sh
Created symlink /etc/systemd/system/multi-user.target.wants/exota-milter.service → /lib/systemd/system/exota-milter.service.
```
Use the `uninstall.sh` script to uninstall the ExOTA-Milter from your systemd environment. Files under `/etc/exota-milter/` (config and policy) are kept undeleted!

View File

@ -1,7 +1,7 @@
version: '2.4' version: '2.4'
services: services:
exota-milter: exota-milter:
image: chillout2k/exota-milter-amd64 image: chillout2k/exota-milter
restart: unless-stopped restart: unless-stopped
environment: environment:
LOG_LEVEL: 'debug' LOG_LEVEL: 'debug'

View File

@ -3,7 +3,6 @@ kind: ConfigMap
apiVersion: v1 apiVersion: v1
metadata: metadata:
name: exota-milter-policy-cmap name: exota-milter-policy-cmap
namespace: devel
data: data:
exota-milter-policy: | exota-milter-policy: |
{ {

View File

@ -2,7 +2,6 @@
kind: Deployment kind: Deployment
apiVersion: apps/v1 apiVersion: apps/v1
metadata: metadata:
namespace: devel
name: exota-milter name: exota-milter
labels: labels:
app: exota-milter app: exota-milter
@ -45,7 +44,7 @@ spec:
path: 'exota-milter-policy.json' path: 'exota-milter-policy.json'
containers: containers:
- name: exota-milter - name: exota-milter
image: chillout2k/exota-milter-amd64 image: chillout2k/exota-milter
imagePullPolicy: Always imagePullPolicy: Always
volumeMounts: volumeMounts:
- mountPath: /data - mountPath: /data

View File

@ -2,7 +2,6 @@
apiVersion: v1 apiVersion: v1
kind: Service kind: Service
metadata: metadata:
namespace: devel
name: exota-milter name: exota-milter
spec: spec:
selector: selector:

View File

@ -0,0 +1,7 @@
namespace: exota-milter
commonLabels:
app: exota-milter
resources:
- 01_config-map.yaml
- 02_deployment.yaml
- 03_service.yaml

View File

@ -3,7 +3,7 @@ Description=ExOTA-Milter
[Service] [Service]
Restart=always Restart=always
ExecStart=/usr/loca/sbin/exota-milter.sh ExecStart=/usr/local/sbin/exota-milter.sh
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target

View File

@ -1,12 +1,13 @@
ARG ARCH ARG PARENT_IMAGE=alpine:3.16
FROM $ARCH/alpine FROM ${PARENT_IMAGE}
LABEL maintainer="Dominik Chilla <dominik@zwackl.de>" LABEL maintainer="Dominik Chilla <dominik@zwackl.de>"
LABEL git_repo="https://github.com/chillout2k/exota-milter" LABEL git_repo="https://github.com/chillout2k/exota-milter"
ADD ./requirements.txt /requirements.txt ADD ./requirements.txt /requirements.txt
RUN apk update \ RUN apk update \
&& apk add python3 python3-dev py3-pip gcc libc-dev libmilter-dev \ && apk add --no-cache python3 python3-dev py3-pip \
gcc libc-dev libmilter-dev \
&& pip3 install -r requirements.txt \ && pip3 install -r requirements.txt \
&& apk del gcc libc-dev libmilter-dev python3-dev py3-pip \ && apk del gcc libc-dev libmilter-dev python3-dev py3-pip \
&& apk add libmilter \ && apk add libmilter \
@ -23,4 +24,4 @@ RUN chown -R exota-milter /app /cmd \
VOLUME [ "/socket", "/data" ] VOLUME [ "/socket", "/data" ]
USER exota-milter USER exota-milter
CMD [ "/cmd" ] CMD [ "/cmd" ]

View File

@ -99,8 +99,8 @@ By the way, the global setting `ENV[MILTER_DKIM_ALIGNMENT_REQUIRED]` can be over
} }
``` ```
## X-MS-Exchange-CrossTenant-Id header (policy binding) ## X-MS-Exchange-CrossTenant-Id header (OPTIONAL!)
Further each Microsoft Exchange-Online tenant has a unique tenant-ID in form of a UUID ([RFC 4122](https://tools.ietf.org/html/rfc4122)). **ExOTA-Milter** extracts the tenant-ID from the *X-MS-Exchange-CrossTenant-Id* email header and uses it as a *mandatory* authentication factor. Further each Microsoft Exchange-Online tenant has a unique tenant-ID in form of a UUID ([RFC 4122](https://tools.ietf.org/html/rfc4122)). **ExOTA-Milter** extracts the tenant-ID from the *X-MS-Exchange-CrossTenant-Id* email header and uses it as a *mandatory* authentication factor. Since September 2022 Microsoft did not set this header anymore reliably.
``` ```
[...] [...]
X-MS-Exchange-CrossTenant-Id: <UUID-of-tenant> X-MS-Exchange-CrossTenant-Id: <UUID-of-tenant>
@ -125,17 +125,18 @@ Finally it´s the combination of all of the above discussed aspects which may re
* matching for client certificate´s CN (ExOTA-Milter) * matching for client certificate´s CN (ExOTA-Milter)
* verification of DKIM signatures providing *Authentication-Results* header (another milter, e.g. OpenDKIM) * verification of DKIM signatures providing *Authentication-Results* header (another milter, e.g. OpenDKIM)
* consideration of DKIM verification results per sender domain (ExOTA-Milter) * consideration of DKIM verification results per sender domain (ExOTA-Milter)
* matching for tenant-id provided in *X-MS-Exchange-CrossTenant-Id* header (ExOTA-Milter) * *OPTIONAL* matching for tenant-id provided in *X-MS-Exchange-CrossTenant-Id* header (ExOTA-Milter)
![Activity policy](http://www.plantuml.com/plantuml/png/5SKn3W8W30NGg-W1f8cZcuEZSN4tM8aq5ahAhyhjZMzvM-ciyIZXkgd0c0SYpv_q5DIunopErb4w4biZhg9gWVsBJj_BzRWxYw8ujJp_POQy1UisJ8LN6j7q1m00) ![Activity policy](http://www.plantuml.com/plantuml/png/bPLHJzi-5CNVyoaERqKY_FzZuc7fmyYI8BhHTae3GffGPUBRriArkzXXyEsNrWsrIa3T8xw-zvppezUvC9PLjbxAm0eh2Tdpk8Z3eP2MAXWgwqhO5woq5EKBPbB_GR3f2A9X4QFKIb5fYVSH1D5LcaT8j9JDaL1pC2bHaQGdfYmMn3XLfXyeRGcIPZR2PQMN9uXhko1bHSciq2hCoTJIcXFSXSD9c3sN2wRc52QLDgOWrSmA1xnLowdKSoNCMiwGQXJ0zP89vUkWO8-aC6lKa5ycvv_FpaxNqbjFO8h_fwlNKcE06X7lnkcszkcq6ItH8_L4Kg_e6C9WD2vUKnurlhBnCCArrezhJ_MgPISK7bZP-E2-DNnZXZYqUbNVk7GPWa3CqFkvPQzhnRyUOyqAlHSonm6mhhl_LOJdy_-_e9IYyOvaX791vSO2AVOWwKqh4ErCxZNtDtNFPJQw_JKSN1TPFhrhRawB_6PIPEaq6Tq7WFDnkJO8MzN64jyR-5QSf27qb7P_0L6UVS-ImYa3nkgLkwUuc0N-_TsPdpxzCHWUaDa3kc3ciAvzLct4xj_jxeSEl4-n1HZV55UJBByTf0va1qfdCix3xU0kUpwT3fuUKPyfqC6GIJ5NIrBs42xTHCFTZ0zRWIXfDSJCjPgcazcw8WZZl13VXV1nEhkJT91YsO02_QwPXDNmDsdgDkae07p30-E5xRBNXRKJRGTU7t7tdBRULVLhVkNmLR3RHp9jtxT_UWhQTi4XrFRnwdSl-KwfdNZ0GZltJld3KDw9DymDSZUYd9RwgVtIt8Nks_NnMgvJhAhD2zyFV6gCy-sTVJnHp7aZSfX0FtXlvUCqdLvbSPhbzxjiRP9aF2e6evki7spoNJJ7zLB-1G00)
# How about using LDAP as policy backend? # How about using LDAP as policy backend?
For small setups, with not so many domains, the JSON-file policy backend (default) may be sufficient. If you´re an email service provider (ESP) maintaining a lot of customer domains in a LDAP server, you may want to use the LDAP backend instead. Details regarding the LDAP backend can be found [in the LDAP readme](LDAP/README.md). For small setups, with not so many domains, the JSON-file policy backend (default) may be sufficient. If you´re an email service provider (ESP) maintaining a lot of customer domains in a LDAP server, you may want to use the LDAP backend instead. Details regarding the LDAP backend can be found [in the LDAP readme](LDAP/README.md).
# How about a docker/OCI image? # How about a docker/OCI image?
## Using prebuilt images from [dockerhub](https://hub.docker.com/) ## Using prebuilt images from [dockerhub](https://hub.docker.com/)
* AMD64: https://hub.docker.com/r/chillout2k/exota-milter-amd64 * **OBSOLETE!** ~~AMD64: https://hub.docker.com/r/chillout2k/exota-milter-amd64~~
* ARM32v6: https://hub.docker.com/r/chillout2k/exota-milter-arm32v6 * **OBSOLETE!** ~~ARM32v6: https://hub.docker.com/r/chillout2k/exota-milter-arm32v6~~
* **NEW multi-architecture image:** https://hub.docker.com/r/chillout2k/exota-milter
The images are built on a weekly basis. The corresponding *Dockerfile* is located [here](OCI/Dockerfile) The images are built on a weekly basis. The corresponding *Dockerfile* is located [here](OCI/Dockerfile)
@ -146,4 +147,33 @@ Take a look [here](OCI/README.md)
First of all please take a look at how to set up the testing environment, which is described [here](tests/README.md) First of all please take a look at how to set up the testing environment, which is described [here](tests/README.md)
# How to install on docker/kubernetes/systemd? # How to install on docker/kubernetes/systemd?
The installation procedure is documented [here](INSTALL/README.md) The installation procedure is documented [here](INSTALL/README.md)
# How to *configure* the ExOTA-Milter?
|ENV variable|type|default|description|
|---|---|---|---|
|MILTER_NAME|`string`|`exota-milter`|Name of the milter instance. Base for socket path. Name appears in logs |
|MILTER_SOCKET|`string`|`/socket/<ENV[MITLER_NAME]>`|Defines the filesystem path of milter socket. The milter can be also exposed as a tcp-socket like `inet:4321@127.0.0.1`|
|MILTER_REJECT_MESSAGE|`string`|`Security policy violation!`|Milter reject (SMTP 5xx code) message presented to the calling MTA|
|MILTER_TMPFAIL_MESSAGE|`string`|`Service temporarily not available! Please try again later.`|Milter temporary fail (SMTP 4xx code) message presentetd to the calling MTA.|
|MILTER_TENANT_ID_REQUIRED|`bool`|`false`|Controls the requirement of the presence of the unofficial `X-MS-Exchange-CrossTenant-Id` header. Used as additional authentication factor.|
|MILTER_DKIM_ENABLED|`bool`|`false`|Enables/disables the checking of DKIM authentication results. Used as additional but strong authentication factor.|
|MILTER_DKIM_ALIGNMENT_REQUIRED|`bool`|`false`|Enables/disables the alighment checks of DKIM SDID with RFC-5322.from_domain. Requires ENV[MILTER_DKIM_ENABLED] = `true`|
|MILTER_TRUSTED_AUTHSERVID|`string`|`invalid`|Specifies the trusted DKIM-signature validating entity (DKIM-validator - producer of Authentication-Results header). The DKIM-validator must place exactly the same string as configured here into the Authentication-Results header! Requires ENV[MILTER_DKIM_ENABLED] = `true`|
|MILTER_POLICY_SOURCE|`string`|`file`|Policy source - Possible values `file` (JSON) or `ldap`|
|MILTER_POLICY_FILE|`string`|`/data/policy.json`|Filesystem path to the (JSON) policy file. Requires ENV[MILTER_POLICY_SOURCE] = `file`|
|MILTER_X509_ENABLED|`bool`|`false`|Enables/disables the checking of client x509-certificate. Used as additional authentication factor.|
|MILTER_X509_TRUSTED_CN|`string`|`mail.protection.outlook.com`|FQDN of authenticating client MTA. Requires ENV[MILTER_X509_ENABLED] = `true`|
|MILTER_X509_IP_WHITELIST|Whitespace or comma separated list of `string`|`127.0.0.1,::1`|List of IP-addresses for which the ExOTA-Milter skips x509 checks. Requires ENV[MILTER_X509_ENABLED] = `true`|
|MILTER_ADD_HEADER|`bool`|`false`|Controls if the ExOTA-Milter should write an additional `X-ExOTA-Authentication-Results` header with authentication information|
|MILTER_AUTHSERVID|`string`|empty|Provides ID of authenticating entity within `X-ExOTA-Authentication-Results` header to further validating instances. Required when ENV[MILTER_ADD_HEADER] = `true`|
|MILTER_LDAP_SERVER_URI|`string`|empty|LDAP-URI of LDAP server holding ExOTA policies. Required when ENV[MILTER_POLICY_SOURCE] = `ldap`|
|MILTER_LDAP_RECEIVE_TIMEOUT|`int`|5|Timespan the ExOTA-Milter waits for the LDAP server to respond to a request. This NOT the TCP-connect timeout! Requires ENV[MILTER_POLICY_SOURCE] = `ldap`|
|MILTER_LDAP_BINDDN|`string`|empty|Distinguished name of the binding (authenticating) *user*|
|MILTER_LDAP_BINDPW|`string`|empty|Password of the binding (authenticating) *user*|
|MILTER_LDAP_SEARCH_BASE|`string`|empty|Search base-DN on the LDAP server. Required when ENV[MILTER_POLICY_SOURCE] = `ldap`|
|MILTER_LDAP_QUERY|`string`|empty|LDAP query/filter used to match for a ExOTA-policy. A placeholder must be used to filter for the authenticating domain (`%d`), e.g. `(domain_attribute=%d)`|
|MILTER_LDAP_TENANT_ID_ATTR|`string`|`exotaMilterTenantId`|Custom LDAP attribute name unless using the ExOTA-milter LDAP schema|
|MILTER_LDAP_DKIM_ENABLED_ATTR|`string`|`exotaMilterDkimEnabled`|Custom LDAP attribute name unless using the ExOTA-milter LDAP schema|
|MILTER_LDAP_DKIM_ALIGNMENT_REQIRED_ATTR|`string`|`exotaMilterDkimAlignmentRequired`|Custom LDAP attribute name unless using the ExOTA-milter LDAP schema|

View File

@ -50,11 +50,14 @@ if (Policy found?) then (yes)
endif endif
else (no) else (no)
endif endif
:Looking up tenant-id in policy; if (Milter: tenant-ID header checking enabled?) then (yes)
if (Found trusted tenant-ID?) then (no) :Looking up tenant-id in policy;
:REJECT; if (Found trusted tenant-ID?) then (no)
stop :REJECT;
else (yes) stop
else (yes)
endif
else (no)
endif endif
else (no) else (no)
:REJECT; :REJECT;

View File

@ -25,6 +25,8 @@ g_milter_socket = '/socket/' + g_milter_name
g_milter_reject_message = 'Security policy violation!' g_milter_reject_message = 'Security policy violation!'
# ENV[MILTER_TMPFAIL_MESSAGE] # ENV[MILTER_TMPFAIL_MESSAGE]
g_milter_tmpfail_message = 'Service temporarily not available! Please try again later.' g_milter_tmpfail_message = 'Service temporarily not available! Please try again later.'
# ENV[MILTER_TENANT_ID_REQUIRED]
g_milter_tenant_id_required = False
# ENV[MILTER_DKIM_ENABLED] # ENV[MILTER_DKIM_ENABLED]
g_milter_dkim_enabled = False g_milter_dkim_enabled = False
# ENV[MILTER_DKIM_ALIGNMENT_REQUIRED] # ENV[MILTER_DKIM_ALIGNMENT_REQUIRED]
@ -163,12 +165,11 @@ class ExOTAMilter(Milter.Base):
return self.smfir_continue() return self.smfir_continue()
def header(self, name, hval): def header(self, name, hval):
log_debug(self.mconn_id + "/" + str(self.getsymval('i')) +
"/HDR: Header: {0}, Value: {1}".format(name, hval)
)
# Parse RFC-5322-From header # Parse RFC-5322-From header
if(name.lower() == "From".lower()): if(name.lower() == "from"):
log_debug(self.mconn_id + "/" + str(self.getsymval('i')) +
"/HDR: Header: {0}, Value: {1}".format(name, hval)
)
hdr_5322_from = email.utils.parseaddr(hval) hdr_5322_from = email.utils.parseaddr(hval)
self.hdr_from = hdr_5322_from[1].lower() self.hdr_from = hdr_5322_from[1].lower()
m = re.match(g_re_domain, self.hdr_from) m = re.match(g_re_domain, self.hdr_from)
@ -185,7 +186,10 @@ class ExOTAMilter(Milter.Base):
) )
# Parse RFC-5322-Resent-From header (Forwarded) # Parse RFC-5322-Resent-From header (Forwarded)
if(name.lower() == "Resent-From".lower()): if(name.lower() == "resent-from"):
log_debug(self.mconn_id + "/" + str(self.getsymval('i')) +
"/HDR: Header: {0}, Value: {1}".format(name, hval)
)
hdr_5322_resent_from = email.utils.parseaddr(hval) hdr_5322_resent_from = email.utils.parseaddr(hval)
self.hdr_resent_from = hdr_5322_resent_from[1].lower() self.hdr_resent_from = hdr_5322_resent_from[1].lower()
m = re.match(g_re_domain, self.hdr_resent_from) m = re.match(g_re_domain, self.hdr_resent_from)
@ -200,24 +204,37 @@ class ExOTAMilter(Milter.Base):
self.hdr_resent_from, self.hdr_resent_from_domain self.hdr_resent_from, self.hdr_resent_from_domain
) )
) )
# Parse non-standardized X-MS-Exchange-CrossTenant-Id header # Parse non-standardized X-MS-Exchange-CrossTenant-Id header
elif(name.lower() == "X-MS-Exchange-CrossTenant-Id".lower()): elif(name.lower() == "x-ms-exchange-crosstenant-id"):
log_debug(self.mconn_id + "/" + str(self.getsymval('i')) + log_debug(self.mconn_id + "/" + str(self.getsymval('i')) +
"/HDR: Tenant-ID: {0}".format(hval.lower()) "/HDR: Header: {0}, Value: {1}".format(name, hval)
) )
if self.hdr_tenant_id_count > 0: if g_milter_tenant_id_required == True:
if not self.hdr_tenant_id == hval.lower(): log_debug(self.mconn_id + "/" + str(self.getsymval('i')) +
self.hdr_different_tenant_id = True "/HDR: Tenant-ID: {0}".format(hval.lower())
log_info(self.mconn_id + "/" + str(self.getsymval('i')) +
"/HDR: Different Tenant-IDs found!"
) )
else: if self.hdr_tenant_id_count > 0:
self.hdr_tenant_id_count += 1 if not self.hdr_tenant_id == hval.lower():
self.hdr_tenant_id = hval.lower() self.hdr_different_tenant_id = True
log_info(self.mconn_id + "/" + str(self.getsymval('i')) +
"/HDR: Different Tenant-IDs found!"
)
else:
self.hdr_tenant_id_count += 1
self.hdr_tenant_id = hval.lower()
# Just for debugging cases
elif(name.lower() == "dkim-signature"):
log_debug(self.mconn_id + "/" + str(self.getsymval('i')) +
"/HDR: Header: {0}, Value: {1}".format(name, hval)
)
# Parse RFC-7601 Authentication-Results header # Parse RFC-7601 Authentication-Results header
elif(name.lower() == "Authentication-Results".lower()): elif(name.lower() == "authentication-results"):
log_debug(self.mconn_id + "/" + str(self.getsymval('i')) +
"/HDR: Header: {0}, Value: {1}".format(name, hval)
)
if g_milter_dkim_enabled == True: if g_milter_dkim_enabled == True:
ar = None ar = None
try: try:
@ -245,6 +262,9 @@ class ExOTAMilter(Milter.Base):
) )
elif(name == "X-ExOTA-Authentication-Results"): elif(name == "X-ExOTA-Authentication-Results"):
log_debug(self.mconn_id + "/" + str(self.getsymval('i')) +
"/HDR: Header: {0}, Value: {1}".format(name, hval)
)
log_debug(self.mconn_id + "/" + str(self.getsymval('i')) + log_debug(self.mconn_id + "/" + str(self.getsymval('i')) +
"/HDR: Found X-ExOTA-Authentication-Results header. Marking for deletion." "/HDR: Found X-ExOTA-Authentication-Results header. Marking for deletion."
) )
@ -300,16 +320,17 @@ class ExOTAMilter(Milter.Base):
reason = '5322.from header missing' reason = '5322.from header missing'
) )
if self.hdr_different_tenant_id == True: if g_milter_tenant_id_required == True:
log_info(self.mconn_id + "/" + str(self.getsymval('i')) + if self.hdr_different_tenant_id == True:
"/EOM: Multiple/different tenant-ID headers found for {0} - action=reject".format( log_info(self.mconn_id + "/" + str(self.getsymval('i')) +
self.hdr_from_domain "/EOM: Multiple/different tenant-ID headers found for {0} - action=reject".format(
self.hdr_from_domain
)
)
return self.smfir_reject(
queue_id = self.getsymval('i'),
reason = 'Multiple/different tenant-ID headers found!'
) )
)
return self.smfir_reject(
queue_id = self.getsymval('i'),
reason = 'Multiple/different tenant-ID headers found!'
)
# Get policy for 5322.from_domain # Get policy for 5322.from_domain
policy = None policy = None
@ -395,28 +416,29 @@ class ExOTAMilter(Milter.Base):
reason = "No policy for 5322.from_domain {0}".format(self.hdr_from_domain) reason = "No policy for 5322.from_domain {0}".format(self.hdr_from_domain)
) )
if self.hdr_tenant_id is None: if g_milter_tenant_id_required == True:
log_error(self.mconn_id + "/" + str(self.getsymval('i')) + if self.hdr_tenant_id is None:
"/EOM: exception: could not determine X-MS-Exchange-CrossTenant-Id - action=reject" log_error(self.mconn_id + "/" + str(self.getsymval('i')) +
) "/EOM: exception: could not determine X-MS-Exchange-CrossTenant-Id - action=reject"
return self.smfir_reject( )
queue_id = self.getsymval('i'), return self.smfir_reject(
reason = 'Tenant-ID is missing!' queue_id = self.getsymval('i'),
) reason = 'Tenant-ID is missing!'
if self.hdr_tenant_id == policy.get_tenant_id(): )
log_info(self.mconn_id + "/" + str(self.getsymval('i')) + if self.hdr_tenant_id == policy.get_tenant_id():
"/EOM: tenant_id={0} status=match".format(self.hdr_tenant_id) log_info(self.mconn_id + "/" + str(self.getsymval('i')) +
) "/EOM: tenant_id={0} status=match".format(self.hdr_tenant_id)
else: )
log_info(self.mconn_id + "/" + str(self.getsymval('i')) + else:
"/EOM: tenant_id={0} status=no_match - action=reject".format( log_info(self.mconn_id + "/" + str(self.getsymval('i')) +
self.hdr_tenant_id "/EOM: tenant_id={0} status=no_match - action=reject".format(
self.hdr_tenant_id
)
)
return self.smfir_reject(
queue_id = self.getsymval('i'),
reason = 'No policy match for tenant-id'
) )
)
return self.smfir_reject(
queue_id = self.getsymval('i'),
reason = 'No policy match for tenant-id'
)
if g_milter_dkim_enabled and policy.is_dkim_enabled(): if g_milter_dkim_enabled and policy.is_dkim_enabled():
log_debug(self.mconn_id + "/" + str(self.getsymval('i')) + log_debug(self.mconn_id + "/" + str(self.getsymval('i')) +
@ -529,6 +551,9 @@ if __name__ == "__main__":
if 'MILTER_TMPFAIL_MESSAGE' in os.environ: if 'MILTER_TMPFAIL_MESSAGE' in os.environ:
g_milter_tmpfail_message = os.environ['MILTER_TMPFAIL_MESSAGE'] g_milter_tmpfail_message = os.environ['MILTER_TMPFAIL_MESSAGE']
log_info("ENV[MILTER_TMPFAIL_MESSAGE]: {0}".format(g_milter_tmpfail_message)) log_info("ENV[MILTER_TMPFAIL_MESSAGE]: {0}".format(g_milter_tmpfail_message))
if 'MILTER_TENANT_ID_REQUIRED' in os.environ:
g_milter_tenant_id_required = True
log_info("ENV[MILTER_TENANT_ID_REQUIRED]: {0}".format(g_milter_tenant_id_required))
if 'MILTER_DKIM_ENABLED' in os.environ: if 'MILTER_DKIM_ENABLED' in os.environ:
g_milter_dkim_enabled = True g_milter_dkim_enabled = True
if 'MILTER_TRUSTED_AUTHSERVID' in os.environ: if 'MILTER_TRUSTED_AUTHSERVID' in os.environ:

View File

@ -14,10 +14,6 @@ def init_logger():
elif re.match(r'debug', os.environ['LOG_LEVEL'], re.IGNORECASE): elif re.match(r'debug', os.environ['LOG_LEVEL'], re.IGNORECASE):
log_level = logging.DEBUG log_level = logging.DEBUG
log_format = '%(asctime)s: %(levelname)s %(message)s ' log_format = '%(asctime)s: %(levelname)s %(message)s '
if log_level == logging.DEBUG:
# Log thread-ID too. This helps to correlate DEBUG logs,
# as Backend-logs do not have a queue_id nor a mconn_id!
log_format = '%(asctime)s: %(levelname)s %(thread)d %(message)s '
logging.basicConfig( logging.basicConfig(
filename = None, # log to stdout filename = None, # log to stdout
format = log_format, format = log_format,

View File

@ -25,7 +25,10 @@ class ExOTAPolicyBackendException(Exception):
class ExOTAPolicy(): class ExOTAPolicy():
def __init__(self, policy_dict): def __init__(self, policy_dict):
self.tenant_id = policy_dict['tenant_id'] if 'tenant_id' in policy_dict:
self.tenant_id = policy_dict['tenant_id']
else:
self.tenant_id = ''
if 'dkim_enabled' in policy_dict: if 'dkim_enabled' in policy_dict:
self.dkim_enabled = policy_dict['dkim_enabled'] self.dkim_enabled = policy_dict['dkim_enabled']
else: else:
@ -50,18 +53,11 @@ class ExOTAPolicy():
@staticmethod @staticmethod
def check_policy(policy_dict): def check_policy(policy_dict):
if 'tenant_id' not in policy_dict:
raise ExOTAPolicyInvalidException(
"Policy must have a 'tenant_id' key!"
)
if policy_dict['tenant_id'] is None:
raise ExOTAPolicyInvalidException(
"'tenant_id' needs a value!"
)
for policy_key in policy_dict: for policy_key in policy_dict:
if policy_key == 'tenant_id': if policy_key == 'tenant_id':
try: try:
UUID(policy_dict[policy_key]) if policy_dict[policy_key] != '':
UUID(policy_dict[policy_key])
except ValueError as e: except ValueError as e:
raise ExOTAPolicyInvalidException( raise ExOTAPolicyInvalidException(
"Invalid 'tenant_id': {0}".format(str(e)) "Invalid 'tenant_id': {0}".format(str(e))
@ -200,7 +196,10 @@ class ExOTAPolicyBackendLDAP(ExOTAPolicyBackend):
entry = response[0]['attributes'] entry = response[0]['attributes']
policy_dict = {} policy_dict = {}
if self.tenant_id_attr in entry: if self.tenant_id_attr in entry:
policy_dict['tenant_id'] = entry[self.tenant_id_attr][0] if len(entry[self.tenant_id_attr]) > 0:
policy_dict['tenant_id'] = entry[self.tenant_id_attr][0]
else:
policy_dict['tenant_id'] = ''
if self.dkim_enabled_attr in entry: if self.dkim_enabled_attr in entry:
if entry[self.dkim_enabled_attr][0] == 'TRUE': if entry[self.dkim_enabled_attr][0] == 'TRUE':
policy_dict['dkim_enabled'] = True policy_dict['dkim_enabled'] = True

View File

@ -1,64 +0,0 @@
Return-Path: <>
Received: from DEU01-FR2-obe.outbound.protection.outlook.com (mail-fr2deu01lp2173.outbound.protection.outlook.com [104.47.11.173])
(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))
(Client CN "mail.protection.outlook.com", Issuer "GlobalSign Organization Validation CA - SHA256 - G3" (verified OK))
by outbound.connector.blahblubb.de (Postfix) with ESMTPS id 4CjqCQ2WRCzGjg6
for <some.recipient@example.org>; Sat, 28 Nov 2020 12:34:26 +0100 (CET)
ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;
b=RuGSfIN1OzQHDqrF0erLAHZ3fyhtmoE5Sllj+Qp6CtbcNUkkmdhR44b8capz/J1mBpyb13udY1mhkPZCK1Cmt+mpg9yFXgkv5BxY+dV9647Fq+MboUE60Psn84d4vXFvyrWDrFW1jWZi7/NdXhjLcCqTHpAzDaRfAOfGhG/VWYJAXnD/EBpCzPfd8hh9ZOONI2UN2HQfRnx0P3WXyeVSGilP4RGPdmcCZV5ZzpjlQoKUshjq293+ZltXaeKfF/LHGX0yScHhKO2f9O+qY3hnH0P+NGwFvhIky3IyszfxpANaJnz2Jpp0sK1W16rwGSTI2gl9bpJsj+wzKLGkJV75+Q==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;
s=arcselector9901;
h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;
bh=KWHUKEKZxeQyFbKe45TV1PJMC2XrVCMTFkdwSYWR6o0=;
b=go8dFv6srV3NnETxQxaANld1if9BOsIgrhjefC4WkRrrgwEjZSNnm9DyO+GC2ZZo60At5JHOVLjqN9kjz2pFdAG0qnFEj3Wx/6NnuTfBUk0n4s32RoFuhADu8BC+aOU9Ec909uu2QQ9ucEMiVSjuyQ3QpGS5DR0yCAZLZ12B61hmoMgkXJ9ah6rluUV4GeMGKTsUn16u6mrJycXp0OoD4n19JomPpQo5o8gouK3Zz4F7DxX4lshNJ+VCsOznqS+FI4rQ2LSyU8Y0AZa9clyCSN94AJa6K0TiDgQ/gLZEWsZ1tZkgPrdMlyqi58ONW/dNQ7lyrEFz6deB4YmsusJPbQ==
ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none
action=none header.from=lalalulu.onmicrosoft.com; dkim=none (message not
signed); arc=none
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=lalalulu.onmicrosoft.com; s=selector1-lalalulu-onmicrosoft-com;
h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;
bh=KWHUKEKZxeQyFbKe45TV1PJMC2XrVCMTFkdwSYWR6o0=;
b=DYTLJtLFjvVrSZtZQagTwuEe5PQYqrNGi7hR5bkhO+GYUV4dcQZnDO4hAPzJkOWhz8JCVJ+/yt5K8L/exegk80g9m0GJjZzJBxMy0ZE/7wg8yqiHNE+iQqWhJLtwsD23kx2+09G5dBSDI1QVqFKkL0YKBWVffSuXi+tjM4/BztffZ7ok7XZdKCFfKzK3TLdiAWYTRIp1214zdnIE0CLBhnOIWC4gnML2fXsVZsWb/CMgaW0vBsZGI/yaSivaNFPZloSb0/sEnMFMEbv2GXt9mN913M0thwCi/+NLwzaW6TNlw2Vz7l4SGRVvciGaa4s2sFnJ0ANMD2u5qBbJ8j8Z0w==
Authentication-Results: blahblubb.de; dkim=none (message not signed)
header.d=none;blahblubb.de; dmarc=none action=none
header.from=lalalulu.onmicrosoft.com;
Received: from AM6P193CA0087.EURP193.PROD.OUTLOOK.COM (2603:10a6:209:88::28)
by BEXP281MB0216.DEUP281.PROD.OUTLOOK.COM (2603:10a6:b10:6::12) with
Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3632.6; Sat, 28 Nov
2020 11:34:25 +0000
Received: from BE0P281MB0257.DEUP281.PROD.OUTLOOK.COM
(2603:10a6:209:88:cafe::a2) by AM6P193CA0087.outlook.office365.com
(2603:10a6:209:88::28) with Microsoft SMTP Server (version=TLS1_2,
cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.3611.20 via Frontend
Transport; Sat, 28 Nov 2020 11:34:24 +0000
From: O365ConnectorValidation@lalalulu.onmicrosoft.com
Date: Sat, 28 Nov 2020 11:34:24 +0000
Message-Id: <b6d9c673-d0f3-4538-bb4e-9e099fb9a388@substrate-int.office.com>
To: some.recipient@example.org
Subject: Test email for connector validation
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
X-MS-PublicTrafficType: Email
X-MS-Office365-Filtering-Correlation-Id: abcd1234-abcd-471a-1234-08d893918edd
X-MS-TrafficTypeDiagnostic: BEXP281MB0216:
X-Microsoft-Antispam-PRVS:
<BEXP281MB021624EF3E3FC35524889C2AB8F70@BEXP281MB0216.DEUP281.PROD.OUTLOOK.COM>
X-MS-Oob-TLC-OOBClassifiers: OLM:2733;
X-MS-Exchange-SenderADCheck: 1
X-Microsoft-Antispam: BCL:0;
X-Microsoft-Antispam-Message-Info:
P2dut4iALZ4EsHFmDE6p0OBg/Q4PvbmhUGI6BnGbHo/u7Vza6tyXE6BPK0VrJQ8WnCYXNx7lEKtiZs8nakJ9EghgxvFRNuYyRBJcGAdlN2TJAb2/7Wp5m7vzuGp1JJhES0RC/hypLDL8miRoP1xYl/pQHZVUGczSddujsZT6im0EgDJvAB0L1vzyKvZJ1QH3vTWDKMAgetlQHiPvCfzZmUgY92g1+sfF9UwGTRXDj8cd83H+TLI7GL8kZF1H219l+DLDiZ3u+qUdprwMn9XDEBljZpczY8BhiFdmnbyJ26ePVNa5JluRboz2Gfaa6GZE+ar8FyKtepxFOyNlI+hyL/vcWNwmnjL+pyYFVPPHnODjxu8JixWg00ThTUiZbclJ
X-Forefront-Antispam-Report:
CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:BE0P281MB0257.DEUP281.PROD.OUTLOOK.COM;PTR:;CAT:NONE;SFS:(376002)(346002)(39830400003)(34036004)(366004)(396003)(136003)(31686004)(78352004)(6916009)(508600001)(5660300002)(42882007)(2906002)(8936002)(558084003)(31696002)(17440700003)(316002)(9686003)(85236043)(68406010)(8676002)(83380400001)(16130700016)(100380200003)(20230700015);DIR:OUT;SFP:1501;
X-OriginatorOrg: lalalulu.onmicrosoft.com
X-MS-Exchange-CrossTenant-OriginalArrivalTime: 28 Nov 2020 11:34:24.7460
(UTC)
X-MS-Exchange-CrossTenant-Network-Message-Id: abcd1234-abcd-471a-1234-08d893918edd
X-MS-Exchange-CrossTenant-AuthSource: AM6P193CA0087.EURP193.PROD.OUTLOOK.COM
X-MS-Exchange-CrossTenant-AuthAs: Internal
X-MS-Exchange-CrossTenant-Id: 1234abcd-18c5-45e8-88de-123456789abc
X-MS-Exchange-CrossTenant-FromEntityHeader: Internet
X-MS-Exchange-Transport-CrossTenantHeadersStamped: BEXP281MB0216
This test email message was sent from Office 365 to check that email can be delivered to you using your new or modified connector. No need to reply.

View File

@ -1,2 +0,0 @@
From: from2@example.com
From: from1@example.org

View File

@ -1,16 +0,0 @@
import sys
import email, email.header
from email.utils import getaddresses
f = open("../samples/exo_validator.eml", "r")
email = email.message_from_file(f)
from_hdr = email.get_all("From")
print("from_hdr: " + str(from_hdr))
if(len(from_hdr) > 1):
print("Multiple From-headers found!")
sys.exit(1)
elif(len(from_hdr) == 1):
print("Exactly one From-header found :)")
print(from_hdr)
from_addr = getaddresses(from_hdr)
print(str(from_addr[0][1]))

View File

@ -33,41 +33,49 @@ end
--if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@yad.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, "fRoM", '"Blah Blubb" <O365ConnectorValidation@chillout2k.de>') ~= nil then if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@staging.zwackl.de>') ~= nil then
error "mt.header(From) failed" error "mt.header(From) failed"
end end
if mt.header(conn, "resent-fRoM", '"Blah Blubb" <blah@yad.onmicrosoft.COM>') ~= nil then if mt.header(conn, "resent-fRoM", '"Blah Blubb" <blah@yad.onmicrosoft.COM>') ~= nil then
error "mt.header(From) failed" error "mt.header(Resent-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
error "mt.header(Subject) failed" error "mt.header(X-MS-Exchange-CrossTenant-Id) failed"
end
dkim_sig = "v=1; a=rsa-sha256; c=relaxed/simple; d=staging.zwackl.de;\n"
.."\ts=selector-xyz; t=1685872089;\n"
.."\tbh=5/ZUJAdcuyAn6J+J6apWtAaJLbDCKkI5Ie31qVKiY0w=;\n"
.."\th=Date:From:To:Subject:MIME-Version:Content-Type;\n"
.."\tb=Bn/xAbFFjAg1b9bBFPHAYSaupsnL4pzPPDUauetfGB0hu0Qz0Dio+4Z2Vi6PMOesA\n"
.."\t72VbehuxG+b++XVL/hs3+K6p7vTgVAWiWAZLvfs5bHE5HAalsCrNenpKTk6RUcSYtw\n"
.."\tLiiYhvw0TR5LbyNoSPG2J16mXEcS+k2q+K7WfwMg="
if mt.header(conn, "DKIM-Signature", dkim_sig) ~= nil then
error "mt.header(DKIM-Signature) failed"
end end
--if mt.header(conn, "X-MS-Exchange-CrossTenant-Id", "4321abcd-18c5-45e8-88de-blahblubb") ~= nil then
-- error "mt.header(Subject) failed"
--end
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 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(Authentication-Results) failed"
end end
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 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(Authentication-Results) failed"
end end
if mt.header(conn, "Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= 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(Authentication-Results) failed"
end end
if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=yad.onmicrosoft.comx header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=yad.onmicrosoft.comx header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
error "mt.header(Subject) failed" error "mt.header(Authentication-Results) failed"
end end
if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=Chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=staging.zwackl.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
error "mt.header(Subject) failed" error "mt.header(Authentication-Results) failed"
end 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 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(Authentication-Results) 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
error "mt.header(Subject) failed" error "mt.header(Authentication-Results) failed"
end end
if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then
error "mt.header(Subject) failed" error "mt.header(X-ExOTA-Authentication-Results) failed"
end end
-- EOM -- EOM

View File

@ -33,7 +33,7 @@ end
--if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@yad.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, "fRoM", '"Blah Blubb" <O365ConnectorValidation@chillout2k.de>') ~= nil then if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@staging.zwackl.de>') ~= nil then
error "mt.header(From) failed" error "mt.header(From) failed"
end end
if mt.header(conn, "resent-fRoM", '"Blah Blubb" <blah@yad.onmicrosoft.COM>') ~= nil then if mt.header(conn, "resent-fRoM", '"Blah Blubb" <blah@yad.onmicrosoft.COM>') ~= nil then
@ -57,7 +57,7 @@ end
if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=yad.onmicrosoft.comx header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=yad.onmicrosoft.comx 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=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=staging.zwackl.de 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=fail header.d=yad.onmicrosoft.com header.s=selector2-asdf header.b=mmmjFpv8") ~= nil then 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
@ -107,13 +107,13 @@ if mt.getreply(conn) ~= SMFIR_CONTINUE then
end end
-- HEADER -- HEADER
if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@chillout2k.de>') ~= nil then if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@staging.zwackl.de>') ~= 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
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=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=staging.zwackl.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
error "mt.header(Subject) failed" error "mt.header(Subject) failed"
end end

View File

@ -31,17 +31,17 @@ if mt.getreply(conn) ~= SMFIR_CONTINUE then
end end
-- HEADER -- HEADER
if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@chillout2k.de>') ~= nil then if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@staging.zwackl.de>') ~= 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-123456789abcXXX") ~= nil then if mt.header(conn, "x-mS-EXCHANGE-crosstenant-id", "1234abcd-18c5-45e8-88de-123456789abcXXX") ~= nil then
error "mt.header(Subject) failed" error "mt.header(x-mS-EXCHANGE-crosstenant-id) failed"
end end
if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=fail header.d=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=fail header.d=staging.zwackl.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
error "mt.header(Subject) failed" error "mt.header(Authentication-RESULTS) failed"
end end
if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then
error "mt.header(Subject) failed" error "mt.header(X-ExOTA-Authentication-Results) failed"
end end
-- EOM -- EOM
@ -81,14 +81,14 @@ if mt.getreply(conn) ~= SMFIR_CONTINUE then
end end
-- HEADER -- HEADER
if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@chillout2k.de>') ~= nil then if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@staging.zwackl.de>') ~= 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
error "mt.header(Subject) failed" error "mt.header(x-mS-EXCHANGE-crosstenant-id) failed"
end end
if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=staging.zwackl.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
error "mt.header(Subject) failed" error "mt.header(Authentication-RESULTS) failed"
end end
-- EOM -- EOM
@ -127,17 +127,17 @@ if mt.getreply(conn) ~= SMFIR_CONTINUE then
end end
-- HEADER -- HEADER
if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@chillout2k.de>') ~= nil then if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@staging.zwackl.de>') ~= 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-123456789abcXXX") ~= nil then if mt.header(conn, "x-mS-EXCHANGE-crosstenant-id", "1234abcd-18c5-45e8-88de-123456789abcXXX") ~= nil then
error "mt.header(Subject) failed" error "mt.header(x-mS-EXCHANGE-crosstenant-id) failed"
end end
if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=fail header.d=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=fail header.d=staging.zwackl.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
error "mt.header(Subject) failed" error "mt.header(Authentication-RESULTS) failed"
end end
if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then
error "mt.header(Subject) failed" error "mt.header(X-ExOTA-Authentication-Results) failed"
end end
-- EOM -- EOM

View File

@ -30,7 +30,7 @@ if mt.getreply(conn) ~= SMFIR_CONTINUE then
end end
-- HEADER -- HEADER
if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@chillout2kx.de>') ~= nil then if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@staging.zwacklx.de>') ~= 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

View File

@ -30,7 +30,7 @@ if mt.getreply(conn) ~= SMFIR_CONTINUE then
end end
-- HEADER -- HEADER
if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@chillout2k.de>') ~= nil then if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@staging.zwackl.de>') ~= 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
@ -42,7 +42,7 @@ 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(tenant-id fail) failed" error "mt.header(tenant-id fail) failed"
end end
if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=chillout2k.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=staging.zwackl.de header.s=selector1-yad-onmicrosoft-com header.b=mmmjFpv8") ~= nil then
error "mt.header(DKIM-AR) failed" error "mt.header(DKIM-AR) failed"
end end
if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then

View File

@ -0,0 +1,82 @@
-- https://mopano.github.io/sendmail-filter-api/constant-values.html#com.sendmail.milter.MilterConstants
-- http://www.opendkim.org/miltertest.8.html
-- socket must be defined as miltertest global variable (-D)
conn = mt.connect(socket)
if conn == nil then
error "mt.connect() failed"
end
if mt.conninfo(conn, "localhost", "::1") ~= nil then
error "mt.conninfo() failed"
end
mt.set_timeout(60)
-- 5321.FROM
if mt.mailfrom(conn, "envelope.sender@example.org") ~= nil then
error "mt.mailfrom() failed"
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
error "mt.mailfrom() unexpected reply"
end
-- 5321.RCPT+MACROS
mt.macro(conn, SMFIC_RCPT, "i", "4CgSNs5Q9sz7SllQ", '{cert_subject}', "mail.protection.outlook.comx")
if mt.rcptto(conn, "<envelope.recipient@example.com>") ~= nil then
error "mt.rcptto() failed"
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
error "mt.rcptto() unexpected reply"
end
-- HEADER
if mt.header(conn, "fRoM", '"Blah Blubb" <O365ConnectorValidation@staging.zwackl.de>') ~= nil then
error "mt.header(From) failed"
end
if mt.header(conn, "resent-fRoM", '"Blah Blubb" <blah@yad.onmicrosoft.COM>') ~= nil then
error "mt.header(From) failed"
end
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"
end
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"
end
if mt.header(conn, "Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then
error "mt.header(Subject) failed"
end
if mt.header(conn, "Authentication-RESULTS", "my-auth-serv-id;\n dkim=pass header.d=yad.onmicrosoft.comx 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=pass header.d=staging.zwackl.de 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"
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
error "mt.header(Subject) failed"
end
if mt.header(conn, "X-ExOTA-Authentication-Results", "my-auth-serv-id;\n exota=pass") ~= nil then
error "mt.header(Subject) failed"
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-ExOTA-Authentication-Results") then
mt.echo("no header added")
else
mt.echo("X-ExOTA-Authentication-Results header added")
end
-- DISCONNECT
mt.disconnect(conn)

View File

@ -7,5 +7,10 @@
"example.com": { "example.com": {
"tenant_id": "abcd1234-18c5-45e8-88de-987654321cba", "tenant_id": "abcd1234-18c5-45e8-88de-987654321cba",
"dkim_enabled": false "dkim_enabled": false
},
"staging.zwackl.de": {
"tenant_id": "",
"dkim_enabled": true,
"dkim_alignment_required": true
} }
} }