1
0
Fork 0
3 creation template lxc debian 12 bookworm
Chl edited this page 2023-11-05 02:01:16 +01:00

Hyperviseur pour développeur avec template LXC unprivileged Debian 12 Bookworm

Ce document retrace la configuration d'une Debian 12 Bookworm afin d'en faire un "hyperviseur de développeur", c'est à dire un peu comme Docker, faire tourner des conteneurs en partageant certains sous-répertoires avec l'hôte.

On retracera aussi la création d'un template de conteneur unprivileged d'une Debian 12.

Quelques points annexes :

  • l'utilisation courante doit pouvoir se faire avec des droits simples (non-root et non-sudoer),
  • dans ce document, ledit utilisateur a comme uid 1000. À adapter au besoin.
  • /home sera une partition Btrfs

Avertissements liminaires :

  • lxc-create -t debian dit explicitement ne pas gérer les conteneurs non-privilégiés, cependant je n'ai trouvé aucune contre-indication à créer le template privilégié et faire un uidshift maison. C'est la piste détaillée ici.
  • une autre piste consiste à partir d'un debootstrap et faire le travail manuellement. Il faut cependant penser à initialiser pas mal de petites choses comme pour openvz (locales, fuseau horaire, /etc/machine-id, etc.)
  • ce tuto est en bêta : caveat emptor.

Configuration de l'hôte

Installation du paquet lxc et activation (optionnelle) des quotas sur la partition Btrfs :

# en root (sudo -i)
apt install lxc
btrfs quota enable /home

Configuration de la partition Btrfs (ajout de user_subvol_rm_allowed dans /etc/fstab pour pouvoir supprimer des conteneurs) :

# /etc/fstab
/dev/mapper/blablavg-home    /home       btrfs   user_subvol_rm_allowed        0       0

Configuration des accès non-privilégiés (réseau + uidmap) :

# /etc/lxc/lxc-usernet
ltorvalds   veth    br0     10
# /etc/subuid
ltorvalds:100000:65536
# /etc/subgid
ltorvalds:100000:65536

Ensuite, on reconfigure l'interface réseau en tant que bridge :

  • normalement, le paquet bridge-utils a été installé en dépendance de lxc mais par prudence, vérifier qu'il est bien là : dpkg -l bridge-utils
  • on en profite pour ajouter quelques interfaces virtuelles tap qui peuvent servir lors de besoins précis (tunnels, VM Qemu)
  • TODO: si l'interface physique est une carte wifi, c'est plus compliqué. À mettre ici à l'occasion.
# /etc/network/interfaces

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto br0
iface br0 inet static
	pre-up ip tuntap add dev tap0 mode tap user ltorvalds
	pre-up ip link set tap0 up
	pre-up ip tuntap add dev tap1 mode tap user ltorvalds
	pre-up ip link set tap1 up
	pre-up ip tuntap add dev tap2 mode tap user ltorvalds
	pre-up ip link set tap2 up
	bridge_ports enp0s25 tap0 tap1 tap2
	bridge_stp off
	bridge_maxwait 5
	bridge_fd 0
	address 192.168.0.5/24
	gateway 192.168.0.1
	post-down ip link set tap0 down
	post-down ip tuntap del dev tap0 mode tap
	post-down ip link set tap1 down
	post-down ip tuntap del dev tap1 mode tap
	post-down ip link set tap2 down
	post-down ip tuntap del dev tap2 mode tap

iface br0 inet6 static
	address 2a01:abcd:ef::5/112
	gateway 2a01:abcd:ef::1

Enfin, on prépare la configuration de l'utilisateur

# en utilisateur lambda
mkdir /home/ltorvalds/lxc
mkdir -p /home/ltorvalds/.config/lxc
echo "lxc.lxcpath = /home/ltorvalds/lxc" >> /home/ltorvalds/.config/lxc/lxc.conf

Note: par défaut, la charge (loadavg) dans un conteneur est celle de l'hôte. Il est possible de containeriser cette valeur mais cela semble consommer des ressources (CPU + locking) :

# /etc/systemd/system/lxcfs.service.d/override.conf
[Service]
ExecStart=/usr/bin/lxcfs /var/lib/lxcfs --enable-loadavg

Création du template

On commence par créer un répertoire de cache dans le même volume Btrfs, puis on lance la commande de création :

# en root (sudo -i)

cd /home/ltorvalds/lxc
mkdir cache
LXC_CACHE_PATH=/home/ltorvalds/lxc/cache MIRROR=http://apt-proxy.example.net:3142/debian SECURITY_MIRROR=http://apt-proxy.example.net:3142/debian lxc-create -n debian-12-bookworm-systemd-lxccreate  -P /home/ltorvalds/lxc -t debian -B btrfs -- -r bookworm

On complète notre magnifique template ainsi :

# en root (sudo -i)

# nettoyage
btrfs subvol delete cache/debian/rootfs-bullseye-amd64
rm -rvf cache/

# droits
chown 100000:ltorvalds debian-12-bookworm-systemd-lxccreate
chown ltorvalds:ltorvalds debian-12-bookworm-systemd-lxccreate/config
# Repris de https://unix.stackexchange.com/questions/127554/building-unprivileged-userns-lxc-container-from-scratch-by-migrating-a-privil/420317#420317
export SUBUID=100000
export SUBGID=100000
#find debian-buster-lxccreate/rootfs | while read i; do CURRENT_UID=$(stat --format=%u $i); CURRENT_GID=$(stat --format=%g $i); NEW_SUBUID=$((CURRENT_UID+SUBUID)); NEW_SUBGID=$((CURRENT_GID+SUBGID)); echo "chown -h $NEW_SUBUID.$NEW_SUBGID $i"; done
# Version "noms-de-fichiers-bizarres". Attention bashism sur 'read -d'.
find debian-12-bookworm-systemd-lxccreate/rootfs -print0 | while IFS= read -r -d '' i; do CURRENT_UID=$(stat --format=%u "$i"); CURRENT_GID=$(stat --format=%g "$i"); NEW_SUBUID=$((CURRENT_UID+SUBUID)); NEW_SUBGID=$((CURRENT_GID+SUBGID)); [ $CURRENT_UID -lt $SUBUID ] && [ $CURRENT_GID -lt $SUBGID ] && chown -h $NEW_SUBUID:$NEW_SUBGID "$i" || echo "uid or gid already changed for $i ?"; done

Adaptation de la configuration

On remplace la config par :

# For additional config options, please look at lxc.container.conf(5)
# Distribution configuration
#lxc.include = /usr/share/lxc/config/debian.common.conf
#lxc.include = /usr/share/lxc/config/debian.userns.conf
lxc.include = /usr/share/lxc/config/common.conf
lxc.tty.dir =
lxc.arch = linux64
# Container specific configuration
lxc.tty.max = 4
lxc.arch = amd64
lxc.pty.max = 1024
# À 1000, on arrête la traduction des UID et on reprend ensuite
# pour avoir une correspondance sur les montages partagés.
#lxc.idmap = u 0 100000 65536
#lxc.idmap = g 0 100000 65536
lxc.idmap = u 0 100000 1000
lxc.idmap = g 0 100000 1000
lxc.idmap = u 1000 1000 1
lxc.idmap = g 1000 1000 1
lxc.idmap = u 1001 101001 64534
lxc.idmap = g 1001 101001 64534
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
# FS configuration
lxc.rootfs.path = btrfs:/home/ltorvalds/lxc/debian-12-bookworm-systemd-lxccreate/rootfs

# Point de montage à partager avec l'hôte
#lxc.mount.entry = /home/ltorvalds/dev/project-ro home/ltorvalds/dev/project-ro none bind,create=dir,ro 0 0
#lxc.mount.entry = /home/ltorvalds/dev/project-rw home/ltorvalds/dev/project-rw none bind,create=dir 0 0

# Network configuration
lxc.uts.name = debian-12-bookworm-systemd-lxccreate
#lxc.net.0.type = empty
lxc.net.0.type = veth
lxc.net.0.link = br0
lxc.net.0.flags = up
lxc.net.0.hwaddr = 00:16:3e:83:ab:cd
lxc.net.0.ipv4.address = 192.168.0.199/24
lxc.net.0.ipv4.gateway = 192.168.0.139
lxc.net.0.ipv6.address = 2a01:ab:cd:ef::abcd:ff/112
lxc.net.0.ipv6.gateway = 2a01:ab:cd:ef::abcd:1

# Profil Apparmor
# Pour Debian Bookworm, on est souvent obligés de passer en 'with-nesting'
# ou par exemple de reconfigurer l'unit systemd d'Apache pour qu'il n'ait
# pas de privatetmp & similaires.
#  /etc/systemd/system/apache2.service.d/override.conf
#   [Service]
#   PrivateTmp=false
lxc.apparmor.profile = lxc-container-default-with-nesting
#lxc.apparmor.profile = lxc-container-default-cgns
#lxc.apparmor.profile = unconfined
#lxc.apparmor.profile = generated
#lxc.apparmor.allow_nesting = 1
# Uncomment the following line to support nesting containers:
#lxc.include = /usr/share/lxc/config/nesting.conf
# (Be aware this has security implications)

lxc.cgroup2.memory.high = 1900M
lxc.cgroup2.memory.max = 2G

Adaptation à l'intérieur du conteneur

On lance le conteneur et on s'y projette :

lxc-unpriv-start -n debian-bullseye-lxccreate
lxc-unpriv-attach -n debian-bullseye-lxccreate

su - 

cd /etc

# Préférence personnelle sur la verbosité de debconf
dpkg-reconfigure debconf

# Versionnage de /etc (etckeeper est plus lourd mais très bien aussi)
apt install git --no-install-recommends
cat <<EOF >.gitignore
/ld.so.cache
/adjtime
/nologin
/.pwd.lock
*-
*~
*.old
*-old
EOF

git init
chmod 0700 .git
git add .
git commit -a -m "commit initial"

# Mon éditeur et mon paginateur favoris
apt install vim less
update-alternatives --config editor

dpkg-reconfigure tzdata
apt install nullmailer  # "" / mail.example.net / postmaster@example.net
rm mailname && ln -s hostname mailname  # pas très orthodoxe :-/

apt install nftables netcat-openbsd

cd /usr/local/share
git clone https://code.bugness.org/chl/scripts-admin-quickndirty-public.git
cd /etc
cat <<EOF >/etc/apt/apt.conf.d/44proxy
Acquire::http::Proxy-Auto-Detect "/usr/local/share/scripts-admin/apt-conf-proxy.sh";
EOF
# Vérifier que /etc/apt/sources.list soit a peu près ok :
# deb http://ftp.fr.debian.org/debian bookworm main
# deb http://ftp.fr.debian.org/debian bookworm-updates main
# deb http://security.debian.org bookworm/updates main

# Pour regagner un peu d'espace disque
apt clean
sed -i 's/^#SystemMaxUse=.*/SystemMaxUse=100M/' /etc/systemd/journald.conf

# Pour accélérer le boot, commenter l'entrée 'eth0 dhcp...' dans /etc/network/interfaces
# L'interface virtuelle est créée et gérée par LXC de toute façon.

Reconfiguration automatique

cat <<EOF >/etc/systemd/system/reset-ssh-server-keys.service
[Unit]
Description=Regenerate OpenSSH server keys
#ConditionPathExists=!/etc/ssh/ssh_host_rsa_key
# If we do it before network.target or ssh.service, the restart of openssh initiated by dpkg-reconfigure hangs and makes us timeout.
After=network.target

[Service]
Type=oneshot
ExecStart=/bin/bash -c "(/bin/date; echo $(( $RANDOM * $RANDOM * $RANDOM * $RANDOM )) )| /usr/bin/md5sum | /usr/bin/cut -f 1 -d ' ' >/etc/machine-id"
ExecStart=/bin/sh -c "/bin/rm -vf /etc/ssh/ssh_host_*"
ExecStart=/usr/sbin/dpkg-reconfigure openssh-server
ExecStartPost=-/bin/bash -c "/bin/systemctl disable reset-ssh-server-keys"
ExecStartPost=/bin/rm -f /etc/systemd/system/reset-ssh-server-keys.service
TimeoutSec=120
RemainAfterExit=no

[Install]
WantedBy=multi-user.target
EOF
systemctl enable reset-ssh-server-keys

Utilisation courante

On peut ensuite profiter du template en tant qu'utilisateur simple via :

lxc-copy -n debian-12-bookworm-systemd-lxccreate -N project1234-dev
# The MAC address will be changed by LXC but remember to affect a new IP
# and possibly new subuid (but that topic will be for another day).
#
# Also, the journalctl seems empty on first boot. Because of the changed hostname ?