Skip to main content

Análisis de vulnerabilidades en AWS con la integración de Snyk

Escrito por
AWS vulnerability scanning

10 de febrero de 2021

0 minutos de lectura

Si usas el conjunto de herramientas de AWS relacionadas con Kubernetes, te alegrará saber que puedes usar Snyk para analizar directamente tus flujos de trabajo, con integraciones para Amazon Elastic Container Registry ( ECR ) y Amazon Elastic Kubernetes Service ( EKS ). ¡Aquí te explicamos cómo empezar!

En esta publicación usaré una de nuestras aplicaciones de prueba de Snyk para compilar e implementar una aplicación. Puedes encontrar el código aquí. También necesitarás instalar algunas dependencias:

Empecemos

Ahora que ya tienes todo listo, ¡empecemos!

Primero, configuremos ECR para que Snyk pueda acceder a nuestros repositorios. Para ello, debemos asignar una política y un rol en AWS Identity and Access Management que otorguen al backend de Snyk los permisos adecuados para realizar acciones como enumerar imágenes y extraerlas de nuestros repositorios de ECR.

Los pasos para configurar la integración están detallados en la documentación de Snyk, pero podemos encontrar toda la configuración necesaria directamente en la interfaz de Snyk. Ve a Settings/Integrations/ECR/Edit Settings y, primero, configura una política con el JSON que aparece en la interfaz:

{
"Version": "2012-10-17",
"Statement": [
 {
  "Sid": "SnykAllowPull",
  "Effect": "Allow",
  "Action": [
   "ecr:GetLifecyclePolicyPreview",
   "ecr:GetDownloadUrlForLayer",
   "ecr:BatchGetImage",
   "ecr:DescribeImages",
   "ecr:GetAuthorizationToken",
   "ecr:DescribeRepositories",
   "ecr:ListTagsForResource",
   "ecr:ListImages",
   "ecr:BatchCheckLayerAvailability",
   "ecr:GetRepositoryPolicy",
   "ecr:GetLifecyclePolicy"
  ],
  "Resource": "*"
 }
]
}

- oriEsto otorgará a Snyk los permisos definidos en la sección Action del JSON para cualquier repositorio e imagen que hayamos configurado en nuestra cuenta de ECR. Este es el conjunto mínimo de permisos necesarios para enumerar repositorios e imágenes, y extraer imágenes de los repositorios. Sigue las instrucciones para agregar ese JSON a la configuración de la política de IAM en AWS.

Luego debemos agregar un rol y asignarle la política, tal como se indica en las instrucciones:

Instrucciones para crear un rol de IAM de AWS para implementar una política, que incluyen seleccionar EC2, la política de solo lectura de ECR y asignarle el nombre SnykServiceRole

También debemos definir el alcance del rol especificando los ID de las organizaciones de Snyk que pueden usarlo. Para ello, edita las relaciones de confianza del rol recién creado usando el JSON de la documentación. La cadena sts:ExternalId será el ID de tu organización de Snyk. Este fragmento de JSON permite que el principal de AWS asuma el rol si el ExternalId proporcionado coincide con el ID de nuestra organización de Snyk. 

{
"Version": "2012-10-17",
"Statement": [
 {
  "Effect": "Allow",
  "Principal": {
   "AWS": "arn:aws:iam::198361731867:user/ecr-integration-user"
  },
  "Action": "sts:AssumeRole",
  "Condition": {
   "StringEquals": {
    "sts:ExternalId": "11111111-1111-1111-1111-111111111111"
   }
  }
 }
]
}

Como se indica en las instrucciones, si necesitas agregar varias organizaciones de Snyk a esta política, debes incluirlas en un arreglo JSON entre corchetes:

"sts:ExternalId": [
"11111111-1111-1111-1111-111111111111",
"22222222-2222-2222-2222-222222222222",
]

Puedes encontrar el ID de tu organización de Snyk en Settings > General.

Página de configuración que muestra la pestaña General con los campos de nombre de la organización, clave de API e ID de la organización.

El último paso es configurar la interfaz de Snyk para que use ese rol al conectarse a ECR. Vuelve a Settings/Integrations/ECR/Edit Settings e ingresa la región en la que configuraste ECR y el rol que creamos anteriormente:

Formulario de credenciales de cuenta para conectar Snyk a una cuenta de Amazon ECR, con campos para la región de AWS, el ARN del rol y guardar cambios

En este punto, la integración de ECR debería estar completamente configurada. Sigamos adelante y probémosla.

Primero, usemos AWS CLI para crear un repositorio en ECR:

% aws ecr create-repository --repository-name goof
{
    "repository": {
        "repositoryArn": "arn:aws:ecr:us-west-2:478468688580:repository/goof",
        "registryId": "478468688580",
        "repositoryName": "goof",
        "repositoryUri": "478468688580.dkr.ecr.us-west-2.amazonaws.com/goof",
        "createdAt": "2021-02-03T13:40:59+00:00",
        "imageTagMutability": "MUTABLE",
        "imageScanningConfiguration": {
            "scanOnPush": false
        },
        "encryptionConfiguration": {
            "encryptionType": "AES256"
        }
    }
}

Ahora que tenemos un repositorio en ECR, configuremos Docker para poder enviarle imágenes:

% aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 478468688580.dkr.ecr.us-west-2.amazonaws.com

Este comando mostrará tu contraseña de inicio de sesión y luego la pasará a Docker CLI para autenticarte en tu registro de AWS ECR. Asegúrate de configurar correctamente la región de AWS donde habilitaste ECR.

Ahora revisemos nuestra aplicación de prueba. Es una aplicación sencilla de Node.js que contiene muchas vulnerabilidades, junto con la configuración para compilarla en Docker e implementarla en Kubernetes.

% git clone https://github.com/mattj-io/goof.git goof
% cd goof

Lo primero que haremos es compilar la aplicación con Docker.

% docker build -t goof .

Una vez compilada la imagen, deberíamos verla en nuestro repositorio local de Docker:

% docker images
REPOSITORY                                TAG      IMAGE ID       CREATED      SIZE
goof                                      latest    7934ddc2fec9   2 days ago   1.04GB

Antes de enviarla a nuestro repositorio ECR de origen, debemos etiquetarla y reemplazar el nombre del repositorio por el que configuraste anteriormente en ECR:

% docker tag goof:latest 478468688580.dkr.ecr.us-west-2.amazonaws.com/goof:latest
% docker images
REPOSITORY                                          TAG       IMAGE ID       CREATED      SIZE
478468688580.dkr.ecr.us-west-2.amazonaws.com/goof   latest    7934ddc2fec9   2 days ago   1.04GB
goof                                      latest    7934ddc2fec9   2 days ago   1.04GB   

Ahora podemos enviarla a ECR:

% docker push 478468688580.dkr.ecr.us-west-2.amazonaws.com/goof:latest 

Cuando termine el envío, podemos revisar nuestro repositorio de ECR para ver la imagen que acabamos de enviar:

% aws ecr list-images --repository-name goof
{
    "imageIds": [
        {
            "imageDigest": "sha256:ca6c19e25b4d7917769ee535f0b073e04e8ddc32ead83493c03abc65e82e5e6c",
            "imageTag": "latest"
        },
        {
            "imageDigest": "sha256:cd100d7c505ced1f5c4f4eb6fbd7ac83a623f9bb483db1e828fb4cd3b3c01bd9"
        }
    ]
}

Una vez que la imagen esté en ECR, podemos configurar Snyk para analizarla desde allí. En la interfaz de Snyk, selecciona Add Project y ve al ícono de ECR:

Menú de configuración del proyecto de Snyk con opciones de integración para GitHub, Docker Hub, ECR, Kubernetes, CLI y otras

Si la configuración de ECR es correcta, ahora deberíamos ver una vista con todos los repositorios y las imágenes que contienen y que están disponibles en ECR:

Pantalla de selección de repositorios de Snyk con un campo de nombre de imagen y casillas para los repositorios aws-ecr, goof y latest

Podemos ver el repositorio goof que creamos anteriormente, con una sola etiqueta de imagen. Selecciona la imagen y luego haz clic en el botón Add selected repositories. En ese momento, Snyk importará el repositorio desde ECR y comenzará a analizarlo y monitorearlo.

Una vez completada la importación, deberíamos verla en la página de proyectos de Snyk:

Panel del repositorio que muestra el proyecto goof y el archivo package.json más reciente, con insignias de estado y las horas de las pruebas recientes

Podemos ver que Snyk detectó tanto la imagen como el archivo package.json que utiliza la aplicación de Node implementada en ella, y creó proyectos para ambos.

La imagen usa una imagen base antigua y vulnerable. Snyk habrá detectado numerosas vulnerabilidades y recomendado imágenes base que podrían reducir el número total de vulnerabilidades.

Resultados del análisis de vulnerabilidades de AWS con recomendaciones para actualizar una imagen base de Docker reciente de Goof, junto con el número de vulnerabilidades y sus niveles de gravedad.

El archivo package.json define los paquetes de Node incluidos en la aplicación, tanto directamente, mediante los paquetes especificados en el archivo package.json, como indirectamente, mediante las dependencias. Snyk habrá creado un árbol de dependencias con todos estos paquetes y consultado la base de datos de vulnerabilidades para detectar las que existen en las versiones específicas utilizadas. Verás que hay muchas, ya que esta aplicación está diseñada para usar versiones vulnerables.

Informe de vulnerabilidades del paquete Goofys que muestra un problema de escritura arbitraria de archivos de alta gravedad durante la extracción de archivos y detalles de remediación

También encontrarás información sobre cada vulnerabilidad y recomendaciones para corregirlas, con indicaciones sobre qué paquetes actualizar. Dedica un tiempo a explorar toda la información que ofrece la interfaz de Snyk.

Configurar la integración de Snyk con EKS

Lo siguiente es configurar la integración de Snyk con Elastic Kubernetes Service. Para hacerlo, necesitarás una prueba gratuita de uno de los planes estándar de Snyk, ya que la integración con Kubernetes forma parte de nuestra oferta de pago.

Para ello, creemos un clúster de Kubernetes con EKS. Hay varias formas de hacerlo, pero una de las más sencillas es usar la herramienta eksctl.

Primero, usaré AWS CLI para crear un keypair que pueda usar para conectarme por SSH a los nodos del clúster si lo necesito:

% aws ec2 create-key-pair --key-name demo --query "KeyMaterial" --output text > demo.pem

Luego usaré eksctl para crear un clúster, en este caso llamado mattjarvis-sko, en la región us-west-2, con un grupo de nodos administrado de Linux y mi keypair agregado.

% eksctl create cluster --name mattjarvis-sko --region us-west-2 --with-oidc --ssh-access --ssh-public-key SKO_demo --managed

Este comando tardará un tiempo en completarse, pero cuando termine tendrás un clúster de EKS completamente funcional y kubectl estará configurado para comunicarse con él.

% kubectl get nodes
NAME                                           STATUS   ROLES    AGE    VERSION
ip-192-168-24-115.us-west-2.compute.internal   Ready    <none>   2d5h   v1.18.9-eks-d1db3c
ip-192-168-84-146.us-west-2.compute.internal   Ready    <none>   2d5h   v1.18.9-eks-d1db3c

Ahora que nuestro clúster de EKS está en funcionamiento, revisemos la configuración de Kubernetes para implementar la aplicación. En la copia del repositorio Git de la aplicación goof, encontrarás un directorio manifests que contiene dos archivos YAML de Kubernetes.

% ls manifests
goof-deployment.yaml goof-service.yaml

El archivo goof-deployment.yaml define la aplicación goof y un pod de mongodb, que necesita como dependencia. Debes cambiar la sección de imagen para usar el repositorio de ECR que configuraste anteriormente:

% cat manifests/goof-deployment.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: goof
spec:
  replicas: 1
  selector:
    matchLabels:
      app: goof
      tier: frontend
  template:
    metadata:
      labels:
        app: goof
        tier: frontend
    spec:
      containers:
        - name: goof
          image: 478468688580.dkr.ecr.us-west-2.amazonaws.com/goof
          resources:
            requests:
              cpu: 100m
              memory: 100Mi
          ports:
            - containerPort: 3001
            - containerPort: 9229
          env:
            - name: DOCKER
              value: "1"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: goof-mongo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: goof
      tier: backend
  template:
    metadata:
      labels:
        app: goof
        tier: backend
    spec:
      containers:
        - name: goof-mongo
          image: mongo
          ports:
            - containerPort: 27017

El archivo goof-service.yaml define el servicio para mongodb y proporciona conectividad al pod de mongodb en el puerto 27017. Así se conecta nuestra aplicación goof a la base de datos. También define un servicio externo de LoadBalancer que se implementará para proporcionar conectividad externa a la aplicación en ejecución. La implementación concreta de LoadBalancer depende de la plataforma donde se implemente el clúster. En AWS, aprovisionará un Elastic Load Balancer, con el puerto 80 expuesto al exterior y dirigido al puerto 3001 de nuestro pod goof, donde se ejecuta nuestra aplicación de Node.js. 

% cat manifests/goof-service.yaml   
apiVersion: v1
kind: Service
metadata:
  name: goof
spec:
  type: LoadBalancer
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3001
    name: "http"
  - protocol: TCP
    port: 9229
    targetPort: 9229
    name: "debug"
  selector:
    app: goof
    tier: frontend
---
apiVersion: v1
kind: Service
metadata:
  name: goof-mongo
spec:
  ports:
  - protocol: TCP
    port: 27017
    targetPort: 27017
    name: "mongo"
  selector:
    app: goof
    tier: backend

Implementemos la aplicación en nuestro clúster:

FIXME TO HERE

% kubectl create -f manifests/goof-deployment.yaml
% kubectl create -f manifests/goof-service.yaml

% kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
goof-5589f855f8-hzvjw        1/1     Running   0          2d13h
goof-mongo-775d5b7d8-w4658   1/1     Running   0          2d13h

% kubectl get services
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP                                                               PORT(S)                       AGE
goof         LoadBalancer   10.100.94.189   adae6cf9abdb84b63919cc27c2ecafcb-1847540121.us-west-2.elb.amazonaws.com   80:32621/TCP,9229:31301/TCP   2d13h
goof-mongo   ClusterIP      10.100.97.98    <none>                                                                    27017/TCP                     2d13h
kubernetes   ClusterIP      10.100.0.1      <none>          

La URL del campo EXTERNAL-IP la proporciona el ELB aprovisionado como parte del servicio LoadBalancer y podemos usarla para ver nuestra aplicación en funcionamiento. Si la abrimos en un navegador, deberíamos ver la aplicación goof en ejecución:

Interfaz minimalista de una app de tareas pendientes con el encabezado “Goof TODO” y un campo de texto vacío.

Nuestra aplicación ya está implementada y en ejecución en el clúster de EKS. El último paso es configurar la integración entre Snyk y nuestro clúster de EKS para poder analizar las cargas de trabajo en ejecución en producción. La integración de Snyk con Kubernetes consiste en un único operador de Kubernetes en un pod, que consulta la API de Kubernetes, analiza las imágenes de contenedor dentro del clúster y se comunica con el backend de Snyk.

Si usáramos CloudFormation para implementar el clúster de EKS, podríamos usar los Quick Starts de AWS que creamos para implementar la integración de Snyk. Pero, como en esta demostración usamos eksctl, hagamos la instalación manual paso a paso.

El primer paso es agregar el repositorio de Helm que contiene los gráficos de Helm para implementar Snyk Kubernetes Monitor:

helm repo add snyk-charts https://snyk.github.io/kubernetes-monitor/

Ahora crearemos un espacio de nombres independiente para ejecutar Snyk Monitor. Por lo general, es recomendable crear espacios de nombres separados para tus aplicaciones en Kubernetes, ya que esto permite un control más detallado de los permisos.

kubectl create namespace snyk-monitor

El siguiente paso es crear un secreto en Kubernetes que contenga nuestro integrationID de Snyk. El proceso de Snyk Monitor lo usará para comunicarse con la API de Snyk y enviarle información sobre los pods en ejecución del clúster. Si usáramos registros privados que requieren credenciales para extraer imágenes, también tendríamos que incluir esos datos de autenticación en la sección dockercfg.json. Encontrarás más detalles en la documentación de Snyk Monitor.

kubectl create secret generic snyk-monitor -n snyk-monitor --from-literal=dockercfg.json={} --from-literal=integrationId=11111111-1111-1111-1111-111111111111

Una vez creado el secreto, podemos usar Helm para instalar Snyk Monitor en el espacio de nombres snyk-monitor.

helm upgrade --install snyk-monitor snyk-charts/snyk-monitor --namespace snyk-monitor --set clusterName="Production"

Cuando termine la instalación del gráfico de Helm, deberíamos ver el pod snyk-monitor en ejecución en el espacio de nombres snyk-monitor de nuestro clúster:

% kubectl get pods -n snyk-monitor
NAME                            READY   STATUS    RESTARTS   AGE
snyk-monitor-589cff67c7-kcj8j   1/1     Running   0          2d22h

También podemos ver los registros y confirmar que snyk-monitor está enviando datos a Snyk:

% kubectl logs snyk-monitor-589cff67c7-kcj8j
----snipped for brevity-----
{"name":"kubernetes-monitor","hostname":"snyk-monitor-589cff67c7-kcj8j","pid":6,"level":30,"workloadLocator":{"userLocator":"eb30b15b-a0a5-47a4-ab75-7e50b853d6a9","cluster":"Production","namespace":"default","type":"Deployment","name":"goof"},"attempt":1,"msg":"workload metadata sent upstream successfully","time":"2021-02-04T10:43:42.789Z","v":0}
{"name":"kubernetes-monitor","hostname":"snyk-monitor-589cff67c7-kcj8j","pid":6,"level":30,"workloadLocator":{"userLocator":"eb30b15b-a0a5-47a4-ab75-7e50b853d6a9","cluster":"Production","namespace":"default","type":"Deployment","name":"goof-mongo"},"attempt":1,"msg":"workload metadata sent upstream successfully","time":"2021-02-04T10:43:42.821Z","v":0}

Es posible que las cargas de trabajo tarden un poco en aparecer en la interfaz de Snyk, ya que el monitor debe enviar los datos a Snyk. Pero en aproximadamente un minuto, deberías poder ver las cargas de trabajo en ejecución en el clúster si vas a Add Project/Kubernetes:

Menú de creación de proyectos con opciones para GitHub, Docker Hub, ECR, Kubernetes, CLI, repositorios públicos de GitHub y Otros

Ahora deberíamos ver las cargas de trabajo en ejecución. Para agregarlas, solo tenemos que seleccionar la casilla correspondiente para que Snyk las importe y analice.

Pantalla de selección de cargas de trabajo de Kubernetes que muestra el entorno de producción, los espacios de nombres predeterminado y synke-monitor, y las opciones de implementación.

Una vez importado el proyecto, podemos ir a la página Projects y abrir el proyecto importado. Lo primero que debemos notar es que Snyk detectó la configuración de ejecución de la carga de trabajo y verificó si presentaba problemas de seguridad. Aquí vemos que no pasó varias pruebas de seguridad, entre ellas, ejecutar como root y no tener límites de CPU establecidos:

Informe de seguridad de implementación de Kubernetes para default/deployment.apps/goof, que muestra vulnerabilidades, dependencias, metadatos y verificaciones de configuración segura.

Si ahora revisamos la carga de trabajo, veremos que Snyk detectó la imagen base y todas sus vulnerabilidades, y recomendó imágenes base alternativas con menos exposición a vulnerabilidades.

Página de detalles de una imagen de AWS ECR que muestra los resultados del análisis de vulnerabilidades y recomendaciones de actualización para una imagen de contenedor

Para terminar

Esta demostración muestra cómo podemos empezar a integrar pruebas de seguridad en todo el ciclo de vida del desarrollo de software, una estrategia clave para mantener la seguridad de las cargas de trabajo y la infraestructura en entornos nativos de la nube.

Snyk también se integra con una amplia variedad de entornos de desarrollo integrados, herramientas de administración de código fuente y sistemas de CI/CD, para que puedas tener cobertura en todo el flujo de implementación.

Si usas los servicios de contenedores y Kubernetes de Amazon, es muy fácil integrar Snyk con ECR y EKS. Además, hay Quick Starts de AWS disponibles para implementar todo esto con plantillas de CloudFormation. ¡Crea una cuenta gratuita de Snyk y pruébalo!

Comienza con Capture the Flag

Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.