En esta sección, explicaré cómo crear un generador de proxies Tor e implementar una funcionalidad de aplicación web para realizar un seguimiento de los proxies y gestionarlos.
Los temas de esta sección incluyen lo siguiente:
- Generador de proxies Tor
- Generación automatizada de proxies Tor
- Creación de proxies
- Comprobación del estado de los proxies
- Eliminación de proxies
- Modelos de base de datos
- Creación del backend
- Creación del frontend
- Pruebas
Generador de proxies Tor
El siguiente repositorio de GitHub contiene el código para un generador de proxies Tor:
https://github.com/0xHamy/tor_proxy_gen
Para cada proxy, necesitarás un contenedor Docker independiente, o al menos eso es lo que yo hago. También puedes comprar cientos de proxies por poco dinero, pero no está de más aprender a configurarlos tú mismo.
Si miras el archivo docker-compose.yaml en ese repositorio, verás que estamos enrutando todo el tráfico a través del puerto 9050 dentro del docker a la red del host a través del puerto 9050:
services:
tor:
build:
context: .
dockerfile: Dockerfile
image: tor-proxy
ports:
- "9050:9050"
container_name: tor-proxy
restart: unless-stopped
Si desea acceder al proxy a través de un puerto diferente al de su red host, puede cambiar los puertos de la siguiente manera:
3531:9050
El primer puerto es el que se abre en su sistema host y el segundo es el que se ejecuta en Docker.
El archivo torrc utiliza el puerto 9050 para el servicio tor:
SocksPort 0.0.0.0:9050
Log notice stdout
Dockerfile está ejecutando una versión reducida del sistema operativo Debian para ejecutar el 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"]
Cuando se trata de crear una aplicación que genere múltiples proxies Tor, no podemos depender únicamente del puerto 9050, ya que se utiliza con frecuencia y no podemos utilizarlo si ya está en uso por otra aplicación. Por este motivo, vamos a crear un script en Python eligiendo un puerto aleatorio entre 40,000 y 60,000. Solo hay 65,535 puertos en cualquier ordenador, por lo que hemos elegido ese rango de puertos específico porque, por lo que he visto, no se suele utilizar y, al menos en Ubuntu, no requiere permisos de root para su uso.
Generación automatizada de proxies Tor
Dado que generamos proxies Tor utilizando Docker, necesitamos implementar tres funcionalidades básicas:
- Una función para crear un contenedor Docker para ejecutar un proxy Tor
- Una función para comprobar el estado de un contenedor Docker y verificar si el proxy está activo
- Una función para apagar y eliminar un contenedor Docker
Los archivos relevantes se encuentran en app/services/*.py. Los archivos específicos que utilizaremos son:
tor_proxy_gen.py: se encarga de la creación de contenedores Docker para proxies Torcontainer_status.py: comprueba el estado de los contenedores Dockerrm_container.py: gestiona la eliminación de contenedores Docker
Creación de proxies
El script tor_proxy_gen.py automatiza la creación de contenedores Docker que ejecutan proxies Tor, proporcionando un proxy SOCKS5 para el acceso anónimo a la red. A continuación se ofrece una explicación concisa de sus componentes y funcionalidad.
Propósito:
- Crea e inicia un contenedor Docker que ejecuta un proxy Tor, asigna un puerto aleatorio, recupera la IP del contenedor y la IP del nodo de salida de Tor, y devuelve los detalles como un diccionario listo para JSON.
Variables globales:
PORT_MIN,PORT_MAX:- Propósito: Define el rango (40001-60001) para seleccionar puertos de host aleatorios.
- Detalles: Garantiza la asignación de puertos únicos para cada contenedor.
SOCKS_PORT_IN_CONTAINER:- Propósito: Establece el puerto SOCKS de Tor dentro del contenedor (9050).
- Detalles: Puerto estándar para el proxy SOCKS5 de Tor.
MAX_PORT_ATTEMPTS:- Finalidad: Limita los intentos de encontrar un puerto libre (100).
- Detalles: Evita bucles infinitos en la selección de puertos.
WAIT_MAX_SECONDS,WAIT_STEP_SECONDS:- Finalidad: Controla el tiempo de espera (60 s) y el intervalo de sondeo (2 s) para la disponibilidad del puerto.
- Detalles: Se utiliza cuando se espera a que se abra el puerto del contenedor.
DOCKERFILE,TORRC,COMPOSE_TEMPLATE:- Finalidad: Define los archivos de configuración de Docker como cadenas.
- Detalles:
DOCKERFILEconfigura una imagen basada en Debian con Tor;TORRCconfigura el puerto SOCKS de Tor;COMPOSE_TEMPLATEdefine la configuración de Docker Compose para el contenedor.
Funciones:
-
die:- Propósito: Registra un mensaje de error y sale del programa.
- Parámetros clave:
msg(mensaje de error),code(código de salida, por defecto 1). - Devuelve: Ninguno (sale del programa).
- Detalles: Envía el error a stderr y lo registra mediante
logging.
-
cmd_exists:- Propósito: Comprueba si un ejecutable (por ejemplo,
docker) está disponible en$PATH. - Parámetros clave:
executable(nombre del comando). - Devuelve: Booleano (
Truesi se encuentra). - Detalles: Utiliza
shutil.whichy registra el resultado.
- Propósito: Comprueba si un ejecutable (por ejemplo,
-
docker_compose_available:- Propósito: Comprueba si Docker Compose está disponible.
- Parámetros clave: Ninguno.
- Devuelve: Booleano (
Truesidocker compose versiontiene éxito). - Detalles: ejecuta un comando para comprobar Docker Compose y registra la salida o los errores.
-
is_port_free:- Finalidad: comprueba si un puerto TCP en
127.0.0.1está libre. - Parámetros clave:
port(número de puerto). - Devuelve: Booleano (
Truesi el puerto está libre). - Detalles: Intenta conectarse al puerto utilizando un socket con un tiempo de espera de 0,5 segundos.
- Finalidad: comprueba si un puerto TCP en
-
random_free_port:- Propósito: Encuentra un puerto aleatorio sin usar en el rango definido.
- Parámetros clave: Ninguno.
- Devuelve: entero (número de puerto libre).
- Detalles: intenta hasta
MAX_PORT_ATTEMPTSpuertos aleatorios; llama adiesi ninguno está libre.
-
random_container_name:- Propósito: genera un nombre de contenedor único.
- Parámetros clave: ninguno.
- Devuelve: Cadena (por ejemplo,
torproxy_abcdef). - Detalles: Combina
torproxy_con seis letras minúsculas aleatorias.
-
wait_for_port:- Propósito: Espera hasta que se abre un puerto en un host o se agota el tiempo de espera.
- Parámetros clave:
host(IP),port(número de puerto),timeout(segundos). - Devuelve: Ninguno (genera un
RuntimeErrorsi se agota el tiempo de espera). - Detalles: Sondea cada
WAIT_STEP_SECONDShasta que se utiliza el puerto o expira eltimeout.
-
fetch_tor_exit_ip:- Propósito: Recupera la IP del nodo de salida de Tor a través de servicios externos.
- Parámetros clave:
host_port(puerto del proxy),timeout(tiempo de espera de la solicitud, por defecto 15 s). - Devuelve: Cadena (IP del nodo de salida).
- Detalles: Consulta servicios de eco de IP (por ejemplo,
checkip.amazonaws.com) a través del proxy SOCKS5; genera unRuntimeErrorsi todos fallan.
-
create_and_start_proxy:- Propósito: Crea e inicia un contenedor de proxy Tor, devolviendo sus detalles.
- Parámetros clave: Ninguno.
- Devuelve: Diccionario con
container_name,container_ip(con puerto),tor_exit_nodeytimestamp. - Detalles:
- Comprueba la disponibilidad de Docker y Compose; sale si no están disponibles.
- Genera un nombre y un puerto aleatorios para el contenedor.
- Crea un directorio temporal con
Dockerfile,torrcycompose.yaml. - Ejecuta
docker compose up --build -dpara iniciar el contenedor. - Espera a que se abra el puerto, recupera la IP del contenedor mediante
docker inspecty obtiene la IP de salida de Tor. - Limpia el contenedor en caso de fallo utilizando
docker rm -f.
En mi máquina, Docker requiere sudo para obtener permisos de root. Al ejecutar tor_proxy_gen.py con sudo python3 tor_proxy_gen.py, se utiliza el intérprete de Python del sistema, que omite el entorno virtual que contiene las dependencias del proyecto tornet_scraper.
En su lugar, ejecute el script de la siguiente manera para utilizar el intérprete de Python del entorno virtual:
-> % 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
}
Esto garantiza que el script utilice el intérprete Python del entorno virtual tornet_scraper con todas las dependencias necesarias instaladas.
Comprobación del estado del proxy
El script container_status.py proporciona funciones útiles para comprobar el estado de los contenedores Docker. A continuación se ofrece una explicación concisa de sus funciones.
-
cmd_exists:- Propósito: comprueba si un ejecutable especificado está disponible en el
$PATHdel sistema. - Parámetros clave:
executable: nombre de cadena del comando (por ejemplo,docker).
- Devuelve: Booleano (
Truesi se encuentra el ejecutable,Falseen caso contrario). - Detalles: Utiliza
shutil.whichpara verificar si el ejecutable existe y es accesible en el$PATHdel sistema.
- Propósito: comprueba si un ejecutable especificado está disponible en el
-
container_running:- Propósito: Determina si un contenedor Docker se está ejecutando.
- Parámetros clave:
name: Nombre de cadena del contenedor Docker.
- Devuelve: Booleano (
Truesi el contenedor existe y se está ejecutando,Falseen caso contrario). - Detalles: ejecuta
sudo docker inspect -f {{.State.Running}} <name>para comprobar el estado de ejecución del contenedor. DevuelveTruesi la salida es«true»,Falsesi el comando falla (por ejemplo, el contenedor no existe) o el estado no es de ejecución. Suprime stderr conDEVNULLpara evitar la salida de errores.
Eliminación de proxies
El script rm_container.py proporciona una función para eliminar de forma forzada un contenedor Docker. A continuación se ofrece una explicación concisa de su única función.
-
delete_container:- Propósito: Elimina un contenedor Docker especificado utilizando
sudo docker rm -f. - Parámetros clave:
name: nombre de cadena del contenedor Docker que se va a eliminar.- Devuelve: booleano (
Truesi la eliminación se realiza correctamente,Falsesi falla). - Detalles: ejecuta
sudo docker rm -f <nombre>para eliminar de forma forzada el contenedor, suprimiendo tanto stdout como stderr conDEVNULL. DevuelveTruesi el comando se ejecuta correctamente, oFalsesi se produce unCalledProcessError(por ejemplo, el contenedor no existe o hay problemas de permisos).
- Propósito: Elimina un contenedor Docker especificado utilizando
Modelos de base de datos
Así es como se ven los modelos de tu base de datos:
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)
Para algunos de vosotros, datetime.utcnow puede parecer obsoleto, pero no tenéis que preocuparos por eso.
Creación del backend
Los módulos de servicios realizan todo el trabajo pesado, pero aún necesitamos un enrutador backend para llamar a esas funciones y gestionar las solicitudes del frontend. El backend para la generación de proxies se encuentra dentro de app/routes/proxy_gen.py.
El script proxy_gen.py define las rutas FastAPI para crear, eliminar y listar contenedores proxy Tor, interactuando con la base de datos y los servicios externos. A continuación se ofrece una explicación concisa de sus componentes y funciones.
Variables globales
logger:- Finalidad: configura el registro para la depuración y el seguimiento de errores.
- Detalles: utiliza el módulo
loggingcon el nivelINFOpara registrar operaciones y errores.
proxy_gen_router:- Propósito: Enrutador FastAPI para puntos finales relacionados con el proxy.
- Detalles: Configurado con el prefijo
/api/proxy-geny las etiquetas[«API», «Proxy Generator»]para su organización.
Funciones
-
create_proxy:- Propósito: Crea un nuevo contenedor de proxy Tor y almacena sus detalles en la base de datos.
- Parámetros clave:
request: ObjetoRequestde FastAPI para la gestión de sesiones.db:Sessionde SQLAlchemy para operaciones de base de datos (a través deDepends(get_db)).
- Devuelve:
JSONResponsecon el estado de éxito, el mensaje y los detalles del proxy (nombre del contenedor, IP, nodo de salida de Tor, marca de tiempo, estado de ejecución). - Detalles: Llama a
create_and_start_proxy(desdetor_proxy_gen.py) para iniciar un contenedor, crea una instancia del modeloProxy, la guarda en la base de datos y devuelve una respuesta JSON. Genera una excepciónHTTPException(500) en caso de error y registra el problema.
-
delete_proxy:- Propósito: Elimina un contenedor proxy Tor especificado y su registro en la base de datos.
- Parámetros clave:
container_name: Nombre de cadena del contenedor que se va a eliminar.request: ObjetoRequestde FastAPI para mensajes flash basados en sesiones.db: SQLAlchemySessionpara operaciones de base de datos.
- Devuelve:
JSONResponsecon el estado de éxito y un mensaje si la eliminación se ha realizado correctamente. - Detalles: Consulta la tabla
Proxyen busca del contenedor; si lo encuentra, llama adelete_container(desderm_container.py) para eliminarlo. Elimina el registro de la base de datos si se realiza correctamente y añade un mensaje flash de éxito a la sesión. Genera una excepciónHTTPException(404 si no se encuentra, 500 si se produce un error) y añade mensajes flash de error.
-
list_proxies:- Propósito: Recupera una lista de todos los proxies con el estado de ejecución actualizado.
- Parámetros clave:
db:Sessionde SQLAlchemy para operaciones de base de datos.
- Devuelve:
JSONResponsecon una lista de proxies, cada uno con el nombre del contenedor, la IP, el nodo de salida de Tor, la marca de tiempo y el estado de ejecución. - Detalles: Consulta todos los registros
Proxy, comprueba el estado de ejecución de cada contenedor utilizandocontainer_running(decontainer_status.py), actualiza el camporunningen la base de datos si es necesario y devuelve la lista en formato JSON. Genera una excepciónHTTPException(500) en caso de error y registra el problema.
Creación del frontend
El código de la plantilla se encuentra en app/templates/proxy_gen.html.
La plantilla proxy_gen.html amplía base.html para proporcionar una interfaz de usuario para gestionar los contenedores proxy Tor en la aplicación tornet_scraper, interactuando con el backend a través de llamadas API. A continuación se ofrece una explicación concisa de sus funciones principales y su interacción con el backend.
-
Herencia de plantillas:
- Propósito: Aprovecha el diseño de
base.htmlpara lograr una estructura coherente. - Interacción con el backend: Hereda la barra de navegación y el manejo de mensajes flash de
base.html. El{% block title %}establece el título de la página como «Proxy Generator», y{% block content %}define la funcionalidad específica de la página. Los mensajes flash del backend (almacenados en la sesión) se muestran en el contenedor heredado.
- Propósito: Aprovecha el diseño de
-
Creación de proxy:
- Propósito: Inicia y confirma la creación de un nuevo contenedor de proxy Tor.
- Interacción con el backend:
- El botón «Crear nuevo proxy» activa la función JavaScript
createProxy(), que abre un modal de confirmación. - El botón «Continuar» del modal llama a
confirmCreateProxy(), que envía una solicitud AJAX POST a/api/proxy-gen/create(gestionada porproxy_gen.py::create_proxy). - El backend crea un contenedor Docker (a través de
tor_proxy_gen.py), guarda los detalles del proxy (nombre del contenedor, IP, nodo de salida de Tor, marca de tiempo, estado de ejecución) en la tablaProxyde la base de datos y devuelve una respuesta JSON. - Si se realiza correctamente, la tabla se actualiza a través de
updateProxyTable(). Los errores activan los registros de la consola y vuelven a habilitar el botón.
- El botón «Crear nuevo proxy» activa la función JavaScript
-
Visualización y actualizaciones de la tabla de proxies:
- Propósito: muestra una lista dinámica de proxies con actualizaciones de estado en tiempo real.
- Interacción con el backend:
- Al cargar la página, Jinja2 renderiza los datos iniciales del proxy (
proxiesdemain.py::proxy_gen) en una tabla con columnas para el nombre del contenedor, la IP, el nodo de salida de Tor, la marca de tiempo y el estado. - La función
updateProxyTable()se ejecuta cada 10 segundos (a través desetInterval) y al crear o eliminar, envía una solicitud AJAX GET a/api/proxy-gen/list(gestionada porproxy_gen.py::list_proxies). - El backend consulta la tabla
Proxy, comprueba el estado de ejecución de cada contenedor (a través decontainer_status.py::container_running), actualiza la base de datos si es necesario y devuelve una lista JSON de proxies. - La tabla se borra y se vuelve a rellenar con los datos más recientes, mostrando el estado como «En ejecución» o «No en ejecución» con insignias estilizadas.
- Al cargar la página, Jinja2 renderiza los datos iniciales del proxy (
-
Eliminación de proxies:
- Objetivo: permite a los usuarios eliminar un contenedor proxy.
- Interacción con el backend:
- Cada fila de la tabla tiene un botón «Eliminar» que llama a
openDeleteModal(containerName)para abrir un modal de confirmación con el nombre del contenedor almacenado en un campo oculto. - El botón «Eliminar» del modal activa
confirmDeleteProxy(), que envía una solicitud AJAX DELETE a/api/proxy-gen/delete/{container_name}(gestionada porproxy_gen.py::delete_proxy). - El backend verifica que el proxy existe en la tabla
Proxy, elimina el contenedor (a través derm_container.py::delete_container), elimina el registro de la base de datos y añade un mensaje flash de éxito a la sesión. - Si se realiza correctamente, se muestra un mensaje flash de éxito (a través de
showFlashMessage) y la tabla se actualiza. Los errores activan un mensaje flash de error.
- Cada fila de la tabla tiene un botón «Eliminar» que llama a
Pruebas
Ejecuta la aplicación web:
sudo /home/hamy/tornet_scraper/venv/bin/python3 -m uvicorn app.main:app --reload
Abre la aplicación web:
http://127.0.0.1:8000/proxy-gen
Crea un proxy:
