mirror of
https://github.com/bokysan/docker-postfix.git
synced 2024-09-20 06:46:10 +08:00
Upd: Add new integration and unit tests
Test a few more functionalities in the image. Change how postfix message IDs are detected. Message ID can be set by the client and hence detecting them via regex is not the best way to go around it. This fix will actually look at the log line and try to determine if we're looking at the message ID or not.
This commit is contained in:
parent
81a600db10
commit
c7c56d3ff1
53
integration-tests/relay-password/docker-compose.yml
Normal file
53
integration-tests/relay-password/docker-compose.yml
Normal file
|
@ -0,0 +1,53 @@
|
|||
version: '3.7'
|
||||
services:
|
||||
postfix_test_587:
|
||||
hostname: "postfix"
|
||||
image: "boky/postfix"
|
||||
build:
|
||||
context: ../..
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: [ "CMD", "sh", "-c", "netstat -an | fgrep 587 | fgrep -q LISTEN" ]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
start_period: 10s
|
||||
retries: 2
|
||||
environment:
|
||||
FORCE_COLOR: "1"
|
||||
ANONYMIZE_EMAILS: smart
|
||||
ALLOWED_SENDER_DOMAINS: "mydomain.example.com"
|
||||
MASQUERADED_DOMAINS: "mydomain.example.com"
|
||||
RELAYHOST: "smtp.gmail.com:587"
|
||||
RELAYHOST_USERNAME: "test@gmail.com"
|
||||
RELAYHOST_PASSWORD: "test12345"
|
||||
|
||||
POSTFIX_myhostname: "mailinator"
|
||||
POSTFIX_mydomain: "target.example.com"
|
||||
POSTFIX_mydestination: "mailinator localhost.target.example.com localhost target.example.com"
|
||||
POSTFIX_luser_relay: "root+@target.example.com"
|
||||
POSTFIX_smtpd_end_of_data_restrictions: "check_client_access static:discard"
|
||||
POSTFIX_smtp_sender_dependent_authentication: "yes"
|
||||
POSTFIX_smtp_sasl_auth_enable: "yes"
|
||||
POSTFIX_smtp_sasl_security_options: "noanonymous"
|
||||
POSTFIX_smtp_sasl_mechanism_filter: "login, plain, digest-md5"
|
||||
POSTFIX_smtpd_relay_restrictions: "permit_sasl_authenticated, reject_unauth_destination"
|
||||
POSTFIX_smtpd_sasl_auth_enable: "yes"
|
||||
POSTFIX_smtpd_sasl_authenticated_header: "yes"
|
||||
POSTFIX_smtpd_sasl_security_options: "noanonymous"
|
||||
POSTFIX_smtpd_sasl_local_domain: "$myhostname"
|
||||
POSTFIX_broken_sasl_auth_clients: "yes"
|
||||
POSTFIX_smtpd_client_restrictions: ""
|
||||
POSTFIX_message_size_limit: "40960000"
|
||||
LOG_FORMAT: "json"
|
||||
tests:
|
||||
image: "boky/postfix-integration-test"
|
||||
restart: "no"
|
||||
volumes:
|
||||
- "../tester:/code"
|
||||
build:
|
||||
context: ../tester
|
||||
command: "/" # relative path to /code
|
||||
environment:
|
||||
FROM: "demo@mydomain.example.com"
|
||||
TO: "root@target.example.com"
|
||||
SKIP_INVALID_DOMAIN_SEND: "1"
|
|
@ -1,7 +1,12 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
if [[ "$#" -gt 0 ]]; then
|
||||
FROM=$1
|
||||
fi
|
||||
|
||||
if [[ "$#" -gt 1 ]]; then
|
||||
TO=$2
|
||||
fi
|
||||
|
||||
if [ -z "$FROM" ]; then
|
||||
FROM="demo@example.org"
|
||||
|
|
|
@ -37,11 +37,6 @@ EMAIL_CATCH_ALL_PATTERN = '([^ "\\[\\]<]+|".+")@(\[([0-9]+\.[0-9]+\.[0-9]+\.[0-9
|
|||
EMAIL_CATCH_ALL = re.compile(EMAIL_CATCH_ALL_PATTERN)
|
||||
EMPTY_RESPONSE = json.dumps({})
|
||||
|
||||
# Postfix formats message IDs like this: 20211207101128.0805BA272@31bfa77a2cab
|
||||
# Let's not mask them.
|
||||
MESSAGE_ID_PATTERN = '[0-9]+\.[0-9A-F]+@[0-9a-f]+'
|
||||
MESSAGE_ID = re.compile(MESSAGE_ID_PATTERN)
|
||||
|
||||
"""A default filter, if none other is provided."""
|
||||
DEFAULT_FILTER_CLASS: str = 'SmartFilter'
|
||||
|
||||
|
@ -80,15 +75,46 @@ def is_truthy(val: any, name: str) -> bool:
|
|||
Abstract base for all filters. Does nothing.
|
||||
"""
|
||||
class Filter():
|
||||
MESSAGE_ID_LINE = "message-id="
|
||||
MESSAGE_ID_LINE_LEN = len(MESSAGE_ID_LINE)
|
||||
|
||||
def init(self, args: 'dict[str, list[str]]') -> None:
|
||||
pass
|
||||
|
||||
def replace(self, match: re.match) -> str:
|
||||
def is_message_id(self, match: re.match, msg: str) -> bool:
|
||||
start = match.start()
|
||||
email = match.group()
|
||||
|
||||
# Note that our regex will match thigs like "message-id=Issue1649523226559@postfix-mail.mail-system.svc.cluster.local"
|
||||
# so we need to filter / check for these first
|
||||
|
||||
if email.startswith(self.MESSAGE_ID_LINE):
|
||||
return True
|
||||
|
||||
if start >= self.MESSAGE_ID_LINE_LEN:
|
||||
pos = start-1
|
||||
while True:
|
||||
char = msg[pos]
|
||||
if char == '=':
|
||||
break
|
||||
elif char in '{<["\'':
|
||||
pos = pos - 1
|
||||
continue
|
||||
|
||||
return False
|
||||
|
||||
check = msg[pos-self.MESSAGE_ID_LINE_LEN+1:pos+1]
|
||||
if check == self.MESSAGE_ID_LINE:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def replace(self, match: re.match, msg: str) -> str:
|
||||
return match.group()
|
||||
|
||||
def processMessage(self, msg: str) -> typing.Optional[str]:
|
||||
result = EMAIL_CATCH_ALL.sub(
|
||||
lambda x: self.replace(x), msg
|
||||
lambda x: self.replace(x, msg), msg
|
||||
)
|
||||
return json.dumps({'msg': result}, ensure_ascii=False) if result != msg else EMPTY_RESPONSE
|
||||
|
||||
|
@ -144,11 +170,11 @@ class SmartFilter(Filter):
|
|||
else: # Local domain
|
||||
return len(domain) * self.mask_symbol
|
||||
|
||||
def replace(self, match: re.match) -> str:
|
||||
def replace(self, match: re.match, msg: str) -> str:
|
||||
email = match.group()
|
||||
|
||||
# Return the details unchanged if they look like Postfix message ID
|
||||
if bool(MESSAGE_ID.match(email)):
|
||||
if self.is_message_id(match, msg):
|
||||
return email
|
||||
|
||||
# The "@" can show up in the local part, but shouldn't appear in the
|
||||
|
@ -237,11 +263,11 @@ class HashFilter(Filter):
|
|||
|
||||
super().init(args)
|
||||
|
||||
def replace(self, match: re.match) -> str:
|
||||
def replace(self, match: re.match, msg: str) -> str:
|
||||
email = match.group()
|
||||
|
||||
# Return the details unchanged if they look like Postfix message ID
|
||||
if bool(MESSAGE_ID.match(email)):
|
||||
if self.is_message_id(match, msg):
|
||||
return email
|
||||
|
||||
if not self.case_sensitive:
|
||||
|
|
51
unit-tests/email-anonymizer-regexs.bats
Normal file
51
unit-tests/email-anonymizer-regexs.bats
Normal file
|
@ -0,0 +1,51 @@
|
|||
#!/usr/bin/env bats
|
||||
|
||||
mapfile EMAILS <<'EOF'
|
||||
prettyandsimple@example.com
|
||||
9453312: message-id=<1649523226559@test.example-org>
|
||||
2F92E13: message-id=<8b38c47e1dd21675a07cf3bb674db074@example.com>
|
||||
62D2E12: to=<demo@example.com>, relay=smtp.sendgrid.net[54.228.39.88]:587, delay=0.94, delays=0.1\/0.11\/0.61\/0.12, dsn=2.0.0, status=sent (250 Ok: queued as 5wukd4NoS6GaNrC3ggB83A)
|
||||
77FCF13: message-id=<Issue1649425486405@postfix-mail.mail-system.svc.cluster.local> from=<test1@demo1.example.com> to=<test2@demo2.example.com>
|
||||
77FCF13: message-id=Issue1649425486405@postfix-mail.mail-system.svc.cluster.local from=test1@demo1.example.com to=test2@demo2.example.com
|
||||
9453312: message-id=<Issue1649523226559@postfix-mail.mail-system.svc.cluster.local>
|
||||
9453312: message-id="Issue1649523226559@postfix-mail.mail-system.svc.cluster.local"
|
||||
message-id=<Issue1649523226559@postfix-mail.mail-system.svc.cluster.local>
|
||||
message-id=Issue1649523226559@postfix-mail.mail-system.svc.cluster.local
|
||||
message-id=Issue1649523226559@postfix-mail.mail-system.svc.cluster.local
|
||||
9453312: message-id='Issue1649523226559@postfix-mail.mail-system.svc.cluster.local'
|
||||
9453312: message-id=Issue1649523226559@postfix-mail.mail-system.svc.cluster.local
|
||||
EOF
|
||||
|
||||
mapfile EXPECTED <<'EOF'
|
||||
{"msg": "*@*.com"}
|
||||
{}
|
||||
{}
|
||||
{"msg": "62D2E12: to=<*@*.com>, relay=smtp.sendgrid.net[54.228.39.88]:587, delay=0.94, delays=0.1\\/0.11\\/0.61\\/0.12, dsn=2.0.0, status=sent (250 Ok: queued as 5wukd4NoS6GaNrC3ggB83A)"}
|
||||
{"msg": "77FCF13: message-id=<Issue1649425486405@postfix-mail.mail-system.svc.cluster.local> from=<*@*.com> to=<*@*.com>"}
|
||||
{"msg": "77FCF13: message-id=Issue1649425486405@postfix-mail.mail-system.svc.cluster.local *@*.com *@*.com"}
|
||||
{}
|
||||
{}
|
||||
{}
|
||||
{}
|
||||
{}
|
||||
{}
|
||||
{}
|
||||
EOF
|
||||
|
||||
|
||||
@test "verify email anonymizer regex" {
|
||||
local email
|
||||
for index in "${!EMAILS[@]}"; do
|
||||
email="${EMAILS[$index]}"
|
||||
email=${email%$'\n'} # Remove trailing new line
|
||||
result="$(echo "$email" | /code/scripts/email-anonymizer.sh paranoid)"
|
||||
result=${result%$'\n'} # Remove trailing new line
|
||||
expected="${EXPECTED[$index]}"
|
||||
expected=${expected%$'\n'} # Remove trailing new line
|
||||
if [ "$result" != "$expected" ]; then
|
||||
echo "Expected '$expected', got: '$result'" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
Loading…
Reference in a new issue