trusteddomainproject / OpenDKIM

Other
97 stars 52 forks source link

Seemingly an issue with how InternalHosts work.. #231

Open nem8 opened 1 month ago

nem8 commented 1 month ago

I added three subnets for some new servers that are sending mail, but openDkim failed to recognize them as internal. The subnets were added in CIDR form, like 192.168.0.1/24 and the same for .2 and .3.

I have other subnets added that works this way, but these subnets did.

I read the docs on InternalHosts (and by reference, PeerList) and decided to try to add the domain, and to my confusion that worked. The servers are not in DNS so i guess openDKIM parses the from address in header of mail to look up the domain? This is not explained in detail in the docs (at least not that i could find), and i was not able to understand the source code.

And it doesnt really explain why it wont recognize the new ip ranges as internal either..

So i guess what im claiming is that the ip range (cidr notation) doesnt always work, or i did something wrong and i dont understand what.

Details:

Debian 11
opendkim: OpenDKIM Filter v2.11.0
    Compiled with OpenSSL 1.1.1w  11 Sep 2023
    SMFI_VERSION 0x1000001
    libmilter version 1.0.1

Postfix 3.5.25

opendkim.conf:

Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes
UMask                   002
OversignHeaders         From
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
ExternalIgnoreList /etc/opendkim/TrustedHosts
InternalHosts /etc/opendkim/TrustedHosts
SignatureAlgorithm rsa-sha256
AutoRestart Yes
UserID opendkim:opendkim
Socket inet:8891@localhost
Canonicalization relaxed/relaxed

(example)TrustedHosts:

127.0.0.1
mailserver.mydomain.com
10.10.1.0/24
domain.com
192.168.1.0/24
192.168.2.0/24
192.168.3.0/24
myotherdomain.com <-- This is the domain in the from adress in header

postfix conf:

milter_default_action = accept
milter_protocol = 2
smtpd_milters = inet:localhost:8891
non_smtpd_milters = $smtpd_milters
mail_version = 3.6
compatibility_level = 3

example mail not signed:

Oct 17 08:00:19 mailserver postfix/smtpd[1755174]: A6A1F2403AC: client=unknown[192.168.3.5]
Oct 17 08:00:19 mailserver postfix/cleanup[1755771]: A6A1F2403AC: message-id=<12315156.98430485.ZG4tLTU3YWEzMDZ1jadfjOWwFJJAFNb44GI0LTEyYTNmN2M3N2ViNw==@myotherdomain.com>
Oct 17 08:00:19 mailserver opendkim[2432314]: A6A1F2403AC: [192.168.3.5] [192.168.3.5] not internal
Oct 17 08:00:19 mailserver opendkim[2432314]: A6A1F2403AC: not authenticated
Oct 17 08:00:19 mailserver opendkim[2432314]: A6A1F2403AC: no signature data
Oct 17 08:00:19 mailserver postfix/qmgr[753745]: A6A1F2403AC: from=<news@myotherdomain.com>, size=160728, nrcpt=1 (queue active)
futatuki commented 1 month ago

Are you sure reloading (sending SIGUSR1) or restarting opendkim milter after changing InternalHosts file? (corrected: SIGHUP -> SIGUSR1)

I read the docs on InternalHosts (and by reference, PeerList) and decided to try to add the domain, and to my confusion that worked. The servers are not in DNS so i guess openDKIM parses the from address in header of mail to look up the domain?

The check for "internal" by InternalHosts and "peer" by PeerList is perfomed against a hostname and an IP address where were passed from MTA (in connection phase (c.f. milter protocol overview, xxfi_connect). So the hostname here is hostname resolved from IP address and verified by IP address look up for the hostname.

nem8 commented 1 month ago

Hello, thanks for the fast reply.

Yes, i reloaded both postfix and opendkim (multiple times) after making the changes. When that didnt work i restarted the entire server. Still no change. I was at this on and off over the course of the past week before i finally tried setting the domain as a last ditch effort.

hostname | the host name of the message sender, as determined by a reverse lookup on the host address. If the reverse >lookup fails or if none of the IP addresses of the resolved host name matches the original IP address, hostname will contain >the message sender's IP address enclosed in square brackets (e.g. `[a.b.c.d]'). If the SMTP connection is made via stdin the >value is localhost.

As you can see from the example log, it is not successfull in looking up the hostname, these servers are not in DNS. (Ip address in brackets then ip address again without brackets). I verified this with dig -x which is unable to reverse lookup the ip's.

If opendkim doesnt read the domain from header then i dont understand why setting the domain in TrustedHosts works. And i still am no closer in understanding why setting the ip ranges didnt accomplish anything.

futatuki commented 1 month ago

I tried to reproduce it with opendkim on my branch (https://github.com/futatuki/OpenDKIM/tree/main) but it cannot.

I've use miltertest(8) and test lua script brought and modified from opendkim/tests/t-sign-rs.lua, for emulating MTA accepting connection from 192.168.5.33 and 192.168.3.22.

In the directory /opt/opendkim-issue-231,

opendkim.conf:
Syslog                  yes
SyslogSuccess           yes
LogWhy                  yes
UMask                   002
OversignHeaders         From
KeyTable /opt/opendkim-issue-231/KeyTable
SigningTable /opt/opendkim-issue-231/SigningTable
ExternalIgnoreList /opt/opendkim-issue-231/TrustedHosts
InternalHosts /opt/opendkim-issue-231/TrustedHosts
SignatureAlgorithm rsa-sha256
#AutoRestart Yes
Socket local:/opt/opendkim-issue-231/sock/opendkim.sock
Canonicalization relaxed/relaxed

TrustedHosts:

127.0.0.1
mailserver.mydomain.com
10.10.1.0/24
domain.com
192.168.1.0/24
192.168.2.0/24
192.168.3.0/24
myotherdomain.com

t-issue-231-internal.lua:

-- setup
sock = "unix:/opt/opendkim-issue-231/sock/opendkim.sock"

-- try to connect to it
conn = mt.connect(sock, 40, 0.25)
if conn == nil then
        error("mt.connect() failed")
end

-- send connection information
-- mt.negotiate() is called implicitly
if mt.conninfo(conn, "[192.168.3.22]", "192.168.3.22") ~= nil then
        error("mt.conninfo() failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.conninfo() unexpected reply")
end

-- send envelope macros and sender data
-- mt.helo() is called implicitly
mt.macro(conn, SMFIC_MAIL, "i", "t-sign-rs")
if mt.mailfrom(conn, "user@example.com") ~= nil then
        error("mt.mailfrom() failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.mailfrom() unexpected reply")
end

-- send headers
-- mt.rcptto() is called implicitly
if mt.header(conn, "From", "user@mydomain.com") ~= nil then
        error("mt.header(From) failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.header(From) unexpected reply")
end
if mt.header(conn, "Date", "Mon, 21 Oct 2024 20:06:25 +0900") ~= nil then
        error("mt.header(Date) failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.header(Date) unexpected reply")
end
if mt.header(conn, "Subject", "Signing test") ~= nil then
        error("mt.header(Subject) failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.header(Subject) unexpected reply")
end

-- send EOH
if mt.eoh(conn) ~= nil then
        error("mt.eoh() failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.eoh() unexpected reply")
end

-- send body
if mt.bodystring(conn, "This is a test!\r\n") ~= nil then
        error("mt.bodystring() failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.bodystring() unexpected reply")
end

-- end of message; let the filter react
if mt.eom(conn) ~= nil then
        error("mt.eom() failed")
end
if mt.getreply(conn) ~= SMFIR_ACCEPT then
        error("mt.eom() unexpected reply")
end

mt.disconnect(conn)

t-issue-231-non-internal.lua:

-- setup
sock = "unix:/opt/opendkim-issue-231/sock/opendkim.sock"

-- try to connect to it
conn = mt.connect(sock, 40, 0.25)
if conn == nil then
        error("mt.connect() failed")
end

-- send connection information
-- mt.negotiate() is called implicitly
if mt.conninfo(conn, "[192.168.3.22]", "192.168.3.22") ~= nil then
        error("mt.conninfo() failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.conninfo() unexpected reply")
end

-- send envelope macros and sender data
-- mt.helo() is called implicitly
mt.macro(conn, SMFIC_MAIL, "i", "t-sign-rs")
if mt.mailfrom(conn, "user@example.com") ~= nil then
        error("mt.mailfrom() failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.mailfrom() unexpected reply")
end

-- send headers
-- mt.rcptto() is called implicitly
if mt.header(conn, "From", "user@mydomain.com") ~= nil then
        error("mt.header(From) failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.header(From) unexpected reply")
end
if mt.header(conn, "Date", "Mon, 21 Oct 2024 20:06:25 +0900") ~= nil then
        error("mt.header(Date) failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.header(Date) unexpected reply")
end
if mt.header(conn, "Subject", "Signing test") ~= nil then
        error("mt.header(Subject) failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.header(Subject) unexpected reply")
end

-- send EOH
if mt.eoh(conn) ~= nil then
        error("mt.eoh() failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.eoh() unexpected reply")
end

-- send body
if mt.bodystring(conn, "This is a test!\r\n") ~= nil then
        error("mt.bodystring() failed")
end
if mt.getreply(conn) ~= SMFIR_CONTINUE then
        error("mt.bodystring() unexpected reply")
end

-- end of message; let the filter react
if mt.eom(conn) ~= nil then
        error("mt.eom() failed")
end
if mt.getreply(conn) ~= SMFIR_ACCEPT then
        error("mt.eom() unexpected reply")
end

mt.disconnect(conn)

maillog on miltertest -s t-issue-231-non-internal.lua:

Oct 21 20:43:45 retina-beta opendkim[59616]: t-sign-rs: [192.168.5.33] [192.168.5.33] not internal
Oct 21 20:43:45 retina-beta opendkim[59616]: t-sign-rs: not authenticated

maillog on miltertest -s t-issue-231-internal.lua:

Oct 21 20:44:02 retina-beta opendkim[59616]: t-sign-rs: no signing table match for 'user@mydomain.com

It seems the milter judged 192.168.5.33 is not 'internal' and 192.168.3.22 is 'internal'.

If opendkim doesnt read the domain from header then i dont understand why setting the domain in TrustedHosts works. ...

If the client IP address was resolved to valid hostname and forward lookup for the hostname returns client IP address, MTA passes the hostname to milters. And then, opendkim milter checks if the hostname is with in the domains in InternalHosts.

... And i still am no closer in understanding why setting the ip ranges didnt accomplish anything.

At least, it works in my testing environment. So it seems there is something we overlook.