>作者容鑫,API7.ai 云原生技术工程师,Apache APISIX Committer。
## **为什么需要 AWS ACM ?**
### **手动管理证书带来的问题**
证书包含持有者、颁发者、失效日期等信息。为了确保证书能够被安全地使用,同时也确保证书能够被及时吊销和续订,证书的相关信息需要被记录下来,例如可以手动记录到电子表格中。
但随着管理的证书越来越多,手动管理的方式容易因遗漏而导致证书中断,它无法根据新信息和法规自动更新。因为证书到期之前电子表格不会通知你,也不会自动为即将到期的证书续订。手动管理的方式不可避免会操作失误,这将为你带来不必要的风险。这时,自动管理证书的方式应运而生,它将减少人为操作的工作负担和相关风险。
### **ACM 自动管理证书**
[AWS Certificate Manager](https://aws.amazon.com/cn/certificate-manager/?trk=cndc-detail)(ACM) 可以轻松预置、管理、部署和续订 SSL/TLS 证书。你可以直接通过 ACM 签发证书保护你的 AWS 网站和应用程序,或者将第三方证书导入 ACM 管理系统中。使用 ACM,你无需经历过去与使用和管理 SSL/TLS 证书相关的大量手动流程,还可以导出由 AWS Private CA 签名的 ACM 证书,以便在内部 PKI 中的任何位置使用。同时它还集成了 AWS [Elastic Load Balancing](https://aws.amazon.com/cn/elasticloadbalancing/?trk=cndc-detail)(ELB)。
![download_image.jpeg](https://dev-media.amazoncloud.cn/8013210677de49a18162ffa77f0b84e3_download_image.jpeg "download_image.jpeg")
## **ACM Private CA 解决了哪些问题?**
当组织中没有任何 Private CA,在部署内部应用程序时,他们都使用了自签名证书。这些应用程序相互访问时,由于对方的不受信任而拒绝访问,如果盲目信任未知来源的应用程序会带来安全风险。这就需要有一个 Private CA 的托管服务,负责创建和管理 CA 层次结构,确保组织内的应用程序之间是受信任的。
ACM Private CA 就是一种高可用的托管服务,用于为你的组织创建和维护内部公钥基础设施,消除了持续维护的成本。私钥存储在经过 FIPS 140-2 认证的 AWS 托管硬件安全模块 (HSM) 中。与 Kubernetes 中的默认 CA 相比,提供更安全的证书颁发机构解决方案。
![download_image (9).png](https://dev-media.amazoncloud.cn/87cced5645cd45febf42b03656ed3384_download_image%20%289%29.png "download_image (9).png")
## **如何与 Kubernetes 配合使用?**
![image (29).png](https://dev-media.amazoncloud.cn/6db5e2bdcd094418bafab5303093b40d_image%20%2829%29.png "image (29).png")
在 Kubernetes 中终止 TLS 证书有两种配置方式:
* **在 NLB 终止**:对于一些使用公开信任的证书,在 NLB 级别终止 TLS 证书是最常见的用例。此方式易于配置,将 ACM 公开信任证书绑定到 NLB。集群内部进行应用的访问依然用的是 HTTP 访问方式,无需额外的加解密运算。
* **在 Ingress 终止**: 当应用之间有加密需求,需要在 Ingress controller 中终止 TLS 。每个应用都可以通过 Ingress 管理自己的证书,互不干扰,能够保证应用之间的通信是受信任的,此方式更易于配置和管理。
在接下来的演示示例中,将会在 [Amazon EKS](https://aws.amazon.com/cn/eks/?trk=cndc-detail) 上设置 APISIX Ingress,我们会根据这两种方式演示 APISIX Ingress 如何与 ACM 配合使用(以及 ACM Private CA)。
## **准备:预置环境**
开始之前,你需要具备以下条件:
* [AWS](https://signin.aws.amazon.com/signin?redirect_uri=https://portal.aws.amazon.com/billing/signup/resume&client_id=signup) 账号与 [AWS 命令行界面](https://aws.amazon.com/cn/cli/?trk=cndc-detail)[(AWS CLI)](http://aws.amazon.com/cli)。
* 你必须有权使用 [Amazon EKS](https://aws.amazon.com/cn/eks/?trk=cndc-detail) IAM 角色和服务相关角色、[AWS CloudFormation](http://aws.amazon.com/cloudformation)、[Amazon Virtual Private Cloud (Amazon VPC)](http://aws.amazon.com/vpc)和相关资源。请参阅 IAM 用户指南中的[适用于 Kubernetes 的 Amazon Elastic Container Service 的操作、资源和条件键](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonelastickubernetesservice.html)以及使用[服务相关角色](https://docs.aws.amazon.com/IAM/latest/UserGuide/using-service-linked-roles.html)。此外,此 IAM 安全主体需要附加一个 AWSCertificateManagerPrivateCAFullAccess IAM 托管策略。
* 安装并配置了 **kubectl** 和 [eksctl](https://docs.aws.amazon.com/zh_en/eks/latest/userguide/getting-started-eksctl.html) 工具。
### **Amazon EKS 集群**
[Amazon EKS](https://aws.amazon.com/cn/eks/?trk=cndc-detail) 是一种托管服务,你可以使用它在 AWS 上运行 Kubernetes,轻松地部署和管理你的 Kubernetes 集群。本文将使用 eksctl 命令工具来管理集群,这里使用 eksctl 默认配置创建集群(如果你已有 EKS 集群请忽略):
```bash
eksctl create cluster
```
### **安装 APISIX Ingress**
第一步,在 EKS 集群中安装 APISIX Ingress,并设置为 LoadBalancer 类型。
```plain
helm repo add apisix https://charts.apiseven.com
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install apisix apisix/apisix \\
--set gateway.type=LoadBalancer \\
--set gateway.tls.enabled=true \\
--set ingress-controller.enabled=true \\
--namespace ingress-apisix \\
--create-namespace
```
需注意:请确保你的集群能够添加持久性卷,详情请参阅 [Amazon EKS Storage](https://docs.aws.amazon.com/zh_en/eks/latest/userguide/storage.html)。 如果只是为了体验本教程,需要在安装时配置 `--set etcd.persistence.enabled=false` 声明不使用持久性卷。
第二步,运行以下命令检查状态,确保全部 Pod 处于 Running。
```plain
\$ kubectl get pods -n ingress-apisix
NAME READY STATUS RESTARTS AGE
apisix-78bfc58588-qspmm 1/1 Running 0 103s
apisix-etcd-0 1/1 Running 0 103s
apisix-etcd-1 1/1 Running 0 103s
apisix-etcd-2 1/1 Running 0 103s
apisix-ingress-controller-6ff56cd4b4-rktr9 1/1 Running 0 103s
```
第三步,检查 NLB 状态,这里重点关注 PORT(S) 和 EXTERNAL-IP。
```plain
\$ kubectl get svc apisix-gateway -n ingress-apisix
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
apisix-gateway LoadBalancer 10.100.178.65 a6cffe9f6fc5c47b9929cb758610fc5a-2074689558.ap-northeast-1.elb.amazonaws.com 80:30851/TCP,443:32735/TCP 103s
```
## **方式一:在 NLB 终止 TLS 证书**
### **准备 ACM 证书**
打开 [ACM 控制台](https://console.aws.amazon.com/acm/home),为你的自定义域申请公共 ACM 证书或导入自定义证书。
![download_image (1).jpeg](https://dev-media.amazoncloud.cn/e1863c3620544a2dadeff225c0aad82e_download_image%20%281%29.jpeg "download_image (1).jpeg")
### **LoadBalancer 配置证书**
1. 打开 [EC2 控制台](https://console.aws.amazon.com/ec2/),选择你的 **Load Balancers(负载均衡器)** -> **侦听器** -> **edit**。
![image (32).png](https://dev-media.amazoncloud.cn/cf7adc17664b4f728295442d7f26f5a9_image%20%2832%29.png "image (32).png")
2. NLB 协议设置为 HTTPS,端口 443,实例协议设置为 HTTP,端口 30851。
![image (33).png](https://dev-media.amazoncloud.cn/ad2537b6a3e140be80dca5caee047323_image%20%2833%29.png "image (33).png")
3. 将 ACM 中的 TLS 证书附加到 NLB。
![image (34).png](https://dev-media.amazoncloud.cn/fd24a7906f9341978bc1159008b61129_image%20%2834%29.png "image (34).png")
### **将自定义域与负载均衡器名称关联**
你可以使用 DNS 提供商的控制台,通过 CNAME 将应用程序的 DNS 记录指向 NLB 的 URL。例如 [Route53](https://console.aws.amazon.com/route53),[并设置指向你的 NLB 的 CNAME 记录](https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/using-domain-names-with-elb.html#dns-associate-custom-elb)。
```plain
httpbin.example-test.org CNAME a6cffe9f6fc5c47b9929cb758610fc5a-2074689558.ap-northeast-1.elb.amazonaws.com
```
### **访问应用域名**
使用以下命令进行访问:
```shell
curl https://httpbin.example-test.org
```
## **方式二:在 Ingress 终止 TLS 证书**
***在开始本示例前,请确保 AWS NLB 的配置已恢复如下图片所示:***
![image (35).png](https://dev-media.amazoncloud.cn/9fa51ff7d0d2471cb68eedb4aa3a02f7_image%20%2835%29.png "image (35).png")
### **安装 cert-manager**
cert-manager 是一个 Kubernetes 附加组件,可用于自动管理和颁发来自各种颁发来源的 TLS 证书,你可以在常规方式[安装 cert-manager](https://cert-manager.io/docs/installation/helm/)。
### **创建 ACM Private CA**
1. 打开 ACM PCA 控制台,选择创建证书,并安装。
![image (36).png](https://dev-media.amazoncloud.cn/979b90845c0e42f19cfe64fe278a2244_image%20%2836%29.png "image (36).png")
2. 在 CA 成功创建后,状态为 active,其中 PCA 的 **ARN** 将会在后续流程中多次使用。
![download_image (2).jpeg](https://dev-media.amazoncloud.cn/1e5af0cd86d34808b6b61f89e2156adc_download_image%20%282%29.jpeg "download_image (2).jpeg")
### **为 ACM Private CA 设置 EKS 节点权限**
默认情况下是无颁发权限,为了从 ACM Private CA 颁发证书,需要将 IAM 策略添加到 EKS NodeInstanceRole 中,包括 iamserviceaccount 服务角色。
第一步,创建 `pca-iam-policy.json` 文件,需要将 `\${PCA_ARN}` 替换成你的 PCA_ARN。
```plain
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "awspcaissuer",
"Action": [
"acm-pca:DescribeCertificateAuthority",
"acm-pca:GetCertificate",
"acm-pca:IssueCertificate"
],
"Effect": "Allow",
"Resource": "\${PCA_ARN}"
}
]
}
```
第二步,根据 `pca-iam-policy.json` 创建 IAM 和 iamserviceaccount,需要将 `\${account_id}` 替换为你的 Amazon 账户ID。
```plain
aws iam create-policy \\
--policy-name AWSPCAIssuerIAMPolicy \\
--policy-document file://pca-iam-policy.json
# 创建命名空间
kubectl create namespace aws-pca-issuer
eksctl create iamserviceaccount \\
--cluster=\${cluster_name} \\
--namespace=aws-pca-issuer \\
--name=aws-pca-issuer \\
--attach-policy-arn=arn:aws:iam::\${account_id}:policy/AWSPCAIssuerIAMPolicy \\
--override-existing-serviceaccounts \\
--approve
```
### **安装 aws-privateca-issuer**
AWS PrivateCA Issuer 充当 cert-manager 的插件([External Issuers](https://cert-manager.io/docs/configuration/external/))签署证书请求。
1. 使用 Helm 安装
```plain
helm repo add awspca https://cert-manager.github.io/aws-privateca-issuer
helm repo update
helm install aws-pca-issuer awspca/aws-privateca-issuer \\
-n aws-pca-issuer \\
--set serviceAccount.create=false \\
--set serviceAccount.name=aws-pca-issuer
```
2. 查看状态
```plain
\$ kubectl get pods -n aws-pca-issuer
NAME READY STATUS RESTARTS AGE
aws-pca-issuer-aws-privateca-issuer-5cdd4b4687-z52n7 1/1 Running 0 20s
```
### **创建颁发者并申请证书**
1. 将 issuer-cert.yaml 文件中的 `\${PCA_ARN}` 替换成你的配置,并执行如下命令:
`kubectl apply -f issuer-cert.yaml`
```plain
# issuer-cert.yaml
apiVersion: awspca.cert-manager.io/v1beta1
kind: AWSPCAClusterIssuer
metadata:
name: demo-test-root-ca
spec:
arn: \${PCA_ARN}
---
kind: Certificate
apiVersion: cert-manager.io/v1
metadata:
name: nlb-lab-tls-cert
spec:
commonName: httpbin.example-test.org # 需要替换成您的自定义域名
dnsNames:
- httpbin.example-test.org # 需要替换为您的自定义域名
duration: 2160h0m0s
issuerRef:
group: awspca.cert-manager.io
kind: AWSPCAClusterIssuer
name: demo-test-root-ca
renewBefore: 360h0m0s
secretName: nlb-tls-app-secret
usages:
- server auth
- client auth
privateKey:
algorithm: "RSA"
size: 2048
```
2. 运行以下命令验证证书是否已颁发,secret 是否已生成。
```plain
\$ kubectl get cert
NAME READY SECRET AGE
nlb-lab-tls-cert True nlb-tls-app-secret 10s
\$ kubectl get secret
NAME TYPE DATA AGE
nlb-tls-app-secret kubernetes.io/tls 3 8s
```
### **公开并保护 httpbin 应用程序**
[请确保 APISIX Ingress 已成功安装](https://github.com/api7/contents/blob/a13324ce0aedff03b66cad4668340aa413a8ba86/blog/using-apisix-ingress-with-aws-acm.md#%E5%AE%89%E8%A3%85-apisix-ingress)
1. 部署 httpbin 应用程序
```plain
kubectl run httpbin --image kennethreitz/httpbin --port 80
kubectl expose pod httpbin --port 80
```
2. 创建 Ingress 以公开并保护 httpbin 应用程序
* `kubectl apply -f ingress-httpbin.yaml`
```plain
# ingress-httpbin.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: httpbin-demo-apisix
spec:
ingressClassName: apisix
tls:
- hosts:
- httpbin.example-test.org
secretName: nlb-tls-app-secret
rules:
- host: httpbin.example-test.org
http:
paths:
- backend:
service:
name: httpbin
port:
number: 80
path: /
pathType: Prefix
```
3. 访问应用域名
[请确保你的自定义域已与负载均衡器名称关联](https://github.com/api7/contents/blob/a13324ce0aedff03b66cad4668340aa413a8ba86/blog/using-apisix-ingress-with-aws-acm.md#%E5%B0%86%E8%87%AA%E5%AE%9A%E4%B9%89%E5%9F%9F%E4%B8%8E%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E5%99%A8%E5%90%8D%E7%A7%B0%E5%85%B3%E8%81%94)。
```bash
\$ curl https://httpbin.example-test.org/headers --cacert acm-pca/cacert.pem
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.example-test.org",
"User-Agent": "curl/7.74.0",
"X-Forwarded-Host": "httpbin.example-test.org"
}
}
```
## **总结**
本文中通过实践演示了 APISIX Ingress 与 AWS ACM 和 ACM Private CA 组件配合使用,并介绍了在 Kubernetes 中终止 TLS 证书的两种配置方式。在公开信任的证书中,ACM + NLB 的配置方式会更合适。如果应用之间有加密的要求,ACM Private CA 提供了安全性更强的托管服务。希望本文的实践环节能帮助读者在 AWS EKS 集群中更有效配置和管理 TLS 流量。