Meilleures pratiques pour exécuter le service Linux en tant qu'utilisateur différent

141

Les services démarrent par défaut au moment rootdu démarrage sur ma boîte RHEL. Si je me souviens bien, il en va de même pour les autres distributions Linux qui utilisent les scripts init dans /etc/init.d.

Selon vous, quelle est la meilleure façon de faire exécuter les processus en tant qu'utilisateur (statique) de mon choix?

La seule méthode à laquelle je suis arrivé était d'utiliser quelque chose comme:

 su my_user -c 'daemon my_cmd &>/dev/null &'

Mais cela semble un peu désordonné ...

Y a-t-il un peu de magie cachée qui fournit un mécanisme simple pour démarrer automatiquement les services en tant qu'autres utilisateurs non root?

EDIT: J'aurais dû dire que les processus que je démarre dans cette instance sont des scripts Python ou des programmes Java. Je préfère ne pas écrire un wrapper natif autour d'eux, donc je ne peux malheureusement pas appeler setuid () comme le suggère Black .

James Brady
la source
Python ne donne-t-il pas accès à la famille d'appels système setuid ()? Cela semble être un grave défaut par rapport à Perl.
Jonathan Leffler
12
Wow, oui c'est le cas: os.setuid (uid). Chaque jour est un jour d'école!
James Brady

Réponses:

67

Sur Debian, nous utilisons l' start-stop-daemonutilitaire, qui gère les fichiers pid, change l'utilisateur, met le démon en arrière-plan et bien plus encore.

Je ne suis pas familier avec RedHat, mais l' daemonutilitaire que vous utilisez déjà (qui est défini dans /etc/init.d/functions, btw.) Est mentionné partout comme l'équivalent de start-stop-daemon, donc il peut également changer l'uid de votre programme, ou la façon dont vous le faites c'est déjà le bon.

Si vous regardez autour du filet, il existe plusieurs emballages prêts à l'emploi que vous pouvez utiliser. Certains peuvent même être déjà emballés dans RedHat. Jetez un oeil à daemonize, par exemple.


la source
Le x-ref est intéressant. J'ai mon propre programme daemonize, très similaire; ne fait pas le pidfile ou le lockfile, définit umask. J'ai un programme racine SUID séparé pour définir les groupes UID, GID, EUID, EGID et aux (appelé asroot). J'utilise 'asroot [opts] - env ​​-i [env] daemonize [opts] - command [opts]'
Jonathan Leffler
(suite): le programme d'environnement standard POSIX n'accepte pas '-' entre le paramètre d'environnement et la commande exécutée (ennuyeux, mais donc).
Jonathan Leffler
4
Comment la fonction démon de /etc/init.d/functions peut-elle être utilisée dans un script upstart? Pouvez-vous montrer un exemple, s'il vous plaît.
Meglio
10
Sur Debian voir /etc/init.d/skeleton. Ajouter des variables UID, GID et en cours d' do_start()utilisation:start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid $UID:$GID -- $DAEMON_ARGS
Jonathan Ben-Avraham
Je remarque qu'il daemon()est défini dans /etc/rc.d/init.d/functionles deux boîtes RHEL et CentOS de la mienne.
quickshift du
53

Après avoir examiné toutes les suggestions ici, j'ai découvert quelques choses qui, je l'espère, seront utiles à d'autres dans ma position:

  1. hop a raison de me rappeler /etc/init.d/functions: la daemonfonction vous permet déjà de définir un autre utilisateur:

    daemon --user=my_user my_cmd &>/dev/null &
    

    Ceci est implémenté en encapsulant l'appel de processus avec runuser- nous en parlerons plus tard.

  2. Jonathan Leffler a raison: il y a setuid en Python:

    import os
    os.setuid(501) # UID of my_user is 501
    

    Cependant, je ne pense toujours pas que vous puissiez définir le guide depuis une machine virtuelle Java.

  3. Ne gérez supas runusercorrectement le cas où vous demandez d'exécuter une commande en tant qu'utilisateur que vous êtes déjà. Par exemple:

    [my_user@my_host]$ id
    uid=500(my_user) gid=500(my_user) groups=500(my_user)
    [my_user@my_host]$ su my_user -c "id"
    Password: # don't want to be prompted!
    uid=500(my_user) gid=500(my_user) groups=500(my_user)
    

Pour contourner ce comportement de suet runuser, j'ai changé mon script d'initialisation en quelque chose comme:

if [[ "$USER" == "my_user" ]]
then
    daemon my_cmd &>/dev/null &
else
    daemon --user=my_user my_cmd &>/dev/null &
fi

Merci à tous pour votre aide!

James Brady
la source
5
  • Certains démons (par exemple apache) le font d'eux-mêmes en appelant setuid ()
  • Vous pouvez utiliser l' indicateur setuid-file pour exécuter le processus en tant qu'utilisateur différent.
  • Bien sûr, la solution que vous avez mentionnée fonctionne également.

Si vous avez l'intention d'écrire votre propre démon, je vous recommande d'appeler setuid (). De cette façon, votre processus peut

  1. Utilisez ses privilèges root (par exemple, ouvrir les fichiers journaux, créer des fichiers pid).
  2. Supprimez ses privilèges root à un certain moment lors du démarrage.
Noir
la source
3

Juste pour ajouter d'autres choses à surveiller:

  • Sudo dans un script init.d n'est pas bon car il a besoin d'un tty ("sudo: désolé, vous devez avoir un tty pour exécuter sudo")
  • Si vous démonisez une application java, vous voudrez peut-être envisager Java Service Wrapper (qui fournit un mécanisme pour définir l'ID utilisateur)
  • Une autre alternative pourrait être su --session-command = [cmd] [utilisateur]
pdeschen
la source
3

sur une machine virtuelle CENTOS (Red Hat) pour serveur svn: édité /etc/init.d/svnserver pour changer le pid en quelque chose que svn peut écrire:

pidfile=${PIDFILE-/home/svn/run/svnserve.pid}

et option ajoutée --user=svn:

daemon --pidfile=${pidfile} --user=svn $exec $args

Le pidfile original était /var/run/svnserve.pid. Le démon n'a pas démarré car seul root pouvait y écrire.

 These all work:
/etc/init.d/svnserve start
/etc/init.d/svnserve stop
/etc/init.d/svnserve restart
Dulcana
la source
3
Cela crée une vulnérabilité d'escalade de privilèges. L'utilisateur svn peut maintenant placer des PID arbitraires dans le fichier /home/svn/run/svnserve.pid qui seront tués au lieu du processus svn chaque fois que le service svn est arrêté ou redémarré.
rbu
2

Quelques points à surveiller:

  • Comme vous l'avez mentionné, su demandera un mot de passe si vous êtes déjà l'utilisateur cible
  • De même, setuid (2) échouera si vous êtes déjà l'utilisateur cible (sur certains OS)
  • setuid (2) n'installe pas les privilèges ou les contrôles de ressources définis dans /etc/limits.conf (Linux) ou / etc / user_attr (Solaris)
  • Si vous suivez la route setgid (2) / setuid (2), n'oubliez pas d'appeler initgroups (3) - plus à ce sujet ici

J'utilise généralement / sbin / su pour passer à l'utilisateur approprié avant de démarrer les démons.

argile
la source
2

Pourquoi ne pas essayer ce qui suit dans le script init:

setuid $USER application_name

Cela a fonctionné pour moi.

cyberJar
la source
3
Ce n'est pas disponible sur toutes les distributions. J'ai essayé sur RHEL 7:setuid: command not found
Cocowalla
0

J'avais besoin d'exécuter une application Spring .jar en tant que service et j'ai trouvé un moyen simple de l'exécuter en tant qu'utilisateur spécifique:

J'ai changé le propriétaire et le groupe de mon fichier jar en l'utilisateur avec lequel je voulais exécuter. Ensuite, un lien symbolique de ce fichier jar dans init.d et a démarré le service.

Alors:

#chown myuser:myuser /var/lib/jenkins/workspace/springApp/target/springApp-1.0.jar

#ln -s /var/lib/jenkins/workspace/springApp/target/springApp-1.0.jar /etc/init.d/springApp

#service springApp start

#ps aux | grep java
myuser    9970  5.0  9.9 4071348 386132 ?      Sl   09:38   0:21 /bin/java -Dsun.misc.URLClassPath.disableJarChecking=true -jar /var/lib/jenkins/workspace/springApp/target/springApp-1.0.jar
Somaiah Kumbera
la source