SPF "breaks" email forwarding. SRS is a way to fix it. SRS is a simple way for forwarding MTAs to rewrite the sender address. The original concept was published in draft-mengwong-sender-rewrite and further expanded on in a paper by Shevek.
srsfilter, so that it will be called when an email for the srs user is received:
echo "| /var/qmail/bin/srsfilter" > /var/qmail/alias/.qmail-srs-default
Then create and configure a virtual domain to be used exclusively for
SRS purposes. Be aware that this virtual domain should not be created by the usual
vadddomain program, as it exists just to run
srsfilter via the alias/.qmail-srs-default account that we created before and its definition is different from the
vpopmail's virtual domains.
echo srs.mydomain.tld:srs >> /var/qmail/control/virtualdomains
Refer to the
Life With Qmail bible to understand the logic behind, expecially for what virtual domains, aliases,
extensions addresses are concerned. An explanation is also provided below in the testing section.
Add srs.mydomain.tld to rcpthosts so that
qmail-smtpd will know that it has to deliver locally all messages for that domain. Do not add it to control/locals otherwise the virtualdomains file will be ignored and
srsfilter will not be run.
echo srs.mydomain.tld >> /var/qmail/control/rcpthosts
Add srs.mydomain.tld in the srs_domain control file, so that
srsfilter will use it in the rewritten address for all virtual hosts. Let's also create the srs_secret file, as well. It is a random string to generate and check
echo srs.mydomain.tld > /var/qmail/control/srs_domain echo "xxxxxxxxxxxxxxxxxxxxxx" > /var/qmail/control/srs_secrets
These are the only mandatory settings; look at the links above to have informations about all the other configuration parameters.
Of course we have to provide an
MX record and also an
SPF record like this to the newly created srs_domain in our
srs.mydomain.tld. IN TXT "v=spf1 a mx -all"
We should have already created an
SPF record for the control/me domain as well. If not, let's do it now.
We can now restart
qmail and test our
We are going to test the ability of our
SRS system to rewrite the addresses in case of a broken forwarder. Let's use a gmail.com account as the original sender, as gmail is known to be a very restrictive provider.
In what follows mydomain.tld is a virtual domain in the same server where we have configured the
SRS stuff, srs.mydomain.tld is the
SRS domain defined in control/srs_domain, firstname.lastname@example.org is the original sender, while email@example.com is the account where the forwarder has to forward the message. Of course it will bounce back and our
MTA has to perform the rewriting and notify the original sender without breaking the
Create a forwarder on our server, so that all the messages for firstname.lastname@example.org will be forwarded to email@example.com:
echo "&firstname.lastname@example.org" > ~vpopmail/domains/mydomain.tld/.qmail-srstest chown vpopmail:vchkpw ~vpopmail/domains/mydomain.tld/.qmail-srstest chmod 600 ~vpopmail/domains/mydomain.tld/.qmail-srstest
Since email@example.com is a fake recipient address, remotedomain.tld will bounce back any message to our
MTA, which will inform firstname.lastname@example.org after the rewriting.
Let's see the journey of the message around the net
This is the same as seen from our
qmail-smtpd receives and accepts the message from the original sender
2023-06-20 22:54:52.607641500 tcpserver: pid 16574 from 18.104.22.168 2023-06-20 22:54:52.627258500 tcpserver: ok 16574 smtp.mydomain.tld:10.0.0.4:25 mail-wm1-f49.google.com:22.214.171.124::44523 2023-06-20 22:55:13.058900500 qlogenvelope: result=accepted code=250 reason=rcptto detail=chkuser helo=mail-wm1-f49.google.com email@example.com firstname.lastname@example.org <---- this is the forwarder ................ qp= pid=16574
qmail-send passes the message to the forwarder email@example.com
2023-06-20 22:55:19.493086500 info msg 32560286: bytes 3377 from <firstname.lastname@example.org> qp 16615 uid 89 2023-06-20 22:55:19.493086500 starting delivery 60: msg 32560286 to local email@example.com <---- qmail converts firstname.lastname@example.org to email@example.com and treats the result as mydomain.tld is a local address 2023-06-20 22:55:19.493087500 status: local 1/10 remote 0/20 2023-06-20 22:55:19.495105500 delivery 60: success: did_0+1+0/qp_16617/
Let's see what's happening more in detail.
qmailopens the file control/virtualdomains looking for a line related to the recipient domain, i.e. mydomain.tld. Here is the content of that line:
mydomain.tld:mydomain.tldBe aware tha the two fields before and after the colon
:are not a duplicate, because they have a different meaning. The one on the left is the recipient address (even though the user@ part is often omitted), while the one on the right is the user whose .qmail file will have to handle the delivery for that domain.
qmailprepends the field on the right of the colon
:to the recipient address, which will be firstname.lastname@example.org, and treats mydomain.tld as a local address to deliver the message to.
qmail-localsearches the user mydomain.tld (technically it's a simple user even before a domain) in the users/assign file (actually its compiled version, which is users/cdb) and it finds something like:
+mydomain.tld-:mydomain.tld:89:89:/home/vpopmail/domains/mydomain.tld:-::It retrieves the home directory, which is /home/vpopmail/domains/mydomain.tld in this example.
qmail-localopens the the home directory to look for the file srstest/.qmail, which will have to handle the delivery. If it doesn't find that file, it will look for the files ~mydomain.tld/.qmail-srstest and ~mydomain.tld/.qmail-default in this order.
- In our case the file .qmail-srstest does exist and, being this the
forwardercreated earlier, it directs
qmail-sendin order to forward the message to someone else.
3. The address of the original sender is rewrited to SRS0=jiQ3=CIemail@example.com and the message sent to the remote server by
2023-06-20 22:55:19.495905500 info msg 32560301: bytes 3491 from <SRS0=jiQ3=CIfirstname.lastname@example.org> <----- SPF check would be valid qp 16617 uid 89 2023-06-20 22:55:19.495918500 starting delivery 61: msg 32560301 to remote email@example.com 2023-06-20 22:55:19.495920500 status: local 0/10 remote 1/20 2023-06-20 22:55:23.822750500 delivery 61: success: <From:SRS0=jiQ3=CIfirstname.lastname@example.org_To:email@example.com>_<remoteip>_accepted_message./Remote_host_said:_25>
qmail-smtpd receives the bounce
2023-06-20 22:55:24.049537500 tcpserver: pid 16687 from <remoteip> 2023-06-20 22:55:24.049945500 tcpserver: ok 16687 smtp.mydomain.tld:10.0.0.4:25 <remoteip>.<remotehost>:<remoteip>::37391 2023-06-20 22:55:44.826126500 qlogenvelope: result=accepted code=250 reason=rcptto detail=chkuser helo=<remotehelo> mailfrom= <------ null sender, as it's a system msg rcptto=SRS0=jiQ3=CIfirstname.lastname@example.org <------- the recipient has the srs_domain ................. qp= pid=16687
qmail-send sends the bounce to the local address SRS0=jiQ3=CIemail@example.com, which has the
SRS domain that we bound to
2023-06-20 22:55:51.265166500 info msg 32560286: bytes 5688 from <> <------- null sender, SPF is ok qp 16716 uid 89 2023-06-20 22:55:51.265166500 starting delivery 62: msg 32560286 to local srs-SRS0=jiQ3=CIfirstname.lastname@example.org <------- the srs prepend will trigger the default srs account, which in turn will run srsfilter 2023-06-20 22:55:51.265167500 status: local 1/10 remote 0/20 2023-06-20 22:55:51.270712500 delivery 62: success: srsfilter:_qp_16720/did_0+0+1/ <------- srsfilter in action... he knows what to do with that address
Now a few more details concerning what's happening behind the scene:
qmailreceives a message for a user of the domain srs.mydomain.tld and it deliver it locally, as that domain is listed in rcpthosts.
qmailsearches a line with the field srs.mydomain.tld on the left of the colon
:in the virtualdomains file:
- The field on the right side of the colon
:will be prepended to the local recipient address, which will be srs-SRS0=jiQ3=CIemail@example.com, with the srs prefix. This prefix represents the user whose .qmail file has to handle the delivery.
qmail-localopens the users/assign file (actually its compiled version users/cdb) looking for a line holding srs in the first field, but it doesn't find it, because we have no srs users (this is why we avoided to use
vadddomainto create the
- Since the user srs doesn't exist, it will look for an alias like alias/.qmail-srs, with no luck. Finally it finds the alias file alias/.qmail-srs-default, which will be the .qmail file that will handle the delivery.
- The alias/.qmail-srs-default file contains the instructions to run
srsfilteris capable to "disassemble" the SRS0=jiQ3=CIfirstname.lastname@example.org address in order to retrieve the recipient address, which will be email@example.com.
qmail-send sends the bounce to the original sender, the mailfrom is the null sender
2023-06-20 22:55:51.271507500 info msg 32560301: bytes 5707 from <> <------ null sender qp 16720 uid 1000 2023-06-20 22:55:51.271507500 starting delivery 63: msg 32560301 to remote firstname.lastname@example.org 2023-06-20 22:55:51.271508500 status: local 0/10 remote 1/20 2023-06-20 22:55:51.915441500 delivery 63: success: <From:_To:email@example.com>_126.96.36.199_accepted_message./Remote_host_said:_250_2.0.0_OK__1687294551_f7-20020adff8c700000>
and gmail will show a successfull
SPF check. Here his the important part of the header as seen from firstname.lastname@example.org:
Note that the
SPF check was done against smtp.mydomain.tld, i.e. the control/me domain, because the message to the original sender was sent with the null sender. That domain was retrieved from the
HELO. This is the reason why we have to define the
SPF also for the control/me domain.