• Docker
  • Bonnes pratiques pour un hôte Docker en "prod"

Bonjour,

J'avais commencé il y a quelques temps à me mettre "doucement" et je m'y remets un peu plus sérieusement depuis quelques jours.

J'ai actuellement une VM qui me sert à faire mes tests et à ajuster mes docker-compose (j'hésite toujours à créer un mariaDB par service ou global, j'ai vu un autre sur ça 😃) mais maintenant j'aimerais connaître les bonnes pratiques pour le passer en "production".

J'ai vu qu'il y a un script de bench qui permet de tester son environnement Docker et qui fait ressortir des WARN sur les mauvais points, en point de repère est-ce que si certains l'utilisent il faut que tout soit en PASS ou certains points sont vraiment trop pointilleux ?
En gros de ce que j'ai vu pour le moment :

  • On crée une partition dédiée à Docker (/var/lib/docker)
  • On utilise "user: PID:GID" pour ne pas lancer le container en root
  • On audite les fichiers de Docker via auditd
  • Le reste que je n'ai pas encore regardé

Est-ce qu'il faut aussi mettre en place un firewall (ufw ou iptables) ? Est-ce qu'il y a des choses indispensables à prendre en compte pour la sécurité ou le bon fonctionnement ?

Merci !

Bonjour,

déjà si c'est pour de l'hébergement de services web, je conseille de mettre un reverse proxy (genre traefik ou nginx-proxy-manager) en front, et de ne jamais exposer les ports des containers directement sur le web.

On utilise "user: PID:GID" pour ne pas lancer le container en root
Oui et non. En fait, c'est surtout le container qui peut lancer des trucs en root, et c'est ça qui est problématique. Si tu lances un container avec un user lambda, mais que ce container a des process root, c'est lui qui est dangereux. Il y a un très bon tuto sur ce site justement qui explique tout ça.

L'histoire de la partition, pourquoi pas, mais je ne vois pas l’intérêt, si il y a une explication qui va avec, je suis preneur.

Merci pour ton avis 🙂

Pour le reverse proxy tout est déjà derrière Traefik v2 (sauf mon serveur VPN Wireguard car il est en UDP, ça me dérange un peu mais Traefik ne gère pas l'UDP pour le moment à priori donc pas possible de l'utiliser).

Pour la partition, c'est ce qui est donné par le script Docker Bench Security, mais je ne sais pas trop pourquoi. Pour ne pas impacter les fichiers Docker en cas de système corrompu ?! Pour la gestion du stockage sur cette partition ?!

    Merci Aerya pour l'info, je vais tester Anchore que je ne connaissais pas.

    Pour la partition je ne sais pas non plus, actuellement sur mon hôte de test je tourne avec une partition séparée mais s'il n'y a pas d'intérêt je vais retourner à une partition unique.

    Merrick, je n'ai pas trouvé le tuto dont tu parles, à moins que ce soit celui pour créer soi-même ses images Docker ?

    Vous faîtes quoi comme bonnes pratiques niveau hôte et sécurité pour vos mises en prod ? Merci 😉

    NicCo Pour le reverse proxy tout est déjà derrière Traefik v2 (sauf mon serveur VPN Wireguard car il est en UDP, ça me dérange un peu mais Traefik ne gère pas l'UDP pour le moment à priori donc pas possible de l'utiliser).

    J'ai des dockers derrière traefik en udp (pour être exact un teamspeak) et cela fonctionne bien.

    Si ça peut t'aider voici mon traefik.yml :

    global:
      checkNewVersion: false
      sendAnonymousUsage: false
    
    providers:
      file:
        directory: /etc/traefik/conf/
        watch: true
    
    api:
      insecure: false
      dashboard: true
    
    log:
      level: INFO
      filePath: /etc/traefik/traefik.log
    
    accessLog: true
    
    entryPoints:
      web:
        address: ":80"
      websecure:
        address: ":443"
      ftp:
        address: ":21"
      ts9987:
        address: ":9987/udp"
      ts9988:
        address: ":9988/udp"
      mc:
        address: ":25565/tcp"

    Et mon teamspeak.yml :

    udp:
      services:
        ts9987:
          loadBalancer:
            servers:
            - address: "teamspeak:9987"
        ts9988:
          loadBalancer:
            servers:
            - address: "teamspeak:9988"
      routers:
        ts9987:
          entryPoints:
            - "ts9987"
          service: "ts9987"
        ts9988:
          entryPoints:
            - "ts9988"
          service: "ts9988"

      Banip Merci pour l'info, j'avais effectivement vu quelque chose dans le genre. Par contre j'ai du mal à saisir l'interêt de le passer derrière Traefik si c'est pour ouvrir quand même un port supplémentaire ?!

        Salut,

        Pour une utilisation en production, j'aurais tendance à dire :

        • Utilisation des user-namespace, comme ça si dans le conteneur tu es root, sur l'hôte tu ne l'ai pas. Cela complexifie la gestion des volumes
        • éviter au maximum le partage du socket docker
        • Utiliser ces propres images, ou des images de confiance (linuxserver, binami, mondedie etc ...)
        • Separer les reseaux des conteneurs au strict minimum, si un conteneur n'a pas besoin du net, on lui mets un réseau que local
        • Utiliser iptables au lieu de docker-proxy
        • Limiter les ressources de chaque conteneur

        Pour moi c'est le minimum, après y'a des tweaks :

        • Utilisation d'un autre runtime comme firecracker ou kata-container, voir gvisor même si c'est un ptrace degueulasse
        • Utiliser docker en rootless voir podman qui est aussi daemonless

        Et je dirais que c'est deja pas mal.

          xataz Merci à toi 🙂 pour le minimum, tu as traité ces points dans ton tuto sur Docker ? Dans un autre ? Pas tous ?
          Il faut que je regarde les user-namespace que je ne connais pas et que je regarde ce qu'il faut configurer sur iptables que je ne connais pas très bien.
          Le partage du socket docker il faut que je regarde les containers qui l'utilisent, je n'ai que traefik et portainer il me semble.
          J'utilise les images officielles ou celles de linuxserver le plus souvent.
          Tous les containers derrière traefik sont sur un réseau traefik (+ un réseau mariadb pour les containers avec une BDD) et mon VPN est sur un réseau vpn.

          Merrick Merci 😉 j'ai lu le tuto mais par contre je n'ai rien trouvé concernant les user-namespaces et iptables

          C'est dans la configuration du daemon pour les deux.
          Tout est bien expliquer dans la doc officiel.

          Pour iptables, sur debian y'a un tweak à faire pour modifier l'alternative iptables-legacy au lieu de iptables-nftable

            NicCo Je ne comprends pas de quel port supplémentaire tu parles ? J'ai un teamspeak derrière 9987 et l'autre derrière 9988.

            Traefik pour une infra de prod c'est une des sécurisations évidentes, on masque les ports qui n'ont pas besoin d'être exposés de l’hôte vers le docker, dans mon exemple les ports 9987 et 9988 ne sont pas vraiment ouverts :

            root@machine:~# netstat -ntpl |egrep "9987"
            root@machine:~# netstat -ntpl |egrep "9988"
            root@machine:~#

            C'est simplement une entrée SRV dans le DNS qui indique que ts.domain.tld et ts2.domain.tld mènent aux ports 9987 et 9988 et c'est traefik qui dirige les requêtes vers le docker en question.

              Banip Merci, j'ai lu trop vite tes fichiers et j'ai cru que tu avais exposé les ports. Je vais regarder ta config, merci 😉

              xataz Merci, je vais regarder ça. Du coup avec ce type de config tu n'ouvres aucun port ? Même le 80/443 ?

              xataz J'ai lu la doc officielle et j'ai mis en place les user-namespaces (soit c'est hyper simple, soit mon environnement docker est simple, soit j'ai oublié de faire quelque chose). J'ai bien vérifié et le root dans le container ne permet plus de modifier les fichiers nécessitant une élévation sur l'hôte. Pour le moment je ne rencontre pas de problèmes, qu'est-ce qui est plus complexe sur la gestion des volumes (pour voir si je n'ai rien oublié) ? Je peux toujours utiliser un dossier docker (/home/myuser/docker) et une arborescence pour stocker les données permanentes ? Il faut quand même déclarer un PUID/GUID dans la déclaration docker-compose ? Et le PUID/GUID ne sont pas ceux de l'utilisateur utilisé pour le user-namespace ?

              J'ai lu aussi la doc pour iptables mais à part restreindre l'accès à une ou plusieurs IP (ce que je ne veux pas vu que je veux accéder à mon hôte depuis plusieurs connexions qui peuvent changer d'IP), je ne vois pas quoi faire avec ?!

              Merci

              Aerya La partition dédiée n'est pas forcément utile parce que sauf à te planter dans le montage, normalement, on container n'a pas accès à tout ton serveur.

              NON, un stockage séparé est indispensable en production, notamment par ce qu'en cas de saturation de l'espace disque le système hôte plante. Et pour d'autres raisons variable suivant la situation (fiabilité, performance, ...).

              Effectivement. Après... je suis du genre à surveiller mon espace disque 😉

              Merci pour les infos, je vais partir sur des partitions séparées ça ne me coûtera pas plus cher 😃
              Par contre j'ai toujours un peu de mal à saisir si je comprends bien la différence entre un user-namespace, un "user: PID:GID" déclaré dans le fichier docker-compose et un "environment: PUID=/PGID=". Si quelqu'un a une explication un peu claire je suis preneur 😉 merci !