Services réseau#
Nginx — serveur web et reverse proxy#
Nginx (prononcé « engine-x ») est un serveur HTTP haute performance, proxy inverse et répartiteur de charge. Son architecture événementielle non-bloquante lui permet de gérer des milliers de connexions simultanées avec une faible empreinte mémoire.
Installation et structure de configuration#
# Installation
apt install nginx # Debian/Ubuntu
dnf install nginx # RHEL/Fedora
# Structure des répertoires
/etc/nginx/
├── nginx.conf # configuration globale
├── conf.d/ # fragments inclus
├── sites-available/ # serveurs virtuels disponibles
└── sites-enabled/ # liens symboliques vers les actifs
Configuration de base — server blocks#
# /etc/nginx/sites-available/mon-site.conf
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
root /var/www/example.com/html;
index index.html index.htm;
# Compression gzip
gzip on;
gzip_types text/plain text/css application/json application/javascript;
# Headers de sécurité
add_header X-Frame-Options "SAMEORIGIN";
add_header X-Content-Type-Options "nosniff";
add_header X-XSS-Protection "1; mode=block";
location / {
try_files $uri $uri/ =404;
}
location /static/ {
alias /var/www/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Logs
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log warn;
}
# Activer le site
ln -s /etc/nginx/sites-available/mon-site.conf /etc/nginx/sites-enabled/
# Tester la syntaxe
nginx -t
# Recharger
systemctl reload nginx
Reverse proxy#
upstream backend_app {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name api.example.com;
location / {
proxy_pass http://backend_app;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
proxy_connect_timeout 10s;
proxy_read_timeout 60s;
proxy_buffering on;
proxy_buffer_size 8k;
}
}
Load balancing#
Nginx supporte plusieurs algorithmes de répartition de charge :
# Round-robin (défaut)
upstream backend { server 10.0.1.1; server 10.0.1.2; }
# Weighted round-robin
upstream backend {
server 10.0.1.1 weight=3;
server 10.0.1.2 weight=1;
}
# Least connections
upstream backend {
least_conn;
server 10.0.1.1;
server 10.0.1.2;
}
# IP hash (persistance de session)
upstream backend {
ip_hash;
server 10.0.1.1;
server 10.0.1.2;
}
# Avec health checks (Nginx Plus ou ngx_upstream_module)
upstream backend {
server 10.0.1.1 max_fails=3 fail_timeout=30s;
server 10.0.1.2 max_fails=3 fail_timeout=30s;
server 10.0.1.3 backup; # serveur de secours
}
TLS avec Nginx#
Certificat auto-signé (développement/test)#
# Générer une clé et un certificat auto-signé (10 ans)
openssl req -x509 -nodes -newkey rsa:4096 \
-keyout /etc/nginx/ssl/example.key \
-out /etc/nginx/ssl/example.crt \
-days 3650 \
-subj "/C=FR/ST=IDF/L=Paris/O=MaSociete/CN=example.com"
# Générer un certificat avec SAN (Subject Alternative Names)
openssl req -x509 -nodes -newkey rsa:4096 \
-keyout /etc/nginx/ssl/example.key \
-out /etc/nginx/ssl/example.crt \
-days 365 \
-addext "subjectAltName=DNS:example.com,DNS:www.example.com,IP:203.0.113.5"
Let’s Encrypt avec Certbot#
# Installation
apt install certbot python3-certbot-nginx
# Obtenir un certificat et configurer Nginx automatiquement
certbot --nginx -d example.com -d www.example.com
# Certificat seul (sans modification Nginx)
certbot certonly --nginx -d example.com
# Renouvellement automatique (déjà configuré via systemd timer)
systemctl status certbot.timer
certbot renew --dry-run
Configuration TLS moderne (A+ sur SSL Labs)#
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Protocoles et ciphers modernes
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# HSTS (2 ans)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
# Session cache
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
}
# Redirection HTTP → HTTPS
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
DNS avec Bind9#
Bind9 (Berkeley Internet Name Domain) est le serveur DNS le plus répandu. En production, il peut être serveur faisant autorité (répond pour ses zones) ou résolveur récursif (interroge d’autres serveurs).
Configuration principale#
# /etc/bind/named.conf.options
options {
directory "/var/cache/bind";
listen-on { 127.0.0.1; 192.168.1.1; };
listen-on-v6 { ::1; };
# Autoriser les requêtes depuis le LAN
allow-query { localhost; 192.168.0.0/16; };
# Forwarders (upstream DNS)
forwarders { 1.1.1.1; 8.8.8.8; };
forward first;
# DNSSEC validation
dnssec-validation auto;
# Masquer la version Bind
version "not available";
};
# /etc/bind/named.conf.local
zone "example.com" {
type master;
file "/etc/bind/zones/db.example.com";
allow-transfer { 192.168.1.2; }; # serveur DNS secondaire
};
zone "1.168.192.in-addr.arpa" {
type master;
file "/etc/bind/zones/db.192.168.1";
};
Fichier de zone#
; /etc/bind/zones/db.example.com
$ORIGIN example.com.
$TTL 3600
@ IN SOA ns1.example.com. hostmaster.example.com. (
2024030101 ; Serial (YYYYMMDDNN)
3600 ; Refresh
900 ; Retry
604800 ; Expire
300 ) ; Negative TTL
; Serveurs de noms
@ IN NS ns1.example.com.
@ IN NS ns2.example.com.
; Enregistrements A et AAAA
ns1 IN A 203.0.113.1
ns2 IN A 203.0.113.2
@ IN A 203.0.113.5
www IN CNAME @
mail IN A 203.0.113.10
api IN A 203.0.113.15
; IPv6
@ IN AAAA 2001:db8::5
; Messagerie
@ IN MX 10 mail.example.com.
@ IN MX 20 mail2.example.com.
; SPF, DKIM, DMARC
@ IN TXT "v=spf1 mx a -all"
@ IN TXT "v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com"
# Vérifier la syntaxe
named-checkzone example.com /etc/bind/zones/db.example.com
named-checkconf
# Recharger les zones sans redémarrer
rndc reload
rndc reload example.com
DNS avec Unbound#
Unbound est un résolveur DNS récursif validant DNSSEC, conçu pour être déployé en local ou comme cache DNS sur un réseau. Plus léger que Bind9 pour ce rôle.
# /etc/unbound/unbound.conf
server:
interface: 127.0.0.1
interface: 192.168.1.1
port: 53
do-ip4: yes
do-ip6: yes
do-udp: yes
do-tcp: yes
access-control: 127.0.0.0/8 allow
access-control: 192.168.0.0/16 allow
access-control: 0.0.0.0/0 refuse
# Cache
cache-min-ttl: 60
cache-max-ttl: 86400
msg-cache-size: 128m
rrset-cache-size: 256m
# DNSSEC
auto-trust-anchor-file: "/var/lib/unbound/root.key"
# DNS-over-TLS vers les forwarders
tls-cert-bundle: /etc/ssl/certs/ca-certificates.crt
# Confidentialité
hide-identity: yes
hide-version: yes
qname-minimisation: yes
forward-zone:
name: "."
forward-tls-upstream: yes
forward-addr: 1.1.1.1@853#cloudflare-dns.com
forward-addr: 8.8.8.8@853#dns.google
# Vérifier la configuration
unbound-checkconf
# Contrôle du service
unbound-control status
unbound-control stats
unbound-control flush example.com # vider le cache pour un domaine
unbound-control flush_zone example.com
NFS — partage de fichiers réseau#
NFS (Network File System) permet de monter un système de fichiers distant comme s’il était local. Il est natif sur Linux et ne nécessite aucun client spécial.
Configuration du serveur NFS#
# Installation
apt install nfs-kernel-server
# /etc/exports
/srv/nfs/data 192.168.1.0/24(rw,sync,no_subtree_check)
/srv/nfs/backups 192.168.1.5(ro,sync,no_subtree_check,no_root_squash)
/home 192.168.1.0/24(rw,sync,root_squash,no_subtree_check)
/srv/public *(ro,sync,insecure)
Options importantes des exports :
Option |
Signification |
|---|---|
|
Lecture-écriture / lecture seule |
|
Écriture confirmée sur disque avant réponse (sûr) |
|
Écriture asynchrone (plus rapide, risqué) |
|
Mappe root client → nobody (sécurité par défaut) |
|
root client = root serveur (dangereux) |
|
Mappe tous les utilisateurs → nobody |
|
UID/GID du compte anonyme |
|
Désactive la vérification de sous-arbre (recommandé) |
|
Autorise les ports > 1024 (clients non-root) |
# Appliquer les exports sans redémarrage
exportfs -ra
exportfs -v # vérifier ce qui est exporté
# Redémarrer le service
systemctl enable --now nfs-kernel-server
Client NFS#
# Montage manuel
mount -t nfs 192.168.1.1:/srv/nfs/data /mnt/data
# Avec options (recommandé)
mount -t nfs -o rw,hard,intr,rsize=65536,wsize=65536,timeo=14 \
192.168.1.1:/srv/nfs/data /mnt/data
# /etc/fstab pour montage automatique
192.168.1.1:/srv/nfs/data /mnt/data nfs rw,hard,intr,rsize=65536,wsize=65536 0 0
# Montage automatique via systemd (lazy mount)
# → utiliser automount units systemd
NFSv4 et Kerberos
NFSv4 est la version recommandée pour les nouvelles installations. Elle utilise un seul port TCP (2049), supporte l’authentification Kerberos (sec=krb5i pour l’intégrité, sec=krb5p pour le chiffrement) et offre un ACL plus riche. idmapd est nécessaire pour la correspondance UID/nom entre client et serveur.
Samba — partage SMB/CIFS#
Samba implémente le protocole SMB/CIFS et permet aux systèmes Linux d’exposer des partages accessibles par Windows, macOS et d’autres Linux.
Configuration de base#
# Installation
apt install samba
# /etc/samba/smb.conf
[global]
workgroup = WORKGROUP
server string = Serveur Samba %v
netbios name = FILESERVER
security = user
map to guest = bad user
dns proxy = no
# Protocoles modernes uniquement
server min protocol = SMB2
client min protocol = SMB2
# Logs
log file = /var/log/samba/log.%m
max log size = 1000
[partage-public]
comment = Partage public en lecture seule
path = /srv/samba/public
browseable = yes
read only = yes
guest ok = yes
[partage-data]
comment = Données équipe
path = /srv/samba/data
browseable = yes
read only = no
valid users = @sambagroup
create mask = 0664
directory mask = 0775
force group = sambagroup
[homes]
comment = Répertoires personnels
browseable = no
read only = no
create mask = 0600
directory mask = 0700
# Vérifier la configuration
testparm
# Gérer les utilisateurs Samba (base de données séparée de /etc/passwd)
smbpasswd -a alice # ajouter et définir le mot de passe
smbpasswd -e alice # activer un compte
smbpasswd -d alice # désactiver
pdbedit -L # lister les utilisateurs Samba
# Statut du service
systemctl enable --now smbd nmbd
smbstatus
Accès depuis un client Linux#
# Lister les partages
smbclient -L //192.168.1.1 -U alice
# Accès interactif
smbclient //192.168.1.1/partage-data -U alice
# Montage CIFS
apt install cifs-utils
mount -t cifs //192.168.1.1/partage-data /mnt/data \
-o username=alice,password=secret,uid=1000,gid=1000,iocharset=utf8
# Via /etc/fstab avec fichier de credentials
//192.168.1.1/partage-data /mnt/data cifs credentials=/etc/samba/.creds,uid=1000 0 0
Supervision des services réseau#
systemctl status et journalctl#
# État d'un service avec les dernières lignes de log
systemctl status nginx
systemctl status bind9
systemctl status nfs-kernel-server
# Logs en temps réel
journalctl -u nginx -f
# Logs depuis le dernier démarrage
journalctl -u nginx -b
# Logs des dernières 24 heures
journalctl -u nginx --since "24 hours ago"
# Logs d'erreur uniquement
journalctl -u nginx -p err
# Filtrer par heure
journalctl -u nginx --since "2024-03-01 08:00" --until "2024-03-01 09:00"
Healthchecks#
# Vérifier qu'un service répond (HTTP)
curl -fsS --max-time 5 http://localhost/health || echo "SERVICE DOWN"
# Vérifier un port TCP
nc -zv -w 3 localhost 443 && echo "OK" || echo "DOWN"
# Script de healthcheck simple
cat > /usr/local/bin/check-nginx.sh << 'EOF'
#!/bin/bash
STATUS=$(curl -fsS -o /dev/null -w "%{http_code}" http://localhost/)
if [ "$STATUS" -ne 200 ]; then
echo "CRITICAL: nginx répond $STATUS"
systemctl restart nginx
exit 2
fi
echo "OK: nginx répond 200"
exit 0
EOF
chmod +x /usr/local/bin/check-nginx.sh
Monitoring externe#
# Uptime Kuma, Nagios, Zabbix ou simple cron
# Exemple avec cron + curl
*/5 * * * * /usr/local/bin/check-nginx.sh >> /var/log/healthcheck.log 2>&1
# Prometheus Node Exporter (métriques système)
apt install prometheus-node-exporter
systemctl enable --now prometheus-node-exporter
# Métriques disponibles sur http://localhost:9100/metrics
Démonstrations Python#
Parse d’une configuration Nginx — extraction des server blocks#
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
# Configuration Nginx simulée
nginx_config = """
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
root /var/www/example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
access_log /var/log/nginx/example.com.ssl.access.log;
error_log /var/log/nginx/example.com.ssl.error.log;
}
server {
listen 443 ssl http2;
server_name api.example.com;
root /var/www/api;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
access_log /var/log/nginx/api.access.log;
}
server {
listen 80;
server_name staging.example.com;
root /var/www/staging;
access_log /var/log/nginx/staging.access.log;
}
"""
def parser_nginx(config: str) -> list[dict]:
"""Extrait les server blocks d'une configuration Nginx."""
blocs = []
# Trouver tous les blocs server { ... }
pattern = re.compile(r'server\s*\{([^}]+)\}', re.DOTALL)
for m in pattern.finditer(config):
contenu = m.group(1)
def get(key):
r = re.search(rf'^\s*{key}\s+([^;]+);', contenu, re.MULTILINE)
return r.group(1).strip() if r else ""
def get_all(key):
return [r.group(1).strip()
for r in re.finditer(rf'^\s*{key}\s+([^;]+);', contenu, re.MULTILINE)]
listen = get("listen")
ssl = "ssl" in listen or "ssl_certificate" in contenu
blocs.append({
"listen": listen,
"server_name": get("server_name"),
"root": get("root") or "—",
"ssl": ssl,
"access_log": get("access_log").split("/")[-1] if get("access_log") else "—",
"protocole": "HTTPS" if ssl else "HTTP",
})
return blocs
blocs = parser_nginx(nginx_config)
df_nginx = pd.DataFrame(blocs)
print(f"Server blocks trouvés : {len(blocs)}\n")
print(df_nginx[["listen", "server_name", "protocole", "ssl"]].to_string(index=False))
# Visualisation
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# Tableau des server blocks
ax = axes[0]
ax.axis("off")
tbl = ax.table(
cellText=df_nginx[["listen", "server_name", "protocole", "root"]].values,
colLabels=["Listen", "Server Name", "Protocole", "Root"],
loc="center", cellLoc="center"
)
tbl.auto_set_font_size(False)
tbl.set_fontsize(8.5)
tbl.scale(1.1, 1.8)
colors_proto = {"HTTPS": "#59a14f", "HTTP": "#f28e2b"}
for i, bloc in enumerate(blocs):
c = colors_proto[bloc["protocole"]] + "44"
for j in range(4):
tbl[(i+1, j)].set_facecolor(c)
for j in range(4):
tbl[(0, j)].set_facecolor("#333333")
tbl[(0, j)].set_text_props(color="white", fontweight="bold")
ax.set_title("Server blocks extraits", fontweight="bold", pad=12)
# Répartition HTTP vs HTTPS
ax2 = axes[1]
proto_counts = df_nginx["protocole"].value_counts()
colors_pie = [colors_proto[p] for p in proto_counts.index]
wedges, texts, autotexts = ax2.pie(
proto_counts.values, labels=proto_counts.index,
autopct="%1.0f%%", colors=colors_pie,
startangle=90, pctdistance=0.7
)
for at in autotexts:
at.set_fontweight("bold")
at.set_fontsize(12)
ax2.set_title("Répartition HTTP / HTTPS", fontweight="bold")
plt.suptitle("Analyse de configuration Nginx", fontsize=13, fontweight="bold")
plt.show()
Server blocks trouvés : 4
listen server_name protocole ssl
80 example.com www.example.com HTTP False
443 ssl http2 example.com www.example.com HTTPS True
443 ssl http2 api.example.com HTTPS True
80 staging.example.com HTTP False
Schéma reverse proxy et load balancing#
sns.set_theme(style="white", palette="muted", font_scale=1.0)
fig, ax = plt.subplots(figsize=(14, 8))
ax.set_xlim(0, 14)
ax.set_ylim(0, 8)
ax.axis("off")
def boite(ax, x, y, w, h, titre, sous="", color="#4e79a7", fs=9):
r = plt.Rectangle((x-w/2, y-h/2), w, h,
facecolor=color+"33", edgecolor=color, linewidth=2, zorder=3)
ax.add_patch(r)
ax.text(x, y+(0.15 if sous else 0), titre, ha="center", va="center",
fontsize=fs, fontweight="bold", zorder=4)
if sous:
ax.text(x, y-0.22, sous, ha="center", va="center",
fontsize=7.5, color="#444444", zorder=4)
def fleche(ax, x1, y1, x2, y2, label="", color="#555555", style="->"):
ax.annotate("", xy=(x2, y2), xytext=(x1, y1),
arrowprops=dict(arrowstyle=style, color=color, lw=1.8), zorder=2)
if label:
ax.text((x1+x2)/2+0.05, (y1+y2)/2+0.2, label, ha="center",
fontsize=7.5, color=color)
# Internet
boite(ax, 7.0, 7.2, 2.0, 0.75, "Internet / Client", "", "#888888", fs=10)
# CDN/WAF
boite(ax, 7.0, 6.0, 2.4, 0.75, "CDN / WAF", "Cloudflare, Fastly…", "#76b7b2")
fleche(ax, 7.0, 6.82, 7.0, 6.38, "HTTPS :443", "#76b7b2")
# Nginx LB
boite(ax, 7.0, 4.7, 2.8, 0.9, "Nginx", "Reverse proxy + LB\n:80 :443", "#f28e2b")
fleche(ax, 7.0, 5.62, 7.0, 5.15, "HTTPS :443", "#f28e2b")
# Upstream 1
boite(ax, 3.0, 3.2, 2.4, 0.85, "Backend App #1", "127.0.0.1:8000\nGunicorn/uWSGI", "#59a14f", fs=8.5)
fleche(ax, 5.6, 4.5, 4.2, 3.5, "HTTP :8000\n(round-robin)", "#59a14f")
# Upstream 2
boite(ax, 7.0, 3.2, 2.4, 0.85, "Backend App #2", "127.0.0.1:8001\nGunicorn/uWSGI", "#59a14f", fs=8.5)
fleche(ax, 7.0, 4.25, 7.0, 3.63, "HTTP :8001", "#59a14f")
# Upstream 3
boite(ax, 11.0, 3.2, 2.4, 0.85, "Backend App #3", "127.0.0.1:8002\nGunicorn/uWSGI", "#59a14f", fs=8.5)
fleche(ax, 8.4, 4.5, 9.8, 3.5, "HTTP :8002", "#59a14f")
# Base de données
boite(ax, 5.0, 1.6, 2.4, 0.8, "Base de données", "PostgreSQL :5432", "#4e79a7", fs=8.5)
boite(ax, 9.0, 1.6, 2.4, 0.8, "Cache Redis", "Redis :6379", "#e15759", fs=8.5)
for xapp, xdb in [(3.0, 5.0), (7.0, 6.0), (11.0, 9.5)]:
fleche(ax, xapp, 2.77, xdb, 2.0, color="#aaaaaa")
# Annotations algorithmes LB
ax.text(7.0, 4.05, "Algorithmes : round-robin | least_conn | ip_hash | weighted",
ha="center", fontsize=7.5, color="#f28e2b",
bbox=dict(boxstyle="round,pad=0.3", facecolor="#fff4e6",
edgecolor="#f28e2b", linewidth=0.8))
# Légende SSL
ax.text(0.5, 1.0, "TLS/SSL\n(chiffré)", fontsize=8, color="#4e79a7", ha="center",
bbox=dict(boxstyle="round", facecolor="#e8f4f8", edgecolor="#4e79a7"))
ax.text(0.5, 0.3, "HTTP interne\n(non chiffré)", fontsize=8, color="#59a14f", ha="center",
bbox=dict(boxstyle="round", facecolor="#e8f8e8", edgecolor="#59a14f"))
ax.set_title("Architecture Nginx — Reverse Proxy et Load Balancing",
fontsize=13, fontweight="bold", pad=14)
plt.show()
Enregistrements DNS — visualisation#
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.0)
# Types d'enregistrements DNS avec exemples et descriptions
enregistrements = [
{"Type": "A", "Famille": "Adressage", "Exemple": "example.com → 203.0.113.5", "Description": "IPv4 address", "RFC": "RFC 1035", "Score": 100},
{"Type": "AAAA", "Famille": "Adressage", "Exemple": "example.com → 2001:db8::1", "Description": "IPv6 address", "RFC": "RFC 3596", "Score": 90},
{"Type": "CNAME", "Famille": "Alias", "Exemple": "www → example.com.", "Description": "Canonical name alias", "RFC": "RFC 1035", "Score": 85},
{"Type": "MX", "Famille": "Messagerie","Exemple": "@ 10 mail.example.com.", "Description": "Mail exchanger", "RFC": "RFC 1035", "Score": 80},
{"Type": "NS", "Famille": "Délégation","Exemple": "@ ns1.example.com.", "Description": "Name server", "RFC": "RFC 1035", "Score": 75},
{"Type": "TXT", "Famille": "Texte", "Exemple": "@ \"v=spf1 mx -all\"", "Description": "Text / SPF / DKIM", "RFC": "RFC 1035", "Score": 70},
{"Type": "PTR", "Famille": "Reverse", "Exemple": "5.113.0.203.in-addr.arpa → …", "Description": "Reverse DNS lookup", "RFC": "RFC 1035", "Score": 60},
{"Type": "SOA", "Famille": "Zone", "Exemple": "@ ns1 admin 2024030101 …", "Description": "Start of Authority", "RFC": "RFC 1035", "Score": 95},
{"Type": "SRV", "Famille": "Service", "Exemple": "_http._tcp 10 0 80 www.", "Description": "Service locator", "RFC": "RFC 2782", "Score": 55},
{"Type": "CAA", "Famille": "Sécurité", "Exemple": "0 issue \"letsencrypt.org\"", "Description": "CA Authorization", "RFC": "RFC 8659", "Score": 50},
]
df_dns = pd.DataFrame(enregistrements)
famille_colors = {
"Adressage": "#4e79a7",
"Alias": "#f28e2b",
"Messagerie": "#59a14f",
"Délégation": "#e15759",
"Texte": "#76b7b2",
"Reverse": "#edc948",
"Zone": "#b07aa1",
"Service": "#ff9da7",
"Sécurité": "#9c755f",
}
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# Tableau des enregistrements
ax = axes[0]
ax.axis("off")
tbl = ax.table(
cellText=df_dns[["Type", "Famille", "Exemple", "RFC"]].values,
colLabels=["Type", "Famille", "Exemple", "RFC"],
loc="center", cellLoc="left"
)
tbl.auto_set_font_size(False)
tbl.set_fontsize(8)
tbl.scale(1.1, 1.65)
for i, row in enumerate(enregistrements):
c = famille_colors[row["Famille"]] + "44"
for j in range(4):
tbl[(i+1, j)].set_facecolor(c)
tbl[(i+1, 0)].set_text_props(fontweight="bold")
for j in range(4):
tbl[(0, j)].set_facecolor("#333333")
tbl[(0, j)].set_text_props(color="white", fontweight="bold")
ax.set_title("Types d'enregistrements DNS", fontweight="bold", pad=12)
# Fréquence d'utilisation
ax2 = axes[1]
colors_bar = [famille_colors[f] for f in df_dns["Famille"]]
bars = ax2.barh(df_dns["Type"][::-1], df_dns["Score"][::-1],
color=colors_bar[::-1], edgecolor="white")
ax2.set_xlabel("Fréquence d'utilisation relative")
ax2.set_title("Utilisation des types DNS", fontweight="bold")
ax2.set_xlim(0, 115)
for bar, val in zip(bars, df_dns["Score"][::-1]):
ax2.text(val + 1, bar.get_y() + bar.get_height()/2,
f"{val}%", va="center", fontsize=8.5)
# Légende familles
patches = [mpatches.Patch(color=v, label=k) for k, v in famille_colors.items()]
axes[1].legend(handles=patches, loc="lower right", fontsize=7.5, ncol=2)
plt.suptitle("Enregistrements DNS — types et utilisation", fontsize=13, fontweight="bold")
plt.show()
Résolution DNS avec la stdlib Python#
sns.set_theme(style="whitegrid", palette="muted", font_scale=1.1)
domaines = [
"python.org",
"github.com",
"wikipedia.org",
"cloudflare.com",
"debian.org",
]
print("=== Résolution DNS via socket.getaddrinfo() ===\n")
resultats = []
for domaine in domaines:
try:
infos = socket.getaddrinfo(domaine, None)
ipv4 = [i[4][0] for i in infos if i[0] == socket.AF_INET]
ipv6 = [i[4][0] for i in infos if i[0] == socket.AF_INET6]
ipv4_uniques = list(dict.fromkeys(ipv4))
ipv6_uniques = list(dict.fromkeys(ipv6))
resultats.append({
"Domaine": domaine,
"IPv4": ipv4_uniques[0] if ipv4_uniques else "—",
"Nb IPv4": len(ipv4_uniques),
"IPv6": ipv6_uniques[0][:20] + "…" if ipv6_uniques else "—",
"Nb IPv6": len(ipv6_uniques),
})
print(f"{domaine}")
print(f" IPv4 : {', '.join(ipv4_uniques) if ipv4_uniques else 'non résolu'}")
if ipv6_uniques:
print(f" IPv6 : {ipv6_uniques[0]}")
print()
except socket.gaierror as e:
print(f"{domaine} : erreur de résolution ({e})\n")
resultats.append({"Domaine": domaine, "IPv4": "erreur",
"Nb IPv4": 0, "IPv6": "—", "Nb IPv6": 0})
df_res = pd.DataFrame(resultats)
# Résolution inverse
print("=== Résolution inverse via socket.gethostbyaddr() ===\n")
ips_test = []
for r in resultats:
if r["IPv4"] not in ("—", "erreur"):
ips_test.append((r["Domaine"], r["IPv4"]))
for domaine_orig, ip in ips_test[:4]:
try:
hostname, _, _ = socket.gethostbyaddr(ip)
print(f" {ip:16s} → {hostname}")
except socket.herror:
print(f" {ip:16s} → (pas de PTR)")
# Visualisation
fig, ax = plt.subplots(figsize=(12, 4.5))
ax.axis("off")
tbl = ax.table(
cellText=df_res.values,
colLabels=df_res.columns,
loc="center", cellLoc="center"
)
tbl.auto_set_font_size(False)
tbl.set_fontsize(9.5)
tbl.scale(1.2, 2.0)
for i in range(len(df_res)):
for j in range(len(df_res.columns)):
tbl[(i+1, j)].set_facecolor("#e8f4f8" if i % 2 == 0 else "#ffffff")
for j in range(len(df_res.columns)):
tbl[(0, j)].set_facecolor("#333333")
tbl[(0, j)].set_text_props(color="white", fontweight="bold")
ax.set_title("Résolution DNS — résultats socket.getaddrinfo()",
fontweight="bold", pad=14, fontsize=12)
plt.show()
=== Résolution DNS via socket.getaddrinfo() ===
python.org
IPv4 : 151.101.128.223, 151.101.192.223, 151.101.0.223, 151.101.64.223
IPv6 : 2a04:4e42:200::223
github.com
IPv4 : 140.82.121.3
wikipedia.org
IPv4 : 185.15.58.224
IPv6 : 2a02:ec80:600:ed1a::1
cloudflare.com
IPv4 : 104.16.132.229, 104.16.133.229
IPv6 : 2606:4700::6810:84e5
debian.org
IPv4 : 151.101.2.132, 151.101.194.132, 151.101.66.132, 151.101.130.132
IPv6 : 2a04:4e42:600::644
=== Résolution inverse via socket.gethostbyaddr() ===
151.101.128.223 → (pas de PTR)
140.82.121.3 → lb-140-82-121-3-fra.github.com
185.15.58.224 → text-lb.drmrs.wikimedia.org
104.16.132.229 → (pas de PTR)
Résumé#
Les services réseau constituent le cœur fonctionnel d’un serveur Linux. Leur configuration rigoureuse et leur supervision continue sont indispensables en production.
Points essentiels à retenir :
Nginx est la solution polyvalente de référence pour HTTP, HTTPS, reverse proxy et load balancing. La directive
proxy_passet les blocsupstreamsuffisent à couvrir la majorité des architectures.Le TLS moderne exige TLS 1.2 minimum, des ciphers AEAD (AES-GCM, ChaCha20-Poly1305) et le HSTS. Let’s Encrypt / Certbot automatise entièrement la gestion des certificats avec renouvellement systemd.
Bind9 est le serveur DNS faisant autorité de référence ; sa configuration en zone primaire/secondaire avec transfert AXFR reste le standard. Unbound est préférable comme résolveur récursif local avec validation DNSSEC et DNS-over-TLS.
NFS est simple à déployer pour les partages de fichiers intra-réseau Linux, mais ses permissions
root_squash/no_root_squashsont critiques pour la sécurité. NFSv4 avec Kerberos est recommandé en environnement hétérogène.Samba permet l’interopérabilité avec les clients Windows.
testparmest l’outil de diagnostic indispensable ; maintenirserver min protocol = SMB2élimine les protocoles SMB1 vulnérables (EternalBlue).La supervision combine
systemctl status+journalctlpour le diagnostic immédiat, et des healthchecks périodiques (cron, systemd timer) pour la détection proactive. Une pile complète (Prometheus + Grafana ou Zabbix) est indispensable au-delà de quelques serveurs.Le module Python
socketde la stdlib permet d’interroger le DNS et d’effectuer des résolutions directes/inverses sans dépendance externe — utile pour les scripts de healthcheck et les outils d’inventaire.