J'ai un système auquel je ne peux me connecter que sous mon nom d'utilisateur (myuser), mais je dois exécuter des commandes en tant qu'autre utilisateur (scriptuser). Jusqu'à présent, j'ai trouvé ce qui suit pour exécuter les commandes dont j'ai besoin:
ssh -tq myuser@hostname "Sudo -u scriptuser bash -c \"ls -al\""
Si toutefois, lorsque j'essaie d'exécuter une commande plus complexe, telle que [[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory"
J'ai rapidement des ennuis avec les citations. Je ne sais pas comment je pourrais passer cet exemple de commande complexe à bash -c
, quand \"
délimite déjà les limites de la commande que je passe (et donc je ne sais pas comment citer le répertoire/tmp/Some, qui comprend un espace.
Existe-t-il une solution générale me permettant de passer n'importe quelle commande, quelle que soit la complexité/la folie de la citation, ou est-ce une sorte de limitation que j'ai atteinte? Existe-t-il d'autres solutions possibles et peut-être plus lisibles?
Une astuce que j'utilise parfois est d'utiliser base64 pour encoder les commandes et de le diriger vers bash sur l'autre site:
MYCOMMAND=$(base64 -w0 script.sh)
ssh user@remotehost "echo $MYCOMMAND | base64 -d | Sudo bash"
Cela encodera le script, avec des virgules, des barres obliques inverses, des guillemets et des variables dans une chaîne sûre, et l'enverra à l'autre serveur. (-w0
Est requis pour désactiver le retour à la ligne, ce qui se produit à la colonne 76 par défaut). De l'autre côté, $(base64 -d)
décodera le script et l'alimentera en bash à exécuter.
Je n'ai jamais eu de problème avec ça, peu importe la complexité du script. Résout le problème de l'évasion, car vous n'avez rien à échapper. Il ne crée pas de fichier sur l'hôte distant et vous pouvez exécuter facilement des scripts extrêmement complexes.
Voir l'option -tt
? Lisez le manuel ssh(1)
.
ssh -tt root@Host << EOF
Sudo some # Sudo shouldn't ask for a password, otherwise, this fails.
lines
of
code
but be careful with \$variables
and \$(other) \`stuff\`
exit # <- Important.
EOF
Une chose que je fais souvent est d'utiliser vim et d'utiliser l'astuce :!cat % | ssh -tt somemachine
.
Je pense que la solution la plus simple réside dans une modification du commentaire de @ thanasisk.
Créez un script, scp
sur la machine, puis exécutez-le.
Ayez le script rm
lui-même au début. Le shell a ouvert le fichier, il a donc été chargé et peut ensuite être supprimé sans problème.
En faisant les choses dans cet ordre (rm
d'abord, d'autres choses ensuite), il sera même supprimé quand il échouera à un moment donné.
MISE À JOUR: Les exemples utilisent désormais explicitement Sudo
.
Voici un moyen d'utiliser la syntaxe Bash avec des affectations composées pour exécuter des commandes arbitrairement complexes sur SSH avec Sudo:
CMD=$( cat <<'EOT'
echo "Variables like '${HOSTNAME}' and commands like $( whoami )"
echo "will be interpolated on the server, thanks to the single quotes"
echo "around 'EOT' above."
EOT
) \
SSHCMD=$(
printf 'ssh -tq myuser@hostname Sudo -u scriptuser bash -c %q' "${CMD}" ) \
bash -c '${SSHCMD}'
CMD=$( cat <<EOT
echo "If you want '${HOSTNAME}' and $( whoami ) to be interpolated"
echo "on the client instead, omit the the single quotes around EOT."
EOT
) \
SSHCMD=$(
printf 'ssh -tq myuser@hostname Sudo -u scriptuser bash -c %q' "${CMD}" ) \
bash -c '${SSHCMD}'
Le formatage correct des commandes à exécuter dynamiquement ailleurs (par exemple, des shells imbriqués, des shells SSH distants, etc.) peut être très délicat - voir https://stackoverflow.com/a/53197638/111948 pour une bonne description de pourquoi. Les structures bash compliquées, comme la syntaxe ci-dessus, peuvent aider ces commandes à fonctionner correctement.
Pour plus d'informations sur la façon dont cat
et <<
se combinent pour former des documents en ligne ici , voir https://stackoverflow.com/a/21761956/111948
Vous pouvez utiliser le %q
spécificateur de format avec printf
pour prendre en charge la variable qui s'échappe pour vous:
cmd="ls -al"
printf -v cmd_str '%q' "$cmd"
ssh user@Host "bash -c $cmd_str"
printf -v
écrit la sortie dans une variable (dans ce cas, $cmd_str
). Je pense que c'est la façon la plus simple de le faire. Il n'est pas nécessaire de transférer des fichiers ou d'encoder la chaîne de commande (autant que j'aime l'astuce).
Voici un exemple plus complexe montrant que cela fonctionne aussi pour des choses comme les crochets et les esperluettes:
$ ssh user@Host "ls -l test"
-rw-r--r-- 1 tom users 0 Sep 4 21:18 test
$ cmd="[[ -f test ]] && echo 'this really works'"
$ printf -v cmd_str '%q' "$cmd"
$ ssh user@Host "bash -c $cmd_str"
this really works
Je ne l'ai pas testé avec Sudo
mais ça devrait être aussi simple que:
ssh user@Host "Sudo -u scriptuser bash -c $cmd_str"
Si vous le souhaitez, vous pouvez ignorer une étape et éviter de créer la variable intermédiaire:
$ ssh user@Host "bash -c $(printf '%q' "$cmd")"
this really works
Ou évitez même de créer une variable entièrement:
ssh user@Host "bash -c $(printf '%q' "[[ -f test ]] && echo 'this works as well'")"
Savez-vous que vous pouvez utiliser Sudo
pour vous donner un Shell où vous pouvez exécuter des commandes en tant qu'utilisateur choisi?
-i, --login
Exécutez le shell spécifié par l'entrée de la base de données de mots de passe de l'utilisateur cible en tant que shell de connexion. Cela signifie que les fichiers de ressources spécifiques à la connexion tels que .profile ou .login seront lus par le shell. Si une commande est spécifiée, elle est transmise au Shell pour exécution via l'option -c du Shell. Si aucune commande n'est spécifiée, un shell interactif est exécuté. Sudo tente de passer au répertoire personnel de cet utilisateur avant d'exécuter le shell. La commande est exécutée avec un environnement similaire à celui qu'un utilisateur recevrait à la connexion. La section Environnement de commande du manuel sudoers (5) documente comment l'option -i affecte l'environnement dans lequel une commande est exécutée lorsque la stratégie sudoers est en cours d'utilisation.
Vous pouvez définir le script sur votre machine locale puis cat
et le diriger vers la machine distante:
user@Host:~/temp> echo "echo 'Test'" > fileForSsh.txt
user@Host:~/temp> cat fileForSsh.txt | ssh localhost
Pseudo-terminal will not be allocated because stdin is not a terminal.
stty: standard input: Invalid argument
Test
Simple et facile.
ssh user @ servidor "bash -s" <script.sh
Si vous utilisez un Bash Shell suffisamment moderne, vous pouvez placer vos commandes dans une fonction et imprimer cette fonction sous forme de chaîne en utilisant export -p -f function_name
. Le résultat de cette chaîne peut alors contenir des commandes arbitraires qui ne doivent pas nécessairement s'exécuter en tant que root.
Un exemple utilisant des pipes de ma réponse sur Unix.SE :
#!/bin/bash remote_main() { local dest="$HOME/destination" tar xzv -C "$dest" chgrp -R www-data "$dest" # Ensure that newly written files have the 'www-data' group too find "$dest" -type d -exec chmod g+s {} \; } tar cz files/ | ssh user@Host "$(declare -pf remote_main); remote_main"
Un exemple qui récupère enregistre le fichier pour l'utilisateur de connexion et installe un programme à la racine:
remote_main() {
wget https://example.com/screenrc -O ~/.screenrc
Sudo apt-get update && Sudo apt-get install screen
}
ssh user@Host "$(declare -pf remote_main); remote_main"
Si vous souhaitez exécuter la commande entière en utilisant Sudo
, vous pouvez utiliser ceci:
remote_main() {
wget https://example.com/screenrc -O ~user/.screenrc
apt-get update && apt-get install screen
}
ssh user@Host "$(declare -pf remote_main);
Sudo sh -c \"\$(declare -pf remote_main); remote_cmd\""
# Alternatively, if you don't need stdin and do not want to log the command:
ssh user@Host "$(declare -pf remote_main);
(declare -pf remote_main; echo remote_cmd) | Sudo sh"
Créez d'abord un script local, puis exécutez-le à distance à l'aide de la commande suivante:
cat <Local Script.sh> | ssh user@server "cat - | Sudo -u <user> /bin/bash"
Pour ceux d'entre vous qui doivent passer des paramètres au script, cela devrait être le suivant:
ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | Sudo bash -s <param1> <param2> <paramN>"
Voici ma solution merdique (pas vraiment testée) pour cela:
#!/usr/bin/env Ruby
# Shell-escape: Escape each argument.
ARGV.each do|a|
print " '#{a.gsub("'","\'\\\\'\'")}' "
end
Vous ne pouvez pas faire:
ssh -tq myuser@hostname "$(Shell-escape Sudo -u scriptuser bash -c "$(Shell-escape ls -al)")"
L'étape suivante consiste à créer un script betterssh
qui le fait déjà:
betterssh -tq myuser@hostname Sudo -u scriptuser bash -c "$(Shell-escape ls -al)"