Files
pyssh-client/README.md
Florian 6cceb3f3b2 README : couvrir les 14 phases precedentes, mettre en avant la confidentialite
Le README ne decrivait encore que les toutes premieres fonctionnalites
(sessions, tunnels -L/-R, SFTP, terminaux locaux, cles, known_hosts) : rien
sur ~/.ssh/config, ProxyJump, le SOCKS dynamique, le keepalive/reconnexion
automatique, le terminal divise, les workspaces, les snippets, les tags/
groupes, le coffre-fort d'identifiants ni l'assistant IA.

Reecriture complete en gardant le ton et la structure d'origine (description
concrete, raccourcis clavier, "pourquoi" plutot que juste "quoi") :
  - Nouveau chapeau explicite : aucun compte cloud, aucune telemetrie, les
    identifiants restent locaux, l'IA est entierement optionnelle.
  - Fonctionnalites reorganisees par theme (terminal / connexion et auth /
    organisation / SFTP / cles et secrets / assistant IA), chaque nouvelle
    fonctionnalite decrite avec le menu ou bouton exact pour y acceder -
    verifie contre le code plutot que suppose.
  - "Structure du projet" reecrite pour refleter l'arborescence actuelle
    (controllers/, ui/, services/, storage/, security/, ai/, openssh/...).
  - "Tests" et nouvelle section "Qualite" mis a jour pour pytest/ruff/pyright/
    CI (Phases 13-14), en gardant la possibilite de lancer un script seul.
  - "Limites connues" mise a jour avec les limites reelles actuelles
    (ProxyJump a un seul rebond, coffre-fort Windows uniquement, socle de
    typage pyright non resorbe) plutot que celles, resolues depuis, de la
    version initiale.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 07:50:24 +02:00

299 lines
19 KiB
Markdown

# PySSH Client
Client SSH/SFTP personnel (type MobaXterm/Termius/Bitvise), **100% local**.
**Aucun compte requis, aucun service cloud, aucune telemetrie.** Sessions, cles, mots de passe
memorises et snippets restent dans des fichiers locaux (ou le trousseau securise de Windows) —
rien n'est jamais envoye a un serveur tiers. La seule fonctionnalite qui communique vers
l'exterieur est l'**assistant IA**, entierement optionnel, desactive par defaut, et qui n'envoie
quoi que ce soit qu'au fournisseur que *vous* configurez avec *votre propre* cle API (voir plus bas).
## Fonctionnalites
### Terminal SSH
- Sessions SSH multiples dans des onglets, avec terminal ANSI colore et trois themes
- Copier-coller **a la souris, comme dans un terminal Unix** : selectionner copie aussitot
(double-clic pour un mot), et le clic droit colle directement — ni menu, ni raccourci.
Le clic molette colle aussi. `Ctrl+Shift+C` / `Ctrl+Shift+V` / `Maj+Inser` restent
disponibles au clavier, et `Ctrl+C` reste l'interruption comme dans tout terminal
- Confort d'affichage : zoom `Ctrl+molette` ou `Ctrl+ +/-` (`Ctrl+0` remet a zero, la taille est
memorisee), barre de defilement de l'historique, `Maj+PagePrec/Suiv`, `Ctrl+Fin` pour revenir
en direct, et recherche dans l'historique avec `Ctrl+F`
- Onglets renommables (double-clic, ou clic droit) et deplaçables ; le clic droit permet
aussi de les diviser en **2 ou 4 panneaux** (cote a cote, empiles, ou grille), de deplacer un
autre onglet ouvert dans un panneau, ou de fermer les autres onglets
- **Keepalive et reconnexion automatique** configurables par session (intervalle, nombre de
tentatives, delai avec recul progressif) : une coupure reseau n'oblige plus a rouvrir
la session et ressaisir les identifiants
- Journalisation de session dans un fichier (bouton "Journal"), sequences ANSI retirees pour
rester lisible
- Reconnexion manuelle en un clic (bouton dans la barre de l'onglet)
### Connexion et authentification
- **Authentification automatique** (mode par defaut) : comme la commande `ssh`, l'application
utilise l'agent SSH et les cles de `~/.ssh` sans que vous ayez a designer quoi que ce soit, et
ne demande un mot de passe qu'en dernier recours
- **Import de votre `~/.ssh/config`** : les `Host` deja definis (HostName, User, Port,
IdentityFile, ProxyJump, ForwardAgent...) apparaissent directement dans le panneau lateral
(section "⚙ Configuration OpenSSH"), utilisables sans les ressaisir, ou importables comme
session PySSH en un clic
- **Bastion / ProxyJump** : une session peut passer par un rebond SSH, choisi parmi vos sessions
existantes, un `Host` de `~/.ssh/config`, ou une configuration manuelle. Un groupe de sessions
peut aussi heriter d'un bastion commun (voir Groupes ci-dessous)
- **Tunnels SSH** (bouton "🔀 Tunnels" dans la barre d'un onglet SSH) : redirection locale
(`-L`, atteindre depuis votre poste un service qui n'ecoute que sur la boucle locale du
serveur), distante (`-R`), et **proxy SOCKS dynamique** (`-D`, pour faire transiter tout le
trafic d'une application par le tunnel)
- Verification des empreintes de cles d'hote (TOFU) avant premiere connexion, comme un client
SSH classique. Les hotes deja approuves par votre commande `ssh` (`~/.ssh/known_hosts`) sont
reconnus : pas de double confirmation d'empreinte
- Gestionnaire des hotes connus (menu "Hotes connus") : si une adresse en DHCP change de
machine, la cle d'hote presentee ne correspond plus a celle enregistree — l'application le
detecte et propose de remplacer l'ancienne entree directement au moment de la connexion
### Organisation
- Carnet d'adresses de serveurs (JSON local, `data/sessions.json`) organise par dossiers,
connexion en un clic
- **Tags** (plusieurs par session) avec filtre/recherche dans le panneau lateral
- **Groupes** : un dossier de sessions peut porter des parametres par defaut (utilisateur, port,
cle SSH, bastion, keepalive) via clic droit > "Parametres du groupe...". Le bouton
"↓ Heriter du groupe" du formulaire de session les reprend en pre-remplissage, sans empecher
de les modifier au cas par cas pour une session donnee
- **Workspaces** : sauvegardent l'etat complet d'une disposition de travail — onglets ouverts,
ordre, divisions et tailles de panneaux, sessions associees — et la restaurent en un clic
(menu "Workspaces", `Ctrl+Shift+W`). Une case optionnelle restaure le dernier workspace au
demarrage. **Aucun mot de passe n'est jamais stocke dans un workspace**
- **Recherche rapide** (`Ctrl+K`) : palette façon lanceur d'applications, recherche floue parmi
sessions, hotes OpenSSH, workspaces, snippets et actions, entierement au clavier
- **Snippets** : commandes enregistrees par categorie (des exemples Linux/Docker/Proxmox sont
fournis au premier lancement), gerables (creer/modifier/supprimer/rechercher) depuis le menu
"Snippets" ou le bouton "📋 Snippets" de chaque onglet. Un snippet peut etre **insere** (le
texte est ecrit, jamais valide automatiquement), **execute**, ou **execute sur tous les
onglets a la fois** via Multi-Send
- **Envoi simultane** : le bouton "⇉ Multi" du panneau lateral diffuse ce que vous tapez vers
tous les onglets terminal ouverts (y compris dans les panneaux divises), pour piloter
plusieurs serveurs d'un coup
### SFTP
- Client SFTP integre facon WinSCP : double panneau local/distant avec colonnes triables (nom,
taille, date, droits), glisser-deposer entre les panneaux, renommage (F2), suppression
(Suppr), creation de dossier, permissions distantes (chmod), et file de transferts avec
progression — le tout en arriere-plan, sans jamais geler l'interface
- **Aucun favori n'est necessaire pour l'utiliser** — trois façons d'y arriver :
- *Vous avez tape `ssh user@hote` dans un terminal* : bouton "📁 SFTP sur cette connexion"
dans la barre de l'onglet. L'hote et l'utilisateur sont deduits de la commande tapee
- *Connexion ponctuelle* : bouton "📁 SFTP" du panneau lateral, ou `Ctrl+Shift+F`
- *Session enregistree* : le panneau de fichiers est deja affiche a cote du terminal des
l'ouverture, comme dans MobaXterm ; ou clic droit > "Ouvrir en SFTP" pour un onglet dedie
### Cles et secrets
- Gestion des cles SSH : generation **Ed25519, ECDSA (courbes NIST P-256/P-384/P-521) et RSA**,
import de cles existantes, export, protection par passphrase. Sous Windows, les cles privees
generees ou importees reçoivent des permissions NTFS restreintes (ACL), equivalent du
`chmod 600` Unix
- **Coffre-fort de mots de passe optionnel** : a la creation d'une session par mot de passe, une
case "Memoriser de maniere securisee" permet de ne plus le ressaisir a chaque connexion. Le
mot de passe est alors stocke dans le **Gestionnaire d'identifiants Windows** (chiffre par
DPAPI), **jamais en clair dans un fichier de PySSH**. Sans cette case cochee (comportement par
defaut), rien n'est memorise : le mot de passe est redemande a chaque connexion, comme
aujourd'hui
### Assistant IA (optionnel, desactive par defaut)
Une aide ponctuelle pour l'administration systeme, pas un pilote automatique : **aucune commande
n'est jamais executee automatiquement**.
- **Desactive par defaut**, et sans le moindre fournisseur impose : compatible avec toute API de
type OpenAI (OpenAI, OpenRouter, un proxy interne, ou un modele local exposant la meme API) —
vous indiquez l'URL et le modele, il n'y a pas de liste figee. Se configure depuis le menu
"Assistant IA > Parametres...", avec un bouton "Tester la connexion"
- Depuis un onglet terminal, le bouton "🤖 Assistant IA" permet, sur la selection courante :
**Expliquer cette sortie**, **Diagnostiquer cette erreur**, **Proposer une solution**, ou
**Poser une question** en y ajoutant la selection comme contexte. Les reponses s'affichent
dans un panneau lateral (conversation en memoire uniquement, jamais journalisee ni ecrite sur
le disque)
- **Generer une commande** (a partir d'une description en langage naturel, sans selection
necessaire) affiche toujours la commande proposee avant tout usage, avec trois choix : la
**copier**, l'**inserer dans le terminal** (le texte est ecrit, jamais valide automatiquement),
ou **annuler**. Une commande correspondant a un motif reconnu comme destructeur (`rm -rf`,
`mkfs`, `dd`, un `DROP DATABASE`...) est signalee par un avertissement explicite
- **Avant tout envoi** au fournisseur configure : un avis de principe s'affiche une seule fois,
et chaque envoi est verifie localement a la recherche de secrets evidents (mot de passe, cle
privee, jeton d'authentification) — si quelque chose est detecte, vous choisissez de le
masquer automatiquement, de l'envoyer tel quel, ou d'annuler l'envoi
- **La cle API** passe exclusivement par le meme coffre-fort securise que les mots de passe de
session (Gestionnaire d'identifiants Windows) : jamais dans `settings.json`, jamais journalisee
## Installation (Windows)
Necessite Python 3.10+ installe sur le poste (verifiable avec `py -3 --version`).
```bash
setup.bat
```
Ce script cree un environnement virtuel local (`venv/`) et y installe les dependances (PySide6,
paramiko, pyte, cryptography, pywinpty) — rien n'est installe globalement sur le poste.
Pour contribuer au code (tests, lint, verification de types), installez plutot les dependances
de developpement :
```bash
venv\Scripts\pip install -r requirements-dev.txt
```
## Lancement
Trois facons, au choix :
- **Raccourci Windows** (recommande) : double-cliquez sur `PySSH Client.lnk` a la racine du projet.
Il lance l'application via `pythonw.exe`, donc **sans fenetre de console noire**. Vous pouvez le
copier sur le Bureau, ou l'epingler a la barre des taches.
Pour le (re)creer — apres un deplacement du projet, ou sur un autre poste :
```bash
powershell -ExecutionPolicy Bypass -File tools\create_shortcut.ps1 -Desktop
```
(`-Desktop` et `-StartMenu` sont optionnels et ajoutent une copie a ces emplacements.)
- **En ligne de commande**, sans console residuelle :
```bash
venv\Scripts\pythonw.exe main.py
```
- **`run.bat`** : garde une fenetre de console ouverte, ce qui est pratique pour voir les erreurs
si l'application refuse de demarrer.
## Version portable (sans Python installe)
```bash
build_exe.bat
```
Produit un dossier autonome `dist\PySSH Client\` contenant l'executable et tout ce dont il a besoin,
interpreteur Python compris. Copiez ce dossier ou vous voulez (y compris sur une cle USB) : il
fonctionne sur n'importe quel poste Windows, **meme sans Python installe**.
Le format retenu est un dossier plutot qu'un fichier `.exe` unique : avec PySide6, un executable
unique doit decompresser environ 200 Mo a chaque lancement, ce qui ajoute plusieurs secondes
d'attente a chaque demarrage. Le dossier demarre immediatement.
> **Note pour qui modifie la recette de build** (`PySSHClient.spec`) : `pywinpty` a besoin de deux
> executables auxiliaires, `OpenConsole.exe` (backend ConPTY) et `winpty-agent.exe` (repli WinPTY).
> PyInstaller ne suit que les DLL liees et les oublie : ils sont donc ajoutes explicitement dans
> `datas`. Sans eux, l'application se lance normalement mais **tous les terminaux se ferment
> instantanement** ("Deconnecte (Terminal ferme)").
**Ou sont stockees les donnees ?** A cote de l'executable, dans son propre sous-dossier `data\`
(mode portable : vos sessions et cles suivent la cle USB). Si cet emplacement n'est pas accessible
en ecriture — installation dans `Program Files`, support en lecture seule — l'application bascule
automatiquement sur `%APPDATA%\PySSHClient\`.
## Premiers pas
1. **Terminal local immediat** : bouton "💻 Terminal" dans le panneau lateral (ou `Ctrl+Shift+T`), ou double-clic sur "PowerShell"/"Invite de commandes" dans la section "💻 Terminaux locaux" de l'arborescence — aucune session a creer.
2. **Nouvelle session SSH** (bouton "+ Session" ou `Ctrl+N`) : renseignez l'hote, le port, l'utilisateur et le mode d'authentification. Vous avez deja un `~/.ssh/config` ? Ses `Host` apparaissent tout seuls dans la section "⚙ Configuration OpenSSH", inutile de les ressaisir.
3. **Cles SSH** (menu "Cles SSH > Gestionnaire de cles") : generez une nouvelle paire de cles ou importez-en une existante, puis selectionnez-la dans le formulaire de session.
4. Double-cliquez sur une session dans l'arborescence pour ouvrir un onglet terminal SSH.
5. **SFTP — trois facons d'y arriver**, selon la situation :
- *Vous avez tape `ssh user@hote` dans un terminal* : bouton **"📁 SFTP sur cette connexion"** dans la barre de
l'onglet. L'hote et l'utilisateur sont deduits de la commande, le formulaire est pre-rempli. Rien a enregistrer.
- *Connexion ponctuelle* : bouton **"📁 SFTP"** du panneau lateral, ou `Ctrl+Shift+F`. La case "Enregistrer dans mes
sessions" est decochee par defaut.
- *Session enregistree* : clic droit > **"Ouvrir en SFTP"** (onglet dedie). Dans une session SSH ouverte par
l'application, le panneau de fichiers est deja affiche a cote du terminal, comme dans MobaXterm.
Dans tous les cas : glisser-deposer entre les deux panneaux, F2 renomme, Suppr supprime, F5 actualise, et le clic
droit sur le panneau distant donne acces aux permissions (chmod).
6. **Aller plus vite** : `Ctrl+K` ouvre une recherche rapide parmi tout ce que vous avez deja ouvert ou enregistre.
7. **Retrouver une disposition de travail** : une fois plusieurs onglets/panneaux organises a votre gout, menu
"Workspaces > Gestionnaire de workspaces..." (`Ctrl+Shift+W`) pour l'enregistrer et la rouvrir en un clic.
8. **Assistant IA** (facultatif) : menu "Assistant IA > Parametres..." pour indiquer l'URL, le modele et la cle API
d'un fournisseur compatible OpenAI. Tant que cette etape n'est pas faite, l'assistant reste inactif et
n'envoie jamais rien nulle part.
## Structure du projet
```
main.py point d'entree
app/
main_window.py fenetre principale : assemble panneau lateral, onglets, menus
session_manager.py base de sessions (JSON), tags, parametres de bastion/keepalive/reconnexion
key_manager.py generation/import/export de cles SSH, empreintes
known_hosts_manager.py gestion du fichier known_hosts (lister/retirer/remplacer une cle d'hote)
ssh_connection.py connexion SSH en arriere-plan (paramiko + QThread), bastion, keepalive
local_terminal.py terminal local Windows (PowerShell/cmd via pywinpty)
terminal_widget.py emulateur de terminal ANSI (pyte + QPainter)
sftp_panel.py client SFTP a deux panneaux, transferts en arriere-plan
tunnels.py tunnels SSH : redirection -L / -R et proxy SOCKS dynamique -D
bastion.py resolution d'un ProxyJump (session / Host OpenSSH / manuel)
credentials.py coffre-fort optionnel (Gestionnaire d'identifiants Windows)
workspace.py, snippets.py, persistance des workspaces, snippets et parametres de groupe
groups.py, app_settings.py
theme.py feuille de style "bleu nuit" + icone d'application
ai/ assistant IA : fournisseur, redaction de secrets, detection de
commandes dangereuses, parametres, execution en arriere-plan
controllers/ logique d'assemblage : cycle de vie des onglets, panneau des
sessions, diffusion Multi-Send, restauration des workspaces
ui/ widgets d'onglet (SSH/local/SFTP), terminal divise, panneau IA
dialogs/ boites de dialogue (session, cles, hotes, tunnels, snippets,
workspaces, groupes, recherche rapide, parametres IA...)
openssh/ lecture de ~/.ssh/config
security/ protection des cles privees (ACL NTFS sous Windows)
storage/ ecriture atomique de fichiers JSON, migration de schema
services/ logique pure sans dependance Qt (detection ~cible SSH, recherche
floue, calcul du delai de reconnexion)
resources/
theme.qss feuille de style Qt "bleu nuit"
data/
sessions.json, keys.json, vos donnees locales (non versionnees) - voir .gitignore
workspaces.json, snippets.json,
groups.json, known_hosts, keys/
tests/ suite de tests (voir "Tests" ci-dessous)
```
## Tests
```bash
pytest
```
(necessite `pip install -r requirements-dev.txt`). Chaque fichier `tests/test_*.py` reste aussi
executable individuellement (`python tests/test_xxx.py`), pratique pour rejouer un seul scenario
pendant le developpement.
Les tests construisent de vraies fenetres Qt sans les afficher (`QT_QPA_PLATFORM=offscreen`) et
couvrent, entre autres : le copier-coller et les raccourcis du terminal, la detection de l'hote
dans une commande `ssh` tapee a la main, la logique SFTP (transferts, renommage, tri, suppression
recursive) avec un serveur simule, le stockage atomique et la migration de schema, la lecture de
`~/.ssh/config`, la resolution de ProxyJump, le coffre-fort d'identifiants, la detection de
secrets et de commandes dangereuses, et l'ensemble de l'assistant IA (fournisseur HTTP, panneau,
integration dans les onglets) — sans jamais faire d'appel reseau reel.
## Qualite
```bash
ruff check app main.py tests # style et erreurs courantes
pyright # verification de types (mode progressif, voir pyproject.toml)
```
Une CI Gitea Actions (`.gitea/workflows/ci.yml`) execute le lint et les tests a chaque push, et
construit la version portable sur la branche principale.
## Limites connues / pistes d'amelioration
- L'emulation de terminal couvre les usages courants (bash, vim, htop...) mais n'est pas un xterm parfaitement complet.
- Le terminal local repose sur `pywinpty` (pseudo-console Windows). Si un onglet local reste bloque sur un ecran vide, tapez une touche : le rafraichissement de la console Windows peut etre paresseux dans certains contextes (notamment les sessions Windows non interactives) et se declenche a la premiere frappe.
- Le SFTP ne propose pas encore l'edition de fichier distant en place (telecharger / modifier / renvoyer a la main).
- Le ProxyJump ne gere qu'un seul rebond pour l'instant (pas de chainage de plusieurs bastions).
- Le coffre-fort d'identifiants (mots de passe de session, cle API IA) repose sur le Gestionnaire d'identifiants Windows : pas d'equivalent sur Linux/macOS pour l'instant, l'application fonctionne mais sans memorisation securisee sur ces systemes.
- L'application est concue et testee pour Windows uniquement (pywinpty, ACL NTFS, Gestionnaire d'identifiants).
- La verification de types (`pyright`) est volontairement en mode progressif : un socle de signalements existe encore dans `app/ui/tabs.py` et `app/ssh_connection.py` (voir `pyproject.toml`), a resorber au fil du temps plutot que d'un bloc.