#!/bin/bash # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 function display_help() { uecho "$scriptname: se connecter par ssh à un ou plusieurs hôtes USAGE $scriptname host [options] $scriptname @hostsfile [options] $scriptname -R hosts... OPTIONS hosts @hostsfile Spécifier un ou plusieurs hôtes distants sur lequels faire la connexion. Pour spécifier plusieurs hôtes, il faut les séparer par un espace ou le caractère ':', e.g. 'host1 host2' ou 'host1:host2'. Si la spécification contient les caractères { et }, l'expansion est effectuée, e.g 'root@{host1,host2}.univ.run' La forme @hostsfile permet de lire la liste des hôtes depuis le fichier hostsfile, à raison d'un hôte par ligne. [options] Les arguments et options de ssh doivent se trouver *APRES* host, pour simplifier leur analyse. Les options suivantes sont reconnues même quand elle sont spécifiées avant le premier argument (les options longues ne sont reconnues qu'avant le premier argument): -q, --quiet Mode non verbeux -t, --tty Forcer l'allocation d'un TTY -l, --login USER Spécifier le user avec lequel se connecter -p, --port PORT Spécifier le port sur lequel se connecter -o OPTION Ajouter une option de SSH Les options suivantes *doivent* se trouver *AVANT* le premier argument: -d, --domain DOMAIN Spécifier un domaine par défaut pour les hôtes qui sont spécifiés sans domaine. -S, --ssh SSH Spécifier l'exécutable à utiliser pour lancer ssh. -R, --remove Lancer 'ssh-keygen -R' pour chacun des hôtes spécifiés. Par exemple: $scriptname -R host.tld est équivalent à: ssh-keygen -R host.tld ssh-keygen -R host ssh-keygen -R 10.10.1.5 si l'adresse ip de host.tld est 10.10.1.5 Quand cette option est spécifié, l'option -c est reconnue et permet de se reconnecter à l'hôte juste après avoir nettoyé les clés." } source "$(dirname "$0")/ulib/ulib" || exit 1 urequire DEFAULTS set_defaults nutools __PARSED_HOSTS=() __PARSED_FILES=() function parse_hostsfile() { # Lire chacun des fichiers $* et initialiser __PARSED_HOSTS avec la liste # des hôtes mentionnés dans les fichiers. local inputfile basedir inputs input for inputfile in "$@"; do inputfile="$(abspath "$inputfile")" array_contains __PARSED_FILES "$inputfile" && { ewarn "$(ppath "$inputfile"): inclusion récursive" continue } array_add __PARSED_FILES "$inputfile" basedir="$(dirname "$inputfile")" array_from_lines inputs "$(<"$inputfile" filter_conf)" || { ewarn "$inputfile: fichier ingnoré" continue } for input in "${inputs[@]}"; do if [ "${input#@}" != "$input" ]; then # fichier inclus parse_hostsfile "$(abspath "${input#@}" "$basedir")" else array_addu __PARSED_HOSTS "$input" fi done done } function __expand_braces() { if [[ "$1" == *{* ]] && [[ "$1" == *}* ]]; then eval "echo $1" else echo "$1" fi } function __dot_is_localhost() { [ "$1" == "." ] && echo "localhost" || echo "$1"; } function fix_hosts() { # Si hosts contient des éléments multiple, comme a:b, séparer ces # éléments. i.e (a b:c "d e") --> (a b c d e) # Supporter la syntaxe @hostsfile qui permet de charger la liste des hôtes # depuis un fichier. # Remplacer aussi les '.' par 'localhost' array_map hosts __expand_braces array_fix_paths hosts ":" array_fix_paths hosts " " local -a _hosts _tmphosts host for host in "${hosts[@]}"; do host="${host%/}" if [ "${host#@}" != "$host" ]; then __PARSED_HOSTS=() parse_hostsfile "${host#@}" array_fix_paths __PARSED_HOSTS array_extendu _hosts __PARSED_HOSTS else array_addu _hosts "$host" fi done array_copy hosts _hosts array_map hosts __dot_is_localhost } function ussh() { args=(${quiet:+-q} ${tty:+-t} ${login:+-l "$login"} ${port:+-p "$port"}) for option in "${options[@]}"; do args=("${args[@]}" -o "$option") done args=("${args[@]}" "$@") [ -n "$ssh" ] || ssh=ssh [ "${#hosts[*]}" -gt 1 ] && showtitle=1 || showtitle= for host in "${hosts[@]}"; do splituserhost "$host" user host [[ "$host" == *.* ]] || host="$host${domain:+.$domain}" [ -n "$showtitle" ] && etitle "${user:+$user@}$host" "$ssh" "${user:+$user@}$host" "${args[@]}" [ -n "$showtitle" ] && eend done } function remove_key() { estep "$1" ssh-keygen -R "$1" >&/dev/null } function remove_keys() { local -a __hosts; array_copy __hosts hosts local -a allhosts hosts ips; local host user ip array_copy allhosts __hosts etitle "Suppression des entrées dans ~/.ssh/known_hosts" for host in "${allhosts[@]}"; do splituserhost "$host" user host [[ "$host" == *.* ]] || host="$host${domain:+.$domain}" if ipcalc_checkip "$host" >/dev/null; then ip="$host" remove_key "$ip" resolv_hosts hosts "$ip" for host in "${hosts[@]}"; do remove_key "$host" hostname="${host%%.*}" [ "$hostname" != "$host" ] && remove_key "$hostname" done else remove_key "$host" hostname="${host%%.*}" [ "$hostname" != "$host" ] && remove_key "$hostname" resolv_ips ips "$host" for ip in "${ips[@]}"; do remove_key "$ip" done fi done eend } action=ssh quiet= tty= login="$USSH_USER" port= options=() domain="$USSH_DOMAIN" ssh= connect_after_remove= parse_opts + "${PRETTYOPTS[@]}" \ --help '$exit_with display_help' \ -q,--quiet quiet=1 \ -t,--tty tty=1 \ -l:,--login: login= \ -p:,--port: port= \ -o: options \ -d:,--domain: domain= \ -S:,--ssh ssh= \ -R,--remove action=remove \ -c,--connect connect_after_remove=1 \ @ args -- "$@" && set -- "${args[@]}" || die "$args" if [ "$action" == "ssh" ]; then [ -n "$1" ] || die "Vous devez spécifier l'hôte sur lequel faire la connexion" hosts=("$1"); shift fix_hosts ussh "$@" elif [ "$action" == "remove" ]; then urequire ipcalc [ -n "$1" ] || die "Vous devez spécifier les hôtes à supprimer de ~/.ssh/known_hosts" hosts=("$@") fix_hosts remove_keys [ -n "$connect_after_remove" ] && ussh fi