---
title: 第 30 章 网络服务器
part: 部分 IV. 网络通讯
prev: books/handbook/mail
next: books/handbook/firewalls
showBookMenu: true
weight: 35
params:
  path: "/books/handbook/network-servers/"
---

[[network-servers]]
= 网络服务器
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:sectnumoffset: 30
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/handbook/network-servers/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

[[network-servers-synopsis]]
== 概要

本章将覆盖某些在 UNIX(R) 系统上常用的网络服务。话题将会涉及 如何安装、配置、测试和维护多种不同类型的网络服务。本章节中将提 供大量配置文件的样例，期望能够对您有所裨益。

在读完本章之后，您将会知道：

* 如何管理 inetd。
* 如何设置运行一个网络文件系统。
* 如何配置一个网络信息服务器以共享用户帐号。
* 如何通过DHCP自动配置网络。
* 如何配置一个域名服务器。
* 如何设置Apache HTTP 服务器。
* 如何设置文件传输（FTP）服务器。
* 如何使用Samba为 Windows(R) 客户端设置文件和打印服务。
* 如何同步时间和日期，以及如何设置使用NTP协议的时间服务器。
* 如何配置标准的日志守护进程， `syslogd`， 接受远程主机的日志。

在阅读此章节之前，您应当：

* 理解有关[.filename]##/etc/rc##中脚本的基本知识。
* 熟悉基本网络术语。
* 懂得如何安装额外的第三方软件（crossref:ports[ports,安装应用程序. Packages 和 Ports]）。

[[network-inetd]]
== inetd"超级服务器"

[[network-inetd-overview]]
=== 总览

man:inetd[8] 有时也被称作 "Internet 超级服务器"， 因为它可以为多种服务管理连接。 当 inetd 收到连接时， 它能够确定连接所需的程序， 启动相应的进程， 并把 socket 交给它 (服务 socket 会作为程序的标准输入、 输出和错误输出描述符)。 使用 inetd 来运行那些负载不重的服务有助于降低系统负载， 因为它不需要为每个服务都启动独立的服务程序。

一般说来， inetd 主要用于启动其它服务程序， 但它也有能力直接处理某些简单的服务， 例如 chargen、 auth， 以及 daytime。

这一节将介绍关于如何通过命令行选项， 以及配置文件 [.filename]#/etc/inetd.conf# 来对 inetd 进行配置的一些基础知识。

[[network-inetd-settings]]
=== 设置

inetd 是通过 man:rc[8] 系统启动的。 `inetd_enable` 选项默认设为 `NO`， 但可以在安装系统时， 由用户根据需要通过 sysinstall 来打开。 将：

[.programlisting]
....
inetd_enable="YES"
....

或

[.programlisting]
....
inetd_enable="NO"
....

写入 [.filename]#/etc/rc.conf# 可以启用或禁用系统启动时 inetd 的自动启动。 命令：

[source,shell]
....
# /etc/rc.d/inetd rcvar
....

可以显示目前的设置。

此外， 您还可以通过 `inetd_flags` 参数来向 inetd 传递额外的其它参数。

[[network-inetd-cmdline]]
=== 命令行选项

与多数服务程序类似， inetd 也提供了为数众多的用以控制其行为的参数。 完整的参数列表如下：

`inetd [-d] [-l] [-w] [-W] [-c maximum] [-C rate] [-a address | hostname] [-p filename] [-R rate] [-s maximum] [configuration file]`

这些参数都可以通过 [.filename]#/etc/rc.conf# 的 `inetd_flags` 选项来传给 inetd。 默认情况下， `inetd_flags` 设为 `-wW -C 60`， 者表示希望为 inetd 的服务启用 TCP wrapping， 并阻止来自同一 IP 每分钟超过 60 次的请求。

虽然我们会在下面介绍关于限制连接频率的选项， 但初学的用户可能会很高兴地发现这些参数通常并不需要进行修改。 在收到超大量的连接请求时， 这些选项则有可能会发挥作用。 完整的参数列表， 可以在 man:inetd[8] 联机手册中找到。

-c maximum::
指定单个服务的最大并发访问数量，默认为不限。 也可以在此服务的具体配置里面通过``max-child``改掉。

-C rate::
指定单个服务一分钟内能被单个IP地址调用的最大次数， 默认不限。也可以在此服务的具体配置里面通过``max-connections-per-ip-per-minute`` 改掉。

-R rate::
指定单个服务一分钟内能被调用的最大次数，默认为256。 设为0 则允许不限次数调用。

-s maximum::
指定同一 IP 同时请求同一服务时允许的最大值； 默认值为不限制。 您可以通过 `max-child-per-ip` 参数来以服务为单位进行限制。

[[network-inetd-conf]]
=== [.filename]#inetd.conf#

对于 inetd 的配置， 是通过 [.filename]#/etc/inetd.conf# 文件来完成的。

在修改了 [.filename]#/etc/inetd.conf# 之后， 可以使用下面的命令来强制 inetd 重新读取配置文件：

[[network-inetd-reread]]
.重新加载 inetd 配置文件
[example]
====

[source,shell]
....
# /etc/rc.d/inetd reload
....

====

配置文件中的每一行都是一个独立的服务程序。 在这个文件中， 前面有 "#" 的内容被认为是注释。 [.filename]##/etc/inetd.conf## 文件的格式如下：

[.programlisting]
....
service-name
socket-type
protocol
{wait|nowait}[/max-child[/max-connections-per-ip-per-minute[/max-child-per-ip]]]
user[:group][/login-class]
server-program
server-program-arguments
....

下面是针对 IPv4 的 man:ftpd[8] 服务的例子：

[.programlisting]
....
ftp     stream  tcp     nowait  root    /usr/libexec/ftpd       ftpd -l
....

service-name::
指明各个服务的服务名。其服务名必须与[.filename]##/etc/services##中列出的一致。 这将决定inetd会监听哪个port。 一旦有新的服务需要添加，必须先在[.filename]##/etc/services##里面添加。

socket-type::
可以是``stream``、``dgram``、``raw``或者 ``seqpacket``。 ``stream`` 用于基于连接的 TCP 服务；而 ``dgram`` 则用于使用 UDP 协议的服务。

protocol::
下列之一：
+
[.informaltable]
[cols="1,1", frame="none", options="header"]
|===
| 协议
| 说明

|tcp， tcp4
|TCP IPv4

|udp， udp4
|UDP IPv4

|tcp6
|TCP IPv6

|udp6
|UDP IPv6

|tcp46
|Both TCP IPv4 and v6

|udp46
|Both UDP IPv4 and v6
|===
{wait|nowait}[/max-child[/max-connections-per-ip-per-minute[/max-child-per-ip]]]::
`wait|nowait` 指明从inetd 里头调用的服务是否可以自己处理socket. `dgram` socket类型必须使用``wait``， 而stream socket daemons， 由于通常使用多线程方式，应当使用 `nowait`. `wait` 通常把多个 socket 丢给单个服务进程， 而 `nowait` 则 会为每个新的 socket 生成一个子进程。
+
`max-child` 选项能够配置 inetd 能为本服务派生出的最大子进程数量。 如果某特定服务需要限定最高10个实例， 把``/10`` 放到``nowait``后头就可以了。 指定 ``/0`` 表示不限制子进程的数量。
+
除了 `max-child` 之外， 还有两个选项可以限制来自同一位置到特定服务的最大连接数。 `max-connections-per-ip-per-minute` 可以限制特定 IP 地址每分钟的总连接数， 例如， 限制任何 IP 地址每分钟最多连接十次。 `max-child-per-ip` 则可以限制为某一 IP 地址在任何时候所启动的子进程数量。 这些选项对于防止针对服务器有意或无意的资源耗竭和拒绝服务 (DoS) 攻击十分有用。
+
这个字段中， 必须指定 `wait` 或 `nowait` 两者之一。 而 `max-child`、 `max-connections-per-ip-per-minute` 和 `max-child-per-ip` 则是可选项。
+
流式多线程服务， 并且不配置任何 `max-child`、 `max-connections-per-ip-per-minute` 或 `max-child-per-ip` 限制时， 其配置为： `nowait`。
+
同一个服务， 但希望将服务启动的数量限制为十个时， 则是： `nowait/10`。
+
同样配置， 限制每个 IP 地址每分钟最多连接二十次， 而同时启动的子进程最多十个， 应写作： `nowait/10/20`。
+
下面是 man:fingerd[8] 服务的默认配置：
+
[.programlisting]
....
finger stream  tcp     nowait/3/10 nobody /usr/libexec/fingerd fingerd -s
....
+
最后这个例子中， 将子进程数限制为 100 个， 而任意 IP 最多同时建立 5 个连接： `nowait/100/0/5`。

user::
该开关指定服务将以什么用户身份运行。一般而言，服务运行身份是 ``root``。基于安全目的，可以看到有些服务以 ``daemon``身份，或者是最小特权的 ``nobody``身份运行。

server-program::
当连接到来时，执行服务程序的全路径。如果服务是由 inetd内置提供的，以``internal``代替。

server-program-arguments::
当``server-program``调用到时，该开关 的值通过``argv[0]``通过传递给服务而工作。 如果命令行为：``mydaemon -d``，则 ``mydaemon -d``为``server-program-arguments`` 开关的值。同样的，如果服务是由inetd 内置提供的，这里还是 ``internal``。

[[network-inetd-security]]
=== Security

随安装时所选的模式不同， 许多 inetd 的服务可能已经默认启用。 如果确实不需要某个特定的服务， 则应考虑禁用它。 在 [.filename]#/etc/inetd.conf# 中， 将对应服务的那行前面加上 "#"， 然后 <<network-inetd-reread,重新加载 inetd 配置>> 就可以了。 某些服务， 例如 fingerd， 可能是完全不需要的， 因为它们提供的信息可能对攻击者有用。

某些服务在设计时是缺少安全意识的， 或者有过长或压根没有连接请求的超时机制。 这使得攻击者能够通过缓慢地对这些服务发起连接， 并耗尽可用的资源。 对于这种情况， 设置 `max-connections-per-ip-per-minute`、 `max-child` 或 `max-child-per-ip` 限制， 来制约服务的行为是个好办法。

默认情况下，TCP wrapping 是打开的。参考 man:hosts_access[5] 手册，以获得更多关于在各种 inetd 调用的服务上设置TCP限制的信息。

[[network-inetd-misc]]
=== 杂项

daytime、 time、 echo、 discard、 chargen， 以及 auth 都是由 inetd 提供的内建服务。

auth 服务提供了网络身份服务， 它可以配置为提供不同级别的服务， 而其它服务则通常只能简单的打开或关闭。

参考 man:inetd[8] 手册获得更多信息。

[[network-nfs]]
== 网络文件系统（NFS）

网络文件系统是FreeBSD支持的文件系统中的一种， 也被称为 NFS。 NFS允许一个系统在网络上与它人共享目录和文件。通过使用NFS，用户和程序可以象访问本地文件 一样访问远端系统上的文件。

以下是NFS最显而易见的好处：

* 本地工作站使用更少的磁盘空间，因为通常的数据可以存放在一 台机器上而且可以通过网络访问到。
* 用户不必在每个网络上机器里头都有一个home目录。Home目录 可以被放在NFS服务器上并且在网络上处处可用。 
* 诸如软驱，CDROM，和 Zip(R) 之类的存储设备可以在网络上面被别的机器使用。 这可以减少整个网络上的可移动介质设备的数量。

=== NFS是如何工作的

NFS 至少包括两个主要的部分： 一台服务器， 以及至少一台客户机， 客户机远程地访问保存在服务器上的数据。 要让这一切运转起来， 需要配置并运行几个程序。

服务器必须运行以下服务：

[.informaltable]
[cols="1,1", frame="none", options="header"]
|===
| 服务
| 描述

|nfsd
|NFS，为来自NFS客户端的 请求服务。

|mountd
|NFS挂载服务，处理man:nfsd[8]递交过来的请求。

|rpcbind
| 此服务允许 NFS 客户程序查询正在被 NFS 服务使用的端口。
|===

客户端同样运行一些进程，比如 nfsiod。 nfsiod处理来自NFS的请求。 这是可选的，而且可以提高性能，对于普通和正确的操作来说并不是必须的。 参考man:nfsiod[8]手册获得更多信息。 

[[network-configuring-nfs]]
=== 配置NFS

NFS的配置过程相对简单。这个过程只需要 对[.filename]##/etc/rc.conf##文件作一些简单修改。

在NFS服务器这端，确认[.filename]##/etc/rc.conf## 文件里头以下开关都配上了:

[.programlisting]
....
rpcbind_enable="YES"
nfs_server_enable="YES"
mountd_flags="-r"
....

只要NFS服务被置为enable，mountd 就能自动运行。

在客户端一侧，确认下面这个开关出现在 [.filename]##/etc/rc.conf##里头:

[.programlisting]
....
nfs_client_enable="YES"
....

[.filename]##/etc/exports##文件指定了哪个文件系统 NFS应该输出（有时被称为"共享"）。 [.filename]##/etc/exports##里面每行指定一个输出的文件系统和 哪些机器可以访问该文件系统。在指定机器访问权限的同时，访问选项 开关也可以被指定。有很多开关可以被用在这个文件里头，不过不会在这 里详细谈。您可以通过阅读man:exports[5] 手册来发现这些开关。

以下是一些[.filename]##/etc/exports##的例子：

下面是一个输出文件系统的例子， 不过这种配置与您所处的网络环境及其配置密切相关。 例如， 如果要把 [.filename]#/cdrom# 输出给与服务器域名相同的三台计算机 (因此例子中只有机器名， 而没有给出这些计算机的域名)， 或在 [.filename]#/etc/hosts# 文件中进行了这种配置。 `-ro` 标志表示把输出的文件系统置为只读。 由于使用了这个标志， 远程系统在输出的文件系统上就不能写入任何变动了。

[.programlisting]
....
/cdrom -ro host1 host2 host3
....

下面的例子可以输出[.filename]##/home##给三个以IP地址方式表示的主机。 对于在没有配置DNS服务器的私有网络里头，这很有用。 此外， [.filename]##/etc/hosts## 文件也可以用以配置主机名；参看 man:hosts[5] 。 `-alldirs` 标记允许子目录被作为挂载点。 也就是说，客户端可以根据需要挂载需要的目录。 

[.programlisting]
....
/home  -alldirs  10.0.0.2 10.0.0.3 10.0.0.4
....

下面几行输出 [.filename]#/a# ，以便两个来自不同域的客户端可以访问文件系统。 `-maproot=root` 标记授权远端系统上的 `root` 用户在被输出的文件系统上以``root``身份进行读写。 如果没有特别指定 `-maproot=root` 标记， 则即使用户在远端系统上是 `root` 身份， 也不能修改被输出文件系统上的文件。 

[.programlisting]
....
/a  -maproot=root  host.example.com box.example.org
....

为了能够访问到被输出的文件系统，客户端必须被授权。 请确认客户端在您的 [.filename]#/etc/exports# 被列出。 

在 [.filename]#/etc/exports# 里头，每一行里面，输出信息和文件系统一一对应。 一个远程主机每次只能对应一个文件系统。而且只能有一个默认入口。比如，假设 [.filename]#/usr# 是独立的文件系统。这个 [.filename]#/etc/exports# 就是无效的：

[.programlisting]
....
# Invalid when /usr is one file system
/usr/src   client
/usr/ports client
....

一个文件系统，[.filename]#/usr#， 有两行指定输出到同一主机， `client`. 解决这一问题的正确的格式是：

[.programlisting]
....
/usr/src /usr/ports  client
....

在同一文件系统中， 输出到指定客户机的所有目录， 都必须写到同一行上。 没有指定客户机的行会被认为是单一主机。 这限制了你可以怎样输出的文件系统， 但对绝大多数人来说这不是问题。

下面是一个有效输出列表的例子， [.filename]#/usr# 和 [.filename]#/exports# 是本地文件系统：

[.programlisting]
....
# Export src and ports to client01 and client02, but only
# client01 has root privileges on it
/usr/src /usr/ports -maproot=root    client01
/usr/src /usr/ports               client02
# The client machines have root and can mount anywhere
# on /exports. Anyone in the world can mount /exports/obj read-only
/exports -alldirs -maproot=root      client01 client02
/exports/obj -ro
....

在修改了 [.filename]#/etc/exports# 文件之后， 就必须让 mountd 服务重新检查它， 以便使修改生效。 一种方法是通过给正在运行的服务程序发送 HUP 信号来完成：

[source,shell]
....
# kill -HUP `cat /var/run/mountd.pid`
....

或指定适当的参数来运行 `mountd` man:rc[8] 脚本：

[source,shell]
....
# /etc/rc.d/mountd onereload
....

关于使用 rc 脚本的细节， 请参见 crossref:config[configtuning-rcd,在 FreeBSD 中使用 rc]。

另外， 系统重启动可以让 FreeBSD 把一切都弄好。 尽管如此， 重启不是必须的。 以 `root` 身份执行下面的命令可以搞定一切。

在 NFS 服务器端：

[source,shell]
....
# rpcbind
# nfsd -u -t -n 4
# mountd -r
....

在 NFS 客户端：

[source,shell]
....
# nfsiod -n 4
....

现在每件事情都应该就绪，以备挂载一个远端文件系统。 在这些例子里头， 服务器名字将是：`server` ，而客户端的名字将是： `client`。 如果您只打算临时挂载一个远端文件系统或者只是打算作测试配置正确与否， 只要在客户端以 `root` 身份执行下面的命令：

[source,shell]
....
# mount server:/home /mnt
....

这条命令会把服务端的 [.filename]#/home# 目录挂载到客户端的 [.filename]#/mnt# 上。 如果配置正确，您应该可以进入客户端的 [.filename]#/mnt# 目录并且看到所有服务端的文件。 

如果您打算让系统每次在重启动的时候都自动挂载远端的文件系统，把那个文件系统加到 [.filename]#/etc/fstab# 文件里头去。下面是例子：

[.programlisting]
....
server:/home	/mnt	nfs	rw	0	0
....

man:fstab[5] 手册里有所有可用的开关。

=== 锁

某些应用程序 (例如 mutt) 需要文件上锁支持才能正常运行。 在使用 NFS 时， 可以用 rpc.lockd 来支持文件上锁功能。 要启用它， 需要在服务器和客户机的 [.filename]#/etc/rc.conf# 中加入 (假定两端均已配好了 NFS)：

[.programlisting]
....
rpc_lockd_enable="YES"
rpc_statd_enable="YES"
....

然后使用下述命令启动该程序：

[source,shell]
....
# /etc/rc.d/lockd start
# /etc/rc.d/statd start
....

如果并不需要真的在 NFS 客户机和 NFS 服务器间确保上锁的语义， 可以让 NFS 客户机在本地上锁， 方法是使用 man:mount_nfs[8] 时指定 `-L` 参数。 请参见 man:mount_nfs[8] 联机手册以了解更多细节。 

=== 实际应用

NFS 有很多实际应用。下面是比较常见的一些：

* 多个机器共享一台CDROM或者其他设备。这对于在多台机器中安装软件来说更加便宜跟方便。 
* 在大型网络中，配置一台中心 NFS 服务器用来放置所有用户的home目录可能会带来便利。 这些目录能被输出到网络以便用户不管在哪台工作站上登录，总能得到相同的home目录。 
* 几台机器可以有通用的[.filename]##/usr/ports/distfiles## 目录。 这样的话，当您需要在几台机器上安装port时，您可以无需在每台设备上下载而快速访问源码。 

[[network-amd]]
=== 通过 amd 自动地挂接

man:amd[8] (自动挂接服务) 能够自动地在访问时挂接远程的文件系统。 如果文件系统在一段时间之内没有活动， 则会被 amd 自动卸下。 通过使用 amd， 能够提供一个持久挂接以外的选择， 而后者往往需要列入 [.filename]#/etc/fstab#。

amd 通过将自己以 NFS 服务器的形式， 附加到 [.filename]#/host# 和 [.filename]#/net# 目录上来工作。 当访问这些目录中的文件时， amd 将查找相应的远程挂接点， 并自动地挂接。 [.filename]#/net# 用于挂接远程 IP 地址上导出的文件系统， 而 [.filename]#/host# 则用于挂接远程主机名上的文件系统。

访问 [.filename]#/host/foobar/usr# 中的文件， 相当于告诉 amd 尝试挂接在主机 `foobar` 上导出的 [.filename]#/usr#。

.通过 amd 来挂接导出的文件系统
[example]
====
您可以通过使用 `showmount` 命令来查看远程主机上导出的文件系统。 例如， 要查看 `foobar` 上导出的文件系统， 可以用：

[source,shell]
....
% showmount -e foobar
Exports list on foobar:
/usr                               10.10.10.0
/a                                 10.10.10.0
% cd /host/foobar/usr
....

====

如同在前面例子中所看到的， `showmount` 显示了导出的 [.filename]#/usr#。 当进入 [.filename]#/host/foobar/usr# 这个目录时， amd 将尝试解析主机名 `foobar` 并自动地挂接需要的文件系统导出。

amd 可以通过启动脚本来启动， 方法是在 [.filename]#/etc/rc.conf# 中加入：

[.programlisting]
....
amd_enable="YES"
....

除此之外， 还可以给 amd 通过 `amd_flags` 选项来传递额外的参数。 默认情况下， `amd_flags` 为：

[.programlisting]
....
amd_flags="-a /.amd_mnt -l syslog /host /etc/amd.map /net /etc/amd.map"
....

[.filename]#/etc/amd.map# 文件定义了挂接导出文件系统时所使用的默认选项。 [.filename]#/etc/amd.conf# 文件， 则定义了更多关于 amd 的高级功能选项。

请参考 man:amd[8] 和 man:amd.conf[8] 联机手册， 以了解进一步的情况。

[[network-nfs-integration]]
=== 与其他系统集成时的常见问题

某些特定的 ISA PC 系统上的以太网适配器上有一些限制， 这些限制可能会导致严重的网络问题， 特别是与 NFS 配合使用时。 这些问题并非 FreeBSD 所特有的， 但 FreeBSD 系统会受到这些问题的影响。

这样的问题， 几乎总是在当 (FreeBSD) PC 系统与高性能的工作站， 例如 Silicon Graphics, Inc., 和 Sun Microsystems, Inc. 的工作站联网时发生。 NFS 挂接能够正常工作， 而且一些操作也可能成功， 但服务器会很快变得对客户机不太理会， 虽然对其他客户机的请求仍然能够正常处理。 这种情况通常发生在客户端， 无论它是一个 FreeBSD 系统或是终端。 在许多系统上， 一旦发生了这样的问题， 通常没办法正常地关闭客户机。 唯一的办法通常是让终端复位， 因为这一 NFS 状况没有办法被解决。

尽管 "正确的" 解决办法， 是为 FreeBSD 系统配备一块高性能的、 适用的以太网适配器， 然而也有办法绕过问题并得到相对满意的结果。 如果 FreeBSD 系统是 _服务器_， 则在客户机挂接时， 应该指定 `-w=1024`。 如果 FreeBSD 系统是 _客户机_， 则应加入 `-r=1024` 参数。 这些选项可以通过在对应的 [.filename]#fstab# 的第四个字段加入， 以便让客户机能够自动地挂接， 或者通过 man:mount[8] 的 `-o` 参数在手工挂接时指定。

还需要注意的是另一个问题， 有时会被误认为是和上面一样的问题。 这个问题多见于 NFS 服务器和客户机在不同的网络上时。 如果是这种情况， 一定要 _确定_ 您的路由器确实把必需的 UDP 信息路由到了目的地， 否则您将什么也做不了。

下面的例子中， `fastws` 是主机 (接口) 的名字， 它是一台高性能的终端， 而 `freebox` 是另一台主机 (接口) 的名字， 它是一个使用较低性能的以太网适配器的 FreeBSD 系统。 同时， [.filename]#/sharedfs# 将被导出成为 NFS 文件系统 (参见 man:exports[5])， 而 [.filename]#/project# 将是客户机上挂接这一导出文件系统的挂接点。 所有的应用场景中， 请注意附加选项， 例如 `hard` 或 `soft` 以及 `bg` 可能是您的应用所需要的。

关于 FreeBSD 系统 (`freebox`) 作为客户机的示范 [.filename]#/etc/fstab# 文件， 见于 `freebox` 之上：

[.programlisting]
....
fastws:/sharedfs /project nfs rw,-r=1024 0 0
....

在 `freebox` 上手工挂接：

[source,shell]
....
# mount -t nfs -o -r=1024 fastws:/sharedfs /project
....

以 FreeBSD 系统作为服务器的例子， 是 `fastws` 上的 [.filename]#/etc/fstab#：

[.programlisting]
....
freebox:/sharedfs /project nfs rw,-w=1024 0 0
....

在 `fastws` 上手工挂接的命令是：

[source,shell]
....
# mount -t nfs -o -w=1024 freebox:/sharedfs /project
....

几乎所有的 16-位 以太网控制器， 都能够在没有上述读写尺寸限制的情况下正常工作。

对于那些关心到底是什么问题的人， 下面是失败如何发生的解释， 同时这也说明了为什么这是一个无法恢复的问题。 典型情况下， NFS 会使用一个 "块" 为单位进行操作， 其尺寸是 8 K (虽然它可能会将操作分成更小尺寸的分片)。 由于最大的以太网包尺寸大约是 1500 字节， 因此 NFS "块" 会分成多个以太网包， 虽然在更高层的代码看来它仍然是一个完整的单元， 并在接收方重新组装， 作为一个整体来 _确认_。 高性能的工作站， 可以将构成 NFS 单元的包迅速发出， 其节奏会快到标准允许的最大限度。 在容量较小的卡上， 后来的包会冲掉同一单元内的较早的包， 因而整个单元无法被重建或确认。 其结果是， 工作站将超时并重试， 但仍然是完整的 8 K 单元， 这一过程将无休止地重复下去。

如果将单元尺寸限制在以太网包尺寸之下， 我们就能够确保每一个以太网包都能够被独立地接收和确认， 从而避免了上面的死锁情形。

溢出在高性能工作站将数据库投向 PC 系统时仍会发生， 但在更好的网卡上， 能够保证这类溢出不会在每一个 NFS "单元" 上都发生。 当出现溢出时， 被影响的单元被重传， 因而此时有很大的机会它将被正确接收、 重组， 并确认。

[[network-nis]]
== 网络信息服务 (NIS/YP)

=== 它是什么？

NIS， 表示网络信息服务 (Network Information Services)， 最初由 Sun Microsystems 开发， 用于 UNIX(R) (最初是 SunOS(TM)) 系统的集中管理。 目前， 它基本上已经成为了业界标准； 所有主流的类 UNIX(R) 系统 (Solaris(TM), HP-UX, AIX(R), Linux, NetBSD, OpenBSD, FreeBSD, 等等) 都支持 NIS。

NIS 也就是人们所熟知的黄页(Yellow Pages)， 但由于商标的问题， Sun 将其改名为现在的名字。 旧的术语 (以及 yp)， 仍然经常可以看到， 并被广泛使用。

这是一个基于 RPC 的客户机/服务器系统， 它允许在一个 NIS 域中的一组机器共享一系列配置文件。 这样， 系统管理员就可以配置只包含最基本配置数据的 NIS 客户机系统， 并在单点上增加、 删除或修改配置数据。

尽管实现的内部细节截然不同， 这和 Windows NT(R) 域系统非常类似， 以至于可以将两者的基本功能相互类比。

=== 您应该知道的术语和进程

有一系列术语和重要的用户进程将在您在 FreeBSD 上实现 NIS 时用到， 无论是在创建 NIS 服务器， 或作为 NIS 客户机：

[.informaltable]
[cols="1,1", frame="none", options="header"]
|===
| 术语
| 说明

|NIS 域名
|NIS 主服务器和所有其客户机 (包括从服务器) 会使用同一 NIS 域名。 和 Windows NT(R) 域名类似， NIS 域名与 DNS 无关。

|rpcbind
|必须运行这个程序， 才能够启用 RPC (远程过程调用， NIS 用到的一种网络协议)。 如果没有运行 rpcbind， 则没有办法运行 NIS 服务器， 或作为 NIS 客户机。

|ypbind
|"绑定(bind)" NIS 客户机到它的 NIS 服务器上。 这样， 它将从系统中获取 NIS 域名， 并使用 RPC 连接到服务器上。 ypbind 是 NIS 环境中， 客户机-服务器通讯的核心； 如果客户机上的 ypbind 死掉的话， 它将无法访问 NIS 服务器。

|ypserv
|只应在 NIS 服务器上运行它； 这是 NIS 的服务器进程。 如果 man:ypserv[8] 死掉的话， 则服务器将不再具有响应 NIS 请求的能力 (此时， 如果有从服务器的话， 则会接管操作)。 有一些 NIS 的实现 (但不是 FreeBSD 的这个) 的客户机上， 如果之前用过一个服务器， 而那台服务器死掉的话， 并不尝试重新连接到另一个服务器。 通常， 发生这种情况时， 唯一的办法就是重新启动服务器进程 (或者， 甚至重新启动服务器) 或客户机上的 ypbind 进程。 

|rpc.yppasswdd
|另一个只应在 NIS 主服务器上运行的进程； 这是一个服务程序， 其作用是允许 NIS 客户机改变它们的 NIS 口令。 如果没有运行这个服务， 用户将必须登录到 NIS 主服务器上， 并在那里修改口令。
|===

=== 它是如何工作的？

在 NIS 环境中， 有三种类型的主机： 主服务器， 从服务器， 以及客户机。 服务器的作用是充当主机配置信息的中央数据库。 主服务器上保存着这些信息的权威副本， 而从服务器则是保存这些信息的冗余副本。 客户机依赖于服务器向它们提供这些信息。

许多文件的信息可以通过这种方式来共享。 通常情况下， [.filename]#master.passwd#、 [.filename]#group#， 以及 [.filename]#hosts# 是通过 NIS 分发的。 无论什么时候， 如果客户机上的某个进程请求这些本应在本地的文件中的资料的时候， 它都会向所绑定的 NIS 服务器发出请求， 而不使用本地的版本。

==== 机器类型

* 一台 _NIS 主服务器_。 这台服务器， 和 Windows NT(R) 域控制器类似， 会维护所有 NIS 客户机所使用的文件。 [.filename]#passwd#， [.filename]#group#， 以及许多其他 NIS 客户机所使用的文件， 都被存放到主服务器上。
+
[NOTE]
====
可以将一台 NIS 主服务器用在多个 NIS 域中。 然而， 本书不打算对这种配置进行介绍， 因为这种配置， 通常只出现在小规模的 NIS 环境中。
====

* _NIS 从服务器_。 这一概念， 与 Windows NT(R) 的备份域控制器类似。 NIS 从服务器， 用于维护 NIS 主服务器的数据文件副本。 NIS 从服务器提供了一种冗余， 这在许多重要的环境中是必需的。 此外， 它也帮助减轻了主服务器的负荷： NIS 客户机总是挂接到最先响应它们的 NIS 服务器上， 而这也包括来自从服务器的响应。
* _NIS 客户机_。 NIS 客户机， 和多数 Windows NT(R) 工作站类似， 通过 NIS 服务器 (或对于 Windows NT(R) 工作站， 则是 Windows NT(R) 域控制器) 来完成登录时的身份验证过程。

=== 使用 NIS/YP

这一节将通过实例介绍如何配置 NIS 环境。

==== 规划

假定您正在管理大学中的一个小型实验室。 在这个实验室中， 有 15 台 FreeBSD 机器， 目前尚没有集中的管理点； 每一台机器上有自己的 [.filename]#/etc/passwd# 和 [.filename]#/etc/master.passwd#。 这些文件通过人工干预的方法来保持与其他机器上版本的同步； 目前， 如果您在实验室中增加一个用户， 将不得不在所有 15 台机器上手工执行 `adduser` 命令。 毋庸置疑， 这一现状必须改变， 因此您决定将整个实验室转为使用 NIS， 并使用两台机器作为服务器。

因此， 实验室的配置应该是这样的：

[.informaltable]
[cols="1,1,1", frame="none", options="header"]
|===
| 机器名
| IP 地址
| 机器的角色

|`ellington`
|`10.0.0.2`
|NIS 主服务器

|`coltrane`
|`10.0.0.3`
|NIS 从服务器

|`basie`
|`10.0.0.4`
|教员工作站

|`bird`
|`10.0.0.5`
|客户机

|`cli[1-11]`
|`10.0.0.[6-17]`
|其他客户机
|===

如果您是首次配置 NIS， 仔细思考如何进行规划就十分重要。 无论您的网络的大小如何， 都必须进行几个决策。

===== 选择 NIS 域名

这可能不是您过去使用的 "域名(domainname)"。 它的规范的叫法， 应该是 "NIS 域名"。 当客户机广播对此信息的请求时， 它会将 NIS 域的名字作为请求的一部分发出。 这样， 统一网络上的多个服务器， 就能够知道谁应该回应请求。 您可以把 NIS 域名想象成以某种方式相关的一组主机的名字。

一些机构会选择使用它们的 Internet 域名来作为 NIS 域名。 并不推荐这样做， 因为在调试网络问题时， 这可能会导致不必要的困扰。 NIS 域名应该是在您网络上唯一的， 并且有助于了解它所描述的到底是哪一组机器。 例如对于 Acme 公司的美工部门， 可以考虑使用 "acme-art" 这样的 NIS 域名。 在这个例子中， 您使用的域名是 `test-domain`。

然而， 某些操作系统 (最著名的是 SunOS(TM)) 会使用其 NIS 域名作为 Internet 域名。 如果您的网络上存在包含这类限制的机器， 就 _必须_ 使用 Internet 域名来作为您的 NIS 域名。

===== 服务器的物理要求

选择 NIS 服务器时， 需要时刻牢记一些东西。 NIS 的一个不太好的特性就是其客户机对于服务器的依赖程度。 如果客户机无法与其 NIS 域的服务器联系， 则这台机器通常会陷于不可用的状态。 缺少用户和组信息， 会使绝大多数系统进入短暂的冻结状态。 基于这样的考虑， 您需要选择一台不经常重新启动， 或用于开发的机器来承担其责任。 如果您的网络不太忙， 也可以使用运行着其他服务的机器来安放 NIS 服务， 只是需要注意， 一旦 NIS 服务器不可用， 则 _所有_ 的 NIS 客户机都会受到影响。

==== NIS 服务器

所有的 NIS 信息的正规版本， 都被保存在一台单独的称作 NIS 主服务器的机器上。 用于保存这些信息的数据库， 称为 NIS 映射(map)。 在 FreeBSD 中， 这些映射被保存在 [.filename]#/var/yp/[domainname]# 里， 其中 [.filename]#[domainname]# 是提供服务的 NIS 域的名字。 一台 NIS 服务器， 可以同时支持多个域， 因此可以建立很多这样的目录， 所支撑一个域对应一个。 每一个域都会有一组独立的映射。

NIS 主和从服务器， 通过 `ypserv` 服务程序来处理所有的 NIS 请求。 `ypserv` 有责任接收来自 NIS 客户机的请求， 翻译请求的域， 并将名字映射为相关的数据库文件的路径， 然后将来自数据库的数据传回客户机。

===== 配置 NIS 主服务器

配置主 NIS 服务器相对而言十分的简单， 而其具体步骤则取决于您的需要。 FreeBSD 提供了一步到位的 NIS 支持。 您需要做的全部事情， 只是在 [.filename]#/etc/rc.conf# 中加入一些配置， 其他工作会由 FreeBSD 完成。

[.procedure]
====

[.programlisting]
....
nisdomainname="test-domain"
....
. 这一行将在网络启动 (例如重新启动) 时， 把 NIS 域名配置为 `test-domain`。
+
[.programlisting]
....
nis_server_enable="YES"
....
. 这将要求 FreeBSD 在网络子系统启动之后立即启动 NIS 服务进程。
+
[.programlisting]
....
nis_yppasswdd_enable="YES"
....
. 这将启用 `rpc.yppasswdd` 服务程序， 如前面提到的， 它允许用户在客户机上修改自己的 NIS 口令。
====

[NOTE]
====
随 NIS 配置的不同， 可能还需要增加其他一些项目。 请参见 <<network-nis-server-is-client,关于 NIS 服务器同时充当 NIS 客户机>> 这一节， 以了解进一步的情况。
====

设置好前面这些配置之后， 需要以超级用户身份运行 `/etc/netstart` 命令。 它会根据 [.filename]#/etc/rc.conf# 的设置来配置系统中的其他部分。 最后， 在初始化 NIS 映射之前， 还需要手工启动 ypserv 服务程序：

[source,shell]
....
# /etc/rc.d/ypserv start
....

===== 初始化 NIS 映射

_NIS 映射_ 是一些数据库文件， 它们位于 [.filename]#/var/yp# 目录中。 这些文件基本上都是根据 NIS 主服务器的 [.filename]#/etc# 目录自动生成的， 唯一的例外是： [.filename]#/etc/master.passwd# 文件。 一般来说， 您会有非常充分的理由不将 `root` 以及其他管理帐号的口令发到所有 NIS 域上的服务器上。 因此， 在开始初始化 NIS 映射之前， 我们应该：

[source,shell]
....
# cp /etc/master.passwd /var/yp/master.passwd
# cd /var/yp
# vi master.passwd
....

这里， 删除掉和系统有关的帐号对应的项 (`bin`、 `tty`、 `kmem`、 `games`， 等等)， 以及其他不希望被扩散到 NIS 客户机的帐号 (例如 `root` 和任何其他 UID 0 (超级用户) 的帐号)。

[NOTE]
====
确认 [.filename]#/var/yp/master.passwd# 这个文件是同组用户， 以及其他用户不可读的 (模式 600)！ 如果需要的话， 用 `chmod` 命令来改它。
====

完成这些工作之后， 就可以初始化 NIS 映射了！ FreeBSD 提供了一个名为 `ypinit` 的脚本来帮助您完成这项工作 (详细信息， 请见其联机手册)。 请注意， 这个脚本在绝大多数 UNIX(R) 操作系统上都可以找到， 但并不是所有操作系统的都提供。 在 Digital UNIX/Compaq Tru64 UNIX 上它的名字是 `ypsetup`。 由于我们正在生成的是 NIS 主服务器的映射， 因此应该使用 `ypinit` 的 `-m` 参数。 如果已经完成了上述步骤， 要生成 NIS 映射， 只需执行：

[source,shell]
....
ellington# ypinit -m test-domain
Server Type: MASTER Domain: test-domain
Creating an YP server will require that you answer a few questions.
Questions will all be asked at the beginning of the procedure.
Do you want this procedure to quit on non-fatal errors? [y/n: n] n
Ok, please remember to go back and redo manually whatever fails.
If you don't, something might not work.
At this point, we have to construct a list of this domains YP servers.
rod.darktech.org is already known as master server.
Please continue to add any slave servers, one per line. When you are
done with the list, type a <control D>.
master server   :  ellington
next host to add:  coltrane
next host to add:  ^D
The current list of NIS servers looks like this:
ellington
coltrane
Is this correct?  [y/n: y] y

[..output from map generation..]

NIS Map update completed.
ellington has been setup as an YP master server without any errors.
....

`ypinit` 应该会根据 [.filename]#/var/yp/Makefile.dist# 来创建 [.filename]#/var/yp/Makefile# 文件。 创建完之后， 这个文件会假定您正在操作只有 FreeBSD 机器的单服务器 NIS 环境。 由于 `test-domain` 还有一个从服务器， 您必须编辑 [.filename]#/var/yp/Makefile#：

[source,shell]
....
ellington# vi /var/yp/Makefile
....

应该能够看到这样一行， 其内容是

[.programlisting]
....
NOPUSH = "True"
....

(如果还没有注释掉的话)。

===== 配置 NIS 从服务器

配置 NIS 从服务器， 甚至比配置主服务器还要简单。 登录到从服务器上， 并按照前面的方法， 编辑 [.filename]#/etc/rc.conf# 文件。 唯一的区别是， 在运行 `ypinit` 时需要使用 `-s` 参数。 这里的 `-s` 选项， 同时要求提供 NIS 主服务器的名字， 因此我们的命令行应该是：

[source,shell]
....
coltrane# ypinit -s ellington test-domain

Server Type: SLAVE Domain: test-domain Master: ellington

Creating an YP server will require that you answer a few questions.
Questions will all be asked at the beginning of the procedure.

Do you want this procedure to quit on non-fatal errors? [y/n: n]  n

Ok, please remember to go back and redo manually whatever fails.
If you don't, something might not work.
There will be no further questions. The remainder of the procedure
should take a few minutes, to copy the databases from ellington.
Transferring netgroup...
ypxfr: Exiting: Map successfully transferred
Transferring netgroup.byuser...
ypxfr: Exiting: Map successfully transferred
Transferring netgroup.byhost...
ypxfr: Exiting: Map successfully transferred
Transferring master.passwd.byuid...
ypxfr: Exiting: Map successfully transferred
Transferring passwd.byuid...
ypxfr: Exiting: Map successfully transferred
Transferring passwd.byname...
ypxfr: Exiting: Map successfully transferred
Transferring group.bygid...
ypxfr: Exiting: Map successfully transferred
Transferring group.byname...
ypxfr: Exiting: Map successfully transferred
Transferring services.byname...
ypxfr: Exiting: Map successfully transferred
Transferring rpc.bynumber...
ypxfr: Exiting: Map successfully transferred
Transferring rpc.byname...
ypxfr: Exiting: Map successfully transferred
Transferring protocols.byname...
ypxfr: Exiting: Map successfully transferred
Transferring master.passwd.byname...
ypxfr: Exiting: Map successfully transferred
Transferring networks.byname...
ypxfr: Exiting: Map successfully transferred
Transferring networks.byaddr...
ypxfr: Exiting: Map successfully transferred
Transferring netid.byname...
ypxfr: Exiting: Map successfully transferred
Transferring hosts.byaddr...
ypxfr: Exiting: Map successfully transferred
Transferring protocols.bynumber...
ypxfr: Exiting: Map successfully transferred
Transferring ypservers...
ypxfr: Exiting: Map successfully transferred
Transferring hosts.byname...
ypxfr: Exiting: Map successfully transferred

coltrane has been setup as an YP slave server without any errors.
Don't forget to update map ypservers on ellington.
....

现在应该会有一个叫做 [.filename]#/var/yp/test-domain# 的目录。 在这个目录中， 应该保存 NIS 主服务器上的映射的副本。 接下来需要确定这些文件都及时地同步更新了。 在从服务器上， 下面的 [.filename]#/etc/crontab# 项将帮助您确保这一点：

[.programlisting]
....
20      *       *       *       *       root   /usr/libexec/ypxfr passwd.byname
21      *       *       *       *       root   /usr/libexec/ypxfr passwd.byuid
....

这两行将强制从服务器将映射与主服务器同步。 由于主服务器会尝试确保所有其 NIS 映射的变动都知会从服务器， 因此这些项并不是绝对必需的。 不过， 由于保持其他客户端的口令信息正确性十分重要， 而这则依赖于从服务器， 强烈推荐明确指定让系统时常强制更新口令映射。 对于繁忙的网络而言， 这一点尤其重要， 因为有时可能出现映射更新不完全的情况。

现在， 在从服务器上执行 `/etc/netstart`， 就可以启动 NIS 服务了。

==== NIS 客户机

NIS 客户机会通过 `ypbind` 服务程序来与特定的 NIS 服务器建立一种称作绑定的联系。 `ypbind` 会检查系统的默认域 (这是通过 `domainname` 命令来设置的)， 并开始在本地网络上广播 RPC 请求。 这些请求会指定 `ypbind` 尝试绑定的域名。 如果已经配置了服务器， 并且这些服务器接到了广播， 它将回应 `ypbind`， 后者则记录服务器的地址。 如果有多个可用的服务器 (例如一个主服务器， 加上多个从服务器)， `ypbind` 将使用第一个响应的地址。 从这一时刻开始， 客户机会把所有的 NIS 请求直接发给那个服务器。 `ypbind` 偶尔会 "ping" 服务器以确认其仍然在正常运行。 如果在合理的时间内没有得到响应， 则 `ypbind` 会把域标记为未绑定， 并再次发起广播， 以期找到另一台服务器。

===== 设置 NIS 客户机

配置一台 FreeBSD 机器作为 NIS 客户机是非常简单的。

[.procedure]
====

. 编辑 [.filename]#/etc/rc.conf# 文件， 并在其中加上下面几行， 以设置 NIS 域名， 并在网络启动时启动 `ypbind`：
+
[.programlisting]
....
nisdomainname="test-domain"
nis_client_enable="YES"
....
+
. 要从 NIS 服务器导入所有的口令项， 需要从您的 [.filename]#/etc/master.passwd# 文件中删除所有用户， 并使用 `vipw` 在这个文件的最后一行加入：
+
[.programlisting]
....
+:::::::::
....
+
[NOTE]
======
这一行将让 NFS 服务器的口令映射中的帐号能够登录。 也有很多修改这一行来配置 NIS 客户机的办法。 请参见稍后的 <<network-netgroups,netgroups 小节>> 以了解进一步的情况。 要了解更多信息， 可以参阅 O'Reilly 的 `Managing NFS and NIS` 这本书。
======
+
[NOTE]
======
需要至少保留一个本地帐号 (也就是不通过 NIS 导入) 在您的 [.filename]#/etc/master.passwd# 文件中， 而这个帐号应该是 `wheel` 组的成员。 如果 NIS 发生不测， 这个帐号可以用来远程登录， 成为 `root`， 并修正问题。
======
+
. 要从 NIS 服务器上导入组信息， 需要在 [.filename]#/etc/group# 文件末尾加入：
+
[.programlisting]
....
+:*::
....
====

想要立即启动 NIS 客户端， 需要以超级用户身份运行执行下列命令：

[source,shell]
....
# /etc/netstart
# /etc/rc.d/ypbind start
....

完成这些步骤之后， 就应该可以通过运行 `ypcat passwd` 来看到 NIS 服务器的口令映射了。

=== NIS 的安全性

基本上， 任何远程用户都可以发起一个 RPC 到 man:ypserv[8] 并获得您的 NIS 映射的内容， 如果远程用户了解您的域名的话。 要避免这类未经授权的访问， man:ypserv[8] 支持一个称为 "securenets" 的特性， 用以将访问限制在一组特定的机器上。 在启动过程中， man:ypserv[8] 会尝试从 [.filename]#/var/yp/securenets# 中加载 securenet 信息。

[NOTE]
====
这个路径随 `-p` 参数改变。 这个文件包含了一些项， 每一项中包含了一个网络标识和子网掩码， 中间用空格分开。 以 "#" 开头的行会被认为是注释。 示范的 securenets 文件如下所示：
====

[.programlisting]
....
# allow connections from local host -- mandatory
127.0.0.1     255.255.255.255
# allow connections from any host
# on the 192.168.128.0 network
192.168.128.0 255.255.255.0
# allow connections from any host
# between 10.0.0.0 to 10.0.15.255
# this includes the machines in the testlab
10.0.0.0      255.255.240.0
....

如果 man:ypserv[8] 接到了来自匹配上述任一规则的地址的请求， 则它会正常处理请求。 反之， 则请求将被忽略， 并记录一条警告信息。 如果 [.filename]#/var/yp/securenets# 文件不存在， 则 `ypserv` 会允许来自任意主机的请求。

`ypserv` 程序也支持 Wietse Venema 的 TCP Wrapper 软件包。 这样， 管理员就能够使用 TCP Wrapper 的配置文件来代替 [.filename]#/var/yp/securenets# 完成访问控制。

[NOTE]
====
尽管这两种访问控制机制都能够提供某种程度的安全， 但是， 和特权端口检查一样， 它们无法避免 "IP 伪造" 攻击。 您的防火墙应该阻止所有与 NIS 有关的访问。

使用 [.filename]#/var/yp/securenets# 的服务器， 可能会无法为某些使用陈旧的 TCP/IP 实现的 NIS 客户机服务。 这些实现可能会在广播时， 将主机位都设置为 0， 或在计算广播地址时忽略子网掩码。 尽管这些问题可以通过修改客户机的配置来解决， 其他一些问题也可能导致不得不淘汰那些客户机系统， 或者不使用 [.filename]#/var/yp/securenets#。

在使用陈旧的 TCP/IP 实现的系统上， 使用 [.filename]#/var/yp/securenets# 是一个非常糟糕的做法， 因为这将导致您的网络上的 NIS 丧失大部分功能。

使用 TCP Wrapper 软件包， 会导致您的 NIS 服务器的响应延迟增加。 而增加的延迟， 则可能会导致客户端程序超时， 特别是在繁忙的网络或者很慢的 NIS 服务器上。 如果您的某个客户机因此而产生一些异常， 则应将这些客户机变为 NIS 从服务器， 并强制其绑定自己。
====

=== 不允许某些用户登录

在我们的实验室中， `basie` 这台机器， 是一台教员专用的工作站。 我们不希望将这台机器拿出 NIS 域， 而主 NIS 服务器上的 [.filename]#passwd# 文件， 则同时包含了教员和学生的帐号。 这时应该怎么做？

有一种办法来禁止特定的用户登录机器， 即使他们身处 NIS 数据库之中。 要完成这一工作， 只需要在客户机的 [.filename]#/etc/master.passwd# 文件中加入一些 `-username` 这样的项， 其中， _username_ 是希望禁止登录的用户名。 一般推荐使用 `vipw` 来完成这个工作， 因为 `vipw` 会对您在 [.filename]#/etc/master.passwd# 文件上所作的修改进行合法性检查， 并在编辑结束时重新构建口令数据库。 例如， 如果希望禁止用户 `bill` 登录 `basie`， 我们应该：

[source,shell]
....
basie# vipw
[在末尾加入 -bill， 并退出]
vipw: rebuilding the database...
vipw: done

basie# cat /etc/master.passwd

root:[password]:0:0::0:0:The super-user:/root:/bin/csh
toor:[password]:0:0::0:0:The other super-user:/root:/bin/sh
daemon:*:1:1::0:0:Owner of many system processes:/root:/sbin/nologin
operator:*:2:5::0:0:System &:/:/sbin/nologin
bin:*:3:7::0:0:Binaries Commands and Source,,,:/:/sbin/nologin
tty:*:4:65533::0:0:Tty Sandbox:/:/sbin/nologin
kmem:*:5:65533::0:0:KMem Sandbox:/:/sbin/nologin
games:*:7:13::0:0:Games pseudo-user:/usr/games:/sbin/nologin
news:*:8:8::0:0:News Subsystem:/:/sbin/nologin
man:*:9:9::0:0:Mister Man Pages:/usr/shared/man:/sbin/nologin
bind:*:53:53::0:0:Bind Sandbox:/:/sbin/nologin
uucp:*:66:66::0:0:UUCP pseudo-user:/var/spool/uucppublic:/usr/libexec/uucp/uucico
xten:*:67:67::0:0:X-10 daemon:/usr/local/xten:/sbin/nologin
pop:*:68:6::0:0:Post Office Owner:/nonexistent:/sbin/nologin
nobody:*:65534:65534::0:0:Unprivileged user:/nonexistent:/sbin/nologin
+:::::::::
-bill

basie#
....

[[network-netgroups]]
=== 使用 Netgroups

前一节介绍的方法， 在您需要为非常少的用户和/或机器进行特殊的规则配置时还算凑合。 在更大的网络上， 您 _一定会_ 忘记禁止某些用户登录到敏感的机器上， 或者， 甚至必须单独地修改每一台机器的配置， 因而丢掉了 NIS 最重要的优越性： _集中式_ 管理。

NIS 开发人员为这个问题提供的解决方案， 被称作 _netgroups_。 它们的作用和语义， 基本上可以等同于 UNIX(R) 文件系统上使用的组。 主要的区别是它们没有数字化的 ID， 以及可以在 netgroup 中同时包含用户和其他 netgroup。

Netgroups 被设计用来处理大的、 复杂的包含数百用户和机器的网络。 一方面， 在您不得不处理这类情形时， 这是一个很有用的东西。 而另一方面， 它的复杂性又使得通过非常简单的例子很难解释 netgroup 到底是什么。 这一节的其余部分的例子将展示这个问题。

假设您在实验室中成功地部署 NIS 引起了上司的兴趣。 您接下来的任务是将 NIS 域扩展， 以覆盖校园中的一些其他的机器。 下面两个表格中包括了新用户和新机器， 及其简要说明。

[.informaltable]
[cols="1,1", frame="none", options="header"]
|===
| 用户名
| 说明

|`alpha`, `beta`
|IT 部门的普通雇员

|`charlie`, `delta`
|IT 部门的学徒

|`echo`, `foxtrott`, `golf`, ...
|普通雇员

|`able`, `baker`, ...
|目前的实习生
|===

[.informaltable]
[cols="1,1", frame="none", options="header"]
|===
| 机器名
| 说明

|`war`, `death`, `famine`, `pollution`
|最重要的服务器。 只有 IT 部门的雇员才允许登录这些机器。

|`pride`, `greed`, `envy`, `wrath`, `lust`, `sloth`
|不太重要的服务器， 所有 IT 部门的成员， 都可以登录这些机器。

|`one`, `two`, `three`, `four`, ...
|普通工作站。 只有 _真正的_ 雇员才允许登录这些机器。

|`trashcan`
|一台不包含关键数据的旧机器。 即使是实习生， 也允许登录它。
|===

如果您尝试通过一个一个地阻止用户来实现这些限制， 就需要在每一个系统的 [.filename]#passwd# 文件中， 为每一个不允许登录该系统的用户添加对应的 `-user` 行。 如果忘记了任何一个， 就可能会造成问题。 在进行初始配置时， 正确地配置也许不是什么问题， 但随着日复一日地添加新用户， _总有一天_ 您会忘记为新用户添加某个行。 毕竟， Murphy 是一个乐观的人。

使用 netgroups 来处理这一状况可以带来许多好处。 不需要单独地处理每一个用户； 您可以赋予用户一个或多个 netgroups 身份， 并允许或禁止某一个 netgroup 的所有成员登录。 如果添加了新的机器， 只需要定义 netgroup 的登录限制。 如果增加了新用户， 也只需要将用户加入一个或多个 netgroup。 这些变化是相互独立的： 不再需要 "对每一个用户和机器执行 ......"。 如果您的 NIS 配置经过了谨慎的规划， 就只需要修改一个中央的配置文件， 就能够允许或禁止访问某台机器的权限了。

第一步是初始化 NIS 映射 netgroup。 FreeBSD 的 man:ypinit[8] 默认情况下并不创建这个映射， 但它的 NIS 实现能够在创建这个映射之后立即对其提供支持。 要创建空映射， 简单地输入

[source,shell]
....
ellington# vi /var/yp/netgroup
....

并开始增加内容。 在我们的例子中， 至少需要四个 nergruop： IT 雇员， IT 学徒， 普通雇员和实习生。

[.programlisting]
....
IT_EMP  (,alpha,test-domain)    (,beta,test-domain)
IT_APP  (,charlie,test-domain)  (,delta,test-domain)
USERS   (,echo,test-domain)     (,foxtrott,test-domain) \
        (,golf,test-domain)
INTERNS (,able,test-domain)     (,baker,test-domain)
....

`IT_EMP`, `IT_APP` 等等， 是 netgroup 的名字。 每一个括号中的组中， 都有一些用户帐号。 组中的三个字段是：

. 在哪些机器上能够使用这些项。 如果不指定主机名， 则项在所有机器上都有效。 如果指定了主机， 则很容易造成混淆。
. 属于这个 netgroup 的帐号。
. 帐号的 NIS 域。 您可以从其他 NIS 域中把帐号导入到您的 netgroup 中， 如果您管理多个 NIS 域的话。

每一个字段都可以包括通配符。 参见 man:netgroup[5] 了解更多细节。

[NOTE]
====
Netgroup 的名字一般来说不应超过 8 个字符， 特别是当您的 NIS 域中有机器打算运行其它操作系统的时候。 名字是区分大小写的； 使用大写字母作为 netgroup 的名字， 能够让您更容易地区分用户、 机器和 netgroup 的名字。

某些 NIS 客户程序 (FreeBSD 以外的那些) 可能无法处理含有大量项的 netgroup。 例如， 某些早期版本的 SunOS(TM) 会在 netgroup 中包含多于 15 个 _项_ 时出现问题。 要绕过这个问题， 可以创建多个 子netgroup，每一个中包含少于 15 个用户， 以及一个包含所有 子netgroup 的真正的 netgroup：

[.programlisting]
....
BIGGRP1  (,joe1,domain)  (,joe2,domain)  (,joe3,domain) [...]
BIGGRP2  (,joe16,domain)  (,joe17,domain) [...]
BIGGRP3  (,joe31,domain)  (,joe32,domain)
BIGGROUP  BIGGRP1 BIGGRP2 BIGGRP3
....

如果需要超过 225 个用户， 可以继续重复上面的过程。
====

激活并分发新的 NIS 映射非常简单：

[source,shell]
....
ellington# cd /var/yp
ellington# make
....

这个操作会生成三个 NIS 映射， 即 [.filename]#netgroup#、 [.filename]#netgroup.byhost# 和 [.filename]#netgroup.byuser#。 用 man:ypcat[1] 可以检查这些 NIS 映射是否可用了：

[source,shell]
....
ellington% ypcat -k netgroup
ellington% ypcat -k netgroup.byhost
ellington% ypcat -k netgroup.byuser
....

第一个命令的输出， 应该与 [.filename]#/var/yp/netgroup# 的内容相近。 第二个命令， 如果没有指定本机专有的 netgroup， 则应该没有输出。 第三个命令， 则用于显示某个用户对应的 netgroup 列表。

客户机的设置也很简单。 要配置服务器 `war`， 只需进入 man:vipw[8] 并把

[.programlisting]
....
+:::::::::
....

改为

[.programlisting]
....
+@IT_EMP:::::::::
....

现在， 只有 netgroup `IT_EMP` 中定义的用户会被导入到 `war` 的口令数据库中， 因此只有这些用户能够登录。

不过， 这个限制也会作用于 shell 的 `~`， 以及所有在用户名和数字用户 ID 之间实施转换的函数的功能。 换言之， `cd ~user` 将不会正常工作， 而 `ls -l` 也将显示数字的 ID 而不是用户名， 并且 `find . -user joe -print` 将失败， 并给出 `No such user` 的错误信息。 要修正这个问题， 您需要导入所有的用户项， 而 _不允许他们登录服务器_。

这可以通过在 [.filename]#/etc/master.passwd# 加入另一行来完成。 这行的内容是：

`+:::::::::/sbin/nologin`， 意思是 "导入所有的项， 但导入项的 shell 则替换为 [.filename]#/sbin/nologin#"。 通过在 [.filename]#/etc/master.passwd# 中增加默认值， 可以替换掉 `passwd` 中的任意字段。

[WARNING]
====

务必确认 `+:::::::::/sbin/nologin` 这一行出现在 `+@IT_EMP:::::::::` 之后。 否则， 所有从 NIS 导入的用户帐号将以 [.filename]#/sbin/nologin# 作为登录 shell。
====

完成上面的修改之后， 在 IT 部门有了新员工时， 只需修改一个 NIS 映射就足够了。 您也可以用类似的方法， 在不太重要的服务器上， 把先前本地版本的 [.filename]#/etc/master.passwd# 中的 `+:::::::::` 改为：

[.programlisting]
....
+@IT_EMP:::::::::
+@IT_APP:::::::::
+:::::::::/sbin/nologin
....

相关的用于普通工作站的配置则应是：

[.programlisting]
....
+@IT_EMP:::::::::
+@USERS:::::::::
+:::::::::/sbin/nologin
....

一切平安无事， 直到数周后， 有一天策略发生了变化： IT 部门也开始招收实习生了。 IT 实习生允许使用普通的终端， 以及不太重要的服务器； 而 IT 学徒， 则可以登录主服务器。 您增加了新的 netgroup `IT_INTERN`， 以及新的 IT 实习生到这个 netgroup 并开始修改每一台机器上的配置...... 老话说得好："牵一发， 动全身"。

NIS 通过 netgroup 来建立 netgroup 的能力， 正可以避免这样的情形。 一种可能的方法是建立基于角色的 netgroup。 例如， 您可以创建称为 `BIGSRV` 的 netgroup， 用于定义最重要的服务器上的登录限制， 以及另一个成为 `SMALLSRV` 的 netgroup， 用以定义次重要的服务器， 以及第三个， 用于普通工作站的 netgroup `USERBOX`。 这三个 netgroup 中的每一个， 都包含了允许登录到这些机器上的所有 netgroup。 您的 NIS 映射中的新项如下所示：

[.programlisting]
....
BIGSRV    IT_EMP  IT_APP
SMALLSRV  IT_EMP  IT_APP  ITINTERN
USERBOX   IT_EMP  ITINTERN USERS
....

这种定义登录限制的方法， 在您能够将机器分组并加以限制的时候可以工作的相当好。 不幸的是， 这是种例外， 而非常规情况。 多数时候， 需要按机器去定义登录限制。

与机器相关的 netgroup 定义， 是处理上述策略改动的另一种可能的方法。 此时， 每台机器的 [.filename]#/etc/master.passwd# 中， 都包含两个 "+" 开头的行。 第一个用于添加允许登录的 netgroup 帐号， 而第二个则用于增加其它帐号， 并把 shell 设置为 [.filename]#/sbin/nologin#。 使用 "全大写" 的机器名作为 netgroup 名是个好主意。 换言之， 这些行应该类似于：

[.programlisting]
....
+@BOXNAME:::::::::
+:::::::::/sbin/nologin
....

一旦在所有机器上都完成了这样的修改， 就再也不需要修改本地的 [.filename]#/etc/master.passwd# 了。 所有未来的修改都可以在 NIS 映射中进行。 这里是一个例子， 其中展示了在这一应用情景中所需要的 netgroup 映射， 以及其它一些常用的技巧：

[.programlisting]
....
# Define groups of users first
IT_EMP    (,alpha,test-domain)    (,beta,test-domain)
IT_APP    (,charlie,test-domain)  (,delta,test-domain)
DEPT1     (,echo,test-domain)     (,foxtrott,test-domain)
DEPT2     (,golf,test-domain)     (,hotel,test-domain)
DEPT3     (,india,test-domain)    (,juliet,test-domain)
ITINTERN  (,kilo,test-domain)     (,lima,test-domain)
D_INTERNS (,able,test-domain)     (,baker,test-domain)
#
# Now, define some groups based on roles
USERS     DEPT1   DEPT2     DEPT3
BIGSRV    IT_EMP  IT_APP
SMALLSRV  IT_EMP  IT_APP    ITINTERN
USERBOX   IT_EMP  ITINTERN  USERS
#
# And a groups for a special tasks
# Allow echo and golf to access our anti-virus-machine
SECURITY  IT_EMP  (,echo,test-domain)  (,golf,test-domain)
#
# machine-based netgroups
# Our main servers
WAR       BIGSRV
FAMINE    BIGSRV
# User india needs access to this server
POLLUTION  BIGSRV  (,india,test-domain)
#
# This one is really important and needs more access restrictions
DEATH     IT_EMP
#
# The anti-virus-machine mentioned above
ONE       SECURITY
#
# Restrict a machine to a single user
TWO       (,hotel,test-domain)
# [...more groups to follow]
....

如果您正使用某种数据库来管理帐号， 应该可以使用您的数据库的报告工具来创建映射的第一部分。 这样， 新用户就自动地可以访问这些机器了。

最后的提醒： 使用基于机器的 netgroup 并不总是适用的。 如果正在为学生实验室部署数十台甚至上百台同样的机器， 您应该使用基于角色的 netgroup， 而不是基于机器的 netgroup， 以便把 NIS 映射的尺寸保持在一个合理的范围内。

=== 需要牢记的事项

这里是一些其它在使用 NIS 环境时需要注意的地方。

* 每次需要在实验室中增加新用户时， 必须 _只_ 在 NIS 服务器上加入用户， 而且 _一定要记得重建 NIS 映射_。 如果您忘记了这样做， 新用户将无法登录除 NIS 主服务器之外的任何其它机器。 例如， 如果要在实验室增加新用户 `jsmith`， 我们需要：
+

[source,shell]
....
# pw useradd jsmith
# cd /var/yp
# make test-domain
....

+ 
也可以运行 `adduser jsmith` 而不是 `pw useradd jsmith`.
* _将管理用的帐号排除在 NIS 映射之外_。 一般来说， 您不希望这些管理帐号和口令被扩散到那些包含不应使用它们的用户的机器上。
* _确保 NIS 主和从服务器的安全， 并尽可能减少其停机时间_。 如果有人攻入或简单地关闭这些机器， 则整个实验室的任也就无法登录了。
+ 
这是集中式管理系统中最薄弱的环节。 如果没有保护好 NIS 服务器， 您就有大批愤怒的用户需要对付了！

=== NIS v1 兼容性

FreeBSD 的 ypserv 提供了某些为 NIS v1 客户提供服务的支持能力。 FreeBSD 的 NIS 实现， 只使用 NIS v2 协议， 但其它实现可能会包含 v1 协议， 以提供对旧系统的向下兼容能力。 随这些系统提供的 ypbind 服务将首先尝试绑定 NIS v1 服务器， 即使它们并不真的需要它 (有些甚至可能会一直广播搜索请求， 即使已经从某台 v2 服务器得到了回应也是如此)。 注意， 尽管支持一般的客户机调用， 这个版本的 ypserv 并不能处理 v1 的映射传送请求； 因而， 它就不能与较早的支持 v1 协议的 NIS 服务器配合使用， 无论是作为主服务器还是从服务器。 幸运的是， 现今应该已经没有仍然在用的这样的服务器了。

[[network-nis-server-is-client]]
=== 同时作为 NIS 客户机的 NIS 服务器

在多服务器域的环境中， 如果服务器同时作为 NIS 客户， 在运行 ypserv 时要特别小心。 一般来说， 强制服务器绑定自己要比允许它们广播绑定请求要好， 因为这种情况下它们可能会相互绑定。 某些怪异的故障， 很可能是由于某一台服务器停机， 而其它服务器都依赖其服务所导致的。 最终， 所有的客户机都会超时并绑定到其它服务器， 但这个延迟可能会相当可观， 而且恢复之后仍然存在再次发生此类问题的隐患。

您可以强制一台机器绑定到特定的服务器， 这是通过 `ypbind` 的 `-S` 参数来完成的。 如果不希望每次启动 NIS 服务器时都手工完成这项工作， 可以在 [.filename]#/etc/rc.conf# 中加入：

[.programlisting]
....
nis_client_enable="YES"	# run client stuff as well
nis_client_flags="-S NIS domain,server"
....

参见 man:ypbind[8] 以了解更多情况。

=== 口令格式

在实现 NIS 时， 口令格式的兼容性问题是一种最为常见的问题。 假如您的 NIS 服务器使用 DES 加密口令， 则它只能支持使用 DES 的客户机。 例如， 如果您的网络上有 Solaris(TM) NIS 客户机， 则几乎肯定需要使用 DES 加密口令。

要检查您的服务器和客户机使用的口令格式， 需要查看 [.filename]#/etc/login.conf#。 如果主机被配置为使用 DES 加密的口令， 则 `default` class 将包含类似这样的项：

[.programlisting]
....
default:\
	:passwd_format=des:\
	:copyright=/etc/COPYRIGHT:\
	[Further entries elided]
....

其他一些可能的 `passwd_format` 包括 `blf` 和 `md5` (分别对应于 Blowfish 和 MD5 加密口令)。

如果修改了 [.filename]#/etc/login.conf#， 就必须重建登录性能数据库， 这是通过以 `root` 身份运行下面的程序来完成的：

[source,shell]
....
# cap_mkdb /etc/login.conf
....

[NOTE]
====
已经在 [.filename]#/etc/master.passwd# 中的口令的格式不会被更新， 直到用户在登录性能数据库重建 _之后_ 首次修改口令为止。
====

接下来， 为了确保所有的口令都按照您选择的格式加密了， 还需要检查 [.filename]#/etc/auth.conf# 中 `crypt_default` 给出的优先选择的口令格式。 要完成此工作， 将您选择的格式放到列表的第一项。 例如， 当使用 DES 加密的口令时， 对应项应为：

[.programlisting]
....
crypt_default	=	des blf md5
....

在每一台基于 FreeBSD 的 NIS 服务器和客户机上完成上述工作之后， 就可以肯定您的网络上它们都在使用同样的口令格式了。 如果在 NIS 客户机上做身份验证时发生问题， 这也是第一个可能出现问题的地方。 注意： 如果您希望在混合的网络上部署 NIS 服务器， 可能就需要在所有系统上都使用 DES， 因为这是所有系统都能够支持的最低限度的公共标准。

[[network-dhcp]]
== 网络自动配置 (DHCP)

=== 什么是 DHCP？

DHCP， 动态主机配置协议， 是一种让系统得以连接到网络上， 并获取所需要的配置参数手段。 FreeBSD 使用来自 OpenBSD 3.7 的 OpenBSD `dhclient`。 这里提供的所有关于 `dhclient` 的信息， 都是以 ISC 或 OpenBSD DHCP 客户端程序为准的。 DHCP 服务器是 ISC 软件包的一部分。

=== 这一节都介绍哪些内容

这一节描述了 ISC 和 DHCP 系统中的客户端， 以及和 ISC DHCP 系统中的服务器端的组件。 客户端程序， `dhclient`， 是随 FreeBSD 作为它的一部分提供的； 而服务器部分， 则可以通过 package:net/isc-dhcp31-server[] port 得到。 man:dhclient[8]、 man:dhcp-options[5]、 以及 man:dhclient.conf[5] 联机手册， 加上下面所介绍的参考文献， 都是非常有用的资源。

=== 它如何工作

当 DHCP 客户程序， `dhclient` 在客户机上运行时， 它会开始广播请求配置信息的消息。 默认情况下， 这些请求是在 UDP 端口 68 上。 服务器通过 UDP 67 给出响应， 向客户机提供一个 IP 地址， 以及其他有关的配置参数， 例如子网掩码、 路由器， 以及 DNS 服务器。 所有这些信息都会以 DHCP "lease" 的形式给出， 并且只在一段特定的时间内有效 (这是由 DHCP 服务器的维护者配置的)。 这样， 那些已经断开网络的客户机使用的陈旧的 IP 地址就能被自动地回收了。

DHCP 客户程序可以从服务器端获取大量的信息。 关于能获得的信息的详细列表， 请参考 man:dhcp-options[5]。

=== FreeBSD 集成

FreeBSD 完全地集成了 OpenBSD 的 DHCP 客户端， `dhclient`。 DHCP 客户端支持在安装程序和基本系统中均有提供， 这使得您不再需要去了解那些已经运行了 DHCP 服务器的网络的具体配置参数。

sysinstall 能够支持 DHCP。 在 sysinstall 中配置网络接口时， 它询问的第二个问题便是： "Do you want to try DHCP configuration of the interface? (您是否希望在此接口上尝试 DHCP 配置?)"。 如果做肯定的回答， 则将运行 `dhclient`， 一旦成功， 则将自动地填写网络配置信息。

要在系统启动时使用 DHCP， 您必须做两件事：

* 您的内核中， 必须包含 [.filename]#bpf# 设备。 如果需要这样做， 需要将 `device bpf` 添加到内核的编译配置文件中， 并重新编译内核。 要了解关于编译内核的进一步信息， 请参见 crossref:kernelconfig[kernelconfig,配置FreeBSD的内核]。
+ 
[.filename]#bpf# 设备已经是 FreeBSD 发行版中默认的 [.filename]#GENERIC# 内核的一部分了， 因此如果您没有对内核进行定制， 则不用创建一份新的内核配置文件， DHCP 就能工作了。
+
[NOTE]
====
对于那些安全意识很强的人来说， 您应该知道 [.filename]#bpf# 也是包侦听工具能够正确工作的条件之一 (当然， 它们还需要以 `root` 身份运行才行)。 [.filename]#bpf#_是_ 使用 DHCP 所必须的， 但如果您对安全非常敏感， 则很可能会有理由不把 [.filename]#bpf# 加入到您的内核配置中， 直到您真的需要使用 DHCP 为止。
====

* 编辑您的 [.filename]#/etc/rc.conf# 并加入下面的设置：
+
[.programlisting]
....
ifconfig_fxp0="DHCP"
....

+
[NOTE]
====
务必将 `fxp0` 替换为您希望自动配置的网络接口的名字， 您可以在 crossref:config[config-network-setup,设置网卡] 找到更进一步的介绍。
====

+ 
如果您希望使用另一位置的 `dhclient`， 或者需要给 `dhclient` 传递其他参数， 还可以添加下面的配置 (根据需要进行修改)：
+
[.programlisting]
....
dhclient_program="/sbin/dhclient"
dhclient_flags=""
....

DHCP 服务器， dhcpd， 是作为 package:net/isc-dhcp31-server[] port 的一部分提供的。 这个 port 包括了 ISC DHCP 服务器及其文档。

=== 文件

* [.filename]#/etc/dhclient.conf#
+ 
`dhclient` 需要一个配置文件， [.filename]#/etc/dhclient.conf#。 一般说来， 这个文件中只包括注释， 而默认值基本上都是合理的。 这个配置文件在 man:dhclient.conf[5] 联机手册中进行了进一步的阐述。
* [.filename]#/sbin/dhclient#
+ 
`dhclient` 是一个静态连编的， 它被安装到 [.filename]#/sbin# 中。 man:dhclient[8] 联机手册给出了关于 `dhclient` 的进一步细节。
* [.filename]#/sbin/dhclient-script#
+ 
`dhclient-script` 是一个 FreeBSD 专用的 DHCP 客户端配置脚本。 在 man:dhclient-script[8] 中对它进行了描述， 但一般来说， 用户不需要对其进行任何修改， 就能够让一切正常运转了。
* [.filename]#/var/db/dhclient.leases#
+ 
DHCP 客户程序会维护一个数据库来保存有效的 lease， 它们被以日志的形式保存到这个文件中。 man:dhclient.leases[5] 给出了更为细致的介绍。

=== 进阶读物

DHCP 协议的完整描述是 http://www.freesoft.org/CIE/RFC/2131/[RFC 2131]。 关于它的其他信息资源的站点 http://www.dhcp.org/[http://www.dhcp.org/] 也提供了详尽的资料。

[[network-dhcp-server]]
=== 安装和配置 DHCP 服务器

==== 这一章包含哪些内容

这一章提供了关于如何在 FreeBSD 系统上使用 ISC (Internet 系统协会) 的 DHCP 实现套件来架设 DHCP 服务器的信息。

DHCP 套件中的服务器部分并没有作为 FreeBSD 的一部分来提供， 因此您需要安装 package:net/isc-dhcp31-server[] port 才能提供这个服务。 请参见 crossref:ports[ports,安装应用程序. Packages 和 Ports] 以了解关于如何使用 Ports Collection 的进一步详情。

==== 安装 DHCP 服务器

为了在您的 FreeBSD 系统上进行配置以便作为 DHCP 服务器来使用， 需要把 man:bpf[4] 设备编译进内核。 要完成这项工作， 需要将 `device bpf` 加入到您的内核配置文件中， 并重新联编内核。 要得到关于如何联编内核的进一步信息， 请参见 crossref:kernelconfig[kernelconfig,配置FreeBSD的内核]。

[.filename]#bpf# 设备是 FreeBSD 所附带的 [.filename]#GENERIC# 内核中已经联入的组件， 因此您并不需要为了让 DHCP 正常工作而特别地定制内核。

[NOTE]
====
如果您有较强的安全意识， 应该注意 [.filename]#bpf# 同时也是让听包程序能够正确工作的设备 (尽管这类程序仍然需要以特权用户身份运行)。 [.filename]#bpf#_是_ 使用 DHCP 所必需的， 但如果您对安全非常敏感， 您可能会不希望将 [.filename]#bpf# 放进内核， 直到您真的认为 DHCP 是必需的为止。
====

接下来要做的是编辑示范的 [.filename]#dhcpd.conf#， 它由 package:net/isc-dhcp31-server[] port 安装。 默认情况下， 它的名字应该是 [.filename]#/usr/local/etc/dhcpd.conf.sample#， 在开始修改之前， 您需要把它复制为 [.filename]#/usr/local/etc/dhcpd.conf#。

==== 配置 DHCP 服务器

[.filename]#dhcpd.conf# 包含了一系列关于子网和主机的定义， 下面的例子可以帮助您理解它：

[.programlisting]
....
option domain-name "example.com";<.>
option domain-name-servers 192.168.4.100;<.>
option subnet-mask 255.255.255.0;<.>

default-lease-time 3600;<.>
max-lease-time 86400;<.>
ddns-update-style none;<.>

subnet 192.168.4.0 netmask 255.255.255.0 {
  range 192.168.4.129 192.168.4.254;<.>
  option routers 192.168.4.1;<.>
}

host mailhost {
  hardware ethernet 02:03:04:05:06:07;<.>
  fixed-address mailhost.example.com;<.>
}
....

<.> 这个选项指定了提供给客户机作为默认搜索域的域名。 请参考 man:resolv.conf[5] 以了解关于这一概念的详情。

<.> 这个选项用于指定一组客户机使用的 DNS 服务器， 它们之间以逗号分隔。

<.> 提供给客户机的子网掩码。

<.> 客户机可以请求租约的有效期， 而如果没有， 则服务器将指定一个租约有效期， 也就是这个值 (单位是秒)。

<.> 这是服务器允许租出地址的最大时长。 如果客户机请求了更长的租期， 则它将得到一个地址， 但其租期仅限于 `max-lease-time` 秒。

<.> 这个选项用于指定 DHCP 服务器在一个地址被接受或释放时是否应对应尝试更新 DNS。 在 ISC 实现中， 这一选项是 _必须指定的_。

<.> 指定地址池中可以用来分配给客户机的 IP 地址范围。 在这个范围之间， 以及其边界的 IP 地址将分配给客户机。

<.> 定义客户机的默认网关。

<.> 主机的硬件 MAC 地址 (这样 DHCP 服务器就能够在接到请求时知道请求的主机身份)。

<.> 指定总是得到同一 IP 地址的主机。 请注意在此处使用主机名是对的， 因为 DHCP 服务器会在返回租借地址信息之前自行解析主机名。

在配制好 [.filename]#dhcpd.conf# 之后， 应在 [.filename]#/etc/rc.conf# 中启用 DHCP 服务器， 也就是增加：

[.programlisting]
....
dhcpd_enable="YES"
dhcpd_ifaces="dc0"
....

此处的 `dc0` 接口名应改为 DHCP 服务器需要监听 DHCP 客户端请求的接口 (如果有多个， 则用空格分开)。

接下来， 可以用下面的命令来启动服务：

[source,shell]
....
# /usr/local/etc/rc.d/isc-dhcpd start
....

如果未来您需要修改服务器的配置， 请务必牢记发送 `SIGHUP` 信号给 dhcpd 并 _不会_ 导致配置文件的重新加载， 而这在其他服务程序中则是比较普遍的约定。 您需要发送 `SIGTERM` 信号来停止进程， 然后使用上面的命令来重新启动它。

==== 文件

* [.filename]#/usr/local/sbin/dhcpd#
+ 
dhcpd 是静态连接的， 并安装到 [.filename]#/usr/local/sbin# 中。 随 port 安装的 man:dhcpd[8] 联机手册提供了关于 dhcpd 更为详尽的信息。
* [.filename]#/usr/local/etc/dhcpd.conf#
+ 
dhcpd 需要配置文件， 即 [.filename]#/usr/local/etc/dhcpd.conf# 才能够向客户机提供服务。 这个文件需要包括应提供给客户机的所有信息， 以及关于服务器运行的其他信息。 此配置文件的详细描述可以在随 port 安装的 man:dhcpd.conf[5] 联机手册上找到。
* [.filename]#/var/db/dhcpd.leases#
+ 
DHCP 服务器会维护一个它签发的租用地址数据库， 并保存在这个文件中， 这个文件是以日志的形式保存的。 随 port 安装的 man:dhcpd.leases[5] 联机手册提供了更详细的描述。
* [.filename]#/usr/local/sbin/dhcrelay#
+ 
dhcrelay 在更为复杂的环境中， 可以用来支持使用 DHCP 服务器转发请求给另一个独立网络上的 DHCP 服务器。 如果您需要这个功能， 需要安装 package:net/isc-dhcp31-relay[] port。 man:dhcrelay[8] 联机手册提供了更为详尽的介绍。

[[network-dns]]
== 域名系统 (DNS)

=== 纵览

FreeBSD 在默认情况下使用一个版本的 BIND (Berkeley Internet Name Domain)， 这是目前最为流行的 DNS 协议实现。 DNS 是一种协议， 可以通过它将域名同 IP 地址相互对应。 例如， 查询 `www.FreeBSD.org` 将得到 FreeBSD Project 的 web 服务器的 IP 地址， 而查询 `ftp.FreeBSD.org` 则将得到响应的 FTP 机器的 IP 地址。 类似地， 也可以做相反的事情。 查询 IP 地址可以得到其主机名。 当然， 完成 DNS 查询并不需要在系统中运行域名服务器。

目前， 默认情况下FreeBSD 使用的是 BIND9 DNS 服务软件。 我们内建于系统中的版本提供了增强的安全特性、 新的文件目录结构， 以及自动的 man:chroot[8] 配置。

在 Internet 上的 DNS 是通过一套较为复杂的权威根域名系统， 顶级域名 (TLD)， 以及一系列小规模的， 提供少量域名解析服务并对域名信息进行缓存的域名服务器组成的。

目前， BIND 由 Internet Systems Consortium https://www.isc.org/[https://www.isc.org/] 维护。

=== 术语

要理解这份文档， 需要首先了解一些相关的 DNS 术语。

[.informaltable]
[cols="1,1", frame="none", options="header"]
|===
| 术语
| 定义

|正向 DNS
|将域名映射到 IP 地址

|原点 (Origin)
|表示特定域文件所在的域

|named, BIND
|在 FreeBSD 中 BIND 域名服务器软件包的常见叫法。

|解析器 (Resolver)
|计算机用以向域名服务器查询域名信息的一个系统进程

|反向 DNS
|将 IP 地址映射为主机名

|根域
|Internet 域层次的起点。 所有的域都在根域之下， 类似文件系统中， 文件都在根目录之下那样。

|域 (Zone)
|独立的域， 子域， 或者由同一机构管理的 DNS 的一部分。
|===

域的例子：

* `.` 在本文档中通常指代根域。
* `org.` 是根域之下的一个顶级域名 (TLD)。
* `example.org.` 是在 `org.` TLD 之下的一个域。
* `1.168.192.in-addr.arpa` 是一个表示所有 `192.168.1.*` IP 地址空间中 IP 地址的域。

如您所见， 域名中越细节的部分会越靠左出现。 例如， `example.org.` 就比 `org.` 范围更小， 类似地 `org.` 又比根域更小。 域名各个部分的格局与文件系统十分类似： [.filename]#/dev# 目录在根目录之下， 等等。

=== 运行域名服务器的理由

域名服务器通常会有两种形式： 权威域名服务器， 以及缓存域名服务器。

下列情况需要有权威域名服务器：

* 想要向全世界提供 DNS 信息， 并对请求给出权威应答。
* 注册了类似 `example.org` 的域， 而需要将 IP 指定到其下的主机名上。
* 某个 IP 地址块需要反向 DNS 项 (IP 到主机名)。
* 备份服务器， 或常说的从 (slave) 服务器， 会在主服务器出现问题或无法访问时来应答查询请求。

下列情况需要有缓存域名服务器：

* 本地的 DNS 服务器能够缓存， 并比直接向外界的域名服务器请求更快地得到应答。

当有人查询 `www.FreeBSD.org` 时，解析器通常会向上级 ISP 的域名服务器发出请求， 并获得回应。 如果有本地的缓存 DNS 服务器， 查询只有在第一次被缓存 DNS 服务器发到外部世界。 其他的查询不会发向局域网外， 因为它们已经有在本地的缓存了。

=== DNS 如何运作

在 FreeBSD 中， BIND 服务程序被称为 named。

[.informaltable]
[cols="1,1", frame="none", options="header"]
|===
| 文件
| 描述

|man:named[8]
|BIND 服务程序

|man:rndc[8]
|域名服务控制程序

|[.filename]#/etc/namedb#
|BIND 存放域名信息的位置。

|[.filename]#/etc/namedb/named.conf#
|域名服务配置文件
|===

随在服务器上配置的域的性质不同， 域的定义文件一般会存放到 [.filename]#/etc/namedb# 目录中的 [.filename]#master#、 [.filename]#slave#， 或 [.filename]#dynamic# 子目录中。 这些文件中提供了域名服务器在响应查询时所需要的 DNS 信息。

=== 启动 BIND

由于 BIND 是默认安装的， 因此配置它相对而言很简单。

默认的 named 配置， 是在 man:chroot[8] 环境中提供基本的域名解析服务， 并且只限于监听本地 IPv4 回环地址 (127.0.0.1)。 如果希望启动这一配置， 可以使用下面的命令：

[source,shell]
....
# /etc/rc.d/named onestart
....

如果希望 named 服务在每次启动的时候都能够启动， 需要在 [.filename]#/etc/rc.conf# 中加入：

[.programlisting]
....
named_enable="YES"
....

当然， 除了这份文档所介绍的配置选项之外， 在 [.filename]#/etc/namedb/named.conf# 中还有很多其它的选项。 不过， 如果您需要了解 FreeBSD 中用于启动 named 的那些选项的话， 则可以查看 [.filename]#/etc/defaults/rc.conf# 中的 `named_*` 参数， 并参考 man:rc.conf[5] 联机手册。 除此之外， crossref:config[configtuning-rcd,在 FreeBSD 中使用 rc] 也是一个不错的起点。

=== 配置文件

目前， named 的配置文件存放于 [.filename]#/etc/namedb# 目录， 在使用前应根据需要进行修改， 除非您只打算让它完成简单的域名解析服务。 这个目录同时也是您进行绝大多数配置的地方。

==== [.filename]#/etc/namedb/named.conf#

[.programlisting]
....
// $FreeBSD$
//
// Refer to the named.conf(5) and named(8) man pages, and the documentation
// in /usr/shared/doc/bind9 for more details.
//
// If you are going to set up an authoritative server, make sure you
// understand the hairy details of how DNS works.  Even with
// simple mistakes, you can break connectivity for affected parties,
// or cause huge amounts of useless Internet traffic.

options {
	// Relative to the chroot directory, if any
	directory	"/etc/namedb";
	pid-file	"/var/run/named/pid";
	dump-file	"/var/dump/named_dump.db";
	statistics-file	"/var/stats/named.stats";

// If named is being used only as a local resolver, this is a safe default.
// For named to be accessible to the network, comment this option, specify
// the proper IP address, or delete this option.
	listen-on	{ 127.0.0.1; };

// If you have IPv6 enabled on this system, uncomment this option for
// use as a local resolver.  To give access to the network, specify
// an IPv6 address, or the keyword "any".
//	listen-on-v6	{ ::1; };

// These zones are already covered by the empty zones listed below.
// If you remove the related empty zones below, comment these lines out.
	disable-empty-zone "255.255.255.255.IN-ADDR.ARPA";
	disable-empty-zone "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA";
	disable-empty-zone "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.IP6.ARPA";

// If you've got a DNS server around at your upstream provider, enter
// its IP address here, and enable the line below.  This will make you
// benefit from its cache, thus reduce overall DNS traffic in the Internet.
/*
	forwarders {
		127.0.0.1;
	};
*/

// If the 'forwarders' clause is not empty the default is to 'forward first'
// which will fall back to sending a query from your local server if the name
// servers in 'forwarders' do not have the answer.  Alternatively you can
// force your name server to never initiate queries of its own by enabling the
// following line:
//	forward only;

// If you wish to have forwarding configured automatically based on
// the entries in /etc/resolv.conf, uncomment the following line and
// set named_auto_forward=yes in /etc/rc.conf.  You can also enable
// named_auto_forward_only (the effect of which is described above).
//	include "/etc/namedb/auto_forward.conf";
....

正如注释所言， 如果希望从上级缓存中受益， 可以在此处启用 `forwarders`。 正常情况下， 域名服务器会逐级地查询 Internet 来找到特定的域名服务器， 直到得到答案为止。 这个选项将让它首先查询上级域名服务器 (或另外提供的域名服务器)， 从而从它们的缓存中得到结果。 如果上级域名服务器是一个繁忙的高速域名服务器， 则启用它将有助于改善服务品质。

[WARNING]
====

``127.0.0.1``__不会__ 正常工作。 一定要把地址改为您上级服务器的 IP 地址。
====

[.programlisting]
....
	/*
	   Modern versions of BIND use a random UDP port for each outgoing
	   query by default in order to dramatically reduce the possibility
	   of cache poisoning.  All users are strongly encouraged to utilize
	   this feature, and to configure their firewalls to accommodate it.

	   AS A LAST RESORT in order to get around a restrictive firewall
	   policy you can try enabling the option below.  Use of this option
	   will significantly reduce your ability to withstand cache poisoning
	   attacks, and should be avoided if at all possible.

	   Replace NNNNN in the example with a number between 49160 and 65530.
	*/
	// query-source address * port NNNNN;
};

// If you enable a local name server, don't forget to enter 127.0.0.1
// first in your /etc/resolv.conf so this server will be queried.
// Also, make sure to enable it in /etc/rc.conf.

// The traditional root hints mechanism. Use this, OR the slave zones below.
zone "." { type hint; file "named.root"; };

/*	Slaving the following zones from the root name servers has some
	significant advantages:
	1. Faster local resolution for your users
	2. No spurious traffic will be sent from your network to the roots
	3. Greater resilience to any potential root server failure/DDoS

	On the other hand, this method requires more monitoring than the
	hints file to be sure that an unexpected failure mode has not
	incapacitated your server.  Name servers that are serving a lot
	of clients will benefit more from this approach than individual
	hosts.  Use with caution.

	To use this mechanism, uncomment the entries below, and comment
	the hint zone above.
*/
/*
zone "." {
	type slave;
	file "slave/root.slave";
	masters {
		192.5.5.241;	// F.ROOT-SERVERS.NET.
	};
	notify no;
};
zone "arpa" {
	type slave;
	file "slave/arpa.slave";
	masters {
		192.5.5.241;	// F.ROOT-SERVERS.NET.
	};
	notify no;
};
zone "in-addr.arpa" {
	type slave;
	file "slave/in-addr.arpa.slave";
	masters {
		192.5.5.241;	// F.ROOT-SERVERS.NET.
	};
	notify no;
};
*/

/*	Serving the following zones locally will prevent any queries
	for these zones leaving your network and going to the root
	name servers.  This has two significant advantages:
	1. Faster local resolution for your users
	2. No spurious traffic will be sent from your network to the roots
*/
// RFC 1912
zone "localhost"	{ type master; file "master/localhost-forward.db"; };
zone "127.in-addr.arpa" { type master; file "master/localhost-reverse.db"; };
zone "255.in-addr.arpa"	{ type master; file "master/empty.db"; };

// RFC 1912-style zone for IPv6 localhost address
zone "0.ip6.arpa"	{ type master; file "master/localhost-reverse.db"; };

// "This" Network (RFCs 1912 and 3330)
zone "0.in-addr.arpa"		{ type master; file "master/empty.db"; };

// Private Use Networks (RFC 1918)
zone "10.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "16.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "17.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "18.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "19.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "20.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "21.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "22.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "23.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "24.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "25.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "26.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "27.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "28.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "29.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "30.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "31.172.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "168.192.in-addr.arpa"	{ type master; file "master/empty.db"; };

// Link-local/APIPA (RFCs 3330 and 3927)
zone "254.169.in-addr.arpa"	{ type master; file "master/empty.db"; };

// TEST-NET for Documentation (RFC 3330)
zone "2.0.192.in-addr.arpa"	{ type master; file "master/empty.db"; };

// Router Benchmark Testing (RFC 3330)
zone "18.198.in-addr.arpa"	{ type master; file "master/empty.db"; };
zone "19.198.in-addr.arpa"	{ type master; file "master/empty.db"; };

// IANA Reserved - Old Class E Space
zone "240.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "241.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "242.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "243.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "244.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "245.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "246.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "247.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "248.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "249.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "250.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "251.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "252.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "253.in-addr.arpa"		{ type master; file "master/empty.db"; };
zone "254.in-addr.arpa"		{ type master; file "master/empty.db"; };

// IPv6 Unassigned Addresses (RFC 4291)
zone "1.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "3.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "4.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "5.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "6.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "7.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "8.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "9.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "a.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "b.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "c.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "d.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "e.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "0.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "1.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "2.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "3.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "4.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "5.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "6.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "7.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "8.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "9.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "a.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "b.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "0.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "1.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "2.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "3.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "4.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "5.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "6.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "7.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };

// IPv6 ULA (RFC 4193)
zone "c.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "d.f.ip6.arpa"		{ type master; file "master/empty.db"; };

// IPv6 Link Local (RFC 4291)
zone "8.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "9.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "a.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "b.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };

// IPv6 Deprecated Site-Local Addresses (RFC 3879)
zone "c.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "d.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "e.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };
zone "f.e.f.ip6.arpa"		{ type master; file "master/empty.db"; };

// IP6.INT is Deprecated (RFC 4159)
zone "ip6.int"			{ type master; file "master/empty.db"; };

// NB: Do not use the IP addresses below, they are faked, and only
// serve demonstration/documentation purposes!
//
// Example slave zone config entries.  It can be convenient to become
// a slave at least for the zone your own domain is in.  Ask
// your network administrator for the IP address of the responsible
// master name server.
//
// Do not forget to include the reverse lookup zone!
// This is named after the first bytes of the IP address, in reverse
// order, with ".IN-ADDR.ARPA" appended, or ".IP6.ARPA" for IPv6.
//
// Before starting to set up a master zone, make sure you fully
// understand how DNS and BIND work.  There are sometimes
// non-obvious pitfalls.  Setting up a slave zone is usually simpler.
//
// NB: Don't blindly enable the examples below. :-)  Use actual names
// and addresses instead.

/* An example dynamic zone
key "exampleorgkey" {
	algorithm hmac-md5;
	secret "sf87HJqjkqh8ac87a02lla==";
};
zone "example.org" {
	type master;
	allow-update {
		key "exampleorgkey";
	};
	file "dynamic/example.org";
};
*/

/* Example of a slave reverse zone
zone "1.168.192.in-addr.arpa" {
	type slave;
	file "slave/1.168.192.in-addr.arpa";
	masters {
		192.168.1.1;
	};
};
*/
....

在 [.filename]#named.conf# 中， 还给出了从域、转发域和反解析域的例子。

如果新增了域， 就必需在 [.filename]#named.conf# 中加入对应的项目。

例如， 用于 `example.org` 的域文件的描述类似下面这样：

[.programlisting]
....
zone "example.org" {
	type master;
	file "master/example.org";
};
....

如 `type` 语句所标示的那样， 这是一个主域， 其信息保存在 [.filename]#/etc/namedb/master/example.org# 中， 如 `file` 语句所示。

[.programlisting]
....
zone "example.org" {
	type slave;
	file "slave/example.org";
};
....

在从域的情形中， 所指定的域的信息会从主域名服务器传递过来， 并保存到对应的文件中。 当主域服务器发生问题或不可达时， 从域名服务器就有一份可用的域名信息， 从而能够对外提供服务。

==== 域文件

下面的例子展示了用于 `example.org` 的主域文件 (存放于 [.filename]#/etc/namedb/master/example.org#)：

[.programlisting]
....
$TTL 3600        ; 1 hour default TTL
example.org.    IN      SOA      ns1.example.org. admin.example.org. (
                                2006051501      ; Serial
                                10800           ; Refresh
                                3600            ; Retry
                                604800          ; Expire
                                300             ; Negative Reponse TTL
                        )

; DNS Servers
                IN      NS      ns1.example.org.
                IN      NS      ns2.example.org.

; MX Records
                IN      MX 10   mx.example.org.
                IN      MX 20   mail.example.org.

                IN      A       192.168.1.1

; Machine Names
localhost       IN      A       127.0.0.1
ns1             IN      A       192.168.1.2
ns2             IN      A       192.168.1.3
mx              IN      A       192.168.1.4
mail            IN      A       192.168.1.5

; Aliases
www             IN      CNAME   example.org.
....

请注意以 "." 结尾的主机名是全称主机名， 而结尾没有 "." 的则是相对于原点的主机名。 例如， `ns1` 将被转换为 `ns1.example.org.`

域信息文件的格式如下：

[.programlisting]
....
记录名          IN 记录类型     值
....

最常用的 DNS 记录：

SOA::
域权威开始

NS::
权威域名服务器

A::
主机地址

CNAME::
别名对应的正规名称

MX::
邮件传递服务器

PTR::
域名指针 (用于反向 DNS) 

[.programlisting]
....
example.org. IN SOA ns1.example.org. admin.example.org. (
                        2006051501      ; Serial
                        10800           ; Refresh after 3 hours
                        3600            ; Retry after 1 hour
                        604800          ; Expire after 1 week
                        300 )           ; Negative Reponse TTL
....

`example.org.`::
域名， 同时也是这个域信息文件的原点。

`ns1.example.org.`::
该域的主/权威域名服务器。

`admin.example.org.`::
此域的负责人的电子邮件地址， 其中 "@" 需要换掉 (mailto:admin@example.org[admin@example.org] 对应 `admin.example.org`)

`2006051501`::
文件的序号。 每次修改域文件时都必须增加这个数字。 现今， 许多管理员会考虑使用 `yyyymmddrr` 这样的格式来表示序号。 `2006051501` 通常表示上次修改于 05/15/2006， 而后面的 `01` 则表示在那天的第一次修改。 序号非常重要， 它用于通知从域服务器更新数据。

[.programlisting]
....
       IN NS           ns1.example.org.
....

这是一个 NS 项。 每个准备提供权威应答的服务器都必须有一个对应项。

[.programlisting]
....
localhost       IN      A       127.0.0.1
ns1             IN      A       192.168.1.2
ns2             IN      A       192.168.1.3
mx              IN      A       192.168.1.4
mail            IN      A       192.168.1.5
....

A 记录指明了机器名。 正如在前面所看到的， `ns1.example.org` 将解析为 `192.168.1.2`。

[.programlisting]
....
                IN      A       192.168.1.1
....

这一行把当前原点 `example.org` 指定为使用 IP 地址 `192.168.1.1`。

[.programlisting]
....
www             IN CNAME        @
....

正规名 (CNAME) 记录通常用于为某台机器指定别名。 在这个例子中， 将 `www` 指定成了 "主" 机器的一个别名， 后者的名字与域名 `example.org` (`192.168.1.1`) 相同。 CNAME 不能同与之有相同名字的任何其它记录并存。

[.programlisting]
....
               IN MX   10      mail.example.org.
....

MX 记录表示哪个邮件服务器负责接收发到这个域的邮件。 `mail.example.org` 是邮件服务器的主机名， 而 10 则是它的优先级。

可以有多台邮件服务器， 其优先级分别是 10、 20 等等。 尝试向 `example.org` 投递邮件的服务器， 会首先尝试优先级最高的 MX (优先级数值最小的记录)、 接着尝试次高的， 并重复这一过程直到邮件递达为止。

in-addr.arpa 域名信息文件 (反向 DNS)， 采用的格式是同样的， 只是 PTR 项代替了 A 或 CNAME 的位置。

[.programlisting]
....
$TTL 3600

1.168.192.in-addr.arpa. IN SOA ns1.example.org. admin.example.org. (
                        2006051501      ; Serial
                        10800           ; Refresh
                        3600            ; Retry
                        604800          ; Expire
                        300 )           ; Negative Reponse TTL

        IN      NS      ns1.example.org.
        IN      NS      ns2.example.org.

1       IN      PTR     example.org.
2       IN      PTR     ns1.example.org.
3       IN      PTR     ns2.example.org.
4       IN      PTR     mx.example.org.
5       IN      PTR     mail.example.org.
....

这个文件给出了上述假想域中 IP 地址到域名的映射关系。

需要说明的是， 在 PTR 记录右侧的名字必须是全称域名 (也就是必须以 "." 结束)。

=== 缓存域名服务器

缓存域名服务器是一种主要承担解析递归查询角色的域名服务器。 它简单地自行进行查询， 并将查询结果记住以备后续使用。

=== 安全

尽管 BIND 是最为常用的 DNS 实现， 但它总是有一些安全问题。 时常会有人发现一些可能的甚至可以利用的安全漏洞。 

尽管 FreeBSD 会自动将 named 放到 man:chroot[8] 环境中运行， 但仍有一些其它可用的安全机制来帮助您规避潜在的针对 DNS 服务的攻击。

阅读 http://www.cert.org/[CERT] 的安全公告， 并订阅 the {freebsd-security-notifications} 是一个有助于帮助您了解最新 Internet 及 FreeBSD 安全问题的好习惯。

[TIP]
====

如果发现了问题， 确保源代码是最新的， 并重新联编一份 named 有可能会有所帮助。
====

=== 进一步阅读

BIND/named 联机手册： man:rndc[8] man:named[8] man:named.conf[8]

* https://www.isc.org/software/bind[官方的 ISC BIND 页面]
* https://www.isc.org/software/guild[Official ISC BIND Forum]
* http://www.oreilly.com/catalog/dns5/[O'Reilly DNS 和 BIND 第 5 版]
* http://www.rfc-editor.org/rfc/rfc1034.txt[RFC1034 - 域名 - 概念和工具]
* http://www.rfc-editor.org/rfc/rfc1035.txt[RFC1035 - 域名 - 实现及其标准]

[[network-apache]]
== Apache HTTP 服务器

=== 纵览

FreeBSD 被用于运行许多全球最为繁忙的 web 站点。 大多数 Internet 上的 web 服务器， 都使用 Apache HTTP 服务器。 Apache 软件包可以在您的 FreeBSD 安装盘上找到。 如果没有在首次安装时附带安装 Apache， 则可以通过 package:www/apache13[] 或 package:www/apache22[] port 来安装。

一旦成功地安装了 Apache， 就必须对其进行配置。

[NOTE]
====
这一节介绍了 1.3.X 版本的 Apache HTTP 服务器 的配置， 因为它是随 FreeBSD 一同使用的最多的版本。 Apache 2.X 引入了很多新技术， 但在此并不讨论。 要了解关于 Apache 2.X 的更多资料， 请参见 http://httpd.apache.org/[http://httpd.apache.org/]。
====

=== 配置

主要的 Apache HTTP Server 配置文件， 在 FreeBSD 上会安装为 [.filename]#/usr/local/etc/apache/httpd.conf#。 这是一个典型的 UNIX(R) 文本配置文件， 它使用 `#` 作为注释符。 关于全部配置选项的详尽介绍超出了本书的范围， 这里将只介绍最常被修改的那些。

`ServerRoot "/usr/local"`::
这指定了 Apache 安装的顶级目录。 执行文件被放到服务器根目录 (server root) 的 [.filename]#bin# 和 [.filename]#sbin# 子目录中， 而配置文件则位于 [.filename]#etc/apache#。

`ServerAdmin you@your.address`::
这个地址是在服务器发生问题时应发送电子邮件的地址， 它会出现在服务器生成的页面上， 例如错误页面。

`ServerName www.example.com`::
`ServerName` 允许您配置发送回客户端的主机名， 如果您的服务器被用户以别的名字访问 (例如， 使用 `www` 而不是主机本身的真实名字)。

`DocumentRoot "/usr/local/www/data"`::
`DocumentRoot`： 这个目录是您的文档所在的目录。 默认情况下， 所有的请求都会从这个位置去获取， 但也可以通过符号连接和别名指定其它的位置。

在修改配置之前备份 Apache 的配置文件永远是一个好习惯。 一旦对初始配置满意了， 就可以开始运行 Apache 了。

=== 运行 Apache

与许多其它网络服务不同， Apache 并不依赖 inetd 超级服务器来运行。 一般情况下会把它配置为一个独立的服务器， 以期在客户的 web 浏览器连入 HTTP 请求时， 能够获得更好的性能。 它提供了一个 shell 脚本来使启动、 停止和重新启动服务器变得尽可能地简单。 首次启动 Apache， 只需执行：

[source,shell]
....
# /usr/local/sbin/apachectl start
....

可以在任何时候使用下面的命令来停止服务：

[source,shell]
....
# /usr/local/sbin/apachectl stop
....

当由于某种原因修改了配置文件之后， 需要重启服务器：

[source,shell]
....
# /usr/local/sbin/apachectl restart
....

要在重启 Apache 服务器时不中断当前的连接， 则应运行：

[source,shell]
....
# /usr/local/sbin/apachectl graceful
....

更多的信息， 可以在 man:apachectl[8] 联机手册中找到。

要在系统启动时启动 Apache， 则应在 [.filename]#/etc/rc.conf# 中加入：

[.programlisting]
....
apache_enable="YES"
....

或者对于Apache 2.2：

[.programlisting]
....
apache22_enable="YES"
....

如果您希望在系统引导时启动 Apache `httpd` 程序并指定其它一些选项， 则可以把下面的行加到 [.filename]#rc.conf#：

[.programlisting]
....
apache_flags=""
....

现在 web 服务器就开始运行了， 您可以使用 web 浏览器打开 `http://localhost/`。 默认显示的 web 页面是 [.filename]#/usr/local/www/data/index.html#。

=== 虚拟主机

Apache 支持两种不同类型的虚拟主机。 第一种方法是基于名字的虚拟主机。 基于名字的虚拟主机使用客户机发来的 HTTP/1.1 头来辨别主机名。 这使得不同的域得以共享同一个 IP 地址。

要配置 Apache 来使用基于名字的虚拟主机， 需要把类似下面的项加到您的 [.filename]#httpd.conf# 中：

[.programlisting]
....
NameVirtualHost *
....

如果您的 web 服务器的名字是 `www.domain.tld`， 而您希望建立一个 `www.someotherdomain.tld` 的虚拟域， 则应在 [.filename]#httpd.conf# 中加入：

[source,shell]
....
<VirtualHost *>
ServerName www.domain.tld
DocumentRoot /www/domain.tld
</VirtualHost>

<VirtualHost *>
ServerName www.someotherdomain.tld
DocumentRoot /www/someotherdomain.tld
</VirtualHost>
....

您需要把上面的地址和文档路径改为所使用的那些。

要了解关于虚拟主机的更多信息， 请参考官方的 Apache 文档， 这些文档可以在 http://httpd.apache.org/docs/vhosts/[http://httpd.apache.org/docs/vhosts/] 找到。

=== Apache 模块

有许多不同的 Apache 模块， 它们可以在基本的服务器基础上提供许多附加的功能。 FreeBSD 的 Ports Collection 为安装 Apache 和常用的附加模块提供了非常方便的方法。

==== mod_ssl

mod_ssl 这个模块使用 OpenSSL 库， 来提供通过 安全套接字层 (SSL v2/v3) 和 传输层安全 (TLS v1) 协议的强加密能力。 这个模块提供了从某一受信的证书签署机构申请签名证书所需的所有工具， 您可以藉此在 FreeBSD 上运行安全的 web 服务器。

如果您未曾安装 Apache， 也可以直接安装一份包含了 mod_ssl 的版本的 Apache 1.3.X， 其方法是通过 package:www/apache13-modssl[] port 来进行。 SSL 支持已经作为 Apache 2.X 的一部分提供， 您可以通过 package:www/apache22[] port 来安装后者。

==== 语言绑定

Apache对于一些主要的脚本语言都有相应的模块。 这些模块使得完全使用某种脚本语言来写 Apache 模块成为可能。 他们通常也被嵌入到服务器作为一个常驻内存的解释器， 以避免启动一个外部解释器对于下一节将描述的动态网站所需时间和资源上的开销。 

=== 动态网站

在过去的十年里，越来越多的企业为了增加收益和暴光率而转向了互联网。 这也同时增进了对于互动网页内容的需求。有些公司，比如 Microsoft(R) 推出了基于他们专有产品的解决方案，开源社区也做出了积极的回应。 比较时尚的选择包括 Django，Ruby on Rails， mod_perl, and mod_php.

==== Django

Django 是一个以 BSD 许可证发布的 framework， 能让开发者快速写出高性能高品质的 web 应用程序。 它提供给一个对象关系映射组件，数据类型可以被当 Python 中的对象，和一组丰富的动态数据库访问 API， 使开发者避免了写 SQL 语句。它同时还提供了可扩展的模板系统， 让应用程序的逻辑部分与 HTML 的表现层分离。

Django 依赖与 mod_python， Apache, 和一个可选的 SQL 数据库引擎。 在设置了一些恰当的标志后，FreeBSD 的 Port 系统将会帮助你安装这些必需的依赖库。

[[network-www-django-install]]
.安装 Django，Apache2， mod_python3，和 PostgreSQL
[example]
====

[source,shell]
....
# cd /usr/ports/www/py-django; make all install clean -DWITH_MOD_PYTHON3 -DWITH_POSTGRESQL
....

====

在安装了 Django 和那些依赖的软件之后， 你需要创建一个 Django 项目的目录，然后配置 Apache，当有对于你网站上应用程序的某些指定的 URL 时调用内嵌的 Python 解释器。

[[network-www-django-apache-config]]
.Django/mod_python 有关 Apache 部分的配置
[example]
====
你需要在 Apache 的配置文件 [.filename]#httpd.conf# 加入以下这几行， 把对某些 URL 的请求传给你的 web 应用程序：

[source,shell]
....
<Location "/">
    SetHandler python-program
    PythonPath "['/dir/to/your/django/packages/'] + sys.path"
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
    PythonAutoReload On
    PythonDebug On
</Location>
....

====

==== Ruby on Rails

Ruby on Rails 是另外一个开源的 web framework， 提供了一个全面的开发框架，能帮助 web 开发者工作更有成效和快速写出强大的应用。 它能非常容易的从 posts 系统安装。

[source,shell]
....
# cd /usr/ports/www/rubygem-rails; make all install clean
....

==== mod_perl

Apache/Perl 集成计划， 将 Perl 程序设计语言的强大功能， 与 Apache HTTP 服务器 紧密地结合到了一起。 通过 mod_perl 模块， 可以完全使用 Perl 来撰写 Apache 模块。 此外， 服务器中嵌入的持久性解释器， 消除了由于启动外部的解释器为 Perl 脚本的启动所造成的性能损失。

mod_perl 通过多种方式提供。 要使用 mod_perl， 应该注意 mod_perl 1.0 只能配合 Apache 1.3 而 mod_perl 2.0 只能配合 Apache 2.X 使用。 mod_perl 1.0 可以通过 package:www/mod_perl[] 安装， 而以静态方式联编的版本， 则可以通过 package:www/apache13-modperl[] 来安装。 mod_perl 2.0 则可以通过 package:www/mod_perl2[] 安装。

==== mod_php

PHP， 也称为 "PHP: Hypertext Preprocessor"， 是一种特别适合于 Web 开发的通用脚本语言。 它能够很容易地嵌入到 HTML 之中， 其语法接近于 C、 Java(TM)， 以及 Perl， 以期让 web 开发人员的一迅速撰写动态生成的页面。

要获得用于 Apache web 服务器的 PHP5 支持， 可以从安装 package:lang/php5[] port 开始。

在首次安装 package:lang/php5[] port 的时候， 系统会自动显示可用的一系列 `OPTIONS` (配置选项)。 如果您没有看到菜单， 例如由于过去曾经安装过 package:lang/php5[] port 等等， 可以用下面的命令再次显示配置菜单， 在 port 的目录中执行：

[source,shell]
....
# make config
....

在配置选项对话框中， 选中 `APACHE` 这一项， 就可以联编出用于与 Apache web 服务器配合使用的可动态加载的 mod_php5 模块了。

[NOTE]
====
由于各式各样的原因 (例如， 出于已经部署的 web 应用的兼容性考虑)， 许多网站仍在使用 PHP4。 如果您需要 mod_php4 而不是 mod_php5， 请使用 package:lang/php4[] port。 package:lang/php4[] port 也支持许多 package:lang/php5[] port 提供的配置和编译时选项。
====

前面我们已经成功地安装并配置了用于支持动态 PHP 应用所需的模块。 请检查并确认您已将下述配置加入到了 [.filename]#/usr/local/etc/apache/httpd.conf# 中：

[.programlisting]
....
LoadModule php5_module        libexec/apache/libphp5.so
....

[.programlisting]
....
AddModule mod_php5.c
    <IfModule mod_php5.c>
        DirectoryIndex index.php index.html
    </IfModule>
    <IfModule mod_php5.c>
        AddType application/x-httpd-php .php
        AddType application/x-httpd-php-source .phps
    </IfModule>
....

这些工作完成之后， 还需要使用 `apachectl` 命令来完成一次 graceful restart 以便加载 PHP 模块：

[source,shell]
....
# apachectl graceful
....

在未来您升级 PHP 时， `make config` 这步操作就不再是必需的了； 您所选择的 `OPTIONS` 会由 FreeBSD 的 Ports 框架自动保存。

在 FreeBSD 中的 PHP 支持是高度模块化的， 因此基本安装的功能十分有限。 增加其他功能的支持非常简单， 只需通过 package:lang/php5-extensions[] port 即可完成。 这个 port 提供了一个菜单驱动的界面来帮助完成 PHP 扩展的安装。 另外， 也可以通过对应的 port 来单独安装扩展。

例如， 要将对于 MySQL 数据库服务器的支持加入 PHP5， 只需简单地安装 [.filename]#databases/php5-mysql#。

安装完扩展之后， 必须重新启动 Apache 服务器， 来令其适应新的配置变更：

[source,shell]
....
# apachectl graceful
....

[[network-ftp]]
== 文件传输协议 (FTP)

=== 纵览

文件传输协议 (FTP) 为用户提供了一个简单的， 与 FTP 服务器交换文件的方法。 FreeBSD 系统中包含了 FTP 服务软件， ftpd。 这使得在 FreeBSD 上建立和管理 FTP 服务器变得非常简单。

=== 配置

最重要的配置步骤是决定允许哪些帐号访问 FTP 服务器。 一般的 FreeBSD 系统包含了一系列系统帐号分别用于执行不同的服务程序， 但未知的用户不应被允许登录并使用这些帐号。 [.filename]#/etc/ftpusers# 文件中， 列出了不允许通过 FTP 访问的用户。 默认情况下， 这包含了前述的系统帐号， 但也可以在这里加入其它不应通过 FTP 访问的用户。

您可能会希望限制通过 FTP 登录的某些用户， 而不是完全阻止他们使用 FTP。 这可以通过 [.filename]#/etc/ftpchroot# 文件来完成。 这一文件列出了希望对 FTP 访问进行限制的用户和组的表。 而在 man:ftpchroot[5] 联机手册中， 已经对此进行了详尽的介绍， 故而不再赘述。

如果您想要在服务器上启用匿名的 FTP 访问， 则必须建立一个名为 `ftp` 的 FreeBSD 用户。 这样， 用户就可以使用 `ftp` 或 `anonymous` 和任意的口令 (习惯上， 应该是以那个用户的邮件地址作为口令) 来登录和访问您的 FTP 服务器。 FTP 服务器将在匿名用户登录时调用 man:chroot[2]， 以便将其访问限制在 `ftp` 用户的主目录中。

有两个文本文件可以用来指定显示在 FTP 客户程序中的欢迎文字。 [.filename]#/etc/ftpwelcome# 文件中的内容将在用户连接上之后， 在登录提示之前显示。 在成功的登录之后， 将显示 [.filename]#/etc/ftpmotd# 文件中的内容。 请注意后者是相对于登录环境的， 因此对于匿名用户而言， 将显示 [.filename]#~ftp/etc/ftpmotd#。

一旦正确地配置了 FTP 服务器， 就必须在 [.filename]#/etc/inetd.conf# 中启用它。 这里需要做的全部工作就是将注释符 "#" 从已有的 ftpd 行之前去掉：

[.programlisting]
....
ftp	stream	tcp	nowait	root	/usr/libexec/ftpd	ftpd -l
....

如 <<network-inetd-reread>> 所介绍的那样， 修改这个文件之后， 必须让 inetd 重新加载它， 才能使新的设置生效。请参阅 <<network-inetd-settings>> 以获取更多有关如何在你系统上启用 inetd 的详细信息。

ftpd 也可以作为一个独立的服务启动。 这样的话就需要在 [.filename]#/etc/rc.conf# 中设置如下的变量：

[.programlisting]
....
ftpd_enable="YES"
....

在设置了上述变量之后，独立的服务将在下次系统重启的时候启动， 或者通过以 `root` 身份手动执行如下的命令启动：

[source,shell]
....
# /etc/rc.d/ftpd start
....

现在可以通过输入下面的命令来登录您的 FTP 服务器了：

[source,shell]
....
% ftp localhost
....

=== 维护

ftpd 服务程序使用 man:syslog[3] 来记录消息。 默认情况下， 系统日志将把和 FTP 相关的消息记录到 [.filename]#/var/log/xferlog# 文件中。 FTP 日志的位置， 可以通过修改 [.filename]#/etc/syslog.conf# 中如下所示的行来修改：

[.programlisting]
....
ftp.info      /var/log/xferlog
....

一定要小心对待在匿名 FTP 服务器中可能遇到的潜在问题。 一般而言， 允许匿名用户上传文件应三思。 您可能发现自己的 FTP 站点成为了交易未经授权的商业软件的论坛， 或发生更糟糕的情况。 如果不需要匿名的 FTP 上传， 可以在文件上配置权限， 使得您能够在其它匿名用户能够下载这些文件之前复查它们。

[[network-samba]]
== 为 Microsoft(R) Windows(R) 客户机提供文件和打印服务 (Samba)

=== 纵览

Samba 是一个流行的开源软件包， 它提供了针对 Microsoft(R) Windows(R) 客户机的文件和打印服务。 这类客户机可以连接并使用 FreeBSD 系统上的文件空间， 就如同使用本地的磁盘一样， 或者像使用本地打印机一样使用 FreeBSD 上的打印机。

Samba 软件包可以在您的 FreeBSD 安装盘上找到。 如果您没有在初次安装 FreeBSD 时安装 Samba， 则可以通过 package:net/samba34[] port 或 package 来安装。

=== 配置

默认的 Samba 配置文件会以 [.filename]#/usr/local/shared/examples/samba34/smb.conf.default# 的名字安装。这个文件必须复制为 [.filename]#/usr/local/etc/smb.conf# 并进行定制， 才能开始使用 Samba。

[.filename]#smb.conf# 文件中包含了 Samba 的运行时配置信息， 例如对于打印机的定义， 以及希望共享给 Windows(R) 客户机的 "共享文件系统"。 Samba 软件包包含了一个称为 swat 的 web 管理工具， 后者提供了配置 [.filename]#smb.conf# 文件的简单方法。

==== 使用 Samba Web 管理工具 (SWAT)

Samba Web 管理工具 (SWAT) 是一个通过 inetd 运行的服务程序。 因此， 需要把 [.filename]#/etc/inetd.conf# 中下面几行的注释去掉， 才能够使用 swat 来配置 Samba：

[.programlisting]
....
swat   stream  tcp     nowait/400      root    /usr/local/sbin/swat    swat
....

如 <<network-inetd-reread>> 中所介绍的那样， 在修改了这个配置文件之后， 必须让 inetd 重新加载配置， 才能使其生效。

一旦在 [.filename]#inetd.conf# 中启用了 swat， 就可以用浏览器访问 connect to http://localhost:901[http://localhost:901] 了。 您将首先使用系统的 `root` 帐号登录。

只要成功地登录进了 Samba 配置页面， 就可以浏览系统的文档， 或从 menu:Globals[](全局) 选项卡开始配置了。 menu:Globals[] 小节对应于 `[global]` 小节中的变量， 前者位于 [.filename]#/usr/local/etc/smb.conf# 中。

==== 全局配置

无论是使用 swat， 还是直接编辑 [.filename]#/usr/local/etc/smb.conf#， 通常首先要配置的 Samba 选项都是：

`workgroup`::
NT 域名或工作组名， 其他计算机将通过这些名字来找到服务器。

`netbios name`::
这个选项用于设置 Samba 服务器的 NetBIOS 名字。 默认情况下， 这是所在主机的 DNS 名字的第一部分。

`server string`::
这个选项用于设置通过 `net view` 命令， 以及某些其他网络工具可以查看到的关于服务器的说明性文字。

==== 安全配置

在 [.filename]#/usr/local/etc/smb.conf# 中的两个最重要的配置， 是选定的安全模型， 以及客户机上用户的口令存放后端。 下面的语句控制这些选项：

`security`::
最常见的选项形式是 `security = share` 和 `security = user`。 如果您的客户机使用用户名， 并且这些用户名与您的 FreeBSD 机器一致， 一般应选择用户级 (user) 安全。 这是默认的安全策略， 它要求客户机首先登录， 然后才能访问共享的资源。
+
如果采用共享级 (share) 安全， 则客户机不需要用有效的用户名和口令登录服务器， 就能够连接共享的资源。 这是较早版本的 Samba 中的默认值。

`passdb backend`::
Samba 提供了若干种不同的验证后端模型。 您可以通过 LDAP、 NIS+、 SQL 数据库， 或经过修改的口令文件， 来完成客户端的身份验证。 默认的验证模式是 `smbpasswd`， 这也是本章将介绍的全部内容。

假设您使用的是默认的 `smbpasswd` 后端， 则必须首先创建一个 [.filename]#/usr/local/etc/samba/smbpasswd# 文件， 来允许 Samba 对客户进行身份验证。 如果您打算让 UNIX(R) 用户帐号能够从 Windows(R) 客户机上登录， 可以使用下面的命令：

[source,shell]
....
# smbpasswd -a username
....

[NOTE]
====
目前推荐使用的后端是 `tdbsam`， 您应使用下面的命令来添加用户帐号：

[source,shell]
....
# pdbedit -a -u username
....

====

请参考 http://www.samba.org/samba/docs/man/Samba-HOWTO-Collection/[官方的 Samba HOWTO] 以了解关于配置选项的进一步信息。 按照前面给出的基本描述， 您应该已经可以启动 Samba 了。

=== 启动 Samba

package:net/samba34[] port 会增加一个新的用于控制 Samba 的启动脚本。 要启用这个脚本， 以便用它来完成启动、 停止或重启 Samba 的任务， 需要在 [.filename]#/etc/rc.conf# 文件中加入：

[.programlisting]
....
samba_enable="YES"
....

此外， 也可以进行更细粒度的控制：

[.programlisting]
....
nmbd_enable="YES"
....

[.programlisting]
....
smbd_enable="YES"
....

[NOTE]
====
这也同时配置了在系统引导时启动 Samba。
====

配置好之后， 就可以在任何时候通过下面的命令来启动 Samba 了：

[source,shell]
....
# /usr/local/etc/rc.d/samba start
Starting SAMBA: removing stale tdbs :
Starting nmbd.
Starting smbd.
....

请参见 crossref:config[configtuning-rcd,在 FreeBSD 中使用 rc] 以了解关于使用 rc 脚本的进一步信息。

Samba 事实上包含了三个相互独立的服务程序。 您应该能够看到 nmbd 和 smbd 两个服务程序都是通过 [.filename]#samba# 脚本启动的。 如果在 [.filename]#smb.conf# 中启用了 winbind 名字解析服务， 则应该可以看到 winbindd 服务被启动起来。

可以在任何时候通过下面的命令来停止运行 Samba：

[source,shell]
....
# /usr/local/etc/rc.d/samba stop
....

Samba 是一个复杂的软件包， 它提供了用于与 Microsoft(R) Windows(R) 网络进行集成的各式各样的功能。 要了解关于这里所介绍的基本安装以外的其它功能， 请访问 http://www.samba.org[http://www.samba.org]。

[[network-ntp]]
== 通过 NTP 进行时钟同步

=== 纵览

随着时间的推移， 计算机的时钟会倾向于漂移。 网络时间协议 (NTP) 是一种确保您的时钟保持准确的方法。

许多 Internet 服务依赖、 或极大地受益于本地计算机时钟的准确性。 例如， web 服务器可能会接收到一个请求， 要求如果文件在某一时刻之后修改过才发送它。 在局域网环境中， 共享文件的计算机之间的时钟是否同步至关重要， 因为这样才能使时间戳保持一致。 类似 man:cron[8] 这样的程序， 也依赖于正确的系统时钟， 才能够准确地执行操作。

FreeBSD 附带了 man:ntpd[8] NTP 服务器， 它可以用于查询其它的 NTP 服务器， 并配置本地计算机的时钟， 或者为其它机器提供服务。

=== 选择合适的 NTP 服务器

为了同步您的系统时钟， 需要首先找到至少一个 NTP 服务器以供使用。 网络管理员， 或 ISP 都可能会提供用于这样目的的 NTP 服务器-请查看他们的文档以了解是否是这样。 另外， 也有一个在线的 http://ntp.isc.org/bin/view/Servers/WebHome[公开的 NTP 服务器列表]， 您可以从中选一个较近的 NTP 服务器。 请确认您选择的服务器的访问策略， 如果需要的话， 申请一下所需的许可。

选择多个相互不连接的 NTP 服务器是一个好主意， 这样在某个服务器不可达， 或者时钟不可靠时就可以有别的选择。 这是因为， man:ntpd[8] 会智能地选择它收到的响应-它会更倾向于使用可靠的服务器。

=== 配置您的机器

==== 基本配置

如果只想在系统启动时同步时钟， 则可以使用 man:ntpdate[8]。 对于经常重新启动， 并且不需要经常同步的桌面系统来说这比较适合， 但绝大多数机器都应该运行 man:ntpd[8]。

在引导时使用 man:ntpdate[8] 来配合运行 man:ntpd[8] 也是一个好主意。 man:ntpd[8] 渐进地修正时钟， 而 man:ntpdate[8] 则直接设置时钟， 无论机器的当前时间和正确时间有多大的偏差。

要启用引导时的 man:ntpdate[8]， 需要把 `ntpdate_enable="YES"` 加到 [.filename]#/etc/rc.conf# 中。 此外， 还需要通过 `ntpdate_flags` 来设置同步的服务器和选项， 它们将传递给 man:ntpdate[8]。

==== 一般配置

NTP 是通过 [.filename]#/etc/ntp.conf# 文件来进行配置的， 其格式在 man:ntp.conf[5] 中进行了描述。 下面是一个例子：

[.programlisting]
....
server ntplocal.example.com prefer
server timeserver.example.org
server ntp2a.example.net

driftfile /var/db/ntp.drift
....

这里， `server` 选项指定了使用哪一个服务器， 每一个服务器都独立一行。 如果某一台服务器上指定了 `prefer` (偏好) 参数， 如上面的 `ntplocal.example.com`， 则会优先选择这个服务器。 如果偏好的服务器和其他服务器的响应存在显著的差别， 则丢弃它的响应， 否则将使用来自它的响应， 而不理会其他服务器。 一般来说， `prefer` 参数应该标注在非常精确的 NTP 时源， 例如那些包含特殊的时间监控硬件的服务器上。

而 `driftfile` 选项， 则指定了用来保存系统时钟频率偏差的文件。 man:ntpd[8] 程序使用它来自动地补偿时钟的自然漂移， 从而使时钟即使在切断了外来时源的情况下， 仍能保持相当的准确度。

另外， `driftfile` 选项也保存上一次响应所使用的 NTP 服务器的信息。 这个文件包含了 NTP 的内部信息， 它不应被任何其他进程修改。

==== 控制您的服务器的访问

默认情况下， NTP 服务器可以被整个 Internet 上的主机访问。 如果在 [.filename]#/etc/ntp.conf# 中指定 `restrict` 参数， 则可以控制允许哪些机器访问您的服务器。

如果希望拒绝所有的机器访问您的 NTP 服务器， 只需在 [.filename]#/etc/ntp.conf# 中加入：

[.programlisting]
....
restrict default ignore
....

[NOTE]
====
这样做会禁止您的服务器访问在本地配置中列出的服务器。 如果您需要令 NTP 服务器与外界的 NTP 服务器同步时间， 则应允许指定服务器。 请参见联机手册 man:ntp.conf[5] 以了解进一步的细节。
====

如果只希望子网内的机器通过您的服务器同步时钟， 而不允许它们配置为服务器， 或作为同步时钟的节点来时用， 则加入

[.programlisting]
....
restrict 192.168.1.0 mask 255.255.255.0 nomodify notrap
....

这里， 需要把 `192.168.1.0` 改为您网络上的 IP 地址， 并把 `255.255.255.0` 改为您的子网掩码。

[.filename]#/etc/ntp.conf# 可能包含多个 `restrict` 选项。 要了解进一步的细节， 请参见 man:ntp.conf[5] 的 `Access Control Support`(访问控制支持) 小节。

=== 运行 NTP 服务器

要让 NTP 服务器在系统启动时随之开启， 需要把 `ntpd_enable="YES"` 加入到 [.filename]#/etc/rc.conf# 中。 如果希望向 man:ntpd[8] 传递更多参数， 需要编辑 [.filename]#/etc/rc.conf# 中的 `ntpd_flags`。

要在不重新启动机器的前提下启动服务器， 需要手工运行 `ntpd`， 并带上 [.filename]#/etc/rc.conf# 中的 `ntpd_flags` 所指定的参数。 例如：

[source,shell]
....
# ntpd -p /var/run/ntpd.pid
....

=== 在临时性的 Internet 连接上使用 ntpd

man:ntpd[8] 程序的正常工作并不需要永久性的 Internet 连接。 然而， 如果您的临时性连接是配置为按需拨号的， 那么防止 NTP 通讯频繁触发拨号， 或保持连接就有必要了。 如果您使用用户级 PPP， 可以使用 `filter` 语句， 在 [.filename]#/etc/ppp/ppp.conf# 中进行必要的设置。 例如：

[.programlisting]
....
 set filter dial 0 deny udp src eq 123
 # Prevent NTP traffic from initiating dial out
 set filter dial 1 permit 0 0
 set filter alive 0 deny udp src eq 123
 # Prevent incoming NTP traffic from keeping the connection open
 set filter alive 1 deny udp dst eq 123
 # Prevent outgoing NTP traffic from keeping the connection open
 set filter alive 2 permit 0/0 0/0
....

要了解进一步的信息， 请参考 man:ppp[8] 的 `PACKET FILTERING`(包过滤) 小节， 以及 [.filename]#/usr/shared/examples/ppp/# 中的例子。

[NOTE]
====
某些 Internet 访问提供商会阻止低编号的端口， 这会导致 NTP 无法正常工作， 因为响应无法到达您的机器。
====

=== 进一步的信息

关于 NTP 服务器的文档， 可以在 [.filename]#/usr/shared/doc/ntp/# 找到 HTML 格式的版本。

[[network-syslogd]]
== 使用 `syslogd` 记录远程主机的日志

处理系统日志对于系统安全和管理是一个重要方面。 当有多台分布在中型或大型网络的机器，再或者是处于各种不同类型的网络中， 监视他们上面的日志文件则显得非常难以操作， 在这种情况下， 配置远程日志记录能使整个处理过程变得更加轻松。

集中记录日志到一台指定的机器能够减轻一些日志文件管理的负担。 日志文件的收集， 合并与循环可以在一处配置， 使用 FreeBSD 原生的工具， 比如 man:syslogd[8] 和 man:newsyslog[8]。 在以下的配置示例中， 主机 `A`， 命名为 `logserv.example.com`， 将用来收集本地网络的日志信息。 主机 `B`， 命名为 `logclient.example.com` 将把日志信息传送给服务器。 在现实中， 这两个主机都需要配置正确的正向和反向的 DNS 或者在 [.filename]#/etc/hosts# 中记录。 否则， 数据将被服务器拒收。

=== 日志服务器的配置

日志服务器是配置成用来接收远程主机日志信息的机器。 在大多数的情况下这是为了方便配置， 或者是为了更好的管理。 不论是何原因， 在继续深入之前需要提一些必需条件。

一个正确配置的日志服务器必须符合以下几个最基本的条件：

* 服务器和客户端的防火墙规则允许 514 端口上的 UDP 报文通过。
* syslogd 被配置成接受从远程客户发来的消息。
* syslogd 服务器和所有的客户端都必须有配有正确的正向和反向 DNS， 或者在 [.filename]#/etc/hosts# 中有相应配置。

配置日志服务器， 客户端必须在 [.filename]#/etc/syslog.conf# 中列出, 并指定日志的 facility：

[.programlisting]
....
+logclient.example.com
*.*     /var/log/logclient.log
....

[NOTE]
====
更多关于各种被支持并可用的 _facility_ 能在 man:syslog.conf[5] 手册页中找到。
====

一旦加入以后， 所有此类 `facility` 消息都会被记录到先前指定的文件 [.filename]#/var/log/logclient.log#。

提供服务的机器还需要在其 [.filename]#/etc/rc.conf# 中配置：

[.programlisting]
....
syslogd_enable="YES"
syslogd_flags="-a logclient.example.com -v -v"
....

第一个选项表示在系统启动时启用 `syslogd` 服务， 第二个选项表示允许服务器接收来自指定日志源客户端的数据。 第二行配置中最后的部分， 使用 `-v -v`， 表示增加日志消息的详细程度。 在调整 facility 配置的时候， 这个配置非常有用， 因为管理员能够看到哪些消息将作为哪个 facility 的内容来记录。

可以同时指定多个 `-a` 选项来允许多个客户机。 此外， 还可以指定 IP 地址或网段， 请参阅 man:syslog[3] 联机手册以了解可用配置的完整列表。

最后， 日志文件应该被创建。 不论你用何种方法创建， 比如 man:touch[1] 能很好的完成此类任务：

[source,shell]
....
# touch /var/log/logclient.log
....

此时， 应该重启并确认一下 `syslogd` 守护进程：

[source,shell]
....
# /etc/rc.d/syslogd restart
# pgrep syslog
....

如果返回了一个 PIC 的话， 服务端应该被成功重启了, 并继续开始配置客户端。 如果服务端没有重启的话， 请在 [.filename]#/var/log/messages# 日志中查阅相关输出。

=== 日志客户端配置

日志客户端是一台发送日志信息到日志服务器的机器， 并在本地保存拷贝。

与日志服务器类似， 客户端也需要满足一些最基本的条件：

* man:syslogd[8] 必须被配置成发送指定类型的消息到能接收他们的日志服务器。
* 防火墙必须允许 514 端口上的 UDP 包通过；
* 必须配置正向与反向 DNS， 或者在 [.filename]#/etc/hosts# 中有正确的记录。

相比服务器来说配置客户端更轻松一些。 客户端的机器在 [.filename]#/etc/rc.conf# 中做如下的设置：

[.programlisting]
....
syslogd_enable="YES"
syslogd_flags="-s -v -v"
....

和前面类似， 这些选项会在系统启动过程中启用 `syslogd` 服务， 并增加日志消息的详细程度。 而 `-s` 选项则表示禁止服务接收来自其他主机的日志。

Facility 是描述某个消息由系统的哪部分生成的。 举例来说， ftp 和 ipfw 都是 facility。 当这两项服务生成日志消息时， 它们通常在日志消息中包含了这两种工具。 Facility 通常带有一个优先级或等级， 就是用来标记一个日志消息的重要程度。 最普通的为 `warning` 和 `info`。 请参阅 man:syslog[3] 手册页以获得一个完整可用的 facility 与优先级列表。

日志服务器必须在客户端的 [.filename]#/etc/syslog.conf# 中指明。 在此例中， `@` 符号被用来表示发送日志数据到远程的服务器， 看上去差不多如下这样：

[.programlisting]
....
*.*		@logserv.example.com
....

添加后， 必须重启 `syslogd` 使得上述修改生效：

[source,shell]
....
# /etc/rc.d/syslogd restart
....

测试日志消息是否能通过网络发送， 在准备发出消息的客户机上用 man:logger[1] 来向 `syslogd` 发出信息：

[source,shell]
....
# logger "Test message from logclient"
....

这段消息现在应该同时出现在客户机的 [.filename]#/var/log/messages# 以及日志服务器的 [.filename]#/var/log/logclient.log# 中。

=== 调试日志服务器

在某些情况下， 如果日志服务器没有收到消息的话就需要调试一番了。 有几个可能的原因， 最常见的两个是网络连接的问题和 DNS 的问题。 为了测试这些问题， 请确认两边的机器都能使用 [.filename]#/etc/rc.conf# 中所设定的主机名访问到对方。 如果这个能正常工作的话， 那么就需要对 [.filename]#/etc/rc.conf# 中的 `syslogd_flags` 选项做些修改了。

在以下的示例中， [.filename]#/var/log/logclient.log# 是空的， [.filename]#/var/log/message# 中也没有表明任何失败的原因。 为了增加调试的输出， 修改 `ayalogd_flags` 选项至类似于如下的示例， 并重启服务：

[.programlisting]
....
syslogd_flags="-d -a logclien.example.com -v -v"
....

[source,shell]
....
# /etc/rc.d/syslogd restart
....

在重启服务之后， 屏幕上将立刻闪现类似这样的调试数据：

[source,shell]
....
logmsg: pri 56, flags 4, from logserv.example.com, msg syslogd: restart
syslogd: restarted
logmsg: pri 6, flags 4, from logserv.example.com, msg syslogd: kernel boot file is /boot/kernel/kernel
Logging to FILE /var/log/messages
syslogd: kernel boot file is /boot/kernel/kernel
cvthname(192.168.1.10)
validate: dgram from IP 192.168.1.10, port 514, name logclient.example.com;
rejected in rule 0 due to name mismatch.
....

很明显，消息是由于主机名不匹配而被拒收的。 在一点一点的检查了配置文件之后， 发现了 [.filename]#/etc/rc.conf# 中如下这行有输入错误：

[.programlisting]
....
syslogd_flags="-d -a logclien.example.com -v -v"
....

这行应该包涵有 `logclient`， 而不是 `logclien`。 在做了正确的修改并重启之后便能见到预期的效果了：

[source,shell]
....
# /etc/rc.d/syslogd restart
logmsg: pri 56, flags 4, from logserv.example.com, msg syslogd: restart
syslogd: restarted
logmsg: pri 6, flags 4, from logserv.example.com, msg syslogd: kernel boot file is /boot/kernel/kernel
syslogd: kernel boot file is /boot/kernel/kernel
logmsg: pri 166, flags 17, from logserv.example.com,
msg Dec 10 20:55:02 <syslog.err> logserv.example.com syslogd: exiting on signal 2
cvthname(192.168.1.10)
validate: dgram from IP 192.168.1.10, port 514, name logclient.example.com;
accepted in rule 0.
logmsg: pri 15, flags 0, from logclient.example.com, msg Dec 11 02:01:28 trhodes: Test message 2
Logging to FILE /var/log/logclient.log
Logging to FILE /var/log/messages
....

此刻， 消息能够被正确接收并保存入文件了。

=== 安全性方面的思考

就像其他的网络服务一样， 在实现配置之前需要考虑安全性。 有时日志文件也包含了敏感信息， 比如本地主机上所启用的服务， 用户帐号和配置数据。 从客户端发出的数据经过网络到达服务器， 这期间既没有加密也没有密码保护。 如果有加密需要的话， 可以使用 package:security/stunnel[]， 它将在一个加密的隧道中传输数据。

本地安全也同样是个问题。 日志文件在使用中或循环转后都没有被加密。 本地用户可能读取这些文件以获得对系统更深入的了解。 对于这类情况， 给这些文件设置正确的权限是非常有必要的。 man:newsyslog[8] 工具支持给新创建和循环的日志设置权限。 把日志文件的权限设置为 `600` 能阻止本地用户不必要的窥探。
