aws, ai, security, serverless

Serverlös AI-drivad tjänst för innehållsmoderering

2024-10-31
This post cover image
aws cloud serverless ai security

Questo file e stato tradotto automaticamente dall'IA, potrebbero verificarsi errori

För ungefär ett år sedan skapade jag ett blogginlägg om skapandet av en Filhanterartjänst. I detta inlägg kommer vi att använda denna tjänst som vår grund och utöka den med innehållsmoderering. Vi kommer att använda GuardDuty och Rekognition för att hjälpa till med denna uppgift. Som vanligt kommer allt att vara serverlöst och händelsedrivet.

Sammanfattning

För att uppfrisha allas minne, låt oss börja med en kort sammanfattning.

Tjänsten kommer att lagra filer i S3 och hålla en registrering av alla filer i en DynamoDB-tabell. Systemöversikten ser ut så här, med ett API som exponerar funktionalitet till användaren och sedan utför arbetet på ett serverlöst och händelsedrivet sätt.

Arkitekturöversikt för den ursprungliga filhanteraren

Uppladdningsflödet initieras av en klient som anropar API:et där en Lambda-funktion skapar en försignerad S3-URL som klienten kan använda för att ladda upp filen. Vi laddar inte upp filer direkt över API:et, eftersom Amazon API Gateway har en maximal payload-storlek på 10 MB, och för att stödja alla typer av filer kommer detta att bli en begränsning.

När klienten sedan laddar upp filen till S3 kommer detta att generera en händelse, delen under den streckade linjen i bilden, vilket kommer att anropa en StepFunction som kommer att uppdatera filinventeringen.

Uppladdningsflöde för den ursprungliga filhanteraren

Utökad arkitektur

I den utökade arkitekturen lägger vi till funktionalitet för att använda GuardDuty S3 malwareskanning och Rekognition för bildmoderering. GuardDuty kommer att skanna nya filer som kommer in i S3-bucketen, som jag kallar staging, en tagg kommer att läggas till på objektet och skanningsresultatet kommer att skickas till standardhändelsebussen. Skanningsresultatet, om det är OK, kommer att anropa en StepFunction som använder Rekognition för bildmoderering. Jag har implementerat samma logik i denna StepFunction och lägger till en tagg på objektet och skickar en händelse till en händelsebuss. Slutligen flyttas filer till antingen en karantän- eller lagringsobjektbuckets.

Varje del av lösningen är avkopplad och kan köra oberoende och ett saga-mönster används för att flytta logiken till nästa fas.

Utökad översikt med innehållsmoderering

Nu låt oss dyka djupare in i varje del av denna lösning.

Malwareskanning

GuardDuty Malwareskanning kräver inte mycket setup. Detta är en helt hanterad funktion i GuardDuty och det enda som krävs är att konfigurera den. GuardDuty kommer då att plocka upp nya objekt automatiskt.

Malwareskanningsflöde

För att uppnå detta flöde är det enda vi behöver göra att skapa en S3MalwareProtectionPlan och tilldela den lämpliga behörigheter. En viktig sak att komma ihåg är att om du krypterar dina objekt i S3 med en Customer Managed Key, glöm inte att ge GuardDuty behörighet att dekryptera med denna nyckel.

  S3MalwareProtectionPlan:
    Type: AWS::GuardDuty::MalwareProtectionPlan
    Properties:
      Actions:
        Tagging:
          Status: ENABLED
      ProtectedResource:
        S3Bucket:
          BucketName:
            Fn::ImportValue: !Sub ${CommonInfraStackName}:staging-bucket-name
      Role: !GetAtt S3MalwareProtectionPlanRole.Arn

Bildmoderering

Modereringsdelen med Rekognition innebär ett par fler steg. En StepFunction anropas av resultatet från malwareskanningen och anropar Rekognition för att moderera bilden. Denna StepFunction kommer sedan att tagga objektet och skicka skanningsresultatet till EventBridge anpassad servicebuss. En viktig sak att komma ihåg är att om du krypterar dina objekt i S3 med en Customer Managed Key, glöm inte att ge behörigheter att dekryptera med denna nyckel. Rekognition ger dig ett konstigt fel Unsupported och inte ett tydligt fel på varför det misslyckades i detta fall.

Bildmodereringsflöde

För att uppnå detta flöde är det enda vi behöver göra att skapa StateMachine och konfigurera de händelser den bör anropas på.

  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_FOUND

StateMachine-definitionen är ganska stor, och det finns behov av lite magi. Eftersom du inte kan lägga till taggar på ett S3-objekt, måste vi först hämta alla befintliga taggar, lägg till vår nya tagg och placera hela arrayen av taggar på objektet. Detta skulle förmodligen vara enklare att göra i en Lambda-funktion, men var är det roliga med det. Inbyggda funktioner för vinnaren....

Comment: Modera bilder med Rekognition
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

Avsluta uppladdningen

Den sista delen av denna lösning är att reagera på modereringen och placera innehållet antingen i karantänbuckets eller i lagringsbuckets på lång sikt. För detta använder jag två olika StepFunction med vissa skillnader i händelsen som anropar dem.

Avslutat flöde

För att uppnå detta flöde skapas en ny StateMachine.

  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 

Med en StateMachine-definition som är lite enklare att följa än bildmoderationsdelen.

Comment: Hantera resultat och kopiera filer till lagringsbuckets
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: null

Slutsats

Detta var ett kort inlägg om hur jag utökade min tidigare byggda filhanterare med malwareskanning och bildmoderering. Genom att endast använda hanterade tjänster gjorde detta ett ganska enkelt uppdrag.

För att få hela källkoden och distribuera den själv, besök Serverless-Handbook Image Moderation

Avslutande ord

Glöm inte att följa mig på LinkedIn och X för mer innehåll, och läs resten av mina Bloggar

Som Werner säger! Nu gå och bygg!