Search code examples
kubernetesistioenvoyproxyistio-gateway

using backtrack regex in istio


We are migrating from ingress-nginx to istio. While migrating existing ingress definitions to istio VirtualServices, we came across nginx style rewriting and wanted to achieve the same in istio. When researched it was found that istio doesn't support backtrack replacement. There is an open bug regarding the same in istio. People suggest to handle this via enjoy filters. Since I'm new to istio I've tried creating an Envoy filter but still, the URL returns 404.

Here is the sample ingress-nginx definition that we want to convert

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/configuration-snippet: |
      rewrite ^(/sample)$ $1/ permanent;
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
  name: sample-ingress
spec:
  tls:
  - hosts:
    - '*.example.com'
    secretName: icog-ssl
  rules:
  - host: abc.example.com
    http:
      paths:
      - backend:
          service:
            name: sample-ingress
            port:
              number: 80
        path: /sample(/|$)(.*)
        pathType: ImplementationSpecific

Here is the Envoy filter that was created to handle Reference

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: sample-filter
spec:
  configPatches:
    - applyTo: HTTP_ROUTE
      match:
        routeConfiguration:
          vhost:
            name: "inbound|http|80"
      patch:
        operation: MERGE
        value:
          route:
            regex_rewrite:
              pattern:
                 google_re2:
                   max_program_size: 100
                 regex: "^/sample(/|$)(.*)$"
              substitution: "/\\2"
  workloadSelector:
    labels:
      app: sample

we also tried the following as well Reference

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: sample-filter
spec:
  configPatches:
    - applyTo: HTTP_ROUTE
      match:
        context: ANY
      patch:
        operation: MERGE
        value:
          route:
            regex_rewrite:
              pattern:
                 google_re2:
                   max_program_size: 100
                 regex: "^/sample(/|$)(.*)$"
              substitution: "/\\2"
  workloadSelector:
    labels:
      app: sample

Here is the Virtual Service:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: sample-vs
  namespace: default
spec:
  hosts:
  - "*.xyz.com"
  gateways:
  - sample-gateway
  http:
  - name: sample
    match:
    - uri:
        regex: /sample(/|$)(.*)
    rewrite:
      uri: /$2
    route:
    - destination:
        host: sample
        port:
          number: 80

Gateway

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: sample-gateway
  namespace: default
spec:
  selector:
    istio: ingressgateway # use istio default controller
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    tls:
      httpsRedirect: true
    hosts:
    - "*.xyz.com"
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: sample-ssl 
    hosts:
    - "*.xyz.com"

Api REquests with Both the Envoyfilter returns 404. Not sure how to make this work with istio.


Solution

  • Finally, I was able to crack it down. It's actually simple. We can just use rewrite along with match in virtual service and there is no need to complicate it using filter. Here is the virtual service.

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: sample-vs
      namespace: default
    spec:
      hosts:
      - "*.xyz.com"
      gateways:
      - sample-gateway
      http:
      - name: sample-trailing
        match:
        - uri:
            prefix: /sample
        redirect:
          uri: /sample/ # This ensures that the trailing slash is added to the path. same as **rewrite ^(/sample)$ $1/ permanent;**
      - name: sample
        match:
        - uri:
            prefix: /sample/
        rewrite:
          uri: / #This ensures that internally it gets routed to **/$2**
        route:
        - destination:
            host: sample
            port:
              number: 80
    

    We are misguided by rewrite here we think rewrite rewrites the HTTP URL in the browser whereas it actually rewrites and forwards the request to the respective destination.