OpenClaw - Partie 02 : Préparation de l'OS et de la conteneurisation

Introduction

Après la présentation de l'objectif de mettre en place une configuration d'un agent IA OpenClaw dans un environnement sécurisé, il est temps de démarrer la mise en œuvre.

Cette première partie va permettre de mettre en place les différents prérequis nécessaires.

Comme cela a été expliqué précédemment, l'idée est d'affecter une VM sous Rocky Linux pour exécuter plusieurs agents dans des environnements conteneurisés.

Dédier une VM est une première couche de sécurisation, certes basique, mais qui permet déjà de définir un premier périmètre en lieu et place de l'exécution des agents sur un serveur ou un poste de travail utilisé pour d'autres tâches.

Création de la VM

Je ne vais pas détailler l'installation de Rocky Linux, dans mon cas, j'ai choisi un déploiement standard en V10.

J'ai simplement créé une VM sur mon pool XCP-ng avec la configuration matérielle suivante :

  • 4 vCPU
  • 6 Go de RAM
  • 80 Go de disque
Caractéristique de la VM sous XCP-ng

Cliquez sur l'image pour l'agrandir.

Côté réseau, la VM est sur mon LAN classique, on aurait pu imaginer dédier une zone réseau spécifique, mais on se contentera de firewalld sur le serveur lui-même pour avoir un minimum de contrôle sur ce qui transite vers et depuis la VM.

À noter que la VM n'est toutefois pas exposée à l'extérieur, à l'inverse des nombreux VPS qui n'ont pas tardé à être identifié sous shodan…

Instance OpenClaw vu sur Shodan

Cliquez sur l'image pour l'agrandir.

Rien de spécial à signaler concernant l'installation de l'OS : l'assistant est simple à suivre et se conforme aux standards des installateurs RedHat.

Installation de rocky

Cliquez sur l'image pour l'agrandir.

Après quelques minutes, on dispose d'une VM prdlinage501 avec un compte utilisateur standard, mais disposant des accès sudo pour les commandes qui l'exigent.

Prérequis

Mise à jour du système

Toujours après une installation, on démarre par une mise à jour du système :

sudo dnf update -y

et le petit reboot qui suit pour être certains de partir sur de bonnes bases :

sudo reboot
update de l'os

Cliquez sur l'image pour l'agrandir.

Reboot de l'OS

Cliquez sur l'image pour l'agrandir.

Podman

On démarre par l'installation des paquets supplémentaires. Comme on souhaite exploiter les conteneurs pour isoler les agents, on commence par :

sudo dnf install -y container-tools
Installation de container tools

Cliquez sur l'image pour l'agrandir.

Cela va permettre de mettre en œuvre Podman. Podman est depuis un certain temps maintenant le choix par défaut de conteneurisation des OS RedHat. Rocky Linux n'échappe donc pas à la règle. Habituellement j'ai tendance à revenir à Docker, parce qu'historiquement plus habitué à la solution.

Mais dans le projet, maintenir Podman est plutôt un gage de sécurité supplémentaire, car par rapport à docker, podman est rootless par défaut, ce qui implique qu'il n'est pas nécessaire d'avoir les privilèges administrateurs sur la machine pour manipuler les conteneurs. Parfois cela rend les choses plus complexes, mais on va essayer de rester dans ce mode (Docker peut également maintenant être rootless mais ce n'est pas son mode par défaut).

À noter que d'autres différences existent entre podman et docker mais que tout le deux utilisent historiquement runc comme runtime, mais dans la v10 de rocky podman est passé à crun, une réécriture de runc (historiquement en go) en C plus léger et plus rapide.

(N'hésitez pas à parcourir cet article pour bien comprendre les différentes couches mises en œuvre pour la conteneurisation.)

Toujours dans un objectif de sécurité, il n'est pas prévu de désactiver SElinux, par contre, on va devoir adapter certaines règles pour faire ce qu'on l'on souhaite, on va donc installer le paquet :

sudo dnf install -y policycoreutils-python-utils
Installation de policycoreutils

Cliquez sur l'image pour l'agrandir.

Enfin on ajoute quelques tools bien utiles pour la suite :

sudo dnf install -y curl jq tree

L'objectif est d'arriver à ce résultat en vérifiant les différentes versions des outils :

podman --version             
buildah --version             
slirp4netns --version
Controle des versions

Cliquez sur l'image pour l'agrandir.

Podman on en a déjà parlé. buildah va nous permettre de construire nos images et slirp4netns va autoriser l'usage du réseau pour nos conteneurs rootless car il s'occupe de simuler une pile TCP/IP en mode utilisateur. (Un autre outil plus performant, Pasta, est normalement disponible pour cela, mais j'ai rencontré quelques difficultés avec celui-ci, durant le projet.)

À noter que vos versions peuvent varier en fonction de quand vous lirez cet article.

On va pouvoir enchainer avec la création du compte dédié au management des agents.

Création du compte

L'idée est effectivement de ne pas utiliser le compte créé lors de l'installation de Rocky Linux et qui dispose des droits sudo adéquate pour les commandes à privilège.

Définition du home et des droits

Nous allons utiliser un compte spécifique openclaw (ce n'est pas très original, mais au moins nous savons à quoi il sert).

Tous les conteneurs seront manipulés et lancés avec cet utilisateur.

Son répertoire personnel ne se trouvera pas à son emplacement habituel, puisqu'il s'agit d'un compte un peu particulier pour lequel on veut avoir une arborescence spécifique pour nos usages. Pour cette raison, on va donc exploiter le chemin /var/lib/openclaw.

Voici les commandes de création du compte :

sudo useradd --system --create-home \
             --home-dir /var/lib/openclaw \
             --shell /bin/bash \
             --comment "OpenClaw containerized agents" \
             openclaw
Création du compte openclaw

Cliquez sur l'image pour l'agrandir.

Dans la foulée on verrouille l'usage de mot de passe pour interdire toute connexion directe avec ce compte :

sudo passwd -l openclaw
Verouillage du mot de passe

Cliquez sur l'image pour l'agrandir.

On va appliquer une autre sécurité, à savoir forcer le home en 700. Cela va garantir que seul le propriétaire donc openclaw va pouvoir agir dans son home (soit /var/lib/openclaw).

sudo chmod 700 /var/lib/openclaw
Caractéristique de la VM sous XCP-ng

Cliquez sur l'image pour l'agrandir.

En effet, des secrets pourraient être disponibles dans cette arborescence (utilisée par les agents). Il est donc très important qu'elle soit la plus verrouillée possible.

Quand nous serons sous l'identité de l'utilisateur openclaw, même sans droit root, il faudra néanmoins avoir quelques éléments de suivi, comme l'accès aux journaux systemd.

On donne donc les droits adéquate au user :

sudo usermod -aG systemd-journal openclaw
Droit pour l'accès aux journaux

Cliquez sur l'image pour l'agrandir.

Linger

Ensuite, nous allons devoir traiter une autre problématique, celle de laisser actifs les process lancés par l'utilisateur openclaw une fois que nous serons sortie de son contexte.

En effet, avec systemd (en charge de la centralisation et du cycle de vie de tous les services et ressources du système), par défaut, lorsqu'un utilisateur se déconnecte, les processus associés sont tués.

Or, c'est l'utilisateur openclaw qui va lancer les conteneurs associés aux agents, il faut donc que ces derniers survivent à sa déconnexion.

On va donc activer le lingering pour l'utilisateur openclaw via la commande :

sudo loginctl enable-linger openclaw
linger

Cliquez sur l'image pour l'agrandir.

En gros cette instruction va permettre de donner les capacités au compte openclaw d'héberger des services permanents sans avoir à être root.

On peut vérifier que la fonction est active avec cette commande :

sudo loginctl show-user openclaw | grep Linger

User Namespace Mapping

Toujours en lien avec le côté rootless de podman, il va falloir définir le User Namespace Mapping. Pour simplifier, il va falloir donner à l'utilisateur openclaw le droit de réserver des uid (User Identifier) et des gid (Group Identifier).

En effet, lorsqu'on lance un conteneur, souvent, le processus à l'intérieur croit être l'utilisateur root soit l'uid 0.

Or avec podman, comme on est en rootless, il n'est pas possible d'être associé à l'uid 0 du système. Il faut donc tricher et faire correspondre l'uid 0 du conteneur à un autre uid du système.

Il faut autoriser l'utilisateur openclaw à utiliser des uid pour ses propres besoins, soit les conteneurs qu'il va lancer.

Pour cela, on va passer la commande :

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 openclaw

Ça laisse de la marge pour openclaw qui va pouvoir taper dans ces uid et gid.

On peut vérifier le succès de la configuration avec la commande :

grep openclaw /etc/subuid
grep openclaw /etc/subgid
Attribution des uid et des gid

Cliquez sur l'image pour l'agrandir.

SELinux et sous arborescence

Ensuite on rentre dans une configuration avec laquelle je ne suis jamais à l'aise : celle de SElinux.

En effet on va devoir mapper des volumes dans les conteneurs, volumes qui seront directement repris de l'arborescence du home de notre utilisateur openclaw (/var/lib/openclaw).

Si on ne paramètre pas SElinux, ce type de mappage sera bloqué. On va donc autoriser l'utilisateur à manipuler son home pour le mapping des volumes des conteneurs.

Mais avant, on va déjà créer les sous-répertoires qui vont nous permettre de correctement organiser nos agents et nos configurations :

sudo -u openclaw mkdir -p /var/lib/openclaw/instances
sudo -u openclaw mkdir -p /var/lib/openclaw/vault
sudo -u openclaw mkdir -p /var/lib/openclaw/.config/containers/systemd

Le répertoire /var/lib/openclaw/instances va servir à créer un sous-dossier par agent pour bien séparer ces derniers les uns des autres. Par exemple, on aura au sein de /var/lib/openclaw/instances un sous-dossier it-claw pour le premier agent décrit en introduction de cette série d'articles.

Ensuite /var/lib/openclaw/vault va être pour la configuration de nos agents vault (pas au sens agent IA), soit notre solution retenue pour stocker les secrets utiles aux agents (IA cette fois).

Enfin /var/lib/openclaw/.config/containers/systemd va servir pour le stockage des fichiers de démarrage de nos conteneurs associé à nos agents (toujours IA). Mais on aura le temps d'en parler plus en détail plus tard.

Une fois ces dossiers créés, il faut les déclarer côté SElinux :

# Déclarer les contextes SELinux pour ces arborescences
sudo semanage fcontext -a -t container_file_t "/var/lib/openclaw/instances(/.*)?"
sudo semanage fcontext -a -t container_file_t "/var/lib/openclaw/vault(/.*)?"

# Appliquer immédiatement
sudo restorecon -Rv /var/lib/openclaw/instances
sudo restorecon -Rv /var/lib/openclaw/vault

Les dossiers pourront ainsi être étiquetés comme associés à des usages en conteneurs, ce qui empêchera SElinux de bloquer l'accès malgré les bons droits sur les répertoires.

Création des dossiers et règles SELinux

Cliquez sur l'image pour l'agrandir.

d-bus

Une autre problématique liée à podman et au mode rootless existe : l'usage de d-bus. En temps normal, d-bus sers de messagerie interne entre processus. L'utilisateur openclaw doit utiliser ce bus pour permettre à podman de communiquer avec systemd en matière de gestion des ressources.

Si on bascule dans le contexte de l'utilisateur openclaw avec la commande classique su -, le système ne créera pas de nouvelle session d-bus pour le user openclaw.

Il faut donc pouvoir ouvrir une véritable session avec un démarrage du processus dbus-daemon pour l'utilisateur openclaw.

Pour cela on va faire appel à la commande machinectl, c'est une manière de créer une véritable session neuve et isolée, comme si l'utilisateur openclaw se connectait physiquement ou via ssh au serveur.

Par défaut, l'utilitaire machinectl n'est pas inclus. Il est nécessaire d'installer le paquet systemd-container pour l'obtenir :

sudo dnf install systemd-container
Installation de sytemd-container

Cliquez sur l'image pour l'agrandir.

Contrôles et initialisation

Une fois ce préalable établi, nous pouvons enfin nous positionner dans le cadre de l'utilisateur openclaw de la machine virtuelle: machinectl shell openclaw@

Connexion dans le contexte de l'utilisateur openclaw

Cliquez sur l'image pour l'agrandir.

À partir de là, on doit vérifier d'emblée qu'une session d-bus est en place :

# Variables d'environnement systemd user (doivent être non vides)
echo "XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
echo "DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS"

# Le bus user doit répondre sans erreur
systemctl --user status 2>&1 | head -10
Controle

Cliquez sur l'image pour l'agrandir.

On peut checker que l'accès aux journaux est possible :

journalctl --user -n 1 --no-pager 2>&1 | head -3
Controle

Cliquez sur l'image pour l'agrandir.

On enchaine ensuite avec l'initialisation et les tests autour de l'usage de podman sous l'identité de l'utilisateur openclaw :


podman info > /dev/null

Cette instruction sert à forcer Podman à créer toute son arborescence de fichiers et ses configurations avant que l'on essaye d'exécuter des conteneurs.

Ensuite on peut vérifier que tout est correct :

# Vérifier le mode rootless
podman info --format '{{.Host.Security.Rootless}}'
# Attendu : true

# Vérifier l'emplacement du stockage (doit être dans le home openclaw)
podman info --format '{{.Store.GraphRoot}}'
# Attendu : /var/lib/openclaw/.local/share/containers/storage

# Vérifier le driver de stockage (overlay recommandé)
podman info --format '{{.Store.GraphDriverName}}'
# Attendu : overlay

# Vérifier le driver réseau
podman info --format '{{.Host.NetworkBackend}}'
# Attendu : netavark 
Controle

Cliquez sur l'image pour l'agrandir.

On va tester également la bonne prise en compte des règles SELinux et du support de l'accès dans les dossiers nécessaires depuis un volume monté dans un conteneur. On commence par créer un fichier test dans /var/lib/openclaw/instances, soit dans le contexte de l'utilisateur openclaw, ~/instances


echo "test selinux volume" > ~/instances/test.txt

Puis on part d'une image Alpine (un OS ultra léger), pour monter au sein du conteneur le dossier en question dans /data pour ensuite tester l'accès au fichier depuis le conteneur via la commande cat

podman run --rm -v ~/instances:/data:Z docker.io/library/alpine:latest \
    cat /data/test.txt
Test d'accès au fichier depuis un conteneur

Cliquez sur l'image pour l'agrandir.

Le test ultime va être d'essayer d'exécuter un conteneur qui va tenter un accès à l'API Ollama hébergée sur le PC qui va exécuter le modèle LLM qu'on va souhaiter utiliser.

Pour cela, on repart d'une image Alpine, avec comme argument un simple curl vers le serveur cible ; pour moi mon PC disposant d'un GPU NVIDIA :


podman run --rm docker.io/library/alpine:latest \
    sh -c "apk add --no-cache curl > /dev/null 2>&1 && \
           curl -s http://192.168.10.2:11434/api/tags"
Test d'accès a ollama

Cliquez sur l'image pour l'agrandir.

Si le test réussit, on devrait voir la liste des modèles supportés retournée par Ollama.

Cela démontre que le contexte d'utilisation de podman à travers l'utilisateurs dédié openclaw est fonctionnel.

Pour terminer, il ne reste plus qu'à améliorer l'ergonomie des sessions futures avec l'utilisateur openclaw via la personnalisation du bash (merci à l'IA) :

cat >> ~/.bashrc <<'EOF'

# --- OpenClaw environment ---
# Raccourci pour la session systemd user
export SYSTEMD_PAGER=
# Éviter que systemctl utilise less quand on ne le veut pas

# PATH : binaires npm si jamais on en installe en user (rare en containerisé)
export PATH="$HOME/.local/bin:$PATH"

# Alias utiles
alias oclogs='journalctl --user -f'
alias ocstatus='systemctl --user status'
alias ocps='podman ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'
EOF
Custom du shell

Cliquez sur l'image pour l'agrandir.

Enfin on quitte la session avec exit pour revenir à l'utilisateur courant.

On peut s'assurer que la branche systemd du compte utilisateur openclaw restera active (ce qui permettra de faire tourner des services) grâce à la commande :

sudo systemctl status "user@$(id -u openclaw).service" --no-pager | head -10

Si le retour comporte « active (running) », on est tout bon.

Test final

Cliquez sur l'image pour l'agrandir.

Conclusion

Nous voilà arrivés à la fin de cette partie dédiée à la préparation de l'OS et des composants de conteneurisation nécessaire au projet. La sécurité a toujours un coût, et rester dans un univers rootless exige quelques complexités de paramétrage.

On est à présent sûr de bonnes bases pour la suite. Commencer par soigner son écosystème d'exécution et s'assurer d'avoir un OS à jour, correctement paramétré est une nécessité de premier niveau pour toute mise en production.

Nos futurs agents conteneurisés seront déjà ainsi isolés du système lui-même avec une incapacité d'emprunter les droits roots même en cas de compromission.

La suite va consister à s'attaquer à vault et nginx. Mais c'est prévu pour un prochain article.