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:
$ kubectl apply -f @samples/httpbin/httpbin.yaml@Para usuarios de macOS, verifica que uses
curlcompilado 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.0Si el comando anterior produce una versión de LibreSSL como se muestra, tu comando
curldebería funcionar correctamente con las instrucciones en esta tarea. De lo contrario, intenta una implementación diferente decurl, 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.
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.crtGenera 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.crtCrea 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.crtGenera 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.crtGenera 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
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.crtConfigura 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
EOFLuego, 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
EOFFinalmente, sigue estas instrucciones
para establecer las variables INGRESS_HOST y SECURE_INGRESS_PORT para acceder al gateway.
Primero, crea un Kubernetes Gateway:
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
namespace: istio-system
spec:
gatewayClassName: istio
listeners:
- name: https
hostname: "httpbin.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: httpbin-credential
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
EOFLuego, configura las rutas de tráfico de ingreso del gateway definiendo un correspondiente HTTPRoute:
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin
spec:
parentRefs:
- name: mygateway
namespace: istio-system
hostnames: ["httpbin.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /status
- path:
type: PathPrefix
value: /delay
backendRefs:
- name: httpbin
port: 8000
EOFFinalmente, obtén la dirección y el puerto del gateway desde el recurso Gateway:
$ kubectl wait --for=condition=programmed gtw mygateway -n istio-system
$ export INGRESS_HOST=$(kubectl get gtw mygateway -n istio-system -o jsonpath='{.status.addresses[0].value}')
$ export SECURE_INGRESS_PORT=$(kubectl get gtw mygateway -n istio-system -o jsonpath='{.spec.listeners[?(@.name=="https")].port}')Envía una solicitud HTTPS para acceder al servicio
httpbina 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
httpbindevolverá el código 418 I’m a Teapot.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.crtAccede al servicio
httpbinconcurlusando 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! ...Si intentas acceder a
httpbinusando 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.
Restaura las credenciales de
httpbindel 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.crtInicia la muestra
helloworld-v1:$ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l service=helloworld $ kubectl apply -f @samples/helloworld/helloworld.yaml@ -l version=v1Crea 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.crtConfigura el gateway de ingreso con hosts
httpbin.example.comyhelloworld.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
EOFConfigura 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
EOFConfigura un Gateway con dos listeners para el puerto 443. Establece el valor de
certificateRefs en cada listener a httpbin-credential y helloworld-credential respectivamente.
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
namespace: istio-system
spec:
gatewayClassName: istio
listeners:
- name: https-httpbin
hostname: "httpbin.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: httpbin-credential
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
- name: https-helloworld
hostname: "helloworld.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: helloworld-credential
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
EOFConfigura las rutas de tráfico del gateway para el servicio helloworld:
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: helloworld
spec:
parentRefs:
- name: mygateway
namespace: istio-system
hostnames: ["helloworld.example.com"]
rules:
- matches:
- path:
type: Exact
value: /hello
backendRefs:
- name: helloworld
port: 5000
EOFEnví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 ...Envía una solicitud HTTPS a
httpbin.example.comy 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.
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.crtpara 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.crtConfigura 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
EOFDebido a que el Kubernetes Gateway API no soporta actualmente el terminado de TLS mutuo en un
Gateway,
usamos una opción específica de Istio, gateway.istio.io/tls-terminate-mode: MUTUAL,
para configurarlo:
$ cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mygateway
namespace: istio-system
spec:
gatewayClassName: istio
listeners:
- name: https
hostname: "httpbin.example.com"
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: httpbin-credential
options:
gateway.istio.io/tls-terminate-mode: MUTUAL
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: default
EOFIntenta 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 0Pasa un certificado de cliente y una clave privada a
curly vuelve a enviar la solicitud. Pasa el certificado de su cliente con la bandera--certy su clave privada con la bandera--keyacurl:$ 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.keyytls.crt, como se describe anteriormente. Para TLS mutuo, una claveca.crtpuede usarse. - Un secreto TLS con claves
tls.keyytls.crt, como se describe anteriormente. Para TLS mutuo, un secreto genérico separado llamado<secreto>-cacert, con una clavecacert. Por ejemplo,httpbin-credentialtienetls.keyytls.crt, yhttpbin-credential-cacerttienecacert. - Un secreto genérico con claves
keyycert. Para TLS mutuo, una clavecacertpuede usarse. - Un secreto genérico con claves
keyycert. Para TLS mutuo, un secreto genérico separado llamado<secreto>-cacert, con una clavecacert. Por ejemplo,httpbin-credentialtienekeyycert, yhttpbin-credential-cacerttienecacert. - El valor de la clave
cacertpuede 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_HOSTySECURE_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_HOSTsea 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.109Verifica 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
curlcompilado 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 secretshttpbin-credentialyhelloworld-credentialdeberí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-credentialfue añadido. Si usas TLS mutuo, entonces el secretohttpbin-credential-cacerttambié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 eshttpbin-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 recursohttpbin-credential-cacert, y que el gateway de ingreso obtuvo el certificado raíz.
Limpieza
- Elimina la configuración del gateway y las rutas:
$ kubectl delete gateway mygateway
$ kubectl delete virtualservice httpbin helloworld$ kubectl delete -n istio-system gtw mygateway
$ kubectl delete httproute httpbin helloworldElimina los secretos, certificados y claves:
$ kubectl delete -n istio-system secret httpbin-credential helloworld-credential $ rm -rf ./example_certs1 ./example_certs2Apaga los servicios
httpbinyhelloworld:$ kubectl delete -f samples/httpbin/httpbin.yaml $ kubectl delete deployment helloworld-v1 $ kubectl delete service helloworld