以不同用户身份运行 Linux 服务的最佳实践
Posted
技术标签:
【中文标题】以不同用户身份运行 Linux 服务的最佳实践【英文标题】:Best practice to run Linux service as a different user 【发布时间】:2010-09-28 13:00:26 【问题描述】:在我的 RHEL 机器上,服务默认以 root
启动。如果我没记错的话,其他使用/etc/init.d
中的初始化脚本的 Linux 发行版也是如此。
您认为让进程作为我选择的(静态)用户运行的最佳方式是什么?
我想到的唯一方法是使用类似的方法:
su my_user -c 'daemon my_cmd &>/dev/null &'
但这似乎有点不整洁……
是否有一些神奇的东西可以提供一种简单的机制来以其他非 root 用户身份自动启动服务?
编辑:我应该说我在这个实例中启动的进程要么是 Python 脚本要么是 Java 程序。我宁愿不围绕它们编写本地包装器,所以很遗憾我无法像Black 建议的那样调用setuid()。
【问题讨论】:
Python 不提供对 setuid() 系列系统调用的访问权限吗?与 Perl 相比,这似乎是一个严重的缺陷。 哇,是的:os.setuid(uid)。每天都是上学日! 【参考方案1】: 一些守护进程(例如 apache)通过调用 setuid() 自行执行此操作 您可以使用setuid-file flag 以其他用户身份运行该进程。 当然,您提到的解决方案也可以。如果您打算编写自己的守护程序,那么我建议您调用 setuid()。 这样,您的进程就可以
-
利用其 root 权限(例如打开日志文件、创建 pid 文件)。
在启动期间的某个时间点放弃其 root 权限。
【讨论】:
【参考方案2】:在 Debian 上,我们使用 start-stop-daemon
实用程序,它处理 pid 文件、更改用户、将守护进程置于后台等等。
我对 RedHat 不熟悉,但是您已经在使用的 daemon
实用程序(在 /etc/init.d/functions
中定义,顺便说一句。)在各处都被提及为等同于 start-stop-daemon
,所以它也可以改变你的程序的uid,或者你这样做的方式已经是正确的了。
如果您环顾网络,您可以使用几个现成的包装器。有些甚至可能已经打包在 RedHat 中。例如,看看daemonize
。
【讨论】:
x-ref 很有趣。我有自己的守护程序,非常相似;不做pidfile或lockfile,设置umask。我有一个单独的 SUID 根程序,用于设置 UID、GID、EUID、EGID 和辅助组(称为 asroot)。我使用'asroot [opts] -- env -i [env] daemonize [opts] -- command [opts]' (续):POSIX 标准 env 程序不接受环境设置和执行命令之间的“--”(令人讨厌,但确实如此)。 如何在 upstart 脚本中使用 /etc/init.d/functions 中的守护程序函数?可以举个例子吗? 在 Debian 上见/etc/init.d/skeleton
。添加UID、GID变量并在do_start()
使用:start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --chuid $UID:$GID -- $DAEMON_ARGS
我注意到 daemon()
在我的 RHEL 和 CentOS 机器上的 /etc/rc.d/init.d/function
中定义。【参考方案3】:
在查看了这里的所有建议后,我发现了一些我希望对我所在职位的其他人有用的东西:
hop 是正确的指向我回来
在/etc/init.d/functions
:
daemon
功能已经让你
设置备用用户:
daemon --user=my_user my_cmd &>/dev/null &
这是通过包装
进程调用runuser
-
稍后会详细介绍。
Jonathan Leffler 是对的: Python中有setuid:
import os
os.setuid(501) # UID of my_user is 501
我仍然认为你不能 setuid 但是,来自 JVM 内部。
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)
为了解决su
和runuser
的这种行为,我将初始化脚本更改为:
if [[ "$USER" == "my_user" ]]
then
daemon my_cmd &>/dev/null &
else
daemon --user=my_user my_cmd &>/dev/null &
fi
感谢大家的帮助!
【讨论】:
【参考方案4】:注意事项:
如您所述,如果您已经是目标用户,su 会提示您输入密码 同样,如果您已经是目标用户(在某些操作系统上),setuid(2) 也会失败 setuid(2) 不安装 /etc/limits.conf (Linux) 或 /etc/user_attr (Solaris) 中定义的权限或资源控制 如果您使用 setgid(2)/setuid(2) 路线,请不要忘记调用 initgroups(3) -- 更多关于此here我一般在启动守护进程之前使用/sbin/su切换到合适的用户。
【讨论】:
【参考方案5】:只是添加一些其他需要注意的事项:
init.d 脚本中的 Sudo 不好,因为它需要一个 tty(“sudo:抱歉,您必须有一个 tty 才能运行 sudo”) 如果您正在对 java 应用程序进行守护进程,您可能需要考虑 Java Service Wrapper(它提供了一种设置用户 ID 的机制) 另一种选择可能是 su --session-command=[cmd] [user]【讨论】:
【参考方案6】:为什么不在初始化脚本中尝试以下操作:
setuid $USER application_name
它对我有用。
【讨论】:
并非所有发行版都提供此功能。我在 RHEL 7 上试过:setuid: command not found
【参考方案7】:
在用于 svn 服务器的 CENTOS (Red Hat) 虚拟机上:
编辑/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
【讨论】:
这会造成权限提升漏洞。 svn 用户现在可以将任意 PID 放入 /home/svn/run/svnserve.pid 文件中,当 svn 服务停止或重新启动时,该文件将被杀死而不是 svn 进程。【参考方案8】:我需要将 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
【讨论】:
以上是关于以不同用户身份运行 Linux 服务的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章
Blazor WebAssembly - 用户帐户和身份服务器的最佳实践
AWS Cognito:处理相同用户(使用相同电子邮件地址)从不同身份提供商(Google、Facebook)登录的最佳实践