Files
innotexBoard/TECHNICAL_EXPLANATION.md
2026-01-16 18:40:39 +01:00

579 lines
15 KiB
Markdown

# 🔧 Explication technique détaillée - InnotexBoard
## Table des matières
1. [Architecture système](#architecture)
2. [Authentification PAM + JWT](#authentification)
3. [Gestion Docker](#docker)
4. [Monitoring système](#monitoring)
5. [Frontend et communication API](#frontend)
6. [Sécurité](#sécurité)
---
## 1. Architecture système {#architecture}
### Diagramme de flux
```
┌─────────────────────────────────────────────────────────────┐
│ Navigateur (Frontend) │
│ http://localhost:3000 │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Vue.js 3 Application │ │
│ │ - Pages: Login, Dashboard, Containers │ │
│ │ - Store Pinia pour l'état │ │
│ │ - Axios pour les requêtes HTTP │ │
│ └────────────────┬────────────────────────────────────┘ │
└─────────────────┬───────────────────────────────────────────┘
│ JWT Bearer Token
│ CORS allowlist
┌─────────────────────────────────────────────────────────────┐
│ Backend FastAPI │
│ http://localhost:8000 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ /api/v1/auth - Authentification PAM + JWT │ │
│ │ /api/v1/system - psutil (CPU/RAM/Processus) │ │
│ │ /api/v1/docker - Docker SDK Python │ │
│ └────┬─────────────────────┬──────────────────┬────────┘ │
└───────┼─────────────────────┼──────────────────┼────────────┘
│ │ │
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌─────────────┐
│ PAM │ │ psutil │ │ Docker │
│ /etc/ │ │ /proc │ │ Socket │
│shadow │ │ /sys │ │ /var/run/ │
└────────┘ └──────────┘ └─────────────┘
```
### Communication API
```
Client → POST /api/v1/auth/login
(username, password)
PAM Authentication
JWT Token Generated
Server → JSON {token, username}
Client → GET /api/v1/system/stats
Authorization: Bearer {token}
Token validation
psutil data gathering
Server → JSON {cpu, memory, processes}
```
---
## 2. Authentification PAM + JWT {#authentification}
### Fichier principal : [backend/app/core/security.py](backend/app/core/security.py)
### Flux d'authentification
```python
# 1. L'utilisateur envoie ses identifiants
POST /auth/login
{
"username": "john",
"password": "secret123"
}
# 2. Backend appelle PAM
pam_auth = pam.pam()
if pam_auth.authenticate(username, password):
# ✅ Identifiants valides
# ✅ L'utilisateur existe sur le système
# 3. Générer un JWT token
token = jwt.encode({
"sub": username,
"exp": datetime.utcnow() + timedelta(hours=8)
}, SECRET_KEY, algorithm="HS256")
# 4. Retourner le token
return {
"access_token": token,
"token_type": "bearer",
"username": username
}
else:
# ❌ Identifiants invalides
raise HTTP 401 Unauthorized
```
### Validation des requêtes protégées
```python
# Chaque endpoint protégé utilise get_current_user
@router.get("/stats")
async def get_system_stats(current_user: User = Depends(get_current_user)):
# 1. Extraire le token du header Authorization
# 2. Décoder et valider le JWT
# 3. Vérifier l'expiration
# 4. Retourner l'utilisateur ou lever 401
return system_stats
```
### Structure du JWT
```
Header:
{
"alg": "HS256",
"typ": "JWT"
}
Payload:
{
"sub": "john", # subject (username)
"exp": 1704067200, # expiration time
"iat": 1704060000 # issued at
}
Signature: HMAC-SHA256(header.payload, SECRET_KEY)
Token final: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
### Avantages PAM vs alternatives
| Aspect | PAM | LDAP | Local DB |
|--------|-----|------|----------|
| Utilise les utilisateurs système | ✅ | ✅ | ❌ |
| Facile à configurer | ✅ | ❌ | ✅ |
| SSO support | Limité | ✅ | ❌ |
| Groupe système | ✅ | ✅ | ❌ |
| Nécessite sudo | ✅ | ❌ | ❌ |
---
## 3. Gestion Docker {#docker}
### Fichier principal : [backend/app/services/docker_service.py](backend/app/services/docker_service.py)
### Initialisation de la connexion
```python
import docker
class DockerService:
def __init__(self):
# Se connecter via le socket Docker
self.client = docker.from_env()
# Equivalent à: docker.DockerClient(base_url='unix:///var/run/docker.sock')
```
### Récupérer les conteneurs
```python
def get_containers(self, all=True):
"""
all=True : obtient tous les conteneurs (running + stopped)
all=False : obtient seulement les conteneurs en cours d'exécution
"""
containers = self.client.containers.list(all=all)
for container in containers:
# Stats en temps réel
stats = container.stats(stream=False)
# Extraction CPU
cpu_delta = stats['cpu_stats']['cpu_usage']['total_usage'] - \
stats['precpu_stats']['cpu_usage']['total_usage']
system_cpu = stats['cpu_stats']['system_cpu_usage'] - \
stats['precpu_stats']['system_cpu_usage']
cpu_percent = (cpu_delta / system_cpu) * 100.0
# Extraction mémoire
memory_mb = stats['memory_stats']['usage'] / (1024*1024)
```
### Opérations sur les conteneurs
```python
# Démarrer
container.start()
# Arrêter (gracieux)
container.stop(timeout=10)
# Redémarrer
container.restart(timeout=10)
# Supprimer
container.remove(force=False) # force=True pour les containers running
```
### Permissions Docker requises
Pour que le serveur web puisse utiliser Docker, il faut soit :
1. **Groupe docker** (moins sécurisé)
```bash
sudo usermod -aG docker www-data
```
2. **Sudo sans mot de passe** (recommandé)
```bash
echo 'www-data ALL=(ALL) NOPASSWD: /usr/bin/docker' | sudo tee /etc/sudoers.d/docker
```
3. **Socket Docker avec permissions** (avancé)
```bash
sudo setfacl -m u:www-data:rw /var/run/docker.sock
```
---
## 4. Monitoring système {#monitoring}
### Fichier principal : [backend/app/services/system.py](backend/app/services/system.py)
### Utilisation CPU
```python
import psutil
# CPU % (moyenne sur 1 seconde)
cpu_percent = psutil.cpu_percent(interval=1)
# Retourne: 45.3
# Nombre de cores
cpu_count = psutil.cpu_count()
# Retourne: 8
```
### Utilisation mémoire
```python
memory = psutil.virtual_memory()
# memory.percent → 65.2%
# memory.used → 4GB (en bytes)
# memory.total → 8GB (en bytes)
# memory.available → 2GB (en bytes)
```
### Processus actifs
```python
for proc in psutil.process_iter(['pid', 'name', 'status', 'username']):
try:
info = proc.info
cpu_pct = proc.cpu_percent(interval=0.1)
mem_pct = proc.memory_percent()
# Résultat:
# {
# "pid": 1234,
# "name": "python3",
# "status": "running",
# "username": "www-data",
# "cpu_percent": 5.2,
# "memory_percent": 0.8
# }
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue # Accès denied pour certains processus
```
### Points d'accès système
- `/proc/cpuinfo` → Info CPU
- `/proc/meminfo` → Info mémoire
- `/proc/[pid]/` → Info processus
- `/sys/class/net/` → Info réseau
### Permissions nécessaires
```bash
# psutil lit /proc et /sys, accessible par défaut
ls -la /proc | head
# dr-xr-xr-x root root /proc
# Certains processus peuvent être inaccessibles
# (/proc/[pid]/stat pour processus d'autres utilisateurs)
# Solution: lancer avec sudo
sudo python main.py
# Ou relâcher les permissions (attention!)
sudo chmod 755 /proc
```
---
## 5. Frontend et communication API {#frontend}
### Architecture Vue 3
```
App.vue (composant racine)
├── Navigation (header)
├── Sidebar (menu)
└── <RouterView>
├── LoginView.vue (unauthenticated)
├── DashboardView.vue (authenticated)
└── ContainersView.vue (authenticated)
```
### Store Pinia
```javascript
// stores/auth.js
export const useAuthStore = defineStore('auth', () => {
// État réactif
const token = ref(null)
const username = ref(null)
// Computed
const isAuthenticated = computed(() => !!token.value)
// Actions
async function login(username, password) {
const response = await api.post('/auth/login', formData)
token.value = response.data.access_token
localStorage.setItem('token', token.value)
}
function logout() {
token.value = null
localStorage.removeItem('token')
}
return { token, username, isAuthenticated, login, logout }
})
```
### Client HTTP avec Axios
```javascript
// api/index.js
const api = axios.create({
baseURL: 'http://localhost:8000/api/v1',
})
// Interceptor de requête: ajouter le JWT token
api.interceptors.request.use((config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers.Authorization = `Bearer ${token}`
}
return config
})
// Interceptor de réponse: gérer les 401
api.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
// Token expiré, rediriger vers login
window.location.href = '/login'
}
return Promise.reject(error)
}
)
```
### Affichage des données
```vue
<template>
<!-- Barre de progression CPU -->
<div class="w-full bg-gray-700 rounded-full h-2">
<div
class="bg-blue-600 h-2 rounded-full"
:style="{ width: stats.cpu.percent + '%' }"
></div>
</div>
<!-- Tableau des processus avec tri -->
<table>
<tr v-for="proc in stats.processes" :key="proc.pid">
<td>{{ proc.name }}</td>
<td :class="proc.cpu_percent > 50 ? 'text-red-400' : 'text-green-400'">
{{ proc.cpu_percent.toFixed(1) }}%
</td>
</tr>
</table>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import api from '../api'
const stats = ref({})
const fetchStats = async () => {
const response = await api.get('/system/stats')
stats.value = response.data
}
onMounted(() => {
fetchStats()
// Rafraîchir chaque 5 secondes
setInterval(fetchStats, 5000)
})
</script>
```
### Styling Tailwind CSS
```css
/* Classes utilitaires utilisées */
.card {
@apply bg-gray-800 rounded-lg p-6 shadow-lg border border-gray-700;
}
.btn-primary {
@apply bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg;
}
/* Design "dark mode" */
body {
@apply bg-gray-900 text-gray-100;
}
```
---
## 6. Sécurité {#sécurité}
### CORS (Cross-Origin Resource Sharing)
```python
# backend/app/core/config.py
ALLOWED_ORIGINS: list = [
"http://localhost:3000", # Dev frontend
"https://admin.example.com", # Production frontend
]
# Appliqué via middleware
app.add_middleware(
CORSMiddleware,
allow_origins=ALLOWED_ORIGINS,
allow_methods=["*"],
allow_headers=["*"],
)
```
**Importance**: Évite que des sites malveillants fassent des requêtes à votre API
### TrustedHost Middleware
```python
app.add_middleware(
TrustedHostMiddleware,
allowed_hosts=["localhost", "127.0.0.1"],
)
```
**Importance**: Valide l'en-tête Host des requêtes
### Validation Pydantic
```python
class LoginRequest(BaseModel):
username: str
password: str
@validator('username')
def username_alphanumeric(cls, v):
assert v.isalnum() # Seulement alphanumérique
return v
```
**Importance**: Valide et parse automatiquement les données
### JWT Secrets
```bash
# Générer une clé sécurisée
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
# Ajouter au .env
SECRET_KEY=your-generated-key-here
```
**Importance**:
- Doit être complexe (32+ caractères)
- Jamais en clair dans le code
- Unique par environnement (dev/prod)
- Jamais partager
### Protection des endpoints
```python
@router.get("/sensitive-data")
async def sensitive_endpoint(current_user: User = Depends(get_current_user)):
# Cette dépendance valide le token
# Lever HTTPException 401 si invalid
return data
```
### Vérification des permissions
```bash
# Avant de lancer l'app
ls -la /var/run/docker.sock
# crw-rw---- root docker /var/run/docker.sock
# L'utilisateur du serveur doit être dans le groupe docker
id www-data
# uid=33(www-data) gid=33(www-data) groups=33(www-data),999(docker)
```
### Best practices en production
1. ✅ Utiliser HTTPS (TLS/SSL)
2. ✅ Ajouter un reverse proxy (Nginx)
3. ✅ Rate limiting sur les endpoints
4. ✅ Logging des accès
5. ✅ Rotation des secrets
6. ✅ Backup de la config
7. ✅ Monitoring de l'uptime
8. ✅ Firewall IP whitelist
---
## 📊 Résumé flux de données
```
1. USER LOGIN
Client → Browser → POST /auth/login → Backend
Backend: PAM auth → JWT encode → JSON response
Browser: Store token in localStorage
2. API CALL
Browser → GET /system/stats (Header: Authorization: Bearer {token})
Backend: Validate JWT → Call psutil → JSON response
Browser: Update UI with new data
3. DOCKER ACTION
Browser → POST /docker/containers/{id}/start
Backend: Docker.container.start() → JSON response
Browser: Refresh container list
4. AUTO-REFRESH
JS Timer every 5 seconds:
GET /system/stats → Update charts
GET /docker/containers → Update list
```
---
## 🔗 Ressources
- [PAM Documentation](http://www.linux-pam.org/)
- [JWT Introduction](https://jwt.io)
- [Docker Python API](https://docker-py.readthedocs.io)
- [psutil Documentation](https://psutil.readthedocs.io)
- [FastAPI Security](https://fastapi.tiangolo.com/tutorial/security/)
- [Vue 3 Composition API](https://vuejs.org/guide/extras/composition-api-faq.html)