OpenBSD-完美指南第二版-全-
OpenBSD 完美指南第二版(全)
原文:Absolute OpenBSD, 2nd
译者:飞龙
简介
我向一位精神科护士实践者询问了偏执,被告知“偏执是感觉人们都在针对你。”医学词典可能会给出一个稍微不同的定义,但这个定义实际上对任何系统管理员都非常有用。并不是互联网上的每个人都试图攻击你,但总有某人想要入侵你的系统。即使你认为你没有什么有价值的东西,也有人想要拥有你的电脑。而且你不会意识到你所拥有的价值,直到别人拥有它。这就是人性。
如果你不在互联网上感到偏执,那你就有麻烦了。
这就是 OpenBSD 的作用所在。
这本书是关于 OpenBSD 操作系统的介绍。OpenBSD 是 BSD 操作系统家族的一员。它被广泛认为是在任何许可条款下最安全的操作系统。它被互联网服务提供商、嵌入式系统制造商以及任何需要安全和稳定性的个人广泛使用。如果你是一位经验丰富的 Unix 系统管理员,想要将 OpenBSD 添加到你的技能库中,这本书就是为你准备的。
当你完成这本书后,你应该能够舒适地使用 OpenBSD。你将了解如何配置、故障排除和升级运行 OpenBSD 的计算机,并对 OpenBSD 的软件、安全和网络管理功能有基本的了解。
什么是安全?
我们经常使用“安全”这个词,所以花点时间来谈谈安全本身是值得的。我们都有一种模糊的概念,知道它意味着什么。“安全”意味着你的东西是安全的,其他人无法得到它。这已经很好了,但还不够。在信息技术中,安全有三个部分:
机密性
这意味着秘密数据应该保持秘密。你的私人信息不得公之于众。那个东欧儿童色情集团不应该得到你的信用卡号码。
完整性
这意味着系统上的数据在没有授权的情况下不应被更改。你的记录应该保持完整。那个入侵者不应该更改订单的送货地址,让你的员工将一箱真正昂贵的物品运送到底特律的一个废弃仓库。
可用性
这意味着系统一直在运行。如果你的业务依赖于你的网站,失去网站就意味着失去生意。能够让你的网站关闭的人可以让你公司陷入困境。而且各种各样的人愿意让你关闭,要么是为了竞争,要么只是为了取乐。
由于担任系统管理员的时间比一些人生存的时间还要长,我对安全性的看法更为非正式。安全性意味着消除由计算机问题引起的糟糕日子。花一天时间让一个软件编译不是糟糕的一天。是不是一个令人烦恼的一天?当然,但这不是 糟糕的。需要从我的系统中驱逐入侵者的一天是糟糕的。因为计算机入侵而需要开会的一天是糟糕的。当我意识到我无法信任网络上的任何计算机,我必须重新安装我拥有的每一件该死的设备时,那真的是糟糕的。[1]
虽然 OpenBSD 无法改变我的某些服务器已经足够老旧,以至于可以离开小学的事实,但它可以修复安全性的软件方面。
什么是 BSD?
在 20 世纪 70 年代,AT&T 需要大量专门定制的计算机软件来运营其业务。该公司被禁止在计算机行业竞争,因此无法销售这种软件。相反,AT&T 以象征性的费用将其软件和相关源代码许可给大学。大学通过使用这种软件而不是价格昂贵的商业软件来节省资金,而大学生则有机会接触这种巧妙的技术,并学习一切是如何工作的。作为回报,AT&T 获得了曝光、一些零花钱,以及一代在 AT&T 技术上磨砺的计算机科学家。每个人从这个交易中都得到了一些东西。
在此计划下分发最著名的软件是 UNIX。
与现代操作系统相比,原始的 UNIX 存在许多问题。然而,有成千上万的学生可以访问其源代码,数百名教师需要为他们的学生寻找有趣的项目。如果一个程序表现异常,或者操作系统本身存在问题,与系统生活在一起的人们拥有解决问题的工具和动力。他们的努力迅速提高了 UNIX 的性能,并创造了我们现在视为理所当然的许多功能。学生们增加了控制运行进程的能力,也称为 作业控制。UNIX 的 S51K 文件系统让系统管理员哀嚎和咬牙切齿,所以他们用快速文件系统(FFS)来替换它,这引入了一系列现代文件系统都具备的特性。多年来,UNIX 添加了许多小型、有用的程序,并替换了整个子系统。
加州大学伯克利分校的计算机科学研究中心(CSRG)从 1979 年到 1994 年作为 UNIX 代码改进的中心交易所。该小组收集了来自其他大学的更改,评估它们,打包它们,并将编译免费分发给任何拥有有效 AT&T UNIX 许可证的人。CSRG 还与国防高级研究计划局(DARPA)签订了合同,在 UNIX 中实现各种功能,如 TCP/IP。这个结果软件集合后来被称为伯克利软件发行版,或 BSD。用户使用 CSRG 的软件,进一步改进它,并将他们的改进反馈给 CSRG。今天,我们认为这是一种相当标准的开源项目运行方式,但在 1979 年,它是革命性的。
在软件开发中,15 年的工作是一生。相比之下,微软在 15 年内从 Windows 95 发展到 Windows 7。CSRG 成员收集了如此多的 UNIX 增强和改进,以至于他们几乎用 CSRG 及其贡献者创建的代码替换了所有原始 UNIX 代码。你必须仔细寻找才能找到任何原始的 AT&T 代码。
最终,CSRG 的资助逐渐减少,很明显 BSD 项目将结束。在加州大学内部经过一些政治上的争执后,1992 年,BSD 代码在被称为BSD 许可证的许可下向公众发布。
BSD 许可证
BSD 代码可以在历史上可能是最宽松的许可下供任何人使用。该许可证可以概括如下:
-
不要声称是你写的。
-
如果它坏了,不要责怪我们。
-
不要使用我们的名字来推广你的产品。
总体来说,这意味着你可以对 BSD 代码做几乎所有你想做的事情。(原始的 BSD 许可证确实要求如果软件产品包含 BSD 许可代码,用户必须被告知,但后来这个要求被取消了。)你甚至不需要与原始作者分享任何更改!人们可以将 BSD 包含在专有、开源或免费产品中。
与限制性的版权或更宽松但仍受限制的copyleft不同,BSD 许可证有时被称为copycenter,即“把这个带到复印中心,为自己打印几份。”不出所料,像 Sun Microsystems 这样的公司立刻就采用了 BSD。它是免费的,它工作得很好,而且许多新毕业生都有使用这项技术的经验。有一家公司,BSDi,专门成立来利用 BSD Unix。
AT&T 与世界的对抗
在 AT&T 的土地上,UNIX 的开发仍在继续。AT&T 从 BSD Unix 发行版中取了部分内容,并将其与官方 UNIX 集成,然后将结果重新许可给那些提供改进的大学。这对所有人来说都很好,直到美国政府拆分了 AT&T,结果的公司被允许在计算机软件业务中竞争。
AT&T 拥有一项特别有价值的软件资产:一个经过数千人广泛调试的高端操作系统,具有强大的功能,如各种小巧但强大的命令、现代文件系统、作业控制和 TCP/IP。AT&T 成立了一家子公司,Unix Systems Laboratories (USL),它欣然开始向企业销售 UNIX,并对其收取非常高的费用,同时维持着最初赋予它如此先进操作系统的大学关系。
加州大学伯克利分校公开发布 BSD 代码引起了 USL 极大的不满。几乎立即,USL 起诉了大学和利用 BSD 的软件公司。加州大学声称 CSRG 从与 AT&T 无关的数千名第三方贡献者那里编译了 BSD,并且它是 CSRG 可以随意处理的知识产权。奇怪的是,这场诉讼将 BSD 推广给了那些否则永远不会听说它的人,催生了开源 BSD 变体,如 386BSD、FreeBSD 和 NetBSD。
1994 年,经过两年的法律纠纷,加州大学的律师证明,AT&T UNIX 的大部分实际上是从 BSD 中取来的,而不是相反。更糟糕的是,AT&T 违反了 BSD 许可证,从它所获取的文件中移除了 CSRG 的版权。
剩余的争议源文件只有寥寥数个。在法庭上备受打击,USL 将其中一些文件捐赠给了 BSD,同时保留了一些作为专有信息。发布了 BSD 4.4-Lite,包含了除专有文件之外的所有内容。由于缺少这些文件,BSD 4.4-Lite 成为了已知唯一一个无法使用甚至无法编译交付的正式操作系统发布版本。每个人都清楚这一点,但仍然购买了它——这是现代供应商可能希望复制的史无前例的壮举。
随后的更新,BSD 4.4-Lite2,是 OpenBSD 的祖父,也是今天使用的所有其他 BSD 代码的祖先,包括 FreeBSD、NetBSD 和 Mac OS X。
OpenBSD 的诞生
Theo de Raadt 曾是 NetBSD 的开发者。在与其他 NetBSD 团队成员就如何管理项目进行了许多激烈、广泛且持续时间长的分歧之后,他独立出去并创立了 OpenBSD 项目,吸引了志同道合的开发者。OpenBSD 团队迅速确立了一个以安全为重点的身份,现在它是最知名的 BSD 后裔之一。
OpenBSD 团队的开发者将几个现在被视为理所当然的想法引入了开源操作系统世界,例如对 CVS 仓库和提交日志的公共只读访问。他们还创建了几个成为许多操作系统行业标准的软件,例如sudo和无处不在的 OpenSSH。
现在,许多主要公司依赖 OpenBSD 作为一个可靠、安全的操作系统,对安全性、正确性、可用性和自由有着狂热的关注。OpenBSD 可以在许多不同类型的硬件上运行,包括标准的 32 位和 64 位“Intel PC”(i386 和 amd64)、苹果的 PowerPC Macintosh(macppc)、Sparc(sparc 和 sparc64),以及像夏普 Zaurus PDA、Lemote Yeeloong 和古老的 VAX 这样的罕见平台。OpenBSD 几乎将所有精力都投入到安全特性、安全调试和代码正确性上,并在过程中证明,正确的代码具有更低的故障率,因此安全性更高。OpenBSD 力求成为终极的安全操作系统。
OpenBSD 团队不断改进操作系统。只有当新功能满足团队对代码和文档的标准时,才会添加新功能。即使在新软件功能尚未完善之前就添加了,也预期它将拥有完整的文档和正确的代码。
OpenBSD 社区
OpenBSD 不仅仅是一系列比特的集合。它是一个由用户、开发者和贡献者组成的社区,拥有一个唯一的中央“独裁者”——或者说,协调者。对于不知道期待什么的任何人来说,这个社区可能会有些令人震惊。
如何让分布在世界各地的个人创建、维护和发展一个操作系统,更不用说建立一个社区?几乎所有讨论都通过电子邮件和在线聊天进行。这个过程比面对面交谈要慢,但这是让不同时区的大量人群以合理方式沟通的唯一经济有效的方式。电子邮件和聊天还提供了讨论的书面记录。如果你想参与 OpenBSD 的开发,你必须习惯使用电子邮件。(虽然有一些针对 OpenBSD 的专用网络论坛,但它们位于主要社区之外。)
OpenBSD 社区分为四个层级:用户、贡献者、提交者和协调者。
OpenBSD 用户
许多开源操作系统投入大量精力来扩大用户基础、传播福音,并将新人们引入 Unix 阵营。OpenBSD 却不是这样。
大多数开源 Unix-like 操作系统团体都做了很多 Unix 的推广工作。然而,OpenBSD 却不是这样。
其他操作系统的周边社区积极鼓励新用户,并努力让新手感到受欢迎。OpenBSD 却特意不这么做。
OpenBSD 社区并不试图成为最受欢迎的操作系统——只是想做到最好。开发者们非常清楚他们的目标市场:他们自己。如果你能使用他们的工作,那很好。如果不能,请离开,直到你能。
OpenBSD 社区通常期望新来者是高级计算机用户。成员们编写了大量的 OpenBSD 文档,并期望新来者愿意阅读它们。他们不感兴趣于宠溺新的 Unix 用户,如果被追问,他们可能会直言不讳地表达这一点。他们不会手把手地教你。他们不会开发新功能来取悦用户。OpenBSD 的存在是为了满足开发者的需求,虽然其他人也欢迎加入,但乘客的需求不会引导项目。
OpenBSD 贡献者
贡献者是那些具备向操作系统添加功能、解决问题、编写文档或准确报告问题所需技能的 OpenBSD 用户。问题范围从文档中的印刷错误到系统崩溃。几乎任何人都可以成为贡献者。事实上,社区甚至接受了我的问题报告,并在数小时内解决了这些问题。
每一个 OpenBSD 功能都存在,因为某个贡献者花费了时间为其编写代码。提交了仔细、正确的修复或提供了有用的问题报告的贡献者,在 OpenBSD 社区中受到欢迎。如果一个贡献者提交了足够多且质量足够的修复,他可能会被提供提交者的角色。
OpenBSD 提交者
提交者有权访问 OpenBSD 的主要源代码仓库。他们可以做出他们认为对他们的 OpenBSD 项目必要的任何更改,但要对彼此和项目负责人负责。大多数提交者都是熟练的程序员,他们在自己的时间里从事 OpenBSD 的工作。
虽然成为提交者看起来很风光,但这个角色承担着很多责任。如果一个提交者破坏了操作系统或改变了某些内容,以至于与 OpenBSD 的驱动“愿景”相冲突,他必须修复它。提交者试图避免破坏事物,并在将其集成到 OpenBSD 的主要源代码集合之前,经常在网站和邮件列表上提供他们的工作,以便有兴趣的人预览、测试和双重检查他们的工作。
许多提交者在 OpenBSD 内部有非常具体的协调角色。例如,许多硬件架构都有一个负责人来处理影响该硬件的问题,编译器有一个维护者,等等。这些提交者在社区中赢得了这种信任地位。
OpenBSD 协调员
Theo de Raadt 在 1995 年创立了 OpenBSD,并且至今仍在协调该项目。他是关于系统应该如何工作、系统应包含什么以及谁可以直接访问仓库的最终决定者。他解决所有贡献者和提交者无法自行解决的争议。Theo 会采取必要的行动,以确保 OpenBSD 项目顺利运行。如果 Theo 发生任何意外,项目确实有计划替换他。
以中央仁慈的独裁者为中心构建 OpenBSD 组织,避免了其他大型开源项目所面临的许多管理问题。
如果你决定在 OpenBSD 上工作,你必须接受 Theo 的决定为最终决定。一个不接受项目领导者的贡献者不会在社区中长期停留。Theo 可能有一根大棒,但作为公认的项目负责人,他不需要像你想象的那样经常使用它。
OpenBSD 的优势
什么让 OpenBSD 成为 OpenBSD?在如此多的 Unix-like 操作系统存在的情况下,为什么还要费心去开发另一个?是什么让这个操作系统值得一台电脑,更不用说值得保护你公司的资产了?
可移植性
OpenBSD 设计用于在广泛的流行处理器和硬件平台上运行,包括与 Intel 兼容的(32 位和 64 位)、Alpha、Macintosh(PowerPC 和 Intel 系统)以及几乎来自 Sun 的任何东西。它可以在 Sharp Zaurus 这样的小型设备上运行,也可以在庞大的惠普 HP 9000 系统上运行,某些 Silicon Graphics 工作站,以及其他引起开发者注意的任何设备。OpenBSD 团队希望支持尽可能多的有趣的硬件架构,只要他们有硬件和技能来维护,因此会定期添加更多,而且你遇到的大多数计算机都有可能运行 OpenBSD。
话虽如此,当一个硬件平台变得过于神秘时,OpenBSD 就会停止支持它。一些 MIPS 系统、68K Macintosh 硬件和 Amiga 系统就是运行较旧版本的 OpenBSD 但不受新版本支持的系统的例子。
力量
作为一种传统,OpenBSD 可以在几十年前就已经过时的硬件上运行,因为当 OpenBSD 开始时,这些硬件正处于流行使用中,开发者们尽可能地保持兼容性和性能。这包括 VAX 和 Alpha 这样的平台,它们在 1980 年代和 1990 年代被认为是强大的。虽然在一个双核 64 位系统上运行 OpenBSD 的人可能不会注意到 OpenBSD 中增加处理网络数据包所需 CPU 时间量的编程更改,但在 VAX 系统上运行 OpenBSD 的人会很快注意到同样的变化。
当然,一些影响性能的更改是无法避免的。例如,系统必须在不久的将来支持 IPv6,我怀疑几十年前的硬件将难以跟上。OpenBSD 无法倒退时钟,但它将为你的应用程序留下每一丝可能的计算能力。毕竟,这才是最重要的——人们使用应用程序,而不是操作系统。这种对性能的关注意味着,即使是在 1GB 硬盘和 486 CPU 上运行的 OpenBSD 系统,仍然可以支持真实的应用程序,如 DNS 或 Web 服务器。
文档
许多自由软件项目在发布代码时就会感到满意。有些人认为,他们在程序中包含一个帮助功能,通过输入一些命令行标志即可使用,就已经做得很好了。其他人则做得更疯狂,提供语法错误且技术模糊的手册页。
OpenBSD 社区期望文档既完整又准确。系统和服务库调用的手册页非常详尽,即使与其他 BSD 相比也是如此,并包括关于使用和安全性的讨论。
文档错误被视为严重的错误,并且与其他任何严重错误一样被严厉处理。这听起来可能有些极端,但在其内部审计中,OpenBSD 团队发现了许多程序员按照手册页中推荐的方式使用库接口,但手册页中的错误使得使用变得危险或不安全。文档很重要。
免费
在原始 BSD 许可的精神下,OpenBSD 对任何人、任何目的都是免费的。你可以使用你喜欢的任何工具,在任何计算机上使用它。
今天的大多数免费软件都是根据要求软件分发商将任何更改返回给项目所有者的条款进行许可的,但 OpenBSD 甚至没有这样的要求。你可以在你的专有系统中使用 OpenBSD,将这个系统运送到世界上任何地方,而不必向开发者支付一分钱。
OpenBSD 可能是最自由的免费操作系统。像其他所有免费的类 Unix 操作系统一样,从 BSD 继承的源代码最初包含了许多在条件许可下分发的程序。有些是非商业用途免费的。有些是在你更改代码后更改名称时免费的。其他的有各种晦涩的许可条款,例如为第三方提供针对诉讼的赔偿。这些程序要么已经重新许可(在原始作者的许可下),要么被移除并用免费替代品替换。
在编程社区中,人们已经给“自由”这个词赋予了多种不同的含义。有些人认为,如果你可以下载并使用它,软件就是自由的。有些人认为,只有当最终用户获得源代码时,软件才是自由的。OpenBSD 对自由的理解是,其代码可以被任何人用于任何目的。
考虑这一点:在 OpenBSD 邮件列表上关于许可条款的讨论中,^([2]) Theo de Raadt 说:
我们知道免费许可证应该说什么。
它应该这样说
- 版权 foo
- 我放弃我的权利并允许其他人:
- 分发
- 销售
- 给予
- 修改
- 使用
- 我保留作为作者/所有者被知晓的权利
当它说其他事情时,问这个问题:
- 这是否是 100%保证的垃圾内容,永远无法影响任何人?
- 它是否在放弃更多权利(作者权利)?
如果不是,那么它必须是在给予某人更多权利,或者换句话说——从某人那里剥夺更多权利!
那么,它比我们规定的自由度要低!
OpenBSD 团队努力确保它支持的每一行代码都以这种方式获得许可。
注意
源代码树确实包含在不同许可下的代码,例如 GNU C 编译器gcc、binutils 等。没有它们,OpenBSD 仍然可以运行得很好——但你不能没有它们来编译 OpenBSD。
这很简单。OpenBSD 是一份礼物。你可以自由地使用它或不使用它。就像任何礼物一样,你可以随意对待它。但你不能随意打扰开发者要求特性或支持。
正确性
每个熟练的程序员都知道,编写正确的程序更可靠、可预测和更安全。然而,许多自由软件生产者如果他们的代码能编译并且看起来似乎可以工作就感到满意,而且相当多的商业软件公司不给他们程序员足够的时间来正确编写代码。
OpenBSD 开发者努力确保解决方案的正确实现。他们把编写可靠和安全的程序作为一项严格的规定,遵循当前最佳编程实践。将代码暴露在“奇怪”的环境,如古老的 VAX 上,也是这项纪律的一部分;OpenBSD 开发者坚持认为,一些细微的 bug(以及一些不那么细微的 bug)只有在 OpenBSD 较不主流的架构上进行测试时才能被精确定位。修复这些 bug 当然会惠及所有用户。
OpenBSD 的实现遵循 UNIX 标准,如可移植操作系统接口(POSIX)和美国国家标准协会(ANSI),但它们对第三方创建的这些标准的扩展不太关心。例如,许多 Linux 扩展在 OpenBSD 中并不存在。当这些扩展被添加到标准中时,OpenBSD 团队才会添加它们。
OpenBSD 代码经过大量艰苦的工作反复审计以确保正确性。任何试图引入错误代码的人都会被拒绝——通常礼貌地,并且经常伴随着建设性的批评,但无论如何都会被拒绝。这让我们想到了 OpenBSD 最著名的声望。
安全
OpenBSD 努力成为世界上最安全的操作系统。虽然它今天可以合理地提出这样的主张,但要保持这一地位需要持续的努力。入侵者不断尝试新的方法来渗透计算机,这意味着今天的特性可能成为明天的安全问题。当 OpenBSD 开发者了解到新的编程错误类别和安全漏洞时,他们会扫描整个源代码树以查找这类问题,并在任何人甚至不知道这些问题可能如何被利用之前进行修复。
此外,OpenBSD 充分利用硬件提供的任何安全特性。例如,AMD 的 64 位与 Intel 兼容的 CPU 可以将内存页面标记为可执行或可写,但不能同时两者都是。(Intel 后来也复制了这一特性。)这减轻了许多缓冲区溢出攻击,但操作系统必须使用这一功能。OpenBSD 在 2003 年支持了这一特性,当时硬件刚刚发布。实际上,OpenBSD 通常支持平台上提供的所有硬件安全特性。
计算机历史表明,不能期望用户修补或维护自己的系统。系统必须能够抵御现有和未来的攻击。OpenBSD 的目标是消除尚未存在的问题。
OpenBSD 与您的安全
即使 OpenBSD 被紧密地保护,入侵者仍然可以突破 OpenBSD 系统。这似乎有些矛盾,但事实上,这意味着运行计算机的人没有理解计算机安全。
OpenBSD 拥有许多集成的安全特性,但您不能假设这些特性可以保护系统上运行的所有内容。这根本不可能。没有任何操作系统可以防御操作员的错误。操作系统可以在一定程度上保护自己免受软件问题的侵害,但最终,安全责任在于管理员。
考虑一个运行在 OpenBSD 上的网络服务器——即使是 OpenBSD 集成的 Apache 服务器。OpenBSD 为网络服务器提供了一个稳定、可靠的平台,并在系统管理员分配的限制内提供所需的服务。如果系统管理员正确配置了网络服务器,网络服务器的故障不会危害操作系统。如果系统管理员将网络服务器配置为以无限权限运行,网络服务器可以对底层系统造成几乎无限制的损害。
或者考虑一个不那么极端的情况。网络服务器可能配置正确,但假设您安装了不安全的论坛软件。入侵者可以入侵论坛并编辑其数据——也许会抓取论坛软件用于访问本地数据库的用户名和密码。如果该账户信息与系统级别的用户名和密码匹配,入侵者可能能够利用它们来访问系统。或者他可能可以使用该用户名和密码获得数据库的管理员级别访问权限并渗透其他应用程序。如果那些应用程序具有提升的权限怎么办?
只有系统管理员进行谨慎、一致和深思熟虑的工作,才能防止入侵。在这本书的整个过程中,我们将讨论您在安装和运行软件时应采取的一些基本安全预防措施。我们还将讨论 OpenBSD 提供的先进安全特性,以保护自身。
OpenBSD 的用途
OpenBSD 在您的计算策略中处于什么位置?这最终取决于您的策略和需求。OpenBSD 可以在您需要稳定、可靠和安全的系统的地方使用。我建议将 OpenBSD 用于以下三个不同的角色:桌面、服务器或网络管理。
桌面
如果您需要一个功能强大的桌面系统,并且希望拥有从完整的 Unix-like 工作站期望的所有功能,OpenBSD 将非常适合。图形界面、办公套件、网络浏览器和其他桌面软件都可在 ports 集合中找到,OpenBSD 还支持各种开发工具、应用程序环境、网络服务器和其他程序员和网页开发者需要的功能。如果您是网络管理员,您会发现 OpenBSD 支持数据包嗅探器、流量分析器和其他您所依赖的程序。
服务器
如果你正在提供网页服务、处理电子邮件、提供轻量级目录访问协议(LDAP)或数据库服务,或向客户提供任何其他类型的网络服务,OpenBSD 可以帮助你。它是一个便宜且可靠的平台。一旦设置好,它就会正常工作。当然,它也是安全的,这在互联网上是不容忽视的。
网络管理
OpenBSD 是一个出色的防火墙、网桥或流量整形器。你可以用它来支持入侵检测软件、Web 代理和流量监控器。集成的包过滤防火墙和相关软件提供了最先进的网络连接管理和控制,并在流量到达你的服务器之前过滤掉许多危险类型的流量。而且它的负载均衡功能具有竞争力,与许多成本高出数千美元的商业产品相比。
关于本书
本书是为那些想要将 OpenBSD 添加到其技能库中的经验丰富的 Unix 用户或系统管理员所写。我假设你熟悉基本命令,例如 tail(1)、chmod(1)、ping(8) 等,并且你知道为什么这个列表中的每个命令在名称后面都有一个括号中的数字。我们将讨论许多你可能已经熟悉的程序,但在 OpenBSD 中可能会有所不同。
为了获得最大效益,你应该在专用机器上安装 OpenBSD。OpenBSD 可以与其他操作系统共存或在虚拟机上运行,但如果你打算在生产环境中使用 OpenBSD,你应该单独运行它。
许多人认为 OpenBSD 不是最容易使用的类 Unix 操作系统,也不是最容易使用的 BSD 版本,甚至不是最容易使用的开源 BSD。OpenBSD 没有方便的向导引导你通过配置过程的每个阶段,尽管它确实有一些基于菜单的前端。然而,一旦你熟悉了系统的工作方式,这些向导只会碍事。
要真正理解 OpenBSD,你必须愿意学习、实验,并花时间积累理解。这些知识中的大部分可以直接应用于其他版本的 BSD、其他类 Unix 操作系统,甚至完全不同的操作系统,如微软的 Windows。
内容概述
虽然本书旨在从头到尾阅读,但这里简要描述了每一章的内容,以防你想要随意跳读。
-
第一章. 讨论了安装在系统中的 OpenBSD 文档以及网络上的文档。在安装 OpenBSD 之前,你需要了解你将要做什么。
-
第二章. 讨论了在标准 amd64(也称为 64 位 Intel 兼容)系统上的安装。在安装 OpenBSD 之前做出一些决定将确保你以后不需要重新安装。
-
第三章. 带您通过真实 OpenBSD 安装的每个步骤。OpenBSD 安装程序假设您对计算机硬件和 OpenBSD 有一定的了解,您可能还没有掌握。本指南将引导您通过难点。
-
第四章. 讨论在安装 OpenBSD 后应采取的基本步骤,以确保系统安全、稳定且可用。
-
第五章. 涵盖系统启动。不同的情况需要不同的启动方法,我们将涵盖所有这些方法。我们还将讨论 OpenBSD 如何启动其组件软件。
-
第六章. 讨论如何添加、删除和限制 OpenBSD 用户账户。
-
第七章. 讨论控制用户权限和权限。OpenBSD 包括诸如类和限制等强大的工具,以及权限管理工具
sudo(8)。 -
第八章. 涵盖使用标准 OpenBSD 文件系统的磁盘管理。
-
第九章. 涵盖高级文件系统主题,如网络文件系统(NFS)、处理磁盘镜像、软件 RAID 和加密磁盘。
-
第十章. 讨论如何使用文件标志、securelevels、OpenBSD 安全公告和一些基本的加密工具来维护安全性。
-
第十一章. 回顾 TCP/IP 版本 4 和 6 的基本知识,并介绍 OpenBSD 的一些用于检查和解决网络问题的工具。
-
第十二章. 带您配置 OpenBSD 的网络堆栈,用于以太网、 trunk 和虚拟局域网(VLAN)。
-
第十三章. 介绍 OpenBSD 的附加软件工具。您将学习如何安装预编译的软件,编译自己的软件,以及验证和删除软件。
-
第十四章. 描述/etc目录中每个主要文件,这些文件在其他地方没有涵盖,并讨论您可能希望如何使用这些文件。
-
第十五章. 讨论 OpenBSD 维护自己的各种方式,以及您如何使这些过程适应您的环境和工作流程。
-
第十六章. 讨论与 OpenBSD 集成的软件配置。您将了解系统日志器和日志文件管理、DHCP 服务器、Web 服务器等。
-
第十七章. 讨论了作为桌面操作系统对 OpenBSD 有用的软件,例如窗口管理器
cwm(1)和 Xenocara。本章还包括了使使用桌面 OpenBSD 更容易的重要软件,例如 SSH 密钥和tmux。 -
第十八章. 讨论了配置标准内核可用的各种工具。与许多其他免费类 Unix 操作系统不同,OpenBSD 不期望或要求系统管理员编译内核。您可以在不重新编译的情况下调整标准内核。
-
第十九章. 讨论了在必须的情况下如何重新编译内核。
-
第二十章. 介绍了如何从快照或源代码升级 OpenBSD。
-
第二十一章. 记录了 OpenBSD 的集成数据包过滤引擎 PF。它包括对现实世界情况的讨论以及如何处理它们。
-
第二十二章. 介绍了包过滤器除了过滤数据包之外还能做什么。
-
第二十三章. 包含了一些不适合其他地方但又不值得单独成章的小知识。这包括无盘 OpenBSD、构建可启动的 USB 安装介质以及制作自定义 OpenBSD 安装套件。
本书不会涵盖 OpenBSD 能做的所有事情,但它会为您打下坚实的基础。要学习其余内容,您需要访问 OpenBSD 的信息资源,这是第一章的主题。
^([1]) 请注意,由于人的原因,我仍然有不好的日子,但我主要通过其他方式来解决它们。不要问我后院里堆成小山的泥土。
^([2]) 这是从 2002 年 10 月 24 日 openbsd-misc 邮件列表上的内容。它已经超过十年了,但仍然说得很多。
第一章. 获取额外帮助
邮件列表是粗糙的;
作业是强制性的。
爱它或离开它。
你已经购买了这本书,所以你现在拥有关于 OpenBSD 所需的所有信息。你手中握有所有 OpenBSD 智慧和洞察力的终极宝库,一旦完成它,你将成为 OpenBSD 提供的所有内容的领主和主人。对吗?
抱歉,不行。没有一本书能包含关于 OpenBSD 的所有知识。UNIX 已经接近 40 岁了,而 BSD 操作系统已经存在了 30 多年。OpenBSD 本身已经超过 15 年历史,建立在数十年的传统、知识和社区发展之上。你不可能仅凭一本书就能掌握它。如果你停止浪费时间在诸如拥有家庭和避免坏血病等琐事上,你可能会通过一堆书籍和几年的学习来掌握它。
OpenBSD 社区维护着各种各样的信息来源。其中一些,如手册,与 OpenBSD 操作系统集成。OpenBSD 团队维护着额外的资源,如主要 OpenBSD 网站和官方 OpenBSD 邮件列表。用户和爱好者维护着额外的网站、邮件列表和文档。信息的洪流可能会压倒经验丰富的用户,并使新用户感到如此害怕,以至于他们甚至不敢尝试整理这些信息。这就是为什么本章将带你手把手地了解一些其他可用的资源。[2]
OpenBSD 的支持模式
如果你只与商业 UNIX 合作过,你可能会发现 OpenBSD 的支持结构有些令人惊讶。没有免费的电话号码可以拨打,也没有供应商可以引导你。不,你不能与支持团队的经理交谈。没有这样的人。管理权在你手中。
许多商业操作系统隐藏了它们的内部工作原理,你只能通过它们提供的程序、应用程序编程接口(API)和应用二进制接口(ABI)来访问。如果你想了解更多关于你的操作系统是如何工作的,你无法做到(除非你进行逆向工程)。当某件事出错时,你只能忍受它或者付钱给供应商来解决问题。
另一方面,OpenBSD 是完全开放的。你可以查看源代码、编译器和生成的二进制文件。你拥有官方手册和大量辅助文档。你可以通过开发者使用的相同工具访问开发者的日志——这些日志描述了系统每个部分的每一次更改。你可以撤销更改,理解更改背后的动机,甚至可以联系最近在您感兴趣组件上工作的人,并询问他们的想法。你可以添加自己的功能。换句话说,你有机会以精致、痛苦的程度理解 OpenBSD。
如果你想了解 OpenBSD,你必须从接受别人提供的食物转变为阅读烹饪书并自己制作餐点。如果你愿意利用提供的信息来学习,你将培养技能,并且你可能会在 OpenBSD 社区中结交一些朋友。如果你想使用 OpenBSD 但没有时间或意愿去学习,投资一个商业支持合同。全世界有许多公司和顾问支持 OpenBSD。OpenBSD 网站列出了数十个。
如果你不想学习,也不想购买支持合同,那么 OpenBSD 对你来说可能就不是合适的选择。
代码是好的。问题出在你身上?
系统管理员很少会遇到 OpenBSD 本身的问题;软件运行良好,并且运行得很好。大多数问题源于他们自己的理解,或者缺乏理解。
当一个程序表现出意外行为时,问题通常是你对期望或理解上的差距,而 OpenBSD 社区期望你会努力提高自己的知识,以便使系统能满足你的需求。其他人使 OpenBSD 运行正确,你也可以。
话虽如此,你可能会发现问题确实很真实,但除非你理解了正确的行为——不仅仅是你认为系统是如何工作的,而是系统实际上是如何工作的——否则你无法确定问题是由 OpenBSD 本身引起的。问题可能是 OpenBSD 的错误、硬件问题或第三方工具的误操作。为了正确识别错误,你必须学习系统应该如何表现以及为什么。
例如,在撰写本书的第一版之前,我从未使用过 OpenBSD 机器来显示串行控制台。我的所有类 Unix 机器都连接到一个生锈的老式终端服务器。大多数人没有那么多串行控制台,他们希望在两个 OpenBSD 机器之间使用 null 调制解调器电缆,并且每台机器都作为对方控制台的终端。(我们将在第五章“启动过程”中介绍串行控制台。)通过阅读手册页(下一节将讨论),这种常见的配置看起来足够简单:连接电缆,配置一台机器将它的控制台输出到串行端口,在显示机器上成为 root 用户,并输入 tip tty00。另一台机器的控制台应该出现在终端窗口中,但事实并非如此。
下一个问题就是,“出了什么问题?”这可能是 OpenBSD 的错误、硬件故障或我对理解上的差距。通过系统交换,我证明了该命令在其他 OpenBSD 机器上工作,但在我特定的测试盒子上不行。进一步使用串行鼠标和调制解调器进行测试显示,测试机器上的串行端口有问题。
如果串行端口处于正常工作状态,我可能实际上会发现一个 OpenBSD 错误,但可能不会。
信息来源
OpenBSD 通过三个主要渠道提供信息:手册(man)页、网站和邮件列表。为了了解为什么你的系统在你的环境中以某种特定方式表现,你可能需要检查这三个渠道。
手册页
手册页是 Unix-like 系统上呈现文档的原始格式。虽然手册页有晦涩、难以阅读或不完整的声誉,但 OpenBSD 团队希望其手册页是可读的、正确的和完整的。
当手册页首次创建时,平均的系统管理员是一位 C 程序员。因此,手册页是由程序员为程序员编写的。OpenBSD 的开发者也是程序员,他们认为手册页是 OpenBSD 文档的最终权威。即使是文档错误也被视为严重的错误,并且会尽快处理。手册页应该是你了解 OpenBSD 工作原理的第一步。
话虽如此,手册页并不是教程。手册解释了事物是如何工作的,而不是如何输入命令以实现特定效果。你必须能够将手册页提供的信息组装成你需要的工具。如果你想学习教程,请阅读第三方网站上的文章、常见问题解答(FAQ)和这本书。如果你找到一个告诉你如何确切完成你想要的事情的教程,请阅读相关的手册页和教程。只需记住,任何人都可以编写教程,而且无法保证任何特定教程的有效性或安全性。
手册章节
OpenBSD 手册有九个部分,每个手册页只出现在一个部分中。这些部分有时被称为卷,这个名字来自手册足够小,可以打印和分发的时代。每个部分涵盖一个单一的主题。这些部分如下:
-
1: 通用命令
-
2: 系统调用和错误号
-
3: C 库
-
3p: Perl 库
-
4: 设备驱动程序
-
5: 文件格式
-
6: 游戏
-
7: 杂项
-
8: 系统维护和管理命令
-
9: 内核内部
手册页通常在命令后面带有括号中的章节号,例如ping(8)或ed(1)。这给出了命令的名称(ping)和命令文档所在的章节(8,系统维护)。几乎 OpenBSD 的每个部分都有一个手册页。
查看手册页
使用man(1)查看手册页。如果你知道章节号,可以在程序名之前输入它,但章节号不是必需的。例如,要查看标准网络实用程序ping(8)的手册页,请输入以下内容:
$ **man ping**
你会得到类似这样的回应。
PING(8) OpenBSD System Manager's Manual PING(8)
NAME
ping - send ICMP ECHO_REQUEST packets to network hosts
SYNOPSIS
ping [-DdEefLnqRrv] [-c count] [-I ifaddr] [-i wait] [-l preload]
[-p pattern] [-s packetsize] [-T tos] [-t ttl] [-V rtable]
[-w maxwait] host
DESCRIPTION
ping uses the ICMP protocol's mandatory ECHO_REQUEST datagram to elicit
an ICMP ECHO_REPLY from a host or gateway. ECHO_REQUEST datagrams
(``pings'') have an IP and ICMP header, followed by a "struct timeval"
and then an arbitrary number of "pad" bytes used to fill out the
packet. The options are as follows:
-c count
Stop sending after count ECHO_REQUEST packets have been sent. If
count is 0, send an unlimited number of packets.
-D Set the Don't Fragment bit.
…
只需阅读这份文档,你就可以了解到关于低级故障排除工具ping的比你想知道的更多的信息。如果你需要更多信息,请查看ping(8)引用的其他手册页。阅读足够的页面,你将深入理解 OpenBSD。
一旦你进入了一个手册页,按下空格键或 PGDN 键可以向前滚动一整屏。如果你不想滚动那么远,请按 ENTER 键或向下箭头键以向下滚动一行。输入 B 或按 PGUP 键可以向后滚动一屏。要在手册页内搜索,请输入 / 后跟要搜索的单词,然后按 ENTER 键。你会跳转到搜索词的第一个出现位置。随后,输入 N 可以跳转到下一个出现位置。
注意
这里讨论的手册页导航假设你正在使用默认的 BSD 分页器,more(1)。如果你使用的是不同的分页器,请使用该分页器的语法。如果你对类 Unix 系统足够了解,已经设置了首选默认分页器,你可能可以跳过这本书的这一部分。
查找手册页
新用户经常说,如果他们能找到正确的手册页,他们会很高兴阅读。你可以使用 apropos(1) 和 whatis(1) 在手册上执行基本的按关键字搜索。apropos 命令搜索包含你指定单词的任何手册页名称或描述。whatis 命令执行相同的搜索,但只匹配整个单词。例如,如果你对 vi 文本编辑器感兴趣,你可能尝试以下操作:
$ **apropos vi**
…
vmware (4) - VMware SVGA video driver
voodoo (4) - Voodoo video driver
wsudl (4) - video driver for DisplayLink USB display devices
xcmsdb (1) - Device Color Characterization utility for X Color Management System
…
在我的系统中,这会生成 686 个条目,其中大多数与 vi(1) 没有关系。这里随机显示的条目包括设备驱动程序和用户实用程序,但没有文本编辑器。如果你仔细检查它们,你会看到字母 vi 出现在每个条目中,被封装在像 video* 或 device 这样的单词中。根据你寻找的内容,apropos 可能会提供太多信息。
尝试使用 whatis 进行类似的搜索:
$ **whatis vi**
ex, vi, view (1) - text editors
只匹配整个单词可能更有用。通过实验 apropos 和 whatis,直到你熟悉它们,你应该能够找到你喜欢的几乎所有主题。
重叠的手册页名称
一些手册页的名称与其他部分的手册页名称相同。例如,假设有人提到 sysctl 并想了解它,那么你将搜索手册页。
$ **whatis sysctl**
sysctl (3) - get or set system information
sysctl (8) - get or set kernel state
sysctl.conf (5) - sysctl variables to set at system startup
我们有一个 sysctl.conf 文件和两个不同名称为 sysctl 的手册页。手册第三部分是关于 C 库的。如果你刚开始学习 sysctl,你可能会觉得这个手册页有些令人生畏。
默认情况下,man 会显示它找到的第一个匹配的页面。它首先搜索命令,然后是游戏,接着是编程库,然后是像 Perl 这样的附加程序。你可以在 /etc/man.conf 中更改这个搜索顺序(参见 第十四章)。
在这种情况下,手册第三部分在手册第八部分之前。如果不指定节号,你将阅读关于编程接口的内容。要查看系统管理员命令 sysctl 的手册页,你必须运行 man 8 sysctl。
手册页内容
手册作者试图有意义地安排他们的内容,尽管意义因所记录的内容而异。一个普通用户命令的手册页可能比内核编程接口的手册页更容易理解。即便如此,大多数手册页都分为几个部分。一些常见的部分包括以下内容:
-
NAME 告诉你程序或工具的名称。一些程序有多个名称;例如,
vi(1)文本编辑器也提供为view(1)或ex(1)。手册页列出了所有这些名称。 -
SYNOPSIS 列出了可能的命令行选项及其参数,或者提供了程序员如何调用库或界面的示例。一旦你阅读了手册页面并使用过几次命令,概要可能就足以提醒你需要什么。
-
DESCRIPTION 包含了程序或功能的简要概要。你还会找到关于命令行选项的详细讨论。
-
BUGS 描述了已知的故障条件和异常行为,通常讨论功能不符合预期的情况。始终查看 BUGS 部分;它可以节省你很多时间。我经常遇到命令问题,结果发现行为,有时甚至是一个解决方案,在这里列出。诚实是计算产品中的一个美好事物。
-
SEE ALSO 传统上是手册页的最后一节。OpenBSD 是一个相互关联的整体,每个命令都与其他命令有关联。在一个理想的世界里,你会阅读每一个手册页,并且能够在脑海中形成一个系统的整体印象。但由于我们大多数人无法做到这一点,本节将指导你查看相关的手册页。
Man Pages on the Web
手册页也可在 www.OpenBSD.org/ 及其各种在线镜像上找到。基于网络的手册页的一个有趣之处在于,你可以查看它们的历史版本和其他架构。你想知道 i386 和龙芯硬件的 sysctl 命令之间是否有差异吗?网络版本将允许你比较两个不同的手册页。(你也可以使用集成手册来做这件事,但网络版本更方便。)
The OpenBSD Website
The OpenBSD website (*www.OpenBSD.org/**) 包含了大量信息——从管理、安装和管理到查找硬件的地方。首页链接到关于 OpenBSD 目标和支持的一般讨论,你可以在这里找到获取 OpenBSD 的途径、可用资源和你可以支持 OpenBSD 的方式。项目成员会保持网站更新。如果你有 OpenBSD 问题或疑问,请首先查看此网站。
镜像
世界各地有许多人镜像了 OpenBSD 网站。主网站访问量很大,镜像通常会更快地响应。你会在主网站底部看到链接。我建议你选择并收藏一个对你来说响应快速的官方镜像。镜像站点通常利用率较低,因此比官方网站更快。
OpenBSD FAQ
OpenBSD FAQ 是 OpenBSD 对常见问题答案的存储库。虽然 FAQ 中的许多信息与 man 页面重复,但 FAQ 以问答格式呈现这些信息,通常更容易理解。
与许多其他 FAQ 不同,OpenBSD FAQ 包含了广泛的教程。例如,FAQ 中的第四章包含了完整的详细安装过程。如果你遇到问题,或者想知道 OpenBSD 的一些主要部分是如何工作的,请首先查看 FAQ!
非项目网站
许多人维护着专门针对 OpenBSD 内容的网站,与 OpenBSD 相关,或者对 OpenBSD 用户来说通常很有用。无论何时你遇到问题或试图理解某事,你的搜索引擎可能会引导你到这些网站上的文章。然而,仔细且怀疑地阅读第三方文档。FAQ 之外的教学和文章可能包含错误信息,违反 OpenBSD 的最佳实践,或者只在作者特定的环境中工作.^([4])
我可以无条件推荐的唯一第三方网站是www.undeadly.org/,一个 OpenBSD 新闻聚合器。当一个网站发布有价值的 OpenBSD 相关内容时,undeadly.org 的维护者会链接到它。
如果你想要一个讨论 OpenBSD 的在线论坛,你可以尝试 DaemonForums (www.daemonforums.org/)。DaemonForums 有所有主要 BSD 变种的讨论组,包括 OpenBSD。
OpenBSD 邮件列表
OpenBSD 项目主要通过邮件列表进行沟通。所有邮件列表都对公众开放,但其中一些比其他列表更欢迎新用户。许多硬件平台都有专门的邮件列表,但它们只欢迎特定平台的讨论,并明确拒绝问题报告。OpenBSD 网站包含邮件列表的完整列表。在这里,我将只介绍对普通用户有用的邮件列表。
announce@OpenBSD.org
这个低频、经过审核的列表只包含重要的 OpenBSD 新闻。当 OpenBSD 发布新版本时,这个列表至少每六个月会收到一条消息。
security-announce@OpenBSD.org
当 OpenBSD 团队发现 OpenBSD 的安全漏洞时,它会将公告发布到这个列表中。如果你在互联网上运行 OpenBSD 机器,你必须订阅这个列表。我将在第十章中详细介绍security-announce。
misc@OpenBSD.org
这个列表包含一般的 OpenBSD 讨论。虽然这是一个“杂项”列表,但它仍然有严格的规则,社区坚定地执行其礼仪。我将在使用邮件列表中介绍如何有效地在 OpenBSD 邮件列表中发布。
tech@OpenBSD.org
这个列表用于深入的技术讨论,例如代码审查和协议分析。如果你想了解 OpenBSD 人员正在做什么,请阅读这个列表。这不是用于支持请求的地方。作为一个好的经验法则,如果你的电子邮件不包含代码差异,请不要发送到这个列表。
advocacy@OpenBSD.org
这个列表用于推广 OpenBSD。如果你想以非技术的方式讨论 OpenBSD 的内在卓越性,请使用这个列表。
你会发现其他可能对你感兴趣的列表,例如 www@(关于网站的讨论)和 ports@(讨论 OpenBSD 的端口系统,我们将在第十三章软件管理中介绍),但这些列表需要比大多数初学者更多的 OpenBSD 知识。
访问邮件列表的最简单方式是访问 lists.OpenBSD.org/ 的网页界面。OpenBSD 团队使用 Majordomo (www.greatcircle.com/majordomo/) 来管理其邮件列表。如果你熟悉这个包,你可以通过 majordomo@OpenBSD.org 访问邮件列表。
非官方邮件列表
你可以在 www.OpenBSD.org/mail.html 找到由第三方托管的所有 OpenBSD 相关邮件列表的相当完整的列表。这包括非英语语言的列表。
一个由 OpenBSD 开发者管理的非官方列表是 PF 邮件列表,供 OpenBSD 数据包过滤器的用户使用。这个列表面向所有 PF 数据包过滤器的用户,不仅限于 OpenBSD,但 OpenBSD 用户占据了列表的主导地位。如果你想了解更多关于 PF 的信息,请订阅这个列表。更多信息请访问 www.benzedrine.cx/。
只读邮件列表
因此,misc@OpenBSD.org 看起来像是你的邮件列表,你进行了订阅。如果你急于提问,你将立即完成几件事情:你会疏远社区,并被告知闭嘴离开;你肯定不会交到朋友。这主要是因为邮件列表的存在是为了阅读而不是为了发布。
除非你处于真正独特的情况或真正处于 OpenBSD 开发的最前沿,否则很可能会有人之前遇到过你的问题。那个人可能已经得到了答案,而且那个答案可能没有改变。回答你问题的最快和最不干扰的方式是找到那封之前的邮件。这就是邮件列表存档的作用所在。
您最喜欢的搜索引擎已经索引了 OpenBSD 邮件列表。在接近邮件列表之前,总是先向搜索引擎提问。如果您已经四处查看并发现您的问题确实是独一无二的,请向邮件列表发送消息。但当你刚开始时,您最好将 OpenBSD 邮件列表视为只读。
使用 OpenBSD 问题解决资源
让我们选择一个常见问题,并使用 OpenBSD 资源来解决问题,而不必求助于发送邮件。OpenBSD 以其硬件密码学支持而闻名。这是如何工作的,OpenBSD 做了些什么来支持它?以下是我如何从 OpenBSD 项目提供的每个信息源中搜索这个主题信息的示例。
使用 OpenBSD 网站
查看www.OpenBSD.org/,您会看到一个指向密码学的链接。这会带您到密码学页面,该页面涵盖了 OpenBSD 的密码学支持。它包括算法,并讨论了团队如何将 OpenSSL 集成到硬件密码加速器中。阅读、学习并享受。
使用手册页
让我们尝试运行 apropos cryptography:
$ **apropos cryptography**
RSA_public_encrypt, RSA_private_decrypt (3) - RSA public key cryptography
这个手册页作为一般概述并不十分有用,使用 whatis cryptography 也不会返回任何内容。
密码学通常被称为 crypto。使用 apropos crypto 会得到太多结果。使用 whatis crypto 会得到更合理的结果:
$ **whatis crypto**
crypto (3) - OpenSSL cryptographic library
crypto (4) - hardware crypto access driver
crypto (9) - API for cryptographic services in the kernel
这是一个相当简短列表,所有条目看起来都很吸引人。手册第三章是针对程序员接口的,第四章是针对设备驱动程序的,第九章是针对内核的。如果您正在寻找特定的硬件密码加速器,第四章应该会立刻引起您的注意,但您可以从您感到最舒适的地方开始。
使用互联网搜索
前往您喜欢的搜索引擎,搜索“OpenBSD 密码学硬件支持”。在我写这篇文章的那天,第一个结果将我引向了 OpenBSD 网站的官方页面。第二个结果是关于 OpenBSD 密码学框架的论文。您会找到旧文章、存档的邮件列表讨论、手册页、教程和无数的博客文章。您可能需要添加特定密码加速卡的型号以减少结果数量,使其变得可管理。
使用邮件列表
如果邮件列表存档、网络搜索、OpenBSD FAQ、OpenBSD 网站、集成手册和其他各种资源都不能回答您的问题,您可以寻求帮助。许多知识渊博且技能高超的计算机专业人士阅读 OpenBSD 邮件列表。这些人中许多人喜欢使用 OpenBSD 并愿意帮助聪明的初学者。在他们看来,“聪明”等同于“不要问已经被问过的问题”。
再看看上一节中我们收集关于 OpenBSD 加密硬件加速器支持信息的方式。关于大多数其他主题的信息同样容易获得。那些花时间阅读并回答 OpenBSD 邮件列表上的问题的人已经花费了大量时间和精力来创建这些内容并确保其准确性。现在想象一下,当有人询问关于加密加速器支持的问题时,他们的反应。大多数 OpenBSD 专家会假设以下任何一种情况:
-
此人希望有人手把手地指导他们。
-
此人不愿意阅读文档。
-
此人对 OpenBSD 开发者毫无敬意。
-
此人的智商像砖头一样。
大多数 OpenBSD 专家会得出结论,提出问题的人根本还没有准备好运行 OpenBSD。至多,提问者会被忽视。在最坏的情况下,一些编写了所有这些文档的资深 OpenBSD 人士会因为他们的辛勤工作被彻底贬低而感到愤怒,并对提问者进行严厉的抨击,以至于他的显示器需要在梅奥诊所烧伤病房待上三个月。
在发送电子邮件之前请记住这一点。你真的已经检查过所有可能的地方寻找答案了吗?有没有其他你还没有尝试的搜索词?使用不同的关键词进行几次额外的搜索比写一封有用的电子邮件要快得多,而且你找到你问题的答案的机会很大。
如果你熟悉其他免费类 Unix 操作系统,OpenBSD 的邮件列表可能会给你带来一点文化冲击。OpenBSD 用户几乎可以定义为高级计算机用户。如果一个经验丰富的系统管理员试图调试一段软件,那么这位管理员应该知道足够的信息来询问负责的人。如果你去 Linux 论坛,你会找到人们正在讨论服务器和客户端程序、桌面环境以及几乎在该平台上运行的任何其他软件。这些论坛由致力于提供全天候支持和极尽所能帮助他们的操作系统征服世界的志愿者维护。
OpenBSD 的人不在乎他们是否统治世界。他们并不真的在乎你是否使用他们的软件。如果其他人能从中受益,那很好。如果不行,那也没关系。他们乐于帮助你解决 OpenBSD 特有的问题,但他们并不真的关心你的数据库问题或你的网站。如果你因为 OpenBSD 的libc中的一些微妙错误而难以将你偏好的窗口管理器移植到 OpenBSD,OpenBSD 的人会非常乐意和你交谈。如果你不能以你想要的方式配置你的窗口管理器,那么你应该与窗口管理器支持小组交谈。
创建一个良好的帮助请求
在发送电子邮件之前,仔细思考你试图解决的问题。你应该实际上问什么问题?尽可能狭窄地定义问题。假设你无法使用 OpenBSD 的 IPsec 客户端连接到虚拟专用网络(VPN)服务器。问题是你是否实际上无法到达 IPsec 服务器?当你关闭 OpenBSD 防火墙时连接是否工作,但在重新启用过滤时返回?每次尝试启动 VPN 时isakmpd(8)是否崩溃并留下核心文件?这些问题都是非常不同的问题。在电子邮件中包括精确的问题将使你得到更好的回应。
你的电子邮件的第一段应该简要而简洁地说明你的问题。如果你的第一段没有足够的内容引起人们的兴趣,他们可能会在看到任何相关内容之前就删除这封电子邮件。
在那个重要的第一段之后,收集与问题相关的任何和所有信息。将这些信息包含在你的电子邮件中。这应该包括以下内容:
-
你正在运行的 OpenBSD 版本
-
你的硬件平台
-
任何错误输出(务必检查/var/log/messages以及你的终端)
-
/var/run/dmesg.boot的内容
-
一个完整但狭窄的问题描述
给你的电子邮件消息一个合适的主题。像“OpenBSD 问题”这样的主题会被忽视。像“在最新的 OpenBSD 快照中可复现的 isakmpd 崩溃”这样的主题会立即吸引注意。许多 OpenBSD 人士完全根据主题行来决定阅读哪些消息。中等高级的电子邮件阅读程序允许收件人根据主题行或消息头删除整个讨论线程。
如何被忽视
许多高级 OpenBSD 用户使用基于文本的电子邮件阅读器,如 Mutt(尽管相当多的人确实使用更图形化的电子邮件阅读器。)基于文本的电子邮件程序是处理每天成千上万封电子邮件的强大程序,但它们只显示文本,并且不很好地显示 HTML 消息。如果你使用的是图形邮件客户端,如 Mozilla Thunderbird 或 Microsoft Outlook,请将文本包裹在 72 列宽。发送纯 HTML 或没有可读行包裹的邮件会邀请经验丰富的收件人将其丢弃而不阅读。
这可能看起来很严厉,而且确实与其他开源操作系统的邮件列表管理方式不同。但大多数电子邮件客户端并不适合处理每天成千上万条消息,这些消息分散在几十个邮件列表中,每个列表中都有几个并行的讨论线程,以人类思维可接受的方式。我每天都会收到成千上万封电子邮件,我知道许多 OpenBSD 开发者接收并处理的消息甚至更多。我们无法在没有解决我们问题的邮件工具的情况下应对。HTML 支持远不如以合理的方式管理、展示和处理大量消息的能力重要。
在类似的情况下,大多数电子邮件附件都是不必要的(而且 OpenBSD 的几个邮件列表会不客气地从传入的消息中移除它们)。你不需要使用 PGP 签名你的电子邮件,那些名片附件只是表明你真的不应该运行 OpenBSD。如果你在电子邮件中包含签名,它不应超过四行。长 ASCII 艺术签名,即使是非常酷的包含 OpenBSD blowfish 的签名,也是不合适的。
很容易让挫折感将一个简单的请求变成对即时援助的狂热需求。请记住要礼貌;收到你消息的人可能会决定帮助你,但他们没有义务这么做。如果你想有人有义务帮助你,请购买支持合同。
发送您的电子邮件
在发送电子邮件之前,请再次检查你的搜索引擎。你确定这不是之前被问过的问题吗?
将你的所有信息和关于 OpenBSD 核心系统的狭窄、具体、有文档记录的问题发送到 misc@OpenBSD.org。是的,OpenBSD 有许多其他邮件列表,其中一些可能看起来更适合你的问题,但向它们发帖的人几乎总是被告知去 misc@ 询问。misc@ 的人可能会将你推荐到另一个邮件列表,但如果你的消息以“So-and-so on misc@ 建议我在这里提问”开头,那么发帖到特定列表会更好。如果你有一个关于某个附加软件(或包,如第十三章中讨论的)的狭窄、具体、有良好文档记录的问题,你可以将其发送到 ports@OpenBSD.org。
回复电子邮件
你收到的回复可能是一段简短的笔记附带一个 URL,或者甚至只是“man such-and-such.” 如果你收到这样的回复,那就是你需要去的地方。记住,你之所以提问是因为你不理解某些内容,而这些回复告诉你如何学习你问题的答案。不要只是回邮件请求别人帮你。
如果你不理解你收到的引用,将其视为另一个问题。缩小你困惑的来源。手册页和教程并不完美,如果你不完全理解它们,某些部分可能会显得相互矛盾或相互排斥。
最后,跟进。如果你被要求提供更多信息,请提供。如果你不知道如何提供,将其视为另一个问题。回到本章的开头,尝试弄清楚。如果你因为不跟进更多信息请求而建立了一个声誉,你可能连第一封回复都收不到。
现在,让我们准备实际安装 OpenBSD。
^([3]) 是的,这本书的第一章是关于在书中之外寻求帮助。我知道这是讽刺;你不需要告诉我。
^([4]) 当然,这并不适用于我博客上的任何内容。我发布的每一篇都是真理。
第二章. 安装准备
我 是 脚本 小子.
Windows is warm and tasty;
blowfish goes down hard.
安装 OpenBSD 并让机器运行起来是不够的;你希望有一个 成功的 安装。成功的安装意味着系统已配置为执行你打算让它做的任务。开发者的笔记本电脑与专用防火墙的要求大不相同,而专用防火墙可能看起来与 Web 服务器大相径庭。适当的规划将使你的 OpenBSD 安装快速、简单且成功。我们将花费大量时间在安装规划上。一旦你明白了你在做什么,实际的安装过程就相当简单了。许多人在 OpenBSD 上遇到的问题都源于没有理解他们众多的选择。
本章中的指南涵盖了大多数情况,但关于安装 OpenBSD 的最终决定权在发布版中包含的安装文档。例如,在 i386 系统上安装 OpenBSD 之前,请阅读您发布版的 i386/INSTALL.i386。
OpenBSD 硬件
OpenBSD 支持广泛的硬件架构。一些平台,如 i386 和 amd64,有广泛的支持,它们的网页和发布说明列出了大量的支持硬件。其他平台,如 SGI,只支持非常特定的硬件型号。
OpenBSD 当前支持的硬件平台包括 i386(标准 PC)、amd64(64 位 PC 风格硬件)、sparc64(Sun 风格硬件)、SGI(硅图形)、以及其他。它还支持旧平台,如 VAX 和像 Zaurus 这样的微型计算机。我感兴趣的以下平台包括:
-
i386. 过去几十年中流行的英特尔兼容计算机
-
amd64. AMD 对 32 位 i386 的 64 位扩展,被英特尔作为 EM64T 实现,有时也称为 x64、x86_64 或 x86-64(这种硬件可以运行 OpenBSD 的 32 位 i386 和 64 位 amd64 版本)
-
sparc64. 64 位 Sun UltraSPARC 和兼容设备
-
macppc. 基于 PowerPC 的 Macintosh 计算机,从 iMac 开始,直到苹果转向 amd64 硬件
-
Zaurus. Sharp Zaurus 个人数字助理(PDA)
本章涵盖了在 i386 和 amd64 平台上安装的内容。这些是大多数供应商提供的标准 32 位和 64 位 PC 系统,也是你在秘书吃午饭时最有可能在办公桌上找到的系统。它们的架构相似,安装方式完全相同。
旧系统可以很好地运行 OpenBSD。我在一个 166 MHz 处理器上运行了 OpenBSD/i386,内存为 128MB,效果相当不错。你可能有一些闲置的旧系统,它们非常适合学习 OpenBSD。
在这本书中,我假设你的设备是 PCI 总线或更新的版本。我不涵盖 EISA 硬件,或现代硬件中除了板载芯片之外的其他 ISA 硬件。如果你有一张仍然工作的 EISA SCSI 卡或网络接口卡(NIC),OpenBSD 可能支持它。我假设你仍然有原始的硬件配置软盘,并记得如何设置中断和中断请求以匹配 OpenBSD 内核所假设的。如果你不记得,就回收这张卡,并购买本世纪建造的设备。
注意,硬件必须处于工作状态。如果你的旧奔腾机因为内存损坏而经常崩溃,使用 OpenBSD 并不能解决这个问题。此外,如果硬件达到某些最低标准,OpenBSD 将非常有用。我根据自己的经验提出建议,但再次强调,文档提供了当前和最终的要求。
你可以在 www.OpenBSD.org/plat.html 找到支持的硬件平台的全列表。这个页面链接到每个硬件平台的页面,你可以获取该硬件的详细支持信息。
支持的硬件
好消息是 OpenBSD 支持大多数硬件。坏消息是它并不支持所有硬件。一般来说,OpenBSD 支持最常见的非专有硬件。它可能不支持最新的硬件,因为 OpenBSD 团队在新硬件发布之前很少有机会接触到硬件。几个月前的硬件支持通常比尖端硬件更好。
要验证 OpenBSD 是否支持你的硬件,请阅读你平台的发布说明,或者直接尝试一下。
专有硬件、二进制对象和固件
一些硬件厂商希望保持其设备的内部工作原理的秘密,这样竞争对手就不能复制他们的设计。他们通常通过两种方式隐藏其硬件设计:专有硬件和二进制对象设备驱动程序。
一些厂商不会为其硬件提供文档。厂商期望用户使用他们提供的驱动程序,并且他们只为最广泛使用的商品操作系统(如 Windows)或特定目标市场(如 Apple)提供驱动程序。没有文档,编写设备驱动程序既繁琐又困难。某些硬件在没有完整文档的情况下也能得到很好的支持,但许多硬件则不行。例如,OpenBSD 的 sparc64 平台在 Sun 发布文档之前,好几年都不支持新的 Sparc 处理器。
一些供应商不希望提供文档,但希望开源操作系统的用户购买他们的硬件。这些供应商以二进制对象或 blob 的形式提供其硬件的驱动程序。这听起来可能一开始是合理的,但操作系统必须将这些 blob 加载到内核中。OpenBSD 团队对此有几个反对意见。首先,代码不可用于审计。如果 blob 存在安全漏洞,或者与内核有细微的交互导致系统不稳定,开发者无法解决这个问题。blob 可能只是低效或浪费,但它可能会对其他内核子系统产生负面影响,甚至可能包含后门。最后,OpenBSD 的哲学要求所有代码都必须在严格的 BSD 许可证下。内核中的 blob 不是免费的,因此 OpenBSD 不会支持它们。
注意,固件与 固件 不同。固件是硬件运行所需的二进制对象,它被加载到硬件本身,而不是操作系统。你几乎可以在每个计算机组件中找到固件:CPU、主板、网络接口卡、磁盘控制器等等。固件永远不会加载到内核中;内核将固件加载到卡上。OpenBSD 团队认为这是可以接受的。固件让硬件能够向操作系统提供其文档化的接口,如果它不在磁盘上,它就会在硬件本身上。
一般而言,如果 OpenBSD 开发者拥有一块硬件、该硬件的文档以及任何对该硬件的使用需求,他们可能会实现对该硬件的支持。如果没有,那么该硬件将无法工作。在大多数情况下,不受支持的专有或 blob 驱动的硬件可以用更有效(且更便宜)的开源硬件替换。
处理器
处理器品牌无关紧要。OpenBSD 不关心它是否运行在英特尔、AMD、Cyrix 或任何其他兼容英特尔的处理器的 CPU 上。OpenBSD 在启动时探测 CPU,并使用它所识别的任何芯片功能。我曾在 486 类处理器上运行过非常有效的多兆比特防火墙,但你最满意的是使用 1 GHz 或更快的处理器。
然而,OpenBSD 的多处理器支持并不像某些其他操作系统那样广泛。OpenBSD 内核主要使用大巨人锁方法运行,因此内核一次只能运行在一个处理器上。(内核的一些小块代码不在大巨人锁之下。)从实际的角度来看,这意味着 OpenBSD 内核不会有效地使用超过两个处理器或核心。
这是否意味着你不应该在双八核处理器的服务器上使用 OpenBSD?这取决于你预期的服务器负载。只要用户进程不进入内核,它们就可以很好地扩展。例如,大多数网络日志分析软件几乎完全在用户空间运行,你可以运行大规模并行分析任务,这些任务可以很好地随着处理器数量的增加而扩展。然而,像转发数据包这样的任务会通过内核。你需要什么硬件完全取决于你预期的负载。
内存(RAM)
内存是好的。你拥有的内存越多,你会越快乐。增加 RAM 比任何其他通用改进都能加速你的系统。你应该至少有 256MB 的 RAM,最好是至少 512MB。如果你能在你的系统中获得几个 GB,OpenBSD 将充分利用这些内存。
如果你继续增加内存,你会达到一个点,你的系统拥有它需要的所有内存,更多的内存不会进一步提高性能。对于一个小型防火墙,这可能是低至 128MB,对于桌面机器,可能是几 GB,而对于大型数据库服务器,则可能更多。
大多数奇怪的崩溃和无法解释、无法重现的问题都可以追溯到不良的内存,所以请确保你使用的内存是好的。内存是旧机器中常见的故障点。
硬盘驱动器
你今天能买到的最小的新硬盘可以运行 OpenBSD,并且还有大量的空间。在较旧的系统中,我建议至少有 40GB 的磁盘空间——不是因为 OpenBSD 无法适应更小的空间,而是因为你需要空间来存储额外的文件和软件。你的磁盘越小,你就越需要密切监控其使用情况。当从源代码构建桌面环境时,很容易填满一个小磁盘,而现在的磁盘又很便宜。如果你是从闪存驱动器运行小型防火墙,我建议至少 512MB.^([5])
虚拟化
许多人在新操作系统适应期间在虚拟环境中运行它们。一些公司甚至有明确政策,要求所有系统都作为虚拟服务器运行。OpenBSD 在常见的虚拟环境中运行良好,甚至为虚拟化系统如 VMware 提供了特定的设备驱动程序。
在虚拟服务器上运行 OpenBSD 的硬件要求与在真实硬件上运行 OpenBSD 的要求相似。请注意,在虚拟环境中运行的任何操作系统都不如在同一真实硬件上运行的操作系统安全。虚拟环境并不能精确地复制真实硬件。模拟 CPU 有其自己新奇的错误,虚拟网络接口卡有独特的错误,等等。此外,提供虚拟服务器的环境本身也是一个操作系统。入侵者可以攻击那个底层操作系统,一旦入侵者控制了虚拟化服务器,运行在该机器上的客户端就更加脆弱。没有操作系统可以保护自己免受其硬件的影响。在规划 OpenBSD 在您环境中的作用时,您必须考虑这种风险。
然而,对于了解 OpenBSD 来说,虚拟环境是完全可以接受的。我在 VirtualBox、ESXi 和 Linux 的 KVM 虚拟机管理程序上轻松地运行了 OpenBSD 机器。
多个操作系统
多年来,我在一台计算机上运行了多个操作系统。我记得我对我的新 6GB 硬盘感到兴奋,因为我可以在一台计算机上运行 FreeBSD、OpenBSD、Windows 和 Linux,并为每个操作系统留有足够的空间。这是在单台桌面计算机上运行多个操作系统的唯一方法,但虚拟化技术的进步已经使这种方法过时。
与其小心翼翼地将您的桌面硬盘分区以运行多个操作系统,并希望某些专有磁盘分区程序不会吞噬其邻居,我建议运行一个支持虚拟化服务器的操作系统,并将您的次要操作系统作为虚拟机运行。OpenBSD 支持使用qemu运行虚拟客户机。
获取 OpenBSD
一旦您有了硬件,您就需要 OpenBSD。您可以从 CD 和互联网上获取 OpenBSD。
官方 CD
你为什么要在 21 世纪购买官方 CD?
OpenBSD 项目主要依靠官方 CD 的销售以及相关的书籍、服装等来资助。您可以从互联网上下载磁盘镜像并烧录自己的安装盘,但购买官方套件有助于改进 OpenBSD。OpenBSD 团队试图使官方 CD 集本身成为有趣的物品,并且通常将它们包装在某种极客主题的艺术作品中。要获取官方 CD,请访问 OpenBSD 网站并查找获取 OpenBSD 链接。您还可以找到大量与 OpenBSD 相关的商品。
您可以从互联网上下载安装镜像,但它们与官方 CD 集不同。下载的磁盘镜像不包含任何软件包,缺乏花哨的物理包装,并且只能在一种硬件架构上运行。您无法下载用于官方磁盘的镜像。
主要的 OpenBSD 分发点位于加拿大,这增加了居住在其他大陆的人的配送成本。OpenBSD 网站列出了提供官方 OpenBSD CD 的各种经销商。选择您国家的经销商可以节省关税。如果此选项对您不可用,您至少可以选择您所在大陆的经销商并节省运费。
互联网下载
其他 OpenBSD 安装方法需要网络访问,无论是下载完整镜像还是安装过程中下载文件。首先选择一个靠近您的 OpenBSD 镜像站点。您可以在www.OpenBSD.org/ftp.html找到镜像的完整列表。
您可以从 ISO 镜像、FTP、HTTP、rsync,甚至在某些平台上从 Andrew 文件系统(AFS)或网络文件系统(NFS)安装操作系统文件。我们将任务分为两部分:使目标系统启动,以及将操作系统文件放置在机器上。
镜像站点布局
所有 OpenBSD 镜像都包含类似这些的文件和目录:
-
5.1, 5.2, 5.3, 和 5.4. 编号目录包含 OpenBSD 版本。大多数镜像包含最后四个版本。这个特定的服务器包含 OpenBSD 版本 5.1、5.2、5.3 和 5.4。
-
Changelogs. 此目录包含对 OpenBSD 并发版本系统(CVS)日志的整理,供对 OpenBSD 开发感兴趣的用户使用。普通用户可能会发现基于 Web 的 CVS 浏览器更有用。
-
distfiles. 此目录包含构建 OpenBSD 端口集合中包含的第三方软件所需的文件(见第十三章、基本说明 (README) 以及关于 OpenBSD 对第三方软件和不同硬件支持的说明
在您的 CD 或镜像站点中查找您硬件架构的目录。架构目录包含每个硬件平台相当类似的文件。
首先,找到您硬件的安装说明。这些文件以 INSTALL 开头,后跟平台名称(例如 INSTALL.i386,INSTALL.amd64 等)。始终阅读您平台的安装说明。虽然我已经尽最大努力确保本书的准确性,但 OpenBSD 不断变化,您发布版本的安装文档是安装说明的最终版本。
启动介质
OpenBSD 的启动介质因硬件平台而异,每个硬件项目都有自己的启动介质要求。您不能期望从 CD 启动 Zaurus 或 VAX。
要轻松地在 i386 或 amd64 硬件上启动 OpenBSD 安装程序,可以使用软盘或光盘(我通常推荐后者)。您可以从 USB 磁盘启动安装程序,但标准方法需要从 OpenBSD 机器引导,并且非标准方法因可用设备而异。
如果您无法从光盘启动,请使用软盘。OpenBSD 提供了一个 amd64 软盘镜像和三个不同的 i386 软盘镜像。如果您从软盘启动 i386,我建议下载所有软盘镜像。
如果您无法使用上述任何一种方法启动,您必须使用如第二十三章中所述的预启动执行环境(PXE)无盘启动方法。这种方法效果良好,但需要更多的准备。
选择安装介质
启动盘可以格式化您的硬盘,配置您的网络,并将安装文件复制到磁盘。然而,启动介质不包括那些安装文件。i386 和 amd64 机器的安装文件包含在 ISO 图像中,并通过 FTP 或 HTTP 在网络上提供。
如果您打算在多台 OpenBSD 机器上安装此版本,您可以下载包含安装文件的 CD 镜像。然而,它比仅启动安装的 ISO 图像大得多,因此下载它需要某种宽带连接。
如果您正在进行单个 OpenBSD 安装,或者您没有光盘驱动器,我建议使用 HTTP 安装。如果您从合理接近的镜像站点安装并且有足够的带宽,OpenBSD 通过 HTTP 安装既快又可靠,并且使用的带宽大约是下载安装 ISO 图像的一半。如果您愿意,也可以通过 FTP 安装。
高级用户可以通过 PXE 方法安装 OpenBSD,如前节所述,并在第二十三章中详细说明。
本地安装服务器
光盘之所以如此受欢迎,是因为您只需要从互联网下载一次文件,但可以重复使用您的下载来在多台机器上安装 OpenBSD。但是,光盘物理上很脆弱,并不是每台机器都有光盘驱动器。如果您想在多台机器上安装 OpenBSD 而不消耗每台安装的带宽,请下载您架构所需的全部安装文件。如果您将这些文件复制到本地 FTP 或 Web 服务器,您就可以从这些文件安装 OpenBSD 到任意数量的机器。要从本地 FTP 服务器安装,您需要 FTP 服务器的用户名和密码。
为了帮助节省 OpenBSD 项目在带宽成本上的开支,请只下载您需要的架构的目录。如果您确切知道您要安装什么,请只下载那些文件集。您可能不关心自己的带宽,但请尊重他人的带宽。
文件集
每个架构的发布目录包含几个名为 comp52.tgz、base52.tgz 等的压缩文件。这些 文件集 包含压缩的 OpenBSD 安装文件。通过选择安装特定的文件集,你可以选择你的 OpenBSD 系统将具有多少开箱即用的功能。例如,文档保存在单独的发行版集中。如果你在其他地方有文档,你可能选择不在特定的系统上安装它。此外,入侵者经常使用编译器,所以你可能不希望在你想保护的系统上安装它们。但如果这是你的实验性“学习 OpenBSD”机器,请安装所有内容。
每个文件集都有一个名称和版本号。例如,OpenBSD 发布 5.2 中的一个发行版集是 base52.tgz。这些是 5.2 版本的基文件。在下一个版本中,这个相同的文件集将被称为 base53.tgz。
所有架构都包含所有文件集,除非架构的发布说明中另有说明。如果你是第一次安装 OpenBSD,请花点时间决定你需要哪些发行版集。如果可能的话,在你的测试机器上安装它们。你总是可以在以后为专用目的机器缩减它们。
以下文件集可用:
bsd, bsd.mp, 和 bsd.rd
这些文件集仅包含 OpenBSD 内核。内核是操作系统的核心,包含设备驱动程序和基本系统功能。没有内核,系统将无法启动。bsd 内核适用于单处理器机器,而 bsd.mp 内核支持多处理器。bsd.rd 内核包含 OpenBSD 安装程序、基本用户空间实用程序和实时系统内核。一次只能运行一个内核。
baseXX.tgz
这包含了 OpenBSD 的核心程序——所有使 OpenBSD 成为类 Unix 系统的东西。包括 /bin、/sbin、/usr/bin 和 /usr/sbin 的内容;系统库;以及你在最小化类 Unix 系统上期望找到的所有杂项程序都包含在这个文件集中。你必须安装这个文件集。
etcXX.tgz
你可能会猜测这个文件集包含 /etc 中的文件,但它还包含其他必需的文件和目录,例如 /var/log 和 root 用户的家目录。你必须安装这个文件集。
manXX.tgz
如果你需要基础和 etc 文件集中的程序的 man 页面,请安装这个发行版集。其他集的 man 页面将与各自的文件集一起安装。
compXX.tgz
这个文件集包含 C 和 C++ 编译器、汇编器、库、工具、手册以及每个工具链。你需要这个文件集来开发或编译软件,或者使用端口集合(见第十三章*,被认为是 UNIX 经典,除非安装,否则老用户不会高兴。其他,如 /usr/games/wargames,假设你熟悉 1980 年代初的电影。你可能不需要游戏文件集(除非你想看看我高中时代所谓的“电脑游戏”)。
xbaseXX.tgz
这包含 Xenocara 的核心,即 OpenBSD 版本的 X Window 系统。如果你想使用 X,你需要这个。尽管你可能没有在这个计算机上安装控制台或显示器,但请记住,X 允许这个服务器上的程序远程显示。
大多数 OpenBSD 软件包都假设你已经安装了这个文件集。如果你发现某个软件包因为缺少 X 库的错误而崩溃,你需要这个文件集。
xetcXX.tgz
这包含 X 配置文件。如果你不仅使用 X 的库,还需要这个文件集。
xfontXX.tgz
这包含 X 种字体。如果你计划在这个机器的控制台上使用 X,请安装这个文件集。
xservXX.tgz
这个文件集包含所有 X 显卡驱动程序。如果你计划在这个机器的控制台上使用 X,请安装这个文件集。
xshareXX.tgz
这包含 X 文档。如果你计划在这个机器的控制台上使用 X,请安装这个文件集。
分区
分区 是硬盘的逻辑子分区。OpenBSD 可以处理具有自己独特特权的不同分区。你可能将一些分区设置为只读,这样它们上的文件就不能被添加、移动或更改。
OpenBSD 可能会拒绝在指定的分区上运行程序,并且它知道设备节点应该只出现在某些分区上。用户文件不应该有 setuid 或 setgid 权限,因此操作系统不会在用户数据分区上的文件上识别这些特权。虽然许多操作系统支持这类权限控制,但 OpenBSD 默认使用它们。
安装 OpenBSD 最困难的部分是分区。当你不知道分区如何工作时,选择分区可能会很麻烦。
如果你熟悉其他类 Unix 操作系统(例如某些 Linux 发行版),你可能习惯于使用一个大的根分区并将所有内容都放在上面。这有几个原因是不好的。OpenBSD 使用分区作为一个安全工具。一个大的单一分区消除了每个分区的安全和权限。当你的日志文件安全地包含在一个分区上时,一个失控的进程或用户无法填满你的整个驱动器。虽然它可能填满一个分区,但你仍然可以在其他分区上创建和编辑文件,这为你提供了解决问题所需的灵活性。
与许多具有花哨菜单和图形工具的安装程序不同,OpenBSD 的安装程序期望你知道如何使用低级磁盘管理工具,例如disklabel(8)。然而,与那些操作系统不同,OpenBSD 可以在更广泛的系统上以多种方式安装,所有这些都可以使用单个安装程序。
如果你这是第一次安装 OpenBSD,请使用安装程序提供的默认分区方案。OpenBSD 将提供所有标准分区,但根据你的磁盘大小调整它们的大小。这里的讨论基于在一个相当小的磁盘上的标准 i386 安装。
如果你之前已经安装了 OpenBSD,并且你正在一个专用机器上安装它,你可能需要特殊的分区方案。在这种情况下,拿一张纸和一支铅笔,写下你的硬盘大小,你需要每个分区,以及每个分区期望的大小。你的专用 OpenBSD 机器几乎肯定会有与默认安装相同的所有分区,但它们的大小会有所不同。一个网络服务器与桌面机器的磁盘空间需求非常不同,而桌面机器的需求又与防火墙的需求不同。
如果你有一个大硬盘,留一些未分配的空间。分区的大小正好满足你的需要可以加速文件系统完整性检查;fsck(8)不会花费周期来检查未使用的磁盘空间。在固态硬盘中,未使用的空间为磨损均衡算法提供了更多的单元来操作,从而增加了硬盘的使用寿命并减少了故障的可能性。有备用的磁盘空间而你从未需要它们,总比需要而你却没有磁盘空间要好。
标准 OpenBSD 分区
标准 OpenBSD 分区包括 /(根)、交换空间、/tmp、/var、/usr、/usr/X11R6、/usr/local、/usr/src、/usr/obj 和 /home。如果你创建了一个自定义布局并且不包括这些分区之一,安装程序将把属于该分区的文件放入你的根或 /usr 分区,从而快速填满它们。如果你想安装后创建一个分区,你必须在你磁盘上找到空间。除非你在磁盘上留下了未分配的空间,否则你最好重新安装整个系统。
根分区
根分区包含主要的 OpenBSD 配置文件和将计算机带入单用户模式并连接到网络所需的最基本软件。你的系统需要快速访问根文件系统,所以如果你有多个磁盘,请将根分区放在最快的(或最小的)一个上。
根分区是唯一一个其磁盘位置至关重要的分区。多年来,i386 系统反复扩展以超越其自身限制——毕竟,它们基于的架构最初只能处理高达 640KB 的 RAM!所有现代操作系统内核都以对用户来说大部分是透明的方式绕过这些限制,但在系统首次启动时,你被硬件的限制所困。
许多旧的 i386 系统对硬盘大小有限制。它们只能识别 128GB 的硬盘、2TB 的硬盘或某些其他数字。硬件 BIOS 无法访问超过这个限制的任何内容。如果你使用的是硬盘大小限制为 128GB 的电脑,并且你将内核放在超过第一个 128GB 磁盘空间的地方,电脑将无法找到内核,因此无法启动系统。在开始之前检查你的硬件手册。如果手册提到了磁盘大小限制,你的整个根分区必须适应那个限制。
如果你违反了这个限制,你的系统可能看起来还能工作。然而,当你更改文件 /bsd 时,你的电脑很可能会拒绝启动。通过将根分区放在磁盘的第一位,并确保它足够小以适应硬件的限制,你可以避免很多痛苦。
交换空间
交换空间用于虚拟内存。当你的电脑 RAM 不足时,它开始将内存中闲置的信息移动到交换空间。当电脑需要这些信息时,它们从虚拟内存加载到实际内存中。这并不一定对性能有害。许多程序的大部分时间只执行它们代码的一小部分。OpenBSD 在确定哪些内存部分可以移动到交换空间以及哪些使用频率过高而不适合交换方面做得相当不错。如果一切顺利,你的电脑几乎永远不会需要交换空间。
OpenBSD 还在系统故障期间使用交换空间。如果内核崩溃,电脑会将系统内存的内容写入交换分区。这意味着交换分区至少要稍微大于系统中的物理 RAM 量。
你需要多少交换空间?简短的回答是,“这取决于系统。”OpenBSD 默认分配的交换空间是你物理 RAM 的两倍。只要你知道这是一个非常通用的规则,这并不是一个坏规则。物理内存的三到四倍大小的交换空间不会有害。如果你的电脑使用的交换空间超过这个量,它就过载了,并且性能会下降。
如果你发现自己经常使用交换空间,考虑增加你的物理内存。RAM 很便宜。
也考虑未来的升级。当你安装 OpenBSD 时,如果你的系统有 2GB 的 RAM,但打算增加到 8GB,分配 16GB 的交换空间是个好主意。除非你在安装软件时留下未分配的磁盘空间,否则以后添加交换分区是困难的。(注意,虽然你可以将交换空间设置为一个文件,但 OpenBSD 只能将崩溃转储写入实际的交换分区。)
/tmp 目录
/tmp 目录是系统上所有用户的临时空间。/tmp的空间需求通常是个人观点的问题——毕竟,你总是可以在你的/home目录中使用一块空间作为临时空间。自动软件安装程序通常会提取文件到/tmp。我通常建议至少 3GB 的/tmp空间,但我对我的临时空间做了很多糟糕的事情。许多人使用 256MB 或 512MB 的/tmp目录并且过得很好。
/var 分区
/var 分区包含经常变化的数据,如日志、数据库、邮件队列、临时运行文件、网站等。OpenBSD 默认为/var分配大约 5GB 的空间。这对于教育安装来说应该足够了。然而,如果你正在构建一个网站、数据库或日志服务器,那么/var应该获得你大部分的磁盘空间。如果你在一个非常小的系统上,你甚至可以为/var使用 10MB 的空间。
/usr 分区
/usr 分区包含操作系统程序、编译器、库和附加程序。/usr的大部分变化只在你升级系统时发生。OpenBSD 默认为/usr分配 2GB 的空间,这在桌面系统上已经足够了。
/usr/X11R6 分区
/usr/X11R6 分区包含 X 窗口系统的程序和文档。OpenBSD 为链接到 X 窗口系统的软件打包,而且许多你可能在服务器上期望找到的软件(如 ImageMagick)需要 X 库。
如果你打算不安装任何 X 软件,并且计划完全使用自己的软件而不使用 X,那么你不需要这个分区。如果你有疑问,或者这是你的第一次安装,请保留这个分区。
/usr/local 分区
/usr/local 分区包含附加的 OpenBSD 软件,通常来自软件包(参见第十三章)。这个分区可能比包含核心 OpenBSD 软件的/usr分区大得多。OpenBSD 默认为/usr/local分配 5GB 的磁盘空间,我从未需要超过这个量。
/usr/src 分区
/usr/src 分区专门用于 OpenBSD 源代码。在一个没有编译器的专用机器上,如防火墙或安全的 Web 服务器,你可能不需要本地源代码副本。如果你不打算从源代码升级这台机器,也不打算在本地机器上使用源代码作为参考,那么你不需要这个分区。如果你有疑问,请保留它。
/usr/obj 分区
/usr/obj 分区是 OpenBSD 构建操作系统和 Xenocara 新版本的地方。这里的文件是临时的;一旦你安装了新的 OpenBSD 版本,你就不需要这些文件了。创建新的文件系统比删除这种文件系统中的单个文件要快,所以/usr/obj被配置为独立的分区。
如果你没有打算从源代码构建新的 OpenBSD,你不需要/usr/obj。如果你后来发现你需要这个分区,你可以从未使用的空间创建它,或者通过 NFS 挂载它。
/home 分区
/home 分区可以描述为“其他所有东西”。用户目录放入/home,以及任何为用户准备的数据。家庭 MP3 和照片收藏应该放在/home,以及你的个人源代码、电子邮件和任何你想要保留的东西。
创建其他分区
OpenBSD 支持每个磁盘最多 16 个分区。如果你想创建其他分区,你可以使用安装程序。你的公司是否有政策要求所有附加软件都必须放在/opt或/usr/companyname?好的,创建那个分区。OpenBSD 的标准不是紧身衣,而是一个起点。你拥有系统。让它按照你的需求运行。
分区文件系统
“文件系统”和“分区”这两个词经常被互换使用。它们密切相关,但却是两件不同的事情。文件系统是一种在分区上分配和跟踪文件的方法。你可以备份和恢复文件系统,但如果分区损坏,你会遇到更大的麻烦。
OpenBSD 默认使用标准的快速文件系统(FFS)。FFS 已经存在了几十年,调试得很好,理解得也很透彻。不幸的是,使用默认设置,它只能处理略小于 1TB 大小的分区。现代硬盘使这种大小的分区变得很常见。
如果分区大小为 1TB 或更大,安装程序会自动使用 FFS 版本 2(FFS2)格式化它。在第八章中,我们将介绍如何调整你的文件系统以完全满足你的需求。
多块硬盘
磁盘输入/输出通常是计算机最慢的部分。如果你有多块硬盘,你可以使用这些硬盘来加速你的系统性能。
首先,确保每个驱动器都在自己的端口上。SCSI 和 SATA 驱动器通常每个端口一个驱动器(除非你特别使用端口多路复用器),但 IDE 驱动器通常每个端口连接两个设备。每个端口都有最大吞吐量。将两个快速驱动器连接到一个端口上是没有用的,因为驱动器会竞争一个端口的吞吐量。
通常,当您有多个驱动器时,您希望在驱动器之间分配读写活动。我通常将我提供的数据放在一个磁盘上,而将重要的系统文件放在另一个磁盘上。如果我在构建数据库服务器,我可能会将一个磁盘用于交换空间和 /var,而将所有其他分区分配给另一个磁盘。
在驱动器之间分配您的交换空间。请确保至少有一个分区的大小足够容纳您的物理 RAM,这样 OpenBSD 在需要时可以进行崩溃转储。OpenBSD 无法在两个不同的交换分区之间分割崩溃转储。
如果您是更资深的 OpenBSD 用户,您可以使用多个硬盘创建一个具有软件 RAID 的冗余磁盘。我们将在 第九章 中介绍如何做到这一点。
如果您的第二块驱动器比主系统驱动器慢得多,那么使用它就没有意义。计算机的运行速度仅与其最慢的组件相同,因此将那个旧的 IDE 驱动器添加到您的 SATA 系统中将会拖慢整个机器。这不仅会降低整个系统的性能,而且它可能比您的主驱动器更老,并且更有可能发生故障。
理解分区
作为历史偶然,i386 和 amd64 系统有两种不同类型的分区。OpenBSD 将第一种称为 MBR 分区,第二种称为 disklabel 分区(或简称 分区)。
MBR 分区
MBR 分区,也称为 主分区,是运行在 i386 硬件上的操作系统普遍理解的。每个硬盘都有四个 MBR 分区。在大多数情况下,只有一个分区分配了空间;其他三个分区的大小为零。如果您想在单个磁盘上安装多个操作系统,那么每个操作系统都需要自己的 MBR 分区。
大多数操作系统使用名为 fdisk 的程序来管理 MBR 分区。请注意,这并不是同一个程序——OpenBSD 的 fdisk(8) 与 Microsoft 的 Fdisk 不同,后者与 Linux、FreeBSD、OpenSolaris 等程序也不同。任何操作系统的 fdisk 都可以查看属于其他操作系统的 MBR 分区,尽管它们可能无法识别 MBR 分区上的内容,但它们会识别出为 某种东西 分配了空间,并会警告您不要覆盖它。不幸的是,并非所有 fdisk 程序都能很好地协同工作。不要使用另一个操作系统的工具来为某个操作系统分区磁盘.^([6])
随着廉价虚拟化的出现,在单个磁盘上安装多个操作系统已不再建议。为每个磁盘分配一个单一的 MBR 分区,使其填满整个磁盘,并将其他三个 MBR 分区的大小设为零。您可以在 第三章 中看到一个如何操作的例子。
Disklabel 分区
BSD 并非起源于 i386 硬件;它拥有自己的磁盘分区系统,该系统基于对磁盘分区的标记。当 BSD 被移植到 i386 时,磁盘标签被固定在 MBR 分区内部。当有人在 OpenBSD 中提到“分区”时,他们几乎肯定是指磁盘标签分区。
一个磁盘标签可以支持 16 个分区。如果你需要超过 16 个分区,你必须创建第二个 MBR 分区并添加更多的磁盘标签。我建议,如果你需要在单个磁盘上超过 16 个分区,你在决策过程中可能走错了方向。退一步,重新评估你想要实现的目标以及你正在如何进行。
外部操作系统无法识别 OpenBSD 磁盘标签。基于 BSD 的操作系统可能看似能理解它们,但过去 20 年中,各种基于 BSD 的系统所使用的磁盘标签格式已经发生了分歧。请仅使用 OpenBSD 磁盘工具来管理 OpenBSD 分区。
理解磁盘标签
OpenBSD 安装程序期望你理解磁盘标签。你可以通过盲目接受 OpenBSD 提供的默认分区来避免学习磁盘标签,但这不会让你走得很远。磁盘标签可能看起来令新用户感到害怕,并需要一些基本的数学知识,但一旦你慢慢走过它们,它们并不那么困难。你需要首先理解磁盘几何形状。
扇区和谎言
从前,磁盘驱动器有明确定义的几何形状。每个磁盘实际上都是圆形的,并在硬盘内部旋转。制造商将每个磁盘分成许多小部分,称为 扇区。每个扇区都有一个编号,扇区 0 位于磁盘的起始位置,扇区按顺序编号直到磁盘的末端。扇区被聚集到一起形成 磁道。磁道堆叠在一起形成 柱面。每个磁盘驱动器都有一定数量的 磁头——数据读取设备,当磁盘在它们下方旋转时,从磁盘读取信息。整体而言,扇区、磁道和柱面描述了磁盘的 几何形状。
这听起来似乎很简单,但今天你实际上不能指望磁盘扇区能映射到任何有用的东西。多年来,硬盘制造商和操作系统都设定并打破了限制。这适用于机器设计的各个方面,从 640KB 的内存限制到 504MB 的磁盘限制。硬盘制造商通过欺骗系统 BIOS 和/或操作系统来避免这些限制。
如果您是制造每磁道 126 个扇区的硬盘驱动器的硬盘制造商,但最流行的操作系统只能接受每磁道 63 个扇区,您就遇到了问题。简单的解决方案是教会您的硬盘说谎。如果您声称每磁道扇区数量减半但盘片数量加倍,数字仍然相加,您仍然可以提供唯一的扇区编号。每个硬盘制造商都会选择以略微不同的方式说谎。最明显的例子是闪存驱动器(即使它们不是圆形且不旋转,它们仍然报告磁道、扇区和磁头^([7]))和硬件 RAID(它报告有关多个磁盘的信息,就像它们是一个一样)。如果您了解硬盘的历史,您会发现各种有趣的谎言。
当磁盘几何信息到达操作系统时,它已经经过一次或多次转换。在您的脑海中寻找写着“接受你所被告知的内容”的按钮,并在重复以下内容时按下它:磁盘被划分为顺序编号的扇区。分区填充一定数量的连续扇区。扇区根据驱动器中的磁头数量分组到磁道中。分区在磁道边界处结束。
扇区和磁盘标签
安装程序将显示您的磁盘标签。(您也可以在系统安装并运行后查看磁盘标签,如第八章磁盘和文件系统中所述。)
我们首先查看磁盘的物理信息。虽然物理信息通常不会直接影响安装,但如果出现问题,您需要知道如何读取它。
**1** # /dev/rsd0c:
**2** type: SCSI
**3** disk: SCSI disk
**4** label: DSA2CW120G3
**5** duid: adb697598fa0a010
flags:
**6** bytes/sector: 512
sectors/track: 63
tracks/cylinder: 255
sectors/cylinder: 16065
cylinders: 14593
**7** total sectors: 234441648
**8** boundstart: 64
**9** boundend: 234436545
drivedata: 0
除了设备唯一标识符(DUID)外,您不能更改这些条目而不更改底层硬件。
第一条是设备名称,/dev/rsd0c 1。前面的/dev表示这是一个设备节点。rsd0c是磁盘名称。sd表示该驱动器使用sd(4)设备驱动程序,而0表示这是 OpenBSD 找到并附加的第一个驱动器。(这通常是,但不总是 BIOS 驱动器 0。)前面的r表示我们以原始模式访问磁盘,而尾部的c表示我们正在检查磁盘标签分区c。磁盘标签分区c始终与包含此磁盘标签的整个 MBR 分区相匹配。几乎所有不是明确 IDE 的磁盘都可能显示为 SCSI 磁盘。
type 2 是一个描述磁盘物理接口的一般标签。任何 IDE 磁盘都会显示为 ESDI(增强型小型设备接口),而 SCSI、SAS、SATA 和几乎所有其他类型的磁盘类型都是 SCSI。
disk 字段 3 显示连接到此接口的磁盘类型。在这里,它显示为 SCSI 磁盘,但我们已经从类型中知道了这一点。
label 4 显示制造商的名称和/或驱动器型号。在虚拟化服务器的案例中,这显示为虚拟驱动器或类似内容。
duid 5 是该磁盘的 DUID。如果你曾经管理过包含两个以上磁盘的系统,你就会知道混淆磁盘是多么容易的事情。硬件 BIOS 通过磁盘连接的物理端口来识别磁盘。如果你需要更换 SATA 或 SCSI 卡,并且在重新运行电缆时将磁盘搞混了,你将很难再次找到你的启动盘。通过在你的系统配置中使用 DUID 而不是 BIOS 分配的设备名称,你将始终使用相同的目的磁盘。如前所述,DUID 是磁盘标签信息顶部可编辑的字段。
每扇区字节数、每磁道扇区数、每柱面磁道数和每柱面扇区数 6 都描述了磁盘的几何形状。这些数字都是假的,但磁盘上的总扇区数 7 是 准确的。你还可以看到你可以用 disklabel 分区填充的第一个扇区 8,以及你可以使用的最后一个扇区 9。(由于硬盘的几何变换,你会丢失一些扇区。不要试图追究硬件的责任。你无法赢得那个争论。)
下一个部分显示了 disklabel 分区,你可以根据需要修改它。以下是我桌面上的一段 disklabel:
16 partitions:
# **1** size **2** offset **3**fstype **4**[fsize bsize cpg]
**5** a: 2097121 64 4.2BSD 2048 16384 1 # /
**6** b: 4698424 2097185 swap
**7** c: 312581808 0 unused
d: 8388576 6795617 4.2BSD 2048 16384 1 # /tmp
e: 16736864 15184193 4.2BSD 2048 16384 1 # /var
f: 4194304 31921057 4.2BSD 2048 16384 1 # /usr
g: 2097152 36115361 4.2BSD 2048 16384 1 # /usr/X11R6
h: 20971520 38212513 4.2BSD 2048 16384 1 # /usr/local
i: 4194304 59184033 4.2BSD 2048 16384 1 # /usr/src
j: 4194304 63378337 4.2BSD 2048 16384 1 # /usr/obj
k: 245003968 67572641 4.2BSD 2048 16384 1 # /home
这个磁盘标签声明它有 16 个分区,但只列出了 11 个。磁盘标签有 16 个分区的空间,但就像 MBR 分区表一样,并不是所有的分区都有分配空间。与 Unix-like 操作系统中的大多数配置文件一样,一个井号(#)表示注释的开始。这里的注释给出了上面表格的标题。
第一列是分区字母。一个独特的字母标识每个 disklabel 分区。在我们例子中的第一个分区是 a 5,第二个是 b 6,第三个是 c 7,以此类推。
size 1 是驱动器使用的扇区数。在这个例子中,分区 a 占用了 2097121 个扇区,分区 b 占用了 4698424 个扇区,分区 c 占用了 312581808 个扇区。
offset 2 是从 MBR 分区开始到 disklabel 分区开始的扇区数。如果一个磁盘是可引导的,它有一个主引导记录(MBR)标记它为可引导。MBR 记录占据了前 63 个磁盘扇区,编号从 0 到 62。第一个可用于 disklabel 分区的扇区是扇区号 63。分区 a 从扇区 64 开始,以便正确对齐固态磁盘中的内存单元。
看一下分区 b。它有一个偏移量 2097185,这意味着它从扇区 2097185 开始。我们如何到达那里?嗯,分区 a 从扇区 64 开始,大小为 2097121。2097121+64=2097185,或者分区 a 结束后的第一个空闲扇区。这似乎非常合理,直到你看到分区 c。Disklabel 分区 c 是神奇的。在每一个 disklabel 分区中,c 代表整个磁盘。它有一个偏移量为 0,大小等于磁盘上的扇区数。你无法在分区 c 上放置文件系统;它只用于参考。分区 d 从分区 b 的位置继续。
fstype 3 标记了该分区上的文件系统类型。OpenBSD 文件系统,如分区 a,被标记为 4.2BSD。(请注意,OpenBSD 文件系统与 BSD 4.2 的文件系统不再完全相同。)分区 b 是交换空间。
接下来的两列 4 展示了该分区文件系统的碎片化行为。这些值是在将文件系统放置到分区时由文件系统创建工具设置的,不应手动更改。如果你好奇,可以阅读 newfs(8) 及其相关的 man 页面。fsize 是分区上任何文件碎片的大小。b 是磁盘上块的大小,以字节为单位。我们将在 第八章 中讨论 FFS 碎片化。你真正需要知道的是,FFS 和 FFS2 都具有很强的抗碎片化能力,并且都不需要任何类型的碎片整理过程。
最后一列显示了每个柱面组中柱面的数量。对于现代磁盘来说,这几乎总是 1。
有趣的是,disklabel 可以被认为是格式化磁盘的配置文件。你可以将这个 disklabel 保存到文件中,获取一个相同的硬盘,将这个标签写入新硬盘,并完美地复制旧硬盘上的分区。
如果你在分区时感到困惑,请打印出当前的 disklabel 并将其与您希望系统看起来的方式进行比较。
其他信息
如果这台机器将要连接到互联网,在开始之前你必须知道其网络配置。如果你的网络有 DHCP,那么你一切就绪。如果没有,你需要一个有效的 IP 地址、子网掩码、默认网关和名称服务器 IP 地址。
提前决定这台机器是否将运行 X Window 系统。通常,桌面运行 X,而服务器则不运行。
到目前为止,你已经拥有了在 i386 或 amd64 硬件上安装 OpenBSD 所需的所有背景知识。拿出你的设备,让我们开始吧。
^([5]) 是的,那是兆字节——你知道,千兆字节以下的单位。是的,兆字节可以应用于磁盘。
^([6]) 我从 OpenBSD 开发者那里得到保证,任何 fdisk 都足以用于任何操作系统。由于反复被有缺陷的 fdisk 程序折磨,我发现自己无法给你完全的自由去尝试这个。
^([7]) 是的,你可以让 U 盘旋转。但一个以 5400 RPM 旋转的 U 盘会有一系列超出常规系统管理范围的问题。
第三章。安装指南
简单直接的问题。
您会接受默认提示吗?
在选择之前先思考。
凭借您的 OpenBSD 软件和具有支持硬件的电脑,您现在可以开始实际安装了。本章将指导您通过 CD 和 FTP 在 amd64 和 i386 系统上完成完整安装,从 CD 或软盘启动。
在本章中,我假设您要将您的电脑用于 OpenBSD。当然,您可以在一台电脑上安装多个操作系统,但这是一种不太常见的用例。如果您想在电脑上安装多个操作系统,请遵循 OpenBSD FAQ 中的说明。(在单台电脑上安装多个操作系统时,很容易不小心损坏其中一个操作系统,因此请谨慎操作。)
在开始安装 OpenBSD 之前,请确保您的机器上的数据已备份!当您将机器用于 OpenBSD 时,您将覆盖整个硬盘。
硬件设置
在开始之前,请确认 OpenBSD 支持您的硬件。您可以在 OpenBSD 网站的平台特定页面上找到 OpenBSD 最新版本的硬件支持列表(www.OpenBSD.org/i386.html 用于 i386 和 www.OpenBSD.org/amd64.html 用于 amd64),列出由 OpenBSD 团队验证可以正常工作的硬件。
如果您发现您的硬件没有列出,它仍然可能运行 OpenBSD。事实上,许多不受支持的硬件可以完美运行 OpenBSD,但并非所有硬件都经过测试,这仅仅是因为 OpenBSD 团队无法访问所有曾经制造的硬件。如果您对某个特定设备感到担忧,请搜索邮件列表存档以查看它是否受支持。
硬件兼容性列表通常通过芯片组来识别设备,而不是通过供应商或型号。芯片组是实际的硬件名称,而不是型号名称,这可能会造成一些混淆,因为毕竟,当你购买一台电脑时,网卡通常被列为“千兆以太网”,而不是“英特尔 PRO/1000MT 双端口服务器适配器型号 PWLA8492MT”。更糟糕的是,许多供应商在单独的品牌或型号名称下使用相同的硬件,或者在相同的品牌或型号名称下使用不同的硬件。例如,Linksys 在 EtherLink 型号名称下销售了许多不同的网卡型号。(幸运的是,这个问题主要适用于低端市场,而 OpenBSD 几乎总是支持这些较旧的芯片组。)
即使您不确定您的硬件是否受支持,您仍然可以尝试安装 OpenBSD 来查看会发生什么。引导信息将提供有关您硬件的大量信息。
BIOS 配置
在安装 OpenBSD 之前,务必评估你的系统基本输入/输出系统(BIOS)。由于每个 BIOS 都不同,我无法提供配置你的 BIOS 的确切说明。你最好的选择是查阅你的主板手册或互联网。
此外,如果你的 BIOS 需要更新,请在安装 OpenBSD 之前处理这个问题。最后,检查启动设备顺序,并确保它符合你计划安装系统的计划。
在设置好硬件后,获取启动媒体。
制作启动媒体
我们将介绍如何从 CD 或软盘启动 OpenBSD 安装程序。通常,通过 CD 启动更可取,因为所有 amd64 系统都可以从 CD 启动,大多数功能正常的 i386 系统也是如此。我们将首先制作用于旧 i386 硬件安装的软盘,然后转向 CD。虽然从 USB 和虚拟系统安装是可能的,但都不受支持。我们将在本书的 第二十三章 中稍后介绍这两种安装类型。
制作启动软盘
只有当你的硬件不能从 CD 启动,或者你有软盘但没有 CD 驱动器时,你才需要制作启动软盘。OpenBSD 启动软盘包含 OpenBSD 的一个非常有限的子集——仅足够识别你的硬件、格式化你的磁盘以及下载和提取文件集。除了软盘本身,你还需要通过以太网建立有效的互联网连接。8 因为完整的内核大小超过了一张软盘的容量,OpenBSD 为 i386 硬件提供了三个软盘镜像,每个镜像针对特定类型的硬件。每个镜像名称都包含版本号。例如,版本 5.3 的软盘镜像命名为 floppy53.fs、floppyB53.fs 和 floppyC53.fs。下载与你的系统最接近的镜像,如下所示:
-
floppyXX.fs. 这是最常见的 i386 硬件的镜像。它可以启动普通工作站或低端服务器。
-
floppyBXX.fs. 这个镜像包含千兆以太网卡、SCSI 和 RAID 的驱动程序。它适用于高端 i386 服务器。
-
floppyCXX.fs. 这个镜像支持 PCMCIA 和 CardBus。它适用于笔记本电脑。
OpenBSD 为 amd64 硬件只提供了一个软盘镜像:floppyXX.fs。(amd64 平台没有携带 20 年的遗留驱动程序作为负担,所以所有内容都适合在一个磁盘上。)请确保使用 amd64 目录中找到的软盘镜像。amd64 镜像使用与标准 i386 软盘相同的名称。
一旦你有了合适的镜像文件,你必须将其复制到软盘上。你不能使用基本的文件系统级别的复制,例如 Windows 的拖放,因为镜像文件不仅包含文件,还包含一个文件系统。你必须使用适当的工具将镜像复制到软盘。
在 Unix-like 系统上创建软盘
如果你已经在运行类 Unix 系统,使用dd(1)创建你的软盘。你需要知道你的软盘驱动器的设备名称,可能是/dev/fd0、/dev/floppy、/dev/rfd0或/dev/rsd0(用于 USB 软盘驱动器)。一旦你知道设备名称,告诉dd使用如下命令将镜像复制到该磁盘设备:
# dd if=*filename* of=*full-path-to-floppy-device*
例如,要使用软盘设备名称/dev/fd0c从镜像floppyB52.fs创建磁盘,请输入以下内容:
# **dd if=floppyB52.fs of=/dev/fd0c**
如果dd立即报错或静默退出而没有写入软盘,请尝试指定不同的软盘设备。
在 Microsoft 系统上创建软盘
如果你需要在 Windows NT 的衍生系统(包括所有现代 Windows 桌面操作系统)上创建软盘,你需要一个镜像写入程序。在你的 OpenBSD 发布版本的tools目录中,你会找到一个名为ntrw.exe的程序。这个程序将磁盘镜像复制到磁盘上。下载程序,打开命令提示符,导航到包含ntrw.exe的文件夹,将空白软盘放入驱动器,并运行以下命令:
C:> **ntrw floppyB53.fs a:**
如果你遇到权限错误,你可能需要以管理员身份运行你的命令提示符。如果命令仍然失败,很可能是你使用了 15 年前藏在抽屉里的坏软盘。尝试另一个。
制作启动光盘
OpenBSD 为 i386 提供三个 ISO 镜像,为 amd64 提供两个,如下所示:
-
cdXX.iso。这个镜像包含内核和安装程序,但没有文件集。它用于启动系统到安装程序可以运行的最小状态。一旦系统启动,它将通过网络获取文件集。
-
installXX.iso。这个镜像包含cdXX.iso镜像中的所有内容,以及文件集。使用它来在多个系统上安装这个版本的 OpenBSD。
-
cdemuXX.iso。一些较旧的 i386 系统有一个 BIOS,它使光盘驱动器模拟软盘驱动器。如果你有这种系统,请使用cdemuXX.iso。如果你不确定是否需要此镜像,你不需要。如果你曾经拥有过这种光盘驱动器,你现在可能已经更换了它。如果你还没有,也许你应该考虑更换。
注意
记住,你可以通过购买官方 CD 集来节省选择 ISO 的麻烦,这样可以直接使用,并且还会包含预编译的软件包。
将 ISO 镜像放到物理磁盘上的过程因操作系统而异。在 Microsoft Windows 系统上,右键单击 ISO 并选择刻录到光盘。类 Unix 系统使用几个不同的程序,如burncd和cdrecord。不同的 Linux 版本在它们的桌面环境中集成了无数的 ISO 刻录前端。在网上查找有关在你的特定操作系统上刻录光盘的说明。
安装 OpenBSD
一旦从你选择的媒体启动,你应该会看到如下内容:
> OpenBSD/amd64 BOOT 3.18
boot>
如果您需要出于任何原因中断引导过程,您可以在这一点上做到。我们将在第五章中讨论如何中断引导过程,并在整本书中讨论这样做的原因。
如果您等待五秒钟,OpenBSD 应该启动。内核将自我介绍并开始识别您的硬件。
booting **1**cd0a:/5.3/amd64/bsd.rd: 2986868+913996+2861496+0+504624 [89+318288+205653]=0xb6f578
entry point at 0x1001e0 [7205c766, 34000004, 24448b12, 1608a304]
Copyright (c) 1982, 1986, 1989, 1991, 1993
The Regents of the University of California. All rights reserved.
Copyright (c) 1995-2012 OpenBSD. All rights reserved. http://www.OpenBSD.org
**2** OpenBSD 5.3 (RAMDISK_CD) #23: Sun Feb 12 09:45:07 MST 2012
deraadt@amd64.openbsd.org:/usr/src/sys/arch/amd64/compile/RAMDISK_CD
real mem = 1072627712 (1022MB)
avail mem = 1032290304 (984MB)
…
在这个输出中,您可以从1处看到系统是从哪个设备引导的——在本例中是 CD 驱动器 0。接下来,您看到版权信息,然后是您的内核是在哪个目录下编译的2。您可以看到这是一个 OpenBSD 快照内核,由用户deraadt在主机amd64.openbsd.org上编译。
在这一点上,OpenBSD 应该探测您的硬件,并在附加设备驱动程序时显示结果。
运行安装程序
一旦引导消息通过,您应该看到以下文本:
Welcome to the OpenBSD/amd64 5.3 installation program.
(I)nstall, (U)pgrade or (S)hell? **i**
如您所见,有三个选项:安装、升级和 Shell。OpenBSD 安装程序是一个 shell 脚本,它调用程序下载文件、格式化磁盘以及准备您的系统。它可能看起来不怎么样,但它非常快,在受过教育的人手中,它非常强大。
Shell 选项将您带入 OpenBSD 命令行,在那里您可以访问安装磁盘上的命令。这些最小命令可能足以修复损坏的系统。我们将在第二十章中检查升级选项。
输入i选择安装。您应该看到一条欢迎信息和一些基本说明:
At any prompt except password prompts you can escape to a shell by
typing '!'. Default answers are shown in []'s and are selected by
pressing RETURN. You can exit this program at any time by pressing
Control-C, but this can leave your system in an inconsistent state.
**1** Terminal type? [vt220]
**2** System hostname? (short form, e.g. 'foo') **caddis**
安装程序在方括号中显示默认答案。要使用默认值,只需按回车键。
如果您的系统有一个标准的键盘和显示器,OpenBSD 将使用它作为标准的 VT220 终端,如1所示。如果您有一个连接到系统的非标准终端,您可能是一位老手,确切地知道它是哪种终端类型。如果您是一位使用一些古老的、未知的、满是灰尘的终端的年轻人,这种终端是在一个废弃的烟花工厂后面的废弃实验室里找到的,因为您认为它会很酷,现在就停止并获取一个标准的显示器和键盘。虽然 OpenBSD 可能支持那个古老的控制台,但这不是尝试它的时候。
接下来,安装程序应该提示您输入系统的短主机名2,这将是一个单词,用于标识您的系统。这台特定的计算机命名为caddis;您可以将其命名为您喜欢的任何名字。
现在来配置网络:
**1** Available network interfaces are: em0 em1 vlan0.
**2** Which one do you wish to configure? (or 'done') [em0]
**3** IPv4 address for em0? (or 'dhcp' or 'none') [dhcp] **192.0.2.85**
**4** Netmask? [255.255.255.0] **255.255.255.128**
**5** IPv6 address for em0? (or 'rtsol' or 'none') [none]
Available network interfaces are: em0 em1 vlan0.
**6** Which one do you wish to configure? (or 'done') [done]
**7** Default IPv4 route? (IPv4 address, 'dhcp' or 'none') **192.0.2.1**
add net default: gateway 192.0.2.1
**8** DNS domain name? (e.g. 'bar.com') [my.domain] **blackhelicopters.org**
**9** DNS nameservers? (IP address list or 'none') [none] **192.0.2.2 192.0.2.10**
在1处,安装程序列出了它在您的机器上识别的网络接口。它找到了三个:em0、em1和vlan0。前两个,em0和em1,是网卡。我在2处选择了em0,这是安装程序的默认选项,按回车键。如果在可能的情况下,避免在安装期间配置虚拟局域网(VLAN),尤其是在您的第一次安装时。如果您需要 VLAN 来连接到互联网,请参阅第十二章。
当被问到是否想要提供一个静态 IP 地址时,你可以通过按回车键选择使用 DHCP。我选择输入一个静态地址,因为我将使用这台机器作为服务器。(如果你不需要静态地址,你可以让 DHCP 自动为你分配一个 IP 地址。)
当使用静态地址时,你必须在4处输入一个子网掩码,并且(如果需要)在5处输入一个 IPv6 地址。现在,配置了一张网络卡后,OpenBSD 会在6处询问你是否已完成网络配置。如果你想让安装程序引导你配置第二张网络卡,你应该输入em1而不是接受默认的done。
如果你分配了一个静态 IP 地址,那么如果你想访问互联网,你必须配置一个静态路由,正如7所示。同样,你需要告诉你的主机它的域名在8处,以及至少一个名称服务器的 IP 地址在9处。
在这个阶段,你应该已经连接到本地网络。如果你无法访问网络,可能是因为输入有误。至少,你可以使用感叹号(!)来中断安装并获取一个 shell 提示符。(第十二章 [em0] !
Type 'exit' to return to install.
2 # ifconfig
lo0: flags=8008<LOOPBACK,MULTICAST> mtu 33152
em0: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:0c:29:aa:09:21
3 media: Ethernet autoselect (1000baseT full-duplex,master)
status: unknown
em1: flags=8802<BROADCAST,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:0c:29:aa:09:2b
4 media: Ethernet autoselect (none)
status: unknown
vlan0: flags=0<> mtu 1500
lladdr 00:00:00:00:00:00
而不是选择一个接口,通过输入感叹号(`!`)在**1**处逃逸到命令提示符。然后通过运行`ifconfig`来让 OpenBSD 在**2**处告诉你它的网络接口。你可以在输出中看到接口`em0`和`em1`。当`em0`在**3**处报告它正在全双工模式下运行 1000baseT 时,在**4**处你可以看到`em1`的媒体类型为`none`。接口`em0`已连接,所以这就是我想配置的接口。输入**`exit`**返回到安装程序,并继续配置卡`em0`。
### 设置服务和第一个用户
安装程序现在应该会要求你配置一些基本系统参数:
1 Password for root account? (will not echo)
Password for root account? (again)
2 Start sshd(8) by default? [yes]
3 Start ntpd(8) by default? [no] yes
NTP server? (hostname or 'default') [default]
4 Do you expect to run the X Window System? [yes]
5 Do you want the X Window System to be started by xdm(1)? [no]
6 Change the default console to com0? [no]
在**1**处,输入两次 root 密码。如果密码不匹配,安装程序会要求你重新输入,直到它们匹配为止。
您可以在**2**处启用安全壳(SSH)守护进程,以便您可以在安装后立即远程连接到这台机器。如果您启用了 SSH 但在安装过程中稍后没有创建用户,您可以用 root 身份 SSH 到这台机器。当使用密码认证时,这是一个非常糟糕的想法,会让入侵者更容易破坏您的服务器。如果您在这里启用了`sshd`,请务必在安装过程中创建用户!如果您不这样做,至少在安装 OpenBSD 后立即通过 root 账户禁用 SSH 登录,如第四章中所述。
在网络中,正确的时间设置很重要。我通常在安装过程中启用网络时间协议(NTP)守护进程`ntpd(8)`,如**3**处所示。OpenBSD 默认选择一组公开可访问的时间服务器,但如果有可用的本地时间服务器,您可以指定它。
现在告诉安装程序**4**,您是否打算运行 X 窗口。X 需要软件被允许有相当广泛的权限进入内核。如果安装程序检测到图形控制台,它默认允许 X。如果您不需要图形控制台,禁用 X 访问。
如果您正在运行 X,您可能还想要 X 显示管理器`xdm(1)`。在**5**处,告诉安装程序您是否想要`xdm`。默认情况下,OpenBSD 在启动时不会启动`xdm`;您通常最好在系统上安装 OpenBSD 而不是配置 X,所以我这里接受了`no`的默认值。
如果您想使用串行端口作为控制台,您可以在安装过程中**6**处设置。我将在第五章中讨论串行控制台。
### 注意
对于基本系统参数,我除了一个之外都使用了默认值。启用时间服务当然不是强制性的——我可以在安装后轻松启用`ntpd`。我也可以告诉安装程序禁用 X,但我也可以在安装后更改这一点。
现在来设置您的第一个用户。
Setup a user? (enter a lower-case loginname, or 'no') [no] mwlucas
Full user name for mwlucas? [mwlucas] Michael W Lucas
Password for mwlucas account? (will not echo)
Password for mwlucas account? (again)
Since you set up a user, disable sshd(8) logins to root? [yes]
我的常用用户账户名是`mwlucas`。在这里,我输入这个用户名,以及一个真实姓名条目。安装程序创建此账户并授予它使用 root 密码的权限(见第六章)。您应该会两次被提示输入用户的密码。
### 注意
您有机会禁用通过 SSH 的 root 登录。使用这个默认值。root 账户永远不应该被允许通过 SSH 登录,除非使用公钥认证,即使那样,这些登录也应该受到限制。为了避免通过 SSH 进行 root 登录的原因,请在互联网上搜索“Hail Mary Cloud”。
### 设置时区
在安装过程中设置您的时区。如果您在安装 OpenBSD 时可以访问互联网,安装程序应尝试确定您的时区。OpenBSD 假定 BIOS 时钟设置为协调世界时(UTC)。如果 BIOS 时钟设置为其他时区,您需要在安装后纠正系统时间。
我在密歇根州的底特律。如果您熟悉美国地理,您可能会认为我需要美国东部时间,但我的州有自己的时区。
1 What timezone are you in? ('?' for list) [US/Eastern] ?
Africa/ Chile/ GB-Eire Israel NZ-CHAT UCT
America/ Cuba GMT Jamaica Navajo US/
Antarctica/ EET GMT+0 Japan PRC UTC
Arctic/ EST GMT-0 Kwajalein PST8PDT Universal
Asia/ EST5EDT GMT0 Libya Pacific/ W-SU
Atlantic/ Egypt Greenwich MET Poland WET
Australia/ Eire HST MST Portugal Zulu
Brazil/ Etc/ Hongkong MST7MDT ROC posix/
CET Europe/ Iceland Mexico/ ROK posixrules
CST6CDT Factory Indian/ Mideast/ Singapore right/
Canada/ GB Iran NZ Turkey
2 What timezone are you in? ('?' for list) [US/Eastern] US
3 What sub-timezone of 'US' are you in? ('?' for list) ?
Alaska Central Hawaii Mountain Samoa
Aleutian East-Indiana Indiana-Starke Pacific
Arizona Eastern Michigan Pacific-New
4 What timezone are you in? ('?' for list) [US/Eastern] US/Michigan
我记不清确切的时间区域,但我知道它不是普通的美国东部时间。我在**1**处输入一个问号(**`?`**)来查看可用的选项。我认不出**2**处列出的任何时区是我所在城市的正确时区,但我知道我处于美国时区,所以我输入**`US`**。我不知道我的子时区选择有哪些,所以我在**3**处输入一个问号(**`?`**)来查看美国的时区。那里有密歇根州!在**4**处,我输入了完整的时间区域名称.^([9])
### 设置磁盘
如前所述,在专用安装中,安装程序会擦除驱动器上的所有数据。与其他大多数操作系统安装程序不同,OpenBSD 安装程序不会警告您这一点;它假设您了解重新分区硬盘的后果。
对于这次首次安装,我们将使用 OpenBSD 的默认分区方案。(我们将在本章后面讨论自定义分区。)我们的演示服务器只有一个磁盘。我们首先将在该磁盘上创建一个 MBR 分区,然后添加 OpenBSD 分区。
Available disks are: sd0.
Which one is the root disk? (or 'done') [sd0]
Use DUIDs rather than device names in fstab? [yes]
安装程序告诉我们它看到一个磁盘,设备`sd0`。安装程序必须知道哪个磁盘将包含根分区。(只有一个磁盘时这似乎是多余的,但如果您的系统有多个磁盘,这就会变得很重要,我们将在自定义磁盘布局中讨论的例子中看到。)当您只有一个磁盘时,OpenBSD 假设您会使用它。它还询问您是否想在使用文件系统表时使用磁盘的 DUID 而不是设备名称。正如我们将在第八章中讨论的,对此问题总是回答**`yes`**。
安装程序现在将显示 MBR 分区表。
Disk: sd0 geometry: 6527/ 255/ 63 [ 104857600 Sectors]
Offset: 0 Signature: 0xAA55
Starting Ending LBA Info:
: id C H S - C H S [ start: size ]
0: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
1: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
2: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
3: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
Use (W)hole disk, use the (O)penBSD area, or (E)dit the MBR? [whole]
Setting OpenBSD MBR partition to whole sd0…done.
第一行显示了检测到的硬盘几何形状。这个特定的驱动器有 6527 个磁柱,255 个磁头,每个磁柱 63 个扇区。如果您将其与物理驱动器上的标签进行比较,几乎肯定不会匹配(因为硬盘会撒谎)。但请注意,这个翻译后的几何形状与硬盘文档中显示的扇区数量完全相同。
在此线下,您可以看到现有的 MBR 分区表。所有分区都被清零,这意味着该驱动器没有分区。我们只想在这台机器上安装 OpenBSD,所以选择默认设置,让 OpenBSD 占用整个驱动器。
现在是考虑您的 OpenBSD 分区的时候了。
The auto-allocated layout for sd0 is:
# size offset fstype [fsize bsize cpg]
1 a: 1.0G 64 4.2BSD 2048 16384 1 # /
b: 1.2G 2097216 swap
c: 50.0G 0 unused
d: 3.6G 4716480 4.2BSD 2048 16384 1 # /tmp
e: 5.7G 12176320 4.2BSD 2048 16384 1 # /var
f: 2.0G 24063040 4.2BSD 2048 16384 1 # /usr
g: 1.0G 28257344 4.2BSD 2048 16384 1 # /usr/X11R6
h: 6.3G 30354496 4.2BSD 2048 16384 1 # /usr/local
i: 1.9G 43566400 4.2BSD 2048 16384 1 # /usr/src
j: 2.0G 47467072 4.2BSD 2048 16384 1 # /usr/obj
k: 25.4G 51661376 4.2BSD 2048 16384 1 # /home
2 Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout? [a]
3 /dev/rsd0a: 1024.0MB in 2097152 sectors of 512 bytes
6 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
…
我们的第一分区在**1**处是`a`,占用 1GB,将用作根分区(*/)。在安装的系统上,这将被称为分区`sd0a`。向下查看列表,查看第二章中讨论的所有标准分区。
我们可以在此时进行自定义磁盘分区,但为了我们的第一次安装,我们将使用默认设置,如**2**所示。然后安装程序应该在所有分区上创建文件系统。
### 选择文件集
现在你已经分配了磁盘空间,让我们将操作系统放到磁盘上。安装程序首先会询问一些基本问题,关于如何获取这些文件集。
Let's install the sets!
Location of sets? (cd disk ftp http or 'done') [cd] 1 ftp
HTTP/FTP proxy URL? (e.g. 'http://proxy:8080', or 'none') [none]
Server? (hostname, list#, 'done' or '?') [ftp5.usa.openbsd.org] 2 ftp.lambdaserver.com
Server directory? [pub/OpenBSD/5.3/amd64]
Login? [anonymous]
虽然我是从光盘启动这个系统的,但我将通过**1** FTP 安装文件集。如果我的网络需要使用代理访问互联网,我会告诉安装程序。
虽然安装程序会为你选择一个 FTP 服务器**2**,但你也可以指定一个你知道的接近或快速的 FTP 服务器。如果你正在安装快照,请给出 FTP 服务器上所需快照的文件路径。最后,如果这个 FTP 服务器需要用户名和密码,请在此处输入。
到这一步,安装程序应该登录到 FTP 服务器,找到所有可用的文件集,并供你批准。
Select sets by entering a set name, a file name pattern or 'all'. De-select
sets by prepending a '-' to the set name, name pattern or 'all'. Selected
sets are labelled '[X]'.
[X] bsd [X] etc53.tgz [X] xbase53.tgz [X] xserv53.tgz
[X] bsd.rd [X] comp53.tgz [X] xetc53.tgz
[X] bsd.mp [X] man53.tgz [X] xshare53.tgz
[X] base53.tgz [X] game53.tgz [X] xfont53.tgz
Set name(s)? (or 'abort' or 'done') [done]
我建议你安装所有内容,但你也可以选择删除一个或多个文件集。
例如,假设你正在构建一个防火墙机器。传统的防火墙通常没有编译器、文档或 X。你可以通过输入一个减号(`-`)和文件集的名称来删除文件集。
Set name(s)? (or 'abort' or 'done') [done] 1 -comp53.tgz -man53.tgz
[X] bsd [X] etc53.tgz [X] xbase53.tgz [X] xserv53.tgz
[X] bsd.rd [ ] comp53.tgz [X] xetc53.tgz
[X] bsd.mp [ ] man53.tgz [X] xshare53.tgz
[X] base53.tgz [X] game53.tgz [X] xfont53.tgz
Set name(s)? (or 'abort' or 'done') [done]
此示例在**1**处删除了编译器和手册文件集。你可以看到它们不再出现在文件集列表中。
你在选择文件集时也可以使用通配符。例如,以下是删除所有以`x`开头的文件集的方法:
Set name(s)? (or 'abort' or 'done') [done] -x*
[X] bsd [X] etc53.tgz [ ] xbase53.tgz [ ] xserv53.tgz
[X] bsd.rd [ ] comp53.tgz [ ] xetc53.tgz
[X] bsd.mp [ ] man53.tgz [ ] xshare53.tgz
[X] base53.tgz [X] game53.tgz [ ] xfont53.tgz
Set name(s)? (or 'abort' or 'done') [done]
如果你改变了主意,你可以通过输入一个加号(`+`)和文件集名称来重新添加文件集。在这里,我通过使用通配符(`*`)添加了所有内容:
Set name(s)? (or 'abort' or 'done') [done] *****
[X] bsd [X] etc53.tgz [X] xbase53.tgz [X] xserv53.tgz
[X] bsd.rd [X] comp53.tgz [X] xetc53.tgz
[X] bsd.mp [X] man53.tgz [X] xshare53.tgz
[X] base53.tgz [X] game53.tgz [X] xfont53.tgz
Set name(s)? (or 'abort' or 'done') [done]
准备就绪后,按回车键安装默认或选定的文件集。
安装程序在硬盘上解包所有文件集后,会询问你是否还有更多文件集要安装。
Location of sets? (cd disk ftp http or 'done') [done]
如果你有任何自定义文件集,你现在可以安装它们。
### 完成安装
解包文件集后,安装程序会自行清理,并显示以下信息告知已完成:
CONGRATULATIONS! Your OpenBSD install has been successfully completed!
To boot the new system, enter 'reboot' at the command prompt.
When you login to your new system the first time, please read your mail
using the 'mail' command.
按照指示操作,输入**`reboot`**并重新启动,如果需要的话,请取出光盘。如果你对默认安装满意,现在就可以跳转到第四章。
## 自定义磁盘布局
如果你系统中有多个硬盘,或者你想要不同于默认的分区布局,你必须手动编辑你的磁盘布局。
安装程序一次分区一个磁盘,你无法轻松地在多个磁盘之间切换。要成功使用多个磁盘,请在开始安装之前确定你的分区方案,并尽可能具体地写下你希望在哪个磁盘上创建哪些分区。
我的系统有两个 50GB 的硬盘。我计划这样划分磁盘:
+ ****磁盘 1****. 1GB */*,1.2GB 交换空间,5GB */tmp*,1GB */usr/X11R6*,2GB */usr/src*,2GB */usr/obj*,其余的 */home*
+ ****磁盘 2****. 1GB */altroot*,1.2GB 交换分区,6GB */var*,10GB */usr/local*,以及其他所有 */var/postgresql*
此布局包括所有标准的 OpenBSD 分区,以及一些额外的分区:我将一些分区大小增加到安装程序生成的默认值以上,并在第二块硬盘上添加了一个额外的交换分区。OpenBSD 不包括单独的 */var/postgresql* 分区,但我添加了一个,因为我希望我的数据库数据在它自己的分区上。(我们将在第九章中讨论 */altroot* 分区。)
安装程序像往常一样运行,直到您到达磁盘部分。
Available disks are: sd0 sd1.
Which one is the root disk? (or 'done') [sd0]
默认情况下,安装程序将根分区放在第一块硬盘上,`sd0`。我将使用此磁盘作为根分区,并使用整个磁盘安装 OpenBSD。
安装程序随后会显示一个自动生成的磁盘标签分区的列表。我们不想使用这些分区;我们想从头开始创建自己的分区。
Use (A)uto layout, (E)dit auto layout, or create (C)ustom layout? [a] c
我们想要一个自定义布局,所以输入 **`c`**。
安装程序现在应该将我们带到 `disklabel(8)` 命令提示符,这里用 `>` 符号表示:
You will now create an OpenBSD disklabel inside the OpenBSD MBR
partition. The disklabel defines how OpenBSD splits up the MBR partition
into OpenBSD partitions in which filesystems and swap space are created.
You must provide each filesystem's mountpoint in this program.
The offsets used in the disklabel are ABSOLUTE, i.e. relative to the
start of the disk, NOT the start of the OpenBSD MBR partition.
Label editor (enter '?' for help at any prompt)
>
我们现在可以使用交互式磁盘标签编辑器在 MBR 分区内创建 OpenBSD 分区,如以下章节所述。
### 查看磁盘标签
`p` 命令打印分区现有的磁盘标签:
p
OpenBSD area: 64-104856255; size: 104856191; free: 32
size offset fstype [fsize bsize cpg]
a: 2104448 64 4.2BSD 2048 16384 1
b: 2506143 2104512 swap
c: 104857600 0 unused
d: 10490432 4610656 4.2BSD 2048 16384 1
…
这正是理解磁盘标签中讨论的信息。这个硬盘之前有一个 OpenBSD 安装,磁盘标签有那些旧分区。
要以兆字节为单位显示分区大小,请输入 **`p m`**:
p m
OpenBSD area: 64-104856255; size: 51199.3M; free: 0.0M
size offset fstype [fsize bsize cpg]
a: 1027.6M 64 4.2BSD 2048 16384 1
b: 1223.7M 2104512 swap
c: 51200.0M 0 unused
d: 5122.3M 4610656 4.2BSD 2048 16384 1
…
您也可以通过输入 **`p g`** 来以千兆字节为单位显示分区大小:
p g
OpenBSD area: 64-104856255; size: 50.0G; free: 0.0G
size offset fstype [fsize bsize cpg]
a: 1.0G 64 4.2BSD 2048 16384 1
b: 1.2G 2104512 swap
c: 50.0G 0 unused
d: 5.0G 4610656 4.2BSD 2048 16384 1
…
选择最适合您磁盘的计量单位。
### 删除分区
使用 `d` 命令删除分区:
d
partition to delete: [] a
就这样。告诉 `disklabel` 在此磁盘上删除一个分区,给出分区字母,它就会消失。但请注意:`disklabel` 不会要求您验证您的选择,所以请确保选择正确的分区。
### 删除现有磁盘标签
您可以手动删除所有分区,但使用 `z` 命令将现有磁盘标签清零要容易得多:
z
p
OpenBSD area: 64-104856255; size: 104856191; free: 104856191
size offset fstype [fsize bsize cpg]
c: 104857600 0 unused
在这里,我们告诉 `disklabel` 使用 **`z`** 删除分区表,然后使用 **`p`** 打印分区表。输出应该是一个空的磁盘标签,因为 `c` 磁盘标签分区代表整个 MBR 分区。我们现在可以创建我们想要的分区。
### 创建磁盘标签分区
这个第一个磁盘需要以下分区:
+ 1GB */* (根)
+ 1.2GB 交换分区
+ 5GB */tmp*
+ 1GB */usr/X11R6*
+ 2GB */usr/src*
+ 2GB */usr/obj*
+ 其他所有 */home*
默认情况下,`disklabel` 按顺序创建分区。您可以手动以任何顺序创建分区,但您需要跟踪扇区和柱面,以便确定每个分区应该开始和结束的位置。我 **强烈** 建议按顺序创建分区,并让 `disklabel` 做数学计算。
使用 `a` 命令以 */* 开始创建分区:
a
1 partition: [a]
2 offset: [64]
3 size: [104856191] 1g
4 Rounding size to cylinder (16065 sectors): 2104451
5 FS type: [4.2BSD]
6 mount point: [none] /
7 Rounding size to bsize (32 sectors): 2104448
默认情况下,在 **1** 处,`disklabel` 提供新的分区所用的下一个空闲字母。磁盘上的第一个分区是 `a`。按 ENTER 键接受它。
磁盘标签分区的偏移量是从磁盘开始处到分区开始的扇区数,而不是从 MBR 分区的开始处,即磁盘的实际开始处。磁盘的前 63 个扇区,编号 0 到 62,包含 MBR。我们可以使用扇区 63,但 OpenBSD 从扇区 64 开始,以便更好地与固态磁盘中的内存单元对齐。在 **2** 处,你可以看到 `disklabel` 提供了 64 作为默认偏移量。
**3** 处的大小是分区使用的扇区数。默认情况下,`disklabel` 提供磁盘上剩余的所有空间,但我想有一个 1GB 的根分区。我可以进行数学计算来确定千兆字节中有多少扇区,但我很懒,所以使用缩写。`disklabel` 命令识别以下缩写作为大小:
+ `b` 代表字节
+ `c` 代表圆柱体
+ `k` 代表千字节
+ `m` 代表兆字节
+ `g` 代表千兆字节
所有分区都必须以柱面边界结束,因此 `disklabel` 会计算出最近的边界,并在 **4** 处将我的根分区大小调整为匹配。我的根分区将非常接近 1GB。
**5** 处的 `FS type` 显示了该分区使用的文件系统。对于 OpenBSD 磁盘,每个数据分区都需要类型 `4.2BSD`。你的交换分区将是 `swap` 类型。
**6** 处的挂载点是你要挂载此分区的地方。默认情况下,`disklabel` 不会分配挂载点,因为它无法猜测你的需求。输入分区的挂载点。
分区必须以柱面边界结束,但应以整个块结束以用于文件系统。`disklabel` 命令 **7** 根据文件系统的标准块大小再次调整分区大小。
我们下一个分区是交换空间。
a
1 partition: [b]
2 offset: [2104512]
3 size: [102751743] 1.2g
4 Rounding size to cylinder (16065 sectors): 2506143
5 FS type: [swap]
`disklabel` 命令假设 **1** 你正在使用下一个分区字母 `b`。它自动计算偏移量 **2**,这是上一个分区之后的下一个空闲扇区。我在 **3** 处使用十进制分数来设置大小(我也可以输入 `1200m`)。**4** 处的大小被四舍五入到最近的柱面边界。最后,`disklabel` 知道分区 `b` 传统上是交换空间,因此它提供 **5** 作为默认选项。交换空间不需要挂载点,也没有块大小。
我们可以以相同的方式创建剩余的分区。创建最后一个分区 */home* 甚至更简单:
a
partition: [h]
offset: [25575456]
1 size: [79280799]
FS type: [4.2BSD]
mount point: [none] /home
Rounding size to bsize (32 sectors): 79280768
如 **1** 处所示,我们不需要跟踪剩余的磁盘空间量,因为 `disklabel` 会为我们做这件事。按 ENTER 键接受全部空间。现在是你留下磁盘空余空间的机会。
现在你已经创建了所有分区,使用 `p` 命令(如本章前面所述)打印磁盘标签以双重检查你的工作。
### 编写新的磁盘标签
当你对分区方案满意时,输入**`q`**将你的 disklabel 写入磁盘:
q
Write new label?: [y] y
/dev/rsd0a: 1027.6MB in 2104448 sectors of 512 bytes
…
`disklabel`给你最后一次改变主意的可能。一旦你写入一个新的 disklabel,恢复磁盘上的任何数据将变得极其困难,所以请确保在开始安装之前备份了该磁盘上的任何重要数据。(这是确保你没有微波你的备份的好时机。)
### 添加更多磁盘
在你分区第一个磁盘之后,安装程序会给你一个机会来分区任何其他硬盘:
Available disks are: sd1.
Which one do you wish to initialize? (or 'done') [done] sd1
默认情况下不会分区任何其他磁盘。如果你选择另一个磁盘,你需要创建 MBR 分区然后是 disklabel 分区。
一旦所有硬盘都格式化完毕,你将返回到安装文件集。
## 高级 Disklabel 命令
虽然基本命令应该足以分区你的磁盘,但`disklabel`支持各种高级命令。我们现在将看看其中的一些。
### 更改基本驱动器参数
记得在 disklabel 顶部显示驱动器基本物理特性的所有那些东西吗?你可以更改所有这些,但几乎永远没有必要。事实上,如果你认为这样做是解决问题的好方法,你可能走错了路。
如果你输入`e`,`disklabel`会引导你通过 disklabel 上部的每个条目。现有值作为默认值提供,允许你快速浏览变量直到你到达想要更改的那个:
e
Changing device parameters for /dev/rsd2c:
disk type: [SCSI]
label name: [Samsung HVX8812]
sectors/track: [63]
…
在自己的风险下编辑这些信息,因为通过修改它,你可能使你的磁盘无法启动或分区无法使用!更改驱动器的物理描述意味着你在向你的电脑撒谎,而电脑在你对其硬件撒谎时会变得非常激动。
### 修改现有分区
`m`命令用于修改现有分区。`disklabel`工具会引导你通过创建磁盘时输入的每个值,将原始值作为默认值提供,并允许你更改它们。但大多数时候,直接删除分区然后重新创建它更容易。
### 进入专家模式
专家模式为高级用户提供访问`disklabel`中一些很少使用的选项。大多数人不需要这些,并且觉得它们只是杂乱无章。(`disklabel`本身已经足够复杂了。)
要访问专家模式,使用`X`命令。你不会立即看到所有可用的选项,但输入其他命令会产生更多选项和更多输出。
### 获取更多帮助
你可以在`disklabel`提示符下输入一个单个问号(`?`)来获取所有可用命令的简要列表。如果你需要更详细的帮助,`M`命令显示`disklabel(8)`手册页。
你现在已经安装了 OpenBSD。让我们看看下一步该做什么。
* * *
^([8]) 是的,我们中的一些人对 i386 硬件还有半抑制的记忆,它不能从 CD 启动 OpenBSD,但一旦你从软盘启动,它就会让你获取安装套件。但说真的,如果你的硬件那么老,那么挑剔,请为自己节省一些痛苦。回到你找到那台电脑的垃圾桶。找一些更现代的东西。
^([9]) 当然,美国/密歇根时区仅适用于上半岛西端的四个县。但接受默认设置并不能让我说明这一点,而且如果我必须编造一些东西,那它至少应该是模糊可信的。
## 第四章. 安装后设置
*首先安装,*
*现在配置软件。*
*服务器已就绪。*
 您已安装 OpenBSD 并重新启动到裸机系统。当然,一个最小的类 Unix 系统实际上相当无聊。虽然它是一个强大的基础,但它实际上并没有做什么。
为了让您开始,本章涵盖了您在安装 OpenBSD 后应采取的一些基本步骤,以建立一个稳固的平台,为后续工作打下基础。我们将直接进入基本任务,例如纠正时区、设置默认网关以及为 root 账户设置电子邮件别名。
但有一点预先警告:当系统运行时,您可以更改 OpenBSD 的许多配置。您可以重新配置主机名、网络配置以及日期和时间,还可以根据需要停止、启动或重置守护进程。但仅仅因为您可以这样做,并不意味着您应该这样做。
当在台式机或笔记本电脑上随意玩耍时可能没问题,但如果您在运行服务器,您应该通过重新启动来测试您的配置。在添加服务之前,请确保系统正好按预期启动。如果您发现 OpenBSD 启动良好,但必须在它工作之前戳一下网卡,请在继续之前修复它。
### 注意
`afterboot(8)` 手册页为刚刚安装了第一个 OpenBSD 系统的系统管理员提供了最新的建议。其中许多建议与本书中我们将要涵盖的材料重叠(我相信会以更加令人兴奋的方式),而其中一些则仅适用于特定的用例。请阅读您系统上的 `afterboot` 文档,以获取最新的信息。
本章中的所有任务都必须以 root 身份执行。我们将在第六章(第六章. 用户管理)中讨论创建额外用户的方法,在第七章(第七章. Root,以及如何避免它)中讨论避免使用 root 账户的方法,但在新安装的系统上您不需要这样做。在开始让用户登录之前,请正确配置系统,否则您会后悔的。^([[10)]
## 第一步
在安装新系统后,您应该做的第一件事是检查任何操作系统补丁或勘误表,并更改 root 管理员密码。这些步骤至关重要,所以不要跳过它们。
### 检查系统勘误表
信不信由你,OpenBSD 并不是完美的。版本有时会有错误。其中一些是严重的问题;而另一些则不那么严重。
当 OpenBSD 团队发现某个版本存在严重问题时,它会发布勘误表,并且每次您构建一个新的服务器时,都应该检查勘误表,网址为 *[`www.OpenBSD.org/errata.html`](http://www.OpenBSD.org/errata.html)*。关键勘误表也会在 *security-announce@OpenBSD.org* 上宣布,所以如果您在这个邮件列表中,您将收到新勘误表的通知。您也会在 *[`www.undeadly.org/`](http://www.undeadly.org/)* 上看到勘误通知。
勘误表不一定会影响您的使用情况。例如,当我写这篇文章时,OpenBSD 5.0 有一个勘误通知:BIND 命名服务器的一个问题。如果这个服务器不能运行 BIND,请不要担心这个勘误。如果您正在构建一个名称服务器,那么在进入生产之前,您需要这个信息。
如果您有疑问,请按照勘误表中的建议修复您的系统,这可能需要从源代码构建 OpenBSD 的一或多个部分。(我将在第二十章中详细讨论勘误和构建 OpenBSD。)
### 设置根密码
在安装过程中,您需要选择一个根密码。要更改它,请使用 `passwd(1)` 命令。当然,您必须是 root 用户才能更改 root 的密码。
## 软件配置
当 OpenBSD 内核完成其初始系统设置并将系统控制权交给用户空间时,`init(8)` 将运行 */etc/rc* 脚本。此脚本启动所有与系统集成的程序,并执行一般系统配置,例如配置网络接口和启动服务器软件。要启用、禁用或配置集成软件,请修改 */etc/rc.conf* 和 */etc/rc.conf.local* 文件。(我将在第五章中详细介绍 OpenBSD 的启动过程,但现在,本节将帮助您入门。)
文件 *rc.conf* 和 *rc.conf.local* 包含控制 */etc/rc* 运行和各个程序的命令行选项的 shell 脚本变量赋值。请记住,*rc.conf.local* 中的任何条目都将覆盖 *rc.conf* 语句。大多数变量赋值有三个合法值:大写 `NO`、引号内的命令行标志(`"-D"`)或双引号(`""`),它们等同于空。每个变量看起来都像 *rc.conf* 中的这个条目:
ntpd_flags=NO # for normal use: ""
变量 `ntpd_flags` 控制在启动 `ntpd(8)` 时 */etc/rc* 使用的命令行标志。
`NO` 禁用这个特定的功能。在上面的例子中,NTP 守护进程 `ntpd(8)` 被禁用。
如果变量为空,*/etc/rc* 将不带任何命令行参数启动程序。例如,这个 `ntpd_flags` 条目意味着 `ntpd` 将不带任何参数启动。
ntpd_flags=""
引号内的任何内容都用作程序的命令行参数。(如果一个程序有典型的默认标志,它们通常会出现在 *rc.conf* 中。)以下示例将变量 `ntpd_flags` 赋值为 `-s`。当系统启动时,而不是运行 `ntpd`,它将运行 `ntpd -s`:
ntpd_flags="-s"
一些变量有额外的可能值。例如,PF 数据包过滤器(见第二十一章` 确保时区已正确设置:
ln -fs /usr/share/zoneinfo/Asia/Omsk /etc/localtime
date
Thu Mar 14 06:02:56 OMST 2013
OpenBSD 还支持在 */usr/share/zoneinfo/Etc* 中找到的 POSIX 时区。POSIX 时区有自己的规则。除非你绝对确定你理解它们,否则不要使用它们。(提示:你可能不懂。)
### 设置日期和时间
现在你已经设置了时区,请设置正确的时间和日期。OpenBSD 包含 OpenNTPD,这是一个 BSD 许可的简化版 NTP 守护进程。如果可能的话,请使用 `ntpd(8)` 来管理时间。如果你无法访问 NTP 服务器(例如,如果你在一个没有它们的私有网络上),请设置自己的服务器。如果你无法设置时间服务器,请手动设置系统时间。
#### 使用 ntpd(8) 设置时间
在 */etc/ntpd.conf* 中配置 OpenNTPD。如果你管理过其他任何 NTP 守护进程,语法应该对你来说很熟悉。
对于基本时间,你需要时间服务器,理想情况下是三个或更多。如果你没有本地时间服务器,请使用公开可访问的时间服务器,例如在 *[`pool.ntp.org/`](http://pool.ntp.org/)* 可用的主机。
在 */etc/ntpd.conf* 中列出你的服务器:
servers pool.ntp.org
然后在 */etc/rc.conf.local* 中启用 `ntpd`:
ntpd_flags=
默认情况下,`ntpd` 通过偏移系统时钟缓慢调整系统时间。如果系统时间偏差几秒钟,缓慢调整通常足够,但如果偏差几分钟或更多,让 `ntpd` 在启动时纠正系统时间,然后根据需要调整时间。要启用启动时的时间校正,请使用 `-s` 标志:
ntpd_flags="-s"
在使用频率高、硬件质量差和虚拟机上,时间偏移最严重。
#### 手动设置日期
要手动设置日期和时间,使用 `date(1)`。首先,确保你知道当前的年份、月份、月份中的日期和时间(以 24 小时制表示)。然后使用以下格式设置日期和时间:
date YYYYmmDDhhMM
例如,要将日期设置为 2013 年 2 月 3 日,时间为下午 1:17,请运行以下命令:
date 201302031317
Sun Feb 3 13:17:00 GMT 2013
话虽如此,`date(1)` 不会持续纠正你的时钟,在一些时钟性能较差的硬件上,时间会逐渐偏移。在负载较重的硬件上的虚拟机几乎肯定会丢失时间。使用 NTP 来处理这个问题。
## 主机名
在 */etc/myname* 中设置系统的主机名。我的测试系统名为 *caddis.blackhelicopters.org*。
$ cat /etc/myname
caddis.blackhelicopters.org
要更改主机名,编辑 */etc/myname*。新的主机名将在下次重启后生效。
要更改主机名直到下次启动,使用 `hostname -s` 和新的主机名,如下所示:
hostname -s treble.blackhelicopters.org
hostname
treble.blackhelicopters.org
你可以编辑 */etc/myname* 并运行 `hostname -s` 来立即生效更改,并在下次启动后持久保存。
## 网络配置
如果你通过互联网安装了 OpenBSD,至少应该配置并启用一块以太网卡。但如果你是从 CD 安装的,你不需要配置网络来安装 OpenBSD。如果你在一个网络上安装了 OpenBSD,并希望将机器移动到另一个网络,你需要在你的系统上重新配置网络。
我在网络原理方面涵盖了第十一章(第十一章。TCP/IP 概述)和配置在第十二章(第十二章。连接到网络),但这个简短的条目将把有效的网络地址附加到你的系统上,安装默认路由,并使 DNS 解析工作。如果你不确定在这里该做什么,直到你阅读了后面的章节之前不要做任何事情。
要使网络更改生效,你可以重启或者运行网络启动脚本 */etc/netstart*,如下所示:
sh /etc/netstart
要仅配置一个接口,请将接口的名称作为参数传递:
sh /etc/netstart em0
再次强调,如果你的系统是服务器,在宣布服务器准备就绪投入生产之前,请先重启。
### 配置以太网接口
要获取你的主机识别的所有网络接口的完整列表,请运行 `ifconfig(8)`。你应该会看到一些条目,如下所示:
1 em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:0c:29:d2:37:be
priority: 0
groups: egress
media: Ethernet autoselect (1000baseT full-duplex,master)
status: active
2 inet 192.0.2.36 netmask 0xffffffe0 broadcast 192.0.2.63
inet6 fe80::20c:29ff:fed2:37be%em0 prefixlen 64 scopeid 0x1
每个 OpenBSD 系统都自带默认接口 `lo0`(环回)、`enc0`(封装接口)和 `pflog0`(PF 日志,在第二十一章中讨论)。这些是虚拟接口,实际上并没有将你的系统连接到本地以太网。其他接口将是物理网络端口,例如在**1**处的以太网接口 `em0`。
每块以太网卡都有自己的配置文件,*/etc/hostname* .interfacename。示例中的 `em0` 接口在文件 */etc/hostname.em0* 中配置。如果该文件不存在,则需要创建它。
在**2**处,你可以看到 IP 地址。静态 IP 地址的格式取决于使用的 IP 版本。
#### 静态 IP 地址
对于 IP 版本 4(IPv4)地址,常见的格式如下:
inet ipaddress netmask broadcastaddress options
此格式包括以下元素:
+ `inet`关键字表示这是一个 IPv4 地址。
+ IP 地址(*`ipaddress`*)以标准点分十进制表示法出现。
+ *`netmask`*可以以点分十进制格式(255.255.255.224)或十六进制(0xffffffe0)出现。
+ 广播地址(*`broadcastaddress`*)允许您在此网络上硬编码广播地址。如果您留空或使用单词`NONE`,OpenBSD 将根据之前给出的 IP 地址和子网掩码计算正确的广播地址。如果您使用`ifconfig(8)`选项,必须使用单词`NONE`来提供间隔。(您也可以在 IP 地址后直接提供子网掩码,例如`192.0.2.2/24`。)
+ *`options`*空间是放置任何特定`ifconfig(8)`命令的地方,例如硬编码速度或双工。请参阅第十一章和第十二章中的示例,以及`hostname.if(5)`手册页以获取详细信息。
当与 IPv6(IP 版本 6)地址一起工作时,格式如下:
inet6 address prefixlength options
此格式包括以下元素:
+ `inet6`关键字告诉系统这是一个 IPv6 地址。
+ *`address`*空间用于 IPv6 地址,不带前缀长度。
+ 前缀长度(*`prefixlength`*)单独出现,不带斜杠。
+ 与 IPv4 一样,根据需要可以使用`ifconfig(8)` *`options`*。
即使是非常简单的配置也能使机器连接到网络。以下配置了`em0`,使用 IPv4 地址 192.0.2.2 和子网掩码 255.255.255.224。它还具有 IPv6 地址 2001:DB8:7700::2/64。
$ cat /etc/hostname.em0
inet 192.0.2.2 255.255.255.224
inet6 2001:DB8::2 64
任何不符合这些 IPv4 和 IPv6 格式的条目都将直接传递给`ifconfig`。但是,如果您不确定特定的配置是否有效,请首先在命令行中尝试。
#### 动态配置
在执行动态配置时,如果该机器是 IPv4 DHCP 客户端,请在*hostname.if*中使用字符串`dhcp`。对于 IPv6 自动配置,使用字符串`rtsol`,单独占一行,以告知 OpenBSD 使用`rtsol(8)` IPv6 自动配置程序。
$ cat /etc/hostname.em0
dhcp
rtsol
为了使 IPv6 自动配置工作,您必须使用以下两个条目在*/etc/sysctl.conf*中禁用 IPv6 路由:
net.inet6.ip6.forwarding=0
net.inet6.ip6.accept_rtadv=1
这些值已经存在于*/etc/sysctl.conf*中,但已被注释掉。
### 设置默认网关
要设置 IPv4 或 IPv6 默认网关,请在*/etc/mygate*文件中将网关 IP 地址放在单独一行,文件中不包含其他条目。您可以分别为两种协议配置默认网关,每个网关占一行。更改将在您下次重启时生效,或者使用`route(8)`手动更改默认网关。以下示例设置了 IPv4 和 IPv6 的默认网关。
192.0.2.1
2001:DB8::1
动态配置需要设置默认路由。如果你的*/etc/hostname.if*文件包含动态配置语句,则不会使用*/etc/mygate*来处理该 IP 协议。
### 设置名称服务服务器
如果你想要通过主机名联系其他机器,你需要设置域名服务(DNS)服务器。
第十二章详细介绍了 DNS 解析,但*/etc/resolv.conf*包含了基本的客户端设置。其第一行使用`domain`关键字和域名定义了本地域名。名称服务器出现在随后的行中,使用`nameserver`关键字和 IP 地址定义,如下所示:
domain blackhelicopters.org
nameserver 192.0.2.1
nameserver 192.0.2.3
## 邮件别名和状态邮件
每个 OpenBSD 系统每天、每周和每月都会运行维护任务,并将结果以电子邮件消息的形式发送到本地 root 账户。我在第十五章中讨论了这些维护任务。如果你有超过两台服务器,你可能需要将这些电子邮件消息转发到单个中央账户。
OpenBSD 系统将发送给本地用户的电子邮件消息重定向到其他用户或远程电子邮件地址,这已在*/etc/mail/aliases*中配置。以下是一些条目:
…
Basic system aliases -- these MUST be present
MAILER-DAEMON: postmaster
postmaster: root
…
原始接收者位于左侧,后面跟着一个冒号,然后是一个要转发消息的人员列表。如果你有多个接收者,请用逗号分隔它们。发送到本地系统 MAILER-DAEMON 的邮件将被转发到 postmaster 账户,而 postmaster 账户又会被转发到 root 账户。
你应该为 root 创建一个别名,将发送给 root 的邮件重定向到你的系统管理团队:
root: mwlucas@bigcompany.com, sysadminstaff@bigcompany.com
在更改*/etc/mail/aliases*后,运行`newaliases(8)`以更新电子邮件转发。
## 键盘映射
OpenBSD 试图猜测你的正确键盘映射。USB 键盘有声明其国家代码的机制。如果自动检测不起作用,你可以使用`kbd(8)`更改键盘布局,并在启动时使用*/etc/kbdtype*设置它。
在触摸键盘布局之前,使用`kbd`查找你的当前键盘映射。`kbd -l`列出了 OpenBSD 支持的超过 100 种键盘编码。浏览列表以找到你的支持键盘布局,或使用`grep`来减少列表。我是一个 Dvorak 用户,所以我像这样搜索:
kbd -l | grep dvorak
fr.dvorak
us.dvorak
…
我首选的布局是`us.dvorak`。我在*/etc/kbdtype*中输入**`us.dvorak`**,并在下一次重启后,控制台应该使用 Dvorak 布局。
要立即将键盘映射设置为 Dvorak,我可以使用`kbd`:
kbd us.dvorak
kbd: keyboard mapping set to us.dvorak
在第十七章中了解更多关于更改控制台的信息。
## 安装端口和源代码
如果你打算构建自己的 OpenBSD 发布版或对 OpenBSD 进行大量自定义,你需要操作系统源代码(见第十八章,第十九章,第二十章中介绍),你需要端口树。端口树和系统源代码都是特定于 OpenBSD 发布版的,如果你需要它们,在安装系统时安装它们。
端口树是存储 OpenBSD 的发布目录中的压缩文件 *ports.tar.gz*,无论是镜像站点还是 CD。所有架构共享一个端口树。将此文件下载到你的主目录,并在 */usr* 中提取它。
cd /usr/
tar -xzvf $HOME/ports.tar.gz
这会将端口树提取到 */usr/ports*。
系统源代码位于发布目录中的三个文件中:内核源代码 *sys.tar.gz*(见第十九章` 命令。默认的 `xdm` 设置是 `NO`,所以将其更改为空引号可以启用它。
xdm_flags=""
如果你需要一个更复杂的 `xdm` 设置,请将任何命令行参数放在引号内。
## 继续前进!
在本章提供的信息的帮助下,你应该能够将你的系统连接到本地网络,并使其至少在最小程度上便于使用。接下来,让我们看看 OpenBSD 的启动过程。
* * *
^([10]) 说“很抱歉你的重要数据时间戳错误,但我还没有设置系统时钟”大致等同于说“我不在乎你或你的数据。”如果你有这样的感觉,我不会争论,但至少要有自信告诉用户你真正的想法。
## 第五章. 引导过程
*单用户模式*
*夜间不按计划进行?*
*有什么东西“砰”的一声爆炸了!*
 为了正确管理任何计算平台,你必须了解引导过程。许多系统管理任务在系统运行时无法完成。OpenBSD 特别要求在引导过程完成之前完成某些任务。当然,在任何操作系统上,有时启动进程会阻止系统完成引导。解决这些问题的唯一方法是中断引导过程。
首先,我们将探讨 OpenBSD 引导过程的关键:引导加载程序。然后,我们将继续讨论单用户模式,最后是多用户启动。在这些阶段中,你可以执行一些有用的工作。
我建议在机器无法引导之前在测试机器上玩 OpenBSD 引导过程。这样,当凌晨时分出现问题,你可以花时间解决问题,而不是笨拙地使用不熟悉的命令。
## 开机与引导加载程序
通常,当 PC 风格的计算机首次引导时,它会启动 BIOS。BIOS 是一小块软件,用于确定哪些驱动器已连接以及它们连接到什么,安装了哪种 CPU,以及有多少可用内存。在获取这些信息后,BIOS 从某种存储设备加载一个最小引导加载程序。^[[11]
*引导加载程序* 是一个小程序,负责处理初始系统配置并引导内核。它找到并启动内核,内核随后检测硬件,附加设备驱动程序,并执行其他核心设置。最后,内核调用 `init(8)`,该程序启动进程并启用用户程序、网络接口、服务器软件等。
虽然大多数此过程无法管理——没有人实际配置 `init`!——但在系统完成引导并将你置于登录屏幕之前,你可以做很多事情。
OpenBSD 引导加载程序允许你在引导过程中中断,在引导之前配置系统,调整内核设置,甚至引导备用内核。
当硬件将引导过程控制权交给 OpenBSD 分区时,你会看到引导加载程序提示符,其外观可能如下所示:
OpenBSD/amd64 BOOT 3.18
boot>
引导加载程序的主要目的是找到内核,将其加载到内存中,并启动它。因为它在内核启动之前运行,所以引导加载程序可以向内核本身传递指令。
在引导完成之前,你可以执行以下操作:
> 使用内置帮助
>
> 使用 `help` 函数打印引导加载程序支持的命令的简要列表。
>
> ```
> boot> **help**
> commands: # boot echo env help ls machine reboot set stty time
> machine: boot diskinfo memory
> ```
>
> 延迟引导过程
>
> 默认情况下,加载程序等待五秒钟以获取指令,然后引导内核。要在提示符处暂停引导,请按空格键。
>
> 设置引导超时
>
> 要设置新的引导空闲超时,请使用 `set timeout` 命令指定秒数。
>
> ```
> boot> **set timeout 10**
> ```
>
> 在引导提示符空闲 10 秒后,系统应该启动。
>
> 启动系统
>
> 如果你暂停了引导过程,系统将不会启动,直到你告诉它。当你准备好启动时,使用`boot`命令:
>
> ```
> > **boot**
> ```
我们将使用`boot`的各种排列组合来配置内核、启动单用户模式等。我将在适当的部分介绍其他引导命令。有关引导提示符上可以执行的所有操作的完整细节,请阅读`boot(8)`手册页。
## 以单用户模式启动
单用户模式是 OpenBSD 最早可以给你 Unix 风格 shell 提示的位置。在这个时候,内核已经探测了所有硬件,将驱动程序附加到它将要认可的所有硬件上,并启动了`init`。系统除了根分区外没有挂载任何文件系统,根分区是以只读模式挂载的。网络尚未启动,没有服务正在运行,安全性未实现,文件系统权限被忽略。
要以单用户模式启动 OpenBSD,请在引导提示符下输入**`boot -s`**。
boot> boot -s
为什么你想以单用户模式启动?如果你的计算机有问题阻止它启动,你应该能够访问单用户模式并修复问题。假设一个失败的磁盘阻止了系统在多用户引导期间启动,或者你更改了*/etc/ttys*中的终端设置,现在你无法登录到系统。或者你可能将一个愚蠢的设置放入*rc.conf.local*,引导过程挂起,因为它正在尝试做一些不可能的事情。在这些时候,单用户模式是你的最佳朋友。
此外,一些系统管理任务,如清除文件系统标志(见第八章),只能在单用户模式下完成。
### 以单用户模式挂载磁盘
通常,在单用户模式下进行任何操作之前,你应该有一个完全功能的文件系统。如果你的系统崩溃了,在挂载它们之前,务必检查文件系统的完整性:
fsck -p
/dev/sd0a (e4bf0318329fe596.a): file system is clean; not checking
/dev/sd0h (e4bf0318329fe596.h): file system is clean; not checking
…
mount -a
`fsck`和`mount`有许多更多选项。我们将在第八章(第八章。磁盘和文件系统)中更详细地介绍它们。
一旦挂载了所有文件系统,所有常规的命令行软件都应该可用。你应该能够编辑配置文件、启动和停止程序,并且通常可以对你喜欢的系统进行任何操作(包括破坏它)。
### 以单用户模式启动网络
使用 shell 脚本*/etc/netstart*在单用户模式下配置网络。(你可以手动运行所有适当的命令,但*/etc/netstart*将读取你的系统配置文件并为你完成繁琐的工作。)你必须通过`sh`显式运行此脚本:
sh /etc/netstart
如果你因为网络问题而以单用户模式启动,这个脚本会方便地为你重现问题。
## 启动备用内核
正如我们在第十八章中将要详细介绍的,你可以配置 OpenBSD 内核,但在这样做之前,请确保你可以引导其他内核。如果你损坏了文件系统到连单用户模式都无法引导的程度,你需要能够引导不同的内核,并使用安装内核进行恢复。
### 引导不同的内核文件
OpenBSD 安装程序默认包含三个内核:单处理器内核 */bsd*,多处理器内核 */bsd.mp*,以及升级和安装内核 */bsd.rd*。(如果你的机器有多个处理器,安装程序会将 */bsd* 重命名为 */bsd.sp*,将 */bsd.mp* 重命名为 */bsd*。)
要引导非标准内核,首先重启并中断引导过程,在引导加载程序提示符下运行`boot`,并给出你想要引导的内核的完整路径:
boot> boot /bsd.rd
这将使用你选择的内核启动系统。你也可以使用其他引导选项,例如以单用户模式引导备用内核:
boot> boot -s /bsd.sp
这将让你能够从损坏的内核中恢复,尝试新的内核,或者介于两者之间的任何东西。
### 从备用硬盘引导
假设你真的把一切都搞砸了,你的根分区上没有可用的内核。幸运的是,如果你在另一个硬盘上有可用的内核,你可以从那个硬盘引导。通常,这个内核位于备用根分区,*/altroot*,如第八章中所述。)在本节中,我将把从那个备用内核引导的任务分解成几个步骤:找到包含分区的硬盘,找到包含文件的分区,并在该分区上引导正确的文件。
#### 寻找硬盘
一旦你熟悉了 OpenBSD,你可能会开始根据它们的设备名称来考虑系统中的硬盘,例如*/dev/sd0*,*/dev/wd1*等等。不幸的是,这些是内核对磁盘的命名;引导加载程序只识别 BIOS 的磁盘名称。
要询问引导加载程序关于磁盘的信息,请使用`machine diskinfo`命令:
boot> machine diskinfo
Disk BIOS# Type Cyls Heads Secs Flags Checksum
fd0 0x0 none 80 2 18 0x4 0x0
hd0 0x80 label 1024 255 63 0x2 0x51db843d
hd1 0x81 label 1024 255 63 0x2 0x9329b723
hd2 0x82 label 1024 255 63 0x2 0xcfadb343
boot>
在这里,引导加载程序找到了四个磁盘设备。第一个,`fd0`,是一个软盘驱动器。这个驱动器可能没有磁盘,或者有磁盘,但无论它有什么,几乎可以肯定它不是你的备用内核。(它可能是一个安装盘,所以不要自动排除用于灾难恢复的可能性。)
其他三个设备—`hd0`,`hd1`和`hd2`—是硬盘。第一个,`hd0`,是默认的系统引导盘。如果你不能从这个磁盘引导,你需要找到包含你的内核的硬盘。
#### 寻找分区
从输出中模糊的记忆让我想到`hd2`可能是包含我的备份根分区的磁盘。为了尝试它,告诉加载程序磁盘分区`hd2a`是新的根分区:
boot> set device hd2a
在尝试从这个分区引导之前,看看它的内容:
boot> ls
stat(hd2a:/.): Invalid argument
boot>
显然,磁盘 `hd2` 没有分区 `a`。在服务恢复后,我会把这块磁盘放在车库后面,把它的问题打掉。现在,让我们尝试剩下的唯一磁盘,`hd1`。
boot> set device hd1a
boot> ls
drwxr-xr-x 0,0 512 .
drwxr-xr-x 0,0 512 ..
drwxr-xr-x 0,0 512 altroot
drwxr-xr-x 0,0 512 home
drwxr-xr-x 0,0 512 tmp
…
这看起来像是一个实际的根分区(`altroot` 提供了提示)。
#### 引导内核
在这一点上,我们可以引导不同的内核,但我们将只以单用户模式引导这个分区上的 */bsd* 内核,因为文件系统表将会有错误的根文件系统条目,这会搞砸各种东西。
boot> boot -s
booting hd1a:/bsd: 5669864+1601484+935608+0+617568 [89+499848+323884]=0xd351b8
…
或者,您也可以在引导提示符处给出设备名称:
boot> boot -s hd1a:/bsd
作为一般规则,您应该在 */mnt* 上挂载实际的根分区,进行必要的更改以进行正常操作,然后重新引导到正确的根分区。您还可以引导 */bsd.rd* 内核,这将使引导更加干净,但可用的工具会减少。
## 将引导加载程序设置永久化
要使引导加载程序选项永久化,请编辑 */etc/boot.conf*。引导加载程序在给您 `boot>` 提示符之前读取并运行此文件中的条目,这意味着您可以使用它来在每次计算机启动时自动运行引导加载程序命令。(尽管如果您宁愿每次重新启动时都坐在电脑前输入设置,请不要阻止我。)
您在引导加载程序提示符下可能给出的任何命令都是有效的 *boot.conf* 条目。例如,如果默认的引导速度太慢,您可以通过在 *boot.conf* 中添加此行来将引导超时设置为两秒:
set timeout 2
您还可以使用正确的 *boot.conf* 命令告诉系统引导不同的内核。
set image /bsd.mp
到目前为止,*boot.conf* 最常用于配置串行控制台。
## 串行控制台
所有这些巧妙的引导功能都让您在系统出现问题时能够做些有用的事情,但您如何在不坐在电脑前的情况下使用它们呢?如果您的电脑在国家的另一边的数据中心,或者坐在十年工资记录背后的地下室里,串行控制台会让您的生活更加愉快。
硬件串行控制台允许您在计算机和终端服务器(在另一台计算机上)之间运行串行电缆,以访问 BIOS 消息和操作系统引导和启动消息,从而简化远程系统的管理。在调试系统崩溃时,串行控制台也极其宝贵;错误消息通过串行端口发送,您可以轻松地捕获它们。^[[12]
真正的 UNIX 硬件具有串行控制台功能,大多数服务器级 i386 和 amd64 硬件也是如此。然而,大多数桌面级硬件则没有。但幸运的是,即使您没有硬件串行控制台,您也可以通过串行端口和软件串行控制台访问 OpenBSD 的所有启动消息。虽然 OpenBSD 的软件串行控制台不会让您访问硬件 BIOS,但它会允许您与引导加载程序接口,并在网络断开时远程访问系统控制台。
### 其他平台串行控制台
每个硬件平台都有自己的串行控制台标准。如果您运行的是不太常见的平台,请检查您的硬件文档。
如果您的硬件支持真实串行控制台,您通常应该在 BIOS 中进行配置。OpenBSD 支持硬件支持的任何功能,因此您的 Sparc64 硬件将像支持任何其他操作系统的串行控制台一样支持 OpenBSD 的串行控制台。
### 串行控制台物理设置
串行控制台需要一个 null modem 电缆,您应该可以从任何电脑店或在线供应商那里获得。虽然镀金电缆不值那么多钱,但也不要买最便宜的电缆。如果您有紧急情况,需要立即使用串行控制台,您可能不会愿意处理一根有缺陷的电缆。
将 null modem 电缆的一端插入您的 OpenBSD 机器的第一个串行端口。(串行控制台仅在第一个串行端口上受支持,或在 i386 和 amd64 硬件上的 `com0`。)将 null modem 电缆的另一端插入另一个系统的空闲串行端口。(为了简单起见,使用另一个 OpenBSD 或类 Unix 系统。)
如果您在远程位置有两台 OpenBSD 机器,并且想在两台机器上使用串行控制台,您可以让每台机器充当另一台的控制台客户端。将每台服务器的第一个串行端口连接到另一台服务器的第二个串行端口。如果您有三台机器,可以将它们串联成环。如果您有四台或更多机器,可以从您喜欢的拍卖网站上购买一台二手终端服务器。
您还可以使用两个 DB9-to-RJ45 转换器,一个标准和一个交叉,这将允许您通过标准 CAT5 电缆运行您的控制台连接。如果您有一个禁止人类进入(除非他们正在安装或拆除设备)的无人值守数据中心,您可以将串行控制台电缆延长约 12 米,这应该可以到达您的温暖房间。(大多数现代数据中心比串行电缆更适合处理 CAT5 电缆。)
### 串行控制台配置
现在您的控制台已经物理准备就绪,下一步是配置您的客户端以访问串行控制台。然后您可以设置串行控制台。
#### 配置串行控制台客户端
以下是一个 OpenBSD i386 或 amd64 系统的默认设置:
+ 9600 波特
+ 8 位
+ 无奇偶校验
+ 1 停止位
将这些值输入到客户端计算机上的任何终端仿真器中,串行控制台应该能够正常工作。您可以在 Microsoft 平台(我推荐 PuTTY)、OS X 和几乎所有其他操作系统上找到终端仿真器。
OpenBSD 包括终端模拟器`tip(1)`,它从`/etc/remote`读取其配置。`/etc/remote`中的`tty00`配置与 i386 和 amd64 系统的默认 OpenBSD 串行控制台配置相匹配(以及几个其他平台)。如果您将 null 调制解调器电缆连接到客户端的第一个串行端口,请使用以下命令连接:
tip tty00
connected
如果没有显示`connected`,您的串行客户端配置不正确。在服务器上启用串行控制台之前,请修复您的客户端。您希望在配置控制台之前准备好串行客户端。
#### 设置串行控制台
OpenBSD 通常使用本地物理键盘、视频和鼠标作为控制台,但它也可以使用第一个串行端口作为串行控制台。
要设置控制台,请使用引导加载程序。您必须知道您首选控制台的加载程序设备名称:`com0`用于第一个串行端口或`pc0`用于物理连接的视频和键盘。
第一次尝试使用串行控制台时,请使用本地测试机器。事先设置您的客户端并启动您的终端模拟器,然后引导测试机器。在引导加载程序提示符下,输入以下命令:
boot> set tty com0
您服务器的显示器和键盘应该停止响应,如果您设置正确,您应该在终端模拟器中看到引导加载程序提示符。
要切换回物理控制台,告诉引导加载程序使用`pc0`设备:
boot> set tty pc0
嘭!服务器的键盘和显示器应该又能工作了。
要在每次引导时让您的机器使用串行控制台,请在`/etc/boot.conf`中添加此语句:
set tty com0
确保在机器安装到永久位置后测试您的串行控制台,并且始终将串行电缆拧紧在服务器上。松散的串行电缆只能提供一种安慰的错觉,但在最需要的时候会背叛您。
#### 测试串行配置
在配置您的串行控制台后,返回您的串行客户端并按回车键。您应该看到类似以下内容:
OpenBSD/amd64 (caddis.blackhelicopters.org) (tty00)
login:
### 修改串行控制台速度
较新的串行端口(意味着在过去 10 年内制造的任何端口)可以以远高于 9600 波特的速度运行。我有一些串行控制台运行速度仅为 115,200 波特的服务器。BIOS 信息以 115,200 波特显示,但 OpenBSD 控制台以 9600 波特运行。我的客户端显示为乱码。(许多 OpenBSD 用户认为不能以 9600 波特进行串行通信的任何东西都是损坏的,但您并不总是能控制您所使用的硬件。)
要使用这些端口,我可以在在切换 BIOS 信息和 OpenBSD 信息时更改我的串行控制台客户端的连接速度,或者更改 OpenBSD 控制台的速度以匹配硬件。
在引导加载程序中,告诉串行控制台以 115,200 波特运行:
boot> stty com0 115200
boot> set tty com0
如果这些设置有效,请将它们复制到`/etc/boot.conf`。
现在配置您的串行客户端。修改`tip`以使用更高的速度。首先,在`/etc/remote`中找到`tty00`条目:
tty00|For hp300,i386,mac68k,macppc,vax:
:dv=/dev/tty00:tc=direct:tc=unixhost:
但不要修改这个条目!我们将用它来说明 */etc/remote* 条目的风格。
### 注意
*/etc/remote* 的设计类似于一个 `termcap(5)` 数据库。如果你需要从头开始编写自己的 *`termcap`* 条目,那么你的生活方向就错了。但你可以识别内容并修改现有条目而不会感到太多痛苦。如果你真的想了解有关这些条目的所有内容,请阅读 *`remote(5)`* 手册页。
在这个条目中,反斜杠 (`\`) 表示“续到下一行”。冒号分隔字段。每一行在第一行之后都必须以冒号开头,并且每个字段都是一个键/值对。
现在,要创建一个以 115,200 波特率运行的控制台条目,请使用以下命令:
console:br#115200:tc=tty00:
*/etc/remote* 条目的第一个字段是名称,每个条目都必须有一个唯一的名称。我给这个条目命名为 `console`。第二个字段是 `br` 值。根据 `remote(5)`,`br` 代表比特率。我已经将比特率设置为 115,200 波特。第三个字段是 `tc`,代表“表继续”,等于 `tty00`。这意味着这个条目的描述将在 `tty00` 条目中继续。
整体来看,这个条目的意思是“复制 `tty00` 条目,并添加一个 115,200 的比特率。”
### 修改客户端串行端口
如果你有两个 OpenBSD 机器,每个机器都将其串行控制台发送到另一台机器的第一个串行端口,你必须告诉 `tip` 连接到第二个串行端口。命令 `tip tty00` 实际上并没有连接到名为 `tty00` 的串行端口——它连接到由 */etc/remote* 条目定义的端口,该条目名为 `tty00`。这意味着你不能运行,例如,`tip tty03` 并连接到串行端口 tty03,除非你有一个名为 `tty03` 的 */etc/remote* 条目。默认情况下,没有这样的条目,但你可以轻松地定义一个,如下所示:
tty03:dv=/dev/tty03:tc=tty00:
这个条目的名称是 `tty01`。`dv` 设置告诉 */etc/remote* 使用哪个物理设备。除了这个之外,所有设置都是从名为 `tty00` 的条目中复制的。
通过这些示例,你应该能够使用 OpenBSD 的 `tip` 连接到几乎任何串行控制台。
### 串行登录
串行控制台允许你与引导过程交互。然而,一旦你的机器完全进入多用户模式,默认的串行控制台将不会让你实际登录到 OpenBSD。在多用户模式下,OpenBSD 使用 `getty(8)` 初始化终端并处理登录,为了通过串行端口登录到你的机器,你需要告诉 `getty` 通过配置 */etc/ttys* 来接管串行线路。
我们将在 第十四章 中进一步讨论 */etc/ttys*,但到目前为止,以下是如何允许通过第一个串行端口进行登录的方法。找到名为 `tty00` 的条目,它应该看起来像这样:
tty00 "/usr/libexec/getty std.9600" unknown off
删除最后两个单词,并将它们替换为以下内容:
tty00 "/usr/libexec/getty std.9600" vt220 on secure
现在,运行 `kill -1 1`,你应该通过串行线路获得登录提示。
## 多用户启动
当内核完成核心设置并将控制权交给用户空间后,`init(8)` 运行 shell 脚本 */etc/rc*。此脚本处理所有系统设置,包括挂载文件系统、配置设备节点、识别共享库以及任何其他使系统可用的任务。一些任务委托给单独的脚本;例如,*/etc/netstart* 用于配置网络。
在本节中,我们将介绍 */etc/rc* 和其他启动脚本如何工作,以及启动过程的流程。有了这种理解,您应该能够轻松地配置您的 OpenBSD 机器启动您所需的确切内容——不多也不少。
### 启动系统脚本
启动系统包括脚本 */etc/rc*、*/etc/rc.conf*、*/etc/rc.conf.local*、*/etc/netstart*、*/etc/rc.securelevel*、*/etc/rc.local*、*/etc/rc.shutdown*、*/etc/rc.firsttime*、*/etc/fastboot* 以及 */etc/rc.d* 目录的内容。
#### /etc/rc 脚本
在 OpenBSD 上,从设置主机名到启动服务器守护进程,所有非内核内容都是通过 shell 命令配置的。主脚本是 */etc/rc*,它按正确顺序运行所有这些命令,确保每次引导时系统配置完全相同。作为最后一步,*/etc/rc* 运行 `getty(8)` 在所有适当的终端上显示登录提示。
除非您是一位经验非常丰富的系统管理员,并且有真正独特的需求,否则永远不要编辑 */etc/rc*。这是 */etc* 中可以技术上可编辑的几个文件之一,但普通人最好将其视为二进制文件。相反,每次您需要禁用功能时,请在 */etc/rc.conf.local* 中将其停用。要向启动过程添加新功能,请使用 shell 脚本 */etc/rc.securelevel* 和 */etc/rc.local*,或者为 */etc/rc.d* 编写 shell 脚本。
#### /etc/rc.conf 脚本
*/etc/rc.conf* 文件只包含所有其他启动脚本的默认值。阅读此文件以查看不同系统服务的配置选项。以下是您将在 *rc.conf* 中找到的片段之一:
…
bgpd_flags=NO # for normal use: ""
rarpd_flags=NO # for normal use: "-a"
bootparamd_flags=NO # for normal use: ""
rbootd_flags=NO # for normal use: ""
sshd_flags="" # for normal use: ""
named_flags=NO # for normal use: ""
…
如果变量设置为 `NO`,则默认禁用相关服务。
如您所见,OpenBSD 默认关闭几乎所有功能,只有一个例外:SSH 守护进程。将变量设置为成对的引号,如前一段代码片段中每个条目之后所示,就足以启用大多数守护进程,而且大多数守护进程在没有命令行标志的情况下运行得很好。然而,如果守护进程需要命令行参数才能运行,该参数将以 `-a` 附加到 `rarpd_flags` 中的原样显示。
### 注意
在冒着让我的服务器彻底崩溃的风险下,永远不要编辑 */etc/rc.conf*(将其视为二进制文件——记得吗?)在系统升级过程中,它将被全部替换。相反,将您的本地值放在 */etc/rc.conf.local* 中。
#### /etc/rc.conf.local 脚本
我之前已经提到过这一点,但我要反复强调:请将您的更改放置在 *rc.conf.local* 中。*rc.conf.local* 中的条目会覆盖 *rc.conf* 中的默认设置。
例如,假设在特定的机器上,您想以额外的调试方式运行 `sshd(8)`,并且您还想运行 `named(8)`。此外,您还想运行时间服务器 `ntpd(8)`,并在启动时使用 `-s` 标志来校正时间。在查阅了这些程序的文档后,您将以下行添加到 *rc.conf.local* 中:
sshd_flags="-D"
ntpd_flags="-s"
named_flags=""
OpenBSD 将启动此处指定的标志的程序。如果您指定了无效、不正确或不兼容的标志,守护进程将在控制台打印错误消息。
#### /etc/netstart 脚本
虽然其名称与其他脚本不同,但 */etc/netstart* 确实是一个系统启动脚本。它读取 */etc/mygate*、*/etc/myname* 以及所有 */etc/hostname.if* 文件,并使用其中的信息来配置所有网络接口、桥接、路由等。文件 */etc/rc* 在启动任何服务器守护进程、网络文件系统等之前运行此脚本。在单用户模式下,您需要手动运行此脚本以启动网络。
#### /etc/rc.securelevel 脚本
*/etc/rc.securelevel* 脚本在启动过程中较早运行,在 */etc/rc* 提高系统安全级别之前,但在启动网络之后。一旦提高安全级别,许多程序,尤其是那些接触内核或密切影响文件系统的程序,将无法运行。如果您运行这样的程序,可以将启动它的命令添加到此脚本中。如果您的本地程序不需要在系统安全级别提高之前运行,您最好从 *rc.local* 启动它或为其编写适当的 *rc.d* 脚本。
*rc.securelevel* 中的一个重要条目是系统安全级别的定义。我们将在 第十章 中讨论安全级别。现在,除非您已经熟悉基于 BSD 的系统并且确切知道您要采取哪种行动,否则不要触摸设置安全级别的行。
#### /etc/rc.local 脚本
在 */etc/rc* 完成几乎所有其他操作后,它会运行 */etc/rc.local*。您可以在 *rc.local* 中放置启动本地守护进程的命令,但最好编写一个 *rc.d* 脚本来启动本地守护进程,这样您可以轻松且一致地稍后重新启动它们。当然,如果您比较懒惰,可以使用 *rc.local*。
#### /etc/rc.shutdown 脚本
每次您使用 `reboot(8)` 或 `halt(8)` 时,OpenBSD 都会运行 */etc/rc.shutdown* 脚本,您可以依赖它来运行安全关闭服务器所需的额外命令。大多数服务器软件在没有特殊干预的情况下可以干净地关闭,但需要数据完整性的软件(如数据库)可能需要帮助才能安全关闭而不会丢失数据。再次提醒,如果可能的话,编写一个 *rc.d* 脚本来管理您的软件。
#### /etc/rc.firsttime 脚本
*/etc/rc* 一次运行 */etc/rc.firsttime* 脚本,将输出邮件发送给 root,并删除 *rc.firsttime*。安装程序使用 *rc.firsttime* 来执行诸如获取无法合法重新分发的固件等任务。虽然您通常不会使用 *rc.firsttime*,但您应该知道它存在,并且您可以使用它来在机器启动时执行一次性任务。
#### /etc/fastboot 脚本
如果 */etc/fastboot* 文件存在,OpenBSD 假定所有文件系统都是干净的(参见第八章),并且引导过程会跳过检查文件系统完整性。
#### /etc/rc.d 目录
如下一节所述,*/etc/rc.d* 目录包含用于管理软件的 shell 脚本。虽然系统附带了一些包含在 OpenBSD 中的软件的脚本,但附加包可以提供自己的脚本(参见第十三章)。
### 软件启动脚本
OpenBSD 使用 shell 脚本来启动、停止、重新启动、检查和重新配置服务器软件。这些脚本位于 */etc/rc.d* 目录中。OpenBSD 附带的所有服务器软件以及大多数需要脚本以正确启动和关闭的 ports 和 packages 都有这个目录中的脚本。使用这些脚本来管理集成软件,而无需重新启动服务器。
*rc.d* 脚本从 *rc.conf* 和 *rc.conf.local* 中读取其配置。大多数服务器运行 SSH 守护进程`sshd`,可以通过在 *rc.conf.local* 中添加行 `sshd_enable=""` 来启用它。查看 */etc/rc.d*,您会找到名为 *sshd* 的 shell 脚本。
如果您更改了您的`sshd`配置,您必须重新启动守护进程。使用 shell 脚本可以一致地完成此操作。
cd /etc/rc.d/
./sshd restart
sshd(ok)
sshd(ok)
当然,您也可以不使用 shell 脚本简单地通过识别当前运行的`sshd(8)`进程,阅读手册页以了解如何正确关闭它,然后使用相同的命令行标志重新启动它。对于`sshd`来说,这很简单:运行`pkill -1 sshd`会指示守护进程重新读取其配置文件。但是,重新启动需要各种标志的守护进程确实是一件大事。自动化这些系统管理任务确保您的守护进程始终运行。
要检查守护进程是否正在运行,请使用`check`命令检查您的 shell 的返回值。如果守护进程正在运行,脚本将返回`0`,如果没有运行,则返回`1`,如下所示:
./nfsd check
echo $?
1
如您通过`1`所见,`nfsd`并未运行。
`check`最常用的用途是在 shell 脚本中。您可以使用`start`参数启动守护进程,使用`stop`参数终止它。使用`restart`参数告诉守护进程重新加载其配置。
在 OpenBSD 中,*rc.d* 脚本在系统启动时运行,并在系统关闭时再次运行。(需要卸载所有这些硬盘驱动器,关闭守护进程,并清理。)在关闭时,*/etc/rc.d* 目录中的每个脚本都会使用`stop`参数调用。
### 第三方 rc.d 脚本
OpenBSD 的第三方软件包包括必要的 *rc.d* 脚本。例如,流行的数据库服务器 MySQL 的 `mysql-server` 软件包安装了脚本 */etc/rc.d/mysqld*。要使用该软件包,你必须将其在 *rc.conf.local* 中启用:
mysqld_flags=""
一旦启用该软件包,你就可以像管理任何其他 OpenBSD 守护进程一样管理你的 MySQL 服务器。然而,软件包化的软件在启动时仍然不会自动启动,所以你必须告诉 OpenBSD 在启动时运行这个特定的 *rc.d* 脚本,并在 *rc.conf.local* 中的 `pkg_scripts` 变量中关闭:
pkg_scripts="mysqld"
启动过程在启动时按给定顺序运行此变量中的脚本。对于某些守护进程,顺序很重要。例如,如果你有一个数据库驱动的网站,你应该在启动 web 服务器之前启动数据库。在关闭时,它以相反的顺序运行这些脚本。
### 强制启动软件
有时候你不想全局启用软件;你只想运行某个守护进程一段时间或解决特定情况。你可以使用 *rc.d* 脚本通过 `-f` 标志强制软件运行。
现在让我们来看一个现实生活中的例子。我之前在我的服务器上运行 PostgreSQL,但有人绑架了我的宠物老鼠,并威胁我使用 MySQL 以换取它们的平安归来。然而,我需要检查旧数据库中的某些数据,所以我强制启动了禁用的 PostgreSQL 服务器:
./postgresql -f start
postgresql(ok)
如果你打包或安装自己的软件,我强烈建议编写你自己的 *rc.d* 脚本。花几分钟阅读现有的脚本将告诉你你需要知道的大部分内容。对于其余部分,请阅读 `rc.d(8)` 和 `rc.subr(8)` 手册页。
现在你已经可以启动 OpenBSD 了,让我们设置一些用户账户。
* * *
^([11]) 在 i386 和 amd64 系统上,这就是 MBR 出现的地方。
^([12]) 当然,一个远程键盘视频鼠标(KVM)系统可以给你所有这些功能,但非常少的 KVM 应用程序允许你从远程控制台复制和粘贴文本。这意味着你需要手动复制错误信息。
## 第六章 用户管理
*这个账户可以登录,*
*这个账户可以收邮件;*
*永远不要泄露 root 权限。*
 虽然互联网上的计算机入侵成为头条新闻,但系统管理员最大的安全威胁通常来自系统自身的用户。他们可能不会将您的数据发送到犯罪集团,但不满和不称职的用户会抓住机会破坏您的服务器——有时是出于恶意,但更常见的是出于无知。将安全视为机密性、完整性和可用性的组合,您会立即明白不受限制的系统访问权限的用户如何损害安全。
尽管您可能从《地狱中的糟糕操作员》中学到了什么,但系统是为用户而存在的,并且正确管理这些用户的账户是绝对必要的。在本章中,我们将介绍系统管理员最常见的任务之一:通过添加、删除、配置和修改用户账户来管理用户。
## Root 账户
近年来,有一种趋势是在只有单个用户的系统上使用特权的 root 账户进行日常任务。^([13)] 使用特权账户阅读您的电子邮件和浏览网页会增加您因用户错误和恶意攻击而面临的风险。虽然普通用户的疏忽按键只会产生一个`权限拒绝`错误,但 root 用户的相同按键可能会使您的系统无法使用并破坏所有数据。即使您是唯一使用 OpenBSD 系统的人,您也必须使用无特权的用户账户进行日常任务。
如果入侵者破坏了一个无特权的账户,潜在的损害仅限于该用户的权限。如果被破坏的账户处理您的电子邮件和网页书签,您可能只会遭受个人尴尬。但如果该账户处理系统管理任务,入侵者可以造成无限的系统损害并迫使您寻找备份。使用常规账户进行日常任务意味着您可以采取额外步骤来限制 root 账户。
使用执行任务所需的最小权限级别。如果您不需要 root 权限来执行任务,就不要使用它!例如,OpenBSD 的 Web 服务器以特定用户`www`的身份运行,而不是以 root 身份。如果入侵者入侵您的 Web 服务器并以`www`用户身份访问您的系统,他只能损坏`www`用户有权限写入的文件。同样,如果 Web 服务器软件进入错误状态并开始随机删除文件,这个相同的原则限制了它可以删除的文件。最小权限方法可以保护系统免受入侵者和自身软件的损害。
给每个用户提供特权访问权限的操作系统因此会产生更多问题。病毒的有效性、意外的配置错误,甚至崩溃都可以追溯到不必要的特权访问。OpenBSD 可能是世界上最安全的操作系统,但所有那些花哨的安全功能都无法保护你免受糟糕的系统管理实践的侵害。
使用 root 账户进行日常任务也会养成坏习惯。在压力下,人们会做他们练习的事情。如果你在桌面电脑上使用 root 账户进行日常工作,当你需要在生产服务器上执行日常任务时,你需要克服你的习惯。这种粗心大意不可避免地会导致安全问题。即使在我的 OpenBSD 桌面上,我作为唯一用户,我也特意以普通用户身份做所有事情,以培养和维持良好的系统管理员习惯。
## 添加用户
OpenBSD 使用许多标准的 UNIX 用户和密码管理程序,例如`passwd(1)`和`vipw(8)`。它还包括一个友好的交互式用户创建程序`adduser(8)`。我们将首先介绍`adduser`,然后探讨一些更高级的工具。
### 交互式添加用户
只有 root 用户可以运行`adduser`。如果你在命令行中启动`adduser`而不指定任何选项,它将进入一个友好的交互式对话框,你可以在此创建新用户。
#### 配置 adduser
`adduser`第一次运行时,会提出一系列问题以确定其默认设置。它保存这些默认设置,但你可以稍后更改默认设置。
adduser
Couldn't find /etc/adduser.conf: creating a new adduser configuration file
Reading /etc/shells
Enter your default shell: csh ksh nologin sh tcsh [ksh]: 1
Your default shell is: ksh -> /bin/ksh
Default login class: authpf bgpd daemon default staff
[default]: 2
Enter your default HOME partition: [/home]: 3
Copy dotfiles from: /etc/skel no [/etc/skel]: 4
Send welcome message?: /path/file default no [no]: 5
Do not send message(s)
Prompt for passwords by default (y/n) [y]: 6
Default encryption method for passwords: auto blowfish des md5 old
[auto]: 7
adduser 首先询问你首选的默认 shell。它读取*/etc/shells*以查看系统上安装的所有 shell。尽管我已经长时间使用 tcsh,但我通常以 OpenBSD 的标准 ksh**1**开始新用户。这样,他们就有了一个更接近全世界使用的 shell,并且他们很快就会了解到我无法回答有关他们 shell 的问题。
接下来,adduser 会询问你的默认登录类。我将在本章后面介绍登录类。现在,将新用户分配给默认登录类**2**。
如果你有一个默认的 OpenBSD 安装,你的用户主目录位于*/home*分区。如果不是,请在**3**处指定默认的主目录。
用户账户需要配置配置文件 (*.shrc*, *.login*, *.profile* 等)。如果你有一个包含自定义配置文件的目录,请在**4**处告诉 adduser。否则,只需接受默认设置。
尽管 OpenBSD 默认不包含欢迎信息,但你可以在系统上放置一条信息,以便新用户在第一次登录时就能收到一封电子邮件。将包含你的欢迎信息的文件的完整路径提供给 adduser**5**。
根据你创建用户账户的方式,你可能在创建用户账户时需要提供一个密码。未设置密码的账户将被禁用,直到分配密码。如果你在创建账户时不会分配密码,你可以在**6**处告诉 adduser 不要提示你。
最后,你可以选择用于哈希用户密码的加密算法,这些密码存储在 */etc/master.passwd* 中。除非你有特定的互操作性需求或知道自己在做什么,否则请接受默认值 **7**。
从现在开始,`adduser` 将使用这些选择的默认值。如果你想稍后修改默认值,请在 */etc/adduser.conf* 中进行更改。阅读 adduser(8) 手册页以获取配置文件选项的完整列表。
#### 创建用户账户
现在你已经设置了默认选项,再次运行 `adduser` 以创建用户账户。
首先分配一个用户名。许多人不合理地偏爱特定的用户名,询问他们是否有偏好是礼貌的。
Ok, let's go.
Don't worry about mistakes. There will be a chance later to correct any input.
Enter username []: pkdick
一旦你有了用户名,你将有机会输入用户的真实姓名或账户的预期用途。
Enter full name []: Phil Dick
你指定的 shell 是用户偏好的问题。shell 列表来自 */etc/shells*,增加了 `nologin` 选项。用户可以更改他们的 shell,除非你明确阻止,所以不必太担心你分配的哪个 shell。
Enter shell csh ksh nologin sh tcsh [ksh]:
接下来,选择一个用户 ID (UID) 号码。默认情况下,UID 编号从 1000 开始,`adduser` 使用可用的最低号码。如果需要,你可以更改此值以匹配某些本地标准。
Uid [1001]:
默认情况下,新用户被分配到与用户名同名的组。每个用户只能被分配到单个登录组(或主要组),但如果需要,你可以将用户账户分配到多个次要组。如果你想这个用户能够使用 root 账户,邀请该用户加入 `wheel` 组。其他常见组包括 `staff`、`users` 和 `operator`。
Login group pkdick [pkdick]:
Login group is ``pkdick''. Invite pkdick into other groups: guest no
[no]: wheel
为用户选择一个登录类别。如果你还不了解登录类别,请接受默认设置。我建议将管理员用户——例如,`wheel` 组中的用户——分配到 `staff` 类别。如果你是桌面用户,你希望属于 `staff` 登录类别。
Login class authpf bgpd daemon default staff [default]: staff
如果你将 `adduser` 设置为要求输入密码,它将要求你输入密码,然后再次要求确认。
Enter password []:
Enter password again []:
现在 `adduser` 将显示你选择的所有内容。
Name: pkdick
Password: ****
Fullname: Phil Dick
Uid: 1001
Gid: 1001 (pkdick)
Groups: pkdick wheel
Login Class: staff
HOME: /home/pkdick
Shell: /bin/ksh
OK? (y/n) [y]: y
在此阶段,要么接受要么拒绝用户。如果你接受,`adduser` 将创建新用户并询问你是否要创建另一个用户。
### 非交互式添加用户
如果你需要创建许多用户,你可能不想整天在 `adduser` 对话框中循环。如果你有脚本、cron 作业或添加用户账户的 Web 界面,你将想要非交互式地创建用户。`adduser` 的 `-batch` 标志启用此功能。在批处理模式下,`adduser` 需要四个额外的参数:用户名、用户名所属的组、全名和加密格式的密码。
adduser -batch username group 'Real Name' encryptedpassword
要批量创建我们的用户 `pkdick`,我们将运行以下命令:
adduser -batch pkdick wheel 'Phil Dick' NotThePassword
这里需要注意的是,`pkdick` 的密码不是 `NotThePassword`。`adduser` 预期我们提供一个散列到字符串 `NotThePassword` 的随机盐,而不是密码本身。有关生成加密密码的说明,请参阅 密码和批量模式。
#### 批量模式中的组
默认情况下,新用户会被分配一个与登录名相同的名字的主组。在批量模式下,你必须指定在命令行上想要的额外组。我们的示例用户 `pkdick` 是以 `pkdick` 的登录组创建的。如果你想为特定用户设置不同的登录组,请使用 `-group` 标志。
adduser -group guest -batch jgballard customers 'Jim Ballard' NotThePassword
你需要将用户添加到另一个组中。在这里,我给 `jgballard` 分配了 `guest` 的登录组,并将其添加到 `customers` 组。
要将用户分配到多个组,请使用逗号分隔组名。
adduser -batch jgballard customers,sftp-only 'Jim Ballard' NotThePassword
最终结果是 `jgballard` 被分配到 `jgballard` 的主组,并被添加到 `customers` 和 `sftp-only` 的次要组中。
#### 密码和批量模式
如果你实际遵循了之前的任何示例,你会创建一个没有已知密码的账户。现代类 Unix 操作系统不会以可读格式存储密码;相反,密码以密码的散列和随机盐的形式存储。当你为用户分配密码时,系统会取密码,添加盐,并执行一些可怕的运算以生成密码的散列。然后系统将这个散列和盐存储在 */etc/master.passwd* 文件中。当你尝试登录时,登录过程会取你的密码,添加盐,并计算这个组合的散列。如果计算出的散列与 */etc/master.passwd* 中存储的散列匹配,则允许登录。
示例中创建了一个密码散列为 `NotThePassword` 的账户。因为这个散列不是合法的散列,所以任何输入的密码都不会与之匹配。我们需要提供一个预先生成的加密密码,输入一个未加密的密码,让 `adduser` 为我们计算散列,或者创建一个没有密码的账户。
创建一个没有密码的新账户是最简单的选项。OpenBSD 将禁用该账户,直到你为其分配密码,但对于用于运行守护进程的账户或如果你有客服人员帮助新用户设置密码的情况,这是可以接受的。要创建一个没有密码的账户,只需在账户创建过程中省略密码即可。
adduser -batch pkdick wheel 'Phil Dick'
如果你想在命令行中输入未加密的密码,请使用 `-unencrypted` 选项。将此选项放在 `-batch` 选项之前。例如,要将 Phil 的账户密码设置为 `IsThePassword`,请输入以下内容:
adduser -unencrypted -batch pkdick wheel 'Phil Dick' IsThePassword
这个账户现在密码为 `IsThePassword`。你可以在脚本中使用或在没有人注意到你的时候使用。然而,密码将出现在系统的进程列表中,因此如果用户足够快地注意到,他们可以看到密码。
另一个选项是使用 `encrypt(1)` 生成预散列密码。默认情况下,`encrypt` 会给你一个空行。当你输入一个单词时,它会返回该单词的散列值。它默认使用在 `default` 登录类中定义的加密算法。(过去几年一直是 Blowfish。)你可以输入任意数量的单词,每个单词都会单独散列。按 CTRL-C 退出 `encrypt`。
encrypt
gerbil
$2a\(06\)V/VO91VVAKSNslesQNH6pezXsGhoKUMcnvWxyDOJUmWRk3fflX5cy
weasel
$2a$06$652ngShUnOBuFEL7X2yrf.E0U2GUw/FseVq/BkVgaiyqvp4wt.Nsy
^C
如果你只加密一个密码或交互式创建密码,给 `encrypt` 选项提供 `-p`。这会给你一个非回显的密码提示。
encrypt -p
Enter string:
$2a\(06\)nyA.mygoei/6VGS2tq4wA.VOzB6inwlK9pWOIAsiUWBkWf0CqOJ7.
#### 其他批量模式选项
我经常使用 `adduser` 的交互模式手动创建管理员账户(我不经常创建系统管理员账户)。其他人使用我编写的 `adduser` 批量模式脚本来创建非特权用户账户。*adduser.conf* 包含系统管理员的默认设置,然后我在脚本中覆盖这些设置。这种方法需要我更少的有机记忆,并确保非特权账户的一致性。
所有这些选项都必须出现在 `-batch` 参数之前的命令行上。`adduser` 将 `-batch` 之后的所有内容视为账户信息。
`-noconfig` 选项告诉 `adduser` 不要从 *adduser.conf* 中读取默认值。在脚本中使用此选项可以确保 *adduser.conf* 中的系统管理员友好默认值不会泄露到非特权账户中。
`-dotdir` 选项指定用户点文件的目录。此目录中的所有文件都将复制到新用户的家目录中。我经常为非特权用户准备特殊的点文件。
`-home` 选项告诉 `adduser` 在哪里创建新用户的家目录。这并不是实际的家目录,而是家目录将被创建的基础目录。例如,如果你的所有网站服务器客户的家目录都在 */www* 分区上,你可能使用 `-home /www`。
要分配非默认登录类,使用 `-class` 选项。
`-message` 选项提供新用户消息的路径。要关闭默认发送消息,使用 `-message no`。
要分配一个 shell,使用 `-shell` 和 */etc/shells* 中显示的 shell 名称,或者使用 `nologin` 来禁用登录。
可能你想为批量创建的用户分配特定范围内的 UIDs。也许所有客户都有一个大于 10000 的 UID,而系统管理员有一个千位数的 UID。使用 `-uid_start` 指定最小 UID,使用 `-uid_end` 指定最大 UID。如果可用,创建的登录组将被分配一个与 UID 相等的 GID。
### 用户账户限制
用户账户受到以下限制,这些限制在 `adduser(8)` 中有详细说明。
+ 用户名可以包含字符(最好是小写)和数字,以及非开头的连字符、点、下划线和结尾的 `$`。用户名长度不能超过 31 个字符。
+ 全名不能包含冒号 (`:`)。
+ 其他值必须存在于相关文件中:shell 必须出现在 */etc/shells* 中,登录类必须在 */etc/login.conf* 中,等等。
## 删除用户账户
删除不必要的用户账户与添加新账户一样重要。使用 `rmuser(8)` 来删除账户。
rmuser pkdick
Matching password entry:
pkdick:*:1001:1001::0:0:phil dick:/home/pkdick:/bin/ksh
Is this the entry you wish to remove? y
Remove user's home directory (/home/pkdick)? y
Updating password file, updating databases, done.
Updating group file: Removing group pkdick -- personal group is empty
done.
Removing user's home directory (/home/pkdick): done.
`rmuser` 命令显示来自 */etc/passwd* 的账户条目,给你一个机会来确认你真的想删除这个特定的用户。阅读账户的真实姓名,并确认你正在删除正确的账户。接下来,`rmuser` 会询问你是否想删除用户的家目录。如果你怀疑你可能需要该用户账户的一些文件,你可以选择暂时保留该目录。它会自动删除用户的 cron 作业和收件箱文件。
## 编辑用户账户
你根据当时所拥有的知识来创建具有特权的用户。你拥有的信息可能是不正确的,所以请习惯于编辑用户。在大多数情况下,`chpass(1)` 以用户友好的方式完成你需要的一切。
用户可以通过运行 `chpass` 而不带任何参数来编辑自己的账户。
$ chpass
$ Changing user database information for mwlucas.
Shell: /usr/local/bin/tcsh
Full Name: mwlucas
Office Location:
Office Phone:
Home Phone:
在这里,用户可以更新他们的 shell 或更改他们的目录信息。许多应用程序会忽略存储在 */etc/passwd* 中的目录信息(电话号码和办公地点),但在某些地方,这些信息很重要。进行更改,保存并退出。
如果你以 root 身份运行 `chpass` 并提供一个用户名作为参数,你会看到一个完全不同的画面。
chpass mwlucas
Changing user database information for mwlucas.
Login: mwlucas
Encrypted password: $2a\(08\)s2EVX.cAhYHskOaHk/4C5eLn76atAmGPU7z5DqRKAYe/V.OGgWXVi
Uid [#]: 1000
Gid [# or name]: 1000
Change [month day year]:
Expire [month day year]:
Class: staff
Home directory: /home/mwlucas
Shell: /usr/local/bin/tcsh
Full Name: mwlucas
Office Location:
Office Phone:
Home Phone:
在这里,你可以强制更改用户的密码(尽管有更好的方法来做这件事),shell、UID、密码过期时间等,以及所有用户的目录信息。
通过 `chpass` 做出的更改只会影响 */etc/passwd*、*/etc/master.passwd* 和 */etc/group*。如果你更改了用户的 UID、GID 或家目录,你也必须对用户拥有的文件及其家目录做出相应的更改;否则,用户的账户将无法正确工作。如果 */etc/passwd* 中将你的家目录列为 */newhome/mwlucas* 在 */etc/passwd* 中,但你的文件在 */home/mwlucas* 中,你将遇到麻烦。
注意,你不能仅使用任何文本编辑器编辑 */etc/master.passwd* 或 */etc/passwd*;你需要使用管理相应密码数据库的工具。如果你坚持手动编辑密码文件,你可以使用 `vipw(8)` 直接编辑 */etc/passwd*。如果你不熟悉 `vipw`,请坚持使用 `chpass`。`vipw` 最常见的用途是当密码文件损坏时,而最常见损坏密码文件的方式就是使用 `vipw`。
## 登录类
一个用户的 shell 可以用来限制用户能做什么,但 OpenBSD 提供了非常具体的基于登录类的访问控制。在 */etc/login.conf* 中设置的登录类定义了用户可访问的资源和信息。登录类还允许你控制密码长度和过期时间,以及外部认证机制。
每个用户都被分配到一个类别,每个类别都对可用资源设置限制。当您更改类别的限制时,新限制将在用户下次登录时应用于每个用户。在创建账户时定义用户的类别,或使用`chpass`更改它。
默认情况下,`*login.conf*`为用户提供两个类别,为守护进程提供一个类别,以及一些特殊案例类别。`default`用户类别赋予用户对系统资源的广泛访问权限,适用于具有有限数量的 shell 用户的机器。`staff`用户类别不对内存使用进行限制,对用户可以同时运行的过程数量设置非常高的限制,并允许用户在登录被禁止时登录。
如果这两个类别满足您的需求,并且您不会使用像远程身份验证拨号用户服务(RADIUS)或 Kerberos 这样的替代身份验证协议,您可以跳过这一节。如果不,请继续阅读。
### 登录类别定义
每个类定义由一系列变量赋值组成,描述了类的资源限制、身份验证和环境。类定义中的每个变量赋值都以冒号开始和结束。反斜杠字符表示类继续在下一行,这使得文件更易于阅读。
这里是`default`类的定义:
default:
1 :path=/usr/bin /bin /usr/sbin /sbin /usr/X11R6/bin /usr/local/bin /usr/local/sbin:
2 :umask=022:
3 :datasize-max=512M:
:datasize-cur=512M:
:maxproc-max=256:
:maxproc-cur=128:
:openfiles-cur=512:
:stacksize-cur=4M:
:localcipher=blowfish,6:
:ypcipher=old:
4 :tc=auth-defaults:
:tc=auth-ftp-defaults:
`default`类有几个变量。其中一些有相当明显的解释。例如,**1**处的`path`变量为用户的 shell 分配默认的命令搜索路径,通常用户可以看到它作为`$PATH`。**2**处的`umask`设置为用户的 shell 分配默认的 umask。用户可以覆盖这两个设置。
其他设置,如**3**中的`datasize-max`和`maxproc-max`,很难通过猜测来定义。我们将在下一节中介绍一些更常用的值。
与第五章中用于配置串行控制台客户端的`termcap` `tc` **4**变量在行为上相似,`default`类从`*login.conf*`中的其他位置复制`auth-defaults`和`auth-ftp-defaults`条目设置。
一些变量不需要指定值来触发行为;这些值只需通过添加到`*login.conf*`即可触发指定的行为。例如,存在`requirehome`意味着用户必须有一个有效的家目录才能登录。
### 修改`login.conf`
在许多 BSD 系统中,您必须使用`cap_mkdb(8)`将`*login.conf*`文件转换为程序友好的数据库文件`*login.conf.db*`。OpenBSD 不需要这样做。首先检查登录类别的程序会查找登录类别数据库,如果没有找到,它们会直接解析`*login.conf*`。您可以使用`cap_mkdb`创建这样的数据库,这将略微提高检查`*login.conf*`的软件的性能。
cap_mkdb /etc/login.conf
注意,一旦您创建了此数据库,每次编辑 *login.conf* 时都必须重新构建它。*login.conf.db* 中的数据库值将始终覆盖您的 *login.conf* 设置。或者,您可以删除 *login.conf.db* 并强制程序始终解析 *login.conf*。
我建议在现代硬件上跳过 `cap_mkdb`。
### login.conf 变量的合法值
*login.conf* 变量只接受非常特定的值,包括以下内容:
+ 文本文件或程序的完整路径
+ 一个以逗号分隔的环境变量列表
+ 以逗号分隔的值列表
+ 一个数字(在数字前加上 0x 以十六进制表示,或加上 0 以八进制表示)
+ 一个以空格分隔的路径名列表
+ 以字节(默认)、千字节(K)、兆字节(M)、吉字节(G)或 512 字节块(T)为单位的大小
+ 以秒(如果没有给出单位则假定)、分钟(m)、小时(h)、天(d)、周(w)或年(y)组合的时间
使用路径名的变量接受特殊符号波浪号(`~`)和美元符号(`$`)。波浪号后跟一个斜杠或用户的登录名,或路径名的末尾代表用户的家目录。您可以使用 `~/bin` 来表示用户家目录中的 `bin` 目录。美元符号代表用户名。例如,您可能使用 `/var/mail/$` 来表示用户的收件箱文件。
一些变量需要特定的值类型。用户家目录的路径必须是完整路径,而用户可以分配的内存量必须是大小。在大多数情况下,合法答案相当明显,但请检查 `login.conf(5)` 以获取可接受值的完整列表。
### 设置资源限制
资源限制允许您控制任何用户在任何时候可以独占的系统资源量。如果几百个用户登录到一台机器上,并且有一个用户决定编译 LibreOffice,那么这个人将消耗比他应得份额多得多的处理器时间、内存和 I/O。通过限制任何用户可以使用多少资源,您可以使得系统对所有用户都更加响应。
当计算设施非常昂贵,并且部门根据使用的计算时间收取费用时,资源限制更常被使用。如今,利用率会计并不那么重要。购买更多计算能力通常比配置会计或资源限制便宜。话虽如此,如果您有一个有缺陷的守护进程,有时会泄漏并开始消耗 CPU 时间或内存,给它一个登录类别可以防止它吞噬系统。
表 6-1 列出了一些资源限制的 *login.conf* 变量。
表 6-1. 表 6-1:一些 login.conf 资源限制
| 变量 | 描述 |
| --- | --- |
| `coredumpsize` | 核心转储文件的最大大小 |
| `cputime` | 任何进程可以使用的最大 CPU 时间 |
| `datasize` | 每个进程的最大数据大小 |
| `filesize` | 任何单个文件的最大大小 |
| `maxproc` | 最大进程数 |
| `memorylocked` | 每个进程的最大锁定核心内存使用量 |
| `memoryuse` | 每个进程的最大核心内存使用量 |
| `openfiles` | 每个进程的最大打开文件描述符数 |
| `stacksize` | 每个进程的最大堆栈大小 |
| `vmemoryuse` | 每个进程的最大虚拟内存使用量 |
资源限制通常是按进程设置的。如果您允许每个进程 200MB 的 RAM 并允许每个用户 40 个进程,那么您已经为每个用户分配了 8GB 的内存。也许您的系统有大量的内存,但它真的有那么多吗?
除了 `vmemoryuse` 之外的所有资源限制变量都支持最大和当前(建议)限制。当用户超过当前限制时,系统会警告用户,并且他们不能超过最大限制。这在多个用户共享资源但需要通知他们接近限制的协作系统中效果很好。
要指定当前限制,请将 `-cur` 添加到变量名中。要设置最大限制,请添加 `-max`。例如,要设置用户可以拥有的进程数当前和最大限制,请在类中使用此定义:
…
:maxproc-cur: 50:
:maxproc-max: 60:
…
在此类中的用户在使用超过 50 个进程时会收到警告,并且无法使用超过 60 个进程。如果您没有指定当前或最大限制,则它同时作为两者。
### 修改 Shell 环境
您可以在用户类中定义环境设置。这可能会比在默认的 shell 配置文件中设置它们更有效,因为更改会立即影响用户下一次登录时所有用户的环境。此设置将影响所有用户 shell,即使那些不读取 *.profile* 或 *.cshrc* 的 shell 也不例外。
表 6-2 列出了影响用户环境的常用用户类变量。
表 6-2. 表 6-2:一些 login.conf 环境变量
| 变量 | 描述 |
| --- | --- |
| `hushlogin` | 如果存在,登录期间不会提供任何系统信息。 |
| `ignorenologin` | 即使 */etc/nologin* 文件存在,用户也可以登录。 |
| `nologin` | 文件路径。如果文件存在,当用户尝试登录时,将显示文件内容并拒绝登录。 |
| `path` | 默认命令搜索路径。 |
| `priority` | 用户的优先级(nice)级别。参见 `renice(1)`。 |
| `requirehome` | 如果存在,用户必须拥有有效的家目录才能登录。 |
| `setenv` | 以逗号分隔的环境变量和值的列表。 |
| `shell` | 用户 shell。覆盖 */etc/passwd* 中的用户 shell 选择。用户的 `$SHELL` 环境变量反映 */etc/passwd*,导致环境不一致。玩弄这个设置是让用户烦恼的绝佳方式。 |
| `term` | 如果环境无法确定终端类型,则为默认终端类型。 |
| `umask` | 初始 umask。应始终以 0 开头。 |
| `welcome` | 包含登录消息的文件路径。 |
### 密码和登录选项
与用户环境不同,用户环境可以在多个不同位置进行配置,许多密码控制只能通过用户类进行配置。密码控制仅影响本地密码数据库,不影响轻量级目录访问协议(LDAP)、Kerberos、RADIUS 或其他远程密码数据库。
让我们来看看一些常用的密码控制。
> **`localcipher`**
>
> 这控制了`/etc/master.passwd`中使用的密码散列方法。默认是 Blowfish。除非您试图与特定的外国类 Unix 操作系统兼容,否则不要更改密码散列方法。有关支持的散列算法列表,请参阅`login.conf(5)`。
>
> **`login-backoff`**
>
> 这控制了用户快速记住密码的速度。在多次不成功的登录尝试之后,`login(1)`会减慢提供新的用户名和密码提示的速度。
>
> **`passwordcheck`**
>
> 这给出了检查新密码质量的外部程序的完整路径。OpenBSD 将密码通过标准输入传递给程序。程序预期返回 0 表示密码足够好,返回 1 表示密码不够好。OpenBSD 包含一个非常简单且有限的密码质量检查器;如果您需要密码质量检查器,请查看`passwdqc`(*/usr/ports/security/passwdqc*)。
>
> **`passwordtries`**
>
> 这是`passwd(1)`使用密码质量检查器的次数。如果用户在这多次尝试中无法提出一个足够复杂的密码,则仍然接受新密码。如果设置为 0,则只有当新密码通过质量检查时才接受。
>
> **`minpasswordlen`**
>
> 这是新密码的最小长度。密码长度并不是质量的衡量标准——一串 128 个`A`字符的密码仍然很糟糕,但它可能有助于满足网站要求。
>
> **`passwordtime`**
>
> 这是密码的最大有效期,以秒为单位。使用此选项可以要求定期更改密码。
>
> **`password-warn`**
>
> 这是`login(1)`开始警告用户密码即将到期的时间长度,以秒为单位。
>
> **`password-dead`**
>
> 这是密码过期后,用户可以最后一次登录以重置自己的密码的时间长度,以秒为单位。如果用户没有重置密码,则无法登录。这是一个最后的宽限期;如果用户错过了这个机会,则需要系统管理员干预来重置密码。
### 更改认证方法
OpenBSD 支持许多不同的认证机制,例如本地密码文件、Kerberos、S/Key、RADIUS 等。在用户类定义中指定所需的认证方法,OpenBSD 将使用它。这个系统背后的机制被称为*BSD 认证*。
设置认证机制并不配置认证机制。例如,配置登录类通过 Kerberos 进行认证并不会神奇地建立 Kerberos 域。如果指定的认证方法不可用,配置使用该方法的类将无法登录。
并非所有认证方法都与所有协议互操作。例如,虽然 SSH 可以与物理令牌一起工作,但它不与允许用户更改密码但不允许登录的 `lchpass` 认证协议一起工作。请查阅每个认证方法的手册页以获取详细信息。
一些认证方法需要额外的配置。例如,如果您想使用 RADIUS 认证,您必须告诉您的系统在哪里找到您的 RADIUS 服务器。特殊 *login.conf* 变量和它们的用途在认证方法的手册页中有文档说明。
表 6-3 列出了 OpenBSD 内置的 BSD 认证支持的认证方法。
表 6-3. 表 6-3:BSD 认证方法
| 方法 | 手册页 | 描述 |
| --- | --- | --- |
| `activ` | `login_activ(8)` | 通过 ActivCard 令牌进行认证 |
| `chpass` | `login_chpass(8)` | 修改密码,无 shell |
| `crypto` | `login_crypto(8)` | 通过 CRYPTOCard 令牌进行认证 |
| `krb5` | `login_krb5(8)` | 通过 Kerberos 进行认证 |
| `krb5-or-pwd` | `login_krb5-or-pwd(8)` | 尝试 Kerberos,然后本地密码数据库 |
| `lchpass` | `login_lchpass(8)` | 修改本地密码 |
| `passwd` | `login_passwd(8)` | 对本地密码文件进行认证 |
| `radius` | `login_radius(8)` | 对 RADIUS 服务器进行认证 |
| `reject` | `login_reject(8)` | 请求密码,然后拒绝登录 |
| `skey` | `login_skey(8)` | 通过 S/Key 进行认证 |
| `snk` | `login_snk(8)` | 通过 SecureNet 令牌进行认证 |
| `token` | `login_token(8)` | 通过 X9.9 令牌进行认证 |
| `yubikey` | `login_yubikey(8)` | 通过 Yubico YubiKey 令牌进行认证 |
端口集合(在第十三章中讨论)包含一些额外的登录方法,例如指纹扫描仪 (*sysutils/login_fingerprint*),OATH 一次性密码 (*sysutils/login_oath*),以及 LDAP 集成 (*sysutils/login_ldap*)。您还可以创建自己的自定义认证方法;有关详细信息,请参阅 `login.conf(5)`。
使用 *login.conf* 中的 `auth` 变量设置认证方法:
:auth=token,passwd:\
具有此设置的类中的用户尝试通过 X9.9 令牌进行认证。如果不可能,系统将回退到本地密码数据库。
BSD 认证支持不同守护进程的不同认证方法。您可以在 `auth` 关键字后指定服务名称,表示此组认证方法仅适用于该特定服务。您经常会看到像 `auth-ssh` 和 `auth-su` 这样的登录类。
这里是从默认的 *login.conf* 文件中的一些示例条目:
Default allowed authentication styles
auth-defaults:auth=passwd,skey:
Default allowed authentication styles for authentication type ftp
auth-ftp-defaults:auth-ftp=passwd:
这定义了 `auth-defaults` 类,只有一个条目。默认情况下,此类用户首先使用密码认证,然后使用 S/Key 认证。`auth-ftp-defaults` 类将 `auth-ftp` 定义为使用密码数据库,并且仅使用密码数据库。
在本章前面,我提到默认类包括其他两个类。这些是 `auth-defaults` 和 `auth-ftp-defaults` 类。默认 *login.conf* 文件中的其他每个登录类都通过引用包含它们。如果您更改 `auth-defaults` 类使用的认证方法,该更改将适用于其他所有登录类。
### 使用登录类进行 RADIUS 认证
我与 RADIUS 有着长期的爱恨情仇。它是认证协议中的最低共同分母。几乎每个操作系统和硬件设备都支持它,但它是一个挑剔的协议,有无数边缘情况。幸运的是,将 OpenBSD 配置为 RADIUS 客户端很简单。任何 RADIUS 服务器都可以为 OpenBSD 提供认证服务。
我鼓励您使用其他登录服务,例如 LDAP 或 Kerberos,而不是 RADIUS。但在某些情况下,对于某些用户,RADIUS 是足够的。RADIUS 与微软的互联网认证服务结合使用,可以轻松实现与本地 Windows 域的密码同步,并减少您的支持负担。
首先,阅读 `login_radius(8)`,然后配置您的 RADIUS 服务器以允许来自您的 OpenBSD 主机的访问。要配置 RADIUS 认证,您需要 RADIUS 服务器的 IP 地址、RADIUS 运行的端口以及一个共享密钥。(出于历史原因,最好明确指定 RADIUS 端口,而不是依赖于 */etc/services*。)在我们的示例中,RADIUS 服务器是 192.0.2.2,端口是 1812,密钥是字符串 `Insubordination88`。
首先,创建一个目录来存放服务器配置文件,并适当地设置其权限,如 `login_radius(8)` 所述。
mkdir /etc/raddb
chgrp _radius /etc/raddb/
chmod 755 /etc/raddb/
现在创建文件 */etc/raddb/servers*。此文件应包含一个服务器及其密钥,每行一个。我们的 *servers* 文件只有一行:
192.0.2.2 Insubordination88
现在将 *login.conf* 更改为默认使用 RADIUS。
auth-defaults:
:auth=radius:
:radius-port=1812:
:radius-server=192.0.2.2:
`auth-defaults` 类是 OpenBSD 的默认认证类。如果我们更改它,就会改变其他每个类进行认证的方式。我们将 `auth` 类型设置为 `radius`,并设置端口和服务器。
保存文件后,OpenBSD 将尝试将所有用户帐户与 RADIUS 服务器进行认证。您可能想要将 `auth-ftp` 类更改为匹配。^([14)]
在您确认一切正常之前,请保持 SSH 会话以 root 身份登录,以便您可以更改 *login.conf*。否则,您可能会将自己锁在系统之外,至少是 root 帐户之外。如果您无法进入系统,您需要以单用户模式重新启动并编辑 *login.conf*。
改变所有用户的身份验证方案可能也不是所希望的。你可能希望 `authpf(8)` 用户通过 RADIUS 进行身份验证,但让 `staff` 类别的用户通过本地密码数据库进行身份验证。也许你不想让 root 账户通过 RADIUS 进行身份验证,因此需要一个指向本地密码数据库的 `auth-su` 登录类。使用登录类,你可以配置用户身份验证以适应你的特定需求。
## 无权限用户账户
无权限用户账户是指没有任何程序或文件权限的用户账户。许多程序以无权限用户身份运行或使用无权限用户执行特定任务。这些无权限用户仅获得执行有限任务所需的权限。
“仅执行有限任务所需的权限”听起来像是每个用户账户,不是吗?这是真的,但最低权限的人类用户账户仍然比许多程序需要的权限要多。任何具有 shell 访问权限的用户通常都有一个家目录。用户可以在家目录中创建文件,运行文本编辑器,处理电子邮件,运行脚本,以及编译(如果未安装)软件。一个普通的 shell 用户需要这些最小权限,但程序不需要。通过让程序以非常受限的用户身份运行,你可以控制软件或入侵者对系统造成的损害程度。
OpenBSD 默认包含几个无权限用户。查看 */etc/passwd*,你会看到 `sshd`、`named`、`_ntp` 等账户。这些都是由特定服务器守护进程使用的无权限账户。检查它们,你会发现一些共同的特征。
无权限用户没有正常的家目录。大多数用户共享 */var/empty* 的家目录,该目录归 root 所有,除了一个日志套接字外不包含任何内容。用户无法写入的家目录使得账户不够灵活,但对于大多数服务器守护进程来说已经足够好。如果这些用户在系统中拥有文件,文件权限通常被设置为用户无法写入。
同样,没有人应该使用这些账户登录系统。如果 `named` 用户账户是为 DNS 子系统保留的,那么为什么有人需要实际以该账户登录呢?无权限用户被分配了一个禁止登录的 shell:*/sbin/nologin*。
所有这些如何增强系统安全性?以常见的入侵向量——网页服务器为例。OpenBSD 以用户 `www` 运行其网页服务器。假设入侵者发现你的网站存在安全漏洞,并可以利用这个漏洞使网页服务器执行任意代码。这是一个安全噩梦;我们的入侵者现在可以让服务器程序做它能力范围内的一切。但网页服务器到底有多大的能力呢?
命令提示符比网站能造成更多的破坏和混乱,所以入侵者可能会尝试访问系统上的命令提示符。`www` 用户有一个不允许命令提示符的 shell。虽然这并不能完全阻止入侵者获得命令提示符,但它确实使这个过程变得更加困难。
但我们的入侵者很聪明。通过真正出色的入侵技巧,他让 Web 服务器打开一个高编号端口,将客户端放入 root shell 中。现在他有了访问命令提示符的能力,可以造成无法估量的破坏……但他能吗?
他没有家目录,也没有创建一个目录的权限。他想要存储的任何文件都必须放入全局可访问的目录,例如 */tmp* 或 */var/tmp*,这增加了他的可见性。Web 服务器配置文件不是由 `www` 用户拥有的。即使入侵者有进入 Web 服务器的路径,他也不能重新配置它。他不能更改网站文件,因为 `www` 用户并不拥有它们。实际上,`www` 用户无法访问系统上的任何东西。此外,OpenBSD 内置的 Web 服务器会将自己 `chroot`。入侵者已经进入了 Web 服务器程序,现在他必须逃离 `chroot` 并渗透一个特权程序。
他能渗透你的系统吗?可能,但会困难得多。如果他专门针对你或你的公司,他可能会费这个劲。然而,如果他只是在寻找容易的目标,他可能会放弃,去烦扰那些运行 Linux 或 Windows 系统的人。
使用非特权用户并不能解决所有安全问题,请注意。被破坏的 `www` 用户可以查看 Web 应用程序源文件。如果你的应用程序编写得不好,或者数据库密码硬编码在隐藏文件中,你仍然会遇到麻烦。但如果你不使用编写不良的应用程序,并且已经保持了系统的更新和修补,入侵者将很难渗透你的服务器其他部分。
### 无权限账户
第一个非特权账户是 `nobody`。它是为了使用网络文件系统(NFS,在第九章中讨论)来映射外系统上 root 所拥有的文件而创建的。第九章。几十年前,人们开始将 `nobody` 作为通用非特权用户使用,以 `nobody` 身份运行 Web 服务器、代理服务器和其他守护进程。虽然这比以 root 身份运行那些程序要好,但这仍然是一种不良做法。如果入侵者渗透了那些程序之一,他将能够访问 `nobody` 所拥有的所有进程。我们假设的 Web 服务器入侵者突然不仅能够访问 Web 服务器,还能访问数据库、NFS 或任何以 `nobody` 身份运行的程序!
每个需要以用户身份运行的守护进程都需要自己的无权限账户——使用无权限用户的主要目的是最小化单个软件可能造成的损害。要广泛使用它们。OpenBSD 为从 `finger(1)` 到音频系统这样的服务提供了离散的无权限用户。遵循这个例子。
### _ 用户名
如果你查看 */etc/passwd*,你会看到许多无权限用户的名字前有一个下划线,例如 `_syslogd`、`_ldapd` 和 `_dhcp`。这是 OpenBSD 用于识别无权限用户的约定。大多数附加软件也使用以下划线开头的无权限用户名,例如 `_mysql` 和 `_postgresql`。
并非所有无权限用户名都以下划线开头。其中一些是 OpenBSD 为了兼容性保留的遗留用户,例如 `nobody`。其他用户有悠久的历史或支持不灵活的软件,更改它们可能弊大于利。
下划线的存在意味着用户没有权限。没有下划线则没有任何意义;用户可能是一个普通账户,也可能是一个没有权限的账户。如果你创建了没有权限的用户,你不需要包含一个前置下划线,但这样做将有助于其他系统管理员理解该用户的作用。
### 创建无权限用户
这里是用于无权限用户的常见设置。你可以根据需要更改这些设置以适应你的应用程序。
+ ****用户名****。分配一个与用户功能相关的用户名,这样你就可以轻松识别它。给无权限用户一个像 `_fgcrl` 这样的用户名可能看起来是一种隐藏其目的的好方法,但它会混淆你的系统管理员,入侵者会很快发现它。
+ ****家目录****。*/var/empty* 是无权限用户的常见设置。
+ ****shell****。*/sbin/nologin* 是无权限用户的常见设置。
+ ****UID/GID****。为你的自定义无权限用户选择一个特定的 UID 和 GID 范围。OpenBSD 为系统分配的无权限用户保留了所有低于 1000 的 UID。
+ ****全名****。分配一个描述用户角色的名称。
+ ****密码****。使用 `chpass(1)` 为用户分配一个单个星号作为加密密码。这将禁用账户密码。
这些设置确实使你的无权限用户非常没有权限。你可以使用 `adduser(8)` 设置除密码之外的所有这些选项。
现在你已经了解了如何创建、管理和使用用户账户,让我们讨论如何管理有权限的用户。
* * *
^([13]) 这可能是从微软文化中泄露出来的,在许多年里,每个用户都有管理权限。
^([14]) 或者你可能不想进行这个更改。FTP 以明文形式传输密码,因此你可能想为 FTP 连接使用单独的密码源。为什么在一个协议上安全地传输密码,而在相邻的端口上不安全地传输?
## 第七章。根用户,以及如何避免它
*万恶之源*
*永远都不会离你太远。*
*sudo 可以救你的命。*
 大多数类 Unix 操作系统的安全性长期以来一直被认为是大粒度的。一个超级用户,root,可以做任何事情。其他用户则是低微的佃农,忍受着 root 强加在他们身上的枷锁。问题是 root 没有多少枷锁,而且它并不能很好地个性化它所拥有的枷锁。一些操作系统使用 POSIX 访问控制列表(ACLs)来提供更细粒度的访问控制,但这些配置起来很困难.^([15])
虽然类 Unix 操作系统的确没有详细的访问控制,但事实是大多数人并不麻烦去使用那些确实存在的控制。幸运的是,你可以结合组和权限来安全地处理几乎所有问题。
## 根用户密码
root 用户拥有系统,并对每一块硬件以及某些需要绝对控制的动作拥有绝对权力,例如操作内核和更改认证源。你需要 root 权限来执行这些任务。你可以以 root 身份登录,使用`su(1)`成为 root,或者使用`sudo(8)`(本章后面将讨论)来获取某些 root 级别的权限,而不必实际使用 root 账户。
要使用 root 密码,你可以要么在控制台登录提示符下以 root 身份登录,要么如果你属于`wheel`组,以自己的身份登录并使用切换用户命令`su(1)`。在这两种方法中,我推荐使用`su`;它会记录谁在使用它,并且当你从远程系统登录时也可以使用。要使用`su`,请运行以下命令:
$ su
Password:
当提示时,输入 root 密码。现在使用`id(1)`检查你的当前用户 ID。
id
uid=0(root) gid=0(wheel) groups=0(wheel), 2(kmem), 3(sys), 4(tty), 5(operator), 20(staff), 31(guest)
如你所见,UID 是 0,这意味着你现在拥有系统,而且我确实是指*拥有*它。仔细考虑每一个按键。粗心大意可能会让你的硬盘回到原始的、未格式化的状态。
只有在`wheel`组中的用户才能使用 root 密码通过`su`成为 root。如果你将 root 密码给了没有物理控制台访问权限且在`wheel`组中的用户,他们可以多次输入`su`和 root 密码,但这不会起作用。(但任何人都可以在系统控制台使用 root 账户和密码,所以不要养成在办公室到处泄露 root 密码的习惯。)
要求加入组才能使用 root 密码引发了一个问题:“谁需要 root 访问权限?”root 是配置 OpenBSD 许多部分所必需的,但一旦系统正常运行,你可以大大减少或停止使用 root。对于任何绝对需要 root 的剩余任务,请使用`sudo`。
## 使用组
减少对 root 需求的最简单方法之一是使用组。类 Unix 操作系统将用户分类到组中,这些组由执行类似管理功能的用户账户组成。例如,你可以定义一个名为 `dnsadmins` 的组,并将编辑 DNS 区域文件的每个用户的账户添加到该组中。然后,通过适当地设置区域文件及其目录的权限,该组的成员可以编辑区域文件并重新加载名称服务器,而无需 root 密码。好消息是,你可以为几乎任何系统功能创建这样的组,从而避免给这些用户 root 访问权限。以这种方式使用组是一种强大且常被忽视的系统管理工具。我使用组来管理我自己的服务器——仅仅因为我 *可以* 使用 root,并不意味着我 *想要* 使用 root。用户可以通过使用 `id(1)` 来识别他们所属的组。
id
uid=1000(mwlucas) gid=1000(mwlucas) groups=1000(mwlucas), 0(wheel), 2005(dnsadmin)
我的 UID 是 1000,我的用户名是 `mwlucas`。我的 GID,即主要组 ID,也是 1000,并且也命名为 `mwlucas`。我还在 `wheel` 和 `dnsadmin` 组中。
### /etc/group 文件
文件 */etc/group* 包含所有组信息。每一行包含四个由冒号分隔的字段:组名、密码、ID 号和成员列表。
wheel:*:0:root,mwlucas,pkdick
*组名* 是组的一个便于人类理解的名字。这个组被命名为 `wheel`。组名完全是任意的,如果你想,你可以将一个组命名为 `lickspittles`,但你应该选择一个能让人想到组目的的名字。虽然你可能记得 `lickspittles` 可以编辑公司网页,但这个组名对你的同事来说有意义吗?如果有,你可能需要更好的同事。
第二个字段是组密码,这是一个伟大的理论,一旦暴露在现实世界中就变成了令人震惊的实践。现代类 Unix 系统对组密码不做什么处理,但这个字段仍然保留,因为旧程序期望在这里找到一些内容。星号只是一个占位符,用来安抚这样的软件。(虽然 OpenBSD 可以消除这个字段,但一些企业会在操作系统之间共享 */etc/group*。)
第三个字段给出了组的唯一数字 GID。许多程序使用 GID 而不是名称来识别组。`wheel` 组的 GID 为 0。最大的 GID 是 232,即 4,294,967,296。
最后是一个用逗号分隔的该组所有用户的列表。正如你所见,用户 `root`、`mwlucas` 和 `pkdick` 都是 `wheel` 组的成员。要向组中添加用户,请将他们的用户名添加到这个列表中,但请记住,没有任何 */etc/group* 条目可以包含超过 200 个用户或超过 1024 个字符。
### 创建组
为了创建一个新组,你需要一个名称和 GID 号码。OpenBSD 通常将下一个可用的 GID 分配给 GID 小于 1000 的新创建的组,因为 GID 小于 1000 的是为 OpenBSD 保留的。需要专用 GID 的 OpenBSD 包含的程序使用小于 1000 的 GID。通过 OpenBSD 软件包系统或 ports(在第十三章中讨论)安装的软件在 500 到 1000 范围内分配专用 GID。用户账户的 GID 从 1000 开始并向上。如果你为特殊角色创建组,从高 GID 开始,这样这些管理组将明显不同于用户账户。
### 组、非特权用户和组权限
创建新组的最简单方法是用 `adduser` 命令为该角色创建一个非特权用户,并使用该用户的组来分配文件权限。与任何其他非特权用户一样,给这个账户分配 */var/empty* 作为家目录和 `nologin` 作为 shell。不要将此用户添加到任何其他组中(特别是不要添加到 `wheel` 组)。最后,让 `adduser` 禁用此账户。当然,shell 会阻止登录,但多一层防御也不会有害。
现在你已经有一个管理用户和一个组,你可以分配文件所有权。每个文件都有一个用户和一个组拥有。要查看现有文件的权限,包括隐藏文件,使用 `ls -la` 命令。(如果你忘记了文件所有权和权限是如何工作的,请阅读 `ls(1)` 和 `chmod(1)`。)许多系统管理员专注于文件所有权和所有者权限,在全局权限上花费的时间较少,并且像它们不存在一样忽略了组权限。仔细查看以下示例 DNS 文件。
ls -la
total 22
drwxr-xr-x 2 mwlucas wheel 512 Apr 16 22:02 .
drwxrwxrwt 8 root wheel 512 Apr 16 22:00 ..
-rw-rw-r-- 1 mwlucas mwlucas 14595 Apr 16 22:02 michaelwlucas.com.db
-rw-r----- 1 mwlucas wheel 198 Apr 16 22:02 rndc.key
此目录包含两个文件。文件 *rndc.key* 可以被用户 `mwlucas` 读写;`wheel` 组中的任何人都可读取 *rndc.key*;其他人甚至无法读取。文件 *michaelwlucas.com.db* 可以被用户 `mwlucas` 或 `wheel` 组中的任何人读写,但其他人只能读取。如果你在 `mwlucas` 组中,你可以编辑此文件。
假设我想让我的初级 DNS 管理员能够编辑区域文件,但不能编辑 `rndc(8)` 配置。文件权限是正确的,但我需要文件的所有权属于我的 DNS 管理用户 `dnsadmin`。我还想让我 DNS 管理员能够创建新的区域文件,因此他们需要在目录上拥有写权限。以下是我将如何做到这一点:
chown dnsadmin:dnsadmin michaelwlucas.com.db
chgrp dnsadmin rndc.key
chown dnsadmin:dnsadmin .
chmod 775 .
ls -la
total 22
drwxrwxr-x 2 dnsadmin dnsadmin 512 Apr 16 22:02 .
drwxrwxrwt 8 root wheel 512 Apr 16 22:08 ..
-rw-rw-r-- 1 dnsadmin dnsadmin 14595 Apr 16 22:02 michaelwlucas.com
-rw-r--r-- 1 root dnsadmin 198 Apr 16 22:02 rndc.key
如你所见,这些文件现在归用户 `dnsadmin` 和组 `dnsadmin` 所有。组 `dnsadmin` 中的任何人都应该能够在不使用 root 密码的情况下编辑 *michaelwlucas.com.db* 文件。用户 `named`——DNS 服务器的非特权用户——应该能够读取这两个文件。将你的 DNS 管理员添加到 */etc/group* 中的 `dnsadmin` 组,他们就不需要 root 密码来完成他们的工作。
然而,这种模型也有局限性。虽然初级管理员无法意外编辑 *rndc.conf*,但他们可以删除和替换它。最好是将该文件放在一个他们可以读取但不能编辑的目录中。而且,尽管我们的 DNS 管理员可能认为他们需要 root 密码来重新启动名称服务器,但他们错了。使用 `rndc(8)` 来管理 DNS 服务器;其他任务可以通过 `cron(8)` 或通过 `sudo` 来管理。
## 使用 `sudo` 隐藏 root 访问
尽管正确使用组可以几乎消除编辑文件时需要 root 访问的需求,但这对于只能由 root 运行的命令没有帮助。您可以设置一个 cron 作业,每天午夜重新加载名称服务器,但每款软件偶尔都需要手动重启。因为 root 是全有或全无的事情,所以那些只有一项小任务要执行的人传统上需要 root 密码。
OpenBSD 包含 `sudo(8)` 及其相关工具,这些工具实现了仅对特定用户可运行的命令的细粒度访问控制。当配置正确时,`sudo` 允许普通用户以其他用户身份运行特定程序,包括 root。配置不当,`sudo` 允许完全的 root 访问。我将解释一个基本的 `sudo` 设置,它涵盖了几乎所有用途,但请记住,还有许多其他可能的组合。而且不要害怕阅读 `sudo(8)`、`sudoers(5)` 以及 `sudo` 主页上的文档([`www.gratisoft.us/sudo/`](http://www.gratisoft.us/sudo/)*)。
### 为什么使用 `sudo`?
`sudo` 工具提供的功能远不止细粒度权限控制。通过 `sudo` 运行的每个命令都会被记录,这使得跟踪谁做了什么变得非常容易。高级系统管理员可以更改 root 密码,而无需将其提供给具有 root 级别访问权限的人。
`sudo` 配置文件旨在跨多个系统共享,因此一个 `sudo` 策略可以覆盖您的整个网络和每个操作系统。诚然,在具有独特目录布局的操作系统上使用单个 `sudo` 配置会遇到麻烦,例如 Mac OS X,但您可以在 OpenBSD、其他 BSD、Linux 以及甚至 OpenSolaris 或 AIX 之间轻松共享一个配置。
### `sudo` 的缺点
`sudo` 最常见的问题是让用户接受它。那些历史上可以访问 root 账户的人认为,通过 `sudo` 工作会“失去一些东西”。
克服这一问题的关键是只给用户授予他们完成任务所需的访问权限。一个抱怨权限不足的初级管理员要么超出了他的职责范围,要么需要更多的权限。发现人们实际做什么的一个可靠方法是实施一个最小的 `sudo` 权限方案,并等待投诉。如果没有人投诉,那么他们可能并没有非常努力地工作。
`sudo` 的配置语法可能会令人困惑,因为它的配置与任何其他配置文件都不太相似,而且一开始要完全正确可能很困难。然而,配置文件实际上非常适合其目的。一旦你理解了它,调整权限就会变得快速且容易。
### 注意
更严重的是,一个有缺陷的 `sudo` 设置可能会给人一种安全的感觉,同时留下用户成为 root 的漏洞。确保每次更改后都测试 `sudo`,并避免我在此处记录的常见配置错误。
一些用户会尽力扩大他们的访问权限,没有其他原因,只是想看看他们是否能智胜你。这些用户最好通过仔细的配置、管理政策和一根板球棒来管理。
### sudo 软件概述
`sudo` 程序是一个 `setuid` root 包装器,可以以任何其他用户身份运行命令。通过提供你想要运行的命令来使用 `sudo`。
$ sudo /etc/rc.d/named restart
`sudo` 软件将所需的命令(在这种情况下,`/etc/rc.d/named restart`)与其内部权限和特权列表进行比较。如果配置文件允许特定用户以 root 身份运行该命令,`sudo` 就会以 root 身份运行它。而且,因为 root 可以以任何用户身份运行任何命令,`sudo` 也可以以任何任意系统用户身份运行命令。你可以利用这个事实来授予任何用户以选择用户身份运行特定命令的能力;例如,某些数据库服务器的管理员必须经常以数据库用户身份运行命令。
`sudo` 软件是一个包含四个部分的套件。第一个部分是实际的 `sudo(8)` 命令,`setuid` root 包装器。第二个是配置文件 */etc/sudoers*,它描述了谁可以以什么用户身份运行哪些命令。(*/etc/sudoers* 在 `sudoers(5)` 中有完整文档。)第三个是 `visudo(8)` 命令,它在一个编辑器中打开 */etc/sudoers* 并在退出前检查配置文件语法。最后,`sudoedit(8)` 程序专门用于以其他用户身份编辑文件。
### visudo(8) 命令
如果 */etc/sudoers* 包含错误的语法,`sudo` 将无法运行。如果你依赖 `sudo` 来提供对系统的 root 级别访问,而你又破坏了 *sudoers* 文件,你将无法访问 root 账户,并失去纠正错误的能力。这是很糟糕的。
幸运的是,`visudo(8)` 程序通过锁定 */etc/sudoers* 以防止同时有人编辑配置文件来提供一些保护,这样只有一个人可以同时编辑配置文件。然后,它会在文本编辑器中打开 */etc/sudoers*(默认为 vi,但会尊重 `$EDITOR` 环境变量)。做出你的更改并保存你的工作。当你退出编辑器时,`visudo` 应该解析文件以检查语法正确性。
如果 `visudo` 检测到错误,它会打印出有问题的行号,并询问你想要做什么。
/etc/sudoers: syntax error near line 34 <<<
What now?
在这里,我在第 34 行附近犯了一个错误。我可以重新编辑文件以修复错误,退出而不保存任何更改,或者强制 `visudo` 接受此文件。
按 E 键,`visudo` 应该返回到编辑器。转到有问题的行,修复你的错误,保存文件,然后再次退出编辑器。
输入 X 键,`visudo` 应该退出并恢复配置文件到其原始状态。你的更改将会丢失,但这可能是可以接受的。拥有一个旧的、可工作的配置比一个新出故障的配置要好。
按 Q 键会强制 `visudo` 接受文件,包括所有错误的语法。如果 `sudo` 无法解析 `/etc/sudoers`,它将立即退出。本质上,你是在告诉 `visudo` 在你以 root 身份登录并修复问题之前破坏 `sudo`。如果你认为你对 `/etc/sudoers` 的理解比 `visudo` 更好,你可能是错的。即使你是对的,你也是错的。
`visudo` 程序并不能保证配置会按照你的期望执行,它只能确保配置能够解析并且是有效的。一个正确格式的配置,声明“任何人不得通过 `sudo` 执行任何操作”,对于 `visudo` 来说是完全可以接受的。
### `/etc/sudoers` 文件
`/etc/sudoers` 文件决定了谁可以以哪个用户身份运行哪些命令。永远不要直接编辑 `/etc/sudoers`,即使你认为你确切知道要做的更改。始终使用 `visudo` 来更改 `/etc/sudoers`。
你会发现的各种 `sudoers` 样本配置通常非常复杂,因为它们展示了 `sudo` 可以做的一切巧妙的事情。然而,在这个阶段,你只想做简单、无聊的事情,比如给特定的用户授权运行特定的命令。而基本的语法非常简单。每个 `sudoers` 规则都遵循以下格式:
Username host=command
+ `*`用户名*` 是可以执行命令的用户名,是用户名的别名,或者是一个系统组。
+ `*`主机*` 是此规则应用到的系统的主机名。你可以将 `/etc/sudoers` 在多个系统之间共享。此条目允许按主机设置规则。
+ `*`命令*` 空间列出了此规则应用的命令。你必须列出每个命令的完整路径,否则 `sudo` 将不会识别它。如果不是这个要求,某个不可信的人可能会调整他的 `$PATH` 来访问命令的重命名版本。
例如,假设我信任用户 `sbaxter` 可以以 root 身份在任意系统上运行任何命令。我使用关键字 `ALL` 来匹配主机和命令的所有可能选项:
sbaxter ALL=ALL
作为首席系统管理员,我应该知道我分配给 `sbaxter` 的职责,以及他确切需要哪些命令。假设 `sbaxter` 是我的 DNS 从属。我通过组权限控制实际编辑区域文件,但有许多合法场合他需要停止、重启或以其他方式处理名称服务器程序。我希望他使用系统脚本 `/etc/rc.d/named` 来完成这项任务,而这个 `sudoers` 条目给了他在所有机器上使用该脚本的权限。
sbaxter ALL=/etc/rc.d/named
如果我将这个文件在多台机器之间共享,那么很可能许多机器甚至没有运行名称服务器。为了限制我的从属机器只能访问 DNS 服务器,我会更改主机字段。
sbaxter dns1=/etc/rc.d/named
然后,`sbaxter` 是电子邮件服务器 `mail1` 的管理员。这个服务器是他的责任,因此他需要运行任何命令。我可以在邮件服务器上为他设置完全不同的权限,同时在所有系统上使用相同的 *sudoers* 文件。
sbaxter dns1=/etc/rc.d/named
sbaxter mail1=ALL
是的,`sbaxter` 可以在 `mail1` 上使用 `visudo`,但他已经拥有该机器上的全部权限。我对此感到满意,因为他知道我会对他负责任何停机时间。
#### sudoers 字段中的多个条目
在单个字段中用逗号分隔多个条目。例如,过了一段时间,我厌倦了 `sbaxter` 让我在 DNS 服务器上挂载 NFS 共享,所以我将 `mount_nfs` 添加到他的权限中。
sbaxter dns1=/etc/rc.d/named,/sbin/mount_nfs
他现在可以挂载他自己的 NFS 共享,并且不再打扰我。
#### 以非 root 用户运行命令
在命令之前指定一个用户名,用括号括起来,表示该用户可以使用 `sudo` 以特定用户身份运行命令。例如,我的用户 `dwsmith` 是数据库管理员,需要在数据库服务器 `db1` 上以用户 `_postgresql` 的身份运行任何命令。
dwsmith db1 = (_postgresql) ALL
`_postgresql` 用户无法成功运行像 `fdisk` 和 `newfs` 这样的关键系统程序,但它可以重启数据库、备份数据库并执行其他数据库管理任务。通过选择特定的用户、特定的机器和特定的命令,你可以定义任意复杂的 *sudoers* 策略。
#### 长行
如果一行中有多个命令、用户名或主机,那么这一行可能会变得不舒服地长。使用反斜杠 (`\`) 来表示规则继续在下一行。
sbaxter dns1=/etc/rc.d/named,/sbin/mount_nfs,
/sbin/reboot, /sbin/dump
使用尽可能多的行来使你的 *sudoers* 文件更容易管理。
### /etc/sudoers 别名
拥有不同角色的多台机器,添加多个具有不同权限级别的系统管理员,你的 */etc/sudoers* 文件会迅速变得复杂。当你有几个具有相同权限和希望他们访问的长命令列表的用户时,维护每个用户权限列表的一致性会变得繁琐。*别名* 简化了这些任务,并使 */etc/sudoers* 文件更容易理解,这使得你的生活更加轻松。
别名是一组用户、主机或命令。你可以在任何通常使用用户、主机或命令的地方使用别名。例如,你可以创建一个名为 `DATABASE_COMMANDS` 的别名,其中包含所有数据库管理员需要使用 `sudo` 运行的命令。
让我们以数据库管理员 `dwsmith` 为例,并使用别名来指定他的命令。
dwsmith db1 = (_postgresql) DATABASE_COMMANDS
这个别名可能看起来没有为我们节省多少,但假设我们有几个数据库管理员。我们可以创建一个名为 `DBAs` 的别名,包括所有他们。
DBAs db1 = (_postgresql) DATABASE_COMMANDS
突然,这一行代表了多条规则。所有数据库管理员都有相同的 `sudo` 权限,当你发现你需要给他们访问额外命令的权限时,只需将命令添加到别名中,它就会立即对每个数据库管理员可用。无需在用户之间繁琐且容易出错地复制条目。
在您可以使用之前,必须定义一个别名,因此别名通常位于文件顶部。每个别名由一个标识其类型的标签、一个名称和其项目列表组成。别名类型包括用户别名、运行时别名、主机别名和命令别名。
#### 用户别名
*用户别名* 是一组用户,并且用字符串 `User_Alias` 标记。在这个别名中只放置用户名。
User_Alias DBAs = dwsmith, kkrusch
在这里,用户别名 `DBAs` 包含用户 `dwsmith` 和 `kkrusch`。通过在我的 *sudoers* 规则中使用别名而不是用户名,我确保这些用户获得完全相同的 `sudo` 权限。
您可以使用系统组在用户别名中,通过在前面加上百分号(`%`)来实现。我可能在 */etc/groups* 中创建一个名为 `databaseteam` 的组,并将 `dwsmith` 和 `kkrusch` 加入到这个团队中。
%databaseteam db1 = (_postgresql) DATABASE_COMMANDS
这种用法中最常见的是给 `wheel` 组无限的 `sudo` 访问权限。
%wheel ALL = ALL
此规则允许 `wheel` 组通过 `sudo` 以 root 身份运行任何命令。这不会改变组成员的权限,但通过 `sudo` 给他们访问权限。这对于运行单个命令来说很方便。
#### 运行时别名
*运行时别名* 是一组用户,其他用户可以以这些用户身份运行命令。例如,在特定的应用服务器上,数据库管理员需要以数据库所有者 `_postgresql` 和 Web 服务器所有者 `www` 的身份运行命令。然而,如果用户必须以多个用户身份运行命令,则需要为每个目标用户创建单独的 *sudoers* 条目。
*运行时别名* 允许您将这些账户分组:
Runas_Alias APPOWNER = _postgresql, www
您现在可以编写一条规则,允许用户以 `_postgresql` 或 `www` 身份运行命令。
#### 主机别名
*主机别名* 是一组主机,定义为主机名、IP 地址或网络块。用字符串 `Host_Alias` 标记主机别名。以下是所有主机别名类型的示例:
Host_Alias DB = db1, db2, db3
Host_Alias DMZ = 192.0.2.0/24
Host_Alias FIREWALL = 192.0.2.1, 192.0.2.2, 192.0.2.3
### 注意
我在这本书的其他地方警告过,基于主机名的安全规则容易受到 DNS 欺骗攻击的威胁。然而,入侵者不能欺骗机器的本地主机名,因此您可以在 *sudoers* 中安全地使用 */etc/myname* 中的主机名。
#### 命令别名
*命令别名* 是一组命令。例如,您可能有一个包含备份系统或从备份中恢复所需的所有命令的命令别名。它们用字符串 `Cmnd_Alias` 标记。
Cmnd_Alias BACKUPS = /bin/mt, /sbin/restore, /sbin/dump
您可以使用通配符告诉命令别名包含特定目录中的所有内容。
Cmnd_Alias APPCOMMANDS = /home/appuser/bin/*
您也可以列出部分命令名称。例如,大多数 PostgreSQL 的命令都以 `pg_` 前缀开始。要给用户访问这些命令的权限,可以使用如下通配符:
Cmnd_Alias APPCOMMANDS = /home/appuser/bin/,/usr/local/bin/pg_
如果您发现自己正在编写包含路径如 */sbin/** 的命令别名,请停止并重新考虑,因为您实际上是在给用户无限的 root 权限。
#### 在 /etc/sudoers 中使用别名
正常情况下,使用别名就像列出用户、命令或主机名一样。在之前的示例中,我定义了用户别名 `DBAs`、运行别名 `APPOWNER`、主机别名 `DB` 和命令别名 `APPCOMMANDS`。以下是它们可能的使用方式:
DBAs DB = ALL
在这里,用户组 `DBAs` 可以以任何用户身份在任何 `DB` 组的服务器上运行任何命令。该组的成员拥有服务器,如果他们搞砸了,这不是我的问题。
嗯,这种态度听起来不错,但事实是,当他们破坏服务器时,我必须介入。即使他们把数据库服务器推入沟里不是我的错,但这**是我的问题**。我必须锁定他们可以运行的命令,只允许他们运行 `APPCOMMANDS` 别名中的命令。因此,`DBAs` 组现在可以在 `DB` 服务器上运行 `APPCOMMANDS` 中的任何命令。
DBAs DB = APPCOMMANDS
然后,我发现我的数据库管理员要么比我之前想的聪明,要么比我之前想的愚蠢。他们以 root 身份运行某些数据库命令,创建了 root 所拥有的日志文件。无特权的数据库用户 `_postgresql` 无法写入这些日志文件,因此应用程序服务器崩溃。解决这个问题需要更改这些日志文件的权限,但数据库管理员没有运行 `chown` 的权限。如果我把更改任意文件权限的能力给他们,那我就不如直接给他们 root 权限。
为了防止这种情况再次发生,我限制了他们的权限,使他们只能以应用程序无特权的用户身份运行他们的命令。
DBAs DB = (APPOWNER) APPCOMMANDS
`DBAs` 组中的每个人都可以在 `APPCOMMANDS` 中运行任何命令,就像 `APPOWNER` 中的任何用户一样,在任何 `DB` 服务器上。我可以通过向各种别名中添加条目来更改他们的访问权限。
没有别名,这条规则会是什么样子?
dwsmith,kkrusch db1,db2,db3 = (postgresql,www)
/home/appowner/bin/*,/usr/local/bin/pg*
这看起来很丑,但它确实做了完全相同的事情。
如果你给别名起名得当,你会发现规则更容易理解。虽然这些示例别名相当简短,但我已经使用了有最多 20 个成员的别名。没有别名的情况下,这些规则看起来令人震惊。
### 注意
在这种情况下,`sudo` 授予的一些权限是不必要的。例如,无特权的网络服务器用户不需要运行各种 PostgreSQL 工具,如果 `www` 尝试运行数据库,也不会发生什么。如果你不喜欢这样,可以制定两条不同的规则。无论如何,这比给数据库管理员提供 root 密码更安全。
#### 别名嵌套
你可以在别名中包含别名。在这里,我将两个用户别名合并为一个用于我的应用程序管理员的单一别名:
User_Alias APPADMINS = DBAs, WEBMASTERS
#### 别名命名约定
传统的做法(但不是强制性的)是将别名命名为全大写字母,以帮助区分用户、主机等。尽管这是有效的语法,但最好避免将别名命名为用户或主机。以下是一个示例:
User_Alias MWLUCAS = mwlucas,pkdick,sbaxter,dwsmith
这会让我很快变得疯狂.^([16])
你也可以重复使用别名名称,如果它们是为不同类型的别名。例如,以下是完全合法的,但完全令人反感。
User_Alias DB = dwsmith,kkrusch
Runas_Alias DB = postgresql,www
Host_Alias DB = db1, db2, db3
Cmnd_Alias DB = /usr/local/bin/pg, /home/appowner/bin/
DB DB = (DB) DB
如果这样做,任何必须调试您的`sudo`配置的人都会诅咒您的名字。即使您认为被诅咒是一种工作福利,这种命名方案也会在不方便的时候让您手机响个不停。
### 改变 sudo 的默认行为
您可以使用`默认`字段自定义`sudo`的行为,或某些用户、主机或别名的行为。例如,`sudo`的一个特性是如果您输入了错误的密码,它会侮辱您。
$ sudo -l
Password:
My pet rat can type better than you!
Password:
我输入了错误的密码。`sudo`侮辱了我,并给了我再次输入密码的机会。如果我连续三次输入错误的密码,`sudo`将退出。
在开源环境中侮辱用户是可以的,但如果您在公司,有人会向管理层投诉。您可以选择参加敏感性培训或通过在*sudoers*中添加以下行来主动禁用侮辱:
Defaults !insults
`默认`语句表示以下项目会影响一个或多个`sudo`默认值。`insults`选项控制侮辱用户。感叹号(`!`)是一个否定符号。通过在选项前放置感叹号,您将关闭该功能。当用户证明他们打字不如我的宠物老鼠时,系统将不再侮辱用户。
$ sudo -l
Password:
Sorry, try again.
Password:
您可以全局或按别名覆盖默认值。
#### 按主机覆盖默认值
要按主机覆盖默认值,请在`默认`后使用`@`符号,并给出主机或主机别名。在这里,我想侮辱那些在`caddis`或`APPSERVERS`别名中的机器上无法输入密码的用户,同时为所有其他服务器禁用侮辱:
Defaults !insults
Defaults@caddis insults
Defaults@APPSERVERS insults
这让我能够为任何服务器组合启用或禁用功能。
#### 按用户覆盖默认值
要按用户更改`sudo`的默认值,请使用`%`和用户或用户别名。
Defaults !insults
Defaults%lasnyder insults
Defaults%DBAs insults
无论`lasnyder`在哪里登录——我将侮辱他,以及`DBAs`别名中的用户。但是数据库管理员已经习惯了软件的恶劣待遇,如果不侮辱他们,可能会让他们感到困惑和失望。
#### 按命令覆盖默认值
您还可以通过在`默认`和命令列表之间放置感叹号来按命令逐个更改`sudo`的行为。
Defaults !insults
Defaults!/sbin/newfs,/sbin/fsck insults
Defaults%APPCOMMANDS insults
任何尝试使用`newfs(8)`或`fsck(8)`(在第八章第八章。磁盘和文件系统` 和 `sftp(1)` 将文件移动到其他服务器。阅读文档,创建一个批准的环境变量列表,并添加条目。
Defaults env_keep += "SSH_CLIENT SSH_CONNECTION SSH_TTY SSH_AUTH_SOCK"
### 注意
将文件复制到其他服务器的权限可能应该仅限于特定组的人员。系统管理员可能需要将文件复制到其他服务器,但许多其他用户不需要这种访问权限。
## 使用 sudo
现在你已经知道了如何设置 `sudo` 权限,让我们看看如何实际使用它。首先,让我们告诉 `sudo` 你的账户有权限运行任何命令。(你应该至少在测试机器上有 root 访问权限,所以这不会是一个安全问题。)
实现这一点的简单方法是取消注释 *sudoers* 中的条目,允许 `wheel` 成员访问所有命令。
%wheel ALL=(ALL) SETENV: ALL
作为 `wheel` 组的用户,检查你的 `sudo` 权限。
$ sudo -l
Password:
Matching Defaults entries for mwlucas on this host:
env_keep+="DESTDIR DISTDIR EDITOR FETCH_CMD FLAVOR FTPMODE GROUP MAKE",
env_keep+="MAKECONF MULTI_PACKAGES NOMAN OKAY_FILES OWNER PKG_CACHE",
env_keep+="PKG_DBDIR PKG_DESTDIR PKG_PATH PKG_TMPDIR PORTSDIR",
env_keep+="RELEASEDIR SHARED_ONLY SSH_AUTH_SOCK SUBPACKAGE VISUAL",
env_keep+=WRKOBJDIR
User mwlucas may run the following commands on this host:
(ALL) SETENV: ALL
当 `sudo` 要求输入密码时,输入你自己的密码,而不是 root 密码。
`-l` 标志告诉 `sudo` 显示你有哪些权限和设置。作为回应,`sudo` 解析 */etc/sudoers* 并输出适用于该系统上你账户的所有设置。任何特定主机的限制已经评估过,不会显示。
### sudo 密码缓存
当你正确输入密码时,`sudo` 记录时间,接下来五分钟内,它会记住你最近输入了密码,并且无需再次输入即可工作。五分钟后,你必须重新认证。这简化了输入一系列 `sudo` 命令的工作,但它的超时时间相对较短。
您可以通过运行 `sudo -k` 来让 `sudo` 忘记您的缓存密码。您可以使用 `sudoers` 中的 `timestamp_timeout` 选项来控制 `sudo` 再次请求密码之前的时间。在这里,我们告诉 `sudo` 不要在 10 分钟内超时密码:
Defaults timestamp_timeout 10
如果您将超时设置为 0,`sudo` 总是会要求输入密码。如果您将其设置为负值,`sudo` 将在整个登录会话中缓存密码。您必须运行 `sudo -k` 来让 `sudo` 忘记您输入了密码。
### 在 sudo 下运行命令
要通过 `sudo` 运行命令,只需在 `sudo` 命令后跟命令名称。例如,这是您如何通过 `sudo` 运行 `tcpdump` 的方法:
$ sudo tcpdump
`sudo` 命令应该提示您输入密码。正确输入后,`tcpdump` 应该以 root 身份运行。
您也可以在 `sudo` 下运行包含参数的命令。例如,我使用 `tail -f` 来查看日志文件的末尾,并显示新出现的条目。但某些日志文件只能由 root 访问,例如认证日志和包含详细 `sudo` 日志的日志。您可以使用 `sudo` 而不成为 root 来查看这些日志。
$ sudo tail -f /var/log/authlog
您可以配置 *sudoers* 以允许任何组合的命令和参数。
### 以其他用户身份运行命令
之前,您看到了如何授予一些用户以非 root 用户身份运行命令的权限。使用 `-u` 标志指定用户。
$ sudo -u _postgresql pg_dump
如果您没有权限以该用户身份运行该命令,您将收到错误。
## sudoedit
我的助手 `sbaxter` 需要编辑 *命名* 配置文件,`/etc/named.conf`。考虑以下 `sudo` 配置:
sbaxter dns1=/etc/rc.d/named,/sbin/mount_nfs,/usr/bin/vi /etc/named.conf
看起来不错,对吧?
哎,不行。
第一个问题是我要求 `sbaxter` 使用特定的编辑器。系统管理员需要具备 vi 的最低能力,但我不想强迫他使用特定的编辑器来完成日常工作。此外,许多编辑器提供 shell 转义功能。虽然大多数人知道在 vi 中转义到 shell,但 emacs 也有 shell 转义功能。如果我的助手在以 root 身份运行编辑器时能够转义到 shell,他将获得 root 权限。这正是我想避免的。
`sudoedit` 功能允许用户使用他们首选的编辑器或由系统管理员选择的默认编辑器编辑特定文件,而无需以 root 身份工作。
sbaxter dns1=/etc/rc.d/named,/sbin/mount_nfs,
sudoedit /etc/named.conf, /etc/rndc.key
关键字 `sudoedit` 后跟一个文件列表,用户可以编辑这些文件,从而允许用户更改这些文件而无需 root 权限。
用户通过传递文件名给 `sudoedit` 来编辑文件。
$ sudoedit /etc/named.conf
技术上,用户并没有编辑实际的文件;相反,`sudoedit` 将文件复制到由用户拥有的临时文件中,当用户关闭编辑器时,它将临时文件复制到原始位置。用户永远不会以 root 身份运行编辑器。
`sudoedit` 关键字使用环境变量 `$SUDO_EDITOR`、`$VISUAL` 或 `$EDITOR` 中给出的编辑器。如果用户不喜欢系统提供的选项,他们可以在他们的 shell 中设置该变量。
## 最大的 `sudo` 错误:排除
现在你已经了解了 `sudo` 的基础知识,让我们考虑一个即使是经验丰富的系统管理员也会陷入困境的配置。有时你想要阻止用户执行特定的命令,但又要让他们能够访问其他所有命令。`sudoers` 文档说明你可以使用感叹号(`!`)作为否定字符来实现这一点,但这并不完全有效。然而,由于这是一个流行的方法,所以我将讨论它是如何工作的,然后演示如果你使用它,你的用户将如何自动获得 root 权限。
首先,定义包含禁止命令的命令别名。一个流行的排除项是 `su`。另一个常见的排除项是用户 shell,因为如果你以用户身份执行 shell,你将变成那个用户。
Cmnd_Alias SHELLS = /bin/sh,/bin/csh,/usr/local/bin/tcsh
Cmnd_Alias SU = /usr/bin/su
现在配置一个排除那些命令的命令别名。
pkdick ALL = ALL, !SHELLS,!SU
看起来很有道理,不是吗?而且它似乎能正常工作。
$ sudo sh
Password:
Sorry, user pkdick is not allowed to execute '/usr/bin/su' as root.
但是这里有个问题:命令是通过完整路径定义的。你允许用户运行除了少数通过完整路径指定的命令之外的所有命令。这个用户需要做的只是将命令复制到另一个位置并运行它。
$ cp /bin/sh /tmp/sh
$ sudo /tmp/sh
欢迎来到 root!
否定命令可以被任何理解 `sudo` 基础知识的人绕过,正如你在 `sudo` 手册和其他文献中会发现的那样。人们 *仍然* 坚持使用它来保护生产系统。不要成为那些人之一。
## sudo 日志
每个使用 `sudo` 的命令都会被 `syslogd` 记录到 */var/log/secure*。每条日志消息都包含一个时间戳、一个用户名、一个终端、运行命令的目录、运行命令的用户以及所使用的命令。
Apr 30 14:16:50 treble sudo: mwlucas : TTY=ttyp8 ; PWD=/home/mwlucas ; USER=root ; COMMAND=/usr/bin/su -m
通过检查文件 *secure*,你可以精确追踪谁做了什么以及何时做的。(将你的 *syslog* 消息发送到一个你的用户无法访问的日志服务器,以防止那些搞砸的人删除他们错误的日志。)
May 15 09:14:55 treble sudo: lasnyder : TTY=ttyp4 ; PWD=/etc ; USER=root ; COMMAND=/bin/rm pf.conf
我确切地知道谁破坏了这个系统以及何时破坏的。日志条目将即将发生的事情从“谋杀”转变为“正当的杀人”。仅此一点就使得正确使用 `sudo` 值得。
本章为你提供了一些避免意外破坏系统的技巧。现在让我们看看如何通过操作磁盘和文件系统真正地破坏你的系统。
* * *
^([15]) 我可以简单地说“我从未见过配置正确的 POSIX ACLs”,但个人的轶事证据并不构成证明。即使我在这个行业中几十年收集到的数十个令人毛骨悚然的个人轶事也不构成证明。请随意证明我错了,但请,请在 *你的* 服务器上这么做。
^([16]) 哦,好吧——battier。高兴了吗?
## 第八章。磁盘和文件系统
*哦,我的头好痛。*
*一串的 0 和 1,真痛苦!*
*文件系统隐藏了它们。*
 正确的数据管理可能是系统管理员最重要的职责。你可以更换几乎所有的计算机组件,但磁盘上的数据是无法替代的。也许这些数据并不重要或者已经备份,但丢失文件会毁了你的一天。作为系统管理员,你必须通过仔细管理你的磁盘和文件系统来保护重要数据。
我们在第二章中介绍了磁盘标签和 MBR 分区的基础知识,但 OpenBSD 允许你以任何数量的方式使用和滥用磁盘和文件系统。你将在本章中学习如何做到这一点。
## 设备节点
*设备节点*是一个文件,它为硬件提供了一个逻辑接口。通过从设备节点读取、向其发送数据或在其上使用命令,你是在告诉操作系统对硬件或在某些情况下对逻辑设备执行操作。
当数据发送到不同的设备时,它们的行为也会有所不同。例如,向控制台写入会在屏幕或终端上显示文本,而向磁盘设备写入会将数据放在该磁盘上。(OpenBSD 将设备节点放在*/dev*中,并禁止在其他文件系统上使用设备节点。)
许多磁盘管理程序期望被给定一个设备名称作为参数。不幸的是,设备节点名称通常是晦涩的,并且在不同的操作系统之间差异很大——即使在同一硬件上运行的密切相关操作系统之间也是如此。为了稍微简化你的生活,表 8-1 列出了常见 OpenBSD 磁盘设备的设备节点名称。
表 8-1. 表 8-1:常见的磁盘设备节点名称
| 设备节点 | 描述 |
| --- | --- |
| */dev/fd** | 软盘(块设备) |
| */dev/rfd** | 软盘(原始设备) |
| */dev/wd** | IDE 和一些 SATA 磁盘(块设备) |
| */dev/rwd** | IDE 和一些 SATA 磁盘(原始设备) |
| */dev/sd** | SCSI/SAS/SATA/USB/RAID 的非 IDE 磁盘(块设备) |
| */dev/rsd** | SCSI/SAS/SATA/USB/RAID 的非 IDE 磁盘(原始设备) |
| */dev/cd** | CD/DVD 驱动器(块设备) |
设备名称还有一个数字,告诉你它指的是该设备的哪个实例。编号从 0 开始。第一个 IDE 硬盘是*/dev/wd0*,*/dev/wd1*是第二个,而*/dev/cd1*是第二个 CD 驱动器。
每个分区都被分配了一个字母。例如,根分区是*a*,交换区是*b*,整个磁盘是*c*,依此类推。每个分区还有一个独立的设备节点,这是通过将分区字母附加到磁盘设备名称上得到的。例如,如果你在一个 IDE 驱动器上安装,你的根分区是*/dev/wd0a*。
### 原始和块设备
注意在表 8-1 中,设备以块或原始(字符)模式列出。这指的是设备是如何被访问的。
#### 块设备
硬盘通常使用块设备节点(有时称为*熟*设备节点)进行访问。当以块的形式访问设备时,传输到或从设备的数据会被*缓冲*,或者收集到足够的数据,使得访问设备变得值得麻烦。块设备通常被认为比原始设备更高效。
块设备的设备节点以设备驱动程序命名;例如,*/dev/wd3*。
#### 原始设备
原始设备有时被称为*字符*设备,因为它们一次访问一个字符。如果你需要精确控制数据在磁盘上的显示方式(例如,在创建文件系统时)使用原始设备。原始设备节点名称前有一个*r*,例如*/dev/rwd3*。
原始设备不进行缓冲。当你告诉系统向原始设备写入数据时,数据会立即传输。原始模式最适合提供自身缓冲或希望以特定方式排列数据的软件。
记住块和原始吞吐量之间的区别有一个简单的方法:假设你洒了一瓶阿司匹林。如果你一个个地捡起阿司匹林并将其直接放入瓶中,你正在进行无缓冲的或原始的传输。如果你用右手拿起阿司匹林,用左手收集它们,然后一次性将一堆阿司匹林(连同地板上的所有灰尘)倒入瓶中,你正在进行缓冲传输.^([17])
#### 选择您的模式
通过选择相应的设备节点,将磁盘(以及许多其他设备)作为原始或块访问。一些程序期望访问原始设备,而另一些程序期望访问块设备。如果一个程序打开*/dev/sd1a*,它是以块设备的形式访问磁盘*sd1*上的分区*a*。如果它打开*/dev/rsd1a*,它是以字符设备的形式访问相同的分区。
无论模式如何,底层硬件保持不变;唯一改变的是你与设备交换信息的方式。
### 设备附加与设备名称对比
不久以前,大多数磁盘都永久固定在系统的一个物理位置上。如果你的计算机有两个 IDE 总线,每个总线有两个硬盘驱动器,操作系统会确切知道它们的位置,通常在*/wd1*和*/wd2*。SCSI 磁盘有一个 SCSI ID 和一个逻辑单元号(LUN),更改它们需要重新启动计算机。传统上,你可以使用磁盘在系统中的位置来识别磁盘。例如,启动的 i386 计算机会通过查找连接到第一个 IDE 控制器第一个端口的硬盘来查找根分区,找到该磁盘上的*a*分区,并从该磁盘读取文件系统表。你可以进入 BIOS 告诉计算机在另一个磁盘上查找根分区,但计算机仍然通过它物理连接到计算机的位置来识别磁盘。
现在,磁盘可以在系统的多个位置出现和消失。例如,你可能需要根据需要连接和断开几个闪存驱动器,或者从总线到总线热插拔串行连接 SCSI (SAS) 或串行 ATA (SATA) 驱动器。物理位置不再是识别磁盘的安全方式。虽然 */dev/sd0* 是第一个 SCSI 磁盘的设备节点,但你不能假设当前连接到第一个 SCSI 端口的磁盘就是上次系统启动时插入的那个磁盘。OpenBSD 使用唯一 ID 对实际磁盘进行标记,如下一节所述。
## DUIDs 和/etc/fstab
所有 OpenBSD 平台都使用 disklabel 来识别分区和其他有关磁盘的信息。当你标记一个磁盘(如我们在第三章中所述,并在本章稍后手动进行),`disklabel`会将一个*disklabel 唯一标识符*,或 DUID,添加到磁盘标签中。DUID 是一个唯一的十六进制数字,允许 OpenBSD 识别特定的磁盘。
要查找磁盘的 DUID,将设备名传递给`disklabel`并查找`duid`条目:
disklabel sd0
…
duid: 55128c3700af5491
…
当前连接为`sd0`的磁盘的 DUID 为`55128c3700af5491`。即使你物理移动磁盘,使其变为`sd9`或`sd18`,OpenBSD 也可以使用 DUID 来唯一识别此磁盘。
OpenBSD 使用文件系统表 */etc/fstab* 来将磁盘上的文件系统映射到挂载点,使用磁盘位置或 DUID。每个文件系统都在 */etc/fstab* 的单独一行上显示,如下所示:
1 55128c3700af5491.b 2none 3swap 4sw
55128c3700af5491.a / ffs rw 1 1
55128c3700af5491.k /home ffs rw,nodev,nosuid 1 2
55128c3700af5491.d /tmp ffs rw,nodev,nosuid 1 2
…
我们将关注第一条目来探索这里发生的事情。第一个字段,`55128c3700af5491.b` **1**,是分区的位置。而较老的系统使用磁盘设备名和分区字母(例如*/dev/sd0a*),较新的系统可以使用 DUID、一个点号和分区字母(如`55128c3700af5491.a`)。通过在文件系统表中使用 DUID,OpenBSD 可以始终在相同的位置挂载相同的磁盘,无论其如何连接。
第二字段,`none` **2**,列出 *挂载点*,即文件系统附加到目录树中的目录。你可以写入文件的每个分区都附加到一个挂载点(例如 */usr*,*/var* 等),其中一个分区是根分区 (*/*)。交换空间使用 `none` 作为挂载点。
接下来,`swap` **3**,是文件系统类型。标准的 OpenBSD 分区使用类型 `ffs`,UNIX 快速文件系统。其他选项包括但不限于 `msdos`(Microsoft 风格的 FAT 分区)、`mfs`(内存文件系统)和 `cd9660`(CD)。
第四字段,`sw` **4**,显示了用于此文件系统的挂载选项。我将在 FFS 挂载选项中更详细地介绍挂载选项,但这里有一些在 */etc/fstab* 中经常出现的选项:
+ ****`ro`****. 文件系统以只读方式挂载。即使是 root 也不能写入它。
+ ****`rw`****. 文件系统以读写方式挂载。
+ ****`nodev`****. 设备节点不会被解释。
+ ****`nosuid`****. 禁止 `setuid` 文件。
+ ****`noauto`****. OpenBSD 不会在启动时或运行 `mount -a` 时自动挂载文件系统。此选项对于可能没有媒体的可移动媒体驱动器很有用,例如 CD 和 USB 闪存驱动器。
第五字段表示 `dump(8)` 是否应该备份此文件系统。如果此字段为 0(或不存在),则 `dump` 不会定期备份文件系统。否则,给出的数字是需要备份文件系统的最小备份级别。
最后一个字段是 *通过次数*。它告诉 `fsck` 在启动时何时检查文件系统。通过次数为 1 的文件系统首先检查,通过次数为 2 的文件系统其次检查,依此类推。通过次数为 0 告诉 `fsck` 在启动时不检查文件系统。如果一个文件系统没有通过次数,则相当于 0。
我强烈建议在 */etc/fstab* 和其他任何地方使用 DUIDs,而不是使用设备节点名称。虽然设备节点名称可能会改变,但 DUID 不会。
## MBR 分区和 fdisk(8)
一些硬件平台对磁盘分区有特定的想法,这与 OpenBSD 的期望不同。例如,i386 和 amd64 平台期望在硬盘上找到 MBR 分区,OpenBSD 通过在其自己的磁盘标签分区中放置自己的磁盘分区来适应这种怪癖。我们简要介绍了安装过程中的分区创建,但如果你在现有系统中添加硬盘,你需要手动使用 `fdisk(8)` 编辑 MBR 分区表。
我的特定测试系统有两个硬盘:*wd0* 和 *wd1*。我认为 *wd1* 完全为空,但在我能使用这个硬盘之前,我需要验证它是否为空,然后创建 MBR 分区。虽然 `fdisk` 有各种编辑磁盘的命令,但我发现最简单的方法是使用交互式磁盘编辑器。运行 `fdisk -e` 并给出新硬盘的设备节点。
fdisk -e wd1
Enter 'help' for information
fdisk: 1>
编辑器很简单,但让你可以查看、添加、删除和编辑 MBR 分区。如果你在任何时候忘记了命令,输入`help`将打印出`fdisk`支持的 所有命令。
### 查看 MBR 分区
要查看当前磁盘上的 MBR 分区,输入**`print`**或**`p`**。以下是一个示例:
fdisk: 1> print
Disk: wd1 geometry: 2088/255/63 [33554304 Sectors]
Offset: 0 Signature: 0x0
Starting Ending LBA Info:
: id C H S - C H S [ start: size ]
0: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
1: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
2: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
3: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
第一行显示了磁盘几何形状(如第二章安装准备中所述)。这个磁盘的 MBR 表中的每个值都设置为 0,意味着它没有配置任何分区。
### 添加和删除分区
假设我们想在磁盘上创建一个 MBR 分区。我习惯使用分区 0,但 OpenBSD 安装程序通常使用分区 3。你选择的特定数字并不重要,除非你想要在磁盘上有多个 MBR 分区。
要编辑一个分区,输入**`edit`**或**`e`**后跟分区号。例如,要编辑分区 0,输入以下内容:
fdisk: 1> e 0
Starting Ending LBA Info:
: id C H S - C H S [ start: size ]
0: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
1 Partition id ('0' to disable) [0 - FF]: [0] (? for help) a6
Do you wish to edit in CHS mode? [n]
2 offset: [0]
3 size: [0] *****
### 警告
便利的是,`fdisk`打印了当前这个 MBR 分区的信息。在弄乱它之前,确保它是你认为的那个分区。
首先,在**1**处设置分区 ID。这是一个标签,表示磁盘上会有什么类型的文件系统。OpenBSD 使用分区 ID `a6`,所以输入那个。
**2**处的偏移量是从磁盘开始到分区开始的扇区数。我们希望使用整个磁盘来安装 OpenBSD,所以将其设置为`0`。
最后,**3**处的尺寸是 MBR 分区填充的扇区数。在这里不需要复制磁盘中的扇区数;OpenBSD `fdisk`使用`*`表示“所有空闲空间”。
现在再次打印 MBR 表以检查你的工作。
fdisk:*1> p
Disk: wd1 geometry: 2088/255/63 [33554304 Sectors]
Offset: 0 Signature: 0x0
Starting Ending LBA Info:
: id C H S - C H S [ start: size ]
0: A6 0 0 1 - 2088 167 63 [ 0: 33554304 ] OpenBSD
1: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
2: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
3: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused
注意,分区 0 的条目类型为 A6,从磁道 0,头 0,扇区 1 开始,到磁道 2088,头 167,扇区 63 结束。它填充了 33,554,304 个扇区——与磁盘中的扇区数相同。这个 MBR 分区填充了整个磁盘。
如果你从另一个操作系统回收了这个磁盘,它可能已经有一个分区了。要删除一个分区,编辑分区并将其分区 ID 设置为 0。
### 使分区可引导
为了从硬盘启动,你需要将一个分区标记为活动分区。使用`flag`命令和一个分区号来完成此操作。
fdisk: 1> flag 0
Partition 0 marked active.
将这块硬盘包含到你的 BIOS 启动顺序中,计算机应该会尝试从它启动。仅仅将一个分区标记为活动分区并不意味着计算机*可以*从它启动;然而,你仍然需要一个内核、引导加载程序以及所有启动计算机所需的其他东西。
要将一个分区标记为不再活动,删除并重新创建它。(没有`unflag`命令。)
### 退出 fdisk
一旦你对你的工作满意,输入**`quit`**或**`q`**,`fdisk`应该将新的 MBR 表写入磁盘并退出。如果你改变了主意,不想做任何更改,输入**`abort`**或**`exit`**,`fdisk`应该退出而不保存对 MBR 分区表的更改。
## 标记磁盘
OpenBSD 使用 `disklabel` 在所有硬件平台上设置分区。我们在安装过程中使用了 `disklabel(8)`,但在你可以使用它们之前,你需要为新磁盘分区。(你也可以使用 `disklabel` 来备份、恢复和复制分区表。)
### 查看标签
要查看当前的磁盘标签,只需将磁盘名称作为参数传递。以下是如何查看上一节中空磁盘的磁盘标签:
disklabel wd1
1 # /dev/rwd1c:
…
2 duid: 0000000000000000
…
16 partitions:
size offset fstype [fsize bsize cpg]
3 c: 33554304 0 unused
这看起来与我们在 第二章 中看到的磁盘标签非常相似,但有几个关键的区别。
首先,注意 **1** 处的设备。`disklabel` 命令访问原始设备,但你应该在命令行中使用块设备。
**2** 上的这个标签没有 DUID。这是一个默认的空磁盘标签。我们稍后会生成一个 DUID。
在 **3** 处,我们看到这个磁盘只有一个分区,*c*,它代表整个磁盘。你可以在分区 *c* 上创建和使用文件系统,但这不是标准做法。
### 创建磁盘标签分区
创建分区最简单的方法是使用我们用来安装 OpenBSD 的相同交互式磁盘标签编辑器。给磁盘标签编辑器传递 `-E` 标志和磁盘名称:
disklabel -E wd1
Label editor (enter '?' for help at any prompt)
现在,你可以添加、删除和编辑分区,就像在 第三章 中一样。
在本书的其余部分,我们将根据需要编辑磁盘标签以更改分区和文件系统的特性。
### 备份和恢复磁盘标签
在对磁盘进行操作之前,备份其磁盘标签,以便在出错时可以回滚到旧标签。你可以使用以下命令备份磁盘标签:
disklabel wd1 > wd1.disklabel.saved
要将保存的磁盘标签应用到磁盘上,给 `disklabel` 传递 `-R` 标志、磁盘设备和标签文件:
disklabel -R wd1 wd1.disklabel.saved
这会将保存的标签写入磁盘。你可以使用保存的磁盘标签在相同磁盘之间复制分区。
现在你已经有了分区,让我们在这些分区上创建一个文件系统。
## 快速文件系统
OpenBSD 的文件系统,FFS,是 BSD 4.4 随附文件系统的改进版本。FFS 有时被称为 UFS(Unix 文件系统),许多系统实用程序仍然使用 UFS.^([18])
FFS 设计得既快又可靠,能够有效地处理最常见的场景,同时仍然支持奇怪的配置。默认情况下,OpenBSD 为通用用途调整了 FFS,但你可以根据需要优化它——无论是需要存储数十亿个微小的文件,还是存储六个 30GB 的文件。你不需要了解太多关于 FFS 内部结构的知识,但至少你应该理解块、碎片和 inode。
### FFS 版本
原始的 FFS 是在 1980 年代编写的,并包含了当时足够的硬编码限制。文件系统可以有高达 2³¹-1 个块,或者说接近一个太字节(TB)。在 1983 年,一个 1TB 的文件系统是无法想象的。在 2013 年,1TB 的硬盘很便宜。
对于较大的文件系统,我们有 FFS 版本 2。FFS2 可以支持高达 8 泽字节(按照 2013 年的标准是无法想象的)的文件系统。(请注意,FFS2 可能在达到文件系统大小限制之前就达到其他限制。)OpenBSD 支持 FFS 和 FFS2。
i386 和 amd64 引导软盘只支持 FFS,不支持 FFS2。然而,安装 CD 支持两者。大多数需要从软盘启动的机器不需要 FFS2,而且可能没有可以支持 2TB 驱动器的 BIOS。文件系统创建程序`newfs(1)`足够智能,可以在需要的情况下使用 FFS2,所以对于大多数安装,你不需要担心 FFS 和 FFS2 之间的区别。
### 注意
在极其不可能的情况下,如果你需要在必须通过软盘安装的机器上使用 FFS2,请确保将根(`/`)、`/var`和`/usr`等关键系统分区格式化为 FFS,而不是 FFS2。仅对非关键系统分区使用 FFS2。否则,你将无法使用安装盘进行升级或紧急修复。
### 区块、片段和 inode
FFS 和 FFS2 都通过区块、片段和 inode 进行管理。这种安排并不仅限于 FFS 和 FFS2;例如,NTFS 这样的文件系统也使用数据块和索引节点。然而,每个文件系统使用的索引系统在很大程度上是独特的。
#### 区块
*区块*是包含数据的磁盘部分。文件放置在一个或多个区块中。OpenBSD 的 FFS 使用默认的 16KB 区块大小,或八倍于片段大小,取较小者。并非所有文件都是 16KB 的倍数,所以剩余的位会放在*片段*中。一个片段是区块大小的八分之一,默认为 2KB。一个 20KB 的文件会填满一个区块和两个片段。
#### Inode
*inode*,或索引节点,包含关于文件的基本数据,例如文件的大小、权限以及包含文件的块列表。inode 中的数据统称为*元数据*,或关于数据的数据。
#### 超级块
你还会看到关于*超级块*的引用,这些块包含关于文件系统大小和规格的重要信息。超级块非常重要,因此 FFS 会为它们创建许多备份副本。如果你需要与超级块打交道,你可能做错了什么,或者你的文件系统出了问题。
### 创建 FFS 文件系统
使用`newfs(8)`创建 FFS 和 FFS2 文件系统,并确保磁盘有一个磁盘标签。`newfs`命令接受一个参数:分区设备节点。
newfs wd1a
/dev/rwd1a: 16383.9MB in 33554304 sectors of 512 bytes
81 cylinder groups of 202.47MB, 12958 blocks, 25984 inodes each
super-block backups (for fsck -b #) at:
32, 414688, 829344, 1244000, 1658656, 2073312, 2487968, 2902624, 3317280, 3731936,
…
你将看到关于文件系统大小、包含多少区块等详细信息。随着`newfs`的进行,每个超级块备份的位置会被打印出来。(当计算机和磁盘速度较慢时,这会告诉操作员计算机实际上正在做事情,并没有卡住。)
分区大小决定了`newfs`使用的文件系统。小于 1TB 的分区使用 FFS 格式化;大于 1TB 的分区使用 FFS2。如果你想指定特定的文件系统格式(是的,如果你喜欢,甚至可以指定老式的 4.3BSD 格式),请使用`-O`标志。在大型分区上要求使用 FFS 文件系统是没有意义的,但你可能有小分区使用 FFS2 的原因。
newfs -O 2 wd1a
如果你认为你需要指定在新的文件系统上使用哪种文件系统格式,你可能错了。
### FFS 挂载选项
OpenBSD 可以以几种特殊方式处理 FFS 分区,控制文件系统支持哪些类型的更改以及可能存在哪些类型的文件。这些被称为**挂载选项**。你可以在命令行上挂载分区时指定挂载选项,正如我们将在挂载和卸载分区中讨论的那样,或者在`/etc/fstab`中指定。
#### 挂载选项和`/etc/fstab`
在文件系统的`/etc/fstab`条目的第四字段中,以逗号分隔的列表形式指定文件系统的挂载选项。例如,以下是我`/etc/fstab`条目中包含我的`/home`目录的分区:
244f6d3acd6374ad.k /home ffs rw,nodev,nosuid,softdep 1 2
我已经指定了`rw`(读-写)、`nodev`(禁止设备节点)、`nosuid`(禁止`setuid`程序)和`softdep`(软更新)选项。我将介绍这些以及其他常见的挂载选项,并解释为什么你可能想要使用它们。
#### 只读挂载
如果你只想读取分区的内容,而永远不会写入它,你可以将分区挂载为**只读**。在大多数情况下,这是挂载磁盘最安全的方式,因为你不能更改磁盘上的数据或写入任何新数据。如果一个文件系统永远不会改变,将其挂载为只读可能是有意义的。
当特定的文件系统损坏时,只读挂载特别有价值。虽然 OpenBSD 不允许你在损坏或脏文件系统上执行标准的读-写挂载,但它通常可以以只读方式挂载这些文件系统。这给你提供了一个从分区中恢复一些数据的机会。(机会不大,但总是一个机会。)
要将文件系统挂载为只读,请使用选项`rdonly`或`ro`。
#### 读-写挂载
如果你既想从磁盘读取又想写入磁盘,你需要将分区挂载为**读-写**模式。默认情况下,OpenBSD 会将所有分区挂载为读-写模式。
使用选项`rw`来明确配置读-写挂载。
在现代硬件上,我建议结合使用软更新和读-写挂载。
#### 同步挂载
使用同步挂载是挂载文件系统的最安全方式。OpenBSD 可以从同步挂载的分区以硬件允许的速度读取数据。然而,每次你向磁盘写入数据时,内核都会向磁盘发送一块数据,等待接收磁盘已接受数据并将其写入磁盘的确认,然后告诉请求写入的程序数据现在已在磁盘上。
您应该知道,即使您使用的是同步挂载,大多数硬盘都会谎称它们实际上是否已将数据写入磁盘。这些硬盘执行 *写入缓存*,在驱动器实际写入数据之前,将写入缓存到磁盘本身的小型闪存或 RAM 缓冲区中。这提出了一个问题:同步挂载真的是同步的吗?硬盘供应商通常声称,在断电的情况下,这些磁盘保留足够的电力将缓存写入磁盘。
虽然它们在崩溃情况下提供最大的数据完整性,但同步挂载较慢。当数据完整性至关重要时,您可能会使用同步挂载,但在大多数情况下,这是过度杀鸡用牛刀,并且您几乎没有能力验证挂载是否真正同步。
使用 `sync` 关键字激活同步挂载。
#### 异步挂载
为了快速写入数据,但风险更高的数据丢失,异步挂载分区。当使用异步挂载时,内核在磁盘确认数据已写入之前,会通知软件所有磁盘写入均成功。这很快,但系统故障可能会在您的磁盘上留下不一致的数据。
在从备份恢复文件系统时,异步挂载很有用,因为如果在恢复过程中中途断电,您无论如何都需要重新开始。如果您关心数据或反对重新创建文件系统,请不要在生产环境中使用异步挂载。
使用 `async` 关键字激活异步挂载。
#### 软更新挂载
软更新挂载组织并安排磁盘写入,以确保文件系统元数据始终保持一致性。这提供了与异步挂载相似的性能,同时具有同步挂载的可靠性。虽然这并不意味着所有数据都会写入磁盘——在错误的时间断电会导致数据丢失——但使用软更新可以防止由丢失数据引起的大量文件系统完整性问题。这不是默认设置,因为一些较旧、较小的硬件没有足够的内存来支持它,但如果您使用现代 i386 和 amd64 硬件,我建议为所有 FFS 分区启用软更新。
要以软更新方式挂载文件系统,请使用 `softdep` 选项。
#### “不跟踪访问时间”挂载
FFS 记录文件最后被读取、执行或以其他方式查看的时间。更新这些访问时间会消耗一小部分但可测量的磁盘 I/O 和性能。您可以使用 `noatime` 挂载选项来告诉 OpenBSD 不要更新任何文件的访问时间。
在笔记本电脑上使用 `noatime` 有意义,因为最小化功耗至关重要。如果您想在自己的服务器上使用此选项以获得一点额外的性能,您应该购买更快的磁盘。一些软件,如 Mutt 邮件客户端,如果在挂载 `noatime` 的文件系统上运行,将会损坏。
#### 无设备节点允许挂载
通过使用 `nodev` 挂载选项,你可以告诉 OpenBSD 不要解释任何给定文件系统上的任何设备节点。入侵者可能会尝试创建“恶意”的设备节点并使用它们来写入文件或攻击网络,但如果内核不会识别这些设备节点,那么它就会切断这一类攻击。
如果你的计算机上有来自多个操作系统的硬盘,这种挂载类型也非常有用。例如,如果你的计算机上双启动了 OpenBSD 和 Linux,但你不想在使用 OpenBSD 时意外访问 Linux 设备节点,`nodev` 选项将阻止你这样做。(你可能认为你会注意到你输入了 */linux/dev/hda* 而不是 */dev/wd1*,但永远不要低估你出错的能力。)在大多数情况下,包含 */dev* 的分区应该是唯一包含设备节点的分区。
#### 执行禁止挂载
`noexec` 挂载选项阻止分区上的任何二进制文件被执行。使用 `noexec` 选项挂载 */home* 可以帮助防止用户安装和运行他们自己的程序,但要使其有效,你需要确保用户不能在任何共享区域(如 */tmp* 和 */var/tmp*)中安装二进制文件。
注意,禁止执行二进制文件不会阻止用户从该分区运行解释脚本。也许用户不能运行编译的 C 程序,但如果他们可以运行 `perl $HOME/rootkit.pl`,那么 `noexec` 不会对他们造成太大的影响。
#### setuid 禁止
`nosuid` 选项禁止在此文件系统上的程序执行 `setuid` 行为。许多分区不应该有 `setuid` 文件,设置这个选项是一个很好的方法来破坏它们。OpenBSD 默认在 */home* 和 */tmp* 等分区上设置此选项。你必须仔细地将此选项放置在所有用户可写文件系统上,以便它防止不希望的行为。
#### 不要自动挂载此文件系统
`noauto` 实际上不是一个挂载选项,而是一种告诉 OpenBSD 在引导时不挂载 */etc/fstab* 中列出的给定分区的办法。我经常为可移动媒体驱动器创建 */etc/fstab* 条目,但系统不应该在引导时尝试挂载这些分区。如果 */etc/fstab* 所需的分区不可用,引导将会挂起,我不想因为拔掉了闪存驱动器而导致我的计算机无法启动。
### 文件系统完整性
两种版本的 FFS 都非常努力地确保磁盘上的数据是正确且完整的。包含文件的块应该记录在 inode 中,inode 应该由目录条目引用,等等。当你删除一个文件时,应该删除对该文件的全部引用。
然而,系统故障后,数据可能不一致。元数据可能引用之前已擦除的块;文件可能位于 inode 记录指定的不同位置;文件系统可能包含各种指向已移动、更改或消失的项目的引用。这些不一致的,或*脏的*文件系统不可信,在您将其挂载为读写之前必须进行合理化,或*清理*。如果您以只读方式挂载脏文件系统,它可能会使您的系统恐慌,但如果您强制 OpenBSD 以读写方式挂载脏文件系统,您将使脏文件系统受到更多损害。
在启动时,OpenBSD 会对文件系统执行最小检查和清理,或*梳理*,并将自动纠正发现的任何小问题。如果梳理无法完全清理文件系统,则启动将挂起,直到您干预。
面对脏文件系统时,您有几个选择:使用文件系统检查工具 `fsck(8)`,使用 `fsdb(8)` 和 `clri(8)` 调试文件系统,或者丢弃文件系统并运行 `newfs(8)`。大多数时候,您会尝试使用 `fsck` 修复文件系统。成功使用 `fsdb` 需要更多关于 FFS 内部结构的知识,所以我只推荐给那些真正想要深入了解 FFS 并且有大量时间投入的人。使用 `newfs` 重建文件系统会破坏文件系统上的所有内容,但它对于只包含临时数据的分区来说是一个不错的选择,例如 */usr/obj*。
在尝试任何修复之前,您可以使用 `dump(8)` 来复制损坏的文件系统。这为您提供了在修复磁盘失败时回退到当前状态的选择。(如果您不得不这样做,那么您可能需要重新评估您的备份策略。)
#### 运行 fsck
如果您尝试在启动时或常规操作期间挂载脏文件系统,您将看到一条类似以下的消息:
/dev/rwd1a: UNEXPECTED INCONSISTENCY; RUN fsck_ffs MANUALLY
`fsck(8)` 程序是几个文件系统特定完整性检查程序的界面。当您运行它时,`fsck` 识别文件系统的类型,并为您调用正确的完整性检查器。通过提供您要检查的文件系统的设备名称来运行 `fsck`:
fsck /dev/wd1a
您可以使用原始或烹饪设备名称;`fsck` 足够智能,即使您提供了烹饪设备名称,也会使用原始节点。
检查文件系统可能需要相当长的时间,所以请耐心等待。
当在脏文件系统上运行时,`fsck` 可能会发现许多问题:与 inode 失去关联的块、引用空块的 inode 等。它通常可以很好地猜测一切是如何组合在一起的。
当 `fsck` 发现它并不完全确定的问题时,它会建议一个修复方案并询问你是否想要进行更改。如果你回答 `y`,`fsck` 会进行更改。如果你回答 `n`,`fsck` 将保持文件系统不变。如果你告诉 `fsck` 不要执行它建议的更改,文件系统仍然会是脏的,你需要启动 `fsdb` 或 `clri` 并进行你认为更合适的更改。
有时,`fsck` 无法识别从损坏的文件系统中恢复的文件的名字或目录。这些文件会进入分区的 *lost+found* 目录(例如,*/usr/lost+found*)。你需要使用 `grep` 和 `strings` 等程序尝试通过其内容来识别这些文件。
#### 盲目信任 fsck
我们中那些缺乏调试文件系统技能的人发现自己处于一个困难的情况,我们只能接受 `fsck(8)` 知道最好的,或者只是从备份中恢复。如果你的文件系统在系统故障前进行了大量的磁盘 I/O,`fsck` 可能需要做出数十或数百次更改。你可能会花一个小时坐在控制台重复按 `y`。
如果你决定信任 `fsck` 并希望它是正确的,运行 `fsck -y`。这意味着“对每个问题回答 `y`”。你可能会在 *lost+found* 目录中找到文件系统的全部内容,或者你可能会丢失文件系统上的所有文件。但除非你非常熟悉 FFS 的内部结构,否则你仍然需要从备份中恢复。
如果你运行 `fsck` 并在途中意识到你希望回答 `y` 给所有后续的问题,请输入 `F`。这告诉 `fsck` 对所有剩余的问题回答 `y`。
在程序结束时,你已经恢复了系统或需要从备份中恢复。
## 当前已挂载的内容是什么?
在执行常规工作时,不可避免地你需要检查哪些磁盘当前已挂载,哪些未挂载。要查看所有挂载的文件系统和它们的挂载选项,请在没有任何选项的情况下运行 `mount(8)`:
$ mount
/dev/wd0a on / type ffs (local)
/dev/wd0k on /home type ffs (local, nodev, nosuid)
/dev/wd0d on /tmp type ffs (local, nodev, nosuid)
/dev/wd0f on /usr type ffs (local, nodev)
/dev/wd0g on /usr/X11R6 type ffs (local, nodev)
/dev/wd0h on /usr/local type ffs (local, nodev)
/dev/wd0j on /usr/obj type ffs (local, nodev, nosuid)
/dev/wd0i on /usr/src type ffs (local, nodev, nosuid)
/dev/wd0e on /var type ffs (local, nodev, nosuid)
FFS 和 FFS2 分区都显示为 `type ffs`。单词 `local` 表示分区位于连接到此机器的物理驱动器上。我们已经在本章前面介绍了各种挂载选项(如 `nodev`、`nosuid` 等)。
注意,`mount` 显示的是每个分区挂载的设备节点,而不是 DUID。如果你想查看磁盘的 DUID,请检查磁盘标签。
## 挂载和卸载分区
要将文件系统附加到你的目录树或 *mount* 它们,请使用 `mount(8)`。如果你以前从未手动挂载过文件系统,请将 OpenBSD 机器引导到单用户模式(参见 第五章) 并按照说明操作。
在单用户模式下,OpenBSD 只挂载一个分区:根分区,它将其挂载为只读。根分区包含足够的系统来执行基本设置、建立核心服务和找到其他文件系统。
因为除了根文件系统之外的其他文件系统没有被挂载,所以它们的内容是不可访问的。在单用户模式下查看系统中的 */usr*,你会发现它是空的。OpenBSD 并没有丢失这些文件;它只是还没有挂载包含这些文件的分区。
在单用户模式下完成任何实际工作,你可能需要挂载其他文件系统。
### 挂载标准文件系统
要手动挂载 */etc/fstab* 中列出的单个文件系统,给 `mount(8)` 提供你想要挂载的文件系统的名称。在这里,我们将挂载我们的 */usr* 分区:
mount /usr
这将以 */etc/fstab* 中描述的精确方式挂载分区,其中包含所有指定的选项。
要挂载 */etc/fstab* 中列出的所有分区,给 `mount` 提供 `-a` 标志:
mount -a
现在应该挂载所有你的文件系统(除了那些在 */etc/fstab* 中未列出的和那些带有 `noauto` 选项的文件系统)。
### 在非标准位置挂载
可能你必须在 */etc/fstab* 中未指定的位置挂载一个文件系统。我通常在向机器添加磁盘时这样做。要在 */etc/fstab* 中指定的位置之外挂载分区,或者要挂载没有 */etc/fstab* 条目的分区,给出分区设备名称和挂载点。
mount /dev/sd0d /mnt
你必须使用设备节点的完整路径,而不仅仅是简短的设备节点名称。
你可以使用 DUID、一个点和分区字母代替设备节点的路径,但在命令行上,这比使用设备节点的路径更痛苦。
### 卸载分区
要从目录树中断开文件系统,在挂载点上使用 `umount(8)`。注意这个命令中只有一个 `n`。在这里,我们将使用 `umount` 来卸载我们的 */usr* 分区:
umount /usr
你不能卸载被任何程序使用的文件系统。即使在挂载目录中的命令提示符也会阻止你卸载分区。
要卸载除了根分区之外的所有分区,给 `umount` 提供 `-a` 标志:
umount -a
由于程序几乎肯定在每个分区上都有文件打开,这可能在单用户模式下才能工作。请注意,你不需要卸载所有分区就可以离开单用户模式。
### 使用选项挂载
假设你从一个退役的 OpenBSD 机器中取出一个磁盘,并且你需要从它那里检索一些文件。你想要以只读方式挂载磁盘,这样你就不改变磁盘上的任何文件。要手动挂载一个在 */etc/fstab* 中未指定选项的分区,使用 `-o` 标志。
例如,如果磁盘显示为 */dev/sd0* 并且你想挂载分区 *a*,运行以下命令:
mount -o ro /dev/sd0a /mnt
为了防止旧软件在你的新系统上运行,使用我们之前提到的某些选项可能是个好主意,例如 `noexec`、`nodev` 和 `nosuid`。
## 分区有多少空间?
要了解你的分区中剩余多少可用空间,请使用`df(1)`。这个程序显示每个分区上的总文件系统块数,已使用的块数,以及空闲的块数。它还提供了使用百分比。
关于`df`的一个令人烦恼的事情是它默认以 512 字节块提供这些信息。当磁盘很小的时候,这没问题,但今天,这就像用码尺测量飞机航程的距离。有些人已经这样做了很长时间,以至于他们在潜意识里自动执行块转换。^([19]) 对于我们其他人来说,`-h`标志告诉`df`提供人类可读的输出,如兆字节或千兆字节,给我们这样的信息:
df -h
Filesystem Size Used Avail Capacity Mounted on
/dev/sd0a 1005M 39.1M 916M 4% /
/dev/sd0k 26.9G 27.0G -104M -1% /home
/dev/sd0d 3.5G 12.0K 3.3G 0% /tmp
…
你可能会想知道为什么这个例子中的*/home*分区有负的空闲空间。这是怎么可能的?默认情况下,FFS 为移动文件和减少碎片保留了每个分区的 5%。当你超过 100%的磁盘利用率时,你开始使用这部分保留空间。
当分区过满时,FFS 的性能会迅速下降。最好在磁盘上保留一些空闲空间,以便 FFS 可以自我碎片化。
你可以减少 FFS 保留的空间量,但这样做会影响性能。有关如何“自毁”的说明,请参阅`tunefs(8)`。
### 所有这些是什么东西?
当你看到分区已满时,一个明显的问题就是“是什么占满了我的磁盘?”我拥有的每一块硬盘都是逐渐满的,没有明显的原因。你可以使用`ls -l`来识别单个大文件,但递归检查文件系统中的每个目录既不实际又繁琐(更不用说令人烦恼了)。
要检查当前目录以下每个目录中使用的文件系统块数,请使用`du(1)`。
$ du
164 ./.ssh
2 ./old
6 ./.mozilla/firefox/bcpuv16e.default/chrome
80 ./.mozilla/firefox/bcpuv16e.default/Cache/0/B0
354 ./.mozilla/firefox/bcpuv16e.default/Cache/0/31
28 ./.mozilla/firefox/bcpuv16e.default/Cache/0/7A
…
当我在我的家目录中运行`du`时,我得到 700 个条目;其中 563 个与某些 Mozilla 工具有关。这样的列表会吓到新系统管理员,并让有经验的系统管理员工作过于辛苦。与其手动筛选这个列表,不如告诉`du`只显示当前目录中目录的总计,然后对输出进行排序,以便最大的目录首先出现。
$ du -s * | sort -rnk 1
25224805 Dark_Shadows_Complete_Series
141104 mibs
14948 tarballs
4668 work
1864 pix
…
我现在知道为什么我的*/home*分区满了。
你可以使用`-h`标志告诉`du`显示人类可读的值,但这样做会在千兆字节、兆字节和千字节之间混合显示值,使得`sort`变得不那么有用。
### 设置$BLOCKSIZE
许多磁盘工具包括但不限于`du(1)`和`df(1)`,都是以 512 字节块为单位显示信息的。如果你习惯于以块为单位工作,你可能不会介意看到它们。然而,如果你不习惯以块为单位工作,它们可能会让你想拔掉自己的头发。
环境变量 `BLOCKSIZE` 告诉这些程序使用不同大小的块来显示信息。如果您将 `BLOCKSIZE` 设置为 `K`,`df` 和 `du` 将以千字节显示总数。如果您将其设置为 `M`,这些工具将显示兆字节。检查您的 shell 手册页或您家目录中的 dotfiles 以获取设置环境变量的示例。
## 添加新硬盘
OpenBSD 安装程序会引导您格式化和分区初始硬盘。如果您需要向现有系统添加磁盘,则必须自行运行这些命令。好消息是,如果您能安装 OpenBSD,您就已经知道如何使用这些命令了,唯一困难的部分是学习要运行哪些命令。
我将以将 */home* 移动到新磁盘为例向您展示。如果您现有磁盘上有一些空闲空间,您可以在磁盘上创建一个新的分区,但那样就会消除这个示例的需要,所以我将假装我从未给你那个建议。(此外,将分区移动到单独的磁盘控制器通道可以提高性能。)
### 警告
在触摸任何涉及磁盘分区或文件系统的内容之前,请备份您的系统。在开始之前验证备份。您已经被警告了。
### 创建 MBR 分区
i386 和 amd64 平台要求磁盘既有 MBR 分区也有 OpenBSD 分区。一个标准的新磁盘需要一个覆盖整个磁盘的单一 OpenBSD MBR 分区。将 `-i` 参数传递给 `fdisk` 正好做到这一点。让我们在 `wd1`,我们的新磁盘上创建一个新的 MBR 分区:
fdisk -i wd1
Do you wish to write new MBR and partition table? [n] y
Writing MBR at offset 0.
一旦您的磁盘上有了 MBR 分区,您就可以创建 disklabel 分区。
### 创建 Disklabel
所有 OpenBSD 平台都使用 disklabel 分区。要激活安装过程中使用的相同 disklabel 编辑器,给 `disklabel` 添加 `-E` 标志和磁盘名称:
disklabel -E wd1
这应该与本章前面的内容相似。使用交互式 disklabel 编辑器创建您的新分区。对于单个 */home* 目录,我们将使用一个大的分区,*wd1a*。新的标签应该看起来像这样:
size offset fstype [fsize bsize cpg]
a: 33543648 64 4.2BSD 2048 16384 1
c: 33554304 0 unused
当您完成编辑分区后,通过打印 disklabel 检查您的工作。这也应该给出新磁盘的 DUID。
当您对分区满意时,使用 `newfs` 在新分区上创建一个文件系统:
newfs wd1a
您现在可以开始向您的计算机添加文件系统。
### 移动分区
将数据从一个磁盘移动到另一个磁盘比添加新的分区稍微复杂一些。您必须首先在临时位置挂载新驱动器,将文件复制到该位置,然后从旧位置删除它们,并将新驱动器挂载到其原来的位置。
我们新的 */home* 文件系统位于磁盘分区 *wd1a* 上。默认的“临时挂载”位置是 */mnt*,所以将其挂载在那里。这是严格临时的,因此没有必要通过 DUID 挂载它或为它创建一个 */etc/fstab* 条目。
mount wd1a /mnt
然后,您可以使用 `tar(1)`、`cpio(1)` 或 `dump(8)` 和 `restore(8)` 将文件复制到临时位置。在这里,我们将 */home* 中的所有内容复制到 */mnt*。
(cd /home && tar cf - . ) | (cd /mnt && tar xpf - )
您也可以使用 `cp(1)` 或 `mv(1)` 来做这件事,但这些命令不能保证文件权限和所有权会完整复制。OpenBSD 的这些程序版本在复制或移动文件时从未给我出错,但我从其他类 Unix 操作系统中了解到,在移动整个文件层次结构时,`tar` 和 `cpio` 都更可靠。如果您使用文件标志进行安全设置(见第十章),您必须使用 `dump(8)` 和 `restore(8)` 来保留这些标志。
使用 `tar` 或 `cpio` 不会从原始位置删除文件。这意味着,如果您在复制文件之后但在更改挂载点之前更改了用户的主目录中的文件,那么当您在移动磁盘时,他将丢失他的更改。^([20])
现在更新 */etc/fstab* 以反映您的新硬盘。
### 添加新的文件系统
查看新硬盘的磁盘标签并获取硬盘的 DUID。这个新硬盘的 DUID 是 `fea9194ee78362d8`。使用 DUID 和分区字母为您的新的分区创建一个 */etc/fstab* 条目。
fea9194ee78362d8.a /home ffs rw,nodev,nosuid,softdep 1 2
您可能希望在新位置保留旧分区,例如 */oldhome*。
如果您不确定为您的新的分区使用哪些挂载选项,`nodev`、`nosuid` 和 `softdep` 选项通常是安全的。您可能还想将分区设置为可读写(`rw`)。
现在卸载旧分区并挂载新分区。
umount /home
mount /oldhome
mount /home
当您卸载一个分区时,`umount` 不会检查 */etc/fstab*。您告诉它卸载一个分区,然后它就卸载了那个分区。
### 可堆叠挂载
OpenBSD 文件系统是可堆叠的,这意味着您可以在另一个分区之上挂载一个分区。顶部的分区会隐藏下面文件系统中的任何文件。
以单用户模式查看您的系统。默认情况下,只有根分区被挂载。您可以查看 */home* 目录,它将是空的。没有理由您不能在 */home* 目录中放置文件,即使 */home* 没有挂载。假设您在单用户模式下将几个核心文件复制到 */home*,然后进入多用户模式。所有常规分区都已挂载。如果您此时查看 */home*,您将找不到您的核心文件。
发生了什么?那些文件去哪里了?
文件在 */home* 目录中,但在根分区上。*/home* 分区挂载在目录之上,因此 */home* 分区掩盖了根分区上 */home* 目录中的文件。要访问这些隐藏的文件,您必须卸载 */home* 分区。然而,这些隐藏文件仍然占用根分区的空间。
这种情况在分区分割时更为常见。例如,如果你发现你的 */var* 分区太小,你可能会将 */var/www* 移动到单独磁盘上的自己的分区中。为了在原始的 */var* 上释放空间,删除你复制到 */var/www* 的文件。
在掌握文件系统管理的基本知识后,你现在可以开始了解一些 OpenBSD 更有趣的文件系统技巧。
* * *
^([17]) 如果是缓释型阿司匹林,那么你正在进行缓释型缓释阿司匹林转移。但让我们不要深入那个话题。
^([18]) OpenBSD 并不是唯一仍然使用 BSD 4.4 文件系统或其衍生版本的操作系统。如果一个 Unix 供应商没有特别吹嘘其“改进和先进的”文件系统,那么它几乎肯定运行的是 FFS 的衍生版本。
^([19]) 嗨,Henning!
^([20]) 假设你在进行维护之前会警告你的用户。或者至少在维护期间。或者……也许是在之后。
## 第九章。更多文件系统
*加密你的硬盘吗?*
*软件 RAID 可以拯救你的日子,*
*或毁灭你的生活。*
 磁盘管理并不复杂,但内容足够多,以至于有两章。幸运的是!在本章中,我们将从如何自动将你的重要根分区备份到第二个磁盘开始。然后我们将探讨 OpenBSD 如何通过内存文件系统使用额外的内存作为磁盘空间,以及如何设置它。接下来,我们将访问格式化为其他操作系统的磁盘,如 NTFS、ext2 和 FAT。可移动媒体并不难处理,但有其自己的关注点。如果你不需要实际的媒体,但可以处理磁盘镜像,你可以访问那些。这两个主题都包含在本章中。我们还将讨论使用 NFS 作为服务器和客户端。我们的最后一个主题是 OpenBSD 的磁盘冗余和磁盘加密功能。
## 将备份保存到 /altroot 分区
你可能会失去大部分分区,但仍希望恢复其余的系统。然而,如果你失去了根文件系统,恢复就会变得困难得多。虽然你可以从 */etc* 中备份关键文件并使用它们来恢复系统,但 OpenBSD 提供了 */altroot* 分区作为自动在第二个磁盘上复制根分区的一种简单方法。
在第二个磁盘上的紧急根分区可以在磁盘故障的情况下为你提供更简单的恢复路径。从第二个磁盘启动让你可以在更换故障磁盘之前,从该磁盘上提取任何数据,甚至可能从第一个磁盘上未损坏的分区中提取数据。然而,没有必要将根分区备份到同一个磁盘,因为整个磁盘可能无法使用。
这个备份需要一个与根分区大小相同的空闲磁盘分区,位于不同的磁盘上。OpenBSD 安装程序的默认设置假设你只有一个磁盘。如果你在安装过程中有第二个磁盘,你需要使用自定义安装过程来创建 */altroot* 分区,就像我们在第三章中的多磁盘安装所做的那样。虽然配置分区在安装过程中最容易,但如果需要,你可以在以后添加更多磁盘,如第八章中讨论的那样。
你的 */altroot* 分区需要一个 */etc/fstab* 条目。如果你在安装过程中创建了分区,那么那个 */etc/fstab* 条目已经存在,但挂载类型不正确。如果你在安装后创建了此分区,你需要自己创建一个 */etc/fstab* 条目。/*altroot* 分区需要一个 `xx` 类型的挂载类型,如下所示:
a914f9a264fa64e6.a /altroot ffs xx 0 0
你不能从这个分区的 */etc/fstab* 条目中挂载它,因为 `xx` 不是一个有效的挂载类型。(如果你想手动挂载这个分区,可以运行,例如,`mount /dev/sd1a /altroot`。)日常系统维护任务 */etc/daily* 使用这个挂载选项来识别根备份分区。
要启用 */altroot* 备份,请将 `ROOTBACKUP=1` 添加到您的 */etc/daily.local* 文件中。
## 内存文件系统
除了在原始磁盘上创建分区外,OpenBSD 允许您在系统内存中创建分区。一个 *内存文件系统* (*MFS*),或 *内存磁盘*,位于您的机器的 RAM 中,而不是物理磁盘上。在文件系统中读写文件比在旋转磁盘上访问相同的文件要快得多,这使得内存支持的文件系统对于某些应用程序来说是一个巨大的优化。
如果 MFSs 对于高性能环境来说听起来好得令人难以置信,那是因为它们确实如此。在您将它们应用到所有地方之前,先了解它们的限制。首先,RAM 在重启或关机后不会持续存在,因此重启或关机将会清除 MFS 的内容。虽然这看起来可能很明显,但我多次因为丢失了一个存储在忘记是 MFS 文件系统上的文件而惊讶。此外,如果您的系统崩溃,您将丢失存储在 MFS 上的任何数据。
您可以使用 MFS 分区作为临时空间,快速编译、压缩、解压缩或其他方式操作临时文件。我见过新闻服务器历史记录、数据库锁和其他存储在 MFS 上的特定应用程序文件。
即使系统经常交换,MFS 也能正常工作。内核保留内存中正在积极使用的任何信息,同时将未使用的信息传输到交换空间。这对于像 */tmp* 这样的小型分区来说非常出色,其中小型、频繁使用的文件可以快速访问。那些不太频繁访问的文件最终会存储在交换空间中,这提供了与访问物理磁盘相似的性能。
最后一句警告:如果您没有多余的 RAM,不要过度使用 MFS。如果您内存和交换空间不足,您的系统将表现非常糟糕。
### 创建 MFS 分区
使用 `mount_mfs(8)` 创建临时 MFS 分区。像其他 `mount_` 命令一样,`mount_mfs` 采取两个参数:物理设备和挂载点。与物理磁盘不同,内存没有设备节点,因此使用系统交换空间的设备节点。如果您有多个交换分区,请选择您喜欢的。
您可以通过将交换分区 */dev/sd0b* 和所需的挂载点 */mnt* 作为参数传递给 `mount_mfs` 来创建一个内存支持的文件系统:
mount_mfs /dev/sd0b /mnt
这个分区的大小将仅限于您的交换分区的大小。
您可以创建较小的内存支持文件系统,这样当您填满内存磁盘时,您将会有内存和/或交换空间可用。使用 `-s` 标志和扇区数或使用尾随的 `b`(字节)、`m`(兆字节)或 `g`(千兆字节)来指定大小。以下是如何在 */mnt* 上创建一个 128MB MFS 的方法:
mount_mfs -s 128m /dev/sd0b /mnt
如果您请求的 MFS 大于您的系统可以支持的,您将收到类似 `mmap: Cannot allocate memory` 的警告。再次尝试,这次使用一个更合理的尺寸。
### 在引导时挂载 MFS
你可以通过在*/etc/fstab*中添加条目来在启动时挂载 MFS。你只需要挂载点和分区大小。
1swap 2/mnt 3mfs 4rw,async,-s=128m 50 60
你不需要指定特定的交换设备;OpenBSD 足够智能,可以让你将内存磁盘通用地指定为交换后置**1**。就像任何其他分区一样,你还需要指定挂载点**2**和文件系统类型**3**。
当处理内存磁盘时,你可以使用与传统磁盘不同的选项**4**。由于系统崩溃会破坏 MFS 上的所有文件,因此你可以安全地使用`async`选项异步挂载 MFS 分区。你可能还希望在分区上使用`nodev`和`nosuid`挂载选项。你可以使用`-s`选项指定大小,但请确保在`-s`和大小之间放置一个等号(`=`)。因为*/etc/fstab*使用空白字符来分隔字段,如果你不使用等号,OpenBSD 会认为转储级别是 128m.^([21])
内存磁盘上的数据按定义是可丢弃的,所以不要备份它**5**。同样,在启动时永远不要使用`fsck(8)`对内存磁盘进行检查**6**。内存磁盘在每次启动时都会重新创建,因此它是自动内部一致的。
## 外部文件系统
任何使用非 FFS 文件系统的分区对 OpenBSD 来说是外部的。尽管 OpenBSD 可以访问许多外部文件系统,但不要期望它能够无缝访问。
对某些文件系统的支持是不完整的。例如,你只能以只读方式挂载 Microsoft NTFS 分区。其他文件系统不支持 OpenBSD 命令的全范围。因为 FAT 文件系统没有任何文件所有权或权限的概念,所以像`chmod`和`chown`这样的命令在磁盘上不会产生任何变化。
每个支持的文件系统都有自己的挂载程序来处理该文件系统的特性。为了简化你的生活,`mount`通常可以从磁盘格式中识别支持的文件系统,并在需要时调用正确的挂载程序。要挂载外部文件系统,你需要设备节点和一个挂载点。根据文件系统,你可能还需要知道你将要挂载的文件系统类型。
### Inodes vs. Vnodes
在我们讨论外部文件系统之前,让我们先谈谈让我困惑很长时间的事情:inode 和 vnode 之间的区别。
FFS 使用索引节点,或称为*inode*,来映射包含数据的磁盘块。当硬盘还是大而昂贵的物品,没有人会在计算机之间移动它们时,这工作得很好。然而,多年来,在机器之间交换磁盘变得越来越流行。
尽管类 Unix 系统在 inode 的概念下访问文件,但 FAT32 文件系统不使用 inode,ext2fs 的 inode 不会直接映射到 FFS 的 inode,CD 使用完全不同的布局。为了以一致的方式访问所有这些文件系统,BSD 需要另一层抽象。
虚拟节点,或*vnode*,是内核用来访问所有文件系统的抽象层。用户永远不会直接操作 vnode,但你会在 OpenBSD 的文档中看到对它们的引用。每个读取或写入磁盘的工具都通过 vnode 进行,将请求映射到文件系统。当你向 FFS 块或 inode 写入时,内核将数据地址到一个 vnode,该 vnode 反过来映射到一个 inode。当你向 FAT32 文件系统写入时,内核将数据地址到一个映射到 FAT32 文件系统中的点的 vnode。你只在处理 FFS 系统时使用 inode,但你的数据在访问任何文件系统时都会通过 vnode。
不要让非 FFS 系统上的 vnode 引用让你困惑。它们是 OpenBSD 的一部分,而不是文件系统的一部分。
### 常见的外国文件系统
常见的国外文件系统包括 MS-DOS、NTFS、ext2fs 和 CD。我们将探讨如何使用 OpenBSD 访问为这些操作系统格式化的磁盘。
#### MS-DOS
OpenBSD 支持 FAT、FAT16 和 FAT32 文件系统。这些格式常见于闪存媒体、旧版 Microsoft 操作系统和软盘。
要挂载具有 FAT 文件系统分区的文件系统,请使用`mount_msdos(8)`。
mount_msdos /dev/sd3i /mnt
如果不确定磁盘上的哪个分区是 FAT 文件系统,请在驱动器上运行`disklabel(8)`并查看。FAT 文件系统通常位于*i*分区。即使你尝试插入 USB 驱动器并挂载其*i*分区,OpenBSD 也可能识别出它是一个 FAT 系统。
如果你经常使用 FAT 磁盘,可能会调查`/usr/ports/sysutils/mtools`,这是一个用于在不挂载的情况下处理 FAT 文件系统的软件集合。虽然`mount_msdos`相当可靠,但 mtools 提供了一个更优雅的界面。
#### NTFS
要挂载为现代 Microsoft 操作系统格式化的磁盘,请使用`mount_ntfs(8)`。
mount_ntfs /dev/sd3k /mnt
当我写这篇文章时,OpenBSD 支持 NTFS4(来自 Windows NT)和 NTFS5(在 Windows 2000 和 XP 中)。Windows Vista 及更新的系统目前尚不支持,但当你阅读这篇文章时可能已经支持了。
如果你需要查看 NTFS 文件系统的特定文件属性,请查看`mount_ntfs`手册页以获取详细信息。
#### ext2fs
要挂载 ext2fs 和 ext3fs 文件系统,请使用`mount_ext2fs(8)`。(该程序可以挂载这两种类型的文件系统。)
mount_ext2fs /dev/sd3l /mnt
由于它们共享 Unix 血统,Linux ext2fs 和 ext3fs 文件系统支持许多类似 FFS 的功能。与 NTFS 不同,你可以在 OpenBSD 中安全地读写 ext2fs 和 ext3fs 磁盘。然而,你不能使用 OpenBSD 读取 ext4fs 分区。
#### CD
用于数据使用的光盘格式使用 ISO-9660 文件系统。要挂载 CD,请使用`mount_cd9660(8)`。
mount_cd9660 /dev/cd0a /mnt
使用设备的*a*或*c*分区挂载 CD。如果你想节省几个按键,`mount(8)`在自动检测 ISO-9660 文件系统方面非常出色。CD 的设备节点绑定到 CD 驱动器,而不是磁盘本身,因此节点不应该改变,除非你添加另一个驱动器。
如果你对烧录 CD 感兴趣,可以查看`mkhybrid(8)`和`cdio(1)`。
### 外来文件系统所有权
大多数外来的文件系统要么没有文件所有权的概念,要么有与类 Unix 操作系统不兼容的所有权方案。(在这些文件系统中,FAT 和 NTFS 是显著的。)挂载这些类型文件系统的程序会周到地允许你指定文件系统的文件所有权。`-u` 标志允许你指定文件所有者,而 `-g` 标志允许你指定组。
例如,我会这样挂载一个属于我账户的 FAT 文件系统:
mount_msdos -u mwlucas -g mwlucas /dev/sd3c /mnt
一些其他文件系统使用与 OpenBSD 权限方案兼容的权限。例如,OpenBSD 需要的所有信息都包含在一个 ext2fs 文件系统中,以分配文件和目录的权限。但这并不意味着 ext2fs 文件系统在 OpenBSD 上会无缝运行。尽管 OpenBSD 会尊重 ext2fs 磁盘的权限,但用户 ID 数字可能在操作系统之间不匹配。
## 可移动介质
这些天,你可能会处理的可移动介质主要是外置硬盘、闪存驱动器和 CD。CD 是最简单的,因为你知道如何使用 `mount(8)` 和 `umount(8)`,你也知道它的设备节点和文件系统类型始终相同。但你是如何识别可移动硬盘的设备名称的呢?
当你将驱动器连接到你的机器时,OpenBSD 会自动为你分配一个设备节点到控制台,并在控制台上打印一条消息。你可以在连接驱动器时检查控制台,或者你可以在连接驱动器之前运行 `tail -f /var/log/messages` 来查看你的消息日志。
如果你经常使用某个特定的可移动磁盘,你可以通过为它创建一个 */etc/fstab* 条目来简化你的常规操作。以下是一个 CD 和 FAT 闪存驱动器的 */etc/fstab* 条目的示例。
/dev/cd0c /cdrom cd9660 ro,noauto
/dev/sd3i /mnt msdos rw,noauto
你不能为可移动介质使用 DUIDs,因为实际的介质可能会改变。
现在,你可以通过输入 `mount /cdrom` 在 */cdrom* 上挂载你的 CD,并通过输入 `mount /mnt` 在 */mnt* 上挂载你的 FAT 闪存驱动器。
注意,OpenBSD 默认不会创建 */cdrom* 目录;你需要自己创建它。你可以将这两个都指向 */mnt*,但我喜欢在我的系统上有一个专门的 CD 挂载点,并且让两个设备共享挂载点可能会隐藏其中一个文件系统。(记住,OpenBSD 有可堆叠的挂载,如第八章所述。第八章。)
## 挂载文件系统镜像
你可以挂载一个磁盘镜像,就像访问一个磁盘分区一样访问镜像。这在你想从 ISO 中提取一些文件但不想麻烦将镜像烧录到物理介质时非常有用。挂载磁盘镜像的技巧是将镜像附加到设备节点,这样你就可以使用正确的 `mount` 命令。
OpenBSD 使用`vnconfig(8)`程序将磁盘镜像连接到设备节点。(记住,vnode 是内核和文件系统之间的抽象层。)使用`vnconfig`在文件和设备节点之间“连接”vnode,然后通过 OpenBSD 的*/dev/svnd*设备访问它们。根据磁盘镜像类型,镜像可能包含 MBR 分区、disklabel 分区,或者只是一个文件系统。
默认内核有四个 vnode 设备。如果你需要同时挂载超过四个磁盘镜像,请使用`config(8)`的`-e`选项编辑你的内核二进制文件,如第十八章(Chapter 18)中所述。
### 将 Vnode 设备连接到磁盘镜像
`vnconfig(8)`命令接受两个参数:你想要使用的设备节点和你想要挂载的磁盘镜像。
vnconfig /dev/svndXc /path/to/file
注意,此示例使用设备的*c*分区。这允许你将磁盘镜像视为整个磁盘。
假设你有一个名为*install52.iso*的 ISO 镜像,你想将其挂载。首先,使用`vnconfig`将此镜像连接到 vnode 设备 0。
vnconfig /dev/vnd0c install52.iso
然后,你可以使用`mount`将 vnode 连接到*/mnt*目录。
mount /dev/vnd0c /mnt/
OpenBSD 的`mount(8)`足够智能,能够识别这是一个 CD 文件系统并将其以这种方式挂载。如果你要挂载使用不太易检测的文件系统的磁盘镜像,你需要使用该文件系统的特定`mount`命令。
### 从镜像中分离 Vnode 设备
连接到文件的 vnode 设备会一直保持连接状态,直到明确断开连接。你一次只能将一个 vnode 设备连接到一个文件。要断开 vnode 设备与文件的连接,请使用`vnconfig`的`-u`标志。例如,要断开位于*vnd0c*的 vnode 设备,请运行以下命令:
vnconfig -u vnd0c
你现在可以将这个 vnode 设备连接到另一个文件。
在`vnconfig`中使用设备的完整路径是可选的。如果你知道设备名称,你可以使用它而不需要前面的*/dev*,就像前面的例子中那样。
## 基本 NFS 设置
NFS 允许一台机器访问另一台机器上的文件。NFS 起源于 UNIX,但今天它出现在大多数操作系统中,包括来自 Microsoft 和 Apple 的系统。OpenBSD 作为客户端和服务器支持 NFS 的 1 到 3 版本。
关于 NFS 可以写整本书——实际上也确实有人这么做了。我们不会深入探讨 NFS 的细节,而是专注于在 OpenBSD 上使基本的 NFS 共享工作。第一次配置 NFS 可能会让人感到害怕,但配置一个或两个文件共享后,你会发现它非常直接。
如果你有一个复杂的多版本多操作系统的 NFS 环境,或者你想要在数百个活跃客户端之间共享一个目录,你应该进行进一步的研究,但即使是一个基本的设置也会帮助你简化工作的一部分。
NFS 基于客户端/服务器模型。一台计算机(服务器)向其他计算机提供文件系统。服务器正在*导出*一个文件系统,提供的文件系统称为*导出*。NFS 客户端可以以几乎与挂载本地文件系统相同的方式挂载导出。
关于 NFS 的一个重要事项是它是无状态的,这意味着 NFS 不会跟踪连接的状态。你可以重启 NFS 服务器,客户端不会出现异常。当服务器关闭时,客户端无法访问服务器上的文件,但一旦服务器恢复,客户端会从上次停止的地方继续。其他网络文件系统并不总是如此有弹性。无状态性也会带来它自己的问题。例如,客户端无法知道他们当前正在读取的文件是否已被其他客户端修改。
如果你刚开始学习 NFS(或 OpenBSD 对 NFS 的实现),请检查*/var/log/messages*以查找与 NFS 相关的错误信息。如果你在学习过程中反复重新配置 NFS 服务器,但事情仍然无法正确工作,请重启 NFS 服务器和/或客户端。NFS 很复杂,有时从头开始清理堆栈可以解决很多问题。一旦你了解了所有部件如何组合在一起,重启以解决问题通常就不再必要了。
### 注意
NFS 协议在多年中不断发展,每个操作系统都实现了略有不同的 NFS 版本。其他 BSD、Illumos、Linux、Apple、Microsoft 以及大多数其他操作系统都可以与 OpenBSD 的 NFS 支持一起工作,但每个可能都需要针对特定环境进行偶尔的调整。如果你在尝试让 NFS 与 OpenBSD 和另一个操作系统一起工作时遇到问题,请阅读`mount_nfs(8)`并将详细信息输入到你喜欢的搜索引擎中。其他人之前可能已经遇到过这个问题,可能性很大。
## OpenBSD NFS 服务器
默认情况下,OpenBSD 包含了作为 NFS 服务器所需的所有程序,但你必须将其启用。NFS 服务器需要三个守护进程:
+ ****`portmap(8)`****. 将远程过程调用(RPC)服务的请求映射到 TCP/IP 端口号。
+ ****`mountd(8)`****. 监听传入的 NFS 挂载请求。
+ ****`nfsd(8)`****. 处理文件系统操作的请求。
`portmap(8)`守护进程有自己的*rc.conf*标志,因为它可以被许多其他 RPC 服务使用。`mountd(8)`和`nfsd(8)`守护进程由单个*rc.conf*标志控制。
将以下条目添加到*rc.conf.local*以在启动时启动所有三个进程:
portmap=YES
nfs_server=YES
你可以从*/etc/rc.d*中的脚本启动这三个守护进程。然而,如果你现在尝试启动这些守护进程,它们将不会运行。在 NFS 服务器守护进程启动之前,你必须至少配置一个导出。
### 导出文件系统
要导出文件系统,定义哪些客户端可以挂载哪些文件系统以及/或目录在`/etc/exports`中。此文件为服务器上的每个磁盘设备以及可以访问该磁盘设备的每个客户端或客户端组占用单独的一行。每一行最多有三个部分:
+ 要导出的目录或分区
+ 那个出口的选项
+ 允许连接的客户端
在`/etc/exports`条目的三个组成部分中,只有目录是必需的。目录路径不能包含符号链接、双点或单点。
如果我想将我的家目录以读写方式导出到互联网上的每个主机,我可以使用只包含我的`/home`文件夹路径的*exports*行:
/home/mwlucas
这个完全有效(但非常愚蠢)的条目没有任何选项和主机限制。
要导出位于同一分区的多个目录,用单个空格将它们分开。
/home/mwlucas /home/lasnyder
你可以在一行中列出任意数量的目录,只要它们位于同一分区上。
NFS 客户端只能挂载在`/etc/exports`中指定的确切目录。如果你导出`/home/mwlucas`,客户端只能将`/home/mwlucas`附加到挂载点。他们不能挂载,比如,`/home/mwlucas/bin`。如果你想导出整个分区,你也可以这样做。如果你想允许客户端挂载挂载点下任何目录,指定挂载点和`-alldirs`选项。你不能在子目录中使用`-alldirs`;它必须是实际的挂载点。此条目允许任何人挂载`/home`中的任何目录:
/home -alldirs
要导出多个分区或来自多个分区的目录,请在单独的行中指定它们。
/home -alldirs
/var/log
每次你更改`/etc/exports`时,都必须通知`mountd`重新读取其配置。你可以通过将`reload`参数传递给`mountd`启动脚本来完成此操作:
/etc/rc.d/mountd reload
虽然这些简单的挂载让你了解了 NFS 的工作方式,但它们非常不安全。要创建一个智能导出,你需要一些选项和访问列表。让我们看看 NFS 的一些常用选项。
### 只读挂载
你可能希望分享文件时无需担心下属是否会删除、修改或以其他方式撤销你的辛勤工作。你可以通过使用`-ro`选项将文件以只读方式共享。在这里,我将我的家目录提供给世界上所有的计算机,但作为一个只读共享:
/home/mwlucas -ro
这比向整个世界提供我的 NFS 导出读写权限稍微聪明一点,但只是稍微聪明一点。
### NFS 和用户
你已经知道文件所有权和权限与 UID 数字相关联。与许多其他文件共享协议不同,NFS 也使用 UID 来标识文件所有权。例如,在我的测试服务器上,我的账户`mwlucas`使用 UID 1000;在我的客户端,我的`mwlucas`账户也使用 UID 1000。这简化了我的生活,因为我无需过多担心文件所有权;服务器上`mwlucas`拥有的文件在客户端也是由`mwlucas`拥有的。
在只有少数用户和机器的小型网络中,^([22]) 你可能通过在所有系统上为同一用户分配相同的 UID 来无问题地保持 UID 号码同步。但在大型网络中,如果有多个用户,并且用户在自己的机器上有 root 权限,文件所有权可能会迅速成为一个严重问题。解决这个问题最好的办法是通过 LDAP 或 Kerberos 维护一个授权用户的中央存储库。
无论你如何管理你的用户,NFS 对 root 账户的处理方式都不同。NFS 服务器不能信任客户端机器上的 root 来以 root 身份在服务器上执行命令或写入文件;如果是这样,一个 NFS 客户端的漏洞就意味着 NFS 服务器的漏洞。默认情况下,来自客户端的 root 请求被映射到 UID 和 GID 32767(也称为`nobody`)。
如果你想要将 root 映射到特定的用户而不是通用的 UID `nobody`,请使用`-maproot`选项并指定一个用户名或 UID。在这里,我们将来自客户端的 root 请求映射到服务器上的`nfsroot`用户:
/home/mwlucas -maproot=nfsroot
你可以通过在用户名后指定它们,并用冒号分隔来给映射的 root 用户一个远程 root 账户可以访问的组列表。在这里,我们给客户端的 root 用户以`nfsroot`用户和`customers`、`webmasters`组的权限访问服务器:
/home/mwlucas -maproot=nfsroot:customers:webmasters
如果你想要明确地从所有组中移除映射的 root 用户,在用户名或 UID 后放置一个冒号,如下例所示:
/home/mwlucas -maproot=nfsroot:
假设你希望所有 NFS 客户端,无论客户端系统上的用户名如何,都使用 NFS 服务器上的单个用户 ID。`-mapall`选项允许你这样做。此选项使用与`-maproot`选项相同的格式。在这里,我们将所有 NFS 用户映射到服务器上的用户名`nfsuser`:
/home/mwlucas -mapall=nfsuser
正确控制用户访问将有助于保护你的 NFS 服务器。
### 允许的客户端
默认情况下,每个主机都可以访问你的 NFS 服务器。由于许多原因,这并不是一个好主意。你可以通过在导出条目末尾列出它们的 IP 地址来限制允许访问你的 NFS 服务器的客户端。
/home/mwlucas 192.0.2.1
你也可以通过主机名指定客户端,但如果服务器有 DNS 故障,它将不允许任何客户端访问。
/home/mwlucas treble.blackhelicopters.org
要允许访问整个网络,请使用`-network`和`-mask`选项。以下示例使用子网掩码允许访问地址 192.0.2.0 到 192.0.2.15。(如果你不熟悉子网掩码,请阅读第十一章。)
/home/mwlucas -network=192.0.2.0 -mask=255.255.255.240
当设置你的 NFS 服务器时,我建议你只授予需要访问的主机权限。
### 一个分区多个导出
对于每个分区和允许的客户端的组合,你只能有一行。如果*/home*是一个单独的分区,你不能有一个看起来像这样的导出文件:
/home/mwlucas -maproot=nfsroot: 192.0.2.1
/home/pkdick 192.0.2.1
如果两个目录位于同一分区上,NFS 不允许您使用不同的权限将它们导出到同一主机。然而,您可以像这里所示的那样,将一个分区的目录导出到不同的主机,使用不同的权限。
/home/mwlucas -maproot=nfsroot: 192.0.2.1
/home/pkdick 192.0.2.2
您可以将分区的目录导出到不同的主机,使用不同的权限。
/home/mwlucas -maproot=nfsroot: 192.0.2.1
/home/mwlucas -maproot=root 192.0.2.2
只有通过结合 IP 限制和控制用户权限,您才能有效地控制 NFS 服务器访问。
## NFS 客户端
OpenBSD 的 NFS 客户端不需要任何守护进程或配置。只需`mount`远程文件系统。在这里,我将我的服务器`treble`上的家目录挂载到`/mnt`:
mount treble:/home/mwlucas /mnt
当通过 NFS 挂载远程文件系统时,输入主机名或 IP 地址,一个冒号,然后是目录。因为我客户端和服务器上的 UID 相同,所以我可以像处理本地文件系统上的文件一样访问、修改、删除和添加`/mnt`中的文件。
使用`df(1)`或`mount(8)`验证您的挂载。
$ df -h
Filesystem Size Used Avail Capacity Mounted on
/dev/sd0a 1005M 266M 689M 28% /
…
treble:/home/mwlucas 26.9G 21.5M 25.5G 0% /mnt
通过 NFS 挂载的目录显示得像任何其他挂载点一样。
要在启动时自动挂载 NFS 共享,或者只是为了方便将来使用,您可以使用`/etc/fstab`条目。如果您的系统在启动时可能没有 DNS 可用,请使用 NFS 服务器的 IP 地址。以下示例指定了两个`fstab`条目:一个使用主机名,一个使用 IP 地址:
treble:/home/mwlucas /mnt nfs,noauto rw 0 0
192.0.2.88:/usr/ports /usr/ports nfs,noauto ro 0 0
将所有 NFS 分区设置为`dump`和`fsck`的数字为`0`。不要在 NFS 挂载点上运行`fsck`或`dump`,因为这些程序需要原始磁盘访问,而 NFS 不提供这种访问。
使用您喜欢的任何其他挂载选项。OpenBSD 社区建议在适用时使用`noexec`、`nodev`和`nosuid`。我建议在不需要用于正常服务器操作的非 NFS 分区上使用`noauto`,这样不可用的 NFS 服务器就不会挂起您的机器的启动过程。
NFS 性能在很大程度上取决于您的硬件、本地网络、涉及的客户端和服务器、月亮的相位以及无数其他因素。如果您对 NFS 性能不满意,请阅读`mount_nfs(8)`并尝试使用 TCP 或 UDP、读写大小以及可能的时间超时进行实验。如果您需要一个复杂的 NFS 环境,您绝对应该花些时间学习更多关于 NFS 的知识。
## 软件 RAID
红外独立磁盘阵列(RAID)技术已成为在机器内镜像硬盘或组合多个硬盘以形成一个巨大分区的标准方式。在许多类型的 RAID 阵列中,如果一个硬盘故障,系统可以在您更换故障硬盘或第二个硬盘故障之前继续运行而不会丢失数据。
您可以从硬件中获得 RAID,或者让操作系统执行 RAID 操作。硬件 RAID 控制器看起来很棒,但实际上只是运行特殊软件的不错的磁盘控制器。使用`softraid(4)`驱动程序,OpenBSD 可以做到同样的事情,让您可以使用普通磁盘构建 RAID 数组。您可以使用与硬件 RAID 控制器一样多的磁盘和 OpenBSD 的 RAID 管理程序`bioctl(8)`以及`softraid(4)`软件 RAID 驱动程序来做几乎所有的事情。
### 注意
除了管理软件 RAID 之外,OpenBSD 的`bioctl(8)`还可以管理大多数类型的硬件 RAID 控制器。如果您计划使用硬件 RAID,阅读`bioctl`手册绝对值得您花时间。
### RAID 类型
OpenBSD 支持以下 RAID 配置:
> **RAID-0,或**条带化
>
> 这种类型不是冗余的。它至少需要两个相同大小的磁盘,数据在磁盘之间共享以增加分区大小和提高吞吐量。您可以使用 RAID-0 将五个 4TB 磁盘组合成一个 20TB 的虚拟磁盘,但请注意:如果数组中的任何一个硬盘驱动器失败,您将丢失所有数据。RAID-0 在您需要非常大的文件系统时很有用,但它比单个磁盘更脆弱,因为它提供了多个故障点(或者正如我的一位半文学、半幽默的朋友曾经说过,“RAID-0 给‘一磁盘统治一切’这个短语赋予了全新的意义’)。RAID-0 数组的大小是所有硬盘驱动器大小的总和。
>
> **RAID-1,或**镜像
>
> 在这种类型中,一个磁盘的内容被复制到另一个磁盘上。镜像至少需要两个相同大小的磁盘,RAID-1 数组的大小等于数组中最小驱动器的大小。我使用镜像来保护所有重要数据,因为它甚至给廉价的桌面机箱服务器提供了一定程度的数据保护。OpenBSD 的软件 RAID 完全支持这一级别。
>
> **RAID-4,或**跨磁盘条带化数据,并使用专用奇偶校验磁盘
>
> 这种类型至少需要三个相同大小的磁盘。奇偶校验数据允许 RAID 阵列在丢失磁盘的情况下恢复数据,而 RAID-4 将奇偶校验数据存储在特定的磁盘上。这意味着您可以在不丢失数据的情况下丢失任何一个磁盘。当我写这段话的时候,`bioctl`的 RAID-4 支持是实验性的。希望这本书到达您手中之前,这种支持已经完善,但如果不是这样,您将需要使用硬件 RAID 卡来获得 RAID-4。
>
> **RAID-5,或**跨所有驱动器共享奇偶校验的条带化
>
> 这是当前行业标准的冗余。奇偶校验数据提供数据冗余——单个驱动器的丢失不会破坏任何数据。它需要至少三个相同大小的磁盘。与 RAID-4 不同,RAID-5 同时将奇偶校验数据共享到所有驱动器上。虽然吞吐量不如 RAID-0,但 RAID-5 阵列可以同时处理多个 I/O 请求。你的 RAID-5 阵列的大小是除了一个硬盘之外所有硬盘的总大小。如果你有五个 4TB 的硬盘,阵列将是 16TB((5 - 1)× 4TB)。与 RAID-4 一样,`bioctl`中的 RAID-5 支持是不完整和实验性的。我希望在你阅读此内容之前它将完成,但如果不是,你将需要使用硬件 RAID 卡来使用 RAID-5。
根据 RAID 标准,这些级别中的每一个都需要相同大小的磁盘。但话虽如此,OpenBSD 的`softraid`使用分区而不是磁盘。你可以使用不同大小的磁盘,但你的 RAID 阵列将只使用每个磁盘上与最小驱动器相等的空间。如果你想镜像一个 1TB 的驱动器和 2TB 的驱动器,你的镜像将只提供 1TB 的空间。较大驱动器上的多余空间将被浪费.^([23])
除了标准的 RAID 方法之外,`softraid`还允许你加密 RAID 阵列中所有磁盘上的数据(如加密磁盘分区中所述)。它还允许你*连接*磁盘。连接的磁盘只是连续运行以创建一个大的虚拟磁盘。你可以连接两个 500GB 的磁盘和一个 1TB 的磁盘来创建一个 2TB 的分区。这些磁盘不需要相同的大小,但与 RAID-0 一样,它们是脆弱的。任何单个磁盘的损坏都将完全破坏虚拟磁盘并丢失所有数据。由于创建连接磁盘的过程与创建 RAID-0 磁盘的过程非常相似,我们将在创建 softraid 设备中介绍它。
### 为 softraid 准备磁盘
`softraid`软件 RAID 设备使用 disklabel 分区构建其虚拟磁盘。要在`softraid`阵列中使用磁盘,就像为常规文件系统中的磁盘准备一样准备它。
在 i386 和 amd64 上,`softraid`设备下的磁盘需要一个 MBR 分区。要使用单个 MBR 分区标记整个磁盘,请在磁盘上运行`fdisk -i`。
假设你有五个磁盘要用于 RAID 阵列:`sd2`、`sd3`、`sd4`、`sd5`和`sd6`。你需要按照以下方式准备每个磁盘:
fdisk -i sd2
Do you wish to write new MBR and partition table? [n] y
Writing MBR at offset 0.
对阵列中的每个磁盘重复此操作。
一旦你为所有磁盘添加了 MBR,你需要在每个磁盘上放置一个 disklabel 分区。我倾向于使用分区字母*p*(最后一个可用的分区字母)为`softraid`设备设置分区。以下是设置`softraid`磁盘的方法:
disklabel -E sd2
Label editor (enter '?' for help at any prompt)
1 > a
2 partition: [a] p
offset: [64]
size: [104856191]
3 FS type: [4.2BSD] RAID
4 > q
5 Write new label?: [y] y
首先,我们使用 `a` **1** 添加一个分区,并分配分区字母 `p` **2**。我们不是使用我们常用的 4.2BSD 文件系统类型,而是分配一个 `RAID` **3** 文件系统类型。然后我们退出 **4** 并让 `disklabel` 将更改写入磁盘标签分区 **5**。
如果你有多块相同的磁盘,你可以使用 `disklabel` 来保存这块磁盘的配置,如下所示:
disklabel sd2 > disklabel.sd2.raid
这将把磁盘 `sd2` 上的标签保存到文件 *disklabel.sd2.raid* 中。你可以使用 `disklabel(8)` 将这种分区复制到其他磁盘,`disklabel` 在复制时会为每个磁盘分配一个唯一的 DUID。这可以节省你为每个磁盘通过交互式编辑器的需要。让我们将这个 disklabel 应用到每个分区:
disklabel -R sd3 disklabel.sd2.raid
disklabel -R sd4 disklabel.sd2.raid
disklabel -R sd5 disklabel.sd2.raid
disklabel -R sd6 disklabel.sd2.raid
磁盘 `sd2` 到 `sd6` 现在已准备好被整合到 `softraid` 中。
### 创建 softraid 设备
使用 `bioctl(8)` 将磁盘拖入软件 RAID。你需要包含在 RAID 中的磁盘分区。OpenBSD 软件 RAID 数组以 `softraid` 开头,后面跟着一个数字。使用 `-c` 参数来指定 RAID 类型,使用 `-l` 来指定分区,并以你创建的 `softraid` 的名称结束。
bioctl -c raidlevel -l partition1,partition2… softraidX
我们有五个磁盘分区——`sd2p`、`sd3p`、`sd4p`、`sd5p` 和 `sd6p`——要添加到 `softraid` 设备中。要从这些分区构建一个 RAID-5 设备,请运行以下命令:
bioctl -c 5 -l sd2p,sd3p,sd4p,sd5p,sd6p softraid0
softraid0: SR 1 RAID 5 volume attached as 2 sd7
响应表明我们已成功创建了一个 RAID-5 设备 **1**,并且它作为设备 `/dev/sd7` **2** 可用。在一个空白 RAID 磁盘上,你需要像其他任何新磁盘一样准备它,运行 `fdisk -i sd7` 和 `disklabel` 来创建 MBR 和 OpenBSD 分区,使用 `newfs` 在新分区上创建文件系统,然后你就可以开始了。(有关添加新磁盘的说明,请参阅第八章的详细说明。)
你可以通过选择不同的 `-c` 选项将这个设备做成 RAID-0、RAID-1 或 RAID-4 设备。复杂的是串联的 `softraid`。要将所有磁盘一起放入一个单一的串联虚拟分区中,请使用 `-c c`。
bioctl -c c -l sd2p,sd3p,sd4p,sd5p,sd6p softraid0
softraid0: SR CONCAT volume attached as sd7
### softraid 状态
要检查 RAID 数组中每个设备的健康状态,给 `bioctl` 提供一个 `softraid` 设备的设备名。
bioctl softraid0
Volume Status Size Device
softraid0 0 Online 214744170496 sd7 RAID5
0 Online 53686099456 0:0.0 noencl
1 Online 53686099456 0:1.0 noencl
2 Online 53686099456 0:2.0 noencl
3 Online 53686099456 0:3.0 noencl
4 Online 53686099456 0:4.0 noencl
我们可以看到,五个驱动器正在使用中,全部组装成一个 RAID-5 虚拟驱动器。这里的一切看起来都很健康。任何看起来不太像这样的事物都表明存在问题。
### 识别失败的 softraid 卷
如果你有一个 RAID-1、RAID-4 或 RAID-5 `softraid` 卷,即使丢失一个驱动器也不会丢失你的数据。`bioctl` 会告诉你哪个驱动器失败了。这里,我的 `softraid` 卷中的一个驱动器已经失败了:
bioctl softraid0
Volume Status Size Device
softraid0 0 Degraded 214744170496 sd7 RAID5
0 Online 53686099456 0:0.0 noencl
1 Offline 0 0:1.0 noencl <>
2 Online 53686099456 0:2.0 noencl
3 Online 53686099456 0:3.0 noencl
4 Online 53686099456 0:4.0 noencl
仔细观察,我可以看到驱动器 `sd2`、`sd3`、`sd4` 和 `sd6` 仍然可用并且在使用中。我的所有数据应该仍然完好无损,但在另一个磁盘失败之前,我需要更换 `sd5`。
### 重建失败的 softraid 卷
到我写这篇文章的时候为止,你无法重建失败的`softraid` RAID-4 或 RAID-5 设备。你必须备份你的数据,更换失败的驱动器,删除`softraid`设备,重新创建文件系统,并从备份中恢复。然而,你可以重建 RAID-1 设备。
让我们看看如何在 RAID-1 设备中更换磁盘。一个健康的三磁盘`softraid`镜像可能看起来是这样的:
bioctl softraid0
Volume Status Size Device
softraid0 0 Online 53686099456 sd51 RAID1
0 Online 53686099456 0:0.0 noencl
1 Online 53686099456 0:1.0 noencl
2 Online 53686099456 0:2.0 noencl
注意,这个 RAID 设备具有设备节点`sd5`[1]并且包括分区*sd2p*、*sd3p*和*sd4p*[2]。
我们更换了两个磁盘并重新启动这台机器。突然,*softraid*设备看起来非常不同。
bioctl softraid0
Volume Status Size Device
softraid0 0 Degraded 53686099456 sd5 RAID1
0 Offline 0 0:0.0 noencl <>
1 Offline 0 0:1.0 noencl <>
2 Online 53686099456 0:2.0 noencl
分区*sd3p*和*sd4p*丢失。这是因为底层磁盘已被更换。^[[24]) 准备替换磁盘以用于软件 RAID,如为 softraid 准备磁盘中所述。然后运行`bioctl`,使用`-R`标志指定`softraid`设备中要更换的磁盘。
bioctl -R /dev/sd3p sd5
softraid0: rebuild of sd5 started on sd3p
如果你使用`bioctl`检查设备的状况,你会看到磁盘状态现在显示为“重建中。”
如果你有一个包含两个以上磁盘的镜像,你必须单独重建每个磁盘。先重建第一个磁盘,然后重建第二个磁盘。
### 删除 softraid 设备
要从你的系统中移除`softraid`设备,向`bioctl`传递`-d`标志和`softraid`设备的设备名。以下是如何移除我们刚刚创建的 RAID-5 设备的方法:
bioctl -d sd7
### 警告
一旦你删除了 RAID 设备,除非你重新创建它并从备份中恢复数据,否则你无法恢复它。
### 重复使用 softraid 磁盘
`softraid`在其使用的磁盘开头写入元数据。在使用这些磁盘作为另一个`softraid`设备之前,你需要覆盖这些元数据。使用`dd(1)`覆盖磁盘的前一兆字节左右。
dd if=/dev/zero of=/dev/sd2c bs=1k count=1024
1024+0 records in
1024+0 records out
1048576 bytes transferred in 0.594 secs (1765074 bytes/sec)
这将擦除 MBR 分区、任何初始磁盘标签以及磁盘上的任何文件系统信息。你现在可以像普通磁盘一样在`softraid`设备中重复使用这些磁盘。
### 从 softraid 设备启动
`softraid`功能仍在开发中。最终,你将能够使用安装程序构建一个软件 RAID 设备,在该设备上安装 OpenBSD,并直接运行完整的 RAID 配置。但在我写这篇文章的时候,你需要跳过一些步骤才能实现这一点。而不是记录一个会随着 OpenBSD 完成`softraid`开发而变化的特定过程,我将告诉你去互联网和*misc@OpenBSD.org*存档中搜索最新的说明。
## 加密磁盘分区
有时候我能看到未来。当有人说,“我已经加密了我的硬盘!”我会有一种通灵的感觉,他们会在说“我丢失了所有数据!”虽然在某些情况下加密硬盘分区是合理的,但大多数时候,这只是虚荣。在本节中,我将假设你理解何时真正需要磁盘加密,如果你在我丢失数据时不对我说抱怨,我将对你表示礼貌。
### 创建加密分区
OpenBSD 将磁盘加密作为`bioctl(8)`选项——具体来说,就像 RAID 纪律一样。在磁盘活动通常通过 RAID 纪律传递的地方,这里它们通过加密纪律传递。加密磁盘甚至显示为`softraid`设备。与对 RAID-5 的支持类似,对加密文件系统的支持是实验性的。尽管它*应该*工作,但如果某些功能尚未包含或如果它吃掉了你的整个磁盘,请不要感到惊讶。做好备份。重新阅读上一段。再次——*请*不要在我面前抱怨它不起作用。
在 OpenBSD 下,加密卷只能包含单个分区。使用 RAID 类型`C`来指定加密卷。以下是如何在`sd4p`分区上创建加密卷的步骤:
bioctl -c C -l sd4p softraid0
1 New passphrase:
Re-type passphrase:
softraid0: SR CRYPTO volume attached as sd5
当提示**1**时,请输入密码短语两次。一个好的密码短语应该由多个单词组成,并包含字符、符号、数字、标点符号和空格的混合。密码短语是用于加密和解密数据的秘密代码,因此它越长、越多样化,就越好。记住这个密码短语;你必须再次输入它才能恢复你的数据。一旦你两次输入了你的密码短语,`bioctl`就会创建加密的磁盘设备。在这种情况下,它创建了一个加密磁盘`softraid0`作为`disk sd5`。
### 使用加密分区
不要立即挂载这个新磁盘!相反,使用`fdisk`来检查我们的新加密分区。
fdisk sd5
Disk: sd5 geometry: 6526/255/63 [104855663 Sectors]
Offset: 0 Signature: 0x8BF9
Starting Ending LBA Info:
: id C H S - C H S [ start: size ]
0: D9 230285 63 36 - 134263 55 58 [ 3699532529: 2752373385 ]
1: 8C 73068 221 44 - 176434 56 49 [ 1173851386: 1660564401 ]
2: C9 218148 78 47 - 141866 243 13 [ 3504552580: 3069507328 ]
3: AC 125252 6 1 - 245307 77 22 [ 2012173758: 1928688070 ]
基础磁盘是空的,我们的`fdisk`输出看起来像垃圾,但这个磁盘现在是一个加密卷。
现在加密磁盘已经存在,创建 MBR 分区并添加 disklabel 分区,就像添加任何其他磁盘一样。然后你可以使用设备节点挂载你的加密设备分区——再次,就像任何其他磁盘一样。
要卸载解密分区,通过传递`bioctl`的`-d`参数来销毁`softraid`设备。
bioctl -d sd5
对于没有密码短语的人来说,这个分区现在看起来像随机的垃圾。
### 自动解密
如果你有一个加密分区,你大概不希望 OpenBSD 在系统启动时自动解密和挂载它。(加密分区的全部意义在于只有拥有密码短语的人才能访问加密数据。)尽管如此,我不是那种告诉你不要自杀的人,所以如果你必须自动解密分区,你可以这样做。
首先,创建一个包含你的密码文件的文件。将此文件的拥有权赋予 root,并设置权限为`600`(所有者可读写;其他用户无访问权限),然后使用`-p`标志将此文件传递给`bioctl(8)`。在这个例子中,加密硬盘被创建为*/dev/sd5*,并且有一个分区在*/dev/sd5a*上。我已经将我的密码存储在文件*/etc/passphrase*中,所以我可以运行类似以下这样的命令:
bioctl -c C -l sd4p -p /etc/passphrase softraid0
mount /dev/sd5a /home/mwlucas
将此内容添加到*/etc/rc.securelevel*中将在启动时挂载这个加密分区。
你现在应该对如何管理 OpenBSD 硬盘和文件系统有了很好的了解。接下来,我们将探讨一些 OpenBSD 的特殊安全功能。
* * *
^([21]) 我不知道 128m 的转储级别意味着什么,除了“不是我所希望的。”
^([22]) “几个用户”我是指多少个?当你在所有系统间同步 UID 开始真正、真正地让你感到烦恼时,你就不再只有几个用户了。
^([23]) 你可以在较大驱动器上未使用的空间中添加一个非 RAID 分区,但这会对你的系统性能造成极大的破坏。只是买更多的硬盘,你这个吝啬鬼。
^([24]) 如果需要在硬盘上强制产生错误,从机器上移除硬盘肯定可以做到。
^([25]) 我帮不上什么忙——我所能做的就是说我“早就告诉你了。”顺便说一句:无论多么愉快的事情,如果你经常做,你都会感到厌倦。
## 第十章. 保护您的系统
*Hackers at the gates?*
*Puffy the Barbarian*
*defends against fiends.*
 保护您的系统意味着确保您的计算机资源仅由授权人员用于授权目的。即使系统没有重要数据,它仍然有宝贵的 CPU 时间、内存、存储和带宽。认为他们的系统太不重要而不值得任何人闯入的人可能会发现他们的设备正在托管色情内容或中继针对工业或军事网站的攻击。如果您像我一样,您宁愿不发现您的计算机通过执法人员的敲门使政府机构瘫痪。
控制大量远程计算机变得越来越容易。每年,都会出现越来越多的用于渗透服务器的点击式工具包。当一位聪明的攻击者发布一个漏洞利用程序时,任何人都可以使用它。闯入计算机是一笔大生意,如果您的计算机没有受到保护,*将会*被渗透。唯一的问题是方式。
通常来说,入侵者不会闯入操作系统;他们侵入在操作系统上运行的服务器程序。即使是最偏执、默认安全性的操作系统也无法保护编写不良的程序免受自身的影响。OpenBSD 的 W^X 和地址空间布局随机化等特性在很大程度上保护了操作系统免受有缺陷程序副作用的影响,但程序本身仍然会崩溃和燃烧。OpenBSD 经过广泛的审计和测试,以消除最常见的安全漏洞,但无法保证每个安全漏洞都已根除。新特性不断出现,可能会以意想不到的方式与旧功能(以及彼此)交互。有关 OpenBSD 特定特性的更多详细信息,请查看*[`www.OpenBSD.org/papers/`](http://www.OpenBSD.org/papers/)*的论文和演示文稿集合。
没有任何单一的工具可以保护您的服务器免受所有威胁,也没有任何单一的工具适用于所有环境。了解 OpenBSD 的安全特性有助于您不仅理解工具的功能,而且了解何时应该使用它们以及何时它们对您的特定情况无济于事。最好的开始方式是理解威胁。
## 敌人是谁?
专门研究安全的书籍将攻击者划分为更小、更具体的群体,并包括各种边缘情况,但这不是您在这里的原因。我把潜在的攻击者分为四组:脚本小子、僵尸网络、不满的用户和熟练的攻击者。这些类别易于理解,包括您可能遇到的 99%的攻击者。
### 脚本小子
最常见的攻击者类型,脚本小子,不是系统管理员。他们是业余爱好者,下载攻击脚本并寻找防御薄弱、易受攻击的系统。
脚本小子很容易防御:保持你的软件更新,并遵循良好的计算实践。就像蝗虫一样,脚本小子很容易被压扁,但这个小东西实在是太多了!
### 恶意软件网络
恶意软件网络由被蠕虫或病毒破坏的机器组成,并从中央点控制。恶意软件网络的控制者可能会使用受害机器来寻找更易受攻击的主机,发送垃圾邮件,或者入侵安全网站。大多数恶意软件网络由 Windows 或 Linux 机器组成,但没有任何理由说明这样的蠕虫不能针对 OpenBSD。病毒作者需要付出很多努力,但如果他找到一个合适的安全漏洞,这是可以想象的。
幸运的是,恶意软件防御与脚本小子防御类似。如果你保持软件更新,安全配置你的服务器软件,并遵循良好的计算实践,你不必担心太多。
### 愤世嫉俗的用户
安全专家通常声称,系统合法用户是大多数安全问题的原因。^([26)] 合法用户最有可能知道你的安全漏洞在哪里,觉得系统规则不适用于他们,并且有必要的访问权限和时间来尝试破解你的安全。如果你告诉一个员工公司政策禁止他访问计算机资源,而这个员工觉得他*应该*有权访问,他可能会寻找绕过限制的方法。你可以修补所有服务器并使用直接敌对的防火墙来保护它们,但如果有人有物理访问权限并且知道 root 密码,你的保护措施就毫无用处。
从两个层面解决这个问题。第一个是技术层面:保持你的服务器修补和更新。第二个是人为层面:不要留下半完成或半文档化的项目。那个为了紧急接入而安装的未加密调制解调器?把它扔掉,或者给它设置一个密码。同样,对于运行在非标准端口上的 telnet 服务器也是如此。
隐藏式安全最差。当有特权的用户离开公司时,立即禁用他的账户,更改所有管理密码,通知员工该人员的离职情况,并提醒他们不要与该人员共享机密信息。实施具有实际违规处罚的计算机安全政策。如果你有人力资源部门,让员工同意该政策并坚持他们执行它。
防止愤世嫉俗的用户最好的方法是什么?不要懒惰。
### 技能攻击者
作为最危险的一群人,熟练的攻击者包括合格的系统管理员、安全研究员、渗透专家以及想要访问特定资源的犯罪分子。如今控制计算机已经成为一项有利可图的生意。发送垃圾邮件或发起分布式拒绝服务攻击可以带来大量金钱。这些入侵者不在乎他们攻击谁,只要他们能确保获得所需的计算资源。
然而,如果你的公司有宝贵的机密信息,你可能会吸引到完全不同类型的入侵者:那些特别想要访问你网络的人。如果你的雇主从软件到前轮驱动车辆的铸铁郁金香都有所创造,那么你的产品很可能存在非法复制品的市场。有人会认为探测你暴露给互联网的每个 IP 地址上的每个端口是值得的。这可能需要很长时间,但没关系。你的数据有价格标签,而扫描是便宜的。这通常被称为高级持续性威胁,或 APT。
阻止其他类型入侵者的安全措施也会影响熟练攻击者所使用的技巧。如果你已经放弃了那个不安全的入站访问方法,入侵者就找不到它。如果你的服务器和程序都是最新版本并且配置正确,入侵者将需要找到之前未知的漏洞来入侵你的网络。如果一个熟练的入侵者真的想要获取*你的*公司的数据,他需要改变策略。他可能会尝试翻垃圾桶寻找旧粘性便签,或者甚至装扮成电信维修工试图安装数据嗅探器。如果一个入侵者对你的网络了如指掌,而他最容易的入侵方式*仍然*是来自冒险电影中的场景,那么你的安全状况相当不错。
### 注意
“黑客”这个词的含义因人而异。在技术界,黑客不仅对技术的内部运作感兴趣,而且能够创造新技术。媒体已经将这个词的含义转变为“入侵计算机的人”。我建议完全避免使用“黑客”这个词,而使用“入侵者”或“贪婪的吸脂猪狗”等术语。当然,何时使用每个术语取决于你。
## OpenBSD 安全公告
防御所有类型入侵者的最佳防线是保持你的计算机软件更新。这意味着你需要知道何时更新你的系统以及更新什么。OpenBSD 项目维护了一个低流量的邮件列表,*security-announce@OpenBSD.org*,专门用于向用户广播新的安全警报。请订阅此列表。
如果你不想再订阅另一个邮件列表,这些安全警报也发布在 OpenBSD 特定的网站上,例如 *[`www.undeadly.org`](http://www.undeadly.org)*。
注意,这不会为在 OpenBSD 上运行的第三方软件提供安全警报。你必须单独为这些程序获取更新。查看软件的网站以获取有关如何获取其安全公告的详细信息。如果你忽视更新而有人劫持了不安全的 Web 应用程序,那么你花费在保护操作系统上的所有时间都将浪费。
## OpenBSD 内存保护
最常见的入侵途径之一是攻击计算机内存中的内容。如果入侵者能够访问他们不应能够访问的内存,或者他们能够使程序访问不应访问的内存,他们就有无数种方法进入系统。
OpenBSD 包含了一系列系统内存的安全特性,系统管理员实际上从未真正看到过。你不需要开启不可执行的堆栈;它只是在那里。
其中一些特性仅在 OpenBSD 中出现。一些首先出现在 OpenBSD 中,然后传播到其他地方。一些来自研究论文。其他则是基于硬件特性。
OpenBSD 团队在安全特性方面比许多其他项目更加积极主动。以几年前 ProPolice 的部署为例。ProPolice 是一种编译器特性,可以防止某些类型的缓冲区溢出。在早期启用 ProPolice 时,许多软件无法构建。甚至更多软件可以构建,但在使用时崩溃。这些失败不是 ProPolice 的问题。ProPolice 只是暴露了软件中的编程错误。但许多用户和开发者说,“启用 ProPolice 会破坏所有东西,所以不要启用它。”
OpenBSD 团队在开发快照中默认启用了 ProPolice。发生了什么?很多问题——很多问题——都出现了。许多 OpenBSD 用户需要的第三方应用程序要么无法构建,要么无法运行。第三方应用程序的供应商开始收到来自 OpenBSD 用户的错误报告,他们能够确切地说明软件是如何出错的。软件供应商开始修复错误。
ProPolice 并没有导致这些崩溃;它只是暴露了错误。通过默认启用 ProPolice,OpenBSD 为免费软件世界提供了修复这些错误的动力。最终,随着 ProPolice 揭示的错误类型变得越来越不常见,其他操作系统也启用了 ProPolice。OpenBSD 愿意采取这一步骤,提高了整体计算机的安全性。
如果你密切关注 OpenBSD 的开发,预计会看到更多这样的行为。OpenBSD 团队做他们认为最正确的事情,而不是最方便或最容易的事情。
你应该了解的常见内存安全特性包括 W^X、.rodata 段、保护页、随机内存分配、ProPolice 以及保护`atexit`和`stdio`。我们将逐一介绍。
### W^X
W^X 代表写或执行。一旦程序被加载,该程序在内存中的页面要么是可写的,要么是可执行的,但不能同时两者都是。
一种常见的攻击技术是欺骗程序将信息写入内存,然后执行该内存片段。攻击者可能会说服程序将信息写入一块内存,但内核不会允许执行该内存。
一些硬件平台(如 amd64)具有 W^X 的硬件支持。如果存在这种支持,OpenBSD 会使用它。
### .rodata 段
传统上,包含程序代码的内存段通常有两个部分:实际代码和只读数据,或*.rodata 段*。在过去,一些操作系统允许程序修改只读内存。OpenBSD 通过利用硬件功能(如果可用)来防止这一点。
### 保护页
许多软件在访问它们分配的内存之外时都会出现问题。如果一个程序写入不属于它的内存,它就是在写入属于另一个程序的内存。入侵者利用这一点来利用程序。*保护页*是紧挨着程序分配的内存的单页内存。程序不能写入此内存。如果程序尝试写入保护页,它很可能会崩溃。通过强制执行此限制,OpenBSD 保护其他程序。
在所有地方使用保护页会消耗大量内存,因此 OpenBSD 只在仔细选择的位置启用保护页。
### 地址空间布局随机化
传统上,计算机连续分配内存。这可能会给入侵者带来某些优势。如果他们知道程序 A 通常在程序 B 之后加载,并且他们知道他们可以让程序 B 写入其分配之外的内存空间,他们可以猜测他们可以写入程序 A 的内存空间并使程序 A 以可预测的方式失败。这样做需要一定的技能,但一旦有人发现这种利用方法,无数的人都可以使用它。
OpenBSD 随机化内存分配的位置。连续启动的两个程序不会获得连续的内存块。这种随机化是智能的,以避免浪费内存。入侵者不能以这种方式利用一个程序攻击另一个程序。
### ProPolice
ProPolice 保护代码免受操纵内存栈的攻击。当代码编译时,ProPolice 会添加额外的代码以使程序保持在它自己的内存区域。如果 ProPolice 确定特定的内存区域(称为*金丝雀*)已被更改,它将立即终止程序。与其他内存保护技术防止写入可执行内存不同,当可写入的内存(*可以*写入,但具体*不应该*写入)被更改时,ProPolice 会终止进程。
### 以及更多!
OpenBSD 包含大量分散的小内存保护器。以下是一些小样本:
+ `malloc()`和`atexit()`系统调用在更新后标记内存为不可写。
+ 文件描述符处理已经经过仔细审计。
+ 当不涉及浮点数时,`snprintf`是异步信号安全的。
列表还在继续。
这些在现实世界中是否可能被利用?其中一些已经被利用,而另一些只是理论上的。但我会更愿意保护自己免受理论威胁,而不是假设没有人能破坏从未被破坏过的事物。
## 文件标志
所有类 Unix 操作系统都共享一个通用的权限方案,但 OpenBSD(以及大多数基于 BSD 的操作系统)通过*文件标志*扩展了权限方案。文件标志与权限一起工作,以改变文件安全性。标志可以使文件不可更改,使得现有数据无法被删除,用户只能向文件中添加内容,并产生其他几种效果。一些标志的功能与安全性无关,但我们将特别关注安全标志。文件标志在`chflags(1)`中列出并文档化。
### 文件标志类型
许多文件标志的效果取决于系统安全级别,我们将在下一节中介绍。理解安全级别的工作原理需要理解文件标志,而文件标志依赖于安全级别。在此期间,当我提到安全级别时,只需点头微笑。一切都会变得清晰,请相信我。
OpenBSD 的 UFS 和 UFS2 文件系统支持以下文件标志:
> **`sappnd`**
>
> 带有系统级别只读标志的文件可以被添加,但不能被删除或以其他方式编辑。`sappnd`标志特别适用于日志文件。例如,常见的入侵者策略是删除*.history*或将它符号链接到*/dev/null*,这样管理员就无法看到发生了什么。如果账户被入侵,在用户的*.history*文件上设置`sappnd`标志可能很有趣。使用`sappnd`标志可以确保入侵者不能以这种方式掩盖他们的踪迹。只有 root 可以设置或删除`sappnd`标志,并且当系统在安全级别 1 或更高时,此标志不能被删除。
>
> **`uappnd`**
>
> 用户级别的只读标志只能由文件所有者或 root 设置。与`sappnd`类似,带有`uappnd`标志的文件可以被添加,但不能被其他方式编辑或删除。这对于个人日志和文件非常有用;它主要增加了一层额外的保护,以防止用户意外删除自己的文件。所有者或 root 可以设置或删除此标志。
>
> **`schg`**
>
> 带有系统级别不可变标志的文件不能以任何方式更改。它们不能被编辑、移动、替换或覆盖。基本上,文件系统本身阻止了所有尝试更改此文件的行为。只有 root 可以设置或删除此标志,并且当系统在安全级别 1 或更高时,此标志不能被删除。
>
> **`uchg`**
>
> 用户级别的不可变标志阻止任何人更改文件。这是一个用户级别的标志,因此 root 可以覆盖它。这个标志有助于防止文件被意外编辑或删除,但它不是确保系统安全的方法。所有者或 root 可以在任何安全级别设置或删除此标志。
>
> **`nodump`**
>
> 无备份标志告诉 `dump(8)` 不要备份文件。在不需要备份到磁带的文件上设置此标志。查看您的备份程序文档,以了解它是否尊重此标志。
### 设置、查看和移除文件标志
使用 `chflags(1)` 设置文件标志。例如,如果您非常担心有人更改您的内核文件,您可以使用系统级别的不可变标志标记 */bsd*。
chflags schg /bsd
这将阻止任何人——包括您自己——更改内核、重新配置内核或升级系统。
您也可以使用 `-R` 标志递归地更改整个目录树上的文件标志。如果您想使 */bin* 下的所有内容都不可变,您将运行以下命令:
chflags -R schg /bin
然后,您将无法再升级您的系统。
要查看文件上的标志,请使用 `ls -lo`。
$ ls -lo vitallog
-rw-r--r-- 1 root wheel - 20915343 Jul 17 16:56 vitallog
此文件上没有设置任何标志。让我们设置系统级别的只读标志。
$ chflags sappnd vitallog
chflags: vitallog: Operation not permitted
哦,对了——只有 root 用户可以设置系统级别的标志。让我们再试一次:
$ sudo chflags sappnd vitallog
Password:
$ ls -lo vitallog
-rw-r--r-- 1 mwlucas mwlucas sappnd 20915343 Jul 17 16:56 vitallog
此文件现在具有 `sappnd` 标志。系统可以添加到它,但不能对其进行其他编辑或删除。
OpenBSD 默认不标记任何文件,因此如果您想设置标志,您需要自己添加。然而,在您疯狂地添加标志之前,请注意,添加文件标志会增加系统维护的开销。如果升级系统很困难,系统管理员可能不想这样做。在 */bin* 中使所有程序不可变是否更安全,或者简化升级、更新和安全补丁的应用是否更安全?
要从文件中移除标志,请使用带有标志名称之前 `no` 的 `chflags`。例如,要取消 *vitallog* 文件上的 `sappnd` 标志,尝试以下操作:
$ sudo chflags noschg vitallog
Password:
chflags: vitallog: Operation not permitted
等一下!我正在使用 `sudo(8)` 运行,并且我有 root 级别的权限。发生了什么事?
默认情况下,OpenBSD 运行在 securelevel 1。在 securelevel 1 或更高级别运行时,您不能取消系统级别的文件标志,因此尝试取消设置将失败。您只能在 securelevel -1 或单用户模式下移除这些标志。继续阅读以了解 securelevel。
## Securelevel
`securelevel(7)` 是一个内核设置,用于限制系统可以执行的操作。随着 securelevel 的提高,内核的行为会有所不同。例如,在低 securelevel 下,上一节中讨论的文件标志可以被移除;文件可能被标记为不可变,但您可以移除标记、删除或编辑文件,并恢复标志。然而,当您提高 securelevel 时,您将无法再移除标志。系统其他部分的类似更改也会发生。整体来看,这些更改可能会挫败或阻止入侵者。
Securelevel 设置范围从 -1 到 2。尽管 OpenBSD 默认运行在 securelevel 1,但您可以根据您的环境更改此设置。
较高的 securelevel 会使得系统维护变得困难。在正常升级和管理过程中采取的许多操作也可能是入侵者用来掩盖行踪的行为。您可能需要在 securelevel 2 上运行一个高度安全、稳定的服务器,并在 -1 上运行您的实验性机器。另一方面,OpenBSD 的人不鼓励更改默认的 securelevel。在 -1 上运行您的系统可能会使您容易受到攻击,而在 2 上运行则会使得管理和维护变得复杂。您选择的 securelevel 取决于您的环境。
尽管名字如此,securelevel 并不是一个万能的安全调节器。随意提高 securelevel 除了让您和您的用户感到烦恼外,不会产生任何作用。虽然您可以在任何时候提高 securelevel,但您不能在不重启系统的情况下降低 securelevel,所以不要盲目实验。
### 设置系统 securelevel
在 */etc/rc.securelevel* 中设置引导时的 securelevel。在该文件中,您会找到如下类似的行:
securelevel=1
将 `1` 改为您想要的 securelevel。在您下一次重启时,系统进入多用户模式时会进入此 securelevel。如果您需要在引导过程提升 securelevel 之前运行一个进程,请将启动进程的命令放入此文件。
如果您想在不停机的情况下提升 securelevel,请调整 `kern.securelevel sysctl(3)` 到所需的值。
sysctl kern.securelevel=2
kern.securelevel: 1 -> 2
记住,您不能降低正在运行系统的 securelevel。如果系统管理员可以降低 securelevel,那么入侵者也可以。
### Securelevel 定义
OpenBSD 有四个 securelevel:-1、0、1 和 2。我们将逐一介绍。
#### Securelevel -1
Securelevel -1 也被称为永久不安全模式。系统并不一定是不安全的——只是没有任何 securelevel 保护措施。我只使用 securelevel -1 来移除我一开始就不应该使用的文件标志。
#### Securelevel 0
Securelevel 0 仅在系统首次启动时使用。它不提供任何特殊功能。然而,当系统达到多用户模式时,securelevel 会自动提升到 1。在 */etc/rc.securelevel* 中设置 `securelevel=0` 在功能上等同于设置 `securelevel=1`。
#### Securelevel 1
在 OpenBSD 的默认 securelevel 1 下,事情变得有趣。
securelevel 影响某些内核配置设置,称为 sysctl(在第十八章中介绍)。在引导过程的早期,OpenBSD 使用 */etc/sysctl.conf* 中的设置来设置 sysctl。当我说某个特定的 sysctl 不能更改时,请将其理解为“不改变配置和重新启动”。
Securelevel 1 实现以下限制:
+ 没有人可以写入 */dev/mem* 和 */dev/kmem* 设备。许多安全漏洞都是通过写入这些设备来实现的。
+ 所有挂载的文件系统的原始磁盘设备都是只读的。(向挂载文件系统的原始设备写入会允许你更改文件而不考虑权限。)程序应该只通过文件系统访问挂载的文件系统,所以这不会改变日常操作。
+ 系统级别的文件标志 `schg` 和 `sappnd` 不能被移除。
+ 内核模块不能加载或卸载。OpenBSD 支持内核模块,但默认内核是单片的。在运行的生产系统上加载内核模块没有合法的理由。
+ sysctl `fs.posix.setuid` 不能更改。默认情况下,`chown(1)` 会根据 POSIX 标准清除文件上的 `setuid` 和 `setgid` 位,当更改文件权限时。你可以通过将 `fs.posix.setuid` 设置为 0 来覆盖此设置。
+ sysctl `hw.allowpowerdown` 不能更改。这控制着电源按钮与系统的交互。当设置为 1 时,短暂按下电源按钮会干净地关闭系统。当设置为 0 时,电源按钮不会关闭系统。(你仍然可以通过按住电源按钮几秒钟来关闭系统,但这不是干净关闭。)并非所有平台都支持这种关闭或电源管理。
+ sysctl `net.inet.ip.sourceroute` 不能更改。源路由是一种允许数据包发送者控制数据包在网络中采取的路径的技术。它已经引起了许多安全问题,并且其使用通常是不被鼓励的。OpenBSD 默认忽略源路由。将 `net.inet.ip.sourceroute` 设置为 1 会强制 OpenBSD 注意源路由。
+ sysctl `machdep.kbdreset` 不能更改。当设置为 1 时,`machdep.kbdreset` 允许系统通过 CTRL-ALT-DELETE 清洁重启。当此 sysctl 设置为 0 时,系统会忽略 CTRL-ALT-DELETE。
+ `ddb.console` 和 `ddb.panic` sysctl 可能不能提高。提高这些 sysctl 启用某些内核调试选项。未经授权的用户如果能够提高这些 sysctl,通过调试器可以获得无限系统访问。
+ sysctl `machdep.allowaperture` 不能提高。如果你想使用 X Window 系统(在第十七章中讨论),你必须通过在引导过程中早期启用此 sysctl 来允许 X 访问内核内存的特定部分。如果你没有运行 X,没有人有合法理由需要这种访问。
+ 通用输入/输出(GPIO)控制器不能进一步配置。GPIO 控制器支持各种特殊用途的硬件。请参阅 `gpio(4)` 和 `gpioctl(8)` 以获取每个控制器的详细信息。
这些限制对日常操作的影响很小,但可能会干扰调试。如果你正在尝试找出为什么你的 GPIO 设备不工作,你可能想将你的 securelevel 设置为 -1。
#### Securelevel 2
Securelevel 2 是 OpenBSD 中的最高 securelevel,它禁用了在正常维护期间可能需要的各种功能。只有在稳定且不打算进行太多更改的机器上才应使用 securelevel 2。如果你需要更改 securelevel 2 限制的内容,你必须重新启动机器。
Securelevel 2 包含了 securelevel 1 的所有内容,以及以下内容:
+ 原始磁盘设备始终是只读的。即使它们未被使用,你也不能格式化、fdisk 或 disklabel 磁盘。
+ 系统时钟不能倒退,也不能接近溢出点。在进入多用户模式之前,请确保你的系统时间正确!
+ 你不能更改数据包过滤规则(在第二十一章和第二十二章中介绍)。数据包过滤器、网络地址转换(NAT)、流量队列等都是不可变的。
+ 内核调试器 sysctl 值(以 `ddb` 开头的)不能更改。
因此,例如,除非你了解数据包过滤规则只能通过重启来更改,否则你不想让你的防火墙在 securelevel 2 上运行。
### 你需要什么 Securelevel?
适合你环境的 securelevel 完全取决于你的情况,但绝大多数情况下,默认的 securelevel 1 是最合适的。
如果你正在调试和测试功能和工具,你可能会发现需要在开发机器上使用 securelevel -1。然而,一旦你解决了如何配置你的 GPIO 设备或系统的正确调试器设置,就使用 securelevel 1,以便在尽可能接近生产环境的情况下进行操作。
如果你有一个非常稳定的系统,你可以尝试使用 securelevel 2。然而,在我运行 OpenBSD 的这些年里,我只遇到过一次 securelevel 2 是正确的选择,还有几次 securelevel 2 带来的麻烦比它值得的要多。
### Securelevel 的弱点
Securelevel 能做什么?考虑一个场景,有人入侵了你的服务器上的一个网络应用程序,利用这个漏洞将自己提升到 shell 权限,然后使用 shell 提升到 root 权限。Securelevel 对此没有任何预防措施。
除非你大量使用了`schg`标志,入侵者可以用发送你的认证凭据到以虚假名字注册的免费电子邮件账户的系统二进制文件来替换。所以你决定四处奔波,将`schg`标志应用到像*/bin*和*/usr/lib*这样的关键目录的内容上。这样就能阻止那个家伙了!嗯,只要把每个文件都设置为不可变,包括*/etc*中的系统配置文件——你知道,你需要更改这些文件来完成你的工作——那么这就会有效。如果你留下一个文件没有保护,入侵者可以在系统启动的早期部分添加一个命令,比如`chflags -R noschg /`,那么下次你重启系统时,你所有的文件都会被解锁。你多久会彻底审计一次你的*/etc*文件?而且每次修补或升级系统时,你都需要撤销这个复杂的混乱!
这只是一个可能的方法。入侵者进入系统的途径有很多。依赖 securelevels 来保护你是不明智的。使用它们,并将它们视为你的工具箱中的一个工具,但不要认为它们是每个问题的万能药。
## 保持安全
本章讨论的工具并不是 OpenBSD 的唯一安全特性。OpenBSD 团队已经投入了大量工作来确保系统的每个部分都安全。但本章涵盖了使 OpenBSD 特殊的一些内容,并给你一个了解这些功能如何工作的想法。
通往安全的最佳路径是什么?保持你的系统更新,并安全地配置你的服务器守护进程。这很无聊,但有效。
* * *
^([26]) 我看到太多僵尸网络或脚本小子入侵几个月都没有被发现,因此我不太愿意将大多数安全问题归咎于合法用户。我同意“内部入侵”是最常见的入侵方式,但通常是因为有罪的用户无法闭嘴。
## 第十一章. TCP/IP 概述
*IPv6:*
*版本 4 的所有痛苦,*
*再加上全新的问题!*
 保护计算机很简单:将其从所有网络断开,移除所有输入和输出,并将其锁在地下掩体中。哦,等等——你想要系统*做*些什么吗?那么你可能想要将你的系统连接到互联网。
许多系统管理员对网络的基本知识有模糊的了解,但要成为一个真正合格的系统管理员,你需要真正理解一切是如何相互关联的。你不需要知道何时使用快速生成树,如何选择 BGP 和 OSPF 之间的区别,甚至这些缩写代表什么。但你必须知道 IP 地址是什么,子网掩码是如何工作的,端口号与协议号有何不同,以及为什么不能使用`telnet(1)`来测试 UDP 连接性。没有这些基本知识,你会感到迷茫。阅读本章并理解它,你将更容易说服你的网络管理员给你你需要的东西。
尽管本章提供了 TCP/IP 的概述,但它并没有涵盖协议中存在的无数细节、警告、烦恼、小缺点和明显的冒犯。如果你发现自己需要深入研究 TCP/IP 的细微之处,可以挑选一本关于这个主题的大厚书。查尔斯·M·科齐罗克的《TCP/IP 指南》(No Starch Press,2005 年)是一个很好的起点。
本章涵盖了 TCP/IP 版本 4(过去 30 多年广泛使用的互联网协议)和协议的新版本,TCP/IP 版本 6。尽管版本号不同,但这两个协议的相似之处更多。
我们将从网络层开始,然后深入探讨协议是如何工作的。
## 网络层
网络协议被分为几个层次。每一层处理特定的任务,并且只与直接位于其上方和下方的层交互。一开始,你可能会嘲笑这种层模型简化了网络过程的想法,但事实确实如此。现在要记住的重要事情是,每一层只与直接位于其上方的层和直接位于其下方的层(理论上是这样)进行通信。
经典的开放式系统互联(OSI)网络协议栈将网络表示为七层。这是一个详尽的完整模型,涵盖了几乎任何使用任何网络协议和任何应用程序的情况。因为互联网是一种非常特定的网络类型,而且这本书不是关于网络或网络应用的通用书籍,所以我将把 TCP/IP 的讨论限制在网络的四个特定层次:物理层、数据链路层、网络层和传输层。不用担心——这四层涵盖了互联网和(几乎)所有企业网络。
### 物理层
不论是铜线还是光纤电缆,甚至是无线电波,物理线缆都是网络的一部分。如果没有某种物理媒体来运行,网络就无法工作。从插入你桌面的 CAT5 电缆到连接你到亚洲的光纤电缆,所有这些都属于物理层。如果可以被绊倒、被挖掘或受到干扰,那么它就是物理层的一部分。为了简化起见,我将物理层称为*线缆*,尽管它可以有无数的形式。
这是最容易理解的一层。如果你的线缆符合物理协议的要求,那么你的网络就可以正常工作。如果不满足,你的网络将无法工作。互联网路由器的一项功能是将一种物理层连接到另一种物理层——例如,将本地以太网转换为 OC3 光纤连接。
物理层本身没有决策能力;所有在其上运行的内容都由数据链路层决定。
### 数据链路层
数据链路层是在物理线路上运行的协议。它将信息转换成实际信号,这些信号通过物理层发送,使用适合该物理媒体的适当编码,如下所示:
+ 以太网和交换式多兆比特数据服务(SMDS)都使用媒体访问控制(MAC)地址和地址解析协议(ARP)。
+ 以太网上的 IPv6 使用邻居发现(ND)。
+ 拨号和广域网(WANs)使用点对点协议(PPP)或高级数据链路控制(HDLC)。
OpenBSD 支持其他常见的链路层协议,如以太网上的 PPP(PPPoE)。如果你有特殊的网络需求,请检查 OpenBSD 网站、邮件列表或 man 页面,看看这些需求是否得到支持。
一些数据链路层已经在许多不同的物理层上实现。例如,以太网已经在双绞线、同轴电缆、CAT3、CAT5、CAT6、CAT7、光纤和无线电波上实现。为了实现真正的设备独立性,我们已经看到 TCP/IP 与一个生物传输层一起实现:信鸽。^([27)]
通过对设备驱动程序进行少量修改,数据链路层可以处理任何类型的物理层。这是层简化网络的一种方式。
第十二章详细讨论了以太网,因为它是 OpenBSD 系统中最常见的网络类型。一旦你了解了以太网的工作原理,你将不会在需要时添加新的数据链路协议时遇到困难。
数据链路层与物理层和网络层交换信息。
### 网络层
网络层是映射网络节点之间连接的部分,回答诸如“其他主机在哪里?”和“我能从这里到达那里吗?”等问题。这种逻辑协议为在网络中运行的程序提供了一个一致的接口,无论物理和链路层看起来如何。
互联网上使用的网络层协议是互联网协议,或 IP。版本 4(IPv4)和版本 6(IPv6)为每个主机提供一个或多个唯一的 *IP 地址*,以便网络上的任何其他主机都可以找到它。好吧,IPv4 网络地址转换破坏了“唯一地址”规则,但你的网络仍然在某处有一个唯一的 IP 地址。
网络层与其下方的链路层和上方的传输层进行通信。
### 传输层
传输层是实际数据流动的地方。最常用的三种传输层协议是互联网控制消息协议(ICMP)、传输控制协议(TCP)和用户数据报协议(UDP)。
ICMP 在具有 IP 地址的主机之间传递基本的连通性消息。如果 IP 提供道路和地址,ICMP 则提供交通灯和高速公路出口标志。大多数时候,ICMP 在后台默默运行。
UDP 和 TCP 是携带主机之间实际数据的协议,它们如此普遍,以至于整个互联网协议套件通常被称为 TCP/IP。UDP 是一个基本的传输协议,提供在网络上传输数据所需的最少服务。它的简约性意味着,如果你想精确地定制应用程序中数据的流动方式,你可以使用有效的 UDP 构建它。TCP 提供了更复杂的功能,如完整性检查和拥塞控制,但许多设置都是硬编码的。
除了这三个之外,还有许多其他协议在传输层运行。文件 */etc/protocols* 包含了一个相当全面的传输协议列表,这些协议建立在 IP 之上。虽然它列出了你实际上在野外可能遇到的许多更多协议,但它不包括 IPX/SPX 或数字设备公司的 DECnet 这样的非 IP 协议。
例如,让我们看看 */etc/protocols* 文件中的第一个条目:
ip 0 IP HOPOPT # internet protocol, pseudo protocol number
每个 */etc/protocols* 条目有三个关键字段:官方名称、协议号和任何别名。IP 协议,协议号 0,被称为 IP,(*非常偶尔*)也称为 HOPOPT。每个协议还有一个注释,提供一些上下文。尽管 */etc/protocols* 中的一些协议已经消失,但一些古老的设备可能仍在使用它们。
注意,当在 IPv4 与 IPv6 上运行时,ICMP、TCP 和 UDP 略有不同。每个协议在 IP 数据包头部都有明确定义的字段,为校验和、目标地址等留出特定的位数。你不能在不兼容的网络协议上运行传输协议——TCPv6 在 IPv4 上根本无法工作。
传输层与下方的网络层和上方的应用层进行通信。
### 应用程序
应用程序无疑是网络的一部分。应用程序请求网络连接,通过网络发送数据,从网络接收数据,并处理这些数据。网页浏览器、电子邮件客户端、JavaServer Pages (JSP) 服务器等都是网络感知应用程序的例子。应用程序只需要与传输协议和用户进行通信。OSI 网络模型的顶层三层都在应用程序内部。
用户层的问题超出了本书的范围,但我发现许多这些问题可以通过正确应用大型链锯来解决。
## 网络请求的生命周期
那么在现实世界中,所有这些层是如何结合在一起的?让我们看看一个假设的网络请求,并逐步了解数据是如何穿越各层和网络。
### 注意
这部分讨论的一些内容涉及本章后面将要讨论的主题,因此您在完成本章后可能需要重新阅读本节。纯粹主义者会注意到我跳过了许多过程的部分,但我试图传达 TCP/IP 在实际中是如何工作的基本原理,而不是详细描述真实网络事务的每一个痛苦细节。
假设一个连接到您网络的用户想查看一个非常重要的与工作相关的网站,例如 Scott Meyer 的 Basic Instructions (*[`www.basicinstructions.net/`](http://www.basicinstructions.net/)*)。用户打开浏览器,输入 URL,然后按回车键。浏览器应用程序将用户的请求转换成正确的格式,并请求传输层与特定 IP 地址的 80 端口建立 TCP 连接。
您计算机内的传输层会检查浏览器的请求,并为它分配适当的资源。请求被分解成可消化的数据块,称为*段*,然后传递给网络层。
网络层并不关心请求的内容;它只关心数据去向何方。网络层将 TCP 数据附加适当的寻址信息。由此产生的数据块称为*数据包*。网络层检查数据包的目的地,选择离该目的地最近的接口,并将数据包下放到数据链路层。
数据链路层并不关心数据包的内容,当然也不关心 IP 地址或路由。它被赋予了一块零和一的数据,其任务是将这些零和一传输到另一个网络节点。数据链路层向数据包添加适当的头部和/或尾部信息,创建适合物理层的*帧*。帧的头部和尾部包含物理层的寻址信息。在大多数网络中,数据链路层为本地以太网准备帧,然后将帧传递给物理层进行传输。
物理层完全没有智能(想想信鸽)。数据链路层将一个帧交给物理层,物理层将这个帧传输到另一个物理设备。对于 Web 浏览客户端来说,这通常是本地以太网的默认路由器。物理层不关心上层协议。它的唯一任务是确保帧无错误地到达目的地。
当客户端计算机的路由器接收到帧时,它将其发送到数据链路层。数据链路层移除帧信息,并将得到的包交给网络层。路由器的网络层检查包,查看其路由表,并决定通过哪个接口发送。这可能是一个以太网接口、T1、DS3、OC3 或路由器用于上行连接的任何其他接口。一旦路由器选择了一个接口,它就将包交给该接口的数据链路层。
本地路由器的上行连接可能要通过一系列路由器。每个路由器根据其路由表决定将请求发送到哪里。请求在传输过程中可能穿越各种数据链路层。多亏了分层和抽象,你或你的计算机不需要了解任何关于它们的信息。
当请求到达目的地时,交易另一端的计算机接受该帧并将其发送回整个协议栈的上层。该帧被分解成数据包,数据包再分解成段,然后重新组装成数据流。随后,数据流被交给应用程序(在这种情况下,是一个 Web 服务器)。应用程序处理请求并返回一个答案,该答案沿着协议栈向下传递并通过网络传输,在必要时在各个数据链路层上下弹跳。
这个例子说明了层模型的重要性:每一层只知道它绝对必须了解的上下层的信息,这使得在必要时可以更换层。当创建一个新的数据链路协议时,其他层不需要改变。网络层只需将包交给数据链路层,并让数据链路层完成其工作。当你安装一个新的网络卡时,你只需要一个与数据链路层和物理层接口的驱动程序;你不需要更改网络栈中的任何更高层。
## 网络栈
网络栈是允许主机与网络通信的软件。主机可以运行 IPv4-only 网络栈、IPv6-only 网络栈或双栈配置。
你已经熟悉了 IPv4-only 栈——这是过去 30 年中大多数主机运行的方式。IPv4-only 栈只能在 IPv4 上通信。今天,IPv4-only 栈可以让你访问整个互联网,但有少数故意排除的例外。这将在几年后不再成立。
同样,仅 IPv6 栈可以与仅 IPv6 主机通信。由于大多数互联网站点尚未支持 IPv6,目前运行仅 IPv6 栈并不实用。然而,这是一个测试你的 IPv6 基础设施和连接性的绝佳方式。
目前最常见的配置是双栈设置。客户端主机尝试同时使用 IPv4 和 IPv6,优先选择其中一个。我建议配置双栈主机,优先选择连接性更好的栈。(如果你通过隧道获得 IPv6 连接,它不如你的 IPv4 连接快。)如果你有相等的 IPv4 和 IPv6 连接,使用你喜欢的任意一个。IPv6 工作得足够好,以至于我经常在分析我的流量之前都没有意识到我在使用它。
在 OpenBSD 上启用 IPv6 不需要做任何特殊的事情——一个 IPv6 地址,一个默认路由器和一个 DNS 服务器,然后就可以使用了。
## IPv4 地址和子网
*IP 地址* 是分配给特定网络节点的唯一 32 位数字。一些 IP 地址更永久一些,例如分配给关键服务器;其他则根据需要更改,例如桌面客户端使用的地址。共享网络上的单个机器使用分配给该网络的地址范围内的 IP 地址。
与将 32 位地址表示为单个数字相比,IP 地址被分为四个 8 位数字,通常以十进制表示。虽然 192.0.2.1 和 11000000.00000000.00000010.00000001 代表相同的地址,但第一种选项更容易让我们这些脆弱的小脑理解。
互联网服务提供商(ISP)以块的形式发行 IP 地址。这些块是它们可以给你提供的最小分配——比如 16 或 32 个地址。如果你的系统在服务器农场,你可能只能从 256 个地址的块中获得几个 IP 地址。
*子网掩码* 表示分配给本地网络的 IP 地址块的大小。你的 IP 块的大小决定了你的子网掩码——或者,你的子网掩码决定了网络有多少个 IP 地址。
ISP 通过前缀长度发行 IP 地址,通常称为 *斜线*。你会看到 IP 地址块以如下形式描述:192.0.2.128/26。每个与网络工作过的人都已经见过子网掩码 255.255.255.0,大多数人知道它与 256 个 IP 地址的块相关联。这个子网掩码也称为 /24。斜线后面的数字是子网掩码中固定位的数量。记住,IPv4 地址是一个 32 位数字;在一个 /24 网络上,24 个这些位永远不会改变。
这不是一本二进制数学教科书,所以我不打算就转换进行测验,但请将 IP 地址视为一串 32 个二进制位。在你的网络上,你可以更改最右边的位,但不能更改最左边的位。但左右分界的线在哪里呢?
子网掩码传统上是在 8 位边界上分割的,但并没有硬性规定它们必须这样。/25 网络有 25 个固定位——比过去所说的 C 类网络多一个固定位——剩下 7 位可以操作。子网掩码的固定位设置为 1,而你的网络位设置为 0,如下面的/25 子网掩码示例所示:
11111111.11111111.11111111.10000000
前三个块设置为二进制的 11111111,即十进制的 255。最后一个块设置为 1000000,即 128。将这些数字组合起来,你得到的子网掩码是 255.255.255.128。
如果你将子网掩码转换为二进制,它们就很容易理解。虽然你不必每天都处理这个问题,但如果你不理解其背后的概念,十进制转换看起来就像是一堆乱码。稍微练习一下,你就会识别出某些十进制字符串是合法的子网掩码。
所以现在你知道了子网掩码是如何工作的,这一切在现实世界中究竟意味着什么呢?
IP 地址以 2 的倍数发放。如果你有 4 位可以操作,你就有 16 个地址(2⁴=16)。如果你有 8 位可以操作,你就有 256 个地址(2⁸=256)。如果有人说你正好有 17 个 IP 地址,那么你要么与其他人共享网络,要么他们错了。
通常可以看到主机 IP 地址后面附有子网掩码,例如 192.0.2.130/26。这为你提供了将主机连接到本地网络所需的一切。(查找默认网关是另一个问题,但通常它是该块中的顶部或底部地址。)
### 计算十进制 IPv4 子网掩码
从二进制到十进制再到二进制的转换容易出错且有些烦人。以下是如何在十进制领域计算你的子网掩码的方法。
查找你的网络上有多少 IP 地址。这将是 2 的倍数,几乎肯定小于 256。从 256 中减去你拥有的 IP 地址数量。这是你的子网掩码的最后一位数。然而,你仍然需要识别合法的网络大小。如果你的 IP 地址是 192.0.2.251/26,你需要知道/26 是 26 个固定位,或 64 个 IP 地址。你的子网掩码是 255.255.255.192(256–64=192)。
我还应该提到,子网掩码偶尔也会以十六进制形式出现。
在你带着这本书来我家反复殴打我之前,表 11-1 显示了/24 和更小网络的子网掩码、IP 信息和相关信息。
表 11-1. 表 11-1:IPv4 子网掩码和 IP 地址转换
| 前缀 | 二进制掩码结束 | 十进制掩码 | 十六进制掩码 | 可用 IP 地址 |
| --- | --- | --- | --- | --- |
| /24 | 00000000 | 255.255.255.0 | 0xffffff00 | 256 |
| /25 | 10000000 | 255.255.255.128 | 0xffffff80 | 128 |
| /26 | 11000000 | 255.255.255.192 | 0xffffffc0 | 64 |
| /27 | 11100000 | 255.255.255.224 | 0xffffffe0 | 32 |
| /28 | 11110000 | 255.255.255.240 | 0xfffffff0 | 16 |
| /29 | 11111000 | 255.255.255.248 | 0xfffffff8 | 8 |
| /30 | 11111100 | 255.255.255.252 | 0xfffffffc | 4 |
| /31 | 11111110 | 255.255.255.254 | 0xfffffffe | 2 |
当您不想做数学题时,可以参考表 11-1 或安装`ipcalc`包进行快速子网掩码计算。不要说我从未同情过我的读者.^([28])
### 查看 IPv4 地址
使用`ifconfig(8)`显示 IP 地址。如果您不带任何参数运行`ifconfig`,它将显示机器上的所有接口。
$ ifconfig fxp0
…
inet 192.0.2.226 netmask 0xfffffff0 broadcast 192.0.2.239
inet 192.0.2.231 netmask 0xffffffff
…
以`inet`开头的行是 IPv6 地址。此接口的主要 IPv4 地址为 192.0.2.226,次要的或*别名*地址为 192.0.2.231。您还可以看到这些地址的子网掩码和子网的广播地址。
### 不可用的 IPv4 地址
每个 IPv4 地址块都为网络预留了第一个和最后一个 IP 地址用于使用:
+ 块中的第一个 IP 地址是*网络地址*,用于分隔网络(以及在原始 BSD 系统中,广播地址)。在/24 网络中,这将是一个以.0 结尾的地址。
+ 块中的最后一个 IP 地址是*广播地址*。在/24 网络中,广播地址以.255 结尾。
### 注意
根据 IP 规范,网络上的每台机器都应该对广播地址的请求做出响应。不幸的是,在 20 世纪 90 年代末,这一特性被用作攻击技术:您只需在任何给定网络上 ping 广播地址,就会得到当前所有正在使用的 IP 地址列表。因此,现在大多数操作系统和网络设备默认禁用了这一功能。
在网络中,您不能将第一个或最后一个 IP 地址分配给设备而不冒网络问题的风险。一些系统会优雅地失败,其他系统会痛苦地失败,而极少数系统能够正常工作。尽管 OpenBSD 不会反对您使用顶部和底部的网络地址,但第一次插入通用打印机或其他嵌入式设备时,请做好混乱的准备。只需一个不灵活的设备就能毁掉您一整天。
### 特殊 IPv4 地址
许多 IPv4 地址块被预留用于特定目的。尽管您不需要了解所有这些,但您会经常看到两组。要获取为特殊目的预留的 IPv4 子网的完整列表,请阅读 RFC 5735 和 RFC 6598。
#### 本地主机
地址范围 127.0.0.1/8 被预留用于*本地主机*,即机器自己的地址。每个类 Unix 系统——以及大多数其他操作系统——都将 127.0.0.1/8 附加到回环接口。所有设备都知道 localhost 地址是特定机器的本地地址。发往或来自 127.0.0.0/8 的数据包永远不应该跨越网络;同样,仅绑定到 127.0.0.1 的守护进程只能在本地机器上访问。
#### 私有网络
互联网标准 RFC 1918 为私有网络和位于网络地址转换(NAT)设备之后设置了三个网络:10.0.0.0/8、172.16.0.0/12 和 192.168.0.0/16。虽然公共 IP 地址必须由 ISP 颁发,但只要这些主机不直接暴露在公共互联网上,任何人都可以使用这三个块内的地址。如果你有一个主机无法访问互联网的网络,或者如果你通过代理服务器或 NAT 提供互联网接入,你可以使用 RFC 1918 网络。
### IPv4 地址分配的陷阱
众所周知,网络中的每台计算机都会为其每个网络接口分配一个单独的 IP 地址。一台计算机,一块网卡,一个 IP 地址——简单,对吧?
并非总是如此。一些专用接口(例如那些用于数据包嗅探的接口)在没有 IP 地址的情况下也能按预期工作,许多操作系统也允许你通过一个称为*别名*的过程为单个网络接口分配多个 IP 地址。你还可以将多个物理网卡绑定成一个单一的网络接口,为计算机提供一个大的虚拟接口。虽然你可能不会每天处理这些配置,但在故障排除时请记住这些。
## IPv6 地址和子网
IPv4 存在一个基本问题:它只提供了 42.9 亿个地址,这远远不够。没有子网划分,这意味着每个人不到一个地址。最终,每个人至少会有一台 IP 设备。
虽然 IPv4 地址尚未耗尽,但它们正变得越来越稀缺。早期过于慷慨的分配,以及为特殊目的预留的大量地址空间,加速了地址的耗尽。世界正缓慢地走向 IPv4 的替代品:IPv6。
世界上的某些地区已经广泛使用 IPv6。即使你的网络今天不使用 IPv6,总有一天你会需要它——可能毫无预兆。现在做好准备,否则你可能会发现你在一周前就需要它了。
### IPv6 基础知识
与 IPv4 一样,IPv6 是一个网络层协议。IPv4 使用 32 位地址,通常表示为四组从 0 到 255 的十进制数字(例如,192.0.2.13)。IPv6 使用 128 位地址,表示为六组由冒号分隔的四位十六进制字符(例如,2001:db8:0:bad:c0de:cafe)。128 位的地址空间为地球上的每个原子提供了超过 10 个 IPv6 地址。TCP、UDP、ICMP 和其他协议在其之上运行。IPv6 有自己的第二层协议,即邻居发现协议,它取代了如以太网的 ARP 协议。
好消息是,你不需要重新学习网络的基础知识。主机仍然需要一个 IP 地址和一个默认网关,路由器仍然使用路由表,你几乎可以——*几乎可以*——用 IPv6 地址替换 IPv4 地址并观察一切正常工作。Web 服务器不在乎它绑定到 192.0.2.13 上的 80 端口还是绑定到 2001:db8:a12a:bad:c0de:café 上的 80 端口。服务器只是接受发送给它的请求并相应地做出响应。话虽如此,软件确实需要稍作改变,因为我们的 Web 服务器必须能够记录来自 IPv4 和 IPv6 地址的连接。这些变化具有深远的影响,我们将在接下来的十年里解决边缘情况。但总的来说,一旦你了解了 IPv6 的新规则,你的所有网络知识都适用。
### 理解 IPv6 地址
正如所述,IPv6 地址是 128 位,表示为六个由冒号分隔的四位十六进制字符组。与十进制 IPv4 地址一样,你不需要在每个组中显示前导零。地址 2001:db8:0:bad:c0de:cafe 也可以写成 2001:db8:0000:0bad:c0de:cafe,但就像我们不会写 192.000.002.013 一样,我们在 IPv6 地址中省略了前导零。29]
IPv6 地址通常包含一长串零。这与子网划分有关,我将在本节后面描述。截至本文撰写时,Sprint 网站的 IPv6 地址是 2600:0:0:0:0:aaaa。当连续的组只包含零时,如这个地址,它们将被两个冒号(`::`)替换。这个 IP 地址通常显示为 2600::aaaa。但是,每个地址只能这样做一次。例如,你不能有地址 2600::1::1,因为这会导致歧义。2600::1::1 代表 2600:0:0:1:0:1 还是代表 2600:0:1:0:0:1?我不知道,你的服务器也不知道。
你可能见过在 IPv4 地址中添加端口号,例如 192.0.2.13:80。使用冒号将端口号粘接到 IPv6 地址上可能会造成混淆。IPv6 地址 2001:db8::bad:c0de:cafe:80 并没有歧义,但如果你快速阅读,可能会错过双冒号并认为这是一个以 80 结尾的 IP 地址。如果你要向 IPv6 地址添加端口号,请将地址用方括号括起来,例如 [2001:db8::bad:c0de:cafe]:80。
### 查看 IPv6 地址
使用 `ifconfig(8)` 查看你机器上分配的所有 IPv6 地址。在这里,我给 `ifconfig` 赋予了我的网卡名称,`fxp0`。
$ ifconfig fxp0
…
inet 192.0.2.13 netmask 0xfffffff0 broadcast 198.0.2.255
inet6 fe80::bad:c0de:cafe%fxp0 prefixlen 64 scopeid 0x2
inet6 2001:db8::bad:c0de:cafe prefixlen 64 autoconf pltime 604399 vltime 2591599
…
以 `inet6` 开头的行是我的 IPv6 地址。此接口已分配了两个 IPv6 地址:`fe80::bad:c0de:cafe%fxp0` 和 `2001:db8::bad:c0de:cafe`。(等等……那个 `%fxp0` 是从哪里来的?你将在链路本地地址中找到答案。现在,只需点头微笑,继续阅读。)
### IPv6 子网
与 IPv4 不同,IPv4 可以在任何位进行子网划分,IPv6 通常在冒号边界进行子网划分。冒号每 16 位出现一次,所以自然的 IPv6 子网是/16、/32、/48 和/64。尽管 IPv6 标准建议使用/64 作为可能的最小网络,但许多运营商为了特殊目的使用/80、/96 和/112 网络。 (我也见过一些人使用不在 16 位边界划分的子网。我不会涉及这些,但当你遇到/51 子网时,不要让你的大脑崩溃。) IPv6 子网总是以斜杠表示,也称为*前缀长度*,所以你不会看到类似于 IPv4 中的 ffff:ffff:ffff:ffff::的子网掩码。
互联网服务提供商通常被分配/32 或/48 子网,并预期向最终用户网络(如典型的家庭网络)分配/64 网络。如果互联网服务提供商向用户分配/64 子网,最终用户网络将提供 2⁶⁴ 个 IP 地址,即 18,446,744,073,709,551,616 个 IP 地址。(这足以满足任何数量的电视、电话、冰箱、水龙头、吸尘器和网络化的玉米卷饼。)
当你在 16 位边界进行子网划分时,每个网络都有 65,536 个子网,其大小比下一个更小的子网小。一个/32 包含 65,536 个/48 网络,一个/48 包含 65,536 个/64 网络。
### 特殊 IPv6 地址
与其前身类似,IPv6 为特殊目的预留了几个地址块。你不需要记住所有这些预留地址,但其中一些会在日常使用中出现。
#### 本地主机
IPv6 的本地地址::1/128 在 IPv4 中工作方式与 127.0.0.1 类似:它始终指向本地机器。在 OpenBSD 中,::1/128 始终分配给`lo0`接口。
#### 链路本地地址
以 fe8*x*:开头的地址(其中*x*是可变的)仅限于其接口。每个链路都有这样的*链路本地*地址,这些地址仅在特定的本地网络上有效。即使 IPv6 网络没有路由器,本地直接连接网络上的主机也可以使用这些本地地址找到彼此并进行通信。这些网络总是/64 子网。你会在其他接口和与你的网络完全断开的网络上看到相同的 IPv6 子网。这是可以的。这些地址仅限于链路。例如,这是一个 OpenBSD 机器上的链路本地地址:
inet6 fe80::bad:c0de:cafe%fxp0 prefixlen 64 scopeid 0x2
此接口的链路本地地址是`fe80::bad:code:cafe`。尾部的`%fxp0`表示此地址仅限于接口`fxp0`,并且不能在机器上的任何其他接口上使用。如果你的机器有一个`fxp1`接口,并且该网络上的主机尝试访问地址 fe80::bad:code:cafe,这台机器将不会响应。这个特定的地址仅对连接到接口`fxp0`的网络有效。
你可能会注意到,链路本地地址与该网络上的公共 IPv6 地址有一个共同的区域。这是因为自动配置的 IPv6 地址通常是从接口的物理地址计算出来的;无论这个自动配置的地址是公共地址还是链路本地地址,这都不重要。
### 分配 IPv6 地址
IPv6 客户端通常可以通过*路由器发现*使用自动配置,这是一个 IPv6 协议,其中路由器向客户端宣布其在网络上的存在和合法地址。不幸的是,IPv6 自动配置不支持常见的动态主机配置协议(DHCP)选项,例如分配域名系统(DNS)服务器,更不用说用于无盘配置的选项了。如果你已经配置了 DNS 服务器——即使是在双栈主机上可访问的 IPv4 服务器——自动配置也能正常工作。如果你运行一个仅 IPv6 的网络,你必须要么设置一个 IPv6 DHCP 服务器向客户端提供 DNS 服务器信息,要么手动配置 DNS 服务器。
服务器不应使用 IPv6 自动配置。服务器通常需要一个静态 IP 地址,即使在 IPv6 中也是如此。同样,路由器也不能使用自动配置。如果一个主机可以转发数据包,它需要一个静态的 IPv6 地址。
你可以通过使用别名将多个 IPv6 地址分配给单个接口,就像在 IPv4 中一样。
在 IPv6 中,/64 网络上的客户端可以使用自动配置。
IPv6 自动配置类似于简化的 DHCP 服务。路由器广播子网和网关信息,主机配置自己使用它。小于/64 的网络上的主机必须手动配置。
## 补救性 TCP/IP
现在你已经对 IP 系统的工作原理有了简化的概述,让我们深入探讨一个真实的网络协议。互联网上占主导地位的传输协议是互联网协议上的传输控制协议,或 TCP/IP。尽管 TCP 是一种传输协议,IP 是一种网络协议,但这两个协议紧密交织在一起,通常被统称为一个实体。
我们将从 ICMP 开始,然后继续到 UDP 和 TCP。
### ICMP
ICMP 用于在网络中传输路由和可用性消息。工具如`ping(8)`和`traceroute(8)`使用 ICMP。ICMP 包括各种不同的协议和工具。
虽然有些人声称出于安全目的需要阻止 ICMP,但这些人并不理解 ICMP 与更为人熟知的传输协议 TCP 和 UDP 一样多样化。适当的 IPv4 网络性能需要大量的 ICMPv4。如果你必须出于安全原因阻止 ICMPv4,请选择性地进行。例如,阻止源抑制消息会破坏路径最大传输单元(MTU)发现,这会让你直接进入一个痛苦的世界。如果你不理解最后一句话,请不要阻止 ICMPv4。
没有 ICMPv6,IPv6 就无法工作,因为 IPv6 不支持数据包分片,所以永远不要阻止 ICMPv6。如果你不知道数据包分片是什么,就相信我吧.^([30])
### UDP
UDP 是在 IP 上运行的最为基础的传输协议。它不提供错误处理,最小化完整性验证,并且对数据丢失没有任何防御措施。传输协议认为 UDP 的每个数据包都是完全自包含的;在协议层没有数据一致性检查。尽管存在这些缺点,UDP 对于某些类型的数据传输来说仍然是一个不错的选择,许多重要的互联网服务都依赖于它。
### 注意
本讨论涵盖了 UDPv4 和 UDPv6。尽管每个都只运行在相应的网络协议上,但在其他方面它们的行为是相同的。
UDP 也是一个数据报协议,这意味着每次网络传输都是完整且自包含的,并且作为一个单一的整体接收。尽管应用程序可能不会将单个 UDP 数据包视为一个完整请求,但网络会。
当一个主机通过 UDP 传输数据时,它无法知道数据是否到达了目的地。接收 UDP 数据的程序只是监听网络并接受任何到达的数据。当程序通过 UDP 接收数据时,它无法验证数据的来源。尽管每个 UDP 数据包都包括一个源地址,但这个地址很容易被伪造。每个 UDP 数据包都包括一个数据包校验和,但没有对整个数据流的完整性检查。这就是为什么 UDP 被称为*无连接的*或*无状态的*。
没有完整性检查,没有防止数据丢失的防御措施,伪造数据包的可能性——所有这些都听起来非常不可靠。那么为什么还要使用 UDP 呢?
基于 UDP 的应用程序通常有自己的错误纠正方法,或者与更可靠协议(如 TCP)不兼容。例如,简单的客户端 DNS 查询必须在几秒钟内超时,否则用户会无法控制地抱怨。TCP 连接只有在两分钟后才会超时。DNS 需要快速失败,并且每个事务只有一个数据包,这使得 UDP 比 TCP 更适合简单的 DNS 查询。实时流媒体服务,如视频会议应用程序,也使用 UDP。(毕竟,如果在视频会议中丢失了一些像素,你不想一分钟后还看到这些像素。)大多数其他基于 UDP 的应用程序出于类似的原因使用 UDP。
由于 UDP 协议本身在连接到端口时不会返回任何内容,因此没有可靠的方法来远程测试 UDP 端口是否可达(尽管像`nmap`这样的工具试图这样做)。
如果你需要一个在网络层做出响应的协议,请查看 TCP。
### TCP
TCP 包括一些巧妙的功能,如错误纠正和恢复。接收方必须确认它收到的每个数据包;否则,发送方会重新传输任何未确认的数据包。与 UDP 不同,使用 TCP 的应用程序可以期待可靠的数据传输。这使得 TCP 成为一个*连接的*或*有状态的*协议。
### 注意
本讨论涵盖了 TCPv6 和 TCPv4。尽管它们因为其底层传输协议的不同而有所区别,但它们的行为方式是相同的。
TCP 也是一个*流协议*,这意味着单个请求可以被分割成几个网络数据包。虽然发送者可能会连续发送几个数据块,但这些数据可能会乱序到达或碎片化。接收者必须跟踪这些数据块并正确组装它们以完成网络事务。
为了主机之间交换 TCP 数据,它们必须为数据流建立一个通道。一个主机请求建立连接,另一个主机响应请求,然后第一个主机开始传输。这个过程被称为*三次握手*。同样,一旦传输完成,系统必须进行一定的工作来断开连接。
要测试 TCP 端口是否开放,你可以使用`telnet(1)`或`nc(1)`连接到该端口。在这里,我看看我是否可以连接到`caddis`主机的端口 22。
$ telnet caddis 22
Trying 192.0.2.35…
Connected to caddis.
Escape character is '^]'.
SSH-2.0-OpenSSH_6.0
1 ^]
2 telnet> c
Connection closed.
我连接到远程端口并查看端口显示的信息,使用 telnet 的转义字符**`^]`**(CTRL-])断开连接**1**,并输入**`c`** **2**关闭 telnet。
TCP 通常被适用于其相当通用的超时和传输特性的应用程序使用,例如电子邮件程序、FTP 客户端和网页浏览器。
### 协议如何协同工作
你可以将网络堆栈比作在假日晚餐时与家人一起坐下。数据链路层(在以太网的情况下是 ARP)让你看到桌子上的每个人。IP 为桌子上的每个人提供他们自己的独特椅子(除了使用钢琴凳 NAT 的双胞胎)。ICMP 提供基本、低层信息,例如“到达烤红薯最快的方式是让迈克叔叔递给你”?^([31]) 或者“丽兹阿姨抬不起火腿盘。”TCP 是你递给别人黄油,而另一个人必须说“谢谢”你才能放手的地方。UDP 就像向露卡斯奶奶扔一个面包卷;她可能接住它,或者它可能弹回她的额头。
### 传输协议端口
传输协议端口允许一个服务器通过单个传输协议提供多种不同的服务,在机器之间多路复用连接。当网络服务器启动时,它会附加到,或*绑定*到一个或多个逻辑端口。逻辑端口只是一个从 0 到 65536 的任意数字,尽管没有东西使用端口 0。例如,互联网邮件服务器通常绑定到端口 25。
每个到达系统的 TCP 或 UDP 数据包都携带一个字段,包含其期望的目标端口号。如果传入的数据包请求端口 25,它将连接到运行在该端口的邮件服务器。这意味着其他程序可以运行在其他端口上,客户端可以与这些不同的端口通信,除了你之外没有人会感到困惑。
注意,端口分配并不是某种物理常数,而是相互协商的结果。除了每个人都同意电子邮件服务应该运行在端口 25 的原因之外,没有理由让电子邮件服务运行在端口 25。如果有人试图给你发送电子邮件,他们的邮件服务器会自动连接到你的服务器上的端口 25。如果你在端口 80 上运行电子邮件并在端口 25 上有一个 Web 服务器,你将永远收不到你的电子邮件,你的 Web 服务器也不会获得太多流量。
文件 */etc/services* 包含了一组端口号及其相关服务。该文件具有非常简单的五列格式,如下两行示例所示:
www 80/tcp http # WorldWideWeb
www 80/udp # HyperText Transfer Protocol
第一列是分配给此端口的服务的名称。此条目是为服务 `www`。端口 80 被分配给 `www`,既支持 TCP 也支持 UDP。然后是分配给此端口的任何其他名称。端口 80 也被称为 `http`。最后,有一个注释,提供了更多关于服务的详细信息。
网络上使用的 HTTP 协议运行在 TCP 上,那么为什么 UDP 端口 80 也被保留用于 HTTP 呢?答案相当简单:计算机人士很容易感到困惑。让两个服务共享相同的端口号但运行在不同的协议上会让人困惑——例如,`syslog` 服务通过 UDP 在端口 514 上运行,而 `lpr` 打印机协议通过 TCP 在端口 514 上运行.^([32])
一些服务器程序在启动时读取 */etc/services* 来了解应该绑定到哪个端口,而许多客户端程序也读取 */etc/services* 来了解它们应该尝试连接到哪个端口。如果你在非标准端口上运行服务器,你可能需要编辑此文件,以便这些程序在需要的地方附加。
与所有标准一样,有时你会想要打破规则。SSH 守护进程 `sshd` 通常绑定到端口 22/TCP,但我已经将它运行在端口 23 (`telnet`)、80 (`www`)、443 (`https`) 等其他端口上,以规避简单的数据包过滤防火墙。你会发现你自己的理由去打破标准。这是可以的,只要你理解你在做什么以及它如何影响他人。
### 保留端口
在 TCP 和 UDP 中低于 1024 的端口只能由 root 用户打开。这些端口分配(主要是)给核心互联网基础设施协议,如 DNS、SSH、HTTP、LDAP 等等——只有网络中少数选定的主机应该提供的服务。只有具有 root 级别权限的程序才能绑定到保留端口。例如,如果系统策略允许,用户可以在高编号端口上运行游戏服务器,但这与设置一个面向全世界的网页不同,该网页声称机器的官方目的是游戏服务器。这些核心协议的端口分配通常是永久的,如果你想要与其他站点互操作,你不会改变它们。
OpenBSD 软件通常以 root 身份绑定到保留端口,然后降低权限,作为无权限用户执行其余功能。这些无权限用户,在第六章(ch06.html "第六章。用户管理")中讨论,比普通用户账户的权限还要少。
如果你必须运行绑定到保留端口的程序,并且它只能以 root 身份运行,请仔细考虑你是否真的需要它。尝试找到一个可以进行权限分离的替代服务器。如果你找不到,至少将此服务安装在专用机器上,以减少其对网络中其他服务构成的威胁。
### 哪些端口是开放的?
因此,网络服务通过 TCP 或 UDP 端口提供。程序绑定到端口以提供网络服务。这提出了两个明显的问题:
+ 哪些端口是开放的?
+ 哪些程序在监听每个端口?
你可以用 `netstat(1)` 和 `fstat(1)` 来回答这些问题。
#### 使用 netstat
`netstat(1)` 程序提供了对网络堆栈的一般可见性。使用 `netstat` 检查你的路由表,检查打开的套接字,查看有多少数据包正在穿越你的接口,等等。(我本可以写一本关于 `netstat` 的整本书,但没有人会买。相反,我将在本书中穿插一些 `netstat` 的魔法。)
在查看网络信息时,我建议使用 `-n` 标志关闭 DNS 查找。你总是可以在 DNS 打开的情况下重新运行检查,但将 DNS 查询添加到网络套接字有时会扭曲你看到的信息,并且几乎总是会减慢命令的执行速度。
`-f` 参数允许你选择要显示的协议族。使用 `-f inet` 只显示 IPv4 套接字,或使用 `-f inet6` 只显示 IPv6 套接字。阅读 `netstat(1)` 获取完整的协议列表。
最后,`-a` 告诉 `netstat` 显示由任何进程打开的所有套接字,而不仅仅是用户拥有的套接字。
让我们把所有这些选项放在一起,看看输出结果。在这里,我展示了系统上的公开 IPv4 套接字:
$ netstat -na -f inet
Active Internet connections (including servers)
Proto Recv-Q Send-Q ?Local Address Foreign Address (state)
1tcp 20 0 3192.0.2.135.22 4192.0.2.8.49997 5ESTABLISHED
tcp 0 0 127.0.0.1.587 6. 7LISTEN
tcp 0 0 127.0.0.1.25 . LISTEN
tcp 0 0 8*.22 . LISTEN
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address Foreign Address (state)
9udp 0 0 127.0.0.1.512 .
udp 0 0 *.514 .
列表以开放的 TCP 端口 **1** 开始。`Recv-Q` 和 `Send-Q` 列 **2** 显示了系统正在接收或尝试发送的字节数。
`Local Address` 列显示了此套接字正在监听的本地机器上的 IP 地址。一个服务绑定到机器上的单个地址的端口是可能的——甚至很常见。如果端口是实际连接的一部分,如第一个示例 **3** 所示,IP 地址后面跟着端口号。这个特定的 TCP 连接连接到地址 192.0.2.135 的端口 22。端口 22 是为 SSH 保留的,所以这很可能是 SSH 连接。
如果本地地址是一个星号后跟端口号 **8**,这是一个通配符绑定。一个程序已绑定到该端口,并请求内核确定 IP 地址。这可能是(但不一定是)一个监听套接字。
`外部地址`列**4**显示了参与连接的远程主机的 IP 地址和端口号。如果有显示外部地址,它总是包括端口号。如果此列显示两个星号**6**,则表示服务正在本地端口上等待连接。
`(状态)`列仅适用于 TCP 连接。一个活跃且正在工作的 TCP 连接处于`ESTABLISHED`状态**5**。其他状态(`SYN_RCVD`、`ACK`和`SYN+ACK`)都是连接创建的正常部分,而`LAST_ACK`、`FIN_WAIT_1`和`FIN_WAIT_2`表示连接正在关闭。`LISTEN`状态**7**表示此套接字正在等待传入连接。
UDP 端口号被分配到自己的部分**9**。你可能会在 UDP 部分看到远程主机,特别是对于像 NFS 和 NTP 这样的长时间运行的协议,但请记住 UDP 是无状态的,所以在 UDP 连接上你永远不会看到状态。
如果你只对 TCP 或 UDP 套接字感兴趣,可以使用`-p`标志来仅显示特定协议。在这里,我查看 TCP 套接字:
$ netstat -na -p tcp
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp 0 52 192.0.2.135.22 192.0.2.8.49997 ESTABLISHED
tcp6 0 0 ::1.587 . LISTEN
tcp 0 0 127.0.0.1.587 . LISTEN
tcp6 0 0 ::1.25 . LISTEN
tcp 0 0 127.0.0.1.25 . LISTEN
tcp 0 0 *.22 . LISTEN
tcp6 0 0 *.22 . LISTEN
虽然这看起来与第一个输出示例相似,但请注意,我们看到了 IPv4 和 IPv6 的 TCP 连接和服务。TCP 可以在 IPv4 和 IPv6 上运行,所以选择它显示了两个地址族。完全有可能有一个服务在一个地址族上运行,而在另一个地址族上不运行。我的许多系统只监听 IPv6 上的传入 SSH 连接;这样做可以让我从端口扫描器和蠕虫中隐藏起来(至少目前是这样)。
而不是列出每个等待传入连接的服务,你可以通过省略`-a`标志来仅显示活动连接:
$ netstat -np tcp
Active Internet connections
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp 0 52 192.0.2.135.22 192.0.2.8.49997 ESTABLISHED
#### 使用 fstat
现在你已经知道了哪些 TCP 和 UDP 端口是开放的,你如何知道哪些程序正在监听它们?OpenBSD 包括`fstat(1)`程序,该程序显示系统上所有打开的文件和套接字。网络连接是打开的套接字。在空闲系统上运行`fstat`可以生成数百行输出——每个进程打开的每个文件都有一个条目。虽然这是教育性和有用的,但并不是我们想要的。具体来说,我们想看到哪些程序正在保持网络套接字打开。字符串`internet`表示网络套接字。
$ fstat | grep internet
mwlucas sshd 21403 3* internet stream tcp 0xd5365994 192.0.2.235:22 <-- 192.0.2.8:49997
root sendmail 19063 4* internet stream tcp 0xd537e330 127.0.0.1:25
root sendmail 19063 5* internet6 stream tcp 0xd537e4c8 [::1]:25
root sendmail 19063 6* internet stream tcp 0xd537e660 127.0.0.1:587
root sendmail 19063 7* internet6 stream tcp 0xd537e7f8 [::1]:587
root sshd 29561 3* internet6 stream tcp 0xd537e000 :22
root sshd 29561 4 internet stream tcp 0xd537e198 :22
_syslogd syslogd 12885 4 internet dgram udp *:514
首先,你看到由用户`mwlucas`拥有的`sshd`进程。这是一个无特权的进程,与特定的 SSH 会话相关联。在列表的下方,你看到由 root 拥有的 SSH 守护进程正在监听网络。当一个连接请求到达时,root 拥有的 SSH 守护进程会将它转交给一个无特权的子进程。你还可以看到我们有一些`sendmail`进程正在监听网络。
这个系统运行了预期的 SSH 和电子邮件服务器,并且没有人将任何内容绑定到奇数端口。我那令人讨厌的偏执怀疑是没有根据的(至少这次是这样)。
在`netstat`和`fstat`之间,你应该能够了解在任何给定时间你的系统在网络上的行为。
## IP 路由
大多数系统管理员不需要深入了解 IP 路由,因为大多数服务器只有一个网络接口和一个默认网关。网络管理员会给你一个 IPv4 地址和一个默认路由,你只需将它们放入适当的配置文件中,就可以进行路由。对于大多数 IPv6 主机来说,甚至不需要这样做,因为自动配置可以使一切神奇地工作。服务器将需要一个静态 IPv6 地址和一个手动默认路由。
一些服务器有多个接口,例如一个连接到默认网关,另一个连接到一组相关的应用程序或备份服务器。然而,OpenBSD 系统通常位于网络基础设施中,或者位于需要做出路由决策的隔离区(DMZ)。如果你想在这样的环境中使用 OpenBSD,或者作为防火墙,你必须了解路由的基本知识。
*路由*简单地说就是决定将数据包发送到何处。如果你的系统连接到网络,它不需要做出任何决定;它只需将数据包发送到该网络。你的 192.0.2.0/24 系统已经知道如何到达以 192.0.2 开头的任何 IP 地址——它只需将所有内容发送到本地以太网。它将这些数据包发送到何处?
大多数计算机使用*默认路由*,这是本地网络中的一个 IP 地址,它们将所有目标非本地 IP 地址的数据包发送到该地址。这在只有一个路由器或防火墙提供所有网络访问的地方非常常见。这个设备可能还有一个指向你的 ISP 的默认路由,为你做出所有复杂的路由决策。
在其他情况下,你可能在网络中运行动态路由协议。如果你使用的是开放最短路径优先(OSPF)、边界网关协议(BGP)或路由信息协议(RIP),OpenBSD 有专门用于集成这些协议的守护进程。然而,在完全动态路由和简单默认路由之间有一个中间地带,在尝试完全动态路由之前,你应该了解它。
我们在这里将使用一个 IPv4 示例来介绍一个简单的情况。(IPv6 路由与 IPv4 路由完全相同,但包含更多的冒号。)
### IPv4 路由网络示例
如果一个网络有多个网关通向不同的网络,网络上的主机必须做出路由决策。假设你的网络连接了多个路由器,每个路由器都连接到不同的网络。你的网络上的计算机决定将数据包发送到何处。以下是一个常见的双防火墙情况的示例:

在这种网络设计中,主机必须穿越 DMZ 才能进入互联网或内部网络。(其他设计也存在,例如中心辐射模型,但我特意选择了一个需要路由的设计。)
外部防火墙提供了一层保护。它只允许被认为必要的特定流量(我们将在第二十一章 Chapter 21 中介绍默认拒绝策略)。然而,它确实允许连接到你的 DMZ 主机。
DMZ 中的主机在一定程度上是脆弱的。它们不足以信任它们在内部网络中。你的入侵检测系统或你的 Web 服务器可能在这里。
内部防火墙,就像外部防火墙一样,只允许被认为对组织目的必要的流量。然而,它可能不允许来自外部世界的任何连接,并且它不信任 DMZ 上的主机。
只有高度信任的主机才被允许在内部网络上。这是组织保存其最宝贵数据的地方,例如财务记录、客户数据库和电影收藏。
这个网络中的许多主机只需要做出非常简单的路由决策。内部网络上的任何事物都只有一种方式可以到达任何事物,而互联网上的任何主机也只有一个方式可以到达内部或 DMZ 网络。
外部防火墙直接连接到 DMZ 网络,因此它可以向这些主机发送数据包。它需要一个指向互联网的默认路由,以便它可以到达世界上的其他地方。要到达内部网络上的主机,它必须将数据包发送到内部防火墙的外部接口。如果你在外部防火墙上没有配置这一点,数据将永远不会到达内部防火墙。因为外部防火墙负责内部网络的互联网访问,丢失此路由将使内部网络与互联网断开连接;内部系统可以发送数据包,但永远不会收到任何数据。外部防火墙需要路由。
类似地,你可以在 DMZ 内部每个主机上配置路由。在这种情况下,来自防火墙的 ICMP 重定向将为这些主机提供路由,但在易受攻击的网络中信任 ICMP 重定向是不明智且混乱的,因为它假设 DMZ 上的每个主机和每个防火墙都接受并发送 ICMP 重定向。如果你使用 OpenBSD,你希望你的服务器是安全的,因此请在你的 DMZ 系统上配置路由。
在这个例子中,我配置了外部防火墙的路由。为 DMZ 主机配置路由几乎与这个例子相同。
### 使用 route(8)管理路由
`route(8)`命令管理所有系统路由。像`netstat`一样,`route`有几个子功能,允许你查看、编辑和监控系统路由表。虽然`route(8)`手册页有完整的详细信息,但查看、添加和删除路由的能力应该足以让你开始。
#### 查看路由
OpenBSD,就像任何其他网络设备一样,将路由保存在路由表中。要查看 IPv4 和 IPv6 路由,请输入**`route show`**。添加**`-n`**以删除 IP 地址到名称的转换。
这是 IPv4 路由表:
$ route -n show
Routing tables
Internet:
Destination Gateway Flags Refs Use Mtu Prio Iface
1 default 192.0.2.1 UGS 4 6414 - 8 vic0
2 127/8 127.0.0.1 UGRS 0 0 33196 8 lo0
3 127.0.0.1 127.0.0.1 UH 1 170 33196 4 lo0
4 192.0.2.32/24 link#1 UC 1 0 - 4 vic0
5 192.0.2.1 00:0c:42:20:7f:42 UHLc 1 0 - 4 vic0
6 224/4 127.0.0.1 URS 0 0 33196 8 lo0
表格显示了以下信息:
+ `目标`字段列出了此路由应用的 IP 地址范围——目标地址。`默认`条目表示默认网关,即系统发送所有没有特定路由的数据包的地方。
+ `网关`字段说明了此路由的数据包应该发送到何处。网关可以是主机名、IP 地址或网络接口。
+ `标志`字段包含指示此路由类型及其行为的标记。下一节将介绍各种路由标志。
+ `引用`字段显示了内核中对该路由的引用次数(也称为*引用计数器*)。如果引用计数器降至零,则删除路由。这对于系统管理来说没有实际用途,因为一个引用就足以保持路由在路由表中;额外的引用不会改变任何事情。
+ `使用`计数器每次数据包使用该路由时都会增加。
+ `Mtu`是 MTU——可以通过此路由传输的最大帧大小。如果字段包含连字符(`-`),OpenBSD 将使用底层物理接口的 MTU。回环接口`lo0`不是一个物理接口,因此 OpenBSD 明确地将 MTU 设置得非常高。您可能会看到具有较低 MTU 的路由,如果路径 MTU 发现已启动。
+ `优先级`字段给出了路由的优先级。OpenBSD 支持到单个目的地的多个路由。一些路由比其他路由更受欢迎,OpenBSD 将使用优先级编号最低的路由。由动态路由协议(如 BGP 或 OSPF)提供的路由比静态路由具有更高的优先级编号。
+ `接口`字段显示了此路由使用的接口。
### 注意
OpenBSD 还包括动态路由守护进程,如`ospfd(8)`和`bgpd(8)`。这里不涉及它们,因为这个主题本身就可以写一本书。
让我们看看这个示例中路由中有什么有趣的。第一个条目**1**是系统默认路由。如果没有更具体的路由,数据包将被发送到 IP 地址 192.0.2.1。
要到达**2**处的网络 127.0.0.0/8,数据包应发送到 IP 地址 127.0.0.1。127.0.0.0/8 是为回环地址保留的地址范围,127.0.0.1 始终是本地机器。注意高 MTU;这是一个软件接口,因此通过它的帧大小没有物理限制。
要到达**3**处的 IP 地址 127.0.0.1,请将数据包发送到 IP 地址 127.0.0.1。这听起来可能有点繁琐,但这是一个有效的路由,需要包含在表中。请记住,127.0.0.1 始终是本地机器的回环地址。
要访问 IP 地址 192.0.2.0/24 的**4**,请使用`link#1`作为网关。这是一个本地物理接口——在这种情况下,我们的以太网接口。名为`link#1`的接口实际上是索引`#1`的接口,实际上在系统管理员的其他地方并没有真正暴露。这些地址是本地的,你必须通过机器上的 IP 地址来确定这个接口。机器本地的地址实际上不需要在路由表中,但没有人费心移除这个历史性的小错误。
要访问本地网络上的特定 IP 地址**5**,你会得到一个包含 IP 地址和物理媒体地址的路由。因为此主机通过以太网连接,网关是一个 MAC 地址。系统需要查找的每个本地地址都会得到一个路由条目,你应该几乎总是显示默认网关的特定路由。
**6**处的最后一个路由是为多播地址范围 224/8 设计的。如果你没有使用多播,它应该指向本地主机。
### 注意
多播是一个超出本书范围(再次)的复杂主题。但如果你感兴趣,OpenBSD 支持多播没有问题。
#### 路由标志
路由表的`Flags`列指示路由是如何生成或使用的。`netstat(1)`包含完整的路由标志列表。表 11-2 列出了常见的标志。
表 11-2。表 11-2:常见路由标志
| 标志 | 描述 |
| --- | --- |
| `C` | 此路由已被克隆。 |
| `c` | 这是一个特定协议的路由(例如,到以太网 MAC 地址)。 |
| `D` | 此路由是动态的。 |
| `G` | 此路由通过网关转发。 |
| `H` | 此路由是为特定主机设计的。 |
| `L` | 此路由是用于本地链路层的。 |
| `M` | 此路由已被修改。 |
| `R` | 这是一个拒绝路由。数据包被丢弃,并发送错误。 |
| `B` | 这是一个黑洞路由。数据包被静默丢弃。 |
这些标志告诉你路由的来源以及如何使用它。
#### 添加路由
使用`route add`命令添加路由。你必须知道目标网络、其子网掩码和网关。
route add address-block/netmask netmask gateway
在我们的示例网络中,外防火墙需要一个路由来访问私有网络,192.0.2.128/25。要将此网络路由到内防火墙 192.0.2.2,请运行以下命令:
route add -net 192.0.2.128/25 192.0.2.2
add net 192.0.2.128: gateway 192.0.2.2
数据包将立即使用该路由。如果你运行`route show`,你会看到新路由。
要添加默认路由,请运行`route add default`,并指定默认网关的 IP 地址,如下所示:
route add default 192.0.2.1
add net default: gateway 192.0.2.1
要在启动时自动添加路由,请将`route`语句放入指向目标网络的*/etc/hostname.if*文件中。这些路由在接口启动时出现,在*/etc/rc.securelevel*运行或任何本地守护进程启动之前。你将在下一章中看到使用*hostname.if*添加路由的示例。
要在启动时自动添加默认路由,请将默认网关 IP 地址放入*/etc/mygate*。
#### 删除路由
要删除路由表条目,请使用`route delete`命令,并指定网络地址和子网掩码。
route delete address-block -netmask netmask
要删除之前示例中添加的路由,请运行此命令:
route delete -net 192.0.2.128 -netmask 255.255.255.128
delete net 192.0.2.128
你现在应该对路由的工作方式有一个相当清晰的认识。
现在你已经知道了事物应该如何配合,让我们看看如何配置以太网网络。
* * *
^([27]) 你可能会笑,但本书的技术审稿人是第一个处理 RFC 1149 中规定的实际测试的 IP-over-carrier-pigeon 实施团队的一员。这就是我知道他花时间如此详细地审阅这本书的原因。(如果他就是这样度过他的时间,他不可能声称自己太忙,对吧?)
^([28]) 我从不真正同情我的读者;我只是不希望你真的*这么说*。
^([29]) 一些操作系统将数字以 0 开头的地址视为八进制。实际上不要使用像 192.000.002.013 这样的地址,否则你可能会得到一个基于 8 的惊喜。
^([30]) 或者你可以去查查。随便吧——你不相信我并不会伤害我的感情。
^([31]) 记录在案,迈克叔叔的安全策略阻止他传递烤甘薯。如果你想得到它们,你将不得不强行拿走。
^([32]) 我曾经计算过有多少人混淆了 514/tcp 和 514/udp,但这个数字太高了,以至于我感到沮丧,所以我停止了。
## 第十二章。连接到网络
*我的隧道现在已启动*。
*我可以做 IPv6*。
*我和其他三个人*。
 所有的 IPv4 和 IPv6 理论都是好的。现在让我们实际操作一下,并连接到网络。虽然拨号连接与 OpenBSD 一起工作,但它们现在不太使用了,所以我们重点关注以太网连接。以太网是当今最常见的网络类型,也是 OpenBSD 系统上最常见的网络接口。
大多数人都有 IPv4 连接,但 IPv6 越来越重要。如果你无法将原生 IPv6 连接到你的网络,你可以使用隧道来访问 IPv6 地址空间,并为你的客户端提供 IPv6。我将在本章中介绍如何获取和配置这样的隧道。
最后,OpenBSD 可以将网络连接组合成 trunk 或将它们拆分成虚拟局域网 (VLAN)。本章将介绍这两种方法。
## DNS 解析
你可能想使用主机名而不是 IP 地址,这样你就可以浏览到 *[`www.cnn.com/`](http://www.cnn.com/)* 而不是 *[`157.166.255.18`](http://157.166.255.18)*。类 Unix 系统使用 *resolver* 来完成这个功能。
大多数主机使用两个工具在 IP 地址和主机名之间进行映射:*hosts* 文件和 DNS。(不同的操作系统支持额外的名称服务,如 YP、LDAP、NIS 等,但几乎每个系统都支持这两个。)
*hosts* 文件是本地机器上的一个文本文件,其中包含静态 IP 地址和主机名列表。DNS 是一个更动态的服务,它跨越网络查找信息。你可以通过 IP 地址指定 DNS 服务器,但我们将更详细地查看 *hosts* 文件。
如果你更喜欢 IPv4 或 IPv6 地址?或者你想让 *hosts* 文件覆盖 DNS?也许你有一个默认域名,你的查询应该使用。解析器会搜索,直到它找到第一个答案或者耗尽其信息来源,所以这些问题很重要。请在 */etc/resolv.conf* 中告诉解析器你的需求。
### /etc/resolv.conf 文件
你在 */etc/resolv.conf* 中配置解析器行为。没有 */etc/resolv.conf* 的系统只能找到 *hosts* 文件中列出的主机名。因为 *hosts* 文件一开始是空的,这可能不是你想要的。首先指定域名。
#### 默认搜索域名
如果你想要 ping 远程网络上的主机,你可能预计需要指定整个域名。输入 **`ping www.openbsd.org`** 应该可以工作。但如果你想要 ping 你公司的网站服务器,直接输入 **`ping www`** 会更有意义。而且你可以这样做,因为 OpenBSD 允许你指定默认域名,这样当你输入一个简短的域名时,它会尝试找到正确的主机。
例如,如果你只有一个本地域名,你可以在 */etc/resolv.conf* 中这样列出域名关键字:
domain michaelwlucas.com
现在,当我输入 `ping ftp` 时,解析器应该获取主机 *ftp.michaelwlucas.com* 的 IP 地址。
如果你有一个以上的本地域,请使用 `search` 关键字和域名列表,如下所示:
search michaelwlucas.com openbsd.org
如果我现在输入 `ping ftp`,解析器应该获取主机 *ftp.michaelwlucas.com* 的 IP 地址。一旦解析器了解到不存在这样的主机,它将检查 *ftp.openbsd.org*。因为该主机存在,`ping` 将开始工作。`search` 关键字可以有最多六个域,并且不能超过 1024 个字符。
#### 使用域名和搜索
你只能使用 `domain` 或 `search`。如果你两者都使用,文件中的最后一个条目将获胜。如果你列出多个搜索或域名行,文件中的最后一行将生效。以下是如何不这样做的方法:
search cnn.com openbsd.org
search sluggy.com michaelwlucas.com
domain blackhelicopters.org
你不妨删除两个 `search` 语句。解析器永远不会遍历这些域名列表;它将仅使用 `domain` 列表,因为它是最新的。
#### 域名服务器
现在解析器知道默认要检查哪些域,告诉它要使用哪些域名服务器。按 IP 地址顺序,每行列出每个域名服务器,每个域名服务器占一行。
nameserver 192.0.2.5
nameserver 198.51.100.5
nameserver 2001:db8::5
你可以按 IP 地址列出最多三个域名服务器。(`nameserver` 条目中的主机名不会工作,原因很明显。)
如果你的 *resolv.conf* 没有列出域名服务器,解析器应在本地机器上查找域名服务器。
#### 查找顺序
你可能从 DNS 或从 *hosts* 文件中获取主机信息。解析器应在找到查询答案后停止。如果你先检查 *hosts* 文件然后检查 DNS,*hosts* 文件中的条目将覆盖域名服务器。如果你在 *hosts* 文件之前检查域名服务器,*hosts* 文件仅在无 DNS 记录可用时使用。两种方法都有其用途,但默认情况下,解析器先检查 *hosts* 文件,然后检查 DNS。要反转此操作,请使用 `lookup` 关键字。
lookup bind file
`file` 选项代表 */etc/hosts*,而由于历史原因,`bind` 代表 DNS。(第一个 DNS 服务器软件是伯克利互联网名称域服务器,或 BIND。)反向(`file bind`)是默认设置,因此不需要明确指定它。
#### 优先 IP 协议
解析器默认先搜索 IPv4 记录,然后查找 IPv6 记录。要反转此操作,请使用 `family` 关键字。
family inet6 inet4
再次强调,反向是默认设置,因此在这种情况下不需要使用此关键字。
### /etc/hosts 文件
*/etc/hosts* 文件将 IP 地址与主机名匹配。虽然 *hosts* 文件非常简单,但其内容仅可在本地机器上访问。*hosts* 文件在小型私有网络中最有用,例如在你的家庭或测试实验室中。你也可以使用 *hosts* 文件来覆盖 DNS 服务器中的数据,例如当你想测试新系统时。
*/etc/hosts* 文件中的每一行代表一个主机。每行的第一个条目是一个 IP 地址。第二个是主机的完全限定域名。在这两个条目之后,你可以为该主机有任意数量的别名。我经常在行末添加注释,以井号(`#`)开头。
曾经有一段时间,我在家里有一个只有四台机器的小型网络:代理/防火墙、妻子的台式机、我的笔记本电脑和我在上面做愚蠢事情的崩溃机器。*hosts*文件看起来像这样:
192.0.2.1 1nat.blackhelicopters.org 2nat firewall gateway
192.0.2.8 boss.blackhelicopters.org boss wife 3#don't crash
192.0.2.20 crashbox.blackhelicopters.org crashbox test
192.0.10.21 laptop.blackhelicopters.org laptop mwlucas
在**1**处的机器`nat.blackhelicopters.org`也有`firewall`和`gateway`的名称**2**。我在**3**处添加了一个笔记提醒自己不要对我的妻子的台式机运行安全扫描器。(机器`crashbox`也被称为`test`。)
任何具有此*hosts*表的机器都可以通过名称找到*hosts*表中列出的任何机器。例如,我可以运行`ping boss`或`ssh crashbox`来访问目标机器。
*hosts*文件在查找网络主机方面工作得很好,但每次添加、删除或更改一台机器时,你都必须在每个计算机上编辑*/etc/hosts*。每次更改 IP 地址时,你都必须在每个机器上编辑*/etc/hosts*。
### 注意
不幸的是,*/etc/hosts*无法扩展。当我得到第五台机器时,我添加了一个仅限内部使用的 DNS 服务器,并清空了我所有系统上的*hosts*文件。
### 解析器与动态配置
如果你的 OpenBSD 系统像笔记本电脑一样在多个网络之间移动,你可能使用 DHCP 来配置你的网络连接。
DHCP 会覆盖*/etc/resolv.conf*,用其网络的信息。这对于大多数用户来说是合适的,但如果你携带的是 OpenBSD 笔记本电脑,你就不是普通人。你可能希望你的解析器配置(如你的域名搜索列表)在无论你连接到哪个网络时都保持有效。
OpenBSD 支持在文件*/etc/resolv.conf.tail*中进行永久解析器配置。当 OpenBSD 的 DHCP 客户端从服务器获取*/etc/resolv.conf*信息时,它会写入*/etc/resolv.conf*并在末尾添加`/etc/resolv.conf.tail`。
记得只有最后一个`search`或`domain`关键字有效吗?*resolv.conf.tail*利用了这一点,允许你覆盖你的网络管理员的搜索顺序。
## 以太网
以太网是一个共享网络,意味着许多不同的机器可以连接到同一个以太网,并且可以直接相互通信。我将假设你使用的是在普通办公室或数据中心中找到的以太网。此外,尽管以太网已经在许多不同的物理媒体上实现,但我将假设你正在使用 CAT5 或更好的电缆——这是今天最受欢迎的选择。如果你使用一些不寻常的媒体类型,或者你的网卡支持多种媒体,你可能需要在你的接口上手动设置首选媒体。
### 协议和硬件
以太网是一种*广播协议*,这意味着你发送的每个数据包都可以发送到网络上的每个主机(尽管大多数以太网硬件限制了接收者)。要么是你的网卡,要么是你的设备驱动程序将为你计算机打算接收的数据与其他计算机打算接收的数据分开。一个所有主机都可以直接与其他所有主机通信的以太网部分,而不涉及路由器,被称为*冲突域*或*段*。
您可以使用*hubs*连接以太网段,这些是硬件设备,可以物理连接多个以太网主机。网络集线器将所有接收到的帧转发到所有其他网络设备,每个主机负责过滤流量。这是老式的以太网,对于调试网络问题可能很有用。
*交换机*在很大程度上取代了集线器。每个以太网连接都需要一个唯一的标识符,称为*MAC 地址*(有时也称为*以太网地址*),它是一个 48 位的数字。交换机通过过滤连接设备的 MAC 和 IP 地址来控制发送到每个主机的流量,并且(主要)只将帧转发到它们打算发送到的设备。交换机通过减少每个主机必须处理的流量量来减少每个系统的流量和负载。
在 i386 和 amd64 硬件上,MAC 地址是卡的一个属性。在某些其他平台上,例如 SPARC,MAC 地址是服务器本身的属性。IPv4 和 IPv6 都使用 MAC 地址在本地网络上找到其他主机。
#### IPv4 和 ARP
当一个系统需要向本地以太网上的另一个基于 IP 的主机传输数据时,它首先广播一个以太网请求,询问:“哪个 MAC 地址负责这个 IP 地址?”如果有一个主机响应,则将针对该 IP 的进一步数据传输到该 MAC 地址。这个过程由 ARP 处理。
使用`arp(8)`查看您系统的 ARP 表,这是您系统所知的宿主机的列表。输入`arp -a`以显示您的计算机所知的所有 MAC 地址和 IPv4 主机名。
$ arp -a
fly.blackhelicopters.org (192.0.2.225) at 00:a0:c8:10:eb:82 on fxp0
caddis.blackhelicopters.org (192.0.2.226) at 00:16:36:c0:58:a5 on fxp0 static
treble.blackhelicopters.org (192.0.2.227) at 00:0c:42:5a:58:ae on fxp0
salmon.blackhelicopters.org (192.0.2.232) at (incomplete) on fxp0
在这里,您可以看到与我以太网网络上的这个主机通信的三个主机。我还有更多的主机,但由于这台机器最近没有与它们交谈,它们不在本地 ARP 表中。
如果 MAC 地址显示为`incomplete`,则表示您的计算机已尝试与此主机通信,但无法获取其 MAC 地址。在这个例子中,我尝试向主机`salmom`发送数据,但我的计算机无法到达它。(重新开启`salmom`可能会有帮助。)
#### IPv6 和邻居发现
IPv6 主机也使用 MAC 地址通过 ND(在上一章中引入的 IPv6 协议)找到彼此。使用`ndp(8)`查询您的 ND 缓存。`ndp`使用的命令行标志故意与`arp`的类似。
$ ndp -a
Neighbor Linklayer Address Netif Expire S Flags
2001:db8:0:12:20c:29ff:feb5:7565 0:c:29:b5:75:65 vic0 permanent R
2001:db8:0:12:5446:fbc:fca0:f2e9 0:c:29:b5:75:65 vic0 permanent R
…
fe80::20c:29ff:feb5:7565%vic0 0:c:29:b5:75:65 vic0 permanent R
fe80::20c:42ff:fe20:7f42%vic0 0:c:42:20:7f:42 vic0 11h20m47s S R
fe80::1%lo0 (incomplete) lo0 permanent R
与 ARP 缓存一样,ND 缓存显示了每个主机的 IPv6 地址、物理地址、接口和其他详细信息。您将看到的 ND 条目比 ARP 条目多,因为所有链路本地地址都显示在 ND 缓存中。
如果您尝试连接到直接连接到您本地网络的主机,但它没有响应,请检查 ND 缓存。如果 ND 缓存条目显示为`(incomplete)`,就像 ARP 一样,存在某种基本的连接问题。
#### 速度和双工
以太网支持多种速度。您今天可能找到的最慢速度是每秒 10 兆比特(Mbps),但它正在迅速消失。大多数人使用 10/100Mbps 或每秒 1 千兆位(Gbps),尽管您会看到 10Gbps、40Gbps 和 100Gbps 以太网的出现。
您的网络上的主机和与之连接的交换机必须就其连接速度达成一致。如果 OpenBSD 主机认为它连接在 100Mbps,但交换机认为连接是 1Gbps,连接将会不稳定。虽然 *自动协商* 通常会使双方就共同设置达成一致(并且对于千兆连接来说是绝对必要的),但您可以为 10/100Mbps 连接手动设置双工和速度。尽管一些交换机厂商因自动协商不佳而臭名昭著,但只要可能,您应该让以太网自行配置。
*双工* 决定了一张卡是否可以同时发送和接收数据。*半双工* 连接意味着在某一时刻,以太网卡要么在发送要么在接收;它不能同时进行。*全双工* 连接可以同时发送和接收。就像连接速度一样,如果交换机和主机在双工设置上不一致,连接将会不稳定。千兆以太网连接涉及的内容远不止速度和双工,它们*必须*进行自动协商。
仅因为一个设备声称它可以使用定义为 10/100Mbps 以太网的协议,并不意味着它可以以任何速度使用该协议。此外,标有“1Gbps”的卡实际上可能无法每秒传输千兆位。一些网卡会传输其声明的流量,而其他网卡则会在该流量的几百分比处犹豫不决。交换机的质量也参差不齐。
如果您将以太网声明的速度视为一种语言,这可能更有意义。例如,我可以声称我会说俄语和德语,但我从 1985 年起就停止学习外语了。当我 2007 年去德国时,我每分钟只能管理大约三个单词——借助翻译卡和短语书。如果我是以太网卡,制造商会声称我会说德语和俄语,并将我运往西伯利亚。^([33)]
获取合适的硬件。不过,不要在 OpenBSD 邮件列表上询问。过去几个月有人询问过硬件推荐。检查存档。建议没有变化。
## 配置以太网
当为客户端计算机配置以太网时,如果您的 IPv4 网络提供 DHCP,您应该可以直接连接。如果您使用 IPv6,您应该可以连接电缆并让自动配置接管。
如果一台特定的机器将作为服务器,静态 IP 地址可能更有意义。在分配静态地址之前,您需要以下信息:
+ 一个 IP 地址(IPv4、IPv6 或两者兼有)
+ 子网掩码/前缀长度
+ 默认网关的 IP 地址
带着这些信息,将您的系统连接到网络并继续阅读。我首先将讨论如何使用 `ifconfig(8)` 和 `route(8)` 手动执行更改,然后回顾如何在启动时自动设置这些更改。无论如何,您必须配置解析器,如本章开头所述。
### 使用 ifconfig(8)
如果您通过网络安装了 OpenBSD,您的以太网连接应该已经工作,但可能不会按照您喜欢的确切方式设置。要管理您的网络接口,请使用 `ifconfig(8)` 工具。
让我们来看看您的以太网卡,看看它有什么要说的。首先,通过运行 `ifconfig` 来询问您的系统安装了哪些接口。
所有 OpenBSD 系统默认都有三个逻辑接口:`lo0`、`enc0` 和 `pflog0`。`lo0` 接口是回环接口,指的是本地机器。`enc0` 接口是一个封装接口,用于 IPsec 流量。最后,`pflog0` 用于记录 PF 流量,如 第二十二章 中所述。其余的接口都是物理接口。
与一些操作系统不同,OpenBSD 网络接口的命名是根据底层硬件的设备驱动程序来命名的。以下是一个示例列表:
$ ifconfig
fxp0: flags=8843<1UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
lladdr 00:16:36:c0:58:a5
priority: 0
groups: egress
media: Ethernet autoselect (100baseTX full-duplex)
2 status: active
3 inet 192.0.2.226 netmask 0xffffff00 broadcast 192.0.2.255
inet6 2001:db8::216:36ff:fec0:58a5 prefixlen 64
inet6 fe80::216:36ff:fec0:58a5%fxp0 prefixlen 64 scopeid 0x2
接口 `fxp0` 使用的是 `fxp(4)` 设备驱动程序,根据手册页的说明,这是一张英特尔 EtherExpress PRO 10/100 网卡。如 **1** 所示,接口处于激活状态,意味着它是活跃的并且准备就绪。`lladdr` 是链路本地地址,或网卡的 MAC 地址。这张网卡位于 `egress` 组中。OpenBSD 在多个地方使用接口组,包括在 第二十二章 中讨论的包过滤器。
要查看连接下层的物理媒体类型,请检查 `media` 行。这个特定的连接以 100Mbps 全双工运行。连接处于活动状态,如 **2** 所示;物理层不仅已配置,而且还有一个链路指示灯,准备就绪。连接已分配了 IPv4 地址和子网掩码,如 **3** 所示。您可以在接下来的两行中看到,已分配了 IPv6 地址和链路本地 IPv6 地址。
使用 `ifconfig` 为网络接口分配、更改或删除 IP 地址。OpenBSD 安装程序在启动时提供配置网络卡的服务,但如果您在安装过程中没有配置所有接口,或者安装后添加或删除了网络接口,您将需要手动进行。
#### 添加 IP 地址
要为 IPv4 添加 IP 地址,从接口分配的 IP 地址和子网掩码开始。
ifconfig interface-name IP-address netmask
例如,如果您的网卡是 `fxp0`,IP 地址是 192.0.2.55,子网掩码是 255.255.255.128,您将运行以下命令:
ifconfig fxp0 192.0.2.55 255.255.255.128
使用点分十进制、十六进制或甚至斜杠表示法指定子网掩码,如下所示:
ifconfig fxp0 192.0.2.55/25
如果你使用斜杠,不需要单独指定子网掩码。
使用 IPv6 添加 IP 地址略有不同。指定地址,一个斜杠,以及前缀长度,但不要尝试添加单独的子网掩码;只需使用地址部分中的斜杠。以下是一个示例:
ifconfig fxp0 inet6 2001:db8:0:12::2/64
#### 移除 IP 地址
如果您需要从一个接口中移除 IP 地址,请使用`ifconfig`的`delete`选项来删除 IPv4 和 IPv6 地址。
ifconfig fxp0 192.0.2.55 delete
效果是立即的,所以请确保不要通过移除所有可到达的 IP 地址或移除 SSH 守护进程所附加的唯一地址而将自己锁定在系统之外。(在某些罕见的情况下,对已删除地址的现有连接可能会继续工作,但它们很可能不会,所以不要寄希望于此。)
#### 一张以太网卡上的多个 IP 地址
一个网络接口可以响应多个 IP 地址的请求,这对于服务器可能支持数百或数千个域名并且需要为每个域名分配 IP 地址来说非常重要。(对于普通网站来说,这并不那么重要,但对于基于 SSL 的网站和依赖于反向 DNS 的协议来说,这可能是重要的。)
要向接口添加额外的 IP 地址,请使用*IP 别名*。IP 别名告诉网络卡“同时回答对这个 IP 地址以及您自己的请求。”要添加别名 IP 地址,请使用`ifconfig`并在接口名称后使用关键字`alias`来告诉`ifconfig`这是一个别名。请确保始终为别名地址使用子网掩码 255.255.255.255 或/32。
ifconfig fxp0 alias 192.0.2.230/32
ifconfig fxp0
…
inet 192.0.2.226 netmask 0xfffffff0 broadcast 192.0.2.239
inet 192.0.2.230 netmask 0xffffffff
这里列出的接口有一个主 IP 地址 192.0.2.226 和一个别名 IP 地址 192.0.2.230。
当处理 IPv6 时,添加`inet6`关键字,如下所示:
ifconfig fxp0 inet6 alias 2001:db8:0:12::3/64
重要的是要认识到,一个网络连接的主机上的所有出站连接都使用主 IP 地址。例如,您可能有一个接口绑定有 2000 个 IP 地址,但当你`ssh`出去时,连接来自主地址。在编写防火墙规则和访问控制列表时请记住这一点,因为虽然一些程序有设置不同源 IP 地址的选项,但它们是例外。
OpenBSD 内核实际上并不区分主 IP 地址和别名——它只是保留一个 IP 地址列表——但它将使用列表中的第一个地址作为源地址,除非被告知否则。如果一个主机有多个网络连接,那么出站连接的源地址是数据包离开系统的网络接口的主 IP 地址。
要删除别名,请使用`ifconfig`的`delete`选项并给出 IP 地址,无需给出子网掩码。
ifconfig fxp0 delete 192.0.2.230
对于 IPv6,使用`inet6 delete`。
ifconfig fxp0 inet6 delete 2001:db8:0:12::3
### 注意
如果您删除了接口上的主 IP 地址,第一个别名将成为主 IP 地址。如果您没有剩余的 IP 地址别名,并且您移除了接口的主 IP 地址,那么该接口将停止传递 IP 流量。
### 配置默认路由
使用`route(8)`为每个协议配置默认路由。
route add default 192.0.2.1
add net default: gateway 192.0.2.1
IPv6 默认路由几乎相同,但您必须添加`-inet6`修饰符。
route add -inet6 default 2001:db8:0:12::1
add net default: gateway 2001:db8:0:12::1
一旦您为主机添加了 IP 地址和默认路由,您就应该能够访问您的整个网络和互联网。现在让我们看看如何在重新启动之间进行这些更改。
### 使用动态配置
要让 OpenBSD 从 DHCP 服务器获取 IPv4 地址,运行`dhclient(8)`并给它您想要配置的接口名称。
dhclient fxp0
`dhclient`获取 IP 地址,覆盖`/etc/resolv.conf`,并配置默认路由。
对于 IPv6,运行`rtsol(8)`代替。
rtsol fxp0
记住,IPv6 自动配置不会配置您的解析器。您需要从 IPv4 DNS 服务器那里获取帮助,或者手动配置`/etc/resolv.conf`。
### 启动时配置网络
虽然`ifconfig(8)`可以用于即时更改,但您的系统应在启动时正确配置其接口,包括接口上的任何别名、接口启动时添加的任何路由等。
每个接口都有一个配置文件,`/etc/hostname.interfacename`,通常称为`hostname.if`。我的台式机上的`fxp0`接口使用配置文件`/etc/hostname.fxp0`,无线接口`wpi0`使用`/etc/hostname.wpi0`,依此类推。在启动时,OpenBSD 的`/etc/netstart`脚本读取所有的`hostname.if`文件,如果找到匹配的物理接口或可以创建匹配的逻辑接口,它将相应地配置接口。
要配置接口的 IPv4 地址,在`hostname.if`中输入以下格式的行:
inet ipaddress netmask broadcastaddress ifconfig-options
广播地址和选项是可选的。要使用选项但不指定广播地址,请将广播地址设置为`NONE`。您还可以使用斜线代替十进制等价的子网掩码。
类似地,添加 IPv6 地址如下:
inet6 ipv6address/prefix ifconfig-options
要在启动时为`fxp0`分配 IPv4 地址 192.0.2.226 255.255.255.240 和 IPv6 地址 2001:db8:0:12::2/64,请在`/etc/hostname.fxp0`中使用以下命令:
inet 192.0.2.226 255.255.255.240 NONE description 'top card'
inet6 2001:db8:0:12::2/64
在这里,我还定义了一个将在`ifconfig`输出中显示的接口描述。
要在启动时创建 IP 地址别名,请在`hostname.if`中使用`alias`关键字。
inet alias 192.0.2.230/32
inet6 alias 2001:db8:0:12::3/64
要在接口启动时运行命令,请在命令前加上感叹号。必须运行的所有命令都必须在根分区上可用(例如,在`/bin`或`/sbin`中)。此功能最常用于路由,但您也可以使用其他命令。
!route add 192.0.2.128/25 192.0.2.2
要动态配置接口,通过 DHCP(IPv4)或`rtsol`(IPv6),将字符串`dhcp`或`rtsol`单独放在一行上。
dhcp
rtsol
任何未按此处所示格式化的内容都将未经编辑传递给`ifconfig(8)`。例如,要运行特定的`ifconfig`命令,请在`hostname.if`中将参数单独放在一行上。
description 'lower card'
如果您只想激活一张卡,但不配置它,请在单独的一行上使用单词`up`来激活接口。
up
并且记住,您可以使用`/etc/netstart`测试`hostname.if`更改,如果适当,指定接口名称,如下所示:
/bin/sh /etc/netstart fxp0
如果不包括接口名称,则重新配置系统上的所有接口。
## 通道化
服务器可以有冗余硬盘、电源等。OpenBSD 通过将多个以太网链路组合成一个单独的虚拟链路或 *trunk* 来支持冗余网络连接。你可能也知道这被称为 *链路聚合*、*网络适配器团队协作* 或 *绑定*。
### 注意
思科人员将 *trunks* 理解为支持多个并发 VLAN 的以太网链路。大多数厂商,包括 OpenBSD,都不那样使用 *trunk* 这个词。OpenBSD 支持在 `trunk(4)` 功能之外,通过单个链路发送多个 VLAN。
### 链路聚合协议
要将多个物理链路作为单个大链路使用,你需要一种方法来在链路之间分配流量。OpenBSD 支持五种不同的方法在 trunk 成员之间分配帧,尽管并非所有方法都适用于所有环境。要获取完整列表,请参阅 `trunk(4)`,但我推荐的适用于实际使用的协议是链路聚合控制协议 (LACP)、roundrobin 和故障转移。LACP 是链路聚合的行业标准。物理接口被绑定成一个具有与各个接口总和大致相同带宽的单个虚拟接口。LACP 非常容错,几乎所有高端管理交换机都应该支持它。如果你的交换机支持 LACP,请使用它,但必须在这种类型的 trunk 传输流量之前在交换机端口上配置 LACP。
在 roundrobin 方法中,OpenBSD 使用 roundrobin 调度器通过 trunk 的活动连接发送帧。trunk 在任何端口上接受传入的包,并且 roundrobin 调度器在 trunk 连接之间轮换,同时添加错误和边缘处理。roundrobin trunk 不需要任何特殊的交换机配置;只需要在同一 VLAN 中的两个端口即可。
在故障转移的情况下,OpenBSD 通过 trunk 的第一个端口发送和接收所有流量,如果该端口失败,则切换到另一个活动端口。故障转移方法不会给你提供额外的带宽,但绝对不需要交换机的支持,甚至可以在老式的集线器上工作。
### trunk 配置
例如,让我们将端口 `em0` 和 `em1` 配置为故障转移 trunk `trunk0`。底层端口之前从未配置过,因此首先在不进行任何配置的情况下激活这些接口。
ifconfig em0 up
ifconfig em1 up
现在创建故障转移 trunk,使用 `ifconfig(8)` 并将这些端口添加到其中,以使 `trunk0` 接口可用。
ifconfig trunk0 trunkproto failover
ifconfig trunk0 trunkport em0
ifconfig trunk0 trunkport em1
你可以将所有这些都在一个长的 `ifconfig` 命令中完成,但我发现当学习时,更简单、更短的命令更容易理解。
就像为物理接口分配 IP 地址一样,为接口分配 IP 地址,并添加默认网关到你的系统中。
ifconfig trunk0 192.0.2.8 netmask 255.255.255.0
route add default 192.0.2.1
你现在应该有一个附加到本地网络的故障转移 trunk。要配置另一个 trunk 协议,只需在创建 trunk 时指定所需的 trunk 协议。你可以在 `trunk(4)` 中找到完整的 trunk 协议列表。
### 启动时的 trunk
在*/etc/hostname.if*中配置您的 trunk。例如,假设您需要编辑*hostname.em0*、*hostname.em1*和*hostname.trunk0*。这两个*em*文件只包含一个单词:
up
这激活了接口,但没有进行配置。
*hostname.trunk0*更为复杂。
trunkproto failover
trunkport em0
trunkport em1
192.0.2.8 netmask 255.255.255.0
您可以将所有这些条目放在一行中,就像您可以使用单个`ifconfig`命令配置 trunk 一样,但再次强调,我发现多行更容易阅读和理解。
您的 trunk 现在应该在启动时开始工作。
注意,trunk 不一定需要由使用相同类型物理介质的接口组成。如果您喜欢冒险,您可以尝试复制一些 OpenBSD 的开发者和用户已知的行为:将有线和无线网络接口连接起来作为 trunk,当您从以太网端口拔掉插头,或者当您重新插入并关闭接入点进行维护时,所有连接都能正常工作(记住,要实现优雅的故障转移?)。
## VLANs
VLANs 是一种在单根线缆上实现多个以太网段的方法。您有时会看到它被称为*802.1q*、*标记*或这些术语的组合。
在 OpenBSD 术语中,一根线缆可以承载多个网络,通过配置额外的接口,您可以像它们有自己的专用线缆一样与这些额外的网络通信。然而,线缆仍然只能承载一定量的数据,所以所有共享线缆的 VLAN 和常规网络(或*原生 VLAN*)共享相同的带宽池。
到达您的网络卡的 VLAN 帧就像常规的以太网帧一样,在以太网帧之前有一个额外的头部信息,表明“这是 VLAN 编号如此这般的部分。”每个 VLAN 都有一个编号。VLAN 编号 1 通常是原生 VLAN——没有任何标记到达的 VLAN。为了方便,我将使用“标记”这个词来描述 VLAN 是如何发送到您的主机的。
您如何在 OpenBSD 中使用 VLANs?也许您的网络被划分为多个以太网段,例如防火墙外、服务器区域和桌面客户端。或者,您可能有一个需要直接访问所有这些段落的 OpenBSD 主机。您可以将所有这些网络通过一根物理线缆进行路由。您可能会遇到带宽问题,但如果您通过服务器推送超过 1Gbps 的数据,您可以考虑添加第二块网络卡。
### 配置交换机
您必须配置您的交换机将 VLANs 以 802.1q 或标记的形式发送到您的 OpenBSD 盒子上,具体取决于交换机的语法。思科使用*802.1q*,惠普的 Procurve 交换机使用*标记*,其他厂商则根据他们的偏见使用不同的方式。为此有数十种不同的语法,所以我不提供具体的示例。如果交换机不能将标记的 VLAN 发送到您的服务器,您将无法使用 VLAN。
### 配置 VLAN 设备
OpenBSD 在请求时会创建 `vlan(4)` 接口。要创建设备,你需要知道你想将 VLAN 连接到哪个物理设备以及你期望的 VLAN 号码。
使用 `ifconfig` 创建 `vlan` 接口。
ifconfig vlanX vlan vlan# vlandev interface
我按照所使用的 VLAN 号码来编号我的 `vlan` 接口。(你可以创建接口 `vlan0` 并将其连接到 VLAN 3,但这对我那脆弱的大脑来说太复杂了。)如果你没有指定 VLAN 号码,OpenBSD 会从接口上的号码分配 VLAN 号码。
例如,这里我创建了一个接口 `vlan3` 并使用它通过接口 `fxp0` 访问 VLAN 3。
ifconfig vlan3 vlandev fxp0
这就是全部内容。现在你可以使用 `ifconfig` 来显示你的新接口:
$ ifconfig vlan3
vlan3: flags=48843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,INET6_PRIVACY> mtu 1500
lladdr 00:16:36:c0:58:a5
priority: 0
vlan: 3 parent interface: fxp0
groups: vlan
status: active
inet6 fe80::216:36ff:fec0:58a5%vlan3 prefixlen 64 scopeid 0x7
它看起来就像任何物理接口一样,从你的角度来看,它确实是。你可以像对其他任何接口一样添加 IP 地址,分配路由,然后继续你的生活。
### 启动时配置 VLAN
要在启动时配置 VLAN 接口,为它创建一个 *hostname.if* 文件。例如,以下是一个 */etc/hostname.vlan3* 文件的内容,它创建了前一个部分中演示的 `vlan3` 接口,将其分配给 VLAN 3,并自动配置 IPv4 和 IPv6:
vlandev fxp0
dhcp
rtsol
OpenBSD 应该在启动时找到这个文件,并根据你的命令创建接口。
## 通过隧道使用 IPv6
假设你已经认真考虑了我的建议,并决定尝试使用 IPv6,但你的 ISP 不提供 IPv6。当你只能得到 IPv4 提供的内容时,如何玩转 IPv6 呢?
许多公司提供免费的 IPv6 隧道服务,他们会通过 IPv4 隧道将你路由到 IPv6。他们甚至免费提供 IPv6 /64,这样你就可以为你的家庭网络配置 IPv6。
虽然我通常避免在本书中推荐供应商,但我确实推荐 Hurricane Electric 的 IPv6 隧道服务,网址为*[`www.tunnelbroker.net/`](http://www.tunnelbroker.net/)*。它的 Web 界面直观,甚至为 OpenBSD 客户端提供配置。
你现在应该对在 OpenBSD 上管理 IPv4 和 IPv6 有了一些了解。当你的大脑从所有这些信息中恢复过来时,我们将转向 OpenBSD 中管理附加软件的主题。
* * *
^([33]) 许多人提出要把我送到西伯利亚。但他们都忘记包括一张返回票。奇怪。
## 第十三章。软件管理
*Blowfish 是可靠的,*
*但是第三方软件呢?*
*通往毁灭的捷径。*
 大多数人并不使用操作系统;他们使用软件,这些软件运行在底层操作系统之上。无论操作系统多么健壮,没有应用程序它都是无用的。
许多商业操作系统包括数百或数千个小程序:游戏、桌面玩具,从看起来很酷的时钟到磁盘清理器和网络浏览器应有尽有。大多数用户从未接触过这些程序中的大多数,但程序仍然占用磁盘空间(以及可能的其他资源)。每个程序都会携带一些基础设施,所有这些软件都可能引起各种问题。
与许多其他操作系统不同,OpenBSD 故意将相对较少的软件包含在默认安装中。你将获得提供软件基础设施所需的一切,没有更多。虽然传统的 UNIX 或类 Unix 系统包括编译器、游戏和手册页,但在安装 OpenBSD 时甚至不需要安装这些!即使你安装了 OpenBSD 中包含的一切,它也将比任何商业操作系统拥有更少的软件。这是因为几乎所有东西都被视为附加包。
这种稀疏性的优点是,你知道系统上确切有什么,这简化了调试。一个来自你从未使用过的程序的随机共享库不会破坏你的程序。缺点是,你需要稍微思考一下才能决定确切需要包含什么,并且你需要安装这些程序。OpenBSD 通过 ports 和 packages 系统尽可能简化软件安装,这些将在本章中介绍。但首先,让我们看看如何构建软件。
## 制作软件
构建软件很复杂,因为源代码必须非常具体地处理才能创建一个能工作的程序——更不用说一个能良好工作的程序了!`make(1)` 程序使软件构建变得易于重复,因此程序可以按照软件作者的意图构建。`make` 从配置文件或 *makefile* 中获取指令,它告诉 `make` 如何从源代码构建程序。你不需要了解 makefile 的内部结构,所以我们不会在这里分析一个。
Makefile 包含一个或多个目标和一组执行指令。例如,输入 `make install` 告诉 `make` 检查 makefile 中是否存在名为 `install` 的过程,如果找到,则执行它。目标名称通常与 `make` 应执行的操作相关。例如,`make install` 过程通常用于安装之前步骤构建的软件。你将找到用于安装、配置和卸载大多数软件的目标,而 `make` 可以处理大量功能,其中一些功能远远超出了创建者的原始意图。
## 源代码和软件
源代码是构建构成程序的机器代码的指令,是供人类阅读的。你可能已经接触过某种形式的源代码;如果没有,去看看*/usr/src*目录下的几个文件(当然,假设你已经像我建议的那样安装了源代码,见第三章]) 这就是为什么访问源代码很重要的一个原因。
在每个系统管理员都是程序员的那些日子里,调试软件构建占据了系统管理员大部分的时间。每个类 Unix 平台都有细微(或极端)的差异。为了构建程序,系统管理员需要了解他们的平台、软件的原始平台以及两者之间的差异。构建常见程序的努力重复是真正可怕的。像`autoconf`和`configure`这样的工具旨在帮助简化这个问题,但这些程序只是掩盖了根本问题。构建许多软件包需要运行`configure`脚本的时间比实际编译所需的时间多得多。
OpenBSD Ports 和 Packages 系统消除了所有这些痛苦。
## Ports 和 Packages 系统
*Ports* 是在 OpenBSD 上可重复和一致地构建软件的一种机制。*Packages* 是针对特定 OpenBSD 版本和平台的预编译 Ports。软件包安装快速且简单,并且被 OpenBSD 团队推荐。从 Ports 安装需要更多时间和精力,但可以根据您的环境或服务器进行定制。
Ports 系统背后的基本思想是,如果源代码必须修改或调整以在 OpenBSD 上构建或运行,修改过程应该自动化。如果您需要其他软件来从源代码构建此程序或运行它,这些依赖项应该自动使用。如果您记录了软件安装的确切文件,您可以轻松地卸载它。如果您拥有所有这些,您就可以将这些软件取出来,并在任何类似的 OpenBSD 系统上安装它。
软件包是由 Ports 系统产生的可安装文件。您可以通过网络安装软件包,无论是从您自己的软件包仓库还是从 OpenBSD 镜像站点。但在您可以使用软件包之前,您必须找到它。
## 使用软件包
软件包是安装 OpenBSD 软件的首选方法。软件包由 OpenBSD 项目 Ports 团队构建,并预期无需用户进行任何特殊调整即可正常工作。当然,您必须配置软件,但软件本身应该按预期工作。除非您计划修改特定软件,否则您将非常高兴简单地安装从附近镜像获取的软件包,而不是从 Ports 构建(或者更糟糕的是,在没有 Ports 的情况下从源代码安装)。
### 软件包文件和 $PKG_PATH
每个软件包都以单个文件的形式提供,文件名以软件包所在的端口名称、版本号和 *.tgz* 扩展名命名。例如,`adsuck` 软件的 2.4.2 版本可在 *adsuck-2.4.2.tgz* 文件中找到。
在您能够安装软件包之前,您需要找到它们的来源。在官方发布 CD 或 OpenBSD 镜像站点上找到软件包文件。
软件包位于 */pub/OpenBSD/*release*/packages/*platform 的 FTP 和 HTTP 镜像目录中。例如,OpenBSD 5.3 的 amd64 平台软件包位于 */pub/OpenBSD/5.3/packages/amd64* 目录中。查看 OpenBSD 镜像列表。选择您附近的镜像服务器,并验证它实际上有您运行版本和平台的 *packages* 目录。我最近的镜像服务器是 *[`ftp10.usa.openbsd.org`](http://ftp10.usa.openbsd.org)*.^([35]) 我在 *[`ftp10.usa.openbsd.org/pub/OpenBSD/5.3/packages/amd64`](http://ftp10.usa.openbsd.org/pub/OpenBSD/5.3/packages/amd64)* 找到了 5.3 amd64 软件包。
在官方 CD 上,您可以在 */release*/*platform*/packages* 目录下找到软件包。(下载的安装 CD 不包含软件包。)如果您将 5.3 CD 挂载到 */mnt*,您将在 */mnt/5.3/amd64/packages* 下找到软件包。
一旦你选择了软件包仓库,在你的 shell 中设置 `$PKG_PATH` 变量到它。这告诉 OpenBSD 的软件包管理工具在哪里获取软件包,并为你提供了快速访问单个权威软件包源的方法。
如果你将 `$PKG_PATH` 设置为无效位置,`pkg_add`(安装软件包的命令)将无法工作。使用不同架构的软件包位置会使 `pkg_add` 报错,表示软件包“不是为正确的架构”。如果你选择了错误的版本,你会看到“主要版本错误”或其他库版本错误。这两种类型的错误都意味着你的 `$PKG_PATH` 是错误的。
你也可以列出多个软件包仓库。如果软件包工具在第一个仓库中找不到所需的软件包,它们会尝试下一个。这让你可以使用本地软件包仓库来存储自定义软件包,如果你没有本地软件包,则会回退到官方 OpenBSD 仓库。我会在必须为我的网络构建自定义软件包并希望跨多台机器使用时使用这种方法。
通过 FTP 或 HTTP 安装软件包并不像从 CD 安装那样安全。虽然 OpenBSD 发布团队已经验证了 CD 集中的所有软件包,但入侵者可能会篡改你选择的任何镜像。这些入侵会被相对较快地发现,但有可能在入侵和修复损坏之间安装软件包。如果你非常关心软件包的完整性,请获取官方 CD 集合。
### 查找软件包
当我写这篇文章时,最新的 OpenBSD/i386 快照在 FTP 站点上拥有 7485 个软件包。这是一个很长的列表,需要浏览以找到你想要的特定软件包。如果你已经安装了 ports 树,你可以用它来搜索软件包,但如果你想要使用 ports 树,你不会使用软件包,对吧?
假设你需要一款只能在 Apache 2.2 上运行的软件。你该如何找到它?在命令行中查找软件包,或者使用一个网站。
### 注意
大多数人不需要在 OpenBSD 上安装外部 Web 服务器;OpenBSD 内置的 Web 服务器对于普通用户来说已经足够好了。我只有在有专门为 Apache 2.2 编写的应用程序时才会安装 Apache 2.2。如果你想运行,比如说,一个 PHP Web 应用程序,只需使用 OpenBSD 内置的 `nginx` Web 服务器。
#### 在命令行中查找软件包
`pkg_info(1)` 显示有关软件包的信息。虽然你通常使用 `pkg_info` 来探索你已经安装的软件包,但你也可以使用 `-Q` 来对你软件包仓库中的软件包进行不区分大小写的搜索。如果你知道软件包名称的一部分,尝试进行软件包搜索。
$ pkg_info -Q apache
apache-ant-1.8.2p3
apache-couchdb-1.0.1p2
apache-httpd-2.2.22
apachetop-0.12.6
modsecurity-apache-1.9.3p5
p5-Apache-ASP-2.61p0
…
从名称中,你可以猜测软件包 `apache-httpd-2.2.22` 包含了 Apache 2.2。
#### 在网络上查找软件包
搜索包的最简单方法是使用非官方的 OpenBSD Ports 网站*[`www.openports.se/`](http://www.openports.se/)*。虽然这不是官方的 OpenBSD 网站,但它已经为 OpenBSD ports 树提供良好的接口多年。如果我在这个网站上搜索 Apache,第三个搜索结果是“www/apache-httpd, apache HTTP 服务器”。
一旦你知道包含你想要软件的包的名称,你就可以安装它。
### 安装包
使用`pkg_add(1)`安装包。你不需要版本号——只需要包名。在这里,我安装了之前找到的 Apache 包:
pkg_add apache-httpd
1 apache-httpd-2.2.22:libiconv-1.14: ok
apache-httpd-2.2.22:pcre-8.30: ok
…
2 apache-httpd-2.2.22: ok
3 The following new rcscripts were installed: /etc/rc.d/httpd2
See rc.d(8) for details.
4 --- +apache-httpd-2.2.22 -------------------
This is the official httpd distributed by the Apache Server Project,
provided as a port for those who, for various reasons, need to run
version 2.
OpenBSD provides a custom Apache server, httpd(8), in the base system
which has been audited for security and may run in a chroot(2)
environment. Users are STRONGLY encouraged to use the system httpd
rather than this port.
许多软件需要其他软件来运行,OpenBSD 的包工具跟踪这些**依赖关系**。`pkg_add`通过安装所选包的各种依赖关系来启动我的 Apache 安装,如**1**所示。Apache 2.2.22 需要`libiconv`和`pcre`等几个其他包。随着每个包的安装,你会在屏幕上看到进度条滚动。如果某个依赖关系无法安装,包安装将终止。
在安装所有依赖关系后,`pkg_add`安装实际的 Apache 2.2 包,如**2**所示。在包安装结束时,你会看到由包添加的启动脚本的通知,如**3**所示,然后是来自 OpenBSD 团队的关于包的任何注释,如**4**所示。
#### 安装了哪些文件?
使用`pkg_info`的`-L`选项来查看一个包安装了哪些文件。
$ pkg_info -L apache-httpd
Information for inst:apache-httpd-2.2.22
Files:
/usr/local/include/apache2/ap_compat.h
/usr/local/include/apache2/ap_config.h
/usr/local/include/apache2/ap_config_auto.h
/usr/local/include/apache2/ap_config_layout.h
/usr/local/include/apache2/ap_listen.h
…
如你所见,所有这些文件都安装在了*/usr/local*下。OpenBSD 将所有包都安装在了*/usr/local*下。
#### 详细安装
如果你感兴趣`pkg_add`的工作细节,使用`-v`标志来触发详细模式。你可以指定多个`-v`标志以获得更多细节。我建议尝试几次详细模式,以不同的详细程度来深入了解`pkg_add`实际上做了什么。
#### 模糊的包
有时`pkg_add`需要额外的提示来了解你想要安装的内容。例如,我的生产网络中的所有内容都与 LDAP 相关联,我需要在每个数据中心运行一个 OpenLDAP 镜像。(我可以用 OpenBSD 的集成 LDAP 守护进程代替,但主服务器运行 OpenLDAP,我不想混合 LDAP 服务器。)以下是我尝试安装 OpenLDAP 的尝试。
pkg_add openldap-server
1 Ambiguous: choose package for openldap-server
a 0:
1: openldap-server-2.3.43p10
2: openldap-server-2.4.31p0
Your choice: 2
2 Ambiguous: choose dependency for openldap-server-2.4.31p0:
a 0: cyrus-sasl-2.1.25p3
1: cyrus-sasl-2.1.25p3-db4
2: cyrus-sasl-2.1.25p3-ldap
3: cyrus-sasl-2.1.25p3-mysql
4: cyrus-sasl-2.1.25p3-pgsql
5: cyrus-sasl-2.1.25p3-sqlite3
Your choice: 2
3 Detected loop, merging sets ok
| cyrus-sasl-2.1.25p3-ldap
| openldap-client-2.4.31
openldap-server-2.4.31p0:cyrus-sasl-2.1.25p3-ldap+openldap-client-2.4.31: ok
openldap-server-2.4.31p0:db-4.6.21v0: ok
openldap-server-2.4.31p0:icu4c-49.1.2p1: ok
openldap-server-2.4.31p0: ok
The following new rcscripts were installed: /etc/rc.d/saslauthd /etc/rc.d/slapd
See rc.d(8) for details.
如**1**所示,OpenBSD 有两个 OpenLDAP 服务器包:2.3 和 2.4 版本的最新发布。我想要 2.4 版本。OpenBSD OpenLDAP 包是用 Cyrus SASL(简单身份验证和安全层)编译的,它又分为六种不同的风味,如**2**所示——每种支持的数据库对应一种。我选择使用 LDAP 作为其后端的版本。(我不需要这个特定的 SASL;任何 SASL 都足够了。)
`pkg_add` 认识到这是一个类似“先有鸡还是先有蛋”的问题。LDAP 是使用 Cyrus 编译的,但 Cyrus 是使用 LDAP 编译的。幸运的是,如您在 **3** 处所见,它知道这是一个允许的配置。依赖项被安装,然后添加了我想要的 OpenLDAP 服务器。
### 确定文件来源
如您在早期示例中看到的,许多软件包会安装其他软件包作为依赖项。一旦您安装了一些复杂的软件包,`/usr/local` 就会开始充满看起来奇怪的文件和程序。最终,您会想知道哪些软件包是必需的,或者软件包是从哪里安装的。
OpenBSD 在 */var/db/pkg* 中维护每个已安装软件包的记录,包括已安装的文件和依赖信息,但浏览这些文件似乎是一项艰巨的任务,而且我不会这么做。此外,许多软件包名称晦涩难懂,不透明,混淆或其它难以理解。(并不是 OpenBSD 软件包团队试图使软件包名称难以理解,但当软件名称像 `icu4c` 这样时,他们能做的也就这么多。)
幸运的是,`pkg_info(1)` 可以轻松回答您关于已安装软件的大部分问题。首先,使用 `-a` 参数获取机器上所有软件包的完整列表。
$ pkg_info -a
cyrus-sasl-2.1.25p3-ldap RFC 2222 SASL (Simple Authentication and Security Layer)
db-4.6.21v0 Berkeley DB package, revision 4
icu4c-49.1.2p1 International Components for Unicode
openldap-client-2.4.31 Open source LDAP software (client)
openldap-server-2.4.31p0 Open source LDAP software (server)
quirks-1.73 exceptions to pkg_add rules
tcsh-6.18.01 extended C-shell with many useful features
等一下!我当然已经安装了 `tcsh`,因为我的老脑筋学不会新的 shell。我安装了 OpenLDAP,并选择添加 `cyrus-SASL` 作为依赖项。`pkg_add` 真的安装了所有这些其他软件包作为依赖项吗?或者我的初级管理员安装了额外的垃圾?我真的*需要*所有这些软件包,还是只需要敲打一个仆人?
OpenBSD 记录了您安装的软件包,以及作为依赖项安装的软件包。使用 `-m` 标志仅显示您手动安装的软件包。
pkg_info -m
openldap-server-2.4.31p0 Open source LDAP software (server)
quirks-1.73 exceptions to pkg_add rules
tcsh-6.18.01 extended C-shell with many useful features
这看起来更熟悉。显然,其他所有东西都是依赖项。
现在让我们看看一些选项。要获取每个软件包的更详细描述,请添加 `-d` 标志或使用 `-a` 标志显示所有软件包的信息。如果您想为单个软件包运行 `pkg_info`,请使用软件包名称作为参数。例如,`-L` 显示软件包安装的文件列表。使用 `-a` 标志,它将显示所有已安装软件包中包含的所有文件,但这可能不是您想要的。要显示软件包安装的所有文件,请使用 `-L` 标志和软件包名称。
$ pkg_info -L tcsh
Information for inst:tcsh-6.18.01
Files:
/usr/local/bin/tcsh
/usr/local/man/man1/tcsh.1
/usr/local/share/nls/C/tcsh.cat
/usr/local/share/nls/de_AT.ISO_8859-1/tcsh.cat
/usr/local/share/nls/de_CH.ISO_8859-1/tcsh.cat
/usr/local/share/nls/de_DE.ISO_8859-1/tcsh.cat
…
如您所见,`tcsh(1)` 软件包包括实际的 `tcsh` 二进制文件、手册页以及一大堆国家语言支持 (NLS) 文件。给定一个软件包名称,您可以识别出哪些文件是该软件包的一部分。
反过来,有时您想知道某个特定文件是从哪里来的。例如,我偶尔会浏览我的服务器文件系统,寻找奇怪的东西。我定义“奇怪的东西”为“我不认识的东西”。如果我看到一个不熟悉的程序或文件,我会检查它是哪个软件包安装的。
$ pkg_info -E /usr/local/sbin/pluginviewer
/usr/local/sbin/pluginviewer: cyrus-sasl-2.1.25p3-ldap
cyrus-sasl-2.1.25p3-ldap RFC 2222 SASL (Simple Authentication and Security Layer)
我之前遇到的唯一`pluginviewer`是设计用来帮助 Unix 网络浏览器在网站要求插件时运行第三方软件的。我不知道这个`pluginviewer`做什么,但显然它是`cyrus-SASL`的一个合法部分。为了找到值得担心的事情,我需要继续寻找。^[[36]) 如果你进行许多此类文件搜索,可以通过使用`pkglocatedb`(*/usr/ports/databases/pkglocatedb*)来获得更快的搜索结果。
安装后,许多软件包会显示一条消息,我经常阅读并迅速忘记。要再次显示此信息,请使用带有`-M`标志的`pkg_info`。
$ pkg_info -M apache-httpd
Information for inst:apache-httpd-2.2.22
Install notice:
This is the official httpd distributed by the Apache Server Project,
…
如果你记不清哪个软件包包含了你想要的消息,请使用`-a`标志而不是软件包名称来显示所有包含该消息的软件包的消息。要显示所有不是由其他软件包所需的软件包,请使用`-t`标志,你可能认为这个标志与你要安装的所有软件包匹配。如果你没有请求安装软件包,它只能作为你请求的某物的依赖项安装,对吧?
$ pkg_info -t
apache-httpd-2.2.22 apache HTTP server
icu4c-49.1.2p1 International Components for Unicode
quirks-1.73 exceptions to pkg_add rules
tcsh-6.18.01 extended C-shell with many useful features
我知道我没有选择安装`icu4c`。请记住,我对该软件没有道德上的反对,但它并不是我请求的。一个我未选择安装且不是其他任何东西所需的软件包是如何出现在这个系统上的?
它在那里是因为我卸载了需要它的东西。
### 卸载软件包
要删除之前安装的软件包,请使用`pkg_delete(1)`。
pkg_delete openldap-server
openldap-server-2.4.31p0: ok
Read shared items: ok
--- -openldap-server-2.4.31p0 -------------------
You should also run /usr/sbin/userdel _openldap
You should also run /usr/sbin/groupdel _openldap
`pkg_delete`不会请求确认。它不会询问你是否确定。它只是将软件从磁盘上删除,然后继续其日常事务。它也不会删除为软件创建的无权限用户和组,因为你可能仍然拥有它们拥有的文件。
记住,许多软件包需要其他软件包。默认情况下,`pkg_delete`不会删除你移除的软件包的依赖项。例如,我们之前看到`icu4c`作为从移除的 OpenLDAP 服务器软件包中遗留的依赖项自动安装。要自动删除不需要的依赖项,请使用`-a`标志。例如,要完全从机器上清除`openldap-server`软件包及其基础设施,请运行`pkg_delete`两次。
pkg_delete openldap-server
pkg_delete -a
这应该会清理你的系统,移除所有作为依赖项安装的软件包。
### 软件包限制
软件包系统快速、高效、可靠,并且是 OpenBSD 项目推荐用户安装软件的方式。但该系统确实有一些限制,你应该了解,包括软件移植过程中的延迟和对旧版 OpenBSD 上较新软件包的支持。
每个 OpenBSD 版本只支持为该版本构建的软件包,并且不会为旧版本构建新软件包。发布中提供的软件包就是你将得到的所有内容。(如果你运行的是`-stable`,则对此有一些轻微的例外;参见第二十章中就建议过这种方法,但你也可以使用`cvs(1)`来获取端口树并保持文件更新,正如第二十章中所述。查看这个目录,你会找到一大堆目录和文件。
*INDEX* 文件包含系统中每个端口的列表,按字母顺序排列,但以机器可读的格式。你可以搜索这个文件以查找端口,但我建议使用稍后讨论的工具之一来这样做。
*Makefile* 包含使端口系统工作的基本机器指令。虽然它是为 `make(1)` 设计的,但通过阅读任何端口的 makefile,你可以学到很多东西。大多数真正复杂的端口代码都在 *ports/infrastructure* 目录中,而端口系统中的所有 makefile 都是基于这个基础设施构建的。
剩余的目录是软件类别。每个类别包含进一步的目录层,每个类别下的目录都是特定软件的端口。截至本文撰写时,OpenBSD 有超过 7600 个端口,因此这种层次结构对于保持它们某种可管理顺序至关重要。
例如,以下是对 *news* 目录内容的列表,该目录包含用于使用和管理 Usenet 新闻的程序。这是一个较小的类别。有些类别有数百个条目,但它们的排列方式大致相同。
CVS leafnode p5-News-Article py-yenc tin
Makefile newsfetch p5-News-Newsrc sabnzbd trn
aub nn pan sickbeard ubh
hellanzb p5-Gateway plor slrn yencode
就像主端口树中的 *CVS* 目录一样,该类别的 *CVS* 目录包含 CVS 版本控制信息,这对于日常操作并不重要。*Makefile* 包含该类别内有效端口的列表。你可以使用这个 makefile 构建该类别中的所有端口,尽管这主要在批量构建软件包时有用。(当 OpenBSD 项目团队构建端口树中的所有内容时,它使用 */usr/ports/infrastructure/bin/dpb*。)
让我们再深入一层。这里是为 `tcsh` 提供的端口,这是作为系统管理员我不可协商的要求之一:
$ ls /usr/ports/shells/tcsh
CVS Makefile distinfo patches pkg
*CVS* 目录包含版本控制信息,就像每个 *CVS* 目录一样。
*Makefile* 为在 OpenBSD 上构建 `tcsh` 提供了具体的说明,包括获取软件和任何补丁的地方,如何提取它,软件包可以从哪里分发,以及任何支持的定制。
*distinfo* 文件包含要下载的源代码的几个不同的加密散列,以避免从受损害的源代码构建软件,以及源文件的尺寸。较新的端口只包含 SHA-256 散列。
### 注意
虽然可能(困难,但可能)存在一个被篡改的文件与特定的哈希值匹配,但一个被修改的源代码文件能够与使用几种不同的算法计算出的哈希值匹配,并且与未受损害的代码具有相同的大小,这种情况极为不可能。即使人们想出如何破解特定的哈希值,使用多个哈希值和文件大小几乎使得篡改源文件变得不可能。
*patches* 目录包含使此软件在 OpenBSD 上运行的代码修改。一些端口没有补丁;其他端口有几十个。
最后,*pkg* 目录描述了软件包并列出了完整软件包必须包含的文件。
### 二级端口
一些端口包含其他端口。以下是 *emulators/fedora* 端口的内含内容。
CVS Makefile Makefile.inc base cups motif sdl
*Makefile.inc* 文件是新的,同样新的还有子目录 *base*、*cups*、*motif* 和 *sdl*。这些子目录是独立的端口。这四个端口通常一起安装,作为一个整体,支持 OpenBSD 的 Linux 仿真(在 `compat_linux(8)` 中有文档说明)。所有四个端口都调用了 *Makefile.inc* 中的公共指令。(端口树不包括这些指令中的许多,但当你发现一个时不要感到惊讶。)
### 只读端口树
构建端口的流程会创建一个可安装的软件包并使用大量临时文件、源文件和状态文件。默认情况下,所有这些文件都放置在端口树内部。虽然这样可行,但我鼓励您将 */usr/ports* 视为一个只读的 OpenBSD 目录树,就像 */usr/bin*、*/usr/lib* 等一样。这样做简化了升级和识别本地更改,有助于识别您从端口构建的内容,并节省 */usr* 分区的空间。
### 注意
端口的构建文件可以从几千字节到几个吉字节不等,因此最好在大型临时分区上构建端口。如果您有未分区的磁盘空间,创建一个仅用于构建端口的分区。或者使用任何有空间的分区,甚至是一个 NFS 分区。
通过在 */etc/mk.conf* 中设置变量来配置端口集合。要使用只读端口树,在这些目录中设置变量:
+ ****`WRKOBJDIR`****. 从源代码提取软件并编译的目录。这些目录可以根据需要删除和重新创建。
+ ****`PACKAGE_REPOSITORY`****. 存储完成软件包的目录。端口集合构建软件包,然后您可以安装它们。
+ ****`PLIST_DB`****. 存储软件打包列表的目录。
+ ****`BULK_COOKIES_DIR`****. 存储在大量构建软件包期间的状态 cookie 的目录。
+ ****`UPDATE_COOKIES_DIR`****. 存储在大量更新软件包期间的状态 cookie 的目录。
+ ****`DISTDIR`****. 存储供应商源代码的目录。通常保留源代码以供重用。
如果这些目录属于您的常规用户账户,您可以在不作为 root 用户的情况下完成大部分软件打包工作。
在一个特定的测试系统中,我在 */home* 中有数百 GB 的空闲空间,所以我选择将我的包目录放在那里。这是我的 */etc/mk.conf*:
WRKOBJDIR=/home/ports/wrkobjdir
DISTDIR=/home/ports/distdir
PLIST_DB=/home/ports/plist
BULK_COOKIES_DIR=/home/ports/bulk_cookies
UPDATE_COOKIES_DIR=/home/ports/update_cookies
PACKAGE_REPOSITORY=/home/ports/pkgrepo
ports 系统将在 */home/ports/wrkobjdir* 中构建所有内容。原始源代码文件放在 */home/ports/distdir* 中。ports 系统在 */home/ports/update_cookies* 和 */home/ports/bulk_cookies* 中维护各种记录。完成的包放入 */home/ports/pkgrepo* 中。
### 注意
如果你有一个专用的端口构建机器,可以考虑按发布版本包仓库。例如,我可能在任何给定时间运行三个版本的 OpenBSD。包构建机器始终运行最新版本,但我不想丢弃我的旧包,因此我使用一个包仓库目录,如 */home/ports/pkgrepo/5.4*,用于在 5.4 系统上构建的包。
### 查找软件
与包一样,ports 的第一个问题是找到你想要的软件。(要在漂亮的界面中随机浏览 ports 树,请参阅 *[`www.openports.se`](http://www.openports.se)* 网站。)OpenBSD 有几种方式可以搜索 ports 集合,包括 ports 索引、关键词和 SQL。
#### Ports 索引
文件 */usr/ports/INDEX* 按类别和字母顺序列出 ports 树中的所有软件。如果你对你的端口名称有一个很好的想法,你可以搜索文件以查找你首选的软件。索引以单行管道分隔符描述每个端口,就像这样:
gcpio-2.11|archivers/gcpio||GNU copy-in/out (cpio)|archivers/gcpio/pkg/DESCR|The OpenBSD ports mailing-list ports@openbsd.org|archivers|
STEM->=0.10.38:devel/gettext converters/libiconv|STEM->=0.10.38:devel/gettext|STEM->=0.10.38:devel/gettext|any|y|y|y|y
虽然 ports 树本身认为这种格式很方便,但它并不特别适合人类阅读。要将此转换为适合人类阅读的格式,请进入 */usr/ports* 并运行 **`make print-index`**。 (这个过程可能涉及数万行,因此请将其传递给分页器。)以下是同一端口的适合人类阅读的格式:
$ cd /usr/ports
$ make print-index | less
…
Port: gcpio-2.11
Path: archivers/gcpio
Info: GNU copy-in/out (cpio)
Maint: The OpenBSD ports mailing-list ports@openbsd.org
Index: archivers
L-deps: STEM->=0.10.38:devel/gettext converters/libiconv
B-deps: STEM->=0.10.38:devel/gettext
R-deps: STEM->=0.10.38:devel/gettext
…
`Port` 语句给出了端口的官方名称和被移植软件的版本。这个软件被称为 `gcpio`,版本为 2.11。`Path` 给出了 ports 树类别和端口可以找到的目录——在这种情况下,*archivers/gcpio*。`Info` 行给出了软件的非常简短的描述。这是 `cpio(1)` 的 GNU 版本。`Maint`,或维护者,是负责在 ports 树中维护此软件的个人或团体。OpenBSD ports 团队支持 `gcpio` 端口。维护得最好的端口通常有一个个人作为维护者,而不是邮件列表。
最后三个条目描述了此软件所需的其它软件。`L-deps` 行列出了共享库,`B-deps` 列出了构建此端口所需的软件,而 `R-deps` 列出了端口的运行时依赖。
这有什么好处?假设你仍然对 Apache 2 网络服务器感到困扰。你可以在 *INDEX* 中搜索以“apache”开头的端口。
$ grep -i ^apache INDEX
…
apache-httpd-2.2.20p1|www/apache-httpd||apache HTTP server|www/apache-httpd/pkg/DESCR|The OpenBSD ports mailing-list ports@openbsd.org|www net|
apr-util-*-!ldap:devel/apr-util converters/libiconv devel/pcre|STEM->=1.21:
textproc/groff|converters/libiconv|any|y|y|y|y
前三个(省略)条目与 Apache 相关,但它们不是网络服务器软件。第四行是我们的端口。
从索引中收集这些信息相当有限。如果你不知道软件的名称,或者不知道 OpenBSD 如何打包软件,你很难找到端口。在这种情况下,尝试以下讨论的其他方法之一。
#### 通过关键词查找
如果你不知道软件包的确切名称,尝试端口集合的搜索功能:`make search` 并输入一个键来扫描索引中的特定单词。要搜索与 Apache 相关的软件,尝试以下:
$ make search key=apache
在我的系统中,这会返回 62 个结果。你可能需要浏览几页的选项,但你会找到你想要的。
对于特定的软件包,你可能需要尝试几个可能的搜索关键词,因为一些关键词没有匹配项,而其他关键词则会产生过多的结果。
#### 通过 SQL 查找
`sqlports` 软件包允许你构建一个基于 *INDEX* 文件的数据库,使你能够通过 SQL 查询根据高度任意的标准搜索端口。例如,假设你想知道所有依赖于 `libiconv` 和 `expat` 的端口。在这种情况下,`sqlports` 是你的好朋友。从端口或软件包中安装它,它将自动从 *INDEX* 构建一个数据库在 */usr/local/share/sqlports*,然后使用 OpenBSD 的 `sqlite3` 查询数据库。
我在这里不会教授 SQL^([37]), 但仅作为一个例子,以下是如何使用 `sqlports`(它可以构建比这更复杂的查询)来搜索名称中包含字符串“apache”的端口:
$ sqlite3 /usr/local/share/sqlports
sqlite> select fullpkgname from ports where fullpkgname like '%apache%';
apache-couchdb-1.0.1p2
apache-ant-1.8.2p3
apachetop-0.12.6
apache-httpd-2.2.22
modsecurity-apache-1.9.3p5
p5-Apache-ASP-2.61p0
p5-Apache-DB-0.14p3
…
Apache 的 `httpd` 服务器是第四个搜索结果,但还有大约十几个端口。所有以 `p5-` 开头的名称都是 Perl 模块。
## 构建端口
你已经决定忽略 OpenBSD 团队关于使用软件包的建议,下载并提取了端口树,从端口中找到了需要安装的软件,并指定了一个构建端口的区域。现在怎么办?
端口目录不包含实际的源代码。当你从一个端口构建软件包时,系统会执行以下操作:
+ 从批准的互联网站点自动下载适当的源代码
+ 检查下载的代码是否存在完整性错误
+ 将代码提取到构建区域
+ 修补代码
+ 编译代码
+ 创建软件包
+ 安装软件包(可选)
此外,如果你添加的端口有未满足的依赖项,系统也会处理安装这些依赖项。
要使所有这些发生,只需转到 *端口* 目录并输入以下命令:
make install
你应该看到端口在构建软件,创建软件包,并在你的系统上安装软件包。
### 端口安装的作用
是时候剖析端口构建和安装过程了。以下是从端口安装 `tcsh` 的方法:
cd /usr/ports/shells/tcsh
make install
===> Verifying specs: c termlib c termlib
===> found c.65.0 termlib.12.1
===> Checking files for tcsh-6.18.01
Fetch ftp://ftp.astron.com/pub/tcsh/tcsh-6.18.01.tar.gz
tcsh-6.18.01.tar.gz 100% |****************************************************
| 905 KB 00:00
(SHA256) tcsh-6.18.01.tar.gz: OK
端口首先检查软件所需的库是否已安装。构建 `tcsh` 需要的库包括 `termlib` 和 `c` 库。端口找到了 `termlib`,但在本地系统中没有找到包含 `tcsh` 源代码的文件,因此端口会下载代码。(在构建端口时,你应该看到系统正在下载适当的源代码。)端口随后验证下载代码的校验和。如果端口无法获取所有代码,或者校验和不匹配,构建过程将停止。
一旦所有必要的源代码都已下载并验证,构建将继续进行,如下所示:
…
===> Extracting for tcsh-6.18.01
===> Patching for tcsh-6.18.01
===> Configuring for tcsh-6.18.01
Using /usr/ports/pobj/tcsh-6.18.01/config.site (generated)
configure: WARNING: unrecognized options: --disable-silent-rules
configure: loading site script /usr/ports/pobj/tcsh-6.18.01/config.site
checking for a BSD-compatible install… /usr/bin/install -c -o root -g bin
checking build system type… i386-unknown-openbsd5.2
checking host system type… i386-unknown-openbsd5.2
…
端口从压缩文件(夹)中提取源代码,应用任何针对 OpenBSD 的特定补丁,并开始构建过程。(你们中很多人都知道 `configure` 并不等于构建软件,但并非所有软件都需要 `configure` 步骤。端口知道该做什么。)
构建过程将延续多行。构建像 OpenOffice 这样的软件可能需要几天时间,并生成数十万行输出。
### 注意
如果你需要调试端口构建失败,那些从你的屏幕或终端窗口顶部滚过的消息包含了你所获得的所有线索。因此,我经常在 `script(1)` 会话中构建端口。如果你喜欢保留构建消息的想法,请查看 `script` 的手册页面以获取详细信息。
最终,你应该会看到一个消息,表明构建已完成,端口正在安装软件。
…
===> Faking installation for tcsh-6.18.01
install -c -s -o root -g bin -m 555 /home/ports/wrkobjdir/tcsh-6.18.01/tcsh-6.18.01/tcsh /home/ports/wrkobjdir/tcsh-6.18.01/fake-i386/usr/local/bin/tcsh
install -c -o root -g bin -m 444 /home/ports/wrkobjdir/tcsh-6.18.01/tcsh-6.18.01/tcsh.man /home/ports/wrkobjdir/tcsh-6.18.01/fake-i386/usr/local/man/man1/tcsh.1
install -c -o root -g bin -m 444 /home/ports/wrkobjdir/tcsh-6.18.01/tcsh-6.18.01/nls/C.cat /home/ports/wrkobjdir/tcsh-6.18.01/fake-i386/usr/local/share/nls/C/tcsh.cat
…
端口将在端口构建目录的临时位置安装软件,但这不是我们希望软件安装的地方!记住,端口系统构建软件包,然后从软件包中进行安装。这种“虚假”安装是为了构建软件包。
…
===> Building package for tcsh-6.18.01
Create /home/ports/pkgrepo/i386/all/tcsh-6.18.01.tgz
…
这就是软件包,保留在之前指定的软件包仓库中。你可能想抓取这个文件以在其他机器上安装,或者甚至通过 NFS 共享软件包仓库。
现在,因为我们已在命令行上指定了 `make install`,端口将安装创建的软件包。
…
===> Verifying specs: c termlib
===> found c.65.0 termlib.12.1
===> Installing tcsh-6.18.01 from /home/ports/pkgrepo/i386/all/
…
tcsh-6.18.01: ok
安装软件包需要执行与构建软件包时相同的检查。是的,没有那些库,端口无法构建软件包,但端口系统并不假设软件包是在本地系统上构建的。
### 端口构建阶段
软件包构建过程实际上包括几个阶段,或者构建过程的更小部分。每个阶段都会执行它之前的所有阶段。最后的阶段 `make install` 会调用所有这些阶段,这提供了几个可以在端口构建过程中干预的点。如果你想对软件包进行自定义更改,你可以在这一步进行。
让我们看看每个端口构建过程中所需的所有阶段。
#### `make fetch` 阶段
`make fetch`阶段获取端口的源代码或*distfiles*。首先,它会查找由*mk.conf*变量`$DISTDIR`指定的任何目录。如果此变量未设置,它会查找由 shell 环境变量`$DISTDIR`指定的目录。如果这两个变量都没有设置,它会查找`/usr/ports/distfiles`目录。如果`make fetch`找到了分发文件并认为它们是正确的版本,它就会将控制权交给下一个请求的阶段,然后构建继续进行。
如果源代码不在本地机器上,`make fetch`会尝试从端口 makefile 中指定的`MASTER_SITES`指定的互联网站点下载它。(你可以自定义下载位置,如自定义端口中讨论的那样。)
当你在一天中的某些时间比其他时间更容易下载时,你会发现在这些时间使用`make fetch`命令非常有用。例如,我家里有一个 T1,^([38)) 但我的雇主办公室的带宽大约是我家里的 66 倍。我可以在访问我的雇主时在我的笔记本电脑上运行`make fetch`,然后回家,在平静中构建端口。(而且老板认为我是因为他买午餐才来的。)
#### `make checksum`阶段
`make checksum`阶段验证分发文件是否未被损坏,无论是下载过程还是恶意行为。OpenBSD 为每个分发文件包含多个不同的校验和,但它只检查 SHA-256 校验和是否与分发文件匹配。如果校验和匹配,构建将进入下一个阶段。如果校验和不匹配,构建将立即中止。构建将不会继续,直到你找到一个与校验和匹配的分发文件。
并非所有软件开发者都会在更新他们的软件时更新他们分发文件的名称。对于这些软件包,端口开发者早上下载的*foo-1.0.tgz*文件可能与当天稍后你下载的*foo-1.0.tgz*文件不同。也许原始软件作者认为没有人会注意到,但 OpenBSD 的人会注意到,即使是通过端口工具中内置的逻辑。毕竟,端口系统无法区分软件作者静默修改的源文件和入侵者静默修改的源文件。如果你得到一个与记录的校验和不匹配的分发文件,请尝试通过将`REFETCH`变量设置为`true`来获取匹配的文件。
make checksum REFETCH=true
现在`make`将遍历端口中列出的所有 distfile 源,依次下载它们,以找到与端口开发者使用的匹配的分发文件。
如果你绝对确信你下载的文件是正确的,未被篡改的,但仍然无法通过`make checksum`,那么你是错误的。如果你知道自己错了,但确实想安装受损或损坏的软件,请设置环境变量`NO_CHECKSUM=yes`以跳过`make checksum`阶段。
### 警告
跳过`make checksum`阶段可能对调试是有效的,但绝对不是创建稳定、有用或安全软件包的方法。您还可能使端口号的其余部分无效。也许 OpenBSD 补丁将不再干净地应用,软件可能无法运行,或者您甚至可能安装了后门,邀请坏蛋在您的机器上存储有问题的内容。如果您坚持忽略校验和不匹配,那么您将完全独自承担风险。
#### `make prepare`阶段
在这个阶段,端口号系统进入递归。在`make prepare`时,端口号会检查构建或运行你试图构建的软件所需的任何软件。如果端口号列出了这些依赖项之一,它会检查这些依赖项是否已安装。如果依赖项未安装,此阶段将启动`make install`以安装这些所需的端口号。一旦所有必需的依赖项都安装完毕,此阶段结束。
#### `make extract`阶段
端口号系统必须在构建软件之前从 distfile 中提取源代码。源代码提取到由`$WRKOBJDIR`定义的目录中,或者在`/usr/ports/pobj/`下的一个以端口号命名的目录中。默认情况下,我的`tcsh`端口号会提取到`/usr/ports/pobj/tcsh/`下,但由于我定义了构建软件的单独位置,它是在`/home/ports/wrkobjdir/tcsh/`下构建的。
#### `make patch`阶段
包含在端口号补丁目录中的任何补丁都在`make patch`阶段应用。如果所有补丁都正确应用,此阶段结束。如果补丁未正确应用,端口号将失败。
要将您自己的补丁应用到端口号,或编译前审查代码,请先运行`make patch`。如果您首先应用补丁,可能会与端口号补丁冲突,导致编译失败,或引发其他各种问题。通过先运行`make patch`,您可以查看 OpenBSD 可以编译的代码。之后您破坏的任何内容都是您的责任。
#### `make configure`阶段
许多软件包使用`configure`脚本来准备在特定平台上编译。`make configure`命令运行该脚本。如果您想编辑`configure`脚本,请在运行此阶段之前进行编辑!如果没有`configure`脚本,端口号会静默跳过此阶段。
#### `make build`阶段
`make build`阶段编译已获取、提取、打补丁和配置的软件。如果您在端口号目录中输入`make`,端口号会调用`make build`。此阶段不会组装软件包;它只是在端口号的工作目录中执行编译并创建实际的程序二进制文件。
#### `make fake`阶段
`make fake` 阶段在子目录中安装软件,布局与在 *root* 目录下完全相同。这个虚拟根目录在工作目录中,命名为 *fake-* 并附加架构,例如 *fake-amd64*。所有将包含在包中的内容都将安装在这个目录下,具有与包中相同的所有权和权限。
#### `make package` 阶段
`make package` 阶段将端口的虚拟安装目录打包,添加打包和安装说明,并将所有内容捆绑成一个与 FTP 站点上可用的包完全相同的包。该包将存储在您之前定义的 *PKGREPO* 目录下(如果没有定义,则存储在 */usr/ports/packages*),在按架构组织的子目录中,并在按可用分发位置组织的进一步子目录中。
`make package` 意味着您可以在不安装的情况下在一个机器上构建此端口。但是,您必须安装构建依赖项来构建端口。
#### `make install` 阶段
`make install` 阶段运行 `pkg_add(1)` 来安装您编译的包。
#### `make clean` 阶段
一些包需要大量的磁盘空间。`make clean` 阶段删除所有构建文件,除了 distfile 和完成的包。
## 自定义 Ports
OpenBSD 包含各种钩子,让您可以轻松自定义获取和构建 ports 的方式。如果可能,您应该使用 OpenBSD 提供的基础设施,但在某些情况下可能无法这样做。在这里,我们将查看一些更常用的自定义设置。
### 本地 Distfile 镜像
虽然 ports 提供了几个获取源代码的地方,但您可能想覆盖这些站点。也许您与一个主要镜像站点共享网络,或者您没有无限制的互联网访问。OpenBSD 允许您设置自己的首选镜像站点。
#### 首选集合镜像
许多软件源可以被分组到 *collections* 中,这些通常一起镜像。一个例子是官方的 GNU 软件集合。GNU 镜像站点可能包含官方 GNU 集合中的所有内容。GNU C 编译器项目有其自己的软件和镜像。还有较旧的软件集合,如 SunSITE,以及较新的,如 SourceForge。
每个集合都可通过镜像站点列表获取。OpenBSD 在 */usr/ports/infrastructure/templates/network.conf.template* 中维护这些镜像站点的列表。永远不要编辑此文件;它是一个核心 ports 文件,升级会更改它。
例如,这里是一个较小项目 BerliOS 的镜像列表:
…
MASTER_SITE_BERLIOS+=
http://download.berlios.de/
http://download2.berlios.de/
http://spacehopper.org/mirrors/berlios/
…
几个 ports 想从主要的 BerliOS 下载站点获取与 BerliOS 相关的软件。OpenBSD 端口开发者已经确定了三个理想的镜像,如变量 `MASTER_SITE_BERLIOS` 中列出。
但假设你有一个离你更近的 BerliOS 镜像。可能它不是一个官方镜像,或者你已经设法获得了对非公开镜像的访问权限。它更近,更快,你更愿意使用它。OpenBSD 在默认镜像列表之前查看 */usr/ports/infrastructure/db/network.conf*。你可以将默认镜像列表复制到该文件并编辑它,但这样在升级期间就需要手动同步更改。这是工作量,因此从道德上讲是有问题的。相反,只在 *network.conf* 中添加条目,并包含默认的 *network.conf.template*。
假设你有一个位于 *[`www.blackhelicopters.org/berlios/`](http://www.blackhelicopters.org/berlios/)* 的私有 BerliOS 镜像。你会创建一个类似这样的 *network.conf* 文件:
MASTER_SITE_BERLIOS+=
http://www.blackhelicopters.org/berlios/
.include "../templates/network.conf.template"
在 *network.conf* 和 *network.conf.template* 中使用的 `+=` 表示“将此值添加到某个变量”。更理想的镜像排在列表前面。此 *network.conf* 条目将私有镜像添加到变量 `MASTER_SITE_BERLIOS`,然后调用 *network.conf.default*,它追加所有其他镜像。最终结果是 BerliOS 镜像列表将包含四个镜像:你首选的镜像排在第一位,默认的 OpenBSD-批准的镜像排在后面。如果一个文件在某个镜像上不存在,端口将按顺序尝试其他镜像。
我以 BerliOS 为例,因为它有一个小的镜像列表,但同样适用于 OpenBSD 识别的任何其他软件集合。目前可用的其他集合在 表 13-1 中显示。
表 13-1. 表 13-1:一些软件集合
| 集合 | 描述 |
| --- | --- |
| `MASTER_SITE_GNU` | GNU 项目的软件 |
| `MASTER_SITE_GCC` | GCC 项目的软件 |
| `MASTER_SITE_XCONTRIB` | 对 X Window 系统的贡献 |
| `MASTER_SITE_R5CONTRIB` | 较旧的 X Window 系统贡献 |
| `MASTER_SITE_SUNSITE` | Sun 软件集合 |
| `MASTER_SITE_SOURCEFORGE` | 由 SourceForge 托管的软件 |
| `MASTER_SITE_SOURCEFORGE_JP` | 日本 SourceForge 镜像 |
| `MASTER_SITE_GNOME` | Gnome 项目的软件 |
| `MASTER_SITE_PERL_CPAN` | 最大的 Perl 模块集合 |
| `MASTER_SITE_TEX_CTAN` | TeX 排版软件 |
| `MASTER_SITE_KDE` | 与 KDE 相关的软件 |
| `MASTER_SITE_SAVANNAH` | 由 FSF 托管的软件开发 |
| `MASTER_SITE_AFTERSTEP` | 与 AfterStep 窗口管理器相关的软件 |
| `MASTER_SITE_WINDOWMAKER` | 与 Window Maker 窗口管理器相关的软件 |
| `MASTER_SITE_FREEBSD_LOCAL` | 由 FreeBSD 项目分发,但未包含在 FreeBSD 中的软件 |
| `MASTER_SITE_PACKETSTORM` | Packet Storm 集合中的安全软件 |
| `MASTER_SITE_APACHE` | Apache 基金会的软件 |
| `MASTER_SITE_BERLIOS` | BerliOS Linux 项目的部分 |
| `MASTER_SITE_MYSQL` | MySQL 项目的软件(Oracle) |
| `MASTER_SITE_PYPI` | Python 软件 |
| `MASTER_SITE_RUBYGEMS` | Ruby 模块 |
| `MASTER_SITE_NPM` | JavaScript 软件包 |
| `MASTER_SITE_ISC` | 来自互联网软件联盟的软件 |
如果你大学数据中心有一个 Debian 镜像,请在*network.conf*中列出它。如果它出现在列表的后面,因为它在*network.conf.template*中列出,那又如何呢?要么 distfile 在那里,这样你就可以节省时间和带宽,要么 distfile 不在那里,这样你就在第二次检查本地镜像时浪费了 50 毫秒。
#### 回退镜像
OpenBSD 支持两个回退镜像。如果所有其他 distfile 源都失败,你可以检查 OpenBSD 或 FreeBSD 镜像中的文件。OpenBSD 和 FreeBSD 都倾向于为活跃的 ports 镜像 distfiles。这不是首选的,因为如果每个人都这样做,就会占用项目分发他们自己的软件所需的带宽。但如果你绝望了,请在*network.conf*中将`MASTER_SITE_OPENBSD`和/或`MASTER_SITE_FREEBSD`设置为`YES`。
#### 主要镜像
你可以让 ports 系统首先检查特定站点上的所有 distfiles,无论端口中列出的下载站点是什么。也许你有一个本地镜像,你在那里存放了大量 distfiles,或者你自动将 distfiles 从你的 ports-building 机器加载到一个中央位置。在*network.conf*中使用变量`MASTER_SITE_OVERRIDE`来定义这个站点。
MASTER_SITE_OVERRIDE=ftp://ftp.mycompany.com/distfiles
### 注意
我已经多次构建了本地的 distfile 镜像,通常是在开始新工作的时候。我设法维持这个镜像大约六个月,然后由于其他任务优先级更高,镜像就变得过时了,所以我不太推荐这种做法。但如果维护本地 distfile 镜像能减轻你的工作量而不是增加,那就请享受吧。
### 版本
一些 ports 可以通过*flavors*创建多个但略有不同的包。我一直在举例的 Apache 2.2 网络服务器,可以带或不带 LDAP 支持来构建,可选 X 支持的程序也是如此。Shell 可以构建为动态或静态版本。OpenBSD 的官方包使用最常用的选择构建,但这些替代方案是合理且偶尔必要的。
要识别端口支持的版本,请转到端口目录并运行**`make show=FLAVORS`**。以下是检查流行文本编辑器 Vim 版本的方法:
cd /usr/ports/editors/vim
make show=FLAVORS
huge gtk2 athena motif no_x11 perl python ruby
你可以猜到这些八个版本中的一些功能,但你如何了解其他的呢?你可以检查包的描述文件,以获取每个版本的简要描述。以下是 Vim 版本的描述,来自*editors/vim/pkg/DESCR-main*:
…
Flavors:
gtk2 - build using the Gtk+2 toolkit (default);
motif - build using the Motif toolkit; athena - build
using the Athena toolkit;
…
Motif?我记得 Motif。现在我打算再次努力忘记它。但如果你想在 Vim 版本中支持 Motif,那就去做吧。
以我正在进行的例子为例,以下是 Apache 2 的版本:
cd /usr/ports/www/apache-httpd
make show=FLAVORS
ldap
我使用 LDAP 将网站连接到我的中央认证系统。如果我的 web 服务器上可以启用 LDAP 认证,我就想用它。
#### 构建风味端口
使用`$FLAVOR`环境变量定义任何所需的口味,但不要在*.profile*或*.cshrc*文件中定义,因为如果请求一个未识别的风味,端口将不会构建。在构建端口时定义它。例如,仍然在*apache-httpd*目录中,我运行这个命令:
env FLAVOR="ldap" make package
===> Checking files for apache-httpd-2.2.20p1-ldap
Fetch http://www.reverse.net/pub/apache/httpd/httpd-2.2.20.tar.gz
…
===> apache-httpd-2.2.20p1-ldap depends on: openldap-client-* - not found
===> Verifying install for openldap-client-* in databases/openldap
…
通过在命令行上定义风味,端口知道要检查构建 Apache 所需的 OpenLDAP 客户端。当构建完成时,你应该得到一个带有风味附加的包文件——在这个例子中,*apache-httpd-2.2.20p1-ldap.tgz*。
#### 风味和依赖
当构建一个*风味端口*时,风味不会传播到依赖项。你需要检查风味端口的依赖项,看看它们是否也需要风味。例如,我的风味 Apache 包调用 OpenLDAP 客户端,它没有风味,但 OpenLDAP 调用`cyrus-SASL`,如果检查该端口,我会看到这个:
cd /usr/ports/security/cyrus-sasl2
make show=FLAVORS
db4 ldap mysql pgsql sqlite3
Cyrus SASL 有 LDAP 风味,但定义我想以 LDAP 风味构建 Apache 并不意味着`cyrus-SASL`也会以 LDAP 支持构建。如果我在这个依赖项中需要 LDAP 支持,我必须单独构建它。我不需要它在我的环境中,所以我不需要麻烦,但在构建你的包时检查这些潜在问题。
如果你决定以某种风味重新构建一个依赖的端口,请确保之后重新构建所有依赖于该端口的端口。确保你的包使用`print-build-depends`和`print-run-depends`目标有正确的依赖。在这里,我看到我需要为我的风味 Apache 2 构建哪些端口:
env FLAVOR="ldap" make print-build-depends
This port requires package(s) "metaauto-1.0 gperf-3.0.4 libiconv-1.14 gettext-0.18.1p1 gmake-3.82p1 groff-1.21p8 pcre-8.30 help2man-1.29p0 autoconf-2.65 autoconf-2.68 cyrus-sasl-2.1.25p3 icu4c-4.8.1.1p0 db-4.6.21v0 openldap-client-2.4.31 apr-1.4.6 apr-util-1.4.1-ldap" to build.
我可以检查这些端口的每个风味。
#### 构建多个风味
你可以在同一系统上构建一个端口的多个风味。每个包文件名都包含风味,因此你可以拥有 Vim 的 Motif 和 GTK2 版本的包。仔细检查依赖关系,以验证每个都是使用正确的风味构建的。对于具有风味依赖的包,我建议移除所有风味依赖并重新构建它们,以确保一切都能获得正确的风味。
#### 卸载和重新安装风味端口
风味包会改变其名称。我不能运行`pkg_delete apache-httpd`,因为它没有安装。查询系统以查找你手动安装的包,你会看到这个:
pkg_info -m
apache-httpd-2.2.20p1-ldap apache HTTP server
…
当使用此包时,你必须指定风味。
pkg_delete apache-httpd-2.2.20p1-ldap
apache-httpd-2.2.20p1-ldap: ok
…
类似地,要重新安装风味包,指定风味包文件。
## 子包
一些端口包含多个截然不同的软件包。这并不像向 Apache 添加 LDAP 支持或向 Vim 添加 Motif 支持那样——那些是对现有软件包的修改,而不是截然不同。一些端口创建了两个完全不同的软件包,例如数据库客户端和相关的数据库服务器。我在本章的示例中引入了 OpenLDAP,OpenLDAP 服务器和客户端都来自同一个端口:*databases/openldap*。其他应用程序可能有插件来访问多个不同的数据库引擎。这些被称为*子软件包*或*多重软件包*。
与风味不同,OpenBSD 提供了一个端口的全部子软件包。你可以从官方软件包中安装 OpenLDAP 的服务器和客户端版本。当端口构建时,所有子软件包都会构建。在软件包捆绑阶段,软件包被分割成子软件包。
要查看一个端口支持的所有子软件包,请运行以下命令:
cd /usr/ports/databases/openldap
make show=MULTI_PACKAGES
-main -server
此端口有两个子软件包:`openldap-main`和`openldap-server`。
你如何了解每个子软件包包含的内容?与风味一样,你可以检查其描述文件,它是*pkg/DESCR*。OpenLDAP 包括*pkg/DESCR-server*和*pkg/DESCR-main*。阅读这些文件显示,`main`软件包是客户端,正如你所期望的那样。
如果你在一个端口目录中运行`make install`,你会得到端口的主体版本——在这个例子中,是 OpenLDAP 客户端。OpenLDAP 客户端的数量超过了服务器,这也是你所期望的。要构建不同的子软件包,在命令行上设置环境变量`SUBPACKAGE`,就像你为风味所做的那样。
env SUBPACKAGE="-server" make package
这将构建`-server`版本。务必包含前面的连字符,因为指定一个不存在的子软件包会导致构建失败。
## 软件包和 rc.d 脚本
第五章介绍了如何让 OpenBSD 启动打包的软件,但让我们快速回顾一下。当你安装一个可以在启动时启动的软件包时,该软件包也会在*/etc/rc.d*中安装一个启动脚本。如果我安装 OpenLDAP 服务器,软件包安装将报告:
…
The following new rcscripts were installed: /etc/rc.d/slapd
要在启动时启动`slapd(8)` OpenLDAP 服务器,请将脚本名称添加到*/etc/rc.conf.local*中的`pkg_scripts`变量。
pkg_scripts="slapd"
OpenBSD 在启动时按顺序运行这些脚本,在关机时按相反顺序运行。
要从默认值更改软件包的命令行参数,请向*/etc/rc.conf.local*中的*`command`*`_flags`变量添加一个*`command`*`_flags`变量。不要编辑启动脚本。
slapd_flags="-u _openldap -6 -l local0"
你现在可以以任何你需要的方式管理你的附加软件。
现在让我们继续配置 OpenBSD 的集成软件,通过*/etc*中的文件。
* * *
^([34]) 在 IT 行业,“最小教育”意味着愿意深入研究并找出答案,再加上几年的大学或专业经验;访问编程教科书或其他教育材料;或者大量的青春、固执和动力。
^([35]) 不,不是的。没有 *ftp10.usa.openbsd.org*。遵循指示。查看镜像列表,选择一个实际存在且离你较近的镜像。永远不要盲目复制我的示例!
^([36]) 如果你在任何服务器上都没有看到任何需要担心的事情,那么你还没有足够仔细地查看。
^([37]) 这个例子耗尽了我对 SQL 的理解。只要我保持对数据库的无知,人们就不会期待我帮助他们修复数据库。
^([38]) 别笑。这是有偿的。
## 第十四章。一切关于 /etc
*你处于一个迷宫中*
*错综复杂的配置;*
*没有两个是相同的。*
 当我被分配到一个不熟悉的服务器时,我首先做的事情是寻找前系统管理员的文档。当我发现没有时,我就研究 */etc* 目录,因为它包含类 Unix 系统的基本配置。从初级系统管理员到中级系统管理员的最快方式是阅读 */etc* 和相关的 man 页面——所有文档。是的,这是一大堆阅读。
一旦你理解了 */etc*,你就理解了系统是如何组合在一起的。你必须学习所有这些,但你不妨走一条更简单的路线,提前学习而不是在一系列无计划的绝望故障排除会议中进行学习。(我已经在前面的章节中讨论了许多相关的 */etc* 文件,例如 第八章 中的 */etc/fstab* 和 第十一章 中的 */etc/services*。)
你会发现 */etc* 中的一些文件只有历史意义,或者正在逐渐消失。我将简要讨论它们的功能,但不会花太多时间在过时的文件上。我也不会花太多时间在只有边缘情况(与不常用或只在非常特殊情况下使用的软件相关)中才有用的文件上。另一方面,我将深入探讨那些尚未在其他地方找到位置的 重要 */etc* 文件。
## /etc 在 Unix 变体中的使用
不同的类 Unix 系统使用不同的 */etc* 文件。在许多商业变体中,这些文件只是原始 BSD 的重命名或重构文件。
例如,我第一次管理 IBM AIX 系统时,需要知道哪些磁盘应该挂载在哪里,但那里没有 */etc/fstab*。一点搜索后,我找到了 */etc/filesystems*,这是 IBM 的 */etc/fstab*。显然,IBM 的人认为名为“文件系统表”的文件很令人困惑,所以给了它一个不同的名字。知道这些信息存在于 */etc* 中,并且知道哪些文件具有无关的目的,大大缩短了我的搜索时间。
对 */etc* 的更改可以显著改变系统的性能。虽然手动恢复混乱的文件系统可能会将一个合格的系统管理员推向成为一个相当不错的系统管理员,但这并不是到达那里的最愉快的方式。
## /etc 文件
现在我们将按字母顺序查看每个 */etc* 文件。我们将从 */etc/adduser.conf* 开始,以 */etc/ypldap.conf* 结束。
### /etc/adduser.conf
*/etc/adduser.conf* 文件包含 `adduser(8)` 的持久配置,第六章 详细介绍了它。要更改 `adduser` 的设置,请编辑此文件。此文件是自文档化的,如果你已经阅读了 第六章,你应该不会遇到任何问题。
### /etc/amd
自动挂载守护进程在请求时自动加载 NFS 文件系统。如果您对此功能感兴趣,请阅读 `amd(8)` 手册页。
自动挂载守护进程并不像您想象的那么有用。正如文档所说,“丰富的想象力对于充分利用所有功能最有用。”
### /etc/authpf
The */etc/authpf* 目录包含认证数据包过滤器配置。如果您对为防火墙访问进行用户认证感兴趣,我推荐 Peter Hansteen(No Starch Press,2010 年)所著的《PF 权威指南》(第 2 版)。
### /etc/bgpd.conf
OpenBSD 包含一个 BGP 守护进程,`bgpd(8)`。大多数系统管理员永远不会接近 BGP,但如果你需要它,请参阅 `bgpd.conf(5)` 手册页。
### /etc/boot.conf
The */etc/boot.conf* 文件控制系统的引导过程,如第五章引导过程所述。
### /etc/changelist
*/etc/changelist* 是一个文本文件,其中包含由每日安全检查脚本 */etc/security* 检查的文件列表。我们将在第十五章系统维护中详细讨论这些任务。
### /etc/chio.conf
`chio(1)` 中间介质更换器允许您管理类似 jukebox 的媒体数组,例如 CD 和磁带更换器。如果您有一个可以独立在多个磁带之间切换的磁带备份单元,`chio` 就是您的朋友。在 */etc/chio.conf* 中配置 `chio`。大多数 OpenBSD 用户没有介质更换器,但如果您感兴趣,请参阅 `chio(1)` 的手册页。
### /etc/csh.*
The */etc/csh.** 文件包含 `csh(1)` 和 `tcsh(1)` 的系统默认设置。当用户使用这两个 shell 之一登录时,shell 会执行 */etc/csh.login* 中列出的任何命令。同样,当用户注销时,shell 会执行 */etc/csh.logout* 中的任何命令。将一般 shell 配置放在 */etc/csh.cshrc* 中。
### /etc/daily 和 /etc/daily.local
The */etc/daily.local* 脚本每天运行以维护系统。有关详细信息,请参阅第十五章系统维护。
### /etc/dhclient.conf
您可以通过使用 `dhclient(8)` 来配置 OpenBSD 的 DHCP 客户端,如第十二章连接到网络所述。
### /etc/dhcpd.conf
OpenBSD 包含一个安全的 DHCP 服务器。它最初是 Internet Systems Consortium (ISC) DHCP 服务器,但已被 OpenBSD 团队反复重写和简化。有关详细信息,请参阅第十六章网络服务器。
### /etc/disklabels/
The */etc/disklabels/* 目录传统上包含磁盘标签的备份副本,如第八章磁盘和文件系统所述。实际上很少系统管理员使用它,因为很少的系统管理员知道它的存在。OpenBSD 将所有系统磁盘的磁盘标签作为关键文件的每日备份的一部分进行复制(请参阅第十五章系统维护)。
### /etc/disktab
几十年前,硬盘是昂贵且珍贵的设备,种类只有寥寥几种。而现代硬盘可以告诉计算机它们的几何形状,但老式硬盘需要手动配置,同样,老式的可移动媒体,如 1.44MB 软盘和 Zip 盘也是如此。如果你想要访问这些古老的硬盘,你需要查看 */etc/disktab* 中的信息。(但如果你需要使用这些老硬盘,你几乎肯定是在解决错误的问题。)
### /etc/dumpdates
*/etc/dumpdates* 文件记录了最后一次成功的 `dump(8)` 备份的日期。`dump` 工具备份文件系统,而不是像 `tar(1)` 那样只备份文件系统上的文件。dump 包括文件标志和文件系统的其他特殊特性。
### /etc/dvmrpd.conf
OpenBSD 使用 `dvmrpd(8)` 实现 Distance Vector Multicast Routing Protocol (DVMRP),配置在 *dvmrpd.conf* 中。这是 OpenBSD 表现良好的另一个边缘情况,但对只有少数用户感兴趣。如果你对 DVMRP 感兴趣,请参阅 `dvmrpd(8)` 的手册页面。
### /etc/exports
NFS 服务器在 */etc/exports* 中列出它们导出的文件系统以及谁可以访问它们。有关将 NFS 用作客户端和服务器的信息,请参阅 第九章。
### /etc/fbtab
OpenBSD 可以根据用户登录的方式更改系统文件和设备的所有权。当用户通过文本控制台登录时,`login(1)` 会更改控制台的所有权,以及键盘和鼠标的所有权,变为该用户。这样,用户就可以根据他们的偏好配置键盘或鼠标。通过 X 会话登录的用户也需要类似的变化。
你可以使用 */etc/fbtab* 来更改其他设备或文件的特殊需求下的权限,但这确实很棘手,通常意味着你正在使事情变得比实际需要复杂得多。如果你正在考虑这种方法,重新思考你的问题。
### /etc/firmware
一些硬件需要软件来运行。在过去,这种软件会被加载到设备本身的可编程只读存储器(PROM)芯片上。网络卡通常有引导 PROM,允许它们从网络获取配置信息。如今,PROM 并不昂贵,但制作数百万件东西会迅速累积成本。硬件供应商现在通常要求操作系统为他们将这种称为 *firmware* 的软件加载到硬件上。
固件通常是一个封闭源代码的二进制对象,或称为 *blob*。虽然 OpenBSD 团队成员永远不会接受将 blob 加载到内核中,但他们愿意将文件交给硬件。无论硬件是从操作系统还是从连接到硬件的 PROM 芯片中获取 blob,它仍然是仅在设备上运行的软件,而不是在操作系统中运行的。
为了方便用户,OpenBSD 分发了它合法允许提供的固件。如果 OpenBSD 无法在安装介质上分发固件,`fw_update` 脚本将使用包系统从互联网上获取它。无论固件来自何处,它都存储在 */etc/firmware* 中。
默认情况下,`fw_update` 在 OpenBSD 安装或升级后的第一次启动时运行,但你可以随时运行它(它存储在 */usr/sbin* 中)。
### /etc/fonts/
*/etc/fonts/* 目录包含 Xenocara X11 字体。我们将在 第十七章 中讨论 Xenocara 和 X11。
### /etc/fstab
文件系统表列出了系统所知的所有文件系统,无论它们是在启动时自动挂载还是保留在备用中。有关详细信息,请参阅 第八章。
### /etc/ftpchroot
在通过 FTP 登录时,列出在 */etc/ftpchroot* 中的用户会自动被 `chroot` 到他们的家目录中。有关完整详细信息,请参阅 `ftpd(8)`。
尽量避免使用 FTP,因为它以明文形式传输用户名和密码。请使用 `scp(1)` 或 `sftp(1)` 代替。
### /etc/ftpusers
*/etc/ftpusers* 文件所做的正好与文件名所暗示的相反,但这种情况已经持续了几十年,所以不必担心。
在 */etc/ftpusers* 中列出的任何用户都无法通过 FTP 登录。有关详细信息,请参阅 `ftpd(8)`,但再次强调,请使用 `scp(1)` 或 `sftp(1)` 而不是 FTP,尤其是在执行管理任务时!
### /etc/gettytab
*/etc/gettytab* 文件包含登录终端的配置信息。Unix 类系统传统上可以通过任何东西访问,从无数的略有不同的串行控制台到网络上的安全外壳 (SSH) 会话。如果你需要使用非标准终端类型,请探索 */etc/gettytab*。
没有必要删除此文件。毕竟,可能有人还在使用 1200 波特率的插孔式终端。但也不再有任何理由修改它。无论如何,阅读手册页是值得的,尤其是如果你读到最后一页。
### /etc/group
*/etc/group* 文件控制每个用户账户属于哪个组。用户组在 第六章 和 第七章 中有介绍。
### /etc/hostapd.conf
OpenBSD 可以充当无线接入点。主机接入点守护进程 (`hostapd(8)`) 允许 OpenBSD 执行一些在大型环境中有用的复杂接入点任务。如果你使用无线网络,OpenBSD 的无线服务很有趣;请阅读 `hostapd(8)` 和 `ifconfig(8)` 以获取完整详细信息。
### /etc/hostname.*
*/etc/hostname.* 文件配置网络接口,如 第四章 简要讨论和 第十二章 详细讨论。
### /etc/hosts
*/etc/hosts* 文件包含 IP 地址和主机名的手动维护映射。有关详细信息,请参阅 第十二章。
### /etc/hosts.equiv
*/etc/hosts.equiv* 文件被各种 r 工具使用,例如 `rcp(1)`、`rlogin(1)` 和 `rsh(1)`。这些工具和底层协议是早期时代的遗物,当时互联网的安全性并不是一个如此大的担忧。OpenBSD 已不再包含 `rlogin(1)` 客户端。r 工具在很大程度上被 SSH 取代。今天,在任何合理的情况下,您都不应使用 r 协议。
*/etc/hosts.equiv* 文件不应包含任何未注释的条目,除非您明确地将它们放入其中。这是我唯一可以合理地说每个系统管理员都应该验证为空且标记为不可变的系统文件(请参阅 第十章)。
“合理情况下”和“使用 r 协议”是什么意思?几年前,我在一个网络上工作,该网络有古老的但至关重要的 VMS 服务器,只能通过 r 协议进行管理,但那些情况并不合理。如果您发现自己处于类似的困境中,请查看 `hosts.equiv(5)`、`rshd(8)` 和 `rsh(1)`。并且做些关于您情况的事情。
### /etc/hosts.lpd
您可以将 OpenBSD 系统配置为接受来自其他机器的打印请求并将它们发送到本地连接的打印机。这在打印机体积大、价格昂贵的时候非常重要,但如今已不那么重要。此文件列出了可能使用本地系统行打印机守护进程 `lpd(8)` 的主机名或 IP 地址。
如果您真的想使您的 OpenBSD 系统能够打印,请查看 `lpd(8)` 和 */etc/printcap*。
### /etc/hotplug/
OpenBSD 可以通过 `hotplugd(8)` 在将设备插入系统时自动采取行动。例如,如果您将数码相机连接到 USB 端口,`hotplugd` 可以运行一个脚本,将设备节点附加到目录并使其对您的用户账户可读。
当设备连接时,`hotplugd` 运行 */etc/hotplug/attach* 脚本,当设备被移除时运行 */etc/hotplug/detach* 脚本。这些脚本必须仔细编写,以匹配要连接和断开的设备。有关此工作方式的详细信息,请参阅 `hotplugd(8)`。
### /etc/ifstated.conf
接口状态守护进程 `ifstated(8)` 监控网络条件,并在指定事件发生时采取行动。例如,您可以配置 `ifstated` 以监视另一台服务器崩溃,当该服务器失败时,启动本地 Web 服务器。有关使用 `ifstated` 的更多信息,请参阅 Hansteen 的 *PF 之书*。
### /etc/iked/、/etc/iked.conf、/etc/ipsec.conf 和 /etc/isakmpd
*/etc/iked/*、*/etc/iked.conf*、*/etc/ipsec.conf* 和 */etc/isakmpd* 文件管理 OpenBSD 对 VPN 的 IPsec 标准的实现。OpenBSD 具有非常健壮的 IPsec 实现,并且实际上被各种互操作性小组用于测试。
您可以用 VPN 技术使用的缩写填满整本书,更不用说实际配置它们的说明。有关详细信息,请参阅手册页。
### /etc/inetd.conf
互联网小服务监听器`inetd(8)`连接到多个网络端口,用于不需要持续运行的程序。当其中一个端口的请求到来时,`inetd`激活正确的程序来处理请求。第十六章更详细地讨论了`inetd(8)`。
### /etc/kbdtype
*/etc/kbdtype*文件包含一行,其中包含您首选的终端键盘映射。使用`kdb(1)`测试您的键盘映射,然后将该键盘映射放入*/etc/kbdtype*。
### /etc/kerberosV/
*/etc/kerberosV/*目录包含 Kerberos 集中认证系统的配置。我建议每个网络都采用某种形式的集中登录。正确配置 Kerberos 是一个大主题。如果您感兴趣,请查阅`kerberos(1)`。
### /etc/ksh.kshrc
*/etc/ksh.kshrc*是 OpenBSD 附带公共领域 Korn shell 的全局配置文件。用户必须将其包含在他们的个人*.kshrc*文件中,以便这里的设置生效。有关详细信息,请参阅`ksh(1)`手册页。
### /etc/ldap/ 和 /etc/ldapd.conf
OpenBSD 包括一个轻量级目录访问协议(LDAP)守护进程。LDAP 通常用于集中认证、地址簿和其他读多写少的数据库操作。截至本文撰写时,`ldapd(8)`的功能尚不完整,但它足以提供一个集中的认证点。
### /etc/localtime
*/etc/localtime*文件是实际时区文件的符号链接。要更改时区,请更改符号链接。第四章涵盖了时区。
### /etc/locate.rc
*/etc/locate.rc*文件控制`locate(1)`数据库的创建。OpenBSD 每周更新 locate 数据库,如第十五章所述。
### /etc/login.conf
通过*/etc/login.conf*,您可以控制用户账户登录行为,如第六章所述。
### /etc/lynx.cfg
OpenBSD 包括`lynx(1)`文本模式网页浏览器。Lynx 可以无限配置,*/etc/lynx.cfg*中的设置会影响系统上的所有 Lynx 用户。您可以通过在这里配置代理服务器设置来节省很多麻烦。
### /etc/magic
许多文件包含特定于其类型的“魔数”和其他特征。`file(1)`使用这些魔数来识别文件类型,其中*/etc/magic*是魔数的索引。请不要手动编辑*/etc/magic*,因为它是由编译`file(1)`自动生成的。
### /etc/mail/
*/etc/mail/* 目录包含 OpenBSD 邮件软件的配置文件。OpenBSD 包含两个邮件包:老式的动力马车 Sendmail 和 OpenBSD 创建的 `smptd(8)`。`smptd` 并不完全适合生产使用,但到这本书上架时可能就准备好了。
此目录还包含 */etc/mail/aliases*,这是一个邮件重定向列表。确保你将 root 账户的电子邮件别名设置为你实际会阅读邮件的地方,如 第四章 中所述。
### /etc/mail.rc
*/etc/mail.rc* 文件与作为邮件服务器的发送或接收邮件无关。它是 `mail(1)` 邮件客户端的全局配置文件。虽然更高级的邮件客户端几乎完全取代了 `mail`,但仍有必要对其进行探索,因为几乎任何类 Unix 系统都会安装它。
### /etc/mailer.conf
传统上,任何类 Unix 操作系统可用的唯一邮件服务器程序是 Sendmail。因此,大量附加软件都期望找到 */usr/sbin/sendmail* 并期望它以某种方式运行。尽管按照现代标准,Sendmail 和整个简单邮件传输协议 (SMTP) 都显得复杂而奇特;软件仍然期望找到它。
更糟糕的是,`sendmail(8)` 的行为取决于它被调用的名称。像 `send-mail`、`mailq` 和 `newaliases` 这样的程序都是戴不同帽子的 Sendmail。如果你通过运行 `mailq` 命令来调用 Sendmail 程序,它的运行方式与通过运行 `newaliases` 命令来调用它的方式不同。然而,它们都指向磁盘上的同一二进制文件。第三方软件也期望找到所有这些名称,并且这些命令按要求运行。
Sendmail 是如此的标准,以至于新邮件服务器程序的编写者被迫将它们命名为 `sendmail` 并使它们的行为与 Sendmail 完全一致,以保持与庞大的已安装基础的兼容性。这使得使用替代邮件客户端变得混乱。此外,OpenBSD 将 Sendmail 作为基础系统的一部分。你不能简单地删除 Sendmail 并继续使用。升级会重新安装品牌名称的 Sendmail。
OpenBSD 通过消除 */usr/sbin/sendmail* 作为实际邮件程序来绕过所有这些混乱。相反,`sendmail` 程序只是一个调用实际邮件处理软件的包装器。*/etc/mailer.conf* 中的条目只是经典 Sendmail 程序名称的列表,以及要运行的实际程序的路径。邮件处理程序 `sendmail` 实际上安装为 */usr/libexec/sendmail/sendmail*,例如。
要运行替代邮件服务器,给 */etc/mailer.conf* 提供预期的命令名称和所有适当程序的完整路径。当你从软件包安装新的邮件传输代理时,这会自动发生。
### /etc/man.conf
*etc/man.conf* 文件告诉 `man(1)` 如何查找和显示手册页。如果你在非标准位置安装软件,请将软件的手册页信息添加到 *man.conf* 中,以便你可以透明地调用这些手册页。此文件有几种类型的条目,每种条目都由关键字或部分名称分隔。
你为什么要这样做?如果你必须从除 ports 或 packages 之外的其他来源安装软件,你可以将其放置在 OpenBSD 管理之外的目录树中,以减少升级或添加软件时的混淆。
例如,我偶尔会在 OpenBSD 桌面上安装 Wireshark。OpenBSD 团队决定移除 OpenBSD 的 Wireshark 软件包,因为它有一个如此不稳定的网络安全历史。如果我将 Wireshark 安装为 */usr/local/bin*,它就会与我的软件包混合在一起。然而,如果我将 Wireshark 安装为 */usr/local/wireshark/bin*,它就与软件包明显区分开来。但是,我无法访问手册页,因为 `man(1)` 不认识 */usr/local/wireshark/man*。^([39]) 让我们来看看如何为 Wireshark 添加手册页访问。
#### 添加到搜索索引
`_whatdb` 关键字给出 `whatis(1)` 数据库的完整路径,由 `apropos(1)` 和 `whatis(1)` 使用,允许你轻松搜索和交叉索引手册页。*man.conf* 文件包含 */usr/local/man*、*/usr/share/man* 和 */usr/X11R6/man* 中的数据库条目。以下是添加 */usr/local/wireshark/man* 的条目方法:
_whatdb /usr/local/wireshark/man/whatis.db
现在我们使用 `makewhatis(8)` 创建一个新的数据库。当你安装软件和每周维护期间,这个任务会自动运行。
#### 添加到手册页目录
手册页散布在系统中的各个目录中。`_default` 关键字告诉 `man(1)` 哪些目录要自动搜索。只使用一个 `_default` 关键字,但如果需要,可以列出多个目录,并使用方括号分组目录。
这里是标准的目录列表:
_default /usr/{share, X11R6,local,ports/infrastructure}/man/
这是一个相当庞大的目录组,方括号组合了多个关联目录。例如,此条目表示我们将检查 */usr/share/man/*、*/usr/X11R6/man/*、*/usr/local/man/* 和 */usr/local/ports/infrastructure/man/*。(条目以斜杠结尾,这意味着最终目录包含子目录。)
要将 */usr/local/wireshark/man/* 添加到默认位置,请将 `local/wireshark` 添加到关联数组中,如下所示:
_default /usr/{share, X11R6,local,ports/infrastructure,local/wireshark}/man/
`_subdir` 关键字按顺序列出要搜索的主目录下的子目录,第一个匹配项首先返回。
_subdir man1 man8 man6 man2 man3 man5 man7 man4 man9 man3p man3f mann
你可以使用 `_subdir` 来改变手册页返回的顺序。例如,如果你的工作是在 OpenBSD 上编程 Perl,你可能希望默认看到 Perl 的手册页。在这种情况下,你可以将 *man3p* 子目录移到列表的前面。
#### 显示手册页
软件供应商以他们认为最好的格式分发他们的手册。作为系统管理员,这意味着你可能会收到各种格式的手册,因为每种格式都需要不同的命令来显示。幸运的是,每种格式都有不同的文件名后缀,这告诉 `man(1)` 如何显示该文件。`_build` 关键字定义了文件名后缀和用于显示文件的命令。(你不太可能有一个需要新 `_build` 语句的 man 页面。)
#### 定义 Man 部分
最终的 *man.conf* 功能是将手册分为几个部分。我们在 第一章 中看到,你可以通过特定部分搜索手册以获取特定的 man 页面。这些部分不过是 */etc/man.conf* 中标识的目录。这里定义了包含在 1 部分的 man 页面。
1 /usr/{share,X11R6,local}/man/man1
没有尾部斜杠,因为我们没有添加任何子目录。这些是包含手册第一部分的实际目录。
你可以在 */etc/man.conf* 中定义任意的部分名称。虽然你应该避免以下划线开头以防止与关键字混淆,但你几乎可以做任何事情。
### /etc/master.passwd、/etc/passwd、/etc/spwd.db 和 /etc/pwd.db
*/etc/master.passwd*、*/etc/passwd*、*/etc/spwd.db* 和 */etc/pwd.db* 文件包含用户名和密码,以及一些关于本地定义用户的关键信息。当你登录时,你输入的密码将与该文件中密码的加密和加盐散列进行比较。因此,*/etc/master.passwd* 对系统安全至关重要。
#### 编辑 /etc/master.passwd
如果你正在考虑直接编辑 */etc/master.passwd*,请停止。回到 第六章。重新阅读它。看看是否有其他方法可以实现你想要的变化。破坏 */etc/master.passwd* 可能会阻止人们登录,并可能导致你的系统无法使用。要编辑 */etc/master.passwd* 中的单个用户账户,以 root 身份运行 `chpass(1)`。
如果你必须直接编辑 */etc/master.passwd*——比如说,将每个人的家目录指向新的文件系统——有一个程序正是为此而设计的。程序 `vipw(8)` 会调用 `$EDITOR` 中的文本编辑器,加载密码文件,让你进行更改,并在保存之前检查文件语法。`vipw` 还会更新 */etc/passwd* 以及密码数据库 */etc/pwd.db* 和 */etc/spwd.db*。
请务必确保此文件与密码数据库同步。使用 `vipw` 可以防止许多基本错误并帮助确保数据一致性,但如果你真的想破坏 */etc/master.passwd*,`vipw(8)` 将使你的任务更加困难,但不会阻止你。
话虽如此,只有高级系统管理员应该使用 `vipw`。你如何知道你是否符合条件?如果你已经用 `vipw` 做过足够的糟糕错误,以至于你知道在骨子里不应该使用它,如果你的胃在仅仅想到输入这四个字母时就会翻腾,那么你可以使用 `vipw`。
#### 控制账户信息访问
许多程序需要访问存储在 */etc/master.passwd* 中的部分信息。例如,一个程序必须能够查找用户的 shell 和家目录,以便正确地找到用户的文件。而不是允许任何人读取这个文件及其中的散列密码,OpenBSD(以及大多数其他类 Unix 系统)提供了在 */etc/passwd* 中每个人都需要查看的账户信息。
访问和解析文本文件可能会很慢,尤其是如果计算机有一个慢速处理器和许多用户账户。文本文件并不适合搜索。如果一个程序必须搜索文本文件以查找用户账户编号 10631,搜索可能会阻碍程序的活动,甚至阻止其他程序。检查数据库中的条目要快得多,因为程序可以说“给我账户 10631”,然后从为搜索设计的文件存储中获取响应。
所有类 Unix 系统在账户信息更改时都会从密码文件中创建数据库。由 */etc/passwd* 构建的公共账户信息数据库是 */etc/pwd.db*。包含从 */etc/master.passwd* 构建的私有账户信息的数据库文件是 */etc/spwd.db*。
实际上,很少有软件直接使用密码文件。大多数程序访问 *pwd.db*。我知道有些人删除 */etc/passwd* 而没有不良影响,但你确实需要保留密码数据库。
#### /etc/master.passwd 字段
每个账户都是 */etc/master.passwd* 和 */etc/passwd* 中的一个行。每一行都有以下九个字段,由冒号分隔。
> 用户名
>
> 此字段包含由系统管理员创建并由人类使用的账户,或者为程序或服务创建的非特权用户。第六章涵盖了用户名。
>
> 散列和加盐的密码
>
> 此字段包含用户的密码,经过散列和加盐处理。(它通常被称为散列,但你应该知道它不仅仅是那样。)你不能从散列中推导出密码,但如果你有散列,你可以尝试密码直到找到一个与散列匹配的密码。这就是为什么你必须保护你的 */etc/master.passwd* 文件。在 */etc/passwd* 中,密码字段是空的。
### 注意
一种临时禁用用户账户的简单方法是通过使用 `chpass(1)` 编辑密码文件,并在散列前放置一个星号 (`*`)。虽然账户仍然处于活动状态,但没有人能够登录到它。当客户账单逾期时,我经常使用这种方法。虽然客户可以忽略逾期付款通知,但他们无法访问账户时会很快打电话。我可以轻松地将电话转接到账单部门。一旦问题解决,我可以通过移除星号来重新启用他们的账户。
> 用户 ID 号
>
> 第三个字段是用户 ID 号,或 UID。每个用户都必须有一个唯一的 UID。
>
> 组 ID 号
>
> 此字段是组 ID 号,或 GID。这是用户的主要组,如第六章中所述。通常,这与 UID 相同,并且用户的主要组与用户名同名。一些站点更喜欢为所有非特权用户使用单个组。
>
> 用户类别
>
> 我们在第六章中讨论了用户类别。更改类别可以增加或减少用户可以访问的系统资源量。
>
> 密码过期日期
>
> 过期日期以自 1970 年 1 月 1 日午夜以来的秒数表示,这是 UNIX 纪元。您可以使用 `date(1)` 将日期转换为秒。如果您必须手动设置密码的过期日期,请使用 `chpass(1)` 并指定一个可读的日期。
>
> Gecos
>
> 此字段包含用户的真实姓名、办公室号码、工作电话号码和住宅电话号码,所有这些信息都由逗号分隔。当计算机是大系统且有数百或数千个用户时,这些信息非常重要,并且当他们的进程完全失控时,您可能需要联系他们。今天,这些信息基本上被忽略。(在此处不要使用冒号;冒号专门用于在 */etc/master.passwd* 中分隔字段。)
>
> 用户家目录
>
> 此字段是用户的家目录。虽然默认情况下它是一个与用户名相同的目录,但在 */home* 之下,您可以将其移动到任何您喜欢的地方。在密码文件中编辑家目录不会移动实际的目录;您需要自己进行操作。
>
> 用户 shell
>
> 最后一个字段给出了用户的 shell。如果此字段为空,则用户将获得无聊的旧 */bin/sh*。
### /etc/mixerctl.conf
OpenBSD 包含强大的音频功能。您可以播放 MP3,混音音乐,或几乎您想做的任何事情。
使用 `mixectl(8)` 控制音频设置,并在 */etc/mixerctl.conf* 中设置引导时的 `mixerctl` 设置。
### /etc/mk.conf
使用 */etc/mk.conf* 配置 `make(1)`。特殊 `make` 设置最常见的使用情况是在构建端口时(在第十三章中介绍)或构建您自己的自定义 OpenBSD 版本时(在第二十章中介绍)。
### /etc/moduli
*/etc/moduli* 文件包含用于 Diffie-Hellman 密码学的质数。永远不要编辑此文件。有些人可能对密码学足够了解,可以编辑 */etc/moduli*,但如果你在读这本书,你很可能不是其中之一.^([40])
### /etc/monthly 和 /etc/monthly.local
如 第十五章 中所述,*/etc/monthly.local* shell 脚本每月运行一次,作为常规系统维护的一部分。讨论了计划内的维护任务。
### /etc/motd
MOTD,或每日消息,在用户登录时显示给用户。在此文件中,您可以放置希望用户注意的系统通知或公告。许多组织在 */etc/motd* 中放置法律通知或可接受使用政策。
注意,*/etc/motd* 的第一行在每次启动时都会被覆盖。请在第 2 行或以下开始您的法律警告。
### /etc/mrouted.conf
除了 `dvmrpd(8)` 之外,OpenBSD 还支持使用 `mrouted(8)` 进行多播路由。`dvmrpd` 实现是首选的,但对于特定的边缘情况,您可能需要 `mrouted`。
在 */etc/mrouted.conf* 中配置 `mrouted`。最终,`mrouted` 将从 OpenBSD 中移除。
### /etc/mtree/
*/etc/mtree/* 目录包含大多数目录的列表,包括它们在标准 OpenBSD 系统中的所有权和权限。系统升级过程使用这些文件。虽然你实际上不需要编辑这些文件,但了解它们的作用是很好的。
### /etc/mygate
如 第十二章 中所述,*/etc/mygate* 脚本提供了 IPv4 和 IPv6 的默认网关地址。
### /etc/myname
如 第四章 中所述,*/etc/myname* 文件包含系统的主机名。
### /etc/netstart
如 第十二章 中所述,*/etc/netstart* 脚本启动网络。
### /etc/networks
*/etc/networks* 是子网及其名称的列表。由于它并不特别有用,因此使用网络数据库已不再流行。
### /etc/newsyslog.conf
如 第十五章 中所述,`newsyslog(8)` 程序会轮换日志文件。
### /etc/nginx/
OpenBSD 已将 `nginx` 网络服务器 (*[`www.nginx.org/`](http://www.nginx.org/)*)作为旧版 Apache 1.3 服务器的替代品导入,但截至本文撰写时,它尚未与系统其他部分完全集成。您可以在该目录中找到 `nginx` 配置文件,服务器相当可用,但还不是官方的 OpenBSD 网络服务器——尚不是。
### /etc/nsd.conf
OpenBSD 已导入名称服务器守护进程 `nsd(8)`,最终将部分替代旧的 DNS 工作马服务器 `named(8)`。它是可用的,但截至本文撰写时,它尚未与系统集成。
### /etc/ntpd.conf
NTP 守护进程将系统时间与网络上的其他机器以及互联网上的机器同步。我们在第十五章中讨论了`ntpd(8)`。
### /etc/ospf6d.conf 和 /etc/ospfd.conf
OSPF 是一种在自治系统内部使用的路由协议。OpenBSD 有两个 OSPF 实现:一个用于 IPv4,一个用于 IPv6。如果您想了解更多关于 OSPF 的信息,请阅读`ospfd(8)`和`ospf6d(8)`。
### /etc/pf.conf 和 /etc/pf.os
在`/etc/pf.conf*`中配置 OpenBSD 的`pf(4)`数据包过滤器。数据包过滤使用`/etc/pf.os*`来识别其他操作系统。第二十一章和第二十二章涵盖了数据包过滤。
### /etc/ppp/
您可以通过拨号调制解调器将 OpenBSD 连接到互联网,这在当今很少见。如果您需要在 OpenBSD 系统上配置调制解调器,请阅读`ppp(8)`手册页。
### /etc/printcap
打印机能力文件描述了本系统可以访问的所有打印机。在 Unix-like 系统中,与随机打印机协同工作长期以来被认为需要某种牺牲,比如月亮处于正确的相位,以及一群穿着长袍的念经僧侣。虽然已经编写了复杂的软件如 CUPS 来简化打印,但配置 OpenBSD 机器以打印到打印服务器或网络 PostScript 打印机相当简单。有关打印的详细信息,请参阅第十六章。
### /etc/protocols
`*/etc/protocols*`文件包含 TCP/IP 网络协议的协议号。第十一章详细介绍了 TCP/IP 版本 4 和 6。
### /etc/rbootd.conf
`rbootd(8)`为 HP 工作站提供引导服务——这是一个非常狭窄的已废弃的无盘客户端子集。OpenBSD 仍然支持需要此服务的 HP300 机器。如果您对现代硬件上的无盘操作感兴趣,请阅读`diskless(8)`手册页,或者查看第二十三章。
### /etc/rc.*
`*/etc/rc.**`文件用于系统引导,这在第五章中详细讨论。
### /etc/relayd.conf
负载均衡器守护进程`relayd(8)`与 OpenBSD 数据包过滤器(PF)系统协同工作,充当网络负载均衡器。然而,`relayd`守护进程需要很好地理解 PF 以及一个非常特定的网络。如果您对负载均衡感兴趣,请阅读 Hansteen 的《PF 之书》。
### /etc/remote
Unix-like 系统广泛支持通过串行线连接到系统,通常用于串行控制台。许多网络设备具有管理串行端口,您可以使用 OpenBSD 作为客户端来配置这些设备。`/etc/remote`文件包含大多数常见现代串行连接的配置(在第五章中介绍)。
### /etc/resolv.conf 和 /etc/resolv.conf.tail
*/etc/resolv.conf*和*/etc/resolv.conf.tail*文件配置了解析器(在第十二章连接到网络中介绍),允许主机将名称映射到 IP 地址,反之亦然。
### /etc/ripd.conf
RIP 是在网络中广播路由指令的旧方法。OpenBSD 有一个 RIP 守护进程`ripd(8)`,配置在*/etc/ripd.conf*中。
RIP 通常被认为是不受欢迎的,就像 r 服务一样。除了其他缺点外,它甚至不支持子网掩码,因此仅限于旧式的 A 类、B 类和 C 类网络。并且与 r 服务一样,有时您可能会陷入使用 RIP,因为您的网络中某些过时的设备无法处理其他任何东西。使用`ripd`来勉强维持,直到您可以安排该设备的悲剧性事故。
### /etc/rmt
远程磁带命令(`rmt`)允许主机访问另一台机器上的磁带驱动器。它最常用于从备份中恢复系统。
### /etc/rpc
RPC 是在远程服务器上执行命令的方法。与 TCP/IP 类似,RPC 也有服务和端口号。文件*/etc/rpc*包含这些服务的列表和端口号。OpenBSD 中最常见的 RPC 消费者是 NFS,如第九章所述。
### /etc/sasyncd.conf
OpenBSD 支持在 IPsec 网关之间进行故障转移,使用安全关联同步守护进程`sasyncd(8)`。这不是操作系统中的常见功能,其存在是 OpenBSD 的一个亮点。要了解 IPsec 故障转移,请阅读`sasyncd(8)`。
### /etc/sensorsd.conf
现代硬件具有检测风扇速度、电路电压、温度等项目传感器的功能。OpenBSD 的`sensord(8)`读取这些传感器并将信息呈现给用户。在*/etc/sensorsd.conf*中配置您想关注的传感器,以及当传感器检测到某些内容时您想执行的操作。有关`sensorsd`的详细信息,请参阅第十五章。
### /etc/services
*/etc/services*文件包含网络服务和它们相关的 TCP/IP 端口的列表。有关详细信息,请参阅第十一章。
### /etc/shells
*/etc/shells*文件包含合法用户 shell 的列表,如第六章所述。
### /etc/skel/
*/etc/skel/*目录包含标准用户环境配置文件。当您创建用户帐户时,`adduser(8)`会将此目录中的文件复制到新用户的主目录。在您升级系统时,此目录可以被覆盖。
如果您需要为您的用户自定义这些文件,请创建一个自定义点文件目录,并告诉`adduser(8)`使用它。
### /etc/sliphome/
串行线路互联网协议(SLIP)在通常用于拨号线的点对点协议(PPP)之前出现。OpenBSD 仍然支持它,因为可能有人需要它,而且没有真正的原因要丢弃它。
### /etc/snmpd.conf
简单网络管理协议 (SNMP) 是一种通过网络访问设备信息的方法。不幸的是,它不是一个非常安全的协议(SNMP 的一个常见缩写是“安全?不是我的问题!”)OpenBSD 团队编写了一个更安全的 SNMP 守护进程 `snmpd(8)`。在 */etc/snmpd.conf* 中配置它,如 第十六章 中所述。
虽然 OpenBSD 的 SNMP 守护进程可能抵制入侵和滥用,但它无法改变 SNMP 本身(通常部署的 SNMP)并不十分安全的事实。
### /etc/ssh/
SSH 守护进程 `sshd(8)` 提供了 `telnet(1)` 和 r-协议的安全替代方案。第十六章 包含了关于 SSH 的简要讨论。
### /etc/ssl/
*/etc/ssl/* 目录用于安全套接字层 (SSL) 证书,以及 OpenSSL 配置文件 *openssl.cnf*。在这里存储系统 SSL 证书。
### /etc/sudoers
*/etc/sudoers* 文件控制 `sudo(1)` 配置。有关 `sudo` 的详细信息,请参阅 第七章。
### /etc/sysctl.conf
在 */etc/sysctl.conf* 中设置内核运行时可调整选项。Sysctls 在 第十八章 中介绍。
### /etc/syslog.conf
记录守护进程 `syslogd(8)` 从程序和主机读取消息,然后根据 */etc/syslog.conf* 中的配置将这些消息分离成记录。有关 `syslogd` 的详细信息,请参阅 第十五章。
### /etc/systrace/
`systrace(4)` 系统调用包装器提供了对系统调用的访问控制。您可以在 `systrace(1)` 之下运行二进制文件,如果程序尝试访问应用程序 `systrace` 策略中未允许的任何系统调用,`systrace` 将阻止访问。
然而,在 `systrace` 中发现了缺陷,使其效果不佳,现在它被认为只是一种部分解决方案。它仍然与 OpenBSD 一起提供,但使用它被劝阻。今天,`systrace` 主要用于打包构建集群,以确保构建的软件不会联系远程服务器或写入虚假安装根目录之外。
如果您需要使用 `systrace`,请将策略存储在 */etc/systrace* 中。
### /etc/termcap
*/etc/termcap* 文件描述了 OpenBSD 支持的所有不同终端。现在几乎每个控制台设备都支持标准的 VT220 终端。
### /etc/ttys
在 */etc/ttys* 中配置系统终端。您可以在其中启用、禁用和更改终端。一个“终端”可以是连接到计算机的键盘和显示器,也可以是通过串行线登录,如串行控制台,或者是一个虚拟终端,如 telnet 或 SSH 所使用的终端。
经典的 UNIX 终端设备类似于电传打字机;这就是 *tty* 标签的由来。所有各种 UNIX 架构细节都源自这一历史事件,Unix-like 系统继承了它们。
#### 终端类型
*/etc/ttys* 列出了三种不同的终端类型:控制台、串行端口和伪终端。
控制台是显示引导消息的地方,也是可以执行单用户模式维护的地方,以及错误消息显示的地方。虽然控制台通常是连接到计算机的键盘、显示器和鼠标,但它不必总是这样。这里的“控制台”是一个抽象概念,通常指向你的物理显示器和键盘。例如,你也可以将控制台指向一个串行端口。控制台使用设备 */dev/console*。
在某些平台(包括 i386 和 amd64)上,OpenBSD 支持多个虚拟控制台连接到你的物理控制台。如果你在物理键盘上按下 CTRL-ALT-F2,你会看到一个全新的登录屏幕。按下 CTRL-ALT-F1 将返回主控制台。你可以拥有与功能键一样多的虚拟控制台,但 OpenBSD 默认有五个。这些虚拟控制台的设备名称以 */dev/ttyC* 开头,以十六进制数字结尾。
串行端口可以用作登录设备,一旦你连接了一个老式的串行终端或 null-modem 电缆以及另一个带有串行端口的设备。它们也可以用于出站串行连接。用作入站设备的串行端口以 */dev/tty* 开头,而出站连接的端口以 */dev/cua* 开头,两者都以十六进制数字结尾。每个串行端口可以连接一个终端。
伪终端支持网络连接。尽管你的远程 SSH 窗口在服务器上没有相应的物理硬件,但 OpenBSD 以某种方式将其视为 `tty` 设备。伪终端的设备名称以 */dev/tty* 开头,一个字母 `p` 到 `z`(小写或大写),以单个字母或数字结尾。当用户通过网络连接(如通过 SSH)时,登录会话被分配一个这些虚拟终端设备之一。
无论终端类型是什么,都使用以下程序在 */etc/ttys* 中进行配置。
#### 配置终端
每个终端都有一个 */etc/ttys* 条目,包含至少三个条目,可能最多五个。
ttyC0 /usr/libexec/getty std.9600 vt220 on secure
第一个条目是设备名称,不带前缀 */dev*。
第二个条目是启动终端登录请求的程序名称。物理终端和虚拟控制台使用 `getty(8)`,而伪终端通过用户登录的任何守护进程处理登录请求。
第三个条目是终端类型。OpenBSD 在显示器和虚拟控制台上使用 `vt220` 终端。串行控制台使用 `unknown` 终端类型,因为没有方法预先知道终端另一端是什么类型的硬件。(如果你使用的是一个相对现代的串行客户端,你可以将其切换为 `vt220` 而不会出现问题。)伪终端使用 `network` 终端类型;服务器守护进程和客户端软件确定终端的功能。
第四字段确定终端是否可用于登录。将其设置为 `on` 以接受登录请求,或设置为 `off` 以拒绝它们。伪终端按需激活,因此你不需要在 */etc/ttys* 中配置它们。
根账户只能登录到安全的控制台。物理终端和控制台是默认定义为安全的唯一设备。你可以以普通用户身份登录并使用 `su(1)` 或 `sudo(1)` 在不安全的终端上;你只能不能直接以 `root` 身份登录。这意味着任何拥有根密码并且对机器有物理访问权限的人都可以直接走到键盘前以 root 身份登录。要禁止直接登录到 root,请从 */etc/ttys* 中的虚拟控制台条目中删除 `secure` 关键字。此外,在安全终端上,当你以单用户模式启动时,不会要求你输入根密码。
如果你使用串行控制台,你可能想通过串行电缆登录到正在运行的多用户系统。默认情况下,你无法在串行线上登录。将串行控制台上的串行线设置为 `on`,你就可以登录了。这使得串行线的行为与在物理控制台上的行为非常相似,在那里你可以以多用户模式工作,同时机器正常运行。
虽然所有默认设置都使用 `getty(8)`,但也有一些替代方案。例如,HylaFAX 软件包允许你将传真机连接到串行线,但你需要重新配置终端以支持它。
tty0e "/usr/local/sbin/faxgetty" dialup on
重点是,一旦你为这项工作找到了合适的软件工具,你就可以按你喜欢的方式使用串行端口。
#### 使 */etc/ttys* 的更改生效
提供终端是一个由 `init(8)` 直接处理的低级系统任务。对 */etc/ttys* 的更改不会生效,直到你告诉 `init` 重新读取其配置。(`init` 总是进程 ID 1。)
kill -HUP 1
如果你没有告诉 `init` 重新读取其配置,更改将不会生效,直到你重新启动。
### /etc/weekly 和 /etc/weekly.local
*/etc/weekly.local* 脚本每周运行一次,以执行每周维护,如 第十五章 中所述。
### /etc/wsconsctl.conf
OpenBSD 通过 `wscons(4)` 驱动程序提供了对物理控制台的硬件无关访问。通过 `wsconsctl(8)` 配置此控制台。启动时的 `wsconsctl` 设置是从 */etc/wsconsctl.conf* 中读取的。有关控制台配置的详细信息,请参阅 第十七章。
### /etc/X11
*/etc/X11* 目录包含 X Window 系统的配置。OpenBSD 的 Xenocara 将 X 与基本系统集成。第十七章 讨论了桌面 OpenBSD,包括一些 X 功能。
### /etc/ypldap.conf
除了 LDAP 守护进程 `ldapd(8)`,OpenBSD 还支持 YP 数据库以集中管理密码、组和主机文件系统。YP 与 Sun 的原始网络信息系统 (NIS) 兼容。OpenBSD 使用 YP 作为 LDAP 认证的网关。如果你对这个功能感兴趣,请参阅 `yp(8)` 和 `ypldap(8)`。
这将带你了解 */etc* 中其他地方未涵盖的所有内容。在下一章中,我们将讨论 OpenBSD 如何维护自身,以及你如何可以干预维护过程。
* * *
^([39]) 如果 Wireshark 有一个不稳定的网络安全历史,我为什么还要安装它?因为,遗憾的是,它仍然是调试真正复杂的网络问题最简单的方法,尤其是在你处理不熟悉的协议时。
^([40]) Henning Brauer,OpenBSD 开发者和数据包过滤的负责人评论说,“我对加密算法有一定的了解。…但我也不乱动模数。”请自行注意。
## 第十五章 系统维护
*当硬件抱怨时,*
*OpenBSD 倾听。*
*你也应该倾听。*
 没有电脑会自己运行。如果它会,你就不需要这份工作了。即使是配置最好的服务器也会产生持续的维护需求。
OpenBSD 包括各种工具,使维护更容易,在需要维护时提醒你,并告诉你系统的状态。每天、每周和每月,OpenBSD 都会执行维护任务,并通知你结果。OpenBSD 会对关键系统功能和文件进行每日备份,并使用它们来监控系统完整性。它可以管理自己的日志文件,保持自己的时间,并在硬件出现故障时提醒你。
所有这些都始于定期维护。
## 计划任务
OpenBSD 包括每天、每周和每月运行的计划任务。这些任务以 root 身份运行,并将结果发送给 root。每日维护脚本是最复杂的;每月脚本是最简单的。
如果服务器运行良好,我可能几周甚至几个月都不会登录到它。事实上,我有一些服务器已经运行了一年多,没有人登录过。我阅读机器的每日报告,说“是的,没问题”,然后继续我的日常生活,自信监控系统会告诉我那些常规状态报告没有告诉我的事情。
定期维护任务会将结果发送到本地 root 用户,但如果没有人登录到机器,就没有人会看到这些结果。始终在*/etc/aliases*中为 root 设置一个电子邮件别名,这样这些消息就会发送给会实际阅读它们的人。
每天阅读每台机器的每封电子邮件都很烦人,但比发现用户告诉我服务有问题时更不烦人。这些消息通常在任何人(包括我)注意到之前就会提醒我系统问题。拥有数百台机器的站点通常会编写脚本来解析传入的电子邮件消息并标记有趣的细节。
你应该将维护电子邮件发送给最终负责系统的人——无论谁最感兴趣系统变化,最有可能意识到任何日常系统变化。如果你将阅读维护电子邮件的任务委托给对系统不太了解的下属,他可能会用关于你昨天做了什么的无尽问题来烦扰你,或者学会忽略状态邮件中的任何实际内容。
在这里,我们将看看每日、每周和每月例程是如何工作的。维护任务的完整文档出现在`daily(8)`中。
### 每日维护
每日维护任务首先从*/etc/daily.local*运行任何自定义维护任务(在本节末尾介绍),包括检查分区、运行提醒服务`calendar(1)`、运行`rdist(1)`、删除临时文件,以及其他一些无聊的事情。
OpenBSD 还可以在其日常维护中执行一些其他有趣的事情:
+ 创建备份根文件系统,*/altroot*。
+ 执行系统安全检查。
+ 在 */var/backup* 中备份重要的系统文件。
+ 检查重要系统文件的变化。
+ 检查文件系统完整性。
+ 运行 `rdist(1)`。
我在 第九章 中讨论了 */altroot*,因为它需要一个专用的文件系统分区。其他每个任务都可以稍后配置。
#### 安全检查
一些出错的情况并不一定意味着你的系统已经遭受了入侵,但仍然令人怀疑。日常安全检查会寻找一系列由恶意或疏忽引起的配置错误和问题。你可以在 `security(8)` 中阅读检查列表,但它们可以分为相当广泛的类别:
> 设备节点变更和权限
>
> 新的设备节点、设备节点权限变更、新的软件包、以及新的或变更过的磁盘或分区可能表明恶意活动,也可能只是正常的系统管理。安全脚本会标记所有这些情况。如果你进行了这些变更,你会点头然后继续你的日常。如果你要求一个从属进程执行变更但变更没有出现,这时你应该问他们昨天整天都做了什么。如果你没有进行显示的变更,你希望了解这些情况。
>
> 不安全的 NFS 导出
>
> OpenBSD 包含了许多用于导出文件系统和远程运行命令的软件。这些服务,如打印和 NFS,不应允许来自任何主机的访问,而只应允许来自你批准的主机。安全任务会检查允许全局访问的配置。
>
> 配置不当的账户
>
> 另一个流行的攻击途径是密码数据库和相关文件。无密码的账户、重复条目、不当关闭的账户等问题都可能被用来危害系统。脚本会检查这些问题。
>
> 权限
>
> 权限设置不当可能导致权限提升。例如,系统上是否有新的 `setuid` 或 `setgid` 文件?如果有,安全脚本会通知你。如果你用这些权限安装了该文件,那么你没问题。如果这是意外的,你应该进行调查。
>
> 用户环境
>
> 如果你能够更改一个用户的环境,无论是 root 还是其他账户,你都可以诱使他们泄露他们的认证凭证或运行可疑命令。如果入侵者可以编辑用户的点文件,如 *.cshrc* 或 *.login*,他可以更改他运行的命令版本。也许他的 shell 被设置为运行一个要求用户输入密码并发送给入侵者匿名电子邮件账户的程序。通过正确设置家目录、点文件、邮件文件等的权限,你可以使这类攻击更困难。安全脚本会验证权限是否设置正确。
注意,安全检查**不是**一个入侵检测系统。它检查的更改类型是脚本小子和新手入侵者最有可能做出的,但熟悉 OpenBSD 的熟练入侵者可能会绕过它。他们甚至可以用一个 shell 脚本替换安全检查,该脚本每天发送一封看似无害的安全检查的电子邮件。
幸运的是,有能力的入侵者相对较少。只需记住,收到没有提及问题的安全检查是令人鼓舞的,但这并不是你的服务器安全的证据。
#### 重要文件备份和测试
每日安全检查会检查 */etc/changelist* 中列出的文件的变化,并轮换它们的备份,因为这些文件通常是关键系统文件,如 */etc/master.passwd*、*/etc/boot.conf* 和 */var/cron/tabs/root*。它还会检查磁盘分区和挂载文件系统的变化,以及设备节点的变化。
查看*/var/backups*,你会看到如下文件:
…
etc_fstab.backup
etc_fstab.current
etc_ftpchroot.current
etc_ftpusers.current
…
以*.current*结尾的文件是当每日维护任务上次运行时这些文件的副本。以*.backup*结尾的文件是这些文件的上一版本。
安全脚本第一次运行时,会将所有这些文件复制到 */var/backup*。在初始设置之后,安全脚本会检查原始文件与当前副本之间的变化。如果文件发生变化,则将文件的上一版本复制到*.backup*文件名,并将新版本复制到*.current*文件。
在前面的例子中,列表显示我在某个时间编辑了我的 */etc/fstab*,这促使安全脚本将其旧文件系统表的副本移动到*.backup*文件。我从未编辑过 */etc/ftpchroot* 或 */etc/ftpusers*,因此这些文件没有*.backup*版本,只有*.current*版本。
安全脚本不会复制它所监视的所有文件。例如,包含私钥或可能包含私钥的文件不会被复制,但安全脚本会计算校验和。(使用校验和监控的文件在 */etc/changelist* 中的名称前有一个加号。)手动编辑 */etc/ssh/ssh_host_ecdsa_key* 没有理由,如果文件发生变化,要么你知道原因,要么你需要从受信任的备份中恢复。
*/etc/changelist* 本身也被列在 */etc/changelist* 中。这看起来是递归的,但系统会备份你想要备份的文件列表,并在有人向 */etc/changelist* 中添加或删除文件时通知你。
#### 添加重要文件
你可以向更改列表中添加文件,甚至可以使用通配符来备份目录中的所有文件。但请注意,如果你在 */etc/changelist* 中使用了通配符,当文件被删除时,你不会收到通知。
考虑这个使用通配符的例子。在 第十三章 中,我将 `apache2` 端口添加到我的机器之一。我将配置文件放在 */etc/apache2* 中。我可以在更改列表中添加这样的行:
/etc/apache2/*
这将自动将 *apache2* 配置目录中的所有文件复制到 */var/backup* 并检查它们是否有变化。然而,当文件从这个目录中删除时,我不会收到通知。如果你使用的是一种配置机制,它说,“包含此类目录中的所有 *.conf* 文件”,这可能不是你想要的。更好的选择是逐个列出每个文件,并在添加关键文件时更新列表。
文件完整性检查最方便的一点是它会自动创建关键系统文件的本地备份。这意味着如果你决定学习如何使用 `vipw(8)` 并在这样做时彻底破坏用户数据库,你可以从 */var/backups* 中抓取昨天的副本,安装它,而且没有人会察觉。这同样适用于每个其他关键系统文件。
#### 文件系统完整性检查
当系统处于多用户模式时,你不能运行完整的 Unix 文件系统 (UFS) 检查,但你可以让 `fsck(8)` 执行文件系统完整性检查,以尝试在问题变得严重之前识别它们。这样做不会修复任何问题,但它会通知你它们的存在,这样你就可以安排停机时间进行维修。
要启用这些检查,请在 */etc/daily.local* 中将 `CHECKFILESYSTEMS` 设置为 `1`。
#### 使用 rdist 复制文件
`rdist(1)` 程序用于将文件复制到其他服务器,让你在许多服务器上维护关键文件的相同副本。如果你有兴趣使用它,请参阅 `rdistd(8)`。
#### 静音 /etc/daily
我们中的一些人拥有监控系统,可以跟踪服务器的磁盘、网络和其他基本信息。如果你不需要这种信息出现在你的日常状态邮件中,请在 */etc/daily.local* 中将 `VERBOSESTATUS` 设置为 `0`。这将关闭日常维护的这些部分,减少你需要阅读的内容。
如果剩余的每日维护没有生成任何输出,服务器当天不应发送状态电子邮件。在不信任监控系统的环境中,你可以使用每日状态消息来确保系统按预期运行。OpenBSD 给你选择。
### 周维护
每周脚本比每日脚本简单,只有三个常用功能:
+ 首先,它运行自定义的每周脚本 */etc/weekly.local*。
+ 其次,它更新了 `locate(1)` 数据库。
+ 最后,它重建了 `whatis(1)` 手册页数据库。
### 月维护
OpenBSD 不需要任何通用的月维护,但为了保持一致性,*/etc/monthly* 脚本运行自定义脚本 */etc/monthly.local*。
### 自定义维护脚本
每个维护脚本在执行其他任务之前都会运行一个自定义脚本。你可以将任何需要的任务放在*/etc/daily.local*、*/etc/weekly.local*和*/etc/monthly.local*中。这些命令由 root 运行,因此不要将它们用于应由其他用户执行的任务。如果你的数据库需要备份,创建一个单独的脚本,并让运行数据库的非特权用户通过`cron(8)`运行该脚本。
一些网站使用计划中的维护任务来运行执行特定任务的复杂软件。例如,我知道有一家安全公司从数百台机器收集数据,并使用每日任务将数据发送到中央管理系统。实际上,你可以根据需要以任何方式使用本地脚本。
如果你有一个可以在其他用户账户下运行的维护任务,但你想将其附加到计划中的维护任务,你可以让本地脚本调用另一个脚本。通过使用`su(1)`切换用户并降低权限来启动该脚本。
自定义维护脚本可能最有用,可以改变标准维护脚本执行工作的方式。例如,假设你有一个包含临时文件的许多临时目录的系统。每周维护脚本更新 locate 数据库,但你不想这些临时文件包含在 locate 结果中。你可以在*/etc/weekly*创建新的 locate 数据库之前立即使用自定义维护脚本删除所有临时文件,并将此作为单独的任务进行安排。通过将其添加到*/etc/weekly.local*,你就会知道它将在*/etc/weekly*运行其他任务之前完成。
## 系统日志
Unix-like 操作系统使用的系统日志已成为日志记录的行业标准,但这并不一定是好事,因为日志机制可能会很麻烦。然而,一旦你正确配置了日志收集和轮换,OpenBSD 的日志系统大部分可以自行管理。
OpenBSD 使用 Unix-like(以及许多嵌入式)系统的标准日志系统`syslog(3)`。syslog 协议使用设施和优先级标记消息,并将这些消息交给守护进程。
任何程序都可以写入本地的`syslogd(8)`服务器,但在日志管理中关键是要决定如何对这些消息进行排序和存储。OpenBSD 的`syslogd`可以根据设施、优先级和源程序对消息进行排序。
### 设施
*设施*表示消息的来源。在大多数情况下,每个需要单独日志文件的程序都使用不同的设施。许多程序或协议,如 FTP,都有专门为它们设置的设施。syslog 协议也有各种通用的设施,你可以按需使用。
表 15-1 列出了标准设施并提供了一些关于它们使用的说明。
表 15-1. 表 15-1:标准 OpenBSD 设施
| 设施 | 用途 |
| --- | --- |
| `auth` | 关于身份验证的公共信息,例如某人登录或某人使用 `su` 时的情况。 |
| `authpriv` | 关于用户身份验证的私人信息,通常只有特权用户可以访问。 |
| `cron` | 来自系统调度程序 `cron(8)` 的消息。 |
| `daemon` | 对于既不需要也不需要专用设施的进程的捕获。 |
| `ftp` | 来自 FTP 和简单文件传输协议 (TFTP) 服务器的消息。 |
| `kern` | 内核生成的消息。 |
| `local0` 通过 `local7` | 这些设施是为系统管理员提供的。许多程序允许系统管理员配置它们的设施。对于此类程序,请使用这八个设施。 |
| `lpr` | 来自打印系统的消息。 |
| `mail` | 来自邮件服务器的消息。 |
| `mark` | 这个特殊设施每 20 分钟写入一条消息。 |
| `news` | 来自 Usenet 新闻服务器的消息。 |
| `syslog` | 来自 syslog 服务器的消息。 |
| `user` | 捕获所有消息的设施。如果用户空间程序没有指定日志设施,消息将最终在这里。 |
| `uucp` | 来自 Unix 到 Unix 复制协议 (UUCP) 服务器的消息。你可能会永远遇到这种互联网之前的电子邮件协议。 |
虽然大多数程序都有合理的默认设置,但作为系统管理员,你的工作是管理哪些程序将日志记录到哪些设施。如果可能,请为你的服务器特定守护进程使用本地设施。虽然完全有可能将设施用于原本的目的之外,但除非你真的没有其他选择,否则尽量不要将 `uucp` 设施重新分配给其他守护进程。
### 优先级
日志消息的优先级表示其重要性。程序通常将它们的日志数据发送到 `syslogd`,但 `syslogd` 决定保留什么和丢弃什么。你可以决定你日志中需要多少细节。使用以下九个 `syslog` 级别来决定记录什么和丢弃什么(从最重要到最不重要):
+ ****`emerg`****. 系统紧急情况。此消息出现在每个活动终端上。计算机可能正在崩溃,或者可能有一些其他需要立即注意的错误。
+ ****`alert`****. 紧急情况。系统可以继续运行,但必须尽快处理此错误。
+ ****`critical`****. 严重问题。这表明严重的错误,例如硬盘故障。
+ ****`err`****. 错误。这些是关于需要关注但不会破坏你的系统的问题。
+ ****`warning`****. 各种警告。这些可能需要关注,但不会阻止生成它们的进程正常运行。
+ ****`notice`****. 重要信息,例如守护进程启动和关闭通知。
+ ****`info`****. 基本信息通常包括事务数据,例如邮件服务器中的单个消息或对 Web 服务器的单个查询。
+ ****`debug`****. 知识点。这个级别通常只对程序员感兴趣,但偶尔对试图弄清楚程序为何以某种方式运行的系统管理员有用。调试日志可以包含任何内容,包括违反用户隐私的信息,例如明文密码。
+ ****`none`****. 不要记录此设施中的任何内容。这最常用于排除日志文件中的信息,如稍后所述。
通过结合优先级,你可以将日志消息分类到单独的文件或其他目标中。
### 通过 syslogd(8)排序消息
`syslogd`将接收到的消息与*/etc/syslog.conf*中的条目进行比较。此文件有两列:第一列(*选择器*)描述了一种日志消息类型,第二列(*操作*)告诉`syslogd`当消息与描述匹配时该做什么。两列都不能有空格;空格只能出现在列之间。例如,以下是从默认的*syslog.conf*中的一行:
daemon.info /var/log/daemon
任何具有守护进程设施和`info`或更高优先级的日志消息都将附加到文件*/var/log/daemon*。当然,如果所有日志都如此容易管理,这将是一个简短的章节。
`syslogd`将所有日志消息与所有*syslog.conf*条目进行比较。如果日志消息与多个选择器匹配,它将被发送到所有匹配的目的地。
#### 通配符
你可以在设施或优先级中使用通配符。例如,此行记录来自邮件设施的所有消息:
mail.* /var/log/maillog
要从所有设施中捕获给定优先级或更高优先级的消息,请使用星号(`*`)。以下是如何将所有优先级`err`或更高消息发送到控制台的方法:
*.err /dev/console
你还可以使用双通配符将所有日志消息发送到同一个地方。
. /var/log/all.log
不要将所有内容记录到同一个位置,这既不实用也不明智。你不应该将`authpriv`调试信息发送到任何世界可读的文件。
#### 排除信息
使用`none`级别排除日志中的信息。例如,以下行排除了其他情况下包含的所有私人认证信息。
.;authpriv.none /var/log/most.log
分号(`;`)允许你在单行上组合选择标准。
### 警告
如果你像这样使用分号组合条目,不要在分号后留空格。唯一的空格只能出现在选择器和目的地之间。
#### 组合设施
你可以使用逗号在单个条目中组合多个设施。以下是如何从多个设施中捕获所有`info`优先级或更高消息的方法:
auth,daemon,syslog,user.info @loghost
来自`auth`、`daemon`、`syslog`和`user`设施,以及优先级`info`或更高的任何日志消息,都将通过网络发送到主机`loghost`。
#### 标记时间
虽然所有日志消息都有时间戳,但你可能想在日志文件中有一个标记来指示时间的流逝。特殊的`mark`设施每 20 分钟创建一条消息,让你可以向文件添加额外的时间戳。以下是如何每 20 分钟向邮件日志添加一个时间戳的方法:
mail.info;mark.info /var/log/maillog
#### 本地设施
八个设施`local0`到`local7`可供你使用。许多程序可以配置为使用特定设施,因此你可以将它们指向特定的文件。我已经配置了一个守护进程使用设施`local7`。在这里,我将来自该设施的消息发送到文件:
local7.* /var/log/postgres.log
一些程序对特定设施有硬编码的偏好。例如,flow-tools 软件包(见我的书《网络流量分析》,No Starch Press,2010 年)将设施`local6`硬编码到代码中。当你看到类似的情况时,不要感到惊讶。幸运的是,OpenBSD 的`syslogd`可以根据程序名称进行过滤,因此你可以轻松地过滤日志,尽管这种做法有些愚蠢。
#### 按程序名称选择
如果你没有设施,你可以使用生成 syslog 消息的程序名称作为选择器。使用程序名称需要两行:第一行包含带有前导感叹号的程序名称,第二行设置日志记录。OpenBSD 提供了以下`sudo(8)`日志记录的示例:
!sudo
. /var/log/sudo
所有来自`sudo`的日志消息都发送到指定的日志文件。
你也可以通过程序名称进行选择,并在程序名称前使用两个感叹号(`!!`)来停止所有后续匹配消息的选择。以下示例将所有来自`sudo`的消息发送到*/var/log/sudo*,但防止`sudo`消息发送到任何其他日志。
!!sudo
. /var/log/sudo
!*
…
`sudo`条目末尾的`!*`是一种表示“所有程序”的方式——换句话说,不再按程序名称排序。只有在你使用双感叹号“在此处停止处理匹配消息”语法时才需要此语法。
### 记录日志操作
现在你已经知道如何将日志消息分类到不同的桶中,让我们看看如何对这些消息采取不同的操作。消息可以写入文件、通过管道发送到程序、发送到另一个主机或写入用户。
#### 将日志记录到文件
到目前为止的大多数示例都是将日志消息发送到文件,给出文件的完整路径作为操作。以下是将来自`local6`设施的所有消息发送到日志文件的步骤:
local6.* /var/log/flowtools
你也可以通过给出设备节点的完整路径将消息发送到设备,但这只对非常少的设备有意义,例如控制台。这是因为将日志消息写入磁盘设备*/dev/wd0d*不会将消息存储在磁盘上。
#### 将日志记录到程序
要将选定的日志发送到程序,请使用管道(`|`)和程序的完整路径,如下所示:
. |/usr/local/bin/logsurfer
日志系统应该启动目标程序,然后将日志消息喂入程序的标准输入。
#### 通知用户
你还可以通过列出逗号分隔的用户列表将日志消息直接发送到已登录用户。例如,要发送消息给所有用户,请使用星号。
*.emerg *
*.info lasnyder
此示例将通知所有已登录用户真正的紧急情况,但会深深惹恼`lasnyder`.^([41])
#### 将日志记录到远程主机
我通常有一个日志主机,它从各个地方收集日志消息——不仅从我的 OpenBSD 服务器,还包括所有其他类 Unix 系统、路由器、交换机以及其他任何支持 syslog 的设备。这减少了我的维护需求并节省了磁盘空间。而且,由于每个日志消息都包含一个主机名,我可以在稍后轻松地对它们进行分类。
要向另一个主机发送消息,请使用 `@` 符号。
*.info @loghost.blackhelicopters.org
这会将优先级为 `info` 及以上的所有内容都转到我的日志主机。
你的日志主机必须接受来自网络的 syslog 消息。如果你的主机是 OpenBSD 机器,请使用 `-u` 标志运行 `syslogd`。并且务必使用数据包过滤器保护你的日志主机,这样随机主机就不能向它写入日志并填满你的磁盘。
### 自定义 syslogd
OpenBSD 默认运行 `syslogd`,你可以自定义 `syslogd` 的行为。常见的自定义包括添加更多的日志套接字和监听网络。
#### 添加额外的日志套接字
程序将日志消息写入 */dev/log* 套接字,但 `chroot` 内部的软件无法访问该设备。要使一个被锁定在 `chroot` 内的程序向 `syslogd` 发送消息,必须在 `chroot` 内的 */dev/log* 处放置一个额外的日志套接字。
例如,由于集成的 BIND DNS 服务器被 `chroot` 到 */var/named*,DNS 服务器期望在 */dev/log* 找到日志套接字,这意味着新的日志套接字应该位于 */var/named/dev/log*。要创建此日志套接字,请使用 `syslogd` 的 `-a` 选项,并在 *etc/rc.conf.local* 中给出日志套接字的完整路径。
syslogd_flags="-a /var/named/dev/log"
你可以使用大约 20 个额外的日志套接字。
#### 监听网络
如果你想让你的 OpenBSD 服务器充当日志主机,接受来自远程主机的日志,请使用 `-u` 标志。
syslogd_flags="-u"
由于 syslog 协议没有访问控制,任何可以访问日志主机上 514/UDP 端口的用户都可以写入你的日志文件。
### 注意
用垃圾填充主机的日志以填满硬盘是一种旧攻击。使用 OpenBSD 的数据包过滤系统(在第二十一章 Packet Filtering 和第二十二章 Advanced PF 中讨论)来保护你的日志主机。
### Syslog 和嵌入式系统
OpenBSD 支持将日志消息写入内存缓冲区,这允许在没有任何可写磁盘的系统上进行日志记录,例如无盘系统、嵌入式路由器和防火墙。`syslogd` 在内存缓冲区中保留这些日志,并且客户端可以通过报告套接字连接到 `syslogd` 并读取日志。正如你所期望的,当 `syslogd` 关闭时,内存中的日志会消失。
要使用 `syslogd` 进行报告,首先使用 `-s` 选项提供一个报告套接字,并给出报告套接字的完整路径。以下是一个在 */var/run/syslog* 中的报告套接字的 *rc.conf.local* 条目:
syslogd_flags="-s /var/run/syslog"
要将日志记录到缓冲区,在 *syslog.conf* 中创建一个动作。指定使用冒号 (`:`) 将日志记录到缓冲区,指定缓冲区的大小(以千字节为单位),然后是另一个冒号,以及内存缓冲区的名称。(最大缓冲区大小为 256KB。)
例如,这里我们捕获所有 `err` 级别或更高的日志消息,并将它们写入名为 `errors` 的 128KB 内存缓冲区:
*.err :128:errors
使用 `syslogc(8)` 读取内存缓冲区,并使用 `-s` 选项告诉 `syslogc` 在哪里找到 `syslogd` 的报告套接字,并提供日志缓冲区的名称。以下是如何读取报告套接字 */var/run/syslog* 并读取 `errors` 缓冲区的方法:
$ syslogc -s /var/run/syslog errors
如果你忘记了要读取的缓冲区名称,请使用 `-q` 选项让 `syslogc` 查询可用的内存日志列表。务必提供报告套接字。
### 注意
即使你不是程序员,你仍然可以使用真实的 syslog 功能。通过 `logger(1)` 程序,shell 脚本可以将日志记录到 syslog。有关详细信息,请参阅 `logger` 的 man 页。
## 日志文件维护
你可以捕获日志。太棒了!现在只需让日志文件增长,直到它们填满你的硬盘,没有空间留给其他东西,对吧?或者你可以丢弃旧日志,让系统将日志保持在一个可管理的尺寸。这被称为 *日志轮转*。
查看系统消息日志 */var/log/messages*,你应该看到六个 *messages* 文件:*messages*,*messages.0.gz*,*messages.1.gz*,*messages.2.gz*,*messages.3.gz*,和 *messages.4.gz*。未压缩的 *messages* 文件是当前日志文件。其他文件是旧日志;*messages.0.gz* 是最新的,*messages.4.gz* 是最旧的。
当当前日志文件达到一定年龄或特定大小时,日志轮转会丢弃最旧的日志文件 (*messages.4.gz*),并将次旧的文件 *messages.3.gz* 重命名为 *messages.4.gz*;*messages.2.gz* 重命名为 *messages.3.gz*;依此类推。现有的 *messages* 文件被重命名为 *messages.0* 并压缩,然后创建一个新的 *messages* 文件。
`newsyslog(8)` 程序轮转日志文件,重启守护进程,运行命令,将旧文件移动到其他目录,并处理所有常规任务。`root` 通过 `cron(8)` 每小时运行一次 `newsyslog`。当 `newsyslog` 启动时,它会读取 */etc/newsyslog.conf* 并检查列出的每个日志文件。如果满足轮转日志文件的条件,则轮转日志并执行其他配置的操作。
### newsyslog.conf 字段
*newsyslog.conf* 使用每行一个日志文件。每一行包含七个字段,如下所示:
/var/log/authlog root:wheel 640 7 * 168 Z
从左到右,字段依次是日志文件、所有者、权限、保留文件数、大小、时间和标志。在标志字段之后,你可能还会看到一些可选参数。我们将按顺序查看每个字段。
#### 日志文件
每行的第一个条目是要处理的日志文件的完整路径(在这个例子中是 `/var/log/authlog`)。这必须与当前日志文件完全匹配。
#### 所有者
第二个字段(在我们的示例中为 `root:wheel`)列出了日志文件的所有者和组,由冒号分隔。此字段是可选的,并且在许多默认条目中不存在。
默认情况下,日志文件属于 root 用户和 `wheel` 组,但 `newsyslog` 可以更改日志文件的所有权。虽然更改所有权并不常见,但您可能希望明确声明特定文件的所有权。
您可以选择只更改所有者或只更改组。在这些情况下,只需在名称的一侧使用冒号,例如 `:wheel` 或 `root:`。如果您正在更改所有权,则必须始终包含冒号。
#### 权限
第三字段(在我们的示例中为 `640`)给出了旋转文件的权限,使用标准八进制表示法,如 `chmod(1)` 中所述。此字段是可选的,并且在许多默认条目中不存在。
#### 计数
第四字段指定 `newsyslog` 保留的归档日志文件的数量。在我们的示例中,*/var/log/messages* 有当前日志文件和五个归档,编号为 0 到 4。*newsyslog.conf* 为 */var/log/messages* 设置了 `5` 的计数。
#### 大小
第五字段是千字节大小的文件。当 `newsyslog` 运行时,它会检查日志文件的大小。如果日志文件的大小超过此处给出的值,`newsyslog` 将旋转日志。如果您不希望文件大小影响 `newsyslog` 何时旋转文件,请在此处放置一个星号。
#### 时间
要根据时间旋转日志,请使用第六个字段,它有四个可能的值:一个星号,一个数字,以及两种标准格式之一的时间。如果您根据大小而不是年龄旋转日志,请在此处放置一个星号。如果您在此处放置一个数字,则指定日志将在多少小时后旋转。我们的示例 */var/log/authlog* 每 168 小时旋转一次。
时间格式——ISO 8601 限制和 `newsyslog` 特定——要复杂一些。
> ISO 8601 限制
>
> 以 `@` 符号开始的日期条目是 ISO 8601 限制时间格式。ISO 8601 限制时间格式在大多数类 Unix 系统上由 `newsyslog` 使用,因为它曾是麻省理工学院原始 `newsyslog` 使用的日期格式。ISO 8601 格式有点晦涩,但我所了解的每个类 Unix 操作系统都支持它。
>
> 完整日期以 ISO 8601 格式表示为中间有 `T` 的 14 位数字。前四位是年份,接下来的两位是月份,接下来的两位是月份中的日期。(`T` 类似于小数点,将整个天数与一天的小数部分分开。)接下来的两位是小时,接下来的两位是分钟,最后两位是秒。例如,2013 年 9 月 13 日下午 3:18 和 58 秒的日期表示为 `20130913T151858`。(指定特定日期和时间来旋转日志不会非常有用,因为日志只会旋转一次。)
>
> 您可以选择仅指定靠近 `T` 的字段,而将较远的字段留空。再次强调,如果您将 `T` 视为小数点,则不需要将 5.87 写成 005.8700;前导和尾随零是不相关的。
>
> 在`newsyslog`的情况下,空字段是通配符。例如,`4T00`匹配每月第四天的午夜,而`T23`匹配每天的二十三点,即晚上 11 点。如果`newsyslog.conf`中列出时间为`@T2359`,则日志每天晚上 11:59 旋转。(当然,`newsyslog`每小时运行一次,因此日志不会正好在那个时间旋转。)
>
> 与`cron(8)`一样,需要详细指定时间单位。例如,`@9T`,表示每月的第九天,每小时旋转一次日志,这意味着在这一天全天都会旋转日志。可能更好的做法是指定时间为`@9T01`,这样日志就会在每月的第九天凌晨 1 点旋转。由于`newsyslog`每小时运行一次,因此不需要比小时更精确地指定时间。
>
> newsyslog 时间
>
> 由于 ISO 8601 时间格式无法轻松指定每周的任务,并且无法指定每月的最后一天,OpenBSD 包含一个针对`newsyslog`特定的日期格式,允许您轻松指定这些常见时间。
>
> 任何以美元符号(`$`)开头的条目都使用*月份 星期 天*格式。
>
> 此特定格式使用三个标识符:*`M`*(月份中的某一天)、*`W`*(星期中的某一天)和*`H`*(一天中的某个小时)。每个标识符后面跟着一个数字,表示您正在使用的单位。小时范围从`0`到`23`,星期范围从`0`(星期日)到`6`(星期六)。月份中的天数从`1`开始到`31`结束,`L`或`l`代表月份的最后一天。例如,要在每月的第五天中午旋转日志,请使用`$M5H12`。要在每月最后一天的晚上 11 点开始月末会计,请使用`$MLH23`。
>
> 如果您没有指定小时,则默认时间为所选日期的午夜。如果`newsyslog.conf`条目同时列出文件旋转的时间和大小,则`newsyslog`会在满足任一要求时旋转日志。
#### 标志
第七字段是可选的,它指导`newsyslog`对文件本身进行特殊处理。OpenBSD 使用四个标志:
+ ****`Z`****. 使用`gzip(1)`压缩文件。
+ ****`B`****. 对于二进制文件,不要向文件中添加“日志文件已切换”的消息。
+ ****`F`****. 跟踪符号链接。
+ ****`M`****. 用户正在监控此日志。
虽然`B`和`Z`标志在严格意义上不是互斥的,但大多数日志文件只需要其中一个,而且大多数二进制文件本身压缩效果不佳。(默认的`newsyslog.conf`压缩了包过滤日志文件,但这有点奇怪。)如果您看到带有`M`标志的`Z`标志,则在压缩日志之前,旧日志文件将被发送给用户。
### 监控日志
OpenBSD 的`newsyslog`可以在旋转日志之前将日志通过电子邮件发送给用户。如果您仔细控制日志的排序方式,此功能可能很有用。例如,`sudo(8)`日志记录成功的使用情况为`notice`优先级,但失败的用法为`alert`优先级。您可以在`syslog.conf`中将这些拆分为单独的日志文件,如下所示:
!sudo
. /var/log/sudo
*.alert /var/log/sudofail
`/var/log/sudofail`文件现在应只包含`sudo`失败,例如用户输入错误的密码或超出其权限。
现在你可以通过运行带有`-m`标志的`newsyslog`来告诉`newsyslog`检查受监控的日志。(`newsyslog`作为 root 的 cron 作业之一运行。)
要在日志轮换时每次都通过电子邮件将`sudo`失败日志发送给你,你可以将你的账户放入监控字段。
/var/log/sudofail root:wheel 640 30 * $H06 ZM mwlucas
这假设这台机器上`mwlucas`账户收到的电子邮件会到达我这里。确保这一点的最简单方法是将电子邮件转发到`/etc/mail/aliases`。
### 注意
如果你认真对待观察这类故障,请在用户无法访问的日志主机上监控日志。一个成为本地机器 root 的用户可以在日志被电子邮件发送和轮换之前编辑日志。
### 添加 PID 文件
如果`newsyslog`试图轮换和压缩文件,但写入文件的进程仍在写入文件,文件可能会损坏。一些程序在释放其日志文件之前需要受到适当的打击。如何?只需在这里列出 PID 文件,然后`newsyslog`将向该进程 ID 发送`SIGHUP`(类似于`kill -1`)。
注意,PID 文件并不是识别特定进程的非常安全的方法,因为它们容易受到竞争条件和其它攻击。如果服务器有一个轮换日志的命令,那可能比向 PID 文件中指示的进程发送信号更明智的选择。
### 信号名称
要向具有 PID 文件的进程发送除`SIGHUP`之外的信号,请使用不同的*信号名称*。信号名称必须以`SIG`开头,并按名称指定。你可以在`signal(3)`中找到信号的全列表,但软件文档应该告诉你进程需要释放哪个信号才能重新启动其日志文件。此字段是可选的,但如果你使用它,你必须立即在它之前输入 PID 文件的完整路径。
### 要执行的命令
你可以通过在双引号中给出命令的完整路径,让`newsyslog`在轮换日志时运行一个命令,而不是向进程发送信号。虽然此字段是可选的,但它不能与 PID 文件结合使用。你可以使用 PID 文件或命令名称,但不能两者都使用。
## 系统时间
系统时间不正确没有借口。一旦你设置了时区,从任何数量可自由获取的网络时间服务器持续校正 OpenBSD 的时钟是很容易的。特别是虚拟机因时钟偏移而臭名昭著,但时间校正对它们也有效,所以,正如我说的,没有借口。
OpenBSD 包括其自己的 NTP 客户端,OpenNTPD,它被编写为安全且安全。然而,在`ntpd(8)`能够做任何事情之前,它需要一些配置。
### 配置`ntpd(8)`
OpenBSD 自带了一个完全可接受的通用`ntpd`配置,该配置使用公共时服务器。如果您的宿主机在公共互联网上,并且您只想设置系统时间,而不是为其他主机提供时间,请使用默认设置。否则,您必须通过选择时间源并决定`ntpd`是否将接受来自其他机器的时间请求来定制`/etc/ntpd.conf`。
#### 时间冗余
NTP 通过查询远程服务器来获取时间。如果您只有一个服务器,那么假定该时间是正确的。然而,如果您有多个时服务器,时间并不是简单地平均。如果一个时服务器与其他所有时服务器的时间相差甚远,那么该服务器的时间将被丢弃,并从剩余的时间中选择中位数。如果您只有两个时服务器,并且从它们那里获取的时间不同,`ntpd`无法确定哪个是正确的。为了帮助`ntpd`做出合理的决策,请始终列出至少三个时服务器。
#### 时间源
使用`server`、`servers`和`sensor`关键字选择您的时间源。
`server`选项告诉`ntpd`从一个单独的服务器获取时间,该服务器可能有多个 IP 地址。如果是这种情况,`ntpd`会尝试使用第一个 IP 地址。如果第一个地址不可用,它会尝试第二个,依此类推,直到得到响应。如果您有特定的时服务器要使用,请使用`server`选项,并确保列出至少三个时服务器。
server time1.blackhelicopters.org
server time2.blackhelicopters.org
server time3.blackhelicopters.org
`servers`选项告诉`ntpd`从共享相同主机名的多个主机获取时间。默认的`ntpd.conf`包括以下条目:
servers pool.ntp.org
主机`pool.ntp.org`有四个 IP 地址,`ntpd`将尝试从所有这些主机获取时间。
如果您有一个硬件时间传感器,您可以告诉`ntpd`从它那里读取时间。硬件时间传感器包括`nmea(4)`、`udcf(4)`和`mbg(4)`。`sensor`选项告诉`ntpd`使用硬件传感器。如果您投资了硬件时间传感器,您可能会对时间测量发射器和接收器之间的距离,并根据光速延迟调整时间感到足够关心。`correction`关键字允许您指定传感器落后于标准时间的微秒数。
当我写下这段文字时,一个 40 毫秒(ms)的延迟在科学界引起了轩然大波,因为研究人员认为他们可能看到了一个中微子以比光速更快的速度移动,所以我们将 40 毫秒的修正值放入我们的时间传感器中。
sensor nmea0 correction 40000
### 注意
警告:OpenBSD 不是一个实时操作系统。您无论如何都不应该用它来测量中微子速度!
#### 提供时间服务
我只在封闭网络上运行时间服务器,在这些网络上,很少的主机可以访问公共互联网。如果我有硬件时间传感器,我也会运行时间服务器,但大多数时候,我只是使用公共时服务器。
要让`ntpd`从其他主机回答时间查询,请使用`listen on`指令。您可以选择一个 IP 地址或使用星号表示“系统上的所有 IP 地址”。
listen on 192.0.2.87
listen on 2001:db8::aaaa
由于 `ntpd` 没有访问控制,任何可以连接到端口 123/UDP 的主机都可以从这个服务器获取时间。如果您担心这一点,可以使用数据包过滤(在第二十一章和第二十二章中讨论)来限制时间检查仅限于您网络上的主机。OpenNTP 的作者在他的整个公司以及公众中,使用一个带有 16MB(是的,是一个 *M*)RAM 的 MicroVAX 3100 上运行 NTP 进程,处理器使用率不超过 5%,因此 NTP 在现代系统上施加的负载是可以忽略不计的。
现在 `ntpd` 已经配置好了,让我们来使用它。
### 使用 ntpd(8)
您可以缓慢地纠正时间,或者一次性完成。我建议在启动时完全纠正时间,然后让 `ntpd` 在系统运行时缓慢调整系统时钟。这样可以在任何东西依赖它之前纠正时间,但又能持续保持同步。
在启动 `ntpd` 时纠正时间,请使用 `-s` 标志。
ntpd -s
一旦 `ntpd` 从时间服务器收到响应并调整了时钟,您将获得一个命令提示符。在启动时,这会延迟其他软件的启动,以便它有正确的时间,并且当您检查时钟时,应该看到正确的时间。您可以在启动时使用 */etc/rc.conf.local* 中的 `ntpd_flags` 来配置此设置。
ntpd_flags='-s'
如果运行中的系统时钟不准确,并且您正在运行可能会因时钟倒退或时间跳跃(如许多数据库)而损坏的软件,您可能需要告诉 `ntpd` 更慢地纠正时钟。为此,请不带任何标志运行 `ntpd`,或者在 `rc.conf.local` 中设置它,以便在启动时以这种方式运行。
### 注意
您的起始时间可能偏差太大,以至于在任何合理的时间内都无法逐渐调整到正确的时间。为了修复时钟,在您能够关闭敏感软件时安排时钟更改,并确保在之后运行 NTP 以确保问题得到解决。最好是立即修复时钟并完成它。
## 硬件传感器
传感器是检查硬件健康和状态的物理探头。制造商在硬件中放置了越来越多的传感器,为操作系统提供低级硬件信息。OpenBSD 支持广泛的硬件传感器,并使用 `sensorsd` 守护进程查询它们并处理错误状态。
解决许多硬件错误需要关闭机器,但提前警告某个组件已停止工作,将硬件故障从意外的白天灾难转变为下班后的烦恼。一些硬件,如热插拔硬盘,一旦知道硬件已故障,就可以在不中断服务的情况下更换。
### 设备驱动程序
每个物理传感器都有一个设备驱动程序。设备驱动程序从硬件中提取信息,并在 sysctl(在第十八章中讨论)中发布。`sensorsd`读取 sysctl 值,并在它们更改或超过临界值时采取行动。例如,以下是我笔记本电脑的与传感器相关的 sysctl 值:
$ sysctl hw.sensors
hw.sensors.acpitz0.temp0=67.00 degC (zone temperature)
hw.sensors.acpiac0.indicator0=On (power supply)
hw.sensors.acpibat0.volt0=11.10 VDC (voltage)
hw.sensors.acpibat0.volt1=12.35 VDC (current voltage)
hw.sensors.acpibat0.power0=0.00 W (rate)
hw.sensors.acpibat0.watthour0=2.61 Wh (last full capacity)
hw.sensors.acpibat0.watthour1=0.30 Wh (warning capacity)
hw.sensors.acpibat0.watthour2=0.06 Wh (low capacity)
hw.sensors.acpibat0.watthour3=9.57 Wh (remaining capacity), OK
hw.sensors.acpibat0.raw0=2 (battery full), OK
hw.sensors.cpu0.temp0=81.00 degC
这款相对简单通用的硬件具有两个温度传感器和各种功率传感器。根据你的硬件,你可以获得数百行传感器输出。
许多 RAID 控制器有自己的传感器,并在阵列失败时报告。这里,我们看到由 AMI RAID 控制器提供的三个虚拟磁盘:
hw.sensors.ami0.drive0=online (sd0), OK
hw.sensors.ami0.drive1=degraded (sd1), WARNING
hw.sensors.ami0.drive2=failed (sd2), CRITICAL
如果没有传感器,你需要查看驱动器外壳上的闪烁灯光。或者你可以听那令人讨厌的“哔哔哔哔”声,这在 5,000 个服务器风扇、空调和某人其他硬件的哔哔声中是如此容易听到。
### 注意
一些传感器需要智能平台管理接口(IPMI)。这是一个默认在 OpenBSD 中禁用的内核功能,因为它会使某些机器表现得很糟糕。第十八章讨论了启用 IPMI。
设备驱动程序会自动连接到传感器,值会自动进入内核,但要以任何自动化的方式对这些结果进行处理,你需要`sensorsd(8)`,或者你需要配置一个基于 SNMP 的外部管理系统并使用`snmpd(8)`。我们将在这里查看使用`sensorsd(8)`。在第十六章中讨论了使用`snmpd(8)`。
### 传感器配置
传感器守护进程`sensorsd(8)`监视传感器监控数据。它记录更改,并在需要时执行命令。由于所有硬件都不同,所有环境都不同,默认情况下,`sensorsd`只注意传感器读数的变化。要采取行动,你必须配置`/etc/sensorsd.conf`中的`sensorsd`。
#### 传感器类型
OpenBSD 支持许多类型的传感器,如表 15-2 所示。
表 15-2. 表 15-2:支持的传感器类型
| 名称 | 功能 |
| --- | --- |
| 温度 | 温度(摄氏度) |
| 风扇 | 风扇速度(RPM) |
| 电压 | 直流电压 |
| 交流电压 | 交流电压 |
| 电阻 | 欧姆电阻 |
| 功率 | 瓦特 |
| 电流 | 安培 |
| 瓦特时 | 功率容量 |
| 安培时 | 功率容量 |
| 指示器 | 设备相关是/否 |
| 原始值 | 设备相关值 |
| 百分比 | 设备相关百分比 |
| 照度 | 照明 |
| 硬盘驱动器 | 硬盘 |
| 时间差 | 操作系统与硬件之间的时间差 |
| 湿度 | 百分比湿度 |
| 频率 | 微赫兹 |
| 角度 | 微度 |
你需要检查你的硬件手册,才能了解如何有效地使用这些传感器中的某些传感器。
一些传感器似乎有重叠。例如,为什么 OpenBSD 有那么多关于功率的单独值,当你可能通过一些数学运算得到一个共同的功率表时?原因是这些是实际传感器报告的值,开发者更愿意给你实际的测量值。OpenBSD 确实执行了一些数据合理化,但仅限于简单数据;例如,所有温度传感器都被归一化到摄氏度。
现在,让我们看看你可以用这些传感器做什么。
#### sensorsd.conf 中的设置
文件*sensorsd.conf*有示例条目,但由于环境差异很大,它们都被注释掉了。它使用`termcap`风格的配置语法,类似于*/etc/remote*(见第五章“引导过程”)或*/etc/login.access*(见第六章“用户管理”),其中用冒号分隔条目中的术语。每个条目以要测量的传感器开始,后跟属性名称和设置。
例如,这是默认*sensorsd.conf*中的一个温度传感器的条目:
hw.sensors.lm0.temp0:high=50C
对于传感器`lm0.temp0`,`high`属性设置为`50C`。
`sensorsd`支持四个属性:
+ ****`high`****. 上限
+ ****`low`****. 下限
+ ****`command`****. 当超过限制或状态改变时运行的命令
+ ****`istatus`****. 忽略此状态
报告的传感器类型值取决于什么是有意义的。对于温度和电压,当高限和低限有意义时,一些传感器会报告特定的值。前面显示的 RAID 控制器报告驱动器为降级、故障或健康。报告标量值的硬盘传感器没有用,因为你想知道 RAID 容器是否健康或驱动器是否已故障。没有中间地带。
对于单个传感器,你可以有高值和低值。例如,在大多数数据中心,温度可能没有低值,但电压肯定有。我在各种奇怪的地方工作,并不是所有这些地方都有干净的电源。
hw.sensors.acpibat0.volt0:low=11.0V:high=13.0V
有这样的行,如果我的笔记本电脑的电源低于 11 伏或高于 13 伏,我会知道。
一些系统可能具有数十种特定类型的传感器,这可能会使配置变得复杂。如果我的主板有 15 个温度传感器,我不想单独配置每个。幸运的是,你可以按类型批量配置传感器,而且由于我不关心哪个温度传感器会超过 80 摄氏度(如果任何一个超过,我想要一个警报),这很有效。
temp:high=80C
当应用此规则时,`sensorsd`首先寻找特定传感器的配置项。如果没有找到该特定规则,它会寻找通用规则。你可以为大多数温度传感器设置一个规则,然后为特定传感器覆盖它,如下所示:
hw.sensors.lm0.temp5:high=90C
temp=80C
这条规则说明,我的大多数温度传感器在 80 度时发出警报,但一个特定的传感器直到 90 度才会发出警报。
我关心温度,但我不在乎我的花哨键盘看到没有光,想要触发其背光。你可以使用 `istatus` 关键字忽略一个传感器或传感器类型。
illuminance:istatus
你应该根据你的环境和设备完全忽略某些类型的警报。自己做出决定。
#### 传感器触发动作
当硬盘故障时在 */var/log/daemon* 中有一个条目是好的,但如果系统会发送电子邮件、给你发短信或触发你的监控系统就更好了。它应该做些事情——*任何事*——不需要你登录并查看日志文件。幸运的是,`sensorsd` 可以在检测到问题或超过阈值时运行任意命令,使用 `command` 属性。
由于传感器种类繁多及其可能的错误状态和条件,`sensorsd` 没有细粒度的“对于错误运行此命令,但对于恢复运行那个其他命令。” 有太多可能的错误状态和条件,这使得这样做没有意义。相反,`sensorsd` 在超过任何阈值或任何状态变化时运行单个命令,包括启动时和单个传感器的状态从“未知”变为它开始时的状态。
考虑这个 *sensorsd.conf* 条目:
temp:high=80C:command=/sbin/reboot
初看,这看起来像是“如果温度高,重启机器。” 你认为这将毫无疑问地杀死任何占用你发热 CPU 的失控进程(完全不考虑除了 CPU 之外的其他硬件也会产生热量的事实),但 `sensorsd` 将在温度状态变化时运行该命令。状态在启动时变化,当第一次读取温度时,这意味着你的系统将启动,然后立即重启。你的脚本需要智能。
为了使脚本编写更容易,`sensorsd` 可以将一组变量传递给脚本:
+ ****`%1`****. 值是否在 *sensorsd.conf* 中设置的范围内?这可以是 `below`、`above`、`within`、`invalid` 或 `uninitialized` 之一。
+ ****`%n`****. 传感器编号。
+ ****`%s`****. 传感器状态。
+ ****`%x`****. 传感器所在设备。
+ ****`%t`****. 传感器类型。
+ ****`%2`****. 传感器的当前值。
+ ****`%3`****. 传感器的下限
+ ****`%4`****. 传感器的上限。
你可能运行一个温度命令如下:
temp:high=80C:command=/usr/local/script/temp %1 %2 %n
你的脚本 */usr/local/script/temp* 将接受三个参数:错误条件、温度和传感器名称。你的脚本将检查这些值,看看是否需要重启。
使用 `sensorsd`、正确的时间管理和日志文件管理,你的 OpenBSD 系统可以很大程度上自我管理。
在下一章中,我们将探讨 OpenBSD 如何照顾其他主机。
* * *
^([41]) 嘿,我快想不出怎么让 lasnyder 不高兴了——至少是合理的办法。
## 第十六章 网络服务器
*幕后工作,*
*处理重要事务,*
*守护进程在这里。*
 OpenBSD 基础系统包括几个服务器来支持网络。本章涵盖了以下网络服务器:
+ 小服务器处理器 `inetd`
+ 打印机守护进程 `lpd`
+ DHCP 守护进程 `dhcpd`
+ TFTP 守护进程 `tftpd`
+ SNMP 代理 `snmpd`
+ SSH 服务器 `sshd`
这本杂乱无章的小精灵支持了即将到来的章节中涵盖的功能。
## inetd 小服务器处理器
`inetd(8)` “超级服务器”处理不常使用的网络服务的传入网络请求。毕竟,许多系统没有稳定的 FTP 请求流,为什么还要一直运行 FTP 守护进程呢?相反,`inetd` 监听传入的网络请求,当 FTP 请求到达时,它启动 FTP 服务器并将请求传递给它。其他经常(但不总是)通过 `inetd` 运行的常见服务包括 ident、finger 和 TFTP。许多这些服务也可以独立运行,如果应用程序的使用需要的话。
`inetd` 还处理一些非常小且很少使用的功能,这些功能在 `inetd` 本身内实现比通过调用单独的程序实现更容易。这些功能包括 `discard`(将接收到的任何数据丢弃到 */dev/null* 的无底洞中),`chargen`(输出一串字符),和 `echo`(重复你发送给它的任何内容)。大多数这些服务在现代互联网上不是必需的,并且默认情况下被禁用,但如果你需要,你可以访问它们。
### 配置 inetd
你在 */etc/inetd.conf* 中配置 `inetd`。以下是 OpenBSD FTP 服务器的默认 `inetd` 配置:
1ftp 2stream 3tcp 4nowait 5root 6/usr/libexec/ftpd 7ftpd -US
ftp stream tcp6 nowait root /usr/libexec/ftpd ftpd -US
你首先会注意到这些条目已被注释掉。OpenBSD 的默认 `inetd` 默认只提供身份服务器 `identd(8)` 和两个时间服务。
第一个字段是服务名称(在这种情况下为 `ftp`)**1**。此字段中的名称必须与 */etc/services* 中的名称匹配。`inetd` 程序使用 *services* 文件来执行服务查找,以确定它必须监听哪些端口。要更改 FTP 服务器运行的 TCP/IP 端口,请更改 */etc/services* 中 FTP 的端口。(你也可以更改第一个字段以使用通常在所需端口上运行的服务名称,但我发现用错误的名字开始我的 FTP 服务器条目只会让我头疼。)
第二个字段是套接字类型(在这种情况下为 `stream`)**2**。此字段决定了这种连接的类型。所有 TCP 连接都是 `stream` 类型,而 UDP 连接是 `dgram` 类型。`inetd` 程序也支持其他类型的连接,但它们很少使用。如果你正在考虑使用它们,要么你正在阅读需要此类连接的软件的文档,要么你错了(可能后者的可能性更大)。
第三字段是第 4 层网络协议,通常是`tcp` **3**,`udp`,`tcp6`或`udp6`。如果您想在 IPv4 和 IPv6 上提供服务,您需要为每个协议分别创建条目。这就是为什么 FTP 服务器有两个其他方面相同的配置。`inetd`程序还支持 RPC 服务,其类型为`rpc/udp`或`rpc/tcp`。
第四字段(在本例中为`nowait`)**4**表示`inetd`是否应该等待服务器程序关闭连接,或者只是启动程序然后离开。一般来说,基于 TCP 的守护进程使用`nowait`,而基于 UDP 的守护进程使用`wait`。(有少数例外。)
第五字段(在本例中为`root`)**5**指定了服务器守护进程运行的用户。许多使用`inetd`的程序必须以 root 身份运行,因为它们可以影响多个用户或接受更具体的登录,但一些较小的程序有专门的未授权用户。
第六字段是当连接请求到达时`inetd`运行的程序的全路径 **6**。在`inetd`中实现的服务路径为`internal`。FTP 服务器位于`/usr/libexec/ftpd`。
最后,最后一个字段给出了启动服务器程序的命令,包括您想要的任何命令行参数。此配置使用`-US`参数运行 FTP 服务器 **7**。
### 限制传入连接
小白用户偶尔会尝试通过发送比服务器能处理的更多的连接请求来将服务器从互联网上移除。`inetd`程序每个服务每分钟最多接受 256 个连接。如果一个服务接收过多的连接请求,`inetd`将记录问题并停止回答该服务的请求 10 分钟。
### 注意
IPv4 和 IPv6 版本分别限制,因此如果请求在协议族之间均匀分配,您每秒可以接受 512 个 FTP 连接。您可以在启动`inetd`时使用命令行标志全局覆盖此限制,或者您可以在每个服务的基础上进行配置。
`-R`标志控制`inetd`每分钟和每个服务接受的连接数。例如,要每分钟接受 1000 个请求,您需要在`/etc/rc.conf.local`中设置以下内容:
inetd_flags='-R 1000'
您可以通过编辑服务的`inetd.conf`条目中的`wait/nowait`字段来设置每个服务的限制。在`wait`或`nowait`条目后添加一个点,然后跟您希望服务每分钟被调用的次数。例如,如果您有一个只有几个朋友使用的 FTP 服务器,您可以将服务器限制为每分钟 10 个请求,如下所示:
ftp stream tcp nowait.10 root /usr/libexec/ftpd ftpd -US
ftp stream tcp6 nowait.10 root /usr/libexec/ftpd ftpd -US
现在,如果一分钟内超过 10 个连接请求到达,`inetd`将停止服务 FTP 请求十分钟。攻击者仍然可以使用此方法使您的 FTP 服务离线,但不会使整个服务器离线。至少这样您可以选择您的故障模式以及何时达到它。
## lpd 打印守护进程
OpenBSD 包含了在 Unix-like 操作系统中常见的 `lpd(8)` 打印守护进程。`lpd` 守护进程有支持成千上万种不同打印机的选项,但找到支持任何特定打印机的正确选项组合可能是一个挑战。
在 OpenBSD 上使用打印机的最简单方法是通过 PostScript 服务器,这正是我将在这里介绍的方法。许多现代打印机,尤其是流行的多功能传真/扫描/打印机组合,都支持 PostScript,你会发现每个办公室的打印服务器也是如此。
系统中知道的每个打印机都需要在 */etc/printcap* 中有一个条目,即打印机功能数据库。这是一个另一种 `termcap(5)` 风格的配置文件。你不需要了解打印机的所有信息就可以在这里更改设置。此条目只需要打印服务器的主机名或 IP 地址以及你想要访问的打印机的打印服务器名称。然后使用以下模板:
lp|printername:
:sh=:
:rm=printservername:
:sd=/var/spool/output/printername:
:lf=/var/log/lpd-errs:
:rp=printername:
第一行给出打印机的名称。每个打印机可以有任意数量的名称,名称之间由管道(`|`)符号分隔。Unix-like 系统上的默认打印机名为 `lp`,因此请确保至少有一台打印机附有该名称。另一个名称应该是打印服务器用于该打印机的名称(例如 `Billing`)。(Microsoft 打印服务器通常在几个不同的名称下共享一台打印机,并且每个名称打印的内容不同,因此请确保使用代表 PostScript 功能的名称。)
其他行列出属性:
+ 默认情况下,`lpd` 在每个打印作业前都会加上一个包含作业名称、编号、主机和其他识别信息的页面。当人们按页付费打印时,这曾经很重要,但除非你在一个只有一台大型打印机的环境中,否则这可能会浪费纸张。`:sh:=\` 条目会抑制此页面。
+ `:rm=` 属性给出了打印服务器的主机名或 IP 地址。你必须能够通过此名称 ping 通打印服务器。
+ 如果每个打印机都有一个唯一的打印队列目录,给定 `:sd=` 属性,则打印效果最佳。打印机守护进程将正在前往打印服务器的文档存储在此目录中。此目录必须由用户 root 和组 daemon 拥有。
+ 几台打印机可以共享一个公共日志文件,由 `:lf=` 属性表示。
+ 最后,使用 `:rp=` 属性指定远程打印机名称。这是唯一一个不以反斜杠结尾的属性。
总是以换行符结束 */etc/printcap*。我通常使用一个完整的空白行,以确保无误。
现在你已经配置了打印机,你可以通过在 *rc.conf.local* 中添加以下条目在启动时启动 `lpd`:
lpd_flags=""
任何时间编辑 */etc/printcap* 后,都使用 `/etc/rc.d/lpd restart` 重新启动 `lpd`。
最后,使用 `lpq(1)` 查看打印队列,并观察 */var/log/lpd-errs* 文件以查找问题。
## DHCP 服务器 dhcpd
DHCP 是在 IP 网络上动态配置客户端的标准方法。你可能知道 DHCP 是一种为计算机提供基本 IP 信息的方法,但它也可以分发路由器和电话等嵌入式设备的配置文件,将无盘机器指向它们的内核和用户空间,等等。
OpenBSD 包含了一个经过大量修改的 ISC DHCP 服务器,`dhcpd(8)`。在这里,我们将介绍如何使用 `dhcpd` 在共享以太网系统中配置动态客户端的基础知识。在 第二十三章 中,我们将讨论使用 DHCP 配置无盘工作站细节。
### DHCP 的工作原理
寻求 DHCP 信息的客户端会在本地网络中广播一个请求,请求某人——任何人——给它一个网络配置。如果你的 DHCP 服务器位于该以太网段上,它会直接响应。如果它位于另一个网络段上,该网络段的路由器可以将 DHCP 请求转发到你的服务器,然后服务器将为客户端提供一个配置,并维护一个已分配哪些唯一配置值(如 IP 地址)给哪些客户端的列表。分配给客户端的配置称为 *租约*。像所有租约一样,DHCP 租约会过期,必须续订才能有效。
客户端可以请求某些 DHCP 功能以支持其操作。例如,Microsoft 客户端请求网络 Windows Internet Name Service (WINS) 服务器的 IP 地址,VoIP 桌面电话请求它们的配置文件,而无盘系统(在第二十三章定制 OpenBSD 中讨论)询问它们在哪里可以找到它们的内核和用户空间。DHCP 服务器可以提供这些信息,也可以不提供。
DHCP 服务器通过客户端连接到网络的网卡 MAC 地址唯一标识每个客户端。要找出客户端从 DHCP 服务器接收了什么信息,获取客户端的 MAC 地址并在 */var/db/dhcpd.lease* 文件中搜索它。
### 配置 dhcpd(8)
在 */etc/dhcpd.conf* 中配置 `dhcpd`。默认的 *dhcpd.conf* 文件包含一个适合小型办公环境的示例配置,以及一个无盘客户端的示例配置。
我将假设你在你的网络上运行一个 DHCP 服务器,并且这个服务器是 DHCP 服务的权威服务器。(OpenBSD 的 DHCP 服务器也支持集群以实现容错。)
在配置 `dhcpd` 以动态配置客户端之前,你需要了解一些关于你网络的事实:
+ 域名
+ DNS 服务器
+ IP 网络和子网掩码
+ 网络中用于 DHCP 客户端的 IP 地址范围
+ 默认路由器
一旦你有了这些信息,你就可以组装一个简短的 *dhcpd.conf* 文件。以下是一个示例:
1 option domain-name "blackhelicopters.org";
2 option domain-name-servers 192.0.2.5 192.0.2.10;
3 subnet 198.51.100.0 netmask 255.255.255.0 {
4 option routers 198.51.100.1;
5 range 198.51.100.51 198.51.100.100;
}
所有从这个主机获取配置的主机都被告知它们的域名是*blackhelicopters.org**1*,并且它们应该使用名称服务器 192.0.2.5 和 192.0.2.10**2**。客户端可以被配置为忽略或覆盖此 DHCP 配置,但你无法阻止本地系统管理员自挂东南枝。
每个子网都需要自己的配置。即使你只有一个子网,你也必须有一个定义该子网 IP 网络的`subnet`语句,以便`dhcpd`可以确定哪些客户端获得哪些配置。以下示例定义了网络在 198.51.100.0/24**3**上的客户端配置。以下方括号内的所有内容仅适用于此子网上的主机。
**4**处的`routers`选项标识了此网络的默认网关。因为`dhcpd`服务器不会让你为客户端定义额外的静态路由,所以你的本地网络路由器必须具有到达目的地的正确路由。如果你在本地网络上有多个网关,你的默认路由器应向 DHCP 客户端发送 ICMP 重定向以纠正其路由。(你不会单方面从防火墙中阻止 ICMP,对吧?)
`range`关键字给出了 DHCP 服务器可以分配给客户端的 IP 地址。在这个例子中,DHCP 服务器控制了从 198.51.100.51 到 198.51.100.100 的地址,包括**5**。如果有 52 个动态客户端同时连接,最后一个客户端将无法获得地址。
此配置应使你的客户端能够连接到网络。
### 静态 IP 地址分配
你可以通过在配置中指定客户端的以太网地址并使用`subnet`语句中的段落来告诉你的 DHCP 服务器为特定的主机分配特定的地址。以下是一个添加了静态条目的早期 DHCP 配置示例:
subnet 198.51.100.0 netmask 255.255.255.0 {
option routers 198.51.100.1;
host lucas-desktop {
hardware ethernet 00:cf:01:b1:9b:07;
fixed-address 192.0.2.254;
}
}
我已经找到了我的工作站的 MAC 地址,并使用它为该机器分配了一个静态 IP 地址。此客户端机器从子网定义继承了默认路由器以及任何默认 DHCP 信息。
### 启用 dhcpd
在`rc.conf.local`中启用`dhcpd`。
dhcpd_flags=""
如果你只有一个面向网络的接口,`dhcpd`将自动在该接口上监听 DHCP 请求。如果你有多个接口,请将接口名称作为参数。例如,以下是如何告诉`dhcpd`只在该接口`fxp1`上监听请求:
dhcpd_flags="fxp1"
接口名称必须是`rc.conf.local`中`dhcpd`参数的最后一个。如果`dhcpd`需要处理多个接口,接口列表必须跟在其他任何参数之后在`dhcpd_flags`中。
### dhcpd 和防火墙
OpenBSD 数据包过滤系统包括*表格*,这些是数据包过滤器应用规则到的一组 IP 地址。来自表格中 IP 地址的流量可以被阻止,带宽被限制或优先处理,或者被允许通过。每个表格都有一个唯一名称。
`dhcpd` 服务器可以将地址添加到包过滤表中,从而根据 IP 地址是否已租用动态更改防火墙规则。在这里,我们将探讨如何配置 `dhcpd` 以向包过滤表提供地址。第二十一章 讨论了如何配置包过滤规则以处理来自 `dhcpd` 的地址。
DHCP 将其地址池中的 IP 地址视为三种状态之一:已租用、已放弃或已更改。*已租用*的地址是指分配给连接到网络的宿主机的地址。使用 `-L` 选项向 `dhcpd` 提供已租用地址的包过滤表名称,然后配置包过滤规则以允许或拒绝这些地址访问网络的其他部分。
*已放弃*的地址是指已分配给宿主机但当前未使用的地址。在实践中,这意味着如果你关闭你的笔记本电脑,DHCP 服务器将认为分配给它的 IP 地址已放弃。问题是未经授权的用户可能会尝试从地址池中获取未使用的地址来接入网络,而不通过 DHCP 服务器。为了解决这个问题,向包过滤规则提供未使用地址的列表,并为非法网络主机提供他们自己的特殊包过滤规则。使用 `-A` 选项告诉 `dhcpd` 已放弃地址的包过滤表名称。
如果宿主机更改了地址,尽管 DHCP 服务器的配置说明,DHCP 服务器仍认为该地址已更改,`dhcpd` 可以将其新地址添加到更改地址表中。使用 `-C` 选项告诉 `dhcpd` 更改地址表的名称。(在第二十一章中,我们将对这些表进行一些有趣的配置。)
dhcpd_enable="-A table1 -L table2 -C table3 fxp1"
### 注意
静态 IP 地址分配不会进入表。如果你为宿主机分配了静态地址,你必须手动为该地址配置防火墙规则。
## TFTP 守护进程 tftpd
简单文件传输协议(TFTP)用于在网络上传输文件。与 FTP 不同,TFTP 不包含身份验证。任何可以访问 TFTP 服务器的人都可以从它上传或下载文件。
TFTP 是一种不灵活的协议。如果没有代理或翻译设备内的某种智能,它无法通过网络地址转换工作,并且没有像 FTP 和 SFTP 那样的交互会话。TFTP 最常用于复制嵌入式设备(如路由器)的配置文件和操作系统映像。
OpenBSD 使用 TFTP 来引导无盘系统,如第二十三章所述。
### 指定 tftpd 目录
OpenBSD 的 `tftpd(8)` 从目录中提供服务,就像一个 Web 服务器。传统上,这个目录是 */tftpboot*,但在此情况下不要遵循传统(您不希望 TFTP 用户填满您的服务器根分区!)。如果您在根分区上使用 */tftpboot*,请确保您的 TFTP 客户端无法写入该目录。(您可以创建一个 */tftpboot* 分区。)通常,我会创建 */var/tftpboot* 并告诉 `tftpd` 使用该目录作为其根目录。如果您的手指习惯于输入 `/tftpboot`,请创建一个符号链接。
要启用 `tftpd`,请在 `rc.conf.local` 中设置 `tftpd_flags` 为 TFTP 根目录。
tftpd_flags="/var/tftpboot"
`tftpd` 将 `chroot` 到您指定的目录,因此 `tftpd` 无法访问此目录之外的文件。
### tftpd 和文件
TFTP 使用文件权限作为访问控制方法。因为 TFTP 服务器上的所有文件都可以被任何可以访问服务器端口的用户读取,所以 TFTP 只允许客户端在其根目录中读取文件,前提是这些文件是全局可读的。要使它们全局可读,请执行以下操作:
chmod +r /var/tftpboot/filename
类似地,`tftpd` 不会允许任何人上传文件,除非该名称的文件已经存在并且是全局可写的。这意味着任何知道文件名的人都可以覆盖它,因此请将重要文件设置为只读。如果攻击者无法写入文件,他就无法填满您的硬盘。
要通过 TFTP 创建文件,以便您可以上传不存在的文件,请使用 `-c` 选项运行 `tftpd`。
`tftpd` 以 root 身份启动,以便绑定 UDP 端口 69,但它随后会降低权限并以无特权的用户 `_tftpd` 运行。`tftpd` 创建的任何文件都将属于其用户。作为一般规则,TFTP 根目录中的文件不应由 `_tftpd` 拥有,以确保服务器无法影响其提供的服务器文件。
### tftpd 日志记录
您应该记录您的 TFTP 传输。使用 `-v` 标志将事务日志发送到 `syslogd`。
tftpd_flags="-v /var/tftpboot"
`tftpd` 日志使用 FTP 功能将消息记录到 */var/log/daemon*。
### 测试 TFTP 服务器
使用 `tftp(1)` 测试您的 TFTP 服务器。
$ tftp caddis
tftp> get testboot.iso
Received 20879569 bytes in 10.4 seconds
在下载文件时,您不会看到任何友好的散列标记,并且您无法切换到另一个目录或列出 TFTP 服务器的内容。一旦测试完成,使用 `quit` 结束您的 TFTP 会话。
在您设置好 TFTP 客户端和服务器之后,您将准备好为无盘 OpenBSD 机器、路由器操作系统镜像或其他任何您需要的设备提供服务。
## SNMP 代理 snmpd
SNMP 是从网络设备收集信息的既定标准。许多不同厂商的不同设备都支持 SNMP 作为管理协议。
OpenBSD 包含一个 SNMP 代理 `snmpd(8)`,它支持所有常规的 SNMP 功能,并提供对 OpenBSD 特定功能(如数据包过滤)的可见性。
SNMP 根据标准的客户端/服务器模型工作。SNMP 客户端(通常是执行网络管理或监控的服务器)查询运行在网络设备上的 SNMP 服务器(或 *代理*)。SNMP 代理 `snmpd` 从本地系统收集信息并将其返回给客户端。
在传统的 SNMP 中,具有正确权限的 SNMP 客户端还可以请求 SNMP 代理修改其设备。大多数类 Unix 操作系统都设计为在命令行中进行配置,并且通常不接受 SNMP 的写请求。OpenBSD 跟随这一趋势,我们将特别关注只读 SNMP。
除了让 SNMP 代理响应 SNMP 客户端的请求外,代理还可以将 SNMP 陷阱发送到网络上的某个 *陷阱接收器*。SNMP 陷阱与 `syslogd(8)` 消息非常相似,但它们遵循 SNMP 所需的特定格式。
### 注意
OpenBSD 不包括 SNMP 陷阱接收器。如果您需要,请查看 `net-snmp` 包中的 `snmptrapd`。
### SNMP MIBs
SNMP 通过管理信息库(MIB)管理信息,MIB 是一个树状结构,包含以 ASN.1 格式的分层信息。每个 SNMP 代理都有一个从本地系统提取信息列表,这些信息按分层 SNMP MIB 安排,具有非常一般的主要类别,如网络、物理、程序等。
将 MIB 树想象成一个组织良好的文件柜,其中每个抽屉都保存着特定的信息,抽屉内的文件则保存着特定的事实。同样,最顶层的 MIB 包含了其下 MIB 的列表。
#### MIB 参考信息
MIBs 可以通过名称或编号进行引用。例如,这里是一个从 OpenBSD 测试机器中提取的 MIB:
interfaces.ifTable.ifEntry.ifDescr.1 = STRING: "em0"
这个 MIB 的第一个术语 `interfaces` 告诉我们,我们正在查看这台机器的网络接口。如果这台机器没有接口,这个类别甚至不会存在(尽管 OpenBSD 机器将始终至少有一个回环接口)。`ifTable` 是 *接口表*,它列出了系统上的所有网络接口。`ifEntry` 字段显示一个特定的条目,而 `ifDescr` 则意味着我们正在查看这个接口的描述。这个 MIB 可以表示为“这台机器上的接口编号 1 被称为 `em0`。”
MIBs 也可以用数字表示,大多数 SNMP 客户端在数字 MIB 中以原生方式工作。您的管理工具应该能够将数字和名称之间进行转换,但为了以防您感到非常惊讶,这里提供了之前示例的数字形式:
.1.3.6.1.2.1.2.2.1.2.1 = STRING: "em0"
用文字表达,这个 MIB 由五个部分组成,部分之间用点分隔。用数字表达,MIB 有 11 个部分。它们不应该是一样的吗?嗯,数字 MIB 更长,因为它包括了默认地址 .1.3.6.1.2.1,这对应于 *.iso.org.dod.internet.mgmt.mib-2*,这是在互联网上使用的 MIB 的标准子集。大多数 SNMP MIB 都以这个字符串开头,所以管理工具就不再打印出这个名称了。
#### MIB 定义
OpenBSD 支持两组 MIB:
+ 标准的主机 MIB,这是每个网络管理系统都理解的。这些信息包括网络和磁盘空间利用率、系统上运行的软件等。
+ 用于 OpenBSD 特定功能(如数据包过滤器、网络故障转移、桥接等)的 MIB。大多数网络管理系统不会默认理解 OpenBSD 特定的 MIB,所以你将想要让你的管理系统了解 OpenBSD 的 MIB。
MIB 是根据 MIB 文件中记录的非常严格的语法定义的。例如,`snmpd`在`/usr/share/snmp/mibs`中包含了 OpenBSD 特定功能的 MIB 文件。这些文件以纯文本形式编写,使用非常正式的 ASN.1 语法。虽然你可以仅用大脑阅读和解释它们,但我强烈建议将它们复制到你的网络管理工作站上,并使用 SNMP 客户端来检查它们。
MIB 浏览器解释 MIB 文件,并以完整的树状结构展示它们,包括树中每一部分的定义和每个 MIB 的描述,这些描述来自 MIB 文件。一般来说,你将 MIB 输入到 MIB 浏览器中,它会显示其数字和文字描述,并提供查询该 MIB 的 SNMP 代理的能力。
如果你已经在你的 OpenBSD 工作站上安装了 MIB 浏览器,请使用`mbrowse`包。如果你不想使用图形界面,请使用`net-snmp`包来获得一系列完整的命令行 SNMP 客户端工具,但要做好准备输入一些长的命令行。
### SNMP 安全
SNMP 最常见的替代缩写是“安全?不是我的问题!”这听起来不友好,但却是真的。你应该只在防火墙后面或信任的网络中使用 SNMP。如果你必须在裸露的互联网上使用 SNMP,请使用数据包过滤来阻止公众查询你的 SNMP 服务。SNMP 代理在 UDP 端口 161 上运行,因此只允许你的管理主机访问你的 SNMP 主机上的该端口。
SNMP 通过*团体*提供基本的安全保障。如果你阅读了 SNMP 文档,你会看到各种解释为什么团体不等于密码,但就系统管理员而言,团体就是密码。
大多数 SNMP 代理默认有两个团体:`public`(只读访问)和`private`(读写访问)。OpenBSD 的`snmpd`守护进程默认支持这两个团体。你的第一个任务之一将是将这些团体名称更改为全世界都不知道的名称。就像密码一样,团体名称应该难以被入侵者猜测,但对你来说容易记住。
如你所料,SNMP 已经有过各种版本。版本 1 是第一次尝试。版本 2c(SNMPv2c)是更常见的更新。版本 3(SNMPv3)使用加密来保护线上的数据,并且它包括强大的身份验证。在实践中,很少有供应商真正使用它,因为它非常复杂。`snmpd`守护进程对 SNMPv3 有部分支持。在这里,我们将专注于完全支持的 SNMPv2c。
### 配置`snmpd`
在`*/etc/snmpd.conf*`中配置`snmpd`。配置格式是一系列文本语句。定义新的共同体字符串会覆盖`public`和`private`的默认值。
我们首先定义新的只读和读写共同体字符串,如下所示:
read-only community hansteen
read-write community henning
通常,大多数`snmpd`配置语句看起来像这两样。`snmpd.conf(5)`手册页列出了所有有效的`*snmpd.conf*`配置语句。
每个 SNMP 系统都应列出联系人、描述和位置,如下例所示:
system contact "mwlucas@michaelwlucas.com"
system description "Web server"
system location "Rack Row 9, Cabinet 6, Under the Meal Replacement Bars"
许多网络管理系统会自动拉取这些信息以填充数据库。在这里,我为我的系统定义了这些值。为你的系统创建类似的条目。
默认的`*snmpd.conf*`只监听本地主机 IP 地址,127.0.0.1,因此外部主机无法联系 SNMP 守护进程。如果你想监听所有可用的地址,取消注释指定地址的行,如下所示。
listen_addr="127.0.0.1"
listen on $listen_addr
或者,你可以提供一个接口 IP 地址,使`snmpd`监听特定外部 IP 地址,对于具有多个地址的机器。
listen_addr="192.0.2.5"
使用此配置,`snmpd`可以提供有关你的系统的信息。在`*/etc/rc.conf.local*`中启用它。
snmpd_flags=""
这将使`snmpd`在启动时启动,或者你可以运行`*/etc/rc.d/snmpd*`。
### 调试`snmpd`
SNMP 调试可能是一个令人烦恼的协议。一方面,因为它使用 UDP,所以没有简单的方法来测试到代理的连接性。此外,它运行得相当安静,因为它不记录查询。
为了验证你的网络管理系统中的查询是否到达你的服务器,尝试以详细模式和调试模式运行`snmpd`。
snmpd -vd
startup
snmpe_bind: binding to address 0.0.0.0:161
当一个 SNMP 查询到达你的服务器时,你应该看到服务器解析请求。同样地,`snmpd`在告诉你为什么它不能提供答案方面做得很好。
snmpe_parse: 192.0.2.197: wrong read community
来自对不存在 MIB 请求的错误,如以下所示,理解起来有点困难。
snmpe_parse: 192.0.2.197: SNMPv1 'henning' context 1 request 1141724535
snmpe_parse: 192.0.2.197: oid iso.org.dod.internet.private.enterprises.2041
snmpe_parse: 192.0.2.197: SNMPv1 'henning' context 0 request 1141724536
snmpe_parse: 192.0.2.197: oid iso.org.dod.internet.private.enterprises.2041
snmpe_parse: 192.0.2.197: invalid varbind element, error index 1
在这里,MIB 请求试图找到对象标识符(OID)`iso.org.dod.internet.private.enterprises.2041`,但 OpenBSD 的`snmpd`不支持它。(它支持`20`*`2`*`1`,这是 Net-SNMP MIB 的一部分。)SNMP 客户端请求了一个无效的 MIB。
这个例子展示了成功请求以及`snmpd`响应时发送的 MIB:
snmpe_parse: 192.0.2.197: SNMPv1 'henning' context 1 request 1531862688
snmpe_parse: 192.0.2.197: oid iso.org.dod.internet.private.enterprises.ucDavis
通过仔细阅读输出,你应该能够看到为什么`snmpd`没有按照预期回答请求。
### 获取`snmpd`信息
SNMP 最重要的特性是它允许您从操作系统和/或软件中读取统计数据。除了 SNMP 支持的常规功能,如资源利用率和进程外,`snmpd`还允许您获取 OpenBSD 特定的系统信息。您可以获得有关数据包过滤器、传感器数据、接口内存和命令地址冗余协议(CARP)的信息。所有这些信息都出现在.1.3.6.1.4.1.30155 MIB 下,这是 OpenBSD 的私有(企业)MIB 树。
#### PF SNMP MIB
OpenBSD 的数据包过滤功能保留了很多统计数据,而我所需要的一切都可以通过 PF MIB 获得。您将找到如下信息:
+ PF 是否开启,以及运行了多长时间(以百分之一秒为单位)
+ 匹配过滤规则的数据包数量
+ 分片和重组的数据包数量
+ 由于内存问题、内部数据包过滤问题、状态表溢出等原因丢弃的数据包数量
+ 从状态表中添加和删除的状态数量
+ 各种协议的超时数量
+ 每个接口上阻止的流量量
+ 数据包过滤表的使用情况,每个表中的地址数量
还有更多。PF SNMP MIB 为您提供了更多关于数据包过滤的有用可见性。将您的 MIB 浏览器指向.1.3.6.1.4.1.30155.1 MIB,以查看所有内容。
#### 传感器
您可以通过`snmpd`查看与`sensorsd(8)`相同的内核值(见第十五章`预留了空间。检查*/usr/share/snmp/mibs*以获取额外的 MIB 文件,并使用您的 MIB 浏览器查看您的 OpenBSD 特定版本支持的内容。OpenBSD 团队根据需要和代码贡献添加 MIB。如果您需要 IPsec MIB,请随时编写并提交代码。
## SSH 服务器 sshd
安全外壳协议(SSH)是在主机之间建立加密隧道的一种协议。SSH 最常用于远程命令行访问系统,但你也可以将其用作其他协议的通用包装器,甚至用于构建虚拟专用网络。SSH 的一个常见用途是支持安全文件传输协议服务(SFTP),它不会提供 shell 提示符,但在文件通过网络传输时会加密文件和认证信息。
OpenBSD 项目支持 OpenSSH,这是一个免费许可的客户端和服务器。OpenSSH 是世界上部署最广泛的 SSH 服务器,市场份额约为 97%,通常被认为是标准的 SSH 服务器。关于 OpenSSH 已经写了很多本书,包括我的(《SSH Mastery》,Tilted Windmill Press,2012 年)。
OpenBSD 包括 OpenSSH 服务器`sshd(8)`,OpenSSH 命令行客户端`ssh(1)`,以及 SFTP 客户端`sftp(1)`。在这里我们将重点关注`sshd`,因为你可以使用任意数量的 SSH 客户端。我使用最频繁的是`ssh`(用于类 Unix 系统)和 PuTTY(用于 Windows)。对于 SFTP,我通常使用`sftp`(用于类 Unix 系统)和 WinSCP(用于 Windows)。
### 禁用 sshd
除非你在安装过程中指定了其他设置,否则 OpenBSD 默认会启动`sshd`。如果你不想`sshd`运行,请在*/etc/rc.conf.local*中禁用它。
sshd_flags=NO
### SSH 主机密钥
第一次启动`sshd`时,OpenBSD 会在*/etc/ssh*目录中创建*主机密钥*。这些是一组公钥和私钥,唯一地标识了一个 SSH 服务器。每个密钥文件的名字中都包含单词*key*。当你的客户端第一次连接到 SSH 服务器时,它会展示服务器主机密钥的指纹摘要。如果你告诉客户端接受这个密钥,客户端会缓存服务器的密钥。如果这个密钥发生了变化,客户端会警告用户服务器的唯一身份已改变,用户可能正在向不同的服务器提供他的登录凭证。(任何获得主机密钥副本的人都可以让另一个服务器伪装成你的服务器。)请确保备份你的主机密钥,并保护它们免遭盗窃。
### sshd 网络选项
你可以通过添加命令行标志来更改`sshd`的行为,但重新配置`sshd`最常见的方式是编辑*/etc/ssh*目录下的文件。
OpenSSH 有许多配置选项。其中最常更改的是网络设置。你可以通过编辑配置文件*/etc/ssh/sshd_config*来控制`sshd`监听的端口、IP 地址和 IP 版本。以下是一个示例:
Port 22
AddressFamily any
ListenAddress 0.0.0.0
ListenAddress ::
`Port`关键字指定了`sshd`附加到的 TCP/IP 端口。默认是 TCP 端口 22。
### 注意
有些人建议使用除 22 号端口以外的端口来避免密码猜测蠕虫。保护你的 SSH 服务器更好的方法仅允许公钥认证或使用数据包过滤器只允许来自选定主机或网络的登录。
`AddressFamily` 关键字指定 `sshd` 使用的 IP 版本。默认情况下,使用 IPv4 和 IPv6,但你可以使用 `inet`(IPv4)或 `inet6`(IPv6)关键字来限制特定协议。
最后,你可以使用 `ListenAddress` 选项将 `sshd` 绑定到特定的 IP 地址。
### chroot 用户
组织通常需要将用户限制在特定的目录或目录子集。例如,许多网站允许用户通过 SSH 命令行访问,以便他们可以更容易地编辑文件和调试问题,或者甚至只提供文件 SFTP 访问。这些用户应该有权访问自己的目录,但不能访问其他用户的文件或系统的任何其他部分。一种解决方案是在用户的家目录中 `chroot` 用户。如果你有多个需要访问共享目录的用户,你可以将他们全部 `chroot` 在该目录中。
将用户锁定在目录中涉及三个步骤:选择要锁定用户的目录、填充该目录以及配置 `sshd` 以 `chroot` 这些用户。为了演示,我们将通过一个示例来展示如何将用户 `lasnyder` 在他的家目录中 `chroot`,并给他命令行访问权限,这样他只能访问他的 `chroot` 中的程序。
#### 选择目录
首先,使用 `ChrootDirectory` 选项指定 `chroot` 目录。
ChrootDirectory /home/lasnyder
如果所有用户都需要锁定在同一个目录中,这效果很好,但如果你想让用户有自己的私有目录,或者你想要在文件系统中的其他位置指定一个目录,事情就会变得复杂。
OpenSSH 支持使用 `%%`、`%h` 和 `%u` 宏来表示家目录。如果你的 `chroot` 目录包含一个字面 `%`,请使用 `%%` 宏来表示它。本例中的服务器在家目录 */disk%3/home* 上,因此需要使用 `%%` 宏来转义百分号。
ChrootDirectory /disk%%3/home/lasnyder
`%u` 宏展开为用户的用户名。你可以使用这个宏来给用户一个除了家目录之外的 `chroot` 目录(尽管我不知道为什么你不想直接在期望的位置给他们一个家目录)。在这里,每个用户在 */var/www* 下都有一个目录:
ChrootDirectory /var/www/%u
最后,你可以使用 `%h` 宏将每个用户锁定在他的家目录中。
ChrootDirectory %h
无论你在哪里锁定用户,都必须给该目录提供用户执行所需的一切,因为用户将无法离开该目录去获取他可能需要的工具。
#### 填充 chroot
大多数程序,如 shell,至少需要几个设备节点,并且用户必须有一个 shell 程序才能运行。如果一个用户只有 SFTP 访问,你不需要对 `chroot` 进行任何特殊准备。OpenSSH 的 SFTP 服务器包含它所需的一切。但如果用户有 shell 访问,他们需要基本的设备节点和一个 shell 程序。
对于我们的示例,为了给`lasnyder`提供他所需要的,请进入`chroot`目录,创建一个*dev*目录,然后使用`/dev/MAKEDEV`创建标准的设备节点。你可以移除`console`、`klog`、`kmem`、`ksyms`、`mem`和`xf86`设备。
cd /home/lasnyder
/dev/MAKEDEV std
rm console klog kmem ksyms mem xf86
现在我们需要为用户提供一个 shell。由于在 shell 内运行的程序无法访问`chroot`之外的任何文件,包括共享库,因此复制到`chroot`中的任何 shell 都必须是静态链接的。包含的系统 shell 是静态链接的,并且大多数 ports 树中的 shell 都可以构建为静态版本。
使用`file(1)`验证 shell 是否为静态链接,然后在`chroot`内创建一个*bin*目录并将 shell 复制到那里。
file /bin/ksh
/bin/ksh: ELF 32-bit LSB executable, Intel 80386, version 1, for OpenBSD, statically linked, stripped
cd /home/lasnyder
mkdir bin
cd bin
cp /bin/ksh .
最后,尽管被`chroot`的用户不应该有对其自己的根目录的写访问权限,但他需要一个真正的家目录。用户在*/etc/passwd*中的家目录相对于`chroot`;换句话说,如果用户在*/etc/passwd*中的家目录是*/home/lasnyder*,并且用户被`chroot`到*/home/lasnyder*,那么他的个人文件和点文件实际上位于*/home/lasnyder/home/lasnyder*。
chown root:wheel /home/lasnyder
mkdir -p /home/lasnyder/home/lasnyder
chown lasnyder:lasnyder /home/lasnyder/home/lasnyder
用户现在在系统上拥有一个友好的命令行监狱单元格。现在我们需要告诉`sshd`将用户锁在其中。
#### chroot 特定用户
将这种`chroot`策略应用于所有用户可能并不明智——至少,你的系统管理员需要无限制的系统访问来执行维护。
要告诉`sshd`对特定用户进行`chroot`,无论是按名称还是按组,请在*sshd_config*的末尾使用`Match`关键字。`Match`允许你根据用户和客户端 IP 地址等因素更改`sshd`的默认行为。(`Match`有许多其他功能;请参阅`sshd_config(5)`以获取示例。)
例如,如果你想仅对用户`lasnyder`进行`chroot`,你可以使用`Match`来指定他的用户名。在配置的早期,你会有一个`ChrootDirectory`语句,该语句关闭大多数用户的`chroot`。然后,在配置的末尾,你会根据匹配该用户名来更改设置。
…
ChrootDirectory none
…
Match User lasnyder
ChrootDirectory %h
你也可以对组中的所有用户进行`chroot`。
…
ChrootDirectory none
…
Match Group webcustomers
ChrootDirectory %h
如果你有多条`Match`条款,请用逗号分隔它们。
…
ChrootDirectory none
…
Match User lasnyder, jgballard, pkdick
ChrootDirectory %h
或者,如果你的大多数用户都已被`chroot`,则反转默认设置,并特别为你的系统管理员去`chroot`。
…
ChrootDirectory %h
…
Match Group wheel
ChrootDirectory none
通过仔细配置,你可以仅限制对所需用户的访问。
SSH 可以做很多事情,例如安全地从你的网络中消除密码。完全掌握这个协议是值得你花时间的。
OpenBSD 的内置服务可以帮助你维护你的网络,并提供各种有用的支持基础设施。现在你了解了如何配置一些这些内置程序,让我们看看如何将 OpenBSD 用作桌面。
## 第十七章. OpenBSD 桌面
*与夏日的阳光相伴*
*将 Blowfish 放在指尖上:*
*没有密码被盗!*
 OpenBSD 最著名的作为服务器操作系统,但它可以是一个非常有效且强大的桌面系统。X Window 系统是类 Unix 操作系统的标准图形桌面软件,OpenBSD 包括使用它的工具。由于本书假设你有一些 Unix 经验,所以不会涵盖所有使 X Windows 舒适的应用程序。你需要进行实验以找到你偏好的邮件客户端、网络浏览器和文本编辑器——其中大多数都不是 OpenBSD 特定的。相反,本章涵盖了 OpenBSD 特有的、起源于 OpenBSD 或需要特定配置的项目。
OpenBSD 包括 Xenocara 框架,用于修改和构建与 OpenBSD 紧密集成的 X.Org。我们将讨论使用 `cwm` 桌面环境将 OpenBSD 引导到图形桌面,以及使用 `tmux` 终端多路复用器的文本控制台。但我们将从自定义控制台开始。
## 使用 wscons 配置你的控制台
`wscons(4)` 硬件无关的控制台驱动程序让你以许多方式配置你无聊的、黑白、非图形化的控制台。
首先,使用 `wsconsctl(8)` 查看当前的控制台设置。在文本控制台而不是 X 会话中运行以下命令(在 `wscons` 中所做的更改可以应用到 X 会话中,但一旦启动 X,你基本上就只能够使用 X 的配置系统)。
$ wsconsctl
keyboard.type=pc-xt
keyboard.bell.pitch=400
keyboard.bell.period=100
keyboard.bell.volume=50
…
每行包含一个系统变量和一个设置,其中许多你可以更改。`keyboard.type` 变量代表系统上的键盘类型。由于这是一个 amd64 系统,它使用与消费级电脑通用的 `pc-xt` 键盘,但你将看到不同硬件有不同的键盘类型。
你也可以使用 `wsconsctl` 来更改这些设置。例如,在上一个列表中,变量 `keyboard.bell.volume` 设置了电脑蜂鸣声的音量。现在,我是一个 `tcsh` 用户,我经常使用 Tab 补全(输入一个或两个字符,按 TAB 键,shell 会填充你即将输入的命令或文件名)。不幸的是,当 Tab 补全遇到模糊的地方时,它会停止。当我通过 SSH 登录时,这并不是问题,因为我可以只输入一个或两个字符然后再次按 TAB 键。但当我使用本地控制台时,每个模糊的地方都会伴随着电脑的蜂鸣声(或铃声)。当我试图解决问题,而铃声响起,大声喊叫“*哔* 错误! *哔* 错误! *哔* 错误!”时,我只有一个想法:
蜂鸣声必须消失。
$ wsconsctl keyboard.bell.volume=0
keyboard.bell.volume=0
现在,寂静无声,我可以不用电脑的打扰来解决问题。(如果你是受虐狂,可以选择提高音量。我不会评判你——至少不会在公共场合。)
在这里,我们将探讨一些你可以用 `wscons` 做的其他事情。
### 屏幕清除
如果您让系统闲置几分钟,屏幕应该变黑以减少功耗。现代显示器通常会自动执行此操作,但您也可以在操作系统中配置此行为,尤其是在较旧的平台上。OpenBSD 仅在知道如何重新激活显示器后才会关闭显示。
您有三个选择:
+ ****`display.kbdact`****. 通过键盘活动唤醒
+ ****`display.msact`****. 通过鼠标活动唤醒
+ ****`display.outact`****. 通过显示器输出唤醒
将这些 `wscons` 变量之一设置为 `on`,OpenBSD 将意识到它应该在闲置超时后开始关闭显示器。变量 `display.screen_off` 给出了闲置超时的时间(以毫秒为单位);默认值 `600000` 是 10 分钟。
您还可以选择将屏幕变黑或将显示器置于“省电”模式,也称为 *睡眠*。显示黑色屏幕的显示器在触发时会立即重新激活,但会消耗更多电力。睡眠状态的显示器实际上是关闭的,需要几秒钟才能重新激活。要设置省电模式,将变量 `display.vblank` 更改为 `on`。(一些旧显示器不相信省电,因此在这些显示器上此功能可能不起作用。)
### 在启动时设置 wscons 变量
用户可以调整控制台设置,但这些设置将在下一次重启时消失。要在启动时设置 `wscons` 变量,请将它们添加到 */etc/wsconsctl.conf*。启动过程会读取此文件,并将找到的任何变量应用到控制台。
## 使用 tmux 运行虚拟终端
终端复用器 `tmux(1)` 允许您在一个 OpenBSD 终端窗口内运行多个虚拟终端。当您从系统中断开连接时,标准虚拟终端会消失,但 `tmux` 虚拟终端即使在断开连接后也会继续运行。`tmux` 软件小巧、快速、易于使用,并且与 OpenBSD 的其他部分一样经过精心编写。
为什么你需要 `tmux`?一个例子是构建程序。在我离开办公室之前,我使用我的笔记本电脑通过 SSH 连接到一个 OpenBSD 服务器,创建一个虚拟终端,开始构建一个大型程序(例如 OpenOffice.org),然后关闭我的笔记本电脑。通常情况下,当我的会话被中断时,服务器上的构建会终止,但 `tmux` 虚拟终端即使在注销后也会继续运行。在断开连接的虚拟终端中,构建会在我开车回家的过程中继续,当我稍后重新连接时,我可以看到构建的进度。虚拟终端会话甚至可以存活于由网络或客户端故障引起的意外断开。
本节提供了对 `tmux` 的介绍。有关此处讨论的功能的完整详细信息,以及数十个其他功能,请阅读 `tmux(1)`。
### tmux 状态栏和窗口名称
要启动虚拟终端会话,请运行 `tmux`。您的终端窗口将显示命令提示符和底部的绿色 `tmux` 状态栏,其中包含如下信息:
[0] 0:ksh* "caddis.blackhelicop" 11:55 26-Jun-13
这是一个虚拟终端会话。状态栏的左侧显示 `tmux` 会话编号(括号内 `[0]`)和 `tmux` 窗口的列表 `0:ksh*`(从窗口编号 0 开始)。右侧显示你的机器名称的前部分 (`caddis.blackhelicopters.org`),后面跟着时间和日期。你将在设置 tmux 选项和配置 tmux 中学习如何自定义这些设置。
窗口名称默认为在该 `tmux(1)` 窗口中运行的程序的名称。例如,如果你启动一个持续到中断的命令,例如 `iostat -w 5`,会话名称将更改为匹配该命令。中断该命令,返回到 shell 提示符,状态栏应该更改其名称以匹配你的 shell。
状态栏通常是绿色的,但如果它变成黄色,`tmux` 正在等待输入。当它是黄色时,任何输入都被解释为 `tmux` 命令。如果你意外进入此模式,请按 ENTER 返回到绿色状态栏和正常操作。
### tmux 命令和窗口管理
按下 CTRL-B 告诉 `tmux` 下一个命令是为 `tmux` 而非虚拟终端中运行的程序。 (如果按下 CTRL-B 会干扰你经常使用的另一个程序,你可以更改这个键组合,如你在取消映射和重新映射键中看到的。)
最常用的 `tmux` 命令是单个字符。例如,要在本 `tmux` 会话中创建第二个终端窗口,请按 CTRL-B-C。你的屏幕将只显示一个命令提示符和一个新的状态栏。
[0] 0:iostat- 1:ksh* "caddis.blackhelicopte" 11:58 26-Jun-13
你有两个窗口:窗口 0 显示 `iostat` 输出,窗口 1 显示 `ksh` 提示符。窗口 1 旁边的星号表示你当前正在查看它。在你的新窗口中运行一个持续命令,例如 `top`,窗口名称应该会自动更改为该命令的名称。
#### 更改当前窗口
要查看另一个窗口,使用以下任一键组合:
+ 要查看下一个窗口,请按 CTRL-B-N。
+ 要切换到上一个窗口,请按 CTRL-B-P。
### 注意
请记住,窗口顺序是循环的。例如,如果你在最后一个窗口上,按下 CTRL-B-N,你应该看到第一个窗口。
+ 要通过编号直接跳转到窗口,请按 CTRL-B 然后按窗口编号。
+ 要打开所有窗口的菜单,请按 CTRL-B-W,然后使用箭头键选择一个窗口。
我发现前后顺序足够了,但如果你在一个终端中打开了一打窗口,你可能会有不同的看法。
#### 重命名窗口
终端窗口的名称与当前运行的程序名称相同,但这并不总是有用的。例如,如果我用 `make build` 编译最新源代码,窗口名称将不断更改以反映当时构建中运行的命令。唯一的问题是,我状态栏中不断闪烁的更改让我头疼。
如果您不想在每次命令执行时看到窗口名称更改,请使用 CTRL-B 为窗口分配一个静态名称。状态栏中会出现一个黄色的 `[rename-window]` 提示。输入您首选的窗口名称,例如 `upgrade`,然后按 ENTER。
#### 终止窗口
要关闭一个窗口并结束其中运行的任何进程,切换到该窗口并按 CTRL-B-&。您将收到一个确认提示。
### 获取在线帮助
按 CTRL-B-? 以查看所有 `tmux` 命令的完整列表。
C-b: send-prefix
C-o: rotate-window
C-z: suspend-client
Space: next-layout
!: break-pane
": split-window
: list-buffers
…
现在,您可以在不阅读手册页的情况下轻松探索 `tmux`。您将使用此列表在取消映射和重新映射键中重新映射键。
### 断开连接、重新连接和管理会话
一组 `tmux` 窗口称为 *会话*。方便的是,`tmux` 可以在不中断其窗口的情况下断开与运行会话的连接。按 CTRL-B-D 断开您的终端与当前 `tmux` 会话的连接。您的终端现在应显示在启动 `tmux` 之前所持有的内容。要重新连接到您的 `tmux` 会话,运行 `tmux attach`。
您可以同时拥有多个 `tmux` 会话。会话编号显示在状态栏的最左侧。(在我们的示例状态栏中,`tmux` 会话是 0。)
要在不连接到您之前的会话的情况下启动一个新的 `tmux` 会话,请不带任何参数运行 `tmux`。例如,当我想从上次离开的地方继续时,我输入 `tmux` 而不是 `tmux attach` 以创建一个新的 `tmux` 会话。您可以在 `tmux` 内部更改您的 `tmux` 会话,使用 `tmux` 命令,但通常我只是结束会话并输入正确的命令。
如果您拥有所有这些 `tmux` 会话,您如何确保您没有留下旧的、无用的会话,其中运行着被遗弃的命令?使用 `tmux list-sessions`。
$ tmux list-sessions
0: 4 windows (created Sun Feb 13 12:17:14 2011) [80x23]
2: 1 windows (created Mon Feb 21 21:57:59 2011) [131x36] (attached)
我可以从输出中的最后一行看到,我在我的另一个工作站上留下了会话 2 运行,并且我仍然连接到它。
要连接到会话 2,使用 `attach-session` 并使用选项 `-t` 选择目标会话。在这里,我连接到 `tmux` 会话 2:
$ tmux attach-session -t 2
我现在从两个不同的 SSH 会话连接到同一个会话——在这种情况下,从两个不同的客户端工作站。我在一个屏幕上的输入会在另一个屏幕上回显。
要销毁一个会话,使用 `kill-session` 命令,并使用 `-t` 指定会话编号。在这里,我销毁 `tmux` 会话 2:
$ tmux kill-session -t 2
在 `tmux` 会话 2 中的窗口中运行的任何程序也将被终止。
### 使用 `tmux` 命令
`tmux` 的命令模式提供了一个提示,用于输入更复杂的命令。要进入命令模式,请按 CTRL-B-:. 状态栏将变为黄色,所有窗口名称将被单个冒号替换。例如,要创建一个专门运行 `systat(1)` 的新窗口,请按 CTRL-B-: 并输入 **`neww systat`**。将出现一个名为 `systat` 的窗口。切换到该窗口,然后按 CTRL-C 停止 `systat`。该窗口将消失。
您可以使用 `tmux` 命令执行各种操作,包括将窗口分割成多个面板、复制粘贴文本等。(阅读 `tmux(1)` 获取完整列表。)如果您想从一个窗口复制粘贴到另一个窗口,使用多个终端窗口是最简单的,但如果您在一个仅文本的控制台或其他受限环境中工作,您可能会发现这些 `tmux` 功能很有用。
`tmux` 命令模式最常用于设置选项。
### 设置 `tmux` 选项
选项会改变 `tmux` 窗口、会话以及 `tmux` 服务器本身的行为。最常见的更改涉及窗口的外观、颜色或状态栏中显示的项目。一些选项影响整个 `tmux` 会话;其他选项仅影响特定窗口。您可以使用 `tmux` 命令 `set-option` 在线更改选项。
继续打开一个 `tmux` 会话以跟随。按 CTRL-B-: 进入命令模式。当出现冒号时,输入 **`set-option status-fg green`**,然后按 ENTER。您的状态栏现在应该是一个实心的绿色条。恭喜!您已将状态栏文本颜色设置为与背景颜色相同,使其难以阅读。返回命令模式,并将颜色更改为黑色以使其可读。(如果您觉得这很烦人,您可以终止此 `tmux` 会话并启动一个新的会话以重置所有选项。)
在进行更改时,使用 **`set-option`**(或仅 `set`)来设置影响 `tmux` 服务器和整个会话的选项。使用 **`set-window-option`**(缩写为 `setw`)来设置仅影响单个窗口的选项。
大多数人可能不需要很多(如果有的话)`tmux` 选项,但它们可能很有用。例如,假设您想状态栏时钟以 24 小时格式显示时间,或者您想使用视觉铃声而不是蜂鸣声。选项让您控制这些行为,以及在状态栏中运行命令。要更改基本的 `tmux` 外观和行为,请参阅 `tmux(1)` 中的选项。
一定要交互式地尝试任何有趣的选项。一旦您有一个按您喜欢的样子运行的 `tmux` 会话,输入 **`show-options`** 以获取当前选项的准确列表。复制该列表,因为我们将会使用它来构建配置文件。
### 配置 `tmux`
在您的家目录中修改 *$HOME/.tmux.conf* 以配置您的 `tmux` 会话,或者使用 */etc/tmux.conf* 将您的 `tmux` 偏好应用于每个系统用户。个人 `tmux` 配置覆盖全局设置。
作为简单的例子,我已经将我的状态栏的左侧(包含会话编号)设置为蓝色,右侧(主机名、时间和日期)设置为红色。如果我喜欢这个设置,我可以通过在 *tmux.conf* 中输入以下内容来使此更改永久化:
set -g status-left-bg blue
set -g status-right-bg red
`-g` 标志设置全局选项,因此它对所有会话和窗口都生效。
这应该会使您熟悉使用 `tmux`。如果您需要同时使用多个终端窗口,请使用图形桌面。请继续关注。
## 设置 X
OpenBSD 开发者修改了由 X.Org 提供的行业标准 X 图形界面,以更好地适应 OpenBSD。X.Org 和 OpenBSD 特定补丁的组合称为 Xenocara。
在大多数情况下,Xenocara 与 X.Org 完全一样,X.Org 文档适用于 OpenBSD。Xenocara 的大部分是为了安全和方便开发人员构建 X,但也有一些新增功能。在我看来,OpenBSD 对 X.Org 的最佳改进是 `cwm(1)` 窗口管理器。在这里,我们将介绍配置和启动 X。下一节将提供有关使用窗口管理器的详细信息。
### 配置 X
配置 X 可能很简单或很痛苦,这取决于您的硬件。
大多数显卡需要特殊访问系统内存,尽管一些新的英特尔显卡可以在不进行此访问的情况下工作。对于其他显卡,您必须在 */etc/sysctl.conf* 中调整 `machdep.allowaperture=2` sysctl,并重新启动。
大多数 amd64 和 i386 系统需要将 `machdep.allowaperture` 设置为 `2`,但其他平台可能需要 `1` 或 `2`。如果没有此 sysctl 设置,内核将不允许 X 与显卡通信。如果您不确定,尝试在不更改 sysctl 的情况下使用 X,当您发现您的硬件太旧或型号不正确而无法以这种方式工作,将其设置为 `2`。
重新启动后,通过运行 `startx` 查看是否可以自动设置您的图形界面。如果它工作正常,您应该看到 `fvwm(1)` 桌面和非常单调的灰色背景以及几个终端窗口。
### 注意
如果 X 无法启动,请参阅 OpenBSD FAQ、X.Org 文档和 */var/log/Xorg.0.log*。X 的自动配置可能会出现许多问题。阅读您的错误日志并在互联网上搜索解决方案。
一旦您知道 X 可以正常工作,您就需要决定您是否希望在每次需要时手动启动 X,或者您是否希望 OpenBSD 自动启动到 X。
### 手动启动 X
在登录到文本控制台后,运行 `startx(1)`。此命令将启动 *$HOME/.xinitrc* 中的命令并启动 X。
### 启动到 X
OpenBSD 包含一个 */etc/rc.conf* 钩子,在启动时使用登录提示启动 X 显示管理器 `xdm(1)`,但默认情况下不使用 `xdm`。以下是您要添加到 *rc.conf.local* 中的行,以便在启动时无任何标志启动 X:
xdm_flags=""
启动后,控制台将显示图形登录提示。一旦用户登录,`xdm` 将运行 *$HOME/.xsession* 中的任何命令。
### 注意
因为这是一本关于使用 OpenBSD 作为桌面的章节,所以我假设你正在使用 `xdm(1)`。示例参考 *$HOME/.xsession*。如果你使用 `startx(1)` 代替,请替换为 *.xinitrc*。
### 模拟三按钮鼠标
许多 X 软件都期望你有一个带有三个或更多按钮的鼠标,但许多只有两个按钮。Xenocara 允许你假装你有第三个鼠标按钮,当你同时按下两个鼠标按钮时,它将解释为按下不存在的第三个按钮。
当然,最好的解决方案是购买一个带有三个或更多按钮的真正鼠标。它们比以前更容易获得。
现在你已经准备好了 X,让我们探索我之前提到的 `cwm` 窗口管理器。
## 使用 cwm 窗口管理器
虽然 X 为图形界面提供了操作系统支持,但该界面的管理则由窗口管理器负责。OpenBSD 为按钮密集型、点按式的窗口管理器提供了软件包,如 KDE、Gnome 和 Xfce。这些窗口管理器可能为消费者友好的操作系统和 OpenBSD 之间提供一个舒适的桥梁,但它们并不是为更核心的 Unix 用户设计的。
Xenocara 包含三个窗口管理器:自上个千年以来与 X 一起发货的经典 `fvwm(1)` 和 `twm(1)` 窗口管理器,以及 OpenBSD 特有的 `cwm(1)`。OpenBSD 开发者专门编写 `cwm` 以作为现代、快速、键盘友好的界面。
要在登录时启动 `cwm`,在 *$HOME/.xsession* 中调用它:
/usr/X11R6/bin/cwm
当你的 `cwm` 会话结束时,`xdm` 会将你返回到登录屏幕。
### 配置 cwm
与使用鼠标驱动的配置菜单不同,`cwm` 使用单个配置文件,*$HOME/.cwmrc*。你可以在 `cwmrc(5)` 中阅读完整的文档。在这里,当我讨论各种 `cwm` 功能时,我会提到如何在 *.cwmrc* 中配置或更改每个功能。
#### 修饰键
大多数 `cwm` 操作都需要你按下可配置的键组合。例如,CTRL-ALT-DEL 锁定屏幕。`cwm` 文档列出了 表 17-1 中显示的修饰键。
表 17-1. 表 17-1: cwm 修饰键
| 符号 | 键 |
| --- | --- |
| C | CTRL |
| S | SHIFT |
| M | META/ALT |
例如,*.cwmrc* 中的 `CS-r` 表示 CTRL-SHIFT-R。`CM-W` 代表 CTRL-ALT-W。
#### 选择新的窗口管理器
默认的 `cwm` 配置允许你在系统上的任何命令中使用 CTRL-ALT-W 选择新的窗口管理器。输入 **`cwm`**,`cwm` 应该重新启动而不会丢失任何窗口。
### 注意
你也可以输入一个不是窗口管理器的命令,例如 `grep`。如果你这样做,OpenBSD 会静默地注销你。它不会说,“在我伤害你之前,请远离键盘。”不威胁你被认为是 OpenBSD 的用户友好。
#### 将键序列绑定到命令
你还可以将一个键序列绑定到 `cwmrc(5)` 中列出的任何 `cwm` 命令。例如,假设你想要使用键序列 CTRL-ALT-R 来删除你的当前窗口。将以下内容添加到 `*.cwmrc*`:
bind CM-r delete
只有当你使用 CTRL-ALT-W 重新启动 `cwm` 或注销并重新登录时,更改才会生效。在你做了其中之一之后,使用 CTRL-ALT-R 删除当前窗口。
### 警告
如果你在一个 `*.cwmrc*` 文件中添加了 `cwm` 无法解析的条目,`cwm` 将不会处理配置文件,并且当你加载配置文件时,你将丢失所有的自定义 `cwm` 设置。如果你的自定义设置消失了,那么你在 `*.cwmrc*` 中的最新更改是错误的。如果你犯了一个 `cwm` 能够解析的错误,`cwm` 将会接受它。在这种情况下,除了用户之外没有人会遇到麻烦。
### 创建 cwm 窗口
当你在运行图形桌面时,屏幕上的所有内容都是一个 *窗口*。终端、网页浏览器和游戏都在窗口中运行。管理窗口——提升、隐藏、调整大小、命名等——是窗口管理器的核心任务。
默认的 `cwm` 会话从一个朴素的灰色屏幕和一个小型的 `xconsole(1)` 窗口开始。使用 CTRL-SHIFT-ENTER 创建一个新的终端窗口。窗口管理器应该聚焦于鼠标所在的任何窗口。(按 SHIFT-+ 增加终端窗口的字体大小。)
如果你反复按 CTRL-SHIFT-ENTER,你将看不到额外的终端窗口。哦,新窗口将会被创建,但会堆叠在一起。按 ALT 和左鼠标按钮移动当前活动窗口,你应该会露出下面的另一个终端窗口。
我觉得默认的终端大小太小了;我想要更宽的终端和更多的行数。要调整终端窗口的大小,按 ALT 和鼠标中键(或同时按两个按钮)。鼠标将会移动到窗口的右下角并变成一个右角符号。只要你按住鼠标按钮,窗口就会继续调整大小。
要垂直最大化窗口,按 CTRL-ALT-等号(=)。要水平最大化窗口,按 CTRL-ALT-SHIFT-等号(=)。要销毁一个窗口,将其聚焦并按 CTRL-ALT-X。你不会收到确认你的决定的提示;`cwm` 将会立即遵守并销毁窗口。
要退出 `cwm` 并返回登录屏幕,按 CTRL-ALT-Q。
### 管理窗口
现在你已经可以创建窗口了,让我们看看如何管理它们。
首先,使用 ALT-TAB 在可见窗口之间切换。新激活的窗口应该上升到前台。
要给一个窗口命名,按 CTRL-ALT-N 以访问标签提示,然后输入窗口期望的名称。当选择隐藏窗口而不销毁它时,名称很有用。
要隐藏一个窗口,先将其聚焦,然后按 ALT-ENTER 使其消失。按 ALT-TAB 不会将其恢复,因为它已被隐藏。按鼠标左键以获取所有隐藏窗口的列表,按名称排列。你没有命名的窗口将显示为程序名称。所有终端都显示为`xterm`。点击名称以取消隐藏窗口。
### 注意
给具有特定目的的窗口命名是个好主意,例如一个长时间运行的软件构建。这样,当它不再有趣时,你可以最小化窗口,并在需要时快速找到它。我根据连接的服务器命名由 SSH 会话创建的窗口。
你可以通过名称搜索窗口。按 CTRL-ALT-/以获取`window>>`提示,然后开始输入窗口的名称。`cwm`将列出所有匹配的窗口。隐藏窗口在其名称前有一个`&`符号。感叹号表示具有焦点的窗口。
### 锁定屏幕
不要在没有锁定的情况下离开一个正在运行的工作站,尤其是如果你登录到敏感系统或作为 root 用户。按 CTRL-ALT-DEL 锁定你的桌面,屏幕将变黑。按另一个键,`cwm`将请求你的密码以解锁工作站。
默认的屏幕保护程序是一个空屏幕,由`xlock(1)`提供。要使用不同的屏幕保护程序,在*.cwmrc*中设置其路径,如下所示:
command lock path-to-command
例如,要将`xlock`的流模式用作屏幕保护程序,请将以下内容添加到*.cwmrc*中:
command lock '/usr/X11R6/bin/xlock -mode flow'
如果你不喜欢 Xenocara 的`xlock(1)`中的任何屏幕保护程序,可以尝试`xscreensaver`包。
### 注意
`xlock`很容易被任何具有控制台访问权限的人绕过。你不能指望它提供安全性,但它确实可以作为你同事的一个不错的提醒。
### 使用 SSH 连接到其他机器
一个常见的任务是用 SSH 连接到远程机器。要做到这一点,按 ALT-.以显示`ssh>>`提示,然后输入你想要连接的机器的名称。方便的是,`cwm`支持基于*known_hosts*条目的自动完成。当你输入主机名到`ssh`提示时,`cwm`会在系统用户和*known_hosts*中查找匹配的名称。按向下箭头键查找你想要的主机,或者继续输入主机名以连接到新主机。(如果*known_hosts*条目被哈希,则自动完成将不起作用。)
顺便说一下,如果你打开多个 SSH 会话,请给它们命名,因为整理多个标记为`ssh`的会话很烦人。
### 创建应用程序菜单
在背景上单击鼠标右键以弹出应用程序菜单。`cwm`开发者不知道哪些程序对你来说很重要,所以他们甚至不尝试提供默认的应用程序菜单。你需要自己使用*.cwmrc*条目来构建它。每个命令都有以下格式:
command name path-to-command
是的,这与设置屏幕保护程序的格式完全相同。`lock`命令实际上是两个特殊命令关键字之一。在这里,我创建了一个包含两个选项的应用程序菜单,这两个选项是我的网络浏览器和我的 PDF 阅读器:
command firefox /usr/local/bin/firefox
command xpdf /usr/local/bin/xpdf
当我右击桌面背景时,我会看到一个带有这两个选项的菜单。
### 使用键盘导航
几乎每个人现在都有鼠标,但有时你处于最好忽略鼠标的情况。也许你的桌子太小,鼠标坏了,反复从键盘上移开你的手会减慢你的速度(就像你没有合适的鼠标键盘一样),或者你今天就是讨厌你的鼠标(这既合理又值得尊重)。
要使用键盘控制鼠标光标,使用 CTRL 和箭头键移动指针一小段距离,或者按 CTRL-SHIFT 和箭头键进行更大的指针移动。在我的系统中,CTRL-SHIFT 向上箭头将鼠标指针向上移动大约一个终端行,但这会随着字体大小而变化。
你还可以使用键盘命令来调整窗口的位置和大小,如 表 17-2 所示。
表 17-2. 表 17-2:cwm 窗口移动方向键
| 键组合 | 方向 |
| --- | --- |
| alt-shift–H | 向左 |
| alt-shift–J | 向下 |
| alt-shift–K | 向上 |
| alt-shift–l | 向右 |
使用 ALT 和方向键可以移动窗口一小段距离,或者使用 ALT-SHIFT 移动窗口一大段距离。要小幅度调整窗口大小,使用 CTRL-ALT 和方向键。CTRL-ALT-SHIFT 和方向键可以大幅度调整窗口大小。就像使用鼠标调整大小一样,大小变化发生在窗口的右下角。将窗口的左上角放置到你想要的位置,然后调整窗口大小。
### 装饰 cwm
默认的 `cwm` 桌面相当单调,但一些调整可以使它更容易看。我首先设置的一件事是背景颜色:黑色。使用 `xsetroot(1)` 来设置你的背景颜色。
$ xsetroot -solid black
你可以将此命令包含在 *.xsession* 中或在终端中运行它。文件 */usr/X11R6/share/X11/rgb.txt* 列出了 X 识别的颜色。如果颜色名称由两个单词组成,要么在名称中删除空格,要么将名称放在单引号中,如下所示:
$ xsetroot -solid 'hot pink'
如果你想在背景中使用图片,使用 `feh`(*/usr/ports/graphics/feh*)。
$ feh --bg-scale /home/mwlucas/galaxies.jpg
要使窗口边缘更容易识别,围绕它们放置边框。默认边框宽度为 1 像素,颜色由你选择。我更喜欢 3 像素的边框,活动窗口为蓝色,非活动窗口为深蓝色。这需要在 *.cwmrc* 中添加以下条目:
borderwidth 3
color activeborder blue
color inactiveborder darkblue
随着你对 `cwm` 的熟悉程度越来越高,你可能会发现你想要特定的应用程序始终可见——也许是一个 MP3 播放器、一个时钟,或者一个花哨的图形系统负载指示器。最大化窗口可能会隐藏这些应用程序。为了解决这个问题,在 *.cwmrc* 中定义一个间隙,这设置了即使最大化窗口也要保持清晰像素数。
gap top bottom left right
例如,当我必须跟踪时间时,我在屏幕的右侧运行 `xclock(1)`。实验表明,我的时钟大约有 175 像素宽。我留出 180 像素的间隙,这样即使我最大化窗口,它也不会覆盖时钟。这是我的 *.cwmrc* 中的 `gap` 条目:
gap 0 0 0 180
现在我不能再以我丢失了桌面上的时钟为借口说错过了工作。幸运的是,我有许多其他方便的借口。
### 解除和重新映射按键
虽然 `cwm` 的作者们尽力选择不会与其他程序冲突的快捷键,但他们无法避免所有可能的冲突。如果你遇到这样的冲突,你可以通过修改 *.cwmrc* 中的条目来替换冲突的 `cwm` 键绑定来解决该问题。
例如,`cwm` 使用 CTRL 和 CTRL-SHIFT 与箭头键一起移动指针,但 OpenOffice 也使用这些键组合在文本文档中移动指针和突出显示。我已经使用 OpenOffice 超过 10 年,并在其中写下了数百万字。我的手指已经得到了很好的训练,我不打算尝试重新训练它们。`cwm` 的按键分配必须改变。
使用 `bind` 命令来重新映射按键。首先,使用 `unmap` 选项将 CTRL 和 CTRL-SHIFT 与箭头键组合从 `cwm` 中断开连接。记住,*.cwmrc* 使用 `C` 来表示 CTRL,使用 `S` 来表示 SHIFT(如前所述在 表 17-1 中所示)。
bind CS-Left unmap
bind CS-Right unmap
bind CS-Up unmap
bind CS-Down unmap
bind C-Left unmap
bind C-Right unmap
bind C-Up unmap
bind C-Down unmap
这些按键现在将传递到应用程序中,例如 OpenOffice。
为了确定如何使用键盘移动指针,我检查 `cwmrc(5)` 以获取可以绑定到键的命令列表。手册使用简短的名字和功能描述来定义命令。指针移动命令以 `ptrmove` 和 `bigptrmove` 开头,加上一个方向。我找到了它们,并使用 Windows 键(也称为修改器 4)来替换我从 CTRL 键中移除的功能。
bind 4-Left ptrmoveleft
bind 4-Right ptrmoveright
bind 4-Up ptrmoveup
bind 4-Down ptrmovedown
bind 4S-Left bigptrmoveleft
bind 4S-Right bigptrmoveright
bind 4S-Up bigptrmoveup
bind 4S-Down bigptrmovedown
我现在可以使用 OpenOffice 和 `cwm` 的键盘功能。
到目前为止,我已经涵盖了自 OpenBSD 引入 `cwm` 以来我所使用的所有内容,这应该能让你开始。有关更多信息,请阅读 `cwm(1)` 和 `cwmrc(5)`。你会发现 `cwm` 支持许多更多功能。
现在我们已经了解了 OpenBSD 的外观,让我们深入探讨操作系统的核心。
## 第十八章。内核配置
*内核,不是上校!*
*这是 Blowfish,不是鸡*。
*少油多汁*。
 根据你的系统管理经验和背景,内核是一个充满神秘和猜测的主题。它可能是你心血来潮时重新配置的东西,或者是你知道不要去动的东西。
大多数商业操作系统只为配置内核提供了一些基本钩子。许多开源操作系统会告诉你,每次更改任何内容时都要从源代码重新构建内核。
OpenBSD 处于中间位置。
标准的 OpenBSD 内核旨在无需修改即可完美使用,但你拥有进行任何调整或调整所需的环境工具。此外,如果你决定进行大规模内核手术,你还有完整的源代码和内核构建工具。
OpenBSD 允许你在系统运行时调整内核行为,通过 `sysctl(8)` 实现。某些硬件或协议需要特殊的 OpenBSD 内核调整才能在特定环境中运行。本章将涵盖这两种类型的更改,但首先,让我们来谈谈内核的一般情况。
## 什么是内核?
“文件 */bsd* 是 OpenBSD 的内核。下一个问题?”
这在技术上是对的,但并不十分有用。一个更通用的描述是,“内核是连接应用程序和硬件的接口。”这不是一个完整的定义,但已经足够了。
内核允许程序将数据写入磁盘驱动器和网络,并向 CPU 发出指令,将位移动到内存中。当你打开一个网页时,浏览器应用程序会请求内核获取显示的数据。
一些内核责任超出了这个定义。例如,内核处理网络连接,包括在需要时从一个接口转发数据包到另一个接口。数据包过滤规则在内核中运行(尽管规则由应用程序管理)。内核处理磁盘冗余。内核还处理所有不影响应用程序但对系统运行至关重要的各种事情。
简单来说,可以把内核看作是处理所有底层功能的程序,这已经足够让你了解内核的作用。
除了 *kernel*,你还会听到 *userland* 这个术语。Userland 是系统中不属于内核的所有内容。你的 shell、库和应用程序都是 userland 的一部分。
### 内核消息
内核向用户空间发出消息。这些包括硬件连接和断开警报、设备驱动程序的警告以及系统启动消息。如果你以文本模式登录到系统控制台,你可能会注意到这些消息。
要查看内核消息,你可以查看控制台、检查系统日志(如第十五章所述 Chapter 15),或使用 `dmesg(8)`。
OpenBSD 有一个系统信息缓冲区,其中它将消息从内核发送出去。这些消息通常被复制到系统日志中,但也可以通过`dmesg`访问。
系统信息缓冲区是环形的。随着它的填满,最旧的信息被删除以腾出空间给新的信息。运行`dmesg`来查看它。
### 启动信息
一个常见的问题是“你的内核找到了哪些硬件?”如果内核处理所有的设备驱动程序和其他硬件支持,那么找到的设备列表应该包括系统中所有受支持的硬件。
虽然系统信息缓冲区是环形的,但 OpenBSD 会将启动时的系统消息复制到`/var/run/dmesg.boot`。以下是我测试系统中的一个启动消息。
OpenBSD 5.2-current (GENERIC) #287: Tue Aug 21 18:15:00 MDT 2013
deraadt@i386.openbsd.org:/usr/src/sys/arch/i386/compile/GENERIC
cpu0: AMD Opteron(tm) Processor 4184 ("AuthenticAMD" 686-class, 512KB L2 cache) 2.80 GHz
cpu0:FPU,V86,DE,PSE,TSC,MSR,PAE,MCE,CX8,APIC,SEP,MTRR,PGE,MCA,CMOV,PAT,
PSE36,CFLUSH,MMX,FXSR,SSE,SSE2,NXE,MMXX,FFXSR,LONG,3DNOW2,3DNOW,SSE3,CX16,
POPCNT,LAHF,ABM,SSE4A
real mem = 267907072 (255MB)
avail mem = 252616704 (240MB)
…
第一行列出了 OpenBSD 的版本、内核名称和版本、内核构建的日期,以及内核构建的机器和目录以及构建者。这台机器运行的是官方 OpenBSD i386 快照,由 Theo de Raadt 构建。
我们接下来看到一些关于处理器的具体信息。熟悉 AMD 的读者会注意到这是一个 64 位的 amd64 处理器。我选择运行 32 位的 i386 版本的 OpenBSD,因为这是我手头上的安装盘。
这个系统配备了 256MB 的 RAM,但由于硬件级别的奇怪问题,有 1MB 丢失。OpenBSD 看到 255MB,此时有 240MB 可供除内核之外的其他程序使用。内核可能会稍后使用一些那部分内存。
### 设备附加
内核随后探索硬件。当它找到与设备驱动程序匹配的硬件时,它会将设备驱动程序附加到硬件上。
mainbus0 at root
bios0 at mainbus0: AT/286+ BIOS, date 10/13/09, BIOS32 rev. 0 @ 0xfd780, SMBIOS rev. 2.4 @ 0xe0010 (98 entries)
bios0: vendor Phoenix Technologies LTD version "6.00" date 10/13/2009
bios0: VMware, Inc. VMware Virtual Platform
acpi0 at bios0: rev 2
OpenBSD 找到了主系统总线`mainbus0`,这有点奇怪,因为它实际上不是一块硬件。内核创建这个逻辑设备作为所有其他设备附加的点。它不是唯一的逻辑设备驱动程序,但它在每台机器上都是存在的。
`bios0`设备,对于硬件 BIOS 来说,也没有什么特别有趣的地方。你知道硬件有一些 BIOS。我们之前在第三章中介绍了如何配置系统 BIOS,并且自从那时起你就不需要查看它了。同样,`acpi0`设备代表高级配置和电源接口(ACPI)。如果它需要任何配置,你会在从运输箱中解包系统后进行处理。
### 连接和编号
现在我们进入真正的硬件。
pci0 at mainbus0 bus 0: configuration mode 1 (bios)
pchb0 at pci0 dev 0 function 0 "Intel 82443BX AGP" rev 0x01
ppb0 at pci0 dev 1 function 0 "Intel 82443BX AGP" rev 0x01
pci1 at ppb0 bus 1
piixpcib0 at pci0 dev 7 function 0 "Intel 82371AB PIIX4 ISA" rev 0x08
pciide0 at pci0 dev 7 function 1 "Intel 82371AB IDE" rev 0x01: DMA, channel 0 configured to compatibility, channel 1 configured to compatibility
第一 PCI 总线,设备`pci0`,连接到插槽总线 0 的`mainbus0`。内核随后找到一个它识别为`pchb0`的设备,并将其作为设备 0 附加到 PCI 总线上。不知道`pchb0`是什么?使用`man pchb`来识别它作为一个 PCI 主机桥。`dmesg`会给你部件号。
接下来是设备 `ppb0`(一个 PCI/PCI 桥,根据 `ppb(4)`),作为设备 1 连接到 PCI 总线 0。这后面跟着另一个 PCI 总线 `pci1`,它连接到 `ppb` 设备。每个设备的实例都分配一个数字,从零开始。我们的第十个 PCI 总线将是设备 `pci9`。(没有技术要求按顺序编号,但内核除非您告知它否则会遵循此规则。)
如果你深入查看 *dmesg.boot*,你会看到每个设备都连接到另一个设备。例如,这是我的键盘。
wskbd0 at pckbd0: console keyboard, using wsdisplay0
键盘 `wskbd0` 已连接到设备 `pckbd0`。
pckbc0 at isa0 port 0x60/5
pckbd0 at pckbc0 (kbd slot)
设备 `pckbd0` 连接到设备 `pckbc0`,而 `pckbc0` 又连接到 `isa0` 设备,这是 ISA 总线。
isa0 at piixpcib0
ISA 总线连接到 Intel PIIX4 ISA 桥。
piixpcib0 at pci0 dev 7 function 0 "Intel 82371AB PIIX4 ISA" rev 0x08
然后,这个桥接器连接到 PCI 总线 0。
OpenBSD 从根向外查找设备,这意味着列表中的顺序与您刚才看到的顺序相反。您会得到一个列表,显示哪些设备连接到某个设备,然后是连接到这些设备的设备。您可以从终端设备开始回溯,但这有点麻烦。
### 使用 dmassage 查看已安装设备
我认为 `dmassage` 软件包在确定哪些设备连接到哪些设备方面非常有用,尽管这并非它的唯一功能。像其他任何软件包一样安装 `dmassage`,然后使用 `-t` 选项运行它以显示以树状结构显示的已安装设备,如下所示:
root
|-mainbus0
| |-bios0
| |-cpu0
| |-ioapic0
| |-pci0
| | |-mpi0
| | | -scsibus1
| | | -sd0
| | |-pchb0
| | |-pciide0
| | | -atapiscsi0
| | | -scsibus0
| | | -cd0
…
虽然这些信息可能不是立即有用的,但 `dmassage` 展示了系统上设备是如何相互连接的,这可能在以后变得很重要。
## 查看和调整 Sysctls
如前几章所述,OpenBSD 内核包括各种称为 *系统控制* 或 *sysctls* 的参数。一些 sysctls 是静态的,可以查看但不能更改。root 账户可以更改其他一些,无论是在运行时还是在启动时。
Sysctls 允许应用程序从内核检索信息。它们还允许系统管理员在不重新配置应用程序、重新编译内核或重启的情况下更改系统行为。您可以使用 `sysctl(8)` 查看 sysctl 值并调整可更改的值。
话虽如此,尽管您 *可以* 更改 sysctls,但这并不意味着您 *应该* 更改它们。OpenBSD 开发者将 sysctls 设置为适用于大多数环境的默认值。您可能需要更改一个或两个以适应您的系统,但如果您发现自己到处都在更改 sysctls,那么您可能正在进入系统管理员的兔子洞。
### Sysctl MIBs
内核以 MIB 树的形式呈现 sysctl。正如您在第十六章中学到的,MIB 树将信息组织成层次类别。顶级类别包括`kern`(内核)、`vm`(虚拟内存)、`net`(网络)、`hw`(硬件)、`machdep`(机器相关值)等等。这些类别中的每一个都有额外的子类别。例如,`net`有`inet`(IPv4)和`inet6`(IPv6)类别。`inet6` MIB 有`ip6`(通用 IPv6 特性)和`icmp6`(IPv6 的 ICMP)子类别。当您到达类别的末尾时,您会找到这些单独的 MIB:
net.inet6.ip6.forwarding=0
这个 MIB 配置了在接口之间转发 IPv6 数据包,将主机变成路由器。我如何知道?我在文档中读过,它是在`/etc/sysctl.conf`中的注释示例。OpenBSD 不维护 sysctl 值的中央列表,但手册页引用了任何相关的 sysctl。
如果您想探索 sysctl,请根据以下描述从您的系统获取列表。
### 查看 Sysctl
使用`sysctl(8)`来查看系统上可用的 sysctl。
$ sysctl
kern.ostype=OpenBSD
kern.osrelease=5.2
kern.osrevision=201211
kern.version=OpenBSD 5.2-current (GENERIC) #287: Tue Aug 21 18:15:00 MDT 2013
deraadt@i386.openbsd.org:/usr/src/sys/arch/i386/compile/GENERIC
…
这个特定的系统有超过 400 个 sysctl。解释`kern.ostype`和`kern.osrelease` sysctl 相对直接,但为什么 OpenBSD 系统会有一个 sysctl 来报告操作系统呢?
`sysctl(3)`接口出现在所有基于 BSD 的操作系统上,甚至在 Linux 上,所以检查`kern.ostype` sysctl 或检查其存在性是第三方软件识别操作系统的好方法。`kern.osrevision`只是这个特定快照构建的年份和月份。`kern.version`是启动时显示的内核编译信息。这难道不难吗?让我们看看接下来的几个 sysctl:
kern.maxvnodes=5926
kern.maxproc=1310
kern.maxfiles=7030
kern.argmax=262144
理解这些 sysctl 的作用比解释之前的 sysctl 名称要难一些。经验丰富的系统管理员可以对这些做出很好的猜测,但猜测并不是系统管理。在更改它们之前,总是要研究 sysctl。
当您知道 sysctl 的名称并想查看其当前值时,请将 sysctl 名称作为`sysctl`的参数。例如,要查看当前的 securelevel(在第十章[Securing Your System]中讨论),请检查`kern.securelevel` sysctl。
$ sysctl kern.securelevel
kern.securelevel=1
`kern.securelevel`的当前值是 1。
您可以通过只提供您感兴趣的树的一部分来查看 sysctl 树的子集。例如,要查看仅与 ICMP 相关的 sysctl,请检查`sysctl net.inet.icmp`子类别。
$ sysctl net.inet.icmp
net.inet.icmp.maskrepl=0
net.inet.icmp.bmcastecho=0
net.inet.icmp.errppslimit=100
net.inet.icmp.rediraccept=0
net.inet.icmp.redirtimeout=600
net.inet.icmp.tstamprepl=1
OpenBSD 有六个与 IPv4 ICMP 网络相关的 sysctl。您可以通过这种方式查看 sysctl 树的任何部分,深入或浅出都可以。
### 修改 Sysctl 值
一些 sysctl 是只读的。例如,`hw.ncpufound` sysctl 显示了系统有多少个处理器。
$ sysctl hw.ncpufound
hw.ncpufound=1
这个系统有一个处理器。您不能通过软件更改硬件处理器的数量(duh)。
另一方面,系统在软件中决定是否转发数据包。OpenBSD 完全在内核中执行数据包转发,就像嵌入式防火墙和路由器一样。sysctl `net.inet.ip.forwarding`控制此功能。如果设置为`0`,则不转发数据包。如果设置为 1,则系统路由数据包。
$ sysctl net.inet.ip.forwarding
net.inet.ip.forwarding=0
要更改此设置,请使用等号分配新值。
sysctl net.inet.ip.forwarding=1
net.inet.ip.forwarding: 0 -> 1
如果需要停止转发数据包,请将此 sysctl 设置为`0`。
变更立即生效。请记住,只有 root 可以更改 sysctl 值。
### Sysctl 值的类型
大多数 sysctl 具有数值,但该数值的解释取决于 sysctl。一些 sysctl 是单词,一些会生成表格。
#### 数值 Sysctl
一些 sysctl 是布尔值——要么开启要么关闭。例如,IP 转发要么开启要么关闭。在正常工作的系统中,您不能有 50%的数据包转发。
其他数值 sysctl 有一个有效的数值范围。例如,`kern.securelevel` sysctl 的范围可以从`-1`到`2`,如第十章第十章。保护您的系统中讨论的那样。虽然您可以为范围之外分配值,但它不会对最接近的有效值之外产生任何影响。
一些 sysctl 具有直接映射到某些内核值的数值。例如,`kern.maxproc` sysctl 提供了系统可以运行的最大进程数。您可以根据需要调整此值以支持您的应用程序。虽然没有最大值,但增加`kern.maxproc`会增加各种内核表中使用的内存。同样,没有最小值,但如果您将此设置降低太多,系统将无法正常运行。
#### 单词 Sysctl
一些 sysctl 是单词,例如前面检查过的`kern.ostype` sysctl。大多数这些 sysctl 不能使用`sysctl`更改,但一些可以使用其他程序更改。例如,sysctl `kern.hostname`提供了系统的主机名。您不能使用`sysctl`更改`kern.hostname`,但可以使用`hostname(8)`更改它。
#### 表格 Sysctl
除了单词和数字之外,一些 sysctl 会以表格的形式生成输出。这些 sysctl 不是供直接人类消费的,而是供专用用户空间程序处理的。例如,`netstat(1)`读取表格 sysctl 以创建其输出。
要查看所有 sysctl,包括表格,请将`-A`选项传递给`sysctl`。
$ sysctl -A
许多表格 sysctl 仍然不会打印(它们将生成警告,说明您应该使用某个程序来查看该数据),但您将在常规输出中看到一些表格。
顺便说一下,表格 sysctl 是只读的。
### 在引导时设置 Sysctl
Sysctl 的更改不是永久的;重启后会恢复。要使 sysctl 更改永久生效,请在*/etc/sysctl.conf*中设置它们。
在*sysctl.conf*中指定的更改在引导过程中早期发生,在任何服务器软件启动之前。例如,如果你需要自定义网络堆栈,这些更改应该在系统打开任何网络连接之前发生。在*sysctl.conf*中列出你需要更改的 sysctl,一个等号,以及所需值。
默认的*sysctl.conf*包含常见的更改 sysctl(OpenBSD 团队预计你可能合理想要更改的)。每个都使用井号(`#`)注释,并设置为最常用的非默认设置。如果你想更改 sysctl,取消注释条目。
以下是从*sysctl.conf*中的一些常见更改条目。(根据你的 OpenBSD 版本,你的系统可能有不同的条目。)
> **`net.inet.ip.forwarding`**
>
> 这控制接口之间的 IPv4 数据包转发。当设置为`1`时,系统根据内部路由表转发接收到的任何接口上的数据包。当设置为`0`(默认值)时,不转发数据包。
>
> **`net.inet.icmp.rediraccept`**
>
> 这决定了主机是否会接受 ICMP 重定向。路由器发送 ICMP 重定向以指导主机使用不同的本地网关来访问更具体的路由。虽然路由器可以转发客户端的包,但使用重定向可以减少网络负载。然而,接受 ICMP 重定向意味着主机可能会被重定向到一个无效的网关,因此可能成为安全问题。将此设置为`1`以接受 ICMP 重定向。默认的`0`会忽略 ICMP 重定向。
>
> **`net.inet6.ip6.forwarding`**
>
> 这控制 IPv6 数据包的转发,就像`net.inet.ip.forwarding`对 IPv4 数据包做的那样。你可以分别控制 IPv4 和 IPv6 的转发。将此设置为`1`以转发 IPv6 数据包。
>
> **`net.inet6.icmp6.rediraccept`**
>
> 默认情况下,OpenBSD ICMPv6 忽略重定向,就像它忽略 IPv4 ICMP 重定向一样。将此设置为`1`以接受 ICMPv6 重定向。
>
> **`net.inet6.ip6.accept_rtadv`**
>
> IPv6 自动配置监听路由器通告,就像 IPv4 自动配置监听 DHCP 服务器的配置一样。为了自动配置 IPv6,主机必须接受路由器通告。将此设置为`0`以禁用接受路由器通告。
>
> **`net.inet.tcp.always_keepalive`**
>
> TCP 保持活动功能会在其他情况下空闲的连接上发送数据包,以便中间设备能够识别出连接仍在使用中。适当的防火墙即使在没有保持活动的情况下也能识别出活跃但空闲的 TCP 连接。如果你有一个损坏的防火墙或 NAT 设备,TCP 保持活动可以帮助保持连接活跃。将此设置为`1`以启用保持活动。
>
> **`net.inet.tcp.ecn`**
>
> 默认情况下,OpenBSD 的 TCP 堆栈不使用显式拥塞通知(ECN)。将此设置为`1`以启用 ECN。
>
> **`ddb.panic`**
>
> OpenBSD 使用`ddb(4)`内核调试器。如果您希望在内核恐慌的极不可能事件中让系统进入调试器,请保持此值为`1`。如果您希望系统尽快重新启动,请将其设置为`0`。
>
> **`ddb.console`**
>
> 当设置为`1`时,此选项启用在某人按 CTRL-ALT-ESC 时从控制台进入`ddb(4)`调试器。此选项主要对开发者感兴趣。
>
> **`vm.swapencrypt.enable`**
>
> 默认情况下,OpenBSD 会对写入交换区的所有数据进行加密。要禁用交换区加密,请将此设置为`0`。实际上没有理由禁用交换区加密,因为加密交换空间只会引起最小的系统负载。
>
> **`machdep.allowaperture`**
>
> 这控制用户空间程序对用户空间实际上不应能访问的内存的访问。X 窗口系统需要访问此内存来显示图形控制台。(第十七章修改内核
虽然`sysctl`允许您调整内核,但它不会让您更改内核二进制文件中硬编码的值。其中一些值用于初始化内核数据结构,一旦内核运行,它们就不能更改。其他一些与设备驱动程序相关。一旦内核完成设备探测,它不会因为您更改了设备驱动程序检查其硬件的位置而回过头来重新探测。要更改这些硬编码的值,您必须编辑现有的内核文件并重新启动,让系统从初始化开始按照您的喜好设置一切。这就是`config(8)`的作用所在。
`config`命令有两个完全不同的功能。第一个是从文本配置文件创建内核编译目录,如第十九章所述。我们现在最感兴趣的功能是编辑现有的内核二进制文件,这允许您调整内核以更好地满足您的需求。
### 注意
现代的 OpenBSD 内核在很大程度上是动态的。如果您请求额外的虚拟接口,内核会创建它们。如果您需要更改缓冲区缓存内存的数量,请使用 sysctl。编辑内核很少是必要的。
### 备份默认内核
在对正在工作的内核进行任何更改之前,无论多么微小,都要备份原始内核!如果你的微小更改使你的机器无法启动,你希望能够轻松地回退到可工作的内核。
内核只是一个文件,*/bsd*。要备份它,将其复制到另一个文件。我建议将默认内核的备份命名为*/bsd.GENERIC*,原因将在第十九章中变得明显。
总是保持一个已知良好的内核在你的系统上。一个坏的内核可能会阻止计算机启动,如果你没有可靠且易于启动的内核,你将需要从安装介质启动。(使用第五章中的说明启动备份内核。)记住,微小的内核错误可能需要几周或几个月才能显现,所以计划永远保留你的备份内核。
### 设备驱动程序和内核
内核中大部分硬编码的信息都与设备驱动程序有关,特别是古老 ISA 卡的驱动程序。
你们中的一些人可能还记得手动配置网络或 SCSI 卡上的中断请求(IRQ)和内存端口地址。内核使用 IRQ 来识别卡。本质上,它咨询一个内部的中断请求和端口号码列表,将其与硬件探测中找到的内容进行比较,并相应地分配驱动程序。“这张卡在 IRQ 10 和内存端口 0x300 处响应?它必须是一张 NE2000 兼容的网络卡。我将分配那个驱动程序给它。”当然,这个过程比这更复杂,但这个探测是过程的一个关键部分。如果你想让 OpenBSD 识别这样的卡,并且卡设置了一个与 OpenBSD 期望不同的 IRQ 和内存端口,你必须告诉内核卡使用的 IRQ 和内存端口。
实际上,处理 ISA 卡的最佳方式是将它们送到回收站。在 25 年的 VAX 上运行 OpenBSD 很有趣,也很有教育意义。在 15 年的 Sparc 硬件上运行 OpenBSD 对于非常特定的应用来说是现实的,也可以很有教育意义和趣味性。在 10 年的消费级 i386 硬件上运行 OpenBSD 要么是浪费时间,要么是自我折磨——可能两者都是。
### 注意
现代基于 PCI 的硬件包括内核识别硬件并分配正确设备驱动程序的钩子。你不应该需要编辑内核来支持硬件。
### 启用驱动程序
而不是更改驱动程序中断请求(IRQs),更现实的做法可能是需要启用默认禁用的设备驱动程序,或者禁用默认启用的设备。
内核包括一些由于与某些硬件反应不良而禁用的设备驱动程序,例如 IPMI 驱动程序。`ipmi(4)`驱动程序已知存在错误,在我写这篇文章的时候,在某些用例中它已经严重损坏。它包含在默认内核中,但默认情况下是禁用的。
你可以选择启用 `ipmi(4)`。如果它对你有用,那很好。如果没有,请随意提交错误报告,最好是带有补丁,或者至少是正确的 `dmesg` 输出和崩溃转储。
### 使用配置编辑内核
当使用 `config` 作为内核编辑器时,使用命令行选项 `-e` 和 `-o`。`-e` 标志告诉 `config` 你正在编辑一个内核二进制文件。`-o` 标志允许你指定内核编辑版本的新的文件。
将原始内核文件路径作为参数。例如,以下是编辑 */bsd* 并将结果写入文件 */bsd.test* 的方法:
config -e -o /bsd.test /bsd
你可以使用 `-f` 标志代替 `-o` 和一个文件名。`-f` 标志告诉 `config` 在原地编辑内核文件,而不是创建一个新文件。
### 注意
如果你正在编辑 */bsd* 并指定了 `-f` 选项,你的更改将直接写入 */bsd*。我建议**不要**这样做。(除非,当然,你绝对确定你在做什么。你可以保留所有部分。)
运行 `config` 将打开内核编辑器,它看起来应该像这样:
OpenBSD 5.2-current (GENERIC) #287: Tue Aug 21 18:15:00 MDT 2013
deraadt@i386.openbsd.org:/usr/src/sys/arch/i386/compile/GENERIC
Enter 'help' for information
ukc>
在这一点上,你需要使用内核编辑器命令来做出更改。
#### 使用帮助和列表命令
从两个编辑器命令 `help` 和 `list` 开始。`help` 命令显示 `config` 中所有可用的命令,并在凌晨愚蠢时刻特别有用,提醒你必要的语法。
`list` 命令每次显示内核支持的所有设备的完整列表。
ukc> list
0 video* at uvideo* flags 0x0
1 audio* at uaudio|sb0|sb|gus0|gus|pas0|ess|wss0|wss|ym|eap|envy|
eso|sv|neo|cmpci|clcs|clct|auacer|auglx|auich|auixp|autri|auvia|
azalia|fms|maestro|esa|yds|emu flags 0x0
2 midi* at umidi|sb0|sb|ym|mpu|mpu|autri|eap|envy flags 0x0
…
在 OpenBSD 5.2 系统上,默认内核有 538 个条目,大多数是为不在任何特定系统上的硬件,但 OpenBSD 默认支持的。让我们更仔细地看看显示的设备。
第 0 行说明这个内核支持 `video` 设备。内核将寻找连接到 `uvideo` 设备的视频设备。`uvideo(4)` 手册页告诉我们 `uvideo` 是 USB 视频,主要用于摄像头等设备,而 `video(4)` 说明 `video` 驱动程序是一个设备无关的视频驱动程序。`flags` 语句提供了要传递给此设备驱动程序的设置。(此内核支持摄像头。)
第 1 行说明这个内核支持 `audio` 设备,并且它可以连接到一系列设备驱动程序中的任何一个。在线手册说明 `uaudio`、`sb0`、`gus0` 等是声卡。我们用视频获得声音?确实,我们生活在一个充满奇迹的时代。
较旧 ISA 设备的条目更复杂。
278 ne0 at isa0 port 0x240 size 0 iomem -1 iosiz 0 irq 9 drq -1 drq2 -1 flags 0x0
这个条目支持老式的 NE2000 ISA 网络卡,包括一个中断请求(IRQ)、直接内存访问请求(DRQ)、内存端口以及一些我(幸运地)已经忘记的其他设置。内核将在指定的端口和中断请求处检查 ISA 总线编号 0,希望找到这样的设备。
504 pflog count 1 (pseudo device)
这是一个**伪设备**——一个软件创建,其行为与实际设备非常相似,但没有底层硬件。`pflog(4)`伪设备是数据包过滤器记录日志的地方。这个内核在启动时创建了一个`pflog`设备的实例,但多亏了 OpenBSD 的可克隆接口,内核可以根据需要创建更多的`pflog`接口。
最后,请注意,有几行声明自己是“空闲的”。你可以复制一个现有的设备并将其添加到内核中。例如,如果你需要一个支持 10 个 NE2000 卡片的内核,并且需要在内核中添加 10 个设备驱动程序的实例,你可以在这些位置复制并添加设备。内核将为现代硬件自动配置任意数量的设备驱动程序实例;它将找到 10 个 PCI Express 网络卡并为它们各自分配设备实例,而无需你任何干预。
#### 查找和启用设备
`list`命令的一个缺点是它会显示内核中的所有内容。你不能中断它;你必须滚动到末尾。通过肉眼搜索几百个设备也很困难。如果你知道你想要的设备,使用`find`来搜索它。这里,我们将使用`ipmi`作为例子。
ukc> find ipmi
493 ipmi0 at mainbus0 disable bus -1 flags 0x0
IPMI 设备是设备编号 493,它连接到设备`mainbus0`。但请注意设备条目中的单词`disable`。`ipmi`设备已被禁用。让我们将其打开。
ukc> enable ipmi
493 ipmi0 enabled
内核现在有一个活动的 IPMI 驱动程序。太好了!
#### 修改内核常量
除了设备驱动程序之外,内核还有一些硬编码的内部数据结构值。如果你在内核编辑器中运行`help`,你会看到这些值作为选项。
ukc> help
…
bufcachepercent [number] Show/change BUFCACHEPERCENT
nkmempg [number] Show/change NKMEMPAGES
如你所见,这里只有两个值:`BUFCACHEPERCENT`和`NKMEMPAGES`。除非你有充分的理由去修改这些值,否则请保持它们不变。
`NKMEMPAGES`是分配给内核的内存页数。如果你的机器开始因为`kmem_map`中的空间不足而恐慌并显示错误信息,你可以增加这个值。然而,如果系统成功启动,你最好设置`vm.nkmempages` sysctl 而不是编辑内核。
`BUFCACHEPERCENT`是分配给缓冲区缓存的物理内存的百分比。在某些相当罕见的情况下,增加缓冲区缓存的大小可以提高文件系统性能。然而,你可以设置 sysctl `kern.bufcachepercent`而不是编辑这个内核值。
要查看当前值,输入其名称。
ukc> bufcachepercent
bufcachepercent = 20
要更改值,输入其名称和所需值。
ukc> bufcachepercent 50
bufcachepercent = 50
再次提醒,不要随意更改这些数字。OpenBSD 开发者出于非常好的原因将它们设置为默认值。
#### 完成配置
一旦你完成了所有的更改,输入**`quit`**来保存你的更改并将它们写入内核文件。`exit`命令会丢弃所有更改并离开编辑器,这使得重新开始变得容易。除非你喜欢被打扰和困惑,否则不要混合使用`quit`和`exit`。
#### 安装你的编辑后的内核
您编辑的内核只是一个文件。请确认您已经备份了您的工作内核,然后将新内核复制到 */bsd* 目录下,并重新启动。
## 启动时内核配置
当您知道自己在做什么时,`config` 内核编辑器很棒,但许多人并不那么幸运或受过教育。当我试图找出如何修复问题时,我经常会进行更改,重新启动以测试更改,并查看是否正常工作。
OpenBSD 允许您在启动时编辑内核。您可以尝试使用更改后的内核启动一次,看看是否正常工作,并将您的更改写入内核。在引导加载程序提示符下,运行 **`boot -c`**。
boot > boot -c
您将看到几行引导输出,然后是内核编辑器的提示符。
ukc>
这与 `config` 内核配置编辑器的工作方式相同。在这里进行任何您想要的更改,就像使用 `config` 一样。当您退出编辑器时,内核应该会使用您选择的更改启动。
启动时编辑的优点是,除非您后来声明它们是永久的,否则它们不是永久的。如果您的更改没有产生预期的行为,请重新启动并再次尝试。然而,如果您的更改解决了问题,您可以将它们写入内核文件。
内核会记住您对其所做的更改。您可以通过使用 `-u` 标志在 `config` 中“重放”这些更改。运行 `config` 时,就像编辑内核一样,但需要添加 `-u` 标志以复制您的启动时更改。
config -u -e -o /bsd.test /bsd
当您获得命令提示符时,输入 **`quit`** 以保存您的更改到新的内核文件。
在 `sysctl` 和 `config` 之间,您应该能够对内核进行任何 OpenBSD 支持的更改。在下一章中,我们将介绍如何通过从源代码重新构建内核来做出广泛不受支持的内核更改。
## 第十九章:构建自定义内核
*重新布线大脑?*
*了解部件的连接方式*
*使其成为可能。*
 OpenBSD 团队非常努力地提供高质量的内核,无需调整,只需偶尔设置 sysctl 或可能启用一个功能即可。但如果你想要使用实验性功能或向内核添加设备驱动程序,或者你想要将 OpenBSD 塞入小型硬件或嵌入式系统,你需要从源代码构建自定义内核。如果你冒险进入自定义内核,OpenBSD 的人不会支持你,但他们会提供你需要的一切,让你在接下来的章节中了解如何“自掘坟墓”。
## 内核注意事项
在我们深入了解构建自定义内核的细节之前,我们将探讨为什么这通常是一个糟糕的想法。
### 不要构建自定义内核
许多开源操作系统鼓励系统管理员构建自定义内核。这些操作系统的邮件列表充满了关于重新构建、调整和修改内核的建议。这些用户社区会引导新用户通过重新构建内核。
OpenBSD 开发者采用不同的方法来重新构建内核。他们提供了一个默认内核,称为 GENERIC,你几乎永远不需要重新构建它。
从源代码构建内核并不能证明你是一个 alpha 级别的极客,而且重新构建内核也绝不是解决问题的推荐方法。构建自定义内核的人要么是内核开发者,要么是无知的初学者。OpenBSD 项目成员对帮助用户解决自定义内核问题没有特别的义务。如果你的自定义内核崩溃,破坏了你的文件系统,或者开始向当地的警察局发出威胁电话,他们不会在乎。为什么?添加、移动或更改一个内核选项可能看起来微不足道,但每个选项可能代表成千上万行你随意删除的源代码。
话虽如此,OpenBSD 项目比闭源操作系统要友好得多,因为它提供了内核的源代码,并提供了构建它所需的工具和说明。这片领土可能很危险,有响尾蛇、熊,偶尔还有无底洞,但他们给你一张地图和一盏手电筒。如果你能为自己开辟一片新天地,那真是太好了!如果你被野狼吃了,那基本上就是这样。
### 注意
这些警告仅适用于自定义内核。OpenBSD 团队对提供的内核中存在的问题非常感兴趣,无论是 GENERIC 内核、安装程序内核还是任何其他内核。
当与内核一起工作时,请记住,一些平台有多个 GENERIC 内核。例如,i386 平台有标准的 GENERIC 内核,但它为多处理器机器提供了 GENERIC.MP,并且支持这两个版本。同样,SGI 平台有几个 GENERIC 内核——每个支持的硬件种类都有一个。这些内核都是 GENERIC,并且都受到支持。
### 为什么构建定制内核?
人们构建定制的内核有多种原因。例如,如果你是内核开发者,或者有成为内核开发者的愿望,你需要构建定制的内核来测试新功能和新的代码。
一些玩内核的人对使用实验性功能感兴趣。例如,OpenBSD 支持新开发但未经充分测试的多路径 SCSI,而 GENERIC 不支持。没有多少人拥有使用多路径 SCSI 的硬件,但那些拥有硬件并且具备编程技能的人被鼓励帮助改进这个功能。(当运行实验性功能时,请确保你理解“实验性”是“不稳定”的李生兄弟。)
很少情况下,修复安全漏洞可能需要修补内核源代码。但与其自己构建,不如从 OpenBSD 的稳定分支或快照(在第二十章中讨论)中获取补丁。
最后,有些人会构建定制的内核以在内存非常低的机器上节省 RAM。从内核中移除功能可以减小其大小。
### 构建定制内核时可能遇到的问题
当构建定制内核时,你可能会遇到麻烦。首先,内核模块之间的相互依赖关系相当复杂,并且没有详细记录。开发者通常假设构建定制内核的人会阅读内核源代码和手册页。你预计会阅读错误信息并自行解决。
OpenBSD 的跨平台设计略微复杂化了内核配置。一些设备在某些架构上运行,但在其他架构上无法运行或行为异常。如果你在内核中包含了错误设备,或者告诉内核一张卡连接到了错误的总线,你将构建一个损坏的内核。确保你了解你的硬件是如何实际组合在一起的。
当在源代码树中捣鼓时,你可能会以各种方式损坏源代码,例如通过错误地应用补丁、打乱一个文件,或者忘记你编辑了一个现在给你带来麻烦的文件。为了测试你的源代码,编译 GENERIC。如果 GENERIC 无法编译,那么你可能已经损坏了源代码,或者你的系统存在更深层的问题。
构建自定义内核通常意味着从配置文件中包含或删除内核选项和功能。然而,如果您正在尝试使用花哨的编译器标志,请停止。自定义编译器选项非常适合暴露编译器错误,但 OpenBSD 团队成员并不努力使他们的代码符合这些编译器选项的要求。许多这些选项和更高的优化如果不在非常特定的架构上运行非常特定的操作系统,就会中断。内核代码假设您正在使用指定的编译器选项;如果您更改它们,您只会得到痛苦。
如果您已经检查了一切,但仍然无法构建您的内核,您可能需要穿上防火服,并在 *misc@OpenBSD.org* 上寻求帮助。一开始就说明您正在尝试构建自定义内核,并包括以下信息:
+ 您的内核配置
+ OpenBSD 版本
+ 从您的计算机上启动 GENERIC 内核时未编辑的启动时间消息
+ 对问题的完整描述
可能有人会同情你并试图帮助你。
### 运行自定义内核时遇到的问题
自定义内核可能会有任何数量的问题,例如以下:
+ 程序可能无法按预期运行。
+ 系统可能无法启动。
+ 系统可能会随机崩溃。
+ 内核可能无法找到您所有的硬件。
+ 内核可能会吞噬您的硬盘或主板(甚至没有芥末,甚至没有一小杯麦芽醋)。
如果您仅对内核进行了有限的定制——比如说,仅向 GENERIC 内核添加了多路径 SCSI 驱动程序——那么正在该功能上工作的开发者可能会对您关于该功能的错误报告感兴趣。
如果您在用 GENERIC 内核启动的相同系统上重现了该问题,OpenBSD 团队肯定会感兴趣。将您的问题报告为发生在 GENERIC 内核上,并且仅包括 GENERIC 的调试输出,而不是自定义内核的。如果您设法识别、调试并创建了一个自定义内核问题的补丁,请将补丁和问题描述发送到邮件列表。您的问题可能是由于运行自定义内核引起的,但您也可能发现了一个可能在 GENERIC 中触发的错误。
但最重要的是,如果您在运行自定义内核时遇到问题,请重新启动到 GENERIC 并继续您的一天。
## 准备自定义内核
在自定义内核之前,请通过将 */bsd* 复制到 */bsd.GENERIC* 来备份您系统上已知的良好状态的 GENERIC 内核。这样,如果您的自定义内核无法启动,您可以通过启动备份内核来恢复。
您需要内核源代码才能构建自定义内核。您可以从 OpenBSD 安装介质中获取 *sys.tar.gz*。如果您从互联网镜像安装,请确保获取您版本 OpenBSD 的源代码。OpenBSD 镜像根目录通常包含相当最近的源代码快照,但请检查您发布版本的目录以获取其源代码。在 */usr/src* 下展开此目录。
cd /usr/src
tar -xzvpf sys.tar.gz
现在你有了备份(你*确实*在我告诉你的时候备份了你的工作内核,对吧?)和源代码,让我们看看内核配置。
## 内核配置
你通过文本文件来配置 OpenBSD 内核。像 4.4BSD 一样,OpenBSD 不提供花哨的图形内核配置工具或菜单驱动系统。每个内核配置都在一行上,包括一个标签,指示条目的类型和描述。井号(`#`)标记注释。
### 配置条目
内核配置条目分为四个一般类别:选项、设备驱动程序、伪设备和关键字。
#### 选项
*选项* 是与硬件无关的内核功能。选项处理诸如文件系统、网络协议和兼容层等问题。
选项条目看起来是这样的:
option FFS # UFS
option INET # IP + ICMP + TCP + UDP
option CRYPTO # Cryptographic framework
要了解更多关于选项的信息,请阅读 `options(4)` 手册页。
#### 设备驱动程序
设备驱动程序为内核提供与硬件交互所需的软件。如果你想让你的内核支持某块硬件,它必须包含适当的设备驱动程序。
设备驱动程序内核配置条目可能相当长。它们可能包括标志或设置,告诉内核在哪里找到设备以及如何初始化它。(ISA 卡通常有硬编码的 IRQ 和/或内存地址。)
设备驱动程序没有共同的标签,但它们的条目以设备名称开头。
mainbus0 at root
cpu0 at mainbus?
fxp* at pci? # EtherExpress 10/100B ethernet
wd* at wdc? flags 0x0000
ec0 at isa? port 0x250 iomem 0xd8000 irq 9 # 3C503 ethernet
#### 伪设备
*伪设备* 的行为与设备非常相似,但没有任何真实硬件与之相连。伪设备通常是抽象的,可以像真实硬件一样打开、读取、写入和关闭。
例如,回环接口是一个用于连接本地机器的网络连接的伪设备。(你的计算机没有回环网络卡,但回环接口的行为就像一个具有不寻常 MTU 值的真实网络卡。)
伪设备用 `pseudo-device` 标记。
pseudo-device loop # network loopback
pseudo-device pf # packet filter
pseudo-device gre # GRE encapsulation interface
#### 关键字
最后,一些其他的关键字只出现一次或很少出现。这些一次性更改会影响内核的运行方式或构建方式,难以归类。以下关键字可能会出现:
+ `machine` 关键字告诉内核它应该在哪种架构上运行。
+ `makeoptions` 关键字告诉编译器如何构建内核。
+ `include` 关键字表示引入另一个配置文件。
+ `maxusers` 值设置了一些内核表中的大小。
你会发现更不常见的关键字散布在不同的内核配置中。
machine amd64
makeoptions DEBUG="-g" # compile full symbol table
include "../../../conf/GENERIC"
maxusers 80 # estimated number of users
所有这些都会以截然不同的方式影响内核。你会在任何内核中找到这些关键字,即使是 GENERIC。
### 配置 GENERIC
让我们看看实际的内核配置。OpenBSD 将内核配置分为机器无关和机器相关文件。
#### 机器无关配置
机器无关的内核配置文件位于*/usr/src/sys/conf*。文件*/usr/src/sys/conf/GENERIC*包含机器无关的内核配置,它描述了 OpenBSD 在所有硬件平台上支持的所有功能。每个 GENERIC 内核都包含这个文件中的配置。如果你更改此文件,它将影响包含此文件的每个构建的内核。
机器无关的配置文件不包含设备驱动程序;相反,设备与特定硬件相关联。此文件不会包含任何特殊的构建指令,因为它们因平台而异。它也不会包括硬编码的系统限制、数据结构大小等,因为运行在 25 年前 VAX 上的 OpenBSD 与全新的 amd64 系统相比资源要少得多。*/usr/src/sys/conf/GENERIC*文件主要包含选项和伪设备。每个 OpenBSD 内核都必须支持文件系统,否则它将无法写入磁盘或类似磁盘的设备。
基于此文件的内核尚不知道文件系统将在哪种硬件上运行,但它知道如何创建文件系统。它不知道将有什么样的网卡,但一旦你给它一个网卡,它就可以创建 TCP 数据流并为你提供网页服务。你需要机器相关的配置来制作一个能在现实世界中运行的内核。
#### 机器相关配置
每个平台在*/usr/src/sys/arch*下都有自己的机器相关内核目录。在这里,你可以找到 OpenBSD 支持的每个平台的子目录,以及任何开发中的平台的目录。单独的目录包含特定平台的代码,以及用于内核配置文件的进一步的*conf*子目录。
我以 amd64 为例,因此内核配置目录是*/usr/src/sys/arch/amd64/conf*。虽然我们将关注常见的 i386 和 amd64 架构,但内核构建过程在所有硬件平台上都是相同的。
传统的内核配置文件名全部为大写字母。你会看到*GENERIC*配置,以及用于安装盘的*RAMDISK**文件。(*GENERIC.MP*内核是多处理器内核。)我们将从*GENERIC*内核配置文件开始:
machine amd64
include "../../../conf/GENERIC"
这个内核配置文件中的第一个条目定义了机器。机器定义告诉内核配置解析器你正在运行哪种硬件,并定义核心硬件特性和约束,例如整数的位数和系统可以支持的内存量。
第二个条目引入了机器无关的内核配置(在上一节中描述),定义了所有使 OpenBSD 成为 OpenBSD 的协议和工具。amd64 内核从这一条目继承了文件系统和网络堆栈。
在这两行之后,您将看到 OpenBSD 在 amd64 硬件上支持的设备。花点时间浏览一下文件。它包含的设备和附件与本章前面描述的相同。
### 您的内核配置
为了构建您自己的内核,您需要一个配置文件。在这里,我们将探讨如何创建配置文件。(不要只是编辑任一 GENERIC 内核文件。)
#### 小幅修改
如果您的内核只向 GENERIC 内核添加了几个项目,请将 GENERIC 配置作为您新配置的基础。例如,以下是多处理器内核配置,GENERIC.MP:
include "arch/amd64/conf/GENERIC"
option MULTIPROCESSOR # Multiple processor support
cpu* at mainbus?
多处理器内核基于 GENERIC 构建,只添加了一个选项和一个设备附件。您可以使用这个模型来定义自己的内核配置。
例如,假设您想在内核中启用实验性的 SCSI 多路径功能。您可以在平台目录中创建一个内核配置文件,并简单地复制从机器无关的 GENERIC 内核中注释掉的 multipathing 条目,如下所示:
include "arch/amd64/conf/GENERIC"
mpath0 at root
scsibus* at mpath?
这将创建一个与 GENERIC 非常相似的定制内核,增加了这两个额外的设备。
#### 移除选项
要从内核中移除选项,请使用`rmoption`关键字。例如,要创建一个基于 GENERIC 的最小内核,您可以使用`rmoption`关键字来移除一些内核选项,如下例所示:
include "arch/amd64/conf/GENERIC"
rmoption NTFS
rmoption HIBERNATE
…
通过包含默认内核来创建配置的一个优点是,当您更新源代码时,您的自定义内核配置可能仍然有效。然而,您从内核中移除的选项越多,内核编译失败的可能性就越大,或者即使编译成功,它可能无法启动。如果启动了,它可能会损坏您的硬盘。
在移除选项时,请记住,一些选项可能比您想象的更重要。例如,移除`INET6`选项(即 IPv6)可能会创建一个无法正常工作的系统。移除选项并不会节省您多少内存,而且可能会损害许多程序。
#### 移除设备
如果您想从机器相关的内核配置中移除大量内容,同时保留基础 OpenBSD 功能选项,请将机器相关的 GENERIC 配置文件复制到一个新的文本文件中,并在该文件中进行修改。
#### 大规模修改
如果您想对内核进行大规模的修改,您需要一个包含机器无关和机器相关部分的配置。首先,将现有的 GENERIC 内核配置复制到一个文件中,在平台的内核配置中。在这里,我将我的新内核命名为 TREBLE,以主机名命名:
cd /usr/src/sys/arch/amd64/conf
cp ../../../conf/GENERIC TREBLE
cat GENERIC >> TREBLE
在进行任何其他更改之前,请删除包含机器无关内核配置文件的行。然后移除使系统功能的一切,并尝试构建新的内核。接下来,逐步添加内容,直到内核构建成功。(尽管移除驱动程序不会节省多少内存,但这样做会使启动稍微快一点。)
### 注意
你可能会想使用手册页从头开始创建自己的内核配置。如果你是内核之王或不可救药的傻瓜,你当然可以这样做。请随意尝试。每个系统管理员都可以从这样宝贵的谦卑课程中受益。
#### 内核瘦身
内核中的每个设备驱动程序和选项都使用内存。如果你试图将 OpenBSD 塞入一个小型计算机,或者你正在进行任何类型的嵌入式开发,你可能想构建一个自定义内核,通过编辑`/var/run/dmesg.boot`来包含尽可能少的设备驱动程序,其中每个条目都与内核配置中的一行匹配。
删除不必要的设备驱动程序的最简单方法是删除计算机中不存在的所有内容。内核包括数十个网络卡驱动程序,但你只需要一个或两个。如果你不确定一个设备,请将其保留在配置中。(特别是 ACPI 和 BIOS 设备与它们紧密相关,没有完整的 ACPI 和 BIOS 设备集,你可能会发现构建可启动的自定义内核非常困难。)
#### 内核剖析
如果你删除设备驱动程序后没有为你创建足够小的内核,尝试删除与机器无关的选项。然而,许多这些选项是相互依赖的,删除它们可能会创建一个无法编译的内核。如果你能编译内核,它可能无法启动,如果启动了,可能无法正确运行。
### 使用 config(8)测试你的内核配置
你的自定义内核配置内部一致吗?为了测试内核并准备编译所需的文件,请使用`config(8)`。
在内核配置目录中时,将内核配置文件名作为参数传递给`config`,如下所示:
config TREBLE
如果收到任何错误信息,请阅读它们。例如,`config`可能会告诉你,在构建新内核之前需要运行`make clean`,或者你的内核配置内部不一致,无法编译。如果有问题,`config`通常会给出你出错的那一行行号。遵循`config`提供的任何建议。
以下是一些更常见的错误类型。
#### 孤儿设备
`config`失败的一个常见方式是如果你缺少另一个设备需要的设备。以下是一个例子:
config TREBLE
TREBLE:36: cpu0 at mainbus? is orphaned
(nothing matching mainbus? declared)
TREBLE:37: bios0 at mainbus0 is orphaned
(no mainbus0 declared)
TREBLE:38: ioapic* at mainbus? is orphaned
(nothing matching mainbus? declared)
TREBLE:82: pci* at mainbus0 is orphaned
(no mainbus0 declared)
*** Stop.
你的配置将各种设备附加到`mainbus0`,但你的配置中没有`mainbus0`条目。包含未附加设备的内核没有意义,也无法编译。
为了解决这个问题,再次检查你的硬件。弄清楚这些设备应该如何连接到系统,并修复你的内核配置。
#### 虚假硬件
另一个常见问题是包含不存在的设备驱动程序,这会产生以下错误。
config TREBLE
TREBLE:36: cpe0: unknown device `cpe'
*** Stop.
`config`会显示错误及其发生的位置。没有`cpe`设备,但有一个`cpu`设备。是我的错。
`config` 执行的错误检查并不能保证您的内核会按预期编译或运行。它捕获的唯一错误是配置内部不一致或完全错误的情况。真正的第一次测试是在您尝试实际构建配置好的内核时。
## 构建内核
如果 `config` 运行成功,您将有一个包含 makefile 和大量头文件的内核编译目录。*编译* 目录的传统位置是在平台目录下,对于 amd64 硬件,该位置是 */usr/src/sys/arch/amd64*。
编译目录包含由 `config` 处理的每个内核配置的子目录。我名为 TREBLE 的 amd64 内核位于 */usr/src/sys/arch/amd64/compile/TREBLE* 目录下,其中包含一个 makefile,以及所有包含设备和选项的头文件。
cd ../compile/TREBLE
make
现在是等待的时候了。如果编译成功,将创建一个名为 *bsd* 的内核文件,而不会生成任何错误信息。
### 内核构建错误
如果您的内核构建失败,您可能有一个完全可以解释的错误。首先,阅读编译提供的错误信息。大多数时候,错误信息将解释内核缺少什么。通常,您需要以某种方式更改内核配置,因为 `config` 无法捕获错误。损坏的内核编译将结束如下:
../../../../arch/amd64/pci/pci_machdep.c: In function 1'pci_intr_map':
../../../../arch/amd64/pci/pci_machdep.c:641: error: 2'PCI_INT_VIA_ISA' undeclared (first use in this function)
../../../../arch/amd64/pci/pci_machdep.c:641: error: (Each undeclared identifier is reported only once
3 ../../../../arch/amd64/pci/pci_machdep.c:641: error: for each function it
appears in.)
*** Error code 1
4 Stop in /usr/src/sys/arch/amd64/compile/ENVY (line 89 of /usr/share/mk/sys.mk).
由于缺少某些内容,这个内核无法构建。当构建失败并显示“未声明”之类的语句(如粗体所示)时,这是一个提示,表明内核缺少必要的条目。
失败的位置名称可能为您提供有关缺少内容的提示。在这种情况下,在 **1**,我在编译失败的地方有一个函数名,然后是一个特定的未声明变量 **2**,它导致编译失败。
我会先找出 `pci_intr_map` 函数的来源以及它应该做什么。在源代码和手册页中搜索对缺失函数的引用。如果那样做失败,尝试邮件列表存档。确保在任何网络搜索中都包含函数和变量名称。通用输出说“有错误”**3**或“编译已停止”**4**不那么独特,因此可能有用。如果所有其他方法都失败,退回到 GENERIC 配置。
## 安装您的内核
您完成的内核是编译目录中的 *bsd* 文件。在您使用新内核之前,请确保您当前的工作内核已备份到根文件系统上的单独文件,然后将新内核复制到 */bsd*。就这样!下次您重启时,您将使用新内核。
### 注意
有些人不喜欢在确定内核能够启动之前将自定义内核复制到 */bsd* 目录。如果你是这些人中的一员,请将你的新内核以不同的名称复制到根目录下,例如 */bsd.test*。以这个备用内核启动。测试你的系统。如果一切正常,请正确安装你的新内核。
## 识别正在运行的内核
如果你构建了多个自定义内核,你可能会忘记你正在运行哪个内核。`uname(1)` 命令会告诉你用于构建正在运行内核的内核配置文件的名称。`-v` 标志会告诉你你的内核配置名称以及你编译它的次数。
uname -v
GENERIC.MP#348
这个输出并不意味着我已经构建了一个多处理器 GENERIC 内核 348 次。我使用 GENERIC 内核,并让 OpenBSD 发布工程师为我构建内核。他们已经构建了 348 个官方快照多处理器内核,而没有清除内核构建目录。记住,构建自定义内核是为高级程序员和不知情的新手准备的。我既不是前者也不是后者。
## 第二十章。升级
*最新的源代码?*
*鲑鱼非凡!*
*勇敢地吞下。*
 这里有一个丑陋的事实:如果升级很困难,系统管理员会尽力避免它们。这可能会导致安全问题。操作系统升级可能会导致多年来一直运行良好的软件出现紧张的症状或完全停止工作。修复在新操作系统版本上不工作的附加包可能需要数天的故障排除。服务器升级甚至可能让经验丰富的系统管理员希望他们有一个更简单的工作,比如在嘉年华中担任小丑,把黄鼠狼塞进他们的裤子里。
虽然您可能在升级后可以处理桌面上的少量异常行为,但您的服务器和防火墙必须表现得完全符合预期。通常,人们会推迟升级,直到系统如此陈旧,以至于可以用运行新版本的全新机器替换,但这既是不良的系统管理实践,也是完全不可接受的安全实践。连接到互联网的计算机必须打补丁、维护和升级,否则入侵者几乎肯定会破坏它们。
幸运的是,OpenBSD 的升级过程比许多其他类 Unix 操作系统使用的要简单。经过适当的准备,您可以轻松升级 OpenBSD。
## 为什么升级?
因为您别无选择。
安全研究人员、程序员和熟练的入侵者不断发现新的方法来渗透以前安全的系统。尽管 OpenBSD 在默认安装中只遭受了两个漏洞,允许入侵者破坏系统,但这并不意味着两年前的 OpenBSD 版本就是安全的。
OpenBSD 项目只为最近两个版本的 OpenBSD 提供安全更新。例如,当 OpenBSD 5.3 发布时,OpenBSD 5.1 将“停止支持”并失去开发者的支持。如果在 5.3 发布后有人发现如何入侵默认的 OpenBSD 5.1 安装,开发者可能不会提供修复。您可能需要调整新的安全补丁以在旧版本的代码上工作,但您会发现回滚修复变得越来越困难。
默认安装中未启用的软件也可能存在问题。例如,2011 年 11 月,OpenBSD 项目为包含的 BIND `named(8)` 命名服务器中的一个问题发布了补丁。OpenBSD 并未默认启用 `named`,因此这并不是默认安装中的问题,但在您可能选择启用的一个功能中,这确实是一个安全问题。
为了保护您的系统,您必须了解如何应用安全补丁,无论是通过应用补丁还是通过升级整个系统。但在我们讨论升级过程本身之前,让我们看看您在 OpenBSD 版本上的选择。
## OpenBSD 版本
全世界的开发者们持续不断地对 OpenBSD 的源代码进行细微的修改。如果你在早上下载源代码,然后在下午再次下载,你会得到两个略有不同的版本。实际上,在任意给定的时间,你都可以得到几个不同的 OpenBSD 版本:*-current*、*snapshots*、*releases* 和 *-stable*。
### OpenBSD-current
OpenBSD-current 是 OpenBSD 最新的开发版本,包含即将公开亮相的代码。开发者们测试彼此的工作,并获得批准将代码提交到树中。最终,他们必须将他们的工作公之于众,以便公众进行测试、审查和调试。
OpenBSD-current 是公众可以访问最新代码的地方。如果某个更改会暂时破坏运行在 *-current* 上的 Web 服务器、游戏、数据库服务器或其他任何东西,但这个更改是从长远来看是有益的,那么这个更改将会进入 *-current*。这些程序可能在下一个 OpenBSD 发布之前再次工作,但并没有要求每个第三方程序在 *-current* 上始终完美运行。
### 注意
开发者期望 *-current* 本身始终能够工作,并且他们认为中断是严重的问题。你可能需要重新编译 Firefox,或者端口维护者可能需要重写一个 makefile,但核心操作系统预计始终能够运行。
在这些注意事项下,你为什么可能想要运行 *-current* 呢?一个非常好的理由是在你的环境中测试新的 OpenBSD。如果今天的 *-current* 在某些条件下会崩溃,但上周的没有,OpenBSD 的人希望知道这一点。
(所有 OpenBSD 的人都在他们的笔记本电脑上运行 *-current*,并且他们在 *-current* 上运行防火墙。)如果你能在真实世界中,使用真实的工作负载来练习 *-current*,他们希望你能这样做。让真实用户运行 *-current* 是 OpenBSD 在官方发布之前唯一能够被测试的方式。
### OpenBSD 快照
每隔几天,OpenBSD 团队会将最新的 *-current* 代码上传到镜像服务器。这是一个中间版本,称为 *snapshot*,它仅通过其发布日期来识别。快照仅仅是 *-current* 在特定时间的状态。虽然开发者们尽量避免在 *-current* 明显出现问题时构建快照,但谁负责快照的质量保证过程呢?那就是你。
提供快照是为了方便安装和测试。安装和测试快照比自行构建 *-current* 更容易。此外,如果你能在特定的快照上重现一个错误,开发者们就能确切地知道你正在运行哪个版本的代码。将问题识别为“出现在 2013 年 7 月 15 日的快照中”比在从“2013 年 7 月 15 日下午 3:17 左右从服务器 X 下载的代码中构建的 -current”中更容易。
### 注意
快照可能包含不在 *-current* 中的未提交代码。更改的补丁通常不可用。
你可以从任何镜像站点获取快照安装介质,在 */pub/OpenBSD/snapshots* 目录下。如果你想运行 -current,推荐的方式是安装你能得到的最新快照,并从那里升级。
### OpenBSD 发布版本
每隔六个月,OpenBSD 的发展速度会故意放慢。开发者们会花时间打磨新特性,而 Theo 会越来越强烈地要求最新的快照测试者。当 OpenBSD 团队满意软件的质量足够时,源代码树会被标记,并构建一个高质量的快照。这个快照被称为*发布*,并会分配一个像 5.2、5.3 这样的编号。这几乎就是你第一次安装 OpenBSD 机器时所安装的版本。新的发布版本每年五月和十一月出现。
OpenBSD 按顺序编号发布版本,从 2.0 开始,每次发布增加 .1。与大多数软件产品不同,.0 版本没有特殊含义。它只是漫长道路上的另一个点——每五年达到的一个里程碑。
发布是 OpenBSD 最受支持的版本。预期一切都能正常工作,开发团队会支持其发布版本。如果在发布中发现严重的安全问题,OpenBSD 将发布一个补丁,或 *勘误表*。
### OpenBSD-stable
OpenBSD-stable 是一个包含所有勘误和非常小补丁的 OpenBSD 发布版本。开发者故意将每个 -stable 版本的变化数量保持在绝对最小。稳定版本被称为其基础发布加上 -stable,例如 5.4-stable、5.5-stable 等等。
什么样的更改会被合并回 -stable?安全修复和勘误会自动加入。除了安全问题外,添加到 -stable 的任何补丁都必须简单、简短且明显正确。可能包含那些对少数用户有重大影响的补丁。例如,如果所有具有特定网络卡的系统在随机间隔内崩溃,补丁可能会加入 -stable(伴随着多位开发者询问为什么在发布之前没有人使用该网络卡的快照)。
你在 -stable 中不会得到新功能。它不会包含任何新的设备驱动程序或数据包过滤功能。API 不会改变。一般来说,-stable 预期永远不会变得更差。
获取 OpenBSD-stable 的唯一方法是使用源代码更新系统。
### 你应该使用哪个版本?
OpenBSD 的发布系统结合了开源开发和商业发布的最佳特性。用户可以访问最前沿的实验性代码和稳定、完善的发布版本。根据你的需求选择你的版本。
+ 如果你在一个生产环境中运行 OpenBSD,要么使用带有适用安全勘误表的发布版本,要么跟踪 -stable。开发者鼓励经验更丰富的用户使用快照或 -current,但这个选择取决于你。
+ 如果你正在评估 OpenBSD 用于生产环境,请安装你打算使用的版本。
+ 如果你刚开始学习 Unix-like 系统,或者如果你想有一个安静的 OpenBSD 体验,请使用发布版并应用适用的勘误表。
+ 如果你是一个操作系统开发者或经验丰富的系统管理员,可以直接跳到-current 或快照。你可以处理你遇到的问题,或者利用这些问题来扩展你的知识。许多人发现,如果他们不需要软件包,他们可以在生产环境中使用-current。这些人通常是更有经验的用户,他们想要防火墙。
+ 爱好者可以运行他们想要的任何东西!
记住你选择分支的限制。发布版是一个良好的起点,但你可以在理解不断扩大的情况下逐步将系统升级到-stable,然后升级到-current。许多开发者最初是作为感兴趣的爱好者开始的。
## OpenBSD 升级过程
OpenBSD 的升级有三个不同的阶段:安装操作系统的较新版本文件、更新本地配置以及更新过时的附加软件包。每个都是一个独立的部分,需要独立处理。
升级需要安装介质。最好的升级介质是新发布的 CD 或互联网镜像。你也可以通过在要升级的机器上直接构建和安装源代码来升级 OpenBSD,但这比从官方发布版升级更困难、风险更高。从网络或 CD 升级的大部分信息也适用于通过源代码升级。
### 注意
OpenBSD 一次只能升级一个主要版本。你不能直接从,比如说,5.3 升级到 5.5;你必须先从 5.3 升级到 5.4,然后升级到 5.5。你需要升级的版本越多,重新安装的理由就越多。你串行升级三个或四个版本所花费的时间,比你重新安装的时间还要多。
在升级之前,备份任何你真正关心的数据。升级过程会在现有操作系统上提取新文件,可能会覆盖某些重要内容。安装程序通常可以正常工作,但人类是会犯错的。备份!
### 按照升级指南操作
随着 OpenBSD 的发展,基本系统功能会发生变化。这本来不是什么大问题,除非相互依赖的更改造成了鸡生蛋或自举问题。如果你只是盲目地运行升级过程而不处理任何其他必需的更改,你会发现你的新系统以不可预测的方式失败。^[[42]) 如果你从源代码构建系统,这些问题可能会阻止构建完成。
所有这些潜在问题都应该可以通过从源代码构建系统的人来解决,但将它们记录下来会更好。方便的是,OpenBSD 为每个版本提供了一个*升级指南*,记录了将系统从一个版本升级到下一个版本所需的步骤。
*升级指南* 根据你执行的升级类型分成几个部分。最简单的升级是针对使用官方 OpenBSD 文件的人,例如网络或 CD 升级。如果你是从源代码构建,说明的复杂性会迅速增加。按照它们出现的顺序遵循说明。大多数升级先决条件是典型的系统管理任务。以下是最常见的要求。
### 注意
在 *升级指南* 中的说明优先于本章中我所说的任何内容。我本可以在本节剩余部分每一段都加上“除非 *升级指南* 中另有说明”的字样,但那样我的编辑可能会责备我。OpenBSD 文档是 OpenBSD 系统管理的最终权威,包括升级过程。
#### 安装程序
尤其是在从源代码构建时,像 `gcc(1)` 和 `perl(1)` 这样的引导工具可能需要使用升级后的工具来构建。在从源代码升级时,你可能需要在开始编译新版本的 OpenBSD 之前安装这些引导工具。*升级指南* 记录了这些要求。
如果你尝试从源代码构建 OpenBSD 失败,在故障排除之前重新阅读 *升级指南*。从最近的可用二进制发布版或快照开始可能解决你的问题。
#### 移除程序和文件
当 OpenBSD 中的程序变得过时或危险,或者它们的函数集成到其他程序中时,OpenBSD 会从基本系统中移除这些程序。当你升级时,你必须手动移除这些程序。这是必要的,因为留在升级系统上的旧软件可能构成安全风险。此外,一些文件和目录在新版本的 OpenBSD 中可能变得不再需要。升级完成后,你可以移除这些程序、文件或目录。
#### 准备软件包升级
当你升级 OpenBSD 时,你必须升级已安装的软件包,因为你不能可靠地在新版本 OpenBSD 上运行为旧版本构建的软件包。(这样做 *可能* 会成功,但没有保证。)*升级指南* 包含有关升级特定已安装软件包的说明,你应在开始升级之前采取其中的一些行动。
例如,最新版本的 *升级指南* 中提到 PostgreSQL 端口进行了主要版本升级。你必须作为升级的一部分进行数据库转储和恢复。旧版本的 PostgreSQL 在新 OpenBSD 上可能运行不佳,因此在进行系统升级之前执行数据库转储。
你的软件包可能不需要任何预升级工作,但在升级操作系统之前进行检查要容易得多,而不是完成操作系统升级后需要回滚,因为你没有准备某些第三方软件包。
#### 系统配置
前面的任务是最常见的升级需求,但在升级过程之前或之后,你可能需要更改其他系统文件。阅读 *升级指南*,否则你的程序可能无法按预期运行。
### 定制升级
升级脚本支持 第二十三章 中讨论的 *siteXX.tgz* 文件。如果该文件存在于安装媒体中,你可以在升级期间选择安装它。
你也可以在升级过程中运行一个自定义的后续脚本。当升级完成后,脚本会检查文件 */upgrade.site*。如果升级器找到这个文件,它将在升级的最后一步执行这个脚本。在开始升级之前,将 */upgrade.site* 复制到系统中。第二十三章更详细地讨论了 *upgrade.site*。
## 从官方媒体升级
在阅读 *升级指南* 后,获取新版本 OpenBSD 的安装媒体并从中启动。如果你计划通过网络升级,你可能只需要新的安装内核 *bsd.rd*。你可以通过 FTP 获取这个内核(或者你也可以获取整个目录并从本地磁盘运行升级)。这里,我从我的根目录获取最新的快照内核:
cd /
ftp ftp://ftp3.usa.openbsd.org/pub/OpenBSD/snapshots/amd64/bsd.rd
Connected to plier.ucar.edu.
…
在你获取快照内核后,进入你的控制台并从新内核启动。
OpenBSD/amd64 BOOT 3.18
boot> boot bsd.rd
booting hd0a:bsd.rd: 2993636+916428+2864232+0+531344 [89+320016+207017]=0xb799f0
entry point at 0x1001e0 [7205c766, 34000004, 24448b12, a608a304]
…
root on rd0a swap on rd0b dump on rd0b
erase ^?, werase ^W, kill ^U, intr ^C, status ^T
Welcome to the OpenBSD/amd64 5.3 installation program.
(I)nstall, (U)pgrade or (S)hell? U
输入 **`U`** 以升级。升级过程看起来很像 第三章 中覆盖的安装过程。默认值出现在方括号内。通过按 ENTER 键接受默认值。
Terminal type? [vt220] 1
Available disks are: sd0 sd1 sd2.
Which one is the root disk? (or 'done') [sd0] 2
Root filesystem? [sd0a] 3
Checking root filesystem (fsck -fp /dev/sd0a)…OK.
Mounting root filesystem (mount -o ro /dev/sd0a /mnt)…OK.
Do you want to do any manual network configuration? [no] 4
Force checking of clean non-root filesystems? [no] 5
fsck -p e4bf0318329fe596.a…OK.
…
总是使用默认的终端,除非你知道确切不应该使用的时候。通用硬件通常使用 vt220,如这里所示在**1**,但默认终端是平台特定的。
升级需要读取你的根分区以了解文件安装的位置。除非你告诉它这样做,否则它无法确定你的实际根磁盘**2**和分区**3**。
升级脚本将根据你现有的设置配置你的网络,你应该希望这些设置是正确的。如果你需要在每次启动时调整网络,升级脚本会给你在**4**重新配置网络的机会。
如果你通过 `reboot` 或 `shutdown` 关闭机器,你的文件系统应该是干净的。如果你因为打算升级机器而不再关心你的文件系统,粗鲁地拔掉电源插头,OpenBSD 会注意到并清理你的文件系统。要深度检查干净的文件系统,你可以在**5**强制运行 `fsck(8)`。升级脚本在继续之前会清理干净的文件系统以检查明显的错误。
### 通过网络升级
默认的升级方法是 CD。我想通过网络进行这次升级,所以我会这样做:
Location of sets? (cd disk ftp http or 'done') [cd] ftp 1
HTTP/FTP proxy URL? (e.g. 'http://proxy:8080', or 'none') [none] 2
Server? (hostname, list#, 'done' or '?') [ftp5.usa.openbsd.org] ftp3.usa.openbsd.org 3
Server directory? [pub/OpenBSD/snapshots/amd64] 4
Login? [anonymous] 5
我选择 `ftp` 作为**1**处的集合位置。我不需要通过代理服务器访问 FTP 服务器,所以我在**2**处留空。
默认的 OpenBSD FTP 服务器完全没问题,但如果你已经确定了一个非常快速的镜像,你可能会使用它。我在 **3** 使用我首选的镜像站点。
每个 *bsd.rd* 安装程序都知道从 **4** 服务器目录安装。只有在我设置了本地镜像的情况下,我才会更改服务器目录。同样,每个 OpenBSD 镜像都允许匿名 FTP。只有在我使用本地镜像时,我才会更改用户名并输入密码 **5**。
### 选择文件集
接下来是选择升级哪些集的机会。
Select sets by entering a set name, a file name pattern or 'all'. De-select
sets by prepending a '-' to the set name, file name pattern or 'all'. Selected
sets are labelled '[X]'.
[X] bsd [X] base51.tgz [X] game51.tgz [X] xfont51.tgz
[X] bsd.rd [X] comp51.tgz [X] xbase51.tgz [X] xserv51.tgz
[X] bsd.mp [X] man51.tgz [X] xshare51.tgz
Set name(s)? (or 'abort' or 'done') [done]
你必须升级机器上安装的每个文件集,否则机器的行为将不可预测。如果你在原始安装期间没有安装某些集合,你现在不需要安装它们。对于大多数机器,我建议安装所有集合。
### 注意
注意,有两个集合丢失:*etcXX.tgz* 和 *xetcXX.tgz*。这些文件属于 */etc*,并且是系统管理员合法编辑的。升级脚本无法知道一个文件是否应该被替换、编辑或忽略。你必须自己更新 */etc*。
升级脚本会下载并提取选定的文件集,然后询问你是否已完成文件集的选择。如果是这样,它会重新创建所有设备节点以适应新的内核。
在此阶段,你可以重启到新的 OpenBSD 用户空间,但用户空间可能不会完全正常工作,因为你还没有更新 */etc*。
## 更新 /etc
*/etc* 目录包含系统和程序配置信息。当程序发生变化时,其配置文件也可能发生变化。如果你尝试使用过时的配置文件运行新程序,程序将无法正确运行。
你绝对必须在运行系统之前更新 */etc* —— 这可能是升级中最令人烦恼的部分。没有任何自动化过程能够知道你的系统应该如何表现。只有你知道这一点,这意味着你必须将现有 */etc* 的内容与新的、库存 */etc* 中的相同文件进行比较。OpenBSD 提供了工具来使这个过程不那么令人烦恼。
在开始之前,如果你的系统执行复杂的功能,如数据库或数据包过滤,请以单用户模式启动。尽管单用户模式并非绝对必要,但我曾在升级一半时遇到软件表现不佳的情况,所以我现在通常会以单用户模式更新 */etc*。
OpenBSD/amd64 BOOT 3.19
boot> boot -s
booting hd0a:/bsd: 5687720+1608588+939456+0+644288 [80+502320+325975]=0xd43b98
…
Enter pathname of shell or RETURN for sh:
虽然我一直是 `tcsh` 的长期用户,^([43]) 但在单用户模式下,我总是使用默认的 shell。如果你想使用其他 shell,它必须是静态链接的,并且必须在根分区上可用。
### 挂载文件系统
现在挂载所有文件系统。如果你通过网络升级,现在启动网络。
mount -a
/bin/sh /etc/netstart
WARNING: inconsistent config - check /etc/sysctl.conf for IPv6 autoconf
我几乎还没开始,就已经收到一个警告了。多么神奇!也许这个 IPv6 错误来自升级,或者可能是之前从未注意到的遗留配置错误。无论如何,这是一个识别和解决问题的好时机。
通过 ping 几个主机来验证你已连接到网络。
你需要*etcXX.tgz*和*xetcXX.tgz*文件来更新你的新版本。如果你有 CD,这些文件在发布目录中。如果没有,可以从网络上获取它们。(我总是从升级时使用的同一个服务器获取这些文件集,以消除文件之间有任何差异的可能性。)
cd /tmp
ftp ftp://ftp3.usa.openbsd.org/pub/OpenBSD/snapshots/amd64/etc51.tgz
ftp ftp://ftp3.usa.openbsd.org/pub/OpenBSD/snapshots/amd64/xetc51.tgz
你现在可以更新*/etc*了。
### 使用 sysmerge(8)比较/etc 文件
`sysmerge(8)`程序比较你的现有*/etc*与安装集中的*/etc*,指出差异,并允许你用新版本替换安装的文件,保留你的文件,或者合并两者。
使用`-s`告诉`sysmerge`新版本的*etcXX.tgz*文件的位置,使用`-x`指向新的*xetcXX.tgz*。我将这两个文件都放在*/tmp*目录下。
sysmerge -s /tmp/etc51.tgz -x /tmp/xetc51.tgz
`sysmerge`会自行处理简单的更改,但会留给你处理需要你干预的文件。
#### 简单的 sysmerge 更新
如果你的系统只有轻微的修改,你应该会看到类似这样的情况:
===> Populating temporary root under /var/tmp/sysmerge.daiHKukKfE/temproot
===> Starting comparison
===> Updating /etc/inetd.conf
===> Updating /etc/login.conf
…
我没有修改这些文件,所以`sysmerge`会自动为我更新它们。
…
===> Installing /etc/nginx/fastcgi_params
===> Installing /etc/nginx/koi-utf
…
我系统上没有这些文件,所以`sysmerge`会为我安装它们。
这些例子是简单的情况。在你编辑了*/etc*文件的情况下,`sysmerge`将需要你的帮助。
#### sysmerge 和编辑过的文件
假设你编辑了一个文件,文件版本已经改变。你的本地更改是否反映了更新,是否冲突,或者两者可以共存?`sysmerge`无法判断是否应该用新版本覆盖安装的文件,保持不变,或者合并两者,因此它会显示旧文件和新文件之间的差异。
===> Displaying differences between ./etc/mail/aliases and installed version:
1 --- /etc/mail/aliases Sun Jun 9 04:50:04 2013
2 +++ ./etc/mail/aliases Wed Oct 23 17:06:02 2013
@@ -1,5 +1,5 @@
3 -# $OpenBSD: aliases,v 1.36 2010/09/22 13:01:10 deraadt Exp $
4 +# $OpenBSD: aliases,v 1.37 2012/10/13 07:42:39 dcoppa Exp $
Aliases in this file will NOT be expanded in the header from
Mail, but WILL be visible over networks or from /usr/libexec/mail.local.
@@ -32,6 +32,7 @@
_identd: /dev/null
_iked: /dev/null
_isakmpd: /dev/null
5 +_iscsid: /dev/null
_kadmin: /dev/null
_kdc: /dev/null
_ldapd: /dev/null
@@ -69,7 +70,7 @@
sshd: /dev/null
Well-known aliases -- these should be filled in!
6 -root: mwlucas@blackhelicopters.org
7 +# root:
manager:
dumper:
如果你已经修改了文件,`sysmerge`会显示旧文件和新文件之间的差异,并附带一些上下文行。在这个例子中,我的系统上的*/etc/mail/aliases*文件日期是 6 月 9 日**1**,而新版本是 10 月 23 日**2**。新版本中存在但安装版本中不存在的行前面有一个加号(`+`)。安装版本中存在但新版本中不存在的行前面有一个减号(`-`)。
这个例子显示,OpenBSD 版本的这个文件从 1.36**3**变到了 1.37**4**。这并不令人惊讶,对于系统管理目的来说,也不是一个特别重要的信息。但新版本有一个新的别名,将发送给用户`_iscsid`的电子邮件重定向到*/dev/null**5**。这可能是重要的,所以我希望在我的系统上使用新的别名。
接下来,我们注意到文件之间的差异,我在**6**处将发送给 root 的电子邮件转发到了我的个人邮箱,但文件的新版本在**7**处没有这样的重定向。我希望保留这一行的旧版本,但另一行的最新版本,尽管我对 OpenBSD 的版本号并不特别关心,但我更倾向于使用更新的版本。我可以手动汇编一个别名文件,但`sysmerge`在打印出差异后,会提供帮助。
1 Use 'd' to delete the temporary ./etc/mail/aliases
2 Use 'i' to install the temporary ./etc/mail/aliases
3 Use 'm' to merge the temporary and installed versions
4 Use 'v' to view the diff results again
Default is to leave the temporary file to deal with by hand
How should I deal with this? [Leave it for later] m
`sysmerge`提供了以下选项:
+ 如果我删除临时的别名文件,`sysmerge`会丢弃新文件**1**。如果我的现有别名文件足够好,这没问题,但在这个情况下,我想要新文件的一部分。
+ 如果我安装临时的别名文件,我会覆盖我的更改**2**。除非我返回并再次更改别名文件,否则我的自定义邮件转发将停止工作。我不想发生这种情况。
+ 我可以合并旧文件和新文件**3**,从每个文件中选择最佳匹配,这是我将会选择的选项。
+ 我可以再次查看差异**4**。这的优势是可以推迟实际的决定。
要合并旧文件和新文件,我输入**`m`**。请注意,合并功能需要密切关注细节。这里的错误可能会导致你的系统在尝试进入多用户模式时挂起。
当你合并时,`sysmerge`会接收你的输入并构建一个合并后的文件。两个版本中相同的行会自动添加到新文件中。当行不同时,`sysmerge`会引导你查看差异,并让你选择一个版本。
新版本行出现在右侧,已安装版本在左侧。输入**`l`**或**`1`**以选择左侧版本,或输入**`r`**或**`2`**以选择右侧版本。
$OpenBSD: aliases,v 1.36 2010/09/22 13:01:10 de | # $OpenBSD: aliases,v 1.37 2012/10/13 07:42:39 dc
%r
版本号已更新,尽管它在注释中,这不会影响别名文件的更新。不过,既然我正在合并文件,我最好也获取新的版本号。我输入**`r`**以选择右侧版本。
> _iscsid: /dev/null
%r
已安装文件中没有与此行等效的行,因为这个别名只存在于新文件中。再次,我选择**`r`**将此行包含在我的文件中。
root: mwlucas@blackhelicopters.org | # root:
%l
在这里,我希望选择已安装文件中的条目,位于左侧。我选择**`l`**。
一旦我处理完所有条目,`sysmerge`会给我提供更多选择。这些包括比较合并和已安装文件,比较合并和新文件,重新开始,查看合并文件,重新合并,以及安装合并文件。
Use 'e' to edit the merged file
Use 'i' to install the merged file
Use 'n' to view a diff between the merged and new files
Use 'o' to view a diff between the old and merged files
Use 'r' to re-do the merge
Use 'v' to view the merged file
Use 'x' to delete the merged file and go back to previous menu
Default is to leave the temporary file to deal with by hand
===> How should I deal with the merged file? [Leave it for later] i
我一切都做得正确,所以我安装了合并文件(尽管我可能应该先查看合并文件,然后再安装它)。
我在使用`sysmerge`时最大的困难在于区分我的左右。更糟糕的是,L 键在右侧,R 键在左侧。(现在你可以笑了,但等一下,你会弄混的。)
#### 完成 sysmerge
当`sysmerge`完成文件安装后,它会检查*/etc*文件的权限,并告诉你在哪里找到日志,以及系统可能需要重启。一个纯粹主义者可能会告诉你不需要重启,但通常你应该继续重启。更改核心系统文件后重启可以防止各种问题,作为升级的一部分重启也是合理的。你可以在升级你的包之后等待重启,只要一切正常工作。
…
===> Comparison complete
===> Checking directory hierarchy permissions (running mtree(8))
===> Output log available at /var/tmp/sysmerge.daiHKukKfE/sysmerge.log
*** WARNING: some new/updated file(s) may require a reboot
reboot
您现在拥有了一个更新的 OpenBSD 基础系统。请注意,新文件或更改的文件可能会改变系统行为,但通常您不会注意到。*升级指南*通常会注明用户可能会注意到的任何更改。不过,只有您知道您的系统做什么以及您希望它如何表现。
升级已安装的软件包是一个单独的任务。
## 更新已安装的软件包
软件包只在编译它们的 OpenBSD 版本上可靠运行。如果您使用软件包,那么更新第三方软件非常容易。(如果您从 ports 收集中构建自己的软件,您仍然可以更新,但不会那么容易。)
首先,再次检查*升级指南*。它描述了任何对主要软件的侵入性更改。在继续之前,采取它推荐的任何行动。
### 更新软件包仓库
在升级软件包之前,检查您的 `$PKG_PATH` 环境变量。它几乎肯定引用了您之前 OpenBSD 版本的软件包目录。
echo $PKG_PATH
ftp://ftp11.usa.openbsd.org/pub/OpenBSD/5.2/packages/i386/
找到您新版本的 OpenBSD 的软件包仓库。您可能只需在 shell 的配置文件中更新版本号,但请访问镜像站点并确保该版本的软件包存在。
如果您从发布版升级到发布版,您可以使用 `uname(1)` 命令在您的配置文件中设置 `PKG_PATH`。例如,如果 *ftp11.usa.openbsd.org* 是您最喜欢的镜像站点,对于基于 `sh` 的配置文件,可以使用这样的行。
export PKG_PATH=ftp://ftp11.usa.openbsd.org/pub/OpenBSD/uname -r/packages/`uname -m'/
### 使用升级命令
要升级已安装的软件包,请使用带有 `-i` 和 `-u` 标志的 `pkg_add`。
pkg_add -iu
quirks-1.73->1.77: ok
apr-1.4.6->1.4.6p0: ok
apr-util-1.4.1-ldap:cyrus-sasl-2.1.25p3->2.1.25p3: ok
apr-util-1.4.1-ldap:openldap-client-2.4.31->2.4.31: ok
apr-util-1.4.1-ldap:libiconv-1.14->1.14: ok
…
`-i` 标志告诉 `pkg_add` 以交互模式运行并询问您任何歧义。`-u` 标志表示“更新”。
此升级器会递归地遍历您的每个附加软件包及其依赖项,卸载旧版本并安装新版本。如果您想看到关于软件包更新过程的更多详细消息,请添加 `-v` 标志。
#### 软件包选项
如果软件包的依赖关系已更改,并且您现在有多种选择,`pkg_add` 会显示您的选择。
Ambiguous: choose dependency for foomatic-db-engine-4.0.8p2:
a 0: curl-7.26.0
1: wget-1.13.4
Your choice: 1
软件包 `foomatic-db-engine` 可以使用 `curl` 或 `wget`。在这两个中,我更喜欢 `wget`,所以我输入 `1`。按回车键告诉 `pkg_add` 使用默认设置。
#### 软件包消息
一旦所有软件包都已更新,`pkg_add` 会显示升级软件包的任何消息。
Read shared items: ok
You may wish to update your font path for /usr/local/share/ghostscript/fonts
Look in /usr/local/share/doc/pkg-readmes for extra documentation.
…
一些安装说明会告诉您清除旧版本软件的缓存文件,例如在这个 CUPS 示例中。
--- -cups-1.5.3p4 -------------------
You should also run rm -rf /var/log/cups/*
You should also run rm -rf /var/cache/cups
You should also run rm -rf /var/spool/cups
…
其他时候,软件可能会跳过版本。例如,OpenBSD 允许您安装多个版本的 PHP、Python 和其他脚本语言,但在升级后,您必须决定哪个是您首选的默认版本。
--- +python-2.7.3p1 -------------------
If you want to use this package as your default system python, as root
create symbolic links like so (overwriting any previous default):
ln -sf /usr/local/bin/python2.7 /usr/local/bin/python
ln -sf /usr/local/bin/python2.7-2to3 /usr/local/bin/2to3
ln -sf /usr/local/bin/python2.7-config /usr/local/bin/python-config
ln -sf /usr/local/bin/pydoc2.7 /usr/local/bin/pydoc
…
在升级后,您可能还需要更改一些系统配置。
--- +tk-8.5.12 -------------------
You may wish to add /usr/local/lib/tcl/tk8.5/man to /etc/man.conf
…
软件包维护者将这些消息放入软件包中供您使用。阅读它们,如果您有疑问,请遵循它们的说明。
如果您的所有软件都是通过软件包安装的,升级过程应该毫无痛苦且透明。我想告诉您所有的问题和边缘情况,但我从未能够触发任何。从官方发布介质升级软件包只需按正常操作即可。然而,从端口树构建的软件包升级更复杂,正如我在升级端口中讨论的那样。
## 为什么要自己构建 OpenBSD?
从 OpenBSD 的源代码构建自己的升级仅适用于高级用户和对开发 OpenBSD 以及高级用户感兴趣的人。您必须能够舒适地阅读和编译源代码,调试问题,并在尝试从源代码构建 OpenBSD 之前从备份中恢复。在 OpenBSD 的早期,我发现从源代码升级是最简单的前进方式,但现在通过快照升级要容易得多,且错误更少。如果可能,您可以使用官方的二进制发布版来安装您所寻找的内容,请这样做。
当您从源代码构建 OpenBSD 时,您正在构建通过 FTP 或 CD 安装的分发集。文件可能没有捆绑成分发版,但它们的 内容将是相同的。您仍然必须使用 `sysmerge` 合并您的配置文件,并且您仍然需要更新您已安装的软件。构建 OpenBSD 唯一能给您带来的就是您所跟踪的分支的最新版本。
我知道有三个原因要从源代码构建自己的 OpenBSD:为了获得 OpenBSD-stable,为了获得最新的 OpenBSD-current,或者为了高度定制 OpenBSD。OpenBSD-stable 只提供源代码。没有官方的安装介质可用于 -stable,因此如果您想从 5.4-release 升级到 5.4-stable,您必须从源代码构建它。如果您想要绝对最新的 OpenBSD 代码——比最新的快照更新的代码——您必须构建它。如果您想高度定制 OpenBSD,您也必须从源代码构建它。
## 构建自己的 OpenBSD 的准备工作
构建 OpenBSD 需要大约 4GB 的磁盘空间,其中 2GB 在 */usr/src* 中,2GB 在 */usr/obj* 中。OpenBSD 默认将这些目录作为单独的分区创建,但如果您设计了您自己的磁盘分区方案,请确保您有足够的空间。
您必须在您的目标平台上构建。不要尝试通过,例如,在 amd64 硬件上构建 VAX 二进制文件来进行交叉编译。OpenBSD 开发者使用交叉编译来启动新的硬件平台,但一旦平台启动并运行,所有开发都在本地硬件上进行。通过交叉编译产生的代码可能与在本地平台上运行的编译器产生的代码不完全相同。
### 准备基础操作系统
你的第一步是准备基础操作系统。你只能在类似于你试图构建的 OpenBSD 安装上构建 OpenBSD,这意味着你需要在你构建机器上安装最接近你的目标平台的二进制集。如果你试图构建 OpenBSD 5.4-stable,首先在你的构建机器上安装 5.4-release。如果你试图构建最新的 OpenBSD-current,首先升级你的构建机器到最新的快照。
为什么从最近的二进制发布版开始呢?首先,这是 OpenBSD 开发者所做的方式;代码旨在从非常近期的 OpenBSD 构建。尽管系统变化缓慢,但这些变化可以累积。OpenBSD-stable 仅基于其基于的 OpenBSD 发布版构建,或者基于该基础发布版之后的-stable 发布版。
此外,OpenBSD-current 偶尔会有“标志日”,其中关键系统变化使得构建源代码变得困难。这些可能包括编译器或链接器升级、库或内核更改,或者几乎任何其他事情。虽然 OpenBSD 团队记录了你需要做什么来克服这些障碍,但这些文档更像是对那些知道自己在做什么的人的笔记,而不是用户友好的说明。
这些标志日会在 OpenBSD 邮件列表上宣布。你可以尝试遵循这些笔记并在这些障碍上构建 OpenBSD,但当你厌倦了与标志日更改对抗时,升级到快照以克服这些障碍。(许多 OpenBSD 开发者也使用快照来克服标志日更改;无法通过标志日编译 OpenBSD 并不是对你男性或女性身份的威胁。)
### 获取源代码
现在获取 OpenBSD 源代码。如果你已经安装了这些文件,并且需要升级到更近的版本,请参阅更新源代码。
对于第一次安装源代码的人来说,OpenBSD 项目提供了四个压缩 tar 文件中的最近代码快照:用户空间(*src.tar.gz*)、内核(*sys.tar.gz*)、X Windows(*xenocara.tar.gz*)和 ports(*ports.tar.gz*)。所有这些都必须保持同步,这意味着你不能在-stable 用户空间和内核之上使用-current ports 树。获取最接近你想要构建的文件的版本。
如果你正在构建-stable,你可以从你安装的二进制文件的发布目录中获取所有四个文件。例如,如果你正在构建 OpenBSD 5.4-stable,你将在你的 CD 上或在 OpenBSD 镜像下的*/pub/OpenBSD/5.4/*中找到*src.tar.gz*、*sys.tar.gz*、*xenocara.tar.gz*和*ports.tar.gz*。
如果你正在构建-current,你最好从源代码的新快照开始。在 OpenBSD 镜像站点上,在*/pub/OpenBSD*目录下,你可以找到*src.tar.gz*和*sys.tar.gz*的最近副本。获取这些中最新的。你仍然需要从最近的 OpenBSD 发布版中获取*xenocara.tar.gz*和*ports.tar.gz*文件,因为这些文件更新频率不高。
确认目录 */usr/src*、*/usr/obj*、*/usr/xenocara*、*/usr/xobj* 和 */usr/ports* 是空的。然后在 */usr/src* 下提取 *src.tar.gz* 和 *sys.tar.gz*,在 */usr* 下直接提取 Xenocara 和 *ports*。在这里,我已经将所有的 tarball 复制到了我的家目录中:
cd /usr/src
tar -xzpf $HOME/src.tar.gz
tar -xzpf $HOME/sys.tar.gz
cd /usr
tar -xzpf $HOME/ports.tar.gz
tar -xzpf $HOME/xenocara.tar.gz
这为你提供了一个已知良好的基础来开始。如果你想要构建自己的 OpenBSD,你可能不想构建一个现在可以安装的版本;你想要构建一个现在不可安装的版本。这意味着你想要将源代码更新到 -current 或 -stable。
### 更新源代码
OpenBSD 使用并发版本系统 (CVS) 进行源代码管理。CVS 是一个较老、有些传统的版本控制系统。虽然许多项目已经从 CVS 转移到更新、更亮、可能更迷人的系统,但 CVS 满足了 OpenBSD 项目的需求。
OpenBSD 有一个单一的中央源代码仓库:主 CVS 服务器。只有开发者和镜像站点可以访问这个服务器。当开发者想要将新的 OpenBSD 代码提供给公众时,他会将更改 *提交* 到这个中央仓库,其他开发者可以在此时看到他的更改。
主 CVS 服务器跟踪了 OpenBSD 源代码的所有更改,以及谁进行了这些更改。镜像站点捕获这些更改,并在数小时内将这些更改提供给用户。作为用户,你可以使用 CVS 将你的本地源代码更新到镜像站点上的最新版本。
#### 源代码仓库和标签
OpenBSD 的源代码被划分为 *仓库*,即特定子系统的代码集合。你需要下载和同步你想要使用的集合。
OpenBSD 的 CVS 仓库包括四个集合:`src`(用户空间和内核源代码)、`ports`(ports 集合)、`xenocara`(X Windows)、`www`(网站)以及过时的 `X11` 和 `XF4` X Windows 集合。虽然 X11 和 XF4 仍然保留以供历史参考,但你永远不需要使用那段代码。`www` 仓库对网站编辑者和贡献者感兴趣。为了构建一个最新的 OpenBSD,你必须将你的 `src`、`ports` 和 `xenocara` 集合更新到你选择的分支的最新版本。
*标签* 是为仓库的特定版本指定的标签。OpenBSD 使用标签来区分 -stable、-release 和 -current。例如,OpenBSD 所有的发布版本中的源代码都包含文件 */usr/src/etc/master.passwd*。但是,与 OpenBSD 2.0 一起提供的 *master.passwd* 版本与 OpenBSD 5.3 一起提供的版本差异极大!CVS 使用标签来区分这个文件以及每个其他文件的各个版本。通过使用标签,你可以要求 CVS 提供任何 OpenBSD 发布版本中包含的任何文件的版本。
OpenBSD 任何 -stable 版本的标签是 `OPENBSD` 和版本号,由下划线分隔的字符串。例如,OpenBSD 5.4 的标签是 `OpenBSD_5_4`。注意两个下划线:没有 `OPENBSD_54` 或 `OPENBSD5_4`。如果你使用一个不存在的标签,而不是更新你的本地源代码文件,你将删除它们。(虽然你应该下载发布版本的源代码,而不是通过 CVS 更新,但 OpenBSD 发布版本会在标签后附加字符串 `_BASE`,例如 `OPENBSD-5_4_BASE`。)话虽如此,OpenBSD-current 是特殊的,因为它没有标签,并且它包含了所有仓库中所有源代码的最新版本。
所有仓库都设计为同步更新。例如,如果你将源代码仓库更新到 -current,但将 ports 和 Xenocara 保持在其 -release 或 -stable 版本,你的系统可能会表现出不可预测的行为。就像学习使用链锯表演杂耍一样,你可以尝试,但请不要抱怨。
#### CVS 镜像
你可以使用匿名 CVS 将你的源代码更新到最新版本。首先,从 *[`www.OpenBSD.org/anoncvs.html`](http://www.OpenBSD.org/anoncvs.html)* 上的列表中选择一个方便的匿名 CVS 服务器。世界上有多个镜像,但请选择你所在大陆上的一个,以获得更好的性能。(在我的例子中,我使用 *anoncvs13.usa.openbsd.org*^([44]))。
跟踪 OpenBSD-current 与跟踪 OpenBSD-stable 略有不同。我们将从 -stable 开始,然后看看 -current 有何不同。
#### 更新到 -stable
第一次更新源代码时,你必须指定命令行上的匿名 CVS 服务器。
cd /usr
cvs -qd anonymous@server:/cvs get -rOPENBSD_tag -P src
CVS 对其命令行参数的顺序非常挑剔,许多标志是位置相关的。除非你知道自己在做什么,否则不要改变参数的顺序。
将你偏好的服务器和想要获取的版本标签替换到这个命令中。例如,要将我的 OpenBSD 5.1 源代码树更新到来自 *anoncvs13.openbsd.org* 的最新稳定版本,我会运行以下命令:
cd /usr
cvs -qd anonymous@anoncvs13.openbsd.org:/cvs get -rOPENBSD_5_1 -P src
The authenticity of host 'anoncvs13.usa.openbsd.org (192.0.2.217)' can't be established.
1 ECDSA key fingerprint is d3:b2:b5:68:87:3b:f6:93:21:fd:28:ea:cc:b6:e1:13.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'anoncvs1.usa.openbsd.org,149.20.54.217' (ECDSA) to the list of known hosts.
因为 OpenBSD 首次运行 `cvs` 时通过 SSH 运行 CVS,它会要求你验证 CVS 镜像的主机密钥。将你的客户端给出的 **1** 个密钥指纹与 CVS 镜像列表上列出的指纹进行比较。如果它们匹配,你就正在与实际的 CVS 服务器通信。如果指纹不匹配,可能存在问题,可能是服务器的问题、跳过的网站更新,或者镜像的实际安全问题。如果密钥指纹不匹配,请选择不同的 CVS 镜像。(SSH 会缓存这个密钥,未来的更新除非密钥更改,否则不会要求你验证密钥。)
当 CVS 开始比较你磁盘上的源代码与服务器上的源代码时,会有一个暂停,当你认为出了问题的时候,你可能会看到实际的源代码更新,如下所示:
U bin/systrace/intercept-translate.c
U lib/libssl/src/crypto/mem.c
U lib/libssl/src/crypto/asn1/a_d2i_fp.c
U lib/libssl/src/crypto/buffer/buffer.c
U sys/conf/newvers.sh
U sys/netinet6/ip6_output.c
U usr.sbin/nsd/query.c
这是更新 OpenBSD 5.1 版本到 -stable 的完整 CVS 更新输出。总共进行了七次更改。当 OpenBSD 的人说“-stable 中最小化更改”时,他们确实是这么说的。
对 */usr/ports* 和 */usr/xenocara* 重复此操作。
cd /usr
cvs -qd anonymous@anoncvs13.openbsd.org:/cvs get -rOPENBSD_5_1 -P ports
cvs -qd anonymous@anoncvs13.openbsd.org:/cvs get -rOPENBSD_5_1 -P xenocara
源代码树记录了最后更新的服务器,在后续更新中,您可以从命令行中省略服务器。源代码树还知道它应该从服务器中的哪个存储库更新,因此您不需要指定它。
cd /usr/src
cvs -q up -rOPENBSD_5_1 -Pd
每次您想要获取最新的 -stable 源代码时,请运行此相同命令。
#### 更新到 -current
将您的源代码更新到 -current 的过程与更新到 -stable 类似,但命令行略有不同。第一次更新源代码树时,您必须指定服务器名称。
cd /usr
cvs -qd anonymous@server:/cvs get -P src
此 `cvs` 命令为您提供最新的 -current 源代码。例如,如果我想从 *anoncvs13.openbsd.org* 更新我的源代码,我会运行这个命令:
cd /usr
cvs -qd anonymous@anoncvs13.openbsd.org:/cvs get -P src
您应该收到 SSH 密钥指纹验证消息,并看到与更新到 -stable 时相同的类型消息。
第一次运行 CVS 更新特定源代码集时,`cvs` 会记录源代码来自哪个服务器。对于后续更新到最新 -current 源代码,您可以省略服务器名称并缩短命令,如下所示:
cd /usr/src
cvs -q up -Pd
现在,您需要将新源代码构建成程序代码。
您以相同的方式构建 -stable 和 -current,但 -current 有更多潜在问题,因此我们将首先构建 -stable。
## 构建 OpenBSD-stable
构建 OpenBSD-stable 需要构建和安装新的内核,构建和安装新的用户空间,以及构建和安装新的 Xenocara。
### 升级内核
构建升级内核就像构建自定义内核,如第十九章(ch19.html "第十九章。构建自定义内核")中所述。基本上,这个过程归结为以下几点:
cd /usr/src/sys/arch/platform/conf/
config GENERIC
cd ../compile/GENERIC
make clean && make
make install
(如果我在这台机器上定期构建 OpenBSD,我会编写脚本。)
### 注意
第十九章 中充满了关于编译自定义内核困难的警告。这些警告不适用于在 -stable 上构建 *GENERIC* 内核。请记住,-stable 仅包含安全修复和显然正确的小改动。ABI、API、配置和语法更改绝对禁止。如果此内核构建失败,您可能以某种方式损坏了源代码树。请从 */usr/src* 中删除所有内容并重新开始。
在构建和安装新内核后,重新启动。您必须运行新内核来构建新的用户空间。
### 构建用户空间
要构建和安装内核外的所有内容,请删除任何旧构建并重新创建对象目录。同时,确保已安装的 OpenBSD 系统具有所有必要的目录。跳过此步骤将导致构建失败并损坏您的源代码树。
rm -rf /usr/obj/*
cd /usr/src
make obj
cd /usr/src/etc
env DESTDIR=/ make distrib-dirs
现在,您可以构建和安装用户空间。
cd /usr/src
make build
在旧式系统上构建用户空间可能需要几天时间,但在更现代的硬件上只需一两个小时。当过程完成后,您应该在本地系统上安装了新的用户空间。
### 注意
安装 -stable 简化了升级的用户空间部分。无需使用 `sysmerge` 合并任何新的 */etc/* 文件(但运行 `sysmerge` 也没有害处,只是为了检查您的配置确实有效),也不需要重新创建您的设备节点,因为它们不会改变。OpenBSD-stable 包含了基于其发布的非常小的更改。
### 构建 Xenocara
X Window 系统在 -stable 期间可能会改变,也可能不会改变。如果您的 CVS 更新显示 -stable 中没有变化,您不需要重新构建它,但如果 */usr/xenocara* 中的任何文件已更改,您最好重新构建 X。对于 Xenocara-stable,构建过程完全是常规的。
cd /usr/xenocara
rm -rf /usr/xobj/*
make bootstrap
make obj
make build
这将在您的系统上构建和安装新的 X。
### 构建 Release
如果您只想升级一台机器,这一切都很不错。但如果您有多个机器要升级到您定制的 OpenBSD 构建,怎么办呢?您不想在防火墙或您的 Web 服务器上构建 -stable。
如果您需要升级多台机器,请在单台机器上构建您的 OpenBSD,并通过构建自己的 *release* 在多台机器上安装该构建。这将比您构建升级并使用所需的磁盘空间少得多。
发布是 OpenBSD 项目放在镜像站点上供您安装的内容。它包含几个内核、包含用户空间文件的 tarball、索引文件等。使用这些文件,您可以像本章前面讨论的官方媒体升级一样升级您的其他主机。构建发布是升级多个 OpenBSD 主机到相同版本的最简单方法。
在您构建 release 之前,您必须构建基础 OpenBSD 系统和 Xenocara。如果您不想在任何主机上安装任何 X,则可以跳过 Xenocara,但许多第三方程序需要 X 库。构建 X 而不安装到选定的主机上比因为第一次没有构建 Xenocara 而重新构建整个 release 要容易得多。
发布过程在临时根目录中安装 OpenBSD,然后将该安装捆绑到发布 tarball 和相关文件中。接下来,它使用 X 软件重复此过程。它假设您在 */usr/src* 中有 OpenBSD 源代码,在 */usr/obj* 中有一个完成的构建,以及 */usr/xenocara* 中的 Xenocara 源代码和 */usr/xobj* 中的完成构建。您可以根据 `release(8)` 中的文档更改构建过程以绕过这些要求,但您应该接受默认设置。
您需要定义三个目录:一个用于存储您的发布版本,一个用于您的临时 OpenBSD 根目录,另一个用于您的临时 Xenocara 根目录。您可以重复使用临时根目录,但我保留它们以供参考。我使用 */home/releasedir* 作为我的发布目录,*/home/destdir* 作为我的临时 OpenBSD 根目录,以及 */home/xdestdir* 作为 Xenocara 的临时根目录。
### 警告
您可以使用任何有足够空闲空间的分区,除了 */mnt*,因为发布过程使用这个分区。同样,构建发布版本会使用第一个 `vnode` 设备,*/dev/vnd0*,来构建 ISO 和软盘镜像。如果您使用该设备挂载了任何磁盘镜像(见第九章),发布过程将失败。如果您在构建发布版本时必须挂载磁盘镜像,请使用除 */dev/vnd0* 之外的其他 `vnode` 设备。
发布过程有三个步骤:打包基础系统、打包 Xenocara 和索引结果。
#### 打包基础系统
OpenBSD 的构建系统包括构建发布版本所需的所有粘合剂。首先,执行 `make build` 以运行您的新 OpenBSD,确保您正在运行您想要构建发布版本的相同版本。接下来,在您的环境中分别定义临时 OpenBSD 根目录和发布目录为 `$DESTDIR` 和 `$RELEASEDIR`。
### 注意
在您开始之前,请确保临时 OpenBSD 根目录和发布目录为空。虽然发布过程可以覆盖旧构建的文件,但目录可能包含您不希望包含在新发布中的过时文件。
echo $DESTDIR
/home/destdir
echo $RELEASEDIR
/home/releasedir
一旦准备好,构建发布版本只需要几个命令。
cd /usr/src/etc && make release
cd /usr/src/distrib/sets && sh checkflist
查看您的发布目录。您应该看到以下项目:
+ 三个内核(*bsd*、*bsd.mp* 和 *bsd.rd*)
+ 如果您为 i386 构建则需要三个软盘引导镜像,如果是为 amd64 构建则只需要一个(其他架构有所不同)。
+ 两个 ISO 镜像
+ 五个 OpenBSD 基础系统的文件集
这些文件在功能上与 OpenBSD 项目分发的文件相同,但它们基于您的自定义构建。
一旦您完成构建发布版本,请务必从您的环境中删除 `$RELEASEDIR` 和 `$DESTDIR` 变量,因为它们可能会破坏其他软件的构建。如果您仍然设置这些变量,则无法成功构建 Xenocara。
#### 打包 Xenocara
与打包基础系统一样,您必须首先完成 Xenocara 的构建。确认您的系统已安装与您想要包含在发布版本中的相同版本的 Xenocara,然后设置 `RELEASEDIR` 和 `DESTDIR` 环境变量。`RELEASEDIR` 应与用于打包基础系统的目录相同,但 `DESTDIR` 应该不同。
### 注意
您可以重复使用 `DESTDIR`,但这将擦除您的临时基础系统安装中的所有内容。保留这些文件,直到您确信您有一个稳定的发布版本。
echo $DESTDIR
/home/xdestdir
echo $RELEASEDIR
/home/releasedir
现在,进入 Xenocara 源代码并打包发布版本。
cd /usr/xenocara
make release
Xenocara 并不比基本系统小多少。捆绑需要一段时间,所以这是一个去吃午饭的好时机。当你回来时,你应该在你的发布版目录中找到五个新文件,所有以 *x* 开头的 tarball。
在你一天结束时,从你的环境中删除 `RELEASEDIR` 和 `DESTDIR`。
#### 发布版的索引
将你的发布版复制到你的本地 FTP 或网页服务器上,并创建内容索引。(只有 HTTP 安装和升级需要索引文件。)OpenBSD 安装程序和升级软件将在安装期间使用此索引。
/bin/ls -l > index.txt
使你想要升级的机器可以访问网页或 FTP 站点。
### 使用发布版
使用此发布版升级或安装,就像使用官方发布版一样。将 *bsd.rd* 复制到要升级的机器上,然后引导到该内核。当安装程序询问文件集的位置时,给出你发布版的位置。提取并重新引导!
## 构建 OpenBSD-current
构建 OpenBSD-current 与构建 -stable 几乎相同,除非它不是。OpenBSD-current 可能会剧烈变化,从源代码构建它被认为是一项高级活动。我实际上构建 -current 的唯一时间是如果需要测试一些 OpenBSD 开发者提供的新功能或补丁。
两个主要问题是 -current 中的激进变化和合并 */etc*。
### 跟踪 -current
当你跟踪 -current 时,请密切关注 OpenBSD 的变化,通过访问 *http://www.OpenBSD.org/faq/current.html* 上的 Following -current 网页。这是 OpenBSD 开发者列出所有可能影响尝试构建 -current 的人的变化的地方。并非所有变化都适用于所有系统,但任何列出的适用于你系统的变化都需要特殊处理。
条目是按时间顺序排列的,包括自上次发布以来的所有内容。只需关注日期在或晚于你用来构建快照的源代码日期的条目。例如,如果你构建了一个 1 月 30 日的快照,而你想在接下来的 2 月 5 日构建 -current,检查这两个日期之间的网页上的任何条目,包括这两个日期。较早的变化已经包含在安装的快照中。
一些变化在你尝试构建系统之前就需要你的干预。例如,如果你有新的非特权用户名,它们在 `make build` 成功之前必须就位——毕竟,由用户 `_fdisk` 拥有的程序除非该用户就位否则无法安装。
如果你不懂这个页面上的某个条目,请不要升级!
### 合并 /etc
当你升级到新的 -stable 时,你可以确信 */etc/* 中的文件没有变化。当你跟踪 -current 时,那些关键系统文件可能会发生变化。任何关键变化通常都会在 Following -current 网站上注明,但最好使用 `sysmerge(8)` 合并所有 */etc* 的变化。你可以给 `sysmerge` 提供系统源路径而不是 *etc.tgz* 文件集。
sysmerge -s /usr/src
有关`sysmerge`的详细信息,请参阅本章前面的相关部分。
## 升级 Ports
如果你使用 OpenBSD 提供的软件包,升级系统就像运行`pkg_add -ui`一样简单。然而,如果你使用 ports 集合从源代码构建第三方软件包,那么升级就没有那么简单了。你必须重新构建这些软件包。没有自动化的方法来做这件事,但 ports 中的`make update`命令可以重新构建特定的 ports。
很可能,你构建了自己的软件包,因为 OpenBSD 提供的软件包缺少了你需要的某些选项或版本。在这种情况下,你可能只需要从源代码构建一到两个软件包。该软件包所需的所有软件都可以从官方 OpenBSD 源安装。你应该通过软件包升级尽可能多的内容,并且只重新构建绝对必要的部分。
现在你可以用任何方式升级 OpenBSD,让我们来看看 OpenBSD 的包过滤功能。
* * *
^([42]) Henning Brauer 告诉我,许多升级失败并不是真的不可预测;它们只是“不受支持和未经测试”的代码路径。对我们大多数人来说,这就像是“不可预测”的,但你可以自己预测它们。
^([43]) 我知道,我知道,你的 shell 比我高级。我大约 30 年前就被分配了 tcsh 作为我的第一个 shell,我的手指已经习惯了它,无法改变。如果你停止告诉我这一点,我会承认你的优越性。
^([44]) 再次强调,没有*anoncvs13.usa.openbsd.org*。找一个离你近的服务器。别盲目地复制我的例子。好像你认为我知道自己在做什么似的!
## 第二十一章。数据包过滤
*名字是 Pond,詹姆斯·庞德。*
*我的 x86 已加载,*
*授权过滤。*
 数据包过滤和流量操作是网络安全中最基本的工具之一。OpenBSD 包含一个非常强大的内核级数据包过滤器 `pf(4)` 或 PF。这个工具不仅执行标准过滤,还可以以多种方式检查、重新组装、重定向以及其他方式滥用数据包;同时以多个不同方向转换地址;验证用户;并管理带宽。与 PF 一起,OpenBSD 包含允许您将系统转换为负载均衡器、透明代理或其他网络设备的程序。
PF 是 OpenBSD 的亮点之一,值得拥有一本自己的书。这本书是 Peter Hansteen(No Starch Press,2010)所著的 *《PF 权威指南》*,*第 2 版*,详细介绍了许多不同的 PF 应用场景。本章涵盖了 PF 的基础知识,以便您能够保护一个小型网络或单个服务器。如果您想保护一个 Web 农场,并且只将流量透明地转发到具有足够空闲容量来处理负载的活跃服务器,请获取 Peter 的书。
话虽如此,即使是 Peter 的书也没有涵盖 PF 的全部内容。OpenBSD 允许您对 TCP/IP 进行折叠、拉伸和扭曲,远远超出了任何合理的人在任何实际环境中可能期望支持的范畴。有关 PF 的完整详细信息,请阅读 `pf(4)`、`pfctl(8)`、`pf.conf(5)` 手册页,以及 OpenBSD PF FAQ 在 *[`www.OpenBSD.org/faq/pf/`](http://www.OpenBSD.org/faq/pf/)*。
### 注意
PF 仍在积极开发中。虽然配置语法不像以前那样频繁变化,但请检查 `pf.conf(5)` 以获取关于您版本 OpenBSD 的最新信息。
## 防火墙
在过去 20 年左右的时间里,*防火墙* 这个词已经被折磨得无法辨认,以至于它不再具有特定的含义。一般来说,防火墙位于私有网络和公共网络之间,并控制两者之间的流量。
您可以用不到 100 美元的价格购买一个用于电缆调制解调器的防火墙,也可以用 100 万美元购买一个企业级防火墙集群。区别在哪里?它们都是防火墙,就像老鼠、猫和象都是哺乳动物一样,但有些是受欢迎的,而大多数则不然.^([45]) 当然,您允许的当然是您个人的偏好。而且防火墙也大致如此。
一些防火墙过滤应用层流量。一些仅基于协议或端口进行过滤。一些防火墙检查协议标志并确保流量合理性。其他只是传递数据包。还有一些防火墙只是转换网络地址,并声称这提供了安全性。更糟糕的是,价格标签与功能集没有任何关系。
在最基本的情况下,所有防火墙都会过滤数据包并可以执行网络地址转换(NAT)。OpenBSD 可以像大多数商业防火墙一样,甚至更好地进行这些任务。但是,如果你想要应用程序代理,它们并不包含在核心 OpenBSD 系统中(除了 FTP 和 TFTP 代理,这些代理对于这些协议与 NAT 一起工作是必要的)。几个流行的应用程序代理在 OpenBSD 上运行得相当好,但它们不是 OpenBSD 的一部分。例如,我使用 Squid(*/usr/ports/www/squid*)和几个相关软件包在 OpenBSD 上构建了一个与大型公司提供的任何东西相媲美的 Web 代理和过滤器,以及一系列其他代理来管理几乎所有其他事情。如果你对防火墙感兴趣,我强烈建议你至少一次从可用的组件中组装一个功能强大的防火墙,以教育为目的,如果其他什么也不做的话。
防火墙是你自己定义的。你可以将所有流量通过简单的 OpenBSD 数据包过滤器发送,并诚实地宣称你有一个防火墙,或者你可以设置应用程序代理、身份验证等,仍然可以说你有一个防火墙。一个简单的数据包过滤器就像那些集成应用程序代理、价格昂贵的设备一样,是一个防火墙。下次有人说他有一个防火墙时,请记住这一点。
实际上,防火墙不是一个安全设备。它是一个政策执行点。46 防火墙不会保护任何东西;它阻止对某些服务的访问。但是阻止访问并不 inherently secure inherently insecure services—it just means you can’t access them. 如果你的防火墙允许访问某个服务,防火墙不会为该服务增加任何安全性。
为了构建一个有效的防火墙,你必须理解 TCP/IP。如果第十一章对你来说是一个启示,请获取一份*The TCP/IP Guide*(No Starch Press,2005)的副本。阅读它。标记它。突出显示它。然后再读一遍。
本章中的许多示例都假设你在构建防火墙。这意味着你的主机有两个或更多网络接口(包括 VLAN 接口),并且你想要保护一侧的网络不受另一侧网络的影响。虽然这是 OpenBSD 的一个流行应用,但这里涵盖的所有内容在单个主机上同样适用。我在孤立的 Web 服务器、桌面和任何裸露在互联网上的主机上过滤数据包。
## 启用和配置 PF
OpenBSD 在系统启动时默认启用 PF,使用以下*rc.conf*变量:
pf=YES
pf_rules=/etc/pf.conf
要在启动时禁用 PF,请在*rc.conf.local*中将`pf`设置为`NO`。
PF 的默认配置文件是*/etc/pf.conf*。这个文件并没有什么特殊之处——它只是一个标准位置。`pf(4)`内核接口不会直接读取该文件;PF 控制程序`pfctl(8)`读取文件并将配置发送到内核。
默认的 PF 配置(硬编码在*/etc/rc*中)阻止了除 ICMP 和 SSH 之外的所有网络流量。在引导过程中,PF 用*/etc/pf.conf*中的规则替换这些默认规则。如果*pf.conf*中的错误在系统引导时使文件不可解析,PF 无法加载这些规则;相反,它保留默认配置。您将能够连接到您的机器以纠正您的规则,但这就足够了。(而且,正如任何管理远程防火墙的人都可以告诉您的那样,这种能力可以为您节省大量的驾驶和电话费。)
默认运行 PF,即使有宽容的规则集,也会在内核的其他部分处理之前清理传入的流量。PF 在将数据包交给内核之前重新组装数据包,并且显然是虚假的流量,如太短而无法成为合法的数据包,将被丢弃。
如果您想在接口之间转发数据包(即,充当“防火墙”),请告诉内核使用`net.inet.ip.forwarding`和`net.inet6.ip6.forwarding` sysctls 转发数据包。(有关已注释示例,请参阅*/etc/sysctl.conf*。)
net.inet.ip.forwarding=1
net.inet6.ip6.forwarding=1
移除井号并重新启动,或者使用`sysctl(8)`在运行时启用和禁用数据包转发。
## 数据包过滤基础
数据包过滤是将数据包与规则列表进行比较,并根据这些规则接受、拒绝或修改它们。作为网络管理员,您有权决定哪些数据包是“坏”的,哪些是“好”的。当您为单个主机过滤数据包时,您可以合法地称该主机为“加固的”。(“加固”一词几乎与“防火墙”一词的意思完全相同:什么都没有。)当您将您的网络上的所有数据包通过一个过滤数据包的主机发送时,您就有一个基本的防火墙。
基本的数据包过滤器可能只允许您根据 TCP 或 UDP 协议号进行过滤。有些甚至不允许您根据 ICMP 类型进行过滤,或者无法处理 GUI 中未列出的协议。然而,PF 可以处理您向其投掷的几乎所有内容。如果您需要一台机器通过 IP 协议 184 与其他机器通信,PF 将支持您。许多商业防火墙不会允许您通过此类流量,或者声称可以,但如果你实际尝试,它们会大发雷霆。
### 数据包过滤概念
第十一章描述了 TCP 连接可以处于各种状态。一个刚开始的 TCP 连接会经历一个三次握手过程。客户端通过向服务器发送一个同步请求,或 SYN,数据包来请求连接。服务器通过向客户端发送 SYN 的确认以及它自己的 SYN 请求,或 SYN+ACK 数据包来响应。客户端随后发送自己的 ACK。
这个三次握手过程的每一部分都必须完成,才能在两台机器之间传输实际数据。您的数据包过滤规则必须允许三次握手和随后的数据传输的每一部分。PF 通过*状态检查*自动识别这些三次握手并跟踪它们。
#### 状态检测
PF 维护一个已完成的连接设置允许连接的列表,这被称为 *状态表*。当客户端发送一个 SYN 数据包时,PF 将该数据包记录在表中,并等待相应的 SYN+ACK 数据包。如果 PF 收到 SYN+ACK 数据包,但没有记录相应的 SYN 请求,则拒绝该 SYN+ACK 数据包。
PF 有一系列内置的超时时间,这些时间决定了空闲连接在状态表中保持多长时间,等待三次握手每个阶段的时长,等等。状态表是自我维护的,我从未需要调整这些超时时间。(偶尔,我需要增加状态表的最大大小。)
UDP 在技术上是无状态的,但某些应用程序期望一定量的状态。当你的系统传输一个 UDP 数据包时,应用程序可能会期望一个 UDP 数据包或 10 个响应,或者没有数据包,这取决于应用程序。
DNS 查询是 UDP 数据包往返流动的常见例子,虽然 UDP 没有状态,但 DNS 确实有。 (ICMP 的行为类似。)你可以根据你的协议,通过将这些流添加到状态表中来让 PF 期望或不需要这种往返。
### 注意
PF 也可以在不进行状态检测的情况下运行,允许根据单个数据包特征从主机和端口传输流量。*无状态过滤* 比状态检测慢,更难正确配置,通常被认为比状态检测不安全且不那么有用。
#### 数据包重组
数据包在传输过程中可能会被破坏,通常是通过 *分片*。数据包过滤器的部分工作就是合理地 *重组* 这些数据包。PF 可以以多种方式重组和合理化数据包。(旧版本的 PF 将此称为 *清洗*。)
#### 默认接受与默认拒绝
数据包过滤中的一个基本概念是默认接受与默认拒绝的问题:
+ *默认接受* 立场意味着你允许任何类型的连接,除了你明确拒绝的连接。默认的 PF 规则是默认接受立场的例子。
+ *默认拒绝* 立场意味着你只允许明确允许的连接。所有其他连接都被拒绝。
一旦你选择了默认设置,你可以调整你的规则,根据需要隐藏或显示网络服务。在当今世界,我建议在所有系统上使用默认拒绝,因为这种立场在将新服务添加到系统时保护了这些服务。在过去十年中,我看到的大多数使用默认接受立场的环境,是因为系统管理员没有理解他们所使用的网络协议。这在 VoIP 安装中尤其常见(是的,你可以对 VoIP 服务器进行数据包过滤!)
除了数据包过滤和重组之外,PF 还提供了一些其他重要功能,包括 NAT、连接重定向和带宽管理,仅举几例。我们将分别考虑。所有这些都在`*pf.conf*`中配置,并通过`pfctl(8)`进行管理。
### “我的网络不会出错”
许多构建防火墙的网络管理员会仔细过滤和限制入站流量,但只在出站流量上施加最小限制。虽然对入站流量的控制是网络管理中最直接的问题之一,但出站流量的控制也同样重要。
即使你信任你的用户,恶意软件也可以将一名熟练工程师的工作站变成垃圾邮件喷发虫。不要假设你的网络不会出错。它可以是有害的,而且总有一天会变成这样,但仔细的流量控制可以最小化你对邻居、客户、客户和声誉造成的损害。
你的员工桌面是否有理由连接到任何随机的远程邮件服务器?如果没有,就阻止它,即使工作站感染了垃圾邮件机器人,世界上的其他人也不会将你列入黑名单。用户是否有理由连接到远程 DNS 服务器,或者他们应该使用公司的 DNS 服务器?阻止出站 DNS,防止用户成为拒绝服务攻击的无意识放大器。我强烈建议对出站和入站流量都采取默认拒绝立场,并明确允许期望的流量。
当然,有些网络可能是个例外。如果你的网络上的每个系统都运行 OpenBSD,你将相当安全,免受常规恶意软件的侵害,但我们已经看到恶意软件针对电视、蓝光播放器、流媒体播放器和其他具有网络连接的设备。现在就保护自己。
无论何时你发现自己认为你的网络不会出错,请停下来提醒自己,你并不像世界上每个恶意软件作者的组合那样聪明。
### 数据包过滤无法做到的事情
数据包过滤根据 TCP/IP 协议和相关特性(如端口号)完全控制网络连接。如果你想阻止来自某些 IP 地址的所有流量,数据包过滤是你的朋友。如果你想只允许连接到特定的 TCP/IP 端口,数据包过滤将为你工作。如果你想只允许设置 ECN 标志的包进入,但没有其他标志,PF 将支持你(尽管这听起来相当愚蠢)。
你可以过滤在逻辑协议层运行的协议,如 IPsec、SKIP、VINES 等,但仅限于网络协议。如果是在不同的协议层,PF 就无能为力了。
### 注意
PF 甚至可以根据 MAC 地址进行过滤。通过在`bridge(4)`接口上添加的标签,对这种特定的媒体层协议提供了特殊支持,如`ifconfig(4)`文档所述。
类似地,PF 对应用程序或应用程序协议一无所知。如果您允许您的网络内服务器的 TCP/IP 连接到端口 25,您可能会认为您正在允许连接到该主机上的邮件服务器。实际上,您正在允许连接到该主机上运行在端口 25 上的任何守护进程!PF 不识别 SMTP 数据流;它只看到连接到端口 25。 (我有一个系统在许多通常分配给其他服务的端口上提供 SSH,这样我就可以轻松绕过任何我可能陷入的简单数据包过滤器。)
## PF 组件
在我们深入研究 PF 之前,让我们看看 OpenBSD 上数据包过滤的基本组件。除了 `pf(4)` 内核模块外,我们还将查看数据包过滤器控制程序和配置文件 */etc/pf.conf*。了解接口组也有帮助。
### 数据包过滤器控制和配置
使用数据包过滤器控制程序 `pfctl(8)` 来管理、配置和从 PF 中提取信息。您可以看到当前的包过滤规则和设置、正在处理连接、TCP/IP 事务的状态、调试信息以及各种其他细节。您还可以解析规则文件并将它们安装到实际的数据包过滤器中。
您将看到许多针对 `pfctl` 的不同选项,涵盖了数据包过滤管理的各个方面。其中许多选项相当长,但您只需输入足够多的单词参数来使命令独特。例如,您不必输入 `pfctl -s rules`,只需输入 `pfctl -sr` 就可以,因为没有其他 `pfctl -s` 的参数以 `r` 开头。话虽如此,我给出所有示例的完整形式,因为无法保证 OpenBSD 不会在未来添加以 `r` 开头的其他参数。
我专注于使用 `pfctl` 来查看 PF 输出,但 OpenBSD 还在 `systat(1)` 中包含了 PF 视图。对于 PF 活动的动态显示,类似于网络中的 `top(1)`,请查看 `systat`。通过提供视图名称作为参数来运行 `systat`,例如 `systat pf`。并且,一如既往,如果您想从 `pfctl` 获取更多详细信息,请添加一个或两个 `-v` 参数以启用详细模式。
您在 */etc/pf.conf* 中配置 PF。*pf.conf* 文件包含语句和规则,其格式根据它们配置的功能而变化。在我们完成之前,您将非常熟悉这个文件。
### 接口组
OpenBSD 允许您将接口放入命名组中,您可以在 PF 规则中引用这些组。这抽象了实际的物理接口,并允许您构建基于策略的规则集。看看这个接口:
ifconfig em0
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
…
groups: egress
…
此接口位于 `egress` 组中。如果一个默认路由通过该接口到达,则该接口将被分配到 `egress` 组。
要将此接口移动到新的组 `dmz`,请将其从 `egress` 组中删除并添加到 `dmz` 组中。当您将第一个接口分配给它时,将创建一个接口组,一个接口可以属于任意数量的组。
ifconfig em0 -group egress
ifconfig em0 group dmz
ifconfig em0
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
…
groups: dmz
…
你现在可以编写引用接口组而不是特定接口的规则。
### PF 配置
让我们拆解一个 OpenBSD 系统中的默认 *pf.conf* 文件,并识别一些部分。许多默认条目已被注释掉,但识别它们将有助于你理解组件是如何组合在一起的。
它从一个 *选项* 开始:
set skip on lo
选项可以打开或关闭功能,或设置其他功能的行为的一般规则。`skip` 选项根据接口禁用 PF。
接下来是 `anchor` 设置:
anchor "ftp-proxy/*",
一个 *锚点* 是一组用于数据包过滤的动态子规则。如果一个数据包在通过过滤规则处理时击中锚点,它将被丢弃到这个子规则集中进行进一步处理。`pfctl` 可以更改内核中运行的规则,而锚点是一种说“在这里添加新规则”的方式。
锚点通常用于允许外部软件向防火墙添加规则。例如,FTP 是一种复杂的协议,需要各种防火墙规则。OpenBSD 包含一个 FTP 代理,它可以动态地添加允许的 FTP 连接所需的所有规则。
然后是两个 *数据包过滤* 规则:
pass in quick inet proto tcp to port ftp divert-to 127.0.0.1 port 8021
pass
第一个是支持 FTP 流量的规则,与 FTP 锚点结合使用。我们将在下一章中更详细地探讨锚点和 FTP 处理。另一个是一个更简单的数据包过滤规则,允许所有流量。
接下来是两个 *表格*,它们是 IP 地址的列表:
table
table
外部程序可以动态地更改表格,你可以在 *pf.conf* 或外部文件中直接向表格添加地址。这两个表格由反垃圾邮件软件 `spamd(8)` 使用。
表格之后是另一个数据包过滤规则:
pass in on egress proto tcp from any port smtp
rdr-to 127.0.0.1 port spamd
这条规则很有趣,因为它指的是一个接口组。只要流量到达 `egress` 组中的接口,就允许进入。
最后一条规则如下:
block in on ! lo0 proto tcp to port 6000:6010
这条数据包过滤规则阻止流量。如果一个数据包到达除了环回接口之外的任何接口,并且该数据包是 TCP 协议,目标端口为 6000 到 6010(包括 6010),则会被阻止。
这是在 *pf.conf* 中你会看到的东西。让我们深入了解过滤规则的具体细节。
## 过滤规则
过滤规则是 PF 的核心。你可以不进行任何花哨的重定向、地址转换、负载均衡或冗余操作来使用 PF,但数据包过滤是大多数这些功能的基础。然而,首先,基本的包过滤被定义为通过源、目的、协议和协议特性对网络数据包进行访问控制。
PF 按顺序处理过滤规则。最后匹配到数据包的规则将被执行。一个典型的数据包过滤规则看起来像这样:
1pass 2in 3on egress 4proto tcp 5from any 6to 192.0.2.12 7port 80
过滤规则的第一词是一个关键字,描述了该规则的结果**1**。PF 将根据规则“通过”或“阻塞”匹配的数据包。 (还有“匹配”,我们将在下一章中探讨。)该行的其余部分是对匹配数据包的描述。如果数据包与描述匹配,则应用该规则。
第二个语句是数据包的方向。数据包要么进入要么出去。在这个规则中,数据包是进入的**2**——它正在进入系统。我们不仅定义了方向,还定义了一个接口组。数据包必须通过`egress`组中的接口进入此系统才能匹配此规则**3**。
然后我们有几个定义流量特征的语句。(这个规则几乎就像 TCP/IP 的正则表达式。)此规则适用于来自任何 IP 地址**5**的 TCP 连接**4**,如果连接是到 IP 地址 192.0.2.12**6**的端口 80**7**。
如果一个数据包与所有这些特征匹配,它就可以通过。如果这些特征中的任何一个不匹配,数据包就不符合这个规则,PF 会继续处理规则,寻找匹配项。
TCP 和 UDP 规则隐式检查连接状态。匹配此规则的数据包需要是一个 SYN 数据包,即标准 TCP/IP 连接的开始。PF 使用状态表来管理同一连接中的后续数据包(参见过滤规则和状态表)。
### 默认允许或默认拒绝
我之前提到了默认接受与默认拒绝的概念。在您的数据包过滤规则开始时设置这种立场,可以使用以下两个语句之一:
pass
block
默认的*pf.conf*具有默认通过立场,但它是为尚未配置防火墙的人准备的。我建议从单独的`block`语句开始您的过滤规则,然后添加规则以明确允许期望的流量。请记住,最后一个匹配的规则获胜。
### 数据包模式匹配
PF 最密集的部分之一是用于描述数据包的语法。大多数过滤规则通过协议、端口、方向和其他特征来描述数据包。PF 将每个到达的数据包与状态表进行比较,如果数据包不是状态表的一部分,它将数据包与过滤规则进行比较。如果规则与数据包描述匹配,则按需通过或阻塞数据包。
一旦您定义了您是在默认接受还是默认拒绝立场,过滤规则就描述了默认规则的例外。因此,如果您默认阻止数据包,您的大多数过滤规则将是描述特定期望连接的“通过”语句。
#### 方向
关键字`in`和`out`描述了数据包的传输方向。在许多商业防火墙中,`in`一词表示进入受保护网络的流量,而`out`指的是离开受保护网络的流量。OpenBSD 不会神奇地知道网络的哪一侧是受保护的,哪一侧不是。在 PF 看来,它正在管理两个接口之间的流量。关键字`in`表示从网络流入机器的流量,而`out`表示离开机器并进入网络的流量。
当你在 PF 规则中看到`in`或`out`时,不要将整个网络视为一个整体。相反,想象你自己非常小,坐在 CPU 上,在散热器上烤牛排,并观察数据包进入和离开计算机。你无法看到箱体之外的情况,只能看到来去的数据包。进入的数据包正在接近你,而出去的数据包正在远离你。
#### 接口匹配
`on`关键字描述了此规则适用的接口或接口组。您必须指定一个接口。
如果你想让规则匹配系统上的每个接口,请使用接口名称`all`。此示例阻止所有进入机器的接口`fxp0`上的流量,但允许所有通过接口组`egress`离开系统的流量:
block in on fxp0
pass out on egress
此规则集意味着接口`fxp0`由于某种原因而特殊,因此它不像`egress`组中的其他接口那样被处理。
#### 地址族
规则可以应用于特定的地址族,即`inet`用于 IPv4 或`inet6`用于 IPv6。以下是禁止 IPv4 但允许 IPv6 的方法:
block in on egress inet
pass in on egress inet6
假设您有更严格限制 IPv6 的后续规则。
#### 网络协议
PF 可以通过数字或名称识别几乎任何网络协议。`proto`关键字告诉 PF 匹配一个协议。网络协议可以通过名称从`/etc/protocols`、协议号或甚至列表(见使用列表)提供。
block in on egress proto tcp
pass in on egress proto udp
您可以使用此方法来通过除 IP 和 IPv6 之外的其他协议。以下是允许 IPsec 所需协议的方法:
pass in on egress proto esp
pass in on egress proto ah
此功能与`inet`和`inet6`语句有些重叠。如果您愿意,您可以明确允许 IP、ICMP、TCP、UDP 以及所有各种 IPv6 协议。
#### 源地址和目标地址
几乎每个过滤规则都指定了源地址和/或目标地址。
pass in on egress from 198.51.100.0/24 to 192.0.2.0/24
IP 地址可以以单个地址或带有子网掩码的地址(如前例所示)的形式出现。关键字`any`表示任何 IP 地址。关键字`all`是“从任何到任何”的简称。
您也可以使用主机名而不是 IP 地址。`pfctl`在加载规则时会检查主机的 IP 地址,并将实际 IP 地址插入到规则中。
pass in on egress from www.michaelwlucas.com
如果主机的 IP 地址发生变化,PF 不会注意到,直到你使用`pfctl`重新加载规则。如果找不到主机名,规则将无法解析,`pfctl`将无法加载它们。我建议不要在过滤规则中使用主机名,就像我建议在游泳时不要穿中世纪的板甲一样,但它是一个可用的选项。
要表示“除了这个地址之外的所有内容”,请使用感叹号作为否定字符。
block in from !192.0.2.0/24
这表示“除了地址 192.0.2.0/24 之外,所有内容都被阻止。”这与说“允许 192.0.2.0/24”不同,但它可以帮助简化你的规则。
你也可以使用列表、宏和表格作为 IP 地址。列表和宏将在本章后面讨论,表格将在下一章介绍。
#### 源和目标变体
你可以使用接口或接口组的名称而不是 IP 地址。
pass out on egress from egress
这允许流量通过出口接口组离开,从该组中任何接口的任何 IP 地址到任何 IP 地址。
如果你将接口名称或组放入括号中,PF 会在接口上的 IP 地址更改时更新其规则。这对于拨号连接或当你从接口添加和删除 IP 地址时非常有用。
pass out on egress from (egress)
你可以通过在名称后跟`:network`来指定直接连接到接口或接口组的网络。
pass in on egress from egress:network
假设出口组只有一个接口,并且该接口的 IP 地址为 192.0.2.88/25。这条规则将转换为以下内容:
pass in on egress from 192.0.2.0/25
这条规则意味着任何连接到出口接口的主机都可以在任何地方进行通信。当你向出口组添加另一个接口时,规则会自动更新以适应新接口的网络。
要过滤接口或组的广播流量,请使用`:broadcast`修饰符。
block in on egress from egress:broadcast
再次假设出口组只有一个接口,并且该接口的 IP 地址为 192.0.2.88/25。这条规则将转换为以下内容,在本地子网上阻止广播流量:
block in on egress from 192.0.2.127
使用`:peer`修饰符来指示点对点链路(如拨号连接)另一端的 IP 地址。
pass in on egress from egress:peer
在这里,我们完全信任我们的拨号服务提供商。
#### 接口主地址
要仅使用接口上的第一个 IP 地址,请使用接口或组名称添加`:0`修饰符。
pass out on egress from (egress:0)
出口接口组可能有 98 个 IP 地址分布在三个接口上,但每个接口上只有一个地址是第一个地址。此主机可以通过出口接口组进行通信,但只能从主 IP 地址进行通信。别名 IP 地址不能发起出站连接。
`:0` 修饰符的问题在于内核对接口上“第一个”地址的概念非常模糊。内核有一个与接口关联的地址列表。列表顶部的地址是当前“第一个”或“主要”地址,但这个地址可能会改变。如果这可能会引起问题,请在你的规则中指定 IP 地址,而不是依赖 `:0`。
你可以将 `:0` 附在任何其他接口修饰符上,即除了规则中的第一个地址之外的 IP 地址。OpenBSD 无法判断远程机器上的 IP 地址是别名还是实际 IP 地址,但你可以在本地机器上禁止对或来自别名的流量。
注意,接口上的第一个地址要么是 IPv4 地址 *要么* 是 IPv6 地址。如果你想允许每个协议的第一个地址,请在规则中指定地址族。
pass out on egress inet from egress:0
pass out on egress inet6 from egress:0
否则,PF 将仅使用它看到的第一个地址,无论地址族如何。
#### 源和目的端口
过滤规则可以描述 TCP 和 UDP 端口。
pass in on egress proto tcp from any to 192.0.2.12 port 80
此示例允许访问服务器 192.0.2.12 上的 TCP 端口 80。这可能是 Web 服务器。
你可以使用来自 */etc/services* 的服务名称而不是端口号,或者甚至使用列表(如本章后面所述)。你还可以使用范围,如表 21-1 所示。
表 21-1. 表 21-1:端口范围
| 符号 | 含义 |
| --- | --- |
| `!=` | 不等于 |
| `<` | 小于 |
| `>` | 大于 |
| `<=` | 小于等于 |
| `>=` | 大于等于 |
| `><` | 范围 |
| `<>` | 反向范围 |
例如,要指定所有大于 1024 的端口,你可以使用大于运算符(`>`)。
pass in on egress proto tcp from any to 192.0.2.12 port > 1024
要指定 1000 和 2000 之间的所有端口,不包括 1000 和 2000,请使用范围运算符(`><`)。
pass in on egress proto tcp from any to 192.0.2.12 port 1000 >< 2000
要将端口 1000 和 2000 包含在你的范围内,使用包含范围运算符(`:`)。(注意,冒号两侧不能有空格。)
pass in on egress proto tcp from any to 192.0.2.12 port 1000:2000
要传递所有小于 1000 且大于 2000 的端口的流量,请使用反向范围运算符(`<>`)。
pass in on egress proto tcp from any to 192.0.2.12 port 1000 <> 2000
范围允许你在非常少的规则中表达大量的端口。
### 完整规则集
以下是一个桌面机的完整规则集,使用了之前描述的许多功能。我们将在后面查看一些更复杂的规则集,但这个例子说明了 PF 规则的许多基本原理。
接口组 `egress` 连接到公共网络,而接口组 `inside` 连接到我的私有网络。
1 set skip on lo
2 block
3 pass in on egress from egress:network
4 pass in on inside from inside:network
5 pass in on egress proto tcp from any to egress:0 port 22
6 pass out all
第一条规则禁用了回环接口 **1** 的数据包过滤,第二条定义了一个默认拒绝立场 **2**。第二条和第三条规则允许来自直接连接到外部 **3** 和内部接口 **4** 的 IP 地址的所有连接。如果我在我的桌面上安装一个 Web 服务器,我希望能够从网络中任何我控制的机器上查看它。然后我允许来自世界任何地方的任何 `egress` 接口的 IP 地址的 SSH 连接进入。最后,我允许所有出站流量,这样我的桌面就可以自由访问外部世界 **6**。
我之前说过,PF 规则按顺序处理,这些规则说明了这一点。我设置了一个默认规则,阻止所有流量,然后使用单个规则来排除全局阻止的例外。
### 激活规则
为了使你的 PF 规则生效,你必须使用 `pfctl -f` 将它们加载到内核中。
pfctl -f /etc/pf.conf
首先,`pfctl` 读取并解析规则文件。如果文件解析正确,`pfctl` 将扩展文件中的任何变量,执行任何必要的 DNS 查询以将主机名转换为 IP 地址,并将完整的规则馈送到内核。内核读取新的规则,然后在一次操作中在旧规则和新规则之间切换。在任何时候,数据包过滤规则都不会缺失、混乱或两个规则集的混合体。另外请注意,`pfctl -f` 不会在禁用的情况下启用 PF。
个人来说,我喜欢知道我的编辑过的数据包过滤配置在预定更改时间之前已经解析。当宣布给团队“新的防火墙配置将在中午生效”并花了一整天的时间追踪一个放错位置的逗号或括号,你应该放花括号时,这会让人感到尴尬。为了在不安装规则的情况下测试你的语法,请使用 `-n` 标志与 `-f` 一起使用。添加 `-v` 以启用详细模式,以查看 `pfctl` 如何扩展你的宏、组等。
pfctl -nvf /etc/pf.conf
规则可能仍然有错误,但只是理解上的错误,而不是语法错误。
加载新规则不会删除任何现有的开放连接或状态条目。如果我的旧规则集允许出站 SSH 连接,而我从新安装的规则中移除了该权限,现有的 SSH 连接仍然保持开放。我可以使用 `pfctl -k` 特意终止这些连接,或者刷新状态表。
### 查看活动规则
要查看这些规则在 PF 内部的解释,请使用 `pfctl -s rules` 查看当前安装的规则集。以下是 一个完整的规则集 中配置生成的规则:
pfctl -s rules
1 block drop all
2 pass in on egress inet6 from 2001:db8:4::/64 flags S/SA
3 pass in on egress inet from 192.0.2.0/28 flags S/SA
4 pass in on inside inet from 192.168.1.0/24 flags S/SA
5 pass in on egress inet proto tcp to 192.0.2.5 port = 22 flags S/SA
6 pass out all flags S/SA
第一条规则建立了一个默认拒绝立场 **1**。然后我特别允许来自 `egress` 组中接口的本地网络的连接,无论是 IPv6 **2** 还是 IPv4 **3**。这个桌面还接受来自我的私有网络的连接 **4**。
私有网络只允许来自 IPv4 地址的连接,因为私有组中的接口只有一个 IPv4 地址。(我真的很应该添加一个 IPv6 地址,但它没有给我带来任何麻烦,所以我又可能再次忘记它。)然后有一个规则允许传入的 SSH 流量**5**,接着是一个最终规则允许所有传出流量**6**。
如果我在我的桌面电脑上更改任何 IP 地址,防火墙规则会自动更新以适应这些更改。这是接口组的一个非常实用的功能。如果我的桌面电脑经常移动,我会把接口组名称放在括号里,这样 PF 就会监视 IP 地址的更改。
### 注意
你可能会注意到,通过规则以`flags S/SA`结束。这意味着在 SYN 和 ACK 标志中,匹配的包只能设置 SYN 标志,表示这些是建立连接的请求。你可以根据 TCP 标志进行过滤,但这需要深入理解 TCP,大多数人应该永远不要这样做。要了解 SYN 和 SYN+ACK 包如何影响连接,你需要理解状态表。
要查看每个规则被触发多少次,请在`pfctl`命令中添加`-v`。
要查看规则如何影响不断更新的显示中的流量,请运行`systat rules`。
## 过滤规则和状态表
OpenBSD 在状态表中跟踪已批准的连接。属于已批准连接的包允许通过。考虑以下早期示例中的规则:
pass in on egress proto tcp from any to 192.0.2.12 port 80
如果一个包匹配此规则,并且它具有指示这是 TCP 连接开始的 TCP/IP 标志,PF 允许该连接。PF 还在状态表中创建一个条目。如果一个包与状态表匹配,PF 将直接传递该包,而无需查阅规则。
### TCP 状态
首先,我们将查看 TCP 连接的状态表条目。要查看状态表,请输入**`pfctl -s states`**。
pfctl -s states
1all 2tcp 3192.0.2.12:80 <- 4198.51.100.227:55635 5ESTABLISHED:ESTABLISHED
…
这个状态表条目代表一个包过滤器批准的特定连接。这个状态适用于所有接口**1**。如果一个状态只适用于一个接口,你将在这里看到接口名称。
这个 TCP 连接**2**目标是 192.0.2.12 端口 80**3**,来自主机 198.51.100.227 端口 55635**4**。当第一个来自 198.51.100.227 端口 55635 的 SYN 包到达时,PF 将此条目添加到状态表中。当 192.0.2.12 向 198.51.100.227 端口 55635 发送 SYN+ACK 包时,PF 查阅了状态表。这显然是一个与允许的 SYN 包匹配的匹配项,所以 PF 允许了该包,尽管在*pf.conf*中没有明确的规则允许该连接。这两个主机和这两个端口之间的数据交换继续进行。
PF 知道实际的 TCP/IP 数据交换看起来是什么样子。开始时有一个三次握手,当连接完成且 PF 跟踪连接状态时,有一个类似的舞蹈。这个特定的连接在双方**5**上建立,这意味着初始设置协商成功,数据可以自由地来回流动。
如果您的服务器足够繁忙,并且您不断刷新状态表视图,您将捕捉到其他状态下的连接。以下是数据交换结束并被拆除时的相同连接:
all tcp 192.0.2.12:80 <- 198.51.100.227:55635 FIN_WAIT_2:FIN_WAIT_2
### 注意
查看状态表可能存在的一个问题是`pfctl`显示的是快照。当您的眼睛滚动到屏幕底部时,表格已经改变。我个人认为这是唯一能够处理信息的方式。如果您需要在一个不断更新的显示中查看状态,在几乎实时的情况下,请运行`systat states`。
状态表非常具体。允许 198.51.100.227 端口 55635 到 192.0.2.12 端口 80 的状态表条目不允许其他主机和端口之间的流量。PF 知道流量应该如何流动,并且不会允许显然不属于现有 TCP/IP 交换的流量。如果一个来自 198.51.100.227 的数据包看起来像是这个数据交换的一部分,除了它来自端口 55634 而不是 55635 之外,状态表条目不会匹配。同样,如果 PF 知道连接处于`FIN_WAIT_2`状态,或者几乎完成,那么设置 ACK 标志的后续数据包不会匹配并将被丢弃。这是因为来自同一主机、同一端口的 SYN 请求不应该到达——客户端应该知道端口正在关闭之前的连接。新的连接应该来自客户端的不同端口并创建新的状态表条目。
没有状态检查,您需要编写的防火墙规则不仅允许传入流量,还允许响应。您的防火墙规则需要允许对数千个高编号端口的出站连接,而不仅仅是与所需连接相关联的单个端口。基于 TCP 标志的过滤几乎是不可能的。
### 注意
作为 20 世纪 90 年代的一名顾问,我拆除了几条被强行塞入无状态包过滤器的规则,因为这些规则在没有状态检查的情况下并不现实。此外,仔细跟踪数据交换不仅简化了规则,还防止了一系列基于 TCP/IP 的攻击。由于状态检查,您现在很少听到这些攻击。
### UDP 状态
UDP 连接的状态条目与 TCP 连接的状态条目相似。
all udp 192.0.2.12:53 <- 198.51.100.227:38469 SINGLE:MULTIPLE
这是一个 DNS 查询,目标为 192.0.2.12 端口 53,来自 198.51.100.227 端口 38469。客户端发送了一个数据包,目标以多个数据包回复。虽然状态检查不能通过标志识别此连接的状态,但它可以跟踪源地址和目标地址以及端口。你只需要写一条规则允许访问 192.0.2.12 端口 53,状态检查将允许匹配的回复数据包。
### ICMP 状态
ICMP 位于 TCP 和 UDP 之间。PF 了解 ICMP 类型,并知道对 ICMP 数据包的合法响应,通过使用状态检查,你可以自动获得所有这些好处。就像你可以编写允许特定 TCP 标志的规则一样,你也可以编写允许特定 ICMP 类型和代码的规则。我们大多数人无法做到这一点,而那些能够做到的人知道得更多。(指向现有 TCP 或 UDP 状态的 ICMP 错误将与该状态匹配,不需要单独允许。)
### 注意
OpenBSD 的状态检查实际上跟踪的细节比源地址、目标地址和端口更多。将`-v`添加到`pfctl`命令中,以查看更多信息,包括时间、由于状态而通过的数据包数量等。
## 使用列表和宏进行数据包过滤
PF 提供了许多方法,让一条规则引用多个相似的项目,或者用变量符号表示某物。基本的方法是列表和宏。
### 使用列表
*列表* 是在一条规则中表示多个相似项目的一种方式。如果你想要在特定的一组主机上打开特定的 TCP 端口组,并且你的规则条目是重复一条规则并做些小修改,那么你可能想使用列表。例如,打开 80 和 443 端口到一台主机需要两条规则:一条对应每个端口。如果你有 30 台 Web 服务器,你需要 60 条规则。这很麻烦,容易出错,但列表让你更容易表达这些常见的元素。
列表在规则中用花括号表示。为了使规则更易读,你可以在项目之间放置逗号。
pass in on egress proto tcp from any to 192.0.2.12 port {80, 443}
这一条`pf.conf`语句创建了两个规则,同时打开目标主机的 TCP 端口 80 和 443。
pass in on egress from any to 192.0.2.12 port = 80 flags S/SA
pass in on egress from any to 192.0.2.12 port = 443 flags S/SA
你也可以使用列表让这条规则覆盖多个 Web 服务器。
pass in on egress proto tcp from any to {192.0.2.12, 192.0.2.13} port {80, 443}
这将扩展为四条规则:每条规则对应服务器和端口的组合。
记住,列表中的每个条目都会创建自己的规则。列表条目不会组合成一条规则。
### 使用宏
宏是你创建并定义用于 PF 规则中的变量。宏使`pf.conf`更易读、可维护和管理。
宏名称必须以字母开头,但可以包含字母、数字和下划线。你不能给宏起一个在 PF 中其他地方使用的名字,比如`pass`、`block`或`proto`。宏的常见用途包括接口名称、网络地址和端口。
之前,我们看到了一个包括流行的 Web 端口 80 和 443 的列表。你可以将这些作为宏,如下所示:
web_ports="{80, 443}"
因此,我们的示例规则将变成这样:
pass in on egress proto tcp from any to 192.0.2.12 port $web_ports
当与花括号结合使用时,宏可以简化你的`pf.conf`文件。考虑以下`pf.conf`片段:
webservers="{192.0.2.12, 192.0.2.13, 192.0.2.14, 192.0.2.15}"
web_ports="{80, 443}"
pass in on egress proto tcp from any to $webservers port $web_ports
这会扩展为八条规则,但只需要三个易于理解的配置语句。当你添加一个新的 Web 服务器时,将它的 IP 地址添加到`webservers`宏中的列表中。更重要的是,你可能会在规则中的数十个地方使用`webservers`宏。更改 IP 地址列表一次比在每个规则中更改要容易得多,也更可能正确。
虽然你可能使用接口组来表示你机器本地的 IP 地址,但你可能还有其他需要表示的 IP 地址。宏在这方面也非常有用。
internal_ip="10.10.0.0/16"
或者,如果你有多个不同的块,你可以在宏内部使用列表。
internal_ip="{10.0.0.0/24, 10.0.5.0/24, 10.0.10.0/24}"
当使用`pfctl`查看正在运行的 PF 规则时,你不会看到宏或列表;相反,你看到的是它们扩展到的规则。
### 常见错误:列表排除和否定
列表可能难以理解,很容易编写出否定其他规则的列表。例如,这似乎应该工作:
clients = "{192.0.2.0/24, !192.0.2.128/29}"
pass in on egress from $clients
这里的想法是我们的客户端拥有 192.0.2.0/24 的 IP 地址。我们希望允许所有这些地址,除了中间的小块,即 192.0.2.128/29。这似乎是合理的,对吧?但就像排除`sudo(8)`中的命令一样,这也会出问题。记住,列表中的每个条目都会扩展为另一条规则。这会创建两条规则。
pass in on egress inet from 192.0.2.0/24 flags S/SA
pass in on egress inet from ! 192.0.2.128/29 flags S/SA
第一条规则适用于从 192.0.2.0/24 子网开始的全部内容。这正是我们想要的。然而,第二条规则却适用于不在 192.0.2.128/29 子网中的所有内容,也称为“世界上所有人”——这并不是我们希望实现的目标。
类似地,否定整个列表会扩展为否定列表中的每个单独项目。如果你需要执行这种排除操作,请使用下一章中描述的表格。
## 净化流量
互联网主机到达的各种奇怪流量。其中一些流量是损坏的垃圾。其他部分告诉你,其他人正在运行损坏的垃圾。
PF 试图在处理之前对流量进行净化和标准化。这些标准化包括丢弃非法数据包、数据包重组和数据包修改。
### 非法数据包
一些随机到达主机的数据是垃圾。如果一个数据包的长度短于 IP 头部,那么它就不能是一个真正的 IP 数据包;如果一个 TCP 数据包太短,无法包含完整的 TCP 头部,那么它也不能是一个真正的数据包。
如果数据包长度与头部中给出的长度不匹配,那么数据包就可能在某些方面损坏。PF 无法确定这些数据包的来源,或者它们是否被恶意损坏,或者只是在传输过程中损坏。由于内核无法对这些数据包进行处理,PF 会自动丢弃它们。
### 数据包重组
在包过滤器决定如何处理一个包之前,该包应该是没有歧义和随机奇怪性的。重新组装清理了这些歧义,默认的重新组装设置适用于大多数环境。当你启用 PF 时,你会得到重新组装。
### 包修改
有时候你需要修改包。如今,PF 为大多数环境处理一切。如果你需要修改包,例如清除分片 UDP 包上的“不要分段”位,请参阅`pf.conf(5)`中的`scrub`关键字。
### 阻止伪造的包
另一种经典的 IP 攻击是发送看起来来自私有网络到防火墙的包,试图绕过包过滤器。如今,大多数防火墙都阻止这种攻击,攻击者很少为此烦恼,但你仍然应该保护免受伪造攻击。仅仅因为其他人已经接种了麻疹疫苗并不意味着你应该不接种。
对于反伪造规则,使用`antispoof for`和一个接口名称。
antispoof for fxp0
当输入到包过滤器时,规则看起来可能像这样:
block drop in on ! fxp0 inet from 192.0.2.5/28
block drop in inet from 192.0.2.5 set ( prio 0 )
第一条规则丢弃了来自除`fxp0`以外的任何接口上`fxp0`本地地址的任何流量。来自`fxp0`本地地址的包应该始终通过`fxp0`到达你的系统。
第二条规则丢弃了来自`fxp0`接口地址的任何流量。具有该源地址的包永远不会从外部世界到达。如果系统需要与自身通信,它使用接口`lo0`。
你可以使用接口组而不是接口名称,但我不建议这样做。如果你有多个`egress`接口,在出口组上使用反伪造规则不会阻止到达错误出口接口的外部包。花时间在你的反伪造规则中列举你的接口。
你不仅可以列出单个接口,还可以使用列表或宏。
antispoof for {lo0, fxp0, em0}
反伪造规则可能会干扰通过环回接口传递的包。我建议跳过对`lo0`的过滤,尽管 PF 为 127.0.0.0/8 地址提供了特殊的内置保护。
现在你已经掌握了基本的包过滤,让我们来考虑一些 PF 的核心设置。
## PF 选项
选项是影响核心 PF 功能的基本设置。选项回答了像这样的问题:
+ 我们是否将碎片重新组装成包?
+ 状态表应该支持多少条记录?
+ 是否开启了日志记录?
所有选项都以`set`关键字开头。因为选项会影响 PF 其他所有部分的操作,我建议将它们放在`pf.conf`的顶部。
在这里,我们将查看一些更常用的选项。
### 设置阻塞策略选项
你的防火墙会静默地丢弃禁止的包,还是会向客户端响应“对不起,不允许?”阻塞策略决定了它采取哪种方法。默认情况下,PF 会丢弃被阻塞的包,但你可以在单个过滤规则上覆盖全局阻塞策略。
严格来说,当 PF 丢弃数据包时,它应该向客户端返回一个错误,以便合法客户端可以立即意识到他们无法连接。使用`set block-policy return`告诉 PF 返回这些礼貌的错误:TCP 连接的 RST 和一个 ICMP 不可达消息用于其他类型的连接。
不幸的是,礼貌在现代社会互联网中被很大程度上忽视了。PF 的默认设置`set block-policy drop`告诉 PF 不要在阻塞的数据包上返回任何类型的错误。客户端应用程序,如网页浏览器、漏洞扫描器、蠕虫和其他恶意软件必须等待网络协议超时才能意识到他们无法连接。
我建议静默丢弃被阻塞的数据包.^([47])
### 设置限制选项
PF 包括限制用于跟踪片段、状态、地址表和其他内存消耗项的各种内部表的大小。我非常罕见地需要调整这些限制。现有的限制被选择是因为它们对于大多数环境中的大多数用户来说是足够的。
使用`pfctl`查看现有的限制。
pfctl -s memory
states hard limit 10000
src-nodes hard limit 10000
frags hard limit 1536
tables hard limit 1000
table-entries hard limit 200000
让我们看看每个限制代表什么。
#### frags 限制
当 PF 收到一个分段数据包时,它会保留这个片段并等待该数据包的其他片段到达。一旦它收集到所有片段,它就会重新组装这个片段并处理它。`frags`限制控制了一次性等待重新组装的包片段的数量。(你不应该需要更改这个设置。)
要查看 PF 处理的总片段数以及每秒到达的片段数,使用`pfctl -s info`并查看计数器部分。
pfctl -s info
…
fragment 368 0.0/s
…
这个主机已经在互联网数据中心裸露放置了三个月,只收到了 368 个片段。我不需要增加 PF 的片段内存,当然也不希望降低限制,以防突然收到大量片段。
如果你怀疑有片段流入,运行`systat pf`以持续更新 PF 统计计数器。
#### src-nodes 限制
PF 可以跟踪每个源地址的状态数量。你可能想限制每个客户端对特定服务器的连接数,比如 10 个连接。这个连接限制包括正在建立和仍在等待完成的连接。以下是一个此类规则的示例:
pass in proto tcp to $webserver port 80 keep state(max-src-states 10)
PF 的负载均衡器功能使用`src-nodes`通过`sticky-address`和`source-track`选项帮助跟踪哪些客户端连接到哪些服务器。
如果你使用这些功能,并且认为你可能会超出源节点,使用`pfctl -s Sources`来检查使用情况。
#### states 限制
`states`限制控制了状态检查列表中可以有多少条目。默认的 10,000 对于大多数环境来说是足够的。
你可以使用`pfctl -s info`查看当前的使用情况。
pfctl -s info
Status: Enabled for 1 days 18:01:06 Debug: err
State Table Total Rate
current entries 30
searches 54510751 6.3/s
inserts 2459724 0.3/s
removals 2459694 0.3/s
…
### 注意
我需要多次更改状态表。每次都是因为一个编写奇特的程序,要求客户端对单个 TCP/IP 端口进行数十次连接。我确信应用程序开发者有他们这样做的原因(可能的原因包括无知和恶意)。乘以数千个同时在线用户,这就变成了很多状态。由于我无法告诉开发者像普通人一样编写应用程序,我不得不调整状态表。
如果你怀疑状态表存在问题,请使用`systat pf`和/或`systat states`来实时查看状态活动。
#### 表和表条目限制
`tables`和`table-entries`限制控制 PF 可以创建多少个表,以及单个表中可以有多少条目。我从未需要调整这些,我建议如果你的过滤规则需要超过 1000 个表,你也许应该重新考虑你的设计。一个表可能需要存储超过 10 万个地址,但这种情况在当今非常少见。
#### 设置限制
要更改限制,请使用`set limit`,限制的名称和新的值。以下是如何将默认状态表大小加倍的方法:
set limit states 20000
再次强调,不要轻易更改这些默认设置。只有在现有限制导致特定问题时才增加它们。而且不要降低它们,否则你将无法为问题和高峰期做好准备。
### 设置优化选项
PF 包含各种超时设置,默认值适用于现代互联网。某些环境,如卫星上行链路,可能需要稍微不同的超时值。
你可以使用`set optimization`来调整 PF 的超时时间。(这个名字是 PF 早期时代的遗留物,但一直沿用至今。)这有四个值:
> **`normal`**
>
> `normal`优化是默认设置。如果你没有指定优化,则使用标准超时时间。
>
> **`conservative`**
>
> `conservative`优化适用于你绝对不希望超时连接的环境。(状态表条目将保留更长时间。)此设置使用更多的内存和处理器时间——在繁忙的网络中可能更多。我使用它来安抚那些不太关心购买更多硬件,而更关心由于某些高管空闲连接超时而可能引起的会议的工业网络管理员。
>
> **`high-latency`**
>
> 如果你通过卫星上行链路或信鸽连接,请使用`high-latency`优化。
>
> **`aggressive`**
>
> 如果你有一个繁忙的防火墙,有很多连接进出,你可能尝试使用`aggressive`优化。这会更快地超时空闲连接,减少内存和处理器使用。许多人报告说,在他们的环境中`aggressive`超时工作得非常好,但如果低超时时间给你带来麻烦,请将其关闭。
通过使用`set optimization`和优化名称来配置这些设置。
set optimization conservative
### 设置跳过选项
你可以告诉 PF 不要管理一个接口。默认情况下,它会监视所有接口,但有些接口实际上并不需要过滤。你的回环接口`lo0`只从本地机器传递流量到自身。在`lo0`上进行数据包过滤是一个有趣的教育练习,但在生产环境中并不十分有用。
set skip on lo0
你也可以指定多个要跳过的接口。
set skip on {lo0 fxp0 fxp1}
在主干下的物理接口上跳过过滤,而选择在主干本身上进行过滤,这种情况相当常见。
这将帮助你开始学习数据包过滤。如果你有一个功能简单的单一服务器,你可以使用本章中介绍的技术相当好地保护它。但 PF 的功能远不止我们在这里讨论的,比如控制带宽和让应用程序动态更改规则。在下一章中,我们将触及 PF 的一些更高级功能。
* * *
^([45]) 对不起,猫和象,你们要找到自己的居住地。
^([46]) 明显地借鉴自 Henning Brauer。幸运的是,他现在对这个书已经厌倦了,不会注意到这一点。
^([47]) 请注意,如果 PF 在数据包被丢弃时提供了一个侮辱客户的功能,类似于 sudo,我可能需要改变我的推荐。但这不是 PF 的问题,而是底层网络协议的缺陷。
## 第二十二章。高级 PF
*Office net 似乎很慢*
*感谢地下电影交换*。
*让我们立刻停止它!*
 上一章介绍了 OpenBSD 数据包过滤器 `pf(4)` 的基础知识。但是,正如我提到的,PF 可以以各种方式操作数据包,而不仅仅是允许或拒绝它们,包括以下内容:
+ 您可以通过外部软件,如 `dhcpd(8)` 或 `spamd(8)`,动态更改要传递或阻止的地址列表。
+ 您可以动态创建子规则集,这样您可以为麻烦的协议设置非常具体的规则,而不会允许不必要的更多访问。
+ PF 可以提供 NAT,让您在没有公共 IP 地址的情况下为整个网络提供互联网访问。
+ 您可以任意重定向传入流量,并控制服务将使用的带宽量。
+ 您可以使用 PF 记录。
本章涵盖了这些主题的每个方面。
## 使用表格进行数据包过滤
*表格* 是 IPv4 和/或 IPv6 地址的列表,就像列表一样。然而,表格比列表更快,并且使用的内存更少。如果您只有几个地址,使用列表是可以的,但一旦地址超过几个,请使用表格。
有趣的是,您可以在不重新加载过滤器规则的情况下编辑表格,并且有几个程序使用此功能来动态更改服务器的行为。有些人将恶意软件感染的计算机列表加载到表格中,以阻止这些主机,或者使用外部程序生成此类列表。(“你连续四次尝试发送无效的电子邮件?再见!”)表格可以永久保存在外部文件中,或者您可以将其视为临时文件。这是您的选择。
### 定义表格
您可以使用 `pfctl` 完全创建和操作表格,但这不如在 *pf.conf* 中定义表格常见。在尖括号内给出表格名称,并在花括号内用逗号分隔的初始成员提供。
table
在这种情况下,`management` 表格包含三个 IP 地址。
如果您想定义 `pfctl` 无法更改的表格,请使用 `const` 关键字。以下示例定义了一个用于私有(RFC 1918)地址空间的表格。这个地址空间已经定义了多年,所以没有人应该更改它。
table
如果没有规则引用表格,PF 会将其丢弃。这对于静态规则来说是有意义的,但如果您正在使用锚点(本章后面将讨论),您可能希望保留表格以备规则再次出现。使用 `persist` 关键字使表格即使在规则未使用的情况下也能保留。
table
一些表格包含足够的地址,您可能不想在配置中列出它们。为了方便,您可以从文件中填充表格,如下所示:
table
我有一个每天更新 *fullbogons.txt* 文件的脚本。(*Bogons* 是不应该出现在全球互联网路由表中的地址。)
bogons 列表包括私有地址空间、为实验或文档保留的地址、未分配给任何网络的地址,以及分配给其他奇异用途的地址。几个组织生产和更新完整的 bogon 列表。我在我的边界处使用 bogons 列表来剔除明显的垃圾。文件看起来像这样:
last updated 1352220481 (Tue Nov 6 16:48:01 2012 GMT)
0.0.0.0/8
10.0.0.0/8
14.1.96.0/19
…
你可以包括单个地址,但不能包括点分十进制子网掩码。你可以使用主机名,但在`pfctl`将规则馈送到内核之前,它会检查主机的 IP 地址或地址。这意味着如果主机在加载规则后更改其 IP 地址,PF 将不知道新的 IP 地址。
### 使用表格
在防火墙规则中使用表格的方式,就像使用地址或列表一样。
block in on egress from
你可以在列表中放入多个表格。
block in on egress from {
是的,列表比表格慢。但如果你以不同的方式维护两个不同的表格,你可能希望这些表格是分开的。而且如果两个项目的列表触发了防火墙耗尽,你真的需要更多的硬件。
### 查看表格
表格有自己的`pfctl`命令子集。要查看内核中有哪些表格,请使用`pfctl -s Tables`。(注意`Tables`以大写`T`开头。)
pfctl -s Tables
fullbogons
scumbags
为什么你需要询问内核它有哪些表格?因为动态规则可以添加和删除表格,如锚点中讨论的那样。
如果你已经知道表格名称,并且想查看表格中的地址,请使用`-t`参数指定表格名称。`-T`参数有几个子命令,就像`-s`一样,但它是用于表格操作的。以下是如何检查`scumbags`表格的内容:
pfctl -t scumbags -T show
157.166.248.10
157.166.248.11
157.166.249.10
157.166.249.11
对于许多表格操作(目前包括`add`、`delete`、`replace`和`test`),你可以在`-T`之前添加一个或两个`-v`选项来增加详细程度。如果你同时处理多个地址,增加详细程度会显示命令执行了什么。
### 搜索表格
你可以很容易地查看有四个条目的表格,但如果一个表格有数千个条目,你不会想一页一页地搜索地址。你可以使用`grep(1)`,但这可能会失败,因为一个地址可能是一个看起来完全不同的网络的组成部分。(我确信如果我输入 10.99.61.4,我可以写一个匹配 10.0.0.0/8 的`grep`表达式,但我不想尝试。)你可以测试一个地址,看看它是否在表格中。
pfctl -t fullbogons -T test 192.0.2.88
1/1 addresses match.
这个地址出现在`fullbogons`表格中。
如果你在一个命令中测试多个地址,请在`-T`之前使用`-v`或`-vv`来查看哪些地址匹配,哪些不匹配。
pfctl -t scumbags -vvT test 192.0.2.88 198.51.100.90
1/2 addresses match.
M 192.0.2.88 192.0.0.0/22
198.51.100.90 nomatch
使用单个`-v`只显示匹配的地址。
### 更改表格
表格的一个重要特性是你可以动态地更改它们而不需要重新加载防火墙规则。如果你必须向表格中添加一个地址,请使用`-T`的`add`命令。
pfctl -t scumbags -T add 192.0.2.88
1/1 addresses added.
通过指定子网掩码和单个命令中的多个地址来添加网络。
pfctl -t scumbags -T add 198.51.100.0/24 2001:db8::/32
2/2 addresses added.
如果你向一个不存在的表格中添加地址,PF 会自动创建该表格(所以现在你知道那个`scumbags`表格是从哪里来的)。
使用`-f`参数将文件中的所有地址添加到表中。
pfctl -t scumbags -T add -f scumbags.txt
1/1 addresses added.
要删除地址,请使用`delete`命令。
pfctl -t scumbags -T delete 198.51.100.0/24
1/1 addresses deleted.
要完全从表中删除所有条目,请使用`flush`。
pfctl -t scumbags -T flush
6 addresses deleted.
如果清空表还不够,并且你想完全从规则中删除它,请使用`kill`。
pfctl -t scumbags -T kill
1 table deleted.
### 表和自动化
OpenBSD 包括可以算法调整表的软件。在第十六章中,我提到了 DHCP 服务器将租用、废弃和更改的地址分配给表的能力。你可以使用 PF 为每个地址组分配不同的规则。
假设你使用`dhcpd(8)`将所有租用的 IP 地址添加到`leased`表,废弃的地址添加到`abandoned`表,更改的地址添加到`changed`表。拥有正确租用地址的主机可以访问网络,但拥有废弃和更改地址的主机不能。在这里,办公室组中的接口面向本地网络:
table
table
table
pass in on lan from
block in on lan from {
如果有人决定将 DHCP 服务器中的地址配置为计算机的静态地址,他们就会自动失去对网络其余部分的访问——问题解决。其他 OpenBSD 软件,如`spamd(8)`,也有类似的功能。
乍一看,这个功能似乎已经准备好与其他程序集成。编写一个解析日志、抓取 IP 地址并将这些地址输入到表中的脚本是相当简单的。几年前,我编写了一个脚本,从 Snort 入侵检测系统中获取警报并自动阻止网络中的攻击者。然而,如果没有仔细和熟练的注意,Snort 会产生许多误报。我的自动阻止脚本非常有效地对我的开发团队发起了拒绝服务攻击。
在自动向 PF 表输入以阻止流量时要小心。很容易损害所需的连接性。
## 使用 NAT
防火墙的一个关键功能是 NAT。使用 NAT 为多台机器提供 IPv4 网络访问,但只显示一个公共 IPv4 地址。一些公司通过 NAT 为成千上万台机器提供互联网访问。
NAT 就像用骨头熬汤——它拉伸了你所拥有的,使其覆盖更多。有些协议与 NAT 配合得不好。它确实会让人试图通过 IP 地址限制访问的人感到困惑。它还可能给网络取证和故障排除人员带来噩梦。但 NAT 是解决 IPv4 地址短缺的首选方案。
NAT**不是**作为安全机制设计的。它有一些小的安全优势,但不足以抵御今天的网络威胁。依赖 NAT 进行安全就像在酒吧喝完一杯黑咖啡后追赶 10 个锅炉工回家一样。你可能侥幸逃脱,但这完全靠运气。
IPv6 最初设计时没有 NAT,但几年后由于市场需求而被迫加入。 (IPv4 最初也是没有 NAT 设计的,所以 IPv6 只是遵循了传统。) 注意,一个 IPv6 地址——即使是全球唯一的 IPv6 地址——并不意味着或甚至暗示“可以从世界上任何地方访问。” 你可以在没有 NAT 的情况下实现稳固的网络隔离。避免使用 NAT 意味着使用你的数据包过滤器来保护你的机器,并在需要时使用额外的应用程序代理。
### 私有 NAT 地址
理论上,你可以使用 NAT 设备后面的任何地址。如果你使用一些随机的 IP 地址,那么你将无法与使用这些 IP 地址的现实中的人交换数据包。强烈建议使用为私有用途保留的一些 IP 地址,通常称为“RFC 1918 地址。” 这些包括以下 IP 地址:
+ 10.0.0.0/8 (10.0.0.0-10.255.255.255)
+ 172.16.0.0/12 (172.16.0.0-172.31.255.255)
+ 192.168.0.0/16 (192.168.0.0-192.168.255.255)
你可以按任何你喜欢的方式子网化和重新排列这些地址,只要你不尝试在公共互联网上路由它们。
如果你有一个非常好的理由,你可以在 NAT 后面使用其他 IP 地址。例如,RFC 5737 定义了用于文档的 IPv4 地址。像 RFC 1918 地址一样,RFC 5737 地址永远不会出现在公共互联网上。因为我写文档,所以我在我家用网络和测试网络上使用这些地址。这让我在写书时节省了搜索和替换的时间.^([48]) 这些地址出现在其他网络上的可能性仍然为零。
### 配置 NAT
可能最常见的一种 NAT 用途是隐藏一个小型网络在单个 IP 地址后面。你会在许多家庭和小型企业中找到这种情况。非常少的家庭办公室有内部路由和多个子网。在这个例子中,我有两个接口组:面向互联网的`egress`组和连接到我的办公室的`lan`组。
pass out on egress from 1lan:network to any 2nat-to egress
这个规则的第一个部分看起来就像任何其他允许`lan`接口上的地址访问任何地方的防火墙规则,但最后两个词还额外配置了 NAT。`nat-to`关键字告诉 PF 转换地址**2**。随后的`egress`告诉 PF 隐藏内部地址在`egress`接口的地址后面**1**。你在这里可以使用接口名称或特定的 IP 地址,但如果你这样做,你必须在你更改服务器时更改你的过滤规则。
为了让 PF 识别从 DHCP 来的 IP 地址变化,请将接口组名称放在括号内。
pass out on egress from lan:network to any nat-to (egress)
现在加载你的防火墙规则,启用 IP 转发,突然之间,你的局域网主机将通过防火墙的公网地址访问互联网。
### NAT 的工作原理
理解地址转换工作原理的最简单方法是通过查看 PF 在来回传递转换后的数据包后状态表(在上一章中讨论过)。在办公室网络中,从机器 192.0.2.2 上,我运行了这个命令:
$ ping www.michaelwlucas.com
几次 ping 之后,我检查了状态表,并发现了如下条目:
pfctl -ss | grep 192.0.2.2
all udp 1203.0.113.5:55797 2(192.0.2.2:10853) -> 3203.0.113.15:53 MULTIPLE:SINGLE
all icmp 203.0.113.5:8813 (192.0.2.2:41584) -> 198.22.63.8:8 0:0
第一种状态表示从防火墙的公共地址**1**到本地 DNS 服务器**3**的 UDP 连接。此状态条目包括客户端的私有 IP 地址**2**,以及客户端、防火墙和 DNS 服务器实际使用的端口号。
客户端通过从其 IP 地址上的 10853 端口向 DNS 服务器的 53 端口发送请求来启动此状态。当数据包通过 PF 时,OpenBSD 重写了数据包,使其看起来是从 203.0.113.5 地址的 55797 端口发出的,并将其发送到 DNS 服务器。DNS 服务器将其响应发送到防火墙的公共 IP 地址的 55797 端口。当回复到达时,防火墙检查状态表,发现 55797 端口的 UDP 数据包是客户端状态的一部分。PF 重写数据包的目标地址并将其转发到客户端。
第二种状态表示一个 ICMP 连接。状态表将用于 ping 请求的各种 ICMP 代码编码为端口号,并根据该信息将响应转发回客户端。否则,它与上面的 DNS 示例非常相似。
换句话说,NAT 通过撒谎来工作。PF 向客户端撒谎,告诉它它可以直接访问公共互联网。它向外部服务器撒谎,为客户端连接提供一个虚假的源地址和端口号。PF 使用状态表来跟踪它的谎言并保持一切一致。这些谎言对于 IPv4 地址的节约很有用,但它们正是地址转换使故障排除和入侵取证复杂化的原因。
现在您已经了解了 NAT 的基本原理,让我们向网络讲述更多复杂和有趣的故事。
### 多个或特定公共地址
您可以使用多个公共 IP 地址进行地址转换。如果您在 NAT 规则中使用接口组作为外部地址,该接口组中的任何地址都可以成为任何连接的公共地址。如果您想具体指定,请列出特定地址。
pass out on egress from lan:network to any 1nat-to 203.0.113.5
当我的防火墙的外部接口有多个 IP 地址,并且我想将桌面客户端隐藏在单个地址后面时(尽管我可能还会定义并使用一个宏来定义和使用外部地址**1**),我会使用这个配置。
但您需要多少个公共地址?答案取决于您的客户端。
端口号范围从 0 到 65535。底部的 1024 个端口通常用于 localhost 上的服务。并非所有这些端口都会在 localhost 上使用,但数据包过滤器通常不会使用这些端口进行转换连接。我很懒,所以我会四舍五入到 64,000 个空闲端口。
即使是最繁忙的桌面客户端也很少能同时使用多达 100 个出站连接。大多数将使用更少的连接,但再次强调,我很懒,并且我想考虑最坏的情况,所以我会将其称为 100。
一个 IP 地址可以支持 64,000 / 100 = 640 台机器同时进行病理状态。实际上,每个客户端可能有 10 个同时的出站连接,因此一个公网地址可以支持 6,400 个同时客户端。有多少用户同时浏览互联网?答案可能不多。如果您有数千名用户,您可能从实现缓存代理中受益,这将大大减少连接数。
如果您担心一个地址的客户机数量过多,请关注您的状态表。直到您有一个公网 IP 地址有数万个状态,您不必担心。
在 NAT 规则中指定单个地址对于双向 NAT 最有用。
### 双向 NAT
如果您为特定的私有 IP 地址分配一个公网 IP 地址作为 NAT 地址,某些应用程序将运行得更好。例如,如果您有一个服务器在不同的端口上提供多种不同的服务,并且您想将其放在防火墙后面,您可能希望为它分配一个单独的地址。这被称为*双向*、*一对一*或*静态* NAT。OpenBSD 文档使用“双向”,但所有这些术语都意味着同一件事。
使用`binat-to`关键字配置双向 NAT。
pass on lan from 192.0.2.65 to any binat-to 203.0.113.6
PF 为私有 IP 地址 192.0.2.65 的 NAT 服务分配了公网 IP 地址 203.0.113.6。
如果您使用双向 NAT,请确保为您的通用 NAT 指定一个特定的 IP 地址,并考虑使用以下 NAT 规则:
pass out log on egress from lan:network to any nat-to egress
pass on lan from 192.0.2.2 to any binat-to 203.0.113.6
这个局域网的 IP 地址隐藏在`egress`接口的 IP 地址后面。如果 203.0.113.6 是`egress`接口上的一个地址,那么局域网的外出数据包可能会将其用作源地址。
当我需要双向 NAT 时,我通常会这样编写我的 NAT 规则:
mainnat="203.0.113.5"
servernat="203.0.113.6"
pass out log on egress from lan:network to any nat-to $mainnat
pass on lan from 192.0.2.2 to any binat-to $servernat
这样,离开我的网络的数据包会被明确地转换。只有特定的服务器使用 IP 地址 203.0.113.6;我本地网络上的所有其他主机使用 203.0.113.5。如果我更改 IP 地址,我必须重新配置*pf.conf*,但与解决网络模糊问题相比,这只是一个小的烦恼。
#### 双向 NAT 和安全
使用双向 NAT 并允许连接重定向,可以让您让网络外的人访问您防火墙后面的服务器,而每一个这样的空隙都是一个潜在的安全漏洞。如果您允许全世界访问您的 Web 服务器,并且入侵者破坏了您的其中一台服务器,那么您防火墙内部就有了一台受损害的机器。防火墙并没有真正保护 Web 服务器;它只是控制谁可以尝试入侵,并限制了可用的攻击向量。
#### 数据包过滤、双向 NAT 和规则顺序
当为双向 NAT 编写数据包过滤规则时,您列出规则的顺序很重要。考虑以下规则:
pass on lan from 192.0.2.2 to any binat-to 203.0.113.6
pass in on egress proto tcp from any to 192.0.2.2 port 80
第一条规则为局域网上的主机 192.0.2.2 建立了静态 NAT,隐藏在公共 IP 地址 203.0.113.6 后面。一切顺利。第二条规则允许连接到同一主机的 80 端口,或者不是吗?到达防火墙`出口`接口的针对此服务器的数据包不会地址到 192.0.2.2;它们会被地址到公共 NAT 地址,即 203.0.113.6。它们不会匹配此规则,因此会被丢弃。
为了允许来自世界各地的连接到这个防火墙后面的 Web 服务器,允许发送到公共地址上正确端口的包。
pass on lan from 192.0.2.2 to any binat-to 203.0.113.6
pass in on egress proto tcp from any to 203.0.113.6 port 80
这将 192.0.2.2 转换为公共地址 203.0.113.6,并允许目标为 203.0.113.6 端口 80 的数据包通过。你会在状态表中看到这一点,如下所示:
all tcp 203.0.113.6:80 <- 198.22.63.8:64791 ESTABLISHED:ESTABLISHED
主机 198.22.63.8 已连接到服务器的公共 IP 地址 80 端口。
为什么这个状态条目中没有隐藏的 IP 地址?因为这是一个双向 NAT。PF 可以通过未修改的端口号发送,所以它可以在状态表中跟踪较少的信息。
这里棘手的是规则顺序会影响你的过滤方式,你必须仔细阅读你的过滤规则,以了解地址转换如何与数据包过滤相互作用。我*总是*在过滤之前进行地址转换。我始终在过滤规则中使用公共 IP 地址,但有时这并不实用。PF 允许你编写任意复杂的规则,主要是因为现实世界是任意复杂的。如果你在 NAT 中遇到流量传输问题,请仔细阅读你的规则。
要查看双向 NAT,请查看加载的规则。
pfctl -sr
…
pass out on lan inet from 192.0.2.2 to any flags S/SA nat-to 203.0.113.6 static-port
pass in on lan inet from any to 203.0.113.6 flags S/SA rdr-to 192.0.2.2
pass on egress inet proto tcp from any to 203.0.113.6 port = 80 flags S/SA
第一条规则允许私有 IP 地址访问公共互联网,并将其翻译为特定的 IP 地址。第三条规则将流量传递到翻译后的地址。
但关于第二条规则,那个`rdr-to`是什么?这是一个重定向,这是 PF 实现静态 NAT 的方式。
### 重定向
双向 NAT 实际上是地址转换和*重定向*的组合;换句话说,它将一个 IP 或端口连接的方向扭转到另一个。在双向 NAT 中,所有连接到指定公共 IP 地址的连接都会被重定向到不同的 IP 地址。有时你不想扭转一个 IP 地址的所有流量——只是几个端口。有时你希望一个端口单向重定向,但在其他地方使用不同的端口。可以通过重定向规则来实现这一点。
假设你有一个公共 IP 地址:203.0.113.5。你希望该 IP 地址上的 80 端口路由到你的 Web 服务器 192.0.2.2,端口 25 和 110 路由到你的邮件服务器 192.0.2.3,端口 443 路由到你的电子商务服务器 192.0.2.4。PF 允许你通过使用标准的包过滤规则并添加`rdr-to`重定向关键字来选择每个端口的发送位置。
pass in on egress proto tcp from any to egress port 80 rdr-to 192.0.2.2
pass in on egress proto tcp from any to egress port {25, 110} rdr-to 192.0.2.3
pass in on egress proto tcp from any to egress port 443 rdr-to 192.0.2.4
这些规则声明,任何连接到 `egress` 接口组(面向公共互联网的接口,默认路由通过它)都可以以三种不同的方式进行重定向。第一条规则将端口 80 请求导向一个内部服务器。第二条规则将端口 25 和 110 的请求导向第二个服务器。最后一条规则将端口 443 的请求重定向到第三个服务器。现在一个公共 IP 地址正在从三个不同的服务器向世界提供服务。
所有端口重定向规则都必须包含一个协议,因为指定 TCP/IP 端口仅在转发包含端口号的协议时才有效,例如 TCP 或 UDP。如果你想要转发 TCP 和 UDP 端口,你必须指定这两个协议。例如,DNS 在 TCP 和 UDP 上都使用端口 53。以下是一个将这两个协议的端口 53 重定向到内部服务器 192.0.2.5 的规则:
pass in on egress proto {tcp, udp} from any to egress port 53 rdr-to 192.0.2.5
选择一个端口,指定你希望它去往的地方,PF 将按照你的意愿进行重定向。
### 注意
你已经学习了双向 NAT 如何结合重定向和地址转换。内核中的 PF 引擎实际上对这种被称为“双向 NAT”的东西一无所知。`pfctl(8)` 将 `binat` 规则转换为两个独立的规则:一个用于转换,一个用于重定向。
### 多个地址和接口组
所有的前述讨论在你只有一个公共 IP 地址时是有意义的。但是,当你有多个地址时会发生什么呢?
记住,在 *pf.conf* 中使用接口组会告诉 `pfctl` 为接口组中的每个 IP 地址创建一个匹配规则。假设你的 `egress` 接口上有三个 IP 地址:203.0.113.5、203.0.113.6 和 203.0.113.7。你将编写以下 *pf.conf* 规则:
pass in on egress proto tcp from any to egress port 80 rdr-to 192.0.2.2
使用 `pfctl` 将此规则加载到内核中,你会得到什么?
pfctl -sr
…
pass in on egress inet proto tcp from any to 203.0.113.5 port = 80 flags S/SA rdr-to 192.0.2.2
pass in on egress inet proto tcp from any to 203.0.113.6 port = 80 flags S/SA rdr-to 192.0.2.2
pass in on egress inet proto tcp from any to 203.0.113.7 port = 80 flags S/SA rdr-to 192.0.2.2
任何连接到这些 IP 地址上的端口 80 的连接都将被导向同一服务器的端口 80。这可能在某些环境中很有用,但这不是我们大多数人想要的。如果你有多个 IP 地址,并且只想在其中一个 IP 地址上重定向端口,你必须指定接口名称和公共 IP 地址。
pass in on em0 proto tcp from any to 203.0.113.5 port 80 rdr-to 192.0.2.2
这不会扩展;它没有任何接口组、地址列表、变量或宏。当 `pfctl` 解析时,它只将一个 PF 规则加载到内核中。
### 端口操作和范围
当你从一个机器重定向端口到另一个机器时,你可以更改端口。以下示例将防火墙上的 TCP 端口 2222 的请求重定向到防火墙内机器的端口 22。
pass in on egress proto tcp from any to egress port 2222 rdr-to 192.0.2.2 port 22
这是一种合理的方式,在只有一个 IP 地址的情况下向防火墙内的多台机器提供 SSH 服务,并为每台机器分配其自己的端口。
如果你有一些特定的源地址想要滥用,你可以通过源 IP 地址给予它们特殊的端口重定向。
pass in on egress proto tcp from 198.51.100.0/24 to egress port 80 rdr-to 192.0.2.2
pass in on egress proto tcp from ! 198.51.100.0/24 to egress port 80 rdr-to 192.0.2.3
来自 198.51.100.0/24 IP 地址的每个 HTTP 连接都将被重定向到一台服务器,而其他所有连接将被导向其他地方。(要为多个源地址重定向连接,请使用源地址表。)
PF 还可以使用与过滤端口相同的逻辑运算符重定向整个端口范围。一个明显的做法是将端口范围重定向到单个机器。NFS 是一个很好的例子,因为它需要 TCP 端口 111,以及从 1024 到 65535 的所有 TCP 和 UDP 端口。
pass in on egress proto {tcp, udp} from any to egress port {111, 1024:65535} rdr-to 192.0.2.15
回想一下第二十一章,端口号之间的冒号表示端口范围。此规则通过 1024 到 65535 的端口,包括端口号。诚然,某些 NFS 实现可以限制使用 TCP 或 UDP,这在你的包过滤器中是一个很大的漏洞。但 NFS 使用随机的高编号端口,这些端口来去非常快,无法在包级别上进行有效过滤或限制。
你也可以将整个端口范围引导到一台机器上的一个端口。
pass in on egress proto tcp from any to egress port {1024:65535} rdr-to 192.0.2.15 port 80
我曾用它将随机流量指向一个显示“走开。你不能使用此服务。”的网页。
### 透明拦截
流量拦截与重定向类似,PF 拦截指向一个端口的流量并将其引导到本地机器上的一个端口。流量拦截是实现透明代理的一种方式。使用`divert-to`关键字告诉 PF 将任何匹配的包引导到本地服务器。
pass in inet proto tcp from lan:network to any port 80 divert-to 127.0.0.1 port 3129
来自本地局域网到端口 80 的任何流量都将被重定向到防火墙上的端口 3129。端口 3129 通常由 Squid 缓存代理(*/usr/ports/www/squid*)使用。如果你选择实现像 Squid 这样的缓存代理,你可能希望将多个端口重定向到缓存。(我们将在 FTP 和 PF 中更详细地查看连接的重定向。)
## 锚点
在 PF 中,*锚点*是过滤器规则中特定点的子规则集,你可以更改它而无需重新加载规则。这是一个标记为“在此处插入规则”的位置,让你可以动态添加和删除过滤器规则、表以及其他 PF 配置。
锚点的最常见用户是软件程序。人类或系统管理员可能只需编辑*pf.conf*并重新加载规则。
OpenBSD 包括几个利用锚点的程序,包括 FTP 代理`ftp-proxy(8)`、认证防火墙访问系统`authpf(8)`和负载均衡器`relayd(8)`。你也可以使用锚点来触发规则的条件评估。
带有锚点的规则集可能看起来像以下示例,其中接口组`egress`面向互联网,而接口组`lan`面向具有地址 192.0.2.0/24 的小办公室。
block
pass in on egress from any to 192.0.2.45 port {25, 80}
anchor "antivirus/*"
pass in on lan from 192.0.2.0/27 to any
这些规则默认阻止所有流量。入站流量允许访问端口 25 和 80 的特定地址,因为那些是邮件和 Web 服务器。规则中间有一个锚点。我还不知道 `antivirus` 锚点中有什么,但其中的任何规则都会被处理。最后,允许一小部分地址的子网流出。
现在让我们向锚点添加一些规则。
### 向锚点添加规则
您可以从文件中、在 *pf.conf* 本身或通过 `pfctl` 将规则插入到锚点中。
#### 文件中的锚点规则
从文件向锚点添加规则是初始化数据包过滤器时锚点的好方法。您可以在其中设置基本规则,稍后可以扩展。在 *pf.conf* 中提供文件名。
anchor dhcp
load anchor dhcp from "/etc/pf/dhcp-anchor.conf"
我创建了一个 */etc/pf/* 目录,因为我不想在 */etc* 中散布大量的 PF 配置文件。毕竟,我很容易混淆。这个文件包含如下 PF 规则:
block from 192.0.2.192/26 to any
这是在启动 PF 时将基本规则加载到锚点的一种方法。
如果你注意到了,我的第一个例子锚点后面有一个 `/*`。这个例子没有。我会在嵌套锚点: /*中解释原因。
#### pf.conf 中的锚点规则
您可以直接在 *pf.conf* 中放置锚点规则。如果您不打算动态更改规则,甚至不需要命名锚点。只需使用花括号来定义锚点的开始和结束即可。
anchor "smtp" on egress {
pass proto tcp from 192.0.2.12 to any port 25
}
这只是比默认 *pf.conf* 中的锚点稍微复杂一点。
为什么你想这么做?请阅读条件过滤。
#### 通过 pfctl 的锚点规则
要使用 `pfctl` 动态更改锚点规则,您需要锚点的名称和您想要放入其位置的规则。例如,假设我想向第一个锚点示例中的 `antivirus` 锚点添加一个规则。
1****echo "block in from 203.0.113.8 to any" 2****| pfctl 3****-a antivirus 4****-f -
让我们稍微从后往前看这个命令。`pfctl` 的 `-a` 参数指定了一个锚点名称——在这个例子中是 `antivirus` 锚点 **3**。`-f` 参数通常提供一个包含新锚点规则的文件名,就像加载 PF 规则集时的 `-f` 一样,但与指向文件的路径不同,我使用一个单独的短横线来告诉 `pfctl` 从标准输入或命令行读取新规则 **4**。我通过回显要添加的规则 **1** 来开始一切,然后将它管道化到 `pfctl` **2**。
总体来说,这会将规则 `block in from 203.0.113.8 to any` 添加到 `antivirus` 锚点。
您也可以将新规则写入文件,并告诉 `pfctl` 从该文件加载规则到锚点中。
pfctl -a antivirus -f newrule.conf
然而,如果您正在将规则写入文件以将其加载到锚点中,那么编辑 *pf.conf* 可能会更好。
### 注意
向锚点添加规则会擦除锚点中已有的任何规则。如果你有一个更新锚点规则的软件包,你的软件需要处理这种行为。如果你的期望行为可以通过 IP 地址列表实现,考虑使用表格而不是锚点。
### 查看和清除锚点
使用 `pfctl` 查看锚点(`-s`)、清除(`-F`)和加载(`-f`)命令,通过指定锚点名称使用 `-a`。
pfctl -a antivirus -s rules
block drop in inet from 203.0.113.8 to any
要擦除锚点中的规则,请清除锚点中的规则。
pfctl -a antivirus -F rules
rules cleared
你的锚点现在是空的。
锚点内的规则集完全相互独立,也独立于主规则集。清除特定锚点中的所有规则不会影响任何其他锚点中的规则,或主规则集中的规则。就这个而言,清除主规则集中的规则也不会影响锚点中的规则。要销毁一个锚点,你必须移除锚点中的所有内容,包括任何子锚点。
“子锚点?”我听到你喊道。“你现在在胡说些什么,伙计?”
### 条件过滤
考虑以下 *pf.conf* 片段:
…
anchor "office/*" in from lan to any {
pass out proto tcp from any to {80, 443}
}
…
`office/*` 锚点后面有一个过滤条件,只有符合过滤条件的流量才能通过该锚点。在这种情况下,只有来自 `lan` 接口组的包才能通过锚点内的规则。你锚点内的规则可能更容易编写,仅仅因为锚点内的一切已知都来自 `lan` 接口。
如果你的包过滤器负载非常重,你可能可以通过仔细的条件过滤来减少它处理数据包所花费的时间。
### 嵌套锚点: /*
锚点可以包含其他锚点。
anchor "office" in from lan to any{
…
anchor "ftp-proxy/*"
pass in quick inet proto tcp to port ftp divert-to 127.0.0.1 port 8021
}
…
只有通过 `office` 锚点的流量才能通过 `ftp-proxy` 锚点。FTP 代理也可以有自己的子锚点。实际上,你可能需要多层锚点来支持复杂的协议,如 FTP。
这就是为什么某些锚点名称后面跟有 `/*` 的原因。没有这个的锚点名称会独立执行。通过添加 `/*`,你告诉 PF 按字母顺序评估此锚点内的所有子锚点。
锚点和子锚点故意模仿文件系统。你可以有一个文件 */office* 或一个包含更多文件的目录 */office/*。如果你列出目录中的文件,它们会按字母顺序显示。锚点的工作方式与此类似。
所有这些锚点内容都是非常理论性的。那么,有没有一个实际例子呢?继续阅读,看看 PF 如何使用锚点来处理最令人烦恼的网络协议:FTP。
## FTP 和 PF
大多数现代应用程序协议都运行在单个网络连接上。如果你发起一个网页请求,你的浏览器会打开到服务器的 80 号端口的连接,请求信息,并接收答案,所有这些都在同一个连接上完成。SSH 在 22 号端口打开一个单独的连接,并在此端口上交换所有信息,即使你在其中隧道了一百种其他协议。对旧协议的经验和实验教会了我们这种方法的好处。FTP 是一个较旧的协议,它提供了大量关于如何不做事的经验。
FTP 的原始版本(今天称为*主动 FTP*)要求客户端连接到服务器的 21 号端口。然后服务器会从 20 号端口打开一个连接回客户端,连接到客户端的某个随机高编号端口以发送信息。从服务器到客户端的连接被称为*数据连接*或*反向通道*。FTP 客户端和服务器就使用的端口以及第二个连接的用途达成一致。然而,在网络协议层面,客户端连接到 21 号端口和服务器从 20 号端口连接之间不存在连接,因此防火墙无法使用状态检测来区分这种连接是否被允许。更糟糕的是,如果客户端位于 NAT 设备后面,就无法确定防火墙应将传入的 FTP 数据请求路由到哪个私有 IP 地址。
*被动 FTP* 是 FTP 协议的一个更新版本,其中客户端初始化了两个 TCP 连接。所有现代客户端和服务器都支持被动 FTP。主动 FTP 和被动 FTP 之间的差异引发了用户教育的无尽循环和帮助台工作量的增加,尤其是在尝试通过网页浏览器使用 FTP 时。(如果有人要破坏我的帮助台工作人员,那肯定是我自己!)主动 FTP 简化了防火墙规则,因为防火墙不需要允许反向通道。不幸的是,被动 FTP 的创造者将修改后的协议称为 FTP。客户端不在乎主动还是被动,他们只想“这个 FTP 东西”能工作,不管它背后的实际协议是什么。
为了使事情更加复杂,一些 FTP 服务器和客户端在主动和被动 FTP 之间实现了某种折中方案。FTP 协议已经存在了几十年(它早于 TCP/IP),人们多年来一直在对其进行调整和“改进”。通过一个随机的 NAT 设备和数据包过滤器将一个随机的 FTP 服务器和客户端组合在一起可能会引起噩梦,或者至少需要打开广泛的 TCP 端口。
OpenBSD 和 PF 通过包含一个 FTP 应用程序代理`ftp-proxy(8)`来解决这个问题。当客户端发起 FTP 请求时,PF 拦截请求并将其重定向到应用程序代理。代理跟踪 FTP 协议事务,使用锚点将适当的规则插入防火墙,并在传输完成后删除规则。严格来说,`ftp-proxy`不是一个传统的代理。数据实际上并没有通过`ftp-proxy`;这个“代理”调整防火墙规则以便流量可以通过。代理需要两个部分:一个运行的`ftp-proxy`实例和重定向规则。
### 配置 ftp-proxy(8)
就像任何其他 OpenBSD 守护进程一样,`ftp-proxy`在`/etc/rc.conf.local`中启用。没有配置文件——只有命令行参数。默认情况下,`ftp-proxy`自动监听回环接口上的 8021 端口。我很少为常规使用添加任何`ftp-proxy`命令行参数。
ftpproxy_flags=""
然而,如果我在调试问题,我可能会在调试模式下前台运行`ftp-proxy`。这样做会显示所有发生的 FTP 事务。
ftp-proxy -dD7
这显示了通过 FTP 代理的所有内容,包括返回客户端的数据通道使用的端口。按 CTRL-C 停止`ftp-proxy`。
我与`ftp-proxy`最常见的问题是调试终端中没有显示任何内容。这意味着防火墙没有将任何流量重定向到代理。检查你的`pf.conf`文件以验证你是否有了支持 FTP 代理的必要规则。
### PF 配置和 FTP 代理
PF 必须知道将 FTP 请求发送到`ftp-proxy`。默认的`pf.conf`文件中有一个很好的配置示例:
anchor "ftp-proxy/*"
pass in quick inet proto tcp to port ftp divert-to 127.0.0.1 port 8021
pass out inet proto tcp from (self) to any port ftp
这是我们使用锚点的地方。`ftp-proxy/*`锚点可以包含子规则集。`ftp-proxy`守护进程会动态修改这些锚点以配置必要的流量或数据连接。第二个规则声明 PF 会将任何指向 FTP 端口(21,根据`/etc/services`)的流量重定向到本地主机的 8021 端口。第三个规则表示防火墙主机可以将 TCP 端口 21 流量发送到任何其他主机。此规则包含一个新术语`(self)`,它是 PF 对“本地主机上的所有 IP 地址”的简称。
你如何确保这能工作?首先,找到一个支持主动 FTP 的 FTP 服务器。打开你的 FTP 客户端并登录到服务器,通过防火墙。一旦登录,在 FTP 提示符下使用`pasv`命令。此命令打开或关闭被动模式。如果服务器不识别`pasv`,则它只支持被动 FTP。为此测试找到另一个 FTP 服务器。一旦 FTP 服务器报告“被动模式已关闭”,列出目录内容。目录列表,就像数据文件一样,通过数据通道传输。
在一个活跃的 FTP 连接的数据传输过程中,你应该在`ftp-proxy/*`锚点中看到规则。
pfctl -a "ftp-proxy/*" -sr
anchor "6837.2" all {
pass in log (all) quick on rdomain 0 inet proto tcp from 129.128.5.191 to 139.171.202.34 port = 62323 flags S/SA keep state (max 1) rtable 0 rdr-to 192.0.2.2 port 64280
pass out log (all) quick on rdomain 0 inet proto tcp from 129.128.5.191 to 192.0.2.2 port = 64280 flags S/SA keep state (max 1) nat-to 129.128.5.191
}
`ftp-proxy` 创建的规则非常具体。它们只允许一个连接,从一个特定的服务器到特定的客户端,并使用地址转换规则使每一方都认为它实际上在与正确的客户端或服务器通信。
### 注意
要了解如何限制你的客户端只使用匿名 FTP,或者如何使用`ftp-proxy`允许防火墙内的服务器进行入站 FTP 访问,请阅读`ftp-proxy(8)`手册页。
## 带宽管理
网络边界设备的一个常见任务是带宽管理。网络管理员必须控制用于特定任务的带宽量,还必须为关键功能预留带宽。如果你的一个下属在 Web 服务器上加载最新的热门漫画电影,你必须能够通过 SSH 连接到服务器,找出为什么你的服务器过载,并解决问题。PF 包括 ALTQ 带宽管理系统。
关于带宽管理,最重要的是记住你不能控制别人发送给你的流量量。你可以在流量进入你的网络时停止它。你可以发送带宽饱和的提示。你可以随意限制来自你的服务器的带宽。但无论你做什么,都无法阻止每秒 10,000 人点击链接到那个服务器。你无法阻止分布式拒绝服务攻击使你的入站带宽饱和。你能做的最好的事情就是控制你如何响应这些请求。
当我运行内容农场时,我通常在我的服务器前面放置专门的带宽控制机器。这种设置控制了多少流量实际上到达我的服务器网络,在流量突然激增时减轻服务器的负载,并防止一个过于繁忙的客户使同一服务器上的其他客户崩溃。
### 带宽管理队列
ALTQ 通过**队列**来管理带宽。队列是一系列等待处理的分组。
通过将你的带宽分成单独的队列,并按照你的配置处理这些队列,你可以管理服务器带宽。队列有点像杂货店的结账队列;一些队列是为 10 个或更少的分组设计的,可以让你快速结账,而其他队列是为每月购物一次并装满三个购物车的人设计的。你可以为队列定义几乎任何特性,就像你可以创建一个“仅肉类”或“白葡萄酒配鱼”的收银台一样。
工程师们定义了许多不同的队列算法,而对于特定情况最合适的队列方法是一个引发激烈讨论的话题。TCP/IP 服务质量队列处理是那些让孩子们哭泣的话题之一。默认情况下,所有基于 BSD 的系统都使用先进先出(FIFO)队列,其中分组按照接收的顺序进行处理。较新的分组会等待在队列中,直到较旧的分组移动。
OpenBSD 还支持优先级队列(PRIQ 或 prio),其中内核将某些类型的数据包视为“优先”并首先处理它们。这意味着如果您将网页数据包分配最高优先级,所有网页数据包都会跳到队列的头部。在优先级队列方案下,低优先级的数据包可能根本不会被处理。如今,几乎所有东西都支持优先级队列,尤其是交换机。优先级队列的目标是减少特定流量(如语音或视频)的延迟,通过增加不那么紧急流量的延迟来支付这种减少的延迟。
然而,在大多数需要调节带宽的操作环境中,基于类的队列(CBQ)是合适的。CBQ 允许网络管理员通过分层类为不同类型的流量分配一定量的带宽。每个类都有自己的队列,具有自己的带宽特性。您可以将不同类型的流量分配到不同的类中:将 SSH 分配到一个类,将 HTTP 和 HTTPS 分配到另一个类,等等。CBQ 的一个优点是其分层特性允许低级队列从高级队列借用可用带宽。
由于我发现 CBQ 适用于大多数环境,所以我在这里重点介绍它。一旦您掌握了 CBQ,如果您需要 PRIQ,您会发现它很容易理解。
### 父队列定义
队列配置从定义父队列开始。所有其他队列都是父队列的子队列。父队列连接到网络接口,通常是面向互联网的接口。将您的队列定义放在*pf.conf*文件中。我把队列放在文件顶部,在所有数据包过滤规则之前。
在接口上定义父队列的方法如下:
1altq on 2interface 3cbq bandwidth 4bw qlimit 5qlim tbrsize 6size 7queue { 8queue1, 9queue2}
所有 ALTQ 父队列定义都以`altq`关键字**1**开始,然后指定此队列所连接的接口**2**。(每个接口最多只能有一个父队列。)然后指定您正在使用的队列类型**3**。对于 CBQ 队列,队列类型始终是`cbq`。
现在定义父队列中的总带宽**4**。这不同于接口可以传输的带宽量,而是您合理预期的上游传输带宽量。如果您的 OpenBSD 机器有一个千兆网卡,但您只有 10 兆的互联网带宽,则使用`10Mb`作为您的带宽(或者调整带宽值,直到达到实际可用的分配)。您可以使用以下区分大小写的带宽缩写:
+ ****`b`****.每秒比特
+ ****`Kb`****.每秒千比特
+ ****`Mb`****.每秒兆比特
+ ****`Gb`****.每秒千兆比特
可选的`qlimit`参数指定队列可以持有的数据包数量**5**。默认值是`50`,对于几乎所有情况都足够了。我建议除非特定的调试显示您需要更大的队列大小,否则不要设置`qlimit`。
此示例包括令牌桶调节器大小配置,因为 `tbrsize` 允许你指定数据包可以传输的速度 **6**。ALTQ 默认以线缆允许的速度传输数据包。与 `qlimit` 一样,除非你遇到问题,否则我建议不要设置 `tbrsize`。
接下来,将其标识为父队列 **7**,并定义子队列 `queue1` **8** 和 `queue2` **9**。
这是如何配置一个具有 50 兆比特上行链路的父队列,并定义子队列 `ssh`、`web` 和 `mgmt`:
altq on em0 bandwidth 50Mb queue {ssh, web, mgmt}
`tbrsize` 和 `qlim` 关键字未设置,因此它们处于默认状态。
### 子队列定义
一旦你有一个父队列,你就可以定义子队列。使用以下语法定义 CBQ 队列:
queue 1name on 2interface bandwidth 3bw [priority 4pri] [qlimit 5qlim] cbq 6(options) 7
每个队列都需要一个名称 **1**,在父队列定义中定义,长度不超过 15 个字符。名称不需要唯一——你可以在不同的接口上使用相同名称的队列——但我建议使用唯一的名称。
接口是应用此队列的特定接口 **2**。如果你没有定义接口,通过任何接口传输的流量都可以分配到这个队列。
`bandwidth` 术语使用与父队列相同的带宽标签,但分配给所有子队列的总带宽不能超过父队列上可用的总带宽 **3**。你也可以使用百分比值来表示带宽,表示此队列可以消耗父队列的百分比。带宽和队列是子队列描述中的唯一强制术语。
以下定义了 `ssh` 子队列并给它分配了 2 兆比特的带宽:
queue ssh bandwidth 2Mb
这里有一个名为 `web` 的子队列,它被允许使用父队列带宽的三分之四:
queue web bandwidth 75%
你可以为队列分配一个优先级 **4**。CBQ 优先级从 `0` 到 `7`,`7` 是最高的。默认优先级是 `1`。具有更高优先级的 CBQ 队列不会排除其他队列,但 PF 会比其他队列更快地处理它。
与父队列一样,你可以给子队列分配一个 `qlimit` **5**,但除非你有特定的问题需要用这个值来解决,否则不要这样做。
你可以为 CBQ 子队列分配选项 **6**。我们将在下一节中查看这些选项。
最后,子队列可以有它们自己的子队列。在队列 **7** 中定义一个队列的子队列。你将在 CBQ 规则集 中看到一个例子。
### 队列选项
通过为队列分配选项来修改子队列处理数据包的方式。选项让你决定队列应该如何响应各种网络条件和带宽可用性。
#### 默认
每个父队列必须有一个且仅有一个默认子队列。如果一个跨越队列接口的数据包没有被分配到其他队列,它将被分配到默认队列。
#### 随机早期检测
随机早期检测(RED)是一种处理队列开始填满时的数据包丢失的方法。随着队列的填满,越来越多的数据包被丢弃。RED 随机选择数据包进行丢弃。结果是,短传输,如 HTTP 请求和交互式 SSH 会话,响应更快,而大数据传输则变慢。
TCP 客户端和服务器通过减少吞吐量来对丢弃的数据包做出反应。UDP、ICMP 和其他协议没有内置的针对数据包丢失的反应。在预期携带 TCP 的队列上使用 RED 是合理的,但不是在其他协议的队列上。
#### 显式拥塞通知
显式拥塞通知(ECN)是对 RED 的一种修改,它是在数据包中设置标志而不是丢弃数据包。如果一个设备识别到 ECN 标志,它将降低传输速率。
然而,并非所有平台都理解 ECN,而且许多可以识别 ECN 的平台默认禁用它。微软的 Windows Vista 及更高版本、Apple OS X、FreeBSD 和 OpenBSD 可以支持 ECN,但默认禁用。较新的 Linux 版本如果其他主机请求,则支持 ECN。我在企业环境中成功使用了 ECN,在那里我可以让支持人员启用桌面上的 ECN。
除非你知道正在使用的操作系统并且可以控制它们的设置,否则请坚持使用标准的 RED。
#### borrow
`borrow`选项仅在 CBQ 中可用。如果父队列有可用带宽,设置了 borrow 的队列可以从父队列借用带宽。例如,你可能有一个为 VoIP 保留 20%带宽的队列。如果你在任何特定时刻没有那么多 VoIP 流量,父队列将有额外的带宽。其他队列可以从这个分配中借用带宽。然而,当你的 VoIP 流量激增时,PF 会撤销带宽贷款,VoIP 流量将获得为其预留的带宽。
在你想要允许借用带宽的队列上使用`borrow`选项,而不是在可能借用带宽的队列上。
### CBQ 规则集
在配置队列之前,确定你想要如何划分你的带宽。虽然你可以使用每秒比特数来管理带宽,但对于我们大多数人来说,百分比更容易处理。以下是一个公司 10 兆比特链路互联网带宽的划分方法。首先列出你想要的带宽预留列表,然后为每个类别分配一个名称,如下所示:
+ SSH(`ssh`)占 5%
+ 我们电子商务服务器的入站流量为 50%,使用 RED(`web`)
+ 高优先级的入站 VoIP 占 5%(`voip`)
+ 其他流量,包括 DNS、SMTP 等占 40%
所有这些队列都可以从父队列借用带宽。
首先定义父队列。
altq on em0 cbq bandwidth 10Mb queue {ssh, web, voip, other}
这个父队列连接到接口`em0`,有 10 兆比特的带宽和四个子队列。保留所有其他选项不变。
现在定义第一个子队列。
queue ssh bandwidth 5% cbq (borrow)
从队列名称和您选择的带宽百分比开始。此百分比是从特定队列的父队列计算的,因此大约是 10 兆比特的 5%,即每秒 500 千比特。这应该足够用于远程登录和修复任何问题。添加`borrow`选项可以让您在可用的情况下使用更多带宽。
从这个示例开始,您可以定义其他子队列。
queue web bandwidth 50% cbq (borrow, red)
queue voip bandwidth 5% cbq (borrow)
queue other bandwidth 5% cbq (borrow, default)
另一个队列是您的默认队列。任何未分配到其自己队列的流量都将分配到这个队列。
### 将流量分配到队列
使用`queue`关键字在数据包过滤规则末尾将流量分配到队列。要允许所有 SSH(端口 22)流量进入网络并将其分配到名为`ssh`的队列,请使用如下规则:
pass in on egress proto tcp from any to lan:network port 22 queue ssh
### 使用`match`关键字
有时您必须对流量进行分类而不进行过滤。前面的示例允许您将传入的 SSH 流量分配到`ssh`队列,但您想同时捕获出站的 SSH 怎么办?考虑以下规则片段:
pass in on egress proto tcp from
pass out on egress from lan:network to any
这允许`customers`表中的主机通过端口 22 连接到`sshservers`表中的主机。第二条规则允许本地网络发送任何流量或任何协议。其中一些出站流量将是 SSH 流量。您是否需要为排队流量单独编写一条规则?
这就是`match`关键字发挥作用的地方。使用`match`,您可以更改 PF 如何分类流量,而无需更改其过滤方式。以下是如何将所有 TCP 端口 22 的流量发送到`ssh`队列,而无需更改任何过滤特性:
match proto tcp from any to any port 22 queue ssh
pass in on egress proto tcp from
pass out on egress from lan:network to any
第一条规则匹配所有 TCP 端口 22 的流量并将其分配到`ssh`队列。接下来的规则控制谁可以发送和接收 SSH 连接。
### 查看队列
要查看当前在数据包过滤器中的队列,请运行 `pfctl -s queues`。
pfctl -sq
queue root_em0 on em0 bandwidth 10Mb priority 0 cbq( wrr root ) {ssh, web, voip, other}
queue ssh on em0 bandwidth 500Kb cbq( borrow )
queue web on em0 bandwidth 5Mb cbq( red borrow )
queue voip on em0 bandwidth 500Kb priority 7 cbq( borrow )
queue other on em0 bandwidth 500Kb cbq( borrow default )
使用`-v`选项可以为您提供每个队列状态的简要快照。要查看所有队列的持续更新视图,包括从每个队列借用多少流量、丢弃了什么等,请使用`-vvsq`或`systat queues`。
## PF 边缘
本节涵盖了几个 PF 配置的细节,它们不太适合其他地方:包含文件和`quick`关键字。
### 使用包含文件
有时将配置文件拆分成多个部分可以简化您的工作。在`pf.conf`中使用`include`语句来完成此操作。
include "/etc/pf/management-addresses"
当我需要管理具有独特配置但某些部分相同的多个 PF 机器时,我会这样做。`management-addresses`文件定义了一个表,列出了可以通过 SSH 连接、执行 SNMP 查询等操作的所有主机和网络。当这些地址中的任何一个发生变化时,我会将此文件复制到所有 PF 主机并重新加载数据包过滤规则。
### 使用快速跳过比赛
PF 按顺序处理数据包过滤规则,最后一个匹配的规则生效,这可能会使设计支持所需精确访问的规则集变得复杂。如果你发现自己陷入了困境,可以使用 `quick` 关键字来终止匹配数据包的其余规则的处理。以下是一个示例:
…
pass in quick proto tcp from any to $sshserver port 22
…
block in proto tcp from any to any port 22
…
第一条规则允许流量访问端口 22 上的宏 `$sshserver` 主机。第二条规则丢弃所有端口 22 的 TCP 流量。第一条规则中的 `quick` 关键字表示,“当一个数据包匹配此规则时,遵循此规则,并不要处理任何更多规则。”在这种情况下,SSH 连接将被允许。
`quick` 关键字在锚点中特别有用,因为自动化过程(如 `ftp-proxy(8)`)为特殊目的添加的规则可能会被后来为无关目的添加的规则覆盖。
我内心那个纯粹主义者想要坚持认为所有静态规则集都应该不使用 `quick` 来编写。虽然从严格意义上来说这是正确的,但有时避免使用 `quick` 可能会创建难以理解的规则集。一个容易理解的规则集比那些复杂但语法纯熟的规则集更安全。
## 记录 PF 日志
在规则中使用 `log` 关键字告诉 PF 记录数据包。
pass out log on egress from lan:network to any
然而,如果没有额外的设置,这些日志将直接发送到 PF 日志设备 `pflog0`。要成功记录 PF 消息,你必须运行数据包过滤器记录器 `pflogd(8)`。如果你在引导时启动 PF,`pflogd` 会自动启动。否则,你必须通过命令行启动它。
需要记住的一点是,如果你正在使用状态检测,只有触发规则的第一数据包会被记录。属于同一状态的其它数据包不会被记录。若要记录状态连接中的所有数据包,给 `log` 关键字添加 `all` 修饰符,但请注意,这可能会生成非常大的日志。
pass out log (all) on egress from lan:network to any
记录日志在解决连接问题时特别有用。如果你认为应该允许通过的数据包被阻止了,请在你的 `block` 语句中添加记录,以查看哪个规则阻止了流量。
我不推荐记录所有内容,尤其是因为日志可能会变得相当大。有选择性地记录。例如,你可能不关心本地用户访问了哪些网站,但确实想了解传入流量。并且确保排除防火墙日志流量从你的数据包过滤器日志中,否则你很快会发现 PF 正在记录日志传输的传输,这些是传输日志的日志,从你传输日志的那一刻起……等等等等。
### 阅读 PF 日志
PF 以 `tcpdump(8)` 二进制格式记录日志。使用 `tcpdump` 来检查数据。若要仅将日志中的所有内容输出,告诉 `tcpdump` 读取日志文件。
tcpdump -r /var/log/pflog
这可能会生成大量输出。参见 过滤 tcpdump 获取一些提示。
### 实时日志访问
*/var/log/pflog* 中的条目不是实时添加的;`pflogd(8)` 会缓冲其记录,直到写入日志信息变得有意义。要实时查看 PF 日志,请使用 `-i` 标志将 `tcpdump` 连接到 `pflog0` 接口。
tcpdump -i pflog0
根据你记录的流量大小,这可能会产生大量令人难以承受的信息。你必须过滤 `tcpdump` 以使其变得有用。或者,如果你假装错过了我之前关于日志大小的警告,你可以编写一个单行命令,使用 `logger` 将你的 PF 日志作为文本发送到 `syslog`。
### 过滤 tcpdump
每个系统管理员都应该知道如何使用 `tcpdump`。这是你这样做的原因。
当你遇到特定连接的问题时,你可能不关心通过过滤器的每个数据包。你关心的是特定主机的流量。使用 `ip` 或 `ip6` 表达式指定一个 IP 地址。
tcpdump -i pflog0 ip host 192.0.2.2
这将仅显示与此特定主机之间的流量。
为了进一步缩小范围并仅查看两个主机之间的流量,请使用 `and` 关键字结合主机。
tcpdump -i pflog0 ip host 192.0.2.2 and ip host 203.0.113.88
也许你只对特定地址上的特定端口感兴趣。使用 `tcp` 或 `udp` 关键字和端口号进行过滤。
tcpdump -i pflog0 ip host 139.171.199.254 and tcp port 80
读取 `tcpdump(8)` 手册页,以获取无数其他过滤选项的详尽列表。
如果你不喜欢使用 `tcpdump`,可以考虑使用 `pflow(4)` NetFlow 导出器。网络流是一个复杂的话题,但书籍 *网络流分析*(No Starch Press,2010)可能对你有所帮助。
### 规则集跟踪
有时候,知道一个数据包是否通过或失败并不足够。你知道数据包被阻止了,但不知道原因。你想要观察数据包通过规则,并看到哪些规则影响了它。
假设内部主机 192.0.2.226 无法连接到外部主机 203.0.113.34。日志将显示数据包被阻止,但不会显示原因。你可以特别让 PF 记录匹配规则。在你的 *pf.conf* 文件顶部添加如下一行:
match log (matches) from 192.0.2.226 to 203.0.113.34
这是一个标准的包过滤规则。你可以使用单个 IP 地址、端口号或其他任何合法的包过滤术语。重新加载你的包过滤规则。
打开 `tcpdump`,并根据你的 `match` 语句中的一个 IP 地址进行过滤。如果你使用 NAT,请过滤不改变的 IP 地址。
tcpdump -n -e -ttt -i pflog0 ip host 203.0.113.34
Dec 17 18:05:07.773703 rule 0/(match) match out on fxp0: 192.0.2.226.24916
203.0.113.34.22: S 1730871963:1730871963(0) win 16384 <mss 1460,nop,nop,
sackOK,nop,wscale 3,nop,nop,timestamp 597858150[|tcp]> (DF)
Dec 17 18:05:07.773708 rule 2/(match) block out on fxp0: 192.0.2.226.24916
203.0.113.34.22: S 1730871963:1730871963(0) win 16384 <mss 1460,nop,nop,
sackOK,nop,wscale 3,nop,nop,timestamp 597858150[|tcp]> (DF)
Dec 17 18:05:07.773712 rule 5/(match) pass out on fxp0: 192.0.2.226.24916
203.0.113.34.22: S 1730871963:1730871963(0) win 16384 <mss 1460,nop,nop,
sackOK,nop,wscale 3,nop,nop,timestamp 597858150[|tcp]> (DF)
虽然我不会详细介绍阅读 `tcpdump` 输出的所有烦人细节,但你可以看到 PF 会记录与这个数据连接匹配的规则编号,以及规则是允许还是阻止了连接。如果连接涉及 NAT,你将看到实际和转换后的 IP 地址。
到目前为止,你已经了解了足够多的 PF 知识来保护一个小型网络。如果你需要更多,请务必查看 *PF 书籍,第 2 版*(No Starch Press,2010)。
现在,让我们看看 OpenBSD 中一些更奇特的功能。
* * *
^([48]) 洛卡斯能在一天内配置一个高可用防火墙集群吗?是的。他能在不搞砸一切的情况下在文本文件中搜索和替换 IP 地址吗?不行。
## 第二十三章. 定制 OpenBSD
*定制安装*
*使用文件和 DHCP,*
*然后无盘运行。*
 这章介绍了使用 OpenBSD 来自定义自己的不同方法,以及如何在非标准情况下安装 OpenBSD 和调试系统问题。我们将要解决的第一项任务是无盘安装。无盘系统通常用于在不连接任何安装媒体的情况下安装 OpenBSD,但它们也可以用于在没有硬盘的情况下运行系统。接下来,我们将创建一个 USB 闪存驱动器,用作 OpenBSD 安装媒体。最后,我们将介绍各种自定义 OpenBSD 安装和升级过程的方法。
所有这些任务都假设你已经有一个运行着你想要定制的版本的 OpenBSD 机器。只要虚拟机软件有必要的支持,你可以使用虚拟机来完成这些任务之一。由于虚拟化是一个常见的选项,让我们先来解决这个问题。
## 虚拟化 OpenBSD
OpenBSD 的开发者对虚拟化非常明确。OpenBSD 是为真实硬件编写的。虚拟硬件不是真实硬件。虽然它可以非常相似,但并不完全相同。
这种方法有几个影响,其中最成问题的是,并非所有虚拟化软件都能运行 OpenBSD。当我写这篇文章时,Oracle 的 VirtualBox 无法干净地运行 i386 或 amd64 版本的 OpenBSD。(有些人报告说能够启动某些版本的 VirtualBox 和/或 OpenBSD,但 OpenBSD 软件到处崩溃。)这不是 OpenBSD 的 bug。VirtualBox 没有充分模拟真实硬件。
话虽如此,OpenBSD 在某些虚拟机上运行得很好。VMware 运行得足够好,以至于 OpenBSD 包括针对 VMware 集成的特定驱动程序,包括内核中的 VMware Tools 驱动程序。KVM 虚拟化也工作,尽管 KVM 需要根据你使用的 KVM 和 OpenBSD 的确切组合进行一些调整。微软的虚拟化大部分工作,尽管 Virtual PC 有一些商业动机的限制。
虚拟化的主要问题是,虚拟化平台的妥协会自动给入侵者提供对所有虚拟机的硬件级访问权限,而 OpenBSD 不可能保护你免受这种攻击。实际上,没有任何操作系统可以做到。当任何脚本小子都能妥协底层的虚拟化服务器时,在 OpenBSD 上运行数据库对你没有任何好处。
根据我的经验,OpenBSD 虚拟机非常适合实验和参考。我使用它们来记录这本书的安装过程,并且在将它们部署到生产之前,我总是在虚拟机上测试软件配置。(虚拟化的真正好处可能就是不再有任何借口不测试更改。)但是,当我需要一个真正安全的服务器时,我会将 OpenBSD 安装在真实硬件上。
### 注意
如果你想在 OpenBSD 上运行虚拟机,你可以在软件包集合中找到 `qemu`、`bochs`、`dosbox` 和其他软件包。检查 */usr/ports/emulators* 以获取其他选项。
## 无盘安装
不使用本地媒体将空白系统引导到 OpenBSD 安装程序可以节省你的时间和精力。许多现代硬件都没有配备 CD 或软盘驱动器。当然,你可以临时添加一个 CD 驱动器,但如果你要安装大量的 OpenBSD 机器,那只是个麻烦。
你也可以使用网络引导在缺少已安装操作系统的硬件上引导 OpenBSD,或者在与你计划覆盖的不同操作系统上。这个过程被称为 *pxebooting*,或 *diskless* 操作。无盘系统可以有磁盘——只是它们不使用它们来引导操作系统。
如果你之前从未使用过无盘系统,你的第一次尝试可能会让你头疼。设置你的第一个无盘环境可能会很棘手,这会教你许多关于你的操作系统和硬件的未知知识。但沿途测试一切,仔细阅读错误信息,很快你就会 wonder why you thought this was hard.
### 注意
我将介绍在 amd64 和 i386 硬件上的无盘安装。其他平台可能有不同的要求,可能非常不同。阅读 `diskless(8)` 手册页以获取你特定架构的平台概述。
无盘系统之所以能够工作,是因为计算机不需要硬盘来运行。它需要一个操作系统。存储计算机操作系统的最简单方法是在本地硬盘上,但足够智能的网络卡可以使用 DHCP 提供的信息找到初始引导加载程序。
所有 amd64 和现代 i386 硬件都使用 Intel 的预引导执行环境 (PXE,发音为“pixie”)。DHCP 服务器告诉网卡文件的名称和可以找到该文件的 IP 地址,服务器通过 TFTP 获取该文件。这个文件通常被称为 *pxeboot*,但 *pxeboot* 文件在不同的操作系统之间可能会有很大的差异。OpenBSD 的 *pxeboot* 文件可能无法引导 FreeBSD 系统,更不用说微软的任何系统了。它是针对每个操作系统的。
一旦计算机加载了 *pxeboot*,它就会回到 TFTP 服务器去寻找适当的内核。一个 OpenBSD *pxeboot* 会寻找一个名为 *bsd* 的文件,假设它是一个内核,将内核加载到内存中,并启动它。要安装 OpenBSD,你需要加载安装内核文件 *bsd.rd*,这可以自动完成。
### 无盘硬件
在无盘系统上安装的 OpenBSD 系统必须足够聪明,能够在网络上找到其引导加载程序和操作系统,否则它们将无法启动。过去几年内制造的任何机器都使用 PXE。
你可能已经看到过一台计算机多次尝试从网络启动,对于大多数人来说,这些 BIOS 信息只是他们不断忘记禁用的烦恼。对于无盘安装,你需要确保该功能是开启的。
要启用 PXE,请启动硬件并进入 BIOS 设置。在 BIOS 中,你应该找到一个设置设备启动顺序的选项。如果机器支持 PXE,其中之一将是通过网络启动。启用该选项并查看是否可行。当你在 BIOS 中时,记下你的网络卡的 MAC 地址。你的 DHCP 服务器将需要它。如果你的 BIOS 默认使用统一可扩展固件接口 (UEFI),请禁用它。
保存你的更改并退出。现在你的硬件应该已经准备好了。让我们准备服务器。
### DHCP 服务器设置
DHCP 不仅仅是分发 IP 地址和网络配置的方式。DHCP 服务器可以告诉网络感知的电话在哪里找到它们的配置,服务器硬件在哪里找到它的操作系统,打印机在哪里找到它们的打印服务器,等等。无盘安装使用 DHCP 向无盘服务器提供 *pxeboot* 文件的存储位置。
#### 每个主机或每个网络的配置
DHCP 期望通过网络或主机配置主机。当 DHCP 服务器收到 DHCP 请求时,它知道主机所在的网络地址和主机的 MAC 地址。根据这些信息,DHCP 服务器必须决定向主机提供哪种配置。这意味着你可以配置你的 DHCP 服务器,以便给定网络上的任何主机都被告知安装 OpenBSD,或者你可以给出你将要安装的机器的 MAC 地址,并告诉 DHCP 服务器只在该机器上启动安装。
由于我经常安装机器,我通常设置一个小型 VLAN,任何连接到网络的机器都会被告知安装 OpenBSD。这样,把他们的笔记本电脑插入我办公室随机以太网线的员工就可以免费升级操作系统。如果你只是偶尔安装机器,并且控制 DHCP 服务器,配置 DHCP 服务器告诉具有特定 MAC 地址的主机安装 OpenBSD 非常容易。
DHCP 服务器需要告诉客户端 PXE 启动文件的存储位置,这给客户端足够的智能来找到可引导的内核。这就像磁盘上的引导加载器一样,只不过 PXE 启动文件是与网络通信。OpenBSD 的 i386 和 amd64 平台包括文件 */usr/mdec/pxeboot*,就是为了这个目的。
使用 `filename` 选项给出 PXE 启动文件的名称,然后使用 `next-server` 选项指定客户端可以获取文件的 TFTP 服务器的 IP 地址。此示例告诉 DHCP 客户端从 IP 地址为 192.0.2.34 的服务器加载文件 *pxeboot*:
filename "pxeboot";
next-server 192.0.2.34;
根据你是否有一个安装网络或你的 DHCP 服务器设置为特定的 MAC 地址,放置这些语句。
#### 每个网络的配置
如果您想让网络上的所有主机都能接收到 OpenBSD 安装的 PXE 启动文件,请在`subnet`段落中放置`filename`和`next-server`选项,如下所示:
option domain-name "michaelwlucas.com";
option domain-name-servers 192.0.2.1;
subnet 192.0.2.0 netmask 255.255.255.0 {
option routers 192.0.2.1;
range 192.0.2.10 192.0.2.15;
filename "pxeboot";
next-server 192.0.2.34;
}
网络上任何在启动时发出 DHCP 请求的主机都会学习到获取 PXE 启动文件的位置。
#### 每台机器的配置
如果您已经在第十六章中讨论的 DHCP 配置中将机器的 MAC 地址硬编码,您可以向该主机提供 PXE 启动信息。
subnet 192.0.2.0 netmask 255.255.255.0 {
…
host installationtarget {
hardware ethernet 02:03:04:05:06:07;
filename "pxeboot";
next-server 192.0.2.34; }
}
在此子网上启动时发出 PXE 请求的机器只有在它们具有 MAC 地址 02:03:04:05:06:07 时才能获得 PXE 启动文件的位置。
决定您希望您的 DHCP 服务器如何行为,并做出类似的配置更改。
现在让我们看看 TFTP 服务器。
### TFTP 服务器设置
下一个任务是使 OpenBSD 特定的启动文件在您的 TFTP 服务器上可用。至少,您需要*pxeboot*文件和一个内核,但添加一个*boot.conf*文件将简化您的工作。
OpenBSD 在`/usr/mdec/`中包含一个特定于架构的*pxeboot*文件。如果您正在安装 i386 机器,从现有的 i386 安装中获取此文件和`/bsd.rd`。如果您正在安装 amd64 硬件,从现有的 amd64 系统中获取*pxeboot*和`/bsd.rd`。将它们复制到 TFTP 服务器的根目录,并验证它们是否对所有用户可读。
*pxeboot*告诉机器查找标准内核*/bsd*,而不是安装内核*/bsd.rd*。当*pxeboot*加载完成后,它看起来就像标准的 OpenBSD 引导加载程序。您可以像在第五章中描述的那样中断引导,并选择不同的内核,但*pxeboot*也识别`/etc/boot.conf`。
要告诉*pxeboot*加载不同的内核,在您的 TFTP 服务器根目录中创建一个*etc*目录,然后在其中创建*boot.conf*文件。这个新的*boot.conf*文件与`/etc/boot.conf`具有完全相同的语法,因此您可以进行如下一行条目:
boot bsd.rd
您可以包括额外的启动选项,例如设置串行控制台。
### 完成无盘安装
一旦您有了 DHCP 和 TFTP,打开安装目标。您应该看到网卡发出 DHCP 请求,获取 IP 地址,并通过 TFTP 获取*pxeboot*。然后您应该看到 OpenBSD 引导加载程序加载安装的*bsd.rd*。最后,您应该获得 OpenBSD 安装脚本。
如果你没有获取到安装程序,请退一步。网卡是否从 DHCP 获取地址?如果不是,检查你的布线和 DHCP 服务器配置。如果你获取了 IP 地址,但不能获取*pxeboot*,请确保你在 DHCP 配置的正确部分放置了`filename`和`next-server`语句,并验证你没有包过滤器阻止对 TFTP 服务器的访问。尝试从不同的 TFTP 客户端获取这些文件,以确保 TFTP 服务器工作正常。如果安装目标部分启动了 OpenBSD,但没有激活安装程序,请确保你有指向*bsd.rd*而不是*bsd*的*etc/boot.conf*条目。
在这个阶段,你应该能够按照第二章和第三章中描述的方法正常安装 OpenBSD。但是,如果你想在没有硬盘的情况下运行完整的 OpenBSD 系统呢?这就是无盘操作的作用所在。
## 运行无盘系统
如果你管理许多计算机,你可能理解到移动部件会带来麻烦。特别是旋转硬盘,这只是一个非常糟糕的想法。
尝试这样做:如果你有一间满是相同机器的房间,尝试通过不使用硬盘来简化维护。这个组中的每台机器将使用通过 NFS 挂载的根目录和文件系统,而不是本地存储。你仍然需要数据存储,但可以使用一个中央高可用磁盘阵列、闪存驱动器或一些其他机制,其可靠性比最低标准的硬盘更好。
你可以将无盘安装过程扩展到在没有本地硬盘的情况下以全多用户模式运行 OpenBSD。你的服务器将需要三个额外的服务来支持完全无盘客户端:`rarpd(8)`、`bootparamd(8)`和 NFS。(只有无盘客户端需要`rarpd`和`bootparamd`。)
### 使用`rarpd(8)`进行反向 ARP
在标准的 ARP 请求中,客户端知道一个 IP 地址,并想要获取相应的 MAC 地址。对于反向 ARP,客户端知道一个 MAC 地址,并想要知道相应的 IP 地址。OpenBSD 需要在无盘启动过程中获取反向 ARP,它使用`rarpd(8)`为其他主机提供反向 ARP 服务。
`rarpd`使用`/etc/ethers`作为以太网地址和主机名的表格。每个无盘客户端需要一个类似于以下的`/etc/ethers`条目:
00:50:56:00:01:01 gill.blackhelicopters.org
这个条目意味着 MAC 地址为 00:50:56:00:01:01 的主机的主机名为*gill.blackhelicopters.org*。`rarpd`服务器必须能够将主机名解析为 IP 地址,无论是在 DNS 中还是在`/etc/hosts`中。
现在决定你想要在哪个网络接口上运行`rarpd`。如果你的服务器只有一个网络接口,那么就使用它。然而,如果你有多个网络接口,可能只监听一个接口是有意义的。
要使用特定的接口,请使用接口名称作为命令行参数;否则,使用 `-a` 以监听所有网络接口。例如,此 *rc.conf.local* 条目指示 `rarpd` 仅在接口 `em0` 上监听:
rarpd_flags="em0"
使用 */etc/rc.d/rarpd* 启动 `rarpd`,然后继续 `bootparamd`。
### 运行 bootparamd(8)
启动参数守护进程 `bootparamd` 告诉无盘 OpenBSD 机器其根文件系统的位置。当启动参数请求到达服务器时,`bootparamd` 会检查 */etc/bootparams* 文件以查找匹配的配置,并将其返回给客户端。
*/etc/bootparams* 中的条目给出一个主机名,后跟字符串 `root=`,一个 NFS 服务器,以及客户端根目录存储的目录。
gill.blackhelicopters.org root=192.0.2.34:/var/diskless/client1
在此示例中,主机 *gill.blackhelicopters.org* 将使用来自 IP 地址 192.0.2.34 的服务器的 NFS 根目录,在目录 */var/diskless/client1* 中。
对于几乎所有环境,您可以在没有任何命令行选项的情况下运行 `bootparamd`。像这样在 *rc.conf.local* 中启用它:
bootparamd_flags=""
启动 `bootparamd`。现在是你处理你的 NFS 服务器的时候了。
### 设置 NFS 根目录
多用户 OpenBSD 系统需要一个用户空间。没有本地磁盘,您需要创建一个 OpenBSD 用户空间。可以将 NFS 服务器的根目录导出以用作无盘客户端的根目录,但这不仅是不安全的,而且也是损坏 NFS 服务器的良好方式。为您的无盘机器创建一个单独的用户空间。
#### 导出根目录
您必须将用户空间的根目录导出到无盘机器。例如,以下是一个 */etc/exports* 行,它将目录 */var/diskless/client1* 共享到 IP 地址 192.0.2.37:
/var/diskless/client1 -maproot=root 192.0.2.37
注意这里的 `-maproot` 选项。无盘客户端期望能够以 root 用户身份写入和拥有文件。此 `-maproot` 条目将客户端的 UID 0(root)映射到 NFS 服务器的 root 账户。您还可以为无盘客户端的 root 账户设置一个单独的用户,将客户端的 root 账户映射到该新账户,并将无盘用户空间中所有文件的拥有权更改为该 root 账户。但是,由于这是您的第一个无盘主机,我们将从基本开始。
#### 填充无盘用户空间
安装最小用户空间的最简单方法是提取您选择的 OpenBSD 版本的 *etcXX.tgz* 和 *baseXX.tgz* 文件集到 NFS 根目录。在以下示例中,我已经将这些文件集复制到 */tmp*,并使用它们在 */var/diskless/client1* 中创建用户空间。
cd /var/diskless/client1
tar -xzpf /tmp/etc53.tgz
tar -xzpf /tmp/base53.tgz
注意在 `tar` 命令中使用 `-p` 标志,以保留提取文件的原始权限。
无盘客户端还需要设备节点。进入新用户空间的 *dev* 目录并创建它们。
cd dev
./MAKEDEV all
虽然 `bootparamd` 告诉内核文件系统的根位置,但用户空间程序期望读取 */etc/fstab* 来获取该信息。创建一个 */etc/fstab* 文件,将根目录指向您的 NFS 共享。
192.0.2.34:/var/diskless/client1 / nfs rw 0 0
您还可以在此处添加任何其他所需的 NFS 挂载目录。
这应该就是您需要的一切。
### 开机!
一旦您有一个基本用户空间、设备节点和文件系统表,您就可以开启无盘节点,它应该会启动。如果它没有启动到登录提示符,请阅读控制台错误消息。通常,它们非常清晰。
由于您绕过了 OpenBSD 安装程序,目前还没有 root 密码或用户账户。立即以 root 身份登录并更改 root 密码,然后设置一个普通用户账户。
对于您的第一个无盘设置,一旦您有一个工作的用户空间,立即备份它。即使是一个包含整个用户空间的 tar 文件也将非常有用。在您试图将一切设置得尽可能完美时,您可能会多次弄乱无盘用户空间,能够将整个用户空间全部删除并从备份文件中恢复是非常宝贵的。
一旦您有一个基本系统运行,就可以扩展它。根据需要添加额外的文件集,设置更多用户,添加软件包,并为您的用户部署。
恭喜您,现在您已经成为 OpenBSD 用户的尖端用户。
## USB 安装介质
对于许多人来说,烧录 CD 来安装操作系统似乎是一种浪费。他们更喜欢将镜像写入 USB 闪存驱动器并从那里安装。OpenBSD 不提供这样的镜像,但如果您愿意做一些额外的工作,您可以创建一个可引导的 USB 设备,用于在目标硬件上安装。
官方建议是在 USB 设备上安装 OpenBSD,将*bsd.rd*文件和文件集复制到该设备,并使用它来安装新硬件。OpenBSD 安装程序允许您选择目标硬盘驱动器。在安装程序中选择 USB 设备,OpenBSD 将像对待任何其他数据存储设备一样安装到 USB 上。但您如何在最初不烧录 CD 的情况下在 USB 设备上安装 OpenBSD 呢?有几种方法可以解决这个问题,包括本章中已经介绍的一些方法。
### 使用虚拟机
您的第一个选择是在虚拟机中执行 USB 安装。许多桌面虚拟机软件包允许您将物理 USB 端口连接到虚拟机。 (OpenBSD 的虚拟化选项在虚拟化 OpenBSD 中讨论。)
如果您有运行 OpenBSD 并支持 USB 的虚拟化软件,请选择此选项。
### 运行无盘安装
您的第二个选择是运行无盘安装。大多数嵌入在廉价家用硬件中的 DHCP 服务器都会允许您向客户端发送一个文件名和一个 TFTP 服务器地址。如果您的服务器不支持,您可以为任何平台获取合适的 DHCP 服务器。您几乎可以在任何操作系统上找到免费提供的 TFTP 服务器。
使用 USB 驱动器启动安装目标,但加载 *bsd.rd* 内核。你现在在目标系统上运行了 OpenBSD 安装程序,并且有一个可以放在口袋里、几乎在任何地方都可以运行的 OpenBSD 系统。如果你已经在具有正确架构和 USB 插座的设备上运行了 OpenBSD,那就更容易了:从适当的 *bsd.rd* 启动系统,选择 `disk` 选项,并将安装程序指向本地目录中的集合。
### 转换 ISO 镜像
作为一种不那么正式的方法,你可以找到将 ISO 镜像转换为可启动 USB 镜像的软件。我在 Windows 上使用了 Rufus (*[`rufus.akeo.ie/`](http://rufus.akeo.ie/)*),在其他类 Unix 系统上使用了 UNetbootin (*[`unetbootin.sourceforge.net/`](http://unetbootin.sourceforge.net/)*)。这种方法可能有效,但绝对不是 OpenBSD 推荐的。
## 自定义 OpenBSD 安装
我们中的许多人安装机器时都会遵循一系列步骤。所有刚刚安装的特定操作系统版本的宿主机都有一个共同的 SSH 服务器配置。我的机器都安装了 `tcsh` 并连接到中央认证系统。你可能有自己的列表。这些任务可以在安装后手动完成,但让 OpenBSD 在安装过程中为你完成这些任务会容易得多。
安装可以通过在安装过程中添加文件或在安装后运行命令来自定义。
### 自定义文件集
自定义文件集包括你想要复制到新安装中的文件。我使用自定义文件集来安装默认的 */etc/sudoers*、SSH 服务器配置、我公司的默认 *pf.conf* 以及类似文件。作为主要系统管理员,我还包括我的主目录中的 dotfiles 和其他个人化细节,以使我的生活更轻松。有些人包括多个主目录,包括 SSH 的 *authorized_keys* 文件。
将这些文件打包成一个 *siteXX.tgz* 文件,安装程序可以在新安装的根目录中提取它。(请确保将 *XX* 替换为你要安装的 OpenBSD 版本;例如,为 OpenBSD 5.4 命名一个 *site54.tgz* 文件。)
首先,安装一个与你想要定制的版本和平台完全相同的 OpenBSD 机器。进行更改并将你的文件添加到这个系统中,验证这个模板系统是否完全符合你的需求,然后将更改后的文件复制到 tar 文件中。
### 注意
你可以创建一个目录层次结构并将你想要的文件复制到其中,但我发现这样做更容易出错。一个小型虚拟机可以让你更可靠地构建 *siteXX.tgz* 文件。
以下示例创建了一个包含一个文件 */etc/ssh/sshd_config* 的 *site54.tar* 文件。请注意,我首先创建了一个普通的 tar 文件。由于我无法轻松地将文件添加到压缩的 tar 文件中,我需要在文件完成后对其进行压缩。
cd /
tar -cf site54.tar etc/ssh/sshd_config
现在我有了初始文件,我可以添加额外的文件。我已经对系统上的几个文件进行了定制,并添加了新的文件,所有这些都添加到 *site54.tar* 文件中。`-r` 标志告诉 `tar` 将文件添加到归档中。
tar -rf site54.tar etc/sudoers
tar -rf site54.tar etc/pf/mgmt-hosts.conf
tar -rf site54.tar etc/pf.conf
下面是如何压缩 tar 文件的方法:
gzip site54.tar
mv site54.tar.gz site54.tgz
我已经构建了自己的 OpenBSD 定制版本,因此我有一个包含所有发布文件的本地 FTP 服务器。如果你使用的是官方 OpenBSD 发布版,但安装了足够多的 OpenBSD 机器,需要创建一个 *siteXX.tgz* 文件,你可以将官方发布版复制到本地 FTP 或 HTTP 镜像。将你的 *siteXX.tgz* 文件复制到这个目录,并更新 *index.txt* 文件。
ls -l > index.txt
现在开始你的安装。告诉安装程序使用你的本地发布镜像而不是官方 OpenBSD 镜像。你应该能看到以下集合:
Select sets by entering a set name, a file name pattern or 'all'. De-select
sets by prepending a '-' to the set name, file name pattern or 'all'. Selected
sets are labelled '[X]'.
[X] bsd [X] etc54.tgz [X] xbase54.tgz [X] xserv54.tgz
[X] bsd.rd [X] comp54.tgz [X] xetc54.tgz [ ] site54.tgz
[ ] bsd.mp [X] man54.tgz [X] xshare54.tgz
[X] base54.tgz [X] game54.tgz [X] xfont54.tgz
Set name(s)? (or 'abort' or 'done') [done] site52.tgz
你的 *site54.tgz* 文件现在应该作为一个文件集可用。添加它,因为安装程序不会自动包含它。一旦安装完成,你应该能在新系统中找到你定制的和添加的文件。
### 安装后 Shell 脚本
一些任务可以通过复制文件来完成,但这很麻烦。例如,我想在我的所有 OpenBSD 服务器上安装 shell `tcsh`。我可以在 `tcsh` 软件包中放置所有文件,以及 */var/db/pkg/tcsh* 的内容,到 *siteXX.tgz* 中,但我知道我可能会以某种方式搞砸。在安装后运行 `pkg_add tcsh` 会容易得多,让 OpenBSD 做它应该做的事情。这就是 `install.site` 脚本的作用所在。
在完成安装但在给出最终命令提示之前,OpenBSD 会检查 */install.site* 文件。如果此文件存在,安装程序会运行它。脚本在 `chroot` 中运行到新安装的系统,所以你不需要担心更改任何路径。然而,脚本需要在最小内核上运行,所以最好在第一次真实启动之前等待低级内核调整。
这里是一个示例 `install.site` 脚本,它安装了两个软件包 `tcsh` 和 `python`:
/bin/sh
export PKG_PATH=ftp://ftp13.usa.openbsd.org/pub/OpenBSD/snapshots/packages/i386/
pkg_add -v tcsh
pkg_add -v python-2.7.3p1
当处理 `install.site` 脚本时,如果软件包的名称可能不明确,请确保给出完整的软件包名称。只有一个 `tcsh` 软件包,但 Python 有几个版本。我指定了完整的软件包名称,而不是使用普通的 `python`。
还要注意,当你在一个包含完整用户空间的 `chroot` 中运行时,那个用户空间并没有完全初始化。当进入 `chroot` 时,OpenBSD 不会在该 `chroot` 中进行完整的多用户启动。环境大致相当于单用户模式。`install.site` 脚本不是你初始化数据库的地方。
当你有准备好的真实用户空间时,要自动在系统第一次真实启动时运行命令,请将命令追加到 */etc/rc.firsttime* 文件中。此文件在安装后的第一次系统启动时运行一次,然后删除自己。
## 定制升级
OpenBSD 允许你在二进制升级期间使用自定义文件集和 shell 脚本。如果你有很多机器需要升级,运行这些脚本来确保你的系统在升级后与升级前尽可能相同。我强烈建议在升级期间自动化已知的变化。
*siteXX.tgz* 文件在升级和安装时工作方式完全相同。将你想要在这个系统上使用的文件放入 *siteXX.tgz* 中,安装程序在安装升级文件时应该会复制这些文件到系统中。然而,与 `install.site` 不同,升级软件会查找脚本 `upgrade.site`。在升级过程中,任何 *install.site* 文件都会被忽略,因此你可以使用相同的 *siteXX.tgz* 进行升级和新的安装。
我发现 `upgrade.site` 脚本与该版本的 *OpenBSD Upgrade Guide* 结合使用特别有用。*升级指南* 包含在升级过程中必须执行的任务,其中许多非常适合脚本化。例如,删除新 OpenBSD 版本中删除的文件、程序和库的常见任务可以轻松添加到 `upgrade.site`。
关于 `upgrade.site` 的一个方便之处是,在运行升级之前,你可以将脚本复制到目标机器上。它不需要是 *siteXX.tgz* 的一部分。话虽如此,我不建议在 `upgrade.site` 中运行 `pkg_add -u`。虽然自动升级所有软件包的想法听起来不错,但请记住,你正在运行在一个有限的内核上,用户空间还没有完全初始化。让 `upgrade.site` 脚本添加任何需要在完全多用户系统上运行的命令到 */etc/rc.firsttime*,这样它们就会在系统第一次启动时运行。
通过本章中的提示,你可以根据需要自定义 OpenBSD。并且,通过本书中的信息,你应该知道 OpenBSD 在你的网络中是如何定位的。记住,他们确实是在针对你,你将实现实用的偏执。
## 附录 A. 后记
*失败已经够糟糕的了;*
*再加上人为因素*
*事情真的很糟糕。*
大约在 2000 年左右,我的雇主的主要业务是设计网络应用程序,但一旦这些应用程序建成,我们的客户就会转过来问,“我们应该在哪里托管这个?”这就是我的工作,为定制应用程序构建和运行一个小型但专业级的数据中心。
就像任何新的商业一样,我们的托管业务需要充分利用现有资源。硬件严格限制为来自网络开发者的废弃设备,我们只使用免费软件。唯一的主要开支是一台知名的商业防火墙,购买的原因更多是出于营销而非技术。
使用大量开源软件,我们构建了一个可靠的网络管理系统,为我们客户提供比他们内部人员所能提供的更多设备洞察。客户支付自己的硬件费用,因此拥有他们选择的、配备有应用程序、平台和操作系统的昂贵高端机架服务器。随着业务的增长,我们升级了硬件(拥有五年以下的硬盘驱动器真是太好了),但我们没有看到更换软件的必要。
一周一早,一位预期使用很少带宽的客户发现,他的请求足以消耗掉整个数据中心带宽的两倍。这影响了每一位客户。如果你的每月 9.95 美元的网页运行缓慢,你没什么好抱怨的;如果你的每月 50,000 美元的网络应用程序运行缓慢,你会拿起电话大喊大叫,直到问题解决。
更糟糕的是,我的祖母几天前去世了。参观是在星期二,葬礼是在星期三早上。我把问题交给了一个小弟,说,“这里,处理一下这个问题。”我知道网络可以在许多点管理带宽。网络服务器本身、它们前面的负载均衡器、商用防火墙,甚至路由器都声称有流量管理能力。
星期二,在参观之后,我的手机语音信箱满了。我们版本的互联网信息服务器(IIS)可以管理带宽——以 8MB 的增量,但仅限于静态 HTML 和 JPEG 文件。在负载均衡器后面的几个网络服务器,这几乎是无用且可笑的。如果购买新的功能集,负载均衡器*会*支持流量整形。如果我们放下信用卡,我们可以在下周日之前安装那个功能集。我们知名的商用防火墙也提供了流量整形功能,*如果*我们升级我们的服务级别并支付额外的(相当可观的)费用来获取该功能集。这留下了路由器,我之前已经调查过,发现只需升级 IOS 即可支持流量整形。
我一直打电话到周二晚上午夜,安排在周三晚上进行紧急路由器 IOS 升级。我计划周三早上参加葬礼,发表悼词,回家小睡一会儿,然后在午夜到达工作地点,准备迎接挑战。
不幸的是,葬礼比我预期的更戏剧化,我是在毫无睡意、眼睛模糊、身体笔直的情况下,仅靠咖啡因和肾上腺素的共同恩赐才到达办公室的。在我的电子邮件中,我发现一条笔记,几个大客户威胁说,除非问题在周四早上解决,否则他们将离开。如果我没有已经压力很大,选择一个下属解雇的可能性就会让我崩溃。(我努力训练我的下属,一旦他们被打造成型,我就不愿意替换他们。)
然而,只有简单的路由器固件升级和一些基本配置就介于我和解脱之间。可能出什么问题呢?
升级过程顺利,但当我启用流量整形功能时,路由器表现异常。在接下来的几个小时里,我发现路由器没有足够的内存同时支持所有的 BGP 数据流和流量整形功能。更糟糕的是,它不接受更多的内存。大约早上 6:00,我终于从路由器供应商那里得到了承认,他们无法帮助我。
我挂断了电话。第一个威胁要离开的客户将在早上 7:30 到达。我在过去的 48 小时内只睡了 4 个小时,大部分时间都在承受着恶魔般的情绪压力。我已经用完了自动售货机里的所有零钱,还从同事的桌子上搜刮了更多的零钱。让我到达办公室的咖啡因和肾上腺素早已消耗殆尽,而更多的剂量只是减缓了我的崩溃。我们每台设备都有支持合同,但它们都毫无用处。我投入的所有时间和我之前的团队,让我一无所获。
我让自己坐了静默两分钟,仅仅专注于呼吸,让我的头不再在我肩膀上滑动,并忽略时钟的巨大滴答声。90 分钟内能做什么——不,现在只有 88 分钟了?
我真的只有一个选择。如果它不起作用,我就会解雇某人或者申请失业救济。
早上 6:05,我开始下载 OpenBSD 安装软盘镜像,然后我抓起一台备用台式机,由于它正好在最上面,我就从众多类似的机器中选择了它。接下来的几分钟,我交替进行着敲击几个必要的安装命令和拆解那些不幸够得着的、未使用的机器,以找到两张不错的网卡。
到早上 6:33,我手里拿着两张 Intel EtherExpress 卡和一个全新的 OpenBSD 系统。我登录了足够长的时间来关闭系统,这样我就可以拧下机箱,把卡插好,然后重新启动。即使是 PF 的早期版本也包含了各种巧妙的过滤功能,但我都忽略了,转而选择了新发布的流量整形功能。到早上 6:37,我推着一辆装有显示器、键盘和我的新流量整形器的车,把它推到架子上。
然后事情变得困难起来。我没有一个额外的交换机可以处理我们的互联网带宽。路由器架子上已经挤满了东西,没有地方放置新的整形器。我花了差不多半小时才找到一根交叉线缆,而且找到的线缆只有两英尺长。当然,路由器是安装在架子顶部的。大约早上 7:10,我发现如果我把台式电脑竖放,放在一个空的运输箱上,然后把箱子放在车上,线缆刚好可以够到路由器。我把所有东西堆起来,确保它们可以到达,然后开始重新布线和重新配置子网。
我模糊地记得我的经理大约在 7:15 进来,带着紧张而平静的语气问他是否可以帮忙。如果我记得正确的话,当我疯狂地在路由器控制台打字时,我说,“是的。走开。”
早上 7:28,我们在托管区域和我们的路由器之间有一个 OpenBSD 流量整形器。所有客户端应用都可以从互联网访问。我瘫坐在椅子上,茫然地盯着墙壁。
虽然一切似乎都在正常工作,但真正的证明将在于我们受影响的网站开始其日常业务时会发生什么。我紧张地看着那个客户端的网络流量逐渐逼近表示问题的红线。流量增长到接近危险线,然后突然停止。其他客户端打电话来,很高兴他们的服务恢复了正常质量。有一个客户抱怨他的网站仍然很慢,但后来发现带宽问题掩盖了他应用程序的问题。客户说,他的网站现在运行得甚至比以前还要慢,我们提出如果他们同意付费,我们可以提供更多的带宽。
稍后,我有了两个新的路由器和新的 DS3。架子再次变得干净。那台破旧的台式机被两个配置为实时故障转移的 OpenBSD 盒子所取代,保护了我们的大名商业防火墙以及流量整形。现在,我储备了各种长度的交叉线缆。
如果我从 OpenBSD 开始,我会有一个更好的夜晚。


浙公网安备 33010602011771号