API网关平坑记--域名定制与流量收敛不可兼得?

2021年04月05日

-
这几天在优化网站架构的时候,笔者发现,自己之前所用的API网关虽然使用了private API类型的API网关保证流量收敛在VPC的内部,不会流出VPC,但是所使用的域名却是云厂商提供的域名(形如https://<API-ID>.execute-api.<region-code>.amazonaws.com)。然而,我们通常需要暴露给最终用户的API的域名应是比较友好的名称,一来需要短小精干、脍炙人口以方便用户记忆,而来表明域名的所有者是域名所指企业或者组织。

 

鉴于此,我计划开始对API网关的架构进行外科手术,既不能伤害现有架构和服务,也要达到替换API域名的目的。

 

本以为一顿简单的操作后就能如愿达成目标,但是在实际方案准备的时候却注意到AWS的private API网关不能直接支持custom domain name。一盆冷水浇下,敢问路在何方?经过一番仔细思量,我备出了两个solution。

 

方案一是将private API切换成regional API,从而利用API网关的custom domain name功能给API启用自定义的域名。但是这样做会将本来在vpc内部流动的流量散发到vpc的外部,会额外的增加安全的风险,也可能会在nat的环节给整体性能造成瓶颈。此外,之前通过resource policy对api网络访问进行的限制也会失效,需要通过引入waf才能实现同样的目的。

 

方案二是增加一个私网nlb,通过dns解析将自定义的域名解析到这个nlb上。这种方式几乎不用对现有api进行改造,对现有环境的改动最小。

 

对比两种方案,最终选择使用后一种方案对API进行改造。

 

改造后的局部架构图如下:

为custom domain name创建证书,此处证书在aws acm​中创建。(截图或cli略。)

 

为api网关创建vpc endpoint:

aws ec2 create-vpc-endpoint --vpc-endpoint-type Interface --vpc-id vpc-****** --service-name com.amazonaws.us-west-2.execute-api --subnet-ids subnet-*******1 subnet-*******2 --security-group-ids sg-**** --private-dns-enabled

 

在上面的安全组里,对tcp 443端口的流量全部或者酌情放行。(截图或cli略。)

 

在api里指定指定vpc endpoint id。(截图或cli略。)

 

创建custom domain name,关联acm证书。

aws apigatewayv2 create-domain-name --domain-name 'api-internal.example.com' --domain-name-configurations CertificateArn=arn:aws:acm:<region-code>:123456789012:certificate/123456789012-1234-1234-1234-12345678

 

aws apigatewayv2 create-api-mapping --domain-name 'api-internal.example.com' --api-id <API-ID> --stage 'prod'

如果不希望额外在domain name的后面额外append一个path,那么请不要指定参数“api-mapping-key”。换句话说,形如:

aws apigatewayv2 create-api-mapping --domain-name 'api-internal.example.com' --api-mapping-key 'myPath' --api-id <API-ID> --stage 'prod'

的命令,意味着你希望通过url: api-internal.example.com/myPath来访问API。

 

创建internal nlb

nlb的target即为在上面步骤中创建的vpc endpoint的eni的ip地址。健康检查使用443端口。

 

创建route 53 dns解析

vim setup-dns-record.json 

{  "Changes": [    {      "Action": "CREATE",      "ResourceRecordSet": {        "Name""api-internal.example.com",        "Type": "A",        "AliasTarget": {          "DNSName": "******.elb.us-west-2.amazonaws.com",          "HostedZoneId""NLB's hosted zone",          "EvaluateTargetHealth": false        }      }    }  ]}

aws route53 change-resource-record-sets --hosted-zone-id ****** --change-batch file://setup-dns-record.json

 

测试验证

将下面Python代码部署到Lambda到VPC环境进行测试​。

# coding=utf-8

import json
import ssl

import urllib.request


ssl._create_default_https_context = ssl._create_unverified_context


def lambda_handler(event, context):
    
    _d = json.loads(urllib.request.urlopen(
        "https://api-internal.example.com/post",
        data=bytes(json.dumps({
            ...
        }), encoding="utf-8")
    ).read().decode('utf-8'))
    
    return {
        'statusCode': 200,
        'body': json.dumps(_d)
    }


if you encounter below error:

urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'hao123.com'. (_ssl.c:1051)>

then add this Python code.

执行test指令后,​测试顺利通过。

 

至此,我到api网关完成了custom domain name到改造,并且顺利到保留了之前到安全和策略特性,例如网络流量到private属性(网络流量不会流出vpc)​。

网站架构全局图:

References

SSL Cert Verification

How do I disable the security certificate check in Python requests

AWS Api Gateway + Lambda + custom domain (Route53) Missing Authentication Token issue

Enabling Private APIs with Custom Domain Names (AWS API Gateway)
-

Category: AWS Tags: public

Upvote


Downvote