Is there a way to proxy calls to an ExternalName service thanks to an Istio VirtualService?

10/8/2020

In a project I'm currently working on, I'd like to create a DNS alias for a Kubernetes service located in another namespace. To do so, I created an ExternalName service such as the following:

kind: Service
apiVersion: v1
metadata:
  name: connector
  namespace: test
spec:
  type: ExternalName
  externalName: gateway.eventing.svc.cluster.local

So far, so good. When I request the 'connector' DNS, I successfully hit the external name, i.e. gateway.eventing.svc.cluster.local.

Now, I would like to add headers to all http requests sent to the connector ExternalName service, so I created an Istio VirtualService to do so:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: connector
  namespace: test
spec:
  hosts:
    - connector
    - connector.test.svc.cluster.local
 http:
   - match:
      - uri:
          prefix: /
      route:
        - destination:
            host: connector
            port:
              number: 80
#headers config ignored for brevity

The problem is that the VirtualService is never called. It seems it does not intercepts request made to the connector DNS or to its fully qualified name, i.e. connector.test.svc.cluster.local.

I figured, after reading the documentation, that this happens because the Istio VirtualService checks the service registry, and the ExternalName service is not part of it, it's just some kind of DNS alias.

I therefore attempted to create an Istio ServiceEntry such as the following:

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: connector
  namespace: test
spec:
  hosts:
  - connector
  endpoints:
  - address: gateway.eventing.svc.cluster.local
  ports:
  - number: 80
    name: http
    protocol: HTTP
  location: MESH_INTERNAL
  resolution: DNS

It works, and I can see in Kiali that instead of calling the PassthroughCluster when requesting connector, it is the connector ServiceEntry which is called, which is to my understanding what should be happening.

However, my connector VirtualService is still not called. Why is that? Is there a way to make it happen?

If not, what can I do to alias in a given namespace (i.e. test) a service located in another (i.e. eventing) and proxy http request thanks to an Istio VirtualService?

Thanks in advance for your help!

EDIT:

Sidecar injection is enabled namespace-wide (i.e. test)

-- Charles d'Avernas
istio
kubernetes

2 Answers

10/9/2020

So, it turns out that all that was missing to make it work was to both specify and name the port on the ExternalName service.

Here's the updated yaml:

kind: Service
apiVersion: v1
metadata:
  name: connector
  namespace: test
spec:
  type: ExternalName
  externalName: gateway.eventing.svc.cluster.local
  ports:
    - name: http
      port: 80

---

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: connector
  namespace: test
spec:
  hosts:
    - connector
    - connector.test.svc.cluster.local
 http:
   - match:
      - uri:
          prefix: /
      route:
        - destination:
            host: connector
            port:
              number: 80
#headers config ignored for brevity

Naming the port is absolutely required, as it lets Istio know of the application protocol to use, as defined by the VirtualService.

No need to add a ServiceEntry, it will work with the BYON host specified in the VirtualService.

Note that the answer supplied by @Christoph Raab works as well, but is unhappilly too verbose to be marked as my prefered answer.

-- Charles d'Avernas
Source: StackOverflow

10/8/2020

Update

I didn't see that the ports list was missing and am not sure how you could apply the yml, because the list should be required.

Anyways, I leave my answer. Maybe it will help someone else in the further.

Original Post (slightly modified)

Docs are not clear, but I think the header manipulation be could done by the receiving sidecar. As far as I understand your setup, the resource behind the ServiceEntry does not have a sidecar, so if that would be true, the manipulation wouldn't work.

In order to add custom headers you can use a EnvoyFilter of type lua that is applied to the sender's sidecar and can manipulate the traffic on the fly.

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: add-custom-header-filter
  namespace: test
spec:
  configPatches:
  - applyTo: CLUSTER
    match:
      context: SIDECAR_OUTBOUND
      cluster: 
        service: connector
    patch:
      operation: INSERT_BEFORE
      value: 
        name: envoy.lua
        typed_config:
          "@type": "type.googleapis.com/envoy.config.filter.http.lua.v2.Lua"
          inlineCode: |
            function envoy_on_request(request_handle)
              response_handle:logInfo("adding custom headers...""); 
              response_handle:headers():add("X-User-Header", "worked");
            end

This filter is applied to every request to the service entry connector by every sidecar in the namespace test on outbound and adds a custom header before any other action done.

-- Chris
Source: StackOverflow