Por que rustls virou assunto de produção
Quando uma aplicação Rust sai do tutorial e entra em produção, TLS aparece em vários lugares: chamada para API externa, conexão com banco, webhook, proxy reverso, fila gerenciada, gRPC, autenticação mTLS, download de artefatos, health check protegido e comunicação entre serviços internos. Em muitos ecossistemas, isso significa depender de OpenSSL instalado no sistema. Em Rust, existe uma alternativa forte: rustls, uma implementação moderna de TLS escrita em Rust.
O valor prático não é apenas dizer que “é tudo em Rust”. A diferença aparece no build, no container, na operação e na superfície de risco. Dependências nativas podem quebrar compilação em CI, exigir pacotes do sistema, variar entre distribuições Linux, complicar cross-compilation e tornar imagens menores mais difíceis. rustls reduz essa fricção em muitos cenários porque evita amarrar o projeto a uma biblioteca C externa para tarefas comuns de TLS.
Para quem trabalha com vagas Rust de backend, plataforma, SRE, segurança ou infraestrutura, esse assunto é bom porque conecta linguagem com operação real. Empresas não contratam apenas alguém que sabe ownership e borrowing. Elas precisam de devs que entendam como um serviço fala com o mundo, como certificados são validados, como builds viram containers e como uma dependência de TLS pode quebrar um deploy.
rustls versus OpenSSL: a decisão correta
A pergunta não deveria ser “rustls é melhor que OpenSSL?” de forma abstrata. A pergunta útil é: qual opção reduz risco para este produto? OpenSSL é maduro, amplamente usado e ainda obrigatório em alguns ambientes. rustls, por outro lado, tem uma proposta mais restrita e moderna: implementar TLS de forma segura, com foco em protocolos e cifras atuais, evitando APIs legadas que muitas aplicações não deveriam usar.
Em serviços HTTP modernos, clientes internos, APIs Axum, CLIs distribuídas, agentes de infraestrutura e containers pequenos, rustls costuma ser uma escolha excelente. Ele combina com o objetivo de produzir binários previsíveis, reduzir dependências nativas e facilitar deploy em ambientes Linux mínimos. Em projetos que precisam integrar com bibliotecas corporativas antigas, engines específicas, módulos FIPS formais ou APIs de OpenSSL muito particulares, a escolha pode continuar sendo OpenSSL.
O erro é decidir por moda. Se o projeto já compila bem com OpenSSL, roda em imagem mantida, tem política corporativa clara e precisa de compatibilidade específica, talvez a troca não seja prioridade. Se o time sofre com libssl-dev, Alpine, musl, containers grandes, CI inconsistente ou cross-compilation, rustls pode resolver uma dor real.
Onde rustls entra no ecossistema
Na prática, você raramente usa rustls sozinho. Ele aparece por meio de crates de cliente, servidor e integração. Em HTTP client, Reqwest pode ser configurado com a feature rustls-tls, evitando o backend TLS nativo. Em banco, SQLx aceita combinações de runtime e TLS como runtime-tokio com tls-rustls. Em gRPC, Tonic também conversa com TLS e mTLS em fluxos de serviço.
Um Cargo.toml típico para cliente HTTP pode preferir rustls explicitamente:
[dependencies]
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
serde = { version = "1", features = ["derive"] }
O detalhe importante é default-features = false. Sem isso, algumas crates podem ativar features que você não queria. Em segurança de supply chain Rust, revisar features é parte do trabalho. Não basta trocar uma palavra no Cargo.toml; confira o grafo de dependências e entenda qual backend TLS foi realmente ativado.
Containers menores e menos frágeis
Um dos motivos mais comuns para escolher rustls é container. Serviços Rust frequentemente querem imagens pequenas, previsíveis e com pouca superfície de ataque. O artigo sobre builds Docker otimizados em Rust já cobre multi-stage build, binário release, usuário não root e runtime enxuto. rustls entra como peça complementar: menos dependências nativas no runtime.
Imagine uma API compilada para rodar em imagem mínima. Se ela depende de OpenSSL dinâmico, o runtime precisa carregar bibliotecas compatíveis. Isso não é impossível, mas adiciona detalhe operacional. Versões de distribuição, pacotes ausentes, Alpine versus Debian, glibc versus musl e certificados CA viram fontes de erro. Com rustls, a parte TLS fica mais próxima do binário, embora o serviço ainda precise de store de certificados CA quando valida servidores externos.
Não confunda imagem pequena com imagem incompleta. Mesmo com rustls, um cliente HTTPS precisa confiar em autoridades certificadoras. Se o container não tem certificados raiz ou não usa uma fonte embutida adequada, chamadas para APIs externas podem falhar. Um smoke test real deve chamar um endpoint HTTPS no ambiente final, não apenas executar cargo build.
Certificados: o problema não desaparece
TLS não é só biblioteca. É identidade. Em produção, alguém precisa decidir quais certificados são aceitos, como eles são renovados, onde ficam chaves privadas, como a aplicação recarrega configuração e o que acontece quando a validação falha. rustls ajuda a implementar TLS moderno, mas não substitui disciplina operacional.
Para clientes que chamam APIs públicas, o fluxo comum é validar cadeia de certificado usando raízes confiáveis. Para comunicação interna, talvez exista CA privada, service mesh, mTLS ou certificados emitidos por automação do cluster. Nesses casos, a aplicação precisa carregar a CA correta e rejeitar conexões inválidas. Desabilitar validação de certificado para “fazer funcionar” é uma das piores gambiarras possíveis: resolve o teste local e cria uma falha grave de segurança.
Em mTLS, o cuidado dobra. O cliente apresenta certificado; o servidor valida. Isso pode ser útil para serviços internos, agentes, operators e integrações de infraestrutura. Mas também exige rotação, revogação, permissão de leitura da chave privada e logs que ajudem a diagnosticar expiração sem vazar material sensível.
Servidores TLS: aplicação ou proxy?
Nem todo serviço Rust precisa terminar TLS dentro da própria aplicação. Em muitos deployments, Nginx, Caddy, Traefik, Cloudflare, Kubernetes ingress ou service mesh fazem terminação TLS na borda. A aplicação recebe HTTP interno e foca em lógica de negócio. Esse desenho é simples e funciona bem quando a rede interna e o proxy são confiáveis dentro do contexto da arquitetura.
Ainda assim, há cenários em que TLS dentro do binário faz sentido: CLI que expõe endpoint local protegido, agente instalado em servidor de cliente, serviço gRPC interno com mTLS, appliance, IoT industrial, ou ambiente onde a aplicação fala diretamente com outro componente sem proxy intermediário. Nesses casos, rustls pode ser uma escolha natural por reduzir dependência nativa e permitir configuração explícita.
A decisão deve estar documentada. Se TLS termina no proxy, registre como headers de origem são tratados, como redirecionamento HTTPS funciona e como health checks passam. Se TLS termina no app, documente certificados, reload, mTLS, rotação, protocolos aceitos e teste de expiração. Segurança que vive apenas na cabeça de uma pessoa vira incidente quando essa pessoa sai de férias.
Observabilidade e falhas de TLS
Falhas de TLS precisam aparecer de forma útil. Um log dizendo apenas connection failed não ajuda. Registre a classe do erro: DNS, conexão TCP, handshake, certificado expirado, hostname inválido, CA desconhecida, protocolo incompatível, timeout ou erro de aplicação depois do handshake. Essa classificação acelera diagnóstico sem expor segredo.
Se o serviço usa OpenTelemetry em Rust, inclua atributos seguros nos spans de chamada externa: dependência lógica, método, status, duração, erro por classe e tentativa. Não registre certificado completo, token, header de autorização, cookie ou payload sensível. Para métricas, acompanhe taxa de erro por dependência e classe. Um pico de certificate_expired pede ação diferente de um pico de timeout.
Também pense em alerta antes do vencimento. Certificados internos e externos expiram. Se um agente Rust roda em Linux embarcado ou em ambiente de cliente, ele pode ficar semanas sem intervenção. Um check de validade, métrica de dias restantes ou runbook de renovação evita descobrir o problema quando todas as conexões param.
Desenvolvimento local sem criar mau hábito
Em desenvolvimento, é comum usar certificado autoassinado. Isso é aceitável quando o objetivo é testar fluxo TLS, mas precisa ser feito com disciplina. Crie uma CA local, configure explicitamente o cliente para confiar nela e mantenha essa configuração separada de produção. Evite flags globais do tipo “aceitar certificado inválido” como padrão de projeto.
O README deve explicar como gerar certificado local, como rodar o serviço, como testar com curl, como simular certificado expirado e como verificar que produção rejeita certificados inválidos. Esse tipo de documentação parece pequena, mas mostra maturidade. O dev que entra no projeto entende o fluxo correto em vez de copiar uma exceção insegura.
Para projeto de portfólio, uma boa ideia é criar uma API Axum com cliente Reqwest usando rustls, endpoint interno protegido e testes que validem sucesso com CA local e falha com certificado incorreto. Combine isso com release engineering em Rust: binário com versão, container mínimo, smoke test HTTPS e documentação de certificados.
Checklist prático antes de usar em produção
Antes de declarar que um serviço Rust está pronto para TLS em produção, revise:
- o backend TLS escolhido está explícito no
Cargo.toml; default-featuresforam revisadas nas crates críticas;- o container final tem estratégia clara para certificados CA;
- validação de certificado nunca é desativada em produção;
- certificados internos têm processo de rotação e dono;
- erros de TLS são classificados em logs e métricas;
- smoke test faz uma chamada HTTPS real no runtime final;
- mTLS, quando usado, tem política de chave privada e revogação;
- CI roda
cargo test --lockede checks de dependência; - documentação explica por que rustls ou OpenSSL foi escolhido.
Esse checklist evita a falsa segurança de “compilou, então está pronto”. TLS costuma falhar em detalhes de ambiente: store de CA, relógio incorreto, hostname, proxy, cadeia intermediária, permissão de arquivo e certificado expirado. Teste o que vai rodar, não apenas o que compila na máquina do dev.
Carreira: o que saber explicar em entrevista
Para entrevista ou conversa técnica, não transforme rustls em slogan. Explique trade-offs. Diga que rustls reduz dependência nativa, combina com containers mínimos e segue um conjunto moderno de TLS. Diga também que OpenSSL ainda pode ser necessário por compatibilidade, política corporativa ou integração legada. Mostre que você sabe revisar features do Cargo, validar certificados, pensar em CA raiz, mTLS, logs seguros e smoke tests.
Esse conhecimento conversa com várias áreas do mercado brasileiro. Fintechs se preocupam com segurança e auditoria. Plataformas internas se preocupam com deploy e confiabilidade. Empresas industriais e embarcadas se preocupam com agentes em campo. Times de produto se preocupam com incidentes que derrubam integração externa. Rust entra bem nessa conversa quando o dev mostra que entende produção, não apenas linguagem.
Se você também trabalha com Go, vale comparar como cada ecossistema trata TLS, containers e dependências nativas no Golang Brasil. Muitas empresas escolhem entre Rust e Go para serviços parecidos; saber argumentar tecnicamente ajuda a defender a melhor opção para cada contexto.
Conclusão
rustls é uma das peças que tornam Rust atraente para produção moderna. Ele não resolve todos os problemas de segurança, mas reduz dependências nativas, simplifica muitos builds, combina com containers enxutos e força uma conversa mais explícita sobre TLS. Usado com cuidado, ele ajuda a entregar serviços mais previsíveis.
A regra prática é simples: escolha rustls quando ele reduz complexidade sem quebrar requisitos reais. Mantenha OpenSSL quando a compatibilidade exigir. Em qualquer caso, trate TLS como parte da arquitetura: certificados, validação, rotação, observabilidade, CI e documentação. É esse conjunto, não apenas a crate, que diferencia um projeto Rust de laboratório de um serviço Rust pronto para produção.