Archives par étiquette : docker

Docker et NFTtables

Il y a un moment déjà, j’avais critiqué la manière dont Docker interagissait par défaut au niveau réseau. Rien n’a changé ces dernières années sur ce point.

Ce qui a changé par contre (autre le fait qu’on ne devrait plus parler de Docker, mais de runC, de rkt, de k8s, … ou de Docker) c’est que la plupart des systèmes ont (enfin) migré du vénérable IPTables vers NFTables. C’est là que le bât blesse, les API de gestion des conteneurs travaillent encore majoritairement avec IPTables uniquement.

Il est néanmoins possible de faire cohabiter les 2, au prix d’une configuration NFTables un peu moins simple et performante, pour permettre à iptables-nft de correctement transformer les règles IPTables demandées par vos conteneurs en règles NFTables.

Ci-dessous une configuration minimaliste compatible, ce qui importe ici, c’est de nommer ses chaines comme avec IPTables et de séparer IPv4 d’IPv6 (donc on ne peut pas utiliser les tables inet) :

#!/usr/sbin/nft -f

define SRVaddrV4 = 198.51.100.1
define SRVaddrV6 = 2001:db8::/32

flush ruleset

table ip filter {
chain INPUT {
type filter hook input priority 0; policy drop;
ct state invalid counter drop comment « drop invalid packets »
ct state {established, related} counter accept comment « accept all established or related »
iifname lo accept comment « accept loopback if »
iifname != lo ip daddr 127.0.0.0/8 counter drop comment « drop connections to loopback not coming from loopback v4 »
ip protocol icmp counter accept comment « Accept all icmp types v4 »
ip saddr $SRVaddrV4 counter accept comment « Accept all from server »

tcp dport {80, 443} counter accept comment « Accept HTTP/HTTPS »

counter comment « count accepted packets »
drop comment « Final DROP »
}
chain FORWARD {
type filter hook forward priority 0; policy drop;
counter comment « count dropped packets »
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
counter comment « count accepted packets »
}
}

table ip6 filter {
chain INPUT {
type filter hook input priority 0; policy drop;
ct state invalid counter drop comment « drop invalid packets »
ct state {established, related} counter accept comment « accept all established or related »
iifname lo accept comment « accept loopback if »
iifname != lo ip6 daddr ::1/128 counter drop comment « drop connections to loopback not coming from loopback v6 »
ip6 nexthdr icmpv6 counter accept comment « Accept all icmp types v6 »
ip6 saddr $SRVaddrV6 counter accept comment « Accept all from server »

tcp dport {80, 443} counter accept comment « Accept HTTP/HTTPS »

counter comment « count accepted packets »
drop comment « Final DROP »
}
chain FORWARD {
type filter hook forward priority 0; policy drop;
counter comment « count dropped packets »
}
chain OUTPUT {
type filter hook output priority 0; policy accept;
counter comment « count accepted packets »
}
}

table ip nat {
chain PREROUTING {
type nat hook prerouting priority -100; policy accept;
}
chain POSTROUTING {
type nat hook postrouting priority 100; policy accept;
}
chain INPUT {
type nat hook input priority -100; policy accept;
}
chain OUTPUT {
type nat hook output priority -100; policy accept;
}
}

Avec cette base, les règles nécessaires pour vos conteneurs devraient fonctionner sans interférer avec vos propres règles.

nb : si vous devez recharger ce fichier, pensez à relancer les services de conteneurs (docker.service par exemple) afin qu’ils réinjectent leurs règles de base.

On pourrait aller plus loin en ajoutant directement les tables nécessaires, ce qui permettrait de n’avoir qu’à relancer les conteneurs, mais je ne trouve pas ça portable ni plus efficace. Si le downtime n’est pas une option, injectez vos règles (nft add/delete …) directement sans toucher à la configuration.

sources :

  • https://blog.ghostinashell.com/linux/nftables/2020/03/07/nftables.html
  • https://ehlers.berlin/blog/nftables-and-docker/

Docker et les réseaux

Docker est probablement l’une des « technologies » les plus importantes de ces dernières années. Bien que s’appuyant sur des briques et des principes qui existaient depuis longtemps (en particulier LXC), l’équipe de Docker a réussi à s’imposer là ou tous les autres ont échoué, en grande partie grâce à sa simplicité (de façade).

Mais en essayant de tout simplifier pour les débutants ou les développeurs, ils ont parfois fait des choix techniques plus que discutables.

Pour un développeur qui travail dans son coin ce n’est pas gênant et ça rend les choses plus simples, mais quand vient l’heure de la mise en production, de gros problèmes peuvent survenir.

Heureusement on peut maintenant (ça n’a pas toujours été le cas) changer tout ou partie de ce comportement, c’est ce qu’on va voir ici. Continuer la lecture

Mémo – Docker

Quelques notes sur l’utilisation de docker.

Je ne vais pas détailler ici ce qu’est docker ni à quoi ça peut servir, il y a suffisamment d’articles sur le net. Pour faire simple, docker est un système de gestion de conteneurs.

Dockerfile

Un Dockerfile est un fichier d’instructions permettant de créer une image. Il doit toujours s’appeler Dockerfile.

# fenrir/test
# Debian latest
# Test image
#
# VERSION 0.0.1
#
FROM debian:latest
MAINTAINER Fenrir <mon.addresse@de.messagerie>

ENV DEBIAN_FRONTEND noninteractive

# Install ssh
RUN apt-get update && apt-get install -y -q ssh

# Configure ssh
RUN mkdir /var/run/sshd
RUN echo 'root:password' | chpasswd
RUN sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config
RUN rm /etc/ssh/ssh_host_*_key.pub

# Start ssh server
CMD ["/usr/sbin/sshd", "-D"]

# Expose port
EXPOSE 22

Image

Les images sont les briques de bases de docker, on peut voir une image comme un instantané d’un système avec sa configuration et ses applications. C’est à partir de ces images que sont créés les conteneurs, mais une image peut aussi servir de base pour une autre image (comme dans le Dockerfile ci-dessus).

Conteneur

C’est principalement ça qu’on manipule, un conteneur est une branche exécutable d’une image. Les bonnes pratiquent veulent qu’un conteneur soit jetable à tout moment sans perte de données, dit autrement, un conteneur ne devrait rien contenir d’autre que ses programmes. Tout ce qui est données (par exemple les pages d’un site web et le contenu de sa base de données) devraient être en dehors du conteneur, par exemple dans des volumes.

Volume

On vient de dire qu’un conteneur doit être jetable, il faut donc enregistrer les données dans un endroit persistant, on peut le faire de plusieurs manières, les plus courantes sont :

  • un conteneur dédié : c’est un conteneur qui n’aura comme but que d’accueillir les données d’autres conteneurs, évidement ce conteneur ne sera pas à jeter
  • un dossier (ou un fichier) de l’hôte : il sera monté dans le conteneur
  • un « data volume » : c’est un dossier particulier, créé au démarrage du conteneur mais qui est conservé à la destruction du conteneur
  • un mixte de tout ou partie des 3 : par exemple un conteneur dédié dans lequel on a mappé un dossier de l’hôte

Une bonne pratique consiste à utiliser un conteneur dédié, car ça offre quelques facilités

  • partage des données entre conteneurs
  • déplacement des conteneurs et de leurs données vers un autre serveur

Commande

Il existe de nombreuses interfaces pour manipuler les conteneurs, mais il est préférable de commencer par utiliser les commandes car dans la plupart des cas c’est :

  • plus rapide
  • plus fiable
  • plus simple
  • plus reproductible
  • plus facile à expliquer

Ci dessous les commandes courantes, pour la liste complète : Docker CLI

Les valeurs entre <> sont à adapter (sans les chevrons)

nb : la plupart des commandes disposent d’options, vous pouvez les obtenir avec :

docker <commande> --help

Image

docker images
docker search <terme à rechercher>
docker pull <nom de l'image>
docker rmi <nom ou ID de l'image>
docker build -t <nom de l'image> </chemin/vers/le/dockerfile/.>
docker commit <nom ou ID du conteneur> <nom de l'image>

Conteneur

docker ps
docker create --name <nom ou ID du conteneur> <nom de l'image>
docker run --name <nom ou ID du conteneur> <nom de l'image>
-p <port de l'hôte:port du conteneur>
-v <dossier de l'hôte:dossier dans le conteneur>
docker attach <nom ou ID du conteneur>
docker exec <nom ou ID du conteneur> <commande à exécuter>
docker inspect <nom ou ID du conteneur>
docker kill <nom ou ID du conteneur>
docker rename <nom ou ID du conteneur> <nom de l'image>
docker start <nom ou ID du conteneur>
docker stop <nom ou ID du conteneur>
docker restart <nom ou ID du conteneur>
docker top <nom ou ID du conteneur>
docker diff <nom ou ID du conteneur>
docker rm <nom ou ID du conteneur>

Exemple de commandes

docker run -i -t --name mon-conteneur -d debian:latest /bin/bash
docker exec mon-conteneur ls /etc
docker attach mon-conteneur