
Инструмент для линтинга шаблонов CloudFormation
Инструмент cfn-nag ищет в шаблонах CloudFormation паттерны, которые могут указывать на небезопасную инфраструктуру. Если говорить в общем, он проверяет:
Дополнительную информацию об инструменте можно найти в этом посте в блоге Stelligent:
Поиск проблем безопасности на ранних этапах разработки шаблона CloudFormation с помощью "cfn-nag"
Если установлен Ruby >= 2.5.x, установка сводится к следующему:
gem install cfn-nag
На MacOS или Linux можно также установить через brew:
brew install ruby brew-gem
brew gem install cfn-nag
Чтобы запускать cfn_nag как действие в CodePipeline, можно развернуть его через AWS Serverless Application Repository.
Для запуска:
cfn_nag_scan --input-path <путь к cloudformation json>
Путь может указывать на каталог или конкретный шаблон. Если это каталог, будут обработаны все файлы .json, .template, .yml и .yaml, включая рекурсивный обход подкаталогов.
Формат вывода по умолчанию — произвольный текст, но вывод в json можно выбрать с помощью флага --output-format json.
При необходимости флаг --debug выведет информацию о внутренних процессах загрузки правил.
Запустите с --help, чтобы получить полный список поддерживаемых параметров.
Чтобы увидеть список всех правил, которые cfn-nag поддерживает в настоящее время, существует утилита командной строки, выводящая их в stdout:
cfn_nag_rules
Для удобства предоставлен Dockerfile. Он опубликован на DockerHub как stelligent/cfn_nag.
https://hub.docker.com/r/stelligent/cfn_nag
Вы также можете собрать его локально.
docker build -t stelligent/cfn_nag .
Вы можете смонтировать локальный каталог с шаблонами в Docker-контейнер, а затем вызвать cfn_nag внутри контейнера. В этом примере используются тестовые шаблоны, применяемые при модульном тестировании cfn_nag:
$ docker run -v `pwd`/spec/test_templates:/templates -t stelligent/cfn_nag /templates/json/efs/filesystem_with_encryption.json
{
"failure_count": 0,
"violations": [
]
}
$ docker run -v `pwd`/spec/test_templates:/templates -t stelligent/cfn_nag /templates/json/efs/filesystem_with_no_encryption.json
{
"failure_count": 1,
"violations": [
{
"id": "F27",
"type": "FAIL",
"message": "EFS FileSystem should have encryption enabled",
"logical_resource_ids": [
"filesystem"
]
}
]
}
cfn_nag_scan можно запускать как часть GitHub Workflow для проверки кода в конвейерах непрерывной интеграции.
В файле GitHub Workflow создайте шаг, использующий cfn_nag Action:
- name: Simple test
uses: stelligent/cfn_nag@master
with:
input_path: tests
Дополнительная информация о GitHub Action доступна здесь.
cfn-nag поддерживает понятие "профиль" — по сути, это список разрешённых правил для применения. Профиль — это текстовый файл, в котором каждая строка должна содержать идентификатор правила. Если профиль указан через аргумент командной строки --profile-path, cfn-nag будет возвращать нарушения ТОЛЬКО по этим конкретным правилам.
Мотивация создания "профиля" в том, что разные разработчики могут заботиться о разных правилах. Например, "инфраструктурный разработчик" может обращать внимание на правила IAM, а "прикладной разработчик" может вообще не иметь возможности создавать ресурсы IAM и поэтому не интересоваться этими правилами.
Вот пример профиля:
F1
F2
F27
W3
W5
Список запрета — это, по сути, противоположность профиля: перечень правил, которые НИКОГДА не применяются. Если он указан через аргумент командной строки --deny-list-path, cfn-nag НИКОГДА не будет возвращать нарушения по этим конкретным правилам, перечисленным в файле.
Если правило указано в обоих списках, список запрета имеет приоритет над профилем, и правило не применяется.
Формат следующий. Единственные два значимых поля — RulesToSuppress и id для каждого элемента. Поле reason не интерпретируется cfn-nag, но рекомендуется указывать обоснование и документально фиксировать, почему правило никогда не должно применяться.
RulesToSuppress:
- id: W3
reason: W3 is something we never care about at enterprise X
Если вы хотите подавить какое-либо правило, к соответствующему ресурсу можно добавить ключ Metadata для cfn_nag, чтобы указать cfn_nag не выдавать ошибку или предупреждение по этому правилу.
Например, если вы настраиваете публичный ELB, открытый для входящих подключений из интернета, с ресурсами следующего вида:
public_alb.yaml
# Partial template
PublicAlbSecurityGroup:
Properties:
GroupDescription: 'Security group for a public Application Load Balancer'
VpcId:
Ref: vpc
Type: AWS::EC2::SecurityGroup
PublicAlbSecurityGroupHttpIngress:
Properties:
CidrIp: 0.0.0.0/0
FromPort: 80
GroupId:
Ref: PublicAlbSecurityGroup
IpProtocol: tcp
ToPort: 80
Type: AWS::EC2::SecurityGroupIngress
cfn_nag выдаст предупреждения следующего вида:
$ cfn_nag_scan -i public_alb.yaml
------------------------------------------------------------
public_alb.yaml
------------------------------------------------------------------------------------------------------------------------
| WARN W9
|
| Resources: ["PublicAlbSecurityGroup"]
|
| Security Groups found with ingress cidr that is not /32
------------------------------------------------------------
| WARN W2
|
| Resources: ["PublicAlbSecurityGroup"]
|
| Security Groups found with cidr open to world on ingress. This should never be true on instance. Permissible on ELB
Failures count: 0
Warnings count: 2
Добавив метаданные, эти предупреждения можно подавить:
public_alb_with_suppression.yaml
# Partial template
PublicAlbSecurityGroup:
Properties:
GroupDescription: 'Security group for a public Application Load Balancer'
VpcId:
Ref: vpc
Type: AWS::EC2::SecurityGroup
Metadata:
cfn_nag:
rules_to_suppress:
- id: W9
reason: "This is a public facing ELB and ingress from the internet should be permitted."
- id: W2
reason: "This is a public facing ELB and ingress from the internet should be permitted."
PublicAlbSecurityGroupHttpIngress:
Properties:
CidrIp: 0.0.0.0/0
FromPort: 80
GroupId:
Ref: PublicAlbSecurityGroup
IpProtocol: tcp
ToPort: 80
Type: AWS::EC2::SecurityGroupIngress