BLOG

Superando as limitações do Service Mesh para a segurança de clusters de aplicativos distribuídos

Ankur Singla Miniatura
Ankur Singla
Publicado em 13 de abril de 2020

Este é o quarto blog de uma série de blogs que abordam vários aspectos do que foi necessário para construir e operar nosso serviço SaaS :

  1. Plano de controle para Kubernetes PaaS distribuído
  2. Malha de serviço global para aplicativos distribuídos
  3. Segurança de plataforma para infraestrutura distribuída, aplicativos e dados
  4. Aplicação e segurança de rede de clusters distribuídos
  5. Observabilidade em uma plataforma distribuída globalmente
  6. Operações e SRE de uma plataforma distribuída globalmente
  7. Estrutura de serviço Golang para microsserviços distribuídos

No blog anterior , fornecemos insights sobre o desafio de usar técnicas criptográficas para proteger nossa plataforma (infraestrutura, aplicativos e dados). Este blog abordará técnicas que usamos para proteger a plataforma contra ataques direcionados da rede — tanto da Internet quanto de dentro dela. Como os aplicativos não estão mais restritos a nenhum local físico, os firewalls tradicionais baseados em perímetro e as soluções de segurança baseadas em assinatura não são mais eficazes. Descreveremos as deficiências da nossa implementação inicial de segurança de confiança zero e por que + como a ampliamos com técnicas algorítmicas e de aprendizado de máquina para proteger adequadamente nossa infraestrutura distribuída + clusters de aplicativos.

TL;DR (Resumo)

  1. Nossos requisitos de segurança de aplicativo para aplicativo e de usuário para aplicativo foram complicados pelo fato de nossa plataforma estar executando centenas de microsserviços (em muitos clusters do Kubernetes) junto com alguns aplicativos monolíticos em vários provedores de nuvem (AWS e Azure), nossa rede global e locais de ponta.
     
  2. Também fomos solicitados a criar processos e fornecer uma solução robusta de segurança de rede + aplicativo que desse acesso seletivo à infraestrutura compartilhada para nossos desenvolvedores, nossas equipes de operações, os desenvolvedores de nossos clientes, suas equipes de operações e seus usuários finais. Isso ficou ainda mais complicado pelo fato de termos que atender a requisitos de conformidade como PCI-DSS, SOC, etc.
     
  3. Embora já tivéssemos criado uma solução de confiança zero com base em nossa malha de serviços global e gateway de API, ela não resolveu muitos problemas de segurança, como vulnerabilidade do sistema, exaustão de recursos, ataques de bots/malware na Internet e as necessidades de alguns serviços que forneciam acesso não autenticado. Além disso, às vezes era difícil para nossa equipe de secOps obter informações de API dos desenvolvedores, o que é essencial para uma boa implantação de confiança zero que depende de listas de permissões de acesso.
     
  4. Atender a essas necessidades exigiria que nossa equipe de plataforma adquirisse serviços de fornecedores/provedores de nuvem ou aumentasse nosso caminho de dados L3-L7+ (detalhado aqui ) com recursos de segurança adicionais — firewall de rede, firewall de aplicativo, proteção DDoS, gerenciamento de acesso privilegiado, etc. Dado o fato de que nenhum fornecedor/provedor de nuvem existente poderia resolver esses problemas com ferramentas que fornecessem políticas unificadas, observabilidade e recursos de descoberta de API automatizada, tivemos que tomar a difícil decisão de embarcar em um projeto interno para aumentar nosso caminho de dados e plano de controle.
     
  5. Como parte da construção dos recursos de segurança de rede e aplicativo no caminho de dados, acabamos adicionando muitos recursos novos — algo que ainda não foi alcançado em nenhum caminho de dados de rede até o momento. Combinamos recursos de segurança algorítmica com aprendizado de máquina em tráfego de rede e aplicativo, juntamente com um mecanismo de política programável que funciona em toda a pilha — rede, HTTP e APIs. Como resultado, agora podemos fornecer diferentes recursos de segurança dependendo do ambiente de implantação, do modelo de tráfego e das necessidades de latência. Isso tornou nossa malha de serviços global mais segura, reduziu o número de falsos positivos para nossa equipe SOC e proporcionou uma redução significativa na latência e nos recursos de computação.

Limitações da Service Mesh e da Arquitetura Zero-Trust

Nossa plataforma executa um grande número de aplicativos em várias equipes que operam seus próprios clusters no Edge, em nossa rede global e nas nuvens públicas AWS e Azure. Embora a maioria das cargas de trabalho sejam orquestradas por microsserviços usando o Kubernetes, temos alguns monólitos de grande escala (por exemplo, Elasticsearch) que gerenciamos usando o Terraform. A Figura 1 demonstra a natureza distribuída da nossa plataforma. Por exemplo, em cada um dos nossos mais de 18 PoPs de rede global (com algumas dezenas a pouco mais de cem servidores físicos), executamos milhares de pods de aplicativos. No entanto, no limite, temos implantações de clientes individuais hoje com mais de 3.000 locais ativos (cada um com de um a sete computadores) executando algumas dezenas de pods de aplicativos.

limites de malha 1
Figura 1: Aplicativos e dados distribuídos na plataforma Volterra

A plataforma é totalmente multilocatária, com cada nó executando cargas de trabalho de diferentes clientes (e os nossos). Como alguns desses aplicativos são expostos à Internet pública, precisamos garantir que toda a comunicação de/para os aplicativos seja protegida. Conforme descrevemos nos dois blogs anteriores, construímos um sistema robusto de identidade, autenticação e autorização, juntamente com nosso próprio caminho de dados de rede L3-L7+ (VoltMesh), que é usado para alimentar nossa malha de serviços e gateway de API . Conforme mostrado na Figura 2, isso nos permitiu fornecer segurança em nível de transporte em clusters de aplicativos (mTLS), de usuários (TLS/mTLS) e funcionários (mTLS), bem como controle de acesso baseado em autenticação+autorização.

limites de malha 2
Figura 2: Segurança de transporte com Service Mesh e API Gateway

Embora essa implementação de confiança zero ofereça muitos benefícios, ela não resolveu automaticamente vários problemas de segurança:

  1. Vulnerabilidade do sistema — Um aplicativo pode ser comprometido devido a uma vulnerabilidade do sistema ou a um funcionário autorizado, mas mal-intencionado. É necessário um mecanismo para detectar tais situações, tomar medidas corretivas automaticamente e alertar nossas equipes de SRE.
     
  2. Esgotamento de recursos — Há muitas situações em que até mesmo o acesso legítimo e autorizado do cliente pode esgotar lentamente os recursos do aplicativo devido a um design ruim. Essas situações podem prejudicar significativamente o desempenho de outros usuários.
     
  3. Ataques na Internet — Como grande parte da nossa infraestrutura e aplicativos (e dos nossos clientes) estão diretamente expostos à Internet pública, esses recursos sofrem ataques contínuos de usuários mal-intencionados, bots e malware. Precisamos detectar e proteger esses recursos contra ataques de negação de serviço, volumétricos e de intrusão para fornecer um bom tempo de resposta a outros usuários.
     
  4. Serviços não autenticados — Embora a maioria dos aplicativos exija autenticação (também necessária para confiança zero), há casos em que um aplicativo não pode ser restringido. Como resultado, esses aplicativos exigem proteção adicional usando soluções baseadas em rede.

Nos últimos 2,5 anos de desenvolvimento nesta plataforma, também percebemos que, muitas vezes, nossos desenvolvedores incorporam aplicativos de código aberto, os colocam em contêineres e pedem para nossa equipe de DevOps implantá-los na plataforma. No entanto, muitas vezes não há detalhes sobre as interações no nível da API dentro desses aplicativos, que são necessários para que nossa equipe de segurança crie políticas para colocar a comunicação na lista de permissões. Este é um grande obstáculo para nossa implementação de segurança de confiança zero, pois exige políticas de lista de permissões que permitem apenas APIs usadas pelos aplicativos e bloqueiam todo o outro tráfego. Sempre que fazíamos exceções a esse requisito, alguns aplicativos ficavam com segmentação muito básica no nível da rede, aumentando assim a superfície de ataque.

Aumentando nossa malha de serviços de confiança zero

Como resultado, precisávamos ampliar nossa solução de segurança de confiança zero existente com recursos de segurança adicionais para lidar com os problemas listados acima. Identificamos uma lista de recursos de segurança adicionais que precisávamos incorporar à plataforma:

  1. Descoberta e controle de API — Desenvolva a capacidade de aprender APIs em aplicativos em execução e automatize a geração de políticas de lista de permissões de aplicativos/APIs sem depender de desenvolvedores
  2. Detecção de anomalias — Capacidade de detectar e proteger contra comportamento anômalo para tráfego de qualquer tipo de fonte (usuário/aplicativo confiável ou não confiável)
  3. Criação de perfil de comportamento — Proteja os aplicativos contra exaustão de recursos, ataques de aplicativo/API (Internet ou rede interna) e proteja contra ataques volumétricos da Internet para a infraestrutura e/ou aplicativos
  4. Registro e visualização — Capacidade de registrar e rastrear todos os acessos para futuras necessidades forenses e/ou de conformidade

Decidimos usar uma combinação de técnicas tradicionais baseadas em assinaturas, algoritmos estatísticos e abordagens mais dinâmicas de aprendizado de máquina para resolver esses problemas. Isso exigiu que fizéssemos alterações em nosso backend SaaS, bem como adicionássemos novos recursos em nosso caminho de dados de rede.

Aprendizado profundo para descoberta e controle de API

Para bloquear a plataforma, permitimos apenas conexões de rede com base na lista de permissões de APIs para cada aplicativo. Isso requer que nossa equipe de segurança coordene com os desenvolvedores e garanta que nosso mecanismo de política programável seja alimentado com as informações corretas da API. Rapidamente percebemos que era impossível para nossos desenvolvedores fornecer essas informações para aplicativos que não foram criados usando nossa estrutura de serviço.

Como nosso proxy de malha de serviço está no caminho de rede de cada acesso ao aplicativo, decidimos aprender APIs e recursos estáticos que são expostos pelo aplicativo fazendo análises de tempo de execução de cada acesso que passa pelo proxy. O desafio dessa abordagem é identificar os endpoints da API inspecionando URLs e separando os componentes que são gerados dinamicamente. Por exemplo, para uma API “api/user/<user_id>/vehicle/”, o proxy verá acessos como:

Pode haver milhões de solicitações desse tipo, o que as torna muito difíceis de decifrar. Como resultado, a identificação de componentes dinâmicos nessas solicitações relacionadas é feita usando aprendizado profundo e análise de gráficos. Representamos todo o conjunto de componentes de URL como um gráfico e, em seguida, realizamos o agrupamento de gráficos para encontrar subgráficos com propriedades semelhantes usando conjuntos de recursos que capturam propriedades específicas de componentes gerados dinamicamente, como:

  • Propriedades estruturais
  • Entropia dos nós
  • Semelhança de Jaccard entre várias partes do gráfico
  • Classificação simples de strings usando métodos de aprendizado profundo bem compreendidos

Como resultado, os componentes dinâmicos são classificados e a saída do sistema se parece com:

Usando esse aprendizado de máquina de APIs, podemos gerar de forma fácil e automática uma política que pode ser aplicada pelo nosso proxy de malha de serviço. Com base nos endpoints de API descobertos, também aprendemos outras propriedades, como quais aplicativos usam quais APIs para se comunicar com outros aplicativos, o comportamento típico dessas APIs, etc. Isso nos permite criar um gráfico de serviço que ajuda nossa equipe de segurança a visualizar a interação entre serviços para análise forense, descoberta e microssegmentação em nível de API.

Identificando deficiências em firewalls

Antes de começarmos a adicionar os dois recursos restantes (detecção de anomalias e criação de perfil de comportamento), decidimos ver se as soluções existentes poderiam nos ajudar. Embora existam muitos firewalls de perímetro e produtos de firewall de aplicativos da web no mercado, a maioria dessas soluções é voltada para a proteção de aplicativos voltados para a Internet. Eles fazem certas suposições de que o tráfego atendido é tráfego da web e fornecem proteção direcionada para HTML, javascript, sql, CMS, etc. — tornando relativamente mais fácil escrever assinaturas e regras para detectar vulnerabilidades e explorações conhecidas.

Embora esse recurso seja importante para nosso tráfego da web, também precisamos atender a uma quantidade crescente de tráfego de API e máquina a máquina em nosso ambiente. Para resolver isso, nossa equipe de segurança teria que escrever regras específicas para aplicativos que não se enquadrassem nas regras típicas da web conhecidas (como OWASP CRS). Normalmente, os administradores de segurança sabem pouco sobre os aplicativos e, com a natureza dinâmica do ambiente, fica ainda mais difícil controlar os tipos de aplicativos e a estrutura para escrever regras específicas para cada aplicativo. Como resultado, embora nossa equipe de plataforma forneça esse recurso em nosso caminho de dados de rede, ele não é usado com frequência por nossa equipe de segurança.

Outro problema para o qual temos uma quantidade significativa de dados em nossa rede é que os ataques a aplicativos estão se tornando muito mais sofisticados ao longo do tempo. O invasor passa dias realizando reconhecimento para determinar os detalhes das APIs, do aplicativo, da infraestrutura subjacente e do tipo de sistema operacional, observando assinaturas HTTP/TCP, etc. As abordagens tradicionais baseadas em assinaturas e regras são de uso muito limitado nessas situações e decidimos continuar com nossa abordagem baseada em IA para aprender automaticamente o comportamento do usuário e impor o comportamento bom e o ruim.

Aprendizado de máquina para criação de perfil de comportamento

A maioria dos aplicativos tem determinados fluxos de trabalho (sequência de APIs) e contexto (dados dentro das APIs) para os quais diferentes casos de uso/implantações são projetados e normalmente seguidos pelos usuários dos aplicativos. Exploramos essas propriedades e treinamos nossos algoritmos de aprendizado de máquina para modelar padrões comportamentais “válidos” em uma interação típica do usuário com o aplicativo.

Nosso datapath coleta amostras de solicitações/respostas para cada API, juntamente com os dados associados, e os envia para nosso mecanismo de aprendizado central, conforme mostrado na Figura 3. Este mecanismo gera e atualiza continuamente o modelo de padrões comportamentais válidos que são então usados pelo mecanismo de inferência em execução no caminho de dados para alertar/bloquear comportamentos suspeitos.

limites de malha 3
Figura 3: Interação entre o Learning Core e os motores de inferência distribuídos

O mecanismo de aprendizado analisa muitas métricas, como a sequência de APIs, lacunas entre solicitações, solicitações repetidas para as mesmas APIs, falhas de autenticação, etc. Essas métricas são analisadas para cada usuário e de forma agregada para classificar o comportamento bom do ruim. Também realizamos agrupamentos de comportamento para identificar várias sequências diferentes de “bom comportamento”. Vamos dar um exemplo para ilustrar isso:

  • O modelo faz uma análise entre muitos usuários para definir uma sequência de bom comportamento. Cada cor representa chamadas de API individuais diferentes:
limites-de-malha-4
Figura 4: Sequência normal de APIs

    A sequência de APIs a seguir será sinalizada pelo sistema como comportamento suspeito/ruim que será automaticamente mitigado pelo sistema ou gerará um alerta para um administrador intervir

limites de malha 5
Figura 5: Sequência suspeita de APIs

À medida que colocamos esse sistema em produção há mais de um ano, refinamos continuamente o modelo com base no uso e no feedback dos clientes. Conseguimos identificar com sucesso os seguintes tipos de ataques: 

  • Crawlers/scanners que fazem o reconhecimento de um aplicativo para encontrar vulnerabilidades e também para criar um mapa de todas as APIs expostas pelo aplicativo
     
  • Atividade maliciosa sustentada por meio do lançamento de vários vetores de ataque a APIs vulneráveis
     
  • Ataque de negação de serviço em nível HTTP que leva ao esgotamento de recursos (por exemplo, várias tentativas de login em APIs)
     
  • Vazamento de dados por bots projetados para coletar informações (por exemplo, informações de preços de um site de comércio eletrônico por concorrentes)
     
  • Ataques de força bruta — ataques de dicionário para login/senhas
     
  • Identificação de bons bots (por exemplo, Google, Bing crawler)

Dito isso, também percebemos que há alguns problemas com essa abordagem: ela não consegue descobrir ataques lentos e de baixa intensidade (força bruta, negação de serviço de aplicativo, scanner), para os quais precisamos aplicar técnicas de detecção de anomalias.

Detecção de anomalias algorítmicas e baseadas em IA

Às vezes, vemos ataques altamente sofisticados que usam grandes botnets distribuídas que passam despercebidos pela nossa técnica de análise comportamental. Exemplos de tais ataques são: 

  • Ataque de força bruta distribuída
  • Ataques de aquisição de conta baseados em dicionário distribuído
  • Scanner distribuído para encontrar vulnerabilidades e explorá-las de vários clientes
  • Ataque HTTP DoS de uma botnet altamente distribuída
  • Botnet visando um pequeno subconjunto de APIs com uso intensivo de computação
  • Botnet visando um grande conjunto de APIs com muitos dados para esgotar recursos

Como nosso caminho de dados de rede coleta informações de cada nó em nossa rede global, fica relativamente fácil realizar análises nas métricas agregadas de um aplicativo específico, como taxa de solicitação, taxa de erro, rendimento de resposta, etc. Essa análise nos permite detectar ataques distribuídos e mitigá-los (em cada nó) identificando os usuários que podem fazer parte dessas botnets. Vamos dar um exemplo em que estamos tentando detectar anomalias em diferentes janelas de tempo (últimos 5 minutos, 30 minutos, 4 horas, 24 horas) observando as taxas de solicitação e, se a taxa de solicitação for alta em uma determinada janela de tempo, a seguinte análise mais profunda dos logs de acesso será realizada pelo sistema: 

  • Analise se a dispersão do endereço IP de origem é maior ou menor que o normal. Se as solicitações vierem de um grande número de IPs de origem exclusivos, isso indica um ataque altamente distribuído que acionará uma versão mais agressiva da análise do comportamento do usuário em cada IP de origem.
  • Execute uma versão mais agressiva da análise do comportamento do usuário em cada um dos endereços IP de origem para atribuir pontuações de suspeita e executar as ações de mitigação configuradas.
  • Se a maioria das solicitações foi para um número menor de endpoints de API (do que o normal), isso indica um ataque de força bruta ou ataque DoS. Se a propagação do APIEP for maior do que o normal, isso indica um rastreador distribuído, scanner ou ataque DoS

Embora a detecção de anomalias sempre tenha sido uma técnica importante para detecção e prevenção de intrusão (IDS/IPS) em dispositivos de firewall, esses dispositivos não conseguem mitigar ataques globais na camada de aplicativo. Com nossa capacidade de executar marcação e aprendizado de API em nossa plataforma global, agora podemos suprimir ataques na fonte em nossa rede distribuída.

Ganhos do aprendizado de máquina para segurança de aplicativos e redes

Embora estivéssemos extremamente satisfeitos com nossa implementação de confiança zero baseada em malha de serviço e gateway de API, percebemos que ela não era abrangente para proteger clusters de aplicativos distribuídos contra vulnerabilidades e ataques maliciosos. Tivemos que aumentá-lo com aprendizado de máquina para análise de comportamento + detecção de anomalias, juntamente com técnicas tradicionais baseadas em assinatura e regras para fornecer uma melhor solução de segurança.

Vimos três ganhos significativos com a adição de inferências distribuídas em nosso caminho de dados de rede L3-L7+, juntamente com o núcleo de aprendizagem em execução em nosso SaaS centralizado: 

  1. Rede de confiança zero de ponta a ponta — agora temos a capacidade de aplicar microssegmentação em nível de API em toda a plataforma (100% sem lacunas) — toda a rede é um proxy distribuído globalmente com acesso em nível de API e acesso zero em nível de rede.
     
  2. Redução de falsos positivos (em >82%) por meio da capacidade de ajustar continuamente nossos modelos. Reduzimos significativamente o número de falsos positivos com os quais nossas equipes NOC e SOC precisam lidar. Essa era uma grande preocupação com as ferramentas tradicionais, dado o número de alarmes que elas geravam, tornando-as totalmente inúteis.
     
  3. Redução significativa na latência e na utilização de computação ao migrar lentamente mais tarefas que eram executadas por regras tradicionais e algoritmos baseados em assinatura para nosso novo núcleo de aprendizado de máquina.

A segurança de redes e aplicativos é uma área em constante evolução e parece que ainda temos um longo acúmulo de novos recursos para adicionar. Voltaremos em um futuro próximo para compartilhar insights adicionais sobre os algoritmos e técnicas incrementais que implementamos.

Continua…

Esta série de blogs abordará vários aspectos do que foi necessário para construir e operar nosso serviço SaaS distribuído globalmente com muitos clusters de aplicativos em nuvens públicas, nossos PoPs de rede privada e sites de ponta. O próximo será “Observabilidade em nossa plataforma globalmente distribuída” (em breve)…