---
tags: [note, Securite]
type: "note"
nature: documentation
ressource: true
univers: PERSO
projects:
  - "[[03_PROJETS/VPS-IA]]"
date: "[[01_JOURNAL/2026/2026-10-16]]"
status: active
description: "Audit en lecture seule via sudoers whitelisté : SSH par clé, domaines orphelins et binds 0.0.0.0 traités."
---
# Audit sécurité VPS datagrandest.net — état des lieux

## Contexte & méthode

Demande d'**état des lieux et d'analyse**, sans aucune correction ni modification du système.

- **Exécutant** : agent `opencode` (compte de service, shell `nologin`, hors groupe `sudo`).
- **Accès privilégié** : fragment sudoers `/etc/sudoers.d/opencode-audit` déposé par GRK, **liste blanche lecture seule uniquement** (pas de shell root possible).
- **Validation de l'isolateur** : `visudo` OK + **12 tests négatifs refusés** (`id`, `bash -c`, `cat /etc/shadow`, `journalctl`, `nft list table`, `systemctl list-unit-files`, `find -exec`, `cat ... /etc/shadow` traversée de chemins, `su -`, `visudo`) et **8 tests positifs conformes**.
- **Conclusion machine** : Debian 12 (bookworm), noyau 6.1.0-51-cloud (2026-07-16), KVM/OpenStack, 87 j d'uptime, IP `217.182.65.158`, 25 apps YunoHost, 41 domaines déclarés.

## 1. Synthèse — état au 17 octobre 2026

Suivi des actions dans : [[04_NOTES/OVH/VPS-OVH_Securisation_20261016]]

| Gravité | Constat | État |
|---|---|---|
| 🔴 Élevé | API **Ollama exposée publiquement hors SSO** (`/api/embed` répondait 200 sans auth) | ✅ **traité** : SSO actif (302) + appel en local `127.0.0.1:11434` |
| 🔴 Élevé | 9 services en `0.0.0.0/*`, protégés **uniquement** par la `policy drop` | 🟡 **partiel** : MariaDB ✅ et Ollama ✅ passés en local ; guacamole 8088, onlyoffice 8095/8126, rabbitmq 4369/5672/25672, slapd 636, uvicorn 8000, epmd **restent** (toujours bloqués par le pare-feu) |
| 🔴 Élevé | **SSH par mot de passe** sur `shizendo` (27 sessions password en 2 semaines) | ✅ **traité** : clé publique + `PasswordAuthentication no` (vérifié `Permission denied (publickey,password)`) |
| 🟠 Moyen | **10 jails fail2ban à 0** → couverture HTTP non prouvée | ⏳ **non traité** : 15 jails actives (ntfy ajoutée), mais aucun test d'efficacité HTTP réalisé |
| 🟠 Moyen | `logrotate` en échec chaque nuit | ✅ **traité** (`su guacamole` + `su coturn`) — **confirmation attendue le 18/10 à 00:00** |
| 🟠 Moyen | Comptes interactifs superflus (`debian`, `postgres`, `kamio` en bash) ; `shizendo` à 35 groupes | ⏳ **non traité** (vérification préalable requise) |
| 🟠 Moyen | ~16 domaines/vhosts **orphelins** | ✅ **traité** : 41 → **29 domaines** le 17/10 (dont `pass.datagrandest.net` conservé → rôle à définir) |
| 🟡 Faible | 5 règles pare-feu orphelines + mDNS 5353 exposé | ✅ **traité** : 22, 53, 1900, 5353, 55354 fermés ; **`8449` conservé** (0 écouteur → à investiguer) |
| 🟠 Moyen | **5 clés d'API en clair** dans l'espace (constat ajouté le 17/10) | ✅ **traité** : 5 → 2 clés, chacune référencée vers KeePass ; note OLLAMA à purger |
| 🟢 OK | 0 paquet à mettre à jour, unattended-upgrades quotidien, TLS 1.3 valide jusqu'au 26/11/2026, `PermitRootLogin no`, SUID standards, rien de world-writable de sensible | ✔ inchangé |

## 2. Surface réseau

**`nft` : `policy input drop`** — bonne base.
Autorisé : `tcp 22, 25, 80, 222, 443, 587, 993, 5351, 7883, 8449, 22000` · `udp 53, 1900, 5351, 5353, 55354`.

| Port autorisé | Écouteur réel | Verdict |
|---|---|---|
| tcp 22 | **aucun** (sshd est sur 222) | règle orpheline |
| tcp 8449 | **aucun** | orpheline **+ fédération tuwunel injoignable** |
| udp 1900, udp 55354 | **aucun** | orphelines (UPnP inutile sur un VPS) |
| udp 5353 | `avahi-daemon` sur `0.0.0.0` | **mDNS exposé à Internet** → fuite des noms de services |
| udp/tcp 53 | `dnsmasq` sur `0.0.0.0` | query vers l'IP publique = **timeout** (pas de résolveur ouvert visible depuis l'intérieur) → à confirmer de l'extérieur |
| tcp/udp 5351 | coturn ✔ | OK, mais coturn écoute aussi **3478/3479 bloqués** → port TURN standard mort |

`forward policy accept` et `output policy accept` (sans règles) : pas d'incident actuel, à documenter.

### Défense en profondeur absente

Ces services ne devraient **pas** lier hors de `127.0.0.1` :

```
*:3306  mariadbd      *:11434 ollama        *:4369 / *:5672 / *:25672 rabbitmq
*:8000  uvicorn       *:8088  java(guac)    *:8095 docservice
*:8126  statsd        *:636   slapd         *:8081 lk-jwt       *:44743 notify_push
```

Une seule régression de règle nft (ou un reconfigurateur UPnP actif) les expose.

## 3. Exposition applicative (test SSO)

**Conforme** (302 → SSO) : codeserver, nodered, guacamole, minio, opencode, ntfy, grist, draw, chat, rclone, excalidraw-mcp, fail2ban.shizendo.ovh, silverbullet.shizendo.ovh, kamio, synapse-admin.

| Endpoint | Réponse | Risque |
|---|---|---|
| `ollama.datagrandest.net/api/tags` | 200 + liste des modèles | énumération |
| `ollama.datagrandest.net/api/embed` (POST) | **200 + vecteurs générés** | **API utilisable librement** → abus de ressources |
| `wallabag.datagrandest.net` | 302 → `/login` | auth propre, hors SSO |
| `onlyoffice.datagrandest.net/welcome/` | 200 | page de service |
| `litechat`, `sync`, `sb-sync`, `site.shizendo.ovh` | 200 | à revalider au cas par cas |
| `fail2ban.datagrandest.net` | **aucune réponse** | vhost résiduel |

## 4. Identités & accès

- **sshd** : port 222, `PermitRootLogin no`, `AllowGroups ssh.main sftp.main ssh.app sftp.app admins root`, kex/ciphers récents ✔ — mais **`PasswordAuthentication yes` global**.
- **Brute force** : 3 820 échecs/jour, **14 164 en 7 jours**, **17 385 échecs btmp** depuis le 01/10.
  Top : `193.187.110.159` (1155), `193.187.110.214` (782), `112.197.2.116` (660). Cibles : `root` (4161), `admin`, `ubuntu`.
- **Sessions réussies** : 50× `forgejo` en **pubkey** ✔ — 27× `shizendo` en **mot de passe**.
- **Session ouverte** : `shizendo` depuis `158.173.158.117` depuis le 14/10. Dernier `sudo` enregistré : 10/10 par `shizendo`.
- **Comptes avec shell** : `root`, `debian`, `gryckelynck`, `kamio`, `postgres` + 24 comptes d'apps en `/bin/sh` (bloqués par `AllowGroups`) ; `opencode` = `nologin` ✔.
- **sudo** : `opencode` hors groupe sudo, isolateur actif (12/12 tests refusés).

### 4.1 Risque du SSH « utilisateur sudo + mot de passe »

`shizendo` est dans `admins` (donc `AllowGroups`) et escalade en root via sudo : **un seul secret protège l'accès distant et root**.

- Pas une faille en soi, mais le **maillon le plus faible** : brute force massif en cours, pas de 2FA, pas de restriction d'IP, `bantime` fail2ban = 10 min / `maxretry` 10 → un botnet peut tenter des milliers de mots de passe par jour en revenant toutes les 10 minutes.
- Un compromis = accès complet au serveur et à 25 apps + données.
- `shizendo` cumule 35 groupes : surface d'applications très large pour un compte d'administration.

## 5. Détection — état des lieux

| Levier | Constat |
|---|---|
| fail2ban | 14 jails actives. Bans cumulés : `sshd 1066`, `postfix 21`, `pam-generic 4`, `recidive 2` — **10 jails à 0**. Défaut `bantime=10m`, `maxretry=10` |
| Lecture logs fail2ban | `root:adm` → **inaccessible hors root** = non supervisable par un service |
| Journal sudo | 0 entrée pour `opencode` ; aucune alerte sur écriture dans `/etc/sudoers.d/` |
| Rotation logs | **`logrotate` en échec quotidien** ; `/var/log` = 558 Mo ; disque à **73 %** (69/99 Go) |
| Services en échec | `minio` **failed depuis le 20/07 mais enabled** ; `rclone` not-found ; résidu chromium `run-u482` — **aucune alerte** |
| Exposition réseau | aucun contrôle « ports ouverts vs écouteurs » → 5 règles orphelines vivent depuis des mois |
| Hors-SSO | aucun contrôle continu → Ollama hors SSO depuis son installation |
| Alerte | domaine `ntfy` déclaré, **aucune chaîne d'alerte opérationnelle** visible |

## 6. Pistes d'amélioration de la détection

1. **Prouver la couverture HTTP** : injecter une entrée factice dans `nginx` access.log pour vérifier que les jails `yunohost` / `nginx-http-auth` matchent ; sinon ajuster filtres, `maxretry`, `findtime`.
2. **Durcir la jail SSH** : `maxretry=3`, `bantime=1h` avec `bantime.increment=true`.
3. **Rendre les logs lisibles** : groupe dédié (`logreaders`) sur `fail2ban.log`, ou export/API — préalable à tout monitoring.
4. **Contrôle continu d'exposition** : cron hebdo `ss -tulnp` + `nft list ruleset` → alerte sur drift (port qui ouvre, écouteur qui apparaît sur `0.0.0.0`).
5. **Contrôle SSO continu** : `curl -i` sur chaque vhost, alerte si code ≠ 302 vers `/yunohost/sso` (aurait détecté Ollama).
6. **Alerte services en échec** : `OnFailure=` sur les unités critiques ou `systemctl --failed` en cron → notification `ntfy`.
7. **Disque & rotation** : corriger les perms `/var/log/guacamole` (seul bloquant de `logrotate`), alerter à 80 %.
8. **Journaliser les privilèges** : alerte sur écriture dans `/etc/sudoers.d/` et sur `sudo:` dans `auth.log`.
9. **Audit externe récurrent** (nmap depuis l'extérieur) : seul moyen de *prouver* que la `policy drop` fait le travail.
10. **Réduire la surface** : 16 domaines orphelins, 5 règles mortes, comptes inutiles → moins de surface, moins de bruit.

## 7. Plan d'action priorisé

### P0 — à traiter en premier
1. **SSH clé publique uniquement** pour `shizendo` (`Match User shizendo` + `PasswordAuthentication no`), break-glass en KeePass.
2. **Durcir fail2ban SSH** : `maxretry=3`, `bantime=1h`, `bantime.increment=true`.
3. **Mettre Ollama derrière le SSO** (ou le restreindre en IP) — l'API `/api/embed` répond sans auth.
4. **Lier `0.0.0.0` → `127.0.0.1`** pour MariaDB, RabbitMQ, Ollama, Guacamole, OnlyOffice, slapd, uvicorn.
5. **Corriger les perms `/var/log/guacamole`** pour débloquer `logrotate`.

### P1 — semaines suivantes
6. Supprimer les 5 règles orphelines ; fermer `udp 5353` (mDNS) et revalider `udp 53` / `1900` / `55354`.
7. Activer un canal d'alerte (`ntfy`) sur `systemctl --failed`, disque > 80 %, dérive de pare-feu.
8. Groupe dédié sur `fail2ban.log` + script de supervision hebdomadaire.
9. Nettoyer les ~16 domaines/vhosts orphelins ; retirer ou documenter les comptes `debian`, `kamio`, `postgres` à shell.
10. Vérifier la fédération tuwunel (port 8449 ouvert mais sans écouteur).

### P2 — à moyen terme
11. Audit externe automatisé (exposition réelle des ports) + test SSO automatisé.
12. 2FA si le mot de passe SSH devait être conservé (PAM, attention compatibilité YunoHost).
13. Réduire les groupes de `shizendo` (moindre privilège) et alerter sur toute session sudo.

## 8. Angles morts de l'audit

`journalctl` · `/etc/nginx` · `/etc/yunohost/apps` · config `dnsmasq` · logs `nginx`/`root` · logs `unattended-upgrades` · `yunohost backup list` · confirmation **externe** de l'exposition (aucun scan sortant réalisé) · contenu de `/etc/sudoers`.

## Annexes

### Whitelist sudoers utilisée

```
/etc/sudoers.d/opencode-audit
Cmnd_Alias AUDIT_NET  = ss -tulnp, ss -tunap
Cmnd_Alias AUDIT_FW   = nft list ruleset, iptables-save, ip6tables-save, yunohost firewall list
Cmnd_Alias AUDIT_F2B  = fail2ban-client status [, ...]
Cmnd_Alias AUDIT_SVC  = systemctl list-units/status/show *
Cmnd_Alias AUDIT_YNH  = yunohost app list|app permission list|domain list|cert-info|service list|diagnosis run *
Cmnd_Alias AUDIT_SYS  = df, free, uptime, uname, who, last, lastb, apt list --upgradable
Cmnd_Alias AUDIT_READ = cat (5 fichiers de config exacts)
Cmnd_Alias AUDIT_SUID = find / -perm -4000 -type f
opencode ALL = (root) NOPASSWD: <les alias ci-dessus>
```

Note d'usage : `systemctl list-units` exige un argument (`--all` ou `--no-pager`). Les commandes `yunohost app permission list` et `yunohost domain cert-info` **n'existent pas** dans cette version de YunoHost (choix : `user permission` / `domain cert` — ce dernier étant une action, il n'a pas été whitelisté).

### Commandes clés utilisées

```
sudo nft list ruleset        sudo yunohost firewall list | app list | domain list
sudo ss -tulnp               sudo fail2ban-client status [jail]
sudo systemctl status X      sudo last / lastb
sudo find / -perm -4000 -type f       sudo cat <config exacte>
curl -i https://<vhost>/             openssl s_client (échéance TLS)
```

### Références internes

→ Projet : [[03_PROJETS/VPS-IA]] · Agent : [[80_CONFIG/IA_AGENTS/vps-admin]]
→ Notes : [[04_NOTES/VPS-IA_Installation-OpenCode]], [[04_NOTES/VPS-IA_Installation-Pass-Gestionnaire-Secrets]]
