TLS Origination for Egress Traffic

The Control Egress Traffic task demonstrates how external, meaning outside of the Kubernetes cluster, HTTP and HTTPS services can be accessed from applications inside the mesh. As described in that topic, by default Istio-enabled applications are unable to access URLs outside the cluster. To enable external access, a ServiceEntry for the external service must be defined, or alternatively, direct access to external services must be configured.

This example describes how to configure Istio to perform TLS origination for egress traffic.

Use case

Consider a legacy application that performs HTTP calls to external sites. Suppose the organization that operates the application receives a new requirement which states that all the external traffic must be encrypted. With Istio, such a requirement can be achieved just by configuration, without changing the code of the application.

In this example, you configure Istio to open HTTPS connections to external services in cases the original traffic was HTTP. The application will send unencrypted HTTP requests as previously and Istio will encrypt the requests for the application.

Before you begin

  • Setup Istio by following the instructions in the Installation guide.

  • Start the sleep sample which will be used as a test source for external calls.

    If you have enabled automatic sidecar injection, deploy the sleep application:

    $ kubectl apply -f @samples/sleep/sleep.yaml@

    Otherwise, you have to manually inject the sidecar before deploying the sleep application:

    $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@)

    Note that any pod that you can exec and curl from will do for the procedures below.

  • Create a shell variable to hold the name of the source pod for sending requests to external services. If you used the sleep sample, run:

    $ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})

Configuring HTTP and HTTPS external services

First, configure access to cnn.com in the same way as in the Control Egress Traffic task. Note that you use a wildcard * in your hosts definition: *.cnn.com. Using the wildcard allows access to www.cnn.com as well as to edition.cnn.com.

  1. Create a ServiceEntry to allow access to an external HTTP and HTTPS services:

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: cnn
    spec:
      hosts:
      - "*.cnn.com"
      ports:
      - number: 80
        name: http-port
        protocol: HTTP
      - number: 443
        name: https-port
        protocol: HTTPS
      resolution: NONE
    EOF
  2. Make a request to the external HTTP service:

    $ kubectl exec -it $SOURCE_POD -c sleep -- curl -sL -o /dev/null -D - http://edition.cnn.com/politics
    HTTP/1.1 301 Moved Permanently
    ...
    location: https://edition.cnn.com/politics
    ...
    
    HTTP/1.1 200 OK
    Content-Type: text/html; charset=utf-8
    ...
    Content-Length: 151654
    ...

    The output should be similar to the above (some details replaced by ellipsis).

Note the -L flag of curl which instructs curl to follow redirects. In this case, the server returned a redirect response (301 Moved Permanently) to an HTTP request to http://edition.cnn.com/politics. The redirect response instructs the client to send an additional request, this time by HTTPS, to https://edition.cnn.com/politics. For the second request, the server returns the requested content and 200 OK status code.

While for the curl command this redirection happens transparently, there are two issues here. The first issue is the redundant first request, which doubles the latency of fetching the content of http://edition.cnn.com/politics. The second issue is that the path of the URL, politics in this case, is sent in clear text. If there is an attacker who sniffs the communication between your application and cnn.com, the attacker would know which specific topics and articles of cnn.com your application fetched. For privacy reasons, you might want to prevent such disclosure from the attacker.

In the next section, you configure Istio to perform TLS origination to resolve these two issues. Clean your configuration before proceeding to the next section:

$ kubectl delete serviceentry cnn

TLS origination for egress traffic

  1. Define a ServiceEntry to allow traffic to edition.cnn.com, a VirtualService to perform request port rewriting, and a DestinationRule for TLS origination.

    Unlike the ServiceEntry in the previous section, here you use HTTP for the protocol on port 433 because clients will send HTTP requests and Istio will perform TLS origination for them. Also, in this example the resolution must be set to DNS to correctly configure Envoy.

    Finally, note that the VirtualService uses a specific host edition.cnn.com (no wildcard) because the Envoy proxy needs to know exactly which host to access using HTTPS:

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1alpha3
    kind: ServiceEntry
    metadata:
      name: cnn
    spec:
      hosts:
      - edition.cnn.com
      ports:
      - number: 80
        name: http-port
        protocol: HTTP
      - number: 443
        name: http-port-for-tls-origination
        protocol: HTTP
      resolution: DNS
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: rewrite-port-for-edition-cnn-com
    spec:
      hosts:
      - edition.cnn.com
      http:
      - match:
          - port: 80
        route:
        - destination:
            host: edition.cnn.com
            port:
              number: 443
    ---
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: originate-tls-for-edition-cnn-com
    spec:
      host: edition.cnn.com
      trafficPolicy:
        loadBalancer:
          simple: ROUND_ROBIN
        portLevelSettings:
        - port:
            number: 443
          tls:
            mode: SIMPLE # initiates HTTPS when accessing edition.cnn.com
    EOF
  2. Send an HTTP request to http://edition.cnn.com/politics, as in the previous section:

    $ kubectl exec -it $SOURCE_POD -c sleep -- curl -sL -o /dev/null -D - http://edition.cnn.com/politics
    HTTP/1.1 200 OK
    Content-Type: text/html; charset=utf-8
    ...
    Content-Length: 151654
    ...

    This time you receive 200 OK as the first and the only response. Istio performed TLS origination for curl so the original HTTP request was forwarded to cnn.com as HTTPS. The cnn.com server returned the content directly, without the need for redirection. You eliminated the double round trip between the client and the server, and the request left the mesh encrypted, without disclosing the fact that your application fetched the politics section of cnn.com.

    Note that you used the same command as in the previous section. For applications that access external services programmatically, the code will not be changed. Therefore, you get the benefits of TLS origination by configuring Istio, without changing a line of code.

Additional security considerations

Note that the traffic between the application pod and the sidecar proxy on the local host is still unencrypted. It means that if an attacker was able to penetrate the node of your application, they would still be able to see the unencrypted communication on the local network of the node. In some environments a strict security requirement might exist that would state that all the traffic must be encrypted, even on the local network of the nodes. With such a strict requirement the applications should use HTTPS (TLS) only, the TLS origination described in this example will not be sufficient.

Also note that even for HTTPS originated by the application, the attacker could know that the requests to cnn.com are being sent, by inspecting Server Name Indication (SNI). The SNI field is sent unencrypted during the TLS handshake. Using HTTPS prevents the attackers from knowing specific topics and articles, it does not prevent the attackers from learning that cnn.com is accessed.

Cleanup

  1. Remove the Istio configuration items you created:

    $ kubectl delete serviceentry cnn
    $ kubectl delete virtualservice rewrite-port-for-edition-cnn-com
    $ kubectl delete destinationrule originate-tls-for-edition-cnn-com
  2. Shutdown the sleep service:

    $ kubectl delete -f @samples/sleep/sleep.yaml@

See also

Describes a simple scenario based on Istio's Bookinfo example.

Describes a simple scenario based on Istio's Bookinfo example.

Describes how to configure Istio to direct traffic to external services through a dedicated gateway.

Describes how to configure Istio to route traffic from services in the mesh to external services.

Introduction, motivation and design principles for the Istio v1alpha3 routing API.

Describes how to configure Istio ingress with a network load balancer on AWS.