LXC : Créer un conteneur servant de modèle

Rédigé par Alexandre le 2020-06-26

#auto-hébergement #debian #loisir

Après avoir mis en place une infrastructure LXD, il convient de créer un premier conteneur.

Dans mon cas, j'ai choisi de créer un conteneur servant de "modèle" à tous les autres. Des méthodes un peu plus technique existent pour faire exactement la même chose mais en partant des modèles officiels (cloud-init ou salt pour n'en citer que deux).

Cet article documente la création et configuration d'un conteneur LXC servant de base à l'ensemble de mes conteneurs.

Instancier

Définir le nom du nouveau conteneur :

target="modele-buster"

Instancier le conteneur, ici en Debian Buster :

lxc launch images:debian/buster $target

Réseau

Histoire de vivre avec mon temps, j'utilise systemd pour la configuration réseau. Il est nécessaire de créer trois fichiers :

  • /etc/systemd/network/1-lo.network : interface de loopback
  • /etc/systemd/network/2-eth0.network : interface du pont interne pour IPv4 (LAN)
  • /etc/systemd/network/3-eth1.network : interface du pont externe pour IPv6 (WAN)
lxc exec $target -- tee /etc/systemd/network/1-lo.network <<EOF
[Match]
Name=lo

[Network]
Address=127.0.0.1/8
Address=::1/128
EOF
lxc exec $target -- tee /etc/systemd/network/2-eth0.network <<EOF
[Match]
Name=eth0

[Network]
DHCP=no
IPv6AcceptRA=no
LinkLocalAddressing=no

Address=192.168.3.189/24
Gateway=192.168.3.254
DNS=80.67.169.12 80.67.169.40
EOF
lxc exec $target -- tee /etc/systemd/network/3-eth1.network <<EOF
[Match]
Name=eth1

[Network]
DHCP=no
LinkLocalAddressing=no

Address=2001:41d0:8:XXXX::3189/64
Gateway=2001:41d0:8:XXXX::1

DNS=2001:910:800::40 2001:910:800::12
EOF

Désactiver l'écoute du serveur DNS de systemd :

lxc exec $target -- sed -i 's/#DNSStubListener=yes/DNSStubListener=no/g' /etc/systemd/resolved.conf

Activer le réseau par systemd :

lxc exec $target -- systemctl unmask systemd-networkd.service systemd-networkd.socket systemd-networkd-wait-online.service
lxc exec $target -- systemctl enable systemd-networkd systemd-resolved

Supprimer l'ancien fonctionnement :

lxc exec $target -- apt purge ifupdown netbase --autoremove
lxc exec $target -- rm -R /etc/network
lxc exec $target -- rm /etc/resolv.conf
lxc exec $target -- ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf

Temps

Tout comme pour le réseau, j'utilise systemd pour maintenir le conteneur à l'heure :

lxc exec $target -- tee /etc/systemd/timesyncd.conf <<EOF
[Time]
NTP=0.fr.pool.ntp.org 1.fr.pool.ntp.org 2.fr.pool.ntp.org 3.fr.pool.ntp.org
FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org
EOF

Activer le temps par systemd :

lxc exec $target -- timedatectl set-ntp true

Changer le fuseau horaire :

lxc exec $target -- dpkg-reconfigure tzdata

Dans la fenêtre qui s'ouvre :

  • Sélectionner Europe
  • Valider avec la touche Entrée
  • Chercher Paris
  • Terminer avec la touche Entrée

Langue

Passer le système en français :

lxc exec $target -- dpkg-reconfigure locales

Dans la fenêtre qui s'ouvre :

  • Chercher en_US.UTF-8 UTF-8
  • Décocher en appuyant sur la barre espace
  • Chercher fr_FR.UTF-8
  • Cocher en appuyant sur la barre espace
  • Valider avec la touche Entrée
  • Choisir fr_FR.UTF-8
  • Terminer avec la touche Entrée

Système

Changer le mot le passe du super-utilisateur :

lxc exec $target -- passwd

Créer un fichier personnel de configuration d'apt :

lxc exec $target -- tee /etc/apt/apt.conf.d/99custom <<EOF
APT::Install-Recommends "false";
APT::Install-Suggests "false";
APT::Get::Show-Versions "true";
APT::Get::Show-Upgraded "true";
APT::Get::Purge "true";
APT::Get::List-Cleanup "true";
EOF

Utiliser les dépôts français :

lxc exec $target -- tee /etc/apt/sources.list <<EOF
deb http://ftp.fr.debian.org/debian/ buster main
deb http://ftp.fr.debian.org/debian/ buster-updates main
deb http://security.debian.org/debian-security buster/updates main
EOF

Ajouter le dépôt des paquets rétro-portés :

lxc exec $target -- tee /etc/apt/sources.list.d/backports.list <<EOF
deb http://ftp.fr.debian.org/debian/ buster-backports main
EOF

/!\ Attention à la distribution et sa version !

Redémarrer :

lxc restart $target

Paquets

Purger les paquets inutiles :

lxc exec $target -- apt purge --autoremove isc-dhcp* krb5-locales openssh-client vim* xauth

Chercher et appliquer les mises à jour :

lxc exec $target -- apt update && lxc exec $target -- apt -y dist-upgrade

Nettoyer le système puis chercher et désinstaller les paquets orphelins :

lxc exec $target -- apt autoremove --purge && lxc exec $target -- apt install -y deborphan dialog && lxc exec $target -- orphaner && lxc exec $target -- apt purge -y --autoremove deborphan

Une fenêtre affiche la liste des paquets orphelins :

  • Cocher les paquets en question avec la barre espace
  • Désinstaller les paquets en appuyant sur la touche Entrée
  • Renouveller l'opération jusqu'à ce qu'il n'y ait plus de paquets orphelins

Installer les outils habituels :

lxc exec $target -- apt install nano apt-utils htop openssh-server locate less ca-certificates msmtp msmtp-mta nftables rsyslog curl iputils-ping sudo cron rsync logrotate

SSH

Créer un utilisateur :

lxc exec $target -- adduser ct

Permettre à l'utilisateur d'élever ses privilèges :

lxc exec $target -- adduser ct sudo

Récupérer l'UID de l'utilisateur créé :

ctUser=$(lxc exec $target -- id -u ct)

Ajouter la clef de l'hôte :

lxc exec $target --user $ctUser -- mkdir /home/ct/.ssh/ && lxc exec $target -- chmod go-rwx /home/ct/.ssh/ && lxc exec $target -- tee /home/ct/.ssh/authorized_keys <<EOF
ssh-ed25519 <clef SSH> pulsar@zira
EOF

Une fois que c'est fait, il est possible de procéder à la sécurisation de SSH.

Courriel

Configurer msmtp pour que les utilisateurs système puissent envoyer des courriels.

Pare-feu

Créer les règles du pare-feu (ici on autorise SSH sur le port 22 depuis l'hôte) (adapter les IP !) :

lxc exec $target -- tee /etc/nftables.conf <<EOF
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority 0;

                # accept any localhost traffic
                iif lo accept

                # accept traffic originated from us
                ct state established,related accept

                # accept neighbour discovery otherwise IPv6 connectivity breaks.
                ip6 nexthdr icmpv6 icmpv6 type { nd-neighbor-solicit,  nd-router-advert, nd-neighbor-advert } accept

                ## Custom
                # Allow SSH (port 22) from zira
                ip saddr 192.168.3.254 tcp dport 22 accept
                ip6 saddr 2001:41d0:8:XXXX::1 tcp dport 22 accept

                # count and drop any other traffic
                counter drop
        }
}
EOF

Activer nftables au démarrage et immédiatement :

lxc exec $target -- systemctl enable nftables && lxc exec $target -- systemctl start nftables

Auto-bannissement

Faute de documentation récente traitant de nftables+fail2ban, j'ai laissé cette partie en attente. Si certains se sentent de tester des choses, voici ce que j'ai pu lire sur le sujet :

  • https://wiki.meurisse.org/wiki/Fail2Ban#nftables
  • https://mondedie.fr/d/10907-securisation-serveur-debian-10

Avant que certains pensent à s'en passer complètement, un outil d'auto-bannissement tel que fail2ban permet de protéger des services qui ne peuvent pas l'être autrement. Je pense par exemple à un htpasswd qui permettra à un attaquant d'essayer autant de combinaisons qu'il le souhaite pour se connecter (attaque par force brute).

Terminer

Redémarrer simplement le conteneur :

lxc restart $target

Afficher la liste des conteneurs :

lxc list

Ce qui devrait retourner quelque chose comme ça :

+--------------------------+---------+----------------------+-------------------------------+-----------+-----------+
|           NAME           |  STATE  |         IPV4         |             IPV6              |   TYPE    | SNAPSHOTS |
+--------------------------+---------+----------------------+-------------------------------+-----------+-----------+
| modele-buster            | RUNNING | 192.168.3.189 (eth0) | 2001:41d0:8:XXXX::3189 (eth1) | CONTAINER | 0         |
+--------------------------+---------+----------------------+-------------------------------+-----------+-----------+

Afficher plus de détail sur le conteneur avec la commande suivante :

lxc info $target