
Linting-Tool für CloudFormation-Vorlagen
Das cfn-nag-Tool sucht in CloudFormation-Vorlagen nach Mustern, die auf unsichere Infrastruktur hindeuten können. Grob gesagt sucht es nach:
Weitere Hintergrundinformationen zum Tool findest du in diesem Beitrag im Stelligent-Blog:
Vorausgesetzt, Ruby >= 2.5.x ist installiert, ist die Installation ganz einfach:
gem install cfn-nag
Unter MacOS oder Linux kannst du alternativ mit brew installieren:
brew install ruby brew-gem
brew gem install cfn-nag
Um cfn_nag als Aktion in CodePipeline auszuführen, kannst du über das AWS Serverless Application Repository bereitstellen.
Zum Ausführen:
cfn_nag_scan --input-path <path to cloudformation json>
Der Pfad kann ein Verzeichnis oder eine bestimmte Vorlage sein. Wenn es ein Verzeichnis ist, werden alle .json, .template, .yml- und .yaml-Dateien verarbeitet, einschließlich der Rekursion in Unterverzeichnisse.
Das Standardausgabeformat ist Freitext, aber die JSON-Ausgabe kann mit dem Flag --output-format json ausgewählt werden.
Optional kann ein --debug-Flag Informationen über die Interna des Regelladens ausgeben.
Führe das Tool mit --help aus, um eine vollständige Liste der unterstützten Schalter zu erhalten.
Um eine Liste aller Regeln zu sehen, die cfn-nag derzeit unterstützt, gibt es ein Befehlszeilenprogramm, das sie auf stdout ausgibt:
cfn_nag_rules
Für die Bequemlichkeit wird ein Dockerfile bereitgestellt. Es wird auf DockerHub als stelligent/cfn_nag veröffentlicht.
https://hub.docker.com/r/stelligent/cfn_nag
Du kannst es auch lokal erstellen.
docker build -t stelligent/cfn_nag .
Du kannst ein lokales Verzeichnis mit Vorlagen in den Docker-Container mounten und dann cfn_nag im Container aufrufen. Dieses Beispiel verwendet die Testvorlagen, die beim Unit-Testen von cfn_nag verwendet werden:
$ 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 kann als Teil eines GitHub-Workflows ausgeführt werden, um Code in Continuous-Integration-Pipelines zu bewerten.
Erstelle in deiner GitHub-Workflow-Datei einen Schritt, der die cfn_nag-Action verwendet:
- name: Simple test
uses: stelligent/cfn_nag@master
with:
input_path: tests
Weitere Informationen zur GitHub Action findest du hier.
cfn-nag unterstützt das Konzept eines "Profils", das im Grunde eine Zulassungsliste der anzuwendenden Regeln ist. Das Profil ist eine Textdatei, die pro Zeile eine Regelkennung enthalten muss. Wenn es über das Befehlszeilenargument --profile-path angegeben wird, gibt cfn-nag NUR Verletzungen von diesen bestimmten Regeln zurück.
Die Motivation hinter der Erstellung eines "Profils" ist, dass verschiedene Entwickler an unterschiedlichen Regeln interessiert sein können. Zum Beispiel könnte ein "infrastructure_developer" sich für IAM-Regeln interessieren, während ein "app_developer" möglicherweise nicht einmal IAM-Ressourcen erstellen kann und sich daher nicht für diese Regeln interessiert.
Hier ist ein Beispielprofil:
F1
F2
F27
W3
W5
Die Deny-Liste ist im Grunde das Gegenteil des Profils: Sie ist eine Liste von Regeln, die NIEMALS angewendet werden sollen. Wenn sie über das Befehlszeilenargument --deny-list-path angegeben wird, gibt cfn-nag NIEMALS Verletzungen von den in der Datei angegebenen Regeln zurück.
Falls eine Regel in beiden angegeben ist, hat die Deny-Liste Priorität gegenüber dem Profil, und die Regel wird nicht angewendet.
Das Format ist wie folgt. Die einzigen beiden wichtigen Felder sind RulesToSuppress und die id pro Eintrag. Die reason wird von cfn-nag nicht interpretiert, aber es wird empfohlen, zu begründen und zu dokumentieren, warum die Regel niemals angewendet werden sollte.
RulesToSuppress:
- id: W3
reason: W3 is something we never care about at enterprise X
Falls es eine Regel gibt, die du unterdrücken möchtest, kann dem betroffenen Ressource ein cfn_nag-Metadata-Schlüssel hinzugefügt werden, um cfn_nag mitzuteilen, für diese Regel keinen Fehler oder keine Warnung auszulösen.
Wenn du zum Beispiel einen öffentlich zugänglichen ELB einrichtest, der für eingehende Verbindungen aus dem Internet offen ist, mit Ressourcen wie den folgenden:
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 wird Warnungen wie die folgenden ausgeben:
$ 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
Durch Hinzufügen der Metadaten können diese Warnungen unterdrückt werden:
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-Template-Parameter können für die statische Analyse ein Problem darstellen, da die Werte zum Zeitpunkt der Bereitstellung angegeben werden. Mit anderen Worten: Die Werte stehen bei der statischen Analyse nicht zur Verfügung – die statische Analyse kann nur den "Code" betrachten, der vor ihr liegt. Daher wird eine Security-Group-Ingress-Regel mit 0.0.0.0/0 nicht markiert, wenn die CIDR parametrisiert ist und die 0.0.0.0/0 erst zur Bereitstellungszeit übergeben wird.
Um die Prüfung von Parameterwerten zu ermöglichen, kann ein Benutzer die Parameterwerte in einer JSON-Datei angeben, die über das Flag --parameter-values-path=<filename/uri> an der Befehlszeile sowohl an cfn_nag als auch an cfn_nag_scan übergeben wird.
Das Format des JSON ist ein einzelner Schlüssel, "Parameters", dessen Wert ein Wörterbuch ist, wobei jedes Schlüssel/Wert-Paar den Parameters entspricht:
{
"Parameters": {
"Cidr": "0.0.0.0/0"
}
}
Dies stellt den folgenden Parameter mit "0.0.0.0/0" bereit:
Parameters:
Cidr:
Type: String
ACHTUNG: Wenn das JSON zusätzliche Parameter enthält, werden diese stillschweigend ignoriert (damit cfn_nag_scan dasselbe JSON auf alle Vorlagen anwenden kann).
Wenn das JSON fehlerhaft ist oder nicht der obigen Spezifikation entspricht, schlägt die Analyse mit einer FATAL-Verletzung fehl.
Vor Version 0.5.55 wurden Aufrufe von Fn::FindInMap praktisch ignoriert. Das zugrunde liegende Modell ließ sie unverändert, sodass sie für Regeln als Hash-Werte erschienen. Zum Beispiel: { "Fn::FindInMap" => [map1, key1, key2]}
Ab Version 0.5.55 versucht das Modell, den Wert für einen Aufruf von FindInMap zu berechnen und diesen Wert den Regeln zu präsentieren. Diese Auswertung unterstützt Schlüssel, die Folgendes sind:
Wenn die Auswertungslogik den Wert für einen Schlüssel nicht ermitteln kann, wird standardmäßig das alte Verhalten verwendet und der Hash für den gesamten Ausdruck zurückgegeben.
Ebenfalls vor 0.5.55 wurden Aufrufe von AWS-Pseudofunktionen praktisch ignoriert. Das zugrunde liegende Modell ließ sie unverändert, sodass sie für Regeln als Hash-Werte erschienen. Zum Beispiel: {"Ref"=>"AWS::Region"}. Ein häufiger Anwendungsfall ist die Organisation von Mappings nach Region, daher ist die Auswertung von Pseudofunktionen wichtig, um die Mapping-Auswertung besser zu unterstützen.
Ab Version 0.5.55 präsentiert das Modell den Regeln die folgenden AWS-Pseudofunktionen mit den Standardwerten:
'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'
Zusätzlich kann der Endbenutzer den Wert überschreiben, der über den herkömmlichen Mechanismus der Parameterersetzung bereitgestellt wird. Zum Beispiel:
{
"Parameters": {
"AWS::Region": "eu-west-1"
}
}
Bis Version 0.4.66 von cfn_nag führte das zugrunde liegende Modell keine Verarbeitung von Fn::If innerhalb einer Vorlage durch. Das bedeutete, dass es an der Regel lag, das Fn::If zu parsen, wenn eine Eigenschaft einen bedingten Wert hatte. Da ein Fn::If fast überall auftauchen konnte, entstand für Regelentwickler eine Art "Whack-a-Mole"-Situation. Im besten Fall konnte die Regellogik Werte ignorieren, die Hash waren, vorausgesetzt der Wert war nicht von vornherein ein Hash.
Um dieses Problem zu beheben, besteht das Standardverhalten von cfn_nag nun darin, Fn::If durch den true-Ausgang zu ersetzen. Das bedeutet, dass Regeln standardmäßig die false-Ausgänge nicht auf Sicherheitsverletzungen untersuchen.
Zusätzlich zur Ersetzung von Fn::If auf der Ebene der Eigenschaftswerte wird dasselbe Verhalten auf Fn::If auf der obersten Ebene von Properties angewendet. Zum Beispiel:
Resource1:
Type: Foo
Properties: !If
- IsNone
- Description: Up
- Description: DOwn
Sieht dann genauso aus wie:
Resource1:
Type: Foo
Properties:
Description: Up
Um dieses Verhalten etwas steuern zu können, kann ein Benutzer die Conditions-Werte in einer JSON-Datei angeben, die über das Flag --condition-values-path=<filename/uri> an der Befehlszeile sowohl an cfn_nag als auch an cfn_nag_scan übergeben wird.
Das Format des JSON ist ein Wörterbuch, wobei jedes Schlüssel/Wert-Paar den Conditions entspricht:
{
"Condition1": true,
"Condition2": false
}
Die Grundlage für SPCM wird in dem Blogbeitrag Thought Experiment Proposed Complexity Metric for IAM Policy Documents beschrieben.
Ab Version 0.6.0 von cfn_nag:
spcm_scan kann ein Verzeichnis mit CloudFormation-Vorlagen scannen (wie cfn_nag_scan) und einen Bericht mit den SPCM-Metriken im JSON- oder HTML-Format erstellen.cfn_nag_scan --rule-arguments spcm_threshold:100--rule-arguments-Mechanismus akzeptieren. Das Rule-Objekt muss nur einen attr_accessor deklarieren, z. B. attr_accessor :spcm_threshold, und cfn_nag kümmert sich um die Details, um Werte aus den --rule-arguments zu injizieren.Das Release 0.5.x enthält einige wesentliche Änderungen darüber, wie benutzerdefinierte Regeln verteilt und geladen werden (können). Vor diesem Release gab es zwei Orte, von denen Regeln geladen wurden: das Verzeichnis lib/cfn-nag/custom_rules innerhalb des cfn_nag-Kern-Gems und das über die Befehlszeile angegebene Custom-Rule-Verzeichnis.
Es gibt zwei Anwendungsfälle, die eine Neugestaltung des Wie/Wo des Ladens benutzerdefinierter Regeln erzwangen. Der Mechanismus zum Laden von Regeln wurde verallgemeinert, sodass Repositories für benutzerdefinierte Regeln zum Auffinden von Regeln verwendet werden können.
Eine Reihe von "Regeldateien", die einfach so in einem Dateisystem herumliegen, ist aus traditioneller Softwareentwicklungsperspektive nicht ideal. Für diese Dateien gibt es keine Version oder Rückverfolgbarkeit, daher führt 0.5.x das Konzept eines "cfn_nag-Regel-Gems" ein. Ein Entwickler kann benutzerdefinierte Regeln als Teil eines separaten Gems entwickeln, versionieren und installieren ... und diese Regeln werden von cfn_nag referenziert, solange die Gem-Metadaten cfn_nag_rules => true enthalten. Bei einem Gem mit einem Namen wie "cfn-nag-hipaa-rules" werden alle *.rb-Dateien unter lib/cfn-nag-hipaa-rules geladen. Alle benutzerdefinierten Regeln sollten von CfnNag::BaseRule in cfn-nag/base_rule abgeleitet werden (nicht cfn-nag/custom-rules/base). Wenn die Regel von etwas anderem abgeleitet werden muss, führt auch die Definition einer Methode cfn_nag_rule?, die true zurückgibt, dazu, dass sie als Regel geladen wird.
Wenn cfn_nag in einer AWS Lambda läuft, gibt es im herkömmlichen Sinne kein echtes Dateisystem (außer /tmp). Daher sind aus der Lambda nur die Kernregeln nutzbar. Um benutzerdefinierte Regeln zu unterstützen, unterstützt cfn_nag das Auffinden von Regeln aus einem S3-Bucket anstelle des Dateisystems.
Alles, was du wahrscheinlich über die Entwicklung benutzerdefinierter Regeln in Ruby gesehen hast, gilt weiterhin.
Um Regeln aus einem S3-Bucket zu finden, erstelle eine Datei s3.yml mit folgendem Inhalt:
---
repo_class_name: S3BucketBasedRuleRepo
repo_arguments:
s3_bucket_name: cfn-nag-rules-my-enterprise
prefix: /rules
Um *Rule.rb-Dateien im Bucket cfn-nag-rules-my-enterprise mit dem Präfix /rules (z. B. /rules/MyNewRule.rb) anzuwenden, gib diese Datei an der Befehlszeile wie folgt an cfn_nag weiter:
cat my_cfn_template.yml | cfn_nag --rule-repository s3.yml
Wenn sich Regeln in mehr als einem Bucket befinden, erstelle mehrere s3*.yml-Dateien und gib sie im Argument --rule-repository an.
Wenn die umgebenden AWS-Anmeldeinformationen die Berechtigung haben, auf den Bucket cfn-nag-rules-enterprise zuzugreifen, werden alle Regeln wie /rules/*Rule.rb gefunden. Wenn ein bestimmtes aws_profile verwendet werden soll, füge es als Schlüssel unter repo_arguments hinzu, z. B. aws_profile: my_aws_profile
Über das Dateisystem, Gem-Installationen und S3 hinaus unterstützt die neue Architektur theoretisch die Entwicklung weiterer "Regel-Repositories", um Regeln aus DynamoDb, relationalen Datenbanken oder anderen Webdiensten zu laden.
Um neue Regeln für den eigenen Gebrauch und/oder für Beiträge zur Community zu erstellen, siehe Custom Rule Development für Details.
Ein Screencast, der die TDD-Entwicklung benutzerdefinierter Regeln von Anfang bis Ende demonstriert, ist hier verfügbar:
https://www.youtube.com/watch?v=JRZct0naFd4&t=1601s
Um die Specs auszuführen, musst du sicherstellen, dass Docker installiert ist und die cfn_nag-Abhängigkeiten installiert sind, über
gem install bundle
bundle install
Führe dann, um alle Specs auszuführen, einfach rake test:all aus.
Um die End-to-End-Tests auszuführen, führe rake test:e2e aus. Das Skript bündelt alle Gems in der Gemfile, erstellt und installiert das cfn_nag-Gem lokal, installiert die Spec-Abhängigkeiten und führt dann Tests mit dem Tag 'end_to_end' aus. Es lädt außerdem von Amazon bereitgestellte Beispielvorlagen herunter und führt cfn_nag_scan gegen sie aus, um zu sehen, ob bekannte gute Vorlagen Ausnahmen in cfn-nag verursachen.
Um den aktuellen Git-Branch lokal zu installieren:
bundle install
scripts/deploy_local.sh
Es gibt eine vollständige Remote-Entwicklungsumgebung, die mit allen Tools und Einstellungen vorkonfiguriert ist, um die Regelentwicklung und -erstellung zu erleichtern. Du kannst dies über die VS-Code-Remote-Development-Funktionalität aktivieren.
Folder contains a dev container configuration file. Reopen folder to develop in a container erscheint, klicke auf die Schaltfläche Reopen in Container[Dev Container] cfn_nag DevelopmentWeitere Informationen zum VS Code Remote Development Setup findest du hier: VS Code Remote Development.
Um einen Fehler zu melden oder ein Feature anzufragen, reiche ein Issue über das GitHub-Repository ein über: https://github.com/stelligent/cfn_nag/issues/new