runv-server

server tooling for runv.club
Log | Files | Refs | README

commit 467d74597314f600d4768644914a561b3dfd1f94
parent 02586fc84e1a4c13408396ac48808aa676baeddd
Author: Pablo Murad <pablo@pablomurad.com>
Date:   Sat, 21 Mar 2026 15:49:28 -0300

fix

Diffstat:
Mscripts/admin/setup_alt_protocols.py | 79+++++++++++++++++++++++++++++++++----------------------------------------------
Mscripts/docs/alt_protocols.md | 30++++++++++++++++++++++--------
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. |