Egress Gateways
La tarea Acceso a Services Externos muestra cómo configurar Istio para permitir el acceso a services HTTP y HTTPS externos desde applications dentro de la malla. Allí, los services externos se llaman directamente desde el sidecar del cliente. Este ejemplo también muestra cómo configurar Istio para llamar a services externos, aunque esta vez indirectamente a través de un service egress gateway dedicado.
Istio utiliza ingress y egress gateways para configurar balanceadores de carga que se ejecutan en el borde de una service mesh. Un ingress gateway le permite definir puntos de entrada a la malla por los que fluye todo el tráfico entrante. Un egress gateway es un concepto simétrico; define puntos de salida de la malla. Los egress gateways permiten aplicar features de Istio, por ejemplo, monitoreo y reglas de ruta, al tráfico que sale de la malla.
Caso de uso
Considere una organización que tiene un requisito de seguridad estricto de que todo el tráfico que sale de la service mesh debe fluir a través de un conjunto de nodos dedicados. Estos nodos se ejecutarán en máquinas dedicadas, separadas del resto de los nodos que ejecutan applications en el cluster. Estos nodos especiales servirán para la aplicación de políticas en el tráfico de salida y serán monitoreados más a fondo que otros nodos.
Otro caso de uso es un cluster donde los nodos de la aplicación no tienen IPs públicas, por lo que los services en malla que se ejecutan en ellos no pueden acceder a Internet. Definir un egress gateway, dirigir todo el tráfico de salida a través de él y asignar IPs públicas a los nodos del egress gateway permite que los nodos de la aplicación accedan a services externos de forma controlada.
Antes de empezar
Configure Istio siguiendo las instrucciones de la guía de instalación.
Despliegue la aplicación de ejemplo curl para usarla como fuente de prueba para enviar solicitudes.
$ kubectl apply -f @samples/curl/curl.yaml@
Establezca la variable de entorno
SOURCE_POD
con el nombre de su pod de origen:$ export SOURCE_POD=$(kubectl get pod -l app=curl -o jsonpath={.items..metadata.name})
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
Desplegar Istio egress gateway
Verifique si el egress gateway de Istio está desplegado:
$ kubectl get pod -l istio=egressgateway -n istio-system
Si no se devuelven pods, despliegue el egress gateway de Istio realizando el siguiente paso.
Si utilizó una configuración de
IstioOperator
para instalar Istio, agregue los siguientes campos a su configuración:spec: components: egressGateways: - name: istio-egressgateway enabled: true
De lo contrario, agregue la configuración equivalente a su comando
istioctl install
original, por ejemplo:$ istioctl install <flags-you-used-to-install-Istio> \ --set "components.egressGateways[0].name=istio-egressgateway" \ --set "components.egressGateways[0].enabled=true"
Egress gateway para tráfico HTTP
Primero cree una ServiceEntry
para permitir el tráfico directo a un service externo.
Defina una
ServiceEntry
paraedition.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-port protocol: HTTP - number: 443 name: https protocol: HTTPS resolution: DNS EOF
Verifique que su
ServiceEntry
se aplicó correctamente enviando 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 301 Moved Permanently ... location: https://edition.cnn.com/politics ... HTTP/2 200 Content-Type: text/html; charset=utf-8 ...
La salida debería ser la misma que en el ejemplo de TLS origination para Tráfico de Salida, sin TLS origination.
Cree un
Gateway
para el tráfico de salida al puerto 80 de edition.cnn.com.
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: istio-egressgateway
spec:
selector:
istio: egressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- edition.cnn.com
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: egressgateway-for-cnn
spec:
host: istio-egressgateway.istio-system.svc.cluster.local
subsets:
- name: cnn
EOF
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cnn-egress-gateway
annotations:
networking.istio.io/service-type: ClusterIP
spec:
gatewayClassName: istio
listeners:
- name: http
hostname: edition.cnn.com
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
EOF
- Configure reglas de ruta para dirigir el tráfico de los sidecars al egress gateway y del egress gateway al service externo:
$ 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: 80
weight: 100
EOF
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: direct-cnn-to-egress-gateway
spec:
parentRefs:
- kind: ServiceEntry
group: networking.istio.io
name: cnn
rules:
- backendRefs:
- name: cnn-egress-gateway-istio
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: forward-cnn-from-egress-gateway
spec:
parentRefs:
- name: cnn-egress-gateway
hostnames:
- edition.cnn.com
rules:
- backendRefs:
- kind: Hostname
group: networking.istio.io
name: edition.cnn.com
port: 80
EOF
Reenvíe la 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 301 Moved Permanently ... location: https://edition.cnn.com/politics ... HTTP/2 200 Content-Type: text/html; charset=utf-8 ...
La salida debería ser la misma que en el paso 2.
Verifique el registro del pod del egress gateway para una línea correspondiente a nuestra solicitud.
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:
[2019-09-03T20:57:49.103Z] "GET /politics HTTP/2" 301 - "-" "-" 0 0 90 89 "10.244.2.10" "curl/7.64.0" "ea379962-9b5c-4431-ab66-f01994f5a5a5" "edition.cnn.com" "151.101.65.67:80" outbound|80||edition.cnn.com - 10.244.1.5:80 10.244.2.10:50482 edition.cnn.com -
Acceda al registro correspondiente al egress gateway utilizando la etiqueta de pod generada por Istio:
$ kubectl logs -l gateway.networking.k8s.io/gateway-name=cnn-egress-gateway -c istio-proxy | tail
Debería ver una línea similar a la siguiente:
[2024-01-09T15:35:47.283Z] "GET /politics HTTP/1.1" 301 - via_upstream - "-" 0 0 2 2 "172.30.239.55" "curl/7.87.0-DEV" "6c01d65f-a157-97cd-8782-320a40026901" "edition.cnn.com" "151.101.195.5:80" outbound|80||edition.cnn.com 172.30.239.16:55636 172.30.239.16:80 172.30.239.55:59224 - default.forward-cnn-from-egress-gateway.0
Tenga en cuenta que solo redirigió el tráfico HTTP del puerto 80 a través del egress gateway. El tráfico HTTPS al puerto 443 fue directamente a edition.cnn.com.
Limpieza del gateway HTTP
Elimine las definiciones anteriores antes de pasar al siguiente paso:
$ kubectl delete serviceentry cnn
$ kubectl delete gateway istio-egressgateway
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule egressgateway-for-cnn
$ kubectl delete serviceentry cnn
$ kubectl delete gtw cnn-egress-gateway
$ kubectl delete httproute direct-cnn-to-egress-gateway
$ kubectl delete httproute forward-cnn-from-egress-gateway
Egress gateway para tráfico HTTPS
En esta sección, dirigirá el tráfico HTTPS (TLS originado por la aplicación) a través de un egress gateway.
Debe especificar el puerto 443 con el protocolo TLS
en una ServiceEntry
y un Gateway
de salida correspondientes.
Defina una
ServiceEntry
paraedition.cnn.com
:$ kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: ServiceEntry metadata: name: cnn spec: hosts: - edition.cnn.com ports: - number: 443 name: tls protocol: TLS resolution: DNS EOF
Verifique que su
ServiceEntry
se aplicó correctamente enviando una solicitud HTTPS a https://edition.cnn.com/politics.$ kubectl exec "$SOURCE_POD" -c curl -- curl -sSL -o /dev/null -D - https://edition.cnn.com/politics ... HTTP/2 200 Content-Type: text/html; charset=utf-8 ...
Cree un
Gateway
de salida para edition.cnn.com y reglas de ruta para dirigir el tráfico a través del egress gateway y del egress gateway al service externo.
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: istio-egressgateway
spec:
selector:
istio: egressgateway
servers:
- port:
number: 443
name: tls
protocol: TLS
hosts:
- edition.cnn.com
tls:
mode: PASSTHROUGH
---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: egressgateway-for-cnn
spec:
host: istio-egressgateway.istio-system.svc.cluster.local
subsets:
- name: cnn
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: direct-cnn-through-egress-gateway
spec:
hosts:
- edition.cnn.com
gateways:
- mesh
- istio-egressgateway
tls:
- match:
- gateways:
- mesh
port: 443
sniHosts:
- edition.cnn.com
route:
- destination:
host: istio-egressgateway.istio-system.svc.cluster.local
subset: cnn
port:
number: 443
- match:
- gateways:
- istio-egressgateway
port: 443
sniHosts:
- edition.cnn.com
route:
- destination:
host: edition.cnn.com
port:
number: 443
weight: 100
EOF
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: cnn-egress-gateway
annotations:
networking.istio.io/service-type: ClusterIP
spec:
gatewayClassName: istio
listeners:
- name: tls
hostname: edition.cnn.com
port: 443
protocol: TLS
tls:
mode: Passthrough
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: direct-cnn-to-egress-gateway
spec:
parentRefs:
- kind: ServiceEntry
group: networking.istio.io
name: cnn
rules:
- backendRefs:
- name: cnn-egress-gateway-istio
port: 443
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
name: forward-cnn-from-egress-gateway
spec:
parentRefs:
- name: cnn-egress-gateway
hostnames:
- edition.cnn.com
rules:
- backendRefs:
- kind: Hostname
group: networking.istio.io
name: edition.cnn.com
port: 443
EOF
Envíe una solicitud HTTPS a https://edition.cnn.com/politics. La salida debería ser la misma que antes.
$ kubectl exec "$SOURCE_POD" -c curl -- curl -sSL -o /dev/null -D - https://edition.cnn.com/politics ... HTTP/2 200 Content-Type: text/html; charset=utf-8 ...
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
Debería ver una línea similar a la siguiente:
[2019-01-02T11:46:46.981Z] "- - -" 0 - 627 1879689 44 - "-" "-" "-" "-" "151.101.129.67:443" outbound|443||edition.cnn.com 172.30.109.80:41122 172.30.109.80:443 172.30.109.112:59970 edition.cnn.com
Acceda al registro correspondiente al egress gateway utilizando la etiqueta de pod generada por Istio:
$ kubectl logs -l gateway.networking.k8s.io/gateway-name=cnn-egress-gateway -c istio-proxy | tail
Debería ver una línea similar a la siguiente:
[2024-01-11T21:09:42.835Z] "- - -" 0 - - - "-" 839 2504306 231 - "-" "-" "-" "-" "151.101.195.5:443" outbound|443||edition.cnn.com 172.30.239.8:34470 172.30.239.8:443 172.30.239.15:43956 edition.cnn.com -
Limpieza del gateway HTTPS
$ kubectl delete serviceentry cnn
$ kubectl delete gateway istio-egressgateway
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule egressgateway-for-cnn
$ kubectl delete serviceentry cnn
$ kubectl delete gtw cnn-egress-gateway
$ kubectl delete tlsroute direct-cnn-to-egress-gateway
$ kubectl delete tlsroute forward-cnn-from-egress-gateway
Consideraciones de seguridad adicionales
Tenga en cuenta que la definición de un Gateway
de salida en Istio no proporciona por sí misma ningún tratamiento especial para los nodos
en los que se ejecuta el service del egress gateway. Depende del administrador del cluster o del proveedor de la nube desplegar
los egress gateways en nodos dedicados e introducir medidas de seguridad adicionales para hacer que estos nodos sean más
seguros que el resto de la malla.
Istio no puede garantizar de forma segura que todo el tráfico de salida fluya realmente a través de los egress gateways. Istio solo habilita dicho flujo a través de sus proxies sidecar. Si los atacantes eluden el proxy sidecar, podrían acceder directamente a services externos sin atravesar el egress gateway. Así, los atacantes escapan del control y monitoreo de Istio. El administrador del cluster o el proveedor de la nube deben asegurarse de que ningún tráfico salga de la malla sin pasar por el egress gateway. Los mecanismos externos a Istio deben hacer cumplir este requisito. Por ejemplo, el administrador del cluster puede configurar un firewall para denegar todo el tráfico que no provenga del egress gateway. Las políticas de red de Kubernetes también pueden prohibir todo el tráfico de salida que no se origine en el egress gateway (consulte la siguiente sección para ver un ejemplo). Además, el administrador del cluster o el proveedor de la nube pueden configurar la red para asegurar que los nodos de la aplicación solo puedan acceder a Internet a través de un gateway. Para ello, el administrador del cluster o el proveedor de la nube pueden evitar la asignación de IPs públicas a pods que no sean gateways y pueden configurar dispositivos NAT para descartar paquetes que no se originen en los egress gateways.
Aplicar políticas de red de Kubernetes
Esta sección muestra cómo crear una
política de red de Kubernetes para evitar
la elusión del egress gateway. Para probar la política de red, creará un namespace, test-egress
, desplegará
la muestra curl en él, y luego intentará enviar solicitudes a un service externo
protegido por gateway.
Siga los pasos de la sección Egress gateway para tráfico HTTPS.
Cree el namespace
test-egress
:$ kubectl create namespace test-egress
Despliegue la muestra curl en el namespace
test-egress
.$ kubectl apply -n test-egress -f @samples/curl/curl.yaml@
Verifique que el pod desplegado tiene un solo contenedor sin sidecar de Istio adjunto:
$ kubectl get pod "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress NAME READY STATUS RESTARTS AGE curl-776b7bcdcd-z7mc4 1/1 Running 0 18m
Envíe una solicitud HTTPS a https://edition.cnn.com/politics desde el pod
curl
en el namespacetest-egress
. La solicitud tendrá éxito ya que aún no ha definido ninguna política restrictiva.$ kubectl exec "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress -c curl -- curl -s -o /dev/null -w "%{\http_code}\n" https://edition.cnn.com/politics 200
Etiquete los namespaces donde se ejecutan el control plane de Istio y el egress gateway. Si desplegó Istio en el namespace
istio-system
, el comando es:
$ kubectl label namespace istio-system istio=system
$ kubectl label namespace istio-system istio=system
$ kubectl label namespace default gateway=true
Etiquete el namespace
kube-system
.$ kubectl label ns kube-system kube-system=true
Defina una
NetworkPolicy
para limitar el tráfico de salida del namespacetest-egress
al tráfico destinado a el control plane, el gateway y el service DNS dekube-system
(puerto 53).
$ cat <<EOF | kubectl apply -n test-egress -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-to-istio-system-and-kube-dns
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kube-system: "true"
ports:
- protocol: UDP
port: 53
- to:
- namespaceSelector:
matchLabels:
istio: system
EOF
$ cat <<EOF | kubectl apply -n test-egress -f -
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-to-istio-system-and-kube-dns
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kube-system: "true"
ports:
- protocol: UDP
port: 53
- to:
- namespaceSelector:
matchLabels:
istio: system
- to:
- namespaceSelector:
matchLabels:
gateway: "true"
EOF
Reenvíe la solicitud HTTPS anterior a https://edition.cnn.com/politics. Ahora debería fallar ya que el tráfico está bloqueado por la política de red. Tenga en cuenta que el pod
curl
no puede eludir el egress gateway. La única forma en que puede acceder aedition.cnn.com
es utilizando un proxy sidecar de Istio y dirigiendo el tráfico al egress gateway. Esta configuración demuestra que incluso si un pod malicioso logra eludir su proxy sidecar, no podrá acceder a sitios externos y será bloqueado por la política de red.$ kubectl exec "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress -c curl -- curl -v -sS https://edition.cnn.com/politics Hostname was NOT found in DNS cache Trying 151.101.65.67... Trying 2a04:4e42:200::323... Immediate connect fail for 2a04:4e42:200::323: Cannot assign requested address Trying 2a04:4e42:400::323... Immediate connect fail for 2a04:4e42:400::323: Cannot assign requested address Trying 2a04:4e42:600::323... Immediate connect fail for 2a04:4e42:600::323: Cannot assign requested address Trying 2a04:4e42::323... Immediate connect fail for 2a04:4e42::323: Cannot assign requested address connect to 151.101.65.67 port 443 failed: Connection timed out
Ahora inyecte un proxy sidecar de Istio en el pod
curl
en el namespacetest-egress
habilitando primero la inyección automática de proxy sidecar en el namespacetest-egress
:$ kubectl label namespace test-egress istio-injection=enabled
Luego vuelva a desplegar el despliegue
curl
:$ kubectl delete deployment curl -n test-egress $ kubectl apply -f @samples/curl/curl.yaml@ -n test-egress
Verifique que el pod desplegado tiene dos contenedores, incluido el proxy sidecar de Istio (
istio-proxy
):
$ kubectl get pod "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress -o jsonpath='{.spec.containers[*].name}'
curl istio-proxy
Antes de continuar, deberá crear una regla de destino similar a la utilizada para el pod curl
en el namespace default
,
para dirigir el tráfico del namespace test-egress
a través del egress gateway:
$ kubectl apply -n test-egress -f - <<EOF
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: egressgateway-for-cnn
spec:
host: istio-egressgateway.istio-system.svc.cluster.local
subsets:
- name: cnn
EOF
$ kubectl get pod "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress -o jsonpath='{.spec.containers[*].name}'
curl istio-proxy
Envíe una solicitud HTTPS a https://edition.cnn.com/politics. Ahora debería tener éxito ya que el tráfico que fluye al egress gateway está permitido por la Política de Red que definió. El gateway luego reenvía el tráfico a
edition.cnn.com
.$ kubectl exec "$(kubectl get pod -n test-egress -l app=curl -o jsonpath={.items..metadata.name})" -n test-egress -c curl -- curl -sS -o /dev/null -w "%{\http_code}\n" https://edition.cnn.com/politics 200
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
Debería ver una línea similar a la siguiente:
[2020-03-06T18:12:33.101Z] "- - -" 0 - "-" "-" 906 1352475 35 - "-" "-" "-" "-" "151.101.193.67:443" outbound|443||edition.cnn.com 172.30.223.53:39460 172.30.223.53:443 172.30.223.58:38138 edition.cnn.com -
Acceda al registro correspondiente al egress gateway utilizando la etiqueta de pod generada por Istio:
$ kubectl logs -l gateway.networking.k8s.io/gateway-name=cnn-egress-gateway -c istio-proxy | tail
Debería ver una línea similar a la siguiente:
[2024-01-12T19:54:01.821Z] "- - -" 0 - - - "-" 839 2504306 231 - "-" "-" "-" "-" "151.101.67.5:443" outbound|443||edition.cnn.com 172.30.239.60:49850 172.30.239.60:443 172.30.239.21:36512 edition.cnn.com -
Limpieza de políticas de red
- Elimine los recursos creados en esta sección:
$ kubectl delete -f @samples/curl/curl.yaml@ -n test-egress
$ kubectl delete destinationrule egressgateway-for-cnn -n test-egress
$ kubectl delete networkpolicy allow-egress-to-istio-system-and-kube-dns -n test-egress
$ kubectl label namespace kube-system kube-system-
$ kubectl label namespace istio-system istio-
$ kubectl delete namespace test-egress
$ kubectl delete -f @samples/curl/curl.yaml@ -n test-egress
$ kubectl delete networkpolicy allow-egress-to-istio-system-and-kube-dns -n test-egress
$ kubectl label namespace kube-system kube-system-
$ kubectl label namespace istio-system istio-
$ kubectl label namespace default gateway-
$ kubectl delete namespace test-egress
- Siga los pasos de la sección Limpieza del gateway HTTPS.
Limpieza
Apague el service curl:
$ kubectl delete -f @samples/curl/curl.yaml@