Self-hosting SimpleLogin - email aliases

I created an Ansible role to set-up self-hosted SimpleLogin service for my email aliases.

· 8 min read
Self-hosting SimpleLogin - email aliases

Intro

Recently, I came across a new service that is self-hostable and seems quite useful:

SimpleLogin | Open source anonymous email service
With email aliases , you can be anonymous online and protect your inbox against spams and phishing.
Link thumbnail

What is does it allows you to create email aliases that you can use to sign up for shops, online services or newsletters instead of your real email address. Emails sent to those aliases will then be forwarded to your inbox, while you stay anonymous (relatively - if you self-host you still have to use some of your domains).

As I have some personal domains, I wanted to check it out, but of course the self-hosted version. As with all things self-hosting, I decided to use Ansible to set-up the service, using my fork of Ansible-NAS:

GitHub - anarion80/ansible-nas: Build a full-featured home server or NAS replacement with an Ubuntu box and this playbook.
Build a full-featured home server or NAS replacement with an Ubuntu box and this playbook. - anarion80/ansible-nas
Link thumbnail

E-mail hosting

Self-hosting email is not generally recommended, as it is difficult and time-consuming to ensure proper deliverability of emails and to maintain good reputation with spam blocklisting services.

I didn’t really want to self-host my email exactly for those reasons. I use Google Workspace with one of my custom domains to send and receive emails, primarily as a relay to send out emails from all my self-hosted services. I have a postfix server internally which is configured to send via Google Workspace, and all the services just use that internal server for sending.

With SimpleLogin, you need to be able to receive email for your email aliases. Theoretically, you are able to receive email on one server, and send it out via another, so that is what I attempted to do with a new domain.

I have two domains: domain1.tld and domain2.tld. The first one is configured with Google Workspace with appropriate SPF and DKIM values on my DNS settings. The second is what I want to use for this experiment with SimpleLogin.

I switched DNS MX records to point to the SimpleLogin server, following their documentation :

 dig domain2.tld mx

; <<>> DiG 9.18.28-0ubuntu0.20.04.1-Ubuntu <<>> domain2.tld mx
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 41244
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;domain2.tld.                   IN      MX

;; ANSWER SECTION:
domain2.tld.            600     IN      MX      500 simplelogin.domain2.tld.
domain2.tld.            600     IN      MX      100 simplelogin.domain2.tld.
domain2.tld.            600     IN      MX      10 simplelogin.domain2.tld.

;; Query time: 160 msec
;; SERVER: 127.0.0.53#53(127.0.0.53) (UDP)
;; WHEN: Sat Oct 05 15:28:52 CEST 2024
;; MSG SIZE  rcvd: 104

Installation

To use the mentioned Ansible playbook, first I had to set up all the necessary env variables in my nas.yaml file:

simplelogin_enabled: true
simplelogin_email_domain: "domain2.tld"
simplelogin_support_email: "[email protected]"
simplelogin_app_address: "simplelogin.domain2.tld"
simplelogin_url: "https://simplelogin.domain2.tld"
simplelogin_postfix_relayhost: "192.168.2.9:25"
simplelogin_available_externally: true
simplelogin_allowlist: "127.0.0.1/32, 192.168.0.0/16"
simplelogin_enable_subscription: true
simplelogin_postfix_port_smtp: "1125"
simplelogin_add_builtin_domain_as_custom_domain: true

The variables are pretty self-explanatory - one needs to set the domain, relay host address and some labels for traefik. I had to set this up on a port different than 25 as the main server was already using that port. I had the then port-forward that port and submission port (587) on the router to my docker host.

As you can see there is no TLS/SSL here, as everything is behind the main reverse proxy.

Then it was just a matter of running the playbook:

 ansible-playbook -i inventories/my-ansible-nas/inventory nas.yml -b -K -v -t simplelogin --limit anarion-nas

and all 5 related services are created, including templating and updating postfix config files:

alt text

First email sending

All I wanted is to receive emails there, so I thought I can forget about all the SPF, DKIM, DMARC settings for the domain, and just use Google Workspace settings. That should make things a lot easier.

After setting it all up, registration and first confirmation email is sent to my email address, but never arrives… I then registered again, using email from Gmail. This time the registration email is received… but in SPAM. Looking into the email details, I can see the following:

ARC-Authentication-Results: i=1; mx.google.com;
       dkim=pass header.i=@domain1.tld.20230601.gappssmtp.com header.s=20230601 header.b=tr40od1u;
       spf=softfail (google.com: domain of transitioning transactional+2+@domain2.tld does not designate [Google IP] as permitted sender) smtp.mailfrom=transactional+2+@domain2.tld; #
       dara=pass header.i=@gmail.com

After some googling, it looks like this is the case of SPF records setting. On my working domain1.tld I have an SPF record like this:

v=spf1 include:mx.my_domain_registrar a mx ptr include:_spf.google.com -all

For my domain2.tld, on a different registrar, I had to add a TXT record:

"v=spf1 include:_spf.google.com -all

and after that the email is sent and received correctly:

Authentication-Results: mx.google.com;
       spf=pass (google.com: domain of transactional+2+@domain2.tld designates [Google IP] as permitted sender) smtp.mailfrom=transactional+2+@domain2.tld; #
       dkim=pass header.i=@domain1.tld.20230601.gappssmtp.com header.s=20230601 header.b=Zhv6vqnF
Received-SPF: pass (google.com: domain of transactional+2+@domain2.tld designates [Google IP] as permitted sender) client-ip=[Google IP];

Postfix restrictions

I was then finally able to receive the registration email and activate the account. I created some test alias just to see if the receiving side works. Unfortunately, it did not - all emails incoming to the alias were rejected:

postfix/smtpd[2688]: NOQUEUE: reject: RCPT from mail-vk1-f170.google.com[209.85.221.170]: 554 5.7.1 <test@domain2.tld>: Recipient address rejected: Access denied; from=<some@gmail.com> to=<test@domain2.tld> proto=ESMTP helo=<mail-vk1-f170.google.com>

Since the error says Recipient I started to debug all postfix recipient-related directives in /etc/postfix/main.cf config file:

smtpd_recipient_restrictions =
    reject_rbl_client zen.spamhaus.org,
    reject_non_fqdn_recipient,
    reject_unknown_recipient_domain,
    permit_mynetworks,
    reject_rbl_client bl.spamcop.net,
    permit

One by one, removing, testing, researching - nothing. I then enabled debug debug_peer_list = 209.85.0.0/16 to catch an email incoming from Gmail and check each step:

postfix/smtpd[2688]: >>> START Recipient address RESTRICTIONS <<<
postfix/smtpd[2688]: generic_checks: name=reject_rbl_client
postfix/smtpd[2688]: reject_rbl_addr: Client host 209.85.221.170
postfix/smtpd[2688]: dns_query: 170.221.85.209.zen.spamhaus.org (A): OK
postfix/smtpd[2688]: dns_query: reply len=65 ancount=1 nscount=0
postfix/smtpd[2688]: dns_get_answer: type A for 170.221.85.209.zen.spamhaus.org
postfix/smtpd[2688]: dns_query: 170.221.85.209.zen.spamhaus.org (TXT): Host not found
postfix/smtpd[2688]: ctable_locate: install entry key 170.221.85.209.zen.spamhaus.org
postfix/smtpd[2688]: ctable_locate: install entry key 127.0.0.[2..11]
postfix/smtpd[2688]: generic_checks: name=reject_rbl_client status=0
postfix/smtpd[2688]: generic_checks: name=reject_unauth_pipelining
postfix/smtpd[2688]: reject_unauth_pipelining: RCPT
postfix/smtpd[2688]: generic_checks: name=reject_unauth_pipelining status=0
postfix/smtpd[2688]: generic_checks: name=reject_non_fqdn_recipient
postfix/smtpd[2688]: reject_non_fqdn_address: test@domain2.tld
postfix/smtpd[2688]: generic_checks: name=reject_non_fqdn_recipient status=0
postfix/smtpd[2688]: generic_checks: name=reject_unknown_recipient_domain
postfix/smtpd[2688]: reject_unknown_address: test@domain2.tld
postfix/smtpd[2688]: ctable_locate: move existing entry key some@gmail.com?test@domain2.tld
postfix/smtpd[2688]: reject_unknown_mailhost: domain2.tld
postfix/smtpd[2688]: lookup domain2.tld type MX flags
postfix/smtpd[2688]: dns_query: domain2.tld (MX): OK
postfix/smtpd[2688]: dns_query: reply len=93 ancount=3 nscount=0
postfix/smtpd[2688]: dns_get_answer: type MX for domain2.tld
postfix/smtpd[2688]: dns_get_answer: type MX for domain2.tld
postfix/smtpd[2688]: dns_get_answer: type MX for domain2.tld
postfix/smtpd[2688]: generic_checks: name=reject_unknown_recipient_domain status=0
postfix/smtpd[2688]: generic_checks: name=permit_mynetworks
postfix/smtpd[2688]: permit_mynetworks: mail-vk1-f170.google.com 209.85.221.170
postfix/smtpd[2688]: match_hostname: mynetworks: mail-vk1-f170.google.com ~? 127.0.0.0/8
postfix/smtpd[2688]: match_hostaddr: mynetworks: 209.85.221.170 ~? 127.0.0.0/8
postfix/smtpd[2688]: match_hostname: mynetworks: mail-vk1-f170.google.com ~? [::ffff:127.0.0.0]/104
postfix/smtpd[2688]: match_hostaddr: mynetworks: 209.85.221.170 ~? [::ffff:127.0.0.0]/104
postfix/smtpd[2688]: match_hostname: mynetworks: mail-vk1-f170.google.com ~? [::1]/128
postfix/smtpd[2688]: match_hostaddr: mynetworks: 209.85.221.170 ~? [::1]/128
postfix/smtpd[2688]: match_hostname: mynetworks: mail-vk1-f170.google.com ~? 10.0.0.0/24
postfix/smtpd[2688]: match_hostaddr: mynetworks: 209.85.221.170 ~? 10.0.0.0/24
postfix/smtpd[2688]: match_hostname: mynetworks: mail-vk1-f170.google.com ~? 172.16.0.0/12
postfix/smtpd[2688]: match_hostaddr: mynetworks: 209.85.221.170 ~? 172.16.0.0/12
postfix/smtpd[2688]: match_hostname: mynetworks: mail-vk1-f170.google.com ~? 192.168.0.0/16
postfix/smtpd[2688]: match_hostaddr: mynetworks: 209.85.221.170 ~? 192.168.0.0/16
postfix/smtpd[2688]: match_list_match: mail-vk1-f170.google.com: no match
postfix/smtpd[2688]: match_list_match: 209.85.221.170: no match
postfix/smtpd[2688]: generic_checks: name=permit_mynetworks status=0
postfix/smtpd[2688]: generic_checks: name=reject_unauth_destination
postfix/smtpd[2688]: reject_unauth_destination: test@domain2.tld
postfix/smtpd[2688]: permit_auth_destination: test@domain2.tld
postfix/smtpd[2688]: ctable_locate: leave existing entry key some@gmail.com?test@domain2.tld
postfix/smtpd[2688]: generic_checks: name=reject_unauth_destination status=0
postfix/smtpd[2688]: generic_checks: name=reject_rbl_client
postfix/smtpd[2688]: reject_rbl_addr: Client host 209.85.221.170
postfix/smtpd[2688]: dns_query: 170.221.85.209.bl.spamcop.net (A): Host not found
postfix/smtpd[2688]: ctable_locate: install entry key 170.221.85.209.bl.spamcop.net
postfix/smtpd[2688]: generic_checks: name=reject_rbl_client status=0
postfix/smtpd[2688]: generic_checks: name=reject
postfix/smtpd[2688]: NOQUEUE: reject: RCPT from mail-vk1-f170.google.com[209.85.221.170]: 554 5.7.1 <test@domain2.tld>: Recipient address rejected: Access denied; from=<some@gmail.com> to=<test@domain2.tld> proto=ESMTP
postfix/smtpd[2688]: generic_checks: name=reject status=2
postfix/smtpd[2688]: >>> END Recipient address RESTRICTIONS <<<

…and nothing really made sense here. None of the directives seem to fail, but still the email was rejected. After re-reading many of ServerFault questions, one answer caught my eye:

Postfix rejects all incoming emails
My email server worked fine till now but for unknown reasons, possibly after setting up forced TLS connection, I am no longer able to receive emails; I did test it during the TLS configuration thou…
Link thumbnail

“Postfix evaluates all restriction lists, including the relay one for received emails.”

And that was it! I had a reject directive, but under smtpd_relay_restrictions, and that was ultimately the cause of the rejection. After changing it to permit at the end, this error was gone.

Spamhaus

Another problem I noticed in the logs was that my postfix server set up with SimpleLogin started rejecting emails from reputable source like Gmail(!). In the logs I could see:

<[email protected]>:
[my IP] does not like recipient.
Remote host said: 554 5.7.1 Service unavailable; Client host [209.85.221.170] blocked using zen.spamhaus.org # [!code focus]
Giving up on [my ip].

Postfix settings for relay restriction looked like this:

smtpd_relay_restrictions =
    reject_unauth_pipelining,
    reject_non_fqdn_recipient,
    reject_unknown_recipient_domain,
    permit_mynetworks,
    reject_unauth_destination,
    reject_rbl_client zen.spamhaus.org, # [!code highlight]
    reject_rbl_client bl.spamcop.net,
    permit

After checking out the link provided by spamhaus it appeared that the issue is related to the usage of Spamhaus Project Public Mirrors , and an appropriate solution is already provided there:

smtpd_relay_restrictions =
    reject_unauth_pipelining,
    reject_non_fqdn_recipient,
    reject_unknown_recipient_domain,
    permit_mynetworks,
    reject_unauth_destination,
    reject_rbl_client zen.spamhaus.org, # [!code --]
    reject_rbl_client zen.spamhaus.org=127.0.0.[2..11], # [!code ++]
    reject_rbl_client bl.spamcop.net,

After that the issue was gone.