mTLS origination on egress: selecting client certificates based on traffic source

Hi everyone,

We are building a setup where the egress gateway originates mTLS to a mesh-external host, roughly following this setup. However, in our setup the mesh-external host needs to identify the client using the client certificate. Hence, the egress gateway needs to send different client certificates based on the source of the traffic within the mesh.

Here’s our approach so far. We create a destination rule for our external host with two subsets, one for each client:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: originate-mtls-for-schlt-example
spec:
  host: dev-s-this-02.draco-910.sccloudres.net
  subsets:
    - name: client-1
      trafficPolicy:
        loadBalancer:
          simple: ROUND_ROBIN
        portLevelSettings:
          - port:
              number: 443
            tls:
              mode: MUTUAL
              clientCertificate: /etc/istio/dev-s-this-02-client-1-certs/tls.crt
              privateKey: /etc/istio/dev-s-this-02-client-1-certs/tls.key
              caCertificates: /etc/istio/dev-s-this-02-ca-certs/
              sni: dev-s-this-02.draco-910.sccloudres.net
    - name: client-2
      trafficPolicy:
        loadBalancer:
          simple: ROUND_ROBIN
        portLevelSettings:
          - port:
              number: 443
            tls:
              mode: MUTUAL
              clientCertificate: /etc/istio/dev-s-this-02-client-2-certs/tls.crt
              privateKey: /etc/istio/dev-s-this-02-client-2-certs/tls.key
              caCertificates: /etc/istio/dev-s-this-02-ca-certs/
              sni: dev-s-this-02.draco-910.sccloudres.net

We then create a virtual service which routes traffic to the external host dev-s-this-02.draco-910.sccloudres.net through the egress gateway. We detect which client is sending the request by matching on sourceLabels, in this test matching app: curl-1 or app: curl-2. Since we did not find a way to separate the traffic from each client across just one egress gateway, we created a second egress gateway service in istio. This allows us to then use separate routes and select the desired subset of the destination rule shown above.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: schlt-example-through-egress
spec:
  hosts:
    - dev-s-this-02.draco-910.sccloudres.net
  gateways:
    - schlt-example-egressgateway
    - schlt-example-second-egressgateway
    - mesh
  http:
    - match:
        - sourceLabels:
            app: curl-1
          gateways:
            - mesh
          port: 80
      route:
        - destination:
            host: istio-egressgateway.istio-system.svc.cluster.local
            subset: schlt
            port:
              number: 443
          weight: 100
    - match:
        - sourceLabels:
            app: curl-2
          gateways:
            - mesh
          port: 80
      route:
        - destination:
            host: istio-second-egressgateway.istio-system.svc.cluster.local
            subset: schlt
            port:
              number: 443
          weight: 100
    - match:
        - gateways:
            - schlt-example-egressgateway
          port: 443
      route:
        - destination:
            host: dev-s-this-02.draco-910.sccloudres.net
            subset: client-1
            port:
              number: 443
          weight: 100
    - match:
        - gateways:
            - schlt-example-second-egressgateway
          port: 443
      route:
        - destination:
            host: dev-s-this-02.draco-910.sccloudres.net
            subset: client-2
            port:
              number: 443
          weight: 100

Our test curl client services are defined as follows. Note the app labels which we are setting.

apiVersion: v1
kind: Service
metadata:
  name: curl-1
  labels:
    app: curl-1
spec:
  ports:
    - port: 80
      name: http
  selector:
    app: curl-1
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: curl-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: curl-1
  template:
    metadata:
      labels:
        app: curl-1
    spec:
      containers:
        - name: curl
          image: cet-docker-virtual.artifactory.swisscom.com/appropriate/curl
          command: ["/bin/sleep", "365d"]
          imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
  name: curl-2
  labels:
    app: curl-2
spec:
  ports:
    - port: 80
      name: http
  selector:
    app: curl-2
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: curl-2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: curl-2
  template:
    metadata:
      labels:
        app: curl-2
    spec:
      containers:
        - name: curl
          image: cet-docker-virtual.artifactory.swisscom.com/appropriate/curl
          command: ["/bin/sleep", "365d"]
          imagePullPolicy: IfNotPresent

Unfortunately, our setup does not work properly yet. In particular, the sourceLabels match does not seem to have an effect because all traffic from mesh seems to fall under the first match and consequently the first client certificate is always sent to the external host, irrespective of which client is calling.

Can anyone help us with the following questions:

  1. Does this setup make sense, or is there a completely different way of achieving the same goal?
  2. Are there any known limitations to matching sourceLabels which could play a role here?
  3. Do we really need to create a second egress gateway service just to separate the traffic or is there a way of marking traffic in one rule of a Virtual Service for processing in a second rule?

Any comments or hints appreciated! Let us know, if more details are needed.

Best regards and thanks!

Mathis

Hi Mathis,

I don’t do matching on sourceLabels, I do it by matching prefixes. eg /client-1, /client-2 and then you can choose your subset.

Now, what I’m not sure of is if you could use SDS for egress, but if it works for ingress, it should work also for egress traffic, but I haven’t tried it yet. Then you would not need to mount certificates, you would only have to reference the secret name. I’d try to set “sds” instead of all those paths.

Hope it helps
Tom

Hi Tomas,

Many thanks for your reply!

Not sure we can use different paths for different clients, which is what you are suggesting, if I understand correctly. Will check that.

Regarding SDS: we are using Istio 1.5.2 and we are using SDS on the ingress gateway. Unfortunately SDS is not yet supported on egress gateways: https://github.com/istio/istio/issues/14039

Best regards and thanks again

Mathis

You’re welcome. :slight_smile:

Regarding egress and SDS, that’s pretty disappointing. :frowning: With Helm installation, you can enable SDS for egress. I don’t even know why are the gateways split into egress and ingress folders in istioctl. I hope there was a good reason for that.