onboard: le chiavi dei server in known_hosts, e verifica dice perche' non entri

Marco Laterza, amministratore, si e' visto dire "produzione: non entri
(normale se non hai il ruolo di rilascio...)" con la chiave gia' sul
server: al suo PC mancava la chiave host della macchina nuova, e con
BatchMode=yes ssh rifiuta un host sconosciuto senza provare la chiave.

configura.sh scrive in ~/.ssh/known_hosts le chiavi di server.known_hosts,
lette sulle tre macchine l'08/10/2026, togliendo prima quelle vecchie per
gli stessi nomi. verifica.sh legge l'errore di ssh: giallo solo se il
server rifiuta la chiave personale, rosso con il rimedio per chiave host
mancante o cambiata e per il blocco dopo troppi tentativi.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Fabio
2026-10-08 10:47:10 +02:00
co-authored by Claude Opus 5.5
parent 2a4dbf3ad2
commit b83fc9883d
4 changed files with 79 additions and 18 deletions
+9 -2
View File
@@ -119,7 +119,8 @@ spiegala in una riga la prima volta.
dal campo di testo della scheda `AWS > Nome Cognome`; crea l'accesso a
Gitea, configura git, genera la chiave SSH e la carica sul suo profilo
Gitea (da lì la prende l'amministratore: **non va mandata a nessuno**),
scrive i nomi dei server in `~/.ssh/config`. Gitea viene prima apposta: è lui a dire chi è la persona. Se sul PC
scrive i nomi dei server in `~/.ssh/config` e le loro chiavi in
`~/.ssh/known_hosts` (da `server.known_hosts`). Gitea viene prima apposta: è lui a dire chi è la persona. Se sul PC
c'è già una chiave AWS **di un altro** (rimasta da chi lo usava, o copiata
da un collega), lo script lo dice e chiede la sua: dille di avvisare
l'amministratore, perché quella chiave va disattivata. Una chiave altrui
@@ -158,4 +159,10 @@ Dillo alla persona, invece di farle scoprire un rosso inspiegabile:
`~/.ssh/config` ci sono `oto-production` (la macchina nuova, dietro la VPN
come staging, utente `admin.produzione`) e `oto-production-old` (quella
dove girano ancora i siti). Chi ha il ruolo di rilascio entra in tutte e
due; per gli altri sono gialle tutte e due, ed è normale.
due; per gli altri sono gialle tutte e due, ed è normale. Giallo vuol
dire solo "il server non accetta la tua chiave": se una riga è rossa il
ruolo non c'entra, e `verifica.sh` dice cosa fare (di solito rilanciare
`configura.sh`, che registra le chiavi dei server). Se ssh chiede *Are you
sure you want to continue connecting?*, non far rispondere `yes` alla
cieca: le chiavi giuste sono in `server.known_hosts`, e le registra
`configura.sh`.
+18 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash
#
# PREPARA IL PC: accesso Gitea, accesso AWS, git, chiave SSH, nomi dei server.
# PREPARA IL PC: accesso Gitea, accesso AWS, git, chiave SSH, nomi e chiavi dei server.
#
# ./configura.sh
#
@@ -229,6 +229,23 @@ BLOCCO="$BLOCCO$FINE_BLOCCO"
&& mv "$CONFIG.nuovo" "$CONFIG" && chmod 600 "$CONFIG"
ok "ssh $STAGING_NOME, ssh $PRODUZIONE_NOME e ssh $PRODUZIONE_VECCHIA_NOME: configurati"
# --------------------------------------------------------------------------
titolo "5. Chiavi dei server in ~/.ssh/known_hosts"
# --------------------------------------------------------------------------
# Quelle di server.known_hosts, lette sulle macchine: cosi' il primo
# collegamento non chiede "Are you sure you want to continue connecting?",
# e la risposta non dipende da chi e' in mezzo alla rete quel giorno. Quelle
# che c'erano per gli stessi nomi si tolgono prima (ssh-keygen ne lascia una
# copia in known_hosts.old): una vecchia rimasta li' farebbe dire a ssh che
# il server e' cambiato.
NOTI="$HOME/.ssh/known_hosts"
touch "$NOTI" && chmod 600 "$NOTI"
for H in $(grep -v '^#' server.known_hosts | awk 'NF {print $1}' | sort -u) ; do
ssh-keygen -R "$H" -f "$NOTI" > /dev/null 2>&1
done
grep -v '^#' server.known_hosts | awk 'NF' >> "$NOTI"
ok "chiavi di $STAGING_NOME, $PRODUZIONE_NOME e $PRODUZIONE_VECCHIA_NOME registrate"
# --------------------------------------------------------------------------
titolo "Fatto"
# --------------------------------------------------------------------------
+22
View File
@@ -0,0 +1,22 @@
# Le chiavi dei tre server, lette sulle macchine stesse (/etc/ssh/ssh_host_*_key.pub)
# l'08/10/2026. configura.sh le scrive in ~/.ssh/known_hosts: senza, il primo
# collegamento chiede "Are you sure you want to continue connecting?" e
# verifica.sh, che non puo' rispondere, dice "non entri" anche a chi la chiave
# sul server ce l'ha. Se un server viene reinstallato, qui vanno quelle nuove.
#
# Il nome e' lo HostName di comune.sh, non il nome corto: e' quello che ssh cerca.
# oto-staging
10.110.185.10 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEdXcmBdVpVzOeRBU7qG22eKxeqrupuhc/vQhlz3hATP
10.110.185.10 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBD6WAf8go/g6PPmAoNTbz53Khg/wFa3da6BPwnJj/bALJ0QBsmd0Dx7f446oQEia7CMGbAAKRjRdNgbSOaI3LNc=
10.110.185.10 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC5j0lk5bMjFDBzqzsh20sF2C/h8kkmekxI/QxsZxSlWT1WFnHCNa3GC0umSQmPKjoZF639dZUDsbE/3UbrwJICD03arQGlClEu/i7ct6eWPhPT71vtH0BAUcjYKWK9dst5dAG4cd1mGP2hCtYTGMXo7KnzPprUTHVytf3KOcRydwlaRchFhxXnzPBVaaIAAvF7++ZNq0krRvw3X5G1tOPCSViiR1w7jmeX/UiDaS/4avJ4TXzqDQG56C1VG/iAxjeImJLQxQAkQMz+J6RG+g7kDGlSpiO8COFLyqgfgDjVV2ZUDqgZdrVb6HOwnIx2eqRCvsRA1G7HHuYp1tECZEkUToGx0P4D9ee38FwKXJ34cslOtz494ZCKF9vvd99602WKQxudNZxKv6/gXdVeBS2UjLVCYfCX+aSvOGJBCheC8sy4dDTeeO9keGV/KfIHRQy27Lgr7tswBabASjTHiOFZvbAeETWi9wtwb0Eyg3PYZ3KAnc+zNx5maqeLnJxQ+9M=
# oto-production
10.110.137.20 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAx5QWSl3YsMGEerqfYRhrXSy5PIdiHQW5rf/yiiLZhv
10.110.137.20 ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBB41d5/XnLsNI4dYfx9IRnSOsFnazb6EvsLRKCotxSgXmrp7Ds+6cnoSB141+Tgj9xNA6NrfI3XuCfLQw1ZQYXw=
10.110.137.20 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCb1sVTJkOoPfUQvZv2TNRNXr180OYO+Ua2yLeDFRpqxevgGiQz7amDcrW1M2ouokkI8YTDBc6bzMXLrWRRI34gTZHe9jYp9kEs+G+1XZnsd2fVoKY7Ix7dV33IRPb4qXb9KT4k9XgnygCRSrjhQNtjO6aFy0IfGWZF75jxDeRs3AWs0bllrJ1TMX91sCxlaYvUkehDqkavocpl4praiQDXnwdDFKiqGfQAFhcTLVfBetNIBx+T1Fvu/8FA24rogVcECkqdxO8PKpEA90rwQt0KwzIqHVcrbxP9vKlddX37spf4wSeJMo/T7w7iU5b1q5PKhxgWxHCRP9Hf09laYdDAiwZs1F9Sv6KWbVGKzhkQvVZF1nzWfPPo9UeyRDRpF5TORi0eeXUem2LprG/msJG1AJX7K0GN4QyuozzQqQr5NKFY7JjlxWS5Q+pZKCvJdaJU6Tvtc8oE8qtIerjdnCnz9eE3bfjPnMwmsPh8dSyGSK97g49Z/W8YWnug96CYCnU=
# oto-production-old
vps-production.oto-farm.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKjqHPAj0yN1PdWbIvJPAc1i/M2fGE6LH9W0ggNOqUda
vps-production.oto-farm.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKCfOvVGQPwctSwrTEPalC8fdhAawKVOlv/uIfIljZTCxX25+u7vw2+NlYhq7ETrSQvLAzaf3cOlbu2/qXUB3OA=
vps-production.oto-farm.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDtikmWxIRAi73fvYXxyqji84qkSt3G2Psqe+ynnwe0iQ6bvf0elYU4HOIdta7MtJjAiRHeXkH7hvlAorXL9+nagzA+2AVs2YM0rF4mTQO6thpUOPh0U7kHAj7WAJMjogJBJ3wA9A1N1VMHdOPc1trK7OTGJ6BCFKVjglsaBh2n/v5Hl4wpTORTpXNbwpxBfr9TtXtqYb/csSURi2LZEewkLZFrWkKyhHWTmiSSHl8QEjgf+/hoafXeAJ7bxv0lZ2h5QGuyV6KN3A9lQ4omFE+2ug+pqQH31Xn934W5Bwt6QI/yjOjcwn4yMKr2wfQRk85qkIESovmH3I2g8ivY8cB3y358cBsz1LMQ2+NJlzbvVrqDAFLPAgBPNeU442bosvX92gIgXWRxhwHjoCc1/BaoyNidPw7aQ6teDb0HicwKfXAAaS9pamhMjdqz4uGiC0iiPbhrxUX/UOviTh8Oy4AniHFxSj3Afs1k5JBNUotXc2o2ZOsuj3Sy4XCUTZtj6LE=
+30 -15
View File
@@ -68,33 +68,48 @@ porta_aperta () {
kill "$sveglia" 2> /dev/null
return "$esito"
}
# Un tentativo, e il motivo se non si entra: "non entri" da solo faceva
# pensare ai ruoli anche quando mancava la chiave del server su questo PC.
# Esce 0 se si entra, 1 se e' la chiave tua, 2 se e' altro (scritto in MOTIVO).
entra () {
local errore
errore="$(ssh -o BatchMode=yes -o ConnectTimeout=8 "$1" true 2>&1 > /dev/null)" && return 0
case "$errore" in
*"Permission denied"*) return 1 ;;
*"Host key verification failed"*|*"IDENTIFICATION HAS CHANGED"*)
MOTIVO="questo PC non ha la chiave giusta del server: rilancia ./configura.sh, che la registra" ;;
*"Connection closed"*|*"Connection reset"*)
MOTIVO="il server ha chiuso la connessione: dopo troppi tentativi sbagliati la chiude per qualche minuto, aspetta e rilancia" ;;
*) MOTIVO="ssh dice: $(printf '%s\n' "$errore" | grep -v '^\*\*' | tail -1)" ;;
esac
return 2
}
if porta_aperta "$STAGING_HOST" 22 ; then
ok "VPN attiva (staging raggiungibile)"
if ssh -o BatchMode=yes -o ConnectTimeout=8 "$STAGING_NOME" true 2> /dev/null ; then
ok "entri su staging ($STAGING_NOME)"
else
rosso "staging non accetta la tua chiave: l'amministratore deve ancora aggiungerla"
fi
entra "$STAGING_NOME" ; ESITO=$?
if [ "$ESITO" = "0" ] ; then ok "entri su staging ($STAGING_NOME)"
elif [ "$ESITO" = "1" ] ; then rosso "staging non accetta la tua chiave: l'amministratore deve ancora aggiungerla"
else rosso "staging ($STAGING_NOME): $MOTIVO" ; fi
else
rosso "staging non raggiungibile: la VPN e' accesa?"
fi
# Le produzioni sono due finche' i siti non sono migrati tutti. La nuova sta
# dietro la stessa VPN di staging. Un tentativo solo per macchina: la nuova
# chiude le porte per un po' a chi sbaglia il login piu' volte di fila.
# Solo il rifiuto della chiave dipende dal ruolo: il resto si sistema da qui.
NON_AUTORIZZATO="il server non accetta la tua chiave (normale se non hai il ruolo di rilascio; se ce l'hai, chiedi a chi amministra di aggiungerla)"
if porta_aperta "$PRODUZIONE_HOST" 22 ; then
if ssh -o BatchMode=yes -o ConnectTimeout=8 "$PRODUZIONE_NOME" true 2> /dev/null ; then
ok "entri in produzione ($PRODUZIONE_NOME)"
else
attento "produzione ($PRODUZIONE_NOME): non entri (normale se non hai il ruolo di rilascio, o se la tua chiave non e' ancora stata aggiunta)"
fi
entra "$PRODUZIONE_NOME" ; ESITO=$?
if [ "$ESITO" = "0" ] ; then ok "entri in produzione ($PRODUZIONE_NOME)"
elif [ "$ESITO" = "1" ] ; then attento "produzione ($PRODUZIONE_NOME): $NON_AUTORIZZATO"
else rosso "produzione ($PRODUZIONE_NOME): $MOTIVO" ; fi
else
attento "produzione ($PRODUZIONE_NOME): non raggiungibile (sta dietro la VPN, come staging)"
fi
if ssh -o BatchMode=yes -o ConnectTimeout=8 "$PRODUZIONE_VECCHIA_NOME" true 2> /dev/null ; then
ok "entri nella produzione vecchia ($PRODUZIONE_VECCHIA_NOME)"
else
attento "produzione vecchia ($PRODUZIONE_VECCHIA_NOME): non entri (normale se non hai il ruolo di rilascio, o se la tua chiave non e' ancora stata aggiunta)"
fi
entra "$PRODUZIONE_VECCHIA_NOME" ; ESITO=$?
if [ "$ESITO" = "0" ] ; then ok "entri nella produzione vecchia ($PRODUZIONE_VECCHIA_NOME)"
elif [ "$ESITO" = "1" ] ; then attento "produzione vecchia ($PRODUZIONE_VECCHIA_NOME): $NON_AUTORIZZATO"
else rosso "produzione vecchia ($PRODUZIONE_VECCHIA_NOME): $MOTIVO" ; fi
titolo "Risultato"
if [ "$ROSSI" = "0" ] ; then