通过使用SDS(Secret Discovery Service)为服务网关提供HTTPS安全支持、证书动态加载,从而提升服务网关安全性。本文介绍如何通过SDS配置TLS入口网关。
背景信息
SDS(Secrete Discovery Service)是Istio提供的一种动态加载证书的方式,TLS (Transport Layer Security)所需的私钥、服务器证书以及根证书都可以由SDS完成配置。
入口网关代理与入口网关在同一Pod中运行,并监视与入口网关相同的命名空间中创建的Secret。在入口网关上启用SDS具有以下好处:
- 入口网关可以在不需要重启的情况下,动态添加、删除或更新所需要的证书、私钥或者对应的根证书。
- 不需要Secret卷挂载。创建Kubernetes Secret后,网关代理会捕获该Secret,并将其包含的证书、私钥或根证书发送到入口网关。
- 网关代理可以监视多个证书、私钥对,只需要为多个主机创建Secret并更新网关定义。
步骤一:为多个主机准备服务器证书和私钥
为aliyun.com
生成证书和私钥,并保存为Secret。
如果您已经拥有针对aliyun.com
可用的证书和私钥,需要将密钥命名为aliyun.com.key
,证书命名为aliyun.com.crt
。如果没有,可以通过openssl执行以下步骤来生成证书和密钥。
- 执行以下命令,创建根证书和私钥。
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=myexample Inc./CN=aliyun.com' -keyout aliyun.root.key -out aliyun.root.crt
- 执行以下命令,为
aliyun.com
服务器生成证书和私钥。openssl req -out aliyun.com.csr -newkey rsa:2048 -nodes -keyout aliyun.com.key -subj "/CN=aliyun.com/O=myexample organization"
openssl x509 -req -days 365 -CA aliyun.root.crt -CAkey aliyun.root.key -set_serial 0 -in aliyun.com.csr -out aliyun.com.crt
- 通过以下命令(注意是在入口网关Pod所在的集群对应的kubeconfig环境下)将在istio-system命名空间中创建包含证书和私钥的Secret。
kubectl create -n istio-system secret tls myexample-credential --key=aliyun.com.key --cert=aliyun.com.crt
注意 Secret名称不应以istio或prometheus开头,且不应包含token字段。
步骤二:为a.aliyun.com定义内部服务
示例中的内部服务是基于Nginx实现的,首先为Nginx服务器创建配置文件。以域名a.aliyun.com
的内部服务为例,定义请求根路径直接返回字样Welcome to a.aliyun.com!及状态码200。myexample-nginx.conf的具体内容如下。
events {
}
http {
log_format main '$remote_addr - $remote_user [$time_local] $status '
'"$request" $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
server {
listen 80;
location /hello {
return 200 'Welcome to a.aliyun.com!';
add_header Content-Type text/plain;
}
}
}
- 在入口网关Pod所在的集群对应的kubeconfig环境下,执行以下命令,创建Kubernetes ConfigMap,即存储Nginx服务器的配置。
kubectl create configmap myexample-nginx-configmap --from-file=nginx.conf=./myexample-nginx.conf
- 在ASM控制台选中对应的服务网格实例,左侧导航栏选中命名空间,设置命名空间default启用sidecar自动注入。
- 创建并拷贝以下内容到myexampleapp.yaml文件中,并执行
kubectl apply -f myexampleapp.yaml
命令,创建域名a.aliyun.com的内部服务。apiVersion: v1
kind: Service
metadata:
name: myexampleapp
labels:
app: myexampleapp
spec:
ports:
- port: 80
protocol: TCP
selector:
app: myexampleapp
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myexampleapp
spec:
selector:
matchLabels:
app: myexampleapp
replicas: 1
template:
metadata:
labels:
app: myexampleapp
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx
readOnly: true
volumes:
- name: nginx-config
configMap:
name: myexample-nginx-configmap
步骤三:为b.aliyun.com定义内部服务
本示例中的内部服务是基于httpbin实现的,创建并拷贝如下内容到httpbin.example.yaml文件中,并执行kubectl apply -f httpbin.example.yaml
命令(注意是在入口网关Pod所在的集群对应的kubeconfig环境下),创建域名b.aliyun.com
的内部服务。
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
spec:
ports:
- name: http
port: 8000
selector:
app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
containers:
- image: docker.io/citizenstig/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
ports:
- containerPort: 8000
步骤四:创建自定义网关配置对象
- 登录ASM控制台。
- 在左侧导航栏,选择。
- 在网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理。
- 在网格详情页面左侧导航栏选择,然后在右侧页面单击使用YAML创建。
- 按以下步骤定义服务网关,然后单击创建。
- 选择相应的命名空间。本文以选择default命名空间为例。
- 在文本框中,定义服务网关。可参考以下YAML定义:
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: mysdsgateway
spec:
selector:
istio: ingressgateway # use istio default ingress gateway
servers:
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
credentialName: myexample-credential # must be the same as secret
hosts:
- '*.aliyun.com'
在网关规则页面可以看到新建的网关。
步骤五:创建虚拟服务
- 登录ASM控制台。
- 在左侧导航栏,选择。
- 在网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理。
- 在网格详情页面左侧导航栏选择,然后在右侧页面单击使用YAML创建。
- 按以下步骤定义虚拟服务,然后单击创建。
- 选择相应的命名空间。本文以选择default命名空间为例。
- 在文本框中,定义Istio虚拟服务。可参考以下YAML定义。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: mysdsgateway-myexamplevs
spec:
hosts:
- "a.aliyun.com"
gateways:
- mysdsgateway
http:
- match:
- uri:
exact: /hello
route:
- destination:
host: myexampleapp.default.svc.cluster.local
port:
number: 80
同样地,为httpbin.example.com提供定义相应的虚拟服务。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: mysdsgateway-httpbinvs
spec:
hosts:
- "b.aliyun.com"
gateways:
- mysdsgateway
http:
- match:
- uri:
prefix: /status
- uri:
prefix: /delay
route:
- destination:
port:
number: 8000
host: httpbin.default.svc.cluster.local
在虚拟服务页面可以看到新建的虚拟服务。
执行结果
可以通过以下任意一种方法获取入口网关服务的地址:
- 通过ASM控制台查看,在控制台选中对应的服务网格实例,左侧导航栏选中ASM网关, 在右侧页面中查看对应的信息。
- 通过容器服务控制台查看,请参见查看入口网关的服务信息。
可以通过以下命令访问入口网关服务:
- 执行以下命令,通过HTTPS协议访问aliyun.com服务。
curl -k -H Host:a.aliyun.com --resolve a.aliyun.com:443:{替换成真实的入口网关IP地址} https://a.aliyun.com/hello
预期输出:
Welcome to aliyun.com!
- 执行以下命令,通过HTTPS协议访问httpbin.example.com服务。
curl -k -H Host:b.aliyun.com --resolve b.aliyun.com:443:{替换成真实的入口网关IP地址} https://b.aliyun.com/status/418
预期输出:
-=[ teapot ]=-
_...._
.' _ _ `.
| ."` ^ `". _,
\_;`"---"`|//
| ;/
\_ _/
`"""`
更新网关证书
如果您重建了挂载证书的数据面Secret,需要手动将网关中credentialName的参数值替换成新的Secret名称,才能更新网关证书。本文以为example.com服务器创建名为new-istio-ingressgateway-certs的Secret为例。
- 创建颁发者为myexample的证书。
- 在OpenSSL工具中执行以下命令,创建根证书和私钥。
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=myexample Inc./CN=example.com' -keyout example.root.key -out example.root.crt
- 执行以下命令,为example.com服务器生成证书和私钥。
openssl req -out example.com.csr -newkey rsa:2048 -nodes -keyout example.com.key -subj "/CN=example.com/O=myexample organization"
openssl x509 -req -days 365 -CA example.root.crt -CAkey example.root.key -set_serial 0 -in example.com.csr -out example.com.crt
- 通过kubectl工具连接集群。
- 执行以下命令,创建名为new-istio-ingressgateway-certs的Secret。
kubectl create -n istio-system secret tls new-istio-ingressgateway-certs --key example.com.key --cert example.com.crt
- 在集群中执行以下命令,删除旧的证书Secret。
kubectl delete secret istio-ingressgateway-certs -n istio-system
- 更新网关规则。
- 登录ASM控制台。
- 在左侧导航栏,选择。
- 在网格管理页面,找到待配置的实例,单击实例的名称或在操作列中单击管理。
- 在网格详情页面左侧导航栏选择。
- 在网关规则页面单击目标规则右侧操作列下的YAML。
- 在编辑面板修改credentialName参数值为新的证书Secret名称new-istio-ingressgateway-certs,然后单击确定。
- 验证更新网关证书是否成功。
- 执行以下命令,在集群中查看当前证书信息。
kubectl exec istio-ingressgateway-xxxx -n istio-system -- curl localhost:15000/config_dump > ingressgateway_dump.yaml
- 执行以下命令,打印并搜索new-istio-ingressgateway-certs证书。
cat ingressgateway_dump.yaml | grep new-istio-ingressgateway-certs -A 3
预期可以看到以下内容:

复制以上inline_bytes参数后的内容,获取Base64编码后的证书。
- 在本地命令行工具中执行以下命令,将证书进行Base64解码。
echo <Base64编码后的证书内容> | base64 --decode
- 将Base64解码内容保存为test.com.crt文件。
- 在OpenSSL工具中执行以下命令,查看证书的组织。
openssl x509 -in test.com.crt -text -noout
预期输出:

可以看到组织成功更换为myexample,说明网关证书更新成功。