Blog
Validierung von AWS Cloudformation in CI/CD-Pipelines

Autonomie für DevOps-Teams, schnellere Feedback-Schleifen und Sicherstellung einer gesetzeskonformen Nutzung der Cloud.
Immer mehr Unternehmen wandeln sich zu einer DevOps-Organisation. Dazu gehört, dass DevOps-Teams die Verantwortung für ihre Cloud-Infrastruktur übernehmen. Ich beschreibe, wie Sie DevOps-Teams die Freiheit geben, mit Cloudformation ihren eigenen Infrastrukturcode zu schreiben und ihren Quellcode anhand der von uns festgelegten Richtlinien zu validieren.
Lesen Sie, wie Sie die Verwendung von Tags erzwingen, eine weiße oder schwarze Liste für Servicetypen verwenden und die Vorlagen auf verbotene Eigenschaftswerte überprüfen. Mit diesem Blogbeitrag und den Beispielen sind Sie in wenigen Minuten startklar.
Bevor ich anfange, möchte ich Coolblue dafür danken, dass sie dieses Open-Source-Projekt zusammen mit AWS ins Leben gerufen haben. Das Tool ist "cool", aber das "How to" scheint zu fehlen.
Zunächst beginnen wir mit einer Beispiel-Cloudformation-Vorlage. Ich werde S3 verwenden, weil es einfach und schnell bereitzustellen ist und eine Ressource ist, für die alle Regeln gelten können. Der Tippfehler in diesem Beispiel ist gewollt.
Resources:
S3Bucket:
Type: AWS::S3:Bucket
Lassen Sie uns nun diese Vorlage gemäß den Standardvorgaben validieren:
$ pip install cfn-lint
$ cfn-lint --template template.yaml
E3001 Invalid or unsupported Type AWS::S3:Bucket for resource S3Bucket in us-east-1
template.yaml:3:9
Tags durchsetzen Immobilienwerte
In großen Unternehmen sehen wir oft Tagging-Strategien. Wir können sie jetzt in Cloudformation zur Pflicht machen. Einige Beispiele, die Sie in Betracht ziehen könnten:
- Team. Name des Teams, das für diese Ressource zuständig ist.
- Anwendung. Name der Anwendung, des Anwendungsstapels oder des Microservices.
- Umgebung. Der Name der Umgebung, zu der diese Ressource gehört, zum Beispiel: sandbox/test/prod/live.
- Repository. Name oder ID des Code-Repositorys, damit Sie den zugehörigen Quellcode und die zugehörigen Pipelines leicht finden können.
- Commit. Commit ID, um den schuldigen Commit leicht zu finden.
- Ablaufdatum. Falls diese Ressource zeitlich begrenzt ist, fügen Sie ein Datum hinzu, damit jeder weiß, dass sie gelöscht werden kann.
- CIA. Bewertung der Vertraulichkeit, Integrität und Verfügbarkeit. Zum Beispiel: 333 für eine hoch- und 111 für eine niedrig eingestufte Ressource.
Weitere Informationen zum Tagging finden Sie auf der AWS-Website.
Das Erzwingen von Tags ist derzeit ein Modul, das Sie selbst in Python schreiben müssen. Ein Beispiel finden Sie required_tags zu:
required_tags = ['Team', 'Application', 'Environment', 'Repository', 'Commit', 'Expiry', 'CIA']
Und führen Sie dann den folgenden Befehl aus. Er sollte die fehlenden Tags in Ihrer Cloudformation-Vorlage auflisten.
$ cfn-lint --template template.yaml
--append-rules ./append_rules
E9000 Missing Tag Application at Resources/S3Bucket/Properties/Tags
template.yaml:5:11
E9000 Missing Tag Environment at Resources/S3Bucket/Properties/Tags
template.yaml:5:11
E9000 Missing Tag Repository at Resources/S3Bucket/Properties/Tags
template.yaml:5:11
E9000 Missing Tag Commit at Resources/S3Bucket/Properties/Tags
template.yaml:5:11
E9000 Missing Tag Expiry at Resources/S3Bucket/Properties/Tags
template.yaml:5:11
E9000 Missing Tag CIA at Resources/S3Bucket/Properties/Tags
template.yaml:5:11
Eine schwarze Liste für Ressourcentypen
Unser Team hat beschlossen, alle Dienste auf eine weiße Liste zu setzen, mit Ausnahme derer, die wir ausdrücklich ausschließen. In realen Anwendungsfällen soll dies verhindern, dass Benutzer teure Cluster einsetzen. Für Demozwecke habe ich mich entschieden, SQS auszuschließen. Erstellen Sie eine Datei spec.json und fügen Sie den folgenden Code hinzu:
{
"IncludeResourceTypes": [
"AWS::*"
],
"ExcludeResourceTypes": [
"AWS::SQS::Queue"
]
}
Versuchen Sie nun, eine SQS-Warteschlange zu erstellen, und führen Sie den folgenden Befehl aus (den vorherigen Parameter --append-rules lassen wir vorerst weg).
Resources:
S3Bucket:
Type: AWS::S3::Bucket
SQS:
Type: AWS::SQS::Queue
$ cfn-lint --template template.yaml
--override-spec spec.json
E3001 Invalid or unsupported Type AWS::SQS::Queue for resource SQS in us-east-1
template.yaml:9:9
Eine Eigenschaft durchsetzen
Unsere Teams dürfen Buckets erstellen, aber wir wollen erzwingen, dass die Buckets standardmäßig Verschlüsselung verwenden. Überschreiben Sie die spec.json mit folgendem Code:
{
"IncludeResourceTypes": [
"AWS::*"
],
"ExcludeResourceTypes": [
"AWS::SQS::Queue"
],
"ResourceTypes": {
"AWS::S3::Bucket": {
"Properties": {
"BucketEncryption": {
"Required": true
}
}
}
}
}
Führen Sie den Befehl nun erneut aus und wir sehen, dass die Eigenschaft BucketEncryption jetzt erforderlich ist.
$ cfn-lint --template template.yaml
--override-specs spec.json
E3001 Invalid or unsupported Type AWS::SQS::Queue for resource SQS in us-east-1
template.yaml:12:9
E3003 Property BucketEncryption missing from resource S3Bucket
template.yaml:4:9
Ein verbotener Eigenschaftswert
Die Ressource AWS::S3::Bucket hat eine optionale Eigenschaft AccessControl. Standardmäßig ist der Bucket privat, aber wir wollen verhindern, dass die Teams einen öffentlich zugänglichen Bucket erstellen. Ich muss meine eigene Regeldatei erstellen: append_rules/S3BucketsNotPublic.py.
from cfnlint import CloudFormationLintRule
from cfnlint import RuleMatch
class S3BucketsNotPublic(CloudFormationLintRule):
"""Check if S3 Bucket is Not Public"""
id = 'E9020'
shortdesc = 'S3 Buckets must never be public'
def match(self, cfn):
matches = list()
recordsets = cfn.get_resources(['AWS::S3::Bucket'])
for name, recordset in recordsets.items():
path = ['Resources', name, 'Properties']
full_path = ('/'.join(str(x) for x in path))
if isinstance(recordset, dict):
props = recordset.get('Properties')
if props:
access_control = props.get('AccessControl')
if access_control:
forbidden_values = ['PublicRead','PublicReadWrite']
if access_control in forbidden_values:
message = "Property AccessControl set to {0} is forbidden in {1}"
matches.append(RuleMatch(
path,
message.format(access_control, full_path)
))
return matches
Wenn ich nun meine Vorlage so ändere, dass alle benutzerdefinierten Regeln und Specs-Prüfungen fehlschlagen:
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
AccessControl: PublicRead
Tags:
- Key: Team
Value: Binx
SQS:
Type: AWS::SQS::Queue
WrongSQS:
Type: AWS::SQS:Queue
Und führen Sie die vollständige Prüfung anhand einer Vorlage voller Probleme durch:
$ cfn-lint --template template.yaml
--append-rules ./append_rules
--override-spec spec.json
E3003 Property BucketEncryption missing from resource S3Bucket
template.yaml:4:9
E9020 Property AccessControl set to PublicRead is forbidden in Resources/S3Bucket/Properties
template.yaml:4:9
E9000 Missing Tag Application at Resources/S3Bucket/Properties/Tags
template.yaml:6:11
E9000 Missing Tag Environment at Resources/S3Bucket/Properties/Tags
template.yaml:6:11
E9000 Missing Tag Repository at Resources/S3Bucket/Properties/Tags
template.yaml:6:11
E9000 Missing Tag Commit at Resources/S3Bucket/Properties/Tags
template.yaml:6:11
E9000 Missing Tag Expiry at Resources/S3Bucket/Properties/Tags
template.yaml:6:11
E9000 Missing Tag CIA at Resources/S3Bucket/Properties/Tags
template.yaml:6:11
E3001 Invalid or unsupported Type AWS::SQS:Queue for resource WrongSQS in us-east-1
template.yaml:10:9
E3001 Invalid or unsupported Type AWS::SQS::Queue for resource SQS in us-east-1
template.yaml:12:9
Fazit
Wenn Sie dies in Ihrer CI/CD-Pipeline implementieren möchten, vergessen Sie nicht, die Eigenschaft Tags für jede Ressource, die Tagging unterstützt, erforderlich zu machen. Entweder fügen Sie dies Ihrer Spezifikationsdatei hinzu oder Sie erstellen eine benutzerdefinierte Regel.
Oft sind Teams für ihre eigene CI/CD-Pipeline verantwortlich und könnten diesen cfn-lint-Befehl, Spezifikationsdateien oder benutzerdefinierte Regeln leicht blockieren. In diesem Fall ist ein Peer-Review eine Option. Das Plattformteam prüft, ob das DevOps-Team nicht an cfn-lint, spec-Dateien oder benutzerdefinierten Regeln herumgepfuscht hat. Es verbessert unsere Sicherheitsmaßnahmen.
Einige Dinge, die ich in diesem Blogpost beschreibe, können mit AWS IAM-Richtlinien gesichert werden. Ich denke, sie ergänzen sich gegenseitig. Denn die Richtlinie wird angewendet, wenn Sie eine Bereitstellung ausführen, während dieses cfn-lint in Ihrer IDE und in Ihren CI/CD-Pipelines verwendet werden kann. Das spart eine Menge Zeit und das Feedback ist viel schneller.
Viele Vorlagen werden nach dem Prinzip von Versuch und Irrtum geschrieben. Sie schreiben eine Cloudformation, stellen sie in einer Sandbox bereit und sie sollte in der Produktion funktionieren. Mit diesem cfn-lint können wir die aktuelle Validierung nutzen und mit unseren eigenen Regeln erweitern, so dass wir nicht immer wieder Fehler machen. Wir müssen keine langen Dokumente über die Regeln erstellen, sondern es wird zu Code. Während wir in der Sandbox testen, können wir mit einem einzigen Befehl prüfen, ob es auch in der Produktion läuft.
Checkout: GitHub - aws-cloudformation/cfn-lint für weitere Informationen und Aktualisierungen!
Verfasst von
Martijn van Dongen
Unsere Ideen
Weitere Blogs
Contact



