Dans cette section, je vais expliquer comment créer un générateur de proxy Tor et implémenter une fonctionnalité d'application web permettant de suivre et de gérer les proxys.

Les sujets abordés dans cette section sont les suivants :

  1. Générateur de proxy Tor
  2. Génération automatisée de proxy Tor
    1. Création de proxys
    2. Vérification de l'état des proxys
    3. Suppression de proxys
  3. Modèles de base de données
  4. Création du backend
  5. Création du frontend
  6. Test

Générateur de proxy Tor

Le dépôt GitHub suivant contient le code d'un générateur de proxy Tor :

https://github.com/0xHamy/tor_proxy_gen

Pour chaque proxy, vous aurez besoin d'un conteneur Docker distinct, du moins c'est ce que je fais. Vous pouvez également acheter des centaines de proxys à bas prix, mais cela ne coûte rien d'apprendre à les configurer vous-même.

Si vous regardez le fichier docker-compose.yaml dans ce référentiel, vous pouvez voir que nous acheminons tout le trafic via le port 9050 à l'intérieur du docker vers le réseau hôte via le port 9050 :

services:
  tor:
    build:
      context: .
      dockerfile: Dockerfile
    image: tor-proxy
    ports:
      - "9050:9050"
    container_name: tor-proxy
    restart: unless-stopped

Si vous souhaitez accéder au proxy via un port différent de celui de votre réseau hôte, vous pouvez modifier les ports comme suit :

3531:9050

Le premier port est celui qui s'ouvre sur votre système hôte et le second est celui qui s'exécute dans Docker.

Le fichier torrc utilise le port 9050 pour le service tor :

SocksPort 0.0.0.0:9050
Log notice stdout

Dockerfile exécute une version réduite du système d'exploitation Debian afin de faire fonctionner le proxy Tor :

# Use a lightweight Debian-based image
FROM debian:bullseye-slim

# Install Tor
RUN apt-get update && \
    apt-get install -y tor && \
    rm -rf /var/lib/apt/lists/*

# Copy custom Tor configuration
COPY torrc /etc/tor/torrc

# Expose the Tor SOCKS5 proxy port
EXPOSE 9050

# Run Tor as the main process
CMD ["tor", "-f", "/etc/tor/torrc"]

Lorsqu'il s'agit de créer une application qui génère plusieurs proxys Tor, nous ne pouvons pas nous fier uniquement au port 9050, car il est très utilisé et nous ne pouvons donc pas l'utiliser s'il est déjà occupé par autre chose. Pour cette raison, nous allons créer un script Python en choisissant un port aléatoire entre 40 000 et 60 000. Il n'y a que 65 535 ports dans un ordinateur, c'est pourquoi nous avons choisi cette plage de ports spécifique, car d'après ce que j'ai pu constater, elle n'est généralement pas utilisée. De plus, elle ne nécessite pas de droits root pour être utilisée, du moins sous Ubuntu.


Génération automatisée de proxys Tor

Comme nous générons des proxys Tor à l'aide de Docker, nous devons implémenter trois fonctionnalités principales :

  1. Une fonction permettant de créer un conteneur Docker pour exécuter un proxy Tor
  2. Une fonction permettant de vérifier l'état d'un conteneur Docker afin de vérifier si le proxy est actif
  3. Une fonction permettant d'arrêter et de supprimer un conteneur Docker

Les fichiers correspondants se trouvent dans app/services/*.py. Les fichiers spécifiques que nous utiliserons sont les suivants :

  1. tor_proxy_gen.py : gère la création de conteneurs Docker pour les proxys Tor
  2. container_status.py : vérifie l'état des conteneurs Docker
  3. rm_container.py : gère la suppression des conteneurs Docker

Création de proxys

Le script tor_proxy_gen.py automatise la création de conteneurs Docker exécutant des proxys Tor, fournissant un proxy SOCKS5 pour un accès anonyme au réseau. Vous trouverez ci-dessous une explication concise de ses composants et de ses fonctionnalités.

Objectif :

  • Crée et démarre un conteneur Docker exécutant un proxy Tor, attribue un port aléatoire, récupère l'adresse IP du conteneur et l'adresse IP du nœud de sortie Tor, puis renvoie les détails sous forme de dictionnaire prêt pour JSON.

Variables globales :

  • PORT_MIN, PORT_MAX :
    • Objectif : définir la plage (40001-60001) pour la sélection aléatoire des ports hôtes.
    • Détails : garantit l'attribution d'un port unique à chaque conteneur.
  • SOCKS_PORT_IN_CONTAINER :
    • Objectif : définit le port SOCKS Tor à l'intérieur du conteneur (9050).
    • Détails : port standard pour le proxy SOCKS5 de Tor.
    • MAX_PORT_ATTEMPTS :
    • Objectif : limite le nombre de tentatives pour trouver un port libre (100).
    • Détails : empêche les boucles infinies dans la sélection des ports.
    • WAIT_MAX_SECONDS, WAIT_STEP_SECONDS :
    • Objectif : contrôle le délai d'expiration (60 s) et l'intervalle d'interrogation (2 s) pour la disponibilité des ports.
    • Détails : utilisé lors de l'attente de l'ouverture du port du conteneur.
  • DOCKERFILE, TORRC, COMPOSE_TEMPLATE :
    • Objectif : définir les fichiers de configuration Docker sous forme de chaînes.
    • Détails : DOCKERFILE configure une image basée sur Debian avec Tor ; TORRC configure le port SOCKS de Tor ; COMPOSE_TEMPLATE définit les paramètres Docker Compose pour le conteneur.

Fonctions :

  • die :

    • Objectif : Enregistre un message d'erreur et quitte le programme.
    • Paramètres clés : msg (message d'erreur), code (code de sortie, par défaut 1).
    • Retour : Aucun (quitte le programme).
    • Détails : Affiche l'erreur sur stderr et l'enregistre via logging.
  • cmd_exists :

    • Objectif : vérifie si un exécutable (par exemple, docker) est disponible dans $PATH.
    • Paramètres clés : executable (nom de la commande).
    • Retourne : booléen (True si trouvé).
    • Détails : utilise shutil.which et consigne le résultat dans le journal.
  • docker_compose_available :

    • Objectif : vérifie si Docker Compose est disponible.
    • Paramètres clés : aucun.
    • Retourne : booléen (True si docker compose version réussit).
    • Détails : exécute une commande pour vérifier la présence de Docker Compose et consigne les résultats ou les erreurs.
  • is_port_free :

    • Objectif : vérifie si un port TCP sur 127.0.0.1 est inutilisé.
    • Paramètres clés : port (numéro de port).
    • Retourne : booléen (True si le port est libre).
    • Détails : tente de se connecter au port à l'aide d'un socket avec un délai d'expiration de 0,5 seconde.
  • random_free_port :

    • Objectif : trouve un port aléatoire inutilisé dans la plage définie.
    • Paramètres clés : Aucun.
    • Retourne : entier (numéro du port libre).
    • Détails : tente jusqu'à MAX_PORT_ATTEMPTS ports aléatoires ; appelle die si aucun n'est libre.
  • random_container_name :

    • Objectif : génère un nom de conteneur unique.
    • Paramètres clés : aucun.
    • Retourne : chaîne (par exemple, torproxy_abcdef).
    • Détails : combine torproxy_ avec six lettres minuscules aléatoires.
  • wait_for_port :

    • Objectif : attend qu'un port sur un hôte soit ouvert ou que le délai expire.
    • Paramètres clés : host (IP), port (numéro de port), timeout (secondes).
    • Retourne : Aucun (lève une exception RuntimeError en cas de dépassement du délai).
    • Détails : Interroge toutes les WAIT_STEP_SECONDS jusqu'à ce que le port soit utilisé ou que le délai expire.
  • fetch_tor_exit_ip :

    • Objectif : Récupère l'adresse IP du nœud de sortie Tor via des services externes.
    • Paramètres clés : host_port (port proxy), timeout (délai d'expiration de la requête, par défaut 15 s).
    • Retourne : Chaîne (adresse IP du nœud de sortie).
    • Détails : interroge les services IP-echo (par exemple, checkip.amazonaws.com) via un proxy SOCKS5 ; lève une exception RuntimeError si toutes les requêtes échouent.
  • create_and_start_proxy :

    • Objectif : crée et démarre un conteneur proxy Tor, puis renvoie ses détails.
    • Paramètres clés : Aucun.
    • Retourne : Dictionnaire contenant container_name, container_ip (avec port), tor_exit_node et timestamp.
    • Détails :
      • Vérifie la disponibilité de Docker et Compose ; quitte si ils sont manquants.
      • Génère un nom de conteneur et un port aléatoires.
      • Crée un répertoire temporaire avec Dockerfile, torrc et compose.yaml.
      • Exécute docker compose up --build -d pour démarrer le conteneur.
      • Attend que le port s'ouvre, récupère l'adresse IP du conteneur via docker inspect et récupère l'adresse IP de sortie Tor.
      • Nettoie le conteneur en cas d'échec à l'aide de docker rm -f.

Sur ma machine, Docker nécessite sudo pour les permissions root. L'exécution de tor_proxy_gen.py avec sudo python3 tor_proxy_gen.py utilise l'interpréteur Python du système, qui contourne l'environnement virtuel contenant les dépendances du projet tornet_scraper.

À la place, exécutez le script comme suit pour utiliser l'interpréteur Python de l'environnement virtuel :

-> % sudo /home/hamy/tornet_scraper/venv/bin/python3 app/services/tor_proxy_gen.py                      
[sudo] password for hamy: 
{
  "container_name": "torproxy_tucgye",
  "proxy_ip_docker": "192.168.128.2:45364",
  "proxy_ip_exit_node": "185.193.52.180",
  "timestamp": 1752431067
}

Cela garantit que le script utilise l'interpréteur Python de l'environnement virtuel tornet_scraper avec toutes les dépendances requises installées.

Vérification de l'état du proxy

Le script container_status.py fournit des fonctions utilitaires pour vérifier l'état des conteneurs Docker. Vous trouverez ci-dessous une explication concise de ses fonctions.

  1. cmd_exists :

    • Objectif : vérifie si un exécutable spécifié est disponible dans le $PATH du système.
    • Paramètres clés :
      • executable : nom de la commande sous forme de chaîne (par exemple, docker).
    • Retourne : booléen (True si l'exécutable est trouvé, False sinon).
    • Détails : utilise shutil.which pour vérifier si l'exécutable existe et est accessible dans le $PATH du système.
  2. container_running :

    • Objectif : détermine si un conteneur Docker est en cours d'exécution.
    • Paramètres clés :
      • name : nom sous forme de chaîne du conteneur Docker.
    • Renvoie : booléen (True si le conteneur existe et est en cours d'exécution, False sinon).
    • Détails : exécute sudo docker inspect -f {{.State.Running}} <name> pour vérifier l'état d'exécution du conteneur. Renvoie True si la sortie est « true », False si la commande échoue (par exemple, le conteneur n'existe pas) ou si l'état n'est pas en cours d'exécution. Supprime stderr avec DEVNULL pour éviter les sorties d'erreur.

Suppression de proxys

Le script rm_container.py fournit une fonction permettant de supprimer de force un conteneur Docker. Vous trouverez ci-dessous une explication concise de sa seule fonction.

  1. delete_container :

    • Objectif : supprime un conteneur Docker spécifié à l'aide de sudo docker rm -f.
    • Paramètres clés :
      • name : nom sous forme de chaîne du conteneur Docker à supprimer.
    • Retourne : booléen (True si la suppression réussit, False si elle échoue).
    • Détails : exécute sudo docker rm -f <name> pour supprimer de force le conteneur, en supprimant à la fois stdout et stderr avec DEVNULL. Renvoie True si la commande réussit, ou False si une erreur CalledProcessError se produit (par exemple, le conteneur n'existe pas ou il y a un problème d'autorisation).

Modèles de base de données

Voici à quoi ressemblent vos modèles de base de données :

class Proxy(Base):
    __tablename__ = "proxies"

    id = Column(Integer, primary_key=True, index=True)
    container_name = Column(String, unique=True, index=True)
    container_ip = Column(String)
    tor_exit_node = Column(String)
    timestamp = Column(DateTime, default=datetime.utcnow)
    running = Column(Boolean, default=True)

Pour certains d'entre vous, datetime.utcnow peut sembler obsolète, mais vous n'avez pas à vous en soucier.


Création du backend

Les modules de services font tout le gros du travail, mais nous avons toujours besoin d'un routeur backend pour appeler ces fonctions et gérer les requêtes provenant du frontend. Votre backend pour la génération de proxys se trouve dans app/routes/proxy_gen.py.

Le script proxy_gen.py définit les routes FastAPI pour créer, supprimer et lister les conteneurs de proxy Tor, interagir avec la base de données et les services externes. Vous trouverez ci-dessous une explication concise de ses composants et fonctions.

Variables globales

  1. logger :
    • Objectif : configure la journalisation pour le débogage et le suivi des erreurs.
    • Détails : Utilise le module logging avec le niveau INFO pour consigner les opérations et les erreurs.
  2. proxy_gen_router :
    • Objectif : Routeur FastAPI pour les points de terminaison liés aux proxys.
    • Détails : Configuré avec le préfixe /api/proxy-gen et les balises [« API », « Proxy Generator »] pour l'organisation.

Fonctions

  1. create_proxy :

    • Objectif : Crée un nouveau conteneur proxy Tor et stocke ses détails dans la base de données.
    • Paramètres clés :
      • request : Objet FastAPI Request pour la gestion des sessions.
      • db : SQLAlchemy Session pour les opérations de base de données (via Depends(get_db)).
    • Retourne : JSONResponse avec le statut de réussite, le message et les détails du proxy (nom du conteneur, IP, nœud de sortie Tor, horodatage, statut d'exécution).
    • Détails : Appelle create_and_start_proxy (à partir de tor_proxy_gen.py) pour démarrer un conteneur, crée une instance du modèle Proxy, l'enregistre dans la base de données et renvoie une réponse JSON. Lève une exception HTTPException (500) en cas d'erreur et consigne le problème dans le journal.
  2. delete_proxy :

    • Objectif : supprime un conteneur proxy Tor spécifié et son enregistrement dans la base de données.
    • Paramètres clés :
      • container_name : nom sous forme de chaîne du conteneur à supprimer.
      • request : objet FastAPI Request pour les messages flash basés sur la session.
      • db : SQLAlchemy Session pour les opérations de base de données.
    • Renvoie : JSONResponse avec le statut de réussite et un message en cas de suppression réussie.
    • Détails : interroge la table Proxy pour le conteneur ; s'il est trouvé, appelle delete_container (à partir de rm_container.py) pour le supprimer. Supprime l'enregistrement de la base de données en cas de succès et ajoute un message flash de réussite à la session. Lève une exception HTTPException (404 si introuvable, 500 en cas d'erreur) et ajoute des messages flash d'erreur.
  3. list_proxies :

    • Objectif : récupère une liste de tous les proxys avec leur état d'exécution mis à jour.
    • Paramètres clés :
      • db : SQLAlchemy Session pour les opérations de base de données.
    • Retourne : JSONResponse avec une liste de proxys, chacun contenant le nom du conteneur, l'IP, le nœud de sortie Tor, l'horodatage et l'état d'exécution.
    • Détails : interroge tous les enregistrements Proxy, vérifie l'état d'exécution de chaque conteneur à l'aide de container_running (à partir de container_status.py), met à jour le champ running dans la base de données si nécessaire et renvoie la liste au format JSON. Génère une exception HTTPException (500) en cas d'erreur et consigne le problème dans le journal.

Création de l'interface utilisateur

Votre code de modèle se trouve dans app/templates/proxy_gen.html.

Le modèle proxy_gen.html étend base.html afin de fournir une interface utilisateur permettant de gérer les conteneurs proxy Tor dans l'application tornet_scraper, en interagissant avec le backend via des appels API. Vous trouverez ci-dessous une explication concise de ses principales fonctionnalités et de leur interaction avec le backend.

  1. Héritage du modèle :

    • Objectif : exploite la mise en page base.html pour garantir une structure cohérente.
    • Interaction avec le backend : hérite de la barre de navigation et de la gestion des messages flash de base.html. Le {% block title %} définit le titre de la page « Proxy Generator » et {% block content %} définit les fonctionnalités spécifiques à la page. Les messages flash provenant du backend (stockés dans la session) sont affichés dans le conteneur hérité.
  2. Création d'un proxy :

    • Objectif : lance et confirme la création d'un nouveau conteneur de proxy Tor.
    • Interaction avec le backend :
      • Un bouton « Créer un nouveau proxy » déclenche la fonction JavaScript createProxy(), ouvrant une fenêtre modale de confirmation.
      • Le bouton « Continuer » de la fenêtre modale appelle confirmCreateProxy(), envoyant une requête AJAX POST à /api/proxy-gen/create (gérée par proxy_gen.py::create_proxy).
      • Le backend crée un conteneur Docker (via tor_proxy_gen.py), enregistre les détails du proxy (nom du conteneur, IP, nœud de sortie Tor, horodatage, état d'exécution) dans la table Proxy de la base de données et renvoie une réponse JSON.
      • En cas de succès, la table est mise à jour via updateProxyTable(). Les erreurs déclenchent des journaux de console et réactivent le bouton.
  3. Affichage et mise à jour de la table des proxys :

    • Objectif : affiche une liste dynamique de proxys avec des mises à jour en temps réel.
    • Interaction avec le backend :
      • Au chargement de la page, Jinja2 affiche les données initiales des proxys (proxies de main.py::proxy_gen) dans une table avec des colonnes pour le nom du conteneur, l'adresse IP, le nœud de sortie Tor, l'horodatage et le statut.
      • La fonction updateProxyTable() s'exécute toutes les 10 secondes (via setInterval) et lors de la création/suppression, en envoyant une requête AJAX GET à /api/proxy-gen/list (gérée par proxy_gen.py::list_proxies).
      • Le backend interroge la table Proxy, vérifie l'état d'exécution de chaque conteneur (via container_status.py::container_running), met à jour la base de données si nécessaire et renvoie une liste JSON des proxys.
      • La table est effacée et remplie avec les dernières données, affichant l'état « En cours » ou « Arrêté » avec des badges stylisés.
  4. Suppression de proxy :

    • Objectif : permet aux utilisateurs de supprimer un conteneur proxy.
    • Interaction avec le backend :
      • Chaque ligne de la table comporte un bouton « Supprimer » qui appelle openDeleteModal(containerName) pour ouvrir une fenêtre modale de confirmation avec le nom du conteneur stocké dans un champ de saisie masqué.
      • Le bouton « Supprimer » de la fenêtre modale déclenche confirmDeleteProxy(), qui envoie une requête AJAX DELETE à /api/proxy-gen/delete/{container_name} (gérée par proxy_gen.py::delete_proxy).
      • Le backend vérifie que le proxy existe dans la table Proxy, supprime le conteneur (via rm_container.py::delete_container), supprime l'enregistrement de la base de données et ajoute un message flash de réussite à la session.
      • En cas de réussite, un message flash de réussite s'affiche (via showFlashMessage) et la table est mise à jour. Les erreurs déclenchent un message flash d'erreur.

Test

Exécutez l'application web :

sudo /home/hamy/tornet_scraper/venv/bin/python3 -m uvicorn app.main:app --reload

Ouvrez l'application web :

http://127.0.0.1:8000/proxy-gen

Créez un proxy :

Page proxy Tornet Scraper