Contornando Bloqueios em um Aplicativo iOS: VLESS + Reality via sing-box e os Obstáculos no Caminho

Contornando Bloqueios em um Aplicativo iOS: VLESS + Reality via sing-box e os Obstáculos no Caminho

Um relato técnico sobre como a equipe do mensageiro RCQ implementou a tecnologia VLESS + Reality com sing-box para contornar bloqueios em seu aplicativo iOS. O artigo detalha as escolhas de design, os desafios de infraestrutura e as lições aprendidas, focando em como manter o aplicativo funcional em ambientes com censura.

MundiX News·23 de maio de 2026·10 min de leitura·👁 8 views

rcq 19 minutos atrás Contornando Bloqueios em um Aplicativo iOS: VLESS + Reality via sing-box e os Obstáculos no Caminho Médio 6 min 499 Swift * Go * Segurança da Informação * Tecnologias de Rede * iOS * Caso de Uso Da Caixa de Areia

Nós desenvolvemos um mensageiro. Na primavera de 2026, nosso backend começou a falhar para alguns usuários na Rússia: as requisições HTTPS para a API expiravam, o WebSocket não subia. A situação é familiar para todos que mantêm um serviço com um único domínio e um único IP.

Para um mensageiro, isso é uma sentença de morte. Não é “inconveniente”, é uma sentença de morte: um aplicativo que nem consegue se conectar é inútil. E a opção “peça ao usuário para primeiro ativar a VPN” não nos agradava nem um pouco. Abaixo, detalharei por que acabamos integrando o contorno diretamente no aplicativo, em que ele funciona e em quais obstáculos tropeçamos. Sem marketing, direto ao ponto.

Por que não “apenas VPN”

A primeira ideia de todos é a mesma: que o usuário instale uma VPN. Mas, se você olhar para isso com os olhos do produto, tudo desmorona.

Primeiro, isso mata o funil. Cada etapa adicional até que “o aplicativo funcione” custa uma parte da sua audiência, e “instale e configure um aplicativo separado” não é uma etapa, é um abismo. É especialmente doloroso que a parte dos usuários que mais precisa do produto seja exatamente a que falha.

Segundo, a própria VPN é um alvo móvel. Protocolos populares (OpenVPN, WireGuard, IKEv2) são detectados há muito tempo e bem por assinaturas, os intervalos de provedores de VPN conhecidos são cortados em massa. Ou seja, você delega uma função crítica para você a um aplicativo de terceiros, que amanhã pode parar de funcionar por conta própria.

Queríamos um comportamento diferente: o usuário abre o mensageiro e ele funciona. Sem um aplicativo separado, sem uma assinatura de um serviço de terceiros, sem um perfil de VPN nas configurações do sistema iOS. O contorno deve ser um detalhe de implementação, e não uma tarefa do usuário.

O que exatamente está sendo bloqueado

Para entender o que consertar, você precisa entender o que está quebrando. Temos um esquema bastante típico: API HTTPS e WebSocket no mesmo domínio. Bloquear essa conexão pode ser feito de várias maneiras, e geralmente elas são combinadas:

  • Por endereço IP do servidor;
  • Por nome de domínio em ClientHello (o campo SNI é enviado em texto simples, mesmo em TLS);
  • Por DPI, que olha para a própria estrutura do tráfego e reconhece o protocolo.

Uma conexão TLS normal para o seu domínio é cortada por SNI em um instante. Portanto, a tarefa é formulada da seguinte forma: a conexão deve parecer uma chamada para algum outro host, sem nada de especial e obviamente permitido. Não “criptografar mais”, mas sim “parecer diferente”.

Por que VLESS e Reality

Um pouco de história, é importante aqui. Shadowsocks e VMess, em seu tempo, resolveram exatamente essa tarefa, e por algum tempo resolveram bem. O problema é a sondagem ativa: o censor não apenas observa seu tráfego passivamente, ele mesmo envia uma solicitação de teste para um servidor suspeito. Se o servidor responder como um proxy (e as primeiras implementações responderam de forma reconhecível), o endereço vai para o bloco. Além disso, assinaturas passivas foram acumuladas, pelas quais o “tráfego de proxy” diferia do HTTPS normal.

Reality resolve isso com um truque cuidadoso. O servidor proxy não apresenta seu próprio certificado TLS. Em vez disso, durante o handshake, ele faz proxy do TLS-handshake para um site real, de terceiros e popular. Para um observador passivo e para um testador ativo, seu servidor é indistinguível desse site: certificado válido, handshake válido, cadeia válida. E somente o cliente, que possui a chave pública correta, após o handshake “muda” para o túnel real. A sondagem de terceiros não tem para que mudar, ela vê um site grande normal.

o que isso dá na prática:

  • Não há seu próprio domínio, que pode ser inserido no bloco por SNI;
  • Não há seu próprio certificado, pelo qual você pode identificar “este é um proxy”;
  • A sondagem ativa se baseia em um site legítimo de terceiros.

VLESS aqui é um transporte leve sem sua própria camada de criptografia (a criptografia é assumida pelo TLS), com uma impressão digital pequena e sem expressão. E o fluxo xtls-rprx-vision suaviza ainda mais o padrão “TLS dentro de TLS”, que, caso contrário, é visível pelos tamanhos característicos dos pacotes.

Não inventamos nada disso. Reality e sing-box são trabalho de outras pessoas, e muito bom. A parte interessante começou mais tarde: como colocar isso dentro de um aplicativo normal para que o usuário não percebesse nada.

sing-box dentro do aplicativo

sing-box é uma plataforma proxy universal em Go e, entre outras coisas, pode ser um cliente VLESS + Reality. Normalmente, ele é executado como um processo separado com uma configuração. Não precisamos de um processo separado, precisamos que ele viva dentro do aplicativo iOS.

Isso é ajudado pelo gomobile. Através do gomobile bind O código Go é compilado em nativo .xcframework , que é vinculado ao aplicativo como uma dependência normal. Ou seja, sing-box não é um CLI próximo a nós, mas um framework dentro do binário. Executamos diretamente no processo do aplicativo.

sing-box, iniciado dessa forma, abre um inbound local (tipo mixed, ou seja, SOCKS e HTTP ao mesmo tempo) em 127.0.0.1 em uma porta aleatória. Em seguida, há uma bifurcação, e ela é fundamental.

Você pode fazer uma VPN do sistema via NEPacketTunnelProvider : então todo o tráfego do dispositivo passará pelo túnel. Você pode fazer um proxy no nível do aplicativo: então apenas o tráfego do seu aplicativo passará pelo túnel. Escolhemos a segunda opção, e é por isso. Não precisamos direcionar todo o telefone pelo relay, precisamos entregar apenas o tráfego do mensageiro ao servidor. E como é assim, então a Network Extension com sua arquitetura, um processo de extensão separado, limites de memória e perguntas adicionais na revisão na App Store, simplesmente não precisamos. Menos peças móveis, menos pontos de falha.

Na prática, isso se parece com o seguinte: sing-box executa um proxy local, e a pilha de rede do aplicativo ( URLSession ) envolvemos nesse endereço local.

swift
let config = URLSessionConfiguration.default
config.connectionProxyDictionary = [
    "SOCKSEnable": 1,
    "SOCKSProxy": "127.0.0.1",
    "SOCKSPort": localPort,
]

Todo o resto do telefone não é afetado, nenhum perfil de VPN aparece nas configurações. A desvantagem da abordagem é honesta: esta não é uma VPN universal, outros aplicativos não passarão por ela. Mas não precisávamos disso.

A configuração para sing-box é gerada em tempo de execução. Se você remover o desnecessário, o núcleo ficará assim:

json
{
  "inbounds": [{
    "type": "mixed",
    "listen": "127.0.0.1",
    "listen_port": 0
  }],
  "outbounds": [{
    "type": "vless",
    "server": "RELAY_ADDR",
    "server_port": 443,
    "uuid": "...",
    "flow": "xtls-rprx-vision",
    "tls": {
      "enabled": true,
      "server_name": "www.site-grande-terceiro.com",
      "utls": { "enabled": true, "fingerprint": "chrome" },
      "reality": {
        "enabled": true,
        "public_key": "...",
        "short_id": "..."
      }
    }
  }]
}

Separo utls com impressão digital chrome . A implementação TLS padrão do Go tem seu próprio ClientHello reconhecível, e esta é uma assinatura em si. uTLS substitui ClientHello para que ele corresponda ao Chrome real. O detalhe é pequeno, mas é desses pequenos detalhes que se forma “parece um navegador normal”.

Obstáculos com relay

Agora, pelo que valeu a pena escrever este artigo. Com o protocolo, tudo acabou bem. Passamos pelos obstáculos na infraestrutura.

O primeiro relay foi configurado em um VPS barato de um hoster típico. O endereço queimou rapidamente. Os intervalos de data centers e hospedagem, nos quais historicamente há muitos proxies, são rastreados e cortados, seja preventivamente, seja de forma muito operacional. Reality esconde perfeitamente o protocolo, mas não esconde o fato de que “o tráfego vai para um IP de um intervalo suspeito”.

Transferimos o relay para um endereço no intervalo de um grande provedor de nuvem. E aqui ele vive. A lógica é simples: cortar o espaço de endereço de uma grande nuvem por atacado é caro em termos de danos colaterais, há uma massa de serviços legítimos nos mesmos endereços que ninguém quer quebrar.

A conclusão que tiramos para nós mesmos: com Reality, o ponto fraco não é o protocolo, mas o IP. É o endereço que queima. Portanto, o relay é, por definição, um consumível, e deve ser tratado como um consumível.

O endereço do relay não deve ser embutido no binário

Consequência direta do item anterior. Se o endereço do relay estiver hardcoded no aplicativo, então o IP queimado significa um novo lançamento na App Store e aguardar a revisão. Para um consumível que pode queimar a qualquer momento, isso é inaceitável.

Portanto, os parâmetros do relay (endereço, chaves, SNI) o aplicativo deve receber separadamente de sua compilação. Então, a alteração do relay é uma alteração da configuração, e não um ciclo de lançamento. O mecanismo específico de entrega da configuração pode ser diferente, o princípio em si é importante: o que queima com frequência não vive no binário.

Honestidade sobre limites

O túnel não é mágica, e vendê-lo como mágica é desonesto.

O operador do relay (no nosso caso, nós mesmos) vê seu tráfego para nossos servidores da mesma forma que seu provedor o veria com uma conexão direta. O túnel muda a aparência da conexão para o censor no caminho e não muda quem está nas extremidades. O conteúdo da correspondência é, em qualquer caso, protegido por criptografia de ponta a ponta no nível do aplicativo (libsignal), e o túnel não adiciona nem subtrai nada a isso. É importante separar isso: contornar bloqueios e a privacidade da correspondência são duas tarefas diferentes que são resolvidas por diferentes camadas.

Reality mantém bem a sondagem ativa, mas “para sempre” não existe nesta área. Os IPs queimam, as assinaturas se acumulam, os métodos de detecção se desenvolvem. Esta é uma corrida, e você precisa tratá-la como uma corrida: ter uma reserva de endereços, ser capaz de alterá-los rapidamente, monitorar o que exatamente falhou.

Em nosso caso, o contorno está desativado por padrão. O aplicativo primeiro tenta se conectar diretamente, e somente se a conexão direta não for bem-sucedida, ele levanta o túnel. Não faz sentido direcionar o tráfego pelo relay onde a rede já está aberta.

O que tirar deste texto

Se você estiver resolvendo um problema semelhante, três coisas que economizarão seu tempo:

  • Um proxy no nível do aplicativo geralmente é melhor do que uma VPN do sistema. Se você só precisa entregar seu tráfego ao servidor, NEPacketTunnelProvider é uma complexidade desnecessária. Um inbound local mais connectionProxyDictionary resolve o problema com menos esforço.
  • O protocolo é a parte fácil. VLESS + Reality + sing-box é um problema resolvido, tudo já foi escrito antes de você. A parte difícil é a infraestrutura: quais endereços você usa, com que rapidez você os altera, como você monitora tudo isso.
  • Planeje a rotação desde o primeiro dia. Não “algum dia colocaremos o relay na configuração”, mas imediatamente. Porque o primeiro IP queimado mostrará se você tem rotação ou não, e descobrir isso na produção é desagradável.

Tudo o que foi descrito vive em nosso mensageiro RCQ, agora está em beta aberto no iOS. O cliente para iOS é de código aberto, então, se desejar, você pode ver exatamente como o trabalho com o transporte é organizado, e não acreditar na palavra: github.com/rcq-messenger/rcq-ios

Se você está fazendo algo semelhante e encontrou seus obstáculos na infraestrutura, conte nos comentários, especialmente interessante sobre a prática de rotação de endereços.

Tags: contorno de bloqueios VLESS Reality sing-box XTLS proxy censura iOS Swift gomobile

Hashes: Swift Go Segurança da Informação Tecnologias de Rede iOS

📤 Compartilhar & Baixar