服务器守护进程的运行方式

运行一个服务(例如SSH)最简单的办法就是让它的守护进程在引导的时候就启动,然后一直运行,监听并处理来自客户机的请求。在刚开始,这样的设置 不会有什么问题。但随着服务的增多,这些运行在后台的守护进程会大量消耗系统资源(因为它们一直在运行!),这种消耗常常是没有必要的。举例来说,SSH 服务一天内可能只会被一个管理员用到几次,这样,/etc/sbin/sshd每天空闲的时间甚至接近20个小时。

inetd和xinetd就是为了解决这种矛盾而诞生的。inetd最初由伯克利的专家们开发,这个特殊的守护进程能够接管其他服务器守护进程使用 的网络端口,在监听到客户端请求后启动相应的守护进程,并为这个服务器守护进程建立一条通往指定端口的输入/输出   通道。

inetd的意义在于,系统上不用同时运行多个"没有事做"的守护进程。像SSH、FTP这样平时不怎么用到的服务可以配置为使用inetd,这样 它们可以把监听端口的任务交给inetd。当出现一条FTP连接时,inetd就启动FTP服务的守护进程;同样,当管理员有事找SSH的时 候,inetd就把sshd叫醒。

inetd最初在UNIX系统上被设计,后来被移植到了Linux上。现在绝大多数Linux已经使用了更好的xinetd。相对于inetd而 言,xinetd有以下优点:

更多的安全特性;

针对拒绝服务攻击的更好的解决方案;

更强大的日志管理功能;

更灵活清晰的配置语法。

尽管如此,一些Linux系统仍然在使用inetd。因此在详细讨论xinetd配置之后,本章还会对inetd做简单的介绍。

现在可以把本节的标题补充完整了。服务器守护进程的运行方式有两种:一种是随系统启动而启动,并持续在后台监听连接请求;另一种是借助于 inetd/xinetd,在需要的时候启动,完成任务后把监听任务交还给inetd/xinetd。通常,前者被称为standalone模式,后者被 称为inetd/xinetd模式。(尽管这种叫法听上去有点别扭,但既然大家都这么说,就随大流吧。)

并不是所有的服务器守护进程都支持inetd和xinetd,应用程序必须在编写的时候就加入对这种模式的支持。一些服务器守护进程(例如 sshd、apache2)既支持standalone模式,也能支持inetd/xinetd模式。在接下来几章的服务器配置中会涉及这两种运行模式的 选择。

inetd/xinetd模式的确有很多优点,但事情总不能一概而论。对大型Web站点而言就不应该使用inetd/xinetd模式运行 Apache(当前最流行的Web服务器软件),因为这些服务器访问量巨大,每分每秒都会有新的连接请求,让inetd/xinetd如此频繁地启动和关 闭Apache守护进程会非常糟糕。

对于桌面版本的Linux而言,inetd和xinetd通常都需要手动安装。Ubuntu Linux在其安装源中提供了inetd和xinetd,而openSUSE只提供了xinetd。

配置xinetd

xinetd守护进程依照/etc/xinetd.conf的配置行事。如今的Linux发行版都不鼓励通过直接编辑/etc /xinetd.conf来添加服务,相反用户应该为每个服务单独开辟一个文件,存放在/etc/xinetd.d目录下。查看xinetd.conf可 以看到这一点:

  1. $ cat /etc/xinetd.conf                      
    ##查看/etc/xinetd.conf  
  2. # Simple configuration file for xinetd  
  3. #  
  4. # Some defaults, and include /etc/xinetd.d/  
  5.  
  6. defaults  
  7. {  
  8.  
  9. # Please note that you need a log_type line to
    be able to use log_on_success  
  10. # and log_on_failure. The default is the following :  
  11. log_type = SYSLOG daemon info  
  12.  
  13. }  
  14.  
  15. includedir /etc/xinetd.d 

最后一行使用includedir命令把目录/etc/xinetd.d下的文件包含进来。这样设置的好处是,如果有很多服务需要依靠 xinetd,那么把它们全部写入xinetd.conf中势必会让整个结构看起来一团糟,把服务器配置分类存放有助于管理员理清头绪。

xinetd.conf中的defaults配置段设置了xinetd一些参数的默认值。在上面的例子中,log_type的值被设置为 SYSLOG deamon info,该变量的含义将在后文解释。

安装xinetd后会在/etc/xinetd.d中自动生成一些服务的配置文件。作为例子,下面显示了time服务的配置信息(在/etc /xinetd.d/time文件中配置)。

  1. service time  
  2. {  
  3.         disable         = yes 
  4.         type            = INTERNAL 
  5.         id              = time-stream  
  6.         socket_type     = stream 
  7.         protocol        = tcp 
  8.         user            = root 
  9.         wait            = no 

每个服务总是以关键字service开头,后面跟着服务名。对该服务的配置包含在一对花括号中,以"参数=值"的形式,每个参数占一行。表22.3 列出了xinetd配置的常用参数。

表22.3  xinetd配置的常用参数

   

   

   

id

有意义的字符串

该 服务的唯一名称

type

RPC/INTERNAL/UNLISTED

指 定特殊服务的类型。

RPC用于RPC服务;

INTERNAL用于构

建 到xinetd内部的

服 务;UNLISTED

用 于非标准服务

disable

yes/no

是 否禁用该服务

socket_type

stream/dgram

网 络套接口类型。

TCP服务用stream

UDP服务用dgram

protocol

tcp/udp

连 接使用的通信协议

wait

yes/no

xinetd是否等待守

护 进程结束才

重 新接管该端口

server

路径

服 务器二进制文件的路径

server_args

参数

提 供给服务器二

进 制文件的命令行参数

port

端口号

该 服务所在的端口

user

用户名

服 务器进程应该由

哪 个用户身份运行

nice

数字

服 务器进程的谦让度。

参 考10.7

instances

数字/UNLIMITED

同 时启动的响应数量。

UNLIMITED表示没有限制

max_load

数字

调 整系统负载值。

如 果实际负载超过

值,就停止服务

only_from

IP地址列表

只 接受来自该地址的

连 接请求

no_access

IP地址列表

拒 绝向该IP地址提供服务

log_on_failure

列表值

连 接失败时应该记

录 到日志中的信息

log_on_success

列表值

连 接成功时应该记录

到 日志中的信息


参数id用于唯一标识服务,这意味着可以为同一个服务器守护进程配置不同的协议。上文中的time服务就拥有两个版本的xinetd配置, 另一个用于UDP协议。

参数disable设置是否要禁用该服务。有些时候,管理员只是想列出将来可能会用到的服务,而不是现在就启用它。对这么多行进行注释会让人感到厌 烦,将disable设置为yes就可以简单地禁用该服务。不过,管理员偶尔也会忘记在启用服务的时候把这个选项改回来,如果正在奇怪为什么某项服务没有 被xinetd加载,那么应该首先检查disable选项是否已经被正确地设置为no了。

将wait参数设置为yes意味着由xinetd派生出的守护进程一旦启动就接管端口,xinetd会一直等待,直到该守护进程自己退 出;wait=no表示xinetd会连续监视端口,每次接到一个请求就启动守护进程的一个新副本。管理员应该参考守护进程的手册,或者xinetd的配 置样例来确定使用何种配置。

参数port在绝大多数情况下是不需要的。xinetd根据服务名从/etc/service文件中查找信息,确定该服务使用的端口和网络协议。如 果没有在/etc/service文件中登记该服务,那么也应该手动添加,而不是使用port参数--把信息集中起来管理总是能省去不少麻烦。下面截取了 /etc/service文件中的一部分。

  1. ftp             21/tcp  
  2. fsp             21/udp          fspd  
  3. ssh             22/tcp                       
    # SSH Remote Login Protocol  
  4. ssh             22/udp  
  5. telnet          23/tcp  
  6. smtp            25/tcp          mail 

/etc/service中的每一行对应一个服务,从左到右依次表示:

服务名称。例如ssh;

该服务使用的端口号。例如22;

该服务使用的传输协议。例如tcp;

别名(或者叫"绰号"?)。例如fspd;

注释。例如# SSH Remote Login Protocol。

参数user设置应该以哪个用户身份运行该服务器进程,大部分服务都使用root。有些时候从安全的角度考虑会使用非特权用户(例如 nobody),但这只适用于那些不需要root权利的守护进程。

xinetd会记录连接失败/成功时的信息,用户可以通过定制log_on_failure和log_on_success这两个参数指导 xinetd记录哪些信息。表22.4列出了和这两个参数有关的取值。

表22.4  和日志记录有关的取值

   

   

HOST

二者皆可

记录远程主机的地址

USERID

二者皆可

记录远程用户的ID

PID

log_on_success

记录服务器进程的PID

EXIT

log_on_success

记录服务器进程的退出信息

DURATION

log_on_success

记录任务持续的时间

ATTEMPT

log_on_failure

记录连接失败的原因

RECORD

log_on_failure

记录连接失败的额外的信息

注意:USERID标志会向远程主机询问建立连接的用户信息,这样总会造成明显的延时,因此应该尽可能避免使用USERID。

完成对服务配置的后,使用下面这条命令重新启动xinetd守护进程。

  1. $ sudo /etc/init.d/xinetd restart

举例:通过xinetd启动SSH服务

作为例子,本节将带领读者配置SSH服务的xinetd实现。总的来说,在xinetd中添加服务无非是下面这几步:

(1)修改(增加)配置文件;

(2)停用该服务的守护进程;

(3)重启xinetd使配置生效;

(4)如果需要,从相应的rc目录中移除该服务的启动脚本。

下面就来逐一实现以上各个步骤。首先在/etc/xinetd.d目录下建立文件ssh,包含下面这些内容。

  1. service ssh  
  2. {  
  3.         socket_type     = stream 
  4.         protocol        = tcp 
  5.         wait            = no 
  6.         user            = root 
  7.         server          = /usr/sbin/sshd  
  8.         server_args     = -i  
  9.         log_on_success  += DURATION  
  10.         disable         = no 

注意log_on_success参数允许使用"+="这样的赋值方式,表示在原有默认值的基 础上添加,而不是推倒重来。类似地,也可以使用"-="在默认值的基础上减去一些值。参数的默认值通常在/etc/xinetd.conf中设置。

下一步停用SSH守护进程,为xinetd接管22端口铺平道路。

  1. $ sudo /etc/init.d/ssh stop  
  2.  * Stopping OpenBSD Secure Shell server sshd  
    [ OK ] 

重新启动xinetd使配置生效。

  1. $ sudo /etc/init.d/xinetd restart  
  2.  * Stopping internet superserver xinetd          
    [ OK ]   
  3.  * Starting internet superserver xinetd       
    [ OK ] 

运行netstat -tulnp命令查看22端口的情况,发现xinetd已经顺利接管了SSH通信端口。

  1. $ sudo netstat -tulnp | grep 22             
    ##查看22端口状态  
  2. tcp        0      0 0.0.0.0:22             
    0.0.0.0:*               LISTEN      8356/xinetd 

现在尝试连接本地的SSH服务。对于客户端而言,看上去和standalone方式没有什么不同。

  1. $ ssh localhost -l lewis  
  2. lewis@localhost's password: 

如果在安装SSH服务器的时候选择了随系统启动(通常这是默认配置),那么接下来还要从相应的rc目录中移除SSH服务的启动脚本,否则下次启动系 统的时候xinetd将无法运行。假设系统默认启动到运行级5(可以参考22.1节获取有关运行级的详细信息)。

  1. $ cd /etc/rc5.d/                             
    ##进入相应的rc目录  
  2. $ ls | grep ssh                               
    ##查找SSH启动脚本  
  3. S16ssh  
  4. $ sudo mv S16ssh ../rc_bak.d/S16ssh_rc5_bak 
    ##移动到另一个地方备份起来 

注意:不要随便删除启动脚本,而应该把它移动到另一个地方,并且取一个有意义的名字。这样在以后需要的时候可以方便地找回来。

配置inetd

与xinetd类似,inetd的配置文件是/etc/inetd.conf。在参数的个数上,inetd要比xinetd少很多,因此每个服务只 需要一行就足够了。下面是从/etc/inetd.conf中截取的一部分配置信息。

  1. #echo       stream  tcp     nowait  root    internal  
  2. #echo       dgram   udp     wait    root    internal  
  3. ...  
  4. ident       stream  tcp     wait    identd  
    /usr/sbin/identd    identd  
  5. swat        stream  tcp     nowait.400 root 
    /usr/sbin/swat  swat  
  6. finger      stream  tcp     nowait  nobody  
    /usr/sbin/tcpd  in.fingerd -w  
  7. ... 

各个字段从左至右依次表示:

服务名称。和xinetd一样,inetd通过查询/etc/service获得该服务的相关信息。

套接口类型。TCP用stream,UDP用dgram。

该服务使用的通信协议。

inetd是否等到守护进程结束才继续接管端口。wait表示等待(相当于xinetd的wait = yes),nowait表示不等待,inetd每次接到一个请求就启动守护进程的新副本(相当于xinetd的wait = no)。

运行该守护进程的用户身份。

守护进程二进制文件的完整路径及其命令行参数。和xinetd不同,inetd要求把服务器命令作为第一个参数(例如in.fingerd),然后 才是真正意义上的"命令行参数"(例如-w)。关键字internal表示服务的实现由inetd自己实现。

完成对/etc/inetd.conf的编辑后,需要给inetd发送一个HUP信号,通知其重新读取配置文件。

  1. $ ps aux | grep inetd                         
    ##查找inetd的进程号  
  2. root      4414  0.0  0.1   1908   428 ?       
    S    13:38   0:00 /usr/sbin/inetutils-inetd  
  3. lewis     5186  0.0  0.3   3216   772 pts/0   
    R+   13:39   0:00 grep inetd  
  4. $ sudo kill -HUP 4414