ajoute le dossier WebDAV de l'élève (bloc C)
Dossier personnel de l'élève monté comme lecteur réseau : rclone sert le dossier local, l'authentification est déléguée au portail via auth_request Nginx (mêmes identifiants que la connexion), sans nouveau système de comptes. Nettoyé par la purge RGPD comme les autres données d'élève. Corrige au passage un vrai bug d'isolation trouvé en testant avec deux comptes jetables : rewrite injectait la variable posée par auth_request_set, mais rewrite s'exécute dans une phase Nginx antérieure à cette variable, donc toujours vide - un élève pouvait lire le fichier d'un autre en devinant son nom. Corrigé par une location à regex nommée + proxy_pass. Détails : ADR 0008. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
cdf79d2823
commit
e549f03e1b
19 changed files with 553 additions and 8 deletions
|
|
@ -24,3 +24,5 @@ portail_port_visio: 8090
|
|||
# routeur par nom (stream Nginx) l'envoie directement à coturn.
|
||||
portail_domaine_turn: turn.syllabuslab.fr
|
||||
portail_port_turn_tls: 5349
|
||||
# Bloc C : dossier de l'élève en WebDAV (rclone, local pour l'instant).
|
||||
portail_port_webdav: 8765
|
||||
|
|
|
|||
|
|
@ -14,3 +14,9 @@
|
|||
name: portail-horizon
|
||||
state: restarted
|
||||
enabled: true
|
||||
|
||||
- name: redémarrer rclone-webdav
|
||||
ansible.builtin.systemd:
|
||||
name: rclone-webdav
|
||||
state: restarted
|
||||
enabled: true
|
||||
|
|
|
|||
35
ansible/roles/portail/tasks/09-webdav.yml
Normal file
35
ansible/roles/portail/tasks/09-webdav.yml
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
# Bloc C (CLAUDE.md) : dossier de l'élève monté comme lecteur réseau WebDAV.
|
||||
# rclone sert un dossier local en WebDAV (bascule vers un backend S3
|
||||
# possible plus tard sans changer l'architecture : rclone sait servir les
|
||||
# deux). Nginx fait l'authentification (déléguée au portail, mêmes
|
||||
# identifiants que la connexion) et restreint chaque élève à son seul
|
||||
# sous-dossier avant de relayer vers rclone — voir 05-nginx.yml et
|
||||
# App\Http\Controllers\WebdavAuthController.
|
||||
- name: installer rclone
|
||||
ansible.builtin.apt:
|
||||
name: rclone
|
||||
state: present
|
||||
|
||||
- name: créer le dossier racine du WebDAV des élèves
|
||||
ansible.builtin.file:
|
||||
path: "{{ portail_dossier_app }}/storage/app/webdav"
|
||||
state: directory
|
||||
owner: www-data
|
||||
group: www-data
|
||||
mode: "0750"
|
||||
|
||||
- name: déployer le service systemd de rclone (WebDAV)
|
||||
ansible.builtin.template:
|
||||
src: rclone-webdav.service.j2
|
||||
dest: /etc/systemd/system/rclone-webdav.service
|
||||
mode: "0644"
|
||||
notify:
|
||||
- recharger systemd
|
||||
- redémarrer rclone-webdav
|
||||
|
||||
- name: activer et démarrer rclone (WebDAV)
|
||||
ansible.builtin.systemd:
|
||||
name: rclone-webdav
|
||||
state: started
|
||||
enabled: true
|
||||
|
|
@ -14,6 +14,9 @@
|
|||
- name: banque de quiz officielle (contenu éditorial de paillasse-contenus/)
|
||||
ansible.builtin.import_tasks: 08-banque-quiz.yml
|
||||
|
||||
- name: dossier de l'élève en WebDAV (bloc C)
|
||||
ansible.builtin.import_tasks: 09-webdav.yml
|
||||
|
||||
- name: Nginx
|
||||
ansible.builtin.import_tasks: 05-nginx.yml
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,47 @@
|
|||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
}
|
||||
|
||||
# Bloc C : dossier de l'élève en WebDAV (rclone en local, voir
|
||||
# 09-webdav.yml). "internal" : jamais appelable depuis l'extérieur,
|
||||
# seulement par la sous-requête auth_request ci-dessous.
|
||||
# $request_uri d'une sous-requête auth_request reflète la requête
|
||||
# d'origine ("/webdav/…", pas "/interne/webdav-auth") : constaté le
|
||||
# 2026-10-14 (404 systématique côté Laravel, qui routait donc sur le
|
||||
# mauvais chemin). D'où REQUEST_URI écrit en dur ci-dessous — l'URI de
|
||||
# cette location est fixe, il n'y a rien d'autre qu'elle ne pourrait
|
||||
# valoir.
|
||||
location = /interne/webdav-auth {
|
||||
internal;
|
||||
fastcgi_pass unix:/run/php/php{{ portail_php_version }}-fpm.sock;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME {{ portail_dossier_app }}/public/index.php;
|
||||
fastcgi_param SCRIPT_NAME /index.php;
|
||||
fastcgi_param REQUEST_URI /interne/webdav-auth;
|
||||
fastcgi_param REQUEST_METHOD GET;
|
||||
}
|
||||
|
||||
# "rewrite" s'exécute dans une phase antérieure à auth_request_set (le
|
||||
# rewrite verrait donc toujours $eleve_dossier vide, et chaque élève
|
||||
# atterrirait à la racine commune du dossier — constaté le 2026-10-14,
|
||||
# un élève pouvait lire les fichiers d'un autre). proxy_pass avec le nom
|
||||
# capturé par la regex de la location, lui, est évalué après coup.
|
||||
location ~ ^/webdav/(?<chemin_webdav>.*)$ {
|
||||
auth_request /interne/webdav-auth;
|
||||
auth_request_set $eleve_dossier $upstream_http_x_eleve_dossier;
|
||||
error_page 401 = @webdav_non_authentifie;
|
||||
client_max_body_size 100m;
|
||||
proxy_pass http://127.0.0.1:{{ portail_port_webdav }}/$eleve_dossier/$chemin_webdav;
|
||||
proxy_http_version 1.1;
|
||||
}
|
||||
|
||||
# Sans cela, un explorateur de fichiers (Windows/macOS) ne propose
|
||||
# jamais de ressaisir les identifiants après un échec.
|
||||
location @webdav_non_authentifie {
|
||||
add_header WWW-Authenticate 'Basic realm="Syllabus WebDAV"' always;
|
||||
return 401;
|
||||
}
|
||||
|
||||
location ~ /\.(?!well-known) {
|
||||
deny all;
|
||||
}
|
||||
|
|
|
|||
27
ansible/roles/portail/templates/rclone-webdav.service.j2
Normal file
27
ansible/roles/portail/templates/rclone-webdav.service.j2
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# Géré par Ansible (rôle portail) — ne pas modifier à la main.
|
||||
# Bloc C (CLAUDE.md) : dossier de l'élève monté comme lecteur réseau.
|
||||
# N'écoute qu'en local (127.0.0.1) : Nginx authentifie (délégué au portail)
|
||||
# et restreint chaque élève à son sous-dossier avant de relayer ici — voir
|
||||
# ansible/roles/portail/templates/nginx-portail.conf.j2 et
|
||||
# App\Http\Controllers\WebdavAuthController.
|
||||
[Unit]
|
||||
Description=Serveur WebDAV (rclone) pour le dossier des élèves — Syllabus
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
Group=www-data
|
||||
# --dir-cache-time : sans ça, les dossiers créés par Laravel (un par élève,
|
||||
# en dehors de rclone) restent invisibles jusqu'à 5 minutes (la valeur par
|
||||
# défaut) — constaté le 2026-10-14, un nouveau dossier élève répondait 404
|
||||
# tant que rclone ne l'avait pas découvert lui-même.
|
||||
ExecStart=/usr/bin/rclone serve webdav {{ portail_dossier_app }}/storage/app/webdav --addr 127.0.0.1:{{ portail_port_webdav }} --no-modtime --vfs-cache-mode off --dir-cache-time 1s
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths={{ portail_dossier_app }}/storage/app/webdav
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Loading…
Add table
Add a link
Reference in a new issue