MySQL/MariaDB + istio TLS Passthrough routing based on SNI

Istio-1.16.2

Create istio ingressgateway as such

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  profile: default
  components:
    ingressGateways:
    - name: istio-ingressgateway
      enabled: true
      k8s:
        service:
          type: NodePort
          ports:
            - 
              name: status-port
              port: 15021
              protocol: TCP
              targetPort: 15021
            -
              name: http2
              port: 80
              protocol: TCP
              targetPort: 8080
              nodePort: 30080
            -
              name: https
              port: 443
              protocol: TCP
              targetPort: 8443
              nodePort: 30443
            -
              name: tls
              port: 3306
              protocol: TCP
              targetPort: 3306
              nodePort: 30336

Create mariadb pod (envoy injected) and service

---
apiVersion: v1
kind: Pod
metadata:
  name: csp
  namespace: default
  labels:
    app: csp
    sidecar.istio.io/inject: "true"
spec:
  containers:
  - name: mariadb
    image: mariadb
    args:
      - --user=mysql
      - --require_secure_transport=ON
      - --ssl-ca=/etc/certs/root-ca.pem
      - --ssl-cert=/etc/certs/server-cert.pem
      - --ssl-key=/etc/certs/server-key.pem
    ports:
    - containerPort: 3306
    volumeMounts:
      - name: mariadb-ssl
        mountPath: /etc/certs/
        readOnly: true
  volumes:
    - name: mariadb-ssl
      secret: 
        secretName: mariadb-ssl
  restartPolicy: Never
---
apiVersion: v1
kind: Service
metadata:
  name: csp-svc
  namespace: default
spec:
  selector:
    app: csp
  ports:
  - name: tls-db
    port: 3306
    targetPort: 3306
  type: NodePort

Create ingress gateway, virtual service and destination rule

---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: csp-gateway
  namespace: default
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 3306
      name: tls
      protocol: TLS
    tls:
      mode: PASSTHROUGH
    hosts:
    - "user1.csp.example.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: csp-virt-svc
  namespace: default
spec:
  hosts:
  - "user1.csp.example.com"
  gateways:
  - csp-gateway
  tls:
  - match:
    - port: 3306
      sniHosts:
        - user1.csp.example.com
    route:
    - destination:
        host: csp-svc
        port:
          number: 3306
        subset: default
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: csp-svc-dest
  namespace: default
spec:
  host: csp-svc
  subsets:
  - name: default
    labels:
      app: csp
---

Checking the ingressgateway routes, it is not showing the expected route

$ ~/istio-1.16.2/bin/istioctl proxy-config routes istio-ingressgateway-b7dd4d8c-nhdhp.istio-system
NAME     DOMAINS     MATCH                  VIRTUAL SERVICE
         *           /stats/prometheus*
         *           /healthz/ready*

Istioctl analyze gives no error. Pod istio proxy logs look normal

2023-03-19T15:38:10.400761Z     info    Readiness succeeded in 564.633686ms
2023-03-19T15:38:10.400958Z     info    Envoy proxy is ready
2023-03-19T16:07:52.324533Z     info    xdsproxy        connected to upstream XDS server: istiod.istio-system.svc:15012

My objective is to use TLS-enabled MySQL/MariaDB to do SNI routing in istio. The above example is 1 instance of mariadb pod+service, I will have many.

How can I achieve this? It seems possible from here

1 Like

I don’t know that if MariaDB connection is server first, but Mysql is. So you have to expose in the gateway not TLS but TCP, without setup tls in gateway. Because you have set up tls already in mariadb server.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: csp-gateway
  namespace: default
spec:
  selector:
    istio: ingressgateway
  servers:
  - port:
      number: 3306
      name: tcp
      protocol: TCP

Try gateway manifest from above

Same,

istioctl proxy-config routes istio-ingressgateway-b7dd4d8c-nhdhp.istio-system
NAME     DOMAINS     MATCH                  VIRTUAL SERVICE
         *           /stats/prometheus*
         *           /healthz/ready*

Still not showing.
Also, I want to use VirtualService with TLS to route based on SNI. If MySQL is server first, basically I cannot achieve what I want, am I right?

You have to setup ssl certificate in Pod not in VirtualService.
In your yaml you try to setup tls to http service, but MySQL/MariaDB is TCP not HTTP.

Certificate in pod should have CommonName and dnsnames for mariadb.example.com

      - --ssl-ca=/etc/certs/root-ca.pem
      - --ssl-cert=/etc/certs/server-cert.pem
      - --ssl-key=/etc/certs/server-key.pem
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: mariadb
spec:
  hosts:
  - mariadb.example.com
  tcp:
  - match:
    - port: 3306
    route:
    - destination:
        host: mariadb.svc
        port:
          number: 3306

Yes, but in future I will have multiple mariadb pods and respective services. My VirtualService needs to be able to route based on SNI in TLS. Something like

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: mariadb
  namespace: default
spec:
  hosts:
  - "mariadb1.example.com"
  - "mariadb2.example.com"
  tls:
  - match:
    - port: 3306
      sniHosts:
        - mariadb1.example.com
    route:
    - destination:
        host: mariadb1.svc
        port:
          number: 3306
  - match:
    - port: 3306
      sniHosts:
        - mariadb2.example.com
    route:
    - destination:
        host: mariadb2.svc
        port:
          number: 3306

(obviously this example did not work)

So that i can use hostname (mariadb1.example.com, mariadb2.example.com) with same port (eg 3306) to connect to individual mariadb services respectively. With TCP this is not possible, only TLSRoute has SNI.

You cant create split TCP port. You can do it with HTTP because HTTP request has headers with Host, TCP has only IP and port.

When processing TCP traffic, Istio has a very small amount of useful information to route the connection - only the destination IP and Port. These attributes are used to determine the intended Service; the proxy is configured to listen on each service IP (:) pair and forward traffic to the upstream service.

Yes I understand TCP alone cannot do it.

Which is why I want to achieve it using TLS (not TCP) routing, which has SNI.

By enabling SSL for mariadb, I can make client communicate with mariadb server via TLS connection, hence it should have SNI for routing already?

Ok, maybe problem is with mysql/mariadb client (https://bugs.mysql.com/bug.php?id=82872)

I tested mysql with tls and SNI. Openssl client works fine, istio route to correct virtualservice, but mysql client doesn’t work and doesn’t send SNI. I think, that happened because that the mysql is server first, so client wait first for server and does not send SNI tls info to the server and this is a problem.

Thanks, this is what I expected too when you said Mysql is server-first. Appreciate for your reply.

It seems, https://dev.mysql.com/doc/relnotes/mysql/8.1/en/news-8-1-0.html (Please share if you had a chance to test)

MySQL now implements client-side Server Name Indication (SNI), which is an extension to the TLS protocol. Client applications can pass a server name to the libmysqlclient C API library with the new MYSQL_OPT_TLS_SNI_SERVERNAME option for mysql_options(). Similarly, each MySQL client program now includes a --tls-sni-servername command option to pass in a name. The new Tls_sni_server_name server status variable indicates the name if one is set for the session. Our thanks to Meta for the contribution. (Bug #33176362, WL #14839)