Comment utiliser su pour exécuter le reste du script bash en tant qu'utilisateur?

126

J'ai écrit un script qui prend, comme argument, une chaîne qui est une concaténation d'un nom d'utilisateur et d'un projet. Le script est censé basculer (su) vers le nom d'utilisateur, cd vers un répertoire spécifique basé sur la chaîne du projet.

Je veux essentiellement faire:

su $USERNAME;  
cd /home/$USERNAME/$PROJECT;  
svn update;  

Le problème est qu'une fois que je fais un su ... ça attend juste là. Ce qui a du sens puisque le flux d'exécution est passé au passage à l'utilisateur. Une fois que je quitte, le reste des choses s'exécute mais cela ne fonctionne pas comme souhaité.

J'ai ajouté su à la commande svn mais la commande a échoué (c'est-à-dire qu'elle n'a pas mis à jour svn dans le répertoire souhaité).

Comment écrire un script qui permet à l'utilisateur de changer d'utilisateur et d'appeler svn (entre autres)?

Avery
la source

Réponses:

86

L'astuce consiste à utiliser la commande "sudo" au lieu de "su"

Vous devrez peut-être ajouter ceci

username1 ALL=(username2) NOPASSWD: /path/to/svn

dans votre fichier / etc / sudoers

et changez votre script en:

sudo -u username2 -H sh -c "cd /home/$USERNAME/$PROJECT; svn update" 

Où username2 est l'utilisateur sous lequel vous souhaitez exécuter la commande SVN et username1 est l'utilisateur exécutant le script.

Si vous avez besoin de plusieurs utilisateurs pour exécuter ce script, utilisez un à la %groupnameplace du nom d'utilisateur1

Kimvais
la source
J'ai un problème similaire, mais je voulais courir chshpour les autres utilisateurs. Mon problème est répertorié ici sur stackoverflow.com/q/15307289/80353 Comment adapter votre réponse à ma situation?
Kim Stacks
Je l'ai fait - mais il m'a quand même demandé un mot de passe.
Hippyjim
@Hippyjim êtes-vous sûr que vous avez bien compris les noms d'utilisateur?
Kimvais
1
Je l'ai fait - il s'est avéré que j'avais également besoin d'autoriser l'utilisation de / bin / bash.
Hippyjim
3
Que vous l'utilisiez sudoou susoit d'importance secondaire, sudoc'est beaucoup plus sûr et pratique.
tripleee
105

Beaucoup plus simple: utilisez sudopour exécuter un shell et utilisez un heredoc pour le nourrir de commandes.

#!/usr/bin/env bash
whoami
sudo -i -u someuser bash << EOF
echo "In"
whoami
EOF
echo "Out"
whoami

(réponse à l' origine sur SuperUser )

Dan Dascalescu
la source
5
Cette réponse fonctionnait le mieux. Je recommande d'utiliser l'option -ipour obtenir someuserl'environnement attendu.
not2savvy
2
sudopeut être pratique mais ce n'est pas bon si ce n'est pas prêt à l'emploi (comme sur AIX). su -c 'commands'est la bonne réponse.
Tricky
1
Solution épique!
3bdalla
Comment rendre les variables disponibles dans le périmètre herdoc?
dotslashlu
sous AIX, il renvoie cette erreur ksh: sh: 0403-006 Autorisation d'exécution refusée.
AhmedRana
54

Utilisez un script comme celui-ci pour exécuter le reste ou une partie du script sous un autre utilisateur:

#!/bin/sh

id

exec sudo -u transmission /bin/sh - << eof

id

eof
Walder
la source
11
Vous pouvez utiliser "sudo -i -u ..." pour vous assurer que des éléments comme $ HOME sont correctement définis.
Bryan Larsen
Désolé pour la question noob, mais quel est un identifiant ici?
Nitin Jadhav
1
@NitinJadhav, il l'a utilisé ici juste pour montrer l'ID de l'utilisateur actuel, l'ID de root est 0, donc le premier id vous montrera un certain nombre, mais le second affichera définitivement 0 (car le second a été exécuté à l'intérieur un bloc exécuté par root). Vous pouvez utiliser à la whoamiplace de idqui retournera le nom au lieu de l'identifiant
Mohammed Noureldin
@MohammedNoureldin Merci!
Nitin Jadhav
sudopeut être pratique mais ce n'est pas bon si ce n'est pas prêt à l'emploi (comme sur AIX). su -c 'commands'est la bonne réponse.
Tricky
52

Vous devez exécuter toutes les commandes des différents utilisateurs comme leur propre script. S'il ne s'agit que d'une ou de quelques commandes, alors en ligne devrait fonctionner. S'il y a beaucoup de commandes, il est probablement préférable de les déplacer vers leur propre fichier.

su -c "cd /home/$USERNAME/$PROJECT ; svn update" -m "$USERNAME" 
Douglas Leeder
la source
4
C'est la seule bonne réponse. Le sudo n'est pas nécessaire pour cela.
helvete
1
Vous devrez peut-être également fournir à shell su -s /bin/bash.
mixel
meilleure solution pour les utilisateurs root
rfinz
Meilleure réponse pour ceux qui n'ont pas installé sudo par défaut (je vous regarde AIX)
Tricky
46

Voici encore une autre approche, qui était plus pratique dans mon cas (je voulais juste supprimer les privilèges root et faire le reste de mon script à partir d'un utilisateur restreint): vous pouvez faire redémarrer le script à partir de l'utilisateur correct. Supposons qu'il soit exécuté en tant que root au départ. Ensuite, cela ressemblera à ceci:

#!/bin/bash
if [ $UID -eq 0 ]; then
  user=$1
  dir=$2
  shift 2     # if you need some other parameters
  cd "$dir"
  exec su "$user" "$0" -- "$@"
  # nothing will be executed beyond that line,
  # because exec replaces running process with the new one
fi

echo "This will be run from user $UID"
...
MarSoft
la source
6
Je me demande pourquoi ce n'est pas mieux noté. Il résout au mieux la question initiale tout en gardant tout en bash.
SirVer le
Ou runuser -u $user -- "$@", comme indiqué dans su (1)
cghislai
1
exec su "$user" "$0" -- "$@"
macieksk
1
Merci @macieksk, belle prise. Mettra à jour. Le --est vraiment utile.
MarSoft
1
L'approche présentée est la meilleure de toutes et est complète. Tout est écrit dans un seul script, et tout utilise bash. En effet, ce script est exécuté deux fois. Au premier test de script, il est root, puis prépare l'environnement et changez d'utilisateur par su. Mais faites-le avec la commande exec, alors il n'y a qu'une seule instance de script. Avec la deuxième boucle, la phrase if / fi est omise, puis le script effectue directement l'action préparée. Dans cet exemple, c'est un écho. Toutes les autres approches ne résolvent le problème que partiellement. Bien sûr, ce script peut être exécuté sous sh pas bash, mais nous devons tester la variable spéciale $ HOME, pas $ UID
Znik le
7

Utilisez sudoplutôt

EDIT : Comme Douglas a souligné, vous ne pouvez pas utiliser cden sudocar il est pas externe commande. Vous devez exécuter les commandes dans un sous-shell pour faire le cdtravail.

sudo -u $USERNAME -H sh -c "cd ~/$PROJECT; svn update"

sudo -u $USERNAME -H cd ~/$PROJECT
sudo -u $USERNAME svn update

Vous pouvez être invité à saisir le mot de passe de cet utilisateur, mais une seule fois.

iamamac
la source
Cela ne fonctionnera pas cependant - le cd sera perdu une fois que le premier sudo aura fini de s'exécuter.
Douglas Leeder
En fait, vous ne pouvez même pas appeler cd directement car ce n'est pas une commande externe .
iamamac
sudopeut être pratique mais ce n'est pas bon si ce n'est pas prêt à l'emploi (comme sur AIX). su -c 'commands'est la bonne réponse.
Tricky
6

Il n'est pas possible de changer d'utilisateur dans un script shell. Les solutions de contournement utilisant sudo décrites dans d'autres réponses sont probablement votre meilleur pari.

Si vous êtes assez fou pour exécuter des scripts perl en tant que root, vous pouvez le faire avec les $< $( $> $) variables qui contiennent uid / gid réel / effectif, par exemple:

#!/usr/bin/perl -w
$user = shift;
if (!$<) {
    $> = getpwnam $user;
    $) = getgrnam $user;
} else {
    die 'must be root to change uid';
}
system('whoami');
Écrous en P
la source
-1 car il est possible avec sudo d'acquérir temporairement les droits d'autres utilisateurs.
Kimvais
4
Ce n'est pas possible dans le sens où l'utilisateur le script shell lui-même s'exécute comme ne peut pas être changé (ce que la question d'origine a posée). L'appel d'autres processus avec sudo ne change pas la personne sous laquelle le script s'exécute.
P-Nuts
2

Cela a fonctionné pour moi

J'ai séparé mon «approvisionnement» de mon «démarrage».

 # Configure everything else ready to run 
  config.vm.provision :shell, path: "provision.sh"
  config.vm.provision :shell, path: "start_env.sh", run: "always"

puis dans mon start_env.sh

#!/usr/bin/env bash

echo "Starting Server Env"
#java -jar /usr/lib/node_modules/selenium-server-standalone-jar/jar/selenium-server-standalone-2.40.0.jar  &
#(cd /vagrant_projects/myproj && sudo -u vagrant -H sh -c "nohup npm install 0<&- &>/dev/null &;bower install 0<&- &>/dev/null &")
cd /vagrant_projects/myproj
nohup grunt connect:server:keepalive 0<&- &>/dev/null &
nohup apimocker -c /vagrant_projects/myproj/mock_api_data/config.json 0<&- &>/dev/null &
httpete
la source
-2

Inspiré par l'idée de @ MarSoft mais j'ai changé les lignes comme suit:

USERNAME='desireduser'
COMMAND=$0
COMMANDARGS="$(printf " %q" "${@}")"
if [ $(whoami) != "$USERNAME" ]; then
  exec sudo -E su $USERNAME -c "/usr/bin/bash -l $COMMAND $COMMANDARGS"
  exit
fi

J'ai l'habitude sudod'autoriser un mot de passe moins l'exécution du script. Si vous souhaitez entrer un mot de passe pour l'utilisateur, supprimez le fichier sudo. Si vous n'avez pas besoin des variables d'environnement, supprimez -Ede sudo.

Le /usr/bin/bash -lgarantit que les profile.dscripts sont exécutés pour un environnement initialisé.

Trendfischer
la source
sudopeut être pratique mais ce n'est pas bon si ce n'est pas prêt à l'emploi (comme sur AIX). su -c 'commands'est la bonne réponse.
Tricky
@Tricky Lisez peut-être la réponse complète, cela suggère déjà de supprimer sudo. En fait, sudo n'est pas si simple, dans de nombreux cas, vous avez besoin de même sudo -Eet d'une entrée de configuration dans sudoers.d pour permettre l'exécution sans tty avec !requiretty. Mais il existe de nombreux cas où sudo est nécessaire pour les scripts appelés automatiques, où une boîte de dialogue de mot de passe peut interférer. Ainsi, je ne le retirerais pas d'une solution standard.
Trendfischer
Le code en question est carrément bogué - il cassera les noms de script ou les arguments avec des espaces; gardez à l'esprit que l'insertion "$@"d'une chaîne signifie que les arguments après le premier sont ajoutés dans une chaîne séparée, non incluse dans l' -cargument. Si vous vouliez le rendre sûr, vous pourriez printf -v arg_q '%q ' "$0" "$@"et ensuite utilisersu "$USERNAME" -c "/usr/bin/bash -l $arg_q"
Charles Duffy
@CharlesDuffy Vous avez raison! J'ai trop simplifié mon script de production pour cette réponse :-( Le simple fait d' ajouter a COMMANDARGS=$@résout le problème avec -c. Les arguments avec des espaces n'étaient pas un problème avant, mais j'ai implémenté votre bonne entrée. Je devais juste faire quelques expériences pour que cela fonctionne. Je 'ai édité la question et j'espère que je n'ai pas collé un autre bogue. Merci pour votre commentaire, humiliant mais nécessaire.
Trendfischer