Egress Gateways con TLS origination

El ejemplo TLS origination para Tráfico de Salida muestra cómo configurar Istio para realizar la TLS origination para el tráfico a un service externo. El ejemplo Configurar un Egress Gateway muestra cómo configurar Istio para dirigir el tráfico de salida a través de un service egress gateway dedicado. Este ejemplo combina los dos anteriores al describir cómo configurar un egress gateway para realizar la TLS origination para el tráfico a services externos.

Antes de empezar

  • Configure Istio siguiendo las instrucciones de la guía de instalación.

  • Inicie la muestra curl que se utilizará como fuente de prueba para llamadas externas.

    Si ha habilitado la inyección automática de sidecar, haga

    Zip
    $ kubectl apply -f @samples/curl/curl.yaml@

    de lo contrario, debe inyectar manualmente el sidecar antes de desplegar la aplicación curl:

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

    Tenga en cuenta que cualquier pod desde el que pueda exec y curl servirá para los procedimientos siguientes.

  • Cree una variable de shell para almacenar el nombre del pod de origen para enviar solicitudes a services externos. Si utilizó la muestra curl, ejecute:

    $ export SOURCE_POD=$(kubectl get pod -l app=curl -o jsonpath={.items..metadata.name})
  • Para usuarios de macOS, verifique que está utilizando openssl versión 1.1 o posterior:

    $ openssl version -a | grep OpenSSL
    OpenSSL 1.1.1g  21 Apr 2020

    Si el comando anterior muestra una versión 1.1 o posterior, como se muestra, su comando openssl debería funcionar correctamente con las instrucciones de esta tarea. De lo contrario, actualice su openssl o pruebe una implementación diferente de openssl, por ejemplo en una máquina Linux.

  • Habilite el registro de acceso de Envoy si aún no está habilitado. Por ejemplo, usando istioctl:

    $ istioctl install <flags-you-used-to-install-Istio> --set meshConfig.accessLogFile=/dev/stdout
  • Si NO está utilizando las instrucciones de la Gateway API, asegúrese de desplegar el egress gateway de Istio.

Realizar la TLS origination con un egress gateway

Esta sección describe cómo realizar la misma TLS origination que en el ejemplo TLS origination para Tráfico de Salida, solo que esta vez utilizando un egress gateway. Tenga en cuenta que en este caso la TLS origination se realizará por el egress gateway, a diferencia del sidecar en el ejemplo anterior.

  1. Defina una ServiceEntry para edition.cnn.com:

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    kind: ServiceEntry
    metadata:
      name: cnn
    spec:
      hosts:
      - edition.cnn.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: https
        protocol: HTTPS
      resolution: DNS
    EOF
  2. Verifique que su ServiceEntry se aplicó correctamente enviando una solicitud a http://edition.cnn.com/politics.

    $ kubectl exec "${SOURCE_POD}" -c curl -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
    HTTP/1.1 301 Moved Permanently
    ...
    location: https://edition.cnn.com/politics
    ...

    Su ServiceEntry se configuró correctamente si ve 301 Moved Permanently en la salida.

  3. Cree un Gateway de salida para edition.cnn.com, puerto 80, y una regla de destino para las solicitudes sidecar que se dirigirán al egress gateway.

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
  - port:
      number: 80
      name: https-port-for-tls-origination
      protocol: HTTPS
    hosts:
    - edition.cnn.com
    tls:
      mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: egressgateway-for-cnn
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: cnn
    trafficPolicy:
      loadBalancer:
        simple: ROUND_ROBIN
      portLevelSettings:
      - port:
          number: 80
        tls:
          mode: ISTIO_MUTUAL
          sni: edition.cnn.com
EOF
  1. Configure reglas de ruta para dirigir el tráfico a través del egress gateway:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: direct-cnn-through-egress-gateway
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - istio-egressgateway
  - mesh
  http:
  - match:
    - gateways:
      - mesh
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        subset: cnn
        port:
          number: 80
      weight: 100
  - match:
    - gateways:
      - istio-egressgateway
      port: 80
    route:
    - destination:
        host: edition.cnn.com
        port:
          number: 443
      weight: 100
EOF
  1. Defina una DestinationRule para realizar la TLS origination para las solicitudes a edition.cnn.com:

    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1
    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 # inicia HTTPS para conexiones a edition.cnn.com
    EOF
  2. Envíe una solicitud HTTP a http://edition.cnn.com/politics.

    $ kubectl exec "${SOURCE_POD}" -c curl -- curl -sSL -o /dev/null -D - http://edition.cnn.com/politics
    HTTP/1.1 200 OK
    ...

    La salida debería ser la misma que en el ejemplo de TLS origination para Tráfico de Salida, con TLS origination: sin el mensaje 301 Moved Permanently.

  3. Verifique el registro del proxy del egress gateway.

Si Istio está desplegado en el namespace istio-system, el comando para imprimir el registro es:

$ kubectl logs -l istio=egressgateway -c istio-proxy -n istio-system | tail

Debería ver una línea similar a la siguiente:

[2020-06-30T16:17:56.763Z] "GET /politics HTTP/2" 200 - "-" "-" 0 1295938 529 89 "10.244.0.171" "curl/7.64.0" "cf76518d-3209-9ab7-a1d0-e6002728ef5b" "edition.cnn.com" "151.101.129.67:443" outbound|443||edition.cnn.com 10.244.0.170:54280 10.244.0.170:8080 10.244.0.171:35628 - -

Limpieza del TLS origination ejemplo

Borra las configuraciones de Istio que se crearon::

$ kubectl delete gw istio-egressgateway
$ kubectl delete serviceentry cnn
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule originate-tls-for-edition-cnn-com
$ kubectl delete destinationrule egressgateway-for-cnn

Realizar la originación mTLS con un egress gateway

De forma similar a la sección anterior, esta sección describe cómo configurar un egress gateway para realizar la TLS origination para un service externo, solo que esta vez utilizando un service que requiere mTLS.

Este ejemplo es considerablemente más complejo porque requiere la siguiente configuración:

  1. generar certificados de cliente y servidor
  2. desplegar un service externo que admita el protocolo mTLS
  3. volver a desplegar el egress gateway con los certificados mTLS necesarios

Solo entonces podrá configurar el tráfico externo para que pase por el egress gateway, que realizará la TLS origination.

Generar certificados y claves de cliente y servidor

Para esta tarea, puede usar su herramienta favorita para generar certificados y claves. Los comandos a continuación usan openssl

  1. Cree un certificado raíz y una clave privada para firmar el certificado de sus services:

    $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt
  2. Cree un certificado y una clave privada para my-nginx.mesh-external.svc.cluster.local:

    $ openssl req -out my-nginx.mesh-external.svc.cluster.local.csr -newkey rsa:2048 -nodes -keyout my-nginx.mesh-external.svc.cluster.local.key -subj "/CN=my-nginx.mesh-external.svc.cluster.local/O=some organization"
    $ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in my-nginx.mesh-external.svc.cluster.local.csr -out my-nginx.mesh-external.svc.cluster.local.crt

    Opcionalmente, puede agregar SubjectAltNames al certificado si desea habilitar la validación SAN para el destino. Por ejemplo:

    $ cat > san.conf <<EOF
    [req]
    distinguished_name = req_distinguished_name
    req_extensions = v3_req
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    countryName = US
    [v3_req]
    keyUsage = critical, digitalSignature, keyEncipherment
    extendedKeyUsage = serverAuth, clientAuth
    basicConstraints = critical, CA:FALSE
    subjectAltName = critical, @alt_names
    [alt_names]
    DNS = my-nginx.mesh-external.svc.cluster.local
    EOF
    $
    $ openssl req -out my-nginx.mesh-external.svc.cluster.local.csr -newkey rsa:4096 -nodes -keyout my-nginx.mesh-external.svc.cluster.local.key -subj "/CN=my-nginx.mesh-external.svc.cluster.local/O=some organization" -config san.conf
    $ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 0 -in my-nginx.mesh-external.svc.cluster.local.csr -out my-nginx.mesh-external.svc.cluster.local.crt -extfile san.conf -extensions v3_req
  3. Genere el certificado de cliente y la clave privada:

    $ openssl req -out client.example.com.csr -newkey rsa:2048 -nodes -keyout client.example.com.key -subj "/CN=client.example.com/O=client organization"
    $ openssl x509 -req -sha256 -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in client.example.com.csr -out client.example.com.crt

Desplegar un servidor mTLS

Para simular un service externo real que admita el protocolo mTLS, despliegue un servidor NGINX en su cluster de Kubernetes, pero ejecutándose fuera de la service mesh de Istio, es decir, en un namespace sin la inyección de proxy sidecar de Istio habilitada.

  1. Cree un namespace para representar services fuera de la malla de Istio, llamado mesh-external. Tenga en cuenta que el proxy sidecar no se inyectará automáticamente en los pods de este namespace ya que la inyección automática de sidecar no estaba habilitada en él.

    $ kubectl create namespace mesh-external
  2. Cree Secrets de Kubernetes para almacenar los certificados del servidor y de la CA.

    $ kubectl create -n mesh-external secret tls nginx-server-certs --key my-nginx.mesh-external.svc.cluster.local.key --cert my-nginx.mesh-external.svc.cluster.local.crt
    $ kubectl create -n mesh-external secret generic nginx-ca-certs --from-file=example.com.crt
  3. Cree un fichero de configuración para el servidor NGINX:

    $ cat <<\EOF > ./nginx.conf
    events {
    }
    
    http {
      log_format main '$remote_addr - $remote_user [$time_local]  $status '
      '"$request" $body_bytes_sent "$http_referer" '
      '"$http_user_agent" "$http_x_forwarded_for"';
      access_log /var/log/nginx/access.log main;
      error_log  /var/log/nginx/error.log;
    
      server {
        listen 443 ssl;
    
        root /usr/share/nginx/html;
        index index.html;
    
        server_name my-nginx.mesh-external.svc.cluster.local;
        ssl_certificate /etc/nginx-server-certs/tls.crt;
        ssl_certificate_key /etc/nginx-server-certs/tls.key;
        ssl_client_certificate /etc/nginx-ca-certs/example.com.crt;
        ssl_verify_client on;
      }
    }
    EOF
  4. Cree un ConfigMap de Kubernetes para contener la configuración del servidor NGINX:

    $ kubectl create configmap nginx-configmap -n mesh-external --from-file=nginx.conf=./nginx.conf
  5. Despliegue el servidor NGINX:

    $ kubectl apply -f - <<EOF
    apiVersion: v1
    kind: Service
    metadata:
      name: my-nginx
      namespace: mesh-external
      labels:
        run: my-nginx
    spec:
      ports:
      - port: 443
        protocol: TCP
      selector:
        run: my-nginx
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: my-nginx
      namespace: mesh-external
    spec:
      selector:
        matchLabels:
          run: my-nginx
      replicas: 1
      template:
        metadata:
          labels:
            run: my-nginx
        spec:
          containers:
          - name: my-nginx
            image: nginx
            ports:
            - containerPort: 443
            volumeMounts:
            - name: nginx-config
              mountPath: /etc/nginx
              readOnly: true
            - name: nginx-server-certs
              mountPath: /etc/nginx-server-certs
              readOnly: true
            - name: nginx-ca-certs
              mountPath: /etc/nginx-ca-certs
              readOnly: true
          volumes:
          - name: nginx-config
            configMap:
              name: nginx-configmap
          - name: nginx-server-certs
            secret:
              secretName: nginx-server-certs
          - name: nginx-ca-certs
            secret:
              secretName: nginx-ca-certs
    EOF

Configurar la originación mTLS para el tráfico de salida

  1. Cree un Secret de Kubernetes en el mismo namespace donde se despliega el egress gateway, para almacenar los certificados del cliente:
$ kubectl create secret -n istio-system generic client-credential --from-file=tls.key=client.example.com.key \
  --from-file=tls.crt=client.example.com.crt --from-file=ca.crt=example.com.crt

Para admitir la integración con varias herramientas, Istio admite algunos formatos de Secret diferentes. En este ejemplo, se utiliza un único Secret genérico con las claves tls.key, tls.crt y ca.crt.

  1. Cree un Gateway de salida para my-nginx.mesh-external.svc.cluster.local, puerto 443, y una regla de destino para las solicitudes sidecar que se dirigirán al egress gateway:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    hosts:
    - my-nginx.mesh-external.svc.cluster.local
    tls:
      mode: ISTIO_MUTUAL
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: egressgateway-for-nginx
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: nginx
    trafficPolicy:
      loadBalancer:
        simple: ROUND_ROBIN
      portLevelSettings:
      - port:
          number: 443
        tls:
          mode: ISTIO_MUTUAL
          sni: my-nginx.mesh-external.svc.cluster.local
EOF
  1. Configure reglas de ruta para dirigir el tráfico a través del egress gateway:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: direct-nginx-through-egress-gateway
spec:
  hosts:
  - my-nginx.mesh-external.svc.cluster.local
  gateways:
  - istio-egressgateway
  - mesh
  http:
  - match:
    - gateways:
      - mesh
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        subset: nginx
        port:
          number: 443
      weight: 100
  - match:
    - gateways:
      - istio-egressgateway
      port: 443
    route:
    - destination:
        host: my-nginx.mesh-external.svc.cluster.local
        port:
          number: 443
      weight: 100
EOF
  1. Agregue una DestinationRule para realizar la originación mTLS:
$ kubectl apply -n istio-system -f - <<EOF
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: originate-mtls-for-nginx
spec:
  host: my-nginx.mesh-external.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    portLevelSettings:
    - port:
        number: 443
      tls:
        mode: MUTUAL
        credentialName: client-credential # this must match the secret created earlier to hold client certs
        sni: my-nginx.mesh-external.svc.cluster.local
        # subjectAltNames: # can be enabled if the certificate was generated with SAN as specified in previous section
        # - my-nginx.mesh-external.svc.cluster.local
EOF
  1. Verifique que la credencial se suministra al egress gateway y está activa:
$ istioctl -n istio-system proxy-config secret deploy/istio-egressgateway | grep client-credential
kubernetes://client-credential            Cert Chain     ACTIVE     true           1                                          2024-06-04T12:46:28Z     2023-06-05T12:46:28Z
kubernetes://client-credential-cacert     Cert Chain     ACTIVE     true           16491643791048004260                       2024-06-04T12:46:28Z     2023-06-05T12:46:28Z
  1. Envíe una solicitud HTTP a http://my-nginx.mesh-external.svc.cluster.local:

    $ kubectl exec "$(kubectl get pod -l app=curl -o jsonpath={.items..metadata.name})" -c curl -- curl -sS http://my-nginx.mesh-external.svc.cluster.local
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    ...
  2. Verifique el registro del proxy del egress gateway:

Si Istio está desplegado en el namespace istio-system, el comando para imprimir el registro es:

$ kubectl logs -l istio=egressgateway -n istio-system | grep 'my-nginx.mesh-external.svc.cluster.local' | grep HTTP

Debería ver una línea similar a la siguiente:

[2018-08-19T18:20:40.096Z] "GET / HTTP/1.1" 200 - 0 612 7 5 "172.30.146.114" "curl/7.35.0" "b942b587-fac2-9756-8ec6-303561356204" "my-nginx.mesh-external.svc.cluster.local" "172.21.72.197:443"

Limpieza del ejemplo de originación mTLS

  1. Elimine los recursos del servidor mTLS de NGINX:

    $ kubectl delete secret nginx-server-certs nginx-ca-certs -n mesh-external
    $ kubectl delete configmap nginx-configmap -n mesh-external
    $ kubectl delete service my-nginx -n mesh-external
    $ kubectl delete deployment my-nginx -n mesh-external
    $ kubectl delete namespace mesh-external
  2. Elimine los recursos de configuración del gateway:

$ kubectl delete secret client-credential -n istio-system
$ kubectl delete gw istio-egressgateway
$ kubectl delete virtualservice direct-nginx-through-egress-gateway
$ kubectl delete destinationrule -n istio-system originate-mtls-for-nginx
$ kubectl delete destinationrule egressgateway-for-nginx
  1. Elimine los certificados y las claves privadas:

    $ rm example.com.crt example.com.key my-nginx.mesh-external.svc.cluster.local.crt my-nginx.mesh-external.svc.cluster.local.key my-nginx.mesh-external.svc.cluster.local.csr client.example.com.crt client.example.com.csr client.example.com.key
  2. Elimine los ficheros de configuración generados utilizados en este ejemplo:

    $ rm ./nginx.conf

Limpieza

Elimine el service y el despliegue curl:

Zip
$ kubectl delete -f @samples/curl/curl.yaml@
¿Fue útil esta información?
¿Tienes alguna sugerencia para mejorar?

¡Gracias por tus comentarios!