В проекте steampipe используется быстрый язык программирования и интеллектуальный подход к кэшированию, превосходящий скорость prowler в десять раз. Пока я пытался обойти ограничения prowler, я узнал много нового об оптимизации.
Оценки лучших практик безопасности AWS
Для поддержки безопасности в первую очередь необходимо проверить свою учетную запись на соответствие лучшим практикам и эталонам. Двумя наиболее используемыми из них являются
AWS Foundational Security Best Practices от AWS и CIS (Center for Internet Security) AWS Benchmark.
Я сравню широко используемый инструмент prowler (git 5.1k звезд) с более новым, steampipe (git 1.2k звезд), который демонстрирует благоприятный подход.
Prowler
Prowler представляет себя:
«Prowler — это инструмент безопасности с открытым исходным кодом для выполнения оценки лучших практик безопасности AWS, аудита, реагирования на инциденты, непрерывного мониторинга, укрепления и готовности к судебной экспертизе. Он содержит более 200 элементов управления, охватывающих CIS, PCI-DSS, ISO27001, GDPR, HIPAA, FFIEC, SOC2, AWS FTR, ENS и системы безопасности custome».
Архитектура
Приложение работает на основе сценариев bash с AWS CLI в качестве основы. Запросы выполняются с помощью комбинации bash pipes и синтаксиса cli «query». Это делает внедрение элементов управления простым на начальном этапе.
Сценарий bash ожидает после каждого запроса AWS API.
Скорость
Проблема такой архитектуры заключается в том, что сканирование может занимать от 30 минут до нескольких часов.
Давайте рассмотрим одну проверку в качестве примера:
проверка122 в качестве примера
В checks/check121
из репозитория prowler вы найдете этот bash-скрипт:
Раздел конфигурации
Атрибуты проверки хранятся в переменных окружения.
CHECK_ID_check122="1.22"
CHECK_TITLE_check122="[check122] Ensure IAM policies that allow full "*:*" administrative privileges are not created"
CHECK_SCORED_check122="SCORED"
CHECK_CIS_LEVEL_check122="LEVEL1"
CHECK_SEVERITY_check122="Medium"
CHECK_ASFF_TYPE_check122= "Software and Configuration Checks/Industry and Regulatory Standards/CIS AWS Foundations Benchmark"
CHECK_ASFF_RESOURCE_TYPE_check122="AwsIamPolicy"
CHECK_ALTERNATE_check122="check122"
CHECK_SERVICENAME_check122="iam"
CHECK_RISK_check122='IAM policies are the means by which privileges are granted to users; groups; or roles. It is recommended and considered a standard security advice to grant least privilege—that is; granting only the permissions required to perform a task. Determine what users need to do and then craft policies for them that let the users perform only those tasks instead of allowing full administrative privileges. Providing full administrative privileges instead of restricting to the minimum set of permissions that the user is required to do exposes the resources to potentially unwanted actions.'
CHECK_REMEDIATION_check122='It is more secure to start with a minimum set of permissions and grant additional permissions as necessary; rather than starting with permissions that are too lenient and then trying to tighten them later. List policies an analyze if permissions are the least possible to conduct business activities.'
CHECK_DOC_check122='http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html'
CHECK_CAF_EPIC_check122='IAM'
Раздел кода
1 check122(){
2 # "Ensure IAM policies that allow full "*:*" administrative privileges are not created (Scored)"
3 LIST_CUSTOM_POLICIES=$($AWSCLI iam list-policies --output text $PROFILE_OPT --region $REGION --scope Local --query 'Policies[*].[Arn,Defau ltVersionId]' | grep -v -e '^None$' | awk -F 't' '{print $1","$2"n"}')
4 if [[ $LIST_CUSTOM_POLICIES ]]; then
5 for policy in $LIST_CUSTOM_POLICIES; do
6 POLICY_ARN=$(echo $policy | awk -F ',' '{print $1}')
7 POLICY_VERSION=$(echo $policy | awk -F ',' '{print $2}')
8 POLICY_WITH_FULL=$($AWSCLI iam get-policy-version --output text --policy-arn $POLICY_ARN --version-id $POLICY_VERSION --query "[Policy Version.Document.Statement] | [] | [?Action!=null] | [?Effect == 'Allow' && Resource == '*' && Action == '*']" $PROFILE_OPT --region $REGION )
9 if [[ $POLICY_WITH_FULL ]]; then
10 POLICIES_ALLOW_LIST="$POLICIES_ALLOW_LIST $POLICY_ARN"
11 fi
12 done
13 if [[ $POLICIES_ALLOW_LIST ]]; then
14 for policy in $POLICIES_ALLOW_LIST; do
15 textFail "$REGION: Policy $policy allows "*:*"" "$REGION" "$policy"
16 done
17 else
18 textPass "$REGION: No custom policy found that allow full "*:*" administrative privileges" "$REGION"
19 fi
20 else
21 textPass "$REGION: No custom policies found" "$REGION"
22 fi
23 }
Код выполняет следующие действия:
1. Перечислить политики (строка 3)
$AWSCLI iam list-policies
2. Петля (строки 5-12)
$AWSCLI iam get-policy-version
Скорость работы AWS cli
При использовании python для каждого вызова запускается интерпретатор python, занимая время на каждый вызов.
Позвольте мне показать вам, что я имею в виду:
time aws iam list-policies
aws iam list-policies 0,93s user 0,13s system 15% cpu 6,894 total
Это занимает около 7 секунд. Теперь «list-policies» является затратной по времени операцией, поэтому я рассмотрю более простую, S3 list.
Чтобы понять, какая часть — это время python, а какая — время AWS, я сравниваю «aws s3 ls» с более быстрым приложением go.
Это AWS python cli:
time aws s3 ls
aws s3 ls 0,46s user 0,07s system 72% cpu 0,726 total
Теперь вы можете сказать, что вывод терминала занимает некоторое время. Мы устраним это:
time aws s3 ls >/dev/null
aws s3 ls > /dev/null 0,43s user 0,07s system 79% cpu 0,635 total
Итого 0,635 секунды для AWS CLI/Python.
Напротив, небольшая программа GO занимает меньше времени:
time ./s3list >/dev/null
./s3list > /dev/null 0,01s user 0,01s system 11% cpu 0,183 total
Где основной код выглядит так:
res, err := client.ListBuckets(context.TODO(), &s3.ListBucketsInput{})
for _, bucket := range res.Buckets {
bucketarray = append(bucketarray, bucket.Name)
}
return bucketarray, nil
Итого 0,183 секунды для скомпилированной GO-программы.
Некоторые из вас могут сказать: Rust — новый король скорости — хорошо, давайте попробуем:
Rust может быть быстрее.
Но на данный момент rust SDK все еще находится в бета-версии, поэтому он занимает больше времени, чем GO, но быстрее, чем python.
time target/debug/rust-s3 >/dev/null
target/debug/rust-s3 > /dev/null 0,15s user 0,03s system 43% cpu 0,403 total
Итого 0,403 секунды для скомпилированной Rust-программы, созданной с помощью cargo build
.
Это код Rust:
use aws_config::meta::region::RegionProviderChain;
use aws_sdk_s3::{Client, Error};
#[tokio::main]
async fn main() -> Result<(), Error> {
let region_provider = RegionProviderChain::default_provider().or_else("eu-central-1");
let config = aws_config::from_env().region(region_provider).load().await;
let client = Client::new(&config);
if let Err(e) = show_buckets(&client).await
{
println!("{:?}", e);
};
Ok(())
}
async fn show_buckets( client: &Client) -> Result<(), Error> {
let resp = client.list_buckets().send().await?;
let buckets = resp.buckets().unwrap_or_default();
for bucket in buckets {
println!("{}", bucket.name().unwrap_or_default());
}
println!();
Ok(())
}
Оптимизация
Как вы видите, скомпилированная GO-программа намного быстрее, чем python-скрипт. Поэтому я попытался оптимизировать cli с помощью GO-кэша.
Проект называется «https://github.com/megaproaktiv/awclip». Пожалуйста, рассматривайте его как эксперимент; он как-то работает, но не дает желаемых результатов.
Вот что я обнаружил:
- AWS CLI делает гораздо больше вещей, чем просто вызов сервиса.
- AWS CLI не всегда работает с json.
Смотрим на строку 8 проверки:
--query "[Policy Version.Document.Statement]
Это работает на json, но важные APIS, такие как EC2 API, S3 API и IAM, отвечают на запросы с помощью XML, а не json.
Поэтому заменить функцию «запрос» собственной программой не так-то просто.
Наиболее перспективным подходом является параллельная предварительная выборка регионов. Prowler делает все последовательно.
Но проработав около дня, я получил лишь 10% прироста скорости от prowler. Это заставило меня понять, что мое мышление было неправильным.
Я оптимизировал локально, а не глобально.
Если вы читали книгу «Цель», то там сказано:
«Нас не волнуют локальные оптимумы».
Поэтому мой подход к замене AWS cli в качестве локального оптимума привел в никуда — обратно к чертежной доске. После некоторых исследований я обнаружил проект, в котором использовался глобальный оптимизационный подход: Steampipe
Steampipe — другой подход
Steampipe использует Postgres Foreign Data Wrapper для представления данных и сервисов из внешних систем в виде таблиц базы данных.
Запросы не выполняются на JSON, как в AWS cli. Данные AWS представлены в таблицах, и запросы работают непосредственно с этими таблицами. Все результаты кэшируются, и запросы выполняются на локальной таблице postgres, а не как вызовы служб AWS.
Поэтому когда я смотрю на подобный контроль, например check-122
, который здесь называется «Control: 1 IAM policies should not allow full ‘*’ administrative privileges», запрос пишется на SQL.
Конфигурация (foundational_security/iam.sp
) отделена от логики в отдельном файле и выглядит следующим образом:
control "foundational_security_iam_1" {
title = "1 IAM policies should not allow full '*' administrative privileges"
description = "This control checks whether the default version of IAM policies (also known as customer managed policies) has administrator access that includes a statement with 'Effect': 'Allow' with 'Action': '*' over 'Resource': '*'. The control only checks the customer managed policies that you create. It does not check inline and AWS managed policies."
severity = "high"
sql = query.iam_custom_policy_no_star_star.sql
documentation = file("./foundational_security/docs/foundational_security_iam_1.md")
tags = merge(local.foundational_security_iam_common_tags, {
foundational_security_item_id = "iam_1"
foundational_security_category = "secure_access_management"
})
}
Этот запрос находится в query/iam/iam_policy_no_star_star.sql
в репозитории github.
С помощью prowler это запрос с AWS cli и pipe some Bash logic:
--query "[PolicyVersion.Document.Statement] | [] | [?Action!=null] | [?Effect == 'Allow' && Resource == '*' && Action == '*']"
С steampipe синтаксис — sql:
with bad_policies as (
select
arn,
count(*) as num_bad_statements
from
aws_iam_policy,
jsonb_array_elements(policy_std -> 'Statement') as s,
jsonb_array_elements_text(s -> 'Resource') as resource,
jsonb_array_elements_text(s -> 'Action') as action
where
s ->> 'Effect' = 'Allow'
and resource = '*'
and (
(action = '*'
or action = '*:*'
)
)
group by
arn
)
Текстовый компаризм bash Effect == 'Allow'
становится where s ->> 'Effect' = 'Allow'
.
Разработка SQL-запросов
Вы можете разрабатывать sql-запросы с помощью команды steampipe query
, которая предоставляет вам локальный инструмент sql-запросов.
Первый шаг заключается в том, что с помощью команды .tables
вы получаете все таблицы AWS после установки плагина AWS.
inspect aws_iam_policy
Второй шаг — посмотреть, какие поля вы можете запросить:
Команда .inspect
показывает поля.
Вы можете начать экспериментировать с запросами:
> select policy_std from aws_iam_policy
+----------------------------------------------------------------------------------------------------------------------
| policy_std
+----------------------------------------------------------------------------------------------------------------------
| {"Statement":[{"Action":["logs:createloggroup",...
Функции для работы с JSON позволяют интерпретировать документы политики, см:
в следующем утверждении:
jsonb_array_elements(policy_std -> 'Statement') as s,
jsonb_array_elements_text(s -> 'Resource') as resource,
jsonb_array_elements_text(s -> 'Action') as action
where
s ->> 'Effect' = 'Allow'
and resource = '*'
and (
(action = '*'
or action = '*:*'
)
)
Теперь мы знаем, как строится элемент управления, давайте посмотрим на скорость.
Скорость для одиночного элемента управления
Скорость с prowler
Запуск одиночного элемента управления с prowler занимает более 2 минут:
Скорость со стимпайпом
При использовании steampipe это время уменьшилось до 23 секунд, при этом результаты остались прежними.
Скорость для полного сканирования
При полном сканировании prowler требует около 30 минут для одного региона, steampipe работает за 3 минуты.
Возможно, вы хотите попробовать это сами — тогда CloudShell — ваш друг.
Вот небольшое полное руководство по проверке вашего аккаунта!
Начало работы
Общие шаги таковы:
- Скачайте и установите Steampipe
- Установите плагин AWS
- Клонируйте репозиторий steampipe-mod-aws-compliance
- Сгенерируйте отчет об учетных данных AWS
- Настройте регионы (необязательно)
- Запустите все контрольные показатели
- Загрузить файл отчета
Подробнее:
1. Войдите в свою учетную запись AWS с правами администратора
2. Откройте cloudshell
3. Выполните эти команды:
sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/turbot/steampipe/main/install.sh)"
steampipe plugin install steampipe
steampipe plugin install aws
git clone https://github.com/turbot/steampipe-mod-aws-compliance.git
cd steampipe-mod-aws-compliance
aws iam generate-credential-report
4. (Необязательно) Редактирование конфигурации региона
Если вы хотите сканировать только определенный регион (регионы), вы можете задать это в конфигурации_
vi ~/.steampipe/config/aws.spc
1 connection "aws" {
2 plugin = "aws"
3
4 regions = ["eu-central-1","us-east-1"]
5
6 }
Вы знаете, что номера строк не копируются, не так ли 😉
5. Дождитесь отчета об авторизации
aws iam generate-credential-report
Вы получите это, когда отчет будет запущен:
{
"State": "STARTED",
"Description": "No report exists. Starting a new report generation task"
}
И вот это, когда отчет готов:
{
"State": "COMPLETE"
}
6. Выполните полную проверку с выводом html
steampipe check all --export=html
Это должно занять всего 3-5 минут.
Вы видите, что процесс запущен:
7. Скачайте отчет
Найдите файл all-$date-$time.html. например all-20220412-150100.html
.
В меню «Действия» cloudshell вы можете «Загрузить» html-файл:
Вы должны написать полный путь, например, так:
Теперь у вас есть полный отчет:
Заключение
Сканирование безопасности
На github есть много хороших инструментов для сканирования соответствия AWS и безопасности с открытым исходным кодом.
С полным отчетом — включая установку на облачную оболочку — примерно за 5 минут, SteamPipe является новой падающей звездой в моем портфеле инструментов. Посмотрим в долгосрочной перспективе.
Вам стоит потратить 10 минут, чтобы попробовать!
Оптимизация
Чему я научился:
- Предпочитайте глобальную оптимизацию перед локальной.
- Инструменты и языки имеют ограничения.
- Ищите альтернативы вместо того, чтобы тратить много времени на обходные пути.
- Компиляция быстрее интерпретации.
- Не доверяйте общим утверждениям. Напишите как можно меньшее доказательство концепции, чтобы опровергнуть ваши убеждения.
Обратная связь и обсуждение
Для обсуждения, пожалуйста, свяжитесь со мной в twitter @megaproaktiv
Узнайте больше о GO
Хотите узнать больше об использовании GOLANG на AWS? — Изучайте GO на AWS: здесь.
Источники
- Голдратт, Элияху М.; Джефф Кокс. Цель: процесс непрерывного совершенствования. North River Press. Kindle-версия.
- Проверка стимпайпа
- Steampipe Hub
- Prowler