No alternative text provided

Remplacer ForwardMX : redirection d’e-mails avec Kubernetes et LDAP

TL;DR : J'ai remplacé mon abonnement ForwardMX à 10€/mois par mon propre service de forwarding de mail, avec OpenLDAP et docker-mailserver sur mon cluster Kubernetes. Il m'a fallu quelques ajustements, mais maintenant je reçois et envoie des emails depuis @oursi.net directement depuis Gmail, gratuitement 🎉


🧠 Contexte

Quand j'ai acheté oursi.net, je voulais :

  • Recevoir les emails en @oursi.net directement sur mon compte Gmail
  • Pouvoir envoyer des emails depuis ce domaine aussi
  • Éviter de créer et gérer des boîtes mail classiques

Au départ, j'utilisais ForwardMX, qui fonctionnait bien mais coûtait presque 10€/mois. C'est beaucoup pour quelques règles de redirection. J'ai donc décidé d'auto-héberger tout ça sur mon propre cluster Kubernetes.

Mon objectif était :

  • Une configuration SMTP uniquement (sans boîte mail)
  • Intégration complète à Gmail
  • Authentification sécurisée + conformité SPF, DKIM, DMARC

🧹 Aperçu de la stack technique

  • Cluster Kubernetes K3s (auto-hébergé sur un VPS)
  • OpenLDAP pour la gestion des alias et utilisateurs
  • Docker-Mailserver (DMS) pour le moteur SMTP
  • MetalLB au lieu de ServiceLB pour conserver les IPs réelles
  • Traefik pour le routage (avec Proxy Protocol v2)
  • Teleport pour exposer les outils internes comme l'interface LDAP

1. Déployer OpenLDAP avec Helm

J'utilise le chart jp-gouin/helm-openldap.

Pourquoi LDAP ?

Parce qu'en mode SMTP-only, docker-mailserver fonctionne mieux avec LDAP pour :

  • Déclarer des alias mails
  • Gérer l'authentification SMTP
  • Définir les adresses de redirection

Mon application ArgoCD pour OpenLDAP :

1apiVersion: argoproj.io/v1alpha1
2kind: Application
3metadata:
4  name: ldap
5  namespace: argocd
6  finalizers:
7    - resources-finalizer.argocd.argoproj.io
8spec:
9  project: default
10  source:
11    repoURL: https://jp-gouin.github.io/helm-openldap/
12    chart: openldap-stack-ha
13    targetRevision: 4.3.3
14    helm:
15      values: |
16        replicaCount: 1
17        global:
18          # Replace with your domain below
19          ldapDomain: oursi.net 
20        replication:
21          # I disable replication because I don't need HA
22          enabled: false
23         
24        ltb-passwd:
25          ingress:
26            enabled: false
27
28          ldap:
29            # This will restrict password update for mail forwarding users only
30            searchBase: "ou=mail,dc=oursi,dc=net"
31
32        phpldapadmin:
33            ingress:
34              enabled: false
35
36        customLdifFiles:
37          00-root.ldif: |-
38            # Root creation, adapt to your domain
39            dn: dc=oursi,dc=net
40            objectClass: dcObject
41            objectClass: organization
42            o: Oursi.net
43
44          01-mailserver-member.ldif: |-
45            # Creating the mailserver user, that will be used by postfix to connect to ldap.
46            # The userPassword will need to be updated in phpldapadmin for instance
47            dn: cn=mailserver,dc=oursi,dc=net
48            objectClass: inetOrgPerson
49            objectClass: top
50            cn: mailserver
51            sn: Postfix
52            userPassword: {SSHA}x
53            description: User for querying mail entries
54
55          02-mail-group.ldif: |-
56            # Mail group creation, that's where I will define all the users for postfix
57            dn: ou=mail,dc=oursi,dc=net
58            objectClass: organizationalUnit
59            objectClass: top
60            ou: mail
61            description: Mail organizational unit
62
63          03-benoit-user.ldif: |-
64            # A sample postfix user, 
65            dn: uid=sample@oursi.net,ou=mail,dc=oursi,dc=net
66            objectClass: inetOrgPerson
67            objectClass: top
68            objectClass: postfixUser
69            uid: sample@oursi.net
70            cn: Sample User
71            givenName: Sample
72            sn: User
73            mail: sample@oursi.net
74            userPassword: {SSHA}x
75            mailacceptinggeneralid: sample@oursi.net
76            mailacceptinggeneralid: sample.user@oursi.net
77            maildrop: sample.redirect@gmail.com
78
79        # this is to grant access to mailserver to the list of users in mail
80        customAcls: |-
81           dn: olcDatabase={2}mdb,cn=config
82           changetype: modify
83           replace: olcAccess
84           olcAccess: {0}to *
85              by dn.exact=gidNumber=0+uidNumber=1001,cn=peercred,cn=external,cn=auth manage
86              by * break
87           olcAccess: {1}to dn.subtree="ou=mail,dc=oursi,dc=net" 
88              by dn="cn=mailserver,dc=oursi,dc=net" read
89              by self read
90              by * break 
91           olcAccess: {2}to attrs=userPassword,shadowLastChange
92              by self write
93              by dn="cn=admin,dc=oursi,dc=net" write
94              by dn="cn=mailserver,dc=oursi,dc=net" read
95              by anonymous auth 
96              by * break
97           olcAccess: {3}to *
98              by dn="cn=admin,dc=oursi,dc=net" write
99              by self read
100              by * break
101
102       
103        customSchemaFiles:
104          #enable memberOf ldap search functionality, users automagically track groups they belong to
105          00-memberof.ldif: |-
106            # Load memberof module
107            dn: cn=module,cn=config
108            cn: module
109            objectClass: olcModuleList
110            olcModuleLoad: memberof
111            olcModulePath: /opt/bitnami/openldap/lib/openldap
112
113            dn: olcOverlay=memberof,olcDatabase={2}mdb,cn=config
114            changetype: add
115            objectClass: olcOverlayConfig
116            objectClass: olcMemberOf
117            olcOverlay: memberof
118            olcMemberOfRefint: TRUE
119
120          01-postfix.ldif: |-
121            # Postfix creation: the users we create are also of class postfix, it allows for attributes maildrop and mailacceptinggeneralid
122            dn: cn=postfix,cn=schema,cn=config
123            cn: postfix
124            objectclass: olcSchemaConfig
125            olcattributetypes: {0}(1.3.6.1.4.1.4203.666.1.200 NAME 'mailacceptinggeneralid' DESC 'Postfix mail local address alias attribute' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024})
126            olcattributetypes: {1}(1.3.6.1.4.1.4203.666.1.201 NAME 'maildrop' DESC 'Postfix mail final destination attribute' EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024})
127            olcobjectclasses: {0}(1.3.6.1.4.1.4203.666.1.100 NAME 'postfixUser' DESC 'Postfix mail user class' SUP top AUXILIARY MAY(mailacceptinggeneralid $ maildrop))
128
129  destination:
130    server: https://kubernetes.default.svc
131    namespace: ldap
132  syncPolicy:
133    automated:
134      prune: true
135      selfHeal: true
136    syncOptions:
137      - CreateNamespace=true

Cela déploie aussi :

  • phpldapadmin — interface web pour explorer LDAP
  • ltb-passwd — outil web pour changer les mots de passe

Beaucoup de paramètres dans cette config, mais principalement tout est fait pour préparer les éléments principaux dont on aura besoin avec LDAP :

  • l'élément racine (OrganisationalUnit)
  • un utilisateur mailserver qui sera celui avec lequel Postfix va se connecter
  • un groupe mail où l'on va créer nos différents utilisateurs pour la redirection
  • une classe PostfixUser qui ajoute les attributs mailacceptinggeneralid et maildrop
  • un exemple d'utilisateur pour nos tests

NB: je n'ai pas modifié les mots de passe admin par défaut ici, et j'ai mis un mot de passe invalide pour le compte mailserver que l'on mettra à jour un peu plus tard. En production, il faudrait changer ces mots de passe admin, je montrerai comment faire dans un prochain article.

Exposition via Teleport

Comme j'utilise Teleport, je déclare les services LDAP dans mon agent kube (voir mon article sur Teleport ici)

1- name: ldapadmin
2  uri: "http://ldap-phpldapadmin.ldap.svc.cluster.local:80"
3  public_addr: ldapadmin.teleport.oursi.net
4  rewrite:
5    redirect:
6      - ldap-phpldapadmin.ldap.svc.cluster.local
7- name: ldappwd
8  uri: "http://ldap-ltb-passwd.ldap.svc.cluster.local:80"
9  public_addr: ldappwd.teleport.oursi.net
10  rewrite:
11    redirect:
12      - ldap-ltb-passwd.ldap.svc.cluster.local

Et j'ajoute les routes HostSNI pour exposer les services dans mon TCPIngressRoute de Traefik.

1- match: HostSNI(`ldapadmin.teleport.oursi.net`)
2  services:
3    - name: teleport
4      port: 443
5      nativeLB: true
6- match: HostSNI(`ldappwd.teleport.oursi.net`)
7  services:
8    - name: teleport
9      port: 443
10      nativeLB: true

Une fois déployé :

Rendez-vous sur phpldapadmin (via Teleport) et connectez-vous avec le compte admin par défaut (Not@SecurePassw0rd, à changer rapidement !). Vous verrez cette interface :

phpldapadmin.jpeg

Les attributs importants :

  • uid : login pour l'auth SMTP (ex: sample@oursi.net)
  • mailacceptinggeneralid : alias de cet utilisateur
  • maildrop : destination de redirection
  • userPassword : mot de passe pour auth SMTP

C'est le moment de modifier le mot de passe de cn=mailserver avec l'interface de ldap admin.

Vous pouvez ensuite tester une connexion LDAP avec cn=mailserver,dc=oursi,dc=net et voir si vous listez bien les utilisateurs du groupe mail, par exemple avec la commande suivante (à executer dans un shell du pod ldap-0 par exemple) :

1ldapsearch -x -H ldap://ldap.ldap.svc.cluster.local -D "cn=mailserver,dc=oursi,dc=net" -W -b "ou=mail,dc=oursi,dc=net" "(objectClass=inetOrgPerson)"

Ca devrait vous retourner notre utilisateur de test.


2. Configuration de docker-mailserver (DMS)

La partie la plus complexe.

2.a. Migration vers MetalLB

ServiceLB ne permet pas de conserver l'IP source car il ne fonctionne pas en couche OSI niveau 2. C'est pourtant nécessaire pour :

  • Garder l'IP réelle pour les logs et la protection antispam de DMS
  • Utiliser le Proxy Protocol (requis pour postfix)

J'ai donc réinstallé k3s sans ServiceLB en utilisant le même script que pour l'installation et en modification la partie 'disable' :

1--disable traefik,metrics-server,servicelb

Puis installé MetalLB via ArgoCD et ce fichier kustomize :

1# kustomization.yml
2apiVersion: kustomize.config.k8s.io/v1beta1
3kind: Kustomization
4namespace: metallb-system
5
6resources:
7  - github.com/metallb/metallb/config/native?ref=v0.15.2
8  - pool.yaml

Voilà le fichier 'pool.yaml', à adapter avec vos adresses IP disponibles.

1apiVersion: metallb.io/v1beta1
2kind: IPAddressPool
3metadata:
4  name: first-pool
5  namespace: metallb-system
6spec:
7  addresses:
8    - <your_ip>
9---
10apiVersion: metallb.io/v1beta1
11kind: L2Advertisement
12metadata:
13  name: first-advertisement
14  namespace: metallb-system

Vérifiez que les services existant fonctionnent toujours correctement et passons à l'étape suivante.


2.b. Traefik EntryPoints + Proxy Protocol

Par défaut, Traefik écoute uniquement sur les ports 80 et 443 (entrypoints web et websecure respectivement). On ajoute de nouveaux entrypoints en modifiant notre values.yaml et on définit le externalTrafficPolicy en mode local.

1service:
2  spec:
3    externalTrafficPolicy: Local # Preserve client IPs
4ports:
5  smtp:
6    port: 8025                # Container port
7    expose:
8      default: true           # Expose through the default service
9    exposedPort: 25           # Service port
10    protocol: TCP             # Port protocol (TCP/UDP)
11    tls:
12      enabled: false          # TLS is not enabled for SMTP
13  submissions:
14    port: 8465                # Container port
15    expose:
16      default: true           # Expose through the default service
17    exposedPort: 465          # Service port
18    protocol: TCP             # Port protocol (TCP/UDP)
19    tls:
20      enabled: true
21  submission:
22    port: 8587                # Container port
23    expose:
24      default: true           # Expose through the default service
25    exposedPort: 587          # Service port
26    protocol: TCP             # Port protocol (TCP/UDP)
27    tls:
28      enabled: false

On ajoute aussi de nouveaux IngressRouteTCP avec le proxy protocol activé :

1apiVersion: traefik.io/v1alpha1
2kind: IngressRouteTCP
3metadata:
4  name: submissions
5  namespace: mailserver-oursi
6spec:
7  entryPoints:
8    - submissions
9  routes:
10    - match: HostSNI(`*`)
11      services:
12        - name: mailserver-docker-mailserver
13          port: subs-proxy
14          proxyProtocol:
15            version: 2
16---
17apiVersion: traefik.io/v1alpha1
18kind: IngressRouteTCP
19metadata:
20  name: submission
21  namespace: mailserver-oursi
22spec:
23  entryPoints:
24    - submission
25  routes:
26    - match: HostSNI(`*`)
27      services:
28        - name: mailserver-docker-mailserver
29          port: sub-proxy
30          proxyProtocol:
31            version: 2
32---
33apiVersion: traefik.io/v1alpha1
34kind: IngressRouteTCP
35metadata:
36  name: smtp
37  namespace: mailserver-oursi
38spec:
39  entryPoints:
40    - smtp
41  routes:
42    - match: HostSNI(`*`)
43      services:
44        - name: mailserver-docker-mailserver
45          port: smtp-proxy
46          proxyProtocol:
47            version: 2

On aura aussi besoin d'un certificat TLS pour 'mail.oursi.net' :

1apiVersion: cert-manager.io/v1
2kind: Certificate
3
4metadata:
5  name: mail-tls-certificate-oursi
6
7spec:
8  secretName: mail-tls-certificate-oursi
9  isCA: false
10  privateKey:
11    algorithm: RSA
12    encoding: PKCS1
13    size: 2048
14  dnsNames: [mail.oursi.net]
15  issuerRef:
16    name: letsencrypt-issuer
17    # We can reference ClusterIssuers by changing the kind here.
18    # The default value is Issuer (i.e. a locally namespaced Issuer)
19    kind: ClusterIssuer
20    # This is optional since cert-manager will default to this value however
21    # if you are using an external issuer, change this to that issuer group.
22    group: cert-manager.io

Le réseau est prêt, passons au serveur mail.


3. Déploiement de DMS avec ArgoCD

J'utilise le chart Helm officiel de docker-mailserver, via ArgoCD.

1# filepath: argocd-apps/apps/teleport.yaml
2apiVersion: argoproj.io/v1alpha1
3kind: Application
4metadata:
5  name: mailserver
6  namespace: argocd
7  finalizers:
8    - resources-finalizer.argocd.argoproj.io
9spec:
10  project: default
11  source:
12    repoURL: https://docker-mailserver.github.io/docker-mailserver-helm
13    chart: docker-mailserver
14    targetRevision: 4.2.2
15    helm:
16      values: |
17        ## Specify the name of a TLS secret that contains a certificate and private key for your email domain.
18        ## See https://kubernetes.io/docs/concepts/configuration/secret/#tls-secrets
19        certificate: mail-tls-certificate-oursi
20
21        deployment:
22          env:
23            LOG_LEVEL: info
24            OVERRIDE_HOSTNAME: mail.oursi.net
25
26            ACCOUNT_PROVISIONER: LDAP
27            LDAP_START_TLS: 'yes'
28            LDAP_SERVER_HOST: ldap://ldap.ldap.svc.cluster.local:389
29            LDAP_SEARCH_BASE: ou=mail,dc=oursi,dc=net
30            LDAP_BIND_DN: cn=mailserver,dc=oursi,dc=net
31            LDAP_BIND_PW: <mailserver password>
32            SPOOF_PROTECTION: 1
33
34            ENABLE_SASLAUTHD: 1
35            SASLAUTHD_MECHANISMS: ldap
36            SASLAUTHD_LDAP_SERVER: ldap://ldap.ldap.svc.cluster.local:389/
37            SASLAUTHD_LDAP_START_TLS: 'yes'
38            SASLAUTHD_LDAP_BIND_DN: cn=mailserver,dc=oursi,dc=net
39            SASLAUTHD_LDAP_PASSWORD: <mailserver password>
40            SASLAUTHD_LDAP_SEARCH_BASE: ou=mail,dc=oursi,dc=net
41            SASLAUTHD_LDAP_FILTER: (&(uid=%u@%r)(objectClass=postfixUser))
42
43            ENABLE_POP3: 
44            ENABLE_CLAMAV: 0
45            SMTP_ONLY: 1
46            ENABLE_SPAMASSASSIN: 0
47            ENABLE_FETCHMAIL: 0
48
49        configMaps:
50          user-patches.sh:
51            create: true
52            path: user-patches.sh
53            data: |
54              #!/bin/bash
55
56              # NOTE: Keep in sync with upstream advice:
57              # https://github.com/docker-mailserver/docker-mailserver/blob/v15.0.0/docs/content/examples/tutorials/mailserver-behind-proxy.md?plain=1#L238-L268
58
59              # Duplicate the config for the submission(s) service ports (587 / 465) with adjustments for the PROXY ports (10587 / 10465) and `syslog_name` setting:
60              postconf -Mf submission/inet | sed -e s/^submission/10587/ -e 's/submission/submission-proxyprotocol/' >> /etc/postfix/master.cf
61              postconf -Mf submissions/inet | sed -e s/^submissions/10465/ -e 's/submissions/submissions-proxyprotocol/' >> /etc/postfix/master.cf
62              # Enable PROXY Protocol support for these new service variants:
63              postconf -P 10587/inet/smtpd_upstream_proxy_protocol=haproxy
64              postconf -P 10465/inet/smtpd_upstream_proxy_protocol=haproxy
65
66              # Create a variant for port 25 too (NOTE: Port 10025 is already assigned in DMS to Amavis):
67              postconf -Mf smtp/inet | sed -e s/^smtp/12525/ >> /etc/postfix/master.cf
68              # Enable PROXY Protocol support (different setting as port 25 is handled via postscreen), optionally configure a `syslog_name` to distinguish in logs:
69              postconf -P 12525/inet/postscreen_upstream_proxy_protocol=haproxy 12525/inet/postscreen_cache_map=proxy:btree:\$data_directory/postscreen_12525_cache 12525/inet/syslog_name=postfix/smtpd-proxyprotocol
70              # This is necessary otherwise postscreen will fail when proxy mode is enabled:
71              postconf -e "postscreen_cache_map = proxy:btree:/var/lib/postfix/postscreen_12525_cache"
72
73              # Remove the default smtpd_sasl_local_domain setting ($mydomain) because I want to use the domain from the provided username
74              # This allows me to support logins like sample@oursi.net and sample@vannesson.com both
75              sed -i /etc/postfix/main.cf \
76                  -e '/^smtpd_sasl_local_domain/d'
77
78              rm -f /etc/postfix/{ldap-groups.cf,ldap-domains.cf}
79
80              postconf \
81                  "virtual_mailbox_domains = /etc/postfix/vhost" \
82                  "virtual_alias_maps = ldap:/etc/postfix/ldap-aliases.cf texthash:/etc/postfix/virtual" \
83                  "smtpd_sender_login_maps = ldap:/etc/postfix/ldap-users.cf"
84
85              sed -i /etc/postfix/ldap-users.cf \
86                  -e '/query_filter/d' \
87                  -e '/result_attribute/d' \
88                  -e '/result_format/d'
89              cat <<EOF >> /etc/postfix/ldap-users.cf
90              query_filter = (&(mailacceptinggeneralid=%s)(objectClass=postfixUser))
91              result_attribute = uid
92              EOF
93
94              sed -i /etc/postfix/ldap-aliases.cf \
95                  -e '/domain/d' \
96                  -e '/query_filter/d' \
97                  -e '/result_attribute/d'
98              cat <<EOF >> /etc/postfix/ldap-aliases.cf
99              domain = oursi.net, vannesson.com
100              query_filter = (&(mailacceptinggeneralid=%s)(objectClass=postfixUser))
101              result_attribute = maildrop
102              EOF
103
104              sed -i /etc/postfix/ldap-senders.cf \
105                  -e '/start_tls/d' 
106              cat <<EOF >> /etc/postfix/ldap-senders.cf
107              start_tls = yes
108              EOF
109              
110              echo vannesson.com >> /etc/postfix/vhost
111
112  destination:
113    server: https://kubernetes.default.svc
114    namespace: mailserver-oursi
115  syncPolicy:
116    automated:
117      prune: true
118      selfHeal: true
119    syncOptions:
120      - CreateNamespace=true

Pensez à modifier le mot de passe de mailserver avec celui que vous avez modifié plus tôt. Je vous montrerai commence stocker ce mot de passe dans un secret dans un prochain article.

Principaux points de configuration :

  • Utilise le certificat TLS mail.oursi.net
  • Auth via LDAP (bind sur cn=mailserver)
  • Patch user-patches.sh pour activer Proxy Protocol, mettre à jour les fichiers LDAP postfix, gérer plusieurs domaines (oursi.net, vannesson.com)

Ce script était complexe mais essentiel. Avec ça, DMS devrait être opérationnel. Il ne reste plus qu’à finaliser la configuration DNS pour que les autres serveurs de messagerie nous identifient et nous fassent confiance.


4. Configuration DNS

✉️ Enregistrements MX

Pour oursi.net et pour vannesson.com, je définis :

MX 10 mail.oursi.net

Cela permettra aux autres serveurs de messagerie de savoir où se connecter pour nous délivrer les emails.

✅ SPF

Enregistrements TXT sur oursi.net et sur mail.oursi.net:

v=spf1 a:mail.oursi.net include:_spf.google.com -all

SPF est utilisé pour spécifier quels serveurs de messagerie sont autorisés à envoyer des emails au nom d’un domaine.

🔒 DMARC

DMARC est un protocole d’authentification des emails qui s’appuie sur SPF et DKIM pour permettre aux propriétaires de domaine de définir comment traiter les messages non authentifiés et recevoir des rapports sur l’activité email.

Enregistrement TXT pour _dmarc.oursi.net:

v=DMARC1; p=reject; rua=mailto:dmarc@oursi.net; ruf=mailto:dmarc@oursi.net; fo=1; pct=100;

Assurez-vous que les adresses définies pour rua et ruf sont bien redirigées quelque part (en utilisant LDAP bien sûr 😊).

🖋 DKIM

Dans un shell du pod de DMS:

1setup config dkim keysize 2048 domain oursi.net

Copier le record TXT fourni et l'enregistrer pour _mail._domainkey.oursi.net.

Ca ressemble à ça :

1v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzuBYS9ZsMLwI7lDXYzGxUTyJs8IOYUm2siwfNdHjlaWvLHKS48kiS/r99A8Lr94VI+DcRblVgykbOjJHRhu0D5jeXrHGdbljRRC6Ym6VKDsmzBOSikG6rdDFOucr+RFK9bsnV/51TiMf82TsVSHNs8LOeVkFxOP4eoBeGGM6Mj5NmxJuG9iF+jKVW08NGQ22Bd/7dL17xxKFuO5TWvuqAbYMxLa2ZP6WyaoO7b5KSWCbE76NFKwO81/sgOHeW8hqqiRpscRA5w4yRd10mvRP+cw8cqeRy1QcBRtVIlfq5dTcvIq9OJ6RCQoRtA96x/bh1vnaZPufqAYbrw3P95905QIDAQAB

Il s’agit en réalité d’une clé publique qui sera utilisée pour valider la signature des messages envoyés par Postfix.


5. Verifying the Setup

Test with:

  • Sending/receiving mail to aliases
  • Configure GMAIL to be able to send mails from your domain (you will have to enter your mail server address, 'mail.oursi.net' for me, alongside username and password).

Everything should route to your Gmail now 🎉


5. Vérification de la configuration

Testez :

  • Réception/redirection des mails envoyés aux alias
  • Envoi via Gmail (config SMTP avec mail.oursi.net et vos identifiants)

Tout devrait être routé vers Gmail ❤️


💌 Conclusion

J'ai maintenant :

  • Redirection email gratuite
  • Envoi SMTP depuis Gmail
  • Adresse personnalisée avec mon domaine
  • Authentification et signatures conformes SPF/DKIM/DMARC

Tout est auto-hébergé, sécurisé, et customisable — si vous en avez marre de payer pour de simples redirections, tentez l'expérience !


Amusez-vous bien !