利用 NGINX Ingress Controller 和 ACM PCA 在使用 Amazon Fargate 的 Amazon EKS 中实现 mTLS



云的灵活性在产生大量应用程序流量的同时,有助于各组织尽快扩展。然而,即使这些企业构建了许多解决方案,如微服务架构、企业对企业应用程序编程接口 (B2B API)、用户身份验证、内容分发、物联网 (IoT) 传感器等,仍必须防止“中间人攻击”或其他形式的网络攻击。通过向网络连接两端的各方证明彼此都能诚实行事,双向传输层安全 (mTLS) 可帮助这些企业提升其应用程序的安全性,或者遵守行业标准或特定的合规性要求。在微服务架构中,服务网格主要用于提供所需基础设施,以安全实现服务间的 mTLS。配置服务网格需要用到包括 Sidecar 代理和控制面板组件在内的更多组件,增加了复杂性,而且需要管理配置和策略。
在本教程中,您将学习如何在不通过服务网格的前提下使用 NGINX Ingress Controller 和 Amazon Certificate Manager (ACM) 私有证书颁发机构 (PCA),为运行在使用 Amazon Fargate 的 Amazon Elastic Kubernetes Service (Amazon EKS) 中的应用程序设置 mTLS。双向传输层安全 (mTLS) 可额外添加层级,保障 Kubernetes 工作负载流量的安全。此外也可用于企业对企业应用程序或标准,如开放银行 (Open Banking)。
本教程会解决两项难题:
- 在使用 Amazon Fargate 的 Amazon EKS 上设置 NGINX Ingress Controller。
- 使用 ACM 私有 CA 颁发的证书,通过 NGINX Ingress Controller 配置 mTLS。
需要注意以下限制:
- 如果您想在 Type=LoadBalancer 服务的后端使用 NGINX Ingress Controller 实现 mTLS 支持,则需要使用网络负载均衡器创建传输控制协议 (TCP) 侦听器并在目标上实现 mTLS。
- Fargate 不支持特权容器。

前提条件
- 拥有有效的亚马逊云科技账户。
- 安装最新版本的 Helm 命令行界面 (CLI)。
- 安装最新版本的 Amazon Command Line Interface (Amazon CLI)
- 安装最新版本的 kubectl。运行以下命令检查您的 Amazon CLI 版本:kubectl version。
- 安装最新版本的 eksctl。运行以下命令检查您的 eksctl 版本:eksctl info。
- 拥有用于测试应用程序的自定义域名。
概述
本教程属于使用 Amazon EKS 管理对安全性要求较高的工作负载这一系列的一部分,致力于在使用 Amazon Fargate 的 Amazon EKS 中利用 mTLS 实现 Kubernetes 工作负载之间的安全通信。在开始按照本教程操作之前,您需要有使用 Fargate 的 现有 Amazon EKS 集群,且已针对运行财务工作负载这一目标进行配置。本教程将涉及以下组件:
- Ingress-Nginx Controller:这是 Kubernetes 的入口控制器,使用 NGINX 作为反向代理和负载均衡器。它能利用入口规则的附加注解开启客户端证书验证,以实现 mTLS。
- Amazon Certificate Manager 私有 CA:有助于创建私有 CA 层次结构,包括根 CA 和从属 CA,而无需运营本地 CA 的投资和维护成本。
- Amazon Private CA Issuer:这是 cert-manager 的附加组件,可使用 Amazon 私有 CA 签发证书申请。
- Amazon Load Balancer Controller:Kubernetes 控制器,有助于为 Kubernetes 集群管理弹性负载均衡器
- Cert-manager:Kubernetes 的附加组件,用于自动管理和颁发各种颁发来源的 TLS 证书。
- ExternalDNS:有助于使用 Amazon Route 53 自动管理应用程序的域名系统 (DNS) 路由。
- 示例应用程序部署:为已启用 mTLS 的服务部署示例工作负载。此工作负载包括在 Kubernetes 环境中为双向 TLS (mTLS) 配置的示例应用程序的部署,这里需要特别关注 Amazon EKS。
使用私有 CA 会产生费用。若使用通用模式,则每个私有 CA 400 美元一个月。若使用短期证书模式,则每个私有 CA 50 美元一个月。对于非整月使用的情况,此费用会根据创建和删除 CA 的时间按比例收取。
步骤 1:配置集群
在此部分,您只会使用 Fargate 配置文件配置 Amazon EKS 集群。若需详细的操作步骤,请参阅构建为财务工作负载预配置的 Amazon EKS 集群中的教程。
步骤 2:安装和配置集群组件
使用 Helm 选项在 Amazon Fargate 上安装 ALB Controller
检索为集群自动创建的虚拟私有云 (VPC),并将以下值替换为实际值:
export AWS_DEFAULT_REGION="us-east-2"
export vpcid=$(aws eks describe-cluster --name fg-security-quickstart --query 'cluster.resourcesVpcConfig.vpcId' --output text)
export mycluster=fg-security-quickstart
export region="us-east-2"
使用以下命令安装 Amazon Load Balancer (ALB) Controller:
helm repo add eks https://aws.github.io/eks-charts
helm repo update eks
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=$mycluster \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller \
--set region=$region \
--set vpcId=$vpcid
下载 NGINX Ingress Controller 清单并为使用 Amazon Fargate 的 Amazon EKS 进行自定义
Fargate 不支持特权容器。默认 NGINX Ingress Controller Pod 需要升级权限才能运行。我们将下载原始的 NGINX Ingress Controller 清单,然后按照以下步骤进行自定义:
1. 从 https://kubernetes.github.io/ingress-nginx/deploy/#aws 下载 deploy.yaml 模板。
curl -O https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/aws/deploy.yaml
2. 编辑下载好的“deploy.yaml”文件,进行以下更改:
找到以下这行文本,将负载均衡器类型改为 nlb-ip,如下所示:
service.beta.kubernetes.io/aws-load-balancer-type: nlb # to nlb-ip
更改 Ingress-Nginx Controller 服务的默认端口,如下所示:
kind: Service
name: ingress-nginx-controller
namespace: ingress-nginx
...
spec:
ports:
- appProtocol: http
targetPort: http # to 8080
- appProtocol: https
targetPort: https # to 8081
(可选)如果想公开应用程序,那么可以将以下额外注解添加到服务部分:
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
将 http-port=8080 和 https-port=8081 作为额外参数添加到部署清单中,如下所示:
kind: Deployment
...
spec:
containers:
- args:
...
- --http-port=8080
- --https-port=8081
更改部署清单中的控制器容器端口,如下所示:
name: controller
ports:
- containerPort: 80 # to 8080
name: http
protocol: TCP
- containerPort:443 # to 8081
name: https
protocol: TCP
将权限升级设为 false:
securityContext:
allowPrivilegeEscalation: true # to false
3. 部署修改后的清单。
kubectl apply -f deploy.yaml
4. 等待 60 秒,让 Fargate 安排到入口控制器 Pod。使用此命令验证安装情况:
kubectl get pods -n ingress-nginx
输出结果应如下所示:
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-xfv5j 0/1 Completed 0 52s
ingress-nginx-admission-patch-5szks 0/1 Completed 2 52s
ingress-nginx-controller-84c9764964-mwxfc 1/1 Running 0 52s
使用 Helm 安装 cert-manager
kubelet 默认侦听 10250 端口,与 cert-manager webhook 的默认端口冲突。在以下命令中,webhook 的侦听端口已设为 10260,防止在使用 Fargate 的 EKS 中出现此问题。
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--version v1.12.2 \
--set installCRDs=true \
--set serviceAccount.create=false \
--set serviceAccount.name=cert-manager \
--set webhook.securePort=10260
在集群中安装和设置 ExternalDNS(可选)
您可以在 Amazon Route53 中为应用程序手动创建 DNS 记录。参阅这些 文档,在 Route 53 中设置和管理指向已部署控制器的 ALB 的记录。
1. 下载示例 external-dns 清单。
wget https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/examples/external-dns.yaml
2. 打开下载好的 external-dns.yaml 文件,编辑 --domain-filter 标志,以纳入托管区域。以下示例适用于托管区域 example.com:
args:
- --source=service
- --source=ingress
- --domain-filter=example.com # will make ExternalDNS see only the hosted zones matching provided domain, omit to process all available hosted zones
- --provider=aws
- --policy=upsert-only
- --aws-zone-type=public
- --registry=txt
- --txt-owner-id=my-identifier # Your Route53 Zone ID
3. 部署下载好的 external-dns.yaml 文件。
kubectl apply -f external-dns.yaml
4. 验证该文件是否已成功部署。
kubectl logs -f $(kubectl get po | egrep -o 'external-dns[A-Za-z0-9-]+')
步骤 3:创建私有 CA
遵照以下步骤,创建 Amazon Certificate Manager 私有 CA,在与集群相同的区域中将 RSA 2048 选为主要算法。
1. 若要创建私有 CA,请在终端执行以下内容,将 example.com 替换为实际值:
export AWS_DEFAULT_REGION="us-east-2"
export SERVICES_DOMAIN="example.com"
export ROOT_CA_ARN=`aws acm-pca create-certificate-authority \
--certificate-authority-type ROOT \
--certificate-authority-configuration \
"KeyAlgorithm=RSA_2048,
SigningAlgorithm=SHA256WITHRSA,
Subject={
Country=US,
State=WA,
Locality=Seattle,
Organization=Build on EKS,
OrganizationalUnit=mTLS Example,
CommonName=${SERVICES_DOMAIN}}" \
--query CertificateAuthorityArn --output text`
2. 创建和安装私有 CA 证书。
ROOT_CA_CSR=`aws acm-pca get-certificate-authority-csr \
--certificate-authority-arn ${ROOT_CA_ARN} \
--query Csr --output text`
3. 使用上一步的证书签名请求 (CSR) 文件颁发根证书。请注意,如果您使用的是 Amazon CLI 第 2 版,那么您需要在调用 issue-certificate 命令前通过编码传递 CSR 数据。
AWS_CLI_VERSION=$(aws --version 2>&1 | cut -d/ -f2 | cut -d. -f1)
[[ ${AWS_CLI_VERSION} -gt 1 ]] && ROOT_CA_CSR="$(echo ${ROOT_CA_CSR} | base64)"
ROOT_CA_CERT_ARN=`aws acm-pca issue-certificate \
--certificate-authority-arn ${ROOT_CA_ARN} \
--template-arn arn:aws:acm-pca:::template/RootCACertificate/V1 \
--signing-algorithm SHA256WITHRSA \
--validity Value=10,Type=YEARS \
--csr "${ROOT_CA_CSR}" \
--query CertificateArn --output text`
4. 导入签名证书作为根 CA。
ROOT_CA_CERT=`aws acm-pca get-certificate \
--certificate-arn ${ROOT_CA_CERT_ARN} \
--certificate-authority-arn ${ROOT_CA_ARN} \
--query Certificate --output text`
5. 导入根 CA 证书,将其安装在 CA 中。Amazon CLI 第 2 版需要通过编码传递证书数据。执行以下命令:
[[ ${AWS_CLI_VERSION} -gt 1 ]] && ROOT_CA_CERT="$(echo ${ROOT_CA_CERT} | base64)"
aws acm-pca import-certificate-authority-certificate \
--certificate-authority-arn $ROOT_CA_ARN \
--certificate "${ROOT_CA_CERT}"
6. 检查 CA 的状态,确认 CA 处于启用状态。如果 CA 处于启用状态,则可以使用。
aws acm-pca describe-certificate-authority \
--certificate-authority-arn "$ROOT_CA_ARN" \
--output json
7. 获取 CA 的 ARN:
echo $ROOT_CA_ARN
步骤 4:安装 Amazon-PCA-ISSUER
Amazon Private CA Issuer 插件充当 cert-manager 的插件,使用 ACM 私有 CA 签发证书申请。定义环境变量:
export ROOT_CA_ARN="YOUR_PRIVATE_CA_ARN"
export SERVICES_DOMAIN="example.com"
记得将 YOUR_PRIVATE_CA_ARN 和 example.com 替换为实际值。Amazon PCA Issuer 插件充当 cert-manager 的插件,使用 Amazon Certificate Manager 私有 CA 签发证书申请。
1. 复制以下命令并将其粘贴到您的终端上,创建 pca-iam-policy.json 文件:
cat<<EOF > pca-iam-policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "awspcaissuer",
"Action": [
"acm-pca:DescribeCertificateAuthority",
"acm-pca:GetCertificate",
"acm-pca:IssueCertificate"
],
"Effect": "Allow",
"Resource": "${ROOT_CA_ARN}"
}
]
}
EOF
2. 使用以下命令创建名为 AWSPCAIssuerIAMPolicy 的 Amazon Identity and Access Management (IAM) 策略:
aws iam create-policy --policy-name AWSPCAIssuerIAMPolicy --policy-document file://pca-iam-policy.json
请注意命令输出结果中的 IAM 策略 ARN,在下一步会用到。
3. 使用以下命令为 Amazon PCA Issuer 插件创建服务账户:
export mycluster=fg-security-quickstart
export ARN="IAM_POLICY_ARN"
eksctl create iamserviceaccount \
--cluster=$mycluster \
--namespace=default \
--name=aws-pca-issuer \
--attach-policy-arn=$ARN \
--override-existing-serviceaccounts \
--approve \
--region us-east-2
将 IAM_POLICY_ARN 替换为之前检索的 ARN 值。
4. 添加 Amazon PCA Issuer Helm 存储库并运行 Helm 安装命令:
helm repo add awspca https://cert-manager.github.io/aws-privateca-issuer
helm install aws-pca-issuer awspca/aws-privateca-issuer -n default \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-pca-issuer
5. 在约 60 秒后运行以下命令,验证 Amazon PCA Issuer 是否正确配置:
% kubectl get pods
NAME READY STATUS RESTARTS AGE
aws-pca-issuer-aws-privateca-issuer-6cf57c44bf-t9qgb 1/1 Running 0 53s
6. 复制以下命令并将其粘贴到您的终端上,创建集群级别证书颁发机构 (ClusterIssuer) 和证书文件:
cat << EOF > cluster-issuer.yaml
apiVersion: awspca.cert-manager.io/v1beta1
kind: AWSPCAClusterIssuer
metadata:
name: demo-test-root-ca
spec:
arn: ${ROOT_CA_ARN}
region: us-east-2
EOF
7. 复制以下命令并将其粘贴到您的终端上,创建 mtls-cert.yaml 文件。该文件会创建包含 CA 证书的密钥,以及可用于 TLS 和客户端身份验证的服务器证书。此外,该文件还会创建客户端应用程序将使用的其他密钥,或者您可以只创建一张证书,使用相同的密钥。
cat << EOF > mtls-cert.yaml
kind: Certificate
apiVersion: cert-manager.io/v1
metadata:
name: mtls-cert-acm
spec:
commonName: mtls.${SERVICES_DOMAIN}
dnsNames:
- www.mtls.${SERVICES_DOMAIN}
- mtls.${SERVICES_DOMAIN}
duration: 2160h0m0s
issuerRef:
group: awspca.cert-manager.io
kind: AWSPCAClusterIssuer
name: demo-test-root-ca
renewBefore: 360h0m0s
secretName: mtls-cert
usages:
- server auth
- client auth
privateKey:
algorithm: "RSA"
size: 2048
---
kind: Certificate
apiVersion: cert-manager.io/v1
metadata:
name: mtls-cert-acm-client
spec:
commonName: mtls.${SERVICES_DOMAIN}
dnsNames:
- www.mtls.${SERVICES_DOMAIN}
- mtls.${SERVICES_DOMAIN}
duration: 2160h0m0s
issuerRef:
group: awspca.cert-manager.io
kind: AWSPCAClusterIssuer
name: demo-test-root-ca
renewBefore: 360h0m0s
secretName: mtls-cert-client
usages:
- server auth
- client auth
privateKey:
algorithm: "RSA"
size: 2048
EOF
8. 针对后端应用程序,在 Amazon EKS 集群中创建证书颁发机构并生成 TLS 证书。
kubectl apply -f cluster-issuer.yaml
kubectl create namespace mtls
kubectl apply -f mtls-cert.yaml -n mtls
步骤 5:部署示例应用程序
1. 复制以下命令并将其粘贴到您的终端上,创建一个示例工作负载:
cat << EOF > mtls.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: mtls-app
labels:
app: mtls
spec:
replicas: 1
selector:
matchLabels:
app: mtls
template:
metadata:
labels:
app: mtls
spec:
containers:
- name: mtls-app
image: hashicorp/http-echo
args:
- "-text=mTLS in Amazon EKS Fargate with NGINX Ingress Controller"
---
kind: Service
apiVersion: v1
metadata:
name: mtls-service
spec:
selector:
app: mtls
ports:
- port: 5678 # Default port for image
EOF
2. 运行以下命令创建工作负载:
kubectl create namespace mtls
kubectl create -f mtls.yaml -n mtls
3. 复制以下命令并将其粘贴到您的终端上,为此工作负载创建 ingress.yaml 入口清单文件:
cat << EOF > ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
# annotations:
# # Enable client certificate authentication
# nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
# # The secret containing the trusted ca certificates. Namespace\secretname
# nginx.ingress.kubernetes.io/auth-tls-secret: mtls/mtls-cert
name: mtls-ingress
spec:
ingressClassName: nginx
rules:
- host: "mtls.${SERVICES_DOMAIN}"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: mtls-service
port:
number: 5678
tls:
- hosts:
- "mtls.${SERVICES_DOMAIN}"
secretName: mtls-cert
EOF
部署清单:
kubectl create -f ingress.yaml -n mtls
约 2 分钟后,运行以下命令验证是否已创建入口。
kubectl get ingress -n mtls
输出结果应如下所示:
NAME CLASS HOSTS ADDRESS PORTS AGE
mtls-ingress nginx mtls.example.com k8s-ingressn-ingressn-f661079efc-a1c19cd4348edbec.elb.us-east-2.amazonaws.com 80, 443 96s
4. 复制以下命令并将其粘贴到您的终端上,创建测试客户端 Pod,其中必须包含与应用程序交互所需的客户端证书。
cat << EOF > mtls-cert-client.yaml
apiVersion: v1
kind: Pod
metadata:
name: mtls-test-pod
spec:
containers:
- name: mtls-container
image: nginx
args:
- /bin/sh
- -c
- >
while true;
do
curl -v -sk "https://mtls.${SERVICES_DOMAIN}" --cert /etc/secret-volume/tls.crt --key /etc/secret-volume/tls.key;
sleep 60
done
volumeMounts:
- name: secret-volume
mountPath: /etc/secret-volume
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: mtls-cert-client
EOF
kubectl apply -f mtls-cert-client.yaml -n mtls
5. 验证是否能从测试 Pod 访问应用程序。在 Pod 中执行以下命令:
kubectl exec -it mtls-test-pod -n mtls -- sh
6. 运行 curl 命令测试应用程序的连通性。
curl -sk -v https://mtls.example.com
若请求成功,就会看到 HTTP 响应状态码 200。
7. 在之前创建的入口清单 ingress.yaml 中启用 mTLS。在 ingress.yaml 中取消以下注释,如下所示:
metadata:
annotations:
# Enable client certificate authentication
nginx.ingress.kubernetes.io/auth-tls-verify-client: "on"
# The secret containing the trusted ca certificates. Namespace\secretname
nginx.ingress.kubernetes.io/auth-tls-secret: mtls/mtls-cert
name: mtls-ingress
执行以下命令,应用更改:
kubectl apply -f ingress.yaml -n mtls
8. 使用双向证书密钥文件验证测试 Pod 是否能与应用程序连接。在 Pod 中执行以下命令:
kubectl exec -it mtls-test-pod -n mtls -- sh
9. 运行 curl 命令测试应用程序的连通性。
curl -v -sk "https://mtls.example.com" --cert /etc/secret-volume/tls.crt --key /etc/secret-volume/tls.key
仅在此 curl 命令中指定证书和密钥时,才会看到 HTTP 响应状态码 200。
清理资源
为避免持续产生费用,建议您删除在学习本教程的过程中创建的资源。可以使用以下命令删除这些资源:
kubectl delete namespace mtls
helm delete aws-pca-issuer
helm delete cert-manager --namespace cert-manager
eksctl delete cluster -f cluster.yaml
# delete the certificates
aws acm delete-certificate --certificate-arn $CERTIFICATE_ARN
aws acm-pca update-certificate-authority --certificate-authority-arn $ROOT_CA_ARN --status DISABLED
aws acm-pca delete-certificate-authority --certificate-authority-arn $ROOT_CA_ARN
总结
在本教程中,您已通过安装自定义 NGINX Ingress Controller,为在使用 Amazon Fargate 的 Amazon EKS 中运行的应用程序成功设置了 mTLS,进而部署了由 Amazon Load Balancer Controller 预配置的网络负载均衡器。

