Blog

Sichere S3 Bucket-Konstrukte mit CDK Version 2

Yvo van Zee

Aktualisiert Oktober 16, 2025
8 Minuten

Hintergrund

Wie in meinem letzten Blog erwähnt, haben Unternehmen oft strenge Sicherheitsstandards. Zum Beispiel die Anforderung, dass die Bereitstellung über Infrastructure as Code erfolgen muss, die Bereitstellung auf AWS-Konten über Pipelines usw. Die meisten Unternehmen gehen noch einen Schritt weiter und beschreiben, wie die Ressourcen in der Cloud sicher konfiguriert werden sollen. Dienste wie AWS Organizations, SecurityHub, Config, Inspector und GuardDuty ermöglichen es, Kontrollen zu steuern und zu bewerten und diese Ergebnisse mit dem Security Framework des Unternehmens zu messen. In diesem Blog wird beschrieben, wie Sie ein sicheres CDK S3 Bucket Konstrukt erstellen, das dem Security Framework eines solchen Unternehmens entspricht.

Voraussetzungen

  • Zugriff auf ein AWS-Konto mit den entsprechenden Rechten für die Bereitstellung von Ressourcen.

  • CDK-Kenntnisse. Da ich zu Beginn direkt über CDK und CDK-Konstrukte sprechen werde, sind Kenntnisse zu diesen Themen eine Voraussetzung. Glücklicherweise hat AWS Workshops zu diesem Thema eingerichtet. Schauen Sie sich diese an, wenn Sie CDK und CDK-Pipelines ausprobieren möchten.

  • Projen, eine neue Generation von Projektgeneratoren, mit denen Sie Ihr eigenes CDK-Konstrukt erstellen und es in Artefakt-Repositories wie pypi oder npm veröffentlichen können.

Ich habe mich für die Verwendung von Projen entschieden. Es ist von Haus aus in der Lage, AWS CDK-Typescript-Konstrukte zu erstellen und alles für Sie bei der Integration mit Github einzurichten.

Installation

Die Idee dabei ist, ein S3-Eimer-Konstrukt zu erstellen, das dem Security Framework entspricht. Dies wird die Chief Information Security Officers (CISO) in einem Unternehmen glücklich machen.

Beginnen wir also damit, ein Verzeichnis zu erstellen und projen zu installieren:

➜ mkdir secure_bucket_construct && cd secure_bucket_construct
➜ npm install -g projen
➜ npx projen neu awscdk-construct

Mit diesem letzten Befehl erstellen Sie ein projen-Projekt, in dem wir unser S3 Secure Bucket-Konstrukt aufbauen.

Szenario der realen Welt

In dem Unternehmen, in dem ich jetzt tätig bin, wurde ein strenges Sicherheits-Framework eingeführt. Dieses strenge Rahmenwerk führt zu AWS-Konfigurationsregeln, die Ihre Ressourcen überprüfen und sie als nicht konform markieren, wenn sie dieser bestimmten Regel nicht entsprechen. Als DevOps-Team müssen wir alle unsere Ressourcen konform machen, bevor wir die Freigabe erhalten, mit unserer Anwendung zum User Acceptance Testing (UAT)-Konto überzugehen. Wie in meinem vorherigen Blog beschrieben, wird die gesamte Anwendung über AWS CDK bereitgestellt.

Eine dieser Ressourcen, die als nicht konforme Ressourcen auftauchen, wenn sie über CDK bereitgestellt werden, sind S3 Buckets. Der Grund für die Nichtkonformität mit dem Security Framework ist, dass ein S3 Bucket die folgenden Konfigurationen aufweisen muss:

  • Der öffentliche Zugang muss gesperrt werden

  • Versionierung muss aktiviert sein

  • Die Verbindung zu einem Bucket muss Secure Socket Layer verwenden

  • Die Zugriffsprotokollierung muss für den Bucket aktiviert sein

  • Bucket und Inhalt müssen mit einem vom Kunden verwalteten KMS-Schlüssel verschlüsselt werden

Unser Team in dem Unternehmen, für das ich arbeite, verwendet eine Menge Buckets in seiner Anwendung. Bei einer normalen Implementierung des Level 2 S3-Konstrukts, wie es vom CDK-Team erstellt wurde, würde der Code wie folgt aussehen:

new Bucket(this, 'Bucket',{
  enforceSSL: true,
  versioniert: true,
  accessControl: BucketAccessControl.LOG_DELIVERY_WRITE,
  serverAccessLogsPrefix: 'access-logs',
  blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
  Verschlüsselung: BucketEncryption.KMS,
  encryptionKey: new Key(this, 'BucketKey', {
  enableKeyRotation: true,
  trustAccountIdentities: true,
  })
})

Dies führt zu 12 Zeilen Code. Stellen Sie sich vor, Sie erstellen viele Buckets in Ihrer Anwendung. Natürlich können Sie dafür eine globale Funktion erstellen, aber was ist, wenn Sie diese innerhalb Ihrer Organisation gemeinsam nutzen möchten?

Wäre es nicht bequemer, einfach ein sicheres S3 Bucket-Konstrukt zu importieren, das sich um all dies kümmert, sowohl aus Sicht der Sicherheit als auch aus Sicht der DevOps? Ich denke schon. Lassen Sie uns also mit unserem Secure S3 Bucket Konstrukt beginnen.

Bauen gehen

Okay, fangen wir an zu bauen. Öffnen Sie zunächst die Datei .projenrc.js in Ihrem bevorzugten Code-Editor. Meine sieht wie folgt aus.

const = require('projen');
const project = new awscdk.AwsCdkConstructLibrary({
  Autor: 'Yvo van Zee',
  authorAddress: 'yvo@yvovanzee.nl',
  cdkVersion: '2.4.0'
  defaultReleaseBranch: 'main',
  Name: 'secure_bucket_construct',
  repositoryUrl: 'https://github.com/yvthepief/secure_bucket_construct.git',

  // deps: [], /* Laufzeitabhängigkeiten dieses Moduls. */
  // description: undefined, /* Die Beschreibung ist nur eine Zeichenfolge, die den Zweck des Pakets verdeutlicht. */
  // devDeps: [], /* Build-Abhängigkeiten für dieses Modul. */
  // packageName: undefiniert, /* Der "Name" in package.json. */
  // release: undefiniert, /* Fügen Sie diesem Projekt ein Release-Management hinzu. */
});
project.synth();

Dabei ist zu beachten, dass ich die neue CDK-Version 2 verwende. Als nächstes müssen Sie die CDK-Abhängigkeiten installieren. Da CDK Version 2 nur zwei 'Basis'-Pakete hat, die benötigt werden, ist es viel einfacher mit der Abhängigkeitshölle. Fügen wir also die Pakete aws-cdk-lib und constructs hinzu. Fügen Sie sie zur Datei .projenrc.js hinzu:

  peerDependencies: [
  'aws-cdk-lib',
  'Konstrukte'
  ],

Wenn Sie die Konfiguration von .projenrc.js hinzufügen oder ändern, müssen Sie den Befehl npx projen ausführen. Dadurch werden alle Pakete installiert und Konfigurationsdateien erzeugt, die von projen verwaltet werden.

src code

Da wir unsere Richtlinien für den sicheren Eimer bereit haben, können wir direkt mit dem Aufbau des Konstrukts beginnen. Öffnen Sie die Datei src/index.ts. Und ersetzen Sie das Hallo-Welt-Beispiel durch den folgenden Code:

// Importieren Sie die erforderlichen Pakete
importieren von 'aws-cdk-lib/aws-kms';
import { BlockPublicAccess, Bucket, BucketAccessControl, BucketEncryption, BucketProps } from 'aws-cdk-lib/aws-s3';
importieren von 'constructs';

export class SecureBucket extends Construct {
  public bucket: Eimer;

  // Optional erlauben Sie die Übergabe von Eimerrequisiten
  constructor(scope: Construct, id: string, props?: BucketProps) {
  super(scope, id);

  // Überschreiben Sie Bucket Props mit sicheren Standardwerten und machen Sie sie obligatorisch
  let newProps: BucketProps = {
  ...Requisiten,
  // Erzwingen Sie die Verschlüsselung mit einem benutzerdefinierten verwalteten Schlüssel
  Verschlüsselung: props && props.encryption && props.encryption != BucketEncryption.UNENCRYPTED
  ? props.encryption
  : BucketEncryption.KMS,
  // Erstellen Sie den Verschlüsselungsschlüssel mit aktivierter Rotation
  encryptionKey: new Key(this,  $61dc0f4a5c5b192856ecb6ea-key, { enableKeyRotation: true }),
  blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
  versioniert: true,
  enforceSSL: true,
  accessControl: BucketAccessControl.LOG_DELIVERY_WRITE,
  serverAccessLogsPrefix: 'access-logs',
  };

  // Aktuellen Eimer erstellen
  this.bucket = new Bucket(this,  $61dc0f4a5c5b192856ecb6ea-bucket, newProps);
  }
}

Lassen Sie uns den Code aufschlüsseln.

Die Importe hier sind die neuen Stilimporte für CDK Version 2. Alle Pakete sind unter dem Paket gebündelt. Für einen sicheren Bucket benötigen wir die Module aws-kms und aws-s3.

Der Export ist vom Typ Construct statt Stack, da wir hier ein Construct erstellen. Außerdem werden die Bucket-Props übergeben und mit den sicheren Standardwerten überschrieben. Aktivieren Sie zum Beispiel die Verschlüsselung, aber lassen Sie nur KMS zu, das ein Custom Managed Key (CMK) ist, und erstellen Sie den Schlüssel im Handumdrehen.

Andere Optionen entsprechen den bereits erwähnten Sicherheitsregeln. Als letztes erstellen Sie den eigentlichen Bucket mit den neuen Eigenschaften.

Testen Sie

Da das Bucket-Konstrukt auf diese Weise in Ordnung ist, besteht die einzige Möglichkeit, sicher zu gehen, darin, dem Projekt auch Tests hinzuzufügen. Benennen Sie die Datei test/hello.test.ts in einen passenderen Namen um, solange sie mit test.ts endet.

Zum Testen verfügt die aws-cdk-lib über ein Assertions-Modul. Mit Assertions ist es möglich, Tests gegen CDK-Anwendungen zu schreiben, wobei der Schwerpunkt auf CloudFormation-Vorlagen liegt.

Lassen Sie uns wieder mit den Importen beginnen.

importieren Sie { App, Stack } von 'aws-cdk-lib';
importieren Sie { Match, Template } von 'aws-cdk-lib/assertions';
import { Bucket, BucketEncryption } from 'aws-cdk-lib/aws-s3';
importieren von '../src';

test('Zeigt den zugrunde liegenden Eimer an', () =>  {
  const mockApp = new App();
  const stack = new Stack(mockApp, 'testing-stack');

  const bucketWrapper = new SecureBucket(stack, 'testing', {});
  expect(bucketWrapper.bucket).toBeInstanceOf(Bucket);
});

Oben wurde auch ein erster Test erstellt. Bei diesem Test wird eine Mock-App mit einem Test-Stack erstellt. Ein sicherer Bucket wird erstellt und dem Test-Stack hinzugefügt und als bucketWrapper gespeichert. Der eigentliche Test prüft dann, ob es sich bei dem mit unserem Konstrukt bucketWrapper.bucket erstellten Bucket um eine Instanz Bucket aus dem S3-Modul handelt.

Nun ist es an der Zeit zu testen, ob unser erster Test korrekt ist. Führen Sie npx projen test in der Befehlszeile aus. Dieser Test schlägt fehl, weil unsere Importe. Wir haben Importe, die wir nur bei diesem einen Test nicht verwenden. Die Assertions und BucketEncryption werden noch nicht verwendet, kommen aber später zum Einsatz. Projen wird den Test nicht bestehen.

Einer der Vorteile von projen besteht darin, dass Sie Ihr Projekt schlank und einfach halten können. Nach der Deaktivierung einiger Importe läuft der Test problemlos.

➜ secure_bucket_construct git:(main) ✗ npx projen test
Garnlauf v1.22.17
$ npx projen test
  test | jest --passWithNoTests --all --updateSnapshot
  PASS test/secure_bucket.test.ts (7.999 s)
  ✓ Zeigt den zugrunde liegenden Eimer an (11 ms)

----------|---------|----------|---------|---------|-------------------
Datei | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s  
----------|---------|----------|---------|---------|-------------------
Alle Dateien | 100 | 60 | 100 | 100 | 100  
  index.ts | 100 | 60 | 100 | 100 | 100 | 17  
----------|---------|----------|---------|---------|-------------------
Testsuiten: 1 bestanden, 1 insgesamt
Tests: 1 bestanden, 1 insgesamt
Schnappschüsse: 0 insgesamt
Zeit: 8.156 s
Alle Testsuiten wurden ausgeführt.
  test " eslint | eslint --ext .ts,.tsx --fix --no-error-on-unmatched-pattern src test build-tools .projenrc.js
Erledigt in 21.29s.

Jetzt können weitere Tests hinzugefügt werden. Tests wie:

  • Hat einen verschlüsselten Bucket

  • Hat BucketVersioning aktiviert

  • Es ist nicht möglich, BucketVersioning zu deaktivieren

  • Hat BlockPublicAccess auf BLOCK_ALL

  • Hat Bucket Logging aktiviert

  • Erlaubt keine unverschlüsselten Buckets

  • Erlaubt keine unverschlüsselten Uploads

  • Verwendet KMS-Verschlüsselung mit Schlüsselrotation beim Schlüssel

Damit dieser Artikel nicht zu lang wird und alle Tests erklärt werden, finden Sie den Code für den zusätzlichen Test im GitHub-Projekt.

Verteilen Sie

Mit projen ist es möglich, Ihr erstelltes Paket an NPM und andere Paketanbieter zu verteilen. Alles, was Sie tun müssen, ist, ein GitHub-Geheimnis für Ihr Repository einzurichten und zum Beispiel ein NPM-Token zu generieren. Ein gutes Beispiel finden Sie in der projen-test Dokumentation.

Versuchen Sie es selbst

Experimentieren Sie mit projen, um Ihre eigenen Konstrukte zu erstellen. Den Code finden Sie auf meinem GitHub.

Verfasst von

Yvo van Zee

I'm an AWS Cloud Consultant working at Oblivion. I specialised myself in architecting and building high available environments using automation (Infrastructure as Code combined with Continuous Integration and Continuous Delivery (CI/CD)). Challenging problems and finding solutions which fit are my speciality.

Contact

Let’s discuss how we can support your journey.