Compare commits

...

5 Commits

Author SHA1 Message Date
link2xt
569a043065 Add execnet dependency 2025-12-06 17:18:24 +00:00
j4n
166bf68915 Remove DKIM-Signature from incoming mail after checking (#747)
The original https://github.com/chatmail/relay/pull/533 attempted to remove the header through postfix, but that is too early. Instead, remove the headers in the OpenDKIM `final.lua` script after the validation.
2025-12-04 12:23:27 +01:00
Maikel Frias Mosquea
96108bbaba fix: cmdeploy webdev now works as intended
Before: cmdeploy webdev just kept running non-stop regeneration of the
files with this it truly stop unless there's an actual change.
2025-11-25 22:26:47 +01:00
Mark Felder
8f68672e31 FreeBSD/pf example: fix small inconsistency
harmless, but better to be consistent
2025-11-21 10:02:44 +01:00
Mark Felder
9e6e3af534 Proxy example for FreeBSD/pf 2025-11-20 17:03:31 +01:00
5 changed files with 100 additions and 26 deletions

View File

@@ -73,5 +73,6 @@ commands =
deps = pytest deps = pytest
pdbpp pdbpp
pytest-localserver pytest-localserver
execnet
commands = pytest -v -rsXx {posargs} commands = pytest -v -rsXx {posargs}
""" """

View File

@@ -9,9 +9,10 @@ if nsigs == nil then
return nil return nil
end end
local valid = false
for i = 1, nsigs do for i = 1, nsigs do
sig = odkim.get_sighandle(ctx, i - 1) sig = odkim.get_sighandle(ctx, i - 1)
sigres = odkim.sig_result(sig) sigres = odkim.sig_result(sig)
-- All signatures that do not correspond to From: -- All signatures that do not correspond to From:
-- were ignored in screen.lua and return sigres -1. -- were ignored in screen.lua and return sigres -1.
@@ -19,10 +20,19 @@ for i = 1, nsigs do
-- Any valid signature that was not ignored like this -- Any valid signature that was not ignored like this
-- means the message is acceptable. -- means the message is acceptable.
if sigres == 0 then if sigres == 0 then
return nil valid = true
end end
end end
odkim.set_reply(ctx, "554", "5.7.1", "No valid DKIM signature found") if valid then
odkim.set_result(ctx, SMFIS_REJECT) -- Strip all DKIM-Signature headers after successful validation
-- Delete in reverse order to avoid index shifting.
for i = nsigs, 1, -1 do
odkim.del_header(ctx, "DKIM-Signature", i)
end
else
odkim.set_reply(ctx, "554", "5.7.1", "No valid DKIM signature found")
odkim.set_result(ctx, SMFIS_REJECT)
end
return nil return nil

View File

@@ -140,34 +140,34 @@ def main():
config.webdev = True config.webdev = True
assert config.mail_domain assert config.mail_domain
# start web page generation, open a browser and wait for changes
www_path, src_path, build_dir = get_paths(config) www_path, src_path, build_dir = get_paths(config)
build_dir = build_webpages(src_path, build_dir, config) build_dir = build_webpages(src_path, build_dir, config)
index_path = build_dir.joinpath("index.html") index_path = build_dir.joinpath("index.html")
webbrowser.open(str(index_path)) webbrowser.open(str(index_path))
stats = snapshot_dir_stats(src_path)
print(f"\nOpened URL: file://{index_path.resolve()}\n") print(f"\nOpened URL: file://{index_path.resolve()}\n")
print(f"watching {src_path} directory for changes") print(f"Watching {src_path} directory for changes...")
stats = snapshot_dir_stats(src_path)
changenum = 0 changenum = 0
count = 0 debounce_time = 0.5 # wait 0.5s after detecting a change
while True: while True:
time.sleep(1)
newstats = snapshot_dir_stats(src_path) newstats = snapshot_dir_stats(src_path)
if newstats == stats and count % 60 != 0:
count += 1
time.sleep(1.0)
continue
for key in newstats: if newstats != stats:
if stats[key] != newstats[key]: changed_files = [f for f in newstats if stats.get(f) != newstats[f]]
print(f"*** CHANGED: {key}") for f in changed_files:
changenum += 1 print(f"*** CHANGED: {f}")
stats = newstats stats = newstats
build_webpages(src_path, build_dir, config) changenum += 1
print(f"[{changenum}] regenerated web pages at: {index_path}") build_webpages(src_path, build_dir, config)
print(f"URL: file://{index_path.resolve()}\n\n") print(f"[{changenum}] regenerated web pages at: {index_path}")
count = 0 print(f"URL: file://{index_path.resolve()}\n\n")
time.sleep(debounce_time) # simple debounce
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -273,9 +273,11 @@ Incoming emails must have a valid DKIM signature with
Signing Domain Identifier (SDID, ``d=`` parameter in the DKIM-Signature Signing Domain Identifier (SDID, ``d=`` parameter in the DKIM-Signature
header) equal to the ``From:`` header domain. This property is checked header) equal to the ``From:`` header domain. This property is checked
by OpenDKIM screen policy script before validating the signatures. This by OpenDKIM screen policy script before validating the signatures. This
correpsonds to strict :rfc:`DMARC <7489>` alignment (``adkim=s``). corresponds to strict :rfc:`DMARC <7489>` alignment (``adkim=s``).
If there is no valid DKIM signature on the incoming email, the If there is no valid DKIM signature on the incoming email, the
sender receives a “5.7.1 No valid DKIM signature found” error. sender receives a “5.7.1 No valid DKIM signature found” error.
After validating the DKIM signature,
the `final.lua` script strips all ``OpenDKIM:`` headers to reduce message size on disc.
Note that chatmail relays Note that chatmail relays

View File

@@ -22,7 +22,12 @@ Note that your chatmail relay still needs to be able to make outgoing
connections on port 25 to send messages outside. connections on port 25 to send messages outside.
To setup a reverse proxy (or rather Destination NAT, DNAT) for your To setup a reverse proxy (or rather Destination NAT, DNAT) for your
chatmail relay, put the following configuration in chatmail relay, follow these instructions:
Linux
^^^^^
Put the following configuration in
``/etc/nftables.conf``: ``/etc/nftables.conf``:
:: ::
@@ -110,5 +115,61 @@ Uncomment in ``/etc/sysctl.conf`` the following two lines:
Then reboot the relay or do ``sysctl -p`` and Then reboot the relay or do ``sysctl -p`` and
``nft -f /etc/nftables.conf``. ``nft -f /etc/nftables.conf``.
Once proxy relay is set up, you can add its IP address to the DNS. FreeBSD / pf
^^^^^^^^^^^^
Put the following configuration in
``/etc/pf.conf``:
::
ext_if = "em0"
forward_ports = "{ 25, 80, 143, 443, 465, 587, 993 }"
chatmail_ipv4 = "AAA.BBB.CCC.DDD"
icmp_types = "{ echoreq, echorep, unreach, timex }"
chatmail_ipv6 = "XXX::1"
icmp6_types = "{ echorep, echoreq, neighbradv, neighbrsol, routeradv, routersol, unreach, toobig, timex }"
set skip on lo0
nat on $ext_if inet from any to any -> ($ext_if:0)
nat on $ext_if inet6 from any to any -> ($ext_if:0)
# Define the redirect rules
rdr on $ext_if inet proto tcp from any to ($ext_if:0) port $forward_ports -> $chatmail_ipv4
rdr on $ext_if inet6 proto tcp from any to ($ext_if:0) port $forward_ports -> $chatmail_ipv6
# Accept the incoming traffic to the specified ports we will NAT redirect
pass in quick on $ext_if inet proto tcp from any to any port $forward_ports flags S/SA modulate state
pass in quick on $ext_if inet6 proto tcp from any to any port $forward_ports flags S/SA modulate state
# Allow incoming SSH for host mgmt
pass in quick on $ext_if proto tcp from any to ($ext_if) port 22 flags S/SA modulate state
# Allow ICMP
pass in quick on $ext_if inet proto icmp all icmp-type $icmp_types keep state
pass in quick on $ext_if inet6 proto ipv6-icmp all icmp6-type $icmp6_types keep state
# Allow traffic from anyone to go through the NAT
pass on $ext_if inet proto tcp from any to $chatmail_ipv4 flags S/SA modulate state
pass on $ext_if inet6 proto tcp from any to $chatmail_ipv6 flags S/SA modulate state
# Default allow out
pass out quick on $ext_if from any to any
# Default block
block drop in log all
Insert into ``/etc/sysctl.conf.local`` the following two lines:
::
net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1
Activate the sysctls with ``service sysctl onestart``.
Enable the pf firewall with ``service pf enable``.
Apply the firewall rules with ``service pf start`` or ``pfctl -f /etc/pf.conf``.
Note, enabling the firewall may interrupt your SSH session, but you can reconnect.
Once proxy relay is set up, you can add its IP address to the DNS.