Istio的流量管理(实操二)

Istio的流量管理(实操二)

涵盖官方文档Traffic Management章节中的inrgess部分。

Ingress网关

在kubernetes环境中,kubernetes ingress资源用于指定暴露到集群外的服务。在istio服务网格中,使用了一种不同的配置模型,称为istio网关。一个网关允许将istio的特性,如镜像和路由规则应用到进入集群的流量上。

本节描述了如何使用istio网关将一个服务暴露到服务网格外。

环境准备

使用如下命令创建httpbin服务

$ kubectl apply -f samples/httpbin/httpbin.yaml

确定ingress的IP和端口

由于本环境中没有配置对外的负载均衡,因此此处的EXTERNAL-IP为空,使用node port进行访问

# kubectl get svc istio-ingressgateway -n istio-system
NAME                    TYPE           CLUSTER-IP    EXTERNAL-IP  PORT(S)    AGE
istio-ingressgateway   LoadBalancer   10.84.93.45   <pending>     ...        11d

获取ingressgateway service的http2https对应的端口

$ export INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
$ export SECURE_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
$ export TCP_INGRESS_PORT=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="tcp")].nodePort}')

下面是istio-system命名空间的istio-ingressgateway service中的一部分端口信息,可以看到http2https的nodeport分别为3119431785,对应上面的INGRESS_PORTSECURE_INGRESS_PORT

{
    "name": "http2","nodePort": 31194,"port": 80,"protocol": "TCP","targetPort": 80
},{
    "name": "https","nodePort": 31785,"port": 443,"targetPort": 443
},

获取istio-system命名空间中ingressgateway pod 的hostIP

$ export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].status.hostIP}')

使用istio网关配置ingress

一个ingress网关描述了在网格边缘用于接收入站HTTP/TCP连接的负载均衡,配置了暴露的端口,协议等。kubernetes ingress资源不包括任何流量路由配置。ingress 流量的路由使用istio路由规则,与内部服务请求相同:

  1. 创建istio Gateway,网关监听地址为httpbin.example.com,端口为80(位于默认的ingressgatewaypod中)

    kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: httpbin-gateway
    spec:
      selector:
        istio: ingressgateway # use Istio default gateway implementation
      servers:
      - port:
          number: 80 #gateway暴露80端口
          name: http
          protocol: HTTP
        hosts: #gateway暴露的主机名
        - "httpbin.example.com"
    EOF
    
  2. 通过Gateway配置进入的流量路由,将URI为httpbin.example.com,且目的地为/status/delay的请求分发到httpbin服务的8000端口,其他请求会返回404响应。

    kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: httpbin
    spec:
      hosts: #virtual service的hosts字段与Gateway的servers.hosts字段需要匹配
      - "httpbin.example.com" 
      gateways:
      - httpbin-gateway
      http:
      - match:
        - uri:
            prefix: /status
        - uri:
            prefix: /delay
        route:
        - destination:
            port:
              number: 8000
            host: httpbin
    EOF
    

    可以看到流量被导入了httpbin service暴露的8000端口上

    $ oc get svc |grep httpbin
    httpbin       ClusterIP      10.84.222.69   <none>    8000/TCP   19h
    

    来自网格内部其他服务的请求则不受此规则的约束,会使用默认的轮询路由进行请求分发。为了限制内部的调用规则,可以将特定的值mesh添加到gateways列表中。由于内部服务主机名(如httpbin.default.svc.cluster.local) 可能与外部不同,因此需要将主机名添加到hosts列表中。

  3. 使用curl命令访问httpbin服务,此时通过-H选项修改了HTTP请求首部的Host字段,使用http2的nodeport方式访问:

    $ curl -s -I -HHost:httpbin.example.com http://$INGRESS_HOST:$INGRESS_PORT/status/200
    HTTP/1.1 200 OK
    server: istio-envoy
    date: Thu,21 May 2020 03:22:50 GMT
    content-type: text/html; charset=utf-8
    access-control-allow-origin: *
    access-control-allow-credentials: true
    content-length: 0
    x-envoy-upstream-service-time: 21
    
  4. 访问其他URL路径则返回404错误

    $ curl -s -I -HHost:httpbin.example.com http://$INGRESS_HOST:$INGRESS_PORT/headers
    HTTP/1.1 404 Not Found
    date: Thu,21 May 2020 03:25:20 GMT
    server: istio-envoy
    transfer-encoding: chunked
    

使用浏览器访问ingress服务

由于无法像使用curl一样修改请求的Host首部字段,因此无法使用浏览器访问httpbin服务。为了解决这个问题,可以在GatewayVirtualService中的host字段使用通配符*

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: httpbin-gateway
spec:
  selector:
    istio: ingressgateway # use Istio default gateway implementation
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "*"  #指定通配符,监听所有流量,不限制外部流量的地址
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "*"
  gateways:
  - httpbin-gateway
  http:
  - match:
    - uri:
        prefix: /headers
    route:
    - destination:
        port:
          number: 8000
        host: httpbin
EOF

问题定位

  1. 检查环境变量INGRESS_HOSTINGRESS_PORT,保证这两个值是有效的

    $ kubectl get svc -n istio-system
    $ echo INGRESS_HOST=$INGRESS_HOST,INGRESS_PORT=$INGRESS_PORT
    
  2. 校验相同的端口上没有其他istio ingress网格

    $ kubectl get gateway --all-namespaces
    
  3. 校验没有在相同的IP和端口上定义kubernetes ingress资源

    $ kubectl get ingress --all-namespaces
    
  4. 如果没有负载均衡,可以参照上面步骤使用node port方式

卸载

$ kubectl delete gateway httpbin-gateway
$ kubectl delete virtualservice httpbin
$ kubectl delete --ignore-not-found=true -f samples/httpbin/httpbin.yaml

Ingress(kubernetes)

执行ingress流量控制中的Before you beginDetermining the ingress IP and ports小节的操作,部署httpbin服务。本节介绍如何通过kubernetes的Ingress(非istio的gateway)进行访问。

下面展示如何配置一个80端口的Ingress,用于HTTP流量:

  1. 创建一个istio Gateway,将来自httpbin.example.com:80/status/*的流量分发到service httpbin8000端口

    $ kubectl apply -f - <<EOF
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    metadata:
      annotations:
        kubernetes.io/ingress.class: istio
      name: ingress
    spec:
      rules:
      - host: httpbin.example.com
        http:
          paths:
          - path: /status/*
            backend:
              serviceName: httpbin
              servicePort: 8000
    EOF
    

    注意需要使用 kubernetes.io/ingress.class annotation来告诉istio网关控制器处理该ingress,否则会忽略该ingress。

  2. 使用curl命令访问httpbin服务。Ingress的流量也需要经过istio ingressgateway

    $ curl -I -HHost:httpbin.example.com http://$INGRESS_HOST:$INGRESS_PORT/status/200
    HTTP/1.1 200 OK
    server: istio-envoy
    date: Fri,22 May 2020 06:12:56 GMT
    content-type: text/html; charset=utf-8
    access-control-allow-origin: *
    access-control-allow-credentials: true
    content-length: 0
    x-envoy-upstream-service-time: 20
    

    httpbin的服务发现是通过EDS实现的,使用如下命令查看:

    $ istioctl proxy-config cluster istio-ingressgateway-569669bb67-b6p5r|grep 8000
    httpbin.default.svc.cluster.local                   8000    -    outbound      EDS
    outbound_.8000_._.httpbin.default.svc.cluster.local   -     -       -          EDS
    
  3. 访问其他未暴露的路径,返回HTTP 404错误:

    $ curl -I -HHost:httpbin.example.com http://$INGRESS_HOST:$INGRESS_PORT/headers
    HTTP/1.1 404 Not Found
    date: Fri,22 May 2020 06:24:30 GMT
    server: istio-envoy
    transfer-encoding: chunked
    

下一步

TLS

Ingress支持TLS设置。Istio也支持TLS设置,但相关的secret必须存在istio-ingressgateway deployment所在的命名空间中。可以使用cert-manager生成这些证书。

指定路径类型

默认情况下,Istio会将路径视为完全匹配,如果路径使用/**结尾,则该路径为前缀匹配。不支持其他正则匹配。

kubernetes 1.18中新增了一个字段pathType,允许声明为ExactPrefix

指定IngressClass

kubernetes 1.18中新增了一个资源IngressClass,替换了Ingress资源的 kubernetes.io/ingress.class annotation。如果使用该资源,则需要将controller设置为istio.io/ingress-controller

apiVersion: networking.k8s.io/v1beta1
kind: IngressClass
metadata:
  name: istio
spec:
  controller: istio.io/ingress-controller
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress
spec:
  ingressClassName: istio
  ...

卸载

$ kubectl delete ingress ingress
$ kubectl delete --ignore-not-found=true -f samples/httpbin/httpbin.yaml

安全网关

本节讲述如何使用simple或mutual TLS暴露安全的HTTPS服务。证书是通过SDS进行密钥发现的。

TLS需要的私钥,服务端证书,根证书是使用基于文件装载的方法配置的。

执行ingress流量控制中的Before you beginDetermining the ingress IP and ports小节的操作,部署httpbin服务,并获取 INGRESS_HOSTSECURE_INGRESS_PORT变量。

生成服务端证书和私钥

下面使用openssl生成需要的证书和密钥

  1. 生成一个根证书和一个私钥,用于签名服务的证书

    $ 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
    
  2. httpbin.example.com生成一个证书和私钥

    $ 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
    

单主机配置TLS ingress网关

  1. 为ingree网关创建一个secret

    secret的名字不能以istioprometheus开头,且secret不应该包含token字段

    $ kubectl create -n istio-system secret tls httpbin-credential --key=httpbin.example.com.key --cert=httpbin.example.com.crt
    
  2. server部分定义一个443端口的Gateway,将credentialName指定为httpbin-credential,与secret的名字相同,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
          protocol: HTTPS
        tls: #对暴露的服务使用SIMPLE模式的tls,即单向tls验证
          mode: SIMPLE
          credentialName: httpbin-credential # must be the same as secret
        hosts:
        - httpbin.example.com
    EOF
    
  3. 配置进入Gateway的流量路由。与上一节中的VirtualService相同

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: httpbin
    spec:
      hosts:
      - "httpbin.example.com"
      gateways:
      - httpbin-gateway
      http:
      - match:
        - uri:
            prefix: /status
        - uri:
            prefix: /delay
        route:
        - destination:
            port:
              number: 8000  #可以看到tls只是这是在gateway上的,当进入网格之后就不需要了
            host: httpbin
    EOF
    
  4. 使用curl向SECURE_INGRESS_PORT发送HTTPS请求访问httpbin服务,请求中携带了公钥example.com.crt--resolve标记可以在使用curl访问TLS的网关IP时,在SNI中支持httpbin.example.com--cacert选择支持使用生成的证书校验服务。

    -HHost:httpbin.example.com 选项仅在SECURE_INGRESS_PORT不同于实际的网关端口(443)时才会需要,例如,通过映射的NodePort方式访问服务时。

    通过将请求发送到/status/418 URL路径时,可以看到httpbin确实被访问了,httpbin服务会返回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"
    
    > --cacert example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    * Added httpbin.example.com:31967:172.20.127.211 to DNS cache
    * About to connect() to httpbin.example.com port 31967 (#0)
    *   Trying 172.20.127.211...
    * Connected to httpbin.example.com (172.20.127.211) port 31967 (#0)
    * Initializing NSS with certpath: sql:/etc/pki/nssdb
    *   CAfile: example.com.crt
      CApath: none
    * SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    * Server certificate:
    *       subject: O=httpbin organization,CN=httpbin.example.com
    *       start date: May 22 09:03:01 2020 GMT
    *       expire date: May 22 09:03:01 2021 GMT
    *       common name: httpbin.example.com
    *       issuer: CN=example.com,O=example Inc.
    > GET /status/418 HTTP/1.1
    > User-Agent: curl/7.29.0
    > Accept: */*
    > Host:httpbin.example.com
    >
    < HTTP/1.1 418 Unknown
    < server: istio-envoy
    < date: Fri,22 May 2020 09:08:29 GMT
    < x-more-info: http://tools.ietf.org/html/rfc2324
    < access-control-allow-origin: *
    < access-control-allow-credentials: true
    < content-length: 135
    < x-envoy-upstream-service-time: 2
    <
    
        -=[ teapot ]=-
    
           _...._
         .'  _ _ `.
        | ."` ^ `". _,\_;`"---"`|//
          |       ;/
          \_     _/
            `"""`
    * Connection #0 to host httpbin.example.com left intact
    

    查看curl输出中的Server certificate中的信息,上述返回值的最后有一个茶壶的图片,说明运行成功。

  5. 删除老的网关secret,创建一个新的secret,并使用该secret修改ingress网关的凭据

    $ kubectl -n istio-system delete secret httpbin-credential
    
    $ mkdir new_certificates
    $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout new_certificates/example.com.key -out new_certificates/example.com.crt
    $ openssl req -out new_certificates/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout new_certificates/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
    $ openssl x509 -req -days 365 -CA new_certificates/example.com.crt -CAkey new_certificates/example.com.key -set_serial 0 -in new_certificates/httpbin.example.com.csr -out new_certificates/httpbin.example.com.crt
    $ kubectl create -n istio-system secret tls httpbin-credential \
    --key=new_certificates/httpbin.example.com.key \
    --cert=new_certificates/httpbin.example.com.crt
    
  6. 使用新的证书链访问httpbin服务

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
    --cacert new_certificates/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    
  7. 如果使用老的证书访问,则返回错误

    $ 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"
    * Added httpbin.example.com:31967:172.20.127.211 to DNS cache
    * About to connect() to httpbin.example.com port 31967 (#0)
    *   Trying 172.20.127.211...
    * Connected to httpbin.example.com (172.20.127.211) port 31967 (#0)
    * Initializing NSS with certpath: sql:/etc/pki/nssdb
    *   CAfile: example.com.crt
      CApath: none
    * Server certificate:
    *       subject: O=httpbin organization,CN=httpbin.example.com
    *       start date: May 22 09:24:07 2020 GMT
    *       expire date: May 22 09:24:07 2021 GMT
    *       common name: httpbin.example.com
    *       issuer: CN=example.com,O=example Inc.
    * NSS error -8182 (SEC_ERROR_BAD_SIGNATURE)
    * Peer's certificate has an invalid signature.
    * Closing connection 0
    curl: (60) Peer's certificate has an invalid signature.
    

多主机配置TLS ingress网关

本节会为多个主机(httpbin.example.comhelloworld-v1.example.com)配置一个ingress网关。ingress网关会在credentialName中查找唯一的凭据。

  1. 删除之前创建的secret并为httpbin重建凭据

    $ kubectl -n istio-system delete secret httpbin-credential
    $ kubectl create -n istio-system secret tls httpbin-credential \
    --key=httpbin.example.com.key \
    --cert=httpbin.example.com.crt
    
  2. 启动helloworld-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. 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
    
  4. helloworld-credential创建secret

    $ kubectl create -n istio-system secret tls helloworld-credential --key=helloworld-v1.example.com.key --cert=helloworld-v1.example.com.crt
    
  5. 定义一个网关,网关端口为443。在credentialName字段分别设置httpbin-credentialhelloworld-credential,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-httpbin #httpbin的gateway配置
          protocol: HTTPS
        tls:
          mode: SIMPLE
          credentialName: httpbin-credential
        hosts:
        - httpbin.example.com
      - port:
          number: 443
          name: https-helloworld #https-helloword的gateway配置
          protocol: HTTPS
        tls:
          mode: SIMPLE
          credentialName: helloworld-credential
        hosts:
        - helloworld-v1.example.com
    EOF
    
  6. 配置gateway的流量路由,为新应用添加对应的virtual service

    $ 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
    
  7. helloworld-v1.example.com发送HTTPS请求

    $ 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"
    
    ...
    < HTTP/1.1 200 OK
    < content-type: text/html; charset=utf-8
    < content-length: 60
    < server: istio-envoy
    < date: Sat,23 May 2020 07:38:40 GMT
    < x-envoy-upstream-service-time: 143
    <
    Hello version: v1,instance: helloworld-v1-5dfcf5d5cd-2l44c
    * Connection #0 to host helloworld-v1.example.com left intact
    
  8. httpbin.example.com发送请求

    $ 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 ]=-
    
           _...._
         .'  _ _ `.
        | ."` ^ `". _,\_;`"---"`|//
          |       ;/
          \_     _/
            `"""`
    * Connection #0 to host httpbin.example.com left intact
    

配置一个mutual TLS ingress网关

删除之前的secreting创建一个新的secret,server会使用该CA证书校验client,使用cacert保存CA证书。

$ 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
  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 #对网关暴露的服务httpbin.example.com启用双向认证
         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 example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    * Added httpbin.example.com:31967:172.20.127.211 to DNS cache
    * About to connect() to httpbin.example.com port 31967 (#0)
    *   Trying 172.20.127.211...
    * Connected to httpbin.example.com (172.20.127.211) port 31967 (#0)
    * Initializing NSS with certpath: sql:/etc/pki/nssdb
    *   CAfile: example.com.crt
      CApath: none
    * NSS: client certificate not found (nickname not specified)
    * NSS error -12227 (SSL_ERROR_HANDSHAKE_FAILURE_ALERT)
    * SSL peer was unable to negotiate an acceptable set of security parameters.
    * Closing connection 0
    curl: (35) NSS: client certificate not found (nickname not specified)
    
  3. 使用公钥example.com.crt生成client的证书和私钥。在curl中传入客户端的证书和私钥,使用--cert传入客户端证书,使用--key传入私钥

    $ 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
    
    $ 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 ]=-
    
           _...._
         .'  _ _ `.
        | ."` ^ `". _,\_;`"---"`|//
          |       ;/
          \_     _/
            `"""`
    * Connection #0 to host httpbin.example.com left intact
    

istio支持几种不同的Secret格式,来支持与多种工具的集成,如cert-manager:

  • 一个TLS Secret使用tls.keytls.crt;对于mutual TLS,会用到ca.crt
  • 一个generic Secret会用到keycert;对于mutual TLS,会用到cacert
  • 一个generic Secret会用到keycert;对于mutual TLS,会用到一个单独的名为 <secret>-cacert的generic Secret,以及一个cacert key。例如httpbin-credential 包含keycerthttpbin-credential-cacert 包含cacert

问题定位

  • 检查INGRESS_HOSTSECURE_INGRESS_PORT环境变量

    $ kubectl get svc -n istio-system
    $ echo INGRESS_HOST=$INGRESS_HOST,SECURE_INGRESS_PORT=$SECURE_INGRESS_PORT
    
  • 检查istio-ingressgateway控制器的错误日志

    $ kubectl logs -n istio-system "$(kubectl get pod -l istio=ingressgateway \
    -n istio-system -o jsonpath='{.items[0].metadata.name}')"
    
  • 校验istio-system命名空间中成功创建了secret。上例中应该存在httpbin-credentialhelloworld-credential

    $ kubectl -n istio-system get secrets
    
  • 校验ingress网关agent将密钥/证书对上传到了ingress网关

    $ kubectl logs -n istio-system "$(kubectl get pod -l istio=ingressgateway \
    -n istio-system -o jsonpath='{.items[0].metadata.name}')"
    

定位mutul TLS问题

  • 校验CA加载到了 istio-ingressgateway pod中,查看是否存在example.com.crt

    $ kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/ingressgateway-ca-certs
    
  • 如果创建了istio-ingressgateway-ca-certs secret,但没有加载CA证书,删除ingress网关pod,强制加载该证书

    $ kubectl delete pod -n istio-system -l istio=ingressgateway
    
  • 校验CA证书的Subject字段是否正确

    $ kubectl exec -i -n istio-system $(kubectl get pod -l istio=ingressgateway -n istio-system -o jsonpath='{.items[0].metadata.name}')  -- cat /etc/istio/ingressgateway-ca-certs/example.com.crt | openssl x509 -text -noout | grep 'Subject:'
            Subject: O=example Inc.,CN=example.com
    

    log中可以看到添加了httpbin-credential secret。如果使用mutual TLS,那么也会出现 httpbin-credential-cacert secret。校验log中显示了网关agent从ingress网关接收到了SDS请求,资源的名称为 httpbin-credential,且ingress网关获取到了密钥/证书对。如果使用了mutual TLS,日志应该显示将密钥/证书发送到ingress网关,网关agent接收到了带 httpbin-credential-cacert 资源名称的SDS请求,并回去到了根证书。

卸载

  1. 删除Gateway配置,VirtualService和secret

    $ kubectl delete gateway mygateway
    $ kubectl delete virtualservice httpbin
    $ kubectl delete --ignore-not-found=true -n istio-system secret httpbin-credential \
    helloworld-credential
    $ kubectl delete --ignore-not-found=true virtualservice helloworld-v1
    
  2. 删除证书目录

    $ rm -rf example.com.crt example.com.key httpbin.example.com.crt httpbin.example.com.key httpbin.example.com.csr helloworld-v1.example.com.crt helloworld-v1.example.com.key helloworld-v1.example.com.csr client.example.com.crt client.example.com.csr client.example.com.key ./new_certificates
    
  3. 停止httpbinhelloworld-v1 服务:

    $ kubectl delete deployment --ignore-not-found=true httpbin helloworld-v1
    $ kubectl delete service --ignore-not-found=true httpbin helloworld-v1
    

不终止TLS的ingress网关

上一节中描述了如何配置HTTPS ingree来访问一个HTTP服务。本节中描述如何配置HTTPS ingrss来访问HTTPS服务等。通过配置ingress网关来执行SNI方式的访问,而不会在请求进入ingress时终止TLS。

本例中使用一个NGINX服务器作为HTTPS服务。

生成客户端和服务端的证书和密钥

  1. 生成一个根证书和私钥,用于签名服务

    $ 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
    
  2. nginx.example.com创建证书和私钥

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

部署NGINX服务

  1. 创建kubernetes Secret保存服务的证书

    $ kubectl create secret tls nginx-server-certs --key nginx.example.com.key --cert nginx.example.com.crt
    
  2. 为NGINX服务创建配置文件

    $ cat <<EOF > ./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 443 ssl;
    
        root /usr/share/nginx/html;
        index index.html;
    
        server_name nginx.example.com;
        ssl_certificate /etc/nginx-server-certs/tls.crt;
        ssl_certificate_key /etc/nginx-server-certs/tls.key;
      }
    }
    EOF
    
  3. 为NGINX服务创建kubernetes configmap

    $ kubectl create configmap nginx-configmap --from-file=nginx.conf=./nginx.conf
    
  4. 部署NGINX服务

    $ cat <<EOF | istioctl kube-inject -f - | kubectl apply -f -
    apiVersion: v1
    kind: Service
    metadata:
      name: my-nginx
      labels:
        run: my-nginx
    spec:
      ports:
      - port: 443
        protocol: TCP
      selector:
        run: my-nginx
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-nginx
    spec:
      selector:
        matchLabels:
          run: my-nginx
      replicas: 1
      template:
        metadata:
          labels:
            run: my-nginx
        spec:
          containers:
          - name: my-nginx
            image: nginx
            ports:
            - containerPort: 443
            volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx
              readOnly: true
            - name: nginx-server-certs
              mountPath: /etc/nginx-server-certs
              readOnly: true
          volumes:
          - name: nginx-config
            configMap:
              name: nginx-configmap
          - name: nginx-server-certs
            secret:
              secretName: nginx-server-certs #保存了NGINX服务的证书和私钥
    EOF
    
  5. 为了测试NGINX服务部署成功,向服务发送不使用证书的方式请求,并校验打印信息是否正确:

    $ kubectl exec -it $(kubectl get pod  -l run=my-nginx -o jsonpath={.items..metadata.name}) -c istio-proxy -- curl -v -k --resolve 
    ...
    * Server certificate:
    *  subject: CN=nginx.example.com; O=some organization
    *  start date: May 25 02:09:02 2020 GMT
    *  expire date: May 25 02:09:02 2021 GMT
    *  issuer: O=example Inc.; CN=example.com
    *  SSL certificate verify result: unable to get local issuer certificate (20),continuing anyway.
    ...
    

配置一个ingress gateway

  1. 定义一个gateway,端口为443.注意TLS的模式为PASSTHROUGH,表示gateway会放行ingress流量,不终止TLS

    $ kubectl apply -f - <<EOF
    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: PASSTHROUGH #不终止TLS
        hosts:
        - nginx.example.com
    EOF
    
  2. 配置经过Gateway的流量路由

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: nginx
    spec:
      hosts:
      - nginx.example.com
      gateways:
      - mygateway
      tls:
      - match:
        - port: 443 #将gateway的流量导入kubernetes的my-nginx service
          sniHosts:
          - nginx.example.com
        route:
        - destination:
            host: my-nginx
            port:
              number: 443
    EOF
    
  3. 根据指导配置SECURE_INGRESS_PORTINGRESS_HOST环境变量

  4. 通过ingress访问nginx,可以看到访问成功

    $ curl -v --resolve nginx.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST --cacert example.com.crt https://nginx.example.com:$SECURE_INGRESS_PORT
    ...
    * SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
    * Server certificate:
    *       subject: O=some organization,CN=nginx.example.com
    *       start date: May 25 02:09:02 2020 GMT
    *       expire date: May 25 02:09:02 2021 GMT
    *       common name: nginx.example.com
    *       issuer: CN=example.com,O=example Inc.
    ...
    <title>Welcome to nginx!</title>
    ...
    

卸载

  1. 移除kubernetes资源

    $ kubectl delete secret nginx-server-certs
    $ kubectl delete configmap nginx-configmap
    $ kubectl delete service my-nginx
    $ kubectl delete deployment my-nginx
    $ kubectl delete gateway mygateway
    $ kubectl delete virtualservice nginx
    
  2. 删除证书和密钥

    $ rm example.com.crt example.com.key nginx.example.com.crt nginx.example.com.key nginx.example.com.csr
    
  3. 删除生成的配置文件

    $ rm ./nginx.conf
    

TIPS

Gateway支持的TLS模式如下:

Name Description
PASSTHROUGH 客户端提供的SNI字符串将用作VirtualService TLS路由中的匹配条件,以根据服务注册表确定目标服务
SIMPLE 使用标准TLS语义的安全连接
MUTUAL 通过提供服务器证书进行身份验证,使用双边TLS来保护与下游的连接
AUTO_PASSTHROUGH 与直通模式相似,不同之处在于具有此TLS模式的服务器不需要关联的VirtualService即可从SNI值映射到注册表中的服务。目标详细信息(例如服务/子集/端口)被编码在SNI值中。代理将转发到SNI值指定的上游(Envoy)群集(一组端点)。
ISTIO_MUTUAL 通过提供用于身份验证的服务器证书,使用相互TLS使用来自下游的安全连接

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


istio的授权功能,也称为基于角色的访问控制(RBAC),它为istio服务网格中的服务提供命名空间级别、服务级别和方法级别的访问控制。基于角色的访问控制具有简单易用、灵活和高性能等特性。本文介绍如何在服务网格中为服务进行授权控制。·前置条件·•安装istio的k8s集群,启用认证功能、
Errorfromserver(Forbidden):errorwhencreating"oot/istio.yaml":configmaps"istio-galley-configuration"isforbidden:unabletocreatenewcontentinnamespaceistio-systembecauseitisbeingterminatedErrorfromserver(Forbid
3.1Istio的核心组件及其功能Istio总体分两部分:控制面和数据面。数据面(sidecar):sidecar通过注入的方式和业务容器共存于一个pod,会劫持业务容器的流量,并接受控制面组件的控制,同时会向控制面输出日志、跟踪以及监控数据。控制面:Istio的核心,管理Istio的所有功能。
在Istio中,双向TLS是传输身份验证的完整堆栈解决方案,它为每个服务提供可跨集群的强大身份、保护服务到服务通信和最终用户到服务通信,以及提供密钥管理系统。本文阐述如何在不中断通信的情况下,把现存Istio服务的流量从明文升级为双向TLS。使用场景 在部署了Istio的集群中,使用人员
在之前的最佳实践中,已经带大家通过一系列的实践任务领略了Istio的无穷魅力。今天,将向大家介绍如何用Istio实现流量熔断。熔断机制是创建弹性微服务应用程序的重要模式。熔断可以帮助您自由控制故障影响的范围、网络延迟的峰值以及抵御其他一些来自外部的恶意***等场景。在接下来
流量镜像流量镜像,也称为影子流量,流量镜像提供一种尽可能低的风险为生产带来变化的强大功能。镜像会将实时流量的副本发送到镜像服务。镜像流量发生在主服务的关键请求路径之外。在非生产或者测试环境中,尝试访问一个服务所有可能的测试用例组合是个非常不现实的任务。在某些情况下
一、负载均衡算法原理与实战负载均衡算法(loadbalancingalgorithm),定义了几种基本的流量分发方式,在Istio中一共有4种标准负载均衡算法。•Round_Robin:轮询算法,顾名思义请求将会依次发给每一个实例,来共同分担所有的请求。•Random:随机算法,将所有的请求随机分发给健康的实例•
本文整理自华为CloudBU技术专家在K8S技术社上关于Istio调用链的分享。前言大家好,我是idouba,来自华为CloudBU,当前在做Istio服务网格在华为云容器服务的产品化工作。今天跟大家分享的主题是Istio调用链相关内容。通过剖析Istio的架构机制与Istio中调用链的工作原理来解答一个大
今天,我们就来谈谈Istio主打功能---保护服务。那么,便引出3个问题:Istio凭什么保护服务?Istio具体如何保护服务?如何告诉Istio发挥保护能力?Istio凭什么保护服务?将单体应用程序分解为一个个服务,为大型软件系统的开发和维护带来了诸多好处,比如更好的灵活性、可伸缩性和可复用性
istio-opentracing链路追踪方案istio-opentracing链路追踪主要是由sidecar(envoy)支持的,istio只是在上层进行配置的修改。envoy链路追踪envoy主要用三个功能来支撑系统范围内的跟踪生成RequestID:envoy会在需要的时候生成UUID,并操作名为[x-request-id]的HTTPHeader。应用可
在前面的文章中,大家都已经熟悉了Istio的故障注入和流量迁移。这两个方面的功能都是Istio流量治理的一部分。今天将继续带大家了解Istio的另一项功能,关于请求超时的管理。首先我们可以通过一个简单的Bookinfo的微服务应用程序来动手实践一下Istio是如何实现请求超时的管理。看过ido
调用链原理和场景正如ServiceMesh的诞生是为了解决大规模分布式服务访问的治理问题,调用链的出现也是为了对应于大规模的复杂的分布式系统运行中碰到的故障定位定界问题。大量的服务调用、跨进程、跨服务器,可能还会跨多个物理机房。无论是服务自身问题还是网络环境的问题导致调用
在Istio中,双向TLS是传输身份验证的完整堆栈解决方案,它为每个服务提供可跨集群的强大身份、保护服务到服务通信和最终用户到服务通信,以及提供密钥管理系统。本文阐述如何在不中断通信的情况下,把现存Istio服务的流量从明文升级为双向TLS。使用场景在部署了Istio的集群中,使用人员刚
前言在Istio的世界里,如果想把外部的请求流量引入网格,你需要认识并会学会配置IstioIngressGateway什么是IngressGateway由于KubernetesIngressAPI只能支持最基本的HTTP路由,使用KubernetesIngress资源来配置外部流量的方式不能满足需求。因此Istiov1alpha3routingAPI引
Istio是什么?使用云平台可以为组织提供丰富的好处。然而,不可否认的是,采用云可能会给DevOps团队带来压力。开发人员必须使用微服务已满足应用的可移植性,同时运营商管理了极其庞大的混合和多云部署。Istio允许您连接、保护、控制和观测服务。在较高的层次上,Istio有助于降低这些
Istio利用k8s的探针对service进行流量健康检查,有两种探针可供选择,分别是liveness和readiness:liveness探针用来侦测什么时候需要重启容器。比如说当liveness探针捕获到程序运行时出现的一个死锁,这种情况下重启容器可以让程序更容易可用。readiness探针用来使容器准备好接收流量。
摘要使用Istio可以很方便地实现微服务间的访问控制。本文演示了使用Denier适配器实现拒绝访问,和Listchecker适配器实现黑白名单两种方法。使用场景有时需要对微服务间的相互访问进行控制,比如使满足某些条件(比如版本)的微服务能够(或不能)调用特定的微服务。访问控制属于策略
导读目前以Kubernetes为基础构建的容器生态逐渐完善,这其中Kubernetes、Istio、Knative三个独立项目被越来越多的人提及,并且已经开始尝试大规模落地实践,它们恰好构成了容器云的未来拼图。今天与大家一起分享下,这三个项目究竟解决了什么问题,为什么它们能够一鸣惊人。随着微服务理念
注:以下讲述的按理环境场景是基于Kubernetes环境基础上部署的Istio环境。涉及到Envoy概念介绍请参考深度解析Istio系列之流量控制篇。本文重点针对Envoy初始化场景进行拆解。Istio-proxy(Envoy)作为Istio数据平面的重要组件,基于sidecar方式与业务应用混合部署到同一pod,为应用提
使用云平台可以为组织提供丰富的好处。然而,不可否认的是,采用云可能会给DevOps团队带来压力。开发人员必须使用微服务以满足应用的可移植性,同时运营商管理了极其庞大的混合和多云部署。Istio允许您连接、保护、控制和观测服务。在较高的层次上,Istio有助于降低这些部署的复杂性,