API网关平坑记--域名定制与流量收敛不可兼得?
-
这几天在优化网站架构的时候,笔者发现,自己之前所用的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
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)
-