Aller au contenu
Retour aux projets

Univ2025

Application de Sauvegarde

Système de sauvegarde automatique client-serveur avec gestion de versions, chiffrement AES-256-GCM et interface web Flask

Durée
3 mois
Équipe
Projet universitaire, équipe de 4 étudiants
Mon rôle
Développeur principal : architecture système, gestion des versions, chiffrement, interface web
TechnologiesPythonSSH/ParamikoSCPFlaskSQLiteCryptography (AES-256-GCM)GzipWatchdog
Application de Sauvegarde

Contexte

Projet de BUT Informatique visant à créer un système de sauvegarde robuste pour protéger les données contre les ransomwares et les erreurs utilisateur, avec une rétention de 30 jours.

Objectifs

Développer un système de sauvegarde automatique, sécurisé via SSH, avec gestion intelligente des versions, compression gzip (70 % de réduction), déduplication par hash SHA-256 et chiffrement AES-256-GCM.

Approche technique

Architecture client-serveur avec un daemon de surveillance (watchdog) sur le client, transfert SSH/SCP sécurisé, traitement côté serveur (compression + chiffrement + déduplication), stockage des métadonnées dans SQLite et interface web Flask pour le monitoring et la restauration.

Architecture

Client : daemon watchdog, SSH/SCP via Paramiko. Serveur : process_file.py pour le traitement, version_manager.py pour la gestion des versions et la déduplication, encryption.py pour AES-256-GCM, app.py (Flask) pour l'interface web (dashboard, restauration, API REST), SQLite pour les métadonnées, service systemd pour l'automatisation.

Compétences développées

Python avancé & Architecture système

Architecture client-serveur avec daemon watchdog, transferts SSH/SCP via Paramiko, et automatisation systemd.

Cryptographie & Sécurité

Chiffrement AES-256-GCM avec dérivation PBKDF2, compression gzip (70% réduction), et déduplication par hash SHA256.

Flask & API REST

Interface web Flask avec dashboard temps réel, API REST pour consultation et restauration de versions, authentification intégrée.

Base de données SQLite

Stockage des métadonnées de versions avec timestamps, gestion de la rétention 30 jours, et requêtes optimisées.

Extraits de code

Gestion de versions avec compression et chiffrement
python
1# daemon-sauvegarde - Version Manager avec deduplication
2class VersionManager:
3    def save_version(self, file_path, relative_path, action='modified'):
4        # 1. Calcul hash SHA256 du fichier
5        file_hash = self._calculate_hash(file_path)
6        file_size = os.path.getsize(file_path)
7
8        # 2. Verification changement (compare avec version actuelle)
9        current_hash = self._get_current_hash(relative_path)
10        if current_hash == file_hash:
11            return  # Fichier inchange, pas de nouvelle version
12
13        # 3. Deduplication - verifier si hash existe deja
14        dedup_info = self._check_deduplication(file_hash)
15
16        if dedup_info:
17            # Reutiliser fichier existant (incrementer ref_count)
18            self._increment_ref_count(file_hash)
19            compressed_size = dedup_info['compressed_size']
20            dedup_ref = dedup_info['dedup_path']
21        else:
22            # 4. Compression gzip (niveau 6, ~70% reduction)
23            temp_compressed = f"{file_path}.gz"
24            with open(file_path, 'rb') as f_in:
25                with gzip.open(temp_compressed, 'wb', compresslevel=6) as f_out:
26                    shutil.copyfileobj(f_in, f_out)
27
28            # 5. Chiffrement AES-256-GCM
29            encrypted_path = f"{temp_compressed}.enc"
30            self.encryption_manager.encrypt_file(temp_compressed, encrypted_path)
31
32            # 6. Stockage deduplication (hash-based path)
33            dedup_ref = self._store_deduplicated(file_hash, encrypted_path)
34            compressed_size = os.path.getsize(encrypted_path)
35
36        # 7. Enregistrement version dans SQLite
37        timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S-%f')
38        self.cursor.execute("""
39            INSERT INTO file_versions
40            (file_path, version_timestamp, file_size, compressed_size,
41             file_hash, dedup_ref, is_compressed, is_encrypted, action)
42            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
43        """, (relative_path, timestamp, file_size, compressed_size,
44              file_hash, dedup_ref, True, True, action))
45
46        self.conn.commit()

Ce code implémente la gestion de versions avec déduplication intelligente. Chaque fichier est haché (SHA-256), comparé à la version actuelle, puis compressé (gzip, 70 %), chiffré (AES-256-GCM) et stocké de manière dédupliquée. Si le même hash existe déjà, le fichier est réutilisé (économie d'espace). Les métadonnées sont enregistrées dans SQLite avec un horodatage pour la rétention de 30 jours.

API Flask pour restauration de versions
python
1# daemon-sauvegarde - Flask Web Interface API
2@app.route('/api/files/<path:file_path>/versions', methods=['GET'])
3@auth.login_required
4def get_file_versions(file_path):
5    """Recupere toutes les versions d'un fichier"""
6    versions = vm.cursor.execute("""
7        SELECT version_timestamp, file_size, compressed_size,
8               file_hash, action, created_at
9        FROM file_versions
10        WHERE file_path = ?
11        ORDER BY version_timestamp DESC
12    """, (file_path,)).fetchall()
13
14    return jsonify({
15        'file_path': file_path,
16        'versions': [{
17            'timestamp': v[0],
18            'size': v[1],
19            'compressed_size': v[2],
20            'hash': v[3],
21            'action': v[4],
22            'created_at': v[5]
23        } for v in versions]
24    })
25
26@app.route('/api/restore', methods=['POST'])
27@auth.login_required
28def restore_file():
29    """Restaure une version specifique d'un fichier"""
30    data = request.json
31    file_path = data.get('file_path')
32    timestamp = data.get('timestamp')
33
34    # 1. Recuperer infos version depuis SQLite
35    version = vm.cursor.execute("""
36        SELECT dedup_ref, is_compressed, is_encrypted
37        FROM file_versions
38        WHERE file_path = ? AND version_timestamp = ?
39    """, (file_path, timestamp)).fetchone()
40
41    if not version:
42        return jsonify({'error': 'Version not found'}), 404
43
44    dedup_ref, is_compressed, is_encrypted = version
45
46    # 2. Charger fichier depuis dedup_store
47    stored_file = os.path.join(vm.backup_path, dedup_ref)
48
49    # 3. Dechiffrement AES-256-GCM
50    decrypted_file = f"{stored_file}.dec"
51    vm.encryption_manager.decrypt_file(stored_file, decrypted_file)
52
53    # 4. Decompression gzip
54    restored_file = os.path.join('./restored', file_path)
55    os.makedirs(os.path.dirname(restored_file), exist_ok=True)
56    with gzip.open(decrypted_file, 'rb') as f_in:
57        with open(restored_file, 'wb') as f_out:
58            shutil.copyfileobj(f_in, f_out)
59
60    # 5. Nettoyage temporaires
61    os.remove(decrypted_file)
62
63    return jsonify({
64        'success': True,
65        'restored_path': restored_file,
66        'download_url': f'/api/download/{file_path}'
67    })

Cette API Flask expose des endpoints REST pour consulter l'historique des versions et restaurer des fichiers. GET /api/files/<path>/versions retourne toutes les versions avec leurs métadonnées (horodatage, tailles, hash). POST /api/restore gère la restauration complète : récupération depuis dedup_store, déchiffrement AES-256-GCM, décompression gzip et écriture du fichier restauré. L'interface web utilise ces endpoints pour permettre une restauration à un instant donné.

Résultats & bilan

Système prêt pour la production avec 80 à 90 % de réduction d'espace disque (compression gzip 70 % + déduplication), interface web fonctionnelle avec dashboard en temps réel, API REST complète, tests automatiques d'intégrité et de restauration, rétention de 30 jours.

Réflexion & apprentissages

Projet enrichissant qui m'a permis d'approfondir mes compétences en architecture système, sécurité (SSH, chiffrement), optimisation (compression + déduplication) et développement web. La gestion de versions avec rétention m'a particulièrement formé aux problématiques de stockage et de récupération de données.

Liens

Projet suivantAutoInit et customisation de Streamline for Invoices chez ITESOFT