Serverless KI-gestützte Inhaltsmoderationsdienst

Diese Datei wurde automatisch von KI ubersetzt, es konnen Fehler auftreten
Vor etwa einem Jahr habe ich einen Blogbeitrag über die Erstellung eines Dateiverwaltungsdienstes veröffentlicht. In diesem Beitrag werden wir diesen Dienst als Grundlage verwenden und ihn um Inhaltsmoderation erweitern. Wir werden GuardDuty und Rekognition zur Unterstützung dieser Aufgabe einsetzen. Wie üblich wird alles serverlos und ereignisgesteuert bleiben.
Zusammenfassung
Um das Gedächtnis aufzufrischen, beginnen wir mit einer kurzen Zusammenfassung.
Der Dienst speichert Dateien in S3 und führt einen Datensatz aller Dateien in einer DynamoDB-Tabelle. Der Systemüberblick sieht wie folgt aus, wobei eine API die Funktionalität für den Benutzer exponiert und die Arbeit anschließend auf serverlose und ereignisgesteuerte Weise ausgeführt wird.

Der Upload-Ablauf wird durch einen Client eingeleitet, der die API aufruft, wobei eine Lambda-Funktion eine vorunterzeichnete S3-URL erstellt, die der Client zur Upload der Datei verwenden kann. Wir laden die Datei nicht direkt über die API hoch, da Amazon API Gateway eine maximale Payload-Größe von 10 MB hat, was bei der Unterstützung aller Arten von Dateien zu einer Einschränkung werden würde.
Wenn der Client die Datei anschließend in S3 hochlädt, wird ein Ereignis ausgelöst, der Teil unter der gestrichelten Linie im Bild, der einen StepFunction aufruft, der das Dateiverzeichnis aktualisiert.

Erweiterte Architektur
In der erweiterten Architektur fügen wir Funktionalität hinzu, um GuardDuty S3-Malware-Scans und Rekognition für die Bildmoderation zu nutzen. GuardDuty wird neue Dateien, die im S3-Bucket ankommen, scannen, den ich Staging nenne, ein Tag wird dem Objekt hinzugefügt und das Scan-Ergebnis wird an den Standard-Event-Bus gesendet. Das Scan-Ergebnis, falls OK, wird einen StepFunction aufrufen, der Rekognition zur Bildmoderation nutzt. Ich habe die gleiche Logik in diesem StepFunction implementiert und füge ein Tag am Objekt hinzu und sende ein Ereignis an einen Event-Bus. Schließlich werden die Dateien entweder in einen Quarantäne- oder einen Langzeit-Speicher-Bucket verschoben.
Jeder Teil der Lösung ist entkoppelt und kann unabhängig laufen, und ein Saga-Muster wird angewendet, um die Logik in die nächste Phase zu überführen.

Lassen Sie uns nun etwas tiefer in jeden Teil dieser Lösung eintauchen.
Malware-Scan
Der GuardDuty-Malware-Scan erfordert nicht viel Einrichtungsaufwand. Dies ist eine vollständig verwaltete Funktion in GuardDuty, und das Einzige, was erforderlich ist, ist die Konfiguration. GuardDuty wird dann automatisch neue Objekte erfassen.

Um diesen Ablauf zu erreichen, müssen wir nur einen S3MalwareProtectionPlan erstellen und ihm entsprechende Berechtigungen zuweisen. Eine wichtige Sache, die man sich merken sollte: Wenn Sie Ihre Objekte in S3 mit einem Customer Managed Key verschlüsseln, vergessen Sie nicht, GuardDuty Berechtigungen zu geben, um mit diesem Schlüssel zu entschlüsseln.
S3MalwareProtectionPlan:
Type: AWS::GuardDuty::MalwareProtectionPlan
Properties:
Actions:
Tagging:
Status: ENABLED
ProtectedResource:
S3Bucket:
BucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:staging-bucket-name
Role: !GetAtt S3MalwareProtectionPlanRole.ArnBildmoderation
Der Moderationsteil mit Rekognition beinhaltet ein paar weitere Schritte. Ein StepFunction wird durch das Ergebnis des Malware-Scans aufgerufen und ruft Rekognition auf, um das Bild zu moderieren. Dieser StepFunction wird dann das Objekt taggen und das Scan-Ergebnis an den EventBridge benutzerdefinierten Service-Bus senden. Eine wichtige Sache, die man sich merken sollte: Wenn Sie Ihre Objekte in S3 mit einem Customer Managed Key verschlüsseln, vergessen Sie nicht, Berechtigungen zum Entschlüsseln mit diesem Schlüssel zu geben. Rekognition gibt Ihnen einen seltsamen Fehler Unsupported und keinen klaren Fehler, warum es in diesem Fall gescheitert ist.

Um diesen Ablauf zu erreichen, müssen wir nur die StateMachine erstellen und die Ereignisse einrichten, bei denen sie aufgerufen werden soll.
ModerateImageStateMachineStandard:
Type: AWS::Serverless::StateMachine
Properties:
DefinitionUri: StateMachine/moderation.asl.yaml
DefinitionSubstitutions:
EventBusName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:event-bus-name
Tracing:
Enabled: true
Policies:
- Statement:
- Effect: Allow
Action:
- logs:*
Resource: "*"
- S3FullAccessPolicy:
BucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:staging-bucket-name
- EventBridgePutEventsPolicy:
EventBusName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:event-bus-name
- RekognitionDetectOnlyPolicy: {}
Events:
GuardDutyMalwareScanResult:
Type: EventBridgeRule
Properties:
InputPath: $.detail
Pattern:
source:
- aws.guardduty
detail-type:
- GuardDuty Malware Protection Object Scan Result
detail:
scanResultDetails:
scanResultStatus:
- NO_THREATS_FOUNDDie StateMachine-Definition ist ziemlich groß und erfordert etwas Magie. Da Sie keinen Tags zu einem S3-Objekt hinzufügen können, müssen wir zuerst alle vorhandenen Tags abrufen, unseren neuen Tag anhängen und den gesamten Array von Tags auf das Objekt setzen. Dies wäre wahrscheinlich einfacher in einer Lambda-Funktion zu implementieren, aber wo wäre da der Spaß? Intrinsische Funktionen für den Sieg....
Comment: Bilder mit Rekognition moderieren
StartAt: Debug
States:
Debug:
Type: Pass
Next: Get Object Metadata
Get Object Metadata:
Type: Task
Parameters:
Bucket.$: $.s3ObjectDetails.bucketName
Key.$: $.s3ObjectDetails.objectKey
Resource: arn:aws:states:::aws-sdk:s3:headObject
Next: Get Object Tags
ResultPath: $.S3MetaData
Get Object Tags:
Type: Task
Parameters:
Bucket.$: $.s3ObjectDetails.bucketName
Key.$: $.s3ObjectDetails.objectKey
Resource: arn:aws:states:::aws-sdk:s3:getObjectTagging
Next: Is File Supported?
ResultPath: $.s3Tags
Is File Supported?:
Type: Choice
Choices:
- Or:
- Variable: $.S3MetaData.ContentType
StringMatches: image/png
- Variable: $.S3MetaData.ContentType
StringMatches: image/jpeg
Next: Moderate Image
Default: File Not Supported
File Not Supported:
Type: Pass
Next: Add FILE_NOT_SUPPORTED to Object Tags Array
Parameters:
ContentTypes: []
ModerationLabels: []
ModerationModelVersion: "7.0"
ThreatsFound: "-1"
ResultPath: $.RekognitionModeration
Add FILE_NOT_SUPPORTED to Object Tags Array:
Type: Pass
ResultPath: $.scanResult
Parameters:
status: FILE_NOT_SUPPORTED
newTagSet.$: >-
States.StringToJson(States.Format('[{},{}]',
States.ArrayGetItem(States.StringSplit(States.JsonToString($.s3Tags.TagSet),
'[]'),0), '{"Key":"ImageModerationStatus","Value":"FILE_NOT_SUPPORTED"}'))
Next: Tag S3 Object
Moderate Image:
Type: Task
Parameters:
Image:
S3Object:
Bucket.$: $.s3ObjectDetails.bucketName
Name.$: $.s3ObjectDetails.objectKey
Resource: arn:aws:states:::aws-sdk:rekognition:detectModerationLabels
Next: File Supported
ResultPath: $.RekognitionModeration
File Supported:
Type: Pass
Parameters:
ThreatsFound.$: States.ArrayLength($.RekognitionModeration.ModerationLabels)
ContentTypes.$: $.RekognitionModeration.ContentTypes
ModerationLabels.$: $.RekognitionModeration.ModerationLabels
ModerationModelVersion.$: $.RekognitionModeration.ModerationModelVersion
ResultPath: $.RekognitionModeration
Next: Was Threats Found?
Was Threats Found?:
Type: Choice
Choices:
- Variable: $.RekognitionModeration.ThreatsFound
NumericGreaterThan: 0
Next: Add THREATS_DETECTED to Object Tags Array
Default: Add NO_THREATS to Object Tags Array
Add THREATS_DETECTED to Object Tags Array:
Type: Pass
Parameters:
status: THREATS_FOUND
newTagSet.$: >-
States.StringToJson(States.Format('[{},{}]',
States.ArrayGetItem(States.StringSplit(States.JsonToString($.s3Tags.TagSet),
'[]'),0), '{"Key":"ImageModerationStatus","Value":"THREATS_FOUND"}'))
ResultPath: $.scanResult
Next: Tag S3 Object
Add NO_THREATS to Object Tags Array:
Type: Pass
Next: Tag S3 Object
Parameters:
status: NO_THREATS_FOUND
newTagSet.$: >-
States.StringToJson(States.Format('[{},{}]',
States.ArrayGetItem(States.StringSplit(States.JsonToString($.s3Tags.TagSet),
'[]'),0), '{"Key":"ImageModerationStatus","Value":"NO_THREATS_FOUND"}'))
ResultPath: $.scanResult
Tag S3 Object:
Type: Task
Parameters:
Bucket.$: $.s3ObjectDetails.bucketName
Key.$: $.s3ObjectDetails.objectKey
Tagging:
TagSet.$: $.scanResult.newTagSet
Resource: arn:aws:states:::aws-sdk:s3:putObjectTagging
ResultPath: null
Next: Post Scan Result Event
Post Scan Result Event:
Type: Task
Resource: arn:aws:states:::events:putEvents
Parameters:
Entries:
- Detail:
metadata: {}
data:
id.$: $.s3ObjectDetails.objectKey
status.$: $.scanResult.status
scanData.$: $.RekognitionModeration
DetailType: Moderation Scan Completed
EventBusName: ${EventBusName}
Source: ImageModeration
End: true
ResultPath: null
Abschluss des Uploads
Der letzte Teil dieser Lösung besteht darin, auf die Moderation zu reagieren und den Inhalt entweder im Quarantäne-Bucket oder im Langzeit-Bucket zu platzieren. Dafür verwende ich zwei verschiedene StepFunctions mit einigen Unterschieden in den Ereignissen, die sie aufrufen.

Um diesen Ablauf zu erreichen, wird eine neue StateMachine erstellt.
MoveFilesToPermanentStorageStateMachineStandard:
Type: AWS::Serverless::StateMachine
Properties:
DefinitionUri: StateMachine/move-to-permanent-storage.asl.yaml
DefinitionSubstitutions:
EventBusName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:event-bus-name
StorageBucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:storage-bucket-name
StagingBucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:staging-bucket-name
Tracing:
Enabled: true
Policies:
- Statement:
- Effect: Allow
Action:
- logs:*
Resource: "*"
- S3FullAccessPolicy:
BucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:staging-bucket-name
- S3FullAccessPolicy:
BucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:storage-bucket-name
- EventBridgePutEventsPolicy:
EventBusName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:event-bus-name
Events:
NoModerationThreatsFoundEvent:
Type: EventBridgeRule
Properties:
EventBusName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:event-bus-name
InputPath: $.detail
Pattern:
source:
- ImageModeration
detail-type:
- Moderation Scan Completed
detail:
data:
status:
- NO_THREATS_FOUND Mit einer StateMachine-Definition, die etwas einfacher zu verfolgen ist als der Bildmoderations-Teil.
Comment: Ergebnis verarbeiten und Dateien in den Speicher-Bucket kopieren
StartAt: Debug
States:
Debug:
Type: Pass
Next: Get Object Metadata
Get Object Metadata:
Type: Task
Parameters:
Bucket: ${StagingBucketName}
Key.$: $.data.id
Resource: arn:aws:states:::aws-sdk:s3:headObject
Next: CopyObject
ResultPath: $.S3MetaData
CopyObject:
Type: Task
Parameters:
Bucket: ${StorageBucketName}
CopySource.$: >-
States.Format('${StagingBucketName}/{}',$.data.id)
Key.$: $.data.id
Resource: arn:aws:states:::aws-sdk:s3:copyObject
ResultPath: null
Next: DeleteObject
DeleteObject:
Type: Task
Parameters:
Bucket: ${StagingBucketName}
Key.$: $.data.id
Resource: arn:aws:states:::aws-sdk:s3:deleteObject
ResultPath: null
Next: Post Event File Moved
Post Event File Moved:
Type: Task
Resource: arn:aws:states:::events:putEvents
Parameters:
Entries:
- Detail:
metadata: {}
data:
id.$: $.data.id
status: STORED
contentType.$: $.S3MetaData.ContentType
fileSize.$: $.S3MetaData.ContentLength
DetailType: Completed
EventBusName: ${EventBusName}
Source: ImageModeration
End: true
ResultPath: nullFazit
Dies war ein kurzer Beitrag darüber, wie ich meinen zuvor erstellten Dateiverwaltungsdienst um Malware-Scans und Bildmoderation erweitert habe. Die Verwendung von nur verwalteten Diensten hat dies zu einer ziemlich einfachen Aufgabe gemacht.
Um den vollständigen Quellcode zu erhalten und ihn selbst zu deployen, besuchen Sie Serverless-Handbuch Bildmoderation
Abschließende Worte
Vergessen Sie nicht, mir auf LinkedIn und X zu folgen, um weitere Inhalte zu erhalten, und lesen Sie den Rest meiner Blogs
Wie Werner sagt! Jetzt loslegen!