diff --git a/cloudpex/README.md b/cloudpex/README.md new file mode 100644 index 0000000..d0d9415 --- /dev/null +++ b/cloudpex/README.md @@ -0,0 +1,63 @@ +# cloudpex : montage à la demande du partage SMB CloudPex + +`cloudpex` monte et démonte le partage SMB `CloudPex` du NAS (`//192.168.1.111/CloudPex`) +sur `/mnt/cloudpex`. Le mot de passe SMB est demandé à chaque montage. Rien n'est +écrit sur disque, rien ne passe en argument (le mot de passe est transmis à +`mount.cifs` par la variable d'environnement `PASSWD`, invisible dans `ps`). + +## Pourquoi à la demande, et pas dans fstab + +Sur l'ancien serveur le partage était monté en permanence, en écriture, avec +`uid=1000` forcé. Tout processus de l'utilisateur pouvait donc tout effacer, agents +Claude compris. C'est ce qui a rendu l'incident du 21/09 total (voir +`RECOVERY/01-prochain-systeme/04-NAS-cloudpex-sauvegardes.md` sur le partage). + +Ce script applique la règle 2 de ce document : + +- montage à la demande, par un humain, jamais automatique au boot ; +- aucun identifiant stocké (`/root/.smbcredentials` n'existe plus) ; +- `noexec,nosuid,nodev` : rien ne s'exécute depuis le partage ; +- `dir_mode=0750`, propriétaire = l'utilisateur qui a lancé `sudo` : les autres + comptes (dont les comptes agents) ne voient pas le contenu. + +Démonte quand tu as fini (`cloudpex -u`). Un partage monté reste effaçable par +tes propres processus. + +## Usage + +```sh +cloudpex # monte (demande le mot de passe de bchanot_smb) +cloudpex -s # état +cloudpex -u # démonte (alias : -d, dc, disconnect, disable) +cloudpex -h # aide +``` + +Le script se relance lui-même via `sudo` : pas besoin de le préfixer. + +## Installation + +```sh +./cloudpex/install.sh +``` + +Ce que ça fait, à l'identique de cette machine : + +| Cible | Détail | +| --- | --- | +| `/usr/local/bin/cloudpex` | copie du script, `root:root`, `0755` | +| `/mnt/cloudpex` | point de montage, créé vide | +| `cifs-utils` | installé via `apt-get` seulement si `mount.cifs` manque | + +Réexécutable. `../install.sh` l'appelle sur Linux, donc une installation complète +du dépôt suffit. Rien n'est monté à l'installation. + +## Paramètres + +En tête de `cloudpex` : `SHARE`, `MNT`, `SMB_USER`, `SMB_VERS`. Pour changer de NAS +ou de compte, édite ces valeurs puis relance `./cloudpex/install.sh`. + +## Dépannage + +- Échec du montage : `dmesg | tail` (mot de passe, réseau, ou version SMB + refusée par le NAS : essayer `SMB_VERS="3.1.1"`). +- Démontage refusé (fichiers ouverts) : `lsof +D /mnt/cloudpex`, fermer, réessayer. diff --git a/cloudpex/cloudpex b/cloudpex/cloudpex new file mode 100644 index 0000000..7daaa0f --- /dev/null +++ b/cloudpex/cloudpex @@ -0,0 +1,71 @@ +#!/usr/bin/env bash +# cloudpex-mount — monte / démonte le partage SMB CloudPex du NAS +# Usage : cloudpex-mount -> monte (demande le mot de passe) +# cloudpex-mount -u -> démonte +# cloudpex-mount -s -> état +# Aucun credential n'est écrit sur disque ni passé en argument (invisible dans ps). + +set -euo pipefail + +SHARE="//192.168.1.111/CloudPex" +MNT="/mnt/cloudpex" +SMB_USER="bchanot_smb" +SMB_VERS="3.0" + +# Re-lance le script en root si nécessaire (avant toute saisie du mot de passe) +if [[ $EUID -ne 0 ]]; then + exec sudo -- "$0" "$@" +fi + +# Propriétaire des fichiers montés : l'utilisateur qui a lancé sudo, sinon 1000 +OWNER_UID="${SUDO_UID:-1000}" +OWNER_GID="${SUDO_GID:-1000}" + +die() { echo "Erreur : $*" >&2; exit 1; } + +is_mounted() { mountpoint -q "$MNT"; } + +do_status() { + if is_mounted; then + echo "Monté : $SHARE -> $MNT" + df -h "$MNT" | tail -n 1 + else + echo "Non monté." + fi +} + +do_umount() { + is_mounted || { echo "Déjà démonté."; return 0; } + umount "$MNT" || die "démontage impossible (fichiers ouverts ? voir : lsof +D $MNT)" + echo "Démonté : $MNT" +} + +do_mount() { + command -v mount.cifs >/dev/null || die "cifs-utils absent (sudo apt install cifs-utils)" + is_mounted && { echo "Déjà monté : $MNT"; return 0; } + + mkdir -p "$MNT" + + local pass + read -rsp "Mot de passe SMB pour ${SMB_USER}@${SHARE} : " pass + echo + [[ -n "$pass" ]] || die "mot de passe vide" + + # mount.cifs lit PASSWD dans l'environnement : pas d'exposition dans ps ni sur disque + if PASSWD="$pass" mount -t cifs "$SHARE" "$MNT" \ + -o "username=${SMB_USER},uid=${OWNER_UID},gid=${OWNER_GID},iocharset=utf8,vers=${SMB_VERS},file_mode=0640,dir_mode=0750,nosuid,nodev,noexec"; then + unset pass + echo "Monté : $SHARE -> $MNT" + else + unset pass + die "échec du montage (mot de passe, réseau ou version SMB ; voir : dmesg | tail)" + fi +} + +case "${1:-}" in + "") do_mount ;; + -u|-d|--umount|dc|disconnect|disable) do_umount ;; + -s|--status) do_status ;; + -h|--help) sed -n '2,6p' "$0" ;; + *) die "option inconnue : $1 (voir -h)" ;; +esac diff --git a/cloudpex/install.sh b/cloudpex/install.sh new file mode 100755 index 0000000..9e8f9f4 --- /dev/null +++ b/cloudpex/install.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# cloudpex/install.sh — installe la commande `cloudpex` (montage à la demande du +# partage SMB CloudPex du NAS, voir README.md) telle qu'elle est déployée ici : +# /usr/local/bin/cloudpex root:root 0755 +# /mnt/cloudpex point de montage (vide tant que rien n'est monté) +# cifs-utils installé si mount.cifs manque (apt-get) +# Réexécutable : réinstalle en place. Ne monte rien, ne stocke aucun identifiant. +# Usage : ./cloudpex/install.sh (appelé aussi par ../install.sh sur Linux) +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +TARGET=/usr/local/bin/cloudpex +MNT=/mnt/cloudpex + +if ! command -v mount.cifs >/dev/null 2>&1; then + if command -v apt-get >/dev/null 2>&1; then + sudo apt-get install -y cifs-utils + else + echo "mount.cifs absent et apt-get introuvable : installe cifs-utils à la main" >&2 + exit 1 + fi +fi + +sudo install -m 0755 -o root -g root "$SCRIPT_DIR/cloudpex" "$TARGET" +sudo install -d -m 0755 "$MNT" +echo "cloudpex installé : $TARGET (monter : cloudpex · état : cloudpex -s · démonter : cloudpex -u)"