Fixed behaviour when using ssh config alias

This commit is contained in:
2026-05-02 13:06:11 +03:00
parent bd31e95992
commit 9e3a061ddd
2 changed files with 138 additions and 16 deletions

View File

@@ -1,4 +1,5 @@
# sshukh zsh plugin
*SSH Update Known Hosts*
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:
**With Oh-my-zsh:** run the following command:
```
$ git clone https://github.com/anatolykopyl/sshukh.git $HOME/.oh-my-zsh/custom/plugins/sshukh
```
**With Antigen:** add the following to your `.zshrc`:
```
antigen bundle anatolykopyl/sshukh
```
To always use with ssh (recommended) add this alias to your `.zshrc`:
```
alias ssh='sshukh'
```
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
```
## Usage
Let's connect to an ip that changed hosts:
```
$ sshukh pi@192.168.1.54
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@ -45,4 +54,5 @@ Update known_hosts? [y/n] y
/Users/akopyl/.ssh/known_hosts updated.
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

@@ -2,7 +2,7 @@
# Description
# -----------
#
# User will be prompted if they want to update known_hosts if ssh errors out
# User will be prompted if they want to update known_hosts if ssh errors out
# with "Host key verification failed."
#
# ------------------------------------------------------------------------------
@@ -13,18 +13,130 @@
#
# ------------------------------------------------------------------------------
sshukh () {
output=$(\ssh "$@" 2>&1 | tee /dev/tty)
error=$(echo $output | tail -1)
if [[ "$error" == "Host key verification failed."* ]]; then
host=$(cut -d'@' -f2 <<< $1)
while true; do
read yn"?Update known_hosts? [y/n] "
case $yn in
[Yy]* ) ssh-keygen -R $host && \ssh "$@"; break;;
[Nn]* ) break;;
* ) echo "Please answer y or n.";;
esac
done
fi
# 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 () {
local output error host
output=$(\ssh "$@" 2>&1 | tee /dev/tty)
error=$(print -r -- "$output" | tail -n1)
if [[ "$error" != *"Host key verification failed"* ]]; then
return 0
fi
while true; do
read yn"?Update known_hosts? [y/n] "
case $yn in
[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;;
* ) echo "Please answer y or n.";;
esac
done
}