Gitfed
bastien-mrq/sssh/ Commits/ a0dc420

feat: chaînes ProxyJump multi-sauts

resolveJump remonte les jumps de host en host (-J hop1,hop2,…), passe les specs brutes telles quelles et coupe les cycles. Doc bastion/ProxyJump dans le README. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

bastien-mrq 2026-09-04 08:29 commit a0dc4209ac89e84d54f487414911e97594b0e214 parent b49f10caaf52b557414b4cf360092fe5c36301dc
4 files changed +38 −3
A .DS_Store Binary file — not shown.
M README.md +15 −0
M internal/.DS_Store Binary file — not shown.
M main.go +23 −3
.DS_Store
diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..347212b Binary files /dev/null and b/.DS_Store differ
README.md
diff --git a/README.md b/README.md index 499cfb1..a36e499 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,21 @@ tags = ["client", "prod"] # optionnel C'est un simple fichier : pour synchroniser deux machines, copie-le, ou versionne `~/.config/sssh/` dans un repo git. +### Bastion / ProxyJump + +Le champ `jump` (option `-J` de `sssh add`, champ « Jump host » du formulaire) accepte : + +- le **nom d'un autre host sssh** : sa cible réelle (user, IP, port) est utilisée, et si ce host a lui-même un `jump`, la chaîne complète est construite (`ssh -J hop1,hop2 cible`) ; +- une **spécification ssh brute** `[user@]host[:port]`, passée telle quelle. + +```sh +sssh add bastion admin@203.0.113.10 +sssh add serveur-prive deploy@10.0.4.2 -J bastion +sssh serveur-prive # → ssh -J admin@203.0.113.10 deploy@10.0.4.2 +``` + +Limite d'OpenSSH : `-J` ne prend pas de clé par hop ; si un bastion demande une clé spécifique, déclare-la dans `~/.ssh/config` (`Host bastion` + `IdentityFile`). + ### Importer depuis `~/.ssh/config` `sssh import-ssh` liste les alias concrets de ton `~/.ssh/config` qui ne sont pas encore dans sssh et te laisse cocher ceux à importer (espace pour sélectionner, `ctrl+a` pour tout prendre). Les champs supportés sont copiés : `HostName`, `User`, `Port`, `IdentityFile`, `ProxyJump`. Avec des noms en arguments, l'import est direct, sans interaction. La touche `i` dans le TUI fait la même chose.
internal/.DS_Store
diff --git a/internal/.DS_Store b/internal/.DS_Store index 2247c85..5e1c830 100644 Binary files a/internal/.DS_Store and b/internal/.DS_Store differ
main.go
diff --git a/main.go b/main.go index 77f0606..297c0f8 100644 --- a/main.go +++ b/main.go @@ -90,14 +90,34 @@ func sshConfigCandidates(st *store.Store) []host.Host { return out } -// resolveJump remplace un jump qui référence un host sssh par sa cible réelle. +// resolveJump construit la chaîne ProxyJump complète. Un jump qui référence +// un host sssh est remplacé par sa cible réelle, et si ce host a lui-même +// un jump, la chaîne remonte (-J hop1,hop2,…) jusqu'au premier hop. +// Un jump inconnu de sssh est passé tel quel à ssh ([user@]host[:port]). func resolveJump(st *store.Store, h *host.Host) { if h.Jump == "" { return } - if j, ok := st.Get(h.Jump); ok { - h.Jump = j.JumpSpec() + var chain []string + seen := map[string]bool{h.Name: true} + jump := h.Jump + for { + if seen[jump] { + break // cycle dans les jumps : on coupe la chaîne là + } + j, ok := st.Get(jump) + if !ok { + chain = append([]string{jump}, chain...) + break + } + seen[jump] = true + chain = append([]string{j.JumpSpec()}, chain...) + if j.Jump == "" { + break + } + jump = j.Jump } + h.Jump = strings.Join(chain, ",") } func cmdConnect(name string, extra []string) error {