AWS: VPC Flow Logs – знайомство та аналітика з CloudWatch Logs Insights

Автор |  10/06/2022
 

AWS VPC Flow Logs – сервіс Amazon, який дозволяє логувати інформацію про трафік між мережевими інтерфейсами у AWS VPC. Далі, ці логи можуть бути передані у AWS CloudWatch Logs для подальшого аналізу, при цьому логування трафіку ніяк не впливає на швидкість роботи мережі.

Коротко розглянемо основні поняття, доступні опції та налаштуємо Flow Logs для VPC з передачею даних для аналізу у CloduWatch Logs.

Опис VPC Flow Logs

Логи можуть бути включені для цілої VPC, підмережі, або конкретного інтерфейсу. При включенні для всієї VPC – логування буде включено для всіх мережевих інтерфейсів цієї мережі.

Сервіси, для яких можна використовувати Flow Logs:

  • Elastic Load Balancing
  • Amazon RDS
  • Amazon ElastiCache
  • Amazon Redshift
  • Amazon WorkSpaces
  • NAT gateways
  • Transit gateways

Дані будуть записані у вигляді flow log records, які представляють собою текстовий запис з заданними полями.

Use Cases – приклади використання

Що можна відстежити за допомогою Flow logs?

  • спрацьовування правил SecuirtyGroup/Network Access List – заблоковані запити будуть відмічені як REJECTED
  • те, заради чого логі впроваджуємо ми – отримати картину трафіку між VPC та сервісами, щоб зрозуміти хто найбільше трафіку споживає, де і скільки cross-AZ трафіку тощо
  • моніторинг віддалених логінів у систему – стежити за портами 22 (SSH), 3389 (RDP)
  • відстеження сканування портів

Flow Log record – поля

Кожен запис у логах містить дані про IP-трафіку, отриманому за aggregation interval і є рядком з полями, розділені пробілами, де кожне поле містить інформацію про передачу даних, наприклад – Source IP, Destination IP і протокол.

За замовчуванням використовується такий формат:

${version} ${account-id} ${interface-id} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${protocol} ${packets} ${bytes} ${start} ${end} ${action} ${log-status}

Див. таблицю Available fields в документації – все у колонці Version 2 включено у default format. Інші версії доступні у Custom Format.

При створенні Flow Logs, ми можемо використовувати дефолтний формат або створити свій – розглянемо його трохи нижче:

Обмеження VPC Flow Logs

  • не можна використовувати з інстансами EC2-Classic
  • не можна створити логі для VPC-пірингів, якщо вони ведуть до VPC в іншому акаунті
  • після створення лога – не можна змінити його конфігурацію чи формат записів
  • якщо в інтерфейсу кілька IPv4-адрес, і траїфк відправляється до одного з secondary-адрес, то в полі dstaddr буде відображено primary-адресу; щоб отримати оригінальну адресу – використовуйте поле pkt-dstaddr
  • якщо трафік відправлений з або на мережевий інтерфейс, поля srcaddr та dstaddr будуть містити його primary private IPv4 адресу; щоб отримати оригінальну адресу – використовуйте поле pkt-dstaddr та pkt-dstaddr

Також, враховуйте, що:

  • не логуються записи до DNS Amazon, але пишуться, якщо використовується свій DNS
  • не логгується трафік на та з адреси 169.254.169.254 для отримання метаданих EC2-інстансу
  • не логується трафік між мережевим інтерфейсом EC2 та інтерфейсом AWS Network Load Balancer

Див. всі обмеження на Flow log limitations.

Створення VPC Flow Log

Для створення нам потрібно вказати:

  • ресурс, логи якого будемо писати – VPC, підмережа або конкретний мережевий інтерфейс
  • тип трафіку, який логуємо (accepted traffic, rejected traffic або all traffic)
  • і куди писатимемо дані – в кошик, або CloudWatch Logs

Поки подивимося, що вийде з CloudWatch Logs, а наступного разу – спробуємо візуалізувати у Kibana або Grafana.

CloudWatch Logs Log Group

Створюємо Log Group:

Створення IAM Policy та IAM Role

Для того, щоб сервіс Flow Logs міг писати в наш CloudWatch – налаштовуємо йому права доступу.

Переходимо в AWS IAM, створюємо IAM Policy та IAM Role.

Починаємо з Policy:

Додаємо саму політику:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents",
        "logs:DescribeLogGroups",
        "logs:DescribeLogStreams"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

Зберігаємо:

Створюємо роль.

Переходимо в IAM Roles, створюємо нову, вибираємо тип EC2:

Знаходимо створену вище політику, підключаємо:

Задаємо ім’я, зберігаємо:

Переходимо до Role Trust relationshitp (див. AWS: IAM AssumeRole – описание, примеры), редагуємо – змінюємо значення поля Service на vpc-flow-logs.amazonaws.com:

Вказуємо:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "vpc-flow-logs.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Зберігаємо:

VPC – включення Flow Logs

І нарешті переходимо до включення логів – знаходимо потрібну VPC, клікаємо Flow Logs > Create:

Задаємо ім’я, Filter, Interval:

У Destination вибираємо CloudWatch Logs, вказуємо створені раніше Log Group та IAM Role:

Format – залишаємо Default.

Перевіряємо Status:

І за декілька хвилин – дані пішли:

У лог-групі з’явився перший стрим з ім’ям Elastic Network Interface, з якого знімаються дані:

CloudWatch Logs Insights

Швидко подивимося, що нам доступно в Logs Insights.

Клікаєм Queries для отримання підказки щодо синтаксису запитів:

Наприклад, отримаємо топ-15 хостів за кількістю пакетів:

Або за обсягом надісланих даних:

stats sum(bytes) as BytesSent by srcAddr, dstAddr
| sort BytesSent desc

Окей, а що з іншими форматами?

Наприклад, хочеться побачити напрямок запиту (egress/ingress) та значення поля pkt-dstaddr.

VPC Flow Log – Custom format

Див. приклади на сторінціFlow log record examples.

Використовуємо такий формат:

region vpc-id az-id subnet-id instance-id interface-id flow-direction srcaddr dstaddr srcport dstport pkt-srcaddr pkt-dstaddr pkt-src-aws-service pkt-dst-aws-service traffic-path packets bytes action

У CloudWatch Logs створюємо новий Log group, назвемо його bttrm-eks-dev-1-21-vpc-fl-custom, не забуваємо про retention, щоб дані не лежали вічно (ну і взагалі не забуваємо, що CloudWatch не самий дешевий сервіс):

Повертаємось до VPC, знаходимо потрібну мережу, створюємо новий Flow Log, назвемо його bttrm-eks-dev-1-21-vpc-fl-custom:

Вибираємо Custom Format та поля, які хочемо записувати. При цьому враховуйте, що порядок полів у логах буде такий, який ви використовуєте під час вибору полів.

Тобто, якщо першим клікнути на “region” – то й у логах він буде йти першим:

Виходить так:

${region} ${vpc-id} ${az-id} ${subnet-id} ${instance-id} ${interface-id} ${flow-direction} ${srcaddr} ${dstaddr} ${srcport} ${dstport} ${pkt-srcaddr} ${pkt-dstaddr} ${pkt-src-aws-service} ${pkt-dst-aws-service} ${traffic-path} ${packets} ${bytes} ${action}

Flow Log Custom format та CloudWatch Logs Insights

Але якщо ми тепер ми зайдемо в Logs Insights, і спробуємо будь-який із попередніх запитів – отримаємо зовсім не ті поля, які хотіли:

Тобто, дані бачимо, але як розбити поля по колонках?

Ми навряд чи будемо щільно користуватися CloudWatch Logs, швидше за все в продакшені дані підуть у S3 і потім в ELK (logz.io), тому докладно тут зупинятись не буду, проте принцип роботи подивимося.

CloudWatch Logs за замовчуванням створює кілька мета-полів, які ми можемо використовувати у запитах:

  • @message: “сирі” дані – все повідомлення в text
  • @timestamp: час події
  • @logStream: ім’я стриму

Для Custom format, щоб сформувати поля, використовуємо команду parse, якою передаємо поле @message з усім змістом, а потім парсимо його по полях, які розділені пробілами:

parse @message "* * * * * * * * * * * * * * * * * * *" 
| as region, vpc_id, az_id, subnet_id, instance_id, interface_id, 
| flow_direction, srcaddr, dstaddr, srcport, dstport, 
| pkt_srcaddr, pkt_dstaddr, pkt_src_aws_service, pkt_dst_aws_service, 
| traffic_path, packets, bytes, action 
| sort start desc

Тут кількість “*” в @message має бути рівним кількості імен полів, які ми задаємо – ${vpc-id} і т.д.

Крім того, імена полів не повинні містити тире. Тобто, оригінальне ім’я поля ${vpc-id} для виведення імені колонки вказуємо як vpc_id (або vpcID – кому який формат більше подобається).

Перевіряємо:

Інша справа!

Окрім parse, ми можемо використовувати такі команди, як filter, display, stats. Див. усі в CloudWatch Logs Insights query syntax.

Приклади Logs Insights

Ну і спробуємо щось зобразити, наприклад – отримати всі заблоковані запити SecuirtyGroup/Network Access List – вони будуть відзначені як REJECTED.

До нашого запиту:

parse @message "* * * * * * * * * * * * * * * * * * * * * *" 
| as start, end, region, vpc_id, az_id, subnet_id, instance_id, interface_id, 
| flow_direction, srcaddr, dstaddr, srcport, dstport, protocol, 
| pkt_srcaddr, pkt_dstaddr, pkt_src_aws_service, pkt_dst_aws_service, 
| traffic_path, packets, bytes, action

Додамо:

  • filter action="REJECT"
  • stats count(action) as redjects by srcaddr
  • sort redjects desc

Тут:

  • фільтруємо по дії над пакетом – вибираємо все REJECTED
  • рахуємо кількість записів по полю action, вибираючи за IP-адресою джерела, виводимо в колонці redjects
  • і сортуємо по колонці redjects

Тобто повністю запит зараз буде:

parse @message "* * * * * * * * * * * * * * * * * * *" 
| as region, vpc_id, az_id, subnet_id, instance_id, interface_id, 
| flow_direction, srcaddr, dstaddr, srcport, dstport, 
| pkt_srcaddr, pkt_dstaddr, pkt_src_aws_service, pkt_dst_aws_service, 
| traffic_path, packets, bytes, action 
| filter action="REJECT" 
| stats count(action) as redjects by srcaddr 
| sort redjects desc

Отримуємо:

Ми також можемо використовувати негативні фільтри та комбінувати умови фільтра з операторами and/or.

Наприклад, прибрати з виводу всі IP, що починаються з 162.142.125 – додаємо filter (srcaddr not like "162.142.125."):

...
| filter action="REJECT"
| filter (srcaddr not like "162.142.125.")
| stats count(action) as redjects by srcaddr
| sort redjects desc

Див. Sample queries.

І додамо фільтр на вибірку тільки вхідних запитів – flow_direction == ingress:

...
| filter action="REJECT"
| filter (srcaddr not like "162.142.125.") and (flow_direction like "ingress")
| stats count(action) as redjects by flow_direction, srcaddr, dstaddr, pkt_srcaddr, pkt_dstaddr
| sort redjects desc

Отримуємо топ відкинутих запитів – спрацювало правило SecurityGroup або VPC Network Access List.

І дивимося – що за IP у dstaddr – кому йшов пакет, який був заблокований?

Переходимо в EC2 > Network Interfaces, шукаємо Private IP:

Знаходимо “Elastic IP address owner“:

LoadBalancer.

Якщо адреса не знаходиться в AWS – можливо, це якийсь ендпоінт у Kubernetes – шукаємо наприклад так:

[simterm]

$ kubectl get endpoints --all-namespaces | grep 10.1.55.140
dev-ios-check-translation-ns                     ios-check-translation-backend-svc                    10.1.55.140:3000                                                     58d
dev-ios-check-translation-ns                     ios-check-translation-frontend-svc                   10.1.55.140:80                                                       58d

[/simterm]

Загалом на це все.

У наступній частині – налаштуємо збір логів в AWS S3, потім будемо їх звідти збирати в ELK, і там спробуємо зробити візуалізацію та алерти.

Посилання по темі

VPC Flow Logs

CloudWatch Logs