
Antes do lançamento de um novo serviço, seria bom garantir que ele funcionasse de acordo com nossas expectativas e estivesse disponível, independentemente de quantos clientes o usassem ao mesmo tempo.
E como esse serviço reagirá se um ataque DoS distribuído for organizado contra ele? O recurso está protegido contra possíveis invasores?
Para avaliar possíveis riscos e aumentar a segurança, faz sentido executar ações de forma independente, simulando um ataque DDoS enquanto o recurso ainda não foi lançado para uso em massa.
Neste artigo, falaremos sobre a experiência de organizar o teste de carga para serviços DNS e HTTP.
Preparação
Para provocar a geração de uma enorme quantidade de tráfego de rede no recurso monitorado, você precisa usar muitas máquinas virtuais, cada uma das quais enviará o número máximo de solicitações ao serviço. Se você não possui um data center poderoso, faz sentido alugar temporariamente máquinas virtuais em uma nuvem. Essa ideia tem um recurso: você precisa garantir que a nuvem não rastreie, levando sua atividade para as ações de um invasor.
Comparando as políticas de vários serviços em nuvem (alguém bane impiedosamente a conta com a qual, presumivelmente, foram realizadas as ações que levaram à falha do recurso) em relação à realização de testes de carga usando sua funcionalidade, decidimos parar no Amazon Web Services (AWS). Seus documentos indicam que a AWS permite o teste de carga, mas solicita aprovação enviando um email para um endereço específico.
Harmonização dos testes de estresse
Enviamos uma mensagem onde falamos brevemente sobre nossas intenções e recebemos um formulário que deve ser preenchido:
Customer ID: Customer Name: Email Address: AWS Account ID load test will be performed from: Does the customer have an NDA? Target Data EC2 Resources: Cloudfront Distribution: API Gateway / Lambda ID: ELB Names: Non-AWS Target: Region (please list all regions in scope for testing): Source Data: IP Addresses: Source Account ID: Regions involved: Testing Parameters: How much traffic do you plan to generate by region during testing? What is your expected peak load from source by region? (ie xx Gbps) What is your expected peak load at destination? (ie xx Gbps) Are you testing traffic outbound from EC2, inbound into EC2, or both? Are you testing traffic outbound (egress) from EC2, inbound (ingress) into EC2, or both: Egress. What is your expected peak load from source by region? (ie xx Gbps) Ingress. What is your expected peak load from source by region? (ie xx Gbps) Start Date: End Date: Finite testing details including timeline of testing: Summary of Test: Testing Timelines by phase including rps, pps, and Gbps: Peak bandwidth for each source IP: Tools Used for each phase of test: Types of testing to be performed for each phase of the request: What criteria/metrics will you monitor to ensure the success of this test? Who is performing the Load Test? (Please provide contact details): Does the tester have an NDA? Testing Security Do you have a way to monitor the data traffic for the duration of the test to verify bandwidth limits do not exceed approved rates? Do you have a way to immediately stop the traffic if we/you discover any issue? 2 Emergency contact names and phone numbers:
Existem várias nuances:
Eles nos perguntam quem vamos "curtir". Temos direito a isso? Dizemos que esse é o nosso recurso (aparentemente, ninguém verifica se é assim) e que o teste é totalmente consistente.
Precisamos designar quanto tráfego criaremos em cada uma das regiões. Durante a correspondência, descobrimos que cada região tem seu próprio limite na quantidade de tráfego da rede. No total, eles podem solicitar 645 Gb / s. Consideramos quanto é necessário para o ataque e selecionamos as regiões de maneira a obter o valor necessário.
É necessário descrever a que horas o ataque será realizado, quanto tempo durará e como seu poder aumentará. De forma livre, mas com detalhes suficientes, falamos sobre nossos planos. O ataque é realizado com um aumento gradual de energia; portanto, pintamos quais estágios os testes terão e qual a potência máxima esperada para cada um deles. A data do ataque pode ser omitida por até um dia; é bem possível indicar um intervalo de duas a três semanas.
E, sem falhas, estamos fazendo o possível para garantir que nos comportemos bem, monitoremos cuidadosamente o progresso dos testes e paremos na primeira solicitação, se necessário.
Provavelmente, em resposta ao formulário preenchido, eles solicitarão algum esclarecimento. Por isso, correspondemos e respondemos às perguntas até obtermos permissão para testar.
Demora cerca de três dias úteis para concluir o processo de aprovação, se respondido prontamente.
Preparação de infraestrutura
Após as aprovações, somos confrontados com a necessidade de preparar a infraestrutura para testes. O fato é que, durante a verificação, precisaremos prontamente:
Incluir uma instância;
• iniciar um ataque de teste;
• coletar estatísticas sobre o progresso;
• interrompa o ataque de teste;
Mudar o endereço IP;
• desativar a instância.
Criar imagem de instância
Selecione o tipo de instância
Primeiro, vamos criar uma imagem da AWS que conterá as ferramentas e scripts necessários para o gerenciamento. O primeiro passo é escolher qual instância alugar. Estudamos as características de diferentes tipos de instâncias: analisamos o preço, a quantidade máxima de tráfego, a potência da CPU (a última é importante, porque afinal o tráfego é criado pelas capacidades do processador) e testamos o desempenho real e o número máximo de solicitações. De acordo com nossas estimativas, t3.small
instâncias t3.small
são as mais convenientes para o teste, mas aqui todo mundo escolhe a seu gosto.
Características de instâncias podem ser encontradas aqui . Você também pode selecionar e comparar instâncias aqui .
Solicitação de limite
Você precisa pensar com antecedência sobre quantas instâncias participarão do teste. O fato é que a Amazon fornece para cada região seus limites no número de instâncias. Se você achar que precisará de mais instâncias do que o disponível por padrão, solicite um aumento no limite o mais cedo possível. Para fazer isso, vá para a seção Suporte , crie uma chamada do tipo Aumento do limite de serviço. O tempo de processamento de uma solicitação pode ser diferente: alguém responde no dia seguinte, fornecendo quantas entidades forem solicitadas, alguém diz que não permitirá que mais de N instâncias sejam executadas. Houve regiões que responderam à solicitação por cerca de um mês.
Ajuste de desempenho
Em seguida, você precisa criar uma imagem de instância que será iniciada durante o teste. Para fazer isso, ativamos a instância do tipo selecionado, definimos todas as configurações e salve o que aconteceu como uma imagem (no menu Ações, onde você pode ativar a instância, bem como a funcionalidade para criar uma imagem Criar imagem).
Precisamos obter o tráfego máximo de saída de cada instância. Primeiro, otimizamos as configurações de rede e memória para nossa tarefa na instância.
Para fazer isso, faça as configurações no arquivo /etc/sysctl.conf
:
• Aumente o alcance das portas locais e reduza o tempo gasto pelos soquetes no estado FIN_WAIT:
net.ipv4.ip_local_port_range = 1024-65535 ( : 32768-61000) net.ipv4.tcp_fin_timeout = 10 ( : 60)
O intervalo de portas locais determina o número máximo de soquetes de saída que um host pode criar a partir de um IP específico.
Com a configuração padrão (61.000 a 32.768), são obtidos 28.233 soquetes. Com novas configurações - 64 500.
Fin_timeout define o tempo mínimo em que os soquetes de saída podem estar no estado FIN_WAIT.
Se valores padrão forem especificados, o sistema poderá fornecer não mais que (61.000–32.768) / 60 = 470 soquetes por segundo.
Ao aumentar o intervalo de portas e diminuir o fin_timeout, podemos afetar a capacidade do sistema de gerar mais conexões de saída.
• Vamos reutilizar soquetes no estado TIME_WAIT quando os livres terminam:
net.ipv4.tcp_tw_reuse = 1
A configuração da opção acima (que está desativada por padrão) ajuda a minimizar a perda de conexões "inativas" já preenchidas.
Muito detalhado sobre TIME_WAIT está descrito neste artigo .
• Ative a opção tcp_timestamps para que a opção tcp_tw_reuse acima funcione:
net.ipv4.tcp_timestamps = 1 – `tcp_timestamps` tcp_tw_reuse
• Outras opções:
net.ipv4.tcp_max_tw_buckets = 720000 – TIME_WAIT net.ipv4.tcp_keepalive_time = 600 – - keepalive- net.ipv4.tcp_keepalive_probes = 3 – keepalive- net.ipv4.tcp_keepalive_intvl = 10 – keepalive- net.ipv4.tcp_window_scaling = 1 – TCP- net.ipv4.tcp_mem = 8192 131072 196304 – TCP- net.ipv4.udp_mem = 8192 131072 196304 – udp- net.ipv4.tcp_slow_start_after_idle=0 – Slow-Start Restart net.core.wmem_default = 31457280 – net.core.wmem_max = 33554432 – net.core.somaxconn = 65535 – net.core.netdev_max_backlog = 65535 – vm.swappiness = 30 – vm.dirty_ratio = 50 – 50 % vm.pagecache = 90 –
Testar scripts de ataque
1. ataque de DNS
Uma das principais tarefas do teste é avaliar o desempenho dos servidores DNS. Ou seja, os servidores DNS podem se tornar o gargalo da tolerância a falhas de um site, porque mesmo se o serviço mais estável estiver indisponível, se houver problemas com o DNS. Para criar uma carga nos servidores DNS, geraremos muitas consultas DNS diferentes. As solicitações devem ser válidas e exigir a maior resposta possível e mais longa do servidor DNS.
O utilitário DNSPerf é adequado para gerar esse tráfego.
DNSPerf é uma ferramenta de teste de desempenho DNS simples, flexível e gratuita. Ele foi desenvolvido principalmente para servidores DNS autoritativos, mas também pode ser usado para medir o desempenho de servidores de cache.
No nosso caso, são carregados servidores DNS autoritativos que atendem a uma zona - exemplo.com.
Para DNSPerf, primeiro preparamos um arquivo com as solicitações dns_queries.txt
(principalmente QUALQUER para aumentar o tempo e o tamanho da resposta do servidor DNS):
#dns_queries.txt example.com ANY www.example.com ANY test.example.com ANY static.example.com ANY example.com www.example.com test.example.com MX
Exemplo de execução do utilitário:
dnsperf -s TARGET_IP -d dns_queries.txt -c 100 -n 100 -s = IP- -d = . – stdin -c = . -n = «» .
2. Ataque ao ICMP
O próximo estágio do teste é avaliar a resistência a um grande número de tráfego ICMP. Como, por razões técnicas, os servidores geralmente precisam responder a solicitações de ping, existe a possibilidade de um ataque DDoS usando solicitações de ping. Além de especificar configurações que excluam a possibilidade de ping-to-death
, você precisa garantir que os servidores sejam resistentes ao pico de cargas de ICMP. Para criar essas cargas, é melhor usar o conhecido utilitário hping3 , que permite ajustar o número de solicitações, o intervalo entre as remessas e o tamanho do pacote.
Exemplo de execução do utilitário:
hping3 -i u1000 -d 1500 -c 100000 -1 TARGET_IP -i u100 = (uX for X microseconds) -d 1500 = -c 1000000 = -1 = ICMP
3. ataque HTTP
Agora, verificamos quanto à resistência ao estresse a principal funcionalidade do tráfego HTTP (S) de processamento de serviço. Uma das ferramentas mais flexíveis e fáceis de gerar tráfego HTTP é o cerco . O Siege é um utilitário multithread de código aberto desenvolvido para testar o desempenho de um recurso da Web.
Como o DNSPerf, o cerco permite carregar o servidor com solicitações de um determinado número de usuários virtuais (a emulação de usuário é realizada usando uma porta separada), além de usar um conjunto predefinido de solicitações. Isso é muito conveniente, pois você pode incluir as consultas com mais recursos no teste.
Exemplo de execução do utilitário:
siege -b -c 100 -f test_urls.txt -b = ( benchmark) -c = . -f =
Formato do conteúdo test_urls.txt
:
http://example.com/site/login POST login=username&password=test http://example.com/site/client POST useragent=Mozilla&version=123&date=24May http://example.com/site/order POST user=username&company=ooo&phone=812345678
Como você pode ver, os testes foram realizados usando principalmente solicitações POST que requerem processamento no lado do servidor e ocupam o maior número de recursos em comparação com outros tipos de solicitações.
Nenhuma das opções usa falsificação de IP, pois a Amazon não permite isso. Qualquer que seja o src_IP especificado no pacote, ele será alterado para o correto ao sair da instância.
Todas as solicitações geradas devem ser legítimas - nenhuma onda de tráfego de saída é respondida -, pois a política DDoS da Amazon é bastante rigorosa. Até um teste de estresse coordenado leva pelo menos alguns dias para se comunicar com o suporte técnico e, durante as primeiras ações "maliciosas", obtemos a proibição da porta da qual o tráfego saiu e o requisito a ser explicado imediatamente.
Scripts para iniciar ataques
Para o gerenciamento de teste remoto, prepararemos scripts bash (dns.sh, ping.sh, http.sh) que iniciam o tipo de ataque desejado e arquivos de carga útil (test_urls.txt, valid_dns_queries.txt).
Quando fazemos upload de tudo isso para a imagem da AWS (a partir da qual todas as instâncias serão criadas), cada teste pode ser executado remotamente usando o comando do seguinte formato:
ssh instance-amazon 'sudo <stress-script>.sh start <params> &>>stress.log &'
O script do tipo requerido é especificado como stress-script.sh, e params são os parâmetros correspondentes. No arquivo stress.log, rastrearemos a saída do utilitário em execução. Por conveniência, usaremos diferentes logs para diferentes utilitários: dns.log, ping.log, http.log.
Exemplo de script dns.sh
:
Como pode ser visto no código, o script pode ser iniciado e interrompido, além de verificar o status (em execução / não em execução).
Os scripts para testes ICMP e HTTP são construídos da mesma maneira, iniciando hping3 e siege, respectivamente, com a sequência de parâmetros passada pelo argumento.
Exemplos de comandos:
ssh instance-amazon 'sudo dns.sh start -s TARGET_IP -d valid_dns_queries.txt -c 1 -n 100 &>>dns.log &' ssh instance-amazon 'sudo ping.sh start -i u1000 -d 1500 -c 100000 -1 TARGET_IP &>>ping.log &' ssh instance-amazon 'sudo http.sh start -b -c 100 -f test_urls.txt &>> http.log &'
Scripts de monitoramento
Para avaliar o tráfego de saída e o estado da infraestrutura de teste, precisamos de uma ferramenta de monitoramento. Por razões de simplicidade e economia de recursos, usamos iptables. Para fazer isso, escreveremos um script que conte o número de MBs enviados e o colocaremos na imagem da AWS:
#iptables.sh sudo iptables -N TRAFFIC_OUT sudo iptables -A TRAFFIC_OUT -p tcp sudo iptables -A TRAFFIC_OUT -p udp sudo iptables -A TRAFFIC_OUT -p icmp sudo iptables -A OUTPUT -j TRAFFIC_OUT sudo iptables-save
O script cria uma nova cadeia TRAFFIC_OUT e adiciona filtros para os protocolos necessários: tcp, udp, icmp.
O encaminhamento de pacotes para TRAFFIC_OUT é adicionado à cadeia OUTPUT.
A quantidade de dados transferidos pode ser encontrada pelo comando:
# iptables -L TRAFFIC_OUT -v -n -x | tail -n 3 | awk '{print $2/1024/1024,"Mb\t\t\t",$3}' : 2.2 Mb tcp 4.4 Mb udp 3.2 Mb icmp
Instale o script como um serviço. Para fazer isso, crie um arquivo Monitoring.service e mova-o para o diretório /etc/systemd/system
da nossa imagem:
# /etc/systemd/system/monitoring.service [Unit] After=network.target [Service] ExecStart=/usr/local/bin/monitoring.sh [Install] WantedBy=default.target
Agora você pode adicionar o serviço à inicialização:
systemctl enable monitoring.service systemctl start monitoring.service
Gerenciamento de instância
Agora, vamos lidar com o gerenciamento de instâncias remotas (o mais automatizado possível).
Para esses fins, você pode usar o mecanismo da AWS CLI - gerenciamento do console.
Crie uma chave secreta (teclas de acesso (ID da chave de acesso e chave de acesso secreta)) e configure o console.
Agora temos acesso a todos os recursos da conta.
A peculiaridade de trabalhar com a AWS é que todas as ações são executadas para uma região específica e devem ser repetidas se várias regiões estiverem envolvidas.
Para criar uma nova instância a partir da imagem que criamos acima (presumimos que exista um ami-ID público que usamos no script), faremos o seguinte:
- crie uma chave SSH e adicione-a à AWS:
yes n |ssh-keygen -q -t rsa -f $KEYNAME -m pem -N "" > /dev/null chmod 400 $KEYNAME aws ec2 import-key-pair --region $REGION --key-name $KEYNAME --public-key-material file:///$(pwd)/$KEYNAME.pub
- crie um grupo de segurança que permita o acesso à máquina via SSH. Caso contrário, as conexões SSH recebidas serão negadas:
SECURITY="ssh-group" aws ec2 create-security-group --region $REGION --group-name $SECURITY --description "Just ssh. Nothing more" IP_RANGE="0.0.0.0/24" aws ec2 authorize-security-group-ingress --region $REGION --group-name $SECURITY --protocol tcp --port 22 --cidr $IP_RANGE
- crie uma instância com a chave e o grupo de segurança criados anteriormente e especifique o ID da imagem. O número de instâncias criadas ao mesmo tempo pode ser arbitrário:
IMG='ami-0d0eaed20348a3389' NUM=1 aws ec2 run-instances --region $REGION --image-id $IMG --count $NUM --instance-type t2.micro --key-name $KEYNAME --security-groups default $SECURITY > instances.json
- aguarde até a máquina ser inicializada. Isso leva algum tempo: primeiro obtemos uma resposta bem-sucedida (instance.json), mas naquele momento a máquina foi criada, mas ainda não foi iniciada (por exemplo, ainda não foi atribuído um endereço IP). É necessário aguardar a conclusão do lançamento (geralmente um minuto é suficiente para isso).
Então você pode se conectar via SSH se soubermos o endereço IP. Basta solicitar uma lista de máquinas que estão em execução no momento. Entre seus parâmetros, encontramos PublicDnsName ou PublicIpAddress.
aws ec2 describe-instances --region
Em seguida, executamos os comandos SSH, indicando a chave SSH criada acima:
ssh -I $KEYNAME -oStrictHostKeyChecking=no ubuntu''+ins_dns echo''O''
Os comandos SSH permitem controlar o ataque e obter todas as informações sobre o estado do ataque, pois fornecemos às instâncias todos os scripts e ferramentas necessários.
Você precisa entender que a maioria das defesas contra ataques de negação de serviço bloqueiam o endereço IP do qual são recebidas anormalmente muitas solicitações. Portanto, os endereços IP das máquinas virtuais devem mudar constantemente para manter o poder de ataque.
A AWS atribui um novo endereço IP sempre que a máquina é iniciada. Portanto, para alterar o IP, é necessário desligar e ligar a máquina novamente (não é necessário removê-la!).
A diferença entre desligar e excluir é que enviamos sinais diferentes. Parar - para desligar, encerrar - para desligar e excluir imediatamente.
Para monitorar o tráfego de entrada de uma instância, usamos o seguinte comando com o ID da instância: quando a medição de tráfego começa, quando termina, por qual período os valores são somados:
aws cloudwatch get-metric-statistics --region REGION --namespace AWS/EC2 \ --statistics Sum --metric-name NetworkIn --start-time $STARTTIME --end-time $FINISHTIME --period $PERIOD --dimensions Name=InstanceId,Value=$INCTANCEID
Monitoramento de disponibilidade de serviço
Além disso, para realizar um ataque, você precisará observar se o serviço que estamos testando está ativo.
Criamos e executamos o script "ping" mais simples que monitora a disponibilidade das portas de destino (53 e 80 no nosso caso).
Exemplo de código Python que automatiza a verificação de disponibilidade:
def http(url): cmd = ['curl', '-w', '"%{time_total}"', '-o', '/dev/null', '-s', url] result = check_output(cmd).decode('utf-8') result = float(json.loads(result)) return result * 1000000 def dns(ip, domain): cmd = ['dig', 'any', '@'+ip, domain ] result = check_output(cmd).decode('utf-8') result = int(result.split('Query time:')[1].split('msec')[0]) return result
As informações recebidas são armazenadas em um arquivo de log, com base no qual, com base nos resultados do ataque, será possível construir um gráfico da disponibilidade de recursos.
Durante o teste, é necessário verificar constantemente o log “ping” para não matar o recurso de forma completa e irrevogável. Assim que uma degradação significativa aparecer e a resposta à solicitação demorar muito, o ataque deverá ser interrompido.
Se a desaceleração for insignificante e o poder de ataque atingir o máximo estabelecido, faz sentido esperar um ou dois minutos e, se o serviço continuar funcionando sem interrupções, a verificação será considerada bem-sucedida.
Questão financeira
Vale a pena discutir outra questão relacionada à organização dos testes - o custo de todo esse evento.
A Amazon fornece informações detalhadas sobre preços, mas você precisa entender que precisa pagar por quase tudo. No entanto, muitos cálculos podem ser negligenciados. Antes de tudo, vale a pena calcular o custo do tráfego (depende da região e de quanto a quantidade total de informações será transmitida) e o custo do aluguel de instâncias (pagamento por minuto). Esses itens representam aproximadamente 99% do custo de todo o ataque.
Portanto, o custo do ataque é calculado em cada caso separadamente, dependendo do poder máximo de ataque da [escala de hostilidades] e do número de lançamentos planejados.
Do ponto de vista da simplificação dos cálculos, é melhor usar uma conta da Amazon, registrada há mais de um ano. Então parte das operações será gratuita. Leia mais sobre os limites de uso gratuito aqui .
Para ilustrar o cálculo do custo da realização de testes de carga, digamos que queremos verificar a estabilidade do servidor DNS para uma carga de 10 Gb / s.
Sabemos que as ferramentas usadas e os recursos da instância t3.small lançada em Mumbai permitem emitir 500 Mb / s em uma instância em execução. O preço para alugar uma entidade é de US $ 0,0224 por hora, para tráfego - US $ 0,01093 por 1 GB. Ou seja, o pico do ataque significa a operação simultânea de 20 entidades.
Aumentaremos o poder de ataque gradualmente; para isso, primeiro lançamos uma entidade e depois adicionamos outra a cada 60 s.
A fórmula para calcular o custo assume a forma:
60 * ( ) + 60 * 0,5 /c * ( ) = 60 . 1 * ( ) + 2 * ( ) + ... + 20 * ( ) =
Acontece que o custo de um ataque com capacidade de 10 Gb / s para um servidor DNS é de aproximadamente US $ 70. Observe que essa é uma estimativa aproximada, pois o volume de tráfego não pode ser previsto com precisão. . , – , .
. .