Chinaunix首页 | 论坛 | 博客
  • 博客访问: 231153
  • 博文数量: 59
  • 博客积分: 1215
  • 博客等级: 少尉
  • 技术积分: 575
  • 用 户 组: 普通用户
  • 注册时间: 2011-11-09 02:18
文章分类

全部博文(59)

文章存档

2012年(53)

2011年(6)

分类: C/C++

2012-02-20 21:04:40

守护进程inetd 和 xinetd
2011-08-29 16:41

从理论上说,UNIX® 是内核,或者说低层软件,它控制对文件系统、内存和处理器等计算机资源的访问。但是,用更通俗的话来说,UNIX 是指在操作系统上运行的一整套软件。实际上,通常说的 “它是一台 UNIX 机器” 是指系统的基础功能:UNIX 机器通常提供 shell 界面、并行访问、强大的安全性和各种连网的服务。

常用缩写词DNS:域名系统FTP:文件传输协议HTTP:超文本传输协议IMAP:因特网信息访问协议POP:邮局协议SMTP:简单邮件传输协议TCP:传输控制协议UDP:用户数据报协议

实际上,UNIX(内核等)被选用的原因通常是它的连网应用。FTP、POP、SMTP 和 HTTP 最初都是在 UNIX 上实现的,而且一直在 UNIX 上使用。UNIX 系统还通过运行服务(常常称为守护进程 )实现各种功能,包括与中心时钟执行同步(网络时间协议)、交换新闻(网络新闻传输协议)、把主机名解析为 IP 地址(DNS)等。在大多数 UNIX 机器上的 /etc/services 中可以找到常用的一部分服务。这个文件与 清单 1相似。



ftp 21/tcp fsp 21/udp fspd ssh 22/tcp ssh 22/udp telnet 23/tcp smtp 25/tcp mail

/etc/services 中的每个条目列出服务的名称;服务使用的端口号和协议(TCP 或 UDP);服务的别名(可能没有,也可能有多个别名)。每个系统守护进程都通过检查 /etc/services 寻找它提供服务时要使用的端口和协议。

例如,处理入站电子邮件的守护进程会寻找 “smtp”(服务名称)或 “mail”(别名之一),在端口 25 上监听到达的 TCP 连接。类似地,远程登录守护进程在文件中搜索 “ssh”,在端口 22 上监听到达的 TCP 连接。

端口和 FSP

根据国际因特网地址分配委员会(Internet Assigned Numbers Authority,IANA)的规定,端口 22 和 25 分别是 Secure Shell (SSH) 和 SMTP 的规范端口。系统管理员可以在每个守护进程的配置文件中把这些中规中矩的设置改为其他端口,这有助于降低受到攻击的风险。毕竟,如果无法找到服务,就无法攻击它。文件服务协议 (FSP) 是一个异步文件传输协议。因为不需要持久的连接,所以它支持并行传输。但是,现代系统上很少提供 FSP。

简单地说,TCP 在两台机器之间建立持久的连接。此外,TCP 连接是可靠的,也就是说两台机器相互协作,保证数据的传输。相反,UDP 是不可靠的,这意味着数据可能无法到达目的地。发送方机器传输数据,然后就不管了。可以把端口号看作一个惟一的地址。它把通信流转发给远程机器上特定的目的地。如果说机器的主机名相当于城市街区,端口号就相当于街道地址。

如果某台机器是您公司的中心服务器,或者你们只使用一个服务器,那么系统可能运行 5 个、10 个甚至更多守护进程。例如,小公司的服务器可能运行多个服务,分别负责与世界时钟同步、提供 Web 页面、传输电子邮件、支持远程 shell 访问、打印页面、传输文件、连接数据库、监视系统的稳定性、提供域名以及通过 NFS 共享文件。这种配置并不少见,这主要是因为守护进程的开销不大。守护进程通常设计为在空闲时休眠,等待请求。当服务请求出现时,守护进程醒来,响应并处理请求,然后继续休眠。

尽管如此,大量休眠的进程仍然会影响系统性能。因此,如果预期会经常请求某一服务,比如有稳定的 Web 访问请求,那么有必要具有一个长期运行的守护进程。否则,最好把守护进程重新配置为根据需要执行。

但是,系统如何提供随时可用的服务并在需要的时候启动?解决方案是使用代理服务,它预测到达的各种请求,根据后续处理的需要启动适当的服务。在 UNIX 和 Linux® 系统上,这个代理称为 inetd。

给定一个服务列表,inetd 会监视对这些服务的端口和协议的请求。当发生活动时,inetd 把入站请求映射到标准输入 (stdin)、标准输出 (stdout) 和标准错误 (stderr),并启动适当的守护进程。服务处理数据并终止。inetd 把资源消耗保持在最低水平,并且让守护进程更容易编写。图 1 显示了运行多个守护进程但没有运行 inetd 的系统和运行 inetd 作为代理的系统之间的差异。



 

在上半部分,每个服务作为一个单独的长期的进程(即守护进程 )运行。每个守护进程监听特定端口上的入站请求并处理它们。在下半部分,inetd 监听许多端口并在接收到请求时启动服务。服务处理请求并退出。有一些服务例外。例如,传输电子邮件的 SMTP 服务器通常独立地运行。

根据它的作用,inetd 常常被称为 “超级服务员”。在近几年,inetd 已经被它的变体 xinetd 替代了。这两个软件的用途是相同的,但是后者更安全并提供许多特性,可以在系统负载过重时限制访问。inetd 和 xinetd 的配置相似,但是不完全相同。系统可以运行inetd 或 xinetd,但是不能同时运行两者。因为后者更安全,它是首选的,所以本文后面一直使用它。

与本专栏讨论过的许多其他软件一样,xinetd 是开放源码的,很容易构建在 UNIX 以及 OpenBSD 和 Linux 等变体上。到 2009 年 10 月底,xinetd 的最新版本是 2.3.14,可以从 xinetd 主页获取它(参见 参考资料)。下载 xinetd 的源代码之后,解压压缩文件,运行配置脚本(见 清单 2)并构建软件。在安装 xinetd 之前,一定要备份 inetd 配置(如果有的话),然后禁用和/或删除 inetd。禁用 inetd 的步骤取决于使用的 UNIX 变体;参见系统的 inetd 手册页。执行这个修改很可能需要超级用户访问权。



$ wget $ tar xgz xinetd-2.3.14.tar.gz $ cd xinetd-2.3.14 $ ./configure checking build system type... i686-pc-linux-gnu checking host system type... i686-pc-linux-gnu checking target system type... i686-pc-linux-gnu ... $ make cd libs/src/portable ; make CC='gcc' CFLAGS='-g -O2 -I../../include' install make[1]: Entering directory `/home/strike/tmp/xinetd-2.3.14/libs/src/portable' gcc -g -O2 -I../../include -c -o difftime.o difftime.c ... $ # Disable and/or remove inetd $ sudo make install

同样,启用 xinetd 让它在每次系统重新引导时启动的步骤因系统而异。

如果您的 UNIX 版本有包管理器,还可以从预构建的二进制包直接安装 xinetd。例如,在 Ubuntu Linux 上,可以用一个命令禁用inetd 并安装和启用 xinetd:

$ sudo apt-get install xinetd

无论如何安装和启用 xinetd,如果以前运行过 inetd,就必须把 inetd 配置文件 inetd.conf 转换为与 xinetd 兼容的文件。可以手工地执行转换,也可以使用 xinetd 提供的转换脚本替您修改文件:

$ xconv.pl < /etc/inetd.conf > /etc/xinetd.conf $ mv /etc/inetd.conf /etc/inetd.conf.sav

Xconv.pl 是 xinetd 提供的 Perl 脚本。后一个步骤(把 inetd 配置文件转移到标准位置之外)只是一项预防措施。

可以完全在 /etc/xinetd.conf 中配置 xinetd。但是,按照惯例,通常在这个文件中提供默认设置,并在特殊目录 /etc/xinetd.d 中包含多个配置文件 — 每个服务一个文件。例如,下面是 Ubuntu 上安装的 xinetd 配置文件:

defaults { log_type = SYSLOG daemon info } includedir /etc/xinetd.d

defaults 提供 xinetd 控制的所有 服务的值。服务可以覆盖这些全局默认值。在这里,log_type 的默认值指定每个守护进程应该把日志条目发送到哪里(如果启用日志的话)。SYSLOG 选项把输出发送到 syslog(中心系统日志)。info 要求只记录信息性消息。其他值包括 emerg、alert、crit、err、warning、notice 和 debug。第一个值 emerg 从 xinetd 生成最少的输出;最后一个值 debug 提供最详细的输出。如果在从 xinetd 启动某个服务时遇到了问题,可以启用更详细的日志选项以帮助判断问题的原因。

/etc/xinetd.d 中的文件采用与 xinetd.conf 相同的格式。其中有一个操作,包含零个、一个或更多操作数,还有一组放在大括号 ({}) 中的变量和值。例如,清单 3 是 /etc/xinetd.d/imap,这是用于 IMAP 服务的条目。(IMAP 是用于读取和管理电子邮件的邮箱协议。它与 POP 相比有一个重要的优点:IMAP 邮箱可以跨任意数量的系统保持同步。)



service imap { socket_type = stream protocol = tcp wait = no user = root only_from = 198.72.5.0 localhost banner = /usr/local/etc/deny_banner server = /usr/local/sbin/imapd }

这是一个常见的服务配置文件。我们逐行看一下:

第一行指定这是一个服务并给服务取一个名称。socket_type 描述连接如何工作,常常是 stream(用于 TCP 连接)或 dgram(用于 UDP 服务)。wait 控制 xinetd 是每次处理一个连接 (wait=yes),还是每次处理多个连接 (wait=no)。user 指定守护进程应该作为哪个用户运行。这个用户常常是根用户(超级用户),但是某些服务最好或必须作为服务的创建者运行。only_from 指定哪些系统可以对这个服务发出请求。在这里,只允许 198.72.5 子网上的系统和本地主机使用 IMAP 服务。最右边的 0 作为通配符;允许 IP 地址前缀为 198.72.5 的任何系统请求服务。可以使用多种表示法指定系统;详情参见 xinetd.conf 手册页。(输入 man 5 xinetd.conf。)如果禁止访问,就把 /usr/local/etc/deny_banner 文件的内容发送给客户机。最后,server 指定允许访问时运行的可执行程序。

服务的配置可能非常丰富。可以只在一天中的特定时间段提供服务(access_times 选项),或者只在机器的负载(以平均负载为准)低于阈值时提供服务。还可以把到达的服务请求转发到另一台服务器(redirect 选项)。

xinetd 还支持 chroot 选项。chroot 选项改变进程的根目录。通过 chroot 设置新的根目录之后,根目录及其子目录之外的文件就相当于不存在了。换句话说,如果使用 chroot 把根目录改为 /tmp/fake_root,那么进程就无法访问 /tmp/fake_root 之外的所有文件系统资源。chroot 可以把服务能够访问的资源与系统的其余部分分隔开。例如,可以在 chroot 下运行 FTP 服务,这样任何人都无法访问新的根目录之外的文件。(这实际上是 FTP 的最佳实践。)

回页首

为了演示 xinetd 如何把应用程序转换为守护进程,我们来编写一个 Ruby 脚本,它返回它能够访问的文本文件的索引。这个脚本见清单 4



#! /usr/bin/env ruby txtfiles = File.join( "/tmp/xinetd/", "**", "*.txt") Dir.glob( txtfiles ).each do |filename| puts "#{filename}" end

必须按以下规则启用新的服务:

为服务选择一个未使用的超过 1024 的端口。(端口 1-1024 为超级用户保留。)作为超级用户编辑服务目录 /etc/services,添加服务的名称、端口和协议。例如,可以添加条目 find 11000/tcp,表示这个 Ruby 脚本在端口 11000 上运行,使用 TCP 协议。在 /etc/xinetd.d 中为服务创建一个条目。Ruby 脚本的条目可以像下面这样:
service find { socket_type = stream protocol = tcp user = martin wait = no server = /tmp/xinetd/find.rb log_type = SYSLOG daemon debug }

虽然有一些小差异,但是这个片段看起来应该很熟悉。这个脚本作为用户 martin 运行,因为它不需要特殊的特权。一般来说,应该提供尽可能少的特权 — 不仅是在这里,在授予对任何系统资源的访问权时都应该这样。对于 TCP 协议服务,必须设置 wait=no。server 指向要运行的脚本或可执行程序,log_type 指定更高的日志记录级别,这有助于解决服务中的任何问题。

重新启动 xinetd,或者向它的进程发送一个重新设置信号。要想重新启动 xinetd,应该在 /etc/init.d 或系统保存启动脚本的地方寻找控制脚本。运行下面这样的命令:
$ sudo /etc/init.d/xinetd restart

另一种方法是向 xinetd 守护进程发送重新设置信号。信号 SIGHUP 让 xinetd 重新读取它的配置,并且根据新的参数,可能会关闭连接。使用的命令是:

$ sudo pkill -SIGHUP xinetd

如果系统没有 pkill(它根据进程名寻找进程 ID),那么使用 ps aux | grep xinetd 寻找进程号,然后使用 sudo kill -SIGHUP pid,其中的 pid 是进程 ID。

为了测试这个新服务,创建一个名为 /tmp/xinetd 的目录,创建 Ruby 脚本并把它保存在 /tmp/xinetd/find.rb 中。用 chmod +x /tmp/xinetd/find.rb 把这个文件设置为可执行的。接下来,创建一些目录和文本文件:

$ mkdir a b c $ touch a/d.txt b/e.txt

现在可以测试新服务。当端口 11000 上出现入站连接时,xinetd 启动 Ruby 脚本。发送到标准输出的任何脚本输出会被发送到发出请求的机器上的标准输出。这个脚本不需要输入,但是如果需要,发出请求的机器上的标准输入会被传递给脚本。Telnet 提供一种连接任何服务的简便方法:

$ telnet localhost 11000 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. /tmp/xinetd/b/e.txt /tmp/xinetd/a/d.txt Connection closed by foreign host.

成功了!端口打开了,控制被传递给脚本,脚本生成了预期的输出。

回页首

xinetd 有许多优点。它只在需要时运行守护进程,这可以节省资源。它提供一个额外的安全层,可以通过 “修改根目录” 把服务隔离在一个目录中。最重要的是,它实际上可以把任何脚本或程序转换为服务。但是要注意一点:如果您的服务非常受欢迎,应该考虑用C 等高效的语言重写它。处理请求越快,性能就越好。

阅读(1993) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~