Existe um tipo de contribuição técnica que quase ninguém enxerga, mas que sustenta o acesso à internet livre de milhares de pessoas todos os dias: doar um pedaço de um servidor para rodar uma bridge da rede Tor. Diferente de um relé comum, a bridge é um ponto de entrada não listado publicamente, existindo especificamente para permitir que usuários sob censura alcancem a rede Tor sem que o provedor de internet local perceba.

Este artigo documenta o processo completo: a fundamentação técnica do que é uma bridge e por que ela importa, os critérios de escolha da VPS, a implantação da stack em Docker (bridge obfs4, monitoramento e alertas) e o hardening do host. Ao final, também é discutido o principal ponto de falha encontrado na prática, a interação entre Docker e UFW, que silenciosamente invalida boa parte das regras de firewall de uma VPS.

Conceitos

Tipos de nó na rede Tor

A rede Tor é sustentada por voluntários que operam três tipos de nó, com propósitos distintos. O relé intermediário repassa tráfego já criptografado entre outros nós da rede, sendo público e listado abertamente no consenso. O relé de saída é o último salto, no qual o tráfego deixa a rede em direção à internet aberta usando o IP do operador, é o tipo mais necessário, porém o mais sujeito a reclamações de abuso e a implicações jurídicas. A bridge, por sua vez, é um ponto de entrada deliberadamente ausente do consenso público.

A distinção é crucial: relés comuns constam de uma lista pública que qualquer governo pode baixar e bloquear integralmente em uma única operação. A bridge não está nessa lista. Um censor não consegue bloquear aquilo que não consegue enumerar, e é exatamente aí que reside sua função.

obfs4 e Pluggable Transports

Não basta que o endereço da bridge seja secreto: o tráfego Tor possui uma assinatura reconhecível no handshake TLS, o que permite que sistemas de Deep Packet Inspection (DPI) identifiquem e bloqueiem a conexão mesmo sem conhecer o IP de destino. Os Pluggable Transports resolvem esse problema ao encapsular o tráfego em uma camada de ofuscação.

O obfs4 é o transporte padrão para bridges. Ele aplica um handshake autenticado por uma chave que só o cliente que possui a bridge line conhece, transformando todo o fluxo em ruído aparentemente aleatório, sem cabeçalhos fixos, sem padrão de tamanho de pacote reconhecível e sem resposta a sondagens de quem não possui a chave. Essa última propriedade é o que confere resistência ao active probing: um censor que se conecte à porta obfs4 sem a chave correta não recebe nada que confirme tratar-se de uma bridge Tor.

Distribuição e ciclo de vida de uma bridge

Uma bridge recém-criada não é entregue diretamente aos usuários. Ela é registrada junto à Bridge Authority e passa a ser distribuída pelo Rdsys, o sistema de distribuição do Tor Project, que a disponibiliza por canais controlados, o BridgeDB via HTTPS, e-mail e Telegram, ou embarcada no Tor Browser. A distribuição é fragmentada de propósito: nenhum canal entrega o conjunto completo de bridges, justamente para impedir que um censor enumere todas de uma vez.

Bridges não são permanentes. Países com capacidade de probing ativo em larga escala conseguem identificar e bloquear bridges ao longo do tempo, mesmo com ofuscação. Isso cria uma dinâmica de reposição contínua: a rede depende de um fluxo constante de bridges novas, ainda ausentes das listas de bloqueio, substituindo aquelas que vão sendo queimadas. O valor de uma bridge nova está precisamente nesse ponto.

Relevância operacional

Os números sustentam o argumento. Rússia e Irã são consistentemente os países com maior volume de conexões via bridge, na casa de dezenas de milhares de usuários diários somados, com picos que se multiplicam por dezenas de vezes durante períodos de repressão mais intensa. No Irã, conexões diretas ao Tor estão bloqueadas há anos; para praticamente todo usuário iraniano, a bridge não é uma alternativa - é o único caminho de entrada existente. A rede opera com algo entre 1.800 e 2.200 bridges ativas em um dado momento, um número modesto frente à demanda.


Por que não rodar em rede doméstica

A tentação de economizar e rodar a bridge em um computador de casa existe, mas esbarra em limitações técnicas que uma VPS elimina de imediato.

CGNAT

Boa parte das conexões residenciais, sobretudo móveis e parte das conexões via cabo, opera atrás de Carrier-Grade NAT, o que significa que o assinante sequer possui um IP público próprio. Sem IP público, não há como a Bridge Authority alcançar a ORPort para validar a alcançabilidade da bridge. Trata-se de um impedimento arquitetural, não de configuração.

IP dinâmico

Mesmo sem CGNAT, provedores residenciais rotacionam o IP do assinante periodicamente. Cada troca rompe a continuidade da bridge, que precisa reconstruir reputação do zero, e os mecanismos de distribuição do Rdsys favorecem bridges com uptime estável e endereço consistente.

Banda compartilhada

A bridge disputa upload com todo o consumo doméstico: streaming, videochamadas, jogos. Em uma VPS, a banda é dedicada e previsível.

Termos de serviço

Diversos provedores residenciais proíbem contratualmente a operação de servidores, ainda que não fiscalizem de forma ativa.

Exposição do IP residencial

Embora a bridge não exponha o operador aos usuários finais, esse é justamente o propósito do desenho - ela permanece exposta a qualquer sistema de varredura da internet, incluindo os de países que realizam probing ativo. É preferível que esse endereço seja o de uma VPS descartável, e não o da residência do operador.


Escolha da VPS

Os requisitos oficiais do Tor Project para uma bridge são modestos: banda mínima de 1 Mbit/s (com faixa ideal entre 10 e 16 Mbit/s), tráfego mensal mínimo de 100 GB, recomendando-se 2 TB ou mais, e nenhuma exigência relevante de CPU ou RAM. Isso viabiliza planos de entrada de baixo custo.

Hetzner (CX22)

2 vCPU, 4 GB de RAM, 40 GB NVMe e 20 TB de tráfego mensal, na faixa de R$ 23 a 27/mês. É a melhor relação entre banda incluída e preço entre os provedores tradicionais.

OVHcloud (linha de entrada)

2 GB de RAM com banda não medida, na faixa de R$ 21 a 25/mês. A ausência de franquia de tráfego elimina qualquer preocupação com crescimento de uso.

Contabo (Cloud VPS)

4 vCPU, 8 GB de RAM e 100 GB de SSD por aproximadamente R$ 27/mês em contratos promocionais de longo prazo. Recursos generosos, com desempenho de rede historicamente mais irregular.

BuyVM

Banda não medida a 1 Gbps na faixa de R$ 18/mês. Provedor menor, porém tradicionalmente tolerante a infraestrutura de privacidade.

O fator decisivo na escolha não é CPU nem RAM, e sim banda alta ou não medida. Uma bridge saudável e bem distribuída tende a receber tráfego crescente conforme ganha reputação na rede, de modo que um plano com franquia limitada tende a se tornar gargalo antes do que se imagina. Também é prudente verificar a política do provedor quanto a nós Tor: bridges e relés intermediários são amplamente aceitos, enquanto relés de saída costumam ser proibidos.


Consumo real de recursos

Após meses de operação, o consumo medido da stack completa, bridge obfs4, monitoramento via Uptime Kuma e o script de resumo diário, ficou consideravelmente abaixo do que qualquer plano de entrada oferece:

RAM     150-400 MB no total, contra 4-8 GB disponíveis
Disco   1-2 GB para a stack completa, com crescimento lento via logs
CPU     média bem abaixo de um núcleo, com picos raros sob tráfego intenso

Na prática, mesmo o plano mais barato da lista anterior mantém folga expressiva de recursos, o que permite que a mesma VPS acumule outras funções, ferramentas de bug bounty e reconhecimento contínuo, por exemplo - sem conflito perceptível com a bridge.


Arquitetura da stack

A stack roda inteiramente em Docker e é composta por três serviços, sobre um host previamente endurecido:

Camada de serviço: obfs4-bridge, construída a partir da imagem oficial do Tor Project
Camada de observabilidade: uptime-kuma, monitorando a porta e alertando via Telegram
Camada de relatório: tor-bridge-report.sh, consultando a API Onionoo do Tor Metrics
Camada de host: UFW, ufw-docker, Fail2Ban, unattended-upgrades e hardening de kernel via sysctl


Hands-on

Preparação do host

Com a instância provisionada, atualize o sistema e instale as dependências básicas.

apt update && apt upgrade -y
apt install -y ufw fail2ban unattended-upgrades curl jq

Em seguida, instale o Docker Engine a partir do script oficial e habilite o serviço.

curl -fsSL https://get.docker.com | sh
systemctl enable --now docker

Hardening de kernel

Antes de expor qualquer serviço, aplique as proteções de rede no nível do kernel. Crie o arquivo /etc/sysctl.d/99-hardening.conf com o conteúdo abaixo.

# Proteções de rede
net.ipv4.conf.all.accept_redirects = 0   # Bloqueia ICMP redirects
net.ipv4.conf.all.send_redirects = 0     # Não envia redirects
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.rp_filter = 1          # Previne IP spoofing
net.ipv4.tcp_syncookies = 1              # Mitiga SYN flood
net.ipv4.conf.all.log_martians = 1

# Proteções de kernel
kernel.kptr_restrict = 2                 # Oculta ponteiros de kernel
kernel.dmesg_restrict = 1
sysctl --system

Firewall e o problema do Docker

Configure o UFW com política restritiva, liberando apenas o SSH. As portas da bridge não devem ser liberadas aqui, o motivo é explicado logo em seguida.

ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw --force enable

O ponto crítico: o Docker ignora o UFW por padrão. A razão é a ordem de avaliação do iptables: o tráfego de uma porta publicada sofre DNAT na cadeia PREROUTING (tabela nat) e é entregue ao contêiner atravessando a cadeia FORWARD, nunca a cadeia INPUT, que é justamente onde o UFW aplica suas regras. Ao publicar uma porta em um docker-compose.yml, o Docker ainda insere suas próprias regras na cadeia DOCKER, avaliada dentro do FORWARD antes de qualquer política do UFW. O resultado é que um contêiner com porta publicada fica acessível externamente mesmo com o UFW configurado para negar tudo, e, de forma inversa, tentativas de liberar a porta apenas pelo UFW não surtem o efeito esperado. É uma divergência silenciosa entre o que o ufw status reporta e o que o kernel efetivamente aplica.

A correção é o projeto comunitário ufw-docker, que reorganiza as cadeias de forma que o UFW volte a governar o tráfego destinado aos contêineres.

wget -O /usr/local/bin/ufw-docker \
  https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
chmod +x /usr/local/bin/ufw-docker
ufw-docker install && systemctl restart ufw

A partir desse ponto, a liberação passa a ser feita por contêiner, e não por porta genérica. As regras específicas da bridge são aplicadas após a subida dos serviços.

* A validação precisa ser feita a partir de fora do host, com ferramentas independentes (nmap ou verificadores de porta online). Confiar apenas no que roda localmente é exatamente o que mascara esse tipo de falha.

Configuração da bridge obfs4

A imagem oficial do Tor Project encapsula o tor e o obfs4proxy já configurados como bridge. Crie o diretório do projeto e o arquivo de variáveis de ambiente.

mkdir -p /opt/tor-bridge && cd /opt/tor-bridge
# .env
OR_PORT=443
PT_PORT=8443
EMAIL=seu-email@exemplo.com
NICKNAME=SuaBridge

A escolha das portas não é arbitrária. A ORPort é usada para a comunicação com a rede Tor e para a verificação de alcançabilidade pela Bridge Authority; a PT_PORT é a porta obfs4 à qual os clientes censurados se conectam. Portas comuns como 443 tendem a atravessar firewalls restritivos com mais facilidade do que portas altas arbitrárias. O EMAIL é usado pelo Tor Project para notificar o operador sobre problemas com a bridge, e o NICKNAME é o identificador público do nó.

Em seguida, defina o docker-compose.yml com os três serviços da stack.

services:
  obfs4-bridge:
    image: thetorproject/obfs4-bridge:latest
    container_name: obfs4-bridge
    restart: unless-stopped
    env_file: .env
    ports:
      - "443:443"
      - "8443:8443"
    volumes:
      - tor-data:/var/lib/tor

  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - "127.0.0.1:3001:3001"
    volumes:
      - kuma-data:/app/data

volumes:
  tor-data:
  kuma-data:

O volume tor-data é o elemento mais importante da configuração: é nele que residem as chaves de identidade da bridge. Perder esse volume significa perder o fingerprint, toda a reputação acumulada na rede e a continuidade da distribuição pelo Rdsys, a bridge volta a ser tratada como um nó novo. O Uptime Kuma, por sua vez, é publicado apenas em 127.0.0.1, deliberadamente inacessível de fora e alcançável somente por túnel SSH.

Suba a stack e acompanhe a inicialização.

docker compose up -d
docker logs -f obfs4-bridge

Agora aplique as regras de firewall por contêiner. Sem esta etapa, a ORPort não é alcançável de fora e a bridge nunca conclui a validação.

ufw-docker allow obfs4-bridge 443/tcp
ufw-docker allow obfs4-bridge 8443/tcp

Validação da bridge

A confirmação definitiva vem dos próprios logs do tor. A mensagem abaixo indica que a Bridge Authority conseguiu alcançar a ORPort a partir da internet.

docker logs obfs4-bridge | grep -i "self-testing"
[notice] Self-testing indicates your ORPort 203.0.113.10:443 is reachable
         from the outside. Excellent. Publishing server descriptor.

Com a bridge publicada, obtenha a bridge line, a string que um cliente censurado utiliza para se conectar manualmente ao nó.

docker exec obfs4-bridge get-bridge-line
obfs4 203.0.113.10:8443 A1B2C3D4E5F60718293A4B5C6D7E8F9012345678 \
  cert=Bvg/itxeL4TWKLP6N1MaQzSOC6tBjZmJ3EXAMPLEcert iat-mode=0

* A bridge line não deve ser divulgada publicamente. A distribuição é responsabilidade do Rdsys, que a entrega de forma fragmentada a usuários reais. Publicá-la em um repositório, fórum ou rede social é o caminho mais rápido para que ela seja coletada e adicionada às listas de bloqueio.

Consulta ao Onionoo

A API pública Onionoo expõe os metadados de nós da rede mantidos pelo Tor Metrics. No caso de bridges, a consulta não é feita pelo fingerprint direto, e sim pelo seu hash SHA-1, mais uma camada de proteção contra enumeração. Extraia o fingerprint do volume e derive o hash.

docker exec obfs4-bridge cat /var/lib/tor/fingerprint
echo -n "<FINGERPRINT>" | xxd -r -p | sha1sum | cut -d' ' -f1 | tr 'a-z' 'A-Z'

Com o hash em mãos, consulte o estado da bridge.

curl -s "https://onionoo.torproject.org/details?lookup=<HASHED_FINGERPRINT>" | jq

* Bridges recém-criadas levam algumas horas para aparecer no Onionoo e alguns dias até serem efetivamente distribuídas pelo Rdsys. Ausência de retorno nas primeiras horas é comportamento esperado, não sintoma de erro.

Monitoramento e alertas

Como o Uptime Kuma está publicado apenas em loopback, o acesso à interface se dá por túnel SSH, evitando expor o painel à internet.

ssh -N -L 3001:localhost:3001 root@<IP_PUBLICO>
# Então, localmente:
# http://localhost:3001

No painel, configure um monitor do tipo TCP Port apontando para a PT_PORT (8443) e um segundo para a ORPort (443), com intervalo de 60 segundos. Em seguida, adicione uma notificação do tipo Telegram, informando o token do bot e o chat ID. A queda de qualquer uma das portas passa a gerar alerta imediato - e, para uma bridge, downtime prolongado significa perda de posição na distribuição do Rdsys.

Resumo diário automatizado

Complementando o alerta reativo do Uptime Kuma, um relatório diário fornece a visão de estado da bridge na perspectiva da própria rede: flags atribuídas, banda observada e data do último descritor publicado. Crie o script /opt/tor-bridge/tor-bridge-report.sh.

#!/usr/bin/env bash
set -euo pipefail

HASHED_FP="<HASHED_FINGERPRINT>"
BOT_TOKEN="<TELEGRAM_BOT_TOKEN>"
CHAT_ID="<TELEGRAM_CHAT_ID>"

DATA=$(curl -s "https://onionoo.torproject.org/details?lookup=${HASHED_FP}")

RUNNING=$(echo "$DATA" | jq -r '.bridges[0].running')
NICKNAME=$(echo "$DATA" | jq -r '.bridges[0].nickname')
FLAGS=$(echo "$DATA" | jq -r '.bridges[0].flags | join(", ")')
SEEN=$(echo "$DATA" | jq -r '.bridges[0].last_seen')

[ "$RUNNING" = "true" ] && STATUS="online" || STATUS="OFFLINE"

MSG="Bridge ${NICKNAME}
Status: ${STATUS}
Flags: ${FLAGS}
Último descritor: ${SEEN}"

curl -s -X POST "https://api.telegram.org/bot${BOT_TOKEN}/sendMessage" \
  -d "chat_id=${CHAT_ID}" \
  --data-urlencode "text=${MSG}" > /dev/null

Torne o script executável e agende a execução diária via cron.

chmod +x /opt/tor-bridge/tor-bridge-report.sh
# crontab -e
0 9 * * * /opt/tor-bridge/tor-bridge-report.sh

* O script contém credenciais do bot do Telegram. Restrinja as permissões com chmod 700 e mantenha-o fora de qualquer repositório versionado.

Fail2Ban e atualizações automáticas

Por fim, complete o hardening do host habilitando o Fail2Ban para o SSH e as atualizações automáticas de segurança. Uma bridge é um serviço de longa duração e baixa manutenção, justamente o perfil de máquina que tende a acumular pacotes desatualizados.

systemctl enable --now fail2ban
dpkg-reconfigure --priority=low unattended-upgrades


Considerações finais

O custo total de manutenção de uma bridge fica na faixa de R$ 20 a 30 por mês, e o conhecimento técnico exigido não ultrapassa o de qualquer pessoa já familiarizada com Linux e Docker. Do ponto de vista operacional, a única disciplina realmente necessária é preservar o volume de dados do contêiner e manter o uptime estável.

A lição mais transferível do processo, contudo, extrapola o contexto do Tor: a divergência entre o que o ufw status reporta e o que o kernel efetivamente aplica sob Docker afeta qualquer serviço containerizado publicado em uma VPS. Validar a superfície de exposição a partir de fora do host, com ferramentas independentes, deveria ser etapa padrão de qualquer implantação, e não apenas o desfecho de uma sessão de depuração.