Proxy WeKnora : implémentation SOCKS5 sans fuites
Proxy WeKnora : implémentation SOCKS5 sans fuites
Le Proxy WeKnora échoue dès que le premier paquet TLS est intercepté par un DPI (Deep Packet Inspection). Une mauvaise gestion du flux TCP rend le tunnel transparent et détectable immédiatement.
La mise en place d’un tunnel SOCKS5/HTTP nécessite une maîtrise parfaite de l’état des connexions. Une fuite de descripteur de fichier peut faire tomber un serveur sous une charge de 1000 connexions simultanées.
Ce guide détaille les erreurs de gestion de flux et les patterns de réutilisation de buffers en Go pour maintenir un Proxy WeKnora stable.
🛠️ Prérequis
Environnement Linux (Debian 12 ou Ubuntu 22.04 recommandé) et Go 1.22+ installé.
- Go 1.22 :
sudo apt install golang-go - Outils de test :
nc(netcat) etcurl - Accès SSH pour tester les tunnels vers un serveur distant.
📚 Comprendre Proxy WeKnora
Le Proxy WeKnora repose sur la manipulation de flux bruts via l’interface net.Conn. Le protocole SOCKS5 (RFC 1928) suit un état machine strict :
Client -> [VER, NMETHODS, METHODS] -> Server Server -> [VER, METHOD] -> Client Client -> [CMD, RSV, ATYP, DST.ADDR, DST.PORT] -> Server Server -> [REP, RSV, ATYP, BND.ADDR, BIND.PORT] -> Client Client <--> [RELAY (io.Copy)] <--> Server
Contrairement à un proxy HTTP classique, le SOCKS5 ne traite pas les en-têtes applicatifs. Il agit au niveau de la couche transport. En Go, l’enjeu est de ne pas bloquer la boucle d’événements du runtime. Si vous utilisez un bufio.Reader sans limiter la lecture, une attaque par déni de service est possible en envoyant un flux infini sans fin de ligne.
🐹 Le code — Proxy WeKnora
📖 Explication
Dans RelayTunnel, l’utilisation de errChan := make(chan error, 2) est cruciale. Le buffer de taille 2 évite que les goroutines ne se bloquent en essayant d’envoyer une erreur sur un canal non lu. Dans HandleSocks5, la vérification buf[0] != 0x05 est la première ligne de défense contre les protocoles non supportés. On ne fait pas confiance au client. L’utilisation de io.Copy est préférée à une boucle manuelle car elle implémente nativement des optimisations de transfert de blocs (splice sur Linux) qui augmentent les performances du Proxy WeKnra.
🔄 Second exemple
▶️ Exemple d’utilisation
Lancement d’un serveur proxy local pour tester la connectivité vers un site censuré.
# Lancer le binaire compilé
./proxy-wenkora --port 1080
# Configurer curl pour utiliser le proxy SOCKS5 local
curl --proxy socks5h://localhost:1080 https://google.com
# Sortie attendue
# HTTP/2 200
# content-type: text/html; charset=UTF-8
# ...
🚀 Cas d’usage avancés
1. Tunneling TLS via HTTP CONNECT : Implémenter un handler qui intercepte la méthode CONNECT pour encapsuler du trafic HTTPS dans un flux TCP brut. Cela permet de masquer la nature du trafic au niveau applicatif.
2. Rotation d’IP dynamique : Intégrer un pool de connexantes sortantes. Chaque requête passant par le Proxy WeKnora est redirigée vers une interface réseau différente en utilisant net.Dialer.LocalAddr.
3. Monitoring de débit : Utiliser un io.TeeReader pour injecter des compteurs de bytes dans un système de métriques (Prometheus) sans interrompre le flux de données.
🐛 Erreurs courantes
⚠️ Fuite de mémoire (OOM)
Charger tout le contenu de la réponse en mémoire avant de l’envoyer.
data, _ := io.ReadAll(resp.Body); clientConn.Write(data)
io.Copy(clientConn, resp.Body)
⚠️ Goroutines zombies
Ne pas fermer la connexion de l’autre côté du tunnel lors d’une erreur.
go io.Copy(c1, c2); go io.Copy(c2, c1)
go func() { defer c1.Close(); io.Copy(c1, c2) }()
⚠️ Absence de timeout
Laisser les connexions TCP sans limite de temps.
conn, _ := net.Accept(); handle(conn)
conn.SetDeadline(time.Now().Add(30 * time.Second)); handle(conn)
⚠️ Mauvaise gestion du DNS
Résoudre l’adresse sur le serveur proxy au lieu du client.
net.Dial("tcp", targetAddr)
net.Dial("tcp", targetHostWithPort)
✅ Bonnes pratiques
Pour un Proxy WeKnora de production, respectez ces règles de l’art :
- Utilisez des buffers partagés : Utilisez un
sync.Poolpour réutiliser les[]bytede lecture et éviter la pression sur le Garbage Collector. - Implémentez le circuit breaker : Si un endpoint cible échoue 5 fois de suite, stoppez les tentatives de connexion pour économiser les ressources.
- Forcez les deadlines : Chaque opération de lecture/écriture doit avoir un timeout explicite via
SetReadDeadline. - Logging structuré : Utilisez
slog(disponible depuis Go 1.21) pour tracer les connexions sans ralentir le pipeline. - Vérifiez l’intégrité des en-têtes : Ne transférez jamais de headers HTTP sans valider leur format, pour éviter l’injection de requêtes.
- Le Proxy WeKnora doit utiliser io.Copy pour éviter l'épuisement de la RAM.
- La gestion des deadlines est obligatoire pour prévenir les DoS.
- Le protocole SOCKS5 exige une vérification stricte de la version 0x05.
- L'utilisation de goroutines sans synchronisation crée des fuites de ressources.
- Le sync.Pool réduit drastiquement la latence liée au GC.
- Le tunneling HTTPS nécessite une gestion propre de la méthode HTTP CONNECT.
- Le parsing des adresses doit être sécurisé contre les injections.
- L'utilisation de Go 1.22+ permet des optimisations de boucle plus sûres.
❓ Questions fréquentes
Pourquoi mon proxy s'arrête-t-il lors de gros transferts ?
Vous utilisez probablement io.ReadAll. Un transfert de fichier volumineux sature la RAM et déclenche l’OOM Killer.
Comment masquer le trafic SOCKS5 ?
Il faut encapsuler le flux dans du TLS ou utiliser un protocole de transport comme Shadowsocks ou Trojan.
Est-ce que Go est assez rapide pour du proxying ?
Oui, le modèle de goroutines de Go est conçu pour gérer des milliers de connexes avec une latence minimale.
Comment gérer les DNS dans le Proxy WeKnora ?
Le client doit envoyer l’adresse déjà résolue ou le proxy doit effectuer la résolution via une méthode sécurisée.
📚 Sur le même blog
🔗 Le même sujet sur nos autres blogs
📝 Conclusion
L’implémentation d’un Proxy WeKnora ne tolère aucune approximation sur la gestion des flux et des ressources. La performance ne vient pas de la complexité, mais de la gestion rigoureuse des buffers et des timeouts. Pour approfondir la gestion des sockets, consultez la documentation Go officielle. Un proxy qui ne gère pas ses deadlines est une porte ouverte aux attaques par épuisement de ressources.