aws apigateway update-method \
--rest-api-id dmtqi47mvg \
--resource-id vlifnq \
--http-method OPTIONS \
--patch-operations op="replace",path="/authorizationType",value="AWS_IAM"
配置之后显示如下信息,其中第二行显示认证已经是 IAM 类型。如下截图。
重复以上过程,对所有资源和方法都执行一遍配置。
6、将开启 IAM 认证的配资部署为新的 Stages 阶段
经过确认,所有接口部署完成。接下来要将 Resource
部署到 Stages
推向生产。在上一步的 Resource
界面,点击 Actions
按钮,从下拉框中选择 Deploy API
按钮,进行部署。如下截图。
在弹出的对话框中,输入要部署的 Stages
的名称,这里需要与前文配置 IAM Policy 的授权的 Stages
名称一致,这里使用 prod-iamauth
。如下截图。
部署完成。回到 API Gateway 界面,点击左侧的 Stages
按钮,可看到页面中间显示的新部署的 prod-iamauth
,点击资源和方案,可看到右侧显示的 Authentication
为 Amazon IAM
,表示配置成功。如下截图。
至此配置部分完成。接下来进行测试。
7、使用 AWS Signature V4 对打开 IAM 身份验证的接口发起访问
1)SigV4 简介
在 API Gateway 打开了 IAM 认证后,要求客户端发送给服务器端的请求必须使用 SigV4 签名进行身份认证。也就是说,客户端不能直接通过 CURL 发送明文的 AKSK 到 API Gateway,而是要经过 HMAC-SHA256 算法生成符合 API Gateway 要求的 SigV4 签名,然后带着签名才能发送到 AWS 云端。签名请求格式类似如下:
Authorization: AWS4-HMAC-SHA256
Credential=AKIAIOSFODNN7EXAMPLE/20220830/cn-northwest-1/execute-api/aws4_request,
SignedHeaders=host;x-amz-date,
Signature=calculated-signature
SigV4 签名才用的 HMAC-SHA256 算法具有足够的强度,因此经过加密的信息不会被破解。关于 SigV4 的介绍,可参考本文末尾的文档。为了满足 SigV4 签名的前提条件,本文后续将使用内置 SigV4 的客户端进行测试,并使用 Python 构建代码进行测试。
2)使用 REST 客户端软件请求
对 API Gateway 的请求可以通过 REST 客户端,例如 Postman 等软件。本文使用 VSCode 并通过 THUNDER CLIENT
插件实现。
在 THUNDER CLIENT 插件的界面上,选择方法是 GET
,输入 API Gateway 调用的资源地址,选择认证标签页 Auth
,然后在下方输入 Access Key 和 Secret Key。然后点击 Send
按钮提交认证。此时右侧可以收到正确的请求返回信息。如下截图。
如果 IAM 和 AKSK 的认证信息不正确,则会收到错误信息。如下截图。
由此确认了带有 IAM 身份验证的 API Gateway 请求正常。
3)使用 Python 代码请求
Python 程序调用 AWS 服务,包括但不限于 API Gateway,需要额外安装 requests
库和 aws_requests_auth
。命令如下:
pip install requests aws_requests_auth -y
构建如下 Python 程序:
import requests
from aws_requests_auth.aws_auth import AWSRequestsAuth
# let's talk to our AWS Elasticsearch cluster
auth = AWSRequestsAuth(aws_access_key='access_key',
aws_secret_access_key='secret_key',
aws_host='dmtqi47mvg.execute-api.cn-northwest-1.amazonaws.com.cn',
aws_region='cn-northwest-1',
aws_service='execute-api')
response = requests.get('https://dmtqi47mvg.execute-api.cn-northwest-1.amazonaws.com.cn/prod-iamauth/pets',
auth=auth)
print(response.text)
以上代码不依赖于 AWS Boto3 SDK,只需要构建正确的 HTTP 请求即可满足 IAM 身份认证要求的 SigV4 签名。将配置信息带入后,运行程序,可看到正常返回结果。
如果 IAM 和 AKSK 的认证信息不正确,则会收到错误信息。如下截图。
由此确认了带有 IAM 身份验证的 API Gateway 请求正常。
至此所有配置过程完成。
四、使用 Mutual TLS(mTLS)双向证书认证
1、背景
通常使用的 HTTPS 接口采用的是服务器端 Transport Layer Security(以下简称TLS)认证。TLS 认证默认是由服务器提供证书,服务器端证书经过全球数家证书颁发机构和顶级 CA 签署认证的证书,来标识服务器自己的真实、正确的身份。在亚马逊云科技的云平台上,这一服务器端证书通常由 Amazon Certificate Manager(ACM)生成并管理,配置到 Amazon CloudFront、Amazon ELB、Amazon API Gateway(以下简称 API Gateway)等服务上。
为了进一步增强安全性,TLS 协议建立连接时候,服务器可以要求客户端也提供自己 X.509 格式的证书用于验明身份。由于服务器和客户端双方都需要提供证书,这种认证方式被称为 mutual Transport Layer Security(以下简称 mTLS),也称为双向 TLS。
双向 TLS 证书验证在金融、物联网、IoT 等公开网络环境中发生在线交易、在线交互等场景下获得普遍应用。在企业内部应用系统中也经常作为强制验证客户端身份真实性的一种手段。使用 mTLS 后,即便拥有正确的用户名和密码,也会因为没有通过证书验证而不能建立网络连接,此时用户名和密码将不会在网络层被发送。应用 mTLS 额外提升了接口安全能力。
在 API Gateway 上开启 mTLS 认证没有额外费用。双向 TLS 证书验证与使用用户名、密码和 token 的认证手段并不矛盾和冲突,mTLS 工作于更基础的网络层。通过证书校验,网络通道建立后,就会进行应用层的身份认证。对于 API Gateway,建议在 API Gateway 使用包括但是不限于 IAM 身份验证、Lambda 认证转发器和 JSON Web Tokens(JWTs)等认证手段进行身份认证。
在 API Gateway 上开启 mTLS 认证主要步骤是:
准备:发布一个正常接受访问的 API Gateway Stages(阶段),并准备好 ACM 证书,自定义域名
生成证书:生成 mTLS 使用的证书
创建 Endpoint:使用自定义域名功能,通过绑定别名和来自 Amazon Certificate Manager(ACM)的证书,生成一个仅支持的 mTLS 的新的 Endpoint
绑定:然后通过 API Mapping 方式将原 API Gateway 与新的 mTLS 认证的 Endpoint 进行关联
加固:关闭原有 API Gateway 的 Endpoint 的公开访问
本文介绍如何为 API Gateway 开启 mTLS 认证。开始配置前,请确认 API Gateway 本身工作正常。然后开始配置。
2、生成并上传证书
双向证书认证要求证书为 X.509 证书。证书可以自行签署,或者通过 Amazon Private Certificate Authority(以下简称 Private CA)生成。
截止本文编写时(2023 年 1 月),亚马逊云科技中国区尚未发布 Private CA 功能,因此本文使用 Openssl 自行签署。
(1)生成 CA
生成证书需要 Linux 环境和 openssl
组件,先生成自有 CA,然后自己为自己颁发证书。全过程需要生成以下文件:
RootCA.key (root CA private key)
RootCA.pem (root CA public key)
my_client.csr (client certificate signing request)
my_client.key (client certificate private key)
my_client.pem (client certificate public key)
首先生成 CA 私有证书文件。
openssl genrsa -out RootCA.key 4096
由此将在当前目录下获得 RootCA.key
文件。接下来生成 CA Public Key。执行如下命令:
openssl req -new -x509 -days 3650 -key RootCA.key -out RootCA.pem
执行以上命令后,提示输入证书所需要的 Country Name
、State
、city
、Company
、section
、name
和 Email
等信息。如下返回信息。
[root@ip-172-31-32-113 ~]# openssl req -new -x509 -days 3650 -key RootCA.key -out RootCA.pem
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:BEIJING
Locality Name (eg, city) [Default City]:BEIJING
Organization Name (eg, company) [Default Company Ltd]:BITIPCMAN
Organizational Unit Name (eg, section) []:DEV
Common Name (eg, your name or your server's hostname) []:lxy
Email Address []:[email protected]
[root@ip-172-31-32-113 ~]#
完毕后,在当前目录下生成 RootCA.pem
文件。
(2)生成客户端证书
首先生成客户端私钥。执行如下命令:
openssl genrsa -out my_client.key 2048
由此获得 my_client.key 文件。为此证书生成 Certificate Signing Request (CSR)。执行如下命令:
openssl req -new -key my_client.key -out my_client.csr
在发出证书签署申请时候,也要逐步输入 Country Name
、State
、city
、Company
、section
、name
和 Email
等信息。当提示 A challenge password
时候,请注意这是可选步骤,直接按回车保持为空不需要输入。如下返回信息。
[root@ip-172-31-32-113 ~]# openssl req -new -key my_client.key -out my_client.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:BEIJING
Locality Name (eg, city) [Default City]:BEIJING
Organization Name (eg, company) [Default Company Ltd]:BITIPCMAN
Organizational Unit Name (eg, section) []:DEV
Common Name (eg, your name or your server's hostname) []:dev01
Email Address []:[email protected]
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
[root@ip-172-31-32-113 ~]#
至此在当前目录下获得 my_client.csr
文件。
(3)签署证书
现在针对上一步的客户端证书签名请求,签署客户端证书,生成客户端公钥。执行如下命令:
openssl x509 -req -in my_client.csr -CA RootCA.pem -CAkey RootCA.key -set_serial 01 -out my_client.pem -days 3650 -sha256
这样即可针对刚才的客户端证书生成签名。返回结果如下:
root@ip-172-31-32-113 ~]# openssl x509 -req -in my_client.csr -CA RootCA.pem -CAkey RootCA.key -set_serial 01 -out my_client.pem -days 3650 -sha256
Signature ok
subject=/C=CN/ST=BEIJING/L=BEIJING/O=BITIPCMAN/OU=DEV/CN=dev01/[email protected]
Getting CA Private Key
[root@ip-172-31-32-113 ~]#
由此在当前目录下获得 my_client.pem
文件。后续在调用 API Gateway 的客户端程序中,就需要使用 client.pem 这个文件。
(4)上传 CA 到 S3 存储桶用于 API Gateway 加载
API Gateway 在开启 mTLS 时候需要检查证书的 CA 的公有证书,因此这里需要将上一个步骤生成的 RootCA.pem
上传到某个 S3 存储桶中,然后在 API Gateway 上加载这个证书文件。
为此,创建一个 S3 存储桶,选择与 API Gateway 相同的 region,存储桶取名为 apigateway-mtls-mycert-zhy
。将上一步生成的 RootCA.pem
复制为一个新文件,名叫 truststore.pem
,然后将其上传到 S3 存储桶内。
以下过程也可以通过 AWSCLI 执行,命令如下。
cp RootCA.pem truststore.pem
aws s3 cp truststore.pem s3://apigateway-mtls-mycert-zhy/
3、为 API Gateway 设置设置自定义域名和 mTLS
为配置 mTLS,需要开启 custom domain name
也就是自定义域名功能。
进入 API Gateway 的界面,点击左侧菜单 custom domain name
,点击右侧的创建按钮。
在创建自定义域名的界面上,输入自定义域名,TLS 协议选择为 1.2 版本(推荐),在 Mutual TLS authentication
开关设置为启用,在 Truststore URI
位置输入上一步的 S3 存储桶名称和文件路径 s3://apigateway-mtls-mycert-zhy/truststore.pem
。然后向下卷动页面。如下截图。
在 Certificate type
位置上,选择 Public ACM certificate
,然后从下拉框中选择事先在本 Region 配置好的 ACM 证书。最后点击右角的 Create domain name
创建自定义域名。
现在可以看到新生成了一个新的 API Gateway Endpoint,和原来没有使用 mTLS 的 Endpoint 是独立的两个端点。由此即可配置自己的域名,设置一个新的 CNAME,将别名指向过来。
如果您的域名是在 Amazon Route53 解析的,请在 Route53 上设置 Alias 记录。如果您的域名是在其他域名服务商解析的,那么请在域名服务商提供的域名解析控制面板上设置 CNAME 记录。设置 Alias 和 CNAME 过程本文不再描述,请参考相关文档完成。
4、设置自定义域名的 Mapping
在创建自定义域名完成后,还要设置 API Mapping。进入自定义域名界面,点击右下的第二个标签页 API mappings
标签页,点击右边的按钮 Configure API mappings
按钮。如下截图。
在创建新的 Endpoint 的位置上,从下拉框选择中选择要绑定的 API Gateway 的名字,从 Stages
下拉框中选择之前配置好的 prod
,在映射为新的 API 地方输入相同的名字 prod
,然后点击右下角的创建按钮。这里可以看到界面上提示原有的 API Gateway 的公开对外访问还没有关闭,这个信息可以暂时忽略,本文后续步骤会关闭不带 mTLS 的访问。如下截图。
创建 Mapping 完成。提示原有的 API Gateway 的公开对外访问还没有关闭,这个信息可以暂时忽略,本文后续步骤会关闭不带 mTLS 的访问。如下截图。
接下来发起访问测试。
5、测试访问
使用 CURL 的参数--key
和--cert
带着证书访问新创建的自定义域名。命令如下:
curl --key my_client.key --cert my_client.pem
https://mtlsdemo.bitipcman.com/prod/pets/
测试可看到访问正常。当去掉证书之后,再次使用 CURL 访问,可以看到提示 SSL 错误无法建立连接。如下截图。
由此表示配置正常。
6、关闭原有的不带 CNAME 不带 mTLS 的 Endpoint
前文已经讲解,新创建的带有 mTLS 的自定义域名是一个全新的 Endpoint,还需要把原先的 Endpoint 设置为禁止公开访问。
方法是进入原先的 API Gateway 的界面,进入左侧的 Settings
设置界面,再右侧找到 Default endpoint
的选项,将默认为 Enable
的开关修改为 Disable
。如下截图。
注意此选项变更后,必须要重新部署新 Stages
才可以生效。界面上也有如下提示:
This is an API level setting and affects all stages of your API. After you enable or disable the default endpoint, deploy your API to any one stage for the update to take effect.
如果不发起新的部署,原有的 API Gateway Endpoint 依然是无需 mTLS 即可访问的。发起新的部署可以在 API Gateway 图形界面控制台完成,也可以通过 AWSCLI 发起,命令如下:
aws apigateway update-rest-api \
--rest-api-id abcdef123 \
--patch-operations op=replace,path=/disableExecuteApiEndpoint,value='True'
aws apigateway create-deployment \
--rest-api-id abcdef123 \
--stage-name prod
在发布新的 Stage
后,再次通过 curl 访问原有 API Gateway,则提示没有权限。
{"message":"Forbidden"}
由此表明,旧的 API Gateway 已经被关闭。目前所有访问只能通过开启了 mTLS 的自定义域名来访问。至此所有配置完成。
五、参考文档
选择网关类型 Restful 类型和 HTTP 类型:https://docs.aws.amazon.com/zh_cn/apigateway/latest/developerguide/http-api-vs-rest.html
为 API Gateway 使用 IAM 身份验证:https://aws.amazon.com/cn/premiumsupport/knowledge-center/iam-authentication-api-gateway/?nc1=h_ls
访问 API Gateway 的 IAM 策略最佳实践和配置样例:https://docs.aws.amazon.com/zhcn/apigateway/latest/developerguide/securityiamid-based-policy-examples.html#securityiam_service-with-iam-policy-best-practices
通过 AWSCLI 为 API Gateway 资源和方法配置 IAM 认证:https://docs.aws.amazon.com/cli/latest/reference/apigateway/update-method.html
AWS SigV4 简介,以及常见编程语言生成 SigV4 签名的 Sample Code:https://docs.aws.amazon.com/zh_cn/general/latest/gr/create-signed-request.html
Python SigV4 签名代码样例(From Github):https://github.com/DavidMuller/aws-requests-auth
Introducing mutual TLS authentication for Amazon API Gateway:https://aws.amazon.com/cn/blogs/compute/introducing-mutual-tls-authentication-for-amazon-api-gateway/
为 REST API 配置双向 TLS 身份验证:https://docs.amazonaws.cn/apigateway/latest/developerguide/rest-api-mutual-tls.html
禁用 REST API 的默认终端节点:https://docs.amazonaws.cn/apigateway/latest/developerguide/rest-api-disable-default-endpoint.html