Berceaux de sécurité: Docker



Les conteneurs Docker sont la technologie de conteneurisation la plus populaire. Initialement, il était principalement utilisé pour les environnements de développement et de test et, au fil du temps, il est passé à la production. Les conteneurs Docker ont commencé à se reproduire dans l'environnement de production, comme les champignons après la pluie, mais peu de ceux qui utilisent cette technologie ont réfléchi à la façon de publier en toute sécurité les conteneurs Docker.

Sur la base de OWASP , nous avons préparé une liste de règles, dont la mise en œuvre protégera considérablement votre environnement, basées sur des conteneurs Docker.

Règle 0

La machine hĂ´te et Docker doivent contenir toutes les mises Ă  jour actuelles.


Pour se protéger contre les vulnérabilités connues qui conduisent à s'échapper de l'environnement de conteneur vers le système hôte, ce qui entraîne généralement une élévation de privilèges sur le système hôte, l'installation de tous les correctifs pour le système d'exploitation hôte, Docker Engine et Docker Machine est extrêmement importante.

De plus, les conteneurs (contrairement aux machines virtuelles) partagent le noyau avec l'hôte, donc l'exploit du noyau s'exécutant à l'intérieur du conteneur s'exécute directement dans le noyau de l'hôte. Par exemple, un exploit d'escalade de privilèges du noyau (tel que Dirty COW) exécuté dans un conteneur bien isolé entraînera un accès root sur l'hôte.

Règle 1

Ne donnez pas accès au socket du démon Docker


Le service Docker (démon) utilise le socket UNIX /var/run/docker.sock pour les connexions API entrantes. Le propriétaire de cette ressource doit être l'utilisateur root. Et pas d'autre moyen. La modification des droits d'accès à ce socket équivaut essentiellement à accorder un accès root au système hôte.

De plus, vous ne devez pas tâtonner le socket /var/run/docker.sock avec des conteneurs, où vous pouvez vous en passer, car dans ce cas, compromettre le service dans le conteneur entraînera un contrôle complet sur le système hôte. Si vous avez des conteneurs qui utilisent quelque chose comme ceci:

-v /var/run/docker.sock://var/run/docker.sock 

ou pour docker-compose:

 volumes: - "/var/run/docker.sock:/var/run/docker.sock" 

besoin urgent de changer cela.

Et le dernier - n'écoutez jamais, n'utilisez jamais la socket Docker TCP sans la certitude absolue que vous en avez besoin, en particulier sans l'utilisation de méthodes de protection supplémentaires (au moins une autorisation). Par défaut, le socket Docker TCP ouvre un port sur l'interface externe 0.0.0.0:2375 (2376, dans le cas des HTTP) et permet un contrôle total des conteneurs, et avec lui le système hôte potentiel.

Règle 2

Configurer un utilisateur non privilégié à l'intérieur du conteneur


La configuration d'un conteneur pour utiliser un utilisateur non privilégié est le meilleur moyen d'éviter une attaque par élévation de privilèges. Cela peut se faire de différentes manières:

1. En utilisant l'option "-u" de la commande "docker run":

 docker run -u 4000 alpine 

2. Pendant la construction de l'image:

 FROM alpine RUN groupadd -r myuser && useradd -r -g myuser myuser <      root-, ,  > USER myuser 

3. Activez la prise en charge de "l'espace de noms utilisateur" (environnement utilisateur) dans le démon Docker:

 --userns-remap=default 

En savoir plus Ă  ce sujet dans la documentation officielle .

Dans Kubernetes, ce dernier est configuré dans le contexte de sécurité via l'option runAsNonRoot:

 kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... runAsNonRoot: true ... 

Règle 3

Limiter les capacités des conteneurs


Sous Linux, à partir du noyau 2.2, il existe un moyen de contrôler les capacités des processus privilégiés appelés capacités du noyau Linux (pour plus de détails, voir le lien).

Docker utilise par défaut un ensemble prédéfini de ces fonctionnalités du noyau. Et cela vous permet de modifier cet ensemble à l'aide des commandes:

 --cap-drop —     --cap-add —     

Le meilleur paramètre de sécurité consiste à désactiver d'abord toutes les fonctionnalités (--cap-drop all), puis à ne connecter que celles nécessaires. Par exemple, comme ceci:

 docker run --cap-drop all --cap-add CHOWN alpine 

Et le plus important (!): Évitez d'exécuter des conteneurs avec le drapeau privilégié !!!

Dans Kubernetes, la contrainte de capacités du noyau Linux est configurée dans le contexte de sécurité via l'option de capacités:

 kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... capabilities: drop: - all add: - CHOWN ... 

Règle 4

Utilisez l'indicateur no-new-privileges


Lors du démarrage d'un conteneur, il est utile d'utiliser l'indicateur --security-opt = no-new-privileges qui empêche l'escalade de privilèges à l'intérieur du conteneur.

Dans Kubernetes, la contrainte de capacités du noyau Linux est configurée dans le contexte de sécurité via l'option allowPrivilegeEscalation:

 kind: ... apiVersion: ... metadata: name: ... spec: ... containers: - name: ... image: .... securityContext: ... allowPrivilegeEscalation: false ... 

Règle 5

Désactiver la communication entre conteneurs


Par défaut, la communication entre conteneurs est activée dans Docker, ce qui signifie que tous les conteneurs peuvent communiquer entre eux (en utilisant le réseau docker0). Cette fonctionnalité peut être désactivée en exécutant le service Docker avec l'indicateur –icc = false.

Règle 6

Utiliser des modules de sécurité Linux (Module de sécurité Linux - seccomp, AppArmor, SELinux)


Par défaut, Docker utilise déjà des profils pour les modules de sécurité Linux. Par conséquent, ne désactivez jamais les profils de sécurité! Le maximum que l'on puisse en faire est de resserrer les règles.

Le profil par défaut de seccomp est disponible ici .

Docker utilise également AppArmor pour la protection, et le moteur Docker lui-même génère un profil par défaut pour AppArmor au démarrage du conteneur. En d'autres termes, au lieu de:

 $ docker run --rm -it hello-world 

démarre:

 $ docker run --rm -it --security-opt apparmor=docker-default hello-world 

La documentation fournit également un exemple de profil AppArmor pour nginx, ce qui est tout à fait possible (nécessaire!) À utiliser:

 #include <tunables/global> profile docker-nginx flags=(attach_disconnected,mediate_deleted) { #include <abstractions/base> network inet tcp, network inet udp, network inet icmp, deny network raw, deny network packet, file, umount, deny /bin/** wl, deny /boot/** wl, deny /dev/** wl, deny /etc/** wl, deny /home/** wl, deny /lib/** wl, deny /lib64/** wl, deny /media/** wl, deny /mnt/** wl, deny /opt/** wl, deny /proc/** wl, deny /root/** wl, deny /sbin/** wl, deny /srv/** wl, deny /tmp/** wl, deny /sys/** wl, deny /usr/** wl, audit /** w, /var/run/nginx.pid w, /usr/sbin/nginx ix, deny /bin/dash mrwklx, deny /bin/sh mrwklx, deny /usr/bin/top mrwklx, capability chown, capability dac_override, capability setuid, capability setgid, capability net_bind_service, deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) # deny write to files not in /proc/<number>/** or /proc/sys/** deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w, deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel) deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/ deny @{PROC}/sysrq-trigger rwklx, deny @{PROC}/mem rwklx, deny @{PROC}/kmem rwklx, deny @{PROC}/kcore rwklx, deny mount, deny /sys/[^f]*/** wklx, deny /sys/f[^s]*/** wklx, deny /sys/fs/[^c]*/** wklx, deny /sys/fs/c[^g]*/** wklx, deny /sys/fs/cg[^r]*/** wklx, deny /sys/firmware/** rwklx, deny /sys/kernel/security/** rwklx, } 

Règle 7

Limiter les ressources de conteneur


Cette règle est assez simple: afin d'empêcher les conteneurs de dévorer toutes les ressources du serveur lors de la prochaine attaque DoS / DDoS, nous pouvons définir des limites d'utilisation de la mémoire pour chaque conteneur individuellement. Vous pouvez limiter: la quantité de mémoire, le processeur, le nombre de redémarrages du conteneur.

Alors allons-y dans l'ordre.

La mémoire

L'option -m ou --memory

Quantité maximale de mémoire qu'un conteneur peut utiliser. La valeur minimale est de 4 m (4 mégaoctets).

Option - échange de mémoire

Option pour configurer l'échange (fichier d'échange). Configuré astucieusement:

  • Si --memory-swap> 0, l'indicateur –memory doit Ă©galement ĂŞtre dĂ©fini. Dans ce cas, memory-swap indique la quantitĂ© totale de mĂ©moire disponible pour le conteneur avec swap.
  • Un exemple plus simple. Si --memory = "300m" et --memory-swap = "1g", le conteneur peut utiliser 300 Mo de mĂ©moire et 700 Mo de swap (1 g - 300 m).
  • Si --memory-swap = 0, le paramètre est ignorĂ©.
  • Si --memory-swap a la mĂŞme valeur que --memory, le conteneur n'aura pas de swap.
  • Si --memory-swap n'est pas spĂ©cifiĂ©, mais --memory est spĂ©cifiĂ©, le nombre de swap sera Ă©gal Ă  deux fois la quantitĂ© de mĂ©moire spĂ©cifiĂ©e. Par exemple, si --memory = "300m" et --memory-swap n'est pas dĂ©fini, le conteneur utilisera 300 Mo de mĂ©moire et 600 Mo de swap.
  • Si --memory-swap = -1, le conteneur utilisera tout le swap possible sur le système hĂ´te.

Remarque à l'hôtesse: l' utilitaire gratuit lancé à l'intérieur du conteneur n'affiche pas la valeur réelle du swap disponible pour le conteneur, mais le nombre de swap d'hôte.

Option --oom-kill-disable

Vous permet d'activer ou de désactiver le tueur OOM (Out of memory).

Attention! Vous pouvez désactiver OOM Killer uniquement avec l'option --memory définie, sinon il peut arriver qu'avec une mémoire insuffisante à l'intérieur du conteneur, le noyau commence à tuer les processus du système hôte.

D'autres options de configuration de la gestion de la mémoire, telles que --memory-swappiness, --memory-reservation et --kernel-memory, sont davantage destinées à optimiser les performances du conteneur.

CPU

Option --cpus

L'option définit la quantité de ressources processeur disponibles que le conteneur peut utiliser. Par exemple, si nous avons un hôte avec deux processeurs et que nous définissons --cpus = "1.5", le conteneur est garanti d'utiliser un processeur et demi.

Option --cpuset-cpus

Configure l'utilisation de cœurs ou de processeurs spécifiques. La valeur peut être spécifiée avec un trait d'union ou une virgule. Dans le premier cas, la plage de cœurs autorisés sera indiquée, dans le second - cœurs spécifiques.

Nombre de redémarrages du conteneur

 --restart=on-failure:<number_of_restarts> 

Ce paramètre définit le nombre de fois où Docker tentera de redémarrer le conteneur en cas de panne inattendue. Le compteur est réinitialisé si l'état du conteneur est passé à en cours d'exécution.

Il est recommandé de définir un petit nombre positif, par exemple 5, ce qui évitera les redémarrages sans fin d'un service qui ne fonctionne pas.

Règle 8

Utiliser des systèmes de fichiers et des volumes en lecture seule


Si le conteneur ne doit rien écrire quelque part, vous devez utiliser autant que possible le système de fichiers en lecture seule. Cela compliquera considérablement la vie d'un intrus potentiel.

Un exemple de démarrage d'un conteneur avec un système de fichiers en lecture seule:

 docker run --read-only alpine 

Un exemple de connexion de volume en mode lecture seule:

 docker run -v volume-name:/path/in/container:ro alpine 

Règle 9

Utiliser des outils d'analyse de sécurité des conteneurs


Des outils doivent être utilisés pour détecter les conteneurs présentant des vulnérabilités connues. Il n'y en a pas encore beaucoup, mais ce sont:

• Gratuit:


• Commercial:


Et pour Kubernetes, il existe des outils pour détecter les erreurs de configuration:

Source: https://habr.com/ru/post/fr448704/


All Articles