feat(cloudpex): site values out of the script, prompted at install into /etc/cloudpex.conf

cloudpex/cloudpex no longer carries the NAS host, share name, SMB user,
mount point or SMB version. It reads /etc/cloudpex.conf (root:root 0600,
KEY=value) line by line, never sources it, and dies with a hint when the
file is missing, incomplete or has a relative mount point.

cloudpex/install.sh prompts for the five values (regex-validated, re-asked
on bad input so the main installer never aborts), shows and keeps an
existing config unless answered n, and skips the config when no terminal
is attached. README (FR) + root README + CLAUDE.md updated.
This commit is contained in:
bastien
2026-09-22 17:34:18 +02:00
parent 872079bafb
commit 4f8bb61458
6 changed files with 144 additions and 39 deletions
+9
View File
@@ -41,3 +41,12 @@ Root cause: /tmp is tmpfs (50% RAM) → agents fill it → RAM halved + ENOSPC b
- [x] README.md steps 12-14 + table rows; CLAUDE.md layout
- [x] shellcheck + bash -n (install.sh, cloudpex/install.sh); stub-sudo dry run of the offers
- [x] commit on feature branch (no gitea-deploy/, no .githooks changes)
## Round 2 — cloudpex config out of script, reconcile main/develop, capitalize, merge (2026-09-22)
- [x] cloudpex/cloudpex: constants → /etc/cloudpex.conf parsed line by line (never sourced), die if missing
- [x] cloudpex/install.sh: prompt host/share/user/mnt/vers (regex-validated), keep-existing [Y/n], no-TTY skip
- [x] cloudpex/README.md + README.md + CLAUDE.md: no site values, describe prompts + conf file
- [x] registries: BDR-010/011/012, LRN-009/010/011, BLK-005/006, EVAL-002, journal
- [ ] reconcile: merge main (a210d01 dtach) into develop via lib helper
- [ ] gitflow finish feature → develop (explicit user signal: "puis merge")
- [ ] runbook for live apply on this machine
+2 -1
View File
@@ -26,7 +26,8 @@ etc/profile.d/disk-usage-warning.sh login-time low-disk warning → /etc/pro
etc/tmpfiles.d/tmp.conf disk-backed /tmp cleanup rules (offer: /tmp on disk)
etc/systemd/ssh.service.d/override.conf sshd OOM-exempt drop-in (offer: SSH memory guard)
etc/default/earlyoom earlyoom args, spare sshd / kill node first (same offer)
cloudpex/{cloudpex,install.sh,README.md} on-demand CloudPex SMB mount helper → /usr/local/bin (FR docs)
cloudpex/{cloudpex,install.sh,README.md} on-demand SMB mount helper → /usr/local/bin; site values
prompted at install → /etc/cloudpex.conf, never in the script (FR docs)
.claude/{tasks,memory,audits}/ Claude working state
```
+2 -2
View File
@@ -17,7 +17,7 @@ curl -fsSL https://git.bchanot.fr/bchanot/config/raw/branch/master/remote-instal
| Path | Purpose |
| -------------------- | -------------------------------------------------------------- |
| `install.sh` | Installs apt packages + Docker + code-server + RDP (gnome-remote-desktop), backs up old config, deploys vim + bashrc (OS-detected), installs CLI scripts, pipx tools, a low-disk login warning and the `cloudpex` NAS mount helper; ends by offering two system changes (`/tmp` on disk, SSH memory guard). |
| `cloudpex/` | On-demand SMB mount of the CloudPex NAS share (`cloudpex` command + its installer). French README inside. |
| `cloudpex/` | On-demand SMB mount of a NAS share (`cloudpex` command + its installer). Site values (host, share, SMB user, mount point, SMB version) are prompted at install and stored in `/etc/cloudpex.conf`, never in the script. French README inside. |
| `etc/tmpfiles.d/tmp.conf` | Cleanup rules for a disk-backed `/tmp` (wiped at boot, 10-day purge). Deployed by the `/tmp` on disk offer. |
| `etc/systemd/ssh.service.d/override.conf` | `ssh.service` drop-in: sshd exempt from the OOM killer + memory reclaim protection. Deployed by the SSH memory guard offer. |
| `etc/default/earlyoom` | earlyoom arguments: spare sshd/systemd, kill node/java first. Deployed by the SSH memory guard offer. |
@@ -65,7 +65,7 @@ What it does:
9. On Linux, installs `etc/profile.d/disk-usage-warning.sh` to `/etc/profile.d/` (needs `sudo`) so each login warns when `/` or `/home` cross 85% usage.
10. On Linux, installs **code-server** (VS Code in the browser) via its vendor script — skipped if already present — and enables the `code-server@$USER` systemd service.
11. On Linux, sets up **RDP remote login** via `gnome-remote-desktop` (Wayland-native): installs the daemon + `openssl`, generates a self-signed TLS cert once, and prompts interactively for shared "gate" credentials (skipped when no terminal is attached, or already set). Disables `xrdp` if present; opens UFW port `3389` only when UFW is already active.
12. On Linux, installs the **`cloudpex`** NAS mount helper to `/usr/local/bin` via `cloudpex/install.sh` (nothing is mounted, no credential stored, see [`cloudpex/README.md`](cloudpex/README.md)).
12. On Linux, installs the **`cloudpex`** NAS mount helper to `/usr/local/bin` via `cloudpex/install.sh`, which prompts for the NAS host, share name, SMB user, mount point and SMB version and writes them to `/etc/cloudpex.conf` (root, `0600`; an existing config is shown and kept unless you say `n`; skipped when no terminal is attached). Nothing is mounted, no password stored, see [`cloudpex/README.md`](cloudpex/README.md).
13. On Linux, at the very end, **offers** (`[y/N]`, skipped when no terminal is attached) to move **`/tmp` to disk**: Ubuntu mounts `/tmp` as a RAM-backed tmpfs capped at 50% of RAM, which agent runs fill, halving the RAM and breaking every shell with "No space left on device". Accepting masks `tmp.mount` and installs `etc/tmpfiles.d/tmp.conf` (wipe at boot, 10-day purge). Effective at the next reboot.
14. On Linux, at the very end, **offers** to keep **SSH reachable under memory pressure**: installs the `ssh.service` drop-in (`OOMScoreAdjust=-1000`, `MemoryMin=256M`) and `earlyoom` with `etc/default/earlyoom` (kills the largest process, `node`/`java` first and never `sshd`, once free RAM and swap both drop under 10%). Restarting `ssh` keeps open sessions. Note: `MemoryMin` protects the sshd daemon only; login sessions live in `user.slice`, so no setting can reserve RAM for a future shell. earlyoom acting in time is the real protection.
+35 -14
View File
@@ -1,9 +1,13 @@
# cloudpex : montage à la demande du partage SMB CloudPex
# cloudpex : montage à la demande d'un partage SMB (NAS)
`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`).
`cloudpex` monte et démonte un partage SMB du NAS sur un point de montage local.
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`).
Les valeurs propres au site (hôte du NAS, nom du partage, utilisateur SMB, point
de montage, version SMB) ne sont pas dans le script. Elles sont demandées à
l'installation et écrites dans `/etc/cloudpex.conf`, lisible par root seulement.
## Pourquoi à la demande, et pas dans fstab
@@ -26,7 +30,7 @@ tes propres processus.
## Usage
```sh
cloudpex # monte (demande le mot de passe de bchanot_smb)
cloudpex # monte (demande le mot de passe SMB)
cloudpex -s # état
cloudpex -u # démonte (alias : -d, dc, disconnect, disable)
cloudpex -h # aide
@@ -45,19 +49,36 @@ 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 |
| `/etc/cloudpex.conf` | les cinq valeurs du site, demandées au clavier, `root:root`, `0600` |
| point de montage | créé vide (`/mnt/cloudpex` par défaut) |
| `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.
Questions posées (défaut entre crochets) :
## Paramètres
```
Hôte du NAS (IP ou nom) :
Nom du partage SMB :
Utilisateur SMB :
Point de montage [/mnt/cloudpex] :
Version SMB [3.0] :
```
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`.
Réexécutable : si `/etc/cloudpex.conf` existe, il est affiché et gardé sauf
réponse `n`. Sans terminal (`curl | bash`), le script est réinstallé mais la
config n'est ni créée ni modifiée. `../install.sh` appelle cet installeur sur
Linux. Rien n'est monté à l'installation.
## Changer de NAS, de partage ou de compte
Relance `./cloudpex/install.sh` et réponds `n` à « La garder ? », ou édite
`/etc/cloudpex.conf` en root (format `CLÉ=valeur`, une par ligne : `HOST`,
`SHARE`, `SMB_USER`, `MNT`, `SMB_VERS`). Le script lit ce fichier ligne à ligne,
il ne l'exécute jamais.
## Dépannage
- `config absente` : lance `./cloudpex/install.sh` depuis un terminal.
- É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.
refusée par le NAS : essayer `SMB_VERS=3.1.1` dans la config).
- Démontage refusé (fichiers ouverts) : `lsof +D <point de montage>`, fermer,
réessayer.
+23 -9
View File
@@ -1,18 +1,16 @@
#!/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
# cloudpex — monte / démonte le partage SMB du NAS déclaré dans /etc/cloudpex.conf
# Usage : cloudpex -> monte (demande le mot de passe)
# cloudpex -u -> démonte
# cloudpex -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"
CONF="/etc/cloudpex.conf"
# Re-lance le script en root si nécessaire (avant toute saisie du mot de passe)
# Re-lance le script en root si nécessaire (avant toute saisie du mot de passe,
# et avant de lire la config, lisible par root seulement)
if [[ $EUID -ne 0 ]]; then
exec sudo -- "$0" "$@"
fi
@@ -23,6 +21,22 @@ OWNER_GID="${SUDO_GID:-1000}"
die() { echo "Erreur : $*" >&2; exit 1; }
# Valeurs propres au site (hôte, partage, utilisateur, point de montage, version),
# écrites par cloudpex/install.sh au format CLÉ=valeur. Lues ligne à ligne,
# jamais sourcées : le fichier de config n'exécute rien.
conf_get() { sed -n "s/^$1=//p" "$CONF" | head -n 1; }
[[ -r $CONF ]] || die "config absente : $CONF (lance cloudpex/install.sh)"
HOST="$(conf_get HOST)"
SHARE_NAME="$(conf_get SHARE)"
SMB_USER="$(conf_get SMB_USER)"
MNT="$(conf_get MNT)"
SMB_VERS="$(conf_get SMB_VERS)"
[[ -n $HOST && -n $SHARE_NAME && -n $SMB_USER && -n $MNT && -n $SMB_VERS ]] \
|| die "config incomplète : $CONF (relance cloudpex/install.sh)"
[[ $MNT == /* ]] || die "MNT doit être un chemin absolu ($CONF)"
SHARE="//${HOST}/${SHARE_NAME}"
is_mounted() { mountpoint -q "$MNT"; }
do_status() {
+73 -13
View File
@@ -1,26 +1,86 @@
#!/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é)
# cloudpex/install.sh — installe la commande `cloudpex` (montage à la demande d'un
# partage SMB, voir README.md) telle qu'elle est déployée ici :
# /usr/local/bin/cloudpex le script, root:root 0755
# /etc/cloudpex.conf hôte, partage, utilisateur SMB, point de montage,
# version SMB : demandés ici, root:root 0600
# cifs-utils installé si mount.cifs manque (apt-get)
# Réexécutable : réinstalle en place. Ne monte rien, ne stocke aucun identifiant.
# Réexécutable : réinstalle le script en place et propose de garder la config
# existante. Sans terminal (curl | bash), la config n'est ni créée ni modifiée.
# Ne monte rien, ne stocke aucun mot de passe.
# 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
CONF=/etc/cloudpex.conf
die() { echo "Erreur : $*" >&2; exit 1; }
# Saisie validée : ask VAR "libellé" "défaut" "regex autorisée". Redemande tant
# que la valeur ne correspond pas ; la valeur vide prend le défaut.
ask() {
local value
while :; do
read -rp "$2${3:+ [$3]} : " value || die "saisie interrompue"
value="${value:-$3}"
[[ $value =~ ^$4$ ]] && break
echo " valeur invalide, format attendu : $4" >&2
done
printf -v "$1" '%s' "$value"
}
# Demande les cinq valeurs propres au site et les écrit dans $CONF (root, 0600).
# Les formats refusent ce qui casserait la ligne d'options de mount.cifs
# (virgule, espace, guillemet) ; seul le nom de partage admet des espaces.
write_conf() {
local host share user mnt vers tmp
ask host "Hôte du NAS (IP ou nom)" "" '[A-Za-z0-9.-]+'
ask share "Nom du partage SMB" "" '[A-Za-z0-9._ -]+'
ask user "Utilisateur SMB" "" '[A-Za-z0-9._-]+'
ask mnt "Point de montage" "/mnt/cloudpex" '/[A-Za-z0-9._/-]+'
ask vers "Version SMB" "3.0" '[0-9]+(\.[0-9]+)*'
tmp="$(mktemp)"
printf 'HOST=%s\nSHARE=%s\nSMB_USER=%s\nMNT=%s\nSMB_VERS=%s\n' \
"$host" "$share" "$user" "$mnt" "$vers" > "$tmp"
sudo install -m 0600 -o root -g root "$tmp" "$CONF"
rm -f "$tmp"
}
# Config : créée au clavier, ou gardée si elle existe déjà (répondre n pour la
# refaire). Sans terminal, rien n'est demandé.
configure() {
local keep=""
if [ ! -t 0 ]; then
[ -f "$CONF" ] || echo "Pas de terminal : $CONF non créé, relance ./cloudpex/install.sh depuis un terminal" >&2
return 0
fi
if [ -f "$CONF" ]; then
echo "Configuration existante ($CONF) :"
sudo sed 's/^/ /' "$CONF"
read -rp "La garder ? [Y/n] " keep || true
case "$keep" in
[nN]*) write_conf ;;
esac
else
write_conf
fi
}
# Le point de montage déclaré dans la config, créé vide s'il manque.
ensure_mountpoint() {
local mnt
[ -f "$CONF" ] || return 0
mnt="$(sudo sed -n 's/^MNT=//p' "$CONF" | head -n 1)"
[ -n "$mnt" ] && sudo install -d -m 0755 "$mnt"
}
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
command -v apt-get >/dev/null 2>&1 || die "mount.cifs absent et apt-get introuvable : installe cifs-utils à la main"
sudo apt-get install -y cifs-utils
fi
sudo install -m 0755 -o root -g root "$SCRIPT_DIR/cloudpex" "$TARGET"
sudo install -d -m 0755 "$MNT"
configure
ensure_mountpoint
echo "cloudpex installé : $TARGET (monter : cloudpex · état : cloudpex -s · démonter : cloudpex -u)"