Kubernetes 容器和应用程序越来越多使用数字证书提供安全身份验证和加密。Kubernetes 中广泛采用的 TLS 证书生命周期管理解决方案是 cert-manager,它是 Kubernetes 的附加组件,用于请求证书、将证书分发到 Kubernetes 容器和自动续订证书。如果客户的业务涉及,HIPPA,FIPS 以及其他强合规场景,不允许集群存储证书私钥,可以考虑使用 AWS Private CA 服务和 cert-manager 集成,完成容器集群证书管理工作。
AWS Private CA(PCA) 是 AWS 的托管服务,帮助企业建立私有的公钥基础系统(PKI),签发证书来满足企业内部身份认证,构建安全网络连接等方面的需求。当前,大量的应用和微服务都采用容器化的方式部署在 Amazon EKS 中,为了实现容器应用之间和各微服务之间端到端的安全通信,企业需要在 EKS 内部为应用签发私有证书以建立 TLS 通道,同时为了避免证书过期对应用的影响,企业需要有效的监控证书过期时间并提前续订。本文将会介绍如何使用 cert-manager 和 PCA 在 EKS 中签发和管理证书,并构建端到端的 TLS 通信。
cert-manager 是一个开源项目,其作为在 kubernetes 环境中管理 X.509 证书的标准工具,可以帮助企业开发团队使用同一个工具在不同的应用环境中基于不同的 CA 来签发和管理证书。在 cert-manager 中,证书和证书颁发者被抽象成了自定义的 kubernetes 资源,并在证书签发成功后会自动创建包含证书内容的 kubernetes secret 供应用使用。AWS Private CA Issuer(AWS PCA Issuer) 是 cert-manager 的一个插件,用户可以借助它在 cert-manger 中处理对 AWS Private CA 的证书请求。
先决条件
- 在本地安装 AWSCLI,并配置权限确保你能访问亚马逊云科技账号中的 IAM、EKS、PCA 等服务;
- 安装命令行工具 eksctl、kubectl 和 helm;
- 根据文档,在us-west-2 区域中正确创建并激活了一个 CA;
创建并配置 Amazon EKS 集群
1. 执行以下命令使用 eksctl 工具创建一个 eks集群,该集群会使用 eksctl 默认的网络配置和权限配置,并拥有 2 个工作节点;在生产环境下,如果需要对 EKS 集群进行更多自定义的配置,请参考:Creating and managing clusters
> eksctl create cluster --name pca-lab --region us-west-2 \
--nodegroup-name nodes --node-type t3.medium --nodes 2
在命令执行成功后,eksctl 将会自动将 k8s 的认证信息写入本地的 kube config 中,你可以通过执行 kubectl 来查看验证集群节点信息,当你通过以下命令看到有类似的输出时,则表明集群已经创建成功;
> kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-192-168-51-138.us-west-2.compute.internal Ready <none> 2m39s v1.29.3-eks-ae9a62a
ip-192-168-6-72.us-west-2.compute.internal Ready <none> 2m46s v1.29.3-eks-ae9a62a
2. 在 eks 集群中安装 EKS Pod Identity Agent;EKS Pod Identity 是 Amazon EKS 推出的一项新功能,可以简化集群管理员配置 Kubernetes 应用程序来获取 AWS IAM 权限的方式,实现在多个集群中共享使用 IAM 角色,已达到简化策略管理的目的;
执行以下命令在 eks 集群中安装 EKS Pod Identity Agent:
> eksctl create addon --cluster pca-lab --region us-west-2 \
--name eks-pod-identity-agent --version v1.2.0-eksbuild.1
3. 在命令执行结束后,你可以通过执行以下命令来通过查看 eks-pod-identity-agent 的 Pod 的运行状态确定 EKS Pod Identity Agent 被正确安装:
> kubectl get pods -n kube-system | grep 'eks-pod-identity-agent'
eks-pod-identity-agent-czdgn 1/1 Running 0 69s
eks-pod-identity-agent-pmd44 1/1 Running 0 69s
安装 cert-manager
1. 执行以下命令使用 helm 在 eks 集群中安装 cert-manger:
> helm repo add jetstack https://charts.jetstack.io --force-update
> helm repo update
> helm install cert-manager jetstack/cert-manager --namespace cert-manager \
--create-namespace --version v1.14.5 --set installCRDs=true
2. 通过以下命令来查看集群中 cert-manager 的 Pod 的运行状况以验证 cert-manager 的安装:
> kubectl get pods -n cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-796cbd6574-vf6ln 1/1 Running 0 51s
cert-manager-cainjector-9b74bc658-47zm4 1/1 Running 0 51s
cert-manager-webhook-7ddfd7c4bd-ls8rw 1/1 Running 0 51s
AWS Private CA Issuer
AWS PCA Issuer 在 eks 集群中会创建一个 Pod 执行账号中的证书相关的操作,因此需要 Pod 具有 AWS PCA 证书相关的权限。在 eks 集群中,Pod 有多种方式可以获得用户权限,比如使用继承自 worknode 的权限,在 Pod 绑定的 Service Account 上绑定的 AWS 账号权限等。在上文中介绍到的 EKS Pod Identity 就是就是通过将权限绑定到 Service Account 从而实现给 eks中资源授权的一种方式,以下将会使用这种方式给 AWS PCA Issuer 绑定权限。
1. 通常,我们将以绑定权限的时候使用最小权限原则,在 AWS PCA Issuer 中,主要执行的操作有签发和获取证书,以获取 CA 的配置和状态等,以下是满足 AWS PCA Issuer 需求的最小权限:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "awspcaissuer",
"Action": [
"acm-pca:DescribeCertificateAuthority",
"acm-pca:GetCertificate",
"acm-pca:IssueCertificate"
],
"Effect": "Allow",
"Resource": "<aws-pca-arn>"
}
]
}
2. 在 AWS IAM 中使用上述的权限创建一个权限策略(IAM Policy);使用实际的 CA ARN 替换上述权限文档中的 aws-pca-arn
并其保存在文件 iam-policy.json
中,执行以下命令来创建 IAM Policy;命令执行成功后你会看到如下的输出,其中的 Arn
字段为新创建的权限策略的 ARN:
> aws iam create-policy --region us-west-2 --policy-name AWSPCAIssuerIAMPolicy \
--policy-document file://iam-policy.json
{
"Policy": {
"PolicyName": "AWSPCAIssuerIAMPolicy",
"PolicyId": "ANPA4T7IE2MYHW4AOBGQT",
"Arn": "<iam-permission-policy-arn>",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"PermissionsBoundaryUsageCount": 0,
"IsAttachable": true,
"CreateDate": "2024-06-03T07:14:58Z",
"UpdateDate": "2024-06-03T07:14:58Z"
}
}
3. 执行以下命令使用 Pod Identity 给 EKS 集群中的 Service Account 增加权限。请将 iam-permission-policy-arn
替换为上文中输出的实际的值:
eksctl create podidentityassociation --cluster pca-lab --region us-west-2 \
--namespace aws-pca-issuer --service-account-name aws-pca-issuer \
--permission-policy-arns <iam-permissoin-policy-arn>
4. 使用 helm 在集群中安装 AWS Private CA Issuer,AWS Private CA Issuer 是 cert-manager 的一个插件,用户可以通过它在 cert-manager 中处理 AWS PCA 中的证书请求。
> kubectl create namespace aws-pca-issuer
helm install aws-pca-issuer awspca/aws-privateca-issuer -n aws-pca-issuer \
--set serviceAccount.create=true --set serviceAccount.name=aws-pca-issuer
在命令执行成功后,执行以下命令查看 aws-pca-issuer 的 Pod 的运行状态,以确定 AWS Private CA Issuer 被安装成功:
> kubectl get pods -n aws-pca-issuer
NAME READY STATUS RESTARTS AGE
aws-pca-issuer-aws-privateca-issuer-5ddf8ddfd9-tzztj 1/1 Running 0 26s
签发证书
在 cert-manager 中,证书颁发机构(CA)根据作用范围不同被抽象成了 k8s 中的自定义资源 Issuer
和 ClusterIssuer
;其中,Issuer
通常会作用在当前特定的 namespace 下,而 ClusterIssuer
则作用在整个 k8s 集群中。AWS PCA Issuer 抽象了自定义资源 AWSPCAClusterIssuer
来作为 cert-manager 中的 Issuer 来处理证书签发请求。
1. 在 eks 集群中创建 AWSPCAClusterIssuer
,将 aws-pca-arn
替换为实际的值后执行以下命令:
> kubectl apply -f - <<EOF
apiVersion: awspca.cert-manager.io/v1beta1
kind: AWSPCAClusterIssuer
metadata:
name: pca-lab-ca
spec:
arn: <aws-pca-arn>
region: us-west-2
EOF
awspcaclusterissuer.awspca.cert-manager.io/pca-lab-ca created
2. 在 cert-manager 中,一个自定义资源 Certificate
就意味着一张证书。当你在创建 Certificate
资源的时候,cert-manager 就会使用 Certificate
的定义的 IssuerRef
来处理证书签发请求。你可以执行以下命令在 cert-manager 中使用 AWS PCA Issuer 从 PCA 中签发一张证书:
> kubectl apply -f - <<EOF
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: pca-lab-tls-test-cert
spec:
commonName: <cert-common-name>
dnsNames:
- <domain-1>
duration: 2160h
issuerRef:
group: awspca.cert-manager.io
kind: AWSPCAClusterIssuer
name: pca-lab-ca
renewBefore: 360h
secretName: example-com-cert-secret
usages:
- server auth
- client auth
privateKey:
algorithm: RSA
size: 2048
EOF
3. 执行以下命令来查看上一步所签发的证书,你可以看到证书当前已经出于 READY 状态。其中:SECRET 属性指明了在 eks 集群中与证书关联的 secret
:
> kubectl get certificate pca-lab-tls-test-cert
NAME READY SECRET AGE
pca-lab-tls-test-cert True example-com-cert-secret 13s
4. 另外,你可以在 eks 集群中通过查看与证书关联的 secret
来查看证书的内容:
> kubectl describe secret example-com-cert-secret
Name: example-com-cert-secret
Namespace: default
Labels: controller.cert-manager.io/fao=true
Annotations: cert-manager.io/alt-names: example.com,*.example.com
cert-manager.io/certificate-name: pca-lab-tls-test-cert
cert-manager.io/common-name: www.example.com
cert-manager.io/ip-sans:
cert-manager.io/issuer-group: awspca.cert-manager.io
cert-manager.io/issuer-kind: AWSPCAClusterIssuer
cert-manager.io/issuer-name: pca-lab-ca
cert-manager.io/uri-sans:
Type: kubernetes.io/tls
Data
====
ca.crt: 1318 bytes
tls.crt: 1346 bytes
tls.key: 1679 bytes
构建端到端的应用程序
在上述的操作流程中,我们在 eks 集群中使用 AWS PCA Issuer 从 AWS PCA 中签发了一个证书,这个证书被保存在 eks 的 secret 中,并由 cert-manager 来处理其更新等操作。eks 集群中的 pod 在使用证书时,可以使用 k8s 原生支持的 secret 挂载将证书挂载到 pod 的本地目录中被应用程序所访问。关于 secret
请参考:https://kubernetes.io/docs/concepts/configuration/secret。
以下,我将会在 eks 集群中使用 nginx 创建一个 web 应用,并使用 AWS Load Balancer Contoller 将其暴露为一个使用 NLB 的 service,来提供互联网服务。在应用中,NLB 将不会处理 TLS 证书的卸载,而会将其透传给后端的 nginx 服务来进行 TLS 握手。
执行以下命令:
1. 根据文档在 eks 集群中安装 AWS Load Balancer Controller,参考:https://docs.aws.amazon.com/zh_cn/eks/latest/userguide/lbc-helm.html
2. 执行以下命令创建示例 web 应用:
> kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-example-app
spec:
replicas: 1
selector:
matchLabels:
app: nginx-example-app
template:
metadata:
labels:
app: nginx-example-app
spec:
containers:
- name: main
image: nginxdemos/nginx-hello:plain-text
ports:
- containerPort: 8443
volumeMounts:
- name: secret
mountPath: /etc/nginx/ssl
readOnly: true
- name: config-volume
mountPath: /etc/nginx/conf.d
volumes:
- name: secret
secret:
secretName: example-com-cert-secret
- name: config-volume
configMap:
name: nginx-config
---
apiVersion: v1
kind: Service
metadata:
name: nlb-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb-ip"
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
ports:
- port: 443
targetPort: 8443
protocol: TCP
name: https
selector:
app: nginx-example-app
type: LoadBalancer
---
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-config
data:
app.conf: |-
server {
listen 8443 ssl proxy_protocol;
real_ip_header proxy_protocol;
set_real_ip_from 192.168.0.0/16;
server_name www.example.com;
ssl_certificate /etc/nginx/ssl/tls.crt;
ssl_certificate_key /etc/nginx/ssl/tls.key;
default_type text/plain;
location / {
return 200 "hello world.\n";
}
}
EOF
3. 你可以在浏览器中访问这个示例应用程序,并查看其证书。从其中的 Issuer Name 可以确定其证书签发者的信息:
Clean up
你可以通过执行以下命令删除上文中所创建的 eks 集群,极其创建 eks 集群过程中所关联的其他计算和网络资源:
> eksctl delete cluster --name pca-lab --region us-west-2 --force --disable-nodegroup-eviction
总结
在本文中,介绍了如何在 eks 集群中使用 cert-manager 来签发和管理 AWS PCA 的证书,并演示了如何在 eks 中使用 PCA 证书构建端到端的全链路 TLS 加密应用;由于 cert-manager 和 AWS PCA Issuer 是开源技术,你可以将其使用范围扩到你本地或者线下的 k8s 集群中,来使用 PCA 构建端到端的加密通信链路,从而满足业务的需求。
本篇作者