commit 467d74597314f600d4768644914a561b3dfd1f94
parent 02586fc84e1a4c13408396ac48808aa676baeddd
Author: Pablo Murad <pablo@pablomurad.com>
Date: Sat, 21 Mar 2026 15:49:28 -0300
fix
Diffstat:
2 files changed, 55 insertions(+), 54 deletions(-)
diff --git a/scripts/admin/setup_alt_protocols.py b/scripts/admin/setup_alt_protocols.py
@@ -7,7 +7,7 @@ Infraestrutura Gopher (gophernicus) e Gemini (molly-brown) para runv.club.
Idempotente, dry-run, subprocess sem shell. Executar como root no Debian.
-Versão 0.05 — runv.club
+Versão 0.06 — runv.club
"""
from __future__ import annotations
@@ -30,7 +30,7 @@ from typing import Any, Final
# Constantes
# ---------------------------------------------------------------------------
-VERSION: Final[str] = "0.05"
+VERSION: Final[str] = "0.06"
DEFAULT_USERS_JSON: Final[Path] = Path("/var/lib/runv/users.json")
DEFAULT_HOMES_ROOT: Final[Path] = Path("/home")
@@ -46,17 +46,12 @@ GOPHER_DEFAULT_PATH: Final[Path] = Path("/etc/default/gophernicus")
GOPHER_SYSTEMD_SERVICE: Final[Path] = Path("/lib/systemd/system/gophernicus@.service")
MOLLY_CONF_DIR: Final[Path] = Path("/etc/molly-brown")
MOLLY_INSTANCE: Final[str] = "runv.club" # molly-brown@runv.club.service
-MOLLY_LOG_DIR: Final[Path] = Path("/var/log/molly-brown")
-MOLLY_SYSTEMD_DROPIN_DIR: Final[Path] = Path(
- "/etc/systemd/system/molly-brown@.service.d"
-)
-MOLLY_LOGS_DROPIN_PATH: Final[Path] = (
- MOLLY_SYSTEMD_DROPIN_DIR / "50-runv-logs.conf"
-)
-MOLLY_LOGS_DROPIN_BODY: Final[str] = (
- "# runv.club — gerido por setup_alt_protocols.py\n"
- "[Service]\n"
- "LogsDirectory=molly-brown\n"
+# StateDirectory=molly-brown no unit Debian — systemd cria /var/lib/molly-brown
+# com o dono correcto (DynamicUser) antes do ExecStart; evita conflitos com
+# LogsDirectory + directório pré-existente em /var/log.
+MOLLY_LOG_DIR: Final[Path] = Path("/var/lib/molly-brown")
+MOLLY_LOGS_DROPIN_PATH: Final[Path] = Path(
+ "/etc/systemd/system/molly-brown@.service.d/50-runv-logs.conf"
)
PACKAGES_GOPHER: Final[tuple[str, ...]] = ("gophernicus",)
@@ -188,37 +183,28 @@ def molly_log_paths(instance: str) -> tuple[Path, Path]:
)
-def write_molly_brown_logs_dropin(
+def retire_molly_brown_logs_dropin(
*,
dry_run: bool,
log: logging.Logger,
force: bool,
) -> None:
"""
- Garante LogsDirectory=molly-brown no unit molly-brown@.
+ Remove 50-runv-logs.conf (LogsDirectory=molly-brown) se existir.
- O pacote Debian usa DynamicUser=yes; o systemd cria /var/log/molly-brown com
- o dono correcto em cada arranque — não usar chown estático nos logs.
+ Esse drop-in + directório /var/log/molly-brown criado antes do arranque faz o
+ systemd migrar para /var/log/private/ e pode deixar o Molly a falhar. Os
+ logs passam a usar só StateDirectory em /var/lib/molly-brown.
"""
- if dry_run:
- log.info(
- "[dry-run] gravaria %s (LogsDirectory=molly-brown)",
- MOLLY_LOGS_DROPIN_PATH,
- )
+ if not MOLLY_LOGS_DROPIN_PATH.is_file():
return
- if MOLLY_LOGS_DROPIN_PATH.is_file() and not force:
- existing = MOLLY_LOGS_DROPIN_PATH.read_text(encoding="utf-8", errors="replace")
- if existing.strip() == MOLLY_LOGS_DROPIN_BODY.strip():
- log.debug("drop-in Molly logs já presente: %s", MOLLY_LOGS_DROPIN_PATH)
- return
- log.info("drop-in Molly logs existe com conteúdo diferente — use --force")
+ if dry_run:
+ log.info("[dry-run] removeria drop-in obsoleto: %s", MOLLY_LOGS_DROPIN_PATH)
return
- if MOLLY_LOGS_DROPIN_PATH.is_file() and force:
+ if force:
backup_if_exists(MOLLY_LOGS_DROPIN_PATH, log, dry_run=False)
- MOLLY_SYSTEMD_DROPIN_DIR.mkdir(parents=True, exist_ok=True)
- MOLLY_LOGS_DROPIN_PATH.write_text(MOLLY_LOGS_DROPIN_BODY, encoding="utf-8")
- os.chmod(MOLLY_LOGS_DROPIN_PATH, 0o644)
- log.info("systemd drop-in Molly: %s", MOLLY_LOGS_DROPIN_PATH)
+ MOLLY_LOGS_DROPIN_PATH.unlink()
+ log.info("removido drop-in obsoleto (logs em StateDirectory): %s", MOLLY_LOGS_DROPIN_PATH)
def ensure_molly_log_files(
@@ -228,28 +214,23 @@ def ensure_molly_log_files(
log: logging.Logger,
) -> tuple[Path, Path]:
"""
- Garante caminhos de log sob /var/log/molly-brown/ (criados vazios se faltarem).
+ Devolve caminhos AccessLog/ErrorLog sob StateDirectory (/var/lib/molly-brown).
- O dono correcto fica a cargo do systemd via drop-in LogsDirectory=molly-brown
- (DynamicUser no Debian). Molly-brown não aceita AccessLog/ErrorLog = \"-\"
- (interpreta como path /- e falha).
+ Não cria directório nem ficheiros: o unit Debian já define StateDirectory e o
+ systemd prepara /var/lib/molly-brown antes do ExecStart. Molly-brown não
+ aceita AccessLog/ErrorLog = \"-\" (interpreta como path /- e falha).
"""
access_p, error_p = molly_log_paths(instance)
if dry_run:
log.info(
- "[dry-run] criaria %s, %s, %s (dono ajustado pelo systemd no arranque)",
- MOLLY_LOG_DIR,
+ "[dry-run] AccessLog/ErrorLog seriam %s, %s (StateDirectory systemd)",
access_p,
error_p,
)
return access_p, error_p
- MOLLY_LOG_DIR.mkdir(parents=True, exist_ok=True)
- for p in (access_p, error_p):
- if not p.exists():
- p.touch(exist_ok=True)
log.info(
- "logs Molly: %s, %s (requer drop-in LogsDirectory; systemd ajusta dono)",
+ "logs Molly (StateDirectory): %s, %s",
access_p,
error_p,
)
@@ -682,7 +663,7 @@ def main(argv: list[str] | None = None) -> int:
key,
)
else:
- write_molly_brown_logs_dropin(
+ retire_molly_brown_logs_dropin(
dry_run=args.dry_run,
log=log,
force=args.force,
@@ -756,7 +737,13 @@ def main(argv: list[str] | None = None) -> int:
dry_run=args.dry_run,
log=log,
)
- wait_for_unit_active(molly_unit, log=log, dry_run=args.dry_run)
+ wait_for_unit_active(
+ molly_unit,
+ log=log,
+ dry_run=args.dry_run,
+ attempts=15,
+ delay_s=1.0,
+ )
validate_final(users, log)
log.info("Concluído.")
diff --git a/scripts/docs/alt_protocols.md b/scripts/docs/alt_protocols.md
@@ -26,17 +26,31 @@ Script em **`scripts/admin/setup_alt_protocols.py`**: instala e configura **goph
O **molly-brown** trata `AccessLog` e `ErrorLog` como **caminhos de ficheiro**. Valores como `"-"` (estilo «stdout» noutros programas) são interpretados de forma errada e o processo tenta abrir `/-`, falhando de imediato.
-- **Comportamento actual do script (v0.05+):** instala o drop-in systemd **`/etc/systemd/system/molly-brown@.service.d/50-runv-logs.conf`** com `LogsDirectory=molly-brown`, para o systemd criar/ajustar **`/var/log/molly-brown`** com o dono correcto em cada arranque (necessário porque o pacote Debian usa **`DynamicUser=yes`** — um `chown` baseado em `getpwnam` ou no nome `User=` **não** coincide com o UID dinâmico real). Cria também os ficheiros `runv.club-access.log` e `runv.club-error.log` se faltarem, e grava os caminhos absolutos em `/etc/molly-brown/runv.club.conf`.
-- **Servidor já provisionado com conf antiga:** o script só reescreve o `.conf` (e o drop-in, se já existir com outro conteúdo) se correr com **`--force`** (faz backup com timestamp onde aplicável). Exemplo:
+- **Comportamento actual do script (v0.06+):** grava `AccessLog` / `ErrorLog` em **`/var/lib/molly-brown/`** (`runv.club-access.log`, `runv.club-error.log`). Esse caminho coincide com **`StateDirectory=molly-brown`** do unit Debian: o systemd cria o directório com o dono correcto (**`DynamicUser=yes`**) **antes** do `ExecStart`, sem `chown` manual. **Não** pré-cria pastas nem ficheiros de log (evita conflitos com `LogsDirectory` em `/var/log`).
+- **Versões antigas (v0.05):** usavam o drop-in `50-runv-logs.conf` com `LogsDirectory=molly-brown`. Se `/var/log/molly-brown` já existia como root, o systemd podia **migrar** para `/var/log/private/molly-brown` e o serviço falhava. O **v0.06+** **remove** esse drop-in e muda os caminhos no `.conf` para `/var/lib/molly-brown/`.
+- **Servidor já provisionado:** correr com **`--force`** para regravar o `.conf` e remover o drop-in obsoleto (com backup do drop-in se usar `--force`). Exemplo:
`sudo python3 scripts/admin/setup_alt_protocols.py --verbose --force`
-- **Correcção manual rápida (só `.conf`):** editar `AccessLog` / `ErrorLog` para caminhos absolutos sob `/var/log/molly-brown/`; garantir o drop-in `LogsDirectory=molly-brown` como acima; `sudo systemctl daemon-reload`; `sudo systemctl reset-failed molly-brown@runv.club.service` e `sudo systemctl start molly-brown@runv.club.service`.
+- **Correcção manual rápida (só `.conf`):** `AccessLog` / `ErrorLog` com caminhos absolutos sob **`/var/lib/molly-brown/`**; **sem** `LogsDirectory` extra em drop-in; `sudo systemctl daemon-reload`; `sudo systemctl reset-failed molly-brown@runv.club.service` e `start`.
-## Erro `permission denied` em `/var/log/molly-brown/…-error.log`
+## Erro `permission denied` em `/var/log/molly-brown/…` ou migração para `/var/log/private/molly-brown`
-Aparece quando os ficheiros de log ficaram com dono **root** ou outro UID que **não** é o do processo molly-brown. No Debian, o unit **`molly-brown@.service`** usa **`DynamicUser=yes`**: o utilizador de runtime é gerido pelo systemd, por isso **`sudo chown molly-brown:molly-brown`** (utilizador estático em `/etc/passwd`, se existir) **não** resolve de forma fiável.
+No Debian, **`DynamicUser=yes`** faz o UID de runtime ser dinâmico; `chown` estático não bate. Se viu **`migrating to /var/log/private/molly-brown`** ou **`permission denied`** em `/var/log/molly-brown`, actualize para **v0.06+** com **`--force`**.
-- **Solução suportada:** o script **v0.05+** instala o drop-in com **`LogsDirectory=molly-brown`**; no arranque, o systemd corrige a propriedade de `/var/log/molly-brown`. Depois de actualizar o repo: `sudo python3 scripts/admin/setup_alt_protocols.py --verbose --force`, `sudo systemctl daemon-reload`, `sudo systemctl reset-failed molly-brown@runv.club.service`, `sudo systemctl start molly-brown@runv.club.service`.
-- **Verificação:** `systemctl cat molly-brown@runv.club.service` deve mostrar o fragmento `50-runv-logs.conf` com `LogsDirectory=molly-brown`.
+**Limpeza recomendada (serviço parado):**
+
+```bash
+sudo systemctl stop 'molly-brown@runv.club.service'
+sudo rm -f /etc/systemd/system/molly-brown@.service.d/50-runv-logs.conf
+sudo rm -rf /var/log/molly-brown /var/log/private/molly-brown
+sudo systemctl daemon-reload
+sudo python3 /opt/runv/src/scripts/admin/setup_alt_protocols.py --verbose --force
+```
+
+(ajuste o caminho do script ao seu clone). Os logs passam a ficar só em **`/var/lib/molly-brown/`** (legível com `sudo`).
+
+## Erro `permission denied` em `/var/lib/molly-brown/…`
+
+Raro se o `.conf` aponta para `/var/lib/molly-brown/` e não há override que desactive `StateDirectory`. Confirme `grep StateDirectory /lib/systemd/system/molly-brown@.service` e caminhos no `.conf`; veja também **TLS** (`privkey` legível pelo grupo `ssl-cert`).
## Checklist rápido (conf antiga, UFW, «activating»)
@@ -74,7 +88,7 @@ sudo python3 scripts/admin/setup_alt_protocols.py --verbose
|------|--------|
| `--dry-run` | Simula; não grava (validação de root ignorada em alguns passos só se documentado). |
| `--verbose` | Log detalhado. |
-| `--force` | Sobrescreve configs de sistema (com backup com timestamp) e ficheiros modelo no backfill. Necessário para **regravar** `/etc/molly-brown/runv.club.conf` ou o drop-in **`50-runv-logs.conf`** após correcções (ex. logs Molly / `DynamicUser`). |
+| `--force` | Sobrescreve configs de sistema (com backup com timestamp) e ficheiros modelo no backfill. Necessário para **regravar** `/etc/molly-brown/runv.club.conf` e remover o drop-in obsoleto **`50-runv-logs.conf`** (v0.05) ao migrar logs para `/var/lib/molly-brown/`. |
| `--skip-install` | Não corre `apt-get`. |
| `--skip-gopher` / `--skip-gemini` | Ignora pacote, config e serviço desse protocolo. |
| `--skip-firewall` | Não altera UFW. |