对话 UNIX

使用 Internet “超级服务员”,inetd 和 xinetd

了解这个用于服务的服务

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 对话 UNIX

敬请期待该系列的后续内容。

此内容是该系列的一部分:对话 UNIX

敬请期待该系列的后续内容。

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

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

清单 1. /etc/services(UNIX 网络服务目录)中常见的条目
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 连接。

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

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

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

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

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

图 1. 运行多个守护进程的系统,一个运行 inetd,一个不运行 inetd
运行多个守护进程的系统,一个运行 inetd,一个不运行 inetd
运行多个守护进程的系统,一个运行 inetd,一个不运行 inetd

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

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

在系统上安装 xinetd

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

清单 2. 安装 xinetd
$ wget http://www.xinetd.org/xinetd-2.3.14.tar.gz
$ 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 要求只记录信息性消息。其他值包括 emergalertcriterrwarningnoticedebug。第一个值 emergxinetd 生成最少的输出;最后一个值 debug 提供最详细的输出。如果在从 xinetd 启动某个服务时遇到了问题,可以启用更详细的日志选项以帮助判断问题的原因。

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

清单 3. /etc/xinetd.d/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 下运行服务

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

清单 4. 生成文本文件索引的 Ruby 脚本
#! /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=noserver 指向要运行的脚本或可执行程序,log_type 指定更高的日志记录级别,这有助于解决服务中的任何问题。

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

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

    $ 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 的更多原因

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


相关主题

  • xinetd:了解关于 xinetd 的更多信息。
  • 了解 daemon 这个词的起源。
  • 对话 UNIX:阅读本系列中的其他部分。
  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。

评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX, Linux
ArticleID=460380
ArticleTitle=对话 UNIX: 使用 Internet “超级服务员”,inetd 和 xinetd
publish-date=01072010