istio-ingress网关安全-为 Gateway 提供 HTTPS 加密支持
为服务器和客户端生成证书
可以使用各种常用工具来生成证书和私钥。这个例子中用了一个来自 https://github.com/nicholasjackson/mtls-go-example 的脚本来完成工作。
1、克隆示例代码库:
git clone https://github.com/nicholasjackson/mtls-go-example
2、进入代码库文件夹
pushd mtls-go-example
3、为 httpbin.example.com
生成证书。注意要把下面命令中的 password
替换为其它值。
./generate.sh httpbin.example.com <password>
看到提示后,所有问题都输入 Y
即可。这个命令会生成四个目录:1_root
、2_intermediate
、3_application
以及 4_client
。这些目录中包含了后续过程所需的客户端和服务端证书。
4、把证书移动到 httpbin.example.com
目录之中:
mkdir ../httpbin.example.com && mv 1_root 2_intermediate 3_application 4_client ../httpbin.example.com
为单一主机配置 TLS Ingress Gateway
1、启动 httpbin
样例:
cat <<EOF | kubectl apply -f - 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 EOF
2、为 Ingress Gateway 创建 Secret:
kubectl create -n istio-system secret generic httpbin-credential \ --from-file=key=httpbin.example.com/3_application/private/httpbin.example.com.key.pem \ --from-file=cert=httpbin.example.com/3_application/certs/httpbin.example.com.cert.pem
secret name 不能 以 istio
或者 prometheus
为开头, 且 secret 不能 包含 token
字段。
3、创建一个 Gateway ,其 servers:
字段的端口为 443,设置 credentialName
的值为 httpbin-credential
。这个值就是 Secret
的名字。TLS 模式设置为 SIMPLE
。
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: mygateway spec: selector: istio: ingressgateway # use istio default ingress gateway servers: - port: number: 443 name: https protocol: HTTPS tls: mode: SIMPLE credentialName: "httpbin-credential" # must be the same as secret hosts: - "httpbin.example.com" EOF
4、配置 Gateway 的 Ingress 流量路由,并配置对应的 VirtualService
:
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: httpbin spec: hosts: - "httpbin.example.com" gateways: - mygateway http: - match: - uri: prefix: /status - uri: prefix: /delay route: - destination: port: number: 8000 host: httpbin EOF
5、用 HTTPS 协议访问 httpbin
服务:
curl -v -HHost:httpbin.example.com \ --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST \ --cacert httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem \ https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418
httpbin
服务会返回 418 I’m a Teapot。
6、删除 Gateway 的 Secret
,并新建另外一个,然后修改 Ingress Gateway 的凭据:
kubectl -n istio-system delete secret httpbin-credential pushd mtls-go-example ./generate.sh httpbin.example.com <password> mkdir ../httpbin.new.example.com && mv 1_root 2_intermediate 3_application 4_client ../httpbin.new.example.com kubectl create -n istio-system secret generic httpbin-credential \ --from-file=key=httpbin.new.example.com/3_application/private/httpbin.example.com.key.pem \ --from-file=cert=httpbin.new.example.com/3_application/certs/httpbin.example.com.cert.pem
7、使用 curl
访问 httpbin
服务:
curl -v -HHost:httpbin.example.com \ --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST \ --cacert httpbin.new.example.com/2_intermediate/certs/ca-chain.cert.pem \ https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418 ... HTTP/2 418 ... -=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/ `"""`
8、如果尝试使用之前的证书链来再次访问 httpbin
,就会得到失败的结果:
curl -v -HHost:httpbin.example.com \ --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST \ --cacert httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem \ https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418 ... * TLSv1.2 (OUT), TLS handshake, Client hello (1): * TLSv1.2 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (OUT), TLS alert, Server hello (2): * SSL certificate problem: unable to get local issuer certificate
为 TLS Ingress Gateway 配置多个主机名
可以把多个主机名配置到同一个 Ingress Gateway 上,例如 httpbin.example.com
和 helloworld-v1.example.com
。Ingress Gateway 会为每个 credentialName
获取一个唯一的凭据。
1、要恢复 “httpbin” 的凭据,请删除对应的 secret 并重新创建。
kubectl -n istio-system delete secret httpbin-credential kubectl create -n istio-system secret generic httpbin-credential \ --from-file=key=httpbin.example.com/3_application/private/httpbin.example.com.key.pem \ --from-file=cert=httpbin.example.com/3_application/certs/httpbin.example.com.cert.pem
2、启动 hellowworld-v1
示例:
cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Service metadata: name: helloworld-v1 labels: app: helloworld-v1 spec: ports: - name: http port: 5000 selector: app: helloworld-v1 --- apiVersion: apps/v1 kind: Deployment metadata: name: helloworld-v1 spec: replicas: 1 selector: matchLabels: app: helloworld-v1 version: v1 template: metadata: labels: app: helloworld-v1 version: v1 spec: containers: - name: helloworld image: istio/examples-helloworld-v1 resources: requests: cpu: "100m" imagePullPolicy: IfNotPresent #Always ports: - containerPort: 5000 EOF
3、为 Ingress Gateway 创建一个 Secret。如果已经创建了 httpbin-credential
,就可以创建 helloworld-credential
Secret 了。
./generate.sh helloworld-v1.example.com <password> mkdir ../helloworld-v1.example.com && mv 1_root 2_intermediate 3_application 4_client ../helloworld-v1.example.com kubectl create -n istio-system secret generic helloworld-credential \ --from-file=key=helloworld-v1.example.com/3_application/private/helloworld-v1.example.com.key.pem \ --from-file=cert=helloworld-v1.example.com/3_application/certs/helloworld-v1.example.com.cert.pem
4、定义一个 Gateway ,其中包含了两个 server
,都开放了 443 端口。两个 credentialName
字段分别赋值为 httpbin-credential
和 helloworld-credential
。设置 TLS 的 mode 为 SIMPLE
。
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: mygateway spec: selector: istio: ingressgateway # use istio default ingress gateway servers: - port: number: 443 name: https-httpbin protocol: HTTPS tls: mode: SIMPLE credentialName: "httpbin-credential" hosts: - "httpbin.example.com" - port: number: 443 name: https-helloworld protocol: HTTPS tls: mode: SIMPLE credentialName: "helloworld-credential" hosts: - "helloworld-v1.example.com" EOF
5、配置 Gateway 的流量路由,配置 VirtualService
:
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: helloworld-v1 spec: hosts: - "helloworld-v1.example.com" gateways: - mygateway http: - match: - uri: exact: /hello route: - destination: host: helloworld-v1 port: number: 5000 EOF
6、向 helloworld-v1.example.com
发送 HTTPS 请求:
curl -v -HHost:helloworld-v1.example.com \ --resolve helloworld-v1.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST \ --cacert helloworld-v1.example.com/2_intermediate/certs/ca-chain.cert.pem \ https://helloworld-v1.example.com:$SECURE_INGRESS_PORT/hello HTTP/2 200
7、发送 HTTPS 请求到 httpbin.example.com
,还是会看到茶壶:
curl -v -HHost:httpbin.example.com \ --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST \ --cacert httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem \ https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418 -=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/ `"""`
配置双向 TLS Ingress Gateway
可以对 Gateway 的定义进行扩展,加入双向 TLS 的支持。要修改 Ingress Gateway 的凭据,就要删除并重建对应的 Secret
。服务器会使用 CA 证书对客户端进行校验,因此需要使用 cacert
字段来保存 CA 证书:
kubectl -n istio-system delete secret httpbin-credential kubectl create -n istio-system secret generic httpbin-credential \ --from-file=key=httpbin.example.com/3_application/private/httpbin.example.com.key.pem \ --from-file=cert=httpbin.example.com/3_application/certs/httpbin.example.com.cert.pem \ --from-file=cacert=httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem
1、修改 Gateway 定义,设置 TLS 的模式为 MUTUAL
:
cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: mygateway spec: selector: istio: ingressgateway # use istio default ingress gateway servers: - port: number: 443 name: https protocol: HTTPS tls: mode: MUTUAL credentialName: "httpbin-credential" # must be the same as secret hosts: - "httpbin.example.com" EOF
2、使用前面的方式尝试发出 HTTPS 请求,会看到失败的过程:
curl -v -HHost:httpbin.example.com \ --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST \ --cacert httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem \ https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418 * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): * TLSv1.3 (IN), TLS handshake, Request CERT (13): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.3 (OUT), TLS handshake, Certificate (11): * TLSv1.3 (OUT), TLS handshake, Finished (20): * TLSv1.3 (IN), TLS alert, unknown (628): * OpenSSL SSL_read: error:1409445C:SSL routines:ssl3_read_bytes:tlsv13 alert certificate required, errno 0
3、在 curl
命令中加入客户端证书和私钥的参数,重新发送请求。(客户端证书参数为 --cert
,私钥参数为 --key
)
curl -v -HHost:httpbin.example.com \ --resolve httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST \ --cacert httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem \ --cert httpbin.example.com/4_client/certs/httpbin.example.com.cert.pem \ --key httpbin.example.com/4_client/private/httpbin.example.com.key.pem \ https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418 -=[ teapot ]=- _...._ .' _ _ `. | ."` ^ `". _, \_;`"---"`|// | ;/ \_ _/
4、
如果不想用 httpbin-credential
secret 来存储所有的凭据, 可以创建两个单独的 secret :
httpbin-credential
用来存储服务器的秘钥和证书httpbin-credential-cacert
用来存储客户端的 CA 证书且一定要有-cacert
后缀
使用以下命令创建两个单独的 secret :
kubectl -n istio-system delete secret httpbin-credential kubectl create -n istio-system secret generic httpbin-credential \ --from-file=key=httpbin.example.com/3_application/private/httpbin.example.com.key.pem \ --from-file=cert=httpbin.example.com/3_application/certs/httpbin.example.com.cert.pem kubectl create -n istio-system secret generic httpbin-credential-cacert \ --from-file=cacert=httpbin.example.com/2_intermediate/certs/ca-chain.cert.pem
参考:
https://preliminary.istio.io/latest/zh/docs/tasks/traffic-management/ingress/secure-ingress-sds/