Istio as a Proxy for External Services

Configure Istio ingress gateway to act as a proxy for external services.

Oct 15, 2019 | By Vadim Eisenberg - IBM

The Control Ingress Traffic and the Ingress Gateway without TLS Termination tasks describe how to configure an ingress gateway to expose services inside the mesh to external traffic. The services can be HTTP or HTTPS. In the case of HTTPS, the gateway passes the traffic through, without terminating TLS.

This blog post describes how to use the same ingress gateway mechanism of Istio to enable access to external services and not to applications inside the mesh. This way Istio as a whole can serve just as a proxy server, with the added value of observability, traffic management and policy enforcement.

The blog post shows configuring access to an HTTP and an HTTPS external service, namely httpbin.org and edition.cnn.com.

Configure an ingress gateway

  1. Define an ingress gateway with a servers: section configuring the 80 and 443 ports. Ensure mode: is set to PASSTHROUGH for tls: in the port 443, which instructs the gateway to pass the ingress traffic AS IS, without terminating TLS.

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: Gateway
    metadata:
      name: proxy
    spec:
      selector:
        istio: ingressgateway # use istio default ingress gateway
      servers:
      - port:
          number: 80
          name: http
          protocol: HTTP
        hosts:
        - httpbin.org
      - port:
          number: 443
          name: tls
          protocol: TLS
        tls:
          mode: PASSTHROUGH
        hosts:
        - edition.cnn.com
    EOF
    
  2. Create service entries for the httpbin.org and edition.cnn.com services to make them accessible from the ingress gateway:

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: httpbin-ext
    spec:
      hosts:
      - httpbin.org
      ports:
      - number: 80
        name: http
        protocol: HTTP
      resolution: DNS
      location: MESH_EXTERNAL
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: cnn
    spec:
      hosts:
      - edition.cnn.com
      ports:
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
    EOF
    
  3. Create a service entry and configure a destination rule for the localhost service. You need this service entry in the next step as a destination for traffic to the external services from applications inside the mesh to block traffic from inside the mesh. In this example you use Istio as a proxy between external applications and external services.

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: localhost
    spec:
      hosts:
      - localhost.local
      location: MESH_EXTERNAL
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: STATIC
      endpoints:
      - address: 127.0.0.1
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: localhost
    spec:
      host: localhost.local
      trafficPolicy:
        tls:
          mode: DISABLE
          sni: localhost.local
    EOF
    
  4. Create a virtual service for each external service to configure routing to it. Both virtual services include the proxy gateway in the gateways: section and in the match: section for HTTP and HTTPS traffic accordingly.

    Notice the route: section for the mesh gateway, the gateway that represents the applications inside the mesh. The route: for the mesh gateway shows how the traffic is directed to the localhost.local service, effectively blocking the traffic.

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: httpbin
    spec:
      hosts:
      - httpbin.org
      gateways:
      - proxy
      - mesh
      http:
      - match:
        - gateways:
          - proxy
          port: 80
          uri:
            prefix: /status
        route:
        - destination:
            host: httpbin.org
            port:
              number: 80
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: localhost.local
            port:
              number: 80
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: cnn
    spec:
      hosts:
      - edition.cnn.com
      gateways:
      - proxy
      - mesh
      tls:
      - match:
        - gateways:
          - proxy
          port: 443
          sni_hosts:
          - edition.cnn.com
        route:
        - destination:
            host: edition.cnn.com
            port:
              number: 443
      - match:
        - gateways:
          - mesh
          port: 443
          sni_hosts:
          - edition.cnn.com
        route:
        - destination:
            host: localhost.local
            port:
              number: 443
    EOF
    
  5. Enable Envoy’s access logging.

  6. Follow the instructions in Determining the ingress IP and ports to define the SECURE_INGRESS_PORT and INGRESS_HOST environment variables.

  7. Access the httpbin.org service through your ingress IP and port which you stored in the $INGRESS_HOST and $INGRESS_PORT environment variables, respectively, during the previous step. Access the /status/418 path of the httpbin.org service that returns the HTTP status 418 I’m a teapot.

    $ curl $INGRESS_HOST:$INGRESS_PORT/status/418 -Hhost:httpbin.org
    
    -=[ teapot ]=-
    
       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`
    
  8. If the Istio ingress gateway is deployed in the istio-system namespace, print the gateway’s log with the following command:

    $ kubectl logs -l istio=ingressgateway -c istio-proxy -n istio-system | grep 'httpbin.org'
    
  9. Search the log for an entry similar to:

    [2019-01-31T14:40:18.645Z] "GET /status/418 HTTP/1.1" 418 - 0 135 187 186 "10.127.220.75" "curl/7.54.0" "28255618-6ca5-9d91-9634-c562694a3625" "httpbin.org" "34.232.181.106:80" outbound|80||httpbin.org - 172.30.230.33:80 10.127.220.75:52077 -
    
  10. Access the edition.cnn.com service through your ingress gateway:

    $ curl -s --resolve edition.cnn.com:$SECURE_INGRESS_PORT:$INGRESS_HOST https://edition.cnn.com:$SECURE_INGRESS_PORT | grep -o "<title>.*</title>"
    <title>CNN International - Breaking News, US News, World News and Video</title>
    
  11. If the Istio ingress gateway is deployed in the istio-system namespace, print the gateway’s log with the following command:

    $ kubectl logs -l istio=ingressgateway -c istio-proxy -n istio-system | grep 'edition.cnn.com'
    
  12. Search the log for an entry similar to:

    [2019-01-31T13:40:11.076Z] "- - -" 0 - 589 17798 1644 - "-" "-" "-" "-" "172.217.31.132:443" outbound|443||edition.cnn.com 172.30.230.33:54508 172.30.230.33:443 10.127.220.75:49467 edition.cnn.com
    

Cleanup

Remove the gateway, the virtual services and the service entries:

$ kubectl delete gateway proxy
$ kubectl delete virtualservice cnn httpbin
$ kubectl delete serviceentry cnn httpbin-ext localhost
$ kubectl delete destinationrule localhost