Compare commits

..

2 Commits

Author SHA1 Message Date
Anatoly Kopyl
1cd49812e6 Merge pull request #2 from anatolykopyl/ssh-config-fix
Fixed behaviour when using ssh config alias
2026-05-02 14:12:11 +03:00
9e3a061ddd Fixed behaviour when using ssh config alias 2026-05-02 13:06:11 +03:00
2 changed files with 138 additions and 16 deletions

View File

@@ -1,4 +1,5 @@
# sshukh zsh plugin # sshukh zsh plugin
*SSH Update Known Hosts* *SSH Update Known Hosts*
Prompts to update `known_hosts` file for you if needed. Prompts to update `known_hosts` file for you if needed.
@@ -6,25 +7,33 @@ Prompts to update `known_hosts` file for you if needed.
## Installation: ## Installation:
**With Oh-my-zsh:** run the following command: **With Oh-my-zsh:** run the following command:
``` ```
$ git clone https://github.com/anatolykopyl/sshukh.git $HOME/.oh-my-zsh/custom/plugins/sshukh $ git clone https://github.com/anatolykopyl/sshukh.git $HOME/.oh-my-zsh/custom/plugins/sshukh
``` ```
**With Antigen:** add the following to your `.zshrc`: **With Antigen:** add the following to your `.zshrc`:
``` ```
antigen bundle anatolykopyl/sshukh antigen bundle anatolykopyl/sshukh
``` ```
To always use with ssh (recommended) add this alias to your `.zshrc`: To always use with ssh (recommended) add this alias to your `.zshrc`:
``` ```
alias ssh='sshukh' alias ssh='sshukh'
``` ```
Zsh may tell you that the plugin is disabled until permissions are fixed. Just run the command it suggests. Zsh may tell you that the plugin is disabled until permissions are fixed. Just run the command it suggests.
``` ```
$ compaudit | xargs chmod g-w,o-w $ compaudit | xargs chmod g-w,o-w
``` ```
## Usage ## Usage
Let's connect to an ip that changed hosts: Let's connect to an ip that changed hosts:
``` ```
$ sshukh pi@192.168.1.54 $ sshukh pi@192.168.1.54
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@ -45,4 +54,5 @@ Update known_hosts? [y/n] y
/Users/akopyl/.ssh/known_hosts updated. /Users/akopyl/.ssh/known_hosts updated.
Original contents retained as /Users/akopyl/.ssh/known_hosts.old Original contents retained as /Users/akopyl/.ssh/known_hosts.old
``` ```
There is a prompt asking if you want to remove the conflicting host from `known_hosts` and upon answering `y` ssh reruns automatically successfully. There is a prompt asking if you want to remove the conflicting host from `known_hosts` and upon answering `y` ssh reruns automatically successfully.

View File

@@ -13,18 +13,130 @@
# #
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# OpenSSH prints "<keytype> host key for <host> has changed." — use that host
# for ssh-keygen -R (correct for SSH config Host aliases that resolve to IPs).
_sshukh_offending_host_from_output() {
print -r -- "$1" | command grep -E -m1 ' host key for .+ has changed' | command sed -E 's/.* host key for ([^ ]+) has changed.*/\1/'
}
# Walk ssh argv like ssh(1): options first, then destination [command ...].
# Sets _sshukh_prefix (options only) and _sshukh_dest (user@host or alias).
_sshukh_split_ssh_args() {
local i a
local -a args
args=("$@")
_sshukh_prefix=()
_sshukh_dest=""
i=1
while (( i <= $#args )); do
a="${args[i]}"
case $a in
--)
_sshukh_prefix+=("$a")
(( i++ ))
(( i <= $#args )) || return 1
_sshukh_dest="${args[i]}"
return 0
;;
-[46AaCfGgKkMNnqsTtVvXxYy])
_sshukh_prefix+=("$a")
;;
-v*)
_sshukh_prefix+=("$a")
;;
-B|-b|-c|-E|-e|-F|-I|-i|-J|-L|-l|-m|-O|-P|-p|-Q|-R|-S|-W|-w)
_sshukh_prefix+=("$a")
(( i++ ))
(( i <= $#args )) || return 1
_sshukh_prefix+=("${args[i]}")
;;
-D)
_sshukh_prefix+=("$a")
(( i++ ))
(( i <= $#args )) || return 1
_sshukh_prefix+=("${args[i]}")
;;
-D*|-B*|-b*|-c*|-E*|-e*|-F*|-I*|-i*|-J*|-L*|-l*|-m*|-O*|-P*|-p*|-Q*|-R*|-S*|-W*|-w*)
_sshukh_prefix+=("$a")
;;
-o)
_sshukh_prefix+=("$a")
(( i++ ))
(( i <= $#args )) || return 1
_sshukh_prefix+=("${args[i]}")
;;
-o*)
_sshukh_prefix+=("$a")
;;
-*)
_sshukh_prefix+=("$a")
;;
*)
_sshukh_dest="$a"
return 0
;;
esac
(( i++ ))
done
return 1
}
# Hostnames to try with ssh-keygen -R: offending line from ssh, then config
# alias / canonical names from ssh -G (covers known_hosts under IP vs name).
_sshukh_hosts_for_keygen() {
local output h stripped dest canon hostpat gout
output="$1"
shift
typeset -U hosts
hosts=()
h=$(_sshukh_offending_host_from_output "$output")
[[ -n "$h" ]] && hosts+=("$h")
if _sshukh_split_ssh_args "$@"; then
dest="$_sshukh_dest"
stripped="${dest#*@}"
[[ "$stripped" == "$dest" ]] && stripped="$dest"
[[ -n "$stripped" ]] && hosts+=("$stripped")
gout=$(\ssh -G "${_sshukh_prefix[@]}" "$dest" 2>/dev/null) || gout=""
if [[ -n "$gout" ]]; then
canon=$(print -r -- "$gout" | command awk '/^hostname / { print $2; exit }')
hostpat=$(print -r -- "$gout" | command awk '/^host / { print $2; exit }')
[[ -n "$canon" ]] && hosts+=("$canon")
[[ -n "$hostpat" ]] && hosts+=("$hostpat")
fi
fi
print -rl -- "${hosts[@]}"
}
sshukh () { sshukh () {
local output error host
output=$(\ssh "$@" 2>&1 | tee /dev/tty) output=$(\ssh "$@" 2>&1 | tee /dev/tty)
error=$(echo $output | tail -1) error=$(print -r -- "$output" | tail -n1)
if [[ "$error" == "Host key verification failed."* ]]; then
host=$(cut -d'@' -f2 <<< $1) if [[ "$error" != *"Host key verification failed"* ]]; then
return 0
fi
while true; do while true; do
read yn"?Update known_hosts? [y/n] " read yn"?Update known_hosts? [y/n] "
case $yn in case $yn in
[Yy]* ) ssh-keygen -R $host && \ssh "$@"; break;; [Yy]* )
while IFS= read -r host; do
[[ -z "$host" ]] && continue
\ssh-keygen -R "$host" 2>/dev/null
done < <(_sshukh_hosts_for_keygen "$output" "$@")
\ssh "$@"
break
;;
[Nn]* ) break;; [Nn]* ) break;;
* ) echo "Please answer y or n.";; * ) echo "Please answer y or n.";;
esac esac
done done
fi
} }