Nesta secção, explicarei como criar um gerador de proxy Tor e implementar uma funcionalidade de aplicação web para acompanhar os proxies e geri-los.

Os tópicos desta secção incluem o seguinte:

  1. Gerador de proxy Tor
  2. Geração automatizada de proxy Tor
  3. Criar proxies
  4. Verificar o estado do proxy
  5. Eliminar proxies
  6. Modelos de base de dados
  7. Criar backend
  8. Criar frontend
  9. Testar

Gerador de proxy Tor

O seguinte repositório GitHub contém o código para um gerador de proxy Tor:

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

Para cada proxy, você precisará de um contentor Docker separado, ou pelo menos é isso que estou a fazer. Você também pode comprar centenas de proxies por um preço baixo, mas não custa nada aprender a configurá-los você mesmo.

Se você olhar o ficheiro docker-compose.yaml nesse repositório, verá que estamos a encaminhar todo o tráfego pela porta 9050 dentro do docker para a rede do host pela porta 9050:

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

Se quiser aceder ao proxy através de uma porta diferente da sua rede host, pode alterar as portas da seguinte forma:

3531:9050

A primeira porta é a porta que se abre no seu sistema host e a segunda porta é a que está a ser executada no Docker.

O ficheiro torrc utiliza a porta 9050 para o serviço tor:

SocksPort 0.0.0.0:9050
Log notice stdout

O Dockerfile está a executar uma versão reduzida do sistema operativo Debian para executar o 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"]

Quando se trata de criar uma aplicação que gera vários proxies tor, não podemos confiar apenas na porta 9050, pois ela é muito utilizada e não podemos usá-la se já estiver a ser utilizada por outra aplicação. Por esse motivo, vamos criar um script python escolhendo uma porta aleatória entre 40.000 e 60.000. Existem apenas 65.535 portas em qualquer computador, então o motivo para essa faixa específica de portas é que elas geralmente não são usadas, pelo que eu vi, e também não requerem permissão de root para serem usadas, pelo menos no Ubuntu.


Geração automatizada de proxy Tor

Como geramos proxies Tor usando o Docker, precisamos implementar três funcionalidades principais:

  1. Uma função para criar um contentor Docker para executar um proxy Tor
  2. Uma função para verificar o estado de um contentor Docker para verificar se o proxy está ativo
  3. Uma função para encerrar e remover um contentor Docker

Os ficheiros relevantes estão localizados em app/services/*.py. Os ficheiros específicos que utilizaremos são:

  1. tor_proxy_gen.py: Lida com a criação de contentores Docker para proxies Tor
  2. container_status.py: Verifica o estado dos contentores Docker
  3. rm_container.py: Gerencia a exclusão de contentores Docker

Criando proxies

O script tor_proxy_gen.py automatiza a criação de contentores Docker executando proxies Tor, fornecendo um proxy SOCKS5 para acesso anónimo à rede. Abaixo está uma explicação concisa de seus componentes e funcionalidades.

Objetivo:

  • Cria e inicia um contentor Docker a executar um proxy Tor, atribui uma porta aleatória, recupera o IP do contentor e o IP do nó de saída Tor e retorna os detalhes como um dicionário pronto para JSON.

Variáveis globais:

  • PORT_MIN, PORT_MAX:
    • Objetivo: Define o intervalo (40001–60001) para selecionar portas de host aleatórias.
    • Detalhes: Garante a atribuição de uma porta exclusiva para cada contentor.
  • SOCKS_PORT_IN_CONTAINER:
    • Objetivo: Define a porta SOCKS do Tor dentro do contentor (9050).
    • Detalhes: Porta padrão para o proxy SOCKS5 do Tor.
  • MAX_PORT_ATTEMPTS:
    • Objetivo: Limita as tentativas de encontrar uma porta livre (100).
    • Detalhes: Impede loops infinitos na seleção de portas.
  • WAIT_MAX_SECONDS, WAIT_STEP_SECONDS:
    • Objetivo: Controla o tempo limite (60s) e o intervalo de pesquisa (2s) para a disponibilidade da porta.
    • Detalhes: Usado ao aguardar a abertura da porta do contentor.
  • DOCKERFILE, TORRC, COMPOSE_TEMPLATE:
    • Objetivo: Define os ficheiros de configuração do Docker como strings.
    • Detalhes: DOCKERFILE configura uma imagem baseada em Debian com Tor; TORRC configura a porta SOCKS do Tor; COMPOSE_TEMPLATE define as configurações do Docker Compose para o contentor.

Funções:

  • die:

    • Objetivo: Regista uma mensagem de erro e sai do programa.
    • Parâmetros principais: msg (mensagem de erro), code (código de saída, padrão 1).
    • Retorna: Nenhum (sai do programa).
    • Detalhes: Envia o erro para stderr e regista-o através de logging.
  • cmd_exists:

    • Objetivo: Verifica se um executável (por exemplo, docker) está disponível em $PATH.
    • Parâmetros principais: executable (nome do comando).
    • Retorna: Booleano (True se encontrado).
    • Detalhes: Utiliza shutil.which e regista o resultado.
  • docker_compose_available:

    • Objetivo: Verifica se o Docker Compose está disponível.
    • Parâmetros-chave: Nenhum.
    • Retorna: Booleano (True se docker compose version for bem-sucedido).
    • Detalhes: Executa um comando para verificar o Docker Compose e regista a saída ou os erros.
  • is_port_free:

    • Objetivo: Verifica se uma porta TCP em 127.0.0.1 está livre.
    • Parâmetros-chave: port (número da porta).
    • Retorna: Booleano (True se a porta estiver livre).
    • Detalhes: Tenta conectar-se à porta usando um socket com um tempo limite de 0,5 s.
  • random_free_port:

    • Objetivo: Encontra uma porta aleatória e não utilizada no intervalo definido.
    • Parâmetros-chave: Nenhum.
    • Retorna: Número inteiro (número da porta livre).
    • Detalhes: Tenta até MAX_PORT_ATTEMPTS portas aleatórias; chama die se nenhuma estiver livre.
  • random_container_name:

    • Objetivo: Gera um nome de contêiner exclusivo.
    • Parâmetros principais: Nenhum.
    • Retorna: String (por exemplo, torproxy_abcdef).
    • Detalhes: Combina torproxy_ com seis letras minúsculas aleatórias.
  • wait_for_port:

    • Objetivo: Aguarda até que uma porta num host seja aberta ou atinja o tempo limite.
    • Parâmetros principais: host (IP), port (número da porta), timeout (segundos).
    • Retorna: Nenhum (gera RuntimeError ao expirar o tempo limite).
    • Detalhes: Verifica a cada WAIT_STEP_SECONDS até que a porta seja usada ou o timeout expire.
  • fetch_tor_exit_ip:

    • Objetivo: Recupera o IP do nó de saída do Tor através de serviços externos.
    • Parâmetros principais: host_port (porta do proxy), timeout (tempo limite da solicitação, padrão 15s).
    • Retorna: String (IP do nó de saída).
    • Detalhes: Consulta serviços de eco de IP (por exemplo, checkip.amazonaws.com) através do proxy SOCKS5; gera RuntimeError se todos falharem.
  • create_and_start_proxy:

    • Objetivo: Cria e inicia um contentor proxy Tor, retornando os seus detalhes.
    • Parâmetros-chave: Nenhum.
    • Retorna: Dicionário com container_name, container_ip (com porta), tor_exit_node e timestamp.
    • Detalhes:
      • Verifica a disponibilidade do Docker e do Compose; sai se estiverem ausentes.
      • Gera um nome e uma porta aleatórios para o contentor.
      • Cria um diretório temporário com Dockerfile, torrc e compose.yaml.
      • Executa docker compose up --build -d para iniciar o contentor.
      • Aguarda a abertura da porta, recupera o IP do contentor através de docker inspect e obtém o IP de saída do Tor.
      • Limpa o contentor em caso de falha usando docker rm -f.

Na minha máquina, o Docker requer sudo para permissões de root. Executar tor_proxy_gen.py com sudo python3 tor_proxy_gen.py usa o interpretador Python de todo o sistema, que ignora o ambiente virtual que contém as dependências do projeto tornet_scraper.

Em vez disso, execute o script da seguinte forma para usar o interpretador Python do ambiente 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
}

Isso garante que o script use o interpretador Python do ambiente virtual tornet_scraper com todas as dependências necessárias instaladas.

Verificar o estado do proxy

O script container_status.py fornece funções utilitárias para verificar o estado dos contentores Docker. Abaixo está uma explicação concisa das suas funções.

  1. cmd_exists:

    • Objetivo: Verifica se um executável especificado está disponível no $PATH do sistema.
    • Parâmetros principais:
      • executable: Nome da string do comando (por exemplo, docker).
    • Retorna: Booleano (True se o executável for encontrado, False caso contrário).
    • Detalhes: Usa shutil.which para verificar se o executável existe e está acessível no $PATH do sistema.
  2. container_running:

    • Objetivo: Determina se um contentor Docker está em execução.
    • Parâmetros-chave:
      • name: Nome em cadeia do contentor Docker.
    • Retorna: Booleano (True se o contentor existe e está em execução, False caso contrário).
    • Detalhes: Executa sudo docker inspect -f {{.State.Running}} <name> para verificar o estado de execução do contentor. Retorna True se a saída for “true”, False se o comando falhar (por exemplo, o contentor não existe) ou se o estado não estiver em execução. Suprime stderr com DEVNULL para evitar a saída de erros.

Eliminar proxies

O script rm_container.py fornece uma função para remover à força um contentor Docker. Abaixo está uma explicação concisa da sua única função.

  1. delete_container:

    • Objetivo: Elimina um contentor Docker especificado usando sudo docker rm -f.
    • Parâmetros principais:
      • name: Nome da string do contentor Docker a ser removido.
    • Retorna: Booleano (True se a remoção for bem-sucedida, False se falhar).
    • Detalhes: Executa sudo docker rm -f <nome> para remover à força o contentor, suprimindo tanto o stdout quanto o stderr com DEVNULL. Retorna True se o comando for bem-sucedido ou False se ocorrer um CalledProcessError (por exemplo, o contentor não existe ou há problemas de permissão).

Modelos de base de dados

É assim que os seus modelos de base de dados se apresentam:

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 alguns de vocês, datetime.utcnow pode parecer obsoleto, mas não precisam de se preocupar com isso.


Criar backend

Os módulos de serviços fazem todo o trabalho pesado, mas ainda precisamos de um router backend para chamar essas funções e lidar com as solicitações do frontend. O seu backend para geração de proxy está dentro de app/routes/proxy_gen.py.

O script proxy_gen.py define as rotas FastAPI para criar, eliminar e listar contentores proxy Tor, interagindo com a base de dados e serviços externos. Abaixo está uma explicação concisa dos seus componentes e funções.

Variáveis globais

  1. logger:
    • Finalidade: Configura o registo para depuração e rastreamento de erros.
    • Detalhes: Utiliza o módulo logging com o nível INFO para registar operações e erros.
  2. proxy_gen_router:
    • Objetivo: Roteador FastAPI para pontos finais relacionados com proxies.
    • Detalhes: Configurado com o prefixo /api/proxy-gen e as tags [“API”, “Proxy Generator”] para organização.

Funções

  1. create_proxy:

    • Objetivo: Cria um novo contentor proxy Tor e armazena os seus detalhes na base de dados.
    • Parâmetros principais:
      • request: Objeto Request FastAPI para gestão de sessões.
      • db: Session SQLAlchemy para operações de base de dados (através de Depends(get_db)).
    • Retorna: JSONResponse com status de sucesso, mensagem e detalhes do proxy (nome do contentor, IP, nó de saída do Tor, carimbo de data/hora, status de execução).
    • Detalhes: Chama create_and_start_proxy (de tor_proxy_gen.py) para iniciar um contentor, cria uma instância do modelo Proxy, salva-a no banco de dados e retorna uma resposta JSON. Gera HTTPException (500) em caso de erros, registrando o problema.
  2. delete_proxy:

    • Objetivo: exclui um contentor proxy Tor especificado e seu registro no banco de dados.
    • Parâmetros principais:
      • container_name: nome em string do contentor a ser excluído.
      • request: objeto Request do FastAPI para mensagens flash baseadas em sessão.
      • db: SQLAlchemy Session para operações de base de dados.
    • Retorna: JSONResponse com status de sucesso e mensagem em caso de exclusão bem-sucedida.
    • Detalhes: Consulta a tabela Proxy para o contentor; se encontrado, chama delete_container (de rm_container.py) para removê-lo. Exclui o registro do banco de dados em caso de sucesso e adiciona uma mensagem flash de sucesso à sessão. Levanta HTTPException (404 se não encontrado, 500 em caso de erros) e adiciona mensagens flash de erro.
  3. list_proxies:

    • Objetivo: Recupera uma lista de todos os proxies com status de execução atualizado.
    • Parâmetros-chave:
      • db: SQLAlchemy Session para operações de base de dados.
    • Retorna: JSONResponse com uma lista de proxies, cada um contendo o nome do contentor, IP, nó de saída Tor, carimbo de data/hora e estado de execução.
    • Detalhes: consulta todos os registos Proxy, verifica o estado de execução de cada contentor utilizando container_running (de container_status.py), atualiza o campo running na base de dados, se necessário, e devolve a lista como JSON. Levanta HTTPException (500) em caso de erros, registando o problema.

Criando o frontend

O código do seu modelo está localizado dentro de app/templates/proxy_gen.html.

O modelo proxy_gen.html estende base.html para fornecer uma interface de usuário para gerenciar contêineres proxy Tor no aplicativo tornet_scraper, interagindo com o backend por meio de chamadas de API. Abaixo está uma explicação concisa de suas principais funcionalidades e sua interação com o backend.

  1. Herança de modelo:

    • Objetivo: aproveita o layout base.html para obter uma estrutura consistente.
    • Interação com o backend: herda a barra de navegação e o tratamento de mensagens flash do base.html. O {% block title %} define o título da página como “Proxy Generator” e o {% block content %} define a funcionalidade específica da página. As mensagens flash do backend (armazenadas na sessão) são exibidas no contentor herdado.
  2. Criação de proxy:

    • Objetivo: Inicia e confirma a criação de um novo contentor de proxy Tor.
    • Interação com o backend:
      • Um botão “Criar novo proxy” aciona a função JavaScript createProxy(), abrindo um modal de confirmação.
      • O botão “Continuar” do modal chama confirmCreateProxy(), enviando uma solicitação AJAX POST para /api/proxy-gen/create (tratada por proxy_gen.py::create_proxy).
      • O backend cria um contentor Docker (através de tor_proxy_gen.py), guarda os detalhes do proxy (nome do contentor, IP, nó de saída Tor, carimbo de data/hora, estado de execução) na tabela Proxy na base de dados e devolve uma resposta JSON.
      • Em caso de sucesso, a tabela é atualizada através de updateProxyTable(). Os erros acionam registos na consola e reativam o botão.
  3. Exibição e atualizações da tabela de proxies:

    • Objetivo: Exibe uma lista dinâmica de proxies com atualizações de status em tempo real.
    • Interação com o backend:
      • Ao carregar a página, o Jinja2 renderiza os dados iniciais do proxy (proxies de main.py::proxy_gen) em uma tabela com colunas para nome do contentor, IP, nó de saída do Tor, carimbo de data/hora e status.
      • A função updateProxyTable() é executada a cada 10 segundos (através de setInterval) e na criação/eliminação, enviando um pedido AJAX GET para /api/proxy-gen/list (tratado por proxy_gen.py::list_proxies).
      • O backend consulta a tabela Proxy, verifica o estado de execução de cada contentor (através de container_status.py::container_running), atualiza a base de dados, se necessário, e devolve uma lista JSON de proxies.
      • A tabela é limpa e repovoada com os dados mais recentes, mostrando o estado como «Em execução» ou «Não em execução» com emblemas estilizados.
  4. Eliminação do proxy:

    • Objetivo: permite aos utilizadores eliminar um contentor proxy.
    • Interação do backend:
      • Cada linha da tabela tem um botão «Eliminar» que chama openDeleteModal(containerName) para abrir um modal de confirmação com o nome do contentor armazenado numa entrada oculta.
      • O botão «Eliminar» do modal aciona confirmDeleteProxy(), enviando um pedido AJAX DELETE para /api/proxy-gen/delete/{container_name} (tratado por proxy_gen.py::delete_proxy).
      • O backend verifica se o proxy existe na tabela Proxy, elimina o contentor (através de rm_container.py::delete_container), remove o registo da base de dados e adiciona uma mensagem flash de sucesso à sessão.
      • Em caso de sucesso, é apresentada uma mensagem flash de sucesso (através de showFlashMessage) e a tabela é atualizada. Os erros acionam uma mensagem flash de erro.

Testes

Execute a aplicação web:

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

Abra a aplicação web:

http://127.0.0.1:8000/proxy-gen

Crie um proxy:

Página do proxy do Tornet Scraper