jueves, noviembre 29, 2018

SuperLatch Docker: Integrar Latch con Docker y Kubernetes en tus aplicaciones (3 de 3)

Al finalizar la segunda parte de este artículo habíamos visto como las aplicaciones en Go y Java están Up & Running, al mismo tiempo que teníamos pareadas ambas con el SuperLacth Docker en nuestra aplicación móvil.

Figura 17: SuperLatch Docker: Integrar Latch con Docker y Kubernetes en tus aplicaciones (3 de 3);

A partir de este momento podemos ver que cuando activamos Latch para poner el estado en OFF de nuestra app SuperLatch Docker las aplicaciones pasan a estar deshabilitadas y no funcionan, tal y como se puede ver en las imágenes siguientes.

Figura 18: Aplicación Go se deshabilita porque el SuperLatch Docker está cerrado
Figura 19: Aplicación Java deshabilitada también desde el SuperLatch Docker

El funcionamiento descrito en este proceso está disponible en el siguiente vídeo, donde se puede observar todo el proceso activación y bloqueo de la aplicación utilizando Latch:

Figura 20: Demo de Activación y Desactivación de SuperLatch Docker

Como podéis observar, cada segundo (tiempo preestablecido para que la aplicación compruebe el estado) se activa el popup en la aplicación Latch, indicando que se ha bloqueado una aplicación. Esto ocurre porque estamos utilizando la versión Comunidad de Latch (en la versión comercial se puede desactivar este popup con un simple flag).

SuperLatch en Kuberneters

Para correr esta misma idea sobre kubernetes, usaremos minikube. Uno de los cambios que haremos con respecto a la versión de docker-compose es que los valores que necesitamos para conectar con Latch (secret, account_id y app_id) se almacenarán en secretos de kubernetes, en vez de en variables de entorno. Para ello tenemos que modificar el fichero secrets.yaml que se encuentra dentro del directorio kube.

Figura 21: Arquitectura de SuperLatch Kubernetes

Los datos de los secretos se asignan codificados en Base64, así que lo primero que necesitamos hacer es codificar nuestros valores:

Account id:
echo -n "QNmcFJuPdqG3fdAUy2GqBw2Ehn2VF8ranuzFbvMHj2h27WPgbrnMmyGTwzqBFvda" | base64 UU5tY0ZKdVBkcUczZmRBVXkyR3FCdzJFaG4yVkY4cmFudXpGYnZNSGoyaDI3V1BnYnJuTW15R1R3enFCRnZkYQ==
app_id:
echo -n "PAyrhmA3Hb8Qmf84N2Js" | base64 UEF5cmhtQTNIYjhRbWY4NE4ySnM=
secret key:
echo -n "isiBUL6PMXgc7EfkVkZgbZirwfK9d7gPtfDVKrDt" | base64 aXNpQlVMNlBNWGdjN0Vma1ZrWmdiWmlyd2ZLOWQ3Z1B0ZkRWS3JEdA==
Ahora ponemos esos valores en nuestro secrets.yaml:
apiVersion: v1
kind: Secret
metadata:
name: latch-secret
type: Opaque
data:
app_id: UEF5cmhtQTNIYjhRbWY4NE4ySnM=
account_id: UU5tY0ZKdVBkcUczZmRBVXkyR3FCdzJFaG4yVkY4cmFudXpGYnZNSGoyaDI3V1BnYnJuTW15R1R3enFCRnZkYQ==
key: aXNpQlVMNlBNWGdjN0Vma1ZrWmdiWmlyd2ZLOWQ3Z1B0ZkRWS3JEdA==
Una vez tenemos dicho fichero modificado y grabado, lo siguiente sería crear nuestros objetos. Para ello asegúrate que minikube está arrancado:

minikube start 
Starting local Kubernetes v1.10.0 cluster... 
Starting VM...
Getting VM IP address...
Moving files into cluster...
Setting up certs... 
Connecting to cluster... 
Setting up kubeconfig...
Starting cluster components... 
Kubectl is now configured to use the cluster. 
Loading cached images from config file. 

Una vez minikube esté arrancado, ejecutamos:
kubectl create -f secrets.yaml -f deployment.yaml -f service.yaml
Asumiendo que estamos situado dentro del directorio kube, si estamos en otro directorio distinto al de nuestros ficheros yaml, pues tendrás que prefijar el fichero con la ruta correcta. Para comprobar que nuestro Pod se ha creado de forma correcta:

kubectl get pod 

NAME                                    READY   STATUS    RESTARTS  
 
latch-poc-deployment-66d8f6c966-vslf7   3/3     Running   0     
     

Como se puede observar en la columna READY, los tres contenedores están corriendo (3/3). Una vez echo esto pues acceder a los servicios, para ello tenemos que ver que puertos a mapeado kubernetes:

minikube service list
|-------------|----------------------|--------------------------------|
|  NAMESPACE  |         NAME         |              URL               |
|-------------|----------------------|--------------------------------|
| default     | kubernetes           | No node port                   |
| default     | latch-poc-service    | http://192.168.99.100:30823    |
|             |                      | http://192.168.99.100:31723    |
| kube-system | kube-dns             | No node port                   |
| kube-system | kubernetes-dashboard | No node port                   |
|-------------|----------------------|--------------------------------|

Vemos que el servicio latch-poc-service tiene dos puertos mareados, pero no sabemos cuál es cuál. Podemos abrir nuestro navegador y probar a ver que puerto esta mapeado al servicio Java y cuál al servicio Go. O podemos ver la descripción del servicio:

kubectl describe service latch-poc-service             
Name:                     latch-poc-service
Namespace:                default
Labels:                   
Annotations:              
Selector:                 app=latch-poc-deployment
Type:                     NodePort
IP:                       10.99.73.112
Port:                     java-port  8080/TCP
TargetPort:               8080/TCP
NodePort:                 java-port  30823/TCP
Endpoints:                172.17.0.5:8080
Port:                     go-port  8081/TCP
TargetPort:               8081/TCP
NodePort:                 go-port  31723/TCP
Endpoints:                172.17.0.5:8081
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>

Aquí podemos ver que el servido Java está mapeado al puerto del nodo 30823 y el de Go al 31723. A continuación podéis ver un vídeo completo de la PoC de SuperLatch Kubernetes:

Figura 22 : PoC de SuperLatch Kubernetes

En el caso de kubernetes estamos desplegando los tres contenedores en un mismo Pod. Si el servicio latch-master estuviera protegiendo sólo un servicio, desplegar ambos contenedores en un mismo Pod sería aceptable. En este caso donde tenemos dos contenedores (servicios) que proteger, idealmente se desplegaría cada contenedor en un Pod distinto y el volumen se haría global (PersistentVolume) o el estado de latch se almacenaría en algún lugar global dentro del cluster. En nuestro caso y para mostrar la prueba de concepto, lo consideramos aceptable poner todos los contenedores en un mismo Pod por temas de simplicidad.

Conclusiones finales

En esta prueba de concepto no sólo podemos ver como añadir una capa más de seguridad protegiendo nuestros contenedores con Latch, si no que además, como los contenedores que queremos proteger sólo necesitan leer de un fichero del sistema y no tienen que acceder a los servidores de Latch para comprobar el estado del mismo, a menos que estos se tuvieran que conectar con otros servidores externos, podrías bloquear las conexiones outbound, es decir, que no podrían salir a Internet como comentamos al principio de este artículo, lo cual añade una capa más de seguridad.

Otro beneficio que esta PoC nos muestra, éste desde el punto de vista del programador, es al ahorro en código y dependencias. Es decir, los servicios protegidos por Latch cómo no necesitan conectar y hablar el “protocolo Latch”, no tienes que añadir las dependencias de las SDK de Latch y por lo tanto no tienes que escribir código para integrar la misma. Esto, además, nos permitiría proteger con Latch cualquier servicio ser necesario un SDK de Latch específico para el lenguaje utilizado. Vamos que podrías crear un servicio en COBOL y protegerlo con Latch sin ningún tipo de problemas :)

Esperamos que esta PoC sirva tanto como idea para implementarlo en alguna aplicación con Docker o como simplemente una forma de aprender un poco más de Docker y kubernetes. Y por supuesto, en nuestro libro Docker: SecDevOps también puedes aprender más de Docker y kubernetes ;)

Happy Hacking Hackers!!!

***********************************************************************************
- SuperLatch Docker: Integrar Latch con Docker y Kubernetes en tus apps (1 de 3)
- SuperLatch Docker: Integrar Latch con Docker y Kubernetes en tus apps (2 de 3)
- SuperLatch Docker: Integrar Latch con Docker y Kubernetes en tus apps (3 de 3)
***********************************************************************************

Autores:

Fran Ramírez, (@cyberhadesblog) miembro del equipo de Crazy Ideas en CDO en Telefónica, co-autor del libro "Microhistorias: Anécdotas y Curiosidades de la historia de la informática (y los hackers)", del libro "Docker: SecDevOps" y del blog Cyberhades.

Rafael Troncoso (@tuxotron) es DevOps Tech Lead en USCIS/DHS, co-autor del libro "Microhistorias: Anécdotas y Curiosidades de la historia de la informática (y los hackers)", del libro "Docker: SecDevOps" y del blog Cyberhades.

2 comentarios:

  1. Si las claves van en base 64 da igual si las mandan en plano

    ResponderEliminar
  2. Hola Mauricio,
    buena observación. La razón por la que usamos base64 es porque es la forma en que kubernetes maneja los secretos, porque en estos se pueden almacenar no solo texto si no también ficheros binarios.
    Aunque kubernetes ofrece como parte de Secrets una opción llamada stringData donde podríamos poner el texto en claro.

    Un saludo

    ResponderEliminar