From ec37d3b892b27e09f81bb0cc03674ee4907bb6c2 Mon Sep 17 00:00:00 2001 From: Marc van der Wal Date: Wed, 25 Oct 2023 15:50:21 +0200 Subject: [PATCH] =?UTF-8?q?Premier=20jet=20fonctionnel=20et=20document?= =?UTF-8?q?=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L’essentiel y est, y compris les instructions pour manipuler l’infrastructure et comment suivre les scénarios. --- INSTRUCTIONS.md | 308 ++++++++++++++ README.md | 29 ++ attacker/Dockerfile | 28 ++ attacker/scripts/send_email.py | 268 ++++++++++++ attacker/scripts/spoof_templates/__init__.py | 35 ++ .../scripts/spoof_templates/extorsion.eml | 14 + .../spoof_templates/fake_newsletter.eml | 34 ++ .../spoof_templates/fake_order_confirm.eml | 55 +++ dns/Dockerfile | 21 + dns/etc/nsd/nsd.conf | 31 ++ .../s6-overlay/s6-rc.d/nsd-control-setup/type | 1 + .../s6-overlay/s6-rc.d/nsd-control-setup/up | 1 + .../s6-rc.d/nsd/dependencies.d/base | 0 .../nsd/dependencies.d/nsd-control-setup | 0 dns/etc/s6-overlay/s6-rc.d/nsd/run | 2 + dns/etc/s6-overlay/s6-rc.d/nsd/type | 1 + .../s6-rc.d/unbound/dependencies.d/base | 0 dns/etc/s6-overlay/s6-rc.d/unbound/run | 2 + dns/etc/s6-overlay/s6-rc.d/unbound/type | 1 + .../s6-overlay/s6-rc.d/user/contents.d/nsd | 0 .../s6-rc.d/user/contents.d/unbound | 0 dns/etc/unbound/unbound.conf | 37 ++ dns/zones/arpa/31.172.in-addr.zone | 30 ++ .../arpa/b.8.2.c.4.c.8.0.a.4.d.f.ip6.zone | 30 ++ dns/zones/example.zone | 33 ++ dns/zones/example/attaquant.zone | 32 ++ dns/zones/example/destinataire.zone | 29 ++ dns/zones/example/expediteur.zone | 31 ++ docker-compose.yml | 62 +++ recipient/Dockerfile | 93 +++++ recipient/etc/apache/httpd.conf | 382 +++++++++++++++++ recipient/etc/apache/roundcube.conf | 17 + recipient/etc/dovecot/conf.d/10-auth.conf | 14 + recipient/etc/dovecot/conf.d/10-director.conf | 60 +++ recipient/etc/dovecot/conf.d/10-logging.conf | 105 +++++ recipient/etc/dovecot/conf.d/10-mail.conf | 391 ++++++++++++++++++ recipient/etc/dovecot/conf.d/10-master.conf | 133 ++++++ recipient/etc/dovecot/conf.d/10-metrics.conf | 74 ++++ recipient/etc/dovecot/conf.d/10-ssl.conf | 85 ++++ recipient/etc/dovecot/conf.d/15-lda.conf | 48 +++ .../etc/dovecot/conf.d/15-mailboxes.conf | 73 ++++ recipient/etc/dovecot/conf.d/20-imap.conf | 99 +++++ recipient/etc/dovecot/conf.d/90-acl.conf | 19 + recipient/etc/dovecot/conf.d/90-plugin.conf | 11 + recipient/etc/dovecot/conf.d/90-quota.conf | 83 ++++ .../conf.d/auth-checkpassword.conf.ext | 21 + .../etc/dovecot/conf.d/auth-deny.conf.ext | 15 + .../etc/dovecot/conf.d/auth-dict.conf.ext | 16 + .../etc/dovecot/conf.d/auth-master.conf.ext | 16 + .../dovecot/conf.d/auth-passwdfile.conf.ext | 20 + .../etc/dovecot/conf.d/auth-static.conf.ext | 24 ++ .../etc/dovecot/conf.d/auth-system.conf.ext | 74 ++++ .../etc/dovecot/dovecot-dict-auth.conf.ext | 54 +++ recipient/etc/dovecot/dovecot-oauth2.conf.ext | 69 ++++ recipient/etc/dovecot/dovecot-openssl.cnf | 31 ++ recipient/etc/dovecot/dovecot.conf | 7 + recipient/etc/opendkim/opendkim.conf | 15 + recipient/etc/opendmarc/opendmarc.conf | 15 + recipient/etc/postfix/main.cf | 23 ++ recipient/etc/postfix/master.cf | 37 ++ recipient/etc/roundcube/config.inc.php | 37 ++ recipient/etc/s6-overlay/s6-rc.d/apache/run | 3 + recipient/etc/s6-overlay/s6-rc.d/apache/type | 1 + recipient/etc/s6-overlay/s6-rc.d/dovecot/run | 2 + recipient/etc/s6-overlay/s6-rc.d/dovecot/type | 1 + recipient/etc/s6-overlay/s6-rc.d/opendkim/run | 2 + .../etc/s6-overlay/s6-rc.d/opendkim/type | 1 + .../etc/s6-overlay/s6-rc.d/opendmarc/run | 2 + .../etc/s6-overlay/s6-rc.d/opendmarc/type | 1 + recipient/etc/s6-overlay/s6-rc.d/postfix/run | 2 + recipient/etc/s6-overlay/s6-rc.d/postfix/type | 1 + .../s6-overlay/s6-rc.d/user/contents.d/apache | 0 .../s6-rc.d/user/contents.d/dovecot | 0 .../s6-rc.d/user/contents.d/opendkim | 0 .../s6-rc.d/user/contents.d/opendmarc | 0 .../s6-rc.d/user/contents.d/postfix | 0 recipient/var/db/public_suffix_list.dat | 7 + sender/Dockerfile | 31 ++ sender/etc/opendkim/key_table | 11 + sender/etc/opendkim/opendkim.conf | 22 + sender/etc/opendkim/signing_table | 11 + sender/etc/postfix/main.cf | 16 + sender/etc/postfix/master.cf | 37 ++ sender/etc/s6-overlay/s6-rc.d/opendkim/run | 2 + sender/etc/s6-overlay/s6-rc.d/opendkim/type | 1 + sender/etc/s6-overlay/s6-rc.d/postfix/run | 2 + sender/etc/s6-overlay/s6-rc.d/postfix/type | 1 + .../s6-rc.d/user/contents.d/opendkim | 0 .../s6-rc.d/user/contents.d/postfix | 0 sender/scripts/send_confirmation_email.sh | 54 +++ sender/scripts/send_newsletter.sh | 24 ++ 91 files changed, 3511 insertions(+) create mode 100644 INSTRUCTIONS.md create mode 100644 README.md create mode 100644 attacker/Dockerfile create mode 100755 attacker/scripts/send_email.py create mode 100644 attacker/scripts/spoof_templates/__init__.py create mode 100644 attacker/scripts/spoof_templates/extorsion.eml create mode 100644 attacker/scripts/spoof_templates/fake_newsletter.eml create mode 100644 attacker/scripts/spoof_templates/fake_order_confirm.eml create mode 100644 dns/Dockerfile create mode 100644 dns/etc/nsd/nsd.conf create mode 100644 dns/etc/s6-overlay/s6-rc.d/nsd-control-setup/type create mode 100644 dns/etc/s6-overlay/s6-rc.d/nsd-control-setup/up create mode 100644 dns/etc/s6-overlay/s6-rc.d/nsd/dependencies.d/base create mode 100644 dns/etc/s6-overlay/s6-rc.d/nsd/dependencies.d/nsd-control-setup create mode 100644 dns/etc/s6-overlay/s6-rc.d/nsd/run create mode 100644 dns/etc/s6-overlay/s6-rc.d/nsd/type create mode 100644 dns/etc/s6-overlay/s6-rc.d/unbound/dependencies.d/base create mode 100644 dns/etc/s6-overlay/s6-rc.d/unbound/run create mode 100644 dns/etc/s6-overlay/s6-rc.d/unbound/type create mode 100644 dns/etc/s6-overlay/s6-rc.d/user/contents.d/nsd create mode 100644 dns/etc/s6-overlay/s6-rc.d/user/contents.d/unbound create mode 100644 dns/etc/unbound/unbound.conf create mode 100644 dns/zones/arpa/31.172.in-addr.zone create mode 100644 dns/zones/arpa/b.8.2.c.4.c.8.0.a.4.d.f.ip6.zone create mode 100644 dns/zones/example.zone create mode 100644 dns/zones/example/attaquant.zone create mode 100644 dns/zones/example/destinataire.zone create mode 100644 dns/zones/example/expediteur.zone create mode 100644 docker-compose.yml create mode 100644 recipient/Dockerfile create mode 100644 recipient/etc/apache/httpd.conf create mode 100644 recipient/etc/apache/roundcube.conf create mode 100644 recipient/etc/dovecot/conf.d/10-auth.conf create mode 100644 recipient/etc/dovecot/conf.d/10-director.conf create mode 100644 recipient/etc/dovecot/conf.d/10-logging.conf create mode 100644 recipient/etc/dovecot/conf.d/10-mail.conf create mode 100644 recipient/etc/dovecot/conf.d/10-master.conf create mode 100644 recipient/etc/dovecot/conf.d/10-metrics.conf create mode 100644 recipient/etc/dovecot/conf.d/10-ssl.conf create mode 100644 recipient/etc/dovecot/conf.d/15-lda.conf create mode 100644 recipient/etc/dovecot/conf.d/15-mailboxes.conf create mode 100644 recipient/etc/dovecot/conf.d/20-imap.conf create mode 100644 recipient/etc/dovecot/conf.d/90-acl.conf create mode 100644 recipient/etc/dovecot/conf.d/90-plugin.conf create mode 100644 recipient/etc/dovecot/conf.d/90-quota.conf create mode 100644 recipient/etc/dovecot/conf.d/auth-checkpassword.conf.ext create mode 100644 recipient/etc/dovecot/conf.d/auth-deny.conf.ext create mode 100644 recipient/etc/dovecot/conf.d/auth-dict.conf.ext create mode 100644 recipient/etc/dovecot/conf.d/auth-master.conf.ext create mode 100644 recipient/etc/dovecot/conf.d/auth-passwdfile.conf.ext create mode 100644 recipient/etc/dovecot/conf.d/auth-static.conf.ext create mode 100644 recipient/etc/dovecot/conf.d/auth-system.conf.ext create mode 100644 recipient/etc/dovecot/dovecot-dict-auth.conf.ext create mode 100644 recipient/etc/dovecot/dovecot-oauth2.conf.ext create mode 100644 recipient/etc/dovecot/dovecot-openssl.cnf create mode 100644 recipient/etc/dovecot/dovecot.conf create mode 100644 recipient/etc/opendkim/opendkim.conf create mode 100644 recipient/etc/opendmarc/opendmarc.conf create mode 100644 recipient/etc/postfix/main.cf create mode 100644 recipient/etc/postfix/master.cf create mode 100644 recipient/etc/roundcube/config.inc.php create mode 100644 recipient/etc/s6-overlay/s6-rc.d/apache/run create mode 100644 recipient/etc/s6-overlay/s6-rc.d/apache/type create mode 100644 recipient/etc/s6-overlay/s6-rc.d/dovecot/run create mode 100644 recipient/etc/s6-overlay/s6-rc.d/dovecot/type create mode 100644 recipient/etc/s6-overlay/s6-rc.d/opendkim/run create mode 100644 recipient/etc/s6-overlay/s6-rc.d/opendkim/type create mode 100644 recipient/etc/s6-overlay/s6-rc.d/opendmarc/run create mode 100644 recipient/etc/s6-overlay/s6-rc.d/opendmarc/type create mode 100644 recipient/etc/s6-overlay/s6-rc.d/postfix/run create mode 100644 recipient/etc/s6-overlay/s6-rc.d/postfix/type create mode 100644 recipient/etc/s6-overlay/s6-rc.d/user/contents.d/apache create mode 100644 recipient/etc/s6-overlay/s6-rc.d/user/contents.d/dovecot create mode 100644 recipient/etc/s6-overlay/s6-rc.d/user/contents.d/opendkim create mode 100644 recipient/etc/s6-overlay/s6-rc.d/user/contents.d/opendmarc create mode 100644 recipient/etc/s6-overlay/s6-rc.d/user/contents.d/postfix create mode 100644 recipient/var/db/public_suffix_list.dat create mode 100644 sender/Dockerfile create mode 100644 sender/etc/opendkim/key_table create mode 100644 sender/etc/opendkim/opendkim.conf create mode 100644 sender/etc/opendkim/signing_table create mode 100644 sender/etc/postfix/main.cf create mode 100644 sender/etc/postfix/master.cf create mode 100644 sender/etc/s6-overlay/s6-rc.d/opendkim/run create mode 100644 sender/etc/s6-overlay/s6-rc.d/opendkim/type create mode 100644 sender/etc/s6-overlay/s6-rc.d/postfix/run create mode 100644 sender/etc/s6-overlay/s6-rc.d/postfix/type create mode 100644 sender/etc/s6-overlay/s6-rc.d/user/contents.d/opendkim create mode 100644 sender/etc/s6-overlay/s6-rc.d/user/contents.d/postfix create mode 100755 sender/scripts/send_confirmation_email.sh create mode 100755 sender/scripts/send_newsletter.sh diff --git a/INSTRUCTIONS.md b/INSTRUCTIONS.md new file mode 100644 index 0000000..1aca28c --- /dev/null +++ b/INSTRUCTIONS.md @@ -0,0 +1,308 @@ +# Instructions détaillées + +## Démarrage et prise en main + +Le plus simple est de faire fonctionner le démonstrateur sur une machine virtuelle Linux dotée de Docker qui soit accessible en SSH. + +Le démonstrateur démarre un serveur Web sur le port 8225. Pour des raisons de sécurité, ce service n’est accessible que sur la boucle locale de votre machine virtuelle. Pour pouvoir l’utiliser sur votre machine, il vous faut vous connecter à la machine qui exécute le démonstrateur à l’aide de la commande : + + $ ssh -L 8225:localhost:8225 + +Vérifiez que Docker et `tmux` soient bien installées sur la machine exécutant le démonstrateur. + +Rendez-vous dans le répertoire dans lequel vous avez cloné le dépôt Git du démonstrateur, puis démarrez l’environnement en exécutant le script `start.sh` : + + $ ./start.sh + +Vous devriez alors voir s’afficher une fenêtre comme celle-ci (TODO capture d’écran) + +Notez la dernière ligne du terminal, qui indique une liste de fenêtres. Ce sont des onglets : vous pouvez cliquer sur l’un des cinq onglets (« Système », « Expéditeur », « Destinataire », « Attaquant », « DNS ») pour changer de point de vue. + +Si la souris ne fonctionne pas, il est possible d’utiliser le clavier pour changer de fenêtre : pour cela, tapez **Ctrl+B**, relâchez toutes les touches, puis tapez le chiffre entre parenthèses (compris entre 0 et 4 inclus) correspondant au numéro de la fenêtre que vous souhaitez afficher. + +En parallèle, dans un navigateur, ouvrez un nouvel onglet et connectez-vous à l’adresse . Vous devriez obtenir une page de connexion pour la webmail Roundcube. Connectez-vous avec l’identifiant **destinataire** et le mot de passe **PasSecretDuTout**. Vous devriez alors arriver sur une boîte mail entièrement vierge. + +## La fausse confirmation de commande + +Nous allons étudier le cas d’un attaquant qui usurpe l’identité de l’entreprise pour envoyer de fausses confirmations de commande. Ces confirmations de commande, qui font croire à une commande pour des milliers d’euros de produits, sont conçues pour faire peur et pousser la victime à cliquer sur le lien d’annulation présent dans le mail et remplir ses informations de carte bleue. + +### Envoi d’un mail légitime + +Tout d’abord, pour tester le système, envoyez un mail de confirmation de commande légitime en utilisant l’infrastructure de l’expéditeur : + + 1. Dans le terminal, basculez sur la fenêtre **Expéditeur**. + + 2. Exécutez le script d’envoi de mail de confirmation avec la commande `./scripts/send_confirmation_email.sh` (vous pouvez utiliser la touche **Tab** pour compléter les noms de fichiers et de répertoires). + + 3. Dans la boîte mail du destinataire devrait apparaître un nouveau message : une confirmation de commande pour une savonnette. + +### Envoi d’un mail frauduleux + +Il est maintenant temps de passer du côté de l’attaquant et d’envoyer votre premier courriel frauduleux. Pour cela : + + 1. Dans le terminal, basculez sur la fenêtre **Attaquant**. + + 2. Exécutez le script de l’attaquant avec la commande `./scripts/send_email.py`. + + 3. Sélectionnez le scénario **Fausse confirmation de commande** en tapant **2** puis **Entrée**. + + 4. Utilisez la même adresse dans l’enveloppe SMTP que dans le corps du mail : sélectionnez le choix **1**. + + 5. Utilisez le HELO par défaut en tapant directement **Entrée**. + + 6. N’ajoutez pas de signature DKIM en tapant directement **Entrée**. + +Le script remet ensuite le courriel frauduleux au serveur du destinataire. Ce faisant, le programme affiche les échanges au moyen du protocole SMTP. + +La première ligne, `220 mx.destinataire.example ESMTP Postfix`, indique que le serveur est prêt. Notez en particulier : + + * la commande `MAIL FROM`, qui donne une adresse e-mail qui n’est pas celle de l’attaquant ; + * le corps du message, transmise après la commande `DATA`, qui contient une ligne `From` dont l’adresse e-mail est également fausse. + +Et lorsque vous affichez la boîte de réception du destinataire, vous verrez qu’une fausse confirmation de commande a bien été remise dans la boîte de réception. + +Vous venez de réaliser votre première usurpation d’identité ! + +### Installation de la politique SPF + +Nous allons maintenant nous défendre contre ce scénario précis en déployant une politique SPF. + +Avec SPF, nous allons spécifier que seul le serveur de `expediteur.example`, c’est-à-dire la machine qui a envoyé la vraie confirmation de commande, a le droit d’envoyer des courriels avec ce nom de domaine. Cela se fait en ajoutant une entrée TXT dans la zone `expediteur.example` dont le contenu suit les règles de SPF. + +Pour cela : + + 1. Dans le terminal, basculez sur la fenêtre **DNS**. + + 2. Ouvrez le fichier de zones pour `expediteur.example` avec la commande : + + nano example/expediteur.zone + + 3. Utilisez les flèches du clavier pour atteindre la ligne repérée par le commentaire `;; Politique SPF`, puis supprimez le caractère `;` en début de ligne pour activer la politique SPF. + + 4. Sauvegardez le fichier de zone modifié (tapez **Ctrl+O** puis **Entrée**), puis quittez (en tapant **Ctrl+X**). + + 5. De retour à l’invite de commandes, rechargez le fichier de zones avec la commande : + + nsd-control reload expediteur.example + + Vous devriez voir le message « ok ». Sinon, cela signifie qu’une erreur de syntaxe s’est glissée dans le fichier de zone. + + 6. Interrogez le DNS pour vérifier que votre politique SPF a été bien installée, avec la commande : + + dig TXT expediteur.example + + Dans l’affichage de cette commande, vous devriez pouvoir identifier la politique SPF que vous venez d’installer. + +Pour les lecteurs avertis : normalement, après chaque modification d’un fichier de zone, il faudrait incrémenter le numéro de série contenu dans l’entrée SOA. Pour simplifier les choses, le démonstrateur a été conçu pour que cette manipulation ne soit pas nécessaire. Par ailleurs, les TTL de toutes les entrées DNS servies dans ce démonstrateur est fixé à 0 pour que les effets soient visibles immédiatement, juste en rechargeant le fichier de zones dans `nsd`. + +### Activation du contrôle SPF chez le destinataire + +Ce que vous venez de faire est la configuration nécessaire chez l’expéditeur. + +Cependant, si vous réessayez d’envoyer le même mail frauduleux via la fenêtre « Attaquant », vous verrez que le mail passe toujours. Comment est-ce possible ? + +Vous avez certes installé une politique SPF pour `expediteur.example`, mais le serveur de courriels chez `destinataire.example` ne fait pas encore de contrôle de politiques SPF. Il s’agit d’activer cela. Pour ce faire, procédez ainsi : + + 1. Dans le terminal, basculez sur la fenêtre **Destinataire**. + + 2. Ouvrez le fichier de configuration principal du serveur mail dans un éditeur de texte : + + nano /etc/postfix/main.cf + + 3. Repérer la ligne qui suit le commentaire « Décommenter pour activer le contrôle SPF au niveau SMTP » (celle qui commence par `smtpd_recipient_restrictions`). Sur cette ligne, supprimez le caractère `#` et l’espace qui suivait ce caractère (c’est important ; ne laissez pas de blanc au début de la ligne). + + 4. Sauvegardez le fichier ainsi modifié (**Ctrl+O**, **Entrée**) puis quittez (**Ctrl+X**). + + 5. Informez le serveur de courriel que sa configuration a été modifiée, qu’il doit la relire et la prendre en compte : + + postfix reload + + Un message devrait apparaître, indiquant que Postfix est en train de relire la configuration. + +### Victoire ? + +En enfilant le chapeau de l’attaquant, essayez d’envoyer une fausse confirmation de commande avec les mêmes paramètres que plus haut. Vous verrez que les choses ne se passent plus comme prévu : le serveur de courriels du destinataire refuse désormais le message. En fait, l’attaquant n’a même pas l’occasion d’envoyer le contenu du courriel, car c’est l’adresse de l’expéditeur dans la commande `MAIL FROM` qui est contrôlée. + +D’ailleurs, vous pouvez vérifier que les vraies confirmations de commande passent toujours : depuis la fenêtre **Expéditeur**, relancez le script d’envoi d’e-mail de confirmation. Vous verrez ce courriel apparaître quelques secondes plus tard dans la boîte de réception du destinataire. + +## SPF tout seul ne suffit pas ## + +Peut-on crier victoire ? Pas tout de suite. L’attaquant peut encore contourner cette mesure. + +Sur un courrier physique, on peut écrire une adresse retour sur l’enveloppe et une adresse différente sur le papier à en-têtes qu’on glisse dans l’enveloppe. Un attaquant peut faire la même chose avec un courriel, ce qui a pour effet de contourner la protection que devait apporter SPF. + +### Envoi d’un mail frauduleux avec une enveloppe différente ### + +Pour voir cela en action, procédez ainsi : + + 1. Dans le terminal, basculez sur la fenêtre **Attaquant**. + + 2. Exécutez le script de l’attaquant avec la commande `./scripts/send_email.py`. + + 3. Sélectionnez le scénario **Fausse confirmation de commande** en tapant **2** puis **Entrée**. + + 4. Cette fois, utilisez une adresse différente dans l’enveloppe SMTP : sélectionnez le choix **2**, et non pas **1** comme vous le faisiez précédemment. + + 5. Utilisez le HELO par défaut ; tapez directement **Entrée**. + + 6. N’ajoutez pas de signature DKIM ; tapez directement **Entrée**. + +Cette fois, le courriel est accepté par le serveur SMTP : l’attaquant a prétendu que le courriel frauduleux venait de chez lui dans l’enveloppe ; mais dans le courriel lui-même, visible après la commande DATA, l’attaquant se fait toujours passer pour le service client. De plus, c’est toujours l’adresse du service client qui apparaît dans les en-têtes que voit le destinataire dans son logiciel de courriels. + +Pour se défendre contre cela, nous avons besoin de DMARC. DMARC requiert que l’expéditeur ait mis en place SPF, mais aussi DKIM. Nous allons donc installer DKIM dans un premier temps, puis mettre en place DMARC pour compléter notre protection. + +### Génération de clefs DKIM ### + +DKIM consiste à signer cryptographiquement le corps et certains en-têtes de courriels. Dans l’infrastructure de l’expéditeur, c’est le logiciel **opendkim** qui se charge d’insérer les signatures. Ce même logiciel est aussi responsable de la vérification des signatures DKIM chez le destinataire. + +La première chose à faire est de générer des clefs DKIM chez l’expéditeur. On en génère deux : le premier est utilisé tout de suite et le second est une clef de réserve. Les clefs cryptographiques ont besoin d’être renouvelées régulièrement, donc lorsque la première clef est retirée du service, une troisième clef est générée et toutes les signatures se font avec la deuxième clef. + +Générons des clefs DKIM pour le domaine `expediteur.example`. Pour cela, procédez ainsi : + + 1. Dans le terminal, basculez sur la fenêtre **Expéditeur**. + + 2. Créez l’arborescence de répertoires nécessaire pour gérer proprement les clefs : + + cd /etc/opendkim/keys + mkdir expediteur.example + cd expediteur.example + + 3. Génerez ensuite deux clefs. On utilise les sélecteurs `demo1` et `demo2`. Le choix des sélecteurs est libre (vous pouvez choisir `toto1` et `toto2` par exemple), mais il faut utiliser les bons sélecteurs partout pour que les courriels soient correctement signés. + + Tapez deux fois la commande `opendkim-genkey` ci-dessous, ceci afin de bien générer deux clefs différentes : + + opendkim-genkey -d expediteur.example -b 2048 -s demo1 + opendkim-genkey -d expediteur.example -b 2048 -s demo2 + + 4. Donnez les bonnes permissions pour que le logiciel **opendkim** puisse accéder aux clefs privées : + + chown -R opendkim /etc/opendkim/keys + +Les clefs sont maintenant générées. Les clefs privées, à garder secrètes, se trouvent dans des fichiers appelés `demo1.private` et `demo2.private`. Les clefs publiques, quant à elles, se trouvent dans les fichiers `demo1.txt` et `demo2.txt` et doivent être publiées dans le DNS. + +### Publication des clefs publiques DKIM dans le DNS ### + +L’étape suivante est donc de publier les clefs publiques dans le DNS : + + 1. Toujours dans la fenêtre **Expéditeur**, obtenez les clefs publiques : + + cat demo1.txt demo2.txt + + 2. Les clefs publiques se présentent sous le format nécessaire pour un fichier de zone DNS. Sélectionnez à la souris l’intégralité de la sortie de la commande précédente (il se peut que vous ayez besoin de maintenir la touche **Maj** enfoncée pour pouvoir sélectionner), puis copiez cette sélection dans le presse-papiers. + + 3. Basculez sur la fenêtre **DNS**. + + 4. Éditez le fichier de zone pour le domaine `expediteur.zone`, comme pour l’installation de la politique SPF : + + nano example/expediteur.zone + + 5. Placez le curseur au début de la ligne suivant immédiatement le commentaire `;; Clef publique DKIM`. Supprimez cette ligne, puis collez le contenu du presse-papiers (suivant le terminal que vous utilisez, la combinaison de touches à utiliser est **Ctrl+Maj+V** ou **Maj+Inser**). Il se peut que l’affichage de l’éditeur `nano` soit perturbé par ce collage ; tapez **Ctrl+L** pour forcer un rafraîchissement complet de l’écran. Vérifiez que vous avez bien ajouté deux entrées DNS de type TXT : une pour chaque clef. + + 6. Sauvegardez et quittez l’éditeur. Rechargez la zone : + + nsd-control reload expediteur.example + + 7. Vérifiez la bonne présence de clefs DKIM avec `dig` : + + dig TXT demo1._domainkey.expediteur.example + dig TXT demo2._domainkey.expediteur.example + +### Terminer la configuration de DKIM chez l’expéditeur ### + +Nous avons donc bien publié nos clefs DKIM dans le DNS. Finissons de configurer **opendkim** chez l’expéditeur : + + 1. Rebasculez sur la fenêtre **Expéditeur**. + + 2. Éditez le fichier `/etc/opendkim/opendkim.conf` et décommentez les deux lignes, à la fin du fichier, commençant par `SigningTable` et par `KeyTable` respectivement. Là encore, pensez bien à supprimer le caractère `#` mais aussi l’espace qui le suivait. + + 3. Éditez le fichier `/etc/opendkim/signing_table` et décommentez la ligne commençant par `expediteur.example`. Ceci active l’ajout de signatures DKIM pour ce domaine et spécifie que la clef de signature à utiliser est celle dont le sélecteur est `demo1`. + + 4. Éditez le fichier `/etc/opendkim/key_table` et décommentez les deux lignes commençant par `demo1._domainkey.expediteur.example` et `demo2._domainkey.expediteur.example` respectivement. Ceci spécifie l’emplacement des fichiers de clef privées associées aux clefs que nous comptons utiliser pour `expediteur.example`. + + 5. Informez **opendkim** de la nécessité de recharger sa configuration en tapant la commande : + + killall -USR1 opendkim + +Le serveur Postfix de l’expéditeur est déjà configuré pour que les courriels sortants passent par **opendkim** avant d’être remis au serveur mail destinataire. Mais il faut aussi que le serveur de courriels du destinataire vérifie les signatures ! + +### Configurer la vérification DKIM et DMARC chez le destinataire ### + +Nous allons faire d’une pierre deux coups et activer à la fois la vérification DKIM et DMARC chez le destinataire. Pour cela : + + 1. Basculez sur la fenêtre **Destinataire**. + + 2. Placez-vous sur la ligne qui suit le commentaire `# Décommenter pour activer DKIM et DMARC`. Supprimez le `#` et l’espace qui suivait ce caractère. + + 3. Sauvegardez, quittez puis notifiez Postfix d’un changement de configuration (`postfix reload`). + +### Essai de DKIM ### + +Nous allons maintenant envoyer une nouvelle confirmation de commande, qui devrait désormais arriver dans la boîte de réception du destinataire munie d’une signature DKIM : + + 1. Basculez sur la fenêtre **Expéditeur**. + + 2. Renvoyez le mail de confirmation de commande : + + cd /home/expediteur + ./scripts/send_confirmation_email.sh + +Vous devriez voir un nouveau courriel non lu chez le destinataire. Ouvrez-le, puis cliquez sur « En-têtes » pour visionner l’intégralité des en-têtes. Notez l’apparition de plusieurs nouveaux en-têtes. + +Les en-têtes **Authentication-Results** ont été insérés par le système destinataire, au moment du contrôle SPF, DKIM (et aussi DMARC). Notez le `spf=pass`, `dkim=pass` et `dmarc=none` : cela signifie que le courriel a été émis par un serveur autorisé par la politique SPF, que la signature DKIM est bonne mais qu’il n’y avait aucune politique DMARC. + +Vous devriez aussi voir un en-tête **DKIM-Signature**, contenant la signature DKIM. + +#### Dépannage #### + +Si le courriel n’arrive pas, il se peut qu’il n’ait pas pu être signé par opendkim. Le symptôme est l’apparition, dans la fenêtre **Système**, de messages comme : + + milter-reject: END-OF-MESSAGE from localhost[127.0.0.1]: 4.7.1 Service unavailable - try again later; from= to= + +Dans ce cas : + + * vérifiez qu’il n’y ait pas eu d’erreurs dans la configuration d’opendkim ; + * vérifiez que le compte UNIX `opendkim` ait bien accès aux clefs privées. + +Après contrôle, il est possible de resoumettre le message avec la commande `postqueue -f`. Le courriel devrait arriver dans la boîte du destinataire. + +## DMARC, la clef de voûte ## + +À ce stade, nous n’avons toujours pas contrecarré la campagne de hameçonnage que mène l’attaquant au moyen de ces fausses confirmations de commande. C’est là où DMARC entre en jeu. + +Maintenant que le système destinataire contrôle SPF, DKIM et DMARC, nous pouvons protéger l’expéditeur en ajoutant une politique DMARC. + +### Configuration DMARC chez l’expéditeur ### + +Pour configurer DMARC chez l’expéditeur : + + 1. Basculez sur la fenêtre **DNS**. + + 2. Ouvrez une nouvelle fois le fichier de zone du domaine `expediteur.example`. + + 3. Repérez la ligne `;; Politique DMARC`. Décommentez la ligne suivante en supprimant le caractère `;`. Changez le `p=none` en `p=reject`. + + 4. Rechargez le fichier de zones, puis vérifiez la bonne publication de la politique DMARC avec la commande `dig TXT _dmarc.expediteur.example`. + +### L’attaquant est-il vaincu ? ### + +Voyons voir maintenant si les mails d’hameçonnage de l’attaquant passent encore. Pour rappel : + + 1. Dans le terminal, basculez sur la fenêtre **Attaquant**. + + 2. Exécutez le script de l’attaquant avec la commande `./scripts/send_email.py`. + + 3. Sélectionnez le scénario **Fausse confirmation de commande** (choix **2**). + + 4. Utilisez une adresse différente dans l’enveloppe SMTP (choix **2**). + + 5. Utilisez le HELO par défaut ; tapez directement **Entrée**. + + 6. N’ajoutez pas de signature DKIM ; tapez directement **Entrée**. + +Cette fois, après que l’attaquant ait envoyé le mail complet, le serveur de courriels du système destinataire effectue le contrôle SPF, DKIM et DMARC. Le message ne passe pas le contrôle et est rejeté par le serveur : on peut le voir avec l’erreur `550 5.7.1 rejected by DMARC policy for expediteur.example`, vers la fin de la conversation. + +Vous pourrez ensuite vérifier que les confirmations de commande authentiques passent encore. + +## Et les sous-domaines ? ## + +(TODO : Les newsletters ne marchent plus ; c’est parce que la politique DMARC `p=reject` s’applique aux sous-domaines de `expediteur.example`. Il manque aussi une politique SPF pour `newsletter.expediteur.example` et des clefs DKIM pour ce sous-domaine.) diff --git a/README.md b/README.md new file mode 100644 index 0000000..94731e9 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Démonstrateur SPF, DKIM et DMARC + +Ce démonstrateur est une infrastructure minimale sur lequel SPF, DKIM et DMARC doivent être déployés. Le but est d’illustrer le fonctionnement de ces protocoles, montrer comment ils peuvent éventuellement être contournés et enfin de servir de support de formation ou de travaux pratiques. + +Ce système est conçu pour pouvoir alterner entre trois points de vue : + + * d’une entreprise qui cherche à se protéger contre l’usurpation d’identité dans les courriels ; + + * d’une « personne lambda » qui reçoit à la fois des mails de cette entreprise mais aussi des tentatives de hameçonnage ; + + * d’un attaquant, muni d’outils pour envoyer des faux mails convaincants. + +Il est composé de quatre conteneurs Docker : + + * **dns** : contient un résolveur DNS (unbound) et un serveur faisant autorité (nsd), dont les fichiers de zone peuvent être édités pour introduire SPF, DKIM et DMARC + + * **sender** : le système mail de l’entreprise, émetteur de mails authentiques ; + + * **attacker** : le système mail de l’attaquant ; + + * **recipient** : le système mail destinataire des mails de l’entreprise et de l’attaquant. + +Pour commencer, clonez le dépôt et tapez la commande : + + $ docker compose build + +Suivez ensuite les [instructions détaillées]. + +[instructions détaillées]: INSTRUCTIONS.md diff --git a/attacker/Dockerfile b/attacker/Dockerfile new file mode 100644 index 0000000..3ba1fbd --- /dev/null +++ b/attacker/Dockerfile @@ -0,0 +1,28 @@ +FROM alpine:latest + +WORKDIR /home/attaquant + +RUN adduser -D attaquant + +RUN apk add \ + bash \ + bind-tools \ + curl \ + ncurses-terminfo-base \ + opendkim-libs \ + opendkim-utils \ + perl \ + python3 \ + py3-dnspython \ + py3-pip \ + py3-rich + +RUN pip install dkimpy + +# TODO mettre un bashrc qui positionne un prompt qui montre bien qu’on est +# en train d’être méchant + +USER attaquant +ENTRYPOINT ["/bin/bash"] + +COPY --chown=attaquant scripts /home/attaquant/scripts diff --git a/attacker/scripts/send_email.py b/attacker/scripts/send_email.py new file mode 100755 index 0000000..5bf0c23 --- /dev/null +++ b/attacker/scripts/send_email.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python3 + +from email.message import EmailMessage +import email.utils +import importlib.resources +import pathlib +import re +import smtplib +import sys +import uuid + +import dkim +from rich.console import Console +import rich.highlighter +import rich.panel +from rich.prompt import Confirm, Prompt +import rich.text +from rich.theme import Theme + +import spoof_templates + +DEFAULT_HELO = "attaquant.example" +ATTACKER_DOMAIN = "attaquant.example" +ATTACKER_MAIL_FROM = "usurpateur@attaquant.example" +VICTIM_NAME = "Destinataire" +VICTIM_DOMAIN = "destinataire.example" +VICTIM_MX = "mx.destinataire.example" +VICTIM_ADDRESS = f"destinataire@{VICTIM_DOMAIN}" + + +class SMTPHighlighter(rich.highlighter.Highlighter): + patterns = { + 'helo': r'(?:HELO|EHLO) (?P.*)', + 'mail_from': r'MAIL FROM:<(?P.*?)>', + 'rcpt_to': r'RCPT TO:<(?P.*)>', + 'data': r'From: (?P.* <(?P.*)>)' + } + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.state = 'connect' + + def highlight(self, text): + if pattern := SMTPHighlighter.patterns.get(self.state, None): + if mobj := re.match(pattern, text.plain): + for style in mobj.groupdict().keys(): + start, end = mobj.start(style), mobj.end(style) + text.stylize(style, start, end) + + + + +custom_theme = Theme({ + "banner": "bold red", + "incoming": "yellow", + "outgoing": "cyan", + "helo": "bold sea_green1", + "mail_from": "bold underline sky_blue1", + "rcpt_to": "bold underline pale_violet_red1", + "data_from": "bold underline medium_purple1", + "data_display_name": "medium_purple1", + "comment": "#777777", + "failure": "bold red", + "success": "green" + }) +console = Console(highlighter=None, theme=custom_theme) + + +class SMTP(smtplib.SMTP): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.highlighter = SMTPHighlighter() + + def trace(self, s, direction): + if direction not in ['in', 'out']: + raise ValueError("direction must be 'in' or 'out'") + + arrows = {'in': '[incoming]←[/]', 'out': '[outgoing]→[/]'} + max_lines = 4 + + if isinstance(s, bytes): + s = s.decode('utf-8') + lines = [self.highlighter(rich.text.Text(line)) for line in s.splitlines()] + if len(lines) > max_lines: + suppressed_count = len(lines[max_lines:-1]) + suppressed_text_obj = rich.text.Text(f"({suppressed_count} ") + if suppressed_count > 1: + suppressed_text_obj.append("lignes omises") + else: + suppressed_text_obj.append("ligne omise") + suppressed_text_obj.append(")") + suppressed_text_obj.stylize("comment") + + lines = lines[:max_lines] + [suppressed_text_obj] + lines[-1:] + + for i, l in enumerate(lines): + prefix = arrows[direction] if i == 0 else " " + console.print(f" {prefix} ", end=None) + console.print(l) + + if direction == 'in': + console.print() + + def putcmd(self, cmd, args=''): + super().putcmd(cmd.upper(), args) + + def send(self, s): + self.trace(s, 'out') + super().send(s) + + def getreply(self): + errcode, errmsg = super().getreply() + lines = errmsg.decode('us-ascii').splitlines() + lines = [ + f"{errcode}{' ' if i == len(lines) - 1 else '-'}{line}" + for i, line in enumerate(lines) + ] + self.trace('\n'.join(lines), 'in') + return errcode, errmsg + + def connect(self, *args, **kwargs): + console.print(" * Connexion établie au serveur", style="comment") + return super().connect(*args, **kwargs) + + def ehlo(self, *args, **kwargs): + self.highlighter.state = 'helo' + return super().ehlo(*args, **kwargs) + + def mail(self, *args, **kwargs): + self.highlighter.state = 'mail_from' + return super().mail(*args, **kwargs) + + def rcpt(self, *args, **kwargs): + self.highlighter.state = 'rcpt_to' + return super().rcpt(*args, **kwargs) + + def data(self, *args): + self.highlighter.state = 'data' + return super().data(*args) + + + +def ask_helo(): + response = Prompt.ask("Nom d’hôte dans le [helo]HELO[/helo]", + console=console, + default=DEFAULT_HELO) + return response + +def ask_mail_from(default_mail_from): + console.print("Adresse RFC5321.MailFrom :") + console.print(f" 1. Utiliser le même que le RFC5322.From ([b]{default_mail_from}[/])") + console.print(f" 2. Remplacer par une adresse qu’on maîtrise ([b]{ATTACKER_MAIL_FROM}[/])") + console.print() + choices = { + 1: default_mail_from, + 2: ATTACKER_MAIL_FROM + } + while True: + try: + response = Prompt.ask("Choix", console=console) + result = choices[int(response)] + console.print(f" ⇒ [mail_from]{result}[/]") + return result + except ValueError: + pass + except IndexError: + pass + + +def add_dkim_signature(email): + if Confirm.ask("Ajouter une signature DKIM ?", choices="on", default=False): + email_bytes = email.as_string().encode('utf-8') + selector = Prompt.ask("Sélecteur") + private_key = pathlib.Path(f"{selector}.private").read_bytes() + selector = selector.encode('ascii') + domain = Prompt.ask("Domaine", default=ATTACKER_DOMAIN).encode('ascii') + signature_line = dkim.dkim_sign(email_bytes, selector, domain, private_key).decode('ascii') + signature = re.match("^DKIM-Signature: (.*)\r\n", signature_line, re.DOTALL).group(1) + console.print(" ⇒ Signature DKIM ajoutée") + email['DKIM-Signature'] = signature + else: + console.print(" ⇒ Pas de signature DKIM") + + return email + + + +def generate_email(template): + msg = template.msg + msg['To'] = f"{VICTIM_NAME} <{VICTIM_ADDRESS}>" + msg['Date'] = email.utils.formatdate() + msg['Message-ID'] = f"{uuid.uuid4()}@{DEFAULT_HELO}" + if not msg['Content-Type']: + msg['Content-Type'] = "text/plain; charset=utf-8" + + return msg + + +def ask_template(): + templates = spoof_templates.spoof_templates() + console.print("[bold]Sélectionner un scénario :[/]") + for i, template in enumerate(templates): + console.print(f"{i + 1:4}. {template.nice_name}") + console.print() + + while True: + try: + response = Prompt.ask("Choix") + result = templates[int(response) - 1] + console.print(f" ⇒ [bold]{result.nice_name}[/]") + console.print() + return result + except ValueError: + pass + except IndexError: + pass + + + + +def send_email(helo, envelope_from, email): + data = email.as_string().encode('utf-8') + try: + with SMTP(local_hostname=helo) as smtp: + smtp.connect(VICTIM_MX) + smtp.sendmail(envelope_from, VICTIM_ADDRESS, data) + except smtplib.SMTPException: + console.print(" [failure]✘[/] Message [failure]rejeté[/] par le serveur SMTP") + except: + raise + else: + console.print(" [success]✔[/] Message [success]accepté[/] par le serveur SMTP") + + +def main(): + try: + console.print( + rich.panel.Panel( + "🕱 Outil d’usurpation d’identité de courriel 🕱", + style="banner", + width=80)) + console.print() + + template = ask_template() + display_name, rfc5322from = re.match('(.*) <(.*)>', template.msg['From']).groups() + console.print(f"Adresse RFC5322.From (tirée du scénario):") + console.print(f" ⇒ [data_display_name]{display_name} <[data_from]{rfc5322from}[/data_from]>[/]") + console.print() + + envelope_from = ask_mail_from(rfc5322from) + console.print() + + helo = ask_helo() + console.print() + + email = generate_email(template) + signed_email = add_dkim_signature(email) + + send_email(helo, envelope_from, signed_email) + except KeyboardInterrupt: + console.print() + pass + except: + raise + + +if __name__ == '__main__': + main() diff --git a/attacker/scripts/spoof_templates/__init__.py b/attacker/scripts/spoof_templates/__init__.py new file mode 100644 index 0000000..b453e99 --- /dev/null +++ b/attacker/scripts/spoof_templates/__init__.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +import email +import email.policy +import importlib.resources + +SPOOF_TEMPLATES = None + + +class SpoofTemplate: + @staticmethod + def read_template(name): + path = importlib.resources.files(__package__).joinpath(f"{name}.eml") + with path.open('r', encoding='utf-8') as fh: + return email.message_from_file(fh) + + def __init__(self, name, nice_name): + self.name = name + self.nice_name = nice_name + self.msg = SpoofTemplate.read_template(name) + + +def load_spoof_templates(): + return [SpoofTemplate(res, desc) + for res, desc + in [('extorsion', 'Extorsion'), + ('fake_order_confirm', 'Fausse confirmation de commande'), + ('fake_newsletter', 'Fausse newsletter')]] + + +def spoof_templates(): + global SPOOF_TEMPLATES + if not SPOOF_TEMPLATES: + SPOOF_TEMPLATES = load_spoof_templates() + return SPOOF_TEMPLATES diff --git a/attacker/scripts/spoof_templates/extorsion.eml b/attacker/scripts/spoof_templates/extorsion.eml new file mode 100644 index 0000000..233f63f --- /dev/null +++ b/attacker/scripts/spoof_templates/extorsion.eml @@ -0,0 +1,14 @@ +From: Destinataire +Subject: Demande de rançon + +Bonjour, + +Je suis très méchant : j’ai piraté votre boîte mail et j’ai envoyé ce mail à +vous-même avec votre propre adresse en expéditeur ! + +Maintenant, payez-moi 50 bitcoins ou vos amis, votre famille et votre employeur +sauront que vous aimez bien regarder des vidéos de chats mignons sur Internet. + +Cordialement, + +Un cybercriminel qui vous veut du bien. diff --git a/attacker/scripts/spoof_templates/fake_newsletter.eml b/attacker/scripts/spoof_templates/fake_newsletter.eml new file mode 100644 index 0000000..9322467 --- /dev/null +++ b/attacker/scripts/spoof_templates/fake_newsletter.eml @@ -0,0 +1,34 @@ +From: Newsletter +Subject: NEWSLETTER : Nous sommes mauvais ! 💩 +Content-Type: text/html; charset=utf-8 + + + +

+ Cher client, +

+

+ Dans ce nouveau numéro de notre newsletter, nous avons une annonce + importante à faire. +

+

+ Nous sommes vraiment mauvais. La preuve : n’importe + qui peut usurper l’adresse d’expédition des newsletters de cette boîte. +

+

+ Donc surtout, n’achetez pas chez nous ! Nos produits + sont de mauvaise qualité, ils sont mal notés sur Internet et vous + trouverez beaucoup de témoignages sur les réseaux sociaux disant le plus + grand mal de notre entreprise. +

+

+ Bref : allez plutôt chez nos concurrents. 😂 +

+

+ Cordialement, +

+

+ Un facétieux cybercriminel. +

+ + diff --git a/attacker/scripts/spoof_templates/fake_order_confirm.eml b/attacker/scripts/spoof_templates/fake_order_confirm.eml new file mode 100644 index 0000000..cc18b27 --- /dev/null +++ b/attacker/scripts/spoof_templates/fake_order_confirm.eml @@ -0,0 +1,55 @@ +From: Service client +Subject: Confirmation de commande 81729098 +Content-Type: text/html; charset=utf-8 + + + + + + +

Bonjour,

+

La commande suivante a bien été confirmée :

+ + + + + + + + + + + + + + + + + + + + + +
QtéDésignationPU HTPU TTCSous-total
1 500Savonnette1,66 €2,00 €3 000,00 €
Total2 500,00 €3 000,00 €3 000,00 €
+

+ Ce n’était pas vous ? Vous pouvez encore + annuler la commande + en vous rendant à l’adresse suivante : +

+

+ + https://expediteur-annulation.example/annulation-commande.php?c=81729098 + +

+

Cordialement,

+

Un cybercriminel qui vous veut du bien.

+ + \ No newline at end of file diff --git a/dns/Dockerfile b/dns/Dockerfile new file mode 100644 index 0000000..8e9a6f8 --- /dev/null +++ b/dns/Dockerfile @@ -0,0 +1,21 @@ +FROM alpine:latest + +RUN apk add \ + bash \ + bind-tools \ + execline \ + nano \ + nano-syntax \ + nsd \ + openssl \ + s6-overlay \ + unbound \ + vim + +COPY etc/unbound/unbound.conf /etc/unbound +COPY etc/nsd/nsd.conf /etc/nsd/nsd.conf +COPY zones /etc/nsd/zones + +COPY etc/s6-overlay /etc/s6-overlay + +ENTRYPOINT ["/init"] diff --git a/dns/etc/nsd/nsd.conf b/dns/etc/nsd/nsd.conf new file mode 100644 index 0000000..0f59ea8 --- /dev/null +++ b/dns/etc/nsd/nsd.conf @@ -0,0 +1,31 @@ +server: + ip-address: 0.0.0.0 + port: 1053 + debug-mode: yes + + zonesdir: "/etc/nsd/zones" + database: "" + +remote-control: + control-enable: yes + control-interface: 127.0.0.1 + +zone: + name: "example" + zonefile: "example.zone" + +zone: + name: "destinataire.example" + zonefile: "example/destinataire.zone" + +zone: + name: "expediteur.example" + zonefile: "example/expediteur.zone" + +zone: + name: "attaquant.example" + zonefile: "example/attaquant.zone" + +zone: + name: "31.172.in-addr.arpa" + zonefile: "arpa/31.172.in-addr.zone" diff --git a/dns/etc/s6-overlay/s6-rc.d/nsd-control-setup/type b/dns/etc/s6-overlay/s6-rc.d/nsd-control-setup/type new file mode 100644 index 0000000..3d92b15 --- /dev/null +++ b/dns/etc/s6-overlay/s6-rc.d/nsd-control-setup/type @@ -0,0 +1 @@ +oneshot \ No newline at end of file diff --git a/dns/etc/s6-overlay/s6-rc.d/nsd-control-setup/up b/dns/etc/s6-overlay/s6-rc.d/nsd-control-setup/up new file mode 100644 index 0000000..d7f5dc0 --- /dev/null +++ b/dns/etc/s6-overlay/s6-rc.d/nsd-control-setup/up @@ -0,0 +1 @@ +/usr/sbin/nsd-control-setup \ No newline at end of file diff --git a/dns/etc/s6-overlay/s6-rc.d/nsd/dependencies.d/base b/dns/etc/s6-overlay/s6-rc.d/nsd/dependencies.d/base new file mode 100644 index 0000000..e69de29 diff --git a/dns/etc/s6-overlay/s6-rc.d/nsd/dependencies.d/nsd-control-setup b/dns/etc/s6-overlay/s6-rc.d/nsd/dependencies.d/nsd-control-setup new file mode 100644 index 0000000..e69de29 diff --git a/dns/etc/s6-overlay/s6-rc.d/nsd/run b/dns/etc/s6-overlay/s6-rc.d/nsd/run new file mode 100644 index 0000000..c16d26a --- /dev/null +++ b/dns/etc/s6-overlay/s6-rc.d/nsd/run @@ -0,0 +1,2 @@ +#!/bin/execlineb -P +/usr/sbin/nsd -d -c /etc/nsd/nsd.conf -P /run/nsd.pid \ No newline at end of file diff --git a/dns/etc/s6-overlay/s6-rc.d/nsd/type b/dns/etc/s6-overlay/s6-rc.d/nsd/type new file mode 100644 index 0000000..1780f9f --- /dev/null +++ b/dns/etc/s6-overlay/s6-rc.d/nsd/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/dns/etc/s6-overlay/s6-rc.d/unbound/dependencies.d/base b/dns/etc/s6-overlay/s6-rc.d/unbound/dependencies.d/base new file mode 100644 index 0000000..e69de29 diff --git a/dns/etc/s6-overlay/s6-rc.d/unbound/run b/dns/etc/s6-overlay/s6-rc.d/unbound/run new file mode 100644 index 0000000..81e99fd --- /dev/null +++ b/dns/etc/s6-overlay/s6-rc.d/unbound/run @@ -0,0 +1,2 @@ +#!/bin/execlineb -P +/usr/sbin/unbound -d -c /etc/unbound/unbound.conf diff --git a/dns/etc/s6-overlay/s6-rc.d/unbound/type b/dns/etc/s6-overlay/s6-rc.d/unbound/type new file mode 100644 index 0000000..1780f9f --- /dev/null +++ b/dns/etc/s6-overlay/s6-rc.d/unbound/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/dns/etc/s6-overlay/s6-rc.d/user/contents.d/nsd b/dns/etc/s6-overlay/s6-rc.d/user/contents.d/nsd new file mode 100644 index 0000000..e69de29 diff --git a/dns/etc/s6-overlay/s6-rc.d/user/contents.d/unbound b/dns/etc/s6-overlay/s6-rc.d/user/contents.d/unbound new file mode 100644 index 0000000..e69de29 diff --git a/dns/etc/unbound/unbound.conf b/dns/etc/unbound/unbound.conf new file mode 100644 index 0000000..3efcf21 --- /dev/null +++ b/dns/etc/unbound/unbound.conf @@ -0,0 +1,37 @@ +server: + do-daemonize: no + + interface: 0.0.0.0 + interface: :: + access-control: 172.31.0.0/16 allow + access-control: fd4a:8c4:c28b::/48 allow + + log-queries: yes + log-replies: yes + log-servfail: yes + logfile: "" + + local-zone: "31.172.in-addr.arpa" nodefault + local-zone: "d.f.ip6.arpa" nodefault + + domain-insecure: "example" + domain-insecure: "31.172.in-addr.arpa" + domain-insecure: "b.8.2.c.4.c.8.0.a.4.d.f.ip6.arpa" + +# Pour une raison que j’ignore, mettre stub-addr: 127.0.0.1@1053 entraîne +# un SERVFAIL. On dirait qu’unbound n’arrive pas à communiquer avec nsd. +# Il faut que nsd écoute sur toutes les interfaces dans le conteneur et +# que, dans la configuration d’unbound, le stub-addr soit l’IP privée et pas +# la boucle locale, pour que ça marche. + +stub-zone: + name: "example" + stub-addr: 172.31.0.53@1053 + +stub-zone: + name: "31.172.in-addr.arpa" + stub-addr: 172.31.0.53@1053 + +stub-zone: + name: "b.8.2.c.4.c.8.0.a.4.d.f.ip6.arpa" + stub-addr: 172.31.0.53@1053 diff --git a/dns/zones/arpa/31.172.in-addr.zone b/dns/zones/arpa/31.172.in-addr.zone new file mode 100644 index 0000000..c6456cb --- /dev/null +++ b/dns/zones/arpa/31.172.in-addr.zone @@ -0,0 +1,30 @@ +$TTL 0 +$ORIGIN 31.172.in-addr.arpa. + +@ IN SOA ( + ns.example. + hostmaster.nic.example. + 2023040400 + 86400 + 14400 + 3600000 + 3600 + ) + + NS ns.example. + +$ORIGIN 0.31.172.in-addr.arpa. + +53 PTR ns.example. + +$ORIGIN 10.31.172.in-addr.arpa. + +1 PTR mx.expediteur.example. + +$ORIGIN 20.31.172.in-addr.arpa. + +1 PTR mx.destinataire.example. + +$ORIGIN 30.31.172.in-addr.arpa. + +1 PTR mx.attaquant.example. diff --git a/dns/zones/arpa/b.8.2.c.4.c.8.0.a.4.d.f.ip6.zone b/dns/zones/arpa/b.8.2.c.4.c.8.0.a.4.d.f.ip6.zone new file mode 100644 index 0000000..8e229ce --- /dev/null +++ b/dns/zones/arpa/b.8.2.c.4.c.8.0.a.4.d.f.ip6.zone @@ -0,0 +1,30 @@ +$TTL 0 +$ORIGIN b.8.2.c.4.c.8.0.a.4.d.f.ip6.arpa. + +@ IN SOA ( + ns.example. + hostmaster.nic.example. + 2023040400 + 86400 + 14400 + 3600000 + 3600 + ) + + NS ns.example. + +$ORIGIN 0.0.0.0.b.8.2.c.4.c.8.0.a.4.d.f.ip6.arpa. + +3.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR ns.example. + +$ORIGIN 0.0.0.1.b.8.2.c.4.c.8.0.a.4.d.f.ip6.arpa. + +1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR mx.expediteur.example. + +$ORIGIN 0.0.0.2.b.8.2.c.4.c.8.0.a.4.d.f.ip6.arpa. + +1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR mx.destinataire.example. + +$ORIGIN 0.0.0.3.b.8.2.c.4.c.8.0.a.4.d.f.ip6.arpa. + +1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR mx.attaquant.example. diff --git a/dns/zones/example.zone b/dns/zones/example.zone new file mode 100644 index 0000000..00e5386 --- /dev/null +++ b/dns/zones/example.zone @@ -0,0 +1,33 @@ +$TTL 0 +$ORIGIN example. + +@ IN SOA ( + ns.example. + hostmaster.nic.example. + 1 + 86400 + 14400 + 3600000 + 3600 + ) + + NS ns.example. + +ns A 172.31.0.53 + AAAA fd4a:8c4:c28b::53 + +nic MX 0 . + + +attaquant.example. NS ns.attaquant.example. +ns.attaquant.example. A 172.31.0.53 + AAAA fd4a:8c4:c28b::53 + +destinataire.example. NS ns.destinataire.example. +ns.destinataire.example. A 172.31.0.53 + AAAA fd4a:8c4:c28b::53 + +expediteur.example. NS ns.expediteur.example. +ns.expediteur.example. A 172.31.0.53 + AAAA fd4a:8c4:c28b::53 + diff --git a/dns/zones/example/attaquant.zone b/dns/zones/example/attaquant.zone new file mode 100644 index 0000000..3334d9b --- /dev/null +++ b/dns/zones/example/attaquant.zone @@ -0,0 +1,32 @@ +$TTL 0 +$ORIGIN attaquant.example. + +@ IN SOA ( + ns.attaquant.example. + usurpateur.attaquant.example. + 2023032900 + 86400 + 14400 + 3600000 + 3600 + ) + + NS ns.attaquant.example. + + MX 10 mx.attaquant.example. + +;; Politique SPF +; TXT "v=spf1 ip4:172.31.30.1 ip6:fd4a:8c4:c28b:3000::1 -all" + + +ns A 172.31.0.53 + AAAA fd4a:8c4:c28b::53 + +mx A 172.31.30.1 + AAAA fd4a:8c4:c28b:3000::1 + +;; Clef publique DKIM +;usurpateur._domainkey TXT "" + +;; Politique DMARC +;_dmarc TXT "v=DMARC1 p=none" diff --git a/dns/zones/example/destinataire.zone b/dns/zones/example/destinataire.zone new file mode 100644 index 0000000..3d77fcc --- /dev/null +++ b/dns/zones/example/destinataire.zone @@ -0,0 +1,29 @@ +$TTL 0 +$ORIGIN destinataire.example. + +@ IN SOA ( + ns.destinataire.example. + root.destinataire.example. + 2023032900 + 86400 + 14400 + 3600000 + 3600 + ) + + NS ns.destinataire.example. + + MX 10 mx.destinataire.example. + +;; Politique SPF +; TXT "v=spf1 mx -all" + +mx A 172.31.20.1 + AAAA fd4a:8c4:c28b:2000::1 + +ns A 172.31.0.53 + AAAA fd4a:8c4:c28b::53 + + +;; Politique DMARC +;_dmarc TXT "v=DMARC1;p=reject" diff --git a/dns/zones/example/expediteur.zone b/dns/zones/example/expediteur.zone new file mode 100644 index 0000000..410dc90 --- /dev/null +++ b/dns/zones/example/expediteur.zone @@ -0,0 +1,31 @@ +$TTL 0 +$ORIGIN expediteur.example. + +@ IN SOA ( + ns.expediteur.example. + root.expediteur.example. + 2023032900 + 86400 + 14400 + 3600000 + 3600 + ) + + NS ns.expediteur.example. + + MX 10 mx.expediteur.example. + +;; Politique SPF +; TXT "v=spf1 ip4:172.31.10.1 ip6:fd4a:8c4:c28b:1000::1 -all" + +mx A 172.31.10.1 + AAAA fd4a:8c4:c28b:1000::1 + +ns.expediteur.example. A 172.31.0.53 + AAAA fd4a:8c4:c28b::53 + +;; Clef publique DKIM +;demo._domainkey TXT "" + +;; Politique DMARC +;_dmarc TXT "v=DMARC1;p=none" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9e643d2 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,62 @@ +version: '3.8' +name: 'spf-dkim-dmarc-workshop' + +services: + dns: + image: spf-dkim-dmarc-workshop/dns + build: ./dns + hostname: dns + dns: + - 127.0.0.1 + networks: + internal: + ipv4_address: 172.31.0.53 + ipv6_address: fd4a:8c4:c28b::53 + + sender: + image: spf-dkim-dmarc-workshop/sender + build: ./sender + hostname: expediteur + dns: + - 172.31.0.53 + networks: + internal: + ipv4_address: 172.31.10.1 + ipv6_address: fd4a:8c4:c28b:1000::1 + + recipient: + image: spf-dkim-dmarc-workshop/recipient + build: ./recipient + hostname: destinataire + dns: + - 172.31.0.53 + networks: + internal: + ipv4_address: 172.31.20.1 + ipv6_address: fd4a:8c4:c28b:2000::1 + external: + ports: + - "127.0.0.1:8225:8225" + + attacker: + image: spf-dkim-dmarc-workshop/attacker + build: ./attacker + hostname: attaquant + dns: + - 172.31.0.53 + networks: + internal: + ipv4_address: 172.31.30.1 + ipv6_address: fd4a:8c4:c28b:3000::1 + +networks: + internal: + # enable_ipv6: true + internal: true + ipam: + driver: default + config: + - subnet: 172.31.10.0/16 + - subnet: fd4a:8c4:c28b::/48 + + external: diff --git a/recipient/Dockerfile b/recipient/Dockerfile new file mode 100644 index 0000000..1b832c9 --- /dev/null +++ b/recipient/Dockerfile @@ -0,0 +1,93 @@ +FROM alpine:latest AS roundcube-build + +RUN apk add composer \ + php81-ctype \ + php81-dom \ + php81-ldap \ + php81-tokenizer \ + php81-xml \ + php81-xmlwriter + +RUN install -d /var/www/roundcubemail + +ADD https://github.com/roundcube/roundcubemail/releases/download/1.6.1/roundcubemail-1.6.1-complete.tar.gz \ + /src/roundcube.tar.gz +RUN tar -C /src --no-same-owner -xf /src/roundcube.tar.gz + +RUN tar -C /src/roundcubemail-1.6.1 -cf - . | \ + tar -C /var/www/roundcubemail -xpf - + +# Plugin authres_status : affichage sympa de l’en-tête Authentication-Results +RUN composer -d /var/www/roundcubemail -n require pimlie/authres_status + +FROM alpine:latest + +COPY var/db/public_suffix_list.dat /var/db/public_suffix_list.dat + +RUN apk add \ + apache2 \ + composer \ + dovecot \ + execline \ + mutt \ + nano \ + nano-syntax \ + opendkim \ + opendmarc \ + php81-apache2 \ + php81-ctype \ + php81-dom \ + php81-intl \ + php81-mbstring \ + php81-openssl\ + php81-pdo \ + php81-pdo_sqlite \ + php81-session \ + php81-tokenizer \ + php81-xml \ + php81-xmlwriter \ + postfix \ + postfix-policyd-spf-perl \ + s6-overlay \ + vim + +COPY --from=roundcube-build --chown=root:www-data \ + /var/www/roundcubemail /var/www/roundcubemail +RUN chown apache /var/www/roundcubemail/logs /var/www/roundcubemail/temp + +RUN install -m 0700 -o apache -g www-data -d /var/db/roundcubemail + +RUN adduser -D destinataire +RUN newaliases + +# TODO faire en sorte que Dovecot logue dans syslog + +# TODO finir de configurer postfix pour qu’il fasse les contrôles +# SPF/DKIM/DMARC si on le lui demande + +# TODO rendre le mot de passe de destinataire@destinataire.example +# configurable + +COPY etc/s6-overlay /etc/s6-overlay + +COPY etc/postfix /etc/postfix + +COPY etc/dovecot /etc/dovecot + +COPY --chmod=0640 --chown=root:www-data \ + etc/roundcube/config.inc.php /var/www/roundcubemail/config/config.inc.php + +COPY etc/apache/httpd.conf /etc/apache2/httpd.conf + +COPY etc/apache/roundcube.conf /etc/apache2/conf.d/roundcube.conf + +RUN install -m 0700 -o opendkim -g opendkim -d /run/opendkim +COPY etc/opendkim/opendkim.conf /etc/opendkim/opendkim.conf + +RUN install -m 0700 -o opendmarc -g root -d /run/opendmarc +COPY etc/opendmarc/opendmarc.conf /etc/opendmarc/opendmarc.conf + +RUN doveadm pw -p "PasSecretDuTout" | \ + awk '{ print "destinataire:" $1 }' > /etc/dovecot/users + +ENTRYPOINT ["/init"] diff --git a/recipient/etc/apache/httpd.conf b/recipient/etc/apache/httpd.conf new file mode 100644 index 0000000..9021eb4 --- /dev/null +++ b/recipient/etc/apache/httpd.conf @@ -0,0 +1,382 @@ +ServerTokens Prod +ServerRoot /var/www + +Listen 80 + +# +# Dynamic Shared Object (DSO) Support +# +# To be able to use the functionality of a module which was built as a DSO you +# have to place corresponding `LoadModule' lines at this location so the +# directives contained in it are actually available _before_ they are used. +# Statically compiled modules (those listed by `httpd -l') do not need +# to be loaded here. +# +# Example: +# LoadModule foo_module modules/mod_foo.so +# +#LoadModule mpm_event_module modules/mod_mpm_event.so +LoadModule mpm_prefork_module modules/mod_mpm_prefork.so +#LoadModule mpm_worker_module modules/mod_mpm_worker.so +LoadModule authn_file_module modules/mod_authn_file.so +#LoadModule authn_dbm_module modules/mod_authn_dbm.so +#LoadModule authn_anon_module modules/mod_authn_anon.so +#LoadModule authn_dbd_module modules/mod_authn_dbd.so +#LoadModule authn_socache_module modules/mod_authn_socache.so +LoadModule authn_core_module modules/mod_authn_core.so +LoadModule authz_host_module modules/mod_authz_host.so +LoadModule authz_groupfile_module modules/mod_authz_groupfile.so +LoadModule authz_user_module modules/mod_authz_user.so +#LoadModule authz_dbm_module modules/mod_authz_dbm.so +#LoadModule authz_owner_module modules/mod_authz_owner.so +#LoadModule authz_dbd_module modules/mod_authz_dbd.so +LoadModule authz_core_module modules/mod_authz_core.so +LoadModule access_compat_module modules/mod_access_compat.so +LoadModule auth_basic_module modules/mod_auth_basic.so +#LoadModule auth_form_module modules/mod_auth_form.so +#LoadModule auth_digest_module modules/mod_auth_digest.so +#LoadModule allowmethods_module modules/mod_allowmethods.so +#LoadModule file_cache_module modules/mod_file_cache.so +#LoadModule cache_module modules/mod_cache.so +#LoadModule cache_disk_module modules/mod_cache_disk.so +#LoadModule cache_socache_module modules/mod_cache_socache.so +#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so +#LoadModule socache_dbm_module modules/mod_socache_dbm.so +#LoadModule socache_memcache_module modules/mod_socache_memcache.so +#LoadModule socache_redis_module modules/mod_socache_redis.so +#LoadModule watchdog_module modules/mod_watchdog.so +#LoadModule macro_module modules/mod_macro.so +#LoadModule dbd_module modules/mod_dbd.so +#LoadModule dumpio_module modules/mod_dumpio.so +#LoadModule echo_module modules/mod_echo.so +#LoadModule buffer_module modules/mod_buffer.so +#LoadModule data_module modules/mod_data.so +#LoadModule ratelimit_module modules/mod_ratelimit.so +LoadModule reqtimeout_module modules/mod_reqtimeout.so +#LoadModule ext_filter_module modules/mod_ext_filter.so +#LoadModule request_module modules/mod_request.so +#LoadModule include_module modules/mod_include.so +LoadModule filter_module modules/mod_filter.so +#LoadModule reflector_module modules/mod_reflector.so +#LoadModule substitute_module modules/mod_substitute.so +#LoadModule sed_module modules/mod_sed.so +#LoadModule charset_lite_module modules/mod_charset_lite.so +#LoadModule deflate_module modules/mod_deflate.so +#LoadModule brotli_module modules/mod_brotli.so +LoadModule mime_module modules/mod_mime.so +LoadModule log_config_module modules/mod_log_config.so +#LoadModule log_debug_module modules/mod_log_debug.so +#LoadModule log_forensic_module modules/mod_log_forensic.so +#LoadModule logio_module modules/mod_logio.so +LoadModule env_module modules/mod_env.so +#LoadModule mime_magic_module modules/mod_mime_magic.so +#LoadModule expires_module modules/mod_expires.so +LoadModule headers_module modules/mod_headers.so +#LoadModule usertrack_module modules/mod_usertrack.so +#LoadModule unique_id_module modules/mod_unique_id.so +LoadModule setenvif_module modules/mod_setenvif.so +LoadModule version_module modules/mod_version.so +#LoadModule remoteip_module modules/mod_remoteip.so +#LoadModule session_module modules/mod_session.so +#LoadModule session_cookie_module modules/mod_session_cookie.so +#LoadModule session_crypto_module modules/mod_session_crypto.so +#LoadModule session_dbd_module modules/mod_session_dbd.so +#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so +#LoadModule slotmem_plain_module modules/mod_slotmem_plain.so +#LoadModule dialup_module modules/mod_dialup.so +#LoadModule http2_module modules/mod_http2.so +LoadModule unixd_module modules/mod_unixd.so +#LoadModule heartbeat_module modules/mod_heartbeat.so +#LoadModule heartmonitor_module modules/mod_heartmonitor.so +LoadModule status_module modules/mod_status.so +LoadModule autoindex_module modules/mod_autoindex.so +#LoadModule asis_module modules/mod_asis.so +#LoadModule info_module modules/mod_info.so +#LoadModule suexec_module modules/mod_suexec.so + + #LoadModule cgid_module modules/mod_cgid.so + + + #LoadModule cgi_module modules/mod_cgi.so + +#LoadModule vhost_alias_module modules/mod_vhost_alias.so +#LoadModule negotiation_module modules/mod_negotiation.so +LoadModule dir_module modules/mod_dir.so +#LoadModule actions_module modules/mod_actions.so +#LoadModule speling_module modules/mod_speling.so +#LoadModule userdir_module modules/mod_userdir.so +LoadModule alias_module modules/mod_alias.so +LoadModule rewrite_module modules/mod_rewrite.so + +LoadModule negotiation_module modules/mod_negotiation.so + + +User apache +Group apache + + +ServerAdmin root@destinataire.example +ServerSignature Off + +ServerName destinataire.example:80 + +# +# Deny access to the entirety of your server's filesystem. You must +# explicitly permit access to web content directories in other +# blocks below. +# + + AllowOverride none + Require all denied + + +# +# Note that from this point forward you must specifically allow +# particular features to be enabled - so if something's not working as +# you might expect, make sure that you have specifically enabled it +# below. +# + +# +# DocumentRoot: The directory out of which you will serve your +# documents. By default, all requests are taken from this directory, but +# symbolic links and aliases may be used to point to other locations. +# +DocumentRoot "/var/www/localhost/htdocs" + + # + # Possible values for the Options directive are "None", "All", + # or any combination of: + # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews + # + # Note that "MultiViews" must be named *explicitly* --- "Options All" + # doesn't give it to you. + # + # The Options directive is both complicated and important. Please see + # http://httpd.apache.org/docs/2.4/mod/core.html#options + # for more information. + # + Options Indexes FollowSymLinks + + # + # AllowOverride controls what directives may be placed in .htaccess files. + # It can be "All", "None", or any combination of the keywords: + # AllowOverride FileInfo AuthConfig Limit + # + AllowOverride None + + # + # Controls who can get stuff from this server. + # + Require all granted + + +# +# DirectoryIndex: sets the file that Apache will serve if a directory +# is requested. +# + + DirectoryIndex index.html + + +# +# The following lines prevent .htaccess and .htpasswd files from being +# viewed by Web clients. +# + + Require all denied + + +# +# ErrorLog: The location of the error log file. +# If you do not specify an ErrorLog directive within a +# container, error messages relating to that virtual host will be +# logged here. If you *do* define an error logfile for a +# container, that host's errors will be logged there and not here. +# +ErrorLog logs/error.log + +# +# LogLevel: Control the number of messages logged to the error_log. +# Possible values include: debug, info, notice, warn, error, crit, +# alert, emerg. +# +LogLevel warn + + + # + # The following directives define some format nicknames for use with + # a CustomLog directive (see below). + # + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined + LogFormat "%h %l %u %t \"%r\" %>s %b" common + + + # You need to enable mod_logio.c to use %I and %O + LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio + + + # + # The location and format of the access logfile (Common Logfile Format). + # If you do not define any access logfiles within a + # container, they will be logged here. Contrariwise, if you *do* + # define per- access logfiles, transactions will be + # logged therein and *not* in this file. + # + #CustomLog logs/access.log common + + # + # If you prefer a logfile with access, agent, and referer information + # (Combined Logfile Format) you can use the following directive. + # + CustomLog logs/access.log combined + + + + # + # Redirect: Allows you to tell clients about documents that used to + # exist in your server's namespace, but do not anymore. The client + # will make a new request for the document at its new location. + # Example: + # Redirect permanent /foo http://www.example.com/bar + + # + # Alias: Maps web paths into filesystem paths and is used to + # access content that does not live under the DocumentRoot. + # Example: + # Alias /webpath /full/filesystem/path + # + # If you include a trailing / on /webpath then the server will + # require it to be present in the URL. You will also likely + # need to provide a section to allow access to + # the filesystem path. + + # + # ScriptAlias: This controls which directories contain server scripts. + # ScriptAliases are essentially the same as Aliases, except that + # documents in the target directory are treated as applications and + # run by the server when requested rather than as documents sent to the + # client. The same rules about trailing "/" apply to ScriptAlias + # directives as to Alias. + # + ScriptAlias /cgi-bin/ "/var/www/localhost/cgi-bin/" + + + + + # + # ScriptSock: On threaded servers, designate the path to the UNIX + # socket used to communicate with the CGI daemon of mod_cgid. + # + #Scriptsock cgisock + + +# +# "/var/www/localhost/cgi-bin" should be changed to whatever your ScriptAliased +# CGI directory exists, if you have that configured. +# + + AllowOverride None + Options None + Require all granted + + + + # + # Avoid passing HTTP_PROXY environment to CGI's on this or any proxied + # backend servers which have lingering "httpoxy" defects. + # 'Proxy' request header is undefined by the IETF, not listed by IANA + # + RequestHeader unset Proxy early + + + + # + # TypesConfig points to the file containing the list of mappings from + # filename extension to MIME-type. + # + TypesConfig /etc/apache2/mime.types + + # + # AddType allows you to add to or override the MIME configuration + # file specified in TypesConfig for specific file types. + # + #AddType application/x-gzip .tgz + # + # AddEncoding allows you to have certain browsers uncompress + # information on the fly. Note: Not all browsers support this. + # + #AddEncoding x-compress .Z + #AddEncoding x-gzip .gz .tgz + # + # If the AddEncoding directives above are commented-out, then you + # probably should define those extensions to indicate media types: + # + AddType application/x-compress .Z + AddType application/x-gzip .gz .tgz + + # + # AddHandler allows you to map certain file extensions to "handlers": + # actions unrelated to filetype. These can be either built into the server + # or added with the Action directive (see below) + # + # To use CGI scripts outside of ScriptAliased directories: + # (You will also need to add "ExecCGI" to the "Options" directive.) + # + #AddHandler cgi-script .cgi + + # For type maps (negotiated resources): + #AddHandler type-map var + + # + # Filters allow you to process content before it is sent to the client. + # + # To parse .shtml files for server-side includes (SSI): + # (You will also need to add "Includes" to the "Options" directive.) + # + #AddType text/html .shtml + #AddOutputFilter INCLUDES .shtml + + +# +# The mod_mime_magic module allows the server to use various hints from the +# contents of the file itself to determine its type. The MIMEMagicFile +# directive tells the module where the hint definitions are located. +# + + MIMEMagicFile /etc/apache2/magic + + +# +# Customizable error responses come in three flavors: +# 1) plain text 2) local redirects 3) external redirects +# +# Some examples: +#ErrorDocument 500 "The server made a boo boo." +#ErrorDocument 404 /missing.html +#ErrorDocument 404 "/cgi-bin/missing_handler.pl" +#ErrorDocument 402 http://www.example.com/subscription_info.html +# + +# +# MaxRanges: Maximum number of Ranges in a request before +# returning the entire resource, or one of the special +# values 'default', 'none' or 'unlimited'. +# Default setting is to accept 200 Ranges. +#MaxRanges unlimited + +# +# EnableMMAP and EnableSendfile: On systems that support it, +# memory-mapping or the sendfile syscall may be used to deliver +# files. This usually improves server performance, but must +# be turned off when serving from networked-mounted +# filesystems or if support for these functions is otherwise +# broken on your system. +# Defaults: EnableMMAP On, EnableSendfile Off +# +#EnableMMAP off +#EnableSendfile on + +# Load config files from the config directory "/etc/apache2/conf.d". +# +IncludeOptional /etc/apache2/conf.d/*.conf diff --git a/recipient/etc/apache/roundcube.conf b/recipient/etc/apache/roundcube.conf new file mode 100644 index 0000000..e880fda --- /dev/null +++ b/recipient/etc/apache/roundcube.conf @@ -0,0 +1,17 @@ +Listen 8225 + + + ServerName destinataire.example + DocumentRoot /var/www/roundcubemail/public_html/ + + + Options FollowSymLinks + AllowOverride All + + + + Options FollowSymLinks MultiViews + AllowOverride All + Require all granted + + diff --git a/recipient/etc/dovecot/conf.d/10-auth.conf b/recipient/etc/dovecot/conf.d/10-auth.conf new file mode 100644 index 0000000..035319c --- /dev/null +++ b/recipient/etc/dovecot/conf.d/10-auth.conf @@ -0,0 +1,14 @@ +auth_mechanisms = plain + +## +## Password and user databases +## + +passdb { + driver = passwd-file + args = scheme=CRYPT username_format=%u /etc/dovecot/users +} + +userdb { + driver = passwd +} diff --git a/recipient/etc/dovecot/conf.d/10-director.conf b/recipient/etc/dovecot/conf.d/10-director.conf new file mode 100644 index 0000000..073d8a8 --- /dev/null +++ b/recipient/etc/dovecot/conf.d/10-director.conf @@ -0,0 +1,60 @@ +## +## Director-specific settings. +## + +# Director can be used by Dovecot proxy to keep a temporary user -> mail server +# mapping. As long as user has simultaneous connections, the user is always +# redirected to the same server. Each proxy server is running its own director +# process, and the directors are communicating the state to each others. +# Directors are mainly useful with NFS-like setups. + +# List of IPs or hostnames to all director servers, including ourself. +# Ports can be specified as ip:port. The default port is the same as +# what director service's inet_listener is using. +#director_servers = + +# List of IPs or hostnames to all backend mail servers. Ranges are allowed +# too, like 10.0.0.10-10.0.0.30. +#director_mail_servers = + +# How long to redirect users to a specific server after it no longer has +# any connections. +#director_user_expire = 15 min + +# How the username is translated before being hashed. Useful values include +# %Ln if user can log in with or without @domain, %Ld if mailboxes are shared +# within domain. +#director_username_hash = %Lu + +# To enable director service, uncomment the modes and assign a port. +service director { + unix_listener login/director { + #mode = 0666 + } + fifo_listener login/proxy-notify { + #mode = 0666 + } + unix_listener director-userdb { + #mode = 0600 + } + inet_listener { + #port = + } +} + +# Enable director for the wanted login services by telling them to +# connect to director socket instead of the default login socket: +service imap-login { + #executable = imap-login director +} +service pop3-login { + #executable = pop3-login director +} +service submission-login { + #executable = submission-login director +} + +# Enable director for LMTP proxying: +protocol lmtp { + #auth_socket_path = director-userdb +} diff --git a/recipient/etc/dovecot/conf.d/10-logging.conf b/recipient/etc/dovecot/conf.d/10-logging.conf new file mode 100644 index 0000000..beb15ba --- /dev/null +++ b/recipient/etc/dovecot/conf.d/10-logging.conf @@ -0,0 +1,105 @@ +## +## Log destination. +## + +# Log file to use for error messages. "syslog" logs to syslog, +# /dev/stderr logs to stderr. +#log_path = syslog + +# Log file to use for informational messages. Defaults to log_path. +#info_log_path = +# Log file to use for debug messages. Defaults to info_log_path. +#debug_log_path = + +# Syslog facility to use if you're logging to syslog. Usually if you don't +# want to use "mail", you'll use local0..local7. Also other standard +# facilities are supported. +#syslog_facility = mail + +## +## Logging verbosity and debugging. +## + +# Log filter is a space-separated list conditions. If any of the conditions +# match, the log filter matches (i.e. they're ORed together). Parenthesis +# are supported if multiple conditions need to be matched together. +# +# See https://doc.dovecot.org/configuration_manual/event_filter/ for details. +# +# For example: event=http_request_* AND category=error AND category=storage +# +# Filter to specify what debug logging to enable. This will eventually replace +# mail_debug and auth_debug settings. +#log_debug = + +# Crash after logging a matching event. For example category=error will crash +# any time an error is logged, which can be useful for debugging. +#log_core_filter = + +# Log unsuccessful authentication attempts and the reasons why they failed. +#auth_verbose = no + +# In case of password mismatches, log the attempted password. Valid values are +# no, plain and sha1. sha1 can be useful for detecting brute force password +# attempts vs. user simply trying the same password over and over again. +# You can also truncate the value to n chars by appending ":n" (e.g. sha1:6). +#auth_verbose_passwords = no + +# Even more verbose logging for debugging purposes. Shows for example SQL +# queries. +#auth_debug = no + +# In case of password mismatches, log the passwords and used scheme so the +# problem can be debugged. Enabling this also enables auth_debug. +#auth_debug_passwords = no + +# Enable mail process debugging. This can help you figure out why Dovecot +# isn't finding your mails. +#mail_debug = no + +# Show protocol level SSL errors. +#verbose_ssl = no + +# mail_log plugin provides more event logging for mail processes. +plugin { + # Events to log. Also available: flag_change append + #mail_log_events = delete undelete expunge copy mailbox_delete mailbox_rename + # Available fields: uid, box, msgid, from, subject, size, vsize, flags + # size and vsize are available only for expunge and copy events. + #mail_log_fields = uid box msgid size +} + +## +## Log formatting. +## + +# Prefix for each line written to log file. % codes are in strftime(3) +# format. +#log_timestamp = "%b %d %H:%M:%S " + +# Space-separated list of elements we want to log. The elements which have +# a non-empty variable value are joined together to form a comma-separated +# string. +#login_log_format_elements = user=<%u> method=%m rip=%r lip=%l mpid=%e %c + +# Login log format. %s contains login_log_format_elements string, %$ contains +# the data we want to log. +#login_log_format = %$: %s + +# Log prefix for mail processes. See doc/wiki/Variables.txt for list of +# possible variables you can use. +#mail_log_prefix = "%s(%u)<%{pid}><%{session}>: " + +# Format to use for logging mail deliveries: +# %$ - Delivery status message (e.g. "saved to INBOX") +# %m / %{msgid} - Message-ID +# %s / %{subject} - Subject +# %f / %{from} - From address +# %p / %{size} - Physical size +# %w / %{vsize} - Virtual size +# %e / %{from_envelope} - MAIL FROM envelope +# %{to_envelope} - RCPT TO envelope +# %{delivery_time} - How many milliseconds it took to deliver the mail +# %{session_time} - How long LMTP session took, not including delivery_time +# %{storage_id} - Backend-specific ID for mail, e.g. Maildir filename +#deliver_log_format = msgid=%m: %$ diff --git a/recipient/etc/dovecot/conf.d/10-mail.conf b/recipient/etc/dovecot/conf.d/10-mail.conf new file mode 100644 index 0000000..8d19d6e --- /dev/null +++ b/recipient/etc/dovecot/conf.d/10-mail.conf @@ -0,0 +1,391 @@ +## +## Mailbox locations and namespaces +## + +mail_location = maildir:~/Maildir + +# If you need to set multiple mailbox locations or want to change default +# namespace settings, you can do it by defining namespace sections. +# +# You can have private, shared and public namespaces. Private namespaces +# are for user's personal mails. Shared namespaces are for accessing other +# users' mailboxes that have been shared. Public namespaces are for shared +# mailboxes that are managed by sysadmin. If you create any shared or public +# namespaces you'll typically want to enable ACL plugin also, otherwise all +# users can access all the shared mailboxes, assuming they have permissions +# on filesystem level to do so. +namespace inbox { + # Namespace type: private, shared or public + #type = private + + # Hierarchy separator to use. You should use the same separator for all + # namespaces or some clients get confused. '/' is usually a good one. + # The default however depends on the underlying mail storage format. + #separator = + + # Prefix required to access this namespace. This needs to be different for + # all namespaces. For example "Public/". + #prefix = + + # Physical location of the mailbox. This is in same format as + # mail_location, which is also the default for it. + #location = + + # There can be only one INBOX, and this setting defines which namespace + # has it. + inbox = yes + + # If namespace is hidden, it's not advertised to clients via NAMESPACE + # extension. You'll most likely also want to set list=no. This is mostly + # useful when converting from another server with different namespaces which + # you want to deprecate but still keep working. For example you can create + # hidden namespaces with prefixes "~/mail/", "~%u/mail/" and "mail/". + #hidden = no + + # Show the mailboxes under this namespace with LIST command. This makes the + # namespace visible for clients that don't support NAMESPACE extension. + # "children" value lists child mailboxes, but hides the namespace prefix. + #list = yes + + # Namespace handles its own subscriptions. If set to "no", the parent + # namespace handles them (empty prefix should always have this as "yes") + #subscriptions = yes + + # See 15-mailboxes.conf for definitions of special mailboxes. +} + +# Example shared namespace configuration +#namespace { + #type = shared + #separator = / + + # Mailboxes are visible under "shared/user@domain/" + # %%n, %%d and %%u are expanded to the destination user. + #prefix = shared/%%u/ + + # Mail location for other users' mailboxes. Note that %variables and ~/ + # expands to the logged in user's data. %%n, %%d, %%u and %%h expand to the + # destination user's data. + #location = maildir:%%h/Maildir:INDEX=~/Maildir/shared/%%u + + # Use the default namespace for saving subscriptions. + #subscriptions = no + + # List the shared/ namespace only if there are visible shared mailboxes. + #list = children +#} +# Should shared INBOX be visible as "shared/user" or "shared/user/INBOX"? +#mail_shared_explicit_inbox = no + +# System user and group used to access mails. If you use multiple, userdb +# can override these by returning uid or gid fields. You can use either numbers +# or names. +#mail_uid = +#mail_gid = + +# Group to enable temporarily for privileged operations. Currently this is +# used only with INBOX when either its initial creation or dotlocking fails. +# Typically this is set to "mail" to give access to /var/mail. +#mail_privileged_group = + +# Grant access to these supplementary groups for mail processes. Typically +# these are used to set up access to shared mailboxes. Note that it may be +# dangerous to set these if users can create symlinks (e.g. if "mail" group is +# set here, ln -s /var/mail ~/mail/var could allow a user to delete others' +# mailboxes, or ln -s /secret/shared/box ~/mail/mybox would allow reading it). +#mail_access_groups = + +# Allow full filesystem access to clients. There's no access checks other than +# what the operating system does for the active UID/GID. It works with both +# maildir and mboxes, allowing you to prefix mailboxes names with eg. /path/ +# or ~user/. +#mail_full_filesystem_access = no + +# Dictionary for key=value mailbox attributes. This is used for example by +# URLAUTH and METADATA extensions. +#mail_attribute_dict = + +# A comment or note that is associated with the server. This value is +# accessible for authenticated users through the IMAP METADATA server +# entry "/shared/comment". +#mail_server_comment = "" + +# Indicates a method for contacting the server administrator. According to +# RFC 5464, this value MUST be a URI (e.g., a mailto: or tel: URL), but that +# is currently not enforced. Use for example mailto:admin@example.com. This +# value is accessible for authenticated users through the IMAP METADATA server +# entry "/shared/admin". +#mail_server_admin = + +## +## Mail processes +## + +# Don't use mmap() at all. This is required if you store indexes to shared +# filesystems (NFS or clustered filesystem). +#mmap_disable = no + +# Rely on O_EXCL to work when creating dotlock files. NFS supports O_EXCL +# since version 3, so this should be safe to use nowadays by default. +#dotlock_use_excl = yes + +# When to use fsync() or fdatasync() calls: +# optimized (default): Whenever necessary to avoid losing important data +# always: Useful with e.g. NFS when write()s are delayed +# never: Never use it (best performance, but crashes can lose data) +#mail_fsync = optimized + +# Locking method for index files. Alternatives are fcntl, flock and dotlock. +# Dotlocking uses some tricks which may create more disk I/O than other locking +# methods. NFS users: flock doesn't work, remember to change mmap_disable. +#lock_method = fcntl + +# Directory where mails can be temporarily stored. Usually it's used only for +# mails larger than >= 128 kB. It's used by various parts of Dovecot, for +# example LDA/LMTP while delivering large mails or zlib plugin for keeping +# uncompressed mails. +#mail_temp_dir = /tmp + +# Valid UID range for users, defaults to 500 and above. This is mostly +# to make sure that users can't log in as daemons or other system users. +# Note that denying root logins is hardcoded to dovecot binary and can't +# be done even if first_valid_uid is set to 0. +#first_valid_uid = 500 +#last_valid_uid = 0 + +# Valid GID range for users, defaults to non-root/wheel. Users having +# non-valid GID as primary group ID aren't allowed to log in. If user +# belongs to supplementary groups with non-valid GIDs, those groups are +# not set. +#first_valid_gid = 1 +#last_valid_gid = 0 + +# Maximum allowed length for mail keyword name. It's only forced when trying +# to create new keywords. +#mail_max_keyword_length = 50 + +# ':' separated list of directories under which chrooting is allowed for mail +# processes (ie. /var/mail will allow chrooting to /var/mail/foo/bar too). +# This setting doesn't affect login_chroot, mail_chroot or auth chroot +# settings. If this setting is empty, "/./" in home dirs are ignored. +# WARNING: Never add directories here which local users can modify, that +# may lead to root exploit. Usually this should be done only if you don't +# allow shell access for users. +#valid_chroot_dirs = + +# Default chroot directory for mail processes. This can be overridden for +# specific users in user database by giving /./ in user's home directory +# (eg. /home/./user chroots into /home). Note that usually there is no real +# need to do chrooting, Dovecot doesn't allow users to access files outside +# their mail directory anyway. If your home directories are prefixed with +# the chroot directory, append "/." to mail_chroot. +#mail_chroot = + +# UNIX socket path to master authentication server to find users. +# This is used by imap (for shared users) and lda. +#auth_socket_path = /run/dovecot/auth-userdb + +# Directory where to look up mail plugins. +#mail_plugin_dir = /usr/lib/dovecot/modules + +# Space separated list of plugins to load for all services. Plugins specific to +# IMAP, LDA, etc. are added to this list in their own .conf files. +#mail_plugins = + +## +## Mailbox handling optimizations +## + +# Mailbox list indexes can be used to optimize IMAP STATUS commands. They are +# also required for IMAP NOTIFY extension to be enabled. +#mailbox_list_index = yes + +# Trust mailbox list index to be up-to-date. This reduces disk I/O at the cost +# of potentially returning out-of-date results after e.g. server crashes. +# The results will be automatically fixed once the folders are opened. +#mailbox_list_index_very_dirty_syncs = yes + +# Should INBOX be kept up-to-date in the mailbox list index? By default it's +# not, because most of the mailbox accesses will open INBOX anyway. +#mailbox_list_index_include_inbox = no + +# The minimum number of mails in a mailbox before updates are done to cache +# file. This allows optimizing Dovecot's behavior to do less disk writes at +# the cost of more disk reads. +#mail_cache_min_mail_count = 0 + +# When IDLE command is running, mailbox is checked once in a while to see if +# there are any new mails or other changes. This setting defines the minimum +# time to wait between those checks. Dovecot can also use inotify and +# kqueue to find out immediately when changes occur. +#mailbox_idle_check_interval = 30 secs + +# Save mails with CR+LF instead of plain LF. This makes sending those mails +# take less CPU, especially with sendfile() syscall with Linux and FreeBSD. +# But it also creates a bit more disk I/O which may just make it slower. +# Also note that if other software reads the mboxes/maildirs, they may handle +# the extra CRs wrong and cause problems. +#mail_save_crlf = no + +# Max number of mails to keep open and prefetch to memory. This only works with +# some mailbox formats and/or operating systems. +#mail_prefetch_count = 0 + +# How often to scan for stale temporary files and delete them (0 = never). +# These should exist only after Dovecot dies in the middle of saving mails. +#mail_temp_scan_interval = 1w + +# How many slow mail accesses sorting can perform before it returns failure. +# With IMAP the reply is: NO [LIMIT] Requested sort would have taken too long. +# The untagged SORT reply is still returned, but it's likely not correct. +#mail_sort_max_read_count = 0 + +protocol !indexer-worker { + # If folder vsize calculation requires opening more than this many mails from + # disk (i.e. mail sizes aren't in cache already), return failure and finish + # the calculation via indexer process. Disabled by default. This setting must + # be 0 for indexer-worker processes. + #mail_vsize_bg_after_count = 0 +} + +## +## Maildir-specific settings +## + +# By default LIST command returns all entries in maildir beginning with a dot. +# Enabling this option makes Dovecot return only entries which are directories. +# This is done by stat()ing each entry, so it causes more disk I/O. +# (For systems setting struct dirent->d_type, this check is free and it's +# done always regardless of this setting) +#maildir_stat_dirs = no + +# When copying a message, do it with hard links whenever possible. This makes +# the performance much better, and it's unlikely to have any side effects. +#maildir_copy_with_hardlinks = yes + +# Assume Dovecot is the only MUA accessing Maildir: Scan cur/ directory only +# when its mtime changes unexpectedly or when we can't find the mail otherwise. +#maildir_very_dirty_syncs = no + +# If enabled, Dovecot doesn't use the S= in the Maildir filenames for +# getting the mail's physical size, except when recalculating Maildir++ quota. +# This can be useful in systems where a lot of the Maildir filenames have a +# broken size. The performance hit for enabling this is very small. +#maildir_broken_filename_sizes = no + +# Always move mails from new/ directory to cur/, even when the \Recent flags +# aren't being reset. +#maildir_empty_new = no + +## +## mbox-specific settings +## + +# Which locking methods to use for locking mbox. There are four available: +# dotlock: Create .lock file. This is the oldest and most NFS-safe +# solution. If you want to use /var/mail/ like directory, the users +# will need write access to that directory. +# dotlock_try: Same as dotlock, but if it fails because of permissions or +# because there isn't enough disk space, just skip it. +# fcntl : Use this if possible. Works with NFS too if lockd is used. +# flock : May not exist in all systems. Doesn't work with NFS. +# lockf : May not exist in all systems. Doesn't work with NFS. +# +# You can use multiple locking methods; if you do the order they're declared +# in is important to avoid deadlocks if other MTAs/MUAs are using multiple +# locking methods as well. Some operating systems don't allow using some of +# them simultaneously. +#mbox_read_locks = fcntl +#mbox_write_locks = dotlock fcntl +mbox_write_locks = fcntl + +# Maximum time to wait for lock (all of them) before aborting. +#mbox_lock_timeout = 5 mins + +# If dotlock exists but the mailbox isn't modified in any way, override the +# lock file after this much time. +#mbox_dotlock_change_timeout = 2 mins + +# When mbox changes unexpectedly we have to fully read it to find out what +# changed. If the mbox is large this can take a long time. Since the change +# is usually just a newly appended mail, it'd be faster to simply read the +# new mails. If this setting is enabled, Dovecot does this but still safely +# fallbacks to re-reading the whole mbox file whenever something in mbox isn't +# how it's expected to be. The only real downside to this setting is that if +# some other MUA changes message flags, Dovecot doesn't notice it immediately. +# Note that a full sync is done with SELECT, EXAMINE, EXPUNGE and CHECK +# commands. +#mbox_dirty_syncs = yes + +# Like mbox_dirty_syncs, but don't do full syncs even with SELECT, EXAMINE, +# EXPUNGE or CHECK commands. If this is set, mbox_dirty_syncs is ignored. +#mbox_very_dirty_syncs = no + +# Delay writing mbox headers until doing a full write sync (EXPUNGE and CHECK +# commands and when closing the mailbox). This is especially useful for POP3 +# where clients often delete all mails. The downside is that our changes +# aren't immediately visible to other MUAs. +#mbox_lazy_writes = yes + +# If mbox size is smaller than this (e.g. 100k), don't write index files. +# If an index file already exists it's still read, just not updated. +#mbox_min_index_size = 0 + +# Mail header selection algorithm to use for MD5 POP3 UIDLs when +# pop3_uidl_format=%m. For backwards compatibility we use apop3d inspired +# algorithm, but it fails if the first Received: header isn't unique in all +# mails. An alternative algorithm is "all" that selects all headers. +#mbox_md5 = apop3d + +## +## mdbox-specific settings +## + +# Maximum dbox file size until it's rotated. +#mdbox_rotate_size = 10M + +# Maximum dbox file age until it's rotated. Typically in days. Day begins +# from midnight, so 1d = today, 2d = yesterday, etc. 0 = check disabled. +#mdbox_rotate_interval = 0 + +# When creating new mdbox files, immediately preallocate their size to +# mdbox_rotate_size. This setting currently works only in Linux with some +# filesystems (ext4, xfs). +#mdbox_preallocate_space = no + +## +## Mail attachments +## + +# sdbox and mdbox support saving mail attachments to external files, which +# also allows single instance storage for them. Other backends don't support +# this for now. + +# Directory root where to store mail attachments. Disabled, if empty. +#mail_attachment_dir = + +# Attachments smaller than this aren't saved externally. It's also possible to +# write a plugin to disable saving specific attachments externally. +#mail_attachment_min_size = 128k + +# Filesystem backend to use for saving attachments: +# posix : No SiS done by Dovecot (but this might help FS's own deduplication) +# sis posix : SiS with immediate byte-by-byte comparison during saving +# sis-queue posix : SiS with delayed comparison and deduplication +#mail_attachment_fs = sis posix + +# Hash format to use in attachment filenames. You can add any text and +# variables: %{md4}, %{md5}, %{sha1}, %{sha256}, %{sha512}, %{size}. +# Variables can be truncated, e.g. %{sha256:80} returns only first 80 bits +#mail_attachment_hash = %{sha1} + +# Settings to control adding $HasAttachment or $HasNoAttachment keywords. +# By default, all MIME parts with Content-Disposition=attachment, or inlines +# with filename parameter are consired attachments. +# add-flags - Add the keywords when saving new mails or when fetching can +# do it efficiently. +# content-type=type or !type - Include/exclude content type. Excluding will +# never consider the matched MIME part as attachment. Including will only +# negate an exclusion (e.g. content-type=!foo/* content-type=foo/bar). +# exclude-inlined - Exclude any Content-Disposition=inline MIME part. +#mail_attachment_detection_options = diff --git a/recipient/etc/dovecot/conf.d/10-master.conf b/recipient/etc/dovecot/conf.d/10-master.conf new file mode 100644 index 0000000..64fa0f2 --- /dev/null +++ b/recipient/etc/dovecot/conf.d/10-master.conf @@ -0,0 +1,133 @@ +#default_process_limit = 100 +#default_client_limit = 1000 + +# Default VSZ (virtual memory size) limit for service processes. This is mainly +# intended to catch and kill processes that leak memory before they eat up +# everything. +#default_vsz_limit = 256M + +# Login user is internally used by login processes. This is the most untrusted +# user in Dovecot system. It shouldn't have access to anything at all. +#default_login_user = dovenull + +# Internal user is used by unprivileged processes. It should be separate from +# login user, so that login processes can't disturb other processes. +#default_internal_user = dovecot + +service imap-login { + inet_listener imap { + #port = 143 + } + inet_listener imaps { + #port = 993 + #ssl = yes + } + + # Number of connections to handle before starting a new process. Typically + # the only useful values are 0 (unlimited) or 1. 1 is more secure, but 0 + # is faster. + #service_count = 1 + + # Number of processes to always keep waiting for more connections. + #process_min_avail = 0 + + # If you set service_count=0, you probably need to grow this. + #vsz_limit = $default_vsz_limit +} + +service pop3-login { + inet_listener pop3 { + #port = 110 + } + inet_listener pop3s { + #port = 995 + #ssl = yes + } +} + +service submission-login { + inet_listener submission { + #port = 587 + } + inet_listener submissions { + #port = 465 + } +} + +service lmtp { + unix_listener lmtp { + #mode = 0666 + } + + # Create inet listener only if you can't use the above UNIX socket + #inet_listener lmtp { + # Avoid making LMTP visible for the entire internet + #address = + #port = + #} +} + +service imap { + # Most of the memory goes to mmap()ing files. You may need to increase this + # limit if you have huge mailboxes. + #vsz_limit = $default_vsz_limit + + # Max. number of IMAP processes (connections) + #process_limit = 1024 +} + +service pop3 { + # Max. number of POP3 processes (connections) + #process_limit = 1024 +} + +service submission { + # Max. number of SMTP Submission processes (connections) + #process_limit = 1024 +} + +service auth { + # auth_socket_path points to this userdb socket by default. It's typically + # used by dovecot-lda, doveadm, possibly imap process, etc. Users that have + # full permissions to this socket are able to get a list of all usernames and + # get the results of everyone's userdb lookups. + # + # The default 0666 mode allows anyone to connect to the socket, but the + # userdb lookups will succeed only if the userdb returns an "uid" field that + # matches the caller process's UID. Also if caller's uid or gid matches the + # socket's uid or gid the lookup succeeds. Anything else causes a failure. + # + # To give the caller full permissions to lookup all users, set the mode to + # something else than 0666 and Dovecot lets the kernel enforce the + # permissions (e.g. 0777 allows everyone full permissions). + unix_listener auth-userdb { + #mode = 0666 + #user = + #group = + } + + # Postfix smtp-auth + #unix_listener /var/spool/postfix/private/auth { + # mode = 0666 + #} + + # Auth process is run as this user. + #user = $default_internal_user +} + +service auth-worker { + # Auth worker process is run as root by default, so that it can access + # /etc/shadow. If this isn't necessary, the user should be changed to + # $default_internal_user. + #user = root +} + +service dict { + # If dict proxy is used, mail processes should have access to its socket. + # For example: mode=0660, group=vmail and global mail_access_groups=vmail + unix_listener dict { + #mode = 0600 + #user = + #group = + } +} diff --git a/recipient/etc/dovecot/conf.d/10-metrics.conf b/recipient/etc/dovecot/conf.d/10-metrics.conf new file mode 100644 index 0000000..f7a758f --- /dev/null +++ b/recipient/etc/dovecot/conf.d/10-metrics.conf @@ -0,0 +1,74 @@ +## +## Statistics and metrics +## + +# Dovecot supports gathering statistics from events. +# Currently there are no statistics logged by default, and therefore they must +# be explicitly added using the metric configuration blocks. +# +# Unlike old stats, the new statistics do not require any plugins loaded. +# +# See https://doc.dovecot.org/configuration_manual/stats/ for more details. + +## +## Example metrics +## + +#metric auth_success { +# filter = event=auth_request_finished AND success=yes +#} +# +#metric auth_failures { +# filter = event=auth_request_finished AND NOT success=yes +#} +# +#metric imap_command { +# filter = event=imap_command_finished +# group_by = cmd_name tagged_reply_state +#} +# +#metric smtp_command { +# filter = event=smtp_server_command_finished +# group_by = cmd_name status_code duration:exponential:1:5:10 +#} +# +#metric mail_delivery { +# filter = event=mail_delivery_finished +# group_by = duration:exponential:1:5:10 +#} + +## +## Prometheus +## + +# To allow access to statistics with Prometheus, enable http listener +# on stats process. Stats will be available on /metrics path. +# +# See https://doc.dovecot.org/configuration_manual/stats/openmetrics/ for more +# details. + +#service stats { +# inet_listener http { +# port = 9900 +# } +#} + +## +## Event exporting +## + +# You can also export individual events. +# +# See https://doc.dovecot.org/configuration_manual/event_export/ for more +# details. + +#event_exporter log { +# format = json +# format_args = time-rfc3339 +# transport = log +#} +# +#metric imap_commands { +# exporter = log +# filter = event=imap_command_finished +#} diff --git a/recipient/etc/dovecot/conf.d/10-ssl.conf b/recipient/etc/dovecot/conf.d/10-ssl.conf new file mode 100644 index 0000000..e50aec6 --- /dev/null +++ b/recipient/etc/dovecot/conf.d/10-ssl.conf @@ -0,0 +1,85 @@ +## +## SSL settings +## + +# SSL/TLS support: yes, no, required. +# Disable plain (unencrypted) POP3 and IMAP, allowed are only POP3+TLS, +# POP3S, IMAP+TLS and IMAPS. +# Plain IMAP and POP3 are still allowed for local connections. +ssl = required + +# PEM encoded X.509 SSL/TLS certificate and private key. They're opened before +# dropping root privileges, so keep the key file unreadable by anyone but +# root. Included doc/mkcert.sh can be used to easily generate self-signed +# certificate, just make sure to update the domains in dovecot-openssl.cnf +ssl_cert = was automatically rejected:%n%r + +# Delimiter character between local-part and detail in email address. +#recipient_delimiter = + + +# Header where the original recipient address (SMTP's RCPT TO: address) is taken +# from if not available elsewhere. With dovecot-lda -a parameter overrides this. +# A commonly used header for this is X-Original-To. +#lda_original_recipient_header = + +# Should saving a mail to a nonexistent mailbox automatically create it? +#lda_mailbox_autocreate = no + +# Should automatically created mailboxes be also automatically subscribed? +#lda_mailbox_autosubscribe = no + +protocol lda { + # Space separated list of plugins to load (default is global mail_plugins). + #mail_plugins = $mail_plugins +} diff --git a/recipient/etc/dovecot/conf.d/15-mailboxes.conf b/recipient/etc/dovecot/conf.d/15-mailboxes.conf new file mode 100644 index 0000000..5eb7ab7 --- /dev/null +++ b/recipient/etc/dovecot/conf.d/15-mailboxes.conf @@ -0,0 +1,73 @@ +## +## Mailbox definitions +## + +# Each mailbox is specified in a separate mailbox section. The section name +# specifies the mailbox name. If it has spaces, you can put the name +# "in quotes". These sections can contain the following mailbox settings: +# +# auto: +# Indicates whether the mailbox with this name is automatically created +# implicitly when it is first accessed. The user can also be automatically +# subscribed to the mailbox after creation. The following values are +# defined for this setting: +# +# no - Never created automatically. +# create - Automatically created, but no automatic subscription. +# subscribe - Automatically created and subscribed. +# +# special_use: +# A space-separated list of SPECIAL-USE flags (RFC 6154) to use for the +# mailbox. There are no validity checks, so you could specify anything +# you want in here, but it's not a good idea to use flags other than the +# standard ones specified in the RFC: +# +# \All - This (virtual) mailbox presents all messages in the +# user's message store. +# \Archive - This mailbox is used to archive messages. +# \Drafts - This mailbox is used to hold draft messages. +# \Flagged - This (virtual) mailbox presents all messages in the +# user's message store marked with the IMAP \Flagged flag. +# \Important - This (virtual) mailbox presents all messages in the +# user's message store deemed important to user. +# \Junk - This mailbox is where messages deemed to be junk mail +# are held. +# \Sent - This mailbox is used to hold copies of messages that +# have been sent. +# \Trash - This mailbox is used to hold messages that have been +# deleted. +# +# comment: +# Defines a default comment or note associated with the mailbox. This +# value is accessible through the IMAP METADATA mailbox entries +# "/shared/comment" and "/private/comment". Users with sufficient +# privileges can override the default value for entries with a custom +# value. + +# NOTE: Assumes "namespace inbox" has been defined in 10-mail.conf. +namespace inbox { + mailbox Archive { + auto = subscribe + special_use = \Archive + } + mailbox Junk { + auto = subscribe + special_use = \Junk + } + mailbox Trash { + auto = subscribe + special_use = \Trash + } + + # Create, but do not autosubscribe, to the following mailboxes, because + # sending e-mail is disabled on the receiving host. + mailbox Drafts { + auto = create + special_use = \Drafts + } + + mailbox Sent { + auto = create + special_use = \Sent + } +} diff --git a/recipient/etc/dovecot/conf.d/20-imap.conf b/recipient/etc/dovecot/conf.d/20-imap.conf new file mode 100644 index 0000000..e60b0cd --- /dev/null +++ b/recipient/etc/dovecot/conf.d/20-imap.conf @@ -0,0 +1,99 @@ +## +## IMAP specific settings +## + +# If nothing happens for this long while client is IDLEing, move the connection +# to imap-hibernate process and close the old imap process. This saves memory, +# because connections use very little memory in imap-hibernate process. The +# downside is that recreating the imap process back uses some resources. +#imap_hibernate_timeout = 0 + +# Maximum IMAP command line length. Some clients generate very long command +# lines with huge mailboxes, so you may need to raise this if you get +# "Too long argument" or "IMAP command line too large" errors often. +#imap_max_line_length = 64k + +# IMAP logout format string: +# %i - total number of bytes read from client +# %o - total number of bytes sent to client +# %{fetch_hdr_count} - Number of mails with mail header data sent to client +# %{fetch_hdr_bytes} - Number of bytes with mail header data sent to client +# %{fetch_body_count} - Number of mails with mail body data sent to client +# %{fetch_body_bytes} - Number of bytes with mail body data sent to client +# %{deleted} - Number of mails where client added \Deleted flag +# %{expunged} - Number of mails that client expunged, which does not +# include automatically expunged mails +# %{autoexpunged} - Number of mails that were automatically expunged after +# client disconnected +# %{trashed} - Number of mails that client copied/moved to the +# special_use=\Trash mailbox. +# %{appended} - Number of mails saved during the session +#imap_logout_format = in=%i out=%o deleted=%{deleted} expunged=%{expunged} \ +# trashed=%{trashed} hdr_count=%{fetch_hdr_count} \ +# hdr_bytes=%{fetch_hdr_bytes} body_count=%{fetch_body_count} \ +# body_bytes=%{fetch_body_bytes} + +# Override the IMAP CAPABILITY response. If the value begins with '+', +# add the given capabilities on top of the defaults (e.g. +XFOO XBAR). +#imap_capability = + +# How long to wait between "OK Still here" notifications when client is +# IDLEing. +#imap_idle_notify_interval = 2 mins + +# ID field names and values to send to clients. Using * as the value makes +# Dovecot use the default value. The following fields have default values +# currently: name, version, os, os-version, support-url, support-email, +# revision. +#imap_id_send = + +# ID fields sent by client to log. * means everything. +#imap_id_log = + +# Workarounds for various client bugs: +# delay-newmail: +# Send EXISTS/RECENT new mail notifications only when replying to NOOP +# and CHECK commands. Some clients ignore them otherwise, for example OSX +# Mail ( for list of plugins and +# their configuration. Note that %variable expansion is done for all values. + +plugin { + #setting_name = value +} diff --git a/recipient/etc/dovecot/conf.d/90-quota.conf b/recipient/etc/dovecot/conf.d/90-quota.conf new file mode 100644 index 0000000..3308c05 --- /dev/null +++ b/recipient/etc/dovecot/conf.d/90-quota.conf @@ -0,0 +1,83 @@ +## +## Quota configuration. +## + +# Note that you also have to enable quota plugin in mail_plugins setting. +# + +## +## Quota limits +## + +# Quota limits are set using "quota_rule" parameters. To get per-user quota +# limits, you can set/override them by returning "quota_rule" extra field +# from userdb. It's also possible to give mailbox-specific limits, for example +# to give additional 100 MB when saving to Trash: + +plugin { + #quota_rule = *:storage=1G + #quota_rule2 = Trash:storage=+100M + + # LDA/LMTP allows saving the last mail to bring user from under quota to + # over quota, if the quota doesn't grow too high. Default is to allow as + # long as quota will stay under 10% above the limit. Also allowed e.g. 10M. + #quota_grace = 10%% + + # Quota plugin can also limit the maximum accepted mail size. + #quota_max_mail_size = 100M +} + +## +## Quota warnings +## + +# You can execute a given command when user exceeds a specified quota limit. +# Each quota root has separate limits. Only the command for the first +# exceeded limit is executed, so put the highest limit first. +# The commands are executed via script service by connecting to the named +# UNIX socket (quota-warning below). +# Note that % needs to be escaped as %%, otherwise "% " expands to empty. + +plugin { + #quota_warning = storage=95%% quota-warning 95 %u + #quota_warning2 = storage=80%% quota-warning 80 %u +} + +# Example quota-warning service. The unix listener's permissions should be +# set in a way that mail processes can connect to it. Below example assumes +# that mail processes run as vmail user. If you use mode=0666, all system users +# can generate quota warnings to anyone. +#service quota-warning { +# executable = script /usr/local/bin/quota-warning.sh +# user = dovecot +# unix_listener quota-warning { +# user = vmail +# } +#} + +## +## Quota backends +## + +# Multiple backends are supported: +# dirsize: Find and sum all the files found from mail directory. +# Extremely SLOW with Maildir. It'll eat your CPU and disk I/O. +# dict: Keep quota stored in dictionary (eg. SQL) +# maildir: Maildir++ quota +# fs: Read-only support for filesystem quota + +plugin { + #quota = dirsize:User quota + #quota = maildir:User quota + #quota = dict:User quota::proxy::quota + #quota = fs:User quota +} + +# Multiple quota roots are also possible, for example this gives each user +# their own 100MB quota and one shared 1GB quota within the domain: +plugin { + #quota = dict:user::proxy::quota + #quota2 = dict:domain:%d:proxy::quota_domain + #quota_rule = *:storage=102400 + #quota2_rule = *:storage=1048576 +} diff --git a/recipient/etc/dovecot/conf.d/auth-checkpassword.conf.ext b/recipient/etc/dovecot/conf.d/auth-checkpassword.conf.ext new file mode 100644 index 0000000..b2fb13a --- /dev/null +++ b/recipient/etc/dovecot/conf.d/auth-checkpassword.conf.ext @@ -0,0 +1,21 @@ +# Authentication for checkpassword users. Included from 10-auth.conf. +# +# + +passdb { + driver = checkpassword + args = /usr/bin/checkpassword +} + +# passdb lookup should return also userdb info +userdb { + driver = prefetch +} + +# Standard checkpassword doesn't support direct userdb lookups. +# If you need checkpassword userdb, the checkpassword must support +# Dovecot-specific extensions. +#userdb { +# driver = checkpassword +# args = /usr/bin/checkpassword +#} diff --git a/recipient/etc/dovecot/conf.d/auth-deny.conf.ext b/recipient/etc/dovecot/conf.d/auth-deny.conf.ext new file mode 100644 index 0000000..ce3f1cf --- /dev/null +++ b/recipient/etc/dovecot/conf.d/auth-deny.conf.ext @@ -0,0 +1,15 @@ +# Deny access for users. Included from 10-auth.conf. + +# Users can be (temporarily) disabled by adding a passdb with deny=yes. +# If the user is found from that database, authentication will fail. +# The deny passdb should always be specified before others, so it gets +# checked first. + +# Example deny passdb using passwd-file. You can use any passdb though. +passdb { + driver = passwd-file + deny = yes + + # File contains a list of usernames, one per line + args = /etc/dovecot/deny-users +} diff --git a/recipient/etc/dovecot/conf.d/auth-dict.conf.ext b/recipient/etc/dovecot/conf.d/auth-dict.conf.ext new file mode 100644 index 0000000..0be4847 --- /dev/null +++ b/recipient/etc/dovecot/conf.d/auth-dict.conf.ext @@ -0,0 +1,16 @@ +# Authentication via dict backend. Included from 10-auth.conf. +# +# + +passdb { + driver = dict + + # Path for dict configuration file, see + # example-config/dovecot-dict-auth.conf.ext + args = /etc/dovecot/dovecot-dict-auth.conf.ext +} + +userdb { + driver = dict + args = /etc/dovecot/dovecot-dict-auth.conf.ext +} diff --git a/recipient/etc/dovecot/conf.d/auth-master.conf.ext b/recipient/etc/dovecot/conf.d/auth-master.conf.ext new file mode 100644 index 0000000..2cf128f --- /dev/null +++ b/recipient/etc/dovecot/conf.d/auth-master.conf.ext @@ -0,0 +1,16 @@ +# Authentication for master users. Included from 10-auth.conf. + +# By adding master=yes setting inside a passdb you make the passdb a list +# of "master users", who can log in as anyone else. +# + +# Example master user passdb using passwd-file. You can use any passdb though. +passdb { + driver = passwd-file + master = yes + args = /etc/dovecot/master-users + + # Unless you're using PAM, you probably still want the destination user to + # be looked up from passdb that it really exists. pass=yes does that. + pass = yes +} diff --git a/recipient/etc/dovecot/conf.d/auth-passwdfile.conf.ext b/recipient/etc/dovecot/conf.d/auth-passwdfile.conf.ext new file mode 100644 index 0000000..c89d28c --- /dev/null +++ b/recipient/etc/dovecot/conf.d/auth-passwdfile.conf.ext @@ -0,0 +1,20 @@ +# Authentication for passwd-file users. Included from 10-auth.conf. +# +# passwd-like file with specified location. +# + +passdb { + driver = passwd-file + args = scheme=CRYPT username_format=%u /etc/dovecot/users +} + +userdb { + driver = passwd-file + args = username_format=%u /etc/dovecot/users + + # Default fields that can be overridden by passwd-file + #default_fields = quota_rule=*:storage=1G + + # Override fields from passwd-file + #override_fields = home=/home/virtual/%u +} diff --git a/recipient/etc/dovecot/conf.d/auth-static.conf.ext b/recipient/etc/dovecot/conf.d/auth-static.conf.ext new file mode 100644 index 0000000..90890c5 --- /dev/null +++ b/recipient/etc/dovecot/conf.d/auth-static.conf.ext @@ -0,0 +1,24 @@ +# Static passdb. Included from 10-auth.conf. + +# This can be used for situations where Dovecot doesn't need to verify the +# username or the password, or if there is a single password for all users: +# +# - proxy frontend, where the backend verifies the password +# - proxy backend, where the frontend already verified the password +# - authentication with SSL certificates +# - simple testing + +#passdb { +# driver = static +# args = proxy=y host=%1Mu.example.com nopassword=y +#} + +#passdb { +# driver = static +# args = password=test +#} + +#userdb { +# driver = static +# args = uid=vmail gid=vmail home=/home/%u +#} diff --git a/recipient/etc/dovecot/conf.d/auth-system.conf.ext b/recipient/etc/dovecot/conf.d/auth-system.conf.ext new file mode 100644 index 0000000..dadb9f7 --- /dev/null +++ b/recipient/etc/dovecot/conf.d/auth-system.conf.ext @@ -0,0 +1,74 @@ +# Authentication for system users. Included from 10-auth.conf. +# +# +# + +# PAM authentication. Preferred nowadays by most systems. +# PAM is typically used with either userdb passwd or userdb static. +# REMEMBER: You'll need /etc/pam.d/dovecot file created for PAM +# authentication to actually work. +passdb { + driver = pam + # [session=yes] [setcred=yes] [failure_show_msg=yes] [max_requests=] + # [cache_key=] [] + #args = dovecot +} + +# System users (NSS, /etc/passwd, or similar). +# In many systems nowadays this uses Name Service Switch, which is +# configured in /etc/nsswitch.conf. +#passdb { + #driver = passwd + # [blocking=no] + #args = +#} + +# Shadow passwords for system users (NSS, /etc/shadow or similar). +# Deprecated by PAM nowadays. +# +#passdb { + #driver = shadow + # [blocking=no] + #args = +#} + +# PAM-like authentication for OpenBSD. +# +#passdb { + #driver = bsdauth + # [blocking=no] [cache_key=] + #args = +#} + +## +## User databases +## + +# System users (NSS, /etc/passwd, or similar). In many systems nowadays this +# uses Name Service Switch, which is configured in /etc/nsswitch.conf. +userdb { + # + driver = passwd + # [blocking=no] + #args = + + # Override fields from passwd + #override_fields = home=/home/virtual/%u +} + +# Static settings generated from template +#userdb { + #driver = static + # Can return anything a userdb could normally return. For example: + # + # args = uid=500 gid=500 home=/var/mail/%u + # + # LDA and LMTP needs to look up users only from the userdb. This of course + # doesn't work with static userdb because there is no list of users. + # Normally static userdb handles this by doing a passdb lookup. This works + # with most passdbs, with PAM being the most notable exception. If you do + # the user verification another way, you can add allow_all_users=yes to + # the args in which case the passdb lookup is skipped. + # + #args = +#} diff --git a/recipient/etc/dovecot/dovecot-dict-auth.conf.ext b/recipient/etc/dovecot/dovecot-dict-auth.conf.ext new file mode 100644 index 0000000..79f43de --- /dev/null +++ b/recipient/etc/dovecot/dovecot-dict-auth.conf.ext @@ -0,0 +1,54 @@ +# This file is commonly accessed via passdb {} or userdb {} section in +# conf.d/auth-dict.conf.ext + +# Dictionary URI +#uri = + +# Default password scheme +default_pass_scheme = MD5 + +# Username iteration prefix. Keys under this are assumed to contain usernames. +iterate_prefix = userdb/ + +# Should iteration be disabled for this userdb? If this userdb acts only as a +# cache there's no reason to try to iterate the (partial & duplicate) users. +#iterate_disable = no + +# The example here shows how to do multiple dict lookups and merge the replies. +# The "passdb" and "userdb" keys are JSON objects containing key/value pairs, +# for example: { "uid": 1000, "gid": 1000, "home": "/home/user" } + +key passdb { + key = passdb/%u + format = json +} +key userdb { + key = userdb/%u + format = json +} +key quota { + key = userdb/%u/quota + #format = value + # The default_value is used if the key isn't found. If default_value setting + # isn't specified at all (even as empty), the passdb/userdb lookup fails with + # "user doesn't exist". + default_value = 100M +} + +# Space separated list of keys whose values contain key/value paired objects. +# All the key/value pairs inside the object are added as passdb fields. +passdb_objects = passdb + +#passdb_fields { +#} + +# Userdb key/value object list. +userdb_objects = userdb + +userdb_fields { + # dict: refers to key names + quota_rule = *:storage=%{dict:quota} + + # dict:. refers to the objkey inside (JSON) object + mail = maildir:%{dict:userdb.home}/Maildir +} diff --git a/recipient/etc/dovecot/dovecot-oauth2.conf.ext b/recipient/etc/dovecot/dovecot-oauth2.conf.ext new file mode 100644 index 0000000..4b3b8ba --- /dev/null +++ b/recipient/etc/dovecot/dovecot-oauth2.conf.ext @@ -0,0 +1,69 @@ +### OAuth2 password database configuration + +## url for verifying token validity. Token is appended to the URL +# tokeninfo_url = http://endpoint/oauth/tokeninfo?access_token= + +## introspection endpoint, used to gather extra fields and other information. +# introspection_url = http://endpoint/oauth/me + +## How introspection is made, valid values are +## auth = GET request with Bearer authentication +## get = GET request with token appended to URL +## post = POST request with token=bearer_token as content +## local = perform local validation only +# introspection_mode = auth + +## Force introspection even if tokeninfo contains wanted fields +## Set this to yes if you are using active_attribute +# force_introspection = no + +## Validation key dictionary (e.g. fs:posix:prefix=/etc/dovecot/keys/) +## Lookup key is /shared/// +# local_validation_key_dict = + +## A single wanted scope of validity (optional) +# scope = something + +## username attribute in response (default: email) +# username_attribute = email + +## username normalization format (default: %Lu) +# username_format = %Lu + +## Attribute name for checking whether account is disabled (optional) +# active_attribute = + +## Expected value in active_attribute (empty = require present, but anything goes) +# active_value = + +## Expected issuer(s) for the token (space separated list) +# issuers = + +## URL to RFC 7628 OpenID Provider Configuration Information schema +# openid_configuration_url = + +## Extra fields to set in passdb response (in passdb static style) +# pass_attrs = + +## Timeout in milliseconds +# timeout_msecs = 0 + +## Enable debug logging +# debug = no + +## Max parallel connections (how many simultaneous connections to open) +# max_parallel_connections = 10 + +## Max pipelined requests (how many requests to send per connection, requires server-side support) +# max_pipelined_requests = 1 + +## HTTP request raw log directory +# rawlog_dir = /tmp/oauth2 + +## TLS settings +# tls_ca_cert_file = /path/to/ca-certificates.txt +# tls_ca_cert_dir = /path/to/certs/ +# tls_cert_file = /path/to/client/cert +# tls_key_file = /path/to/client/key +# tls_cipher_suite = HIGH:!SSLv2 +# tls_allow_invalid_cert = FALSE diff --git a/recipient/etc/dovecot/dovecot-openssl.cnf b/recipient/etc/dovecot/dovecot-openssl.cnf new file mode 100644 index 0000000..f65a80c --- /dev/null +++ b/recipient/etc/dovecot/dovecot-openssl.cnf @@ -0,0 +1,31 @@ +[ req ] +default_bits = 2048 +encrypt_key = yes +distinguished_name = req_dn +x509_extensions = cert_type +prompt = no + +[ req_dn ] +# country (2 letter code) +#C=FI + +# State or Province Name (full name) +#ST= + +# Locality Name (eg. city) +#L=Helsinki + +# Organization (eg. company) +#O=Dovecot + +# Organizational Unit Name (eg. section) +OU=IMAP server + +# Common Name (*.example.com is also possible) +CN=imap.example.com + +# E-mail contact +emailAddress=postmaster@example.com + +[ cert_type ] +nsCertType = server diff --git a/recipient/etc/dovecot/dovecot.conf b/recipient/etc/dovecot/dovecot.conf new file mode 100644 index 0000000..cf94f11 --- /dev/null +++ b/recipient/etc/dovecot/dovecot.conf @@ -0,0 +1,7 @@ +protocols = imap +!include_try /usr/share/dovecot/protocols.d/*.conf + +#listen = *, :: + +!include conf.d/*.conf +!include_try local.conf diff --git a/recipient/etc/opendkim/opendkim.conf b/recipient/etc/opendkim/opendkim.conf new file mode 100644 index 0000000..5544439 --- /dev/null +++ b/recipient/etc/opendkim/opendkim.conf @@ -0,0 +1,15 @@ +BaseDirectory /run/opendkim +UserID opendkim + +# On ne signe rien; on ne fait que vérifier les signatures ici. +Mode v + +LogWhy yes +Syslog yes +SyslogSuccess yes + +Socket inet:8891@localhost +#Socket local:opendkim.sock + +ReportAddress postmaster@destinataire.example +SendReports yes diff --git a/recipient/etc/opendmarc/opendmarc.conf b/recipient/etc/opendmarc/opendmarc.conf new file mode 100644 index 0000000..c12a126 --- /dev/null +++ b/recipient/etc/opendmarc/opendmarc.conf @@ -0,0 +1,15 @@ +AuthservID mx.destinataire.example + +BaseDirectory /run/opendmarc + +#IgnoreHosts /etc/opendmarc/ignore.hosts + +PublicSuffixList /var/db/public_suffix_list.dat + +Socket inet:8893@localhost + +RejectFailures true + +SPFIgnoreResults true + +SPFSelfValidate true diff --git a/recipient/etc/postfix/main.cf b/recipient/etc/postfix/main.cf new file mode 100644 index 0000000..28cc218 --- /dev/null +++ b/recipient/etc/postfix/main.cf @@ -0,0 +1,23 @@ +compatibility_level = 3.7 + +myhostname = mx.destinataire.example +mydestination = destinataire.example + +# Journaliser sur STDOUT parce qu’on tourne dans un conteneur +maillog_file = /dev/stdout + +# Stockage des mails +home_mailbox = Maildir/ + +# Décommenter pour activer le contrôle SPF au niveau SMTP +# smtpd_recipient_restrictions = check_policy_service unix:private/policy + +# Décommenter pour activer DKIM et DMARC +# smtpd_milters = inet:127.0.0.1:8891, inet:127.0.0.1:8893 + +non_smtpd_milters = $smtpd_milters +milter_default_action = accept + +inet_protocols = ipv4 +meta_directory = /etc/postfix +shlib_directory = /usr/lib/postfix diff --git a/recipient/etc/postfix/master.cf b/recipient/etc/postfix/master.cf new file mode 100644 index 0000000..6816664 --- /dev/null +++ b/recipient/etc/postfix/master.cf @@ -0,0 +1,37 @@ +# ========================================================================== +# service type private unpriv chroot wakeup maxproc command + args +# (yes) (yes) (no) (never) (100) +# ========================================================================== + +smtp inet n - n - - smtpd +pickup unix n - n 60 1 pickup +cleanup unix n - n - 0 cleanup +qmgr unix n - n 300 1 qmgr +tlsmgr unix - - n 1000? 1 tlsmgr +rewrite unix - - n - - trivial-rewrite +bounce unix - - n - 0 bounce +defer unix - - n - 0 bounce +trace unix - - n - 0 bounce +verify unix - - n - 1 verify +flush unix n - n 1000? 0 flush +proxymap unix - - n - - proxymap +proxywrite unix - - n - 1 proxymap +smtp unix - - n - - smtp +# FIXME: a-t-on besoin de ce service ? +relay unix - - n - - smtp + -o syslog_name=postfix/$service_name +showq unix n - n - - showq +error unix - - n - - error +retry unix - - n - - error +discard unix - - n - - discard +local unix - n n - - local +virtual unix - n n - - virtual +lmtp unix - - n - - lmtp +anvil unix - - n - 1 anvil +scache unix - - n - 1 scache +postlog unix-dgram n - n - 1 postlogd + + +# Ajout pour le contrôle SPF +policy unix - n n - - spawn + user=nobody argv=/usr/bin/postfix-policyd-spf-perl diff --git a/recipient/etc/roundcube/config.inc.php b/recipient/etc/roundcube/config.inc.php new file mode 100644 index 0000000..ef915c5 --- /dev/null +++ b/recipient/etc/roundcube/config.inc.php @@ -0,0 +1,37 @@ + :: +## + +# demo1._domainkey.expediteur.example expediteur.example:demo1:/etc/opendkim/keys/expediteur.example/demo1.private +# demo2._domainkey.expediteur.example expediteur.example:demo2:/etc/opendkim/keys/expediteur.example/demo2.private + +# demo1._domainkey.newsletter.expediteur.example newsletter.expediteur.example:demo1:/etc/opendkim/keys/newsletter.expediteur.example/demo1.private +# demo2._domainkey.newsletter.expediteur.example newsletter.expediteur.example:demo2:/etc/opendkim/keys/newsletter.expediteur.example/demo2.private \ No newline at end of file diff --git a/sender/etc/opendkim/opendkim.conf b/sender/etc/opendkim/opendkim.conf new file mode 100644 index 0000000..ca65dff --- /dev/null +++ b/sender/etc/opendkim/opendkim.conf @@ -0,0 +1,22 @@ +BaseDirectory /run/opendkim +UserID opendkim + +Mode sv +Canonicalization relaxed/relaxed + +LogWhy yes +Syslog yes +SyslogSuccess yes + +Socket inet:8891@localhost + +ReportAddress postmaster@expediteur.example +SendReports yes + +## À terme, ce système signera les courriels provenant de plusieurs domaines. +## Il vaut donc mieux paramétrer une SigningTable (qui liste les expéditeurs +## pour lesquels on signe) et une KeyTable (qui liste les emplacements des +## clefs privées). +# +# SigningTable file:/etc/opendkim/signing_table +# KeyTable file:/etc/opendkim/key_table diff --git a/sender/etc/opendkim/signing_table b/sender/etc/opendkim/signing_table new file mode 100644 index 0000000..5fcbfea --- /dev/null +++ b/sender/etc/opendkim/signing_table @@ -0,0 +1,11 @@ +## +## FORMAT DE LA TABLE +## +## +## +## L’adresse mail peut être un wildcard (ex. *@expediteur.example). +## + +# expediteur.example demo1._domainkey.expediteur.example + +# newsletter.expediteur.example demo1._domainkey.newsletter.expediteur.example diff --git a/sender/etc/postfix/main.cf b/sender/etc/postfix/main.cf new file mode 100644 index 0000000..d84001b --- /dev/null +++ b/sender/etc/postfix/main.cf @@ -0,0 +1,16 @@ +compatibility_level = 3.7 + +myhostname = mx.expediteur.example +mydestination = expediteur.example + +# Journaliser sur STDOUT parce qu’on tourne dans un conteneur +maillog_file = /dev/stdout + +# Signer les mails sortants avec DKIM +smtpd_milters = inet:127.0.0.1:8891 +non_smtpd_milters = $smtpd_milters +milter_default_action = accept + +inet_protocols = ipv4 +meta_directory = /etc/postfix +shlib_directory = /usr/lib/postfix diff --git a/sender/etc/postfix/master.cf b/sender/etc/postfix/master.cf new file mode 100644 index 0000000..6816664 --- /dev/null +++ b/sender/etc/postfix/master.cf @@ -0,0 +1,37 @@ +# ========================================================================== +# service type private unpriv chroot wakeup maxproc command + args +# (yes) (yes) (no) (never) (100) +# ========================================================================== + +smtp inet n - n - - smtpd +pickup unix n - n 60 1 pickup +cleanup unix n - n - 0 cleanup +qmgr unix n - n 300 1 qmgr +tlsmgr unix - - n 1000? 1 tlsmgr +rewrite unix - - n - - trivial-rewrite +bounce unix - - n - 0 bounce +defer unix - - n - 0 bounce +trace unix - - n - 0 bounce +verify unix - - n - 1 verify +flush unix n - n 1000? 0 flush +proxymap unix - - n - - proxymap +proxywrite unix - - n - 1 proxymap +smtp unix - - n - - smtp +# FIXME: a-t-on besoin de ce service ? +relay unix - - n - - smtp + -o syslog_name=postfix/$service_name +showq unix n - n - - showq +error unix - - n - - error +retry unix - - n - - error +discard unix - - n - - discard +local unix - n n - - local +virtual unix - n n - - virtual +lmtp unix - - n - - lmtp +anvil unix - - n - 1 anvil +scache unix - - n - 1 scache +postlog unix-dgram n - n - 1 postlogd + + +# Ajout pour le contrôle SPF +policy unix - n n - - spawn + user=nobody argv=/usr/bin/postfix-policyd-spf-perl diff --git a/sender/etc/s6-overlay/s6-rc.d/opendkim/run b/sender/etc/s6-overlay/s6-rc.d/opendkim/run new file mode 100644 index 0000000..cb9f502 --- /dev/null +++ b/sender/etc/s6-overlay/s6-rc.d/opendkim/run @@ -0,0 +1,2 @@ +#!/bin/execlineb -P +/usr/sbin/opendkim -f diff --git a/sender/etc/s6-overlay/s6-rc.d/opendkim/type b/sender/etc/s6-overlay/s6-rc.d/opendkim/type new file mode 100644 index 0000000..1780f9f --- /dev/null +++ b/sender/etc/s6-overlay/s6-rc.d/opendkim/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/sender/etc/s6-overlay/s6-rc.d/postfix/run b/sender/etc/s6-overlay/s6-rc.d/postfix/run new file mode 100644 index 0000000..2a6d585 --- /dev/null +++ b/sender/etc/s6-overlay/s6-rc.d/postfix/run @@ -0,0 +1,2 @@ +#!/bin/execlineb -P +/usr/sbin/postfix start-fg -c /etc/postfix diff --git a/sender/etc/s6-overlay/s6-rc.d/postfix/type b/sender/etc/s6-overlay/s6-rc.d/postfix/type new file mode 100644 index 0000000..1780f9f --- /dev/null +++ b/sender/etc/s6-overlay/s6-rc.d/postfix/type @@ -0,0 +1 @@ +longrun \ No newline at end of file diff --git a/sender/etc/s6-overlay/s6-rc.d/user/contents.d/opendkim b/sender/etc/s6-overlay/s6-rc.d/user/contents.d/opendkim new file mode 100644 index 0000000..e69de29 diff --git a/sender/etc/s6-overlay/s6-rc.d/user/contents.d/postfix b/sender/etc/s6-overlay/s6-rc.d/user/contents.d/postfix new file mode 100644 index 0000000..e69de29 diff --git a/sender/scripts/send_confirmation_email.sh b/sender/scripts/send_confirmation_email.sh new file mode 100755 index 0000000..b3b86a2 --- /dev/null +++ b/sender/scripts/send_confirmation_email.sh @@ -0,0 +1,54 @@ +#!/bin/sh + +sendmail -f support@expediteur.example -F "Service client" \ + destinataire@destinataire.example < + + + + +

Bonjour,

+

La commande suivante a bien été confirmée :

+ + + + + + + + + + + + + + + + + + + + + +
QtéDésignationPU HTPU TTCSous-total
1Savonnette1,66 €2,00 €2,00 €
Total1,66 €2,00 €2,00 €
+

+ Pour suivre votre commande, connectez-vous à votre + espace client. +

+

Cordialement,

+

Le service client.

+ + + +EOF diff --git a/sender/scripts/send_newsletter.sh b/sender/scripts/send_newsletter.sh new file mode 100755 index 0000000..a1ffaef --- /dev/null +++ b/sender/scripts/send_newsletter.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +sendmail -f info@newsletter.expediteur.example -F "Newsletter" \ + destinataire@destinataire.example < + +

Bonjour bonjour !

+

+ Nous allons bientôt entamer la période des soldes. Profitez-en pour + faire de bonnes affaires chez nous ! +

+

+ Dans le prochain numéro de cette newsletter, nous vous proposerons + quelques offres irrésistibles. Ne les manquez pas ! +

+

Cordialement,

+

Le marketing.

+ + + +EOF