最近Openresty项目增加了一个新的功能,可以在Nginx中开启一个agent进程,这个agent进程不像Nginx的worker进程那样监听服务端口然后对外提供服务,而是继承了master进程的用户权限。
出于安全的考虑,Nginx一般以root身份启动,启动后worker进程通过setuid和setgid以nobody方式运行,只有master进程保留了root身份。而agent进程拥有与master进程相同的权限,便可以实现对Nginx自身的控制,如Nginx的重载等功能。
安装
通过openresty安装
通过openresty仓库下载安装openresty
从源码编译
开启agent进程
在Nginx中,master进程生成worker进程、cache-manager进程、agent进程等都是在init和init_worker两个阶段之间进行的,要启用agent进程,必须要init_by_lua中进行设置。如下所示开启agent进程。
1 |
|
为agent进程添加任务
Nginx以事件驱动的方式工作,时间的来源主要有IO事件和定时器时间两种。agent进程关闭了监听的端口,无法通过网络IO的方式来驱动,貌似只能通过定时器的方式来实现。
方法是在init_worker_by_lua中,通过ngx.timer.at设置定时器,在定时器中完成相应的工作。
1 |
|
当然直接在init_worker_by_lua中执行工作,不用定时器也可以,只是一直在init_worker阶段,功能受到限制,无法使用ngx.socket,ngx.sleep,ngx.thread等API。
利用agent进程实现Nginx自身的控制
如下面的代码,每隔一小时向Nginx的master进程发送重载信号。
1 |
|
这里为了方便直接用os.execute执行,实际代码中os.execute效率比较低。
agent进程实现原理
openresty修改了Nginx源码以实现agent进程的支持。
生成agent进程
在ngx_master_process_cycle中调用ngx_start_privileged_agent_processes生成agent进程。
1 |
|
agent进程执行
生成argent进程后,执行ngx_privileged_agent_process_cycle函数。ngx_privileged_agent_process_cycle会执行ngx_close_listening_sockets关闭所有的监听端口。然后通过ngx_worker_process_init进行一些初始化工作。最后在for循环中和worker进程一样,通过ngx_process_events_and_timers执行相应工作。
1 |
|
agent进程初始化
agent执行ngx_worker_process_init进行进程的初始化时,与普通的worker进程主要区别是没有执行setgid和setuid操作,保持了和master进程相同的权限。
如下面的代码所示,agent进程的ngx_is_privileged_agent为1,因此不会执行setgid和setuid。
1 |
|
总结
- agent进程不监听服务端口,其工作任务需要在init_worker时设置。
- agent继承了master进程的用户权限,可以向master进行发送信号,控制Nginx进行重载,关闭等操作。
- 如果Nginx以root启动,agent进程会拥有root权限,可以控制整个操作系统,使用需非常谨慎。