Сервис AWS Config предназначен для оценки и аудита конфигурации ресурсов AWS. AWS Config ведет непрерывный мониторинг конфигурации ресурсов AWS и фиксирует результаты. Сервис позволяет автоматизировать сопоставление записанных и требуемых конфигураций и оценку соответствия.
Что это означает на практике? Расскажу о своем недавнем кейсе.
Ко мне обратился клиент, использующий сервис AWS Secrets Manager для хранения API ключей и токенов OAuth. Ключей и токенов было очень много (несколько сотен).
Они были разного типа, и их ротация (обновление) осуществлялась с использованием различных AWS Lambda функций. Администраторам было сложно вручную отслеживать, все ли в системе работает корректно, нет ли ключей, которые по какой-то причине не обновлены в течение заданного промежутка времени. К примеру, если кто-то внесет изменение в функцию, отвечающую за ротацию ключа, он перестанет обновляться, и этого не будет видно.
Передо мной встала задача создания 2х кастомных правил AWS Config:
1) Дата последнего обновления любого ключа AWS Secrets Manager должна быть не старше, чем 90 дней назад.
2) Не должно быть Secrets, для которых не задана функция ротации, и в настройках автоматического выполнения функции ротации должен стоять период ротации менее 90 дней.
Для создания и проверки выполнения данных правил в системе мне понадобилось создать 2 AWS Lambda функции с доступом к AWS Config, AWS Secrets Manager и CloudWatch (для логирования).
Далее я создала сами правила AWS Config, подключив в каждом из них соответствующую Lambda-функцию для проверки и указав периодичность проверки:
{ "invokingEvent": "{\"configurationItem\":{\"configurationItemCaptureTime\":\"2016-02-17T01:36:34.043Z\",\"awsAccountId\":\"123456789012\",\"configurationItemStatus\":\"OK\",\"resourceId\":\"i-00000000\",\"ARN\":\"arn:aws:ec2:us-east-2:123456789012:instance/i-00000000\",\"awsRegion\":\"us-east-2\",\"availabilityZone\":\"us-east-2a\",\"resourceType\":\"AWS::EC2::Instance\",\"tags\":{\"Foo\":\"Bar\"},\"relationships\":[{\"resourceId\":\"eipalloc-00000000\",\"resourceType\":\"AWS::EC2::EIP\",\"name\":\"Is attached to ElasticIp\"}],\"configuration\":{\"foo\":\"bar\"}},\"messageType\":\"ConfigurationItemChangeNotification\"}", "ruleParameters": "{\"myParameterKey\":\"myParameterValue\"}", "resultToken": "myResultToken", "eventLeftScope": false, "executionRoleArn": "arn:aws:iam::123456789012:role/config-role", "configRuleArn": "arn:aws:config:us-east-2:123456789012:config-rule/config-rule-0123456", "configRuleName": "change-triggered-config-rule", "configRuleId": "config-rule-0123456", "accountId": "123456789012", "version": "1.0" }
2) После проверки источника запроса (код проверки, как и другие скучные моменты я не привожу в данном посте) необходимо получить необходимые данные — в данном кейсе это настройки Secrets. Для доступа к ресурcам AWS используется python библиотека boto3. Получить данные, необходимые для проверки — очень просто:
secrets_list = [] response = self._lambda_client.list_secrets() for secrets_dict in response['SecretList']: secrets_list.append(secrets_dict) while "NextMarker" in response: response = self._lambda_client.list_secrets( Marker=response["NextMarker"]) for secrets_dict in response['SecretList']: secrets_list.append(secrets_dict) for item in secrets_list: if key in item: detail_response = self._lambda_client.describe_secret( SecretId=item['ARN'])
3) Анализируем полученные данные на соответствие заданным правилам и готовим ответ:
@staticmethod def prepare_report(item): """Prepare report for answer to AWS Config""" rotation_arn = item['RotationLambdaARN'] aa_days = item['RotationRules']['AutomaticallyAfterDays'] secret_name = item['Name'] if not rotation_arn: answer = { "type": "NON_COMPLIANT", "annotation": "Value of RotationLambdaARN = null (empty) for Secret {}. Refer to documentation here: {}. ".format( secret_name, Environment.get_reference_url(), ) } elif aa_days > 90: answer = { "type": "NON_COMPLIANT", "annotation": "Automatic rotation for the Secter {} is after {} days. Refer to documentation here: {}. ".format( secret_name, aa_days, Environment.get_reference_url(), ) } else: answer = { "type": "COMPLIANT", "annotation": "Automatic rotation for the Secter {} is after {} days. Refer to documentation here: {}. ".format( secret_name, aa_days, Environment.get_reference_url(), ) } return answer
config_delegator = ConfigDelegator(event["resultToken"], CONFIG_CLIENT) config_delegator.add_evaluation( answer["type"], answer["annotation"], item['Name'], self._resource_type)
Здесь answer[«type»] может принимать 2 значения: COMPLIANT, если правило выполняется или NON_COMPLIANT, если правило не выполняется.