Gateways Seguros

La tarea de Control de Tráfico de Ingreso describe cómo configurar un gateway de ingreso para exponer un servicio HTTP al tráfico externo. Esta tarea muestra cómo exponer un servicio HTTPS seguro usando TLS simple o mutuo.

Antes de comenzar

  • Configura Istio siguiendo las instrucciones en la Guía de instalación.

  • Inicia la muestra httpbin:

    Zip
    $ kubectl apply -f @samples/httpbin/httpbin.yaml@
  • Para usuarios de macOS, verifica que uses curl compilado con la biblioteca LibreSSL:

    $ curl --version | grep LibreSSL
    curl 7.54.0 (x86_64-apple-darwin17.0) libcurl/7.54.0 LibreSSL/2.0.20 zlib/1.2.11 nghttp2/1.24.0

    Si el comando anterior produce una versión de LibreSSL como se muestra, tu comando curl debería funcionar correctamente con las instrucciones en esta tarea. De lo contrario, intenta una implementación diferente de curl, por ejemplo en una máquina Linux.

Generar certificados y claves de cliente y servidor

Esta tarea requiere varios conjuntos de certificados y claves que se usan en los siguientes ejemplos. Puedes usar tu herramienta favorita para crearlos o usar los comandos a continuación para generarlos usando openssl.

  1. Crea un certificado raíz y una clave privada para firmar los certificados para tus servicios:

    $ mkdir example_certs1
    $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs1/example.com.key -out example_certs1/example.com.crt
  2. Genera un certificado y una clave privada para httpbin.example.com:

    $ openssl req -out example_certs1/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
    $ openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 0 -in example_certs1/httpbin.example.com.csr -out example_certs1/httpbin.example.com.crt
  3. Crea un segundo conjunto del mismo tipo de certificados y claves:

    $ mkdir example_certs2
    $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example_certs2/example.com.key -out example_certs2/example.com.crt
    $ openssl req -out example_certs2/httpbin.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs2/httpbin.example.com.key -subj "/CN=httpbin.example.com/O=httpbin organization"
    $ openssl x509 -req -sha256 -days 365 -CA example_certs2/example.com.crt -CAkey example_certs2/example.com.key -set_serial 0 -in example_certs2/httpbin.example.com.csr -out example_certs2/httpbin.example.com.crt
  4. Genera un certificado y una clave privada para helloworld.example.com:

    $ openssl req -out example_certs1/helloworld.example.com.csr -newkey rsa:2048 -nodes -keyout example_certs1/helloworld.example.com.key -subj "/CN=helloworld.example.com/O=helloworld organization"
    $ openssl x509 -req -sha256 -days 365 -CA example_certs1/example.com.crt -CAkey example_certs1/example.com.key -set_serial 1 -in example_certs1/helloworld.example.com.csr -out example_certs1/helloworld.example.com.crt
  5. Genera un certificado de cliente y una clave privada:

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

Configurar un gateway de ingreso de TLS para un solo host

  1. Crea un secreto para el gateway de ingreso:

    $ kubectl create -n istio-system secret tls httpbin-credential \
      --key=example_certs1/httpbin.example.com.key \
      --cert=example_certs1/httpbin.example.com.crt
  2. Configura el gateway de ingreso:

Primero, define un gateway con una sección servers: para el puerto 443, y especifica valores para credentialName para ser httpbin-credential. Los valores son los mismos que el nombre del secreto. El modo TLS debe tener el valor de SIMPLE.

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: httpbin-credential # must be the same as secret
    hosts:
    - httpbin.example.com
EOF

Luego, configura las rutas de tráfico de ingreso del gateway definiendo un correspondiente virtual service:

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: httpbin
spec:
  hosts:
  - "httpbin.example.com"
  gateways:
  - mygateway
  http:
  - match:
    - uri:
        prefix: /status
    - uri:
        prefix: /delay
    route:
    - destination:
        port:
          number: 8000
        host: httpbin
EOF

Finalmente, sigue estas instrucciones para establecer las variables INGRESS_HOST y SECURE_INGRESS_PORT para acceder al gateway.

  1. Envía una solicitud HTTPS para acceder al servicio httpbin a través de HTTPS:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    I'm a teapot!
    ...

    El servicio httpbin devolverá el código 418 I’m a Teapot.

  2. Cambia las credenciales del gateway eliminando el secreto del gateway y luego recreándolo usando diferentes certificados y claves:

    $ kubectl -n istio-system delete secret httpbin-credential
    $ kubectl create -n istio-system secret tls httpbin-credential \
      --key=example_certs2/httpbin.example.com.key \
      --cert=example_certs2/httpbin.example.com.crt
  3. Accede al servicio httpbin con curl usando la nueva cadena de certificados:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs2/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    I'm a teapot!
    ...
  4. Si intentas acceder a httpbin usando la cadena de certificados anterior, el intento ahora falla:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    * TLSv1.2 (OUT), TLS handshake, Client hello (1):
    * TLSv1.2 (IN), TLS handshake, Server hello (2):
    * TLSv1.2 (IN), TLS handshake, Certificate (11):
    * TLSv1.2 (OUT), TLS alert, Server hello (2):
    * curl: (35) error:04FFF06A:rsa routines:CRYPTO_internal:block type is not 01

Configurar un gateway de ingreso de TLS para múltiples hosts

Puedes configurar un gateway de ingreso para múltiples hosts, httpbin.example.com y helloworld.example.com, por ejemplo. El gateway de ingreso está configurado con credenciales únicas correspondientes a cada host.

  1. Restaura las credenciales de httpbin del ejemplo anterior eliminando y recreando el secreto con los certificados y claves originales:

    $ kubectl -n istio-system delete secret httpbin-credential
    $ kubectl create -n istio-system secret tls httpbin-credential \
      --key=example_certs1/httpbin.example.com.key \
      --cert=example_certs1/httpbin.example.com.crt
  2. Inicia la muestra helloworld-v1:

    ZipZip
    $ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l service=helloworld
    $ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l version=v1
  3. Crea un secreto helloworld-credential:

    $ kubectl create -n istio-system secret tls helloworld-credential \
      --key=example_certs1/helloworld.example.com.key \
      --cert=example_certs1/helloworld.example.com.crt
  4. Configura el gateway de ingreso con hosts httpbin.example.com y helloworld.example.com:

Define un gateway con dos secciones de servidor para el puerto 443. Establece el valor de credentialName en cada puerto a httpbin-credential y helloworld-credential respectivamente. Establece el modo TLS a SIMPLE.

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https-httpbin
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: httpbin-credential
    hosts:
    - httpbin.example.com
  - port:
      number: 443
      name: https-helloworld
      protocol: HTTPS
    tls:
      mode: SIMPLE
      credentialName: helloworld-credential
    hosts:
    - helloworld.example.com
EOF

Configura las rutas de tráfico del gateway definiendo un correspondiente virtual service.

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: helloworld
spec:
  hosts:
  - helloworld.example.com
  gateways:
  - mygateway
  http:
  - match:
    - uri:
        exact: /hello
    route:
    - destination:
        host: helloworld
        port:
          number: 5000
EOF
  1. Envía una solicitud HTTPS a helloworld.example.com:

    $ curl -v -HHost:helloworld.example.com --resolve "helloworld.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://helloworld.example.com:$SECURE_INGRESS_PORT/hello"
    ...
    HTTP/2 200
    ...
  2. Envía una solicitud HTTPS a httpbin.example.com y aún obtienes HTTP 418 en retorno:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    server: istio-envoy
    ...

Configurar un gateway de ingress de TLS mutuo

Puedes extender la definición del gateway para soportar TLS mutuo.

  1. Cambia las credenciales del gateway de ingreso eliminando su secreto y creando uno nuevo. El servidor usa el certificado de CA para verificar a sus clientes, y debemos usar la clave ca.crt para mantener el certificado de CA.

    $ kubectl -n istio-system delete secret httpbin-credential
    $ kubectl create -n istio-system secret generic httpbin-credential \
      --from-file=tls.key=example_certs1/httpbin.example.com.key \
      --from-file=tls.crt=example_certs1/httpbin.example.com.crt \
      --from-file=ca.crt=example_certs1/example.com.crt
  2. Configura el gateway de ingreso:

Cambia la definición del gateway estableciendo el modo TLS a MUTUAL.

$ cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: mygateway
spec:
  selector:
    istio: ingressgateway # use istio default ingress gateway
  servers:
  - port:
      number: 443
      name: https
      protocol: HTTPS
    tls:
      mode: MUTUAL
      credentialName: httpbin-credential # must be the same as secret
    hosts:
    - httpbin.example.com
EOF
  1. Intenta enviar una solicitud HTTPS usando el enfoque anterior y verás cómo falla:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
    --cacert example_certs1/example.com.crt "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    * TLSv1.3 (OUT), TLS handshake, Client hello (1):
    * TLSv1.3 (IN), TLS handshake, Server hello (2):
    * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
    * TLSv1.3 (IN), TLS handshake, Request CERT (13):
    * TLSv1.3 (IN), TLS handshake, Certificate (11):
    * TLSv1.3 (IN), TLS handshake, CERT verify (15):
    * TLSv1.3 (IN), TLS handshake, Finished (20):
    * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
    * TLSv1.3 (OUT), TLS handshake, Certificate (11):
    * TLSv1.3 (OUT), TLS handshake, Finished (20):
    * TLSv1.3 (IN), TLS alert, unknown (628):
    * OpenSSL SSL_read: error:1409445C:SSL routines:ssl3_read_bytes:tlsv13 alert certificate required, errno 0
  2. Pasa un certificado de cliente y una clave privada a curl y vuelve a enviar la solicitud. Pasa el certificado de su cliente con la bandera --cert y su clave privada con la bandera --key a curl:

    $ curl -v -HHost:httpbin.example.com --resolve "httpbin.example.com:$SECURE_INGRESS_PORT:$INGRESS_HOST" \
      --cacert example_certs1/example.com.crt --cert example_certs1/client.example.com.crt --key example_certs1/client.example.com.key \
      "https://httpbin.example.com:$SECURE_INGRESS_PORT/status/418"
    ...
    HTTP/2 418
    ...
    server: istio-envoy
    ...
    I'm a teapot!
    ...

Más información

Formatos de clave

Istio soporta la lectura de varios formatos diferentes de Secret, para soportar la integración con varias herramientas como cert-manager:

  • Un secreto TLS con claves tls.key y tls.crt, como se describe anteriormente. Para TLS mutuo, una clave ca.crt puede usarse.
  • Un secreto TLS con claves tls.key y tls.crt, como se describe anteriormente. Para TLS mutuo, un secreto genérico separado llamado <secreto>-cacert, con una clave cacert. Por ejemplo, httpbin-credential tiene tls.key y tls.crt, y httpbin-credential-cacert tiene cacert.
  • Un secreto genérico con claves key y cert. Para TLS mutuo, una clave cacert puede usarse.
  • Un secreto genérico con claves key y cert. Para TLS mutuo, un secreto genérico separado llamado <secreto>-cacert, con una clave cacert. Por ejemplo, httpbin-credential tiene key y cert, y httpbin-credential-cacert tiene cacert.
  • El valor de la clave cacert puede ser un paquete de CA consistente en la concatenación de certificados de CA individuales.

Enrutamiento de SNI

Un Gateway HTTPS realizará SNI coincidencia contra sus hosts configurados antes de reenviar una solicitud, lo que puede causar que algunas solicitudes fallen. Ver configuración de enrutamiento de SNI para más detalles.

Solución de problemas

  • Inspecciona los valores de las variables de entorno INGRESS_HOST y SECURE_INGRESS_PORT. Asegúrate de que tengan valores válidos, según la salida de los siguientes comandos:

    $ kubectl get svc -n istio-system
    $ echo "INGRESS_HOST=$INGRESS_HOST, SECURE_INGRESS_PORT=$SECURE_INGRESS_PORT"
  • Asegúrate de que el valor de INGRESS_HOST sea una dirección IP. En algunas plataformas en la nube, como AWS, podrías obtener un nombre de dominio en su lugar. Esta tarea espera una dirección IP, por lo que necesitarás convertirla con comandos similares a los siguientes:

    $ nslookup ab52747ba608744d8afd530ffd975cbf-330887905.us-east-1.elb.amazonaws.com
    $ export INGRESS_HOST=3.225.207.109
  • Verifica el log del controlador de gateway por mensajes de error:

    $ kubectl logs -n istio-system <gateway-service-pod>
  • Si estás usando macOS, verifica que uses curl compilado con la biblioteca LibreSSL, como se describe en la sección Antes de comenzar anterior.

  • Verifica que los secretos se hayan creado correctamente en el namespace istio-system:

    $ kubectl -n istio-system get secrets

    httpbin-credential y helloworld-credential deberían aparecer en la lista de secretos.

  • Verifica los logs para asegurarte de que el agente de gateway de ingreso haya enviado la pareja clave/certificado al gateway de ingreso:

    $ kubectl logs -n istio-system <gateway-service-pod>

    El log debería mostrar que el secreto httpbin-credential fue añadido. Si usas TLS mutuo, entonces el secreto httpbin-credential-cacert también debería aparecer. Verifica que el log muestre que el agente de gateway recibe solicitudes SDS del gateway de ingreso, que el nombre del recurso es httpbin-credential, y que el gateway de ingreso obtiene la pareja clave/certificado. Si usas TLS mutuo, el log debería mostrar clave/certificado fue enviado al gateway de ingreso, que el agente de gateway recibió la solicitud SDS con el nombre de recurso httpbin-credential-cacert, y que el gateway de ingreso obtuvo el certificado raíz.

Limpieza

  1. Elimina la configuración del gateway y las rutas:
$ kubectl delete gateway mygateway
$ kubectl delete virtualservice httpbin helloworld
  1. Elimina los secretos, certificados y claves:

    $ kubectl delete -n istio-system secret httpbin-credential helloworld-credential
    $ rm -rf ./example_certs1 ./example_certs2
  2. Apaga los servicios httpbin y helloworld:

    $ kubectl delete -f samples/httpbin/httpbin.yaml
    $ kubectl delete deployment helloworld-v1
    $ kubectl delete service helloworld
¿Fue útil esta información?
¿Tienes alguna sugerencia para mejorar?

¡Gracias por tus comentarios!