Compare commits

..

8 commits
v0.1.2 ... main

Author SHA1 Message Date
83e267a608 chore: merkwerk auto-update 2025-09-10 20:04:19 +02:00
f684ea1b4c bump version to 0.1.4 2025-09-10 20:04:19 +02:00
caeb5662d4 chore: merkwerk auto-update 2025-09-10 20:01:19 +02:00
f20915ff33 fix(smtp): add missing headers to prevent spam classification
Add required SMTP headers to fix spam classification issues:
- Message-ID: generated from timestamp and from_address domain
- MIME-Version: 1.0 header for proper email formatting
- Content-Transfer-Encoding: 8bit for UTF-8 content

Fixes rspamd spam score from 10.42/10.00 (reject) to 4.80/10.00 (clean)
by resolving MISSING_MID (-2.50), MISSING_MIME_VERSION (-2.00),
and R_BAD_CTE_7BIT (-1.05) penalties.

Tested with mail-tester.com (10/10 score) and production deployment
on tiamat shows successful delivery to inbox instead of spam folder.

Related DAW/infrastruktur#35
2025-09-10 20:00:34 +02:00
4af068e15c chore: merkwerk auto-update 2025-09-10 16:46:13 +02:00
7a921dc791 Release v0.1.3: Add STARTTLS support for port 587 2025-09-10 16:46:12 +02:00
ec7086259e chore: merkwerk auto-update 2025-09-10 16:45:13 +02:00
304b010a56 fix(smtp): add STARTTLS support for port 587
- Add STARTTLS handshake after EHLO for port 587
- Upgrade socket to SSL after STARTTLS command
- Perform second EHLO over encrypted connection
- Resolves authentication issues with Hetzner and other SMTP providers
- Fixes 'Must issue a STARTTLS command first' error

Closes #113
2025-09-10 16:45:12 +02:00
3 changed files with 43 additions and 3 deletions

View file

@ -27,3 +27,7 @@ a670de0f,24bd94d,feature/systemd-hardening,2025-09-07T16:40:47Z,michael,git,lua-
4ee95dbc,08b49d3,security/sanitize-test-scripts,2025-09-07T19:25:38Z,michael,git,lua-api 4ee95dbc,08b49d3,security/sanitize-test-scripts,2025-09-07T19:25:38Z,michael,git,lua-api
59c85431,8b78066,main,2025-09-10T10:20:50Z,michael,git,lua-api 59c85431,8b78066,main,2025-09-10T10:20:50Z,michael,git,lua-api
a71dd794,f5d9f35,main,2025-09-10T12:27:54Z,michael,git,lua-api a71dd794,f5d9f35,main,2025-09-10T12:27:54Z,michael,git,lua-api
de5318f2,304b010,main,2025-09-10T14:45:12Z,michael,git,lua-api
980d67cd,7a921dc,main,2025-09-10T14:46:13Z,michael,git,lua-api
efbcbbd8,f20915f,main,2025-09-10T18:01:18Z,michael,git,lua-api
f777e765,f684ea1,main,2025-09-10T18:04:19Z,michael,git,lua-api

View file

@ -1 +1 @@
0.1.2 0.1.4

View file

@ -1,6 +1,6 @@
-- furt-lua/src/smtp.lua -- src/smtp.lua
-- Universal SMTP implementation with SSL compatibility -- Universal SMTP implementation with SSL compatibility
-- Supports both luaossl (Arch/karl) and luasec (OpenBSD/walter) -- Supports both luaossl (Arch) and luasec (OpenBSD)
-- Dragons@Work Digital Sovereignty Project -- Dragons@Work Digital Sovereignty Project
local socket = require("socket") local socket = require("socket")
@ -237,6 +237,33 @@ function SMTP:send_email(to_address, subject, message, from_name)
return cleanup_and_fail("EHLO failed: " .. response) return cleanup_and_fail("EHLO failed: " .. response)
end end
-- STARTTLS hinzufügen für Port 587
if self.port == 587 and self.use_ssl then
-- STARTTLS command
local success, response = self:send_command(sock, "STARTTLS", 220)
if not success then
return cleanup_and_fail("STARTTLS failed: " .. response)
end
-- Upgrade connection to SSL
local ssl_sock, err = self.ssl_compat:wrap_socket(sock, {
mode = "client",
protocol = "tlsv1_2"
})
if not ssl_sock then
return cleanup_and_fail("SSL upgrade failed: " .. err)
end
sock = ssl_sock
-- EHLO again over encrypted connection
local success, response = self:send_command(sock, "EHLO furt-lua", 250)
if not success then
return cleanup_and_fail("EHLO after STARTTLS failed: " .. response)
end
end
-- AUTH LOGIN -- AUTH LOGIN
local success, response = self:send_command(sock, "AUTH LOGIN", 334) local success, response = self:send_command(sock, "AUTH LOGIN", 334)
if not success then if not success then
@ -277,6 +304,11 @@ function SMTP:send_email(to_address, subject, message, from_name)
return cleanup_and_fail("DATA command failed: " .. response) return cleanup_and_fail("DATA command failed: " .. response)
end end
-- Generate unique Message-ID
-- Extract domain from configured from_address
local hostname = self.from_address:match("@(.+)") or self.server
local message_id = string.format("<%d.%d@%s>", os.time(), math.random(10000), hostname)
-- Build email message -- Build email message
local display_name = from_name or "Furt Contact Form" local display_name = from_name or "Furt Contact Form"
local email_content = string.format( local email_content = string.format(
@ -284,7 +316,10 @@ function SMTP:send_email(to_address, subject, message, from_name)
"To: <%s>\r\n" .. "To: <%s>\r\n" ..
"Subject: %s\r\n" .. "Subject: %s\r\n" ..
"Date: %s\r\n" .. "Date: %s\r\n" ..
"Message-ID: %s\r\n" ..
"MIME-Version: 1.0\r\n" ..
"Content-Type: text/plain; charset=UTF-8\r\n" .. "Content-Type: text/plain; charset=UTF-8\r\n" ..
"Content-Transfer-Encoding: 8bit\r\n" ..
"\r\n" .. "\r\n" ..
"%s\r\n" .. "%s\r\n" ..
".", ".",
@ -293,6 +328,7 @@ function SMTP:send_email(to_address, subject, message, from_name)
to_address, to_address,
subject, subject,
os.date("%a, %d %b %Y %H:%M:%S %z"), os.date("%a, %d %b %Y %H:%M:%S %z"),
message_id,
message message
) )