以其他用户身份运行Linux服务的最佳实践


141

服务默认为root在启动时在我的RHEL盒上启动。如果我没记错的话,其他使用init中的init脚本的Linux发行版也是如此/etc/init.d

您认为让流程以我选择的(静态)用户身份运行的最佳方法是什么?

我到达的唯一方法是使用类似以下内容的方法:

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

但这似乎有点不整洁...

是否有一些魔术可以提供一种简单的机制来像其他非root用户一样自动启动服务?

编辑:我应该说,我在此实例中启动的进程是Python脚本或Java程序。我不想在它们周围编写本机包装,所以不幸的是,我无法按照Black的建议调用setuid()


Python是否不提供对setuid()系列系统调用的访问?与Perl相比,这似乎是一个严重的缺陷。
乔纳森·莱夫勒

12
哇,是的,它是:os.setuid(uid)。每天都是上学日!
詹姆斯·布雷迪

Answers:


67

在Debian上,我们使用该start-stop-daemon实用程序来处理pid文件,更改用户,将守护程序置于后台等等。

我不熟悉RedHat,但是daemon您已经在使用的实用程序(在/etc/init.d/functionsbtw中定义)被等同于所提及start-stop-daemon,因此它既可以更改程序的uid,也可以更改您的操作方式它已经是正确的了。

如果您环顾网上,可以使用几种现成的包装纸。有些甚至可能已经打包在RedHat中。看看daemonize,例如。


外部参照很有趣。我有自己的守护程序,非常相似;不执行pidfile或lockfile,而是设置umask。我有一个单独的SUID根程序,用于设置UID,GID,EUID,EGID和aux组(称为asroot)。我使用“ asroot [opts]-env -i [env] daemonize [opts]-command [opts]”
Jonathan Leffler

(续):POSIX标准环境程序在环境设置和已执行的命令之间不接受“-”(讨厌,但是这样)。
乔纳森·莱夫勒

4
如何在upstart脚本中使用/etc/init.d/functions中的守护程序函数?请给我一个例子。
Meglio 2012年

10
在Debian上看看/etc/init.d/skeleton。添加UID,GID变量并在do_start()使用中:start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid $UID:$GID -- $DAEMON_ARGS
Jonathan Ben-Avraham 2013年

我注意到daemon()在我的/etc/rc.d/init.d/functionRHEL和CentOS盒子上均已定义。
quickshiftin

53

在查看了这里的所有建议之后,我发现了一些希望对我所处位置的其他人有用的东西:

  1. 是正确的指向我/etc/init.d/functions:该 daemon功能已经允许您设置备用用户:

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

    这是通过将流程调用包装为来实现的runuser-稍后会详细介绍。

  2. Jonathan Leffler是对的:Python中有setuid:

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

    不过,我仍然认为您不能从JVM内部使用setuid。

  3. 既不 要求您以已经是用户的身份运行命令的情况,也su不能runuser妥善处理。例如:

    [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)
    

为了解决suand的行为runuser,我将init脚本更改为:

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

感谢你的帮助!


5
  • 一些守护程序(例如apache)通过调用setuid()自己执行此操作
  • 您可以使用setuid-file标志以其他用户身份运行该进程。
  • 当然,您提到的解决方案也可以。

如果您打算编写自己的守护程序,则建议调用setuid()。这样,您的过程可以

  1. 利用其root特权(例如,打开日志文件,创建pid文件)。
  2. 在启动过程中的某个时候删除其根特权。

3

只是添加一些其他注意事项:

  • init.d脚本中的Sudo不好,因为它需要tty(“ sudo:抱歉,您必须具有tty才能运行sudo”)
  • 如果要守护Java应用程序,则可能需要考虑Java Service Wrapper(提供了用于设置用户ID的机制)
  • 另一个替代方法可以是su --session-command = [cmd] [user]

3

在用于svn服务器的CENTOS(红帽)虚拟机上:已编辑/etc/init.d/svnserver 以将pid更改为svn可以写入的内容:

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

并添加了选项--user=svn

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

原始pidfile是/var/run/svnserve.pid。该守护程序没有启动,因为只有root可以在其​​中写。

 These all work:
/etc/init.d/svnserve start
/etc/init.d/svnserve stop
/etc/init.d/svnserve restart

3
这将创建一个特权升级漏洞。svn用户现在可以在/home/svn/run/svnserve.pid文件中放置任意PID,无论何时停止或重新启动svn服务,该文件都会被杀死而不是svn进程。
rbu 2014年

2

需要注意的一些事情:

  • 如前所述,如果您已经是目标用户,su会提示您输入密码
  • 同样,如果您已经是目标用户(在某些操作系统上),setuid(2)将失败
  • setuid(2)不会安装/etc/limits.conf(Linux)或/ etc / user_attr(Solaris)中定义的特权或资源控制
  • 如果您使用setgid(2)/ setuid(2)路由,请不要忘记调用initgroups(3)-有关此内容的更多 信息

我通常在启动守护程序之前使用/ sbin / su切换到适当的用户。


2

为什么不尝试在初始化脚本中进行以下操作:

setuid $USER application_name

它为我工作。


3
并非在所有发行版中都可用。我在RHEL 7上尝试过:setuid: command not found
Cocowalla 2014年

0

我需要将Spring .jar应用程序作为服务运行,并找到了一种以特定用户身份运行该应用程序的简单方法:

我将jar文件的所有者和组更改为我要作为其运行的用户。然后在init.d中将该jar链接起来,并启动服务。

所以:

#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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.