Premier jet fonctionnel et documenté

L’essentiel y est, y compris les instructions pour manipuler
l’infrastructure et comment suivre les scénarios.
This commit is contained in:
Marc van der Wal 2023-10-25 15:50:21 +02:00
parent 27dd30c857
commit ec37d3b892
91 changed files with 3511 additions and 0 deletions

308
INSTRUCTIONS.md Normal file
View File

@ -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 nest accessible que sur la boucle locale de votre machine virtuelle. Pour pouvoir lutiliser sur votre machine, il vous faut vous connecter à la machine qui exécute le démonstrateur à laide de la commande:
$ ssh -L 8225:localhost:8225 <nom de votre machine>
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 lenvironnement en exécutant le script `start.sh`:
$ ./start.sh
Vous devriez alors voir safficher 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 lun 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 dutiliser 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 à ladresse <http://localhost:8225>. Vous devriez obtenir une page de connexion pour la webmail Roundcube. Connectez-vous avec lidentifiant **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 dun attaquant qui usurpe lidentité de lentreprise pour envoyer de fausses confirmations de commande. Ces confirmations de commande, qui font croire à une commande pour des milliers deuros de produits, sont conçues pour faire peur et pousser la victime à cliquer sur le lien dannulation présent dans le mail et remplir ses informations de carte bleue.
### Envoi dun mail légitime
Tout dabord, pour tester le système, envoyez un mail de confirmation de commande légitime en utilisant linfrastructure de lexpéditeur:
1. Dans le terminal, basculez sur la fenêtre **Expéditeur**.
2. Exécutez le script denvoi 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 dun mail frauduleux
Il est maintenant temps de passer du côté de lattaquant et denvoyer votre premier courriel frauduleux. Pour cela:
1. Dans le terminal, basculez sur la fenêtre **Attaquant**.
2. Exécutez le script de lattaquant 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 lenveloppe 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. Najoutez 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 nest pas celle de lattaquant;
* le corps du message, transmise après la commande `DATA`, qui contient une ligne `From` dont ladresse e-mail est également fausse.
Et lorsque vous affichez la boîte de réception du destinataire, vous verrez quune fausse confirmation de commande a bien été remise dans la boîte de réception.
Vous venez de réaliser votre première usurpation didentité!
### 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`, cest-à-dire la machine qui a envoyé la vraie confirmation de commande, a le droit denvoyer 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 à linvite de commandes, rechargez le fichier de zones avec la commande:
nsd-control reload expediteur.example
Vous devriez voir le message «ok». Sinon, cela signifie quune erreur de syntaxe sest 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 laffichage de cette commande, vous devriez pouvoir identifier la politique SPF que vous venez dinstaller.
Pour les lecteurs avertis: normalement, après chaque modification dun fichier de zone, il faudrait incrémenter le numéro de série contenu dans lentré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 lexpéditeur.
Cependant, si vous réessayez denvoyer 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 sagit dactiver 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 lespace qui suivait ce caractère (cest 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, quil 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 lattaquant, essayez denvoyer 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, lattaquant na même pas loccasion denvoyer le contenu du courriel, car cest ladresse de lexpéditeur dans la commande `MAIL FROM` qui est contrôlée.
Dailleurs, vous pouvez vérifier que les vraies confirmations de commande passent toujours: depuis la fenêtre **Expéditeur**, relancez le script denvoi de-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. Lattaquant peut encore contourner cette mesure.
Sur un courrier physique, on peut écrire une adresse retour sur lenveloppe et une adresse différente sur le papier à en-têtes quon glisse dans lenveloppe. 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 dun 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 lattaquant 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 lenveloppe 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. Najoutez pas de signature DKIM; tapez directement **Entrée**.
Cette fois, le courriel est accepté par le serveur SMTP: lattaquant a prétendu que le courriel frauduleux venait de chez lui dans lenveloppe; mais dans le courriel lui-même, visible après la commande DATA, lattaquant se fait toujours passer pour le service client. De plus, cest toujours ladresse 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 lexpé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 linfrastructure de lexpéditeur, cest le logiciel **opendkim** qui se charge dinsé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 lexpé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 larborescence 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 linté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 linstallation 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 laffichage 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 lexpéditeur ###
Nous avons donc bien publié nos clefs DKIM dans le DNS. Finissons de configurer **opendkim** chez lexpé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 lespace qui le suivait.
3. Éditez le fichier `/etc/opendkim/signing_table` et décommentez la ligne commençant par `expediteur.example`. Ceci active lajout 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 lemplacement 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 lexpé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 dune 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 lespace qui suivait ce caractère.
3. Sauvegardez, quittez puis notifiez Postfix dun 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 dune 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 lintégralité des en-têtes. Notez lapparition 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 quil ny avait aucune politique DMARC.
Vous devriez aussi voir un en-tête **DKIM-Signature**, contenant la signature DKIM.
#### Dépannage ####
Si le courriel narrive pas, il se peut quil nait pas pu être signé par opendkim. Le symptôme est lapparition, 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=<support@expediteur.example> to=<destinataire@destinataire.example>
Dans ce cas:
* vérifiez quil ny ait pas eu derreurs dans la configuration dopendkim;
* 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 navons toujours pas contrecarré la campagne de hameçonnage que mène lattaquant au moyen de ces fausses confirmations de commande. Cest là où DMARC entre en jeu.
Maintenant que le système destinataire contrôle SPF, DKIM et DMARC, nous pouvons protéger lexpéditeur en ajoutant une politique DMARC.
### Configuration DMARC chez lexpéditeur ###
Pour configurer DMARC chez lexpé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`.
### Lattaquant est-il vaincu? ###
Voyons voir maintenant si les mails dhameçonnage de lattaquant passent encore. Pour rappel:
1. Dans le terminal, basculez sur la fenêtre **Attaquant**.
2. Exécutez le script de lattaquant 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 lenveloppe SMTP (choix **2**).
5. Utilisez le HELO par défaut; tapez directement **Entrée**.
6. Najoutez pas de signature DKIM; tapez directement **Entrée**.
Cette fois, après que lattaquant 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 lerreur `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; cest parce que la politique DMARC `p=reject` sapplique aux sous-domaines de `expediteur.example`. Il manque aussi une politique SPF pour `newsletter.expediteur.example` et des clefs DKIM pour ce sous-domaine.)

29
README.md Normal file
View File

@ -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 dillustrer 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:
* dune entreprise qui cherche à se protéger contre lusurpation didentité dans les courriels;
* dune «personne lambda» qui reçoit à la fois des mails de cette entreprise mais aussi des tentatives de hameçonnage;
* dun attaquant, muni doutils 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 lentreprise, émetteur de mails authentiques;
* **attacker**: le système mail de lattaquant;
* **recipient**: le système mail destinataire des mails de lentreprise et de lattaquant.
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

28
attacker/Dockerfile Normal file
View File

@ -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 quon est
# en train dêtre méchant
USER attaquant
ENTRYPOINT ["/bin/bash"]
COPY --chown=attaquant scripts /home/attaquant/scripts

268
attacker/scripts/send_email.py Executable file
View File

@ -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<helo>.*)',
'mail_from': r'MAIL FROM:<(?P<mail_from>.*?)>',
'rcpt_to': r'RCPT TO:<(?P<rcpt_to>.*)>',
'data': r'From: (?P<data_display_name>.* <(?P<data_from>.*)>)'
}
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 dhô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 quon 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 dusurpation didentité 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()

View File

@ -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

View File

@ -0,0 +1,14 @@
From: Destinataire <destinataire@destinataire.example>
Subject: Demande de rançon
Bonjour,
Je suis très méchant : jai piraté votre boîte mail et jai 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.

View File

@ -0,0 +1,34 @@
From: Newsletter <info@newsletter.expediteur.example>
Subject: NEWSLETTER: Nous sommes mauvais ! 💩
Content-Type: text/html; charset=utf-8
<html>
<body>
<p>
Cher client,
</p>
<p>
Dans ce nouveau numéro de notre newsletter, nous avons une annonce
importante à faire.
</p>
<p>
<strong>Nous sommes vraiment mauvais.</strong> La preuve : nimporte
qui peut usurper ladresse dexpédition des newsletters de cette boîte.
</p>
<p>
Donc surtout, <strong>nachetez pas chez nous</strong> ! 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.
</p>
<p>
Bref : allez plutôt chez nos concurrents. 😂
</p>
<p>
Cordialement,
</p>
<p>
Un facétieux cybercriminel.
</p>
</body>
</html>

View File

@ -0,0 +1,55 @@
From: Service client <support@expediteur.example>
Subject: Confirmation de commande 81729098
Content-Type: text/html; charset=utf-8
<html>
<head>
<style>
table, th, td {
border: 1px solid;
border-collapse: collapse;
padding: 0.5ex 1ex;
}
table { margin-bottom: 1em; }
.r { text-align: right; }
</style>
</head>
<body>
<p>Bonjour,</p>
<p>La commande suivante a bien été confirmée:</p>
<table>
<tr>
<th>Qté</th>
<th>Désignation</th>
<th>PU HT</th>
<th>PU TTC</th>
<th>Sous-total</th>
</tr>
<tr>
<td class="r">1500</td>
<td>Savonnette</td>
<td class="r">1,66 €</td>
<td class="r">2,00 €</td>
<td class="r">3000,00 €</td>
</tr>
<tr>
<td colspan="2"><strong>Total</strong></td>
<td class="r">2500,00 €</td>
<td class="r">3000,00 €</td>
<td class="r">3000,00 €</td>
</tr>
</table>
<p>
Ce nétait pas vous? Vous pouvez encore
<a href="https://expediteur-annulation.example/annulation-commande.php?c=81729098">annuler la commande</a>
en vous rendant à ladresse suivante:
</p>
<p style="margin-left: 2em">
<a href="https://expediteur-annulation.example/annulation-commande.php?c=81729098">
https://expediteur-annulation.example/annulation-commande.php?c=81729098
</a>
</p>
<p>Cordialement,</p>
<p>Un cybercriminel qui vous veut du bien.</p>
</body>
</html>

21
dns/Dockerfile Normal file
View File

@ -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"]

31
dns/etc/nsd/nsd.conf Normal file
View File

@ -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"

View File

@ -0,0 +1 @@
oneshot

View File

@ -0,0 +1 @@
/usr/sbin/nsd-control-setup

View File

@ -0,0 +1,2 @@
#!/bin/execlineb -P
/usr/sbin/nsd -d -c /etc/nsd/nsd.conf -P /run/nsd.pid

View File

@ -0,0 +1 @@
longrun

View File

@ -0,0 +1,2 @@
#!/bin/execlineb -P
/usr/sbin/unbound -d -c /etc/unbound/unbound.conf

View File

@ -0,0 +1 @@
longrun

View File

@ -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 jignore, mettre stub-addr: 127.0.0.1@1053 entraîne
# un SERVFAIL. On dirait quunbound narrive pas à communiquer avec nsd.
# Il faut que nsd écoute sur toutes les interfaces dans le conteneur et
# que, dans la configuration dunbound, le stub-addr soit lIP 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

View File

@ -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.

View File

@ -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.

33
dns/zones/example.zone Normal file
View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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"

62
docker-compose.yml Normal file
View File

@ -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:

93
recipient/Dockerfile Normal file
View File

@ -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 len-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 quil 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"]

View File

@ -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
<IfModule !mpm_prefork_module>
#LoadModule cgid_module modules/mod_cgid.so
</IfModule>
<IfModule mpm_prefork_module>
#LoadModule cgi_module modules/mod_cgi.so
</IfModule>
#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
<IfModule unixd_module>
User apache
Group apache
</IfModule>
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
# <Directory> blocks below.
#
<Directory />
AllowOverride none
Require all denied
</Directory>
#
# 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"
<Directory "/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
</Directory>
#
# DirectoryIndex: sets the file that Apache will serve if a directory
# is requested.
#
<IfModule dir_module>
DirectoryIndex index.html
</IfModule>
#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<Files ".ht*">
Require all denied
</Files>
#
# ErrorLog: The location of the error log file.
# If you do not specify an ErrorLog directive within a <VirtualHost>
# container, error messages relating to that virtual host will be
# logged here. If you *do* define an error logfile for a <VirtualHost>
# 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
<IfModule log_config_module>
#
# 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
<IfModule logio_module>
# 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
</IfModule>
#
# The location and format of the access logfile (Common Logfile Format).
# If you do not define any access logfiles within a <VirtualHost>
# container, they will be logged here. Contrariwise, if you *do*
# define per-<VirtualHost> 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
</IfModule>
<IfModule alias_module>
#
# 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 <Directory> 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/"
</IfModule>
<IfModule cgid_module>
#
# ScriptSock: On threaded servers, designate the path to the UNIX
# socket used to communicate with the CGI daemon of mod_cgid.
#
#Scriptsock cgisock
</IfModule>
#
# "/var/www/localhost/cgi-bin" should be changed to whatever your ScriptAliased
# CGI directory exists, if you have that configured.
#
<Directory "/var/www/localhost/cgi-bin">
AllowOverride None
Options None
Require all granted
</Directory>
<IfModule headers_module>
#
# 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
</IfModule>
<IfModule mime_module>
#
# 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
</IfModule>
#
# 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.
#
<IfModule mime_magic_module>
MIMEMagicFile /etc/apache2/magic
</IfModule>
#
# 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

View File

@ -0,0 +1,17 @@
Listen 8225
<VirtualHost *:8225>
ServerName destinataire.example
DocumentRoot /var/www/roundcubemail/public_html/
<Directory />
Options FollowSymLinks
AllowOverride All
</Directory>
<Directory /var/www/roundcubemail>
Options FollowSymLinks MultiViews
AllowOverride All
Require all granted
</Directory>
</VirtualHost>

View File

@ -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
}

View File

@ -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
}

View File

@ -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: %$

View File

@ -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. <doc/wiki/UserIds.txt>
#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. <doc/wiki/Chrooting.txt>
#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. <doc/wiki/Chrooting.txt>
#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=<size> 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 <mailbox>.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 =

View File

@ -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. <doc/wiki/LoginProcess.txt>
#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 =
}
}

View File

@ -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
#}

View File

@ -0,0 +1,85 @@
##
## SSL settings
##
# SSL/TLS support: yes, no, required. <doc/wiki/SSL.txt>
# 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 = </etc/ssl/dovecot/server.pem
ssl_key = </etc/ssl/dovecot/server.key
# If key file is password protected, give the password here. Alternatively
# give it when starting dovecot with -p parameter. Since this file is often
# world-readable, you may want to place this setting instead to a different
# root owned 0600 file by using ssl_key_password = <path.
#ssl_key_password =
# PEM encoded trusted certificate authority. Set this only if you intend to use
# ssl_verify_client_cert=yes. The file should contain the CA certificate(s)
# followed by the matching CRL(s). (e.g. ssl_ca = </etc/ssl/certs/ca.pem)
#ssl_ca =
# Require that CRL check succeeds for client certificates.
#ssl_require_crl = yes
# Directory and/or file for trusted SSL CA certificates. These are used only
# when Dovecot needs to act as an SSL client (e.g. imapc backend or
# submission service). The directory is usually /etc/ssl/certs in
# Debian-based systems and the file is /etc/pki/tls/cert.pem in
# RedHat-based systems. Note that ssl_client_ca_file isn't recommended with
# large CA bundles, because it leads to excessive memory usage.
#ssl_client_ca_dir =
#ssl_client_ca_file =
# Require valid cert when connecting to a remote server
#ssl_client_require_valid_cert = yes
# Request client to send a certificate. If you also want to require it, set
# auth_ssl_require_client_cert=yes in auth section.
#ssl_verify_client_cert = no
# Which field from certificate to use for username. commonName and
# x500UniqueIdentifier are the usual choices. You'll also need to set
# auth_ssl_username_from_cert=yes.
#ssl_cert_username_field = commonName
# SSL DH parameters
# Generate new params with `openssl dhparam -out /etc/dovecot/dh.pem 4096`
# Or migrate from old ssl-parameters.dat file with the command dovecot
# gives on startup when ssl_dh is unset.
#ssl_dh = </etc/dovecot/dh.pem
# Minimum SSL protocol version to use. Potentially recognized values are SSLv3,
# TLSv1, TLSv1.1, TLSv1.2 and TLSv1.3, depending on the OpenSSL version used.
#
# Dovecot also recognizes values ANY and LATEST. ANY matches with any protocol
# version, and LATEST matches with the latest version supported by library.
#ssl_min_protocol = TLSv1.2
# SSL ciphers to use, the default is:
#ssl_cipher_list = ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH
# To disable non-EC DH, use:
#ssl_cipher_list = ALL:!DH:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH
# Colon separated list of elliptic curves to use. Empty value (the default)
# means use the defaults from the SSL library. P-521:P-384:P-256 would be an
# example of a valid value.
#ssl_curve_list =
# Prefer the server's order of ciphers over client's.
ssl_prefer_server_ciphers = yes
# SSL crypto device to use, for valid values run "openssl engine"
#ssl_crypto_device =
# SSL extra options. Currently supported options are:
# compression - Enable compression.
# no_ticket - Disable SSL session tickets.
#ssl_options =

View File

@ -0,0 +1,48 @@
##
## LDA specific settings (also used by LMTP)
##
# Address to use when sending rejection mails.
# Default is postmaster@%d. %d expands to recipient domain.
#postmaster_address =
# Hostname to use in various parts of sent mails (e.g. in Message-Id) and
# in LMTP replies. Default is the system's real hostname@domain.
#hostname =
# If user is over quota, return with temporary failure instead of
# bouncing the mail.
#quota_full_tempfail = no
# Binary to use for sending mails.
#sendmail_path = /usr/sbin/sendmail
# If non-empty, send mails via this SMTP host[:port] instead of sendmail.
#submission_host =
# Subject: header to use for rejection mails. You can use the same variables
# as for rejection_reason below.
#rejection_subject = Rejected: %s
# Human readable error message for rejection mails. You can use variables:
# %n = CRLF, %r = reason, %s = original subject, %t = recipient
#rejection_reason = Your message to <%t> 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
}

View File

@ -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
}
}

View File

@ -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 (<v2.1). Outlook Express breaks more badly though, without this it
# may show user "Message no longer in server" errors. Note that OE6 still
# breaks even with this workaround if synchronization is set to
# "Headers Only".
# tb-extra-mailbox-sep:
# Thunderbird gets somehow confused with LAYOUT=fs (mbox and dbox) and
# adds extra '/' suffixes to mailbox names. This option causes Dovecot to
# ignore the extra '/' instead of treating it as invalid mailbox name.
# tb-lsub-flags:
# Show \Noselect flags for LSUB replies with LAYOUT=fs (e.g. mbox).
# This makes Thunderbird realize they aren't selectable and show them
# greyed out, instead of only later giving "not selectable" popup error.
#
# The list is space-separated.
#imap_client_workarounds =
# Host allowed in URLAUTH URLs sent by client. "*" allows all.
#imap_urlauth_host =
# Enable IMAP LITERAL- extension (replaces LITERAL+)
#imap_literal_minus = no
# What happens when FETCH fails due to some internal error:
# disconnect-immediately:
# The FETCH is aborted immediately and the IMAP client is disconnected.
# disconnect-after:
# The FETCH runs for all the requested mails returning as much data as
# possible. The client is finally disconnected without a tagged reply.
# no-after:
# Same as disconnect-after, but tagged NO reply is sent instead of
# disconnecting the client. If the client attempts to FETCH the same failed
# mail more than once, the client is disconnected. This is to avoid clients
# from going into infinite loops trying to FETCH a broken mail.
#imap_fetch_failure = disconnect-immediately
protocol imap {
# Space separated list of plugins to load (default is global mail_plugins).
#mail_plugins = $mail_plugins
# Maximum number of IMAP connections allowed for a user from each IP address.
# NOTE: The username is compared case-sensitively.
#mail_max_userip_connections = 10
}

View File

@ -0,0 +1,19 @@
##
## Mailbox access control lists.
##
# vfile backend reads ACLs from "dovecot-acl" file from mail directory.
# You can also optionally give a global ACL directory path where ACLs are
# applied to all users' mailboxes. The global ACL directory contains
# one file for each mailbox, eg. INBOX or sub.mailbox. cache_secs parameter
# specifies how many seconds to wait between stat()ing dovecot-acl file
# to see if it changed.
plugin {
#acl = vfile:/etc/dovecot/global-acls:cache_secs=300
}
# To let users LIST mailboxes shared by other users, Dovecot needs a
# shared mailbox dictionary. For example:
plugin {
#acl_shared_dict = file:/var/lib/dovecot/shared-mailboxes
}

View File

@ -0,0 +1,11 @@
##
## Plugin settings
##
# All wanted plugins must be listed in mail_plugins setting before any of the
# settings take effect. See <doc/wiki/Plugins.txt> for list of plugins and
# their configuration. Note that %variable expansion is done for all values.
plugin {
#setting_name = value
}

View File

@ -0,0 +1,83 @@
##
## Quota configuration.
##
# Note that you also have to enable quota plugin in mail_plugins setting.
# <doc/wiki/Quota.txt>
##
## 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
}

View File

@ -0,0 +1,21 @@
# Authentication for checkpassword users. Included from 10-auth.conf.
#
# <doc/wiki/AuthDatabase.CheckPassword.txt>
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
#}

View File

@ -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
}

View File

@ -0,0 +1,16 @@
# Authentication via dict backend. Included from 10-auth.conf.
#
# <doc/wiki/AuthDatabase.Dict.txt>
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
}

View File

@ -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.
# <doc/wiki/Authentication.MasterUsers.txt>
# 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
}

View File

@ -0,0 +1,20 @@
# Authentication for passwd-file users. Included from 10-auth.conf.
#
# passwd-like file with specified location.
# <doc/wiki/AuthDatabase.PasswdFile.txt>
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
}

View File

@ -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
#}

View File

@ -0,0 +1,74 @@
# Authentication for system users. Included from 10-auth.conf.
#
# <doc/wiki/PasswordDatabase.txt>
# <doc/wiki/UserDatabase.txt>
# 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. <doc/wiki/PasswordDatabase.PAM.txt>
passdb {
driver = pam
# [session=yes] [setcred=yes] [failure_show_msg=yes] [max_requests=<n>]
# [cache_key=<key>] [<service name>]
#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. <doc/wiki/AuthDatabase.Passwd.txt>
#passdb {
#driver = passwd
# [blocking=no]
#args =
#}
# Shadow passwords for system users (NSS, /etc/shadow or similar).
# Deprecated by PAM nowadays.
# <doc/wiki/PasswordDatabase.Shadow.txt>
#passdb {
#driver = shadow
# [blocking=no]
#args =
#}
# PAM-like authentication for OpenBSD.
# <doc/wiki/PasswordDatabase.BSDAuth.txt>
#passdb {
#driver = bsdauth
# [blocking=no] [cache_key=<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 {
# <doc/wiki/AuthDatabase.Passwd.txt>
driver = passwd
# [blocking=no]
#args =
# Override fields from passwd
#override_fields = home=/home/virtual/%u
}
# Static settings generated from template <doc/wiki/UserDatabase.Static.txt>
#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 =
#}

View File

@ -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:<key> refers to key names
quota_rule = *:storage=%{dict:quota}
# dict:<key>.<objkey> refers to the objkey inside (JSON) object
mail = maildir:%{dict:userdb.home}/Maildir
}

View File

@ -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/<azp:default>/<alg>/<kid:default>
# 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

View File

@ -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

View File

@ -0,0 +1,7 @@
protocols = imap
!include_try /usr/share/dovecot/protocols.d/*.conf
#listen = *, ::
!include conf.d/*.conf
!include_try local.conf

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,23 @@
compatibility_level = 3.7
myhostname = mx.destinataire.example
mydestination = destinataire.example
# Journaliser sur STDOUT parce quon 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

View File

@ -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

View File

@ -0,0 +1,37 @@
<?php
$config = [];
$config['db_dsnw'] = 'sqlite:////var/db/roundcubemail/roundcubemail.db';
$config['imap_host'] = 'localhost:143';
$config['smtp_host'] = 'localhost:25';
$config['mail_domain'] = 'destinataire.example';
// Désactiver la rédaction de mails
$config['disabled_actions'] = ['compose'];
// Fournir un URL où lutilisateur peut obtenir de laide pour cette
// installation de Roundcube (ne pas mettre dURL chez Roundcube !)
$config['support_url'] = '';
// Message affiché sur linvite de connexion
$config['product_name'] = 'Webmail de destinataire.example';
// Clef de chiffrement des mots de passe IMAP stockés dans la session.
// Cette clef doit faire exactement 24 caractères.
$config['des_key'] = 'JusteUneDemoMaisQuandMem';
// Afficher ladresse e-mail des expéditeurs dans lIHM
$config['message_show_email'] = true;
// Liste de plugins actifs
$config['plugins'] = [
'authres_status',
];
$config['skin'] = 'elastic';
// Pour éviter de se faire déconnecter de la webmail pendant la démo, on
// configure une durée de session de 24 heures.
$config['session_lifetime'] = 1440;

View File

@ -0,0 +1,3 @@
#!/bin/sh
/bin/rm -f /var/run/apache2/apache2.pid
/usr/sbin/httpd -D FOREGROUND

View File

@ -0,0 +1 @@
longrun

View File

@ -0,0 +1,2 @@
#!/bin/execlineb -P
/usr/sbin/dovecot -F

View File

@ -0,0 +1 @@
longrun

View File

@ -0,0 +1,2 @@
#!/bin/execlineb -P
/usr/sbin/opendkim -f

View File

@ -0,0 +1 @@
longrun

View File

@ -0,0 +1,2 @@
#!/bin/execlineb -P
/usr/sbin/opendmarc -f -c /etc/opendmarc/opendmarc.conf

View File

@ -0,0 +1 @@
longrun

View File

@ -0,0 +1,2 @@
#!/bin/execlineb -P
/usr/sbin/postfix start-fg -c /etc/postfix

View File

@ -0,0 +1 @@
longrun

View File

@ -0,0 +1,7 @@
# The real public suffix list does not contain “.example” as a valid public
# suffix, which confuses opendmarc.
#
# Besides, since we are only using domain names ending in “.example”, there
# is no actual need for the real Public Suffix List.
example

31
sender/Dockerfile Normal file
View File

@ -0,0 +1,31 @@
FROM alpine:latest
RUN adduser -D expediteur
RUN apk add \
bash \
execline \
nano \
nano-syntax \
opendkim \
opendkim-utils \
opendmarc \
postfix \
s6-overlay \
vim
RUN newaliases
RUN install -m 0700 -o opendkim -g opendkim -d /run/opendkim
RUN install -m 0700 -o opendkim -g opendkim -d /etc/opendkim/keys
COPY etc/s6-overlay /etc/s6-overlay
COPY etc/postfix /etc/postfix
COPY etc/opendkim /etc/opendkim
ENTRYPOINT ["/init"]
# Ne pas positionner USER, ou sinon les services ne démarreront pas de manière
# adéquate.
COPY --chown=expediteur scripts /home/expediteur/scripts

View File

@ -0,0 +1,11 @@
##
## FORMAT DE LA TABLE
##
## <identifiant> <domaine>:<sélecteur>:<fichier>
##
# 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

View File

@ -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

View File

@ -0,0 +1,11 @@
##
## FORMAT DE LA TABLE
##
## <domaine ou adresse mail> <identifiant>
##
## Ladresse mail peut être un wildcard (ex. *@expediteur.example).
##
# expediteur.example demo1._domainkey.expediteur.example
# newsletter.expediteur.example demo1._domainkey.newsletter.expediteur.example

View File

@ -0,0 +1,16 @@
compatibility_level = 3.7
myhostname = mx.expediteur.example
mydestination = expediteur.example
# Journaliser sur STDOUT parce quon 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

View File

@ -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

View File

@ -0,0 +1,2 @@
#!/bin/execlineb -P
/usr/sbin/opendkim -f

View File

@ -0,0 +1 @@
longrun

View File

@ -0,0 +1,2 @@
#!/bin/execlineb -P
/usr/sbin/postfix start-fg -c /etc/postfix

View File

@ -0,0 +1 @@
longrun

View File

@ -0,0 +1,54 @@
#!/bin/sh
sendmail -f support@expediteur.example -F "Service client" \
destinataire@destinataire.example <<EOF
Subject: Confirmation de commande 21903482
Content-Type: text/html; charset=utf-8
<html>
<head>
<style>
table, th, td {
border: 1px solid;
border-collapse: collapse;
padding: 0.5ex 1ex;
}
table { margin-bottom: 1em; }
.r { text-align: right; }
</style>
</head>
<body>
<p>Bonjour,</p>
<p>La commande suivante a bien été confirmée:</p>
<table>
<tr>
<th>Qté</th>
<th>Désignation</th>
<th>PU HT</th>
<th>PU TTC</th>
<th>Sous-total</th>
</tr>
<tr>
<td class="r">1</td>
<td>Savonnette</td>
<td class="r">1,66 €</td>
<td class="r">2,00 €</td>
<td class="r">2,00 €</td>
</tr>
<tr>
<td colspan="2"><strong>Total</strong></td>
<td class="r">1,66 €</td>
<td class="r">2,00 €</td>
<td class="r">2,00 €</td>
</tr>
</table>
<p>
Pour suivre votre commande, connectez-vous à votre
<a href="https://expediteur.example/suivi/21903482">espace client</a>.
</p>
<p>Cordialement,</p>
<p>Le service client.</p>
</body>
</html>
EOF

View File

@ -0,0 +1,24 @@
#!/bin/sh
sendmail -f info@newsletter.expediteur.example -F "Newsletter" \
destinataire@destinataire.example <<EOF
Subject: NEWSLETTER: Ne manquez pas nos soldes! 💰🛒
Content-Type: text/html; charset=utf-8
<html>
<body>
<p>Bonjour bonjour!</p>
<p>
Nous allons bientôt entamer la période des soldes. Profitez-en pour
faire de bonnes affaires chez nous!
</p>
<p>
Dans le prochain numéro de cette newsletter, nous vous proposerons
quelques offres <strong>irrésistibles</strong>. Ne les manquez pas!
</p>
<p>Cordialement,</p>
<p>Le marketing.</p>
</body>
</html>
EOF