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:
- Gerador de proxy Tor
- Geração automatizada de proxy Tor
- Criar proxies
- Verificar o estado do proxy
- Eliminar proxies
- Modelos de base de dados
- Criar backend
- Criar frontend
- 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:
- Uma função para criar um contentor Docker para executar um proxy Tor
- Uma função para verificar o estado de um contentor Docker para verificar se o proxy está ativo
- 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:
tor_proxy_gen.py: Lida com a criação de contentores Docker para proxies Torcontainer_status.py: Verifica o estado dos contentores Dockerrm_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:
DOCKERFILEconfigura uma imagem baseada em Debian com Tor;TORRCconfigura a porta SOCKS do Tor;COMPOSE_TEMPLATEdefine 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 (
Truese encontrado). - Detalhes: Utiliza
shutil.whiche regista o resultado.
- Objetivo: Verifica se um executável (por exemplo,
-
docker_compose_available:- Objetivo: Verifica se o Docker Compose está disponível.
- Parâmetros-chave: Nenhum.
- Retorna: Booleano (
Truesedocker compose versionfor 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.1está livre. - Parâmetros-chave:
port(número da porta). - Retorna: Booleano (
Truese a porta estiver livre). - Detalhes: Tenta conectar-se à porta usando um socket com um tempo limite de 0,5 s.
- Objetivo: Verifica se uma porta TCP em
-
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_ATTEMPTSportas aleatórias; chamadiese 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
RuntimeErrorao expirar o tempo limite). - Detalhes: Verifica a cada
WAIT_STEP_SECONDSaté que a porta seja usada ou otimeoutexpire.
-
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; geraRuntimeErrorse 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_nodeetimestamp. - 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,torrcecompose.yaml. - Executa
docker compose up --build -dpara iniciar o contentor. - Aguarda a abertura da porta, recupera o IP do contentor através de
docker inspecte 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.
-
cmd_exists:- Objetivo: Verifica se um executável especificado está disponível no
$PATHdo sistema. - Parâmetros principais:
executable: Nome da string do comando (por exemplo,docker).
- Retorna: Booleano (
Truese o executável for encontrado,Falsecaso contrário). - Detalhes: Usa
shutil.whichpara verificar se o executável existe e está acessível no$PATHdo sistema.
- Objetivo: Verifica se um executável especificado está disponível no
-
container_running:- Objetivo: Determina se um contentor Docker está em execução.
- Parâmetros-chave:
name: Nome em cadeia do contentor Docker.
- Retorna: Booleano (
Truese o contentor existe e está em execução,Falsecaso contrário). - Detalhes: Executa
sudo docker inspect -f {{.State.Running}} <name>para verificar o estado de execução do contentor. RetornaTruese a saída for“true”,Falsese o comando falhar (por exemplo, o contentor não existe) ou se o estado não estiver em execução. Suprime stderr comDEVNULLpara 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.
-
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 (
Truese a remoção for bem-sucedida,Falsese falhar). - Detalhes: Executa
sudo docker rm -f <nome>para remover à força o contentor, suprimindo tanto o stdout quanto o stderr comDEVNULL. RetornaTruese o comando for bem-sucedido ouFalsese ocorrer umCalledProcessError(por exemplo, o contentor não existe ou há problemas de permissão).
- Objetivo: Elimina um contentor Docker especificado usando
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
logger:- Finalidade: Configura o registo para depuração e rastreamento de erros.
- Detalhes: Utiliza o módulo
loggingcom o nívelINFOpara registar operações e erros.
proxy_gen_router:- Objetivo: Roteador FastAPI para pontos finais relacionados com proxies.
- Detalhes: Configurado com o prefixo
/api/proxy-gene as tags[“API”, “Proxy Generator”]para organização.
Funções
-
create_proxy:- Objetivo: Cria um novo contentor proxy Tor e armazena os seus detalhes na base de dados.
- Parâmetros principais:
request: ObjetoRequestFastAPI para gestão de sessões.db:SessionSQLAlchemy para operações de base de dados (através deDepends(get_db)).
- Retorna:
JSONResponsecom 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(detor_proxy_gen.py) para iniciar um contentor, cria uma instância do modeloProxy, salva-a no banco de dados e retorna uma resposta JSON. GeraHTTPException(500) em caso de erros, registrando o problema.
-
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: objetoRequestdo FastAPI para mensagens flash baseadas em sessão.db: SQLAlchemySessionpara operações de base de dados.
- Retorna:
JSONResponsecom status de sucesso e mensagem em caso de exclusão bem-sucedida. - Detalhes: Consulta a tabela
Proxypara o contentor; se encontrado, chamadelete_container(derm_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. LevantaHTTPException(404 se não encontrado, 500 em caso de erros) e adiciona mensagens flash de erro.
-
list_proxies:- Objetivo: Recupera uma lista de todos os proxies com status de execução atualizado.
- Parâmetros-chave:
db: SQLAlchemySessionpara operações de base de dados.
- Retorna:
JSONResponsecom 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 utilizandocontainer_running(decontainer_status.py), atualiza o camporunningna base de dados, se necessário, e devolve a lista como JSON. LevantaHTTPException(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.
-
Herança de modelo:
- Objetivo: aproveita o layout
base.htmlpara 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.
- Objetivo: aproveita o layout
-
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 porproxy_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 tabelaProxyna 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.
- Um botão “Criar novo proxy” aciona a função JavaScript
-
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 (
proxiesdemain.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 desetInterval) e na criação/eliminação, enviando um pedido AJAX GET para/api/proxy-gen/list(tratado porproxy_gen.py::list_proxies). - O backend consulta a tabela
Proxy, verifica o estado de execução de cada contentor (através decontainer_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.
- Ao carregar a página, o Jinja2 renderiza os dados iniciais do proxy (
-
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 porproxy_gen.py::delete_proxy). - O backend verifica se o proxy existe na tabela
Proxy, elimina o contentor (através derm_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.
- Cada linha da tabela tem um botão «Eliminar» que chama
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:
