Skip to content

Guide d'installation en production — APT Repo Manager

Ce guide couvre l'installation complète d'APT Repo Manager dans un environnement de production sur un serveur Linux (Debian/Ubuntu). Suivez chaque étape dans l'ordre.


Table des matières

  1. Prérequis système
  2. Récupération du projet
  3. Configuration
  4. Premier démarrage
  5. Configuration GPG
  6. Changement du mot de passe admin
  7. Vérification du bon fonctionnement
  8. Configuration du pare-feu
  9. Sauvegarde automatique
  10. Mise à jour de l'application
  11. Résolution des problèmes courants

1. Prérequis système

Système d'exploitation

  • Debian 11/12 ou Ubuntu 22.04/24.04 LTS (recommandé)
  • Accès root ou sudo

Logiciels requis

Logiciel Version minimale Vérification
Docker Engine 24.0 docker --version
Docker Compose 2.20 (plugin) docker compose version
Git 2.x git --version
OpenSSL 1.1+ openssl version

Important : Utilisez le plugin Docker Compose v2 (docker compose) et non l'ancienne commande docker-compose.

Installation de Docker (si absent)

# Supprimer d'éventuelles anciennes versions
sudo apt-get remove -y docker docker-engine docker.io containerd runc

# Installer les dépendances
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release

# Ajouter le dépôt officiel Docker
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Ajouter l'utilisateur courant au groupe docker
sudo usermod -aG docker $USER
newgrp docker

Ressources matérielles minimales

Ressource Minimum Recommandé
CPU 2 vCPU 4 vCPU
RAM 2 Go 4 Go
Disque 20 Go 100 Go+
Réseau 100 Mb/s 1 Gb/s

Le disque doit être suffisant pour stocker les paquets .deb dans repos/pool/. Prévoyez de l'espace selon le volume de paquets attendu.

Ports réseau requis

Port Service Exposition
80 Dépôt APT Publique (clients)
3003 Interface web Publique ou VPN
8000 API backend Reverse proxy TLS

2. Récupération du projet

Cloner le dépôt

# Répertoire d'installation recommandé
sudo mkdir -p /opt/repod
sudo chown $USER:$USER /opt/repod

cd /opt/repod
git clone https://github.com/votre-organisation/repod.git .

Si vous déployez depuis une archive plutôt que depuis Git :

tar -xzf repod-v2.0.0.tar.gz -C /opt/repod --strip-components=1

Vérifier la structure du projet

ls /opt/repod
# Attendu : backend/ frontend/ repos/ docker-compose.yaml .env.example backend.env.example backup.sh

3. Configuration

3.1 Fichier .env (variables frontend et ports)

cd /opt/repod
cp .env.example .env

Éditez .env avec l'URL publique réelle de votre serveur :

nano .env

Contenu type — remplacez repo.example.com par votre domaine ou adresse IP :

PUBLIC_URL=https://repo.example.com:3003
REACT_APP_API_URL=https://repo.example.com:8000
REACT_APP_REPO_URL=https://repo.example.com:80
FRONTEND_PORT=3003
APP_VERSION=v2.0.0

PUBLIC_URL et REACT_APP_API_URL doivent correspondre aux URLs réellement accessibles par les navigateurs des utilisateurs.

3.2 Fichier backend.env (secrets et authentification)

cp backend.env.example backend.env

Générer la clé secrète JWT

openssl rand -hex 32

Copiez la valeur produite, elle sera utilisée comme JWT_SECRET_KEY.

Générer le hash bcrypt du mot de passe admin

docker run --rm python:3.10-slim python3 -c \
  "from passlib.context import CryptContext; \
   print(CryptContext(schemes=['bcrypt']).hash('VotreMotDePasse!'))"

Remplacez VotreMotDePasse! par un mot de passe respectant la politique : - Minimum 8 caractères - Au moins une majuscule - Au moins un chiffre ou caractère spécial

Attention : Dans backend.env, si le hash bcrypt contient le caractère $, chaque $ doit être doublé en $$. Exemple :

$2b$12$abc...  →  $$2b$$12$$abc...

Contenu type de backend.env

nano backend.env
JWT_SECRET_KEY=<valeur générée par openssl rand -hex 32>
JWT_EXPIRE_MINUTES=60
ADMIN_USERNAME=admin
ADMIN_PASSWORD_HASH=$$2b$$12$$<reste du hash bcrypt>
CORS_ORIGINS=https://repo.example.com:3003
AUTH_RATELIMIT_PER_MINUTE=10

JWT_SECRET_KEY est obligatoire en production. L'application refuse de démarrer si la valeur par défaut est détectée.

3.3 Sécuriser les fichiers de configuration

chmod 600 /opt/repod/.env
chmod 600 /opt/repod/backend.env

3.4 Créer la structure des volumes

mkdir -p /opt/repod/repos/{audit,auth,clamav-db,gnupg,grype-db,imports,logs,manifests,package-index,pool,security,staging/incoming,staging/quarantine}

Description des répertoires :

Répertoire Contenu
audit/ Journaux d'audit JSONL (append-only)
auth/ Base SQLite des utilisateurs + tokens de reset
clamav-db/ Définitions antivirus ClamAV
gnupg/ Trousseau GPG (partagé entre services)
grype-db/ Cache base de données CVE Grype
imports/ Cache temporaire des imports
logs/ Journaux de téléchargements Nginx
manifests/ Manifestes JSON des paquets
package-index/ Index APT SQLite
pool/ Fichiers .deb
security/ Décisions CVE et tokens API JSON
staging/ Zone de staging (upload et quarantaine)

4. Premier démarrage

4.1 Construire et démarrer les services

cd /opt/repod
docker compose -f docker-compose.yaml up -d

Docker va télécharger et construire les images puis démarrer trois conteneurs :

Conteneur Rôle Port
frontend-ui Interface web React 3003
backend-api API FastAPI + logique métier 8000
depot-apt Serveur APT Nginx 80

4.2 Vérifier que les conteneurs sont en cours d'exécution

docker compose ps

Tous les services doivent afficher l'état Up ou running.

4.3 Surveiller les logs au démarrage

# Logs de l'API backend
docker logs backend-api -f

# Logs du frontend
docker logs frontend-ui -f

# Logs du serveur APT
docker logs depot-apt -f

Attendez que le backend affiche quelque chose de similaire à :

INFO:     Application startup complete.

4.4 Vérifier le health check de l'API

curl http://localhost:8000/health/live

Réponse attendue :

{"status": "ok"}


5. Configuration GPG

Le dépôt APT doit être signé avec une clé GPG pour que les clients apt acceptent les paquets.

Option A : Via l'interface web (recommandé)

  1. Ouvrez https://repo.example.com:3003 dans un navigateur
  2. Connectez-vous avec les identifiants admin configurés
  3. Allez dans Paramètres > GPG
  4. Cliquez sur Générer une clé GPG
  5. Renseignez le nom réel et l'adresse e-mail
  6. Cliquez sur Générer

Option B : Via la ligne de commande

docker exec depot-apt gpg --homedir /repos/gnupg --no-default-keyring \
  --keyring /repos/gnupg/pubring.kbx --batch --gen-key <<EOF
%no-protection
Key-Type: RSA
Key-Length: 4096
Name-Real: repod APT Repository
Name-Email: repod@example.com
Expire-Date: 2y
%commit
EOF

Adaptez Name-Real et Name-Email à votre organisation.

Exporter la clé publique GPG

La clé publique doit être distribuée aux clients pour qu'ils puissent vérifier les signatures :

# Exporter la clé publique depuis le conteneur
docker exec depot-apt gpg --homedir /repos/gnupg \
  --armor --export repod@example.com > /opt/repod/repos/pubkey.asc

# Vérifier l'export
cat /opt/repod/repos/pubkey.asc

Configuration côté client APT

Les machines clientes doivent importer la clé et configurer le dépôt :

# Sur chaque machine cliente
curl -fsSL https://repo.example.com/pubkey.asc | sudo gpg --dearmor \
  -o /etc/apt/keyrings/repod.gpg

echo "deb [signed-by=/etc/apt/keyrings/repod.gpg] \
  http://repo.example.com/ stable main" \
  | sudo tee /etc/apt/sources.list.d/repod.list

sudo apt-get update

6. Changement du mot de passe admin

Il est fortement recommandé de changer le mot de passe admin dès la première connexion via l'interface web.

Via l'interface web

  1. Connectez-vous avec le mot de passe configuré dans backend.env
  2. Allez dans Profil > Changer le mot de passe
  3. Saisissez l'ancien mot de passe puis le nouveau (et sa confirmation)
  4. Cliquez sur Enregistrer

Via l'API

# Récupérer un token JWT
TOKEN=$(curl -s -X POST http://localhost:8000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"AncienMotDePasse!"}' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# Changer le mot de passe
curl -s -X POST http://localhost:8000/auth/change-password \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"old_password":"AncienMotDePasse!","new_password":"NouveauMotDePasse!"}'

7. Vérification du bon fonctionnement

Utilisez cette liste de contrôle après l'installation initiale.

Checklist de vérification

# 1. Tous les conteneurs sont en cours d'exécution
docker compose ps

# 2. Health check de l'API
curl -s http://localhost:8000/health/live | python3 -m json.tool

# 3. Interface web accessible
curl -s -o /dev/null -w "%{http_code}" http://localhost:3003
# Attendu : 200

# 4. Serveur APT accessible
curl -s -o /dev/null -w "%{http_code}" http://localhost:80
# Attendu : 200 ou 301

# 5. Swagger UI désactivé en production (doit retourner 404)
curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/docs
# Attendu : 404

# 6. Connexion admin fonctionnelle
curl -s -X POST http://localhost:8000/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"VotreMotDePasse!"}'
# Attendu : réponse JSON avec access_token

# 7. ClamAV opérationnel
docker exec backend-api curl -s http://localhost:3310
# Attendu : réponse PONG ou signature ClamAV

# 8. GPG configuré
docker exec depot-apt gpg --homedir /repos/gnupg --list-keys
# Attendu : au moins une clé listée

Vérification de la signature du dépôt APT

# Vérifier que le Release est signé
curl -s http://localhost/dists/stable/InRelease | head -5
# Attendu : bloc PGP SIGNED MESSAGE

Vérification des logs d'audit

# Les actions admin doivent être tracées
ls -la /opt/repod/repos/audit/
cat /opt/repod/repos/audit/$(date +%Y-%m-%d).jsonl | head -20

8. Configuration du pare-feu

Avec UFW (recommandé sur Ubuntu)

# Activer UFW si ce n'est pas déjà fait
sudo ufw enable

# Autoriser SSH (ne pas s'oublier !)
sudo ufw allow 22/tcp

# Interface web APT Repo Manager
sudo ufw allow 3003/tcp

# Dépôt APT (clients apt)
sudo ufw allow 80/tcp

# Port 8000 (backend API) : NE PAS exposer directement
# Utilisez un reverse proxy TLS — voir docs/REVERSE_PROXY.md

# Vérifier les règles
sudo ufw status numbered

Note de sécurité : Le port 8000 (API backend) ne doit pas être exposé directement sur Internet. Placez-le derrière un reverse proxy (Nginx, Caddy, Traefik) avec TLS. Consultez docs/REVERSE_PROXY.md pour la configuration TLS.

Avec iptables

# Autoriser le trafic entrant sur le port 3003 (UI)
sudo iptables -A INPUT -p tcp --dport 3003 -j ACCEPT

# Autoriser le trafic entrant sur le port 80 (APT)
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

# Sauvegarder les règles
sudo apt-get install -y iptables-persistent
sudo netfilter-persistent save

9. Sauvegarde automatique

Sauvegarde manuelle

cd /opt/repod
./backup.sh

Le script sauvegarde le répertoire repos/ et les fichiers de configuration.

Configurer la rétention

# Conserver les sauvegardes 30 jours
BACKUP_RETENTION_DAYS=30 ./backup.sh

Planification automatique avec cron

# Éditer la crontab de root
sudo crontab -e

Ajoutez la ligne suivante pour une sauvegarde quotidienne à 3h du matin :

0 3 * * * cd /opt/repod && ./backup.sh >> /var/log/repod-backup.log 2>&1

Vérifier les sauvegardes

# Lister les sauvegardes existantes
ls -lh /opt/repod/backups/

# Vérifier le journal des sauvegardes
tail -50 /var/log/repod-backup.log

Sauvegarde des secrets

Les fichiers suivants doivent être sauvegardés séparément dans un coffre-fort sécurisé :

# Fichiers critiques à sauvegarder hors du serveur
/opt/repod/.env
/opt/repod/backend.env
/opt/repod/repos/gnupg/     # Clé GPG privée
/opt/repod/repos/auth/      # Base des utilisateurs

Attention : La perte de la clé GPG privée (repos/gnupg/) rend impossibles les futures signatures du dépôt APT. Sauvegardez-la de manière sécurisée.


10. Mise à jour de l'application

Procédure de mise à jour standard

cd /opt/repod

# 1. Sauvegarder avant la mise à jour
./backup.sh

# 2. Récupérer les nouvelles versions
git fetch origin
git pull origin main

# 3. Arrêter les services
docker compose -f docker-compose.yaml down

# 4. Reconstruire les images avec la nouvelle version
docker compose -f docker-compose.yaml build --no-cache

# 5. Redémarrer les services
docker compose -f docker-compose.yaml up -d

# 6. Vérifier le bon démarrage
docker compose ps
curl http://localhost:8000/health/live

Mise à jour de l'image Docker uniquement (sans rebuild)

cd /opt/repod

./backup.sh

docker compose -f docker-compose.yaml pull
docker compose -f docker-compose.yaml up -d

Vérifier la version déployée

curl http://localhost:8000/health/live
# ou via l'interface : Paramètres > À propos

Rollback en cas de problème

cd /opt/repod

# Revenir à la version précédente dans git
git log --oneline -10
git checkout <commit-précédent>

# Redémarrer avec la version précédente
docker compose -f docker-compose.yaml down
docker compose -f docker-compose.yaml up -d --build

11. Résolution des problèmes courants

Le conteneur backend-api ne démarre pas

Symptôme : docker compose ps affiche Exited pour backend-api.

# Voir les logs d'erreur
docker logs backend-api --tail 50

Causes fréquentes :

  • JWT_SECRET_KEY non défini ou utilisant la valeur par défaut dans backend.env
  • Solution : générez une clé avec openssl rand -hex 32 et relancez
  • $ non doublé en $$ dans le hash bcrypt de backend.env
  • Solution : éditez backend.env et doublez tous les $ du hash
  • Erreur de syntaxe dans backend.env
  • Solution : vérifiez qu'il n'y a pas d'espaces autour des =

L'interface web affiche une erreur de connexion à l'API

Symptôme : Le frontend charge mais affiche une erreur lors de la connexion.

# Vérifier que le backend répond
curl http://localhost:8000/health/live

# Vérifier les CORS
docker logs backend-api --tail 20 | grep -i cors

Cause fréquente : CORS_ORIGINS dans backend.env ne correspond pas exactement à PUBLIC_URL dans .env (protocole, port, domaine).

Les clients APT rejettent le dépôt (erreur de signature)

Symptôme : apt update échoue avec The following signatures couldn't be verified.

# Vérifier que la clé GPG est configurée
docker exec depot-apt gpg --homedir /repos/gnupg --list-keys

# Vérifier que le Release est signé
curl http://localhost/dists/stable/InRelease | head -3

Solution : Si aucune clé n'est listée, générez une clé GPG (voir section 5) puis re-publiez le dépôt via l'interface web.

ClamAV ne se met pas à jour

Symptôme : Avertissements dans les logs concernant les définitions ClamAV obsolètes.

# Mettre à jour manuellement les définitions
docker exec backend-api freshclam

# Vérifier le volume clamav-db
ls -la /opt/repod/repos/clamav-db/

L'espace disque est insuffisant

# Vérifier l'espace disponible
df -h /opt/repod/

# Lister les plus gros paquets
du -sh /opt/repod/repos/pool/* | sort -rh | head -20

# Nettoyer les paquets en quarantaine
ls /opt/repod/repos/staging/quarantine/
# rm -f /opt/repod/repos/staging/quarantine/*  # à faire manuellement après vérification

Accès refusé sur le port 3003 ou 80

# Vérifier les règles de pare-feu
sudo ufw status

# Vérifier que Docker écoute bien sur les ports
ss -tlnp | grep -E '3003|8000|:80'

Réinitialiser le mot de passe admin

Si le mot de passe admin est perdu :

# Générer un nouveau hash bcrypt
docker run --rm python:3.10-slim python3 -c \
  "from passlib.context import CryptContext; \
   print(CryptContext(schemes=['bcrypt']).hash('NouveauMotDePasse!'))"

# Mettre à jour backend.env avec le nouveau hash (doubler les $)
nano /opt/repod/backend.env

# Redémarrer le backend pour prendre en compte
docker compose restart backend-api

Consulter tous les logs en temps réel

# Tous les services en même temps
docker compose logs -f

# Un seul service
docker logs backend-api -f
docker logs frontend-ui -f
docker logs depot-apt -f

Annexe : Commandes de référence rapide

# Démarrer en production
docker compose -f docker-compose.yaml up -d

# Arrêter tous les services
docker compose -f docker-compose.yaml down

# Redémarrer un service spécifique
docker compose restart backend-api

# Voir l'état des services
docker compose ps

# Health check
curl http://localhost:8000/health/live

# Sauvegarder
cd /opt/repod && ./backup.sh

# Voir les logs
docker logs backend-api -f
docker logs frontend-ui -f
docker logs depot-apt -f

# Accéder au shell d'un conteneur
docker exec -it backend-api bash
docker exec -it depot-apt bash

Guide valable pour APT Repo Manager v2.0.0 — Dernière mise à jour : mai 2026