以不同用户身份运行 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/functionsdaemon 功能已经让你 设置备用用户:

    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 内部。

    surunuser 都不是 优雅地处理你的案件 要求以您的用户身份运行命令 已经是了。例如:

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

为了解决surunuser 的这种行为,我将初始化脚本更改为:

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 服务的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章

存储 oauth 和本地身份验证方法的最佳实践?

在微服务架构中组织授权的最佳实践?

Blazor WebAssembly - 用户帐户和身份服务器的最佳实践

Vaadin 身份验证最佳实践

AWS Cognito:处理相同用户(使用相同电子邮件地址)从不同身份提供商(Google、Facebook)登录的最佳实践

平均堆栈身份验证 - 最佳实践