Linux网络管理员手册(9) 第九章 各种网络应用程序 inetd服务器、rlogin
Linux网络管理员手册(9) 第九章 各种网络应用程序 inetd服务器、rlogin Linux网络管理员手册(9)
gohigh@shtdu.edu.cn
第九章 各种网络应用程序
在成功地设置好IP和解析器以后,我们现在转过来讨论你想在网络上提供的服务。这一章讲解了一些简单网络应用程序的配置,包括inetd服务器、以及rlogin族中的程序。象网络文件系统(NFS)以及网络信息系统(NIS)所基于的远程过程调用接口也将概要地给予讨论。然而,NFS和NIS的配置内容太多,将用独立的章节进行描述。电子邮件和网络新闻(netnews)也将用独立的章节给予讨论。
当然,我们不可能在本书中讨论所有的网络应用程序。如果你要安装一个这里没有讨论过的程序,比如说,talk、gopher或Xmosaic,请参考它们的手册以获得详细信息。
9.1 inetd超级服务器(super-server)
通常,服务是由所谓的后台程序{或称端口监控程序}daemons执行的。一个端口监控程序是一个程序,它打开一个端口,并且等待进入的连接。如果来了一个连接,它就创建一个子进程来接纳这个连接,而父进程继续监听更多的请求。这个概念有个缺点,即对于所提供的每个服务,就必须运行一个监听某个端口上连接发生的端口监控程序,这通常意味着象交换空间那样的系统资源的浪费。
因而,几乎所有的UN*X安装程序运行一个“超级服务器”,它为许多服务创建套接字,并且使用select(2)系统调用同时监听所有这些端口。当远程系统请求一个服务时,超级服务器注意到这点并且会产生该端口的服务器程序。
常用的超级服务器是inetd,即Internet Daemon(Internet端口监听程序)。它是在系统引导时启动的,并且从名为/etc/inetd.conf的启动文件中取得它所要管理的服务的列表。除了上面所述的那些调用的服务器程序以外,还有许多由inetd自己处理的一般服务,称作内部服务(internal services)。这些包括简单地产生字符串的chargen、返回系统日期时间的daytime。
该文件中的一行是由以下几个字段组成的:
service type protocol wait user server cmdline
每个字段的含义如下:
service
给出服务的名称。通过查找/etc/services文件,服务名称可以转换成一个端口号。这个文件将在services和protocols文件一节(10.3节)中给予描述。
type
指定一个套接字类型,或者是stream(对于基于连接的协议)(for connection-oriented protocols)或者是dgram(对于基于数据报的协议)(for datagram protocols)。因此对于基于TCP的服务总是使用stream,而基于UDP的服务总是使用dgram。
protocol
命名服务所使用的传输协议。这必须是protocols文件中能找到的有效协议名称,也将在下面给出解释。
wait
该选项只用于dgram套接字。它可以是wait或nowait。如果指定了wait,那么对于指定的端口inetd任何时候只执行一次服务器程序。否则的话,在执行了指定的服务器以后,它将立刻在这个端口上进行监听。
这对于读取所有进入的数据报,然后退出的“单线程”服务器程序来讲是很有用的。许多RPC服务器程序是这种类型的,因而对它们需指定为wait。对于相反的类型,“多线程”服务器程序允许无限数量的例程并发运行;这是很少用到的。这种服务器程序需指定为nowait。stream套接字应该总是使用nowait。
user
这是用户的登录id,用户的进程就在其下运行。这通常是root用户,但某些服务可以使用不同的帐号。这里使用最小特权原则是个很好的主意,这是指你不应该在一个享有特权的帐号下运行一个命令,如果该程序不需要特权也能正常工作的话。例如,NNTP新闻服务器程序将作为news运行,而可能引起安全危险问题的服务(比如tftp或finger)经常作为nobody运行。
server
给出将被执行的服务器程序的全路径。内部服务用关键字internal标记。
cmdline
这是传给服务器程序的命令行(参数)。这包括参数0 –命令名。通常,这是是服务器程序名,除非当使用一个不同的名字调用时,该程序有不同的行为。对于内部服务该字段是空的。
#
# inetd services
ftp stream tcp nowait root /usr/sbin/ftpd in.ftpd -l
telnet stream tcp nowait root /usr/sbin/telnetd in.telnetd -b/etc/issue
#finger stream tcp nowait bin /usr/sbin/fingerd in.fingerd
#tftp dgram udp wait nobody /usr/sbin/tftpd in.tftpd
#tftp dgram udp wait nobody /usr/sbin/tftpd in.tftpd /boot/diskless
login stream tcp nowait root /usr/sbin/rlogind in.rlogind
shell stream tcp nowait root /usr/sbin/rshd in.rshd
exec stream tcp nowait root /usr/sbin/rexecd in.rexecd
#
# inetd internal services
#
daytime stream tcp nowait root internal
daytime dgram udp nowait root internal
time stream tcp nowait root internal
time dgram udp nowait root internal
echo stream tcp nowait root internal
echo dgram udp nowait root internal
discard stream tcp nowait root internal
discard dgram udp nowait root internal
chargen stream tcp nowait root internal
chargen dgram udp nowait root internal
图9.1 一个/etc/inetd.conf样本文件。
图9.1给出了inetd.conf的一个样本文件。finger服务被注释掉了,所以这个服务就不存在了。这样做通常是为了安全方面的原因,因为它可能被入侵者用来获取你系统上的用户名。
tftp同样也被注释掉了。tftp实现原始文件传输协议(Primitive File Transfer Protocol)它允许无须口令检查直接从你的系统上传输任何可读的文件等等。这对于/etc/passwd文件尤其有害,当没有使用影子口令文件时就更没的说了。
TFTP通常用于无盘客户以及X终端从一个引导服务器上下载代码。如果由于这个原因你需要运行tftpd的话,请通过在tftpd的命令行上增加那些目录名来限制它们的范围到客户获取文件的目录中。见例子中的第二个tftp行所示。
9.2 tcpd访问控制工具
由于对网络访问开放一个计算机包含许多安全方面的风险,所以应用程序被设计成能够防卫几种类型的攻击。然而,其中的某些防卫可能有缺陷(最为彻底的例子是RTM Internet 蠕虫),或者并不区分请求能被接受的特定服务的安全主机与请求将被拒绝的不可靠的主机。上面我们已经概要地讨论过了finger和tftp服务。因此,人们想限制这些服务只有“可信的主机”才能访问,而使用通常的设置是不可能做到的,对于这些服务inetd要么提供给所有的客户,要么一个客户都不能使用。
为了达到此目的,一个有用的工具是tcpd,[1] 即所谓的端口监控程序包装器(daemon wrapper)。对于你想监视和保护的TCP服务,将调用这个tcpd而不是服务器程序。tcpd将请求记录到syslog后台程序,检查远程主机是否允许使用那个服务,并且只有检查通过时才会执行真正的服务器程序。注意,这对于基于UDP的服务不起作用。
例如,要包装finger daemon时,你必须改变indetd.conf中的相应行成为
# wrap finger daemon
finger stream tcp nowait root /usr/sbin/tcpd in.fingerd
不增加任何访问控制,这将如通常的finger设置一样展现在客户面前,除了任何请求将被记录到syslog的auth设施中。
访问控制是通过两个文件/etc/hosts.allow和/etc/hosts.deny来实现的。它们分别含有对特定服务和主机允许和禁止访问的条目。当tcpd处理来自名为biff.foobar.com的客户主机的服务请求时,比如说是finger服务请求,它将hosts.allow和hosts.deny(以这个次序)文件中扫描匹配这个服务和主机的条目。如果在hosts.allow中找到了匹配条目,就准予访问,而不管hosts.deny中的任何条目了。如果在hosts.deny中发现了匹配的条目,该请求将通过关闭这个连接而被拒绝。如果都没有找到匹配的条目,也准予访问。
在访问文件中的条目看上去象这样:
servicelist: hostlist [:shellcmd]
servicelist是来自于/etc/services中的服务名称的一个列表,或者是关键字ALL。要匹配除了finger和tftp的所有服务时,使用“ALL EXCEPT finger, tftp”。
hostlist是一个主机名或IP地址的列表,或者是关键字ALL、LOCAL或UNKNOWN。ALL匹配任何主机,而LOCAL匹配不含有点的主机名。[2] UNKNOWN匹配于任何名字或地址查询失败的主机。一个以点开头的名字与域名等于这个名字的主机匹配。例如,.foobar.com与biff.foobar.com相匹配。也有对IP网络地址和子网号的规定。详细信息请参见hosts_access(5)手册页。
除了本地主机以外,要限制所有主机对finger和tftp服务的访问,就将下面一行加入到/etc/hosts.deny文件中,并且/etc/hosts.allow文件为空:
in.tftpd, in.fingerd: ALL EXCEPT LOCAL, .your.domain
可选的shellcmd字段可以含有一个shell命令,当条目匹配时将被调用。这对于设置可以使潜在的入侵者暴露的陷阱是很有用的:
in.ftpd: ALL EXCEPT LOCAL, .vbrew.com : \
echo "request from %d@%h" >> /var/log/finger.log; \
if [ %h != "vlager.vbrew.com" ]; then \
finger -l @%h >> /var/log/finger.log \
fi
%h和%d参数被tcpd分别展开成客户的主机名和服务名称。详细信息请参见hosts_access(5)手册页。
9.3 services和protocols文件
确定的“标准”的服务端口号是在“Assigned Numbers”RFC中定义的。为了使得服务器和客户程序能够将服务名称转换成这些号码(数值),每个主机起码要保存这个表的一部分;它被存于文件/etc/service中。每个条目是由象下面这行一样组成的:
service port/protocol [aliases]
这里,service指定服务的名称、port定义提供该服务的端口、protocol定义使用何种传输协议。通常protocol是udp或tcp两者之一。为多种协议提供一个服务是可能的,同样,只要协议不同,在相同的端口上提供不同的服务也是可能的。Aliases字段用于为相同的服务指定另一个可选用的名称。
通常,你无须更改你的Linux系统上的随同网络软件而来的services文件。不过,我们下面文件中给出一个小例外。
# The services file:
#
# well-known services
echo 7/tcp # Echo
echo 7/udp #
discard 9/tcp sink null # Discard
discard 9/udp sink null #
daytime 13/tcp # Daytime
daytime 13/udp #
chargen 19/tcp ttytst source # Character Generator
chargen 19/udp ttytst source #
ftp-data 20/tcp # File Transfer Protocol (Data)
ftp 21/tcp # File Transfer Protocol (Contr
telnet 23/tcp # Virtual Terminal Protocol
smtp 25/tcp # Simple Mail Transfer Protocol
nntp 119/tcp readnews # Network News Transfer Protoco
#
# UNIX services
exec 512/tcp # BSD rexecd
biff 512/udp comsat # mail notification
login 513/tcp # remote login
who 513/udp whod # remote who and uptime
shell 514/tcp cmd # remote command, no passwd use
syslog 514/udp # remote system logging
printer 515/tcp spooler # remote print spooling
route 520/udp router routed # routing information protocol
注意,例如,对于TCP和UDP,都在端口7上提供了echo服务,并且端口512被用于两个不同的服务,即在UDP上是COMSAT daemon(它用于提醒用户有新邮件到达,见xbiff(1x)),在TCP上是用于远程执行(rexec(1))。
与services文件相类似,网络库文件需要有一个方法来将协议名—例如,那些用于services文件中的—转换成为能够被别的主机的IP层理解的协议号。这是通过在/etc/protocols文件中查找协议名来做到的。这个文件每行包含一个条目,每个条目包括一个协议名和一个相关的号码。与/etc/services文件相比,就更不用对这个文件作任何改动了。下面给出一个样本文件:
#
# Internet (IP) protocols
#
ip 0 IP # internet protocol, pseudo protocol number
icmp 1 ICMP # internet control message protocol
igmp 2 IGMP # internet group multicast protocol
tcp 6 TCP # transmission control protocol
udp 17 UDP # user datagram protocol
raw 255 RAW # RAW IP interface
9.4 远程过程调用
客户-服务器应用程序的一个非常普通的机制是有RPC提供的,远程过程调用(Remote Procedure Call)软件包。RPC是由Sun Microsystems开发出来的,是工具和库函数的一个汇集。建立在RFC上的重要应用程序是NFS—网络文件系统、以及NIS—网络信息系统,这两者将在后面的章节中给予介绍。
一个RPC服务器是由一个过程的集合组成,客户可以通过向服务器发出一个带有过程参数的RPC请求来调用这些过程。服务器将代表客户调用指定的过程,如果有任何返回值时就返回它。为了与机器无关,所有在客户和服务器之间交换的数据都被发送者转换成所谓的外部数据表示格式(External Data Representation format)(XDR),并有接收者在转回机器本地表示方式。
有时候,对一个RPC应用程序的改进会给过程调用的接口带来不兼容性的改变。当然,只是简单地改变服务器将导致所有期望原先行为的应用程序不能使用。因此,RPC程序有相应的版本号,通常是从1开始的,并且随着每一个新版本的RPC接口这个计数值将增加。经常,一个服务器可能同时提供几个版本;因此客户通过在他们请求中的版本号来指定他们想使用服务的哪个实现。
在RPC服务器和客户之间的网络通信有些奇特。一个RPC服务器提供一个或多哥过程的集合;每个集合被称作一个程序(program),并且唯一地由一个程序号(program number)指定。映射服务名到程序号的一个列表通常存于/etc/rpc中。它的一个摘要在下面图9.2中给出。
#
# /etc/rpc – miscellaenous RPC-based services
#
portmapper 100000 portmap sunrpc
rstatd 100001 rstat rstat_svc rup perfmeter
rusersd 100002 rusers
nfs 100003 nfsprog
ypserv 100004 ypprog
mountd 100005 mount showmount
ypbind 100007
walld 100008 rwall shutdown
yppasswdd 100009 yppasswd
bootparam 100026
ypupdated 100028 ypupdate
图9.2 一个/etc/rpc样本文件
在TCP/IP网络中,RPC的作者所面临的问题是将程序号映射到普通的网络服务上。他们为每个程序的每个版本选择同时提供TCP和UDP端口服务。一般来讲,RPC应用程序将使用UDP来发送数据,并且仅当数据不能放入单个UDP数据报时才转回来使用TCP传输数据。
当然,客户程序必须有一个方法来找出哪个程序号映射到哪个端口上。如果使用一个配置文件来做这事将显得太不灵活了;因为RPC应用程序不使用保留的端口,无法保证一个原先被我们的数据库应用程序使用的端口没有被某个其它进程占用。因此,RPC应用程序抓取它能得到的任何端口,并且用所谓的portmapper daemon进行登记注册。后者为所有运行在它的机器上的RPC服务器充当代理的角色:一个希望用给出的程序号联系一个服务的客户将首先询问服务器主机上的portmapper,portmapper将返回能够到达这个服务的TCP和UDP端口号。
这个方法有一特别的缺点,它会带来一个失败点,正象在标准Berkeley服务中inetd端口监控程序一样。然而,这个情况甚至有些更糟,因为当portmapper死去时,所有的RPC端口信息都将丢失;这通常意味着你必须手工重新启动所有的RPC服务器程序,或者干脆重新启动整个机器。
在Linux中,portmapper被称作rpc.portmap并且在/usr/sbin中。它是从rc.inet2中启动的,其它可以确定的是portmapper不用做任何配置工作。
9.5 配置r命令
有许多命令用于在远程主机上执行命令。这些是rlogin、rsh、rcp以及rcmd。它们都在远程主机上产生一个shell并允许用户执行命令。当然,客户需要在执行命令的主机上有一个帐号。因此所有这些命令都要执行授权认证过程。通常,客户将告知服务器用户的登录名,服务器随之会请求一个使用常用方法鉴定的口令。
然而,有时候希望放宽对特定用户的认证检查。比如,如果你必须频繁地登录进你的LAN上的其它机器上,你可能希望不要每次都键入密码而被接纳。
禁用授权认证仅在有很少一些口令数据库是同步的主机的情况下、或是对于很少部分特权用户他们由于管理方面的原因需要访问许多主机的情况下才是可取的。每当你想要允许人们不用指定登录id或口令就能登录进你的主机时,确信你没有意外地授权准许别人访问你的机器。
对于r命令有两种方法来禁用认证检查。一个方法是超级用户允许特定的或所有的用户在特定的或所有的主机上(后者肯定是个坏点子)登录而无须键入口令。这种访问是由/etc/hosts.equiv文件控制的。它含有等同于在本地主机上的用户的一张主机和用户名列表。另一个方法是一个用户在特定的主机上准许其他用户访问她的帐号。这些可以在用户的主目录中列于.rhosts文件里。考虑到安全方面的原因,这个文件必须由该用户或超级用户掌管,并且不能是一个符号连接,否则的话它将被忽略掉。[3]
当一个客户请求一个r服务时,就会在/etc/hosts.equiv文件中搜寻她的主机和用户名,然后会在她想作为其登录的用户的.rhosts中搜寻。作为一个例子,假设janet是在gauss上工作并且试图在euler上登录进joe的帐号中。在下面,我们将Janet看作为一个客户用户,而将Joe看作是一个本地用户。现在,当Janet在gauss上键入下面一行时
$ rlogin –l joe euler
服务器将首先检查hosts.equiv如果Janet被准许自由访问,并且如果这个检查失败了,就会在joe主目录的.rhosts文件中查找她。
euler上的hosts.equiv文件看上去象这样:
gauss
euler
-public
quark.physics.groucho.edu andres
每一个条目由一个主机名和一个可选的用户名组成。如果一个条目上只有一个主机名,那么那个主机上的所有用户使用他们的本地帐号都将被接受而无须作任何检查。在上面的例子中,当从gauss过来时,Janet将被允许登录到她的帐号janet中,对于任何其他用户也是这样的,除了root用户。然而,如果Janet想以joe登录时,她将象平常一样得到一个输入口令的提示信息。
如果一个主机名后跟一个用户名的话,就如上面例子文件中的最后一行,那么除了root帐号以外,这个用户将可以无口令地访问其他所有的帐号。
主机名前也可以有一个减号,就象条目“-public”。这表明对于public上的所有帐号都需要授权认证,而不管个别用户在它们的.rhosts文件中被准许访问。
.rhosts文件的格式与hosts.equiv的格式是一样的,但是意思稍微有些不同。考虑euler上Joe的.rhosts文件:
chomp.cs.groucho.edu
gauss janet
第一个条目准许joe在从chomp.cs.groucho.edu登录进来时能自由地访问,但并不影响(侵犯)euler或chomp上的任何其它帐号。第二个条目稍微有些不同,它同意janet在从gauss登录进来时可以自由访问Joe的帐号。
注意,客户的主机名是通过逆向映射呼叫者的地址到名字而获得的,所以这个特性对于解析器未知的主机来说是没有用的。在以下几种情况下,客户的主机名被认为是与hosts文件中的名字匹配的:
客户的规范主机名(不是一个别名)与文件中的主机名相匹配。
如果客户主机名是一个全资域名(比如当你正运行着DNS时解析器的返回值),并且与hosts文件中的主机名不是逐字符匹配的,那么就与用本地域名扩展的主机名相比较。
注释
[1] 由Wietse Venema编制,wietse@wzv.win.tue.nl。
[2] 通常只有从/etc/hosts中查到的本地主机名不含有点。
[3] 在一个NFS环境当中,你可能需要给它一个444的属性保护,因为超级用户对通过NFS加载的磁盘上文件的访问经常是非常严格的。
[4] 注意,当某人试图以root登录时,hosts.equiv文件是不会被搜寻的。