commit e203a8e4b3ffa2e7d104ef0683efc149eade640f
parent f2732bd78e052572dd2ad915070018980dd162cd
Author: Pablo Murad <pblmrd@gmail.com>
Date: Wed, 13 May 2026 22:08:22 -0300
Mariela Boca Murcha
Diffstat:
2 files changed, 36 insertions(+), 8 deletions(-)
diff --git a/scripts/admin/remove_runv_jails.py b/scripts/admin/remove_runv_jails.py
@@ -106,6 +106,7 @@ def main(argv: list[str] | None = None) -> int:
users = [args.user.strip()] if args.user else group_members()
users = [u for u in users if u and not runv_jail.jail_skip_username(u)]
+ failures = 0
if not users:
log.info("nenhum membro em %s", runv_jail.RUNV_JAILED_GROUP)
for username in users:
@@ -115,12 +116,17 @@ def main(argv: list[str] | None = None) -> int:
log.warning("%s não existe em passwd; ignorado", username)
continue
log.info("--- removendo jail de %s", username)
- runv_jail.teardown_runv_jail_for_user(
- username,
- Path(pw.pw_dir),
- log,
- dry_run=bool(args.dry_run),
- )
+ try:
+ runv_jail.teardown_runv_jail_for_user(
+ username,
+ Path(pw.pw_dir),
+ log,
+ dry_run=bool(args.dry_run),
+ )
+ except Exception as e:
+ failures += 1
+ log.error("%s: falha ao remover jail: %s", username, e)
+ continue
if not args.keep_sshd_dropin:
try:
@@ -129,6 +135,9 @@ def main(argv: list[str] | None = None) -> int:
log.error("%s", e)
return 1
+ if failures:
+ log.error("concluído com %d falha(s)", failures)
+ return 1
log.info("concluído")
return 0
diff --git a/scripts/admin/runv_jail.py b/scripts/admin/runv_jail.py
@@ -22,6 +22,18 @@ def _run(cmd: list[str], *, log: logging.Logger) -> subprocess.CompletedProcess[
return subprocess.run(cmd, capture_output=True, text=True, timeout=600)
+def is_mounted(path: Path, log: logging.Logger) -> bool:
+ """Detecta mountpoint usando findmnt quando disponível; fallback para os.path.ismount."""
+ p = str(path.resolve())
+ if shutil.which("findmnt") is not None:
+ r = _run(["findmnt", "-R", "--target", p], log=log)
+ if r.returncode == 0 and (r.stdout or "").strip():
+ return True
+ if r.returncode not in (0, 1):
+ log.debug("findmnt %s: %s", p, (r.stderr or r.stdout or "").strip())
+ return os.path.ismount(path)
+
+
def ensure_runv_jailed_group(log: logging.Logger) -> None:
r = _run(["groupadd", "-f", RUNV_JAILED_GROUP], log=log)
if r.returncode != 0:
@@ -146,13 +158,19 @@ def remove_user_from_jailed_group(username: str, log: logging.Logger) -> None:
def unbind_jail_home(jail_home: Path, log: logging.Logger) -> None:
"""Desmonta o bind em ``jail_home`` se estiver montado."""
- if not os.path.ismount(jail_home):
+ if not is_mounted(jail_home, log):
log.debug("jail: %s não está montado", jail_home)
return
r = _run(["umount", str(jail_home.resolve())], log=log)
if r.returncode != 0:
err = (r.stderr or r.stdout or "").strip()
- raise RuntimeError(f"umount {jail_home}: {err}")
+ log.warning("umount %s falhou (%s); tentando lazy umount", jail_home, err)
+ lazy = _run(["umount", "-l", str(jail_home.resolve())], log=log)
+ if lazy.returncode != 0:
+ lazy_err = (lazy.stderr or lazy.stdout or "").strip()
+ raise RuntimeError(f"umount {jail_home}: {err}; umount -l: {lazy_err}")
+ log.info("jail: lazy umount em %s", jail_home)
+ return
log.info("jail: desmontado bind em %s", jail_home)
@@ -274,6 +292,7 @@ def teardown_runv_jail_for_user(
remove_fstab_bind(real_home, jail_home, log)
remove_user_from_jailed_group(username, log)
if jail_root.is_dir():
+ unbind_jail_home(jail_home, log)
shutil.rmtree(jail_root, ignore_errors=False)
log.info("jail: removido %s", jail_root)
elif jail_root.exists():