
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 <path to cloudformation json>
경로는 디렉터리 또는 특정 템플릿일 수 있습니다. 디렉터리인 경우 모든 .json, .template, .yml 및 .yaml 파일이 하위 디렉터리로 재귀적으로 처리됩니다.
기본 출력 형식은 자유 형식 텍스트이지만, --output-format json 플래그로 JSON 출력을 선택할 수 있습니다.
선택적으로 --debug 플래그는 규칙 로딩 내부 정보를 출력합니다.
지원되는 스위치의 전체 목록은 --help로 실행하십시오.
cfn-nag가 현재 지원하는 모든 규칙 목록을 보려면 표준 출력으로 덤프하는 명령줄 유틸리티가 있습니다:
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는 해당 특정 규칙의 위반만 반환합니다.
"프로필"을 만든 동기는 개발자마다 중요하게 생각하는 규칙이 다를 수 있기 때문입니다. 예를 들어, "infrastructure_developer"는 IAM 규칙에 관심이 있을 수 있지만, "app_developer"는 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
억제하려는 규칙이 있는 경우, 영향을 받는 리소스에 cfn_nag Metadata 키를 추가하여 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
$ cfn_nag_scan -i public_alb_with_suppression.yaml
------------------------------------------------------------
public_alb_with_supression.yaml
------------------------------------------------------------
Failures count: 0
Warnings count: 0
CloudFormation 템플릿 파라미터는 값이 배포 시점에 지정되므로 정적 분석에 문제가 될 수 있습니다. 즉, 정적 분석이 수행될 때 값은 사용할 수 없으며, 정적 분석은 앞에 있는 "코드"만 볼 수 있습니다. 따라서 cidr이 파라미터화되고 0.0.0.0/0이 배포 시 전달되는 경우 보안 그룹 인그레스 규칙 0.0.0.0/0은 플래그로 표시되지 않습니다.
파라미터 값을 확인할 수 있도록 사용자는 --parameter-values-path=<filename/uri> 플래그를 사용하여 명령줄에서 cfn_nag와 cfn_nag_scan 모두에 전달되는 JSON 파일에 파라미터 값을 지정할 수 있습니다.
JSON 형식은 단일 키 "Parameters"이며, 그 값은 각 키/값 쌍이 Parameters에 매핑되는 사전입니다:
{
"Parameters": {
"Cidr": "0.0.0.0/0"
}
}
이렇게 하면 다음 파라미터에 "0.0.0.0/0"이 제공됩니다:
Parameters:
Cidr:
Type: String
주의: JSON에 추가 파라미터가 있으면 자동으로 무시됩니다(cfn_nag_scan이 모든 템플릿에 동일한 JSON을 적용할 수 있도록 하기 위함).
JSON이 잘못되었거나 위 사양을 충족하지 않으면 FATAL 위반으로 구문 분석이 실패합니다.
0.5.55 이전에는 Fn::FindInMap 호출이 사실상 무시되었습니다. 기본 모델이 그대로 두었기 때문에 규칙에 Hash 값으로 나타났습니다. 예: { "Fn::FindInMap" => [map1, key1, key2]}
0.5.55부터 모델은 FindInMap 호출에 대한 값을 계산하여 규칙에 제시하려고 시도합니다. 이 평가는 다음 키를 지원합니다:
평가 로직이 키 값을 알아낼 수 없으면 전체 표현식에 대해 Hash를 반환하는 이전 동작으로 기본 설정됩니다.
0.5.55 이전에는 AWS 의사 함수 호출도 사실상 무시되었습니다. 기본 모델이 그대로 두었기 때문에 규칙에 Hash 값으로 나타났습니다. 예: {"Ref"=>"AWS::Region"}. 일반적인 사용 사례는 지역별로 매핑을 구성하는 것이므로 의사 함수 평가는 맵 평가를 더 잘 지원하는 데 중요합니다.
0.5.55부터 모델은 기본값과 함께 다음 AWS 의사 함수를 규칙에 제시합니다:
'AWS::URLSuffix' => 'amazonaws.com',
'AWS::Partition' => 'aws',
'AWS::NotificationARNs' => '',
'AWS::AccountId' => '111111111111',
'AWS::Region' => 'us-east-1',
'AWS::StackId' => 'arn:aws:cloudformation:us-east-1:111111111111:stack/stackname/51af3dc0-da77-11e4-872e-1234567db123',
'AWS::StackName' => 'stackname'
추가로, 최종 사용자는 기존 파라미터 치환 메커니즘을 통해 제공되는 값을 재정의할 수 있습니다. 예:
{
"Parameters": {
"AWS::Region": "eu-west-1"
}
}
cfn_nag 버전 0.4.66까지는 기본 모델이 템플릿 내의 Fn::If를 전혀 처리하지 않았습니다. 즉, 속성에 조건부 값이 있으면 Fn::If를 구문 분석하는 것은 규칙의 몫이었습니다. Fn::If는 거의 모든 곳에 나타날 수 있기 때문에 규칙 개발자에게 잡아내기 어려운 상황을 만들었습니다. 기껏해야 규칙 로직은 값이 원래 Hash가 아닌 경우 Hash인 값을 무시할 수 있었습니다.
이 문제를 해결하기 위해 cfn_nag의 기본 동작은 이제 Fn::If를 true 결과로 대체하는 것입니다. 즉, 기본적으로 규칙은 보안 위반에 대해 false 결과를 검사하지 않습니다.
Fn::If를 속성 값 수준에서 대체하는 것 외에도 동일한 동작이 Properties의 최상위 수준 Fn::If에도 적용됩니다. 예:
Resource1:
Type: Foo
Properties: !If
- IsNone
- Description: Up
- Description: DOwn
다음과 동일하게 보입니다:
Resource1:
Type: Foo
Properties:
Description: Up
이 동작을 어느 정도 제어하기 위해 사용자는 --condition-values-path=<filename/uri> 플래그를 사용하여 명령줄에서 cfn_nag와 cfn_nag_scan 모두에 전달되는 JSON 파일에 조건 값을 지정할 수 있습니다.
JSON 형식은 각 키/값 쌍이 Conditions에 매핑되는 사전입니다:
{
"Condition1": true,
"Condition2": false
}
SPCM의 기반은 블로그 게시물 IAM 정책 문서에 대한 제안된 복잡성 지표 사고 실험에 설명되어 있습니다.
cfn_nag 버전 0.6.0부터:
spcm_scan은 (cfn_nag_scan처럼) CloudFormation 템플릿 디렉터리를 스캔하고 SPCM 지표를 JSON 또는 HTML 형식으로 보고서를 생성할 수 있습니다.cfn_nag_scan --rule-arguments spcm_threshold:100--rule-arguments 메커니즘을 통해 설정에 대한 최종 사용자 값을 허용하는 규칙을 개발할 수 있습니다. Rule 객체는 attr_accessor만 선언하면 됩니다(예: attr_accessor :spcm_threshold). cfn_nag가 --rule-arguments에서 값을 주입하는 세부 사항을 처리합니다.0.5.x 릴리스에는 사용자 정의 규칙을 배포하고 로드하는 방식에 몇 가지 주요 변경 사항이 포함되어 있습니다. 이 릴리스 이전에는 규칙이 로드되는 두 곳이 있었습니다: 핵심 cfn_nag gem 내의 lib/cfn-nag/custom_rules 디렉터리와 명령줄에 지정된 사용자 정의 규칙 디렉터리입니다.
사용자 정의 규칙이 로드되는 방식/위치를 재설계하게 만든 두 가지 사용 사례가 있습니다. 규칙 로딩 메커니즘은 사용자 정의 규칙 저장소를 사용하여 규칙을 발견할 수 있도록 일반화되었습니다.
파일 시스템에 흩어져 있는 많은 "규칙 파일"은 전통적인 소프트웨어 개발 관점에서 좋지 않습니다. 이러한 파일에는 버전이나 추적성이 없으므로 0.5.x는 "cfn_nag 규칙 gem" 개념을 도입했습니다. 개발자는 별도의 gem의 일부로 사용자 정의 규칙을 개발하고 버전을 지정하고 설치할 수 있습니다. gem 메타데이터에 cfn_nag_rules => true가 포함되어 있는 한 해당 규칙은 cfn_nag에서 참조됩니다. "cfn-nag-hipaa-rules"와 같은 이름의 gem의 경우 lib/cfn-nag-hipaa-rules 아래의 모든 *.rb가 로드됩니다. 모든 사용자 정의 규칙은 cfn-nag/base_rule의 CfnNag::BaseRule에서 파생되어야 합니다(cfn-nag/custom-rules/base가 아님). 규칙이 다른 것에서 파생되어야 한다면 cfn_nag_rule? 메서드를 정의하여 true를 반환하도록 하면 규칙으로 로드됩니다.
cfn_nag가 AWS Lambda에서 실행될 때 - 전통적인 의미의 파일 시스템(/tmp 제외)이 실제로 없습니다. 따라서 Lambda에서는 핵심 규칙만 사용할 수 있습니다. 사용자 정의 규칙을 지원하기 위해 cfn_nag는 파일 시스템 대신 S3 버킷에서 규칙을 발견하는 것을 지원합니다.
Ruby에서 사용자 정의 규칙을 개발하는 방법에 대해 지금까지 본 모든 내용은 여전히 유효합니다.
S3 버킷에서 규칙을 발견하려면 다음 내용으로 s3.yml 파일을 만드십시오:
---
repo_class_name: S3BucketBasedRuleRepo
repo_arguments:
s3_bucket_name: cfn-nag-rules-my-enterprise
prefix: /rules
접두사 /rules (예: /rules/MyNewRule.rb)를 사용하여 cfn-nag-rules-my-enterprise 버킷의 *Rule.rb 파일을 적용하려면 명령줄에서 cfn_nag에 이 파일을 다음과 같이 지정하십시오:
cat my_cfn_template.yml | cfn_nag --rule-repository s3.yml
규칙이 둘 이상의 버킷에 있으면 여러 s3*.yml 파일을 만들고 --rule-repository 인수에 지정하십시오.
주변 AWS 자격 증명에 cfn-nag-rules-enterprise 버킷에 액세스할 수 있는 권한이 있으면 /rules/*Rule.rb와 같은 모든 규칙을 찾습니다. 특정 aws_profile을 사용해야 하는 경우 repo_arguments 아래에 키로 추가하십시오(예: aws_profile: my_aws_profile).
파일 시스템, gem 설치 및 S3 외에도 새 아키텍처는 이론적으로 DynamoDb, 관계형 데이터베이스 또는 기타 웹 서비스에서 규칙을 로드하는 다른 "규칙 저장소" 개발을 지원합니다.
직접 사용 및/또는 커뮤니티 기여를 위한 새 규칙을 작성하려면 사용자 정의 규칙 개발을 참조하십시오.
TDD 사용자 정의 규칙 개발을 처음부터 끝까지 보여주는 스크린캐스트는 여기에서 볼 수 있습니다:
https://www.youtube.com/watch?v=JRZct0naFd4&t=1601s
스펙을 실행하려면 Docker가 설치되어 있고 cfn_nag 종속성이 설치되어 있어야 합니다:
gem install bundle
bundle install
그런 다음 모든 스펙을 실행하려면 rake test:all을 실행하면 됩니다.
엔드투엔드 테스트를 실행하려면 rake test:e2e를 실행하십시오. 스크립트는 Gemfile의 모든 gem을 번들하고, cfn_nag gem을 로컬에 빌드 및 설치하고, 스펙 종속성을 설치한 다음 'end_to_end' 태그가 지정된 테스트를 실행합니다. 또한 Amazon이 제공하는 샘플 템플릿을 가져와서 cfn_nag_scan을 실행하여 알려진 정상 템플릿이 cfn-nag 내에서 예외를 일으키는지 확인합니다.
현재 git 분기를 로컬에 설치하려면:
bundle install
scripts/deploy_local.sh
규칙 개발 및 생성을 쉽게 하기 위해 모든 도구와 설정이 사전 구성된 완전한 원격 개발 환경이 만들어져 있습니다. VS Code 원격 개발 기능을 사용하여 활성화할 수 있습니다.
Folder contains a dev container configuration file. Reopen folder to develop in a container라는 메시지가 표시되면 Reopen in Container 버튼을 클릭합니다.[Dev Container] cfn_nag Development 옵션을 사용합니다.VS Code 원격 개발 설정에 대한 자세한 내용은 VS Code 원격 개발에서 확인할 수 있습니다.
버그를 신고하거나 기능을 요청하려면 GitHub 저장소를 통해 이슈를 제출하십시오: https://github.com/stelligent/cfn_nag/issues/new