亚马逊AWS官方博客

利用Amazon API Gateway和AWS Lambda处理Cloudfront的内容请求

概述

国内Amazon Cloudfront目前不支持Lambda@edge功能,不能实现基于CDN的A/B测试、rewrite、redirect、token认证和产生response等功能,本文介绍如何利用API Gateway和Lambda实现Lambda@edge的功能。下面实验介绍通过request header参数值,实现redirect和rewrite的测试场景,根据header(test_version)参数值,回源到指定目录的文件,根据header(redirect)参数值,返回302重定向地址。

整体实验的架构图如下:

 

架构图说明:

  1. Cloudfront是Amazon的CDN服务,可以设置源站域名,回源header,缓存策略等。
  2. API Gateway有两种类型可以支持rewrite和redirect测试场景,实验中采用HTTP API,考虑到成本更低,同时不需要Rest API的高级功能。
  3. Lambda实现了rewrite和redirect的测试代码,支持验证security header。支持多种主流语言,实验中采用Python3.9语言实现。
  4. S3保存测试的html和png文件。

详细步骤说明

1.新建S3 Bucket

比如:bucket name:lambda-api-2022

上传文件列表:

index.html – 欢迎页

v1/test.html – A测试页

v1/test.png – A测试图片

v2/test.html – B测试页

v2/test.png – B测试图片

2.新建Lambda程序

1)新建IAM Role,执行Lambda程序,比如Role name:RoleForLambda

需要的权限如下:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": " s3get",
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::lambda-api-2022/*"
        },
        {
            "Sid": " putlogs",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}

2)创建Lambda程序

采用下列的参数和配置:

Function name:lambdaapi

Runtime:Python 3.9

Execution role:RoleForLambda(上一步创建的)

修改Configuration的配置:

添加Environment variables

添加Key=bucket,Value=lambda-api-2022

添加Key=lambda_auth,Value=lambdaapi_test

添加Key=redirect_path,Value=https://xxx.cloudfront.net,value来自下面创建的Cloudfront distribution

General configuration

修改Timeout为20秒

Lambda Source Code:

import boto3
import base64
import os

s3_client = boto3.client('s3')

def lambda_handler(event, context):

    print(event)

    # check security header
    if 'lambda_auth' in event['headers'] and event['headers']['lambda_auth'] == os.environ['lambda_auth']:

        bucket = os.environ['bucket']
        
        key = event['rawPath'][1:]  # request URI
    
        print(key)
        
        if len(key) == 0:
            key = 'index.html'
        
        # rewrite url
        if 'test_version' in event['headers']:
            key = event['headers']['test_version']+'/'+key
        
        # redirect    
        if 'redirect' in event['headers'] and event['headers']['redirect'] == 'true':
            return {
                'statusCode': 302,
                'statusDescription': 'Found',
                'headers': { 'Location': os.environ['redirect_path'] + '/' + key }
            }
        
        # return content body - rewrite
        try:
            response = s3_client.get_object(Bucket = bucket, Key = key)
            responseBody = response['Body'].read() # return bytes from S3
            responseContentType = response['ResponseMetadata']['HTTPHeaders']['content-type']
            return {
                    'headers': { "Content-Type": responseContentType },
                    'statusCode': 200,
                    'body': base64.b64encode(responseBody).decode('utf-8'),
                    'isBase64Encoded': True
                }
        except Exception as e:
            print('error message - ', e.__class__.__name__, ' : ', e)
            
            return {
                    'statusCode': 200,
                    'body': 'no file: '+key
                }
    
    else:
        # auth failed
        return {
                'statusCode': 403,
                'body': 'request is forbidden'
            }

Lambda Source Code说明:

在Cloudfront回源时,添加lambda_auth header,用于Lambda认证请求,当认证失败时,返回403错误。

当请求根目录时,返回index.html

当request header包含test_version时,转向到指定目录下的文件。

将返回的body通过base64编码,以支持binary对象。

当request header包含redirect=true时,返回302重定向信息。

3.新建API Gateway

在API Gateway中,新建HTTP API,比如API Name:lambdaapi

新建Lambda integration,选择上一步创建的Lambda(lambdaapi)

在Configure routes时,Resource path设置为“/{proxy+}”

在Deploy Stages中,找到$default stage的Invoke URL,如https://xxx.execute-api.xxx.amazonaws.com,此为API Gateway的请求地址。

测试API gateway:

  • 测试security header

测试命令:curl https://xxx.execute-api.xxx.amazonaws.com

返回结果:request is forbidden

  • 测试访问根路径,传入lambda_auth header

测试命令:curl -v -H “lambda_auth:lambdaapi_test” https://xxx.execute-api.xxx.amazonaws.com

返回结果:statusCode=200

  • 访问B测试页,传入lambda_auth header和test_version header

测试命令:curl -H “lambda_auth:lambdaapi_test” -H “test_version:v2” https://xxx.execute-api.xxx.amazonaws.com/test.html

返回v2/test.html的内容

  • 访问B测试图片,传入lambda_auth header和test_version header

测试命令:curl -H “lambda_auth:lambdaapi_test” -H “test_version:v2” https://xxx.execute-api.xxx.amazonaws.com/test.png > test.png

将response保存为test.png图片。

  • 测试redirect,传入redirect header

测试命令:curl -v -H “lambda_auth:lambdaapi_test” -H “test_version:v1” -H “redirect:true” https://xxx.execute-api.xxx.amazonaws.com/test.html

返回302重定向信息

4.配置Cloudfront

1)创建Origin request policy,Amazon Cloud Global支持该功能

在Cloudfront Policies中,新建origin request policy,例如Name:lambdaapi

配置如下:

Headers:选择 Include the following headers,并手工添加header:test_version和redirect

Query strings: All

Cookies:All

2)创建Cloudfront Distribution

在Cloudfront中,新建Distribution,例如Description:lambdaapi

配置如下:

Origin domain:xxx.execute-api.xxx.amazonaws.com,上面创建的HTTP API域名

Protocol:HTTPS only

Add custom header:

Header name = lambda_auth,Value = lambdaapi_test

在Amazon Cloud Global,支持Cache policy and origin request policy (recommended),配置下面两个参数:

Cache policy:CachingDisabled

Origin request policy:Custom lambdaapi

或者在Amazon Cloud China,支持Legacy cache settings,配置下面3个参数:

Headers:选择 Include the following headers,并手工添加header:test_version和redirect。

Query strings: All

Cookies:All

如果不需要Cloudfront缓存内容时,需要设置Object caching为Customize,同时将Minimum TTL、Maximum TTL和Default TTL都设为0.

注:需新建origin和behavior,配合redirect后Cloudfront地址。

5.测试Cloudfront

1.在Cloudfront Distributions (Lambdaapi)的General Details中,找到Distribution domain name,例如cloudfront.net

2.访问A测试页,传入test_version header

测试命令:curl -H “test_version:v1” https://xxx.cloudfront.net/test.html

返回v1/test.html的内容

3.测试redirect,传入test_version和redirect header

测试命令:curl -I -H “test_version:v1” -H “redirect:true” https://xxx.cloudfront.net/test.html

返回302重定向的内容

结论

在这篇文章中,介绍了如何利用API Gateway和Lambda处理Cloudfront的内容请求,实现Lambda@edge的功能,在实验中,介绍了Amazon S3、Lambda、API Gateway和Cloudfront的配置方法,实现了rewrite和redirect的测试场景。

参考资料

本篇作者

薛召兵

AWS解决方案架构师,负责帮助客户进行上云架构的设计和咨询。同时致力于AWS容器服务、媒体服务和机器学习服务在国内和全球商业客户的应用和推广,推进企业服务迁移上云进程。有10年以上的软件开发、售前技术支持、系统架构设计等经验。