在上一篇文章中,我们讲了如何在 Linux 上用 Python 编写一个守护进程。
主要原理是利用 Linux 的 fork 函数创建一个进程,然后退出父进程,生成的子进程就会成为一个守护进程。细心观察的可能会发现,这个守护进程的运行身份,就是执行这个程序的用户。如果把这个守护程序加入到系统的服务项,那么这个守护程序的执行身份通常就是 root。
这里就出现了一个问题。root 的权限比较大,如果通过 root 身份的守护程序来处理请求,危险性还是比较大的。一种比较好的办法,是生成一个身份为 root 的 master 进程,用来接受请求,然后再生成若干个 worker 进程来处理请求。这样,真正处理请求的进程就不需要拥有 root 的全部权限。
事实上,现在很多软件,例如 Nginx、MySQL、Apache、vsftpd 等,基本都是类似的思路。那么,怎么样在 Linux 中更改子进程的运行身份呢?
实际上,Linux 提供了相应的函数。来看一下 Python 代码:
#!/usr/bin/env python
import time
import os
import pwd
import sys
import signal
logfile = "/tmp/d2.log"
# step one,获取希望程序运行的用户名
try:
user = sys.argv[1]
except:
user = raw_input(
"Please input a username in this machine "
"you want to run this program: "
)
if user == "":
sys.exit(-1)
try:
uid = pwd.getpwnam(user)
uid = uid.pw_uid
except:
print "User not exists!"
sys.exit(-1)
# step two,生成 daemon
pid = os.fork()
if pid:
sys.exit(0)
os.setsid()
os.chdir("/")
os.umask(0)
# step three,再次 fork
pid = os.fork()
if pid == 0:
os.setuid(uid)
os.setsid()
os.chdir("/")
os.umask(0)
log = open(logfile, 'a')
log.write(
'Daemon start up at %s\n' %
(
time.strftime(
'%Y:%m:%d',
time.localtime(time.time())
)
)
)
log.close()
def reload(a, b):
log = open(logfile, 'a')
log.write(
'Daemon reload at %s\n' %
(
time.strftime(
'%Y:%m:%d',
time.localtime(time.time())
)
)
)
log.close()
while True:
signal.signal(
signal.SIGHUP,
reload
)
time.sleep(2)
运行这个程序的时候,比如输入:
nginx
这里假设 nginx 是系统中已经存在的用户。
然后使用:
ps aux | grep python
查看系统中正在运行的 Python 程序,可以看到,其中一个 worker 进程的运行身份已经变成了 nginx:
[root@home ~]# ps aux | grep python
root 1139 0.0 0.5 5288 2372 ? Ss 22:40 0:00 python ./d2.py nginx
nginx 1140 0.0 0.5 5288 2360 ? S 22:40 0:00 python ./d2.py nginx
root 1151 0.0 0.1 2336 648 pts/0 S+ 22:50 0:00 grep python
这里可以看到:
root → master
nginx → worker
也就是说,负责处理实际请求的 worker 进程已经不再使用 root 身份。因为身份为 nginx 的进程主要用来处理请求,所以一些属于 root 的权限就不会被这个进程直接调用。同时,还可以进一步通过设置文件权限,对进程能够操作的文件进行限制。这样就可以在一定程度上实现权限控制,减少因为进程权限过大而带来的安全隐患。
评论0
欢迎分享你的看法,也欢迎补充不同的实践经验。