AWS Lambda@Edge향천현으로부터의 방문을 차단하다

개시하다


이 보도는 뉴스 보도다.
본 보도에서 소개한 바와 같이 특정 지역에서 온 방문 블록 등은 추천하지 않는다.
그러나 향천현의 대중 평론에 대한 대답에 따르면
Q: 경영자의 대책, 협력 의무에 대해 구체적으로 어떤 대책을 강구했다.가가와현의 교통 체증도 포함됩니까?
A:경영자에게 자율적인 조치를 호소해야 한다고 생각합니다.
[전22문] "1일 60분의 근거" "왜 의사록이 없느냐" - 가가와현의 "온라인 게임 의존증 대책 조례(가정명)"와 의회사무국의 일문일답
라고 답한 뒤 만일 이 조례가 통과된다면 경영자 자율행동의 일환으로 향천현에서 온 방문 블록을 진행할 가능성을 부정할 수 없다.
조례가 통과된 후 향천현의 방문이 기술적으로 가능한지 검증하기 위해 나는 이 글을 썼다.

개요


그럼 어떻게 이루어질까요?
  • 만일을 대비하여 향천현으로부터의 방문을 피하다
  • 그렇다면 자바스크립트의 Geolocation API를 사용해 가가와로부터의 접근을 차단하는 방법을 소개했다.
    이 기사는 더 간단합니다. AWSLambda@EdgeIPinfoDB에 IP 주소를 묻고 RegionCode가 향천현을 차단하는 방법을 소개한다.AWS 자원의 조작은terraform,aws cli,Lamda의 디자인을 사용하고lambroll을 사용합니다.
  • lambroll: https://github.com/fujiwara/lambroll
  • terraform: https://www.terraform.io/
  • AWS WAF 같은 데서 좀 더 간단하게 하려고 했는데 WAF의 GeoCode는 국가 단위이기 때문에 불가능하다.유감입니다.

    AWS Lambda@Edge무엇

  • 공식 문서
  • 간단히 말해 클라우드 프론트의 에지 서버에서 Lambda Function 기능을 수행할 수 있다.
    연결된 트리거로 다음 그림과 같은 이벤트를 준비했습니다.

    이번에 "Viewer request"를 트리거로 하면...Lambda@Edge블록에 액세스할 수 있습니다.

    Lambda 코드 쓰기


    Lambda\Edge는 Nodejs 및 Python입니다.7개만 맞춰서 좋아하는 쪽으로만 썼어요.파이썬 3으로 코드를 썼어요.
    block.py
    #!/usr/bin/env python3
    
    import urllib.request
    import json
    import re
    import os
    
    def lambda_handler(event, context):
        key          = "XXXXXXXXXXXXXXXXXXXXXXX"
        block_region = "Tokyo"
    
        request   = event['Records'][0]['cf']['request']
        client_ip = request['clientIp']
        print("request:\n%s" % request)
    
        url = "http://api.ipinfodb.com/v3/ip-city/"
        try:
          req = urllib.request.urlopen("%s/?key=%s&ip=%s&format=json" % (url, key, client_ip))
        except urllib.error.URLError as e:
          print(e.reason)
        except urllib.error.HTTPError as e:
          print(e.code)
    
        res = req.read().decode('utf-8')
        ip_info  = json.loads(res)
        print("ip_info:\n%s" % ip_info)
    
        if ip_info["regionName"] != block_region: 
            return request
    
        print("BLOCK REQUEST FROM %s" % block_region)
        response = {
          'status': '451',
          'statusDescription': 'Unavailable For Legal Reasons',
        }
    
        return response
    
    
    행위는 다음과 같다.
  • Request에서 추출clientIP(소스 IP에 액세스)
  • ipinfodb.com의 API를 사용하여 IP 주소를 체크 아웃한 경우regionName
  • block_region와 일치할 때 상태 코드 451
  • 을 반환합니다.
  • block_region 불일치 시 정상 응답
  • 반환
    몇 가지 주의점이 있다

  • API 키를 https://ipinfodb.com/에 새로 등록하고 릴리즈해야 합니다.

  • block_레지온이 "Tokyo"지만?
  • 동작을 확인하는 데 쓰인다.나중에'카가와'로 바꿔주세요.
  • lambda@Edge환경 변수는 지원되지 않습니다.API 키 같은 것도 꽉 채워야 돼.위험해.

  • 상태 코드 451은 무엇입니까?
  • 필자도 조사를 해보니 Unavailable For Legal Reasons는 바로'법률적인 이유로 취득할 수 없다'는 것을 알게 되었다.이번 요구에 완전히 부합하다.
  • 그나저나 유명한 소설'화씨 451도'로 명명되었다고 합니다.
  • IAM ROM 만들기


    lambda를 실행하는 IAM Role을 만듭니다.terraform으로 쓰면 그렇습니다.
    resource "aws_iam_role" "lambda_edge" {
      name = "lambda"
      assume_role_policy = data.aws_iam_policy_document.lambda_assume_role.json
    }
    
    data "aws_iam_policy_document" "lambda_assume_role" {
      statement {
        actions = ["sts:AssumeRole"]
    
        principals {
          type        = "Service"
          identifiers = [
            "lambda.amazonaws.com", 
            "edgelambda.amazonaws.com"
          ]
        }
      }
    }
    
    resource "aws_iam_role_policy_attachment" "lambda_basic_role" {
      role       = aws_iam_role.lambda_edge.name
      policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
    }
    
  • IAM의 신뢰 관계에 추가"edgelambda.amazonaws.com".
  • 액세스 권한으로 CloudWatch Logs에서 로그를 재생하기 위한 정책AWSLambdaBasicExecutionRole을 제공합니다.
  • 디자인 lambda


    준비되면.Lambda@Edge에 사용되는 us-east-1(버지니아 북부) 지역 설계.
    terraform으로 디자인할 수 있지만, 여기에는 lambroll이라는 lambda의 디자인 도구를 사용합니다.
  • lambroll: https://github.com/fujiwara/lambroll
  • 설계용 설정은 다음과 같다.
    $ lambroll init
    $ cat function.json
    {
      "FunctionName": "block",
      "Handler": "block.lambda_handler",
      "MemorySize": 128,
      # さきほど作成したIAM Role
      "Role": "arn:aws:iam::XXXXXXXXXXXX:role/lambda",
      "Runtime": "python3.7",
      "Timeout": 3
    }
    
    실행 프로그램
    $ lambroll deploy --region us-east-1
    2020/02/07 01:41:02 [info] lambroll v0.3.3
    2020/02/07 01:41:02 [info] starting deploy function block
    2020/02/07 01:41:03 [info] creating zip archive from .
    2020/02/07 01:41:03 [info] zip archive wrote 973 bytes
    2020/02/07 01:41:03 [info] creating function 
    2020/02/07 01:41:03 [info] deployed function version 1
    2020/02/07 01:41:03 [info] creating alias set current to version 1 
    2020/02/07 01:41:04 [info] alias created
    2020/02/07 01:41:04 [info] completed
    
    이렇게 lambda의 준비가 끝났습니다.

    S3 준비


    이 일대는 해치워라.S3을 생성합니다.이름은 적당히 붙여야 한다.
    resource "aws_s3_bucket" "web-hosting" {
      bucket = "sakutomo-webhosting-test"
      acl    = "private"
    }
    
    만들어진 빵은 적당한 그림이고 향천이니 우동을 넣자.
    $ aws s3 ./bukkake_udon.png s3://sakutomo-webhosting-test/
    
    이어 클라우드 프론트도 한다.
    resource "aws_cloudfront_origin_access_identity" "web-hosting" {
      comment = "sakutomo-webhosting-origin-access-identity"
    } 
    
    resource "aws_cloudfront_distribution" "web-hosting" {
      enabled             = true
      price_class         = "PriceClass_All"
      comment             = "Web Hosting Sakutomo Test"
    
      origin {
        domain_name = aws_s3_bucket.web-hosting.bucket_domain_name
        origin_id   = "S3-sakutomo-webhosting-test.s3.amazonaws.com"
        s3_origin_config {
          origin_access_identity = aws_cloudfront_origin_access_identity.web-hosting.cloudfront_access_identity_path
        }
      }
    
      default_cache_behavior {
        allowed_methods  = ["GET", "HEAD"]
        cached_methods   = ["GET", "HEAD"]
        target_origin_id = "S3-sakutomo-webhosting-test.s3.amazonaws.com"
    
        forwarded_values {
          query_string = false
          cookies {
            forward = "none"
          }
        }
    
        viewer_protocol_policy = "redirect-to-https"
        min_ttl                = 0
        default_ttl            = 3600
        max_ttl                = 86400
      }
    
      restrictions {
        geo_restriction {
          restriction_type = "none"
        }
      }
    
      viewer_certificate {
        cloudfront_default_certificate = true
        minimum_protocol_version       = "TLSv1.1_2016"
        ssl_support_method             = "sni-only"
      }
    
      wait_for_deployment = false
    }
    
    사냥꾼자리는 아까 S3통을 지정했습니다.
    마지막으로 CloudFront에서 S3의 이미지를 볼 수 있도록 통 정책을 만듭니다.
    data "aws_iam_policy_document" "web-hosting" {
      statement {
        actions   = ["s3:GetObject", "s3:ListBucket"]
        resources = [
          aws_s3_bucket.web-hosting.arn, 
          format("%s/*", aws_s3_bucket.web-hosting.arn), 
        ]
    
        principals {
          type        = "AWS"
          identifiers = [aws_cloudfront_origin_access_identity.web-hosting.iam_arn]
        }
      }
    }
    
    resource "aws_s3_bucket_policy" "web-hosting" {
      bucket = aws_s3_bucket.web-hosting.id
      policy = data.aws_iam_policy_document.web-hosting.json
    }
    
    이 정도면 클라우드 프론트의 주소로 아까 우동이 보였는지 확인해 보세요.

    우동이 잘 보이네.
    CLI에서도 반환 코드를 확인합니다.
    $ curl https://XXXXXXXXXXXXXx.cloudfront.net/bukkake_udon.png -I
    HTTP/2 200 
    content-type: image/png
    ....
    server: AmazonS3
    x-cache: Hit from cloudfront
    ...
    

    Lambd@Edge응용 프로그램


    향천현에서는 이 우동을 볼 수 없다.
    아까의terraform에 다음 옵션을 추가합니다
    resource "aws_cloudfront_distribution" "web-hosting" {
    ...
    
      default_cache_behavior {
    +    lambda_function_association {
    +      event_type = "viewer-request"
    +      lambda_arn = "arn:aws:lambda:us-east-1:XXXXXXXX:function:block:1"
    +    }
    
    
    이렇게 CloudFront의 Viewer Request에Lambda@Edge해봤어!!

    확인


    우동이 정말 보이지 않는지 확인해 봐.
    반영하는 데 시간이 좀 걸릴 것 같습니다.CloudFront의 Destribution의 Status에서 꼬르륵꼬르륵 회전하는 In Progress부터 Deployed까지 인내심을 가지고 기다려야 한다.
    순환이 끝난 후 아까처럼 브라우저로 방문하세요.
    $ curl https://XXXXXXXXXXX.cloudfront.net/bukkake_udon.png -I                                                                           02:06:26
    HTTP/2 451 
    content-length: 0
    server: CloudFront
    date: Thu, 06 Feb 2020 17:13:29 GMT
    
    잘, 반환 인코딩이 451로 되돌아오고 있습니다.
    우동이 안 보여.

    향천현으로부터의 방문을 막다


    block_"Tokyo"가 된 곳을 "Kagawa"로 개작하다.
    block.py
    
    def lambda_handler(event, context):
    ...
    -    block_region = "Tokyo"
    +    block_region = "Kagawa"
    
    
    나는 다시 람보를 설계할 것이다.
    $ lambroll deploy --region us-east-1
    2020/02/07 02:07:05 [info] lambroll v0.3.3
    2020/02/07 02:07:05 [info] starting deploy function block
    2020/02/07 02:07:06 [info] creating zip archive from .
    2020/02/07 02:07:06 [info] zip archive wrote 974 bytes
    2020/02/07 02:07:06 [info] updating function configuration 
    2020/02/07 02:07:06 [info] updating function code 
    2020/02/07 02:07:07 [info] deployed version 2 
    2020/02/07 02:07:07 [info] updating alias set current to version 2
    2020/02/07 02:07:07 [info] alias updated
    2020/02/07 02:07:07 [info] completed
    
    CloudFront의 설정도 변경됩니다.버젼을 1->2로 바꾸다.
    resource "aws_cloudfront_distribution" "web-hosting" {
    ...
    
      default_cache_behavior {
        lambda_function_association {
          event_type = "viewer-request"
    -      lambda_arn = "arn:aws:lambda:us-east-1:XXXXXXXX:function:block:1"
    +      lambda_arn = "arn:aws:lambda:us-east-1:XXXXXXXX:function:block:2"
    
        }
    
    축하합니다!!!이렇게 하면 Kagawa의 방문을 막을 수 있습니다!!!!(향천현 사람, 확인해 주세요.)

    다른 보충 사항.


  • Lambda@Edge의 일지는 어디에 있습니까?
  • 요청을 받은 클라우드 프론트의 에지 서버가 있는 구역입니다.
  • 일본에서 온 방문은 도쿄 지역의 클라우드워치 로그로 수출된다.

  • 정말 IP에서 확인할 수 있는 곳이 있나요?
  • IP DBINFO의 데이터가 오래되면 잘못 막힐 수도 있다.항상 옳은 것은 아니다.

  • 팟캐스트 때마다 IP DBINFO에 대한 팟캐스트 있죠?본격적으로 운용하는 건 비현실적이죠?
  • 맞아요.그러나 같은 방법으로 람바다 안에 IP 데이터베이스를 설정하면 외부로 통신하지 않아도 제어가 가능할 수 있다.

  • 조례의 대상은 인터넷이 아니라 컴퓨터 게임?
  • 이번엔 시도되지 않았지만, 클라우드 프론트의 올리브를 ALB로 만들면 게임 등api 서버도 적용할 수 있다
  • 또 다른 궁금한 게 있다면 Twitter!!

    좋은 웹페이지 즐겨찾기