Olá, Habr! Meu nome é Valentin, sou engenheiro DevOps na equipe Platform V Kintsugi. Desenvolvemos um serviço em nuvem e regularmente enfrentamos tanto desafios arquiteturais na construção de sistemas distribuídos quanto questões de segurança.
Em um artigo anterior, analisamos os princípios básicos de funcionamento de serviços no Istio e abordamos as principais abordagens para garantir a segurança em ambientes de nuvem, utilizando nosso serviço em nuvem como exemplo. Nosso produto é um console de gerenciamento de bancos de dados, portanto, uma parte significativa de sua arquitetura é construída em torno da interação de microsserviços com SGBDs. Este contorno é a base da maioria das operações – desde gerenciamento e administração até monitoramento e manutenção – o que significa que os requisitos para sua confiabilidade e segurança se tornam criticamente importantes.
Neste contexto, a questão da organização da interação de serviços com bancos de dados externos é particularmente interessante. Neste artigo, focaremos neste aspecto prático e o analisaremos usando o PostgreSQL como exemplo. Utilizaremos o Platform V Synapse Service Mesh como base tecnológica – uma solução da SberTech para gerenciar a interação de microsserviços em clusters Kubernetes, criada com base em Istio e Envoy. O Synapse Service Mesh é uma camada de infraestrutura separada que permite o gerenciamento centralizado de tráfego, segurança, observabilidade de serviços e políticas de interação entre eles. Analisaremos quais cenários arquiteturais e operacionais são possíveis aqui, e também mostraremos como as tarefas de segurança podem ser delegadas do nível de aplicação para o nível de infraestrutura, utilizando os recursos do Synapse.
Anteriormente, já abordamos o cenário de interação de um microsserviço em nuvem com o SGBD PostgreSQL no modo passthrough, onde a segurança da conexão é totalmente responsabilidade da aplicação – resultando em uma conexão TLS de ponta a ponta com o servidor SGBD. Proponho começar a exploração justamente com este cenário básico. Para clareza, reproduziremos o cenário anteriormente considerado. Para isso, é necessário preparar o seguinte conjunto de CRDs (Custom Resource Definitions):
- ServiceEntry: registra um serviço externo no registro da mesh e permite gerenciar o tráfego para ele.
- Service: define o ponto de entrada interno para o Egress Gateway.
- Gateway: descreve a configuração do próprio Egress Gateway e aceita o tráfego de entrada.
- VirtualService: gerencia o roteamento de tráfego dentro da mesh e através do gateway.
- DestinationRule: define políticas de processamento de tráfego, incluindo TLS e comportamento para subsets específicos.
ServiceEntry
yamlapiversion: networking.istio.io/v1beta1 kind: ServiceEntry metadata: name: postgres-se-passthrough spec: endpoints: - address: 10.40.20.72 exportTo: - . hosts: - postgres.solution.test location: MESH_EXTERNAL ports: - name: tcp-5432 number: 5432 protocol: tcp resolution: STATIC
Nesta configuração, é criada uma entrada no registro Istio para o serviço externo PostgreSQL – ele é registrado sob o nome de domínio estaticamente especificado postgres.solution.test (IP: 10.40.20.72) e aceita conexões pela porta 5432 (TCP). A partir deste momento, o tráfego para este serviço será monitorado e gerenciado pelo Istio.
Service
yamlkind: Service apiversion: v1 metadata: name: postgres-egress-service-passthrough spec: ports: - name: tcp-5000 protocol: TCP port: 5000 targetPort: 5000 type: ClusterIP selector: app: egressgateway
Na próxima etapa, definimos a porta 5000 (TCP), pela qual as conexões de nossos serviços internos ao SGBD serão processadas no Egress Gateway.
Gateway Com o uso do Gateway, adicionamos um ponto de entrada para o tráfego que chega ao Egress Gateway:
yamlapiversion: networking.istio.io/v1beta1 kind: Gateway metadata: name: postgres-gw-passthrough spec: selector: app: egressgateway servers: - hosts: - postgres.solution.test port: name: tcp-5000 number: 5000 protocol: TCP
A configuração cria um recurso Gateway que aceita tráfego TCP de entrada pela porta 5000 para o host postgres.solution.test (IP: 10.40.20.72). O tráfego é processado no modo passthrough: um canal de dados seguro com criptografia de ponta a ponta é configurado entre o microsserviço e o SGBD.
VirtualService
yamlapiversion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: postgres-vs-passthrough spec: exportTo: - . gateways: - postgres-gw-passthrough - mesh hosts: - postgres.solution.test tcp: - match: - gateways: - mesh port: 5432 route: - destination: host: postgres-egress-service-passthrough port: number: 5000 subset: postgres-internal-passthrough - match: - gateways: - postgres-gw-passthrough port: 5000 route: - destination: host: postgres.solution.test port: number: 5432 subset: postgres-external-passthrough
O VirtualService criado gerencia o roteamento do tráfego TCP: todas as requisições destinadas ao host do SGBD, registrado no Istio sob o nome postgres.solution.test na porta 5432 (TCP), são redirecionadas através da service mesh para a porta interna 5000 (TCP) do proxy de saída (Egress Gateway). Em seguida, o tráfego que chega à porta 5000 (TCP) do proxy de saída é roteado para o endereço do host (IP: 10.40.20.72) e porta 5432 (TCP).
DestinationRule Para finalizar, definimos os recursos da regra DestinationRule para cada fluxo de tráfego:
- DestinationRule (internal) estabelece políticas de processamento de tráfego do microsserviço para o Egress Gateway (subset
postgres-internal-passthrough). - DestinationRule (external) estabelece políticas de processamento de tráfego do Egress Gateway para o serviço SGBD (subset
postgres-external-passthrough).
yamlapiversion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: postgres-external-dr-passthrough spec: exportTo: - . host: postgres.solution.test subsets: - name: postgres-external-passthrough workloadSelector: matchLabels: app: egressgateway
Vamos para a demonstração. Para isso, utilizaremos o cliente psql rodando dentro do pod da aplicação, simulando assim o comportamento de um cliente PostgreSQL de aplicação, e analisaremos o processo de estabelecimento de uma conexão TLS segura através do proxy do Egress Gateway no modo passthrough. Para não nos distrairmos com os passos preparatórios, assumiremos que o serviço cliente de teste já foi implantado na nuvem, os certificados de cliente e servidor corretos foram emitidos, e todos os artefatos necessários para a demonstração foram entregues nos sistemas de arquivos do cliente e do servidor.
Como política de acesso ao servidor SGBD PostgreSQL, utilizaremos uma configuração especial do pg_hba.conf:
hostssl all all 0.0.0.0/0 cert
Esta entrada significa o seguinte:
hostssl: a conexão é permitida apenas por um canal seguro.all all: quaisquer bancos de dados e usuários são permitidos.0.0.0.0/0: a conexão é possível de qualquer endereço IP.cert: a autenticação é realizada pelo certificado do cliente. Este é um aspecto chave para verificar a funcionalidade da autenticação em nosso esquema.
Em nosso exemplo, isso significa que para uma conexão bem-sucedida, o cliente deve:
- Estabelecer uma sessão TLS.
- Fornecer um certificado válido, assinado por uma autoridade certificadora confiável.
- Usar um nome de usuário que corresponda ao Common Name (CN) do certificado.
O formato do comando para conectar ao banco de dados, que usaremos no exemplo básico, é:
psql "host=<db_host> \ port=<db_port> \ dbname=<db_name> \ user=<db_user> \ sslmode=<db_sslmode> \ sslrootcert=<db_sslrootcert> \ sslcert=<db_sslcert> \ sslkey=<db_sslkey>"
Onde:
host: endereço do servidor PostgreSQL.port: porta TCP do PostgreSQL.dbname: nome do banco de dados dentro do PostgreSQL.user: nome do usuário PostgreSQL.sslmode: modo de uso do TLS:disable: a conexão é estabelecida sem o uso de TLS/SSL, os dados são transmitidos em texto claro.allow: o cliente primeiro tenta se conectar sem criptografia, mas permite o uso de SSL, se necessário.prefer: por padrão, uma conexão SSL é usada, mas se não estiver disponível, é permitido mudar para um canal não criptografado.require: SSL é obrigatório: a conexão é estabelecida apenas se houver um canal seguro, mas o certificado do servidor não é verificado.verify-ca: SSL é obrigatório, e adicionalmente é verificado se o certificado do servidor foi assinado por uma autoridade certificadora (CA) confiável.verify-full: o modo mais rigoroso: a confiança na CA e a correspondência do nome do host no certificado com o endereço do servidor são verificadas. Isso garante proteção completa contra a substituição do servidor.
sslrootcert: certificado da autoridade certificadora confiável.sslcert: certificado do cliente.sslkey: chave privada do cliente.
Conectaremos ao servidor PostgreSQL substituindo os valores dos parâmetros apropriados:
bash10001@psql-66f8cccd88-lhb58:/$ psql "host=postgres.solution.test user=postgres port=5432 sslmode=verify-full sslcert=/tmp/certs/postgres.crt sslkey=/tmp/certs/postgres.key sslrootcert=/tmp/certs/ca.crt"
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Verificaremos os valores reais dos parâmetros da conexão estabelecida usando os próprios meios do SGBD:
sqlpostgres=# SELECT ssl, version, cipher FROM pg_stat_ssl WHERE pid = pg_backend_pid();
Resultado:
ssl | version | cipher
-----+---------+------------------------
t | TLSv1.3 | TLS_AES_256_GCM_SHA384
(1 row)
Ao estabelecer a sessão, a verificação criptográfica mútua foi bem-sucedida: o servidor confirmou sua autenticidade usando um certificado verificado por um centro confiável, e o cliente se autenticou usando seu próprio certificado e chave privada, o que corresponde ao modelo mTLS.
Adicionalmente, a rota do tráfego através do Egress Gateway é confirmada:
[2026-04-23T17:52:44.346Z] "- - -" 0 - - - "-" 2706 3577 13828 - "" "" "" "" "10.40.20.72:5432" outbound|5432| |postgres.solution.test 172.21.16.23:45806 172.21.16.23:5000 172.21.18.58:37264 - -
O formato dos logs do Egress Gateway foi detalhado em nosso artigo anterior, portanto, aqui prestaremos atenção apenas aos elementos-chave:
outbound|5432|postgres-external-passthrough|postgres.solution.test: indica o subset utilizado e confirma que a regra de roteamento correta foi aplicada.172.21.16.23:5000: endereço e porta internos do Egress Gateway pelos quais o tráfego passou.
Neste cenário, toda a responsabilidade pelo estabelecimento de uma conexão segura recai inteiramente sobre a aplicação. Portanto, o serviço deve:
- Saber onde os certificados e chaves estão localizados.
- Usá-los corretamente ao estabelecer a conexão.
- Gerenciar os parâmetros TLS (modo, verificação, cadeia de confiança).
- Garantir o processamento correto de erros ao trabalhar com uma conexão segura.
Na prática, isso leva a uma série de dificuldades.
Primeiro, a configuração começa a ser duplicada de serviço para serviço: cada cliente que interage com o PostgreSQL deve ter seu próprio conjunto de configurações e artefatos.
Segundo, o gerenciamento de certificados se torna mais complexo:
- É necessário entregá-los em cada pod.
- Controlar os prazos de validade.
- Garantir a rotação e atualização.
Terceiro, o risco de erros de configuração aumenta. Qualquer imprecisão – desde o caminho incorreto para o certificado até parâmetros TLS incorretos – pode levar à falha da conexão ou, pior, à redução do nível de segurança. Como resultado, com o aumento do número de microsserviços, o número de configurações, artefatos e pontos de falha aumenta, tornando essa abordagem cada vez menos gerenciável.
Surge a pergunta lógica: é possível liberar a aplicação dessas tarefas e transferir a responsabilidade pelo estabelecimento de uma conexão segura para a infraestrutura? Os recursos do Istio permitem implementar essa abordagem delegando o TLS para o nível do Egress Gateway.
Agora, vamos analisar como a mesma tarefa é resolvida no cenário de uso do produto SberTech – Platform V Synapse Service Mesh. Esta é uma versão enterprise do Istio, que possui recursos aprimorados e suporta protocolos adicionais. Em nosso caso, analisaremos o suporte nativo ao protocolo PostgreSQL, que está disponível exclusivamente no Synapse. O cenário em si permanece inalterado: mantemos a mesma conexão, mas transferimos o ponto de inicialização da conexão TLS para o Egress Gateway. Os certificados não estão mais no pod da aplicação – eles são armazenados e gerenciados no nível da infraestrutura. Vamos ver como isso é feito no Synapse e conectar o microsserviço ao SGBD PostgreSQL.
Semelhante ao exemplo descrito acima, definimos uma porta dedicada 5001 (TCP), pela qual as conexões de nossos serviços internos ao SGBD serão processadas no Egress Gateway:
yamlkind: Service apiversion: v1 metadata: name: postgres-egress-service-tls-origin spec: ports: - name: tcp-5001 protocol: TCP port: 5001 targetPort: 5001 type: ClusterIP selector: app: egressgateway
Com o uso do Gateway, adicionamos um ponto de entrada para o tráfego que chega ao Egress Gateway:
yamlapiversion: networking.istio.io/v1beta1 kind: Gateway metadata: name: postgres-gw-tls-origin spec: selector: app: egressgateway servers: - hosts: - postgres.solution.test port: name: postgres-5001 number: 5001 protocol: TCP
A configuração cria um recurso Gateway que aceita tráfego TCP de entrada pela porta 5001 para o host postgres.solution.test (IP: 10.40.20.72).
Com o uso do VirtualService, criamos as regras de roteamento de tráfego TCP necessárias: todas as requisições destinadas ao host do SGBD, registrado no Istio sob o nome postgres.solution.test na porta 5432 (TCP), são redirecionadas através da service mesh para a porta interna 5001 (TCP) do proxy de saída. Em seguida, o tráfego que chega à porta 5001 (TCP) do proxy de saída é roteado para o endereço do host postgres.solution.test (IP: 10.40.20.72) e porta 5432 (TCP):
yamlapiversion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: postgres-vs-tls-origin spec: exportTo: - . gateways: - postgres-gw-tls-origin - mesh hosts: - postgres.solution.test tcp: - match: - gateways: - mesh port: 5432 route: - destination: host: postgres-egress-service-tls-origin port: number: 5001 subset: postgres-internal-tls-origin - match: - gateways: - postgres-gw-tls-origin port: 5001 route: - destination: host: postgres.solution.test port: number: 5432 subset: postgres-external-tls-origin
Definimos os recursos da regra DestinationRule para os fluxos de tráfego interno e externo:
DestinationRule (internal)
yamlapiversion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: postgres-internal-dr-tls-origin spec: exportTo: - . host: postgres-egress-service-tls-origin subsets: - name: postgres-internal-tls-origin
DestinationRule (external)
yamlapiversion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: postgres-external-dr-tls-origin spec: exportTo: - . host: postgres.solution.test subsets: - name: postgres-external-tls-origin trafficPolicy: tls: caCertificates: /secrets/istio/egressgateway-certs/ca.crt clientCertificate: /secrets/istio/egressgateway-certs/postgres.crt privateKey: /secrets/istio/egressgateway-certs/postgres.key mode: MUTUAL workloadSelector: matchLabels: app: egressgateway
Ao contrário do exemplo anterior, é necessário definir adicionalmente os valores dos parâmetros da conexão TLS, com os quais criaremos um transporte seguro para o serviço externo do SGBD PostgreSQL.
Como resultado, o esquema funcionou, registramos o serviço externo no registro Istio:
yamlapiversion: networking.istio.io/v1beta1 kind: ServiceEntry metadata: name: postgres-se-tls-origin spec: endpoints: - address: 10.40.20.72 exportTo: - . hosts: - postgres.solution.test location: MESH_EXTERNAL ports: - name: postgres-5432 number: 5432 protocol: postgres resolution: STATIC
Aqui é importante prestar atenção ao parâmetro protocol: postgres. À primeira vista, ele pode parecer equivalente a TCP, no entanto, no contexto do Platform V Synapse Service Mesh, estes são modos de operação fundamentalmente diferentes.
Se a configuração usar protocol: tcp, o Service Mesh tratará o tráfego como um fluxo de dados opaco. Neste modo:
- O sidecar e o Egress Gateway não possuem informações sobre o protocolo.
- A conexão não é analisada.
- Ou o TLS é totalmente implementado no lado da aplicação, ou é iniciado pelo proxy sem levar em conta as especificidades do protocolo.
Na verdade, o Service Mesh atua como um proxy L4, que simplesmente transfere bytes entre a origem e o destino. Para confirmar esse comportamento, analisaremos o tráfego de rede entre o Egress Gateway e o servidor PostgreSQL.
No dump, há a seguinte sequência:
- Após o estabelecimento da conexão TCP, o Egress Gateway inicia imediatamente o TLS handshake (Client Hello).
- Não há uma etapa preliminar de SSL negotiation, característica do PostgreSQL.
- O servidor PostgreSQL encerra a conexão (RST), pois espera um SSLRequest antes de iniciar o TLS.
Esta sequência corresponde ao cenário incorreto apresentado no diagrama à esquerda:
Isso indica uma violação do protocolo: a sessão TLS é iniciada sem a etapa preliminar de SSLRequest e já após o início da interação do PostgreSQL. Como resultado, dois estados incompatíveis se misturam em uma única conexão: plaintext PostgreSQL e a tentativa de transição para TLS, o que leva ao encerramento da conexão.
Isso confirma que no modo protocol: tcp:
- O gerenciamento de TLS permanece no nível da aplicação.
- A infraestrutura não pode se integrar corretamente ao estabelecimento da conexão.
- As capacidades de gerenciamento centralizado de segurança são limitadas.
Ao usar protocol: postgres, o comportamento muda.
O Synapse Service Mesh começa a tratar o tráfego como uma conexão PostgreSQL e obtém contexto adicional que permite:
- Processar corretamente a etapa de estabelecimento da conexão.
- Levar em conta as especificidades do handshake do PostgreSQL (inicialização da conexão, troca de parâmetros de sessão e transição para TLS via SSLRequest).
- Iniciar a sessão TLS no lado do Egress Gateway.
- Aplicar os parâmetros de segurança definidos no DestinationRule.
Ao contrário do cenário anterior, a sequência correta de interação é a seguinte:
- Estabelece-se uma conexão TCP.
- O Egress Gateway inicia um SSLRequest.
- O servidor PostgreSQL confirma a possibilidade de transição para TLS.
- Estabelece-se uma sessão TLS.
- Após isso, o StartupMessage é transmitido e a autenticação começa.
Assim, graças ao protocol: postgres, o seguinte modelo se torna possível:
- A aplicação estabelece uma conexão TCP normal (
sslmode=disable). - O sidecar direciona o tráfego através do Egress Gateway.
- O Egress Gateway inicia uma sessão TLS com o PostgreSQL.
- São utilizados certificados gerenciados no nível da infraestrutura.
Agora, voltemos ao nosso exemplo e vejamos como isso se reflete no nível da aplicação. Do ponto de vista da aplicação, a conexão é simplificada:
bashpsql "host=postgres.solution.test user=postgres port=5432 sslmode=disable"
Observe o valor do parâmetro sslmode=disable: a conexão do pod da aplicação é feita com a criptografia explicitamente desativada.
Executaremos o comando e analisaremos o resultado:
bash10001@psql-a-6d5669b5d-ztf4g:/$ psql "host=postgres.solution.test user=postgres port=5432 sslmode=disable"
sqlpostgres=# SELECT ssl, version, cipher FROM pg_stat_ssl WHERE pid = pg_backend_pid();
ssl | version | cipher
-----+---------+-----------------------------
t | TLSv1.2 | ECDHE-RSA-AES256-GCM-SHA384
(1 row)
Uma conexão segura foi estabelecida, apesar de no nível da aplicação o TLS estar explicitamente desativado via sslmode=disable. Ou seja, o proxy sidecar intercepta o tráfego de saída, o direciona através do Egress Gateway, onde a sessão TLS é estabelecida usando os certificados necessários.
[2026-04-23T18:01:09.986Z] "- - -" 0 - - - "-" 89 510 10977 - "" "" "" "" "10.40.20.72:5432" outbound|5432|postgres-internal-tls-origin|postgres.solution.test 172.21.16.23:51452 172.21.16.23:5001 172.21.11.129:39034 –
Registramos os pontos-chave:
outbound|5432|postgres-internal-tls-origin|postgres.solution.test: indica o subset utilizado e confirma que a regra de roteamento correta foi aplicada.172.21.16.23:5001: endereço e porta internos do Egress Gateway pelos quais o tráfego passou.
Adicionalmente, verificaremos um cenário negativo. Para o transporte configurado, tentaremos nos conectar com o nome de um usuário técnico que não corresponde ao Common Name do certificado usado pelo Egress Gateway na autenticação com o servidor SGBD.
bash10001@psql-a-6d5669b5d-ztf4g:/$ psql "host=postgres.solution.test user=kintsugi port=5432 sslmode=disable dbname=postgres"
psql: error: connection to server at "postgres.solution.test" (10.40.20.72), port 5432 failed: FATAL: certificate authentication failed for user "kintsugi"
Neste caso, a conexão não é estabelecida como esperado devido a um erro na verificação do certificado do cliente no lado do PostgreSQL durante o TLS handshake.
Como resultado, a aplicação é completamente liberada das tarefas relacionadas ao TLS: ela não gerencia certificados, não armazena artefatos confidenciais e não participa do estabelecimento de uma conexão segura. Embora no nível do cliente o TLS seja explicitamente desativado via sslmode=disable, na verdade a conexão é estabelecida em modo seguro graças ao fato de que o proxy sidecar redireciona o tráfego para o Egress Gateway, onde a sessão TLS é iniciada com o PostgreSQL. Isso é adicionalmente confirmado pelos logs do Egress Gateway, onde é visível que o tráfego é roteado através do subset apropriado e concluído com o estabelecimento de uma conexão segura. Ao tentar usar um certificado de cliente incorreto, a conexão é rejeitada na etapa de TLS handshake no lado do PostgreSQL, o que confirma a correção do modelo de autenticação.
No entanto, como qualquer solução arquitetural, este esquema tem características e limitações que são importantes considerar durante o projeto.
Ao usar o Egress Gateway como ponto de estabelecimento da conexão TLS, a criptografia é realizada não no lado da aplicação, mas em um nó intermediário, portanto:
- A conexão entre a aplicação e o Egress Gateway pode ser transmitida sem criptografia (dependendo das configurações do Service Mesh).
- O Egress Gateway se torna um ponto confiável com acesso ao tráfego em texto claro.
- O modelo de criptografia de ponta a ponta (end-to-end TLS) é violado.
Em cenários onde dados confidenciais são transmitidos, essa abordagem requer uma avaliação adicional de riscos e, geralmente, é inferior ao modelo clássico de conexão TLS direta da aplicação ao servidor SGBD. Para tais canais, é recomendado usar criptografia de ponta a ponta sem terminação TLS intermediária.
A segunda limitação está relacionada às especificidades do modelo de roteamento no Istio: para um mesmo host:port, apenas uma política de transporte pode ser aplicada. Isso significa que não é possível usar simultaneamente diferentes modos de proxy (por exemplo, diferentes configurações TLS ou protocolos) para o mesmo endpoint de rede. Na prática, isso limita a flexibilidade de configuração e requer a introdução de portas adicionais ou a divisão lógica de serviços no nível da rede.
O exemplo considerado descreve a interação de um serviço com um servidor SGBD. No entanto, em sistemas reais, a situação é geralmente significativamente mais complexa: em um ambiente de nuvem, muitos serviços operam simultaneamente, cada um usando suas próprias credenciais para acessar o banco de dados. Aqui, as limitações da abordagem considerada, relacionadas ao roteamento de tráfego TCP e à escolha de políticas TLS, começam a se manifestar. Na próxima parte, analisaremos detalhadamente quais dificuldades surgem ao escalar essa solução e como elas podem ser contornadas usando os mecanismos de roteamento no Service Mesh.
Todos os exemplos de configurações apresentadas podem ser encontrados em meu repositório. Obrigado pela atenção!
