1
0

【AWS学習記録5回目】Amazon CloudFrontおよびAWS WAFを用いたアプリを構築してみた

Posted at

概要

をもとにAmazon CloudFrontおよびAWS WAFを用いた翻訳アプリを構築してみました
※ハンズオンの内容と全て一致していません

image.png

CloudFormation

米国東部 (バージニア北部)リージョンに作成

waf.yml
AWSTemplateFormatVersion: 2010-09-09
Resources: 
  #======================
  # WebACL
  #======================  
  WebACL:
    Type: AWS::WAFv2::WebACL
    Properties:
      DefaultAction:
        Allow: {}
      Name: handson-waf
      Rules:
        - Action:
            Block: 
              CustomResponse: 
                ResponseCode: 400
          Name: handson-rule
          Priority: 1
          Statement: 
            GeoMatchStatement: 
              CountryCodes: 
                - JP
          VisibilityConfig: 
            CloudWatchMetricsEnabled: true
            MetricName: BlockRequestsFromJapan
            SampledRequestsEnabled: true
      Scope: CLOUDFRONT
      VisibilityConfig: 
        CloudWatchMetricsEnabled: true
        MetricName: handson-waf
        SampledRequestsEnabled: true

アジアパシフィック (東京)に作成

app.yml
AWSTemplateFormatVersion: 2010-09-09
Resources: 
  #======================
  # Distribution
  #======================
  Distribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        CacheBehaviors:
          - PathPattern: static/*
            TargetOriginId: S3
            ViewerProtocolPolicy: redirect-to-https
            CachePolicyId: !Ref CachePolicy
          - PathPattern: api
            TargetOriginId: RestApi
            ViewerProtocolPolicy: redirect-to-https
            CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
            OriginRequestPolicyId: !Ref OriginRequestPolicy
        DefaultCacheBehavior:
          TargetOriginId: S3
          ViewerProtocolPolicy: redirect-to-https
          CachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
        DefaultRootObject: index.html
        Enabled: true
        Origins: 
          - DomainName: !Sub "${RestApi.RestApiId}.execute-api.${AWS::Region}.amazonaws.com"
            Id: RestApi
            CustomOriginConfig: 
              OriginProtocolPolicy: match-viewer
          - DomainName: !GetAtt Bucket.DomainName
            Id: S3
            OriginAccessControlId: !GetAtt OriginAccessControl.Id
            S3OriginConfig:
              OriginAccessIdentity: ''
        WebACLId: !Sub "arn:aws:wafv2:us-east-1:${AWS::AccountId}:global/webacl/handson-waf/b5739b66-17f5-4e82-b61c-101517f3f028"
  
  #======================
  # CachePolicy
  #======================
  CachePolicy:
    Type: AWS::CloudFront::CachePolicy
    Properties:
      CachePolicyConfig: 
        DefaultTTL: 86400
        MaxTTL: 31536000
        MinTTL: 1
        Name: CachePolicy
        ParametersInCacheKeyAndForwardedToOrigin: 
          CookiesConfig: 
            CookieBehavior: none
          EnableAcceptEncodingBrotli: true
          EnableAcceptEncodingGzip: true
          HeadersConfig: 
            HeaderBehavior: none
          QueryStringsConfig: 
            QueryStringBehavior: none
  
  #======================
  # OriginRequestPolicy
  #======================
  OriginRequestPolicy:
    Type: AWS::CloudFront::OriginRequestPolicy
    Properties:
      OriginRequestPolicyConfig: 
        CookiesConfig: 
          CookieBehavior: none
        HeadersConfig: 
          HeaderBehavior: none
        Name: OriginRequestPolicy
        QueryStringsConfig: 
          QueryStringBehavior: whitelist
          QueryStrings: 
            - input_text
  
  #======================
  # OriginAccessControl
  #======================
  OriginAccessControl:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig: 
        Name: OACj7rotyh3xxkt1uj0wocu
        OriginAccessControlOriginType: s3
        SigningBehavior: always
        SigningProtocol: sigv4

  #======================
  # Bucket
  #======================
  Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: bucketaeb934895097f22ba2d64e053c0c3fa099
      PublicAccessBlockConfiguration: 
        BlockPublicAcls: false
        BlockPublicPolicy: false
        IgnorePublicAcls: false
        RestrictPublicBuckets: false
      WebsiteConfiguration: 
        ErrorDocument: error.html
        IndexDocument: index.html
  
  #======================
  # BucketPolicy
  #======================
  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref Bucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Sid: PublicReadGetObject
            Effect: Allow
            Principal:
              Service: cloudfront.amazonaws.com
            Action:
              - s3:GetObject
            Resource:
              - arn:aws:s3:::bucketaeb934895097f22ba2d64e053c0c3fa099/*
            Condition:
              StringEquals:
                AWS:SourceArn: !Sub "arn:aws:cloudfront::${AWS::AccountId}:distribution/${Distribution}"
  
  #======================
  # RestApi
  #======================
  RestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
      Name: RestApi
  
  #======================
  # Method
  #======================
  Method:
    Type: AWS::ApiGateway::Method
    Properties:
      AuthorizationType: NONE
      HttpMethod: GET
      Integration: 
        Type: "AWS_PROXY"
        IntegrationHttpMethod: "POST"
        Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:Lambdafunction/invocations"
        RequestParameters: 
          integration.request.querystring.input_text: method.request.querystring.input_text
      RequestParameters: 
        method.request.querystring.input_text: true
      ResourceId: !GetAtt RestApi.RootResourceId
      RestApiId: !Ref RestApi

  #======================
  # Stage
  #======================
  Stage:
    Type: AWS::ApiGateway::Stage
    Properties:
      StageName: api
      RestApiId: !Ref RestApi
      DeploymentId: !Ref Deployment
  
  #======================
  # Deployment
  #======================
  Deployment:
    DependsOn: Method
    Type: AWS::ApiGateway::Deployment
    Properties:
      RestApiId: !Ref RestApi

  #======================
  # Permission
  #======================
  Permission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref Function
      Principal: apigateway.amazonaws.com
  
  #======================
  # Function
  #======================
  Function:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: Lambdafunction
      Handler: index.lambda_handler
      Runtime: python3.11
      Role: !GetAtt Role.Arn
      Code: 
        ZipFile: !Sub |
          import boto3

          def lambda_handler(event, context):
              translate = boto3.client(service_name='translate', use_ssl=True)
              result = translate.translate_text(Text=event['queryStringParameters']['input_text'], SourceLanguageCode="ja", TargetLanguageCode="en").get('TranslatedText')
              return {
                  'statusCode': 200,
                  'body': result
              }

  #======================
  # Role
  #======================
  Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/TranslateReadOnly

S3バケットの内容

index.html
<!DOCTYPE html>
<html>
  <head>
    <link rel="icon" type="image/x-icon" href="static/image/cloudfront.png">
    <link href="static/css/styles.css" rel="stylesheet" type="text/css">
    <script src="static/js/script.js"></script>
  </head>
  <body>
    <form id="form1" action="#">
        <ul>
            <li>
              <img src="static/image/cloudfront.png" width="100" height="100">
            </li>
            <li>
              <p>Input Message</p>
              <textarea id="input_message" rows="4" cols="40"></textarea>
            </li>
            <li>
                <input type="button" onclick="func1()" value="Send" />
                <input type="button" onclick="func2()" value="All Clear"  />
            </li>
            <li>
                <br />
                <p>Output Message</p>
                <textarea id="output_message" rows="4" cols="40"></textarea>
            </li>
          </ul>
    </form>
  </body>
</html>
static/css/style.css
#form1 { text-align : center ;}
ul li{
    list-style: none;
    padding: 10px;
}
static/js/script.js
function func1() {
  var apiurl = 'https://' + document.domain + '/api?input_text=' + document.getElementById("input_message").value;
  var request = new XMLHttpRequest();

  request.open('GET', apiurl, true);
  request.responseType = 'text';
  request.onload = function () {
      var data = this.response;
      document.getElementById("output_message").innerHTML = data;
  };
  request.send();
}

function func2() {
    document.getElementById("input_message").value = '';
    document.getElementById("output_message").innerHTML = '';
}

static/image/cloudfront.png

参考文献

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0