Mettre un serveur IRC derrière un reverse proxy est un réflexe d'administrateur système que peu de guides IRC abordent correctement. La raison est simple : le protocole IRC n'est pas du HTTP, et Nginx est avant tout un serveur HTTP. Mais Nginx a un module stream pour le proxy TCP, et les clients IRC modernes supportent le WebSocket, ce qui ouvre des possibilités que je n'aurais pas imaginées quand j'ai monté mon premier serveur IRC en 2001. Voici comment assembler tout ça proprement.
Pourquoi mettre IRC derrière Nginx
Trois raisons reviennent systématiquement.
La première : la TLS termination centralisée. Si vous hébergez plusieurs services sur la même machine — site web, IRC, un client web IRC — gérer les certificats TLS à un seul endroit (Nginx) est nettement plus simple que de configurer le TLS dans chaque application séparément. Certbot renouvelle le certificat, Nginx le sert, et vos services backend communiquent en clair sur localhost.
La deuxième : cacher l'IP du serveur IRC. Exposer directement un serveur IRC sur Internet, c'est exposer son adresse IP aux attaques DDoS. Un reverse proxy — surtout combiné avec un CDN comme Cloudflare en mode TCP proxy — ajoute une couche d'indirection. L'attaquant voit l'IP du proxy, pas celle du serveur.
La troisième : servir un client IRC web. The Lounge, Kiwi IRC, Gamja — ces clients web IRC fonctionnent via WebSocket. Nginx gère le WebSocket nativement et permet de servir le client web sur le port 443 standard, sans ouvrir de port supplémentaire. Pour les entreprises qui font appel à un prestataire d'infogérance comme PowerITi, cette standardisation sur le port 443 simplifie les règles de firewall et les audits de sécurité.
Stream proxy : le mode TCP pur
Le module stream de Nginx permet de proxifier du trafic TCP brut — exactement ce dont IRC a besoin. Ce module n'est pas toujours compilé par défaut. Vérifiez sa présence :
# Vérifier que le module stream est disponible
nginx -V 2>&1 | grep -- '--with-stream'
# Sur Debian/Ubuntu, le paquet nginx-full l'inclut
sudo apt install nginx-full
# Ou le module séparé
sudo apt install libnginx-mod-stream
La configuration du stream proxy se fait en dehors du bloc http {}, au niveau racine de nginx.conf :
# /etc/nginx/nginx.conf — Ajout du bloc stream
stream {
# Proxy TCP pour IRC TLS
upstream irc_backend {
server 127.0.0.1:6667; # IRCd écoute en local, plaintext
}
server {
listen 6697 ssl;
proxy_pass irc_backend;
# Certificats TLS (même certificat que le site web)
ssl_certificate /etc/letsencrypt/live/irc.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/irc.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Timeouts adaptés à IRC (connexions longues)
proxy_timeout 600s;
proxy_connect_timeout 10s;
}
# Proxy TCP pour IRC plaintext (désactivez en production)
server {
listen 6667;
proxy_pass irc_backend;
proxy_timeout 600s;
}
}
Le proxy_timeout est le paramètre le plus fréquemment mal configuré. La valeur par défaut de Nginx (10 minutes) est correcte pour du HTTP, mais IRC maintient des connexions ouvertes pendant des heures ou des jours. Si le timeout est trop court, vos utilisateurs seront déconnectés périodiquement sans raison apparente. Avec 600 secondes (10 minutes), le PING/PONG IRC suffit à maintenir la connexion active.
proxy_protocol on; dans le bloc server du stream, et configurez votre IRCd pour accepter le PROXY protocol sur le port correspondant. Sans cela, tous vos utilisateurs apparaîtront avec l'IP 127.0.0.1.# Avec PROXY protocol activé
stream {
upstream irc_backend {
server 127.0.0.1:6667;
}
server {
listen 6697 ssl;
proxy_pass irc_backend;
proxy_protocol on; # Envoie l'IP réelle au backend
ssl_certificate /etc/letsencrypt/live/irc.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/irc.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
proxy_timeout 600s;
proxy_connect_timeout 10s;
}
}
TLS termination centralisée
L'avantage principal de la TLS termination par Nginx est la gestion centralisée des certificats. Un seul certificat wildcard ou multi-SAN, renouvelé automatiquement par Certbot, sert à la fois le site web, le client IRC web, et le proxy TCP IRC.
# Certificat multi-domaine avec Certbot
sudo certbot certonly --nginx \
-d example.com \
-d www.example.com \
-d irc.example.com \
-d chat.example.com
# Renouvellement automatique (déjà configuré par Certbot)
# Vérification :
sudo certbot renew --dry-run
# Hook de rechargement Nginx après renouvellement
# /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
#!/bin/bash
nginx -t && systemctl reload nginx
Quand Nginx gère le TLS, votre serveur IRC n'a plus besoin de gérer ses propres certificats. Il écoute en plaintext sur localhost, et Nginx chiffre le trafic côté client. Cette séparation simplifie la configuration de l'IRCd et élimine le risque d'un certificat expiré côté serveur IRC — le certificat n'existe qu'à un seul endroit.
WebSocket pour les clients IRC web
Les clients IRC modernes basés sur le web — The Lounge, Kiwi IRC, Gamja — utilisent le WebSocket pour communiquer avec le serveur IRC. Le WebSocket commence par une requête HTTP classique (le "handshake"), puis bascule en communication bidirectionnelle persistante. Nginx gère ce processus nativement.
# /etc/nginx/sites-available/chat.example.com
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 443 ssl http2;
server_name chat.example.com;
ssl_certificate /etc/letsencrypt/live/chat.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# Client IRC web (The Lounge)
location / {
proxy_pass http://127.0.0.1:9000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
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;
# Timeouts longs pour WebSocket
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
# WebSocket IRC direct (si l'IRCd supporte le WebSocket)
location /webirc {
proxy_pass http://127.0.0.1:8097;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
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_read_timeout 600s;
proxy_send_timeout 600s;
}
}
Le bloc map en haut du fichier est la clé du WebSocket dans Nginx. Il détecte l'en-tête Upgrade de la requête HTTP et propage le basculement de protocole vers le backend. Sans ce bloc, le WebSocket ne fonctionnera pas.
Ergo (le serveur IRC en Go) supporte le WebSocket nativement. Vous pouvez configurer un listener WebSocket dans ircd.yaml et le proxifier directement via Nginx. InspIRCd et UnrealIRCd nécessitent un module supplémentaire ou un gateway externe comme webircgateway.
Cas pratique : The Lounge derrière Nginx
The Lounge est un client IRC web auto-hébergé. Il se connecte à n'importe quel serveur IRC et fournit une interface web moderne avec historique persistant, notifications push, et support multi-appareils. C'est l'outil que je recommande pour offrir un accès IRC via navigateur.
# Installation de The Lounge
npm install -g thelounge
# Première configuration
thelounge start
# Par défaut, écoute sur http://localhost:9000
# Créer un utilisateur
thelounge add monuser
# Configuration dans ~/.thelounge/config.js
module.exports = {
public: false, // Mode privé (comptes requis)
port: 9000,
bind: "127.0.0.1", // Écoute locale uniquement
reverseProxy: true, // Fait confiance aux en-têtes X-Forwarded-*
https: {
enable: false // TLS géré par Nginx
},
theme: "default",
prefetch: true,
defaults: {
name: "Libera.Chat",
host: "irc.libera.chat",
port: 6697,
tls: true
}
};
# Service systemd pour The Lounge
# /etc/systemd/system/thelounge.service
[Unit]
Description=The Lounge IRC Client
After=network.target
[Service]
Type=simple
User=thelounge
ExecStart=/usr/bin/thelounge start
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Avec la configuration Nginx de la section précédente, The Lounge est accessible sur https://chat.example.com. Le TLS est géré par Nginx, The Lounge écoute en HTTP sur localhost, et le WebSocket traverse le proxy de façon transparente.
Sécurité et durcissement
Quelques mesures de sécurité spécifiques au proxy IRC.
# Rate limiting sur les connexions TCP (module stream)
stream {
# Limite de connexions par IP
limit_conn_zone $binary_remote_addr zone=irc_conn:10m;
server {
listen 6697 ssl;
proxy_pass irc_backend;
limit_conn irc_conn 5; # Max 5 connexions par IP
ssl_certificate /etc/letsencrypt/live/irc.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/irc.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
proxy_timeout 600s;
}
}
# Headers de sécurité pour le client web IRC
server {
# ... (configuration HTTPS)
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy strict-origin-when-cross-origin;
add_header Content-Security-Policy "default-src 'self'; connect-src 'self' wss:; img-src 'self' data:; style-src 'self' 'unsafe-inline';";
# Bloquer les user agents suspects
if ($http_user_agent ~* (bot|crawl|spider|scan)) {
return 403;
}
}
Le Content-Security-Policy mérite une attention particulière pour un client IRC web. Le connect-src 'self' wss: autorise les connexions WebSocket (nécessaire pour le fonctionnement du client). Le img-src 'self' data: autorise les images inline (avatars, prévisualisations). Ajustez selon les fonctionnalités de votre client.
Pour les structures qui confient leur infogérance à un partenaire comme PowerITi, cette configuration standardisée autour de Nginx facilite la prise en main par l'équipe support. Nginx est un outil que tout administrateur système connaît, contrairement aux configurations TLS spécifiques à chaque serveur IRC.
Monitoring et logs
Les logs du module stream de Nginx sont configurés séparément des logs HTTP :
# Logs du proxy TCP IRC
stream {
log_format irc_log '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time';
access_log /var/log/nginx/irc-access.log irc_log;
error_log /var/log/nginx/irc-error.log;
server {
listen 6697 ssl;
proxy_pass irc_backend;
# ...
}
}
Ces logs vous donnent la durée de session ($session_time), le volume de données échangé, et l'IP source de chaque connexion. C'est suffisant pour détecter les patterns anormaux : connexions très courtes (scan de port), transferts massifs (flood), ou connexions depuis des plages IP suspectes.
# Analyse rapide des logs
# Top 10 des IP par nombre de connexions
awk '{print $1}' /var/log/nginx/irc-access.log | sort | uniq -c | sort -rn | head -10
# Connexions de moins de 5 secondes (potentiel scan)
awk '$NF < 5 {print $0}' /var/log/nginx/irc-access.log
# Volume total transféré par jour
awk '{sent+=$5; recv+=$6} END {print "Envoyé:", sent/1024/1024, "Mo", "Reçu:", recv/1024/1024, "Mo"}' /var/log/nginx/irc-access.log
Questions fréquentes
Nginx ajoute-t-il de la latence pour IRC ?
La latence ajoutée par le proxy Nginx est de l'ordre de la milliseconde, ce qui est imperceptible pour IRC. Le proxy opère au niveau TCP et ne modifie pas les données en transit. L'impact sur les performances est négligeable même pour des serveurs IRC avec des milliers de connexions.
Peut-on utiliser Cloudflare comme proxy pour IRC ?
Cloudflare supporte le proxy TCP via Spectrum, mais c'est un service payant (plan Enterprise ou Spectrum add-on). Le plan gratuit ne proxifie que le HTTP/HTTPS. Si vous utilisez uniquement le WebSocket IRC via le port 443, le plan Pro de Cloudflare peut suffire, car le WebSocket est supporté sur tous les plans payants.
HAProxy est-il une meilleure alternative que Nginx pour le proxy IRC ?
HAProxy est plus spécialisé dans le proxy TCP/L4 et offre des fonctionnalités avancées (health checks TCP, sticky sessions, statistiques détaillées). Si votre seul besoin est le proxy IRC sans composant web, HAProxy est un choix solide. Si vous avez aussi un site web et un client IRC web à servir, Nginx gère les deux avec une seule configuration.