sshrc, utiliser vos fichiers de configuration dans des sessions SSH

sshrc apporte une réponse à 2 problématiques :

  • Vous souhaitez pouvoir utiliser votre configuration locale aux petits oignons (alias, fonctions, variables d’environnement, bashrc, vimrc, inputrc, screenrc, etc.) lorsque vous êtes connectés sur les pc/serveurs que vous administrez. Cependant vous désirez ne pas toucher aux fichiers de configuration de ces pc/serveurs car vous ne pouvez pas (contexte professionnel) ou vous ne voulez pas. De plus plusieurs personnes se connectent au(x) même(s) compte(s), il faut respecter l’usage de chacun et ne pas imposer vos fichiers de configuration aux autres
  • Au contraire vous avez décidé d’utiliser vos fichiers de configuration (aussi appelés dotfiles) mais passer sur X pc/serveurs pour modifier un de vos fichiers de configuration ça vous gonfle et vous trouvez la gestion de configuration (Ansible, Puppet, Git + liens symboliques…) trop lourde pour si peu

Alors sshrc est fait pour vous. Sur votre poste vous allez créer un fichier ~/.sshrc dans lequel vous allez exporter/sourcer votre configuration personnalisée et un dossier ~./sshrc.d où vous allez placer vos fichiers de configuration (.bashrc, .vimrc, .inputrc, etc.). Au lieu de vous connecter sur ces pc/serveurs avec la commande ssh, vous allez vous connecter avec la commande sshrc (no stress c’est propre). Un dossier /tmp/.cascador.sshrc.XXXX unique (XXXX généré aléatoirement) sera créé sur l’hôte distant, votre configuration personnalisée sera envoyée dedans (avec openssl) puis chargée. A la déconnexion ce dossier sera supprimé.

sshrc est un script shell sous licence MIT aisément maintenable, compréhensible et lisible. Il nécessite comme prérequis openssl sur l’hôte local et distant. Il existe également moshrc pour Mosh. Voici le code source.

#!/usr/bin/env bash
function sshrc() {
    local SSHHOME=${SSHHOME:=~}
    if [ -f $SSHHOME/.sshrc ]; then
        local files=.sshrc
        if [ -d $SSHHOME/.sshrc.d ]; then
            files="$files .sshrc.d"
        fi
        SIZE=$(tar cfz - -h -C $SSHHOME $files | wc -c)
        if [ $SIZE -gt 65536 ]; then
            echo >&2 $'.sshrc.d and .sshrc files must be less than 64kb\ncurrent size: '$SIZE' bytes'
            exit 1
        fi
        if [ -z "$CMDARG" -a ! -e ~/.sshrc.d/.hushlogin ]; then
            WELCOME_MSG="
                if [ ! -e ~/.hushlogin ]; then
                    if [ -e /etc/motd ]; then cat /etc/motd; fi
                    if [ -e /etc/update-motd.d ]; then run-parts /etc/update-motd.d/ 2>/dev/null; fi
                    last -F \$USER 2>/dev/null | grep -v 'still logged in' | head -n1 | awk '{print \"Last login:\",\$4,\$5,\$6,\$7,\$8,\"from\",\$3;}'
                fi
                "
        else
            WELCOME_MSG=""
        fi
        ssh -t "$DOMAIN" $SSHARGS "
            command -v openssl >/dev/null 2>&1 || { echo >&2 \"sshrc requires openssl to be installed on the server, but it's not. Aborting.\"; exit 1; }
            $WELCOME_MSG
            export SSHHOME=\$(mktemp -d -t .$(whoami).sshrc.XXXX)
            export SSHRCCLEANUP=\$SSHHOME
            trap \"rm -rf \$SSHRCCLEANUP; exit\" 0
            echo $'"$(cat "$0" | openssl enc -base64)"' | tr -s ' ' $'\n' | openssl enc -base64 -d > \$SSHHOME/sshrc
            chmod +x \$SSHHOME/sshrc

            echo $'"$( cat << 'EOF' | openssl enc -base64
                if [ -r /etc/profile ]; then source /etc/profile; fi
                if [ -r ~/.bash_profile ]; then source ~/.bash_profile
                elif [ -r ~/.bash_login ]; then source ~/.bash_login
                elif [ -r ~/.profile ]; then source ~/.profile
                fi
                export PATH=$PATH:$SSHHOME
                source $SSHHOME/.sshrc;
EOF
                )"' | tr -s ' ' $'\n' | openssl enc -base64 -d > \$SSHHOME/sshrc.bashrc

            echo $'"$( cat << 'EOF' | openssl enc -base64
#!/usr/bin/env bash
                exec bash --rcfile <(echo '
                [ -r /etc/profile ] && source /etc/profile
                if [ -r ~/.bash_profile ]; then source ~/.bash_profile
                elif [ -r ~/.bash_login ]; then source ~/.bash_login
                elif [ -r ~/.profile ]; then source ~/.profile
                fi
                source '$SSHHOME'/.sshrc;
                export PATH=$PATH:'$SSHHOME'
                ') "$@"
EOF
                )"' | tr -s ' ' $'\n' | openssl enc -base64 -d > \$SSHHOME/bashsshrc
            chmod +x \$SSHHOME/bashsshrc

            echo $'"$(tar czf - -h -C $SSHHOME $files | openssl enc -base64)"' | tr -s ' ' $'\n' | openssl enc -base64 -d | tar mxzf - -C \$SSHHOME
            export SSHHOME=\$SSHHOME
            echo \"$CMDARG\" >> \$SSHHOME/sshrc.bashrc
            bash --rcfile \$SSHHOME/sshrc.bashrc
            "
    else
        echo "No such file: $SSHHOME/.sshrc" >&2
        exit 1
    fi
}

function sshrc_parse() {
  while [[ -n $1 ]]; do
    case $1 in
      -b | -c | -D | -E | -e | -F | -I | -i | -L | -l | -m | -O | -o | -p | -Q | -R | -S | -W | -w )
        SSHARGS="$SSHARGS $1 $2"; shift ;;
      -* )
        SSHARGS="$SSHARGS $1" ;;
      *)
        if [ -z "$DOMAIN" ]; then
         DOMAIN="$1"
        else
          local SEMICOLON=$([[ "$@" = *[![:space:]]* ]] && echo '; ')
          CMDARG="$@$SEMICOLON exit"
          return;
        fi
        ;;
    esac
    shift
  done
  if [ -z $DOMAIN ]; then
    ssh $SSHARGS; exit 1;
  fi
}

command -v openssl >/dev/null 2>&1 || { echo >&2 "sshrc requires openssl to be installed locally, but it's not. Aborting."; exit 1; }
sshrc_parse "$@"
sshrc

Prise en main

On ne s’embête pas avec Git, on se contente de copier sshrc et le placer dans /usr/local/bin.

wget https://raw.githubusercontent.com/Russell91/sshrc/master/sshrc
sudo mv sshrc /usr/local/bin/
sudo chown root: /usr/local/bin/sshrc && sudo chmod +x /usr/local/bin/sshrc
touch ~/.sshrc && mkdir -p ~/.sshrc.d

Voici mon fichier ~/.sshrc actuel. Dans le dossier ~/.sshrc.d j’ai mes .bashrc, .inputrc, .vimrc.

export EDITOR=vim
export INPUTRC=$SSHHOME/.sshrc.d/.inputrc
export LESS='-FRXi'
export LESS_TERMCAP_mb=$'\E[01;31m'
export LESS_TERMCAP_md=$'\E[01;31m'
export LESS_TERMCAP_me=$'\E[0m'
export LESS_TERMCAP_so=$'\E[01;44;33m'
export LESS_TERMCAP_se=$'\E[0m'
export LESS_TERMCAP_us=$'\E[01;32m'
export LESS_TERMCAP_ue=$'\E[0m'
export VIMINIT="let \$MYVIMRC='$SSHHOME/.sshrc.d/.vimrc' | source \$MYVIMRC"

source $SSHHOME/.sshrc.d/.bashrc
if [[ -f ${SSHHOME}/.sshrc.d/.bash_aliases ]]; then source ${SSHHOME}/.sshrc.d/.bash_aliases ; fi
if [[ -f ${SSHHOME}/.sshrc.d/.bash_functions ]]; then source ${SSHHOME}/.sshrc.d/.bash_functions ; fi

On se connecte ensuite à un serveur comme ceci sshrc root@bloglibre.net. Pour retrouver le répertoire sshrc echo $SSHHOME et s’y déplacer cd $SSHHOME; ls -a.

Je vous invite à taper « dotfile sshrc » dans votre moteur de recherche préféré pour avoir d’autres exemples : 1, 2, 3, 4, 5.

Deux points négatifs

Il n’est pas possible d’envoyer plus de 64 Ko de fichiers de configuration compressés avec sshrc. Première réaction la peur puis après quelques tests, j’arrive à passer 1 Mo sans problème, le mot important à retenir : compressés. Cependant ça reste un problème important pour certains, on peut citer les plugins Vim par exemple. Dans ce cas l’auteur conseille de copier les fichiers dans un obscur dossier sur le serveur et d’utiliser sshrc pour automatiquement sourcer ces configurations au login.

On a vu que sshrc était bien conçu et propre, il crée un dossier /tmp/.cascador.sshrc.XXXX unique qu’il supprime à la déconnexion. Cependant pour certains cas, vous serez obligés de « sortir » de ce dossier. Pour nano il n’est pas possible d’exporter un fichier de configuration, il lit seulement /etc/nanorc et ~/.nanorc, il ne possède pas d’option permettant de préciser où se situe (ailleurs) nanorc. On est donc obligé d’aller placer .nanorc dans ~/. Certains font une copie, d’autres créent un lien symbolique, c’est plus propre. Il faut gérer la suppression de ce fichier (on peut modifier trap \"rm -rf \$SSHRCCLEANUP; exit\" 0 entre autres solutions) mais si une autre personne se connecte sous le même compte que vous, il va charger ce fichier de configuration… et puis vous touchez aux fichiers de configuration du pc/serveur (/root/.nanorc pourrait déjà exister par exemple).

Quelques conseils

N’oubliez pas que les fichiers de configuration de votre pc sont probablement différents de ceux de vos serveurs. Typiquement je suis sur Xubuntu et j’administre des serveurs Debian, les versions des paquets sont différentes, les fichiers de configuration également. Je vous invite à copier les fichiers de configuration à partir de vos serveurs dans ~/.sshrc.d puis les modifier.

N’hésitez pas à abuser de if [ "$(hostname)" == "jessie1" ]; then source ${SSHHOME}/.sshrc.d/.bashrc_jessie1 ; fi afin de gérer finement vos dotfiles par serveur.

Vous pouvez créer des liens symboliques au lieu de copier certains fichiers de configuration dans ~/.sshrc.d. Personnellement j’ai fait ln -s ~/.inputrc ~/.sshrc.d/.inputrc pour inputrc. Ça me permet ainsi d’avoir mes raccourcis claviers personnalisés en fonction du poste sur lequel je suis (touches utilisées différentes si je suis sur mon pc portable).

Je vous invite à lire ce fil sur Hacker News autour de sshrc, il y a des réflexions intéressantes. Je me demande notamment dans quelle mesure on peut charger des fichiers de configuration (rendus disponibles par un serveur web) avec wget/curl. A noter le reddit dotfiles, the unofficial guide to doing dotfiles on GitHub, awesome-shell et tant qu’à faire awesome.

Déjà 2 avis pertinents dans sshrc, utiliser vos fichiers de configuration dans des sessions SSH

Les commentaires sont fermés.