Skip to content

Commit e4041e8

Browse files
committed
fix(ssh): disable strict host checking by default
1 parent 7178a6c commit e4041e8

11 files changed

Lines changed: 19 additions & 13 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
- **SSH_ASKPASS fallback** for OpenSSH ≥ 8.4: the helper script is created in `~/.cache/pcm/` (permissions `0700`, not in `/tmp`) and deleted after 5 seconds. The password is passed via environment variable only, never written to the file.
9393
- **Command injection protection**: all profile parameters (host, port, user, device, etc.) are sanitised with `shlex.quote()` before use in shell commands. Pre-commands run with `shell=False`.
9494
- **Protected credential files** (`connections.json`, `pcm_settings.json`, `audit_log.json`): written with permissions `0600` — readable only by the owner.
95-
- **SSH host key verification enabled by default**: new profiles use `StrictHostKeyChecking=yes`; this can be explicitly changed per profile. The SFTP browser uses Paramiko `RejectPolicy` with automatic `known_hosts` loading.
95+
- **SSH host key verification is opt-in**: new profiles leave strict host key checking disabled by default; it can be enabled globally or per profile when verification against `known_hosts` is desired. The SFTP browser uses Paramiko `RejectPolicy` when enabled.
9696
- **AES-128 encryption (Fernet + PBKDF2-SHA256, 480k iterations)**: usernames and passwords in `connections.json` encrypted with a master password. The key never touches the disk. The verification token uses a random canary to prevent offline dictionary attacks.
9797
- **Audit log with hash chaining**: each entry contains the SHA-256 of the previous one. PCM can detect entries whose chain was not recomputed; the local log is not an externally anchored, tamper-proof audit trail.
9898
- **KeePassXC integration** via Browser Protocol v2 (NaCl box): find and fill credentials directly from the open KeePassXC database — no browser needed.
@@ -555,7 +555,7 @@ If you find PCM useful and want to thank the developer, you can buy him a coffee
555555
- **Fallback SSH_ASKPASS** per OpenSSH ≥ 8.4: lo script helper è creato in `~/.cache/pcm/` (permessi `0700`, non in `/tmp`) ed eliminato dopo 5 secondi. La password è passata solo via variabile d'ambiente, mai scritta nel file.
556556
- **Protezione command injection**: tutti i parametri dei profili (host, porta, utente, device, ecc.) sono sanificati con `shlex.quote()` prima di essere usati nei comandi shell. Il pre-comando è eseguito con `shell=False`.
557557
- **File credenziali protetti** (`connections.json`, `pcm_settings.json`, `audit_log.json`): scritti con permessi `0600` — leggibili solo dal proprietario.
558-
- **Verifica host key SSH attiva**: `StrictHostKeyChecking=yes` su tutte le connessioni. Il browser SFTP usa `RejectPolicy` di paramiko con caricamento automatico di `known_hosts`.
558+
- **Verifica host key SSH opzionale**: nelle nuove connessioni `StrictHostKeyChecking` è disabilitato per impostazione predefinita; può essere abilitato globalmente o per singolo profilo per verificare gli host tramite `known_hosts`. Il browser SFTP usa `RejectPolicy` di paramiko quando l'opzione è attiva.
559559
- **Cifratura AES-128** (Fernet + PBKDF2-SHA256, 480k iterazioni): utenti e password in `connections.json` cifrati con password master. La chiave non tocca mai il disco. Il token di verifica usa un canary casuale per prevenire attacchi a dizionario offline.
560560
- **Audit log con hash chaining**: ogni voce include l'SHA-256 della voce precedente — le manomissioni sono rilevabili.
561561
- **KeePassXC integrato** via Browser Protocol v2 (NaCl box): cerca e compila credenziali direttamente dal database KeePassXC aperto — nessun browser necessario.

gtk3/PCM.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -880,7 +880,7 @@ def _start_ssh_gateway(self, dati: dict) -> tuple:
880880
return None, None
881881

882882
ssh_exe = shutil.which("ssh") or "ssh"
883-
strict_default = config_manager.load_settings().get("ssh", {}).get("strict_host_check", True)
883+
strict_default = config_manager.load_settings().get("ssh", {}).get("strict_host_check", False)
884884
strict = "yes" if dati.get("strict_host", strict_default) else "accept-new"
885885
cmd = [
886886
ssh_exe, "-N", "-T",

gtk3/config_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ def _create_default_sessions():
324324
},
325325
"ssh": {
326326
"keepalive_interval": 60,
327-
"strict_host_check": True,
327+
"strict_host_check": False,
328328
"default_sftp_browser": True,
329329
},
330330
"tunnels": [],

gtk3/pcm.1.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ enabled.
211211
- All profile parameters are sanitised with **shlex.quote()** before
212212
shell use. Pre-commands run with shell=False.
213213
- Credential files are written with permissions **0600**.
214-
- SSH connections use **StrictHostKeyChecking=yes**.
214+
- SSH host key checking is disabled by default and can be enabled globally or per profile.
215215
- Optional AES-128 encryption (Fernet + PBKDF2-SHA256, 480 k iterations)
216216
of usernames and passwords with a master password.
217217

gtk3/session_command.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,8 @@ def _esc(s: str) -> str:
523523

524524

525525
def _strict_host_check(profile: dict) -> bool:
526-
"""Use the global secure default when a profile has no explicit choice."""
527-
default = config_manager.load_settings().get("ssh", {}).get("strict_host_check", True)
526+
"""Use the global default when a profile has no explicit choice."""
527+
default = config_manager.load_settings().get("ssh", {}).get("strict_host_check", False)
528528
return profile.get("strict_host", default)
529529

530530

gtk3/session_dialog.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -691,7 +691,7 @@ def _build_tab_advanced(self) -> Gtk.Widget:
691691
self.chk_keepalive.set_tooltip_text(t("tt.ssh_ka"))
692692
self.chk_strict_host = _check(t("sd.ssh.strict"))
693693
self.chk_strict_host.set_active(
694-
config_manager.load_settings().get("ssh", {}).get("strict_host_check", True)
694+
config_manager.load_settings().get("ssh", {}).get("strict_host_check", False)
695695
)
696696
self.chk_strict_host.set_tooltip_text(t("tt.ssh_strict"))
697697
self.chk_agent_forward = _check(t("sd.ssh.agent_forward"))
@@ -1900,7 +1900,7 @@ def _popola(self, nome: str, dati: dict):
19001900
self.chk_compression.set_active(dati.get("compression", False))
19011901
self.chk_keepalive.set_active(dati.get("keepalive", False))
19021902
self.spin_keepalive_interval.set_value(int(dati.get("keepalive_interval", 60)))
1903-
strict_default = config_manager.load_settings().get("ssh", {}).get("strict_host_check", True)
1903+
strict_default = config_manager.load_settings().get("ssh", {}).get("strict_host_check", False)
19041904
self.chk_strict_host.set_active(dati.get("strict_host", strict_default))
19051905
self.chk_panel_sftp_side.set_active(dati.get("sftp_browser", True))
19061906
self.spin_panels_ssh_port.set_value(int(dati.get("mon_ssh_port", 22)))

gtk3/tests/test_config_manager.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def test_load_settings_returns_defaults(self, temp_files):
7171
assert "general" in s
7272
assert "terminal" in s
7373
assert s["general"]["language"] in ("it", "en")
74+
assert s["ssh"]["strict_host_check"] is False
7475

7576
def test_save_and_load_settings(self, temp_files):
7677
s = config_manager.load_settings()

gtk3/tests/test_session_command.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ def test_ssh_external(self):
2828
})
2929
assert mode == "ssh_term_ext"
3030

31+
def test_ssh_strict_host_check_is_disabled_by_default(self, monkeypatch):
32+
monkeypatch.setattr(config_manager, "load_settings", lambda: {"ssh": {}})
33+
cmd = session_command._build_ssh({"host": "example.com"})
34+
assert "-o StrictHostKeyChecking=accept-new" in cmd
35+
3136
def test_ssh_keepalive_interval_from_session_is_honored(self):
3237
"""Lo spinner 'keepalive_interval' nell'editor sessione (0 = disabilitato)
3338
deve incidere sul comando reale: prima veniva salvato nel profilo ma

gtk3/translations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@ def init_from_settings() -> None:
12821282
"tt.ssh_comp": {"it": "Abilita la compressione del traffico SSH. Utile su connessioni lente, controproducente su reti veloci", "en": "Enable SSH traffic compression. Useful on slow connections, counterproductive on fast networks", "de": "SSH-Verkehrskomprimierung aktivieren. Nützlich bei langsamen Verbindungen, kontraproduktiv bei schnellen Netzwerken", "fr": "Activer la compression du trafic SSH. Utile sur les connexions lentes, contre-productif sur les réseaux rapides", "es": "Habilitar compresión del tráfico SSH. Útil en conexiones lentas, contraproducente en redes rápidas"},
12831283
"tt.ssh_ka": {"it": "Invia pacchetti keepalive per mantenere attiva la connessione attraverso firewall e NAT", "en": "Sends keepalive packets to keep the connection alive through firewalls and NAT", "de": "Sendet Keepalive-Pakete, um die Verbindung durch Firewalls und NAT aufrechtzuerhalten", "fr": "Envoie des paquets keepalive pour maintenir la connexion active à travers les pare-feu et le NAT", "es": "Envía paquetes keepalive para mantener la conexión activa a través de firewalls y NAT"},
12841284
"tt.ssh_ka_int": {"it": "Intervallo in secondi tra i pacchetti keepalive. 0 = disabilitato. Valori tipici: 30-120 secondi", "en": "Interval in seconds between keepalive packets. 0 = disabled. Typical values: 30-120 seconds", "de": "Intervall in Sekunden zwischen Keepalive-Paketen. 0 = deaktiviert. Typische Werte: 30-120 Sekunden", "fr": "Intervalle en secondes entre les paquets keepalive. 0 = désactivé. Valeurs typiques : 30-120 secondes", "es": "Intervalo en segundos entre paquetes keepalive. 0 = desactivado. Valores típicos: 30-120 segundos"},
1285-
"tt.ssh_strict": {"it": "Verifica rigorosa della chiave host del server. Disabilitare solo in ambienti di test controllati", "en": "Strict server host key verification. Disable only in controlled test environments", "de": "Strikte Überprüfung des Server-Host-Schlüssels. Nur in kontrollierten Testumgebungen deaktivieren", "fr": "Vérification stricte de la clé d'hôte du serveur. Désactiver uniquement dans des environnements de test contrôlés", "es": "Verificación estricta de la clave de host del servidor. Deshabilitar solo en entornos de prueba controlados"},
1285+
"tt.ssh_strict": {"it": "Verifica rigorosa della chiave host del server. È disabilitata per impostazione predefinita; abilitala se vuoi verificare gli host tramite known_hosts", "en": "Strict server host key verification. Disabled by default; enable it to verify hosts against known_hosts", "de": "Strikte Überprüfung des Server-Host-Schlüssels. Standardmäßig deaktiviert; aktivieren, um Hosts gegen known_hosts zu prüfen", "fr": "Vérification stricte de la clé d'hôte du serveur. Désactivée par défaut ; activez-la pour vérifier les hôtes via known_hosts", "es": "Verificación estricta de la clave de host del servidor. Deshabilitada de forma predeterminada; actívela para verificar los hosts mediante known_hosts"},
12861286
"tt.ssh_sftp_br": {"it": "Apre automaticamente il browser SFTP laterale quando si connette a questo host via SSH", "en": "Automatically opens the lateral SFTP browser when connecting to this host via SSH", "de": "Öffnet automatisch den seitlichen SFTP-Browser beim Verbinden mit diesem Host über SSH", "fr": "Ouvre automatiquement le navigateur SFTP latéral lors de la connexion à cet hôte via SSH", "es": "Abre automáticamente el navegador SFTP lateral al conectarse a este host mediante SSH"},
12871287
"tt.ssh_startup": {"it": "Comando da eseguire automaticamente all'apertura della sessione SSH. Es: htop, sudo -i, screen -r", "en": "Command to run automatically when the SSH session opens. E.g.: htop, sudo -i, screen -r", "de": "Befehl, der beim Öffnen der SSH-Sitzung automatisch ausgeführt wird. Z.B.: htop, sudo -i, screen -r", "fr": "Commande à exécuter automatiquement à l'ouverture de la session SSH. Ex : htop, sudo -i, screen -r", "es": "Comando a ejecutar automáticamente al abrir la sesión SSH. Ej: htop, sudo -i, screen -r"},
12881288
"tt.jump_host": {"it": "Host intermedio (bastion/jump server) attraverso cui raggiungere il server finale. Es: bastion.example.com", "en": "Intermediate host (bastion/jump server) through which to reach the final server. E.g.: bastion.example.com", "de": "Zwischenhost (Bastion/Jump-Server) zum Erreichen des Zielservers. Z.B.: bastion.example.com", "fr": "Hôte intermédiaire (bastion/jump server) par lequel atteindre le serveur final. Ex : bastion.example.com", "es": "Host intermedio (bastión/jump server) a través del cual llegar al servidor final. Ej: bastion.example.com"},

gtk3/tunnel_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ def _build_cmd(t: dict) -> list:
576576
cmd = [
577577
"ssh", "-N",
578578
"-p", sport,
579-
"-o", "StrictHostKeyChecking=yes",
579+
"-o", "StrictHostKeyChecking=accept-new",
580580
"-o", "ConnectTimeout=10",
581581
"-o", "ServerAliveInterval=60"
582582
]

0 commit comments

Comments
 (0)