En este instructivo, se describe cómo usar las puertas de enlace de entrada y salida de Anthos Service Mesh para ayudar a proteger el tráfico entre clústeres mediante la seguridad de la capa de transporte mutua (mTLS). El instructivo está dirigido a los administradores de clústeres de Kubernetes responsables de los aspectos de red, de seguridad y de plataforma. Los controles que se describen aquí pueden ser especialmente útiles para organizaciones con requisitos de seguridad muy estrictos o a fin de cumplir con los requisitos regulatorios. Este instructivo está acompañado por una guía de conceptos complementaria.
En este instructivo, se supone que estás familiarizado con Kubernetes y Anthos Service Mesh.
Objetivos
- Usar Terraform para configurar la infraestructura:
- Crear una red de VPC personalizada con dos subredes privadas.
- Crear dos clústeres de Kubernetes con Anthos Service Mesh habilitada:
- Un clúster de GKE
- Un clúster de operaciones de Kubernetes (kOps) que se ejecutan en la red de VPC personalizada
- Registrar clústeres en GKE Hub
- Implementar un cliente MySQL en un clúster de GKE.
- Implementar un servidor MySQL en un clúster de kOps
- Configurar las puertas de enlace de entrada y salida para exponer un servidor mediante mTLS.
- Probar el acceso a un servidor MySQL mediante un cliente MySQL que se ejecute en diferentes clústeres o VPC.
Costos
En este documento, usarás los siguientes componentes facturables de Google Cloud:
- Google Kubernetes Engine (GKE)
- Compute Engine
- Container Registry
- Anthos Service Mesh
- Herramientas de redes de Cloud y balanceo de cargas
Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios.
Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.
Antes de comenzar
Para este instructivo, necesitas un proyecto de Cloud. Puedes crear uno nuevo o seleccionar un proyecto que ya hayas creado:
-
En la consola de Google Cloud, ve a la página del selector de proyectos.
-
Selecciona o crea un proyecto de Google Cloud.
-
Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.
En la consola de Google Cloud, ve a Cloud Shell.
En la parte inferior de la consola de Google Cloud, se abre una sesión de Cloud Shell en la que se muestra una ventana emergente de línea de comandos. Cloud Shell es un entorno de shell con la CLI de Google Cloud ya instalada, incluido Google Cloud CLI . La sesión puede tardar unos segundos en inicializarse.
- En Cloud Shell, asegúrate de estar trabajando en el proyecto que creaste o seleccionaste:
export PROJECT_ID=PROJECT_ID gcloud config set project ${PROJECT_ID}
Reemplaza
PROJECT_ID
con el ID del proyecto. - Crea una variable de entorno para la dirección de correo electrónico que usas en Google Cloud:
export GOOGLE_CLOUD_EMAIL_ADDRESS=GOOGLE_CLOUD_EMAIL_ADDRESS
Reemplaza
GOOGLE_CLOUD_EMAIL_ADDRESS
por la dirección de correo electrónico que usas en Google Cloud. - Establece la región y la zona para tus recursos de procesamiento:
export REGION=us-central1 export ZONE=us-central1-b gcloud config set compute/region ${REGION} gcloud config set compute/zone ${ZONE}
En este instructivo, se usa
us-central1
para la región yus-central1-b
para la zona. Puedes implementar en una región de tu elección. - Configura las funciones de administración de identidades y accesos (IAM) requeridas. Si eres el propietario del proyecto, tienes todos los permisos necesarios para completar la instalación. De lo contrario, pídele al administrador que te otorgue las funciones de administración de identidades y accesos (IAM) mediante la ejecución del siguiente comando en Cloud Shell:
ROLES=( 'roles/container.admin' \ 'roles/gkehub.admin' \ 'roles/iam.serviceAccountAdmin' \ 'roles/iam.serviceAccountKeyAdmin' \ 'roles/resourcemanager.projectIamAdmin' \ 'roles/compute.securityAdmin' \ 'roles/compute.instanceAdmin' \ 'roles/storage.admin' \ 'roles/serviceusage.serviceUsageAdmin' ) for role in "${ROLES[@]}" do gcloud projects add-iam-policy-binding ${PROJECT_ID} \ --member "user:${GOOGLE_CLOUD_EMAIL_ADDRESS}" \ --role="$role" done
- Habilita las API necesarias para el instructivo:
gcloud services enable \ anthos.googleapis.com \ anthosgke.googleapis.com \ anthosaudit.googleapis.com \ compute.googleapis.com \ container.googleapis.com \ cloudresourcemanager.googleapis.com \ serviceusage.googleapis.com \ stackdriver.googleapis.com \ monitoring.googleapis.com \ logging.googleapis.com \ cloudtrace.googleapis.com \ meshca.googleapis.com \ meshconfig.googleapis.com \ iamcredentials.googleapis.com \ gkeconnect.googleapis.com \ gkehub.googleapis.com
Prepare su entorno
En Cloud Shell, clona el siguiente repositorio:
git clone https://github.com/GoogleCloudPlatform/anthos-service-mesh-samples cd anthos-service-mesh-samples/docs/mtls-egress-ingress
Actualiza Terraform para tu entorno. De forma predeterminada, la consola de Cloud incluye Terraform 0.12. En este instructivo, suponemos que tienes instalado Terraform 0.13.5 o una versión posterior. Puedes usar otra versión de Terraform de forma temporal mediante la ejecución de los siguientes comandos:
mkdir ~/bin curl https://releases.hashicorp.com/terraform/0.13.5/terraform_0.13.5_linux_amd64.zip -o ~/bin/terraform.zip unzip ~/bin/terraform.zip -d ~/bin/
Ve a la subcarpeta
terraform
y, luego, inicializa Terraform:cd terraform/ ~/bin/terraform init
Crea un archivo
terraform.tfvars
(basado en las variables de entorno que creaste antes):cat << EOF > terraform.tfvars project_id = "${PROJECT_ID}" region = "${REGION}" zones = ["${ZONE}"] EOF
En el siguiente paso, crearás la infraestructura inicial. A fin de hacerlo, debes crear y aplicar el plan de ejecución de Terraform para esta configuración. Las secuencias de comandos y los módulos de este plan crean lo siguiente:
- Una red de VPC personalizada con dos subredes privadas
- Dos clústeres de Kubernetes con Anthos Service Mesh habilitado
- Un clúster de GKE
- Un clúster de kOps que se ejecuta en la red de VPC personalizada
El plan de ejecución también registra clústeres en GKE Hub.
Ejecuta el plan de ejecución:
~/bin/terraform plan -out mtls-terraform-plan ~/bin/terraform apply "mtls-terraform-plan"
El resultado es similar al siguiente:
Apply complete! Resources: 27 added, 0 changed, 0 destroyed. Outputs: server_token = <sensitive>
La parte
<sensitive>
es una variable de resultado de Terraform que no se muestra en la consola, pero que se puede consultar. Por ejemplo,~/bin/terraform output server_token
.Obtén el archivo
kubeconfig
del clúster de tu servidor desde el directorioterraform
. Luego, debes combinarlo con el archivo de configuraciónclient-cluster
:cd .. export KUBECONFIG=client-server-kubeconfig cp ./terraform/server-kubeconfig $KUBECONFIG gcloud container clusters get-credentials client-cluster --zone ${ZONE} --project ${PROJECT_ID}
El archivo
client-server-kubeconfig
ahora contiene la configuración de ambos clústeres, que puedes verificar si ejecutas el siguiente comando:kubectl config view -ojson | jq -r '.clusters[].name'
Este es el resultado:
gke_PROJECT_ID_us-central1-c_client-cluster server-cluster.k8s.local
Obtén el contexto de los dos clústeres para usarlo más adelante:
export CLIENT_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep client) export SERVER_CLUSTER=$(kubectl config view -ojson | jq -r '.clusters[].name' | grep server) echo -e "${CLIENT_CLUSTER}\n${SERVER_CLUSTER}"
El resultado es (otra vez) el siguiente:
gke_PROJECT_ID_us-central1-c_client-cluster server-cluster.k8s.local
Ahora puedes usar estos nombres de clúster como contexto para otros comandos de
kubectl
.Haz referencia al clúster del cliente:
kubectl --context ${CLIENT_CLUSTER} get pods -n istio-system
Haz referencia al clúster del servidor:
kubectl --context ${SERVER_CLUSTER} get pods -n istio-system
Configura el cliente
Como se mencionó en la guía de conceptos, del lado del cliente, se requiere que configures la puerta de enlace de salida en Anthos Service Mesh.
En esta sección, debes configurar los elementos de Anthos Service Mesh a fin de identificar el tráfico externo según su origen y usar un certificado personalizado para encriptar la comunicación. Además, solo quieres enrutar ese tráfico de forma específica a su destino (la base de datos de MySQL en un contenedor). Por lo general, se hace mediante un servicio en Kubernetes. En este caso, debes capturar ese tráfico dentro de la comunicación de malla. Para capturar el tráfico, usa los elementos de Istio a fin de crear una definición especial del servicio. Debes definir los siguientes elementos:
- Puerta de enlace de salida
- Entrada de servicio
- Servicio virtual
- Certificados TLS (como un secreto)
- Reglas de destino
En Cloud Shell, obtén la dirección IP de la puerta de enlace de entrada del servidor mediante una consulta a la dirección IP del balanceador de cargas del servicio
istio-ingressgateway
mediante el contexto del servidor ($SERVER_CLUSTER
, que creó antes):INGRESS_HOST=$(kubectl -n istio-system --context ${SERVER_CLUSTER} get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
Debido a que
INGRESS_HOST
es solo la parte de la dirección IP del host, debes crear un nombre de dominio completamente calificado (FQDN). Este paso es necesario porque, para funcionar de forma correcta, los certificados requieren un nombre de dominio.En este instructivo, se usa el servicio de DNS comodín nip.io a fin de crear un FQDN para la dirección IP de entrada. Este servicio te permite crear el FQDN sin tener un dominio.
Almacene la URL del servicio FQDN en una variable de entorno:
export SERVICE_URL="${INGRESS_HOST}.nip.io"
Ahora, con el
SERVICE_URL
definido como FQDN, puedes comenzar a definir la parte de Istio del clúster del cliente.
Crea la puerta de enlace de salida
Primero, crea la puerta de enlace de salida para escuchar el tráfico destinado al servicio externo.
En Cloud Shell, crea el siguiente archivo YAML y asígnale el nombre
client-egress-gateway.yaml
:cat <<EOF > client-egress-gateway.yaml apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: istio-egressgateway-mysql spec: selector: istio: egressgateway servers: - port: number: 15443 name: tls protocol: TLS hosts: - $SERVICE_URL tls: mode: ISTIO_MUTUAL EOF
Aplica el archivo YAML anterior al clúster del cliente:
kubectl --context ${CLIENT_CLUSTER} apply -f client-egress-gateway.yaml
Presta atención a los puertos. Usaste los puertos
default
aquí para el interruptor de los servidores de salida, que es15443
. Si deseas usar un puerto diferente, debes editar el objetoservice
de la puerta de enlace de salida para agregar los puertos personalizados.El interruptor
hosts
define el extremo, que es adonde debe dirigirse el tráfico.
Define la entrada de servicio
El siguiente paso es informar a la malla de servicios sobre el servicio externo. Istio tiene su propio registro, en el que se almacenan extremos de servicio para la malla. Si Istio se instala en Kubernetes, los servicios definidos en el clúster se agregan de forma automática al registro de Istio. Con la definición de la entrada de servicio, agregas un extremo nuevo al registro de Istio, como se muestra en el siguiente diagrama.
En Cloud Shell, crea el siguiente archivo YAML y asígnale el nombre
client-service-entry.yaml
:cat <<EOF > client-service-entry.yaml apiVersion: networking.istio.io/v1alpha3 kind: ServiceEntry metadata: name: mysql-external spec: hosts: - $SERVICE_URL location: MESH_EXTERNAL ports: - number: 3306 name: tcp protocol: TCP - number: 13306 name: tls protocol: TLS resolution: DNS endpoints: - address: $SERVICE_URL ports: tls: 13306 EOF
Aplica el archivo YAML anterior al clúster del cliente:
kubectl --context ${CLIENT_CLUSTER} apply -f client-service-entry.yaml
La definición del servicio del cliente en este archivo YAML le indica al servicio qué tipo de tráfico esperar (MySQL L4: capa cuatro de red, mediante el puerto 3306). También debes definir que la comunicación será “malla externa”. En la sección de extremos, debes definir que el flujo debe ir hacia la dirección FQDN
$SERVICE_URL
que configuraste antes y que se asigna a la puerta de enlace de entrada en el clúster del servidor (kOps).
Define el servicio virtual
Un servicio virtual es un conjunto de reglas de enrutamiento de tráfico que se aplican cuando se dirige un host. Cada regla de enrutamiento define los criterios de coincidencia para el tráfico de un protocolo específico. Si se detecta una coincidencia con el tráfico, se envía a un servicio de destino con nombre (o a un subconjunto o una versión) que se define en el registro. Para obtener más información, consulta la documentación de Istio.
La definición del servicio virtual le indica a Istio cómo aplicar el enrutamiento del tráfico que llega al servicio externo. Con la siguiente definición, le indicas a la malla que enrute el tráfico del cliente a la puerta de enlace de salida en el puerto 15443
. Desde la puerta de enlace de salida, enruta el tráfico al host $SERVICE_URL
en el puerto 13306
(donde la puerta de enlace de entrada del servidor escucha).
Crea el siguiente archivo YAML y asígnale el nombre
client-virtual-service.yaml
:cat <<EOF > client-virtual-service.yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: direct-mysql-through-egress-gateway spec: hosts: - $SERVICE_URL gateways: - istio-egressgateway-mysql - mesh tcp: - match: - gateways: - mesh port: 3306 route: - destination: host: istio-egressgateway.istio-system.svc.cluster.local subset: mysql port: number: 15443 weight: 100 - match: - gateways: - istio-egressgateway-mysql port: 15443 route: - destination: host: $SERVICE_URL port: number: 13306 weight: 100 EOF
Aplica la definición YAML al clúster cliente:
kubectl --context ${CLIENT_CLUSTER} apply -f client-virtual-service.yaml
Para especificar a qué puertas de enlace se debe aplicar la configuración, edita el interruptor
gateways
en el archivo YAML.La parte importante de esta definición es el uso de la palabra reservada
mesh
, que implica todos los archivos adicionales en la malla. Según la documentación de Istio, cuando se omite este campo, se usa la puerta de enlace predeterminada (malla) y se aplica la regla a todos los archivos adicionales en la malla. Si proporcionas una lista de nombres de puertas de enlace, las reglas se aplican solo a las puertas de enlace. Para aplicar las reglas a las puertas de enlace y los archivos adicionales, especificamesh
como uno de los nombres de la puerta de enlace.
En la siguiente sección, se define cómo controlar el tráfico que sale del proxy de producción del cliente, match.gateways.mesh
. También puedes definir cómo enrutar el tráfico desde la salida al servicio externo mediante el interruptor match.gateways.istio-egressgateway-mysql
.
Define una regla de destino (del cliente a la puerta de enlace de salida)
Ahora que definiste cómo enrutar el tráfico al servicio externo, debes definir qué políticas de tráfico se deben aplicar. El servicio virtual que acabas de definir controla dos casos de enrutamiento a la vez. Uno controla el tráfico desde el proxy de sidecar hasta la puerta de enlace de salida y el otro controla el tráfico desde la puerta de enlace de salida al servicio externo.
Para hacer coincidir estos casos con las reglas de destino, necesitas dos reglas por separado. En el siguiente diagrama, se muestra la primera regla, que controla el tráfico del proxy a la puerta de enlace de salida. En esta definición, le indicas a Anthos Service Mesh que use sus certificados predeterminados para la comunicación con mTLS.
En Cloud Shell, crea el siguiente archivo YAML y asígnale el nombre
client-destination-rule-to-egress-gateway.yaml
:cat <<EOF > client-destination-rule-to-egress-gateway.yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: egressgateway-for-mysql spec: host: istio-egressgateway.istio-system.svc.cluster.local subsets: - name: mysql trafficPolicy: loadBalancer: simple: ROUND_ROBIN portLevelSettings: - port: number: 15443 tls: mode: ISTIO_MUTUAL sni: $SERVICE_URL EOF
Aplica la definición YAML anterior al clúster del cliente:
kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-egress-gateway.yaml
En el archivo YAML anterior, usaste el interruptor de
hosts
para definir cómo enrutar el tráfico desde proxy de cliente hasta la puerta de enlace de salida. Además, configuraste la malla para que useISTIO_MUTUAL
en el puerto15443
, que es uno de los puertos que se abren automáticamente en la puerta de enlace de salida.
Crea una regla de destino (de la puerta de enlace de salida a un servicio externo)
En el siguiente diagrama, se muestra la segunda regla de destino, que indica a la malla cómo controlar el tráfico desde la puerta de enlace de salida hasta el servicio externo.
Debes indicarle a la malla que use tus certificados incorporados para la comunicación de TLS mutua con el servicio externo.
En Cloud Shell, desde el directorio
anthos-service-mesh-samples/docs/mtls-egress-ingress
, crea los certificados:./create-keys.sh
Asegúrate de proporcionar una contraseña cuando la secuencia de comandos la solicite.
Copia los archivos generados en el directorio actual:
cp ./certs/2_intermediate/certs/ca-chain.cert.pem ca-chain.cert.pem cp ./certs/4_client/private/$SERVICE_URL.key.pem client-$SERVICE_URL.key.pem cp ./certs/4_client/certs/$SERVICE_URL.cert.pem client-$SERVICE_URL.cert.pem
Crea un secreto de Kubernetes que almacene los certificados para que se pueda hacer referencia a ellos más adelante en la puerta de enlace:
kubectl --context ${CLIENT_CLUSTER} create secret -n istio-system \ generic client-credential \ --from-file=tls.key=client-$SERVICE_URL.key.pem \ --from-file=tls.crt=client-$SERVICE_URL.cert.pem \ --from-file=ca.crt=ca-chain.cert.pem
El comando anterior agrega los siguientes archivos de certificado al secreto:
client-$SERVICE_URL.key.pem client-$SERVICE_URL.cert.pem ca-chain.cert.pem
La práctica recomendada actual para distribuir certificados se sigue aquí mediante el servicio de descubrimiento secreto (SDS) en lugar de activaciones de archivos. Esta práctica evita reiniciar los Pods cuando se agrega un certificado nuevo. A partir de Istio 1.8/1.9, con esta técnica ya no necesitas derechos de acceso de lectura (RBAC) para el secreto de las puertas de enlace.
Agrega los certificados a
DestinationRule
y asígnale el nombreclient-destination-rule-to-external-service.yaml
:cat <<EOF > client-destination-rule-to-external-service.yaml apiVersion: networking.istio.io/v1alpha3 kind: DestinationRule metadata: name: originate-mtls-for-mysql spec: host: $SERVICE_URL trafficPolicy: loadBalancer: simple: ROUND_ROBIN portLevelSettings: - port: number: 13306 tls: mode: MUTUAL credentialName: client-credential sni: $SERVICE_URL EOF
Aplica la definición YAML anterior al clúster del cliente:
kubectl --context ${CLIENT_CLUSTER} apply -f client-destination-rule-to-external-service.yaml
Esta regla solo funciona si creaste el secreto antes. El secreto garantiza que los certificados se usen para la encriptación mTLS de la puerta de enlace de salida al extremo externo.
Cuando hayas completado estos pasos, la configuración del cliente habrá terminado y se verá como el siguiente diagrama.
Configura el servidor
Como se explicó en la guía de conceptos, del servidor debes configurar la puerta de enlace de entrada en Anthos Service Mesh. Antes de crear los archivos necesarios, es una buena idea revisar qué componentes son necesarios para realizar la parte del servidor de la solución.
En resumen, quieres identificar el tráfico entrante según su origen y certificado. También quieres enrutar de forma específica solo ese tráfico a su destino: tu base de datos MySQL en un contenedor. Por lo general, para hacerlo, usas un servicio en Kubernetes, pero en este ejemplo, debes identificar el tráfico entrante dentro de la comunicación de malla, por lo que necesitas una definición especial de un servicio que incluya lo siguiente:
- Certificados TLS (como un secreto)
- Puerta de enlace de entrada
- Servicio virtual
Crea el secreto para la puerta de enlace de entrada
Como lo hiciste con la puerta de enlace de salida, necesitas los mismos certificados para proteger la comunicación de la puerta de enlace de entrada. Para lograr esto, almacena los certificados en un secreto y defínelo con tu objeto de puerta de enlace de entrada.
En Cloud Shell, copia los archivos generados en la ubicación desde la que ejecutas el siguiente comando:
cp ./certs/3_application/private/$SERVICE_URL.key.pem server-$SERVICE_URL.key.pem cp ./certs/3_application/certs/$SERVICE_URL.cert.pem server-$SERVICE_URL.cert.pem
Crea el secreto del servidor:
kubectl --context ${SERVER_CLUSTER} create secret -n istio-system \ generic mysql-credential \ --from-file=tls.key=server-$SERVICE_URL.key.pem \ --from-file=tls.crt=server-$SERVICE_URL.cert.pem \ --from-file=ca.crt=ca-chain.cert.pem
Acaba de agregar los siguientes archivos de certificado al secreto:
server-$SERVICE_URL.key.pem server-$SERVICE_URL.cert.pem ca-chain.cert.pem
Define la puerta de enlace de entrada
Para recibir tráfico del clúster del lado del cliente, debes especificar una puerta de enlace de entrada que pueda desencriptar y verificar la comunicación TLS mediante los certificados.
Este diagrama muestra dónde se encuentra la puerta de enlace de entrada en su clúster. El tráfico pasa a través y se inspecciona si se ajusta a los criterios de seguridad y reenvío.
En Cloud Shell, usa el siguiente archivo YAML y asígnale el nombre
server-ingress-gatway.yaml
:cat <<EOF > server-ingress-gatway.yaml apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: gateway-mysql spec: selector: istio: ingressgateway # Istio default gateway implementation servers: - port: number: 13306 name: tls-mysql protocol: TLS tls: mode: MUTUAL credentialName: mysql-credential hosts: - "$SERVICE_URL" EOF
Aplica la definición YAML anterior al clúster del cliente:
kubectl --context ${SERVER_CLUSTER} apply -f server-ingress-gatway.yaml
Presta atención a la sección
tls:
porque este es muy importante. En esta sección, definirás que deseas mTLS. Para garantizar que esto funcione como se espera, debes proporcionar el secreto que creaste que contiene los certificados.Para habilitar el puerto
13306
, aplica un parche al servicio de entrada. Para habilitar este puerto, crea el siguiente archivo JSON y asígnale el nombregateway-patch.json
:cat <<EOF > gateway-patch.json [{ "op": "add", "path": "/spec/ports/0", "value": { "name": "tls-mysql", "protocol": "TCP", "targetPort": 13306, "port": 13306 } }] EOF
Aplica el parche al servicio de puerta de enlace:
kubectl --context ${SERVER_CLUSTER} -n istio-system patch --type=json svc istio-ingressgateway -p "$(cat gateway-patch.json)"
Ahora que abriste el puerto en la puerta de enlace de entrada, debes recoger el tráfico proveniente de tu nueva puerta de enlace de entrada y dirigirlo al Pod de base de datos. Para ello, usa un objeto interno de malla: el servicio virtual.
Define el servicio virtual
Como se explicó antes, el servicio virtual es una definición de patrones de coincidencia de tráfico que definen el tráfico dentro de tu malla. Debes identificar y reenviar de forma correcta el tráfico de tu puerta de enlace de entrada a tu servicio de base de datos MySQL, como se muestra en el siguiente diagrama.
En Cloud Shell, crea el siguiente archivo YAML y asígnale el nombre
server-virtual-service.yaml
:cat <<EOF > server-virtual-service.yaml apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: mysql-virtual-service spec: hosts: - "$SERVICE_URL" gateways: - gateway-mysql tcp: - route: - destination: port: number: 3306 host: mysql.default.svc.cluster.local EOF
Es importante que este servicio virtual haga referencia a la puerta de enlace de entrada de la que proviene el tráfico. La puerta de enlace se llama
gateway-mysql
.Debido a que este servicio virtual se aplica en L4, debes proporcionar una marca
tcp
para describir el tráfico de MySQL. Esta marca, básicamente, le indica a la malla que se usa L4 en este tráfico.Quizá hayas notado que el servicio de entrada usa el puerto
13306
para reenviar el tráfico. El servicio virtual lo capta y lo convierte en3306
.Por último, reenvía el tráfico al servidor de MySQL en el clúster de Kubernetes del servidor. En este ejemplo, el servidor escucha en el puerto estándar de MySQL
3306
.Aplica la definición YAML al clúster del servidor:
kubectl --context ${SERVER_CLUSTER} apply -f server-virtual-service.yaml
Estas dos definiciones desencriptan la solicitud del cliente MySQL encapsulada en mTLS y la reenvían a la base de datos MySQL dentro de la malla.
Es importante comprender que el reenvío interno de malla también se realiza mediante la encriptación, pero en este caso la encriptación se basa en certificados internos de malla. La finalización de mTLS se produce en la puerta de enlace.
Ahora tienes una manera mutua y completamente encriptada de comunicarte con tu servidor MySQL. Esta forma de encriptación es transparente para el cliente y el servidor MySQL, por lo que no es necesario cambiar la aplicación. Esto facilita las tareas como cambiar o rotar certificados. Además, esta forma de comunicación puede usarse para muchas situaciones diferentes.
Prueba la configuración
Ahora que tienes los clientes y el servidor implementados, puedes probar la configuración.
Llegó el momento de generar tráfico del cliente al servidor. Deseas seguir el flujo de tráfico y asegurarte de que el tráfico se enrute, encripte y desencripte como desees.
En Cloud Shell, implementa el servidor MySQL en el clúster del servidor:
kubectl --context ${SERVER_CLUSTER} apply -f server/mysql-server/mysql.yaml
Inicia un cliente MySQL en el clúster del cliente:
kubectl run --context ${CLIENT_CLUSTER} --env=SERVICE_URL=$SERVICE_URL -it --image=mysql:5.6 mysql-client-1 --restart=Never -- /bin/bash
Cuando se inicia el contenedor, se presenta una shell, que debería verse de la siguiente manera:
root@mysql-client-1:/#
Conéctate a tu servidor MySQL:
mysql -pyougottoknowme -h $SERVICE_URL
Usa los siguientes comandos para agregar una base de datos y una tabla:
CREATE DATABASE test_encrypted_connection; USE test_encrypted_connection; CREATE TABLE message (id INT, content VARCHAR(20)); INSERT INTO message (id,content) VALUES(1,"Crypto Hi");
Después de conectar y agregar la tabla, sal de la conexión de MySQL y del Pod:
exit exit
Debes escribir exit dos veces: primero, a fin de salir de la conexión de la base de datos y, luego, para salir del Pod. Si el Pod deja de responder tras la salida, presiona Control+C para cancelar fuera de la shell Bash.
Si sigues estos pasos, deberías haber generado un resultado de registro significativo que podrás analizar con más detalle.
En la siguiente sección, verificarás que el tráfico del cliente pase el proxy y la puerta de enlace de salida. También puedes probar si puedes ver el tráfico que ingresa al lado del servidor a través de la puerta de enlace de entrada.
Prueba el proxy del cliente y la puerta de enlace de salida
En Cloud Shell, en el proxy del lado del cliente, verifica que puedas ver los registros del proxy de Istio:
kubectl --context ${CLIENT_CLUSTER} logs -l run=mysql-client-1 -c istio-proxy -f
El resultado de depuración es similar al siguiente:
[2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 10 - "-" "-" "-" "-" "192.168.1.4:15443" outbound|15443|mysql|istio-egressgateway.istio-system.svc.cluster.local 192.168.1.12:58614 34.66.165.46:3306 192.168.1.12:39642 - -
Presiona Control+C para salir del resultado del registro.
En esta entrada de registro, puedes ver que el cliente solicita el servidor que se ejecuta en la dirección IP
34.66.165.46
en el puerto3306
. La solicitud se reenvía (outbound
) aistio-egressgateway
que escucha en el puerto15443
de la dirección IP192.168.1.4
. Define este reenvío en tu servicio virtual (client-virtual-service.yaml
).Lee los registros del proxy de la puerta de enlace de salida:
kubectl --context ${CLIENT_CLUSTER} logs -n istio-system -l app=istio-egressgateway -f
El resultado de depuración es similar al siguiente:
[2021-02-10T21:19:08.292Z] "- - -" 0 - "-" "-" 176 115 19 - "-" "-" "-" "-" "34.66.165.46:13306" outbound|13306||34.66.165.46.nip.io 192.168.1.4:53542 192.168.1.4:15443 192.168.1.12:58614 34.66.165.46.nip.io -
Presiona Control+C para salir del resultado del registro.
En esta entrada de registro, puedes ver que la solicitud del cliente enrutada a
istio-egressgateway
mediante la escucha en la dirección IP192.168.1.4
en el puerto15443
se enruta aún más afuera de la malla de servicios al servicio externo que escucha en la dirección IP34.66.165.46
en el puerto13306.
. Debes definir este reenvío en la segunda parte de tu servicio virtual (client-virtual-service.yaml
).
Prueba la puerta de enlace de entrada del servidor
En Cloud Shell, del servidor, visualiza los registros del proxy de la puerta de enlace de entrada:
kubectl --context ${SERVER_CLUSTER} logs -n istio-system -l app=istio-ingressgateway -f
El resultado en el registro es similar al siguiente:
[2021-02-10T21:22:27.381Z] "- - -" 0 - "-" "-" 0 78 5 - "-" "-" "-" "-" "100.96.4.8:3306" outbound|3306||mysql.default.svc.cluster.local 100.96.1.3:55730 100.96.1.3:13306 100.96.1.1:42244 34.66.165.46.nip.io -
Presiona Control+C para salir del resultado del registro.
En esta entrada de registro, puedes ver que la solicitud del cliente externo enrutada a
istio-ingressgateway
que escucha en el puerto34.66.165.46
de la dirección IP13306
que se enruta aún más al servicio de MySQL dentro de la malla identificada por el nombre del serviciomysql.default.svc.cluster.local
en el puerto3306.
. Debes definir este reenvío en la puerta de enlace de entrada (server-ingress-gateway.yaml
).Para el servidor MySQL, consulta los registros del proxy de Istio:
kubectl --context ${SERVER_CLUSTER} logs -l app=mysql -c istio-proxy -f
El resultado es similar al siguiente:
[2021-02-10T21:22:27.382Z] "- - -" 0 - "-" "-" 1555 1471 4 - "-" "-" "-" "-" "127.0.0.1:3306" inbound|3306|mysql|mysql.default.svc.cluster.local 127.0.0.1:45894 100.96.4.8:3306 100.96.1.3:55730 outbound_.3306_._.mysql.default.svc.cluster.local -
Presiona Control+C para salir del resultado del registro.
En esta entrada de registro, puedes ver la llamada entrante al servidor de base de datos MySQL que escucha en la dirección IP
100.96.4.8
en el puerto3306
. La llamada proviene del pod de entrada con la dirección IP100.96.1.3
.Para obtener más información sobre el formato de registro de Envoy, consulta Obtén los registros de acceso de Envoy.
Prueba tu base de datos para ver la entrada generada:
MYSQL=$(kubectl --context ${SERVER_CLUSTER} get pods -n default | tail -n 1 | awk '{print $1}') kubectl --context ${SERVER_CLUSTER} exec $MYSQL -ti -- /bin/bash
Usa este comando para verificar la base de datos creada:
mysql -pyougottoknowme USE test_encrypted_connection; SELECT * from message;
El resultado es similar al siguiente:
+------+-----------+ | id | content | +------+-----------+ | 1 | Crypto Hi | +------+-----------+ 1 row in set (0.00 sec)
Sal de la base de datos MySQL:
exit exit
Debes escribir
exit
dos veces: primero, a fin de salir de la conexión de la base de datos y, luego, para salir del Pod.
Omite los certificados para probar el acceso
Ahora que probaste y verificaste que el acceso funciona mediante los certificados inyectados, prueba lo contrario: qué sucede si omites la puerta de enlace de salida y la inyección del certificado. Esta prueba también se denomina prueba negativa.
Puedes realizar esta prueba si inicias otro Pod en un espacio de nombres sin la inyección del proxy habilitada.
En Cloud Shell, crea un espacio de nombres nuevo:
kubectl --context ${CLIENT_CLUSTER} create ns unencrypted
Crea un Pod y, luego, inicia una shell interactiva dentro del contenedor:
kubectl --context ${CLIENT_CLUSTER} run -it --image=mysql:5.6 \ mysql-client-2 --env=SERVICE_URL=$SERVICE_URL \ -n unencrypted --restart=Never -- /bin/bash
Intenta conectarte a la base de datos una vez que se haya iniciado la shell interactiva:
mysql -pyougottoknowme -h $SERVICE_URL
Después de 30 segundos, verás un resultado similar al siguiente:
Warning: Using a password on the command line interface can be insecure. ERROR 2003 (HY000): Can't connect to MySQL server on '104.154.164.12.nip.io' (110)
Se espera esta advertencia porque este Pod omite la puerta de enlace de salida y trata de llegar a la puerta de enlace de entrada (
$SERVICE_URL
) directamente a través de Internet.Intenta resolver la dirección IP del servicio:
resolveip $SERVICE_URL
El resultado es similar al siguiente. Tu dirección IP será diferente.
IP address of 104.154.164.12.nip.io is 104.154.164.12
Esto demuestra que el FQDN se puede resolver y que la conexión con errores se debe a la inserción de certificados faltantes.
Sal de la conexión de MySQL y del Pod del servidor MySQL:
exit exit
Nuevas investigaciones
Un tema que no se explica en este instructivo es que, por lo general, los parámetros de configuración de salida son propiedad de una organización o función diferente en tu empresa, porque están alojadas en el espacio de nombres istio-system
. Configura los permisos de RBAC de Kubernetes para que solo los administradores de red puedan crear y modificar directamente los recursos mencionados en este instructivo.
Ahora que sabes cómo usar una malla de servicios para ayudar a garantizar una comunicación segura, te recomendamos probarlo con una aplicación que deba intercambiar datos de forma segura y en la que quieres controlar la encriptación hasta la capa del certificado. Para comenzar, puedes instalar Anthos Service Mesh.
Prueba usar dos clústeres de GKE y combínalos con la técnica de este instructivo. Esta técnica también funciona en la plataforma GKE Enterprise entre dos clústeres de Kubernetes externos.
Las mallas de servicios son una excelente manera de aumentar la seguridad en tu clúster y los servicios externos. Un último caso de uso a tratar es tener un extremo mTLS que no sea un segundo clúster de Kubernetes, sino un proveedor externo (como un proveedor de pagos).
Limpia
Puedes borrar tu proyecto para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos usados en este instructivo.
Borra el proyecto
- En la consola de Google Cloud, ve a la página Administrar recursos.
- En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
- En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.
¿Qué sigue?
- Lee la guía de conceptos complementarios.
- Si deseas obtener más recomendaciones para configurar tu puerta de enlace de salida, consulta Usa puertas de enlace de salida de Anthos Service Mesh en los clústeres de GKE: Instructivo.
- Consulta la guía de endurecimiento de GKE y el módulo de Terraform adjunto.
- Explora arquitecturas de referencia, diagramas y prácticas recomendadas sobre Google Cloud. Consulta nuestro Cloud Architecture Center.