一、引言
在 K8s 环境下,安全网关的运用有着诸多重要作用。本文将围绕 Envoy Gateway 相关的安全网关配置及应用场景展开详细介绍,旨在帮助使用者更好地理解和运用相关技术,实现安全高效的网络通信。
二、安装
按照快速入门任务中的步骤来安装 Envoy Gateway 以及示例清单。在继续操作之前,应当能够使用 HTTP 来查询示例后端。
验证网关状态:
kubectl get gateway/eg -o yaml
三、TLS 证书
-
生成网关用于终止客户端 TLS 连接所使用的证书和密钥:
- 创建一个根证书和私钥来对证书进行签名:
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./***=example.***' -keyout example.***.key -out example.***.crt
- 为 `www.example.***` 创建一个证书和私钥:
openssl req -out www.example.***.csr -newkey rsa:2048 -nodes -keyout www.example.***.key -subj "/***=www.example.***/O=example organization"
openssl x509 -req -days 365 -CA example.***.crt -CAkey example.***.key -set_serial 0 -in www.example.***.csr -out www.example.***.crt
- 将证书/密钥存储在一个 Secret 中:
kubectl create secret tls example-cert --key=www.example.***.key --cert=www.example.***.crt
- 更新快速入门中的网关,使其包含一个监听在 443 端口且引用 `example-cert` Secret 的 HTTPS 监听器:
kubectl patch gateway eg --type=json --patch '
- op: add
path: /spec/listeners/-
value:
name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
group: ""
name: example-cert
'
- 再次验证网关状态:
kubectl get gateway/eg -o yaml
-
多个 HTTPS 监听器:
- 为额外的 HTTPS 监听器创建一个 TLS 证书/密钥:
openssl req -out foo.example.***.csr -newkey rsa:2048 -nodes -keyout foo.example.***.key -subj "/***=foo.example.***/O=example organization"
openssl x509 -req -days 365 -CA example.***.crt -CAkey example.***.key -set_serial 0 -in foo.example.***.csr -out foo.example.***.crt
- 将证书/密钥存储在一个 Secret 中:
kubectl create secret tls foo-cert --key=foo.example.***.key --cert=foo.example.***.crt
- 在示例网关上创建另一个 HTTPS 监听器:
kubectl patch gateway eg --type=json --patch '
- op: add
path: /spec/listeners/-
value:
name: https-foo
protocol: HTTPS
port: 443
hostname: foo.example.***
tls:
mode: Terminate
certificateRefs:
- kind: Secret
group: ""
name: foo-cert
'
- 更新 HTTPRoute 以将主机名 `foo.example.***` 的流量路由到示例后端服务:
kubectl patch httproute backend --type=json --patch '
- op: add
path: /spec/hostnames/-
value: foo.example.***
'
- 验证网关状态:
kubectl get gateway/eg -o yaml
- 按照“测试”部分中的步骤,通过两个网关监听器测试到后端应用的连接性。将 `www.example.***` 替换为 `foo.example.***` 来测试新的 HTTPS 监听器。
-
跨命名空间证书引用:
网关可以配置为引用不同命名空间中的证书。这需要在目标命名空间中创建一个 ReferenceGrant。若没有 ReferenceGrant,跨命名空间引用是无效的。- 在继续操作之前,确保能够从“测试”部分查询 HTTPS 后端服务。
- 为了演示跨命名空间证书引用,创建一个 ReferenceGrant,允许来自“default”命名空间的网关引用“envoy-gateway-system”命名空间中的 Secrets:
cat <<EOF | kubectl apply -f -
apiVersion: gateway.***working.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: example
namespace: envoy-gateway-system
spec:
from:
- group: gateway.***working.k8s.io
kind: Gateway
namespace: default
to:
- group: ""
kind: Secret
EOF
- 删除之前创建的 Secret:
kubectl delete secret/example-cert
此时网关的 HTTPS 监听器应当会显示 Ready: False 状态条件,并且示例 HTTPS 后端将无法再通过网关访问。
kubectl get gateway/eg -o yaml
- 在“envoy-gateway-system”命名空间中重新创建示例 Secret:
kubectl create secret tls example-cert -n envoy-gateway-system --key=www.example.***.key --cert=www.example.***.crt
- 更新网关的 HTTPS 监听器(指定命名空间为“envoy-gateway-system”),示例如下:
cat <<EOF | kubectl apply -f -
apiVersion: gateway.***working.k8s.io/v1
kind: Gateway
metadata:
name: eg
spec:
gatewayClassName: eg
listeners:
- name: http
protocol: HTTP
port: 80
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
group: ""
name: example-cert
namespace: envoy-gateway-system
EOF
此时网关的 HTTPS 监听器状态应当会显示 Ready: True 条件,并且应当能够再次通过网关查询 HTTPS 后端。
最后,使用上述“测试”部分的内容测试连接性。
四、清理
按照快速入门中的步骤卸载 Envoy Gateway 和示例清单。
删除 Secrets:
kubectl delete secret/example-cert
kubectl delete secret/foo-cert
五、RSA + ECDSA 双栈证书
-
预检查:
在集群内测试(无外部负载均衡器支持的情况下),我们可以在强制使用 RSA 密码套件时通过 Envoy 代理查询示例应用,如下所示:
curl -v -HHost:www.example.*** --resolve "www.example.***:8443:127.0.0.1" \
--cacert example.***.crt https://www.example.***:8443/get -Isv --ciphers ECDHE-RSA-CHACHA20-POLY1305 --tlsv1.2 --tls-max 1.2
由于此时配置的 Secret 是基于 RSA 的 Secret,如果我们强制使用 ECDSA 密码套件,调用将会失败,如下所示:
curl -v -HHost:www.example.*** --resolve "www.example.***:8443:127.0.0.1" \
--cacert example.***.crt https://www.example.***:8443/get -Isv --ciphers ECDHE-ECDSA-CHACHA20-POLY1305 --tlsv1.2 --tls-max 1.2
-
TLS 证书:
重用在“安全网关”任务中生成的 CA 证书和密钥对,并使用该 CA 来签署 RSA 和 ECDSA 服务器证书。注意,CA 证书和密钥名称分别为example.***.crt和example.***.key。- 为
www.example.***创建一个 ECDSA 证书和私钥:
- 为
openssl ecparam -noout -genkey -name prime256v1 -out www.example.***.ecdsa.key
openssl req -new -SHA384 -key www.example.***.ecdsa.key -nodes -out www.example.***.ecdsa.csr -subj "/***=www.example.***/O=example organization"
openssl x509 -req -SHA384 -days 365 -in www.example.***.ecdsa.csr -CA example.***.crt -CAkey example.***.key -CAcreateserial -out www.example.***.ecdsa.crt
- 将证书/密钥存储在一个 Secret 中:
kubectl create secret tls example-cert-ecdsa --key=www.example.***.ecdsa.key --cert=www.example.***.ecdsa.crt
- 用这个额外的 ECDSA Secret 来修补网关:
kubectl patch gateway eg --type=json --patch '
- op: add
path: /spec/listeners/1/tls/certificateRefs/-
value:
name: example-cert-ecdsa
'
- 验证网关状态:
kubectl get gateway/eg -o yaml
-
测试:
同样,在集群内测试(无外部负载均衡器支持的情况下),我们可以在强制使用 RSA 密码套件时通过 Envoy 代理查询示例应用,这应当和之前一样能正常工作:
curl -v -HHost:www.example.*** --resolve "www.example.***:8443:127.0.0.1" \
--cacert example.***.crt https://www.example.***:8443/get -Isv --ciphers ECDHE-RSA-CHACHA20-POLY1305 --tlsv1.2 --tls-max 1.2
此外,现在强制使用 ECDSA 密码套件查询示例应用也应当能正常工作了:
curl -v -HHost:www.example.*** --resolve "www.example.***:8443:127.0.0.1" \
--cacert example.***.crt https://www.example.***:8443/get -Isv --ciphers ECDHE-ECDSA-CHACHA20-POLY1305 --tlsv1.2 --tls-max 1.2
六、基于 SNI 的证书选择
-
额外配置:
使用“TLS 证书”部分的内容,我们首先为另一个主机www.sample.***生成额外的 Secret。
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=sample Inc./***=sample.***' -keyout sample.***.key -out sample.***.crt
openssl req -out www.sample.***.csr -newkey rsa:2048 -nodes -keyout www.sample.***.key -subj "/***=www.sample.***/O=sample organization"
openssl x509 -req -days 365 -CA sample.***.crt -CAkey sample.***.key -set_serial 0 -in www.sample.***.csr -out www.sample.***.crt
kubectl create secret tls sample-cert --key=www.sample.***.key --cert=www.sample.***.crt
然后更新网关配置以适配这个将用于终止 TLS 流量的新证书:
kubectl patch gateway eg --type=json --patch '
- op: add
path: /spec/listeners/1/tls/certificateRefs/-
value:
name: sample-cert
'
最后,更新 HTTPRoute 以将主机名 www.sample.*** 的流量路由到示例后端服务:
kubectl patch httproute backend --type=json --patch '
- op: add
path: /spec/hostnames/-
value: www.sample.***
'
-
测试:
参考之前在“有外部负载均衡器支持的集群中的测试”部分提到的步骤。
七、自定义网关 TLS 参数
除了通过网关 API 启用 TLS 之外,Envoy Gateway 还支持自定义 TLS 参数。在本示例中,我们将自定义最小支持的 TLS 版本为 TLSv1.3。
cat <<EOF | kubectl apply -f -
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
name: enforce-tls-13
namespace: default
spec:
targetRefs:
- group: gateway.***working.k8s.io
kind: Gateway
name: eg
tls:
minVersion: "1.3"
EOF
八、测试 TLS 参数
尝试使用不支持的 TLS 版本进行连接:
curl -v -HHost:www.sample.*** --resolve "www.sample.***:8443:127.0.0.1" \
--cacert sample.***.crt --tlsv1.2 --tls-max 1.2 https://www.sample.***:8443/get -I
输出显示,由于客户端使用了不支持的 TLS 协议版本,连接失败。现在,在不指定客户端版本的情况下连接到网关,注意此时连接是使用 TLSv1.3 建立的:
curl -v -HHost:www.sample.*** --resolve "www.sample.***:8443:127.0.0.1" \
--cacert sample.***.crt https://www.sample.***:8443/get -I
| 专业术语 | 翻译(若有) |
|---|---|
| TLS | 传输层安全 |
| ReferenceGrant | 引用授权 |
| ClientTrafficPolicy | 客户端流量策略 |
| Gateway API | 网关 API |
| LoadBalancer | 负载均衡器 |
| SNI | 服务器名称指示 |