Ce serveur virtuel est destiné à l'hébergement de 1 à N domaines avec leur site Web, leur email (SMTP et IMAP) et leur DNS.
/etc/apache2/sites-available/domain.conf
/etc/apache2/sites-enabled/domain.conf
a2ensite
a2dissite
/etc/apache2/mods-enabled/
a2enmod
a2dismod
Debian architecture la configuration Apache pour l'utilisation d'hôtes virtuels
où la configuration personnelle se trouve finalement dans des fichiers de
/etc/apache2/sites-available
dédiés chacun à un site.
Le module SSL
doit être activé après installation (avec a2enmod ssl
).
Pour PHP, j'étais parti avec libapache2-mod-php
mais en fait
PHP-FPM semble plus approprié. Je supprime donc libapache2-mod-php
et installe php8.2-fpm
à la place. Apt explique ce qu'il faut faire :
NOTICE: Not enabling PHP 8.2 FPM by default. NOTICE: To enable PHP 8.2 FPM in Apache2 do: NOTICE: a2enmod proxy_fcgi setenvif NOTICE: a2enconf php8.2-fpm NOTICE: You are seeing this message because you have apache2 package installed.
/etc/letsencrypt/options-ssl-apache.conf
/etc/letsencrypt/live/certName/privkey.pem
/etc/letsencrypt/live/certName/fullchain.pem
Les certificats Let's Encrypt sont gérés par certbot
(et le paquet python3-certbot-apache
).
Pour l'instant je me suis contenté de certificats pour les domaines
servis par Apache. Mais la
documentation de Certbot
liste de nombreux plugins dont certains utilisant le DNS, y compris
hébergé chez Gandi
ou Infomaniak.
Quelques commandes utiles.
certbot certonly --apache
certbot certificates
certbot renew --dry-run
Le renouvellement est en principe automatisé. De fait la création du certificat
a créé /etc/cron.d/certbot
, mais c'est surchargé par SystemD, comme
le montre systemctl show certbot.timer
et plus généralement
systemctl list-timers
.
Ce n'est pas facile à faire sur un petit serveur personnel, mais ça se fait, comme l'écrit Stéphane Bortzmeyer.
/etc/postfix/main.cf
/etc/postfix/master.cf
/etc/postfix/vmailbox
et vmailbox.db
généré par postmap /etc/postfix/vmailbox
journalctl -u postfix.service
pour le log principal de Postfixjournalctl -xeu postfix@-.service
pour voir les connexionsLa documentation Postfix de Debian
n'est pas à jour du passage à SystemD.
J'opte plutôt pour la documentation officielle, notamment pour les
boîtes aux lettres virtuelles
qui peuvent être créées pour des utilisateurs et des domaines arbitraires
avec les réglages virtual_mailbox_domains
,
virtual_mailbox_maps
, etc.
Les courriers peuvent être stockés façon Maildir.
Sur Debian 12, les logs ne sont plus dans
/var/log/mail.log
mais obtenus avec
journalctl -u postfix@-.service
.
Le serveur n'ayant pas d'adresse IPv6 routable, je configure
postconf -e "inet_protocols = ipv4"
pour éviter des tentatives
de connexion d'envoi inutiles, ou pire, le rejet de courriers
envoyés si jamais le serveur reçoit un jour une adresse IPv6 routable qui
ne serait pas (encore) dans ma configuration SPF.
Postfix offre le chiffrement avec
TLS,
configuré avec les réglages smtpd_tls_chain_files
,
smtpd_tls_security_level
, smtpd_tls_auth_only
quand il fait serveur, et smtpd_tls_security_level
quand il fait client.
Pour la soumission des emails, les standards sont changeants.
La documentation de Postfix
parle du port 465 au passé.
Le port 465 était anciennement appelé smtps
.
Il est désormais appelé submissions
.
Sur ce port, SMTP utilise implicitement TLS (sans STARTTLS
).
Le port 465 du temps où il était appelé smtps
n'était pas censé être utilisé pour la soumission d'emails,
mais en même temps il ne faisait pas sens car le port 25 était utilisé
dans tous les cas par l'infrastructure de transport MX.
Il y a donc eu une période de flottement avant que le
RFC 8364
désigne le port 465 comme port de soumissions implicitement en TLS
et le promeuve.
Pour éviter de relayer des emails et limiter le spam,
Postfix SMTP relay and access control
donne les réglages de base (smtpd_client_restrictions
,
smtpd_helo_restrictions
, smtpd_sender_restrictions
,
smtpd_recipient_restrictions
, smtpd_relay_restrictions
,
smtpd_data_restrictions
, smtpd_end_of_data_restrictions
).
Évidemment, des serveurs idiots comme spring-chicken-bc.twitter.com
se font jeter avec leur HELO
spring-chicken-bc.x.com
.
Postscreen aide à lutter contre le spam en avant-poste.
Postfix Postscreen Howto
donne les réglages utiles (postscreen_denylist_action
,
postscreen_greet_action
, postscreen_dnsbl_action
et postscreen_dnsbl_sites
).
/etc/bind/db.domain
/etc/postfix-policyd-spf-python/policyd-spf.conf
Pour l'envoi, il n'y a rien à faire dans Postfix. Il suffit selon le
RC 7208 d'un enregistrement
TXT
(on trouve des exemples avec SPF
, mais c'est abandonné)
dans le DNS pour indiquer que seul le MX du domaine mongenet.ch
est autorisé à envoyer des emails de ce même domaine :
mongenet.ch. 3600 IN TXT "v=spf1 mx -all"
Pour la réception, il faut utiliser
Postfix SMTP
Access Policy Delegation, et en particulier déléguer à
python-postfix-policyd-spf
qui a son paquet Debian.
La commande man 1 policyd-spf
donne la configuration
Postfix à faire.
/etc/bind/db.domain
/etc/opendkim.conf
/etc/dkimkeys/key.private
/etc/dkimkeys/key.txt
enregistrement TXT pour DNS
Mon ancienne installation contient les clés dans un répertoire /etc/opendkim/
créé à la main. Mais dans Debian (12) le répertoire /etc/dkimkeys
est prévu.
Debian fournit une documentation d'installation d'OpenDKIM,
car c'est un peu plus manuel que d'habitude.
À la fin, j'ai publié l'enregistrement DNS suivant :
201810._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; s=email; " "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxhNr+Crx3f76vgsTw3uPVKl02Eb/4q70YQTU4kt0e8w2uBLOULuZoxzh5VwB0T+AezWTH8uYK5dBApN2ogbTBQUQ5spwAGqNGElFYNNKZ1ITRLryM8tNdDK858FMkMOU21CqSixld0WP5iJovCtxvlC10dLZi1IN/j2XDJzxhbcNqPRYTawGnyWanOxmS7v9vXKEpv9THPY/HL" "MhxDr9d+DaJo0a6bpT8iR7RZUkJ1V/vCZ8g/5gV5rhD/AHS37P8mCdFWYlynjTjGr6zudkADYiCfkacc4DTLWXHYOhH3RliRKcANGX5nRmxsVRn3sxWX9XgNQBJQ13jlWpDGGNRQIDAQAB" ) ; ----- DKIM key 201810 for mongenet.ch
/etc/bind/db.domain
DMARC est ce qui rend SPF et DKIM utiles à l'envoi, en disant aux serveurs destinataires que faire des emails sans signatures valables. C'est documenté dans le RFC 7489 mais une documentation plus pratique comme celle de LinuxBabe aide. Et un site comme MxToolbox aide à vérifier la configuration faite. À la fin, j'ai publié l'enregistrement DNS suivant :
_dmarc IN TXT "v=DMARC1; p=reject; fo=1; rua=mailto:dmarc@mongenet.ch;"
/etc/dovecot/conf.d/10-auth.conf
pour utiliser des utilisateurs email au
lieu des utilisateurs système./etc/dovecot/users
, les utilisateurs email/etc/dovecot/conf.d/10-mail.conf
pour utiliser les boîtes aux lettres Maildir
/etc/dovecot/conf.d/10-master.conf
pour autoriser Postfix à utiliser
le service d'identification, selon
Postfix and Dovecot SASL¶/etc/dovecot/conf.d/10-auth.conf
pour utiliser /etc/dovecot/users
/etc/dovecot/private/dovecot.key
lien symbolique sur la clé privée du domaine/etc/dovecot/private/dovecot.pem
lien symbolique sur le certificat du domaineDovecot tient mordicus à utiliser l'UID 1002 pour accéder à Maildir
.
Suivant la documentation
Simple Virtual User Installation¶
de Dovecot je crée l'utilisateur et le groupe vmail
à la main avec ID 1002.
journalctl -u named.service
pour voir les logs/etc/resolv.conf
pour mettre nameserver 127.0.0.1
/etc/bind/named.conf.local
pour inclure mes zones/etc/bind/named.conf.options
pour les réglages globaux (pour DNSSEC)/etc/bind/primary/domain
dans un répertoire
créé à la main selon la documentation DNSSEC de Debian/var/cache/bind/keys/
pour les clés DNSSEC/etc/apparmor.d/local/usr.sbin.named
réglage à faire pour DNSSEC selon DebianJ'installe les paquets bind9
et dnsutils
.
Comme le serveur n'a pas d'adresse IPv6 routable,
je supprime le support IPv6 en utilisant l'option
de ligne de commande -4
de named
(systemctl edit named.service
).
Je pourrais aussi ajouter l'option filter-aaaa-on-v4 yes;
dans
la configuration de named
, mais je vais attendre de voir si c'est
utile.
Par défaut, à partir de Bind 9.4.1-P1, allow-recursion
a pour
liste d'accès par défaut : localnets; localhost;
. J'ai mis
du temps à trouver l'information, mais je l'ai eu dans
What has changed in the behavior
of "allow-recursion" and "allow-query-cache" du site officiel.
C'est cohérent avec la sortie de named -C
qui donne les options compilées.
J'ai donc bien un DNS récursif pour mon serveur, mais pas pour l'extérieur.
Sur le serveur précédent je n'avais pas DNSSEC. La commande
delv
permet de vérifier DNSSEC. La page
DNSSEC de Debian
donne une marche à suivre détaillée pour utiliser DNSSEC,
sauf pour ce qui est de trouver la longueur des clés utilisées dans le domnaine racine (.ch
).
D'après Switch
c'est de l'ECDSA, du SHA-256, avec KSK et ZSK de 256 bits (256 bits pour les clés privées, 512 pour les publiques).
Une fois les clés générées, une commande donne l'enregistrement DS
mongenet.ch. IN DS 52882 13 2 9DB8A6FC25D3A87E281582915C0D1BB04BCECF903F3ECDA2EEAE83839A861F52
.
Il m'a fallu un peu d'aide artificielle pour savoir que mettre dans les champs clé et hash
de mon registrar. Enfin, c'est à présent tout vert sur
https://dnssec-debugger.verisignlabs.com/mongenet.ch.
Le site DNSVIZ donne un bon aperçu de la chaîne de clés DNSSEC :
La documentation de Debian ne dit pas qu'il est possible d'indiquer à Bind9
que le DS est publié dans le DNS parent. Or j'ai vu avec la commande
rndc dnssec -status mongenet.ch
que ma clé CSK n'a pas un statut clair :
# rndc dnssec -status mongenet.ch dnssec-policy: default current time: Mon Oct 28 00:02:40 2024 key: 52882 (ECDSAP256SHA256), CSK published: yes - since Tue Oct 8 01:46:11 2024 key signing: yes - since Tue Oct 8 01:46:11 2024 zone signing: yes - since Tue Oct 8 01:46:11 2024 No rollover scheduled - goal: omnipresent - dnskey: omnipresent - ds: rumoured - zone rrsig: omnipresent - key rrsig: omnipresent ...
D'après la documentation
DNSSEC de Bind9
la commande rndc dnssec -checkds -key 52882 published mongenet.ch.
fait l'affaire.
La commande répond KSK 52882: Marked DS as published since 28-Oct-2024 01:45:34.000
mais il n'y a aucun changement dans la sortie de rndc dnssec -status mongenet.ch
...
Cela dit, ça viendra peut-être avec le temps, si j'en crois la réponse à
after
DS RECORD publish/verify, DSStatus stuck @ "rumoured" after manual `rndc dnssec -checkds` update ?.
Encore plus troublant est la mise à jour d'une zone protégée par DNSSEC.
J'ai changé l'enregistrement _dmarc
à p=reject
et le
numéro de série, mais après un rndc reload
, le nouveau numéro de série est servi
(dans un transfert de zone par exemple) mais pas la mise à jour du _dmarc
!
Je pensais avoir une piste avec la commande rndc zonestatus mongenet.ch
m'indique :
# rndc zonestatus mongenet.ch name: mongenet.ch type: primary files: /etc/bind/primary/mongenet.ch serial: 2024102800 signed serial: 2024102800 nodes: 7 last loaded: Sun, 27 Oct 2024 21:34:38 GMT secure: yes inline signing: yes key maintenance: automatic next key event: Mon, 28 Oct 2024 03:40:18 GMT next resign node: _dmarc.mongenet.ch/NSEC next resign time: Fri, 01 Nov 2024 15:59:37 GMT dynamic: no reconfigurable via modzone: no
Mais le next resign node: _dmarc.mongenet.ch/NSEC semble sans rapport
avec ma mise à jour de la valeur TXT
.
Finalement, la seule solution que j'ai trouvée est de stopper Bind,
effacer les fichiers mongenet.ch.jnl
, mongenet.ch.signed
mongenet.ch.signed.jnl
, puis lancer à nouveau Bind.
J'installe un serveur VLN. D'abord collecte de quelques informations avant de risquer une casse. J'utilise ip COMMAND CHEAT SHEET for Red Hat Enterprise Linux.
root@rwxweb:/home/debian# ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host noprefixroute valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether bc:24:11:99:44:18 brd ff:ff:ff:ff:ff:ff altname enp0s18 altname ens18 inet 62.220.136.5/24 brd 62.220.136.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::be24:11ff:fe99:4418/64 scope link valid_lft forever preferred_lft forever root@rwxweb:/home/debian# ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether bc:24:11:99:44:18 brd ff:ff:ff:ff:ff:ff altname enp0s18 altname ens18 root@rwxweb:/home/debian# ip route default via 62.220.136.254 dev eth0 onlink 62.220.136.0/24 dev eth0 proto kernel scope link src 62.220.136.5 root@rwxweb:/home/debian# ip maddr 1: lo inet 224.0.0.1 inet6 ff02::1 inet6 ff01::1 2: eth0 link 33:33:00:00:00:01 link 01:00:5e:00:00:01 link 33:33:ff:99:44:18 inet 224.0.0.1 inet6 ff02::1:ff99:4418 inet6 ff02::1 inet6 ff01::1
Je configure en me documentation à la fois avec Wireguard Debian wiki et Setup a WireGuard server using systemd-networkd car ça documente l'IP Masquerading en configuration systemD.
/etc/systemd/network/wg0.netdev
:
[NetDev] Name=wg0 Kind=wireguard Description=wg0 - wireguard tunnel [WireGuard] ListenPort=51111 PrivateKey=my-private-key [WireGuardPeer] AllowedIPs=10.0.0.20/32 PublicKey=my-client-public-key
/etc/systemd/network/wg0.network
:
[Match] Name=wg0 [Network] Address=10.0.0.1/24 IPMasquerade=both
Avec ça j'ai lancé systemctl daemon-reload
et
systemctl start systemd-networkd
et voilà.
# wg show wg0 interface: wg0 public key: my-server-public-key private key: (hidden) listening port: 51111 peer: my-client-public-key allowed ips: 10.0.0.20/32 # networkctl status wg0 ● 3: wg0 Link File: /usr/lib/systemd/network/99-default.link Network File: /etc/systemd/network/wg0.network State: routable (configured) Online state: online Type: wireguard Kind: wireguard Driver: wireguard MTU: 1420 (max: 2147483552) QDisc: noqueue IPv6 Address Generation Mode: none Number of Queues (Tx/Rx): 1/1 Address: 10.0.0.1 Activation Policy: up Required For Online: yes Mar 26 01:36:08 rwxweb.vserver.nimag.net systemd-networkd[3149095]: wg0: netdev ready Mar 26 01:36:08 rwxweb.vserver.nimag.net systemd-networkd[3149095]: wg0: netdev exists, using existing without changing its parameters Mar 26 01:36:08 rwxweb.vserver.nimag.net systemd-networkd[3149095]: wg0: Configuring with /etc/systemd/network/wg0.network. Mar 26 01:36:08 rwxweb.vserver.nimag.net systemd-networkd[3149095]: wg0: Link UP Mar 26 01:36:08 rwxweb.vserver.nimag.net systemd-networkd[3149095]: wg0: Gained carrier Mar 26 01:42:12 rwxweb.vserver.nimag.net systemd-networkd[3149264]: wg0: netdev ready Mar 26 01:42:12 rwxweb.vserver.nimag.net systemd-networkd[3149264]: wg0: Link UP Mar 26 01:42:12 rwxweb.vserver.nimag.net systemd-networkd[3149264]: wg0: Gained carrier Mar 26 01:42:12 rwxweb.vserver.nimag.net systemd-networkd[3149264]: wg0: netdev exists, using existing without changing its parameters Mar 26 01:42:12 rwxweb.vserver.nimag.net systemd-networkd[3149264]: wg0: Configuring with /etc/systemd/network/wg0.network. # ip link 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000 link/ether bc:24:11:99:44:18 brd ff:ff:ff:ff:ff:ff altname enp0s18 altname ens18 3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000 link/none # ip addr 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host noprefixroute valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether bc:24:11:99:44:18 brd ff:ff:ff:ff:ff:ff altname enp0s18 altname ens18 inet 62.220.136.5/24 brd 62.220.136.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::be24:11ff:fe99:4418/64 scope link valid_lft forever preferred_lft forever 3: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000 link/none inet 10.0.0.1/24 scope global wg0 valid_lft forever preferred_lft forever
Pour tester la connexion depuis le client, la documentation Wireguard Quick Start donne les commandes :
# ip link add wg0 type wireguard # ip addr add 10.0.0.20/24 dev wg0 # wg set wg0 private-key private.key # ip link set wg0 up # wg set wg0 peer my-server-public-key allowed-ips 10.0.0.1/32 endpoint marc.mongenet.ch:51111 # ping 10.0.0.1 PING 10.0.0.1 (10.0.0.1) 56(84) octets de données. 64 octets de 10.0.0.1 : icmp_seq=1 ttl=64 temps=50.5 ms 64 octets de 10.0.0.1 : icmp_seq=2 ttl=64 temps=30.4 ms ^C --- statistiques ping 10.0.0.1 --- 2 paquets transmis, 2 reçus, 0% packet loss, time 1002ms rtt min/avg/max/mdev = 30.420/40.475/50.531/10.055 ms # wg interface: wg0 public key: my-client-public-key private key: (hidden) listening port: 39352 peer: my-server-public-key endpoint: 62.220.136.5:51111 allowed ips: 10.0.0.1/32 latest handshake: 6 minutes, 18 seconds ago transfer: 732 B received, 820 B sent #
Finalement je me crée un script qui passe en root, crée un namespace pour y mettre Wireguard,
puis ouvre un shell dans ce namespace en repassant en utilisateur non root. Pour cela j'ai
appliquéla longue documentation
Using
WireGuard For Specific Apps on Linux de Pro Custodibus, mais en utilisant su
au lieu de sudo
: su -c "ip netns add pvt-net1; ip -n pvt-net1 link set lo up; ip link add wg0 type wireguard; ip link set wg0 netns pvt-net1; ip netns exec pvt-net1 wg setconf wg0 /home/marc/.wg/wg0.conf; ip -n pvt-net1 address add 10.0.0.20/32 dev wg0; ip -n pvt-net1 link set wg0 up; ip -n pvt-net1 route add default dev wg0; ip netns exec pvt-net1 su -c bash $me; ip netns del pvt-net1"
.
Pour fignoler le tout, wg genpsk
génére une clé partagée à ajouter
sous PresharedKey
dans la section [WireGuardPeer]
de
/etc/systemd/network/wg0.netdev
(section [Peer]
dans la
configuration Wireguard hors SystemD).
© 2025 Marc Mongenet
Ce document est disponible sous licence CC BY-SA 4.0.
Dernière mise à jour et
validation le 5 avril 2024.