使用 Cert Manager 自动管理 Kubernetes Gateway 证书
在本文中,我们将探讨如何利用 cert-manager 自动管理 Kubernetes Gateway 证书,以增强集群入口安全并提升管理效率。
背景
Kubernetes Gateway
或许,确切地应该称为 Kubernetes Gateway API (下称 GWAPI)。GWAPI 是由 SIG-NETWORK 社区管理的开源项目,是一种规范,不提供实现。Gateway API 被认为是 Kubernetes Ingress API 的继任者,在 2019 年的 Ingress 革命 中首次被提出,并在 2020 年 11 月发布了第一个版本。GWAPI 的发展非常快,截止本文发出已经演进到了 1.0.0 版本,并且 1.1.0 也即将发布(已经发布了 1.1.0-rc2)。
GWAPI 是一种管理 Kubernetes 集群内部和集群外部流量的方法。它提供了一个更精细、更模块化的方法来描述路由和网关配置,相比于传统的 Ingress API,GWAPI 设计上更为灵活和可扩展。
cert-manager
cert-manager 是一个专门为 Kubernetes 设计的自动化证书管理工具,它简化了在 Kubernetes 集群中使用 X.509 证书的管理过程。通过 cert-manager,用户可以轻松地为 Kubernetes 中的各种资源,如服务、应用程序和内部通信、自动颁发、更新和管理 SSL/TLS 证书。这对于确保数据传输的安全性非常重要。
cert-manager 主要有以下特性:
- 自动证书颁发和续期:cert-manager 可以自动处理证书的申请、续期和生命周期管理。
- 支持多种颁发源:cert-manager 支持多种证书颁发机构(CA),包括自签的证书;通过 ACME 使用公共的 CA 如 Let’s Encrypt;企业自建的 CA 系统,如 HashiCorp Vault、铜锁的 RustyVault 的 PKI 等。
- Kubernetes 原生集成:作为 Kubernetes 的原生组件,cert-manager 可以使用 CRD 来定义颁发者 Issuer、证书 Certificate 等资源。
尤其是最后这点,cert-manager 可以便捷地与 Kubernetes 生态的其他组件无缝集成,比如本文要介绍的与 K8s Gateway 的整合。
Why cert-manager?
在 Kubernetes 环境中,Gateway 作为整个集群的入口,是管理入站流量的重要组件。在众多功能特性中,安全性尤为突出和重要。其中,通信加密是确保数据安全的首要措施。通过使用 TLS/SSL 证书,Gateway 确保所有进出集群的数据都是加密的。这不仅保护了数据免受窃听和篡改,还确保了客户端和服务之间的通信是安全的。除了这种单向 SSL 加密以外,还可以配置为支持双向的 SSL 加密来实现端到端的安全验证。
而使用 cert-manager 来管理 Kubernetes Gateway 的证书提供了许多优势,显著提升操作效率、增强系统安全性,并且能够以可扩展和可维护的方式支持企业级的部署和管理:
- 自动证书管理:开发者和管理员可以减少手动介入,从而降低因手动处理证书相关任务(如忘记续期)而导致的错误和服务中断风险。这些都可以通过 cert-manager 的自动化来完成。
- 集成外部 CA:cert-manager 支持多种证书颁发机构(CA),包括 Let’s Encrypt、HashiCorp Vault、Venafi 等。这使得在 Kubernetes 环境中使用商业或自建的 CA 变得容易,并能确保证书的合规性和认证。
- 安全和合规性:自动化证书轮换和短期证书的使用,增强安全性和合规性(自动更新证书意味着攻击者窃取的证书只在很短的有效期内有用)。
- 与 Kubernetes 原生集成:与操作 GWAPI 的 CRD 一样,cert-manager CRD 的操作方式对开发者友好,也能够与 GWAPI 无缝集成。
演示
说明
- FSM Gateway 作为 Gateway API 的实现 之一,也是 FSM 整个生态中的众多组件之一,担负着 Kubernetes 入口流量管理的重任。在这次演示中,我们将采用 FSM Gateway 作为网关实施方案,利用其 TLS 卸载 功能来增强上游服务的安全性。
- 前面提到 cert-manager 可以支持 多种颁发源,这里为了操作简便,选择最简单的自签发的 CA 证书。
前提条件
- K8s 集群 >=1.22
- kubectl
- helm
安装 FSM Gateway
参考 FSM Gateway 的安装文档,我们使用 CLI 进行安装,当前 FSM 的最新版本为 1.2.4。
system=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m | sed -E 's/x86_/amd/' | sed -E 's/aarch/arm/')
release=v1.2.4
curl -L https://github.com/flomesh-io/fsm/releases/download/$release/fsm-$release-$system-$arch.tar.gz | tar -vxzf -
./$system-$arch/fsm version
执行下面的命令安装。
fsm install \
--set=fsm.fsmGateway.enabled=true
安装成功后,可以看到已经注册了 GatewayClass fsm-gateway-cls
。
kubectl get gatewayclass
NAME CONTROLLER ACCEPTED AGE
fsm-gateway-cls flomesh.io/gateway-controller True 106s
安装 cert-manager
cert-manager 的安装可以通过 kubectl manifest 或者 helm chart 来安装,我们这里选择 helm chart。 注意在安装时记得安装 CRD。
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
由于使用自签名的 CA 证书,在创建颁发源之前先自签 CA 证书。
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 365000 \
-key ca-key.pem \
-out ca-cert.pem \
-subj '/CN=flomesh.io'
将 CA 证书保存在 Secret ca-key-pair
中,要指定命名空间 cert-manager
。
kubectl create secret generic -n cert-manager ca-key-pair \
--from-file=tls.crt=./ca-cert.pem \
--from-file=tls.key=./ca-key.pem
这里我们直接创建一个 ClusterIssuer
,类型为 ca
,指定为我们上面创建的 secret ca-key-pair
。
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: ca-issuer-sample
spec:
ca:
secretName: ca-key-pair
EOF
一切就绪之后,接下来让我们来部署示例应用进行测试。
部署示例应用
创建一个 httpbin 服务。
kubectl create namespace httpbin
cat <<EOF | kubectl apply -n httpbin -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
template:
metadata:
labels:
app: httpbin
spec:
containers:
- name: httpbin
image: kennethreitz/httpbin
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
spec:
selector:
app: httpbin
ports:
- protocol: TCP
port: 8080
targetPort: 80
EOF
签发 TLS 证书
要签发 TLS 证书,我们需要创建一个 CR Certificate
。cert-manager 会监控该资源,然后通过资源的配置签发证书,并保存在字段 secretName
指定的 secret 中。
- 证书保存在 secret
simple-gateway-cert
中。 - 证书有效期
duration
设置为60m
- 证书轮换的提前时间
renewBefore
设置为59m
,也就是每分钟轮换一次。 - 通用名称
commonName
设置为foo.example.com
- 还有最重要的指定证书的颁发源为
ClusterIssuer
ca-issuer-sample
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: simple-gateway-cert
namespace: httpbin
spec:
secretName: simple-gateway-cert
duration: 60m
renewBefore: 59m
subject:
organizations:
- flomesh-io
commonName: foo.example.com
issuerRef:
name: ca-issuer-sample
kind: ClusterIssuer
group: cert-manager.io
EOF
Certificate
创建完成后,我们可以在命名空间 httpbin
下找到 cert-manager 创建的 secert,类型为 kubernetes.io/tls
。
kubectl get secret simple-gateway-cert
NAME TYPE DATA AGE
simple-gateway-cert kubernetes.io/tls 3 20s
有了证书之后,就可以为 httpbin 服务暴露 HTTPS 的访问入口了。
创建网关和路由
为了向集群外暴露 httpbin 服务,需要为其创建网关和路由:
- 监听器监听在端口
8000
- 协议设置为
TLS
- 证书通过 secret
simple-gateway-cert
提供 - 还有最重要的模式设置为
Terminate
cat <<EOF | kubectl apply -n httpbin -f -
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: simple-fsm-gateway
spec:
gatewayClassName: fsm-gateway-cls
listeners:
- name: https
port: 8000
protocol: HTTPS
tls:
certificateRefs:
- kind: Secret
name: simple-gateway-cert
mode: Terminate
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: http-route-foo
spec:
parentRefs:
- name: simple-fsm-gateway
port: 8000
hostnames:
- foo.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: httpbin
port: 8080
EOF
测试
如果不提供 CA 证书直接访问,会遇到类似下面的问题。
curl https://foo.example.com/headers --connect-to foo.example.com:443:$GATEWAY_IP:8000
curl: (60) SSL certificate problem: unable to get local issuer certificate
指定 CA 证书然后再次访问,同时添加参数 -v
可以发现请求成功。
curl --cacert ca-cert.pem https://foo.example.com/headers --connect-to foo.example.com:443:$GATEWAY_IP:8000 -v
* Connecting to hostname: 198.19.249.153
* Connecting to port: 8000
* Trying 198.19.249.153:8000...
* Connected to 198.19.249.153 (198.19.249.153) port 8000
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* CAfile: ca-cert.pem
* CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-CHACHA20-POLY1305-SHA256
* ALPN: server accepted h2
* Server certificate:
* subject: O=flomesh-io; CN=foo.example.com
* start date: May 8 14:59:33 2024 GMT
* expire date: May 8 15:59:33 2024 GMT
* common name: foo.example.com (matched)
* issuer: CN=flomesh.io
* SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://foo.example.com/headers
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: foo.example.com]
* [HTTP/2] [1] [:path: /headers]
* [HTTP/2] [1] [user-agent: curl/8.4.0]
* [HTTP/2] [1] [accept: */*]
> GET /headers HTTP/2
> Host: foo.example.com
> User-Agent: curl/8.4.0
> Accept: */*
>
< HTTP/2 200
< server: gunicorn/19.9.0
< date: Wed, 08 May 2024 15:00:07 GMT
< content-type: application/json
< content-length: 141
< access-control-allow-origin: *
< access-control-allow-credentials: true
<
{
"headers": {
"Accept": "*/*",
"Connection": "keep-alive",
"Host": "foo.example.com",
"User-Agent": "curl/8.4.0"
}
}
* Connection #0 to host 198.19.249.153 left intact
从输出的 debug 内容中我们还可以找到 TLS 握手的详细信息。比如证书有效期 start date: May 8 14:59:33 2024 GMT
和 expire date: May 8 15:59:33 2024 GMT
。
等待 1 分钟后再次请求,你将会看到证书信息已经更新。此时,如果检查 Certificate 资源,可以确认证书确实发生了轮换。
kubectl describe certificate simple-gateway-cert
.
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Issuing 61s cert-manager-certificates-trigger Renewing certificate as renewal was scheduled at 2024-05-08 15:03:33 +0000 UTC
Normal Requested 61s cert-manager-certificates-request-manager Created new CertificateRequest resource "simple-gateway-cert-1"
Normal Issuing 1s cert-manager-certificates-trigger Renewing certificate as renewal was scheduled at 2024-05-08 15:04:33 +0000 UTC
Normal Reused 1s (x2 over 61s) cert-manager-certificates-key-manager Reusing private key stored in existing Secret resource "simple-gateway-cert"
Normal Requested 1s cert-manager-certificates-request-manager Created new CertificateRequest resource "simple-gateway-cert-2"
Normal Issuing 1s (x2 over 61s) cert-manager-certificates-issuing The certificate has been successfully issued
总结
使用 cert-manager 自动管理 Kubernetes Gateway 证书可以显著提升安全性和管理效率。通过自动化的证书处理,如自动续期和轮换,cert-manager 减轻了管理负担并降低了人为错误,确保了持续的系统安全。这一工具的集成和灵活性使其成为维护 Kubernetes 集群安全的理想选择。