Istio流量管理之安全Gateway

上文“Istio流量管理之Ingress Gateway”介绍了如何使用Gateway将一个7层HTTP服务暴露给外部使用。本文将介绍如何为Gateway配置单向或双向TLS从而暴露一个安全的HTTPS服务给外部访问。关于Istio安装等环境准备,请参阅“Istio安装使用”

1 部署httpbin

使用Istio安装目录自带的配置文件将httpbin部署至istio-demo namespace。

$ cd /usr/local/istio-1.8.1
$ kubectl apply -n istio-demo -f samples/httpbin/httpbin.yaml

2 生成证书及私钥

使用openssl生成用于为服务签发证书的根证书及私钥,如下命令执行后会生成两个文件(example.com.crtexample.com.key)。

$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt

httpbin.example.com生成证书及私钥,如下命令执行后会生成三个文件(httpbin.example.com.csrhttpbin.example.com.keyhttpbin.example.com.crt)。

$ openssl req -out httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
$ openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in httpbin.example.com.csr -out httpbin.example.com.crt

3 配置TLS Ingress Gateway

使用第2步生成的私钥及证书为Ingress Gateway创建secret。

$ kubectl create -n istio-system secret tls httpbin-credential --key=httpbin.example.com.key --cert=httpbin.example.com.crt

应用Gateway配置,端口为443,hosts为httpbin.example.com,开启TLS SIMPLE模式,并配置credentialName为刚刚创建的secret名称。

$ kubectl apply -n istio-demo -f - <<EOF
heredoc> 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
heredoc> EOF

使用Virtual Service为httpbin配置Gateway路由规则。

$ kubectl apply -n istio-demo -f - <<EOF 
heredoc> 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
heredoc> EOF

使用curl对httpbin发送https请求(本文使用Docker Desktop Kubernetes环境,INGRESS_HOST为127.0.0.1,SECURE_INGRESS_PORT为443),成功返回“418 I’m a Teapot”。

$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
--cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"

...
    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`

4 为多Host配置TLS Gateway

上面配置的Gateway仅支持一组Host的TLS访问。下面再部署一个helloworld-v1服务,然后配置Ingress Gateway,让其同时支持httpbin.example.comhelloworld-v1.example.com两个Host的TLS访问。

部署helloworld-v1样例。

$ kubectl apply -n istio-demo -f - <<EOF
heredoc> >....                                                                             
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
heredoc> EOF

helloworld-v1.example.com生成证书及私钥。

$ openssl req -out helloworld-v1.example.com.csr -newkey rsa:2048 -nodes -keyout helloworld-v1.example.com.key -subj "/CN=helloworld-v1.example.com/O=helloworld organization"
$ openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in helloworld-v1.example.com.csr -out helloworld-v1.example.com.crt

为Ingress Gateway创建secret helloworld-credential

$ kubectl create -n istio-system secret tls helloworld-credential --key=helloworld-v1.example.com.key --cert=helloworld-v1.example.com.crt

修改Gateway配置,增加对helloworld-v1.example.com的TLS访问支持。

$ kubectl apply -n istio-demo -f - <<EOF
heredoc> >....                                                                             
  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
heredoc> EOF

使用Virtual Service为Gateway配置路由规则。

$ kubectl apply -n istio-demo -f - <<EOF
heredoc> 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
heredoc> EOF

然后,使用curl对helloworld-v1发起https请求,发现成功返回200状态码。

$ curl -v -HHost:helloworld-v1.example.com --resolve "helloworld-v1.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
--cacert example.com.crt "https://helloworld-v1.example.com:$SECURE_INGRESS_PORT/hello"

再次使用刚刚的命令对httpbin发起https请求,同样成功返回结果。说明Gateway同时支持两组Host的TLS访问。

$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
--cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"

...
    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`

5 配置双向TLS Ingress Gateway

为使Gateway支持双向TLS通信,须将原有secret删除,创建新的secret,并将用于校验客户端的根证书囊括进来。

$ kubectl -n istio-system delete secret httpbin-credential
$ kubectl create -n istio-system secret generic httpbin-credential --from-file=tls.key=httpbin.example.com.key --from-file=tls.crt=httpbin.example.com.crt --from-file=ca.crt=example.com.crt

更新Gateway配置,为httpbin开启双向TLS模式。

$ kubectl apply -n istio-demo -f - <<EOF
heredoc> 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
heredoc> EOF

配置生效后,之前请求httpbin的方式就不好使了。

下面使用如下命令尝试为客户端创建证书及私钥。

$ openssl req -out client.example.com.csr -newkey rsa:2048 -nodes -keyout client.example.com.key -subj "/CN=client.example.com/O=client organization"
$ openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in client.example.com.csr -out client.example.com.crt

使用--cert--key选项将客户端证书及私钥传入后,再次使用https方式请求httpbin,这时返回成功,

$ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
--cacert example.com.crt --cert client.example.com.crt --key client.example.com.key \
"https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"

...
    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`

6 环境清理

测试结束,使用如下命令删除Gateway,Virtual Service及Secret。

$ kubectl delete gateway mygateway -n istio-demo
$ kubectl delete virtualservice httpbin helloworld-v1 -n istio-demo
$ kubectl delete --ignore-not-found=true -n istio-system secret httpbin-credential helloworld-credential

使用如下命令卸载httpbin及helloworld-v1服务。

$ kubectl delete deploy --ignore-not-found=true httpbin helloworld-v1 -n istio-demo
$ kubectl delete svc --ignore-not-found=true httpbin helloworld-v1 -n istio-demo

总结本文,首先介绍了Istio Ingress Gateway支持简单及双向TLS访问;然后使用httpbin样例测试了简单TLS访问;引入helloworld-v1样例测试了多Host TLS访问;最后使用httpbin样例测试了双向TLS访问。

参考资料

[1] Istio Secure Gateways

[2] Istio Traffic Management

若我的文章对您有帮助,欢迎小额打赏,以支持我更好的写作,Thanks!
微信 支付宝