Proxy WeKnora

Proxy WeKnora : implémentation SOCKS5 sans fuites

Anti-patterns et pièges GoAvancé

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.

Proxy WeKnora

🛠️ 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) et curl
  • 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

Go
package main

import (
	"io"
	"net"
)

// HandleSocks5 gère la phase de négociation SOCKS5.
func HandleSocks5(clientConn net.Conn) error {
	// Lecture du premier octet (version)
	buf := make([]byte, 256)
	n, err := clientConn.Read(buf)
	if err != nil {
		return err
	}

	// Vérification RFC 1928 : version doit être 0x05
	if buf[0] != 0x05 {
		return io.ErrUnexpectedEOF
	}

	// Le Proxy WeKnora doit ici traiter la sélection de méthode
	// ... (logique de handshake)
	return nil
}

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

Documentation officielle Go

🔄 Second exemple

Go
package main

import (
	"io"
	"net"
)

// RelayTunnel effectue le transfert de données entre le client et la cible.
func RelayTunnel(clientConn, targetConn net.Conn) error {
	errChan := make(chan error, 2)

	// Goroutine pour le flux Client -> Cible
	go func() {
		_, err := io.Copy(targetConn, clientConn)
		errChan <- err
	}()

	// Goriente pour le flux Cible -> Client
	go func() {
		_, err := io.Copy(clientConn, targetConn)
		errChan <- err
	}()

	// Attente de la première erreur ou fin de flux
	return <-errChan
}

▶️ 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.

✗ Mauvais

data, _ := io.ReadAll(resp.Body); clientConn.Write(data)
✓ Correct

io.Copy(clientConn, resp.Body)

⚠️ Goroutines zombies

Ne pas fermer la connexion de l’autre côté du tunnel lors d’une erreur.

✗ Mauvais

go io.Copy(c1, c2); go io.Copy(c2, c1)
✓ Correct

go func() { defer c1.Close(); io.Copy(c1, c2) }()

⚠️ Absence de timeout

Laisser les connexions TCP sans limite de temps.

✗ Mauvais

conn, _ := net.Accept(); handle(conn)
✓ Correct

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.

✗ Mauvais

net.Dial("tcp", targetAddr)
✓ Correct

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.Pool pour réutiliser les []byte de 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.
Points clés

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

Publications similaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *