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_root2_intermediate3_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.comhelloworld-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-credentialhelloworld-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/

posted @ 2021-08-31 15:28  fat_girl_spring  阅读(863)  评论(0编辑  收藏  举报