close_entre.py (4250B)
1 #!/usr/bin/env python3 2 """ 3 Script para fechar temporariamente os registros do terminal `entre`. 4 Ele edita o ficheiro de drop-in do SSH para usar `closed_app.py` em vez de `entre_app.py`. 5 6 Executar como root no servidor Debian. 7 """ 8 import os 9 import sys 10 import subprocess 11 import argparse 12 from pathlib import Path 13 14 ADMIN_DIR = Path(__file__).resolve().parent.parent / "scripts" / "admin" 15 if str(ADMIN_DIR) not in sys.path: 16 sys.path.insert(0, str(ADMIN_DIR)) 17 18 from admin_guard import ensure_admin_cli 19 20 def eprint(msg: str) -> None: 21 print(msg, file=sys.stderr) 22 23 def require_root() -> None: 24 if os.geteuid() != 0: 25 eprint("Execute como root (sudo).") 26 raise SystemExit(1) 27 28 def run(cmd: list[str]) -> None: 29 r = subprocess.run(cmd, capture_output=True, text=True, timeout=60) 30 if r.returncode != 0: 31 err = (r.stderr or r.stdout or "").strip() 32 raise RuntimeError(f"Falhou: {' '.join(cmd)}\n{err}") 33 34 def main() -> int: 35 parser = argparse.ArgumentParser( 36 description="Fecha temporariamente os registros do terminal entre.", 37 ) 38 parser.add_argument( 39 "--dry-run", 40 action="store_true", 41 help="mostra o que seria alterado sem gravar nem recarregar o SSH", 42 ) 43 parser.add_argument( 44 "--auth-mode", 45 default="empty-password", 46 help="opção compatível com setup_entre.py; mantida por simetria operacional", 47 ) 48 parser.add_argument( 49 "--install-pam-empty-password-rule", 50 action="store_true", 51 help="opção compatível com setup_entre.py; close_entre.py não altera PAM", 52 ) 53 parser.add_argument( 54 "--skip-pam-empty-password-rule", 55 action="store_true", 56 help="opção compatível com setup_entre.py; close_entre.py não altera PAM", 57 ) 58 args = parser.parse_args() 59 60 ensure_admin_cli( 61 script_name=Path(__file__).name, 62 dry_run=bool(args.dry_run), 63 ) 64 require_root() 65 66 dropin_path = Path("/etc/ssh/sshd_config.d/runv-entre.conf") 67 if not dropin_path.is_file(): 68 eprint(f"Erro: o ficheiro de configuração SSH {dropin_path} não foi encontrado.") 69 eprint("Parece que o setup do terminal `entre` ainda não foi executado ou está corrompido.") 70 return 1 71 72 content = dropin_path.read_text(encoding="utf-8") 73 74 if "closed_app.py" in content: 75 print("Os registros já parecem estar fechados (closed_app.py detetado na config).") 76 return 0 77 78 if "entre_app.py" not in content: 79 eprint("Aviso: 'entre_app.py' não encontrado na configuração. Modificação ignorada por segurança.") 80 return 1 81 82 # Substitui a app principal pela fechada 83 new_content = content.replace("entre_app.py", "closed_app.py") 84 85 if args.dry_run: 86 print(f"[dry-run] modificaria {dropin_path.name}: entre_app.py -> closed_app.py") 87 print("[dry-run] correria sshd -t e systemctl reload ssh") 88 return 0 89 90 # Grava novamente 91 dropin_path.write_text(new_content, encoding="utf-8") 92 print(f"Modificado {dropin_path.name}: entre_app.py -> closed_app.py") 93 94 # Testa as confiugrações do ssh 95 print("Testando a configuração com 'sshd -t'...") 96 try: 97 run(["sshd", "-t"]) 98 except RuntimeError as e: 99 # Tenta reverter 100 eprint(f"Erro de sshd detectado: {e}") 101 eprint("Revertendo as mudanças.") 102 dropin_path.write_text(content, encoding="utf-8") 103 return 1 104 105 print("sshd -t: OK.") 106 107 # Recarrega o SSH 108 try: 109 run(["systemctl", "reload", "ssh"]) 110 print("Serviço SSH recarregado (reload).") 111 except RuntimeError: 112 try: 113 run(["systemctl", "reload", "sshd"]) 114 print("Serviço SSH recarregado (reload).") 115 except RuntimeError as e: 116 eprint(f"Aviso: Não foi possível fazer o recarregamento do SSH de forma automática: {e}") 117 eprint("Por favor recarregue manualmente: systemctl reload ssh") 118 119 print("\n[+] Fechado com sucesso! Agora as ligações serão encaminhadas para o ecrã CLOSED.") 120 print("Para reabrir os registros no futuro, basta correr 'sudo ./setup_entre.py' novamente.") 121 122 return 0 123 124 if __name__ == "__main__": 125 sys.exit(main())