Linux-系统管理高级教程-全-

Linux 系统管理高级教程(全)

原文:Pro linux system administration

协议:CC BY-NC-SA 4.0

一、Linux 简介

您决定学习更多关于系统管理的知识,或者在自由开放源码(FOSS)基础设施上运营您的业务?祝贺并欢迎来到 Linux 和开源软件的世界!本章将带您完成实现该基础设施的最初步骤。我们包括选择平台或发行版、选择合适的受支持的硬件,以及找到您需要的软件。我们还为您提供一些资源的位置,以帮助您支持您的 Linux 环境。然后,在第二章,我们将向您展示如何安装您的第一台 Linux 主机。

Linux 发行版

什么是 Linux 发行版?简单来说,它是运行在 Linux 内核上的应用程序、包、管理和特性的集合。内核是所有发行版的共同点(有时由发行版维护者定制),但它们的核心都运行 Linux。

Note

你会问,内核是什么?别慌,我们会告诉你的。内核是所有计算机操作系统的核心,通常是允许操作系统与计算机硬件交互的层。内核包含允许您使用硬盘驱动器、网卡、RAM 和其他硬件组件的软件。在 Linux 世界中,内核是基于最初由 Linux 创始人、芬兰开发人员 Linus Torvalds 开发的代码。内核现在由一个开源开发者社区维护,并且变化会经历一个软件生命周期过程。您的发行版将附带该内核的一个版本,像 Windows 或其他操作系统一样,它可以更新和升级,以提供新功能或修复错误。

Linux 发行版的世界初看起来可能有点混乱。你可能在想,“如果都是‘Linux’,为什么会有这么多不同的名字,我选哪个?”你可能听说过像 Red Hat、Fedora、Debian 这样的名字,以及更奇怪的名字 Ubuntu(这是一个祖鲁语单词,大致翻译为“对他人的人性”!).在这一节中,我们将解释什么是发行版,描述发行版的不同之处,并建议一些为您选择正确发行版的策略。

发行版在几个方面有所不同,其中三个最重要的是

  • 目的
  • 配置和包装
  • 支持模型

首先,不同的发行版通常设计用于不同的目的,并提供不同的用户体验。一些发行版被设计成服务器,另一些被设计成桌面,还有一些被设计成执行特定的功能,例如,作为嵌入式系统。大多数 Linux 安装仍然倾向于服务器。虽然越来越多的 Linux 桌面出现,但这些数字还不足以挑战 Windows 和苹果 OS X 在桌面市场的主导地位。

发行版之间的第二个主要区别是它们的配置。虽然有些发行版将所有配置设置和文件保存在相同的位置,但其他发行版会改变它们的位置。此外,安装和更新应用程序(通常由软件包安装)的过程在不同的发行版中并不一致。许多发行版使用不同的应用程序安装和管理工具(通常称为包管理工具)。如果您的环境中有不同的发行版,这可能会造成混乱,并使管理变得困难。在第十九章中,我们将仔细研究配置管理工具以及如何克服这些问题。

第三个区别是发行版也有不同的支持模型。有些,像 Debian、CentOS 和 Fedora,是由志愿者社区维护的。其他的,像 Red Hat Enterprise Linux 和 Ubuntu,由商业供应商维护和支持。该软件仍然是开源的,但你可以支付支持和维护费用。大多数商业 Linux 供应商通过销售维护和支持服务来支持自己。

让我们看看一些可用的选择;这不是一个全面的列表,但是我们将涵盖大多数主要的流行发行版,然后给出一些选择特定平台的理由。我们还将把一些相似的发行版组合在一起,特别关注从两个主要发行版派生的发行版:CentOS(从 Red Hat 发行版派生)和 Ubuntu(本身从 Debian 发行版派生)。

Note

那么一个分布如何从另一个分布“派生”出来呢?开源软件意味着开发者可以获得源代码。开发人员可以在发行版中挑选他们想要的特性,并有可能创建他们自己的发行版。许多主要发行版的出现是因为一个开发人员或一组开发人员决定创建他们自己版本的另一个发行版。这些新的衍生产品通常有自己的品牌和特色。一些保持接近父发行版,而另一些遵循它们自己的路径。

红帽企业版 Linux

红帽企业 Linux ( www.redhat.com/rhel/ ))是一个流行的商业支持的 Linux 平台。它有许多版本,最常见的两个是红帽企业 Linux(也称为 RHEL)和红帽企业 Linux 高级平台(RHELAP)。两个版本之间的主要区别是支持的 CPU(中央处理器)数量,RHEL 最多支持两个 CPU,而 RHELAP 支持的数量没有限制。

Red Hat 平台通常被企业组织用作服务器平台,因为供应商提供专门的支持和服务级别。Red Hat 和大多数基于它的发行版都使用 Red Hat Package Management (RPM)打包系统。

在撰写本文时,RHEL 的基本支持费用约为每年 350 美元,特优支持费用高达 1,500 美元。其更高级的同类产品 RHELAP 每年的成本从 1,500 美元到 2,500 多美元不等,具体取决于硬件架构和所需的支持级别。这些费用为您提供技术支持以及发行版所需的任何补丁或更新。

Red Hat 过去也是由志愿者社区管理的,直到分发对于商业组织的技术基础设施变得如此重要,以至于人们乐于为有保证的支持付费。他们最初的志愿者团体仍然作为 Fedora 项目存在。

CentOS

CentOS ( www.centos.org/ )是红帽企业 Linux 平台的衍生。基于相同的源代码,它是免费的(并且没有 Red Hat 的支持)。希望利用 Red Hat 平台及其稳定性而无需支付额外支持的人通常会使用它。它采用与 Red Hat 产品相同的打包系统、RPM 和许多相同的管理工具。这是我们将在这本书的版本中使用的发行版本之一。

Fedora 项目

Fedora 项目( http://fedoraproject.org/ )是由社区和红帽共同运营的发行版。它是 Red Hat Enterprise Linux 的衍生产品,为该产品提供了一个向前开发的平台。Fedora 由 Red Hat 赞助,是 Red Hat 许多新功能的试验场。因此,它偶尔会被一些人认为对于商业用途来说过于前卫。Fedora 中引入的许多特性经常会出现在新的 RHEL 版本中。Fedora 还利用了 RPM 包和许多 RHEL 使用的管理工具。

Debian Linux

Debian Linux 发行版( www.debian.org )是一个由社区开发和管理的免费发行版,拥有一群多元化的活跃的开发者和用户。它始于 1993 年,围绕着一个社会契约( www.debian.org/social_contract )而建立。Debian 发行版致力于自由、开放,并专注于提供用户想要的东西。

Debian 发行版以 dpkg 打包系统和该发行版的近 23,000 个应用程序和工具的可用性而闻名。

人的本质

由南非技术专家和企业家马克·舒托沃尔斯发起的 Ubuntu 操作系统( www.ubuntu.com/ )是免费的,基于 Debian Linux 平台。它是由社区开发的,每六个月发布一次升级。其协调组织 Canonical 以及第三方支持提供商也提供商业支持。它有不同的风格,可以用作台式机或服务器。一些专家认为 Ubuntu 无处不在的特性和稳定性预示着 Linux 作为桌面平台的使用会越来越多。许多人认为 Ubuntu 是最容易使用和理解的 Linux 平台之一,它的大部分开发都是为了易用性和良好的用户体验。Ubuntu 使用 Debian 的打包系统和一些管理工具。

巴布亚企鹅

Gentoo 发行版( www.gentoo.org/ )是另一个社区开发的平台。值得注意的是,它提供了在硬件上从源代码编译整个发行版的选项。这允许您定制每个选项以适合您的特定硬件组合,但可能需要相当长的时间才能完成。Gentoo 也可以以预编译的形式安装,供那些不希望编译所有内容的技术水平较低的人使用。Gentoo 还因其经常被用作 MythTV 的平台而闻名,MythTV 是一种类似于微软媒体中心的开源媒体中心应用程序。Gentoo 使用了平台特有的打包系统 Portage。

Tip

你可以在 DistroWatch ( http://distrowatch.com/ )上了解 Linux 世界中无数可用的发行版。

那么应该选择哪个发行版呢?

选择特定的发行版应该基于您组织的预算、技能和需求。然而,我们的广泛建议是,你要么选择 Red Hat 衍生的发行版,要么选择 Ubuntu(基于 Debian 的发行版)或 Debian。所有这些都得到维护它们的组织和社区的支持。

Tip

你会在网上找到一个有用的非科学的自动测试来选择一个合适的 Linux 发行版,可以在 www.proprofs.com/quiz-school/story.php?title=which-linux-distribution-are-you-1 找到,在 http://lifehacker.com/5889950/how-to-find-the-perfect-linux-distribution-for-you 找到一篇关于这个主题的文章。

除了 Red Hat Enterprise Linux 需要签订支持合同来接收更新和补丁之外,我们讨论的所有发行版都是免费的。您可以下载并安装它们,无需支付许可费。

Note

你可以免费获得 Red Hat Enterprise Linux 软件,并且无需支付许可证就可以安装它——唯一的麻烦是,如果没有支持协议,你将无法获得任何更新,这可能会给你留下一个漏洞百出、不安全的主机。

我们讨论过的几个发行版都有商业支持,如果你的技术能力不强,值得考虑这样的发行版,比如 Red Hat Enterprise Linux 或者 Ubuntu(由他们的协调公司 Canonical 提供支持)。您还应该记住,当地提供商可能会提供技术支持。例如,许多信息技术(IT)公司和系统集成商提供 Linux 支持,而且 IT 支持行业中经常有中小型公司也可以提供相关的支持服务。

如果您不想为第三方或供应商提供的商业技术支持付费,您可能希望从大量以其大型活跃社区而闻名的发行版中进行选择,您可以在这些社区中找到支持和帮助。尤其是 Ubuntu 支持资源在最近几年有所增长,这是因为许多 Linux 新手已经采用了这个发行版。

最后,不要对自己的亲身经历打折扣。自己探索发行版。试用 LiveCDs,安装一些发行版,感受一下各种管理工具和界面。不应该低估你自己对哪个发行版适合你以及哪个发行版最容易使用的感觉。

那么这本书涵盖了哪些发行版呢?

正如我们所讨论的,两个流行的选择是 Red Hat,或者像 CentOS 和 Fedora 这样的衍生品,以及 Ubuntu 和其他相关发行版。我们选择了 Red Hat 衍生的发行版和 Ubuntu,一个 Debian 衍生的发行版。我们选择这些是因为它们代表了两个主要的分布族。它们还允许我们展示主要的配置选项和风格、包管理工具以及大量可用的 Linux 发行版所使用的相关管理技术。

具体来说,本书涵盖了在上实现应用程序和工具所需的材料

  • Red Hat Enterprise Linux 或基于 Red Hat 的发行版,如 CentOS 或 Fedora
  • Ubuntu 或其他基于 Debian 的发行版

在提供具体示例时,我们选择使用 CentOS 7 和 Ubuntu LTS XenialXerus (16.04)进行演示。

Note

LTS 是“长期支持”的缩写 Ubuntu 项目每六个月更新一次服务器和桌面版本。Ubuntu 项目保证 LTS 版本在发布后的五年内得到支持(例如,修复错误和安全问题)。Red Hat 也有类似的发布周期,Red Hat 试图在发布周期内保持二进制兼容性(这意味着它不会在发布周期内改变主要的包)。Red Hat 还有一个 ELS(扩展生命周期支持)。更多信息,请参见 https://wiki.ubuntu.com/LTShttps://access.redhat.com/support/policy/updates/errata

每章将提供每个发行版的配置示例,并记录发行版之间的任何差异,如配置文件的位置或包的名称。

挑选硬件

关于选择合适硬件的详细分析超出了本书的范围。我们通常建议您购买具有足够可靠性和支持的硬件,以满足您组织的要求。如果您需要 24/7/365 全天候依赖您的基础架构并要求高可用性,您应该购买具有冗余功能的硬件,如备用电源。您还应该购买适当的支持功能,例如备件和现场、电话或在线支持。

当然,购买自己的硬件可能不是实现服务的经济有效的方式。根据您的需求,云提供商可能是您更好的选择。您可以从 Rackspace ( www.rackspace.com )或 Linode ( www.linode.com )等提供商处购买专用或虚拟服务器。如果你想要完全虚拟化的服务器,你还可以看看亚马逊云服务( https://aws.amazon.com )或谷歌计算引擎( https://cloud.google.com/compute ),甚至像数字海洋( www.digitalocean.com )这样的简单服务

Note

选择云还是购买自己的硬件取决于多种因素。服务器放在您的办公室或数据中心有什么真正的原因吗?您是否有足够的互联网带宽和稳定性来将您的服务放入云中。根据您的需求,您始终可以选择内部硬件和 PaaS(平台即服务)的组合。

支持的硬件

除了购买合适的硬件,您还应该考虑一些重要的选择和性能因素。最重要的考虑是,Linux 操作系统并不支持所有的硬件。虽然很少,但一些硬件组件(例如,一些无线网卡)在一些或所有 Linux 平台上缺乏驱动程序和支持。

您应该确认您购买的任何硬件都受到您选择的发行版的支持。大多数发行版都有硬件兼容性列表(HCl ),您可以用它来验证您的硬件是否受支持。以下是一些目前维护的 HCL 站点:

也有很多大型硬件厂商为系统提供 OEM(原始设备制造商)Linux 软件。您可以从戴尔、惠普和 IBM 等公司中进行选择,以提供保证与指定的受支持 Linux 发行版列表一起工作的硬件。

Note

在后面的章节中,当我们研究特定的应用程序和工具时,我们将讨论各种特定的性能问题。

获取软件

您从哪里开始安装第一台主机?首先,你需要得到你需要的软件的拷贝。有多种方法可以获得基本操作系统软件。有些发行版出售 CD-r om 和 DVD,其他发行版提供 ISO 映像下载(有些发行版两者都提供!).其他发行版也提供通过网络或互联网的安装。

Note

我们将在第十九章中探讨基于网络的自动化服务器供应流程。

以下是一些可以获得 CD-r om 和 DVD 的网站列表:

下载所需软件后,您可以将 ISO 刻录到 CD、DVD 或 USB 上。以下是一些软件的链接以及如何操作的说明:

或者,如果您已经有可用的介质,您可以在第二章开始安装。

获得支持

根据发行版的不同,为您的 Linux 发行版寻找帮助和支持会有很大的不同。如果您选择了商业发行版,您可以联系您的供应商以获得您需要的支持。对于非商业发行版,您可以在您的发行版网站上记录票证或查看文档。

此外,永远不要低估搜索引擎为你的问题找到解决方案的能力。世界上许多人都在使用 Linux,他们可能也经历过和你一样的问题,并发表或撰写了解决方案。

对于特定的发行版,以下网站最有用:

查看其他发行版的站点,了解它们的支持机制。其他有用的网站包括

摘要

在这一章中,我们已经向您介绍了 Linux 的一些变种,包括本书关注的两个发行版:

  • CentOS
  • 人的本质

我们还讨论了选择特定发行版的一些原因,如何选择一些合适的硬件,以及从哪里获得对您选择的发行版的一些基本支持。在下一章,我们将向您展示如何安装本书涵盖的两个发行版。

二、安装 Linux

在本章中,我们将带您完成安装 CentOS 主机和 Ubuntu Server 主机的过程。我们将使用图形化安装工具展示每个发行版的安装过程,并详细说明安装过程中可用的选项。我们将执行基本安装,并安装运行基本 web、邮件和 DNS(域名系统)服务器所需的软件包。如果你目前不知道这些功能是什么,不要担心——我们在第十章解释 DNS,在第十一章解释 web 服务器,在第十二章解释邮件服务器。

Tip

我们建议您阅读整个章节,包括涉及 CentOS 和 Ubuntu 安装过程的部分,以获得对安装 Linux 主机的最佳理解。

在“CentOS 服务器安装”一节中,我们将从安装基于 Red Hat 的发行版开始。虽然本节中的屏幕截图是特定于 CentOS 的,但 Red Hat Enterprise Linux (RHEL)和 Fedora 的安装过程以非常相似的方式运行(CentOS 和 RHEL 源自 Fedora)。因此,如果您选择了这些发行版中的任何一个,您应该能够从我们的解释中很容易地识别出这些发行版的安装过程。你会发现大多数 Red Hat 衍生发行版的配置和管理都是如此。

如果你选择了 Ubuntu,你会在“Ubuntu 安装”一节中找到关于 Ubuntu 安装过程的完整解释。Ubuntu 源自 Debian,但安装过程不同。不过,配置和选项非常一致,通过遵循 Ubuntu 安装过程,您应该能够识别 Debian 和其他 Debian 衍生发行版的安装过程。

Note

如果您希望使用基于 CD /DVD /USB 的安装过程和所提供的图形安装程序,那么您需要在一台配有显示器、键盘(最好是鼠标)的主机上安装。这些外围设备将允许您有效地与安装工具进行交互。我们将在第十九章描述如何进行无人值守或无头(无监控器)安装。

我们还将在第八章中扩展潜在的安装选项,当我们考虑在 Linux 上安装软件时,以及在第十九章中,当我们检查自动化安装和构建的方法时。

Caution

发行版会改变,安装屏幕和选项也会随之改变。如果本章提供的屏幕截图与您在安装过程中看到的不完全一致,请不要惊慌。通常,不同版本之间的大多数安装选项和步骤都是相似的。

LiveCDs 和虚拟机

在我们开始第一次安装之前,我们将介绍在主机上了解 Linux 的另外两个选项,在提交构建物理服务器之前尝试这两个选项可能是有用的:LiveCDs 和 virtual machines。这些方法允许您探索 Linux 发行版,以及如何以最少的时间和基础设施投资来使用它。

livecds

LiveCDs 是可以从 CD 或 DVD 在计算机上运行的发行版本。它们无需在你的电脑上安装任何软件就能把自己载入内存。这意味着您可以在您的计算机上尝试一个发行版,然后移除 CD 并重新启动以返回到您现有的操作系统,这使得探索和测试 Linux 发行版和软件变得非常容易,而无需更改您计算机上的任何内容。你可以在 http://en.wikipedia.org/wiki/Live_CD 找到更多关于 LiveCDs 的信息。

您可以在 LiveCD 格式中找到如下流行的发行版:

您还可以在 www.livecdlist.com/ 找到众多 LiveCDs 的完整列表。

虚拟计算机

您还可以在虚拟机上运行您的 Linux 发行版。虚拟机是主机的软件实现,就像物理主机一样运行。您可以在单个物理主机上运行多个虚拟主机。虚拟化应用程序和服务器的示例包括 VMware ( www.vmware.com/ )、VirtualBox ( www.virtualbox.org/ )以及像 Xen ( www.xen.org/ )等开源替代产品。你也可以从主机公司购买虚拟主机。

Note

在第三章,我们演示如何安装 VirtualBox。以下说明适用于“裸机”或虚拟主机。裸机安装和虚拟安装之间的差异相对较小。虚拟主机的一个不同之处是,您可以直接从 ISO 映像安装主机,而不必先将 ISO 映像刻录到 CD /DVD /USB,然后将其加载到 CD /DVD 驱动器或 USB 驱动器中。虚拟主机安装还使构建和重建主机变得更加容易,并且您可以执行诸如创建不同类型主机的时间点备份之类的功能。当第一次探索 Linux 时,虚拟主机是学习的最佳方式。

您可能还希望利用预制的虚拟设备,这些虚拟设备是您随虚拟化软件一起加载的 Linux 发行版的虚拟映像。它们已经安装和配置好了,而且这些设备通常是为特定目的而创建的,比如 VoIP 服务器、文件服务器或邮件服务器。您可以在以下站点查看可用设备列表:

CentOS 服务器安装

让我们从安装 CentOS 主机开始。我们将在这里做一些假设:

  • 您正在使用 CentOS 网站( www.centos.org/download/ )上的 CentOS ISO,并将其刻录到 CD/DVD/USB 上。
  • 您只是在构建一个基本的邮件、DNS(域名系统)和 web 服务器。
  • 您正在没有任何先前操作系统的全新服务器上安装。

首先,将您的安装介质(通常是 CD、DVD 或 USB)放入主机并打开电源。

Note

如果您正在构建一个虚拟机,您应该从原始 ISO 开始构建。虚拟机通常包括一个“虚拟 DVD”,您可以在其中挂载安装 ISO 以进行引导。我们将在下一章向您展示这一点。

加载安装介质并启动主机后,您将看到 CentOS 安装闪屏,如图 2-1 所示。

A185439_2_En_2_Fig1_HTML.jpg

图 2-1。

The CentOS 7 installation splash screen

从闪屏中,您可以通过图形界面启动安装。如果我们不选择任何东西,自动引导将开始安装过程。只需在此处按 Enter 键即可开始安装。

按 Tab 键将允许您访问引导命令行。在这里,您可以添加其他您认为适合传递给将要引导的内核的引导选项,如网络 kickstart 文件或其他选项。在本书的后面,我们将展示这些选项的实际应用。

最后,进入“故障排除”部分将提供更多选项,如救援 CentOS 系统、内存测试和从本地驱动器启动。救援模式假设您已经加载了 Linux,它允许您引导并可能修复或救援损坏的 Linux 安装。您将引导进入一个救援提示符,允许您挂载磁盘、编辑配置文件和访问其他有用的实用程序。但是现在,只需按 Enter 键就可以进入安装的下一个阶段。

在图 2-2 中,将安装主机的“anaconda”安装程序进程已经启动。anaconda 应用程序是安装 CentOS 的软件,它运行在 X Window 系统中,也称为 X。X 是 Linux 上常用的图形用户界面;我们将在第四章中对此进行更多的讨论。有一个基于文本的安装模式,允许您进行以下选择。内存非常少的系统可能必须在此模式下运行才能安装 CentOS。这也可以通过称为 Kickstart 的过程来实现,这是一种自动选择安装的方式。我们在第十九章中讨论 Kickstart。

A185439_2_En_2_Fig2_HTML.jpg

图 2-2。

The CentOS graphical installer

在图 2-2 中,我们选择想要使用的语言。我们正在把英语本地化为澳大利亚版本,伙计。选择继续进入下一个屏幕。

Tip

发行说明告诉您这个版本和上一个版本之间有什么变化。如果您要升级主机,最好阅读并理解发行说明中记录的任何更改的含义。CentOS 7 的发布说明可以在这里找到: https://wiki.centos.org/Manuals/ReleaseNotes/CentOS7

接下来,在图 2-3 中,我们看到了主安装摘要屏幕。

A185439_2_En_2_Fig3_HTML.jpg

图 2-3。

Main Installation Summary screen

在这里,我们可以访问主要的配置项目。它们分为几个部分:本地化、安全性、软件和系统。将鼠标悬停在其中一个项目上,会显示相关屏幕的链接。

Note

有些高级配置设置有时需要先配置网络设置(如日期和时间设置中的 NTP 服务器设置),这一点并不明显。要配置网络设置,您必须首先完成安装目标。因此,我们将从安装目的地开始,而不是从图 2-3 的顶部开始,然后在继续其他步骤之前配置网络。

如上所述,我们不是从列表的顶部开始,然后逐步向下,而是从底部开始,然后向上!让我们首先从选择安装目的地开始,来设置我们将要安装操作系统的位置。

Caution

接下来的几步可能会很危险。如果您在具有现有操作系统或重要数据的主机上安装,您可能会丢失所有现有数据,并且操作系统可能会变得不可用。请谨慎行事,必要时采取必要的后备措施。对于那些刚刚开始使用 Linux 的人,在第三章中,我们将向您介绍 VirtualBox,它是一个虚拟化应用程序,允许您在当前的操作系统中安装 Linux 服务器。执行以下步骤时,使用 VirtualBox 等虚拟化软件不会破坏您当前的操作系统。

在选择如图 2-3 所示的安装目标屏幕后,我们将看到当前的磁盘布局。现在我们需要对我们的磁盘进行分区。

对磁盘进行分区就像切蛋糕一样:你可以根据吃蛋糕的人的胃口来选择每块蛋糕应该有多大。例如,如果您的系统有一个网站,并且该网站有大量的日志记录,您可以选择划分磁盘,以便在保存 web 数据和日志文件的分区中有更多的空间。如果您运行的是文件服务器,您将为用户数据而不是 web 数据或日志保留更多的磁盘空间。我们使用分区而不是将所有数据集中到一个分区中的原因是,您可以完全填充该分区,而不会破坏它周围的其他分区。在前面的场景中,如果我们用数据将日志分区填充到 100%,我们的系统分区和 web 数据分区将不会被日志数据占用。

在图 2-4 中,我们有一个来自 VirtualBox 安装的简单磁盘布局。根据您的系统,您可能有更多的磁盘。

A185439_2_En_2_Fig4_HTML.jpg

图 2-4。

Automatically configuring and encrypting disks

在图 2-4 中,我们选择了 ATA VBOX 硬盘并加密了我的数据。我们还选择了自动配置分区。安装程序将为您配置一个合理的默认磁盘分区布局。如果您选择我将配置分区,您将必须手动创建磁盘布局。

Note

我们将在第九章解释更多关于分区以及如何定制和改变你的磁盘和存储器。

在有意义的时候加密数据是明智的,但是它有一些你应该知道的缺点。每当系统启动时,您都需要在系统启动时在控制台上输入密码。这意味着无人值守重启是不可能的,您需要控制台访问(物理或虚拟)才能进入。另一个问题是安装的自动化更加困难,因为密码必须在安装时传递,并且该密码可能会被拦截。此外,这只是静态加密,在系统未开机或未输入密码时有效。假设您只有一个磁盘,我们无法将该磁盘安装到另一个系统上并读取其内容。但是,如果我们可以访问已输入加密密码的开机主机,我们就可以读取并可能复制硬盘的内容。它也只和你的密码一样安全。简单的密码可以通过基于字典的攻击来破解您的加密。因此,强烈建议使用长而强的密码。

Note

什么时候加密你的磁盘“有意义”?那得看情况。您可能有法规要求加密您的数据。您的系统可能不经常重启,并且包含敏感数据。你可以在办公室之间运送磁盘。

如果它妨碍了你的工作流程,降低了工作效率,那就没有意义了。最后,安全性是风险和可用性之间的平衡,很难判断界限在哪里。

熟悉我们在这里使用的加密技术也是一个好主意。以下链接将提供更多背景信息。

Tip

对于任何移动设备(如笔记本电脑、USB 闪存盘和运送到其他地方的硬盘)或未经授权的人可以实际访问的系统(如不安全的数据中心、贸易展览或其他活动),加密硬盘几乎是强制性的。

记住所有这些,在图 2-4 中选择完成后,图 2-5 要求我们输入加密磁盘的密码。

A185439_2_En_2_Fig5_HTML.jpg

图 2-5。

Entering a strong passphrase

输入强密码,并将其安全地存储在加密的密码管理器中。

Tip

您可以使用基于浏览器的著名加密密码管理器,如 1Password、LastPass 或 DashLane。其中一些具有组或团队支持,允许您以授权的方式在团队成员之间共享密码。相信我,这其实是一个很难的问题。在某些时候,您需要在团队中共享密码,共享的帐户和长期密码可能会成为您的一个大问题。但是,可能有一些密码需要更严格的访问和非常有限的共享。像 http://keepass.info 或 GPG 这样的软件安全地存储加密的密码文件可能是你的最佳解决方案。

在图 2-6 中,如果您的磁盘上有将要更改的数据,您将看到以下屏幕:

A185439_2_En_2_Fig6_HTML.jpg

图 2-6。

Destroying and recreating disk layout

此时,根据您看到的内容,您可以选择继续。但是请记住,这是一个不可逆的操作,数据可能会丢失。如果您有疑问,请取消并返回自定义分区。

如果现有数据没有变化,您将会看到图 2-8 。然而,我们想在图 2-7 中向你展示幕后的一点。为此,我们选择了“我将配置分区”,然后选择“单击此处自动创建分区”。现在它向我们展示了它是如何自动分割我们的磁盘的。

A185439_2_En_2_Fig8_HTML.jpg

图 2-8。

Selecting Network & Host Name

A185439_2_En_2_Fig7_HTML.jpg

图 2-7。

How automatic partitioning divides the disks

在图 2-7 中,我们将磁盘分成三个部分。一个/boot分区,一个/或根卷,以及一个swap卷。/ boot分区是我们的内核映像和引导配置文件所在的位置,因此被设置为“标准分区”和一个xfs文件系统。根卷/是一个 LVM(逻辑卷管理)卷,分配给它的空间占 LVM 磁盘大小的百分比最大。主要操作系统程序在/中。最后,swap是一片磁盘,用于写出内存页面以释放系统 RAM。

Note

LVM 允许您管理磁盘卷,并动态扩展、收缩和更改磁盘分区的大小。LVM 软件允许管理员更改磁盘布局、添加新的磁盘存储,或者移除磁盘存储并将其重新用于系统的另一部分,而不必重建系统和重新格式化底层磁盘。Btrfs 也是可用的,它是一个新的 CoW(写时复制)文件系统。像 LVM 一样,它允许你以灵活的方式对你的储物进行分组。但是 Btrfs“子卷”不同于 LVM 中的逻辑卷,它具有 LVM 没有的特性和限制。我们将在第九章更详细地讨论 LVM 和 Btrfs。

如果您有任何特殊需求,您可以改变这个默认结构并创建新的分区,或者您可以删除所有内容并使用安装程序的分区管理器重新开始。我们将在第九章更详细地讨论如何将一个或几个驱动器分割成不同的用途。

同样,在以前版本的安装过程中,您可以通过在安装时提供密码来保护 GRUB2 引导加载程序。这仍可通过 Kickstart 获得,但在此过程中不可用。这显然可以在安装后按照下面的说明进行补救: https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/System_Administrators_Guide/sec-GRUB_2_Password_Protection.html

Caution

重申一下,Linux 主机的物理安全非常重要——您不希望任何人窃取您昂贵的物理资产(以及您的数据!).您应该将主机存放在上锁的机柜或机架中,或者存放在您可以控制进入的房间中。如果您将服务器托管在 co-lo 或数据中心,那么您应该确保该位置有适当的物理安全控制来保护您的主机。

在图 2-8 中,我们可以看到,一旦我们的安装目标任务完成,网络&主机名就会出现。

这里最重要的配置项是您的 IP(互联网协议)地址,它是您的主机的网络地址,允许其他主机找到它并与之通信。

Tip

您可以在 http://en.wikipedia.org/wiki/IP_addresshttp://computer.howstuffworks.com/internet/basics/question549.htm 查看 IP 地址和寻址。

根据您的网络设置,接下来的步骤有两种方式。您的网络中可能已经有了 DHCP(动态主机配置协议)服务器。DHCP 服务器是一种将 IP 地址分配给 MAC 地址(网卡上特殊且唯一的地址)的服务。现在,大多数办公室和家庭都在防火墙中安装了 DHCP 服务器,或者在网络中安装了路由器。如果您跟随 VirtualBox,VirtualBox 有自己的 DHCP 服务器,它为自己的内部网络分配地址。在图 2-9 中,我们依靠 DHCP 为我们提供 IP 地址,并手动为我们的主机分配一个名称。

A185439_2_En_2_Fig9_HTML.jpg

图 2-9。

DHCP and manually assigning a hostname

在图 2-9 中,我们允许 DHCP 通过将右上角的 OFF 按钮切换到 on 来分配 IP 地址 10.0.2.15。

在左下方的主机名部分,我们输入了主机名 au-mel-centos-1。在小型创业公司和家庭网络中,以最喜爱的电视角色、乐队或神话生物命名主机是很常见的。虽然这很有趣,但当您在多个地理位置有多台主机在做特定的工作时,这很快就会变得很烦人。另外,你已经使用了所有你最喜欢的角色,当你不得不杀死你的宠物服务器时,这变得非常困难。我们的主机名是 au-mel-centos-1,因为我们更喜欢描述性命名标准 region-city-OS type-number。作为描述性命名格式的另一个示例,如果您在美国有一个 IP 地址以 155 结尾的文件服务器,您可以选择 us-ny-fileserver-155。最重要的是描述性的,而不是将你的主人命名为“凯蒂”或“索尔”

Note

您可以选择任何适合您的环境的命名标准。我们更喜欢描述性的命名约定。

如果我们将自己的 IP 地址分配给这台服务器,我们将采取以下额外的步骤。通常有两种方法在您的网络上分配 IP 地址。第一种是在配置过程中直接指定每台主机的 IP 地址。这些被称为静态地址。第二种方法使用 DHCP 网络服务。当主机请求 IP 地址时,DHCP 使用网络上的服务器为主机分配 IP 地址。

Note

我们将在第十章告诉你如何配置你自己的 DHCP 服务器。

DHCP 服务器跟踪这些地址,并确保没有冲突。您的网络上可能已经有一个支持 DHCP 的路由器(大多数 ADSL 调制解调器/路由器都是如此)。在下一个屏幕中,您可以选择 DHCP 选项并自动获得一个 IP 地址。

Caution

将您自己的 IP 地址添加到您的网络中可能会导致“冲突”也就是说,网络中出现了两台地址相同的主机,导致无法确定向何处发送流量。

在网络屏幕上,我们点击配置,我们会看到一个类似于图 2-10 的屏幕。我们选择了 IPv4(互联网协议版本 4)设置和方法下拉列表。

A185439_2_En_2_Fig10_HTML.jpg

图 2-10。

Setting IPv4 settings

连接名称是我们正在配置的设备,它是在系统启动时自动创建的。根据您的设备,您的可能会有所不同。下拉菜单允许我们为网络界面选择不同的选项。DHCP 我们已经讨论过,仅链路本地仅用于与本地网段(通常不使用)中的主机通信,因为它与其他计算机共享。为您的网络接口选择这两个选项将意味着您将不会被分配一个可以路由到 Internet 的 IP 地址。我们希望选择手动,并通过单击右侧的添加来分配我们自己的 IP 地址。

A185439_2_En_2_Fig11_HTML.jpg

图 2-11。

Default gateway

在确保 IP 地址未被我们的网络占用后,我们输入了 IPv4 地址 192.168.1.150。我们已经添加了网络掩码/24 (192.168.1.0/24 ),我们的网关正在侦听 192.168.1.254。

Note

如果您在本地网络上发出命令 ping 192.168.1.150 并得到响应,则该地址已经被占用,您应该在这里选择一个不同的地址。此外,地址空间 192.168.1.0/24 可能不适合您的网络。我们将在第七章中详细讨论网络。如有疑问,请尝试 DHCP。

接下来,我们添加我们的 DNS 详细信息,如图 2-12 所示。

A185439_2_En_2_Fig12_HTML.jpg

图 2-12。

DNS details

在我们的例子中,我们的 DNS 服务器也是我们的网关地址。您的网络上可能有一个或多个 DNS 服务器,您可以在此添加它们,用逗号分隔。搜索域也是 DNS 设置,允许您将域名附加到 DNS 搜索查询中。例如,当您查询 au-mel-centos-2 的 IP 地址时,您的网络层会知道为您将域 au-mel-centos-2.example.com 添加到查询中。同样,您可以在这里用逗号分隔多个域,也可以留空。我们将把我们的设置为 example.com,以匹配我们的虚拟网络。

默认网关是所有流量离开网络之前经过的路由。它可以是调制解调器/ADSL 网关,也可以是将您的网络连接到互联网或其他专用网络的物理路由器。主要和辅助 DNS 名称服务器是将 IP 地址解析为完全合格的域名的特殊服务器。例如,完全合格的域名是 au-mel-centos-1.example.com。

Tip

指定主 DNS 服务器和辅助 DNS 服务器会增加网络的冗余。如果一台服务器没有响应,您的主机将尝试另一台服务器。

每当您的主机访问某个网站时,它都会使用默认网关和 DNS 服务器来查找如何到达该网站。例如,如果您在浏览器的地址栏中键入 www.google.com ,您的主机将首先找到 DNS 服务器,它可能在您的网络上,也可能不在您的网络上。如果它不在您的网络上,您的主机将使用默认网关到达它。然后你的主机会向 DNS 服务器询问 www.google.com 的 IP 地址,DNS 服务器会用类似 74.125.19.104 的东西来回答。然后,您的主机将再次使用您的默认网关离开您的网络,并获取由 www.google.com 提供的网页。一般来说,您的 DNS 服务器是 IP 地址到主机名的映射,您的默认网关是您在网络之间穿越的第一个链接。

现在点击保存,弹出如下确认画面,如图 2-13 。如果您满意,可以选择完成。

A185439_2_En_2_Fig13_HTML.jpg

图 2-13。

Network confirmation screen Note

我们已经向您展示了如何配置 IPv4 IP 地址。还有另一种可以配置的地址,即 IPv6 地址。如果您有 IPv6,您的地址可以使用 IPv6 设置选项卡进行配置。您可以像配置 IPv4 一样配置 IPv6。您设置的 DNS 服务器也需要是 IPv6 地址。

IPV4 AND IPV6

互联网的工作原理是计算机能够相互发送信息。为了能够做到这一点,需要沿着路径在彼此之间有一组唯一的地址。IPv4 是该协议的第一个版本,使这成为可能。

IPv4 中的唯一地址基于 32 位地址空间。这意味着可能有 4,294,967,296 个唯一地址。这个地址空间对于今天的互联网来说太小了,因此 IPv6 的发布解决了这个问题。

IPv6 基于 128 位地址空间,有 2 个 128 唯一地址,比 IPv4 多 7.9×10 28 个地址。一些网络已经迁移到这个新的地址空间。

但是,如果您没有 IPv6 地址,没有必要惊慌。许多 ISP 提供“双栈”,向客户提供 IPv4 和 IPv6,同时他们自己通过 IPv6 连接到其他网络。您的本地网络很可能也是 IPv4 网络,并且您的 DHCP 服务器很可能会为您分配一个动态 IPv4 地址。我们将在第七章中详细讨论网络。

关于 IPv4 和 IPv6 的区别,请看这里: www.networkworld.com/article/2692482/ipv6/infographic-ipv4-vs-ipv6.html

这里也可以参考这个词条: http://en.wikipedia.org/wiki/IPv6

配置好网络后,我们可以继续进行其他一些设置,如日期和时间。通过选择如图 2-14 所示,我们将本地化我们的数据&时间。

A185439_2_En_2_Fig14_HTML.jpg

图 2-14。

Setting Date & Time

设置正确的时区对于服务器非常重要,因为它会影响日志中的时间戳以及数据库中的创建和更新记录。Linux 内核使用协调世界时(UTC)作为它的时钟源,但是您可以在这里设置您合适的时区。您还可以考虑将所有服务器设置为 UTC,而不是本地时区。

Why Set All Your Servers to UTC?

假设您正在为一个全新的应用程序构建基础架构。大多数人在开始设计他们的服务时没有考虑到全球化的世界。您的系统开始时可能处于您当地的时区,但是随着您的发展,您可能会发现您的系统处于完全不同的时区。这可能会导致问题,当你读一个时间戳,想知道,“是啊,但凌晨 4 点是他们的时间,我们的时间还是 UTC?”

您仍然可以获得本地化的时间信息。您可以在应用程序级别使用标准 UTC 时区偏移量来帮助本地化不同时区的时间。

当然,你可能只是建立了一个本地的文件共享网络,它永远不会覆盖全球。在这种情况下,使用当地时区以外的任何东西都没有意义。将您的时区设置为您的本地时区的另一个非常重要的原因是,您的数据已经设置了该时区,将您的服务器设置为 UTC 将导致您已经拥有的日期字段出现灾难性问题。

Note

不,你不是第一个说“呃?UTC?为什么不剪?”好吧,下面是你的答案: www.timeanddate.com/time/utc-abbreviation.html

在图 2-15 中,我们将向你展示我们将如何设定墨尔本的时间。请记住,如果我们尚未建立网络,您仍可以完成这一部分,但下一部分将会失败。这在屏幕底部突出显示,但可能会让您感到困惑。

A185439_2_En_2_Fig15_HTML.jpg

图 2-15。

Setting time zone and localizing to Melbourne

但是我们真的应该设置为 UTC,不是吗?所以在图 2-16 中,这就是我们将要做的。

A185439_2_En_2_Fig16_HTML.jpg

图 2-16。

Setting to UTC

完成后,我们可以进入下一步,即设置我们的网络时间服务器。

Note

网络时间是一项帮助您的计算机系统时间与世界上其他时间服务器保持同步的服务。随着时间的推移,计算机的系统时钟将“漂移”失去同步。因此,为了解决这个问题,网络时间服务或 NTP 服务(网络时间协议)将查询时间服务器,并找出它们的时钟。然后,它根据最佳的三个响应调整自己的时钟。我们将在第十章中详细介绍 NTP。

当您的网络设置好后,您将能够打开网络时间,它看起来将如图 2-17 所示。

A185439_2_En_2_Fig17_HTML.jpg

图 2-17。

Network Time settings

单击右上角的设置选项,您可以看到弹出了一个新窗口,允许我们添加自己的 NTP 服务器。

A185439_2_En_2_Fig18_HTML.jpg

图 2-18。

Network Time Servers

在这里你可以添加一个本地时间服务器或者选择你喜欢的。你离时间服务器越近越好,因为距离和延迟会影响你的时钟结果。带有绿色勾号的 NTP 服务器可以访问并且正在工作。红色时间服务器不可达。最好至少有三台工作时间服务器。您可以添加自己的 NTP 服务器,方法是单击+并将您的服务器添加到列表中。要返回主屏幕,选择确定和完成。

在摘要屏幕中,我们可以看到安全策略设置。如果选择安全策略,您可以向主机添加安全策略。实施安全策略并不是强制性的,但政府法规或个人安全要求可能会要求您这样做。本节的任务是安装工具,根据预定义的安全策略对您的系统执行安全内容自动化协议(SCAP)扫描和完整性检查。

你应该选择哪个?它们本身并不能让您的系统更加安全,但是它们可以帮助您识别、报告和修复任何漏洞。例如,当您管理数千台服务器时,您希望确保所有主机都符合相同的安全配置文件,并能够向外部审计员证明这一点。

例如,考虑一个非常简单的例子,如果法律要求您的所有主机符合特定的安全标准,该标准规定您的密码长度必须至少为 8 个字符(2 个大写字母,2 个符号),通过运行这些扫描,您可以确定您的网络上没有不符合该要求的主机。然后,您将需要一些方法来补救那些不符合您的需求的离群值。

如果法规遵从性对您的组织很重要,那么您至少应该阅读以下内容:

当然,您还应该阅读您的组织的任何相关标准遵从性文档,并采取适当的措施。

出于本次安装的目的,我们将安装“标准系统安全配置文件”,如图 2-19 所示。这为我们提供了以下最低规则,并安装了 oscap 工具和软件:

A185439_2_En_2_Fig19_HTML.jpg

图 2-19。

Selecting Security Profile

http://static.open-scap.org/ssg-guides/ssg-rhel7-guide-standard.html

一旦我们完成了系统安装,我们将能够运行一个符合性测试,并了解我们的系统与 DISA STIG 公司的 Red Hat Enterprise Linux 7 基线相比看起来如何。

Note

DISA 代表国防信息系统局,它是美国国防部的一部分,为美国机构运行信息系统。STIG 是标准的技术实施指南。open-oscap 的作用是运行和收集这些合规性报告。你可以在这里查看源代码: https://github.com/OpenSCAP

选择完成后,我们将返回安装摘要屏幕。接下来,我们将选择要安装的软件。从安装摘要中选择软件选择屏幕。

我们将选择安装一个 DNS 服务器,并使它更容易,Gnome 桌面。如果您熟悉 Linux 命令行,这是可选的。随意安装你感兴趣的其他软件。

首先,我们选择基础架构服务器,并选择 DNS 名称服务器,如图 2-20 所示

A185439_2_En_2_Fig20_HTML.jpg

图 2-20。

Selecting DNS Name server

我们之前已经讨论过 DNS 域名服务器及其功能。它们帮助计算机将 IP 地址映射到主机名。我们将从基础架构服务器列表中选择它。如您所见,该列表中还有其他服务器,如电子邮件服务器、FTP 服务器以及文件和存储服务器。这将只安装 DNS 名称服务器所需的最低要求,而不会安装 GUI(图形用户界面)。根据您的习惯,这可能会让您感觉有点不舒服,好像我们继续这样做,您将只能使用命令行,而不是一个漂亮的 GUI。

Tip

随着您对 Linux 越来越熟悉,您可能会发现您很少使用 GUI 来配置和使用 Linux。几乎所有东西都可以从命令行运行。只有当你使用像图形编辑器或集成开发环境(ide)这样的桌面应用程序时,你才真正需要使用桌面 GUI。

在图 2-21 中,我们将安装桌面。CentOS 提供了两个标准的 Linux 桌面供你选择,一个是 Gnome,另一个是 KDE。两者都有他们的粉丝,这取决于你个人的喜好。您可以安装这两个软件,并在登录前进行切换,以便选择最适合您的软件。

A185439_2_En_2_Fig21_HTML.jpg

图 2-21。

Selecting to install a server with a GUI

选择 Done,现在我们将有一个要在新服务器上安装的包的列表。

Note

包是软件应用程序所需的文件包。它们可以是二进制文件或配置文件。在第八章中,我们将解释更多关于软件包以及如何使用它们的内容。

我们现在将快速检查最后一项,安装源。如果我们想为当前媒体(CD/DVD/USB)选择不同的安装源,那么我们可以在这里添加它。您可以从网络 httpd 服务器安装 Linux,或者通过代理服务器运行您的安装。同样,您需要设置安装目的地和网络,然后才能配置任何需要网络的内容。

在图 2-22 中,你可以看到我们已经从builder.example.com主机添加了另一个安装源,它位于我们的网络上。该主机将通过http://提供 ISO 映像的副本。这意味着您不需要将所有物理介质都物理连接到您的主机。您可以选择使用最小安装盘来访问该屏幕,然后通过网络访问更大的媒体——当您使用多台服务器时,这非常有益。

A185439_2_En_2_Fig22_HTML.jpg

图 2-22。

Adding a network installation source

您还可以配置访问远程存储库所需的任何代理服务器设置。此外,您可以添加其他存储库。存储库是包的集合,通常通过本地或互联网上的http://服务器提供服务。

我们不会在此屏幕上添加任何内容,我们只是通过 Done 退出,现在我们可以开始安装了。

A185439_2_En_2_Fig23_HTML.jpg

图 2-23。

Selecting Begin Installation

选择 Begin Installation 后,您会看到一个屏幕,显示您的安装进度,并让您有机会创建一个“root”密码和一个“user”密码。在 Linux 中,root 用户是可以访问整个系统的超级用户,很像 Windows 操作系统中的 Windows 管理员。在图 2-24 中,您可以再次看到我们使用鼠标点击 ROOT 密码。

A185439_2_En_2_Fig24_HTML.jpg

图 2-24。

Selecting to create the root user Tip

我们将在章节 5 和 6 中更详细地讨论根用户。

正如我们前面所讨论的,密码应该是复杂的,由大小写字母、数字和特殊标点符号组合而成,如下所示:@!%#*.它们也应该至少有八个字符长。记住要像保存磁盘加密密码一样安全地保存它。

在图 2-25 中,当您输入密码时,会再次显示密码的强度。它也不会接受你的密码,除非你的确认匹配。完成后,选择完成。

A185439_2_En_2_Fig25_HTML.jpg

图 2-25。

Entering the root password Tip

你可以在 http://en.wikipedia.org/wiki/Password_strength 了解一个好密码的特征。

在您的主机上设置用户帐户是可选的,但建议您设置一个。在 Ubuntu 中,我们将在后面看到,你不能直接访问 root 帐户。root 帐户是一个非常强大的帐户,你不能轻易使用它。我们将在系统上创建一个用户进行初始访问。

在图 2-26 中,我们将通过首先选择用户创建来创建一个名为 JSmith 的用户。

A185439_2_En_2_Fig26_HTML.jpg

图 2-26。

Selecting User Creation

你会注意到,在图 2-27 中,当我们输入全名时,建议的用户名被创建。您可以将它更改为您希望的任何内容,但是要注意,您可能需要在以后记住这个用户是谁。在本例中,我们将让此人成为管理员。这意味着该用户将拥有与根用户相似的能力,但在执行任何根级命令之前需要确认他或她的密码。

A185439_2_En_2_Fig27_HTML.jpg

图 2-27。

Creating new user JSmith

在图 2-27 中也选择该用户需要密码的选项;有些帐户是在您不希望用户访问主机或运行需要密码的命令的情况下创建的,例如运行 web 服务器的用户。您还会注意到,在创建这个用户之前,我们需要有正确匹配的密码。您的主机上也可以有系统或服务用户。这些系统或服务用户不需要密码(因为他们永远不会登录到主机,但可能会执行类似数据库服务器的程序)。我们将在第四章和第五章中讨论更多关于用户的内容。

修复密码后,我们将单击高级,然后继续图 2-28 。

A185439_2_En_2_Fig28_HTML.jpg

图 2-28。

Advanced settings for the user JSmith

在高级设置中,我们可以配置用户的主目录、他们的用户和组 id,以及他们是什么组的成员。在本例中,他们有权访问“车轮”组。这是给予他们同等“根”访问权限的组。我们在第五章中会详细讨论这些高级设置。

这是最后一个安装步骤。选择保存更改,然后在用户屏幕中选择完成。当您执行此操作时,您的服务器已经在后台继续安装过程,安装可能已经完成或即将完成。

在图 2-29 中,我们看到了完成的安装。选择重新启动,您的系统将重新启动并准备好供您使用。

A185439_2_En_2_Fig29_HTML.jpg

图 2-29。

Completed installation ready for Reboot

您的系统将开始重新启动,并向您呈现 GRUB2 (GRand Unified Bootloader)引导加载程序。引导加载程序是实际加载操作系统的一小段代码。这允许您选择要引导到的操作系统,或者引导到您可能已经安装的其他操作系统。如果您在此倒计时过程中按下任何键,加载将会中断。

按照图 2-30 中屏幕上的指示,您可以选择您希望启动的内核映像或改变它的加载方式(按 e 键)。你可以通过按 c 键访问 GRUB2 Shell。我们在第六章中讨论了更多关于引导加载程序的过程。如果您在上面突出显示的 CentOS Linux 上按 Enter 键,您将被引导到该内核。

A185439_2_En_2_Fig30_HTML.jpg

图 2-30。

Booting your new host Note

我们在这里互换使用“内核”和“操作系统”这两个词。在这方面它意味着同样的事情,内核是 Linux 操作系统。CentOS 是 Linux 的发行版或版本。

当系统通过引导加载程序时,我们需要输入磁盘加密密码,如图 2-31 所示。

A185439_2_En_2_Fig31_HTML.jpg

图 2-31。

Disk encryption password

如果我们不知道密码,我们就只能走到这里,如果您忘记了密码,您将不得不重新开始。成功输入密码后,我们的系统现在开始从我们的磁盘加载信息,我们的系统继续启动。

现在,根据你在安装过程中是否选择了 Gnome 包,你会看到一个 EULA(最终用户许可协议)出现。老实说,这有点令人困惑,它是 gnome-initial-setup 包的一部分。这是你应该尝试通过它的顺序。

A185439_2_En_2_Fig32_HTML.jpg

图 2-32。

Gnome Desktop EULA

这是" 1 "," 2,"和" c "的记录。如果你没有安装桌面系统,你就不用安装了,你可以继续。假设您安装了桌面,您现在将看到桌面登录屏幕。

在图 2-33 中,输入我们的 JSmith 用户密码。输入密码后,您将看到以下桌面设置屏幕。

A185439_2_En_2_Fig33_HTML.jpg

图 2-33。

Installation is complete and the system is ready to use

在图 2-34 中,我们选择本地化的桌面语言,并在右上角选择下一步。

A185439_2_En_2_Fig34_HTML.jpg

图 2-34。

Selecting localized desktop language

现在我们选择图 2-35 中的键盘布局。我们用的是美式键盘。

A185439_2_En_2_Fig35_HTML.jpg

图 2-35。

Selecting keyboard layout

在图 2-36 中,我们可以登录谷歌,访问谷歌应用程序和存储、ownCloud 甚至 Windows Live。这只有在你运行桌面或者允许通过这个服务器访问这些云存储服务的情况下才真正有用。除非你打算使用这些服务,否则你应该跳过。

A185439_2_En_2_Fig36_HTML.jpg

图 2-36。

You can sign into your favorite cloud provider or you can skip

在图 2-37 中,你可以看到一个谷歌登录的例子,但是如果你不愿意,你可以不注册任何一个。

A185439_2_En_2_Fig37_HTML.jpg

图 2-37。

An example of Google sign in

图 2-38 将我们带到桌面配置部分的最后。接下来,我们将快速向您展示如何访问终端控制台。

A185439_2_En_2_Fig38_HTML.jpg

图 2-38。

The end of the desktop configuration !

如图 2-39 所示,很容易找到应用程序和我们要找的终端应用程序。单击应用程序、实用程序,然后向下滚动到终端。

A185439_2_En_2_Fig40_HTML.jpg

图 2-40。

Your first terminal session

A185439_2_En_2_Fig39_HTML.jpg

图 2-39。

To find the applications, click Applications

我们已经学完了 CentOS 安装部分。你可以进入下一章,我们将向你介绍 Linux 以及如何开始使用你的新主机。或者,你可以继续阅读本章来学习如何安装 Ubuntu。

Ubuntu 安装

Ubuntu 有两种版本:桌面版和服务器版。在本节中,我们将演示如何安装服务器版本。安装 Ubuntu Server 的过程与安装 CentOS 服务器非常相似。主要概念是相同的:选择语言和键盘布局,选择您想要的磁盘分区方式,然后选择您想要安装的软件包。

为了安装 Ubuntu,我们将从 Ubuntu 网站下载一个 ISO 文件,其中包含了我们完成安装所需的大部分数据。在本练习中,我们将使用来自 www.ubuntu.com/download/server 的全尺寸 ISO。

Note

Ubuntu 和 Debian 很好地利用了网络安装程序,提供了安装的灵活性。网络安装程序是操作系统的一个小版本,通常以 ISO 文件的形式提供,您可以将其刻录到 CD/USB 驱动器上并从中启动。它包含一个简单的内核和发行版的安装程序。net installer 为您的主机提供了引导和启动安装过程所需的基础知识,然后可以从在线存储库中下载任何其他软件或应用程序。这意味着您需要连接到互联网来安装新的主机。这也意味着在慢速互联网连接上安装一个完整的 4GB 操作系统可能需要很长时间,但是使用网络安装程序是加载较小系统的一个好方法。我们将在第十九章中进一步探讨网络安装,届时我们将探讨供应多个系统的方法。

我们将在这里做一些假设:

将 CD 放入 CD 驱动器或插入 USB 并打开主机电源后,您将看到的第一个屏幕允许您选择安装过程的语言(图 2-41 )。

A185439_2_En_2_Fig41_HTML.jpg

图 2-41。

Choose the installer language

然后你会看到一个闪屏(见图 2-42 ),给出几个选项。我们将选择安装 Ubuntu 服务器,但你也会看到一些其他新的选项。Ubuntu 有一个产品叫做 MAAS——或者金属即服务。这是一项针对数据中心规模安装的“零接触”部署服务。只要接通电源,MAAS 就可以安装数千台服务器,而不是只安装一台。我们将在第十九章中讨论这种供应。您还可以看到,我们可以运行一些系统检查或启动进入救援模式。在第九章中,我们将详细讨论系统恢复。

A185439_2_En_2_Fig42_HTML.jpg

图 2-42。

First installation splash screen

选择安装 Ubuntu 服务器后,我们会看到图 2-43 ,为您的安装选择语言。你应该选择适合你的语言。

A185439_2_En_2_Fig43_HTML.jpg

图 2-43。

Choosing the language for installation

如图 2-43 所示,我们选择了英语。这也将是最终系统的默认语言。

然后会要求您选择您所在的地区。这是您要安装的服务器所在的地理位置。在图 2-44 中,我们选择了澳大利亚。这有助于安装程序选择最近的 Ubuntu 档案库,以便以后获取额外的包。

A185439_2_En_2_Fig44_HTML.jpg

图 2-44。

Selecting your region

接下来,选择键盘和键盘布局首选项。如前所述,键盘布局是您正在使用的键盘映射。不同的地区会有不同的映射,因此请选择最适合您所在地区和语言的映射。在这里选择是,如图 2-45 所示,会引出一系列的问题和答案,通过这些问题和答案,Ubuntu 试图通过让你按下不同的键来判断出你使用的是什么类型的键盘。

A185439_2_En_2_Fig45_HTML.jpg

图 2-45。

Ubuntu attempts to detect your keyboard

选择“否”以节省时间,并直接告诉安装程序您正在使用哪种键盘。这里的默认设置适用于大多数安装,但是您可以自由选择最适合您所在地区的设置。图 2-45 开始显示键盘选择的一系列截图。

选择“否”后,出现图 2-46 中的屏幕,在此选择键盘的原点。我们会选择自己,继续前进。

A185439_2_En_2_Fig46_HTML.jpg

图 2-46。

Selecting the origin of your keyboard

在图 2-47 中,我们为自己选择了键盘布局,这将为我们提供澳大利亚计算机的标准按键映射。

A185439_2_En_2_Fig47_HTML.jpg

图 2-47。

Selecting keyboard layout Tip

安装完成后,您可以随时更改键盘设置。

Ubuntu 安装现在需要休息一下来探索你的硬件并发现更多关于目标主机的信息。完成此过程后,系统会提示您输入新主机的主机名。如图 2-48 所示,我们在这里输入 au-mel-ubuntu-1,因为它符合 CentOS 安装一节中讨论的命名标准。

A185439_2_En_2_Fig48_HTML.jpg

图 2-48。

Setting the hostname

接下来,要求您为此主机创建一个用户。在第五章中,我们将更详细地讨论用户管理,但重要的是要知道 Ubuntu 发行版通过禁用密码来禁用 root 用户帐户。root 用户类似于 Windows 管理员,可以访问主机上的所有内容。在 Ubuntu 中,用户不用像在 CentOS 安装中那样设置 root 用户的密码,而是使用一个名为 sudo 的特殊命令来访问所有与 root 用户相同的权限。我们将在第五章中详细讨论 sudo 命令。

在图 2-49 中,输入新用户的全名。

A185439_2_En_2_Fig49_HTML.jpg

图 2-49。

Entering the full name of a new user

在下一个屏幕(图 2-50 )中,您为新用户设置用户名。

A185439_2_En_2_Fig50_HTML.jpg

图 2-50。

Entering the username

最后,你为你的用户设置密码,如图 2-51 所示。同样,正如我们在 CentOS 安装一节中所讨论的,我们建议实施一个强而复杂的密码。您将被要求验证该密码。

A185439_2_En_2_Fig51_HTML.jpg

图 2-51。

Entering the password for the new user

接下来会询问我们是否希望加密该用户的主目录。这与 CentOS 中的磁盘加密具有相同的含义。我们将在图 2-52 中选择“否”,以便在几个步骤的时间内完成完整的磁盘加密。

A185439_2_En_2_Fig52_HTML.jpg

图 2-52。

Choosing not to encrypt the home directory

我们现在被要求确认我们的时区。这里我们使用当地时区。

A185439_2_En_2_Fig53_HTML.jpg

图 2-53。

Confirming time zone

接下来,您需要对您的主机进行分区。这种分区方式与 CentOS 安装示例中描述的方式非常相似。您可以根据主机的要求将磁盘划分为不同大小的分区。同样,在 CentOS 安装中,系统会提示您选择几个默认分区选项之一,或者使用分区工具定制您自己的分区选项。

  • 引导式—使用整个磁盘:此选项要求您选择一个硬盘,该硬盘将被完全擦除。然后,系统创建一个根分区和一个交换分区。
  • 引导式—使用整个磁盘并设置 LVM:此选项还会擦除所有数据。然后,它创建一个小的引导分区,并将剩余的磁盘空间用于 LVM 的根和交换卷。
  • guided—使用整个磁盘并设置加密的 LVM:此选项与前一选项相同,只是 LVM 数据全部加密。系统会要求您提供密码。请注意,您需要在引导时输入该密码,因此该选项不适用于远程或无头服务器。如果您丢失了加密密码,您将无法检索您的数据。
  • 手动:该选项打开分区编辑器,并允许您手动配置分区、软件 raid、加密和 LVM。如果您想要调整预先安装的 Windows 的大小,应该选择这个选项。

对于我们的示例主机,我们有兴趣使用“引导—使用整个磁盘并设置加密 LVM”选项。这使用了我们可用的整个硬盘,并利用了 LVM。如“CentOS 服务器安装”一节所述,

LVM 是一种管理分区和磁盘的强大方法,并为您以后更改分区布局提供了更大的灵活性。

Note

我们将在第九章更详细地讨论 LVM。

图 2-54 显示了我们默认的分区选择。

A185439_2_En_2_Fig54_HTML.jpg

图 2-54。

Choosing encrypted LVM to partition disks

接下来,选择要执行分区的驱动器。我们只能选择一个磁盘,如图 2-55 所示。

A185439_2_En_2_Fig55_HTML.jpg

图 2-55。

Choosing the disk to partition Caution

如果磁盘已经包含分区,系统会提示您覆盖它们。如果您确信要这样做,请指定 Yes 并继续。如果您在以前的系统上安装,在此选择“是”将会销毁您可能拥有的任何现有数据。如果您不确定,请指定否。替代方法包括使用类似 GParted 的工具从某个 LiveCD 安装中对主机进行重新分区,安装在没有数据的硬盘上,或者安装在虚拟机上。

下一个屏幕(见图 2-56 )让您确认是否要将分区信息写入所选磁盘。在配置 LVM 之前,需要将分区信息写入磁盘。选择是并转到下一个屏幕。

A185439_2_En_2_Fig56_HTML.jpg

图 2-56。

Writing partition information to disk

我们现在需要添加一个密码来保护加密的数据。同样,请确保密码很长、很复杂,并且存储安全。

在图 2-57 之后,您还会被要求进行密码验证。

A185439_2_En_2_Fig57_HTML.jpg

图 2-57。

Providing the disk encryption password

接下来,系统会询问我们希望在 LVM 分区中使用多少卷组。在图 2-58 中,默认是所有可用的 LVM 分区,或 8.1G

A185439_2_En_2_Fig58_HTML.jpg

图 2-58。

LV group partitioning

我们将在第九章更好地解释 LVM,但是正如磁盘分区是将磁盘分割成多个部分一样,LVM 组是这个概念的进一步扩展。LVM 卷来自一个称为组的共享存储桶。我们可以将这个组分割成我们的卷,还可以在创建卷后使用奇妙的磁盘魔术来扩展和收缩卷。

在图 2-58 中,我们被问及希望将多少卷组用于此分区,我们将选择默认使用全部,即 8.1 GB。

现在您将看到 LVM 分区布局,它将显示少量的交换空间,其余的用于root或/分区。当我们选择“是”确认该布局时,图 2-59 所示的 LVM 分区被创建并格式化。

A185439_2_En_2_Fig59_HTML.jpg

图 2-59。

. Writing LVM partition changes to disk Note

交换空间是硬盘驱动器上的额外存储空间,用于从 RAM 中“溢出”数据。如果您发现您的主机频繁地使用所有的交换空间或频繁地交换,那么您可能需要调整您的主机,并且通常添加更多的 RAM。我们将在第九章和第十七章中更详细地讨论交换空间。

在安装的这个阶段,Ubuntu 将开始安装安装操作系统其余部分所需的基础软件包。如果您使用网络安装,这可能需要一些时间,取决于您的互联网连接。您将看到一个类似于图 2-60 所示的进度条。

A185439_2_En_2_Fig60_HTML.jpg

图 2-60。

Installing base system requirements

完成后,系统会询问我们是否需要使用代理服务器来下载完整安装所需的软件包。您可以提供包含用户名和密码的 URL(如果有的话)。我们将保留空白,如图 2-61 所示。

A185439_2_En_2_Fig61_HTML.jpg

图 2-61。

Proxy server settings

之后,我们现在被问及我们希望如何处理我们系统上的软件更新。

A185439_2_En_2_Fig62_HTML.jpg

图 2-62。

Automatically install system updates

这可能是一个有争议的问题。有些人不喜欢在他们不知情的情况下更新他们的系统,因为这会导致意想不到的结果。理想情况下,您应该随时了解您的系统的状态,并在您选择的时间部署安全或其他方面的更新。然而,由于我们是刚开始,自动安装这些是首选。

Note

有了 LTS(长期支持)系统,您可以更加确定更新只会对您的系统做出最小的更改。此外,这些变化将在一个大的部署范围内得到很好的测试。对于前沿系统,你并不总是得到同样的确定性。

然后会询问您希望通过选择应用程序组在您的主机上安装哪些应用程序。我们选择了 DNS、LAMP server (Linux、Apache、MySQL 和 PHP)、mail (Postfix)和 OpenSSH,如图 2-63 所示。准备就绪后,选择继续。

A185439_2_En_2_Fig63_HTML.jpg

图 2-63。

Selecting applications for your host

在这个特殊的安装中,由于你选择安装的应用程序,你会被问一系列的问题来帮助 Ubuntu 配置或保护你选择的应用程序。每次在 Ubuntu 上安装需要配置输入的新应用,都会提示你回答类似的问题。

正如您在图 2-64 中看到的,您首先被要求提供 MySQL 数据库 root 用户的密码。这是您的 MySQL 安装的主密码,您应该输入一个安全且复杂的密码。您将被要求再次输入该密码进行确认。

A185439_2_En_2_Fig64_HTML.jpg

图 2-64。

Setting the MySQL root password

一旦您提供了该密码,系统会要求您描述您的邮件服务器配置。图 2-65 中的屏幕显示了配置选项,并简要描述了每个选项。我们将只选择默认的 Internet 站点。

A185439_2_En_2_Fig65_HTML.jpg

图 2-65。

Configuring the mail server

我们将在第十二章中解释如何配置和保护邮件服务。选择此处的默认值将为您的域提供发送和接收邮件的基本安全配置。

接下来,您为您的邮件服务器提供域名(参见图 2-66 )。你现在应该输入主机的域名,我们将在第十二章中解释其他可能的选项。

A185439_2_En_2_Fig66_HTML.jpg

图 2-66。

Setting the mail server domain name

接下来,我们在 CentOS 安装指南中讨论了 GRUB,并讨论了它是如何加载操作系统的。在这个屏幕中,我们被要求将其安装到主引导记录中。MBR 是硬盘上的一小块空间,用于向系统描述分区布局。它还有一个安装 GRUB 引导加载程序的小空间。

Note

您可能看不到 GRUB 安装屏幕(图 2-67 )。运行 UEFI(统一可扩展固件接口)的系统不会显示下一个屏幕。我们将在第六章中讨论 BIOS(基本输入/输出系统)和 UEFI 的区别。

A185439_2_En_2_Fig67_HTML.jpg

图 2-67。

Installing the GRUB into the boot loader

在图 2-67 中,我们选择 Yes 将引导加载程序安装到 MBR 中。现在你的安装完成了,Ubuntu 会通知你,如图 2-68 所示。

A185439_2_En_2_Fig68_HTML.jpg

图 2-68。

Installation complete

如果您使用的是 CD 或 DVD,它应该会自动弹出。如果您使用的是 USB 拇指驱动器,请取出并选择继续。您的系统现在将重新启动,您将看到一个密码提示(参见图 2-69 )来解密您的硬盘。

A185439_2_En_2_Fig69_HTML.jpg

图 2-69。

Decrypting hard drives before boot

现在我们已经完成了安装。系统现在正在启动。很快你会看到如图 2-70 所示的登录控制台。

A185439_2_En_2_Fig70_HTML.jpg

图 2-70。

Booting to the console screen

你会注意到 Ubuntu 不是引导到 GUI,而是引导到控制台屏幕。这是因为默认的 Ubuntu 服务器安装不安装 GUI。我们将在第四章中更多地讨论命令行和 GUI 的使用。

现在,您已经有了一个运行 Ubuntu 发行版的可用的邮件、DNS 和 web 服务器,可以为您的环境做进一步的定制了。

解决纷争

有时安装会因为某些原因而失败。最常见的原因是安装介质有缺陷;由于不支持或有缺陷的硬件,这种情况很少发生。

如果安装介质有问题,您可能会看到记录的读取错误,或者安装程序可能会显示一个错误,说明它无法读取文件。您应该检查安装 CD 或 DVD 是否有划痕。如果您是从 ISO 文件创建的 CD 或 DVD,那么以较低的速度刻录新盘可能是值得的。介质问题通常在安装过程的同一个步骤中重复出现。

如果连接中断,网络安装也可能会失败,因此请检查电缆是否插好,互联网连接是否正常。

不太常见的故障类型是由不受支持的硬件引起的。例如,如果安装内核不支持磁盘控制器,安装程序将无法访问硬盘。如果发生这种情况,请检查安装盘上包含的内核版本,并验证它确实支持您的硬件。您的发行版可能会有更新的版本,支持更多更新的硬件。现在这已经不太常见了,因为主要的制造商已经开始越来越多地支持 Linux,并积极地为内核贡献驱动程序。

在安装过程中随机出现的不可再现的崩溃通常表示硬件问题,最常见的问题是坏 RAM 或过热。你可以运行一个类似 memtest86 ( www.memtest.org/ ),的 RAM 测试器,你应该验证 CPU 和机箱风扇工作正常。

诊断信息

如果在安装时需要额外的诊断信息,可以从安装过程中访问有限的 shell 和一些日志记录信息。您可以使用这些来进一步诊断您可能遇到的任何问题。这些被称为虚拟控制台(也称为 tty ),我们将在第四章再次讨论它们。

在 Ubuntu 上,Alt+F2 和 Alt+F3 分别提供对有限 shell 的访问。Alt+F4 为安装程序提供详细的安装进度和日志。Alt+F1 组合键切换回安装程序界面。

OnCentOS,图形化安装在 Ctrl-Alt-F1 上运行。在 Ctrl-Alt-F2 上有一个 shell 提示符,安装消息转到 Ctrl-Alt-F3,系统消息转到 Ctrl-Alt-F4,其他消息出现在 Ctrl-Alt-F5 上。

如果安装没有按计划进行,您可以使用这些终端来收集有关您的安装的信息或查看任何可能有帮助的错误信息。

重新启动您的安装

出现问题后,通常应该从头开始重新安装。因为先前安装尝试的文件可能仍然存在于磁盘上,所以最好让安装程序重新初始化分区并从头开始。

疑难解答资源

如果遇到麻烦,不要害怕利用大多数 Linux 发行版周围的社区。很可能其他人也经历过和你一样的问题,并记录了解决方案。以下是一些可供尝试的资源:

摘要

在本章中,我们逐步完成了安装两个流行的 Linux 发行版的过程:

  • CentOS 服务器
  • Ubuntu 服务器

我们还解释了如果在安装过程中出现问题,您可以做些什么。在下一章中,我们将向您介绍一些我们用来帮助快速建立安装原型的工具。我们将在本书的其余部分更多地使用这些工具。

三、VirtualBox、Git 和 Vagrant 简介

在第二章中,我们向您展示了如何从 ISO 映像在物理服务器上安装 Linux。然而,部署 Linux 服务器还有许多其他选择——包括网络供应、部署虚拟机映像或在“云中”启动 Linux 实例现在,与本地或世界各地的其他人共享虚拟机的能力也更强了。我们将向你展示一种可能有助于你理解这本书的方法。

有些人发现刚开始使用 Linux 很难,因为有许多安装选项和选择——拥有所有这些选项我们认为是一件很棒的事情!幸运的是,有一些工具可以让研究和探索 Linux 变得更加容易。我们想借此机会向您介绍一些我们将在本书稍后使用的工具,或者您现在就可以使用它们来跟随我们,因为我们将在接下来的章节中带您了解不同的示例。对你来说,这意味着你可以在你的桌面上跟随,不管发生了什么,如果你犯了一个可怕的错误,你可以快速安全地恢复。另外,在这个过程中,您将熟悉我们使用的一些工具。

我们在原型和测试中每天使用的工具组合是 VirtualBox、vagger 和 Git(在我们试验和测试时跟踪我们配置的变化)。能够在不污染本地主机或其他服务器的情况下构建完整系统的原型是一个真正的好处。能够试用不同类型的操作系统并能够运行快速有效的测试来验证您的工作应该是所有管理员的目标。

一种既便宜又快速地构建服务器的方法是使用虚拟化。虚拟化有许多不同的形式,首先是 KVM、Xen 和 LXC 容器。在这一章中,我们将向您展示我们日常在桌面上进行原型设计和测试的工具——无论是 Linux 服务器、Mac 还是 Windows 主机。可以在 Windows 机器、Mac 和 Linux 上使用的一种虚拟化技术是 VirtualBox。出于这个原因,VirtualBox 是一个很好的入门级虚拟化平台,但它不是一个用于提高性能的平台。但是,如果您来自 Windows 和 Mac 世界,它允许您进一步探索像 Docker 这样的 LXC 容器软件,这需要运行 Linux 内核。

VirtualBox 还允许你在桌面上运行许多不同的操作系统。这样就省去了双启动不同操作系统的时间和麻烦(也就是说你必须关闭一个操作系统才能启动另一个)。它还允许您共享资源,比如客户操作系统和桌面之间的文件系统。

Git 是计算机软件的版本源控制技术。它非常擅长跟踪对基于文本的文件(如配置文件)的更改。与 GitLab、GitHub 或 BitBucket 等资源相结合,这是一种以开放和协作的方式管理组织变革的有效方式。使用 Git,您的配置更改可以与当前配置进行比较,并且您可以让其他管理员能够同意这些更改。您还可以使用 Git 构建测试管道,在部署到生产环境之前测试系统的变更。

虽然您可以使用 VirtualBox GUI 或 VirtualBox CLI 来创建、安装和管理您的 VirtualBox 计算机,但我们将向您介绍一款名为 vagger 的工具。vagger 是 VirtualBox CLI 命令的“包装器”,但它的功能远不止于此。它有基于文本的配置文件,适合跟踪 Git 中的变化。这有助于您原型化和开发主机构建或应用程序。它允许集成许多配置管理工具,如 Puppet、Ansible 或 SaltStack。但是一个主要的好处是你可以和其他开发者共享图片,或者“盒子”和构建指令。这有助于您的团队在一组一致的映像上进行开发,这些映像可以在桌面商用硬件上运行,并且易于创建、重新创建、开发和制作。

系统管理员发现这种快速原型非常有用。在过去,要查看一个新的软件包做了什么,或者操作系统的新更新会做什么,您可能需要找到一些备用服务器并运行新软件。有了 VirtualBox 和 vagger,你可以更快地完成这项工作,而且不会损害你的本地桌面。在本书的后面,我们将使用配置管理工具 Puppet 和 Ansible 来部署我们的服务器配置,我们将使用 VirtualBox、Later 和 Git 的组合来实现和跟踪这一点。

共享和下载 Vagrant 盒子或共享 VirtualBox 机器的能力意味着你可以快速集成或试用不同的技术。例如,您的应用程序中可能需要 memcached 服务。就您的应用程序而言,要用您的应用程序测试 memcached,您只需要 IP(互联网协议)地址和一个到达它的端口。与其为此构建另一个服务器,您可以下载一个由您团队中的其他人创建的带有完全配置的 memcached 服务的 travel box,或者从在线 travel box 存储库中下载一个公共可用的 box。从开发的角度来看,这有助于更快的原型和构建。如果您的应用程序与 memcached 配合得很好,那么您可以在以后的阶段为您的应用程序开发、测试和部署一个合适的 memcached 服务。

有关虚拟化技术的一般信息,请参见以下内容:

VirtualBox 安装

VirtualBox 非常容易安装。有 Windows、Mac、Linux 甚至 Solaris(对于不了解的人来说是 Unix 操作系统)的自安装包。只需进入 www.virtualbox.org/wiki/Downloads ,为您的操作系统选择合适的下载。您应该可以轻松安装 VirtualBox。如果你遇到麻烦,你可能会发现以下 PDF 文件的第二章很有帮助: http://download.virtualbox.org/virtualbox/5.0.14/UserManual.pdf

批准

VirtualBox 是 Oracle 拥有的一个开源虚拟化平台。它是在 GNU 通用公共许可证(GPL)版本 2 许可下发布的。该许可证的全部细节可在 www.gnu.org/licenses/old-licenses/gpl-2.0.html 找到。

如果您还希望使用 VirtualBox Oracle VM VirtualBox 扩展包,您需要同意 VirtualBox 个人使用和评估许可证的条款(参见 www.virtualbox.org/wiki/VirtualBox_PUEL )。本许可证仅允许用于个人或产品评估或学术用途。如果您打算将本产品用于商业用途,请遵守这些许可。

创建新的 VirtualBox 计算机

在我们启动新的 VirtualBox 机器之前,首先确保您已经下载了适用于 CentOS 7 的 ISO 映像文件。可以去 www.CentOS.org/download 取。有三个选项供您下载。DVD ISO 和完整的 ISO 将需要更长的时间来下载,但将包含一切你将需要一个桌面环境。如果你选择最小的 ISO,你的下载会更快,但是当你需要的时候,你必须从网上下载额外的软件包。

在本例中,我们从以下下载链接安装 CentOS:http://isoredirect.CentOS.org/CentOS/7/isos/x86_64/CentOS-7-x86_64-Minimal-1511.iso

VirtualBox 应用程序像桌面上的任何其他应用程序一样运行。找到应用程序并启动它。您将看到一个与此类似的屏幕。

A185439_2_En_3_Fig1_HTML.jpg

图 3-1。

VirtualBox console

全新安装不会安装任何虚拟机。要创建我们自己的 CentOS 安装,我们选择 New 按钮。在出现的窗口中,我们将为机器命名为 Centos7,并选择 Linux 和 Red Hat (64 位)的类型和版本。

A185439_2_En_3_Fig2_HTML.jpg

图 3-2。

Naming and setting VirtualBox type

接下来,我们可以配置内存大小。这可以是系统允许的最大值。一般来说,1024Mb 是合适的,或者 512Mb 用于没有图形桌面之类东西的较小系统。您也可以在安装后根据需要进行调整。使用滑块或键入系统所需的内存量。

Note

值得注意的是,您可以“过量使用”内存(即,将超过主机系统可用的内存分配给多个虚拟机),这意味着如果我的主机系统有 8GB RAM,我们可以有一个 2GB、一个 4GB 和一个 6GB 的虚拟机,它们都可以同时正常运行。如果三个虚拟机都使用全部分配的内存,我们就无法同时运行所有这三个虚拟机,或者至少会让主机爬行。在这种情况下,我们也许可以同时运行 2GB 和 4GB 的主机。

A185439_2_En_3_Fig3_HTML.jpg

图 3-3。

Memory allocation

我们现在被询问是否希望创建一个硬盘。在这里,您可以选择使用现有磁盘,也可以选择不使用磁盘。我们将单击“立即创建虚拟硬盘”,然后单击“继续”。

A185439_2_En_3_Fig4_HTML.jpg

图 3-4。

Choosing disk

我们可以选择下一步要选择的虚拟磁盘格式。VirtualBox 给了我们六种选择。我们将选择默认值 VDI (VirtualBox 磁盘映像) ,然后继续。

A185439_2_En_3_Fig5_HTML.jpg

图 3-5。

Selecting Virtual Disk format Note

有许多不同类型的虚拟磁盘格式。VirtualBox 拥有自己的 VDI 格式,并支持 VMWare 的 VMDK(虚拟机磁盘)、微软的 VHD(虚拟硬盘)、Mac OSX 的 HDD (Parallels 硬盘)、QEMU QED (QEMU 增强型磁盘)和 QCOW (QEMU 写入时复制)。这些文件系统都在磁盘文件上(意味着“磁盘”实际上只是文件系统上的文件,可以像任何其他文件一样被复制和移动)。这意味着,您可以创建一个具有特定格式的磁盘,如 VHD,然后将该映像文件移动到另一台主机并将其安装在那里。

我们现在可以选择磁盘是固定大小还是动态分配。简而言之,我们可以创建一个磁盘来分配我们所请求的全部磁盘大小,或者我们现在可以只分配其中的一小部分,当我们需要它时,VirtualBox 会分配更多的空间。动态分配的好处是,您可以过度分配磁盘(这意味着您可以向虚拟主机分配比主机上实际更多的磁盘空间)。缺点是会影响性能,因为 VirtualBox 会在需要时分配空间。一旦分配了磁盘,就不能将其释放回虚拟主机中的可用空间。但是,因为这些虚拟磁盘只是您主机上的文件,所以我们可以将它们移动到其他存储设备上,或者在我们的主机空间开始耗尽时清理它们。

我们将选择动态分配并继续。

A185439_2_En_3_Fig6_HTML.jpg

图 3-6。

Dynamically allocating disk

默认磁盘大小为 8GB。我们将选择此项,您可以使用滑块或键入您希望虚拟主机使用的磁盘数量。如果您愿意,您也可以在这里通过选择小文件夹图标来更改磁盘的名称和位置。

A185439_2_En_3_Fig7_HTML.jpg

图 3-7。

Disk amount

这就创建了虚拟主机 Centos7。

A185439_2_En_3_Fig8_HTML.jpg

图 3-8。

Centos7 VM is almost ready

我们可以更改新虚拟主机的设置或添加更多存储设备。我们需要添加一个存储设备来启动我们的操作系统。因此,我们将选择“Settings”并转到“storage”选项卡。你可以看到我们有一个空的 IDE 控制器。我们可以点击小 DVD 符号并选择选择光学虚拟磁盘文件。导航到之前下载 ISO 映像的目录。选择 CentOS-7 ISO。

A185439_2_En_3_Fig9_HTML.jpg

图 3-9。

Selecting image

磁盘现在应该显示为连接到 IDE 控制器。

A185439_2_En_3_Fig10_HTML.jpg

图 3-10。

Selected CentOS ISO is ready

我们现在准备安装我们的操作系统。只需遵循第二章中的步骤。然而,让机器快速启动并运行的另一种方法是使用另一个工具,vagger。我们稍后将向您展示这一点,但首先我们想向您展示 Git(我们实际上只是想要终端模拟器,但让 Git 在您的系统上工作并无大碍)。

安装 Git

在整个开发和系统管理任务中,您会发现一个非常方便的工具是 Git。Git 是一个分布式版本控制系统,它非常擅长将您对配置文件的更改存储在不同的工作块或提交中。您可以为这些提交添加描述,以便关注您的人(并且总是有人关注您)可以查看您所做的更改,并看到描述该更改的注释。您还可以使用 Git 快速查看不同提交之间发生了什么变化。

Note

Git 来自于版本控制系统的悠久传统。过去的主要版本控制系统(VCS)是集中的,这意味着你在一个 VCS 上做所有的修改。Git 将它改为分布式的,这样您就不必再依赖“一个”服务器来为您的所有团队提供服务。这极大地帮助了分布式团队能够在非常复杂的系统(如 Linux 内核)上进行更改,并成为协作、共享代码和项目的一种很好的方式。

下载 Git 是非常无痛的。您可以在大多数(如果不是全部的话)Linux 发行版中以标准包的形式获得它,也可以访问

http://git-scm.com/download

我们将演示在 Windows 机器上的安装,因为这比在 Mac 或 Linux 上安装 Git 更不常见,并且有一些可能难以理解的选项。

Tip

这里的 Git 站点也有 Mac、Linux、Windows 的安装说明: https://git-scm.com/book/en/v1/Getting-Started-Installing-Git

开始,下载并打开安装程序后,我们会被问一些问题。这些对一些人来说可能是新的。其中之一就是在 Windows 中设置%PATH%变量。虽然这些设置由您决定,但我们建议您采取以下措施:

在图 3-11 中,我们选择允许我们在 Windows 命令提示符和 Git Bash 提示符下使用 Git 的设置。这给了我们在处理文件时一点灵活性。

A185439_2_En_3_Fig11_HTML.jpg

图 3-11。

Allow Git commands from the windows command line

下一步可能也需要一些思考。令人困惑的是,Windows 和 Linux 系统(甚至 MAC)有不同的方式来告诉计算机一行的结尾在文件的什么地方。在 Linux 中,文件中一行的结尾被称为 LF(换行),在 Windows 中被称为 CRLF(回车换行)。因此,当您对文件进行更改时,操作系统会将适当的行尾添加到文件中每一行的末尾。当您在另一个操作系统上打开这个文件时,您会在编辑器中看到奇怪的格式。我们在这里所做的是在您提交这个更改时控制 Git 做什么。Git 可以控制这些不可见的行尾应该是什么。

在图 3-12 中,我们允许 Git 将 LF 转换为 CRLF 以进行窗口编辑,但是当我们提交文件时,我们再次将 CRLF 转换为 LF。有了这个选项,您应该能够毫不费力地与 Git 控制的文件进行交互。

A185439_2_En_3_Fig12_HTML.jpg

图 3-12。

Allow Git to change LF to CRLF on checkout and CRLF to LF on commits

接下来,我们配置哪个终端模拟器用于 Git。Git Bash 是学习和与 Git 交互的好方法。它有许多可用的 Linux 命令和 Git 命令。当然,如果你觉得用 Windows 控制台更舒服,请随意选择。

A185439_2_En_3_Fig13_HTML.jpg

图 3-13。

Selecting the terminal

下一个屏幕现在不是很重要。通过选择图 3-14 中的复选框,您可以添加一些实验性的性能调整。我们不会选择它,但是如果你喜欢冒险,你可以选择。

A185439_2_En_3_Fig14_HTML.jpg

图 3-14。

Experimental performance options

去吧,普西

让我们快速浏览一下您需要知道的通用 Git 命令。Git 不同于集中式版本控制软件的地方在于它是去中心化的!这意味着 Git 存储库的每个克隆都相当于存储库的一个“主”副本——包括历史。这相当于每个开发人员的本地副本也是存储库的分布式备份。然而,大多数人使用集中托管的 Git 存储库服务,如 GitLab、GitHub 或 BitBucket,首先要做的事情之一是克隆存储库:

$ git clone git@<githost.com>:<username>/git-repository.git

上述命令将使用 SSH 协议登录到 githost.com 服务器(您也可以使用 HTTPS、Git 或本地协议)。您可以使用 SSH 公钥在 githost.com 服务器上进行身份验证,从而获得对存储库的访问权。然后,Git 将在本地文件系统的同名目录中创建 git-repository.git 存储库的副本。

一旦您有了存储库的副本,您就可以开始进行更改了。第一步是创建一个分支。分支是一组离散的提交,它们都与特定的变更相关。有一个主要分支被认为是主线或主分支(通常称为主)。分支中的更改通常会合并到主分支中。要从主分支创建新分支,请执行以下命令:

$ git branch my_change

您这样做是为了在一个命名的分支中创建包含的和离散的工作分支——而 Git 的分支很便宜(这是卖点之一!).

现在,您已经克隆、创建了一个分支,并且编辑了一些文件。您可以使用以下命令查看您对这些文件所做的更改:

$ git diff

该命令将显示存储库中的所有更改,如果您希望缩小比较的范围,可以在命令行中添加目录或文件名。但是,假设您对您的差异感到满意,并且您现在准备提交这些更改。

$ git commit –v path/to/file/or/directory

您将再次看到您的更改的差异,并且您将能够向提交添加提交消息,以便人们知道更改是什么。好的提交消息让你知道你做了什么——最好是做了什么和为什么做。错误的提交消息是“更新的文件”现在,您的分支中有一个提交,您可能希望将它合并回主分支中(如果您正在处理一个共享项目,您可能不希望直接这样做——您可能希望将您的分支“推”到您克隆的远程存储库中,并让其他人先检查它)。要合并回主服务器,请发出以下命令:

$ git checkout master
$ git pull origin master
$ git merge my_change

我们在上面所做的是,我们首先离开 my_change 分支,并检查主分支——主分支通常是从中提取生产代码的分支。我们首先在源(或者 githost.com 主存储库)上做一个“拉”来拉进任何其他已经被签入的变更。然后,将您的更改合并到主存储库中,并处理由此引起的任何冲突。一旦您对存储库的状态感到满意,您就可以“推”回原点(githost.com 存储库)。

$ git push origin master

您的更改现在可以被任何使用该存储库的 githost.com 副本的人共享。当然,这是一个非常简单而且可能很糟糕的例子。像上面这样的工作流毫无疑问会导致破坏,你应该有一种在合并到 master 之前测试代码的方法。GitHub 在这里提供了一个很好的关于 git 工作流的文档: https://guides.github.com/introduction/flow/

Note

有关 Git 的更多信息,请参见 https://git-scm.com/book/en/v2

对于 Windows 用户,现在已经安装了 Git,可以使用 Git Bash 访问有用的 Linux 实用程序,如 less、vi、grep 和 tail。我们将在下一节向您展示如何使用 Git Bash。

介绍 Vagrant

vagger 是一种在开发人员之间快速方便地共享和分发开发环境的方式。从这个意义上说,“开发环境”是一组虚拟机和运行这些虚拟机的配置。最初,它被设计为通过配置文件和命令行工具的组合来管理 VirtualBox 机器,但后来它扩展到更多方面,并支持其他虚拟化技术。它是由 Mitchell Hashimoto 开发的,他随后凭借该产品的受欢迎程度创建了 HashiCorp。它是用 Ruby 编写的,可以在任何支持 Ruby 的操作系统上运行。目前你可以下载并在 Linux、Mac OSX 和 Windows 上运行它。

安装 Vagrant

您首先可以在此下载适用于您的操作系统的软件:

www.vagrantup.com/downloads.html

Vagrant 的意思是由命令行驱动;它不提供图形用户界面(GUI)。在苹果电脑上,你可以从终端窗口操控一切。在 Windows 机器上,你可以从cmd.exe程序、PowerShell 或者我们刚刚安装的 Git Bash 运行命令。

Tip

有些事情您无法在 Windows 命令或 PowerShell 控制台中直接完成。其中一个是宋承宪。SSH 是一种通过网络访问控制台的安全方式。通过安装 Git Bash,我们可以访问 SSH 客户端。如果您不能让 SSH 工作,您应该检查 SSH 可执行文件的路径是否在您的控制台环境的路径中。Windows 中常见的地方是C:\Program Files\Git\usr\bin。使用$env:path =+'; C:\Program Files\Git\usr\bin'将其添加到您当前的 PowerShell 环境中。对于 Windows 命令提示符,set PATH=%PATH%;C:\Program Files\Git\usr\bin。您需要永久地设置它,因为它只持续您当前的控制台会话。我们会让你自己调查如何做到这一点。

批准

Vagrant 是在麻省理工学院的许可下分发的。这意味着你可以用任何你想要的方式使用这个软件。您可以在以下位置查看许可证

https://raw.githubusercontent.com/mitchellh/vagrant/master/LICENSE

流浪的概念

最初开发的时候,vagger 试图解决的问题空间是虚拟机及其配置在 VirtualBox 下运行的共享。从那时起,它扩展了对虚拟化技术的支持,但其核心租户保持不变。它通过两种方式做到这一点。它提供了分发和下载其他 VirtualBox 虚拟机的机制,以及管理它们的配置和运行的工具。

主要是 Vagrant 用盒子工作。盒子只是预建的 VirtualBox 虚拟机的压缩文件,这些虚拟机配置了一些特殊的东西。他们有一个“流浪”用户,可以访问 root 权限,OpenSSH 已经安装并正在运行。vacant 使用 VirtualBox 命令(或 VBoxManage 命令)来创建、配置、启动和停止这些机器,它通过 vacant 用户结合使用 VirtualBox CLI 和 SSH 访问来完成这些操作。你可以从 HashiCorp 的另一项名为 Atlas 的服务下载盒子,也可以自己制作。你也可以通过 Atlas 存储、分发和发布盒子。Atlas 提供私人和企业服务,并与 HashiCorp 的其他服务整合。

Note

构建你自己的 Vagrant 盒子有点复杂,但是当你更加熟悉 Linux 的时候,你应该尝试一下。当你准备好了,请点击这里查看详情 www.vagrantup.com/docs/boxes/base.html

您也不仅限于使用 VirtualBox 机器。你可以运行亚马逊 AWS 或者 Openstack 实例,LXC 容器,当然还有 Docker 容器。在本练习中,我们将向您展示如何使用 VirtualBox 虚拟机。你可以在闲暇时自由探索他人。

How Vagrant Integrates with Virtualbox

在本练习中,我们将初始化一个浮动配置文件,对该文件进行一些编辑,然后使用浮动 CLI 启动该 VirtualBox 虚拟机。但是这一切是如何运作的呢?

嗯,当你下载一个流浪盒,它基本上是一个压缩的 tarball(类似于压缩文件)的 VirtualBox 虚拟机,包括虚拟磁盘映像。Vagrant 命令将把这些盒子存储在你的主目录的适当的.vagrant.d/boxes目录中。

virtualbox/
total 1029016
drwxr-xr-x  6   dennismatotek  staff             204 12 Dec 16:44 .
drwxr-xr-x  3   dennismatotek  staff             102 12 Dec 16:44 ..
-rw-r--r--  1   dennismatotek  staff             505 12 Dec 16:44 Vagrantfile
-rw-------  1   dennismatotek  staff       526833152 12 Dec 16:44 box-disk1.vmdk
-rw-------  1   dennismatotek  staff           10589 12 Dec 16:44 box.ovf
-rw-r--r--  1   dennismatotek  staff              25 12 Dec 16:44 metadata.json

当您第一次发出 vagrant up 命令时,正如我们马上要做的,variant 将下载指定的 box,存储在一个类似命名的目录中,然后使用 VBoxManage import 命令将下载的 VirtualBox 机器(box.ovf)导入到 VirtualBox 本身。

然后,VirtualBox 将为该虚拟机分配一个 UUID(通用唯一标识符),然后,vagger 可以使用该标识符发出 VBoxManage startvm —headless 命令。类似地,当调用一个 vagrant halt 时,vacant 将使用 vbox manage control VMpower off 命令来停止虚拟机。

vagger 还会做一些事情,比如设置网络端口转发(用于 SSH 访问和其他网络服务,如果配置的话)和在 virtualbox 虚拟机中挂载文件系统(除非您配置为不这样做)。

这些例子是特定于 VirtualBox 虚拟机的——但是,通过发出所需的命令或 API(应用程序编程接口)调用来管理它们,vagger 支持其他虚拟化和基于云的实例。这意味着我们可以使用同一个 lavour up 命令来启动 VirtualBox 虚拟机或 AWS 实例——但是当然要对配置文件(lavour file)进行适当的更改。

流浪汉入门

安装后,您需要做的第一件事是打开一个终端,或者通过cmd.exe、PowerShell 提示符或 Git Bash 提示符进入命令提示符。转到您最喜欢的工作区,创建一个名为 first 的新目录或文件夹。使用cd命令进入新目录。

无论您使用哪种操作系统,命令的输出都是相同的。发出以下命令:

> vagrant
Usage: vagrant [options] <command> [<args>]

   -v, --version                    Print the version and exit.
   -h, --help                       Print this help.

Common commands:
    box             manages boxes: installation, removal, etc.
    connect         connect to a remotely shared Vagrant environment
    destroy         stops and deletes all traces of the vagrant machine
    global-status   outputs status Vagrant environments for this user
    halt            stops the vagrant machine
    help            shows the help for a subcommand
    init            initializes a new Vagrant environment by creating a Vagrantfile
    login           log in to HashiCorp's Atlas
    package         packages a running vagrant environment into a box
    plugin          manages plugins: install, uninstall, update, etc.
    port            displays information about guest port mappings
    powershell      connects to machine via powershell remoting
    provision       provisions the vagrant machine
    push            deploys code in this environment to a configured destination
    rdp             connects to machine via RDP
    reload          restarts vagrant machine, loads new Vagrantfile configuration
    resume          resume a suspended vagrant machine
    share           share your Vagrant environment with anyone in the world
    snapshot        manages snapshots: saving, restoring, etc.
    ssh             connects to machine via SSH
    ssh-config      outputs OpenSSH valid configuration to connect to the machine
    status          outputs status of the vagrant machine
    suspend         suspends the machine
    up              starts and provisions the vagrant environment
    vbguest
    version         prints current and latest Vagrant version

For help on any individual command run `vagrant COMMAND -h`

Additional subcommands

are available, but are either more advanced
or not commonly used. To see all subcommands, run the command
`vagrant list-commands`.

Listing 3-1.Vagrant Options

这将向您显示可用的子命令的完整列表。我们将只向您展示四个,以帮助您入门。我们计划向您展示的任务是

  • 初始化第一个目录
  • 更新 Vagrant 的配置文件
  • 启动我们的流浪盒子
  • 检查状态
  • 最后,SSH 进入控制台

我们需要做的第一项任务是初始化我们的第一个目录。发出如下命令,vagrant init如清单 3-2 所示。

> vagrant init
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
Listing 3-2.Vagrant init

Vagrant 初始化命令只是把一个文件放在你的目录中。这是我们用来配置第一个 Vagrant 图像的文件。如果我们使用ls命令列出我们目录的内容,我们将会看到如下内容(对于 Windows cmd.exe,您可能需要发出dir)。

> ls
Vagrantfile
Listing 3-3.Showing the Vagrantfile

在图 3-3 中,我们有一个名为 Vagrant 文件的文件,这个文件包含了vagrant命令用来创建 Vagrant 主机的配置信息。如果我们查看该文件的内容,它看起来非常像 Ruby 代码。那是因为它是。这个文件使用了 Ruby 代码,在执行 vagger 命令时,vagger 解析并加载这个文件。这意味着您可以在文件中使用 Ruby 语言来创建列表、数组、散列等等。这在以后处理你的 Vagrant 文件中的多个图像时会很有用。现在用你的编辑器打开 Vagrant 文件。如果你使用 Git Bash,你可以使用 vi 程序。我们将在第四章中详细解释 vi。

流浪汉文件的内容看起来类似于清单 3-4 中所示的缩短版本。

Vagrant.configure(2) do |config|
 config.vm.box = "base"

 # config.vm.network "forwarded_port", guest: 80, host: 8080

 # config.vm.network "private_network", ip: "192.168.33.10"

 # config.vm.synced_folder "../data", "/vagrant_data"

 # config.vm.provider "virtualbox" do |vb|
 #   # Display the VirtualBox GUI when booting the machine
 #   vb.gui = true
 #
 #   # Customize the amount of memory on the VM:
 #   vb.memory = "1024"
 # end
end

Listing 3-4.
Vagrantfile

在清单 3-4 中,我们删除了很多注释和建议设置。不用深入 Ruby 语言,你就可以看到我们可以在 do 和 end 语句之间编写代码块。我们将Vagrant.configure(2)的结果传递给配置变量对象。然后我们可以用它来分配我们的 Vagrant 配置。在图 3-4 中,我们将config.vm.box设置为等于“基数”

Tip

Ruby 是一种非常灵活的编程语言。它类似于 Python 语言,另一种非常流行的强大语言。我鼓励你去 http://tryruby.org/ 看看。

因此,让我们为我们的第一个 Vagrant 形象创建一个基本配置。首先,我们可以转到以下链接,查看所有可用的框:

https://atlas.hashicorp.com/boxes/search

Note

记住一个流浪的“盒子”是一个自包含的 Linux 虚拟机,它可以在你的 VirtualBox 虚拟化软件内部运行。HashiCorp 在 Atlas 上托管的盒子是由社区成员或组织创建的,在设计和用途上千差万别。

Ubuntu 团队定期推送一个官方的 Vagrant 盒子供公众下载。我们将把它作为我们的流浪箱。这些 Vagrant 盒子是“基础”图像。你下载它们,并把它们作为你自己的盒子的基础。有很多可供你探索。

在清单 3-5 中,我们添加了ubuntu/xenial64框。当我们启动流浪主机时,流浪会自动从 Atlas 下载这个。

Vagrant.configure(2) do |config|
 config.vm.box = "ubuntu/xenial64"
end
Listing 3-5.Vagrantfile—Adding aBox

在清单 3-5 中,我们将config.vm.box设置为ubuntu/xenial64

Note

此处列出的是 64 位操作系统版本(ubuntu/xenial64)。根据您使用的系统,您可能只有 32 位操作系统。如果是这种情况,可以用ubuntu/xenial32替换上面的框来使用 32 位版本。如果您的系统不支持 64 位操作系统,将弹出一条消息:Vt-x/AMD-V 硬件加速在您的系统上不可用。这表示您的处理器没有启用适当的硬件虚拟化扩展,或者您的处理器不支持虚拟化。你可以启用这些扩展,你可以查看下面的 FAQ 帖子, https://forums.virtualbox.org/viewtopic.php?f=1&t=62339 ,寻找可能的解决方案。

接下来,我们将向您展示如何配置转发端口。转发端口允许从您的主机到 VirtualBox 内部运行的漫游主机的端口连接。在本例中,我们将主机 localhost 地址上的端口 8080 连接到来宾漫游主机的端口 80。这意味着如果我们在我们的漫游客户机上运行一个 web 服务器,我们可以打开我们的浏览器并指向http://localhost:8080,让一个 web 服务器监听我们的漫游主机上的端口 80 进行响应。

Vagrant.configure(2) do |config|
 config.vm.box = "ubuntu/xenial64"
 config.vm.network "forwarded_port", guest: 80, host: 8080
end
Listing 3-6.Vagrantfile—Adding Forwarded Ports

在清单 3-6 中,您可以看到我们通过设置config.vm.network来添加转发端口。我们还可以将文件夹从我们的主机同步到我们的来宾主机。这在开发时是非常宝贵的。默认情况下,vacator 会将当前目录挂载到 guest 的/vagrant目录中。出于本练习的兴趣,让我们将./temp目录挂载到 guest 虚拟机上的 Vagrant 用户的主目录中。

Vagrant.configure(2) do |config|
  config.vm.box = "ubuntu/xenial64"
  config.vm.network "forwarded_port", guest: 80, host: 8080
  config.vm.synced_folder "./temp", "/home/vagrant/temp"
end
Listing 3-7.Vagrantfile—Adding Synced Folder

最后,我们可以配置以下内容。在这个代码组中,我们作用于 virtualbox 提供程序。也就是运行我们机器的底层虚拟化平台。有时,启动您的主机并在启动时查看控制台上发生了什么是有用的,尤其是在学习和调试时。下面将启用 VirtualBox GUI,并将 guest 虚拟机的内存设置为 1024Mb。

Vagrant.configure(2) do |config|
  config.vm.box = "ubuntu/xenial64"
  config.vm.network "forwarded_port", guest: 80, host: 8080
  config.vm.synced_folder "./temp", "/home/vagrant/temp"
  config.vm.provider "virtualbox" do |vb|
    vb.gui = true
    vb.memory = "1024"
  end
end
Listing 3-8.Vagrantfile—Adding Synced Folder

让我们把这些都保存到我们的流浪档案中,并选出我们的第一位流浪主持人。发出清单 3-9 中所示的命令。

> vagrant up
vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
There are errors in the configuration of this machine. Please fix
the following errors and try again:

vm:
* The host path of the shared folder is missing: ./temp

Listing 3-9.Issuing vagrant up

哦看。在清单 3-9 中,我们有一个错误。Vagrant 在这里告诉我们,出了问题的“虚拟机:*共享文件夹的主机路径丢失:。/temp "。当然,我们没有创建临时文件夹。在我们启动机器之前,travel 将解析 travel file 并确保所有的东西都在适当的位置。继续在第一个目录中创建temp目录,然后再次尝试该命令(mkdir temp)。

> vagrant up
Listing 3-10.Issuing Vagrant up

在第一次“启动”时,您将看到清单 3-11 中所示的内容:

==> default: Importing base box 'ubuntu/xenial64'...
==> default: Setting the name of the VM: first_default_1455714694375_15606
==> default: Clearing any previously set forwarded ports...
==> default: Fixed port collision for 22 => 2222\. Now on port 2203.
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 80 (guest) => 8080 (host) (adapter 1)
    default: 22 (guest) => 2203 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2203
    default: SSH username: vagrant
    default: SSH auth method: private key
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
Listing 3-11.
Vagrant up Output

在清单 3-11 中,我们可以看到我们正在导入一个基础盒子(这个已经被下载了,你也可以看到下载步骤)。虚拟机的名称将采用目录 _ 框名 _ 随机数的形式。您将能够在您的 VirtualBox 控制台中查看它(并像任何其他 VirtualBox 一样更改设置)。我们可以看到网络正在建立,包括我们将用于 SSH 的端口(22 = > 2222)。有自动端口冲突检测,所以不使用端口 2222,Vagrant 选择端口 2203。接下来是 80 = > 8080 的端口转发,然后我们等待机器启动。当它启动时,我们创建一组 SSH 密钥,用于登录主机。这个密钥对的公钥被注入到机器中。最后,我们让机器启动并准备好。

==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
    default: /vagrant => /Users/dennismatotek/workspace/source/book/first
    default: /home/vagrant/temp => /Users/dennismatotek/workspace/source/book/first/temp
Listing 3-12.Final Steps of the vagrant up

一旦机器启动,我们就可以挂载我们的目录。我们有一个默认挂载,它将我们的第一个目录挂载到/vagrant中,我们有我们的临时目录,我们已经将它挂载到/home/vagrant/temp.

好了,现在我们的主机已经启动并准备好了,让我们用清单 3-13 中所示的命令检查它的状态。

> vagrant status
Current machine states:

default                   running (virtualbox)

Listing 3-13.
Vagrant Status

默认情况下,我们的主机名是 default。你可以在你的流浪文件中给我们的机器起不同的名字。在清单 3-13 中,我们可以看到我们的处于运行状态。

接下来,让我们看看我们是否可以登录到我们的流浪机器。为此,我们发出清单 3-14 中所示的命令。

> vagrant ssh
Welcome to Ubuntu 14.04.1 LTS (GNU/Linux 3.13.0-35-generic x86_64)

 * Documentation:  https://help.ubuntu.com/

  System information as of Wed Feb 17 13:11:35 UTC 2016

  System load:  0.77              Processes:           80
  Usage of /:   2.7% of 39.34GB   Users logged in:     0
  Memory usage: 12%               IP address for eth0: 10.0.2.15
  Swap usage:   0%

  Graph this data and manage this system at:
    https://landscape.canonical.com/

  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud

0 packages can be updated.
0 updates are security updates.

vagrant@vagrant-ubuntu-xenial-64:∼$

Listing 3-14.SSH to the Vagrant

Host

vagrant@vagrant-ubuntu-xenial-64:∼$我们有我们的 Linux Ubuntu 主机的命令提示符。如您所见,这是构建和运行 Linux 服务器的一种非常简单的方法。当您想要停止您的漫游主机时,只需键入 exit 退出终端会话,然后发出 flower halt。

在图 3-15 中,我们展示了运行在 Windows 主机上的 Git Bash 终端的步骤和输出。

A185439_2_En_3_Fig15_HTML.jpg

图 3-15。

Summary of actions

摘要

本章向您介绍了一些方便的虚拟化软件,它们将帮助您快速、安全地试验和使用 Linux。我们还向您展示了一些用于开发和测试服务器配置代码的工具。这也使得与他人共享您的开发环境变得更加容易。

在概述中,我们展示了以下内容:

  • 如何安装 VirtualBox
  • 如何创建虚拟主机
  • 如何安装 Git(在 Windows 上)
  • 如何安装 Vagrant
  • 如何使用 vagger 和 VirtualBox 调出虚拟主机

在下一章,我们将带你参观一台 Linux 主机。在那一章中,我们将介绍一些基本概念,比如登录、导航和一些基本的 Linux 命令。

四、Linux 基础知识

在第一章,我们谈了一点什么是 Linux,它来自哪里,在第二章,我们安装了我们的第一台 Linux 主机。在这一章中,我们将向你介绍一些基本的 Linux 概念和技能。有些人觉得 Linux 令人生畏,因为它看起来像带有奇怪开关和神秘选项的神秘命令。我们将解码一些你需要知道的神秘命令,并演示这些命令及其功能。

本章重点介绍入门、登录、本地和远程登录,以及使用和导航命令行和文件系统。我们还将介绍一些基本的 Linux 概念:用户、组、包、服务。然后,我们将深入研究文件系统,以及如何处理文件和目录,包括文件类型,如何设置权限,然后读取、编辑和管理它们。在接下来的章节中,我们将扩展这些概念,并向您介绍为了操作和管理您的 Linux 主机您需要知道的关键活动。

在这一章中,我们将主要讨论在命令行上运行的命令。这将向您介绍如何使用命令行,并帮助您熟悉如何操作它。这并不是说 Linux 没有广泛的图形管理工具。如果您更喜欢类似 Windows 的图形化环境,您仍然可以轻松有效地找到管理 Linux 主机的机制。对于我们将要向您展示的大多数命令行工具,都有一个图形等价物。然而,您会发现 Linux shell 在一个或多个 Linux 系统上运行起来更容易、更快。

Note

这一章是对 Linux 的广泛介绍。这不会让你成为专家。相反,它将为您迈出部署 Linux 基础设施的第一步做好准备。

入门指南

如果您还没有安装 Linux 主机,那么在开始安装 Linux 之前,尝试 Linux 命令的最简单方法是尝试一个 LiveCD。LiveCDs 是 CD、DVD 或 USB 上的 Linux 发行版。要使用 LiveCD,您需要下载 ISO 文件格式的图像。您可以选择任何 LiveCD 并将该图像刻录到 CD、DVD 或 USB 上。以下是一些描述如何将 ISO 文件刻录到 CD、DVD 或 USB 上的 URL:

刻录完 LiveCD 后,您可以将光盘或 USB 插入电脑并重新启动。大多数计算机会自动检测 LiveCD,并为您提供从它启动的选项。

Note

如果您的主机不提供从 CD/DVD 启动的选项,您可能需要调整 BIOS(基本输入/输出系统)设置来更改启动顺序,以便 CD、DVD 或 USB 在硬盘启动之前启动。

LiveCD 将加载并向您展示一个可运行的 Linux 发行版,您可以进行试验。默认情况下,这不会在您的主机上安装任何东西,当您取出 CD/DVD 并重新启动计算机时,您的原始桌面配置将可用。

LiveCDs 可用于各种发行版。一些可以尝试使用其 LiveCDs 的好发行版包括:

Tip

http://en.wikipedia.org/wiki/Comparison_of_Linux_LiveDistros 有一些可用的 Linux LiveCDs 的比较列表。

你也可以考虑使用 Vagrant,就像我们在第三章给你展示的那样。使用 vagger 是探索和学习 Linux 的一个很好的方法,不需要安装物理服务器,它运行在 VirtualBox 虚拟化环境中。

登录

在您的 Linux 主机或 LiveCD 启动后,您将看到一个登录提示:命令行或 GUI(图形用户界面)登录提示。

在图 4-1 中,您可以看到 Ubuntu Linux 主机的典型命令行登录提示,在图 4-2 中,您可以看到 CentOS 主机的图形化登录。

A185439_2_En_4_Fig2_HTML.jpg

图 4-2。

Graphical login prompt

A185439_2_En_4_Fig1_HTML.jpg

图 4-1。

Command-line login prompt Note

如果您的初始屏幕与这些略有不同,不要惊慌,因为不同版本之间确实会出现一些微小的变化。

在任一登录提示符下,您都需要提供您的用户名和密码或类似形式的身份验证。(和 Windows 一样,Linux 也可以使用智能卡、令牌或其他机制来验证用户。)

如果您刚刚安装了一个 Linux 主机,系统会提示您创建一个用户,现在您可以使用该用户登录了。如果您正在用 LiveCD 测试 Linux,您可能会看到一个默认的用户名和密码,系统会提示您使用该用户名和密码登录,或者您甚至可以自动登录。例如,Ubuntu LiveCD 的默认用户名为ubuntu,密码为 Ubuntu,它通常会自动让您登录。如果您没有看到默认的用户名和密码,您可能需要查看 LiveCD 的联机文档,或者系统可能会提示您创建用户名和密码。

一旦您的主机验证了您的访问权限,您就可以登录了,根据您的配置,您的主机将显示命令行或 GUI 桌面环境。

Linux 与微软视窗系统

这部分的标题可能听起来有点像我们即将呈现一场职业摔跤比赛。然而,更多的是 Linux 和微软 Windows(以下简称 Windows)之间的相似之处,而不是它们的不同之处。Windows 和 Linux 都是操作系统,虽然在许多技术方面不同,但它们有许多相同的概念。因此,我们将研究这些相似之处,以帮助您利用一些关于 Windows 的现有知识来理解 Linux 中的相关概念。

在这本书里,我们将看看如何与你的 Linux 主机接口。有两个主要的界面:GUI 桌面和命令行。我们将在本书中探索这两种接口。

图形用户界面桌面

Linux 和 Windows 都可以有图形用户界面。与最近的 Windows 版本不同,Linux 总是能够引导到 GUI 或命令行。一旦启动,你也可以在这两种模式之间切换,我们将在本章后面的“Shells”一节中讨论如何切换,在第五章中会有更详细的介绍。

在 Linux 上,GUI 是几个应用程序的组合。基本的应用程序被称为 X 窗口系统(你也会看到它被称为 X11 或简称为 X)。X 应用程序提供了一个底层的“窗口”环境。

Note

您不需要担心安装或管理 x。如果您安装了 GUI 桌面,您的发行版通常会为您安装它。如果你没有安装一个 GUI 桌面,例如,如果你正在安装一个服务器,X 将不会被安装,你通常将通过命令行界面与 Linux 交互。默认情况下不加载 GUI 的发行版的一个例子是我们在第二章安装的 Ubuntu 服务器发行版。

然后在 X 之上添加一个桌面环境来提供“外观”和桌面功能,比如工具栏、图标、按钮等等。Linux 上流行两种主要的桌面环境:Gnome 和 KDE。大多数发行版都将这些桌面环境之一作为默认环境;例如,Gnome 是 Debian、CentOS、Red Hat 和 Fedora 发行版的默认桌面环境,KDE 是 ubuntu 衍生版 Kubuntu 和 SUSE 的默认桌面环境,而 Ubuntu 使用 Unity,这是一个 Gnome shell(基于 Gnome)。

Tip

为了保持 Linux 的灵活性,您可以在所有这些发行版上更改默认的桌面环境。Unity、Gnome 和 KDE 也不是唯一的选择。有关当前可用桌面的讨论,请勾选 www.techradar.com/au/news/software/operating-systems/best-linux-desktop-which-is-ideal-for-you--1194516

在图 4-3 中,你可以看到 Ubuntu 发行版上默认的 Gnome 桌面。

A185439_2_En_4_Fig3_HTML.jpg

图 4-3。

Ubuntu Unity desktop on the Xenial Xerus release

Unity 桌面提供了许多底层的 Gnome 工具。在本书中,我们将主要讨论例子中的 Gnome 桌面,这些例子也适用于 Unity 桌面。

命令行

在 Linux 世界中,命令行是您可以使用的最强大的工具之一。命令行可以被称为“控制台”、“终端”或“Shell”;每一个都有稍微不同的上下文,但是最终它是您键入 Linux 命令的地方。在本书中,大量的重点将放在命令行上。这是您至少要执行一些管理任务的地方,理解并使用命令行非常重要。事实上,在某些情况下,您将没有可用的 GUI 环境。如果您的 GUI 环境不起作用,您将需要能够使用命令行管理您的主机。命令行还提供了一些强大的工具,可以使您的管理任务更快、更有效。

Note

这并不是说我们要忽略 GUI。我们还将向您展示如何使用 GUI 工具管理您的 Linux 主机。

让我们来看看 Linux 命令行。您可以通过几种方式之一访问命令行。如果您的主机已经引导至命令行提示符,如图 4-1 所示,您可以简单地登录并使用该提示符。

在 Gnome 或 KDE GUI 中,您有两种选择。第一种是使用虚拟控制台——一种默认运行在大多数 Linux 发行版上的 Linux 管理控制台。或者您可以启动一个终端模拟器应用程序,如 Gnome 终端或 Konsole。在图 4-3 中,你可以看到我们如何在 Unity 桌面中访问终端仿真器。

Note

终端模拟器是一种在另一个应用程序中模拟文本终端的工具。例如,当您在 Windows 中启动命令提示符或命令行 shell 时,您已经启动了一个 Windows 终端模拟器。

要从 Gnome 或 KDE GUI 中启动虚拟控制台,请使用组合键 Ctrl+Alt 和 F1 到 F7 中的一个键。每个可以打开的窗口都是一个新的虚拟控制台。有六个虚拟控制台可用。您可以使用 Crtl+Alt+F1 到 F7 键在控制台之间切换。每个终端都是独立和分离的。Ubuntu 和 CentOS 在不同的控制台上有 GUI。使用 Ctrl+Alt+F7 访问 Ubuntu GUI,使用 Ctrl+Alt+F1 访问 CentOS。

Tip

如果您没有运行 GUI 界面,虚拟控制台仍然可用,您可以使用 Alt+F1 到 F6 键以及 Alt+左箭头键和 Alt+右箭头键来导航它们。

您也可以启动终端模拟器。例如,在 Gnome 中,你点击应用程序菜单,打开附件标签,选择终端应用程序。这将启动 Gnome 终端应用程序,如图 4-4 所示。

A185439_2_En_4_Fig4_HTML.jpg

图 4-4。

Launching the Gnome Terminal application

在 KDE,情况略有不同。在早期版本的 KDE 上,您可以通过单击应用程序,打开系统工具,然后选择 Konsole 应用程序来启动 Konsole 应用程序。在 KDE 版本 4 和更高版本上,您可以通过单击应用程序,然后单击系统,并选择 Konsole 应用程序来启动 Konsole。在图 4-3 中,我们可以看到我们需要通过选择 Ubuntu 符号并键入 Terminal 来调出 Unity 中的搜索。在图 4-3 中,我们可以看到它已经出现,无需我们搜索终端,因为它是最近使用的应用程序。

贝壳

向您显示什么命令行取决于为您的用户运行什么 shell。Shells 是操作系统和主机内核的接口。例如,Windows 主机上的命令行也是一个 shell。每个 shell 都包含一组内置命令,这些命令允许您与您的主机进行交互(这些命令由您的发行版安装的附加工具进行补充)。

有多种 shell 可供使用,其中最常见的是 Bash(或 Bourne-again) shell,它默认用于许多发行版,包括流行的 Red Hat、Ubuntu 和 Debian 发行版。

Note

我们在第一章和第二章中更多地谈到了一些具体的发行版。

我们将在本章的所有例子中使用 Bash shell,因为它很可能是您默认找到的 shell。

命令行提示符

登录到 Linux 主机后,您应该会看到如下所示的提示:

jsmith@au-mel-centos-1 ∼$

那么这意味着什么呢?好吧,让我们来分析一下。

user@host directory$

在大多数 Linux 发行版中,基本提示由您登录时的用户名、主机名、当前目录和$符号构成,该符号表示您登录时的用户类型。

Tip

您可以自定义提示以包括附加信息、添加或更改颜色,或者实现各种其他选项。点击 http://tldp.org/HOWTO/Bash-Prompt-HOWTO/ 了解更多信息。

在我们的例子中,jsmith 是我们登录的用户名;接下来是@符号,后面是我们登录的主机名称,即 au-mel-centos-1 上的jsmith

Note

这部分提示看起来像一个电子邮件地址,这是有原因的。电子邮件就是这样开始的——在相连的 Unix 机器上拥有登录帐户的人们互相发送信息。@符号首次用于此目的是在 1971 年!你可以在 http://openmap.bbn.com/~tomlinso/ray/ firstemailframe.html 了解一下。

接下来你会看到一个符号,这是一个引用你的主目录的缩写方法。在 Linux 主机上,大多数用户都有一个特殊的目录,称为主目录,它是在创建用户时创建的。像 Microsoft Window 的用户配置文件一样,用户的首选项、配置文件和数据都存储在这个目录中。任何时候你看到使用的符号,它表示一个快捷方式,意思是主目录。我们之前讨论过主目录,它们大致等同于 Windows 文档和设置配置文件的概念和My Documents文件夹的组合。您通常会在名为/home的目录下找到主目录。

Note

Linux 是一个多用户操作系统,多个用户可以多次登录并同时工作。与 Windows 一样,用户可以拥有自己的环境、存储、访问控制和权限。

最后,您会看到$符号。这个符号告诉你你是什么类型的用户;默认情况下,主机上的所有普通用户都会将$作为他们的提示。有一个特殊的用户,叫做root,他的提示使用了#符号:

root@au-mel-centos-1 ∼#

root用户是超级用户。在 Windows 上,我们称这个用户为管理员。与 Windows 上的管理员用户一样,root用户可以控制和配置一切。因此,如果您看到#符号,您就知道您是以root用户的身份登录的。

在某些发行版中,您可以作为根用户登录,在安装过程中,通常会提示您为root用户指定一个密码。其他发行版,最著名的是 Ubuntu 发行版,禁用了root用户的密码。在 Ubuntu 上,假设你从来不使用root用户,而是使用一个叫做sudo的特殊命令。sudo命令允许您以root用户的权限运行命令,而无需以该用户的身份登录。我们将在第五章中讨论sudo命令。要使用sudo命令,您只需键入 sudo 和您希望运行的命令。通常会提示您输入密码,如果您输入了正确的密码,就会执行该命令。

$ sudo passwd root

这个命令将改变root用户的密码,这是在 Ubuntu 上启用root用户的一种方法。

用户是全能的,可以在你的主机上做任何事情。因此,当以root用户身份登录时,很容易意外出错,这可能会删除数据或中断您的应用程序和服务。因此,出于安全原因,您永远不应该以root用户的身份登录。我们将在第五章的后面讨论不使用root用户管理你的主机的其他方法。

Note

近年来,已经引入了其他安全控制来帮助减少对root用户的依赖,并提供更细粒度的安全控制。这些控件包括像 SELinux 和 AppArmor 这样的工具,我们在第二章中简要讨论过。

键入您的第一个命令

现在是时候尝试输入命令了。命令可以是二进制可执行文件(如 Windows 可执行文件或 EXE 文件),或者命令可以作为 shell 的一部分提供。让我们键入一个名为whoami的命令,并按回车键执行它:

$ whoami
jsmith

whoami命令返回您登录的用户名。你可以看到我们的主机已经返回jsmith。这告诉我们,我们以用户jsmith的身份登录到我们的主机。

每个 shell 都包含一系列内置命令和函数来帮助您使用命令行。现在让我们试一试这些。我们从再次运行whoami命令开始。但是这一次,我们犯了一个拼写错误,键入了错误的命令名:

$ whoamii

Note

在本书中,我们将把 shell 提示符缩写成最后一个提示符字符,或者是$或者是#

然后,我们按 Enter 键运行命令,发现 Bash 返回了以下响应:

-bash: whoamii: command not found

发生了什么?嗯,Bash 告诉我们主机上不存在名为whoami的命令。我们可以解决。让我们从更正命令开始。我们可以通过使用向上箭头键返回先前键入的命令。现在这样做,您可以看到前面的命令已经返回到命令行:

$ whoamii

Bash 有一个有用的命令历史,它记录了以前输入的许多命令。Bash 允许您使用上下箭头键浏览这些命令。

Tip

保留的历史数量可由用户配置,并可使用history命令进行操作。现在输入命令history查看您的命令历史。如果您刚刚登录,您可能会发现此历史记录为空。在这种情况下,使用几个命令并重试。您将看到一个编号行列表,显示您之前键入的命令。您可以通过在命令旁边输入前缀为!的数字来检索这些命令中的任何一个。符号。例如,!12会检索并执行你历史中的第 12 条命令。如果您键入!!,您将重新运行您上次输入的命令。

您也可以使用左右箭头键沿着命令行移动光标来编辑命令。使用箭头键移动到命令的末尾,删除多余的 I,留下

$ whoami

现在按 Enter 键,您将在命令行上看到结果:

jsmith

这一次,更正后的命令whoami再次返回登录用户的名称。

Tip

另一个有用的 Bash 特性是自动完成。开始输入命令,然后按 Tab 键,Bash 将搜索您的路径,试图找到您试图发出的命令。键入更多字符,Tab 键将进一步缩小搜索范围。

The Path

当 Linux 通知您它找不到二进制文件或命令时,可能是您拼错了命令的名称,或者它找不到那个特定的命令。像 Windows 一样,当执行命令时,Linux 搜索目录列表,试图找到那个特定的命令。默认情况下,大多数发行版都设置了默认路径,通常包含包含可执行二进制文件的典型位置。大多数时候你不需要设定你的道路;默认路径将是合适的。如果你想改变路径,你需要更新一个名为$PATH的环境变量。您可以通过键入$ echo $PATH来查看它当前的设置。我们将在第五章中讨论环境变量。

远程存取

在上两节中,我们已经讨论了 GUI 桌面和命令行。在这两种情况下,我们都假设您在本地登录到您的主机(即,坐在屏幕前,用键盘直接向主机键入命令)。但是在很多情况下,人们远程访问 Linux 主机。对于作为服务器运行的 Linux 主机来说尤其如此,这些服务器可能托管在数据中心或其他地理位置,或者存储在机架或机柜中。在许多情况下,这些主机甚至没有屏幕或键盘,只能通过网络访问。

使用 Linux,很容易远程连接到这些主机,这样您就可以管理它们。您可以使用许多不同的方法来完成这个远程连接。这些包括桌面共享协议,如虚拟网络计算(通常称为 VNC)、远程桌面协议(RDP),它通常用于提供对 Windows 主机的远程访问,以及广泛使用的安全 Shell(SSH)。

使用 SSH

我们很快就会看到使用 SSH 来提供对 Linux 主机的远程命令行访问。你也可以用 SSH 访问你的 GUI 桌面,但是我们会在第十章讨论这个。

SSH 既是一个应用程序,也是一个安全协议,用于多种目的,但主要用于主机的远程管理。在 Linux 主机上,SSH 由一个名为 OpenSSH 的开源版本的应用程序提供(参见 www.openssh.com/ ).

SSH 在客户端-服务器模型中通过 TCP/IP(传输控制协议/互联网协议)网络进行连接。您的连接主机是客户端。例如,如果您从笔记本电脑连接到远程主机,那么您的笔记本电脑就是客户端。您正在连接的主机称为服务器,它接收和管理您的连接。

使用 SSH 的远程连接是加密的,需要身份验证,要么是密码,要么是公钥加密。要建立 SSH 连接,您需要知道远程主机的 IP 地址或主机名。然后在客户端启动一个连接,并通过 TCP 在端口 22 上连接到服务器(您可以更改这个端口,我们将在第十章中讨论如何更改)。

Note

您可能以前遇到过 IP 地址和主机名,但是您可能没有遇到过端口。端口是 SSH 等服务使用的通信端点。端口号范围从 0 到 65535,一些常见的端口是 HTTP 端口 80、SMTP 端口 25 和 FTP 端口 21。1 到 1023 之间的端口通常是为系统服务保留的,而 1024 和更高的端口(也称为临时端口)是更随意分配的。我们将在第七章对此进行更详细的讨论。

初始连接后,服务器会提示客户端输入用户名和身份验证凭证,如密码。如果用户存在于服务器上,并且提供了正确的凭据,则允许客户端连接到服务器。

在大多数发行版中,SSH 是作为默认应用程序之一安装的,默认情况下会启动一个服务器。这个 SSH 服务器或 SSH 守护进程(服务器在 Linux 世界中也称为守护进程)允许远程连接到您的主机的命令行或 GUI。

您可以通过命令行或从多个客户端之一使用 SSH。通过命令行,使用名为ssh的命令建立客户端连接。大多数 Linux 和类 Unix 操作系统(例如,Mac OS X)都安装了 SSH,并提供了ssh命令。要使用ssh命令,您需要指定您的用户名和您想要连接的主机,用@符号隔开,如清单 4-1 所示。

$ ssh  jsmith@us-ny-server-1.example.com
Password:
Listing 4-1.SSH Connections

在清单 4-1 中,我们以用户jsmith的身份连接到一个名为us-ny-server-1.example.com的主机。然后系统会提示我们输入密码。如果我们输入了正确的密码,我们将登录到远程主机的命令行。

Caution

实际上,如果您运行这个确切的命令,它不会工作,因为主机us-ny-server-1.example.com不存在。如果您想测试这一点,您需要指定一个实际的活动主机。

还有各种 SSH 客户端或终端模拟器可用,例如,流行的免费 PuTTY 客户端(可从 www.chiark.greenend.org.uk/~sgtatham/putty/ 获得),它运行在 Windows 上(也运行在 Linux 上)。你也可以使用 Git Bash 自带的,我们在第三章安装了它,当然,它运行在 Windows 上。

SSH 客户端允许您从 GUI 向 Unix 或 Linux 主机的命令行运行文本终端。您可以在图 4-5 中看到 PuTTY 客户端的配置屏幕。

A185439_2_En_4_Fig5_HTML.jpg

图 4-5。

The PuTTY client

使用像 PuTTY 这样的 GUI 客户端非常简单。与命令行一样,您需要指定希望连接的主机的主机名(或 IP 地址)和端口。有了像 PuTTY 这样的客户端,您还可以做一些有用的事情,比如保存连接,这样您就不需要再次输入您的主机名。

使用 Git Bash,我们可以从 Bash 终端访问 ssh。我们在第三章中展示了如何安装 Git Bash。看一下图 4-6 ,我们在这里连接到us-ny-server-1.example.com

A185439_2_En_4_Fig6_HTML.jpg

图 4-6。

Using Git Bash to make an ssh connection

如清单 4-1 所示,我们正在连接美国服务器。您会注意到,当我们第一次连接到从未连接过的主机时,系统会要求我们接受 RSA 密钥指纹。这来自您正在连接的 SSH 服务器。接受该密钥后,它将根据服务器名称存储在一个名为 known_hosts 的文件中。到该主机的每个后续 SSH 连接都将检查指纹以查看它是否被更改。如果有,您将被要求在重新连接前清理密钥。这使您有机会验证主机或您的通信是否已被篡改。同样,我们将在第十章中详细讨论这一点。

在图 4-6 中,我们在理想情况下首先检查我们正在连接到我们认为正在连接的主机之后接受指纹(在网络世界中,这很难做到,因为您可能会连接到域外的主机)。完成后,我们存储指纹,然后提示我们输入密码。

如果您打算从 Windows 管理 Linux 服务器,我们建议您下载一个像 PuTTY 这样的客户端,如果您更喜欢 GUI 的话;否则你可以安装并使用 Git Bash 终端,就像我们在第三章中展示的那样。这两种方式都将有效地允许您从自己熟悉的环境中远程连接和管理您的 Linux 主机。基于 Unix 的 Mac OS 内置了一个 SSH 客户端。

Tip

SSH 客户端也适用于 Windows Mobile、Android、Symbian 和 Apple iPhone 等操作系统,允许出差族在旅途中连接到他们的 Linux 主机!

获得帮助

那么,如何在 Linux 主机上获得帮助呢?你可能在想,“我不能用 F1 键,对吧?”事实上你可以。在 Gnome 和 KDE 的 GUI 中,F1 键会显示界面的帮助文本。但是在命令行上,也有各种各样的工具来告诉您事情是如何工作的,帮助您找到您想要的命令,然后解释该命令可用的选项。

最简单的方法就是查看命令或者应用的man页面(手动页面的简称)。一个man页面告诉您该命令可以做什么,有哪些选项可用,以及关于它的各种其他信息。您可以通过键入 man 和您希望查看其man页面的命令的名称来访问man页面,如清单 4-2 所示。

$ man ls
Listing 4-2.The man Command

man命令将返回一个描述ls(或 list)命令及其各种选项的文档。

Note

ls或 list 命令列出主机上的文件和目录。在本章的后面,我们将向您展示更多关于使用ls命令的文件,敬请关注。

如果你在一个命令上有困难,它的man页面是你应该寻求帮助的第一个地方。不是所有的命令都有man页面,如果某个命令的man页面不存在,您会得到一条错误消息。在这种情况下,尝试在命令中添加--help开关通常很有用,如清单 4-3 所示。

Note

开关是可以添加到特定命令中的命令行选项。它们使用一个破折号(-)或两个破折号(--)和单字母缩写或交换机名称来指定,例如-l--name。您可以使用缩写或更长的版本。在编写脚本时,较长的版本有时会更有帮助,而且不那么模糊。比如,确保人们知道您在使用–v 时指的是–version,而不是–verbose。

$ ls --help
Listing 4-3.The --help Switch

Tip

通过man命令还可以获得对 Linux 的一般性介绍。要查看这个介绍,使用命令man intro

您可以搜索与简短描述中的单词相匹配的相关man页面。

$ man –k user
adduser (8)                   - create a new user or update default new user information
applygnupgdefaults (8) - Run gpgconf - apply-defaults for all users.
arpd (8)                         - userspace arp daemon.

或者您也可以使用-K选项在主机上的所有man页面中搜索关键字。

$ man -K user

这将在所有的man页面中搜索关键字user,并返回包含该关键字的所有man页面的列表。然后会提示您查看返回的每一页,跳过一页并转到下一页,或者退出搜索。

这个搜索可能有点慢,因为你的主机通常有很多man页面,所以有两个更简单的搜索命令可以提供你所寻找的快捷方式:whatisaproposwhatis命令搜索大多数 Linux 发行版上都有的命令摘要数据库,以获得完整的单词匹配,如下所示:

$ whatis useradd
useradd(8) – create a new user or update default new user information

whatis搜索返回了useradd命令,并包含了该命令的简要描述。

apropos命令也搜索whatis数据库,但是搜索字符串而不是完整的单词。

$ apropos whoam
ldapwhoami(1) – LDAP who am i? tool
whoami(1) – print effective userid

apropos搜索已经在whatis数据库中搜索了对字符串whoami的所有引用,并返回了许多包含该字符串的命令和函数。

还有一些其他有用的命令可以告诉您主机上的命令。例如,info命令有时会对命令的功能和选项提供更详细的解释;尝试info ls来阅读关于ls命令的更多细节。

Note

info界面不是很直观。它,以及man页面,出现在互联网(或者优秀的 UX 设计)无处不在之前。它们被设计成在本地系统上有尽可能多的信息,以防你不能连接到互联网,但这并不意味着是漂亮的。

用户和组

Linux 是多用户操作系统。这意味着它允许多个用户通过多个命令行或 GUI 会话同时连接。Linux 通过用户和组帐户控制对主机及其资源的访问。还为特定的系统组件创建用户,并用于运行服务;例如,如果您安装了一个mail服务器,也可能会创建一个名为 mail 的用户来使用该服务,或者一个名为lp(行式打印机)的用户来控制打印机资源。

Linux 也依赖于组,组是相似用户的集合。用户可以是一个或多个组的成员,通常被放在一个组中,以便他们可以访问某种资源。例如,所有需要访问应付账款系统的用户可能会被添加到一个名为accounts的组中。

Tip

您的用户和组信息主要包含在两个文件中:/etc/passwd保存您的用户信息,/etc/group保存您的组信息。我们将在第六章中详细讨论这些文件。

用户和组很重要,我们将在第六章解释它们是如何工作的以及如何创建它们。从概念上讲,用户和组的操作方式与他们在 Windows 主机上的操作方式非常相似。每个用户都有一个通常用密码保护的帐户。创建大多数普通用户时,也会创建一个类似于 Windows 配置文件的主目录。这个主目录为用户提供了一个存储数据的地方,也是许多应用程序存储用户特定配置的默认位置。用户也属于组,就像他们在 Windows 上一样,这为他们提供了对附加资源或服务的访问。

服务和流程

在 Windows 主机上,许多后台活动和服务器应用程序作为服务运行。服务可以启动和停止,当应用程序重新配置时,通常必须重新启动。这些服务通常通过控制面板中的服务管理器来控制。在 Linux 主机上,服务的概念也存在。服务也称为守护程序,在您的主机上运行许多关键功能。

Note

术语“Daemon”是 demon 的希腊文拼写,基于在热力学第二定律的麦克斯韦妖思想实验中对分子进行分类的假想存在。更多信息,请参见 [https://en.wikipedia.org/wiki/Daemon_(computing](https://en.wikipedia.org/wiki/Daemon_(computing) )

像在 Windows 主机上一样,每个服务或守护进程都是在您的主机上运行的一个或多个进程。这些过程都有名字;例如,我们前面讨论的安全 Shell 守护进程通常作为一个名为 sshd 的进程运行。其他常见的守护进程包括master(Postfix 邮件服务器)、httpd(Apache web 服务器)和mysqld(MySQL 数据库服务器)。默认情况下,这些进程中的一些可能与执行各种系统和应用程序功能的许多其他进程一起在您的主机上运行。大多数守护进程的名称通常以“d”结尾。

在清单 4-4 中,我们使用了带有-A标志的 ps 命令(针对所有)来列出我们的主机上当前运行的所有进程。

$ ps -A
  PID TTY          TIME CMD
    1 ?        00:00:00 systemd
    2 ?        00:00:00 kthreadd
    3 ?        00:00:00 ksoftirqd/0
    4 ?        00:00:00 kworker/0:0
    5 ?        00:00:00 kworker/0:0H
    6 ?        00:00:00 kworker/u4:0
...<snip>...
    445 ?    00:00:00 crond
    1571 ?  00:00:00 sshd
Listing 4‑4.The ps Command

在清单 4-4 中,您可以看到在我们的主机上运行的进程的删减列表。这个列表是使用带有-A(或列出所有进程)选项的ps命令生成的。主机上运行的每个进程按照其进程 ID (PID)的顺序列出,在清单 4-4 中由左边的列表示。PID 用于控制进程,我们将在第六章中查看启动和停止进程时使用它们。你的主机上最重要的进程叫做systemdsystemd进程是 Linux 主机上的基础进程,它派生出主机上的所有其他进程。此主进程始终使用 PID 1,并且必须运行才能使您的主机正常工作。

Note

根据您选择的操作系统,您可能会注意到在 PID 1中没有systemd进程,而是有一个init进程。Systemd 是主流 Linux 操作系统的新成员,已经被 Fedora 和 Debian 等不稳定的操作系统接受了一段时间。在较旧的系统上,您会看到init。我们在第六章中帮助解释这两者的区别。

很多名字以“k”开头的进程并不是真正的进程,而是内核线程。这些线程是一种特殊的服务,在操作系统的核心——内核中执行管理任务。这些“轻量级进程”允许内核进程在后台处理不同的任务,比如处理当有人插入 USB 驱动器时发生的事情。清单<thread>/0表示线程的名称,而/0是它运行的处理器。

清单 4-4 中的进程4451571是在您的系统上运行的守护进程的例子。我们看到了 crond 和 SSHd 守护进程,crond 是一个作业调度服务,sshd 是 OpenSSH 守护进程,用于处理来自 ssh 客户端的连接。

还有另一个有用的命令可以告诉您哪些进程正在您的主机上运行,哪些进程消耗了最多的 CPU 和内存。这个命令叫做top,我们在清单 4-5 中运行它。

$ top
Listing 4-5.The top Command

top命令启动一个交互式监控工具,该工具每隔几秒钟更新一次您主机上正在运行的顶级进程。你可以在图 4-7 中看到top命令输出的快照。

A185439_2_En_4_Fig7_HTML.jpg

图 4-7。

The top process-monitoring command

这是一个非常安静的系统的输出。顶部显示了许多详细信息,包括系统正常运行时间、系统平均负载、CPU 使用率和内存利用率。默认情况下,Top 每三分钟刷新一次,当进程消耗资源时,它们会在列表中时隐时现。大量消耗资源的进程,如 CPU,将始终列在列表的顶部,但您也可以按其他资源(如内存使用)列出。

包装

Microsoft Windows 世界中的应用程序通常通过运行二进制应用程序并遵循安装过程来安装。有些应用程序还附带卸载程序,如果您不再需要它们,卸载程序会将其删除。在某些情况下,您可以使用控制面板中的“添加或删除程序”工具来添加或删除应用程序。

在 Linux 世界中,包管理器相当于添加或删除程序工具。包管理器包含一组预打包的应用程序,例如 Apache web 服务器或 LibreOffice 套件。毫不奇怪,这些预打包的应用程序被称为包。打包成包的应用程序包含所需的二进制文件、支持文件,通常还包含配置文件,它们在安装后可以直接运行。

在第八章中,我们将广泛涵盖两种常用的包管理系统:RPM 和 Deb。它们分别由基于 Red Hat 和 Debian 发行版的发行版使用。所以 Red Hat Enterprise Linux,CentOS,Fedora Project,甚至 SUSE(一个基于非 Red Hat 的发行版)都是使用 RPM 的发行版。使用 Deb 的发行版包括 Ubuntu、Debian 和许多其他发行版。

文件和文件系统

在 UNIX 中,有一句话是这样说的,“Unix 中的所有东西要么是文件,要么是进程。”Linux 也坚持这种说法。文件有几种类型,但我们将从文件和目录开始。在 Linux 中,目录只是包含其他文件名称的文件。让我们仔细看看 Linux 文件和文件系统。

我们将从使用一个名为pwd的命令开始,或者打印工作目录。

$ pwd
/home/jsmith

pwd命令允许您通过识别我们的工作目录或当前目录来确定自己在文件系统中的位置。从这里,您可以导航文件系统;首先使用cd或更改目录命令将目录更改为根目录,如清单 4-6 所示。

user@host:∼$ cd /
user@host:/$
Listing 4-6.Changing Directories

如果您可以暂时忽略清单 4-6 中的user@host,您可以看到我们已经从当前目录转移到了/,它被称为根目录。把∼$改成/$就知道了。根目录是目录树的基础。Linux 文件系统是一个单一的目录。这意味着,与 Windows 不同,Linux 有一个单一的层次目录结构。不同于多个驱动器,例如C:\D:\,它们下面有单独的目录树,所有的驱动器、分区和存储都位于根目录或/目录之外。

这是如何工作的?安装 Linux 驱动器和设备(这可以在引导时自动进行,也可以手动进行)。这些挂载的驱动器和设备在文件系统中显示为“/”根目录下的子目录。我们在第六章解释了“/”目录最初是如何挂载的。

Note

我们还将在第九章中讨论更多关于存储和安装设备的内容。

使用cd命令,您可以遍历其他目录和子目录。Linux 将遍历文件系统的步骤称为路径。有两种类型的路径—绝对路径和相对路径。绝对路径总是以斜杠符号(/)开始,代表根目录,并指定您所描述的位置的最终位置;例如,/home/jsmith/是绝对路径。

相对路径允许您指定相对于当前位置或起点的位置。例如,该命令

$ cd foobar

尝试从当前目录切换到名为 foobar 的目录。如果不存在这样的目录,cd命令将失败。

还有几个符号经常与相对路径一起使用:

$ cd ..

这..表示我们希望在目录树上向上遍历一级(如果我们已经在顶部,我们就不会再去任何地方)。

我们还可以使用这种机制以其他方式遍历目录树,正如您在下面一行中看到的:

$ cd ../foo/bar

在这个例子中,我们有

  1. 如符号..所示,向上遍历一个目录级别
  2. 改变到上一级的一个名为foo的目录中
  3. 然后改成了foo目录下的一个名为bar的目录

让我们在图 4-8 中对此进行说明。

A185439_2_En_4_Fig8_HTML.jpg

图 4-8。

Directory traversal to ../foo/bar Note

如果您习惯使用 Microsoft Windows 命令行,您可能会注意到分隔目录的斜杠是正斜杠或/,而不是反斜杠或\。这确实需要一点时间来适应,但你很快就会适应的!

我们还可以使用以下结构引用目录中的相对对象:

$ ./make

在命令前面添加的./在我们当前的目录中执行 make 命令。

您可以遍历哪些目录取决于它们的权限。许多目录只允许特定用户和组访问(用户可以去任何地方)。如果您尝试更改到您没有适当权限的目录,您将会收到一条错误消息:

$ cd /root
-bash: cd: /root: Permission denied

Note

我们将在本章后面的“权限”一节中讨论权限。

现在您知道如何在目录树中移动了。但是你的主机上的所有东西都在哪里呢?大多数 Linux 发行版都遵循非常相似的目录结构。这并不是说所有的发行版都是相同的,但是一般来说,文件和目录都位于一个逻辑和一致的模型中。在表 4-1 中可以看到根目录下的典型目录结构。每个条目都有每个目录的简短描述。

表 4-1。

Linux Directory Structure

| 目录 | 描述 | | --- | --- | | `/bin/` | 用户命令和二进制文件。 | | `/boot/` | 引导加载程序使用的文件。(我们将在第六章中讨论引导加载程序。) | | `/dev/` | 设备文件。 | | `/etc/` | 系统配置文件。 | | `/home/` | 用户的主目录。 | | `/lib/` | 共享库和内核模块。 | | `/media/` | 可移动媒体通常安装在这里(参见第八章)。 | | `/mnt/` | 临时安装的文件系统通常安装在这里(参见第八章)。 | | `/opt/` | 附加应用软件包。 | | `/proc/` | 内核和进程状态数据以文本文件格式存储在这里。 | | `/root/` | root 用户的主目录。 | | `/run/` | 应用程序可以存储操作所需数据的目录。 | | `/sbin/` | 系统二进制文件。 | | `/srv/` | 此主机提供的服务的数据。 | | `/sys` | 虚拟文件系统,包含信息和对 Linux 内核子系统的访问。 | | `/tmp/` | 临时文件的目录。 | | `/usr/` | 用户实用程序、库和应用程序。 | | `/var/` | 可变或瞬时文件和数据,例如日志、邮件队列和打印作业。 |

Note

不是每个发行版都有这些目录(其他发行版可能有附加目录),但通常这个列表是准确的。

让我们看看根(/)目录下的一些关键目录,这些目录在表 4-1 中列出。第一个,也是最重要的一个,是/etc/。以 etcetera 命名的/etc/目录是主机上大多数重要配置文件所在的位置。在向主机添加应用程序和服务时,您将经常使用位于此目录中的文件。

接下来,/home/目录包含用户的所有主目录(除了根用户——其主目录通常是/root/)。/tmp目录是你通常会找到临时文件的地方。类似的还有/var目录,其中存储了临时数据,比如日志。您将经常看到包含在/var/log/目录中的日志文件,这些文件是由应用程序或通过主机的 syslog(或系统日志程序)守护进程创建的。这些日志文件包含关于应用程序、守护程序和服务状态的各种信息。

让我们仔细看看文件和目录,以及如何使用它们。首先转到根目录或/,目录:

$ cd /

现在你在根目录,你想看看目录中包含了什么。为此,您可以使用ls或 list directory 命令,如清单 4-7 所示。

$ ls
bin dev etc lib lost+found mnt proc root sys usr
boot home  lib64  media opt sbin srv tmp var
Listing 4-7.Listing the Contents of a Directory

在清单 4-7 中,您可以看到ls命令返回了根目录中的文件和目录列表。你会看到它看起来非常接近表 4-1 中的列表。

默认情况下,ls列出一个目录中的所有文件,但是您可以通过在命令行中列出该文件,将其限制为显示单个文件名或多个文件名,如下所示:

$ ls foobar

该命令将显示任何名为foobar的文件或目录。我们也可以使用通配符或星号来选择文件。

$ ls foo*

这将返回任何名为foo的文件加上任何以foo开头的文件,比如foobar,以及任何名称以foo开头的目录的内容。单独指定星号符号会列出所有文件和所有目录及其内容。

Tip

您将会看到更多的*符号,因为它在 Linux 上的使用与在 Windows 上一样多。它表示用于替换一个或多个字符的通配符;例如,您刚刚看到了foo*,它表示以foo开头的任何东西。使用?符号匹配单个字符;例如,指定?at将匹配cat, mat, bat,等等。这个活动统称为 globbing,你可以在 www.faqs.org/docs/abs/HTML/globbingref.html 阅读它在 Linux shells 中的使用。

您也可以通过指定目录名来列出其他目录中的文件:

$ ls /usr/local/bin

这将列出/usr/local/bin目录中的所有文件。

不过,在清单 4-7 中,您看不到关于这些文件和目录的很多细节。它只显示一个名单。要找到关于这个列表的更多信息,您可以在ls命令中添加开关,如清单 4-8 所示,以显示更多信息。

$ ls -la
total 192
drwxr-xr-x      25  root  root         4096  2016-07-22 12:47  .
drwxr-xr x      25  root  root         4096  2016-07-22 12:47  ..
-rw-r--r--          1   root  root              0  2016-07-15 20:47  .autofsck
drwxr-xr-x       2   root  root         4096  2016-05-18 04:11  bin
drwxr-xr-x       6   root  root         3072  2016-05-25 21:57  boot
drwxr-xr-x     14   root  root         4100  2016-07-19 12:26  dev
drwxr-xr-x   116   root  root       12288  2016-07-22 12:47  etc
drwxr-xr-x      7   smtpd smtpd    4096  2016-05-02 12:00  home
drwxr-xr-x    12   root  root          4096  2016-05-17 18:14  lib
drwxr-xr-x      8   root  root          4096  2016-06-06 10:19  lib64
drwx------       2   root  root        16384  2016-06-11 16:01  lost+found
drwxr-xr-x      2   root  root          4096  2016-06-11 16:14  media
drwxr-xr-x      4   root  root          4096  2016-06-12 11:28  mnt
...
Listing 4-8.Getting More Information from ls

在清单 4-8 中,la开关被添加到ls命令中。l开关是 long 的缩写,它使用长列表格式,如您所见,它显示了更多信息。a开关告诉ls列出所有文件和目录,甚至是“隐藏的”文件,也就是众所周知的“点”文件。

Tip

在 Linux(和 Unix)中,“隐藏”或“点”文件以句号或句号为前缀(例如清单 4-8 中的.autofsck文件),通常用于保存配置和历史信息或作为临时文件。它们是普通文件,但是有些实用程序,比如ls,默认情况下不显示它们。当您在目录中查找占用大量空间的文件时,它们也会让您大吃一惊。一个ls -lh将显示人类可读大小的正常文件(-h),一个ls -lah将显示文件大小,包括点文件。稍后我们会详细讨论这一点。

通过阅读命令的man页面,你可以看到ls命令可用开关的完整列表——只需输入man ls

那么长的列表格式告诉你关于你的文件和目录什么呢?在清单 4-8 中,每一项都有一个小的信息集合。在清单 4-9 中,您可以看到该清单的一个子集,显示了一个文件和一个目录,我们将对此进行更详细的研究。

-rw-r--r--      1 root  root        0  2016-07-15 20:47 .autofsck
drwxr-xr-x   2 root  root   4096  2016-05-18 04:11  bin
Listing 4-9.File Listing Subset

清单的每一行都包含关于每个对象的七条信息:

  • Unix 文件类型
  • 许可
  • 硬链接的数量
  • 用户和组所有权
  • 大小
  • 日期和时间
  • 名字

清单中包含的一些信息还介绍了一些关键的 Linux 概念,比如权限和用户、组和所有权。我们将利用这一介绍,不仅解释每一项,而且探索它们所代表的一些更广泛的概念。

文件类型和权限

文件类型和权限包含在前十个字符中,类似于-rw-r--r--。这个潜在的令人生畏的字符集合实际上很容易破译:第一个字符描述文件的类型,接下来的九个字符描述文件的权限。

文件类型

Linux 文件系统上的几乎所有东西都可以被描述为一个文件。清单的第一个字符告诉我们文件的确切类型。这里的破折号(-)表示可能包含数据或文本的常规文件,或者是二进制可执行文件。一个d表示一个目录,本质上是一个列出其他文件的文件。一个l表示一个符号链接。符号链接允许您在文件系统的多个位置显示文件和目录。它们很像微软视窗中使用的快捷方式。

表 4-2 列出了可用的文件类型。

表 4-2。

File Types

| 类型 | 描述 | | --- | --- | | `-` | 文件 | | `d` | 目录 | | `l` | 环 | | `c` | 字符设备 | | `b` | 阻止设备 | | `s` | 窝 | | `p` | 命名管道 |

我们将在这里简要介绍其他类型。你不会经常需要大多数类型,但它们会在后面的章节中偶尔出现。bc文件类型用于不同类型的输入和输出设备(如果你查看/dev目录,你会看到这些设备文件的例子)。设备允许操作系统与特定的硬件设备交互;例如,许多发行版都有一个名为/dev/usb的设备,代表连接到主机的 USB 驱动器。

Tip

当我们向您展示如何在您的主机上加载 USB 时,您将在第九章中了解更多关于设备的信息。

最后,套接字和命名管道是允许不同类型的进程间通信的文件。它们允许进程之间相互通信。您将在本书的后面看到一些套接字和命名管道。

许可

接下来的九个字符详细说明了分配给文件或目录的访问权限。在 Linux 上,权限用于确定用户和组对文件的访问权限。控制您对文件和应用程序的权限和访问对于您的 Linux 主机的安全性至关重要,在本书中,我们将经常使用权限来提供对文件的适当访问。因此,理解权限如何工作以及如何更改它们是很重要的。

有三种通常分配的文件权限类型:

  • 阅读,由字母r表示
  • 写,用字母w表示
  • 执行,由字母x表示

Note

还有另外两种类型的权限,sticky 和 setuid/setgid 权限,分别用ts字符表示。我们将在本章后面的边栏“Setuid、Setgid 和粘性权限”中讨论这些内容。

读取权限允许读取或查看文件,但不允许编辑。如果在目录上设置,则可以读取目录中的文件名,但看不到其他详细信息,如权限和大小。写权限允许您对文件进行更改或写入。如果在目录上设置了写权限,您可以在该目录中创建、删除和重命名文件。执行权限允许您运行文件;例如,所有二进制文件和命令(二进制文件类似于 Windows 可执行文件)都必须标记为可执行,这样您才能运行它们。如果在一个目录上设置了该权限,您就能够遍历该目录,例如,通过使用cd命令来访问子目录。因此,对目录设置的读取和执行权限的组合允许您遍历目录并查看其内容的详细信息。

主机上的每个文件都有三类权限:

  • 用户
  • 其他(其他所有人)

每个类代表一个不同的文件访问类别。User 类描述了拥有该文件的用户的权限。这是我们清单中的前三个字符。Group 类描述拥有该文件的组的权限。这些是我们清单中的第二组三个字符。

Note

Linux 中的组是用户的集合。组允许出于允许访问应用和服务的目的将相似的用户聚集在一起;例如,会计部门的所有用户可以属于同一个组,以允许他们访问您的应付帐款应用程序。我们将在第六章讨论群组。

最后,另一个类描述了所有其他人对该文件拥有的权限。这是清单中最后一组三个字符。

图 4-9 描述了这些等级及其位置。

A185439_2_En_4_Fig9_HTML.jpg

图 4-9。

File permission breakdown Note

任何位置的破折号都意味着根本没有设置特定的权限。

您可以在清单 4-10 中看到一个文件,我们将更详细地检查它的权限,然后您将了解如何对这些权限进行一些更改。

-rw-r--r--   1 root  root     0 2016-07-15 20:47 myfile
Listing 4-10.Permissions

在清单 4-10 中,我们有一个文件,如清单开头的破折号(-)所示。该文件归root用户和root组所有。前三个权限是rw-,表示root用户可以读写文件,但是破折号表示没有设置执行权限,用户不能执行文件。接下来的三个权限,r--,表示属于root组的任何人都可以读取该文件,但是不能对其做任何其他事情。最后,我们有最后三个权限的r--,它告诉我们另一个类有什么权限。在这种情况下,其他人可以读取该文件,但不能写入或执行它。

现在您已经看到了权限的样子,但是您如何着手更改它们呢?使用chmod(更改文件模式位)命令更改权限。更改权限的关键是只有拥有文件的用户或root用户才能更改文件的权限。因此,在清单 4-10 中,只有root用户可以更改myfile文件的权限。

chmod命令的语法很简单。在清单 4-11 中,您可以看到一些权限被更改了。

# chmod u+x myfile
# chmod u-x,og+w myfile
# chmod 654 myfile
Listing 4-11.Changing Permissions

在清单 4-11 中,我们已经三次更改了myfile文件的权限。权限更改是通过指定类、要执行的操作、权限本身以及要更改的文件来执行的。在我们的第一个例子中,你可以看到u+x。这相当于向用户类添加执行权限。

Note

执行权限通常只在本质上可执行的文件上设置,如脚本和二进制文件(也称为应用程序或程序)以及目录。On directories】的意思是遍历,也就是说你可以列出父目录中的目录,但是不能再往前了。

更新后,我们文件的权限将如下所示:

-rwxr--r--   1 root  root      0  2016-07-15 20:47 myfile

您可以看到在 User 类中添加了 x。那么chmod是如何知道这样做的呢?嗯,我们的变化中的 u 代表用户类。使用chmod,每个类都被缩写成一个字母:

  • u:用户
  • g:组
  • 其他人或所有人
  • a:全部

课后,您可以指定希望对该课程采取的行动。在清单 4-11 的第一行中,+号代表添加一个权限。您可以指定-符号来删除某个类的权限,或者指定=符号来设置该类的绝对权限。最后,指定动作的权限,在本例中是x

您还可以在一个命令中指定多个权限更改,如清单 4-11 的第二行所示。在第二行,我们有变化u-x,go+w。这将从用户类中删除x或执行权限,并向组和其他类添加w或写入权限。您可以看到,我们用逗号分隔了每个权限更改,并且我们可以列出多个要操作的类。(也可以列出多个权限;例如,u+rw会将读写权限添加到 User 类。)

因此,清单 4-11 中的第二行将我们的文件权限保留为

-rw-rw-rw-   1 root  root      0  2016-07-15 20:47 myfile

对于chmod,还可以使用a类的缩写,表示一个动作应该应用于所有的类;例如,a+r将向所有类添加读取权限:用户、组和其他。

我们还可以通过使用=符号将一个类的权限应用到另一个类。

# chmod u=g myfile

在前一行中,我们已经将用户类权限设置为与组类权限相同。

您还可以为多个文件设置权限,方法是列出每个文件,用空格分隔,如下所示:

# chmod u+r file1 file2 file3

ls命令一样,您也可以引用其他位置的文件,如下所示:

# chmod u+x /usr/local/bin/foobar

前一行为位于/usr/local/bin目录中的 foobar 文件的 User 类添加了执行权限。

您还可以使用星号来指定所有文件,并添加-R开关来递归到更低的目录,如下所示:

# chmod -R u+x /usr/local/bin/*

前一行中的chmod命令会将用户类的执行权限添加到/usr/local/bin目录中的每个文件。

清单 4-11 中的最后一行略有不同。我们指定了一个数字654,而不是类和权限。这个数字被称为八进制记数法。每个数字代表三个类别之一:用户、组和其他。此外,每个数字是分配给该类的权限的总和。在表 4-3 中,您可以看到分配给每个权限类型的值。

表 4-3。

Octal Permission Values

| 同意 | 价值 | 描述 | | --- | --- | --- | | `r` | four | 阅读 | | `W` | Two | 写 | | `x` | one | 执行 |

每个权限值加在一起,得到每个类的一个从17的数字。因此清单 4-11 中654的值将代表以下权限:

-rw-r-xr-- 1 root root 0  2016-08- 14 22:37 myfile

第一个值6,相当于为用户类分配值为 4 的读权限和值为2的写权限。第二个值5,为组类分配值为4的读取权限和值为1的执行权限。最后一个值4,只将读取权限分配给另一个类。为了更清楚起见,您可以在表 4-4 中看到从07的可能值列表。

表 4-4。

The Octal Values

| 八进制的 | 许可 | 描述 | | --- | --- | --- | | `0` | `---` | 没有人 | | `1` | `--x` | 执行 | | `2` | `-w-` | 写 | | `3` | `-wx` | 编写并执行 | | `4` | `r--` | 阅读 | | `5` | `r-x` | 阅读并执行 | | `6` | `rw-` | 直读式记录 | | `7` | `rwx` | 读取、写入和执行 |

在表 4-5 中,可以看到一些常用的八进制数以及它们所代表的相应权限。

表 4-5。

Octal Permissions

| 八进制数 | 许可 | | --- | --- | | `600` | `rw-r--r--` | | `644` | `rw-r--r--` | | `664` | `rw-rw-r--` | | `666` | `rw-rw-rw-` | | `755` | `rwxr-xr-x` | | `777` | `rwxrwxrwx` |

Tip

chmod命令有一些改变权限的附加语法,你可以在命令的man页面中读到它们。

最后,有一个叫做 umask 的重要概念,您也需要理解它才能完全理解权限是如何工作的。umask 规定了创建文件时分配给文件的默认权限集。默认情况下,如果没有设置 umask,创建文件的权限为0666(或者设置所有者、组和其他人的读写权限),创建目录的权限为0777(或者所有者、组和其他人的读写权限)。您可以使用 umask 命令修改这些默认权限。我们来看一个例子。

# umask 0022

这里我们指定了一个 umask 为0022。这看起来很熟悉,不是吗?是的,这是一种八进制记数法。在这种情况下,它表明什么没有被授予。因此,这里我们将获取文件的默认权限0666,并减去0022值,得到的权限为 0644。使用 umask0022,将创建一个新文件,文件所有者拥有读和写权限,组和其他人拥有读权限。新创建的目录(默认权限为 0777)现在将拥有0755 with a umask of 0022的权限。另一个常用的 umask 是0002,它导致文件的默认权限为0664,目录的默认权限为 0 775。这也允许对组进行写访问,此 umask 通常用于位于共享目录或文件共享中的文件。

在大多数主机上,umask 是由 shell 中的设置自动设置的。对于 Bash shells,您通常可以在/etc/bashrc文件中找到全局 umask,但是您也可以使用umask命令或使用pam_umask模块在每个用户的基础上覆盖它(我们将在第五章中提供关于 PAM 的更多信息)。

Tip

umask命令也可以使用替代语法设置 umasks。我们刚刚描述了最简单和最容易的。您可以在umask man页面找到更多详细信息。

Setuid, Setgid, and Sticky Permissions

还有另外两种类型的权限,setuid/setgid 和 sticky,理解它们也很重要。

setuid 和 setgid 权限允许用户运行命令,就好像他是拥有该命令的用户或组一样。那么,为什么需要这样做呢?这允许用户执行他们通常被限制执行的特定任务,或者允许用户共享资源,例如使用相同的组 id 访问文件服务器上的共享文件。

一个很好的例子就是passwd命令。passwd命令允许用户更改自己的密码。为此,该命令需要写入密码文件,这是一个限制访问的文件。通过添加 setuid 权限,用户可以执行passwd命令并运行它,就像她是root用户一样,因此允许她修改自己的密码。

您可以通过使用权限列表中的sS来识别 setuid 和 setgid 权限。例如,passwd 命令的权限是

-rwsr-xr-x 1 root root 25708  2016-09- 25 17:32 /usr/bin/passwd

在 User 类的执行位置可以看到s,这表示passwd命令设置了 setuid 权限。现在看一下下面的目录清单。

-rwSrwSr-- 1 jsmith jsmith     0 Jun  5 09:55 adirectory

这也说明“adirectory”目录的 setuid 和 setgid 已经设置,但这次是用“S”指定的。这意味着该目录没有与用户和组相关联的“执行”权限。换句话说,这个目录(和一个文件)的权限应该已经用‘u+rws,g+rws,o+r’.设置好了

在大多数发行版中,setuid/setgid 权限被用来允许这种类型的访问。很少使用它们,因为你通常不希望一个用户能够以另一个用户的身份运行应用程序或者拥有特殊的特权(另一种方法是通过susudo命令,我们将在第六章中进一步描述)。由于它们也有可能被滥用,并带来安全风险,因此不应该不加选择地使用它们。在本书中,您可能会看到一两个使用 setuid/setgid 权限的应用程序。

粘性权限略有不同,用于目录(对文件没有影响)。当在目录上设置粘滞位时,该目录中的文件只能由拥有它们的用户、目录的所有者或根用户删除,而不管在该目录上设置的任何其他权限。这允许创建公共目录,其中每个用户都可以创建文件,但只能删除他们自己的文件。你可以从另一个类的执行位置的t中识别出一个带有粘性权限的目录。最常见的是在/tmp目录下设置:

drwxrwxrwt 4 root root 4096 2016-08-15 03:10 tmp

在八进制表示法中,setuid/setgid 和 sticky 权限由表示法前面的第四个数字表示,例如,6755。和其他权限一样,每个特殊权限也有一个数值:setuid 用4,setgid 用2,sticky 用1。所以要在目录上设置粘滞位,你可以使用类似1755的八进制符号。如果没有设置 setuid/setgid 或 sticky 权限,则该前缀数字为 0,如下所示:

# chmod 0644 /etc/grub.conf

链接

让我们再来看看清单 4-9 中的例子:

-rw-r--r--        1 root  root        0   2016-07-15 20:47   .autofsck
drwxr-xr-x     2 root  root   4096  2016-05-18  04:11   bin

在我们的清单中,文件类型和权限之后是文件的硬链接数量。硬链接是将文件连接到存储卷上的物理数据的引用。一个特定的数据可以有多个链接。然而,硬链接不同于我们前面介绍的符号链接(由文件类型l表示),尽管这两种链接都是用同一个命令ln创建的。我们将在本章后面的“链接文件”一节中讨论ln命令

用户、组和所有权

清单中的下一项是文件的所有权。每个对象由一个用户和一个组拥有;在清单 4-9 中,根用户和根组拥有对象。在讨论权限时,我们简要讨论了用户和组的所有权。我们解释了只有拥有文件的用户才能更改其权限,而组是用户的集合。组通常用于允许访问资源;例如,需要访问打印机或文件共享的所有用户可能属于提供对这些资源的访问的组。正如我们在本章前面所讨论的,在 Linux 主机上,用户必须至少属于一个组,称为主要组,但是也可以属于一个或多个附加组,称为补充组。

您可以使用chown命令更改文件的用户和组所有权。只有根用户有权更改文件的用户所有权(尽管您可以使用我们在本章前面讨论过的sudo命令来获得这一权限,并将在第六章中更详细地介绍)。

在清单 4-12 中,我们展示了一些如何使用chown命令来改变用户和组所有权的例子。

# chown jsmith myfile
# chown jsmith:admin myfile
# chown -R jsmith:admin /home/jsmith/*
Listing 4-12.Changing Ownership

在清单 4-12 中,我们有三个chown命令。第一个命令将拥有myfile文件的用户更改为jsmith。第二个命令更改文件的用户和组的所有权,用户更改为jsmith,组更改为admin,所有者和组用冒号分隔。第三个也是最后一个命令使用-R开关来启用递归。该命令会将/home/jsmith目录中每个文件和目录的所有者更改为jsmith,将组更改为admin

Note

也可以使用chgrp命令。它允许用户更改他们拥有的文件组。用户只能将组所有权更改为该用户所属的组。你像使用chgrp groupname 文件一样使用它。

尺寸和空间

接下来,在我们的列表中,您将看到磁盘上对象的大小。文件的大小以字节为单位列出(1024 字节是一千比字节,即 K)。我们还可以通过添加如下的-h开关,以更易于阅读的格式显示尺寸:

$ ls -lh
-rw-rw-r--  1 jsmith jsmith  51K  2016-08- 17 23:47 myfile

在前一行中,您可以看到myfile文件的大小为 51 千比字节。

在清单中,目录旁边的大小不是其总大小,而是目录元数据的大小。要获得一个目录中所有文件的总大小,可以使用du或 disk usage 命令。指定(或切换到)您想要查找其总大小的目录,并使用-s-h开关运行命令。-s开关汇总总数,-h开关以人类可读的形式显示大小。

$ du -sh /usr/local/bin
4.7M     /usr/local/bin

du工具有许多额外的开关和选项,你可以通过查看它的man页面看到。

除了文件和目录的大小,您还可以使用另一个命令df查看主机上已用和可用的总磁盘空间。此命令显示所有磁盘和存储设备以及它们的可用空间。您可以在清单 4-13 中看到df命令。

$ df -h
Filesystem                                           Size     Used  Avail   Use%  Mounted on
/dev/mapper/VolGroup00-LogVol01  178G     11G   159G    6%      /
/dev/sda1                                             99M     37M    58M    39%    /boot
tmpfs                                                  910M       0     910M    0%      /dev/shm
Listing 4-13.Displaying Disk Space

我们已经执行了命令并添加了-h开关,它返回人类可读的尺寸。它显示了我们当前的文件系统及其已用和可用空间,以及已用的百分比。还有一些额外的选项可以和df命令一起使用,你可以在命令的man页面查看这些选项。我们将在第九章中重温dfdu命令。

日期和时间

清单中的倒数第二项和最后一项是文件上次修改的日期和时间(称为 mtime)以及文件或目录的名称。Linux 还跟踪文件的最后访问时间(称为 atime)和创建时间(称为 ctime)。您可以使用-u开关列出文件的最后访问时间,如下所示:

$ ls -lu

您可以使用-c开关列出创建日期:

$ ls -lc

Note

我们将在第十七章中重温 atime。

如果您想知道当前主机上的实际时间和日期,您可以使用有用且强大的 date 命令。在没有任何选项的命令行上使用date将返回当前时间和日期,如下所示:

$ date
Tue Aug 19 13:01:20 EST 2016

您还可以在date命令中添加开关,将输出格式化为不同的日期或时间格式;例如,要显示 Unix 纪元时间(自 1970 年 1 月 1 日以来的秒数),您可以如下执行date命令:

$ date +%s
1219116319

这里我们使用+符号添加了一个格式,然后指定了格式,在本例中是%s,以显示纪元时间。在计算时差或作为后缀添加到文件以获得唯一性时,纪元时间会很有用。您可以在date命令的man页面中看到其他格式。您也可以使用date命令来设置时间。键入 date,然后以 MMDDhhmm[[CC]YY]格式指定所需的日期和时间。你可以在 http://en.wikipedia.org/wiki/Unix_time 找到更多关于 Unix 纪元时间的信息。

Note

这只是设置时间的一种方法,我们将在第十章讨论其他更有效的方法,如网络时间协议。

使用文件

因此,在探索我们的简单文件列表的过程中,我们涵盖了许多概念,向您介绍了一些 Linux 命令,并教您如何执行一些关键的管理任务。从这些任务开始,我们将通过讲述如何查看、编辑、搜索、复制、移动和删除文件来结束本章。为了管理您的 Linux 主机,您需要知道如何处理所有这些任务。

读取文件

你要学的第一件事是如何读取文件。Linux 主机上的许多文件,尤其是配置文件,都是基于文本的,可以使用一些简单的命令行工具来读取。

Note

请始终记住,为了读取文件,您必须拥有该文件的读取权限。这意味着您需要拥有该文件,或者属于对该文件具有读取权限的组,或者该文件为其他类设置了读取权限。

这些工具中的第一个是catcat命令如此命名是因为它“连接并打印文件”在清单 4-14 中,您可以看到cat命令在文本文件中的使用。

$ cat /etc/hosts
# Do not remove the following line, or various programs
# that require network functionality will fail.
127.0.0.1               localhost.localdomain localhost localhost
::1             localhost6.localdomain6 localhost6
Listing 4-14.Using the cat Command

在清单 4-14 中,我们已经将/etc/hosts文件输出到屏幕上。/etc/hosts文件包含我们的 Linux 主机的主机条目(就像 Windows 下的\WINDOWS\System32\services\etc\hosts文件),它们将主机名与 IP 地址相匹配。但是cat命令是一个非常简单的工具,它直接输出文本。如果文件非常大,文本将继续输出并在屏幕上向下滚动,这意味着如果您想查看文件开头的内容,您需要向后滚动。

Tip

您可以通过 Shift+Page Up 和 Shift+Page Down 组合键上下滚动虚拟控制台。

为了解决这个问题,我们将看看另一个名为less的命令。

Note

您可以在/etc/passwd/etc/group文件上尝试使用cat命令来查看您的主机上的用户和组的完整列表。

less命令允许你向前和向后滚动文件,一次滚动一个屏幕。每次显示页面时,都会提示您如何继续。我们通过如下指定文件名来运行less:

$ less /etc/services

less界面内部,您可以滚动浏览文件。要转到下一页,可以使用空格键,要一次前进一行,可以使用Enter键。要向后滚动,可以使用 B 键。你也可以使用箭头键滚动,要退出less命令,你可以使用 Q 键。

Note

通过查看命令的man页面,您可以看到使用less导航文件的其他方法。

除了在文件中导航之外,还可以在一个或多个文件中搜索特定信息。为此,我们可以使用非常强大的grep命令。grep命令允许您在一个或多个文件中搜索字符串或模式(使用正则表达式)并返回搜索结果。

Note

“grep”一词已经成为 IT 界常用的搜索术语,就像“google”一词用于在线搜索引擎一样。2003 年,牛津英语词典增加了“grep”这个词,它既是名词又是动词(例如,“John grep ' ed his mailbox to find the e-mail”)。

在清单 4-15 中,您可以看到一个非常简单的grep搜索文件/etc/hosts中的字符串localhost

$ grep localhost /etc/hosts
127.0.0.1      localhost.localdomain localhost localhost
::1                 localhost6.localdomain6 localhost6
Listing 4-15.Introducing grep

要使用grep,您需要指定您要搜索的字符串,在本例中是localhost ( grep区分大小写,所以它只会找到这个小写的字符串),然后是您要搜索的文件的名称。

Note

您可以通过在命令中添加-i开关来使grep不区分大小写。

默认情况下,grep返回文件中包含我们正在搜索的字符串的那些行。您还可以使用星号来搜索多个文件,就像我们在本章前面对其他命令所做的演示一样,例如:

$ grep localhost /etc/host*
$ grep localhost /etc/*

第一个命令将搜索/etc/目录中以host*开始的所有文件,第二个命令将搜索/etc/目录中的所有文件。两次搜索都是针对字符串localhost

您还可以通过添加-r开关递归地向下搜索到更低的目录,如下所示:

$ grep -r localhost /etc

Tip

在 Ubuntu 和 Debian 主机上,rgrep命令自动递归到目录中。

您还可以指定更复杂的搜索词,例如多个单词,如下所示:

$ grep "local host" /etc/hosts

您可以看到,我们指定了单词“local”和“host ”,它们之间有一个空格。为了告诉grep这些单词被组合在一起并正确解析,我们需要用引号将它们括起来。引号通常用在许多命令上,以防止输入被不恰当地解析。在本例中,我们正在搜索精确的字符串“local host”,而grep没有返回任何结果,因为该字符串不在/etc/hosts文件中。

命令的功能远不止这些简单的例子。您可以使用grep在文件中进行复杂的正则表达式搜索,例如:

$ grep 'J[oO][bB]' *

这将在当前目录的所有文件中找到字符串JOB, Job, JOb,JoB(记住,grep在默认情况下是区分大小写的,所以我们的正则表达式已经明确指定了大写和小写的变体)。正则表达式允许你在你的主机上做一些非常强大的搜索。

让我们看看其他一些使用grep的有用的正则表达式搜索。

$ grep 'job$' *

在前一行中,我们搜索了当前目录中的所有文件,寻找以job结尾的字符串。符号$告诉grep在字符串末尾搜索文本。

您可以使用^符号依次搜索以特定字符串开始的字符串,如下所示:

$ grep '^job' *

这将返回任何以job开头的字符串。你会发现有无数其他的正则表达式可以经常使用。

Regular Expressions

正则表达式是一种用于识别文本字符串的正式语言;例如,正则表达式可能标识文件中对字符串job的所有引用。各种非常相似的正则表达式语言被像grep这样的工具和编程语言使用,例如 Perl。大多数正则表达式语言的语法非常相似,但偶尔也会有细微的差别。你可以在 http://en.wikipedia.org/wiki/Regular_expression 进一步了解正则表达式。

您可以使用三种版本的正则表达式,BRE(基本正则表达式)、ere(扩展正则表达式)和 PCRE (Perl 正则表达式)。根据你对grep的理解,BRE 和 ere 没有区别。要使用 PCRE,您需要 Perl 中的以下包:libpcre3-dev (Ubuntu)或 pcre-devel (CentOS)。

要测试正则表达式,可以使用以下两种正则表达式编辑器之一:

了解一些常见的模式非常有用:

grep –o –E "([0-9]{1,3}[\.]){3}[0-9]{1,3}" /var/log/auth.log

以上将匹配/var/log/auth.log (Ubuntu 日志文件)中的所有 IP 地址。–o表示只打印比赛的结果,否则你将看到整行比赛的结果。–E说使用 ERE 版本(尽管我们已经说过 ERE 和 BRE 在这里是一样的)。看看表达式本身,首先我们寻找 1 到 3 个出现在 0 到 9 之间的数字{1,3}``[0-9],后面跟一个点。

[0-9]{1,3}\.

这将匹配 0。高达 999。我们期待看到这些:

([0-9]{1,3}[\.]){3}

然后,我们希望看到后面没有点的另一组八位字节。

([0-9]{1,3}[\.]){3}[0-9]{1,3}

将所有这些放在一起,实际上将匹配从 000.000.000.000 到 999.999.999.999 的任何 IP 地址,我们知道这在技术上是错误的,但选择它是因为它更清楚地解释了只匹配 0 到 255 之间的数字是一个更复杂和长的正则表达式(但在互联网上广泛记录)。

另一个有用的正则表达式是在邮件日志中搜索电子邮件地址。

egrep -o "[A-Za-z0-9._-]+@example.com" /var/log/mail.log

这里你可以看到我们正在使用egrep命令,它只是执行带有–E标志的grep。在本例中,我们正在搜索与<username>@example.com匹配的电子邮件地址。我们首先查找任何字母(仅拉丁文),大写或非大写、[A-Za-z...],、数字、[...0-9]、点、[.]或破折号、[...-]

[A-Za-z0-9._-]

这些可以出现一次或多次[...]+

[A-Za-z0-9._-]+

其次是我们感兴趣的领域:

[A-Za-z0-9._-]+@example.com

为了改进我们的匹配,让我们考虑下面的示例文本文件:

email: root@example.com
email: jsmith@example.com
iam a sentence with somewordroot@example.comsomeotherword
email: bjuice@example.com

使用“[A-Za-z0-9._-]+@example.com”正则表达式,我们将在“somewordroot@example.com”上得到一个匹配,这不是我们想要的。我们可以使用一个单词边界锚' \b ',它将只捕获'root@example.com'。这将使正则表达式如下所示:

"\b[A-Za-z0-9._-]+@example.com\b"

现在,如果我们对文本文件示例运行grep,我们只匹配我们想要的电子邮件地址:

$ egrep -o "\b[A-Za-z0-9._%+-]+@example.com\b" file.txt
root@example.com
jsmith@example.com
bjuice@example.com

带有正则表达式的grep命令可能非常强大,您可以执行一些非常复杂的表达式,但是请记住这个老笑话:

“我用正则表达式解决了我的问题。。。现在我有两个问题,”这意味着复杂的正则表达式可能很难维护。

对于进一步的阅读,我们建议拿起一本书,如 Jeffrey Friedl (O'Reilly,2006)的《掌握正则表达式》来帮助你学习正则表达式。

搜索文件

我们已经向您展示了如何读取文件,但是如果您需要找到文件的位置呢?Linux 主机上的许多命令和工具允许您以与 Windows 搜索功能非常相似的方式查找文件。在图 4-10 中,可以看到 Gnome 搜索功能。

A185439_2_En_4_Fig10_HTML.jpg

图 4-10。

Gnome search function

在命令行上,您也可以使用find命令搜索文件。让我们使用find命令在/home目录中搜索一个名为myfile的文件:

$ find /home/ -type f -iname myfile*

find 命令使用起来非常简单。首先指定要搜索的位置,在本例中是在/home/目录中。您还可以指定/作为根目录(从而搜索整个目录树),或者您可以访问的任何其他位置。

Note

如果您没有搜索特定目录的权限,您会收到一条错误消息,指出您的搜索已被拒绝。

接下来,我们指定了两个选项,-type-iname。第一个选项,-type,指定了我们要搜索的文件类型;在这种情况下,正常文件由f表示。例如,您还可以为目录指定d或为套接字指定s(用于进程间通信的特殊文件)(参见man页面,了解您可以搜索的所有可能类型)。-iname选项搜索不区分大小写的模式,在本例中,是以myfile开头的所有文件。这些选项只是可能的搜索选项中非常小的一部分;您还可以按所有者、组、权限、创建或修改的日期和时间以及大小等进行搜索。然后,find命令将搜索指定的位置,并返回符合搜索标准的文件列表。

您还可以使用find命令来定位不属于任何用户或组的文件和目录。如果用户或组已被删除,并且相关文件没有随该用户或组一起重新分配或删除,则通常会出现这些问题。我们将在第六章中详细讨论这一点。使用下面的find命令,您可以列出处于这种状态的所有文件:

# find / -nouser -o -nogroup

这个命令作为root运行,将在整个目录树中搜索不属于有效用户或组的任何文件。

Tip

您可能还想看看其他一些与搜索相关的命令,包括locatewhereiswhich。你应该阅读他们的man页面了解更多信息。

复制文件

除了查看文件之外,管理主机时需要执行的最常见操作之一是复制文件。关于复制文件,首先要理解的是,像读取文件一样,为了复制,你需要有适当的权限。要复制文件,您需要两种权限:对要复制的文件的读取权限和对要复制到的目标的写入权限。

要复制文件,使用cp命令(复制的简称)。在清单 4-16 中,您可以看到一个简单的cp命令。

$ cp /home/jsmith/myfile /home/jsmith/yourfile
Listing 4-16.Copying Files

在清单 4-16 中,我们已经将文件/home/jsmith/myfile复制到了/home/jsmith/yourfile。你需要小心使用cp命令。默认情况下,cp命令会在不提示您的情况下复制现有文件。如果您已经有一个与要复制到的文件同名的文件,这可能会很糟糕。您可以通过添加-i开关来改变这种行为。-i开关启用交互模式,如果您要复制的文件已经存在,系统会提示您是或否。您回答y覆盖或 n 中止复制。

Note

在一些较老的 Red Hat、Fedora 和 CentOS 发行版上,cp, mv,rm命令的-i开关是通过别名化每个命令自动设置的;例如,cp -i别名为cp。您可以使用alias命令在其他发行版上这样做;详见 www.ss64.com/bash/alias.html

如果我们没有权限读取该文件,我们会得到如下错误:

cp: cannot open `/home/jsmith/myfile' for reading: Permission denied

如果我们不能写入目标,我们会得到一个类似的错误。

cp: cannot stat `/home/jsmith/yourfile': Permission denied

还可以用cp多做几件事。您可以使用星号复制多个文件,如下所示:

$ cp /home/jsmith/* /home/jsmith/backup/

前一行中的目标/home/jsmith/backup/必须是一个目录,我们将把/home/jsmith目录中的所有文件复制到这个目录中。

您也可以选择文件的子集。

$ cp -i /home/jsmith/*.c ./

在前一行,我们复制了所有后缀为。c到当前目录(使用。/快捷方式)。我们还添加了-i开关,以确保如果文件已经存在,我们会得到提示。

您还可以通过添加–r开关,使用 cp 复制目录及其内容。

$ cp -r /home/jsmith /backup

前一行将/home/jsmith目录及其下的所有文件和目录复制到/backup目录。

Caution

使用-r开关时,注意不要像在 Windows 上一样使用*.*通配符。当在 Linux 上使用时..目录也将被递归复制,这可能不是你的意图!

最后,当使用cp命令复制文件时,一些关于文件的项目,如日期、时间和权限,可以被更改或更新。如果您想在副本上保留原始值,您可以使用-p开关。

$ cp -p /home/jsmith/myfile /home/jsmith/yourfile

Working with Directories

除了文件,您还可以操作目录。要创建一个目录,使用mkdir命令。您必须对创建目录的位置拥有写权限。如果你想复制目录并递归地复制它们的内容,你可以通过添加-r开关用cp命令来完成。

您也可以使用mv命令移动目录,就像移动文件一样。

最后,如果你想删除一个目录,使用rmdir命令。rmdir命令将只删除空目录(即其中没有文件的目录)。

我们之前研究过的cat命令也可以通过一个名为重定向的命令行功能来复制文件。

$ cat /home/jsmith/myfile > /home/jsmith/yourfile

使用>符号将一个命令的输出发送到>符号另一侧的命令或动作。在这种情况下,cat命令的输出被重定向到一个名为yourfile的文件中。如果该文件不存在,将会创建它。如果它确实存在,其内容将被覆盖。

Caution

使用重定向时要小心,因为你的目标文件会被覆盖而没有警告。

您还可以使用相同的机制向文件追加内容。

$ cat /home/jsmith/myfile >> /home/jsmith/yourfile

使用>>语法将把来自myfilecat的输出附加到yourfile的末尾。如果yourfile不存在,它将被创建。

Tip

许多其他命令也可以使用重定向来将输出从一个命令定向到另一个命令。它还与另一个 Bash 功能 piping 紧密相关(参见侧栏“Piping 和其他 Bash 技巧”)。

Piping and Other Bash Tips

您已经快速浏览了 Bash 命令行以及使用它可以做的一些事情。这包括使用带有>或>>符号的重定向将输出从一个命令重定向到另一个命令。这个概念可以使用|或管道符号来扩展。例如,管道将一个命令的输出传递给另一个命令

$ cat /etc/passwd | grep ataylor

在前一行中,我们已经输出了/etc/passwd文件的内容,然后将结果传送给grep命令来搜索术语ataylor。这将输出包含术语ataylor的任何一行或多行。您几乎可以用任何在命令行上接受输入的命令来做到这一点。可以与管道一起使用的一些有用的命令有sort(以各种方式对输入进行排序)、uniq(生成唯一的列表)和wc(计算行数、字数等)。).你可以在他们的man页面中读到这些命令。

您还可以进一步进行重定向,多次重定向,或者同时使用管道和重定向。让我们看一个例子。

$ cat *.txt | sort | uniq > text

在本例中,我们要求主机输出所有带有后缀.txt的文件,按字母顺序对它们进行排序,删除重复的行(使用uniq命令),然后将结果输出到一个名为text的文件(如果不存在,将创建该文件,如果存在,将覆盖该文件)。

您还可以重定向输入和输出。

$ grep accounts < /etc/group > matched_accounts

在前面的例子中,我们使用<符号将文件/etc/group导入 grep 命令。然后,我们告诉grep搜索术语accounts,并使用>符号将该命令的输出指向一个名为matched_accounts的文件。

另一个有用的技巧是能够在一个命令行上运行多个命令,用分号分隔每个命令。

$ ./configure; make; make test

这个命令行将运行当前目录中的configure脚本,然后运行makemake test命令。这些命令将依次运行,一个接一个。

这些只是 Bash 命令行、重定向和管道功能的一些非常简单的例子。通过查看 Bash 的man页面、man bash,或者查看在线 Bash 教程之一,如 www.hypexr.org/bash_tutorial.phphttp://tldp.org/LDP/Bash-Beginners-Guide/html/ ,或者查看位于 www.gnu.org/software/bash/manual/bashref.html 的 Bash 参考手册,可以发现更多 Bash 功能。

移动和重命名文件

在 Linux 中移动文件非常简单。使用mv命令,您可以将文件或目录从一个位置移动到另一个位置。要移动文件,您必须对该文件具有写权限,并且对要将其移动到的位置具有写权限。

清单 4-17 演示了如何移动文件。

$ mv -i ∼/myfile /home/bjones/yourfile
Listing 4-17.Moving Files

清单 4-17 中的命令将名为myfile的文件从主目录移动到/home/bjones,并将其重命名为yourfile-i选项再次确保我们在目标文件已经存在时得到提示。你也可以用mv命令来重命名文件。

$ mv -i ∼/myfile ∼/mynewfile

您可以对目录做同样的事情。

删除文件

使用rm(删除)命令删除文件。与任何主机一样,删除文件应该小心谨慎。然而,在 Linux 上,与 Windows 不同,没有快速简单的方法来恢复删除的文件,所以在删除文件之前,您需要小心谨慎,采取一些预防措施。第一个预防措施是使用带有rm命令的-i开关。-i开关启用交互模式,每次删除文件时都会提示您。您必须用一个yY来响应,以删除文件或中止任何其他操作,如清单 4-18 所示。

$ rm -i /home/jsmith/myfile
rm: remove regular file `/home/jsmith/myfile'? n
Listing 4-18.The rm -i Switch

Tip

许多发行版使用别名命令rmrm -i来强制删除检查。您可以输入命令alias来查看您的主机上所有当前别名的列表。您也可以创建自己的别名。有关说明,请查看alias命令的手册页。

您也可以使用-r开关递归删除目录及其内容,如下所示:

$ rm -r /home/jsmith/backup

这将删除/home/jsmith/backup目录及其所有内容。

您也可以使用-f或强制开关来覆盖-i开关,如下所示:

$ rm -fr /home/jsmith/backup

这也将删除备份目录及其所有内容,但不会提示您确认删除。使用该开关时要小心,并且在执行命令时要始终意识到自己在目录树中的位置——不恰当使用该命令的结果可能是毁灭性的!

Caution

与 Windows 的回收站不同,在 Linux 上删除文件往往是相当持久的,除非你有备份。有一些方法可以恢复文件,例如: http://unix.stackexchange.com/questions/80270/unix-linux-undelete-recover-deleted-files 。你用这些方法的成功会有很大的不同。您应该非常小心地删除文件,并且应该确保您有一个适当的备份。这在编辑配置文件时尤其重要。在编辑、移动或删除文件之前,请务必备份文件。有时最好移动文件或目录,等待一段时间,然后在确定不需要时删除。

链接文件

在 Linux 主机上,您还可以创建指向文件的链接。链接可以像 Windows 快捷方式一样使用,但有两种形式——硬链接和软链接或符号链接。硬链接不像 Windows 快捷方式。它们是对物理文件的实际引用。如果删除文件的所有硬链接,它们引用的文件也会被删除。

Note

因此,只能在物理分区或硬盘上创建硬链接;您不能链接到位于另一个驱动器或分区上的文件。

软链接或符号链接更像 Windows 快捷方式:如果它们被删除,原始文件仍然存在,只有链接被删除。

使用ln命令创建链接。默认情况下创建硬链接,通过添加-s开关创建软链接。有几种方法可以使用ln命令,但最简单的是创建一个到目标文件的链接,如下所示:

$ ln -s /home/jsmith/myfile

前一行将创建一个名为myfile的符号链接到/home/jsmith/myfile文件。通过查看其手册页,您可以看到其他可以与ln命令一起使用的选项。

编辑文件

Linux 提供了各种各样的文件编辑工具,包括 GUI 和命令行工具。在 GUI 中,您可以找到类似于kate或更简单的gedit的编辑器。这些是简单的编辑器,但它们不是文字处理器——很像 Windows 记事本。还有一些工具允许你从命令行编辑文件,比如流行的vim, nano, joe,或者奇怪的流行的emacs

我们将从快速浏览一下vim开始,这是一个文本编辑器,是一个名为vi的旧 Unix 编辑器的增强版。要编辑一个文件,运行vim命令并指定要编辑的文件名。

$ vim ∼/newfile

Tip

一些发行版还将vim命令别名为vi,以方便习惯旧名称的人使用。

这将在您的主目录中打开一个名为newfile的文件。要在文件中插入一些文本,请键入 I,它是 insert 的缩写。您将看到屏幕底部出现单词-- INSERT --。这意味着您处于插入模式,这允许您添加到文件中。现在,您可以输入文件了。我们输入 hello jsmith。您也可以使用箭头键在文件行和文件间移动。回车键可用于向文件中添加行。

Tip

您可以使用touch命令创建空文件。只需输入touch和您想要创建的文件名,即可创建一个空文件,例如touch /home/jsmith/newfile

完成后,按下 Esc 键。Esc 键使您退出插入模式(您将看到文本--   INSERT  --从屏幕底部消失)。您现在可以通过输入冒号(:)和字母wq作为:wq的组合来保存您添加到文件中的内容。这意味着写和退出。您也可以只指定w,它会写但不会退出。如果您退出回到命令行,您现在可以查看您的文件并看到您键入的文本:

$ cat newfile
hello jsmith

Tip

你可以在 www.openvim.com/ ,找到关于 vim 的介绍,或者你也可以在命令行上运行vimtutor来开始一个vim教程(取决于你是否安装了 vim 增强包)。

各种 GUI 编辑器也是可用的。有些是简单的,风格类似于微软窗口的记事本或写字板应用程序,而其他的是完全成熟的文字处理器和文本编辑器。

Gnome 默认文本编辑器gedit就是一个例子。您可以在 Gnome 中启动gedit,方法是点击应用程序菜单,打开附件标签,然后选择文本编辑器应用程序。这将启动gedit编辑器,如图 4-11 所示。

A185439_2_En_4_Fig11_HTML.jpg

图 4-11。

The gedit editor

这些编辑器的另一个例子是 KDE GUI 附带的凯特( http://kateeditor.org/ ),。在图 4-12 中,你可以看到一个kate打开的 KDE 桌面。

A185439_2_En_4_Fig12_HTML.jpg

图 4-12。

The kate editor

摘要

你学到了什么?首先,Linux 并不难理解,命令行是一个非常强大的工具。与 Window 或 MAC 相比,它包含了许多相似的概念和原则,这将帮助您开始使用 Linux。你应该从本章中吸取的一些重要的东西如下:

  • 您可以使用 LiveCDs 测试很多东西,而不会对您现有的主机和数据带来任何风险,或者使用虚拟机。
  • 学会使用命令行——它有用而且强大(有时你别无选择!).Bash shell 特别强大,您会发现自己不知道没有命令 shell 是怎么过的。
  • 了解更多关于 globbing 和正则表达式的知识——在管理您的主机时,您会发现您的武器库中既有用又强大的工具。
  • 不要使用root用户来管理你的主机。相反,可以使用 sudo 命令。下一章将详细介绍sudo命令。
  • 使用 Linux man页面找到关于每个命令的更多信息。这些都是有用的信息资源。
  • 学习使用vim编辑器(或者您选择的其他编辑器)来帮助您在命令行上处理配置文件。
  • 如果你需要帮助,不要忘记在线资源或 Linux 社区。很多人都在使用 Linux,有人可能已经找到了解决您遇到的问题的方法。使用谷歌或你最喜欢的搜索引擎找到这些解决方案。

在下一章,我们将更多地讨论用户和组,以及如何在您的主机上管理它们。

五、用户和组

第四章介绍了 Linux 基础知识以及用户和用户组的概念。我们解释了用户和组如何拥有文件和对象。我们还演示了所有权如何与权限相结合来控制对这些文件和对象的访问。用户和组也用于启动和运行流程。

在本章中,我们将详细介绍用户和组是如何工作的,从您登录时会发生什么以及如何控制其中的一些过程开始。我们将演示如何创建用户和组,为您提供更多关于密码的信息,并解释 Linux 控制对您的主机的访问的过程。

我们将要谈论的话题包括susudo命令。这些命令允许您以其他用户的身份运行命令,特别是root用户。因此,这些命令允许您避免作为root用户登录来执行管理任务。susudo命令对于安全管理您的主机至关重要。

登录后会发生什么?

在第四章中,我们讨论了让用户登录到 Linux 主机,您看到了一些示例屏幕,显示了如何输入您的用户名和密码。在这一章中,我们将进一步解释当你登录时会发生什么,并且我们将开始探索在这个过程中你可以管理的一些选项和安全控制。

那么,在输入用户名和密码之后,在进入命令行提示符或图形用户界面(GUI)屏幕之前,实际上会发生什么呢?这个过程在不同的发行版之间略有不同,但通常会执行一个名为login的应用程序,并执行以下操作:

  • 检查用户和组是否存在,以及是否允许用户登录
  • 检查是否允许用户从特定位置登录(例如,只有一些用户可以登录到控制台,或者连接到 Linux 主机的屏幕)
  • 检查密码是否正确,如果密码不正确,允许指定次数(通常为三次)的重试
  • 检查密码是否有效,如果密码过期,则提示用户输入新密码
  • 设置环境变量,如用户的主目录和路径
  • 启动 Shell 进程
  • 向用户显示命令行提示符或 GUI 屏幕

在接下来的小节中,我们将带您完成这些过程,并解释如何配置和更改其中的一些步骤以适应您的环境。您将从学习如何创建、删除和管理用户和组开始。

Note

在本章中,我们将演示命令行用户管理,但是如果您喜欢以这种方式管理您的用户和组,我们向您展示的大多数内容也可以从 GUI 工具中获得。在应用程序搜索栏中,键入“用户”,你会找到用户应用程序(CentOS)或用户帐户(Ubuntu)——或者打开设置或系统设置菜单,你会在那里找到它们。

使用用户和组

管理对 Linux 主机的访问的核心是用户和组的概念。我们在第四章中介绍了用户和用户组,您会发现它们很像微软 Windows 平台上的用户和用户组。

您可以通过两种方式组织主机上的用户和组。一种方法是向域中的每个主机添加用户和组;另一种方法是将用户管理集中在一个或两个身份验证服务器上。在本章中,我们将解释前者,在第十六章中,我们将解释后者。

与在 Microsoft Windows 主机上一样,每个需要登录到您的主机的人都需要创建一个用户。许多应用程序,如 web 和邮件服务器,也需要创建用户。当它们启动时,这些应用程序将利用该用户的权利和特权来访问系统资源,如文件、网络或主机的其他方面。

Linux 主机上的每个用户还需要至少属于一个组,但是也可以属于任意数量的其他组。组是用户的集合,因为他们相似或需要访问特定资源而聚集在一起。例如,您组织的销售部门中的所有用户可能属于一个名为sales的组。您可以配置您的主机,确保只有sales组中的用户可以访问销售部门的应用程序和文件。

Note

安装应用程序时,他们通常会安装运行这些应用程序所需的其他用户和组。

使用两个命令很容易创建用户和组:useradd(创建用户)和groupadd(创建组)。此外,我们可以使用两个命令来修改现有的用户和组,它们是usermodgroupmod。最后,为了完成生命周期,可以使用userdelgroupdel命令删除用户和组。

Tip

在 CentOS 和 Ubuntu 上使用用户和组是一个非常相似的过程,使用许多相同的命令和选项。我们会告诉你发行版之间的任何微小变化。

介绍 sudo

在我们开始解释如何创建用户和组之前,我们想讨论一下sudo命令,我们在第四章中已经讨论过一点。sudo命令允许用户运行命令,就好像这个人是以root用户的身份登录的,这是 Linux 的 Windows 管理员帐户。这种能力非常有用,原因有三:

  • 它增加了安全性。
  • 它允许更好地控制特权命令。
  • 它为您提供了更好的审计跟踪,以了解谁在您的主机上做了什么。

Note

在 Ubuntu 上使用sudo而不是root用户的另一个好理由是 Ubuntu 默认不启用root用户。您根本无法以root用户的身份登录。

本章中我们将需要sudo,因为几乎所有用于管理用户和组的命令都需要root用户的特权才能运行。例如,只有root用户可以创建另一个用户。

当您运行sudo命令时,它会提示您输入密码(以确认您确实是您所说的那个人),然后您可以在 CentOS 上使用sudo命令 5 分钟,在 Ubuntu 上使用 15 分钟。这段时间过后,系统会提示您再次输入密码。

Tip

第一次运行sudo命令时,它可能会警告您小心使用sudo命令的威力。

在 Ubuntu 上,sudo命令是可用的,并且是为您在安装 Ubuntu 时创建的用户配置的。如果您以该用户身份登录,您已经可以使用sudo命令了。您还可以通过将其他用户添加到admin组来为他们启用sudo访问。您可以使用usermod命令(您将在本章后面看到更多)将用户添加到组中。

$ sudo usermod -G admin ataylor

这里我们使用了sudousermod命令来修改一个名为ataylor的用户。我们通过指定-G选项和用户要添加到的组的名称,将用户添加到了admin组。(注意,我们已经使用了sudo命令来进行用户修改。唯一允许这样做的用户是您在安装主机时创建的用户;因此,您必须以该用户身份登录才能进行更改。)

在 CentOS 上,如果您没有将用户(jsmith)创建为管理员,默认情况下不会启用sudo命令,您需要启用它。为此,您需要使用一个名为visudo的命令来编辑sudo命令的配置文件/etc/sudoers。为此,您需要作为root用户登录并运行visudo命令。

# visudo

正如您在#命令提示符下看到的,您以root用户的身份登录,并且正在执行visudo命令。这将打开一个看起来很像vivim编辑器的编辑应用程序。该文件中有下面一行:

# %wheel ALL=(ALL) ALL

如上所示,这一行中的#表示您正在处理的行是一个注释。您需要取消对该行的注释。为此,将光标放在#附近,并按下x键两次。这将删除行中的散列(或井号,#)和一个空格字符。完成后,使用与使用vim相同的命令编写并退出文件,方法是键入冒号、:wq,然后输入 Enter 或:wq。这使得名为wheel的组中的任何成员都可以使用sudo命令。然后,您可以将用户添加到wheel组,如下所示:

# usermod –a wheel ataylor

同样,您使用-a选项和您想要最后添加到组中的用户名来指定组wheel。现在ataylor用户可以使用sudo命令了。您也可以使用–G <group1>,<group2>为用户设置群组,这将替换分配给用户的任何现有群组。

创建用户

现在您已经知道如何启用和使用sudo命令,我们可以开始查看用户和组了。让我们首先使用useradd命令创建一个新用户,如清单 5-1 所示。

$ sudo useradd –m –c 'John Smith' jsmith
Listing 5-1.Creating a New User

Note

在清单 5-1 中,您可以看到我们在useradd命令前面加上了sudo命令,以避免作为root用户登录。

命令有很多选项,我们在清单 5-1 中只使用了几个。第一个参数-m告诉主机为用户创建一个主目录。主目录的名称和位置格式通常类似于/home/ username

Tip

例如,您可以用通用配置文件预填充新的主目录。为此,将文件添加到/etc/skel(skeleton 的缩写)目录。创建新的主目录时(使用–m选项),该目录中包含的所有文件都被复制到用户的新主目录中。

–c选项添加了我们新用户的描述。该描述存储在/etc/passwd文件中。所有用户在这个文件中都有一个条目,我们将在本章后面检查这个文件和用于存储组数据的/etc/group文件。最后,我们指定了新用户的名字jsmith

默认情况下,新用户将被禁用,并且没有设置密码。您将需要使用passwd命令更改用户的密码(我们将在本章后面更详细地介绍)。

表 5-1 列出了一些其他有用的useradd命令行选项。

表 5-1。

Some useradd Command-Line Options

| [计]选项 | 描述 | | --- | --- | | `-c` | 添加用户的描述 | | `-d homedir` | 用户的主目录 | | `-m` | 创建用户的主目录 | | `-M` | 不要创建用户的主目录(仅限 CentOS) | | `-s shell` | 指定用户将使用的 Shell |

–d选项允许您指定用户的主目录。主目录是用户登录后所在的目录。这是用户拥有的用于用户文件的私有目录,适用于系统用户(如运行数据库的用户)或普通用户。–M选项告诉 Red Hat 派生的发行版不要创建主目录。该选项强调了在 CentOS 和 Ubuntu 发行版上创建用户的主要区别。在 Red Hat 衍生的发行版中,主目录是自动创建的。

Ubuntu 要求使用–m选项执行useradd命令;否则,不会创建主目录。

Note

关于在 Ubuntu 上创建用户的另一种方法,请参见侧栏“Adduser:Ubuntu 上的另一种方法”。

最后,–s选项允许您为用户指定不同于默认的 shell。

Tip

我们建议您阅读useradd命令的man页面,了解关于该命令的更多详细信息。

用户默认设置

您的新用户也将使用各种默认设置创建(例如,用户 shell 的设置)。那么,useradd命令从哪里得到这些默认值呢?在 CentOS 和 Ubuntu 发行版中,默认值都包含在/etc/default/useradd文件中,您可以使用以下命令显示当前默认值:

$ sudo /usr/sbin/useradd -D

清单 5-2 展示了这个文件的一个例子。

$ sudo cat /etc/default/useradd
# useradd defaults file
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
Listing 5-2.The /etc/default/useradd File

该文件通常在安装主机时默认填充,但您可以修改它以适应您的环境。

Note

当用户登录时,会为系统设置系统范围的默认值。这些可以在/etc/login.defs文件中找到。它包含创建用户时使用的 uid 和 gid 范围等内容。

表 5-2 显示了可以包含在useradd文件中的可能选项。

表 5-2。

The /etc/default/useradd File

| [计]选项 | 描述 | | --- | --- | | `SHELL` | 默认 shell 的路径 | | `HOME` | 用户主目录的路径 | | `SKEL` | 用于提供用户新主目录的默认内容的目录 | | `GROUP` | 默认组 ID | | `INACTIVE` | 密码过期后可以更改密码的最大天数 | | `EXPIRE` | 用户帐户的默认到期日期 |

文件中的每个选项控制一个指定的默认值;例如,shell 选项为用户指定默认 SHELL。HOME 选项指定应该在其中创建所有新主目录的目录。SKEL 选项指定使用哪个目录来填充用户的主目录,正如我们前面讨论的,这默认为/etc/skel。GROUP 选项指定要使用的默认组 ID (GID)。它被设置为 group 100,或者“users”组,通常您不需要更改它。在接下来的部分中,我们将更多地讨论组、成员和 GID。

最后,非活动和过期这两个选项控制两种不同类型的用户帐户过期。非活动值控制用户密码过期后用户可以重置其密码的天数。这允许您指定如果用户的密码过期,用户在被标记为非活动之前有有限的时间重置该密码。然后,用户需要一些交互来重新启用它进行访问。设置为-1 将禁用此设置,设置为 0 将在密码过期后立即禁用该帐户。

Note

我们将在本章后面更多地讨论密码过期。

EXPIRE选项对于创建临时帐户很有用,因为它以YYYY-MM-DD的格式指定了帐户过期和禁用的日期。默认的EXPIRE允许你为所有账户指定一个日期。您也可以使用以下命令在命令行上创建个人帐户:

$ sudo useradd –e 2016-09-15 temp_account

此命令创建一个名为 temp_account 的帐户,该帐户将于 2016 年 9 月 15 日被禁用。

您可以通过执行带有-D选项的useradd命令来更改该文件中的许多默认设置。清单 5-3 向您展示了如何为您的新用户更改默认 shell,表 5-3 显示了可用于-D选项的附加选项。

表 5-3。

The useradd -D Defaults

| [计]选项 | 描述 | | --- | --- | | `-b path/to/default/home` | 指定新用户主目录的路径前缀 | | `-e date` | 指定默认到期日期 | | `-f days` | 指定在密码过期多少天后帐户将被禁用 | | `-g group` | 指定了默认组 | | `-s shell` | 指定了默认 Shell |
$ sudo useradd -D -s /bin/bash
Listing 5-3.Changing useradd Defaults with the -D Option

Tip

您还可以使用 chsh 命令更改您的默认 shell。使用 CentOS 上的chsh -l查看所有可用 Shell 的列表。在 Ubuntu 上,你可以在/etc/shells文件中看到列表。

创建组

我们之前提到过,每个用户必须至少属于一个组。默认情况下,在大多数 Linux 发行版上,包括 CentOS 和 Ubuntu,当您创建一个新用户时,还会创建一个与该用户同名的新组。新用户始终是该组的唯一成员。

但是之前我们说过useradd的默认组是 100?为什么不用那个?如果对 useradd 使用–N(无用户组)选项,或者如果 login.defs 文件中的用户组 _ENAB 设置为“否”,则将分配默认的 100(用户)组。默认情况下,useradd命令将创建一个与用户同名的组。

Note

为每个用户创建一个唯一的组称为用户专用组(UPG)方案。这是一种灵活的管理组权限的模型。你可以在 https://wiki.debian.org/UserPrivateGroups 了解 UPG 的一些细节

在我们的例子中,我们的第一个用户jsmith,将自动属于一个名为jsmith的组。这个组被称为主要组。我们的用户也可以属于其他组,这些附加的组称为补充组。

那么,我们如何判断我们的新用户属于哪个组呢?要检查特定用户的详细信息,我们可以使用清单 5-4 中所示的 id 命令。

$ id jsmith
uid=1001(jsmith) gid=1001(jsmith) groups=1001(jsmith)
Listing 5-4.The id Command

在清单 5-4 中,我们使用id命令查询我们的新用户jsmith。但是这个命令返回了一些关于uidgid的相当神秘的信息,我们用户的名字,以及一些数字。那么这些到底是什么?

每个用户和组在创建时都会被分配一个唯一的用户 ID (UID)和 GID。UID 的范围从 0 到 65535,根用户的 UID 始终为 0。GID 的范围也是从 0 到 65535,其中root用户的 GID 也总是为 0。

如果您为 root 用户运行id命令,您可以在下面一行看到结果:

$ id root
uid=0(root) gid=0(root) groups=0(root)

这显示了 UID 为 0、GID 为 0 的 root 用户。

Note

主机上的每个用户和组都必须有唯一的 UID 和 GID。不能在您的主机上创建与现有用户或组具有相同 UID 或 GID 的用户或组。您的操作系统将自动分配号码,并防止任何冲突。

大多数发行版为特定类型的用户和组保留了号码范围。在你的系统中有两种用户。一类是系统用户,如运行服务的用户和组,例如运行数据库或 web 服务器的用户。所以像“apache”和“www-data”这样的系统用户已经设置并知道了 UID。其他类型的用户是需要登录系统的“人”(例如,您和我)。

CentOS 发行版为分配的系统 UID 保留了从 1 到 200 的 UID 和 GID 范围(如 apache)。它们通过在创建用户时指定 UID 来实现这一点。从 201 到 999 的 UID 用于动态分配的系统 UID—这些是尚未定义 UID 的守护程序,将在安装时选择 UID。这是通过在创建用户时指定--system选项来完成的。普通用户是用 1000 及以上的 uid 创建的。

Ubuntu 为指定的系统用户保留 UID 和 GID 范围 1 到 99。100 到 999,用于动态分配的系统用户(同样通过传递--system选项)。在 Ubuntu 上,第一个新用户的 UID 和 GID 也是 1000。

Tip

您可以控制在/etc/login.defs文件中提供给用户的 uid 和 GID 的范围。编辑 uid 的UID_MINUID_MAX范围以及 GID 的GID_MINGID_MAX范围。你不太可能想这么做,但是选择就在那里。您也可以设置SYS_UID_MINSYS_UID_MAX以及群组。

因此,在清单 5-4 中,我们为jsmith用户执行了id命令,并显示了用户的 UID 1001 和 GID 1001(UID 和 GID 后面的括号中是用户名和组名)。最后一个字段groups,显示主要组和任何补充组。

有两种方法可以将用户添加到一个或多个组中。首先,您可以在创建时使用useradd命令将用户添加到一个或多个组中。其次,您可以使用usermod命令修改现有用户并添加组。

在下面一行,我们将创建第二个名为ataylor的用户,并在创建她时将她添加到一些组中。

$ sudo useradd –m –c 'Anne Taylor' –G printing,finance ataylor

我们已经指定了–G选项,这允许我们提供一个逗号分隔的组列表,我们希望新用户ataylor加入这些组。-G选项允许我们的用户加入除主组之外的其他组,主组是在创建用户并共享她的用户名时创建的唯一组。这里的用户ataylor是一个名为ataylor的独特的 UPG 计划主组的成员,我们试图将她添加到附加的补充组printingfinance

但是,如果我们现在执行该命令,它将会失败,因为在我们可以向这些组添加用户之前,每个组都需要存在,否则我们将会得到一条错误消息,用户将无法创建。这是在 Ubuntu 上的这种情况下会产生的错误消息:

useradd: unknown group printing
useradd: unknown group finance

所以在这种情况下,我们需要首先创建我们的组,我们可以用groupadd命令来完成,如清单 5-5 所示。

$ sudo groupadd printing
$ sudo groupadd finance
Listing 5-5.Creating New Groups

表 5-4 显示了一些可用于groupadd命令的命令行选项。

表 5-4。

The groupadd Command-Line Options

| [计]选项 | 描述 | | --- | --- | | -我也想 | 设置组的 GID。这必须是一个唯一的数字。 | | -r | 创建系统组(GID 在系统 GID 范围内)。 |

如果您希望用特定的数字覆盖自动生成的 GID,请使用-g选项。仅在 CentOS 上提供的–r选项允许您创建一个系统组,并确保为该组分配一个系统组范围内的 GID。

当我们尝试创建ataylor用户时,我们成功了,因为先决条件组现在已经存在。

$ sudo useradd -m -c 'Anne Taylor' -G printing,finance ataylor

我们还可以使用usermod命令将现有用户添加到组中。

$ sudo usermod -a -G accounts ataylor

usermod 命令用于修改现有用户。通过指定–a(用于追加)选项、-G选项和要加入的新组的名称(该组必须已经存在),我们将ataylor用户添加到accounts组。

Tip

您可以使用usermod命令改变用户的许多方面,我们建议您阅读其man页面以获取更多信息。如果用户当前已登录,则在用户注销并再次登录之前,更改不会生效。

还可以使用gpasswd命令来管理组,该命令允许您委派管理组及其成员的责任。您可以为特定用户分配权限,以便在特定组中添加或删除用户。例如,您可以让销售团队中的某个人管理sales组的成员资格。你可以在man页面上更详细地了解gpasswd

删除用户和组

除了创建和修改用户和组之外,您还希望能够删除它们。您可以使用以下两个命令来完成此操作:userdelgroupdeluserdel命令删除用户,groupdel命令删除组。现在让我们删除我们之前使用userdel命令创建的ataylor用户,如清单 5-6 所示。

$ sudo userdel ataylor
Listing 5-6.Deleting a User

userdel命令删除用户,但是默认情况下,它不会删除用户的主目录。您可以使用userdel命令的–r选项强制 Linux 删除用户的主目录。这将删除/home/ username目录和其中的所有文件,但不会删除该目录之外可能也属于该用户的任何文件。userdel命令也不会删除当前登录到主机的用户。

删除拥有文件的用户可能会有问题。如果您删除某个用户,那么该用户的所有对象将不再归该用户所有。您可以识别这些对象,因为用户名将在文件列表中被替换为以前的 UID(这同样适用于任何已删除的组)。因此,如果您创建另一个使用相同 UID 或 GID 的用户,该用户现在将拥有已删除用户的文件。在删除用户之前,确认用户拥有的文件和目录,并确定您将如何处理它们是一个非常好的主意。我们将在本章的后面向您展示如何分配文件和目录的所有权。鉴于此问题,有时禁用用户比删除用户更好。但是如果您决定删除一个用户,您可以运行命令find / -user UID –o –group GID来查找与您刚刚删除的用户相关联的所有文件。

要删除一个组,使用groupdel命令并指定要删除的组的名称。

$ sudo groupdel finance

此命令将从主机中删除该组。需要注意的是,groupdel命令不会删除任何用户的主要组——例如,在删除ataylor用户之前,不能删除ataylor组。如果要删除用户的主要组,必须先删除该用户。与用户一样,删除组会使这些组拥有的文件成为孤立文件。

Adduser: An Alternative on Ubuntu

Ubuntu 附带了两个额外的用户管理工具,adduseraddgroup。这些为useraddgroupadd命令提供了易于使用和方便的替代方法。运行adduser的正常方式是使用你想要创建的新用户的用户名。然后,该实用程序会要求您提供附加信息。例如,让我们为用户 Anne Taylor 添加一个帐户。

$ sudo adduser ataylor
Adding user `ataylor' ...
Adding new group `ataylor' (1001) ...
Adding new user `ataylor' (1001) with group `ataylor' ... Creating home directory `/home/ataylor' ...
Copying files from `/etc/skel' ... Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
Changing the user information for ataylor
Enter the new value, or press ENTER for the default
    Full Name []: Anne Taylor
    Room Number []:
    Work Phone []:
    Home Phone []:
    Other []:
Is the information correct? [Y/n] y

adduser命令要求提供它需要的所有变量,然后用正确的参数调用useradd命令来创建帐户。这意味着即使你使用adduser命令,你在/etc/default/useradd中配置的默认useradd选项仍然有效。

您还可以通过运行以下命令,使用adduser脚本快速将用户添加到组中:

$ sudo adduser username groupname

用户和组都需要已经存在于主机上。

adduser 和 addgroup 脚本本身也可以通过/etc/adduser.conf文件进行配置。默认情况下,他们将创建指定名称的用户,将他们放在同名的组中,创建主目录,并分配最低的可用用户和组 id。

通过 GUI 管理用户和组

CentOS 和 Ubuntu 都有管理用户的 GUI。在 CentOS 和 Ubuntu 上,通过选择应用程序➤系统工具➤设置(见图 5-1 )启动设置面板,即可访问管理用户帐户的 GUI 工具。

A185439_2_En_5_Fig1_HTML.jpg

图 5-1。

Accessing users from the system settings

您可以使用此设置面板在中更改用户。但是首先您需要获得 root 权限(或者在 GUI 中也称为管理员权限)才能在这里更改设置。图 5-2 显示您需要点击解锁按钮,然后会提示您输入密码。

A185439_2_En_5_Fig2_HTML.jpg

图 5-2。

Unlocking the Users settings interface

在图 5-2 中,当前用户的列表在左侧面板中。要添加用户,请单击列表底部的+按钮。您可以通过单击–按钮删除用户。在图 5-3 中,我们添加了一个名为ataylor的新用户。

A185439_2_En_5_Fig3_HTML.jpg

图 5-3。

Adding user ATaylor

在图 5-3 中,我们正在添加用户 ataylor。通过选择允许用户在下次登录时设置密码,ATaylor 将被允许在下次登录时创建自己的密码。我们可以添加两种类型的用户,标准用户或管理员。您还可以选择将用户的企业登录添加到 Active Directory 域或 IPA(身份策略审计)域。在第十六章中,我们将对此进行更深入的探讨。通过单击“Add”按钮,我们将在再次通过系统验证后添加用户 ataylor。

Caution

允许用户在下次登录时创建自己的密码确实会带来安全风险。任何以ataylor身份登录的人都会被提示设置一个新密码,这样,他就可以访问ataylor所做的一切。在这种情况下,最好确保让ataylor尽快登录并确认访问。

最后,在图 5-4 中,我们展示了如何查看用户 JSmith 的登录历史。

A185439_2_En_5_Fig4_HTML.jpg

图 5-4。

Login history for JSmith

在图 5-4 中,我们可以看到 JSmith 在 21:17 退出,并在 21:18 再次登录。使用标题栏中的箭头滚动浏览历史记录,查看更早的时间。

虽然您可以通过此应用程序添加和删除用户,但您不能管理组设置。为此,您必须使用命令行。

Note

请记住,Ubuntu Server 版本默认不附带 GUI,您应该使用提供的命令行工具。Ubuntu 桌面版确实附带了合适的 GUI 工具,因为它默认安装了一个 GUI。

密码

当您通过命令行创建帐户时,不会为该帐户设置密码。您现在可能想要设置或在某个时候更改用户的密码。为此,您可以使用passwd命令。根据运行命令的人的不同,passwd命令有两种工作方式。如果一个普通用户,比如ataylor,已经设置了一个密码并且运行了这个命令,那么系统会提示她修改自己的密码。您可以在清单 5-7 中看到passwd命令的运行。

$ passwd
Changing password for ataylor.
(current) UNIX password:
Enter new UNIX password:
Retype new UNIX password:
Listing 5-7.Changing Your Password

你输入当前密码,然后输入新密码。系统会提示您输入两次新密码,以确保其正确无误。你还必须提供一个合适的密码。默认情况下,在大多数发行版中,会执行一些基本的密码检查,以防止您提供一个脆弱的或容易猜到的密码。在大多数发行版中,这些检查通常如下:

  • 最小密码长度为四个字符
  • 不是回文(即最后一个密码的反码)
  • 与之前的密码不同,只是大小写发生了变化(即,从密码到密码)
  • 一些基本的相似性检查
  • 基于密码长度和字符组合(全是字母、全是数字等)的一些简单测试。)
  • 简单的旋转检查(即旋转密码中的字母,例如ginger被更改为ingerg)

如果您提供的密码不够复杂,您会收到一条错误消息,指出您的密码有什么问题。然后会提示您提供一个更容易接受的密码。

或者,如果您以 root 用户身份运行 passwd 命令,您可以更改其他用户的密码,如清单 5-8 所示。

$ sudo passwd jsmith
Listing 5-8.Changing Someone Else’s Password

在清单 5-8 中,passwd命令提示您为用户jsmith提供一个新密码。

Tip

值得注意的是,作为root用户,您可以忽略任何关于错误密码的警告,并将用户密码更改为弱密码或容易被猜到的密码。

密码老化

密码时效允许您指定密码有效的时间段。该时间段到期后,用户将被迫选择新密码。这有利于确保密码定期更改,并且被盗、被破解或被前雇员知道的密码将具有时间限制值。不幸的是,对于许多用户来说,定期更改密码的需要增加了他们写下密码的愿望。我们建议您对大多数密码使用 30 到 60 天的密码期限,这取决于主机的性质和鼓励密码管理员。更重要的是,主机应该具有更短的密码到期期限(例如 30 天),而不太重要的主机可以具有更长的期限。一些组织选择单个过期期限,以便该期限对所有主机上的所有用户保持一致。

有两种方法可以处理密码老化。第一种方法使用名为 chage 的命令行工具来单独设置或更改用户帐户的密码到期时间。清单 5-9 显示了这个命令。

$ sudo chage -M 30 ataylor
Listing 5-9.The chage Command

清单 5-9 使用-M选项将用户ataylor的密码有效期设置为 30 天。30 天后,用户密码将过期,系统会提示用户输入新密码。表 5-5 显示了其他几个你可以设置的变量。

表 5-5。

The chage Command Flags

| [计]选项 | 描述 | | --- | --- | | `-m days` | 设置密码更改的最小间隔天数。零允许用户随时更改密码。 | | `-M days` | 设置密码保持有效的最大天数。 | | `-E date` | 设置用户帐户过期并自动停用的日期。 | | `-W days` | 设置在密码过期前警告用户更改密码的天数。 | | `-d days` | 设置自 1970 年 1 月 1 日以来最后一次更改密码的天数。 | | `-I` `days` | 设置密码过期后帐户被锁定的天数。 |

Tip

在 Unix/Linux 世界中,您会多次遇到日期 1970 年 1 月 1 日。这个日期也称为 Unix 纪元或 Unix 时间。它用于描述时间点,从 1970 年 1 月 1 日开始以秒为单位进行测量(例如 1229519557)。您可以使用命令date +%s在您的主机上找到 Unix 时间。

第一个选项-m,允许您指定密码更改之间的最短时间。设置为 0 允许用户随时更改密码。选项-W指定用户密码到期前的天数,用户将收到密码即将到期的警告。-d选项主要用于立即终止密码。通过将-d选项设置为0,用户的最后一次密码更改日期变为 1970 年 1 月 1 日,如果–M选项大于0,则用户必须在下次登录时更改密码。最后一个选项-I提供了一个以天为单位的时间框架,在该时间框架之后,密码过期且未更改的用户帐户将被锁定,从而无法用于登录。

如果不带任何选项运行chage,并且只指定用户,它将启动一系列交互式提示来设置所需的值,如清单 5-10 所示。方括号[ ]之间的值表示该用户密码时效设置的当前值。

$ sudo chage ataylor
Changing the aging information for ataylor
Enter the new value, or press return for the default
Minimum Password Age [0]:
Maximum Password Age [30]:
Last Password Change (YYYY-MM-DD) [2016-06-27]:
Password Expiration Warning [7]:
Password Inactive [-1]:
Account Expiration Date (YYYY-MM-DD) [2016-07-28]:
Listing 5-10.Running chage Without Options

用户还可以使用带有-l选项的chage命令来显示密码何时到期。

$ chage -l ataylor

另一种处理密码老化的方法是在/etc/login.defs文件中为所有用户设置默认值。

清单 5-11 显示了/etc/login.defs中可用于密码老化的控件。

PASS_MAX_DAYS    60
PASS_MIN_DAYS    0
PASS_WARN_AGE    7
Listing 5-11.The login.defs Password-Aging Controls

在清单 5-11 中,我们使用PASS_MAX_DAYS选项将最长密码期限设置为 60 天,通过将PASS_MIN_DAYS选项设置为 0,允许用户随时更改密码,并使用PASS_WARN_AGE选项警告用户其密码将在密码到期日期前七天到期。

禁用用户

作为root用户,您还可以使用passwd命令来禁用和启用使用–l或锁定选项的用户帐户。例如,考虑以下情况:

$ sudo passwd –l ataylor

前面的命令将锁定ataylor用户,并阻止ataylor使用她的密码登录主机。然后,您可以使用–u或解锁选项解锁用户。

$ sudo passwd –u ataylor

但是,此命令不会完全禁止对主机的访问。用户可以通过其他身份验证机制访问主机,例如使用 SSH(安全 Shell)进行远程访问的公钥。

还有一种方法可以完全禁止用户使用带有--expiredate选项的usermod命令:

$ sudo usermod --expiredate 1 ataylor

这会将帐户到期日期设置为 1970 年 1 月 1 日,并立即禁用该帐户。用户现在无法在主机上执行任何操作。

最后,您可以将登录 shell 设置为/bin/false/usr/sbin/nologin。这不会锁定用户,但会禁止用户访问 shell。

$ sudo usermod –s /bin/false ataylor

Note

您还可以将用户的 shell 设置为命令。例如,您可以将用户的 shell 设置为/bin/mail命令,这是一个小的命令行邮件阅读器。当用户登录时,她只能访问该命令。

存储用户数据

首先,您的主机检查您的用户是否存在并被允许登录。Linux 发行版将用户、组和其他信息的详细信息存储在主机上的三个文件中:/etc/passwd/etc/shadow/etc/group。您通常不需要编辑这些文件,因为有一些命令和工具允许您添加、删除和管理用户和组。然而,知道它们包含什么信息是有用的。

Tip

如果您使用其他形式的身份验证,如 NIS(网络信息服务)、LDAP(轻量级目录访问协议)或 Active Directory,我们将在第十六章中介绍,那么您的主机通常会查询这些身份验证存储之一,以确认您的用户存在并被允许登录。

第一个文件/etc/passwd,包含所有用户及其详细信息的列表。清单 5-12 显示了一些passwd条目的例子。

root:x:0:0:root:/root:/bin/bash
daemon:x:2:2:daemon:/sbin:/sbin/nologin
Listing 5-12.
/etc/passwd Entries

每个条目都可以分成几个组成部分,用冒号分隔。

username:password:UID:GID:GECOS:Home Directory:Shell

用户名最多可包含 32 个字符,区分大小写(尽管通常都是小写)。下一个字段中的 x 是密码的标记。实际的密码存储在/etc/shadow文件中,我们将在侧栏“影子密码”中讨论它

接下来是 UID 和 GID。如前所述,在 Linux 主机上,每个用户帐户和组都被分配一个数字 ID;为用户分配一个 UID,为组分配一个 GID。根据发行版的不同,编号较低的 uid 和 GID 表示系统帐户和组,如 root 或 daemon。在 CentOS 和 Ubuntu 上,你通常会看到系统账户 uid 和 GID 低于 1000。

Note

如前所述,root用户的 UID 和 GID 是0。这应该是主机上 UID 和 GID 为 0 的唯一用户。

下一项是 GECOS 或注释字段。该字段通常包含诸如用户名、办公室位置和电话号码等数据。如果 GECOS 字段中有多个数据项,则用逗号分隔每个数据项。

接下来是用户的主目录。正如我们在第四章中描述的,它通常位于/home目录中(例如/home/jsmith)。

最后一项是用户的默认 shell。正如我们在第四章中讨论的,shell 是一个命令行环境,用户通过它与主机交互。每个 shell 都是通过运行二进制文件来初始化的。例如,要启动 Bash shell,需要执行/bin/bash二进制文件。这个二进制文件在/etc/passwd文件中指定。如果默认 shell 指向一个不存在的文件,那么用户将无法登录。

清单 5-12 中的第二行使用 shell /sbin/nologin,这是一个伪 shell,它不仅阻止用户登录,还记录对syslog守护进程的登录尝试。

Note

syslog守护进程是 Linux 日志服务器。它从操作系统和应用程序接收日志条目,并将它们写入文件,通常在/var/log目录中。我们将在第十八章中讨论更多关于登录的内容。

除非用户被设置为不登录,否则大多数用户都会有一个 shell 条目,引用启动其 shell 的二进制文件,例如,/bin/bash

Shadow Passwords

您可能已经注意到在/etc/passwd中没有出现密码,而是出现了字母x。这是因为现代发行版使用影子密码来处理密码管理。

以前,密码作为单向散列存储在/etc/passwd中,这提供了有限的安全性,并将用户名和密码暴露给暴力破解方法。暴力破解是一种攻击密码的方法,其中尝试数千或数百万个不同的密码,直到找到匹配的密码。/etc/passwd文件特别容易受到这种攻击,因为应用程序对它的使用要求它对所有用户都可读,或者对所有人都可读。当 passwd 文件的副本可能从主机上被窃取并在离线时被暴力破解时,这尤其危险。鉴于这种类型的密码存储在passwd文件中的安全性较低,现代计算机可以在几分钟内破解简单的密码,或者在几天内破解更难的密码。

影子密码通过将用户和密码分开并将密码作为散列存储在/etc/shadow文件中来帮助降低这种风险。默认情况下,使用 SHA512 哈希。这些 SHA512 哈希更难破解,为了进一步保护密码,/etc/shadow文件由root用户所有,root是唯一可以访问该文件的用户。下一行显示了来自shadow文件的一个典型行:

root:$6$RwETwzjv$ifht......7L/HiLCPR8Zc935fd0:13675:0:99999:7:::

您也可以将shadow文件分解成组件,像passwd文件一样,用冒号分隔每个组件。shadow文件的组成如下:

  • 用户名
  • 密码
  • 上次更改密码的日期
  • 密码更改的最小间隔天数
  • 密码过期时间(天)
  • 密码过期警告期限(天)
  • 密码过期后该帐户被禁用的天数
  • 自帐户被禁用以来的日期

用户名与passwd文件中的用户名相匹配。密码本身是加密的,两种类型的特殊字符可以告诉您可以作为密码字段前缀的用户帐户的状态。如果密码字段以!*为前缀,则帐户被锁定,用户不允许登录。如果密码字段以!!为前缀,则从未设置过密码,用户无法登录主机。其余条目涉及密码老化,我们将在“密码老化”一节中讨论这些内容

存储组数据

在 Linux 主机上,关于组的信息存储在/etc/groups文件中。清单 5-13 展示了这个文件的一个例子。

root:x:0:root
ataylor:x:501:finance,printing
Listing 5-13.Sample of the /etc/groups File

/etc/group文件的结构很像/etc/passwd文件,数据用冒号分隔。该文件分为组名、密码、GID 和该组成员的逗号分隔列表。

groupname:password:GID:member,member

组文件中的密码允许用户使用newgrp命令登录该组。如果启用了影子密码,那么像passwd文件一样,组文件中的密码被替换为 x,真实密码存储在/etc/gshadow文件中。

Login Messages

用户首先看到的是你的登录界面。在登录屏幕中放置一些重要的警告和信息是一个好主意。为此,您需要编辑/etc/issue/etc/issue.net文件的内容。当您通过主机控制台上的命令行登录时,会显示问题文件,当您通过 SSH 会话登录到命令行时,会显示issue.net文件。大多数发行版都使用这些文件,包括 CentOS 和 Ubuntu。这些文件可以包含纯文本和特殊转义字符的组合,例如,允许您输出颜色、换行和回车。

您还应该包括一条警告消息,说明未经授权访问主机是被禁止的,并且将被起诉。您可以在文件中使用一系列转义字符中的一个来用来自您的主机的数据填充登录屏幕。我们建议您使用如下所示的登录消息:

^[c
\d at \t
Access to this host is for authorized persons only.
Unauthorized use or access is regarded as a criminal act
and is subject to civil and criminal prosecution. User
activities on this host may be monitored without prior notice.

^[c 转义字符清除屏幕,\d 和\t 转义字符分别显示主机上的当前日期和时间。如果你查看问题、issue.net 和 getty man页面,你还可以找到其他的转义字符。

除了/etc/issue/etc/issue.net文件之外,/etc/motd文件的内容会在命令行登录后直接显示,您可能希望调整它们以包含可接受的使用策略或类似信息。

如果您使用的是贵由,可以在登录屏幕上设置消息。你可以按照这些 CentOS 的指令来做: https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Desktop_Migration_and_Administration_Guide/customizing-login-screen.html

对于 Ubuntu,您需要看到以下内容: http://askubuntu.com/questions/193357/how-do-i-create-a-popup-banner-before-login-with-lightdm .

配置您的 Shell 和环境

在用户被认证和授权之后,他们的 shell 被启动。大多数 shell 都是高度可定制的,许多用户最终会调整 shell 环境的各个方面,以帮助他们更快、更有效地工作。

Bash shell 从/etc/profile文件中读取它的初始配置。该文件通常包含对其他全局配置文件的引用,用于为主机上除 root 用户之外的所有用户配置 Bash。最后,处理用户主目录中的所有配置文件。.bash_profile.profile文件是最常用的,每个用户的主目录中都有这些文件。

Note

您可以在bash man页面的INVOCATION部分查看其他配置文件的完整列表。

环境变量

定制 shell 的一个主要原因是设置环境变量。这些变量充当许多应用程序使用的默认选项。他们可以定义一些特性,比如你喜欢的文本编辑器,你喜欢的语言,以及用ls列出文件和目录时使用的颜色。您还可以定义自己的变量,用于自己的脚本。

要获得所有环境变量的完整列表,使用env命令。表 5-6 列出了最常见的定制变量。

表 5-6。

Environment Variables

| 名字 | 用于 | | --- | --- | | `HOME` | 用户的主目录 | | `LANG` | 定义应用程序应该使用的语言文件 | | `LS_COLORS` | 定义`ls`命令使用的颜色 | | `MAIL` | 用户邮箱的位置 | | `PATH` | 一个用冒号分隔的目录列表,shells 在其中查找可执行文件 | | `PS1` | 定义正常提示 | | `SHELL` | 当前 Shell | | `_` | 包含在此会话中执行的最后一个命令 |

您可以通过echo命令显示环境变量的内容。在想要显示的变量名称前加上$

$ echo $PS1
\u@\h:\w\$

前面是一串特殊的转义码,在提示符下显示用户名\u,主机名\h,当前工作目录\w,以及最后一个字符\$\$如果提示显示为 root 用户,则显示一个英镑(#)符号,否则显示一个美元符号($)。有关可用转义码的完整列表,请参见bash man页的PROMPTING部分。

您可以通过在任何 Bash 配置文件中定义环境变量或者从命令行设置它们来更改环境变量。如果我们想改变我们的提示,使其包含一个时间戳,并打破它,给我们更多的空间来键入命令,我们可以添加\T\n代码:

$ PS1="[\T] \u@\h:\w\n\$ "
[12:50:59] jsmith@au-mel-ubuntu-1:∼
$

Tip

你可能已经注意到我们有时用$有时不用。这里的简单规则是,如果我们引用变量并在它前面加上$前缀,那么我们感兴趣的是变量的值(即变量的内容)。没有$,我们说的就是变量本身。

另一个有用的例子是在路径中添加目录。您可以快速在路径中添加前缀或后缀目录。您可以在路径的开头添加一个目录,如下所示:

$ PATH=/home/ataylor/scripts:$PATH

这里,我们将目录/home/ataylor/scripts添加到路径的前面,然后通过用冒号分隔并指定$PATH值来包含现有路径。这允许您将二进制文件、脚本或其他应用程序放入路径中,每次运行命令或应用程序时都会搜索该路径。在这种情况下,当执行命令时,Linux 会先在/home/ataylor/scripts目录中查找命令,然后再在主机上的其他地方查找。

您可以使用相同的基本结构将目录添加到路径的末尾:

$ PATH=$PATH:/home/ataylor/scripts

然后,当您运行一个命令时,Linux 将搜索您的路径中的所有目录,如果没有找到匹配的命令或应用程序,它将搜索您的后缀目录。

任何类型为KEY=value的字符串都被 Bash 假定为环境变量赋值。将变量改为大写是一种惯例。

当然,在命令行上设置环境变量只会在会话期间改变它们。如果您注销,它们将恢复到以前的配置。要使这样的更改永久化,请将它们放在位于您的主目录中的.bash_profile文件中,例如,

PATH=$PATH:/home/ataylor/scripts
export PATH

这里我们指定了新的路径,然后使用一个特殊的命令export来传播更改。通常情况下,对环境变量的更改只会更改进行更改的当前会话或脚本。为了在其他会话或脚本中使用它们,您需要导出它们。为了对所有用户进行路径或其他环境更改,将更改添加到/etc/profile文件中,或者通常将这些更改添加到/etc/profile.d/目录中的文件中。该文件由所有用户使用(除了root用户;使用/root目录下的.bash_profile文件修改root用户变量)来设定值。

Tip

你可以在 http://tldp.org/HOWTO/ Bash-Prompt-HOWTO/找到更多关于配置你的 Bash 提示符的信息。

命令别名

配置 shell 的第二个原因是创建命令别名。别名允许您为常用命令创建快捷方式或设置默认选项。一个主要的例子是我们在第四章中看到的ls命令的别名。

当用ls列出文件时,我们在第四章中看到,我们得到了一个目录中文件的简单列表。通过使用别名,您可以让 shell 在每次键入ll时执行ls -lah,这样您就可以一直拥有一个完整的目录列表。您可以通过alias命令创建一个别名。

$ alias ll='ls -lah'
$ ll
total 40K
drwx------. 4 vagrant vagrant 4.0K Mar  9 06:07 .
drwxr-xr-x. 4 root    root    4.0K Mar  3 05:37 ..
-rw-------. 1 vagrant vagrant 2.2K Mar  9 06:08 .bash_history
-rw-r--r--. 1 vagrant vagrant   18 Nov 20 00:02 .bash_logout
-rw-r--r--. 1 vagrant vagrant  225 Mar  9 06:04 .bash_profile

您可以通过将别名添加到主目录中的.bash_profile配置文件来使其永久化。

要获得 shell 中定义的所有别名的列表,运行不带任何参数的alias命令。

$ alias
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -lah'
alias ls='ls --color=auto'

在这里,您可以看到我们定义的列表别名、其他列表别名,以及我们从/etc/profile.d/colorgrep.csh文件中获得的几个别名。

除非设置默认选项,否则不应定义与现有命令同名的别名。通过指定可执行文件的完整路径,您仍然可以运行原始命令,但是这可能会在您没有预料到的时候产生令人讨厌的意外(例如,在自动化脚本中)。

要删除别名,使用unalias命令。要删除我们的交互式删除,我们将使用:

$ unalias ll

要了解有关别名的更多信息,请参见bash man页面的ALIASES部分。

Bash shell 极其强大和灵活,它可以使日常管理任务变得非常容易。如果你想知道更多关于 Bash 的知识以及你可以用它做什么,请看 www.tldp.org/LDP/Bash-Beginners-Guide/html/http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html

控制对主机的访问

您可以控制相当多的用户特征,包括用户何时以及如何登录,他们的密码是什么样的,以及他们更改和重置密码的频率。这些控件都是用户登录主机时检查的,一般由一系列模块管理。这些模块统称为可插拔身份验证模块(PAM)。几乎所有的 Linux 发行版,包括 CentOS 和 Ubuntu,都依赖 PAM 来控制用户如何以及何时与主机交互。

在本节中,我们将向您介绍 PAM 及其工作原理。您通常不需要对 PAM 配置进行太多的更改,但是理解它的工作原理是很重要的。

Note

我们将在第十六章中详细讨论 PAM 如何与其他认证机制一起使用(例如,与 Active Directory 和 LDAP 的集成)。

PAM 最初是由 Sun Microsystems 设计的,用于提供插件认证框架。它在 Linux 世界中得到了大量的使用和开发,并且存在大量的 PAM 模块来执行从检查密码到创建主目录的各种功能。PAM 模块最初用于为缺乏身份验证或特定身份验证功能的应用程序提供身份验证和其他服务。后来,随着更复杂的身份验证类型的出现,如智能卡和一次性密码(或令牌),PAM 成为集成和扩展身份验证机制的一种方式。不必为新的身份验证方法重写每个应用程序,只需添加 PAM 支持。然后,PAM 负责通过标准 API 进行身份验证的艰巨工作。

配置 PAM

本质上,PAM 是一个身份验证和授权检查的层次结构,当应用程序想要执行某个操作时,就会执行这些检查。这些支票叠在一起;例如,当登录时,我们检查用户是否存在,然后检查用户的密码是否有效,然后检查密码是否过期。这个堆栈通常由多个 PAM 模块组成,每个模块执行一些检查功能。此外,某些检查必须通过(例如,您的用户必须存在),而其他检查可能是可选的。理解 PAM 的最好方法是检查一些 PAM 配置文件。

在大多数 Linux 发行版中,您有两个可能的位置来查找 PAM 配置信息。遗留文件/etc/pam.conf用于保存 Linux 发行版上的 PAM 配置信息,但是现在它已经被弃用,并被目录/etc/pam.d所取代。大多数现代版本的 CentOS 和 Ubuntu 都使用这个目录来保存 PAM 感知服务的配置文件集合。该服务与其要验证的应用程序同名;例如,passwd命令的 PAM 配置包含在一个名为/etc/pam.d/passwd的文件中。这些文件称为服务配置文件。

有各种各样的服务配置文件——例如,当用户登录到主机时,我们使用一个名为login的应用程序。当用户登录时,登录应用程序被触发,在pam.d目录中,您会发现一个名为login的文件,其中包含应用程序的认证配置。类似地,您会发现一个名为sshd的文件,它为通过 SSH 连接登录的用户执行类似的工作。

默认 PAM 配置中的其他常见服务以及在/etc/pam.d目录中找到的服务是passwd命令和 cron 调度守护进程。在这些文件中,您会发现这些应用程序使用的身份验证配置。

Note

我们将在第六章讨论 crontab 以及如何调度作业和动作。

不过,我们不会查看每个特定的文件,因为大多数服务都依赖于一些通用的身份验证配置。CentOS 和 Ubuntu 都有单独的文件定义通用认证配置。许多服务文件都引用并包含这种通用配置。在 CentOS 上,这个文件是/etc/pam.d/system-auth,它是在您安装主机时自动生成的,并使用一个名为authconfig的特殊命令进行更新。在 Ubuntu 上,同样的角色由四个独立的文件执行:common-authcommon-passwordcommon-sessioncommon-account。让我们看看清单 5-14 中 CentOS system-auth文件的内容。

#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required      pam_env.so
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 1000 quiet_success
auth        required      pam_deny.so

account     required      pam_unix.so
account     sufficient    pam_localuser.so
account     sufficient    pam_succeed_if.so uid < 1000 quiet
account     required      pam_permit.so

password    requisite     pam_pwquality.so try_first_pass
local_users_only retry=3 authtok_type=
password    sufficient    pam_unix.so md5 shadow nullok
try_first_pass use_authtok
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
-session     optional      pam_systemd.so
session     [success=1 default=ignore] pam_succeed_if.so service
in crond quiet use_uid
session     required      pam_unix.so

Listing 5-14.The 
login PAM File

system- auth和其他服务配置文件有四个可能的指令。让我们使用清单 5-14 中的一行来更详细地检查它们。

auth         sufficient    pam_unix.so nullok try_first_pass

我们行中的第一个指令是 auth,这是我们正在配置的管理组。PAM 中有四个主要的管理组,它们代表了可以配置的身份验证和授权过程的不同部分:

  • auth:这些模块执行用户认证,例如检查密码。
  • account:该管理组处理帐户验证任务,例如,确认用户帐户是否解锁,或者是否只有根用户可以执行操作。
  • 这些模块设置密码,例如,检查以确保您的密码足够强。
  • 这些模块检查、管理和配置用户会话。

Note

记得我们讨论过设置强而复杂的密码;密码管理组是您可以设置复杂性密码规则的地方之一。

通常为每个管理组分配一个或多个模块,通常按照指定的顺序检查这些模块,每个模块将返回成功或失败的结果。在 PAM 配置中,一个特定的模块也可能被多次指定。例如,在清单 5-14 中,您可以看到所有四个管理组都指定了pam_unix.so模块。

auth        sufficient    pam_unix.so nullok try_first_pass
account     required      pam_unix.so
password    sufficient    pam_unix.so m5 shadow nullok try_first_pass use_authtok
session     required      pam_unix.so

这表明pam_unix.so模块可以为每个管理组执行检查和功能,该模块负责大多数标准 Unix 身份验证功能,如输入传统密码。例如,它可以确认用户在 auth 组中的密码是正确的,还可以确认该用户存在于account组中。

下一个指令sufficient称为控制标志,它告诉 PAM 如何处理模块的结果。如前所述,有些检查比其他检查更重要。控制标志告诉 PAM 如何处理成功或失败的结果,以及该结果如何影响整个身份验证过程。表 5-7 列出了四个 PAM 控制标志。

表 5-7。

PAM Control Flags

| 旗 | 描述 | | --- | --- | | `required` | 要使身份验证成功,必需的模块必须成功。 | | `requisite` | 如果一个必要的模块失败,那么认证将立即失败。 | | `sufficient` | 如果模块成功,认证立即成功。 | | `optional` | 模块的成功或失败不影响认证。 |

required标志表示模块结果必须成功,认证过程才能成功。如果这个模块的结果是失败,那么整个认证也是失败的。如果多个模块堆叠在一起,堆栈中的其他模块也将被处理,但整体身份验证仍然会失败。

requisite标志还指示模块结果必须成功,认证才能成功。此外,与required标志不同,该模块的成功或失败将立即通知请求认证的服务,认证过程将完成。这意味着,如果任何模块堆叠在一起,并且一个带有requisite控制标志的模块出现故障,那么剩余待处理的模块将不会被执行。相比之下,利用所需的控制标志,堆栈中的剩余模块继续被处理。

下一个控制标志是sufficientsufficient标志意味着该模块的成功足以使认证过程成功,或者如果模块被堆叠,则足以使堆叠成功。这取决于在该模块出现故障之前没有处理其他所需的模块。然而,如果一个sufficient模块出现故障,那么整个堆栈不会出现故障。

最后一个控制标志是optional。一个optional模块对于认证过程或模块堆栈的整体成败并不重要。它的成功或失败不会决定整个认证过程的成功或失败。

下一个指令pam_unix.so指示将使用什么 PAM 模块及其位置。如果您指定一个没有路径的 PAM 模块,那么该模块被假定位于/lib/security目录中。您还可以通过提供模块的路径,从这里的另一个位置指定模块,如下面一行所示:

auth required /usr/local/pamlib/pam_local.so id=-1 root=1

最后的指令是要传递给 PAM 模块的参数——在本例中,我们将参数try_first_passnullok传递给pam_unix.so模块。try_first_pass参数告诉模块查看模块是否已经收到密码,如果收到,则使用该密码进行身份验证。nullok参数告诉模块可以使用空白密码。大多数模块将忽略传递给它们的无效或不正确的参数,模块将继续被处理,尽管有些模块确实会生成错误消息或失败。

Tip

您可以找到大多数 PAM 模块的man页面(例如,man pam_unix将返回pam_unix man页面)。也可以在 www.linux-pam.org/Linux-PAM-html/Linux-PAM_SAG.html/ 找到文档。

还有最后一个我们需要提到的 PAM 函数:includeinclude函数允许您将一个 PAM 文件包含在另一个中。这就是我们的通用配置被包含在特定服务配置文件中的方式。为了查看这个函数,让我们来看看清单 5-15 中 Ubuntu login PAM 服务配置文件的一个片段。

@include common-auth
@include common-account
@include common-session
@include common-password
Listing 5-15.The Ubuntu login PAM

Service Configuration File

该文件中有更多的配置,但是我们可以看到,使用格式@include,您可以在 PAM 服务配置文件中包含其他文件。所以@include common-account会将文件common-account的内容包含在login文件中。当使用login文件时,该文件中指定的每个模块将被处理。文件在被包含的地方被拉进来并被解析,被包含的文件中的任何模块都按顺序执行。

您也可以使用include选项作为控制标志,如下所示:

auth    include system-auth

这将包括来自文件system-auth的所有auth类型行。

更多关于须藤的信息

正如本章前面所讨论的,sudo命令允许您以另一个用户的权限运行一些命令,在大多数情况下是root用户。该命令的工作方式很像微软 Windows 中的RunAs命令,允许一个用户以另一个用户的身份运行命令。

Note

另一个名为su的命令,也称为替代用户或切换用户,允许您以特定用户的身份打开子 shell。通常用来改变root用户来执行某个动作。你可以通过它的man页面了解它。请注意,如果root账户被锁定,那么su将无法工作,因为它在 Ubuntu 上。您可以通过为root用户设置密码来解锁帐户,但通常您只会使用sudo su来临时打开根 subshell。使用sudo su <user>,您不需要知道您试图访问的用户的密码,因为您通过sudo使用权限。

要使用这个命令,您可以键入sudo,然后键入您想要执行的命令。如果您被允许在那个命令上运行sudo,您将被提示输入一个密码,通常是您自己的用户密码。那么指定的命令将以root用户的身份执行,除非您使用–u <username>选项为 sudo 指定一个不同的用户..这允许您执行root用户可以执行的操作,比如创建用户,而不需要以root用户的身份登录。您可以在清单 5-16 中看到sudo在工作。

$ sudo userdel ataylor
We trust you have received the usual lecture from the local System Administrator.
It usually boils down to these three things:
    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.
Password:
Listing 5-16.
Using sudo

这个相当吓人的消息通常在你第一次使用sudo命令时出现;之后,您只会得到密码提示。

正确输入密码后,sudo命令不会在每次使用时提示您输入密码。输入密码后,sudo命令会给你一个宽限期,在此期间不会提示你输入密码。这个时间段在 CentOS 上是 5 分钟,在 Ubuntu 上是 15 分钟。过了这段时间后,当您下次运行sudo命令时,系统会再次提示您输入密码。

在 Ubuntu 和 CentOS 上,默认安装sudo命令。Ubuntu 其实连root用户的密码都不设置;相反,我们鼓励您总是使用sudo来运行特权命令。Ubuntu 上 admin 组的任何成员都可以运行sudo命令。在 CentOS 上,我们之前对其进行了配置,如果您是wheel组的成员,那么您可以使用sudo命令运行命令。

sudo命令也是高度可配置的。您可以使用sudo命令精确地指定可以执行哪些命令,包括将命令分组为不同的命令类别。您可以配置sudo命令,允许用户执行所有命令、部分命令,甚至执行命令时不提示输入密码(尽管不建议这样做)。

Tip

正如你在本章开始时学到的,你可以通过使用一个叫做visudo的特殊命令编辑一个叫做/etc/sudoers的文件来配置sudo能做什么。我们强烈建议你只使用visudo来编辑这个文件,因为糟糕的配置会使所有的sudo命令失败。我们将在“配置 sudo”一节中详细讨论这一点

那么,如果不允许您执行sudo命令,会发生什么呢?在清单 5-17 中,我们尝试以用户ataylor的身份使用sudo命令,他没有使用sudo命令的正确权限。

$ sudo useradd –m –c 'Illegal User' iuser
ataylor is not in the sudoers file. This incident will be reported.
Listing 5-17.
Unauthorized sudo

使用sudo命令的失败尝试将被你的主机的syslog(或系统日志程序)服务记录,然后消息被发送到/var/log/目录中的一个文件。在 CentOS 上,你可以在/var/log/secure文件中看到sudo命令失败,在 Ubuntu 上它们出现在/var/log/auth.log文件中。将生成一条日志消息,显示日期、时间、试图执行sudo命令的用户以及用户试图执行的未授权命令。

Sep 1 20:27:43 au-mel-centos-1 sudo:       ataylor : user NOT in sudoers ; TTY=pts/1 ;
PWD=/home ; USER=root ; COMMAND=/usr/sbin/useradd –m –c 'Illegal User' iuser

这些消息允许您监控试图在您的主机上执行不适当操作的人,并且它们可用于检测企图的安全漏洞。

Note

在第十八章中,我们将更多地讨论日志记录,以及如何监控类似本节中详述的消息,并发送警报或采取某种措施。

配置 sudo

sudo命令检查/etc/sudoers文件以获得运行命令的授权。使用visudo,您可以配置sudoers文件来限制特定用户、特定命令和特定主机的访问。

我们来看一下/etc/sudoers文件。首先,你需要使用命令visudo来编辑/etc/sudoers文件。visudo命令是一个特殊的编辑器,设计用来和sudo命令一起使用,它是编辑sudoers文件最安全的方式。该命令锁定文件以防多次同时编辑,提供基本的健全性检查,并检查任何解析错误。如果当前正在编辑/etc/sudoers文件,您将收到一条消息,提示您稍后再试。

我们将从如何允许用户ataylor运行userdel命令开始。我们已经将清单 5-18 的内容添加到sudoers文件中。

ataylor ALL=/bin/userdel
Listing 5-18.Sample sudoers

我们可以把这条线分解成它的组成部分。

username host = command

清单 5-18 显示用户ataylor被允许在所有主机上(使用变量ALL)使用命令/bin/userdel,就好像她是根用户一样。在命令选项中指定的任何命令都必须用其完整路径来定义。您还可以指定多个命令来授权使用,每个命令之间用逗号分隔,如下一行所示:

ataylor ALL=/bin/userdel,/bin/useradd

在前一行中,ataylor现在被授权使用userdeluseradd命令,就像她是root用户一样。在sudoers文件中的所有配置行必须只在一行,你可以使用\来表示配置在下一行继续。

单个sudoers文件设计用于配置多个主机。因此,它允许特定于主机的访问控制。您将在中央主机上维护您的sudoers文件,并将更新后的文件分发给您的所有主机。

Note

在第十九章中,我们将讨论配置管理以及如何将这个文件分发给多个主机。

通过主机访问控制,您可以为不同的主机定义不同的授权,如清单 5-19 所示。

ataylor au-mel-centos-1=/bin/userdel,/bin/useradd
ataylor au-syd-ubuntu-1=ALL
Listing 5-19.Using sudo Authorization on Multiple Hosts

在清单 5-19 中,用户ataylor仅被允许在主机 au-mel-centos-1 上使用userdeluseradd命令,但是在主机 au-syd-ubuntu-1 上,她被允许使用由ALL选项表示的所有命令。

Caution

当使用ALL变量来定义对主机上所有命令的访问时,您应该小心。ALL变量不允许授权配置的粒度。

通过授予对特定目录中命令的访问权限,您可以更有选择性地进行授权:

ataylor au-mel-centos-1=/bin/*

这只适用于定义的目录,而不适用于它的任何子目录。例如,如果您授权访问/bin/*目录,那么您将不能运行/bin/extra/目录中的任何命令,除非您明确定义了对该目录的访问,如下一行中的配置所示:

ataylor au-mel-centos-1=/bin/*,/bin/extra/*

有时,您希望将特定命令的访问权限授予某个用户,但希望该命令以另一个用户的身份运行。例如,假设您需要作为特定用户启动和停止一些守护进程,比如 MySQL 或named守护进程。您可以通过将用户名放在命令前面的括号中来指定希望命令启动的用户,如下所示:

ataylor au-mel-centos-1=(mysql) /usr/bin/mysqld,(named) /usr/sbin/named

可以想象,授权命令、用户和主机的列表会变得很长。sudo命令还带有定义别名的选项。别名是相似用户、命令和主机的集合。通常在sudoers文件的开头定义别名。我们来看一些别名。第一种别名是User_Alias,它将用户分组。

User_Alias ADMIN = ataylor,jsmith

您以正在使用的别名类型的名称开始别名,在本例中为User_Alias,后面是您正在定义的特定别名的名称,在这里为ADMIN。接下来,指定属于该别名的用户列表。然后,您可以在配置行中引用此别名。

ADMIN=/bin/userdel,/bin/useradd, \
(named) /usr/sbin/named

在前一行中,我们已经指定别名ADMIN中的用户能够使用命令userdeluseraddnamed

您可以定义的下一种别名是命令别名Cmnd_Alias,它对命令集合进行分组。

Cmnd_Alias USER_COMMANDS = /bin/userdel,/bin/useradd

您可以将此别名与刚刚创建的用户别名结合使用。

ADMIN ALL=/bin/groupadd,USER_COMMANDS

现在,别名ADMIN中定义的所有用户都可以在ALL主机上使用命令/bin/groupadd以及命令别名USER_COMMANDS中定义的所有命令。

您还可以指定对主机集合进行分组的别名。Host_Alias别名可以指定主机名、IP 地址和网络的列表。

Host_Alias SERVERS = au-mel-centos-1, au-mel-centos-2, au-syd-centos-1

您可以将此别名与之前定义的别名结合使用。

ADMIN SERVERS=USER_COMMANDS

现在,ADMIN别名中指定的所有用户都可以在SERVERS别名组中定义的主机上运行USER_COMMANDS中指定的命令。

您也可以通过放置感叹号(!idspnonenote)来否定别名。)在他们面前。让我们来看一个例子。首先,您用一些您不希望用户使用的命令定义一个命令别名,然后您可以将该别名与 sudo 配置行结合使用。

Cmnd_Alias DENIED_COMMANDS = /bin/su,/bin/mount,/bin/umount
ataylor au-mel-centos-1=/bin/*,!DENIED_COMMANDS

在这里,用户ataylor可以使用 au-mel-centos-1 主机上/bin目录中的所有命令,除了那些在DENIED_COMMANDS命令别名中定义的命令。

让我们看看授权用户使用sudo的另一种方式。在sudoers文件中,您可以基于主机中的组信息定义另一种类型的别名,方法是在组名前面加上%。

%groupname ALL=(ALL) ALL

然后,您可以用主机上定义的组名替换groupname。这意味着定义的组的所有成员都能够执行您授权给他们的任何命令,在本例中是在ALL主机上的ALL命令。

在 CentOS 主机上,已经有一个名为wheel的组用于此目的,如果您在 CentOS 主机上取消注释/etc/sudoers文件中的以下行,那么添加到wheel组的任何用户都可以使用sudo命令在您的主机上获得root权限。在 Ubuntu 上这个组被称为admin而不是wheel

%wheel ALL=(ALL) ALL

此外,sudoers文件本身有许多选项和缺省值,您可以定义它们来改变sudo命令的行为。例如,您可以配置sudo在使用sudo命令时发送电子邮件。要定义将电子邮件发送给谁,您可以使用以下行中选项:

mailto "admin@au-mel-centos-1.yourdomain.com"

然后,您可以使用更多选项修改sudo发送电子邮件的时间。

mail_always on

为了让您对可用的默认和选项有个概念,表 5-8 定义了一个与电子邮件相关的选项列表。

表 5-8。

Sending E-mail When sudo Runs

| [计]选项 | 描述 | | --- | --- | | `mail_always` | 每次用户运行`sudo`时发送电子邮件。该标志默认设置为`off`。 | | `mail_badpass` | 如果运行 sudo 的用户没有输入正确的密码,则发送电子邮件。该标志默认设置为`off`。 | | `mail_no_user` | 如果运行 sudo 的用户不在`sudoers`文件中,则发送电子邮件。默认情况下,此标志设置为开。 | | `mail_no_host` | 如果运行`sudo`的用户存在于`sudoers`文件中,但无权在该主机上运行命令,则发送电子邮件。该标志默认设置为`off`。 | | `mail_no_perms` | 如果运行`sudo`的用户存在于`sudoers`文件中,但无权执行他试图运行的命令,则发送电子邮件。该标志默认设置为`off`。 |

sudoers手册页详细介绍了许多其他选项和缺省值。

sudo命令本身也有一些你可以使用的命令行选项。表 5-9 显示了一些最有用的选项。

表 5-9。

sudo Command-Line Options

| [计]选项 | 描述 | | --- | --- | | `-l` | 为当前主机上的当前用户打印允许(和禁止)的命令列表 | | `-L` | 列出在`sudoers`文件中设置的任何默认选项 | | `-b` | 在后台运行给定的命令 | | `-u user` | 以`root`以外的用户身份运行指定的命令 |

-l选项特别有用,它允许您确定当前主机上的当前用户被授权和禁止运行什么命令。

$ sudo -l
Password:
User ataylor may run the following commands on this host:
    (root) ALL

为了提高可读性,将您的规则分解成逻辑分组的文件并将它们放在/etc/sudoers.d目录中也是一个好主意。您将发出以下命令:

$ sudo visudo –f /etc/sudoers.d/01_operators

在上面的文件中,我们将放置特定于 operators 组的内容。Sudo 将浏览/etc/sudoers.d/目录并加载它找到的任何文件,如果存在的话,按顺序,如上。

sudo命令很复杂,如果执行不当,会使您的主机面临安全漏洞。我们建议您在实现任何sudo配置之前仔细测试它,并彻底研究sudosudoers手册页的内容。

Auditing User Access

跟踪用户在做什么是用户管理的一个重要部分。在第十八章中,我们将讨论日志记录,事实上,记录用户行为的第一资源就是日志文件的内容。但是其他命令和资源对于跟踪用户及其活动也很有用。

who命令显示当前登录到主机的所有用户,以及他们登录到的终端。如果用户远程连接,该命令会显示他们连接的 IP 地址或主机名。

$ sudo who
root     tty1        Jul  3 12:32
ataylor      pts/0       Jul  8 11:39 (host002.yourdomain.com)

您可以修改who命令的输出,并且可以在 who man页面中看到选项的完整列表。可能最有用的命令行选项是–a,它结合了各种命令行选项,提供了登录到您的主机的用户、登录过程以及主机重启和运行级别详细信息的详细概述。

同样有用的还有lastlastb命令,它们分别显示用户最后一次登录主机的记录和不良用户登录的记录。如果您在没有任何选项的情况下执行最后一个命令,它将打印上次登录主机的报告。

$ sudo last
root       tty1                          Sat  Jul   3 12:32   still logged in
ataylor    pts/0         192.168.0.23    Sat  Jul   3 14:25 - 14:26  (00:01)
reboot     system boot   2.4.20-28.8     Sat  Jul   3 12:31         (4+05:40)

如您所见,last命令告诉您root已经登录,并且仍然处于登录状态。该列表还显示了用户ataylor,他从 IP 地址 192.168.0.23 登录,并保持登录一秒钟。最后一个条目显示了一个reboot条目。每次主机重新引导时,都会记录一个条目,给出重新引导的时间和主机引导到的内核的版本。

lastb命令产生相同风格的报告,但是只列出那些“坏”的登录换句话说,它列出了那些输入了不正确的密码或者由于其他错误导致登录失败的登录。

lastlastb命令相关的是lastlog命令。lastlog命令显示一份报告,其中显示了主机上所有用户的登录状态,包括那些从未登录过的用户。该命令显示所有用户及其上次登录日期和时间的列表,或者如果该用户从未登录过,则显示一条指示**Never Logged In**的消息。使用命令行选项,您可以搜索特定用户的记录。阅读lastlog命令的man页了解更多详情。

摘要

在本章中,您学习了如何从命令行或通过 GUI 界面创建用户和组。您还了解了登录主机时会发生什么,以及 PAM 和如何控制对主机的访问。

本章还详细介绍了sudo命令以及如何使用它来避免使用root用户来管理您的主机。此外,我们研究了如何配置sudo来控制谁可以访问特定的命令,以及如何报告sudo的使用情况。最后,我们讨论了如何监控用户的登录。

在下一章,我们将看看当你的主机启动时会发生什么,以及如何启动、停止和管理服务。

六、启动和服务

在前几章中,您已经学习了如何安装 Linux 主机,探索了一些基本的 Linux 概念,并了解了用户和组的概念。这一章将更深入地探究你的 Linux 主机的工作方式,并检查它在“引擎盖下”的运行方式

在这一章中,我们将看看当你的主机启动时会发生什么。我们将逐步完成该过程,并向您展示如何在各种模式下启动您的主机,以及如何配置和修改启动过程。为了演示所有这些,我们将带您了解从您打开主机电源到登录提示符的整个启动过程。

我们还将带您超越引导过程,看看您的主机如何启动和停止应用程序、系统服务和其他进程。CentOS 和 Ubuntu 这两个发行版对这些服务的添加、删除、启动和停止的管理略有不同。我们将向您展示如何在每个发行版上管理这些服务,以及其中的细微差别。您将了解什么是服务,如何启动和停止服务,以及如何查看它们的状态。最后,我们将讨论如何让服务在您的主机启动或关闭时自动启动和停止。

当你的主机启动时会发生什么?

引导过程(boot 是 bootstrap 的缩写)通常涉及三个独立但相互联系的过程:BIOS(基本输入/输出系统)或 UEFI 统一可扩展固件接口)、引导加载程序和操作系统的加载。

  1. BIOS 或 UEFI 启动并检查您的硬件。
  2. 引导加载程序允许您选择要加载的操作系统。
  3. 最后,您的操作系统被加载和初始化。

这些步骤不是特定于 Linux 的;你会发现大多数操作系统执行相似的功能和步骤。

你会发现现代硬件将支持 UEFI。它是 BIOS 的替代品,旧系统将使用 BIOS。它们都执行类似的任务,我们将从 BIOS 开始向您解释它们。

打开电源

让我们更详细地看看当您引导您的主机时会发生什么。您可能已经注意到,当您打开主机时,通常会听到几声哔哔声和呼呼声,并看到前面板上和键盘上有一些闪烁的灯。这是主机启动的第一步,在运行 BIOS 的主机上,这个过程由主板上一个叫做 BIOS 的小芯片控制。

BIOS

BIOS 对连接到系统的不同硬件(如内存、硬盘、键盘和显卡)的可用性执行基本的系统检查或开机自检(POST)操作。根据您的 BIOS,您可以更改不同的设置,但我们会让您在闲暇时进行调查。

BIOS 还将轮询其他硬件,如硬盘控制器,并启动它们的板载芯片。根据您的硬件,您可能会看到 BIOS 屏幕,后面是控制器找到的设备的信息。您可以选择通过按下某个键序列(通常在硬件启动时显示在屏幕上)来配置控制器或其他硬件。此菜单使您能够操作主机的配置;例如,它允许您在硬盘控制器上设置 RAID(廉价磁盘冗余阵列)或解决现有硬件配置的问题。

Caution

更改某些配置可能会有危险;例如,错误地更改 RAID 配置可能会破坏您的数据,因此请谨慎使用这些菜单。

BIOS 还允许您更改主机的引导源。引导源是主机查找操作系统的介质(如硬盘)。启动顺序设置允许您从多个来源之一启动:硬盘驱动器、CD/DVD,甚至 USB 闪存盘。默认情况下,您的主机通常会尝试从连接的硬盘启动,如果配置了硬盘,还会寻找 CD/DVD 驱动器或 USB 闪存盘等替代设备。

每个主板制造商都有不同的方式进入启动源菜单,例如,按 Esc、Del 或功能键,如 F1、F2 或 F10。通常屏幕上会显示一条消息,指示要按的键。从这里通常会显示一个菜单,您可以选择您想要的引导顺序。

统一可扩展固件接口

更多最新的主板将支持 UEFI。当你用 UEFI 启动你的主机时,你会发现和 BIOS 相比差别很小;灯光会闪烁,东西会旋转。预计会比 BIOS 快一点。然而,幕后的事情却完全不同。

创建 UEFI 是为了克服 BIOS 的一些实际限制。BIOS 运行在 16 位处理器上;UEFI 可以在现代处理器上运行。BIOS 被限制在 RAM 中;UEFI 可以在任意多的 RAM 中运行。BIOS 从硬盘的一小部分(主引导记录,或 MBR)中读取数据来加载操作系统。UEFI 改为从安装时创建的特殊 FAT32 分区读取,并且不限于 512 字节。此外,UEFI 要求磁盘上的分区表是 GPT (GUID 分区表),这意味着我们可以启动超过 2tb 大小的磁盘。都说 UEFI 比老 BIOS 更像现代操作系统;在鼠标和键盘的支持下,它甚至可以运行更好的图形界面。

恶意软件可以加载到系统上的方式之一是通过引导加载程序。UEFI 提供了“安全引导”,旨在通过在加载之前使用公钥/私钥验证签名的引导加载程序和硬件来防止这种情况。大多数现代 64 位发行版应该支持安全引导,但这仍然是相对较新的,在安全引导下,您可能会遇到一些硬件问题。在这种情况下,您可以向发行版提交错误报告,并通过 UEFI 配置屏幕禁用安全引导。

最后,目前并不强制要求您使用 UEFI。BIOS 在大多数主板上仍然是“传统”的,并且已经过广泛的测试和支持。但是,在下列情况下,这是强制性的;您在已经运行 UEFI 的系统上运行双引导,并且想要访问大于 2TiB 的磁盘。BIOS 和 UEFI 的目的都是准备好您的硬件,并找到引导加载程序来加载您的操作系统。

More on Disks

在前面的章节中,我们已经讨论了磁盘分区,并且说磁盘就像一个蛋糕,你可以把它分成小块,但是这是怎么做到的呢?当你分割你的磁盘时,你在磁盘上创建了一个叫做分区表的表。分区表通常位于磁盘的开头,它告诉计算机磁盘是如何布局的。也就是说,有多少磁盘已经分配给了'/boot'分区,预计有多少是逻辑卷管理(LVM)等等。

上面我们谈到了 MBR 和 GPT,这是两种类型的分区表。分区表保存有关块地址的信息(以 512 字节或 4KB 磁盘为单位)。MBR 只能容纳 2tb 的地址,而 GPT 可以容纳 8-9ZiB。

A185439_2_En_6_Fig1_HTML.jpg

图 6-1。

Your hard disk

磁盘要么在开头有一个 MBR(前 512 个字节),要么有 GPT。如果它有 MBR,分区表会在磁盘的前 446 个字节之后写入。分区表的大小是 64 字节,后面是 MBR 引导签名(2 字节)。直到第一个分区的剩余空间通常称为 MBR 间隙,其大小会有所不同,因为大多数分区器会将分区的开头与第一个 1MB 对齐。

另一方面,GPT 的地址空间为 16,384 字节,最多可以容纳 128 个分区。GPT 会在磁盘末端制作一份自身副本,以备损坏时恢复。除了分区表之外,其余的磁盘可以按照您认为合适的方式进行划分。有关更多详细信息,请参见以下内容:

引导装载程序

带 BIOS 的引导加载程序

一旦您的主机加载了所有检查过的连接到主板的所有东西,并设置了低级系统设置,它就可以启动您的操作系统了。BIOS 和 UEFI 都不知道你正在引导的操作系统,但是它知道如何运行你的发行版提供的代码。这个代码被称为引导加载程序,它需要在您引导的磁盘上的正确位置。

Note

严格来说,UEFI“对你的操作系统一无所知”是不正确的。它实际上确实知道一些,但主要的一点是它不是你的操作系统,而是硬件的底层接口。

运行 BIOS 的主机使用引导源设置来指定在哪里寻找引导过程的下一阶段:引导加载程序。BIOS 使用硬盘上的特殊部分,即主引导记录。

当我们安装操作系统时,我们安装了一个名为 GRUB2 的引导加载程序。它代表 GRand Unified Bootloader (2 ),它的工作是启动自己,然后找到、加载并移交给你的操作系统。它分两个阶段完成,第一阶段的目的是加载第二阶段。我们之前讨论过 MBR,并且说过在分区表之前,磁盘的起始处有 446 个字节。这是您的操作系统的引导加载程序代码的第一阶段将被存储的地方,BIOS 将尝试执行它在那里找到的代码。对于 Linux,这是一个名为boot.img的文件。

当 BIOS 执行boot.img时,它会找到并运行一个名为core.img的文件。该文件通常安装在 MBR 间隙中,即 MBR 和磁盘上第一个分区之间的空间。core.img的工作是访问/boot/grub并加载它在那里找到的模块。core.img将加载菜单,并有能力加载目标操作系统。

使用 UEFI 的引导加载程序

如果您使用 UEFI 系统,引导过程会有所不同。UEFI 可以读取 GPT 分区表,并且可以在 EFI 系统分区的/EFI/目录中找到并执行引导加载程序代码。与 BIOS 不同,UEFI 有自己的分区,引导加载程序和模块可以安装到其中。

在图 6-2 中,我们可以看到在安装之后,我们在驱动器/dev/sda上创建了三个分区。第一个是我们的 EFI 系统分区,它是一个 FAT32 文件系统,大小为 537MB。它有 boot 和 esp 标志(都意味着它是一个引导分区)。另外要注意的是,分区表是 GPT。

A185439_2_En_6_Fig2_HTML.jpg

图 6-2。

EFI partition on Ubuntu

因此,当 UEFI 准备运行引导加载程序时,它会读取这个分区,寻找引导加载程序。在这种情况下,我们的 Ubuntu 主机会找到/EFI/ubuntu/grubx64.efi。引导加载程序的初始部分将找到包含 GRUB2 软件的'/boot'分区,并从 GRUB2 中加载core.efi并调出 grub 2 菜单。

有关 UEFI 和 UEFI Shell(可用于管理 UEFI 的交互式 Shell)的更多信息,请参见以下内容:

在没有任何干预的情况下,GRUB2 将在短暂的倒计时后启动默认内核。按任意键将停止倒计时,向您显示更详细的可用选项菜单,并让您有机会编辑您的引导配置。在“使用 GRUB2 菜单”一节的后面,我们将解释这些选项以及如何操作它们。

在选择了您希望引导到的内核之后(或者在超时之后等待默认内核被加载),GRUB2 现在将找到内核二进制文件(vmlinuz-<release-number>),然后将一个名为initrd.img的特殊文件加载到内存中。这个文件包含您的内核需要加载的驱动程序,以利用您的主机的硬件。

启动操作系统

加载initrd.img后,GRUB2 完成并将控制权移交给内核,内核通过初始化硬件(包括硬盘)继续引导过程。你的操作系统被启动,一个叫做systemd, upstartinit的特殊程序被调用,它启动你的服务并使你的主机活跃起来。我们将在本章的后面看一下all three of these system initializers以及如何管理服务,但是首先,更多关于引导加载程序。

了解 GRUB2 引导加载程序

因此,现在让我们深入研究什么是引导加载程序,它做什么。我们还将了解一下如何配置它。我们不打算涵盖引导加载程序的每一个细节,因为你很少需要改变它,并且对它的大多数改变是自动发生的,例如,当你安装一个新的内核包时。但是您应该了解它的作用以及它在主机引导过程中的位置。

一些历史;在 Linux 世界中,有两种主要的引导加载程序:LILO 和 GRUB。几年前,GRUB 引导加载程序成为 Red Hat 和 Ubuntu 的默认设置。在这里,我们将专注于 GRUB 的最新版本 GRUB2。GRUB2 是一个强大的多重引导加载程序。多重引导加载程序可以使您的主机引导到许多不同的操作系统。与 Microsoft Windows 或 Mac OS X(以及它们的引导加载器工具—分别为 NTLDR 和 Boot Camp)不同,GRUB2 允许您在一个硬件上引导多个版本的 Linux、Microsoft Windows 和 Mac OS X。这并不意味着您可以像使用虚拟机技术一样同时运行它们,但是您可以在启动时通过从 GRUB2 菜单中选择它们来单独启动它们。

Note

LILO 是一个遗留的引导加载程序,在许多旧版本的 Linux 发行版中用作默认的引导加载程序。今天很少看到它,2015 年它的开发停止了。关于 LILO 引导加载程序的信息,请参见 http://tldp.org/ HOWTO/LILO.html

那么 GRUB2 是如何工作的呢?GRUB2 的核心使用四项来引导系统:内核文件、驱动器名、内核文件所在的分区号,以及可选的初始 RAM 磁盘。

GRUB2 能够以两种方式引导。一种是直接找到并加载所需的内核,这是大多数 Linux 发行版的引导方式。GRUB2 还支持一种引导的方法,叫做链式加载;使用这种方法,GRUB2 加载另一个引导加载程序,例如用于 Microsoft Windows 的加载程序,然后加载所需的操作系统内核。这允许 GRUB2 使用自己的引导加载程序来引导其他操作系统。

使用 GRUB2 菜单

当您的主机启动时,它将启动到默认内核(或操作系统),或者您可以覆盖它并显示 GRUB2 菜单。一旦菜单显示出来,您将看到一个引导选项列表,您可以使用键盘上的上下箭头来选择您希望引导的内核。您还可以编辑 GRUB 菜单,并在引导到您选择的内核之前更改参数、命令和自变量。

例如,我们可以选择引导到所谓的单用户模式或维护模式。这种特殊的模式在您的主机出现问题时使用,它将对主机的访问限制为根用户:通常在系统控制台。这种模式的功能与 Microsoft Windows 恢复控制台非常相似,允许您使用磁盘和文件等资源,而不必担心冲突或其他用户操纵主机。现在让我们来看看如何引导到单用户模式。

首先,这在 CentOS 和 Ubuntu 之间略有不同。两者都显示了类似的 GRUB2 引导菜单,允许你用箭头键选择你想运行的内核。CentOS 提供了我们之前讨论过的两个菜单项,一个是内核,另一个被描述为救援内核。在 Ubuntu 上,你会看到一个名为高级选项的子菜单项。您可以使用箭头键浏览以查看恢复模式内核。

在这个练习中,我们将使用 Ubuntu 主机的普通内核。按 e 键编辑突出显示的内核。您将看到该内核的配置,比如它的位置和参数,如图 6-3 所示。

A185439_2_En_6_Fig3_HTML.jpg

图 6-3。

Booting to single-user mode , or maintenance mode , using the GRUB2 menu

您将在这里看到您在grub.cfg菜单中看到的详细信息。在这个特定的例子中,我们感兴趣的线是linux /vmlinuz-4.4.0-15-generic.efi.signed…线。使用箭头键导航到该行的末尾。要引导进入单用户模式,请在该行末尾添加单词'single’(不带引号)。在图 6-3 中,你可以看到我们已经在线尾添加了single字样。在图 6-3 的底部,你可以看到我们现在可以输入 Ctrl-x 或 F10 以此设置启动,或者输入 Ctrl-c 以获得命令行或 ESC 以放弃这些更改并返回主菜单。

我们可以选择 Ctrl-x,引导进入单用户模式。我们首先被要求输入密码来解密我们的硬盘。然后,根据您的发行版,您将得到一个要求 root 用户(仅 CentOS)的维护密码提示,或者您可以点击 enter 并得到一个 shell 提示(参见图 6-4 )。

A185439_2_En_6_Fig4_HTML.jpg

图 6-4。

Maintenance mode

现在我们有了 shell 提示符,我们可以着手修复我们系统中可能存在的任何问题。不过,对内核行的这些更改不是永久的,下次主机启动时,它将正常启动。使用 GRUB2 菜单,您可以操作几乎所有可用的引导运行时配置设置。

需要注意的是,我们提供上面的内容作为示例,它显然有一些相关的安全问题——如果您在引导时可以访问控制台,这将为您的主机提供非常强大的访问权限。我们接下来会帮助解决这个问题。还有其他的恢复方法,我们将在第九章中进一步讨论。同时,以下是很好的信息来源:

配置 GRUB2

GRUB2 引导加载程序是高度可配置的,它的配置包含在grub.conf配置文件中。GRUB2 使grub.cfg比它更简单的前身更加模块化。您需要对 GRUB2 配置进行许多更改的机会很小,但却是可能的。

GRUB2 文件位于不同发行版的不同位置。在 CentOS 等基于 Red Hat 的主机上,可以在/boot/grub2/grub.cfg找到它们(文件通常象征性地链接到/etc/grub.conf)。在 Ubuntu 和 Debian 主机上,这些文件可以在/boot/grub/grub.cfg找到。

GRUB2 中的grub.cfg文件由一系列配置文件组成。在 Ubuntu 和 CentOS 主机上,您都可以在目录/etc/grub.d中找到配置文件。在该目录中,您会看到一个按编号排序的文件列表。在清单 6-1 中,您可以看到 CentOS 7 主机上的文件。

total 72
-rwxr-xr-x. 1 root root  8702  Nov 25 02:49  00_header
-rwxr-xr-x. 1 root root   992   May   4  2015  00_tuned
-rwxr-xr-x. 1 root root   230   Nov 25 02:49  01_users
-rwxr-xr-x. 1 root root 10232 Nov 25 02:49  10_linux
-rwxr-xr-x. 1 root root 10275 Nov 25 02:49  20_linux_xen
-rwxr-xr-x. 1 root root  2559  Nov 25 02:49  20_ppc_terminfo
-rwxr-xr-x. 1 root root 11169 Nov 25 02:49  30_os-prober
-rwxr-xr-x. 1 root root   214   Nov 25 02:49  40_custom
-rwxr-xr-x. 1 root root   216   Nov 25 02:49  41_custom
-rw-r--r--.   1 root root   483   Nov 25 02:49  README
Listing 6-1.
ls –la /etc/grub.d

清单 6-1 显示了组成一个grub.cf g 文件的文件。在这种情况下,这些文件通过grub2-mkconfig命令从00_header读入41_custom。您通常不需要了解这些文件,因为您很少需要定期接触它们。您可能感兴趣的文件是01_users40_custom文件。如果设置了引导加载程序密码,则01_users文件会加载该密码。40_custom文件是您可能希望添加定制 GRUB2 配置的地方。

Tip

在 Linux 配置文件中,以#符号为前缀的行通常表示注释,在处理配置文件时会跳过这些行。

GRUB2 配置文件通过grub2-mkconfig命令生成。它有一个参数,即您希望在哪里创建文件的–o--output选项。您现在可以创建自己的grub.cfg文件了。它将探测您的系统,运行您的配置文件,并输出一个您可以查看的文件。当然,您不必用这个文件覆盖您当前的grub.cfg,所以我们将把输出更改为本地目录中的一个文件。

$ sudo grub2-mkconfig --output mygrub.cfg
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-3.10.0-327.el7.x86_64
Found initrd image: /boot/initramfs-3.10.0-327.el7.x86_64.img
Found linux image: /boot/vmlinuz-0-rescue-65e567f18fe84907a6f1d8519e921c97
Found initrd image: /boot/initramfs-0-rescue-65e567f18fe84907a6f1d8519e921c97.img
done
Listing 6-2.Creating a grub.cfg

在清单 6-2 中,您可以看到我们使用了sudo命令来提升我们执行grub2-mkconfig命令的权限。该命令遍历/etc/grub.d目录中的文件,并按编号顺序执行。控制台的命令输出显示,is 已经找到了两个内核(vmlinuz ),其中一个是“救援”内核。grub2-mkconfig命令已经探测了我们的系统,并通过/etc/grub.d/30_os-prober脚本找到了它们。

查看已经生成的mygrub.cfg文件(使用$ less mygrub.cfg),您将在/etc/grub.d中看到所有这些文件的输出。我们将集中讨论启动操作系统的部分,从下面一行开始:

### BEGIN /etc/grub.d/10_linux ###

在开始行下面是配置我们菜单项的 menuentry。

menuentry 'CentOS Linux (3.10.0-327.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-327.el7.x86_64-advanced-f60589f6-4d39-4c5b-8ac6-b1252f28f323' {
  ....
}
Listing 6-3.menuentry in the grub.cfg

这看起来已经很难消化了,我们已经把清单 6-3 减少到了第一行。以menuentry, this开头,告诉 grub 我们有一个新的菜单项要列出来。CentOS Linux (...) 7 (Core)给出了菜单列表中显示的名称。其他信息由--class参数提供,用于显示某些主题(如闪屏等)。

--unrestricted选项用于允许任何用户在引导时通过控制台菜单运行该条目。这可以通过--users选项进行更改,在这里您可以列出能够运行该条目的用户(注意,任何被列为superuser的用户仍然可以访问)。当然,这意味着任何移除了--unrestricted的条目都不会自动引导,需要干预。GRUB2 文档指出,这并不是真正的最佳安全形式,因为它需要在重启时访问控制台,但在 kiosk 类型的安装中可以考虑。我们将在“保护您的引导加载程序”一节中详细讨论这个问题和超级用户

在清单 6-3 中,我们省略了花括号之间的代码。现在让我们看看清单 6-4 中的代码。

menuentry ... {
        load_video
        set gfxpayload=keep
        insmod gzio
        insmod part_msdos
        insmod xfs
        set root='hd0,msdos1'
        if [ x$feature_platform_search_hint = xy ]; then
          search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1 --hint='hd0,msdos1'  85932bb0-c5fe-431f-b129-93c567e4f76f
        else
          search --no-floppy --fs-uuid --set=root 85932bb0-c5fe-431f-b129-93c567e4f76f
        fi
        linux16 /vmlinuz-3.10.0-327.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet
        initrd16 /initramfs-3.10.0-327.el7.x86_64.img
}
Listing 6-4.Grub Boot Menu Code

在 menuentry 代码的花括号内,前几行代码安装了各种模块。例如,第一行,load_video调用grub.cfg脚本中的一个函数,为闪屏插入各种视频模块。CentOS 7 主机上的模块可以在/boot/grub/i386-pc/中找到,并通过您在后续行中看到的insmod命令加载。您在这里看到的三行 insmod 正在加载压缩和文件系统模块。

set root='hd0,msdos1'是我们如何为 GRUB2 设置根设备。GRUB2 使用此设置来帮助查找 GRUB2 需要的文件。这意味着根设备在硬盘驱动器(hd)上,并且是第一个硬盘驱动器(hd0)'msdos'是根设备的文件系统,最后的“1”是hd0上的分区号。如果你从一个 USB 驱动器启动,你可能希望它看起来像'usb0,msdos1'

Note

Linux 操作系统中的编号一般从 0 开始计数,而不是从 1 开始计数。然而,就 GRUB2 而言,磁盘上的第一个分区是分区 1。第一个磁盘/设备从 0 开始编号。

接下来在清单 6-4 中,我们看到一个 if…fi 条件语句,里面有一个搜索命令。根据‘$feature_platform_search_hint’是否设置为 y,搜索命令会有所不同。此处发生的情况是,搜索命令通过 UUID(通用唯一标识符)查找设备。这用于再次设置 GRUB2 的根设备,以便它可以找到我们将要加载的 Linux 内核。

在清单 6-4 中,我们终于加载了我们的 Linux 内核。linux16 行的第一部分说我们将使用 16 位模式加载内核(16 位模式用于解决视频模式的某些问题)。下一部分是 Linux 内核本身,/vmlinuz-3.10.0-327.el7.x86_64。您将在/boot目录中找到这个文件。接下来,我们将选项传递给内核。我们通过root=/dev/mapper/centos-root来提供到'/'分区的路径。最初,我们将内核加载为‘??’或只读。crashkernel=auto选项是 kdump 内核的一个设置,表示为它保留的内存量。接下来的两个选项告诉内核它需要加载两个 LVM 逻辑卷:centos/rootcentos/swap。最后,我们可以看到我们加载了 Red Hat Graphical Boot (rhgb),这是一个比文本更好的引导体验,并且我们设置了quiet选项来抑制引导期间的噪声输出。

最后,清单 6-4 中我们关心的最后一行是加载内核 initrd。initrd 是一个临时根文件系统,引导过程使用它首先加载所需的可执行文件和驱动程序,然后挂载系统的“真正的”根('/')文件系统。它仅在启动时使用,并且一旦装载了真正的文件系统,它就会被卸载。

我们只向您展示了一个 GRUB2 菜单,并且只来自 CentOS 主机。对于 Ubuntu 主机来说,菜单项会稍有不同。通常情况下,您会看到每个已安装的内核至少有两个菜单项,其中一个是用于系统恢复的“救援”内核。它是内核的一个副本,以防在 CentOS 上被破坏,没有特殊的内核选项。Ubuntu 是不同的,因为它通过了recoverynomodeset内核选项。我们稍后会详细讨论这些。

Note

你可以在 http://www.gnu.org/software/grub/manual/grub.htmlhttps://wiki.archlinux.org/index.php/kernel_parameters 找到更多关于定义内核和内核选项的信息。你也可以通过info grub命令查找信息。

Managing Your Kernel

不是所有的内核都需要一个initrd文件。例如,您自己编译的包含系统所需的所有模块的内核不应该需要一个initrd文件。

例如,如果内核的后续版本支持您需要的一些新硬件或功能,您可能希望编译自己的内核。编译你自己的内核是一个很好的练习,只是为了看看选项是什么以及如何工作。

我们通常建议您坚持使用您的发行版提供的普通内核,并且只编译您自己的内核以获得乐趣,或者如果您确实需要某些特定的功能。管理“手工制作”的内核对于修补和更新来说可能是一个负担,有时会留给您一个无法更新的系统。

当你更新你的内核时,你需要重启以获得关键补丁。在 Linux 内核的版本 4 中,自 2015 年以来,一直在致力于一种修补内核的方法,使其不再需要重新启动,即使是关键的补丁。它是 SUSE 和 Red Hat 的工作成果,灵感来自 Oracle 的原创作品。在撰写本文时,它仍然是新的,支持代码仍然需要一些工作。

要获得最新内核的副本和其他信息,您可以到这里:

推荐的更新内核的方法是通过发行库。然而,这也是支持诸如 RHEL (Red Hat Enterprise Linux)、Oracle Linux、Canonical Enterprise Support 等发行版的巨大优势所在。如果你需要紧急的内核补丁,他们会经常为你报告的问题更新和发布内核补丁。等待非商业发行版发布您感兴趣的补丁可能会失败。

保护您的引导加载程序

拥有这样一个通用且可配置的引导加载程序有其缺点。我们提到的其中之一是安全。任何怀有恶意的人都可以在引导时修改不安全的引导加载程序。许多小型办公室会将服务器放在两张桌子上或下面,而不是锁在计算机房里,因此这些服务器很容易受到这种攻击。我们强烈建议您考虑在您的引导加载程序上设置一个密码,并将它与您的其他密码一起存储在一个非常安全的地方。

Caution

同样,你应该把你的密码放在一个安全的地方,比如一个保险箱或者其他安全的上锁的地方。有时,将它保存在异地的某个地方也是很好的(就像对异地备份一样),这样,如果您的站点发生问题,您不仅能够恢复您的数据,还可以随时使用正确的密码来访问您的主机。您还应该备份所有密码,并告诉您公司中信任的人在紧急情况下可以在哪里找到这些密码。

幸运的是,GRUB2 提供了为引导加载程序设置密码的能力,因此对预先配置的引导过程的任何更改都需要用户输入密码。CentOS 主机提供了一些工具来简化这一过程,我们将在本练习中探索这种分布。首先,我们发出以下命令:

$ sudo grub2-setpassword

这将要求您输入密码并进行确认,并且只会为 root 用户创建一个密码。这个命令实际上是创建一个文件/boot/grub2/user.cfg。该文件的内容如下所示:

GRUB2_PASSWORD=grub.pbkdf2.sha512.10000.00E574C[...]DDF512CC090A9B[...]CCEA3B

既然文件已经创建,当您的主机重新启动时,现在将执行/boot/grub2/grub.cfg文件中的代码:

### BEGIN /etc/grub.d/01_users ###
if [ -f ${prefix}/user.cfg ]; then
  source ${prefix}/user.cfg
  if [ -n ${GRUB2_PASSWORD} ]; then
    set superusers="root"
    export superusers
    password_pbkdf2 root ${GRUB2_PASSWORD}
  fi
fi
### END /etc/grub.d/01_users ###
Listing 6-5.Adding Root User to grub.cfg

清单 6-5 中代码的意思是,如果user.cfg文件存在,那么我们将“获取”内容。如果声明了一个$GRUB2_PASSWORD变量,那么我们将把root添加到超级用户列表中,这将其他用户锁定在GRUB2命令行之外。最后,password_pbkdf2是一个 GRUB2 命令,它将用户与密码相关联。我们现在需要运行grub2-mkconfig –o /boot/grub2/grub.cfg来设置超级用户 root 密码。

当您的主机下次启动时,您会注意到,如果您在 GRUB2 阶段中断启动过程,您将能够使用--unrestricted menuentry 选项选择一个内核。但是,如果您想要编辑任何 GRUB2 配置详细信息,您需要按 e 键输入密码。然后,您将能够正常编辑 GRUB2 配置。

要删除密码,请删除user.cfg文件。请记住,GRUB2 文档建议不要这样做,除非您的主机是公开的。我们建议你根据自己的情况做出最好的判断。

请记住,Ubuntu 并没有提供这些现成的工具,但是您可以遵循该发行版的类似过程。有关 Ubuntu 的具体指南,请参见 https://help.ubuntu.com/community/Grub2/Passwords

开机后会发生什么?

所以你的主机找到了你的内核并启动了它。您的操作系统现在开始加载,您的硬件初始化,磁盘准备就绪,IP(互联网协议)地址分配,以及各种其他任务的执行。为此,Linux 运行一个程序,其任务是启动操作系统及其服务。这个程序是第一个在您的系统上启动的程序,并且一直运行到系统关闭;不出所料,这个进程的进程 id (PID)将为 1。初始化程序,或称“init”,将通过找出需要启动的服务以及它们所依赖的东西,使系统达到一个已知的“状态”。它通过遍历文件系统上的一系列文件并读取它们的指令来实现这一点。

目前 Linux 正处于一个过渡阶段。直到最近,至少有三种不同的程序处理系统初始化。这些是旧的 SysV Init 程序,Ubuntu 的 Upstart 程序,或者更新的 systd。

Upstart 是 Ubuntu 试图替换旧的 SysV Init 初始化器。因为它与 SysV 兼容,所以它也被其他发行版用作初始值设定项;然而,没有多少完全实现暴发户。它只是被用作一个更好的“初始化”基于 Red Hat 的主机已经完全实现了 Systemd,基于 Debian 的系统,比如 Ubuntu,正在从 Upstart 和 SysV Init 过渡到 Systemd。

Systemd 是一种较新的系统初始化方法,可以在 CentOS 上默认使用(或者说是本地运行 systemd ),您可以在图 6-5 中看到 systemd 是 PID 1。另一方面,据说 Ubuntu“模仿”SysV Init。它通过使用 SysV initd 脚本来初始化许多使用 systd 包提供的兼容性脚本的服务来做到这一点。

A185439_2_En_6_Fig6_HTML.jpg

图 6-6。

PID 1 onUbuntu

A185439_2_En_6_Fig5_HTML.jpg

图 6-5。

PID 1 on CentOS

在 Ubuntu 上,/sbin/init程序是 PID 1。PID 为 1 的 init 程序来自 systemd-sysv 包,实际上链接到 systemd 二进制文件或程序。Ubuntu 目前支持所有三种 init 类型,同时正在过渡到原生运行 Systemd。

这两个发行版都安装了 systd-sysv 包,其中包含用于运行 SysVInit 风格的 initd 脚本的兼容性实用程序。它旨在替代 SysVInit,但并不是 100%兼容。

让我们看看如何管理 Systemd 本机实现。我们还将简要讨论 Upstart 和较老的 SysV init。

Note

init 程序有很长的历史,解决如何启动系统的问题的流行和不受欢迎的尝试都有。为了更好地观察它们,请参见 http://blog.darknedgy.net/technology/2015/09/05/0/

理解系统

正如我们已经提到的,Systemd 是最新的系统初始化器,在许多现代发行版中都有使用。systd 是 SysV Init 和 Upstart 的替代品。有人批评它违背了 Unix 的哲学(做一件事,把它做好),但是它在以下几个方面(或多或少)比它的前辈提供了许多优势:

  • 它是事件驱动的。这意味着它可以响应系统事件(例如插入新硬件、网络端口上开始的流量)
  • 并发和并行引导处理
  • 它可以重生过程
  • 事件记录
  • 通过内核的 CGroups 跟踪进程

对于那些已经熟悉 SysV 的人来说,systd 没有像 SysV 那样的运行级别,而是有“目标”。目标用于将服务依赖项组合在一起。systemd 中有一些常见的目标,如多用户、重启和救援。就像在运行级别中一样,我们可以有不同的目标,将我们带到特定的和离散的状态。例如,如果我们希望系统处于每个人都可以登录并使用服务的状态,我们将使用目标多用户。在这种情况下,我们希望 sshd、日志和网络可用。这些被认为是多用户目标的“需求”。

还记得图 6-3 中我们向您展示了如何从 GRUB2 菜单进入“维护”模式吗?我们在启动 Linux 内核的那一行的末尾键入单词“single”。嗯,使用 Systemd,我们也可以通过声明我们希望启动的目标来做同样的事情。

linux16 /vmlinuz-3.10.0-327.el7.x86_64 root=/dev/mapper/centos-root ro crashkernel=auto rd.lvm.lv=centos/root rd.lvm.lv=centos/swap rhgb quiet systemd.unit=rescue.target
Listing 6-6.Using Systemd Targets to Boot to Rescue Mode

从清单 6-6 中,您可能会意识到我们是在 CentOS 服务器上;我们可以在 Ubuntu 服务器上做同样的事情。您可以看到,我们已经在该行末尾发出了systemd.unit=rescue.target。我们告诉内核,当它运行 systemd 时,我们应该到达救援目标。

Systemd 使用一组“单元”文件来管理系统上的服务。当 systemd 启动时,它通过遍历以下加载路径来查找所需的单元文件:

表 6-1。

Systemd Load Path

| 小路 | 目的 | | --- | --- | | /etc/systemd/system | 本地化的配置文件 | | /run/systemd/system | 运行时单元文件 | | /usr/lib/systemd/system | 已安装软件包的单元文件 |

因为我们使用了system.unit=rescue.target参数,当系统进程启动时,它将按照优先顺序在这些目录中查找rescue.target文件,然后是/etc/systemd/system, then the /run/systemd/system目录,最后是/usr/lib/systemd/system目录。/etc/systemd/system/run/systemd/system目录被称为“插入”目录,systemd 将在那里搜索单元和*.conf文件。目标文件将类似于下面的rescue.target

[Unit]
Description=Rescue Mode
Documentation=man:systemd.special(7)
Requires=sysinit.target rescue.service
After=sysinit.target rescue.service
AllowIsolate=yes

[Install]
Alias=kbrequest.target

Listing 6-7.The rescue.target File

在清单 6-7 中,我们删除了系统进程忽略的以“#”开头的注释。选项区分大小写。在分解这个文件时,您可以看到它有两个部分,用方括号([…])表示。Systemd 单元文件至少需要[Unit]和[Install]部分。您可以选择包含一个[服务]部分,我们将很快对此进行讨论。

[Unit]部分有一个描述和文档设置,它们是不言自明的。在清单 6-7 中,我们可以看到 rescue 目标需要 sysinit.target 和 rescue . service。Requires 不提供排序,但告诉 systemd 这些服务也应该执行。如果所需的目标或服务之一失败,该单元也将被停用。

通过添加像 After 这样的选项来提供对目标或服务的订购支持。rescue.unit现在应该在sysinit.targetrescue.service之后开始。常见的模式是在所需选项中看到相同的服务和目标,在之后选项中也列出了这些服务和目标。

AllowIsolate 选项是一个由systemctl isolate命令读取的布尔值。systemctl 命令的 isolate 参数本质上类似于 SysV 运行级别。您可以使用它将您的系统置于特定的“状态”,因此要启动您的图形环境,您可以选择运行systemctl isolate graphical.target。因此,AllowIsolate 默认设置为“false/no ”,因为不是每个目标文件都提供稳定的系统状态。

当装置启用或禁用时,systemctl 工具使用[Install]部分。systemctl 工具是用于管理 systemd 的实用工具。这里的 Alias 选项指向kbrequest.target,它是一个特殊的 systemd 单元,每当在控制台上按下 Alt+ArrowUp 时就会启动。看一下图 6-7 来看看它是如何工作的。

A185439_2_En_6_Fig7_HTML.jpg

图 6-7。

Using systemctl to enable the Alt-ArrowUp alias

在图 6-7 中你可以看到,当我们发出$ sudo systemctl enable rescue.target时,安装部分的别名选项意味着我们创建了一个从kbrequest.targetrescue.target的符号链接。现在,在控制台上,如果我们点击 Alt+向上箭头键,我们会得到如图 6-8 所示的结果。

A185439_2_En_6_Fig8_HTML.jpg

图 6-8。

Entering rescue console from pressing the Alt+ArrowUp keys

让我们快速地看一下一个服务文件,它配置了我们如何管理 rsyslog 守护进程。Rsyslog 是 Linux 上的日志服务,我们将在第十八章中深入探讨它。在清单 6-8 中,我们显示了rsyslog.service文件的内容。

[Unit]
Description=System Logging Service
;Requires=syslog.socket

[Service]
Type=notify
EnvironmentFile=-/etc/sysconfig/rsyslog
ExecStart=/usr/sbin/rsyslogd -n $SYSLOGD_OPTIONS
Restart=on-failure
UMask=0066
StandardOutput=null

[Install]
WantedBy=multi-user.target
;Alias=syslog.service

Listing 6-8.The rsyslog.service systemd File

在这个文件中,我们有[Unit]和[Install]部分,就像我们以前看到的一样。值得注意的是,在这些部分中,您可以看到 Requires 和 Alias 选项上的分号。如果行以#;开头,那么它们被认为是注释并被忽略。在清单 6-8 中,我们看到有一个【服务】部分,我们用它来描述我们正在配置在我们系统上运行的服务。

所有[服务]部分都需要类型选项。它可以设置为“简单”、“分叉”、“一次性”、“dbus”、“通知”或“空闲”默认设置是“简单”对于 simple 类型,您期望由 ExecStart 选项运行的进程是主进程(即,它不派生子进程)。Notify 类似于 simple,但它会在完成启动后发送一条通知消息。一旦 Systemd 读取到此消息,它将启动其他单元。因为这是一个日志服务,所以在启动依赖于记录日志事件的其他单元之前,确保该服务启动并运行是有意义的。

环境文件是我们可以加载环境参数的地方。可以为 rsyslogd 设置许多可选参数,您可以将它们添加到/etc/sysconfig/rsyslog文件中。可以将man rsyslogd中的选项添加到文件中。

接下来我们有 ExecStart 选项。这是与任何参数一起执行的命令。这应该是守护进程的主进程,除非你要分叉这个进程(用 Type=fork)。

Restart 选项控制 systemd 如何监控服务并对任何问题做出反应。选项包括“否”、“成功时”、“失败时”、“异常时”、“监控时”、“中止时”或“总是”rsyslog 服务被设置为失败时重新启动,如果未设置,默认值为“否”。设置 on-failure 意味着当进程以非零退出代码退出、被信号终止、操作超时或触发监控超时时,systemd 将尝试重新启动服务。

UMask 设置由此进程创建的任何文件的权限。如果我们还记得在第四章中学到的内容,umask 是一个八位字节,用于设置文件和目录的权限。当应用它时,意味着我们给予由该进程创建的文件 0711 或 rwx-x-x 权限。这意味着该进程的所有者被给予对文件的读、写和执行权限,该组和其他每个人将只有执行权限。

[Service]部分的最后一个选项是 StandardOutput,它定义了文件描述符 1 或进程的标准输出应该连接到哪里。这些值是" inherit "、" null "、" tty "、" journal "、" syslog "、" kmsg "、" journal+console "、" syslog+console "、" kmsg+console "或" socket "。rsyslogd 服务被设置为 null,这是 Linux 上一个名为/dev/null的特殊文件。如果你向/dev/null写,你希望你的数据被黑掉或丢失。这意味着我们不关心这个服务在标准输出上的输出。

最后,如果您查看[Install]部分,您将看到 WantedBy=multi-user.target。该行指示 systemctl 命令在服务启用时在/etc/systemd/system/multi-user.target.wants目录中创建一个到/lib/systemd/system/rsyslog.service的符号链接,或者在服务禁用时删除该链接。稍后会有更多内容。

Systemd 单元文件很复杂,并且依赖于许多其他的依赖单元。systemd 单元还有数不清的选项和设置。如需进一步阅读,请参阅以下有用的链接:

一旦您的内核被转移到 systemd 进程,并使您的机器达到目标状态,您将需要知道如何与系统上运行的服务进行交互。我们将在查看 systemctl 命令时对此进行探讨。

新贵:Ubuntu 的 Init

Upstart 背后的想法是创建一个全面的init进程,可以用来代表 Linux 主机上的服务启动、停止、监控和响应事件。这是对 SysV Init 范式的改造和扩展;设计师没有直接替换 SysV Init,而是创建了 Upstart 来模拟 SysV Init,直到所有人都转换过来。

Upstart 下的init进程是一个基于事件的守护进程,它使用事件触发器来启动或停止进程。事件是可以通知init的状态变化。事件可以是添加外围设备,如插入 USB 记忆棒。然后内核可以通过发送事件通知来通知 init 这个动作。根据init控制下的作业定义,该事件反过来可以触发其他作业的启动或停止。

Upstart 在其命令作业下统一调用进程,您可以使用initctl命令与这些作业进行交互。作业可以是服务或任务,也可以是抽象的。服务是持久的,就像邮件服务器,任务执行一个功能,然后退出到等待状态,就像备份程序。抽象作业永远运行,但没有子进程。作业的定义文件(或 Upstart 脚本)可以在/etc/init目录下找到。

清单 6-9 展示了 rsyslogd 守护进程的系统作业定义。在这种情况下,rsyslogd 是一个需要写入到/var/log/syslog文件的日志守护进程。作为日志守护进程,如果它由于某种原因停止了,最好让它尝试重新启动。让我们在这里过一遍要点。

# rsyslog - system logging daemon
#
# rsyslog is an enhanced multi-threaded replacement for the traditional
# syslog daemon, logging messages from applications

Description      "system logging daemon"

start on filesystem
stop on runlevel [06]

expect fork
respawn

pre-start script
    /lib/init/apparmor-profile-load usr.sbin.rsyslogd
end script

script
    . /etc/default/rsyslog
    exec rsyslogd $RSYSLOGD_OPTIONS
end script

Listing 6-9.
/etc/init/rsyslog.conf

Upstart 是“事件化的”,这意味着它可以对系统上的事件做出反应。在文件系统上启动意味着当文件系统“事件”发生时,我们也将自动启动 rsyslog。停止配置选项是一个事件定义信号 Upstart,用于在检测到运行级别 0 或 6 事件时停止 rsys logd。如您所知,运行级别 0、1、6 是特殊的运行级别,它们可以关闭、重启或使您的主机进入维护模式。我们希望服务派生另一个进程,如果任务意外停止,那么respawn选项会指示 Upstart 重新启动任务。我们可以编写在执行主脚本部分之前运行的预启动脚本。

虽然 Upstart 不同于 SysV,没有运行级别的自然概念,但它已经与 SysV Init 脚本向后兼容。这些通过执行/etc/init.d/rc脚本来模拟旧式 SysV 脚本。这将依次运行旧的/etc/rcN.d目录,并启动和停止特定运行级别的服务。

您可以使用initclt命令管理 Upstart,它允许您启动、停止服务。

$ sudo initctl start rsyslog
$ sudo initctl stop rsyslog

Upstart 没有启用或禁用服务的工具,但是您可以使用 pre-start script section 在包含已启用变量集的文件中进行编译。暴发户 Ubuntu Cookbook 提出了这样一种被接受的方式:

pre-start script

  # stop job from continuing if no config file found for daemon
  [ ! -f /etc/default/myapp ] && { stop; exit 0; }

  # source the config file
  . /etc/default/myapp

  # stop job from continuing if admin has not enabled service in
  # config file.
  [ -z "$ENABLED" ] && { stop; exit 0; }

end script

你可以在 http://upstart.ubuntu.com/cookbook/ 阅读更多关于新贵的内容。

记忆系统

让我们回到过去。很长一段时间以来,Linux 系统都是由一个名为 systvinit、SysV Init、SysV 或简称为 Init 的程序初始化的。在大多数主流发行版中,包括 CentOS 和 Ubuntu,Init 已经被另一个名为 Systemd 的程序所取代。然而,这两个发行版仍然支持 SysVInit 启动脚本。我们将向您介绍 Init 的一些背景知识以及它是如何工作的。

SysVInit 有一个运行级别的概念。运行级别定义了主机在特定时刻应该处于什么状态。每个运行级别都包含一组应用程序和服务,以及一个指示每个应用程序和服务是应该启动还是应该停止的指示器。例如,在您的主机正常启动期间,每个运行级别的init工具将启动该运行级别中所有需要的应用程序和服务集。另一个例子发生在您关闭主机时。当您告诉您的主机关闭时,init工具将 runlevel 更改为 0。在此运行级别,所有应用程序和服务都将停止,系统也将暂停。

SysV 有 7 个运行级别,范围从 0 到 6,每个发行版使用不同的运行级别用于不同的目的。但是有些运行级别在所有发行版中都相当通用。常见的运行级别是 0、1 和 6。您已经看到了运行级别 0,它用于关闭主机。运行级别 1 是单用户模式,或维护模式,我们在本章前面已经描述过。当您的主机重新启动时,使用运行级别 6。

Ubuntu 和 CentOS 主机上的运行级别略有不同。在 Ubuntu 上,运行级别 2 到 5 都运行所谓的多用户模式。多用户模式是指多个用户可以登录的模式,而不仅仅是一个控制台用户。所有必需的服务通常都被设置为在这个运行级别启动。

相比之下,如果您安装了 GUI 控制台,CentOS 通常在运行级别 5 启动,或者只在命令行运行级别 3 启动。CentOS 具有以下运行级别:

  • 运行级别 0:关闭主机并使系统停止运行
  • 运行级别 1:在单用户(维护)模式下运行,命令控制台,无网络
  • 运行级别 2:未分配
  • 运行级别 3:在多用户模式下运行,使用网络,并启动级别 3 程序
  • 运行级别 4:未分配
  • 运行级别 5:在多用户模式下运行,使用网络,X Windows (KDE,GNOME),启动级别 5 的程序。
  • 运行级别 6:重启主机

管理 SysV 初始化文件

传统上,在大多数发行版中,/sbin/init工具是使用/etc/inittab文件配置的。inittab文件指定了系统应该使用的默认运行级别。它还详细说明了其他运行级别,以及在每个运行级别中哪里可以找到要启动或停止的应用程序列表。在运行 Systemd 的系统上,你可以忘记基本上忽略运行级别,因为它们在新的世界中并不严格适用,你也不会在你的系统上找到inittab文件。

Note

如果您正在寻找比 inittab 手册页中更详细的信息,您可以在 www.cyberciti.biz/howto/question/man/inittab-man-page.php 中阅读信息。

传统的 init 程序会读取 inittab 来确定默认的运行级别,然后为该运行级别执行一系列脚本(文件在/etc/rcX.d目录中,其中 X 是运行级别)。那些以 S 或 K '开头的目录中的文件将被执行,这取决于系统是正在启动(S)还是正在关闭(K)(或者在服务应该运行或停止的运行级别之间移动)。

对于安装了 systemd-sysv 包的主机,如果我们找不到 systemd 服务文件,系统将在/etc/init.d目录中查找文件;比如/etc/init.d/postfix。这个文件将被提取并生成到一个 systemd 服务文件中(稍后将详细介绍)。

在运行 systd 的现代发行版上,不再需要配置 SysV。

在运行级别之间移动

在旧系统上,您可以使用telinitinit命令在运行级别之间切换。在今天的系统上,您仍然可以使用这些命令,但是真正的运行级别概念不再适用。这些现在映射到不同的 systemd“目标”

但是,如果您碰巧在旧的 SysV 主机上,您可以使用如下的telinit命令:

$ sudo telint 3

这将把你从当前的运行级别移动到运行级别 3。更多信息见man页。

了解 SysV Init 中的 Initd 脚本

Initd 脚本是停止和启动进程的脚本,有时还提供进程的当前状态和可能的其他操作。今天的 initd 脚本据说是 LSB 兼容的,因为它们有特定的结构。让我们从看一个init脚本开始:看一看位于我们 Ubuntu 服务器上/etc/init.dpostfix脚本。

LSB 是什么?Linux Standard Base 的缩写,它是各种 Linux 发行版同意的一组标准,使每个使用它的人,尤其是使用它进行开发的人的生活更加轻松。它寻求为 Linux 配置、文件位置、包名和其他约定制定通用标准。近年来,LSB 一直在努力保持相关性,根据它认证的发行版越来越少。你可以在 www.linuxfoundation.org/en/LSB 了解更多信息。

#!/bin/sh –e

### BEGIN INIT INFO
# Provides:          postfix mail-transport-agent
# Required-Start:    $local_fs $remote_fs $syslog $named $network $time
# Required-Stop:     $local_fs $remote_fs $syslog $named $network
# Should-Start:      postgresql mysql clamav-daemon postgrey spamassassin saslauthd dovecot
# Should-Stop:       postgresql mysql clamav-daemon postgrey spamassassin saslauthd dovecot
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Postfix Mail Transport Agent
# Description:       postfix is a Mail Transport agent
### END INIT INFO

Listing 6-10.Ubuntu Postfix Script Header

我们在这里省略了一些细节,但是我们讨论的重要信息在###BEGIN###END注释之间。符合 LSB 的 init 脚本需要一个标准头。该标准要求你必须有以下关键字,后跟一个参数列表,有些用一个$前缀表示。这些$参数是保留的虚拟设备,在 LSB 规范中有描述。它们旨在为您的 init 脚本提供更多的顺序。例如,$local_fs意味着“所有本地文件系统都被挂载了”因此,当您看到“Required-Start’,”时,这意味着在执行该脚本之前必须启动列出的服务。清单 6-10 中的例子会阻止 Postfix 启动,除非所有的文件系统都已挂载,日志服务正在运行,指定的服务器正在运行,网络已经建立,并且时间已经同步。

列表 6-10 中的关键字的含义如下:

  • 简要说明本服务提供的内容。此信息由其他服务使用。
  • Required-Start:列出该脚本启动时必须可用的服务。
  • Required-Stop:表示在停止此处列出的服务之前,必须停止该服务。
  • Should-Start:定义在服务启动前可以启动的服务列表,尽管不是强制性的。
  • Should-Stop:表示此服务应该在此处列出的服务之前停止,尽管不是强制性的。
  • Default-Start:定义服务应该运行的默认运行级别。
  • Default-Stop:定义该服务不应该运行的默认运行级别。
  • Description:给出服务的描述。

有关 LSB init 脚本的更多信息,请访问

进一步剖析清单 6-10 示例,注意默认开始和默认停止关键字。这些映射到我们之前讨论过的运行级别。在这个脚本中,我们被告知后缀应该从运行级别 2、3、4 和 5 开始。我们不应该在运行级别 0、1 和 6 下运行。

Note

CentOS 5 等较老的操作系统使用前 LSB 标准来编写init.d脚本。这些标准被 LSB 标准所取代。Ubuntu 已经在很多版本中使用了符合 LSB 的init.d脚本。

postfix init脚本的其余内容(我们在清单 6-10 中省略了)是用于启动、停止、有时查询由脚本管理的应用程序或服务的状态的指令。

How Systemd Emulates Sysv Init

systd 的实现支持与 SysV Init(以及 Upstart,正如它发生的那样)的向后兼容。它通过以下方式实现这一点:

  • 检查 systemd 目录路径中的服务单元文件
  • 如果找不到,它会在/etc/init.d/中查找没有.service后缀的服务文件
  • 如果找到一个,它使用systemd-sysv-generator将其转换成 systemd 单元文件
  • 它使用 LSB 运行级别来决定它需要在哪个目标中执行。

当我们在/etc/init.d/中有 SysV Init (LSB 兼容)文件,并使用 systemctl 命令启动服务时,systd 将找到这些文件,然后通过一个生成器(systd-SysV-generator)运行它们。其输出然后被转换成 Systemd 单位。

SysV 运行级别也映射到 systd 目标。Systemd 将把每个运行级映射到一个适当的目标,就像运行级 3 将链接到多用户目标一样通过使用符号链接。

$ ls -la /usr/lib/systemd/system/runlevel3.target
lrwxrwxrwx. 1 root root 17 Feb  3 22:55 /usr/lib/systemd/system/runlevel3.target -> multi-user.target

因此,当您发出 telinit 3 命令时,Systemd 将运行以下目标。

[Unit]
Description=Multi-User System
Documentation=man:systemd.special(7)
Requires=basic.target
Conflicts=rescue.service rescue.target
After=basic.target rescue.service rescue.target
AllowIsolate=yes

对于不是“Systemd 就绪”的应用程序和包,只要它们具有符合 LSB 的 init.d 脚本,就没有区别。

最初的 SysV Init 实际上已经很久没有在操作系统上使用了,Upstart 被用来管理基于 Red Hat 和 Debian 的系统中的 SysV。

管理服务

在第四章中,我们向您介绍了流程和服务的概念。您运行的每个应用程序和命令都会创建一个进程。某些过程在命令完成时结束,例如,列出目录的内容。其他进程的运行时间更长,除非您请求它们停止或者您重新启动主机,否则它们不会停止。这些长时间运行的进程中的一些运行应用程序和服务,如邮件和 web 服务器或打印或文件服务。这些类型的长时间运行的进程通常被称为守护进程。守护进程是在后台运行的进程;也就是说,它们不需要连接到控制台。正如我们在第四章中解释的,这些过程中的每一个都有一个名字;例如,sshd daemon 或 httpd,或者 apache web 服务器的 Apache。

Note

Linux 上的所有进程都源于一个父进程。派生一个进程包括一个父进程制作它自己的副本,称为子进程。对于 systemd,Systemd 进程将是父进程。这意味着进程可以持续存在,而不需要附加到控制台或用户会话。当父进程停止时,它的所有子进程也会停止。例如,如果您终止主机上的systemd进程,您将停止主机上的所有进程。这不是一个很好的主意,可能会对宿主产生不良影响。如果您需要启动和停止所有服务,您应该使用我们在本章中介绍的命令。

用系统管理服务

正如我们之前所解释的,在 Systemd 中,目标用于对单元进行分组,以便当您将主机引导到特定目标时,您可以预期某些服务是可用的,而其他可能冲突的服务将被停止。如果您更改目标,您的服务也可以从运行状态更改为停止状态。您还可以随时手动停止和启动单个服务。

Systemd 有一个命令工具叫做 systemctl。Systemctl 用于管理本地、远程或虚拟容器上的 systemd 资源。通过发出--help参数,我们可以看到参数列表、单元文件命令、机器命令、作业命令等等。首先,让我们看看如何管理单个服务,然后我们可以看看更广泛的系统。

Note

是的,您可以使用 systemctl 来管理远程或虚拟机。当用--host <hostname>指定时,systemctl 使用 SSH 登录到远程系统并执行 systemctl 命令。当用--machine <machinename>指定时,它将在指定的本地容器上执行命令。

系统服务—状态、停止和启动

首先,让我们看看如何获得一个名为 postfix 的服务的状态,它是一个用于发送电子邮件的 SMTP 服务器。

$ sudo systemctl status postfix.service
• postfix.service - Postfix Mail Transport Agent
   Loaded: loaded (/usr/lib/systemd/system/postfix.service; enabled; vendor preset: disabled)
   Active: active (running) since Wed 2016-04-27 20:35:10 AEST; 3h 0min ago
  Process: 642 ExecStart=/usr/sbin/postfix start (code=exited, status=0/SUCCESS)
  Process: 636 ExecStartPre=/usr/libexec/postfix/chroot-update (code=exited, status=0/SUCCESS)
  Process: 621 ExecStartPre=/usr/libexec/postfix/aliasesdb (code=exited, status=0/SUCCESS)
Main PID: 993 (master)
   CGroup: /system.slice/postfix.service
           ├─ 993 /usr/libexec/postfix/master -w
           ├─1003 qmgr -l -t unix -u
           └─1926 pickup -l -t unix -u

Apr 27 20:35:09 au-mel-centos-1 systemd[1]: Starting Postfix Mail Transport Agent...
Apr 27 20:35:10 au-mel-centos-1 postfix/master[993]: daemon started -- version 2.10.1, configuration /etc/postfix
Apr 27 20:35:10 au-mel-centos-1 systemd[1]: Started Postfix Mail Transport Agent.

Listing 6-11.Getting the Status of the postfix
Service

我们在这里可以看到很多细节。我想引起你注意的主要事情是加载的和活动的。Loaded 向您显示启动此服务所使用的单元文件。Active 为您提供服务的当前状态,“active”,以及它已经运行了多长时间。从这个输出中,我们还可以看到这个服务是通过运行/usr/sbin/postfix start启动的,并且那个进程(642)以退出代码 0 或成功结束。我们还执行了另外两个ExecStartPre命令,都很成功。这些很好地映射到 CentOS 上的 systemd postfix.service单元文件。

[Service]
Type=forking
PIDFile=/var/spool/postfix/pid/master.pid
EnvironmentFile=-/etc/sysconfig/network
ExecStartPre=-/usr/libexec/postfix/aliasesdb
ExecStartPre=-/usr/libexec/postfix/chroot-update
ExecStart=/usr/sbin/postfix start

清单 6-11 中需要注意的另一件事是 CGroup 信息。这表明postfix.service在 CGroup system.slice下。CGroups 在内核中实现,用于对系统资源进行分组,以达到隔离和提高性能的目的。

清单 6-11 中的输出非常详细,有助于系统管理员查看详细信息,但是如果我们以编程的方式来做,要查看我们服务的状态需要“搜索”大量数据。因此,systemctl 提供了一种压缩相同信息的方法,使程序很容易检测到状态。

$ sudo systemctl is-active postfix.service ; echo $?
active
0

上述命令提供了标准输出的状态(“活动”、“非活动”)和退出代码(0 表示活动,非零表示其他状态),这两种状态都可以在可能管理服务的脚本或程序中进行测试。在上面的代码行中,我们使用了 bash shell 中的特殊变量$?,它保存了最后一次命令运行的退出代码。

现在我们知道了如何查看我们的服务的状态,我们可以看到它已经在运行,让我们停止服务。

$ sudo systemctl stop postfix.service

该命令没有明显的输出,因此如果我们查询状态,可以看到它已经停止。

● postfix.service - Postfix Mail Transport Agent
   Loaded: loaded (/usr/lib/systemd/system/postfix.service; enabled; vendor preset: disabled)
   Active: inactive (dead) since Thu 2016-04-28 11:28:04 AEST; 1s ago
  Process: 5304 ExecStop=/usr/sbin/postfix stop (code=exited, status=0/SUCCESS)
.....
Main PID: 5267 (code=killed, signal=TERM)

很明显,我们可以看到服务是“不活动的”,主 PID 被 TERM 信号终止。

Note

进程可以接受来自 kill 命令的“信号”。一个 TERM 或 SIGTERM 信号告诉一个进程结束它正在做的事情并退出。你也可以发送更严厉的信号,比如 SIGKILL,它会立即退出进程,可能是在重要的事情进行中。SIGHUP 有时可以用来重新加载正在运行的进程的配置。更多信息见 http://linux.die.net/man/7/signal

现在 postfix 服务停止了,我们将再次启动它。

$ sudo systemctl start postfix.service

同样,控制台没有输出;我们运行状态以确认它正在运行。

● postfix.service - Postfix Mail Transport Agent
   Loaded: loaded (/usr/lib/systemd/system/postfix.service; enabled; vendor preset: disabled)
   Active: active (running) since Thu 2016-04-28 11:51:45 AEST; 2s ago

我们在本节中看到的是如何使用systemctl命令启动、停止和查看服务的状态。但是这些服务最初是如何开始的呢?这些服务是如何在引导时启动的?让我们现在调查一下。

孤立的目标

我们已经说过,systemd 是管理我们主机上所有服务的进程,systemctl 是我们用来管理 systemd 进程的命令。我们已经讨论了 systemd 目标,它们使系统达到某一状态,在该状态下正确的进程正在运行。在 SysV 中,我们称之为运行级,它们由一系列从/etc/rcX.d目录符号链接的文件控制。对于 systemd 来说,单一运行级别的概念并不十分准确,因为许多目标可以同时处于活动状态。但是,在 systemd 中,有些目标有特殊的用途。

在 systemd 中,像多用户这样的目标,如果在它们的单元文件中设置了布尔值 AllowIsolate=yes。这意味着这些目标将响应systemctl isolate命令。这个命令允许我们启动离散的系统“状态”,其中我们启动一个单元(及其所有依赖项)并停止所有其他单元。

让我们发现我们当前的系统状态。我们将再次使用 systemctl 命令,这一次我们将列出系统上运行的当前目标。

$ sudo systemctl list-units --type target
UNIT                     LOAD   ACTIVE      SUB   DESCRIPTION
basic.target           loaded   active   active   Basic System
cryptsetup.target      loaded   active   active   Encrypted Volumes
getty.target           loaded   active   active   Login Prompts
local-fs-pre.target    loaded   active   active   Local File Systems (Pre)
local-fs.target        loaded   active   active   Local File Systems
multi-user.target      loaded   active   active   Multi-User System
network.target         loaded   active   active   Network
paths.target           loaded   active   active   Paths
remote-fs.target       loaded   active   active   Remote File Systems
slices.target          loaded   active   active   Slices
sockets.target         loaded   active   active   Sockets
swap.target            loaded   active   active   Swap
sysinit.target         loaded   active   active   System Initialization
timers.target          loaded   active   active   Timers

下面是我们系统上运行的当前目标的列表。其中一些目标是“特殊”目标,如 swap.target,用于管理交换分区和交换文件及片。target,用于设置所有的 CGroup 片单元。多用户目标由所有这些其他目标组成。如果您查看 multiuser.target 的单元文件,您会看到它需要= basic . target。basic . target 具有 Requires = sysinit . target。sysinit . target 具有 Wants = local-fs . target swap . target。我在这里省略了一些其他细节,如冲突和 After 指令,但是您应该能够看到多用户目标由所有这些其他目标组成,这就是 systemd 如何知道要拉入哪个目标的原因。

我们已经说过,这些目标中的一些定义了机器的“状态”。这些状态类似于救援、多用户、图形化和关机。当您的系统启动后,您可以通过发出以下命令在这些目标之间移动:

$ sudo systemctl isolate graphical.target

这个命令将做什么查看 graphical.target 单元文件,确定它是否具有 AllowIsolate=yes,然后执行 Requires、Conflicts、After、Wants 和其他指令,使系统进入那个“状态”当您查看 graphical.target 单元文件(在/lib/systemd/system目录中找到)时,您会注意到它需要多用户目标,并且在完成之前不应该启动。那么 systemd 如何知道如何在启动时启动我们的 postfix 服务呢?

禁用服务

让我们通过查看禁用后缀服务时会发生什么来探究这一点。如果我们不想让服务在系统重启时启动,我们需要禁用它。要禁用它,让我们执行以下命令:

$ sudo systemctl disable postfix.service
Removed symlink /etc/systemd/system/multi-user.target.wants/postfix.service.

这个命令的输出告诉我们它做了什么。它从/etc/systemd/system/multi-user.target.wants目录中删除了一个符号链接(symlink)。如果我们在/lib/systemd/system目录中查看我们的 postfix.service 文件,我们会看到[Install]部分有一个选项,WantedBy=multi-user.target。如果我们记得[Install]部分包含的指令告诉 systemctl 在我们启用或禁用服务时创建符号链接或删除符号链接,就是这个选项告诉 systemctl 在我们禁用服务时删除符号链接。

支持服务

现在我们已经完成了,我们希望启用该服务,以便它在系统启动时自动启动。让我们再次启用该服务,看看输出是什么:

$ sudo systemctl enable postfix.service
Created symlink from /etc/systemd/system/multi-user.target.wants/postfix.service to /usr/lib/systemd/system/postfix.service.

输出告诉我们,它已经在/etc/systemd/system/multi-user.target.wants目录中创建了指向 postfix.service 文件的符号链接。现在,当 systemd 启动 multiuser.target 时,它将查看multi-user.target.wants目录并启动这些服务。

现在让我们尝试获得 systemd 正在控制的所有当前正在运行的服务的列表(参见清单 6-12 )。

$ sudo systemctl --type=service --state=running
UNIT                       LOAD      ACTIVE    SUB        DESCRIPTION
atd.service                loaded    active    running    Job spooling tools
crond.service              loaded    active    running    Command Scheduler
getty@tty1.service         loaded    active    running    Getty on tty1
NetworkManager.service     loaded    active    running    Network Manager
postfix.service            loaded    active    running    Postfix Mail Transport Agent
systemd-journald.service   loaded    active    running    Journal Service
systemd-logind.service     loaded    active    running    Login Service
systemd-udevd.service      loaded    active    running    udev Kernel Device Manager
wpa_supplicant.service     loaded    active    running    WPA Supplicant daemon
Listing 6-12.Listing the Running Services

在清单 6-12 中,我们可以看到使用- type=service 和- state=running 选项运行的systemctl命令的(修剪后)输出。如果您运行不带任何选项或参数的 systemctl 命令,您将获得 systemd 已经加载的每个已加载单元定义(单元文件)的完整列表。我们通过添加我们感兴趣的单元的类型和该单元的状态来减少这个列表。您可以在列表中看到,我们的 postfix.service 已经加载、激活并正在运行。您也可以通过在命令中添加- all 来查看所有不活动的单元。

管理 SysV 样式文件

到目前为止,我们已经看到了如何处理控制服务的 systemd 单元文件。记住,我们也可以使用旧的 SysV init.d文件。我们还说过,systemd 将首先在它自己的目录路径中寻找一个name.service文件,或者在我们的例子中是postfix.service。如果找不到,它将在/etc/init.d/中寻找一个可执行的后缀文件(没有.service后缀)。如果有一个并且它是 LSB 兼容的,它将运行systemd-generate-sysv命令来创建一个 systemd 包装文件。

Ubuntu 服务器仍然使用一些 SysV init.d 系统文件(而 CentOS 原生使用 systd ),所以我们将看看该发行版如何管理 postfix 服务。当系统启动时或触发systemctl daemon-reload后,如果 systemd 找到一个 postfix init.d服务文件,它会将其传递给 systemd-generate-sysv,后者会创建一个 postfix.service 并将其放在/run/systemd/generator.late/postfix.service中。它还将在/run/systemd/generator.late目录中创建到multi-user.target.wants目录的适当符号链接。

生成的机组文件具有与其他机组文件相同的选项([机组]和[服务])。启动、停止和重新加载选项引用如下init.d文件:

[Service]
...
ExecStart=/etc/init.d/postfix start
ExecStop=/etc/init.d/postfix stop
ExecReload=/etc/init.d/postfix reload

我们可以使用同一个 system CTL start | stop postfix . service 来管理服务。同样,我们可以使用 systemctl enable|disable 来管理我们是否希望它在引导时启动。

$ sudo systemctl disable postfix
postfix.service is not a native service, redirecting to systemd-sysv-install
Executing /lib/systemd/systemd-sysv-install disable postfix

当处理 SysV init.d 脚本时,systd 做两件事。一种是将相同的符号链接应用于后缀服务的multi-user.target.wants目录(在/run/systemd/generator.late/目录中)。但是在这里您也可以看到,由于我们没有使用本地 systemd 服务,我们将把它重定向到另一个兼容性脚本,以处理 SysV land 中的启用和禁用。这个脚本在后台执行旧的 SysV init.d 管理器,在这里是在 Ubuntu,update-rc.d上。在 CentOS 上,这将是chkconfig命令。

让我们快速浏览一下这些命令,以便您也熟悉在这些情况下会发生什么。

update-RC . d—管理 Ubuntu SysV init.d

通过update-rc.d命令管理 SysV init.d 服务 onUbuntu。如果得到指示,update-rc.d command将创建进入/etc/rc?.d目录的符号链接或者删除它们。update-rc.d命令采用表 6-2 中列出的选项。

表 6-2。

update-rc.d Options

| [计]选项 | 描述 | | --- | --- | | `Start` | 允许您显式声明运行级别和启动顺序。 | | `Stop` | 允许您明确说明希望停止服务的顺序和运行级别。 | | `Defaults` | `update-rc.d`脚本将用默认的开始序列(S20)创建进入运行级 2、3、4 和 5 的开始符号链接,并用停止序列(K80)创建进入运行级 0、1、6 的停止符号链接。 | | `Remove` | 只要文件`/etc/init.d/` `script-name`已经被删除,就从每个运行级别删除符号链接。(更多信息见`-f`。) | | `-n` | 给你一个在不改变任何东西的情况下会发生什么的预演。 | | `-f` | 当与 remove 选项一起使用时,强制从`/etc/rcn.d`目录中删除符号链接,即使`/etc/init.d/` `script-name`仍然存在。 |

update-rc.d命令将简单地将init.d脚本链接到/etc/rcN.d目录(其中 N 是运行级别),通常默认运行级别为 2、3、4、5,启动优先级为 S20,运行级别为 0、1、6,停止优先级为K80

从命令行,您可以发出update-rc.d来操作在特定运行级别运行的服务。例如,要使用 Ubuntu 默认值打开服务,您可以发出以下命令:

$ sudo update-rc.d postfix start defaults

如前所述,默认情况下,init.d脚本象征性地链接到/etc/rcN.d目录,并被赋予 20 和 80 的标准启动和停止优先级。

您可以使用以下命令指定希望服务启动的运行级别和优先级:

$ sudo update-rc.d postfix start 23 40

这里,我们将服务设置为在运行级别 2 和 3 启动,优先级为 40。

要在运行级别 2 中关闭该服务,请发出以下命令:

$ sudo update-rc.d postfix stop 2

前面的命令会将一个K80postfix符号链接添加到/etc/rc2.d目录中。

要从所有运行级别删除服务,可以发出以下命令:

$ sudo update-rc.d postfix remove

如果您试图删除的服务的init.d脚本仍然存在于/etc/init.d目录中,如果您没有卸载 Postfix,就会出现这种情况,除非您使用-f选项,否则会出现错误。在这种情况下,您发出以下命令:

$ sudo update-rc.d -f postfix remove

这就是管理 Ubuntu SysV init.d 的方法。接下来让我们看看如何关闭系统。

关闭并重新启动您的 Linux 主机

有几种方法可以从 GUI 和命令行关闭和重启 Linux 主机。在命令行中,我们可以使用systemctl命令来管理主机的电源状态。

要关闭系统,我们将发出以下命令:

$ sudo systemctl poweroff

这将立即关闭系统电源。您还可以发出重启、暂停、休眠、睡眠等命令。更多信息见systemctl --help

我们仍然可以发出旧的命令来关闭主机——您可以发出适当命名的shutdown命令:

$ sudo shutdown –h now

systemctl poweroffshutdown命令都链接到/lib/systemd/system/poweroff.target的同一个 systemd 目标。

从 GUI 中,关闭主机很容易。找到电源符号,通常在屏幕的右上角,然后选择Shutdown选项。

计划服务和命令

您可以使用 Linux 调度重复的作业,使其在指定的事件、指定的时间或指定的间隔运行。有两种方法可以做到这一点。一个是使用 systemd 计时器,另一个是使用名为 Cron 的工具。

系统计时器

Systemd 定时器是以后缀*.timer结尾的单元文件。它们控制服务何时运行。预计这些将取代 Cron 来管理定期调度的作业管理。

它比 Cron 更有优势,因为它不仅可以根据挂钟(系统时间)触发事件,还可以根据上次事件运行后的时间、引导、启动或这些事情的组合触发事件。定时器单元文件也可以用来触发另一个没有定时器的服务单元。

该配置要求单元文件中有一个[Timer]部分。让我们以管理 apt 缓存日常检查的每日 apt 时间表为例(参见清单 6-13 )。

[Unit]
Description=Daily apt activities

[Timer]
OnCalendar=*-*-* 6,18:00
RandomizedDelaySec=12h
AccuracySec=1h
Persistent=true

[Install]
WantedBy=timers.target

Listing 6-13.Systemd timer /lib/systemd/system/apt-daily.timer

在这里,您可以看到我们有常用的[单元]和安装部分。OnCalendar 选项指定我们应该在什么时候运行服务,这可以通过星期几、年、月、日,然后 HH:MM:SS 或任何方式来指定!man 7 systemd.timer 中有一个完整的选项列表。星号(*)表示任何值,这是每日(*-*-*)的简写。触发时间为 06:00 或 18:00,随机窗口为 12 小时(RandomizedDelaySec),误差为 1 小时(AccuracySec)。Persistence=true 表示如果错过了最后一次启动就运行(比如系统被关闭)。

假设我们有一个 monitor.service 文件,它向一个网站发出一个 cURL POST 请求,告诉我们系统已经启动,并在此后每 20 分钟检入一次。

[Unit]
Description=Tell the monitoring service we are up

[Timer]
OnBootSec=2min
OnUnitActiveSec=20min

[Install]
WantedBy=timers.target

我们将把这个配置放到一个名为/etc/systemd/system/monitor.timer的文件中,紧挨着我们的monitor.service文件。在这里,我们可以看到一个定时器的单元文件需要这三个部分,[单元]、[定时器]和安装

[定时器]部分是我们放置定时器选项的地方。我们在这里选择在引导事件(OnBootSec)后 2 分钟触发 cURL 作业,然后在最后一个事件(OnUnitActiveSec)后每 20 分钟触发一次。这里的时间可以表示为 1h (1 小时)、2w (2 周)或各种其他方式。这些被称为单调定时器,而不是我们在清单 6-13 中看到的实时定时器。

[Install]部分中的 WantedBy=timers.target 创建指向 timers 目标的符号链接,该目标启用 systemd 中的计时器。我们可以通过以下方式启用计时器:

$ sudo systemctl enable monitor.timer
Created symlink from /etc/systemd/system/timers.target.wants/monitor.timer to /etc/systemd/system/monitor.timer.

现在,我们可以列出如下计时器:

你可以在图 6-9 中看到我们的计时器。您可以看到它激活了我们的 monitor.service,并且在 18 分钟前成功完成。你也可以看到apt-daily.timer

A185439_2_En_6_Fig9_HTML.jpg

图 6-9。

Listing timers

它不能简单地在失败时触发一封电子邮件。有些人依赖这个特性,这也是他们选择 Cron 的原因。

有关计时器的更多信息,请参见

Cron 简介

我们需要向您展示最后一种类型的服务管理:调度。您可能已经熟悉了 Microsoft 任务计划程序,可以使用它来计划在给定的分钟、小时、天、周或月运行一次或定期重复的任务。Linux 中的对等物称为 crontab(计时表的缩写)。它的目的是根据主机的时钟在设定的时间提交任务。任务可以是您想要的任何脚本或应用程序。通常,您会在 crontabs 中找到维护类型的任务。这些可以被安排为每晚、每周或每月运行,并执行某种脚本,比如删除/var/ log/httpd目录中超过两个月的所有文件。

Cron 作业(crontab 执行的任务)是在/etc/crontab文件中定义的目录下的一系列脚本中定义的。这些被称为系统 cron 作业。/etc/crontab文件中的目录列表如下所示:

$ less /etc/crontab
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# run-parts
01  *  *  *  *  root  run-parts  /etc/cron.hourly
02  4  *  *  *  root  run-parts  /etc/cron.daily
22  4  *  *  0  root  run-parts  /etc/cron.weekly
42  4  1  *  *  root  run-parts  /etc/cron.monthly

您不应该编辑这个文件,因为它是系统 crontab 文件,每次更新 crontab 时,它都可能被替换为新版本。这意味着任何更改都将丢失。此外,如果你犯了一个错误,你可能会导致其他问题。但是,它确实提供了一个很好的 crontab 文件语法示例。

Tip

当一台主机用于运行许多虚拟化服务器时,您应该更改 cron 作业在每台虚拟服务器上的启动时间,以确保它们不会同时启动。让多个虚拟服务器同时启动它们的日常 cron 任务会影响系统性能。

文件顶部列出的是 SHELL、PATH、MAILTO 和 HOME 环境变量,我们在第四章中已经描述过了。以#开头的行是注释,可以忽略。在文件的下面,您可以看到五列,一列是数字或*,一列是root,一列是run-parts,最后是一个目录列表。

前五列表示分钟、小时、月中的日、月和星期几。让我们看看最后一行:

42 4 1 * * root run-parts /etc/cron.monthly

这里,42 是一小时的第 42 分钟,小时是 4(小时基于 24 小时制),1 表示一个月的第一天。所以 crontab 会在每个月的第一天凌晨 04:42 运行最后一行。

Note

您还可以为月份、月份中的日期和星期几列指定标准的三个字母缩写,例如,sun分别代表星期日和aug代表八月。

任何带有星号(*)的列都意味着所有值都有效,并且不受运行时间的限制。让我们再看一下最后一行:

42 4 1 * 3 root run-parts /etc/cron.monthly

这里,我们将“星期几”列中的值更改为 3。我们的作业现在将在每月第一天以及每周三的凌晨 4:42 运行。

Note

一周中的每一天从星期日的 0 点开始,到星期六的 6 点结束。当在“一月中的第几天”列中指定值时,天数是累积的。因此,作业将在“一月中的某一天”列中列出的所有日期以及“一周中的某一天”列中列出的每个工作日执行。

您还可以指定自动重复的作业,如下所示:

*/2 4 1 * 3 root run-parts /etc/cron.monthly

这里,我们没有指定运行的确切时间,而是使用了符号*/2。这种表示法告诉 cron 每当分钟数被 2 整除时运行作业。这允许你做一些强有力的事情。例如,您可以在 hour 列中使用*/4,作业将每隔四小时运行一次,如下所示:

*/2 */4 1 * * root run-parts /etc/cron.monthly

使用逗号表示一系列值,例如

2 0,1,2,3,4 1 * * root run-parts /etc/cron.monthly

这将在凌晨 12:02、1:00、2:00、3:00 和 4:00 运行作业

您也可以指定数字范围,如下所示:

2 0-4,12-16 1 * * root run-parts /etc/cron.monthly

在这里,该命令将在凌晨 12 点和 4 点之间以及中午 12 点和下午 4 点之间的整点后 2 分钟运行

下一列root代表运行该程序的用户。当您添加自己的 cron 作业(或脚本)时,可以将其设置为任何有效用户。

run-parts选项是正在运行的命令。run-parts是一个特殊的命令,它将运行指定目录下的任何可执行脚本。在这种情况下,run-parts将切换到/etc/cron.hourly目录、/etc/cron.daily目录等等,并运行它在那里找到的可执行脚本。

让我们检查一个系统 cron 目录,例如,/etc/cron.daily目录,并检查 Red Hat 主机上已经存在的一个脚本。除非另有定义,否则这些是每天运行一次的系统 crons。

Note

如果需要,您可以编辑这些 crontab 目录中的脚本;但是,当您的软件包更新时,任何更改都可能被覆盖。您还可以在这些目录中添加自己的脚本,每小时、每天、每周或每月运行一次。

$ ls -l /etc/cron.daily/
-rwxr-xr-x 1 root root  379 Dec 19  2016 0anacron
-rwxr-xr-x 1 root root  118 Oct  1 00:06 cups
-rwxr-xr-x 1 root root  180 Oct 22  2017 logrotate
-rwxr-xr-x 1 root root  114 Jan 16  2018 rpm
-rwxr-xr-x 1 root root  290 Nov 26  2016 tmpwatch

让我们查看其中一个文件/etc/cron.daily/rpm,看看里面有什么。

$ less /etc/cron.daily/rpm
#!/bin/sh

/bin/rpm -qa --qf '%{name}-%{version}-%{release}.%{arch}.rpm\n' 2>&1 \
           | /bin/sort > /var/log/rpmpkgs

这个每天执行的脚本用主机上所有 RPM 包的排序列表填充了/var/log/rpmpkgs文件。

Note

我们将在第八章中进一步讨论 RPM 包。

个人用户也可以创建 crontab。您可以使用crontab –e命令创建和编辑现有的 crontabs。如果用户的 crontab 不存在,这个命令会在/var/spool/cron/ username中创建一个 crontab 文件。

用户的 cron 作业中使用的语法与您之前看到的系统 crontab 文件中的语法相同,只有一处不同。您只能在系统 crontab 文件中指定用户字段。让我们看一个由用户 jsmith 使用 crontab 的–l或 list 选项创建的例子。

$ crontab -l
*/2 * * * * [ -e /tmp/log ] && rm -f /tmp/log

您将看到该用户安排的所有 cron 作业的列表。这是一系列简单的命令,首先检查名为/tmp/log的文件是否存在,如果存在,就删除它。设置为每 2 分钟运行一次(*/2)。

作为特权用户,您可以通过发出带有–u username选项的crontab命令来查看另一个人的 cron。

$ sudo crontab –u ataylor –l
1 2 * * * /usr/local/bin/changeLog.sh

您还可以通过发出相同的 crontab 命令和–u username -e 选项来编辑另一个人的 cron。

$ sudo crontab –u ataylor –e

这允许您为用户编辑 crontab。

您还可以通过发出带有–r选项的crontab命令来删除您的 crontab 或另一个用户的 crontab。

$ sudo crontab –u ataylor –r

这将删除 ataylor 的 crontab 文件:/var/spool/cron/ataylor

您的主机有一个监控 cron 作业及其任何更改的服务。它还会在调度时执行单个作业。这个服务被称为 crond,可以通过我们前面谈到的 systemctl 命令来启动和停止。

$ sudo systemctl start|stop|reload cron(d if CentOS)

摘要

本章探讨了主机如何引导以及它背后的进程,如init守护进程。您已经学习了如何管理您的服务,如何启动和停止它们,以及如何从不同的运行级别添加和删除它们。您还了解了 LSB 项目,并对新贵init守护进程有了一个大致的了解。

现在,您应该能够执行以下操作:

  • 描述 Linux 的引导过程。
  • 使用、配置和保护 GRUB2。
  • 描述 init 脚本,包括使用 LSB 标准的脚本。
  • 描述 SysV、Upstart 和 Systemd
  • 启动和停止 Red Hat 和 Ubuntu 上的服务。
  • 了解目标和运行级别。
  • 用 crontab 和 Systemd 定时器调度任务。

在下一章,我们将向您展示如何配置您的网络,讨论防火墙,并向您介绍 Linux 安全性。

七、网络和防火墙

到目前为止,我们已经向您展示了 Linux 的一些基本特性,但是最重要的特性之一是联网。通过网络,您的主机可以与其他主机通信,您的应用程序可以与您的用户和世界通信。在这一章中,我们将描述如何设置你的主机的网络,然后如何使用防火墙保护网络免受攻击者的攻击。

Note

防火墙是一系列控制通过网络访问主机的规则。

我们将教您如何配置您的网卡或接口,以及如何为它们分配 IP(互联网协议)地址。您将学习如何连接到其他网络,以及如何排除连接故障。

我们还将看到一个名为 Netfilter 的软件应用程序,它是所有 Linux 发行版通用的防火墙。您将学习如何管理防火墙以及如何编写防火墙规则。为此,我们将向您介绍 Netfilter 的管理界面iptables。我们还将向您展示如何使用 iptables 工具,如 Firewalld 和 ufw。最后,我们还将向您展示如何使用 TCP Wrappers 来保护在您的主机上运行的守护进程,然后使用 Linux 建立到 ISP (Internet 服务提供商)的 PPPOE(以太网点对点协议)连接。

在本章中,我们将使用网络术语。我们不指望你是一个网络专家,但我们已经假定你有一些基本的知识。如果您觉得自己了解的还不够,我们建议您查看以下网站和教程:

Note

iptables用于保护您的网络服务。在第九章中,你会学到更多关于如何在你的主机上运行 DNS(域名系统)和 DHCP(动态主机配置协议)等网络服务的知识。

网络和网络简介

网络由硬件和软件组成。它们的复杂程度取决于它们的大小和所需的互联程度。在小企业中,你可能会有一个简单的网络。您可能有一个 web 服务器和邮件服务器,也可能有一个文件/打印服务器(有时所有这些服务器实际上是一台主机)。毫无疑问,您将连接到互联网,并且您可能希望与您组织中的其他人共享该连接。

你的业务性质和你所做的工作将在很大程度上决定你如何选择建立你的网络。如今,许多企业充分利用 SaaS(软件即服务)提供商为他们提供某些业务功能,如电子邮件、文件存储和访问。这些企业可能有简单的网络要求。或者,刚起步的企业通常只有一台主服务器,几乎可以完成企业所需的所有功能。它可以是集 DHCP、DNS、文件、邮件和 web 服务器于一体的服务器。熟悉微软产品的人会认为这与 Windows Small Business Server 类似。但随着业务的增长,它可能会开始将这些组合功能转移到自己的主机上。很少有大型企业会将整个公司的 IT(信息技术)基础架构托付给一台承担如此多角色的主机。应该尽可能避免这种单点故障,但是小型企业很少有机会为其希望提供的每项服务配备一台主机。

Caution

如果您的业务确实存在单点故障,如一台主机上的许多服务,备份和恢复就变得至关重要。丢失数据对您的企业来说可能是一场灾难,因此您应该始终拥有备份以及恢复主机和数据的能力。有关如何为您的组织实施备份和恢复策略的详细信息,请参见第十三章。

然后是你可能需要的硬件的互连部分。如果您要将办公室中的用户连接到单个网络,您将需要电缆、配线架交换机,并且最有可能需要一个可以创建无线网络的无线接入点。

Caution

无线网络是扩展网络的一种有用而廉价的方式。它们不需要昂贵的布线和交换机,而且您的员工在办公室可以更加灵活。然而,它们也带来了一些挑战。众所周知,如果安全措施不当,无线网络会允许攻击者连接并嗅探您的网络,并且它们的性能不如有线网络(使用物理电缆的网络)。例如,通过有线连接传输大量数据仍然比通过无线连接快得多。如果您正在考虑为您的企业提供无线网络,我们建议您阅读位于 http://en.wikipedia.org/wiki/Wireless_securityhttps://www.fcc.gov/consumers/guides/protecting-your-wireless-networkhttps://www.communications.gov.au/what-we-do/internet/stay-smart-online/computers/secure-your-internet-connection 的信息。

我们将从解释如何在单台主机上配置网络开始,并向您介绍配置更广泛的网络所需的工具和命令。

为了向您展示如何配置网络,我们将使用我们创建的示例网络。你可以在图 7-1 中看到这个示例网络。

A185439_2_En_7_Fig1_HTML.jpg

图 7-1。

Our example network

这是我们完整的网络图。到本章结束时,这个图表将不会像现在看起来那样令人生畏。我们将带您了解它的组件是什么,以及它们是如何配置的。我们还将解释所有这些 IP 地址的用途,以及我们如何能够阻止并从一个网络移动到另一个网络。

在本章中,我们将向您展示如何配置示例网络的元素。我们将配置一个名为gateway.example.com的防火墙/路由器主机。它有多个 IP 地址,每个网络都有一个 IP 地址,充当路由器:我们内部网络上的 192.168.0.254,我们无线网络上的 192.168.1.254,以及外部 IP 地址 10.0.2.155。

我们还将配置一个名为headoffice.example.com的主服务器。它在我们的内部网络上的 IP 地址将是 192.168.0.1。它将通过gateway.example.com主机路由到其他网络,如我们的无线网络、分支机构和互联网。

如您所见,我们将网络划分为不同的网段,并选择了不同的网络地址来显示这一点。如前所述,我们的无线网络的网络地址为 192.168.1.0/24,由 IP 地址为 192.168.1.250 的无线接入点提供支持。

Note

有几种方法可以将 IP 地址分配给电脑。您可以手动配置它,允许它请求一个地址池中的随机 IP,并允许它请求一个它保证得到的分配的 IP 地址。对于像网络网关这样的重要 IP 地址,您应该总是手动配置它,或者确保它们总是接收一个已知的保证地址。当我们在第十章中讨论 DHCP 时,会详细讨论最后一个选项。

我们每个分公司都有一个独立的网络地址,其范围从 192.168.10.0/24 到 192.168.30.0/24。这为我们在每个分支办公室提供了 254 个可能的节点(或设备),如果需要,还可以扩展它们。

IP Addressing and Subnetting

在前面的例子中,我们看了像192.168.10.0/24这样的 IPv4 网络。但是/24是什么意思呢?

由于 IPv4 的地址空间有限,所以有必要保留 IP 地址,这就是将地址空间划分成更小的块或子网的想法的由来。IPv4 地址由 32 个二进制位组成。这些位被分成 4 个八位字节(1 个八位字节等于 8 位)。地址255.255.255.255将被表示为11111111.11111111.11111111.111111110.0.0.000000000.00000000.00000000.00000000。每个二进制八位数中的每个位置代表一个值,前导位置是 128,然后是 64,然后是 32,然后是 16,然后是 8,然后是 4,然后是 2,尾随位置是 1。如果你把所有这些值加在一起,它们等于 255。

网络地址由网络 ID 和主机组成。在192.168.10.0/24的情况下,地址的前 24 位是网络 ID。它以二进制格式表示如下:

11000000.10101000.00001010
128+64.128+32+8.8+2

这样,最后一个二进制八位数可能有 254 个地址(不能使用 0 和 255,一个是全零地址(或基本网络地址),另一个是全 1 地址(或广播地址)——这些地址有特殊含义并被保留)。

因此,192.168.10.129/24的 IPv4 地址将表示如下:

11000000.10101000.00001010.10000001
128+64.128+32+8.8+2.128+1

IPv6 的情况略有不同,但基本原理是相同的。你看到的每个地址字符实际上是 4 位。意味着这个本地链接地址fe80::a00:27ff:fea4:da6b/64中的第一个字符(f)实际上是二进制的1111。每组四个字符代表 16 位(4x4 位),总共 80 位。完整的 IPv6 地址是 128 位。ISP 通常会根据您的要求提供/48 地址空间。这样就剩下 128 位,其中 48 位是可用地址空间,即 80 位。考虑到整个 IPv4 地址空间是 32 位,这是一个相当大的工作量。

这里还需要记住的是,每个 IPv6 都是全球可路由的,这与我们在 IPv4 示例中使用的私有地址空间不同,我们必须使用源网络地址转换(SNAT)将来自私有地址空间的连接路由到全球可路由的公共地址空间。在 IPv6 世界中,完整的 IPv6 地址由本地链接地址和网络地址组成,并且永远是唯一的。

作为一名使用 IPv6 的网络管理员,您将使用相同的二进制计算来确定您的 IPv6 子网划分,但是您可能的地址空间现在非常大!

我们还有一个本地有线网络,网络地址为 192.168.0.0/24。IP 地址为 192.168.0.1 的主服务器headoffice.example.com将能够通过我们建立的从该主机到远程分支机构的 VPN 网络与分支机构通信(我们将在第十五章中解释 VPN)。

我们本地有线网络中的台式机被分配了一个地址池,可在 192 . 168 . 0 . 101–192 . 168 . 0 . 200 范围内使用。这允许 100 个节点,并且可以根据需要进行扩展。

实际上,对于这种规模的网络,我们可能会有更多的服务器,并且可能会通过将服务器放在分支办公室来分散服务器。然而,出于本章的目的,我们将专注于图 7-2 所示的场景,在该场景中,您可以看到我们已经将网络分解为一个更小的模块。

A185439_2_En_7_Fig2_HTML.jpg

图 7-2。

The local wired network

在这里,我们可以集中精力为我们的办公室、gateway.example.comheadoffice.example.com构建主服务器。

我们将向您展示如何在gateway.example.com主机上设置 PPPoE 连接,作为我们的 ISP 和互联网的防火墙/路由器主机。

Note

PPPoE 是一种用于从 ADSL 调制解调器连接到互联网的方法。

我们的主机headoffice.example.com,将为公众提供邮件、网络和 DNS 服务(我们将在第十章、 11 章和 12 章中向您展示)。

我们将使用我们的gateway.example.com防火墙主机接受来自互联网的流量,并通过我们的内部网络将其路由到我们的主主机。总部主机还将为我们的本地网络提供 DNS、DHCP、NTP(网络时间协议)、SMTP(简单邮件传输协议)、IMAP(互联网消息访问协议)、HTTP(超文本传输协议)和 HTTPS(安全超文本传输协议)服务。我们还将向您展示如何在我们的网络中将流量从一台主机路由到另一台主机。让我们从设置我们的界面开始。

界面入门

我们将向您介绍的第一个网络要素是接口,即网络接口。主机上的一个或多个网卡都将是一个网络接口。当我们在第二章中向您展示如何安装一个发行版并配置一个网络时,我们正在配置一个接口。

Tip

接口通常处于两种主要状态—开启和关闭。开启的接口处于活动状态,可用于接收网络流量。关闭的接口不会连接到网络。

您可以配置哪些类型的接口?现代计算机主机现在可以很容易地拥有一个以上的网卡(或网络接口),有时有数百个 IP 地址。一个接口可以有多个别名,或者您可以将两个或多个接口绑定或组合在一起,以显示为一个接口。

Linux 使您能够在一个接口上拥有多个 IP 地址。接口对所有 IP 地址使用相同的 Mac 地址。绑定或成组接口由两个或多个网络接口组成,显示为单个接口。这可用于为接口提供更高的容错能力或增加带宽。绑定接口也可以有许多 IP 地址。我们将在“CentOS 的网络配置文件”和“Ubuntu 的网络配置文件”部分对此进行进一步的阐述。

您的每个网络接口可能至少分配有一个 IP 地址。我们将从演示一个名为 ip 的简单工具开始介绍接口,该工具来自 iproute (CentOS)或 iproute2 (Ubuntu)包,可用于查看和更改接口的状态和配置。最简单形式的ip工具可以像微软 Windows 上的ipconfig命令一样使用。

命令是一个非常强大的工具,可以用来配置你所有的网络。ip命令适用于不同的网络“对象”每个ip对象处理你网络的一个特定部分。如果您键入ip help,您将得到您可以管理的不同组件的列表。

在图 7-3 中,我们可以看到ip help命令的输出,您可以看到我们可以使用它来配置我们网络堆栈的许多部分。ip命令的基本语法如下:

A185439_2_En_7_Fig3_HTML.jpg

图 7-3。

ip help command

ip [ OPTIONS ] OBJECT { COMMAND | help }

您将处理最常见的对象是linkaddressroute。表 7-1 描述了该命令的这些和其余对象。

表 7-1。

Describing the Full List of Objects in the ip Command

| 目标 | 描述 | | --- | --- | | `link` | 该网络设备 | | `address` | 接口的地址(IPv4 或 IPv6) | | `addrlabel` | 允许您将标签应用于地址 | | `l2tp` | 基于 IP 的隧道以太网 | | `maddress` | 多播地址 | | `monitor` | Netlink 消息监控 | | `mroute` | 多播路由缓存条目 | | `mrule` | 管理多播路由策略中的规则 | | `neighbor` | ARP 或 NDISC 缓存条目 | | `netns` | 管理网络命名空间 | | `ntable` | 管理邻居缓存的操作 | | `route` | 路由表条目 | | `rule` | 路由策略数据库中的规则 | | `tcp_metrics` | 管理 tcp 指标 | | `tunnel` | IP 隧道 | | `tuntap` | 管理调谐器/TAP 设备 | | `xfrm` | 管理 IPSec 策略 |

这些对象都有手册页来进一步描述它们的用途。例如,关于 ip 地址命令的更多信息可以通过输入$ man ip-address找到。通过查看$ man ip页面的 SEE ALSO 部分,您可以看到所有相应的手册页。

我们现在将向您展示如何使用ip命令来发现有关您的网络接口的更多信息。要显示主机上所有接口的 IP 地址信息,我们使用以下命令:

$ ip address show

运行带有address show选项的ip命令会显示主机上的所有接口及其当前状态和配置。我们也可以使用简称ip addr show.

前面将列出所有主机接口及其地址。为了便于浏览特定接口的配置,您还可以显示单个接口,如下所示:

$ ip addr show enp0s3
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:a4:da:6b brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.1/24 brd 192.168.0.255 scope global enp0s3
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fea4:da6b/64 scope link
       valid_lft forever preferred_lft forever
Listing 7-1.Status Output of ip addr show on an Active

Interface

第一行显示接口的状态。2:只是一个订购号。enp0s3是接口的设备名称。在< >之间是接口标志。BROADCAST表示我们可以向链路上的所有其他主机发送流量,MULTICAST表示我们可以发送和接收多播数据包,UP表示设备正在运行。LOWER UP表示电缆或底层链路已打开。

同样,我们还有其他一些信息。界面的状态当前为UP。这意味着接口处于活动状态,如果配置正确,可能会接收流量。MTU或最大传输单位,是网络上数据包的最大字节数;1500 是常见的默认值。qdisc pfifo_fast 是排队规程,它是数据如何被发送到网络的;这是先进先出,也是默认的。您还可以将接口组合在一起并对其执行操作;这说明我们的在默认组。最后,qlen 是以太网缓冲区传输队列长度,它可以被称为网卡的速度,在本例中为 1,000 mbits。

输出还显示了三条重要信息,即该主机的 ipv4 地址、ipv6 地址和 MAC(媒体访问控制)地址。它有一个分配给它的 IP 地址,inet 192.168.0.1,我们很快会解释。下一行是 IPv6 地址,这里是inet6 fe80::a00:27ff:fea4:da6b/64,这是从 MAC 地址派生的链路本地 IPv6 地址。每个以太网网卡都有一个唯一的硬件标识符,用于识别它并与其他以太网设备通信。这个 MAC 地址可以在输出的第一行看到(在这个例子中是link/ether 08:00:27:a4:da:6b)。

Note

https://wiki.ubuntu.com/IPv6 or www.internetsociety.org/deploy360/ipv6/basics/ for more information on IPv6

IPv6 地址可用于通过无状态地址自动配置(SLAAC)与其他主机通信。SLAAC 经常被 PDA 和移动电话等设备使用,它需要一个不太复杂的基础设施来与本地网络上的其他设备进行通信。这是因为每个 SLAAC IPv6 地址都是全球可路由的,并与网络地址结合在一起形成一个全球唯一的地址。

Note

有关无状态自动配置和 IPv6 的更多信息,请参见 http://www.ipv6.com/articles/general/Stateless-Auto- Configuration.htm

在下一个示例中,我们将查看刚刚连接到主机的网络设备。此当前没有关联的 IP 地址:

  $ ip addr show
3: enp0s8: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 08:00:27:13:3c:00 brd ff:ff:ff:ff:ff:ff

将其与我们之前的输出进行比较,您可以立即注意到状态为DOWN,并且我们只能看到一个已经分配给网络设备enp0s8的 MAC 地址。现在让我们看看当我们打开设备或链接时会发生什么。我们通过使用下面的ip link子命令和ip addr show enp0s8来完成:

$ sudo ip link set dev enp0s8 up
...
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:13:3c:00 brd ff:ff:ff:ff:ff:ff
    inet6 fe80::a00:27ff:fe13:3c00/64 scope link
       valid_lft forever preferred_lft forever

设备enp0s8现在是UP,并且已经分配了一个qdisc (pfifo_fast).。有趣的是,我们还有一个 IPv6 生成的 IP 地址,该地址基于分配的 MAC 地址。

要关闭接口,您可以使用以下命令:

$ sudo ip link set dev enp0s8 down

您可以通过发出仅提供链接状态的以下命令来验证您所拥有的内容:

$ sudo ip link show enp0s8
3: enp0s8: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast state DOWN mode DEFAULT group default qlen 1000
    link/ether 08:00:27:13:3c:00 brd ff:ff:ff:ff:ff:ff

现在,让我们为网络设备添加一个 IPv4 地址。我们已经检查了我们的网络,IP 地址 192.168.10.1 是可用的。让我们将它添加到设备 enp0s8 中。

$ sudo ip addr add 192.168.10.1/24 dev enp0s8

查看$ sudo ip addr show enp0s8的结果,我们看到我们在输出中添加了以下行:

    inet 192.168.10.1/24 scope global enp0s8

这是我们所期望的,但是状态仍然是 down,所以我们现在发出以下命令:

$ sudo ip link set dev enp0s8 up

再次查看ip addr show enp0s8的输出,它现在将处于 UP 状态,并准备在 at 接口上发送和接收流量。它现在看起来应该类似于清单 7-1 。

我们还可以删除一个接口。当我们“关闭”一个链接时,我们不会删除 IP 地址。为此,我们将发出以下命令:

$ sudo ip addr del 192.168.10.1/24 dev enp0s8

如果您现在显示接口,则链路仍在运行,IPv4 地址已消失。您应该注意到,只要链路正常,IPv6 地址就可用。要禁用 IPv6 接口,您需要断开链接。

Note

使用 ip 命令对接口所做的更改不会在重新启动后持续存在;当您重新启动时,您将丢失所有更改。我们将很快解释如何将您的更改永久应用到您的主机。

管理界面

正如我们已经说过的,使用ip命令有利于动态添加和管理接口,但是我们也需要能够永久地配置接口。在 Linux 上有几种方法可以做到这一点。您可以选择使用图形用户界面(GUI)或直接从命令行运行。如果您愿意,也可以直接写入网络文件。

CentOS 和 Ubuntu 可以不同地管理网络。CentOS 现在使用 NetworkManager 来管理其网络。Ubuntu 可以使用 NetworkManager,但默认情况下不会安装。NetworkManager 是一个管理网络连接和服务的守护进程。它作为 Unity 桌面的一部分安装在 Ubuntu 上,你也可以单独安装。NetworkManager 将与旧的样式(LSB)文件集成,通过 LSB 网络服务管理网络接口。

NetworkManager 通过 Dnsmasq 程序(本地 DNS 服务器)集成和配置域名系统(DNS)解析,并处理 VPN 和无线连接的配置和设置。

我们在第六章中讨论了 systemd 和多用户目标,这就是网络管理器。NetworkManager 具有插件,使其能够管理您的网络配置文件。它们是ifcfg-rhifupdown,分别处理 Red Hat 风格和 Debian 风格的脚本。您可以通过设置网络连接配置文件中的NM_CONTROLLED=’no’(稍后解释),在 Red Hat 风格的主机中禁用通过 NetworkManager 对接口的管理。在 Debian 风格的主机中,NetworkManager 被禁用来管理/etc/network/interfaces中列出的接口,除非您在/etc/NetworkManager/NetworkManager.conf中明确地将以下内容更改为:

[ifupdown]
managed=true

有些人选择不运行 NetworkManager,因为他们觉得它太麻烦,他们更喜欢自己管理网络。NetworkManager 旨在使网络“正常工作”,您的里程数可能会有所不同,但自从它问世以来,已经做了大量的工作,它肯定比它问世时好得多。

NetworkManager 还提供了一些 CLI(命令行界面)程序来帮助管理您的配置。

  • nmcli 控制和报告网络管理器的状态
  • 用于管理网络管理器的基于文本的用户界面

在本章的后面,我们将向您展示更多关于nmclinmtui的内容。有关网络管理器的更多信息,您可以查看以下内容:

通过图形用户界面管理网络

如果您使用桌面来管理您的服务器,您可以使用 GUI 来管理您的网络配置。使用 CentOS,我们可以通过应用程序➤系统工具➤设置来访问网络设置。

在图 7-4 中,我们可以看到我们系统上配置的网络。在左侧面板中,您可以选择连接到系统的设备,这里我们的网络接口称为“有线”右下角的齿轮齿允许您访问配置。您可以使用开关按钮打开或关闭界面。如果您希望添加另一种类型的接口,如成组或绑定或 VPN 接口,您应该选择左侧面板底部的+。

A185439_2_En_7_Fig4_HTML.jpg

图 7-4。

CentOS network settings

如果我们要添加一个新接口,并在那里进行配置,我们将看到接口按其设备名称列在左侧面板中,如enp0s8。如果您选择配置其中一个接口,您可以看到选项与我们在安装服务器时看到的类似。

我们以前见过这种情况;在图 7-5 中,我们添加了192.168.10.1地址。同样,我们可以选择手动配置接口,或者通过下拉选项允许通过 DHCP 进行配置。

A185439_2_En_7_Fig5_HTML.jpg

图 7-5。

Adding an IP address in CentOS network settings

如果你运行 Ubuntu 桌面来管理你的服务器,我们可以做同样的事情。在图 7-6 中,我们使用 Unity Desktop 的搜索工具来查找网络设置。

A185439_2_En_7_Fig6_HTML.jpg

图 7-6。

Network settings in Ubuntu

在图 7-6 中选择网络时,我们会看到一个与 CentOS 相似的用户界面来配置我们的网络。在图 7-7 中,我们可以看到您将与图 7-5 中相同的信息插入该界面。

A185439_2_En_7_Fig7_HTML.jpg

图 7-7。

Configuring a network interface in Ubuntu

在图 7-7 中,我们再次可以选择使用 DHCP 或手动配置接口。使用用户界面配置网络接口在重新启动后仍然有效。

现在让我们看看用网络配置文件和脚本来保存网络配置的另一种方法。

使用 nmtui 配置接口

NetworkManager 附带的基于 curses 的配置工具设计用于任何类型的终端。您可以通过此终端用户界面(tui)创建和管理接口以及设置主机名。

在本练习中,我们将运行 nmtui 程序来配置我们新连接的enp0s8以太网设备。我们可以使用 dmesg 找到新接口的设备名称。

$ sudo dmesg |grep enp
[    6.903532] IPv6: ADDRCONF(NETDEV_UP): enp0s8: link is not ready

我们将很快使用这些信息。要启动它,您需要发出$ sudo nmtui

A185439_2_En_7_Fig8_HTML.jpg

图 7-8。

Edit a connection

在这里,我们选择编辑连接。

在图 7-9 中,我们添加选择添加来添加我们的新接口。

A185439_2_En_7_Fig10_HTML.jpg

图 7-10。

Type of interface, Ethernet

A185439_2_En_7_Fig9_HTML.jpg

图 7-9。

Add a connection

在这里,我们选择希望添加的接口类型。有几个选项可供选择,包括用于连接到您的 ISP 的 DSL、Wi-Fi 以及其他选项,如 VLAN 和团队界面。我们将选择以太网。

A185439_2_En_7_Fig11_HTML.jpg

图 7-11。

Add in our interface

我们提供了一个配置文件名;该名称可以是您希望的任何名称,但是将其命名为有意义的名称通常会很有帮助,因为您可以在使用姊妹命令nmcli时使用该配置文件名。在我们的例子中,我们只是将其命名为与我们添加到设备部分的设备名称相同。

我们可以设置一个 IP 地址,或者允许我们的接口从 DHCP 服务器接收一个 IP 地址,这就是我们决定要做的。但是,添加 IP 地址就像我们之前做的一样简单。我们可以向下箭头到并退出。

Consistent Device Naming

enp0s3 或者 enp0s8 这个名字是怎么回事?以前,当系统启动时,它们的网络设备会被驱动程序探测到,并被命名为 eth0。如果有多个网络设备,eth0 可能在一次引导时是 eth0,而在下一次引导时是 eth1。这导致了明显问题。我们需要可预测的网络接口。幸运的是,systemd/udev 已经找到了解决方案。

设备现在可以从 biosdevname udev helper 实用程序中获得它们的名称。这些名字意味着一些东西。

  • em[1,2,3…]–嵌入式网络接口
  • P1 P1–PCI 卡(p p
  • P2P 1 _ 1–虚拟接口(p p _

不同的硬件供应商默认启用此功能,有些则没有。您可以在 boot 命令行上将它设置为 enabled,biosdevname=0(或 1 表示禁用)。

如果 biosdevname 不可用,Systemd 提供了一个后备方案。这就是我们的设备名 enp0s8 的由来。systemd 中可能出现的其他命名模式有:

  • eno1–车载设备
  • en S1–PCI 插槽索引号
  • enp0s 8–连接器的物理位置
  • enx7f 291992–组合 MAC 地址
  • eth 0–经典

要更仔细地阅读 systemd,请看一下 www.freedesktop.org/wiki/Software/systemd/PredictableNetworkInterfaceNames/ .

使用 nmcli 配置接口

还有另一种方法可以配置我们的接口。它包含名为 nmcli 的实用程序。这是一个非常强大的工具,可以处理和配置 NetworkManager 配置。

有了这个工具,我们可以创建、显示、编辑和调出界面。让我们先花点时间来探索一下这个实用程序。

我们以前谈到过网络管理器配置文件。这些是接口的名称。要使用 nmcli 显示当前连接信息,我们将发出以下命令:

$ sudo nmcli connection show
NAME        UUID                                     TYPE                DEVICE
enp0s3      da717740-45eb-4c45-b324-7d78006bb657     802-3-ethernet      enp0s3

nmcli 有几个快捷方式,如“c”表示连接,“d”表示设备。要管理实际设备(连接、断开、删除),可以使用 device 子命令。

$ sudo nmcli d status
DEVICE  TYPE      STATE                                  CONNECTION
enp0s3  ethernet  connected                              enp0s3
lo      loopback  unmanaged                              --

nmcli 命令采用以下格式:

$ nmcli <OPTIONS> <OBJECT> <COMMAND|HELP>

因此,如果您需要帮助来记住如何在连接对象上使用 add 命令,您可以键入

$ nmcli connection add help
Usage: nmcli connection add { ARGUMENTS | help }
....

我们接下来要做的是为我们的 CentOS 主机添加一个团队界面。组接口是由两个或多个链接组成的聚合接口。它可以提供比单个接口更大的吞吐量或链路冗余。它可以支持主动/被动、LACP(链路聚合控制协议)和 VLANs(虚拟局域网)等协议。

一个团队有两个主要部分,一个团队守护进程和运行程序。teamd 守护进程管理网络接口和内核之间的 API(应用程序编程接口)。runners 是模块的另一种说法,它提供实现特定功能的代码,如主动/被动和 VLANs。

我们将添加两个设备来创建一个带有主动/被动跑步者的团队界面。它们是 enp0s8 和 enp0s9。

$ sudo nmcli device status
DEVICE  TYPE      STATE         CONNECTION
enp0s8  ethernet  disconnected  --
enp0s9  ethernet  disconnected  --

首先,我们用清单 7-2 中的命令添加组接口(team0)。

 sudo nmcli c add type team con-name team0 ifname team0 config
 '{"device": "team0", "runner": {"name":"activebackup"}}' ip4 192.168.10.10 gw4 192.168.10.254

Listing 7-2.Adding team0 Interface

在清单 7-2 中,我们使用nmcli命令添加了一个组接口,并为其分配了 IP 地址192.168.10.10和网关地址192.168.10.254。我们通过添加(add)连接(c)类型的团队来做到这一点。我们已经给这个接口命名为 team0 ( ifname)和一个配置文件名 team0 ( con-name)。

对于一个团队接口,我们需要提供一个 JSON 配置来描述我们希望使用的 runner。这既可以在 JSON 文件中,也可以在命令行中,就像我们上面提到的那样。您可以在man 5 teamd.conf中使用更多的设置示例。在清单 7-2 中,我们已经提供了所需的最低要求,即设备和流道。

这将创建一个名为/etc/sysconfig/network-scripts/ifcfg-team0的文件,我们可以看到它包含以下详细信息:

DEVICE=team0
TEAM_CONFIG="{\"device\": \"team0\", \"runner\": {\"name\":\"activebackup\"}}"
DEVICETYPE=Team
BOOTPROTO=none
IPADDR=192.168.10.10
PREFIX=32
GATEWAY=192.168.10.254
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_PEERDNS=yes
IPV6_PEERROUTES=yes
IPV6_FAILURE_FATAL=no
NAME=team0
UUID=2cef1621-4e56-4cb8-84f7-2d613fbc168f
ONBOOT=yes

我们将很快解释这些配置文件,你可以看到我们的组配置和 IP 地址的设置。我们已经创建了“主”接口,现在我们必须向它添加“从”接口。

我们使用以下命令来实现这一点:

 sudo nmcli c add type team-slave con-name team0-port1 ifname enp0s8 master team0
Connection 'team0-port1' (4e2f4307-e026-4b1a-b19e-da8f1ad64d7a) successfully added.
 sudo nmcli c add type team-slave con-name team0-port2 ifname enp0s9 master team0
Connection 'team0-port2' (ca1206ce-c0d3-45a5-b793-f10b8c680414) successfully added.

我们再次添加了一种连接名称为 team-port{1,2}的组从类型,并将它们连接到主组 0 的接口 enp0s{8,9}。让我们来看看它创建的一个文件。

/etc/sysconfig/network-scripts/ifcfg-team0-port1
NAME=team0-port1
UUID=4e2f4307-e026-4b1a-b19e-da8f1ad64d7a
DEVICE=enp0s8
ONBOOT=yes
TEAM_MASTER=team0
DEVICETYPE=TeamPort 

现在让我们看看它们是否出现在我们的连接列表中。

 sudo nmcli c show
NAME             UUID                                    TYPE              DEVICE
team0-port1      4e2f4307-e026-4b1a-b19e-da8f1ad64d7a    802-3-ethernet    enp0s8
team0            2cef1621-4e56-4cb8-84f7-2d613fbc168f    team              team0
team0-port2      ca1206ce-c0d3-45a5-b793-f10b8c680414    802-3-ethernet    enp0s9

设备列表现在如下所示:

$  sudo nmcli d status
[sudo] password for jsmith:
DEVICE  TYPE      STATE      CONNECTION
enp0s8  ethernet  connected  team0-port1
enp0s9  ethernet  connected  team0-port2
team0   team      connected  team0
lo      loopback  unmanaged  --

我们将快速查看是否能从接口获得 ping 响应。

 ping 192.168.10.10
PING 192.168.10.10 (192.168.10.10) 56(84) bytes of data.
64 bytes from 192.168.10.10: icmp_seq=1 ttl=64 time=0.047 ms

所以现在我们有了一个团队界面。你也可以用团队工具来管理团队界面,比如teamctlteamnl。让我们继续查看配置文件的更多信息。

使用网络脚本配置网络

CentOS 和 Ubuntu 都将其网络配置文件存储在/etc目录中。CentOS 将其文件存储在/etc/sysconfig/network-scripts下的网络相关目录集合中,Ubuntu 将其文件存储在/etc/networks目录下。

CentOS 的网络配置文件

如前所述,CentOS 使用 NetworkManager 来管理插入或连接的网络接口。与网络相关的 CentOS 文件可以在/etc/sysconfig目录下找到。找到这些信息的主要地方有

  • /etc/sysconfig/network文件
  • /etc/sysconfig/network-scripts目录

/etc/sysconfig/network文件包含全局设置,可以包含像HOSTNAME和默认GATEWAY这样的东西。/etc/sysconfig/network-scripts目录包含所有网络接口的启动和关闭脚本。这些文件是/etc/sysconfig/网络脚本目录中文件的一般副本,由系统配置网络工具为您创建。

查看一下/etc/sysconfig/network-scripts目录的内容,您会看到清单 7-3 中所示的文件。

 sudo ls /etc/sysconfig/network-scripts/
ifcfg-enp0s3 ifdown-ipv6  ifdown-ppp ifdown-tunnel
ifup-ipv6 ifup-routes  ifup-wireless network-functions-ipv6
ifcfg-lo ifdown-eth ifdown-routes ifup ifup-eth ifup-post
init.ipv6-global ifdown ifdown-post ifup-aliases
ifup-ppp ifup-tunnel  network-functions
Listing 7-3.Files Found in /etc/sysconfig/network-scripts

在清单 7-3 中,您可以看到一个精简的脚本列表,这些脚本用于配置您的接口并使它们启动或关闭。存在各种文件。

ifcfg-enp0s3这样的文件是以太网接口的配置文件或连接配置文件。命名约定为if<action>-device的文件,例如ifdown-ppp,是用于控制状态的脚本(即,使界面打开或关闭)。

我们之前讨论过网络管理器。如果配置了 NetworkManager,它将读取和写入这些文件。与ifcfg-enp0s3一样,配置描述文件由网络管理器读入,并将设备名称传递给ifup脚本。如果设备已经连接并由网络管理器管理,ifup将使用nmcli来管理接口。如果我们有NM_CONTROLLED=”no”,那么ifup将使用ip命令来管理界面。在此之前,它会检查该设备是否已经由网络管理器管理,如果是,它不会尝试管理该设备。要启动enp0s3设备,您需要发出以下命令:

 ifup enp0s3

network-functions这样的文件是包含函数和变量的脚本。其他脚本可以从network-functions获取(或包含)函数和变量,并在它们的脚本中使用它们。

我们来看看 enp0s3 的配置文件,名字叫ifcfg-enp0s3。您可以在清单 7-4 中看到这个文件的内容。

 sudo less /etc/sysconfig/network-scripts/ifcfg-enp0s3
TYPE="Ethernet"
DEVICE="enp0s3"
NAME="enp0s3"
UUID="fa56e72e-ea22-43ca-9a90-e19d64c0c431"
ONBOOT="yes"
BOOTPROTO="dhcp"
DEFROUTE="yes"
PEERDNS="yes"
PEERROUTES="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_PEERDNS="yes"
IPV6_PEERROUTES="yes"
IPV6_FAILURE_FATAL="no"
Listing 7-4.The ifcfg-enp0s3 File

在清单 7-4 中,你可以看到许多选项=参数形式的配置选项。要配置 CentOS 界面,我们需要设置选项,如我们正在管理的设备的名称:DEVICE=”enp0s3”。我们对名称使用相同的值。

引导协议BOOTPROTO=”dhcp”被设置为从 DHCP 获取其地址。设备的 UUID(通用唯一标识符)是“fa 56 e 72 e-ea22-43ca-9a 90-e 19d 64 c 0 c 431”,这是分配给此设备的唯一标识符,由网络管理器用来映射设备。您可以通过指定ONBOOT=”yes”来声明接口是否会在启动时初始化。接口的类型由TYPE=”Ethernet”声明。如果您不想用 IPv6 地址初始化我们的接口,您可以将 IPV6INIT 设置为“no”;这里是默认的IPV6INIT=”yes”。当PEERDNS=”yes”被声明时,意味着/etc/resolv.conf文件带有 DHCP 服务器提供的名称服务器。如果我们将它设置为 no,那么当这个接口进入 up 状态时,/etc/resolv.conf将保持不变。我们将在第十章详细解释这是如何工作的。

表 7-2 列出了您可以在 CentOS 界面文件中使用的选项。

表 7-2。

Some of the Common Network Configuration File Options, CentOS

| [计]选项 | 描述 | | --- | --- | | `DEVICE` | 您正在创建的设备的名称。这将出现在接口列表中。 | | `BOOTPROTO` | 设备启动时使用的协议。这里的选择有`static`、`dhcp`和`none`。 | | `ONBOOT` | 主机启动时设备是否启动。 | | `NETWORK` | 这个设备的网络地址。 | | `NETMASK` | 该设备的网络掩码。 | | `IPADDR` | 这个设备的 IP 地址。 | | `IPV4_FAILURE_` `FATAL` | 如果设置为是,如果我们没有从 DHCP 获得 IP 地址,`ifup-eth`脚本将立即结束 | | `MASTER` | 该设备所属的设备是`SLAVE`。 | | `SLAVE` | 设备是否由`MASTER`指令中指定的主机控制。 | | `NM_CONTROLLED` | 如果设置为否,网络管理器将忽略该设备。CentOS 上的默认值是 yes。 | | `DNS{1,2}` | DNS 主机的 IP 地址(多个地址用逗号分隔)。如果`PEERDNS`设置为是,这将被添加到`/etc/resolv.conf`中。 | | `PEERDNS` | 确定是否将 DNS 中指定的`DNS`主机添加到`/etc/resolv.conf`的设置。如果设置为`yes`,它们被添加。如果`no`,则不添加。 | | `VLAN` | 将此设备设置为 VLAN 接口(“是”、“否”) | | `IPV6INIT` | 启用或禁用此设备的 IPv6 配置。 |

表 7-2 中的选项只是可以在您的网络连接配置文件中设置的一部分;要获得选项的完整列表,您可以阅读/usr/share/doc/initscripts-<version>/sysconfig.txt,其中<version>是 initscripts 包的版本号。

创建结合界面

我们现在将带您了解如何通过在连接配置文件中设置一些选项来手动配置绑定的以太网设备。绑定的以太网设备也可以称为中继设备。绑定允许您使用两个或更多以太网端口作为一个接口,为您提供扩展的带宽和一些冗余。这样,对于一个虚拟接口,您可以将 1 GiB 链路转换为 2 GiB 链路。

那么我们该如何完成这一壮举呢?我们的主机上连接了两个器件:enp0s8 和 enp0s9。我们将这两个器件绑定在一起,使它们显示为一个接口,即 bond0。首先,我们必须将每个器件配置为从机,在清单 7-5 中,您可以看到我们是如何配置 enp0s8 的。enp0s9 器件将是该器件的镜像。

 less  /etc/sysconfig/network-scripts/ifcfg-enp0s8
DEVICE="enp0s8"
NAME="bond0-slave"
TYPE="Ethernet"
ONBOOT="yes"
BOOTPROTO="none"

SLAVE="yes"

MASTER="bond0"

Listing 7-5.The enp0s8 Slave Device Configuration

这是一个非常简单的配置。我们已经指定了我们希望控制的设备 enp0s8,以及当我们通过指定ONBOOT=”yes”启动主机时是否希望它初始化。该绑定设备的 IP 地址将自身连接到 bond0 设备,而不是 enp0s8 或 enp0s9。因此,我们在这里不指定引导协议,而是使用选项BOOTPROTO=”none”。当我们打开绑定接口时,NAME=”bond0-slave”将变得清晰,但是它提供了一个易于阅读的名称,NetworkManager 将显示这个名称(记住,除非我们明确地拥有NM_CONTROLLED=”no”,否则 NetworkManager 将管理我们的连接)。

接下来是将该设备添加到绑定配置的两个选项。第一个是SLAVE=”yes”,声明该设备是从设备。接下来,我们通过指定MASTER=”bond0”来声明它属于哪个主机。我们所指的bond0是我们即将创造的同名设备。

如我们所说,接口 enp0s9(在/etc/sysconfig/network-scripts/ifcfg-enp0s9文件中配置)将镜像复制并粘贴来自 enp0s8 的细节。然后我们需要把DEVICE=enp0s8改成DEVICE=enp0s9。其余的可以保持不变。

接下来,我们将创建焊接 0 器件文件。在 CentOS 上,配置细节将保存在一个名为/etc/sysconfig/network-scripts/ifcfg-bond0的文件中。在清单 7-6 中,你可以看到我们需要什么来创建一个绑定接口。

[jsmith@au-mel-centos-1∼]$ vi /etc/sysconfig/network-scripts/ifcfg-bond0
DEVICE="bond0"
NAME="bond0"
TYPE="bond"
BONDING_MASTER="yes"
BONDING_OPTS="mode=1 delayup=0 delaydown=0 miimon=100"
BOOTPROTO="none"
ONBOOT="yes"
NETWORK="192.168.0.0"
NETMASK="255.255.255.0"
IPADDR="192.168.0.1" 

Listing 7-6.Configuration for a Bonded Interface

如您所见,它非常类似于标准的以太网设备文件。您需要指定设备名称,在我们的示例中为 bond0,并为其提供适当的网络信息,如 IP 地址、网络和网络掩码信息。同样,我们希望这个设备在启动时被初始化。

清单 7-6 中列出了一些特定于绑定的选项,比如 BONDING_MASTER 和 BONDING_OPTS。BONDING_MASTER 相当简单,仅仅意味着这是一个绑定主接口。BONDING_OPTS 允许您为您的接口设置每个接口的绑定选项。让我们看看我们在上面设置了什么。

模式 1 将接口绑定类型设置为主动备份,如果您愿意,可以使用“mode=active-backup”代替“mode=1”。对于主动备份,当主动接口出现故障时,另一个接口会接管。这主要是为了网卡冗余,而不是吞吐量。miimon 是检查接口是否处于活动状态的频率(毫秒)。在高可用性配置中,当 miimon 发现一个接口关闭时,它将激活其余的接口。delayupdelaydown是以毫秒为单位给出时间周期的设置,在这个时间周期内,我们应该等待对从状态的变化做出反应。我们已经将这些设置为零,随着时间的推移,我们会根据需要进行调整。

根据您的网络以及在给定网络设备的情况下可以实现的绑定类型,您可以在此处添加其他选项,以便为绑定的设备提供容错、冗余和循环功能。有关更多信息,您可以查看 iputils 包提供的文档,该包管理 CentOS 主机上的绑定,特别是/usr/share/doc/iputils-<version>/README.bonding

当您的接口出现时,您的脚本(NetworkManager 或 ifup-eth 脚本)会将绑定模块插入内核。绑定模块允许内核知道如何处理绑定接口,它也可以被称为内核的驱动程序。管理绑定接口的低级工具,如 iputils 包中的ifenslave,需要在绑定接口之前加载绑定模块。如果您想检查您的绑定模块是否已经加载到内核中,您可以发出以下命令,如果是,您将会看到如下内容:

 lsmod |grep bonding
bonding   136705  0

如果不是,也不用担心。我们将在调出界面后再次检查。如果您愿意,您可以使用modprobe命令插入焊接模块。

sudo /usr/sbin/modprobe bonding

Tip

modprobe命令是将模块(而不是 insmod 命令)插入内核的更聪明的方法,因为它处理依赖关系。

我们现在将调出 bond0 器件或主接口。遵循接口的启动顺序很重要;例如,当您首先启动主接口时,您的从接口不会自动启动,但是当您启动从接口时,您的主接口会自动启动。我们正在设置一个静态 IP 地址,即使从接口没有启动,我们的主接口也会启动 IP 连接并响应“ping”。在 DHCP 中,主设备在从设备启动之前不会获得 IP。另外,请记住,NetworkManager 将管理这些接口,因此我们将开始使用一些nmcli命令来显示我们的连接,但是您也可以使用我们前面看到的ip命令。

当我们修改界面脚本时,比如ifcfg-enp0s8,我们需要告诉网络管理器重新读取它。我们可以指定要重读的接口文件,但在这种情况下,我们将通过发出

 sudo nmcli connection reload

Tip

这可以缩短为nmcli c r,因为许多命令可以只取子命令的第一个字母。

现在让我们检查一下接口的当前状态。我们发布了以下内容:

 sudo nmcli device status
DEVICE  TYPE      STATE         CONNECTION
enp0s8  ethernet  disconnected  --
enp0s9  ethernet  disconnected  --

这表明我们的设备当前处于断开状态。我们现在将通过启动从属接口来启动主(bond0)接口。我们也可以通过nmcli命令来完成,如下所示:

 sudo nmcli device connect enp0s8
Device 'enp0s8' successfully activated with '00cb8299-feb9-55b6-a378-3fdc720e0bc6'.

 sudo nmcli device status
DEVICE  TYPE      STATE         CONNECTION
bond0   bond      connected     bond0
enp0s3  ethernet  connected     enp0s3
enp0s8  ethernet  connected     bond0-slave
enp0s9  ethernet  disconnected  --
lo      loopback  unmanaged     --

 sudo nmcli device connect enp0s9

 sudo nmcli device status
DEVICE  TYPE      STATE      CONNECTION
bond0   bond      connected  bond0
enp0s3  ethernet  connected  enp0s3
enp0s8  ethernet  connected  bond0-slave
enp0s9  ethernet  connected  bond0-slave
lo      loopback  unmanaged  --

我们的第一个命令是连接我们的enp0s8设备。当连接可以建立时,我们的bond0设备也可以,但是我们的enp0s9设备仍然是断开的。然后我们拿出了我们的enp0s9设备。

很快,我们可以通过再次发出以下命令来验证我们的绑定驱动程序已经加载到内核中:

 lsmod |grep bonding
bonding   136705  0

如果我们想关闭 bond 接口,我们可以发出以下命令:

 sudo nmcli connection down  bond0
Connection 'bond0' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/6)

 sudo nmcli device status
DEVICE  TYPE      STATE         CONNECTION
enp0s3  ethernet  connected     enp0s3
enp0s8  ethernet  disconnected  --
enp0s9  ethernet  disconnected  --

您可以看到,关闭 bond0 接口也断开了两个从机的连接。

我们还可以通过发出/sbin/ip addr show命令来查看您新绑定的器件,这将产生以下输出:

3: enp0s8: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP>
mtu 1500 qdisc pfifo_fast master bond0 state UP qlen 1000
    link/ether 08:00:27:71:c3:d8 brd ff:ff:ff:ff:ff:ff
4: enp0s9: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP>
mtu 1500 qdisc pfifo_fast master bond0 state UP qlen 1000
    link/ether 08:00:27:71:c3:d8 brd ff:ff:ff:ff:ff:ff
8: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP>
mtu 1500 qdisc noqueue state UP
    link/ether 08:00:27:71:c3:d8 brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.1/24 brd 192.168.0.255 scope global bond0
       valid_lft forever preferred_lft forever
    inet6 fe80::a00:27ff:fe71:c3d8/64 scope link
       valid_lft forever preferred_lft forever

如果看 enp0s8 和 enp0s9 的接口描述,可以看到它们都被设置为SLAVE,可以看到 bond0 被设置为MASTER。两个从站都没有相关联的 IP 地址。bond0 接口具有与之关联的 IP 地址。

向接口添加多个 IP 地址

我们可以向一个接口添加多个地址。它们不必在同一个网络中,可以用于在您的主机上路由来自不同网络的流量。为此,我们可以编辑我们的网络配置文件。让我们再次看看我们的 CentOS 主机及其 enp0s3 配置文件。

我们可以使用nmcli命令来做到这一点。为此,我们必须修改现有的接口。首先,我们看到以下可用接口:

 sudo nmcli c s
enp0s3       da717740-45eb-4c45-b324-7d78006bb657  802-3-ethernet  enp0s3

记下设备 uuid(da 717740-45e b-4c 45-b324-7d 78006 bb 657),我们可以使用它来引用我们的接口。我们将使用连接对象来修改我们的接口并添加多个 IP 地址,如下所示:

sudo nmcli con mod da717740-45eb-4c45-b324-7d78006bb657 ipv4.addresses '192.168.14.10/24, 172.10.2.1/16, 10.2.2.2/8'

如果我们现在查看/etc/sysconfig/network-script/ifcfg-enp0s3文件,我们可以看到我们添加了 IP 地址。

TYPE=Ethernet
BOOTPROTO=dhcp
DEFROUTE=yes
<snip>
NAME=enp0s3
UUID=da717740-45eb-4c45-b324-7d78006bb657
DEVICE=enp0s3
ONBOOT=yes
<snip>
IPV6_PEERROUTES=yes
IPADDR=192.168.14.10
PREFIX=24
IPADDR1=172.10.2.1
PREFIX1=16
IPADDR2=10.2.2.2
PREFIX2=8

在这里,您可以看到我们添加了两个 IPv4 地址:IPADDR、IPADDR1 和 IPADDR2。我们可以使用前缀、前缀 1 和前缀 2 来提供网络掩码。

刷新接口以显示新地址。我们也使用 nmcli 这样做。

 sudo nmcli con up enp0s3
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/2685) 

我们现在可以看到如下新地址:

 ip addr show enp0s3 |grep inet
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
    inet 192.168.14.10/24 brd 192.168.14.255 scope global enp0s3
    inet 172.10.2.1/16 brd 172.10.255.255 scope global enp0s3
    inet 10.2.2.2/8 brd 10.255.255.255 scope global enp0s3
    inet6 fe80::a00:27ff:feb2:9245/64 scope link

添加别名的另一种方法是在主机启动时使用脚本来分配 IP 地址。我们可以将类似这样的内容添加到我们的rc.local文件中:

/etc/rc.d/rc.local
/sbin/ip addr add 192.168.0.24/24 brd 192.168.0.255 dev enp0s3
/sbin/ip addr add 192.168.0.25/24 brd 192.168.0.255 dev enp0s3
/sbin/ip addr add 192.168.0.26/24 brd 192.168.0.255 dev enp0s3

在上文中,当我们的主机重新启动时,我们将 IPv4 地址192.168.0.24-26添加到我们的enp0s3地址中。/etc/rc.d/rc.local文件需要设置执行位(chmod +x rc.local),因为我们运行的是 systemd 而不是 SysVInit,所以我们需要确保在重启系统时 systemd 会执行它。如果你还记得第六章的话,我们通过执行下面的代码来实现:

 sudo systemctl enable rc-local.service

这就是我们如何在 CentOS 下管理不同类型的接口。现在让我们看看在 Ubuntu 下我们是如何做到这一点的。

Ubuntu 的网络配置文件

Ubuntu 在/etc/network中有一个类似的网络文件目录。它包含 Ubuntu 用来设置网络的网络文件和脚本。如前所述,Ubuntu 将接口信息存储在文件/ etc/network/interfaces中。它包含任何已配置接口的所有接口信息。

使用 DHCP 的接口可能很简单,如下所示:

# The primary network interface
auto enp0s3
iface enp0s3 inet dhcp

首先我们声明 enp0s3 是用auto enp0s3自动启动的。接下来,我们声明 enp0s3 接口将使用 IPv4 地址,该地址将从 DHCP 服务器iface enp0s3 inet dhcp获得。如果您要配置其它以太网卡,enp0s8 或 enp0s9,您也可以使用/etc/network/interfaces文件。您可以将配置文件放在/etc/network/interfaces.d目录中,然后使用以下命令从/etc/network/interface文件中“获取”它:

source-directory interfaces.d

这将自动引入interfaces.d目录中的任何配置文件。您可以在界面文件中使用的其他参数见表 7-3 。

表 7-3。

Ubuntu Parameters in /etc/network/interfaces

| 参数 | 描述 | | --- | --- | | `auto` | 在启动时打开界面 | | `inet` | 指定 IPv4 寻址 | | `inet6` | 指定 IPv6 寻址 | | `ppp` | 指定设备是 ppp 连接 | | `address` | 指定了 ip 地址 | | `netmask` | 指定了网络掩码 | | `gateway` | 为该接口指定了默认网关 | | `dns-nameserver` | 指定该接口的名称服务器 | | `post-up` | 接口启动后要运行的指定操作 | | `pre-down` | 接口关闭前要运行的指定操作 | | 源目录 | 包括指定目录中的文件 |

在表 7-3 中,你可以看到在 Ubuntu 上设置网络接口时可用的大多数参数。我们将使用其中一些在/etc/network/interfaces文件中为 enp0s3 设置一个静态网络接口,如下所示:

auto enp0s3
iface enp0s3 inet static
address 192.168.0.10
netmask 255.255.255.0
gateway 192.168.0.254
dns-nameservers 192.168.0.1

这里我们已经设置了 enp0s3 接口。我们已经将它设置为在我们的主机启动时自动启动,auto enp0s3,并告诉我们的操作系统将为它分配一个静态 IP 地址,iface enp0s3 inet static。我们为 enp0s3 接口分配了地址 192.168.0.10 和默认网关(默认路由)192.168.0.254。我们还将 DNS 服务器指定为 192.168.0.1,这是我们内部网络的主要名称服务器。

我们现在将向您展示如何在下面的示例中使用它们。使用接口文件,我们将在 Ubuntu 主机上创建一个绑定的以太网设备。

我们需要做的第一件事是安装一个额外的软件包,如果它还没有安装的话。

sudo aptitude install ifenslave

在这种情况下,我们将启用模式“1”绑定,我们已经知道,这也可以称为主动备份。这可以在从属接口之间实现一些简单的循环负载平衡,并支持从属设备的连接和分离。

在 Ubuntu 中,和在 CentOS 中一样,处理接口的脚本也可以在/etc/network目录中找到,比如 if-up.d,处理将绑定模块插入内核。如果你使用的是旧版本的 Ubuntu,你可以在/etc/modules文件中添加“bonding ”,它会在启动时加载上面列出的模块。

我们为 Ubuntu 主机添加了两个新的网卡,它们显示为设备 enp0s3 和 enp0s8。我们需要配置这些设备进行绑定。为此,我们编辑 Ubuntu /etc/network/interfaces文件,并添加从接口以及主接口 bond0 的配置。让我们从展示一个从机的配置开始。

# slave interface
auto enp0s8
iface enp0s8 inet manual
bond-master bond0

这里,我们将第一个从机接口设置为 enp0s8。iface enp0s8 inet manual表示我们不想给这个接口分配 IP 地址。显然,我们将 bond-master 设置为主接口的名称 bond0。enp0s9 接口再次反映了 enp0s8 的配置。

转到焊接设备,我们可以按如下方式进行设置:

# The primary network interface
auto bond0
iface bond0 inet static
address 192.168.0.10
netmask 255.255.255.0
bond-mode active-backup
bond-miimon 100
bond_downdelay 25
bond_updelay 25
bond-primary enp0s8
bond-slaves enp0s8 enp0s9

在第一行“自动焊接 0”中,我们已经声明焊接 0 器件应该在引导时自动加载。接下来,在iface bond0 inet static中,我们已经声明接口 bond0 是一个 IPv4 静态分配的接口,这意味着我们不会使用 DHCP 或其他协议为其分配地址。然后,我们分别使用关键字address, netmask, gatewaydns nameservers分配 IP 地址、网络掩码、网关和 DNS 服务器。然后,我们将bond-mode设置为active-backup(模式 1),将bond-miimon设置为100,并指定该接口的从机。我们还配置了一个 25 毫秒的等待时间,然后对我们的从机的状态进行处理。这些都有相同的含义,因为他们在 CentOS 债券。

现在你可以选择两种方法来启动这个界面。首先是使用systemctl命令重启 networking.service,如下所示:

 sudo systemctl restart networking.service

另一种是使用ifup命令。不过,这应该按照特定的顺序来完成。从接口将自动启动主接口,但主接口不会启动从接口。

 sudo ifup enp0s8 && sudo ifup enp0s9

这里我们说的是调出 enp0s8 接口,如果退出代码为 0 (' &&'),则调出 enp0s9 接口。我们也可以重启我们的主机。我们现在可以通过查看特殊的/proc/net/bonding/bond0文件来验证我们的绑定接口是否正常工作。

 sudo cat /proc/net/bonding/bond0

Bonding Mode: fault-tolerance (active-backup)
Primary Slave: None
Currently Active Slave: enp0s8
MII Status: up
MII Polling Interval (ms): 100

这是针对该文件的 cat 命令输出的一部分。我们可以看到绑定模式设置为“容错(主动备份)”,主动从机为 enp0s8,MII 状态为“启动”;这告诉我们债券运作正常。在完整的输出中,您也可以看到列出的从机。

您也可以通过如下的ip命令进行检查:

 sudo ip link show enp0s8
3: enp0s8: <BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP>
mtu 1500 qdisc pfifo_fast master bond0 state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:5c:94:ef brd ff:ff:ff:ff:ff:ff

 sudo ip addr show bond0
9: bond0: <BROADCAST,MULTICAST,MASTER,UP,LOWER_UP>
mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:5c:94:ef brd ff:ff:ff:ff:ff:ff
    inet 192.168.0.10/24 brd 192.168.0.255 scope global bond0
       valid_lft forever preferred_lft forever

现在可以看到 IP 地址 192.168.0.10 被附加到 bond0,enp0s8 和 enp0s9 都是 bond0 的从机:<BROADCAST,MULTICAST,SLAVE,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master bond0。您还会注意到,所有三台设备都有相同的 MAC 地址08:00:27:5c:94:ef,这意味着它们都可以响应地址解析协议(ARP)对该 MAC 地址的请求。我们将在“TCP/IP 101”一节中进一步解释 ARP

Ethtool

ethtool命令用于进一步研究和操作您的界面。例如,根据您的驱动程序,您可以更改接口的链接速度或双工设置。

您可以通过发出以下命令来查看您的设备正在使用的设置:

 sudo ethtool enp0s8
Settings for enp0s8:
        Supported ports: [ TP ]
        Supported link modes:   10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Supported pause frame use: No
        Supports auto-negotiation: Yes
        Advertised link modes:  10baseT/Half 10baseT/Full
                                100baseT/Half 100baseT/Full
                                1000baseT/Full
        Advertised pause frame use: No
        Advertised auto-negotiation: Yes
        Speed: 1000Mb/s
        Duplex: Full
        Port: Twisted Pair
        PHYAD: 0
        Transceiver: internal
        Auto-negotiation: on
        MDI-X: off (auto)
Cannot get wake-on-lan settings: Operation not permitted
        Current message level: 0x00000007 (7)
                               drv probe link
        Link detected: yes

这显示了 enp0s8 设备的当前设置。如果要更改双工模式和速度,可以发出如下命令:

 sudo ethtool -s enp0s8 speed 1000 duplex half

这里我们将以太网卡的speed更改为1000Mb/s,将duplex设置为half双工。您也可以使用其他设置。为了更好地了解可用设置,请阅读ethtoolman页面。

TCP/IP 101

是时候稍微深入研究一下 TCP/IP(传输控制协议/互联网协议)了。您可能对 IP 地址很熟悉,但是它与 TCP/IP 的其他部分有什么关系呢?IP 地址用于查找网络上的其他主机,并供其他主机查找您。但是它是怎么做到的呢?

让我们看看这个简化的例子,当你用你的网络浏览器查找一个页面时。你在地址栏输入地址,然后按回车键。浏览器应用程序获取这些数据,并确定它需要通过打开会话向 web 服务器发送页面请求。为此,它将需要发送的信息分成离散的数据包。然后,它将 web 服务器的地址附加到数据包上,并开始试图确定将数据包发送到哪里。当它获得此信息时,它将启动与 web 服务器的连接,然后在物理线路上传输数据包中的数据,该信息将找到到达 web 服务器的路径。

您可能有兴趣知道,当您启动 TCP/IP 连接(也称为套接字)时,一个三阶段的过程会使该连接进入“已建立”状态,这意味着两台主机都知道彼此的套接字,同意如何发送数据,并准备好发送该数据。第一阶段是主机通过向它想要开始通信的主机发送一个称为 SYN 包的包来启动套接字。该主机用另一个数据包(称为 SYN ACK 数据包)作出响应,表示它已准备好开始通信。然后,发起主机发送一个数据包,另一个 SYN,ACK 数据包,确认该数据包并告诉远程主机它将开始发送数据。当它们完成通信后,连接被 FIN 数据包关闭。这是 TCP/IP 通信过程的基本概述。

Note

要更深入地了解实际数据包的外观,请阅读 http://www.tcpipguide.com/free/t_TCPMessageSegmentFormat- 3.htm中关于 TCP 数据段的讨论。你也可以去下面找一个方便的袖珍指南: http://www.sans.org/resources/tcpip.pdf?ref=3871 , and of course : https://en.wikipedia.org/wiki/Transmission_Control_Protocol

TCP/IP 协议可以在所谓的 OSI 模型下描述,即使它最初是在一个不同的模型下构思的,称为 DoD 模型(国防部)。OSI 模型由七层组成。在主机之间通过网络进行数据通信的过程中,每一层都有特殊的职责。在诊断网络时,您通常会对第 1 层、第 2 层和第 3 层感兴趣。你可以在图 7-12 中看到这些层的描述。

A185439_2_En_7_Fig12_HTML.jpg

图 7-12。

The OSI layer model Note

我们将描述前三层;有关 OSI 模型和层的更多信息,请参见: http://www.webopedia.com/quick_ref/OSI_Layers.asp .

第 1 层涉及数据如何通过物理线路传输,这由图 7-12 底部出现的物理层表示。诊断网络问题时,晃动连接电脑和网络其他部分的电缆不会有什么坏处。此外,当所有其他方法都失败时,您可以尝试更换电缆。如果电缆有故障,您可能看不到主机连接的交换机/集线器和网卡上的任何指示灯。没有这些灯,您的主机将无法与其他主机通信,因此首先尝试更换电缆,然后更换交换机上它所连接的端口,最后更换网卡。

第 2 层,即数据链路层,通过线路提供实际的通信协议。在这一层遇到的问题很少。正是在这里,IP 地址通过使用 ARP(地址解析协议)与 MAC 地址相匹配。当网络上的两台主机具有相同的 IP 地址和不同的 MAC 地址时,您的主机可能会开始尝试向错误的主机发送数据。在这种情况下,可能需要刷新 ARP 表,并且 IP 地址不正确的主机需要脱机。

第 3 层是网络层。它能够发现到您的目的地的路线,并发送您的数据,同时检查错误。这是 IP 层,因此它也可以负责 IPsec 隧道。也是这一层负责响应您的 pings 和其他路由请求。

层 4 到 7 被描述为宿主层。TCP 运行在第 4-5 层,即网络层和会话层。网络层关注寻址和链路可靠性,如错误控制和流量控制。会话层控制计算机之间连接的建立和关闭。第 6 层到第 7 层负责连接用户,这是 HTTP 和浏览器将您连接到 web 服务器的地方。

常规网络故障排除

你的网络可能会出错。有时您无法连接到服务,或者网络的某些部分配置不正确。要识别和解决这些问题,您需要一套工具。我们将研究各种工具:

  • ping:在主机之间发送数据包以确认连通性的命令
  • tcpdump:显示和捕获网络流量的命令
  • mtr:网络诊断工具
  • 另一个网络诊断和测试工具 netcat

平!

人们用来检查网络的最常用工具可能是一个名为ping的命令。您可以 ping 主机或接口,看看它是否有响应。在发出ping命令的输出响应中,显示了该响应返回所用的时间。你可以 ping 地球的另一端,并在几毫秒内收到响应。那很好。如果您得到的响应时间是几秒钟或一条“主机不可达”消息,那就糟了。当然,如果你 ping 的主机在另一个大陆或者通过卫星链接连接,ping 次数会比你桌子底下的主机多。

我们刚刚使用了ping命令来测试我们的路由,如下所示:

$  ping 192.168.0.50
PING 192.168.0.50 (192.168.0.50) 56(84) bytes of data.
64 bytes from 192.168.0.50: icmp_seq=1 ttl=64 time=1.24 ms

ping命令可以接受以下参数。关于其他参数,请参考man页。

ping –c <count> –i <interval> –I <interface address> –s <packet size> destination

ping命令将无限期地向主机发送 ping 命令,除非您通常使用 Ctrl+C 停止它,或者使用–c number选项给它提供 ping 命令的次数。

您也可以将 pings 的间隔表示为–i number(以秒为单位),并选择您希望使用的接口作为源地址,通过-I IP address或接口名称指定,如:-I enp0s8。如果您有多个接口,并且希望测试其中一个,这将非常方便。如果没有定义地址,它通常会使用主机上的主接口 ping,通常是 eth0。您还可以使用-s number of bytes指定数据包大小,这对于测试带宽问题或 MTU 设置问题非常有用。

Note

正如本章前面提到的,MTU 是最大传输单位。它用于将数据包的大小限制在该网络上的网络设备能够处理的范围内。通常它被设置为 1,500 字节,但是对于巨型帧,它可以高达 9,000 字节。拥有更大的数据包意味着您可以更高效地发送更多数据,因为通过网络传输的数据包更少。

要测试您的网络,您可以做的第一件事就是使用pingping 您已经配置的接口。如果它们响应来自您自己主机的 pings,则它们已启动,您的接口正在响应 TCP 流量。您可以这样做:

 ping 192.168.0.253
PING 192.168.0.253 (192.168.0.253) 56(84) bytes of data.
64 bytes from 192.168.0.253: icmp_seq=2 ttl=128 time=1.68 ms

在这里,我们向主机的本地 IP 地址发送了一个 ping 命令,我们可以看到一系列的响应,表明已经建立了一个连接以及连接所用的时间。如果我们没有收到响应,我们就会知道我们的接口或网络有问题。

下一步是 ping 网络上的另一台主机,比如您的默认网关或 DNS 服务器(这两台主机对您的互联网通信至关重要)。

 ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_seq=1 ttl=128 time=2.97 ms

如果您可以到达那里,您就知道您的主机可以到达其他主机。然后,您可以 ping 您网络外的一台主机,比如说 www.ibm.comwww.google.com ,并测试它的响应:

 ping www.google.com
PING www.l.google.com (150.101.98.222) 56(84) bytes of data.
64 bytes from g222.internode.on.net (150.101.98.222): icmp_seq=1 ttl=59 time=20.0 ms

当您在连接到互联网上的主机时遇到问题,可能有多种原因。在某些情况下,部分互联网会因为核心路由器损坏而中断。在这种情况下,您的所有专用网络 ping 都将返回,但对互联网上主机的某些 ping 可能不会返回。如果您没有从互联网上的一台主机得到响应,请尝试另一台主机,看看问题是在本地还是在其他地方。一些远程网络会主动阻止 ICMP(互联网控制消息协议)流量。这些主机不会响应 pings,因此您应该在惊慌之前检查其他主机的响应。在这些情况下,将您的调查与其他工具结合起来也很方便,其中之一就是mtr

平均拆卸时间(Mean Time of Remove)

如果您可以 ping 通网络内部的主机,但无法 ping 通网络外部的主机,您可以使用 traceroute 或 mtr 等工具进行故障排除。两者都提供类似的服务:它们跟踪到达目的主机的路由。两者都使用“TTL 在传输中过期”消息来记录沿途的主机。这些消息是什么意思?嗯,正如我们已经解释过的,TTL 是用来杀死 TCP/IP 包的,这样它们就不会像大型强子对撞机中丢失的粒子一样在互联网上不停地闪烁。这些命令利用礼貌的路由器,当它们杀死你的包时,发送回“TTL 在传输中过期”的消息。

让我们看一个例子。如果我们对 www.ibm.com 执行 ping 操作,并将 TTL 设置为1,它将到达路径上的第一个路由器,无论 www.ibm.com 解析为什么。第一个路由器查看 TTL,发现它是1。它现在会丢弃数据包并发送传输中过期的消息。

 ping -t 1 -c 1 www.ibm.com
PING www.ibm.com.cs186.net (129.42.60.216) 56(84) bytes of data.
From 192.168.0.254 (192.168.0.254) icmp_seq=1 Time to live exceeded

--- www.ibm.com.cs186.net ping statistics  ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms

mtrtraceroute应用程序使用发回的数据包来发现路由器的 IP 地址(因为包含回复的 TCP/IP 数据包将保存此信息)。然后显示该信息并发送下一个 ping,这次 TTL 设置为2。一旦 ping 到达我们的目的地,我们应该会收到标准的回应。一些路由器被配置为不发送这些 ICMP 消息,并在跟踪输出中显示为空白。

看看 mtr 的输出。这里,我们使用 mtr 跟踪主机au-mel-centos-1www.ibm.com 之间的路由,命令如下:

 sudo /usr/sbin/mtr www.ibm.com --report

Start: Tue May 17 18:04:01 2016
HOST: au-mel-centos-1             Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 192.168.0.254              0.0%    10    2.7   2.4   1.8   3.2   0.0
  2.|-- lnx20.mel4.something.net   0.0%    10  444.7 339.7 231.5 514.1 101.8
  3.|-- xe-0-3-2.cr1.mel4.z.net    0.0%    10  456.4 332.2 211.5 475.6 103.0
  4.|-- ae0.cr1.mel8.boo.net       0.0%    10  463.3 338.3 240.2 463.3  91.1
  5.|-- ???                        100%    10    0.0   0.0   0.0   0.0   0.0
  6.|-- a104-97-227-232.deploy.st  0.0%    10  476.3 341.9 229.3 476.3  97.0

因为我们使用了--report开关,所以直到至少发送了十个包,才会显示输出。该命令的输出显示,我们的第一跳(我们经过的每个主机/路由器称为一跳)是我们的防火墙路由器。接下来是我们 ISP 的互联网连接的默认网关。当我们经过沿途的每一跳时,我们记录下关于我们路线的信息。在倒数第二跳,我们到达了 IBM gateway。如果看到一个???,那就说明路由器已经被设置为拒绝返回 ICMP 数据包,mtr 因为没有收到“TTL 在传输中过期”的消息,所以打印了???。一些网络管理员这样做是因为他们认为 ICMP 是一个安全漏洞。

更多关于 ICMP 的一些安全漏洞,请阅读: http://resources.infosecinstitute.com/icmp-attacks/

tcpdump 命令

您无法轻易查看第 1 层的通信,但您可以使用数据包嗅探软件查看第 2 层和第 3 层的通信。查看这些细节的一个应用程序是tcpdump命令行工具。tcpdump命令以及类似的命令可以在网络上查看数据包级别的流量。您可以看到进出主机的数据包。tcpdump命令在没有任何表达式的情况下运行时,将打印通过接口的每个数据包。您可以使用表达式来缩小它将显示的数据包类型的范围。更多信息见man tcpdump页。

Note

你可以尝试的另一个程序叫做 Wireshark。它有一个非常好的图形用户界面,可以让您轻松地过滤流量。它还有一个名为tshark的命令行实用程序,其操作方式与tcpdump类似。WireShark 的一个优点是,您可以从tcpdump(通过指定–w 选项)获取输出,并在 WireShark 中读取它,这使得查看信息变得更加容易。这里可以看到更多关于 Wireshark 的内容: http://www.wireshark.org/ .

为了向您展示如何建立网络连接,我们将建立一个从防火墙到邮件服务器的连接。为了说明会发生什么,我们将使用tcpdump命令,输出如下所示:

 sudo /usr/sbin/tcpdump -i enp0s3
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s3,  link-type EN10MB (Ethernet), capture size 96 bytes
19:26:35.250934 ARP, Request who-has 192.168.0.1 tell 192.168.0.254, length 46

19:26:35.475678 ARP, Reply 192.168.0.1 is-at 00:50:56:a9:54:44 (oui Unknown), length 28

19:31:17.336554 IP 192.168.0.254.33348 > 192.168.0.1.smtp: Flags [S],
seq 3194824921, win 29200, options [mss 1460,sackOK,TS val 47080429
ecr 0,nop,wscale 7], length 0

19:31:17.619210 IP 192.168.0.1.smtp > 192.168.0.254.33348: Flags [S.],
seq 2011016705, ack 3194824922, win 65535, options [mss 1460], length 0

19:31:17.619249 IP 192.168.0.254.33348 > 192.168.0.1.smtp: Flags [.],
ack 1, win 29200, length 0

19:31:17.900048 IP 192.168.0.1.smtp > 192.168.0.254.33348: Flags [P.],
seq 1:42, ack 1, win 65535, length 41

19:31:17.900081 IP 192.168.0.254.33348 > 192.168.0.1.smtp: Flags [.],
ack 42, win 29200, length 0

我们已经发出命令,告诉它转储通过 enp0s3 接口的所有流量。我们使用-i 选项来指定要检查哪个接口。在输出中,我们可以将这些行分解如下:

timestamp source.port > destination.port : flags

在前面的输出中,前两行包含告诉您命令正在做什么的信息,在本例中是listening on enp0s3。其余的都是真正的数据包。每行的第一个数字,例如19:26:35.250934,是时间戳。

ARP, Request who-has 192.168.0.1 tell 192.168.0.254

第一个字段(去掉时间戳)是 TCP/IP 模型中的协议。这是一个 ARP 请求,ARP 运行在 TCP/IP 协议栈的第 2 层(或数据链路层)。ARP 用于匹配 MAC 地址和 IP 地址。可以看到 192.168.0.254 想知道谁有 192.168.0.1。有一个 ARP 回复说 192.168.0.1 在 MAC 地址00:50:56:a9:54:44

ARP, Reply 192.168.0.1 is-at 00:50:56:a9:54:44

既然 192.168.0.254 知道了要将它的数据包发送到哪里,它就试图通过发送一个 SYN 数据包来建立一个套接字,Flags [S]。SYN 数据包携带 SYN 位组,初始序列号为seq 3194824921

IP 192.168.0.254.33348 > 192.168.0.1.smtp: Flags [S], seq 3194824921,
win 29200, options [mss 1460,sackOK,TS val 47080429 ecr 0,nop,
wscale 7], length 0

来源和目的地由192.168.0.254 > 192.168.0.1.smtp描述,其中。smtp是我们正在连接的端口。该端口映射到端口 25。这是正在建立的与 SMTP 邮件服务器的连接。

再来看下一部分:Flags [S]。源和目的地后面的[S]表示这是一个 SYN 请求,我们正在建立连接。接下来是数据包的初始序列号3194824921。序列号是随机生成的,用于对数据包进行排序和匹配。该数据包的长度为0,这意味着它是一个零字节数据包(即,它不包含任何有效载荷)。

其他标志win 29200, options [mss 1460,sackOK,TS val 47080429 ecr 0,nop,wscale 7]提供通信中的其他信息,如滑动窗口大小、最大分段大小等等。

Note

更多信息,请参见 http://www.tcpipguide.com/free/ t_TCPMaximumSegmentSizeMSSandRelationshiptoIPDatagra.htm.

下一个包是192.168.0.1的回复。

IP 192.168.0.1.smtp > 192.168.0.254.33348: Flags [S.], seq 2011016705,
ack 3194824922, win 65535, options [mss 1460], length 0

该数据包具有Flag [S.]集合和序列号2011016705。[S.]是一个 SYN ACK(准确地说是无标志),这是对 SYN 数据包的响应。序列号是另一个随机生成的数字,数据有效载荷也是零,(length 0)。附加到该序列的是 ACK 响应ack 3194824922。这是增加了 1 的原始初始序列号,表示它正在确认我们的第一个序列。

下一个数据包是由始发主机发送的另一个确认数据包:

IP 192.168.0.254.33348 > 192.168.0.1.smtp: Flags [.],
ack 1, win 29200, length 0

在这个输出中,点(.)表示没有设置标志。ack 1表示这是一个确认包,从现在开始,tcpdump将显示当前序列和初始序列之间的差异。这是在两台主机之间建立连接所需的最后一次通信,称为三次握手。

最后两个包是数据的交换。

IP 192.168.0.1.smtp > 192.168.0.254.33348: Flags [P.],
seq 1:42, ack 1, win 65535, length 41

邮件服务器正在向 192.168.0.254 上的客户端发送消息,如[P.] seq 1:42, ack 1所示。这是将[P.], 41字节的数据推入有效载荷,并确认之前的通信(ack 1)。

IP 192.168.0.254.33348 > 192.168.0.1.smtp: Flags [.],
ack 42, win 29200, length 0

最后一次通信是 192.168.0.254 确认该数据包,ack 42

现在,您已经知道了如何使用数据包嗅探程序(如tcpdump)在最基本的层面上查看两台主机之间的通信,让我们来看看另一个有用的工具netcat

Note

如果你有兴趣深入探讨tcpdump和连接建立,可以试试下面这篇文章: http://www.linuxjournal.com/article/6447

Netcat 工具

可以用来诊断网络问题的另一个非常有用的工具是ncncat命令。您可以使用这个工具来测试您的能力,不仅可以到达其他主机,还可以到达它们可以监听的端口。

当您想测试通过防火墙到端口的连接时,这个工具特别方便。让我们测试一下我们的防火墙是否允许我们从主机 192.168.0.254 连接到主机 192.168.0.1 上的端口 80。

首先,在主机 192.168.0.1 上,我们将确保我们已经停止了 web 服务器。例如,我们发出以下命令:

 sudo systemctl stop httpd

然后,我们将在 IP 地址为 192.168.0.1 的主机上使用-l或 listen 选项启动nc命令。

 sudo nc -l 80

这将我们的nc命令绑定到端口上的所有接口。我们可以通过运行另一个名为 netstat 的命令进行测试:

 sudo netstat –lpt
tcp     0      0 *:http     *:*   LISTEN  18618/nc     .

我们启动了带有三个选项的netstat命令。–l选项告诉netstat命令列出监听套接字。–p选项告诉netstat显示哪些应用程序正在使用每个连接,最后一个选项–t告诉netstat只寻找 TCP 连接。

netstat命令显示监听主机上某些端口的程序。我们可以在前面的输出中看到,18618的 PID 程序nc正在监听端口 80 上的 TCP 连接。*:http表示它正在监听端口 80 上所有可用的地址(网络接口 IP 地址)(http端口映射到端口 80)。好了,我们知道我们的nc命令正在监听并等待连接。接下来,我们测试从 IP 地址为 192.168.0.254 的主机进行连接的能力。

我们将使用nc命令连接到主机 192.168.0.1 上的端口 80,如下例所示:

 nc 192.168.0.1 80
hello host

nc程序允许我们测试两台主机之间的连接,并向远程主机发送文本。当我们在连接窗口中键入文本并按 Enter 键时,我们将看到我们键入的内容在au-mel-centos-1主机上得到回应。

 sudo nc -l 80
hello host

我们现在知道我们的主机可以连接到端口 80 上的au-mel-centos-1主机,确认我们的防火墙规则正在工作(或者如果我们试图阻塞端口 80,这种情况可能过于宽松)。

你喜欢吗?

dig是解决 DNS 问题的另一个便捷工具。如果你将这个工具与其他工具如pingnc结合使用,你将能够解决许多问题。dig命令是域名信息搜索工具(domain information groper)的缩写,用于查询 DNS 服务器。简单地说,该命令将通过查询在/etc/resolv.conf文件中找到的nameserver来解析一个完全合格的域名。

/etc/resolv.conf文件用于存储nameserver信息,以便您的主机知道查询哪个 DNS 服务器进行域名解析。一个/etc/resolv.conf文件看起来如下:

 sudo cat /etc/resolv.conf
; generated by /sbin/dhclient-script
search example.com
nameserver 192.168.0.1
nameserver  192.168.0.254

首先,您可以看到resolv.conf文件是由 DHCP 客户端生成的。一般来说,这个文件应该不需要编辑;通常它会被网络管理器覆盖。默认的search域是example.com,任何主机名搜索都会将该域附加到查询的末尾。接下来,我们有我们希望查询域名解析的nameserver(s)。这些应该是 IP 地址格式。如果第一个nameserver不可用,将使用第二个。

除非另有指示,否则dig命令将查询这些nameservers。让我们看看这个查询。

 dig www.google.com

; <<>> DiG 9.10.3-P4-Ubuntu <<>> www.google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 16714
;; flags: qr rd ra; QUERY: 1, ANSWER: 12, AUTHORITY: 4, ADDITIONAL: 5

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.google.com.                        IN      A

;; ANSWER SECTION:
www.google.com.         233     IN      A       150.101.161.181
www.google.com.         233     IN      A       150.101.161.187
www.google.com.         233     IN      A       150.101.161.146
www.google.com.         233     IN      A       150.101.161.152
www.google.com.         233     IN      A       150.101.161.153
www.google.com.         233     IN      A       150.101.161.159
www.google.com.         233     IN      A       150.101.161.160
www.google.com.         233     IN      A       150.101.161.166
www.google.com.         233     IN      A       150.101.161.167
www.google.com.         233     IN      A       150.101.161.173
www.google.com.         233     IN      A       150.101.161.174
www.google.com.         233     IN      A       150.101.161.180

;; AUTHORITY SECTION:
google.com.             10960   IN      NS      ns4.google.com.
google.com.             10960   IN      NS      ns2.google.com.
google.com.             10960   IN      NS      ns1.google.com.
google.com.             10960   IN      NS      ns3.google.com.

;; ADDITIONAL SECTION:
ns1.google.com.         9192    IN      A       216.239.32.10
ns2.google.com.         13861   IN      A       216.239.34.10
ns3.google.com.         9192    IN      A       216.239.36.10
ns4.google.com.         2915    IN      A       216.239.38.10

;; Query time: 28 msec
;; SERVER: 192.168.0.254#53(192.168.0.254)
;; WHEN: Sun Jul 17 15:43:41 AEST 2016
;; MSG SIZE  rcvd: 371

ANSWER SECTION中,你可以看到 www。谷歌。主机名将解析为 12 个可能的 IP 地址。AUTHORITY SECTION告诉我们哪个nameservers负责为 www 提供 DNS 信息。谷歌。comADDITIONAL SECTION告诉我们AUTHORITY SECTION中的域名服务器将解析到什么 IP 地址。

IN A表示互联网地址(或相对记录)。IN NS表示一个nameserver记录。在前面代码的三个部分中显示的数字,如 233、10960 和 9192,表示记录被缓存的时间(以秒为单位)。当它们达到零时,nameserver将查询权威 DNS 服务器以查看它是否已经改变。

dig输出的底部,您可以看到提供响应的SERVER192.168.0.254,以及查询花费的时间28 msec

你可以通过使用@符号使用dig来查询特定的nameserver,这里我们使用的是谷歌公共 DNS 服务器之一。您还可以使用–t type选项查询某些记录类型。例如,如果我们想测试我们的 DNS 服务器是否正常工作,我们可以使用dig来查找 Google 邮件服务器的 IP 地址。

 dig @8.8.8.8 –t MX google.com

<snip>
;; QUESTION SECTION:
;google.com.                    IN      MX

;; ANSWER SECTION:
google.com.             392     IN      MX      20 alt1.aspmx.l.google.com.
<snip>
google.com.             392     IN      MX      10 aspmx.l.google.com.

;; AUTHORITY SECTION:
google.com.             10736   IN      NS      ns4.google.com.
google.com.             10736   IN      NS      ns3.google.com.
google.com.             10736   IN      NS      ns1.google.com.
google.com.             10736   IN      NS      ns2.google.com.

;; ADDITIONAL SECTION:
aspmx.l.google.com.     55      IN      A       64.233.189.27
aspmx.l.google.com.     112     IN      AAAA    2404:6800:4008:c03::1b
<snip>
ns3.google.com.         8968    IN      A       216.239.36.10
ns4.google.com.         2691    IN      A       216.239.38.10
<snip>

我们可以使用这些信息并比较其他nameservers的反应。如果我们的nameserver提供了相同的信息,我们的 DNS 服务器工作正常。如果有差异或者我们没有返回结果,我们将不得不进一步调查我们的 DNS 设置。在第九章中,我们将更详细地讨论 DNS 和dig命令。

其他故障排除工具

我们刚刚讨论的程序涵盖了基本的网络故障排除。还有许多其他网络工具可以进一步帮助诊断问题。我们简要介绍了 netstat 命令,但是还有许多其他命令。表 7-4 列出了一些最常见的故障排除命令及其描述。

表 7-4。

Other Handy Network Diagnostic Tools

| 工具 | 描述 | | --- | --- | | `netstat` | 这个工具给出了你的主机上正在监听的内容的状态以及更多信息,包括路由和统计数据。 | | `host` | 另一个诊断 DNS 服务器的工具。我们将在第九章的中告诉你如何使用`host`。 | | `openssl s_client` | 这对于测试 SSL 连接非常有用,因为它可以尝试建立到远程主机端口的 SSL 连接。 | | `arp` | 这个程序查询和管理 ARP 缓存表,并允许您删除 ARP 条目。 |

表 7-4 中的有用工具列表绝非详尽无遗。然而,只需使用其中的一些工具,您就可以诊断许多常见问题。

添加路由和转发数据包

您将使用ip命令做的另一件常见事情是向您的主机添加路由。路由是主机访问其它网络或其它主机时应该遵循的路径。

IP Forwarding

默认情况下,Linux 主机通常关闭 IP 转发。IP 转发允许您的主机充当路由器,将网络中主机发送给它的数据包定向到目的主机。如果您的主机打开了 IP 转发,您的主机将能够将数据包从一个接口转发到另一个接口或从一个网络转发到另一个网络。这是一个内核级函数,可以通过编辑/etc/sysctl.conf文件来打开或关闭。这个文件允许您在运行时配置内核参数。您可以在sysctl.conf文件中设置许多其他参数,包括内存、交换和其他与网络相关的内核参数,这些参数可以在运行时进行调整。

内核将这类信息存储在/proc目录中。例如,IP 转发设置位于/proc/sys/net/ipv4/ip_forward文件中。这个文件系统是由内核在启动时创建的,直接对其进行的更改不会在重新启动后保持不变。如果将其设置为0,IP 转发将被禁用,您的主机将不会传递任何目的地不是它的数据包。如果设置为 1,IP 转发会将那些不是发往它的数据包重定向到它们的目的主机。

要使设置在重启后保持不变,您需要取消对/etc/sysctl.conf中下面一行的注释,保存文件,然后发出sysctl –p命令将更改加载到内核中:

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1

您还可以通过将数字 1 回显到文件/proc/sys/ net/ipv4/ip_forward中来立即打开数据包转发,如下所示:

echo 1 > /proc/sys/net/ipv4/ip_forward

此更改不会在重启后持续,但编辑/etc/sysctl会使其永久化。

您也可以为 IPv6 接口配置 IP 转发。类似地,我们需要将net.ipv6.conf.all.forwarding=1添加到sysctl.conf文件中,或者再次将 1 回显到/proc/sys/net/ipv6/conf/all/forwarding中。

有关sysctl.confsysctl使用的更多信息,请阅读手册页。

我们想向您展示一个如何使用 ip 命令添加路由的示例。首先,我们将解释我们想要实现的目标。我们将通过防火墙将流量从一个网络路由到另一个网络。我们位于主服务器主机上,其 IP 地址为 192.168.0.1,即图 7-13 中的主机 A。我们的防火墙/路由器位于网络中,接口 enp0s8(主机 b)的 IP 地址为 192.168.0.254。我们在防火墙主机上配置了接口 enp0s9,地址为 192.168.1.254/24。网络 192.168.1.0/24 将成为我们的无线网络,我们将把它划分到自己的子网中。在 IP 地址为 192.168.1.220 的网络中,有一台我们希望与之通信的主机,即主机 c。图 7-13 提供了我们的网络图。

A185439_2_En_7_Fig13_HTML.jpg

图 7-13。

Adding a route

主机 A 和主机 C 已正确配置为在网络间路由。在路由表中,它们通过默认网关发送所有发往其它网络的请求。在这种情况下,它们的默认网关是防火墙/路由器,默认网关的地址分别是 192.168.0.254 和 192.168.1.254。主机 A 不知道主机 B 的 enp0s9 接口连接了 192.168.1.0/24 网络,主机 C 也不知道主机 B 的 enp0s8 接口连接了 192.168.0.0/24 网络。

Note

我们将使用 ip 命令来浏览路由表。但是首先你需要知道有不同的路由表。默认情况下,有一个主要的,本地和默认。也可以添加自己的。您可以通过发出ip route show table local|main|default|number.来查看不同的表。当我们发出ip route show命令时,我们看到的是主表,相当于ip route show table main

让我们查看主机 A 的路由表。通过发出以下命令,我们可以列出主机 A 的路由表:

 sudo /sbin/ip route show
192.168.0.0/24 dev enp0s8 proto kernel scope link src 192.168.0.1
default via 192.168.0.254 dev enp0s8 scope link

路由表显示了该主机知道的网络。它知道自己的网络,如192.168.0.0/24 dev enp0s8 proto kernel scope link src 192.168.0.1所示。

可以看到默认路线,default via 192.168.0.254 dev enp0s8 scope link。默认路由指向网络中的主机,该主机可以处理其网络中主机的路由请求。这些设备通常连接有多个接口或 IP 地址网络,因此可以将路由请求沿路由路径传递到主机,进而到达最终目的地。

让我们将路由表与主机 B 路由表进行比较。主机 B 是我们的网络路由器,因此它应该有多个网络连接到其接口。

 sudo /sbin/ip route show
192.168.0.0/24 dev enp0s8 proto kernel scope link src 192.168.0.254
192.168.1.0/24 dev enp0s9 proto kernel scope link src 192.168.1.254
10.204.2.10 dev dsl-provider proto kernel scope link src 10.0.2.155
default dev dsl-provider scope link

首先让我们来看看主机 b 上的default路由。它表示它不知道的任何东西都将从dev dsl-provider发出。设备dsl-provider是我们到 ISP 的 PPP 链接。第一行与该路线相关。它显示网络 10.204.2.10 是通过设备dsl-provider和 10.0.2.155 的link src地址到达的。10.204.2.10 主机位于我们的 ISP 中,在我们建立 PPP 连接时由 ISP 分配给我们。

在主机 C 上,我们有这样的路由配置:

 sudo /sbin/ip route show
192.168.1.0/24 dev enp0s8 proto kernel scope link src 192.168.0.220
default via 192.168.1.254 dev enp0s8 scope link

我们知道,主机 A 和主机 C 位于不同的网络中,它们本身无法相互联系。对于我们来说,要建立一个简单的连接,比如从主机 A 到主机 C 的ping连接,TCP/IP 数据包必须通过主机 b。ping命令向命令行中指定的主机发送 ICMP 回应请求。回应请求是一种定义好的协议,它向发出请求的主机传达计算机语言“我在这里”

Note

互联网控制消息协议(ICMP)是一种 TCP/IP 协议,用于在主机之间发送消息,通常是错误消息;但是在ping的情况下,它也可以发送信息性的 echo 回复。

因此,让我们看看是否可以通过在主机 A 上发出ping命令从主机 A 到达主机 C。

 ping –c 4 192.168.1.220
PING 192.168.1.220 (192.168.1.220) 56(84) bytes of data.
64 bytes from 192.168.1.220: icmp_seq=1 ttl=64 time=1.79 ms

我们使用了ping命令,这是一种发送网络回应的方式,带有–c 4选项来限制回复给4的回应数量。这足以确认我们可以从我们的主机 a 到达我们的主机 C。这是相对容易的。让我们看一个稍微不同的场景,其中我们必须添加自己的路由。你可以在图 7-14 中看到它的示意图。

A185439_2_En_7_Fig14_HTML.jpg

图 7-14。

Adding another route

在这种情况下,我们假设主机 D 试图通过主机 E 到达主机 F。主机 E 有两个以太网设备 enp0s8 和 enp0s9。设备 enp0s8 连接到 IP 地址为 192.168.0.2 的 192.168.0.0/24 网络,enp0s9 连接到 IP 地址为 192.168.10.0/24 的 192.168.10.2 网络。主机 D 位于 192.168.0.0/24 网络中,IP 地址为 192.168.0.50。主机 D 的默认路由是 192.168.0.254。主机 F 位于 192.168.10.0/24 网络中,IP 地址为 192.168.10.100。主机 F 的默认路由是 192.168.10.254。

让我们看看它们的路由表:

Host D
192.168.0.0/24 dev enp0s8 proto kernel scope link src 192.168.0.50
default via 192.168.0.254 dev enp0s8 scope link

Host E
192.168.0.0/24 dev enp0s8 proto kernel scope link src 192.168.0.2
192.168.10.0/24 dev enp0s9 proto kernel scope link src 192.168.10.2
default via 192.168.0.254 dev enp0s8 scope link

Host F
192.168.10.0/24 dev enp0s8 proto kernel scope link src 192.168.10.100
default via 192.168.10.254 dev enp0s8 scope link

您可以看到,主机 E 包含连接了两个不同网络的两个接口。要让主机 D 看到主机 F,我们需要告诉主机 D 通过主机 e 到达主机 F。我们通过添加如下路由来实现:

 sudo /sbin/ip route add 192.168.10.0/24 via 192.168.0.2 dev enp0s8

这里,我们必须使用 ip 命令和 route 对象来添加从主机 d 到 192.168.10.0/24 网络的路由。我们为该命令提供主机的 IP 地址 192.168.0.2,它是我们网络中通往目的网络的路由器。

既然主机 D 有到达 192.168.10.0/24 网络的路径,我们应该能够 ping 通主机 F,对吗?

 ping 192.168.10.100
PING 192.168.10.100 (192.168.10.100) 56(84) bytes of data.

似乎没有返回数据包,我们没有看到该命令的输出。如果我们通过尝试从主机 f ping 主机 D 来测试这一点会怎么样呢?我们使用–c 4选项将发送到目的主机的 ping 次数限制为四次。

ping –c 4 192.168.0.50
PING 192.168.0.50 (192.168.0.50) 56(84) bytes of data.

--- 192.168.0.50 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 2999ms

当我们从主机 F ping 主机 d 时,我们的主机看不到彼此。这是为什么?这是因为主机 F 对 192.168.0.0/24 网络一无所知。该主机会向其默认网关发送它不知道的任何信息。因为主机 F 的默认网关没有配置为知道将数据包发送到 192.168.0.50 的位置,所以它会将数据包发送到默认网关。最终这些数据包会死亡。

Note

TCP/IP 协议的一部分是 TTL(生存时间),在一段时间后,数据包会被上游路由器忽略和丢弃。TTL 由一个数字表示,现在通常是 64,但也可以是 32 或 128。当数据包通过每个路由器(或跳)时,它们会将 TTL 值减 1。如果 TTL 在到达路由器时为 1,并且该路由器不是最终目的地,则路由器将丢弃该数据包,并使用 ICMP 协议发送“TTL 在传输中过期”消息。您将很快在“MTR”部分看到这是多么有用。

那么我们如何解决这个问题呢?我们给主机 F 添加了一条路由,告诉它如何通过主机 e 向主机 D 发送数据包。

 sudo /sbin/ip route add 192.168.0.0/24 via 192.168.10.2 dev enp0s8

现在,当主机 F 尝试 ping 主机 D 时,我们会得到以下结果:

 ping 192.168.0.50
PING 192.168.0.50 (192.168.0.50) 56(84) bytes of data.
64 bytes from 192.168.0.50: icmp_seq=1 ttl=64 time=1.24 ms

添加到主机 F 的路由后,我们现在可以到达主机 d。结合所有这些信息,我们可以在脚本中使用这些命令来打开接口、添加 IP 地址和添加路由。

#!/bin/bash

# bring up the interface
ip link set enp0s8 up

# add an address
ip addr add 192.168.10.1/24 dev enp0s8

# add a route
ip route add 192.168.100.1 via 192.168.10.254 dev enp0s8

# ping the host
ping 192.168.100.1 –c 4

# bring down the route, remove the address and bring down the interface.
ip route del 192.168.100.1 via 192.168.10.254 dev enp0s8
ip addr del 192.168.10.1/24 dev enp0s8
ip link set enp0s8 down

exit 0

我们使用了相同的命令来控制我们的界面,打开界面,添加地址,然后添加路由。我又以同样的命令把它降下来。这种脚本可以用来将文件从一台主机复制到另一台主机,或者建立任何类型的连接。

现在让我们继续深入探讨如何使用一些简单的实用程序来测试连接和排除网络故障。

Netfilter 和 iptables

许多人现在明白了什么是防火墙,因为它们已经开始成为最简单的主机上的强制性要求。您可能已经熟悉了运行在您桌面上的防火墙。这些简单的防火墙可以用来阻止不需要的流量进出单个主机,但这是它们唯一能做的。

Netfilter 是一个复杂的防火墙应用程序,可以位于网络的外围或单台主机上。它不仅可以阻止不需要的流量并在网络中路由数据包,还可以控制流量。“塑造流量?”你可能想知道,“那是什么?”

数据包整形是改变进出网络的数据包的能力。您可以使用它来增加或减少某些连接的带宽,优化其他数据包的发送,或保证特定类型数据包的性能。这对于那些希望确保 VoIP 通话不会被下载 GoT 的人打断的公司来说是有益的。

Netfilter /iptables 如何工作

iptables命令是 Netfilter 的用户空间管理工具。Netfilter 是由 Paul“Rusty”Russell 开创的,从 2.4 版本开始就存在于 Linux 内核中。它允许操作系统在内核级执行包过滤和整形,这使得它比用户空间程序受到的限制更少。这对于专用防火墙和路由器主机尤其有用,因为它可以显著提高性能。

Note

术语用户空间程序指的是最终用户用来配置操作系统的某个部分的工具。在这种情况下,内部操作系统组件称为 Netfilter,用户空间组件,即用于配置 Netfilter 的命令,称为iptables

包过滤和整形是根据一组标准或规则,在数据包进入或离开主机时更改或丢弃数据包的能力。Netfilter 通过在数据包进入、通过和/或离开主机时重写数据包报头来实现这一点。

Netfilter 是一种状态包过滤防火墙。存在两种类型的包过滤防火墙:有状态和无状态。无状态包过滤防火墙只检查数据包的报头来过滤信息。它孤立地看待每个数据包,因此无法确定数据包是现有连接的一部分还是孤立的恶意数据包。状态防火墙维护通过它的连接的状态信息。这允许防火墙根据连接状态过滤数据包,从而对您的流量进行更精细的控制。

Netfilter 是 Linux 内核的一部分,可以通过iptables命令在用户空间进行控制和配置..在本章中,我们将经常使用iptables来指代一般的防火墙技术。大多数基于 Linux 的发行版都有一个iptables包,但是它们也有自己的工具来配置防火墙规则——cmd 和 ufw。

Netfilter 通过引用一组表来工作。这些表包含链,链又包含单独的规则。链条拥有一组有序且相似的规则;例如,控制传入流量的一组规则可以保存在一个链中。规则是基本的 Netfilter 配置项目,包含匹配特定流量并对匹配的流量执行操作的标准。

将主机当前正在处理的流量与这些规则进行比较,如果当前正在处理的数据包满足规则的选择标准,则执行该规则指定的操作(称为目标)。这些操作包括忽略数据包、接受数据包、拒绝数据包或将数据包传递给其他规则进行更精细的处理。我们来看一个例子;假设您的 web 服务器上的以太网接口刚刚收到来自 Internet 的数据包。根据您的规则检查该数据包,并将其与选择标准进行比较。选择标准可以包括诸如目的地 IP 地址和目的地端口之类的项目。在本例中,假设您希望允许网络接口上的传入 web 流量访问侦听 web 服务的 HTTP 端口 80。如果传入流量符合这些标准,您可以指定一个操作让它通过。

每个iptables规则依赖于指定一组网络参数作为选择标准来为每个规则选择数据包和流量。您可以使用许多网络参数来构建每个iptables规则。例如,两台主机之间的网络连接称为套接字。这是源 IP 地址、源端口、目的 IP 地址和目的端口的组合。要建立连接,这四个参数都必须存在,iptables可以使用这些值来过滤进出主机的流量。此外,如果您观察基于 TCP/IP 的网络上的通信是如何执行的,您会发现三种协议是最常用的:Internet 控制消息协议(ICMP)、传输控制协议(TCP)和用户数据报协议(UDP)。iptables防火墙可以轻松区分这些不同类型的协议和其他协议。

有了这五个参数(源和目的 IP 地址、源和目的端口以及协议类型),您现在就可以开始构建一些有用的过滤规则了。但是在你开始构建这些规则之前,你需要理解iptables规则是如何构建和交互的。为了获得这种理解,您需要进一步理解一些初始的iptables概念,例如表、链和策略,我们将在接下来讨论这些概念,并略微涉及网络地址转换(NAT)。

桌子

我们讨论过 Netfilter 拥有规则表,可以与流量进行比较,可能会导致采取一些行动。Netfilter 有四个内置表,可以保存处理流量的规则。第一个是filter表,这是用于所有与流量过滤相关的规则的默认表。第二个是nat,处理 NAT 规则。接下来是mangle表,它涵盖了各种数据包变更功能。有一个raw表,用于使数据包免于连接跟踪,它在任何其他 Netfilter 表之前被调用。最后是security表,用于强制访问控制(MAC)网络规则,在过滤表之后处理。这是 SELinux 之类的模块使用的。

每个 Netfilter 表filter, nat, mangleraw and security都包含一组预定义的钩子,Netfilter 将按顺序处理这些钩子。这些挂钩包含称为链的有序规则分组。每个表都包含表中内置的默认链。表 7-5 中描述了内置链条。

表 7-5。

Built-in Chains

| 链子 | 描述 | | --- | --- | | `INPUT` | 用于对到达主机接口的数据包进行规则排序。仅在`filter`、`mangle and security`表格中找到。 | | `FORWARD` | 用于对发往另一台主机的数据包进行规则排序。仅在`filter`、`mangle and security`表中找到。 | | `OUTPUT` | 用于对来自主机接口的传出数据包的规则进行排序。在`filter`、`nat`、`mangle`、`raw and security`表中找到。 | | `PREROUTING` | 用于在数据包被路由到其他链之前改变数据包。在`nat`、`mangle`和`raw`表中找到。 | | `POSTROUTING` | 用于在数据包离开其他链并即将离开接口后更改数据包。仅在`nat`和 mangle 表中找到。 |

每个链都与数据包通过主机的基本路径相关联。当 Netfilter 逻辑遇到一个数据包时,它首先评估该数据包的目的地是哪个链。并非所有表格都包含表 7-5 中列出的所有内置链。

让我们以filter表为例。它仅包含INPUT, OUTPUT,FORWARD链,并且是您最常配置的链。如果一个数据包通过网络接口进入主机,它需要通过INPUT链中的规则进行评估。如果数据包由此主机生成,并通过网络接口发送到网络上,则需要通过OUTPUT链中的规则对其进行评估。FORWARD链用于已经到达本地网络接口但目的地是其他主机(例如,充当路由器的主机)的数据包。您可以在每个表中创建自己的链来保存额外的规则。您还可以将数据包流从内置链定向到您创建的链,但这只能在同一个表空间内进行。

现在让我们来探索一下数据包进入您的系统时的生命周期。我们将只关注filter表,因为这是您最常处理的表。记住表格filter包含三个内置链:INPUT, FORWARD,OUTPUT。这些都有默认的ACCEPT政策(我们很快就会谈到政策)。请看图 7-15 ,图中显示了一个普通的网络数据包如何流经filter表。

A185439_2_En_7_Fig15_HTML.jpg

图 7-15。

Traffic flow through the filter table

在图 7-15 中,你可以看到数据包流经filter表的方式。Netfilter 防火墙程序会根据 IP 数据包的目的地址将它们路由到INPUTFORWARD链。它在 Netfilter 路由块中做出此决定,数据包在到达过滤器之前由 raw、mange 和 nat 表检查。

如果传入数据包的目的地是另一台主机,并且在防火墙主机上启用了 IP 转发,那么它们将被传送到FORWARD链。一旦被该链中的任何规则集处理,如果允许,数据包将离开主机,否则将被丢弃(DROP)。就像在OUTPUT链中一样,这些数据包在离开网络之前,由 Netfilter 程序路由并传递到其他表(mangle,nat)。

如果数据包的目的地是防火墙主机本身,它们将被传递到INPUT链,如果它们被该链中的任何规则集接受,它们将被传递到应用程序(通过安全表)。然后,应用程序可能希望发回一个响应。如果是的话,这个流量由OUTPUT链管理。

OUPUT链用于从实际的防火墙主机发送到别处的另一台主机的数据包。这些可以是从防火墙到远程套接字发起的新连接,也可以是已建立的数据包流的一部分。

Tip

把前面解释的鱼钩想象成一套鱼钩,用不同类型的鱼饵钓不同类型的鱼。当一种特定的鱼游过时,它会被某种诱饵所吸引。一旦它被钩住,我们就根据线末端的东西来决定它的命运。iptables 就像捞取网络数据包一样!

政策

过滤器表中定义的每个内置链也可以有一个策略。策略是链对数据包采取的默认操作,以确定数据包是否完全通过链中的规则,而不匹配任何规则。您可以对数据包使用的策略有DROPREJECTACCEPT。默认情况下,Ubuntu 和 CentOS 都将ACCEPT作为所有表中所有链的默认策略。

DROP策略在不通知发送者的情况下丢弃数据包,通常被描述为“丢弃到地板上”。REJECT策略也会丢弃数据包,但是它会向发送方发送一个 ICMP 数据包,告诉发送方已经发生了拒绝。REJECT策略意味着设备将知道其数据包没有到达目的地,并将快速报告错误,而不是像DROP策略那样等待超时。DROP政策与 TCP RFCs(请求评论)相反,对网络设备可能有点苛刻;具体来说,它们可以长时间等待来自它们丢弃的分组的响应。但是出于安全考虑,通常认为使用DROP策略比REJECT策略更好,因为它向外界提供的网络信息更少。

ACCEPT策略接受流量,如果它没有被链中的规则捕获,就允许它通过防火墙。自然,从安全角度来看,如果将防火墙用作默认策略,它会使防火墙失效。如前所述,默认情况下,iptables使用ACCEPT策略配置所有链,建议对所有链将其设置为DROP策略,这是最佳实践,但必须经过仔细考虑,因为如果操作不当,这可能会停止所有网络流量。

Caution

在远程主机上,很容易将主机与您用来配置它的网络隔离开来。在将策略更改为DROP.之前,确保您已经配置了允许您连接的ACCEPT规则

DROP的默认策略符合防火墙“默认拒绝立场”的基本原则。默认情况下,您应该拒绝所有流量,只向您明确授予访问权限的流量开放主机。这种拒绝可能会有问题,因为为INPUTOUTPUT链设置默认策略DROP意味着不允许传入和传出流量,除非您明确添加允许流量进出防火墙主机的规则。同样,在这样做的时候需要一些小心和考虑,因为如果没有明确允许它们进入或离开,这将导致所有通过防火墙从您的主机或您的内部网络连接的服务和工具失败。

网络地址转换

网络地址转换允许您的专用网络 IP 地址空间看起来来自一个公共 IP 地址。它是在 IP 伪装的帮助下做到这一点的,IP 伪装会重写 IP 数据包报头。NAT 在转换表中保存有关穿越防火墙/路由器的连接的信息。一般来说,当数据包从私有地址空间穿越防火墙/路由器到达公有地址空间时,转换表中会写入一个条目。当匹配的数据包返回时,路由器使用转换表将返回的数据包与始发源 IP 进行匹配。

许多年前,互联网上的每台主机都被分配了一个公共 IP 地址,以便成为互联网的一部分并与其他主机通信。人们很快意识到,如果每台想上网的主机都有自己的公有 IP 地址,那么可用的地址就会用完。发明 NAT 是为了能够让私有地址空间看起来像来自单个 IP 地址。

The End of Nat

IPv6 当然将我们从 IPcalypse 中拯救了出来,并且不再需要 NAT 网络地址了。然而,虽然 NAT 是一种路由服务(相对于防火墙服务),但它确实为它隐藏的地址空间提供了一些安全性。NATs 隐藏我们“私有”网络地址空间的能力意味着许多主机和设备不必担心网络攻击(嗯,他们应该担心,但他们不担心)。

当我们考虑到每一个 IPv6 设备都可以从互联网到达,并且我们考虑到我们刚刚展示的防火墙链的默认规则时,我们的系统可能会在没有 NAT'ing 的安全遮羞布的情况下处于混乱状态。我们仍将有能力在 IPv6 中执行防火墙和路由操作,以保护我们的设备安全,但我们将不能只是“隐藏”我们的网络。

所以纳特死了。但是你仍然会看到它的存在——很多——直到 IPv6 更加普及。

使用防火墙-cmd 命令

对于 CentOS,正如我们提到的,Firewalld 是默认安装的。Firewalld 附带了两个配置工具,一个是 GUI firewall-config,另一个是 firewall-cmd 命令行工具。我们将向您展示如何使用此命令来管理您的主机。

首先是一些关于防火墙的背景。防火墙拥有大家熟悉的区域概念。区域是网络中不同信任级别的名称。从低信任度下降区域开始,逐步提升到公共、外部、隔离区、工作、家庭、内部和受信任区域的更高信任度。这个想法是,根据您的安全需求,将您的接口添加到其中一个区域。默认情况下,接口会添加到公共区域。

它还具有定义服务的能力。可以用简单的语法在 xml 格式的文件中定义服务。取/usr/lib/firewalld/services目录下的https.xml区域文件:

<?xml version="1.0" encoding="utf-8"?>
<service>
 <short>Secure WWW (HTTPS)</short>
 <description>HTTPS is a modified ..<snip>.. be useful.</description>
 <port protocol="tcp" port="443"/>
</service>

您可以在/etc/firewalld/services目录中创建自己的服务文件。这些允许您在防火墙配置中将它们作为服务对象引用。

防火墙的配置选项可以在/etc/firewalld/firewalld.conf/etc/sysconfig/firewalld中找到。在/etc/firewalld/firewalld.conf中你可以设置默认区域和其他选项。在/etc/sysconfig/firewalld中,您可以向 firewalld 守护进程添加任何参数。

默认情况下,防火墙不会在您的系统上运行。您需要启用它来使用它。您可以使用以下命令在防火墙服务上运行 stop | start | disable | enable | status:

 sudo systemctl stop|start|disable|enable|status firewalld

您可以使用以下命令检查其运行状态:

 sudo firewall-cmd –state
not running

要启动 firewalld,并确保它在重新启动主机时再次启动,您需要使用 systemctl 来启用并启动 firewalld 服务。启动后,我们可以查看哪些区域处于活动状态:

 sudo firewall-cmd --get-active-zones
public
 interfaces: enp0s8 enp0s9

要获取有关区域的信息,可以执行以下命令:

 sudo firewall-cmd --zone=public --list–all
public (default, active)
 interfaces: enp0s8 enp0s9
 sources:
 services: dhcpv6-client ssh

 ports:
 masquerade: no
 forward-ports:
 icmp-blocks:
 rich rules:

在这里,我们可以看到公共区域是默认的,并且处于活动状态。我们还可以看到附加到它的接口。服务描述了允许访问的服务,DHCP 客户端和 SSH。DHCP 允许我们自动获取接口的 IP 地址。这些是在服务 xml 文件中定义的,如果需要,我们也可以使用端口。

如果要永久更改接口的区域,可以使用 nmcli 修改网络接口:

 sudo nmcli c modify enp0s9 connection.zone dmz

这将把“ZONE=dmz”添加到接口配置文件/etc/sys config/network-scripts/if CFG-enp0s 9 中。在我们再次打开该界面之前,这不会起任何作用。

我们可以向标记为 dmz 的区域添加 http 服务。我们通过以下方式做到这一点:

 sudo firewall-cmd --zone=dmz --add-service=https --permanent

如果我们要使一个改变成为永久性的,我们需要记住添加- permanent 选项。您可以在不丢失状态信息(不中断连接)的情况下重新加载配置,并且可以使用完整重新加载重新加载丢失状态(用户丢失连接)的配置:

 sudo firewall-cmd reload
 sudo firewall-cmd --complete-reload

在这里,您可以看到我们的隔离区现在连接了 https 和 ssh 服务:

 sudo firewall-cmd --zone=dmz --list-all
dmz
 interfaces:
 sources:
 services: https ssh
 ports:
 masquerade: no
 forward-ports: 

 icmp-blocks:
 rich rules:

但是你可以看到它没有接口。我们将向该接口添加 enp0s9,正如我们所说,该接口需要再次“升级”。

 sudo nmcli c up enp0s9

现在,您应该能够访问运行在 enp0s9 接口上的 HTTPS web 服务了。

使用 ufw 命令

Ubuntu 还有一个从命令行操作防火墙规则的工具。它叫做ufw,可以从ufw包中获得。

Tip

我们将在第八章中讨论更多关于安装包的内容。

ufw 和 Firewalld 完全不同。在幕后,它使用直接的 iptables 指令来创建防火墙规则。如果我们查看/etc/ufw/目录中的规则文件,我们可以看到 ufw 用来创建 Netfilter 规则集的指令。

现在让我们展示一些基本命令。

您可以像这样启用或禁用 ufw:

 sudo ufw enable|disable

要获得当前防火墙状态的输出:

 sudo ufw status numbered
Status: active

   To             Action     From
   --             ------       ----
[ 1] 22/tcp        ALLOW IN  Anywhere
[ 2] 22/tcp (v6)     ALLOW IN  Anywhere (v6)

也就是说,我们将允许来自任何网络(Anywhere)的流量进入(ALLOW IN),并且流量的目的地是端口 TCP 22(在 IPv4 和 IPv6 接口上)。

您可以添加另一个规则,如下所示,以允许 http 流量到达任何本地接口:

 sudo ufw allow 80/tcp
Rule added
Rule added (v6)
 sudo ufw status numbered

...
[ 3] 80             ALLOW IN  Anywhere
[ 4] 80 (v6)         ALLOW IN  Anywhere (v6)
...

在上图中,您可以看到我们已经在所有 IPv4 和 IPv6 接口上添加了从任意位置访问端口 80 的功能。对于公共服务(即/etc/services 文件中列出的服务),也可以这样表达。

 sudo ufw allow http

要删除规则,您可以使用删除命令:

 sudo ufw delete 3
Deleting:
 allow 80
Proceed with operation (y|n)? y
Rule deleted

我们需要在提示符下回答一个yn来确认我们是否希望删除该规则。您也可以指定要删除的规则:

 sudo ufw delete allow http

您还可以使用简化的语法来描述应用程序可能需要的规则。您可以看到 ufw 允许 TCP 端口 22 访问系统的方式(记住 TCP 端口 22 用于 OpenSSH)。缺省情况下,/ etc/ufw/applications . d/OpenSSH-server 文件用于配置端口 22 访问。

[OpenSSH]
title=Secure shell server, an rshd replacement
description=OpenSSH is a free implementation of the Secure Shell protocol.
ports=22/tcp

这里我们有名称空间[OpenSSH],后面是标题和描述。然后描述我们需要的端口,用“|”分隔多个端口和协议。例如,假设我们有一个 web 应用程序需要打开以下 TCP 端口,80、443 和 8080。我们可以通过添加文件/etc/ufw/applications . d/we B- server 来创建一个 ufw 应用程序,如下所示:

[WebServer]
title=Application Web Server
description=Application X web services
ports=80/tcp|443/tcp|8080/tcp

要查看我们的新配置,我们可以使用以下命令:

 sudo ufw app info WebServer
sudo: unable to resolve host ubuntu-xenial
Profile: WebServer
Title: Application Web Server
Description: Application X web services

Ports:
 80/tcp
 443/tcp
8080/tcp

创建应用程序配置后,我们需要发出如下更新子命令:

 sudo ufw app update WebServer

然后,要应用新的规则集,您可以发出类似这样的命令,只允许 192.168.0.0/24 地址范围内的主机。

 sudo ufw allow from 192.168.0.0/24 to any app WebServer
Rule added

让我们稍微分解一下。我们说,我们希望从192.168.0.0/24网络到本地主机上的any地址的任何连接能够allowapp WebServer中列出的任何端口。现在列出我们已经完成的工作,我们将看到以下内容:

 sudo ufw allow from 192.168.0.0/24 to any app WebServer
 sudo ufw status numbered
Status: active

   To             Action   From
   --              ------    ----
[ 1] WebServer     ALLOW IN  192.168.0.0/24

需要使用 ufw route 命令路由通过该主机的流量。例如,我们有 SSH 流量在 enp0s8 上进入,在 enp0s9 上离开,我们将这样编写 ufw 路由规则:

 sudo ufw route allow in on enp0s8 out on enp0s9 to any port 22 proto tcp

这里我们说的是允许 SSH 流量(22)在 enp0s8 上进入,在 enp0s9 上离开,到达任何目的地 IP 地址。这将在 iptables 中创建一个规则,当使用 iptables -L 命令查看时,该规则如下所示:

Chain ufw-user-forward (1 references)
num target   prot opt  source        destination
1  ACCEPT   tcp  --  0.0.0.0/0       0.0.0.0/0      tcp dpt:22

让我们继续探索关于 iptables 的更多信息以及这意味着什么。

使用 iptables 命令

之前,我们已经描述了 Netfilter 中数据包流的概念,并介绍了可以用来管理它们的两个主要命令,firewall-cmd 和 ufw。重要的是,我们现在了解它们是如何工作和做什么的。我们现在将向您展示如何使用iptables命令来管理 Netfilter。iptables命令允许您执行以下操作:

  • 列出数据包筛选器规则集的内容。
  • 添加/删除/修改数据包筛选器规则集中的规则。
  • 列出/清零数据包筛选器规则集的每规则计数器。

对于 IPv6,您使用 ip6tables 命令,但是从语法上来说,它是相同的命令,但是指示 Netfilter 如何对 IPv6 数据包进行操作。在本节中,我们将主要讨论 IPv4 命令,但 IPv6 的命令用法没有什么不同。为了有效地使用iptables命令,您必须拥有 root 权限。基本的iptables命令结构和参数如下:

iptables –t table-name command chain rulenumber paramaters –j target

表 7-6 给出了命令的概要,如前面的语法command所示,我们将演示它们的描述。

表 7-6。

Command Options Available to the iptables Command

| [计]选项 | 描述 | | --- | --- | | `-L` | 列出您记忆中的当前`iptables`规则。 | | `-D` | 删除链中的规则。要删除的规则可以表示为规则编号或要匹配的模式。 | | `-I` | 将规则插入链中。这将在链的顶部插入规则,除非指定了规则编号。 | | `-F` | 冲洗链条。这将从一个链中删除所有规则,或者如果没有指定链,则删除表中所有链中的所有规则。 | | `-A` | 将规则追加到链中。与`-I`相同,只是默认情况下规则被附加到链的底部。 | | `-` `X` | 删除一个链。该链必须为空,并且在您希望删除它时没有被任何其他链引用。 | | `-N` | 创建新链。不能存在同名的目标。 | | `-R` | 替换现有规则 | | `-C` | 检查规则是否存在于链中,并返回退出代码。 | | `-S` | 打印指定链中的规则(类似于–L)。这些可以在以后用于恢复 IPtables。 | | `-P` | 为链设置默认策略。每个内置链(`INPUT, OUTPUT, FORWARD, POSTROUTING`等)。)的默认策略是`ACCEPT, REJECT`或`DROP`。 |

Caution

当您使用防火墙规则时,如果您犯了一个错误并阻止了所有网络流量,那么访问系统控制台总是一个好主意。如果您犯了一个错误(每个人至少都会犯一个错误),那么更改网络上的防火墙规则,尤其是在远程系统上,会让您陷入尴尬的境地。

iptables –t table-name command chain rulenumber paramaters –j target

大量的参数,如上面的parameters中的iptables命令语法所示,可以用来操纵通过防火墙的数据包。当我们解释链条时,我们说它们就像钩子。这些参数是放在鱼钩上的不同类型的鱼饵。每个参数将根据包中的信息吸引包。它可以是数据包或协议的源地址和目的地址。此外,分组可以与源或目的地端口或者分组所处的状态相匹配。这些参数还可以用于匹配数据包、捕获数据包、标记数据包和释放数据包。

iptables –t table-name command chain rulenumber paramaters –j target

iptables命令语法中由–j target描述的目标参数是如果信息包被钩住时要对其执行的动作。常见的目标有ACCEPT, DROP, LOGMASQUERADECONNMARK。目标也可以是另一个用户定义的链。

我们现在想通过一些例子向你展示如何使用iptables命令。

iptables命令是 Ubuntu 和 CentOS 通用的,操作方式相同。让我们来看看列出我们当前运行的iptables规则集的内容。我们通过发出以下命令来实现这一点:

 sudo /sbin/iptables -t filter -L --line-numbers

这里我们正在查看filter表。–L选项列出了所有链及其相关的规则,每条链都有由--line-numbers指定的行号。行号很重要,因为正如已经提到的,iptables 从第一个到最后一个按顺序贯穿每个规则。当您想要添加一个新规则或删除一个旧规则时,您可以使用这些数字来确定您想要的目标规则。清单 7-7 中列出了该命令的输出。

sudo /sbin/iptables -t filter -L --line-numbers
Chain INPUT (policy ACCEPT)
num target   prot opt source        destination

Chain FORWARD (policy ACCEPT)
num target   prot opt source        destination

Chain OUTPUT (policy ACCEPT)
num target   prot opt source        destination

Listing 7-7.
iptables

filter table

这有点反高潮。默认情况下,我们在过滤表的任何链中都没有规则。这当然意味着在我们的主机上启动的任何服务都可以立即在我们的网络上开始通信。我们应该解决这个问题。

由你来设定最低的规则要求,我们会在进行过程中提供帮助。我们可以选择创建自己的链,这样我们就可以对防火墙规则进行分组。您不必创建新的链,但是如果您希望将所有规则与默认链分开,您可以创建新的链,我们可以将它们与逻辑业务组相关联。我们也可以通过逻辑网络组来定义这个链,我们将保持它的简单性,称它为IPV4-INCOMING

 sudo /sbin/iptables –t filter –N IPV4-INCOMING

该命令使用filter表中的–N IPV4-INCOMING创建一个新的链,如–t filter所示。它还没有任何关联的规则,但是在本节结束时,您将能够添加和删除规则。

Note

您不必使用前面的命名标准来命名链。但是,使用对您的配置有意义的东西会让您受益。否则,您的iptables规则的输出可能看起来非常混乱,难以诊断错误。如果你不想的话,你也不需要创建你自己的用户定义的链,但是如果你这样做的话,会使阅读你的规则更简单。

通过列出我们的 iptables,您将看到我们创建的新链:

Chain IPV4-INCOMING (0 references)
num target   prot opt source        destination

它还没有规则,所以我们要创建一些。让我们从一个简单的规则开始..

sudo iptables -t filter -A IPV4-INCOMING -d 0.0.0.0/0 -s 0.0.0.0/0 -j ACCEPT
Chain IPV4-INCOMING (0 references)
num target   prot opt source  destination
1  ACCEPT   all -- anywhere anywhere

该规则允许来自任何地方、任何端口的所有连接,相当于接受策略。我们将集中精力解释我们发出的命令,而不是它的优点。分解一下,我们再次在filter桌上工作。接下来,我们向IPV4-INCOMING链添加(-A)一个规则。我们对- destination ( -d)使用了简化的语法,并使用了0.0.0.0/0的 anywhere IP 表示法来表明这个规则应该匹配我们本地主机上的所有 IPv4 地址。同样,对于- source ( -s),我们接受该规则的所有源地址(0.0.0.0/0)。最后,我们提供这个规则的目标(-j),它将跳转到ACCEPT目标。接受目标允许套接字建立连接。

好吧,这是一个错误,让我们取消这条规则。从上面的 iptables 列表中,我们可以看到我们想要删除的规则位于位置 1。我们将在删除该规则时使用它。

 sudo /sbin/iptables –t filter –D IPV4-INCOMING 1

如果您再次列出这些表,我们会发现没有关联的规则。我们现在可以删除该链,因为它是空的。为此,我们发出以下命令:

 sudo /sbin/iptables -t filter -X IPV4-INCOMING

整个链条都被拆了。您不能删除默认的输入、输出正向链。

我们将重新创建链,并添加一些规则,以便显示下一部分。这是我们的名单。

1  ACCEPT   all -- anywhere  anywhere  ctstate INVALID
2  ACCEPT   all -- anywhere  anywhere  ctstate RELATED,ESTABLISHED
3  DROP    all -- anywhere  anywhere
4  ACCEPT   tcp -- anywhere  anywhere  tcp   dpt:ssh

如果这个规则集在输入链中,将会导致问题。让我们使用 iptables 命令来修复它。首先,我们需要将规则 4 移到规则 1 之上。为此,我们发布了以下内容:

iptables –t filter –I IPV4-INCOMING 1 –p tcp –-dport 22 –j ACCEPT

当您现在查看IPV4-INCOMING链中的第三行时,您可以看到我们的规则已经被插入,并且具有目标ACCEPT

num target   prot opt source   destination
1  ACCEPT   tcp -- anywhere  anywhere   tcp   dpt:ssh
2  ACCEPT   all -- anywhere  anywhere   ctstate INVALID
3  ACCEPT   all -- anywhere  anywhere   ctstate RELATED,ESTABLISHED
4  DROP    all -- anywhere  anywhere
5  ACCEPT   tcp -- anywhere  anywhere   tcp dpt:ssh

我们将删除第 5 条规则,就像用–D IPV4-INCOMING 5开关删除之前的规则一样。但是我们的规则看起来还是不对。规则 2 不应该接受连接,而应该丢弃它们。让我们用正确的规则替换那个规则(不要担心,我们很快会解释conntrack是做什么的)。

sudo iptables -R IPV4-INCOMING 2 -m conntrack --ctstate INVALID -j DROP
1  ACCEPT   tcp -- anywhere  anywhere  tcp   dpt:ssh
2  DROP    all -- anywhere  anywhere  ctstate INVALID
3  ACCEPT   all -- anywhere  anywhere  ctstate RELATED,ESTABLISHED
4  DROP    all -- anywhere  anywhere

看起来好多了。因此,像 Netfilter 防火墙一样从上到下阅读规则,我们允许 SSH 流量,丢弃无效流量,允许任何当前和已建立的流量,然后丢弃所有其他流量。

您还可以flush您的链,这意味着从所有链中移除所有规则或者从指定链中移除所有规则。这可用于在添加新规则集之前清除任何现有规则。

Note

我们将很快向您展示如何保存您的 iptables,如果您不想丢失当前的设置,您可能需要在下一步之前做这件事。(提示:iptables-save > iptables.backup)

您可以通过发出类似下面的命令来实现这一点:

 sudo /sbin/iptables –t filter –F IPV4-INCOMING

这里我们已经清空了filter表中IPV4-INCOMING链的所有规则,如-F所示。如果您现在查看filter表中的INPUT链,您将会看到不再有任何与它相关联的规则。

 sudo /sbin/iptables -L IPV4-INCOMING --line-numbers
Chain IPV4-INCOMING (policy ACCEPT)
num target   prot opt source        destination

在这种情况下,由于我们有一个默认策略ACCEPT,清除这个链并不影响我们主机的操作方式。如果我们将策略更改为DROP并刷新链,所有入站连接都将被切断,使我们的主机无法用于网络相关的任务和服务。这是因为该策略决定了与任何规则都不匹配的数据包会发生什么情况。如果您的链中没有规则,那么iptables将使用默认策略相应地处理数据包。

目前,我们已经能够随意更改我们的链,因为 Netfilter 没有配置为使用它。为了解析我们的IPV4-INCOMING链中的规则,我们需要在INPUT链中添加一个规则,将所有传入主机的数据包定向到IPV4-INCOMING链。

 sudo /sbin/iptables –t filter –A INPUT –j IPV4-INCOMING
num target     prot opt source      destination
1  IPV4-INCOMING all --  anywhere     anywhere

我们已经在上面包含了 list ( –L)命令的输出。这里,我们发出了将规则附加到输入链的命令。它是接收以我们的主机为最终目的地的防火墙入站数据包的第一个链。就像我们的其他规则一样,我们给它添加了-t filter –A INPUT。具有任何协议的任何分组现在将被发送到链IPV4-INCOMING,如- j IPV4-INCOMING所示,用于进一步处理。

我们可能不希望所有的数据包都进入 IPV4 传入链。也许我们只想让 tcp 数据包进入该链,我们将在不同的链中处理 udp 数据包。您可以通过添加其他参数来细化您想要在规则中捕获的内容。在下面的例子中,我们可以只匹配 TCP 流量,并通过替换(-R)我们的第一个输入规则将它发送到某个目标。

 sudo /sbin/iptables –t filter –R INPUT 1 –p tcp –j IPV4-INCOMING

我们可以进一步完善我们的“诱饵”来吸引不同类型的鱼。。。呃。。。小包。也许我们只想要目的地址在 192.168.10.0/24 网络中的 tcp 数据包?看起来会像这样:

 sudo /sbin/iptables –t filter –R INPUT 1 –s 0.0.0.0/0 –d 192.168.10.0/24 –p tcp –j IPV4-INCOMING
num target     prot opt source    destination
1  IPV4-INCOMING tcp -- anywhere   192.168.10.0/24

我们现在已经替换了一个规则,通过指定- RINPUT链,该链仅定向具有tcp协议的包,并且仅当它们的目的地地址在192.168.10.0/24网络中。像这样的数据包被发送到IPV4-INCOMING链。

最后,我们现在将在我们的INPUT链上设置一个默认策略。我们将把它设置为DROP,这样任何与我们的规则集不匹配的东西都将被自动“扔到地板上”我们通过指定表名(由–t filter指定)、链(由INPUT表示)和目标(??)来实现这一点。

Caution

这是一个有潜在危险的命令,可能会断开所有的网络连接。在运行这个命令之前,确保您可以访问物理控制台,以防万一。

 sudo /sbin/iptables –t filter –P INPUT DROP

当我们现在在filter表中列出INPUT链时,默认策略显示为DROP.

 sudo iptables -L INPUT
sudo /sbin/iptables -t filter -L --line-numbers
Chain INPUT (policy DROP)
num target     prot opt source    destination
1  IPV4-INCOMING all --  anywhere   anywhere

记住将DROP政策应用到你所有的连锁店是一个好的实践和好的习惯。为了使防火墙最安全,默认的拒绝策略应该在您的所有链中强制执行。

有了这些基本命令,您将能够在您的链和规则集上执行大多数功能。您可以将更多功能添加到您的规则中,使您的防火墙执行非常复杂和有趣的路由。请参阅iptables man页,了解您可以执行的任务的完整列表。

您可以非常简单地保存和恢复 iptables 配置。Ubuntu 和 CentOS 都有相同的命令来完成这个任务。首先,让我们保存 iptables 规则:

 sudo iptables-save > /path/to/your/iptables.bak

这里,我们发出了 iptables 命令,并将输出(>)定向到文件系统中的一个文件。如果不将输出重定向到文件,它将打印到屏幕上。恢复这些文件也很简单。首先,我们可以使用以下命令对文件的语法进行测试:

 sudo iptables-restore –t -v < /path/to/your/iptables.bak
Flushing chain `INPUT'
Flushing chain `FORWARD'
Flushing chain `OUTPUT'
# Completed on Fri May 20 21:49:49 2016

这显示了该命令的详细输出(-v ),并且如果您的配置文件中有错误(如iptables-restore: line 4 failed),将会给出一个错误。要恢复 iptables 文件,您需要移除测试开关(-t)。

在 Ubuntu 和 CentOS 上启动和停止 iptables 是不同的。在 Ubuntu 上,你可以做以下事情来添加和删除你的防火墙规则。你可以对你的规则做任何修改和补充。然后,您可以在您的/etc/network/interfaces文件中使用以下内容来激活您的规则并再次保存它们。

auto enp0s3
iface enp0s3 inet dhcp
  pre-up iptables-restore < /etc/network/firewall
  post-down iptables-save -c > /etc/network/firewall

这里是 enp0s3 的标准接口配置。在调出界面之前,我们使用pre-up命令激活防火墙规则。一旦我们的接口关闭,我们就使用post-down命令保存我们的规则。

如果你在 Ubuntu 主机上使用ufw,你可以使用systemctl命令来停止和启动ufw:

 sudo systemctl start|stop|restart ufw

这里的systemctl用于管理ufw服务。

在 CentOS 机器上,因为它紧跟 Red Hat,所以需要安装管理 iptables 的常用工具(如果您还没有安装的话)。您需要下载并安装 iptables-services 包(yum install –y iptables-services)来访问以下命令。

要启动和停止iptables,您可以使用systemctl命令:

 sudo systemctl start|stop|restart iptables

这些操作现在应该不言自明了。你也可以使用status到你期望的当前状态。当您执行stop时,您当前的防火墙规则将被保存。

进一步阅读

Netfilter 和iptables是复杂的主题,我们建议您进一步阅读它们。有关 Netfilter 和iptables命令的更多信息,请阅读其man页面。还提供了一些在线教程:

Ubuntu 还会在本地硬盘上安装一些 iptables 文档:

  • /usr/share/doc/iptables/html/packet-filtering-HOWTO.html
  • /usr/share/doc/iptables/html/NAT-HOWTO.html

对于更高级的主题,您可能会感兴趣:

  • 使用 netfilter、iproute2、NAT 和 L7-filter 设计和实现具有 QoS 的 Linux 防火墙
  • Linux 防火墙:用 iptables、psad 和 fwsnort 进行攻击检测和响应
  • 《Linux 防火墙:使用 nftables 增强安全性》(第 4 版)作者:Steve Suehring(Pearson Education 2015 年)

解释防火墙规则

现在是更仔细地研究您可能在主机上看到的防火墙规则的好时机。根据您的安装方法,CentOS 和 Ubuntu 都可以预装防火墙配置。像ufwfirewalld这样的工具都是管理防火墙的用户工具,它们试图以一种合理的方式做到这一点,并为您隐藏规则的复杂性。

针对 firewalld 或 ufw 的过滤器表的默认规则集可以超过 100 行,清单 7-8 显示了安装了 firewalld 的 CentOS 主机上过滤器表的输入链的输出(iptables-save)。在清单 7-8 中,我们减少了输出并增加了行号来帮助澄清我们对输出的解释。

1\. *filter
2\. :INPUT ACCEPT [0:0]
3\. :INPUT_ZONES – [0:0]
4\. :INPUT_ZONES_SOURCE - [0:0]
5\. :INPUT_direct - [0:0]
6\. -A INPUT -i virbr0 -p udp -m udp --dport 53 -j ACCEPT
7\. -A INPUT -i virbr0 -p tcp -m tcp --dport 53 -j ACCEPT
8\. -A INPUT -i virbr0 -p udp -m udp --dport 67 -j ACCEPT
9\. -A INPUT -i virbr0 -p tcp -m tcp --dport 67 -j ACCEPT
10\. -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
11\. -A INPUT -i lo -j ACCEPT
12\. -A INPUT -j INPUT_direct
13\. –A INPUT –j INPUT_ZONES_SOURCE
14\. –A INPUT –j INPUT_ZONES
15\. –A INPUT –p icmp –j ACCEPT
16\. -A INPUT -j REJECT --reject-with icmp-host-prohibited
Listing 7-8.Firewalld INPUT chain

Netfilter 喜欢以下格式的 iptables 规则配置:

* <table name>
<CHAIN> <POLICY> [<byte:counter>]
-A <CHAIN> <rule set>
COMMIT

* filter是过滤表节的开始,每一行将被读入,直到COMMIT语句。然后列出链以及它们的默认策略(如果它们有一组的话)。字节和计数器标记[ 0:0 ],可用于查看有多少流量通过该链,以及有多少数据包通过该链(如果您愿意,可以使用iptables –Z chain将这些计数器重置为零)。然后,我们列出该特定链的所有规则。最后,我们要求COMMIT告诉 Netfilter 添加表节。

清单 7-8 中的第 2-5 行是过滤表中链的定义。我们只显示了与INPUT相关的链,但是还有FORWARDOUTPUT和其他用户定义的链在这里没有显示。第 2 行的INPUT ACCEPT是默认的INPUT链,该链的默认策略是ACCEPT。第 3-5 行是 Firewalld 用来将规则分组为更多逻辑单元的链。它们没有附加策略(-)。

在清单 7-8 中,第 6-9 行做了类似的事情。首先要注意的是,符合这些规则的数据包将跳转到ACCEPT目标。第 6-7 行允许在 udp 和 tcp 协议上从任何源到virbr0接口上的任何目的地进行 DNS 查询。virbr0接口用于共享物理设备的虚拟接口。第 8-9 行类似,提供了对 bootps (DHCP)服务的访问。这些规则允许可能已经启动的虚拟主机(例如 KVM 或 Xen 虚拟主机)访问这些服务以获得 DNS 和 DHCP 服务。我们将在第二十章中更多地讨论 KVM 和 Xen。

为了理解第 10 行,我们需要记住我们谈到的 tcpdump 和 IP 协议用来与另一台主机建立连接(套接字)的三次握手。在第 10 行中,我们希望接受连接状态为RELATEDESTABLISHED的所有连接。这意味着已经完成握手并且已经“建立”的任何连接或者与现有连接“相关”的任何连接都被允许。Netfilter 使用conntrack模块来跟踪连接并了解它们的状态,正是这一点使得 Netfilter 成为有状态防火墙。

New, Established, Related, and Invalid States

NEW连接状态表示新发起的连接,数据没有通过该连接来回传递。如果您想允许一个服务的新连接,您必须允许NEW连接状态,无论是传入还是传出。例如,如果不指定邮件服务器上的传入 SMTP 流量接受NEW连接状态,远程客户端将无法使用邮件服务器发送电子邮件。

ESTABLISHED连接状态表示正在传输数据的现有连接。如果您希望服务能够保持与远程客户端或服务器的连接,您需要允许ESTABLISHED连接。例如,如果您希望允许 SSH 连接到您的主机,您必须允许NEWESTABLISHED传入流量和已建立的传出流量,以确保连接是可能的。

RELATED状态指的是用于促进另一个连接的连接。一个常见的例子是 FTP 会话,其中控制数据被传递到一个连接,而实际的文件数据流经另一个连接。

INVALID状态标记在处理数据包时出现问题的连接上:它们可能超出了防火墙的处理能力,或者是与任何当前连接无关的数据包。

在清单 7-8 中,第 11 行的规则被设置为接受环回设备上的所有连接,也称为 lo 或 127.0.0.1。这对于进程间通信非常重要,因为许多进程经常使用回送来相互发送消息。重要的是允许它接受数据包,否则许多东西会停止工作。

清单 7-8 中的第 12-14 行用于将所有来自本地接口的数据包定向到防火墙链。它们的格式如下:

 –A <CHAIN> -j <CHAIN TO JUMP TO>.

所以第 12 行说将所有包发送到由 Firewalld 管理的INPUT_direct链。如果您查看 iptables-save 命令的完整输出,您可能看不到任何与此链相关联的规则。如果我们找不到任何规则,我们将转到输入链中的下一个规则。第 13 行也可能没有与该链相关联的任何规则。如果我们遵循第 14 行(–A INPUT –j INPUT_ZONES)的规则,我们将跳转到INPUT_ZONES链。那条链子看起来像这样:

-A INPUT_ZONES -g IN_public
-A IN_public -j IN_public_log
-A IN_public -j IN_public_deny
-A IN_public -j IN_public_allow
-A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
Listing 7-9.Iptables Jump and Goto

在清单 7-9 中,我们有一个INPUT_ZONES链,带有一个到IN_public链的–g开关。–g表示转到用户链IN_public并在那里继续处理,而不是返回INPUT_ZONES

一个数据包将进入IN_public,然后跳转到IN_public_log,返回IN_public,跳转到IN_public_deny,返回IN_public,然后跳转到IN_public_allow。在IN_public_allow中,我们有允许 ssh 连接的规则。

-A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT

为了解释这一行,让我们假设我们将启动一个到主机上本地 SSH 服务的传入连接。最初的传入数据包将包含 SYN 标志,以尝试启动套接字。上面一行告诉我们,我们匹配第一个 tcp ( -m tcp)数据包,其目的端口为 22 ( --dport 22)。如果我们以前没有见过这个连接,那么它与任何数据包都没有关系,它也是一个 SYN 数据包,所以我们还没有建立连接。--ctstate NEW允许我们从远程主机启动套接字。一旦连接处于ESTABLISHED状态,我们的防火墙就允许正在进行的通信。然后清单 7-8 中的第 10 行处理ESTABLISHEDRELATED连接。

The Different Protocols

Netfilter 或iptables根据 TCP/IP 协议栈中的不同协议过滤流量。您可以过滤 TCP、UDP、AH、IPsec、PPPoE、STP 等协议。

您可以在/etc/protocols中获得所有可能的协议及其协议编号的列表。在 iptables 中,您可以在声明规则时指定协议名称,如 udp,或协议编号,如 17(对于 UDP)。下面,我们将对 UDP 数据包进行过滤:

iptables –A INPUT –p 17 –m conntrack --ctstate NEW --dport 53 –j ACCEPT

如果不指定协议,默认为匹配所有协议。您也可以使用!来排除协议,方法是指定您的规则,如下所示:

iptables –A INPUT –p ! udp –m conntrack --ctstate NEW –j ACCEPT

此规则接受所有协议的数据包,除了在新状态下使用 UDP 协议的数据包。下面是一个很好的关于网络协议的参考: http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml

日志记录、速率限制和保护 Netfilter

保护和管理防火墙的一部分是监控日志记录、丢弃不需要的流量和限制可能的攻击媒介。使用 Netfilter,我们可以在日志中记录进入防火墙的连接尝试,然后接受或拒绝这些连接。这本身就是一个攻击媒介,因为攻击者可以用连接尝试来填充您的防火墙日志,并利用它来掩盖另一个攻击,或者使持续记录的不良连接淹没您的防火墙资源。因此,对任何防火墙日志进行速率限制或对连接尝试本身进行速率限制是一个好主意。

当数据包进入防火墙时,我们可以在协议、端口、源地址、目的地址等方面进行匹配。以 SSH 为例,我们可以匹配协议 TCP 和目的端口 22。当你建立一个服务器,它的 SSH 端口对外界开放时,你会看到很多僵尸程序试图使用暴力的用户名/密码组合来访问这个端口。通常我们会这样设置我们的 Netfilter(使用 iptables、firewall-cmd 或 ufw ):

-A INPUT_ZONES -g IN_public
-A IN_public -j IN_public_log
-A IN_public -j IN_public_deny
-A IN_public -j IN_public_allow
-A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT

在 IN_public_log 链中,我们可以添加这样的规则:

-A IN_public_log ! --source 10.0.0.1/32 \
 –p tcp –m tcp --dport 22 \
 –m conntrack --ctstate NEW \
 –m limit --limit 2/m --limit-burst 5 \
 –j LOG --log-prefix “IN_LOG_SSH:” 

在这里,我们添加了一条规则:

  • 如果我们的源地址不是来自我们信任的 IP 地址
  • 它是端口 22 上的 TCP 请求
  • 并且它具有新的连接状态
  • 在前 5 个匹配的数据包之后,应用每分钟 2 个连接的限制
  • 记录这些连接尝试,并在日志中添加前缀“IN_LOG_SSH”。

这将记录 SSH 连接尝试地址,我们通常不希望连接来自(! --source 10.0.0.1/32–感叹号,!,表示‘不从’)。我们还可以通过在 IN_public_allow 链中编写如下规则来进一步保护我们的 SSH 服务:

-A IN_public_allow -p tcp --dport ssh -m conntrack --ctstate NEW -m recent --set --name drop_ssh
-A IN_public_allow -p tcp --dport ssh -m conntrack --ctstate NEW \
 -m recent --name drop_ssh --update --seconds 60 --hitcount 5 -j DROP

这里我们使用的是 Netfilter recent模块。recent模块允许我们创建一个动态的 IP 地址列表,我们可以在以后的匹配中应用这些动作。上面创建了与新 TCP SSH 连接相匹配的 IP 列表,这些 IP 与目标 TCP 端口 22 和新连接状态相匹配。该列表被命名为 drop_ssh。下一行表示,每次看到这种类型的连接时,我们都会更新上次看到的时间戳。如果我们在 60 秒的时间段内看到该规则的 5 次命中,则断开这些连接。

您还可以使用 firewall-cmd 来启用日志记录和速率限制。如果我们想将最新的模块应用到我们的 firewall-cmd 中,请从命令行发出类似这样的命令:

 sudo firewall-cmd --permanent --direct --add-rule ipv4 filter IN_public_allow 0 -p tcp --dport 22 \
 -m state --state NEW -m recent --set –name ssh_drop
 sudo firewall-cmd --permanent --direct --add-rule ipv4 filter IN_public_allow 1 -p tcp --dport 22 \
 -m state --state NEW -m recent –name ssh_drop --update --seconds 60 --hitcount 5 -j DROP

这使用了firewall-cmd--direct配置选项,通过recent模块添加我们的规则。直接论证是这样表述的:

--permanent --direct --add-rule { ipv4 | ipv6 | eb } <table> <chain> <priority> <args>

所以在第一个命令中,表是filter,链是IN_public_allow,优先级是0,后面跟着参数。你可以在这里阅读更多关于--direct论点的内容:

在该链接中,您还将发现如何使用丰富语言命令以及使用firewall-cmd来应用限制和日志记录。

另外,每次您想用firewall-cmd更改防火墙规则时,请记住发出以下命令:

 sudo firewall-cmd --reload

Caution

如果有人假冒您的合法 IP 地址来触发丢弃规则,实际上拒绝您访问您的系统,这些规则有时会有问题。

除此之外,我们可以使用 Fail2ban 或 Sec 等工具来解析我们的 SSH 日志,然后“监禁”或“丢弃”与一组不成功的连接尝试标准相匹配的连接。你可以在这里阅读更多关于 Fail2ban 的信息:

现在分别看一下 IN_public_allow 和 IN_public_deny 链中的这些 to 规则:

-A IN_public_allow -p icmp -m icmp --icmp-type echo-request –m limit --limit 1/s -j ACCEPT
-A IN_public_deny -p icmp -m icmp --icmp-type any -j DROP

IN_public_allow链中,我们已经指定对于类型为echo-request的 ICMP 数据包,我们将接受每秒 1 个数据包。ICMP 数据包可能是攻击的途径,因此一些管理员通常会默认阻止所有外部(有时是内部)ICMP 数据包。在这里,我们决定允许回应请求,或 ping,其他类型的 ICMP 将被丢弃。

Different Types of Attacks

人们可以使用不同类型的攻击来破坏您的网络。一些攻击你网络的外围,用虚假的连接把它捆绑起来,直到它崩溃并停止响应;这些被称为拒绝服务攻击(DoS),例如 SYN flood 攻击。其他人将使用您正在运行的服务(如邮件或 web 服务器)绕过您的外围安全并从您的网络内部发起攻击(例如,病毒攻击、特洛伊木马攻击、脚本注入攻击)。

分布式拒绝服务攻击(DDoS)也越来越普遍,并且很难防范。这就是攻击者使用僵尸网络(一组“拥有的”互联网计算机,可以通过发送数百万个虚假请求来攻击网络)用大量特制数据包淹没网络防火墙的地方。即使是拥有非常昂贵设备的大公司也可能受到这些攻击。

虽然保持网络安全是一个持续的过程,但您可以做一些事情来最大限度地降低风险:

  • 跟踪主机的安全警报邮件列表。
  • 让您的主机保持最新的安全补丁。
  • 关闭不需要的端口和服务。
  • 了解你的日志,学会发现任何看起来奇怪的事情。
  • 至少每六个月检查一次您的安全性,以确保它仍然正常工作。
  • 使用上游 DDoS 缓解服务来帮助保护您的网络。

连接到互联网的任何东西都不是 100%安全的,但是您可以采取措施来确保您的网络不存在众所周知的漏洞。脚本小子很快就会找到您的主机,并尝试常见的可预防的攻击。

还有一些其他规则可以帮助防止不同的网络攻击。一种常见的攻击称为 SYN flood,攻击者向受害者发送伪造的 SYN 数据包。SYN flood 使用 TCP 三次握手来耗尽防火墙上的资源。

Linux 内核(3.12 版)最近增加了一个模块,可以使用 SYNPROXY。此 Netfilter 模块旨在缓解 SYN 泛滥。您可以在此阅读有关 SYN floods、DDoS 缓解和 SYNPROXY 的更多信息:

使用 ufw,我们只能应用简单的过滤和日志记录。如果我们想要限制我们的 SSH 连接,我们将发出以下命令:

 sudo ufw limit ssh/tcp

目前仅支持 ipv4,如果在过去 30 秒内尝试超过 5 次,将会拒绝连接。如果我们想要记录我们的 SSH 连接,我们将发出以下命令:

 sudo ufw allow log 22/tcp

这将记录 SSH 的连接。有关这些选项的更多信息,请参见 ufw 手册页。

进一步探索防火墙-cmd

如前所述,我们还可以在 CentOS 主机上使用 firewall-cmd 来管理 iptables。在 CentOS 主机上,我们可以通过 firewall-cmd 执行相同的 iptables 指令。在本例中,我们将把enp0s8添加到外部区域,然后在该接口上配置 IP 伪装。然后,我们将端口 80“转发”到我们在192.168.0.1:80上的内部主机。

首先,我们需要将我们的enp0s8接口设置在外部区域中。有两种方法,一种是使用firewall-cmd cli,另一种是在/etc/sysconfig/network-scripts/ifcfg-enp0s8中直接编辑网络配置文件。如果接口由网络管理器管理,则直接编辑以下内容并将其添加到文件的底部:

ZONE=external

如果设备不受网络管理器管理,则可以发出以下命令:

 sudo firewall-cmd --zone=external --add-interface=enp0s8

下一步是向外部区域添加伪装。首先,我们可以检查它是否已经有它,然后像这样添加它:

 sudo firewall-cmd --zone=external --query-masquerade
no
 sudo firewall-cmd --zone=external --add-masquerade --permanent

我们添加了--permanent来使更改持久化。现在,为了添加我们的端口转发,或者我们之前所说的 DNATs,我们将发出以下命令:

 sudo firewall-cmd --zone=external --add-forward-port=port=80:proto=tcp:toport=80:toaddr=192.168.0.1 --permanent

我们的区域现在应该是这样的:

 sudo firewall-cmd --zone=external --list-all
external (active)
 interfaces: enp0s8
 sources:
 services: ssh
 ports:
 masquerade: yes
 forward-ports: port=80:proto=tcp:toport=80:toaddr=192.168.0.1
 icmp-blocks:
 rich rules:

我们现在将为我们的服务设置一个 DMZ 区域,这些服务位于我们的网络内部,但由外部客户端访问,例如托管在内部服务器上的公共网站。为此,我们将我们的enp0s9接口添加到zone=dmz中,就像我们为enp0s8所做的那样。

一旦我们有了,我们的非军事区应该是这样的:

$msudo firewall-cmd --zone=dmz --list-all
dmz (active)
 interfaces: enp0s9
 sources:
 services: ssh
 ports:
 masquerade: no
 forward-ports:
 icmp-blocks:
 rich rules:

现在,当我们试图从公共互联网访问我们的网站时,用类似 curl 的东西:

 curl -I http://website.example.com
HTTP/1.1 200 OK
Date: Mon, 23 May 2016 12:40:16 GMT
Server: Apache/2.4.18 (Ubuntu)
Last-Modified: Tue, 10 May 2016 11:54:38 GMT

Firewalld 的最终指南可以在 Red Hat 文档网站上找到:

这里也有一些专门用于防火墙的发行版。在这里可以找到一个列表,有些是商业产品,有些是社区支持的。

对于防火墙来说,还有其他非 Linux 操作系统,比如 NetBSD https://www.netbsd.org/ 。Linux 和 Unix 风格的操作系统也可以在商业设备中找到。

TCP 包装

最后,保护主机安全的另一种方法是使用 TCP 包装器。如果您的网络服务在编译时支持 TCP 包装,您可以使用 TCP 包装来进一步保护您的主机的服务。TCP Wrappers 通过/etc/hosts.allow/etc/hosts.deny文件中的一系列定义来控制对主机上运行的守护进程的访问,而不是对端口的访问。

hosts.allow中的规则优先于hosts.deny中的规则。如果您打算使用 TCP Wrappers,在hosts.deny:中设置以下内容是一个好主意

ALL: ALL

这将设置所有服务的默认拒绝操作,除非在/etc/hosts.allow文件中指定。规则是按照从上到下的顺序读的。

然后,您可以添加网络服务。例如,对于我们的示例网络,我们将通过设置以下内容来允许网络服务:

ALL: localhost ACCEPT
sshd: .example.com EXCEPT .baddomain.com

这些设置将首先允许接受任何本地主机连接。许多服务需要连接到运行在 loopback (localhost)接口上的服务,这些需要被接受。接下来,我们允许example.com网络上的主机连接到我们的 SSH 守护进程。这里我们也明确否认了baddomain.com。有关配置 TCP Wrappers 的更多信息,请参见hosts_optionshosts.allowhosts.man页面。

设置 ppp 连接

在我们的 gateway.example.com 堡垒或网关主机(网络外围路由器/防火墙的别称)上,我们需要设置 PPP 服务。PPP 服务与您的 ISP 建立连接,提供一些身份验证细节,并接收 ISP 提供给您的 IP 地址。这个 IP 地址可以是静态地址,表示它不会改变,也可以是动态地址,表示它是从地址池中提供的,每隔几天就会定期改变。我们将建立一个 PPP xDSL 连接,这是连接到 ISP 的常用方式。

在没有网络管理器的情况下,在 Linux 上建立 xDSL 连接最常见的方法是使用咆哮企鹅 PPPoE 客户端。为此,首先您需要检查是否安装了此客户端。在 CentOS 上,我们将再次使用 nmcli 来创建连接。在 Ubuntu 上,你得安装pppoeconf

让我们简单看看如何使用 CentOS 和 Ubuntu 设置 PPP ADSL 连接。

有了 Ubuntu,你可以使用pppoeconf命令或者在你的/etc/network/interfaces文件中创建一个 PPP 连接。

使用 nmcli 设置 ADSL

我们将了解如何对 PPPOE 连接使用 nmcli 命令。如你所见,这相对容易。首先,你需要发现你的 Linux 服务器通过什么接口连接到你的调制解调器。在本练习中,我们将使用 enp0s8 作为界面。

 sudo nmcli c add type adsl ifname enp0s8 protocol pppoa username ourname password psswd encapsulation llc
Connection 'adsl-enp0s8' (ae7bc50a-c4d5-448a-8331-4a6f41c156c5) successfully added.

根据您的 ISP 连接,这些可能会有所不同。您的 ISP 将提供关于您的封装协议的信息,在我们的例子中,它是 llc(逻辑链路控制)。nmcli 命令现在已经创建了一个名为/etc/network manager/system-connections/ADSL-enp0s 8 的文件。网络管理器现在控制这个连接。我们的adsl-enp0s8文件看起来像清单 7-10 。

 sudo cat /etc/NetworkManager/system-connections/adsl-enp0s8
[connection]
id=adsl-enp0s8
uuid=ae7bc50a-c4d5-448a-8331-4a6f41c156c5
type=adsl
interface-name=enp0s8
permissions=
secondaries=

[adsl]
encapsulation=llc
password=psswd
protocol=pppoa
username=ourname

[ipv4]
dns-search=
method=auto

[ipv6]
dns-search=
method=auto

Listing 7-10.adsl-enp0s3 File on CentOS

该文件分为几个部分,[连接]、[adsl]、[ipv4]和[ipv6]。连接部分描述了接口和类型。adsl 部分包含我们的连接所需的配置细节。ipv4 和 ipv6 是连接信息。如果您的 ISP 提供专用的 IP 地址,您可以修改我们的 nmcli 命令来适应。

在 Ubuntu 上,你需要运行 pppoeconf 命令。

A185439_2_En_7_Fig16_HTML.jpg

图 7-16。

Checking for modem/concentrators

 pppoeconf enp0s8

我们要做的第一件事是搜索设备上的信号,如 7-16 所示。接下来,系统会询问我们是否希望修改 pppoeconf 更改的配置文件。

如图 7-17 所示选择是。

A185439_2_En_7_Fig17_HTML.jpg

图 7-17。

Okay to Modify?

采用图 7-18 中的默认值。

A185439_2_En_7_Fig19_HTML.jpg

图 7-19。

ISP username

A185439_2_En_7_Fig18_HTML.jpg

图 7-18。

Popular options

输入您用来连接到 ISP 的 ISP 用户名。

A185439_2_En_7_Fig20_HTML.jpg

图 7-20。

ISP password

输入您的 ISP 密码。

A185439_2_En_7_Fig21_HTML.jpg

图 7-21。

Say No ot USER PEER DNS

我们将对 DNS 设置说“不”,就像我们对 CentOS 配置所做的那样,因为我们将很快管理它们。在接下来的屏幕中,我们将选择默认值。

A185439_2_En_7_Fig25_HTML.jpg

图 7-25。

Checking DSL connection

A185439_2_En_7_Fig24_HTML.jpg

图 7-24。

Start the DSL connections now?

A185439_2_En_7_Fig23_HTML.jpg

图 7-23。

Start the PPPOE connection at boot.

A185439_2_En_7_Fig22_HTML.jpg

图 7-22。

Clamp MSS at 1452.

一旦你在 Ubuntu 上创建了 PPPoE 设备,你会看到ppp部分已经被添加到了/etc/network/interfaces文件中:

auto dsl-provider
iface dsl-provider inet ppp
pre-up /bin/ip link set enp0s8 up # line maintained by pppoeconf
provider dsl-provider

这里您可以看到,当网络启动时,将创建一个名为 dsl-provider 的接口,用auto dsl-provider表示。接下来,注意设备信息将由provider dsl-provider提供。提供商信息可以在/etc/ppp/peers目录下找到。Ubuntu 在我们的/etc/ppp/peers/dsl-provider文件末尾添加了以下信息:

# Minimalistic default options file for DSL/PPPoE connections
noipdefault
defaultroute
replacedefaultroute
hide-password
#lcp-echo-interval 30
#lcp-echo-failure 4
noauth
persist
#mtu 1492
#persist
#maxfail 0
#holdoff 20
plugin rp-pppoe.so
nic-enp0s8
user "username"

密码信息存储在这里,在/etc/ppp/chap-secrets文件中,内容很像清单 7-10 。

如果不成功,可以通过 Ubuntu 上的plog命令或 CentOS 上的/var/log/messages查看连接日志。

您还应该检查以确保您的默认路由设置为 ISP 的第一跳,并且从正确的接口出去。

 ip route show
default via 10.1.1.1 dev adsl-enp0s8

在上面的例子中,10.1.1.1将是您的 ISP 的第一跳,如果流量的目的地是离开您的网络,它将从 adsl-enp0s8 接口出去。

有关在 Ubuntu 上建立连接的更多信息,请访问:

摘要

在本章中,我们将带您了解网络和 Linux Netfilter 防火墙。我们讨论了基本的网络,如何管理 Linux 网络接口以及如何解决网络问题。本章讲述了如何执行以下操作:

  • 在主机上配置接口。
  • 网络过滤器基础
  • 防火墙和防火墙-cmd
  • 管理 Ubuntu 防火墙的 ufw 工具
  • 用于管理 Linux 防火墙的 iptables 工具
  • 记录、限制和保护您的连接
  • 使用 TCP 包装来保护您的守护进程。
  • 配置 PPP 连接

在下一章,我们将看看如何管理主机上的软件包。在该章中,您将了解如何在您的主机上安装、删除和更新软件。

八、包管理

在第二章中,你安装了你的第一台 Linux 主机。正如您在该章中了解到的,主机可以安装各种不同的应用程序,从最基本的安装到安装了所有应用程序和工具的安装。但是这并不是安装和管理应用程序的终点。一旦你安装了你的主机,你经常需要添加应用程序,升级和修补它们,有时还需要删除它们。本章解释了如何在 CentOS 和 Ubuntu 上执行这些任务。

在 Linux 发行版中,这种应用程序管理称为包管理。这是因为 Linux 主机上可用的大多数应用程序都是以包的形式提供的。软件包使在主机上添加和删除应用程序变得非常容易。软件包通常包含安装应用程序所需的所有二进制文件、配置文件和其他支持材料。该软件包还知道这些文件需要安装在哪里,通常还知道是否需要安装其他软件包来满足应用程序的任何先决条件。做个比较,Linux 包很像 Windows 安装 msi 或安装可执行文件。

软件包通常由称为软件包管理器的应用程序控制中心安装。大多数 Linux 发行版都有一个包管理器,它通常有一组用于管理包的命令行和图形工具,以及一个记录已安装内容的小型数据库。

在本章结束时,你将很好地理解什么是软件包,如何安装和删除它们,如何找到适合你需要的软件包,以及如何从源代码安装软件。我们将使用命令行工具以及 CentOS 和 Ubuntu 发行版可用的图形用户界面(GUI)来演示所有这些任务。

Note

在“软件包管理简介”一节之后,本章又分为几节,分别介绍在 CentOS、Ubuntu 上的安装以及从源代码安装。您只需要阅读与您选择的发行版相关的部分。我们还建议您阅读如何从源代码安装软件。您不会经常需要这样做(我们建议您坚持使用包来管理应用程序),但是知道这一点是很有用的。

包管理简介

不同的发行版有不同的软件打包方式。例如,Fedora、Red Hat Linux 和 CentOS 使用RPM (Red Hat Package Management)包格式,而 Ubuntu 使用 deb(Debian 的缩写,Ubuntu 最初基于的发行版)格式。正如包格式不同一样,您可以用来管理它们的工具也不同。

这些包类型中的每一种都使用不同的工具来安装和管理包。在使用RPM包的系统上,基本的包管理器被称为rpm,而在使用 deb 包的系统上,它被称为dpkg。两者都是非常强大的应用程序,允许您操作系统上的软件包。除了这些基本的包管理器,还有一些应用程序提供额外的功能,比如基于网络的升级、包搜索和 GUI。

Note

拥有不同的包格式和管理器可能看起来很奇怪——毕竟这些都是 Linux 发行版——但是原因主要是历史原因。当最初创建这些发行版时,开发人员没有就软件包系统应该如何工作达成一致,所以他们创建了自己的版本。多年来,对它们的开发一直在继续,现在我们有多种不同的成熟的包系统和格式。当然,如果您只使用一个发行版,那么您只需要学习一种类型的包管理。

尽管所有的 Linux 发行版都可以包含成千上万个包,但广义地说,这些包分为三个主要类别。

  • 应用程序包
  • 库包
  • 开发包

顾名思义,大多数应用程序包都包含应用程序。这些应用程序的范围可以从简单的命令行编辑器到整个 LibreOffice 生产力套件。

Note

LibreOffice 是微软 Office 的开源等价物。它包含一个文字处理器,电子表格程序,演示软件,以及其他工具。它允许您编辑 Microsoft Office 文档,并提供与 Microsoft Office 类似的功能。

库包包含应用程序和操作系统用来提供附加功能的文件。例如,libssl包提供了加密支持。就像你的社区图书图书馆一样,Linux 图书馆是应用程序可以找到他们需要的东西的地方,而不必自己拥有它。因为这样的库经常被多个应用程序使用,所以将它们分发到它们自己的包中是有意义的,而不是在每个应用程序中包含每个库的副本。如果更新了库包,所有使用该库的应用程序现在将自动使用更新的版本。这些包的名字往往以lib开头。

开发包包含从源代码编译软件所需的源代码和头文件。这些包在 Ubuntu 上通常以-dev结尾,在 Red Hat 上以-devel结尾。大多数库包都有一个附带的开发包,允许使用这些库进行软件开发。通常,除非您正在开发应用程序,否则您不需要安装这些包。但是一些应用程序确实使用它们,如果您选择从源代码编译应用程序并以这种方式安装它们,您通常需要开发包来完成这项工作。

因为 CentOS 和 Ubuntu 使用的包管理工具完全不同,我们将在各自的章节中介绍它们。我们将首先介绍 CentOS 上的软件包管理,然后介绍 Ubuntu。

What’s a Package?

包旨在简化应用程序的管理。它们通常由应用程序的源代码构造而成,并且具有告诉您的发行版将应用程序的二进制文件、文件和配置放在哪里的逻辑。我们将使用两种类型的包,RPM s 和 deb 文件。这两种包类型都是包含其他文件的归档。那么这些包裹里面是什么呢?包包含数据、元数据,有时还包含控制文件。数据是将要安装的文件。控制文件包含有关软件包的描述性信息、用户交互脚本以及管理自动化安装前或安装后任务的脚本。

CentOS 上的包管理

在最基本的层面上,在基于 Red Hat 的系统上管理应用程序的方式是通过 Red Hat 包管理工具,或rpm。它用于 Red Hat Enterprise Linux、CentOS、Mandriva 和 Fedora 项目等发行版。rpm工具本身是为在本地系统上安装、操作、查询和删除包而设计的。

Tip

您可以通过后缀.rpm来识别 RPM 包。每个 RPM 包都是使用包含在一个spec文件中的信息构建的。spec文件包含关于每个包中的内容的元数据,并描述了应该如何在您的系统上安装这个包。在这一章的后面,我们将会稍微讨论一下spec文件以及如何构建你自己的包。

rpm工具提供了基本的包管理任务,如安装和删除包,但它不处理各种其他任务,如从在线存储库中检索依赖包(即,您需要在特定包安装的同时或之前安装的包)或包更新的定期自动化。

Note

长期使用 Linux 之后,我们可以证明,由于智能包管理,管理 Linux 系统现在变得更加容易了。在过去,安装一个软件包或更新一个应用程序可能需要几个小时来寻找依赖项,这些软件包应该在您安装和使用另一个软件包之前就在您的系统上了。在这些经理到来之前,您必须从源代码构建所有的应用程序,并处理所有出现的冲突。如今它简单多了。但是,当然,如果你真的想这么做,你总是可以从源代码开始构建——Linux 是如此强大,它给了你这样的选择。我们将在本章的最后一节讨论从源代码构建。

为了提供一些rpm所缺乏的功能,大多数 Red Hat 衍生发行版都有一些额外的工具。这些工具中的大多数都是通过从存储库(存储包的地方;大多数发行版都有许多在线可用的存储库)并展示它们以供安装。这些工具包括 Red Hat 的 Red Hat Network (RHN,这是 Red Hat 的商业更新服务),杜克大学的 Yellowdog Updater Modified or yum,Mandriva 的urpmi,以及 Fedora 中名为dnf的新包管理器。在这一节中,我们将重点介绍作为 CentOS 的一部分提供的工具,但其中包含的信息也将有助于其他基于 Red Hat 的发行版,如 Red Hat Enterprise Linux (RHEL)、Fedora 和 Mandriva。

在接下来的小节中,我们将带您通过 GUI 界面和命令行完成包的安装,以及如何使用rpm工具本身来管理单个的包。

入门指南

在基于 Red Hat 的主机上,如 CentOS、Scientific Linux 和 Fedora,通过桌面管理软件包非常简单,不需要订阅。

然而,对于 RHEL 主机,您需要订阅才能获得软件更新和 Red Hat 的服务。RHEL 7 的订阅服务叫做红帽订阅管理。你可以在找到如何加入的详细信息

登录到你的桌面后,你会看到通常的 Gnome CentOS 桌面。我们将向您展示如何使用名为应用安装程序的工具。您可以使用应用程序安装程序来列出、搜索、安装和删除您的软件包。

您可以通过选择应用程序➤系统实用程序➤应用程序安装程序找到这两个软件包管理工具。或者,您可以将鼠标移动到屏幕的左上角,调出搜索窗口来搜索应用程序。这就是我们在图 8-1 中的做法。

A185439_2_En_8_Fig1_HTML.jpg

图 8-1。

Finding Application Installer on CentOS

这个工具非常容易使用,我们将在下一节中演示。

应用程序安装程序

应用程序安装程序有一个简单的界面,可能类似于您可能见过的许多其他应用程序安装程序界面。在图 8-2 中,您可以看到顶部有以下视图,“全部”、“已安装”和“更新”

A185439_2_En_8_Fig2_HTML.jpg

图 8-2。

Installation application

然后你就有了火狐浏览器和编辑推荐的常用软件包。下一部分是类别。在类别中,您可以找到您可能喜欢的相似应用程序组。我们将安装“虚拟机管理器”软件,该软件为 KVM、Xen 或 LXC 提供管理界面。

在图 8-3 中,我们选择系统类别。在这里,我们会发现 Virtual Machine Manager 是一个特色应用程序。如果它不再出现,您可以在系统页面的其他类别下找到它。

A185439_2_En_8_Fig3_HTML.jpg

图 8-3。

Select System

在图 8-4 中你会注意到的一件事是盒子应用程序。您会注意到,我们的系统中已经“安装”了。我们将选择 Virtual Machine Manager 进行安装。

A185439_2_En_8_Fig4_HTML.jpg

图 8-4。

Selecting Virtual Machine Manager

选择后,我们将看到关于该包的信息屏幕。有一个网站的链接,一些截图,甚至还有程序的评级。

如图 8-5 所示,我们所要做的就是点击安装按钮进行安装。如果软件包或您的系统有问题,安装程序将处于等待状态,如图 8-6 所示。

A185439_2_En_8_Fig6_HTML.jpg

图 8-6。

Pending installation

A185439_2_En_8_Fig5_HTML.jpg

图 8-5。

Virtual Machine Manager information screen

您的安装可能停滞在挂起状态的一些原因如下:

  • 您的电脑在连接到互联网时可能会出现问题。这种情况的一个线索是信息屏幕没有显示屏幕截图。现在,您应该能够检查您的界面,使其正常工作;如果没有,返回第七章并再次查看ip命令。
  • 软件包有问题,无法安装。CentOS 论坛通常可以帮助确认这一点。
  • 安装软件包的存储库有问题。我们将很快讨论更多关于存储库的内容。

理想情况下,您将看到以下屏幕。

A185439_2_En_8_Fig7_HTML.jpg

图 8-7。

Installing

安装完成后,您将会看到图 8-8 中的屏幕。

A185439_2_En_8_Fig8_HTML.jpg

图 8-8。

Installation complete, Remove?

如果您希望删除应用程序,只需点击图 8-8 中的删除按钮。您将被要求输入密码。

这就是安装和删除软件包所需要做的全部工作。要查看所有已安装的软件包,我们返回初始屏幕并选择顶部的 installed 按钮(参见图 8-9 )。

A185439_2_En_8_Fig9_HTML.jpg

图 8-9。

Installed packages

显然,您可以通过单击 remove 按钮从该屏幕中删除软件包。您也可以通过点击它们来查看已安装软件包的更多详细信息,并且您也可以从那里启动它们。当您打开此屏幕时,您会注意到您无法卸载系统软件包。如果你需要删除其中的任何一个,你必须通过YUM包管理器来完成(稍后会有更多)。

通过转到“更新”选项卡,您可以看到您的系统当前需要的任何更新。在图 8-10 中,我们可以看到有三个更新在等着我们。我们可以通过单击左上角的刷新按钮来检查这是否是最新的列表。

A185439_2_En_8_Fig10_HTML.jpg

图 8-10。

Refresh Updates

因为我们有一个操作系统更新,我们被要求重新启动和安装;通常我们可以不重启就安装更新。

A185439_2_En_8_Fig11_HTML.jpg

图 8-11。

Restart & Install Updates message

单击重启和安装按钮后,在重启和安装前的 30 秒内,我们可以选择取消或继续。在主机安装了更新之后,我们会注意到我们安装了一个新的内核,如图 8-12 所示。

A185439_2_En_8_Fig12_HTML.jpg

图 8-12。

New kernel has been installed

这些系统更新已经安装了一个新的内核,你的 grub 配置已经更新了一个新的菜单条目,我们在第六章已经讨论过了。您现在可以看到,我们可以选择最新的(新的系统默认值)或旧的内核版本。如果你在新内核上遇到问题,你可以重启并选择旧版本,看看问题是否仍然存在。

现在您的系统是最新的。在图 8-13 的底部,您将看到“上次检查:”时间。当你点击如图 8-10 所示的刷新按钮时,这个更新。您也可以选择从命令行更新您的系统。在下一节中,我们将看到如何在 CentOS 上实现这一点。

A185439_2_En_8_Fig13_HTML.jpg

图 8-13。

System up to date

Yellowdog 更新程序已修改

对于基于 Red Hat 的发行版,用于安装、升级或更新系统的最常用工具之一是基于命令行的工具,称为 Yellowdog Updater Modified (YUM)。YUM提供在远程存储库中搜索和查找系统可用软件的功能。如前所述,存储库是由发行版的供应商提供给你的发行版的软件包的集合,并由利益集团、大学和 ISP(互联网服务提供商)进行“镜像”。

Note

YUM可用于 Red Hat Enterprise Linux 和 CentOS,以及其他 Red Hat 衍生的发行版。这通常是通过命令行更新和维护这些发行版上的包的默认方法。

YUM 还能够通过获取安装任何单个包所需的包来解决依赖性问题。它使用一个数据库来查询您的主机上安装了哪些软件包,然后将其与存储库提供的软件包列表进行比较。如果您需要一个或多个额外的包来满足任何依赖关系,那么这些包也会被下载和安装。

YUM 使用起来很简单。在命令行上,您只需输入yum和一系列可能的操作之一,例如 install、remove 或 list,然后输入您希望对其执行操作的软件包的名称,例如:

$ sudo yum install nmap

Note

您需要 root 权限来运行yum命令,要么作为 root 用户登录,要么使用sudo命令。我们建议使用sudo命令,正如我们在第五章中演示的那样。

表 8-1 列出了 YUM 提供的主要选项。

表 8-1。

Options Available with YUM

| [计]选项 | 描述 | | --- | --- | | 搜索 | 允许您搜索可供下载的软件包。 | | 目录 | 列出所有可供下载的软件包。 | | 安装 | 安装一个或多个软件包。 | | 检查-更新 | 列出可供更新的软件包。 | | 更新 | 更新指定的软件包或下载并安装任何可用的更新。 | | 移动 | 从主机中删除一个或多个包。 | | 信息 | 提供关于包的包信息。 | | 提供 | 告诉你一个特定的文件或特性属于哪个包。 | | 列表更新 | 列出所有只提供更新的软件包。 | | 可用列表 | 仅列出所有可用的软件包。 | | deplist | 列出包依赖项。 | | 全部清洗 | 清理不再需要的下载包文件。 | | 历史 | 显示了在此系统上执行的 YUM 命令的历史记录。 | | mssnapshot | 此子命令和相关子命令可以在发布 YUM 更新或安装之前管理 LVM 快照的创建和删除。 | | 重新喷水 | 查看在您的系统上配置的 YUM 存储库。您也可以通过`–v`或使用`repoinfo`命令来获取更多信息。 |

接下来,我们将看看每个动作以及如何使用它。

用 YUM 安装软件包

让我们从YUM开始,试着安装一个包。在正常情况下,我们会首先搜索包以确保它是可用的。我们选择安装nmap包(如前所述,它是一个有用的网络映射和扫描工具)。我们将使用search选项来查看该包是否可用,如清单 8-1 所示。

$ sudo yum search nmap
Listing 8-1.Searching with YUM

在清单 8-1 中,我们发出了带有search选项的yum命令,然后指定了我们希望搜索的包的名称。这将产生一个名称中有nmap的可用包列表。yum命令不够智能,无法准确知道您在寻找什么,所以它会为您提供名称或描述中带有nmap的任何内容。

如果您不确定完整的名称,也可以搜索包名称的一部分。因此,举例来说,您可以输入yum search map 并获得所有描述或名称中带有模式映射的包的列表。

Tip

您可以通过使用我们在第四章中展示的grep命令来优化软件包列表。例如,您可以键入yum search python | grep boto,这将通过搜索术语“boto”来细化与python相关的包列表,然后显示结果包列表。

您还可以使用 yum list 查看存储库中所有可用的包,并且您可能会发现 yum groups list 对于查看包的分组很有用(yum help groups将列出用于管理组的其他可能的子命令)。组是为方便起见而提供特定应用程序的单个包的集合。因此,例如,安装 KDE 等离子工作区组将提供运行您的 KDE 桌面的一切,而安装基本 Web 服务器组将提供 Apache web 服务器和相关的包。

有了这些信息,为了安装nmap包,我们运行清单 8-2 中的命令。

$ sudo yum install nmap

Listing 8-2.Installing the nmap Package

将下载并安装一个名为nmap的包。yum install 命令的输出中显示了一些信息。

Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
 * base: centos.mirror.crucial.com.au
 * extras: centos.mirror.crucial.com.au
 * updates: centos.mirror.crucial.com.au
Resolving Dependencies
--> Running transaction check
---> Package nmap.x86_64 2:6.40-7.el7 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

=============================================================
 Package      Arch        Version       Repository       Size
=============================================================
Installing:
 nmap        x86_64    2:6.40-7.el7       base          4.0 M

Transaction Summary
=============================================================
Install  1 Package

Total download size: 4.0 M
Installed size: 16 M
Is this ok [y/d/N]:

第一行显示了 yum 加载的任何插件,以提供额外的特性或功能。然后我们可以看到这个安装命令使用的存储库* base:, * extra:* updates:。接下来我们会看到依赖项和事务检查,然后是我们将要安装的软件包列表,在本例中是一个软件包。我们有更多关于我们正在安装的包的细节,包括所有正在安装的包的名称、架构(x86_64)、版本(2:6.40-7.el7)、我们正在下载这个包的 yum 存储库(base)以及下载包的大小(4.0M)。然后显示事务摘要,我们有机会继续安装(y)、仅下载软件包(d)或不安装软件包(N)。如果您想在不提示确认的情况下安装软件包和依赖项,可以发出

$ sudo yum install –y nmap

这将对任何安装问题回答“是”(-y),这在您通过脚本安装软件包时非常有用。

您也可以使用 groups install 命令,如下所示:

$ sudo yum groups install 'Basic Web Server'

这将在 group Basic Web 服务器中安装所有软件包。如果需要任何依赖项,它们也会被下载,并且会询问您是否要安装它们(除非您指定了groups install –y)。如果您没有安装所有必需的依赖项,那么您将无法安装您需要的软件包。

更新您的存储库

YUM 存储库缓存由几个文件组成。这些是小型 db 文件,包含文件列表和包列表之类的内容。随着时间的推移,随着新的包被添加到存储库中并在存储库中进行更新,您的 YUM 存储库缓存数据将会过时。

要重建或刷新缓存,而不检查主机是否有任何可用的更新包,可以发出以下命令:

$ sudo yum makecache

这将从存储库中提取缓存文件。要检查您的主机是否有任何更新,您可以发出清单 8-3 中的命令。

$ sudo yum check-update
Listing 8-3.Checking for Updated Packages

check-update子命令将下载最新的 YUM 存储库缓存数据(如果它已经过时),然后使用它来找出您的系统上哪些包有可用的更新。这将显示有可用更新的软件包列表。要安装这些更新,您可以发出

$ sudo yum update

您还可以更新一个或多个单独的包,如清单 8-4 所示。

$ sudo yum update nmap
$ sudo yum update nmap mutt
Listing 8-4.Updating Packages with YUM

Note

您不应该使用 yum 在操作系统的主要版本之间进行升级(例如,从 6.5 升级到 7.1)。从技术上讲,没有什么可以阻止您将 YUM 存储库指向最新版本并运行 yum update,但是这将产生非常不可预测的结果,并且很可能导致系统丢失。不过,您可以使用 yum 在次要版本之间进行升级(例如,从 7.1 升级到 7.2)。

使用 YUM 删除包

还可以用 YUM 删除包,如清单 8-5 所示。

$ sudo yum remove nmap
Listing 8-5.Removing Packages with YUM

清单 8-5 中使用的remove选项将从你的系统中移除所有与该包相关的文件。您可以使用这个命令的别名erase,它会做同样的事情。任何与原始包版本不同的配置文件都将被保存,扩展名为< filename>.conf .rpmsave

执行其他 YUM 任务

您可以使用yum命令做许多其他事情。让我们简单地看一下其他一些命令。

以下命令提供了有关您正在查询的包的信息,例如它的大小、版本和安装状态。包的描述;和其他有用的信息。

$ sudo yum info kernel

如果您想找到哪个包提供了一个特定的文件,您可以使用下面的。

$ sudo yum provides /bin/bash
bash-4.2.46-19.el7.x86_64 : The GNU Bourne Again shell
Repo        : base
Matched from:
Filename    : /bin/bash

前面的命令告诉您/bin/bash二进制文件是由bash包提供的,并描述了这个包的版本和细节。

要列出由文件路径提供的包,还可以使用以下命令:

$ sudo yum provides */sftp

这将列出所有提供sftp文件的包。这将列出 openssh-client 包以及其他包。

查看主机可用更新列表的另一种方法是发出以下命令:

$ sudo yum list updates

如果您的主机是最新的,此命令应该不会产生任何结果。

接下来,通过发出以下命令,您可以列出可以安装在您的主机上的所有软件包:

$ sudo yum list available

前面的命令列出了尚未从存储库中为您的主机安装的所有可用软件包。

要获取主机的安全更新列表,请使用以下命令:

$ sudo yum --security check-update

这将提供等待安装到您的主机上的当前安全软件包的列表。要安装,只需安装那些安全补丁。

$ sudo yum --security update

您也可以使用以下命令从特定的 YUM 存储库中安装软件包

$ sudo yum install --enablerepo=myrepo my-package

在前面的命令中,我们告诉 YUM 从我们的myrepo YUM 存储库中安装我们的my-package。如果存储库被禁用,这意味着我们通常不从该存储库中获取包,我们可以在执行该命令时启用该存储库。除非指定,否则后续的 yum 命令不会从该存储库中提取。不久将有更多关于 YUM 存储库的内容。

最后,您可以清理您的缓存目录(YUM 临时存储您已经下载的包的地方)。如果需要回收一些磁盘空间临时磁盘空间,可以清除缓存。

$ sudo yum clean all

前面的命令删除了包含在/var/cache/yum cache目录中的缓存包和头文件。

配置 YUM

YUM 应用程序可以通过多种方式进行配置。YUM 将其配置文件存储在/etc/yum.conf/etc/yum.repos.d/下,并将状态文件存储在目录/var/lib/yum/var/cache/yum下。

Note

状态文件告诉YUM你的主机上已经安装了什么,还保存了下载包的缓存版本。注意不要删除这些目录,因为您很容易损坏您的软件包数据库。

您通常不需要更改默认的/etc/yum.conf。例如,当您希望更改配置时,可以添加一个代理服务器,YUM 应该在下载包时使用它。默认设置通常适合大多数环境。YUM 可用配置设置的完整列表可通过yum.conf手册页获得。

$ man yum.conf

虽然您很少改变 YUM 的配置方式,但是您可能希望向 YUM 添加额外的存储库,以便从中下载额外的包。定义 YUM 可用的存储库的文件包含在/etc/yum.repo.d目录中。让我们看一个通用的存储库文件:

$ cat /etc/yum.repo.d/myrepo.repo
[myrepo]
name=myrepo
baseurl=http://myrepo.mydomain.com/pub/linux/releases/$releasever/$basearch/os/enabled=1
gpgcheck=1
gpgkey=http://myrepo.mydomain.com/linux/RPM-GPG-KEY-linux

表 8-2 中解释了这些选项。

表 8-2。

Basic Options for Adding a yum Repository

| [计]选项 | 描述 | | --- | --- | | `[repo-id]` | `repo_id`是存储库的唯一名称。 | | `name` | `name`是对存储库的描述。 | | `baseurl` | `baseurl`是存储库的 URL。这通常是一个 HTTP URL,就像您在 web 浏览器中使用的一样。它也可以是一个`ftp://`或`file:///`资源。 | | `enabled` | 您可以通过将 0 指定为 disabled 并将`1`指定为 enabled 来启用或禁用程序包。 | | `gpgcheck` | 这个选项告诉`yum`检查用于“签名”包的 GPG 密钥,这样你就可以确保包没有被篡改。指定`0`关闭检查,`1`表示检查开启。 | | `gpgkey` | 这是`yum`应该找到存储库的 GPG 键的 URL。它可以是`http://`、`ftp://`或`file:///`资源。 |

在清单 8-6 中,我们为 CentOS 发行版定义了一个新的存储库,用于下载源 rpm 文件。如果我们希望重新构建我们自己的 rpm 包,将使用源 rpm 文件。这些源文件是 CentOS 维护人员用来生成我们在包中使用的普通二进制 rpm 的文件。

[source]
name=CentOS-releasever - Sources
baseurl=http://vault.centos.org/centos/$releasever/os/Source/
gpgcheck=1
enabled=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Listing 8-6.Adding a CentOS Repository in a YUM Repo File

您可以看到,在 baseurl 选项中,我们指定了一个名为$releasever的变量。在 Red Hat 和 CentOS 上,该变量默认为redhat-release包的版本。

$ cat /etc/redhat-release
CentOS Linux release 7.2.1511 (Core)

在这种情况下,$releasever将等于7.2.1511。这个发布文件由centos-release-7-2.1511.el7.centos.2.10.x86_64包放在系统中。

您还会看到我们用于源的 baseurl 指向了vault.centos.org主机。在这里,您可以找到 CentOS 的早期历史版本以及用于构建您自己的 rpm 的源文件。在 baseurl 选项中可以列出多个 baseurl。

baseurl=http://myrepo1.org/centos/...
        http://myrepo2.org/centos/...

URL 列表也可以通过mirrorlist=选项而不是baseurl=从网络服务器中检索。mirrorlist.txt文件只包含以下内容:

mirrorlist=http://myrepo.org/mirrorlist.txt
http://myrepo1.org/centos/...
http://myrepo2.org/centos/...

加载了 yum 的fastestmirror插件后,YUM 将从列表中找到响应最快的镜像,并在运行时使用它。

YUM 存储库配置中可能存在其他变量:

  • $arch–您系统的架构(由 yum 检测)
  • $basearch–您系统的基础架构(例如,i686 变成 i386)
  • $uuid–为您的系统生成并存储在/var/lib/yum/uuid中的唯一标识符
  • $infra–目前未使用,但您可以在您的镜像列表 URL ( &repo=os&infra=$infra)中看到它。

当变量如下使用时:

http://myrepo.org/centos/$releasever/$arch/myapp

这可以推断为

http://myrepo.org/centos/7-2.1511/x64_86/myapp

有关 YUM 使用的配置选项的完整列表,请参见man yum.conf。当您创建一个新的 Yum 存储库时,您可以使用makecache命令来刷新您的存储库缓存,并向其中添加新的存储库。

$ sudo yum makecache

在现代 Fedora 发行版(从版本 22 开始)和即将发布的 Red Hat 和 CentOS 发行版中,您将更新软件包管理器来处理称为 DNF 的 rpm。现在让我们来看看。

DNF——或者说奢华的美味

DNF 最初是百胜软件包管理器的一个分支,它是百胜的替代版本,而不仅仅是一个新版本。它被设计成保持(大致)与 YUM 当前版本的 CLI(命令行界面)兼容性。因此,您应该已经熟悉了 DNF 软件包管理器的主要命令。

虽然最终用户不应该注意到百胜和 DNF 之间有太多的区别,但实际上有很多区别。不同之处主要在于如何重写代码,以使开发人员更容易使用可预测和记录的 API(应用程序编程接口)进行维护和扩展。在 YUM 不兼容的地方,它也兼容 Python3。此外,包依赖算法已经完全重写。DNF 将会是一个受欢迎的,更快的,即使不被注意到的进步。

类似下面的命令执行与等效的 YUM 命令类似的功能。

$ dnf search nmap
$ dnf info nmap
$ dnf install  nmap
$ dnf remove nmap

这些命令都具有与 YUM 相似的语法。有关可用命令的完整列表,请参见 DNF 文档,网址为

DNF 的存储库文件也将类似于当前的 YUM 存储库文件,并且来自同一个位置,/etc/yum.repos.d/*.repo

[fedora]
name=Fedora $releasever - $basearch
failovermethod=priority
#baseurl=http://download.fedoraproject.org/pub/fedora/linux/releases/$releasever/Everything/$basearch/os/
metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch
enabled=1
metadata_expire=28d
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch
skip_if_unavailable=False

正如您所看到的,在这里我们如何描述一个与 DNF 一起使用的存储库并没有真正的区别。有关 DNF 使用的配置选项的完整列表,请参见手册dnf.conf

红帽包管理

我们刚刚看了yumdnf命令以及如何使用它们来管理包。YUM 或 DNF 的唯一目的是管理您系统上实际的 Red Hat Package Management (RPM)软件包。每个 RPM 包也可以通过rpm命令检查、安装和移除。rpm命令是操纵包含软件包的RPM文件的基本工具。虽然您可能不经常需要直接使用rpm命令,但了解它的工作原理是很重要的。

有许多选项可以传递给rpm命令。要获得这些命令的完整列表,我们建议您阅读其man页面。

$ man rpm

rpm手册页上,您可以看到rpm工具可以执行三个基本功能。

  1. 查询包
  2. 安装、升级和删除软件包
  3. 杂项功能

让我们更详细地看看其中的一些选项。表 8-3 显示了你可以传递给rpm的主要选项以及它们的作用。每个选项都有许多您添加的标志。

表 8-3。

The Major rpm Options and Flags

| 选项和标志 | 描述 | | --- | --- | | `-q | --query` | 允许您查询 RPM 数据库,并查找有关主机上已安装的软件包的信息 | | `-i | --install` | 在您的主机上安装本地软件包 | | `-e | --erase` | 从主机上删除软件包 | | `-U | --upgrade` | 升级现有软件包,或者在您的主机上安装软件包(如果尚未安装) | | `-F | --freshen` | 仅当安装了早期版本时才升级软件包 | | `-V | --verify` | 验证已安装的软件包 |

查询包

首先,我们将使用-q 或- query 选项来查询安装在您的主机上的包。为了找出您的主机上安装了什么,rpm工具查询存储在数据库中的数据。当软件包被安装和删除时,这个数据库被更新。

Note

RPM 数据库只知道您的主机的当前状态;它不知道昨天删除了什么,也不知道软件包的以前版本是什么。当您使用yumdnf时会记录该信息,但当您使用rpm时不会。

现在让我们使用rpm来检查 Linux 操作系统的核心——内核。假设有人问你,“我们安装了什么内核?”要找到答案,您可以使用rpm命令,如清单 8-7 所示。

Note

您也可以通过发出uname –r命令找到您当前使用的内核。

# rpm --query kernel
kernel-3.10.0-327.el7.x86_64
kernel-3.10.0-327.18.2.el7.x86_64
Listing 8-7.Querying the Installed Kernel Version

这里我们运行了带有--query标志的rpm命令,并指定了我们想要查询的包的名称,在我们的例子中是内核。您可以看到,这产生了一个已安装内核的列表。

Note

您会在清单 8-7 中看到不止一个内核,因为您可以安装不止一个内核。这并不意味着你有多个内核同时运行;相反,你有多个潜在的内核可以运行。

安装的每个内核末尾的数字是不同的版本。一个是 3.10.0-327.el7.x86_64,一个是 3.10.0-327.18.2.el7.x86_64。两者都与 Red Hat Enterprise Linux 7 系统(. el7)兼容。

Tip

我们在第六章中讨论了如何选择想要启动的内核。

如果你不知道你要找的包的名字怎么办?或者,如果您想查看所有已安装的软件包,该怎么办?在这些情况下,您可以使用清单 8-8 中的命令。

# rpm --query --all
Listing 8-8.Querying All Packages

该命令使用- query标志和- all flag标志,表示您正在查询所有软件包,它列出了所有已安装的软件包。

正如您将看到的,这个列表可能会很长。假设您只想查找任何名称包含 vim 的包。然后,您可以通过grep工具来处理输出,该工具会在输出中搜索字符串 vim,如清单 8-9 所示。

# rpm --query --all | grep vim
vim-minimal-7.4.160-1.el7.x86_64
vim-filesystem-7.4.160-1.el7.x86_64
vim-enhanced-7.4.160-1.el7.x86_64
vim-common-7.4.160-1.el7.x86_64
Listing 8-9.Querying All Using Piping and grep

Note

我们将在第四章中介绍grep命令和管道。

清单 8-9 显示,通过管道将查询输出传递给grep命令已经将列表从数千个包减少到四个名称中带有字符串vim的包(空列表表示没有安装名称中带有该字符串的包)。

让我们了解更多关于内核包的信息。在清单 8-10 中,我们结合使用查询选项和--info选项来查找关于我们安装的一个内核的更多信息。

# sudo rpm --query --info kernel-3.10.0-327.18.2.el7.x86_64
Name        : kernel
Version     : 3.10.0
Release     : 327.18.2.el7
Architecture: x86_64
Install Date: Thu 26 May 2016 17:43:40 EDT
Group       : System Environment/Kernel
Size        : 142681799
License     : GPLv2
Signature   : RSA/SHA256, Thu 12 May 2016 19:41:18 EDT, Key ID 24c6a8a7f4a80eb5
Source RPM  : kernel-3.10.0-327.18.2.el7.src.rpm
Build Date  : Thu 12 May 2016 07:51:03 EDT
Build Host  : kbuilder.dev.centos.org
Relocations : (not relocatable)
URL         : http://www.kernel.org/
Summary     : The Linux kernel
Description :
The kernel package

contains the Linux kernel (vmlinuz), the core of any
Linux operating system.  The kernel handles the basic functions
of the operating system: memory allocation, process allocation, device
input and output, etc.
Listing 8-10.Getting Information About Packages

清单 8-10 产生了大量的信息。其中一些可能最初对你来说意义不大,但让我们来看看一些数据。您可以看到版本和发布号,以及它的构建和安装日期。您还可以看到该软件包属于哪个组,因为主机上的每个软件包都属于一组相似的软件包。当你在第二章安装你的主机并选择你想要的软件包作为这个过程的一部分时,你就看到了这一点。

此外,您可以看到发布软件包的许可证(在本例中是 GPLv2 许可证)、软件包的大小,最重要的是,软件包的描述和摘要以及一些关于软件包的更多信息的链接。

有时你会想知道哪个软件包安装了一个特定的文件或命令。您也可以使用rpm工具来查询这些信息。例如,我们在主机上安装了/bin/bash命令,我们可以通过使用清单 8-11 中所示的rpm来找出是什么安装了这个命令。

# rpm  --query  --whatprovides /bin/bash
bash-4.2.46-19.el7.x86_64
Listing 8-11.Using query and whatprovides

清单 8-11 告诉我们包bash负责文件/bin/bash,它也通知我们安装的bash包的版本号(你也可以用更短的格式$ rpm -qf /bin/bash)。

所以现在我们知道bash提供了那个文件,但是我们系统上还有哪些文件属于bash包呢?有了来自--whatprovides的信息,我们可以通过使用清单 8-12 所示的--query --list选项来查看还有哪些文件属于bash包。

# rpm  --query  --list bash
/etc/skel/.bash_logout
/etc/skel/.bash_profile
/etc/skel/.bashrc
/usr/bin/alias
<snip> ...
Listing 8-12.Using query and list

清单 8-12 显示了出现在bash包中的所有文件的删减列表。我们还可以列出关于软件包的其他有用信息。例如,rpm –qc <package>命令会显示与包相关的配置文件,rpm –qd会列出文档文件。

软件包通常会在安装或卸载之前或之后运行安装前和安装后以及卸载脚本。这些可能会创建用户,配置和启动服务,或清理自己。您可以使用以下内容查看此类脚本:

# rpm –q --scripts bash
postinstall scriptlet (using <lua>):
nl        = '\n'
sh        = '/bin/sh'..nl
bash      = '/bin/bash'..nl
f = io.open('/etc/shells', 'a+')
if f then
  local shells = nl..f:read('*all')..nl
  if not shells:find(nl..sh) then f:write(sh) end
  if not shells:find(nl..bash) then f:write(bash) end
  f:close()
end

在上面的 Lua 脚本(Lua 是一种编程语言)中,我们可以看到在安装 bash 程序后执行的脚本。在安装 bash 包之后,该脚本执行对/etc/shells文件的必要添加。

正如你现在可能看到的,我们可以用yumdnf完成类似的任务。让我们继续用rpm安装软件包。

使用 RPM 安装软件包和删除软件包

rpm工具也可以用来安装或升级软件包;但是,不建议这样做。这与百胜的工作方式相冲突。通过 RPM 安装通常也很烦人。要安装软件包,您需要下载所需的RPM文件和任何依赖项,然后使用rpm工具将其安装到您的主机上。

移除包会出现同样的问题,也应该避免。

从源代码构建 RPM 包

为什么需要从源代码构建一个包?嗯,有时你需要一个源代码的补丁,或者你想构建一个更新版本的包。如果是这种情况,您可以采取几种方法。

许多包都有所谓的上游RPM包,上游RPM包包含的应用程序版本比发行版附带的版本更新。它们通常是由应用程序的开发人员或应用程序开发社区的其他成员构建的。它们会有你可能需要的更新的代码补丁或更新的特性,但是它们会更尖锐,可能包含错误和问题。这些上游 ?? 通常可以在应用程序的网站上找到。

Caution

上游RPM s 文件任何人都可以建。它们可能不稳定、不安全或没有定期维护。你应该小心使用它们。

第二种方法是下载需要更新的应用程序的源代码,并构建自己的 rpm。网上有一些优秀的参考资料可以帮助你创建自己的RPM文件:

我们将快速演示构建 RPM 包的过程。这个包将简单地在我们的系统上放置一个脚本。为此,我们需要安装 rpm-build 和 rpmdevtools 包。如果你打算编译一个程序,你也需要合适的开发包。

$ sudo yum install rpm-build rpmdevtools

接下来,我们需要创建一些rpm build命令所需的基本目录。为此,我们使用 rpmdevtools 包中的一个命令。

$ rpmdev-setuptree
$ ls -l rpmbuild/
total 20
drwxrwxr-x. 2 vagrant vagrant 4096 Jul 24 09:46 BUILD
drwxrwxr-x. 2 vagrant vagrant 4096 Jul 24 09:46 RPMS
drwxrwxr-x. 2 vagrant vagrant 4096 Jul 24 09:46 SOURCES
drwxrwxr-x. 2 vagrant vagrant 4096 Jul 24 09:46 SPECS
drwxrwxr-x. 2 vagrant vagrant 4096 Jul 24 09:46 SRPMS

当构建 RPM 包时,我们至少需要上面目录中的规范和源代码。构建是我们构建包的地方。构建完成后,我们的包将放在 RPMS。SOURCES 是我们放置用于构建的源文件的地方。SPECS 是放置我们的 spec 文件的地方。SRPMS 是我们的 RPM 源文件所在的地方。我们将进入我们的rpmbuild目录。

我们在名为simple_echo.tar.gz的 SOURCES 目录中放置了一个压缩的 tarball(一个压缩的归档文件)。它包含一个名为simple_echo.sh的文件,我们打算将它安装到/usr/local/bin directory中。

在我们继续之前,我们需要创建我们的simple_echo.spec文件,并把它放在 SPECS 目录中。在我们的 rpmdevtools 包中,我们使用如下的rpmdev-newspec命令:

$ rpmdev-newspec simple_echo && mv simple_echo.spec SPECS/
simple_echo.spec created; type minimal, rpm version >= 4.11.

这已经创建了我们的规范文件,并将其移动到我们的规范目录中。我们现在可以使用我们的 vi 编辑器来编辑文件。spec 文件包含关于我们的包的细节——版本号、许可证等等。它还有几个宏,rpmbuild 用它们来帮助创建包。它们以%<macro>开始。

我们的规范文件将如下所示:

Name:           simple_echo
Version:        1.0
Release:        1%{?dist}
Summary:        Echoes your input
License:        GPLv3+
URL:            http://www.example.com/simple_echo
Source0:        %{name}-%{version}.tar.gz

BuildRequires:  bash
Requires:       bash

%description
This program echoes the first parameter entered.

%prep
%setup -q

%install
rm -rf $RPM_BUILD_ROOT
mkdir -p $RPM_BUILD_ROOT/usr/local/bin
install simple_echo.sh -m0755 $RPM_BUILD_ROOT/usr/local/bin/simple_echo.sh

%files
%doc
%attr(0755,root,root)/usr/local/bin/simple_echo.sh

%changelog
* Sun Jul 24 2016 JSmith <jsmith@example.com>
- First simple_echo package

在我们的规范文件中,我们给出了包名和版本。在发布中你可以看到第一个宏,%{?dist}。这会将分发详细信息添加到包中。摘要、许可证、来源和 URL 是必需的信息。如果需要,您可以有多个源文件,我们再次使用宏来描述它——它可以是一个 URL 或者必须存在于 SOURCES 目录中。

BuildRequires 和 Requires 用于处理包依赖关系。如果指定,必须在构建或安装之前安装所需的软件包。我们选择 bash 作为我们需要的包。

Tip

您也可以为 RPM 构建创建自己的宏。有关宏的更多信息,请参见 www.rpm.org/wiki/PackagerDocs/Macros

我们已经删除了一些用于编译软件的标准宏,因为我们在这里不编译任何东西。prep 和 setup 宏通过清理旧文件并将源文件解包到构建目录中来准备构建空间。接下来是%install 部分,在这里我们删除任何旧的构建数据并创建 BUILDROOT 目录结构。然后,我们使用 install 命令(将文件复制到所需的位置,并为它们提供所需的模式)。

下一节将列出我们正在安装的文件。它们可以是 doc 文件、conf 文件和目录。在本例中,我们列出了一个模式为 0755、所有者和组根的文件。

最后,我们有一个变更日志部分。这应在特定日期格式(DayofWeek Month Day Year name <email>)中定义。这需要跟一个以‘-‘开头的 changelog 消息。

一旦这个文件被保存,我们现在可以运行构建。构建 RPM 有几种主要的方法。首先是只构建二进制文件。第二步是编译源代码和二进制代码。还有其他选项,您可以使用 man rpmbuild命令找到这些选项。

$ rpmbuild –bb SPEC/simple_echo.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.7kXFJS
+ umask 022
+ cd /home/vagrant/rpmbuild/BUILD
+ cd /home/vagrant/rpmbuild/BUILD
+ rm -rf simple_echo-1.0
+ /usr/bin/gzip -dc /home/vagrant/rpmbuild/SOURCES/simple_echo-1.0.tar.gz
+ /usr/bin/tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ cd simple_echo-1.0
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ exit 0
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.EJ4LAv
+ umask 022
+ cd /home/vagrant/rpmbuild/BUILD
+ '[' /home/vagrant/rpmbuild/BUILDROOT/simple_echo-1.0-1.el7.centos.x86_64 '!=' / ']'
+ rm -rf /home/vagrant/rpmbuild/BUILDROOT/simple_echo-1.0-1.el7.centos.x86_64
++ dirname /home/vagrant/rpmbuild/BUILDROOT/simple_echo-1.0-1.el7.centos.x86_64
+ mkdir -p /home/vagrant/rpmbuild/BUILDROOT
+ mkdir /home/vagrant/rpmbuild/BUILDROOT/simple_echo-1.0-1.el7.centos.x86_64
+ cd simple_echo-1.0
+ rm -rf /home/vagrant/rpmbuild/BUILDROOT/simple_echo-1.0-1.el7.centos.x86_64
+ mkdir -p /home/vagrant/rpmbuild/BUILDROOT/simple_echo-1.0-1.el7.centos.x86_64/usr/local/bin
+ install simple_echo.sh -m0755 /home/vagrant/rpmbuild/BUILDROOT/simple_echo-1.0-1.el7.centos.x86_64/usr/local/bin/simple_echo.sh
+ '[' '%{buildarch}' = noarch ']'
+ QA_CHECK_RPATHS=1
+ case "${QA_CHECK_RPATHS:-}" in
...
<snip>
...
Processing files: simple_echo-1.0-1.el7.centos.x86_64
Provides: simple_echo = 1.0-1.el7.centos simple_echo(x86-64) = 1.0-1.el7.centos
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Requires: /bin/bash
Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/vagrant/rpmbuild/BUILDROOT/simple_echo-1.0-1.el7.centos.x86_64
Wrote: /home/vagrant/rpmbuild/RPMS/x86_64/simple_echo-1.0-1.el7.centos.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.LPOTLL
+ umask 022
+ cd /home/vagrant/rpmbuild/BUILD
+ cd simple_echo-1.0
+ /usr/bin/rm -rf /home/vagrant/rpmbuild/BUILDROOT/simple_echo-1.0-1.el7.centos.x86_64
+ exit 0

我们有构建过程的输出。你可以看到我们有线Wrote ... simple_echo-1.0-1.el7.centos.x86_64.rpm。我们现在将使用 rpm–IVH 安装该 rpm。

$ sudo rpm –i RPMS/x86_64/simple_echo-1.0-1.el7.centos.x86_64.rpm

现在,我们可以测试脚本是否已安装并正常工作:

$ simple_echo.sh hello
hello

应该指出的是,从源代码创建您自己的包可能需要一些时间,这取决于您要构建的包的复杂性以及它所依赖的包的数量。你还必须支持它,这有时会比你想象更麻烦。因为您可能会选择不常见的选项,所以您可能会发现自己独自面临其他人没有经历过的生产问题。

在您构建自己的包之前,您还可以查看 EPEL 包,它们是基于 Fedora 的上游包,但为 RHEL、CentOS 和 Scientific Linux 进行了重建。这些可能比管理你自己的一套 rpm 更好,但仍然带有相同的警告。

这并不是说要阻止你去尝试,而是要提醒你,构建你自己的包有时会变得漫长而危险。在“从源代码编译”一节中,我们将向您展示如何从源代码编译您自己的包,并为您提供一种制作 rpm 和 deb 包的方法。

Ubuntu 上的包管理

在 Ubuntu 服务器上管理软件包通常是使用命令行工具来完成的,因为 Ubuntu Server Edition 在默认情况下不安装 GUI。在这一节中,我们将首先介绍管理包的命令行工具。之后,我们将看看 Unity 桌面上的图形化软件包管理器。

在 Ubuntu 主机上添加软件最常见的方式是通过命令行工具从在线软件仓库安装软件包。该发行版提供了包含 22,000 多个现成安装包的在线软件库。这些年来,已经开发了一些工具来帮助你从远程仓库向你的 Ubuntu 主机添加应用程序。这些工具为基于 Red Hat 的发行版提供了相当于rpm and yum的 Ubuntu。我们将看看两个命令行工具:

  • aptitude
  • dpkg

这些工具都默认安装在 Ubuntu 主机上。aptitude工具允许您安装、删除、更新和搜索在线存储库中的包。这是管理包的首选方式,所以我们将最详细地介绍这个工具。另一个具有类似用途的流行工具是Apt ( https://wiki.debian.org/Apt )。

dpkg工具是用于安装和移除软件包的基本命令行工具。Aptitude 在内部使用dpkg来管理包。我们将只简要地看一下dpkg,因为你不太需要经常使用它。

才能

你可以用两种方式使用aptitude工具:通过菜单和对话框交互,或者通过命令行向它传递指令。对于初级用户,最简单的方法是使用aptitude的基于菜单的用户界面。让我们启动它看看。

$ aptitude

处理完包列表后,aptitude显示其主窗口,如图 8-14 所示。

A185439_2_En_8_Fig14_HTML.jpg

图 8-14。

The main aptitude screen

主屏幕由菜单栏、命令列表、窗口列表、状态信息、软件包列表窗口和信息窗口组成。您的光标将首先出现在软件包列表窗口中。您可以使用箭头键在列表中上下移动,并通过按 Enter 键展开突出显示的项目。

按 Enter 键展开已安装的软件包列表,然后展开主列表。您将在main部分看到按字母顺序排列的当前已安装软件包列表。突出显示某个包时,该包的描述会显示在信息窗口中。通过按 Tab 键切换到此窗口,然后使用箭头键上下滚动信息。再次按 Tab 键切换回列表窗口。在图 8-15 中,您可以看到我们突出显示了adduser包。

A185439_2_En_8_Fig15_HTML.jpg

图 8-15。

Displaying the package description

要显示更详细的包信息,请滚动以突出显示包名称并按 Enter 键,如图 8-16 所示。您现在可以看到诸如依赖项、维护者等信息。

A185439_2_En_8_Fig16_HTML.jpg

图 8-16。

Displaying detailed package information

按 q 关闭信息窗口并返回软件包列表。

因为滚动这个列表并不快,所以能够搜索它是有用的。按下正斜杠(/)并输入所需的搜索词,调出搜索对话框。我们找找 Ubuntu 内核包,叫linux-image。当您键入时,aptitude将在列表中向下跳转,显示第一个匹配的包。你可以在图 8-17 中看到这样的例子。

A185439_2_En_8_Fig17_HTML.jpg

图 8-17。

Searching packages by name

按 Enter 键关闭搜索对话框。要跳转到下一个匹配的包名,请按 n。如果您继续按 n,您最终会发现自己在未安装包的列表中,其中包含更多的内核。要在屏幕上显示更多的软件包名称,您可以通过按 d 键切换信息窗口。

现在让我们安装一个包。一个小但有用的网络实用程序是nmap,所以我们将选择它。使用find命令在列表中找到这个包,然后突出显示它。要标记此软件包以便安装,请按加号(+)。你会看到一个警告对话框,告诉你aptitude不能安装包,除非你成为 root 用户,如图 8-18 所示。

A185439_2_En_8_Fig18_HTML.jpg

图 8-18。

root user warning dialog

您可以禁止此警告在将来出现。您会注意到有一个“不再显示此消息”复选框。首先按 Tab 跳转到复选框,按空格键选中它,然后再次按 Tab 选择 OK 按钮并回车接受。要确认此警告,请按 Enter 键。

Tip

如果我们通过sudo启动aptitude,警告对话框就不会出现。然而,作为一个non-root用户进行选择增加了一层防止错误的保护。因为不作为根用户是不可能意外删除包的,所以作为non-root用户运行aptitude并成为根用户来应用挂起的更改是一个好主意。

您将看到软件包状态已经从 p 变为 pi,而不是立即安装软件包。现在它被标记为安装。这允许您在将更改应用到系统之前,选择任意数量的软件包进行安装或删除。

现在您将只安装nmap应用程序,但是首先您必须成为 root 用户。为此,按 Ctrl+T 激活动作菜单(参见图 8-19 )。

A185439_2_En_8_Fig19_HTML.jpg

图 8-19。

Selecting Become root

使用箭头键选择成为 root(避免玩扫雷游戏的诱惑),按回车键,aptitude现在会为你运行sudo。输入密码后,它会以根用户权限重新启动。虽然软件包列表会恢复到最初的折叠状态,但是安装软件包的命令会添加到内部待办事项列表中。

为了处理你的待安装,按 g. aptitude发现nmap包需要其他几个包存在于系统中,因此它被自动选择安装(用 piA 标记),如图 8-20 所示。

A185439_2_En_8_Fig20_HTML.jpg

图 8-20。

Added package dependencies

再次按 g 确认您想要安装这个依赖项,然后aptitude现在将下载所需的包文件并安装它们。在这个过程中,你会被告知正在发生的事情(见图 8-21 )。

A185439_2_En_8_Fig21_HTML.jpg

图 8-21。

Processing installation tasks

安装完成后,按回车键,返回到Aptitude菜单。您现在将学习如何通过移除刚刚安装的nmap包来移除包(参见图 8-22 )。再次使用/键进行搜索。您会看到它旁边有一个(I)表示已安装。按连字符(-)键标记要移除的包。您将看到所需的包状态字符从 I 变为 id。如果您已经定制了配置文件,并希望确保在此阶段也删除这些文件,您可以按下下划线(_)键来标记要清除的包;如果这样做,您将看到所需的状态变为 p。在图 8-22 中,您可以看到nmap现在被设置为 id,因为它被标记为删除。要应用排队的更改,请按 g。

A185439_2_En_8_Fig22_HTML.jpg

图 8-22。

Package removal via aptitude

安装 nmap 时作为依赖项添加的软件包,由于没有被任何其他软件包使用,因此也被设置为删除。再次按 g 确认并处理更改(参见图 8-23 )。

A185439_2_En_8_Fig23_HTML.jpg

图 8-23。

Process pending removals

有关Aptitude中可用命令的更多信息,请按问号(?)键。图 8-24 显示了可用命令的列表。

A185439_2_En_8_Fig24_HTML.jpg

图 8-24。

The aptitude command list

您可以按 q 退出帮助列表。然后您可以再次按 q 退出aptitude

非交互模式

所有这些导航和窗口切换可能是用户友好的,但它很难快速。为此,aptitude还有一个命令行模式,不使用交互菜单,而是以动作命令和包名作为参数。为了证明以这种方式使用aptitude和使用 GUI 一样方便,我们将再次安装nmap包。当以非交互方式使用aptitude时,您需要以 root 用户身份运行它来执行安装和卸载任务。看看如何在清单 8-13 中再次安装nmap

$ sudo aptitude install nmap
The following NEW packages will be installed:
  libblas-common{a} libblas3{a} liblinear3{a} liblua5.2-0{a} libxslt1.1{a} lua-lpeg{a} ndiff{a} nmap python-bs4{a} python-chardet{a} python-html5lib{a} python-lxml{a} python-pkg-resources{a}
  python-six{a}
0 packages upgraded, 14 newly installed, 0 to remove and 4 not to upgrade.
Need to get 0 B/6,311 kB of archives. After unpacking 28.1 MB will be used.
Do you want to continue? [Y/n/?] y
Selecting previously unselected package libblas-common.
(Reading database ... 99366 files and directories currently installed.)
Preparing to unpack .../libblas-common_3.6.0-2ubuntu2_amd64.deb ...
Unpacking libblas-common (3.6.0-2ubuntu2) ...
...<snip>...
update-alternatives: using /usr/lib/libblas/libblas.so.3 to provide /usr/lib/libblas.so.3 (libblas.so.3) in auto mode
Setting up nmap (7.01-2ubuntu2) ...
Processing triggers for libc-bin (2.23-0ubuntu3) ...
Listing 8-13.Installing nmap with aptitude in Noninteractive Mode

首先,aptitude检查软件包数据库以确保系统能够安装软件包;如果有部分配置或挂起的软件包,它会告诉您并中止安装。

接下来,aptitude通知我们nmap有几个依赖项也将被安装,并通知我们这些包将使用多少磁盘空间(28.1 MB)。请注意,它告诉我们需要获得 6,311KB 归档文件中的 0 个。因为我们之前已经安装了nmap,原始的包文件仍然缓存在机器上,否则一旦你输入 y 开始安装,你就会看到 aptitude 下载这些包。

它在内部使用dpkg命令来处理这一部分。完成后,它处理可能由刚刚添加的包定义的触发器,并重新检查包状态数据库以确保一切成功。

现在我们可以通过从命令行执行以下命令来检查是否安装了nmap

$ nmap –v
Starting Nmap 7.01 ( https://nmap.org ) at 2016-05-31 23:37 AEST

The Apt Cache

当您从互联网安装软件包时,它们首先被下载到您的计算机并存储在位于/var/cache/apt/archives的缓存中。如果您删除一个包,然后重新添加它,它不需要重新下载。有一些实用程序可以让您在多台计算机之间共享这样的缓存,如果您的互联网连接速度很慢或很贵,这很有用。这类实用程序的例子有apt-cacherapt-cacher-ngapt-proxy,它们都可以作为软件包获得。

使用 Aptitude 移除包

清单 8-14 展示了如何使用aptitude移除一个包。同样,像使用dpkg一样,您可以删除或清除一个包。当然,删除会从主机上删除除配置文件之外的所有内容,而清除会删除整个软件包,包括所有配置文件。

$ sudo aptitude remove nmap
The following packages will be REMOVED:
  libblas-common{u} libblas3{u} liblinear3{u} liblua5.2-0{u} libxslt1.1{u} lua-lpeg{u} ndiff{u} nmap python-bs4{u} python-chardet{u} python-html5lib{u} python-lxml{u} python-pkg-resources{u}
  python-six{u}
0 packages upgraded, 0 newly installed, 14 to remove and 4 not to upgrade.
Need to get 0 B of archives. After unpacking 28.1 MB will be freed.
Do you want to continue? [Y/n/?] y
(Reading database ... 100407 files and directories currently installed.)
Removing nmap (7.01-2ubuntu2) ...
Removing libblas3 (3.6.0-2ubuntu2) ...
...<snip>...
Processing triggers for man-db (2.7.5-1) ...
Processing triggers for libc-bin (2.23-0ubuntu3) ...
Listing 8-14.Removing Packages with aptitude

如果您还想删除任何配置文件,您可以将purge选项传递给aptitude

$ sudo aptitude purge nmap

Repositories

高级打包工具(APT)和Aptitude都从在线存储库中获取包。如前所述,存储库是由包维护者为您的特定发行版维护的包的集合。我们要看的两个工具都使用特殊的配置文件,称为 APT 源文件,来定义它们将去哪里找到这些库,以及它们想要什么类型的包可用。

APT 和Aptitude使用这些源文件来查找数万个包的信息。默认存储库的配置信息通常存储在/etc/apt/sources.list文件中,该文件是在主机安装过程中创建的。在/etc/apt/sources.list.d/目录中还可以定义更多的库。

一般来说,不同的发行版有不同的存储库,在这些发行版中,发行版的每个版本都有不同的存储库。对于不同类型的软件,还存在一组存储库。听起来很复杂?当你分解它的时候就不是了。再来看 Ubuntu 16.04 版本(代号 Xenial Xerus)。如果您安装了这个版本,您应该会在/etc/apt/sources.list文件中看到如下一行:

deb http://archive.ubuntu.com/ubuntu/ xenial main restricted

存储库定义从存储库类型和 URL 开始。URL 可能会被本地化到离您最近的存储库,如“ http://au.archive.ubuntu.com ”。类型指示存储库是包含二进制包还是源代码。存储库类型deb包含二进制包,存储库类型deb-src包含包含应用程序源代码的包。你通常不需要使用deb-src库,除非你正在创建反向端口(见侧栏“Ubuntu 反向端口”)。URL 指向托管存储库的服务器。下一个字段是版本,对于 Ubuntu 16.04 是“xenial”,你可以在 https://en.wikipedia.org/wiki/List_of_Ubuntu_releases 找到 Ubuntu 的其他版本。最后,我们有一个由一个或多个部分组成的列表,这些部分定义了您希望哪些包集可用。

这些软件包按照许可证类型和支持级别分为以下几个部分。Ubuntu 有四个部分。首先是“main”,它包含了 Ubuntu 的开发者 Canonical 支持的所有自由软件。“restricted”包由规范但不完全免费的许可证支持,如 Nvidia 驱动程序包。还有其他类型的。Canonical 不直接支持“universe”包,但是它受到更广泛的 Linux 社区的支持。最后,可能受到专利或法律问题阻碍的软件——如 MP3 播放器或 DVD 播放器软件——可从“multiverse获得你可以在 https://help.ubuntu.com/community/Repositories/Ubuntu 了解更多。

通过不指定这些部分中的一个或多个,您可以限制在您的主机上安装什么类型的软件包。例如,您可能希望只安装 Canonical 提供的受支持的免费软件包,在这种情况下,您的源代码行可能如下所示:

deb http://archive.ubuntu.com/ubuntu xenial main

作为一个练习,为什么不把你的浏览器指向 http://archive.ubuntu.com/ubuntu ,看看一个存储库是如何布局的呢?

您可以在 https://help.ubuntu.com/community/Repositories/Ubuntu 找到更多关于存储库和分区以及如何设置它们的信息。

使用 Aptitude 更新包

升级的另一个标准任务是,首先更新可用软件包的列表(检查您是否拥有存储库中可用的最新更新软件包记录),然后在您的主机上执行需要升级的软件包的升级。

清单 8-15 展示了如何更新可用包的列表。这里发生的事情是,aptitude程序使用在/etc/apt/sources.list文件中找到的库列表(以及包含在/etc/apt/sources.list.d/中的任何附加库)并为您的主机编译一个可用包列表。

$ sudo aptitude update
Listing 8-15.
aptitude Update

现在当你升级时,你有两个选择:safe-upgradefull-upgradesafe-upgrade不会删除已安装的软件包来升级正在升级的软件包,这有时是必需的。有时,为了升级第二个软件包,您可能需要删除第三方软件包。有了safe-upgrade,这个包就不会升级到新版本。在这种情况下,您必须使用full-upgrade,它将通过移除和安装完成工作所需的任何包来升级所有已安装的包。

清单 8-16 显示了这些命令的语法。

$ sudo aptitude safe-upgrade
$ sudo aptitude full-upgrade
Listing 8-16.Automatically Install Pending Package Upgrades

Advanced Packaging Tool

aptitude之前,APT 工具套件在基于 deb 的发行版上提供了大部分在线包管理功能。这些工具仍然存在,但是它们提供的一些功能在aptitude中不存在或者不容易使用,所以熟悉它们是值得的。组成 APT 的命令有apt-getapt-cacheapt-fileapt-get命令下载、安装和删除软件包和源文件。apt-cache命令搜索包列表并显示包信息。apt-file命令搜索包提供文件的文件内容列表。

aptitude工具是作为apt-get的替代工具编写的,所以在所有非交互式运行aptitude的情况下,都可以用apt-get来替代它。然而,aptitude有更高级的算法来解决包依赖和冲突,所以我们建议您尽可能使用aptitude。例如,如果您想安装带有apt-getnmap包,您可以使用以下命令:

$ sudo apt-get install nmap

要使用apt-cache命令找出关于特定包的信息,可以按如下方式使用:

$ sudo apt-cache showpkg nmap

要使用apt-file命令找出哪个包提供了系统上尚未安装的特定文件,您可以使用apt-file来搜索由存储库提供的包内容文件:

$ apt-file search /usr/sbin/foo

这需要您的系统上有最新的内容列表。如果您认为这些内容可能已经过时,您可以通过以下方式更新到最新版本:

$ sudo apt-file update

或者,您可以通过 http://packages.ubuntu.com 在线搜索套餐列表。

使用 Ubuntu 软件应用程序进行软件包管理

如果你已经在 Ubuntu 上安装了 Unity desktop,你还有另外一种管理软件安装的方式。在 Unity desktop 中有一个应用商店 Ubuntu Software,它与我们在 CentOS desktop 中看到的非常相似。

如果您的服务器上没有安装 Unity desktop,您可以跳过此部分。大多数服务器安装在机架中,没有屏幕或键盘,因此安装大型复杂的桌面环境毫无意义。如果你想添加一个完整的 GUI,你应该能够安装unity8-desktop-session-mir包。

安装完成后,通过sudo service lightdm start启动显示管理器,并使用您的用户名和密码登录。

访问 Ubuntu 软件商店非常容易,你可以通过图 8-25 所示的图标访问它。

A185439_2_En_8_Fig25_HTML.jpg

图 8-25。

Starting the Ubuntu Software app store

打开后如图 8-26 所示。

A185439_2_En_8_Fig26_HTML.jpg

图 8-26。

The Ubuntu Software app store

这个界面非常容易导航。在顶部,我们看到了与 CentOS、All、Installed 和 Updates 相同的视图。我们已经看到有四个更新等待安装。下面是搜索栏,底部是热门应用和推荐应用的快速链接。

让我们搜索一个叫做“终结者”的程序,这是一个很棒的小高级终端。在搜索栏中输入,会返回一个可能匹配的列表。由于这是唯一的“终结符”,我们返回了一个包(见图 8-27 )。

A185439_2_En_8_Fig27_HTML.jpg

图 8-27。

Finding the terminal package

我们可以点击安装按钮开始安装,或者点击列表上的任意位置获取更多信息(参见图 8-28 )。

A185439_2_En_8_Fig28_HTML.jpg

图 8-28。

More information for terminal

现在我们要安装这个包(见图 8-29 )。

A185439_2_En_8_Fig29_HTML.jpg

图 8-29。

Entering our password to get root privileges to install packages

我们被要求输入密码,然后才能安装软件。

一旦安装完毕(见图 8-30 ),我们可以从这里启动应用程序,也可以将其移除(见图 8-31 )。

A185439_2_En_8_Fig31_HTML.jpg

图 8-31。

Our installation is finished

A185439_2_En_8_Fig30_HTML.jpg

图 8-30。

Installing the package

现在让我们移除包(参见图 8-32 )。就像点击删除按钮一样简单。

A185439_2_En_8_Fig32_HTML.jpg

图 8-32。

Are we sure we want to remove?

一旦我们确认要移除软件包,可能会要求您再次确认您的密码,然后我们开始移除应用程序(参见图 8-33 )。

A185439_2_En_8_Fig33_HTML.jpg

图 8-33。

Removing the terminator application

如果我们单击 Installed 视图,我们将获得所有已安装软件包的列表(参见图 8-34 )。

A185439_2_En_8_Fig34_HTML.jpg

图 8-34。

Listing our installed packages

在已安装的页面中,我们可以单击任何列出的软件包,并获得更多相关信息,如果您愿意,也可以删除某个软件包。

转到更新页面,我们看到以下内容(参见图 8-35 ):

A185439_2_En_8_Fig35_HTML.jpg

图 8-35。

Updates view

我们再次看到,我们可以刷新列表,确保我们有最新的列表,方法是单击左上角的 reload 图标。我们可以通过单击安装按钮来安装个别更新,也可以通过右上角的安装按钮来安装所有更新。

如果我们想看看将安装什么,我们可以点击更新。

在图 8-36 中,我们看到了将随操作系统更新一起安装的软件包列表。

A185439_2_En_8_Fig36_HTML.jpg

图 8-36。

Listing the packages in the OS Updates update

现在我们将安装所有更新(见图 8-37 )。

A185439_2_En_8_Fig37_HTML.jpg

图 8-37。

Installing updates

最后,当所有更新都已安装后,您应该会看到以下屏幕(图 8-38 ):

A185439_2_En_8_Fig38_HTML.jpg

图 8-38。

All up to date

这就是使用桌面应用安装程序的全部内容,现在让我们来看看 Ubuntu 上最基本的打包工具。

使用dpkg

Ubuntu 上最基本的包管理工具是dpkg(读作“dee-package”)。aptaptitude工具都是dpkg的包装器,就像yumrpm命令的包装器一样。

Tip

我们建议您使用aptitude而不是dpkg来管理您的软件包。命令aptitude处理依赖关系并管理包之间的关系;dpkg命令不会。

dpkg命令允许你列出已经安装的软件包,安装你之前下载的软件包文件,并找出系统上的软件包文件属于哪个。通过发出 man dpkg命令,您可以找到对dpkg可用的所有选项(有很多),如清单 8-17 所示。

$ man dpkg
Listing 8-17.The dpkg man page

表 8-4 列出了dpkg命令的一些主要选项和标志。

表 8-4。

Options and Flags for dpkg

| 选项和标志 | 描述 | | --- | --- | | `-l | --list` | 列出主机上所有已安装的软件包。 | | `-p | --print-avail` | 打印包信息。 | | `-c | --contents` | 列出特定已卸载软件包的内容。 | | `-L | --listfiles` | 列出已安装软件包的内容。 | | `-i | --install` | 在您的主机上安装软件包。 | | `-S | --search` | 查找安装的文件属于哪个包。 | | `-r | --remove` | 从主机上删除软件包。留下相关的配置文件。 | | `-P | --purge` | 从主机中清除软件包。删除关联的配置文件。 |

首先,我们将获得已经安装在我们新的 Ubuntu 系统上的软件包列表,如清单 8-18 所示。

$ dpkg -l
Listing 8-18.Listing Installed Packages

这将生成我们主机上所有包的完整列表。这通常是一个很长的列表,但是我们可以将来自dpkg的输出通过管道传输到 more 命令,并逐页显示输出(参见清单 8-19 )。然后我们可以在闲暇时浏览它。

$ dpkg -l | more
Listing 8-19.Piping the dpkg -l Output to More

注意在图 8-39 中dpkg列出了四列输出:状态、名称、版本和描述。我们通过head命令传输这个输出,默认情况下,它只给出前十行输出。

A185439_2_En_8_Fig39_HTML.jpg

图 8-39。

Viewing results of dpkg -l Note

虽然从清单 8-20 的输出中没有自动清除,但前三行实际上是状态栏。

让我们看一下这些列。“状态”列实际上由包可能处于的三种状态组成。

  • 期望的状态
  • 当前状态
  • 错误

通常情况下,状态会是ii,这意味着软件包当前已经安装,如果有更新的版本,将会升级。表 8-5 列出了最常见的状态代码及其含义。

表 8-5。

The dpkg Status Codes

| 密码 | 意义 | | --- | --- | | `ii` | 软件包已安装,并将在有更新时安装。 | | `hi` | 程序包已暂停并已安装,但不会安装更新。 | | `un` | 软件包未安装。 | | `rc` | 软件包未安装,但有剩余的配置文件(通常意味着软件包已被删除)。 |

其他专栏不言自明。它们包含软件包名称、版本(如果当前安装了软件包)和简短描述。

您通常不希望列出所有内容,所以让我们通过传递一个字符串让dpkg来匹配,从而将输出限制在提供 Linux 内核的包中。Ubuntu 中的 Linux 内核被命名为linux-image,这与 RHEL 不同,在那里它只是被称为内核。l 通过在我们的目标字符串前后使用*符号,使用一个 glob 字符串列出安装在我们主机上的所有linux-image(意味着我们捕获所有包含字符串linux-image的结果)。

Tip

Glob strings 是处理字符串的非常有用的方法,尤其是像文件名这样的字符串。你可以在 www.faqs.org/docs/abs/HTML/globbingref.html 阅读关于使用 glob strings 的内容。

在这种情况下,我们使用的是*linux-image*(您应该阅读一些关于 globbing 的知识,并测试当您使用linux-image *和linux-image作为搜索字符串时返回的结果)。

在图 8-40 中,你可以看到有几个linux-image包,它们的名字都基于它们包含的内核版本。这就是为什么不同的内核包没有相同的名字,从而允许你安装多个内核。此外,您会发现linux-image包,这是一个所谓的虚拟包。这个包实际上不包含任何文件,但是它包含一个到最新可用内核的链接,所以正常的升级总是包含任何可用的新 Linux 内核包。

A185439_2_En_8_Fig40_HTML.jpg

图 8-40。

Listing the Linux Kernel Tip

因为缺省的 Linux 终端只有 80 个字符宽,而dpkg想要在一行中显示每个包的信息,所以如果包名超过 14 个字符,它就不能显示完整的包名。对于许多包来说,这不是问题,但是内核包有很长的名字,所以不能完全显示。要解决这个问题,您可以使用环境变量覆盖dpkg的终端大小,这样它将显示更多信息。要做到这一点,给命令加上前缀如下:$ COLUMNS=200 dpkg -l ' *linux-image*'。这告诉你的主机你的屏幕有 200 个字符宽,所以它显示了更多的列。

检查包详细信息

我们先来看看 Ubuntu 上的一个常用包 adduser 用dpkg的一些信息。清单 8-20 显示了dpkg –p命令查询我们的adduser包的信息的输出。

$ dpkg -p adduser
Package: adduser
Priority: required
Section: admin
Installed-Size: 648
Origin: Ubuntu
Maintainer: Ubuntu Core Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Architecture: all
Multi-Arch: foreign
Version: adduser-3.113+nmu3ubuntu4
Replaces: manpages-it (<< 0.3.4-2), manpages-pl (<= 20051117-1)
Depends: perl-base (>= 5.6.0), passwd (>= 1:4.1.5.1-1.1ubuntu6), debconf | debconf-2.0
Suggests: liblocale-gettext-perl, perl-modules, ecryptfs-utils (>= 67-1)
Filename: pool/main/a/adduser/adduser_3.113+nmu3ubuntu4_all.deb
Size: 161698
MD5sum: 36f79d952ced9bde3359b63cf9cf44fb
Description: add and remove users and groups
Original-Maintainer: Debian Adduser Developers <adduser-devel@lists.alioth.debian.org>
SHA1: 6a5b8f58e33d5c9a25f79c6da80a64bf104e6268
SHA256: ca6c86cb229082cc22874ed320eac8d128cc91f086fe5687946e7d05758516a3
Homepage: http://alioth.debian.org/projects/adduser/
Description-md5: 7965b5cd83972a254552a570bcd32c93
Supported: 5y
Task: minimal
Listing 8-20.Output of the dpkg -p Command

adduser 包包含用于系统用户管理的命令。您可以在清单 8-20 中看到,除了描述和版本之外,每个包还包含一个支持联系人的电子邮件以及安装时使用的磁盘空间信息。Depends 部分还详细说明了在安装软件包本身之前需要安装的软件包,这称为依赖性。

检查包裹内容

除了关于软件包的描述信息之外,您还可以查询它安装了哪些文件以及将它们安装到了哪些目录。您可以使用dpkg -L命令找出包内容,如下所示:

$ dpkg –L adduser

这将返回由adduser包安装的文件的完整列表。

Tip

找出软件包提供了哪些命令的一个简单方法是列出它安装到包含可执行应用程序的目录中的文件。让dpkg列出包的内容,并通过管道将输出传递给grep,以将输出限制在包含字符串 bin 的文件和目录中,例如:dpkg -L | grep bin

执行文件搜索

对于主机上已经存在的文件,您可以使用dpkg来确定它们属于哪个包:

$ dpkg -S /usr/sbin/userdel

这个命令告诉你哪个包提供了userdel命令。

安装软件包

所有的 Ubuntu 包文件都由三部分组成:包名、包版本和目标架构。例如,用于 32 位 Intel 机器的 foobar 包的 2.17 版应该是“foobar-2.17_i386.deb”。如果你能在任何架构上安装这个包,它将是“foobar-2.17_all.deb”

Note

目标体系结构是您的主机的处理器技术(例如,i386 或 x64)。

在您获得了适用于您的 Ubuntu 版本和架构的包文件之后,您可以使用dpkg来安装它。因为软件包包含需要安装到特权系统位置的文件(比如/bin, /usr/sbin等)。),安装需要以root用户的身份进行。在 Ubuntu 上,你可以使用第五章讨论的sudo命令,以root用户的身份执行安装命令:

$ sudo dpkg -i wget_1.17.1-1ubuntu1_amd64.deb

在安装过程中,dpkg命令会让您了解进度。根据软件包的不同,它可能还会询问您一些关于如何配置软件包的问题。不要担心,你可以在以后更改这些问题的答案。

安装手动下载的软件包时要小心。如果该软件包不是为您的 Ubuntu 版本创建的,它所依赖的其他软件包可能在正确的版本中不可用。无论如何安装都可能会导致“无法解析的依赖关系”,这可能会使您的软件包系统处于不一致的状态,在这种状态下,软件包被部分安装,并阻止进一步的软件包管理,直到问题得到解决。会警告你,不强迫你就不让你这么做。一个最佳实践是,在从第三方来源下载软件包之前,总是检查该软件包在发行版中是否可用。

移除包

你有两种不同的方法来移除一个 Ubuntu 包:一种是移除这个包,另一种是从系统中清除这个包。当您使用--remove选项时,您将从主机上删除除该包的已修改配置文件之外的所有内容。当您使用--purge选项时,您是在告诉dpkg删除该软件包安装到您主机上的所有东西,包括您自己修改的配置文件。

为什么有这两种方法?因为有时您希望完全删除所有内容,有时您希望删除某些内容,以便在以后的某个阶段重新安装。

清单 8-21 展示了用dpkg命令从主机删除包的两种方法。

$ sudo dpkg --remove wget
Listing 8-21.Package Removal

或者

$ sudo dpkg --purge wget

从源代码编译

尽管打包软件的列表很广泛,但并不是所有东西都可以作为方便的 deb 或RPM包获得。如果一个软件没有打包格式,甚至没有用于反向移植的源代码包(见侧栏“Ubuntu 反向移植”),你可能需要从源代码构建它。

Ubuntu Backports

有时,如果您需要的特定包版本不可用,您可能会被告知创建一个 backport。如果您使用的是发行版的旧版本,或者您的发行版中还没有打包应用程序的新版本,就可能会出现这种情况。创建一个 backport 需要从一个较新(或较旧)的 Ubuntu 版本中获取源码包,并在你自己的机器上编译它。反向端口超出了本书的范围,但是网上有一些很好的参考资料,你可以从中学习如何使用和创建它们。一个好的起点是 https://help.ubuntu.com/community/UbuntuBackports

在这一节中,我们将向您展示如何从源代码编译软件,并给出一些关于如何保持这种源代码安装可管理性的提示。

从源代码构建应用程序通常有三个阶段:

  1. 配置应用程序。
  2. 编译或制作应用程序。
  3. 安装应用程序。

我们将带您经历这三个阶段,并以nginx web 服务器为例。这一次,我们将在撰写本文时构建最新的源代码(版本 1.10.1),而不是从包中安装我们的应用程序。首先,让我们使用wget工具从 http://nginx.org/ 中抓取 tarball。

$ wget –c http://nginx.org/download/nginx-1.10.1.tar.gz

Note

tarball 是包含一组文件和/或目录的文件(通常是压缩的)。用来创建这些 tarballs 的应用程序被称为tar(磁带存档),这样命名是因为它最初被用来将存档写入磁带。Tarballs 的文件扩展名通常表示归档文件是如何压缩的。例子包括tar.gz或者。tgzgziptar.bz2或。tbzbzip2压缩。你可以在它的man页面上看到更多关于 tar 的信息。

c 标志告诉wget恢复部分下载。这意味着如果下载由于某种原因被中断,它不会重新开始下载。使用-c 选项,如果下载失败,您可以重新运行该命令,下载将从它停止的地方恢复。

wget命令将文件下载到当前目录,所以我们现在有了一个可以解压的 tarball。扩展告诉我们它是用gzip压缩的,所以我们需要-z标志让tar使用gzip解压缩。我们传递–x 来告诉 tar 提取归档文件。–f 用于提供要处理的文件。

$ tar -xvzf nginx-1.10.1.tar.gz

Tip

如果您不知道 tarball 使用什么压缩方式,您可以使用file命令来查找。运行file <tarball>file将检查 tarball 的魔术字节,这表明了文件类型。数千种文件类型的神奇字节列表存储在/usr/share/file/magic中。一旦你知道 tarball 是如何被压缩的,你就可以打开它了。

-v参数告诉tar要详细并打印它从存档中提取的每个文件的路径。如果您知道归档中包含哪些文件,您可能希望删除此参数,这样您的终端就不会滚动显示冗余信息。额外的好处是,不打印文件名意味着提取操作完成得更快。

tarball 现在已经被提取出来了,所以我们应该转到源文件目录并查看一下,看看那里有哪些文件。

$ cd  nginx-1.10.1
∼/ nginx-1.10.1$ ls

在许多情况下,编译和安装应用程序的说明将包含在一个名为 README 或 INSTALL 的文件中。对于nginx,似乎没有安装文件,但有一个自述文件。通过阅读这个文件,我们应该能够找到安装应用程序的说明。

$ less README

我们从这个文件中了解到,安装文档是在线的,不包含在 tarball 中。详细信息可参考 http://nginx.org/nginx具体在线文档。我们将在这里使用这些指令( http://nginx.org/en/docs/configure.html )来编译和安装应用程序。

但是在编译我们的应用程序之前,我们需要安装一个编译器及其相关的库和实用程序。这些通常是打包的,所以我们将简单地通过包系统安装它们。表 8-6 显示了 Red Hat 和 Ubuntu 所需的包。

表 8-6。

Installing a Compiler and Essential Build Tools

| 分配 | 命令 | | --- | --- | | CentOS | `yum install gcc make` | | 人的本质 | `aptitude install build-essential` |

安装ˌ使成形

安装了编译器之后,我们现在可以配置编译的源代码了。大多数软件都是高度可配置的,不仅在可用特性方面,而且在安装位置方面。为了在构建应用程序之前配置它,我们使用了configure命令。在清单 8-22 中,我们运行带有--help选项的配置命令来显示所有可用于配置我们的应用程序的选项。

∼/nginx-1.10.1$ ./configure --help

  --help                                    print this message

  --prefix=PATH                      set installation prefix
  --sbin-path=PATH                set nginx binary pathname
  --modules-path=PATH         set modules path
  --conf-path=PATH                set nginx.conf pathname
  --error-log-path=PATH         set error log pathname
  --pid-path=PATH                  set nginx.pid pathname
  --lock-path=PATH                 set nginx.lock pathname

  --user=USER                        set non-privileged user for
                                                       worker processes
  --group=GROUP                   set non-privileged group for
                                                       worker processes

Listing 8-22.Shortened List of Configuration Help

Note

注意,在清单 7-28 中,我们在configure命令前面指定了./。这告诉 Linux 运行它在当前目录中找到的configure脚本。

输出还在继续,但是我们将在最重要的选项--prefix处停止。这个选项决定了软件的安装位置,重要的是这个位置不能被打包的软件使用。否则,您可能会遇到从源文件安装的文件覆盖打包文件的情况。这将使包系统混乱,当您删除有问题的包时,您编译的文件也将被删除。

通常,当您从源代码安装应用程序时,它们被部署在/usr/本地目录结构中。这通常是--prefix的默认选项。我们将把nginx安装在另一个安装你自己软件包的公共目录下,/opt

其他需要注意的选项是那些决定软件中可用特性的选项。它们决定了在配置期间是否应该检查第三方库,如果存在,是否应该使用第三方库。这些选项通常以--with -和--without -为前缀。我们将编译最基本的默认nginx服务器。有关可用选项的更多信息,请参考上面的文档链接。

有了这些新的知识,我们现在可以用默认选项配置我们的nginx源,如清单 8-23 所示。

∼/nginx-1.10.1$ ./configure --prefix=/opt/nginx
checking for OS
 + Linux 4.4.0-22-generic x86_64
checking for C compiler ... found
 + using GNU C compiler
 + gcc version: 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2.1)
checking for gcc -pipe switch ... found
checking for -Wl,-E switch ... found
checking for gcc builtin atomic operations ... found
checking for C99 variadic macros ... found
checking for gcc variadic macros ... found
...<snip>...
checking for sha1 in system md library ... not found
checking for sha1 in system OpenSSL crypto library ... not found
checking for zlib library ... not found

./configure: error: the HTTP gzip module requires the zlib library.
You can either disable the module by using --without-http_gzip_module
option, or install the zlib library into the system, or build the zlib library
statically from the source with nginx by using --with-zlib=<path> option.

Listing 8-23.Configuring Our Source Tree

脚本检查我们的系统是否存在编译器、所需的头文件以及这些头文件中的函数和数据结构的定义。我们在本章开始时讨论了头文件和库,并提到通常只有在从源代码编译软件时才需要它们。在清单 7-29 中,注意脚本无法找到 zlib 头。我们可以选择给出 zlib 文件的安装路径(--with-zlib=<path>),或者在没有 zlib 的情况下编译(--without-http_gzip_module)。对于我们的 Ubuntu 主机,我们将安装 zlib1g-dev 和 libpcre3-dev,它们提供了必要的压缩和 perl 头文件。根据您在构建时包含的选项,您可能需要其他包。

$ sudo aptitude install -y zlib1g-dev libpcre3-dev

如果您指定--without-<option>,该选项将被禁用。我们现在再次运行编译命令。

∼/nginx-1.10.1$ ./configure --prefix=/opt/nginx
checking for OS
 + Linux 4.4.0-22-generic x86_64
checking for C compiler ... found
 + using GNU C compiler
 + gcc version: 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2.1)
checking for gcc -pipe switch ... found
checking for -Wl,-E switch ... found
checking for gcc builtin atomic operations ... found
checking for C99 variadic macros ... found
checking for gcc variadic macros ... found
...<snip>...
creating objs/Makefile

Configuration summary
  + using system PCRE library
  + OpenSSL library is not used
  + using builtin md5 code
  + sha1 library is not found
  + using system zlib library

  nginx path prefix: "/opt/nginx"
  nginx binary file: "/opt/nginx/sbin/nginx"
  nginx modules path: "/opt/nginx/modules"
  ...<snip>...
  nginx http proxy temporary files: "proxy_temp"
  nginx http fastcgi temporary files: "fastcgi_temp"
  nginx http uwsgi temporary files: "uwsgi_temp"
  nginx http scgi temporary files: "scgi_temp

输出显示了 nginx 的成功配置,并提供了一个摘要。

编译和制作

完成后,configure命令会写入一个配置头文件和一个名为Makefile的特殊文件。前者包含向编译器指示可用函数和库的代码,后者包含通过make命令构建软件所需的命令。make命令读取Makefile并执行其中包含的命令和有序步骤(参见 http://www.tutorialspoint.com/makefile/why_makefile.htm 了解更多关于 make 的信息)。我们发出make命令开始构建清单 8-24 中的nginx

∼/nginx-1.10.1$ make
make -f objs/Makefile
make[1]: Entering directory '/home/jsmith/nginx-1.10.1'
cc -c -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g  -I src/core -I src/event \
              -I src/event/modules -I src/os/unix -I objs \
        -o objs/src/core/nginx.o \
        src/core/nginx.c
cc -c -pipe  -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g  -I src/core -I src/event \
              -I src/event/modules -I src/os/unix -I objs \
        -o objs/src/core/ngx_log.o \
        src/core/ngx_log.c
sed -e "s|%%PREFIX%%|/opt/nginx|" \
        -e "s|%%PID_PATH%%|/opt/nginx/logs/nginx.pid|" \
        -e "s|%%CONF_PATH%%|/opt/nginx/conf/nginx.conf|" \
        -e "s|%%ERROR_LOG_PATH%%|/opt/nginx/logs/error.log|" \
        < man/nginx.8 > objs/nginx.8
make[1]: Leaving directory '/home/jsmith/nginx-1.10.1'
Listing 8-24.Compiling nginx

如果make过程成功完成,应用程序就构建好了。如果失败,您通常会收到一条错误消息,指出原因,并希望得到一些关于如何修复它的指导。构建应用程序失败的原因有很多——太多了,无法在此详述——但一般来说,您可能会遇到别人以前发现过的问题。您遇到的问题可能在应用程序的网站上有详细说明,例如,在安装文档或常见问题部分。通过谷歌搜索特定的错误信息也可能指出可能的解决方案。此外,联系支持列表,通常会有人非常乐意提供帮助。

安装

现在我们的nginx应用程序已经编译好了,我们需要让它对系统上的所有用户都可用,方法是将它安装到我们之前配置时选择的前缀位置。Makefile 也包含这样的命令,您可以在清单 8-25 中看到安装过程。

∼/nginx-1.10.1$ sudo make install
[sudo] password for jsmith:
make -f objs/Makefile install
make[1]: Entering directory '/home/jsmith/nginx-1.10.1'
test -d '/opt/nginx' || mkdir -p '/opt/nginx'
test -d '/opt/nginx/sbin' \
        || mkdir -p '/opt/nginx/sbin'
test ! -f '/opt/nginx/sbin/nginx' \
        || mv '/opt/nginx/sbin/nginx' \
                '/opt/nginx/sbin/nginx.old'
<snip>
test -d '/opt/nginx/logs' \
        || mkdir -p '/opt/nginx/logs'
make[1]: Leaving directory '/home/jsmith/nginx-1.10.1'
Listing 8-25.Installing nginx

我们需要使用sudo,因为作为普通用户,我们不允许在/opt/nginx下创建新文件。同样,make处理Makefile中的规则,并执行命令在系统上安装nginx及其相关文件。我们现在可以运行新安装的应用程序来确保一切正常,如清单 8-26 所示。

∼/nginx-1.10.1$ sudo /opt/nginx/sbin/nginx -v
nginx version: nginx/1.10.1
Listing 8-26.Running nginx

从计算机上卸载

当您想从系统中删除它们时,管理源代码安装的棘手部分就来了。一些(但不是全部)包含卸载规则。对于nginx,没有一个,但是我们将调用下面的:

∼/<some-package-source>$ sudo make uninstall

这意味着我们需要在我们的系统中保存配置好的资源。这并不理想,因为我们不仅需要跟踪我们从源代码安装了哪些软件,还需要跟踪我们将这些源代码保存在哪里。这也是我们建议您避免从源代码安装应用程序,而是依靠软件包来提供应用程序的原因之一。因为我们在/opt/nginx中安装了我们的应用程序,这意味着我们可以通过发出rm -rf /opt/nginx有效地“卸载”,这将删除整个nginx目录。

使用 FPM 创建包

FPM 是一个应用程序,它从各种不同的源类型构建 deb 或 rpm 包(以及其他包类型),包括 deb 源包和 RPM 源包。我们将构建一个 nginx 安装包作为 deb,但是这个过程对于 RPM 包也是一样的。

从安装 FPM 开始,它就像一个 Ruby 宝石,一个封装和分发软件的另一种方式的宝石,特别是对于 Ruby 应用程序。

首先,在 Ubuntu 上,我们将发布以下内容:

$ sudo aptitude install –y install ruby ruby-dev

如果您正在运行 CentOS:

$ sudo yum install -y ruby ruby-devel

下面的步骤对于 CentOS 和 Ubuntu 是一样的。我们将在我们的 Ubuntu 主机上构建并执行它(但是如果你已经安装了所需的包,你可以在任一发行版上构建 rpm 和 deb)。然后 FPM 通过宝石命令:

$ sudo gem install fpm

我们现在创建一个临时安装目录来保存我们的 nginx 安装。

$ sudo mkdir /tmp/installdir

然后我们转到 nginx 源目录,在那里我们编译了 nginx 源,如清单 8-25 所示,现在发出下面的命令将 nginx 安装到我们的临时安装目录中。

$ sudo make install DESTDIR=/tmp/installdir

如果你查看/tmp/installdir目录,你会看到我们已经在那个目录中安装了 nginx。我们现在指示 FPM 进入那个目录,为我们创建一个 deb 包。我们让 FPM 施展它的魔法,发布了以下内容:

$ sudo fpm -s dir -t deb -n nginx -v 1-10.1 -C /tmp/installdir/

这里我们为 dir 的源类型指定了–s,这意味着目录。目标(-t)是一个 debian 包。如果我们正在构建一个 RPM 包,这将是-t rpm。我们将其命名为(-n ) nginx,并给出版本(-v)或1-10.1。最后,我们告诉 FPM 转到(-C /tmp/installdir)并将目录中的内容打包。

这将在本地目录中创建一个nginx_1-10.1_amd64.deb包。您现在可以用dpkg命令安装它。

$ sudo dpkg -i nginx_1-10.1_amd64.deb
Selecting previously unselected package nginx.
(Reading database ... 213564 files and directories currently installed.)
Preparing to unpack nginx_1-10.1_amd64.deb ...
Unpacking nginx (1-10.1) ...
Setting up nginx (1-10.1) ...

我们可以通过发出以下命令来测试它是否工作:

$ /opt/nginx/sbin/nginx -v
nginx version: nginx/1.10.1

这就是我们,一个由我们编译的源代码制作的 debian 包。你可以在这里看到更多关于 FPM 的信息:

摘要

在这一章中,我们已经看到了很多。现在,您应该能够在您的 Linux 服务器上安装、删除、更新和维护这些包了。我们展示了以下内容:

  • CentOS 应用程序安装程序
  • 百胜包管理和 DNF
  • RPM 包管理
  • Ubuntu 软件应用程序
  • 智能包管理
  • Dpkg 包管理
  • 从源代码编译
  • 创建一个 debian 或 RPM 包

在下一章中,我们将看看如何设置您的存储以获得最大的可靠性,您将学习如何避免硬盘问题并从中恢复。

九、存储管理和灾难恢复

当您安装第一台 Linux 主机时,您接受了设置磁盘和分区的所有默认设置。现在,您已经掌握了一些基本的系统管理知识,让我们重新回顾一下存储配置,看看如何更改它以满足您的需求。我们将了解各种类型的存储硬件,以及如何利用存储管理软件为您带来优势。任何业务的关键部分都是数据,因此您需要确保数据既安全又可访问,并保持这种状态。

在本章中,我们将解释如何创建和管理磁盘分区和 RAID,如何使应用程序可以访问您的存储,以及如何从崩溃中恢复。

Note

在第十四章中,我们将介绍如何备份和恢复您的数据。

存储基础知识

我们将从 Linux 如何处理存储开始。为此,我们将添加各种新磁盘,对这些磁盘进行分区,然后格式化和管理这些存储。

格式化后,Windows 下的驱动器显示为驱动器号,但 Linux 的工作方式不同。它没有驱动器号的概念,格式也不完全相同。相反,驱动器和存储显示为可以分区的设备。这些分区又可以被格式化或聚集到逻辑卷中,然后被格式化。

让我们从设备开始,设备是 Linux 存储的基本构建块。然后,我们将继续讨论分区和文件系统。

设备

我们在第四章中简单提到了设备文件。这些文件是 Linux 使硬盘驱动器、USB 和 DVD 驱动器等硬件设备可以从操作系统内部访问的方式。主机中的大多数(但不是全部)设备由/dev目录中的文件表示。

/dev目录是一个特殊的目录,由名为udev的服务填充。当主机启动时,内核检测到一个设备,它告诉udev,然后在/dev目录中创建该设备的一个表示。这些设备文件是内核为应用程序和服务提供访问设备的方式。

设备文件有很多种,但是在这一章中,我们将只讨论那些处理存储的文件,它们都属于块设备的范畴。此类别包括硬盘、USB 驱动器、磁带驱动器以及 CD 和 DVD 驱动器。所有类型的硬盘(例如 ATA、串行 ATA、SCSI、SAS 和 SSD)都由名称以sd开头的设备文件表示,它代表 SCSI 磁盘,因为所有这些不同类型的驱动器都像 SCSI 驱动器一样被访问。

Note

SCSI 是代表小型计算机系统接口的首字母缩略词,它是计算机如何连接和访问存储设备的规范。您可以在 http://en.wikipedia.org/wiki/SCSI .了解更多关于此规范的信息

您可以通过使用ls命令列出主机上可用的磁盘设备,如清单 9-1 所示。

$ $ ll /dev/sda*
brw-rw---- 1 root disk 8, 0 Jun  7 22:45 /dev/sda
brw-rw---- 1 root disk 8, 1 Jun  7 22:45 /dev/sda1
brw-rw---- 1 root disk 8, 2 Jun  7 22:45 /dev/sda2
brw-rw---- 1 root disk 8, 5 Jun  7 22:45 /dev/sda5
Listing 9-1.Listing Device Nodes

清单 9-1 显示了四个块设备,或设备节点。它们对root用户和disk组是可读和可写的。接下来,通常显示文件大小的地方是由逗号分隔的两个数字。这些是设备主要编号和次要编号。主号告诉内核使用哪个设备驱动程序来访问设备,次号给出内核关于设备的特定信息,在本例中是分区号。最后,显示上次修改设备文件的日期和时间。

实际的设备文件名由前缀sd和一个表示它属于哪个磁盘的字母组成。第一个检测到的磁盘是sda,第二个是sdb,第三个是sdc,依此类推。最后,磁盘上的每个分区也有自己的设备节点,分区号是名称的最后一部分。这意味着sda1是磁盘sda上的第一个分区,sdb2是磁盘sdb上的第二个分区,依此类推。我们将很快讨论分区。

您可能会看到的其他设备有:

| 设备名称 | 你会在哪里找到它 | | --- | --- | | 巨人十六号 | Xen 虚拟机 | | /dev/vda | KVM 虚拟机 | | /dev/hda | KVM 虚拟机,较旧的 ATA 主机 | | /dev/md | Linux 软件 raid | | /dev/sda | 配备 SAS、SSD 等设备的物理服务器。 |

Note

旧系统不能支持很多驱动器,因为设备次编号范围从 1 到 255,每个磁盘只能有 16 个编号,所以 Linux 可以容纳 16 个硬盘,每个硬盘有 16 个分区,在耗尽设备节点之前从/dev/sda1/dev/sdp16。现在你的系统理论上可以支持 10000 个驱动器( https://access.redhat.com/articles/rhel-limits )。这是关于 Linux 内核中块和字符设备的权威文档: www.kernel.org/doc/Documentation/devices.txt

如果您有硬件 RAID 控制器,它可能会以不同的方式命名您的阵列和任何分区。RAID 控制器将多个磁盘组合成廉价磁盘冗余阵列(RAID)。我们将在本章的后面详细讨论 RAID。要找出 RAID 阵列的设备节点,您可以使用以下命令列出/dev/目录中的所有块设备:

$ ls -l /dev | grep ^b

该命令将只列出以b开头的行。然而,检查内核内部日志缓冲区的内容会更准确。每当内核事件发生时,它都会被添加到内核内部日志缓冲区中。然后,日志守护程序将这个缓冲区写入日志文件,您可以使用dmesg命令直接查询它。

$ dmesg |less

大多数 RAID 控制器也至少使用部分内核 SCSI 子系统,您可以通过less中的内置搜索功能来搜索检测到的 SCSI 设备。在less窗口中输入/scsi并按回车键搜索包含字符串scsi的任何行。你可以按 n 键跳到下一场比赛。

划分

将磁盘添加到主机后,需要执行一些步骤来使其可用。首先,您可以在该磁盘上创建一个或多个分区。如果创建一个分区,系统需要能够找到关于分区几何的信息。它将这些信息存储在磁盘的开始处(有时会在磁盘的其他地方存储一份副本——稍后会详细介绍)。

我们之前已经描述过分区是将一块蛋糕分割成更小的块,这就是我们可以对物理磁盘做的事情。我们把磁盘分成小块。例如,这样您可以将日志和用户数据与操作系统分开,这样日志或用户就不会填满您的系统磁盘并导致问题。

在第六章中,我们向您介绍了两种不同的分区管理器,主引导记录(MBR)和 GPT (GUID 分区表)。您可能还记得,存储在磁盘前 446 个字节中的 MBR 描述了分区信息,它存储在引导记录之后的 64 个字节中。你不能在 64 字节中存储大量数据,所以一个磁盘可以容纳的分区数量最初是相当有限的。另一方面,GPT 最多可以容纳 128 个分区。

Disks and Partitions

我们之前已经解释了 MBR 和 GPT 分区之间的区别。如果您记得 MBR 保存在磁盘的前 512 个字节中,并且只能保存小于 2 Tb 的磁盘大小的足够信息。GPT 没有这种限制,可以在任何大小的磁盘上使用。这导致了分割磁盘方式的不同。

使用 MBR,分区有三种风格:物理的、扩展的和逻辑的。这是因为只有有限数量的分区信息可以存储在可供此类数据使用的 64 个字节中。一个分区需要 16 个字节的数据来描述,所以有了四个分区的信息,就满了!

作为一种变通方法,人们发明了扩展分区的概念。四个可用的物理分区中的一个被标记为扩展分区,然后作为无限数量的逻辑分区的容器。

这 16 个字节描述了每个分区,包括关于分区类型的信息,在磁盘上的什么地方可以找到它,以及它是否是可引导的,尽管 Linux 不关心后者。

您将使用 fdisk 实用程序来管理 MBR 磁盘。

对于 GPT,我们之前说过我们可以对高达 2ZiB 的磁盘进行分区。GPT 的默认限制是 128 个分区。GPT 使用 64 位逻辑块地址。它增加了校验和的可靠性。分区被赋予 UUIDs 和名称以避免冲突。

您可以使用partedgdisk工具来管理 GPT。

您可以在 https://wiki.manjaro.org/index.php?title=Some_basics_of_MBR_v/s_GPT_and_BIOS_v/s_UEFI 找到关于这些分区管理器的更多详细信息。

您可以使用fdisk实用程序创建和删除分区,或者如果您使用的是 GPT,gdiskparted实用程序。让我们通过列出 Ubuntu 主机上第一个磁盘上的分区来看看已经有哪些分区(参见清单 9-2 )。因为只允许 root 用户读写原始磁盘设备,所以需要使用sudo

  • 引导分区的物理分区
  • 容纳其他分区的扩展分区
  • 与 LVM 一起使用的分区
$ sudo fdisk -l /dev/sda
Disk /dev/sda: 8 GiB, 8589934592 bytes, 16777216 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x105922fd

Device     Boot   Start      End  Sectors  Size Id Type
/dev/sda1  *       2048   999423   997376  487M 83 Linux
/dev/sda2       1001470 16775167 15773698  7.5G  5 Extended
/dev/sda5       1001472 16775167 15773696  7.5G 83 Linux

As you can see in the output of Listing 9-2), the installer created three partitions:

Listing 9-2.Listing Partitions with fdisk

你不想修改你的系统盘,但是假设你买了一个新硬盘,需要分区,你就可以开始用它存储数据了。首先,您需要检查磁盘是否被操作系统检测到,以及它的设备名称是什么。内核在启动时会打印它检测到的所有设备的信息,一旦登录,您就可以通过dmesg命令访问这些信息。

$ dmesg | grep sd
[    1.838874] sd 2:0:0:0: [sda] 16777216 512-byte logical blocks: (8.59 GB/8.00 GiB)
[    1.839510] sd 2:0:0:0: [sda] Write Protect is off
[    1.839824] sd 2:0:0:0: [sda] Mode Sense: 00 3a 00 00
[    1.839834] sd 2:0:0:0: Attached scsi generic sg1 type 0
[    1.840183] sd 2:0:0:0: [sda] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[    1.842304]  sda: sda1 sda2 < sda5 >
[    1.842784] sd 2:0:0:0: [sda] Attached SCSI disk
[    2.178862] sd 3:0:0:0: [sdb] 16777216 512-byte logical blocks: (8.59 GB/8.00 GiB)
[    2.179508] sd 3:0:0:0: [sdb] Write Protect is off
[    2.179863] sd 3:0:0:0: [sdb] Mode Sense: 00 3a 00 00
[    2.179874] sd 3:0:0:0: Attached scsi generic sg2 type 0
[    2.180268] sd 3:0:0:0: [sdb] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA
[    2.181498] sd 3:0:0:0: [sdb] Attached SCSI disk
[   25.702112] EXT4-fs (sda1): mounting ext2 filesystem using the ext4 subsystem
[   25.711836] EXT4-fs (sda1): mounted filesystem without journal. Opts: (null)

通过使用grep只显示包含sd的行,您可以将输出限制为关于 SCSI 磁盘子系统的信息。

Dmesg and the Kernel Ring Buffer

内核将关于它正在做什么的消息写入内核环形缓冲区。这个缓冲区保存一定量的消息,当它满了时,新的消息进来,旧的消息被丢弃。内核并不认为会有一个日志守护进程将这些消息写到一个文件中,例如在系统启动时。因此,内核将其所有消息写入环形缓冲区,您可以通过dmesg访问这些消息。

内核的环形缓冲区包含诸如内核是如何被调用的,内核找到的硬件,以及一旦找到它它做了什么。您可以使用dmesg找到您的网络设备以及您的磁盘是如何配置的。它还可以显示内核某个组件出现严重故障或问题的时间。

一些对dmesg有用的选项有:

  • -C 清除内核环形缓冲区
  • -H 人类可读
  • -T 人类可读时间戳
  • -w 关注或等待新消息
  • -l 仅显示特定级别的消息,如信息、紧急、错误、紧急

dmesg的输出中,我们可以看到系统已经检测到两个磁盘,sdasdb。当它检测到sda时,它也发现了分区sda1sda2sda5。分区sda5 (<sda5>)周围的尖括号表示这是一个逻辑分区。另一个磁盘是新的,没有分区表(sdb),所以让我们使用gdisk命令创建一个。

$ sudo gdisk /dev/sdb
sudo gdisk /dev/sdb
GPT fdisk (gdisk) version 1.0.1

Partition table scan:
  MBR: not present
  BSD: not present
  APM: not present
  GPT: not present

Creating new GPT entries.

Command (? for help): ?
B       back up GPT data to a file
c       change a partition's name
d       delete a partition
i       show detailed information on a partition
l       list known partition types
n       add a new partition
o       create a new empty GUID partition table (GPT)
p       print the partition table
q       quit without saving changes
r       recovery and transformation options (experts only)
s       sort partitions
t       change a partition's type code
v       verify disk
w       write table to disk and exit
x       extra functionality (experts only)
?       print this menu

如果您打算使用 MBR 分区表,那么gdisk实用程序反映了您可以在fdisk中使用的许多选项。如果我们使用gdisk实用程序中的?选项,就会得到帮助输出。让我们快速浏览一下这些选项。

您可以使用l选项列出设备上的分区。d选项允许你删除一个(小心,删除分区是危险的)。要删除当前的分区表并创建一个新的空分区表,使用o选项,这比较危险,但有时需要删除分区表。此选项将破坏您磁盘上的所有分区。

要创建一个分区,使用n选项,这将启动一个向导来引导您完成创建过程,您马上就会看到。

要列出当前分区表,请按 p。这将按分区表在内存中的位置列出,而不是按它在磁盘上的位置列出。

如果您做出了不想保存的更改,请按 q。这将退出gdisk,而不会将修改后的分区表写入磁盘。

分区还包含关于它们所包含的文件系统类型的信息。我们从l选项获得的十六进制标识符可以使用t选项进行设置。

当您对新的分区图满意时,您可以按 w 键将它保存到磁盘。最后,x 允许您访问高级的gdisk选项,例如恢复和转换选项、更改 GUID、更改磁盘几何和移动分区中包含的数据。我们不包括这些很少使用的专家选项的使用。

现在按 p 键打印当前磁盘上的分区列表。你会发现它是空的。通常,我们建议在一个数据存储磁盘上只创建一个分区,但是让我们来玩玩这个磁盘,创建几个分区。我们将创建一个 4 GiB 分区和两个 2 GiB 分区。

首先创建一个 2gb 大小的分区,按 n。

Command (? for help): n
Partition number (1-128, default 1):
First sector (34-16777182, default = 2048) or {+-}size{KMGTP}:
Last sector (2048-16777182, default = 16777182) or {+-}size{KMGTP}: 2G
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300):
Changed type of partition to 'Linux filesystem'

首先要求您输入分区号,一个介于 1 和 128 之间的数字。我们将采用默认值 1。接下来要求我们选择磁盘扇区,每个扇区为 512 字节。现代系统在扇区边界对齐,这里我们选择从扇区 2048 或 1024 千比字节开始。接下来,输入2G表示您想要创建一个 2 GiB 大小的分区。最后,我们选择一个分区 ID,用十六进制代码表示,缺省值是 8300,即 Linux 文件系统。

重复该过程以创建另一个分区。

Partition number (2-128, default 2):
First sector (34-16777182, default = 4196352) or {+-}size{KMGTP}:
Last sector (4196352-16777182, default = 16777182) or {+-}size{KMGTP}: +2G

对于分区 2,我们再次采用分区号和第一个扇区的默认值。对于最后一个扇区,我们必须添加一个+2G,这样该实用程序将额外添加 2 GiB。

为了创建最后一个 4gb 分区,我们再次选择分区号、第一个扇区和最后一个扇区的默认值。

Partition number (3-128, default 3):
First sector (34-16777182, default = 8390656) or {+-}size{KMGTP}:
Last sector (8390656-16777182, default = 16777182) or {+-}size{KMGTP}:

这将创建一个分区所有剩余的磁盘空间。现在我们可以通过选择 p 选项来打印我们一直在做的事情。

Command (? for help): p
Disk /dev/sdb: 16777216 sectors, 8.0 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 1C42CAB1-754B-4B21-A7A9-D7CE87C8965B
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 16777182
Partitions will be aligned on 2048-sector boundaries
Total free space is 4061 sectors (2.0 MiB)

Number   Start (sector)      End (sector)      Size      Code     Name
   1               2048           4194304      2.0 GiB   8300     Linux filesystem
   2            4196352           8390655      2.0 GiB   8300     Linux filesystem
   3            8390656          16777182      4.0 GiB   8300     Linux filesystem

我们还没有把这个分区表写到实际的磁盘上;如果这看起来不对,我们可以使用q选项放心退出。如果我们满意,我们选择w选项来编写我们的 GPT 表。

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/sdb.
The operation has completed successfully.

前面我们提到了分区类型和 id。Linux 本身通常不关心分区类型,但是为了使管理更容易,我们建议您更改分区类型以匹配预期用途。正如我们已经说过的,分区 id 是一个十六进制代码。您可以通过发出一个l来获得所有可能选项的列表。在表 9-1 中,我们给你一个与 Linux 相关的列表。

表 9-1。

Linux Partition IDs and Types

| 十六进制代码/分区 id | 分区类型 | | --- | --- | | Eight thousand two hundred | `Linux swap` | | Eight thousand three hundred | `Linux filesystem` | | Eight thousand three hundred and one | `Linux reserved` | | Eight thousand three hundred and two | `Linux /home` | | Eight thousand three hundred and three | `Linux x86 root (/)` | | Eight thousand three hundred and four | `Linux x86-64 root (/)` | | Eight thousand three hundred and five | `Linux ARM64 root (/)` | | Eight thousand three hundred and six | `Linux /srv` | | Eight thousand three hundred and seven | `Linux ARM32 root (/)` | | 8e00 | 使用 |

如果您想改变您的分区类型,您可以通过发出以下命令来实现。我们将在刚刚分区的/dev/sdb驱动器上再次查看 gdisk。在这里,我们将把第一个 2 GiB 分区从 Linux 文件系统类型改为 Linux 交换。

$ sudo gdisk /dev/sdb
Command (? for help): t
Partition number (1-3): 1
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): 8200
Changed type of partition to 'Linux swap'

这里,我们使用 gdisk 来管理分区表,并发出了t选项来更改分区的类型代码。我们选择分区 1,它显示我们当前的类型,“Linux 文件系统”。接下来,我们输入代码 8200,它现在已经将类型更改为 Linux swap。当我们打印结果时,我们会看到以下内容:

Command (? for help): p
Disk /dev/sdb: 16777216 sectors, 8.0 GiB
Logical sector size: 512 bytes
Disk identifier (GUID): 1C42CAB1-754B-4B21-A7A9-D7CE87C8965B
Partition table holds up to 128 entries
First usable sector is 34, last usable sector is 16777182
Partitions will be aligned on 2048-sector boundaries
Total free space is 4061 sectors (2.0 MiB)

Number  Start (sector)  End (sector)  Size        Code  Name
   1    2048            4194304       2.0 GiB     8200  Linux swap
   2    4196352         8390655       2.0 GiB     8300  Linux filesystem
   3    8390656         16777182      4.0 GiB     8300  Linux filesystem

我们必须选择w将此更改写入表格,选择q退出。内核现在重新加载分区图,并为您的分区创建新的设备节点。您将在dmesg的输出中看到磁盘检测例程已经运行并找到了您的新分区。您还可以检查它们的设备节点现在是否存在于磁盘上。

$ ls -l /dev/sdb*
brw-rw---- 1 root disk 8, 16 Jun 16 23:56 /dev/sdb
brw-rw---- 1 root disk 8, 17 Jun 16 23:56 /dev/sdb1
brw-rw---- 1 root disk 8, 18 Jun 16 23:56 /dev/sdb2
brw-rw---- 1 root disk 8, 19 Jun 16 23:56 /dev/sdb3

有时内核无法重新读取分区表,这意味着在重新启动主机之前,您无法访问新的分区设备文件。如果您正在编辑的磁盘上的其中一个分区仍处于装载状态,则会发生这种情况。若要避免重新启动,请确定您正在分区的磁盘上没有装载任何分区。我们将在这一章的后面讨论安装。

Note

您还可以通过运行partprobe命令让内核重新检测分区——无需重启。

另一个创建和删除分区的工具是 parted。与gdiskfdisk不同,该实用程序允许您编辑分区的大小和顺序。我们建议你不要走用parted调整分区大小的道路,因为这些操作可能是灾难性的,而是使用 LVM。我们将在本章后面详细介绍 LVM。更多关于parted的信息,请访问 www.gnu.org/software/parted/index.shtml

Caution

调整分区大小会导致不可恢复的数据丢失。务必首先备份您的数据!

Gibibytes VS. Gigabytes

当硬盘制造商为其产品做广告时,它希望可用的存储空间看起来尽可能大,因此它将每千兆字节计算为 1,000 兆字节,依次是 1,000,000 千字节,即 1,000,000,000 字节。

但是因为计算机上的所有计算都是通过二进制算术来完成的,所以实际的乘法值是 1024(gibi 的意思是 2 30 )。但是,如果一家存储制造商使用了这一因素,那么与竞争对手相比,它的设备似乎会更小,所以它不会。

为了避免这些计算大小的方法之间的混淆,使用 1,024 因子的值创造了新的术语:kibibyte、mebibyte、gibibyte 等等。它们用 KiB、MiB、GiB 等表示。Linux 文件系统工具使用 1,024 因子,因此如果您购买一个 500 GB 的磁盘,当通过 Linux 查看时,它的大小将总是小于 500 GiB。

更多信息,请参见 http://en.wikipedia.org/wiki/Gigabyte .

文件系统

您现在已经创建了分区,但是还没有准备好使用它们。接下来您需要做的是创建一个文件系统。你可能知道这就是格式化。

文件系统有点像库。它存储大量数据,并有一个目录,以确保您可以找到您正在寻找的东西。过道和货架的布局以及目录的设计决定了查找和检索任何特定信息所需的时间。创建文件系统就像初始化目录并将书架移到一个空的库中。

正如没有适合所有图书馆的最佳过道和书架布局一样,也没有适合所有用途的“最佳”文件系统。我们不会涉及太多细节,但是让我们看看一些最常用的 Linux 文件系统。表 9-2 列出了它们的主要特征。

表 9-2。

Linux Filesystems and Their Main Features

| 文件系统 | 特征 | | --- | --- | | Ext2 | 稳定,通用,可收缩或膨胀 | | Ext3 | 稳定,通用,快速恢复,可收缩或膨胀 | | Ext4 | 稳定、通用、快速恢复,比 ext3 有所改进 | | XFS | 稳定,通用,恢复快,可在线扩展 | | btr 护堤 | 不稳定、容错、写时拷贝(COW)、池化和多设备跨转 |

ext2 和 ext3 文件系统是较老的文件系统。这些文件系统的一般用途是在其上存储许多小文件。对于电子邮件存储、网站存储或 office 文件存储来说,它们是文件系统的良好选择,因为这些文件通常由许多大小高达数百千字节的文件组成。在较新的系统上,你很少看到 ext3,但是在 Ubuntu 16.04 上,默认情况下,你会看到 ext2 作为/boot分区的文件系统。

ext2 和 ext3 的主要区别之一是日志支持。对于 ext2,如果发生崩溃,在可以再次挂载磁盘之前,fsck 会等待很长时间。为了解决这个问题,创建了日志文件系统。Ext3、ext4 和 XFS 就是这样的日志文件系统,因此没有 ext2 那样长的恢复等待时间。有关更多信息,请参见侧栏“日志文件系统”。

有了 ext3 的经验教训,ext4 得到了进一步的发展。它提供了一些 ext3 中没有的特性,比如在线碎片整理、更好的日志可靠性和更快的文件系统检查。Ext4 旨在成为一个性能卓越的全方位文件系统。它可以支持高达 1 兆字节的卷和 16 兆字节的最大文件大小。这是 Ubuntu 16.04 发行版的默认选择。

存储视频、大型图像或数据库文件的另一个选择是 XFS 文件系统。它提供了一些与 ext4 相同的优点;但是,您不能收缩 XFS 分区(在线)。它的性能不亚于 ext4。它最多可以支持 8ex bibyte 和 8ex bibyte 的文件大小。这是 CentOS 7 的默认选择。

最后,Btrfs 是一个更新的文件系统,具有与 XFS 和 ext4 不同的特性。首先,它可以支持大得离谱的卷(16 兆字节)和相同的最大文件(16 兆字节)。在如此大的规模下,ext4 和 XFS 上的日志记录变得非常慢而且不可能。它旨在自然地支持快照和池化等操作和组织。它还具有自动碎片整理和清理等功能,使用校验和来自动检测和纠正错误。根据工作负载的不同,Btrfs 可能是一个不错的选择,而且在一定规模下,它是唯一的选择。由于 Raid5/6 配置中最近出现的一些写入漏洞问题,我们在表 9-2 中将它列为不稳定。查看此页面以了解文件系统的最新状态: https://btrfs.wiki.kernel.org/index.php/Status

Note

Btrfs 不是唯一的大规模文件系统,但它是 Linux 默认安装中可用的文件系统。像 ZFS 这样的其他软件也很受欢迎,性能也很好,但是 ZFS 由于其许可证的原因不能再发行(它不能成为 Linux 发行版的一部分)。尽管如此,您仍然可以在 Linux 上使用它;不过需要自己下载安装: http://zfsonlinux.org/

您可以在 http://en.wikipedia.org/wiki/List_of_file_systemshttp://en.wikipedia.org/wiki/Comparison_of_file_systems 找到文件系统的详尽列表及其特性对比。

Journaled Filesystems

想象一下,在一个图书馆里,一本书被归还后,图书管理员会走开,在书架上找一个空的地方放书,然后更新目录,然后回到前台处理下一本书——在此期间,任何人都不可能借到书。还书的顾客也可能会想,仅仅因为他们站在图书馆里,图书馆目录就知道他们已经还书了。这不是一个有效的系统,顾客可能会排队等着还书,或者更糟糕的是,把书放在地上就走了。

有了退书滑槽,这个问题就可以解决了。一旦书被放入返回槽,顾客就可以回去工作了,因为他们知道书已经被图书馆安全地接受了。当图书馆不忙于人们借阅新书时,图书管理员可以处理还书。即使图书馆关门前没有处理好斜道里的书,它们也不会丢失。他们第二天还会在滑槽里。

日志文件系统利用了循环缓冲区,它的工作方式有点像一个带有书籍滑道的图书馆。这是文件系统上的一个区域或日志,保存尚未提交到文件系统存储主要部分的更改。任何需要写入磁盘的信息都放在日志中,然后当操作系统有空闲时间时,再放在磁盘的最终位置。同样,如果计算机崩溃,日志中的数据也不会丢失。如果系统崩溃,可以在重新装载磁盘后重新应用日志。这有助于防止应用部分更改,并在系统出现故障时避免损坏。

不过,我们的比喻在这里失效了。在我们的文件系统库中,人们也可以从还书槽借书,如果还书槽太满,图书管理员可以忽略想借书的人。

大多数现代文件系统使用日志,尽管有些文件系统仅将日志用于文件元数据。像 Btrfs 这样的文件系统可以用不同的方式处理元数据。您可以选择不同于文件数据的元数据布局(例如,元数据为 raid1,数据为 raid10)。对于频繁更新的文件数据,Btrfs 使用日志树,这是一个按子卷记录更改的日志,有助于在崩溃或重新启动时保持一致性。

创建交换文件系统

我们将使用您之前创建的第一个分区/dev/sdb1作为交换分区。为这个文件系统选择文件系统很容易,因为只有一种交换文件系统格式。让我们首先使用mkswap命令来设置它,如清单 9-3 所示。

$ sudo mkswap /dev/sdb1
Setting up swapspace version 1, size = 2 GiB (2146430976 bytes)
no label, UUID=6d0ce2f6-f9f6-4ac2-91f6-3099a40d5624
Listing 9-3.Setting Up Swap Space

您正在使用mkswap实用程序将/dev/sdb1标记为交换空间。您可以使用生成的 UUID 在/etc/fstab文件中添加一个条目,该文件列出了将在主机上使用的所有文件系统(参见侧栏“UUID”了解什么是 UUID)。我们将在本章的后面回到/etc/fstab文件。从技术上讲,您没有格式化分区;相反,您正在编写少量的信息向内核表明它可以用作交换空间。

您可以通过swapon命令立即激活新的交换分区。这个命令告诉内核它可以使用指定的分区作为交换空间。

$ sudo swapon /dev/sdb1

该命令将完成,但不打印任何内容,但是您可以检查dmesg以了解发生了什么。将输出输入tail,将显示的行数限制在指定的数量。

$ sudo dmesg | tail -n 1
[13066.031700] Adding 2096124k swap on /dev/sdb1\.  Priority:-2 extents:1 across:2096124k FS

检查交换的另一种方法是查看free命令是否报告了交换空间。指定-h选项,以人类可读的形式显示尺寸。

$ sudo free -h
         total       used     free      shared  buff/cache   available
Mem:     992M        520M     62M       12M     409M         321M
Swap:    3.0G        0B       3.0G

或者,您可以使用swapon命令:

$ swapon –s
Filename        Type               Size           Used        Priority
/dev/dm-1       partition          3145728000        0              -1

该命令报告了总共 3.0 G 的交换空间,这是我们已经拥有的原始 1 G 加上我们刚刚添加的 2 G。当我们讨论绩效管理时,我们将在第十七章中回到free命令。

UUID

在安装软件时或在某些网站的 URIs 中,您可能会看到长而随机的十六进制字符串,如“6 d0ce 2 f 6-f9f 6-4 ac2-91 F6-3099 a40d 5624”。这些字符串是通用唯一标识符(UUIDs)。

UUIDs 提供了一种方便且计算成本低廉的识别信息的方法,而不需要检查生成的 ID 是否已经在使用。因为 UUIDs 是随机或半随机生成的,所以它们很难猜测,因此也能提供一点安全性。

在 Linux 上,UUIDs 越来越多地被用来区分 RAID、逻辑卷和文件系统的组件。它使我们能够管理设备,并使它们在重启后保持不变。虽然标记设备也可以达到这个目的,但是 UUIDs 不太可能发生命名冲突。

你可以在 http://en.wikipedia.org/wiki/Universally_Unique_Identifier 了解更多。

创建 Ext4 分区

对于您的数据分区,从另一个新的 2 GiB /dev/sdb2 partition开始。您将使用mkfs.ext4实用程序将其格式化为 ext4,如清单 9-4 所示。如果你想创建一个 ext2 文件系统,有时用于创建一个引导分区,只需运行mkfs.ext2即可。

$ sudo mkfs.ext4 –L mail /dev/sdb2
mke2fs 1.42.9 (28-Dec-2013)
Filesystem label=mail
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
Stride=0 blocks, Stripe width=0 blocks
131072 inodes, 524288 blocks
26214 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=536870912
16 block groups
32768 blocks per group, 32768 fragments per group
8192 inodes per group
Superblock backups stored on blocks:
       32768, 98304, 163840, 229376, 294912

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

Listing 9-4.Creating an Ext4 Filesystem

在清单 9-4 中,我们创建了一个 ext4 文件系统,并使用-L参数指定了一个标签。这个标签将允许您通过标签名来引用分区,而不是设备名或 UUID。在没有太多格式化分区的系统上(命名冲突的可能性更小),使用标签有助于提高可读性。在这种情况下,我们为将要使用的内容选择了一个标签。

使用 Ext 文件系统,您可以看到一系列关于文件系统大小和存储空间如何分配的统计数据。在输出中,您可以看到“Block size”、“Maximum filesystem blocks”和“Inodes”的设置,这些设置描述了您的文件系统是如何设置的。请看侧栏“块和索引节点”中对这些的简短解释。值得注意的是为超级用户和超级数据块备份保留的数据块。

26214 blocks (5.00%) reserved for the super user
...<snip>...
Superblock backups stored on blocks:
       32768, 98304, 163840, 229376, 294912

超级块是文件系统元数据的一部分。它包含关于文件系统的信息,例如文件系统的大小、文件系统中的可用空间量以及文件系统中可以找到数据的位置。如果发生崩溃,这个超级块被损坏,您将无法确定文件系统的哪些部分包含您的数据。为了在出现这种问题时帮助您,超级块的几个备份副本被保存在众所周知的块号中。我们将在本章的后面重新讨论恢复。

超级用户百分比的保留块的存在使得普通用户不能将文件系统填充到超级用户(root)不能再登录的程度,或者作为root用户运行的服务不能将数据写入磁盘。

5%的限制是历史的,适合于根文件系统'/',它通常不会大于几千兆字节。但是,当您使用 1 TiB 文件系统时,这一限制相当于 50 GiB 的空间,您不能用它来存储用户数据,因此在数据存储卷上更改或删除它是有意义的。

您可以在创建文件系统时为mkfs. ext4指定-m 0选项,以将保留块的百分比设置为0,或者您可以稍后更改该值(稍后将详细介绍)。

Blocks and Inodes

当您创建文件系统时,可用磁盘空间被划分为特定大小的单元。这些单元称为块,默认情况下,它们的大小为 4 KB。

一个数据块只能容纳一个文件或一个文件的一部分,因此一个 1 KB 的文件仍然会用完整个数据块,也就是 4 KB 的磁盘空间,浪费 3 KB 的存储空间。较大的文件分布在多个块中。如果您主要存储小于 4 KB 的文件,您可以选择为您的文件系统使用不同的、更小的块大小。同样,如果您的文件预计比 4 KB 大得多,您可以选择更大的块大小。

为特定任务设置服务器时,试验和测试不同块大小的性能非常重要。数据库、共享文件系统和邮件系统都有不同的块大小最佳点来获得最佳性能。如果您可以模拟预期的工作负载,那么您应该对文件系统的最佳调优有更好的想法。

Inodes 是符合 posix 标准的文件系统存储元数据的地方,比如文件或目录的创建和修改日期、权限和所有权,以及指向哪些块包含实际文件数据的指针。这意味着文件系统只能包含与它拥有的 inodes 一样多的文件和目录。因此,在 Ext 文件系统的情况下,块很小而文件很多,您可以在用完磁盘空间之前用完索引节点。要了解关于 inodes 的更多信息,请参见 http://en.wikipedia.org/wiki/Inode

调整 ext2、ext3 和 ext4 文件系统选项

要在创建后更改 ext2、ext3 和 ext4 文件系统参数,可以使用 tune2fs 实用程序。要获得可用选项的概述,首先运行不带任何参数的实用程序。也可以通过man tune2fs调出整本手册。

$ tune2fs
tune2fs 1.42.13 (17-May-2015)
Usage: tune2fs [-c max_mounts_count] [-e errors_behavior] [-g group]
       [-i interval[d|m|w]] [-j] [-J journal_options] [-l]
       [-m reserved_blocks_percent] [-o [^]mount_options[,...]] [-p mmp_update_interval]
       [-r reserved_blocks_count] [-u user] [-C mount_count] [-L volume_label]
       [-M last_mounted_dir] [-O [^]feature[,...]]
       [-Q quota_options]
       [-E extended-option[,...]] [-T last_check_time] [-U UUID]
       [ -I new_inode_size ] device

虽然没有明确说明,但是-l参数列出了当前的文件系统选项。让我们在您的新 ext4 分区上运行它(参见清单 9-5 )。

$ sudo tune2fs -l /dev/sdb2
tune2fs 1.42.13 (17-May-2015)
Filesystem volume name:   mail
Last mounted on:          <not available>
Filesystem UUID:          71bd5774-33cb-491b-8ffe-49cb33935001
...<snip>...
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype extent flex_bg sparse_super large_file huge_file uninit_bg dir_nlink extra_isize
Filesystem flags:         signed_directory_hash
Default mount options:   user_xattr acl
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              131072
Block count:              524288
Reserved block count:     26214
Free blocks:              498900
Free inodes:              131061
First block:              0
Block size:               4096
Fragment size:            4096
...<snip>...
Last mount time:          n/a
Last write time:          Sun Jun 19 10:42:04 2016
Mount count:              0
Maximum mount count:      -1
Last checked:             Sun Jun 19 10:42:04 2016
Check interval:           0 (<none>)
...<snip>...
Journal backup:           inode blocks
Listing 9-5.Displaying Ext2, Ext3, or Ext4 Filesystem Options

显示了很多信息,但我们最感兴趣的是文件系统 UUID 和状态,它们告诉我们如何引用文件系统及其健康状况。“错误行为”表明如果有文件系统错误会发生什么。在这种情况下,如果我们检测到错误,我们会“继续”,但其他选项是“remount-ro”(以只读方式重新挂载文件系统)或“panic”,这会导致内核崩溃,从而暂停系统。对诊断容量问题有用的其他信息是“空闲信息节点”和“空闲块”“上次写入时间”、“上次装载时间”和“上次装载时间”也很有用。

Note

当我们讨论容量规划和性能时,我们将在第十七章中更仔细地研究一些文件系统特性。

我们现在将使用 tune2fs 将保留块百分比设置为 0,因为我们不需要该分区上的保留空间。

$ sudo tune2fs -m 0 /dev/sdb2
tune2fs 1.42.9 (28-Dec-2013)
Setting reserved blocks percentage to 0% (0 blocks)

表 9-3 列出了你最有可能使用的tune2fs选项。

表 9-3。

Commonly Used tune2fs Options

| [计]选项 | 功能 | | --- | --- | | `-c N` | 将文件系统检查强制执行前的装载次数设置为 N | | `-l` | 列出了当前的文件系统选项 | | `-m N` | 将保留块百分比设置为所有块的 N% | | `-r N` | 将保留块数设置为 N | | `-j` | 在这个文件系统上创建一个日志(将 ext2 转换成 ext3) | | `-L label` | 将标签“label”分配给文件系统 | | `-O` `feat` | 打开或关闭文件系统功能“feat” |

Note

当我们讨论性能和容量规划时,我们将在第十七章中回到-O选项和高级文件系统特性。

XFS 文件系统

XFS 文件系统最初是专有的和封闭源代码的。XFS 是由硅图形公司为其 IRIX 操作系统开发的。

几年前,XFS 的文件系统驱动程序是开源的,IRIX 帮助将其集成到 Linux 内核中,因为当时 Linux 缺少日志文件系统。社区热情地接受了这些新的开源文件系统,因为它们都提供了新的特性和出色的性能。现在它们在 Linux 平台上被广泛接受和支持,包括作为 CentOS 7 的默认设置。

XFS

您已经创建了一个 ext4 分区来存储一些小文件。让我们使用 XFS 文件系统格式化另一个分区。为此,我们将使用mkfs.xfs工具。根据发行版的不同,您可能没有管理 XFS 文件系统所必需的实用程序。这些实用程序是由xfsprogs包提供的,在您开始之前,您应该已经安装了它。在 Ubuntu 上,您可以按如下方式安装它们:

$ sudo aptitude install xfsprogs

在 CentOS 上,您可以使用命令(尽管它是作为默认文件系统安装的)

$ sudo yum install xfsprogs

安装包之后,您可以使用默认选项创建您的文件系统,如清单 9-6 所示。

$ sudo mkfs.xfs /dev/sdb3
meta-data=/dev/sdb3 isize=512    agcount=4,     agsize=262079 blks
                 =  ectsz=512    attr=2,        projid32bit=1
                 =  crc=1        finobt=1,      sparse=0
            data =  bsize=4096   locks=1048315, imaxpct=25
                 =  sunit=0      swidth=0       blks
          naming =  version 2    bsize=4096     ascii-ci=0 ftype=1
             log =  internal log bsize=4096     blocks=2560, version=2
                 =  sectsz=512   sunit=0 blks,  lazy-count=1
        realtime =  none         extsz=4096     blocks=0, rtextents=0
Listing 9-6.Creating an XFS Filesystem

创建文件系统时,会显示一些关于其配置的信息。我们将在第十七章中进一步利用这些信息来研究性能和容量规划。

所有这些选项,例如控制块大小和日志大小,都可以在创建文件系统时设置,但是mkfs.xfs工具会根据需要格式化的分区的大小选择合理的默认值。

Note

XFS 不会为 root 用户保留 5%的可用空间,也不会在特定时间过后自动强制进行文件系统检查。

XFS 文件系统可以通过几个命令来管理。这些命令以 xfs_ 开头,您可以通过键入 xfs_ 并按 tab 键两次来查看可用的选项。表 9-4 显示了您感兴趣的主要产品。

表 9-4。

Common xfs_ Commands

| 命令 | 目的 | | --- | --- | | xfs_repair | 帮助修复受损或损坏的文件系统 | | xfs_growfs | 扩展 XFS 文件系统。 | | xfs_freeze | 创建快照时很有用。 |

Btrfs 文件系统

我们已经解释了 Btrfs 文件系统的许多好处,现在我们将向您展示如何创建和管理它。如果您还没有安装实用程序,您可以通过安装 btrfs-progs 包来完成,类似于我们安装 XFS 包的方式。

我们说过 Btrfs 使用 COW(或写时复制)。但那是什么?当数据被修改时,数据被复制、修改,然后写入新的空闲位置,而不是在先前的数据位置上写入修改的数据。然后,元数据(文件的位置)以同样的方式更新,以反映数据的新位置。

我们将快速演示如何创建 Btrfs 分区并挂载它。我们有一个连接到主机的新磁盘。我们通过 dmesg 发现这个新磁盘被赋予了设备/dev/sdc。我们将把整个磁盘用于该分区。创建 Btrfs

$ sudo mkfs.btrfs /dev/sdc
btrfs-progs v4.4
See http://btrfs.wiki.kernel.org for more information.

Label:              (null)
UUID:               e1c6cbb0-4fbf-4a61-a912-0a9cda611128
Node size:          16384
Sector size:        4096
Filesystem size:    8.00GiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         DUP             417.56MiB
  System:           DUP              12.00MiB
SSD detected:       no
Incompat features:  extref, skinny-metadata
Number of devices:  1
Devices:
   ID      SIZE     PATH
    1      8.00GiB  /dev/sdc

我们现在可以简单地挂载这个分区了。我们已经创建了一个名为/data1的目录,并将它挂载在那里。

$ mount /dev/sdc /data1

我们可以看到文件系统已经挂载。

$ df -h /data1
Filesystem      Size   Used    Avail   Use%    Mounted on
/dev/sdc         8.0G   60M    7.2G   1%         /data1

Btrfs 附带了一个管理 Btrfs 文件系统的实用程序。我们将向您展示该实用程序的一些特性:首先是如何调整文件系统的大小。我们将把我们的文件系统减少 2 GiB,然后再把它加回来。

$ sudo btrfs filesystem resize -2G /data1
Resize '/data1' of '-2G'

Btrfs 实用程序的一个子命令是filesystem。这里我们传递了选项resize -2G /data1,它告诉实用程序将文件系统减少 2 GiB。使用filesystem show子命令,我们可以看到结果。

$ sudo btrfs filesystem show /data1
Label: none  uuid: e1c6cbb0-4fbf-4a61-a912-0a9cda611128
       Total devices 1 FS bytes used 42.03MiB
       devid    1 size 6.00GiB used 1.64GiB path /dev/sdc

我们现在要添加 2 个 GiB。所以我们简单地使用下面的:

$ sudo btrfs filesystem resize +2G /data1
Resize '/data1' of '+2G'

在下一个示例中,我们有四个连接到主机的备用磁盘。我们将把这些磁盘用作一个组合磁盘。dmesg 的输出显示它们已被分配给以下设备:

[   47.815498]  sdb: unknown partition table
[   47.833520]  sdc: unknown partition table
[   47.848420]  sdd: unknown partition table
[   47.868448]  sde: unknown partition table

有了 Btrfs,我们可以用 RAID 对设备进行分组。它使用多设备文件系统来做到这一点,我们将在“RAID”一节中详细讨论我们可以使用的一种可能的 RAID 类型是 RAID 10。这种 RAID 类型为我们提供了镜像和条带化,这意味着设备是成对镜像的,然后进行条带化。这将给我们带来冗余和速度。

为了创建 Btrfs RAID 分区,我们发出以下命令:

$ sudo mkfs.btrfs -d raid10 -m raid10  /dev/sdb  /dev/sdc /dev/sdd /dev/sde
btrfs-progs v3.19.1
See http://btrfs.wiki.kernel.org for more information.

Turning ON incompat feature 'extref': increased hardlink limit per file to 65536
Turning ON incompat feature 'skinny-metadata': reduced-size metadata extent refs
adding device /dev/sdc id 2
adding device /dev/sdd id 3
adding device /dev/sde id 4
fs created label (null) on /dev/sdb
       nodesize 16384 leafsize 16384 sectorsize 4096 size 32.00GiB

这里我们发出了mkfs.btrfs命令。我们已经指定了为数据块组设置配置文件的–d选项。–m选项设置元数据块组的配置文件。然后,我们指定了正在使用的四个磁盘。

最后,它说我们已经在/dev/sdb上创建了一个 fs 标签。让我们获取该设备的 UUID,以便将其放入 fstab 中。

$ sudo blkid /dev/sdb
[sudo] password for jsmith:
/dev/sdb: UUID="0cd0e135-feb8-4f99-a973-5751549d2e4f" UUID_SUB="4d327afb-1330-43e5-b392-0e676ebab1b5" TYPE="btrfs"

我们将我们的行添加到 fstab 中,如下所示:

UUID=0cd0e135-feb8-4f99-a973-5751549d2e4f  /data btrfs defaults 0 0

让我们知道使用 mount 命令挂载我们的磁盘:

$ sudo mount /data2

最后,让我们用df –h命令来看看我们在/data2分区上有多少空间:

$ df –h /data2
Filesystem                          Size    Used     Avail  Use% Mounted on
/dev/sdb                             16G     18M      14G     1%    /data2

我们有四个 8 GiB 的磁盘组合在一起,形成一个 14 GiB 的可用分区。我们现在将向您简要展示创建子卷和快照的能力。

子卷是一个“POSIX 命名空间”或容器。它不是像/dev/sda或 LVM(逻辑卷管理)逻辑卷那样的块设备。也就是说,您不能单独挂载它或者在它上面创建一个不同的文件系统,但是您可以将它挂载为一个子卷,Linux 内核可以读写它。

您可以像使用普通目录一样使用子卷。它们具有以下优点:

  • 您可以重命名和删除子卷
  • 您可以轻松快速地拍摄子卷的快照
  • 您可以装载快照
  • 您可以嵌套子体积
  • 您可以对子卷应用配额

我们将创建一个名为 mail 的子卷,并挂载它。

$ sudo btrfs subvolume create /data2/mail
Create subvolume '/data2/mail'

我们现在已经创建了一个名为/srv/mail的目录,我们将在那里挂载我们的邮件子卷:

$ sudo mount -t btrfs -o subvol=mail /dev/sdc /srv/mail

我们现在可以看到该文件系统已经挂载。

$ df -h /srv/mail
Filesystem      Size  Used    Avail  Use%  Mounted on
/dev/sdb         16G   18M      14G    1%   /srv/mail

BtrFS 子体积的最大特点是快照的速度。我们可以创建两种类型的快照:只读子卷快照或可写子卷快照。因为这是一个 CoW 文件系统,所以在我们对数据进行修改之前,我们不会改变磁盘空间。现在,让我们创建子卷的快照,我们可以将它挂载到其他地方,例如在/mnt/snap_mail中。

$ sudo btrfs subvolume snapshot /data/mail /data2/snap_mail

现在将它安装在/mnt/snap_mail上。

$ sudo mount -t btrfs -o subvol=snap_mail /dev/sdc /mnt/snap_mail
$ df -h /mnt/snap_mail
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdb         16G   18M   14G   1% /mnt/snap_mail

快照是子卷的时间点副本。您可以使用它来复制繁忙的文件系统上的数据,或者制作子卷的时间点备份。要制作只读快照,您需要发出btrfs subvolume snapshot –r (vol_target) (vol_dest)

数据共享的文件系统

到目前为止,我们已经讨论了只能由 Linux 访问的文件系统。如果您需要在不同的操作系统之间传输数据——例如,当您的笔记本电脑和客户的机器之间没有网络时——您可能希望使用 Windows 和 Mac OS X 以及 Linux 都可以访问的文件系统。

用于此目的的事实上的标准是 FAT 文件系统,它是由微软为 MS-DOS 开发的。脂肪有几种口味。最新版本是 FAT32,它支持超过 32 GiB 的磁盘大小和高达 4 GiB 的文件大小。

要创建 FAT32 文件系统,可以使用mkfs.vfat实用程序。这个工具在 Ubuntu 和 CentOS 上都由dosfstools包提供,所以你需要确保安装了dosfstools

插入想要格式化的 USB 驱动器后,通过内核日志检查它的设备节点名,如清单 9-7 所示。

$ dmesg
[   52.464662] usb 1-1: new high speed USB device using ehci_hcd and address 2
[   52.887506] usb 1-1: configuration #1 chosen from 1 choice
[   52.967324] usbcore: registered new interface driver libusual
[   52.981452] Initializing USB Mass Storage driver...
[   52.986046] scsi3 : SCSI emulation for USB Mass Storage devices
[   52.987804] usbcore: registered new interface driver usb-storage
[   52.987831] USB Mass Storage support registered.
[   52.988661] usb-storage: device found at 2
[   52.988687] usb-storage: waiting for device to settle before scanning
[   58.982976] usb-storage: device scan complete
[   59.350262] usb 1-1: reset high speed USB device using ehci_hcd and address 2
[   59.772402] scsi 3:0:0:0: Direct-Access     SanDisk  Cruzer
8.01 PQ: 0 ANSI: 0 CCS
[   59.789834] sd 3:0:0:0: [sdg] 15682559 512-byte hardware sectors (8029 MB)
[   59.792747] sd 3:0:0:0: [sdg] Write Protect is off
[   59.792754] sd 3:0:0:0: [sdg] Mode Sense: 45 00 00 08
[   59.792766] sd 3:0:0:0: [sdg] Assuming drive cache: write through
[   59.805772] sd 3:0:0:0: [sdg] 15682559 512-byte hardware sectors (8029 MB)
[   59.815884] sd 3:0:0:0: [sdg] Write Protect is off
[   59.815891] sd 3:0:0:0: [sdg] Mode Sense: 45 00 00 08
[   59.815894] sd 3:0:0:0: [sdg] Assuming drive cache: write through
[   59.816480]  sdg: sdg1
[   59.831448] sd 3:0:0:0: [sdg] Attached SCSI removable disk
[   59.831942] sd 3:0:0:0: Attached scsi generic sg7 type 0
Listing 9-7.Determining the Device Node for a USB Key

在清单 9-7 中,SanDisk Cruzer USB 驱动器被检测为/dev/sdg。一旦您知道 USB 驱动器是哪个设备节点,您就可以创建一个类型为c - W95 FAT32 (LBA)的主分区,然后您可以使用mkfs.vfat格式化这个分区。使用-n选项标记分区,并通过-F 32选项指定您想要一个 FAT32 文件系统。

$ sudo mkfs.vfat -n "USB Key" -F 32 /dev/sdg1
mkfs.vfat 2.11 (12 Mar 2005)

其他文件系统

Linux 有太多不同的文件系统,所以你可能会问为什么我们只讨论了其中的三种。尽管存在许多其他文件系统,但我们认为它们中的大多数不适合或不适合在生产环境中使用。文件系统需要具备的最重要的特性是稳定性,我们讨论的文件系统提供了这一点,以及出色的性能。如果您根据存储的数据类型选择 ext4、XFS 或 Btrfs,您应该会看到出色的可靠性和速度。为您的服务器选择一个更快但不太稳定的文件系统不会有任何帮助,如果您因此需要花费时间每月一次从备份中恢复数据的话。

对于 Linux 内核支持的其他文件系统的简要概述,您可以阅读filesystems手册页。

Note

Linux 可以通过ntfsprogs包中的mkntfs工具创建 NTFS 文件系统。但是,我们建议您不要在 Linux 下使用 NTFS 文件系统来存储数据。

使用您的文件系统

现在,您已经在新磁盘/dev/sdb上创建了分区,并且已经用您选择的文件系统格式化了这些分区。然而,在使用文件系统存储数据之前,您需要挂载它。

正如我们在第四章和本章开始时简要解释的,Linux 上的文件系统不会被分配一个驱动器号。相反,它们被挂载为一个目录,位于根文件系统或子目录下。在第四章中,我们提到过/mnt目录通常被用作临时挂载文件系统的地方。接下来,您将创建一个名为/mnt/data的目录,并将其用于新的 ext4 分区。

$ sudo mkdir /mnt/data

挂载分区是通过mount命令完成的。使用-t选项指定文件系统类型,然后是设备文件,最后是希望文件系统可用的目录。

$ sudo mount -t ext4 /dev/sdb2 /mnt/data/

如果一切顺利,mount命令将不会打印任何信息,而只是退出。要验证分区现在是否已经挂载,请使用df命令。

$ df -h
Filesystem                                  Size   Used    Avail  Use%   Mounted on
udev                                        478M      0     478M    0%   /dev
tmpfs                                       100M   3.3M      96M    4%   /run
/dev/mapper/au--mel--ubuntu--1--vg-root     6.3G   2.6G     3.4G   44%   /
tmpfs                                       497M      0     497M    0%   /dev/shm
tmpfs                                       5.0M      0     5.0M    0%   /run/lock
tmpfs                                       497M      0     497M    0%   /sys/fs/cgroup
/dev/sda1                                   472M   147M     301M   33%   /boot
tmpfs                                       100M      0     100M    0%   /run/user/1000
/dev/sdb2                                   2.0G   3.0M     1.8G    1%   /mnt/data

我们的分区列在输出的底部,所以mount命令成功了。我们将在本章的后面重新讨论df,并更详细地解释这个输出意味着什么。

您还可以通过使用dmesg命令检查内核日志来查看一些更详细的信息。

$ dmesg
[37881.206060] EXT4-fs (sdb2): mounted filesystem with ordered data mode. Opts: (null)

内核检测到一个 ext4 文件系统,并使用默认选项“有序数据模式”挂载它(这意味着我们首先将数据写入主文件系统,然后将元数据提交给日志)。它还启动了一个内核线程,每五秒钟将日志中的数据刷新到文件系统中——我们的图书管理员正在清空图书槽。

看看新挂载的分区内部,我们可以使用 ls 来查看它是否包含任何内容:

$ cd /mnt/data && ls -l
total 16
drwx------ 2 root root 16384 Jun 19 10:42 lost+found

您全新的文件系统包含一个名为lost+found的目录,这个目录不是您创建的!这是一个特殊的目录,存在于所有 ext2、ext3 和 ext4 文件系统中。这个目录由 Linux 的文件系统修复工具使用,我们将在后面的“从故障中恢复”一节中研究它

当您不再需要文件系统时,可以使用umount命令将其从主机上卸载。

$ sudo umount /mnt/data
umount: /mnt/data: target is busy
        (In some cases useful info about processes that
         use the device is found by lsof(8) or fuser(1).)
$ pwd
/mnt/data

这里发生了什么事?命令 umount 拒绝卸载目录,因为它包含正在使用的文件或目录。在这种情况下,这是因为我们当前的工作目录是/mnt/data,当我们在目录中时,我们的主机不能卸载设备——也就是说,我们位于我们试图卸载的目录中!设备繁忙的原因有很多,而且并不总是清楚哪个用户或应用程序打开了哪个文件或目录。为了帮助您找到答案,lsof命令列出了打开的文件和目录:

$ sudo lsof /mnt/data
COMMAND     PID     USER      FD     TYPE   DEVICE  SIZE/OFF   NODE     NAME
bash        2750    jsmith    cwd    DIR      8,18      4096      2     /mnt/data
sudo        3999    root      cwd    DIR      8,18      4096      2     /mnt/data
lsof        4000    root      cwd    DIR      8,18      4096      2     /mnt/data
lsof        4001    root      cwd    DIR      8,18      4096      2     /mnt/data

除了lsof本身,还有一个用户jsmith拥有的bash流程。您可以通过返回到您的主目录来停止使用该目录。键入 cd 和主目录的快捷方式,然后使用lsof再次检查/mnt/data

$ cd ∼
$ lsof /mnt/data

这一次,lsof命令没有返回任何打开的文件和目录,由于目录不再被列为使用中,您现在可以安全地卸载它了:

$ sudo umount /mnt/data

Note

正确卸载文件系统会将您在tune2fs输出中看到的Filesystem state标志设置为clean,因为它会要求内核处理整个日志文件,并确保所有数据都写入磁盘。这可以防止下次主机启动时自动检查文件系统。

当您作为非root用户运行lsof时,它将只列出该用户拥有的进程。其他人可能仍在使用您试图卸载的文件系统上的文件或目录。使用sudo运行lsof进行检查总是一个好主意。

Note

如果某个系统服务正在使用一个已挂载的文件系统,您必须先停止该服务,然后才能卸载该文件系统。

自动化安装

您可能已经注意到,您的其他分区不需要手动挂载。当您启动主机时,它们已经被装载。这是启动过程的一部分。您希望在启动时自动挂载的每个分区都需要在/etc/fstab文件中列出。清单 9-8 显示了来自我们 Ubuntu 主机的一个。

# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system>                  <mount point>   <type>    <options>          <dump>  <pass>
/dev/mapper/au--mel--ubuntu--1--vg-root     /      ext4    errors=remount-ro  0         1
# /boot was on /dev/sda1 during installation
UUID=d036bc4a-6f9b-4989-a377-7778a29bf16c   /boot  ext2     defaults          0         2
/dev/mapper/au--mel--ubuntu--1--vg-swap_1    none  swap     sw                0         0
Listing 9-8.An fstab File

文件中的每一行由六个字段组成,由空格或制表符分隔。这些字段指定了每个文件系统的挂载方式和位置,以及在执行检查时应该做什么。所有以散列符号(#)开始的行都是注释。

文件系统字段包含要挂载的文件系统的设备节点名称。您还可以通过指定LABEL=label或文件系统 UUID 来替换文件系统标签,如示例所示。我们将使用 UUID 引用,因为即使以不同的顺序检测到磁盘,它们也不会改变,因此可能会以不同的方式命名。Ubuntu 将原始设备节点名放在正上方一行的注释中。接下来是挂载点,它只是文件系统上任意位置的一个目录。挂载点可以位于也是单独挂载的分区上。

Tip

请记住,/etc/fstab文件中的条目是按照从上到下的顺序处理的。

文件系统类型告诉系统预期的类型。如果不匹配,装载将会失败。您可以指定要尝试的逗号分隔的类型列表,例如 DVD-ROM 驱动器/dev/scd0。这将首先尝试udf DVD 文件系统,然后尝试 CD-r om 使用的iso9660

挂载选项也以逗号分隔的列表形式传递。在我们的示例fstab中,您可以看到 ext4 文件系统使用了两种不同的选项。选项errors=remount-ro,控制文件系统出错时会发生什么。在这种情况下,文件系统将立即以只读模式挂载。这可以防止额外的数据损坏,同时保持文件对服务和用户可读。

错误行为的另外两个可能值是continue,这将导致系统写入一个日志条目,但忽略问题,以及panic,这将导致系统不正常地崩溃。默认的错误行为也可以通过tune2fs -e命令在文件系统中指定。在清单 9-5 中,我们向您展示了如何使用tune2fs来列出选项,对根挂载“/”这样做表明使用了以下默认挂载选项:

Default mount options:    user_xattr acl

user_xattr是允许支持“用户”扩展属性(可用于提高文件系统的安全性,有关更多详细信息,请参见 man attr)。acl 选项允许使用 posix acl,这同样可以用于细粒度的目录访问(有关详细信息,请参见 man acl)。

还有许多挂载选项,用于定义对文件系统上的文件和目录的访问,这可能会调整性能,还有一些选项用于不支持 Unix 风格的文件权限的文件系统,如 FAT32 和 NTFS。每个支持的文件系统的选项可以在mount手册页中找到。

dump 字段包含一个数字(0 或 1),它告诉系统在执行文件系统检查时是否转储某些文件系统元信息。文件系统修复工具可以使用这些转储信息。0意味着文件系统不需要被转储。我们将在后面的“从失败中恢复”一节中更详细地介绍这一点

最后,pass 字段用于确定检查文件系统的顺序。在我们的fstab文件中,根文件系统被列为 1,所以首先检查它。之后,将检查/boot文件系统。最后检查该列中带有0的文件系统。您可以在fstab手册页上找到这些字段的详细描述。

将分区添加到/etc/fstab

为了将您的新分区添加到我们的/etc/fstab中,我们将把我们的设备 id(设备路径、标签或 UUID)映射到我们的挂载点。我们将使用 UUID,因此我们需要知道它的 UUID。

您可以在 Ext 文件系统的tune2fs -l清单中找到它,XFS 文件系统的xfs_admin -u清单,BtrFS 的btrfs filesystem show清单,或者您可以使用blkid实用程序。如果您不带任何参数运行后者,它将打印所有检测到的块设备的 UUID,如清单 9-9 所示。

$ sudo blkid
/dev/mapper/sda5_crypt: UUID="MdUlYF-y6Ol-XcB5-mS9L-mxPN-jNLF-ATAImA" TYPE="LVM2_member"
/dev/mapper/au--mel--ubuntu--1--vg-root: UUID="0b9eec02-06a4-46e4-b9ac-1e1ea871ff89" TYPE="ext4"
/dev/sda1: UUID="d036bc4a-6f9b-4989-a377-7778a29bf16c" TYPE="ext2" PARTUUID="105922fd-01"
/dev/sda5: UUID="33dcd288-27f0-4f09-ab74-617db851a552" TYPE="crypto_LUKS" PARTUUID="105922fd-05"
/dev/mapper/au--mel--ubuntu--1--vg-swap_1: UUID="e45b953f-284f-45f5-b16d-8f5be5d5a970" TYPE="swap"
/dev/sdb2: LABEL="mail" UUID="71bd5774-33cb-491b-8ffe-49cb33935001" TYPE="ext4" PARTLABEL="Linux filesystem" PARTUUID="b704ec19-833d-4727-a572-189f214f2ecf"
/dev/sdb1: UUID="6d0ce2f6-f9f6-4ac2-91f6-3099a40d5624" TYPE="swap" PARTLABEL="Linux swap" PARTUUID="db962e77-53f3-4cfe-847c-f53133f063f7"
/dev/sdb3: UUID="ccd60fc3-bbaf-40e5-a93e-43743f9176d9" TYPE="xfs" PARTLABEL="Linux filesystem" PARTUUID="f9d90e5f-0186-4cd5-a2be-9b89e7286abb"
Listing 9-9.Displaying All UUIDs

要让它只打印单个设备的 UUID,请将设备节点名作为参数传递,比如

$ sudo blkid /dev/sdb2

要使用默认挂载选项挂载 ext4 分区(uuid命令也会打印文件系统类型),请在/etc/fstab文件中添加下面一行:

UUID="71bd5774-33cb-491b-8ffe-49cb33935001"  /mnt/data       ext4  defaults          0            0

在挂载 ext4 文件系统时,“defaults”选项为我们提供了以下挂载选项。它们是rw,读/写;relatime,这意味着系统相对于修改或更改时间更新信息节点访问时间(一种性能改进);以及data=ordered,这意味着系统将首先将数据写入主文件系统,然后将元数据提交给日志。

如果您想使用设备节点,可以执行以下操作。但是,我们更希望您使用标签或 UUID,因为设备路径可能会改变。

/dev/sdb2      /mnt/data      ext4     defaults            0        0

现在,您可以测试这个条目,而不需要重新启动。如果您使用mount命令,并且只将挂载点作为参数传递,它将检查/etc/fstab文件中的匹配条目,并使用文件中指定的选项挂载它。

$ sudo mount /mnt/data

如果mount命令退出而没有显示任何错误,那么fstab条目是正确的,并且您的文件系统将在您每次引导主机时自动挂载。如果您传递文件系统类型,比如用一个mount -t ext4,它将挂载所有的 ext4 文件系统。你可以用一个mount –a挂载fstab文件中的所有挂载点。您可以通过运行清单 9-10 中的mount来仔细检查文件系统是否已经挂载。

$ sudo mount –t ext4
/dev/mapper/au--mel--ubuntu--1--vg-root on / type ext4 (rw,relatime,errors=remount-ro,data=ordered)
/dev/sdb2 on /mnt/data type ext4 (rw,relatime,data=ordered)
Listing 9-10.All Mounted Filesystems

我们使用参数–t ext 4 在输出中只指定 ext4 fs-types,您可以看到我们的/dev/sdb2挂载在/mnt/data上。或者,要添加 XFS 分区,创建挂载点目录并将正确的 UUID 或设备节点名称和文件系统类型添加到/etc/fstab:

UUID="ccd60fc3-bbaf-40e5-a93e-43743f9176d9" /mnt/data2 xfs defaults  0  0

Caution

文件fstab中的错误可能会导致系统无法启动。如果是这样,您可能需要遵循“从故障中恢复”一节中讨论的一些步骤,或者使用第六章中描述的单用户模式来引导并修复错误。

内核可能没有注册文件系统上的 UUID 已经改变。如果发生这种情况,尝试使用 UUID 引用来装载文件系统将导致如下错误:

$ sudo mount /mnt/datax
mount: special device /dev/disk/by-uuid/ccd60fc3-bbaf-40e5-a93e-43743f9176d9
    does not exist

您可以通过重新加载udev服务来重新检测 UUID,并在/dev/disk/by-uuid中创建正确的符号链接。我们可以通过以下方式做到这一点:

$ sudo udevadm control --reload

检查文件系统使用情况

当您开始使用文件系统进行数据存储时,您会希望能够留意可用空间的数量。当文件系统填满时,使用它的服务可能会拒绝启动、停止工作或崩溃。

您可以通过df命令列出整个文件系统的空间使用情况,该命令通常与-h选项一起使用。该选项产生人类可读的输出,用 KiB、MiB、GiB 或 TiB 的数字代替块,如清单 9-11 所示。

$ df -h
Filesystem                                 Size   Used   Avail   Use%   Mounted on
udev                                       478M      0    478M     0%   /dev
tmpfs                                      100M   4.6M    95M      5%   /run
/dev/mapper/au--mel--ubuntu--1--vg-root    6.3G   2.6G   3.4G     44%   /
tmpfs                                      497M      0   497M      0%   /dev/shm
tmpfs                                      5.0M      0   5.0M      0%   /run/lock
tmpfs                                      497M      0   497M      0%   /sys/fs/cgroup
/dev/sda1                                  472M   147M   301M     33%   /boot
tmpfs                                      100M      0   100M      0%   /run/user/1000
/dev/sdb2                                  2.0G   3.0M   1.8G      1%   /mnt/data
/dev/sdb3                                  4.0G    33M   4.0G      1%   /mnt/data2
Listing 9-11.Filesystem Usage

输出显示了总大小、已用空间量和可用空间量,以及这相当于每个已挂载文件系统的百分比。这个命令很快,因为它只是查询文件系统元数据。如您所见,您还可以传递装载路径,以便只返回该装载的信息。

$ df -h /data
Filesystem      Size  Used    Avail   Use%   Mounted on
/dev/sdb         16G   22M    14G     1%       /data

Note

要检查索引节点的数量,请使用df -i命令。当应用程序报告磁盘已满时,请使用此命令,即使显然还有大量剩余空间。

您可以使用du命令检查目录及其包含的所有文件的累积大小。这个命令需要递归扫描运行它的目录下的文件,所以可能需要很长时间才能完成。我们将再次使用-h选项来给出人类可读的输出。默认情况下,它还会打印每个子目录的大小。为了避免这种情况,我们传递了-s选项,所以它只显示最终的总数。

$ du -sh *
2.5M    Documents
44G     src

这里我们列出并计算了主目录中的文件和目录。我们有两个目录,Documents 和 src,我们可以看到这些目录中每个文件的总大小。当我们尝试一个像/etc目录这样的系统目录时会发生什么?

$ du -sh /etc
du: cannot read directory '/etc/ssl/private': Permission denied
du: cannot read directory '/etc/lvm/archive': Permission denied
du: cannot read directory '/etc/lvm/backup': Permission denied
du: cannot read directory '/etc/polkit-1/localauthority': Permission denied
7.0M    /etc

因为它扫描/etc中的子目录,所以该命令可能会遇到您无权访问的目录。它无法计算这些目录的大小,因此在这种情况下,它报告的总数是不正确的。为了总是得到正确的总数,您可以以root用户的身份运行du:

$ sudo du -sh /etc
7.0M    /etc

虽然我们的结果中显示了相同的数量(7.0 M),但这是由于四舍五入的原因。如果我们使用–k(代表 KiB)而不是–h,我们会看到值分别为 7,084 和 7,116。如果文件系统已满,这对于确定将哪些目录移动到自己的分区很有帮助。另一种解决方案是调整文件系统的大小,我们很快就会谈到这一点。

Tip

在第 17 和 18 章中,当我们讨论监控和日志记录时,我们将讨论如何自动化文件系统监控。

袭击

将数据存储在硬盘上有助于保持数据在服务器上的可访问性,但是当硬盘出现故障时,您会丢失数据。有几种方法可以解决这个问题。一种是使用 LVM,它可以将多个磁盘分组并作为一个设备呈现,或者您可以使用 BtrFS,它可以做类似的事情。在本节中,我们将向您展示另一种替代方案,RAID。

RAID 允许您使用多个磁盘,就像它们是一个更大的磁盘一样,并具有可选的内置冗余。RAID 实施的三大类型如下:

  • 硬件 RAID
  • 假突袭
  • 软件 RAID

硬件 RAID 使用专门的硬件控制器,通常称为 RAID 控制器,它从操作系统透明地管理 RAID。企业级服务器通常带有这些专用的硬件控制器。在这样的系统上,你通常会通过 BIOS(基本输入/输出系统)或 UEFI(统一可扩展固件接口)来配置 RAID(我们在第六章中简要讨论过)。Linux 将会看到一个 RAID 阵列,您可以像使用普通硬盘一样使用它。

伪 RAID 是一种较小形式的硬件 RAID,用于较小的系统或台式机。在这里,制造商可能已经通过芯片为主板添加了 RAID 功能。我们建议您不要使用假 RAID,因为使用此实现创建的任何 RAID 阵列都只能在共享相同控制器的主机上工作。它的性能还取决于制造商提供的专有代码。这些控制器通常可以配置为作为简单的串行 ATA 控制器运行,而不是 RAID。在 https://raid.wiki.kernel.org/index.php/DDF_Fake_RAID 可以找到最常见的假冒 RAID 控制器的简短列表。

Note

如果你用 Windows 设置了伪 RAID,并且想要双引导,你仍然可以通过 dmraid 系统在 Linux 下使用大多数伪 RAID 阵列。禁用假 RAID 会导致 Windows 停止工作,您可能会丢失数据。

第三种 RAID 实现类型是通过 Linux 内核中包含的软件。这个系统被称为 md 或多磁盘。md 系统的性能通常比假 RAID 好得多,并且 md RAID 阵列可以在主机之间转移。在本节中,我们将重点介绍 md RAID 的使用。

RAID 的类型

RAID 有几种类型或级别。你使用的级别取决于什么对你最重要。不同的级别提供了可用磁盘空间、可靠性和速度之间的平衡。表 9-5 列出了最常用的 RAID 级别。

表 9-5。

Commonly Used RAID Levels

| Raid 级别 | 功能 | 存储容量 | | --- | --- | --- | | RAID 0 | 速度 | N *尺寸 | | RAID 1 | 裁员 | N *尺寸/ 2 | | RAID 5 | 冗余,速度 | n-1 *大小 | | RAID 6 | 冗余、可靠性、速度 | n-1 *大小 | | RAID 10 | 冗余、可靠性、速度 | N / 2 *尺寸 | | RAID 50 | 冗余,速度 | n–1 *尺寸 |

存储容量的计算方法是 raid 阵列中的 N(磁盘总数)(减去所有奇偶校验磁盘)乘以磁盘大小。你可以在 http://en.wikipedia.org/wiki/Redundant_array_of_independent_disks 找到 RAID 等级的详尽列表和描述。

使用一个硬盘作为备用也是很常见的。如果阵列中的一个磁盘出现故障,备用磁盘可以立即取代它的位置。

Note

没有任何备用设备也可以运行 RAID,但是您需要立即更换故障设备,以避免数据丢失。

条带化和镜像

使用 RAID 的最基本方式是使用两个磁盘,这使您可以选择使用 RAID 级别 0 或 RAID 级别 1。

RAID 0,也称为条带化,使 Linux 将两个磁盘视为两倍大小的组合磁盘。当向这样的 RAID 阵列写入数据时,部分数据将最终出现在每个磁盘上。因为这是 Linux 可以在两个磁盘上同时执行的操作,所以写入 RAID 0 比写入单个磁盘更快。但是,缺点是当其中一个磁盘出现故障时,分布在两个磁盘上的文件的任意部分都会消失。所以你丢失了所有的数据。

Caution

避免在服务器或任何保存持久数据的机器上使用 RAID 0。

RAID 1,也称为镜像,允许您在阵列上仅存储单个磁盘所能容纳的数据。它在两个磁盘上存储所有文件的相同副本,因此,如果一个磁盘出现故障,您仍可以从另一个磁盘检索数据。因为所有数据都需要写入每个磁盘,所以 RAID 1 没有提供任何改进的写入性能。

图 9-1 显示了使用 RAID 0 或 RAID 1 时文件是如何存储在磁盘上的。在 RAID 1 上,每个磁盘包含每个文件的完整副本。在 RAID 0 上,每个磁盘只包含每个文件的部分副本。

A185439_2_En_9_Fig1_HTML.jpg

图 9-1。

RAID 0 and RAID 1 file storage

当您有更多的磁盘可以使用时,您将有更多的选项来选择 RAID 级别,从而提高性能并增加冗余。最简单的扩展是 RAID 1+0 (RAID 10),它使用多个 RAID 1 镜像作为 RAID 0 条带的元素。这样,所有条带化数据都保存到至少两个磁盘上,这为您提供了 RAID 0 速度和 RAID 1 冗余的优势。但是,您只能存储组合磁盘大小一半的数据。当磁盘既大又便宜,并且服务器中有足够的插槽来容纳至少四个磁盘时,这可能是一个不错的选择。

处理器拯救世界

为了获得最好的结果——冗余、存储容量和速度——你可以借助一些处理能力。RAID 级别 5 最少使用三个磁盘,可让您更有效地利用可用存储空间,并提高读写速度。它通过在多个磁盘上对数据进行分条并将每个分条的校验和写入不同的磁盘来实现这一点(也称为块奇偶校验-有关如何计算的详细信息,请参见 http://searchstorage.techtarget.com/definition/parity )。如果磁盘出现故障,校验和可用于重建丢失条带中的数据。

权衡的结果是,这种方法使用处理能力来计算校验和。当数据写入阵列时,需要计算校验和并将其存储在其中一个磁盘上。如果一个磁盘出现故障,校验和可以与其余磁盘上的数据结合使用,以重新计算丢失的数据部分。你的 CPU 越快,这个过程就越快。

图 9-2 显示了一个简单的图表,说明了数据和校验和是如何在磁盘之间分割的。B1、B2 和 B3 是文件 b 的一部分。Bp 是校验和,即奇偶校验。如果磁盘 1 出现故障,可以从 B1、B3 和 Bp 计算 B2,因此当添加替换磁盘时,可以恢复其内容。

A185439_2_En_9_Fig2_HTML.jpg

图 9-2。

RAID 5 stripe layout across multiple disks

请记住,使用 RAID 不能替代创建常规备份,这一点很重要。它将保护您免受硬件故障,但不会被故意删除。如果您不小心从 RAID 阵列中删除了一个文件,该文件将从阵列中的所有设备中删除。我们将在第十四章中讨论数据备份和恢复。

创建数组

您希望保护主机上的数据免受磁盘故障的影响,因此您希望在主机上使用 RAID。为了更好地概述常见的 RAID 级别,我们将向您展示 RAID 1 和 RAID 5。您使用哪一种取决于您可用的硬盘数量。首先,您需要确保至少有三个磁盘,并在所有磁盘上创建大小相同的分区。

Note

如果没有足够的磁盘来使用 RAID,您可以在单个磁盘上创建多个大小相同的分区,并将它们用作 RAID 阵列中的组件。这将允许您测试 RAID 安装和管理。请注意,这种配置的性能会非常慢,因为数据需要多次写入同一个磁盘的不同部分。它也不能提供更多的磁盘故障恢复能力。如果单个磁盘出现故障,那么您的 RAID 阵列也会出现故障。

在我们的示例主机上,我们将再次使用三个新磁盘,sdc、sdd 和 sde,它们的大小都相同。磁盘大小必须相同,RAID 才能工作,如果大小不同,您需要在相同大小的磁盘上创建分区。这里我们创建一个 2 G 分区,并将分区类型设置为 fd00–Linux RAID,如清单 9-12 所示。

$ sudo gdisk /dev/sdc
GPT fdisk (gdisk) version 0.8.6

Partition table scan:
  MBR: not present
  BSD: not present
  APM: not present
  GPT: not present

Creating new GPT entries.

Command (? for help): n
Partition number (1-128, default 1):
First sector (34-16777182, default = 2048) or {+-}size{KMGTP}:
Last sector (2048-16777182, default = 16777182) or {+-}size{KMGTP}: 2G
Current type is 'Linux filesystem'
Hex code or GUID (L to show codes, Enter = 8300): fd00
Changed type of partition to 'Linux RAID'

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/sdc.
The operation has completed successfully.

Listing 9-12.Clearing the Partition Table and Creating a RAID Partition

您需要对/dev/sdd/dev/sde重复这个过程。但是,如果您的磁盘都是相同大小的(因此最好是来自同一制造商的相同驱动器),则根本不需要对驱动器进行分区。因为我们的驱动器大小相同,所以我们不会先对它们进行分区。

现在,您已经准备好了三个磁盘,可以创建 RAID 阵列了。为此,您将需要 RAID 管理实用程序,它由mdadm包提供。

管理 RAID 配置所有方面的命令也称为mdadm,您可以通过模式选项指定它应该对您的阵列做什么。要创建一个阵列,您需要指定create模式,您想要使用哪个 RAID 级别,以及哪些分区需要成为阵列的一部分。清单 9-13 展示了如何创建一个 RAID 1 阵列。

$ sudo mdadm --create /dev/md0 --level=raid1 --raid-devices=2 /dev/sdc /dev/sdd
   --spare-devices=1 /dev/sde
mdadm: Note: this array has metadata at the start and
    may not be suitable as a boot device.  If you plan to
    store '/boot' on this device please ensure that
    your boot-loader understands md/v1.x metadata, or use
    --metadata=0.90
Continue creating array? y
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.
Listing 9-13.Creating a RAID 1 Array with a Hot Spare

在这里,我们创建了一个名为/dev/md0的可挂载设备,这是一个由两个分区/dev/sdc/dev/sdd组成的RAID 1设备,其中/dev/sde作为热备盘。如果我们要从这个设备启动(例如,可能创建一个 MBR 分区并在其上安装 grub ),我们需要改变我们希望使用的元数据格式。我们可以通过指定- metadata=0.90 来实现(这是原始的超级块格式,更多细节请参见man mdadm页)。

创建或启动 RAID 阵列将导致加载 md 内核模块并显示一些状态信息。您可以通过dmesg查看内核日志,如清单 9-14 所示。

$ sudo dmesg
[ 9508.794689] md: bind<sdc>
[ 9508.795609] md: bind<sdd>
[ 9508.795996] md: bind<sde>
[ 9508.806492] md: raid1 personality registered for level 1
[ 9508.807304] md/raid1:md0: not clean -- starting background reconstruction
[ 9508.807306] md/raid1:md0: active with 2 out of 2 mirrors
[ 9508.807318] md0: detected capacity change from 0 to 8584626176
[ 9508.809302] RAID1 conf printout:
[ 9508.809305]  --- wd:2 rd:2
[ 9508.809306]  disk 0, wo:0, o:1, dev:sdc
[ 9508.809307]  disk 1, wo:0, o:1, dev:sdd
[ 9508.812318] md: resync of RAID array md0
[ 9508.812320] md: minimum _guaranteed_  speed: 1000 KB/sec/disk.
[ 9508.812321] md: using maximum available idle IO bandwidth (but not more than 200000 KB/sec) for resync.
[ 9508.812323] md: using 128k window, over a total of 8383424k.
[ 9508.821845]  md0: unknown partition table
[ 9550.509411] md: md0: resync done.
[ 9550.516479] RAID1 conf printout:
[ 9550.516481]  --- wd:2 rd:2
[ 9550.516483]  disk 0, wo:0, o:1, dev:sdc
[ 9550.516484]  disk 1, wo:0, o:1, dev:sdd
[ 9550.517712] RAID1 conf printout:
[ 9550.517715]  --- wd:2 rd:2
[ 9550.517716]  disk 0, wo:0, o:1, dev:sdc
[ 9550.517717]  disk 1, wo:0, o:1, dev:sdd
Listing 9-14.Kernel RAID Information

因为您的新阵列从未同步过,所以内核将首先确保两个磁盘上的数据是相同的。它会通知您它将尽可能快地执行同步,但不会慢于每个磁盘每秒 1,000 KB,并且总速度不会超过每秒 200,000 KB。

Tip

我们将在第十七章告诉你如何改变同步速度。

要检查我们的 RAID 设备的状态,您可以在查询模式下使用带有--detail选项的mdadm实用程序。这将显示关于指定 RAID 设备的大量信息,如清单 9-15 所示。

$ $ sudo mdadm --query --detail /dev/md0
/dev/md0:
Version          : 1.2
Creation Time    : Mon Jun 20 09:41:18 2016
Raid Level       : raid1
Array Size       : 8383424 (8.00 GiB 8.58 GB)
Used Dev Size    : 8383424 (8.00 GiB 8.58 GB)
Raid Devices     : 2
Total Devices    : 3
Persistence      : Superblock is persistent
Update Time      : Mon Jun 20 09:42:00 2016
State            : clean
Active Devices   : 2
Working Devices  : 3
Failed Devices   : 0
Spare Devices    : 1

Name             : au-mel-centos-1.example.com:0  (local to host gateway.example.com)
UUID             : ca66c4e2:49e8c87e:94d311de:01ca4f55
Events           : 17

Number   Major   Minor   RaidDevice      State
     0       8      33            0      active sync   /dev/sdc
     1       8      49            1      active sync   /dev/sdd

     2       8      65            -      spare         /dev/sde

Listing 9-15.Querying RAID Device Status

清单 9-15 显示了关于阵列的元信息,以及每个组件的详细状态。在我们的 RAID 1 阵列中,您可以看到/dev/sdc/dev/sdd都处于活动状态并且同步。这意味着写入 RAID 设备的任何数据都会立即写入/dev/sdc/dev/sdd。如果这些设备中的任何一个出现故障,我们的备用(/dev/sde)将自动启动并同步。

您还可以通过查询/proc 文件系统来快速查看您的 RAID 设备:

$ cat /proc/mdstat
Personalities : [raid1]
md0 : active raid1 sde2 sdd[1] sdc[0]
      8383424 blocks super 1.2 [2/2] [UU]

unused devices: <none>

我们可以看到[UU],这告诉我们两个设备[2/2]都已启动。如果一个被降级或关闭,它可能会出现这样的[U_]。设备指示其正被用作备用设备后的(S)

在引导时,您的 Linux 主机将调用mdadm实用程序。根据其配置,该实用程序将扫描所有分区或已定义磁盘的 RAID 超级块。如果它发现任何,它将分析它们,并尝试组装和启动所有 RAID 阵列。您还可以在mdadm配置文件中显式定义 RAID 阵列,以确保它们的设备节点名称不会改变。定义阵列的配置文件在 CentOS 上是/etc/mdadm.conf,在 Ubuntu 上是/etc/mdadm/madadm.conf。我们在清单 9-16 中包含了一个来自 Ubuntu 的默认mdadm.conf文件。

# mdadm.conf
#
# Please refer to mdadm.conf(5) for information about this file.
#

# by default (built-in), scan all partitions (/proc/partitions) and all
# containers for MD superblocks. alternatively, specify devices to scan, using
# wildcards if desired.
#DEVICE partitions containers

# auto-create devices with Debian standard permissions
CREATE owner=root group=disk mode=0660 auto=yes

# automatically tag new arrays as belonging to the local system
HOMEHOST <system>

# instruct the monitoring daemon where to send mail alerts
MAILADDR root

# definitions of existing MD arrays

# This file was auto-generated on Tue, 10 May 2016 21:55:12 +1000
# by mkconf $Id$

Listing 9-16.Default mdadm.conf

这个配置文件将使主机在启动时扫描阵列,并创建属于root用户和磁盘组的设备节点,就像普通硬盘驱动器一样。它指定当mdadm在监控模式下运行时,任何关于设备故障的电子邮件都会发送给root用户。

Note

我们将在第十二章中向您展示如何将root用户的所有电子邮件重定向到不同的地址。

最后,还有一个空间可以添加 RAID 阵列的配置。您将在这里为您的 RAID 1 阵列添加一个定义。根据mdadm.conf手册页,您需要以下内容:

ARRAY /dev/md0 level=raid1 num-devices=2 spares=1
    UUID=ca66c4e2:49e8c87e:94d311de:01ca4f55 devices=/dev/sdc,/dev/sdd,/dev/sde

注意,添加这个数组定义并不是绝对必要的。mdadm将自动检测和组装阵列,即使您在配置文件中没有这个阵列定义。

我们已经提到,如果事件发生,mdadm将向root用户发送电子邮件。稍后,当我们开始让阵列中的设备出现故障时,我们将向您展示一个示例。

The /Proc Filesystem

你可能已经注意到本章中提到了一个名为/proc的目录。这是一个特殊的目录,包含内存文件系统中的虚拟或伪目录,提供了一种与内核交互的方式。例如,您可以通过cat /proc/cpuinfo获得关于主机处理器的信息,或者您可以通过发出echo 1 > /proc/sys/net/ipv4/ip_forward来改变内核网络栈以转发 IPv4 包。

内部内核变量可以通过/proc/sys目录下的文件访问。我们将在第十七章回到这些。

/proc文件系统实际上并不存在于磁盘上,所以它不使用任何空间,即使它看起来包含一些非常大的文件。

你可以在 http://en.wikipedia.org/wiki/Procfs 阅读更多关于/proc文件系统的内容。

如果您至少有四个硬盘,您可以创建一个 RAID 5 阵列。这样做可以让您更有效地使用可用的存储空间,在某些情况下,还可以提高性能。要创建新的 RAID 5 阵列,您需要首先拆卸 RAID 1 阵列。通过停止它,您可以释放它使用的所有设备:

$ sudo mdadm --manage /dev/md0 --stop
mdadm: stopped /dev/md0

现在,您可以将这些设备用于新的 RAID 5 阵列。注意删除添加到mdadm.conf文件中的条目。您将添加一个磁盘/dev/sdf,并在其上创建一个类型为“Linux RAID”的主分区。

完成后,您可以创建一个 RAID 5 阵列,其中有三个活动设备和一个备用设备,如清单 9-17 所示。

$ sudo mdadm --create /dev/md0 --level=raid5 --raid-devices=3 --spare-devices=1 /dev/sdc /dev/sdd /dev/sde /dev/sdf
mdadm: /dev/sdc appears to be part of a raid array:
       level=raid1 devices=2 ctime=Mon Jun 20 09:41:18 2016
mdadm: /dev/sdd appears to be part of a raid array:
       level=raid1 devices=2 ctime=Mon Jun 20 09:41:18 2016
mdadm: /dev/sde appears to be part of a raid array:
       level=raid1 devices=2 ctime=Mon Jun 20 09:41:18 2016
Continue creating array? y
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.
Listing 9-17.Creating a RAID 5 Array

其中一些设备是以前的 RAID 1 阵列的一部分,它们仍然包含带有阵列信息的旧 RAID 超级块。您想要创建一个新的阵列,覆盖旧的数据,所以回答 y。因为旧的 RAID 1 阵列是同步的,所以所有设备现在都包含一个文件系统,即使您最初只在/dev/sdc 上创建了一个文件系统。您可以通过/proc/mdstat再次检查阵列状态。

$ cat /proc/mdstat
Personalities : [raid1] [raid6] [raid5] [raid4]
md0 : active raid5 sde[4] sdf3 sdd[1] sdc[0]
      16765952 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/2] [UU_]
      [===>.................]  recovery = 15.2% (1280840/8382976) finish=0.7min speed=160105K/sec

在这里,您可以看到我们有[3/2][UU_],这意味着其中两个驱动器运行正常,另一个驱动器已降级但正在恢复,这意味着它正在获取将数据放入 RAID 阵列所需的数据拷贝。在此系统上,阵列启动不需要很长时间。

$ cat /proc/mdstat
Personalities : [raid1] [raid6] [raid5] [raid4]
md0 : active raid5 sde[4] sdf3 sdd[1] sdc[0]
      16765952 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU]

现在,您有了一个 RAID 5 阵列,其中有三个活动设备和一个备用设备。如果您有足够的硬盘,您也可以增加 RAID 5 阵列的大小。这将导致数据被转移,校验和被重新计算,因此需要一些时间来完成。您将向您的主机添加第六个磁盘/dev/sdg,这样您就可以扩展阵列,并且仍然有一个备用设备可用。然后,您可以将新设备添加到您的阵列中,并通过--add选项在管理模式下使用 mdadm 扩展阵列,如清单 9-18 所示。

$ sudo mdadm --manage /dev/md0 --add /dev/sdg
mdadm: added /dev/sdf
Listing 9-18.Expanding a RAID 5 Array

要在一个命令中添加多个设备,只需在--add选项后列出所有设备。快速检查/proc/mdstat现在显示 sde1 和 sdf1 都被列为备件。

$ cat /proc/mdstat
Personalities : [raid1] [raid6] [raid5] [raid4]
md0 : active raid5 sdg5 sde[4] sdf3 sdd[1] sdc[0]
      16765952 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU]

unused devices: <none>

现在,您可以在增长模式下使用 mdadm 将阵列从三个活动磁盘扩展到四个。其中一个备件将自动用于此目的。此过程的一部分是破坏性的,因此如果在扩展阵列时发生电源故障,您可能会丢失数据。为了防止这种情况,指定--backup-file选项。确保不要将该备份文件存储在/tmp目录中,该目录在启动时会被清空!

$ sudo mdadm --grow /dev/md0 --raid-disks=4 --backup-file=/root/raid-backup-file
mdadm: Need to backup 384K of critical section..
mdadm: ... critical section passed.

您可以再次通过/proc/mdstat文件关注进度。

$ cat /proc/mdstat
Personalities : [linear] [multipath] [raid0] [raid1] [raid6] [raid5] [raid4]
    [raid10]
md0 : active raid5 sdg15 sde1[4] sdf1[3] sdd1[1] sdc1[0]
      16771584 blocks super 0.91 level 5, 64k chunk, algorithm 2 [4/4] [UUUU]
      [=======>.............] reshape = 35.8% (3008512/8385792) finish=22.2min
    speed=4022K/sec

unused devices: <none>

现在,您有四个活动设备和一个备用设备。在整形完成之前,无法访问数组的完整新大小。如您所见,整形运行的速度比 RAID 1 重新同步慢得多。

Tip

不用手动重新运行cat /proc/mdstat命令,您可以通过watch命令以指定的时间间隔自动运行它。登录第二个控制台,运行watch –n 5 cat /proc/ mdstat,每五秒钟自动运行一次命令。按 Ctrl+C 退出watch

我们将在“从故障中恢复”一节中再次讨论 RAID,并向您展示如何处理磁盘故障关于 Linux 上 md RAID 的更多信息,可以访问 http://linux-raid.osdl.org/index.php/Linux_Raid

接下来,我们将向您展示如何利用这些 RAID 设备,而无需对它们进行分区。

逻辑卷管理

我们已经看到了如何对磁盘进行分区,并在磁盘上创建不同的文件系统。问题是,一旦对磁盘进行分区,就很难调整分区大小或添加额外的分区。即使您添加了磁盘和分区,您的数据也可能分布在不同的位置和目录中。这使得整合、备份和管理您的数据变得更加困难,并且有可能使您的用户更难找到他们的数据。为了解决这个问题,创建了逻辑卷管理(LVM)。

LVM 将一个或多个分区或设备合并成一个逻辑卷组,而不是将磁盘分割成固定数量的分区,存储在磁盘的固定区域。然后,您可以动态地创建、调整和删除卷组中的卷,从而无需卸载卷或重新启动系统来更新分区图。

LVM 系统有三层。底层由物理卷组成:磁盘、分区或 RAID 阵列。物理卷用于创建卷组。卷组可以由一个或多个物理卷组成。最后,卷组可以包含任意数量的逻辑卷,这些逻辑卷是分区的 LVM 等价物。

Ubuntu 和 CentOS 都有一个图形用户界面(GUI),可以用来管理磁盘,但它们不支持 LVM(有一个应用程序叫做“disks”)。我们将带您通过命令行进行管理,从 LVM 卷和组开始。

创建组和卷

如果想使用 LVM 分区,需要使用 gdisk 将其类型设置为8e00 - Linux LVM。您也可以使用整个磁盘或 RAID 阵列作为 LVM 的存储。这很方便,因为您通常不会在软件 RAID 阵列上创建分区。

对于我们的示例,我们将在前面创建的 RAID 5 阵列上设置 LVM。在 RAID 1、单个分区或整个磁盘上设置 LVM 时,步骤是相同的。

LVM 使用的每个存储设备都称为一个物理卷(PV)。您可以通过pvcreate命令对设备进行标记。

$ sudo pvcreate /dev/md0
  Physical volume "/dev/md0" successfully created

这个命令在设备的开头写一个小水印,标识它用于 LVM。您可以通过pvs命令列出系统中的所有此类设备。

$ sudo pvs
PV            VG         Fmt     Attr     PSize       PFree
/dev/md0                 lvm2    ---     15.99g       15.99g
/dev/sda2    centos      lvm2    a--      7.51g       40.00m

Note

如果使用pvdisplay命令,您将获得关于物理卷的更多详细信息。

回想一下,当我们第一次安装 CentOS 系统时,我们选择了使用 LVM。您现在可以看到,它使用了/dev/sda2分区作为物理卷。第二列标记为 VG,表示卷组,这是 LVM 系统中的下一层。

您可以通过vgs命令列出系统上的所有卷组。

$ sudo vgs
VG      #PV   #LV    #SN    Attr      VSize     VFree
centos    1     2      0    wz--n-    7.51g     40.00m

Note

如果使用vgdisplay命令,您将获得更多关于卷组的详细信息。

有一个名为centos的卷组,是由安装程序创建的。它跨越一个物理卷,包含两个逻辑卷。您可以通过lvs命令列出这些。目前有两卷,rootswap_1

$ sudo lvs
    LV      VG  Attr        LSize  Pool  Origin  Data%  Meta%  Move  Log  Cpy%Sync  Convert
  root  centos  -wi-ao---   6.67g
  swap  centos  -wi-ao--- 820.00m

Note

如果使用lvdisplay命令,您将获得更多关于逻辑卷的详细信息。

现在,您可以通过vgextend命令将您的物理卷添加到现有组中:

$ sudo vgextend centos /dev/md0
  Volume group "centos" successfully extended

为了进行检查,您可以使用pvsvgs来显示物理卷。

$ sudo pvs
  PV         VG        Fmt    Attr    PSize    PFree
  /dev/md0   centos    lvm2   a--    15.99g   15.99g
  /dev/sda2  centos    lvm2   a--     7.51g   40.00m

$ sudo vgs
  VG      #PV   #LV   #SN     Attr       VSize    VFree
  centos    2     2     0     wz--n-    23.50g   16.03g

新的物理卷现在是centos卷组的一部分。将新的物理卷添加到组中意味着它现在有 23.50 GiB 的未分配空间。

另一种方法是创建一个新的卷组,并使用您的物理卷。您仍然可以通过vgreduce命令从centos组中移除/dev/md0来完成:

$ sudo vgreduce centos /dev/md0
Removed "/dev/md0" from volume group "centos"

它现在可用于不同的卷组,该卷组是使用vgcreate命令创建的。您将把/dev/md0设备分配给新的raid-volume卷组。完成后,您可以使用vgs命令检查新卷组是否存在。

$ sudo vgcreate raid-volume /dev/md0
  Volume group "raid-volume" successfully created

$ sudo vgs
  VG               #PV    #LV    #SN     Attr       VSize     VFree
  centos             1      2      0     wz--n-     7.51g    40.00m
  raid-volume        1      0      0     wz--n-    15.99g    15.99g

现在您有了一个新的卷组,可以用来创建逻辑卷。为了准备后面的章节,我们为什么不为网站创建一个存储区呢?有了 LVM,我们可以很容易地在磁盘上给每个函数分配一个专用的区域。

首先,您需要创建一个逻辑卷,这可以通过lvcreate命令来完成。您需要指定名称、大小和要在其中创建卷的卷组。然后您可以通过lvs将其列出。

$ sudo lvcreate --name www --size 2G raid-volume
  Logical volume "www" created
$ sudo lvs
  LV    VG           Attr         LSize  Pool Origin Data%  Meta%  Move Log Cpy%Sync Convert
  root  centos       -wi-ao----   6.67g
  swap  centos       -wi-ao---- 820.00m
  www   raid-volume  -wi-a-----   2.00g

现在剩下要做的就是在逻辑卷上创建一个文件系统,并将其挂载到某个地方。为此,您需要逻辑卷的设备节点名。这由名为 device-mapper 的驱动程序管理,它为您创建的任何卷创建一个设备节点条目。

逻辑卷可通过/dev/mapper/<vgname>-<lvname>访问,从/dev/<vgname>/<lvname>开始进行符号链接。所以对于新“www”的吕,可以使用/dev/raid-volume/www。我们将使用以下内容为这个逻辑卷创建一个 XFS 文件系统:

$ sudo mkfs.xfs /dev/raid-volume/www

您可以像使用普通分区一样使用该卷,并将其添加到/etc/fstab文件中,这样当您的主机启动时,它会自动挂载到/var/www上。为此,您可以使用设备节点名或刚刚创建的文件系统的 UUID。blkid 会为你提供这两者。/etc/fstab条目如下所示:

# /dev/mapper/raid--volume-www
UUID=0814d564-b61c-407a-8483-9b176c684816  /var/www  xfs   defaults  0  0

创建了/var/www目录后,可以使用以下命令挂载它:

$ sudo mount /var/www

扩展逻辑卷

到目前为止,使用 LVM 似乎只是一种更复杂的磁盘使用方式,但是如果您的网站超过 2gb 会怎样呢?如果没有 LVM,您将需要在一个未使用的磁盘上创建一个分区,然后复制所有数据并确保/etc/fstab得到更新。然而,使用 LVM,您可以简单地扩展逻辑卷,然后在其上调整文件系统的大小。如果卷组中没有剩余空间,您可以先向组中添加一个物理卷。

您需要完成两个步骤来安全地调整逻辑卷中包含的文件系统的大小。首先,您需要调整逻辑卷本身的大小,其次,您需要调整文件系统的大小。LVM 可以为你做这些,我们很快会告诉你怎么做。

可以使用lvextend命令扩展卷。您需要指定新的总大小或所需的大小增量,以及卷的名称。通过在 size 参数前加+前缀,可以表明您希望将指定的大小添加到现有大小中。

$ sudo lvextend --size +2G --resizefs /dev/raid-volume/www
  Size of logical volume raid-volume/wwwl changed from 2.00 GiB (512 extents) to 4.00 GiB (1024 extents).
  Logical volume spool successfully resized.
meta-data=/dev/mapper/raid--volume-www isize = 256 agcount=4, agsize=131072 blks
                                            = ectsz=512     attr=2, projid32bit=1
                                            = crc=0         finobt=0
                                       data = bsize=4096    blocks=524288, imaxpct=25
                                            = sunit=0       swidth=0 blks
                                     naming = version 2     bsize=4096 ascii-ci=0 ftype=0
                                        log = internal      bsize=4096 blocks=2560, version=2
                                            = sectsz=512    sunit=0 blks, lazy-count=1
                                   realtime = none          extsz=4096 blocks=0, rtextents=0
data blocks changed from 524288 to 1048576

如您所见,我们已经指定要向/dev/raid-volume/www逻辑卷添加 2 G ( +2G)。我们也希望它能为我们调整大小(--resizefs。这告诉 LVM 只需在调整逻辑卷大小后执行xfs_growfs

Note

在调整文件系统大小时,您会受到文件系统功能的限制——lvextend 和 lvreduce 使用底层的文件系统工具来调整文件系统的大小,不需要添加任何特殊的东西。因此,您将能够使用 LVM 扩展和缩减 Ext 系列的文件系统,但只能扩展 XFS,因为您不能缩减 XFS 文件系统。

要指定新的总大小,您也可以使用以下命令,而不是将 2G 添加到卷中:

$ sudo lvextend -s 4G  /dev/raid-volume/www
  Size of logical volume raid-volume/www changed from 2.00 GiB (512 extents) to 4.00 GiB (1024 extents).
  Logical volume www successfully resized.

在这种情况下,两种方法产生相同的结果:逻辑卷 4 GiB。但是,在这种情况下,我们没有告诉 lvmextend 调整文件系统的大小,因此文件系统的大小仍然是 2gb。虽然我们可以让 lvextend 使用–r(或--resize)选项来调整文件系统的大小,但是我们将自己手动完成。

我们现在将告诉文件系统它所包含的设备的新大小,为此您可以使用xfs_growfs实用程序。如果我们有一个 Ext4 文件系统,我们将使用resize2fs实用程序。您将从一个 2 GiB 的文件系统开始,如清单 9-19 所示。

$ df -h /var/www
Filesystem Size Used Avail Use% Mounted on
/dev/mapper/raid--volume-www 2.0G 135M 1.9G 7% /var/www

$ sudo xfs_growfs /dev/raid-volume/www
meta-data=/dev/mapper/raid--volume-www isize=256 agcount=8, agsize=65408 blks
                                            = sectsz=512    attr=2, projid32bit=1
                                            = crc=0         finobt=0
                                       data = bsize=4096    blocks=523264, imaxpct=25
                                            = sunit=128     swidth=256 blks
                                     naming =version 2      bsize=4096 ascii-ci=0 ftype=0
                                        log =internal bsize=4096 blocks=2560, version=2
                                            = sectsz=512 sunit=8 blks, lazy-count=1
                                   realtime =none extsz=4096 blocks=0, rtextents=0
data blocks changed from 523264 to 1048576

$ df -h /var/www
Filesystem                     Size     Used   Avail    Use%   Mounted on
/dev/mapper/raid--volume-www   4.0G     136M    4.0G     3%      /var/www

Listing 9-19.Resizing an Ext3 Filesystem

现在您有了一个 4 GiB 的文件系统。

缩小逻辑卷

除了扩展文件系统之外,您还可以收缩一些文件系统。要收缩文件系统和逻辑卷,可以反向执行前面的步骤,并使用lvreduce命令。只需确保不要将逻辑卷缩小到比它包含的文件系统更小。

我们创建了一个新的 4g LVM 组和 LVM 卷。我们将其格式化为 Ext4 文件系统,并准备将其大小调整为 2 G。与增加文件系统不同,如果您正在减少的卷已装入,以下命令将卸载并重新装入它。

$ sudo lvreduce --size -2G -r /dev/vg-mail/spool
Do you want to unmount "/tmp/block"? [Y|n] y
fsck from util-linux 2.23.2
/dev/mapper/vg--mail-spool: 11/262144 files (0.0% non-contiguous), 53326/1048576 blocks
resize2fs 1.42.9 (28-Dec-2013)
Resizing the filesystem on /dev/mapper/vg--mail-spool to 524288 (4k) blocks.
The filesystem on /dev/mapper/vg--mail-spool is now 524288 blocks long.

  Size of logical volume vg-mail/spool changed from 4.00 GiB (1024 extents) to 2.00 GiB (512 extents).
  Logical volume spool successfully resized.

现在我们的文件系统已经减少了。

$ df -h /tmp/block
Filesystem                  Size  Used Avail Use% Mounted on
/dev/mapper/vg--mail-spool  1.9G   12M  1.8G   1% /var/spool

LVM 命令

虽然这比简单的分区要多做一点工作,但是 LVM 允许你以更灵活的方式使用你的存储空间。表 9-6 列出了你最常用的 LVM 命令。

表 9-6。

Basic LVM Commands

| 命令 | 用于 | | --- | --- | | `pvcreate` | 与 LVM 一起使用的标签装置 | | `pvremove` | 从物理卷中删除 LVM 标签 | | `pvdisplay / pvs` | 显示系统上指定设备或所有物理卷的信息 | | `vgcreate` | 创建新的卷组 | | `vgremove` | 移除(删除)卷组 | | `vgextend` | 向卷组添加物理卷 | | `vgreduce` | 从卷组中删除物理卷 | | `vgdisplay / vgs` | 显示系统上指定组或所有卷组的信息 | | `lvcreate` | 创建新的逻辑卷 | | `lvremove` | 移除(删除)逻辑卷 | | `lvextend` | 增加逻辑卷的大小 | | `lvreduce` | 减小逻辑卷的大小 | | `lvdisplay / lvs` | 显示系统上或指定卷组中的所有逻辑卷 |

从失败中恢复

如果您的主机发生崩溃,导致文件系统处于不一致的状态,系统将在引导时自动尝试修复问题。图 9-3 显示了我们主机上的根文件系统是如何被自动检查的,以及/dev/mapper/au--mel--ubuntu--1--vg--root上的日志是如何在崩溃后被重放的。

A185439_2_En_9_Fig3_HTML.jpg

图 9-3。

Automatic filesystem recovery

虽然我们不建议这样做,但是您可以通过在引导时将fsck.mode=force添加到内核来强制您的系统在引导时运行fsck,或者通过编辑 grub 永久地运行,或者通过中断引导并通过 grub 控制台添加来一次性关闭。根据文件系统的大小,fsck可能会花费大量的时间——长达几个小时!这些自动恢复通常是有效的,但是有时修复工具会发现他们不能自动修复的问题。

如果存储修复实用程序的根文件系统出现这种情况,您可以尝试使用 systemd 紧急目标(systemctl emergency)或者需要从安装 DVD 或 USB 引导。你可能还记得第二章中,从 DVD 或 USB 引导包括一个选择,以救援或恢复模式引导。这种模式从安装盘引导至最小系统,并让您进入根 shell。从这个 shell 中,您可以执行恢复文件系统所需的任何步骤。我们稍后将更详细地解释引导至救援模式。

最简单的步骤是运行适当的文件系统检查器。表 9-7 详细说明了您最可能使用的文件系统的系统检查和修复工具。

表 9-7。

Filesystem Check and Repair Tools

| 文件系统 | 修理工具 | | --- | --- | | Ext2、ext3 和 ext4 | `e2fsck or its aliases fsck.ext2, fsck.ext3 or fsck.ext4` | | XFS | `xfs_repair` | | btr 护堤 | `btrfs check --repair ` |

要运行这些修复工具,您需要确保您正在检查的文件系统不是为了写入而挂载的。修复以读/写模式挂载的文件系统肯定会破坏数据,因为修复工具会直接修改文件系统,而内核对此一无所知。

要检查文件系统,需要将适当的设备节点名作为参数传递给修复工具。

Note

当通过从根文件系统本身运行工具来修复根文件系统时,确保首先以只读方式挂载文件系统。

让我们带您了解一下我们的一个磁盘的问题。如果文件系统出现问题,系统将通知您文件系统无法挂载并进入维护模式,如图 9-4 所示。

A185439_2_En_9_Fig4_HTML.jpg

图 9-4。

A filesystem problem prevents an automatic mount

您可以按 enter 键进入维护模式,在这种模式下,我们将查看日志日志(journalctl,它是 systemd 日志记录实用程序)。通过输入我们访问的journalctl –xb,我们向消息(-x)添加了更多的细节,并指定了 boot (-b)。在图 9-5 中,我们看到了我们停止系统的原因。

A185439_2_En_9_Fig5_HTML.jpg

图 9-5。

Error message on disk mount

我们可以看到,我们未能在/data 目录中挂载/dev/sdg1。它说它找不到文件系统。我们将退出日志,并将手动运行fsck检查(参见图 9-6 )。

A185439_2_En_9_Fig6_HTML.jpg

图 9-6。

Manually running fsck

这里fsck调用了 e2fsck 并试图修复文件系统。我们告诉它对所有问题都“假设是”(-y),因此它会自动尝试修复您的文件系统。我们现在可以重启我们的系统,或者用 ctrl-d 继续启动。

有时,ext2、ext3 或 ext4 文件系统的超级块出现问题,这意味着文件系统检查工具无法找到修复文件系统所需的文件系统元数据。因此,这些文件系统也保留备份超级块。您可以使用dumpe2fs工具显示它们在磁盘上的位置,然后使用-b选项为e2fsck指定这个位置(使用另一个超级块),如清单 9-20 所示。

$ sudo dumpe2fs /dev/sdg1 | grep Backup
dumpe2fs 1.42.13 (17-May-2015)
Journal backup:           inode blocks
  Backup superblock at 32768, Group descriptors at 32769-32769
  Backup superblock at 98304, Group descriptors at 98305-98305
  Backup superblock at 163840, Group descriptors at 163841-163841
  Backup superblock at 229376, Group descriptors at 229377-229377
  Backup superblock at 294912, Group descriptors at 294913-294913
  Backup superblock at 819200, Group descriptors at 819201-819201
  Backup superblock at 884736, Group descriptors at 884737-884737
  Backup superblock at 1605632, Group descriptors at 1605633-1605633
Listing 9-20.Finding Backup Superblocks

一旦您知道备份超级块的位置,您可以尝试运行e2fsck并使用-b选项指定备份超级块的位置。

$ sudo e2fsck -b 98304 -y /dev/sdg1
e2fsck 1.42.13 (17-May-2015)
/dev/sdg1 was not cleanly unmounted, check forced.
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information

/dev/sdg1: ***** FILE SYSTEM WAS MODIFIED *****
/dev/sdg1: 11/524288 files (0.0% non-contiguous), 70287/2096891 blocks

e2fsck命令现在可以运行并修复文件系统了。

如果出现问题,并且在索引节点中发现了e2fsck无法放置的文件数据,这些数据将存储在您正在检查的文件系统上的lost+found目录中的文件中。文件名是与数据相关联的索引节点号。虽然这并不能告诉您哪些文件已经不在它们应该在的位置,但是您可以手动检查这些编号的文件,并了解哪些数据可能丢失了。如果发生了大量损坏,并且在lost+found目录中有数百个文件,那么从备份中恢复您的数据可能会更好。

引导加载程序问题

如果硬盘出现问题,系统可能会因为引导扇区损坏而完全无法引导。在这种情况下,引导加载程序无法启动,您会在屏幕上看到奇怪的错误,正如我们在第六章中向您展示的那样。

使用安装介质上的 Rescue 模式修复引导加载程序相当简单。如果由于某些问题需要重新安装引导加载程序,您可以使用安装介质(DVD、USB)并进入救援部分(参见图 9-7 )。

A185439_2_En_9_Fig7_HTML.jpg

图 9-7。

Enter Rescue Mode

接下来,我们将经历一个类似的安装过程,就像我们第一次安装系统时执行的一样,注意,这是一个 Ubuntu 系统。进入救援模式的 CentOS 程序在概念上非常相似。

救援过程最终会扫描您的磁盘,并向您提供一个可能的根分区列表,您可以选择安装。在这个 Ubuntu 系统上,我们的根分区类似于图 9-8 所示:

A185439_2_En_9_Fig8_HTML.jpg

图 9-8。

Selecting the root partition to mount

我们的根分区是 LVM 卷 au-mel-ubuntu-1-vg/root。我们选择该选项,然后进入救援菜单,如图 9-9 所示。

A185439_2_En_9_Fig9_HTML.jpg

图 9-9。

The rescue menu

我们选择了重新安装 GRUB 引导加载程序,进入后,我们将看到下一个屏幕,选择我们希望安装 GRUB 的磁盘(参见图 9-10 )。

A185439_2_En_9_Fig10_HTML.jpg

图 9-10。

Re-installing the GRUB boot loader

我们将把引导加载程序重新安装到我们的/dev/sda设备上。如果我们使用 UEFI,我们将有一个不同的分区来安装引导加载程序。

您还可以挂载根分区并执行 shell 来修复您可能遇到的其他问题,比如执行其他类型的磁盘修复和维护。

从图 9-11 所示的救援菜单中选择以下选项:

A185439_2_En_9_Fig11_HTML.jpg

图 9-11。

Opening a shell on the root partition

这向我们提出了几个问题,并告诉我们它在做什么(见图 9-12 )。

A185439_2_En_9_Fig12_HTML.jpg

图 9-12。

Mount /boot ?

就像它说的那样,您的单独的引导分区可能已损坏,您可能不想在此时挂载它。相反,您可能希望保持未安装状态,然后对其运行e2fsck以进行任何修复。在这种情况下,我们将挂载/boot 分区。

图 9-13 是信息性的,表示如果您将分区挂载到文件系统的其他区域,可能还需要挂载其他分区。您可以在这里继续,并采取任何必要的措施来挂载任何分区。

A185439_2_En_9_Fig13_HTML.jpg

图 9-13。

Information about other mounts

现在我们有了救援 Shell(见图 9-14 )。这是您的系统通过 chroot 特别安装的。chroot 是一种特殊的坐骑,我们很快会解释。

A185439_2_En_9_Fig14_HTML.jpg

图 9-14。

The rescue shell Rescue and Chroot

chroot 命令允许您运行“带有特殊根目录的命令或交互式 shell”当我们在救援模式下运行时,我们首先挂载我们的磁盘,然后使用 chroot 切换到我们挂载的设备上的根目录。这允许我们访问我们系统上的所有数据和命令,就像我们已经引导进入它一样。

当使用救援模式时,您可以改为进入“安装程序环境”并自己设置 chroot 环境。在运行chroot之前,我们需要确保我们可以通过动态/dev/proc文件系统访问设备文件和内核接口。我们可以将现有的挂载到根文件系统上,我们将通过绑定挂载在根文件系统上运行chroot。绑定挂载是一种无需使用符号链接就可以在系统的多个位置使用目录的方式。在这种情况下,我们不能使用符号链接,因为它们会指向chroot外部的目标,而从chroot内部是无法访问的。

# mount –o bind /dev /mnt/dev
# mount –o bind /proc /mnt/proc

当绑定挂载完成后,我们可以chroot到挂载的根文件系统,并在那里运行 Bash shell。这个提示看起来有点奇怪,因为 Bash 启动脚本不会运行。

# chroot /mnt && /bin/bash
bash-2 #

现在我们有了一个 shell,我们可以运行命令在第一个磁盘上重新安装引导加载程序。

bash-2 # /usr/bin/grub-install /dev/sda

完成后,我们从chroot注销,并以相反的顺序卸载文件系统。如果我们不卸载它们,我们的固定根文件系统就不能被卸载,并且在我们下次启动时会再次强制检查。

bash-2 # exit
# umount /mnt/proc
# umount /mnt/dev
# umount /mnt

我们现在可以使用 Ctrl+Alt+Delete、reboot命令或shutdown -r now命令重新启动主机。移除安装介质,以便主机从硬盘启动。

有关 chroot 的更多信息,请参见 www.cyberciti.biz/faq/unix-linux-chroot-command-examples-usage-syntax/

磁盘故障

迟早你的一个硬盘会出故障。当您使用 RAID 时,这不再是一个问题,但是您将需要采取措施来确保阵列在下一个磁盘出现故障之前再次完全工作。正如您在本章前面所看到的,您可以将一个磁盘标记为备用磁盘,这将自动完成该过程的一部分。

为了模拟磁盘故障,我们将使用mdadm告诉内核/dev/sdb1已经发生故障,如清单 9-21 所示。

$ sudo mdadm --manage /dev/md127 --fail /dev/sdd1
mdadm: set /dev/sdd1 faulty in /dev/md127
Listing 9-21.Testing Our RAID Array

RAID 子系统现在知道设备出现了故障。这与 RAID 驱动程序检测到阵列中的某个设备出现错误时的情况相同。让我们再看一下/proc/mdstat文件。

$ cat /proc/mdstat
Personalities : [raid6] [raid5] [raid4] [linear] [multipath] [raid0] [raid1] [raid10]
md127 : active raid5 sdc1[1] sde1[3] sdb1[0] sdf1[5] sdd14
      25148928 blocks super 1.2 level 5, 512k chunk, algorithm 2 [4/3] [UU_U]
      [======>..............]  recovery = 34.0% (2852268/8382976) finish=2.3min speed=39764K/sec

unused devices: <none>

sdd1磁盘现在用(F)标记为故障,而我们的备用磁盘sdf1正在更新。我们可以检查系统日志,以确保 RAID 监控器已经注意到这些变化并采取了适当的行动。我们可以使用journalctl –xb命令来查看任何消息。

Jun 23 01:06:01 au-mel-ubuntu-1 kernel: md/raid:md127: Disk failure on sdd1, disabling device.
                                        md/raid:md127: Operation continuing on 3 devices.
Jun 23 01:06:01 au-mel-ubuntu-1 kernel: RAID conf printout:
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  --- level:5 rd:4 wd:3
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 0, o:1, dev:sdb1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 1, o:1, dev:sdc1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 2, o:0, dev:sdd1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 3, o:1, dev:sdf1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel: RAID conf printout:
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  --- level:5 rd:4 wd:3
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 0, o:1, dev:sdb1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 1, o:1, dev:sdc1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 3, o:1, dev:sdf1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel: RAID conf printout:
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  --- level:5 rd:4 wd:3
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 0, o:1, dev:sdb1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 1, o:1, dev:sdc1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 2, o:1, dev:sde1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel:  disk 3, o:1, dev:sdf1
Jun 23 01:06:01 au-mel-ubuntu-1 kernel: md: recovery of RAID array md127
Jun 23 01:06:01 au-mel-ubuntu-1 kernel: md: minimum _guaranteed_  speed: 1000 KB/sec/disk.
Jun 23 01:06:01 au-mel-ubuntu-1 kernel: md: using maximum available idle IO bandwidth (but not more than 200000 KB/sec) for recovery.
Jun 23 01:06:01 au-mel-ubuntu-1 kernel: md: using 128k window, over a total of 8382976k.

重建完成后,我们应该会看到类似这样的内容:

Jun 23 01:09:21 au-mel-ubuntu-1 kernel: md: md127: recovery done.
Jun 23 01:09:21 au-mel-ubuntu-1 kernel: RAID conf printout:
Jun 23 01:09:21 au-mel-ubuntu-1 kernel:  --- level:5 rd:4 wd:4
Jun 23 01:09:21 au-mel-ubuntu-1 kernel:  disk 0, o:1, dev:sdb1
Jun 23 01:09:21 au-mel-ubuntu-1 kernel:  disk 1, o:1, dev:sdc1
Jun 23 01:09:21 au-mel-ubuntu-1 kernel:  disk 2, o:1, dev:sde1
Jun 23 01:09:21 au-mel-ubuntu-1 kernel:  disk 3, o:1, dev:sdf1
Jun 23 01:09:21 au-mel-ubuntu-1 mdadm[1341]: RebuildFinished event detected on md device /dev/md127
Jun 23 01:09:21 au-mel-ubuntu-1 mdadm[1341]: SpareActive event detected on md device /dev/md127, component device /dev/sde1

太好了。监控器发现了故障,激活了备用设备,开始重建,并完成了阵列的重建。RAID 系统保护了我们的数据,阵列仍然完好无损。我们剩下要做的就是从阵列中移除故障磁盘,然后更换它。

首先,我们将调用mdadm从 RAID 阵列中移除故障磁盘。

$ sudo mdadm --manage /dev/md127 --remove /dev/sdd1
mdadm: hot removed /dev/sdd1

下一步取决于你的硬盘控制器。如果它支持热插拔驱动器,您可以拔下损坏的磁盘并用新磁盘替换它。否则,您将不得不关闭主机,以物理方式更换驱动器。

安装新驱动器并启动主机后,您需要对新磁盘进行分区,因为阵列中有其他磁盘。新磁盘可能与它替换的磁盘具有相同的设备节点名称。分区完成后,您可以通过mdadm将新分区添加到数组中,如清单 9-22 所示。

$ sudo mdadm --manage /dev/md127 --add /dev/sdd1
mdadm: added /dev/sdd1
$ cat /proc/mdstat
Personalities : [raid6] [raid5] [raid4] [linear] [multipath] [raid0] [raid1] [raid10]
md127 : active raid5 sdd14 sde1[6] sdc1[1] sdb1[0] sdf1[5]
      25148928 blocks super 1.2 level 5, 512k chunk, algorithm 2 [4/4] [UUUU]

unused devices: <none>

Listing 9-22.Adding a New Device to a RAID Array

新磁盘作为备用磁盘添加,准备在另一个磁盘出现故障时接管。

有可能是某个问题阻止了 RAID 阵列自动启动。如果您从 DVD 或 USB 以救援模式启动,阵列可能也不会被检测到和启动。如果发生这种情况,您可以手动组装阵列。要从现有组件重新创建 RAID 阵列,请在汇编模式下使用mdadm。这将尝试从您指定的组件组装阵列。有关更多信息,请查看mdadm手册页。

摘要

在本章中,您学习了如何在 Linux 上管理存储,以及如何利用 RAID 和 LVM 最安全、最灵活地存储数据。

  • 我们探索了 Ubuntu 和 CentOS 附带的不同文件系统
  • 我们看了 XFS 的 ext4
  • 我们看到了如何创建 Btrfs RAID 卷和子卷
  • 我们看了安装和自动安装分区
  • 我们学习了如何设置软件 RAID
  • 我们查看了 LVM,并调整了卷和文件系统的大小。
  • 在磁盘出现故障的情况下,您现在已经知道如何从 RAID 阵列中移除组件以及向其中添加组件
  • 现在您可以修复基本的文件系统错误了。

在第十章中,我们将向您展示如何设置 SSH 服务器等基础设施服务,讨论如何使用 NTP(网络时间协议)管理主机上的时间,并介绍 DNS(域名系统)和 DHCP(动态主机配置协议)。

十、基础设施服务:NTP、DNS、DHCP 和 SSH

在前面的章节中,您安装了您的主机,并了解了它的使用方法。然后,您学习了如何添加和配置存储硬件。现在是时候看看如何让软件为你工作了。在本章中,我们将介绍帮助您管理网络基础的基础设施服务。

我们将首先描述如何保持系统时间同步,这很重要,因为许多应用程序都依赖于您的主机拥有正确的时间。在此过程中,我们将向您介绍网络时间协议(NTP)。

我们还将介绍域名系统(DNS ),它是一种粘合剂,通过允许主机找到另一个主机,使像互联网这样的网络发挥作用。我们将详细介绍 DNS 的组件以及如何设置和管理 DNS 服务器。

然后我们将讨论动态主机配置协议(DHCP),它用于为您的主机分配地址和网络配置。使用 DHCP 意味着您不必为网络中的客户端配置单独的网络设置;相反,这可以自动提供。您将了解如何使用 DHCP 以及如何设置地址分配并将网络配置信息传递给主机。

Note

我们将在第十九章中探讨自动配置主机的其他方法。

最后,我们将扩展安全 Shell(SSH)服务,并向您展示如何轻松访问主机以及如何使用 SSH 在主机之间传输文件。

保存期

当然,让系统保持时间是非常重要的。想象一下,让你的 Linux 主机驱动你的固体火箭助推器与你的主推进器不同步?当你定时两者都燃烧时,你可能会有几秒到几分钟的不同步。在更平凡的世界中,想象一下数据库事务具有不正确时间戳的恐怖!我都不愿意去想。

让我们看看保持时间,或者至少我们的服务器具有相同的相对时间。保持时间的通用工具是 NTP,您可以在许多系统上找到它。在 Red Hat 衍生的系统上,您会发现 Chrony。

带有 timedatectl 的时间

我们将向您展示如何使用timedatactl命令在您的 Linux 服务器上管理时间。这是 systemd 系统的一部分。有了它,我们可以做以下事情:

  • 设置当前时间
  • 设定日期
  • 设置时区

首先让我们来看看目前的状况:

$ sudo timedatectl status
      Local time: Fri 2016-09-30 21:22:26 EDT
  Universal time: Sat 2016-10-01 01:22:26 UTC
        RTC time: Fri 2016-09-30 18:06:27
       Time zone: America/New_York (EDT, -0400)
     NTP enabled: yes
NTP synchronized: yes
 RTC in local TZ: no
      DST active: yes
 Last DST change: DST began at
                  Sun 2016-03-13 01:59:59 EST
                  Sun 2016-03-13 03:00:00 EDT
 Next DST change: DST ends (the clock jumps one hour backwards) at
                  Sun 2016-11-06 01:59:59 EDT
                  Sun 2016-11-06 01:00:00 EST

这是我们当前的时间和日期状态。您可以看到当地时间、UTC(协调世界时)、时区和夏令时信息。

您可以从该信息中看到,我们的时区设置为纽约。让我们演示一下如何将它更改为我们当地的时区。

首先我们将列出时区,但是因为结果是一个很长的列表,我们将使用 grep 只返回墨尔本的结果。

$ timedatectl list-timezones |grep Melb
Australia/Melbourne

现在,为了设置时区,我们发出以下命令:

$ sudo timedatectl set-timezone Australia/Melbourne

现在,当我们检查状态时,我们已经设置了正确的时区。

$ timedatectl status
      Local time: Sat 2016-10-01 11:29:01 AEST
  Universal time: Sat 2016-10-01 01:29:01 UTC
        RTC time: Fri 2016-09-30 18:13:03
       Time zone: Australia/Melbourne (AEST, +1000)
     NTP enabled: yes

您可以使用 timedatectl - help 选项列出更多选项。例如,要设置系统时间,可以发出 timedatectl set-time。用timedatectl管理你的时间非常简单。让我们继续来看我们如何设法让您的主机时间与世界时钟自动同步。

网络时间协议

首先,我们将向您展示如何保持主机上所有系统时钟的同步。虽然这可能看起来是个小问题,但是让系统时钟匹配意味着您的日志条目都将带有一致的时间戳。这反过来意味着,如果需要,您可以轻松地关联来自不同主机的日志条目。同步的系统时钟也是我们稍后将启用的功能的先决条件。你不能简单地依赖你的主机主板的板载时钟,因为它们的质量变化很大,有些可能每天不同步几分钟。

我们已经在第二章中对 NTP 做了一些解释。时间服务由网络时间协议提供。NTP 服务器为连接到它们的客户端提供同步服务,并且它们也与上游时间服务器同步。该模型中的层被称为 strata,最高级别为 strata 0,由专用时间硬件(如原子钟或卫星接收器)组成。连接到这些层 0 时间源的服务器称为层 1 服务器。与层 1 服务器同步的服务器是层 2 服务器,依此类推。

Note

你可以在 http://www.akadia.com/services/ntp_synchronize.html 了解更多 NTP 地层。

您可以通过两种方式利用 NTP 服务器。一种方法是运行一个名为 ntpdate 的客户端实用程序,它会在每次运行时同步系统时钟。另一种方法是运行 NTP 服务,每当系统时钟与实际时间不同步时,该服务就会自动同步。很多系统实际上都使用了这两种方法。如果系统时钟和原子时相差太大,系统可能需要一段时间才能与上游时间服务器同步。为了解决这个问题,在启动 NTP 服务之前,会调用 ntpdate 实用程序并同步时钟。

让我们先来看看 ntpdate 实用程序。在 CentOS 和 Ubuntu 上都是由ntpdate包提供的。要更新系统时间,请使用上游服务器地址作为唯一的命令行参数来运行该实用程序。它需要作为root运行,以便能够更新系统时钟。

$ sudo ntpdate pool.ntp.org
24 Jun 21:25:35 ntpdate[1565]: step time server 220.233.156.30 offset 1.810551 sec

Note

如果您已经运行了 ntpd 守护程序,ntpdate 命令将会失败,并显示如下消息:“24 Jun 23:53:05 ntpdate[22609]:NTP 套接字正在使用中,正在退出。”您可以使用systemctl ntpd stop再试一次。

ntpdate实用程序连接到一个 pool.ntp.org 服务器,并将我们的系统时间调整了 1.810551 秒。有两种有效的方法来确保系统时钟保持同步,您可以在/ etc/crontab中添加一个条目,该条目运行ntpdate一次,比如说,每两个小时运行一次。或者,您可以使用更有效、更准确的 ntp 守护程序。

如果您打算使用 cron 方法,您应该将标准输入和标准输出重定向到/ dev/null,这样您就不会收到每小时两次的电子邮件。

0 */2 * * *    root    /usr/sbin/ntpdate pool.ntp.org > /dev/null 2>&1

然而,您需要在您的每台主机上安装和维护这样一个 crontab 条目,即使这样,根据硬件的质量,系统时钟在两个小时的过程中也会有很大偏差。通过在您的主机上安装并运行 NTP 守护程序,您可以确保每当系统时钟试图失去同步时都会进行调整。这将使您的主机保持同步,也允许您使用它来同步网络上的其他主机。

NTP 守护程序和一些相关的实用程序由ntp包提供。你需要通过 CentOS 上的 yum install ntp或者 Ubuntu 上的sudo aptitude install ntp来安装它。然而每个发行版都有一个稍微不同的ntp.conf文件,我们将向你展示 Ubuntu 版本。当它启动时,ntpd服务将从/ etc/ntp.conf文件中读取它的选项,并监听 UDP 端口 123。当您查看这个配置文件时,可以看到它由两个主要部分组成:第一部分是实际时间源配置,第二部分是授权配置。我们将从清单 10-1 中所示的报告和时间源配置开始。

# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help

driftfile /var/lib/ntp/ntp.drift

# Enable this if you want statistics to be logged.
#statsdir /var/log/ntpstats/

statistics loopstats peerstats clockstats
filegen loopstats file loopstats type day enable
filegen peerstats file peerstats type day enable
filegen clockstats file clockstats type day enable

# Specify one or more NTP servers.

# Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board
# on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for
# more information.
pool 0.ubuntu.pool.ntp.org iburst
pool 1.ubuntu.pool.ntp.org iburst
pool 2.ubuntu.pool.ntp.org iburst
pool 3.ubuntu.pool.ntp.org iburst

# Use Ubuntu's ntp server as a fallback.
pool ntp.ubuntu.com

Listing 10-1.
ntp.conf

driftfile指令给服务器一个地方来存储关于本地系统时钟特性的信息。根据漂移容差,它每小时存储一次时钟频率偏移,并在守护程序启动时使用该信息。如果文件不存在,它将频率偏移设置为零。随着时间的推移,它将使用这些信息更精确地报告同步尝试之间的时间,因为守护程序知道本地时钟的行为。

默认情况下不启用统计报告,因为没有启用statsdir选项。但是,如果您取消注释该行,下一个指令statistics将启用loopstats, peerstatsclockstats向/ var/log/ntpstats中的文件报告。

loopstats收集由ntpd服务器对本地时钟进行更新的信息。peerstats记录所有对等方的信息——上游服务器以及使用您的服务器进行同步的客户端。最后,clockstats将关于本地时钟的统计信息写入日志文件。

filegen指令告诉守护程序您希望将这些统计信息写入哪个文件,以及文件需要更改的频率。在我们的例子中,由于使用了type day指令,每天都会创建这些文件的一个新版本。

最后,pool选项告诉ntpd使用哪个上游服务器进行同步。为了确保您的主机保持同步,通常为多个不同的服务器添加多个服务器指令是一个好主意。您可以使用server选项或服务器的pool来指定单独的时间服务器,就像我们在这里所做的那样。iburst选项告诉 ntp 如果没有得到初始响应,就额外发送 8 个包,最初是为像调制解调器和 ISDN 这样建立连接很慢的设备准备的。我们稍后将解释更多关于 pool.ntp.org 服务器的内容。

首先让我们快速看一下/etc/ntp.conf文件中的下一部分,它定义了哪些主机可以访问您的 NTP 服务器。在 CentOS 上,这一部分列在文件的顶部,如清单 10-2 所示。

# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery limited
restrict -6 default kod notrap nomodify nopeer noquery limited

# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1

# Needed for adding pool entries
restrict source notrap nomodify noquery

Listing 10-2.Access Control in ntp.conf

restrict关键字用于定义访问类别。这里使用-4 和-6 参数为 IPv4 和 IPv6 客户端定义了相同的访问级别。

default是一个通配符关键字,匹配所有可能的地址。Kod“死亡之吻”通过发送一个特殊的响应包来减慢超过规定速率限制的客户端。这些限制是用discard选项定义的,因为我们没有定义这样的限制,所以这里不使用它。notrap拒绝任何发送的控制数据包,而nomodify不允许修改服务器上的时间。nopeer确保您的服务器不会将连接客户端用作上游 NTP 服务器。noquery防止您的服务器被查询对等和其他统计数据。最后,limited用于在数据包违反discard选项中设置的限制时拒绝时间服务。

第二组restrict指令确保来自本地机器 127.0.0.1 (IPv4)和::1(IPv6)的连接可以询问和重新配置 NTP 服务器。不过,这些都不会阻止客户机与 NTP 服务器同步。

最后一组 restrict 指令允许池服务器成为对等服务器,这意味着我们可以向它们查询时间信息。

restrict source notrap nomodify noquery

这里的source指的是池服务器,您会注意到nopeer没有设置,但是其他限制仍然存在。

您可以使用加密密钥进一步限制 ntp 服务器,以确保只有参与交换公钥加密数据包的服务器才能使用您的 ntp 服务。这在大多数情况下并不常见,在使用 pool.ntp.org 服务器时也是不可能的。如需更多信息,请参见 www.ntp.org/ntpfaq/NTP-s-algo-crypt.htm

Note

你可以在这里找到更多关于 NTP 配置和访问控制的信息: http://doc.ntp.org/4.1.1/confopt.htm

全球 NTP 服务器池

许多组织运行自己的时间服务器,并让第三方访问它们。微软和苹果的运行时服务器被各自的操作系统默认使用,许多 Linux 供应商也是这样做的。

然而,当您想要添加额外的服务器(使用server配置)到您自己的ntp.conf文件中时,您将需要知道它们的地址。幸运的是,有一个开源项目旨在为各大洲提供一个本地 NTP 服务器池。这个项目被称为 pool.ntp.org,参与者是允许第三方使用他们的服务器进行同步的个人用户和组织。

该项目为各种服务器层和地理位置提供基于 DNS 的组,例如,1.pool.ntp.org 由层 1 服务器提供,au.pool.ntp.org 仅包含位于澳大利亚的服务器,us.pool.ntp.org 由位于美国的服务器提供。通过添加选择的 pool.ntp.org 服务器,您可以确保始终有最新的和邻近的服务器可用于同步。

Note

您可以在 www.pool.ntp.org/ 阅读更多关于该项目的信息并加入人才库。

使用host命令,我们将看到0.ubuntu.pool.ntp.org将返回什么,如清单 10-3 所示。清单 10-3 显示了我们的 ntpd 守护进程将尝试同步的服务器。

$ host 0.ubuntu.pool.ntp.org
0.ubuntu.pool.ntp.org has address 129.250.35.250
0.ubuntu.pool.ntp.org has address 129.250.35.251
0.ubuntu.pool.ntp.org has address 27.124.125.252
0.ubuntu.pool.ntp.org has address 121.0.0.41
Listing 10-3.Ubuntu ntp Pool Servers

将此与 CentOS 的0.centos.pool.ntp.org返回的数据进行比较。

$ host 0.centos.pool.ntp.org
0.centos.pool.ntp.org has address 27.124.125.252
0.centos.pool.ntp.org has address 121.0.0.41
0.centos.pool.ntp.org has address 129.250.35.250
0.centos.pool.ntp.org has address 129.250.35.251

嘿,看,它们包含相同的服务器。如果我们看一下0.au.pool.ntp.org

$ host 0.au.pool.ntp.org
0.au.pool.ntp.org has address 129.250.35.251
0.au.pool.ntp.org has address 27.124.125.252
0.au.pool.ntp.org has address 121.0.0.41
0.au.pool.ntp.org has address 129.250.35.250

很有趣吧。你应该测试来自0.us.pool.ntp.org的结果,看看你会得到什么。通常的做法是将本地网络主机设置为两个或三个本地 ntp 服务器,然后这些服务器与 ntp 池中的服务器同步。

如果您选择更改这些设置,您的 ntp.conf 可以使用 Ubuntu 上的 sudo service ntp restart或 CentOS 上的sudo systemctl restart ntpd重新启动 ntp 服务器。服务器将任何状态更新写入系统记录器;你可以在 Ubuntu 的/var/log/syslog或者 CentOS 的/var/log/messages中找到它们。清单 10-4 向您展示了一个启动后与上游服务器同步的服务器的输出。

Jun 25 09:07:00 ubuntu ntp[13644]:  * Starting NTP server ntpd
Jun 25 09:07:00 ubuntu ntpd[13653]: ntpd 4.2.8p4@1.3265-o Fri Apr  8 20:58:07 UTC 2016 (1): Starting
Jun 25 09:07:00 ubuntu ntpd[13653]: Command line: /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 112:116
Jun 25 09:07:00 ubuntu ntp[13644]:    ...done.
Jun 25 09:07:00 ubuntu systemd[1]: Started LSB: Start NTP daemon.
Jun 25 09:07:00 ubuntu ntpd[13655]: proto: precision = 0.059 usec (-24)
Jun 25 09:07:00 ubuntu ntpd[13655]: Listen and drop on 0 v6wildcard [::]:123
Jun 25 09:07:00 ubuntu ntpd[13655]: Listen and drop on 1 v4wildcard 0.0.0.0:123
Jun 25 09:07:00 ubuntu ntpd[13655]: Listen normally on 2 lo 127.0.0.1:123
Jun 25 09:07:00 ubuntu ntpd[13655]: Listen normally on 3 enp0s3 10.0.2.15:123
Jun 25 09:07:00 ubuntu ntpd[13655]: Listen normally on 4 lo [::1]:123
Jun 25 09:07:00 ubuntu ntpd[13655]: Listen normally on 5 enp0s3 [fe80::ff:86ff:fe2d:ca23%2]:123
Jun 25 09:07:00 ubuntu ntpd[13655]: Listening on routing socket on fd #22 for interface updates
Jun 25 09:07:01 ubuntu ntpd[13655]: Soliciting pool server 203.122.222.45
Jun 25 09:07:02 ubuntu ntpd[13655]: Soliciting pool server 27.124.125.251
Jun 25 09:07:02 ubuntu ntpd[13655]: Soliciting pool server 103.51.68.133
Jun 25 09:07:03 ubuntu ntpd[13655]: Soliciting pool server 130.102.128.23
Jun 25 09:07:03 ubuntu ntpd[13655]: Soliciting pool server 150.101.217.196
Jun 25 09:07:03 ubuntu ntpd[13655]: Soliciting pool server 121.0.0.41
Jun 25 09:07:04 ubuntu ntpd[13655]: Soliciting pool server 121.0.0.42
Jun 25 09:07:04 ubuntu ntpd[13655]: Soliciting pool server 202.127.210.36
Jun 25 09:07:04 ubuntu ntpd[13655]: Soliciting pool server 202.127.210.37
Jun 25 09:07:04 ubuntu ntpd[13655]: Soliciting pool server 200.160.7.186
Listing 10-4.ntpd Status in the Ubuntu System Log

我们还可以通过使用ntpq命令从本地主机查询 NTP 服务器来验证我们的主机是否同步,如清单 10-4 所示。我们使用-p选项列出我们连接到的任何对等体,使用-4选项将主机名解析为 IPv4 地址。

在图 10-1 中,您可以看到我们的对等列表的输出。以“*”开头的远程主机是当前时间源,而带有“+”的主机是已被选择用于加权平均计算的最终集合的主机(您还记得第二章中的内容吗?);那些带有-的已经被丢弃。st列是服务器层,我们通过 Ubuntu stratum 16 服务器对等到层 1 和层 2 服务器(您不需要直接对等到任何层 1 服务器)。您还可以看到关于delayoffsetjitter的其他详细信息。

A185439_2_En_10_Fig1_HTML.jpg

图 10-1。

Listing connected peers

现在,您可以配置网络上的主机,将 bastion 主机用作它们的上游 NTP 服务器,并且可以通过ntpq命令验证它们是否工作。

克罗妮

Chrony 是 NTP 服务守护程序的替代方案,它使主机与世界时钟保持同步。Chrony 和 NTP 在实现上有一些不同,但它们都使用相同的上行时间源。这两种实现的主要区别如下:

  • Chrony 不支持多播和多点传送。
  • 在网络时断时续的情况下,Chrony 非常有用。
  • Chrony 在拥塞的网络和虚拟主机中工作得更好。

安装和配置时间

Chrony 安装快捷方便,CentOS 和 Ubuntu 上都有。让我们快速演示一下如何安装和设置它。

通过两个发行版上的包管理,安装很简单。

$ sudo yum install –y chrony
$ sudo aptitude install –y chrony

CentOS 上的/etc/chrony.conf和 Ubuntu 上的/etc/chrony/chrony.conf中有一个配置文件。在该文件中,您将找到公共时间服务器和其他设置。

# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
server 0.centos.pool.ntp.org iburst
server 1.centos.pool.ntp.org iburst
server 2.centos.pool.ntp.org iburst
server 3.centos.pool.ntp.org iburst

# Ignore stratum in source selection.
stratumweight 0

# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift

# Enable kernel RTC synchronization.
rtcsync

# In first three updates step the system clock instead of slew
# if the adjustment is larger than 10 seconds.
makestep 10 3

# Allow NTP client access from local network.
#allow 192.168/16

# Listen for commands only on localhost.
bindcmdaddress 127.0.0.1
bindcmdaddress ::1

# Serve time even if not synchronized to any NTP server.
#local stratum 10

keyfile /etc/chrony.keys

# Specify the key used as password for chronyc.
commandkey 1

# Generate command key if missing.
generatecommandkey

# Disable logging of client accesses.
noclientlog

# Send a message to syslog if a clock adjustment is larger than 0.5 seconds.
logchange 0.5

logdir /var/log/chrony
#log measurements statistics tracking

Listing 10-5./etc/chrony.conf from CentOS

清单 10-5 中列出的设置类似于我们之前展示的ntp.conf文件。默认情况下,在 Ubuntu 和 CentOS 上,我们不允许客户端从这个时间服务同步。为此,我们需要如下设置允许设置:

allow 192.168/16

只有当您打算将网络中的这些主机用作本地网络时钟时,才需要设置此项。否则,您可以将其注释掉。

您可能还需要在 CentOS 上添加cmdallow设置,以使chronyc命令能够访问服务。我们很快就会看到chronyc

现在我们使用systemctl命令来启用和启动我们的服务。在 Ubuntu 上把chrony换成chronyd

$ sudo systemctl enable chronyd && sudo systemctl start chronyd

然后检查服务正在运行

$ sudo systemctl status chronyd

管理慢性与慢性

Chrony 提供了一个命令行界面工具来查询和管理名为chronyc的 Chrony。同样,这类似于如何使用ntp工具。

您可以通过键入chronyc进入命令行界面,也可以直接从 Linux 命令 shell 访问子命令。您可以获得以下帮助:

$ chronyc help

Chrony 从命令行提供跟踪信息,即关于时间服务的不同指标:

$ chronyc tracking
Reference ID    : 192.189.54.33 (warrane.connect.com.au)
Stratum         : 3
Ref time (UTC)  : Sat Oct  1 00:51:03 2016
System time     : 0.000043108 seconds slow of NTP time
Last offset     : -0.000094345 seconds
RMS offset      : 0.027604111 seconds
Frequency       : 459.036 ppm slow
Residual freq   : -0.012 ppm
Skew            : 0.430 ppm
Root delay      : 0.075154 seconds
Root dispersion : 0.012796 seconds
Update interval : 260.5 seconds
Leap status     : Normal

从上面我们可以看到,我们有各种各样的指标来描述我们的系统时间和时钟精度。我们可以看到我们正在同步的地层、系统时间和偏移。

接下来,我们可以使用以下内容查看时钟源:

$ chronyc sources
210 Number of sources = 4
MS Name/IP address             Stratum   Poll   Reach   LastRx  Last sample
===========================================================================================
^+ 0.time.itoc.com.au                2      9     377      446   +798us[ +798us] +/-   73ms
^+ dns02.ntl01.nsw.privatecl         2      9     377      249  +1507us[+1507us] +/-   52ms
^* warrane.connect.com.au            2      9     377      504  -1782us[-1876us] +/-   50ms
^+ 203.122.222.45                    2      9     377      183   -200us[ -200us] +/-   85ms

您可以使用chronyc命令添加和拒绝对 chrony 的访问,还可以添加和删除对等体。

有关更多信息,请参见以下内容:

域名系统

在第二章中,我们建议对主机使用描述性名称。我们还为主机提供了一个 IP(互联网协议)地址。当然,我们可以使用 IP 地址来访问我们的主机;然而,比起“奇怪”的数字,人类更喜欢用名字。但是我们如何将主机名映射到 IP 地址呢?我们可以使用本地主机的文件来进行地址到名称的映射,就像我们在第四章中展示的那样。但是,一旦您的网络超过了几台主机,确保该文件的所有副本保持同步就成了一项工作。

因此设计了一种将这些名称转换成主机 IP 地址的方法。这就是所谓的域名系统或 DNS。DNS 服务器维护地址到主机名(反之亦然)映射的列表,并且可以被其他主机查询,或者被用户使用各种实用程序直接查询。DNS 可用于查找本地网络中主机的 IP 地址或世界上任何网络中主机的 IP 地址。让我们看看它是如何做到这一点的,从根服务器开始。

Note

在 DNS 存在之前,使用单一的hosts.txt文件。该文件由网络信息中心(NIC)维护,并通过 FTP(文件传输协议)分发给所有与 ARPANET 连接的机器。

根服务器

不知何故,DNS 服务器需要知道向哪个或哪些主机查询正确的地址。apple.com 的 DNS 服务器不知道 google.com 的主机,那么我们自己的 DNS 服务器怎么知道去哪里找呢?

整个 DNS 结构就像一棵倒挂的大树。域名中的每个句点就像是这棵树中的一个分支。当你从左向右读一个域名时,每一个句点表示一个到树中更低一级的分裂,这更接近于根。这些级别称为区域,对于域所属的每个区域,都会执行一个查询来找出该区域的名称服务器。然后依次查询这些服务器中的一个,以获得下一个区域的 DNS 服务器。最低级别的区域(所有其他区域都是其成员)称为根区域。我们用一个周期来表示这个区域。下一级由顶级域名(TLD)组成,包括 net、com、org 和 edu 等通用域名,以及 au、nz、uk 和 us 等国家代码。图 10-2 显示了该树形结构的一小部分。

A185439_2_En_10_Fig2_HTML.jpg

图 10-2。

DNS tree structure

当书写主机名或域名时,您通常会省去根区域的尾随句点,但是当您处理 DNS 服务器时,您应该明确地包括它,因为省去它可能会导致意想不到的结果。这些顶级域名的 DNS 信息存储在所谓的根服务器中。

目前世界上有 13 个全球分布的根域名服务器返回顶级域名的权威域名服务器列表。实际上,并不是只有 13 台服务器在不停地工作,而是多个数据中心中高度分布式的专用服务器集群。在接下来的“摘要”部分,我们将看到这些服务器在委托路径中扮演的角色。

Note

由于根服务器是互联网绝对关键的核心,它们一直是网络攻击的目标。以下是汇总列表: https://en.wikipedia.org/wiki/Distributed_denial-of-service_attacks_on_root_nameservers

名目项

当一个组织或个人购买一个域时,根 DNS 服务器需要知道树中更下面的哪些 DNS 服务器已经被委托来响应对该域的查询。负责的组织。com domain 是互联网名称与数字地址分配机构(ICANN),负责管理注册商。

当您从注册商处购买域名时,您可以指定域名服务器。然后,注册商会确保您的 DNS 服务器被添加到正确的 TLD 区域,这样第三方就可以使用您的 DNS 服务器来查找您的域中的主机名。这称为委托路径。

您可以通过直接查询注册商的数据库来获得给定域的 DNS 服务器列表。用于此目的的工具whois可以方便地确保 DNS 授权是正确的。在 CentOS 和 Ubuntu 上,它由whois包提供。安装包之后,我们可以查看 google.com 域的委托的详细信息,如清单 10-6 所示。

$ whois 'domain google.com'
Whois Server Version 2.0

Domain names in the .com and .net domains can now be registered
with many different competing registrars. Go to http://www.internic.net
for detailed information.

   Domain Name: GOOGLE.COM
   Registrar: MARKMONITOR INC.
   Sponsoring Registrar IANA ID: 292
   Whois Server: whois.markmonitor.com
   Referral URL: http://www.markmonitor.com
   Name Server: NS1.GOOGLE.COM
   Name Server: NS2.GOOGLE.COM
   Name Server: NS3.GOOGLE.COM
   Name Server: NS4.GOOGLE.COM
   Status: clientDeleteProhibited https://icann.org/epp#clientDeleteProhibited
   Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
   Status: clientUpdateProhibited https://icann.org/epp#clientUpdateProhibited
   Status: serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited
   Status: serverTransferProhibited https://icann.org/epp#serverTransferProhibited
   Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
   Updated Date: 20-jul-2011
   Creation Date: 15-sep-1997
   Expiration Date: 14-sep-2020

>>> Last update of whois database: Sat, 25 Jun 2016 12:34:50 GMT <<<

Listing 10-6.Using whois to Check Delegation Details

我们发出whois命令,并指定我们只寻找domain google.com。如果我们搜索时没有指定要搜索的域,那么将会返回域名中包含 google.com 的所有域。你可以看到我们已经检索了一些关于谷歌域名的信息,包括注册商的名字;它被委派到的名称服务器;以及创建、修改和到期日期。

一些注册服务商还通过whois提供域名所有者的联系方式。这是在选择注册商购买你的域名时要记住的,因为这是垃圾邮件发送者收集电子邮件地址的一种相对方便的方式。

Note

大多数注册服务商只允许每天从特定地址对其数据库进行有限次数的查找,以阻止地址收集。

查询名称服务器

您将已经在使用由您的互联网服务提供商运行的 DNS 服务器来查找互联网上的主机地址。当你想进行网络搜索时,输入 www.google.com 比记住 74.125.19.147 要方便得多。这些 DNS 服务器的地址存储在/etc/resolv.conf file中。我们已将我们的产品列入清单 10-7;当然,你的会有所不同。

$ cat /etc/resolv.conf
search example.com
nameserver 192.168.1.1
nameserver 192.168.1.254
Listing 10-7.
/etc/resolv.conf

当您访问网站或通过 SSH 连接到主机时,相关的应用程序会使用这些 DNS 服务器执行主机查找。这些应用程序使用系统库,它首先检查您的/etc/hosts文件,然后仅在需要时查询名称服务器。

在下图 10-3 中,我们可以看到一个浏览器如何从本地 dns 请求一个 IP 地址,然后它将查询一个根服务器列表来找到它需要请求的域名服务器google.com域。

A185439_2_En_10_Fig3_HTML.jpg

图 10-3。

Simple recursive DNS query

本地 dns 服务器将首先需要向“.”根服务器请求一个可以响应“. com .”区域的服务器。然后,本地 dns 服务器将询问其中一个“. com .”服务器,哪个服务器可以回答对“. google.com”的查询。然后我们查询其中一个 nsX.google.com。“www”记录的服务器。浏览器现在知道在寻找 www 时使用哪个 IP 地址。谷歌。com

主机命令

您也可以手动查询 DNS 服务器。与 DNS 相关的工具由 CentOS 上的bind-utils包和 Ubuntu 上的dnsutils包提供,所以安装它们。可以通过host工具直接查找主机或地址。

Note

您可能习惯于使用不推荐使用的nslookup实用程序。host命令是它的替代品。

您传递您想要查找的主机名或地址,以及您想要查询的可选 DNS 服务器,如清单 10-8 所示。如果您关闭 DNS 服务器,实用程序将使用在/etc/resolv.conf中定义的服务器。

$ host www.google.com 192.168.1.1
Using domain server:
Name: 192.168.1.1
Address: 192.168.1.1#53
Aliases:

www.google.com has address 150.101.161.167
www.google.com has address 150.101.161.173
www.google.com has address 150.101.161.174
www.google.com has address 150.101.161.180
www.google.com has address 150.101.161.181
www.google.com has address 150.101.161.187
www.google.com has address 150.101.161.146
www.google.com has address 150.101.161.152
www.google.com has address 150.101.161.153
www.google.com has address 150.101.161.159
www.google.com has address 150.101.161.160
www.google.com has address 150.101.161.166
www.google.com has IPv6 address 2404:6800:4006:800::2004

Listing 10-8.Querying a DNS Server with host

在清单 10-8 中,我们要求运行在 192.168.1.1 上的 DNS 服务器查找 www.google.com 的地址,它返回几个不同的 IP 地址,包括一个 IPv6 地址。Google web 站点将对这些地址中的任何一个做出响应,并在循环中使用,返回列表的顺序会经常改变(就像我们前面看到的 ntp 池服务器一样)。

相反,我们也可以对 IP 地址的主机名进行 dns 查找。

$ host 205.251.193.236
236.193.251.205.in-addr.arpa domain name pointer ns-492.awsdns-61.com.

所做的是查询 DNS 服务器,询问它是否知道地址205.251.193.236的任何记录以及它所指的主机。它被称为反向名称查找,可能并不总是返回任何结果,因为你不必在你的 DNS 记录中记录这些信息,只是有时有这样的记录会更好。

挖掘命令

尽管 host 很有用,但它通常不能提供足够的信息来帮助解决您可能遇到的任何 DNS 问题,尤其是当您运行自己的 DNS 服务器时。更灵活的实用程序是dig,它也由bind-utilsdnsutils包提供。

在最基本的层面上,dig 也进行基于名称或地址的查找,但是它为每次查找提供了额外的信息。让我们进行与清单 10-8 中相同的查找,但是使用 dig(参见清单 10-9 )。

$ dig www.google.com

; <<>> DiG 9.10.3-P4-Ubuntu <<>> www.google.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33352
;; flags: qr rd ra; QUERY: 1, ANSWER: 12, AUTHORITY: 4, ADDITIONAL: 5

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.google.com.                     IN       A

;; ANSWER SECTION:
www.google.com.        41     IN      A       150.101.161.153
www.google.com.        41     IN      A       150.101.161.159
www.google.com.        41     IN      A       150.101.161.160
www.google.com.        41     IN      A       150.101.161.166
www.google.com.        41     IN      A       150.101.161.167
www.google.com.        41     IN      A       150.101.161.173
www.google.com.        41     IN      A       150.101.161.174
www.google.com.        41     IN      A       150.101.161.180
www.google.com.        41     IN      A       150.101.161.181
www.google.com.        41     IN      A       150.101.161.187
www.google.com.        41     IN      A       150.101.161.146
www.google.com.        41     IN      A       150.101.161.152

;; AUTHORITY SECTION:
google.com.            2071   IN      NS      ns4.google.com.
google.com.            2071   IN      NS      ns1.google.com.
google.com.            2071   IN      NS      ns3.google.com.
google.com.            2071   IN      NS      ns2.google.com.

;; ADDITIONAL SECTION:
ns1.google.com.        179    IN      A       216.239.32.10
ns2.google.com.        4851   IN      A       216.239.34.10
ns3.google.com.        186    IN      A       216.239.36.10
ns4.google.com.        8300   IN      A       216.239.38.10

;; Query time: 11 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Sun Jun 26 00:11:48 UTC 2016
;; MSG SIZE rcvd: 371

Listing 10-9.Querying a DNS Server with dig

在清单 10-9 中,您可以看到 dig 在不同的部分输出查询结果。首先是关于您正在运行的命令的一些信息,包括查询是否成功(opcode: QUERY, status: NOERROR, id: 33352)。接下来是查询部分,它显示了实际发送到 DNS 服务器的内容。在本例中,我们为主持人 www.google.com 寻找一个 A 记录。

;; QUESTION SECTION:
;www.google.com.                  IN      A

A 记录是将姓名映射到地址的记录。我们稍后将更详细地介绍记录类型。

Note

如果记录不存在,您将获得状态响应“NXDOMAIN”所有可能的响应列表见 www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6

答案部分包含对您的查询的响应。在这种情况下,它表示 www.google.com 有多个 A 记录分配给它。

;; ANSWER SECTION:
www.google.com.        41      IN      A      150.101.161.153
www.google.com.        41      IN      A      150.101.161.159
...

在 authority 部分,dig 列出了该查询的权威名称服务器。在这里,您可以看到可以从四台 DNS 服务器获得对google.com.区域的权威响应。

;; AUTHORITY SECTION:
google.com.            2071    IN     NS      ns4.google.com.
google.com.            2071    IN     NS      ns1.google.com.
google.com.            2071    IN     NS      ns3.google.com.
google.com.            2071    IN     NS      ns2.google.com.

dig在附加部分为我们提供了这四台服务器的 IP 地址。

;; ADDITIONAL SECTION:
ns1.google.com.        179     IN     A       216.239.32.10
...

最后,dig 告诉我们查询用了多长时间,查询了哪个服务器,查询是何时运行的,以及它接收了多少数据。

;; Query time: 11 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Sun Jun 26 00:11:48 UTC 2016
;; MSG SIZE rcvd: 371

实际响应数据显示在五列中。这种格式与 Berkeley Internet Name Domain(BIND)内部定义域的方式相同,其中记录使用五个字段定义,分号用于注释。这五个字段是记录名称、数据到期前的时间(更好的说法是生存时间或 TTL)、记录类(对于互联网来说实际上总是IN)、记录类型,最后是该记录的数据。

<record name>          <ttl>            <class>         <type>          <data>
www.google.com.          41               IN              A             150.101.161.153

您也可以使用dig向任何 DNS 服务器查询特定的记录类型。表 10-1 列出了最常用的记录类型。我们稍后也会设置其中的一些。

表 10-1。

DNS Record Types

| 类型 | 用于 | | --- | --- | | `SOA` | 定义域的序列号和到期信息 | | `A` | IPv4 主机名到地址的映射 | | `AAAA` | IPv6 主机名到地址的映射 | | `CNAME` | 为现有的 A 或 AAAA 记录添加别名 | | `MX` | 为域指定的邮件服务器 | | `TXT` | 文本记录,通常与 SPF 或 DKIM (MX 记录验证)以及其他机器可读数据一起使用 | | `SRV` | 与服务相关联的指定服务记录 | | `NS` | 为域指定的 dns 服务器 | | `PTR` | 将地址映射到主机名 | | `DS` | 委托签名人-在 DNSSEC 使用 | | `DNSKEY` | DNSSEC 使用的 DNSKEY 记录 | | `RRSIG` | DNSSEC 使用的资源记录签名 |

有了这些知识,您现在可以利用dig更高级的特性。我们之前只使用主机名作为参数来调用它,但是完整的命令通常看起来像dig @server name type。在我们的第一个例子中,完整的显式命令应该是dig @192.168.1.1 www.google.com A

Note

要使用host实用程序进行同类查找,请输入host -v -t <type> <name> <server>

我们之前通过whois找到了google.com域的主要 DNS 服务器。为了检查这些 DNS 服务器是否配置正确,我们可以查询它们在google.com域中的 NS type的所有记录,如清单 10-10 所示。

$ dig @ns1.google.com google.com NS

; <<>> DiG 9.10.3-P4-Ubuntu <<>> google.com NS
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44887
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 5

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;google.com.                  IN      NS

;; ANSWER SECTION:
google.com.            10158  IN      NS      ns2.google.com.
google.com.            10158  IN      NS      ns3.google.com.
google.com.            10158  IN      NS      ns1.google.com.
google.com.            10158  IN      NS      ns4.google.com.

;; ADDITIONAL SECTION:
ns1.google.com.        8267   IN      A       216.239.32.10
ns2.google.com.        12939  IN      A       216.239.34.10
ns3.google.com.        8274   IN      A       216.239.36.10
ns4.google.com.        1987   IN      A       216.239.38.10

;; Query time: 9 msec
;; SERVER: 10.0.2.3#53(10.0.2.3)
;; WHEN: Sun Jun 26 01:57:01 UTC 2016
;; MSG SIZE  rcvd: 175

Listing 10-10.Querying a DNS Server for a Specific Record Type

清单 10-10 向我们展示了ns1.google.com DNS 服务器确实有关于google.com域名的四个域名服务器的信息,所以它看起来配置正确。

有时你需要知道你的域名服务器的授权路径(列表 10-11 )。为了找出这些信息,我们可以使用带有+ trace选项 dig。

$ dig +trace www.google.com

; <<>> DiG 9.10.3-P4-Ubuntu <<>> +trace www.google.com
;; global options: +cmd
.                      9903    IN     NS      a.root-servers.net.
.                      9903    IN     NS      d.root-servers.net.
.                      9903    IN     NS      j.root-servers.net.
.                      9903    IN     NS      e.root-servers.net.
.                      9903    IN     NS      k.root-servers.net.
...<snip>...
.                      9903    IN     NS      h.root-servers.net.
...<snip>...
;; Received 913 bytes from 10.0.2.3#53(10.0.2.3) in 12 ms

com.                   172800  IN     NS      a.gtld-servers.net.
com.                   172800  IN     NS      b.gtld-servers.net.
com.                   172800  IN     NS      c.gtld-servers.net.
com.                   172800  IN     NS      d.gtld-servers.net.
com.                   172800  IN     NS      e.gtld-servers.net.
...<snip>...
com.                   172800  IN     NS      m.gtld-servers.net.
...<snip>...
;; Received 738 bytes from 192.58.128.30#53(j.root-servers.net) in 25 ms

google.com.            172800  IN     NS      ns2.google.com.
google.com.            172800  IN     NS      ns1.google.com.
google.com.            172800  IN     NS      ns3.google.com.
google.com.            172800  IN     NS      ns4.google.com.
...<snip>...
;; Received 664 bytes from 192.52.178.30#53(k.gtld-servers.net) in 176 ms

www.google.com.        300     IN     A      150.101.213.166
www.google.com.        300     IN     A      150.101.213.159
...<snip>...
www.google.com.        300     IN     A      150.101.213.174
;; Received 224 bytes from 216.239.34.10#53(ns2.google.com) in 177 ms

Listing 10-11.Seeing the Delegation Path

我们已经删除了输出中列出的一些身份验证记录,以使其更加清晰。使用+trace选项,我们可以看到“.”根区域委托给“com.”区域中的服务器,然后再委托给“google.com.”的名称服务器。如果您将 DNS 服务器设置为您的域的授权机构,您可以使用+trace选项验证您的授权路径是否正确。

Tip

通常会有一个“传播延迟”,因为 DNS 世界会根据更新进行自我重组。这种延迟取决于记录 TTL 和域名服务器更新其记录的频率。使用 dig 有助于展示 DNS 世界。

区域元数据

我们前面提到,dig 结果中列出的列之一是 TTL。此字段定义 DNS 记录的有效期,这允许您的本地应用程序将 DNS 查找的结果缓存一段时间。这样,就不需要为您建立的每个连接执行多次 DNS 查找(记住,首先要执行一次或多次查找来查找权威的 DNS 服务器),这大大加快了建立网络连接的过程。

另一种重要的类型叫做SOA,代表权威的开始。该记录包含关于该区域的元信息。例如,它包括一个序列号,这样服务器可以检查区域是否被更改,它还为服务器管理员定义了一个联系电子邮件。

让我们向其中一个 Google 服务器请求google.com域的SOA记录(列出 10-12 )。我们已经把权威和额外的部分从输出中去掉了。

$ dig google.com @ns1.google.com SOA
; <<>> DiG 9.10.3-P4-Ubuntu <<>> google.com @ns1.google.com SOA
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43145
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 4, ADDITIONAL: 4
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;google.com.        IN   SOA

;; ANSWER SECTION:
google.com.    60   IN    SOA    ns2.google.com. dns-admin.google.com. 125880391 900 900 1800 60

Listing 10-12.Querying a DNS Server for an SOA Record

清单 10-12 显示 SOA 记录由七个字段组成,它们定义了其他 DNS 服务器如何与该区域交互。在 DNS 类型(SOA)之后,您将看到七个字段。

<name server>    <admin email address>      serial #     refresh   retry  expiry   nx ttl
ns2.google.com.   dns-admin.google.com.    125880391         900    900     1800       60

我们稍后会更详细地讨论它们,但我们想提一下列表中的最后一项,即负缓存 TTL,或 nx ttl。这告诉其他 dns 服务器缓存否定结果(“NXDOMAIN”),以防止权威名称服务器连续查找不存在的主机。在这种情况下,远程服务器应该在初始查询后的 60 秒内继续响应“没有这样的主机”,然后再查询权威名称服务器。这段时间可能超过 60 秒,有些可能长达一周。

运行缓存 DNS

并非所有 ISP 的域名服务器都同样可靠,有些可能会很慢,所以我们为什么不自己运行呢?有一些 DNS 服务器软件包可用,但最常用和最著名的是伯克利互联网域名(BIND)。

Note

BIND 是以它的开发地命名的,加州大学伯克利分校。

软件由 CentOS 上的bind包提供。你通过yum install bind安装这些。在 Ubuntu 上,这些是通过sudo aptitude install bind9添加的bind9包提供的。DNS 服务器二进制文件本身被称为named,因为它是名称(服务器)守护进程。

Ubuntu 上的主配置文件是/etc/bind/named.conf,而 CentOS 上使用的是/etc/named.conf文件。清单 10-13 向您展示了 Ubuntu 附带的基本文件。

// This is the primary configuration file for the BIND DNS server named.
//
// Please read /usr/share/doc/bind9/README.Debian.gz for information on the
// structure of BIND configuration files in Debian, *BEFORE* you customize
// this configuration file.
//
// If you are just adding zones, please do that in /etc/bind/named.conf.local

include "/etc/bind/named.conf.options";
include "/etc/bind/named.conf.local";
include "/etc/bind/named.conf.default-zones";

Listing 10-13.The Top of /etc/bind/named.conf in Ubuntu

该文件包含对其他文件的引用,这些文件包含实际的配置设置和(可选)关于本地托管的域的信息。这些配置文件中的注释以双斜杠(//)为前缀,所有指令和块以分号(;)结束。

include指令告诉named读取指定的文件并处理它包含的任何指令,包括嵌套的include命令。在这种情况下,named.conf.options文件包含选项部分,这影响了named的操作方式。这是您可以编辑的文件,以便对您在 Ubuntu 上的配置进行更改(参见清单 10-14 )。

options {
        directory "/var/cache/bind";

        // If there is a firewall between you and nameservers you want
        // to talk to, you may need to fix the firewall to allow multiple
        // ports to talk.  See http://www.kb.cert.org/vuls/id/800113

        // If your ISP provided one or more IP addresses for stable
        // nameservers, you probably want to use them as forwarders.
        // Uncomment the following block, and insert the addresses replacing
        // the all-0's placeholder.

        // forwarders {
        //     0.0.0.0;
        // };

        //========================================================================
        // If BIND logs error messages about the root key being expired,
        // you will need to update your keys.  See https://www.isc.org/bind-keys
        //========================================================================
        dnssec-validation auto;

        auth-nxdomain no;    # conform to RFC1035
        listen-on-v6 { any; };
};

Listing 10-14.Default named Options in Ubuntu

指令directory决定了位置named将被用来寻找文件和写任何文件,如果它被配置这样做的话。您可以通过指定一个以/开头的完整系统路径来覆盖单个文件。

转发器是命名调用上游 DNS 服务器。如果您希望您的高速缓存名称服务器只使用您的 ISP 的名称服务器或一组其他名称服务器,您可以在forwarders块中列出它们的 IP 地址,每个地址单独占一行,以分号结束。

下一个选项dnssec-validation默认为自动。这意味着,如果它收到委派签名人记录,它将尝试验证来自启用了 dnsSEC 的 DNS 服务器的回复。dnsSEC 旨在防止响应篡改或 dns 缓存中毒,恶意行为者可以将虚假记录注入 DNS 缓存服务器,并将互联网流量路由到他们选择的主机。

在图 10-4 中,我们使用 dig 工具找到更多关于google.com dnssec 记录的信息。

A185439_2_En_10_Fig4_HTML.jpg

图 10-4。

Google’s DNSSEC records

如果您不得不斜视,我们很抱歉,但是我们使用了dig google.com ds +dnssec命令来查询 dnssec 的委托签名记录。看看下面的边栏,了解更多关于它们的意思。

DNSSEC

DNSSEC 使用交换加密签名的过程,并致力于“信任链”模型。这意味着您可以根据签署记录的密钥来验证记录的真实性,然后由您信任的人来验证这些记录。

众所周知,DNS 树的顶端被称为根。区域。它们下面是顶级域名(如“. com .”、“. org .”、“. net .”、“. io .”等)。在它们下面是许多不同的领域。DNSSEC 提供了一种方法,根区域验证来自 TLD 的记录,然后 TLD 可以验证其下其他域的记录。

它通过签署资源记录集(例如,RRset 是区域文件中的所有 MX 资源记录,或所有 NS 资源记录,或所有 AAAA 资源记录)来实现这一点。这些 RRsets 由区域签名私钥签名。这些记录作为 RRSIG 记录存储在名称服务器中。

区域签名密钥对的公钥称为 DNSKEY,它也作为记录发布在名称服务器上。DNSKEY 用于验证 RRSIG 签名,以验证 RRset 的真实性。

然而,我们现在需要验证 DNSKEY 我们怎么知道它是真的?为此,我们有一个密钥签名密钥对。即公钥签名密钥(也称为 DNSKEY)和私钥签名密钥。我们创建 DNSKEYs 的 RRset,并使用私钥签名密钥对它们进行签名。这产生了另一个 RRSIG。这样,公钥签名密钥可以用于验证公共区域签名密钥。

在这个阶段,我们可以验证 RRset 和/或公共 DNSKEYS。但是我们还信任给我们这些文件的服务器吗?不,我们没有。这就是 TLD 的用武之地。我们给我们的 TLD 一份我们的公钥签名密钥,他们用它来创建一个委托歌手唱片(DS)。该记录是密钥签名密钥 DNSKEY 的散列。因此,每当一个解析器被称为 TLD 的“子”时,这个 DS 记录就被包括在内。

解析程序使用该 DS 记录来验证公钥签名密钥的真实性。它通过对公钥签名密钥执行哈希运算并将其与 DS 记录进行比较来实现这一点。如果它们匹配,那么我们可以信任其余的密钥。

作为 DNS 基础设施的守护者,ICANN 控制着根,并为我们在 DNSSEC 使用的整个互联网创建 RRSIG 和 DNSKEYs 记录。然后,它为所有 TLD 提供 DS 记录。反过来,它们可以为其下的任何内容提供 ds 记录,这些可以为其下的任何内容创建 DS 记录,以此类推。

在下图中你会看到,当我们请求 www.google.com 时,根服务器会给我们发送一个 RRSIG 和一个“. com .”TLD 的 DS。我们将使用公开可用的公钥签名 DNSKEY 和 RRSIG 来验证 RRset 此时,我们没有父记录或 DS 记录来验证 DNSKEY,因此我们“信任”顶级域。

然后,我们使用该 RRset 来查找地址,以查询“. com .”名称服务器。在这里,我们对 RRSIG 和 DNSKEY 进行散列,并将其与我们从根域的 DS 记录中收到的散列进行比较。如果它们匹配,它们就是有效的,然后我们将使用这些 RRsets 来查找google.com的名称服务器。在google.com名称服务器处,我们接收 RRSIG。我们散列 DNSKEY 和 RRSIG 并将其与。com DS 记录有;如果它们匹配,那么我们可以信任来自谷歌域名服务器的密钥。

A185439_2_En_10_Figa_HTML.jpg

对根域的明确信任有一个有趣的人类副作用。为了有趣地了解根密钥签署仪式,请阅读以下内容: www.cloudflare.com/dnssec/root-signing-ceremony/

有关 DNSSEC 的详细解释,请参见以下内容:

下一个选项auth-nxdomain设置为 no。这控制名称服务器如何响应它认为不存在的域的查找,这意味着如果您的本地 DNS 服务器找不到有关域的信息,它不会声称自己是权威的。这反过来意味着,如果客户端无法找到有关域的信息,它可以继续查询其他 DNS 服务器。

最后,listen-on-v6选项告诉 BIND 它应该监听所有网络接口上所有可用 IPv6 地址的查询。

为了避免先有鸡还是先有蛋的问题,缓存 DNS 服务器附带了根服务器的内置列表。你可以在 CentOS 的/var/named/named.ca和 Ubuntu 的/etc/bind/db.root中找到它们。您还可以使用 dig 来获取根服务器的当前列表,方法是在根服务器上查询“根服务器”中所有类型为NS的记录区域。

$ dig @a.root-servers.net . NS > db.root.

现在让我们看看在 CentOS 上安装 BIND 包时安装的/etc/named.conf文件(见清单 10-15 )。

//
// named.conf
//
// Provided by Red Hat bind package to configure the ISC BIND named(8) DNS
// server as a caching only nameserver (as a localhost DNS resolver only).
//
// See /usr/share/doc/bind*/sample/ for example named configuration files.
//

options {
        listen-on port 53 { 127.0.0.1; };
        listen-on-v6 port 53 { ::1; };
        directory      "/var/named";
        dump-file      "/var/named/data/cache_dump.db";
        statistics-file "/var/named/data/named_stats.txt";
        memstatistics-file "/var/named/data/named_mem_stats.txt";
        allow-query     { localhost; };

        /*
         - If you are building an AUTHORITATIVE DNS server, do NOT enable recursion.
         - If you are building a RECURSIVE

(caching) DNS server, you need to enable
           recursion.
         - If your recursive DNS server has a public IP address, you MUST enable access
           control to limit queries to your legitimate users. Failing to do so will
           cause your server to become part of large scale DNS amplification
           attacks. Implementing BCP38 within your network would greatly
           reduce such attack surface
        */
        recursion yes;

        dnssec-enable yes;
        dnssec-validation yes;

        /* Path to ISC DLV key */
        bindkeys-file "/etc/named.iscdlv.key";

        managed-keys-directory "/var/named/dynamic";

        pid-file "/run/named/named.pid";
        session-keyfile "/run/named/session.key";
};

logging {
        channel default_debug {
                file "data/named.run";
                severity dynamic;
        };
};

zone "." IN {
        type hint;
        file "named.ca";
};

include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";

Listing 10-15.CentOS /etc/named.conf

CentOS 和 Ubuntu 文件的主要区别在于您用来存储named数据的位置。dump-file指令允许named在退出时将临时数据写入文件。当它再次启动时,可以重新读取这些数据。statistics-file定义了named将关于它收到的查询的类型和数量的统计信息写到哪里。

此绑定服务器设置为在本地主机上侦听 IPv4 和 IPv6。使用这种配置,网络上的其他主机将无法使用此服务。我们需要更改监听(-v6)来监听一个可到达的 IP 地址。

listen-on port 53 { 127.0.0.1; 192.168.1.1; };
listen-on-v6 port 53 { ::1; };

清单 10-5 中的下一个设置是递归(应该是‘yes’),因为我们没有使用这个 dns 服务器作为权威的名称服务器(这意味着它不会被查询任何区域本身,而是询问其他 dns 服务器)。我们通过将 dnssec 和 dnssec-validation 都设置为“yes”来启用它们。bindkeys-file 指向iscdlv.key路径。当没有 DS 记录时,在 dnssec 验证中使用这个键(现在这种情况不太常见,因为 DNSSEC 几乎在所有地方都推出了)。

然后我们有日志选项。您可以在 CentOS 上查看/var/named/data/named.run中的日志,并且可以动态调整严重性级别。

接下来是根区域文件。这是您从dig @a.root-servers.net . NS接收到的输出,您将把它放在名为/var/named/named.ca的文件中。Bind 使用它来查找递归查找的根服务器。

最后,就像我们在 Ubuntu named.conf文件中看到的,我们可以用include指令包含其他配置文件。

我们现在可以通过sudo systemctl start named命令启动名称服务器。在 Ubuntu 上,名称服务器在安装时会自动启动,但是如果它没有运行,我们可以通过sudo service bind9 start来启动它。

为了能够查询我们的新 DNS 服务器,我们需要确保防火墙没有阻止流量。DNS 默认在端口号 53 上使用 UDP 协议,但是如果响应包含大量数据,它将切换到 TCP。为您的网络布局向正确的 Netfilter 链添加适当的规则。

$ sudo /sbin/iptables –t filter –A INPUT -p udp --dport 53 -j ACCEPT
$ sudo /sbin/iptables –t filter –A INPUT -p tcp --dport 53 -j ACCEPT

Note

确保将 DNS 主机上的防火墙配置为也允许传出 DNS 响应。我们在第七章中讲述了防火墙和iptables

我们现在有了自己的缓存 DNS 服务器,可以用来进行查找。我们称它为缓存 DNS 服务器,因为它保存了我们所做的任何查询的答案,所以下次我们执行相同的查询时,它可以立即用缓存的信息做出响应。

为了确保它能够工作,我们将直接查询它,如清单 10-16 所示。

$ host www.google.com localhost
Using domain server:
Name: localhost
Address: 127.0.0.1#53
Aliases:

www.google.com has address 216.58.220.132
www.google.com has IPv6 address 2404:6800:4006:806::2004

Listing 10-16.Querying Our Local Caching DNS Server

我们要求运行在 localhost 上的 DNS 服务器查找 www.google.com 的地址,它响应了,所以它工作了!

有了有效的缓存 DNS,我们可以用nameserver 192.168.0.1替换/etc/resolv.conf file中的nameserver条目,以使用我们自己的服务器。我们还可以将这个 DNS 服务器添加到本地网络中任何其他主机上的resolv.conf files中。

权威 DNS

如果您需要将主机名映射到您自己网络中主机的 IP 地址,那么 DNS 缓存服务器就不行。你需要有一个权威的 DNS 服务器。权威 DNS 服务器是区域的权威信息源。一个权威的 DNS 将为我们的本地网络提供 DNS 解析,这将容纳我们的 example.com 域。为此,我们定义了两个区域:一个提供从名称到地址的映射,另一个提供从地址到名称的反向映射。

再分区

域被描述为区域。区域是在区域文件中定义的,很像我们前面提到的根区域文件。区域文件总是包含一个标题,也称为 SOA 记录。这个头后面可选地跟着定义服务和主机的 DNS 记录。我们在清单 10-17 中包含了一个示例区域文件头。

$ORIGIN example.com.
$TTL 86400
@   IN    SOA    example.com.    root.example.com. (
       2016070100  ; Serial
       604800          ; Refresh
       86400            ; Retry
       2419200        ; Expire
       3600 )            ; Negative Cache TTL
Listing 10-17.Zone File Header for the example.com Domain

这个头定义了一些关于我们的区域的元信息,缓存 DNS 服务器和我们可能定义的任何从属服务器都使用这些元信息。从属服务器是权威的 DNS 服务器,可以从主 DNS 服务器自动检索它们的区域信息。你可以用它们来提供冗余的 DNS 服务,就像你的 ISP 所做的那样。

Note

DNS 是一个读取量很大的数据库,写入量很少,因此它可以很容易地扩展到许多从服务器。

我们已经在表 10-2 中列出了区域标题中的字段及其用途。在我们的例子中,我们以秒为单位列出了所有的时间,但是你也可以用1d代替86400来表示一天,或者用4w代替2419200来表示四周。

表 10-2。

Zone Header Fields

| 田 | 使用 | | --- | --- | | $来源 | 定义区域的起点 | | $TTL | 生存时间,这是该区域中没有设置自己的到期时间的记录的默认到期时间 | | 前进速度 | 授权的开始,它包含区域元数据的七个记录 | | 掌握 | 此域的主要权威 DNS 服务器 | | 接触 | 此域的联系人的电子邮件地址,用句点替换 at 符号(@) | | 连续的 | 定义从属名称服务器使用的该区域文件的版本 | | 恢复精神 | 定义从属服务器更新其该区域副本的频率 | | 重试 | 定义尝试刷新从属服务器的时间间隔 | | 期满 | 定义允许从属服务器使用该区域文件任何版本的时间 | | 负缓存 TTL | 定义失败的查找结果可以缓存多长时间 |

同样值得注意的是,我们使用的是基于当前日期的序列号。您可以使用 YYYYMMDD 表示当前的年、月和日,后跟一个两位数。这使人们可以很容易地看到该区域最后一次更改的时间,同时每天仍允许 99 次更改。您也可以选择使用基于 unix 纪元时间(自 1970 年 1 月 1 日以来的秒数)的日期。格式是一个十位数的字符串,可以用下面的命令生成:$ date +%s。另一种方法是,您可以使用一个简单的递增数字作为序列号。SOA前面的 at 符号(@)表示当前区域的名称。我们也可以输入 example.com。在它的位置上。

该区域的 TTL 可能会有很大差异。较小的值意味着记录将被其他 DNS 服务器缓存较短的时间。这意味着您的 DNS 服务器将被更频繁地查询,而更长的 TTLs 意味着对您的 DNS 服务器的查询更少。如果您的主机 IP 经常变化,您会希望该值较小。如果不是,那么更长的时间是合适的。

正向查找区域

通过文本编辑器很容易创建区域文件,并将其定义添加到/etc/named.conf.local文件中。我们将把我们的正向查找区域存储在一个名为example.com.db的文件中。

在我们继续之前,需要注意 CentOS 和 Ubuntu 之间的一些区别。区域文件存储在/var/named/中,Ubuntu 存储在/var/cache/bind中。在 CentOS 上运行 DNS 服务的守护进程称为named,由named用户运行。另一方面,Ubuntu 使用bind用户运行bind守护进程。在下面的例子中,我们将使用 Ubuntu 服务器。因为只有根用户可以写入区域文件目录,所以我们使用 sudo 启动编辑器。

$ sudo vim /var/cache/bind/master/example.com.db

我们将这些权威区域文件放在主目录中。我们很快会向你展示奴隶是如何运作的。根据您的发行版,您需要确保目录/var/cache/bind/master/var/named/master存在。现在,我们简单地将清单 10-18 中的区域标题复制并粘贴到这个文件中,然后保存它。头文件完成后,我们可以开始将实际的主机和服务记录添加到这个文件中。

您的区域中需要有两种基本的服务记录类型。一个是 NS 记录,它定义了哪个主机充当这个域的 DNS 服务器,另一个是MX记录,它定义了这个域的邮件服务器。两条记录都以空白字段开始,因为它们没有定义主机名。

IN      NS           ns.example.com.
IN      MX      10   mail.example.com.
Listing 10-18.Our Service Records

MX 记录的数据包括一个优先级数字,然后是远程服务器应该尝试向其发送邮件的主机名。正确配置的远程邮件服务器将处理 MX 记录列表,从最低优先级数字开始,并尝试传递电子邮件。注意,我们已经指定了一个完全合格的域名(fully 即主机名加上完整的域名),并在这些条目的后面加上一个句点。如果我们省略了尾随句点,DNS 服务器会认为我们只定义了主机名,并会自动将$ORIGIN 附加到这些记录的末尾。

我们已经在这些定义中使用了 ns 和邮件主机名,但是我们还没有在区域文件中定义这些主机,所以接下来让我们这样做(参见清单 10-19 )。主机到地址的记录称为 A 记录,记住 AAAA 记录是针对 IPv6 的。我们还将为当前主机名添加一个 A 记录。

@                   IN    A    192.168.0.1
ns                  IN    A    192.168.0.254
mail                IN    A    192.168.0.1
au-mel-ubuntu-1     IN    A    192.168.0.1
Listing 10-19.Creating A Records for Our Domain

我们没有在 host 列中为这些记录指定 FQDN,因此 DNS 服务器会将它们视为附加了$ORIGIN ( example.com.)的记录,这正是我们想要的。@符号也被替换为 origin,因此用户也可以通过访问域来访问主机。

您会注意到,这两个名称现在将解析为同一个地址。一个 IP 地址可以有任意多的相关记录。前向区域中的另一种类型的 ofd 记录被称为 CNAME,也称为别名。

当您希望将多个别名与一台主机相关联,并且仍然能够更改该主机的地址而无需更改一长串记录时,可以使用 CNAME。例如,我们的主机 au-mel-ubuntu-1 需要提供 web 和 SQL 服务,邮件服务器也将提供 POP 和 IMAP 访问。我们可以创建一些 CNAME 条目来提供别名,这些别名都指向邮件 A 条目(参见清单 10-20 )。将来,如果我们将邮件服务迁移到不同的主机,我们只需更改 A 记录,所有 CNAME 条目也会自动指向新地址。

gateway          IN    CNAME     ns.example.com.
headoffice       IN    CNAME     au-mel-ubuntu-1.example.com.
smtp             IN    CNAME     mail.example.com.
pop              IN    CNAME     mail.example.com.
imap             IN    CNAME     mail.example.com.
www              IN    CNAME     au-mel-ubuntu-1.example.com.
sql              IN    CNAME     au-mel-ubuntu-1.example.com.
Listing 10-20.Adding Some CNAME Entries

我们还创建了名为gatewayheadoffice的 CNAMEs,我们将在第十五章的中建立虚拟专用网络时使用它们。这就是我们目前所需要的。我们将保存该文件并创建一个附带的反向区域文件,该文件将提供地址到名称的映射。

反向查找区域

为了设置一个反向区,你需要先搞清楚它叫什么。与转发区域不同,它没有域名,但有唯一的地址范围。为了提供地址查找,使用了一个名为 in-addr.arpa .的特殊域。这实际上是反向映射的根区域。

就像转发区域一样,您将网络地址的各个部分添加到这个区域,最重要的部分放在右边。对于我们的 192.168.0.x 网络,这导致 0.168.192.in-addr.arpa .反向区域名称。

Note

In-addr.arpa. zones 总是以多达四分之三的点号为前缀。对于少于 255 个地址的子网,没有建立反向区域的标准方法。

我们再次启动我们的编辑器(作为 root 用户)来创建一个新的区域文件。

$ sudo vim /var/cache/bind/192.168.0.db

这个文件中的标题需要与我们的转发区域稍有不同,因为区域名称不同。添加清单 10-21 的内容。

$ORIGIN 0.168.192.in-addr.arpa.
$TTL  86400
@  IN  SOA    ns.example.com.    root.example.com. (
       2016070100 ; Serial
       604800     ; Refresh
       86400      ; Retry
       2419200    ; Expire
       3600 )     ; Negative Cache TTL
Listing 10-21.The Reverse Zone Header

创建了头之后,我们现在可以开始添加 PTR 记录,它将地址映射到姓名。让我们为我们的堡垒主机和 192.168.0.254 上的主机以及邮件 A 记录添加一个,如清单 10-22 所示。

IN      NS     ns.example.com.
1       PTR    mail.example.com
1       PTR    au-mel-ubuntu-1.example.com.
254     PTR    ns.example.com.
Listing 10-22.Adding PTR Records for Our Hosts

我们保存反向区域文件并退出编辑器。现在剩下要做的就是将这两个区域的区域定义添加到/etc/bind/named.conf.local中。我们打开这个文件并添加定义,如清单 10-23 所示。每个区域指令块都包含对定义区域的文件的引用。默认情况下,服务器希望这些文件位于主配置文件中指定的目录中。因为我们是提供这些区域的权威 DNS 服务器,所以我们需要将区域类型设置为主。

zone "example.com" {
    type master;
    file "master/example.com.db";
};

zone "0.168.192.in-addr.arpa" {
    type master;
    file "master/192.168.0.db";
};

Listing 10-23.Adding Zone Definitions

然后我们保存文件并退出编辑器。我们需要告诉服务器重新加载它的配置,要么通过 systemctl reload or service重新启动服务器,要么使用rndc实用程序。后者要快得多,而且不会中断服务,所以我们就这么做吧。

$ sudo rndc reload
server reload successful

rndc 实用程序用于控制命名(绑定)服务器。它不仅可以重新加载命名配置(在您编辑文件之后),还可以重新加载特定区域、更改日志记录级别和刷新缓存。在本章中,我们将向您展示 rndc 的更多用途。

名称服务器应该知道我们的新区域,我们可以查询它来检查这一点。让我们从查找ns.example.com的地址开始,如清单 10-24 所示。

$ host ns.example.com localhost
Using domain server:
Name: localhost
Address: 127.0.0.1#53
Aliases:

ns.example.com has address 192.168.0.254

Listing 10-24.Testing Forward Name Resolution

那很好。让我们通过查找与 192.168.0.1 地址相关联的名称来检查反向区域是否工作,如清单 10-25 所示。

$ host 192.168.0.1 localhost
Using domain server:
Name: localhost
Address: 127.0.0.1#53
Aliases:

1.0.168.192.in-addr.arpa domain name pointer au-mel-ubuntu-1.example.com.
1.0.168.192.in-addr.arpa domain name pointer mail.example.com.0.168.192.in-addr.arpa.

Listing 10-25.Testing Reverse Name Resolution

那不太对!名称服务器已将反向区域名称附加到mail.example.com主机上。不过,我们知道通常是什么导致了这种情况,所以如果我们去检查反向区域文件,我们可以看到我们确实忘记了mail.example.com条目末尾的尾随句点。

但是让我们花点时间研究一下如何在我们的 bind 服务器上查看查询日志。再次使用 rndc 实用程序,我们可以发出以下命令:

$ sudo rndc querylog on

如果我们跟踪/var/log/syslog(在 Ubuntu 上),我们将看到以下内容:

Jun 28 12:23:26 localhost named[2548]: received control channel command 'querylog on'
Jun 28 12:23:26 localhost named[2548]: query logging is now on

我们现在可以在系统日志中看到 DNS 查询,如下所示:

Jun 28 12:25:31 localhost named[2548]: client 127.0.0.1#47616 (1.0.168.192.in-addr.arpa): query: 1.0.168.192.in-addr.arpa IN PTR + (127.0.0.1)

要关闭查询日志记录,我们只需发出以下命令:

$ sudo rndc querylog off

我们现在将添加句点并增加区域序列号,记录现在应该如下所示:

$ORIGIN 0.168.192.in-addr.arpa.
$TTL  86400
@  IN  SOA    ns.example.com.    root.example.com. (
       2016070100  ; Serial
       604800          ; Refresh
       86400            ; Retry
       2419200        ; Expire
       3600 )            ; Negative Cache TTL

       IN  NS      ns.example.com.

1      PTR    mail.example.com.
1      PTR    au-mel-ubuntu-1.example.com.
254    PTR    ns.example.com.

完成后,我们再次发出sudo rndc reload命令。如果我们再次测试反向分辨率,我们可以看到问题已经解决。

$ host 192.168.0.1 localhost
Using domain server:
Name: localhost
Address: 127.0.0.1#53
Aliases:

1.0.168.192.in-addr.arpa domain name pointer au-mel-ubuntu-1.example.com.
1.0.168.192.in-addr.arpa domain name

pointer mail.example.com.

安全考虑

我们现在在 bastion 主机上运行 DNS 服务器,作为权威和缓存 DNS 服务器。尽管该软件可以很好地处理这个问题,但还是有一些安全考虑。其中最主要的一个是由于一种被称为 DNS 缓存中毒的攻击,它允许攻击者使您的缓存 DNS 服务器分发不正确的地址。这可能导致用户单击恶意 web 链接或打开带有嵌入链接的电子邮件。

Note

你可以在 http://en.wikipedia.org/wiki/ DNS_cache_poisoning了解更多关于 DNS 缓存中毒的信息。

幸运的是,BIND 为我们提供了一种方法,通过 DNSSEC(DNS 协议的安全扩展)来缓解这个问题。对于 bind 的最新版本(bind v9.7 及更高版本),我们可以使用自动密钥签名,它会在定义的时间间隔自动签名和重新签名区域。从 9.9 版本开始,我们可以使用一种称为“内联”密钥签名的功能。

我们可以创造两种记录,NSEC 和 NSEC3。根据您的需求,您可以实现任何合适的。它们都提供“认证的否认存在”,这意味着如果没有记录,那么您可以信任服务器的响应。这两个记录之间的主要区别在于 NSEC3 防止了区域遍历,在区域遍历中,您可以根据哪些记录不在那里来构建域图。你可以在 www.internetsociety.org/deploy360/resources/dnssec-nsec-vs-nsec3/ 了解更多差异。

Tip

接下来的几个步骤将需要大量的系统熵来创建高质量的加密密钥。你应该安装haveged,这是一个守护进程,将提供额外的熵源。详见 www.issihosts.com/haveged/

我们将在/etc/bind/keys目录中创建并存储我们的密钥。我们需要创建该目录,然后将本地区域的绑定配置文件更改为如下所示:

zone "example.com" {
    type master;
    file "master/example.com.db";
    key-directory "/etc/bind/keys";
    auto-dnssec maintain;
    inline-signing yes;
};

zone "0.168.192.in-addr.arpa" {
    type master;
    file "master/192.168.0.db";
    key-directory "/etc/bind/keys";
    auto-dnssec maintain;
    inline-signing yes;
};

我们使用key- directory指令告诉 bind 在哪里找到我们要创建的键。我们现在将创建这些密钥。为此,我们需要dnssec-keygen命令来创建一个区域签名密钥(ZSK)和一个密钥签名密钥(KSK)。转到/etc/bind/keys目录,我们将创建我们的 ZSK。

$ sudo dnssec-keygen -a RSASHA256 -b 2048 -3 example.com
Generating key pair...............................................+++ ...............+++
Kexample.net.+008+50331

现在我们需要一个 KSK 来验证我们的区域密钥。

$ sudo dnssec-keygen -a RSASHA256 -b 2048 -f KSK -3 example.com
Generating key pair........................................................................+++ ...........................+++
Kexample.net.+008+62695

查看目录内部,我们看到生成了四个密钥。两个私钥,以.private结尾,两个公钥,以.key结尾。

-rw-r--r-- 1 root bind  606 Jul  3 02:41  Kexample.net.+008+50331.key
-rw------- 1 root bind 1776 Jul  3 02:41  Kexample.net.+008+50331.private
-rw-r--r-- 1 root bind  605 Jul  3 02:44  Kexample.net.+008+62695.key
-rw------- 1 root bind 1776 Jul  3 02:44  Kexample.net.+008+62695.private

私钥的副本应该存储在安全的地方,就像我们存储所有高度敏感的信息一样。您会注意到这些密钥是由root用户拥有的。我们需要将文件的所有者改为bind (Ubuntu)。有了密钥,我们将使用$ sudo systemctl start bind9命令启动我们的绑定服务器,或者如果您的绑定服务器已经在运行,我们可以运行$ sudo rndc reconfig。我们可以在另一个终端窗口或 shell 中使用$ sudo journalctl –x –u bind9命令查看日志。

我们将发出以下命令来确保我们的区域被签名。

$ sudo rndc sign example.com
$ sudo rndc signing -list example.com
Done signing with key 814/RSASHA256
Done signing with key 62760/RSASHA256

在清单 10-26 中,我们可以看到来自journalctl命令的日志。

Jul 06 13:01:59 ubuntu-xenial named[12461]: received control channel command 'sign example.com'
Jul 06 13:01:59 ubuntu-xenial named[12461]: zone example.com/IN (signed): reconfiguring zone keys
Jul 06 13:01:59 ubuntu-xenial named[12461]: zone example.com/IN (signed): next key event: 06-Jul-2016 14:01:59.665
Jul 06 13:02:09 ubuntu-xenial named[12461]: received control channel command 'signing -list example.com'
Jul 06 13:04:40 ubuntu-xenial named[12461]: received control channel command 'sign 0.168.192.in-addr.arpa'
Jul 06 13:04:40 ubuntu-xenial named[12461]: zone 0.168.192.in-addr.arpa/IN (signed): reconfiguring zone keys
Jul 06 13:04:40 ubuntu-xenial named[12461]: zone 0.168.192.in-addr.arpa/IN (signed): next key event: 06-Jul-2016 14:04:40.598
Listing 10-26.bind journalctl Log

您可以看到绑定服务器已经自动签署了我们的区域(zone example.com/IN (signed): loaded serial 2016070100)。您还可以看到我们有一个“next key event”,这是重新生成密钥的时间。现在看看我们的区域文件目录。

$ ll /var/cache/bind/master/
total 48
drwxr-xr-x 2 bind bind  4096 Jul  3 12:46 ./
drwxrwxr-x 3 root bind  4096 Jul  3 12:47 ../
-rw-r--r-- 1 bind bind   346 Jul  3 12:35 192.168.0.db
-rw-r--r-- 1 bind bind   512 Jul  3 12:35 192.168.0.db.jbk
-rw-r--r-- 1 bind bind   349 Jul  3 12:35 192.168.0.db.signed
-rw-r--r-- 1 bind bind   899 Jul  3 12:25 example.com.db
-rw-r--r-- 1 bind bind   512 Jul  3 12:35 example.com.db.jbk
-rw-r--r-- 1 bind bind   839 Jul  3 12:35 example.com.db.signed
-rw-r--r-- 1 bind bind 13384 Jul  3 12:46 example.com.db.signed.jnl

我们现在有已经签名的区域文件(example.com.db.signed)。我们现在可以验证我们的密钥是否如我们所期望的那样进行了签名。

$ dig +dnssec +multiline @127.0.0.1 mail.example.com
mail.example.com.      86400 IN A 192.168.0.1
mail.example.com.      86400 IN RRSIG A 8 3 86400 (
                              20160802120825 20160703120630 6513 example.com.
                              <snip>
                              TorDjrwEutOJnt1HLxoJ/+EVJ6K9l+sZfrfG4ZM4lB5i
                              eVxmZe3quQ3M+HHDHPVwZu1XwJkNz97Kuw== )

mail

.example.com. 86400 IN RRSIG A 8 3 86400 (
                              20160802124245 20160703120630 65028 example.com.
                              <snip>
                              qGxaP6lJ+WKbIhw3NoqSd++E6bVUU5L46qaxczIhact3
                              xZEOwrAnAQ2MSq9Qx1b41ghbwfVBUOMQZQ== )

添加从属服务器

为了提供可靠的 DNS 服务,几乎所有的域名注册商都要求您为任何域名输入至少两台 DNS 服务器。当然,维护所有区域文件的多个副本是可能的,但是您可以利用 BIND 中的主/从功能来自动化这个过程。

在本节中,我们将使用 CentOS 服务器添加一个 DNS 从服务器。首先,我们将向您展示如何配置主服务器。

Note

我们注意到,有些人反对在描述领导/下属关系时使用“主/从”术语。BIND 仍然使用这个术语,我们选择使用这个术语是为了不让读者感到困惑,而不是出于对这个主题的漠不关心。

人的本质

如果您想要设置主主机以允许从 Ubuntu 上的从主机传输,您需要更改/etc/bind/named.conf.local中的区域定义。您需要确保主服务器在区域更新时联系从服务器,您可以通过添加notify yes指令来实现这一点。这意味着您不需要等到从属服务器到达区域过期时间,因为主服务器上的任何区域更改都会立即复制到从属服务器。

接下来,添加一个allow-transfer指令,它应该包含从属服务器的 IP 地址。我们已经在清单 10-27 中包含了example.com区域的新定义。

acl "transfer-hosts" {
        192.168.0.254 ;
        127.0.0.1 ;
};
zone "example.com" {
    type master;
    file "master/example.com.db";
    notify yes;
    allow-transfer {
      transfer-hosts ;
    };
    key-directory "/etc/bind/keys";
    auto-dnssec maintain;
    inline-signing yes;
};
Listing 10-27.Adding Zone Definitions

我们在图 10-26 中所做的是使用一个名为"transfer-hosts"的变量,它是我们在绑定区域配置文件中用acl选项创建的。然后我们在allow-transfer部分使用它来允许我们在transfer-hosts中定义的 IP 地址。创建acl变量是很好的实践,因为你可以在一个地方快速更新你的配置。这使得阅读和验证您的配置更加容易。如果我们添加更多的从设备,我们可以将它们添加到transfer-hosts acl 中。

当您添加了所有从机的地址后,保存文件,然后告诉 BIND 通过sudo rndc reload重新加载它的配置。

Tip

要测试主设备配置,您可以在从设备上使用 dig 来模拟区域传输。使用AXFR查询类型:dig example.com @127.0.0.1 AXFR

默认情况下,绑定服务器监听本地主机地址(127.0.0.1)。您需要设置listen-on named.conf选项来让绑定服务器监听本地网络接口。对于主文件,您需要将以下内容添加到/etc/bind/named.conf.options文件中。

listen-on port 53 { 127.0.0.1; 192.168.0.1; };

在从机上,您需要将以下内容添加到/etc/named.conf文件中:

listen-on port 53 { 127.0.0.1; 192.168.0.254; };
...
allow-query     { localhost; 192.168.0.0/24; };

下一步是告诉从服务器在哪里可以找到主服务器。在slave服务器上打开/etc/named/named.conf.local文件,为 example.com 域添加一个区域定义。将区域类型设置为从属。为了确保服务器可以检索区域数据,您需要在masters配置块中指定主服务器的地址。我们已经在清单 10-28 中包含了我们网络的配置。

zone "example.com" {
    type slave;
    masters {
       192.168.0.1;
    };
    file "slaves/example.com.db";
};
Listing 10-28.Slave Server Zone Configuration

完成后,保存配置文件,并告诉从属服务器通过 sudo rndc reload 重新加载它。您可以检查/var/log/syslog文件以验证区域正在被传输,或者您可以使用hostdig查询从服务器以确保区域数据存在。

在查看绑定日志(sudo journalctl –xf –u bind9)的同时,您发送一个$ sudo rndc notify example.com命令,告诉从机登记并转移区域。触发notify后,在从属日志(sudo journalctl –xf –u named)上,您会看到类似如下的内容:

Jul 05 09:35:47 au-mel-centos-1 named[4266]: client 192.168.0.1#47692: received notify for zone 'example.com'
Jul 05 09:35:47 au-mel-centos-1 named[4266]: master 192.168.0.1#53 (source 0.0.0.0#0) deleted from unreachable cache
Jul 05 09:35:47 au-mel-centos-1 named[4266]: zone example.com/IN: Transfer started.
Jul 05 09:35:47 au-mel-centos-1 named[4266]: transfer of 'example.com/IN' from 192.168.0.1#53: connected using 192.168.0.254#34645
Jul 05 09:35:47 au-mel-centos-1 named[4266]: zone example.com/IN: transferred serial 2016070107
Jul 05 09:35:47 au-mel-centos-1 named[4266]: transfer of 'example.com/IN' from 192.168.0.1#53: Transfer completed: 1 messages, 82 records, 17410 bytes, 0.001 secs (17410000 bytes/sec)

最后一行显示我们已经转移了 82 条记录。这表明主设备将向从设备传输区域文件。现在,您应该能够挖掘奴隶并解决以下问题:

$ dig @192.168.0.254 mail.example.com
...
;; ANSWER SECTION:
mail.example.com.      86400   IN    A      192.168.0.1
...
;; SERVER: 192.168.0.254#53(192.168.0.254)
...

动态域名系统

如果您的 ISP 在您每次连接到互联网时都为您的主机分配一个新的随机地址,那么运行您自己的权威 DNS 就没有什么意义了。您的服务器地址会不断变化,您需要不断改变 WHOIS 数据库中的授权信息。

另一种解决方案是动态 DNS,它可以从互联网上的各种提供商那里获得。使用此解决方案,动态 DNS 提供商托管 DNS 服务器。一个小型客户端应用程序运行在您的一个系统上,每当您的 IP 地址发生变化时,它就会远程更新 DNS 服务器上的主机记录。这些动态 DNS 服务的 TTL 足够低,不会中断邮件传递等服务。当然,即使您的外部 IP 地址从未更改,您也可以使用这样的服务。

有各种各样的动态 DNS 提供商,可在 www.dmoz.org/Computers/Internet/Protocols/DNS/DNS_Providers/Dynamic_DNS 获得这些提供商的非详尽列表。如果您选择以这种方式外包 DNS 托管,您选择的动态 DNS 提供商将向您提供 DNS 服务器的详细信息,供您在注册服务商的注册表中输入。

如果您需要动态更新,您应该选择一个提供在 Linux 下工作的客户机实用程序的提供商。Ubuntu 中有几个实用程序是打包提供的,比如ddclient:

对于 CentOS,您需要下载其中一个工具的 tarball 并手动安装,或者找到第三方创建的 RPM 包。这里对 Ubuntu 的说明对 CentOS 应该是一样的: www.noip.com/support/knowledgebase/installing-the-linux-dynamic-update-client-on-ubuntu/

当然,许多家用/商用防火墙路由器也支持动态 dns 更新,如果您有这样的路由器,这可能是一个更好的选择。

不要将动态 dns 与本地动态 DNS 更新相混淆,后者在原理上是相似的,但有所不同。我们将很快进行动态 dns 更新。

动态主机配置协议

既然我们已经对主机的命名进行了分类,那么将网络地址自动分配给一些主机可能会更好,比如工作站或笔记本电脑。用于此的服务是动态主机配置协议。它由一个服务器和一个客户端组成,服务器定义哪些地址可以分配给哪些客户端,客户端从服务器请求地址并使用响应来配置本地网络接口。

这对于您可能想要添加到您的网络中的随机机器来说是非常好的,因为您并不真正关心它们被分配了什么地址。然而,对于服务器来说,如果你在服务器上使用 DHCP,你通常会想要静态分配。如果服务器的地址发生意外变化,您可能无法使用它提供的服务。

幸运的是,DHCP 服务器允许您将可用的网络地址划分到不同的地址池中。然后,可以将每个池配置为分配给已知主机或未知主机。这样,就有可能从特定的地址池中为来访的笔记本电脑分配一个特定范围内的随机空闲地址。

安装和配置

DHCP 服务器由 Ubuntu 上的isc-dhcp-server包和 CentOS 上的dhcp包提供。在 Ubuntu 和 CentOS 上,一个示例配置文件被安装为/etc/dhcp/dhcpd.conf。在 CentOS 上,这个文件没有任何配置。它提供了在哪里可以找到示例文件的说明,您可以通过发出:

$ cat /usr/share/doc/dhcp*/dhcpd.conf.example

配置文件包含一组全局指令,后跟一个或多个子网定义。注释以井号(#)为前缀。我们在清单 10-29 中包含了 Ubuntu 文件中的全局指令(去掉了注释)。

ddns-update-style none;
option domain-name "example.org";
option domain-name-servers ns1.example.org, ns2.example.org;
default-lease-time 600;
max-lease-time 7200;
log-facility local7;
Listing 10-29.
dhcpd.conf Global Settings

第一个指令指定我们的 DHCP 服务器不会对它分发的地址进行 DNS 更新。稍后您将看到如何改变这一点。default-lease-time指令指定如果连接客户端没有指定时间,DHCP 租约将有效多长时间。如果指定了时间,该时间不能长于max-lease-time。两种设置都以秒为单位指定时间。最后,log-facility指定了系统日志记录器应该如何处理 DHCP 服务器生成的日志条目。我们将在第十八章告诉你如何配置系统日志。这些选项被传递给 dhcp-client 并用于更新resolv.conf文件。这些当前的选项是示例,不是我们已经设置的。

让我们稍微改变一下这个配置来适应我们自己的需要。因为我们不希望每分钟都有很多机器开关,所以我们也可以增加租赁时间。让我们将默认值设为 6 小时,最大值设为 24 小时。

default-lease-time 21600;
max-lease-time 86400;

完成后,我们可以添加一个子网,我们的 DHCP 服务器应该在其上分发租约。

subnet 192.168.0.0 netmask 255.255.255.0 {
}

DHCP 服务器将在启动时检查分配给本地网络接口的网络地址,并自动将每个子网声明分配给正确的网络接口。

我们现在可以在这个配置块中添加特定于子网的选项。我们将从定义哪个地址用作网络上的默认路由以及哪个主机用作名称服务器的选项开始。

subnet 192.168.0.0 netmask 255.255.255.0 {
    option routers 192.168.0.254;
    option domain-name "example.com";
    option domain-name-servers 192.168.0.1;
    option broadcast-address 192.168.0.255;
}

这里,我们定义了当客户端请求租用时,应该将哪些网络设置发送给客户端。路由器选项指定客户端使用的默认网关。domain-name选项不言自明。在domain-name-servers选项中,我们可以添加一个或多个 DNS 服务器地址,用空格分隔。广播地址是网络上的一个特殊地址,用于向同一网络范围内的所有主机发送请求,我们通过broadcast-address选项指定它。

然而,我们还没有指定任何允许 DHCP 服务器分发的地址。我们通过range指令做到这一点。

subnet 192.168.0.0 netmask 255.255.255.0 {
    option routers 192.168.0.254;
    option domain-name "example.com";
    option domain-name-servers 192.168.0.1;
    option broadcast-address 192.168.0.255;
    option subnet-mask 255.255.255.0;
    range 192.168.0.101 192.168.0.200;
}

这告诉服务器,如果客户端请求租用,它可以分配 192.168.0.101 到 192.168.0.200 之间的任何地址。我们在这里没有指定完整的网络范围,所以我们还有一些地址可以手动分配给服务器或其他主机。

我们现在要做的就是告诉 DHCP 服务器应该监听哪些网络接口。如果我们不这样做,它就不会启动。在 Ubuntu 上,我们可以通过编辑/etc/default/isc-dhcp-server文件并添加我们希望服务器监听 INTERFACES 变量的每个接口来指定这一点。

INTERFACES="enp0s3"

在 CentOS 上,我们不需要设置监听的接口。当 dhcpd 启动时,它将监听任何与子网声明相关联的接口。因此,如果接口 enp0s100 的 ip 地址为 192.168.100.1/24,并且我们在 dhcpd.conf 中声明了子网 192.168.100.0/24,则 enp0s100 接口将自动用于该子网。

我们保存文件,然后使用 Ubuntu 上的sudo system start isc-dhcp-server或 CentOS 上的sudo systemclt start dhcpd启动服务器。

当服务器将租约分配给特定客户端时,它会将客户端 MAC(媒体访问控制)地址和分配的租约记录到文件中。一般来说,当它重新连接时,它会尝试将相同的地址重新分配给客户端,即使已经过了比max-lease-time更长的时间。当然,如果该地址由于被分配给不同的客户机而不可用,服务器将需要发出一个不同的地址。

静态租赁转让

有时,您希望能够将同一个 IP 地址分配给一个主机或设备——例如,一台联网的打印机或一个托管开发网站的工作站。您可以在客户端上手动编辑配置,但这意味着您需要登录到客户端才能更改网络配置。如果 DHCP 将某人分配给另一台主机的相同地址分配给另一台主机,您也可能会遇到 IP 地址冲突。

DHCP 允许您将相同的 IP 地址与主机的 MAC 地址相匹配,从而分配给主机。如果您利用这一点,您可以通过简单地编辑dhcpd.conf,重新启动 DHCP 服务,并等待主机更新其租约,来更改对任何主机的地址分配。

回想一下,您可以通过运行ip link show命令来获取主机的 MAC 地址。您还可以运行arp命令来列出本地网络上的 IP 地址和相关的 MAC 地址,但是这可能很难将地址与正确的机器匹配起来。

这些配置指令都在dhcpd.conf文件的子网块中。你从定义一个组开始,你可以给它起任何你喜欢的名字;这里我们选择了“静态”

subnet ... {
    group "static" {
    }
}

接下来,添加主机定义。在您的组定义中,每个主机都在它自己的块中定义。hardware ethernet选项指定 MAC 地址,该地址将分配有由fixed-address选项指定的地址。

Note

您可以通过ip link show命令找到网络接口的 MAC 地址。

该选项可以包含 IP 地址或可解析的 FQDN。我们将使用 FQDN,因为 DNS 对我们来说很好。这也意味着,如果我们想要更改分配给主机的 IP 地址,但不想更改其主机名,我们只需更新 DNS 区域文件,而不需要更新 DHCP 服务器。

subnet ... {
    group "static" {
        host au-mel-ubuntu-2 {
            hardware ethernet 00:16:3E:15:3C:C2;
            fixed-address au-mel-ubuntu-2.example.com;
        }
    }
}

我们将 use- host-decl-names标志设置为 on。这确保了我们在主机块上设置的名称——在我们的例子中是 au-Mel-Ubuntu-2——将作为它应该使用的主机名发送到 DHCP 客户机。如果我们不这样设置,我们就必须为我们以这种方式定义的每个静态主机添加一个特定的主机名选项。因为我们在组内定义它,所以它不适用于组外的任何配置。

subnet ... {
    group "static" {
        use-host-decl-names on;
        host au-mel-ubuntu-2 {
            hardware ethernet 00:16:3E:15:3C:C2;
            fixed-address au-mel-ubuntu-2.example.com;
        }
    }
}

最后,我们要确保我们用于静态 DHCP 租借的地址永远不会被分配给 DHCP 服务器不知道的客户端。为此,我们可以通过定义地址池来保留 100 个地址中的一部分。我们将首先为 DHCP 服务器知道的主机定义一个池。同样,这些池定义位于子网块内。

subnet ... {
    ...
    pool {
        range 192.168.0.101 192.168.0.150;
        deny unknown clients
    }
}

这将保留 50 个地址供需要静态分配的主机使用。接下来,我们将为所有其他客户端定义一个池。在这个池中,我们还将覆盖租用时间,因为访问机器通常一整天都不需要地址。

subnet ... {
    ...
    pool {
        range 192.168.0.101 192.168.0.150;
        deny unknown clients
    }
    pool {
        range 192.168.0.151 192.168.0.200;
        allow unknown clients;
        default-lease-time 7200;
        max-lease-time 21600;
    }
}

我们已经将原来的 IP 地址范围一分为二。为了确保服务器认为不允许两次分配相同的范围,我们注释掉了文件顶部附近的原始语句。

subnet ... {
    ...
    // range 192.168.0.101 192.168.0.200;
...

我们现在可以重新启动 DHCP 服务器。剩下要做的就是确保 DHCP 请求通过防火墙到达我们的服务器。根据定义,DHCP 客户端还没有分配 IP 地址,因此它不能向特定的网络地址发送数据包。

它会将 UDP 数据包广播到地址为 255.255.255.255 的端口 67,这是 0.0.0.0 网络的广播地址。DHCP 服务器知道如何监听这些数据包,如果收到一个数据包,就会做出响应。因此,我们需要在 DHCP 服务器主机上配置防火墙,以接受发往任意地址的端口 67 的数据包。

$ sudo /sbin/iptables –t filter –A INPUT -p udp --dport 67 -j ACCEPT

我们现在有了一个 DHCP 服务器配置,它将特定的保留 IP 地址分配给已定义的主机,并为其他主机使用不同的地址范围。通过让这些主机使用一组预先确定的 IP 地址,我们还可以通过设置防火墙规则和更改服务器配置来管理它们的访问,以便根据主机连接的地址来授予或拒绝访问权限。

动态 DNS 更新

您可能还想为特定主机分配固定的 DNS 名称,而不管 DHCP 服务器为它们分配了哪个 IP 地址。这允许您通过名称来引用机器,即使它们的地址改变了。这是通过设置由 DNS 和 DHCP 服务器共享的加密密钥来实现的。如果需要,DHCP 服务器将在发布新租约时联系 DNS 服务器,并更新相关的 A 和PTR条目。

配置 DNS

在 Ubuntu 上,我们从生成密钥开始,为此我们将使用ddns-confgen工具。我们将使用- a选项指定 hmac-sha512 算法,使用–k选项为密钥命名,并使用–z选项声明区域。

# ddns-confgen -a hmac-sha512 -k dynamic-update-key -z example.com
# To activate this key, place the following in named.conf, and
# in a separate keyfile on the system or systems from which nsupdate
# will be run:
key "dynamic-update-key" {
        algorithm hmac-sha512;
        secret "kHATLZ8hl4RbSoe7W71pqaGZ3oCIP3WIgZZI/UcXYzbOooCOTS3cN7lFdQ/+97VYVfFYEGmzRCqRKyj4AcLfdg==";
};

# Then, in the "zone" definition statement for "example.com",
# place an "update-policy" statement like this one, adjusted as
# needed for your preferred permissions:
update-policy {
         grant dynamic-update-key zonesub ANY;
};

# After the keyfile has been placed, the following command will
# execute nsupdate using this key:
nsupdate -k <keyfile>

我们看到了下一步需要做什么的有用输出。如果不想看到这个输出,可以使用–q选项。

我们将把密钥“动态更新密钥”添加到我们的/etc/bind/named.conf.options文件中。

key "dynamic-update-key" {
     algorithm hmac-sha512;
     secret "kHATLZ8hl4RbSoe7W71pqaGZ3oCIP3WIgZZI/UcXYzbOooCOTS3cN7lFdQ/+97VYVfFYEGmzRCqRKyj4AcLfdg==";
};

接下来,我们需要将更新策略添加到example.com区域文件的区域定义中。

zone "example.com" {
    type master;
    file "master/example.com.db";
    notify yes;
    allow-transfer {
      transfer-hosts ;
    };
    update-policy {
      grant dynamic-update-key zonesub ANY;
    };
    key-directory "/etc/bind/keys";
    auto-dnssec maintain;
    inline-signing yes;
};

然后,我们通过sudo rndc reload重新加载名称服务器配置。

Tip

您还可以在主 DNS 服务器和从 DNS 服务器之间使用基于密钥的身份验证。

配置 DHCP

下一步是告诉 DHCP 服务器这个密钥,并对它进行配置,以便它在分发新租约时向 named 发送 DNS 更新请求。我们首先将ddns-update-style变量从 none 改为 interim。我们还希望这些动态 DNS 条目的 TTL 相当低,这样在主机从网络上消失后它们就不会保持活动状态。我们会指定一个小时。

ddns-update-style interim;
ddns-ttl 3600;

在那下面,仍然在全局配置部分,我们添加了键定义。使用与名称服务器上完全相同的密钥名称很重要,否则更新将不起作用。

key "dynamic-update-key" {
  algorithm hmac-sha512;
  secret "kHATLZ8hl4RbSoe7W71pqaGZ3oCIP3WIgZZI/UcXYzbOooCOTS3cN7lFdQ/+97VYVfFYEGmzRCqRKyj4AcLfdg==";
}

你会注意到我们去掉了“;”来自。最后,我们需要告诉 DHCP 服务器我们想要在正向和反向区域上执行动态更新。我们需要在配置文件的全局部分中为每个区域添加一个区域定义。我们还需要指定哪个密钥应该用于更新,以及更新需要发送到哪个 DNS 服务器,如清单 10-30 所示。

zone 0.168.192.in-addr.arpa. {
    key dynamic-update-key;
    primary 192.168.0.1;
}
zone example.com. {
    key dynamic-update-key;
    primary 192.168.0.1;
}
Listing 10-30.Adding Zone Update Definitions in dhcpd.conf

我们也重新启动 DHCP 服务器,完成后,服务器端的配置就完成了。现在剩下要做的就是告诉 DHCP 客户机在请求租用时向服务器发送主机名字符串。该主机名字符串将用于创建 DNS 条目的 FQDN。

为了设置它,我们在客户机上编辑文件/etc/dhclient.conf并添加send host-name选项。在我们希望命名为 au-mel-centos-2.example.com 的主机上,我们添加以下内容:

send host-name "au-mel-centos-2";

Note

/etc/dhclient.conf中的配置适用于所有网络接口。相反,您可以使用/etc/dhclient-enp0s3.conf文件将其仅应用于第一个接口。

然后我们可以运行dhclient实用程序来更新我们的地址租约,如清单 10-31 所示。如果我们将主机配置为使用 DHCP,这也会在启动时自动运行。如果是这种情况,并且您想要手动更新租约,那么您应该首先终止正在运行的dhclient进程。

$ sudo dhclient enp0s3
Internet Systems Consortium DHCP Client V3.0.5-RedHat
Copyright 2004-2006 Internet Systems Consortium. All rights reserved.
For info, please visit http://www.isc.org/sw/dhcp/

Listening on        LPF/eth1/00:0c:29:7b:b1:77
Sending on        LPF/eth1/00:0c:29:7b:b1:77
Sending on        Socket/fallback
DHCPREQUEST on enp0s3 to 255.255.255.255 port 67
DHCPACK from 192.168.0.1
bound to 192.168.0.200 -- renewal in 7181 seconds.

Listing 10-31.Obtaining a Lease with dhclient

我们可以检查服务器上的系统日志,看看发生了什么。我们在清单 10-32 中包含了一个片段。在 CentOS 上,这个日志文件是/var/log/messages,在 Ubuntu 上是/var/log/syslog。我们将在第十八章中向你展示如何将特定的日志信息重定向到不同的文件。

Jun 11 11:23:15 au-mel-ubuntu-1 dhcpd: DHCPDISCOVER from 00:0c:29:7b:b1:77 via enp0s3
Jun 11 11:23:16 au-mel-ubuntu-1 dhcpd: DHCPOFFER on 192.168.0.200 to
    00:0c:29:7b:b1:77 (au-mel-centos-2) via enp0s3
Jun 11 11:23:16 au-mel-ubuntu-1 named[5187]: client 192.168.0.1#46749: updating
    zone 'example.com/IN': adding an RR at 'au-mel-centos-2.example.com' A
Jun 11 11:23:16 au-mel-ubuntu-1 named[5187]: client 192.168.0.1#46749: updating
    zone 'example.com/IN': adding an RR at 'au-mel-centos-2.example.com' TXT
Jun 11 11:23:16 au-mel-ubuntu-1 named[5187]: journal file example.com.db.jnl does
    not exist, creating it
Jun 11 11:23:16 au-mel-ubuntu-1 dhcpd: Added new forward map from
    au-mel-centos-2.example.com to 192.168.0.200
Jun 11 11:23:16 au-mel-ubuntu-1 named[5187]: zone example.com/IN:sending
    notifies (serial 2009020102)
Jun 11 11:23:16 au-mel-ubuntu-1 named[5187]: client 192.168.0.1#58073: updating zone
    '0.168.192.in-addr.arpa/IN': deleting rrset at '200.0.168.192.in-addr.arpa' PTR
Jun 11 11:23:16 au-mel-ubuntu-1 named[5187]: client 192.168.0.1#58073: updating zone
    '0.168.192.in-addr.arpa/IN': adding an RR at '200.0.168.192.in-addr.arpa' PTR
Jun 11 11:23:16 au-mel-ubuntu-1 named[5187]: journal file 192.168.0.db.jnl does
    not exist, creating it
Jun 11 11:23:16 au-mel-ubuntu-1 dhcpd: added reverse map from
    200.0.168.192.in-addr.arpa. to au-mel-centos-2.example.com
Jun 11 11:23:16 au-mel-ubuntu-1 dhcpd: DHCPREQUEST for 192.168.0.200 (192.168.0.1)
    from 00:0c:29:7b:b1:77 (au-mel-centos-2) via enp0s3
Jun 11 11:23:16 au-mel-ubuntu-1 dhcpd: DHCPACK on 192.168.0.200 to
    00:0c:29:7b:b1:77 (au-mel-centos-2) via enp0s3
Listing 10-32.The DHCP Server Log

您可以看到服务器收到了来自 MAC 地址为 00:0c:29:7b:b1:77 的主机的 DHCP 请求。然后,它向该主机提供了地址 192.168.0.200,并被告知该主机的名称是 au-mel-centos-2。接下来,您可以看到名称服务器为 au-mel-centos-2.example.com FQDN 添加了一个 A 和一个TXT记录。TXT 条目包含用于跟踪 DNS 条目是否由 DHCP 服务器创建的校验和。如果它不存在,服务器将不会改变或删除相关的 A 条目。

然后,对区域的更改将被写入与之前创建的区域文件相关联的日志文件中。实际的区域文件本身不会被修改。更新转发区域后,它会向为该区域配置的所有从属服务器发送通知。如果我们有,这将触发从服务器从主服务器传输更新区域文件。

接下来,对反向区域重复相同的过程。完成后,DHCP 服务器允许客户端获得它提供的租约,并更新其内部租约文件。通过使用host命令执行查找,我们可以快速地再次检查这些新的 DNS 条目是否有效。

$ host 192.168.0.200
200.0.168.192.in-addr.arpa domain name pointer au-mel-centos-2.example.com.
$ host au-mel-centos-2.example.com.
au-mel-centos-2.example.com has address 192.168.0.200

两种查找都有效,所以我们现在可以配置网络上的任何其他主机,我们还希望在 DNS 服务器中动态更新这些主机。

手动更改 DNS 条目

因为这些动态更新使用日志文件,所以如果您想要手动更改任何 DNS 条目,则需要执行额外的步骤。如果您只是更改区域文件,这些更改将被忽略,因为日志文件中的数据将取代它。

您可以在开始编辑之前发出 sudo rndc freeze命令,告诉 DNS 服务器您想要锁定区域日志文件,并拒绝编辑区域时的任何动态更改。当您编辑完区域文件后,您可以通过sudo rndc unfreeze解锁区域,再次允许动态更新。

The Internet Superserver

并非所有网络服务都需要一直运行。对于他们中的一些人来说,有一种方法来监听特定端口号上的流量并仅在需要时启动服务将是非常方便的。一旦客户端断开连接,服务可以再次关闭。并非所有服务都支持这一点,但如果它们支持,这是节省系统资源的好方法。

这种“主管”功能是由互联网超级服务器提供的。该软件最新、功能最丰富的版本由xinetd包提供。

xinetd管理的每个服务在/etc/xinetd.d目录中都有自己的配置文件片段,默认情况下它会为一些非常基本的服务安装配置文件,比如 echo,它只是重复你发送给它的任何字符。但是,这些服务直到您编辑它们的配置文件时才会启用。

我们将在第十九章中向您展示如何向xinetd添加服务。

安全 Shell

到目前为止,您实际上只使用 SSH 从工作站连接到服务器,以便进行配置更改或添加新软件。我们现在将向您展示如何充分利用 SSH。我们将建立基于密钥的认证,使用ssh在主机之间复制文件,并利用隧道通过防火墙访问远程服务。

当您通过 SSH 连接到主机时,系统会要求您输入密码。如果您需要一天输入一次,这没问题,但是如果您经常连接到远程主机,这可能会变得很耗时,尤其是如果您有一个安全的长密码。

SSH 允许您使用基于密钥的认证。为了利用这一点,您创建公钥和私钥,然后将公钥复制到您想要连接的远程服务器。当您连接时,远程主机将验证您是否拥有属于该主机上的公钥组件的私钥。如果是这样,您就通过了身份验证。

Note

公钥和私钥用于验证您的身份。连接加密由 SSH 主机密钥提供,这些密钥是在安装服务时生成的。

创建和分发密钥

我们将从使用ssh -keygen 实用程序创建一个公钥/私钥对开始。我们可以定义密钥类型(支持两种加密算法)和以位为单位的密钥大小,以及要使用的输出文件名。对于后者,我们将使用默认值,对于前者,我们将使用-t选项指定 RSA 算法,并使用-b选项指定 4096 位密钥,如清单 10-33 所示。

$ ssh-keygen -t rsa -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/home/jsmith/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/jsmith/.ssh/id_rsa.
Your public key has been saved in /home/jsmith/.ssh/id_rsa.pub.
The key fingerprint is:
c9:62:dd:da:cd:71:33:78:3d:08:93:3e:8c:25:b0:f3 jsmith@au-ubuntu-1.example.com
Listing 10-33.Generating a New SSH Key Pair

为您的私钥添加一个密码短语非常重要,因为没有密码短语,任何获得您的私钥的人都可以使用它登录(不需要密码)任何包含您的公钥的主机。

现在我们有了一个密钥对,我们可以将公共部分复制到远程主机。我们需要将公钥存储在主目录中的.ssh目录下的一个名为authorized_keys的文件中,以便能够使用它登录。我们既可以手动将密钥添加到该文件中,也可以使用ssh-copy-id实用程序来完成这项工作,如清单 10-34 所示。

$ ssh-copy-id au-mel-centos-1.example.com
The authenticity of host 'au-mel-centos-1.example.com (192.168.0.1)' can't be
    established.
RSA key fingerprint is 67:e3:50:bf:8c:2c:a0:d5:0c:e9:fc:26:3f:9f:ea:0e. Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'au-mel-centos-1.example.com,192.168.0.1' (RSA) to the
    list of known hosts.
jsmith@au-mel-centos-1.example.com's password:
Listing 10-34.Copying a Public SSH Key to a Remote Host

现在,我们将尝试使用以下信息登录机器:

$ ssh au-mel-centos-1.example.com

因为我们还没有从我们登录的主机连接到 au-mel-centos-1,所以系统会提示我们接受远程 SSH 主机密钥。唯一标识该密钥的指纹被打印出来,因此您可以直观地验证它是否与远程主机上的密钥相匹配。

Note

要获取主机密钥指纹,您可以使用ssh-keygen工具。在这种情况下,使用ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key.pub获取主机 RSA 密钥的指纹。

您会注意到 SSH 假设我们在远程主机上的用户名与我们在本地登录的用户名相同。如果不是这样,我们可以将密钥复制到username@remotehost中。

接下来,我们被提示输入登录密码,因为我们的密钥还没有在远程主机上的正确文件中列出。一旦我们通过了身份验证,ssh-copy-id会将公钥附加到正确的文件中,并要求我们对其进行测试。我们通过登录远程主机来做到这一点,如清单 10-35 所示。

$ ssh au-mel-centos-1.example.com
Enter passphrase for key '/home/jsmith/.ssh/id_rsa':
Last login: Tue Feb 10 15:14:42 2009 from au-mel-ubuntu-1.example.com
[jsmith@au-mel-centos-1 ∼]$
Listing 10-35.Logging In Using an SSH Key

这一次,我们没有被要求输入 au-mel-centos-1 上的登录密码,这正是我们想要的。我们现在可以检查 au-mel-centos-1 上的.ssh/authorized_keys文件,以确保我们没有添加额外的、意外的键。

当然,在我们提供服务器时,公钥也可以安装到用户的主目录中。它们也可以由配置管理服务提供,如 Puppet、Ansible、Chef 或 SaltStack。我们将在第十九章的后面讨论配置管理服务。

使用 SSH 代理

然而,我们仍然需要输入我们在私有 SSH 密钥上设置的密码。如果每次想要连接到远程主机时都必须这样做,这就违背了设置基于密钥的身份验证的目的。进入 SSH 代理,这是一个小的守护进程,它将未锁定的私有 SSH 密钥保存在内存中。一旦我们启动它,我们就可以解锁一个或多个私钥,并将它们添加到代理中。然后,SSH 可以使用代理来提供私钥,并向远程主机验证我们的身份。

告诉 SSH 关于代理的方法是设置两个环境变量,SSH_AUTH_SOCKSSH_AGENT_PID。如果这些都设置好了,ssh就可以和代理人交流了。代理在启动时输出 shell 代码来设置这些变量,如清单 10-36 所示。

$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-SZGGF11534/agent.11534; export SSH_AUTH_SOCK; SSH_AGENT_PID=11535; export SSH_AGENT_PID;
echo Agent pid 11535;
Listing 10-36.Starting ssh-agent

如果我们将这些行粘贴到 shell 中,变量将被设置。

$ SSH_AUTH_SOCK=/tmp/ssh-SZGGF11534/agent.11534; export SSH_AUTH_SOCK;
$ SSH_AGENT_PID=11535; export SSH_AGENT_PID;
$ echo Agent pid 11535;
Agent pid 11535

不得不复制和粘贴这些行有点麻烦,所以我们可以使用 eval shell 函数来使事情变得简单一些。这个函数执行传递给它的任何参数,就像它们是命令一样。首先,我们将通过ssh-agent -k停止代理,然后我们将重新启动它并一次性设置环境变量。参数周围的反引号导致它作为命令被 shell 执行。该命令产生的输出由eval解释。

$ ssh-agent -k
unset SSH_AUTH_SOCK;
unset SSH_AGENT_PID;
echo Agent pid 11535 killed;
$ eval `ssh-agent`
Agent pid 11541

我们现在需要做的就是解锁私钥并将其添加到代理中。

$ ssh-add
Enter passphrase for /home/jsmith/.ssh/id_rsa:
Identity added: /home/jsmith/.ssh/id_rsa (/home/jsmith/.ssh/id_rsa)

我们能够连接到任何包含匹配公钥的主机,而无需输入密码。

$ ssh jsmith@au-mel-centos-1
Last login: Tue Feb 10 15:17:19 2009 from au-mel-ubuntu-1.example.com
[jsmith@au-mel-centos-1 ∼]$

Tip

只需在 shell 中将SSH_AUTH_SOCKSSH_AGENT_PID变量设置为正确的值,就可以告诉同一主机上的多个 shell 您正在使用代理。

调整 SSH 配置

当您的所有 SSH 服务器监听同一个端口,并且您对所有主机使用一个密钥对时,默认的服务器配置将非常适合您。如果不是这样(例如,端口 22 的流量可能受到防火墙的保护,或者每个主机的远程用户名不同),您可能需要对服务器或客户机的配置进行一些调整。

基本服务器配置

SSH 的服务器端从/etc/ssh/sshd_config文件中读取它的配置。默认情况下,它监听所有可用网络接口的端口 22。您可以通过更改配置文件中的端口和ListenAddress选项来改变这一点。

Port选项接受一个参数,即您希望服务器监听的端口号。要让服务器监听多个端口,您可以添加额外的Port指令,每个端口号一个。

这也适用于ListenAddress指令。只要没有这样的指令,服务器就会监听所有接口。当您添加一个时,它将只在指定的地址上开始监听所有已定义的端口。您可以通过添加额外的ListenAddress指令让它监听多个地址。

例如,要让 bastion 主机上的 SSH 服务器只监听内部网络接口上的端口 22 和 2022,我们可以将这些指令添加到配置文件中:

Port 22
Port 2022
ListenAddress 192.168.0.1
ListenAddress 19.168.1.1

我们现在可以告诉服务器通过 CentOS 上的sudo systemctl reload sshd或 Ubuntu 上的sudo systemctl reload ssh重新加载它的配置文件。这不会影响当前的连接,因此您可以远程运行该命令。

Caution

确保不要将 SSH 服务器重新配置到无法再访问的地步!如果您担心,请不要在配置更改后注销。首先尝试创建一个新连接,以确保它仍然工作。

我们将讨论的另一个基本服务器选项旨在让您在远程主机上使用 GUI 应用程序时更加轻松。当X11Forwarding选项设置为 on 并且在连接到这样的主机时将-X参数传递给 SSH 客户端时,您可以运行任何图形应用程序,它们的窗口将显示在您的本地桌面上。这个特性利用了 X Window 系统的客户机/服务器模式,通过到本地主机上的 X 服务器的 SSH 连接,将任何连接尝试转发到远程主机上的 X 服务器。

要强制所有用户使用基于密钥的认证,可以在服务器配置文件中添加PasswordAuthentication no。这将防止每个人都能够使用密码登录。请注意,如果您丢失了私钥,您将无法再登录到设置了此选项的主机。

您可以在手册页上找到所有可用服务器配置选项的完整列表。

客户端配置

SSH 客户端可以针对主机上的所有用户进行全局配置,也可以针对每个用户进行本地配置。全局配置文件是/etc/ssh/ssh_config,每个用户的文件是。ssh/config在用户的主目录中。

最基本的客户端配置指令允许您定义连接到给定主机或所有主机时使用的用户名和端口号。这些配置块中的每一个都以一个Host指令开始,如果该部分只适用于单个主机,则后面跟着一个主机名或简称,如果它适用于所有主机,则后面跟着一个星号。

例如,我们可以通过向我们的。ssh/config文件。

Host gateway
    Hostname au-mel-centos-1
    Port 2022
    User ataylor

每次我们使用ssh gateway命令时都会用到这个配置。它告诉客户端连接到端口号为 2022 的 au-mel-centos-1 主机,并以用户ataylor的身份登录。通过在客户机配置文件中添加这些选项,我们不需要在命令行中一直指定端口号和登录名。

类似地,我们可以告诉客户机在连接到远程主机时使用不同的私有密钥文件,方法是使用IdentityFile指令添加它。我们将通过ssh-keygen -t rsa -s 2048 -f .ssh/gateway-ataylor为网关主机上的ataylor生成一个密钥对。一旦完成,我们可以告诉客户端使用这个密钥连接到 bastion 主机。

Host gateway
    Hostname au-mel-centos-1
    Port 2022
    User ataylor
    IdentityFile ∼/.ssh/gateway-ataylor

我们将讨论的最后一个选项旨在让您在远程主机上工作时更加轻松。首先,ForwardAgent yes 选项允许您告诉服务器应该使用发起主机上的 SSH 代理进行身份验证。这允许您通过 SSH 从一台主机跳到另一台主机,而不需要输入密码来在每台主机上启动 SSH 代理。

因此,您不必为了启用 X 转发而一直向 ssh 添加-X 参数,您也可以在配置文件中基于每个主机启用它。对于您希望远程运行 GUI 应用程序的每台主机,添加一个ForwardX11 yes 指令来自动启用该选项。

最后,您可能有一个跳转主机,或堡垒主机,您必须首先 ssh 到它,然后才能访问它之外的网络。从安全的角度来看,管理一个代理 SSH 连接到专用网络的跳转主机要比暴露该专用网络中的每台主机更容易。我们可以在 SSH 配置中使用 poxy 命令来配置一个跳转主机。

Host jumphost
  Hostname jumphost.foo.com

Host private.foo.com
  Hostname 10.0.1.1
  ForwardAgent yes
  ProxyCommand ssh -qxT jumphost nc %h %p

为了连接到private.foo.com,我们首先设置一个到 jumphost 的代理连接。它的工作方式是使用%p(默认为 22)设置一个 netcat 连接(nc)到主机名 10.0.1.1(%h)。我们使用–q不显示烦人的连接信息,-x禁用 X11 转发,–T禁用 jumphost 上的伪终端(我们不需要代理)。

在较新版本的 OpenSSH (7.3 和更高版本)中,您将使用 ProxyJump 配置选项来处理这个问题。人们使用 ProxyCommand ssh -W %h:%p jumphost 代替 nc 命令也是很常见的。

现在,我们可以发出一个ssh private.foo.com命令,ssh 将连接到 jumphost,然后直接建立到private.foo.com的连接。

开挖隧道

您还可以使用 SSH 来访问远程主机和网络上受保护的服务,而无需先设置 VPN(虚拟专用网络)。如果两个站点共享相同的专用网络范围,VPN 将无法工作,因为 VPN 两端的地址范围将是相同的。在这种情况下,您可以使用 SSH 将连接从本地主机转发到远程地址,反之亦然。这种转发充当单端口隧道。

您可以通过命令行参数分别执行此操作,也可以在中为每个主机定义转发。ssh/config文件。例如,您可以创建一个 SSH 隧道,将本地主机上端口 8080 的连接转发到远程网络上机器上的端口 80。这样,您就可以通过浏览本地网络上的地址来访问远程网站。通过将-L选项传递给 SSH 客户机,指定一个可选的本地地址,后跟一个强制的本地端口作为隧道的起点,然后指定一个远程主机和一个远程端口作为隧道的终点,所有这些都用冒号分隔,从而创建一个本地转发。

$ ssh -L 8080:192.168.1.12:80 ataylor@192.168.1.1

该命令以用户ataylor的身份将我们连接到主机 192.168.1.1,并建立一个隧道,允许我们通过在 web 浏览器中访问http://localhost:8080来浏览主机 192.168.1.12 上的网站。该连接将通过我们的 SSH 连接转发,主机 192.168.1.12 上的 web 服务器将看到来自地址 192.168.1.1 的传入连接。

Note

由于托管方式的原因,通过隧道访问它们可能不适用于所有网站。我们将在第十二章中讨论这种基于名称的虚拟主机。

相反,您可以通过使用-R选项创建远程转发,为远程主机上的用户提供对本地网络上的服务的访问。该选项采用与-L选项相同的参数,但是指定一个可选的远程地址和强制端口号来监听远程主机,后面是隧道端点的本地地址和端口号。

为了允许远程用户连接到本地网络上通常不可访问的 SSH 服务器,我们可以在端口 2022 上创建一个远程隧道,将连接转发到本地网络上主机的端口 22。

$ ssh -R 192.168.1.1:2022:192.168.0.15:22 ataylor@192.168.1.1

在我们以用户ataylor的身份登录到主机 192.168.1.1 之后,我们可以 SSH 到本地主机上的端口 2022,这样我们就可以登录到主机 192.168.0.15 上的 SSH。

出于安全原因,隧道的起点只会绑定到环回网络接口,因此网络中不同主机上的用户无法使用隧道。我们可以通过向 SSH 服务器配置文件添加GatewayPorts指令来改变这种行为。此选项仅适用于转发的起始点,因此对于本地隧道,我们将其添加到本地主机上,对于远程转发,我们将其添加到远程主机上。

为了允许我们指定其他主机上的用户是否应该能够使用转发,我们将GatewayPorts选项设置为clientspecified。如果我们不为转发起点指定 IP 地址,则只有本地用户可以访问它,而如果我们指定了可访问的地址,则与隧道起点位于同一网络的任何用户都可以访问它。

因为这需要大量的输入,所以在 SSH 客户机配置文件中定义常用的隧道更容易。我们通过LocalForwardRemoteForward指令来实现这一点。每个都有两个参数,转发起始地址和端口,用冒号分隔,端点地址和端口也用冒号分隔。

我们可以将之前使用的转发添加到客户端配置文件中:

Host gateway
    Hostname 192.168.1.1
    Port 22
    User ataylor
    IdentityFile ∼/.ssh/gateway-ataylor
    LocalForward 8080 192.168.1.12:80
    RemoteForward 192.168.1.1:2022 192.168.0.15:22

最后,ForwardAgent yes 选项让 SSH 配置远程 shell,使用本地主机上的 SSH 代理进行任何身份验证。如果您的公钥在所有远程主机上都可用,这允许您从一个主机跳到另一个主机,而无需重新输入密码或在每个中间主机上启动一个新的ssh-agent实例。这是一个非常有用的选项,所以您也可以通过将它添加到/etc/ssh/ssh_config文件的全局部分来为所有用户启用它。

执行快速安全的文件传输

SSH 协议不仅仅允许远程登录。您还可以使用它在主机之间安全地传输文件。一种方法是使用scp命令,它的工作方式和cp一样,只是源文件或目标文件可以以远程用户名和主机名为前缀,如清单 10-37 所示。

$ scp data.txt jsmith@au-mel-centos-1:/tmp
data.txt                                        100% 3072KB    3.0MB/s    00:00
Listing 10-37.Using scp to Transfer a File to a Remote Host

因为我们之前已经向 au-mel-centos-1 发送了我们的公共 SSH 密钥,scp能够使用 SSH 代理进行身份验证,并且我们没有被要求输入密码。我们可以登录 au-mel-centos-1 主机,看到文件data.txt现在位于/ tmp目录中。

我们还可以通过将远程文件路径指定为第一个参数,将本地文件或目录指定为第二个参数,从远程主机复制回本地主机。

$ scp jsmith@au-mel-centos-1:/tmp/data.txt /tmp
data.txt                                      100% 3072KB    3.0MB/s    00:00

我们甚至可以将文件或目录从一台远程主机复制到另一台远程主机,而无需登录到其中任何一台。例如,在 au-mel-ubuntu-1 上,我们可以运行以下命令:

$ scp jsmith@au-mel-centos-1:/tmp/data.txt ataylor@au-mel-centos-2:/tmp
data.txt                                      100% 3072KB    3.0MB/s    00:01

SSH 也提供了 FTP 的替代品。如果您希望能够交互地移动文件或目录,您可以使用sftp命令,如清单 10-38 所示。同样,这个命令将使用 SSH 代理(如果存在的话)。

$ sftp jsmith@au-mel-centos-1
Connecting to au-mel-centos-1...
jsmith@au-mel-centos-1's password:
sftp> cd /tmp
sftp> ls
data.txt        ssh-IWYooo5675 sftp>
get data.txt
Fetching /tmp/data.txt to data.txt
/tmp/data.txt                                100% 3072KB    3.0MB/s    00:00
sftp> quit
Listing 10-38.Using sftp

Tip

结合 SSH 端口转发,您还可以轻松地将文件复制到无法直接访问的主机上。注意,scp使用-P选项来指定端口号,而ssh使用-p

摘要

在本章中,您学习了基本的基础设施服务,如 NTP、DNS 和 DHCP。我们还向您展示了如何连接到远程主机,以简化系统管理和维护。现在,您应该能够执行以下操作:

  • 在所有主机上设置并保持正确的时间。
  • 为所有主机创建正向和反向 DNS 记录,并将这些记录复制到多个 DNS 服务器。
  • 设置 DHCP 以自动分配地址,并将其链接到 DNS 以自动更新相关的 DNS 记录。
  • 使用 ssh、scp 和 sftp 轻松安全地在远程主机上工作和传输文件。

在下一章,我们将向你介绍邮件服务,并教你如何运行自己的邮件服务器。

十一、Web 和 SQL 服务

您要做的最常见的事情之一是设置 web 服务。web 服务很可能需要一个结构化的英语查询语言(SEQUEL)数据库。在这一章中,我们将探索安全 web 服务的主要组成部分。

在本章中,您将学习如何安全地设置 Apache web 服务器和 MariaDB 数据库服务器。为了保护与新 web 服务器的通信,我们将使用 Let's Encrypt 来创建安全套接字层(SSL)/传输层安全性(TLS)证书。这也将为我们提供保护其他服务的证书。然后我们将展示如何安装一个内容管理系统和一个 webmail 应用程序。最后,我们将向您展示如何通过 web 代理保护您的员工,让他们的 web 浏览体验变得更快、更安全。

Apache Web 服务器

Apache 是当今使用最广泛的开源软件之一。虽然它的受欢迎程度在过去几年中有所下降,但它仍然被用于托管超过 30%的现有网站 1 ,并且通常因其成熟度、稳定性和应用程序的性质而被选中。它被设计成模块化的,因此可以通过启用或禁用模块来添加或删除额外的功能。几乎所有的 Linux 发行版都有软件包,所以您可以通过软件包管理系统在您的主机上安装它。

Apache 服务器可以作为单个站点的单个 web 服务器运行,或者更常见的是,可以作为虚拟主机服务于数百个站点。也就是说,许多网站共享底层资源,如单个 web 服务器的中央处理器(CPU)、磁盘资源和互联网协议(IP)地址。

Apache 目前有三个多处理模块(MPM)供您选择使用。它们是预工作、工作人员和事件。

  • prefork 模块是由单独的进程处理连接的地方。它适用于非线程安全的 web 应用程序。在 prefork 模块中,控制进程创建一个监听连接的子进程。
  • worker 模块混合了基于进程和基于线程的处理。它适用于线程安全应用程序。在 worker 模型中,父进程创建一个启动几个线程的子进程;为每个传入连接分配一个线程。一种是监听传入的连接,并将它们传递给等待的服务器线程。
  • 事件模块较新,基于工作模块。它的线程模型类似于 worker,但是针对处理 keepalive 连接进行了优化。它用几个线程来处理 keepalivess,keepalive 将活动连接交给等待的线程。

Apache web 服务器的另一个流行替代品是 Nginx。Nginx 是一种快速的模块化 web 服务器,通常与 Apache 服务器一起用作缓存服务器,或者单独用作 web 服务器。我们将在第十九章讨论 Ansible 时展示如何安装 Nginx 服务器。

安装和配置

CentOS 和 Ubuntu 都安装 Apache 版,但是包的命名不同。在 CentOS 上运行sudo yum install httpd,在 Ubuntu 上运行sudo aptitude install apache2。添加这些包将导致安装一些额外的库。

此外,CentOS 和 Ubuntu 都允许您通过加载您想要使用的特定模块来选择 MPM。我们将很快展示如何做到这一点。除了加载特定的 MPM,您还可以加载模块来加载运行不同 web 服务器应用程序所需的软件,比如 PHP 或 Django。我们很快也会向您展示这一点。

Note

如果您运行一个高容量、高流量的网站,您可能会考虑换一个不同的 Apache 引擎或者完全不同的 web 服务器,比如 Nginx。Nginx 不同于 Apache,因为它是真正的非阻塞、事件驱动的大规模 web 服务器。关于这些 web 服务器的差异和功能,请参见 https://www.digitalocean.com/community/tutorials/apache-vs-nginx-practical-considerations

您还可以在 http://en.wikipedia.org/wiki/Comparison_of_web_servers 找到 web 服务器列表。

我们将从展示如何为 Apache 进行基本配置开始。稍后,我们将展示如何添加一些模块来扩展功能。

CentOS

在这一节中,我们将展示如何配置我们的 Apache web 服务器。Apache web 服务器将其配置文件存储在/etc/httpd中,主配置文件是/etc/httpd/conf/httpd.conf。我们将首先编辑/etc/httpd/conf/httpd.conf文件,然后创建一个虚拟主机文件。

CentOS 的默认 MPM 模块是 prefork。您可以通过编辑/etc/httpd/conf.modules.d/00-mpm.conf来更改模块,并对适当的模块进行注释和取消注释。我们将使用 worker 模块,因为它提供了优于 prefork 模块的性能配置文件。

#LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
LoadModule mpm_worker_module modules/mod_mpm_worker.so

httpd.conf文件超过 350 行,所以我们不会显示每个选项。与许多其他 Linux 配置文件一样,#表示注释,选项以<name> <directive>的形式出现。让我们从基础开始:服务器名(ServerName)和网站管理员的电子邮件地址(ServerAdmin)。

ServerName au-mel-centos-1.example.com
ServerAdmin webmaster@example.com

Apache 在显示默认错误页面时会使用服务器名称和网站管理员电子邮件地址。我们已经为自己的主机输入了值,au-mel-centos-1.example.comwebmaster@example.com。默认情况下,Apache 将在所有可用的网络地址上监听和服务请求。如果你想选择一个特定的地址,你可以使用Listen指令来改变它。您也可以配置想要监听的端口。要更改默认值,您可以指定如下内容:

Listen 192.168.0.1:8080

前一行指定只监听特定的 IPv4 地址和端口 8080。要小心,因为当 Apache 试图启动时,具有相同 IP 和端口的多个Listen指令会给出一个错误((98)Address already in use)。在我们的例子中,我们将设置Listen监听端口 80 上的所有地址。

Listen 80

通过设置UserGroup字段,可以更改 web 服务器运行的用户和组。

User apache
Group apache

如果您更改了UserGroup字段,重要的是不要将它们更改为系统中其他地方使用的用户和组,例如数据库用户。Apache 服务是由 root 用户启动的,因此它可以绑定到端口 80(一个特权端口),并按照这里定义的用户启动子进程。这个用户应该拥有尽可能少的特权,并且应该能够阅读它所提供的内容。

最后,我们将重点介绍这些配置项目:

Include conf.modules.d/*.conf
IncludeOptional conf.d/*.conf

第一个加载模块,比如前面描述的 MPM。模块提供不同的软件功能;有授权模块,像 PHP 这样的 web 语言模块,以及其他种类的特性。IncludeOptional指令可以用来存储配置文件,比如虚拟主机或者其他配置文件,比如php.conf。如果这些文件是有编号的,像00-php.conf,那么它们将按数字顺序加载。

创建虚拟主机

我们现在准备创建我们的第一个虚拟主机。一台运行 Apache 的 Linux 主机可以服务数百或数千个网站,它们都有自己的主机名。我们称之为虚拟主机,因为这些网站都运行在一个单独的网络服务器上。有两种虚拟主机:基于 IP 和基于名称。Apache 可以同时提供这两者。

基于 IP 的虚拟主机使 Apache 根据接收请求的 IP 地址,从特定的目录提供 web 页面。对于每个基于 IP 的虚拟主机,Linux 主机需要有一个分配给网络接口的 IP 地址。正如我们在第七章中所讨论的,这是通过向接口添加额外的地址来实现的。现在服务器名称指示(SNI; www.ietf.org/rfc/rfc4366.txt )可用,这是使用 SSL/TLS 证书时向 web 服务器提供服务器名称的一种方式。因为一些老的浏览器不支持 SNI,你仍然可以看到基于 IP 的虚拟主机,这样做是有意义的。

基于名称的虚拟主机使 Apache 根据远程用户连接的站点名称为特定目录提供网页。任意数量的基于名称的虚拟主机可以共享一个 IP 地址。站点的名称由一个特殊的头决定,这个头在请求中被发送到 web 服务器。

我们将创建一个命名的虚拟主机。为此,我们需要为我们的站点创建一个配置文件。我们在 Apache conf.d目录下/etc/httpd/conf.d/ www.example.com.conf 中创建这个文件。让我们从如何描述虚拟主机开始。

<VirtualHost *:80>
...
</VirtualHost>

Apache 有一个特殊的语法来描述虚拟主机。每个虚拟主机的所有配置都需要包含在<VirtualHost *:80></VirtualHost>指令中。opening 指令声明虚拟主机的开始,在这种情况下,指定您应该监听该主机端口 80 上的所有网络接口(*:80)。然后,我们用关闭指令</VirutalHost>关闭虚拟主机配置。

对于基本虚拟主机,我们可以使用以下配置:

<VirtualHost *:80>

  ServerName www.example.com
  ServerAdmin webmaster@example.com
  DocumentRoot  /var/www/html/www.example.com

</VirtualHost>

ServerName告诉 Apache 服务器应该将对 www.example.com 网站的请求发送到这个虚拟主机。DocumentRoot目录设置是 Apache 为网站提供文件的地方。您可以覆盖此虚拟主机的网站管理员电子邮件地址。

我们可以在文档根目录下创建一个index.html文件,并启动 Apache 服务器。

$ sudo mkdir –p /var/www/html/www.example.com
$ sudo bash –c "echo www.example.com > /var/www/html/www.example.com/index.html"

现在我们只需要重启我们的 Apache 服务器。不过,在此之前,我们应该验证我们的 Apache 配置。我们通过以下方式做到这一点:

$ sudo apachectl configtest
Syntax OK

这里我们使用apachectl命令来管理我们的 Apache 服务。apachectl程序可以用来启动、停止、重启、正常重启和检查配置,就像我们在这里所做的一样。更多信息参见man apachectl页面。要启动 Apache 服务,还可以在 CentOS 上使用systemctl命令。让我们继续做那件事。

$ systemctl start httpd.service

您将在/var/log/httpd目录中看到 Apache 服务的日志。您可以使用以下命令跟踪日志:

$ sudo tail –f /var/log/httpd/access_log /var/log/httpd/error_log

现在我们可以检查我们的主机是否如我们预期的那样响应。为此,我们将使用curl命令。curl命令将允许我们在 shell 中向 web 服务器发出请求。

$ curl –H 'Host: www.example.com' http://localhost
www.example.com

这表明我们的服务器已经正确地响应了我们的虚拟主机。使用curl命令,我们向监听本地主机的 web 服务器发出请求。我们还发送了一个Host头(-H 'Host:),其中包含了我们希望向其发送请求的 web 服务器的名称。Apache 将读取这个头并将这个请求传递给相应的虚拟主机。如果它找不到具有相同ServerName字段的虚拟主机,它会将请求路由到“基本”虚拟主机,或者换句话说,它加载的第一个虚拟主机。因此,最好在配置文件中添加一个数字作为后缀,如00- www.example.com.conf ,它将决定虚拟主机的加载顺序。

在我们向外界公开之前,我们应该确保我们的防火墙允许超文本传输协议(HTTP)流量到达我们的主机。首先,让我们看看当前防火墙允许的服务列表。

$ sudo firewall-cmd --list-services
dhcpv6-client ssh

我们需要添加http服务。我们用firewall-cmd再做一次。

$ sudo firewall-cmd --add-service http --permanent
$ sudo firewall-cmd --reload

默认情况下,这将服务添加到公共区域。现在我们应该能够从远程客户端访问我们的站点了。

在 Ubuntu 上,基本配置被分成多个文件。Ubuntu 上 Apache 加载的主文件是/etc/apache2/apache2.conf。我们已经在清单 11-1 中包含了这个文件中的基本配置指令。虽然该文件包含常见的 Apache 指令,但它引用了其他文件来通过 include 语句配置虚拟主机、模块、端口和 IP 地址,就像我们在 CentOS httpd.conf文件中看到的那样。

ServerRoot "/etc/apache2"
Mutex file:${APACHE_LOCK_DIR} default
PidFile ${APACHE_PID_FILE}

Timeout 300

KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5

User ${APACHE_RUN_USER}
Group ${APACHE_RUN_GROUP}

AccessFileName .htaccess
<FilesMatch "^\.ht">
        Require all denied
</FilesMatch>

HostnameLookups Off
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf
Include ports.conf

# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf

# Include the virtual host configurations:
IncludeOptional sites-enabled/*.conf

Listing 11-1.Defaults in Ubuntu’s apache2.conf File

Ubuntu 上的默认配置与 CentOS 上的基本相同。然而,Ubuntu 和 Debian 在管理虚拟主机或站点配置的方式上有所不同。最明显的是,您会看到在配置中声明了几个环境变量。这些解析的值可以在/etc/apache2/envvars文件中找到。

同样值得注意的是AccessFileName指令。它指定可能包含服务器配置指令的文件的名称。这个文件被命名为.htaccess,任何 web 目录都可能包含这样一个文件。服务器将检查文件是否存在,并在尝试向连接的客户端提供文件之前处理它包含的任何指令。

因为.htaccess文件可能包含敏感信息,我们应该限制对该文件的访问。以下指令实现了这一点:

<FilesMatch "^\.ht">
        Require all denied
</FilesMatch>

这是一个匹配指令的例子,我们可以匹配任何以.ht ( <FilesMatch "^\.ht">)开头的文件,并拒绝所有访问这些文件的请求(Require all denied)。

Note

不建议使用文件,因为使用它们会对性能产生负面影响。

日志指令描述了我们将如何记录我们的 Apache 信息。您可以通过使用LogFormat指令来决定在 Apache 日志中记录什么。

LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined

Apache 解释字符串中的每个%<value>,并允许您以对自己有用的方式格式化日志输出。在这个LogFormat指令中,我们让 Apache 包含接受请求的虚拟主机的虚拟主机(%v)和端口(%p)。接下来我们有远程主机名(%h)和远程用户名(%l)、认证用户(%u)。记录请求(%r)以及最终状态(%>s)和字节大小(%O)。最后,我们记录 referrer ( %{Referer}i)和 user agent ( %{User-Agent}i),它们利用了 Apache VARNAMEs——可用于记录格式的变量名。这些是从浏览器发送的标头中派生出来的。然后我们将这种日志格式命名为vhost_combined。现在,我们可以在配置中使用这种格式,方法是像这样声明CustomLog:

<VirtualHost *:80>
...
  CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined
...
</VirtualHost>

你可以在 https://httpd.apache.org/docs/2.4/mod/mod_log_config.html#formats 找到更多关于LogFormat选项的信息。在 http://httpd.apache.org/docs/current/expr.html 可以看到 VARNAMEs 中可以使用的变量。

在 CentOS httpd.conf文件中,我们有Listen指令;在 Ubuntu 上,Apache 从ports.conf文件中加载关于应该监听哪些 IP 地址和端口的信息;该文件的默认值如下:

Listen 80

<IfModule ssl_module>
    Listen 443
</IfModule>

<IfModule mod_gnutls.c>
    Listen 443
</IfModule>

在这个文件中,你将看到如何防御性地为模块添加指令。类似超文本标记语言(HTML)的语法<IfModule ssl_module>…</IfModule>表示只有当ssl_module被加载时Listen 443指令才应该被加载。这可以防止 Apache 在模块尚未启用时出错。

Ubuntu 与 CentOS 的不同之处还在于它包含了模块和配置文件,比如虚拟主机。你会看到有IncludeOptional <resource>-enabled/*.conf之类的指令。Ubuntu(当然还有 Debian)利用符号链接来启用模块或配置选项。

管理模块

我们来看看/etc/apache2/mod-available目录。我们已经进入了/etc/apache2目录,并将执行一个mod-available目录的列表。在该目录中,您将找到当前可以在 Apache 启动时加载的模块。事实上,这就是我们选择想要为 Ubuntu 加载的 MPM 的方式。

$ ll mods-available/mpm*
-rw-r--r-- 1 root root 668 Mar 19 09:48 mods-available/mpm_event.conf
-rw-r--r-- 1 root root 106 Mar 19 09:48 mods-available/mpm_event.load
-rw-r--r-- 1 root root 571 Mar 19 09:48 mods-available/mpm_prefork.conf
-rw-r--r-- 1 root root 108 Mar 19 09:48 mods-available/mpm_prefork.load
-rw-r--r-- 1 root root 836 Mar 19 09:48 mods-available/mpm_worker.conf
-rw-r--r-- 1 root root 107 Mar 19 09:48 mods-available/mpm_worker.load

在前面的清单中,您可以看到我们已经讨论过的三种不同的 Apache MPM 引擎。如果我们现在查看/etc/apache2/mod-enabled目录,我们可以看到 Apache 启动时加载了这些模块中的哪一个。

$ ls -l mods-enabled/mpm*
lrwxrwxrwx 1 root root 32 Aug  5 12:18 mods-enabled/mpm_event.conf -> ../mods-available/mpm_event.conf
lrwxrwxrwx 1 root root 32 Aug  5 12:18 mods-enabled/mpm_event.load -> ../mods-available/mpm_event.load

现在你可以看到默认情况下 Ubuntu 将运行 MPM 事件。有一个.conf文件和一个加载文件,一个文件用于配置指令,另一个用于实际加载模块。虽然您可以自己添加符号链接来启用模块,但首选方法是使用 Ubuntu 为此提供的命令。这些命令是用于启用模块(创建符号链接)的a2enmod和用于禁用模块(删除符号链接)的a2dismod

为了展示如何使用这些命令,我们将启用和禁用status模块,该模块允许我们查看 web 服务器的当前状态。首先,为了启用它,我们将发出以下命令:

$ sudo a2enmod status
Enabling module status.
To activate the new configuration, you need to run:
  service apache2 restart

这可以在任何目录下执行,并且需要重启apache2服务。该命令创建了以下符号链接:

$ ls -l mods-enabled/status*
lrwxrwxrwx 1 root root 29 Aug  6 05:08 mods-enabled/status.conf -> ../mods-available/status.conf
lrwxrwxrwx 1 root root 29 Aug  6 05:08 mods-enabled/status.load -> ../mods-available/status.load

现在,让我们使用以下命令禁用该模块:

$ sudo a2dismod status
Module status disabled.
To activate the new configuration, you need to run:
  service apache2 restart

Ubuntu 没有检查目录中的符号链接,而是提供了另一个命令来检查当前的 Apache 设置。a2query命令可用于查找模块的状态,如下所示:

$ a2query -m status
No module matches status (disabled by site administrator)

因为我们刚刚禁用了该模块,所以状态模块被列为禁用。如果模块不存在,您只会看到“没有模块匹配 fakemodule”。a2query也可用于查询其他配置选项,如表 11-1 所述。

表 11-1。

a2query Options

| `-q` | 安静输出,用于脚本 | | `-m ` | 列出已启用的模块,如果没有指定模块,则列出所有模块 | | `-M` | 列出当前的 MPM | | `-s ` | 检查站点是否已启用,如果没有指定站点,则检查全部 | | `-c ` | 列出已启用的配置文件,如果没有指定配置,则列出全部 | | `-d` | 返回`Apache2`模块目录 |
在 Ubuntu 上管理网站

我们将向 Ubuntu web 服务器添加一个虚拟主机,就像我们对 CentOS 主机所做的那样。传统上,Debian(和 Ubuntu)系统管理员倾向于将服务相关的文件放在/srv目录中。在这个例子中,我们不打算遵循这个习惯用法,但是没有理由不能将 web 服务器文件放在/srv/www/var/local目录中或者文件系统上任何其他有意义的地方。为了定义虚拟主机,我们将把清单 11-2 中的指令添加到/etc/apache2/sites-available/ www.example.com 中。

<VirtualHost *>
    ServerName www.example.com
    ServerAlias example.com
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html/www.example.com
    <Directory /var/www/html/www.example.com>
        Options Indexes FollowSymLinks Includes IncludesNOEXEC SymLinksIfOwnerMatch
        AllowOverride None
    </Directory>
</VirtualHost>
Listing 11-2.Our New Virtual Host Definition

这类似于我们的 CentOS 虚拟主机声明。在这个例子中,我们添加了一些额外的指令。我们首先打开VirtualHost标签,并指定该定义将应用于所有地址。接下来,我们指定希望该虚拟主机回复的名称。只有一个ServerName可以应用于任何给定的虚拟主机,但是其他的可以通过ServerAlias添加。您可以通过使用更多的ServerAlias指令或者通过将额外的主机名添加到由空格分隔的单个ServerAlias来添加额外的别名。

正如我们已经说过的,DocumentRoot指令指定了这个虚拟主机将提供文件的目录。一旦定义了这个,就可以使用Directory指令来指定这个目录及其包含的所有文件和目录的选项。您指定的列表与 CentOS 机器上的列表相同。Options指令列出了在任何特定目录中启用的特性。在表 11-2 中,我们对此处包含的内容进行了解释。通过将AllowOverride设置为None,您拒绝服务器使用来自.htaccess文件的设置来修改这些选项。您可以通过将AllowOverride设置为All来实现这一点,或者使用类似Options=Indexes,MultiViews的东西来更详细地描述什么是允许的。最后,关闭DirectoryVirtualHost指令并保存文件。

表 11-2。

Options

| [计]选项 | 功能 | | --- | --- | | `All` | 除`MultiViews`外的所有选项。 | | `ExecCGI` | 脚本作为应用程序执行,其输出被发送到浏览器。 | | `FollowSymLinks` | 服务器可以跟随符号链接并提供它所指向的文件或目录。 | | `Includes` | 服务器将处理可以嵌入页面的服务器端包含指令。 | | `IncludesNOEXEC` | 服务器端 include 指令可能不会在服务器上执行脚本。 | | `Indexes` | 如果目录中不存在索引页,则在列表中直接显示目录。 | | `SymLinksIfOwnerMatch` | 只有当链接的所有者与链接目标的所有者相同时,服务器才能跟踪符号链接。 | | `MultiViews` | 使用`mod_negotiation`模块,您可以根据客户端的能力指定如何选择内容。 |

我们可以通过使用实用程序a2ensite来启用虚拟主机。像a2enmod一样,这将创建从/etc/apache2/sites-available/etc/apache2/sites-enabled的符号链接。

$ sudo a2ensite www.example.com
Enabling site www.example.com.
To activate the new configuration, you need to run:
  service apache2 reload

您现在可以按照脚本建议的那样做,或者使用sudo apache2ctl graceful手动重新加载服务器配置。您还可以使用a2query来检查站点是否启用,如下所示:

$ sudo a2query -s www.example.com
www.example.com (enabled by site administrator)

我们还可以通过发出以下命令来确保防火墙允许 HTTP 流量通过我们的主机:

$ sudo ufw allow http

这允许 web 流量通过我们的 web 服务。

httpd 性能

有几个旋钮和转盘可以针对 Apache 性能进行调整。一个 Apache web 服务器,在不改变默认设置的情况下,可以处理数百个并发用户。我们将在这里讨论其中的一些。性能是一个相对的主题,您应该使用从您的 web 服务和主机收集的指标来指导您完成这个过程。第十七章将处理指标收集。

大多数文档会告诉你,随机存取存储器(RAM)是首先要考虑的。对于您的 web 应用程序和系统进程,您将需要足够的内存。写密集型应用程序将需要速度适当的磁盘。为此,从系统指标收集的数据将成为您的指南。

以下设置决定了 Apache 如何管理连接超时和保持活动状态:

Timeout 300

KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5

Timeout是连接超时设置,以秒为单位。这可以根据您的要求加长或缩短。KeepAlive设置决定了 Apache 如何处理长时间运行的会话。您可以指定 Apache 服务器应该使用相同的 TCP 连接,而不是为会话创建新的 TCP 连接。您可以使用MaxKeepAliveRequestsKeepAliveTimeout微调这些设置。

不同的 MPM 也有办法调整它们的性能。我们将在这里查看工人和事件 MPM。

对于工作人员和事件绩效,有两个主要的绩效选项可以调整。第一个是ThreadsPerChild,第二个是MaxRequestWorkers. ThreadsPerChild描述每个子进程创建了多少个服务器线程。MaxRequestWorkers确定总共启动的最大线程数。许多 web 浏览器与 web 服务器建立多个连接,因此它们可以同时下载样式表和图像。因此,来自用户的单个 web 连接可以启动多个线程。这意味着每个用户连接都会增加系统资源的数量,例如 RAM 和 CPU 时间。如果您遇到性能不佳的情况,应该调整、监控和优化这些设置。

Apache httpd 文档为一些合理的性能设置提供了一些指导。但是,以下是员工 MPM 的默认设置:

ServerLimit          16
StartServers          3
MaxRequestWorkers   400
MinSpareThreads      75
MaxSpareThreads     250
ThreadsPerChild      25

至于设置,ServerLimit定义了活动线程数量的硬限制。MaxRequestWorkers设置为 400,这是总线程数。该值来自于ServerLimit值乘以ThreadsPerChild值,后者被设置为 25。StartServers是启动的线程的初始数量。MinSpareThreadsMaxSpareThreads定义最小和最大空闲线程。Apache 将根据这些设置来减少或增加可用的空闲线程。

对于事件 MPM,除了对工人 MPM 进行调优之外,还有另外一个性能考虑因素。对于事件 MPM,由于没有线程来处理新请求,这种情况可能会导致线程饥饿。AsyncRequestFactor可以通过限制并发连接数来对空闲线程进行微调。

建议您在测量和调优 Apache 服务器时,将mod_status模块与任何可用的指标收集工具结合使用。

存取限制

在配置部分,我们讨论了使用Require指令来限制对目录的访问。在本节中,我们将向您展示更多关于该指令以及如何使用基本用户名和密码来保护您的站点的信息。

Require指令可以用来限制对目录(和位置)路径的访问。您已经看到它已经被用来保护对.htacess文件的访问。

<FilesMatch "^\.ht">
        Require all denied
</FilesMatch>

这里我们拒绝了对任何以.ht开头的文件的访问。该指令来自mod_authz_core模块。还有其他人扩展了这个模块,比如mod_authz_host,它允许我们基于 IP 授权访问。

举个例子,假设我们有一个名为/var/www/html/ www.example.com/uploads 的目录。我们可以限制只能从内部 IP 地址访问。

<Directory /var/www/html/www.example.com/uploads>
    Options -Indexes -FollowSymLinks
    AllowOverride None
    Require ip 192.168.0
</Directory>

模块mod_authz_host为我们提供了根据 IP 地址限制访问部分站点的功能。在前面几行中,只有远程 IP 地址位于 192.168.0.0/24 子网中的主机才能访问此目录。在表 11-3 中,你可以看到Require指令可用的其他选项。

表 11-3。

Require Options

| `Require all granted` | 无条件允许访问。 | | `Require all denied` | 无条件拒绝访问。 | | `Require env ` | 如果设置了环境变量,则授予访问权限。 | | `Require method ` | 对某些 HTTP 方法授予访问权限。 | | `Require expr ` | 如果表达式为真,则授予访问权限。 | | `Require user ` | 仅向这些用户授予访问权限(`mod_authz_user`)。 | | `Require group ` | 将访问权授予指定组中的用户(`mod_authz_groupfile`)。 | | `Require valid-user` | 将访问权授予授权用户。 | | `Require ip ` | 授予 IP 地址范围内的客户端访问权限。 |

限制访问资源的另一种方法是要求用户输入用户名和密码。许多 web 应用程序在内部进行管理,但是您也可以让 Apache 管理用户名和密码的列表,从而允许您保护特定的目录,而不需要额外的软件。身份验证源可以是文件、数据库或轻量级目录访问协议(LDAP)。我们将向您展示如何执行由文件支持的身份验证。

首先,我们需要创建一个文件,其中包含我们希望通过htpasswd实用程序使用的用户名和密码。我们不想把这个文件放在 Apache 服务器服务的目录中,所以我们创建了一个名为/opt/apache/ www.example.com 的目录,并把我们的密码文件放在那里。通常,我们传递要使用的文件名和要创建的用户作为参数,但是如果文件尚不存在,我们还需要传递-c选项。

$ sudo htpasswd -c /opt/apache/www.example.com/passwords jsmith
New password:
Re-type new password:
Adding password for user jsmith

我们现在可以添加额外的用户,而不需要-c选项。如果我们不想被要求输入密码,我们也可以通过-b选项在命令行上设置。

$ sudo htpasswd -b /opt/apache/www.example.com/passwords ataylor s3kr@t
Adding password for user ataylor

接下来,我们需要告诉 Apache 请求认证。如果您选择在/var/www/ www.example.com 目录中的.htaccess文件中这样做,您将需要添加覆盖AuthConfig选项的能力。我们需要设置AllowOverride AuthConfig,这样才能正常工作。我们不打算添加.htaccess指令;我们将在虚拟主机配置中添加目录级别的身份认证。

    <Directory /var/www/html/www.example.com/uploads>
        Options -Indexes -FollowSymLinks
        AllowOverride None
        AuthType Basic
        AuthName "Restricted Uploads"
        AuthBasicProvider file
        AuthUserFile "/opt/apache/www.example.com/passwords"
        Require valid-user
</Directory>

首先,我们通过AuthType指令指定想要使用的认证类型,在我们的例子中是basic。然后我们需要告诉 Apache 哪个模块将使用AuthBasicProvider,也就是file,提供基本的认证。接下来,我们需要通过AuthUserFile指令告诉 Apache 哪个文件保存了我们的认证信息。

为了帮助用户确定他们试图访问什么,我们可以通过AuthName指令为受保护的资源指定一个名称。当用户被要求提供凭证时,该名称将会显示给用户,如图 11-1 所示,因此让该名称更具描述性会有所帮助。

A185439_2_En_11_Fig1_HTML.jpg

图 11-1。

Apache authentication

最后,需要告诉 Apache,只有在用户成功通过身份验证的情况下,才能授予访问权限。我们通过指定Require valid-user来做到这一点。

如果我们现在浏览到 www.example.com/uploads ,浏览器会要求我们输入用户名和密码,如图 11-1 所示。

如果我们不提供有效的用户名和密码,我们将不会被授予访问权限,但是如果我们输入有效的凭证,Apache 将允许我们查看该站点。应该注意的是,我们通过未加密的 HTTP 会话传递凭证,这是一种危险的做法。我们将很快向您展示如何设置一个 HTTP 安全(HTTPS)服务器。

您可以在 Apache 文档站点上阅读更多关于基于主机和基于用户的访问控制的内容。

模块

我们已经讨论了模块,以及它们如何为 Apache 提供额外的功能,并使用LoadModule指令来启用。该指令指定应该加载的模块文件的路径。

在 CentOS 上,额外的模块通常由模块包安装的/etc/httpd/conf.modules.d中的配置片段启用。当服务器重新启动时,它会选取这些新文件并处理它们的指令。您可以通过在文件名前添加一个数字来对这些模块的加载方式进行排序,这是 CentOS 上的默认设置。为了防止包含这样的代码片段,从而禁用该模块,您可以重命名它,使其文件名不再以.conf结尾,或者从目录中移动或删除该文件。您也可以注释(#)文件中的任何指令。

在 Ubuntu 上,模块包将这些代码片段添加到/etc/apache2/mods-available目录中,然后在/etc/apache2/modules-enabled目录中创建指向它们的链接。这些链接也可以使用a2enmoda2dismod命令手动管理,类似于a2ensitea2dissite

http://httpd.apache.org/docs/2.4/mod/ 可以获得关于所有包含的 Apache 模块及其提供的功能的信息。

安装 PHP 支持

许多 web 应用程序都是用 PHP 编写的,PHP 是一种由拉斯马斯·勒德尔夫开发的脚本语言,它为世界各地的许多网站提供支持,包括流行的 WordPress 内容管理系统(CMS)。当您的 web 浏览器从这样的应用程序请求页面时,web 服务器处理页面中的代码,并向您的浏览器显示输出。为了能够托管这些 web 应用程序,web 服务器需要能够理解和执行 PHP 代码。

执行 PHP 有两种方法。您可以通过安装一个模块并使用 prefork Apache MPM 来为 Apache 添加 PHP 支持。然而,我们将使用 worker MPM,由于 PHP 不是线程安全的,我们将使用 FastGGI 进程管理器将 Apache 链接到 PHP。

Note

你可以在 www.php.net/ 阅读所有关于 PHP 的内容。

PHP 本身是模块化的,所以你可以通过安装额外的包来增加 PHP 的功能。我们将很快建立一个 MariaDB 服务器,所以要让 web 应用程序使用它,我们需要在 PHP 中添加 MariaDB 支持。此外,我们将安装对常用的 GD 图形库(用于处理和操作图像文件)、mbstring 字符串转换库(提供对多字节字符串编码的支持)和 IMAP 邮件协议(提供处理 Internet Message Access Protocol[IMAP]和 Post Office Protocol POP3功能以处理邮件的能力)的支持。后者将允许我们安装和使用基于 PHP 的 webmail 应用程序。

在 CentOS 上,我们可以通过sudo yum install php-fpm php-mysql php-gd php-imap php-mbstring安装所有这些,在 Ubuntu 上通过sudo aptitude install php-fpm php-mysql php-mbstring php-gd php-imap安装。

在本章的后面,我们将建立一个使用 PHP 的网站。但是在这里,我们将向您展示如何在我们的 CentOS 主机上的虚拟主机中使用 PHP-FPM。

首先,我们需要确保加载了以下模块。如您所知,模块是通过/etc/httpd/conf.modules.d/目录加载的。我们正在寻找两个代理文件。

$ grep -E '(proxy.so|fcgi)' /etc/httpd/conf.modules.d/00-proxy.conf
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so

这些模块分别由 CentOS 和 Ubuntu 的httpd包和apache2包提供。

接下来我们将移走我们的/etc/httpd/conf.d/php.conf文件,因为这是php模块的配置文件。

$ sudo mv /etc/httpd/conf.d/php.conf /etc/httpd/conf.d/php.conf_bak

PHP-FPM 是一个守护进程,它将翻译来自 HTTP 服务器的请求,并用来自 PHP 代码的适当响应进行响应。php-fpm.conf文件配置这个守护进程。PHP-FPM 可以将应用程序分成多个池。您在/etc/php-fpm.d目录中创建池。我们现在要编辑/etc/php-fpm.d/ www.conf 文件并添加下面一行:

; Start a new pool named 'www'.
[www]

; The address on which to accept FastCGI requests.
; Valid syntaxes are:
;   'ip.add.re.ss:port'    - to listen on a TCP socket to a specific address on
;                            a specific port;
;   'port'                 - to listen on a TCP socket to all addresses on a
;                            specific port;
;   '/path/to/unix/socket' - to listen on a unix socket.
; Note: This value is mandatory.
listen = 127.0.0.1:9000
listen = /run/php-fcgi.sock

编辑完该文件后,您需要重启php-fpm。你可以用$ sudo systemctl restart php-fpm在 CentOS 上做到这一点。您现在应该注意到在/run目录(这是一个特殊的内存目录)中创建了一个套接字文件。

$ ll /run/php-fcgi.sock
srw-rw-rw-. 1 root root 0 Aug 16 09:49 /run/php-fcgi.sock

一旦完成,我们就可以运行 PHP 网站了。剩下要做的就是在我们的 HTTP 配置中添加一个ProxyPassMatch指令来匹配任何.php文件,并将它们传递给 PHP-FPM 守护进程。

<VirtualHost *:80>

  ServerName www.example.com
  ServerAdmin webmaster@example.com
  DocumentRoot  /var/www/html/www.example.com
  DirectoryIndex index.php
  ProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/run/php-fcgi.sock|fcgi://127.0.0.1:9000/var/www/html/www.example.com/
  <Directory /var/www/html/www.example.com>
     Require all granted
  </Directory>
</VirtualHost>

虚拟主机现在已经准备好处理我们的 PHP 应用程序,我们将在本章的后面安装它。ProxyPassMatch允许我们使用正则表达式来匹配统一资源定位符(URL ),并将请求传递给代理后端。我们提供代理监听的位置,并给它我们将安装 PHP 代码的目录的位置。

你可以在这里阅读更多关于mod_proxy的指令,包括ProxyPassMatch指令:

文件和目录权限

当您使用网站时,您需要对安装网站的文档根目录具有写权限。同时,Apache 用户需要不能写入相同的目录,因为这可能允许匿名 web 用户在发现网站中的漏洞时向您的系统写入文件。

如果多个用户将管理网站,为此创建一个组是个好主意。只要该组对文档根目录具有写权限,您添加到该组的任何用户都将能够写文件和创建目录。很多时候,web 应用程序是由部署用户自动部署的。

Tip

使用一个特定的系统组来管理网站也意味着你可以允许这个组的成员通过sudo使用apachectlapache2ctl命令,而不需要给他们完全的根访问权限。

为了确保一个用户创建的文件可以被同一组中的另一个用户修改,您需要设置umask选项,以便任何新创建的文件和目录都可以被该组写入。您还需要设置setgid位,以便新的文件和目录将从拥有父目录的组继承所有权,而不是碰巧创建了有问题的文件或目录的用户的主要组。稍后,当我们安装一些 web 应用程序时,我们将向您展示一个这样的示例。

Tip

您可以在 http://httpd.apache.org/ 找到更多信息和 Apache 文档。

SQL 数据库

因为许多基于 web 的应用程序使用 SQL server 来存储数据,所以我们还将向您展示如何安装 SQL server。我们将向您展示如何安装 MySQL 数据库服务器的一个名为 MariaDB 的分支。数据库服务器提供数据存储和检索,而客户端可以是使用数据库服务器的任何应用程序,可以是命令行实用程序、LibreOffice 或网站使用的库。

因为 MariaDB 是 MySQL 的一个分支,所以它是 MySQL 数据库的直接替代。在 MariaDB 中,MySQL 的许多命令都是相同的,配置和环境变量也是相同的,因此很容易在这些数据库之间进行交换。MariaDB 之所以产生,是因为 MySQL 被甲骨文公司收购后,MySQL 的前开发者希望在 GNU 通用公共许可证(GNU GPL)许可下保持免费。

MariaDB 仍然将 MySQL 中的代码或移植代码合并到其代码库中,尽管它们不相同,但 MariaDB 的版本可以映射到 MySQL 的版本。直到 MariaDB 的 5.5 版本,MariaDB 一直保持着与 MySQL 相同的版本号。但是最近它把发布号改成了 10.x,这可能会让事情变得更加混乱。下表 11-4 说明了这些版本。

表 11-4。

MariaDB to MySQL Versions

| MariaDB 5.5 | MariaDB 5.3 和 MySQL 5.5 | | MariaDB 10.0 | MariaDB 5.5 和后端口 MySQL 5.6 | | MariaDB 10.1 | 包含 MySQL 5.6 和 5.7 的端口 | | MariaDB 10.2 | Alpha 版本 |

Note

对于这些版本号变化背后的原因的解释,见 https://mariadb.org/explanation-on-mariadb-10-0/

装置

在 CentOS 和 Ubuntu 上,您可以使用软件包管理器轻松安装 MariaDB。在 CentOS 上,MariaDB 通过mariadb-server包安装。

$ sudo yum install mariadb-server

在 Ubuntu 上,服务器组件由mariadb-server-core-10.0包提供(编写时)。你可以通过虚拟包安装它。

$ sudo aptitude install mariadb-server.

初始配置

您需要进行一些基本的配置更改。默认情况下,在 CentOS 上,MariaDB 服务器监听所有已配置的网络接口和地址上的连接。因为我们将 web 服务器和数据库放在同一台主机上,所以我们将限制数据库只监听环回地址。这更安全,但是在理想情况下,我们应该将数据库服务器和 web 服务器放在不同的主机上。对于 Ubuntu,如果您检查/etc/mysql/mariadb.conf.d/50-server.cnf配置文件,您会看到下面的 bind-address 指令已经存在。

在 CentOS 上,我们在文本编辑器中打开/etc/my.cnf,并在[mysqld]部分下添加以下行:

bind-address = 127.0.0.1

这里我们指示数据库服务器只监听环回地址,这样可以防止其他主机访问我们的数据库。当我们希望其他主机访问我们的数据库时,我们需要更改它,在适当的接口上监听。我们的新配置文件将类似于清单 11-3 。

[mysqld]
bind-address = 127.0.0.1

datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
# Disabling symbolic-links is recommended to prevent assorted security risks
symbolic-links=0

[mysqld_safe]
log-error=/var/log/mariadb/mariadb.log
pid-file=/var/run/mariadb/mariadb.pid

!includedir /etc/my.cnf.d

Listing 11-3.Our /etc/my.conf on CentOS

可以为 MariaDB 服务器配置许多不同的设置。您可以在这里找到一些有用的文档:

我们现在可以通过systemctl命令启动 MariaDB 服务器,如清单 11-4 所示。

$ sudo systemctl start mariadb
Listing 11-4.MariaDB First Run on CentOS

服务器现在正在运行,因此我们可以设置一个 root 密码并清理默认表。我们可以在 CentOS 和 Ubuntu 上这样做。有一个名为mysql_secure_installation的实用程序将为我们做这件事。

$ sudo mysql_secure_installation

NOTE: RUNNING ALL PARTS OF THIS SCRIPT IS RECOMMENDED FOR ALL MariaDB
      SERVERS IN PRODUCTION USE!  PLEASE READ EACH STEP CAREFULLY!

In order to log into MariaDB to secure it, we'll need the current
password for the root user.  If you've just installed MariaDB, and
you haven't set the root password yet, the password will be blank,
so you should just press enter here.

Enter current password for root (enter for none):
OK, successfully used password, moving on...

Setting the root password ensures that nobody can log into the MariaDB
root user without the proper authorisation.

Set root password? [Y/n] y
New password:
Re-enter new password:
Password updated successfully!
Reloading privilege tables..
 ... Success!

to log into MariaDB without having to have a user account created for
them.  This is intended only for testing, and to make the installation
go a bit smoother.  You should remove them before moving into a
production environment.

Remove anonymous users? [Y/n] y
 ... Success!

Normally, root should only be allowed to connect from 'localhost'.  This
ensures that someone cannot guess at the root password from the network.

Disallow root login remotely? [Y/n] y
 ... Success!

access.  This is also intended only for testing, and should be removed
before moving into a production environment.

Remove test database and access to it? [Y/n] y
 - Dropping test database...
 ... Success!
 - Removing privileges on test database...
 ... Success!

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.

Reload privilege tables now? [Y/n] y
 ... Success!

Cleaning up...

All done!  If you've completed all of the above steps, your MariaDB
installation should now be secure.

Thanks for using MariaDB!

我们的服务器现在是安全的。它不接受来自远程主机的连接,也不允许没有 MariaDB 帐户的用户进行连接。为了确保它在引导时启动,我们将使用systemctl命令在引导时启用mariadb

$ sudo systemctl enable mariadb

让我们现在测试我们的服务器,以确保我们可以连接到它。

测试服务器

为了检查 MariaDB 服务器是否正在运行,我们可以通过命令行客户端连接到它。我们需要指定-u选项来指定要连接的用户(图 11-2 )。-p选项将提示我们输入相关密码。

A185439_2_En_11_Fig2_HTML.jpg

图 11-2。

Signing in to MariaDB

我们能够连接并运行查询;MariaDB 服务器工作正常。请注意,根据安装的 MariaDB 服务器版本,主机上返回的版本字符串可能会有所不同。在 Ubuntu 上,你可以通过sudo命令访问mysql命令行。

$ sudo mysql

MariaDB 存储引擎

MariaDB 有几种不同的存储引擎,旨在更好地满足不同数据集的要求。默认引擎称为 XtraDB,是一个符合原子性、一致性、隔离性、持久性(ACID)属性和多版本并发控制(MVCC)的数据库;对于大多数应用程序类型来说,这通常是一个不错的选择。

还有其他几个可用的。表 11-5 中列出了您可能感兴趣的主要产品。

表 11-5。

MariaDB Storage Engines

| 档案馆 | 数据存档 | | --- | --- | | 咏叹调 | 增强的 MyISAM 数据库 | | 卡桑德拉 | 访问 Cassandra 集群中数据的 NoSQL 存储引擎 | | 连接 | 允许像访问数据库表一样访问文本文件 | | ScaleDB | 商业大规模高可用性(HA)/耐用数据库存储引擎 | | 蜘蛛;状似蜘蛛的物体;星形轮;十字叉;连接柄;十字头 | 允许通过分片无共享架构访问分布式数据库 | | 德 DB(德 db) | 高性能写入密集型数据库 | | XtraDB | MySQL InnoDB 的直接替代 |

通过发出图 11-3 中的命令,可以看到已经安装的引擎列表。

A185439_2_En_11_Fig3_HTML.jpg

图 11-3。

Showing database engines

在图 11-3 中,您可以看到默认安装的数据库引擎。你可以看到 XtraDB 仍然被称为 InnoDB 引擎,你可以看到每一个都支持什么。

XtraDB 的基本调谐

所有的性能调优通常涉及这三个方面:硬件、操作系统和数据库配置。硬件相当容易;对于数据库,一般规则是更快的磁盘、更快的 CPU 和更多更快的内存。操作系统有许多性能调整,包括磁盘挂载选项、sysctl设置、内核调度程序等等。通过数据库调优,我们有许多配置选项和数据库优化策略。我们将专注于 MariaDB 的 XtraDB 引擎的基本调校。

要显示 MariaDB 的当前设置,可以从命令行发出以下命令:

MariaDB [(none)]> show variables;

这个命令产生一个当前存储在 MariaDB 中的所有变量的长列表。要查看单个设置,我们可以使用图 11-4 所示的 SQL。

A185439_2_En_11_Fig4_HTML.jpg

图 11-4。

Listing environment variables

在图 11-4 中,我们使用了 SQL like子句来搜索show variables列表中的innodb_fast_shutdown变量并显示其设置。

How Innodb Stores Data

InnoDB 和 XtraDB 有两层数据存储设计。存储在磁盘上的数据位于/var/lib/mysql目录中。在这个目录中有ibdata1ib_logfile0ib_logfile1文件。ibdata1文件包含系统和用户数据,ib_logfile0ib_logfile1文件是重做或事务日志。数据库是在/var/lib/mysql/<databasename>目录下的目录中创建的。这些数据库中的表存储在/var/lib/mysql/<databasename>/<tablename.frm>中。

当表中的数据在内存中发生变化时(例如,因为您插入了一条记录),InnoDB 会将其存储在重做日志中。当这些事务日志写满时,MariaDB 服务器将这些更改的数据记录刷新到表文件中。

其中一个原因是性能,就像日志文件系统一样。通过一次执行所有这些操作,磁盘写入效率更高。另一个原因是持久性,因为当系统从意外崩溃中恢复时,这些日志可以用来重放事务。

当 MariaDB 服务器关闭时,它不会处理这些事务日志,因此它们通常包含实时数据。这意味着不能简单地通过删除和重新创建来增加或减少它们的大小。

我们可以做的一个简单的性能调优是更改重做日志的默认大小。如果文件太小,它会很快填满,这意味着 SQL server 会不断清空该文件,从而降低性能。如果文件太大,则恢复时间可能会延长。

在图 11-5 中,我们可以看到 MariaDB 5.5 的日志文件大小,默认为 5MB。MariaDB 10.0 和更高版本的默认值为 48MB。对于较旧的 MariaDB 5.5 版本,在我们更改 InnoDB 事务日志文件大小之前,我们需要确保事务日志文件不再包含任何实时数据。

A185439_2_En_11_Fig5_HTML.jpg

图 11-5。

Redo log file size

我们可以通过强制服务器处理事务日志中的所有条目,并在关闭服务器时将它们写入表文件来做到这一点。这个行为是由变量innodb_fast_shutdown控制的,我们可以在一个正在运行的服务器上更改这个变量,方法是以root用户的身份连接到它,然后运行查询SET GLOBAL innodb_fast_shutdown=1,如清单 11-5 所示。

MariaDB [(none)]> SET GLOBAL innodb_fast_shutdown = 1;
Query OK, 0 rows affected (0.00 sec)
Listing 11-5.Forcing an InnoDB Transaction Log Flush at Shutdown

我们现在可以关闭 MariaDB 服务器,它将把所有挂起的更改从事务日志刷新到表文件中。这意味着我们可以安全地将现有文件移走,并更改事务日志文件的大小。在 CentOS 上,我们通过sudo systemtctl stop mariadb来实现。

日志文件被称为ib_logfile0ib_logfile1,它们可以在/var/ lib/mysql目录中找到。我们将把这两个文件都移走,以便 MariaDB 服务器可以在下次启动时创建新的文件。

$ cd /var/lib/mysql
/var/lib/mysql$ sudo mv ib_logfile* /root

Caution

在验证 MariaDB 服务器可以使用其新配置之前,不要删除这些日志文件。

我们现在可以编辑配置文件了。在 CentOS 上,该文件是/etc/my.cnf.d/server.cnf。这个文件在 CentOS 上相对来说是空的,所以您可以简单地添加我们稍后会给您的配置指令。

在 Ubuntu 上,安装的是 MariaDB 10.0.25。默认情况下,日志文件大小为 48MB,因此通常不需要更改。但是,如果您确实需要更改它,您只需要编辑文件/etc/mysql/mysql.conf.d/mysqld.cnf并添加或更改innodb_log_file_size。然后,在 Ubuntu 上,你需要重启mysql服务(是的,Ubuntu 目前用mysql服务运行 MariaDB)。

我们在这里做的所有更改都在[mysqld-5.5]部分(CentOS)或[mysqld]部分(Ubuntu)下。

innodb_log_file_size = 48M
innodb_log_buffer_size = 16M
innodb_log_files_in_group = 2
innodb_buffer_pool_size = 128M
innodb_flush_method = O_DIRECT

我们将 InnoDB 事务日志文件大小设置为 48Mb,内存中的日志缓冲区设置为 16Mb。这些值意味着服务器将使用多一点的 RAM,但它需要访问磁盘的频率将会降低,从而获得更好的性能。我们通过innodb_log_files_in_group告诉服务器它有两个事务日志文件,这是默认的。

接下来,我们需要为服务器分配一些 RAM 来保存表数据和执行查询。这个数量由innodb_buffer_pool_size变量控制,我们将其设置为 128Mb。在运行 MariaDB 和其他服务的现代服务器上,这应该是一个合理的数量。在专用服务器上,它可以设置为 80%的可用内存。

我们可以通过设置innodb_flush_method告诉服务器不要在操作系统磁盘缓存中缓存任何数据。毕竟,数据存储在我们为 InnoDB 缓冲池保留的内存中。通过指定O_DIRECT,我们通过定期将数据刷新到磁盘来防止系统在 RAM 中保存数据的两个副本。默认情况下,它是未设置的,有几个选项可供选择。根据您的情况和版本,您可能会选择ALL_O_DIRECT(对于大型 InnoDB 数据库文件)。

当数据不在 RAM 中,需要从磁盘写入时,MariaDB 默认一次读取一个很小的 128KB 的数据块。这可以节省内存使用,但当需要读取大量数据时,速度会非常慢。我们将通过read_buffer_sizeread_rnd_buffer_size变量增加这个块的大小。

read_buffer_size = 1M
read_rnd_buffer_size = 1M

我们还将允许服务器执行非常大的查询,因此可以存储更多的数据。默认值为 1Mb(在旧版本上);我们将把它改为 16Mb。

max_allowed_packet = 16M

最后,我们将通过设置log_bin变量来启用二进制日志。这将有助于我们在撞车时恢复。

log_bin  = /var/log/mariadb/mariadb-bin.log
expire_logs_days = 14
max_binlog_size = 128M

bin 日志或二进制日志用于数据库上的复制事务。插入、更新和删除都记录在其中,并且可以在辅助服务器上重放以保持同步。管理员也可以使用它们来恢复数据库的备份。在前面的代码中,我们告诉服务器在 14 天后自动清除二进制日志。这意味着我们应该至少每两周备份一次 MariaDB 数据。(我们将在第十四章介绍如何实现备份过程的自动化。)最后,我们告诉服务器,一旦当前的二进制日志文件达到 128Mb,就启动一个新的二进制日志文件。这使得 bin 日志更易于管理,并且可以更快地传输到任何二级或三级数据库。我们还在独立于主数据库文件的位置创建了它们,最好是在它们自己的磁盘或分区上。

我们现在已经完成了基本的 MariaDB 服务器调整,所以我们可以通过 CentOS 上的sudo systemctl start mariadb或 Ubuntu 上的sudo systemctl start mysql重新打开它。要验证 MariaDB 服务器是否运行良好并创建了新的 InnoDB 事务日志文件,您可以检查日志。在 CentOS 上,mariadb写入/var/log/mariadb/mariadb.log文件;在 Ubuntu 上,它使用的是/var/log/syslog文件。

请注意,我们没有针对高端性能调整 MariaDB 服务器;我们刚刚修改了基本配置,以提供更好的数据完整性,并比正常情况下执行得更好。如果您需要极高的性能或高级功能,如跨多个服务器的数据复制,我们建议您阅读 Baron Schwartz 等人的《高性能 MySQL,第三版》(O'Reilly Media,Inc .,2012)。

基本 MariaDB 管理

正如您已经看到的,MariaDB 有一个用户和密码的内部列表。这意味着您需要知道如何管理 MariaDB 用户,因为您不希望所有应用程序都以root的身份连接到 MariaDB 服务器。我们将演示如何通过命令行mysql客户机创建和删除数据库和用户。

数据库

在 MariaDB 中创建数据库很容易。您通过命令行实用程序连接到服务器并发出CREATE DATABASE语句,将数据库名称作为参数给它,如图 11-6 所示。(注意,为了清楚起见,我们在 SQL 语句中使用了大写字母;如果你使用小写,他们仍然会工作得很好。)

A185439_2_En_11_Fig6_HTML.jpg

图 11-6。

Creating a new database in MariaDB

在图 11-6 中,你可以看到我们创建了一个名为mydb的数据库,然后我们切换到那个数据库并检查它是否包含任何表。你会注意到,当我们切换到mydb数据库时,我们的提示也从[(none)]变成了[mydb],表示我们正在处理的当前数据库。请注意,我们使用了反引号来引用数据库名称。在这种情况下,没有明确的必要这样做,但是数据库、表和列的名称有时可以包含一个保留字符,如连字符。例如,如果你想创建一个名为my-db的数据库,你需要使用反引号;否则,MariaDB 会将my-db解释为从my列的值中减去db列的值。由于这两列都不存在,将会产生一个错误,如图 11-7 所示。

A185439_2_En_11_Fig7_HTML.jpg

图 11-7。

The importance of proper quoting

使用引号,数据库以指定的名称创建。

Tip

命名数据库和表时,通常最好只使用字母数字字符和下划线字符。即使这样,适当的引用也是一个好习惯。

但是我们不需要这个数据库,所以我们将再次删除它。我们通过DROP DATABASE语句做到这一点。

MariaDB [mydb]> DROP DATABASE `my-db`;
Query OK, 0 rows affected (0.00 sec)

Caution

您不能撤销一个DROP DATABASE命令,即使您在一个事务中运行它然后回滚也不行。在这样做之前,请仔细考虑,并确保在删除数据之前创建了备份。

用户和权限

特权是通过GRANT语句管理的。该语句接受一组参数,这些参数定义了允许给定主机上的用户对特定对象执行的一组操作。

实际上,您通常只需创建一个用户,允许他在单个数据库上执行所有操作。

这意味着每个使用自己数据库的应用程序都有自己的 MariaDB 登录。如果某个应用程序包含允许访问数据库服务器的错误,那么只有该应用程序使用的数据会受到威胁。

我们以 root 身份连接到 MariaDB 服务器,然后创建一个名为jsmith的用户,就像我们的主机帐户一样,他可以访问所有数据库和表并创建新用户。这样我们就不需要继续使用 MariaDB root账户了。

图 11-8 中的代码创建了一个名为jsmith的用户,他只能用密码“secret”从本地主机连接ALL关键字指定用户拥有所有权限。我们使用简写*.*来表示所有数据库中的所有表格。

A185439_2_En_11_Fig8_HTML.jpg

图 11-8。

Creating a GRANT for users

我们可以通过使用mydb.*来限制对名为mydb的单个数据库中的表的访问。最后,我们指定了GRANT OPTION,它允许这个用户使用GRANT语句。表 11-6 显示了可能的特权。

表 11-6。

GRANT Privileges

| `SELECT` | 提供执行`SELECT`语句的能力 | | `INSERT` | 提供执行`INSERT`语句的能力 | | `UPDATE` | 提供执行`UPDATE`语句的能力 | | `DELETE` | 提供执行`DELETE`语句的能力 | | `INDEX` | 提供在表上创建索引的能力 | | `CREATE` | 提供创建数据库表的能力 | | `ALTER` | 提供改变数据库表的能力 | | `DROP` | 提供删除数据库表的能力 | | `GRANT OPTION` | 能够向其他用户授予相同的权限 | | `ALL` | 授予除`GRANT OPTION`之外的所有权限 |

让我们以刚刚创建的用户身份登录,并创建一个只能访问mydb数据库的用户。我们现在不需要指定一个 MariaDB 用户来连接,因为我们刚刚创建了一个与我们的主机帐户同名的 MariaDB 用户。

要做到这一点,就像我们在图 11-9 中看到的那样,将以下参数传递给mysql客户端。我们使用–h连接到本地主机,-u表示我们想要连接的用户,–p表示我们将提供一个密码。

A185439_2_En_11_Fig9_HTML.jpg

图 11-9。

Creating user accounts

我们现在有一个名为mydb的用户,他只能访问mydb数据库中的表。由于我们目前不需要这个用户,我们将向您展示如何通过从系统中删除mydb来删除该用户。

MariaDB [(none)]> DROP USER `mydb`@`localhost`;
Query OK, 0 rows affected (0.00 sec)

由于我们也不需要mydb数据库,我们也将删除它。

MariaDB [(none)]> DROP DATABASE `mydb`;
Query OK, 0 rows affected (0.00 sec)

教授 SQL 和 MariaDB 管理技能超出了本书的范围,但是这些基本技能将允许您通过遵循安装说明来设置大多数基于 MariaDB 的 web 应用程序。许多网站致力于教授 MySQL 和 MariaDB 技能,以下资源也是很好的信息来源:

管理网站内容

有了可用的 web 和 SQL server,您现在可以安装一些 web 应用程序来增强您的在线状态。在本节中,我们将向您展示如何在自己的虚拟主机上安装一些 web 应用程序。我们不会向您展示如何使用这些 web 应用程序,因为大多数都附带了优秀的文档和支持社区。

我们将首先创建一个名为www的组,我们将向其中添加任何需要能够修改网站安装的用户,并且我们将自己添加到该组中。我们可以通过-K参数覆盖这个组的默认umask

$ sudo groupadd -K UMASK=0002 www
$ sudo usermod -G www jsmith

一旦我们注销并重新登录,组成员资格更改将生效。

接下来,我们将更改 /var/www/html/www.example.com 目录及其包含的任何目录的所有权和权限,以便www组可以完全访问它。

$ sudo chgrp -R www /var/www/html/www.example.com
$ sudo chmod u+rwx,g+srwx,o+rx /var/www/html/www.example.com

除了完整的权限字符串,我们还可以指定八进制模式 2775。在 CentOS 上,web 服务器作为apache用户运行,在 Ubuntu 上,它作为www-data用户运行,您需要根据需要chown文件和目录。

$ sudo chown www-data -R /var/www/html/www.example.com

网络状态

当然,你会希望你的企业有一个网站,当然这从来没有这么容易实现。为此,我们将安装一个 CMS 作为我们的网站。

CMS 允许我们将精力集中在创建内容并使其看起来不错,同时为我们提供了一个框架,可以保存页面的多次修订,将 web 内容与图形设计分开,并管理用户和员工的访问权限。

PHP 是世界上最流行的网站框架之一,而用于开发网站的最流行的工具之一叫做 WordPress。我们将在 www.example.com 虚拟主机上安装 WordPress。当然有很多 CMSs 我们选择 WordPress 是因为在其他基于 PHP 的 CMSs 中,它是最简单和最广泛的。你还有其他选择;您可以考虑以下方法之一:

该软件从 https://wordpress.org/download/ 开始以 tarball 的形式提供。在撰写本文时,最新的版本是 WordPress 4.5.3。我们将使用curl命令将其下载到我们的主目录。

$ curl https://wordpress.org/latest.tar.gz -o wordpress.tar.gz

接下来,我们打开 tarball。我们可以在这里将其解包,然后将所需的文件和目录移动到 web 根目录,或者我们可以直接将其解包到 web 根目录。我们将通过告诉tar从归档中去除第一个目录组件并使用-C选项指定一个目标目录来实现后者。

$ sudo tar -xz --strip-components=1 -C /var/www/html/www.example.com/ -f wordpress.tar.gz

Tip

要找出一个 tar 存档包含哪些目录,使用-t-v选项显示一个文件列表,而不提取它们(例如tar -tvzf wordpress.tar.gz)。

我们现在需要创建一个新的数据库和一个新的数据库用户。我们就像之前展示给你的那样做。

MariaDB [(none)]> CREATE DATABASE example;
MariaDB [(none)]> GRANT CREATE, SELECT,  INSERT,  UPDATE,  DELETE  ON example.*
  TO  `wpexample`@`localhost` IDENTIFIED BY 'secret';

我们在那里做了什么?我们只授予了 WordPress 通常需要的数据库的必要权限。这符合最小特权访问原则,我们只提供用户需要的权限,不提供更多。我们还为该用户使用了一个不太好的密码示例。

WordPress 站点有一个需要编辑的配置文件,描述了数据库细节和一些密钥。我们可以复制这个文件,并将其重命名为所需的名称。我们将进入 www.example.com 目录来编辑这些文件。

$ sudo cp wp-config-sample.php wp-config.php

在我们编辑wp-config.php文件之前,请访问这个站点,它创建了配置文件中所需的随机密钥: https://api.wordpress.org/secret-key/1.1/salt/ 。这些密钥用于对会话 cookies 进行签名,您可以随时更改它们(任何已登录的用户都必须重新登录)。我们只是复制了这些钥匙。

define('AUTH_KEY',         'yr-[fb[mc=0ef:L9 Px|6∼al0PwR<KrxOy!|%g??@hD&hPh(=1J-DWO9pSWGiuic');
define('SECURE_AUTH_KEY',  '24|Nn+<)pFE@6Ity9LwMrDT!|JYe*JQFQm+qb(#[2-J?|c!U|$5/$rr;_wln∼p-a');
define('LOGGED_IN_KEY',    'D_OYeZJLx∼,/bB^]l1-?dDIni1StB(z-/-2FQSd^:}2.l|]uJXlMW%,<h6Q!k9x^');
define('NONCE_KEY',        ' 7=5Z7c4%tO!b@HAD= [n0by2Unrp^Et@.h-&3S2SrxdLL6gKV>3<o+dVj;,BI^h');
define('AUTH_SALT',        'ZYV|3qST=QVlH^MsccnF;k,-yKa=oq&x8iA|ohNN,6j.Y:o_,9zp$XBPzO3UcI^i');
define('SECURE_AUTH_SALT', 'vvC.{}1RjuE2I!yRs?]D/iHmZ3rbf->bHzpAlz?tR]$Nt..#=5{WC52#ty#C93+]');
define('LOGGED_IN_SALT',   'JZ>-u/:oUbhdK4qgJ.n_ReUi%Lj∼J(t8{MI?kme#.U[qF:aZw*zpwIoc^:#4/[$O');
define('NONCE_SALT',       'T%|]FT^^!.:[sL}S4-DXz{o)R*TasHB.eh}<hknQjuK|R&yW⁵ff9M-f{KlC-I@4');

现在,当您编辑wp-config.php文件时,您可以在删除带有put your unique phrase here字符串的行之后,将您的密钥粘贴到“认证唯一密钥和 Salts”部分。接下来,您还应该添加我们在上一节中创建的数据库细节。

有了正确位置的站点内容,我们现在可以将 web 浏览器指向 www.example.com/wp-admin/install.php 。这将允许我们开始基于网络的安装过程,如图 11-10 所示。

A185439_2_En_11_Fig10_HTML.jpg

图 11-10。

WordPress installation

要继续,我们点击继续安装我们本地语言的 WordPress。下一页如图 11-11 所示,让我们有机会命名我们的网站并提供管理细节。

A185439_2_En_11_Fig11_HTML.jpg

图 11-11。

Entering the web site details and admin credentials

记得把你的密码记在安全的地方;你很快就会需要它。完成后,我们会看到成功页面,并提供一个登录按钮(图 11-12 )。

A185439_2_En_11_Fig12_HTML.jpg

图 11-12。

Successfully installed WordPress

下面给大家快速展示一下登录流程。首先,我们通过刚刚创建的用户名和密码获得访问权限(图 11-13 )。

A185439_2_En_11_Fig13_HTML.jpg

图 11-13。

Logging in

登录后,我们就可以访问 WordPress 管理控制台(图 11-14 )。

A185439_2_En_11_Fig14_HTML.jpg

图 11-14。

WordPress admin console

使用 SSL/TLS 证书保护您的 Web 服务

拥有安全的 web 服务几乎被认为是强制性的。保护您的 web 服务有很多原因,而不保护它们的原因很少。适当保护的 web 服务器通信不仅可以防止对您的通信(如用户名和密码或信用卡信息)的任何窃听,还可以阻止对 cookies 或会话信息的窥探,并有助于防止跨站点脚本和恶意代码注入。谷歌现在使用 HTTPS 作为排名信号,这意味着如果你的网站完全加密,那么你的排名将高于相同内容的未加密网站。

我们将讨论 TLS,然后向您展示如何创建自己的证书颁发机构(CA ),最后我们将安装由 Let's Encrypt 提供的证书。

TLS 和证书

TLS 使用数字证书和一种称为公钥加密的加密技术。公钥加密使用两个密钥:公钥是公开的,私钥存储在服务器上并保密。使用公钥加密的任何内容只能用相应的私钥解密;这与第十章的“DNSSEC”边栏部分描述的概念相同。

Note

数字证书和公钥加密是复杂的话题。这只是给你使用 TLS 的基础知识的一个介绍。如果你真的对这背后的数学感兴趣,我们推荐这本优秀的书:《应用密码学:协议、算法和 C 语言源代码》,20 周年纪念版,作者 Bruce Schneier (John Wiley & Sons,2015)。

使用 TLS 时,数字证书是服务器的公钥,其作用类似于电子驾照。它标识您正在连接的服务器或网站。当你连接到一个 HTTPS 网站时,你的浏览器所做的是接受该网站的数字证书作为该网站是它所声称的那个网站的证据。像驾照一样,证书也有有效期,并且只在固定的时间内有效,通常是 12 个月。

每个数字证书还可以包含对证书颁发机构的引用。CA 是一种颁发证书的机制,它有一个称为根证书的特殊证书,用于验证服务器证书的真实性。用同样的许可证比喻,根证书就像您所在州的机动车部门。人们去那里检查你是否有有效的执照,以及你是否是你所说的那个人。这些根证书通常与用于连接服务器的客户端捆绑在一起;例如,您的 web 浏览器将拥有来自知名 ca 的根证书集合。

因此,基于证书的加密的基本流程(简单来说)如下:

  1. 您的客户端连接到服务器并请求证书。
  2. 服务器出示其证书。
  3. 客户端检查对根证书的引用。
  4. 客户端使用捆绑的根证书来验证您的证书是真实的。
  5. 如果您的客户端信任该证书,则使用服务器的公共证书在客户端和服务器之间启动并加密连接。

Tip

在某些情况下,您的客户端会告诉您不确定是否信任该证书,并提示您决定是否信任该服务器。

您需要了解四种类型的证书,使用每种类型都有利弊。

  • 由商业 CA 颁发的证书
  • 由非商业性 CA 颁发的证书
  • 由自我管理的 CA 颁发的证书
  • 自签名证书

商业证书颁发机构颁发的证书

来自商业 ca 的证书由流行的提供商(如 VeriSign、Thawte 或 Comodo)颁发。这些证书通常要求定期付款,例如一年或两年一次。价格取决于证书的类型和数量。大多数商业 ca 的根证书与客户端捆绑在一起,如浏览器、邮件客户端和其他使用 SSL/TLS 连接的工具。通常会定期对商业 ca 进行安全性审核,并且由它们颁发的证书通常被认为是安全的。

来自非商业证书颁发机构的证书

除了商业证书提供商之外,还有少量非商业证书提供商。这些提供商不对他们的证书收费,但相应地,他们的根证书有时并不与许多浏览器捆绑在一起。这意味着如果您将这些证书用于网站或保护简单邮件传输协议(SMTP)之类的服务,您的客户端很可能会警告您无法确定证书的有效性和安全性。

如果浏览器中没有安装 CA 的根证书,唯一的解决方法是手动将非商业 CA 的根证书添加到浏览器中。如果您有很多客户端浏览器,这会给您的环境增加很多开销和维护。在许多情况下,例如在网站上,您无权访问客户端,这些错误可能会导致某人收到客户端无法验证证书的消息,从而不信任您的网站。例如,这使得在电子商务网站上使用非商业证书成为问题。

然而,最近情况发生了变化。为了使互联网更加安全,人们大力推动对互联网进行加密。不在网站上使用 HTTPS 的原因之一是证书太贵,而且非商业证书没有得到广泛支持,正如我们提到的。互联网安全研究小组(ISRG)决定创建“让我们加密”来解决这个问题。

Let's Encrypt 是一个非营利组织,致力于通过提供一种简单的自动机制来获取和安装 TLS 证书,从而帮助加密互联网。让我们加密根 CA 也捆绑在许多现代浏览器中,这解决了非商业 CA 面临的许多问题。你可以在 https://letsencrypt.org/ 找到更多关于 Let's Encrypt 的信息。

获得非商业证书的另一种方法是使用 CAcert。它提供免费的临时证书(如 Let's Encrypt ),但也允许您拥有更长的证书,如果您传递一个验证域所有者的“信任密钥”。欲了解更多信息,请访问 CAcert 网站和 wiki: www.cacert.org/http://wiki.cacert.org/wiki/

来自自我管理的证书颁发机构的证书

您也可以创建和管理自己的证书。这些证书由您自己创建和管理的证书颁发机构颁发。因此,这些证书不需要花费任何钱,但是它们确实有其他问题。首先,既然是自己的 CA,就不能指望别人信任你的证书。这就引出了第二个问题:可用性。您的 CA 的根证书不与客户端捆绑在一起,将来也不会。所以如果你想安装我们的根证书,你需要通过软件管理来完成(比如 Ansible 或 Puppet,在第十九章中讨论)。

Note

在非商业 ca 的情况下,至少有少量的客户端捆绑了它们的根证书。在您自己的自我管理 CA 的情况下,拥有您的根证书的客户端是您自己安装的。

因此,例如,当您的 web 客户端尝试验证您的 CA 提供的证书时,会生成一条错误消息,指示客户端不信任该 CA。其他服务可能会拒绝使用有效证书进行连接。为了克服这个错误,您需要在客户端上安装 CA 的根证书。这是您可以为您管理的客户端(例如,您的内部桌面)做的事情,但对其他人来说这是不可行的。

Tip

在这种模式下,您必须保护和管理自己的 CA。对于少量的证书来说,这并不太复杂,但是它确实会带来一些问题和风险,我们将会讨论这些问题和风险。

自签名证书

自签名证书不使用 CA。它们是由你签名的,因此也不花任何钱。与自我管理的 CA 生成的证书一样,它们是不可信的,并且会在您的客户端上生成类似的错误消息。与自我管理的 CA 生成的错误不同,您不能通过添加根证书来消除此错误,因为您没有根证书可添加到客户端。自签名证书通常仅用于测试,很少在生产环境中使用。

选择证书类型

如果您想长期购买证书,最好使用由商业 ca 颁发的证书。这里的关键问题是成本。来自商业 CA 的证书一年可能要花费数百美元。仅仅为了保护你的电子邮件或你的商业营销网站,这就是一笔相当大的费用。因此,如果您不想要购买证书的费用,我们推荐一个让我们加密证书。

如果我们选择商业证书,我们将需要创建私钥和证书签名请求(CSR)。我们接下来会展示这一点。

为 TLS 创建证书

正如您已经发现的,为了让 TLS 工作,我们需要两个证书:一个服务器证书和一个 CA(商业 CA、非商业 CA 或您自己的 CA)的根证书。让我们从生成第一个服务器证书开始。第一步是生成服务器密钥和 CSR。无论我们是从商业或自我管理的 CA 生成证书,我们都会采取这些步骤。

该过程创建了我们的私钥和 CSR。该 CSR 然后被提交给 CA,在我们的例子中是我们自己的 CA,但也提交给商业 CA。正是这个签名过程允许客户端确认服务器证书的身份。

在清单 11-6 中,我们使用作为 OpenSSL 应用程序一部分的openssl命令生成一个密钥和请求。OpenSSL 应用程序是一个开源的 SSL 实现,它允许 Linux 和其他操作系统使用 SSL 加密和保护应用程序。

$ openssl genpkey -algorithm RSA -out www.example.com.key -pkeyopt rsa_keygen_bits:2048
.........................................................................+++
.................+++
Listing 11-6.Generating a Server Key and Request

在清单 11-6 中,我们使用了openssl命令来生成一个使用 RSA 密码的私钥。密钥长度为 2048 位,这是互联网的当前标准,但如果您愿意,也可以更长。问题是一些 ca 不支持更大的密钥长度。

Note

更长的密钥长度增加了加密的安全性,但是有一个(轻微的)处理代价,密钥加倍并不会导致加密安全性加倍。因此,网站使用的大多数密钥长度为 2048 位,预计在 2030 年左右仍能保持安全。如果您愿意,可以将密钥长度增加到 4,096。

我们通过-algorithm选项告诉openssl命令使用 RSA 密码,-out选项告诉openssl命令把密钥写到哪里,这里是写到 www.example.com.key 文件。我们通过–pkeyopt rsa_keygen_bits:2048将位大小传递给openssl

Tip

输入man genpkey可以看到关于openssl genpkey选项的更多细节。

接下来,我们想要生成一个 CSR。这就是我们要交给 CA 签字的东西。这是从我们刚刚生成的私钥中生成的。这样,我们就不必为了得到证书而把我们的私钥给任何人。虽然私钥很珍贵,需要保护,但 CSR 是公开的,不需要这样的限制。我们创建一个 CSR,如清单 11-7 所示。

$ openssl req -new -sha256 -key www.example.com.key -out www.example.com.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:AU
State or Province Name (full name) []:Victoria
Locality Name (eg, city) [Default City]:Melbourne
Organization Name (eg, company) [Default Company Ltd]:Example Inc
Organizational Unit Name (eg, section) []:IT
Common Name (eg, your name or your server's hostname) []:www.example.com
Email Address []:admin@example.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

Listing 11-7.Generating the CSR

在请求创建过程中,系统会提示您输入一些有关新证书的信息。如果不想回答特定的查询,可以键入 Enter 跳过该字段。系统将提示您输入两个字母的国家代码,例如,US 代表美国,GB 代表英国,AU 代表澳大利亚。

Note

您可以在 www.nationsonline.org/oneworld/country_code_list.htm 找到国家代码的完整列表。

系统还会提示您输入您所在组织的州/省、直辖市和名称,以及组织中的组织单位(可选)。当客户端或用户查询时,这些数据将显示在您的证书中。这是一个好主意,在这里要具体和准确——特别是如果你提交你的 CSR 由商业 CA 签署。

接下来,也是最重要的,我们需要指定证书的通用名称。这必须是将要使用该证书的服务器的确切主机名。如果您指定了不正确的主机名,您将得到一个关于服务器和证书名称不匹配的错误。在这种情况下,我们指定mail.example.com,这是电子邮件服务器的完全限定主机名。

然后指定证书联系人的电子邮件地址,在本例中为admin@example.com.

最后,系统会提示您输入证书的一些额外属性。你不需要担心这些,你可以输入回车跳过这些字段。

这个过程会给你留下两个文件: www.example.com.keywww.example.com.csr 。我们将保留这两个文件,因为我们在这个过程的后面会用到它们。

该过程的下一步是用 CA 签署 CSR。如果您要创建自己的 CA,请参阅下一节“创建自己的证书颁发机构”

否则,您需要提供您将提供给商业 CA 的 www.example.com.csr 文件的内容,然后它将向您发送证书。您的商业 CA 将提供如何提供 CSR 文件的说明。通常,您会将 CSR 的内容剪切并粘贴到网页中,然后提交。CA 随后会对其进行签名,并在签名证书可供下载时通知您。

在下一章中,我们将向您展示如何创建另一个证书并在您的 Postfix 安装中使用它。

创建自己的证书颁发机构

创建自己的 CA 是一件容易的事情。首先,您创建一个目录来保存您的 CA 和该目录中的一些子目录。CentOS 将所有 OpenSSL 文件存储在/etc/pki目录中。Ubuntu 用/etc/ssl。在本例中,我们将把我们的 CA 放在 CentOS 主机上的/etc/pki目录中。以下目录应该已经存在:

$ sudo mkdir /etc/pki/CA
$ sudo mkdir /etc/pki/CA/{private,newcerts,crl,certs}
$ sudo chown –R root:root /etc/pki/CA
$ sudo chmod 0700 /etc/pki/CA/private

私有目录将保存 CA 的私有密钥,newcerts目录将包含 CA 将签署的每个证书的副本。我们还确保 root 用户拥有所有目录,并且我们保护私有目录,以便只有 root 用户可以访问它。

接下来,您需要创建一个数据库来保存您的签名证书的详细信息。

$ echo '01' | sudo tee /etc/pki/CA/serial
$ sudo touch /etc/pki/CA/index.txt

Note

在第一行中,我们将数字01回显到了/etc/pki/CA/serial文件中。为此,我们使用了一个名为tee的命令,它可以从标准输出中读取数据,然后写入标准输出和文件。您可以在 tee 命令的man页面中找到更多信息。

串行文件跟踪通过此 CA 颁发的证书的最后一个序列号,从数字 01 开始。CA 颁发的每个证书都有一个唯一的序列号。

index.txt文件将包含当前由该 CA 管理的证书列表。每个证书旁边都有一个字母,表示该证书的状态。

  • R:撤销
  • E:过期
  • V:有效

OpenSSL 有一个控制缺省值和 CA 设置的配置文件。大多数发行版上都有一个模板文件。这个名为openssl.cnf的文件位于 CentOS 发行版的/etc/pki/tls/目录和 Ubuntu 的/etc/ssl/目录中。

如果您的 CA 使用非标准目录或者有一些其他设置,您可以复制这个文件并在您的CA目录中编辑它。为此,请在配置文件中找到以下列内容开头的部分:

[ CA_default ]

在下面的例子中,我们更改了名为dir的配置选项。如果我们想更改openssl命令来从当前目录中读取 CA 信息,我们应该执行以下操作:

dir = .

这告诉 OpenSSL 在当前目录中查找配置和签署证书所需的目录和文件。对于 CentOS,这已经设置为/etc/pki/CA。Ubuntu 将此设置为./demoCA。这意味着当您签署证书时,您必须将您的工作目录更改为CA目录,在我们的例子中是/etc/pki/CA

Tip

查看一下您的 CA 在openssl.cnf文件中的默认设置。它应该显示您为 CA 创建的默认目录和文件;在我们的例子中,它们是private目录和serial文件。

现在您需要为您的 CA 创建一个自签名证书和一个私钥。

$ cd /etc/pki/CA
$ sudo openssl req -new -x509 -newkey rsa:4096 -keyout private/cakey.pem \
   -out certs/cacert.pem -days 3650 \
   -subj '/C=AU/ST=Victoria/L=Melbourne/O=Example Inc/OU=IT/CN=ca.example.com/emailAddress=admin@example.com/'
Generating a 4096 bit RSA private key
...................................++
..................................................................................................................................................++
writing new private key to 'private/cakey.pem'
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----

首先,正如我们提到的,我们将工作目录改为/etc/pki/CA。接下来,我们创建一个密钥,这个也是 RSA,长度为 4096 位。我们将密钥存储在/etc/pki/CA/private/cakey.pem文件中。我们还将证书创建为自签名的,因为我们没有另一个 CA 来签名它。我们将证书的期限指定为 3,650 天(或 10 年)。我们已经指定了一些其他选项:-x509,这表明证书是自签名的,以及-extensions v3_ca,这表明我们正在创建一个 CA 证书。最后一个选项-subj,允许我们回答我们添加到证书主题的属性。

Tip

值得通读一下openssl.cnf配置文件,以了解您还有哪些其他选择。

系统将提示您输入 CA 私钥的密码短语或密码。选择一个好的密码并记住它。每次创建新证书时,您都需要此密码。

如您所见,CA 由一个私钥和一个公钥证书组成。您现在有了自己的 CA,可以用它来签署证书。

Securing Your Certificate Authority

如果您使用 CA 来保护生产或任何敏感数据的传输,您会希望 CA(私钥、CRL 等)非常安全。如果你只是为了好玩和试验而使用它,你可以对如何保护它不那么严格。

在本例中,我们在用于 web 服务器的同一台主机上创建了一个 CA。这在现实世界中并不理想,最好有一个专门的主机来进行这项活动。理想情况下,该主机在使用时应该受到限制或与网络断开连接,而将它放在 web 服务器上是很危险的。我们强烈建议使用专用主机。

保护 CA 的一个好方法是在有密码保护的加密磁盘上建立一个专用的虚拟映像。当您想要签署证书时,可以装载加密的磁盘,启动虚拟映像,签署证书,关闭虚拟主机,然后卸载磁盘。

我们可以在任何我们喜欢的主机上为 www.example.com 创建一个私钥。您不需要在具有相同主机名的主机上创建私钥。然后,您必须将私钥复制到 www.example.com 主机才能使用它。建议您不要将私钥放在不使用它们的主机上。如果不需要的话,应该安全地删除它们(比如使用shred程序)。

如果您的 CA 遭到破坏,根据谁在使用它,您可以重新创建它并重新签名您的所有证书。如果其他组织正在使用您的 CA,或者颁发的证书数量很大,那么重新创建 CA 的工作可能会很烦人,而且会造成干扰。

当然,与 CA 相关联的密码将被安全地存储在密码管理器中,并且 CA 的私有密钥的副本可以被存储在加密的拇指驱动器上并且存储在安全的保险库中。

向您的证书颁发机构签署您的证书

现在,您已经创建了您的 CA,您可以使用它来签署您的证书请求。在我们的例子中,CA 位于我们创建 CSR 请求的同一个主机上。您通常需要将 CSR 文件复制到 CA 主机。这将获取您的 CSR 并用您的 CA 对其进行签名,并输出一个签名证书。在清单 11-8 中,我们已经签署了我们的 CSR。

$ cd /etc/pki/CA
$ sudo openssl ca -out /root/www.example.com.cert -infiles /root/www.example.com.csr
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Aug 21 02:05:11 2016 GMT
            Not After : Aug 21 02:05:11 2017 GMT
        Subject:
            countryName               = AU
            stateOrProvinceName       = Victoria
            organizationName          = Example Inc
            organizationalUnitName    = IT
            commonName                = www.example.com
            emailAddress              = admin@example.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                3E:37:13:CB:D3:84:58:9D:47:73:89:A6:80:12:DD:90:FE:C7:06:4B
            X509v3 Authority Key Identifier:
                keyid:54:57:27:C4:82:CA:C2:97:CE:5E:C7:64:A8:99:D3:A8:D1:1E:EC:77

Certificate is to be certified until Aug 21 02:05:11 2017 GMT (365 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Listing 11-8.Signing Our Certificate Request

在清单 11-8 中,我们使用了ca选项来签署我们的请求。-out选项指定了我们将要输出的签名证书,而-infiles选项指定了我们想要签名的 CSR(我们假设我们的 CSR 文件在/root目录中)。

Tip

您可以通过输入man ca来查看openssl ca命令的其他可用选项。

系统将提示您输入为 CA 的私钥创建的密码,然后将显示您的证书的详细信息,最后将提示您签署证书并将其详细信息写入 CA 的数据库。对两个问题都回答y是。

在这个过程的最后,您将拥有一个签名的证书、一个私钥和一个证书请求,它们都位于/root目录中。您的证书有效期为一年(您可以使用-days选项指定不同的有效期来覆盖此期限)。

您可以使用openssl命令检查证书的详细信息。

$ openssl x509 -in /root/www.example.com.cert -noout -text -purpose | more

Tip

你可以把握你的企业社会责任。当您想要更新您的证书时,在我们的例子中是一年后,您可以使用清单 11-8 中的命令重新签署这个请求。这意味着你不需要不断重新创造你的企业社会责任。

请记住,为了避免您的客户端指出您的证书不可信,您必须将您的根证书安装在相关的客户端中,例如,用户的浏览器!CAcert 网站有在 http://wiki.cacert.org/FAQ/BrowserClients 将其根证书安装到各种客户端的说明。若要安装您的证书,请在说明中用您的证书替换它的证书。

使用“让我们加密”创建 HTTPS 证书

Let's Encrypt ( https://letsencrypt.org )有加密互联网的使命!它是一个免费的、自动化的、开放的认证机构,来自非营利的互联网安全研究组织(ISRG)。Let's Encrypt 是一个免费的 CA,它将自动创建、请求和签署证书,主要用于 web 服务器。我们将在我们的许多服务中使用 Let's Encrypt,因为这是加密所有流量的一种安全便捷的方式。

它是如何工作的?嗯,就像我们说的,它的核心就像一个普通的 CA。Let's Encrypt 提供了一个代理,它使用自动证书管理环境(ACME)协议首先验证 web 服务器域,然后创建私钥和 CSR 请求来创建有效的证书。续订和撤销也由 Let's Encrypt 完成。更详细的解释请参考这里的文档: https://letsencrypt.org/how-it-works/

我们将使用 Let's Encrypt 提供的 Certbot 代理来管理我们的证书。为此,您的 web 服务器需要一个有效的域名系统(DNS)公共记录,并且能够接受来自 Internet 的 HTTP 和 HTTPS 连接。我们还将使用 Let's Encrypt staging 环境,以便我们的测试不会影响他们的服务。

要安装 Certbot,我们可以在这里看到说明: https://certbot.eff.org 。我们选择了 Apache 和 CentOS 7 的指令。我们需要发布以下内容:

$ sudo yum install epel-release

然后我们需要安装python-certbot-apache包。

$ sudo yum install python-certbot-apache

对于 CentOS 上的 Apache HTTPS,我们要求安装mod_ssl包。在 Ubuntu 上,SSL 模块是核心 Apache 包的一部分。对于 CentOS,模块通过/etc/httpd/conf.modules.d/00-ssl.conf加载,在安装软件包后应该会出现。

我们现在要做的是使用certbot命令来创建和安装我们的证书。Certbot 将处理证书的创建和安装,它将复制我们的虚拟主机并将http://流量重定向到https://。因为我们使用的是 Let's Encrypt 临时服务器,所以我们将制作无用的证书,但是在测试生产 Let's Encrypt 系统时,我们不会受到访问限制的约束。

Note

我们不能使用example.com域来创建加密证书,因为我们显然不拥有该域。相反,我们在这些例子中使用了另一个域。

certbot命令有几个选项可用。您可以通过键入certbot --help来查看它们,或者对于所有可用的选项,您可以发出certbot --help all。让我们使用certbotwww.example.com 创建我们的证书。

$ sudo certbot --test-cert –d www.example.com

这里,我们发出了带有--test-cert参数的certbot,这意味着我们将使用分段应用程序编程接口(API)而不是真正的加密端点。这个 staging API 将完成我们需要的一切,除了创建一个有效的证书(证书是由假的 LE 发布的)。我们还将–d选项传递给 Certbot,以提供我们想要认证的主机名。我们可以通过添加额外的–d <hostname>参数来提供多个主机名,只要您有虚拟主机。

当我们第一次运行certbot时,我们提供了一个电子邮件地址来联系我们的证书(图 11-15 )。

A185439_2_En_11_Fig15_HTML.jpg

图 11-15。

Adding e-mail address to Certbot

这个过程在/etc/letsencrypt/accounts目录中为暂存 API 创建一系列文件。当我们第一次运行certbot时,我们提供了一个电子邮件地址来联系我们的证书(图 11-16 )。

A185439_2_En_11_Fig16_HTML.jpg

图 11-16。

Agreeing to the Let’s Encrypt terms of service

在图 11-17 中,我们有两个选项。

A185439_2_En_11_Fig17_HTML.jpg

图 11-17。

Securing our web site

第一种选择很容易;这是我们保留http://https://访问的地方。第二个选择(安全)是我们只允许https://访问我们的网站。在这个场景中,http://流量被重定向到https://。它通过将以下几行添加到我们的虚拟主机配置中来实现这一点:

<VirtualHost *:80>
...
RewriteEngine on
RewriteCond %{SERVER_NAME} =www.example.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,QSA,R=permanent]
</VirtualHost>

certbot命令还创建了一个新文件来包含我们的 SSL 配置。它与我们的http://虚拟主机具有相同的配置,但是添加了 SSL 密钥。RewriteEngine允许我们根据特定条件路由 HTTP 流量。在这里,我们已经将http://的所有流量重定向到网站的https://版本。

/etc/httpd/conf.d/www.example.com-le-ssl.conf
<IfModule mod_ssl.c>
<VirtualHost *:443>

  ServerName www.example.com

  ServerAdmin webmaster@example.com
  DocumentRoot  /var/www/html/www.example.com
  DirectoryIndex index.php
  ProxyPassMatch ^/(.*\.php(/.*)?)$ unix:/run/php-fcgi.sock|fcgi://127.0.0.1:9000/var/www/html/www.example.com/
  <Directory /var/www/html/www.example.com>
     Require all granted
  </Directory>

SSLCertificateFile /etc/letsencrypt/live/www.example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
SSLCertificateChainFile /etc/letsencrypt/live/www.example.com/chain.pem
</VirtualHost>

</IfModule>

这里我们有一个之前创建的虚拟主机的副本,但是让我们加密添加了刚刚创建的 SSL 证书细节。这样做,它将重启 Apache 服务器,我们可以访问https://版本(图 11-18 )。

A185439_2_En_11_Fig18_HTML.jpg

图 11-18。

We have our certificates.

前面说过,我们无法用 www.example.com 来测试真实,所以你可以看到假装的网址(图 11-19 )。

A185439_2_En_11_Fig19_HTML.jpg

图 11-19。

Our fake certificate

staging API 会给我们一些我们可以使用的证书,但是这些证书不会被任何人信任。为了使它成为一个真正可信的证书,我们只需从我们的certbot命令中删除–test-cert并再次添加我们的电子邮件地址,我们将拥有一个工作证书(图 11-20 )。

A185439_2_En_11_Fig20_HTML.jpg

图 11-20。

Our true certificate

$ sudo certbot -d cido.cloud.ownenergy.com.au

其他 Web 应用程序

我们已经向您展示了如何在自己的虚拟主机中安装两个 web 应用程序,这应该会让您对这个过程有一个基本的了解。我们不能在这里包含所有 web 应用程序的安装指南,但是我们可以推荐一些您可能会觉得有用的指南。

MediaWiki 是一个运行维基百科(以及其他站点)的协作工具。可以从 www.mediawiki.org/ 下载。

Moodle 是一个课程或学习管理系统。许多学校用它来向学生提供基于网络的学习。可以从 www.moodle.org/ 下载。

SugarCRM 是客户关系管理(CRM)软件,可以帮助您和您组织的销售人员管理客户和客户关系。可以从 www.sugarcrm.com/ 下载。

Web 缓存

Web 缓存用于通过将缓存的响应返回给多个客户端来加速网页的交付。这可以是内部用户或外部公共客户端,可以使您的 web 服务器不必处理进入您站点的每个请求,从而将它们解放出来,以便更好地处理动态内容生成。

有不同类型的缓存软件和服务。有 Akamai、Cloudflare、CloudFront 和 Fastly 等内容交付网络(CDNs)服务,也有 Varnish、Nginx、Apache 和 Squid-Cache 等软件。

Web 缓存通过使用 HTTP 头来工作。浏览器使用Cache-ControlETag头来确定它所请求的资源何时已经改变并且应该被再次获取。对于图像、JavaScript 和 CSS 之类的文件,您可以设置一个类似于max-age=120Cache-Control头,这意味着浏览器会将一个资源对象在其缓存中存储 120 秒,然后才会认为它过时。过了这段时间,它将再次获取资源。

但是,如果资源在这段时间内没有发生任何变化,那么再次下载它将是一种资源浪费。最好更新缓存版本。浏览器可以通过使用ETag值来确定资源是否已经改变。浏览器通过在请求中设置一个报头IF-NONE-MATCH和它所具有的ETag值,询问 web 服务器具有这个特定ETag的资源是否已经改变。如果已经改变,则再次下载该资源;如果没有,那么 web 服务器发回一个304 Not Modified响应,缓存中的对象被更新 120 秒。

一些组织将在其主 web 服务器前使用 web 缓存服务器。例如,您可能在 Apache 服务器前使用 Varnish 或 Nginx 服务器,或者您可能希望您的全球用户能够从 CDN 中受益,该 CDN 提供了离您的客户端更近的 web 呈现。这取决于您的基础设施和您的网站将如何被使用的性质。如果你一个月只有几千个用户,可能没什么好处。如果你正在向世界各地成千上万的人分发内容,你肯定会想看看一个 CDN。

在我们的示例中,我们将安装一个 web 缓存服务器来帮助减少本地带宽并加快浏览速度。

鱿鱼贮藏库

当您办公室的多个用户开始同时浏览网页时,他们可能会占用大量带宽。为了帮助最小化 web 浏览对其他网络用户的影响,您可以安装缓存或代理服务器。

Linux 上常用的 web 缓存服务器之一是 Squid-Cache。当您的浏览器配置为使用缓存时,Squid 会保存您从网站检索的任何文件的本地缓存副本,因此下次访问相同的文件时,可以从本地 Squid 缓存提供。

Squid 的另一种操作模式是作为反向代理,它位于连接的 web 服务器端的一个或多个 web 服务器的前面。当网站提供大量不经常改变的内容时,使用反向代理。通过允许反向代理缓存不变的数据,可以减少底层 web 服务器上的负载。这种代理也被称为 web 加速器。

配置

鱿鱼由squid包提供。通过 CentOS 上的sudo yum install squid或 Ubuntu 上的sudo aptitude install squid安装。两个发行版对 Squid 使用相同的配置文件,即/etc/squid/squid.conf。因为该文件可能包含敏感信息(如密码),如果不使用sudo,您甚至无法查看该文件。

Ubuntu 上的默认配置文件已经被很好地注释了,所以我们不会对大多数选项进行详细说明。CentOS 上的squid.conf文件已被分条为建议的最低设置。我们将向您展示如何配置 Squid 应该监听的地址和端口,以及如何配置它,以便您的用户实际上被允许通过缓存访问 Web。

默认情况下,Squid 监听所有网络接口上的端口 3128,尽管端口 8080 也很常用。配置文件中修改端口的指令是http_port。此指令允许您指定端口号或地址和端口号的组合。

如果希望 Squid 监听多个地址和端口,可以添加更多的http_port指令。

因为我们在网关主机上设置了 Squid,所以我们不希望它监听所有接口上的连接;不在我们本地网络上的用户应该不能访问它。因此,我们需要添加两行http_port:一行用于无线网络地址范围,一行用于有线网络地址范围。

http_port 192.168.0.254:3128
http_port 192.168.1.254:3128

接下来,我们需要告诉 Squid 允许哪些 IP 地址范围连接到它并访问网站。Squid 为此使用访问控制列表(ACL)。对于您想要控制的每个网络范围,您需要定义一个 ACL。然后创建控制每个 ACL 访问的规则。配置文件包含一些基本的 ACL,我们已经将它们包含在清单 11-9 中。

acl all src 0.0.0.0/0.0.0.0
acl localhost src 127.0.0.1/255.255.255.255
acl to_localhost dst 127.0.0.0/8
Listing 11-9.ACL Definitions

acl指令告诉 Squid 该行的其余部分是 ACL 定义。接下来,我们给 ACL 一个标签,以便以后引用。然后,我们指定要创建的 ACL 类型。Squid 支持许多类型,但是我们将只使用srcdstport,它们控制我们是处理源地址、目的地址还是端口号。最后,我们使用字符串定义源、目的地或端口。要获得 ACL 类型的完整列表,您可以阅读 Squid 网站上的 ACL 部分,网址为 www.squid-cache.org/Versions/v2/2.6/cfgman/acl.html

在我们的例子中,我们定义了一个名为all的 ACL,它包含了所有的互联网地址。下一个 ACL 允许我们将源自本地网络接口的所有流量称为 localhost。第三个执行相同的操作,但用于本地网络接口的流量。

让我们为我们的网络地址添加一些 ACL。我们可以将这些直接添加到定义to_localhost的行下面。

acl wired src 192.168.0.0/255.255.255.0
acl wireless src 192.168.1.0/255.255.255.0

我们定义了两个新的 ACL,一个用于有线网络范围,一个用于无线范围。我们可以通过简单地为两个 ACL 指定相同的标签来合并它们。然而,给他们不同的名字意味着我们可以给他们不同级别的访问权限,如果我们想的话。

另一个可能有用的配置选项是设置允许来自任何特定网络或区域的访问的时间。

acl wireless_hours time M T W T F 8:30-17:30

Squid 包含一个名为Safe_ports的 ACL,其中包含 web 和 FTP 流量的常用端口。这样,您可以控制希望本地用户能够连接到远程服务器上的哪些端口。如果您需要访问的站点运行在非标准端口上,您可以将端口号添加到Safe_ports ACL,从而允许浏览器连接到它。

acl Safe_ports port 80  # http
acl Safe_ports port 21     # ftp
acl Safe_ports port 443 # https

既然我们已经定义了我们需要的所有 ACL,我们可以通过为这些 ACL 定义访问规则来完成我们的配置。Squid 使用http_access指令来确定是否允许给定的 ACL 使用缓存。这个指令有两个参数,一个动作和一个 ACL。这些指令按顺序处理,当规则匹配时,处理停止。

第一条规则防止浏览器连接到我们没有在Safe_ports ACL 中明确列出的端口。

http_access deny !Safe_ports

接下来,允许 localhost 连接,拒绝所有其他连接。

http_access allow localhost
http_access deny all

如果没有http_access规则匹配,则应用与最后看到的动作相反的动作。这就是为什么总是把http_access deny all留在原位作为最终规则是很重要的;这意味着 Squid 将拒绝访问,除非它遇到允许访问的规则。

我们应该在它们之间插入新的规则,如清单 11-10 所示。

http_access allow localhost
http_access allow wired
http_access allow wireless wireless_hours
http_access deny all
Listing 11-10.Granting Access for Our Networks

我们将添加一些日志记录来帮助跟踪我们的配置中的任何问题。为此,我们添加了access_log指令。日志将被写入/var/log/squid

access_log daemon:/var/log/squid/access.log squid

这将为我们的访问日志提供一些日志详细信息,如下例所示:

1471953526.429    934 192.168.0.1 TCP_MISS/200 10265 GET http://blahblah.com/ - HIER_DIRECT/64.207.180.61 text/html
1471953526.961    493 192.168.0.1 TCP_MISS/200 19834 GET http://blahblah.com/blah.jpg - HIER_DIRECT/64.207.180.61 image/jpeg
...
1471953633.942    389 192.168.0.1 TCP_REFRESH_UNMODIFIED/304 305 GET http://blahblah.com/ - HIER_DIRECT/64.207.180.61 –

在这个访问日志的例子中,您可以看到我们首先获得了我们所追求的资源,然后我们可以看到一个后续的请求返回一个 304 未修改状态。

最后,我们可以使用cache_dir指令更改磁盘上 Squid 用来存储其缓存对象的目录。这被注释掉了,这意味着它将使用内置的默认值,如下所示:

# cache_dir ufs /var/spool/squid 100 16 256

前面一行告诉 Squid 以ufs格式将缓存对象存储在/var/spool/squid目录下。它将存储最多 100Mb 的对象;之后,旧对象将从缓存中过期,并被新对象替换。最后两个数字控制 Squid 将在主缓存目录中创建多少个子目录。在这种情况下,它将创建 16 个主子目录,每个子目录包含另外 256 个子目录。

这个子目录模式的出现是因为大多数文件系统在访问包含大量文件的目录时非常慢。例如,如果每个 Squid 子目录包含大约 100 个缓存文件,缓存中的文件总数将超过 400,000。如果它们都存储在单个目录中,每次浏览器请求这些文件中的一个时,主机将需要在目录 inode 中搜索多达 400,000 个条目,以找出它需要的数据存储在哪里。通过细分缓存,对于任何给定的文件,需要搜索的索引节点的数量都要少得多,从而减少了缓存增长时的性能下降。

由于磁盘空间通常比带宽便宜,我们将允许我们的缓存变得更大。

cache_dir ufs /var/spool/squid 2000 16 256

我们保存配置文件并启动 Squid。在 CentOS 上,我们应该首先通过systemctl创建启动链接。

$ sudo systemctl enable squid

我们现在可以通过 CentOS 上的sudo systemctl start squid启动 Squid,或者通过sudo service squid restart在 Ubuntu 上重启它。在 CentOS 上,我们指定的缓存目录将在 Squid 首次启动时创建。当我们安装这个包的时候,这已经在 Ubuntu 上发生了。

客户端配置

剩下我们要做的就是配置 web 浏览器来使用代理。在 Chrome 中,我们选择设置➤网络➤更改代理设置。在这台苹果 Mac 主机上,我们可以如图 11-21 所示为仅http://流量配置代理服务。

A185439_2_En_11_Fig21_HTML.jpg

图 11-21。

Setting a proxy server in Chrome

如果我们愿意,我们也可以配置https://的流量。然后我们关闭网络和 Chrome 设置,访问我们最喜欢的网站。我们可以通过查看 Squid 访问日志来验证代理正在被使用。

透明度

如果您不想让您的用户为了使用 Squid 缓存而更改他们的代理设置,您可以将 Squid 作为透明代理来运行。透明代理通过防火墙规则将所有出站 web 流量重定向到它。访问 web 的浏览器不知道他们正在使用缓存,并且您不需要显式配置任何 Web 浏览器来使用它。

Note

如果您希望对代理服务使用身份验证,您将无法使用透明代理。客户端认为它是在直接与原始服务器对话。

要将我们当前的配置转换成透明代理,我们必须对配置文件做两个小的修改。对于每个http_port指令,我们需要添加选项transparent

http_port 192.168.0.254:3128 transparent
http_port 192.168.1.254:3128 transparent

重启 Squid 后,我们在网关主机上添加防火墙规则。这些规则应该拦截到远程网站的所有连接,并将它们重定向到我们的代理。我们希望在网关将数据包发送到互联网之前,更改数据包的目的地址和端口号。这是网络地址转换(NAT)的一种形式,由 Netfilter 在 NAT 表中完成。命令firewall-cmd可再次用于此目的。

Note

我们将在第七章中介绍防火墙。

要更改数据包的目的地地址,我们需要创建一个DNAT目标,并破坏 HTTP 出站流量。首先确保我们伪装我们的联系。然后,我们将进入防火墙端口 80 的流量转发到端口 3128,Squid 将在那里等待。

$ sudo firewall-cmd --permanent --zone=public --add-masquerade
$ sudo firewall-cmd --permanent --zone=public --add-forward-port=port=80:proto=tcp:toport=3128:to_addr=192.168.0.254
$ sudo firewall-cmd --reload

然后,我们重新加载防火墙,使这些更改永久生效。

Caution

如果您的透明代理主机不是网关主机,您应该确保它可以直接访问远程网站,而无需重定向到自身。

摘要

在本章中,我们向您展示了如何使用您的 Linux 主机作为具有 SQL 支持的灵活的 web 服务器,以及如何通过安装 web 服务来利用这一点。现在,您应该能够执行以下操作:

  • 创建和管理虚拟网站
  • 通过模块向 Apache 添加功能
  • 基于主机名或用户名和密码控制对站点的访问
  • 创建和管理 MariaDB 数据库和用户
  • 安装和配置第三方 web 应用程序
  • 配置 web 代理以节省带宽成本并提高速度

在下一章,我们将看看如何设置我们的邮件服务器。

Footnotes 1

http://news.netcraft.com/archives/web_server_survey.html

十二、邮件服务

部署 Linux 主机的一个最常见的原因是提供邮件服务,包括通过 Internet 消息访问协议(IMAP)和邮局协议(POP3)等机制接收和发送电子邮件以及检索电子邮件。在本章中,我们将简要解释电子邮件的工作原理,并向您介绍电子邮件解决方案的组成部分,包括

  • 邮件传输代理(MTA):发送和接收电子邮件的服务器
  • 邮件用户代理(mua):用户通过其发送和接收电子邮件的客户端
  • 邮件传递代理(MDA):帮助您将电子邮件传递到邮箱的工具

我们还将向您介绍执行这些功能的应用程序:

  • Postfix:简单邮件传输协议(SMTP)电子邮件服务器
  • Dovecot:一个 IMAP 和一个 POP3 服务器

Postfix 电子邮件服务器将允许您的用户发送和接收来自内部用户和外部(如来自互联网)的电子邮件。Dovecot 服务器提供 IMAP 和 POP3 守护程序。IMAP 和 POP3 是用户从电子邮件服务器上的邮箱中检索电子邮件的两种不同方式(我们将解释这些区别以及为什么您可能会使用其中一种而不是另一种)。

我们还将向您展示如何保护您的用户免受不请自来的电子邮件或垃圾邮件以及病毒的侵害。

在本章中,我们将解释基本的邮件服务——发送、接收和管理电子邮件。许多用户现在对电子邮件服务器有了更多的期望,如日历、消息传递,甚至文档管理。他们希望这些功能能够在不同的设备上无缝运行。这些协作服务可以在经过不同程度的改进后的 Linux 下获得。有些是专有许可的,有些是 GPL 或其变体。有些将提供一个基本的服务,有些将对每个用户收取少量费用,有些将是免费的,除了某些专有模块。我们将在本章末尾讨论其中的一些。

电子邮件是如何工作的?

电子邮件在个人和商业交流中仍然无处不在。虽然社交媒体现在也用于交流,但电子邮件仍然是大多数公司的核心。大多数人不需要担心撰写电子邮件和点击发送按钮之外的事情。然而,要运行你自己的邮件服务器,你需要对电子邮件的内部工作原理有更多的了解。在图 12-1 中,您可以看到典型的电子邮件生命周期。

A185439_2_En_12_Fig1_HTML.jpg

图 12-1。

E-mail life cycle

当你发送电子邮件时会发生什么?

电子邮件基于一种叫做 SMTP 的协议(定义于 RFC 5321 www.ietf.org/rfc/rfc5321.txt )。每封电子邮件都有一系列的标题,告诉您的邮件服务器如何处理它以及将它发送到哪里。因此,当用户创建一个新的电子邮件时,他们会注明该电子邮件的地址。他们添加一个收件人(或收件人字段),并且可能将电子邮件“抄送”或“密件抄送”给其他人。然后他们发送电子邮件。

用户的电子邮件客户端配置了一个服务器,您的客户端现在联系这个服务器(在 TCP 端口 25 上)并说“Hello!我有这个人给这些人的邮件—请把它发给他们!”实际上,我们的客户端并没有准确地说“你好”,它说了一些非常接近的东西——一个名为EHLO(或者更罕见的是HELO)的命令。

Note

电子邮件服务器有很多名字。它们可以被称为 SMTP 服务器或 SMTP 守护程序,但它们的正确名称是邮件传输代理。发送电子邮件的客户端在此术语中称为邮件用户代理。在本章的后面,我们还将了解另一个组件,称为邮件传递代理,它可以用来将电子邮件传递到用户的邮箱。

EHLO是 SMTP 命令,是 SMTP“语言”的一部分命令是 SMTP 客户端和服务器(或 mua 和 MTA)相互通信的方式。最初,SMTP 语言中只有大约十个单词或命令。最近,一个名为扩展 SMTP (ESMTP)的 SMTP 增强版本被开发出来,它向该语言添加了许多更有用的命令,以提供诸如身份验证和加密等功能。

Note

我们之前提到的HELO命令是EHLO命令的旧形式。现在已经很少使用它了,但是所有性能良好的邮件服务器都应该支持它,作为万一你的客户端老了,认不出EHLO时的后备。

我们现在将使用 netcat ( nc)命令连接到一个邮件服务器。我们将使用nc向您展示邮件服务器在发送电子邮件时相互发送的实际命令。为此,我们将发布$ nc mail.example.com 25。在这种情况下,客户机告诉服务器它是谁,在清单 12-1 中,您可以看到用 SMTP 的“语言”进行的简单对话

220 mail.example.com ESMTP Server
EHLO client.example.com
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
Listing 12-1.A Simple SMTP Conversation

当您启动到邮件服务器的连接时,它应该用状态代码 220 及其名称和功能来响应。清单 12-1 中的第一行是服务器告诉客户机它是谁,mail.example.com,它是一个支持 ESMTP 的 SMTP 服务器。

Note

您可以看到该行以格式为2xx的数字开始。这是一个响应代码,客户端和服务器交换该代码以指示成功或失败。如果该行以数字5xx开头,您的客户机和服务器就会知道这是某种错误代码。稍后您会看到更多这些数字。

在下一行,我们输入了命令EHLO client.example.com。当客户端打招呼告诉服务器是谁的时候,应该提供一个像client.example.com这样的全限定域名。根本没有验证这是否是合法的主机名,所以它可以是任何东西(不幸的是,这就是大量垃圾邮件的传送方式——稍后将详细介绍)。最后,服务器做出响应,确认连接(状态代码 250),并返回一个功能列表。

接下来,我们将通过该服务器向example.com域中的用户发送一封电子邮件。这从发送发件人和收件人的详细信息开始,也就是通常所说的信封。

MAIL FROM: <ataylor@example.com>
250 2.1.0 Ok

这里,发送者ataylor@example.com使用了MAIL FROM命令。服务器进行检查,返回 250 响应代码,并指示允许该发件人提交电子邮件。这种接受可以基于许多标准,包括一个正确构造的电子邮件地址,我们将在本章的后面讨论像身份验证这样的机制,但是仍然很少验证这个发送地址是否合法。

接下来,服务器期待RCPT TO命令,或者电子邮件发送给谁。

RCPT TO: <jsmith@example.com>
250 2.1.5 Ok

在这里,地址的接受取决于一些标准,例如具有格式正确的电子邮件地址,以及该服务器被配置为代表example.com域接受邮件。如果此邮件服务器配置不当,并且接受不受其控制的域的邮件,则称为开放中继。

接下来,在我们的简单示例中,我们需要实际电子邮件的内容。客户端向服务器发送一个名为DATA的命令。

DATA

354 End data with <CR><LF>.<CR><LF>

Message-ID:

Date: Mon, 17 Aug 2016 12:29:26 +1100

From: Anne Taylor ataylor@example.com

To: John Smith jsmith@example.com

Subject: Email is cool

This is an email message.

.

250 2.0.0 Ok: queued as DF44644A9

服务器的响应是请求电子邮件的内容,然后在一行中单独添加一个句点或句号标记,表示电子邮件的结束。

您可以看到我们传递了一些相当默认的标题,如日期、收件人和发件人标题、主题和电子邮件的内容。我们已经指定了.标记,服务器已经响应说它已经接受了这封电子邮件。然后,每封电子邮件都被提交到邮件队列,由服务器进行处理和发送。

您现在可以通过重复MAIL FROM命令继续发送电子邮件,或者使用QUIT命令断开与服务器的连接。

QUIT
221 2.0.0 Bye

这是一个发送电子邮件的简单场景。这是最基本的命令交换。当您考虑加密和身份验证等因素时,大多数正常的电子邮件发送会稍微复杂一些。

E-mail Addresses

那么什么是可以接受的电子邮件地址呢?什么是格式正确的电子邮件地址?电子邮件地址最基本的形式是用户名和主机名、域名或完全限定的域名,由一个@符号分隔,例如jsmith@example.com。关于允许使用哪些字符以及电子邮件地址的适当结构的规则常常令人困惑。电子邮件地址可以采用多种形式,不同的电子邮件服务器或 MTA 接受不同的有效格式。我们将向您展示的 MTA Postfix 接受各种格式的电子邮件地址。Postfix 的电子邮件地址重写指南展示了一些被接受的电子邮件地址格式: www.postfix.org/ADDRESS_REWRITING_README.html

发送电子邮件后会发生什么?

在服务器(或 MTA)收到来自客户机的电子邮件并将其放入邮件队列后,一组全新的命令和步骤将被执行。首先,服务器需要找到发送电子邮件的目的地。为此,服务器将电子邮件地址的一部分放在@符号的右边。这通常是完全限定的域名,例如example.com。然后,电子邮件服务器使用域名系统(DNS)查询来联系远程域,并询问它将电子邮件发送到哪里。

Note

我们在第十章讨论了 DNS。

电子邮件服务器通过查询一种称为 MX 记录的特殊 DNS 记录来实现这一点。查询 MX 记录会返回一个或多个条目,告诉您的电子邮件服务器将电子邮件发送到哪里,通常是一个特定的主机或 IP 地址。如果返回了多个电子邮件服务器,还会返回一个优先级,告诉您的电子邮件服务器首先使用哪个条目,然后再使用哪个条目,依此类推。在本章的后面,我们将展示如何配置我们的 DNS MX 记录。

Note

如果 DNS 查询表明没有 MX 记录,您的电子邮件服务器将无法传递该电子邮件,并将向您发送一封表明这一点的邮件。如果用户键入错误或指定了不正确的电子邮件地址,这种情况经常会发生。

然后,电子邮件服务器将您的待发电子邮件提交到另一个队列,并从那里发送到目标电子邮件服务器。为此,您的电子邮件服务器会尝试通过 TCP 端口 25 或 TCP 465 的安全传输端口,按照记录指定的优先级顺序连接到 MX 查询返回的每个电子邮件服务器。然后,电子邮件服务器会按照提交顺序查看是否可以发送您的电子邮件。

  1. 如果电子邮件服务器响应,它将尝试提交电子邮件。
  2. 如果电子邮件服务器没有响应,您的电子邮件服务器将按顺序尝试从 MX 记录返回的下一个服务器。
  3. 如果没有电子邮件服务器响应,您的电子邮件服务器通常会将您的电子邮件排队,稍后再试。
  4. 如果在连续失败后,电子邮件仍然无法发送,电子邮件服务器将通过电子邮件向用户报告失败。

您的电子邮件服务器试图发送的目的地服务器可能是您的电子邮件的最终目的地,或者它可能只是一个网关,您的电子邮件通过它到达一个或多个更远的电子邮件服务器,直到它最终到达它的目的地。这取决于目的地如何配置其电子邮件环境。许多环境都有接收邮件的面向 Internet 的电子邮件网关,然后有处理内部邮件的内部电子邮件服务器。此配置允许在您的电子邮件网关上使用与内部服务器不同的功能,如垃圾邮件和病毒过滤。然而,正如我们所说的,一个正确配置的电子邮件服务器应该被设置为只接受其控制下的域的邮件。

Note

需要注意的是,电子邮件被归类为“尽力而为”,没有保证。也就是说,邮件服务器通常会尝试发送 4 个小时的邮件,然后才会放弃。虽然通常邮件会在不到一分钟的时间内送达世界各地,但这是无法保证的。

配置电子邮件

我们将向您展示如何创建一个基本的电子邮件服务器配置,允许您发送和接收电子邮件,并帮助保护您的用户免受垃圾邮件、病毒和恶意软件的侵害。我们还将利用传输层安全性(TLS),这是一种加密形式,可用于加密您的电子邮件,并讨论 SMTP AUTH,这是一种在用户发送电子邮件时对其进行身份验证的方法。

我们将使用headoffice.example.com主机作为我们的电子邮件服务器,我们的gateway.example.com将电子邮件流量传递到该主机,正如我们在第七章的示例网络配置中所描述的。

我们的邮件服务器将被称为mail.example.com(这是我们在第十章中创建的一个 DNS CNAME)并且有一个内部 IP 地址 192.168.0.1。你可以在图 12-2 中看到我们的示例网络。

A185439_2_En_12_Fig2_HTML.jpg

图 12-2。

Example network

装置

我们将从安装我们需要的包开始 Postfix 和 Dovecot 应用程序。

CentOS 装置

在 CentOS 的更高版本中,默认 MTA 是 Postfix,并作为最小安装的一部分安装。另一种常见的 MTA 叫做 Sendmail。我们将使用后缀 MTA,它更易于理解、配置和故障排除。

Note

总是有很多关于哪个 MTA 最好的争论,有很多可用的,包括 Sendmail、Postfix、Exim 等等。我们的建议是从 Postfix 开始,看看效果如何。我们认为你会发现 Postfix 功能强大且易于使用,它能满足你的所有需求。

首先,我们来看看 Postfix 和 Dovecot 是否已经安装。

$ sudo rpm -q postfix dovecot
postfix-2.10.1-6.el7.x86_64
package dovecot is not installed

如果一个或多个软件包没有安装,我们需要安装它们。我们在这里使用了yum命令:

$ sudo yum install -y postfix dovecot

Note

一些附加的必备软件包也可以通过这两个命令安装。我们还将在本章的后面安装一些额外的包来提供对其他功能的支持。

Ubuntu 安装

后缀 MTA 也是 Ubuntu 发行版的默认 MTA,所以它通常已经安装了。事实上,你在第二章中看到,我们的 Ubuntu 安装提示我们执行一些基本的 MTA 配置。我们将很快再次访问配置对话框(参见“编辑后缀配置”侧栏)。

如果没有安装 Postfix MTA,我们希望安装 Postfix 和 Dovecot 软件包。让我们先检查一下它们是否已安装。

$ sudo aptitude show postfix
Package: postfix
State: installed
...
$ sudo aptitude show dovecot-core
Package: dovecot-core
State: installed
...

这里我们已经检查了是否安装了postfix包和dovecot-core包。如果两者都安装了,您就已经拥有了所需的东西。

如果没有安装,可以使用aptitude命令安装。

$ sudo aptitude install postfix dovecot-core dovecot-imapd dovecot-pop3d

开始后缀

安装后,使用我们在第六章中介绍的服务管理工具启动 Postfix 很容易。对于 CentOS 和 Ubuntu,使用systemctl命令启动 Postfix。

$ sudo systemctl start postfix.service

Tip

在进行配置更改后重新加载或重启 Postfix 也很重要,你会看到我们在整章中都提到了这一点。如果您无法找出某些东西不工作的原因,重启通常是故障诊断的良好开端。

您可以通过检查日志文件来确认 Postfix 已启动。Postfix 将其输出记录到系统日志或 syslog 中,后者依次记录到位于您主机上的文件中。您可以在/var/log目录中找到它的日志文件。在 CentOS 上,我们需要查看/var/log/maillog file。在 Ubuntu 上,Postfix 将所有日志消息发送给/var/log/mail.log。在 Ubuntu 上,错误和警告信息也会被分别记录到/var/log/mail.err/var/log/mail.warn文件中。监控这些文件中的错误消息是一个好主意。一个很好的方法是使用-f选项的tail命令,它实时监控文件,并在新的日志消息添加到文件中时向下滚动屏幕。

$ sudo tail –f /var/log/maillog

当 Postfix 成功启动时,您应该会看到以下日志消息:

Jul 10 03:21:34 au-mel-centos-1 postfix/postfix-script[4500]: starting the Postfix mail system
Jul 10 03:21:34 au-mel-centos-1 postfix/master[4502]: daemon started -- version 2.10.1, configuration /etc/postfix

了解后缀配置

大部分后缀配置由位于/etc/postfix目录中的两个文件处理。这些文件被称为main.cfmaster.cfmain.cf文件包含您可以用来定制 Postfix 的主要配置选项的子集。您可以向该文件添加配置 Postfix 所需的任何附加选项。master.cf文件控制客户端如何连接到您的服务器,以及组成服务器的服务如何配置。大多数时候你在master.cf档不会有太大的改变。

让我们先快速看一下每个文件的结构。

Note

大多数发行版安装预配置的main.cfmaster.cf文件。这些通常包括描述每个选项的大量内嵌文档。

main.cf中,每个选项的结构如下:

option = value

选项不必按任何顺序排列,空行会被忽略。

Note

后缀配置文件是 INI 格式,带有option = value。像大多数 Linux 配置文件一样,任何以#开头的行都是注释。

在清单 12-2 中,您可以看到一个典型的main.cf文件的样本。

...
# LOCAL PATHNAME INFORMATION
#
# The queue_directory specifies the location of the Postfix queue.
# This is also the root directory of Postfix daemons that run chrooted.
# See the files in examples/chroot-setup for setting up Postfix chroot
# environments on different UNIX systems.
#
queue_directory = /var/spool/postfix
...
Listing 12-2.The main.cf File

我们将在本章中讨论main.cf文件中的一些配置选项;您可以通过$ man 5 postconfwww.postfix.org/postconf.5.html 查看完整列表。

master.cf文件中,您可以看到可以在 Postfix 中启用和配置的守护进程、服务和进程的结构化列表。在清单 12-3 中,您可以看到这个文件中的一些示例行。

...
# ==========================================================================
# service        type         private         unpriv           chroot        wakeup       maxproc        command + args
#                    (yes)          (yes)           (yes)             (never)        (100)
# ==========================================================================
smtp              inet                 n             -                  n                 -                  -                 smtpd
...
submission        inet                 n                 -                    n            -                  -                smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=n
...
Listing 12-3.The master.cf File

这里定义了两个服务,smtpsubmission。每个服务通常都是后台运行的守护进程,执行特定的功能。

Note

与许多其他服务不同,Postfix 不是一个单一的守护进程,而是一些小进程的集合,它们执行各自的功能并相互通信。因此,当您启动 Postfix 时,您可能会看到多个进程启动,而不是单个进程。

在这种情况下,smtp服务是在 TCP 端口 25 上为您的主机接收邮件的基本 SMTP 服务,而submission服务是在 TCP 端口 567 上侦听的替代服务,有时用于接收来自内部邮件客户端(mua)的电子邮件。

接下来,我们定义服务的类型;这里inet将这些定义为基于网络的服务(即,在您的网络接口上运行的服务)。其他可用的服务类型包括本地 Unix 套接字和命名管道。Unix 域套接字和命名管道用于在服务之间发送消息;参见 http://en.wikipedia.org/wiki/Unix_domain_sockethttp://en.wikipedia.org/wiki/Named_pipe

接下来,我们对每个服务进行一系列设置。每个设置都在一行中按顺序指定,并用空格分隔。对于您的基本配置,您不需要担心这些设置,但您可以在$ man 4 masterwww.postfix.org/master.5.html 找到更多详细信息。

最后,每个服务都需要指定启动服务的命令,以及传递给该命令的任何参数。在清单 12-3 中,两个服务都是用命令smtpd启动的,但是submission服务有几个传递给命令的选项。这些选项与main.cf配置文件中的选项相同,它允许我们以不同于主配置的方式配置特定的服务。我们将在本章后面的“抗击病毒和垃圾邮件”一节中讨论如何将这些选项传递给单个服务。

Editing Postfix Configuration

有三种方法可以编辑您的 Postfix 配置文件,尤其是main.cf文件。

  • 您可以使用postconf命令。
  • 在 Ubuntu 上,dpkg-reconfigure命令可以进行基本的后缀配置。
  • 您可以使用文本编辑器,如vimgedit

我们通常推荐最后一个选项,使用文本编辑器来编辑您的配置文件,但是首先让我们快速地看一下另外两个选项。该文件可能很长,您可能会误以为同一个选项有两个值。在这种情况下,最后一个值将获胜,您可能会花几个小时挠头,想知道为什么它不像您预期的那样工作。

postconf命令是一个特殊的命令,允许您操作main.cf文件并显示您现有的配置。该命令还有一些用于调试和显示配置的有用的命令行选项。例如,要显示任何与默认配置不同的配置选项(即针对您的主机进行了修改),您可以使用–n配置标志。

$ sudo postconf -n

如果您正在寻找故障排除,通常会要求您提供postconf -n命令的输出。您还可以使用–d标志显示每个配置选项及其默认设置。最后,您可以使用命令通过-e选项编辑您的配置文件,如下所示:

$ sudo postconf -e 'inet_interfaces = all'

这里的inet_interfaces选项(控制 Postfix 绑定到哪些网络接口)已经被设置为值all

在 Ubuntu 上,可以使用dpkg-reconfigure命令来配置 Postfix 的基本状态。

$ sudo dpkg-reconfigure postfix

此命令为您提供了一系列基本配置模型,例如,发送和接收电子邮件的基本 Internet 主机。另一种型号使用智能主机;在这种情况下,电子邮件被发送到中间中继服务器。这种模式通常用于 Internet 服务提供商(ISP)环境,在这种环境中,您有时会受到限制,无法直接通过端口 25 发送和接收电子邮件。这旨在通过防止从 xDSL 或有线网络中受损的桌面主机发送和转发电子邮件来减少垃圾邮件数量。我们将在本章后面的“智能主机”边栏中讨论智能主机。

该实用程序还设置了许多其他配置选项。但是,我们不建议您使用该实用程序;相反,您应该使用文本编辑器手动编辑您的配置文件,这样您会更加熟悉所有选项。

永远记住,在任何 Postfix 配置更改之后,您必须重新加载或重启postfix服务。

初始配置

尽管有看起来相当复杂的配置过程和选项,Postfix 实际上很容易配置。一个简单的电子邮件服务器,设计用于发送和接收域的电子邮件,可以在几分钟内建立起来。

第一步是告诉 Postfix 应该处理哪些域的邮件。为此,我们通过添加我们的域来更新main.cf配置文件中的mydestination配置选项,在本例中是example.com。我们可以通过直接编辑main.cf文件并更改选项来进行更改,如下所示:

mydestination = mail.example.com, localhost.localdomain, localhost, example.com

我们也可以使用postconf命令来改变它。

$ sudo postconf -e "mydestination = mail.example.com, localhost.localdomain,
localhost, example.com"

在前一行,我们已经添加了两个项目:我们的邮件服务器和我们的本地域。Postfix 现在知道,如果它收到发往这些地址之一的邮件,例如,ataylor@mail.example.comataylor@example.com,它应该接受并处理这封电子邮件。

Note

Postfix 将只接收它知道的用户的邮件。通常,这将是在主机上创建的用户,但是我们将在本章后面的“虚拟域和用户”一节中简要地看一下“虚拟”用户。在某些情况下,Postfix 知道其他电子邮件服务器上的用户,并可以配置为将邮件转发到另一台主机。但是一般来说,如果 Postfix 找不到电子邮件的收件人(例如,如果一封电子邮件被发送到bjones@example.com并且 Postfix 不知道这个用户),该电子邮件将被拒绝。

当我们希望 Postfix 接收其他域的电子邮件时(例如,我们也希望接收来自example.net域的电子邮件),我们可以将该域添加到mydestination选项中。

mydestination = mail.example.com, localhost.localdomain, localhost,
example.com, example.net

其他条目通常已经存在,包括localhost,它告诉 Postfix 处理主机本地的电子邮件,例如本地进程发送的电子邮件。也正是这种设置阻止了你的邮件像我们之前谈到的那样成为一个开放的中继。邮件服务器将只接受为此处列出的域配置的邮件。如果您的域名未在mydestination中列出,当您向用户发送邮件时,您将会收到如下内容:

RCPT TO: <bobby@unlisted.org>
554 5.7.1 <bobby@unlisted.org>: Relay access denied

接下来,我们需要将本地网络添加到mynetworks配置选项中。这告诉 Postfix SMTP 客户端处理电子邮件需要哪些可信的 IP 地址范围。允许受信任的 SMTP 客户端通过我们的电子邮件服务器中继(向其他电子邮件服务器发送电子邮件)。在我们的例子中,我们只关心 192.168.0.0/24 和 192.168.1.0/24 范围,这是我们的本地有线和无线网络范围。127.0.0.1 或 localhost 地址应该已经存在于选项中。

mynetworks = 127.0.0.0/8, 192.168.0.0/24, 192.168.1.0/24

如果我们有外部用户需要从其他网络访问我们的电子邮件服务器,他们需要在被允许中继之前进行身份验证。我们可以再次使用postconf命令来编辑选项。

$ sudo postconf -e "mynetworks = 127.0.0.0/8, 192.168.0.0/24, 192.168.1.0/24"

现在我们需要将 Postfix 绑定到我们希望它监听的网络接口。在这种情况下,我们将绑定到所有网络接口,这是默认设置。

inet_interfaces = all

或者我们可以使用postconf命令。

$ sudo postconf –e "inet_interfaces = all"

但是,您可以更有选择性,如果您想将 Postfix 绑定为只在单个接口上侦听,您可以按如下方式进行:

inet_interfaces = enp0s3

Tip

有一个名为inet_protocols的设置,它控制 Postfix 在建立或接受连接时将尝试使用哪个 IP 版本。默认设置为all,但您可以根据需要指定ipv6ipv4。在 CentOS 上,将all设置为inet_protocol也将成为我们在 IPv6 上监听的默认接口,这意味着我们的电子邮件服务器需要支持 IPv6 以避免错误。

最后,在做了所有相关的修改后,我们需要重启 Postfix。我们可以发布以下内容:

$ sudo systemctl reload postfix.service

Note

每次更改 Postfix 配置选项时,都应该重新加载或重启 Postfix。

除了重新启动 Postfix,您还需要确保主机上的 TCP 端口 25 是打开的,以允许传入的连接。如果您运行了iptables防火墙,您将需要创建适当的规则来允许访问。这里有一个例子:

$ sudo iptables -A INPUT -p tcp -m conntrack --ctstate NEW -m tcp --dport 25 -j ACCEPT

Tip

我们在第七章中讨论了iptables防火墙、编写规则和开放端口。
我们现在应该在第十章中设置的 DNS 服务器中配置我们的 MX 记录。请记住,MX 记录是邮件交换的缩写,当您的 MTA 服务器向另一个域发送邮件时,它会查找该记录。

首先,我们需要冻结从主 DNS 服务器上的 DHCP 等服务到我们的example.com区域的任何nsupdate(名称服务器更新)。

$ sudo rndc freeze example.com

现在打开区域文件,将序列号增加到适当的日期。要添加 MX 记录,我们将在我们的example.com.db文件中的 NS 记录后添加一个,如下所示:

       IN    NS         ns.example.com.
       IN    MX  10  mail.example.com.

我们有记录的名称、互联网类型(IN)、记录类型(MX)、邮件服务器优先级(10,以及它映射到的主机(mail.example.com)。保存区域文件并退出。

接下来,我们需要再次解冻该区域。

$ sudo rndc thaw example.com

最后,让我们验证一下使用dig命令所做的更改。

$ dig -t MX example.com @192.168.0.1
;; ANSWER SECTION:
example.com. 86400 IN MX 10 mail.example.com.

看起来像我们期望的那样。我们准备开始测试。

测试后缀

我们现在可以通过给自己发送一封电子邮件来测试 Postfix 是否正常工作。就像我们在本章开始时所做的那样,我们将通过名为 netcat ( nc)的有用工具来完成这项工作。正如我们所见,nc命令是网络工具的瑞士军刀,可用于创建和操作传输控制协议(TCP)和用户数据报协议(UDP)连接。

Note

还有其他发送测试电子邮件的方法(例如,从命令行使用mail命令),但是nc命令允许您查看 SMTP 命令,并在命令行上显示任何可以看到的错误消息。

让我们再次使用nc命令编写与 Postfix 电子邮件服务器的会话,如清单 12-4 所示。

$ nc mail.example.com 25
220 mail.example.org ESMTP Postfix
ehlo example.com
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
mail from: jsmith@example.com
250 2.1.0 Ok
rcpt to: ataylor@example.com
250 2.1.5 Ok
data
354 End data with <CR><LF>.<CR><LF>
Subject: My first mail for my domain
This is a test.
Thanks
Mr Testing
.
250 2.0.0 Ok: queued as 61A703FA5E
quit
221 2.0.0 Bye
Listing 12-4.A Scripted E-mail Session with nc

在清单 12-4 中,我们首先指定了nc命令、我们想要连接的电子邮件服务器以及我们想要连接的端口 25。接下来,我们逐步完成了 SMTP 命令(这次是小写的;这些命令是不区分大小写的)。在清单 12-4 中,所有粗体文本代表我们输入的命令,您需要输入这些命令来发送您的电子邮件。调整文本以适应您的环境,例如,用您的域名替换example.com。非折叠文本是我们的电子邮件服务器的预期响应。

Note

您可以看到每一行都以 SMTP 响应代码为前缀,例如 250。您可以在 https://www.iana.org/assignments/smtp-enhanced-status-codes/smtp-enhanced-status-codes.xml#smtp-enhanced-status-codes-3 查看这些代码的完整列表。

我们用EHLO命令告诉服务器我们是谁,它用自己的身份和可用特性列表来响应。然后,我们指出我们要向谁发送电子邮件,以及邮件来自谁。在我们的例子中,我们向ataylor@example.com和从jsmith@example.com发送电子邮件。

Note

我们在第五章中创建了这些用户。如果您继续进行,您应该替换本地主机上的用户名,或者创建一些新用户来测试您的电子邮件。

然后,我们告诉我们的服务器,我们正在发送带有DATA命令的电子邮件。我们输入我们的电子邮件,用句号(.)标记它的结尾,然后退出。

Tip

如前所述,从命令行发送本地电子邮件的另一种方式是使用mail命令,可以从mailutils包(Ubuntu)或mailx包(CentOS)中获得。这并没有给我们一个服务器的诊断视图,但是它更容易用来发送邮件。要启动该命令,请在命令行中键入 mail 以及您要发送电子邮件的电子邮件地址,例如 mail root@example.com。单独输入 mail 命令将打开一个简单的命令行邮件客户端。

我们现在可以检查我们的电子邮件是否已经到达。在我们的例子中,我们希望检查用户ataylor是否收到了电子邮件。默认情况下,CentOS 和 Ubuntu 都使用一种叫做 mbox 的邮箱格式来存储收到的电子邮件。mbox 格式将所有的电子邮件保存在一个文件中。Ubuntu 和 CentOS 都将这些文件存储在不同的位置,但都使用符号链接来确保文件出现在相似的位置。

Note

有关 mbox 格式的更多详细信息,请参见 http://en.wikipedia.org/wiki/Mbox

在 Ubuntu 上,会在/var/mail目录下创建一个以用户命名的邮箱文件,例如/var/mail/ataylor。但是对于/var/spool/mail目录,有一个到/var/mail目录的符号链接。

在 CentOS 上,每个用户在/var/spool/mail目录中都有一个 mbox 格式的邮箱文件。文件以用户命名,例如/var/spool/mail/ataylor。同样,/var/mail目录有一个到/var/spool/mail目录的符号链接。

让我们看看清单 12-5 中的一个 mbox 文件。

$ more /var/mail/ataylor
From jsmith@example.com  Sun Jul 10 12:12:46 2016
Return-Path: <jsmith@example.com>
X-Original-To: ataylor@example.com
Delivered-To: ataylor@example.com
Received: from example.com (ns.example.com [192.168.0.254])
    by mail.example.com (Postfix) with ESMTP id 2625C3FA5A
    for <ataylor@example.com>; Sun, 10 Jul 2016 12:12:18 +0000 (UTC)
Subject: My first mail for my domain

This is a test.
Thanks
Mr Testing

Listing 12-5.An mbox File

在清单 12-5 中,您可以看到 mbox 文件包含了我们的电子邮件,包括电子邮件的所有标题和内容。额外的电子邮件将被附加到这个文件。因此,如果您有大量的电子邮件,这些文件会变得难以管理、搜索和备份。

Aliases

当电子邮件到达您的主机时,Postfix 会查看收件人以确定将邮件发送给谁。一些应用程序使用收件人,例如邮局主管地址(例如,来自 MTA 的错误通常被发送到您的域中的用户邮局主管)和root用户。因为人们通常不会以这两个用户中的任何一个登录,所以这封电子邮件可能永远不会被看到。Postfix 使用一种称为别名的功能,允许您将发送给这些收件人的邮件重定向到其他用户。Postfix 在main.cf配置文件中使用一个名为alias_maps的配置选项来指定一个文件,该文件将收件人与实际接收电子邮件的用户进行匹配。默认的别名文件通常是/etc/aliases。在这个文件中,您会发现一个用户列表,如下所示:

user1:user2

在这里,user1的所有邮件将被重定向到user2,每个用户名用冒号隔开。您应该检查这个文件,并确保邮局主管和root用户的电子邮件都指向一个合适的用户。您还可以通过指定外部电子邮件地址向另一台主机上的用户发送电子邮件,如username@example.com。一种常见的模式是将系统用户的所有电子邮件发送给root用户,然后将root用户重定向到需要查看该电子邮件的人,例如:

postmaster: root
operator: root
lp: root
root: ataylor

这里,postmasteroperatorlp用户的邮件都被重定向到root用户,而root用户的邮件又被重定向到用户ataylor

您也可以将一个用户的邮件发送给多个后续用户。如果我们想将根用户的邮件重定向到ataylorjsmith,我们应该做以下事情:

root: ataylor,jsmith

现在,当 root 收到一封电子邮件时,ataylorjsmith都会收到同一封电子邮件。

在对/etc/aliases文件进行任何更改之后,您需要运行一个名为newaliases的命令来更新 Postfix。

$ sudo newaliases

在接下来的“后缀查找表和虚拟域”一节中,我们将深入探讨这是如何工作的。

选择邮箱格式

除了有可能变成一个大而难以处理的文件之外,默认的 mbox 格式还有另一个问题:文件损坏的可能性。例如,如果您的 MTA 正在向您的 mbox 文件发送邮件,同时您的 MUA 或邮件客户端正在删除邮件,则您的 mbox 文件可能会损坏或返回不可预知的结果。

mbox 的替代方法是 Maildir 邮箱格式。Maildir 将你的电子邮件存储在一个目录下的不同文件中,而不是一个单独的文件。这允许多个进程与您的邮箱交互,而没有任何冲突或损坏的风险。备份和恢复也更容易。

Maildir 格式是一个目录,恰当地称为Maildir,包含三个子目录:curnewtmp。您可以在此处看到 Maildir 格式目录的列表:

$ ls -l Maildir
total 168
drwxr-xr-x 2 ataylor ataylor 28672 2009-01-01 13:53 cur
drwxr-xr-x 2 ataylor ataylor  4096 2009-01-01 13:53 new
drwxr-xr-x 2 ataylor ataylor  4096 2009-01-01 13:53 tmp

电子邮件消息首先被传递到tmp目录,并被赋予一个惟一的名称(通常由当前时间、主机名和其他伪随机特征构成)。然后,电子邮件被转移到new目录中,处于“未读”状态。当您的 MUA 或邮件客户端连接到邮箱时,它会检测到new目录中的电子邮件,然后将它们移动到cur目录。

Note

这听起来有点复杂,但它可以确保在从您的邮箱中传递、阅读、发送和删除电子邮件时,电子邮件不会损坏或放错位置。

要使用 Maildir 而不是 mbox,我们需要告诉 Postfix 我们正在使用不同的邮箱格式。我们还将把默认邮箱位置从现有位置/var/mail/var/spool/mail更改为用户的主目录。为了做到这两点,我们将像这样更新一个名为home_mailbox的后缀选项:

home_mailbox = Maildir/

或者使用postconf命令,就像这样:

$ sudo postconf -e "home_mailbox = Maildir/"

home_mailbox选项告诉 Postfix 用户邮箱的位置,相对于用户的主目录,这样Maildir/就转化为/home/ataylor/Maildir

Note

结尾的/很重要。它需要在那里,它告诉 Postfix 这个目录是一个Maildir目录。

我们还需要确认另一个选项mailbox_command为空。mailbox_command选项可以指定外部命令,例如procmailmaildrop等邮件处理工具。这些工具称为 MDA 或邮件过滤器,可以在收到的邮件发送到您的邮箱时对其执行操作。我们将在本章后面的“邮件传递代理和邮件过滤”边栏中详细讨论这些应用程序。所以我们现在将这个选项设置为空值。

mailbox_command =

或者再次使用postconf命令。

$ sudo postconf -e "mailbox_command = "

最后,我们需要重新加载 Postfix 以使所有这些生效,例如:

$ sudo systemctl reload postfix

现在,如果您发送另一封电子邮件,您会发现在收件人的主目录中创建了一个名为Maildir的新目录。在这个目录中有tmpnewcur子目录,在new目录中有你的电子邮件。您可以使用less命令显示该文件的内容,例如:

$ less /home/jsmith/Maildir/new/1468154018.V801I3fa69M48418.mail.example.com
Return-Path: <ataylor@au-mel-centos-1.example.com>
X-Original-To: jsmith@example.com
Delivered-To: jsmith@example.com
Received: from au-mel-centos-1.example.com (ns.example.com [192.168.0.254])
        by mail.example.com (Postfix) with ESMTP id 09F9E3FA66
        for <jsmith@example.com>; Sun, 10 Jul 2016 12:33:38 +0000 (UTC)
Received: by au-mel-centos-1.example.com (Postfix, from userid 1000)
        id 1F5EF61A55; Sun, 10 Jul 2016 08:33:38 -0400 (EDT)
Date: Sun, 10 Jul 2016 08:33:38 -0400
To: jsmith@example.com
Subject: My first email to my new Maildir mailbox
User-Agent: Heirloom mailx 12.5 7/5/10
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Message-Id: <20160710123338.1F5EF61A55@au-mel-centos-1.example.com>
From: ataylor@au-mel-centos-1.example.com (ataylor)

This is also a test.
Thanks
Mr Testing

在这里,您可以看到单个的Maildir文件将包含一封带有所有标题的电子邮件。

Note

每个Maildir必须由它所属的用户拥有。例如,属于用户ataylorMaildir目录必须只有该用户拥有和可写(权限为 0700)。如果需要,您可以使用chownchmod命令分别更改所有权和权限。

虽然在这种情况下,Postfix 自动创建了Maildir目录,但是用一个空的Maildir目录预先填充用户的主目录通常是一个好主意。您可以通过在/etc/skel目录中添加一个空的Maildir目录来实现这一点。您在第五章中看到了/etc/skel目录,并发现其内容被复制到任何新创建用户的主目录中。

在 Ubuntu 上创建一个新的Maildir目录,有一个很有用的命令叫做maildirmake.dovecot,可以自动创建Maildir结构。

$ sudo maildirmake.dovecot /etc/skel/Maildir

该命令还将创建tmpnewcur子目录。

Tip

您还可以在您的Maildir结构中创建文件夹,例如一个Sent文件夹。文件夹可以用来分类和存储电子邮件,以便更容易找到电子邮件。文件夹是以句号(.)为前缀的子目录;一个Sent文件夹将具有通过使用命令maildirmake.dovecot /etc/skel/Maildir/.Sent创建的结构/home/ataylor/Maildir/.Sent。注意避免文件夹名称包含空格,如“我的个人邮件”,因为有时你的邮件客户端可能会混淆。你应该用下划线或破折号将单词连接起来,就像这样:“我的个人邮件。”

在 Ubuntu 上,有另一种可能更简单的方法来创建你的Maildir目录和文件夹。为此,您可以安装一个名为maildrop的包,其中包含maildirmake命令,这是maildirmake.dovecot命令的一个更复杂的版本。

然后您可以使用maildirmake命令创建框架目录和任何需要的文件夹。

$ sudo maildirmake /etc/skel/Maildir
$ sudo maildirmake -f Sent /etc/skel/Maildir
$ sudo maildirmake -f Trash /etc/skel/Maildir
$ sudo maildirmake -f Drafts /etc/skel/Maildir
$ sudo maildirmake -f Spam /etc/skel/Maildir

这里我们使用了malldirmake命令的-f选项来创建文件夹。我们指定想要创建的文件夹的名称和在其中创建文件夹的Maildir

如果不想安装maildrop包,可以使用mkdir命令创建目录,如清单 12-6 所示。

$ sudo mkdir -p /etc/skel/Maildir/{cur,new,tmp}; chmod -R 0700 /etc/skel/Maildir
Listing 12-6.Manually Creating Maildir

Note

在清单 12-6 中,我们使用了一个聪明的 Bash 快捷方式,列出了所有三个用括号({ })括起来并用逗号分隔的目录。这个技术,大括号扩展,告诉mkdir创建所有三个子目录。您可以将它与各种其他命令一起使用,而不需要键入命令三次。–p选项创建所有的父目录和目标目录。

清单 12-6 创建Maildir和所需的子目录,并将结果目录的权限更改为 0700,因此只允许拥有Maildir的用户访问它。

Mail Delivery Agents and Mail Filtering

在本章的前面,我们讨论了 MDA。这些工具位于您的 MTA 和用户邮箱之间,您可以告诉 Postfix 什么 MDA(如果有的话)要与您之前看到的mailbox_command配置选项一起使用。当在此选项中指定了 MDA 时,Postfix 会将电子邮件传递到此 MDA,而 MDA 会将电子邮件传递到用户的邮箱。

在交付过程中,MDA 可以执行各种操作;例如,它可以查找电子邮件的特征,如来自谁,并将其重定向到特定的邮箱文件夹。许多人使用 MDA 将邮件列表中的电子邮件分类到单独的文件夹中。MDA 还用于根据其他应用程序添加的邮件头对电子邮件进行排序;例如,许多垃圾邮件过滤器会添加指示电子邮件是否为垃圾邮件的标题。MDA 可以读取这些邮件头,并将电子邮件放入适当的文件夹中,例如,放入垃圾邮件文件夹中。您还可以使用 MDA 来生成外出通知,将特定的电子邮件转发给其他人,以及执行各种其他任务。

您还可以从一些 MDA 调用其他应用程序。例如,有些人不在他们的 MTA 中运行垃圾邮件过滤器,而是在他们的 MDA 中运行垃圾邮件过滤器。

在本章中,我们将使用 Dovecot 将邮件从 MTA 发送到用户的邮箱。使用 Dovecot LDA(本地递送代理- MDA 和 LDA 是同义词),我们可以使用 Sieve 插件进行邮件过滤、转发和自动回复电子邮件。

扩展后缀配置

到目前为止,我们只是触及了允许本地用户发送和接收电子邮件的 Postfix 配置的基础。然而,Postfix 还有许多其他方面可以让您的环境更安全,让您的用户体验更好,包括以下方面:

  • 加密:保护电子邮件和用户凭证的传输
  • 认证:确保只有适当的和经过认证的用户才能发送电子邮件

使用加密

后缀 MTA 能够通过称为 TLS 的加密协议对电子邮件传输进行加密。TLS 是安全套接字层(SSL)的后续协议,通常用于加密传输控制协议/互联网协议(TCP/IP)流量。邮件流量的 TLS 与使用超文本传输协议安全(HTTPS)协议连接到网站时相同,例如, https://www.gmail.com

TLS 为我们的电子邮件通信提供了两个关键功能。

  • 防止窃听我们的电子邮件内容
  • 加密客户端和服务器之间的通信,从而保护身份验证

不幸的是,您应该意识到电子邮件传输中的 TLS 并不像每个人希望的那样安全。邮件服务器可以配置为通过STARTTLS加密电子邮件的发送和接收。因为不要求邮件服务器使用STARTTLS,所以很多会降级为明文,或者容易受到恶意降级攻击。因此,虽然我们建议使用像 TLS 这样的安全传输,但它并不能保证防止窃听。电子邮件客户端是不同的,您可以确保客户端仅使用正确配置的安全传输。

为 Postfix 创建 SSL 证书

我们将使用 Let's Encrypt 来获取 SSL 证书。我们用上一章看到的certbot命令来完成。Certbot过程需要一个 web 服务器来管理验证过程以获得您的证书。好消息是,在我们运行获取证书的过程时,certbot命令可以选择运行一个独立的 web 服务器。

独立的 web 服务需要通过端口 80 (HTTP)或端口 443 (HTTPS)访问您的主机。我们可以用ufwfirewall-cmd命令来完成。对于 Ubuntu,我们将发出以下命令:

$ for h in http https ;do sudo ufw allow $h ;done

或者在 CentOS 上,我们将发布以下内容:

$ for h in http https; do sudo firewall-cmd --zone public --add-service=$h ;done

一旦完成,我们可以使用Certbot为我们的邮件主机创建证书。同样,如果我们正在测试我们的证书的创建,我们应该使用--test-cert选项在 Let's Encrypt 中使用 staging API。

$ sudo certbot certonly --test-cert --standalone --uir --hsts --agree-tos -n -m admin@example.com -d mail.example.com
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/mail.example.com/fullchain.pem.
   Your cert will expire on 2016-11-22\. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   "certbot renew"

当我们高兴地准备好真正生成我们的证书时,我们将删除--test-cert。我们使用的其他选项是certonly,它表示我们只需要证书,而不需要将它安装到我们的 web 配置中。--standalone选项告诉certbot启动自己的 web 服务来生成证书。接下来的两个选项,--uir--hsts,是为了确保每个到 Certbot API 的通信都在https://之下。使用--agree-tos选项,我们同意服务条款。–n选项是用于非交互式的,这有利于脚本编写。然后我们提供注册联系人(-m)和我们想要注册的主机名(-d)。然后,证书被存储在/etc/letsencrypt/live/mail.example.com/目录中。

为 TLS 配置后缀

现在,您已经从 Let's Encrypt 获得了一个证书和一个密钥,或者您已经获得了一个商业证书或一个由您的自我管理的证书颁发机构(CA)签名的证书。您还需要来自商业 CA 或自我管理 CA 的根证书。如果您使用的是商业证书,您的 CA 通常会提供一个可下载根证书的链接,或者如果您使用的是自己的 CA,该证书会在/etc/pki/CA/cacert.pem中。

现在您已经有了证书和密钥,您需要配置 Postfix 的main.cf配置文件。在清单 12-7 中,您可以看到您需要添加到您的main.cf配置文件中的选项,我们将带您浏览这些设置。

smtp_tls_security_level = may
smtp_tls_CAfile = /etc/letsencrypt/live/mail.example.com/chain.pem
smtp_tls_cert_file = /etc/letsencrypt/live/mail.example.com/cert.pem
smtp_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem

smtpd_tls_security_level = may
smtpd_tls_CAfile = /etc/letsencrypt/live/mail.example.com/chain.pem
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/cert.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_loglevel = 1
smtpd_tls_mandatory_exclude_ciphers = aNULL, MD5
smtpd_tls_mandatory_protocols = TLSv1

Listing 12-7.Postfix TLS Configuration

如果你查看清单 12-7 中的选项,你会发现它们看起来很相似,除了初始前缀的不同。一个以smtp开头,一个以smtpd开头。当 Postfix 向另一个邮件服务器发送邮件时,使用以smtp开头的配置选项。以smtpd开头的选项在 Postfix 接收电子邮件时使用,例如从客户端接收邮件。通过指定smtp_tls_security_levelsmtpd_tls_security_level,我们告诉 Postfix 我们希望加密传入和传出的连接。

两个选项的may值启用了一种称为机会 TLS 的模式。这基本上意味着,如果远程客户机或服务器支持 TLS,就应该使用它。否则,纯文本连接是可以接受的。不幸的是,并非所有的电子邮件服务器都支持 TLS。因此,考虑到并非所有的客户端和服务器都支持 TLS,目前may是一个明智的选择,将服务器限制为加密连接将意味着一些电子邮件服务器无法向您发送电子邮件。

接下来的三个选项,smtpd_tls_key_filesmtpd_tls_cert_filesmtpd_tls_CAfile,指定我们的证书、密钥文件和 CA 证书的位置。

下一个选项smtpd_tls_loglevel控制 Postfix 将生成多少 TLS 连接日志。此处指定 0 会禁用日志记录,指定 1 会提供基本日志记录,而指定 3 和 4 会产生最高级别的日志记录(除非您正在进行故障排除,否则不建议使用)。对于日常操作,我们建议将其设置为 1,这将产生一些关于连接和所用证书的简要信息。

我们还设置了一些选项来决定我们将接受什么样的密码以及我们想要使用什么样的协议。smtpd_tls_mandatory_exclude_ciphers选项决定了我们不接受哪些密码;由于aNULLMD5是弱密码,我们不会在 TLS 传输中使用它们。对于smtpd_tls_mandatory_protocols,我们声明我们将只支持TLSv1和更高版本,因为任何更低的版本都被认为是易受攻击的。

进行更改后,您需要重新启动 Postfix 服务。

$ sudo service postfix restart

一旦 Postfix 服务重新启动,您就可以使用我们之前介绍的相同的nc命令来测试 TLS 是否被启用,如清单 12-8 所示。

$ $ openssl s_client -connect localhost:25 -starttls smtp
CONNECTED(00000003)
depth=1 CN = Fake LE Intermediate X1
verify error:num=20:unable to get local issuer certificate
verify return:0
---
Certificate chain
 0 s:/CN=mail.example.com
   i:/CN=Fake LE Intermediate X1
 1 s:/CN=Fake LE Intermediate X1
   i:/CN=Fake LE Root X1
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIE+zCCA+OgAwIBAgITAPpVz+iRXsKC430YQrhOPwEhdjANBgkqhkiG9w0BAQsF
ADAiMSAwHgYDVQQDDBdGYWtlIExFIEludGVybWVkaWF0ZSBYMTAeFw0xNjA4MjQx
...<snip>...
iIVJyRvMWqW2x9i0o/t9bheRaoSX/Vt7X4ZF8vClEQQ0iSNTC956WxAiyFOXLU7A
86RIlXw3Zm01CtiP4rHi2ZzoIChvcSBfdNG5kOCy5w==
-----END CERTIFICATE-----
subject=/CN=mail.example.com
issuer=/CN=Fake LE Intermediate X1
---
Acceptable client

certificate CA names
/CN=Fake LE Intermediate X1
Server Temp Key: ECDH, prime256v1, 256 bits
---
SSL handshake has read 3421 bytes and written 428 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : ECDHE-RSA-AES256-SHA
    Session-ID: 419EB576C7D51B5818CD1F64564275F8104AEB4ABAE8FAA0C9EA300A0F44B8F3
    Session-ID-ctx:
    Master-Key: 6FD71DE08D3888747E57D506FCDB710BCCEBE7557810DC92F1E4FBCC77CCAD0CCA2DE0616E882E0B9BC1775E8054298D
    Key-Arg   : None
    Krb5 Principal: None
    PSK identity: None
    PSK identity hint: None
    TLS session ticket lifetime hint: 3600 (seconds)
    TLS session ticket:
    0000 - 5d e2 39 53 22 6b 33 9e-d9 4e 14 de 63 6d de 73   ].9S"k3..N..cm.s
    0010 - d0 1c e9 55 7a 1e 70 32-b3 02 30 93 e5 f5 d3 d4   ...Uz.p2..0.....
    0020 - 8e a6 e5 bf c8 d5 20 a2-88 0e 20 88 15 29 4c f4   ...... ... ..)L.
    0030 - f1 88 eb b8 a1 47 1a 2c-3e 74 2e f0 b5 c1 f5 d2   .....G.,>t......
    0040 - 97 ec 26 a9 65 c8 e9 b6-92 3a 07 a0 30 56 5f e5   ..&.e....:..0V_.
    0050 - ad 73 a7 46 42 47 7f a2-82 b8 ed 08 6a da 25 6b   .s.FBG......j.%k
    0060 - ae 44 a6 c7 b3 b8 e4 f9-8f 73 64 b6 47 01 79 36   .D.......sd.G.y6
    0070 - 7d 91 3a 26 e7 03 74 5d-4f db 1a d4 28 65 e1 f7   }.:&..t]O...(e..
    0080 - b8 d0 a5 91 81 96 0a 3a-cd fa a1 f0 97 c7 b5 37   .......:.......7
    0090 - 0e bd 29 7b 1d 56 ad 91-81 a9 50 6e c4 ee 0f 94   ..){.V....Pn....

    Start Time: 1472046666
    Timeout   : 300 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
---
250 DSN
EHLO o.yeah.com
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN

Listing 12-8.Testing Postfix with TLS

在清单 12-8 中,我们已经连接到我们的电子邮件服务器并发出了EHLO命令。我们使用openssl s_client命令连接到我们的电子邮件服务器,并传递START命令。您可以看到电子邮件服务器做出了响应,我们建立了一个加密会话。在我们发出EHLO命令后,电子邮件服务器用可用的支持命令进行了响应。

如果您只连接 netcat,您会看到这里列出了一个新命令,STARTTLS。这告诉我们 Postfix 现在向客户端和其他服务器提供 TLS。

$ nc localhost 25
220 mail.example.com ESMTP Postfix
EHLO ah.ah.com
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
STARTTLS
220 2.0.0 Ready to start TLS

然后,我们输入STARTTLS命令,告诉 Postfix 我们想要启动一个加密连接。Postfix 回应说它已经准备好开始加密连接。这表示 TLS 已成功设置,正在等待连接。

另一种测试你的服务器是否被正确设置来发送安全邮件的方法是对照 www.checktls.com/testreceiver.html 来检查它。这将尝试与您的电子邮件服务器建立连接,并向您报告连接是否安全。

如果您在配置 Postfix TLS/SSL 加密时遇到问题,请参阅即将到来的“获取 Postfix 帮助”一节中的指针和链接。

Caution

请务必注意一些关于使用 Postfix TLS 加密的问题,以确保您的电子邮件内容的机密性。Postfix 向其他服务器发送电子邮件,但只有当其他服务器支持 TLS 时才加密邮件。由于并非所有服务器都支持加密,有些电子邮件可能没有加密。确保所有电子邮件加密的唯一方法是使用基于内容的加密解决方案,如 S/MIME ( http://en.wikipedia.org/wiki/S/MIME )、PGP ( www.pgp.com/ )和 GnuPG ( http://www.gnupg.org/ )。

证明

现在我们已经配置了 TLS,我们可以加密客户端和服务器之间的会话,以及我们的服务器和其他支持 TLS 的服务器之间的会话。这就把我们带到了后缀配置的下一个阶段:认证。

在其默认配置中,您的 Postfix 服务器将只接受来自其受信任网络中客户端的电子邮件,如在mynetworks配置选项中所定义的,在我们的示例中,是 192.168.0.0/24 和 192.168.1.0/24 网络。这可以防止不适当的用户使用我们的电子邮件服务器。没有这些限制的电子邮件服务器被称为开放中继。开放中继允许任何人向它发送电子邮件,服务器将继续发送。垃圾邮件制造者大量利用开放中继,用不需要的电子邮件污染互联网。

Caution

开放中继是很成问题的,不正确的配置会导致服务器成为开放中继,这对您的组织来说是非常麻烦的。当检测到开路继电器时,它们通常会被列入黑名单。这个黑名单过程意味着开放中继的 IP 地址被添加到不接受来自其的电子邮件的服务器列表中。如果您的服务器是一个开放的中继,即使在关闭中继并修复您的服务器后,也很难将您自己从这些黑名单中删除并允许您的用户发送电子邮件。你应该定期测试你的服务器是否像一个使用 www.mailradar.com/openrelay/ 服务的开放中继。

默认情况下,Postfix 的配置会阻止您的服务器像开放中继一样运行。但是,尽管这种配置停止了开放中继,但它留下了一个安全漏洞,并产生了一个功能缺口。

关于这个漏洞,任何能在你的网络上获得 IP 地址的人都可以向你的邮件服务器发送电子邮件。例如,如果攻击者破坏了您的无线网络,她可以利用您的电子邮件服务器发送垃圾邮件。

这种配置也给移动用户留下了功能空白。几乎可以肯定的是,有些用户会外出旅行、在家工作,或者使用手机或平板电脑等移动设备。这些用户无法使用您的电子邮件服务器,因为他们是移动用户,在您的网络上没有 IP 地址。这些用户必须依赖服务提供商的电子邮件服务器,该服务器允许中继、开放中继或虚拟专用网络(VPN)进入您的组织。通常这不是一个理想的情况。

有了身份验证,只要经过身份验证,您的用户就可以从任何地方发送电子邮件。这还意味着您信任的网络上的内部用户需要提供身份验证凭据,才能被允许发送电子邮件。这降低了有人可能直接进入您的网络并使用您的电子邮件服务器的风险。

您的身份验证又受到您刚刚配置的 TLS 加密的保护,允许您的用户进行身份验证,而不会在网络上暴露他们的凭据。

Note

这是一件好事,因为人们通常使用像tcpdump或 Wireshark(两者都在第七章中提到)这样的工具来“嗅”网络。这些人“嗅探”加密或暴露的密码等东西来窃取。对于无线网络,这尤其容易,因为攻击者甚至不需要实际接入您的网络。

SMTP 授权和 SASL

电子邮件服务器的认证是由一种叫做 SMTP AUTH的机制提供的。这是另一个 SMTP 命令,它在允许用户发送电子邮件之前提示用户输入用户名和密码。该用户名和密码可以通过各种机制提供,包括明文和加密形式。还可以使用支持一次性密码的机制,如智能卡或令牌。使用什么机制在很大程度上取决于您的客户端支持什么机制。例如,微软的 Outlook 客户端只支持少量的机制,而 Mozilla Thunderbird 支持更多的机制。

Note

您可以在 http://en.wikipedia.org/wiki/One-time_password 了解一次性密码。

为了确认用户的凭证是有效的,AUTH命令使用了一个名为简单认证和安全层(SASL)的认证框架。SASL 很像可插拔认证模块(PAM),我们在第五章中讨论过,它抽象了认证。它允许在简单身份验证和安全层(SASL)协议背后隐藏多种类型的身份验证。这意味着您的电子邮件服务器可以检查各种后端服务,以验证用户是否被允许发送电子邮件,而无需了解如何通过这些服务的身份验证。

这些后端服务可以包括 PAM(可用于允许用户使用其 Linux 登录名和密码进行身份验证)、用户和密码数据库,甚至包括 LDAP 或 Active Directory 等用户存储库。Postfix 没有内置 SASL。它依赖于与其他应用程序的集成来提供 SASL 功能。我们将使用 Dovecot 服务器向 Postfix 提供这些 SASL 功能。

如果到目前为止您一直遵循这个示例,那么当我们在本章前面向您展示如何安装 Postfix 时,您应该已经安装了 Dovecot。如果没有,如果您想按照接下来的说明进行操作,请使用这些说明立即安装。然后,您需要确保 Postfix 支持使用 Dovecot 的 SASL 认证。这可以通过使用-a选项的postconf命令来完成。

$ sudo postconf -a
cyrus
dovecot

这里的postconf命令返回了 Postfix 支持的所有 SASL 认证插件。我们正在寻找dovecot条目。所有最新版本的 CentOS 和 Ubuntu 都应该提供这种支持。

为 SASL 配置鸽笼

现在,我们需要配置 Dovecot 的 SASL 支持并启动 Dovecot 守护进程。鸽笼配置文件位于/etc/dovecot/dovecot.conf/etc/dovecot/conf.dconf.d目录中的文件用于为 Dovecot 配置不同的服务和机制。

我们将编辑/etc/dovecot/dovecot.conf并设置 Dovecot 的 SASL 认证服务。我们打开配置文件,寻找我们想要编辑的第一个选项:protocols。我们首先要关闭除身份验证之外的所有服务。我们通过将它设置为none来做到这一点。

protocols = none

Note

当我们在本章后面的“配置 IMAP 和 POP3”一节中讨论 IMAP 和 POP3 以及如何启用它们时,我们将回到这个选项。

接下来,我们需要配置身份验证服务。为此,我们将编辑/etc/dovecot/conf.d/10-master.conf,找到service auth配置选项。你可以在清单 12-9 中看到我们将要设置的service auth配置选项。

service auth {
...
  # Postfix smtp-auth
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user   = postfix
    group = postfix
  }
....
}
Listing 12-9.Configuring Dovecot auth

service auth由一系列括在括号内的指令组成({ })。该服务可能已经在您现有的10-master.conf配置文件中进行了部分配置。您需要确保现有配置文件中的配置与清单 12-9 中的配置相匹配。

清单 12-9 中的unix_listener指令是提供 Postfix 和 Dovecot 之间连接的套接字。套接字监听来自 Postfix 的身份验证请求,然后返回结果。它通过使用一种叫做套接字的特殊类型的文件来做到这一点,我们在第四章中简要讨论过,它允许应用程序之间的交互。这个套接字位于一个 Postfix 目录中,该目录存储了守护进程/var/spool/postfix/private/使用的文件和套接字,文件名为authmodeusergroup选项控制套接字的权限和所有权,我们只将这些权限和所有权限制给postfix用户。

当 Dovecot 守护进程运行时,您可以在目录中看到这个文件。

$ sudo ls -l /var/spool/postfix/private/auth
srw-rw----. 1 postfix postfix 0 Aug 25 09:33 /var/spool/postfix/private/auth

接下来,我们必须更改/etc/dovecot/conf.d/10-auth.conf中的设置。该文件控制身份验证机制。我们要更改的这个文件中的指令是mechanisms,它指定了这个 Dovecot 实例支持哪些认证机制。默认情况下,接受纯文本格式的用户和密码的PLAIN机制通常是启用的。

Caution

只有在启用了 TLS 的情况下,才应该使用PLAIN机制;否则,攻击者可能会从网络上窃取您的用户凭证。

您可以在表 12-1 中看到 Dovecot 可用的其他类型。

表 12-1。

Dovecot Authentication Mechanisms

| 机制 | 描述 | | --- | --- | | `PLAIN` | 纯文本身份验证。 | | `LOGIN` | Microsoft Outlook 客户端中使用的一种 Microsoft 身份验证机制。 | | `CRAM-MD5` | 加密密码机制。这在邮件客户端中有一些支持。 | | `DIGEST-MD5` | 像`CRAM-MD5`一样,但是有更强的密码。这限制了客户的支持。 | | `NTLM` | 基于 Microsoft Windows 的身份验证,通常仅在 Microsoft 客户端受支持。 | | `GSSAPI` | Kerberos v5 支持。这限制了客户的支持。 | | `ANONYMOUS` | 支持匿名登录。这是不推荐的,也不安全。 | | `OTP` | 一次性密码机制。 | | `SKEY` | 一次性密码机制。 |

Note

您不能指定 Dovecot 不支持的认证机制。例如,没有正确的支持配置,您就不能指定 NTLM 机制。

在清单 12-9 中,我们还启用了LOGIN认证机制,以防我们的任何用户拥有微软客户端,但是我们不打算启用任何其他类型。绝大多数客户端将支持PLAIN,许多其他客户端也将支持LOGIN认证类型。您可以在 http://en.wikipedia.org/wiki/Comparison_of_e-mail_clients#Authentication_support 找到邮件客户端及其支持的认证机制的完整列表。

此外,在10-auth.conf文件中,我们确定将使用哪个认证服务。在文件的底部,你会看到我们包含了!include auth-system.conf.ext!include声明说我们想要包含这个文件中的配置设置。那个文件位于这里:/etc/dovecot/conf.d/auth-system.conf.ext

如果我们查看该文件,我们需要确保它与清单 12-10 匹配。

passdb {
  driver = pam
  # [session=yes] [setcred=yes] [failure_show_msg=yes] [max_requests=<n>]
  # [cache_key=<key>] [<service name>]
  #args = dovecot
}
userdb {
  # <doc/wiki/AuthDatabase.Passwd.txt>
  driver = passwd
  # [blocking=no]
  #args =

  # Override fields from passwd
  #override_fields = home=/home/virtual/%u
}

Listing 12-10.Specifying the Authentication Service

清单 12-10 中的指令控制 Dovecot 检查以执行认证的认证存储。认证存储的默认驱动程序是pampam存储是一个密码数据库,它利用 PAM 应用程序根据本地主机的用户来验证用户。因此,对于要向主机发送电子邮件进行身份验证的用户,他需要在主机上创建一个具有有效密码的用户。

认证用户时,Dovecot 会在/etc/pam.d目录中查找名为dovecot的 PAM 服务定义。当您在发行版上安装 Dovecot 包时,会安装这个文件。在清单 12-11 中,我们展示了 Ubuntu 的/etc/pam.d/dovecot文件。

#%PAM-1.0
auth       required     pam_nologin.so
auth       include      password-auth
account    include      password-auth
session    include      password-auth
Listing 12-11.Dovecot PAM Service

您可以在清单 12-11 中看到,对 Dovecot 的认证查询使用的 PAM 认证检查与登录到主机的用户所经历的相同(正如您在第五章中看到的)。

Note

您可以在 http://wiki.dovecot.org/PasswordDatabase 找到关于 Dovecot 密码数据库的更多信息,以及在 http://wiki.dovecot.org/PasswordDatabase/PAM 找到关于 Dovecot PAM 认证的更多信息。

清单 12-10 、userdb中的另一条指令设置为driver = passwd。这执行用户查找以返回关于用户的一些信息。它返回用户的 UID、GID 和主目录等信息,并从/etc/passwd file中检索这些信息。如您所见,您可以选择覆盖您在passwd文件中的值,比如用户的主目录,将他们的邮件放在不同于他们正常主目录的目录中。

Note

您可以在 http://wiki.dovecot.org/UserDatabase 了解更多关于用户数据库查找的信息。

配置完 Dovecot 后,您需要启动(或重启)它。我们使用 systemctl 命令启动 Dovecot。

$ sudo systemctl start dovecot

或者,您可以检查 Dovecot 进程是否像这样运行:

$ systemctl status dovecot
● dovecot.service - Dovecot IMAP/POP3 email server
   Loaded: loaded (/usr/lib/systemd/system/dovecot.service; disabled; vendor preset: disabled)
   Active: active (running) since Thu 2016-08-25 09:33:09 EDT; 37min ago
  Process: 7172 ExecStartPre=/usr/libexec/dovecot/prestartscript (code=exited, status=0/SUCCESS)
 Main PID: 7177 (dovecot)
   CGroup: /system.slice/dovecot.service
           ├─7177 /usr/sbin/dovecot -F
           ├─7180 dovecot/anvil
           ├─7181 dovecot/log
           ├─7183 dovecot/config

Aug 25 09:33:09 ip-10-0-10-154.ap-southeast-2.compute.internal systemd[1]: Starting Dovecot IMAP/POP3 email server...
Aug 25 09:33:09 ip-10-0-10-154.ap-southeast-2.compute.internal systemd[1]: Started Dovecot IMAP/POP3 email server.
Aug 25 09:33:09 ip-10-0-10-154.ap-southeast-2.compute.internal dovecot[7177]: master: Dovecot v2.2.10 starting up without any protocols (core dumps disabled)

在这里您可以看到dovecotdovecot-auth进程正在运行,表明 Dovecot 已经成功启动。

在 CentOS 上,Dovecot 记录到/var/log/maillog文件,在 Ubuntu 上记录到/var/log/mail.log file,如果您看到如下所示的日志条目,您可以确认它正在运行:

Jan  7 18:37:03 au-mel-rhel-1  dovecot: Dovecot v1.0.7 starting up

为 SASL 配置后缀

接下来,我们需要配置 Postfix 来使用我们刚刚配置的 Dovecot SASL 服务。将清单 12-12 中的条目添加到main.cf配置文件中。

smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_recipient_restrictions = permit_sasl_authenticated,permit_mynetworks,reject_unauth_destination
smtpd_sasl_auth_enable = yes
smtpd_tls_auth_only = yes
Listing 12-12.Configuring Postfix for Dovecot SASL

在清单 12-12 中,我们使用了smtpd_sasl_type选项来指定我们使用 Dovecot 来执行我们的 SASL 认证。smtpd_sasl_path选项指定了认证套接字相对于 Postfix 的 spool 目录的位置,通常是/var/spool/postfix目录。这与我们之前在清单 12-9 中定义的 Dovecot 客户端套接字相匹配。smtpd_sasl_auth_enable选项告诉 Postfix 启用 SASL 认证。

下一个选项smtpd_tls_auth_only告诉 Postfix 只有在 TLS 启用并运行时才宣布并使用认证。这意味着STARTTLS命令需要在客户机和服务器之间创建加密连接的情况下发出。

最后一个选项smtpd_recipient_restrictions,是 Postfix 的限制列表之一。当发出RCPT TO命令时,它告诉 Postfix 允许或拒绝什么,例如,当从客户端收到电子邮件时。默认情况下,正如我们前面提到的,Postfix 将接受电子邮件

  • 来自 IP 地址与mynetworks选项中的值相匹配的客户端
  • 发送到与relay_domains选项值匹配的远程目的地,默认为mydestination选项值
  • 往返于本地主机

我们将调整这个默认行为,告诉 Postfix 也接受来自 SASL 认证的用户的电子邮件。

首先,我们有permit_mynetworks选项,它在mynetworks选项中维护对网络的访问。然后我们添加permit_sasl_authenticated选项,告诉 Postfix 接受来自 SASL 认证用户的邮件。最后,为了让 Postfix 接收电子邮件并拥有有效的配置,我们必须以拒绝限制结束,在本例中是reject_unauth_destination选项。此选项拒绝任何不符合我们刚刚建立的最后两个标准的电子邮件:发往指定的远程目的地或发往和来自本地主机的电子邮件。

Note

你可以在 www.postfix.org/postconf.5.html#smtpd_recipient_restrictions 看到更多关于收件人限制的信息。

测试后缀身份验证

现在,一旦我们重新启动 Postfix,我们的 SASL 配置应该是活动的,我们可以测试这一点。我们现在有很多方法可以测试这个。第一步是配置一个客户机,向我们的服务器发送经过身份验证的电子邮件。但是由于我们还没有为客户端提供浏览邮箱的方式,比如 IMAP 或 POP3 协议,所以这并不是非常有用。我们可以再次使用openssl s_client命令进行测试。

为了测试连接,我们需要创建一个 base64 编码的用户名和密码。为此,我们将使用 Python。

$ python  -c 'import base64; print base64.b64encode("jsmith")'
anNtaXRo
$ python  -c 'import base64; print base64.b64encode("secret")'
c2VjcmV0

在前面的代码片段中,我们使用了python命令来执行(-c)代码,该代码将创建我们需要的用户名和密码的 base64 编码。我们现在将使用这些值来测试我们是否能够通过 SMTP 服务器的身份验证并发送电子邮件。

在清单 12-13 中,您可以看到一个测试 SASL 认证的openssl s_client会话。

$ openssl s_client -connect mail.example.com:25 -starttls smtp
CONNECTED(00000003)
depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3
verify return:1
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify return:1
depth=0 CN = mail.example.com
verify return:1
---
Certificate chain
 0 s:/CN=mail.example.com
   i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
   i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate

-----BEGIN CERTIFICATE-----
MIIFGjCCBAKgAwIBAgISA0fRIzPUESQUdGrLEPG8niSXMA0GCSqGSIb3DQEBCwUA
...
<snip>
...
PLCtj2aR+DrP2jz7IKO3CmzrvSbPxs+wtmIpgmV96HLE6zc94xAV6bQEoZWvav5F
I2Ra8G/fFEYE1/nNvinV1ik0Qa68vHqYhL0hemU/2Z8/pBCFWg1txqfSSUq4G4mH
NsOPhphTx/QyYjeU7K0=
-----END CERTIFICATE-----
subject=/CN=mail.example.com
issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
...
< snip>
...
Verify return code: 0 (ok)
---
250 DSN
ehlo me.com
250-mail.example.com
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
AUTH LOGIN
334 VXNlcm5hbWU6
anNtaXRo
334 UGFzc3dvcmQ6
c2VjcmV0
235 2.7.0 Authentication successful
MAIL FROM: me@here.com
250 2.1.0 Ok
rcpt to: jsmith@example.com
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
Subject: hello you
You're ace
.
250 2.0.0 Ok: queued as 5F2051866087
DONE
Listing 12-13.Using swaks to Test SASL

在清单 12-13 中,我们用–starttls smtp选项启动了openssl s_client命令,它告诉openssl在连接到我们的电子邮件服务器时传递STARTTLS命令。我们已经指定了–connect选项,并传递了我们要连接的主机和端口。

当执行该命令时,您可以看到一个连接被启动,启动了STARTTLS命令,并且显示了服务器的公钥细节。我们得到返回代码250 DSN(表示“交付状态通知”)。然后我们发出ehlo me.com命令,SMTP 服务器用它的服务器名和它拥有的列表功能来响应,其中一个是AUTH PLAIN LOGIN,显示了提供的两种可用的认证机制:PLAINLOGIN

然后我们发出AUTH LOGIN命令,我们看到的是334 VXNlcm5hbWU6,这是 base64 编码的 word 用户名。我们提交用户名,然后我们被要求输入密码(334 UGFzc3dvcmQ6)。我们提交密码,全部采用 base64 编码。身份验证成功,测试电子邮件已提交。如果认证机制失败了,Postfix 会继续尝试认证机制,在我们的例子中是LOGIN然后是PLAIN,直到一个机制成功或者所有机制都失败。如果全部失败,客户端将收到一条错误消息。

Note

我们在前面的例子中使用了小写的 RCPT 来:(rcpt to: jsmith@example.com ))。这是因为openssl将重新协商 TLS 会话,如果线路以大写字母 R 开头,您将无法登录。

您还可以通过检查 Postfix 的 syslog 日志文件来确认会话是否成功。

$ sudo less /var/log/maillog
Aug 26 20:38:31 ip-10-0-10-154 postfix/smtpd[10683]: initializing the server-side TLS engine
Aug 26 20:38:31 ip-10-0-10-154 postfix/smtpd[10683]: connect from
  ppp12-29-41-55.bras1.mel11.internode.on.net[12.29.41.15]
Aug 26 20:38:31 ip-10-0-10-154 postfix/smtpd[10683]: setting up TLS connection from
  ppp12-29-41-55.bras1.mel11.internode.on.net[12.29.41.15]
Aug 26 20:38:31 ip-10-0-10-154 postfix/smtpd[10683]: ppp12-29-41-15.bras1.mel11.internode.on.net[12.29.41.15]:
  TLS cipher list "aNULL:-aNULL:ALL:!EXPORT:!LOW:!MEDIUM:+RC4:@STRENGTH:!aNULL:!MD5"

Aug 26 07:52:14 mail postfix/smtpd[9215]: 5F2051866087:  
  client=ppp12-29-41-55.bras1.mel11.internode.on.net[12.29.41.15], sasl_method=LOGIN, sasl_username=jsmith
Aug 26 07:52:29 mail postfix/anvil[9162]: statistics: max connection rate 1/60s for (smtp: 12.29.41.15) at Aug 26 07:42:29
Aug 26 07:52:29 mail postfix/anvil[9162]: statistics: max connection count 1 for (smtp: 12.29.41.15) at Aug 26 07:42:29
Aug 26 07:52:29 mail postfix/anvil[9162]: statistics: max cache size 1 at Aug 26 07:42:29

Aug 26 07:52:37 mail postfix/cleanup[9227]: 5F2051866087: message-id=<>
Aug 26 07:52:37 mail postfix/qmgr[1186]: 5F2051866087: from=<me@here.com>, size=270, nrcpt=1 (queue active)
Aug 26 07:52:37 mail postfix/local[9228]: 5F2051866087: to=<jsmith@example.com>, relay=local, delay=235, delays=235/0.01/0/0, dsn=2.0.0, status=sent (delivered to mailbox)
Aug 26 07:52:37 mail postfix/qmgr[1186]: 5F2051866087: removed

Aug 26 07:52:44 mail postfix/smtpd[9215]: disconnect from ppp12-29-41-15.bras1.mel11.internode.on.net[12.29.41.15]

请注意,在第一部分中建立了 TLS 连接,然后启动身份验证,发送电子邮件,最后断开连接。

如果您的身份验证成功,您现在已经运行了 Postfix 身份验证,您的服务器现在可以被远程用户使用,并且您的内部用户可以安全地提交电子邮件。

如果您在配置 Postfix 身份验证时遇到问题,请参阅即将到来的“获取 Postfix 帮助”一节中的指针和链接。

Smarthosting

正如我们在本章前面所提到的,一些 ISP 会阻止传出的 SMTP 流量。这是为了帮助减少垃圾邮件。此外,一些 SMTP 服务器被配置为不接收来自特定类型网络的电子邮件,如 ADSL 和使用动态 IP 地址的网络。如果您无法发送出站电子邮件,或者某些服务器拒绝来自您的服务器的连接,这可能就是问题所在。检查这一点的一个简单方法是,看看您是否可以使用类似swaks的工具连接到外部 MTA,或者您是否可以联系您的 ISP 以了解它是否限制传出的 SMTP 流量。

如果你被屏蔽了,你可以用智能主机来解决这个问题。智能主机是接收和转发电子邮件的中继服务器。许多 ISP 为他们的用户提供智能主机。

有两种类型的智能主机—未经身份验证的和经过身份验证的。这两种类型都是用main.cf配置文件中的relayhost配置选项指定的(参见 www.postfix.org/postconf.5.html#relayhost ),如下所示,其中mail.isp.net是您的智能主机的主机名:

relayhost = mail.isp.net

这将告诉您的 Postfix 电子邮件服务器将所有发出的电子邮件发送到mail.isp.net。这假定智能主机将接受来自您的电子邮件;例如,一些 ISP 智能主机很乐意接受来自 ISP 管理的任何 IP 地址的电子邮件。

然而,有时如果没有 SASL 认证,你的智能主机不会接受你发来的电子邮件。如果是这样,您需要配置智能主机身份验证。为此,您需要配置 SASL 身份验证,其中 Postfix 是客户端而不是服务器。请记住,当您将它配置为服务器时,所有的配置选项都以smtpd_开头,表示它是用于连接到服务器的。现在您需要将 Postfix 配置为客户端,并且您使用以smtp开始的配置选项。为此,在main.cf文件中设置以下选项:

smtp_sasl_password_maps = hash:/etc/postfix/smtp_sasl_passwd
smtp_sasl_auth_enable = yes
smtp_sasl_mechanism_filter = plain, login
smtp_sasl_security_options = noanonymous

当服务器是客户端时,这将为您的服务器配置 SASL 身份验证。smtp_sasl_password_maps选项(参见 www.postfix.org/postconf.5.html#smtp_sasl_password_maps )指定一个数据库文件,该文件包含一系列智能主机及其所需的凭证。这是通过编辑一个文件并使用postmap命令从该文件创建一个数据库来创建的。/etc/postfix/smtp_sasl_passwd文件需要包含智能主机的名称以及进行身份验证所需的用户名和密码(用冒号分隔),如下所示:

mail.isp.net        username:password

您需要确保这些文件只对根用户可读,并且有适当的权限阻止任何人查看您的密码。

$ sudo chown root:root /etc/postfix/smtp_sasl_passwd
$ sudo chmod 0600 /etc/postfix/smtp_sasl_passwd

然后我们使用postmap命令创建一个数据库。

$ sudo postmap hash:/etc/postfix/smtp_sasl_passwd

然后创建一个名为smtp_sasl_passwd.db的文件。

剩下的选项非常简单。smtp_sasl_auth_enable选项打开 Postfix 作为客户端的 SASL 认证。smtp_sasl_mechanism_filter选项指定智能主机支持什么类型的认证机制,而smtp_sasl_security_options选项禁用匿名机制。

更新配置后,您需要重新加载或重新启动 Postfix,然后可以测试您的服务器是否可以向智能主机发送经过 SASL 验证的电子邮件。您还可以使用传输选项指定更细粒度的智能托管(参见 www.postfix.org/transport.5.html )。

后缀查找表和虚拟域

我们已经在本章讨论了查找和映射,或者是别名,或者是密码。这些通常被称为查找表。后缀查找表用于存储有关访问、路由、重写和过滤内容的信息。查找表可以是基于文本文件的数据库、LDAP 数据库或 SEQUEL 数据库。我们将看两个查找表的例子:别名和虚拟别名域。

前面我们谈到了newaliases命令。该命令使用postalias命令获取在/etc/aliases文件中列出的名称和别名,并创建一个由local交付守护程序使用的索引文件。我们可以使用postalias命令来创建和管理索引目录;然而,newaliases命令为我们管理了这一点,因为我们也需要在每次更改后将配置重新加载到守护进程中,因此更容易使用(并且也与 Sendmail 程序兼容)。

我们可以使用postalias命令来查询别名散列。

$ sudo postalias -c /etc/postfix/ -q root hash:/etc/aliases
ataylor

我们通过main.cf文件中的alias_database配置告诉 Postfix 使用哪个表。我们可以通过以下内容轻松查看:

$ postconf –n |grep alias_database
alias_database = hash:/etc/aliases

我们可以转向更复杂的用途。我们的主机可能是发往example.com域的邮件的最终目的地。但是它也可以用来“托管”其他域。在这种情况下,我们的服务器可能是example.comexample.netexample.id或任何其他类型的注册域名的最终目的地,这些域名的 MX 记录指向我们的邮件服务器。

我们可以通过main.cf文件中的mydestination设置来实现。

mydestination = mail.example.com example.net example.id example.com

这意味着ataylor@example.com的邮件将需要一个本地的ataylor Linux 用户帐户,该地址将被发送到ataylor@example.comataylor@example.netataylor@example.id的同一个邮箱。你可能希望admin@example.comataylor,而admin@example.netbsingh。您可能根本不希望发生这种情况,也可能不希望在您的邮件服务器上为每个邮件用户提供一个 Linux 用户帐户。如果你为多个不同的域托管,这也变得笨拙。

Postfix 可以用两种不同的方式解决这个问题。

  • 虚拟别名域
  • 虚拟邮箱域

您可以采取的第一步是创建虚拟域。这允许您将虚拟域映射到每个域的用户帐户。因此,在前面的例子中,admin@example.com可以转到不同于admin@example.net的用户。为此,我们在 Postfix 中使用一个查找表来管理每个域的用户。

我们需要在我们的main.cf文件中设置以下内容。

virtual_alias_domains = example.net example.id example.com
virtual_alias_maps = hash:/etc/postfix/virtual

我们已经告诉 Postfix,如果它收到给example.com.net.id的邮件,就使用索引文件alias_map /etc/postfix/virtual,这个文件将包含我们的列表。

/etc/postfix/virtual
admin@example.com ataylor
sales@example.com  jsmith
....
admin@example.net bsingh
sales@example.net jsmith ataylor@example.com

这里我们有两个域,example.comexample.netadmin@example.com地址别名为atayloradmin@example.net将被发送到bsingh。该地址可以是本地 Linux 地址或远程地址,就像您在sales@example.net地址中看到的那样。在我们创建了这个文件之后,以及每次我们更改它之后,我们需要使用postmap命令来重新创建索引,并重启 Postfix 来获取更改。

$ sudo postmap /etc/postfix/virtual
$ sudo systemctl reload postfix

假设我们不想在邮件服务器上创建 Linux 用户帐户。我们仍然可以通过虚拟邮箱为邮件用户发送邮件。我们不能为同一个域设置虚拟别名域和虚拟邮箱域,所以只能选其一。

要设置虚拟邮箱域,我们需要设置以下内容:

/etc/postfix/main.cf:
mydestination = localhost
virtual_mailbox_domains = hash:/etc/postfix/virt_domains
virtual_mailbox_maps = hash:/etc/postfix/virt_mailbox
virtual_alias_maps = hash:/etc/postfix/virtual
virtual_transport = lmtp:unix:private/dovecot-lmtp

在前面的例子中,我们已经将mydestination选项重置为localhost,并从virtual_mailbox_domains设置中获取目的地列表。virtual_mailbox_maps设置将列出我们接受的邮件地址。virtual_alias_maps设置将包含邮件用户的别名,就像别名文件一样。

这里是我们接受邮件的虚拟域。它们的格式是<domain> <action>

/etc/postfix/virt_domain
example.com         OK
example.net         OK
example.id          OK

在这里,我们为所有的域设置了OK动作。其他可以设置的动作包括DISCARDFILTERREJECT等。virtual_mailbox文件有类似的语法。

/etc/postfix/virt_mailbox:
ataylor@example.com    OK
ataylor@example.net    OK
bsingh@example.com     OK
jsmith@example.net     OK

这里列出了我们接受的电子邮件地址。同样,我们可以设置其他动作来代替OK,比如REJECTFILTERBCC

最后,我们也可以像对待/etc/aliases文件一样使用虚拟别名。在这里,我们在左侧列出我们接受的电子邮件地址,在右侧列出目的地:

/etc/postfix/virtual:
admin@example.com ataylor@example.com

一旦我们配置或更改了这些,我们需要再次运行postmap命令。

$ sudo postmap /etc/postfix/virt_domains
$ sudo postmap /etc/postfix/virt_mailbox
$ sudo postmap /etc/postfix/virtual
$ sudo systemct reload postfix

现在我们已经为我们的域做好了准备,我们可以将查找表改为 SQL 或 LDAP 支持的。查找表还有许多其他选项;如需完整列表,请访问:

获取后缀的帮助

Postfix 不仅是最容易配置的应用程序之一,具有简单的配置模型和大量的文档,而且也是最容易诊断的应用程序之一,大多数错误消息都是描述性的和有帮助的。

Note

不是所有的 MTA 都很容易配置,其他的有复杂的语法和令人困惑的错误信息。您会发现 Postfix 很容易配置,但其他如 Exim 和 Qmail 也很容易。

你可以在 Postfix 主页( www.postfix.org/ )找到有用的文档。这包括文档( www.postfix.org/documentation.html )以及操作方法和常见问题( www.postfix.org/docs.html )。

有一些有用的资源专门用于 SSL/TLS 加密和 SASL 的配置和故障排除。加密可以在 www.postfix.org/TLS_README.html 找资源。对于 SASL,你可以在 www.postfix.org/SASL_README.html 找到一个有用的 how-to,在 http://adomas.org/2006/08/postfix-dovecot/ 找到另一个 Ubuntu,在 www.lxtreme.nl/index.pl/docs/linux/dovecot_postfix_pam 找到一个更高级的版本。

以下文档也能有所帮助: www.postfix.org/DEBUG_README.html 。它展示了如何调试后缀问题,从增加日志的冗长度到跟踪后缀进程。最有用的调试工具之一是debug_peer_list选项。

通过设置debug_peer_list,可以增加邮件日志中关于一个对等体的信息。您可以这样设置它:

debug_peer_list = troubled.domain.com

在从troubled.domain.com开始的下一次连接中,您将看到日志记录的增加,您会发现这非常有用。

您还可以在 www.postfix.org/SOHO_README.html 找到一些在小型办公室/家庭办公环境中运行 Postfix 的技巧,您可能想考虑在 www.postfix.org/lists.html 加入 Postfix 邮件列表。

请记住,如果您提交问题或错误,您应该包括以下信息:

  • 您的后缀配置(运行postconf -n)
  • 你的平台(运行uname -a
  • 生成的任何日志消息(在 Ubuntu 上的/var/log/mail.log文件中或在 CentOS 上的/var/log/maillog文件中)

在 Freenode IRC 服务器( http://freenode.net/ )上还有一个名为#postfix的 IRC 频道,你可以在那里寻求帮助。

抗击病毒和垃圾邮件

现在您已经运行了带加密和认证的 Postfix,我们将向您展示如何保护您的用户和组织免受垃圾邮件和病毒的侵害。我们将会看到两种工具。

  • SpamAssassin:一个开源反垃圾邮件工具
  • ClamAV:一个开源的反病毒扫描器和引擎

我们将把这两个工具与 Postfix 集成在一起,并教你如何使用它们。

打击垃圾邮件

垃圾邮件是不请自来的电子邮件,范围从请求帮助处理各种非法金融交易到提供将扩大人体解剖部分的药物。垃圾邮件是对用户幸福的最大威胁之一。没有什么比上班时发现一大堆垃圾邮件和你的真实邮件被埋在地下更让人恼火的了。这对您的组织和用户也是一种威胁,因为垃圾邮件通常隐藏着网络钓鱼攻击( http://en.wikipedia.org/wiki/Phishing )、病毒分发和其他类型的恶意软件攻击。

我们将配置我们的 Postfix 服务器来自行拒绝一些垃圾邮件,然后向您介绍一个流行的反垃圾邮件工具 SpamAssassin。SpamAssassin 是一个贝叶斯垃圾邮件过滤器( http://en.wikipedia.org/wiki/Bayesian_spam_filtering )。简单地说,贝叶斯垃圾邮件过滤是一种预测电子邮件中出现单词、短语或其他特征意味着该电子邮件是垃圾邮件的可能性的方法。

根据通过一系列可定制的测试或规则计算的数字分数,每封电子邮件被标记为垃圾邮件或不被标记为垃圾邮件;默认情况下,高于 5.0 的分数被标记为垃圾邮件。根据规则的权重,每个规则会增加或减少该分数。例如,一个规则可能会检查电子邮件的某个特定特征,如果匹配,SpamAssassin 可能会给分配给该电子邮件的分数加 0.5。

贝叶斯垃圾邮件过滤器还可以从用户收到的邮件中学习模式,并可以通过告诉他们哪些邮件是垃圾邮件,哪些不是垃圾邮件来进行训练,这被 SpamAssassin 称为 ham(http://en.wikipedia.org/wiki/Spam_(food`)。从它的学习中得到的数据被添加到一个数据库中,并用于使未来对收到的电子邮件的分析更加准确。

SpamAssassin 在您的主机上作为一个守护进程运行,电子邮件被提交给守护进程,经过分析,然后通过添加一个新的标头被标记为垃圾邮件或非垃圾邮件。

为反垃圾邮件配置 Postfix

在我们配置 SpamAssassin 之前,我们将收紧我们的后缀配置。为此,我们将一些配置选项添加到我们的main.cf配置文件中。这些选项中的大多数会拒绝不符合 SMTP RFC 的电子邮件,例如,拒绝源地址不是有效电子邮件的电子邮件。

我们主要是要更新我们在研究后缀认证时引入的限制列表。表 12-2 列出了这些限制列表。

表 12-2。

Postfix Restriction Lists

| 限制列表 | 描述 | | --- | --- | | `smtpd_client_restrictions` | 客户端连接时的限制 | | `smtpd_helo_restrictions` | 发出`HELO` / `EHLO`命令时的限制 | | `smtpd_sender_restrictions` | 发出`MAIL FROM`命令时的限制 | | `smtpd_relay_restrictions` | 在`smtpd_recipient_restrictions`之前应用于`RCPT TO`的限制 | | `smtpd_recipient_restrictions` | 发出`RCPT TO`命令阻止垃圾邮件时的限制 | | `smtpd_data_restrictions` | 发出`DATA`命令时的限制 |

当特定事件发生时,限制列表被触发;比如客户端连接时检查smtpd_client_ restrictions,客户端发出EHLO命令时检查smtpd_helo_restrictions。如果客户端不发送EHLO命令,这些限制无效。使用postconf命令,我们可以用postconf –emain.cf文件添加指令。我们将添加以下内容:

$ sudo postconf –e 'smtpd_helo_restrictions = reject_unknown_helo_hostname'
$ sudo postconf –e 'smtpd_helo_required = yes'

对于第一个选项smtpd_helo_restrictions,如果它在 DNS 中没有 A 或 MX 记录,我们将拒绝连接的电子邮件服务器。为此,smtpd_helo_required告诉 Postfix 拒绝来自不发送正确的EHLO的客户端的连接,并宣布它们的名称。smtpd_helo_required使连接的 MTA 发送EHLO命令。

在这种情况下,我们将向发件人、收件人和数据限制列表添加一些选项。首先,向main.cf添加smtpd_sender_restrictions选项,如下所示:

$ sudo postconf –e 'smtpd_sender_restrictions = reject_non_fqdn_sender, reject_unknown_sender_domain'

reject_non_fqdn_sender选项拒绝发件人邮件地址格式不正确的电子邮件。这意味着由MAIL FROM命令传递的值必须是有效电子邮件地址的形式,例如ataylor@example.com。诸如Anne TaylorataylorAnne之类的地址或任何您无法回复的地址将不会被接受,该电子邮件也因此被拒绝。

reject_unknown_sender_domain选项拒绝发件人的域名没有 DNS A 或 MX 记录的电子邮件。这通常是因为电子邮件是从一个伪造的域发送的,垃圾邮件也是如此。

我们可以通过添加以下内容来防止我们的电子邮件服务器成为中继服务器:

$ sudo postconf –e 'smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated,
reject_unauth_destination'

这里我们说,我们将允许任何指定的网络(permit_mynetworks)向其他目的地邮件服务器发送邮件。接下来,我们将允许任何经过身份验证的(通过 SASL)客户端(permit_sasl_authenticated)向其他目标服务器发送电子邮件。最后,我们将拒绝任何邮件到未经授权的目的地(reject_unauth_destination)。

接下来,我们想为我们的smtpd_recipient_restrictions选项添加另一个拒绝标准。

$ sudo postconf –e 'smtpd_recipient_restrictions = reject_rbl_client zen.spamhaus.org,
  reject_rhsbl_reverse_client dbl.spamhaus.org,
   reject_rhsbl_helo dbl.spamhaus.org, reject_rhsbl_sender dbl.spamhaus.org, reject_unauth_pipelining '

在本节中,我们试图通过拒绝已知的黑名单服务器和域来减少垃圾邮件的数量。Spamhaus 项目是一个跟踪垃圾邮件和其他网络威胁的非营利组织。它提供了一项服务,您可以通过查询来帮助减少您收到的垃圾邮件数量。这些实时黑洞列表(rbl)将允许您查询他们的数据库中已知的开放中继和坏的往来地址。

reject_rbl_clientzen.spamhaus.org查询 DNS 阻止列表(DNSBL)并拒绝那里的任何肯定条目。reject_rhsbl_reverse_client基于客户端查询dbl.spamhaus.org,客户端是一个域黑名单(DBLs)。它可以根据几个要素(??、IP/DNS、收件人和发件人等)来帮助拒绝垃圾邮件。另外两个 dbl 分别工作在HELO ( reject_rhsbl_helo)和reject_rhsbl_sender

我们包含了reject_unauth_pipelining限制,拒绝使用一种称为管道的特殊技术提交的电子邮件,而不检查是否支持管道。这是垃圾邮件发送者提交电子邮件的常用技巧。

我们还将把这个相同的选项添加到smtpd_data_restrictions中,以捕捉在发出DATA命令时使用相同技术的垃圾邮件发送者。

smtpd_data_restrictions = reject_unauth_pipelining

最后,我们将配置一个与限制列表无关的选项,以阻止一些垃圾邮件。

$ sudo postconf –e 'disable_vrfy_command = yes'

disable_vrfy_command选项禁用 SMTP VRFY命令。VRFY命令允许发送者查询后缀服务器并验证地址是否存在。垃圾邮件发送者使用它来验证地址,黑客偶尔也会在攻击前获取您主机上用户的名称。

Note

您还可以启用其他限制,但这些是最简单、最容易且最不可能将合法电子邮件限制到您的 Postfix 服务器的限制。详见 www.postfix.org/uce.html

完成更改后,重新启动 Postfix 守护进程。

Note

在进行更改后,最好确认您仍然可以发送和接收电子邮件。

安装和配置 SpamAssassin

为了补充我们的后缀更改,我们将安装和配置 SpamAssassin。我们将从安装所需的包开始。在 CentOS 上,我们需要安装spamassassin包,并告诉它自动启动 SpamAssassin 守护进程。

$ sudo yum install spamassassin

在 Ubuntu 上,我们需要安装spamassassinspamc包。

$ sudo aptitude install spamassassin

当 SpamAssassin 打开时,它会启动一个名为spamd的守护进程。我们将创建一个运行这个名为spamd的守护进程的系统用户。

$ sudo useradd –r –m –s /sbin/nologin spamd

–r表示我们需要一个系统用户(UID 低于 1000),–m表示我们需要创建主目录,-s是 shell。前一个是给 CentOS 的;对于 Ubuntu,你可以将 shell 改为/usr/sbin/nologin

然后,我们通过在两个发行版上发出以下命令来启用 SpamAssassin 守护进程:

$ sudo systemctl enable spamassassin

Note

SpamAssassin 软件包有一些额外的必备软件包,当您安装它们时,它们也将安装在 CentOS 和 Ubuntu 上。

对于 SpamAssassin,您可以通过编辑两个发行版上的/etc/mail/spamassassin中的文件来修改配置。您可能想要编辑具有邮件被分类为垃圾邮件的阈值的local.cf文件(required_score)。

我们需要更改的另一个文件是 CentOS 上的/etc/sysconfig/spamassassin或 Ubuntu 上的/etc/default/spamassassin。这个文件控制我们提供给spamd的选项(CentOS 上的SPAMDOPTION,Ubuntu 上的OPTION)。我们将添加运行spamd守护进程的用户和组。

SPAMDOPTIONS="-d -c -m5 -H -u spamd -g spamd"

这里我们将–u–g选项添加到默认设置中。这些缺省值是–d代表后台化,-c代表创建用户偏好文件,-m代表子进程的数量,-H代表指定不同的帮助目录(针对第三方服务)。

然后我们需要启动 SpamAssassin spamd守护进程。我们使用systemctl命令。

$ sudo systemctl start spamassassin

Tip

与 Postfix 一样,在对配置进行更改后,重新启动 SpamAssassin 是一个好主意。

为 SpamAssassin 配置后缀

有许多不同的方法可以将 SpamAssassin 这样的垃圾邮件过滤器集成到您的电子邮件环境中。一种是在将用户收到的电子邮件发送给用户之前,使用 MDA 对其进行扫描。然后,垃圾邮件过滤器通常会在电子邮件中添加邮件头,MDA 会检测到邮件头,并将垃圾邮件定向到特定的文件夹。

另一种方法是在电子邮件仍在 MTA 中时对其进行分析。这允许我们将邮件从 Postfix 的邮件队列传递到 SpamAssassin。然后,电子邮件被扫描并发送回 Postfix 进行发送。然后,客户端或 MDA 可以利用该扫描的结果(同样是添加到电子邮件的标题)来确定如何处理该电子邮件。在图 12-3 中,你可以看到通过 Postfix 的电子邮件的建议流程。

A185439_2_En_12_Fig3_HTML.jpg

图 12-3。

SpamAssassin and Postfix filtering

我们将选择后一种方法,因为对于大多数环境来说,这种方法效率更高,可伸缩性更强。

首先,我们更新master.cf文件以向 SpamAssassin 发送电子邮件。为此,我们调整master.cf文件中的smtp服务,如下所示:

smtp     inet     n     -     n     -     -     smtpd
    -o content_filter=spamassassin

这将添加行-o content_filter=spamassassin(-o应该用空格缩进,以显示它是前一行的后续)。content_filter选项告诉 Postfix,我们希望所有发送到smtp服务的电子邮件都被发送到一个名为spamassassin的过滤器。

我们现在需要定义这个过滤器。为此,我们将过滤器添加到master.cf文件的底部。如下定义过滤器:

spamassassin     unix     -     n     n     -     -     pipe
 user=spamd argv=/usr/bin/spamc -e /usr/sbin/sendmail -oi -f ${sender} ${recipient}

这在master.cf file中创建了一个类型为unix的新服务,这是一个 Unix 套接字,它调用另一个名为pipe的 Postfix 守护进程,这个守护进程向外部命令发送电子邮件。在接下来的几行中(再次缩进),我们指定外部命令。

我们指定将要运行的外部命令、运行该命令的用户,然后指定要传递给该命令的命令和参数。我们调用spamc命令,这个二进制文件连接到spamassassin守护进程,提交我们的电子邮件,然后接收扫描结果。

对于spamc命令,我们传递了-e参数。这个参数(必须在命令行的最后指定)告诉spamc命令在电子邮件被扫描后如何处理它。在这种情况下,我们将它提交给/usr/sbin/sendmail命令,这是 Postfix 命令,它将我们的电子邮件传递回 Postfix 以传递给用户(这里的sendmail程序是指向/usr/sbin/sendmail.postfix的符号链接)。

我们还为sendmail命令指定了一些选项。–oi选项告诉sendmail命令,当它发现一行只有一个句点(.)时,不要停止处理电子邮件(还记得我们之前的测试电子邮件在发送一行有句点的行时就结束了)。这是因为电子邮件中可能有一行带有句点,可能并不表示电子邮件的结束。-f ${sender}选项确保电子邮件的发件人被发送到 Postfix。最后,${recipient}选项包含电子邮件的收件人,因此 Postfix 知道该将电子邮件发送给谁。

测试 SpamAssassin

为了测试 SpamAssassin,让我们使用swaks工具发送一封电子邮件,这是一个可以通过包管理器安装的电子邮件测试工具,并检查结果。

$ swaks -tls -a -au ataylor -ap password -t jsmith@example.com -f
ataylor@example.com --body /usr/share/doc/spamassassin-3.4.0/sample-spam.txt

这个命令连接到您的邮件服务器,并从经过 SASL 认证的用户ataylor向用户jsmith@example.com发送一封电子邮件。我们还添加了--body选项来指定我们电子邮件的内容。在本例中,我们使用 SpamAssassin 包中提供的垃圾邮件示例。该电子邮件位于/usr/share/doc/spamassassin-<version>/目录中(用您的 SpamAssassin 版本替换版本,例如 3.4.0)。这个垃圾邮件样本肯定会触发 SpamAssassin 的垃圾邮件过滤器,从而测试我们的垃圾邮件检测。

如果我们检查我们的 Postfix 日志,我们可以看到这封电子邮件被接收和处理,如清单 12-14 所示。

$ sudo less /var/log/mail.log
Aug 27 08:11:35 ip-10-0-10-154 postfix/qmgr[12743]: 564B21866089: from=<ataylor@example.com>, size=1316,
  nrcpt=1 (queue active)
Aug 27 08:11:35 ip-10-0-10-154 spamd[12345]: spamd: connection from localhost [::1]:46002 to port 783, fd 6
Aug 27 08:11:35 ip-10-0-10-154 spamd[12345]: spamd: setuid to mail succeeded
Aug 27 08:11:35 ip-10-0-10-154 spamd[12345]: spamd: creating default_prefs: /home/spamd/.spamassassin/user_prefs
Aug 27 08:11:35 ip-10-0-10-154 spamd[12345]: spamd: failed to create readable default_prefs:
  /var/spool/mail/.spamassassin/user_prefs
Aug 27 08:11:35 ip-10-0-10-154 spamd[12345]: spamd: processing message (unknown) for mail

:8
Aug 27 08:11:36 ip-10-0-10-154 spamd[12345]: spamd: identified spam (999.1/5.0) for mail:8 in 0.3 seconds, 1280 bytes.
Aug 27 08:11:36 ip-10-0-10-154 spamd[12345]: spamd: result: Y 999 - ALL_TRUSTED,GTUBE,MISSING_MID
  scantime=0.3,size=1280,user=mail,uid=8,required_score=5.0,rhost=localhost,raddr=::1,rport=46002,
  mid=(unknown),autolearn=no autolearn_force=no
Aug 27 08:11:36 ip-10-0-10-154 spamd[12344]: prefork: child states: II
Aug 27 08:11:36 ip-10-0-10-154 postfix/pipe[12758]: 564B21866089: to=<jsmith@example.com>, relay=spamassassin,
  delay=0.89, delays=0.57/0.01/0/0.3, dsn=2.0.0, status=sent (delivered via spamassassin service)
Aug 27 08:11:36 ip-10-0-10-154 postfix/qmgr[12743]: 564B21866089: removed
Aug 27 08:11:36 ip-10-0-10-154 postfix/pickup[12742]: 0AF49181186C: uid=8 from=<ataylor@example.com>
Aug 27 08:11:36 ip-10-0-10-154 postfix/cleanup[12757]: 0AF49181186C: message
  id=<20160827121136.0AF49181186C@mail.example.com>
Aug 27 08:11:36 ip-10-0-10-154 postfix/qmgr[12743]: 0AF49181186C: from=<ataylor@example.com>, size=2091,
   nrcpt=1 (queue active)
Aug 27 08:11:36 ip-10-0-10-154 postfix/local[12762]: 0AF49181186C: to=<jsmith@example.com>, relay=local,
  delay=0.04, delays=0.03/0.01/0/0, dsn=2.0.0, status=sent (delivered to mailbox)
Aug 27 08:11:36 ip-10-0-10-154 postfix/qmgr[12743]: 0AF49181186C: removed
Listing 12-14.Postfix Logs with SpamAssassin

在清单 12-14 中,我们已经连接到我们的 Postfix 服务器,通过了身份验证,并提交了我们的电子邮件。在交易过程中,电子邮件被提交给 SpamAssassin(参见带有spamd的行,这是 SpamAssassin 守护进程)。

Aug 27 08:11:35 ip-10-0-10-154 spamd[12345]: spamd: connection from localhost [::1]:46002 to port 783, fd 6
Aug 27 08:11:35 ip-10-0-10-154 spamd[12345]: spamd: setuid to mail succeeded
Aug 27 08:11:35 ip-10-0-10-154 spamd[12345]: spamd: creating default_prefs: /home/spamd/.spamassassin/user_prefs
Aug 27 08:11:35 ip-10-0-10-154 spamd[12345]: spamd: processing message (unknown) for mail:8
Aug 27 08:11:36 ip-10-0-10-154 spamd[12345]: spamd: identified spam (999.1/5.0) for mail:8 in 0.3 seconds, 1280 bytes.
Aug 27 08:11:36 ip-10-0-10-154 spamd[12345]: spamd: result: Y 999 - ALL_TRUSTED,GTUBE,MISSING_MID
 scantime=0.3,size=1280,user=mail,uid=8,required_score=5.0,rhost=localhost,raddr=::1,rport=46002,
  mid=(unknown),autolearn=no autolearn_force=no

我们可以看到 SpamAssassin 守护进程从服务器获得一个连接,将我们的用户更改为mail用户,并处理我们的电子邮件。它返回了一行指示该邮件是垃圾邮件的信息,得分为999.1/5.0(明显大于 5.0,因此是垃圾邮件)。

SpamAsssassin 的工作原理是对已知的垃圾邮件模式进行多次检查。

  • 标题测试
  • 身体短语
  • 贝叶斯过滤器
  • 白名单/黑名单
  • 声誉检查
  • 协作检查
  • rbl 和 DNSRBLs

每一个都会增加总分数,如果超过 5 分,就被认为是垃圾邮件。对之前的测试垃圾邮件触发的检查列表为ALL_TRUSTED,GTUBE,MISSING_MID。看到类似ADVANCE_FEE_5_NEW_FRM_MNY, FILL_THIS_FORM,FILL_THIS_FORM_LOAN这样的垃圾邮件,你会看到其他被触发的。

您还可以训练您的贝叶斯过滤器,以更好地检测来自火腿的垃圾邮件。如果您有一个已知充满垃圾邮件的邮箱,您可以通过发出以下命令来训练您的过滤器:

$ sudo sa-learn --showdots --mbox --spam file-of-spam

您还应该向您的过滤器传授有关 ham 的知识,为此,您应该发出以下命令:

$ sudo sa-learn --showdots --mbox --ham file-of-ham

我们还可以查看我们的实际电子邮件,了解 SpamAssassin 添加的邮件头的详细信息。

$ sudo cat /home/jsmith/Maildir/new/1472350940.Vca02I203b0aaM132480.mail.example.com
Return-Path: <ataylor@example.com>
X-Original-To: jsmith@example.com
Delivered-To: jsmith@example.com
Received: by mail.example.com (Postfix, from userid 993)
id 1F0C4181186F; Sat, 27 Aug 2016 22:22:20 -0400 (EDT)
X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
mail.example.com
X-Spam-Flag: YES
X-Spam-Level: **************************************************
X-Spam-Status: Yes, score=999.1 required=5.0 tests=ALL_TRUSTED,GTUBE,
MISSING_MID autolearn=no autolearn_force=no version=3.4.0
X-Spam-Report:
* -1.0 ALL_TRUSTED Passed through trusted hosts only via SMTP
* 1000 GTUBE BODY: Generic Test for Unsolicited Bulk Email
*  0.1 MISSING_MID Missing Message-Id: header
Received: from me.here (me.here [63.38.238.98])
for <jsmith@example.com>; Sat, 27 Aug 2016 22:22:19 -0400 (EDT)
Date: Sat, 27 Aug 2016 22:22:17 -0400
To: jsmith@example.com
From: ataylor@example.com
Subject: [SPAM] test Sat, 27 Aug 2016 22:22:17 -0400
X-Mailer: swaks v20130209.0 jetmore.org/john/code/swaks/
X-Spam-Prev-Subject: test Sat, 27 Aug 2016 22:22:17 -0400
Message-Id: <20160828022220.1F0C4181186F@mail.example.com>

Subject: Test spam mail (GTUBE)
Message-ID: <GTUBE1.1010101@example.net>
Date: Wed, 23 Jul 2003 23:30:00 +0200
From: Sender <sender@example.net>
To: Recipient <recipient@example.net>
Precedence: junk
MIME-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

我们可以看到 SpamAssassin 增加了 6 个头球。

  • SpamAssassin 版本
  • X-Spam-Level:星号表示总分
  • X-Spam-Flag:仅当电子邮件被识别为垃圾邮件时出现
  • X-Spam-Status:垃圾邮件状态(NoYes),电子邮件的总得分,以及检查的垃圾邮件测试列表
  • X-Spam-Report:垃圾邮件分数是如何得出的
  • X-Spam-Prev-Subject:上一个垃圾邮件主题标题

列表中的前三个被添加到所有电子邮件中;只有当电子邮件被认为是垃圾邮件时,才会添加最后三个。

获得 SpamAssassin 的帮助

与 Postfix 一样,获得 SpamAssassin 的帮助也很容易。网上有丰富的文档以及有用的、广泛的社区。寻求帮助的最佳起点是位于 http://spamassassin.apache.org/ 的 SpamAssassin 主页。该页面包括维基、常见问题和文档。还可以在 http://wiki.apache.org/spamassassin/MailingLists 加入活动邮件列表,在 https://issues.apache.org/SpamAssassin/index.cgi 提交 bug。

您可以通过在我们之前描述的spamd守护进程选项中添加一个-D来提高spamd的调试级别。这将增加您的日志记录级别,并让您深入了解spamd正在做什么。

请记住,如果您提交问题或错误,您应该包括以下信息:

  • 您的 SpamAssassin 和 Perl 版本(运行带有--version选项的spamassassin命令)
  • 你的平台(运行uname -a
  • 生成的任何日志消息(在 Ubuntu 上的/var/log/mail.log文件中或在 CentOS 上的/var/log/maillog文件中)

在 Freenode IRC 服务器( http://freenode.net/ )上还有一个名为#spamassassin的 IRC 频道,你可以在那里寻求帮助。

如何处理垃圾邮件?

如果我们的电子邮件被 SpamAssassin 识别为垃圾邮件,我们可以通过添加“[SPAM]”来修改主题行,但仅此而已。它仍然作为新邮件发送到用户的收件箱中。在大多数情况下,人们把他们的垃圾邮件放在一个单独的文件夹中进行审查,通常会删除。有些人拒绝或删除任何标记为垃圾邮件的电子邮件,导致可能重要的电子邮件丢失。包括 SpamAssassin 在内的垃圾邮件检测工具并不可靠,有时会产生误报。这意味着合法的电子邮件可以被标记为垃圾邮件。如果您删除了您的垃圾邮件,您就失去了这封电子邮件。将它存储在文件夹中一段时间可以让您潜在地找到并检索这些误报。

我们将使用第一种方法,将我们的垃圾邮件移动到我们的Maildir目录中的一个特殊文件夹中,供以后查看。为此,我们可以使用两种主要方法来利用新获得的邮件头,将电子邮件移动到我们希望的位置。这些方法是

  • 使用本地邮件传输协议(LTMP)代理,如 Dovecot
  • 使用 MDA,如procmailmaildrop
  • 使用邮件客户端规则或过滤

使用鸽笼

我们以前见过 Dovecot 来配置我们的 SMTP 认证。我们现在将对其进行配置,以将邮件从 MTA 传输到用户的主目录中。Dovecot 允许我们使用 Sieve ( http://sieve.info/ )来为我们的用户过滤哪些电子邮件被发送到哪里。

为了使用 Dovecot,我们将设置一个 LMTP(本地邮件传输协议),这是一个由 Dovecot 主进程启动的长期运行的进程。这描述了这个 Dovecot 解决方案与procmailmaildrop解决方案之间的区别,这两个解决方案是在每封邮件发送时触发的二进制文件。此外,LMTP 可以运行在单独的服务器上,因为它可以从 TCP 和 unix 套接字读取数据。

第一步是下载 Dovecot Sieve 支持包。对于 CentOS,我们将安装:

$ sudo yum install dovecot-pigeonhole

对于 Ubuntu,我们将安装以下软件:

$ sudo aptitude install dovecot-sieve dovecot-lmtp

我们配置 Dovecot 开始接受与 LMTP 的连接。我们通过向/etc/dovecot/dovecot.conf添加以下内容来实现。

protocols = lmtp

现在我们需要配置下面的文件,/etc/dovecot/conf.d/20-lmtp.conf:

protocol lmtp {
  postmaster_address = postmaster@example.com
  mail_plugins = quota sieve
}

我们需要将 LMTP 进程或 Unix 套接字添加到 Dovecot 主进程中,以便 Postfix 可以通过它传递消息。我们像这样编辑/etc/dovecot/conf.d/10-master.conf文件:

service lmtp {
 unix_listener /var/spool/postfix/private/dovecot-lmtp {
   group = postfix
   mode = 0600
   user = postfix
  }
}

最后,我们需要告诉 Dovecot 在发送邮件时将域名从用户名中删除。LMTP 进程从RCPT TO:获取用户名,我们需要剥离域名。否则,它会尝试将邮件发送给jsmith@example.com,而不仅仅是jsmith(这将与550 5.1.1 User doesn't exist发生错误)。我们在/etc/dovecot/conf.d/10-auth.conf文件中这样做。

auth_username_format = %Ln

现在我们需要编辑后缀配置,告诉它使用 LMTP 套接字。我们通过以下方式做到这一点:

$ postconf –e 'mailbox_transport = lmtp:unix:private/dovecot-lmtp'

我们现在需要重新启动 Postfix 和 Dovecot,以通过以下方式推动更改:

$ sudo systemctl restart dovecot
$ sudo systemctl restart postfix

用筛子过滤

用 Sieve 过滤邮件的语法相当简单。您可以在用户的主目录中设置全局过滤器或单个过滤器。在本例中,我们将向您展示如何将被标记为垃圾邮件的邮件移动到您的垃圾邮件文件夹中,以便以后检查。

Sieve 有几个易于理解的命令和条件。你需要那些你需要的在你的筛子文件的顶部,你通常在∼/.dovecot.sieve中找到。这里有一个例子:

require ["fileinto" ];

还有许多其他命令可以使用,您可以通过运行以下命令找到可用的列表:

$ doveconf -n managesieve_sieve_capability
fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date ihave

我们将使用条件语句if将垃圾邮件存储到垃圾邮件文件夹中。我们将测试 SpamAssassin 提供给我们的标题。

require ["fileinto"];
if header :contains "X-Spam-Flag" "YES" {
        fileinto "Spam";
}

这表示如果标题X-Spam-Flag包含单词YES,则将邮件归档到Spam文件夹中。你可以看到它是多么简单。

在测试之前,我们需要确保Spam文件夹存在。我们可以通过使用doveadm命令来做到这一点。首先我们列出来看看文件夹是否存在。

$ doveadm mailbox list -u jsmith
INBOX

在本例中没有,因此我们将创建它。

$ doveadm mailbox create -u jsmith Spam
$ doveadm mailbox list -u jsmith
Spam
INBOX

接下来,当我们像以前一样发送测试垃圾邮件时,我们将看到以下内容:

Aug 28 05:17:10 ip-10-0-10-154 dovecot: lmtp(18407): Connect from local
Aug 28 05:17:10 ip-10-0-10-154 dovecot: lmtp(18407, jsmith): Kl1sKRaswlfnRwAArNTl4g: sieve: msgid=<20160828091710.A40A21811885@mail.example.com>: stored mail into mailbox 'Spam'
Aug 28 05:17:10 ip-10-0-10-154 dovecot: lmtp(18407): Disconnect from local: Successful quit

太好了。我们将邮件发送到我们的邮箱,并让它过滤掉垃圾邮件。

抗病毒素

我们都应该知道,病毒是恶意代码,旨在攻击您的主机、窃取您的数据或危害您组织的其他方面。虽然在 Linux 发行版上极其罕见,但一些病毒已经瞄准了 Linux 发行版。但是我们并不特别担心病毒通过电子邮件攻击我们的 Linux 主机;相反,我们担心的是病毒可能通过电子邮件传播到其他更易受感染的主机上,如 Microsoft Windows 桌面,或者从您的组织传播到其他组织。

我们将介绍一个名为 ClamAV 的应用程序,并向您展示如何将它与 Postfix 集成为一个邮件过滤器。ClamAV 是一个开源防病毒引擎,很像赛门铁克和迈克菲等公司的类似工具。与这些商业产品不同,ClamAV 软件及其更新签名是免费的。

Note

ClamAV 使用称为签名的特殊规则来扫描您收到的电子邮件中看起来像病毒的数据。每个特征包含关于特定病毒的信息,例如,病毒文件包含的一串数据,当发现时,告诉 ClamAV 已经检测到病毒。

我们将向您展示如何将 ClamAV 作为一个 milter 集成到 Postfix 中。Postfix 允许将邮件发送到过滤器,然后重新排队。有两种类型的后缀分隔符,仅 smtpd 和非 smtpd。我们将向您展示如何设置 smtpd 过滤器。

network -> smtpd -> filter -> smtpd -> delivery

前面是我们将向您展示的流程的简化版本。

安装 ClamAV

首先,您需要安装和配置 ClamAV 扫描器及其守护进程,名为clamd。您还需要安装一个名为 FreshClam 的更新工具,该工具可以自动下载和更新 ClamAV 用于检测病毒的病毒签名。

在 CentOS 上,安装 ClamAV 的过程是通过以下软件包完成的:

$ sudo yum install -y clamav-scanner clamav-update clamav-server-systemd clamav-milter-systemd sendmail-milter

在 Ubuntu 上,你安装clamavclamav-daemon包。一些附加的软件包也可以作为这些软件包的先决条件进行安装。

$ sudo aptitude install clamav clamav-daemon clamav-milter

同样,发行版将配置文件放在不同的位置。对于 CentOS,我们在/etc/mail/clamav-milter.conf中找到clamav-milter,对于clamav守护进程,我们在/etc/clam.d/scan.conf中找到它们。对于 Ubuntu,你可以在/etc/clamav找到文件。

对于 CentOS,我们希望从/etc/mail/clamav-milter.conf中删除单词 Example,并设置以下内容:

MilterSocket /var/run/clamav-milter/clamav-milter.socket
MilterSocketGroup mail
MilterSocketMode 660
AllowSupplementaryGroups yes
ClamdSocket unix:/var/run/clamd.scan/clamd.sock
OnInfected Accept
AddHeader Add
ReportHostname mail.example.com

在前面几行中,我们已经设置了一些配置选项,比如用于clamav-milter的套接字文件和我们与扫描仪(clamd)对话的套接字。值得注意的是OnInfected Accept。通常我们会将此设置为Quarantine,但是在接下来的例子中,我们希望我们的受感染邮件被发送,这样我们就可以看到我们添加的邮件头。

对于 CentOS 上的扫描仪,我们必须编辑/etc/clamd.d/scan.conf并确保设置以下内容,如果移除了Example:

LocalSocket /var/run/clamd.scan/clamd.sock

Ubuntu 不需要太多配置。文件/etc/clamav/clamav-milter.conf需要以下内容:

MilterSocket inet:7357@localhost
OnInfected Accept
AddHeader Add

同样,为了测试,我们接受受感染的文件并添加文件头。此外,对于 Ubuntu,我们将设置 milter 监听环回地址,而不是像在 CentOS 设置中那样使用命名套接字。两种发行版都可以使用这种方法。我们不需要更改/etc/clamav/clamd.conf,尽管该文件包含您可能想要查看的不同扫描设置(CentOS 中的/etc/clamd/scan.conf)。

现在,我们需要将postfixclamilt用户添加到 CentOS 上的mail组。

$ sudo usermod –aG mail postfix && sudo usermod –aG mail clamilt

而对于 Ubuntu,除非我们使用的是命名套接字,否则不需要做任何改动。但是我们需要对后缀main.cf文件做一些修改来反映我们的smtpd_milter。为此,我们向 CentOS 主机添加了以下内容:

milter_default_action = accept
smtpd_milters = unix:/var/run/clamav-milter/clamav-milter.socket

因为使用 Ubuntu,我们通过回送地址与 milter 对话,所以我们需要:

milter_default_action = accept
smtpd_milters = inet:7357

最后,在 CentOS 上,我们需要对 SELinux 策略进行一些调整。以下要求安装policycoreutils-python包。通常你可以启动你的服务,等待它失败,检查/var/log/audit/audit.log,并运行audit2allow命令来修复任何 SELinux 权限问题。在本例中,我们将使用目标条目文件来生成我们的策略包文件,然后将它加载到 SELinux 中(有关 SELinux 的更多信息,请参见即将到来的“SELinux 和 Apparmor”侧栏)。我们的clamav-write.te目标条目文件的内容如下所示:

module clam-write 1.0;

require {
       type unconfined_t;
       type var_run_t;
       type postfix_smtpd_t;
       type init_t;
       class sock_file write;
       class unix_stream_socket connectto;
}

#============= postfix_smtpd_t ==============
allow postfix_smtpd_t init_t:unix_stream_socket connectto;
allow postfix_smtpd_t unconfined_t:unix_stream_socket connectto;
allow postfix_smtpd_t var_run_t:sock_file write;

第一行由模块名和版本组成。部分包含了我们需要的不同类型和类别。最后,我们有允许smtpd守护进程连接和写入 Unix 套接字(如/run/clamav-milter/clamav-milter.socket)的代码行。我们现在必须编译策略包,将其加载到 SELinux 策略配置中。为此,我们首先使用以下命令编译一个策略模块文件:

$ sudo checkmodule -M -m -o clamav-write.mod clamav-write.te

checkmodule命令接受以下参数,-M启用 LSM 支持,-m生成模块二进制文件,–o是输出文件名。现在,我们使用以下命令创建策略包:

semodule_package –o clamav-write.pp -m clamav-write.mod

我们通过将模块(-m)文件传递给semodule_package命令来创建策略包,并将其(-o)写入clamav-write.pp文件。现在我们准备将这个策略包加载到 SELinux 中。这通过简单地发出以下命令来实现:

$ sudo semodule –i clamav-write.pp

我们已经告诉semoduleclamav-write.pp策略安装到 SELinux 中(-i)。我们现在准备启动 ClamAV 服务。

一旦安装了所有相关的包,您需要确保 ClamAV 守护进程已经启用并启动。让我们启动clamd服务。

$ sudo systemctl enable clamd@scan && sudo systemctl start clamd@scan (CentOS)
$ sudo systemctl enable clamav-daemon && sudo systemctl start clamav-daemon (Ubuntu)

接下来,我们将启动clamav-milter服务和freshclamd服务,它们轮询最新的病毒签名更新。

$ sudo systemctl enable clamav-milter && sudo systemctl start clamav-milter (Both)
$ sudo systemctl enable clamav-freshclam && sudo systemctl start clamav-freshclam (Ubuntu)

我们现在应该能够更多地了解 ClamAV 的配置。

SELinux and Apparmor

在 Linux 中,通过 Linux 安全模块(LSM)提供了不同类型的访问控制,它是 Linux 内核的一部分。其中一个叫做强制访问控制(MAC ),由 SELinux 在基于 RHEL 和 Debian 的发行版上提供,由 Apparmor 在 Ubuntu 和 Debian 上提供。每一种都提供了允许或限制访问文件、进程和网络资源的能力,当然,每一种都有不同的做法。

MAC 不同于自主访问控制(DAC),自主访问控制是 Linux 操作系统的核心,也是我们比较熟悉的一个。DAC 允许资源(例如文件)的所有者设置该文件的权限。另一方面,MAC 规定了内核中资源的权限,只有通过将访问策略从用户空间加载到内核中,才能更改权限。

SELinux 由美国国家安全局(NSA)开发,其工作原理是标记系统上的每个对象,然后使用 SELinux 安全模块来验证对资源的访问。它会将访问违规记录到审计日志中,通常是/var/log/audit/audit.log。从那里,您可以使用audit2allow命令来创建新的目标条目文件和策略包。目标入口文件(通常以.te结尾)用纯文本解释策略。由此我们可以创建策略包(以.pp结尾),并将其加载到内核中。

type=AVC msg=audit(1472477657.569:13159): avc:  denied  { write } for  pid=24859 comm="smtpd" name="clamav-milter.socket" dev="tmpfs" ino=281983 scontext=system_u:system_r:postfix_smtpd_t:s0 tcontext=unconfined_u:object_r:var_run_t:s0 tclass=sock_file

这里我们可以看到日志 SELinux 在audit.log中的输出。我们可以将其分为三个部分,所需的访问、源上下文和目标上下文。

type=AVC是访问向量缓存,然后是审计时间戳。然后是被拒绝的avc动作,write ( denied { write }),由命令smtpd尝试写入clamav-milter.socket。源上下文(scontext)是system_u:system_r:postfix_smtpd_t:s0,目标上下文(tcontext)是tcontext=unconfined_u:object_r:var_run_t:s0。这意味着类型为postfix_smtpd_t的进程试图访问类型为var_run_t的套接字文件,但是失败了。解决方案是允许后缀smtpd进程对套接字文件进行写操作。如果我们收集这些消息并将它们传递给audit2allow程序,我们将能够创建允许正确访问所需的策略包。

sudo grep "denied" /var/log/audit/audit.log |grep clamav | audit2allow -M clamav-write

该命令将创建clamav-write.teclamav-write.pp,然后您可以通过以下方式加载它们:

sudo semodule -i clamav-write.pp

对于 Apparmor 来说,这是一个不同的概念。SELinux 在对象上使用标签,而 Apparmor 对路径名应用安全策略。您可以通过定义应用程序需要什么样的权限来创建应用程序的概要文件,并且您可以使用 Apparmor 附带的工具很容易地管理它。

要查看 Apparmor 的状态,可以发出以下命令:

$ sudo apparmor_status
apparmor module is loaded.
15 profiles are loaded.
15 profiles are in enforce mode.
   /sbin/dhclient
   /usr/bin/freshclam
   /usr/bin/lxc-start
...
4 processes have profiles defined.
4 processes are in enforce mode.
   /usr/bin/freshclam (2153)
   /usr/sbin/clamd (11998)
   /usr/sbin/mysqld (2997)
0 processes are in complain mode.
0 processes are unconfined but have a profile defined.

您可以看到已经有一个针对clamd服务的策略。这些概要文件与它们相关的包一起安装,包clamav-daemon提供了clamd概要文件。

要创建您自己的概要文件,您需要创建一个测试计划,这是一个运行和执行您正在概要分析的流程的计划。然后用aa-genprof命令运行程序。在回答了关于应用程序访问的问题之后,就构建了概要文件。然后可以将生成的概要文件复制到/etc/apparmor.d/目录中,并通过发出以下命令来启用它:

$ sudo aa-enforce /etc/apparmor.d/<your profile>

Apparmor 发布在 Ubuntu 和 OpenSUSE 上,但是可以安装在 Debian 和 CentOS 上。

我们只是触及了这些主题。阅读以下文件对你有好处:

在任何面向互联网的系统中使用这两者中的任何一个都是明智的想法。

配置 ClamAV

你通常不需要改变 ClamAV 的任何配置选项,但是你应该知道一点它是如何配置的。在 CentOS 上,ClamAV 守护程序通过/etc/clamd.d/scan.conf文件进行配置。scan.conf文件配置 ClamAV 守护进程。软件包安装过程还为 FreshClam 更新工具创建一个 cron 条目(在/etc/cron.daily目录中),该工具每天更新 ClamAV 的签名一次。

在 Ubuntu 上,ClamAV 守护进程通过/etc/clamav/clamd.conf配置文件配置,而freshclam更新守护进程通过/etc/clamav/freshclam.conf配置文件配置。与 Ubuntu 上的cron作业不同,FreshClam 服务是作为一个守护进程运行的,每天会多次尝试下载任何可用的签名。

在 CentOS 和 Ubuntu 上,运行 ClamAV 守护程序将创建一个 Unix 套接字文件。ClamAV milter 使用这种特殊类型的文件与防病毒扫描程序进行通信。电子邮件被提交到套接字,进行扫描,然后返回到 ClamAV milter,并评估它是否是病毒。然后,该评估以X-Spam-Virus标题的形式添加到扫描的电子邮件中。

在 CentOS 上,这个插座默认位于/var/run/clamd.scan/clamd.sock中,您可以在这里看到它的列表:

$ sudo ls -l /var/run/clamd.scan/clamd.sock
srw-rw-rw-. 1 clamscan clamscan 0 Sep  3 04:07 /var/run/clamd.scan/clamd.sock

在 Ubuntu 上,socket 位于/var/run/clamav目录下,名为clamd.ctl

$ sudo ls -lart /var/run/clamav/clamd.ctl
srw-rw-rw- 1 clamav clamav 0 Sep  3 11:20 /var/run/clamav/clamd.ctl

用 ClamAV 测试后缀

现在您已经启用了 ClamAV milter,您需要测试您的接收电子邮件是否正在进行病毒扫描。为此,使用swaks命令和一个特殊文件,该文件包含一个名为eicar.txt的测试病毒签名,如下所示:

$ swaks -tls -a -au ataylor -ap password -t jsmith@example.com -f ataylor@example.com --body eicar.txt

我们再次使用了swaks命令向jsmith@example.com发送电子邮件。文件eicar.txt包含这个字符串,它将触发病毒扫描程序。

X5O!P%@AP4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

Note

您可以在您的环境中用适当的值替换一个用户来测试这一点。

该电子邮件将由您的 MTA 接收和处理,然后传递给 ClamAV milter,ClamAV milter 将与 ClamAV 扫描器对话,然后根据您的配置传递给 SpamAssassin 进行分析。电子邮件将被提交到用户jsmith的邮箱,然后您可以检查电子邮件标题的内容。在清单 [12-15 中,您可以看到我们刚刚发送的电子邮件的标题。

X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
X-Spam-Level: *
X-Spam-Status: No, score=-0.9 required=5.0 tests=ALL_TRUSTED,MISSING_MID
X-Mailer: swaks v20130209.0 jetmore.org/john/code/swaks/
X-Virus-Scanned: clamav-milter 0.99.2 at mail.example.com
X-Virus-Status: Infected (Eicar-Test-Signature)
Listing 12-15.E-mail Headers After SpamAssassin with ClamAV Scan

在清单 12-15 中,您可以看到一个名为X-Spam-Virus的标题被添加到了电子邮件中。清单 12-15 中的头有一个值Infected和它匹配的签名。如果检测到病毒,标题会被标记为Clean

如何处理受感染的电子邮件?

与使用X-Spam-Status标题一样,您可以使用此标题以不同方式处理被识别为包含病毒的电子邮件,例如,如果您想要将所有此类电子邮件移动到一个名为Viruses的单独文件夹中。您可以使用之前用于将垃圾邮件分类到单独文件夹中的筛选配置来完成此操作。要使用 Sieve 做到这一点,您可以向用户的∼/.dovecot.sieve文件中添加一个如下所示的配方:

require ["fileinto"];
if header :contains "X-Virus-Status" "" {
        fileinto "Spam";
}
if not header :contains "X-Virus-Status" "Clean" {
        fileinto "Virus";
}

但是,最好不要将受感染的文件传递给用户。您可以通过设置clamav-milter.conf文件中的OnInfected Quarantine值来隔离邮件服务器上的受感染文件。我们将它提前设置为 accept,这样我们就可以看到邮件到达我们的收件箱。然而,现在明智的做法是阻止那些邮件被别人误打开。

获得 ClamAV 的帮助

您可以找到各种资源来对 ClamAV 进行故障诊断。你应该从 ClamAV 主页开始( www.clamav.net/ )。在同一个网站上,你会找到各种可用的支持资源( www.clamav.net/support/ ),你也可以加入 ClamAV 邮件列表( www.clamav.net/support/ml/ )。你可以在 www.clamav.net/bugs/ 找到当前 bug 列表。

请记住,如果您提交问题或错误,您应该包括以下信息:

  • 您的 ClamAV 版本(运行带有--version选项的clamscan命令)
  • 你的平台(运行uname -a
  • 生成的任何日志消息(在 Ubuntu 上的/var/log/clamav/clamav.log或/var/ log/clamav/freshclam.log文件中,或者在 CentOS 上的/var/log/clamav/clamd.log/var/log/clamav/freshclam.log文件中)

在 Freenode IRC 服务器( http://freenode.net/ )上还有一个名为#clamav的 IRC 频道,你可以在那里寻求帮助。

SPF 和 DKIM,控制你的电子邮件

我们可以采取进一步的行动来帮助击败垃圾邮件。这两种方法本身都不能阻止垃圾邮件,但每一种都是帮助垃圾邮件难以通过的工具的一部分。它们确实有助于阻止垃圾邮件发送者在发送邮件时伪造您的域名。

发件人策略框架(SPF)用于标识可以代表您的域发送电子邮件的电子邮件服务器。域密钥识别邮件(DKIM)是一种通过根据您的公开密钥验证邮件头来识别来自您的服务器的邮件的方法。

防晒系数

SPF 为您的域使用添加到 DNS 中的文本记录。您的 SPF 记录告诉接收您的电子邮件的电子邮件服务器,它们应该根据发送邮件的服务器来接受还是拒绝该邮件。让我们举一个例子,你通过 MailChimp 或 Sendgrid 或其他大量电子邮件服务向你的客户发送公司的营销通讯。当电子邮件进入客户的电子邮件服务器时,它将来自批量电子邮件提供商的电子邮件服务器之一。客户的电子邮件服务器如何确定它真的来自您的营销团队?

使用 SPF,我们可以告诉客户的电子邮件服务器,它应该代表我们接受来自这个批量电子邮件服务的电子邮件。这样,我们可以说电子邮件服务器1.bulk.mailer.net可以为example.com域发送邮件。

让我们来看看我们的example.com域名的 DNS 记录示例。我们将为example.com域添加一个 TXT 记录。

example.com.    IN  TXT “v=spf1 mx -all"

这里我们正在编辑example.com的区域文件并添加 TXT 记录。您也可能通常将此视为已经贬值的 SPF 记录。TXT 记录本身包含 SPF 的版本(spf1)、哪些电子邮件服务器可以为我们的域发送(所有列出的 MX 服务器),以及我们是否应该接受或拒绝来自不在该列表中的服务器的电子邮件(-all)。

在这个例子中,我们说任何被列为 MX 记录的服务器都能够代表我们发送邮件。那么,我们如何允许1.bulk.mailer.net代表我们发送电子邮件呢?我们可以指定主机名、IPv4 或 IPv6、CIDRs 以及它们的组合,而不是指定mx。批量电子邮件服务通常具有用于发送邮件的 IP 地址的地址范围。你应该把这些加到你的防晒指数记录里。这个 RFC 有更多关于什么被接受的细节: https://tools.ietf.org/html/rfc7208#section-3

–all选项表示我们希望接收邮件的服务器拒绝任何不是来自我们确定的邮件服务器的邮件。您可以将此设置为∼all,它告诉接收邮件的服务器不要拒绝它,而是将其标记为伪造的。如果您完全控制您域名的电子邮件,您应该能够将其设置为–all

要获得创建 SPF 记录的帮助,您可以访问以下网站。它会根据您输入的信息生成 SPF 记录: http://spfwizard.com/

一旦我们的电子邮件服务完成了这一步,我们就需要对它进行设置,以便我们接收电子邮件的电子邮件服务器也使用 SPF 来验证发送给它的电子邮件。为此,我们需要添加另一个包来处理这个验证。

对于 Ubuntu,我们必须添加以下软件包:

$ sudo aptitdue install –y postfix-policyd-spf-python

对于 CentOS,我们将添加此包:

$ sudo yum install –y pypolicyd-spf.noarch

那么 Postfix 的配置只需将以下内容添加到/etc/postfix/main.cf:

smtpd_recipient_restrictions =
....
   reject_unauth_destination
   check_policy_service unix:private/policyd-spf
   reject_rbl_client zen.spamhaus.org
...
policyd-spf_time_limit = 3600

然后到/etc/postfix/master.cf,我们需要以下内容:

policyd-spf  unix  -       n       n       -       0       spawn
 user=nobody argv=/usr/libexec/postfix/policyd-spf

我们可以通过编辑/etc/postfix-policyd-spf-python/policyd-spf.conf (Ubuntu)或/etc/python-policyd-spf/policyd-spf.conf (CentOS)来更改 Postfix SPF 的配置。policyd-spf.confman页面描述了配置选项。默认值应该没问题,但是您可能想要调整skip_addresses并设置debug_level (1 到 5,5 是最详细的)。

现在,当您发送一封测试电子邮件时,您将看到以下邮件头被添加到电子邮件中(SpamAssasin 将使用它来计算垃圾邮件分数):

X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on
        ip-10-0-10-154.ap-southeast-2.compute.internal
X-Spam-Level: *
X-Spam-Status: No, score=1.8 required=5.0 tests=MISSING_SUBJECT autolearn=no
        autolearn_force=no version=3.4.0
Received-SPF: Pass (sender SPF authorized)  identity=mailfrom; client-ip=31.28.208.98; helo=mail.example.net; envelope-from=ataylor@example.net; receiver=jsmith@example.com
X-Virus-Scanned: clamav-milter 0.99.2 at mail.example.com
X-Virus-Status: Clean

你可以看到现在我们有了Received-SPF: Pass (sender SPF authorized),这意味着我们已经查找了example.net的 TXT 记录,发现mail.example.net能够为example.net域发送邮件。

现在让我们看看如何设置域密钥标识的邮件。

dkim!dkim

域密钥识别邮件是指我们将在发送的邮件中添加一个签名头,以便其他邮件服务器可以通过我们发布的公钥来验证它是否来自我们。您已经看到了用于验证 web 服务器和域记录的公钥基础设施(PKI)。现在,我们可以使用该框架来验证我们正在发送的电子邮件。

我们将通过 DNS 再次发布我们的公钥,以便其他邮件服务器可以验证我们的签名。此外,我们将需要 OpenDKIM 包来管理我们的 DKIM 服务。

$ sudo yum install –y opendkim (CentOS)
$ sudo aptitude install –y opendkim (Ubuntu)

这将安装 OpenDKIM 包并创建opendkim用户。在 Ubuntu 和 CentOS 上,您都可以通过配置文件/etc/opendkim.conf配置 OpenDKIM,我们将进行以下配置设置:

Mode               sv
SubDomains         no
SignHeaders        From,Subject,Date
OversignHeaders    From,Subject,Date
Syslog             yes
UMask              002
UserID             opendkim
KeyTable           /etc/opendkim/KeyTable
SigningTable       refile:/etc/opendkim/SigningTable
ExternalIgnoreList /etc/opendkim/TrustedHosts
InternalHosts      /etc/opendkim/TrustedHosts

Canonicalization   relaxed
AutoRestart        yes
AutoRestartRate    10/1M
Background         yes
DNSTimeout         5
SignatureAlgorithm rsa-sha256
Socket             /var/spool/opendkim/opendkim.socket

我们不会遍历每一行,但是我们会把您带到声明ModeSubDomains. Mode可以是svsv的前两行。s表示签名,v表示验证,sv表示两者。SubDomains no表示我们不会签署我们在表格文件中列出的域名的子域。我们还设置了一个套接字文件,稍后我们将连接到 Postfix。

最后,我们来谈谈SignHeadersOversignHeaders From。我们在签名中包含头,并告诉 OpenDKIM 包含From,Subject,Date头。如果需要,您可以包含其他内容,但 From 标头必须经过签名。OverSignHeaders告诉 OpenDKIM 包括这些头,即使它们不存在,并将它们记录为 null,这可以防止以后添加它们。

在 Ubuntu 上,我们将使用以下内容创建所需的 OpenDKIM 目录:

$ sudo mkdir –p /etc/opendkim/keys && sudo touch /etc/opendkim/{KeyTable,SigningTable,TrustedHosts}
$ sudo mkdir –p /var/spool/opendkim
$ sudo chown opendkim:opendkim –R /etc/opendkim/* /var/spool/opendkim && sudo chmod 0640 /etc/opendkim/*
$ sudo chmod 0750 /etc/opendkim/keys

接下来,我们将讨论加密配置。我们需要将我们的域添加到表文件中。首先我们将编辑/etc/opendkim/SigningTable。我们将添加以下内容:

*@example.com   example

这告诉 OpenDKIM 对example.com域(*@example.com)中的所有地址进行签名,它有一个简称example。接下来,我们用以下代码编辑/etc/opendkim/KeyTable文件:

example     example.com:201609:/etc/opendkim/keys/example.com.private

这里我们使用的是KeyTable中 like 的简称来匹配SigningTable中的 like。然后我们看到域和选择器,这是一个唯一的任意值(在 DNS 查找期间使用,格式为 YYYYMM),后面是我们将用于签名的私钥。

我们现在将编辑TrustedHosts文件,添加我们将发送邮件的可信主机。

127.0.0.1
::1
localhost
mail
mail.example.com
example.com

这涵盖了我们的主机可以引用的不同名称。我们现在要生成我们的密钥。我们使用以下命令来实现:

$ opendkim-genkey –D /etc/opendkim/keys -a -d example.com -b 2048 -r -s 201609
$ sudo ls –l /etc/opendkim/keys
total 8
-rw-------. 1 root root 1679 Sep  5 09:21 201609.private
-rw-------. 1 root root  504 Sep  5 09:21 201609.txt

这里我们在/etc/opendkim/keys目录(-D)中创建了一个 2048 位的私钥(-b 2048)和一个公钥。我们附加域(-a),这将我们的域附加到区域文件存根。–d表示我们正在为特定的域生成密钥。如果您想对多个域使用同一个密钥,可以删除它。我们还将此密钥(-r)限制为仅用于电子邮件。–s是选择器,我们已经说过,它用于 DNS 名称查找。

我们将把这两个生成的文件移动到我们在KeyTable文件中指定的名称。

$ sudo mv /etc/opendkim/keys/201609.private /etc/opendkim/keys/example.com.private
$ sudo mv /etc/opendkim/keys/201609.txt /etc/opendkim/keys/example.com.txt
$ sudo chown opendkim /etc/opendkim/keys/ownenergy.com.au.private

我们现在需要用example.com.txt文件中的文本再次更新我们的 DNS。如果我们深入观察,我们会看到以下内容:

201609._domainkey.example.com. IN TXT ( "v=DKIM1; k=rsa; s=email; " "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz5DbbqRCsZ564tDCDeonTkr4ggYrVr5H19qBCYPwnksFyqzmQtpntQq78hpt7lcYghwmhDT9V3o72lUKYn151p6e3rsvtSXmNHuhgxHRwozDf7NdQeDEzpEa7+/UdWvDDtmg9Bbsx6kLhOfTZU8TvnOW3UCJPFkzKNhCg5rrZGLXUqs0S762T4gLDJYCrgkIxUW1KEazkRn1mr" "XvcvE+wt6QL4GPcz6ddPYw4DS9sdZ17DZMa7ngv2C0EjrQwfTcIfoTkfc2G6GgjayVM+RgAs234Eo6+7tX+W7ZmXpzgk2YtHah1cNjHV2dAgGRo/B6H2W0KK89LkZsfTzMYjasfQIDAQAB" )  ; ----- DKIM key 201609 for example.com

DNS TXT 记录的字符串长度不能超过 255 个字符;我们需要把它分成多行。我们需要复制()之间的数据。对于我们的绑定 DNS,我们可以将其添加为多行 DNS 记录:

201609._domainkey IN TXT ("v=DKIM1 ; k=rsa, s=email; "
    "p= MIIBIjANBgkqhkiG ... "
    "XvcvE+wt6Q ... jasfQIDAQAB")

如果您使用外部 DNS 提供商,您可能也需要使用 multiline(例如 AWS Route53)。最好查阅任何关于 DKIM 的文档。我们现在可以使用以下命令来验证我们的 DNS 记录:

opendkim-testkey -d example.com -s 201609

没有产出意味着我们成功了。我们现在将启用并启动opendkim

$ sudo systemctl enable opendkim
$ sudo systemctl start opendkim

要配置 Postfix,我们需要添加另一个 milter。我们通过再次编辑main.cf文件来实现。

smtpd_milters = unix:/var/run/clamav-milter/clamav-milter.socket unix:/var/spool/opendkim/opendkim.socket

我们将把opendkim组添加到postfix用户中。

$ sudo usermod –aG opendkim postfix

然后我们需要修改 SELinux 策略。

我们现在也可以重启 Postfix 了。

$ sudo systemctl restart postfix

我们现在可以给自己发送一封电子邮件到我们的一个外部地址。我们选择了一个 Gmail 地址,如果我们查看该电子邮件的标题,我们会看到:

Authentication-Results: mx.google.com;
       dkim=pass header.i=@example.com;
       spf=pass (google.com: domain of jsmith@example.com designates 52.82.67.42 as permitted sender) smtp.mailfrom=jsmith@example.com
Received: by mail.example.com (Postfix, from userid 1000)
id 99CFA187135A; Tue,  6 Sep 2016 07:59:43 -0400 (EDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple;
d=example.com; s=201609; t=1473163183;
bh=ShAzA6t1zCobzHEAhrmK5udJcy/7FvQ3DqD9cIsQ/Lk=;
h=Date:Subject:From:From:Subject:Date;
b=ecfUXsieztVsVnngyFtsY1RrAeApoCCt+MoAclGdrS4XmSEOQIMrq3olstlsLm8WO
 /qmV5MSxvFzpQ0EXZ5RnRMyo0VPAgaHx4gSP5mjEpWozawD4KYC6WA09jxVNSX8fzU
 Mc0Jn7wQdDIWAjXjv0ubEkFFn9AyLs77aUhwRj0T2CwpJhSzzPYvWOR+LZrO6PymTf
 FQ6C9t4jMSRfGDHnWMTp/QkleeSjCzLlebaQFaDgo38phdNYx2LOKLxdyqzJq/nkeK
 2XXM7rGRM9fllTMy4OHDiabvjSg2GfubOMkwKiJaEv7S0Fc5MrW6nGFR+3s5u4jdq8
 kKDmm2pyeR+oQ==

现在,Authentication-Results头被添加到我们发送的电子邮件中,您可以看到我们的公钥也被添加了。您也可以像这样使用外部验证器服务:

echo 'hello' | mail check-auth@verifier.port25.com

这将向check-auth@verfier.port25.com发送一封电子邮件,它将运行一份关于您的 DKIM 和 SPF 设置的报告,并通过电子邮件向您发送一份详细的报告。

配置 IMAP 和 POP3

与你在本章前面大部分章节中看到的不同,你的用户不会通过命令行直接访问他们的电子邮件。他们希望从本地桌面上的电子邮件客户端访问它。这就是 IMAP 或 POP3 服务器发挥作用的地方。这些协议代表了从邮件客户端或 MUA 访问邮箱的两种不同方法。我们将研究这两种方法,解释每种方法的优缺点,并演示如何在您的环境中配置和实现它们。

因特网邮件访问协议

IMAP 用于从远程客户端(如台式机或笔记本电脑)访问电子邮件邮箱。您的客户端连接到 IMAP 服务器,您可以阅读、管理和删除邮箱中的任何电子邮件。您还可以搜索邮件、创建和删除文件夹,以及执行各种其他管理任务。

POP3

POP3 还用于从远程客户端访问电子邮件邮箱。电子邮件被接收并保存在用户的邮箱中,直到他们检查他们的电子邮件。当用户的电子邮件客户端连接时,所有等待的电子邮件都被下载到客户端并从服务器上删除。

有什么区别?

IMAP 协议的作用很像文件服务器。您的电子邮件保留在服务器上,可以被阅读、删除和操作。POP3 是一种存储转发机制。每种基本协议都有优点和缺点。

IMAP 的优势:

  • 允许用户从多个位置访问电子邮件,而不仅仅是他们的客户端
  • 允许网络邮件访问
  • 保护您的邮件不被意外删除
  • 集中化电子邮件,使备份和恢复更容易

POP3 的优势:

  • 你不需要连接到服务器(或者网络)来访问你的电子邮件。
  • 它不使用服务器上的任何存储,所有邮件在检索后都存储在您的客户端上。

IMAP 的缺点:

  • 您需要能够连接到服务器(从而连接到网络和/或互联网)来访问您的电子邮件。
  • 它要求服务器上有足够的存储空间。

POP3 的缺点:

  • 邮件只存在于客户端,不能从其他地方访问。
  • 如果用户丢失、损坏或重建他们的客户端台式机、笔记本电脑等,而没有足够的备份,他们可能会丢失电子邮件。
  • 如果用户要求备份电子邮件,则基于客户端的备份和恢复策略可能会很复杂,而且难以实施。

在 IMAP 和 POP3 之间选择

在绝大多数情况下,我们建议您使用 IMAP。这样做的原因是简单和易于使用。IMAP 允许您的用户带着或不带他们的客户端漫游;例如,IMAP 服务器允许用户通过她的笔记本电脑和智能手机或其他移动设备访问她的电子邮件。

您的用户的电子邮件也在一个中心位置,这使得您可以轻松地备份它(并且当您的用户不可避免地删除了一封重要的电子邮件时,可以恢复它)。另外,由于存储成本低廉,在大多数公司中,将电子邮件集中存储在服务器或磁盘阵列上不再是一个问题。

主要的警告是,如果您的用户无法连接到 IMAP 服务器,他们将无法检索他们的电子邮件。在这种情况下,我们认为 IMAP 的其他优势大于风险。

在这一节中,我们将在示例中演示如何配置 IMAP。如果你对配置 POP3 感兴趣,你可以在 http://wiki.dovecot.org/POP3Serverhttp://wiki.dovecot.org/QuickConfiguration 看到一些说明、提示和警告。

Dovecot IMAP 简介

已经向您介绍了 Dovecot 服务器,因为在我们的示例中,我们将它用作 Postfix 的认证服务。结果是有一些好消息。如果您一直关注这一点,那么您已经安装、启动并部分配置了 Dovecot。这意味着你现在需要采取的步骤有限。

在本节中,我们将向您展示如何打开 IMAP,特别是 IMAP 协议的安全版本 IMAPS(S 表示安全)。当用户的电子邮件在客户端和服务器之间流动时,它使用 SSL 加密来保护用户身份验证和内容。IMAP 和 IMAPS 的另一个主要区别是它们运行的 TCP 端口。IMAP 协议在端口 143 上运行,而 IMAPS 协议在端口 993 上运行。您需要在您的邮件客户端中指定这个端口号(并可能告诉您的客户端使用 SSL)。

Tip

POP3 协议在 TCP 端口 110 上运行,它的安全对等协议 SSL-POP3 在 TCP 端口 995 上运行。

我们还将配置 Dovecot 来查找我们的本地 Maildir 邮箱。我们不需要配置身份验证,因为我们已经完成了。我们为 Postfix 启用的相同的身份验证机制,使用 PAM 身份验证来检查本地用户的用户名和密码,将很好地用于 IMAP 连接。

配置鸽笼

Dovecot 服务器是使用dovecot.conf配置文件配置的,正如您在本章前面所发现的。在两个发行版中,这个文件都位于/etc/dovecot/dovecot.conf。让我们通过启用 IMAPS 协议并指定将要使用的 SSL 证书的位置来开始我们的配置过程。为了简单起见,我们将重用为后缀加密创建的相同证书。

首先,我们通过编辑protocols配置选项并将其从lmtp更改为lmtp imap来启用 IMAP,如下所示:

protocols = lmtp imap

我们在/etc/dovecot/conf.d/10-master.conf文件中指定 IMAP 如何监听。我们需要将service imap-login改为如下:

service imap-login {
  inet_listener imap {
    port = 0
  }
  inet_listener imaps {
    port = 993
    ssl = yes
  }

这将关闭非 SSL 纯文本端口,只监听 SSL 端口 993。

接下来,我们指定 SSL 证书和密钥文件。为此,我们需要取消注释并更新ssl_cert_filessl_key_file选项,并添加我们的证书和密钥的位置。该文件位于/etc/dovecot/conf.d/10-ssl.conf

ssl = required

ssl_cert = </etc/letsencrypt/live/mail.exmaple.com/cert.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
ssl_ca = </etc/letsencrypt/live/mail.example.com/chain.pem
# SSL protocols to use
ssl_protocols = !SSLv2 !SSLv3
ssl_cipher_list = ALL:!LOW:!SSLv2:!SSLv3:!EXP:!aNULL
# Prefer the server's order of ciphers over client's.
ssl_prefer_server_ciphers = yes

我们将要使用的证书和密钥文件与我们用来提供 Postfix 加密的文件相同。您可以按照我们在那一节中遵循的相同步骤来创建特定于 Dovecot 的证书,或者如果您愿意,您甚至可以专门为 Dovecot 购买额外的证书,但是我们认为这没有必要。

Note

记住,您的证书与一个主机名相关联,在我们的例子中是mail.example.com。如果您在另一台主机上运行 Dovecot 服务器,您应该使用运行 Dovecot 的服务器的主机名创建一个新的密钥和证书。此外,由于证书与主机名相关联,因此您必须在邮件客户端中指定该主机名(在本例中为mail.example.com),而不是该主机已知的任何其他 DNS 名称。

接下来,我们取消注释并将/etc/dovecot/conf.d/10-auth.conf文件中的disable_plaintext_auth选项改为yes

disable_plaintext_auth = yes

此选项禁用任何纯文本身份验证,除非启用了 SSL 并且加密连接正在运行。这可以保护我们用户的身份验证凭证免受攻击,这些攻击可能会从网络或互联网上嗅探或获取这些凭证。

最后,使用mail_location选项(我们之前设置的)确保我们的邮箱位置正确。您将需要编辑/etc/dovecot/conf.d/10-mail.conf

mail_location = maildir:∼/Maildir

这告诉 Dovecot 我们正在使用位于用户主目录或∾中的Maildirs和目录Maildir

现在我们需要重新启动 Dovecot 服务器,然后我们可以测试是否可以建立连接并在我们的服务器上检索我们的电子邮件,例如:

$ sudo systemctl restart dovecot

测试鸽笼

现在我们已经配置了 Dovecot,我们可以启用一个客户端并测试它的访问。我们将配置一个 Mozilla Thunderbird 客户端来测试 Dovecot 当然,您可以使用任何 IMAP 客户端。

Note

Mozilla Thunderbird 是 Mozilla 基金会发布的一款流行的开源邮件客户端,该基金会还开发了 Firefox 浏览器。

要配置客户端,我们首先需要安装它。在 CentOS 和 Ubuntu 上,所需的包称为thunderbird,您应该使用您的应用程序安装工具(或包管理器)来安装它。

现在,让我们回顾一下为了配置我们的客户端,我们需要知道些什么:

  • 我们服务器的名称
  • 我们的用户名和密码

我们将通过选择菜单选项应用程序➤互联网➤雷鸟来启动 Mozilla 雷鸟邮件客户端,如图 12-4 所示。

A185439_2_En_12_Fig4_HTML.jpg

图 12-4。

Launching Mozilla Thunderbird

如果这是你第一次启动雷鸟,帐户向导将会启动。此向导允许您在服务提供商处创建一个帐户。您可以通过选择首选项➤帐户设置并单击添加帐户来创建新帐户。

到目前为止,大多数人已经熟悉了电子邮件客户端的设置。你将需要类似于图 12-5 的细节。

A185439_2_En_12_Fig5_HTML.jpg

图 12-5。

Specifying server details

您可以测试连接,雷鸟将尝试连接到邮件服务器,并验证它实际上是一个电子邮件服务器(图 12-6 )。完成后,点按“完成”。现在我们要做的就是给自己发一封电子邮件,我们已经成功地设置了电子邮件服务器。

A185439_2_En_12_Fig6_HTML.jpg

图 12-6。

Testing our ability to send and receive e-mail

您还应该能够在适用于您的发行版的日志文件中看到您的连接和认证记录:CentOS 上的/var/log/maillog和 Ubuntu 上的/var/log/mail.log,如清单 12-16 所示。

Sep  6 10:19:49 ip-10-0-10-154 dovecot: imap-login: Login: user=<jsmith>, method=PLAIN, rip=203.217.94.151, lip=10.0.10.154, mpid=2762, TLS, session=<aqdGfdc72ADL2V6X>
Sep  6 10:25:18 ip-10-0-10-154 dovecot: imap(jsmith): Disconnected: Disconnected in IDLE in=1186 out=120979
Listing 12-16.Dovecot Authentication Log Entries

鸽笼故障排除

如果有些东西不工作,或者您无法连接到 Dovecot 服务器,那么我们建议您首先查看日志文件。您可以简化这个过程,因为 Dovecot 有一些有用的调试设置,如果遇到问题,您可以启用这些设置来获得更详细的输出。表 12-3 提供了这些选项的列表。

表 12-3。

Dovecot Debugging Options

| [计]选项 | 描述 | | --- | --- | | `mail_debug` | 当设置为`yes`时,显示邮件过程的更多信息。 | | `auth_verbose` | 设置为`yes`时,显示认证过程的更多信息。 | | `auth_debug` | 设置为`yes`时,显示认证过程的调试信息。 | | `auth_debug_password` | 如果`auth_debug`和该选项都设置为`yes`,则显示认证机制和密码。 |

打开这些选项中的一个或多个以查看详细的调试信息。这些信息被发送到 syslog 守护进程,并因此保存到您的/var/log/maillog/var/log/mail.log日志文件中。这些额外的信息将使您更容易确定 Dovecot 安装是否有问题。

Note

在更改这些选项后,您必须重新启动 Dovecot 服务器,我们建议您在修复任何问题后将选项设置回no。打开调试会影响 Dovecot 服务器的性能。

获得 Dovecot 的帮助

大量有用的信息可帮助您解决 Dovecot 的任何潜在问题。寻求帮助的最佳起点是位于 www.dovecot.org/ 的鸽派主页。该网站包括一个全面的 wiki ( http://wiki.dovecot.org/ ),其中包括许多如何在各种实现中配置 Dovecot 的方法和示例。同样可用的还有 Dovecot 邮件列表( www.dovecot.org/mailinglists.html )和 bug 提交说明( www.dovecot.org/bugreport.html )。

请记住,如果您提交问题或错误,您应该包括以下信息:

  • 您的 Dovecot 版本(运行带有--version选项的dovecot命令)
  • 包括您的配置,您可以通过doveconf -n获得
  • 你的平台(运行uname -a
  • 生成的任何日志消息(在 Ubuntu 上的/var/log/mail.log文件中或在 CentOS 上的/var/log/maillog文件中)

在 Freenode IRC 服务器上还有一个名为#dovecot的 IRC 频道( http://freenode.net/ ),你可以在那里寻求帮助。

虚拟域和用户

在本章中,我们已经介绍了向用户提供电子邮件的基础知识。在这里,我们将讨论扩展本章中介绍的邮件服务的方法。

扩展 Postfix 和 Dovecot 的主要方式是扩展到虚拟域和虚拟用户。到目前为止,我们一直假设我们发送邮件的域和接收邮件的用户都是物理实例。我们接受电子邮件的域是邮件服务器所属的域,我们的用户实际上存在于我们的邮件服务器上。通过虚拟域和用户,您可以配置非物理的电子邮件目的地和收件人。我们不会演示这些概念,但我们会解释它们,并让您参考文档,这些文档将允许您扩展我们在本章中构建的内容来完成所有这些工作。

托管虚拟域允许您接收其他域的电子邮件。例如,假设您当前收到了域example.com的邮件。你的公司可能也有域名product.comanotherproduct.com。您可以配置 Postfix 来接收这些附加域的电子邮件。

虚拟用户是一个类似的概念。到目前为止,我们所有的用户都是真正的 Linux 用户,例如,jsmithataylor。他们的邮箱已经存储在/home目录树中。使用虚拟用户,您的邮件用户不需要被创建为操作系统用户。每个电子邮件地址都映射到一个虚拟用户或包含在 MySQL 或 LDAP 等数据库中的用户。这减少了创建和管理大量操作系统用户的需要。

您可以在 www.postfix.org/VIRTUAL_README.html 找到使用 Postfix 配置虚拟域和用户的完整操作方法。你可以在 https://help.ubuntu.com/community/PostfixCompleteVirtualMailSystemHowto 找到更具体的 Ubuntu 指南。你也可以在 http://wiki.dovecot.org/ VirtualUsershttp://wiki.dovecot.org/HowTo 的 Dovecot 操作指南页面上找到以类似方式扩展 Dovecot 的说明。

适用于 Linux 的备选邮件服务器

有几个免费的和商业的电子邮件解决方案提供了比本章所描述的更多的功能。许多人希望日历、企业认证、文件共享和协作功能成为他们日常工作体验和需求的一部分。

表 12-4 列出了一些已经存在多年的解决方案。他们在不同的许可下提供他们的软件,但是每个都有一个开源版本。许多公司以商业价格提供附加功能并签署支持协议。

表 12-4。

List of Alternative E-mail Solutions

| 科拉布社区 | 自由软件(`https://www.gnu.org/philosophy/free-sw.en.html`)。 | | 打开-exchange | 免费用于非商业用途。商业许可证付款( [`https://www.open-xchange.com/terms-and-conditions`](https://www.open-xchange.com/terms-and-conditions) )。 | | 津巴 | 开源版免费。商业许可可用于获得某些功能( [`https://www.zimbra.com/legal/licensing/`](https://www.zimbra.com/legal/licensing/) )。 | | iRedMail | 带商业附加组件的开源( [`https://creativecommons.org/licenses/by-nd/3.0/us/`](https://creativecommons.org/licenses/by-nd/3.0/us/) )。 |

摘要

在本章中,我们向您介绍了 Linux 主机上的邮件服务,它允许您的用户从您的组织发送和接收电子邮件。我们讨论了 IMAP 和 POP3,这些协议允许用户连接到您的主机,并检索和管理他们邮箱中的电子邮件。

在这个过程中,我们讨论了一些重要的概念:

  • 通过加密保护用户的电子邮件及其身份验证凭据
  • 启用 SASL 身份验证以允许您的用户安全可靠地通过邮件服务器的身份验证来发送和接收电子邮件
  • 利用 SpamAssassin 过滤您的电子邮件,阻止垃圾邮件到达您的用户
  • 使用开源 ClamAV 防病毒引擎检查用户的电子邮件中是否有病毒和恶意软件
  • 使用 Dovecot 和 Sieve 等 LMTPs 来过滤你的电子邮件
  • 通过使用 SPF 和 DKIM 进一步保护您的服务器及其信誉
  • 实现 Dovecot 服务器以允许用户连接到他们的邮箱并检索电子邮件

在下一章,我们将向您介绍文件和打印服务,并教您如何运行自己的 NFS 服务器和查看集群网络文件系统。

十三、文件共享和打印

最常见的办公功能之一是共享/打印文档和文件。在这一章中,我们将向您展示如何使用 Linux 来实现这一点。Linux 提供了许多共享信息的方法。它可以使用名为 Samba 的集成工具与 Microsoft Windows 客户端或 macOS 共享信息。它还可以使用一种叫做网络文件系统(NFS)的工具在 Linux(和其他 Unix)主机之间共享文档。

在异构环境中,将文档管理和存储转移到基于云的提供商变得越来越普遍。这些服务非常普遍,与传统的文件共享相比具有几个关键优势,如版本控制、多设备支持、协作功能和一些支持工作流。企业现在希望这种访问文档的便捷性,而 Samba 和 NFS 都不太适合这项工作。

除了基本的文件共享,我们还将向您介绍不同的平台,这些平台对于文档管理来说既便宜又有效。我们将为此给出一个可能的替代方案列表。

最后,我们将向您展示如何在您的 Linux 主机上配置打印和打印服务。我们将演示如何从您的主机打印,以及如何让您的主机充当打印服务器。在 Linux 上打印很容易实现,CentOS 和 Ubuntu 共享一个通用的工具集,使得它很容易实现和管理。

与桑巴和 NFS 共享文件

文件共享是在用户之间共享文档的能力。我们将公司文档集中在一个地方,并允许员工以受控和安全的方式访问它们,而不是每个人都将公司文档保存在自己的台式计算机上,并通过电子邮件等方式传递。这样做的好处是巨大的,因为它减少了每个人桌面上一个文档的多个副本的激增,并使限制访问和备份文档变得更加容易。某些应用程序需要文件共享才能被桌面客户端访问,文件共享可用于此目的。

在您的台式机上,您可以安装网络驱动器。当您通过运行登录脚本的域控制器或在需要时手动登录主机时,可以分配文件共享。您还可以在 Linux 桌面上挂载文件共享。Linux 桌面可以很容易地挂载一个 Samba 或 NFS 共享;但是,如果不做一些修改,它们就无法运行 Windows 可执行文件。

Note

使用 Wine 开发的软件( www.winehq.org )运行一些 Windows 应用程序可能会取得一些成功。这个开源社区致力于集成一组选定的 Windows 应用程序,以便在 Linux 操作系统(或 macOS)上运行。还有一个由姐妹组织 CodeWeavers 提供的支持版本( www.codeweavers.com )。

使用 Linux 可以通过多种方式实现文件共享,这取决于您对访问、安全性和您必须支持的客户机的需求。Linux 可以为传统的文件共享提供一个很好的平台,因为它使您能够在客户机主机上安装网络驱动器。这允许访问由中央文件服务器主机共享的公共数据。实现这一点的两个主要工具是用于微软 Windows 桌面的 Samba 和用于 Linux 或 Unix 主机的 NFS(MAC OS 可以使用其中任何一个)。

Samba 可以为文件共享和打印机服务提供用户认证。NFS 本身不提供任何用户身份验证,但是它可以集成到 Kerberos 域中进行身份验证。这种设置很复杂,我们不会在本书中涉及。

接下来,我们让你近距离看看桑巴。

桑巴舞

Samba 是用于 Linux 的文件共享和打印服务。它以标准的客户机-服务器模式运行,带有一个接受网络客户机请求的守护进程。它基于通用 Internet 文件系统(CIFS)和服务器消息块(SMB)协议,这些协议处理 Microsoft 产品上文件系统的主机间通信,使 Samba 与 Microsoft Windows 桌面客户端和 Microsoft Windows 域服务兼容。

目前有两个受支持的 Samba 版本,3.x 版和 4.x 版。您想要达到的目标将决定哪一个最适合您。4.x 版是模拟 Active Directory (AD),而 3.x 版更像 NT 4。3.x 版支持轻量级目录访问协议(LDAP)集成,而 4.x 版中的 LDAP 集成仍处于试验阶段。此外,Samba 4 目前最多支持 Windows Server 2012 R2,但 2012 服务器需要 Windows 2008 服务器首先加入 Samba AD。Windows Server 2012 或 2012 R2 目录架构目前处于试验阶段。请参见 https://wiki.samba.org/index.php/Joining_a_Windows_Client_or_Server_to_a_Domain 了解支持哪些客户端以及如何加入 Windows 客户端和服务器(2008 和 2012)的域。

在本例中,我们将向您展示如何实现 Samba 的 4.x 版本,并在我们的 macOS 主机上安装一个驱动器。我们向您展示了如何使用 Samba 建立一个简单的文件,它将允许您与同事共享文档。您还将学习如何设置 Samba AD 域服务器,以便您的用户可以进行身份验证,以及如何设置部门共享,您也可以将该共享挂载到您的桌面上。

此外,在本例中,需要注意的是我们将创建一个samba.example.com域名系统(DNS)子域。这是因为 Samba 将管理这个子域,这将与我们的example.com域冲突。我们还只设置了一个域控制器,并让它充当我们的文件服务器。Samba 文档建议这种设置不适合生产使用,而是应该有一两个 Samba 广告和几个挂广告的文件服务器。拥有多个 AD 有利于维护(您可以在一个 AD 上执行维护,同时仍然使用另一个 AD)、可靠性和可用性。

首先你需要安装软件。CentOS 和 Ubuntu 都需要相同的包来启动和运行 Samba。

在 CentOS 上,您发出以下命令:

$ sudo yum install samba-client samba-common samba winbind

但是,CentOS 7 没有提供我们配置 Samba AD 所需的 samba-tool 二进制文件。您可以将 Samba 配置为经典的 NT4 服务器,也可以从 https://www.sernet.de/en/samba/samba/ 为 CentOS 购买 Samba 软件订阅。我们建议你使用 Samba 和 Ubuntu。

在 Ubuntu 上,发出以下命令:

$ sudo aptitude install samba-client samba-common samba winbind

配置 Samba 广告

我们将在我们的 Ubuntu 服务器上安装 Samba。为此,我们需要做几件事。

  • 设置主机名
  • 更新设备
  • 更新 DNS

一旦这些任务完成,我们就可以配置 Samba 了。

首先,我们需要确保主机名设置正确。我们将使用主机名dc1.samba.example.com,它在我们的域中是唯一的。要完整了解桑巴命名建议,请阅读 https://wiki.samba.org/index.php/Active_Directory_Naming_FAQ 。我们的主机需要符合以下配置:

$ hostname –s
dc1
$ hostname –d
samba.example.com

dc1.samba.example.com添加到您的/etc/hosts文件中也是一个好主意。

127.0.0.1   localhost dc1 dc1.samba.example.com

此外,因为我们将让 Samba 管理它自己的 DNS 子域,所以我们需要允许它访问绑定配置。bind9服务器必须与 Samba AD 服务器在同一个服务器上,因为它需要访问某些目录。因为我们使用的是 Ubuntu 服务器,所以我们需要对 Apparmor 进行一些修改,以允许bind9服务访问 Samba 目录。记住,我们在第十二章中谈到过 Apparmor 它是一个安全模块,描述对系统资源(如端口和文件)的安全访问。

我们将以下内容添加到我们的/etc/apparmor.d/local/usr.sbin.named文件中,记住我们将本地主机更改添加到/etc/apparmor.d/local目录中:

# Samba4 DLZ and Active Directory Zones
/var/lib/samba/lib/** rm,
/var/lib/samba/private/dns.keytab r,
/var/lib/samba/private/named.conf r,
/var/lib/samba/private/dns/** rwk,

保存这个文件后,我们需要将它加载到 Apparmor 中,如下所示:

$ sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.named

为了配置我们的bind9服务,为我们的 Samba 配置做准备,我们将把以下内容添加到我们的/etc/bind/named.conf.options文件的底部:

include "/var/lib/samba/private/named.conf";

我们需要用以下内容重新加载我们的bind9配置:

$ sudo rndc reload

现在是时候配置 Samba 了。Samba 提供了一个名为samba- tool的工具来帮助配置 Samba 服务器。您可能需要删除或复制系统中任何现有的/etc/samba/smb.conf文件,因为以下命令将为您创建一个新文件。

在清单 13-1 中,我们在交互模式中使用了samba-tool(--interactive)。关于您可以在命令行上提供的选项的完整列表,请参见samba-tool provision --help。我们已经提供了--use-rfc2307选项,它告诉 Samba 将用户和组信息存储在内部 LDAP 目录中。这对于跨域成员维护一致的用户 id/组 id(uid/GID)很有用,并且有一些相关的好处;https://wiki.samba.org/index.php/Setting_up_RFC2307_in_AD见。我们还提供了我们的首选主机名(--host-name),dc1

$ sudo samba-tool domain provision --use-rfc2307 --host-name=dc1 --interactive
Realm [SAMBA.EXAMPLE.COM]:
 Domain [SAMBA]:
 Server Role (dc, member, standalone) [dc]:
 DNS backend (SAMBA_INTERNAL, BIND9_FLATFILE, BIND9_DLZ, NONE) [SAMBA_INTERNAL]: BIND9_DLZ
Administrator password:
Retype password:
Looking up IPv4 addresses
More than one IPv4 address found. Using 192.168.0.10
Looking up IPv6 addresses
No IPv6 address will be assigned
Setting up secrets.ldb
Setting up the registry
Setting up the privileges database
Setting up idmap db
Setting up SAM db
Setting up sam.ldb partitions and settings
Setting up sam.ldb rootDSE
Pre-loading the Samba 4 and AD schema
Adding DomainDN: DC=samba,DC=example,DC=com
Adding configuration container
Setting up sam.ldb schema
Setting up sam.ldb configuration data
Setting up display specifiers
Modifying display specifiers
Adding users container
Modifying users container
Adding computers container
Modifying computers container
Setting up sam.ldb data
Setting up well known security principals
Setting up sam.ldb users and groups
Setting up self join
Adding DNS accounts
Creating CN=MicrosoftDNS,CN=System,DC=samba,DC=example,DC=com
Creating DomainDnsZones and ForestDnsZones partitions
Populating DomainDnsZones and ForestDnsZones partitions
See /var/lib/samba/private/named.conf for an example configuration include file for BIND
and /var/lib/samba/private/named.txt for further documentation required for secure DNS updates
Setting up sam.ldb rootDSE marking as synchronized
Fixing provision GUIDs
A Kerberos configuration suitable for Samba 4 has been generated at /var/lib/samba/private/krb5.conf
Setting up fake yp server settings
Once the above files are installed, your Samba4 server will be ready to use
Server Role:           active directory domain controller
Hostname:              dc1
NetBIOS Domain:        SAMBA
DNS Domain:            samba.example.com
DOMAIN SID:            S-1-5-21-295742502-4045385941-247307200
Listing 13-1.Configuring Samba with samba-tool

在交互模式下,我们在[]括号中得到默认值,我们选择了RealmDomainServer Role的默认值。Server Role设置为dc,代表域控制器,意思是我们是 Samba Windows 域的权威,SAMBA

对于 DNS,我们有选项SAMBA_INTERNALBIND9_FLATFILEBIND9_DLZNONE。我们选择了BIND9_DLZ,这将告诉 Samba 通过我们之前包含在named.conf.options文件中的特殊 Samba 模块来动态管理我们的 DNS 子域。Samba 将在设置过程中创建所有必需的 DNS 记录。如果您想让 Samba 拥有并管理您的 DNS 设置,那么SAMBA_INTERNAL也是合适的。BIND9_FLATFILE不是推荐选项,如果想完全管理自己的 DNS,应该选择NONE

您将被要求提供管理员密码。密码需要八个或更多字符,并且需要符号、字母和数字。一旦输入,Samba 的设置就很快完成了。一旦完成,我们可以开始我们的服务。

$ sudo systemctl start samba-ad-dc

这将启动 Samba 服务,您可以使用systemctl status samba-ad-dc命令来检查它是否已经正常启动。另外,记得在开机时用systemctl enable samba-ad-dc启用它。

测试 Samba

现在,我们可以测试我们是否可以针对服务进行身份验证。为此,我们使用smbclient命令,这是用于与 Samba 服务交互的工具。

$ smbclient –L dc1 -U Administrator
Enter Administrator's password:
Domain=[EXAMPLE] OS=[Windows 6.1] Server=[Samba 4.3.9-Ubuntu]

    Sharename       Type      Comment
    ---------       ----      -------
    netlogon        Disk
    sysvol          Disk
    IPC$            IPC       IPC Service (Samba 4.3.9-Ubuntu)
Domain=[EXAMPLE] OS=[Windows 6.1] Server=[Samba 4.3.9-Ubuntu]

    Server               Comment
    ---------            -------

    Workgroup            Master
    ---------            -------
    WORKGROUP            DC1

这里,我们从我们的dc1 Samba 服务获得了一个清单(-L),使用Administrator用户(-U)进行认证。如果你在这里得到错误,那么你在你的设置中有一个错误。这里有一个故障排除页面可能会帮到你: https://wiki.samba.org/index.php/Samba_AD_DC_Troubleshooting

从测试中可以看到,我们有两个磁盘共享(netlogonsysvol)和一个IPC$(用于与 Samba 通信的进程间连接)。我们可以运行ls命令(-c)来获得netlogon共享的列表,再次测试我们可以列出netlogon共享。

$ smbclient //localhost/netlogon -UAdministrator -c 'ls'
Enter Administrator's password:
Domain=[EXAMPLE] OS=[Windows 6.1] Server=[Samba 4.3.9-Ubuntu]
  .                                   D        0  Sat Sep 24 14:02:06 2016
  ..                                  D        0  Sat Sep 24 14:02:15 2016

        10098468 blocks of size 1024\. 7667640 blocks available

测试 DNS

我们现在也可以测试我们的 DNS 响应。对于充当域控制器的 Samba,它必须响应以下文本记录请求:

$ host -t SRV _ldap._tcp.samba.example.com.
_ldap._tcp.samba.example.com has SRV record 0 100 389 dc1.samba.example.com.
$ host -t SRV _kerberos._udp.samba.example.com.
_kerberos._udp.samba.example.com has SRV record 0 100 88 dc1.samba.example.com.
$ host -t A dc1.samba.example.com.
dc1.samba.example.com has address 192.168.0.10

在这里,我们已经测试了我们可以解析 LDAP 服务记录、Kerberos 服务记录和dc1.samba.example.com的 A 记录。如果这个解析正确,那么 Samba 可以正确地使用我们的bind9 DNS 服务。

如果这不起作用,从/var/log/syslog开始检查绑定服务是否有错误。

测试 Kerberos

Kerberos 是一种网络身份验证协议,可用于将 Active Directory 服务连接在一起,并对用户和机器进行身份验证。Kerberos 是作为 Samba 自动安装的一部分安装的。因为我们只有一个 Samba 服务器,所以实际上并不需要它,除非我们加入另一个 Active Directory 域控制器,或者我们希望通过 Samba 服务作为单点登录机构来验证我们的 Linux 用户帐户。

在测试我们的 Kerberos 配置之前,我们首先需要创建一个符号链接。清单 13-1 中的输出告诉我们 Kerberos 配置文件已经生成并放在了/var/lib/samba/private/krb5.conf中。我们将把它符号链接到/etc/krb5.conf

$ sudo ln –sf /var/lib/samba/private/krb5.conf /etc/krb5.conf

看一下这个文件,我们可以看到它是这样的:

[libdefaults]
    default_realm = SAMBA.EXAMPLE.COM
    dns_lookup_realm = false
    dns_lookup_kdc = true

dns_lookup_选项决定了我们是否对 SRV 记录使用 DNS 查找。我们已经测试了 SRV 记录(host -t SRV _kerberos._udp.samba.example.com)。)在我们的 DNS 测试之前,并已确认它是为我们的 KDC (Kerberos 域控制器)工作。

现在让我们测试实现,以确保它工作正常。我们将获得一个 Kerberos 票证,然后验证它。

$ kinit administrator@SAMBA.EXAMPLE.COM
Password for administrator@SAMBA.EXAMPLE.COM:
Warning: Your password will expire in 41 days on Mon 07 Nov 2016 11:04:17 PM UTC

您确实需要为域指定大写字母。现在让我们看看我们的票:

$ klist
Ticket cache: FILE:/tmp/krb5cc_1000
Default principal: administrator@SAMBA.EXAMPLE.COM

Valid starting       Expires              Service principal
09/26/2016 23:34:37  09/27/2016 09:34:37  krbtgt/SAMBA.EXAMPLE.COM@SAMBA.EXAMPLE.COM
    renew until 09/27/2016 23:34:30

这证明 Kerberos 工作正常。我们现在都准备好创建一个共享了。

Note

Kerberos 可能是一个非常复杂和令人畏惧的话题。你可能喜欢读下面的温柔介绍: www.roguelynn.com/words/explain-like-im-5-kerberos/

配置 Samba 共享

要访问 Samba 共享,我们需要做几件事:

  • 将共享添加到smb.conf
  • 创建目录并添加权限
  • 创建任何必要的系统用户
  • 将 Samba 用户添加到 Samba

让我们从查看我们将要使用的 Samba 配置文件开始。这是由供应过程创建的。

配置文件可以分成两部分,一部分是[ global ]配置选项,另一部分是特定的配置选项,比如[ netlogon ]、[ sysvol ]等等(见清单 13-2 )。[ global ]部分,顾名思义,定义了影响整个服务器的配置选项,[ netlogon ]和[ sysvol ]中的具体配置只影响它们试图定义的那些服务。

# Global parameters
[global]
    workgroup = SAMBA
    realm = SAMBA.EXAMPLE.COM
    netbios name = DC1
    server role = active directory domain controller
    server services = s3fs, rpc, nbt, wrepl, ldap, cldap, kdc, drepl, winbindd, ntp_signd, kcc, dnsupdate
    idmap_ldb:use rfc2307 = yes

[netlogon]
    path = /var/lib/samba/sysvol/samba.example.com/scripts
    read only = No

[sysvol]
    path = /var/lib/samba/sysvol
    read only = No

Listing 13-2.Samba /etc/samba/smb.conf File

我们来看一下[ Global ]部分。首先你会看到选项workgroup。如果您是 Windows 用户,应该对这个选项很熟悉。它是将出现在网上邻居中的工作组名称。工作组是共享信息的计算机的集合。通常,在工作组中,计算机没有中央身份验证,工作组中的每台主机负责自己的身份验证。换句话说,您维护一个可以访问每台主机的用户列表。

当工作组中的一台主机成为主域控制器(PDC)时,就实现了集中身份验证。主机加入域,然后使用 PDC 验证用户对其资源的访问。如果主机不是域的一部分,它仍然可以共享资源,但它必须维护自己的身份验证和访问列表。

在此设置工作组并不会使我们的主机成为 PDC,而只是工作组的一部分。我们将它设置为我们的域名,EXAMPLE

workgroup = SAMBA

接下来,您需要配置 NetBIOS。NetBIOS 是一种本地广播协议,用于处理主机之间的连接信息。NetBIOS 信息用于将名称与 WINS 服务器中的互联网协议(IP)地址进行匹配,有点像 DNS。NetBIOS 协议本身用于名称服务、会话服务和数据报服务器。正如您在清单 13-2 中看到的,我们正在将netbios name选项设置为dc1,我们将能够使用这个 NetBIOS 名称来引用我们的主机。

Note

如果你对 NetBIOS 的进一步解释感兴趣,请阅读 http://en.wikipedia.org/wiki/NetBIOS

server role指令被设置为active directory domain controller。这显然意味着我们正在经营一个广告 DC。如果您要使用传统的域控制器,您应该在这里设置它(NT4)。server services指令定义了该服务器支持的服务。您可以在这里用+或-添加或删除服务,就像-kdc

通过使用hosts allow选项指定哪些主机可以访问您的服务,您可以进一步控制对 Samba 服务的访问。在我们的配置中,我们指定了环回网络和 192.168.0。网络。符号127.192.168.0.相当于分别指定网络掩码 127.0.0.0/8 和 192.168.0.0/24。在这里,您还可以使用完整的 IP 地址来指定单个主机,并使用EXCEPT子句来排除某些主机。例如,如果我们希望允许访问 192.168.0 上的所有主机。除了地址为 192.168.0.15 的恶意主机,我们将在hosts allow中使用以下内容:

hosts allow = 127\. 192.168.0\. EXCEPT 192.168.0.15

您还可以使用完全限定的域名(FQDNs)如headoffice.example.comgateway.example.com来指定单个主机。我们没有在这里显示,但hosts deny允许您列出您不想访问此服务的主机和网络。

我们将让网络 192.168.0.0 到 192.168.30.0 访问此 Samba 服务,因此我们将指定以下内容,这将允许 192.168.0.0/16 范围内的任何地址进行连接:

hosts allow = 127\. 192.168.

Samba 认为一些服务是“特殊的”,这些服务以[]括号开始划分。具体来说,[global、【打印机】和homes描述特殊服务。Samba 对待这些服务的方式不同于[sales或[tmp]共享。

例如,如您所料,当 Samba 通过[ printer ]服务接收到一个文件时,它会以不同于通过用户定义的[ tmp ]服务接收到的文件的方式来处理它。Samba 具有只与这些“特殊”服务中的定义相关联的功能。在本例中,[]中的路径定义定义了一个假脱机目录。当您的打印机通过[ printer ]服务将您的打印输出假脱机到这个路径时,Samba 会自动将其传递给 CUPS 进行打印。在另一个服务中设置path的值不会触发此行为。这种特殊行为也适用于[homes],如果需要的话,Samba 将动态创建主目录。您可以在smb.conf man页面的“特殊章节”部分了解更多信息。

当您定义自己的服务时,您使用一组特殊服务和用户定义的服务通用的指令。在表 13-1 中,我们列出了我们将在示例中使用的指令。您可以使用这些指令来改变或创建您自己的服务。

表 13-1。

Samba Service Directives

| 管理的 | 描述 | | --- | --- | | `path` | 定义您正在描述的共享的路径(例如,`/tmp`)。 | | `browseable` | 描述共享在浏览器共享列表中是否可见。 | | `comment` | 给出共享的描述。 | | `writable` | 表示共享可以写入,而不是只读。 | | `readonly` | 表示共享是只读的。 | | `printable` | 允许创建假脱机文件并提交到共享。仅适用于打印。 | | `guest ok` | 表示共享不需要密码。默认为否。也称为`public`。 | | `valid users` | 指定允许使用此服务的用户列表。 | | `write list` | 指定可以读取/写入共享的用户/组列表,而不管`readonly`设置如何。 | | `force user` | 将默认用户分配给到此共享的所有连接。 | | `force group` | 将默认组分配给到该主机的所有连接。 | | `force create mode` | 对创建的文件强制 Unix 权限。 | | `force directory mode` | 对创建的目录强制 Unix 权限。 | | `create mask` | 对创建的文件设置 Unix 权限。 | | `directory mask` | 对创建的目录设置 Unix 权限。 |

您可以使用这些服务指令来定义共享的路径和访问权限。还有更多可用的指令,您可以在man smb.conf页面中找到对它们的全面解释。

[netlogon]共享在 Samba 中没有被归类为特殊服务,但它是许多默认的smb.conf文件中的标准服务,并且默认包含在 Ubuntu 和 CentOS Samba 包中。这个和[sysvol]是作为默认配置过程的一部分创建的。[netlogon]服务用于验证 Windows 客户端,可以包含登录脚本和其他信息。每个域控制器都需要[sysvol]服务,它是在其他域控制器上复制的文件夹。

我们将在文件末尾的部分添加以下内容,以建立一个供sales组成员共享的sales共享:

[sales]
        comment = shared sales directory
        path = /data/staff/sales
        readonly = yes
        public = no
        browseable = yes
        valid users = +SAMBA\sales
        write list = jsmith, bsingh
        force create mode = 0770
        force directory mode = 2770
        create mask = 0770
        directory mask = 2770
        force group = sales
        force user = exbackup

我们添加了一个名为[ sales ]的共享。这份股票仅供我们的销售人员使用。我们已经决定将我们的员工共享文档放入/data/staff目录,而sales目录将位于该目录下。我们已经指定默认情况下它应该是只读的(readonly = yes)。这将阻止非预期用户写入此目录。我们也拒绝公众或客人用户(public = no)。它是可浏览的(browserable = yes),所以它将出现在这个 Samba 服务器的共享浏览器列表中。我们还在这里指定了一个有效的用户列表,它被设置为 sales 组(valid users = +SAMBA\sales)。+指示 Samba 查看本地 Unix 用户/组列表。sales组中的人将对[ sales ]共享具有只读访问权限。然后,我们具体指定我们希望谁拥有读/写访问权限,jsmithbsingh。我们也可以在这里使用组列表。如果我们有一个名为sales_admins的组,我们可以添加write list = @sales_admins

最后一部分确保使用正确的所有权、组和权限创建文件和目录。我们不希望用户在整个目录树中拥有自己的文档,然后当他们想要与他人共享这些文档时,不得不找人更改他们的权限或所有权。我们希望所有者始终是exbackup,这样我们将始终能够使用我们的备份脚本访问共享,并且我们希望共享根据其部门组拥有组所有权。我们希望文件是可读和可写的,对于目录,我们应该给指定的用户和组完全的访问权限,但不允许普通公众访问。

force create mode = 0770
force directory mode = 2770
create mask = 0770
directory mask = 2770
force group = sales
force user = exbackup

正如您在第四章中看到的,这里的权限是对所有文件和目录的读、写和执行/访问。对于您的需求来说,这可能太宽松了,但是它给了您一个如何使用权限的想法。您可能希望创建仅具有 0660 权限的文件,这是完全合理的,但可能需要一些管理开销。

Samba 应该自动加载您的新配置,而不需要重启服务;但是,如果看不到您的配置,您可以使用以下命令重新启动该服务:

$ sudo systemctl restart samba-ad-dc

Samba Variable Substitutions

Samba 有一些标准的变量替换,我们将在这里看一下。以下列表是man页面上列出的可用变量的一部分:

  • %U : Session username(客户端需要的用户名,不一定是得到的用户名)。
  • %G:主组名%U
  • %S:当前服务的名称,如果有的话。
  • %L:服务器的 NetBIOS 名称。这允许您根据客户端对您的调用来更改您的配置。你的服务器可以有双重人格。
  • %M:客户端机器的互联网名称。
  • %D:当前用户的域名或工作组名。
  • %H:由%u给出的用户的主目录。

这里我们没有显示所有的变量;有关变量的完整列表,请参见man smb.conf页。

向 Samba 添加用户

我们现在有了一个基本的 Samba 配置。如果您认为合适,可以将您自己的共享添加到此配置中。我们现在需要对主机做几件事,以便为用户做好准备。我们很快将通过samba-tool命令创建我们的 Samba 用户和组,一旦完成,我们将创建我们的目录并分配权限。

我们设置 Samba 的方式要求在 Samba 主机上管理用户帐户。Samba 为samba-tool提供了一个子命令来管理 Samba 主机上的用户帐户。工具samba- tool使用以下语法:

$ sudo samba-tool user --help
Usage: samba-tool user <subcommand>

User management.

Options:
  -h, --help  show this help message and exit

Available subcommands:
  add          - Create a new user.
  create       - Create a new user.
  delete       - Delete a user.
  disable      - Disable an user.
  enable       - Enable an user.
  list         - List all users.
  password     - Change password for a user account (the one provided in authentication).
  setexpiry    - Set the expiration of a user account.
  setpassword  - Set or reset the password of a user account.
For more help on a specific subcommand, please type: samba-tool user <subcommand> (-h|--help)

让我们列出我们现有的用户。

$ sudo samba-tool user list
Administrator
dns-dc1
krbtgt
Guest

您可以看到,我们有四个当前用户,它们是由 Samba 供应创建的。我们将再添加两个用户,jsmithbsingh,他们是销售团队的成员。

$ sudo samba-tool user create jsmith
New Password:
Retype Password:
User 'jsmith' created successfully

我们添加了一个名为jsmith的新用户。我们将为bsingh做同样的事情。密码至少要有 8 个字符,并且要复杂。

不幸的是,tbear刚刚离开公司,他的 Samba 帐户需要禁用。为此,我们禁用tbear的详细信息,如下所示:

$ sudo samba-tool user disable tbear
Disabled user tbear

如果我们想完全移除他,我们会使用samba-tool user delete

接下来我们将创建我们的sales组,然后我们将添加jsmithbsingh到其中。我们通过group子命令再次使用samba-tool。发布help显示我们的选项。

$ sudo samba-tool group
Usage: samba-tool group <subcommand>

Group management.

Options:
  -h, --help  show this help message and exit

Available subcommands:
  add            - Creates a new AD group.
  addmembers     - Add members to an AD group.
  delete         - Deletes an AD group.
  list           - List all groups.
  listmembers    - List all members of an AD group.
  removemembers  - Remove members from an AD group.
For more help on a specific subcommand, please type: samba-tool group <subcommand> (-h|--help)

如您所见,它类似于user子命令。首先让我们添加sales组。

$ sudo samba-tool group add sales
Added group sales

这很简单,您还应该添加组staff。我们现在可以将我们的用户添加到sales组。

$ sudo samba-tool group addmembers sales jsmith,bsingh
Added members to group sales

我们现在可以列出sales组的成员了。

$ sudo samba-tool group listmembers sales
jsmith
bsingh

我们可以通过以下方式删除组和移除组成员:

$ sudo samba-tool group removemembers sales tbear
Removed members from group sales

我们已经将用户tbearsales组中删除;我们也可以用下面的内容删除fails组:

$ sudo samba-tool group delete fails
Deleted group fails

Samba 将对我们的用户进行身份验证,并为他们提供对他们有权访问的任何共享的访问。Samba 将使用 Winbind 为我们认证和映射用户和组。但是,我们仍然需要创建一个 Linux 系统用户。这个用户将拥有这些文件,因此需要存在于每个 Samba 服务器上。该用户将是我们的备份用户。我们将在第十四章中向您展示如何在我们的备份策略中使用该用户。

$ sudo useradd –u 903 exbackup

接下来,我们需要创建目录/data/staff/sales。我们将使exbackup成为/data/staff中文件和目录的所有者。我们需要确保适当的组可以访问我们定义的共享,我们将很快做到这一点。

$ sudo mkdir –p /data/staff/sales
$ sudo chown exbackup –R /data/staff

Note

这些用户和组 id 都是任意的,它们可能已经在您的主机上使用了。您可能需要为您的网络选择不同的范围。记住,小于1000的 ID 值是为系统帐户保留的。

我们在桑巴建立了sales团。我们现在需要查询 Winbind 来获取已经分配给sales组的 GID,然后我们将更改目录上的组所有权。这些用户和组不存在于普通的 Linux /etc/passwd/etc/group文件中,所以我们需要使用 Samba 在访问文件和目录时将使用的 GID。

$ wbinfo --group-info sales
SAMBA\sales:x:3000010:

我们可以看到wbinfo已经查询了销售组的组信息,并且 GID 3000010 已经被分配给它。我们将使用这个 GID 来更改组所有权。

$ sudo chgrp 3000010 /data/staff/sales

我们还发现 staff GID 是 3000012,我们将在这里使用它:

$ sudo chgrp 3000012 /data/staff

现在,我们已经在这些目录上设置了权限。我们希望阻止对目录的一般访问,只允许定义的用户和组。

$ sudo chmod 0750 /data && sudo chmod 2750 /data/staff
&& sudo chmod 2770 –R /data/staff/sales

我们现在准备使用我们的 Samba 服务。

Samba 所需的 iptables 规则

Samba 4 要求在防火墙中打开以下端口:

  • NetBIOS 名称服务端口 137 和 138 上的 UDP 协议
  • 用于 NetBIOS 会话的端口 139 上的 TCP 协议
  • Samba 服务器的 Microsoft-dn TCP 端口 445

对于 Ubuntu,我们只需要添加以下命令:

$ sudo ufw allow samba
$ sudo ufw allow bind9

有许多端口正在监听 Samba。最好将您的 ufw 默认策略更改为deny,以拒绝对端口的访问,除非明确允许。

$ sudo ufw default deny

这样做时应该小心,因为它可能会阻止其他合法进程侦听主机。

Note

我们在第七章中讨论了iptables

在 Linux 上挂载 Samba 共享

Linux 主机也可以使用mount命令和cifs类型挂载 Windows 共享。你需要安装cifs-utils软件包。然而,一些 Linux 发行版不包括读写 NTFS 共享的能力,因为微软认为这样做侵犯了它的专利。这两个发行版都允许挂载 NTFS 和 FAT 文件系统,您可以在cifs-utils包中找到它。

您需要创建/data/sales目录。要在 Linux 主机上挂载 Samba 共享,您应该执行类似于下面的操作:

$ sudo mount -t cifs //dc1.samba.example.com/sales /data/sales -o username=jsmith
Password for jsmith@\dc1.samba.example.com\sales:  *****************

这将在/data/sales目录下挂载远程 Samba 共享/data/staff/sales。您传递远程主机和您的用户名。您将被要求提供一个密码,然后共享应该被挂载在/data目录下。有关挂载 Samba 共享的更多信息,请阅读man mount.cifs页面。

我们可以用我们的mount命令看到挂载的共享。

$ sudo mount
\\dc1.samba.example.com\sales on /data/sales type cifs (rw,relatime,vers=1.0,cache=strict,username=jsmith,domain=SAMBA,uid=0,noforceuid,gid=0,noforcegid,addr=192.168.0.1,unix,posixpaths,serverino,acl,rsize=61440,wsize=65536,actimeo=1)

在前面的例子中可以找到cifs mount的默认选项,完整的细节可以在man mount.cifs页面中找到。你可以看到我们附上的用户名;域名是SAMBA。您可以看到rsizewsize,它们是可以在主机和 Samba 服务器之间发送的数据的最大字节大小,如果需要,这是可以配置的。cache=strict设置缓存策略,支持并遵循 CIF/SMB2 协议;其他选项有noneloosely

如果我们想通过fstab自动挂载它,我们需要在引导时提供凭证。这些凭证需要在文件系统上的一个文件中以纯文本的形式提供,最好是在用户的主目录中。当然,这是一个安全问题,因为在文件系统上存储纯文本凭证可能会在受损的系统上被窥探,并被用来访问其他系统。但是,如果选择这种方式,您可能希望 Samba 拥有不同于其他系统的单独凭证(这会增加管理负担)。

因此,首先需要在主目录中创建.smb_credentials文件。它应包含以下内容:

username=bsingh
password=C0mpl#xp$sSw0rd

您应该通过以下方式保护它:

$ chmod 0600 .smb_credentials

您需要编辑您的/etc/fstab文件,并为自动挂载添加以下内容:

//dc1.samba.example.com/sales /data/sales cifs _netdev,credentials=/home/bsingh/.smb_credentials

这对于单一桌面访问很有用,但是如果人们共享开发服务器,这就变得更复杂了。

在许多人共享一台服务器的情况下,您可以使用multiuser mount 选项共享对共享目录的访问。用户通过本地用户和共享装载连接获得对这些共享的访问权限。例如,用户jsmithbsingh可以通过一个公共的本地用户共享一个公共挂载。

在本例中,组sales不需要存在于我们装载共享的本地 Linux 服务器上,但是用户exbackup也需要存在。在 Linux 服务器上也有jsmithbsingh的本地用户账户。

在 Samba 服务器上,我们将创建一个用户,我们将使用该用户作为一组用于挂载的共享凭证。记得给这个用户一个唯一的复杂密码。

$ sudo samba-tool user add sharedcreds

我们会将他们添加到sales组。

$ sudo samba-tool group addmembers sales sharedcreds

当我们正常装载共享时,我们提供用户名和密码,并使用它来管理我们的装载。这允许只安装一个/data/sales,并且只有一组用户凭证能够访问它。在共享的multiuser环境中,我们可以为一个普通用户挂载共享。从本地 Linux 服务器上的用户会话中,我们可以向内核提供凭据,内核将允许我们使用这些凭据访问共享。

在本地 Linux 服务器上,我们将把/data/sales挂载为sharedcreds用户(本地 Linux 服务器上不存在的用户)。我们通过cifscreds命令这样做:

$ sudo cifscreds add -u sharedcreds dc1.samba.example.com

我们可以使用任何拥有sudo访问权限的用户来做这件事。我们提供了add参数、用户以及我们想要访问的主机。要删除这些凭证,我们可以发出以下命令:

$ sudo cifscreds clear –u sharedcreds

查看cifscredsman页面,了解其他选项。我们现在可以使用以下命令在本地服务器上装载共享:

$ sudo mount -tcifs //dc1.samba.example.com/sales /data/sales/ -o multiuser,username=sharedcreds, \
uid=exbackup,gid=3000017

这将使用sharedcreds用户挂载/data/sales共享。但是因为没有本地的sharedcreds用户,我们没有权限写入/data/sales。如果我们从jsmith的角度来看,我们可以看到它也无法进入。

[jsmith@backup]
$ cp text.file /data/sales/
cp: cannot create regular file '/data/sales/text.file': Permission denied
[jsmith@backup ∼]$ ll /data/
ls: cannot access /data/sales: Permission denied
total 20
d?????????? ? ?        ?         ?            ? sales

用户jsmith甚至看不到/data/sales目录上的权限,这就是????的原因。我们也需要向内核提供我们的凭证,以便它可以管理我们到那个挂载的会话。

$ sudo cifscreds add –u jsmith  dc1.samba.example.com

jsmith用户将需要sudo访问cifscreds;否则,它将无法执行该命令。现在我们已经将凭证添加到内核中,我们可以列出sales目录,并使用exbackupsales组权限对其进行读写操作。

[jsmith@backup ∼]$ ll /data/sales/
total 1405408
-rwxrwx---+ 1 exbackup 3000017     66376 Nov 26 17:14 logo.png
-rwxrwx---+ 1 exbackup 3000017 479678976 Nov 27 13:21 forecast-2016.xls
-rwxrwx---+ 1 exbackup 3000017 479678976 Nov 27 14:04 media.docx

现在,bsingh也需要访问sales目录。我们需要将用户添加到 Samba sales组,然后在本地 Linux 服务器上的用户会话中设置他们的 Samba 凭证。因此,在 Samba 服务器上,我们发出以下命令:

$ sudo samba-tools group addmembers sales bsingh

现在,在bsingh的本地 Linux 服务器登录中,我们发出以下命令:

$ sudo cifscreds add -u bsingh dc1.samba.example.com

现在bsingh也可以访问安装在/data/sales上的 Samba 共享。

在 macOS 上增加股份

我们将向您展示如何使用图形界面在 macOS 上挂载 Linux Samba 共享。这在 macOS 或 Windows 桌面上是一个简单的过程,并且遵循相同的模式。

我们需要能够解析 Samba 服务器的主机名。如果您无法解析dc1.samba.exmaple.com主机名,请检查您的/etc/resolv.conf,或者,如果您使用的是 Windows,请检查您的网络设置。

首先,我们使用 Finder 挂载 Samba 共享。一旦应用程序启动,选择去➤连接到服务器,或者你可以按⌘ K,并获得相同的屏幕。

在图 13-1 中,我们只需添加我们的服务器地址并点击连接。

A185439_2_En_13_Fig1_HTML.jpg

图 13-1。

Adding server address

下一步是添加凭证,如图 13-2 所示。

A185439_2_En_13_Fig2_HTML.jpg

图 13-2。

Adding Samba credentials

如果我们想安全地保存这些凭证,我们可以将它们添加到我们的钥匙串中,这是一个加密的凭证保险箱(图 13-3 )。

A185439_2_En_13_Fig3_HTML.jpg

图 13-3。

Adding credentials to the keychain

现在我们需要做的就是点击连接(图 13-4 )。

A185439_2_En_13_Fig4_HTML.jpg

图 13-4。

Successfully mounted

如图 13-4 所示,我们已经成功挂载了sales目录。互联网上有大量的指导页面,可以帮助您使用特定版本的软件,或者帮助您解决 Windows 和 macOS 可能出现的不同错误。

网上已经有几个关于为 Windows 主机挂载驱动器的资源,比如这个: www.laptopmag.com/articles/map-network-drive-windows-10 。我们将把它作为一个练习留给你。该过程类似于我们为 Mac 安装驱动器的过程,也类似于安装其他 Windows 网络驱动器的过程。

资源

有关设置 Samba 的更多信息,请参见以下资源:

NFS 分享:Linux 到 Linux

Linux 主机也可以像 Samba 一样相互挂载共享。传统上,这是通过网络文件系统(NFS)在 Linux 和 Unix 主机上实现的。最新版本的 NFS 4 比以前的 NFS 版本有很多优势。也就是说,现在它只需要一个端口,而在此之前它需要几个端口,在此之前,您无法知道它将使用哪些端口!这使得使用防火墙变得不可能,这当然使得许多安全管理员非常乐意否认防火墙在他们的网络上的存在。它已经从中吸取了教训,今天是一个值得尊敬的网络公民。

我们将快速向您展示如何在主机之间共享文件系统,通常称为网络挂载。在 Ubuntu 主机上,你需要安装nfs-server包。在 CentOS 主机上,您需要安装nfs-utils

NFS 要求在您的防火墙上打开 TCP 2049 端口。对于 CentOS,您可以这样添加:

$ sudo firewall-cmd --permanent --zone public --add-service nfs
$ sudo firewall-cmd --reload

在 Ubuntu 上,您可以添加以下内容:

$ sudo ufw allow 2049/tcp

现在,让我们确保 NFS 服务器正在我们的主机上运行。如果它没有运行,您可以通过发出以下命令来启动它(在 Ubuntu 上将nfs-server替换为nfs):

$ sudo systemctl restart nfs
$ sudo systemctl enable nfs

一旦完成,您需要编辑/etc/exports文件。

NFS 从/etc/exports文件中读取其共享指令。在这里,您可以添加想要共享的目录以及一些关于如何共享它们的选项。您需要使用以下语法:

directory network(nfs_options),network(nfs_options)

您可以选择想要共享的目录、想要共享的网络,然后选择几个 NFS 选项。看一下我们要用的那个:

/data/staff 192.168.0.2/255.255.255.255(rw,root_squash,fsid=0)

这里我们将把/data/staff目录共享给位于 192.168.0.2/32 的主机。这个 IP 地址也可以是一个 FQDN,比如fileserver2.example.com,或者是一个完整的域名,比如*.example.com,如下所示:

/data/staff 192.168.0(rw,root_squash,fsid=0) *.example.com(rw,root_squash,fsid=0)

接下来,我们设置以下选项:rw,root_squash,fsid=0。第一个选项(rw)允许共享可读写。选项root_squash意味着远程主机上的 root 用户将 UID/GID 设置为匿名 UID/GID,这意味着该用户的 root 权限在该共享上有效。这是为了保护网络安装不被远程root用户破坏。

fsid选项标识 NFS 正在导出的文件系统。这(fsid=0)告诉 NFS/data/staff是所有导出文件系统的根,而不是/data。这意味着当我们发出下面的mount命令时:

$ sudo mount nfs1:/ /path/to/mountpoint

只有/data/staff和它下面的所有东西可以从nfs1:/挂载。这也意味着你不必用你的mount命令指定nfs1:/data/staff。当你挂载nfs1:/时,你也将看不到/data目录中的任何其他目录。

还有许多其他选项可以指定,我们建议您阅读导出的man页,以获得更多详细信息。要激活这些设置,您需要运行以下命令:

$ sudo exportfs –a

前面的命令将导出或取消导出/etc/exports中列出的所有目录。您也可以使用exportfs –rv,它也将更新导出的目录并删除未导出的目录。这些选项将导出与/var/lib/nfs/etab和 Linux 内核同步。

既然已经定义了新的网络挂载,我们将尝试在远程主机上挂载我们的共享。让我们看看我们的 NFS 山是否有人招待。您可以使用showmount命令通过发出以下命令来检查您的 NFS 股票:

$ sudo showmount -e localhost
Export list for localhost:
/data/staff 192.168.0

您可以看到,输出显示了我们的 NFS 装载以及可以连接到它的主机 IP 地址。

在远程主机 192.168.0.2 上,我们需要发出以下命令来将共享/data/staff挂载到/data/remote(在运行该命令之前需要存在/data/remote):

$ sudo mount -t nfs4 -o rw,intr,hard 192.168.0.1:/data/staff /data/remote

这将把/data/staff目录挂载到远程主机上的/data/remote目录。您会注意到,我们已经指定了共享名/data/staff,后面跟着要装载的主机,如设置192.168.0.1:/data/staff所示。这是因为我们特别请求在那个远程主机上我们有权访问的目录;然而,如果我们愿意,我们可以指定/,我们将挂载我们可以访问的所有共享。

我们使用mount命令挂载一个类型为nfs4的文件系统。我们在这个挂载上设置了以下选项:读/写、可中断(由intr指定)和hard。第一种是不言自明的;后两个不是。NFS 传统上有一个怪癖,如果共享文件系统的主机出现问题,它会导致所有加入它的主机受到严重影响,直到服务恢复或主机重启。intr或中断,允许在等待服务器响应时中断 NFS 4 操作。hard选项意味着文件系统将被视为本地挂载的文件系统。如果它被设置为soft,作为hard的替代,文件系统将在空闲一段时间后自动卸载。有关安装选项的更多信息,请参见mountman页。

要将它设置为在主机重启时自动挂载,我们需要将它添加到/etc/fstab文件中:

192.168.0.1:/ /data/remote nfs4 rw,hard,intr,_netdev 0 0

在这里,我们从主机 192.168.0.1 访问 NFS 文件系统,并在/data/remote目录下挂载远程共享。我们指定了与之前相同的选项,并增加了一个选项。_netdev选项告诉我们的主机在我们的网络设备启动之前不要尝试挂载我们的文件系统。如果未设置此项,主机将无法装载文件系统,并等待很长时间,直到失败且尝试超时。

NFS 故障排除

众所周知,NFS 很难排除故障,因为它提供有限的日志记录和模糊的错误消息。它正在变得越来越好,您可以通过在mount命令中添加–v来尝试一些基本的故障排除,这会给您提供更多的信息。

$ sudo mount -v -tnfs4 192.168.0.30:/data/cows /data/remotes/cows -oro
mount.nfs4: timeout set for Fri Sep 30 03:29:32 2016
mount.nfs4: trying text-based options 'addr=192.168.0.30,clientaddr=192.168.0.1'
mount.nfs4: mount(2): No such file or directory
mount.nfs4: mounting 192.168.0.30:/data/cows failed, reason given by server: No such file or directory

作为主流 Linux 操作系统的一部分,内核 NFS 服务器有一个“用户空间”或基于 FUSE 的替代品。它被称为 NFS-甘尼萨,可以在这里找到:

FUSE 是 UserSpacE 中 Filesystem 的缩写,意思是 NFS 服务器不在内核中执行,而是使用内核模块访问必要的资源,在用户空间中执行(见 https://en.wikipedia.org/wiki/Filesystem_in_Userspace )。

NFS 服务器的设计目的不是在不进行实质性修改的情况下实现高可用性。没有本地的群集概念,他们需要块级设备复制或群集文件系统、浮动 IP 寻址等来扩展。现在是时候向您展示一种替代方案了:分布式网络文件系统。

资源

你可以在这里找到更多关于 NFS 的信息:

分布式网络文件系统

分布式网络文件系统(DNFS)是一个使用商用硬件的大型、分布式和可扩展的文件系统。分布式网络文件系统对于处理磁盘映像文件、媒体文件和用于分析处理的可伸缩数据非常有用。与简单的 NFS 相比,DNFS 有几个优点:

  • 分布式(丢失一台服务器不会影响服务)
  • 可扩展至数 Pb
  • 地理分布,能够跨数据中心使用

有几种不同的解决方案可供选择。我们将向您展示如何设置 GlusterFS,但是您也可以根据您的工作量或偏好从几个备选方案中进行选择。

对于跨许多服务器共享文件的直接文件系统,您可能不希望 Hadoop HDFS,因为它更适合 Hadoop map-reduce工作负载,并且要求您使用 Hadoop API 进行文件系统访问。其他的都提供了 POSIX 兼容的文件系统,您可以在您的服务器上挂载和访问文件和目录。

CephFS 和 GlusterFS 在架构上最相似的地方在于它们都不需要元数据服务器,而 BeeGFS 和 HDFS 的架构相似之处在于它们都需要元数据服务器。Ceph 本身是对象块存储和可靠的自治分布式对象存储(RADOS ),可以支持大量数据。所有这些都可以得到商业支持许可证或创建它们的相关社区的支持。

格鲁斯特

GlusterFS 是一个网络文件系统,启动其开发的公司(Gluster Inc .)被 Red Hat 收购。Red Hat 提供了软件的支持模型,如果您有复杂的需求,那么您应该考虑购买它。你可以运行网站上的开源版本( https://www.gluster.org/ )。

因为我们要安装分布式服务,所以至少需要三台主机。与所有分布式服务一样,拥有奇数个主机通常是首选,因为这有助于避免“裂脑”在设计集群时,您还可以考虑其他一些事情,例如将物理服务器放在不同的机架或不同的数据中心,以帮助实现数据弹性。

Note

裂脑是指在一个集群中,至少有两台服务于同一应用程序的服务器彼此看不到对方,但仍能响应客户端。在这种情况下,数据完整性和一致性开始偏离,因为两台服务器都继续提供和存储数据,但不再能在彼此之间同步任何数据。

GlusterFS 的关键概念

在考虑 GlusterFS 时,有几个关键概念。

  • GlusterFS 网络文件系统是“无元数据”分布式文件系统,这意味着它没有用于处理文件位置数据的专用元数据服务器。相反,它使用确定性散列技术来发现文件位置(参见 DHT http://gluster.readthedocs.io/en/latest/Quick-Start-Guide/Architecture/ )。
  • GlusterFS 导出一个完全符合 POSIX 的文件系统,这基本上意味着您可以从 Unix 和类 Unix 操作系统(比如 Linux)挂载、读取和写入 GlusterFS。
  • GlusterFS 是一个用户空间文件系统,这意味着它不在 Linux 内核中运行,而是使用 FUSE 模块。
  • 可信池是在 GlusterFS 集群中运行的服务器网络。每台服务器称为一台对等机。
  • 砖块是 GlusterFS 的基本储存单位。它被导出到受信任池中的服务器。
  • 卷是砖块的逻辑集合。

有几种方法可以将数据存储在 GlusterFS 中。这些概念类似于 RAID 中的概念。根据您的配置选项,文件可以存储在具有或不具有冗余级别的 Gluster 卷中。

  • 分布式卷:默认情况下,如果没有指定分发类型,GlusterFS 会创建一个分布式卷。在这种设置中,文件可以存储在卷中的任何块上,没有冗余。
  • 复制卷:在复制卷设置中,您的文件会跨卷中的所有块进行复制。这至少需要两块砖,并提供一定程度的冗余。
  • 分布式复制卷:此配置中的文件存储在复制的砖块集合中。这里砖块的数量必须是副本数量的倍数。这是高可用性文件存储的配置。
  • 条带卷:对于许多客户端频繁访问的大文件,此配置将大文件存储在块中(块的数量与砖的数量相同)。这种配置提供零冗余。
  • 分布式条带卷:在这里,我们将数据条带化(分块)到许多砖块上。砖块的数量必须是条带数量的倍数。这可以提供更快的速度,但仍然不提供冗余。

安装 GlusterFS

幸运的是 GlusterFS 很容易安装。Ubuntu 和 CentOS 都有可用的原生包。但是要在 CentOS 上安装最新的 GlusterFS,我们就要安装 CentOS 特别兴趣小组(SIG 见 https://wiki.centos.org/SpecialInterestGroup )。为此,我们将为 GlusterFS 版本 3.7.x 安装 YUM 存储库,如下所示:

$ sudo yum install centos-release-gluster37
$ sudo yum install –y glusterfs-server

对于 Ubuntu,我们将添加 Gluster 团队 PPA(这个 PPA,或个人包存档,并不直接与 Gluster 相关,而是一组 Ubuntu 维护者)。这也将为我们提供最新的 Gluster 3.7 . x 版本。

$ sudo add-apt-repository ppa:gluster/glusterfs-3.7 && sudo aptitude update
$ sudo aptitude install –y glusterfs-server

现在,对于 Ubuntu 和 CentOS,让我们确保服务在引导时启动。在 Ubuntu 上执行此操作:

$ sudo systemctl enable glusterfs-server

为 CentOS 执行此操作:

$ sudo systemctl enable glusterd glusterfsd

现在它们已经安装好了,我们可以配置 GlusterFS 了。

配置 GlusterFS

让我们看看如何设置我们的服务器。我们有三台服务器,每台都有一个 50GB 的驱动器用于存储数据。我们将拥有一台 Ubuntu 和两台 CentOS 服务器。Ubuntu 服务器已经将 50GB 的磁盘连接到/dev/sdc,而在 CentOS 上,它连接到/dev/sdb

我们将使用 XFS 文件系统格式化这些驱动器,因为这是推荐的文件系统。考虑到这一点,如果您愿意,可以用 ext4 格式化。我们通过以下方式做到这一点:

$ sudo mkfs.xfs –L brick1_r1 /dev/sdb
$ sudo mkdir –p /data/brick1_r1 && sudo bash –c 'echo LABEL=brick1_r1  /data/brick1_r1  xfs  defaults  1  2 >> /etc/fstab'
$ sudo mount –a

我们已经选择创建带有brickN_rN后缀的设备标签和模块,其中brickN是一个递增的数字,_rN是它们所属的复制集。这允许我们在复制集中添加和替换数据块。给事物命名是困难的,如果你有一个更好的命名方案,没有理由遵循这个。让我们在我们的 Ubuntu 服务器上启动 Gluster 为此,我们执行以下命令:

$ sudo systemctl start glusterfs-server

如果我们选择了 CentOS 服务器,我们将发出以下命令:

$ sudo systemctl start glusterd && sudo systemctl start glusterfsd

当然,我们可以通过发出sudo systemctl status <service.name>来检查服务是否正确启动,但是我们将运行其中一个gluster命令。如果一切正常,我们应该不会收到错误。

$ sudo gluster peer status
Number of Peers: 0

太棒了。我们已经使用了gluster命令来查询 Gluster 集群并检查对等机的状态。我们没有添加任何对等项,因此我们预计在此阶段该值为 0。

您可以通过gluster命令实用程序访问和配置 GlusterFS 服务。语法如下所示:

$ sudo gluster <subcommand> <options> <args...>

gluster help选项在描述每个指令的子命令、选项和参数时非常有用。表 13-2 列出了一些你会用到的常用子命令。

表 13-2。

Gluster CLI Commands

| `volume info` | 显示任何已配置卷的信息 | | `volume list` | 列出当前卷 | | `volume create` | 创建卷 | | `volume delete` | 删除卷 | | `volume start/stop` | 启动或停止卷 | | `volume add-brick` | 将砖添加到卷 | | `volume replace-brick` | 取代砖块 | | `volume top` | 给出 Gluster 音量指标 | | `volume set` | 打开或设置音量选项` ` | | `peer status` | 给出集群中对等方的状态 | | `peer probe` | 探测并添加其他对等方 | | `pool list` | 列出受信任存储池中的所有节点 |

GlusterFS:添加对等点和创建卷

我们选一个主机吧(哪个都无所谓);我们将开始我们的集群。我们将选择我们的 Ubuntu 服务器,但任何都可以。我们将在这台服务器上执行以下操作:

  • 将对等机添加到我们的集群
  • 在我们的砖块上创建一个卷目录
  • 创建卷

我们将主机命名为au-mel-dfs-1au-mel-dfs-3,并将其添加到我们的 DNS 区域。Ubuntu 主机已经分配给au-mel-dfs-3。为了将我们的对等体添加到集群中,我们发出以下命令:

$ sudo gluster peer probe au-mel-dfs-1
peer probe: success.

我们对au-mel-dfs-2做同样的事情。现在我们可以再次展示我们同伴的状态了。

$ sudo gluster peer status
Number of Peers: 2

Hostname: au-mel-dfs-1
Uuid: a605a82d-fa77-48dd-8183-95a960547b1f
State: Peer in Cluster (Connected)

Hostname: au-mel-dfs-2
Uuid: 5ceee284-616b-4c2d-87d7-1c44f4cbdca0
State: Peer in Cluster (Connected)

我们现在也可以在gluster可信存储池中看到这些主机。

sudo gluster pool list
UUID                                    Hostname        State
a605a82d-fa77-48dd-8183-95a960547b1f    au-mel-dfs-1    Connected
5ceee284-616b-4c2d-87d7-1c44f4cbdca0    au-mel-dfs-2    Connected
30cf104f-00b2-4371-8935-d91719a2e17b    localhost       Connected

每个对等体都有一个 UUID,您可以看到主机名和当前状态。只有该列表中的对等体可以探测新的对等体。现在,我们将在所有对等体上创建我们的卷目录。这可以是任何名字,但我们称自己的名字为vol1

$ sudo mkdir /data/brick1_r1/vol1

我们现在将创建我们的 Gluster 卷。我们使用下面的命令来完成这项工作:

$ sudo gluster volume create vol1 replica 3 \
       au-mel-dfs-1:/data/brick1_r1/vol1 \
       au-mel-dfs-2:/data/brick1_r1/vol1 \
       au-mel-dfs-3:/data/brick1_r1/vol1
volume create: vol1: success: please start the volume to access data

我们创建了一个名为vol1的新 Gluster 卷。它将在三块砖上复制每个文件。我们正在添加的这些砖块位于我们的三个对等体上,我们之前已经列出了这些对等体。

Note

这里值得注意的是,每个文件有三个副本会使您的存储需求增加至少三倍。

我们现在可以看到与该卷相关的信息;让我们运行以下命令:

$ sudo gluster volume info
Volume Name: vol1
Type: Replicate
Volume ID: e9964568-feef-4f2f-a61d-14ba643b76e5
Status: Created
Number of Bricks: 1 x 3 = 3
Transport-type: tcp
Bricks:
Brick1: au-mel-dfs-1:/data/brick1_r1/vol1
Brick2: au-mel-dfs-2:/data/brick1_r1/vol1
Brick3: au-mel-dfs-3:/data/brick1_r1/vol1
Options Reconfigured:
performance.readdir-ahead: on

我们的 Gluster 卷已经创建,我们可以在前面的行中看到详细信息。我们可以看到类型(Replicate)和砖的数量(1 × 3 = 3)。这告诉我们,每个文件将存储在每个砖块上,我们有一组三个砖块,总共三个砖块。

我们可以检查音量的状态。我们可以通过以下方式做到这一点:

$ sudo gluster volume status
Volume vol1 is not started

因此,就像创建卷时的消息一样,它还没有启动,我们必须启动它。现在,让我们使用以下命令来完成这项工作:

$ sudo gluster volume start vol1
volume start: vol1: success

至此,我们已经创建并启动了 Gluster 卷,现在我们准备测试我们可以挂载并写入它。

测试 GlusterFS

在这个测试场景中,我们将把 Gluster 卷安装到另一台服务器上。我们将执行一些写测试。然后我们将执行同样的写测试,并关闭我们装载 Gluster 的服务器。

我们要做的第一件事是安装 Gluster 客户端。现在,如果您打算将 Gluster 与 NFS 或 SMB (Samba)一起使用,您不必这样做。Gluster 客户端确实提供了一个很棒的特性,我们将演示这个特性。

CentOS 需要安装glusterfs-fuse包,Ubuntu 需要安装glusterfs-client

现在我们将安装我们的 Gluster 卷。为此,我们发布了以下内容:

$ mount –t glusterfs au-mel-dfs-1:/vol1 /mnt

我们已经将 GlusterFS 卷安装到了我们的/mnt目录中。看一看df显示如下:

$ df -h /mnt
Filesystem           Size  Used   Avail  Use%    Mounted on
au-mel-dfs-1:/vol1   50G   33M    50G    1%      /mnt

如图所示,我们成功装载了 50GB 的卷。我们可以做一个写测试,看看是否可以向其中写入数据。为此,我们将使用一个for循环 bash 一行程序。它将在递增大小的挂载目录中的 100 个文件中写入零。

$ for a in {1..100} ; do sudo dd if=/dev/zero of=/mnt/datafile${a} bs=4096 count=$a ;done
....
100+0 records in
100+0 records out
409600 bytes (410 kB, 400 KiB) copied, 0.0287382 s, 14.3 MB/s

我们可以看到已经创建了测试数据文件,当查看df时,我们将看到所有主机都报告了其砖块的相同磁盘使用情况。

$ df -h /data/brick1_r1/
Filesystem      Size  Used Avail Use% Mounted on
/dev/sdc        50G   62M  50G   1% /data/brick1_r1

让我们做另一个练习。当您挂载 GlusterFS 卷时,Gluster FUSE 客户端将从一个对等体接收到一个volfile。然后,它将从列出的任何一个对等点装入卷,不一定是我们首先联系的那个对等点。这一次,我们将删除我们在mount命令中使用的主机au-mel-dfs-1,我们将看到在复制文件的过程中会发生什么。

$ while true ; do sudo cp /var/log/syslog /mnt/ ; date ; done
....
Sat Sep 17 15:09:05 UTC 2016
Sat Sep 17 15:09:05 UTC 2016
Sat Sep 17 15:09:05 UTC 2016
....

前面的命令说,让我们无限期地将syslog文件复制到/mnt,并在两次复制之间打印当前日期。每次复印后,你都会看到日期在屏幕上快速闪烁。同时,在au-mel-dfs-1上输入reboot命令。

$ sudo reboot

主机将立即重新启动。当这一切发生时,有两件事可能会发生。

如果我们的主机已经从au-mel-dfs-1装载了卷,您将会看到复制暂停。您可以看出我们暂停了,因为日期输出将停止增加。我们需要等待 Gluster 网络 ping 超时,或者主机重启,服务恢复。如果主机没有出现,网络 ping 超时,Gluster 客户端将请求访问volfile中的其他对等体之一,并尝试要求它需要的资源(文件锁等;这可能需要一分钟)。网络超时是可配置的,我们将很快向您展示如何更改它。一旦完成,我们将继续复制。

如果我们的主机从其他对等机之一装载卷,不会发生任何令人兴奋的事情,复制将继续。如果是这种情况,当au-mel-dfs-1出现时重新启动其他对等机,看看会发生什么。

管理 GlusterFS

在这一节中,我们将向您展示如何管理 GlusterFS 服务器。我们想向您展示以下内容:

  • 设置音量选项
  • 扩展我们的存储
  • 更换砖块
设置配置选项

我们可以更改每个卷的选项设置。从更改vol1的集群卷配置开始,我们将把我们的网络 ping 超时从默认的 42 秒设置为 15 秒。这个设置告诉 GlusterFS 客户机,在放弃并从另一个 Gluster 对等体重新获取所有资源之前,应该等待 15 秒。这是一个昂贵的行动,我们真的应该尽可能地推迟这一行动。这里,仅作为演示,我们将向您展示如何更改它。

我们使用gluster volume子命令来更改卷上的选项。语法如下:

$ sudo gluster volume set <volume name> <option> <value>

因此,在我们的例子中,我们将发出以下命令来更改network.ping-timeout设置:

$ sudo gluster volume set vol1 network.ping-timeout 15

如果我们查看卷信息,我们现在会在Options Reconfigured部分看到我们的更改。

$ sudo gluster volume info
Volume Name: vol1
Type: Replicate
Volume ID: e9964568-feef-4f2f-a61d-14ba643b76e5
Status: Started
Number of Bricks: 1 x 3 = 3
Transport-type: tcp
Bricks:
Brick1: au-mel-dfs-1:/data/brick1_r1/vol1
Brick2: au-mel-dfs-2:/data/brick1_r1/vol1
Brick3: au-mel-dfs-3:/data/brick1_r1/vol1
Options Reconfigured:
network.ping-timeout: 15
performance.readdir-ahead: on

使用volume set子命令可以改变大量的配置选项;你可以在这里看到它们:

扩展 GlusterFS 卷存储

当然,有时你需要扩大存储容量,而这正是 Gluster 的优势所在,相比之下,它的替代品就像普通的老 NFS。扩展选项归结为向现有对等体添加新的块,或者向卷中添加新的对等体和块。我们已经为现有的同行添加了一个新的 50GB 硬盘,并且我们将把它们添加到vol1卷中。

我们将创建另外三个 50GB 的驱动器,并将它们作为另一组驱动器添加到卷中。这将为我们提供总共 100GB 的可用存储空间,冗余系数为 3。我们将按照相同的说明制作第一块砖。

$ sudo mkfs.xfs -L brick1_r2 /dev/sdc && sudo mkdir -p /data/brick1_r2 && \
           sudo bash -c 'echo LABEL=brick1_r2 /data/brick1_r2 xfs defaults 1 2 >> /etc/fstab' && sudo mount –a
$ sudo mkdir /data/brick1_r2/vol1

在这里,我们创建并装载了新设备(在 CentOS 主机上的/dev/sdc),并在新设备上创建了一个vol1目录。这就像我们之前做的一样,但是我们已经将命令压缩成两行。现在,我们创建新的副本集,并将其添加到vol1卷。

$ sudo gluster volume add-brick vol1 replica 3  \
          au-mel-dfs-1:/data/brick1_r2/vol1  \
          au-mel-dfs-2:/data/brick1_r2/vol1  \
          au-mel-dfs-3:/data/brick1_r2/vol1
volume add-brick: success

我们现在已经将这些新砖块添加到了vol1卷中。如果我们运行volume info子命令,我们将看到我们有了一个新的副本集。

$ sudo gluster volume info
Volume Name: vol1
Type: Distributed-Replicate
Volume ID: e9964568-feef-4f2f-a61d-14ba643b76e5
Status: Started
Number of Bricks: 2 x 3 = 6
Transport-type: tcp
Bricks:
Brick1: au-mel-dfs-1:/data/brick1_r1/vol1
Brick2: au-mel-dfs-2:/data/brick1_r1/vol1
Brick3: au-mel-dfs-3:/data/brick1_r1/vol1
Brick4: au-mel-dfs-1:/data/brick1_r2/vol1
Brick5: au-mel-dfs-2:/data/brick1_r2/vol1
Brick6: au-mel-dfs-3:/data/brick1_r2/vol1
Options Reconfigured:
network.ping-timeout: 15
performance.readdir-ahead: on

如您所见,我们在卷中添加了三个新模块,即模块 4-6。您还可以看到,我们现在有两组三个磁盘,总共有 6 个磁盘(2 × 3 = 6)。查看我们在测试服务器上挂载的卷,我们可以看到该卷已经增加到 100GB。

$ df -h /mnt
Filesystem            Size    Used  Avail   Use%   Mounted on
au-mel-dfs-1:/vol1    100G    85M   100G    1%     /mnt

更换砖块

我们是愚蠢的。我们刚刚清除了au-mel-dfs-1上的/data/brick1_r2/vol1目录。这现在将该砖块置于失败状态。我们可以从系统日志和volume status子命令中查看。

$ sudo gluster volume status
Status of volume: vol1
Gluster process                             TCP Port  RDMA Port  Online     Pid
--------------------------------------------------------------------------------
Brick au-mel-dfs-1:/data/brick1_r1/vol1        49152      0         Y       1292
Brick au-mel-dfs-2:/data/brick1_r1/vol1        49152      0         Y       3198
Brick au-mel-dfs-3:/data/brick1_r1/vol1        49152      0         Y       1978
Brick au-mel-dfs-1:/data/brick1_r2/vol1        N/A        N/A       N       N/A
Brick au-mel-dfs-2:/data/brick1_r2/vol1        49153      0         Y       11652
Brick au-mel-dfs-3:/data/brick1_r2/vol1        49153      0         Y       2133

如您所见,当前状态显示au-mel-dfs-1:/data/brick1_r2/vol1的服务没有监听端口,也没有 PID。我们要换掉它。我们向我们的 50GB 对等设备添加了另一个设备,并像扩展卷时一样对其进行了格式化和装载。

$ sudo mkfs.xfs  -L brick2_r2 /dev/sdd && sudo mkdir -p /data/brick2_r2 && \
         sudo bash -c 'echo LABEL=brick2_r2 /data/brick2_r2 xfs defaults 1 2 >> /etc/fstab' && \
         sudo mount –a
$ sudo mkdir /data/brick2_r2/vol1

现在,为了替换损坏的砖块,我们使用以下命令:

$ sudo gluster volume replace-brick vol1 au-mel-dfs-1:/data/brick1_r2/vol1 au-mel-dfs-1:/data/brick2_r2/vol1 commit force
volume replace-brick: success: replace-brick commit force operation successful

现在,我们可以看到卷状态的运行状况。

$ sudo gluster volume status
Status of volume: vol1
Gluster process                             TCP Port  RDMA Port  Online     Pid
---------------------------------------------------------------------------------
Brick au-mel-dfs-1:/data/brick1_r1/vol1      49152       0          Y       12252
...
Brick au-mel-dfs-3:/data/brick2_r2/vol1      49154       0          Y       2280

砖块已经被替换,我们可以从df命令中看到数据已经被同步到其中。

$ df -h /data/brick2_r2
Filesystem      Size  Used Avail Use% Mounted on
/dev/sde        50G   42M   50G   1% /data/brick2_r2

管理文档

文件共享是您公司分发文档的重要组成部分。然而,当涉及到跟踪和版本控制文档时,它确实有一定的局限性。如果在编写和检查权限方面没有过多的开销,您就不能很好地控制谁在访问您的文档。此外,您不能锁定正在编辑的文件,因此两个人可以访问同一个文件,分别进行更改,并且当一个用户将其保存回文件共享时,会破坏另一个用户的工作。很明显,仍然需要文件共享,但是有一种更好的方法来管理您企业中的文档。

一个好的文档管理系统(DMS)将理想地为你实现这五件事:

  • 与其他员工安全地共享您的文档
  • 为文档提供版本控制,这样以前的编辑就不会丢失
  • 要求文档被签出,这样两个人就不能同时编辑同一文档
  • 设计 DMS 的样式,使其符合您公司创建、审阅和发布文档的工作流程
  • 为所有文档共享提供单一入口点,而不必管理多个文件服务器及其文件共享

有了一个好的文档管理系统,您通常会有一个 web 门户,成为所有文档访问的中心点。这可以是安全内部网的一部分,远程办公室通过虚拟专用网络(VPN)链接到您的主办公室来访问它。

使用文件管理系统

你的公司不需要成为一个大公司来拥有一个好的文档管理系统。在企业早期设计 DMS 时稍加考虑,随着企业的发展和对文档控制的需求越来越明显,将会为您省去许多问题。一个好的 DMS 不仅有助于工作流程,也有助于保护您的文档。为您的文档添加这种版本控制,您与同事共享的数据将更加安全。

当然,我们有在线文档管理,如 Google Docs、Quip 和 Microsoft360,这些可能正好满足您的需求。在许多情况下,使用这些解决方案非常适合没有基础设施且成本相对较低的小型企业。当然,文档是在线的(或部分在线的),因此可能会受到网络问题的影响,但一般来说,它们是非常安全的,高度可用的,而且成本很低。

有几个开源或部分开源的选择,你可能想试试。

  • alfresco:Community edition lgpl v3 许可证,与商业版相比功能有限
  • logical doc:Community edition lgplv 2 许可证,与商业版相比功能有限
  • OpenKM:社区版 GPLv2,与商业版相比功能有限

这些解决方案都有在线文档,如果需要,您可以购买支持。我们这样做是因为与 Samba 或 NFS 相比,这很可能是更好的文档管理选择。

打印服务器

在 CentOS 和 Ubuntu 上设置打印机服务器很容易。两种发行版都使用相同的打印服务器软件来管理打印服务。我们将向您展示如何设置 CUPS 打印机服务器,它是 Linux 发行版的标准,并且在 CentOS 和 Ubuntu 上都有一致的 web 界面。

在许多情况下,为打印设置 CUPS 服务器已经变得多余,因为许多现代打印机现在都有自己的打印服务器,并且可以在网络、Wi-Fi 甚至蓝牙上使用。此外,我们真的希望更少的人需要打印,因为在这个时代有许多其他方式来传输和阅读文件。

如果您仍然需要在网络上设置打印机(例如,您有一台 USB 打印机,并且希望在网络上的主机之间共享它),您可以将它连接到 Linux 服务器,甚至连接到一个小的 Raspberry Pi 并使用 CUPS 打印服务器。

Note

树莓派有很多用途;其中之一是成为打印服务器。Raspberry Pi 是一种微型计算机,可以通过 USB 连接到打印机,并可以充当打印服务器。它通常带有 Raspberian 操作系统,这是一个基于 Debian 的 Linux 发行版。一个树莓派更适合用来学习! https://www.raspberrypi.org/

杯子

首先,我们将向您展示如何配置通过 USB 连接到主机的新打印机。然后,您可以将其与网络中的其他主机共享。为了演示这一点,我们将把打印机连接到客户机。

安装和配置杯子

您可以从命令行通过手动编辑 CUPS 配置文件来配置您的打印机(尽管不推荐这样做)。CUPS 还附带了一个 web UI,我们将使用它来添加和共享我们的打印机。

当您插入打印机时,内核将向 Udev 和硬件抽象层(HAL)管理器发送一个事件通知。Udev 是一个用户空间设备管理应用程序,HAL 为桌面程序提供了一致的接口。这意味着文字处理器等应用程序可以一致地知道如何使用打印机设备,日历可以使用一组一致的定义好的规则与智能设备对话。

让我们看看当我们在 Ubuntu 主机上插入 Epson USB 打印机时会发生什么。为了确保一切正常工作,首先我们在日志上发出tail命令,以确保我们的设备已经被内核拾取。

$ sudo dmesg
[13433828.652994] usb 2-1: new high-speed USB device number 4 using ehci-pci
[13433828.786834] usb 2-1: New USB device found, idVendor=04b8, idProduct=0811
[13433828.786847] usb 2-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[13433828.786855] usb 2-1: Product: USB2.0 MFP(Hi-Speed)
[13433828.786860] usb 2-1: Manufacturer: EPSON
[13433828.786865] usb 2-1: SerialNumber: L76010502151505100
[13433828.789721] usblp 2-1:1.1: usblp0: USB Bidirectional printer dev 4 if 1 alt 0 proto 2 vid 0x04B8 pid 0x0811
[13433828.790798] usb-storage 2-1:1.2: USB Mass Storage device detected
[13433828.791051] scsi9 : usb-storage 2-1:1.2
[13433829.790811] scsi 9:0:0:0: Direct-Access     EPSON    Stylus Storage   1.00 PQ: 0 ANSI: 2
[13433829.791627] sd 9:0:0:0: Attached scsi generic sg5 type 0
[13433829.795826] sd 9:0:0:0: [sdf] Attached SCSI removable disk

在这里,您可以看到我们的主机已经识别出我们已经连接了一个 USB 打印机,并将该设备注册为usblp。我们可以通过发出ls /dev/usblp来检查设备是否存在。现在,我们可以满足于我们的设备已被认可,并准备好添加到 CUPS 打印服务的控制之下。

我们首先需要在打印服务器上设置一些东西。我们需要添加一个管理用户来管理我们的打印设备的设置。这可以是任何用户,我们将通过将他们添加到lpadmin组来使bsingh成为我们的打印机管理员。

$ usermod –aG lpadmin bsingh

接下来,除非这个 CUPS 服务器在本地运行,否则我们将允许它被远程管理访问。

$ sudo cupsctl --remote-admin

现在,我们要确保它可以通过我们的打印服务器主机名到达。我们将编辑/etc/cups/cupsd.conf文件并添加以下内容:

LogLevel warn
PageLogFormat
MaxLogSize 0
Port 631
Listen print-srv.example.com
ServerAlias *

这里我们添加了Listen指令,允许它监听我们的打印机服务器接口,并添加了一个ServerAlias *,这样我们就可以使用print-srv.example.com主机名来连接到管理网页。

我们还应该添加主机名print-srv.example.com,并将其映射到我们的 DNS 服务器中的 192.168.0.1。

$ sudo nsupdate –k /etc/bind/dynamic-update-key
> server ns1.example.com
> update add print-srv.example.com 86400 A 192.168.0.1
> send
<ctrl d>

这里我们使用了nsupdate命令来添加我们的print-srv.example.com记录。我们现在应该重启我们的cups服务,并确保它在启动时可用。

$ sudo systemctl enable cups
$ sudo systemctl start cups

现在,我们可以转到 web 管理页面并添加我们的打印机。打开网页,进入网址 https://print-srv.example.com:631 。由于我们没有安装有效的安全套接字层(SSL)证书,我们将收到该站点不受信任的警告。我们将接受这种风险,这将我们带到如下页面,如图 13-5 所示。

A185439_2_En_13_Fig5_HTML.jpg

图 13-5。

The CUPS home page

在此页面中,我们将在管理员列的 CUPS 下单击添加打印机和类。系统将提示我们输入用户名和密码;我们将使用bsingh的证书(图 13-6 )。

A185439_2_En_13_Fig6_HTML.jpg

图 13-6。

Using the administrator’s credentials

在这里,我们将使用添加打印机按钮添加一台打印机(图 13-7 )。

A185439_2_En_13_Fig7_HTML.jpg

图 13-7。

Administration page for CUPS

单击“添加打印机”按钮会弹出我们的第一个打印机对话框。我们有一台通过 USB 连接到主机的本地 Epson 针式打印机,如图 13-8 所示。

A185439_2_En_13_Fig8_HTML.jpg

图 13-8。

Selecting the USB printer

您还会注意到,我们可以添加其他类型的网络打印机,包括通过 Samba 服务器进行打印。您可以在这里看到如何设置 Samba 打印机共享:

我们现在被问了一系列关于打印机的问题。我们被要求给出打印机的名称、描述和位置。这些可以是对你有意义的任何东西。我们将启用这台打印机的共享并允许色彩管理,如图 13-9 所示。

A185439_2_En_13_Fig9_HTML.jpg

图 13-9。

Printer information

然后我们被询问更多信息,这次是关于打印机的型号。我们将从下拉列表中选择(图 13-10 )。

A185439_2_En_13_Fig10_HTML.jpg

图 13-10。

Selecting the printer model

如果找不到打印机型号,我们可以添加一个 PPD 文件。接下来是打印机默认值(图 13-11 )。

A185439_2_En_13_Fig11_HTML.jpg

图 13-11。

Editing or selecting defaults

您可以编辑或选择打印机的默认值。我们将只保留默认值(图 13-12 )。

A185439_2_En_13_Fig12_HTML.jpg

图 13-12。

Ready and waiting for jobs

我们的打印机现在可以打印了。我们现在将它附加到一个客户端。在我们的 macOS 客户端,我们可以再次选择我们的打印机并添加它(图 13-13 和 13-14 )。

A185439_2_En_13_Fig14_HTML.jpg

图 13-14。

The printer is installed and ready

A185439_2_En_13_Fig13_HTML.jpg

图 13-13。

Bonjour finds our print server

现在我们的打印机已经安装好,可以打印了。

摘要

在这一章中,我们探讨了 Linux 的文件和打印共享产品。我们展示了文件共享的传统方法是通过 Samba 服务器和 NFS 服务器,这取决于您想要服务的客户端。我们向您展示了如何使用 GlusterFS 建立可以扩展到 Pb 大小的数据存储。我们讨论了传统文件共享的替代解决方案,如文档管理服务(DMS)。与文件共享相比,它们有许多优势,因为它们提供了更细粒度的访问控制和文档版本控制,并且允许您实现公司工作流。最后,您学习了如何设置 CUPS 打印服务。

在本章中,您学习了如何执行以下操作:

  • 将 Samba 服务器配置为 AD 域控制器
  • 配置 NFS 服务器以在 Linux 主机之间挂载文件系统
  • 安装和配置 GlusterFS
  • 谈论 DMS
  • 配置 CUPS 打印机服务器并将其连接到主机

在下一章中,我们将看看您办公室的备份和恢复。

十四、备份和恢复

备份和恢复数据的能力在任何组织中都至关重要。您不仅需要高质量的备份来恢复删除或覆盖的数据,而且在许多情况下还需要满足您所在国家的法律要求(例如,与保留税务或客户记录相关的要求)。

本章一开始,我们将讨论灾难恢复规划(DRP)和业务连续性管理(BCM),为您提供这些概念的基础。我们将向您展示如何安全地从远程主机复制数据,无论它是在您的网络上还是在世界的另一端。然后,我们将向您介绍备份服务器 Bareos,并向您展示如何使用它来保存和恢复您的文件。同样使用 Bareos,我们将演示如何备份和恢复数据库。最后,我们将讨论 Web-UI 控制台,这是一个用于 Bareos 的基于 Web 的 UI。

学完本章后,您应该能够做到以下几点:

  • 了解 DRP 和 BCM 所需的要求
  • 使用rsync命令将数据从一台主机安全地复制到另一台主机,并使用脚本来自动化该过程
  • 安装和配置 Duply 以备份到亚马逊 S3
  • 安装并配置名为 Bareos 的备份服务器
  • 在 Bareos 中管理您的备份和创建作业
  • 使用 Bareos 将文件恢复到主机
  • 安装和配置 Web-UI 管理控制台

我们将从 DRP 的一般讨论开始。

灾难恢复规划

当然,我们都希望我们的业务不会发生灾难性的事情,但为各种情况做好准备也很重要,以防万一。灾害主要有两类:人为灾害和自然灾害。一台电子邮件服务器宕机一天,导致重要的、时间敏感的业务事项被错过,这是一场与人为错误或机械故障相关的人为灾难,可能会有一个与之相关的恢复过程。另一方面,摧毁办公室的地震是一种自然灾害,需要完全不同的恢复响应。这两种情况都可以根据发生的可能性进行规划。

灾难恢复规划就是要识别、管理和降低风险。这是一个被称为业务连续性管理的总体过程的一部分,或者确保一个企业在面对未知的逆境时能够至少在预定的最低水平上继续运营。BCM 涵盖了您组织的各个方面,应该详细列出您的企业同意的特定服务恢复的时间表。

以下是制定贵组织的 BCM 和 DRP 战略时需要考虑的问题:

  • 我们能否预测我们的业务最有可能面临的中断?从它们中恢复需要哪些步骤?预期恢复的时间表是什么?
  • 降低风险和从每个潜在事件中恢复的相关成本是多少?
  • 我们是否需要一个可以转移业务的共同地点?
  • 在任何潜在的危机情况下,我们是否需要租用额外的设备,如发电机?
  • 在业务中断的情况下,需要联系/沟通哪些人和组织?中断应该如何传达给公众?
  • 在大规模灾难性事件的情况下,决定业务能否实现延续的要点是什么?如果关键基础设施或业务资产遭到破坏,损失会迅速累积。

制定 BCM 和 DRP 计划可能是一个复杂的过程。在您的组织中,您应该有一个 BCM 计划,其中包含风险分析、业务影响分析和危机管理调查的结果,可以由主要业务部门签署。即使是小企业也能从半正式的安排中受益,尽管可能不需要开发完整的 BCM 所需的资源。有关 BCM 和 DRP 的更多信息,我们推荐以下资源:

在本章中,我们将重点介绍备份和恢复数据的过程,这应该是您组织的 BCM 和 DRP 计划的一部分。下一节将介绍备份策略。

备份过程

在选择备份方案时,您需要考虑许多不同的问题。回答这些问题将为您的公司提供备份策略。

  • 我们要备份的是什么?
  • 我们需要多久备份一次数据?
  • 我们希望将数据备份多长时间?
  • 我们应该将备份存储在哪里,存储在什么介质上?

这里重要的是了解你的数据。你需要知道它多久变化一次,变化量有多大。您还需要了解您的存储介质,以及它可以存储多少数据和存储多长时间。数据量可能是一件棘手的事情,如果没有计划,您可能有太多的数据需要备份,或者没有足够的时间来备份数据。在这些情况下,您可能会发现自己在备份不需要的数据,或者您可能需要获得不同的存储设备以获得更快的性能或更大的备份卷。

数据保留期,也称为数据生命周期,也是需要考虑的重要因素。出于法律或税务目的,您可能需要将您的数据保留一段特定的时间,通常是几年。您可能希望将客户端数据保留几年,并且可能希望将其他类型的数据保留较短的时间。根据您正在备份的内容,将数据保存在介质上以恢复几个月前的数据可能完全没有意义;在这种情况下,您可以考虑缩短备份周期来释放介质。

另一件要考虑的事情是日程安排。您可以在哪个时间段安排备份?您可能一周 7 天 24 小时都在运营,几乎没有时间进行离线备份。您可能必须执行热备份(即在某个时间点备份活动主机),并且您可能没有太多时间来执行此操作。对于时间安排,您可能需要考虑每日、每周或每月备份方案。

最后,您需要确定要执行的备份类型。在大多数备份方案中,您可以将备份分为三种类型:完整备份、增量备份和差异备份。完整备份是数据的最终备份,也是执行的最大和最长的备份。增量备份是对自上次备份以来发生更改的文件和目录的备份(无论是完整备份、差异备份还是增量备份)。增量备份比完整备份和差异备份小,通常执行起来也快得多。差异备份是自上次完整备份以来所有已更改文件的备份。这些备份可能比增量备份大,但在执行恢复时非常有用,因为您只需要完整备份和最新的差异备份(如果您根本不运行增量备份)。在定时备份中,您可以每周进行一次完整备份,每夜进行增量或差异备份。

对于完全备份、增量备份和差异备份,还原操作按以下顺序进行:首先还原最后一次完全备份,然后还原最近的差异备份,最后还原任何后续的增量备份。如果您不使用差异备份,则它是最后一次完全备份,后面是每次后续的增量备份,从最旧到最新。

Caution

在备份操作中,您可能会还原不需要的、已删除的文件和目录以及有用的数据。在继续之前,您应该仔细检查已恢复的内容。

您的网络将有其特殊的备份要求,并且有大量的硬件设备甚至在线存储选项可供您选择。硬件设备可以与不同供应商的软件捆绑在一起,或者您可以购买硬件并运行开源软件来运行备份。以下是一些硬件存储选项:

  • 磁带:不同的类型取决于您的备份量
  • 硬盘:不同的速度和音量选项
  • 光学:DVD/CD-ROM 低容量数据备份

在线存储选项也可以用于存储大量数据,只要你有一个快速、可靠的互联网连接和较低的数据收费率。对于大型数据集来说,将数据存储在自动气象站 S3(通过 S3 存储类对数据进行生命周期循环,然后在一段时间后转移到冰川存储)可能是经济的。谷歌云存储也有竞争对手的数据存储服务,但没有冰川的超便宜的长期存储。

以下是一些 AWS 存储产品:

  • S3 标准:最快的对象存储
  • S3 标准-IA:中等,不经常访问
  • 冰川:长期,不经常进入

以下是一些谷歌存储产品:

  • 标准:最快的对象存储
  • DRA:中等,不经常访问
  • 近线:长期、不频繁的访问

默认情况下,带有选项的 AWS 和 Google 都提供静态数据的服务器端加密(SSE ),但是您也可以使用自己的加密密钥来保护您的数据。两者都符合 PCI DSS (3.x)和关于保护硬件和服务器、存储数据以及保护数据中心的各种 ISO 标准。将这种安全性与运行高度可靠和持久的服务相结合,使 AWS 或 Google 成为中长期存储的良好选择。

AWS 还提供 AWS 存储网关,这是一种虚拟设备(虚拟机),安装在数据中心内,用作 S3 和冰川的网关。它还能够充当虚拟磁带库(VTL),Bareos 等软件可以使用它进行备份。然后,AWS 存储网关将数据同步到 AWS 存储,同时维护本地缓存。它的价格很高,但这可能会被磁带机和存储磁带的成本抵消。

需要考虑的事情

我们已经讨论了灾难恢复和业务连续性以及保护您的数据的重要性,但是我们还想谈一件事。我们真正需要考虑的是从数据丢失或硬件故障中恢复的速度。你有主机,你有数据。在硬件丢失的情况下,您能否快速恢复您的主机和数据?

如果您有一个数据库,您的数据库可能是您拥有的最重要的数据和服务器。数据库服务器通常是精心构建的,创建了磁盘阵列,安装并调优了操作系统。在这种情况下,如果您有最新数据,重建该主机可能需要很长时间。

当然,有几种方法可以解决这个恢复时间问题。

  • 最好是有主/辅助数据库。您的辅助数据库可以升级为主数据库,为您重建主机提供时间。
  • 您可以利用虚拟化技术。您的主机可以从模板或映像快速重建,也可以在不中断的情况下从故障硬件上移除。结合配置管理,如 Puppet 或 Ansible,您将能够快速恢复您的系统。
  • 如果您有裸机主机,那么您需要备份磁盘架构以及操作系统和数据。像 Relax-and-Recover 这样的东西可能是个好主意,或者像 Mass 或 Cobbler 这样的东西可以帮助您从已知的构建中快速重建服务器。

我们可能遇到的最糟糕的情况是依赖一个运行列表,列出我们在 6 个月前可能如何构建主机,而从那以后从未尝试过构建这样的主机。实际上,可能还有一种更糟糕的情况:没有运行列表。

对于其他类型的主机,如 web 服务器,您可能不需要备份它们。您应该仔细考虑日志之类的东西,但是如果您可以比恢复主机更快地重建主机,那将是您的最佳选择。如果重建过程是通过配置管理自动化的,那么作为管理员,您就处于一个很好的位置。这是将服务器视为牲畜而非宠物的概念,使您的备份机制更加简单。

网络备份

我们的网络很简单:我们有一台主机headoffice.example.com,以及位于远程分支机构的主机,这些主机可能有我们需要备份的数据。

图 14-1 显示了我们的网络。(这是第七章中图表的变体。)

A185439_2_En_14_Fig1_HTML.jpg

图 14-1。

Our network

我们将向您展示两种备份网络的方法。我们将把数据从远程主机复制到headoffice.example.com中央服务器。复制完这些远程文件后,我们可以使用驻留在backup.example.com上的全功能备份应用程序对它们进行备份。

我们选择使用单独的主机来存放我们的备份服务器。理想情况下,您不希望您的主服务器也成为您的备份主机,因为如果您失去了主服务器,您也就失去了恢复备份的能力。但是,如果您买不起额外的主机,任何备份都比没有备份好,然后您应该备份到外部介质,如 DVD 或数据磁带。

Note

传统上,Linux 备份是手工编写的脚本中的targziprsyncddcpiodumprestoremt命令的组合。对于一台或两台主机来说,这种方法可能是值得的,但是对于一些跨平台的主机来说,这种方法就变得不实用了。

使用 rsync

首先,我们将向您展示如何使用名为rsync的工具安全高效地从远程主机复制数据。该解决方案专为不需要全面备份计划的不太复杂的网络而设计。为此,我们将使用rsync在主机之间安全地复制数据。

Note

rsync工具是 Samba 产品套件的一部分(与我们在第十三章中提到的 Samba 服务器项目直接相关)。桑巴社区已经将rsync收归旗下,它位于这里: http://rsync.samba.org/

rsync工具可以在客户机/服务器配置或远程 shell 中使用。当我们谈到“远程 Shell”时,我们的意思是我们可以通过另一种传输机制(如ssh)在主机之间rsync,这将为rsync程序提供目标主机上的远程 Shell(或命令行)。正是因为使用了ssh,我们才能够在诸如互联网这样的恶劣环境中从主机上安全、高效地复制数据。

rsync程序是一种快速有效的数据复制方式,因为它只复制与初始副本相比发生了变化的数据。在将远程数据的初始rsync拷贝到我们的主机后,任何后续拷贝都将与我们已经拥有的数据进行比较。rsync通过向接收主机发送它想要复制的数据的文件列表来实现这一点。文件列表包括我们要备份的数据的路径名,以及权限、所有权、大小、模式时间和文件的校验和(如果指定的话)。这使得rsync可以对正在复制的文件进行比较,然后只复制那些不同的文件,这使得rsync在主机之间发送数据时非常高效。它还将检查正在复制的文件,并仅复制文件中不同的数据部分。例如,如果一个 1MB 的文本文件包含 990KB 相同的数据,rsync将只复制不同的 10KB。不幸的是,这不能用二进制文件(如 JPEGs)来实现,因为对于二进制文件,很难区分对文件的任何更改。

如前所述,我们可以在客户机/服务器模型中使用rsync,在那里我们在目标主机上运行一个rsync守护进程,等待rsync连接。这些连接不在加密会话上运行,因此可能是一个安全问题,所以我们不打算进一步讨论这种使用rsync的方法。在这一章中,我们将描述使用带有安全 Shell(SSH)的rsync来提供安全传输。这也意味着我们只需要在防火墙上启用 TCP 端口 22,而不是 TCP 端口 873,这是rsync守护进程所需要的。您可以在下面的教程中了解更多关于设置rsync守护进程的信息:

通过 SSH 使用 rsync

需要在您的直接网络之外备份远程主机的情况并不少见,例如在 Internet 这样的恶劣环境中。在这些情况下,您可以使用rsync通过 SSH 远程访问这些主机,从它们那里获取数据或进行备份。通过实现一个简单的 Bash 脚本,您可以通过连接到远程主机并将文件复制回备份主机来自动化备份。

在本节中,我们将执行以下操作:

  • 创建用户来管理我们的备份
  • 创建我们的无密码 SSH 密钥
  • 将我们的密钥复制到我们的远程主机
  • 创建一个只允许通过 SSH 使用某些命令的脚本
  • 创建一个使用rsync从远程主机同步文件的脚本
  • 将该脚本添加到cron

我们在第 4 和第十章中向您介绍了 SSH 通信。通常,您可以使用合适的密码创建您的 SSH 密钥,当您登录到远程主机时,您可以使用该密码来验证您的会话。我们需要一个不带密码短语的 SSH 密钥来建立连接的原因是,我们将通过crontab运行这个脚本,这是我们在第六章中介绍的,因此我们可以定期备份我们的远程主机,而无需任何用户交互。我们的密钥上没有密码是一个安全问题,所以我们将采取措施来减少它们稍后被滥用的可能性。

首先,我们将创建一个新用户exbackup,来控制这些备份,如果您还没有创建它们的话。然后我们将生成我们的 SSH 密钥。

$ sudo /usr/sbin/useradd -m -d /data/backups -u 903 -g adm exbackup

这里我们创建了一个名为exbackup的用户,UID 为 903,默认组为adm。我们为这个用户选择了一个小于 1000 的 UID,因为我们正在创建一个服务用户(任何小于 1000 的用户 UID 都可以),我们希望服务用户的 UID 在 900 到 999 之间。我们已经将该用户添加到了adm或管理员组,因为该组传统上有权读取日志文件。exbackup主目录是/data/backups,那是我们存储所有备份的地方。接下来,我们将sudo进入用户帐户并创建我们的 SSH 密钥。

$ sudo su – exbackup
$ mkdir .ssh && chmod 0700 –R .ssh && cd .ssh
$ ssh-keygen -b 4096 -t rsa -f exbackup
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in exbackup.
Your public key has been saved in exbackup.pub.
The key fingerprint is:
c1:0c:1f:a5:e4:cf:b9:a5:30:c3:4b:45:23:4f:c9:96 exbackup@backup
$ ls –l total 16
-rw-------. 1 exbackup adm 3243 Oct 12 23:10 exbackup
-rw-r--r--. 1 exbackup adm  741 Oct 12 23:10 exbackup.pub

首先,我们发出sudo su – exbackup来改变到exbackup的 Shell。然后,我们创建.ssh目录来存储我们的密钥,如果成功了(如&&所示),我们更改该目录的权限并进入该目录。然后我们使用ssh-keygen命令来创建我们的密钥。我们选择使密钥长度为 4096 字节,-b 4096,类型为rsa,我们将密钥命名为exbackup,以便我们更容易识别它。在生成密钥时,我们被要求输入密码,我们只需按两次 Enter 键。你可以看到我们已经列出了我们的.ssh目录的内容,我们有两个密钥,一个私有的,一个公共的,如后缀.pub所示。我们在这个主机上保持私有密匙(exbackup)的安全,并为我们远程主机上的用户将公钥(exbackup.pub)的内容复制到authorized_keys文件中。

我们现在需要谈一谈安全性。让无密码的密钥在互联网上呼啸而过到达你的远程主机是一个潜在的安全风险。数据将被加密以防止随意窥探,但是一个有决心的攻击者可能会使用这些密钥作为访问点来攻击您的主机。我们还可以在我们的远程主机上创建一个名为 chroot jail 的安全机制,但是这将限制我们访问主机其余文件系统的能力。

Tip

有关如何建立 chroot 监狱的信息,请访问 https://www.debian.org/doc/manuals/securing-debian-howto/ap-chroot-ssh-env.en.html

我们可以将密码与我们的密钥一起使用,并使用一个名为key-chain的工具来缓存我们的连接密码,但我们必须在每次重启主机时输入密码,这并不理想。我们要做的是限制我们的 SSH 密钥可以使用的命令,让攻击者稍微困难一点。在我们的远程主机上,我们将创建一个名为ssh_limiter.sh的可执行文件。当我们用 SSH 密钥登录到我们的远程主机时,这个脚本将被调用,它只允许任何人用这个密钥执行一个命令。

您可以通过向authorized_keys文件添加一些选项来限制 SSH 可以做的事情。authorized_keys是远程主机上用户的.ssh目录中的一个文件,它保存了授权连接到我们主机的公钥的副本。我们在远程主机上的用户主目录中创建它们。我们可以使用这些选项来限制按键的功能,如表 14-1 所示。

表 14-1。

authorized_keys Options

| [计]选项 | 描述 | | --- | --- | | `From="hostname.example.com"` | 限制连接的来源。接受域、FQDN 和 IP 地址。 | | `Command="/command/to/execute"` | 指定每当此密钥用于身份验证时要执行的命令。 | | `Environment` | 如果`sshd`允许,设置用户环境。 | | `no-agent-forwarding` | 阻止 SSH 身份验证代理转发,这意味着我们无法使用这些密钥通过`ssh`代理连接到其他主机。 | | `no-port-forwarding` | 阻止 SSH 端口转发,这意味着我们无法使用`ssh`端口转发来打开与其他主机的连接。 | | `no-X11-forwarding` | 阻止 X11 转发。这意味着我们不能使用 X 协议;因此,我们无法打开新的 X 窗口或终端。 | | `no-` `pty` | 防止交互式 Shell。这意味着拥有这个键的用户只能运行命令,而不能使用交互式 shell。 |

表 14-1 是全部可用选项的子集。有关更多信息,请阅读sshdman页。

让我们使用这些信息来创建一个authorized_keys文件,我们将把它发送到我们的远程主机。首先,我们将把现有的exbackup.pub密钥复制到一个名为remote_authorized_keys的文件中。这将为exbackup用户创建一个包含公钥的文件。我们很快就会编辑这个文件副本,给它添加一些限制,并把它发送到远程主机上的authorized_keys文件中。

$ cp exbackup.pub remote_authorized_keys

如果我们想简单地将我们的公钥复制到任何远程主机,我们可以使用ssh-copy-id,它将在远程服务器上为用户创建authorized_keys文件,如下所示:

$ ssh-copy-id -i .ssh/exbackup jsmith@headoffice.example.com
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: ".ssh/exbackup.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
jsmith@headoffice.example.com's password:

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'jsmith@headoffice.example.com'"
and check to make sure that only the key(s) you wanted were added.

但是我们要做的不仅仅是复制authorized_keys文件。事实上,我们要做这些额外的事情:

  • 创建一个包装脚本来拒绝未经授权的命令
  • 在远程主机上,创建一个包含.sshbin目录的用户和主目录
  • 将带有授权命令的 SSH 包装脚本移动到bin目录
  • authorized_keys移动到.ssh目录
  • 确保每个文件和目录的权限正确

创建包装脚本

首先,我们将创建一个名为ssh_limiter.sh的文件,当我们使用exbackup SSH 键连接时,这个脚本将强制我们的连接运行。这个脚本将在我们的远程主机上运行。这为拥有这个 SSH 密钥的任何人都可以执行的命令范围提供了一些安全性。

当我们建立到主机的 SSH 连接时,变量$SSH_ORIGINAL_COMMAND保存我们想要在远程主机上执行的命令。因此,如果我们建立以下 SSH 连接:

$ ssh somehost@example.com ls –l /tmp

变量$SSH_ORIGINAL_COMMAND将保存值ls –l /tmp。当显示一个 SSH 键时,我们现在可以测试该变量,并决定它是否是我们将接受这个键使用的命令类型。当我们在远程主机上执行rsync时,变量将包含rsync --server <some other arguments for rsync>。我们希望允许这种情况,并排除任何其他情况。

我们用一个包装器脚本来做这件事,如清单 14-1 所示。

$ vi ssh_limiter.sh
#!/bin/bash
# Command to be used by exbackup at example.com to limit what exbackup can
# do on a remote host.
# SSH2 stores the original command sent by the remote host in a variable
# $SSH_ORIGINAL_COMMAND. We will use case to test and limit the commands
# we are running.

case "$SSH_ORIGINAL_COMMAND" in
  *\&*)
  echo "UNAUTHORIZED COMMAND"
  ;;
  *\;*)
  echo "UNAUTHORIZED COMMAND"
  ;;
  *\|*)
  echo "UNAUTHORIZED COMMAND"
  ;;
  rsync\ --server*)
  $SSH_ORIGINAL_COMMAND
  ;;
  *)
  echo "UNAUTHORIZED COMMAND"
  ;;
esac

Listing 14-1.Limiting the Commands ssh Can Do Using Keys

在清单 14-1 中,我们使用 Bash 脚本语言来测试由ssh用户呈现的命令。

case语句测试变量$SSH_ORIGINAL_COMMAND以确保它只包含命令rsync --server。首先,我们否认控制命令&,;、和|,它们可用于将其他命令添加到我们想要的命令的末尾。如果命令以rsync --server开始,那么我们接受它(\ --server确保 Bash 转义<space>--)。其他任何可能作为命令传递的内容都被*.拒绝。case语句以esac语句结束。

我们现在需要编辑我们的remote_authorized_keys文件来为我们的键添加选项。

$ vi remote_authorized_keys

command="∼/bin/ssh_limiter.sh",from="*.example.com", no-port-forwarding,no-X11-

forwarding,no-agent-forwarding,no-pty ssh-rsa
AAAAB3NzaC1yc2EAAAABIwAAAgEAp7jGL2il3QKREVTpNWkdPqiEbG4rKdCLt/nx57PHkZvz
SGI64Glscl0zIz92PBN/ZjNb4Z1ZaOGS7UYQOg4SHKXsw5/VHchIN1k3p9Vwm9rZUiDg3azKr9J+R
+r9TDhwReyYt0QhR/j1aZf1gYS3+xRLs+bQb6UXVRrccygCFtxvrA2B5Kkgw2QJhctSlNRyi8XobUK
7kOs2Bw4zIY8hEZMRBFEibqi/diXPngWsMeo2UQQGICo6yXmgUKqiuQq1azdDuTbEstLS97/LdT
qWd9MNAsYk= exbackup@backup.example.com

在这里,我们将选项添加到了最终将位于远程主机上的remote_authorized_key文件中。我们指定了在使用密钥和可以与之连接的主机时要运行的命令,并且我们限制了一般用户通常允许的功能。任何用这个键连接的人现在只能运行ssh_limiter.sh脚本,它只允许执行rsync命令,并且只允许来自*.example.com域的连接。如果我们想输入发起连接的主机的 IP 地址,我们可以在from=选项中更严格一些。

我们还指定不能向其他主机(no-port-forwarding)、x11 终端或 X 窗口(no-X11-forwarding)进行端口转发;我们不能使用ssh代理将我们的密钥转发给其他主机(no-agent-forwarding);而且我们也将无法得到一个交互式的 Shell(no-pty)。

部署到远程主机

我们将使用用户名和目录设置我们的远程主机,镜像我们之前为exbackup所做的。我们需要确保远程主机上的exbackup主目录有一个.ssh目录,并且在其上设置了 0700 权限。我们现在将使用普通用户将remote_authorized_keys文件复制到我们的远程服务器。首先,我们将把remote_authorized_keys文件复制到用户jsmith可以访问的地方;/tmp应该可以吧。我们也将把ssh_limiter.sh文件复制到/tmp

使用在远程主机上拥有管理sudo访问权限的账户jsmith,我们执行以下操作:

$ scp /tmp/remote_authorized_keys /tmp/ssh_limiter.sh
 jsmith@branch1.example.com:∼/

这将安全地将remote_authorized_keys文件复制到远程主机上jsmith的主目录中。现在我们发出以下一系列ssh命令:

$ ssh jsmith@branch1.example.com 'sudo useradd –u 903 –g adm –m
–d /data/backups exbackup && sudo –u exbackup mkdir –p /data/backups/.ssh &&
sudo chmod 0700 /data/backups/.ssh'

这将在我们的远程主机上设置我们的exbackup用户。它还在主目录中创建一个名为.ssh的目录,并在其上设置 0700 的权限,这是 SSH 安全性的要求。我们想指出的是,我们使用了sudo –u exbackup来创建目录,因此它拥有正确的所有权权限。双&符号(&&)表示如果第一组命令成功,我们希望执行下一组命令。

Caution

将任何用户添加到adm组都可以给他们提升的sudo权限。

我们在jsmith的主目录中有包装器脚本(ssh_limiter.sh)和remote_authorized_keys文件,并且我们已经创建了exbackup用户。接下来,我们将把remote_authorized_keys文件复制到远程主机上的适当位置,在途中重命名并设置所需的权限。我们还将创建/data/backups/bin目录并将ssh_limiter.sh移到那里。

$ ssh jsmith@branch1.example.com
'sudo mv remote_authorized_keys /data/backups/.ssh/authorized_keys \
&& sudo chown exbackup:adm /data/backups/.ssh/authorized_keys \
&& sudo chmod 0600 /data/backups/.ssh/authorized_keys \
&& sudo -u exbackup mkdir /data/backups/bin \
&& sudo mv ssh_limiter.sh /data/backups/bin \
&& sudo chown exbackup:adm /data/backups/bin/ssh_limiter.sh \
&& sudo chmod 0750 /data/backups/bin/ssh_limiter.sh'

在前面的代码中,我们使用反斜杠(\)来分隔行;反斜杠告诉 Bash,当我们按 Enter 键时,我们的命令在下一行继续,而不是执行代码行。就 Bash 而言,它可能只有一行,但这让我们更容易看到发生了什么。

除非与–u <username>选项一起使用,否则sudo命令将创建所有新文件和目录,而root是所有者。因此,我们需要更改正在创建的目录和文件的权限和所有权。我们还在/data/backups目录中创建了一个bin目录,这是exbackup的主目录。在authorized_key文件中,我们指定了command=∼/bin/ssh_limiter.sh,所以我们的ssh_limiter.sh脚本也需要复制到具有适当权限的bin目录中。

Tip

这个相当复杂的设置过程可以通过配置管理工具变得非常简单,就像在第十九章中解释的那样。

测试 rsync

我们将在我们的远程主机上创建一个文件来测试我们将要向您展示的rsync脚本。在我们的远程主机上,我们将在/tmp目录中创建一个名为/tmp/test_sync.txt的文本文件,并用垃圾文本填充它。

$ vi /tmp/test_sync.txt
fldjfsl
lfdsjfsla
fsdjfsl
fjsdl
fsjfs
fsl
fsa
23433

这里我们在remotehost.example.com上创建了一个文件,并将随机文本添加到一个名为/tmp/test_sync.txt的文件中。如果你在这个文件上使用cat命令(cat /tmp/test_sync.txt,你会看到它包含了所有的随机文本。

我们现在将使用rsync和我们的 SSH 密钥在远程主机上测试这个文件的备份。

$ sudo su - exbackup
[sudo] password for jsmith:
$ rsync -av -e 'ssh -i .ssh/exbackup' remotehost.example.com:/tmp/test_sync.txt /tmp
receiving file list ... done
test_sync.txt
sent 42 bytes received 194 bytes 472.00 bytes/sec
total size is 58 speedup is 0.25
$ cat /tmp/test_sync.txt
fldjfsl
lfdsjfsla
fsdjfsl
fjsdl
fsjfs
fsl
fsa
23433
LDJAS

我们使用rsync命令将文件test_sync.txt简单复制到我们的本地/tmp目录。通过使用cat显示文件内容,可以看到文件已经被复制。稍后我们将解释rsync命令的细节。

接下来,让我们在文件中添加更多的行,并再次同步它。

$ vi /tmp/test_sync.txt
fldjfsl
...
<snip>
...
fsa
23433
ldjas
dfald
asd
12344556

然后,我们将保存文件并再次执行rsync

$ rsync -av -e 'ssh -i .ssh/exbackup' branch1.example.com:/tmp/test_sync.txt /tmp
receiving file list ... done
test_sync.txt

sent 48 bytes received 213 bytes 174.00 bytes/sec
total size is 77 speedup is 0.30

当我们对/tmp/test_sync.txt文件使用cat命令时,在我们的本地主机上,您会注意到它包含了对文件的新更改。

$ cat /tmp/test_sync.txt
fldjfsl
...
<snip>
...
fsa
23433
ldjas
dfald
asd
12344556

因此,我们可以从远程主机安全地同步文件,而无需使用密码。让我们快速测试一下我们的ssh_limiter.sh脚本,检查它是否如预期的那样工作。在这里,我们将测试是否可以使用我们的密钥ssh访问远程主机并运行top命令。

$ ssh -i .ssh/exbackup remotehost.example.com top
UNAUTHORIZED COMMAND

完美——发送禁止的或意外的命令会引发UNAUTHORIZED COMMAND响应。

在 crontab 中设置 rsync 脚本

现在我们可以设置crontab脚本来定期将我们的远程主机文件同步到我们的备份目录。

表 14-2 列出了一些可与rsync一起使用的选项。

表 14-2。

rsync Options

| [计]选项 | 描述 | | --- | --- | | `-a` | 存档,使用这些选项递归复制的通用选项:`-rlptgoD`。 | | `-r` | 递归复制目录。 | | `-l` | 将符号链接复制为符号链接。 | | `-p` | 复制权限。 | | `-t` | 复制时间戳。 | | `-g` | 复制组权限。 | | `-o` | 复制所有权权限。 | | `-D` | 保留设备(字符和块设备)和特殊文件(fifo 和命名套接字)。 | | `--exclude` | 排除目录或文件;可以是模式。一个例子是`.svn/`排除`.svn`目录。 | | `--include` | 包括目录或文件;微调您想要复制的文件。语法与`--exclude`相同。 | | `-n, --dry-run` | 预演。显示会发生什么,但不实际执行同步。 |

一般来说,您将主要使用设置了归档选项的rsync命令,即–a。这是一个捆绑选项,代表以下选项:-rlptgoD。这些选项是–r,递归;-l,复制符号链接为符号链接;-p,复制权限;-t,复制文件和目录修改次数;-g,保存团体;-o,保全所有权;和-D,保存设备和特殊文件。这些选项通常足以归档您的系统,但是如果您需要,您可以添加更多选项,这些选项在rsync man页中有说明。

您可能会使用--exclude--include选项,它们允许您微调想要同步的文件或目录。您也可以使用--exclude-from=<file>--include-from=<file>来列出您想要定位的文件或目录的多个选择。

rsync 备份脚本

让我们来看一个典型的脚本,我们可以用它来同步我们的远程主机和本地主机。在我们的远程主机上,我们将有一个名为/data/staff/sales的目录,我们希望将该目录同步到我们的本地主机,然后我们将使用备份应用程序备份该目录。我们将有两个远程主机,branch1.example.combranch2.example.com,我们将使用rsync来同步它们的/data/staff/sales目录的内容,除了我们想要排除的/data/staff/sales/temp目录。我们还希望这个脚本由使用我们创建的无密码密钥的用户运行。

首先,让我们设置.ssh/config文件,它将处理我们需要的所有 SSH 配置。在该文件中,我们将添加主机名、IP 地址或 FQDN 名称以及要连接的用户。我们还将定义我们将在连接中使用的 SSH 密钥。

$ cat .ssh/config
Host *.example.com
  User exbackup
  Identityfile ∼/.ssh/exbackup

前面的代码将用户名exbackup和身份文件∼/.ssh/exbackup添加到与example.com域空间中的主机建立的每个ssh连接中。这相当于指定了$ ssh –I ∼/.ssh/exbackup exbackup@somehost.example.com

我们将用来在我们的主机之间运行rsync的脚本看起来如清单 14-2 所示。

 1\. #!/usr/bin/env bash
 2.
 3\. # This uses rsync to sync down remote files to the /data/backups/<hostname>
 4\. # directories.
 5\. # The rsync command we will use.
 6\. RSYNC='which rsync'
 7\. RSYNC_OPTS="-av "
 8.
 9\. # Host list - Bash array
10\. HOSTLIST='
11\. branch1.example.com
12\. branch2.example.com
13\. '
14\. # Back up directory on local host and source directory on remote host
15\. BACKUP_DIR='/data/backups/'
16\. SALES_DIR='/data/staff/sales'
17.
18\. # excluded directory
19\. EXCLUDED="temp/"
20.
21\. # error function
22\. error_check() {
23\.     if [ $1 -eq 0 ] ; then
24\.     echo "backup successful"
25\.     else
26\.     echo "backup failed: see error number: $1"
27\.     fi
28\. }
29.
30\. # The rsync functions
31\. get_sales() {
32\.   ${RSYNC} ${RSYNC_OPTS} --exclude $EXCLUDED $HOST:$SALES_DIR $BACKUP_DIR/$HOST
      2>&1 > /dev/null
33\. }
34.
35\. # Bash for loop to go through each host and rsync the data.
36\. for HOST in $HOSTLIST ; do
37\.   get_sales
38\.   error_check $?
39\. done
40.
41\. exit 0
Listing 14-2.The nightly_remote_sync.sh Script

清单 14-2 中的脚本的目的是将文件从一个或多个远程主机同步到运行脚本的主机的/data/backups目录。这可以是本地网络的备份主机backup.example.com,我们将在其中安装适当的备份软件。这只是实现这一结果的众多方法之一。

第 1 行包含将环境设置为 Bash 脚本的调用。我们还可以使用传统的 shebang ( #!/bin/bash)让 Linux 知道我们正在运行 Bash 脚本。

第 2–5 行是描述我们脚本的注释。第 6–7 行设置了RSYNC变量,第 10–13 行声明了我们想要同步的主机列表。第 15–19 行是更多的变量。我们将备份定向到的备份目录是BACKUP_DIR='/data/backups'sales目录SALES_DIR='/data/staff/sales'是我们正在备份的目标目录。temp/目录是我们想要排除的目录。

Note

用户exbackup必须对所有要备份的文件和目录拥有读取权限,并对要备份到的目录拥有写入权限。你可以看看使用组来实现这一点。

第 22–28 行是一个 Bash 函数,处理我们的错误检查。如果脚本的结尾不是零,那么它会失败;如果以零结尾,则脚本成功。error_check()子例程或函数接受$?参数,即另一个函数调用的退出代码,并测试它是否为零。我们知道,如果一切顺利,rsync将以零退出;否则,它会以另一个错误代码退出。我们可以使用这个错误检查函数来测试任何其他以零成功退出的函数的成功或失败。

第 31–33 行声明的get_sales函数描述了调用rsync命令并将/data/staff/sales目录同步到/data/backups/<hostname>目录的rsync函数。在第 32 行,2>&1 >/dev/nullstdoutstderr(或标准输出和标准误差)导向/dev/null。注意/dev/null是 Linux 黑洞;如果你向它发送东西,比如stdoutstderr,它们就会消失在虚无中。

Note

当你在 Linux/Unix 主机上运行一个程序时,会用到三个标准的特殊文件描述符:stdin(标准输入)、stdout(标准输出)和stderr(标准错误)。当您的程序接收输入时,它可以通过附加到stdin文件描述符来接收输入。同样,当它产生输出时,它可以把它写到stdout。如果程序中有错误,它可以将其写入stderr。有关处理和重定向stdinstdoutstderr的更多信息,请访问 www.tldp.org/LDP/abs/html/io-redirection.html

最后,第 36–39 行使用for循环函数循环通过主机列表中的每个主机,并在每个主机上执行rsync。然后,脚本检查每个错误代码,看它是否为零,如果是,它打印成功消息。

Note

www.tldp.org/LDP/abs/html/index.html 有一个学习更多 Bash 编程的好资源。另一个参考是 Sander Van Vugt 的书《Linux 命令行入门》(Apress,2015)。

当我们从命令行运行该脚本时,我们会得到以下结果:

exbackup@au-mel-ubuntu-1:∼$ ./bin/nightly_remote_rsync.sh
backup successful
backup successful

Note

当您第一次运行这个脚本时,您将看到 SSH,要求您确认远程主机的新密钥签名。键入 yes 确认签名,然后脚本将正常运行。

我们现在将这个脚本放到一个crontab文件中,这样我们就可以定期运行它。回想一下,我们在第五章中讨论过crontab文件。让我们创建文件/etc/cron.d/example_nightly_sync,并添加以下内容:

# run the nightly rsync script at 5 minutes past 12 every morning.
MAILTO=jsmith@example.com
5       0       *       *       *       exbackup
  /data/backups/bin/nightly_remote_rsync.sh

在这里,我们将脚本设置为以用户exbackup的身份在每晚午夜过后 5 分钟运行。如果有错误,将向jsmith@example.com发送一封电子邮件。这是一种将文件从一台主机同步到另一台主机的好方法,但它并不是一种很好的备份策略。例如,每天晚上我们同步所有更改过的文件,包括错误。如果没有及早发现,错误就会传播到我们的备份主机,我们就会丢失数据的良好拷贝。当我们使用适当的备份策略时,我们失去恢复良好数据能力的可能性被降至最低。

Tip

当从crontab运行命令时,考虑使用flock确实是个好主意。这使用文件锁定来防止在现有命令已经运行的情况下执行计划的命令。更多细节见man flock

围绕rsync已经开发了一些备份工具。

接下来,我们将了解一个名为 Bareos 的开源备份服务器应用程序,它可以备份 Linux、Microsoft Windows 和 macOS 主机。

使用 Duply 备份

Duply 是一个备份程序,使得使用 Duply 程序非常简单。Duplicity 是一个使用rsync的程序,rsync可以同步大量数据。在全球范围内,rsync已经被许多公司成功地用作他们的远程备份策略。rsync它本身在同步方面非常出色,但不容易管理离散备份。Duplicity 可以有效地管理离散备份,但它可以有许多选项可供选择。Duply 提供了一种配置重复备份的简单方法。

表里不一可以备份到各种云存储提供商。在这个例子中,我们将使用 AWS,但你也可以轻松地使用 Dropbox、RackSpace Cloudfiles、Google Cloud Storage 或更多,就像在表里不一的man页面中列出的那样。

在本练习中,我们将做以下事情:

  • 创建具有保留策略的 S3 存储桶
  • 创建允许访问我们的存储桶的策略
  • 双重配置
  • 测试我们的备份
  • 设置一个cron任务来触发夜间备份

我们将假设您已经拥有一个 AWS 帐户,并且您已经创建了一个用户来访问它。您将需要这个用户的凭证SECRET_ACCESS_KEY_IDSECRET_ACCESS_KEY,因为我们需要将它们添加到我们的配置中。如果您需要创建 AWS 用户的帮助,应该从以下文档开始:

设置 S3 时段

为了备份我们的数据,我们将使用亚马逊的 S3 对象存储。为此,我们需要创建一个 S3 桶。一个 S3 桶是用来储存 S3 物体的。S3 对象由唯一键、数据和元数据组成,存储在平面结构中。虽然键可以有前缀(比如nightly/backupfile.zip,其中nightly是前缀,nightly/backupfile.zip是键),但是它们不是分层嵌套的,而是都存在于一个桶中。

  • 我们将建立一个 S3 桶。
  • 我们将创建一个生命周期版本策略。
  • 设置访问策略,允许我们的备份用户将文件放入 S3,列出它们并获取它们。

当你为你的帐户登录到 AWS web 控制台后,你需要进入 S3 服务选项卡,如图 14-2 所示。

A185439_2_En_14_Fig2_HTML.jpg

图 14-2。

Going to S3

在那里,我们将创建一个名为our-backups的存储桶。你可以自由选择一个合适的名字。单击创建存储桶按钮继续。

如图 14-3 所示,我们创建了一个名为our-backups的 S3 桶。我们接下来要确保我们在这个存储桶上有一个生命周期版本化策略,这样我们就不必为任何旧数据付出太多。该策略将在 35 天后移动到成本较低的存储,并删除任何超过一年的对象。原因是,如果我们在一个月后没有使用这些数据,那么我们需要它的可能性就很小,因此我们会将其移动到成本较低的存储层。我们允许一个月后有 5 天(30+5)作为一个小缓冲,就像我们在 1 年期满时所做的那样。这确实意味着检索会更慢,但是仅仅几个星期后,我们真正需要它的机会就更低了。可能有些时候情况并非如此,我们应该边走边修改。

A185439_2_En_14_Fig3_HTML.jpg

图 14-3。

Creating an S3 bucket

我们可以将这些生命周期策略应用于我们的存储桶中的不同路径或整个存储桶。我们将在夜间将这些应用到前缀,因为这些是我们主机的夜间备份。创建铲斗后,在控制台的右窗格中,您将看到一个生命周期选项。点击它,您将看到如图 14-4 所示的屏幕。

A185439_2_En_14_Fig4_HTML.jpg

图 14-4。

Adding a life-cycle rule

单击“添加规则”开始添加我们的规则。

在图 14-5 中,我们正在创建一个生命周期策略,该策略只与关键字中带有“nightly”前缀的对象有关。现在,我们单击配置规则。

A185439_2_En_14_Fig5_HTML.jpg

图 14-5。

Setting life-cycle policy target

在图 14-6 中,你可以看到我们可以调整三个按钮。还记得我们之前讨论过的 S3 层吗?我们最初备份到标准层。下一个是标准的不经常访问(标准-IA ),再下一个是冰川存储。标准层的成本最高,因此我们希望尽快过渡,我们选择了 35 天。这给了我们标准的一个月加上几天假期或人们下班作为缓冲。我们不会费心去移动到冰川储存库,因为如果我们需要它,我们需要它的速度会比冰川取回它的速度要快。Standard-IA 还规定了 128Kb 的最小对象大小,这意味着小于 128 kb 的对象将不会被转换,并将在一年后被删除。此外,一旦我们将文件转移到 Standard-IA,它们需要在那里保留 30 天,然后我们才能将它们转移到另一个层,如 Glacier。

A185439_2_En_14_Fig6_HTML.jpg

图 14-6。

Configuring life-cycle rules

我们在一年后永久删除这些文件,并再次设置一个缓冲区,以防一年后我们发现需要检索它们。任何部分上传的文件将被清理,多部分上传将在 7 天后结束。多部分上传是指以较小的块上传文件。这将清理并结束所有这些作业。您可以在此了解更多有关该流程的信息:

完成后,我们可以单击“查看”按钮。

我们有机会在图 14-7 中给我们的政策命名,我们称之为nightly-lifecycle。一个存储桶可以有多个策略。您可以使用此页面查看设置;我们将单击“创建并激活规则”按钮。

A185439_2_En_14_Fig7_HTML.jpg

图 14-7。

Reviewing the life cycle

AWS 用户策略

我们希望保护我们创建的 S3 存储桶,只允许那些拥有正确凭证的 AWS 用户访问和上传。我们将向您之前创建的 AWS 用户添加一个内嵌策略。

现在,我们可以添加策略,允许我们的 AWS 用户正确访问 S3 存储桶。为此,我们需要使用 AWS 中的 IAM 服务。

Tip

一般来说,将策略附加到用户的首选方式是不使用内联策略,而是创建一个托管策略,并将其附加到用户甚至角色。这样,您就可以更轻松地集中和共享您的策略。

在图 14-8 中,我们可以看到我们将附加到 AWS 备份用户的策略。第 3–11 行允许我们列出所有的 s 3 存储桶。它由三部分组成:效果、动作和资源。效果可以是Allow或者Deny。动作是正在执行的 API 动作(在本例中是ListAllMyBuckets)。该资源是您要应用它的 AWS 资源(Amazon 资源名称);arn:aws:s3:::*是我们的 S3。

A185439_2_En_14_Fig8_HTML.jpg

图 14-8。

Attaching an inline policy to our user

策略声明的第二部分显示了我们允许的其他 API 操作。它们管理我们的对象,并允许我们放置(PutObject)、获取(GetObject)和列表(ListObject)。在第 22–25 行中,我们看到我们将这些操作仅限于备份桶 ARN,arn:aws:s3:::our-backup

有关这方面的更多信息,请阅读以下内容:

测试 S3 存储桶访问

我们将测试我们对备份 S3 存储桶的访问。为此,我们将下载并配置awscli程序,该程序允许我们从命令行与 AWS 资源进行交互。

我们要做的第一件事是下载awscli包。这可以使用您的本地包管理器或通过 PIP(Python 包管理器)下载来完成。

$ sudo aptitude install –y awscli

如果您使用 PIP,请尝试以下方法:

$ sudo pip install awscli

一旦安装完毕,我们就可以开始使用aws命令与 AWS 资源进行交互。作为将运行我们的备份的本地 Linux 用户,我们需要配置我们的 AWS 凭证来访问 AWS 资源。我们需要运行以下命令:

$ aws configure
AWS Access Key ID [None]: ALIAIJBASN4NOMJ4FLTR
AWS Secret Access Key [None]: +GDHpm+FLPj311tu6YJ29c9luMlQTHwntgy7vgs2
Default region name [None]: ap-southeast-2
Default output format [None]: json

这里我们使用了aws configure命令来添加我们的 AWS 访问密钥 ID 和我们的访问秘密。我们还指定了我们的默认区域,也就是离我们最近的区域,以及 JSON 的输出格式;这些可以在任何时候被覆盖,并不真正重要。

Note

aws configure命令为您的 AWS 凭证创建一个明文文件。这可能是一个安全风险,您可能喜欢使用其他加密方法来存储它们,比如aws-vaultcredstashawscli-keyring等等。

aws命令有几个处理不同资源的子命令。我们要求它列出一个 S3 资源。如果我们要处理 EC2 资源,我们将使用aws ec2 <command>。您可以通过发出以下命令获得 AWS 资源列表:

$ aws help

现在我们已经设置了 AWS 凭据,我们将测试是否可以列出我们的 S3 存储桶。

$ aws s3 ls our-backup/

如果没有错误,这表明我们可以列出我们的 S3 桶。我们可以尝试将本地文件复制或放入我们的 S3 存储桶中。为此,我们发布了以下内容:

$ aws s3 cp afile s3://our-backup/
upload: ./afile to s3://our-backup/afile
$ aws s3 ls our-backup/
2016-10-16 05:02:47      32768 afile

在这里,您可以看到我们已经上传了一个小文件到我们的 S3 桶。现在,让我们看看能否将该文件拷贝回我们的主机,然后从 S3 删除该文件。

$ aws s3 cp s3://our-backup/afile afile2
download: s3://our-backup/afile to ./afile2
$ aws s3 rm s3://our-backup/afile
delete: s3://our-backup/afile

这些是我们的备份程序将执行的所有操作,我们可以看到我们的凭据允许我们执行这些操作。现在让我们继续使用 Duply 创建我们的夜间备份。

双重安装和配置

正如我们已经说过的,Duply 是管理双重备份的便捷方式。Duplicity 可以运行多种方法来安全地将文件备份到远程系统。对于 AWS,它将以大约 25Mb 的文件块复制我们的备份。这样做是因为管理几个小文件比管理一个相当大的文件更容易。

有两种方法可以保护你的数据,一种是用 GPG 对称密钥,另一种是用 GPG 非对称密钥。我们将使用对称密钥,但是如果您有可用的 GPG 非对称密钥,请使用它们。我们还使用 S3 存储加密的静态数据,并且我们可以使用服务器端加密进一步加密我们的文件。

安装 Duply 将安装 Duplicity 作为一个要求。但是,它可能不会安装python-boto,您可能也必须安装它。但是,通过之前安装的awscli,我们应该已经安装了python-boto

$ sudo aptitude install –y duply

对于 CentOS,您当然需要使用 YUM。

现在 Duply 已经安装好了,我们可以研究如何使用这个命令了。表 14-3 显示了获取帮助信息的命令用法(--helphelp不起作用)。

表 14-3。

$ duply usage

| 创造 | 创建配置描述文件。 | | 支持 | 创建备份(如果不存在则为完整备份,如果存在则为增量备份)并执行前脚本和后脚本。 | | 前/后 | 执行`'/pre', '/post'`脚本。 | | 消隐图像 | 备份时不执行前脚本和后脚本。 | | 全部 | 强制进行完全备份。 | | 增量 | 强制进行增量备份。 | | 列表[ ] | 列出备份中的所有文件(与在``时一样)。默认为`now`。 | | 状态 | 打印存储库中当前的备份集和链。 | | 验证[][-比较-数据] | 列出自``以来更改的文件(如果给定)。 | | 验证路径 [ ] [ -比较数据] | 列出自``以来备份中文件或文件夹路径相对于本地路径的变化(如果给定)。 | | 恢复 [ ] | 将完整备份恢复到``(如同在``)。 | | 取 [ ] | 从备份中获取单个文件/文件夹(如在``时)。 | | 清除[ ] [ -力] | 列出过期的备份文件(早于`$MAX_AGE`)。使用`--force`实际删除这些文件。 | | purgeFull [ ] [ - force] | 列出要保留的完整备份和关联增量备份的数量,按时间倒序计数。使用`--force`实际删除这些文件。 | | purgeIncr [ ] [ - force] | 列出过期的增量备份(`$MAX_FULLS_WITH_INCRS`是将保留关联增量备份的完整备份数量,按时间倒序计数)。使用`--force`实际删除这些文件。 | | 清理[ -强制] | 列出损坏的备份链文件归档(例如,在未完成运行后)。使用`--force`实际删除这些文件。 | | `version` | 显示副本和所需程序的版本信息。 |

当我们与我们的备份交互时,我们需要像这样使用duply命令:

$ duply <profile> command

要通过 Duply 配置 Duply,我们需要创建一个备份配置文件。这将存储我们的配置细节,以供重复使用。

$ duply nightly create

# although called exclude, this file is actually a globbing file list

Congratulations. You just created the profile 'nightly'.
The initial config file has been created as
'/home/exbackup/.duply/nightly/conf'.
You should now adjust this config file to your needs.

IMPORTANT:
  Copy the _whole_ profile folder after the first backup to a safe place.
  It contains everything needed to restore your backups. You will need
  it if you have to restore the backup from another system (e.g. after a
  system crash). Keep access to these files restricted as they contain
  _all_ informations (gpg data, ftp data) to access and modify your backups.

  Repeat this step after _all_ configuration changes. Some configuration
  options are crucial for restoration.

该命令在发出命令的用户的主目录中创建一个目录(∼/.duply)。在那个目录中,创建一个概要文件(∼/.duply/nightly)目录,并且在那个目录中,创建一个∼/.duply/nightly/conf文件。创建了一个排除文件(∼/.duply/nightly/exclude),它可以包含我们想要在备份配置文件中包含或排除的目录。

在这个每夜配置文件目录中,我们还可以包括可以在备份之前或之后运行的前脚本和后脚本。这些应该包含在.duply/nightly/pre.duply/nightly/post文件中。

此外,create命令末尾的输出告诉我们在第一次备份后制作整个配置文件目录的安全副本,因为我们在恢复系统时可能需要它。

配置双重备份

正如我们之前所说的,create命令将在概要文件目录中创建一个 conf 文件。现在,我们将对其进行编辑,以管理我们的备份。

Note

如果要为 root 用户配置 Duply,可以在/etc/duply目录下配置 Duply。

在配置文件中有许多选项可供我们使用,但是我们只需要设置几个来创建我们的备份。我们需要设置以下内容:

GPG_PW='mygeniusbigbikeisnice'

首先,我们提供了GPG_PW值。这是我们将用于加密和解密备份的密码。您可以使用比这个简单示例更随机的密码。您应该将其复制到您的密码保险箱或管理器中,以供将来参考。或者,您也可以使用非对称加密,我们使用私有和公共 GPG 密钥来加密我们的数据。

接下来,我们可以设置我们的目标和目标凭证。目标可以是任何可接受的双重后端类型;列表见man duplicity页。通常,类型应符合以下要求:

<scheme://><uid>:<password>@<other.host>/<some_dir>

我们将使用 S3 方案,在 URL 中不需要用户和密码信息。

TARGET='s3://s3-ap-southeast-2.amazonaws.com/our-backup/nightly'

这类似于我们用来通过aws cli命令复制测试文件的 URL。接下来,我们需要设置我们的 AWS 访问凭证。

TARGET_USER='ALIAIJBASN4NOMJ4FLTR'
TARGET_PASS='+GDHpm+FLPj311tu6YJ29c9luMlQTHwntgy7vgs2'

我们已经在这里设置了 AWS 凭证,但是我们可能不喜欢这样做,因为这意味着我们将 AWS 凭证分散在主目录中的许多文件中。或者,我们可以像下面这样做:

TARGET_USER=`awk '/aws_access_key_id/ {print $3}' ∼.aws/credentials`
TARGET_PASS=`awk '/aws_secret_access_key/ {print $3}' ∼.aws/credentials`

这里我们使用awk命令搜索∼/.aws/credentials文件中的访问和秘密 AWS 密钥,打印第三列(print $3)。另一种方法是使用类似这样的环境变量,这些变量是从一个安全的钥匙串中填充的:

TARGET_USER=`echo $AWS_ACCESS_KEY_ID`
TARGET_PASS=`echo $AWS_SECRET_ACCESS_KEY`

为 AWS 存储您的秘密的备选方案列表如下所示:

接下来,我们列出所有备份的基本根目录或源。

SOURCE='/'

如果你正在做主目录,你可能喜欢使用你的主目录作为来源。

SOURCE='/home/jsmith'

最后,我们将把MAX_AGE设置为 1 个月。当我们对备份文件运行清除时,实际文件不会被删除,直到我们用命令运行一个--force选项。我们仍然可以将我们的文件在 S3 保存 12 个月,但它们不会出现在duply的目录中。有几个月的备份会降低我们的duply命令的速度。

MAX_AGE=1M

这就是我们需要为 conf 文件配置的全部内容。接下来,我们将编辑∼/.duply/nightly/exclude文件,以便不备份某些文件。exclude文件采用以下语法:

- /path/to/exclude
+ /path/dir/of/directory/to/include
+ /path/*/**.py

我们可以使用各种条件来选择想要包含(+)或排除(-)的路径。除非明确排除,否则会包括文件列表。排序很重要,因为包含的文件优先于排除的文件。

在我们的示例中,我们将进行完整的系统备份。以下是我们要排除的文件:

- /data/
- /dev/
- /lost+found/
- /media/
- /mnt/
- /proc/
- /run/
- /srv/
- /sys/
- /tmp/
- /var/tmp

默认情况下,所有文件都包含在由我们的双重conf文件中的SOURCE='/'配置决定的/目录下。这个例子表明我们将排除所有正常的系统目录,如/dev/proc/sys。这些是短暂的,不需要备份。还有一些不需要备份,包括/data,它将保存远程网络文件系统,以及/run/tmp目录,它们是易失的。

您可以在duplicity man页面中了解更多关于如何包含和排除目录和文件的信息。我们现在准备备份或系统。

运行双重备份

现在是时候备份我们的系统了。第一次备份总是最长的,任何完整备份也是如此。然而,为了确保我们的exbackup用户可以在运行duply时访问系统的所有部分,我们必须首先在我们的sudoers文件中进行这个更改。

$ sudo visudo
# Cmnd alias specification
Cmnd_Alias BACKUPS = /usr/bin/duply

%backup ALL=(ALL) NOPASSWD:  BACKUPS

然后,我们需要将exbackup添加到backup组中。在 CentOS 上,您可能需要在此步骤之前创建组;在 Ubuntu 上,备份组应该已经存在。

$ sudo usermod -aG backup exbackup

为了确保我们备份了所有的系统文件,我们将使用sudo命令来提升我们的权限。

$ sudo duply nightly backup
Start duply v1.11, time is 2016-10-15 23:57:21.
Using profile '/home/exbackup/.duply/nightly'.
Using installed duplicity version 0.7.06, python 2.7.12, gpg: unsafe (Home: ∼/.gnupg), awk 'GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.4, GNU MP 6.1.0)', grep 'grep (GNU grep) 2.25', bash '4.3.46(1)-release (x86_64-pc-linux-gnu)'.
Signing disabled. Not GPG_KEY entries in config.
Checking TEMP_DIR '/tmp' is a folder and writable (OK)
Test - Encryption with passphrase (OK)
Test - Decryption with passphrase (OK)
Test - Compare (OK)
Cleanup - Delete '/tmp/duply.12989.1476575841_*'(OK)

--- Start running command PRE at 23:57:21.238 ---
Skipping n/a script '/home/exbackup/.duply/nightly/pre'.
--- Finished state OK at 23:57:21.252 - Runtime 00:00:00.013 ---

--- Start running command BKP at 23:57:21.262 ---
Reading globbing filelist /home/exbackup/.duply/nightly/exclude
Local and Remote metadata are synchronized, no sync needed.
Last full backup date: none
No signatures found, switching to full backup.
Error accessing possibly locked file /var/lib/lxcfs
--------------[ Backup Statistics ]--------------
StartTime 1476588974.26 (Sun Oct 16 03:36:14 2016)
EndTime 1476591675.63 (Sun Oct 16 04:21:15 2016)
ElapsedTime 2701.37 (45 minutes 1.37 seconds)
SourceFiles 140466
SourceFileSize 2375250457 (2.21 GB)
NewFiles 140466
NewFileSize 2375250457 (2.21 GB)
DeletedFiles 0
ChangedFiles 0
ChangedFileSize 0 (0 bytes)
ChangedDeltaSize 0 (0 bytes)
DeltaEntries 140466
RawDeltaSize 2281512996 (2.12 GB)
TotalDestinationSizeChange 198548201 (189 MB)
Errors 1
-------------------------------------------------

--- Finished state OK at 04:39:42.342 - Runtime 01:03:29.436 ---

--- Start running command POST at 04:39:42.354 ---
Skipping n/a script '/home/exbackup/.duply/nightly/post'.
--- Finished state OK at 04:39:42.375 - Runtime 00:00:00.020 ---

备份完成后,我们可以看到我们在备份中捕获了 2.21GB 的数据。备份从几次检查开始,然后我们读取排除文件,然后开始备份。有一个文件没有成功备份(/var/lib/lxcfs,它是/proc文件系统cgroups的 docker 视图,也可以被排除)。尽管如此,我们还是成功备份了。检查我们的 S3 存储区,我们可以看到现在有几个文件与我们的备份相关。

$ aws s3 ls our-backup/nightly/
2016-10-16 03:35:34          0
2016-10-16 04:25:55   36877167 duplicity-full-signatures.20161015T235721Z.sigtar.gpg
2016-10-16 04:39:28     634738 duplicity-full.20161015T235721Z.manifest.gpg
2016-10-16 03:35:37   26244347 duplicity-full.20161015T235721Z.vol1.difftar.gpg
2016-10-16 03:35:37   26194091 duplicity-full.20161015T235721Z.vol10.difftar.gpg
2016-10-16 03:35:37   26196749 duplicity-full.20161015T235721Z.vol11.difftar.gpg
2016-10-16 03:35:38   26227650 duplicity-full.20161015T235721Z.vol12.difftar.gpg
...

在 S3 列表中,我们可以看到构成备份的文件。文件本身的备份在*.difftar.gpg文件中。也有*.manifest.gpg*.sigtar.gpg档。manifest 文件是文件的列表,sigtar 是文件的签名,用于查看它们在备份之间是否发生了变化。

我们现在还可以运行一个list命令来获取一些我们已经备份的文件。

$ sudo duply nightly list
Start duply v1.11, time is 2016-10-16 10:41:48.
Using profile '/home/exbackup/.duply/nightly'.
Using installed duplicity version 0.7.06, python 2.7.12, gpg: unsafe (Home: ∼/.gnupg), awk 'GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.4, GNU MP 6.1.0)', grep 'grep (GNU grep) 2.25', bash '4.3.46(1)-release (x86_64-pc-linux-gnu)'.
Signing disabled. Not GPG_KEY entries in config.
Checking TEMP_DIR '/tmp' is a folder and writable (OK)
Test - Encryption with passphrase (OK)
Test - Decryption with passphrase (OK)
Test - Compare (OK)
Cleanup - Delete '/tmp/duply.18655.1476614508_*'(OK)

--- Start running command LIST at 10:41:49.064 ---
Local and Remote metadata are synchronized, no sync needed.
Last full backup date: Sun Oct 16 10:01:58 2016
Thu Oct 13 07:21:56 2016 .
Thu Sep 29 23:57:15 2016 bin
Fri Jun 24 15:44:14 2016 bin/bash
Tue Jan 19 23:11:42 2016 bin/btrfs
Tue Jan 19 23:11:41 2016 bin/btrfs-calc-size
...<snip>...
Thu May 26 23:31:30 2016 sbin/wipefs
Tue Feb 16 13:19:00 2016 sbin/xfs_repair
Fri Feb 19 15:21:11 2016 sbin/xtables-multi
Thu May 26 23:31:30 2016 sbin/zramctl
Thu Sep  1 17:37:32 2016 snap
Tue Oct 11 14:07:06 2016 vmlinuz
Thu Sep 29 12:36:50 2016 vmlinuz.old
--- Finished state OK at 10:41:58.667 - Runtime 00:00:09.603 ---

这是我们已经备份的所有文件的一个长列表。我们可以使用一个时间参数来减少输出,使用grep来进一步缩小我们要寻找的范围(duply nightly list 2016-10-15 | grep /usr/sbin)。

从列表中,我们现在将从我们的备份中获取一个文件(/bin/bash),并将其复制到本地/tmp目录。

$ sudo duply nightly fetch bin/bash /tmp/restores/bash
Start duply v1.11, time is 2016-10-16 11:41:15.
Using profile '/home/exbackup/.duply/nightly'.
Using installed duplicity version 0.7.06, python 2.7.12, gpg: unsafe (Home: ∼/.gnupg), awk 'GNU Awk 4.1.3, API: 1.1 (GNU MPFR 3.1.4, GNU MP 6.1.0)', grep 'grep (GNU grep) 2.25', bash '4.3.46(1)-release (x86_64-pc-linux-gnu)'.
Signing disabled. Not GPG_KEY entries in config.
Checking TEMP_DIR '/tmp' is a folder and writable (OK)
Test - Encryption with passphrase (OK)
Test - Decryption with passphrase (OK)
Test - Compare (OK)
Cleanup - Delete '/tmp/duply.20766.1476618075_*'(OK)

--- Start running command FETCH at 11:41:15.242 ---
Synchronizing remote metadata to local cache...
Copying duplicity-full-signatures.20161015T235721Z.sigtar.gpg to local cache.
Copying duplicity-full-signatures.20161016T100158Z.sigtar.gpg to local cache.
Copying duplicity-full.20161015T235721Z.manifest.gpg to local cache.
Copying duplicity-full.20161016T100158Z.manifest.gpg to local cache.
Last full backup date: Sun Oct 16 10:01:58 2016
--- Finished state OK at 12:04:32.407 - Runtime 00:23:17.164 ---

duply fetch进程将在本地下载签名文件和清单文件,以便对它们进行处理。这可能需要一些时间。一旦完成,Duply 将恢复文件。在这里,您可以看到我们已经成功恢复了bin/bash文件。要恢复整个备份,我们使用duply restore <path_to_restore_to>命令。

既然我们已经成功地备份和恢复了主机上的文件,我们将添加一个cron作业,在每晚为我们完成这项工作。我们将使用以下命令将其添加到exbackup crontab 中:

$ crontab –e
0 2 * * * env HOME=/home/exbackup sudo duply nightly backup

我们将在每天凌晨 2 点运行这个双重备份,并保证正确设置HOME环境变量,因为许多部分(gpgduply)依赖于正确的主目录。

使用 Bareos

商业备份解决方案的成本可能非常高。Bareos 为 Linux、Unix 和 Windows 桌面和服务器提供了强大、可靠、可定制且高效的开源备份服务。它适用于大多数存储设备,DAT,LTO 和自动加载器,也可以备份到磁盘,包括 GlusterFS。Bareos 易于配置、保护和升级,并且是一个完整、免费和强大的备份服务器。

Bareos 是流行的 Bacula 备份服务器软件的一个分支。Bacula 仍然有一个开源社区版本,但它专门为其软件的商业版本开发。Bareos 和 Bacula 在功能上有了很大的不同,但在配置和安装上仍然相似。在大多数情况下,在本节中,您可以将 Bareos 替换为 Bacula,并且您可以配置任何一种。

Bareos 在客户端/服务器模式下工作,需要在要备份的目标主机上安装 Bareos 客户端。Bareos 的组成部分如下:

  • 导演守护进程
  • 存储守护程序
  • 文件守护程序
  • 备份到何处的目录
  • 管理 Bareos 的控制台

Bareos 备份服务器本身至少需要运行这两个守护程序:控制器守护程序和存储守护程序。

控制器守护程序控制备份内容、备份时间以及备份位置。它还为可能需要进行的任何恢复作业提供类似的服务。它有一个配置文件,其中包含控制控制器守护程序本身运行的详细信息,以及在目标主机上运行的存储守护程序和文件守护程序。

存储守护程序与目标主机上运行的控制器守护程序和文件守护程序通信,并控制对存储数据的设备(磁盘或磁带)的访问。存储守护程序控制对备份介质、磁带驱动器和自动加载器的访问。它甚至可以被配置为写入 GlusterFS,我们将很快演示这一点。

文件守护程序位于目标主机上,等待来自 Bareos Director 守护程序的连接。当目标主机收到启动备份的指令时,它会获得要备份的文件列表。然后,它直接连接到存储守护程序,并将备份数据发送到存储守护程序,以写入备份介质。文件守护程序将哪些文件已备份到控制器守护程序,并将该信息写入 Bareos 目录。

目录记录了已备份的文件、备份位置、备份时间以及备份到的介质(或卷)上。该目录保存在 SQL 数据库中,以供将来参考。备份完成后,Bareos 程序可以通过比较写入目录的内容和写入磁带的内容来验证备份是否成功。

当请求恢复操作时,Bareos 将读取目录的内容,并请求加载适当的介质。然后,它将联系目标客户端和存储守护程序,它们将开始恢复过程。

在 Bareos 中,备份和恢复操作被称为作业。您可以在 Bareos Director 配置文件/etc/bareos/bareos-dir.conf中安排备份和恢复作业。每个作业都由一系列定义组成,这些定义可以继承一组通用定义,使您的配置更易于管理。每个作业作用于作为目标主机的客户机。作业在卷上备份或从卷中还原,卷是指您正在使用的存储介质(例如,磁带、DVD 或磁盘)。可以将这些卷分组到池中,并给出关于保留期、使用和循环的通用定义。当然,调度程序可以管理作业、客户机、卷和池的协调,以运行一次性操作或重复操作。

您可以通过bconsole控制 Bareos 操作,这是一个终端控制台程序,可用于运行作业和查看控制器、存储或文件守护程序的状态。您还可以使用它来管理卷、池和恢复操作。它很容易设置,你可以把它放在网络上的任何地方。

Note

我们将在“介绍 Bareos Web-UI”一节中介绍如何设置 Bareos 的控制台。

Bareos 服务器要求在服务器上打开以下 TCP 端口:

  • Bareos Director 守护程序需要端口 9101(仅在 Bareos 服务器上)。
  • Bareos 存储守护程序需要端口 9102(仅在 Bareos 服务器上)。
  • Bareos 文件守护程序需要端口 9103(在任何目标主机或客户端上)。

您可以在备份服务器上使用以下命令添加它们:

$ sudo firewall-cmd --zone public --permanent --add-service bacula
$ sudo firewall-cmd --reload

在客户端上,你只需要打开 TCP 端口 9103,在 CentOS 主机上你会发现firewalld中提供了一个bacula-client服务。

$ sudo firewall-cmd --zone public --permanent --add-service bacula-client
$ sudo firewall-cmd –reload

如果你在 Ubuntu 上运行这个,你可以使用ufw来添加应用文件(/etc/ufw/applications.d/bareos),如下所示:

[Bareos]
title=Bareos Backup Server
description=The Bareos Backup Server.
ports=9101,9102,9103/tcp

您可以对客户端进行类似的操作,只允许端口 9103 访问。然后,我们将执行以下命令:

$ sudo ufw reload && sudo ufw allow bareos

获取软件

Bareos 下载页面( https://www.bareos.org/en/download.html )提供了最新版本的 RPM 和 DEB 包的链接供您下载。发行版可能比最新版本落后几个版本,目前只有 Ubuntu 与 Bareos 一起发布。CentOS 仍有 Bacula 可用,但没有 Bareos。你可以在这里得到包裹和 tarball:http://download.bareos.org/bareos/

对于 CentOS,我们将在文件/etc/yum.repos.d/bareos.repo中安装以下 YUM repo 内容,如下所示:

[bareos_bareos-15.2]
name=Backup Archiving Recovery Open Sourced 15.2 (CentOS_7)
type=rpm-md
baseurl=http://download.bareos.org/bareos/release/15.2/CentOS_7/
gpgcheck=1
gpgkey=http://download.bareos.org/bareos/release/15.2/CentOS_7/repodata/repomd.xml.key
enabled=1

然后,我们可以安装以下软件包:

$ sudo yum install bareos-client bareos-director bareos-storage bareos-storage-glusterfs bareos-bconsole

在 Ubuntu 上,您必须使用以下内容:

$ sudo aptitude install bareos-filedaemon bareos-bconsole bareos-director bareos-storage

在 Ubuntu 上,你会被要求通过dbconfig-common助手为你的 MariaDB 服务提供一个用户名和密码。在这个阶段,我们还没有准备好提供这些信息,所以我们将选择<No>。如果您有自己的详细信息,或者正在本地主机上进行设置,您可以选择 yes 并完成设置数据库的步骤。我们将很快浏览数据库配置。

安装过程安装所有必需的包和所有依赖项,并在配置目录/etc/bareos中创建默认配置文件。此时也创建了bareos用户。要启动并运行 Bareos,您必须编辑配置目录中的以下文件,以访问 Bareos 管理控制台:/etc/bareos/bareos-dir.conf/etc/bareos/bareos-sd.conf/etc/bareos/bareos-fd.conf/etc/bareos/bconsole.conf

配置数据库

我们在第十一章中讨论了 MariaDB 服务器。我们现在将在该主机上创建一个数据库来存放我们的目录。在此过程中,我们将配置要备份的 MariaDB 主机。首先,我们需要在 MariaDB 主机上创建正确的帐户和数据库。

我们将假设headoffice.example.com主机安装了 MariaDB 数据库服务器,并且我们从backup.example.com开始运行备份。当然,如果我们没有额外的主机作为单独的备份主机,我们可以将这个数据库放在一台主机headoffice.example.com上。在这种情况下,您应该在下面我们使用了backup.example.com的例子中使用 localhost。其他替代方法是在备份主机上存储单独的 MariaDB 数据库服务器(或者 PostgreSQL,如果您愿意的话),或者在备份主机上使用 SQLite 数据库。

headoffice.example.com MariaDB 主机上,我们将为 Bareos 创建一个新的数据库,并授予用户bareos从我们的backup.example.com主机进行访问的权限。然后,我们将通过更改防火墙来确保我们的backup.example.com主机可以连接到headoffice.example.com上的端口 3306。在这样做的同时,我们还将确保我们的 Bareos 文件守护程序安装在headoffice.example.com上,并且我们从backup.example.com开始对该主机上的 TCP 端口 9103 进行防火墙访问。

我们需要在将存放 Bareos 数据库的主机上安装以下软件包;它们包含帮助管理数据库的助手脚本:

$ sudo aptitude install –y bareo-database-common bareos-database-mysql

当然,在 CentOS 上,您可以使用yum命令。

headoffice.example.com日,我们将执行以下操作:

$ sudo mysql –u root –p
Enter Password:
...
mysql> CREATE DATABASE 'bareos';
Query OK, 1 row affected (0.00 sec)
mysql> GRANT ALL PRIVILEGES ON bareos.* to bareos@backup.example.com
IDENTIFIED BY 'somepassword';
Query OK, 0 rows affected (0.00 sec)
mysql> FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)
mysql> \q;

在前面的代码中,我们创建了一个名为bareos的数据库。在那个数据库上,如果用户bareos使用somepassword密码,我们已经授予他从backup.example.com登录的所有权限。如果你是为本地主机设置的,你应该把bareos@backup.example.com改成bareos@localhost。最后,我们使用FLUSH PRIVILEGES,它用所有最近的更改重新加载 grants 表。

在 CentOS 和 Ubuntu 主机上,有创建、删除、更新和备份 Bareos 数据库的脚本。你会在/usr/lib/bareos/scripts/中找到它们。

为了为 Bareos 数据库创建数据库表,我们将在backup.example.com主机上运行以下命令:

$ sudo /usr/lib/bareos/scripts/create_bareos_database
Creating mysql database
Creating of bareos database succeeded.
$ sudo /usr/lib/bareos/scripts/grant_bareos_privileges
Granting mysql tables
Privileges for user bareos granted ON database bareos.

运行 Bareos 提供的这些脚本将为我们的目录创建必要的表,并为 Bareos 用户创建适当的授权。

我们确认数据库是通过从backup.example.com运行以下命令创建的:

$ sudo –u bareos mysql –h headoffice.example.com –u bareos –p –D bareos
–e 'show tables;'

该命令使用 Bareos 用户名和密码连接到运行在headoffice.example.com主机上的 MariaDB 数据库。-p提示输入密码,-psomepassword是您在命令行上提交密码的方式(在–p和密码之间没有空格)。获得输出将做两件事:确认我们的用户名和密码工作正常,并且脚本确实创建了一些表。

Note

您可以在 http://doc.bareos.org/master/html/bareos-manual-main-reference.html#x1-41100032.1.2 找到关于结合 MySQL 和 MariaDB 使用 Bareos 的更多信息。

需要注意的其他事情是,/data/backups/bareos/FileStorage目录必须存在,并且它必须对 Bareos 用户是可写的,就像/var/log/bareos.log文件一样。

配置 Bareos

我们现在将检查需要为您的 Bareos 服务器定义的配置文件。如前所述,有三个文件用于处理 Bareos 服务器本身,一个用于管理控制台。

在这些练习中,我们要做的是:

  • bareos-dir.conf文件中:
    • 使用正确的名称配置控制器
    • 配置默认的 Linux 备份作业定义
    • headoffice服务器配置备份作业
    • 配置要备份的文件集
    • 配置备份的时间表
    • 配置客户端进行备份
    • 为控制器添加存储守护程序配置
    • 配置池
    • 配置控制台
    • 配置消息
  • bareos-sd.conf文件中:
    • 配置存储守护程序
  • bareos-fd.conf文件中:
    • 配置客户端守护程序

在我们在 Bareos 中配置任何东西之前,我们应该解释一下这些文件之间的关系。对于相互通信的 Bareos 守护程序,它们需要交换一组密码。每个存储守护程序(您可以有多个)和每个文件守护程序(您通常会有多个)都必须包含 Bareos 控制器的名称和用于验证自身的密码。对于希望与 Bareos 控制器通信的每个守护程序,密码设置在两个位置。

图 14-9 显示了配置文件及其公共定义之间的关系。

A185439_2_En_14_Fig9_HTML.jpg

图 14-9。

Bareos common configuration file definitions

如图 14-9 所示,控制器的 Bareos /etc/bareos/bareos-dir.conf文件将包含存储守护程序、客户端守护程序和bconsole程序的信息。您还可以看到配置对象的定义,比如StoragePoolsFileSets等等,需要被配置的对象的名称,并且定义要用花括号括起来:{<definition = value>}。每行必须有一个定义。

在图 14-9 中还可以看到,在存储文件中,NameMedia Type条目必须与 Director 文件中Device对象定义中的Device TypeMedia Type条目相匹配。存储文件中的 Director 对象定义中的Director NamePassword必须与 Director 文件的Storage部分中的存储密码相匹配。

当我们在 Director 文件中定义客户端时,分配给该客户端的密码必须存在于该 Director 名称的文件守护程序配置中。同样在图 13-2 中,您可以看到bconsole程序配置文件在建立连接之前需要控制器的名称和密码。

让我们从主文件开始,导演,在/etc/bareos/bareos-dir.conf

巴勒斯-迪尔. conf

控制器配置文件是 Bareos 使用的主要文件。在这里,您定义了 Bareos 服务的每个方面:作业、文件集、客户机、时间表、池等等。借用 Bareos 文档的说法,文件集是要备份的内容,客户端是要备份的对象,时间表定义了备份时间,池定义了备份位置(即备份什么卷)。这个配置文件有很多部分,很容易弄不清它与其他文件的关系,但是我们会尽力指导您完成它。

Director {}部分定义了目录本身。

Director {                            # define myself
  Name = backup-dir
  QueryFile = "/usr/lib/bareos/scripts/query.sql"
  Maximum Concurrent Jobs = 10
  Password = "uogx2tNL9dRUwGfNvY/b+uQrU8osZn+JOM7t8iIrpszN"     # Console password
  Messages = Daemon
  Auditing = yes

  # Enable the Heartbeat if you experience connection losses
  # (eg. because of your router or firewall configuration).
  # Additionally the Heartbeat can be enabled in bareos-sd and bareos-fd.
  #
  # Heartbeat Interval = 1m

  # remove comment in next line to load dynamic backends from specified directory
  # Backend Directory = /usr/lib64/bareos/backends

  # remove comment from "Plugin Directory" to load plugins from specified directory.
  # if "Plugin Names" is defined, only the specified plugins will be loaded,
  # otherwise all director plugins (*-dir.so) from the "Plugin Directory".
  #
  # Plugin Directory = /usr/lib64/bareos/plugins
  # Plugin Names = ""
}

当我们安装 Bareos 时,我们的许多基本配置都已准备就绪。安装应该自动配置密码。这里我们改变了Name的定义;密码是自动生成的。其余条目都是默认值,没有问题。控制器端口位于 9101 上。QueryFile指令指向/usr/lib/bareos/scripts/query.sql,它是一个 SQL 文件,包含各种“预扫描的”SQL 语句,可用于从控制台查询数据库。如果您认为 Bareos 控制台没有您正在寻找的信息,您可以向该脚本添加您自己的 SQL 查询。

作业描述

一个JobDefs部分将相似的备份作业要求组合在一起。然后在其中一个Job部分中引用它们。例如,为了对定义了FileSetScheduleStorage媒体的 web 服务器的需求进行分组,我们可以为它们创建一个JobDefs部分,而邮件服务器可以在另一个部分中定义。你用来定义一个Job的所有选项都可以放入JobDefs部分。下面创建一个通用的DefaultLinux作业定义:

JobDefs {
  Name = "DefaultLinux"
  Type = Backup
  Level = Incremental
  Client = bareos-fd
  FileSet = "Linux”
  Schedule = "WeeklyCycle"
  Storage = File
  Messages = Standard
  Pool = Incremental
  Priority = 10
  Write Bootstrap = "/var/lib/bareos/%c.bsr"
  Full Backup Pool = Full
  Incremental Backup Pool = Incremental
}

前面的代码取自提供的/etc/bareos/bareos-dir.conf。这里的所有定义也可以在Job资源中定义。首先,我们声明花括号之间的JobDefs部分,{}。接下来,我们将JobDefs命名为DefaultLinux

作业的Type可以是BackupRestoreVerifyAdminBackup作业类型执行备份,Restore类型执行恢复,Verify类型将目录的内容与您刚刚备份的文件系统进行比较,Admin类型可用于执行目录维护(如果需要)。

所需备份的Level不是真正必需的,但是必须在JobDefsSchedule部分指定。Schedule内的任何设置都会覆盖此处的设置。

Level的值可以是FullIncrementalDifferential。完整备份会备份指定的FileSet中的所有文件,而不考虑它们的备份历史。增量备份将备份自上次完全备份、差异备份或增量备份以来更改过的文件。差异备份的工作与增量备份非常相似,但它将备份自上次完全保存以来更改过的所有文件。让我们再多讨论一下。

每当执行差异备份或增量备份时,Bareos 文件守护程序客户端将检查文件列表中已修改的文件时间戳和已更改的文件时间戳,并将它们与之前的完整备份进行比较。如果时间戳不同,则文件将通过增量或差异备份过程进行备份。但是,差异备份与增量备份的区别在于,差异备份将自上次完全备份以来所有更改过的文件合并为一个备份。这意味着,尽管备份可能会占用卷(存储媒体)上更多的空间,但当您从备份中恢复时,您将只需要最后一次完全备份和最近一次差异备份。如果您一直在使用增量备份,您将需要上一次完整备份加上自上一次完整备份以来的每次增量备份。这里我们要说明的另一点是,如果在保留期内此客户端上没有此FileSet的完整备份,Bareos 会自动将您的备份升级到完整备份。我们将把Level = Incremental留在这个JobDefs

Always Incremental

Bareos 还为更传统的完整、差异、增量备份机制提供了另一种选择,称为“始终增量”。顾名思义,这是您只对文件数据进行增量备份的地方(它只适用于基于文件的备份)。

它有助于减少备份过程中的网络流量,当然也能加快备份速度,因为它不需要完整备份。增量备份可以设置为根据天数或作业号进行存储。

该备份期限到期后,整合作业会将增量备份合并到新的备份中。该备份本身可以在特定时间点复制到长期存储中,或者通过虚拟完整作业将所有备份复制到新的长期备份中。

当然,备份从完整备份开始,但随后会执行后续的增量备份(可能在夜间执行),然后每天或在适当的时候在备份服务器上运行整合作业。这将客户端从长时间运行的备份作业中解放出来,并将大量普通的完整备份处理转移到备份服务器。

这里的Client指令设置为bareos-fd。这被Client {}定义所覆盖,但是配置文件需要它。客户端bareos-fd资源必须存在于配置文件中;否则,将会出现错误。我们将让它保持原样。

FileSet = "Linux"指令定义了描述我们希望为主机备份的文件的资源。稍后将在我们的控制器配置文件中定义FileSet。在FileSet中,您可以包含和排除目录和文件名或类型。您还可以定义要备份或排除的 Linux、Microsoft Windows 或 Mac 文件。这里包含的默认FileSet指的是一个典型的 Linux 文件集,很快就会看到。

接下来是我们分配给JobDefsSchedule资源,它定义了我们应该何时运行这种类型的作业。Bareos 中包含的WeeklyCycle的默认定义是简单的每月完整备份,每周一次差异备份。它还从星期一到星期六运行每夜增量备份。我们将对此进行修改,使其每三周运行一次完整备份;我们将很快介绍如何做到这一点。

在默认的JobDefs中,我们还可以看到Storage资源已经被定义为File。这指向由Name = File定义的存储设备的名称。我们刚才解释说,这指向我们的存储配置文件,我们的备份将进入硬盘的一部分,我们已经留出备份,/data/backups/FileStorage

Messages = Standard指的是Messages资源。在这里,您可以设置邮件的处理方式。它们可以写入日志或发送到电子邮件地址。

Pool = Full定义将作业指向备份它们的特定卷池。如前所述,卷是一组存储介质。您可能有一个每日备份组、一个每周备份组和一个每月备份组。在本例中,我们为备份定义了一个默认组Full。但是,我们已经定义了Full Backup Pool = FullIncremental Backup Pool = Incremental,以确保完整备份和增量备份进入正确的介质。

最后,在我们的JobDefs中,我们有Priority = 10。此设置有助于确定作业的优先级;数字越小,Bareos 给予该工作的优先级越高。JobDefs中的一些设置可以被其他区域的定义覆盖,如JobScheduleClient定义。

乔布斯

作业定义了可能发生的实际备份。最基本的作业必须有一个名称、一个客户端、一个JobDefs名称,并且如果启用的话。如果作业已启用,它将在时间表中列出。

Job {
  Name = "BackupCatalog"
  JobDefs = "DefaultJob"
  Level = Full
  FileSet="Catalog"
  Schedule = "WeeklyCycleAfterBackup"

  # This creates an ASCII copy of the catalog
  # Arguments to make_catalog_backup.pl are:
  #  make_catalog_backup.pl <catalog-name>
  RunBeforeJob = "/usr/lib/bareos/scripts/make_catalog_backup.pl MyCatalog"

  # This deletes the copy of the catalog
  RunAfterJob  = "/usr/lib/bareos/scripts/delete_catalog_backup"

  # This sends the bootstrap via mail for disaster recovery.
  # Should be sent to another system, please change recipient accordingly
  Write Bootstrap = "|/usr/bin/bsmtp -h localhost -f \"\(Bareos\) \" -s \"Bootstrap for Job %j\" root@localhost" # (#01)
  Priority = 11                   # run after main backup
}

Job {
  Name = "RestoreFiles"
  Type = Restore
  Client=au-mel-centos-1-fd
  FileSet = "Linux All"
  Storage = File
  Pool = Incremental
  Messages = Standard
  Where = /tmp/bareos-restores
}

Job {
  Name = headoffice.example.com
  Client = headoffice-fd
  Enabled = yes
  JobDefs = "DefaultLinux"
}

前两个作业定义在 Bareos 中是标准的。我们需要名为BackupCatalog的第一个作业来备份保存目录的 MariaDB 数据库。第二个作业定义由还原作业使用。这些都是当前 Bareos 安装中的默认设置。

您还可以看到,我们现在已经描述了一项新工作。在headoffice.example.com作业中,我们已经声明了作业的NameClient(我们将很快对其进行定义)Enabled,并给它一个JobDef。我们使用了JobDefs = "DefaultLinux"来定义我们在工作中可以使用的其他选项。

我们需要处理的剩余项目是FileSetPoolSchedule,我们需要设置一个Client。让我们先来看看FileSet的定义。

文件获取

A FileSet描述了您希望在主机上备份的内容。您可以声明任意多的文件集,并且可以为 Windows、Linux 和 Mac 主机声明这些文件集。我们将创建一个名为Linux的新FileSet

FileSet {
  Name = "Linux"
  Include {
    Options {
      Compression = GZIP
      Signature = SHA1
     }
     File = "/etc"
     File = "/var/lib" File = "/data" File = "/home"
     }
  Exclude {
    File = "/proc"
    File = "/sys"
    File = "/dev"
    File = “/run”
    File = “/tmp”
    File = "/data/backups/FileStorage"
  }
}

在这个FileSet中,我们首先定义Name。这可以在JobDefsJob资源定义中使用。如你所见,我们可以将FileSet分成两部分:?? 段和 ?? 段。在Include部分,我们指定了几个选项:CompressionSignature。这些设置允许我们用 gzip 程序压缩备份数据,并定义SHA1MD5的签名。如果我们使用的磁带机在数据写入磁带时对数据进行硬件压缩,我们就不会在这里使用软件压缩。它还支持其他压缩格式,如 LZO、LZ4 和 LZ4HC。LZ 格式倾向于提供更快的压缩/解压缩速度,但压缩比较小。您还可以在 GZIP1 到 GZIP9 之间选择您的 GZIP 压缩比(GZIP9 是最好的,也是最慢的压缩)。

使用签名,您可以使用 SHA1 或 MD5,但是 MD5 会在数据大小和 CPU 时间方面为每个文件节省额外的开销。签名存储在目录中,用于验证文件的内容以及它们是否已被更改。建议使用 SHA1,因为与 MD5 相比,它冲突的可能性更小,完整性更高。

接下来,我们定义要包括在备份中的文件。这些是/etc/var/lib/data/home目录。我们选择/etc是因为它包含我们系统的主要配置文件,选择/var/lib/目录是因为我们的一些 Samba 和 MariaDB 数据将存储在那里,选择/data/home目录是因为我们的用户数据将存储在那里。

我们也排除了一些目录(或者至少确保它们不会被错误地备份):/proc/sys/devothers。这些目录是非永久性的,并且在主机每次启动时都会改变,因此没有必要保存它们。/data/backups/FileStorage目录被排除在外,因为那是我们要备份到的地方。在这种情况下,对备份进行备份是没有意义的—如果我们这样做了,我们的备份大小会随着每次备份而翻倍。

有趣的是,我们并没有在这里备份整个系统;例如,我们不备份/usr/lib/boot,这些在完整系统恢复中很重要。这背后的原因是,我们有信心重建该主机的速度会快于恢复速度。这样,我们可以减少备份的大小,而不会增加无法在需要时恢复数据的风险。如果你想备份整个 Linux 系统,可以看看Linux All文件集。

我们的文件列表也可以保存在一个单独的文件中,我们将其包含在带有@符号的FileSet中。例如,假设我们有一个如下所示的窗口列表,我们将它保存到一个名为/etc/bareos/windows.list的文件中:

File = "C:/"
File = "D:/"

在我们的FileSet中,我们将包括这样的文件列表:

FileSet {
  Name = "Windows All Drives"
  Enable VSS = yes
  Include {
    Options {
      Signature = MD5
      Drive Type = fixed
      IgnoreCase = yes
      WildFile = "[A-Z]:/pagefile.sys"
      WildDir = "[A-Z]:/RECYCLER"
      WildDir = "[A-Z]:/$RECYCLE.BIN"
      WildDir = "[A-Z]:/System Volume Information"
      WildDir = "*/Cache/*"
      WildDir = "*/cache/*"
      Exclude = yes
      }
  @/etc/bareos/windows.list
  }
}

在这里,我们通过@/etc/bareos/windows.list文件在 Windows 主机上包含了C:D:驱动器。我们在产品目录中使用了 SHA1 签名。我们还排除了一些不需要的目录,使用[A-Z]正则表达式来处理 A 和 z 之间的任何驱动器号。我们不需要任何临时或缓存目录,因为这些目录往往包含大量不需要备份的数据,并且可能非常大。

FileSet {
  Name = "Catalog"
  Include {
    Options {
      signature = MD5
    }
    File = "/var/lib/bareos/bareos.sql" # database dump
    File = "/etc/bareos"                # configuration
  }
}

这个最后的FileSet已经在BackupCatalog资源作业中定义。

时间表

接下来,我们有Schedule资源。一个Schedule定义了一个任务应该何时运行。我们还可以描述我们的备份级别(如果它在时间表中声明,它将覆盖JobJobDefs资源中的设置)。对于我们正在使用的WeeklyCycle,我们需要对它的缺省值稍作修改。我们希望每两个月对我们的数据进行一次完整备份,因此我们将Schedule更改为如下所示:

Schedule {
  Name = "WeeklyCycle"
  Run = Full 1st 3rd sun at 23:05
  Run = Differential 2nd 4th 5th sun at 23:05
  Run = Incremental mon-sat at 23:05
}
Schedule {
  Name = "WeeklyCycleAfterBackup"
  Run = Full sun-sat at 23:10
}

如名为WeeklyCycleSchedule中所述,我们在每个月的第一个和第三个周日运行完整备份。我们每隔第二、第四和第五个星期日运行一次差异,在星期一和星期六之间每晚运行一次增量。这些日期-时间规范由几个定义明确的关键字组成,如果需要,可以很容易地创建复杂的时间表。

Note

你可以在 http://doc.bareos.org/master/html/bareos-manual-main-reference.html#x1-1300009.4 阅读更多关于你可以在Schedule资源文档中使用的术语。

客户

在运行任何备份之前,我们需要创建Client资源。我们通过提供一个Address、一个Name和一个Password来做到这一点。在Client定义中的Name将匹配在Job资源中定义的ClientPassword必须与文件守护程序配置文件(bareos-fd.conf ) Director资源中定义的密码相匹配。Address可以是完全合格的域名或 IP 地址。我们的Client资源将如下所示:

Client {
  Name = headoffice-fd
  Address = headoffice.example.com
  Password = "6rZQQqVsOJeeTPefrG2AslT5ODxtPWO1hNsd7Re1u6J/"
}

仓库

现在让我们配置Storage定义。同样,这将在bareos-dir.conf文件的Storage {}部分中。我们将很快在bareos-sd.conf文件中使用这些信息。

Storage {
  Name = File
# Do not use "localhost" here
  Address = backup.example.com
  Password = "4ncvx7V+Mw4NOMDMyuYjCHsNg1nKgcdh8nl0szWpi6t4"
  Device = FileStorage
  Media Type = File
}

这是Storage部分的默认配置。我们只修改了安装过程中创建的密码Address细节。在图 14-9 中,你看到NamePasswordDeviceMedia Type细节也应该在/etc/bareos/bareos-sd.conf文件中。

目录

接下来,我们应该将这些详细信息添加到 Bareos Director 配置文件中。目录的配置将从Catalog定义开始。

Catalog {
  Name = MyCatalog
  # Uncomment the following lines if you want the dbi driver
  # dbdriver = "dbi:postgresql"; dbaddress = 127.0.0.1; dbport =
  #dbdriver = "postgresql"
  dbdriver = "mysql"
  dbname = "bareos"
  dbuser = "bareos"
  dbpassword = "somepassword"
  dbaddress = headoffice.example.com
}

Name可以是任何东西,我们将坚持使用默认的MyCatalog。默认情况下,Bareos 将使用 PostgreSQL 驱动程序和数据库。在这里,我们为正在使用的 MariaDB 数据库指定了dbdriver mysqldbname应该是bareos,也应该是userdbpassword是我们之前创建的密码。dbaddress应该是Catalog所在主机的地址。

泳池

如前所述,池是组合在一起以反映其用途的媒体卷。您可以按组(如每月、每周、每天或存档)或甚至按 web 服务器、数据库服务器等设置媒体池。在Pool定义中,您可以指定诸如保留策略、标签格式等内容。我们使用硬盘作为存储设备,因此我们不会像使用磁带介质那样加载或转出任何物理上不同的介质。我们在这里的主要兴趣是保留,即在我们擦除数据之前,我们将数据保留多长时间。

在我们的Pool定义中,我们对默认值做了以下更改:

Pool {
  Name = Full
  Pool Type = Backup
  Recycle = yes
  Recycle Oldest Volume = Yes
  AutoPrune = yes
  Volume Retention = 365 days
  Maximum Volume Bytes = 50G
  Maximum Volumes = 100
  Label Format = "Full-${Year}-${Month:p/2/0/r}-${Day:p/2/0/r}"
}

Pool {
  Name = Differential
  Pool Type = Backup
  Recycle = yes
  Recycle Oldest Volume = Yes
  AutoPrune = yes
  Volume Retention = 90 days
  Maximum Volume Bytes = 10G
  Maximum Volumes = 100
  Label Format = "Differential-${Year}-${Month:p/2/0/r}-${Day:p/2/0/r}"
}

Pool {
  Name = Incremental
  Pool Type = Backup
  Recycle = yes
  Recycle Oldest Volume = Yes
  AutoPrune = yes
  Volume Retention = 30 days
  Maximum Volume Bytes = 1G
  Maximum Volumes = 100
  Label Format = "Incremental-${Year}-${Month:p/2/0/r}-${Day:p/2/0/r}"
}

PoolName将用于引用其他资源定义中的Pool。每种都有BackupPool Type,根据 Bareos 文档,这是目前唯一可用的类型。我们设置了两个不同的Maximum Volume Bytes,一个 50GB,一个 10GB,一个 1GB。当卷达到这些大小限制时,这会将卷转到下一个可用卷(或者 Bareos 会为我们创建一个新卷)。这意味着我们将需要处理小文件,而不是一个包含所有备份数据的大文件。

Note

对于磁带介质,您可能不会设置Maximum Volume Bytes限制,因为一般来说,您会希望磁带能容纳尽可能多的容量。如果创建一个具有此限制的卷,无论Pool资源中的设置如何,它都将保持该限制。您必须使用 Bareos 控制台更新卷本身才能更改此限制。

在我们的Pool定义中,我们还设置了AutoPrune = Yes,这使得 Bareos 能够在Volume Retention期到期后从目录中删除备份详细信息。有时候你不希望这种情况发生。例如,对于归档备份,也就是说,当您希望尽可能长时间地保留数据时,您不会希望从目录中删除这些备份的记录。在这种情况下,为了安全起见,您应该将AutoPrune设置为No

Caution

即使您已经将Pool设置为AutoPrune = NoVolume Retention = 10 years,您的备份数据也只会持续磁带或磁盘上的数据。覆盖数据或数据损坏将导致无法恢复数据,无论 Bareos 目录如何规定。

Volume Retention告诉 Bareos 它应该在多长时间内将卷上的数据视为最新数据。我们将池设置为不同的值:我们希望将完整卷中的数据保留 12 个月 90 天,将差异卷中的数据保留 30 天。您可能需要考虑更长的保留期。在此保留期到期后,Bareos 认为卷数据已到期,可以覆盖该数据。

在我们的Pool定义中设置Recycle = Yes意味着一旦卷上的数据被认为过期,该卷将再次对通用池可用,用于再次写入。设置Recycle Oldest Volume = Yes意味着当有多个卷可用时,最早的卷将在任何其他卷之前被使用。这有一个明显的优势,即在最近可能的时间销毁那些卷上的任何现有数据,因此您仍然能够从那些卷恢复到那个时间。

我们的Pool资源中的最后一项是Label定义。Bareos 根据这种格式为卷指定名称或标签。自动加载机中带有条形码标签的磁带不会自动读入 Bareos,需要在添加新磁带时手动添加标签。在磁带介质的情况下,一小部分数据被写入磁带的开头,为其提供该卷的标签名称。标记磁带后,Bareos 将始终知道哪盘磁带插入了磁带单元。我们使用"Full-""Incremental-"并添加后缀"${Year}-${Month:p/2/0/ r}-${Day:p/2/0/r}"来产生类似Full-2016-01-01的标签卷。有关 Bareos 可用变量扩展的完整信息,请访问 http://doc.bareos.org/master/html/bareos-manual-main-reference.html#x1-1190008.3.3

Note

有关使用 Bareos 管理磁带和循环的备份策略的讨论,请访问 http://doc.bareos.org/master/html/bareos-manual-main-reference.html#x1-23200019

安慰

Console服务使控制器能够轮询文件或存储守护程序并报告它们的状态。这种能力通过提供的密码来验证,并且需要允许监控它的 Bareos 服务的名称。在控制器配置文件中,我们有以下内容:

Console {
  Name = example-mon
  Password = "tcqT4bG9NLl11YoZ5GXFLrcb8o9mSuM2U4rxl7mgH8eD"
  CommandACL = status, .status
  JobACL = *all*
}

信息发送

消息服务在控制器配置中定义,是所有来自 Bareos 的通信通过控制器发送的一种方式,而不是来自单个客户端。当事件发生或作业完成时,消息会发送到控制器中配置的标准消息服务。名为Standard的资源在Messages部分中定义,如下所示:

Messages {
  Name = Standard
  mailcommand = "/usr/bin/bsmtp -h localhost -f \"\(Bareos\) \<%r\>\" -s \"Bareos: %t %e of %c %l\" %r"
  operatorcommand = "/usr/bin/bsmtp -h localhost -f \"\(Bareos\) \<%r\>\" -s \"Bareos: Intervention needed for %j\" %r"
  mail = admin@example.com = all, !skipped, !audit # (#02)
  operator = admin@example.com = mount     # (#03)
  console = all, !skipped, !saved, !audit
  append = "/var/log/bareos/bareos.log" = all, !skipped, !audit
  catalog = all, !audit
}

#
# Message delivery for daemon messages (no job).
#
Messages {
  Name = Daemon
  mailcommand = "/usr/bin/bsmtp -h localhost -f \"\(Bareos\) \<%r\>\" -s \"Bareos daemon message\" %r"
  mail = admin@example.com = all, !skipped, !audit # (#02)
  console = all, !skipped, !saved, !audit
  append = "/var/log/bareos/bareos.log" = all, !skipped, !audit
  append = "/var/log/bareos/bareos-audit.log" = audit
}

Bareos 使用mailcommandoperatorcommand命令发送指示 Bareos 状态的电子邮件消息,可以是警报或完成消息。它使用自己的邮件二进制程序bsmtp,向适当的地址发送消息。–f设置发件人标题,-s设置电子邮件的主题标题。您需要更改mailoperator设置来适应您的环境,方法是将电子邮件地址设置为负责管理 Bareos 服务器的人(在我们的例子中是admin@example.com)。与邮件定义中定义的成功/失败消息相比,您可以将operator类型的消息(如“请安装磁带”)设置到不同的电子邮件地址。我们还添加了–h mail.example.com设置,它为mailcommandoperatorcommand命令指向我们的邮件服务器。更多信息,请参见以下文档: http://doc.bareos.org/master/html/bareos-manual-main-reference.html#x1-16500012

在下一节中,我们将查看将要配置的文件,以描述我们的存储守护程序。

巴伦西亚-sd.conf

Bareos 将存储守护程序的配置详细信息存储在/etc/bareos/bareos-sd. conf文件中。Bareos 提供的存储配置文件中有许多不同设备配置方式的示例。您应该使用该文件作为设置不同类型存储设备的指南。在我们的存储文件中,我们有以下内容:

Storage {                             # definition of myself
  Name = example-sd
  Maximum Concurrent Jobs = 20

  # remove comment from "Plugin Directory" to load plugins from specified directory.
  # if "Plugin Names" is defined, only the specified plugins will be loaded,
  # otherwise all storage plugins (*-sd.so) from the "Plugin Directory".
  #
  # Plugin Directory = /usr/lib64/bareos/plugins
  # Plugin Names = ""
}

#
# List Directors who are permitted to contact Storage daemon
#
Director {
  Name = example-dir
  Password = "4ncvx7V+Mw4NOMDMyuYjCHsNg1nKgcdh8nl0szWpi6t4"
}

在文件/etc/bareos/bareos-sd.conf中,我们定义了存储守护进程example-sdName。我们的网络上可能有其他存储守护程序,因此该名称必须是唯一的。Director部分定义了我们的 Bareos 控制器的名称和存储密码,该密码是在/etc/bareos/bareos-dir.conf中定义的,也是在安装过程中自动创建的。

Director {
  Name = example-mon
  Password = "/H9QQD9YJOcd9jTnX1w/aFMMghxFuHmPYuJv882Sk54F"
  Monitor = yes
}
Messages {
  Name = Standard
  director = example-dir = all
}

bareos-dir.conf中的MonitorConsole部分定义了资源Director { ... Name = example-mon ... }。这用于监控资源,并允许控制器获取存储守护程序的状态。它要求在控制器配置中和此处声明密码。同样,该密码是在安装过程中生成的。

消息资源允许存储守护程序通过 Bareos Director 守护程序发送信息性消息。

Device部分中,Name与控制器配置文件的Storage部分中的Device相匹配。Media Type = FileStorage在控制器和存储配置文件中匹配。

Device {
  Name = FileStorage
  Media Type = File
  Archive Device = /data/backups/FileStorage
  LabelMedia = yes;
  Random Access = Yes;
  AutomaticMount = yes;
  RemovableMedia = no;
  AlwaysOpen = no;
}

在这里,我们定义了存储备份的设备。我们没有任何磁带机或自动加载器,因此这将是一个简单的基于磁盘的存储设置。我们定义备份文件在主机上的存储位置。在本例中,我们将它们存储在/data/backups/FileStorage目录中。当谈到备份到文件系统时,大多数其他选项实际上并不相关,我们保留了其他选项的默认值。

另外,值得注意的是,Plugin Names = ""指令是您可以加载任何您可能想要使用的特定插件的地方。有文件守护程序插件和存储守护程序插件。文件守护程序插件主要关心如何从备份数据,而存储处理如何将数据备份到。

Note

您可以在 http://doc.bareos.org/master/html/bareos-manual-main-reference.html#x1-15000010 了解有关存储守护程序配置的更多信息。

bareos-fd.conf 文件

在进行备份之前,我们必须做的最后一件事是在我们的headoffice.example.com主机上安装bareos-fd软件。CentOS 和 Ubuntu 主机需要bareos-commonbareos-client,它们将下载我们可能也需要的任何依赖项。一旦这些被安装,我们将编辑/etc/bareos/bareos-fd.conf文件并添加以下信息:

Director {
FileDaemon {                          # definition of myself
  Name = headoffice-fd
  WorkingDirectory = /var/lib/bareos
  Maximum Concurrent Jobs = 20

  # remove comment from "Plugin Directory" to load plugins from specified directory.
  # if "Plugin Names" is defined, only the specified plugins will be loaded,
  # otherwise all storage plugins (*-fd.so) from the "Plugin Directory".
  #
  # Plugin Directory = /usr/lib64/bareos/plugins
  # Plugin Names = ""

  # if compatible is set to yes, we are compatible with bacula
  # if set to no, new bareos features are enabled which is the default
  # compatible = yes
}

#
# List Directors who are permitted to contact this File daemon
#
Director {
  Name = example-dir
  Password = "6rZQQqVsOJeeTPefrG2AslT5ODxtPWO1hNsd7Re1u6J/"
}

#
# Restricted Director, used by tray-monitor to get the
#   status of the file daemon
#
Director {
  Name = example-mon
  Password = "WOhEm9JC+uBvpNBJta1RuA7NJSmUgQrz3QPRgPYhRzyZ"
  Monitor = yes
}

# Send all messages except skipped files back to Director
Messages {
  Name = Standard
  director = example-dir = all, !skipped, !restored
}

在文件守护程序配置脚本中,我们声明了用于连接到 Bareos Director 守护程序的Director {}节。这需要我们正在连接的控制器的Name和我们在headoffice-fdClient资源中定义的PasswordFileDaemon主要定义NameMaximum Concurrent Jobs设置。Name设置必须与bareos-dir.conf文件中Client资源中的Name相匹配。Maximum Concurrent Jobs定义一次可以连接到该主机的作业数量。

DirectorMessages部分与存储文件中描述的相同。它们用于允许 Director 获取文件守护程序的状态,并定义在出现错误时向何处发送消息。

测试语法

我们现在可以通过发出以下命令来测试我们的bareos-dir.conf配置文件的语法:

$ sudo /usr/sbin/bareos-dir -t -c /etc/bareos/bareos-dir.conf

对于文件和存储守护程序,我们将发出以下命令:

$ sudo /usr/sbin/bareos-fd -t -c /etc/bareos/bareos-fd.conf

或者这个:

$ sudo /usr/sbin/bareos-sd -t -c /etc/bareos/bareos-sd.conf

如果两个命令都没有返回错误,那么每个命令都有语法上正确的配置,我们可以使用systemctl来启用和启动我们的服务。太好了。他们在跑,是吗?你说得对——我们怎么知道呢?我们可以使用 status 参数来查看它们是否正在运行。

$ sudo systemctl enable bareos-dir bareos-sd bareos-fd
$ sudo systemctl start bareos-dir bareos-sd bareos-fd
$ sudo systemctl status bareos-dir bareos-sd bareos-fd

bconsole.conf

我们现在必须设置控制台,以便管理和监控备份服务。保存我们的bconsole的配置的文件叫做/etc/bareos/bconsole.conf

有几个控制台可用,根据您自己的偏好,您可以设置 GUI 或基于屏幕的控制台。我们将首先设置基于屏幕的控制台,然后我们将在本章的后面设置 Web-UI 控制台。在/etc/bareos/bconsole.conf文件中,我们定义了以下内容:

Director {
  Name = example-dir
  DIRport = 9101
  Address = backup.example.com
  Password = "uogx2tNL9dRUwGfNvY/b+uQrU8osZn+JOM7t8iIrpszN"
}

这与bareos-dir.conf配置文件中的Directory资源具有相同的细节(NameportAddressPassword)。同样,密码是在安装过程中创建和安装的。

使用 bconsole 管理 Bareos

在配置了 Bareos 之后,您可以使用systemctl命令启动服务,就像我们之前做的那样。我们现在可以使用bconsole命令启动 Bareos 控制台程序,如下所示:

$ sudo /usr/sbin/bconsole

首先,我们可以通过发出status all命令来列出控制器的当前状态。发出此命令将提供 Bareos 控制器上发生的情况的详细列表,包括连接的存储守护程序和文件守护程序。

为了了解控制器本身在做什么,我们使用了stat dir命令。此命令概述了挂起的作业、当前正在运行的作业以及已完成的作业。我们通过发出以下命令来访问控制台:

$ sudo /usr/sbin/bconsole
Connecting to Director backup.example.com:9101
1000 OK: example-dir Version: 15.2.2 (16 November 2015)
Enter a period to cancel a command.
* stat dir
example-dir Version: 15.2.2 (16 November 2015) x86_64-redhat-linux-gnu redhat CentOS Linux release 7.0.1406 (Core)
Daemon started 10-Oct-16 00:56\. Jobs: run=0, running=0 mode=0 db=mysql
 Heap: heap=274,432 smbytes=86,156 max_bytes=86,156 bufs=321 max_bufs=322

Scheduled Jobs:
Level          Type     Pri  Scheduled          Name                      Volume
=================================================================================
Incremental    Backup    10  10-Oct-16 23:05    headoffice.example.com     *unknown*
Full                 Backup    11  10-Oct-16 23:10    BackupCatalog                   *unknown*

Running Jobs:
Console connected at 10-Oct-16 00:57
No Jobs running.
====
No Terminated Jobs.
====
*

如您所见,我们没有已完成的作业,也没有正在运行的作业。我们计划在 10 月 11 日 23:05 运行两个作业,一个备份headoffice.example.com,另一个备份目录(BackupCatalog)。多花点时间探索一下bconsole。例如,键入help,然后按 Enter 键查看命令行上可用的命令列表。表 14-4 列出了最有用的命令。

表 14-4。

Useful Bareos bconsole Commands

| 命令 | 描述 | | --- | --- | | `run` | 启动作业(备份) | | `cancel` | 取消当前作业 | | `mess` | 显示任何当前消息 | | `restore` | 管理器还原作业 | | `label` | 标记磁带或媒体卷 | | `update volume` | 允许您更新任何特定卷的属性 | | `stat dir` | 获取控制器守护程序的当前状态 | | `stat client` | 获取文件守护程序的当前状态 | | `stat storage` | 获取存储守护程序的当前状态 | | `mount` | 装入卷(例如,将媒体卷装入磁带驱动器) | | `unmount` | 卸载媒体卷(例如,从驱动器中卸载磁带) |

Note

有用命令的另一个资源在位于 http://doc.bareos.org/master/html/bareos-manual-main-reference.html#x1-18400015 的 Bareos 文档中。

您可以通过两种方式运行作业或备份。一种是使用run命令并按回车键。然后,您将看到一个易于理解的菜单驱动方式来提交您的备份。另一种方法是将命令直接放在命令行上,如下所示:

* run job=headoffice.example.com level=full priority=7 yes

当我们运行* stat dir命令时,我们可以看到我们的作业正在运行。

Running Jobs:
Console connected at 10-Oct-16 01:21
 JobId Level   Name                       Status
============================================================
     1 Full    headoffice.example.com.2016-10-10_01.21.34_04 is running
====

从该状态中,您可以看到我们的作业 ID 为 1,并且我们正在运行完整备份,因为我们还没有完整备份。如果不存在完整备份,Bareos 将自动启动完整备份。新卷已创建,备份正在恢复到该卷。如果您在命令行中键入 mess,您将会收到有关您的作业的任何可用消息。

现在,如果我们想要停止这个作业,我们可以使用带有参数JobId 1cancel命令。你可以在前面的Running Jobs输出中看到JobId的使用。

*cancel jobid=1

cancel命令可以不带参数运行,并且会自动为您选择JobId(如果只有一个作业正在运行;否则,它会显示一个列表以供选择)。我们再次运行了该作业,这次没有取消它,它已经完成。当我们在Terminated Jobs部分执行stat dir时,我们会发现以下内容:

Terminated Jobs:
 JobId  Level    Files      Bytes   Status   Finished        Name
==================================================================
 2      Full     8,908      140.1 M  OK      10-Oct-16 01:21 headoffice.example.com

前面的输出显示,我们对主机headoffice.example.comLinux FileSet进行了完整保存。您可以看到它保存的文件数量和数据量。让我们尝试使用restore命令恢复/etc/hosts文件。

* restore
To select the JobIds, you have the following choices:
     1: List last 20 Jobs run
     2: List Jobs where a given File is saved
     3: Enter list of comma separated JobIds to select

这里使用的选项列表实际上有 12 个,但是我们将使用选项 2 来找出JobId(从Terminated Jobs输出中我们已经知道它是 4)。

Select item: (1-12): 2
Automatically selected Client: headoffice-fd
Enter Filename (no path):hosts
+-------+------------+---------------------+---------+-----------+----------+-------------+
| JobId | Name       | StartTime           | JobType | JobStatus | JobFiles | JobBytes    |
+-------+------------+---------------------+---------+-----------+----------+-------------+
| 2     | /etc/hosts | 2016-10-10 01:21:36 | B       | T         | 8908     | 140144767   |
+-------+------------+---------------------+---------+-----------+----------+-------------+

现在我们知道了我们的JobId,我们将使用选项 3 来恢复我们的 hosts 文件,如下所示:

Select item: (1-12): 3
Enter JobId(s), comma separated, to restore: 4
You have selected the following JobId: 4

Building directory tree for JobId 4 ... ++++++++++++++++++++++++++++++++++++
1 Job, 1,174 files inserted into the tree. cwd is: /
$ cd /etc
cwd is: /etc/
$ mark hosts
1 file marked.
$ done
Bootstrap records written to /var/lib/bareos/example-dir.restore.1.bsr

The job will require the following
   Volume(s)                 Storage(s)                SD Device(s)
===========================================================================

    Full-0001                 File                      FileStorage

Volumes marked with "*" are online.

1 file selected to be restored.

Run Restore job
JobName:         RestoreFiles
Bootstrap:       /var/lib/bareos/example-dir.restore.1.bsr
Where:           /tmp/bareos-restores
Replace:         Always
FileSet:         Linux All
Backup Client:   headoffice-fd
Restore Client:  headoffice-fd
Format:          Native
Storage:         File
When:            2016-10-10 01:32:35
Catalog:         MyCatalog
Priority:        10
Plugin Options:  *None*
OK to run? (yes/mod/no): yes
Job queued. JobId=4

选择要恢复的文件时,您可以使用lscd命令在搜索文件时列出并更改目录。

cwd is: /
$ cd /etc
cwd is: /etc/
$ mark hosts
1 file marked.
$ done

这里,在使用cd命令浏览备份的文件系统之后,我们使用mark命令选择我们想要恢复的文件。要递归地选择目录中的所有内容,可以使用mark *命令。当您选择完想要恢复的文件后,使用done命令。

您将获得执行该还原所需的介质卷列表。如果备份主机还没有它们,您必须加载它们。然后,您会看到将要运行的还原作业的概要。记下Where: /tmp/bareos-restores,因为这是您恢复的文件所在的位置。

查看stat dirTerminated Jobs部分,完整的恢复将如下所示:

JobId  Level    Files      Bytes    Status    Finished          Name
==========================================================================
    4      1      403        OK     10-Oct-16 01:32          RestoreFiles

现在我们可以检查文件是否已经恢复到了/tmp/bareos-restores目录。

$ $ sudo ls -l /tmp/bareos-restores/etc/
total 4
-rw-r--r--. 1 root root 403 Oct 10 00:53 hosts

在我们确认要运行恢复作业之前,我们可以通过更改ClientWhere选项,始终使用restore命令来恢复到不同的主机和不同的目标。

使用 GlusterFS 进行备份存储

我们将快速介绍如何设置您的 Bareos 服务器,以使用 GlusterFS 存储作为我们的备份设备。正如您所记得的,GlusterFS 在商用硬件上为我们提供了可靠且可扩展的存储选项,并且是在磁盘上安全保存备份的绝佳选择。在本节中,我们将执行以下操作:

  • 在 GlusterFS 中创建新的备份卷,并更新我们的 GlusterFS 配置
  • 向我们的 DNS 服务器添加新记录
  • 挂载卷和安装目录
  • 配置 Bareos 存储

Bareos 可以通过bareos-storage-glusterfs包提供的libgfapi直接与 Gluster 守护进程对话。这意味着我们不会 FUSE mount g luster 卷,而是允许 Bareos 直接访问守护进程。

正在创建备份 Gluster 卷

像第十三章一样,要在 GlusterFS 集群上创建一个新卷,我们首先需要添加新设备(磁盘)并格式化和挂载它们。完成后,我们需要用这些新的砖块创建新的卷。

我们将假设我们已经向 Gluster 服务器添加了新磁盘。我们可以执行以下命令:

$ sudo mkfs.xfs -f -L brick3_r1 /dev/sdf && \
   sudo mkdir -p /data/brick3_r1 && \
   sudo bash -c 'echo LABEL=brick3_r1 /data/brick3_r1 xfs defaults 1 2 >> /etc/fstab' && \
   sudo mount –a && \
   sudo mkdir /data/brick3_r1/backups

我们之前已经看到了这个命令,我们正在格式化磁盘,创建将要挂载设备的目录,将磁盘添加到我们的fstab中的自动挂载,挂载设备,最后在挂载的磁盘上创建一个backup目录。我们已经在所有三台服务器上完成了这项工作,现在我们准备创建 GlusterFS 卷。

$ sudo gluster volume create backups replica 3 \
  au-mel-dfs-1:/data/brick3_r1/backups \
  au-mel-dfs-2:/data/brick3_r1/backups \
  au-mel-dfs-3:/data/brick3_r1/backups

我们现在开始放量。

$ sudo gluster volume start backups

我们的卷现在可以挂载了,但是首先我们需要对 Gluster 配置进行一些更改,以允许 Bareos 服务器直接联系我们的glusterd守护进程。为此,我们需要做以下工作。

首先,我们需要编辑/etc/glusterfs/glusterd.vol文件,并在每台 Gluster 服务器上添加以下内容:

volume management
    type mgmt/glusterd
    option working-directory /var/lib/glusterd
...<snip>...
    option rpc-auth-allow-insecure on
end-volume

在做出这一更改后,我们需要重新启动glusterd服务。此更改允许从我们的备份服务器进行非特权连接。我们还将仅对 Gluster backup的音量进行更改。

$ gluster volume set backups server.allow-insecure on

这只需要在一个 Gluster 对等体上执行。我们现在准备进入下一步。

更新 DNS 记录

我们将使用一个主机名来联系我们的 GlusterFS 服务。我们要把这个叫做storage.example.com,需要创建一系列 A 记录来响应。

我们需要在我们的 DNS 服务器上,我们将发出以下命令:

$ sudo nsupdate -k /etc/bind/ddns_update.key
> server localhost
> update add storage.example.com 360 A 192.168.0.240
> update add storage.example.com 360 A 192.168.0.241
> update add storage.example.com 360 A 192.168.0.242
> send
> quit

使用dig,我们可以看到我们的 DNS 返回了正确的记录:

$ dig @localhost storage.example.com
....
;; ANSWER SECTION:
storage.example.com.    360    IN    A    192.168.0.242
storage.example.com.    360    IN    A    192.168.0.240
storage.example.com.    360    IN    A    192.168.0.241

现在,当我们查询storage.example.com时,我们将以循环方式接收这些 IP 地址中的一个。

准备 Bareos 服务器

现在是准备 Bareos 服务器的时候了。在backup.example.com我们将挂载 Gluster 卷,创建存储目录,并设置该目录的所有权。

我们需要通过熔丝包安装 Gluster 卷。我们将确保软件包glusterfs-fuse安装在我们的 CentOS 备份服务器上。一旦完成,我们将装载我们的卷。

$ sudo mount –t glusterfs storage.example.com:/backups /mnt

现在我们已经挂载了 Gluster 卷,我们将创建一个目录并更改所有权。我们首先获取用户的 UID 和 GID。我们可以通过发出以下命令来做到这一点:

$ id bareos
uid=899(bareos) gid=986(bareos) groups=986(bareos),6(disk),30(tape)

现在我们将创建目录,然后更改目录的所有权和模式。

$ sudo mkdir /mnt/FileStore && \
  sudo chown 899:986 /mnt/FileStore && \
  sudo chmod 2770 /mnt/FileStore

我们现在可以卸载该目录。

$ sudo umount /mnt

现在我们需要更改/etc/bareos/bareos-dir.conf/etc/bareos/bareos-sd.d/device-gluster.conf文件。先说后者。

/etc/bareos/bareos-sd.d/device-gluster.conf
Device {
  Name = GlusterStorage
  Archive Device = "Gluster Device"
  Device Options = "uri=gluster://storage.example.com/backups/FileStore"
  Device Type = gfapi
  Media Type = GlusterFile
  Label Media = yes
  Random Access = yes
  Automatic Mount = yes
  Removable Media = no
  Always Open = no
}

一旦这样做了,我们就可以改变bareos-dir.conf。让我们现在做那件事。

/etc/bareos/bareos-dir.conf
Storage {
  Name = Gluster
  Address = backup.example.com                # N.B. Use a fully qualified name here
  Password = "4ncvx7V+Mw4NOMDMyuYjCHsNg1nKgcdh8nl0szWpi6t4"
  Device = GlusterStorage
  Media Type = GlusterFile
  Maximum Concurrent Jobs = 10
}

这将目录存储映射到设备存储文件。我们现在将为存储选项更改名为DefaultLinuxJobDefs

JobDefs {
  Name = "DefaultLinux"
  Type = Backup
...<snip> ...
  Storage = Gluster
...<snip>...
}

最后,我们需要告诉存储守护进程加载我们的 Gluster 设备。我们需要删除下面一行中的#:

/etc/bareos/bareos-sd.conf
...<snip>...
@/etc/bareos/bareos-sd.d/device-gluster.conf
...<snip>...

我们现在需要重新加载我们的配置,以便 Bareos 存储守护程序将添加到device-gluster.conf文件中。使用bconsole,我们可以发出reload命令,或者我们可以使用普通的systemctl命令重启。我们现在准备备份到我们的 GlusterFS 服务器。

bconsole开始,我们将发出以下命令,查看我们是否已经为备份到 Gluster 做好了一切准备:

*status Storage=Gluster
... <snip>...
====

Device status:

Device "GlusterStorage" (Gluster Device) is not open.
==
...<snip>...

我们现在准备好进行备份了。让我们在下一节测试我们的数据库备份。

使用 Bareos 插件备份数据库

我们现在想向您展示一个更高级的场景。我们的主机上运行着一个 MariaDB 数据库。目前它保存着我们的 Bareos 目录数据库,但它也可能包含我们在第十一章中创建的 WordPress 数据库或我们使用的任何其他数据库。

正如我们提到的,您可以在存储和文件守护进程上安装 Bareos 插件来执行特殊任务。我们将在数据库服务器上安装一个 Python 插件,以便文件守护程序可以为我们备份 MariaDB 数据库。这个插件将利用mysqldumpmysql命令来创建一个数据库备份,为每个找到的数据库创建一个文件。

我们将要使用的插件可以在下面找到:

我们将把它安装在/usr/lib64/bareos/plugins中,然后做一个配置更改来更新我们的bareos-fd.conf文件。为此,我们将使用curl命令从 GitHub 库中复制两个文件。

# curl -s https://raw.githubusercontent.com/bareos/bareos-contrib/master/fd-plugins/mysql-python/BareosFdMySQLclass.py \
  -o /usr/lib64/bareos/plugins/BareosFdMySQLclass.py
# curl -s https://raw.githubusercontent.com/bareos/bareos-contrib/master/fd-plugins/mysql-python/bareos-fd-mysql.py \
  -o /usr/lib64/bareos/plugins/bareos-fd-mysql.py

这两个文件已经作为根用户通过curl复制到plugins目录中。现在我们必须在数据库所在的headoffice.example.com服务器上配置/etc/bareos/bareos-fd.conf文件。

FileDaemon {                          # definition of myself
  Name = headoffice-fd
  Maximum Concurrent Jobs = 20

  # remove comment from "Plugin Directory" to load plugins from specified directory.
  # if "Plugin Names" is defined, only the specified plugins will be loaded,
  # otherwise all storage plugins (*-fd.so) from the "Plugin Directory".
  #
  Plugin Directory = /usr/lib64/bareos/plugins
  Plugin Names = "bareos-fd-mysql.py"
...
}

您需要重启headoffice.example.com服务器上的文件守护程序。现在,我们将更改控制器配置,并为数据库服务器创建一个新的FileSet

FileSet {
  Name = "DBLinux"
  Include {
    Options {
      Compression = GZIP
      Signature = SHA1
     }
     File = "/etc"
     File = "/var/lib" File = "/data" File = "/home"
     Plugin = "python:module_path=/usr/lib64/bareos/plugins:module_name=bareos-fd-mysql"
     }
  Exclude {
    File = "/proc"
    File = "/sys"
    File = "/dev"
    File = "/run"
    File = "/tmp"
    File = "/data/backups"
  }
}

我们还将为headoffice.example.com创建一个新的JobDefs

JobDefs {
  Name = "DBLinux"
  Type = Backup
  Level = Incremental
  Client = headoffice-fd
  FileSet = "DBLinux"
  Schedule = "WeeklyCycle"
  Storage = Gluster
  Messages = Standard
  Pool = Incremental
  Priority = 10
  Write Bootstrap = "/var/lib/bareos/%c.bsr"
  Full Backup Pool = Full
  Incremental Backup Pool = Incremental
}

我们为数据库服务器创建了一个新的JobDefs并添加了 DBLinux FileSet。我们可以根据需要更改作业的其他默认值,如时间表或存储。最后,我们需要更改数据库服务器的作业。

Job {
  Name = headoffice.example.com
  Client = headoffice-fd
  Enabled = yes
  JobDefs = "DBLinux"
}

我们需要重新加载或重新启动 Director 服务,它才会变为活动状态。让我们尝试为headoffice.example.com运行我们的备份。

*  run job=headoffice.example.com client=headoffice-fd
Automatically selected Catalog: MyCatalog
Using Catalog "MyCatalog"
Run Backup job
JobName:  headoffice.example.com
Level:    Incremental
Client:   headoffice-fd
Format:   Native
FileSet:  DBLinux
Pool:     Incremental (From Job IncPool override)
Storage:  Gluster (From Job resource)
When:     2016-10-11 22:29:37
Priority: 10
OK to run? (yes/mod/no): yes
Job queued. JobId=15

在这里,您可以看到我们的任务正在被调度,并使用DBLinux FileSet运行。随着该作业的运行,我们可以在/var/log/bareos/bareos.log中跟踪它,我们可以在这里看到正在备份的数据库:

11-Oct 22:29 example-sd JobId 15: Ready to append to end of Volume "Full-0004" size=145926917
11-Oct 22:29 headoffice-fd JobId 15:      /var/lib/nfs/rpc_pipefs is a different filesystem. Will not descend from /var/lib into it.
11-Oct 22:29 headoffice-fd JobId 15: Starting backup of /_mysqlbackups_/test.sql
11-Oct 22:29 headoffice-fd JobId 15: Starting backup of /_mysqlbackups_/mysql.sql
11-Oct 22:29 headoffice-fd JobId 15: Starting backup of /_mysqlbackups_/bareos.sql
11-Oct 22:29 example-sd JobId 15: Elapsed time=00:00:08, Transfer rate=18.22 M Bytes/second

当我们在bconsole中发出restore命令并选择jobid进行最后一次恢复时,我们可以看到数据库文件的备份。

cwd is: /
$ cd _mysqlbackups_/
cwd is: /_mysqlbackups_/
$ ls
bareos.sql
mysql.sql
test.sql
$ mark bareos.sql
1 file marked.
$ done

恢复时,bareos.sql文件的内容包含我们的bareos数据库的完整备份。

Caution

确保您的主机上有足够的空间来处理您的数据库转储的大小——如果您用完了空间,您可能会遇到麻烦。将该文件转储到一个单独的分区是一个好主意,这样,如果空间不足,就不会影响主机上的其他进程。

Bareos 还在Job资源中提供了两个指令,我们可以在运行复杂的任务时加以利用:Client Run Before JobClient Run After Job。顾名思义,这些指令可用于在目标主机上运行脚本。Client Run Before Job将在目标主机上执行命令或脚本,并要求成功完成后才能继续。如果不成功,作业将终止并产生一条错误消息。Client Run After Job还要求在目标主机上成功完成脚本或命令;否则,它将停止对该作业的进一步处理,并提交一条错误消息。如果我们不想使用 Python 插件,我们可以编写自己的脚本。为了使用这些,我们可以改变我们之前定义的headoffice.example.com Job资源。我们将对其进行如下更改:

Job {
  Name = headoffice.example.com
  Client = headoffice-fd
  Enabled = yes
  JobDefs = "DefaultLinux"
  Client Run Before Job = "/usr/local/bin/mysql_backup start"
  Client Run After Job = "/usr/local/bin/mysql_backup stop"
}

脚本mysql_backup必须存在于我们的目标主机的/usr/local/bin目录中。

现在,您可以看到如何在 Bareos 备份服务器上管理复杂的备份。您可以使用 Bareos 备份整个文件系统,也可以只备份数据目录。如果您打算只以数据目录为目标,请确保考虑在主机出现问题时如何重建主机。

接下来,我们将了解如何使用 Web-UI 控制台来管理我们的 Bareos 配置。

介绍 Bareos Web-UI

使用基于文本的控制台是设置和管理 Bareos 服务的快捷方式。然而,有些人更喜欢 GUI,所以 Bareos 为管理 Bareos Director 创建了一个漂亮、干净的 web 界面,称为 Web-UI。

我们在第十一章中建立了一个 PHP 网络服务,如果你已经忘记了,现在是时候回顾一下了。Web-UI 可以安装在任何 Web 主机上;它不需要安装 Bareos 控制器。您可以通过在线资源库中的bareos-webui包安装 Web-UI。它将为您安装所需的依赖项。

bareos-webui包将把 web 文件安装到/usr/share/bareos-webui中,并在/etc/httpd/conf.d/bareos-webui.conf中安装一个 Apache 配置文件。

我们可以重启 Apache 服务,然后我们将创建一个用户来访问 web 界面。如果您的安装过程中尚未创建文件/etc/bareos/bareos-dir.d/admin.conf,我们将创建该文件。

Console {
  Name = "admin"
  Password = "secret"
  Profile = "webui"
}

接下来,我们需要创建文件/etc/bareos/bareos-dir.d/webui-consoles.conf,并为该文件输入以下内容:

Profile {
  Name = webui
  CommandACL = status, messages, show, version, run, rerun, cancel, .api, .bvfs_*, list, llist, use, restore, .jobs, .filesets, .clients
  Job ACL = *all*
  Schedule ACL = *all*
  Catalog ACL = *all*
  Pool ACL = *all*
  Storage ACL = *all*
  Client ACL = *all*
  FileSet ACL = *all*
  Where ACL = *all*
}

该配置文件与管理员用户可以使用webui配置文件访问的命令相关。如果你想限制对某些概要文件的访问,你可以通过列出概要文件可以访问的资源来编辑这个文件(更多细节见 http://doc.bareos.org/master/html/bareos-manual-main-reference.html#x1-1510009.12 )。

我们现在需要 Bareos Director 在启动时读入这些文件。为此,我们需要将以下内容添加到我们的bareos-dir.conf文件的底部:

@/etc/bareos/bareos-dir.d/webui-consoles.conf
@/etc/bareos/bareos-dir.d/webui-profiles.conf

同样,我们使用@符号告诉 Bareos 控制器读取这些文件。我们现在必须重新启动或重新加载控制器。

一旦所有这些都完成了,我们应该能够重启 Apache 服务器并登录到 Bareos Web-UI。让我们登录,如图 14-10 所示。

A185439_2_En_14_Fig10_HTML.jpg

图 14-10。

Log Web-UI page

我们使用密码秘密和用户名 admin,这是我们在前面的webui-consoles.conf中指定的。在图 14-11 中,我们看到了主仪表板。

A185439_2_En_14_Fig11_HTML.jpg

图 14-11。

Dashboard page of Web-UI

我们不会浏览每一页;如您所见,有许多部分需要探索,这些部分映射到 Bareos 配置的各个部分。我们确实想向您展示恢复页面,因为您可以使用它来查找和恢复您的备份,如图 14-12 所示。

A185439_2_En_14_Fig12_HTML.jpg

图 14-12。

Restore page on Web-UI

在这里,您可以看到您可以通过 Web-UI 浏览和选择您的恢复选项,如果您需要恢复任何文件,这将非常有用。

摘要

在本章中,我们探讨了在混合环境中备份单个 Linux 主机或数百台服务器和工作站时可用的备份选项。我们向您介绍了可用于管理备份、rsync和 Bareos 的软件。您可以将它们结合起来,以安全地备份远程主机。

在第十九章中,我们将介绍自动配置系统,该系统与备份方案相结合,可让您快速高效地恢复主机。

在本章中,我们讨论了以下主题:

  • 制定灾难恢复规划和业务连续性管理战略时要考虑的问题
  • 如何使用rsync和 SSH 将远程主机备份到中央主机
  • 如何编写一个简单的 Bash 脚本来定期rsync你的远程主机
  • 如何安装和配置 Duply 和 Duplicity
  • 如何安装和配置 Bareos
  • 如何使用 Bareos 运行备份作业
  • 如何使用 Bareos 恢复文件
  • 如何使用 MariaDB 数据库的 Bareos 进行高级备份
  • Bareos Web-UI 控制台,这是一个用于 Bareos 的基于 Web 的高级管理 GUI

在下一章,我们将向你展示如何管理你自己的 VPN 链接和网络安全。

十五、使用 VPN 联网

在前几章中,我们讨论了您的组织可能实现的许多服务(例如,电子邮件和 web 服务)。我们向您展示了向您的用户和客户提供这些服务的各种方式,包括通过互联网,以及向移动用户和位于其他站点的用户提供这些服务。但是,有些服务在本地提供更容易、更安全(例如文件和打印服务)。如果您的用户不在本地,那么您需要某种方式将他们连接起来,就像他们在本地一样。输入虚拟专用网络(VPN)。

本质上,VPN 是一个运行在公共或敌对网络上的私有网络。VPN 通常被称为隧道,它们用于保护您希望在其他公共网络(如互联网)上保密的流量。VPN 可以从网络设备或主机发起。例如,它们可以在两个办公室之间进行,或者从台式机或笔记本电脑等客户端发送到办公室。通过 VPN 运行的流量通常通过 TLS/SSL 证书、密码或双因素身份验证机制(如令牌或智能卡)等机制进行加密和身份验证。

VPN 是常见的,家庭和企业级防火墙通常会支持它们。

在本章中,我们将向您展示如何安装创建 VPN 所需的软件(在我们的例子中,是一个名为 OpenVPN 的开源 VPN 工具),以及如何配置和生成 VPN 隧道。

我们的示例网络

在本章中,我们将演示各种 VPN 连接,并且我们将使用我们在第七章中创建的示例网络来配置我们的示例环境和网络。图 15-1 再次显示网络。

A185439_2_En_15_Fig1_HTML.jpg

图 15-1。

Our example network

目前,在我们的示例网络中,我们的总部有两台主机:

  • gateway.example.com:我们的堡垒主机。它的外部 IP 地址为 10.0.2.155,内部 IP 地址为 192.168.0.254。
  • headoffice.example.com:我们主要的总部服务器主机。它的内部 IP 地址为 192.168.0.1,外部连接通过网关主机出去。

Note

我们在第十章中展示了如何创建这些域名CNAMEs(例如gatewayheadoffice)。

我们还有三个分支机构,每个都有自己的内部和外部 IP 地址范围。

  • branch1.example.com:外部 IP 地址为 10.0.2.156 的分支机构主机。该分支机构的内部 IP 地址范围为 192.168.10.0/24。
  • branch2.example.com:外部 IP 地址为 10.0.2.157 的分支机构主机。该分支机构的内部 IP 地址范围为 192.168.20.0/24。
  • branch3.example.com:外部 IP 地址为 10.0.2.158 的分支机构主机。该分支机构的内部 IP 地址范围为 192.168.30.0/24。

OpenVPN 简介

OpenVPN ( http://openvpn.net/ )是由 James Yonan 编写的开源 SSL VPN 应用程序,可以在 GNU GPLv2 许可证(OpenVPN v2)和 AGPL (OpenVPN v3)下获得。还有一个单独的 EULA 下的商业版本。您还可以选择使用 AWS Marketplace 捆绑的 EC2 实例,它允许您按小时付费,并消除了一些设置它的管理负担。

它以客户机/服务器模式工作,服务器运行在您的主机上,客户机连接到服务器并创建 VPN 隧道。

Note

其他基于 Linux 的 VPN 解决方案也是可用的,包括 Openswan 等 IPsec 实现。

OpenVPN 运行在各种平台上,包括 Linux、Solaris、macOS 和 Microsoft Windows。这允许您将各种客户机连接到您的 Linux 主机;例如,您可以从运行 Microsoft Windows 的台式机或笔记本电脑连接 VPN 隧道。OpenVPN 甚至可以在运行 Android、苹果 iPhone 或 Windows Mobile 的移动设备上运行。您可以使用它从这些类型的设备创建 VPN 隧道,以允许您安全地访问内部网络中的资源。

OpenVPN 也可以配置成使用几种不同的认证服务。我们将向您展示如何使用普通的 Linux PAM 模块进行身份验证,但是您也可以使用 PAM 针对 LDAP 和 RADIUS 之类的服务进行身份验证。

在接下来的部分中,我们将演示如何在各种配置中安装和设置 OpenVPN。

安装 OpenVPN

您需要在连接的两端安装 OpenVPN。对于主机来说,这意味着在两端安装 OpenVPN 服务器。如果连接的一端是支持连接到 OpenVPN 的网络设备,那么您只需要在将用作隧道端点的主机上安装服务器。

我们将从在示例网络的总部分支机构中的堡垒主机gateway.example.com上安装服务器开始,该主机的内部 IP 地址为 192.168.0.254,外部 IP 地址为 192.0.2.155。

OpenVPN 可以在 CentOS 和 Ubuntu 上运行,并且可以通过正常的包管理方法安装。在 CentOS 上,您将从 EPEL 库获得最新的 OpenVPN 包。Ubuntu 目前落后几个小版本。

要查看您的操作系统上当前可用的版本,请发出以下命令之一:

$ sudo aptitdue show openvpn (Ubuntu)
$ sudo yum info openvpn (CentOS)

在 CentOS 上,我们首先安装 EPEL 存储库(如果尚未安装),然后安装软件包。

$ sudo yum install epel-release
$ sudo yum install openvpn

在 Ubuntu 上,你安装openvpn包,一般也会安装一些额外的先决条件。

$ sudo aptitude install openvpn

现在让我们看看停止和启动服务。

启动和停止 OpenVPN

OpenVPN 在您的主机上作为服务运行。Ubuntu 和 CentOS 上的openvpn包将安装适当的 Systemd 服务脚本。

然而,对于 OpenVPN,我们启动和停止服务的方式略有不同。以 CentOS 为例,我们需要传递想要启动的 OpenVPN 配置的名称。systemctl命令可以接受参数。查看 OpenVPN 的 Systemd 服务文件,您会看到以下内容:

ExecStart=/usr/sbin/openvpn --daemon --writepid /var/run/openvpn/%i.pid --cd /etc/openvpn/ --config %i.conf

你注意到%i了吗?我们需要在启动openvpn服务时指定配置文件(--config %i.conf)。我们使用带有@符号的systemctl命令来完成这项工作。

$ sudo systemctl start service@configuration

systemctl命令将把%i.conf外推至我们在openvpn@之后传入的配置名。在下面的命令中,我们将开始在/etc/openvpn/gateway.conf中定义的配置:

$ sudo systemctl start openvpn@gateway

您使用 Systemd enable命令来确保它在启动时启动。

$ sudo systemctl enable openvpn@gateway

在 Ubuntu 上,我们只需运行以下命令:

$ sudo systemctl start openvpn

当我们启动 OpenVPN 时,这些配置服务文件将被自动加载,服务器将尝试启动指定的 VPN。

配置 OpenVPN

正如我们前面提到的,我们需要在任何连接的两端配置 OpenVPN。我们将通过在总部的堡垒主机上设置 OpenVPN 服务器来开始我们的配置,然后我们将配置到分公司的连接。像这样的两个办公室之间的连接被称为静态或点对点 VPN。最后,我们将向您展示如何为移动用户配置客户端,例如笔记本电脑或台式机。

我们建议的 VPN 配置

让我们快速查看一下我们提议的 VPN 隧道配置的网络图(见图 15-2 )。我们将在总公司分部和每个分部之间建立隧道。

A185439_2_En_15_Fig2_HTML.jpg

图 15-2。

Point-to-point VPN configuration between the head office and branches

在我们的网关服务器上配置 OpenVPN

我们已经在我们总公司的堡垒服务器gateway.example.com上安装了 OpenVPN,现在我们将开始配置它。我们首先告诉 OpenVPN 一些关于我们配置的基础知识。我们将在/etc/openvpn目录下创建一个名为gateway.conf的文件,如清单 15-1 所示。

# Network configuration
dev tun
port 1194
proto udp
server 10.8.0.0 255.255.255.0
keepalive 10 120

# Logging configuration
log-append /var/log/openvpn.log
status /var/log/openvpn-status.log
verb 4
mute 20

# Security configuration
user nobody
group nobody
persist-key
persist-tun

# Compression
comp-lzo

Listing 15-1.The gateway.conf Configuration File

Note

我们将在继续进行的过程中用额外的选项扩展gateway.conf文件。

在清单 15-1 中,我们指定了许多选项。第一个选项是dev tun,即隧道设备。tun 设备是内核中用户空间程序可以使用的特殊软件网络接口。调谐器设备将接收原始 IP 数据包,其姊妹设备 tap 期待以太网帧。

我们将配置一个 tun 来运行我们的 VPN,我们将创建一个虚拟设备tun0,它将用于 VPN 连接。通过指定dev tun,我们还创建了一个路由 VPN。OpenVPN 可以创建两种类型的 VPN:桥接和路由。简单地说,桥接 VPN 在以太网级别将您的网络连接在一起,而路由 VPN 依靠 TCP/IP 网络将您的网络连接在一起。我们将实现一个路由 VPN(这是 tun 设备的原因),因为这些类型使用 IP 协议,它更具可扩展性,更适合我们的网络需求。

Note

如果您有兴趣阅读更多关于路由 VPN 和桥接 VPN 的区别,您可以在 http://openvpn.net/index.php/documentation/howto.html#vpntype 找到更多信息。有关 taps 或 tuns 的更多信息,请访问 https://www.kernel.org/doc/Documentation/networking/tuntap.txt

接下来,我们指定了两个选项,port 1194proto udp。它们告诉 OpenVPN 在建立 VPN 连接时监听端口 1194 上的 UDP 流量。需要将防火墙配置为允许此端口上的流量接受传入的连接。

Tip

如果需要,您可以通过将这些选项更改为所需的端口和协议来更改端口并使用 TCP。

使用 Netfilter 防火墙和iptables命令,您可以确保流量通过主机上适当的端口,如清单 15-2 所示。

$ sudo firewall-cmd --zone public --permanent --add-port=1194/udp && sudo firewall-cmd --reload
Listing 15-2.OpenVPN Firewall Rules CentOS

这里我们添加了一条规则,允许传入的 UDP 流量到达端口 1194 上的tun0接口。

或者,如果您使用 TCP,您可以使用如下规则:

$ sudo firewall-cmd --zone public --permanent --add-port=1194/tcp && sudo firewall-cmd –reload

当然,对于 Ubuntu 来说是这样的:

$ sudo ufw allow 1194/udp

Tip

VPN 配置失败的最常见原因是防火墙问题。您应该使用tcpdumpnmap命令检查您的防火墙规则是否允许访问该端口。我们将在第七章中讨论防火墙的设置、规则和故障排除。

接下来在清单 15-1 中,服务器选项告诉 OpenVPN 服务器的 IP 地址和可供我们的 VPN 客户端使用的 IP 地址池。我们已经指定了默认网络 10.8.0.0/24。默认情况下,我们的 OpenVPN 服务器将采用地址 10.8.0.1,并将剩余的地址分配给传入的 VPN 连接。

我们已经指定了一个名为keepalive的选项,用于保持我们的连接打开。我们指定了keepalive选项和两个值,10120。值10表示 OpenVPN 将每 10 秒 ping 一次连接,以检查它是否处于活动状态。值120表示 OpenVPN 等待响应的时间(秒)。如果在 120 秒内没有收到响应,那么 OpenVPN 将假定连接断开并通知我们。

然后我们添加了一些日志功能。第一个是log-append,它告诉 OpenVPN 记录到/var/log/openvpn.log文件。第二个选项status,输出一个状态文件,显示当前到日志文件的连接,在我们的例子中是/var/log/openvpn-status.log。第三个选项,verb,告诉 OpenVPN 要做多少日志记录。范围从 0 到 15,其中 0 表示不记录日志,15 表示最大记录日志。值为 4 会生成足够的日志来满足大多数目的。最后,mute减少来自同一类别的连续消息。

下一组配置选项为我们的 OpenVPN 服务器提供了一些额外的安全性。前两个选项usergroup允许我们为 OpenVPN 指定一个用户和组来运行。这将放弃该进程拥有的任何权限(例如来自root用户的权限),并确保如果有人危害该进程,他将拥有在您的主机上利用该危害的有限权限。在 CentOS 上,您可以使用nobody用户和nobody组。在 Ubuntu 上,我们推荐你使用用户nobody和组nogroup

user nobody
group nogroup

接下来的两个选项persist-tunpersist-key与特权的放弃有关。它们允许 OpenVPN 保留足够的权限来使用网络接口和 SSL 证书。

文件中的最后一个选项comp-lzo告诉 OpenVPN 对 VPN 隧道使用压缩。这提高了隧道的性能。

我们已经配置了 VPN 的基础,但是现在准备好了吗?还没有——我们还有一个步骤:认证。

配置 OpenVPN 身份验证

身份验证确保只有经过授权的主机才能启动 VPN 并进行连接。OpenVPN 允许多种认证机制,包括预共享密钥、双因素认证(如令牌)和 TLS/SSL 证书。

最基本的身份验证是预共享密钥,这是在您的主机上生成的静态密钥,然后分发到您想要连接的主机。您可以使用openvpn命令的--genkey选项生成一个静态密钥,如下所示:

$ sudo openvpn --genkey --secret /etc/openvpn/secret.key

这将在目录/etc/openvpn中一个名为secret.key的文件中创建一个包含密钥的文件,文件名和目录由--secret选项指定。您可以使用secret选项在 OpenVPN 配置中指定该文件的位置。

secret /etc/openvpn/secret.key

然后你可以复制这个文件,最好是以一种安全的方式,比如通过scp或 GPG 加密的文件共享(就像 https://keybase.io 提供的),或者通过加密和使用一个配置管理系统,并将其应用到其他主机的 OpenVPN 配置。

不过,我们不打算使用预共享密钥,因为它们有一些限制。它们最大的限制是只能有一个服务器和客户端连接(即,每个 VPN 隧道只能连接一个主机)。如果您想要连接多台主机或办公室,这不是一个理想的模式。例如,允许多个移动用户连接到您的总部是无效的。

相反,我们将使用证书来保护总公司和分公司之间的 VPN。要使用证书,我们需要创建证书并由证书颁发机构(CA)签名。另一种方法是使用加密或商业证书。商业证书可能会变得昂贵,让我们加密证书需要经常更新。我们将使用我们自己的 CA,因为它更便宜,而且我们可以管理更新周期。

Note

我们在第十一章中详细讨论了 CA 过程(如果你愿意,现在就参考那里)。

我们将向您展示如何使用我们在第十一章中创建的 CA 来创建和签署您自己的 CA。

我们首先需要为 VPN 服务器创建一个服务器证书。您应该记得,创建新证书过程的第一步是创建证书签名请求(CSR)和密钥。现在让我们通过首先切换到/etc/pki/tls目录并执行以下命令来实现这一点:

$ openssl req -new -newkey rsa:4096 -nodes -keyout private/gateway.example.com.key
-out gateway.example.com.req

我们已经在/etc/pki/tls/private目录中生成了一个 4096 位的 RSA 密钥,并创建了一个 CSR。系统将提示您填写必填字段(州、城市等。).您需要使用与您的证书颁发机构相同的值。对于 Common Name 字段,您应该指定服务器的完全合格的域名,在我们的例子中是 gateway.example.com。

我们需要登录到管理我们的 CA 的主机,签署我们的 CSR 请求并生成我们的公共证书。我们需要将 CSR 复制到 CA 主机,然后运行清单 15-3 中所示的命令。

$ cd /etc/pki/CA
$ sudo openssl ca -out gateway.example.com.cert -cert certs/cacert.pem -infiles gateway.example.com.req
Using configuration from /etc/pki/tls/openssl.cnf
Enter pass phrase for /etc/pki/CA/private/cakey.pem:
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Oct 22 11:47:26 2016 GMT
            Not After : Oct 22 11:47:26 2017 GMT
        Subject:
            countryName               = AU
            stateOrProvinceName       = Victoria
            organizationName          = Example Inc
            organizationalUnitName    = IT
            commonName                = gateway.example.com
            emailAddress              = admin@example.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                A6:A4:16:17:32:D2:7B:03:D2:5C:5A:DE:85:29:51:BE:E4:73:EA:20
            X509v3 Authority Key Identifier:
                keyid:98:3E:03:EB:FF:8A:FF:E8:1A:BC:56:04:CA:BE:BC:DB:D2:FA:68:12

Certificate is to be certified until Oct 22 11:47:26 2017 GMT (365 days)
Sign the certificate? [y/n]:y

1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

Listing 15-3.Signing Our Server Certificate

首先,在我们的 CentOS CA 服务器上,我们切换到我们的/etc/pki/CA目录,然后运行openssl ca命令来签署我们的 CSR。这会输出一个由我们的 CA 签名的证书。我们现在有了我们的公共证书文件,gateway.example.com.cert。我们需要将这个文件复制到网关主机上的/etc/pki/tls/certs目录中。我们还需要复制 CA 根证书。

为此,我们在 CA 服务器和gateway服务器上都打开了 OpenSSH shell。然后我们使用cat程序将 public 和 cacert 证书打印到屏幕上。对于每个文件,我们将它们复制并粘贴到以下目录的gateway.example.com.certcacert.pem文件中:

[ca.example.come] $ sudo cat gateway.example.com.cert
[gateway.example.com] $ sudo vi /etc/pki/tls/certs/gateway.example.com.cert
[ca.example.come] $ sudo cat certs/cacert.pem
[gateway.example.com] $ vi /etc/pki/tls/certs/cacerts.pem

然后在gateway主机上,我们确保将私钥移动到正确的目录。

[gateway.example.com] $ sudo mv gateway.example.com.key /etc/pki/tls/private/gateway.example.com.key

在前面几行中,我们已经将 TLS 证书复制到了我们的gateway主机上的正确位置,包括 CA 的根证书cacert.pem。另外,gateway.example.com.key应该在我们的gateway.example.com主机上,并被移到适当的位置。

Note

除了私钥和证书文件,您还有一个 CSR 请求文件。当你的证书过期时,值得保留它。正如我们在第十一章中提到的,您可以再次使用这个请求来创建一个新的证书。

我们希望用正确的所有权和对密钥的一些受限权限来保护我们的证书和密钥。

$ sudo chown root:root /etc/pki/tls/{private,certs}/gateway.example.com.*
$ sudo chmod 0400 /etc//pki/tls/private/gateway.example.com.key

我们还需要创建一些 Diffie-Hellman 参数,即增强 VPN 会话安全性的加密参数。这仅在服务器上。该密钥用于生成公钥,通信方使用该公钥来生成双方将用于通信的共享秘密。(你可以在 https://wiki.openssl.org/index.php/Diffie_Hellman 阅读关于迪菲-海尔曼更详细的内容)。我们可以使用openssl命令来创建这些。

$ sudo openssl dhparam -out /etc/openvpn/dh2048.pem 2048
Generating DH parameters, 2048 bit long safe prime, generator 2
This is going to take a long time
.........................................+..........................................

这里我们在/etc/openvpn目录中创建了一个名为dh2048.pem的文件。这是一个 2048 位的 DH 参数文件,我们已经使用2048选项指定了大小。在几年内,这将是一个足够安全的密钥大小,但是如果您想要更长时间地防止将来被破解,您可以将它增加到 4,096。

我们要快速做的另一件事是创建一个tls-auth键。这也用于通过使用预共享密钥为 TLS 通道提供更高的安全性。此 PSK 应该部署到所有服务器和客户端。

openvpn --genkey --secret /etc/openvpn/ta.key

现在我们告诉 OpenVPN 我们的新证书以及我们的 CA 证书和 DH 参数文件的位置(参见清单 15-4 )。

# Network configuration
dev tun
port 1194
proto udp
server 10.8.0.0 255.255.255.0
keepalive 10 120

# Certificate configuration
ca /etc/pki/tls/certs/cacert.pem
dh /etc/openvpn/dh2048.pem
cert /etc/pki/tls/certs/gateway.example.com.cert
key /etc/pki/tls/private/gateway.example.com.key
tls-auth ta.key 0

# Logging configuration
log-append /var/log/openvpn.log
status /var/log/openvpn-status.log
verb 4
mute 20

# Security configuration
user nobody
group nobody
persist-key
persist-tun

# Compression
comp-lzo

Listing 15-4.The gateway.conf Configuration File

您可以看到我们增加了四个选项。第一个是ca选项,指定 CA 证书的位置,在我们的例子中是/etc/pki/tls/certs/cacert.pem。下一个是dh,指定我们创建的 Diffie-Hellman 参数文件的位置,在我们的例子中是/etc/openvpn/dh2048.pem。最后,我们使用了certkey选项来分别指定我们的证书和密钥文件的位置,在我们的例子中是/etc/pki/tls/certs/gateway.example.com.cert/etc/pki/tls/private/gateway.example.com.key

需要在服务器上将tls-auth键指定为tls-auth <key> 0,在客户端指定为tls-auth <key> 1。我们需要将tls-auth密钥复制给所有客户端。

我们可以如下启动我们的 OpenVPN 服务器:

$ sudo systemctl start openvpn@gateway
$ sudo systemctl status openvpn@gateway.service
● openvpn@gateway.service - OpenVPN Robust And Highly Flexible Tunneling Application On gateway
   Loaded: loaded (/usr/lib/systemd/system/openvpn@.service; enabled; vendor preset: disabled)
   Active: active (running) since Sat 2016-10-22 22:37:09 UTC; 1h 39min ago
  Process: 7779 ExecStart=/usr/sbin/openvpn --daemon --writepid /var/run/openvpn/%i.pid --cd /etc/openvpn/ --config %i.conf (code=exited, status=0/SUCCESS)
 Main PID: 7780 (openvpn)
...

您可以看到我们已经启动了 OpenVPN 服务器,并且您可以看到它何时启动了名为gateway.service的 VPN。VPN 以gateway.conf配置文件命名。

我们还可以在/var/log/openvpn.log文件中看到一些日志条目,如清单 15-5 所示。

Sat Oct 22 22:37:09 2016 us=712503 OpenVPN 2.3.12 x86_64-redhat-linux-gnu [SSL (OpenSSL)] [LZO] [EPOLL] [PKCS11] [MH] [IPv6] built on Aug 23 2016
Sat Oct 22 22:37:09 2016 us=712511 library versions: OpenSSL 1.0.1e-fips 11 Feb 2013, LZO 2.06
Sat Oct 22 22:37:09 2016 us=724495 Diffie-Hellman initialized with 2048 bit key
Sat Oct 22 22:37:09 2016 us=724919 TLS-Auth MTU parms [ L:1542 D:1212 EF:38 EB:0 ET:0 EL:3 ]
Sat Oct 22 22:37:09 2016 us=724982 Socket Buffers: R=[212992->212992] S=[212992->212992]
Sat Oct 22 22:37:09 2016 us=725172 ROUTE_GATEWAY 10.0.2.2/255.255.255.0 IFACE=eth0 HWADDR=52:54:00:c5:83:ad
Sat Oct 22 22:37:09 2016 us=733863 TUN/TAP device tun0 opened
Sat Oct 22 22:37:09 2016 us=733929 TUN/TAP TX queue length set to 100
Sat Oct 22 22:37:09 2016 us=733946 do_ifconfig, tt->ipv6=0, tt->did_ifconfig_ipv6_setup=0
Sat Oct 22 22:37:09 2016 us=733963 /usr/sbin/ip link set dev tun0 up mtu 1500
Sat Oct 22 22:37:09 2016 us=740224 /usr/sbin/ip addr add dev tun0 local 10.8.0.1 peer 10.8.0.2
Sat Oct 22 22:37:09 2016 us=745857 /usr/sbin/ip route add 10.8.0.0/24 via 10.8.0.2
Sat Oct 22 22:37:09 2016 us=755742 Data Channel MTU parms [ L:1542 D:1450 EF:42 EB:143 ET:0 EL:3 AF:3/1 ]
Sat Oct 22 22:37:09 2016 us=756240 GID set to nobody
Sat Oct 22 22:37:09 2016 us=756256 UID set to nobody
Sat Oct 22 22:37:09 2016 us=756265 UDPv4 link local (bound): [undef]
Sat Oct 22 22:37:09 2016 us=756270 UDPv4 link remote: [undef]
Sat Oct 22 22:37:09 2016 us=756279 MULTI: multi_init called, r=256 v=256
Sat Oct 22 22:37:09 2016 us=756297 IFCONFIG POOL: base=10.8.0.4 size=62, ipv6=0
Sat Oct 22 22:37:09 2016 us=756313 Initialization Sequence Completed
Listing 15-5.The /var/log/openvpn.log Log File

清单 15-5 显示 OpenVPN 服务器已经启动,我们的接口tun0被创建,我们的 IP 地址 10.8.0.1 被添加并绑定到 UDP 端口 1194。

Note

确保您有正确的防火墙规则来允许 VPN 连接到您的主机,在这种情况下,需要接受 UDP 端口 1194 上的传入连接。

我们的服务器正在运行,但我们还没有完全完成。我们需要配置我们的客户端,安装 OpenVPN 软件,并为我们的客户端创建用于连接的证书。

Note

在本章中,我们将提到两台主机。当一个命令需要在一个特定的主机上运行时,我们将为它加上与该主机相同的前缀:[ca]\(、[`gateway]\)或[branch1]$`。

在我们的分支机构服务器上配置 OpenVPN

首先,我们需要在我们的分支服务器branch1.example.com上安装 OpenVPN 包。遵循适用于您的发行版的说明,并将 OpenVPN 设置为在您的主机启动时启动,例如:

[branch1]$ sudo systemctl enable openvpn@branch1

Note

通过命令前缀[branch1]$,可以看出我们正在branch1.example.com主机上运行前面的命令。

下一步是为我们的每个分支机构创建一个证书和密钥。我们将在ca.example.com主机上创建我们的证书和密钥,并使用该主机上的 CA 对其进行签名。我们将从一个分支机构开始,branch1.example.com

[ca]$ openssl req -new -newkey rsa:4096 -nodes -keyout branch1.example.com.key
-out branch1.example.com.req

我们已经生成了一个 4,096 位 RSA 密钥并创建了一个 CSR。系统将提示您填写必填字段:州、城市等等。您需要使用与您的证书颁发机构相同的值。对于 Common Name 字段,您应该指定服务器的完全合格的域名,在我们的例子中是branch1.example.com

然后,我们使用我们的 CA 来签署我们的证书,就像前面所做的那样。

[ca]$ cd /etc/pki/CA
[ca]$ sudo openssl ca -out branch1.example.com.cert -cert certs/cacert.pem
-infiles branch1.example.com.req

现在,我们需要将我们的证书和密钥以及 CA 证书发送到分支服务器。有很多方法可以做到这一点,但是你必须安全地做——例如,不要用电子邮件发送,因为它们很容易被拦截。将它们放在 USB 密钥上,添加到您的配置管理系统(加密),并在目标服务器/客户机上本地安装它们。或者,如果您能够直接连接到主机,请使用scp(安全复制)或sftp(安全 FTP)命令发送文件。这些命令使用 SSH 连接安全地连接到另一台主机并传输文件。

在本例中,我们将使用sftp命令连接到我们的分支机构服务器并传输所需的文件。

[ca]$ sftp jsmith@branch1.example.com
Connecting to branch1.example.com...
jsmith@branch1.example.com's password:
sftp> put branch1.example.com.cert
Uploading /home/jsmith/branch1.example.com.cert to
/home/jsmith/branch1.example.com.cert
/home/jsmith/branch1.example.com.cert 100%    5881    12.0KB/s    00:03

我们已经连接到了branch1.example.com主机,并使用put命令将branch1.example.com.cert证书文件从它的位置(这里是/home/jsmith directory)传输到了branch1主机上的对等目录/home/jsmith。您可以使用cd命令切换到远程主机上您想要写入文件的适当目录。

sftp> cd /tmp

您将需要权限来写入该位置。如果您的文件位于其他地方,您可以使用cd命令来更改本地gateway.example.com主机上的目录。

我们使用put命令将branch1.example.com.keycacert.pem CA 证书文件放到branch1主机上。因为这是一个 Ubuntu 服务器,我们现在要把我们的文件移到/etc/ssl目录,并保护它们的所有权和权限。在 CentOS 上,我们将再次使用/etc/pki/tls目录。

[branch1]$ sudo mv branch1.example.com.cert /etc/ssl/certs/
[branch1]$ sudo mv branch1.example.com.key /etc/ssl/private/
[branch1]$ sudo mv cacert.pem /etc/ssl/certs/

我们希望用正确的所有权和对密钥的一些受限权限来保护我们的证书和密钥。

branch1$ sudo chown root:root /etc/ssl{certs,private}/branch1.example.com.*
branch1$ sudo chown root:root /etc/ssl/certs/cacert.pem
branch1$ sudo chmod 0400 /etc/ssl/private/gateway.example.com.key

接下来,我们需要为我们的客户机创建一个配置文件。我们将在我们的/etc/openvpn目录中创建一个名为branch1.conf的文件,如清单 15-6 所示。

# Network configuration
dev tun
client
remote gateway.example.com 1194
keepalive 10 120

# Certificate configuration
ca /etc/ssl/certs/cacert.pem
cert /etc/ssl/certs/branch1.example.com.cert
key /etc/ssl/private/branch1.example.com.key
tls-auth ta.key 1

# Logging configuration
log-append /var/log/openvpn.log
status /var/log/openvpn-status.log
verb 4
mute 20

# Security configuration
user nobody
group nogroup
persist-key
persist-tun

# Compression
comp-lzo

Listing 15-6.The branch1.conf Configuration File

Note

如果你使用的是 CentOS,你的group设置应该设置为nobody而不是nogroup

清单 15-6 中的文件类似于gateway.conf配置文件,但是我们指定了一些不同的选项,因为主机的角色是客户端。同样,我们已经为路由 VPN 指定了dev tun。我们还指定了client选项,它表明这是一个客户端,以及remote选项,它告诉 OpenVPN 在哪里连接我们的 VPN 隧道。我们已经指定了gateway.example.com和端口 1194。

Note

OpenVPN 必须能够解析该主机(即,它必须能够找到该主机的 IP 地址)。如果您没有 DNS(您应该有),那么您可以在此选项中直接指定一个 IP 地址。

我们还使用ca选项指定了我们的 CA 证书的位置,并分别使用certkey选项指定了我们的客户端证书和密钥的位置。

在我们的分支机构服务器上启动 OpenVPN

在我们的服务器上配置好 VPN 后,我们现在可以在我们的客户机上启动 OpenVPN。例如,我们在 CentOS 上使用以下命令:

[branch1]$ sudo systemctl start openvpn@branch1

测试我们的 OpenVPN 隧道

您可以确定您的连接是否以多种方式工作,我们将带您了解所有这些方式。首先,您应该在branch1主机上的/var/log/openvpn.log文件中看到一些条目。您应该会看到类似于清单 15-5 中的条目,但是您也会看到我们的客户端连接到服务器时的协商过程。

Sun Oct 23 06:24:58 2016 us=729639 [gateway.example.com] Peer Connection Initiated
 with [AF_INET]10.0.2.155:1194

Note

在这些示例中,您会看到不同的公共 IPv4 地址(10.0.2.155 和 10.0.2.156 ),它们与图 15-2 中的图表相匹配。

您还应该在显示连接的/var/log/openvpn.log文件中看到网关主机上的一些条目。

gateway$ less /var/log/openvpn.log
Sun Oct 23 06:25:06 2016 us=416152 branch1.example.com/10.0.2.156:1194 SENT CONTROL [branch1.example.com]: 'PUSH_REPLY,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5' (status=1)

此外,您可以看到在gatewaybranch1主机上都创建了一个新接口,从tun开始。在网关主机上,您可以看到一个名为tun0的新接口,其 IP 地址为 10.8.0.1(如前所述)。

[gateway]$ ip addr show tun0
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 100
    link/none
    inet 10.8.0.1 peer 10.8.0.2/32 scope global tun0
       valid_lft forever preferred_lft forever

branch1主机上,已经创建了一个接口,也称为tun0,其 IP 地址为我们服务器提供的地址池中的 10.8.0.6。

[branch1]$ ip addr show tun0
5: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 100
    link/none
    inet 10.8.0.6 peer 10.8.0.5/32 scope global tun0
       valid_lft forever preferred_lft forever

您还可以看到branch1主机上的路由表(使用第七章中介绍的ip命令)有一条到我们 10.8.0.0/24 网络的路由。

branch1$ ip route show
sudo ip route show
default via 10.0.2.2 dev enp0s3
10.0.2.0/24 dev enp0s3  proto kernel  scope link  src 10.0.2.15
10.8.0.1 via 10.8.0.5 dev tun0
10.8.0.5 dev tun0  proto kernel  scope link  src 10.8.0.6
192.168.0.0/24 dev enp0s8  proto kernel  scope link  src 10.0.2.156

通过tun0接口有一条到 10.8.0.1 主机的路由。

您会注意到在两台主机上都创建了一个名为/var/log/openvpn-status.log的文件。该文件包含当前连接的列表,每 60 秒刷新一次。让我们看看网关主机上的这个文件。

gateway$ less /var/log/openvpn-status.log
OpenVPN CLIENT LIST
Updated,Sun Oct 23 06:44:43 2016
Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since
branch1.example.com,10.0.2.156:1194,16384,16774,Sun Oct 23 06:24:30 2016
ROUTING TABLE
Virtual Address,Common Name,Real Address,Last Ref
10.8.0.6,branch1.example.com,10.0.2.156:1194,Sun Oct 23 06:33:01 2016
GLOBAL STATS
Max bcast/mcast queue length,0
END

您可以看到从 IP 地址为 10.0.2.156 的branch1主机列出的一个连接。

最后,您可以使用网络工具来测试您的实际连接。让我们从在网关主机上使用ping命令 ping 我们的branch1主机开始。我们将 ping 用作我们在branch1主机上的 VPN 隧道末端的地址,我们之前发现该地址是 10.8.0.6。

[gateway]$ ping 10.8.0.6 -c 3
PING 10.8.0.6 (10.8.0.6) 56(84) bytes of data.
64 bytes from 10.8.0.6: icmp_seq=1 ttl=64 time=0.593 ms
64 bytes from 10.8.0.6: icmp_seq=2 ttl=64 time=0.588 ms
64 bytes from 10.8.0.6: icmp_seq=3 ttl=64 time=0.807 ms

前面的代码向我们展示了网关主机可以看到使用 ICMP 的branch1主机,并且该branch1主机上的 10.8.0.6 IP 地址响应 ICMP 流量。

我们可以通过在网关主机端 ping IP 地址 10.8.0.1,从branch1主机做同样的事情。

branch1$
ping 10.8.0.1 -c 3
PING 10.8.0.1 (10.8.0.1) 56(84) bytes of data.
64 bytes from 10.8.0.1: icmp_seq=1 ttl=64 time=0.682 ms
64 bytes from 10.8.0.1: icmp_seq=2 ttl=64 time=0.672 ms
64 bytes from 10.8.0.1: icmp_seq=3 ttl=64 time=0.751 ms

如果两端都有响应,那么您的 VPN 就开通了,您可以使用它将流量从分公司路由到总公司。

如果现在需要,您可以为任何其他分支机构重复此配置。例如,在我们的例子中,我们可以添加来自branch2.example.combranch3.example.com办公室的隧道。

使用 OpenVPN 暴露总部资源

根据目前的配置,我们可以通过 VPN 隧道在分公司和总部之间路由流量。让我们看看我们的总公司和分公司现在是如何互动的。

再看一下图 15-2 。目前,从我们的分支机构到我们的总部有两条路径。第一个是通过你在第七章中看到的 10.0.2.0/24 网络。这是我们办公室与互联网之间的 DSL 或 ASDL(或类似类型)互联网连接。每个单独的办公室通常都有单独的互联网连接。我们使用 10.0.2.0/24 网络作为他们的 IP 地址,但更有可能的情况是,每个办公室都有一个从 ISP 处获得的地址。

这个网络不安全,因为它是在互联网上运行的。除非我们保护特定的应用程序或协议(例如,在第十二章中我们使用 SSL/TLS 来保护我们的 SMTP 和 IMAP 服务),否则攻击者可以从该网络读取我们的数据。由于这一潜在的安全问题,我们通过此连接实例化了这些主机之间的第二条路径,即我们用于 VPN 隧道的 10.8.0.0/24 网络。我们的总部是 OpenVPN 服务器,IP 地址为 10.8.0.1。每个分支办公室都有一个该范围内的 IP 地址,例如,您已经看到了分配给branch1.example.com办公室的 10.8.0.6 IP 地址。

然而,目前通过我们的 VPN 隧道,我们只能到达 IP 地址 10.8.0.1。这对我们没有多大用处,因为我们无法访问内部网络上的任何可用资源(例如,headoffice.example.com主机上的文件共享)。我们可以通过试着 pingheadoffice主机,从我们的分支机构主机测试这一点:

branch1$ ping 192.168.0.1

我们不会从这些 pings 得到任何回复,因为没有到这个网络的路由。要访问这些资源,我们需要确保两个要素有序:路由和防火墙规则。我们将在接下来的章节中讨论这些。

选择途径

我们首先需要配置我们的分支机构,以路由到我们总部的内部网络。为此,我们告诉我们的分支机构,当它想要路由到 192.168.0.0/24 网络时,它需要通过 VPN 隧道到达网关主机。如清单 15-7 所示,我们通过在网关主机上的gateway.conf配置文件中添加一行来将路由推送到我们的分支主机。

push "route 192.168.0.0 255.255.255.0"
Listing 15-7.Push Route Added to gateway.conf

该行向连接到 OpenVPN 服务器的所有客户端添加一个路由。对于要推送的新路由,我们需要在网关主机和分支机构主机上重启openvpn服务。

如果我们查看我们的branch1主机上的路由表,我们可以在我们的路由中看到一条到 192.168.0.0/24 网络的新路由(粗体)。

[branch1]$ ip route
default via 10.0.2.2 dev eth0  proto static  metric 100
10.0.2.0/24 dev eth0  proto kernel  scope link  src 10.0.2.15  metric 100
10.8.0.1 via 10.8.0.5 dev tun0
10.8.0.5 dev tun0  proto kernel  scope link  src 10.8.0.6
169.254.0.0/16 dev eth1  scope link  metric 1003
10.0.2.0/24 dev eth1  proto kernel  scope link  src 10.0.2.156

192.168.0.0/24 via 10.8.0.5 dev tun0

192.168.10.0/24 dev eth1  proto kernel  scope link  src 192.168.10.254

我们现在可以从branch1主机 ping 这个网络。

branch1$ ping 192.168.0.1
PING 192.168.0.1 (192.168.0.1) 56(84) bytes of data.
64 bytes from 192.168.0.1: icmp_seq=1 ttl=64 time=1.18 ms
64 bytes from 192.168.0.1: icmp_seq=2 ttl=64 time=1.31 ms
64 bytes from 192.168.0.1: icmp_seq=3 ttl=64 time=2.33 ms
64 bytes from 192.168.0.1: icmp_seq=4 ttl=64 time=1.25 ms
64 bytes from 192.168.0.1: icmp_seq=5 ttl=64 time=0.923 ms

您可以看到,我们的 branch1 主机收到了来自主机 192.168.0.1 的响应。我们可以访问网关主机上的防火墙允许我们访问 192.168.0.0/24 网络的任何内容。

如果branch1主机是 192 . 168 . 10 . 0/24(branch1站点的本地网络)的默认路由,那么 192.162.10.0/24 网络中的所有用户都将能够访问我们总部的 192.168.0.0/24 网络中的资源。

Note

我们不会告诉我们的总部如何路由到我们分支机构的内部网络,但这也是可能的。

防火墙

我们还需要确保网关和分支机构主机上的防火墙规则允许流量进出相关网络。在我们的网关主机上,这涉及到将 IP 流量从我们的网关主机转发到内部网络并返回,很像我们在第七章中创建的配置,用于从我们的堡垒主机gateway.example.com转发我们的服务。

首先,我们使用公共区域从 VPN 隧道接口tun0引导我们希望通过网关主机转发的流量。我们将把tun0接口添加到public区域。

[gateway]$ sudo firewall-cmd --zone public --permanent --add-interface tun0

然后,我们需要为这个区域创建一个规则集,以允许我们的网关主机通过该主机转发特定的流量,如清单 15-8 所示。

[gateway]$ sudo firewall-cmd --zone public --permanent --add-service=http
success
[gateway]$ sudo firewall-cmd --zone public --permanent --add-service=https
success
[gateway]$ sudo firewall-cmd --zone public --permanent --add-service=smtp
success
[gateway]$ sudo firewall-cmd --zone public --permanent --add-service=dns
success
[gateway]$ sudo firewall-cmd --zone public --permanent --add-service=ntp
success
[gateway]$ sudo firewall-cmd --zone public --permanent --add-service=imaps
success
Listing 15-8.Some Sample firewalld Rules for OpenVPN Routing

现在我们必须重新加载防火墙配置。

[gateway]$ sudo firewall-cmd --reload

我们创建了各种简单的规则来允许流量通过 VPN 隧道,并将其转发到我们的内部网络。我们转发了 SMTP、HTTP/HTTPS 和 IMAP 等协议。

Note

您可以使用第七章中提供的说明将这些规则添加到您的网关主机中。

移动用户的 VPN 连接

OpenVPN 不仅仅允许从分支机构到总部的点对点连接。您还可以使用它来允许移动用户连接到您的总部并访问文件共享、打印机和应用程序等资源。为此,我们需要在网关主机上设置另一个 VPN 隧道,并在客户机上安装 OpenVPN。正如我们在本章前面提到的,OpenVPN 可以运行在包括 Linux、Microsoft Windows、macOS 等平台上。

我们将对我们的移动用户做一些不同的事情。我们不打算使用证书(尽管我们可以使用它们)来认证我们的客户端,因为潜在的生成大量证书的开销可能相当高。

Note

利用 OpenVPN 使证书管理更容易的一个工具是easy-rsa。文档可以在这里找到: https://openvpn.net/index.php/open-source/documentation/miscellaneous/77-rsa-key-management.html

相反,我们将向您展示如何使用 PAM(我们在第五章中介绍过)来认证您的用户。由于 PAM 能够插入各种身份验证机制,我们可以使用它来包括如下身份验证机制:

  • 本地 Linux 用户
  • 二元身份认证,如 RSA 令牌或智能卡
  • 麻省理工学院开发的安全认证系统
  • 半径
  • IMAP(针对 IMAP 服务器)
  • 轻量级目录访问协议

我们将向您展示如何配置基本的本地用户身份验证,也就是说,您的用户将在网关主机上拥有一个 Linux 用户,他们将通过身份验证,就像使用控制台或通过 SSH 登录到该主机一样。使用 PAM,您可以很容易地将其扩展到其他形式的身份验证。

配置我们的移动 VPN

要开始我们的配置,我们需要一个新的.conf文件。我们将在/etc/openvpn中创建一个名为mobile.conf的,如清单 15-9 所示。

# Network configuration
dev tun
port 1195
proto udp
server 10.9.0.0 255.255.255.0
keepalive 10 120

# Certificate configuration
dh /etc/openvpn/dh2048.pem
ca /etc/pki/tls/certs/cacert.pem
cert /etc/pki/tls/certs/gateway.example.com.cert
key /etc/pki/tls/private/gateway.example.com.key
tls-auth ta.key 0

plugin /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so login

# Logging configuration
log-append /var/log/openvpn-mobile.log
status /var/log/openvpn-status-mobile.log
verb 4
mute 20

# Security configuration
user nobody
group nobody
persist-key
persist-tun

# Compression
comp-lzo

Listing 15-9.Mobile User’s mobile.conf Configuration File

Note

在本节的后面,当我们查看在我们的客户机上配置路由和相关功能时,我们将扩展这个配置。

你可以看到我们的mobile.conf配置类似于我们的gateway.conf VPN 隧道。我们将两者分开,以便机器使用 1194 上的服务器,漫游用户使用 1195,这将通过他们的用户名和密码得到进一步的保护。

让我们把重点放在不同点上。我们更改了一些网络配置:我们使用了不同的端口 1195,因为我们的另一个 VPN 隧道绑定到 1194 端口。管理员为移动用户使用 TCP 协议和端口 443 是很常见的;这是因为像 1195 或 1194 这样的端口可以在机场这样的地方被阻塞。我们还为移动用户指定了一个额外的 IP 子网 10.9.0.0/24。

我们需要确保为该子网制定合适的防火墙规则。首先,我们在tun1接口上为我们的隧道打开 1195 端口(对于我们的新隧道,接口号已经增加)。

[gateway]$ sudo firewall-cmd --zone public --permanent --add-port=1195/udp

Note

我们的新接口将是tun1,因为我们已经有了一个tun0接口。如果你没有另一个 VPN 隧道,那么你的接口可能是tun0

我们指定了相同的证书、密钥、tls-auth和 DH 参数,但是我们添加了一个名为plugin的新选项。plugin配置选项允许我们指定外部插件,在我们的例子中,OpenVPN 包附带了一个名为openvpn-pluging-auth-pam.so的 PAM 插件。

在 Ubuntu 上,插件位于/usr/lib/openvpn/openvpn-plugin-auth-pam.so

我们还需要指定一个 PAM 认证文件的名称,OpenVPN 将使用它来认证 VPN 隧道。在 Ubuntu 上,我们已经指定了标准的影子密码 PAM 认证文件passwd,作为要使用的认证机制。在 CentOS 上,我们可以指定system-auth默认 PAM 认证文件。

对于其他形式的身份验证,您可以在这里为该机制指定一个合适的 PAM 身份验证文件。例如,要启用双因素身份验证,您可以指定一个使用 Google Authenticator PAM 模块的文件pam_google_authenticator.so(参见 https://www.linux.com/learn/how-set-2-factor-authentication-login-and-sudo 以获取如何实现它的示例)。该模块允许您将 Google Authenticator 与 PAM 集成,以允许您提供一个令牌来认证用户。

我们已经更新了日志文件,以便为我们的移动连接创建新文件。我们还指定了usergroup选项(在本例中,我们使用了 CentOS 上的nobody组)。

接下来,我们需要重启 OpenVPN 服务来启动我们的移动 VPN 隧道。

配置移动 VPN 客户端

您需要配置您的客户端以连接到网关,根据客户端的不同,您可以通过多种方式来完成。有许多客户端可用,从普通的 OpenVPN 二进制程序到复杂的 GUI 客户端。在这一节中,我们将提供一些可用客户端的列表,并向您展示如何通过 OpenVPN 进行连接。

最简单的客户端是 OpenVPN 二进制。我们将为mobile1.example.com创建并签署一个密钥和证书,就像我们为其他 TLS 证书所做的那样。我们将把它们放在和以前相似的地方,或者在/etc/pki/tls目录中,或者在/etc/ssl目录中,这取决于我们的主机。

如果您使用 OpenVPN 二进制文件,您还需要创建一个客户端配置文件。我们将调用我们的mobileclient.conf并将它存储在客户端的/etc/openvpn目录中,如清单 15-10 所示。

# Network configuration
dev tun
client
remote gateway.example.com 1195
keepalive 10 120

# Certificate configuration
ca /etc/ssl/certs/cacert.pem
cert /etc/ssl/certs/mobile1.example.com.cert
key /etc/ssl/private/mobile1.example.com.key
tls-auth ta.key 1

auth-user-pass

# Logging configuration
log-append /var/log/openvpn.log
status /var/log/openvpn-status.log
verb 4
mute 20

# Security configuration
user nobody
group nobody

persist-key
persist-tun

# Compression
comp-lzo

Listing 15-10.The mobileclient.conf Configuration File

您可以看到我们之前使用的选项,其中增加了一个选项,并进行了一些小的更改。我们已经把要连接的远程端口改成了 1195。我们还添加了auth-user-pass选项,它告诉客户端我们将使用用户名和密码,而不仅仅是证书认证。

现在,如果我们在客户机上启动 OpenVPN,它将连接到网关,提示用户输入适当的凭证。

$ sudo /etc/init.d/openvpn restart
Shutting down openvpn                                      [ OK ]
Starting openvpn:
Enter Auth Username:jsmith
Enter Auth Password:********

请注意验证用户名和密码提示。我们已经在网关主机上输入了一个用户的用户名jsmith,以及他的密码。

然后客户端将会连接,您应该能够看到一个新的界面(在我们的例子中是tun1)。

$ ip addr show tun0
10: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 100
    link/none
    inet 10.8.0.6 peer 10.8.0.5/32 scope global tun0
       valid_lft forever preferred_lft forever

接口有网关主机下发的 IP 地址 10.9.0.6(记住,我们把我们的移动客户端 VPN 网络设置为 10.9.0.0/24)。然后,您可以 ping 网关主机的这个 IP 地址(在我们的例子中是 10.9.0.1 ),反之亦然,返回到 10.9.0.6。

$ ping 10.9.0.1
PING 10.9.0.1 (10.9.0.1) 56(84) bytes of data.
64 bytes from 10.9.0.1: icmp_seq=1 ttl=64 time=10.3 ms
64 bytes from 10.9.0.1: icmp_seq=2 ttl=64 time=10.64 ms
64 bytes from 10.9.0.1: icmp_seq=3 ttl=64 time=10.59 ms
64 bytes from 10.9.0.1: icmp_seq=4 ttl=64 time=10.73 ms
64 bytes from 10.9.0.1: icmp_seq=5 ttl=64 time=10.59 ms

OpenVPN 客户端配置文件

我们分发 OpenVPN 客户端配置的另一种方法是创建一个 OpenVPN 配置文件。这些可以通过 OpenVPN 访问服务器或其他方式分发。在本例中,我们将为我们的移动客户端创建一个.ovpn配置文件,将其上传到 iTunes 中的 OpenVPN 应用程序,并连接到 OpenVPN 服务器。

配置文件基本上与普通的 OpenVPN 配置文件相同。不同之处在于我们包括了 TLS 证书,包括私钥。以下是我们为个人资料创建的mobile1.ovpn文件:

client
proto udp
remote gateway.example.com
port 1195
dev tun
nobind
auth-user-pass
key-direction 1

<ca>
-----BEGIN CERTIFICATE-----
# TLS root ca
-----END CERTIFICATE-----
</ca>

<cert>
-----BEGIN CERTIFICATE-----
# TLS public certificate
-----END CERTIFICATE-----
</cert>

<key>
-----BEGIN PRIVATE KEY-----
# TLS private key
-----END PRIVATE KEY-----
</key>

<tls-auth>
-----BEGIN OpenVPN Static key V1-----
# tls-auth key
-----END OpenVPN Static key V1-----
</tls-auth>

现在我们需要在 iPhone 上安装来自 App Store 的应用程序(图 15-3 )。

A185439_2_En_15_Fig3_HTML.jpg

图 15-3。

Installing the app

现在我们需要通过 iTunes 将mobile1.ovpn文件添加到应用程序中(图 15-4 )。

A185439_2_En_15_Fig4_HTML.jpg

图 15-4。

Adding mobile1.ovpn to the OpenVPN client

完成后,我们就可以连接到 OpenVPN 服务器了。在应用程序中,我们使用绿色加号按钮添加个人资料。然后我们准备好签到。在图 15-5 中,我们输入jsmith的用户名和密码,然后切换连接按钮。

A185439_2_En_15_Fig5_HTML.jpg

图 15-5。

Entering the username and password

图 15-6 显示我们通过 iPhone 应用程序联系在一起。

A185439_2_En_15_Fig6_HTML.jpg

图 15-6。

Connected via the iPhone app

我们可以在网关机器上的服务器日志中确认这一点。

AUTH-PAM: BACKGROUND: received command code: 0
AUTH-PAM: BACKGROUND: USER: jsmith
AUTH-PAM: BACKGROUND: my_conv[0] query='Password: ' style=1
Mon Oct 24 09:50:48 2016 us=779104 118.209.127.108:55174 PLUGIN_CALL: POST /usr/lib64/openvpn/plugins/openvpn-plugin-auth-pam.so/PLUGIN_AUTH_USER_PASS_VERIFY status=0
Mon Oct 24 09:50:48 2016 us=779146 118.209.127.108:55174 TLS: Username/Password authentication succeeded for username 'jsmith' [CN SET]
...
<snip>
...
Mon Oct 24 09:50:48 2016 us=801096 118.209.127.108:55174 [jsmith] Peer Connection Initiated with [AF_INET]118.209.127.108:55174

我们已经完成了身份验证,现在我们的 iPhone 通过 OpenVPN 服务器连接到了本地网络。

由于许多网络工具将支持 OpenVPN,因此有各种平台的各种其他客户端可用。您也可以在此下载适用于大多数系统的客户端:

移动 VPN 路由

正如您可以创建点对点总公司到分公司 VPN 隧道一样,您也可以在您的客户端主机和网关之间执行各种路由配置。在这种情况下,您不仅要帮助客户端看到网关主机后面的主机,还要告诉客户端如何配置自己,尤其是它的 DHCP 设置。

您还将看到如何强制所有流量通过 VPN。这是一种通常用于确保用户流量只流向您的组织的方法。例如,它通常用于确保所有用户 web 流量通过您组织的代理,从而确保它符合您组织的可接受使用策略或类似标准。

首先,让我们的移动用户看到我们总部的 192.168.0.0/24 内部网络。我们通过将push选项添加到网关主机上的mobile.conf配置中来实现这一点,并使用它来推送路由。

push "route 192.168.0.0 255.255.255.0"

我们还需要更新网关主机上的防火墙规则,就像我们为分支机构到总部的隧道添加规则一样。我们为移动 VPN 隧道添加了另一个链。

[gateway]$ sudo firewall-cmd --zone public --permanent --add-interface tun1

现在我们已经重新加载了防火墙配置。

[gateway]$ sudo firewall-cmd --reload

我们创建了各种简单的规则来允许流量通过 VPN 隧道,并将其转发到我们的内部网络。我们转发了 SMTP、HTTP/HTTPS 和 IMAP 等协议。

Note

您可以使用第七章中提供的说明将这些规则添加到您的网关主机中。

我们还可以向我们的客户端传递各种选项,例如,帮助设置 DHCP 选项,如 DNS 和 WINS 服务器。

Note

我们在第十章中设置了 DHCP。

不同类型的客户端(例如,Linux 和 Microsoft Windows 客户端)需要不同的方法来下推所需的选项。当向 Microsoft Windows 客户端传递选项时,我们可以简单地使用 push 选项传递所需的选项。例如,要将 DNS 服务器 IP 地址推送到客户端,我们需要执行以下操作:

push "dhcp-option DNS 10.0.2.155"

在这里,我们告诉 OpenVPN 告诉微软 Windows 客户端将 DNS 服务器 10.0.2.155 推送到它的 DHCP 选项。

在微软 Windows 环境中,我们还可以下推各种其他选项,如表 15-1 所示。

表 15-1。

DHCP Options

| [计]选项 | 描述 | | --- | --- | | `DOMAIN`姓名 | 设置客户端的 DNS 后缀。 | | `DNS`地址 | 设置 DNS 服务器地址。重复设置辅助 DNS 服务器。 | | `WINS`地址 | 设置 WINS 服务器地址。重复设置辅助 WINS 服务器。 | | `NBDD`地址 | 设置 NBDD 服务器地址。重复设置辅助 NBDD 服务器。 | | `NTP`地址 | 设置 NTP 服务器地址。重复设置辅助 NTP 服务器。 | | `DISABLE-NBT` | 禁用 TCP/IP 上的 NetBIOS。 |

在 Linux 和其他主机上,您不能使用push选项直接设置这些选项。相反,你需要告诉 OpenVPN 在隧道到达updown时运行脚本。为此,您可以使用适当命名的updown选项。我们可以在 VPN 客户端的mobileclient.conf中添加以下选项:

up /etc/openvpn/tunnelup.sh
down /etc/openvpn/tunneldown.sh

每个选项都指定了当 VPN 隧道开启和关闭时将运行的脚本或命令。如果我们想设置客户机的 DNS 配置,我们可以使用一个 up 脚本tunnelup.sh,如下所示:

#!/bin/sh
mv /etc/resolv.conf /etc/resolv.conf.bak
echo "search example.org" > /etc/resolv.conf
echo "nameserver 10.0.2.155" >> /etc/resolv.conf exit 0

然后,我们可以使用一个 down 脚本tunneldown.sh来恢复我们的配置选项,如下所示:

#!/bin/sh
mv /etc/resolv.conf.bak /etc/resolv.conf

Note

你需要自己将这些脚本传输到客户端,或者使用配置管理工具,我们将在第十九章向你展示。

最后,我们可以强制所有流量从客户端流向 VPN。这通常用于强制用户遵守某些策略或标准,或者确保通过代理或病毒扫描程序对所有流量进行病毒和恶意软件扫描。

不过,让所有流量都通过隧道会有一些问题。最值得注意的是,将流量通过隧道推至您的办公室,然后推至互联网,会影响性能。对于客户端生成的所有流量,您还需要一个代理或 NAT 重定向,因为每个协议(不仅仅是 web 流量)都需要一种连接方式。

为了强制来自客户端的所有流量通过 VPN 隧道,我们将以下指令添加到网关主机上的mobile.conf配置文件中:

push "redirect-gateway def1"

如果您的 VPN 设置是通过无线网络进行的,并且所有客户端和服务器都在同一个无线网络上,则您需要将添加到此指令中。

push "redirect-gateway local def1"

你可以看到我们已经在指令中添加了local选项。

OpenVPN 故障排除

排除 OpenVPN 故障要求您考虑连接的所有要素:网络、防火墙和 OpenVPN 本身。OpenVPN 广泛的日志记录(你在本章前面看到了log-appendstatusverb选项)允许你快速发现错误。此外,OpenVPN 的错误信息通常会清楚准确地指出实际问题。

但是,您还需要确保检查您的网络连接和适当的防火墙规则,包括主机上的iptables规则和任何中间网络设备上的潜在规则,以允许连接。您需要检查连接是否正常,防火墙规则是否允许 VPN 隧道连接,以及是否存在允许流量通过 VPN 到达预期目的地的规则和路由。

Tip

第六章介绍网络和防火墙故障排除。

寻求故障排除帮助的最佳起点是 OpenVPN 网站( http://openvpn.net/ )。在那里您可以找到文档,包括一个全面的操作方法页面:

http://openvpn.net/index.php/documentation/howto.html

和一个常见问题页面:

http://openvpn.net/index.php/documentation/faq.html

您可以在此处找到 OpenVPN 的手册页:

https://openvpn.net/index.php/open-source/documentation/manuals.html

您也可以在此加入邮件列表:

openvpn.net/index.php/documentation/miscellaneous/mailing-lists.html

对于更复杂的实现,OpenVPN 开发人员提供商业支持,或者您可以求助于 Markus Feilner 所著的《OpenVPN:构建和集成虚拟专用网》(Packt Publishing,2006)一书。

摘要

在本章中,我们向您介绍了配置和管理 VPN 通道的过程。我们引入了点对点隧道,比如总部和远程分支机构之间的隧道。我们还解释了如何使用 VPN 隧道让您的移动用户安全可靠地连接到您的总部或其他位置的资源。您已经学会了如何执行以下操作:

  • 配置 VPN 隧道
  • 创建和配置用于身份验证的证书
  • 利用 PAM 允许替代形式的身份验证
  • 配置您的iptables防火墙以允许 VPN 隧道
  • 配置您的网络和路由,以允许用户穿越 VPN 隧道并访问资源
  • 使用 OpenVPN 在客户端上配置网络选项

在下一章,我们将讨论 LDAP 服务。

十六、目录服务

目录服务在主要的计算机网络中广泛存在。轻量级目录访问协议(LDAP)目录就是这种服务的一个例子。LDAP 目录是特殊的数据库,通常包含用户名、密码、常用名、电子邮件地址、业务地址和其他属性。组织首先使用目录服务来促进地址簿和用户信息的分发。从那时起,目录服务已经发展成为所有用户信息和身份验证服务的中央存储库。开发的应用程序能够根据目录服务进行身份验证,这进一步增强了它们在组织中的重要性。

在本章中,我们将向您展示如何安装和配置 OpenLDAP 服务器。我们还将讨论通过添加您自己的模式来扩展您的 OpenLDAP 目录服务器。我们将向您展示如何设计访问控制列表来保护您的安装,以及如何通过命令行工具和基于 web 的 GUI 来管理 LDAP 服务器。最后,您将看到如何将 LDAP 服务器与现有的网络和应用程序集成,包括实现单点登录服务和 Apache web 身份验证的能力。

目录服务的实现可能很复杂。虽然安装很简单,但要安全地配置它们通常很复杂。OpenLDAP 没有商业支持的版本,但即使是 OpenLDAP 邮件列表中最简单的问题,也会由该项目的高级工程师和设计师定期回答(这对他们的奉献精神是一个巨大的帮助和绝对的荣誉)。也就是说,在您开始安装之前,购买一本专门介绍该主题的书会对您有所帮助,从而加深您对该软件的理解。我们向您推荐以下内容:

  • 如果您需要专家支持,请查看技术支持页面: www.openldap.org/support/
  • 部署 OpenLDAP
  • Matt Butcher 的《掌握 OpenLDAP:配置、保护和集成目录服务》(Packt Publishing,2007)

Tip

OpenLDAP 网站在 www.openldap.org 也包含了很好的管理指南和常见问题解答。

概观

在这一章中,我们将探索 OpenLDAP 并把它作为一种认证服务。我们可以将它用作任何支持 LDAP 的身份验证机制的单点登录服务。它可以用来集中保存我们所有用户的身份信息,包括用户名、密码、电子邮件地址以及其他用户和组信息。在本章中,我们将带您了解以下内容:

  • 安装和设置 OpenLDAP 服务
  • 解释模式并创建一个属性,我们可以使用该属性作为过滤器来检查活动和非活动用户
  • 向我们的 LDAP 服务添加用户
  • 使用访问控制列表保护服务以保护敏感数据
  • 使用 LDAP 工具,如ldapmodifyldapaddldapsearch
  • 设置 web GUI 来管理 LDAP
  • 使用 SSSD 和 LDAP 执行单点登录
  • 使用 LDAP 实现 web 身份验证

您可以搜索、添加、修改、删除和验证 LDAP 服务中的条目。这些操作受访问列表的限制,不同的用户可以有不同的访问权限。在确定访问级别的初始阶段会发生一个身份验证过程,这称为绑定。这可以由一个用户代表另一个用户完成,也可以匿名完成,具体取决于您如何配置您的访问列表。完成后,我们就可以访问服务中的条目了。

请继续阅读,我们将解释什么是 LDAP,并让您了解构成 LDAP 服务的组件。

什么是 LDAP?

轻型目录访问协议用于访问从目录访问协议(DAP)派生的基于 X.500 的目录服务。X.500 是一组协议,概述了应该如何存储用户信息以及应该如何访问这些信息。LDAP 是由没有 TCP/IP 功能的目录访问协议产生的。

Note

有关 X.500 OSI 协议的更多信息,请参见 http://en.wikipedia.org/wiki/X.500

存在几种常见类型的目录服务,它们都是从 X.500 DAP OSI 模型派生出来的。一些常见的例子有:微软的活动目录、Red Hat 的目录服务和 Oracle 目录服务器企业版。

在这一章中,我们将集中讨论常用且健壮的 OpenLDAP 服务器。OpenLDAP 是从密歇根大学最初设计的原始项目中分出来的,现在通过 OpenLDAP 项目( www.openldap.org/project/ )的工程师和开发人员社区的工作继续进行。

Note

另一个建议是 FreeIPA 项目,尽管本书没有探讨。它允许您管理身份(用户帐户等),执行策略授权,如 DNS 和sudo的 Kerberos 策略,并在其他身份服务(如 Microsoft AD)之间创建相互信任。您可以在 https://www.freeipa.org/page/Main_Page 查看更多相关信息。还有一篇关于在 https://www.dragonsreach.it/2014/10/12/the-gnome-infrastructures-freeipa-move-behind-the-scenes/ 从 OpenLDAP 迁移到 FreeIPA 的文章。

X.500 DAP OSI 模型描述了 LDAP 遵循的一些基本概念。首先,您需要有一个单一的目录信息树(DIT)。这是条目的层次结构。这些条目中的每一个都需要一个可分辨名称(DN)。条目的 DN 由相对可分辨名称(RDN)及其所属的祖先条目组成。图 16-1 显示了 DIT、DN 和 RDN 之间的基本关系。

A185439_2_En_16_Fig1_HTML.jpg

图 16-1。

DITs, DNs, and RDNs

DIT 是目录树,在这种情况下,它的根 DN 是dc=com。有几种方法来定义你的根和主分支。有些人根据他们的 DNS 域名选择布局,就像我们这里一样,有些人使用地理位置,如o=USo=AUo=DE作为他们的根。在我们的例子中,我们选择使用 DNS 命名标准,因为我们不太关心我们组织的地理位置。如果我们愿意,我们总是可以在树的更下面引入LocalityName属性,就像我们在compB分支中指定l=Amsterdam的位置一样。应该考虑如何布置目录结构,但最终你希望它尽可能简单易懂。

Tip

现在定义一种命名分支机构和描述组织的标准方式并坚持下去是很重要的。

分支用于将信息组织成逻辑组。这些逻辑组被称为组织单元,表示为ou。您可以将您喜欢的任何东西分组在一起,但是您通常会在 LDAP 树中看到的主要组织单位是PeopleGroupsMachines。你将与你的人相关的一切都存储在ou=people下,将你的用户组存储在ou=groups下,将你的非人类资产存储在ou=Machines(通常也叫做ou=hosts)下。组织单元可以包含其他组织单元,并且可以像您希望的那样复杂,尽管我们再次建议在设计您的 DIT 时越简单越好。

DN 是根下的唯一条目,它由 RDN 及其祖先组成。你可以在图 16-1 中看到我们有一个cn=Angela Taylor,ou=people,dc=example,dc=com的 DN。它由 RDN cn=Angela Taylorou=peopledc=exampledc=com的祖先组成。同样,ou=people,dc=example,dc=comPeople组织单元的 DN,ou=people是它的 RDN。

每个 DN 条目都由描述该条目的对象类和属性组成。对象类描述了什么属性必须存在或者允许存在。这些类可以支持其他类,以便为它们提供扩展属性。这些属性由 RDN 值描述。类和属性必须在模式中定义,并且必须是唯一的。

模式是一组定义,描述可以存储在目录服务器中的数据。模式用于描述可用类和属性定义的语法和匹配规则。如果您发现可用的模式文件不能正确描述您的组织,您可以根据需要为您的公司创建自己的模式文件。一旦创建了模式文件,就可以将它包含在 OpenLDAP 配置文件中。

组织通常需要某些属性来描述您的用户或内部系统,这些属性在提供的模式文件中没有提供。在这种情况下,您将在自己的模式文件中创建自己的对象类和属性。创建模式文件时,必须记住使对象类和属性的名称是唯一的。

为所有创建的属性和类添加前缀以确保它们是唯一的,这是一个很好的做法。假设我们想自己添加一个属性,让我们知道用户何时被禁用。在这种情况下,我们可以为我们的示例公司定义一个“活动”属性为exampleActive。然后,我们可以通过将exampleActive设置为TRUEFALSE来启用和禁用条目。

下面是它在 LDAP 条目中的样子:

dn: uid=user1,ou=people,dc=example,dc=com
uid: user1
exampleActive: TRUE

一旦这个属性被添加到 LDAP 中的一个条目,我们就可以使用过滤器在 LDAP 目录中搜索所有的exampleActive = TRUE实例,这将加快活动用户的搜索速度。这只是一个如何使用自己的模式定义的例子;可能有其他方法可以达到同样的效果。

Note

《OpenLDAP 管理员指南》在这里有关于如何创建模式文件的解释: www.openldap.org/doc/admin24/schema.html

OpenLDAP 可以使用各种后端。默认情况下,OpenLDAP 使用内存映射数据库(MDB ),它基于 Lightning 内存映射数据库(LMDB)。LMDB 是由 Symas 开发的,Symas 是一个软件组织,由核心 OpenLDAP 开发团队中的许多人(如果不是全部的话)创建。它速度极快,可伸缩性也很强,数据库可以容纳数百万条记录。它针对阅读、搜索和浏览进行了优化。如果您愿意,OpenLDAP 可以使用其他数据库作为后端。

Note

你可以在 https://symas.com/products/lightning-memory-mapped-database/ 了解更多关于 LMDB 的信息。

总则

Ubuntu 和 CentOS 提供了不同版本的 OpenLDAP。CentOS 和 Ubuntu 都提供了最新的 OpenLDAP 2.4 版本。以下是它支持的一些功能:

  • 镜像模式和多主控复制
  • 代理同步复制
  • 扩展文档
  • LDAP 版本 3 扩展
    • LDAP 链接操作支持
    • 不使用复制控制支持
    • LDAP 动态目录服务(RFC 2589)
  • 添加覆盖层以获得更强大的功能

如果您正在寻求对多主控复制功能的支持(即拥有多个 LDAP 主目录服务的能力),多主控可以增强 LDAP 安装的冗余性。

你也可以利用叠加。覆盖为 OpenLDAP 提供了高级功能,以改变或扩展正常的 LDAP 行为。密码策略(ppolicy)覆盖等覆盖层支持 OpenLDAP 基本代码中未提供的密码控制。ppolicy 覆盖允许您设置密码时效和最小字符长度等内容。

您还需要决定您的组织将支持哪种身份验证方法。OpenLDAP 支持两种认证方法,简单和 SASL。简单方法有三种操作模式。

  • 匿名:不提供用户名或密码。
  • 未经验证:提供了用户名,但没有密码。
  • 用户名/密码验证:必须提供有效的用户名和密码。

对于 SASL 方法,《OpenLDAP 管理员指南》说,您需要一个现有的塞勒斯 says 安装来提供 SASL 机制。这并不完全正确,这取决于您想要实现的 SASL 机制。您可以非常容易地设置PLAIN / LOGIN和 DIGESTMD5 机制。然而,你必须安装赛勒斯 SASL。SASL 提供了以下机制:

  • 普通/登录
  • DIGESTMD5
  • GSSAPI (Kerberos v5)
  • 外部(X.509 公钥/私钥身份验证)

Note

SASL (PLAIN/LOGIN,DIGESTMD5)要求在userPasswd属性中使用明文密码。这对于安全来说是好是坏是一个激烈的争论。争论的一方是这样的:“一旦我进入了你的数据库,我就可以访问你所有的密码。”对此的反驳是,“如果你能进入我的数据库,游戏就结束了。至少我不会通过可能被拦截的线路发送密码。”

您可以在《OpenLDAP 管理员指南》的以下页面中了解有关这些不同认证方法的更多信息:

履行

在我们向您展示如何在我们的示例系统上安装 OpenLDAP 服务器之前,我们需要回顾一下实现的一些细节。

  • 我们将在 DNS 中设置一个 CNAME,它将把ldap.example.com指向headoffice.example.com记录,或者为安装 LDAP 服务器的主机定义一些其他的 DNS A 记录。有关 DNS 的说明,请参见第十章。
  • 我们没有使用任何目录服务的副本。复制是指我们的网络上可以有多个 LDAP 服务器共享我们的全部或部分 LDAP 数据,并响应客户端请求。这需要额外的配置。

让我们只看一下网络的一部分。假设我们的网络上有一个 web 服务器,我们想确保只有我们组织中某个组的人才能访问它。通常,我们需要在网站上添加复杂的登录机制,使用某种用户数据库来存储信息,等等。有了 Apache web 服务器,我们可以使用 Apache LDAP 模块让 web 服务器使用 LDAP 服务器来验证请求。如果没有此身份验证,网站将无法访问。我们还可以让其他服务对我们的 OpenLDAP 目录服务器进行认证。在图 16-2 中,你可以看到我们如何认证我们的 web 服务器。

A185439_2_En_16_Fig2_HTML.jpg

图 16-2。

LDAP authentication of web services

图 16-2 给出了一个简单的图表,显示了一个 web 服务器使用 LDAP 向 web 服务认证我们的桌面或互联网客户端(如果没有必要的硬件资源,LDAP 服务和 web 服务可以在同一个主机上)。当网站收到请求时,发出请求的用户需要在被授予访问权限之前进行验证。在headoffice.example.com向 LDAP 服务发送认证请求。如果用户通过验证,LDAP 服务器会将响应发送给 web 服务,用户就可以访问站点了。

我们在本书中描述的许多服务都可以使用 LDAP 服务。这使您可以将身份认证服务集中在一台主机上,降低了复杂性,提高了身份认证安全性,并为您的所有员工详细信息提供了一个中央存储库。

我们将向您展示如何为 Apache web 服务设置 LDAP 服务和身份验证。

装置

在 CentOS 和 Ubuntu 上都可以通过它们的在线存储库获得 OpenLDAP。同样,对于 OpenLDAP,这两个发行版之间存在细微的差异,我们将在接下来详述它们。

CentOS 安装指南

现在,我们将带您在 CentOS 主机上安装 OpenLDAP 服务器。二进制文件可以从 CentOS 存储库中获得,您可以通过yum命令或软件包管理器 GUI 来安装它们。我们将通过yum命令安装它们,如下所示:

$ sudo yum install openldap openldap-clients openldap-servers

这将安装配置、运行和管理 LDAP 服务器所需的文件。openldap包安装允许主机与 OpenLDAP 服务器集成所需的基础包。openldap-clients包安装管理和查询 LDAP 服务器的工具。openldap-servers包安装运行 OpenLDAP 服务器所需的文件。

Ubuntu 安装指南

要在 Ubuntu 主机上安装 OpenLDAP,我们需要安装ldap-utils包和slapd包。以下命令将安装这些软件包:

$ sudo aptitude install ldap-utils slapd

当您发出这个命令时,slapd包将要求您提供根 LDAP 用户的密码。您可以输入密码并继续安装。如果您不想提供密码,只需按两次 Enter 键(我们将在接下来的“配置”部分向您展示如何创建密码)。一旦安装完毕,ldap-utils包将安装管理和搜索 LDAP 目录所需的文件。slapd包安装运行和配置 LDAP 目录所需的文件。

客户端配置包是auth-client-config (PAM 和 NSS 配置文件切换器)和ldap-auth-client(用于 LDAP 认证和ldap-auth-config的元包)。你可能也想安装这些。

配置

我们将向您展示如何配置 LDAP 目录服务。LDAP 服务器被称为 SLAPD。我们将向您展示如何配置该服务。

OpenLDAP 使用动态运行时配置来管理 SLAPD,这意味着它通过自己的 DIT(目录树)来配置自己。这意味着 SLAPD 配置更改可以通过使用用于更改其他 LDAP 记录的标准工具来更改 DIT 中的记录来动态完成,使用的命令有ldapmodify之类。

在我们的例子中,我们将在 Ubuntu 主机上配置我们的 SLAPDCentOS 的某些目录路径会有所不同。对于 CentOS 主机,配置目录称为/etc/openldap,而不是 Ubuntu 主机上的/etc/ldap。两个发行版都将数据库存储在/var/lib/ldap

例如,CentOS 主机上 OpenLDAP 的配置文件存储在/etc/openldap/中。

$ sudo ls -l /etc/openldap/
total 12
drwxr-xr-x. 2 root root   85 Oct 26 12:46 certs
-rw-r--r--. 1 root root  121 Mar 31  2016 check_password.conf
-rw-r--r--. 1 root root  365 Oct  3 13:47 ldap.conf
drwxr-xr-x. 2 root root 4096 Oct 26 12:46 schema
drwx------. 3 ldap ldap   43 Oct 26 12:46 slapd.d

机密存储在certs目录中。专门用于配置 LDAP 客户端的配置文件是ldap.confschema目录包含我们的ldap服务的模式文件。在那里你会找到.schema文件和.ldif文件。LDIF 文件是 LDAP 交换格式文件,这是一种在 LDAP 中指定数据更改的特殊格式。在slapd.d目录中,您将找到包含 SLAPD DIT 的文件。我们将在本章中解释这些。

要求

在配置 OpenLDAP 之前,我们将设置一些要求。我们需要创建一个 TLS 证书和密钥以及一个 DNS 名称条目。

第一步是创建 DNS 记录。像 LDAP 这样的身份验证系统通常不会向公众公开,如果公开,它们可能会受到外部攻击。因此,我们通常不会提供公共 IP 地址。对于需要针对服务进行身份验证的外部办公室,我们建议您使用专用 VPN 进行访问。

这个 OpenLDAP 服务将被安装在我们的headoffice.example.com主机上。我们将为我们的 DNS 服务器提供CNAME记录,以将ldap.example.com指向headoffice.example.com。我们需要在我们的 DNS 服务器上发出以下命令:

$ sudo nsupdate -k /etc/bind/ddns_update.key
> server localhost
> update add ldap.example.com 8600 CNAME headoffice.example.com
> send
> quit
$ host ldap.example.com
ldap.example.com is an alias for headoffice.example.com.
headoffice.example.com has address 192.168.0.1

由于这指向一个内部私有 IPv4 地址,我们将无法使用 Let's Encrypt 来创建我们的 TLS 证书,而必须使用我们自己的私有 CA。连接到我们的 LDAP 服务器的客户端需要安装 CA 根证书。

首先,创建一个名为/etc/ldap/certs的新目录,然后更改它的权限。

$ sudo mkdir /etc/ldap/certs

我们将在我们的ldap.example.com主机上创建密钥和 CSR,并从/etc/ldap/certs目录中运行以下内容:

$ sudo openssl req -new -newkey rsa:4096 -nodes -keyout
ldap.example.com.key -out ldap.example.com.req

继续,像我们在第 11 和 15 章中所做的那样,通过我们的私人 CA 在请求上签名。然后,我们需要将生成的公共证书和根 CA 一起添加到/etc/ssl/certs目录中,如果根 CA 不在那里的话,cacert.pem

当证书被安装到certs目录中时,我们应该将所有权和权限更改如下:

$ sudo chown openldap:openldap –R /etc/ldap/certs
$ sudo chmod 600 /etc/ldap/certs/ldap.example.com.key

在 CentOS 上运行 LDAP 服务的用户是ldap,需要在前面的chown命令中使用。

配置 SLAPD

有了这些要求,我们现在可以开始配置 OpenLDAP 服务器了。当我们在 Ubuntu 上安装了slapd包并被要求输入管理员密码时,基本的 OpenLDAP 服务器被配置、安装并启动(在 CentOS 上,您必须在运行该命令之前启动slapd服务)。

在清单 16-1 中,我们有ldapsearch命令的输出。该命令是用于与 LDAP 服务器(或任何 LDAP 服务器)交互的命令套件的一部分。在这个例子中,我们将–Q参数传递给ldapsearch来启用 SASL 安静模式(因为我们使用提升的sudo特权)。这三个字母(-LLL)都有一个意思。拥有一个意味着以 LDIF 格式打印;另外两个减少输出。–H参数是我们想要连接的 URI,ldapi:///,也就是说,通过本地主机上的 Unix 套接字连接到本地 LDAP 服务器。这样,我们可以将用户的 UID 和 GID 传递给 LDAP 服务器进行身份验证。接下来,-b定义搜索基数;我们使用dn作为过滤器,在cn=config DIT 中搜索每个dn(或识别名)。

$ sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config dn
dn: cn=config
dn: cn=module{0},cn=config
dn: cn=schema,cn=config
dn: cn={0}core,cn=schema,cn=config
dn: cn={1}cosine,cn=schema,cn=config
dn: cn={2}nis,cn=schema,cn=config
dn: cn={3}inetorgperson,cn=schema,cn=config
dn: olcBackend={0}mdb,cn=config
dn: olcDatabase={-1}frontend,cn=config
dn: olcDatabase={0}config,cn=config
dn: olcDatabase={1}mdb,cn=config
Listing 16-1.Viewing the Default Configuration

在清单 16-1 中,您还可以看到我们已经通过了–Y选项。这指定了我们想要使用的 SASL 认证机制。EXTERNAL这里说在这种情况下使用 localhost 的认证。我们通过 Unix 套接字将 root 用户的 UID 和 GID 传递给 LDAP 进行身份验证。默认安装允许本地超级用户访问已安装的 LDAP 服务器。

LDIF Format

LDAP 目录交换格式(LDIF)是如何在 LDAP 数据库中添加和删除条目的规范。它有自己的 RFC ( https://www.ietf.org/rfc/rfc2849.txt ),LDAP 工具可以使用它来更改 LDAP 数据库中的记录。

LDIF 文件的格式如下:

dn: <the distinguished name you wish to change>
changetype: optional change type of either add, replace, or delete
<attribute or objectclass>: value

下面是一个例子:

dn: dc=example,dc=com
objectclass: dcObject
objectclass: organizationalUnit
dc: example
ou: example

这里我们已经描述了 DIT 的顶层。DN dc=example,dc=com将由那些特定的对象类和属性组成。

要修改现有的 DN 条目,我们可以像这样使用:

dn: uid=ffrank,ou=people,dc=example,dc=com
changetype: replace
replace: userPassword
userPassword: <new password>
-

这里我们用一个 LDIF 格式的文本文件修改了ffrankuserPassword。可以看到我们要更改的 DN,我们要执行的更改类型(replace,我们要替换的属性(userPassword);最后,我们将新值赋给属性。我们可以在一个文件中有很多这样的语句,我们用-在新的一行中把每个语句分开。

然后在清单 16-1 中,我们有了在cn=config全局配置 DIT 中的全局指令 dn 列表。你可以看到全局 DIT 由根cn=config组成,然后域名嵌套在它下面,如图 16-3 所示。

A185439_2_En_16_Fig3_HTML.jpg

图 16-3。

cn=config DIT

从清单 16-1 和图 16-3 中可以看到,LDAP 模式cn={1}cosine,cn=schema,cn=config位于cn=schema DN 下,DN 位于cn=config下。{1}表示模式 DN 的索引。

此外,在清单 16-1 中,还有其他几个全局指令,如olcBackendolcDatabase。顾名思义,它们描述了后端数据存储。

dn: olcBackend={0}mdb,cn=config
dn: olcDatabase={-1}frontend,cn=config
dn: olcDatabase={0}config,cn=config
dn: olcDatabase={1}mdb,cn=config

要获得cn=config DIT 的完整列表或备份,可以发出以下命令:

sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config > slapd.ldif

我们使用了与之前相同的命令,但是从末尾移除了dn过滤器,并将输出定向到slapd.ldif文件。如果您查看该文件,您将会看到这些指令是如何配置的。

看第一个声明,我们有了cn=config的 DN。它有一个olcGlobalObjectClass,它是定义全局 DIT 的对象类。您还可以看到,我们可以声明通用名称(cn:)、用于slapd ( olcArgsFile:)的参数文件、日志级别(olcLogLevel:)、进程 ID ( olcPidFile:)和要使用的线程(olcToolThreads:)。

olcToolThreads指令告诉slapd守护进程只使用一个 CPU 来运行索引。如果您有多个 CPU,您可以将其设置为一个较大的数字,但不能高于您拥有的 CPU 数量。可以开启其他性能设置,包括olcThreadsolcTimeLimitsolcSockBuffMaxIncomingolcSockBuffMinIncoming

Tip

你可以转动的另一个调节旋钮是在一次点击中从一个ldapsearch返回的条目的数量,olcSizeLimit

每次您声明一个 DN 时,您都需要提供它所属的对象类。该对象类将具有它所采用的属性。因此,olcGlobal对象类将olcArgsFile作为属性,这将在模式文件中描述。

定义日志级别

olcGobal对象类中,我们可以定义我们的日志记录。默认设置为none。这是一个关键字,但也可以用数字(甚至十六进制)来表示,如表 16-1 所述。

表 16-1。

Additive Logging Levels

| 水平 | 关键词/描述 | | --- | --- | | `-1` | (任何)打开所有调试信息。这有助于在更细粒度地记录日志之前找出 LDAP 服务器的故障所在。 | | `0` | 关闭所有调试。这是生产模式的推荐选项。 | | `1` | (`0x1` trace)跟踪函数调用。 | | `2` | (`0x2` packets)调试数据包处理。 | | `4` | (`0x4` args)提供重跟踪调试(函数 args)。 | | `8` | (`0x8` conns)提供连接管理。 | | `16` | 打印出发送和接收的数据包。 | | `32` | (`0x20` filter)提供搜索过滤处理。 | | `64` | (`0x40` config)提供配置文件处理。 | | `128` | (`0x80` ACL)提供访问控制列表处理。 | | `256` | (`0x100` stats)提供连接、LDAP 操作和结果(推荐)。 | | `512` | (`0x200` stats2)表示已发送的统计日志条目。 | | `1024` | (`0x400` shell)打印与 shell 后端的通信。 | | `2048` | (`0x800` parse)解析条目。 | | `16384` | (`0x4000` sync)提供 LDAPSync 复制。 | | `32768` | (`0x8000`无)无论设置了什么日志级别,都只记录消息。 |

日志级别对于帮助调试您的安装非常重要。老实说,对于新用户来说,报告的内容可能非常混乱。然而,日志级别是附加的,您可以在日志中获得更细粒度的细节。在生产环境中,我们建议将该值设置为0,如果您愿意,可以使用审计覆盖来监控您的安装发生了什么(覆盖是一个软件模块,可以连接到后端以提供特定的信息,在本例中是审计跟踪)。

我们希望将日志级别设置为 480。这将在我们的日志中显示搜索过滤器、配置文件处理、访问控制和连接信息。如上所述,Loglevel设置是附加的,这意味着您可以通过添加想要记录的内容的值来启用更多的日志记录。您可能已经知道了,我们的480Loglevel由层次32(搜索过滤器)、64(配置处理)、128(访问控制列表处理)和256(连接和 LDAP 操作结果)组成。当我们设置 LDAP 服务时,这是一个很好的设置,因为它提供了很好的信息。如果我们卡住了,我们可以将Loglevel改为-1来打开调试,这将打开所有的日志功能。另外,请记住,在生产环境中,您通常希望将Loglevel设置为0。要设置Loglevel,也可以在一行中列出十六进制数,达到同样的效果;在这种情况下,我们会将日志级别设置为Loglevel 0x20 0x40 0x80 0x100

使用 ldapmodify 修改日志级别配置

让我们修改LogLevel到我们想要的水平。为此,我们将使用ldapmodify命令。这采用了与我们之前使用的ldapsearch命令相似的参数。我们将为该命令提供一个名为loglevel.ldif的文件,如下所示:

dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: 480

要修改一个属性,我们需要提供要修改的dn值(dn: cn=config)、变化类型(changetype: modify)、要替换的属性(replace: olcLogLevel),最后是要设置的属性(olcLogLevel: 480)。

现在,让我们使用ldapmodify来修改日志属性。

$ sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f loglevel.ldif

为了确认这已经被设置,我们可以再次发出ldapsearch命令来验证。

$ sudo ldapsearch –Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=config cn=config
dn: cn=config
objectClass: olcGlobal
cn: config
olcArgsFile: /var/run/slapd/slapd.args
olcPidFile: /var/run/slapd/slapd.pid
olcToolThreads: 1
olcLogLevel: 480

很好,这就是我们请求的日志设置。因为我们已经设置了这个属性,所以任何其他的 SLAPD 配置属性都可以以类似的方式设置。

添加模块

slapd.ldif文件中,我们有模块部分。模块被添加到配置中以提供对某些功能的访问。

dn: cn=module{0},cn=config
objectClass: olcModuleList
cn: module{0}
olcModulePath: /usr/lib/ldap
olcModuleLoad: {0}back_mdb

这里我们声明了找到我们的模块的路径,olcModulePath: /usr/lib/ldap。我们加载了一个模块,back_mdb,这是我们前面提到的层次内存映射数据库。

我们还希望启用政策覆盖模块。ppolicy 模块允许我们通过密码到期和其他密码控制功能对数据库中的密码进行更好的控制。如果我们检查前面描述的模块路径,我们可以验证所需的文件在那里。

$ ll /usr/lib/ldap/pp*
-rw-r--r-- 1 root root 39328 May 11 17:11 /usr/lib/ldap/ppolicy-2.4.so.2.10.5
-rw-r--r-- 1 root root   948 May 11 17:11 /usr/lib/ldap/ppolicy.la

为了加载策略,我们将创建一个名为ppolicy_module.ldif的文件,并使用ldapmodify来添加它。

dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: ppolicy.la

当我们执行ldapmodify命令时,您可以看到我们现在要求它添加模块ppolicy.la。如果我们现在仅对那些包含对象类olcModuleList的 DNs 进行ldapearch过滤,我们会看到以下内容:

$ sudo ldapsearch -H ldapi:// -Y EXTERNAL -b "cn=config" -LLL -Q "objectClass=olcModuleList"
dn: cn=module{0},cn=config
objectClass: olcModuleList
cn: module{0}
olcModulePath: /usr/lib/ldap
olcModuleLoad: {0}back_mdb
olcModuleLoad: {1}ppolicy.la

在“密码策略覆盖”一节中,当我们将 LDIFs 加载到 OpenLDAP 数据库中时,我们将进一步添加到 ppolicy 覆盖配置中。

设置后缀、RootDN 和 RootPW

我们现在将配置保存 DIT 的后端数据库。如果需要,我们可以更改默认的数据库后端,这里有几个选项。一般情况下,你会选择默认的mdb。其他类型可以选择(ldapldifmetadirectoryperl等)。)用于代理您的 LDAP 服务器。

Note

有关后端数据库选择的更多信息,请参见在线文档: www.openldap.org/doc/admin24/backends.html

为了查看当前的数据库设置,我们可以发出下面的ldapsearch:

$ sudo ldapsearch -H ldapi:/// -Y EXTERNAL -b "olcDatabase={1}mdb,cn=config" -LLL  -Q
dn: olcDatabase={1}mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {1}mdb
olcDbDirectory: /var/lib/ldap
olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none
olcAccess: {1}to attrs=shadowLastChange by self write by * read
olcAccess: {2}to * by * read
olcLastMod: TRUE
olcDbCheckpoint: 512 30
olcDbIndex: objectClass eq
olcDbIndex: cn,uid eq
olcDbIndex: uidNumber,gidNumber eq
olcDbIndex: member,memberUid eq
olcDbMaxSize: 1073741824
olcSuffix: dc=nodomain
olcRootDN: cn=admin,dc=nodomain
olcRootPW: {SSHA}EEyEuYme4zBPYbRzHc+l4rApfvrXjXnV

我们的数据库类型的默认值在这里定义:olcDatabase: {1}mdb。您可以声明多个数据库实例。我们配置的下一个细节是 DIT 的顶部,后缀,以及一个可以完全访问它的用户,比如根用户。

Note

如果您在 Ubuntu 服务器上安装 OpenLDAP 服务器时配置了它,您就不需要执行这个步骤。

这里我们将创建一个名为db.ldif的文件,它包含以下内容:

dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=example,dc=com
-
replace: olcRootDN
olcRootDN: cn=admin,dc=example,dc=com
-
replace: olcRootPW
olcRootPW: {SSHA}QN+NZNjLxIsG/+PGDvb/6Yg3qX2SsX95

olcSuffix将对dc=example,dc=com的查询指向这个数据库实例。因为这些属性已经有了值,所以我们在 LDIF 文件中使用 replace 指令,例如:replace: olcRootPW。您可以在这里声明多个后缀。olcRootDN是根用户,对数据库有完全的访问权;密码在olcRootPW中声明。您可以使用slappasswd命令创建密码,如下所示:

$ sudo slappasswd

然后可以像前面一样将打印的密码复制并粘贴到olcRootPW中。在应用此 LDIF 之前,我们将查看我们的索引。

创建索引

接下来,我们可以设置我们的索引。索引用于加快数据库的搜索速度。您可以通过运行以下命令来查看数据库的当前索引:

$ sudo ldapsearch -H ldapi:/// -Y EXTERNAL -b "olcDatabase={1}mdb,cn=config" -LLL –Q olcDbIndex
olcDbIndex: objectClass eq
olcDbIndex: cn,uid eq
olcDbIndex: uidNumber,gidNumber eq
olcDbIndex: member,memberUid eq

作为一个规则,你应该索引你的客户通常会搜索什么。当电子邮件客户端的地址簿寻找人们的名字来填充其地址簿条目时,它可能会搜索常用名,或cn。在这种情况下,您可能希望为子字符串sub优化cn属性的索引。表 16-2 列出了可用的常用索引类型。

表 16-2。

Common Index Types

| 类型 | 描述 | | --- | --- | | `sub` | 对于优化包含通配符`cn=Jane*`的字符串搜索非常有用 | | `eq` | 有助于优化对精确字符串的搜索,如`sn=Smith` | | `pres` | 有助于优化对象类或属性的搜索,如`objectclass=person` | | `approx` | 有助于优化类似声音的搜索,如`sn∼=Smi*` |

其他索引类型也是可用的,您可以在slapd.conf手册页上阅读它们。我们想要索引objectclasscnuid,我们知道当用户尝试进行身份验证时会经常搜索到它们。在前面的代码中,你可以看到我们已经在索引这些东西了。我们将为即将创建的属性exampleActive添加一个索引。我们将在db.ldif中添加以下内容:

add: olcDbIndex
olcDbIndex: exampleActive pres,eq

现在让我们继续使用ldapmodify来应用我们的db.ldif更改。

$ sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f db.ldif

Note

你可以在这里阅读更多关于配置引擎数据库: www.openldap.org/doc/admin24/slapdconf2.html

列出、添加和创建模式

模式向 SLAPD 服务器提供对象的类和属性的结构。虽然与数据库模式不同,但 LDAP 模式描述了 LDAP 服务器将拥有的对象类和属性,就像数据库模式描述表和行一样。您可以使用下面的ldapsearch查看当前加载的模式:

$ sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=schema,cn=config dn
dn: cn=schema,cn=config
dn: cn={0}core,cn=schema,cn=config
dn: cn={1}cosine,cn=schema,cn=config
dn: cn={2}nis,cn=schema,cn=config
dn: cn={3}inetorgperson,cn=schema,cn=config

最上面的dncn=schema,cn=config,那是我们图式的父代。然后,我们有了一些由我们的安装提供的默认模式。核心模式提供了诸如dcObject ( dc)和organizationalUnit ( ou)这样的对象类。cosine模式提供了dNSDomain对象类和host属性。nis模式提供用户帐户对象和属性,例如posixAccount和影子密码设置。inetorgperson保存其他各种与员工相关的对象和类。您可以使用也可以不使用这些提供的对象和属性。

要查看所有可用的模式,可以列出/etc/ldap/schema目录。例如,我们可以看到在该目录中存在 ppolicy 模式的ppolicy.schemappolicy.ldif文件。

$ ls /etc/ldap/schema/pp*
/etc/ldap/schema/ppolicy.ldif  /etc/ldap/schema/ppolicy.schema

对于我们来说,ppolicy.ldif文件是从ppolicy.schema文件中派生出来的。我们将把我们的ppolicy.ldif模式添加到 SLAPD 中。我们通过使用ldapadd命令来做到这一点。它采用了与ldapsearchldapmodify相似的论点。

$ sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/ppolicy.ldif
adding new entry "cn=ppolicy,cn=schema,cn=config"

让我们看看是否已经加载。

$ sudo ldapsearch -Q -LLL -Y EXTERNAL -H ldapi:/// -b cn=schema,cn=config dn
dn: cn=schema,cn=config
dn: cn={0}core,cn=schema,cn=config
dn: cn={1}cosine,cn=schema,cn=config
dn: cn={2}nis,cn=schema,cn=config
dn: cn={3}inetorgperson,cn=schema,cn=config
dn: cn={4}ppolicy,cn=schema,cn=config

您可以看到我们的 ppolicy 模式已经添加到索引{4}处。记住这个索引号,因为我们在添加自己的模式时会用到它。让我们看看如何创建和添加我们自己的模式。

创建我们的模式

我们将创建一个名为/etc/ldap/schema/exampleactive.schema的文件。在这个模式文件中,我们将包含一个简单的类和属性,用于指示用户帐户是否处于活动状态。

首先,让我们看看如何在模式中声明一个对象类。以下内容出现在schema目录下的core.schema文件中:

objectclass ( 1.3.6.1.4.1.1466.344 NAME 'dcObject'
        DESC 'RFC2247: domain component object'
        SUP top AUXILIARY MUST dc )

这是将包含在 DIT 中的主要对象类之一。我们需要声明我们正在使用什么类型的实体,对于一个对象类,我们从objectclass ( schema detail)开始。空白在声明对象类和属性时很重要,并且在每一端的( )中必须有一个空格。对象类声明应遵循以下格式:

objectclass ( <OID> NAME <name> DESC <description> SUP <parent class> <class type> <MUST|MAY> attritubutes )

你看到的数字,1.3.6.1.4.1.1466.344,是私企号(PEN),或者说是对象标识符(OID),是用于识别对象的唯一的一系列数字;如果您熟悉 SNMP 之类的东西,您应该认识到这一点,因为它们使用相同的 OID 概念。

Note

您可以在互联网号码分配机构(IANA)网站: http://pen.iana.org/pen/PenApplication.page 注册自己的 OID 或 PEN。

对象类被赋予一个名称、dcObject和一个描述(DESC)。下一行告诉你这将继承对象类SUP topSUP代表上级,top表示该对象类没有父对象类;它是对象类层次结构中的最高级别。其他后续的对象类可以使用这个对象类作为它们的SUP或继承的对象类。

AUXILIARY表示对象类的类型。有三种类型的对象类。

  • AUXILIARY:允许您向条目添加属性,但不能创建条目
  • STRUCTURAL:允许您创建有效条目
  • ABSTRACT:可以定义其他对象类的基础对象;top是一个ABSTRACT的例子

MUST dc表示如果这个对象是在目录服务器中声明的,那么属性dc也必须被添加。对于对象类来说,非强制但可用的属性可以声明为MAY

Note

声明对象类的全部细节包含在这个 RFC 中: www.rfc-editor.org/rfc/rfc4512.txt 。扩展你的图式的快速解释可以在这里找到: www.openldap.org/doc/admin24/schema.html

属性也有一定的规则。它们必须在模式中声明,并且同一属性可以包含在一个或多个对象类中。另外,默认情况下,属性是MULTI-VALUE,这意味着我们可以为 DN 声明多个值。常见的例子是电子邮件地址;一个用户可以有多个电子邮件地址。其他属性被声明为SINGLE-VALUE,并且只能声明一次,比如用户的密码。

属性可以是分层的,并且可以继承其父属性。它们以不同的方式表示为对象类层次结构,如下所示:

  • 它们没有以顶部结束。
  • 上级定义的缺失表明了等级制度的终结。

属性继承的常见例子是name属性。name属性是普通名(cn)、名(gn)和姓(sn)的父属性。

让我们来看看我们自己创建的模式文件,/etc/ldap/schema/exampleactive.schema:

# $Id$

attributetype ( 1.1.3.10 NAME 'exampleActive'
DESC 'Example User Active'
SINGLE-VALUE
EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7)

objectclass ( 1.1.1.2 NAME 'exampleClient'
SUP top AUXILIARY
DESC 'Example.com User objectclass'
MAY ( exampleActive ))

在这两个模式对象中,我们有两个 oid,这是我们编造的。本例中的文件可能与其他现有的模式文件冲突,并且仅用于演示。为了避免这种情况,我们通常会申请我们自己的笔。我们将假设我们这样做了,并且我们收到了 1.3.6.1.4.1.111111 的 OID,其中 1.3.6.1.4.1 是 IANA 弧或节点,111111 是区分我们公司和其他公司的特殊数字。我们现在可以使用我们的 OID 来代替前面模式中的那些。

Caution

正如我们提到的,我们已经为此次演示构建了 1.3.6.1.4.1.111111 OID。请不要在您的生产环境中编造数字或使用此 OID。你真的应该有自己的笔;否则,你就有发生冲突和打破东西的风险。有关 oid 和 LDAP 的更多信息,请同时查看以下内容: www.zytrax.com/books/ldap/apa/oid.html

attributetype ( 1.3.6.1.4.1.111111.3.1.1 NAME 'exampleActive'
DESC 'Example User Active'
SINGLE-VALUE
EQUALITY booleanMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 )

objectclass ( 1.3.6.1.4.1.111111.3.2.1 NAME 'exampleClient' SUP top AUXILIARY DESC
'Example.com User objectclass'
MAY ( exampleActive ))

一旦有了钢笔或 OID,就可以将其分成有用的线段(也称为节点或弧)。通常,您不仅可以将 OID 用于 LDAP 模式对象,还可以用于 SNMP MIBs 之类的对象。如您所见,我们已经为 LDAP 模式定义分支了 1.3.6.1.4.1.111111.3。在那之下,我们将把我们所有的对象类定义放在 1.3.6.1.4.1.111111.3.2 下,把我们的属性放在 1.3.6.1.4.1.111111.3.1 下。

Note

将 1.3.6.1.4.1.111111.3.1 和 1.3.6.1.4.1.111111.3.2 分配给 LDAP 类和属性完全是任意的。您可以选择任何您想要的编号方案。

我们的属性exampleActive只能声明一次,所以我们将它设为SINGLE-VALUE。如果我们试图为一个特定的 DN 多次声明这个属性,我们将得到一个违例错误。

我们将exampleActive属性设置为布尔匹配,这意味着它可以是真或假。将该属性设置为TRUE将意味着我们的帐户是活动的。将它设置为FALSE将意味着该帐户是不活跃的。我们可以索引这个属性,这将再次加快我们的搜索。这就是为什么我们在前面的db.ldif中添加了以下内容:

olcDbIndex: exampleActive pres,eq

exampleClient对象类定义了当我们在 DN 条目中包含该对象类时,我们可能会出现exampleActive属性(如MAY所示)。如果我们想加强它的存在,我们可以指定MUST来代替。对象类的类型是AUXILARY,并且具有由SUP top定义的超类。默认的对象类型是STRUCTURAL。条目中必须有一个STRUCTURAL对象类,不能有两个STRUCTURAL对象类指向同一个父类或上级类。

Note

您可以在 www.rfc-editor.org/rfc/rfc4512.txt 找到描述 LDAP 模式文件的 RFC。

添加我们的模式

要添加我们的模式,我们需要经历以下过程:

  • 通过slaptest将我们的模式转换为 LDIF 模式
  • 编辑输出,为输入模式做准备
  • 通过ldapadd将其添加到我们的 SLAPD 中

为了将模式文件转换成 LDIF,我们使用了slaptest命令。slaptest命令对于将基于文本的模式文件转换成 LDIF 格式很有用。

我们将把/etc/ldap/schema/exampleactive.schema传递给slaptest,输出文件将在一个临时的 SLAPD 配置目录中生成。

首先创建一个临时目录来保存我们转换后的文件。

$ sudo mkdir /etc/ldap/ldif_converted && cd /etc/ldap

在这个目录中,我们现在将创建一个名为schema_load.conf的文件,格式为old slapd.conf,它将用于指导slaptest命令读取我们的模式文件。它有以下内容:

include /etc/ldap/schema/exampleactive.schema

现在我们可以用它作为我们的slaptest命令的输入文件。

$ sudo slaptest –f schema_load.conf –F ldif_converted

这会创建一个 LDIF 格式的文件。

 /etc/ldap/ldif_converted/cn\=config/cn\=schema/cn={0}exampleactive.ldif

如果出现以下错误:

58180fbb schema/exampleactive.schema: line 1 attributetype: Missing closing parenthesis before end of input

这表明模式文件中有空白错误。您可以将声明放在一行中,没有回车,并注意空格。这里有一个例子:

attributetype ( attribute detail )

objectclass ( object detail )

我们将使用我们的vi编辑器编辑已经输出的 LDIF 文件,并使用sudo提升我们的特权。

# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.

# CRC32 39f1bf5a

dn: cn={0}exampleactive
objectClass: olcSchemaConfig
cn: {0}exampleactive
olcAttributeTypes: {0}( 1.3.6.1.4.1.111111.3.1.1 NAME 'exampleActive' DESC '
 Example User Active' EQUALITY booleanMatch SYNTAX 1.3.6.1.4.1.1466.115.121.
 1.7 SINGLE-VALUE )
olcObjectClasses: {0}( 1.3.6.1.4.1.111111.3.2.1 NAME 'exampleClient' DESC 'E
 xample.com User objectclass' SUP top AUXILIARY MAY exampleActive )

structuralObjectClass: olcSchemaConfig

entryUUID: 53a98d60-3432-1036-9ae2-35c34321a848

creatorsName: cn=config

createTimestamp: 20161101035217Z

entryCSN: 20161101035217.399551Z#000000#000#000000

modifiersName: cn=config

modifyTimestamp: 20161101035217Z

我们需要删除粗体字的行,比如从structuralObjectClass: olcSchemaConfmodifyTimestamp: 20161101035217Z以及最上面的两个#行。接下来,您可以看到下面一行:

dn: cn={0}exampleactive

记住,{0}指的是索引,如果我们试图加载这个 DN,我们将与任何具有cn={0}的现有模式冲突,回到我们的ldapsearch输出,它是核心模式。当我们添加 ppolicy 模式时,我们说过要记住索引号({4}),现在我们需要给它添加一个,以确保我们的索引不冲突。

dn: cn={5}exampleactive

然后我们会将该文件保存到/etc/ldap/schema/exampleactive.ldif。您现在可以使用ldapadd命令将创建的 LDIF 添加到我们的 SLAPD 服务器中。

$ sudo ldapadd -Q -Y EXTERNAL -H ldapi:/// -f schema/exampleactive.ldif
adding new entry "cn={5}exampleactive,cn=schema,cn=config"

当我们在本章的“LDIFs 和添加用户”一节中声明我们的用户时,我们将使用exampleactive.schema文件。

访问控制列表

如果您希望它是安全的,那么每个访问您的 LDAP 服务器的连接都必须被赋予对树的各个部分的特定访问权。OpenLDAP 默认访问权限是 read,如果您存储了密码之类的秘密,那么您将希望锁定它。您可以指定从何处接受连接、安全级别或连接必须具有的加密以获得访问权限,直到您允许访问的分支或属性。您还可以授予请求连接几个级别的访问权限:managewritereadsearchauth

列出访问控制

访问控制附加到数据库配置。要查看当前的访问控制列表,我们需要执行以下命令:

$ sudo ldapsearch -H ldapi:/// -Y EXTERNAL -b "olcDatabase={1}mdb,cn=config" -LLL  -Q
dn: olcDatabase={1}mdb,cn=config
objectClass: olcDatabaseConfig
objectClass: olcMdbConfig
olcDatabase: {1}mdb
olcDbDirectory: /var/lib/ldap
olcSuffix: dc=example,dc=com

olcAccess: {0}to attrs=userPassword by self write by anonymous auth by * none

olcAccess: {1}to attrs=shadowLastChange by self write by * read

olcAccess: {2}to * by * read

olcLastMod: TRUE
...

在前面几行中,您可以看到访问列表。它们以olcAccess开头,并被分配了一个索引号{0}。我们可以查看指标{0}如下:

(access) to attrs=userPassword

    by self write

    by anonymous auth

    by * none

这允许用户写入他们自己的userPassword属性,并且匿名用户可以进行身份验证。其他一切都做不了什么(by * none)。

如何定义访问控制列表

您将看到“旧的slapd.conf”风格和动态或 LDIF 格式的访问控制列表文档。在旧的格式中,您将使用access指令来引导访问列表。在 LDIF 格式中,你会有索引号。

在其最基本的形式中,访问是使用以下语法给出的:

[access|{n}]to what [ by who [ access-level ] [ control ] ]

what是 LDAP 数据库中的一个实体,who是请求信息的客户端,access-level是您希望该客户端拥有的访问级别。control指定该条目后如何处理列表,可选。

Note

在本节中,当我们显示访问列表指令时,我们将忽略访问指令或索引号。最终的访问列表将采用 LDIF 格式。

在下面这个简单的例子中,我们给出了对 DIT 中所有内容的读取权限。

to *
   by * read stop

您可以使用通配符*来允许一般的无限制访问。这里的访问控制表示任何用户都拥有对任何内容的读取权限。接下来是一个控制语句,告诉slapd停止处理任何其他指令。顺序在访问控制列表中很重要,顺序较高的指令在顺序较低的指令之前被处理。当您给出一个特权或访问级别时,它意味着所有以前的特权或访问级别。例如,读访问自动授予前面的discloseauthcomparesearch访问级别,包括read访问权限。表 16-3 列出了可以分配给实体访问请求的访问级别。

表 16-3。

Access Privileges

| 接近 | 特权 | | --- | --- | | `none` | 不允许任何访问 | | `disclose` | 不允许访问,但返回错误 | | `auth` | 启用绑定操作(身份验证) | | `compare` | 允许您比较实体 | | `search` | 允许您搜索 DIT 的这一部分 | | `read` | 允许读取访问 | | `write` | 允许写访问 | | `manage` | 允许所有访问和删除实体的能力 |

当您选择none时,您拒绝对实体的所有访问,而不会向请求者返回错误。这有助于防止您的 DIT 中有什么和没有什么的信息泄露。与none不同,disclose访问将向请求客户端返回一个错误。

定义谁

进一步看请求对实体的访问,您需要知道谁在请求访问。可以有多个who声明,每个声明使用特定的关键字。这些关键字可以与一个style限定符结合,这个限定符可以是类似于regexexact的东西。regex风格指的是可以用来匹配 DN 各个部分的正则表达式。处理访问控制列表的成本更高。

Tip

关于使用正则表达式的技巧以及我们在这里讨论的其他主题,请参见 OpenLDAP 管理员指南: www.openldap.org/doc/admin24/access-control.html

从处理的角度来看,它总是成本较低,但更精确地描述您希望向谁提供访问权限。这里有一个例子:

to dn.subtree=ou=people,dc=example,dc=com
   by dn.exact="cn=admin,ou=meta,dc=example,dc=com" read

在这里,我们再次授予对组织单位People下所有内容的读取权限。我们很明确地定义了这个访问权限只授予 DN cn=admin,ou=meta, dc=example,dc=com

定义授予什么访问权限会变得很棘手。有几种标准方法可用于授予访问权限。您可以使用以下内容:

dn.base
dn.one
dn.subtree
dn.children

为了解释这些与我们正在处理的对象的关系,我们将借用 OpenLDAP 管理员指南中的一个例子。假设我们有以下列表:

0: dc=example,dc=com
1: cn=Manager,dc=example,dc=com
2: ou=people,dc=example,dc=com
3: uid=jsmith,ou=people,dc=example,dc=com
4: cn=addresses,uid=jsmith,ou=people,dc=example,dc=com
5: uid=ataylor,ou=people,dc=example,dc=com

当我们试图处理 DIT 的一部分时,我们可以声明模式匹配的范围。

dn.base="ou=people,dc=example,dc=com" match 2;

dn.one="ou=people,dc=example,dc=com" match 3, and 5;

dn.subtree="ou=people,dc=example,dc=com" match 2, 3, 4, and 5; and

dn.children="ou=people,dc=example,dc=com" match 3, 4, and 5.

声明正确的范围将捕获 DIT 树的正确部分。如您所见,范围dn.base将只引用声明的树的级别ou=people,dc=example,dc=comdn.one的范围将在ou=people,dc=example,dc=com后作用于树的直接部分。

dn.subtree范围将作用于ou=people,dc=example,dc=com下的所有东西及其自身,而dn.children将作用于ou=people,dc=example,dc=com下的所有东西。

通过过滤器定义人员

在 LDAP 中,您可以使用过滤器,这是一种剔除不需要的数据并留下您想要的确切结果的方法。在访问控制列表中,您可以使用过滤器来更具体地确定您要授予的访问权限。请看下面一行:

to dn.subtree="ou=people,dc=example,dc=com" attrs="userPassword"
   by dn.exact="cn=admin,ou=meta,dc=example,dc=com" write
   by * none

在这个例子中,我们已经声明我们希望这个应用于所有在ou=people,dc=example,dc=com下的东西,以及任何可能在那里找到的名为userPassword的属性。在这种情况下,属性userPassword就是过滤器。我们给予管理员用户对userPassword的写访问权限,一切都将被无声地拒绝。

关于访问控制列表的更多信息,手册页是很好的资源,OpenLDAP 管理员指南也很好: www.openldap.org/doc/admin24/access-control.html

定义我们的访问控制列表

现在,我们将带您浏览我们将在example.com LDAP DIT 中使用的访问控制列表。这是我们想要做的:

  • 我们希望用户更改自己的密码并绑定。
  • 我们想要创建一个能够代表用户绑定的meta用户组。
  • 我们希望一个管理组能够管理用户的条目。

我们将为我们的系统 root 用户授予对 DIT 的管理访问权限。这可以在以后的阶段删除,但它为我们提供了访问权限,以防我们的访问列表出错。这与在cn=config数据库上提供的默认访问相同。

to *
     by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
     by * break

这表示根用户(uid 0, gid 0)被允许manage整个 DIT (to *)。然后我们进行break处理,并进入下一个访问列表。正如我们之前提到的,这个用户是由外部提供者进行认证的(系统认证,或 PAM)。当我们提供-Y EXTERNAL时,LDAP 将允许访问 UID 和 GID 0(或 root 用户),而不提示身份验证本身。

接下来,我们将定义对密码信息的访问。正如我们前面提到的,访问控制列表是自顶向下读取和实现的。将敏感的访问控制列表放在顶部很重要,这样它们就不会被更高的条目覆盖。

在本节中,我们限制了对用户密码信息的访问,这些密码信息存储在整个 DIT 的属性userPasswordshadowLastChangeentrymember中。entry是一个特殊的伪属性,我们必须指定它来访问一个条目,而member是用来访问组成员。

我们将只允许管理员拥有特殊访问权限。webadmin用户将用于从我们的 web 服务器绑定到我们的 LDAP 服务器,以便我们的 web 用户可以进行身份验证。我们只允许 TLS 安全强度因子(tls_ssf)等于或大于 128 的连接访问这些属性。我们将在本章的“使用 TLS 保护 SLAPD”一节中进一步解释ssf,但是现在,tls_ssf指定了访问这些属性所需的最小 TLS 密钥大小,这意味着只有当这些属性具有足够安全的传输层时,我们才允许访问这些属性。

Note

我们将很快解释安全强度因素。您可以使用其他选项来限制对您的属性的访问,例如指定接受连接的对等名称或域。有关该列表和一般访问控制列表的更多信息,请参见 www.openldap.org/doc/admin24/access-control.html

我们授予anonymous auth访问权;也就是说,客户端不需要绑定(或认证)到我们的 LDAP 服务器来进行认证。有三种方法进行身份验证;一种是不提供用户名或密码(匿名),另一种是只提供用户名,另一种是提供用户名和密码。在没有严格准入条件的情况下,不特别推荐匿名。如果您的 LDAP 服务器在互联网上是公开的,就不应该使用匿名。

Note

您可以使用用户和密码对 LDAP 服务器进行初始身份验证,以执行绑定操作(对用户进行身份验证)。在本章的后面,当我们使用 Apache 进行身份验证时,我们将向您展示如何做到这一点。

匿名认证对于我们实现单点登录服务是必需的,我们将在本章的“单点登录:集中式 Linux 认证”部分解释这一点。只有当匿名的 TLS 安全强度因子(tls_ssf)为 128 时,我们才能保证匿名的身份验证。我们还允许用户通过self write访问来更改他们自己的密码细节。

清单 16-2 中的最后一行很重要。这是一个控制语句,用于阻止访问列表中的下一级访问。

olcAccess: {1}to attrs=userPassword,shadowLastChange,entry,member
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" tls_ssf=128 auth
        by anonymous tls_ssf=128 auth
        by group.exact="cn=admins,ou=groups,dc=example,dc=com" tls_ssf=128 write
        by self tls_ssf=128 write
        by * tls_ssf=128 search
        by * none stop
Listing 16-2.Access List for Sensitive Attributes

它表示任何其他用户(*)没有访问权限(none),然后停止进一步处理。

我们已经说过,秩序很重要。当一个访问请求进入您的 LDAP 主机时,访问控制列表被解析,如果发现匹配,访问被授予或拒绝。您可以通过将访问控制列表按照请求最多的访问到最少的访问的顺序排列来加快您的访问请求。您希望所有这些常见的请求位于访问控制列表的顶部,不太常见的请求位于底部。假设在这个例子中,一些元用户可以访问我们的目录服务器的各个部分,并且这些用户有最常见的访问请求。这就是为什么我们将处理元用户组的访问控制放在列表顶部,就在用户密码条目的下面。

分支ou=meta包含我们用来将我们的认证代理到我们的目录服务器的用户。我们并不总是要求用户直接绑定到我们的目录服务器,但是有时我们仍然希望他们能够根据目录服务器进行身份验证,比如当我们执行 web 身份验证时。您已经看到,我们已经将用户密码条目的授权访问权授予了webadmin。现在,我们宣布这些域名能够看到自己的信息。

to dn.children="ou=meta,dc=example,dc=com"
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" read
        by group.exact="cn=admins,ou=groups,dc=example,dc=com" write
        by self read

我们允许cn=admins组对这个组织单元进行write访问,在这个组中,我们将把我们的系统管理员用户和read访问放在元用户本身。这防止了在ou=meta组织单元下定义的用户能够改变他们自己的任何条目,并且这为那些用户提供了更大的安全性。

接下来,我们授予对ou=people分支下所有内容的访问权,记住我们已经在前面的访问控制列表中定义了对用户密码属性的访问。先前的访问定义将覆盖我们在这里为先前定义的属性详述的任何访问。管理员帐户至少需要读取权限,我们已经给了adminswrite访问权限。我们将希望admins组也不时改变细节。webadmin用户只需要只读权限。我们用关键字self赋予条目本身的读权限。

to dn.children="ou=people,dc=example,dc=com"
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" read
        by group.exact="cn=admins,ou=groups,dc=example,dc=com" write
        by self write
        by users read

在您的网络中,您可能有不同的需求,将self访问改为write是很常见的。该设置将使用户能够更改定义其个人信息的属性细节,而read access 则不能。

在下面的代码中,我们授予对ou=groups分支的访问权,在那里我们将保存所有的组信息。

to dn.children="ou=groups,dc=example,dc=com"
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" read
        by group.exact="cn=admins,ou=groups,dc=example,dc=com" write
        by anonymous read

如您所见,这类似于ou=people分支,相同的管理员帐户拥有相同的访问权限。然而,我们已经允许认证用户通过指定users read来读取组。

接下来,我们有ou=hosts组织单位。有人把这个单位命名为machines,但选择权在你。它将保存您所有的主机信息、IP 地址、位置等等。我们已经使用了subtree的作用域,除了cn=admins组之外,对任何事物都授予了最小的write访问权限。

to dn.children="ou=hosts,dc=example.com"
        by group.exact="cn=admins,ou=groups,dc=example,dc=com" write
        by anonymous read

这里,cn=admins组将需要write访问。我们给予anonymous客户端读权限,这些客户端没有建立绑定连接(未经身份验证)。各种应用程序可以利用ou=hosts组织单元,包括 Samba 这样的应用程序。

最后一条规则是全面拒绝规则。这将强制拒绝所有其他访问。这基本上是多余的,因为任何未被授予显式访问权限的内容都将被拒绝;但是,它显示了您的访问控制列表集的结尾,并防止可能出现在它下面的任何访问控制列表被错误地读入。

to * by * none stop

这里的通配符匹配所有内容,这意味着任何访问排序都会被拒绝,并且所有进一步的处理都会被 control 字段中的 stop 选项停止。其他可用的加工控制有breakcontinue

在匹配时,break控制选项将停止访问控制组中的进一步处理,并跳到下一个。匹配后,continue选项将继续进一步处理访问控制组,允许授予增量特权。stop选项只是立即停止任何进一步的处理,并且是默认的控件。清单 16-3 显示了我们完整的访问控制列表。

Note

你在清单 16-3 中看到的olcAccess:指令清单被分成不同的行,只是为了文档的清晰。如果您对空白有任何错误,请尝试将每个指令放在一行中。

dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to *
        by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
        by * break
-
add: olcAccess
olcAccess: {1}to attrs=userPassword,shadowLastChange,entry,member
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" tls_ssf=128 auth
        by anonymous tls_ssf=128 auth
        by group.exact="cn=admins,ou=groups,dc=example,dc=com" tls_ssf=128 write
        by self tls_ssf=128 write
        by * tls_ssf=128 search
        by * none stop
-
add: olcAccess
olcAccess: {2}to dn.children="ou=meta,dc=example,dc=com"
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" read
        by group.exact="cn=admins,ou=groups,dc=example,dc=com" write
        by self read
-
add: olcAccess
olcAccess: {3}to dn.children="ou=people,dc=example,dc=com"
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" read
        by group.exact="cn=admins,ou=groups,dc=example,dc=com" write
        by self write
        by users read
-
add: olcAccess
olcAccess: {4}to dn.children="ou=groups,dc=example,dc=com"
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" read
        by group.exact="cn=admins,ou=groups,dc=example,
dc=com" write
        by anonymous read
-
add: olcAccess
olcAccess: {5}to dn.children="ou=hosts,dc=example.com"
        by group.exact="cn=admins,ou=groups,dc=example,dc=com" write
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" search
-
add: olcAccess
olcAccess: {6}to * by * none
Listing 16-3.The Complete Access Control List

关于更新访问控制列表,有一些事情需要注意。在清单 16-3 中,我们看到我们正在使用 LDIF 格式来添加这些访问列表。先拿第一节来解释一下。

dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}<access list>
-
add: olcAccess
olcAccess: {1}<access list>

第一行是 DN,在本例中是主配置数据库,我们希望在上面工作。第二行是变更类型,即修改。对于第一个索引元素{0},我们需要使用替换修改类型。对于之后的访问列表,我们需要添加列表。当使用动态访问列表和ldapmodify时,需要记住一些规则。

  • 如果替换一个索引元素,需要加载完整的访问列表。
  • 只能用一个add指令追加到访问列表的末尾。
  • 访问列表是从头到尾读取的。

我们现在可以将这些访问列表指令放入一个名为access.ldif的文件中,然后使用ldapmodify来应用它们。

$ sudo ldapmodify -H ldapi:/// -Y EXTERNAL  -f access.ldif

我们将很快解释如何搜索测试这些。

使用 slapd 守护程序

您可以通过两种方式运行您的slapd守护进程:使用slapd.d配置引擎(动态配置)或不使用它。如前所述,配置引擎支持使用 LDIF 语法和 LDAP 命令动态更改 SLAPD 配置。

另一种方法是加载一个有旧样式指令的slapd.conf文件。当我们把我们的exampleactive.schema文件转换成 LDIF 格式时,我们看到了一个slapd.conf语法的例子。

这两种方式都受支持,但是用旧式的slapd.conf运行会被否决,所以我们不建议用它启动slapd。您可以通过发出以下命令将您的旧式slapd.conf转换为动态 LDIF 配置引擎(SLAPD 不能已经在运行):

$ sudo slapd -f slapd.conf -F slapd.d -u openldap -g openldap

这类似于我们之前运行的slaptest命令。您会注意到这是在前台运行的,当它试图启动时,您可以看到是否有任何问题。对于 CentOS 主机,运行 OpenLDAP 的用户可以使用-u ldap -g ldap,而不是运行 Ubuntu 主机的-u openldap。然后-f slapd.conf指向我们想要读入的配置文件,-F指向slapd.d目录,该目录将保存您的配置引擎的 LDIF 文件。

当您的slapd实例启动时,您将看到slapd.d目录现在包含几个文件和目录。这些文件包含您在slapd.conf中指定的 LDAP 设置以及其他 LDIF 文件格式的附带文件。

Note

您可以在 https://help.ubuntu.com/lts/serverguide/openldap-server.html 查看更多关于管理您的 OpenLDAP 服务器配置的信息。

对于故障排除,在前台以调试模式运行 SLAPD 守护进程来查看服务正在做什么通常是很有用的。为此,您可以发出以下命令(在 Ubuntu 上,使用–u ldap–g ldap表示 CentOS):

$ sudo slapd -F /etc/ldap/slapd.d -d -1 -u openldap -g openldap -h ldapi:///

您可以在 CentOS 或 Ubuntu 上使用以下命令手动启动或停止 SLAPD 服务器服务:

$ sudo systemctl start slapd
$ sudo systemctl stop slapd

然后,您可以使用以下命令检查守护程序的状态:

$ sudo systemctl status slapd

您可以使用以下命令在启动时启用:

$ sudo systemctl enable slapd

一旦服务启动,您可以跟踪日志以查看任何日志记录信息。

$ sudo journalctl -xfe -u slapd

您可以使用日志来监控和解决您的访问请求问题。

用 TLS 保护 SLAPD

因为 LDAP 通常包含敏感数据,所以确保在 LDAP 客户端和 LDAP 服务器之间传输的数据是加密的是一个很好的预防措施。LDAP 可用于地址簿之类的东西,但也可用于存储更敏感的数据,如密码、员工详细信息等。

我们可以配置传输层安全性(TLS)来保护我们的网络传输。TLS 用于加密我们的服务器和它的客户端之间的通信。我们将创建一个 LDIF 文件来添加这些记录。

dn: cn=config
changetype: modify
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ldap/certs/cacert.pem
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ldap/certs/ldap.example.com.cert
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ldap/certs/ldap.example.com.key
-
add: olcTLSVerifyClient
olcTLSVerifyClient: allow

在这里,我们从我们的私钥创建一个证书文件,并添加细节。发布证书文件被添加到cacert.pem文件中。我们还为TLSVerifyClient指定了allow。这意味着我们将验证在 TLS 交换期间提交给我们的任何客户端证书,但如果我们无法验证证书,我们不会失败。其他选项有neverallowtrydemand. trydemand未验证证书连接失败。

我们像以前一样用ldapmodify来应用tls.ldif

$ sudo ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f tls.ldif

我们可以验证我们仍然可以使用 root 用户来查询 LDAP 服务器并查看我们的 TLS 条目。

$ sudo ldapsearch -H ldapi:/// -Y EXTERNAL -b "cn=config" -LLL  -Q |grep TLS
olcTLSCACertificateFile: /etc/ldap/certs/cacert.pem
olcTLSCertificateFile: /etc/ldap/certs/ldap.example.com.cert
olcTLSCertificateKeyFile: /etc/ldap/certs/ldap.example.com.key

我们将无法针对ldap:/// URI 发出ldapsearch请求,直到我们对/etc/ldap/ldap.conf文件进行一些调整。我们将在下一节中这样做(在前面的章节中,我们使用了 Unix 套接字ldapi:///)。

与 SSF 合作

我们现在将讨论安全强度因子指令,或ssf。我们可以为我们的连接定义最小的安全强度,并为更敏感的角色指定更高安全强度的通信。

例如,如果我们有一个如下所示的sec.ldif文件,它描述了我们对某些连接类型所要求的安全因素:

dn: cn=config
changetype: modify
add: olcSecurity
olcSecurity: ssf=128 update_ssf=256 simple_bind=128 tls=256

ssf=128设置描述了基于加密密钥大小的服务所需的整体安全强度因子。更大的密钥意味着更强的加密强度。

如果我们在全局 DIT 中定义了ssf安全性,那么它是为所有其他 DIT 定义的。我们将其设置为 128,这是合理的,并且我们可以限制具有更强安全需求的更敏感的 DITs。update_ssf=256设置描述了目录更新所需的整体安全强度,simple_bind=128设置是simple_bind操作所需的安全因素。ssf值如下:

  • 0(零)表示无保护。
  • 1仅表示完整性保护。
  • 56允许 DES 或其他弱密码。
  • 112允许三重 DES 和其他强密码。
  • 允许 RC4、河豚和其他强密码。
  • 256 允许 AES、SHA 密码。

默认为0。您可以在访问控制列表中组合它们,根据连接的安全强度来控制这些连接可以访问的内容。

我们目前不打算应用这个配置,但是我们已经在我们的访问控制列表中用ssf_tls=128保护了我们的敏感用户数据。

如果我们想对用户密码或其他敏感数据进行调整,我们现在必须用 TLS 证书的详细信息设置 LDAP 客户端。

设置您的 LDAP 客户端

Ubuntu 和 CentOS 都使用ldap.conf文件为客户端配置系统范围的 LDAP 默认值(还有另一种使用sssd程序的方法,我们将在“单点登录:集中式 Linux 身份验证”一节中讨论)。使用 OpenLDAP 库的应用程序将使用这些文件来获取 LDAP 细节。你会在目录/etc/ldap中找到 Ubuntu 的文件,在/etc/openldap中找到 CentOS 的文件。

Note

重要的是,不要把这个与libnss-ldap文件提供的文件混淆,这个文件也叫做ldap.conf,在两个发行版中都可以找到:/etc/ldap.conf。该文件用于为您的系统配置用户和主机信息,而/etc/(open)ldap/ldap.confldapmodifyldapadd等 OpenLDAP 工具使用。

您需要通过添加以下几行文本来编辑您的ldap.conf文件。在我们的例子中,我们将稍微欺骗一下,不用担心为 LDAP 客户端设置客户端 SSL 证书。如果这台主机用于复制我们的 LDAP 服务器,我们肯定会确保服务器和客户机都启用了 SSL 验证。详情请查看ldap.confman页面。

URI ldap://ldap.example.com/
BASE dc=example,dc=com
TLS_CACERT /etc/ldap/certs/cacert.pem
TLS_REQCERT demand

URI指向我们的 LDAP 服务器。BASE是 LDAP 操作的默认基本 DN。TLS_CACERT指向我们的 CA 证书文件,其中将包含我们的example.com CA 证书。在某些客户端上,您可能已经将 CA 证书安装到默认位置/etc/ssl/certs。我们在TLS_REQCERT字段中指定的demand意味着我们将尝试验证证书,如果无法验证,我们将取消连接(这是默认设置)。其他选项有try,表示如果不提供证书,连接将继续,但如果提供了一个坏的证书,则立即停止连接;allow,这意味着如果提供的证书是坏的,会话仍然可以继续;和never,这意味着在建立连接之前,您的主机不会请求或检查服务器证书。

如果您正在查看 CentOS 主机,您很可能会在/etc/pki/tls/certs目录中找到您的 SSL CA 证书。

LDAP 管理和工具

那么如何用 LDAP 管理条目呢?有几种工具可用于此目的。使用命令行,您可以从文本文件添加条目、搜索现有条目以及删除条目。文本文件必须是一种叫做 LDIF 的格式。LDIF 文件的格式如下:

dn: <dn entry>
objectclass: <objectclass to be included>
attribute: <attribute value described in an objectclass>

通常,为处理的不同部分创建单独的 LDIF 文件是个好主意。比如ou=people,dc=example,dc=com下的东西都可以在people.ldif里,ou=groups,dc=example,dc=com下的东西都可以在groups.ldif里。或者,对于新的 LDAP 服务器,您可以将所有条目放在一个文件中,但是要注意,在具有现有条目的 LDAP 服务器中,如果您再次尝试添加现有条目,将会出现错误。在这种情况下,可以在条目的每一行的开头使用#符号,在 LDIF 文件中注释掉该条目。LDAP 工具可以通过使用–f filename选项来使用 LDIF 文件,我们将在下面的章节中详细介绍。

管理条目的另一种方法是使用众多可用的 GUI 工具之一。我们将在“LDAP 帐户管理器:基于 web 的 GUI”一节中展示如何安装和配置基于 Web 的 GUI。

LDIFs 和添加用户

DIT 的顶端是rootDN。DIT 从dcObject类的声明开始。下面是我们将用来填充 LDAP 服务器的 LDIF 文本文件的一个片段:

dn: dc=example,dc=com
objectclass: dcObject
objectClass: organization
dc: example
o: example

这表明我们将创建rootDN dc=example,dc=com。根据 Ubuntu 上core.schema中的dcObject对象类,我们必须包含dc属性。让我们看看来自core.schema文件的对象类声明:

objectclass ( 1.3.6.1.4.1.1466.344 NAME 'dcObject'
         DESC 'RFC2247: domain component object'
         SUP top AUXILIARY MUST dc )

您可以看到我们如何在 DN: dc=example, dc=com的声明中使用前面的 object 类。我们指定了被指示使用的dc属性,由对象定义中的MUST子句指示。记住,这是一个AUXILLARY对象类,不能用于创建条目。我们需要一个STRUCTUAL对象类,而organization就是这样一个类。它需要organizationo属性。我们将在下一节添加用户时添加这个条目。这应该是您添加到 LDAP 服务器的第一个条目。

这里需要注意的是,根据你使用的是不是 Ubuntu,rootDN可能已经存在。您可以通过运行以下命令来找到答案:

$ sudo ldapsearch -D "cn=admin,dc=example,dc=com" -b "dc=example,dc=com" –ZZ -H ldap://ldap.example.com –W
# example.com
dn: dc=example,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: example
dc: example

如果是这种情况,请不要将其包含在users.ldif中,否则当您尝试添加它时,会出现如下错误:

adding new entry "dc=example,dc=com"
ldap_add: Already exists (68)

接下来,我们希望在我们的组织中设置用户,因此我们现在将声明我们的people组织单位。如果需要的话,我们可以将这个部分单独放入一个新文件中,只存放我们的people条目。

dn: ou=people,dc=example,dc=com
objectclass: organizationalUnit
ou: people

您可以看到,LDIF 格式要求声明 DN,后面是我们想要使用的对象类和属性。每个声明都应该用一个空行隔开。声明的顺序也很重要;在创建组织单位之前,您不能在ou=people,dc=example,dc=com中创建用户。对象类organizationalUnit要求我们像这里的ou: people一样声明ou属性。

现在我们要添加一个用户,jsmith

dn: uid=jsmith,ou=people,dc=example,dc=com
objectclass: top
objectclass: person
objectclass: posixAccount
objectclass: exampleClient
cn: Jane Smith
sn: Smith
uid: jsmith
uidNumber: 1000
gidNumber: 1000
exampleActive: TRUE
homeDirectory: /home/jsmith
userPassword: {SSHA}IZ6u7bmw12t345s3GajRt4D4YHkDScH8

所以,我们先来看看 DN。您可以看到我们使用uid属性声明了我们的 DN。除了uid=jsmith: cn=Jane Smithuid=jane.smith@example.com,我们还可以在这里使用一些变体。您最终在您的系统上使用哪一个取决于您认为哪一个最适合您的服务器(记住索引),并且您应该意识到这些必须是唯一的(SINGLE-VALUE)。

接下来,我们必须声明对象类toppersontop对象类,一个ABSTRACT超类,需要提供其他对象类,并用于终止层次结构。

person对象类提供了sn(姓氏)和cn(常用名)属性。让我们快速查看一下这个对象类的模式。

objectclass ( 2.5.6.6 NAME 'person'
        DESC 'RFC2256: a person'
        SUP top STRUCTURAL
        MUST ( sn $ cn )
        MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )

person对象类的父类是top ( SUP top),它是一个STRUCTURAL对象类,这意味着它的属性可以在 d it 中形成一个条目。对于person类,我们MUST提供了一个cn和一个sn条目。我们MAY提供userPasswordtelephoneseeAlsodescription

包含posixAccountexampleClient对象类是可选的。对象类posixAccount将提供对 Unix/Linux 主机有用的属性,例如userPassworduiduidNumbergidNumberhomeDirectory。我们在模式部分创建的exampleClient对象类提供了exampleActive属性,我们可以用它来激活和停用我们的用户。请注意,布尔属性如exampleActive的属性值必须是大写的,因为它是一个SINGLE-VALUE属性,所以我们只能声明它一次。

我们现在将添加groups作为organizationalUnit,这样我们就可以利用组来管理对用户的访问。同样,我们可以为我们的组创建一个新的 LDIF 文本文件。

dn: ou=groups,dc=example,dc=com
objectclass: top
objectclass: organizationalUnit
ou: groups

您可以看到为groups创建组织单位与我们之前声明people组织单位的方式相似。按照模式定义的要求,我们使用并声明了ou属性来命名我们的 DN。接下来,我们声明将用于对管理员进行分组的组admins

dn: cn=admins,ou=groups,dc=example,dc=com
objectclass: top
objectclass: groupOfNames
cn: admins
member: uid=ataylor,ou=people,dc=example,dc=com

我们用groupOfNames对象类声明一个组列表,这允许我们只添加成员。我们也可以使用posixGroup对象类,这也允许我们使用gidNumbers。我们可以通过在单独一行添加member: DN来添加任意多的成员。

现在我们可以看看如何将我们的详细信息添加到 LDAP 数据库中。为此,我们将使用 OpenLDAP 附带的ldapadd工具。

从 LDIF 文件添加用户

LDAP 工具都共享一组公共选项,您可以提供这些选项来连接到 LDAP 服务器。OpenLDAP 客户端工具可用于连接其他软件制造商提供的其他 LDAP 服务器。表 16-4 列出了大多数 LDAP 工具可用的公共选项。

表 16-4。

Common LDAP Tool Options

| [计]选项 | 描述 | | --- | --- | | `-x` | 执行简单绑定。 | | `-v` | 指定详细输出。 | | `-W` | 提示输入密码。 | | `-f` | 指向一个输入文件,该文件在不同的工具上下文中可以是不同的类型。 | | `-D` | 指定要绑定的 DN。此 DN 必须具有适当的访问权限才能处理条目。 | | `-Z` | 尝试使用 TLS 建立 LDAP 连接。`ZZ`表示在继续连接之前必须成功使用 TLS。 | | `-Y` | 指定连接到 LDAP 服务器的 SASL 身份验证机制。您必须将 SASL 配置为使用此选项。 | | `-X` | 指定 SASL authzid,或为 SASL 绑定请求的授权 id。 | | `-U` | 指定 SASL 绑定的 SASL 身份验证 id 或身份验证 ID。 | | `-b` | 指定基本 DN。不用查询整个树,你可以指定一个基数开始,比如`ou=people,dc=example,dc=com`。 | | `-s` | 指示搜索查询的范围。可以是`base`、`one`、`sub`或`children`。 |

最常见的是,您将使用–D选项来指定您正在绑定的用户,以进行查询或修改,并将使用-xW选项来执行简单的绑定,并被提示输入密码(与 SASL 绑定–Y相反)。我们在表 16-4 中显示的一些选项并不适用于所有的 LDAP 工具。LDAP 工具命令的语法通常如下所示:

ldaptool <options> filter entry

有几个可用的 LDAP 工具。主要的选项有ldapaddldapmodifyldapsearchldapdelete,正如我们所说的,它们都共享一些或所有前面显示的公共选项。有关可用的确切选项,请参考这些工具的man页面。在接下来的章节中,我们将举例说明如何使用这些命令和选项。

既然我们已经创建了 LDIF 文件,那么向 LDAP 服务器添加用户就很容易了。让我们看看完整的 LDIF 档案。正如我们已经提到的,我们需要在这个文件的顶部添加dc条目,或者我们的 DIT 的顶层(如果它已经存在,记得从文件中删除它)。如果您要添加数百个用户,您可能希望使用一个脚本或程序来处理现有用户(或在尝试重新添加现有用户或 DN 时生成的 LDAP 错误)。

$ sudo cat users.ldif
dn: dc=example,dc=com
objectclass: dcObject
objectclass: organizationalUnit
dc: example
ou: example

dn: ou=people,dc=example,dc=com
objectclass: organizationalUnit
ou: people

dn: uid=jsmith,ou=people,dc=example,dc=com
objectclass: top
objectclass: person
objectclass: posixAccount
objectclass: exampleClient
cn: Jane Smith
sn: Smith
uid: jsmith
uidNumber: 1000
gidNumber: 1000
exampleActive: TRUE
homeDirectory: /home/jsmith
userPassword: {SSHA}IZ6u7bmw12t345s3GajRt4D4YHkDScH8

dn: uid=ataylor,ou=people,dc=example,dc=com
objectclass: top
objectclass: person
objectclass: posixAccount
objectclass: exampleClient
cn: Angela Taylor
sn: Taylor
uid: ataylor
uidNumber: 1002
gidNumber: 1000
exampleActive: TRUE
homeDirectory: /home/ataylor
userPassword: {SSHA}PRqu69QU5WK5i8/dvqQuvFXo0xJ74OFG

dn: ou=meta,dc=example,dc=com
objectclass: organizationalUnit
objectclass: top
ou: meta

dn: cn=webadmin,ou=meta,dc=example,dc=com
objectClass: organizationalRole
objectclass: simpleSecurityObject
userPassword: {SSHA}KE0JMvJjYjQ/9lpigDCbLla5iNoBb8O8

dn: ou=groups,dc=example,dc=com
objectclass: top
objectclass: organizationalUnit
ou: groups

dn: cn=staff,ou=groups,dc=example,dc=com
objectclass: top
objectclass: posixGroup
gidNumber: 1000
cn: staff

dn: cn=admins,ou=groups,dc=example,dc=com
objectclass: top
objectclass: groupOfNames
cn: admins
member: uid=ataylor,ou=people,dc=example,dc=com

dn: ou=hosts,dc=example,dc=com
objectclass: top
objectclass: organizationalUnit
ou: hosts

我们现在将使用文件users.ldif添加我们的用户。ldapadd工具是多功能的,并且有许多选项。我们将使用它的方式如下:

$ sudo ldapadd -D "cn=admin,dc=example,dc=com" -ZZ -H ldap://ldap.example.com
 -xWv -f users.ldif

ldapadd命令可以使用 SASL 认证方法或简单方法。正如我们前面提到的,如果您设置了 SASL,您可以在不通过网络发送密码的情况下进行绑定,使用简单身份验证方法,密码被发送到 LDAP 服务器上进行验证,因此应该通过 TLS 之类的安全传输进行发送。-x会让ldapadd使用简单的方法。-W告诉ldapadd我们希望被提示输入密码。-v就是信息要啰嗦。当您指定-D时,您给出了想要绑定的用户名。在本例中,我们使用的是cn=admin,dc=example,dc=com用户,您可能还记得,这是我们添加到SLAPD server earlier中的rootDN-h开关是主机名,ldap.example.com. -Z告诉命令使用STARTTLS,或者建立到 LDAP 主机的 TLS 连接,但是如果您已经在/etc/ldap/ldap.conf/etc/openldap/ldap.conf中设置了 TLS,您的命令将会失败。最后,-f表示我们想要用来添加用户的文件,users.ldif

这里使用的选项对于所有其他 LDAP 工具都是一样的;更多详情见man页。当您发出这个命令时,您将得到类似这样的内容:

jsmith@ldap:/etc/ldap$ ldapadd -xWv -D cn=admin,dc=example,dc=com
-h ldap.example.com -Z -f users.ldif
ldap_initialize( ldap://ldap.example.com )
Enter LDAP Password:
add objectclass:
        top
        person
        exampleClient
        posixAccount
add cn:
        Jane Smith
add sn:
        Smith
add uid:
        jsmith
add uidNumber:
        1000
add gidNumber:
        1000
add exampleActive:
        TRUE
add homeDirectory:
        /home/jsmith
add userPassword:
        {SSHA}IZ6u7bmw12t345s3GajRt4D4YHkDScH8
adding new entry "uid=jsmith,ou=people,dc=example,dc=com"
modify complete

如果成功,您将看到“修改完成”消息。如果出现问题,您将在控制台输出上收到一个错误,您可以使用journalctl来进一步检查生成的日志条目。

$ journalctl –xe –u slapd

如果您在日志中没有看到足够的细节,记得像前面描述的那样调整您的LogLevel条目。

搜索您的 LDAP 树

现在我们的 LDAP 数据库中有了一些条目,我们可以搜索它以确保我们可以返回有用的信息。让我们看看搜索 LDAP 目录的方法。

$ ldapsearch -xvW -H ldap://ldap.example.com -ZZ \
-D cn=admin,dc=example,dc=com \
-b ou=people,dc=example,dc=com -s sub \
'(&(&(objectclass=person)(uid=jsmith))(exampleActive=TRUE))' cn

我们用于搜索的参数类似于我们用于ldapadd命令的参数。我们首先指定我们正在执行一个带有详细输出的简单绑定,我们希望得到输入密码的提示,-xvW. -h声明我们想要连接的主机,-Z说尝试使用 TLS 建立连接(一个-ZZ意味着在继续之前确认 TLS 连接成功)。安吉拉·泰勒是我们放在cn=admins,ou=groups,dc=example,dc=com的用户;记住,我们已经通过访问控制列表给了write访问ou=people,dc=example,dc=com下所有条目的权限。我们刚刚将用户简·史密斯添加到我们的 LDAP 目录中,我们将进行搜索以查看她的详细信息。

在我们的ldapsearch命令中,您可以看到我们包含了一个过滤器来利用索引并减少我们的搜索响应时间。我们知道所有用户条目都有对象类person。正如我们解释过的,在我们的slapd.conf文件中,所有的对象类都被索引,所以选择一个你知道在你要找的实体中的对象将会加快你的搜索。uid属性也被编入索引,所以我们还想过滤我们正在寻找的条目的uid。我们的搜索过滤器如下所示:

 (&(&(objectclass=person)(uid=jsmith))(exampleActive=TRUE)),

这些是从里到外读的;让我们开始第一部分。

&(objectclass=person)(uid=jsmith)

这意味着过滤对象类别为person AND uid is jsmith的条目。第二部分是这样的:

(&(<first match>)(exampleActive=TRUE))

这里,我们在第一次匹配时进行匹配,并且账户是活动的,如exampleActive=TRUE所示。&操作符表示我们正在搜索一个和另一个。我们也可以使用|符号来表示我们想要搜索其中一个。

我们指定我们希望开始搜索的 DIT 树的基础,-b ou=people, dc=example,dc=com,,我们的搜索范围是-s sub,或者它下面的所有内容。最后,我们在寻找简的普通名字,或cn。该搜索的结果将如下所示:

ldap_initialize( ldap://ldap.example.com )
filter: (&(&(objectclass=person)(uid=jsmith))(exampleActive=TRUE))requesting: cn
# extended LDIF
#
# LDAPv3
# base <ou=people,dc=example,dc=com> with scope subtree

# filter: (&(&(objectclass=person)(uid=jsmith))(exampleActive=TRUE))
# requesting: cn
#

# jsmith, People, example.com
dn: uid=jsmith,ou=people,dc=example,dc=com
cn: Jane Smith

# search result
search: 3
result: 0 Success

# numResponses: 2
# numEntries: 1

在这里,您可以看到我们已经返回了我们正在寻找的 DN 和那个条目的公共名称。接下来,我们来看看删除条目。

从 LDAP 目录中删除条目

另一件你需要经常做的事情是删除 LDAP 目录中的条目。要删除条目,使用ldapdelete命令。同样,这采用了与ldapaddldapsearch相同的参数。要删除多个条目,您可以输入文本文件,也可以单独删除条目。假设我们在一个新的users.ldif文件中有以下条目,我们想删除它们。

uid=jbob,ou=people,dc=example,dc=com uid=tbird,ou=people,dc=example,dc=com

我们现在可以将这两个条目添加到名为deluser.ldif的文件中,然后运行带有-f参数的ldapdelete命令,如下所示:

ldapdelete -xvW -D uid=ataylor,ou=people,dc=example,dc=com \
-h ldap.example.com -Z -f deluser.ldif
ldap_initialize( ldap://ldap.example.com )
deleting entry "uid=jbob,ou=people,dc=example,dc=com"
deleting entry "uid=tbird,ou=people,dc=example,dc=com"

因此,这些条目已不在我们的目录中,并已被删除。

Note

OpenLDAP 不区分大小写,这意味着uid=jsmith,ou=people,dc=example,dc=comuid=jSmith,ou=people,dc=example,dc=com被同等对待。尝试添加两个 Jane Smiths,一个带有小写 S,一个带有大写 S,将返回重复错误。

密码策略覆盖

通过设置密码策略覆盖,我们可以更好地控制密码老化和更改历史。如前所述,覆盖层为 OpenLDAP 服务器提供了额外的功能。我们希望将我们的密码时效设置为7776000 (90 天(秒)】,将我们的密码历史设置为3,这意味着我们将存储用户提供的前三个密码,这样他们就不能一直使用同一个密码。密码至少需要八个字符。

添加我们的策略覆盖

是时候添加我们的密码策略覆盖了。如前所述,覆盖层提供了 OpenLDAP 服务器通常不提供的某些附加功能。在本例中,我们声明了 ppolicy 覆盖,它有助于管理我们的密码。

我们将创建一个名为ppolicy.ldif的文件,其内容如下:

dn: ou=policies,dc=example,dc=com
objectClass: organizationalUnit
ou: policies

dn: olcOverlay={0}ppolicy,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcPPolicyConfig
olcOverlay: {0}ppolicy
olcPPolicyDefault: cn=default,ou=policies,dc=example,dc=com
olcPPolicyHashCleartext: FALSE
olcPPolicyUseLockout: TRUE
olcPPolicyForwardUpdates: FALSE

首先,您可以看到我们创建了一个organizationalUnit,一种在我们的 DIT 中收集类似项目的方式,称为ou=policies,dc=example,dc=com。这提供了放置我们稍后将定义的默认策略的结构。

ppolicy 覆盖提供了某些功能,可让您更好地控制 LDAP 服务器上的密码安全性。OpenLDAP 本身不提供密码管理特性,比如密码到期和密码历史。这个覆盖允许您声明一个策略,或者将不同的策略与 DIT 树的不同部分相关联。这里我们用 DN cn=default,ou=Policies,dc=example,dc=com声明一个默认策略。我们还声明希望使用策略的锁定功能。这允许我们在请求客户端被锁定时向其发回消息。这可以为攻击者提供信息,然后他们将知道用户名是否存在,所以您可能需要关闭它。我们在这个阶段也关闭HashCleartextForwardUpdate

我们现在必须定义实际的策略,该策略设置我们希望在密码体系中实施的值。为此,我们需要将以下 LDIF 添加到我们的 LDAP 服务器:

dn: cn=default,ou=policies,dc=example,dc=com

objectClass: top

objectClass: device

objectClass: pwdPolicy

cn: default

pwdAttribute: userPassword

pwdMaxAge: 7776000

pwdExpireWarning: 6912000

pwdInHistory: 3

pwdCheckQuality: 1

pwdMinLength: 8

pwdMaxFailure: 4

pwdLockout: TRUE

pwdLockoutDuration: 1920

pwdGraceAuthNLimit: 0

pwdFailureCountInterval: 0

pwdMustChange: TRUE

pwdAllowUserChange: TRUE

pwdSafeModify: FALSE

我们现在已经将密码策略添加到我们的 LDAP 服务器中,它有一些基本设置,如密码年龄(90 天,以秒为单位)和历史密码(3)。这个覆盖现在将使所有密码帐户遵守密码策略。

我们将使用ldapadd来应用这个ppolicy.ldif文件。在本例中,由于我们正在修改 SLAPD 数据库配置,我们将使用本地 root 用户的权限来进行这一更改:

$ sudo ldapadd -H ldapi:/// -Y EXTERNAL -f ppolicy.ldif

Note

参见man slapo-ppolicy页了解密码策略覆盖的更多详情。

测试您的访问控制列表

由于访问控制列表功能不正确,您有时会遇到权限问题。有一个叫做slapacl的工具可以用来测试你的 ACL。此工具通过要授予访问权限的 DN 来测试对属性和对象类的访问权限。例如,如果我们想要确保 DN cn=webadmin,ou=meta,dc=example,dc=com,即我们在认证期间用来绑定我们的 web 服务的用户,拥有对我们的用户 Angela Taylor 的userPassword属性的auth访问权,我们将发出以下命令:

sudo slapacl -F /etc/ldap/slapd.d \
  -b uid=ataylor,ou=people,dc=example,dc=com \
  -D cn=webadmin,ou=meta,dc=example,dc=com \
  -o tls_ssf=128 \
  -v userPassword/auth

slapacl命令需要sudo访问。您需要在 Ubuntu 主机上使用-F /etc/ldap/slapd.d指定您想要测试的slapd config目录;在 CentOS 主机上,您需要使用/etc/openldap/slapd.d目录。-b uid=ataylor,ou=people,dc=example,dc=com DN 是我们想要在其上测试我们的访问的 DN。-D cn=webadmin,ou=meta,dc=example,dc=com是我们要确认的 DN 是否有auth访问uid=ataylor,ou=people,dc=example,dc=com的 DN。–o允许我们提供与slapd访问相关的选项。在这种情况下,因为我们需要模拟我们的 TLS 安全强度因子,所以我们添加了tls_ssf=128–v用于详细说明。我们指定想要测试的属性和认证级别,在本例中是针对访问权限auth的属性userPassword。如您所知,我们至少需要对 DN cn=webadmin,ou=meta,dc=example,dc=comauth访问权,以便与userPassword进行认证。如果我们成功了,我们会得到以下结果:

authcDN: "cn=webadmin,ou=meta,dc=example,dc=com"
auth access to userPassword: ALLOWED

这证实了我们的访问控制列表中的行像我们预期的那样工作。

olcAccess: {1}to attrs=userPassword,shadowLastChange,entry,member
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" tls_ssf=128 auth

我们将进行测试,看看我们是否可以对同一个属性进行写访问,以确认我们的访问控制列表中没有安全漏洞。

sudo slapacl -F /etc/ldap/slapd.d \
  -b uid=ataylor,ou=people,dc=example,dc=com \
  -D cn=webadmin,ou=meta,dc=example,dc=com \
  -o tls_ssf=128 \
  -v userPassword/write
 authcDN: "cn=webadmin,ou=meta,dc=example,dc=com"
write access to uid: DENIED

这是我们所期望的:我们应该被拒绝除了auth访问和以下的一切。您还可以传入其他选项,这些选项允许您根据诸如peernamesssf之类的东西来测试访问。

了解访问控制列表情况的另一个有用的方法是,在测试时在访问控制列表中包含以下内容,这可能很难做到正确:

access to * by * search

您可以将此与修改您的SLAPD中的日志配置结合起来,将您的日志级别更改为如下所示:

olcLogLevel:  416

这将显示搜索过滤器和访问控制列表处理,以及连接管理和配置文件处理。您可以使用journalctl命令来访问日志。当一个请求进来时,它将产生如下输出:

$ journalctl –xe –u slapd

slapd[1350]: conn=1011 op=2 SRCH base="ou=people,dc=example,dc=com" scope=2 deref=0 filter=”(&(objectClass=person)(uid=jsmith))"

slapd[1350]: conn=1011 op=2 SRCH attr=uid

...<snip>...

slapd[1350]: => access_allowed: read access to "uid=jsmith,ou=people,dc=example,dc=com" "uid" requested

slapd[1350]: => acl_get: [1] attr uid

slapd[1350]: => acl_mask: access to entry "uid=jsmith,ou=people,dc=example,dc=com", attr "uid" requested

slapd[1350]: => acl_mask: to value by "uid=ataylor,ou=people,dc=example,dc=com", (=0)

slapd[1350]: <= check a_dn_pat: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth

slapd[1350]: <= check a_dn_pat: *

slapd[1350]: <= acl_mask: [2] applying +0 (break)

slapd[1350]: <= acl_mask: [2] mask: =0

slapd[1350]: => dn: [3] ou=people,dc=example,dc=com

slapd[1350]: => acl_get: [3] matched

slapd[1350]: => acl_get: [3] attr uid

slapd[1350]: => acl_mask: access to entry "uid=jsmith,ou=people,dc=example,dc=com", attr "uid" requested

slapd[1350]: => acl_mask: to value by "uid=ataylor,ou=people,dc=example,dc=com", (=0)

slapd[1350]: <= check a_dn_pat: cn=webadmin,ou=meta,dc=example,dc=com

slapd[1350]: <= check a_group_pat: cn=admins,ou=groups,dc=example,dc=com

slapd[1350]: <= acl_mask: [2] applying write(=wrscxd) (stop)

slapd[1350]: <= acl_mask: [2] mask: write(=wrscxd)

slapd[1350]: => slap_access_allowed: read access granted by write(=wrscxd)

slapd[1350]: => access_allowed: read access granted by write(=wrscxd)

第一行显示了请求的搜索字符串SRCH base="ou=people,dc=example,dc=com" scope=2 deref=3 filter="(&(objectClass=*)(uid=jsmith))",我们正在寻找属性 UID。

输出还显示了发出请求的用户,uid=ataylor,ou=people,dc=example,dc=com。您可以看到,访问请求的过程从搜索ou=people,dc=example,dc=com开始,然后通过check a_group_pat: cn=admins,ou=groups,dc=example,dc=com最终接受搜索(和读取),这给了 ataylor 写访问权限。

通过日志、slapaclldapsearch工具以及有用的 OpenLDAP 邮件列表的组合,您可以实现复杂的访问控制列表。让我们看看可以用来管理 LDAP 服务器的其他工具,包括我们刚刚提到的ldapsearch工具。

备份您的 LDAP 目录

基于文本的文件非常适合构建或恢复 LDAP 目录。一旦实现了您的目录,我们建议您设置一个脚本,该脚本可能会定期将您的 LDAP 数据库输出到一个文本文件中并保存它。在第十四章中,我们向您介绍了 Bareos 备份服务器,并向您展示了在备份 MySQL 数据库时如何使用Client Run Before JobClient Run After Job选项。您可以对 LDAP 数据库做类似的事情,如清单 16-4 所示。

#!/bin/bash

case $1 in
start)
   slapcat -b dc=example,dc=com -l /var/lib/ldap/backup.ldif
    if [ $? -eq 0 ] ; then
         echo "backup successful"
     else
        echo "backup failed"
        exit 1;
   fi
;;

stop)
  if [ -e /var/lib/ldap/backup.ldif ] ; then
      rm -f /var/lib/ldap/backup.ldif
      if [ $? -eq 0 ] ; then
           echo "removal of file successful"
       else
           echo "failed to remove file"
         exit 1;
     fi

  fi
 ;;
esac
exit 0

Listing 16-4.slapcat LDIF Dump

为了获得一个完美的备份,您可能希望在运行清单 16-4 中的命令之前停止 OpenLDAP 目录服务器;然而,这并不总是可能的,热备份比完全不备份更可取。

使用Client Run Before Job脚本,使用slapcat命令将 LDAP 数据库转储到磁盘上的一个文件中。然后你让 Bareos 做备份;Bareos 可以通过运行Client Run After Job脚本删除它(参见清单 16-5 )。

Job {
   Name = ldap.example.com
   Client = ldap-fd
   Enabled = yes
   JobDefs = "DefaultLinux"
   Client Run Before Job = "/usr/local/bin/ldap_backup start"
   Client Run After Job = "/usr/local/bin/ldap_backup stop"
}
Listing 16-5.The Job Definition for Bareos Backup Service

这是基于这样一个条件:您已经在您的ldap.example.com主机上的/usr/local/bin中安装了 LDAP 备份脚本。

恢复您的 LDAP 数据库只需在您的主机上恢复文件,并运行带有以下参数的slapadd命令(在此过程中应该关闭 OpenLDAP):

$ slapadd -b dc=example,dc=com -F /etc/ldap/slapd.d -l restored.ldif.backup.file

这里,我们将 LDAP 数据库恢复到了上次保存时的状态。因为 LDAP 没有预写日志,所以您不能像在全功能的事务关系数据库中那样重放对 LDAP 目录服务器的最新更新,所以定期备份很重要。我们建议您至少每晚做一次备份。

通过文本文件管理你的目录服务器会变得很无聊。幸运的是,如果您是那些喜欢基于 web 的 GUI 为您做所有繁琐工作的人之一,我们有一个解决方案,我们将在接下来讨论这个问题。

LDAP 帐户管理器:基于 Web 的 GUI

有几个工具可以用来管理 LDAP 目录。我们决定在本书中关注其中的一个,LDAP 客户管理器(LAM)。这是一个基于 web 的 GUI,可以减轻更新文本文件的管理负担。它有两个版本:免费版和需要付费的企业版。如果您发现自己不喜欢使用这个工具,您可能希望尝试其他一些工具,例如:

有些是旧的,有些是新的。OpenLDAP 这些年来没有太大的变化,所以您可以自由地尝试您选择的任何一个。我们选择向您展示 LAM,因为它不仅设计用于管理 LDAP,还用于提供用户帐户。它允许您基于易于遵循的模板创建用户。它也足够灵活,允许您集成 Samba 用户管理,如果您愿意的话。

安装和配置

LAM 可以从 Ubuntu 的在线仓库下载。它已经发布了几个版本,如果你正在寻找一个更新的版本,你可以从网站的下载页面( https://www.ldap-account-manager.org/lamcms/releases )或者从 Debian 仓库获得.deb包。

对于 LAM,您需要在您的主机上安装高于或等于 5.2.4 的 PHP 版本。

要安装它,您可以发出以下命令:

$ sudo aptitude install php-mcrypt php-zip ldap-account-manager apache2

在撰写本文时,Ubuntu 资源库(5.2-1ubuntu1)中提供的版本不支持 Xenial 安装的默认 PHP (7.x)。解决方案是首先从下载页面安装.deb包,然后手动安装,就像我们使用 CentOS 一样。

对于 CentOS 主机,您必须从网站( https://www.ldap-account-manager.org/lamcms/releases )下载 Fedora/CentOS rpm。这将链接到一个 SourceForge 下载,您可以获得直接链接的副本(从 URL 中删除镜像信息)。然后使用yum安装软件包,如下例所示:

$ sudo yum install -y httpd php php-ldap php-xml
$ sudo yum install -y http://downloads.sourceforge.net/project/lam/LAM/5.5/ldap-account-manager-5.5-0.fedora.1.noarch.rpm

CentOS 安装将所有的 LAM 文件放在/usr/share/ldap-account-manager下。所有的配置文件都安装在/var/lib/ldap-account-manager/config/下。

LAM 相当容易配置。在 Ubuntu 上,一些配置文件被安装到/etc/ldap-account-manager中。您将看到 Apache web 服务器的示例配置和配置文件/etc/ldap-account-manager/config.cfg,其中包含 LAM 安装的默认用户名和密码。

$ sudo vi /etc/ldap-account-manager/config.cfg
# password to add/delete/rename configuration profiles
password: {SSHA}tj1yDeQfLJbmISXwh8JfjMb2ro3v5u44

# default profile, without ".conf"
default: lam

config.cfg文件中,您可以看到我们已经将自己的密码设置为{SSHA}tj1yDeQfLJbmISXwh8JfjMb2ro3v5u44(注意您的所有权和权限)。我们还需要更改以下文件来添加我们自己的 LDAP 目录细节。首先,我们复制一份文件/var/lib/ldap-account-manager/config/lam.conf。然后我们用粗体字对/var/lib/ldap-account-manager/config/lam.conf文件进行修改。

$ sudo vi /var/lib/ldap-account-manager/config/lam.conf
# LDAP Account Manager configuration

# server address (e.g. ldap://localhost:389 or ldaps://localhost:636)
ServerURL: ldaps://ldap.example.com

# list of users who are allowed to use LDAP Account Manager
Admins: cn=admin,dc=example,dc=com

# password to change these preferences via webfrontend
Passwd: somepassword

# suffix of tree view

treesuffix: dc=example,dc=com

# maximum number of rows to show in user/group/host lists
maxlistentries: 30

# default language (a line from config/language)
defaultLanguage: en_GB.utf8:UTF-8:English (Great Britain)

# Number of minutes LAM caches LDAP searches.
cachetimeout: 5

# Module settings
modules: posixAccount_minUID: 1000

modules: posixAccount_maxUID: 30000

modules: posixAccount_minMachine: 50000

modules: posixAccount_maxMachine: 60000

modules: posixGroup_minGID: 1000

modules: posixGroup_maxGID: 20000

modules: posixGroup_pwdHash: SSHA
modules: posixAccount_pwdHash: SSHA

在这个文件的第一部分,我们添加了 LDAP 目录的细节,包括连接细节、树信息、Posix UID、GID 和机器号。当我们在 LAM 界面中创建新用户时,会用到 UID 和 GIDs 它们将在前面列出的范围内递增。

# List of active account types.

activeTypes: user,group,host

types: suffix_user: ou=people,dc=example,dc=com

types: attr_user: #uid;#givenName;#sn;#uidNumber;#gidNumber
types: modules_user: person,posixAccount,shadowAccount,exampleClient

types: suffix_group: ou=groups,dc=example,dc=com

types: attr_group: #cn;#gidNumber;#memberUID;#description
types: modules_group: posixGroup

types: suffix_host: ou=hosts,dc=example,dc=com

types: attr_host: #cn;#description;#uidNumber;#gidNumber
types: modules_host: account,posixAccount

# Access rights for home directories scriptRights: 750

在文件的最后一部分,我们详细说明了我们希望在activeTypes部分启用的帐户类型以及包含这些帐户的 LDAP 分支。LAM 管理工具将使用这些详细信息在 LDAP 服务器中为我们创建用户帐户。

为 LAM 添加 Apache 虚拟主机

我们将向我们的 web 服务器添加一个 Apache 虚拟主机来托管我们的 LAM 站点。在第十一章中,我们向你展示了如何建立一个 Apache 虚拟主机。web 服务可以在任何主机上运行;它不必与 LDAP 服务器在同一个主机上,但是在我们的例子中,情况就是这样。我们选择将这个站点放在我们的主机上,IP 地址为 192.168.0.1,DNS 名称为ldap.example.com,也称为headoffice.example.com

Note

我们的headoffice.example.com主机现在可能超载了安全虚拟主机(https),我们可能不得不选择一个非标准端口,比如 8443,来运行我们的ldap.example.com网站。或者,我们可以在完全不同的主机上运行它。

在我们的 Ubuntu 主机上,我们将有如下配置:这个虚拟主机的主要部分已经由 LAM 包提供,可以在/etc/ldap-account-manager/apache.conf中找到。我们将把这个文件包含在我们的VirtualHost信息中。VirtualHost号将被放置在/etc/apache2/sites-available并被命名为ldap.example.com.conf。在 CentOS 上,我们会将文件包含在/etc/httpd/conf.d/目录中。

$ sudo vi /etc/apache2/sites-available/ldap.example.com.conf
<VirtualHost 192.168.0.1:443>
  ServerName ldap.example.com
  SSLEngine on
  SSLCertificateFile /etc/ldap/certs/ldap.example.com.cert
  SSLCertificateKeyFile /etc/ldap/certs/ldap.example.com.key
  SSLCACertificateFile /etc/ldap/certs/cacert.pem

  LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost
  CustomLog /var/log/apache2/ldap.example.com_access.log comonvhost
  ErrorLog /var/log/apache2/ldap.example.com_error.log
  Loglevel debug

  Include /etc/ldap-account-manager/apache.conf

</VirtualHost>

我们在<VirtualHost> </VirtualHost>标签之间创建了一个VirtualHost。在这个VirtualHost中,我们添加了指向我们的/etc/ldap/certs目录的 TLS/SSL 密钥,并在/var/log/apache2目录中创建了单独的日志文件,以帮助诊断与这个VirtualHost相关的任何问题。

我们已经包含了(Include /etc/.../apache.conf)LAM 包提供的 Apache 配置文件。这允许该包由包管理器管理并相应地更新。

在 Ubuntu 上,我们需要启用站点,并确保 SSL 也已启用。我们通过以下方式做到这一点:

$ sudo a2ensite ldap.example.com.conf
$ sudo a2enmod ssl

在 CentOS 主机上,/etc/httpd/conf.d/目录中有一个lam.apache.conf文件。您可能希望将 Ubuntu 示例中的VirtualHost指令添加到该文件的副本中,以包含 SSL 和日志指令。如果不这样做,LAM GUI 将从 http://ldap.example.com/lam 可用。有关管理 CentOS 虚拟主机的更多信息,请参考第十一章。

接下来,我们启动 Apache web 服务器,将浏览器指向 https://ldap.example.com/lam 。我们现在看到的是 LAM 配置工具的登录页面,如图 16-4 所示。

A185439_2_En_16_Fig4_HTML.jpg

图 16-4。

LAM login page

请注意右上角的 LAM 配置链接。这用于对 LAM 配置工具执行一般维护。在登录页面,您将被要求输入您添加到/var/lib/ldap-account-manager/config.cfg的密码。您可以在这里更改管理员的常规登录设置和密码。

您在图 16-4 中看到的admin用户就是我们在/var/lib/ldap-account-manager/config/lam.conf中指定的rootDN。当我们输入rootDN的密码时,我们会看到已经配置好的用户,如图 16-5 所示。

A185439_2_En_16_Fig5_HTML.jpg

图 16-5。

Front page of the LAM web GUI

我们现在将使用标准配置文件创建一个新用户。简档用作创建用户的模板。我们首先点击 New User 按钮,调出如图 16-6 所示的页面。

A185439_2_En_16_Fig6_HTML.jpg

图 16-6。

Creating a new user

我们现在需要单击“个人”选项卡,并填写所需的详细信息。我们将在个人选项卡中输入的唯一详细信息是名字、姓氏和描述(见图 16-6);当然,您可以根据需要添加更多细节。

在 Unix 选项卡中,我们填写添加 Unix/Linux 帐户所需的详细信息,如图 16-7 所示。如果我们不输入 UID 号,将根据我们在lam.conf文件中指定的限制为我们生成一个。我们将用户附加到 staff 组,如果组已经存在,我们可以通过单击“Edit groups”按钮添加更多用户。

A185439_2_En_16_Fig7_HTML.jpg

图 16-7。

Linux(Unix) details

在启用阴影部分之前,我们需要启用阴影扩展。完成后,您将看到图 16-8 中的屏幕。

A185439_2_En_16_Fig8_HTML.jpg

图 16-8。

Shadow details

在“Shadow”选项卡中,我们将保持默认值不变。你可以在图 16-9 中看到这些默认值。

A185439_2_En_16_Fig9_HTML.jpg

图 16-9。

User add complete

要完成创建我们的用户,我们返回到主选项卡并单击 Create Account 按钮。然后我们会看到一个如图 16-9 所示的确认屏幕,显示操作成功。

如图 16-10 所示,我们的新用户tbird已经创建完毕。

A185439_2_En_16_Fig10_HTML.jpg

图 16-10。

Listing the new user

您可以使用 LAM 在 LDAP 目录中添加和删除组和主机条目,我们将留给您自己进一步探索。记住,LAM 不是唯一的 LDAP 管理工具。如果您不喜欢这个 LDAP 管理客户端来管理您的 LDAP 服务,我们建议您尝试我们前面提到的其他客户端之一。

有关配置 LAM 的更多文档,请访问:

与其他服务的集成

部署 LDAP 服务器的主要目的是能够将需要认证的不同服务集成到一个认证服务中。我们希望在尽可能多的服务中使用相同的用户名和密码。这使我们能够更好地管理用户,并允许我们在所有服务中设置通用的密码管理策略,从而提供更高的安全性。

我们的第一步将是集中我们的 Linux 认证,以便我们所有的 Linux 桌面和服务器共享相同的认证凭证。接下来,我们将展示如何向 web 服务添加 LDAP 认证。最后,我们将展示一个基于 web 的应用程序如何将 LDAP 用于其认证服务。

单点登录:集中式 Linux 认证

我们现在将向您展示如何在您的 Linux 主机上集中您的用户帐户。拥有几个 Linux 主机,每个主机上有几个用户帐户,管理起来会很麻烦。密码可能会不同步,并且当用户离开您的公司时,您可能不会将他们从您的所有主机上删除,从而产生潜在的安全风险。为了简化这种用户管理,您可以通过将 Linux 主机指向 LDAP 服务器来集中您的认证服务。为了向您展示如何做到这一点,我们将首先安装必要的软件,然后检查配置中使用的文件。好的一面是,您应该能够使用发行版提供的身份验证工具来配置使单点登录工作的必要文件。

我们需要修改我们的访问列表。我们需要匿名访问某些条目。当我们对 LDAP 服务器进行身份验证时,我们需要首先访问某些条目。有两种方法可以做到这一点;第一种是使用绑定 DN 和密码,就像代理一样,绑定并访问条目,第二种是匿名绑定,其中不进行绑定(记住绑定是身份验证的另一种说法)。如果我们使用代理绑定 DN,我们需要在每台连接的主机上设置明文密码。在本练习中,我们选择不这样做。

除了简单身份验证,您还可以选择使用其他几种身份验证方法。如果你愿意,你可以用 SASL 或 Kerberos 设置认证,我们在第十三章讨论了这些认证方法。

访问控制列表现在看起来像这样。你会看到粗体的变化;我们已经给了 anonymous 读取某些属性的能力,这些属性是在认证过程中查找的,并且已经给了auth访问userPassword属性的权限。我们还防止用户更改他们不应该拥有写权限的某些属性,比如uidNumberhomeDirectory等等。

dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to *
        by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
        by * none break
-
add: olcAccess

olcAccess: {1}to attr=entry,member,objectClass,uid,uidNumber,gidNumber,homeDirectory,cn,shadowWarning,modifyTimestamp

        by group.exact="cn=admins,ou=Groups,dc=example,dc=com" tls_ssf=128 write

        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" tls_ssf=128 read

        by anonymous tls_ssf=128 read

        by self tls_ssf=128 read

-
add: olcAccess
olcAccess: {2}to attrs=userPassword
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" tls_ssf=128 auth
        by group.exact="cn=admins,ou=Groups,dc=example,dc=com" tls_ssf=128 write
        by self tls_ssf=128 write
        by anonymous tls_ssf=128 auth

        by * none stop
-
add: olcAccess
olcAccess: {3}to dn.children="ou=People,dc=example,dc=com"
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" read
        by group.exact="cn=admins,ou=Groups,dc=example,dc=com" write
        by self write
        by users read
-
add: olcAccess

olcAccess: {4}to dn.children="ou=Groups,dc=example,dc=com"
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" read
        by group.exact="cn=admins,ou=Groups,dc=example,dc=com" write
-
add: olcAccess
olcAccess: {5}to dn.children="ou=meta,dc=example,dc=com"
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" read
        by group.exact="cn=admins,ou=Groups,dc=example,dc=com" write
        by self read
-
add: olcAccess

olcAccess: {6}to dn.children="ou=Hosts,dc=example.com"
        by group.exact="cn=admins,ou=Groups,dc=example,dc=com" write
        by dn.exact="cn=webadmin,ou=meta,dc=example,dc=com" search
-
add: olcAccess
olcAccess: {7}to * by * none

设置 sssd

为了从我们的 Linux 客户机对 LDAP 服务器进行认证,我们将安装一个名为sssd的包。它被设计为一个守护进程,将根据包括 LDAP 在内的各种身份验证服务进行身份验证。

在 Ubuntu 上,您需要安装以下软件包:

$ sudo aptitude install sssd

当然,在 CentOS 上,您可以使用 YUM 安装相同的包名。它们也采用相同的配置。我们需要创建一个名为/etc/sssd/sssd.conf的文件。此文件需要设置 0600 权限。

该文件本身将包含以下内容:

[sssd]
config_file_version = 2
services = nss, pam
domains = LDAP

[domain/LDAP]
cache_credentials = true

id_provider = ldap
auth_provider = ldap

ldap_uri = ldap://ldap.example.com
ldap_search_base = dc=example,dc=com
ldap_id_use_start_tls = true
ldap_tls_reqcert = demand
ldap_tls_cacert = /etc/ssl/certs/cacert.pem
chpass_provider = ldap
ldap_chpass_uri = ldap://ldap.example.com
entry_cache_timeout = 6
ldap_network_timeout = 2
ldap_group_member = uniquemember
ldap_pwdlockout_dn = cn=ppolicy,ou=policies,dc=example,dc=com
ldap_access_order = lockout

该文件中的主要指令可以在man sssd.confsssd-ldap中找到。但是第一部分告诉sssd我们将使用nsspam来运行我们的 LDAP 域。在 LDAP 域部分,我们有idauth的提供者,然后是连接细节,包括 TLS 设置和密码策略细节。

当 Linux 寻找一条信息,比如主机或密码时,它会检查一个名为/etc/nsswitch.conf的文件,看看在哪里可以找到这条信息。需要用以下信息更新nsswitch.conf文件,告诉它对passwdgroupshadow文件使用sssd;这是登录所需的信息。

passwd:     files sss
group:      files sss
shadow:     files sss
gshadow:    files

hosts:      files mdns4_minimal [NOTFOUND=return] dns
networks:   files

protocols:  db files
services:   db files sss
ethers:     db files
rpc:        db files

# pre_auth-client-config # netgroup:       nis
netgroup:   nis sss
sudoers:    files sss

左边是我们要寻找的信息,右边是我们在哪里寻找这些信息以及寻找它们的顺序。例如,当我们需要通常包含在/etc/passwd文件中的信息(如用户名)时,我们首先查看该文件。如果在文件中没有找到用户名,我们就查询sssd守护进程(或sss)。这同样适用于groupshadow

我们需要更新的下一部分是 PAM 身份验证模块。我们需要允许我们通过 PAM 使用sss(d)进行认证。在 Ubuntu 上,我们通过改变以下内容来做到这一点(我们已经从示例中删除了注释):

/etc/pam.d/common-auth
auth    [success=3 default=ignore]     pam_unix.so nullok_secure

auth    [success=2 default=ignore]     pam_sss.so use_first_pass

auth    [success=1 default=ignore]     pam_ldap.so use_first_pass
auth    requisite                      pam_deny.so
auth    required                       pam_permit.so

/etc/pam.d/common-password
password   requisite                   pam_pwquality.so retry=3
password   [success=3 default=ignore]   pam_unix.so obscure use_authtok try_first_pass sha512

password   sufficient                  pam_sss.so use_authtok

password   [success=1 user_unknown=ignore default=die]  pam_ldap.so use_authtok try_first_pass
password   requisite                   pam_deny.so
password   required                    pam_permit.so
password   optional                    pam_gnome_keyring.so

/etc/pam.d/common-account
account    [success=2 new_authtok_reqd=done default=ignore]    pam_unix.so
account    [success=1 default=ignore]  pam_ldap.so
account    requisite                   pam_deny.so
account    required                    pam_permit.so
account    sufficient                  pam_localuser.so

account    [default=bad success=ok user_unknown=ignore]    pam_sss.so

/etc/pam.d/common-session
session    [default=1]        pam_permit.so
session    requisite          pam_deny.so
session    required           pam_permit.so
session    optional           pam_umask.so
session    required           pam_unix.so

session    optional           pam_sss.so

session    optional           pam_ldap.so
session    optional           pam_systemd.so

session    required           pam_mkhomedir.so skel=/etc/skel umask=0022

当你在 Ubuntu 上安装了sssd之后,这些文件就会自动更新,我们不需要修改它们。你会注意到在common-session文件的末尾,我们允许认证用户创建他们自己的主目录,并用/etc/skel的内容填充它们。

在 CentOS 上,您可能需要自己将这些pam指令添加到/etc/pam.d/system-auth中。这两个发行版略有不同,但基本相同。

auth        required      pam_env.so
auth        sufficient    pam_fprintd.so
auth        sufficient    pam_unix.so nullok try_first_pass

auth        sufficient    pam_sss.so use_first_pass

auth        requisite     pam_succeed_if.so uid >= 1000 quiet_success
auth        required      pam_deny.so

account     required      pam_unix.so
account     sufficient    pam_localuser.so
account     sufficient    pam_succeed_if.so uid < 1000 quiet

account     [default=bad success=ok user_unknown=ignore]  pam_sss.so

account     required      pam_permit.so

password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok

password    sufficient    pam_sss.so use_authtok

password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
-session    optional      pam_systemd.so
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid

session     optional      pam_mkhomedir.so skel=/etc/skel umask=0022

session     required      pam_unix.so

我们在第五章中讨论了配置 PAM,但是我们将在接下来的“PAM 如何工作”一节中进一步讨论。

现在我们已经配置了 PAM 和sssd,我们只需要确保我们可以从客户端连接到 LDAP 服务器。在我们的sssd.conf文件中,我们指定了ldap_tls_cacert。我们需要确保将/etc/ssl/certs/cacert.pem安装在正确的位置;否则,我们将被 LDAP 服务器拒绝。

一旦我们将cacert.pem放在正确的位置,我们就可以测试是否可以查询 LDAP 服务器上的用户。

$ grep ataylor /etc/passwd

在我们的主机上,这没有返回任何结果,这意味着用户ataylor还没有被创建,但是我们在 LDAP 配置中创建了她。我们将使用名为getent的命令来查询passwd文件和sssd(根据我们在nsswitch.conf中的配置指示;getent是查询那些条目的工具)。

$ getent passwd ataylor
ataylor:*:1002:1000:Angela Taylor:/home/ataylor:

我们已经返回了 Angela Taylor 的用户详细信息,包括 UID/GID 和主目录信息。这意味着我们可以成功地与 LDAP 服务器对话并返回信息。

下一步是证明我们可以以她的身份登录。为此,我们将使用su命令,或超级用户命令。此命令允许您以 root 用户身份登录或登录到另一个帐户。当我们发出这个命令时,我们将被要求提供 Angela 的密码。

jsmith@au-mel-ubuntu-2:∼$ su - ataylor
Password:
ataylor@au-mel-ubuntu-2:∼$

在这里,我们已经成功地登录到一个只存在于 LDAP 中的用户。我们第一次登录时,已经创建了一个主目录。现在,任何 LDAP 用户都可以登录我们的主机并创建他们的主目录。

我们可以进一步细化我们的sssd.conf文件来过滤某些用户,比如只有当他们将exampleActive设置为TRUE时。

ldap_access_filter = (exampleActive=TRUE)

如果您使用的是 Ubuntu Unity 桌面,您需要对以下文件进行更改,以允许其他用户从桌面登录:

/usr/share/lightdm/lightdm.conf.d/50-unity-greeter.conf
[Seat:*]
greeter-session=unity-greeter
greeter-show-manual-login=true

这将允许你在重启 Ubuntu 桌面后看到如图 16-11 所示的屏幕。输入用户名后,输入密码,如图 16-12 所示。

A185439_2_En_16_Fig12_HTML.jpg

图 16-12。

Providing the LDAP password

A185439_2_En_16_Fig11_HTML.jpg

图 16-11。

Logging in via LDAP to the desktop

您也可以使用 CentOS 提供 LDAP 用户名和密码,而无需进行任何特殊配置。

PAM 是如何工作的

正如在第五章中所解释的,Linux 可以使用可插拔认证模块(PAM)来认证你的 LDAP 服务。PAM 针对 LDAP 服务器为主机提供身份验证、授权和密码更改功能。PAM 通过位于/etc/pam.d目录中的文件进行配置。如第五章所述,CentOS 主机上的主 PAM 文件是/etc/pam.d/system-auth文件。清单 16-6 显示了在您的主机上建立 LDAP 认证所需的设置示例。

auth        required      pam_env.so
auth        sufficient    pam_fprintd.so
auth        sufficient    pam_unix.so nullok try_first_pass

auth        sufficient    pam_sss.so use_first_pass

auth        requisite     pam_succeed_if.so uid >= 1000 quiet_success
auth        required      pam_deny.so

account     required      pam_unix.so
account     sufficient    pam_localuser.so
account     sufficient    pam_succeed_if.so uid < 1000 quiet

account     [default=bad success=ok user_unknown=ignore]  pam_sss.so

account     required      pam_permit.so

password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok

password    sufficient    pam_sss.so use_authtok

password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
-session    optional      pam_systemd.so
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid

session     optional      pam_mkhomedir.so skel=/etc/skel umask=0022

session     required      pam_unix.so

Listing 16-6.PAM Settings for system-auth on CentOS

这个文件是为您生成的,除非有充分的理由,否则您不应该自己修改它。从清单 16-6 可以看出,该文件由四个独立的管理组组成:authaccountpasswordsession

看看下面一行,这是auth管理组的一个例子:

auth        sufficient    pam_sss.so use_first_pass

该组通常通过某种密码质询-响应机制来验证用户。sufficient控制值表示,如果该模块成功,则认为用户通过了身份验证。pam_sss.so是要使用的 PAM 共享对象,即决定如何对用户进行身份验证的代码。最后,use_first_pass是一个可选的语法,它告诉你不要再次询问你的密码,而是使用栈中较高模块提供的第一个密码。

在 Ubuntu 主机上,对应的文件是/etc/pam.d目录下的common-authcommon-passwordcommon-sessioncommon-account

Note

你可以在系统管理员指南中阅读更多关于 PAM 的内容: www.linux-pam.org/Linux-PAM-html/Linux-PAM_SAG.html

PAM 针对 LDAP 服务进行身份验证的另一个核心文件是/etc/nsswitch.conf。该文件要求passwdgroup和 shadow 关键字具有以下值:

passwd: files ldap
group:  files ldap
shadow: files ldap

正如我们已经解释过的,它们告诉 PAM 使用什么认证数据库以及使用它们的顺序。因此,当我们寻找通常会在/etc/passwd中找到的信息时,我们会首先使用主机上的文件,然后使用 LDAP。同样的道理也适用于groupshadow。PAM 和nsswitch.conf文件应该由您的发行版提供的认证配置工具来配置。

Note

使用不同的身份验证服务时,您可能需要映射某些属性。当认证服务需要 OpenLDAP 通常不提供的某个属性时,例如 AD 服务器需要的属性,就完成了属性的映射。你可以在 https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/Windows_Integration_Guide/sssd-ad-integration.html 找到做这件事的红帽文档。

LDAP 和 Apache 认证

现在让我们看看如何让我们的 web 服务器使用我们的 LDAP 服务器来认证客户端。当客户试图访问 https://ldap.example.com 网站时,他们将被要求输入其 LDAP 用户名和密码才能获得访问权限。我们将对我们的 web 服务器做两件事来实现这一点:通过在我们的 web 主机上启用 SSL 来确保我们与 web 服务器的所有通信的安全,并将 LDAP 细节添加到ldap.example.com虚拟主机。

Note

第十一章讨论了 Apache 虚拟主机。

我们将假设它是从ldap.example.com主机上运行的,并且没有其他 Apache 服务在其上运行。

接下来,让我们检查我们将对我们的ldap.example.com虚拟主机文件进行的更改。

$ sudo vi /etc/apache2/sites-available/ldap.example.com.conf
LDAPTrustedGlobalCert CA_BASE64 /etc/ldap/certs/cacert.pem
LDAPTrustedMode TLS

<VirtualHost 192.168.0.1:443>
  ServerName ldap.example.com
  SSLEngine on
  SSLCertificateFile /etc/ldap/certs/ldap.example.com.cert
  SSLCertificateKeyFile /etc/ldap/certs/ldap.example.com.key
  SSLCACertificateFile /etc/ldap/certs/cacert.pem

  LogFormat "%v %l %u %t \"%r\" %>s %b" comonvhost
  CustomLog /var/log/apache2/ldap.example.com_access.log comonvhost
  ErrorLog /var/log/apache2/ldap.example.com_error.log
  Loglevel debug

  <Location /lam >
     AuthType Basic

     AuthName "LDAP example.com"

     AuthBasicProvider ldap

     AuthLDAPBindAuthoritative on

     AuthLDAPURL ldap://ldap.example.com/ou=people,dc=example,dc=com?uid?sub

     AuthLDAPBindDN cn=webadmin,ou=meta,dc=example,dc=com

     AuthLDAPBindPassword <thewebadminpasswordincleartext>

     Require valid-user

     Require ldap-group cn=admins,ou=groups,dc=example,dc=com

  </Location>

  Include /etc/ldap-account-manager/apache.conf

</VirtualHost>

Note

在 CentOS 主机上,该文件可以在/etc/httpd/conf.d/vhost.conf中找到,这取决于您在 CentOS 上管理虚拟主机的方式。

<VirtualHost>标签中,我们添加了一个Location指令。Location指令说,任何匹配/lam的 URI 现在都会触发以下配置,提示用户通过 LDAP 进行身份验证:

AuthType Basic
AuthName "LDAP example.com"
AuthBasicProvider ldap
AuthLDAPBindAuthoritative on
AuthLDAPURL ldap://ldap.example.com/ou=people,dc=example,dc=com?uid?sub
AuthLDAPBindDN cn=webadmin,ou=meta,dc=example,dc=com
AuthLDAPBindPassword Zf3If7Ay
Require valid-user
Require ldap-group cn=admins,ou=groups,dc=example,dc=com

我们将AuthType设置为Basic,将AuthName设置为LDAP example.com. AuthType来定义认证方法,您可以在BasicDigest之间进行选择。LDAP 认证要求Basic. AuthName是弹出的认证窗口中的名称。

AuthBasicProvider ldap定义我们将要使用的服务器,在本例中是 LDAP 服务器,以提供我们的认证机制。我们通过指定AuthzLDAPAuthoritative on来表明我们希望 LDAP 服务器成为接受或拒绝访问的权威服务。接下来是我们将用于认证服务的 LDAP URL,AuthLDAPURL ldap:// ldap.example.com/ou=people,dc=example,dc=com?uid?sub。它指定了我们搜索的基础,ou=people,dc=example,dc=com;我们感兴趣的属性,uid;以及我们搜索的范围,sub。在这里,您现在可以看到我们在哪里使用了cn=webadmin,ou=meta,dc=example,dc=com元帐户,该帐户将绑定到我们的 LDAP 服务器,同时还提供了密码。您不必以明文形式提供密码;如果这让你不舒服,你也可以试试其他方法:

最后,我们指定我们需要一个有效的用户,并且认证用户也必须属于 LDAP 组cn=admin,ou=groups,dc=example,dc=com

Note

要了解关于 LDAP 和 Apache 认证的更多信息,请阅读以下内容: https://httpd.apache.org/docs/2.4/mod/mod_authnz_ldap.html

在我们继续之前,我们需要确保模块被添加到我们的 web 主机中,在 Ubuntu 上我们要做以下事情:

$ sudo a2enmod authnz_ldap
$ sudo a2enmod ldap
$ sudo a2enmod ssl

对于 CentOS,我们需要确保安装了包mod_sslmod_ldap;这将在conf.modules.d目录下创建文件。

$ cat /etc/httpd/conf.modules.d/01-ldap.conf
# This file configures the LDAP modules:
LoadModule ldap_module modules/mod_ldap.so
LoadModule authnz_ldap_module modules/mod_authnz_ldap.so

我们现在需要重启我们的 Apache 服务。

$ sudo systemctl restart httpd

我们现在使用 web 浏览器连接到 LAM web GUI,地址如下: https://ldap.example.com/lam

在图 16-13 中,您可以看到 Apache 提供的认证挑战。我们已经输入了atayloruid,我们知道他是cn=admins,ou=groups,dc=example,dc=com组的成员,这是我们的 Apache 配置所要求的。

A185439_2_En_16_Fig13_HTML.jpg

图 16-13。

The Apache request for username and password

您现在应该能够访问 LAM 了,并且应该在 Apache 日志中看到成功的登录。

...authorization result of Require ldap-group cn=admins,ou=groups,dc=example,dc=com...
...auth_ldap authenticate: accepting ataylor....
...authorization result of Require valid-user : granted...

这表明 LDAP 服务器正在使用用户名ataylor验证我们的请求,并测试这个用户是cn=admin,ou=groups,dc=example,dc=com组的成员。这种详细程度是由虚拟主机LogLevel指令中的调试日志选项提供的。

摘要

在本章中,我们讨论了什么是目录服务器以及目录信息树中的条目是如何组织的。我们向您展示了如何配置和安装 OpenLDAP 目录服务器,并使用用户帐户和管理帐户填充它。我们讨论了模式、索引和访问控制列表。我们向您展示了如何使用 OpenLDAP 提供的各种客户机工具来查询和管理 LDAP 服务器。现在,您可以设置一个 web GUI 来管理 LDAP 目录,并将 LDAP 集成到您的网络和现有应用程序中。

现在,您应该能够执行以下操作:

  • 在 Ubuntu 和 CentOS 主机上安装和配置 OpenLDAP
  • 了解和配置访问控制列表
  • 查询和管理您的 LDAP 目录
  • 安装和配置 LAM web GUI
  • 为 Linux 到 LDAP 设置单点登录
  • 将 Apache web 服务器配置为使用 LDAP 身份验证来验证客户端访问

正如我们已经说过的,目录服务可以在你的网络中扮演一个中心角色,而且关于这个主题还有很多东西我们在本章中还没有涉及到。我们建议您购买一本关于该主题的书籍,阅读位于 www.openldap.org 的在线文档,并使用邮件列表来帮助您进一步了解该领域的知识。

在下一章,你将会读到性能监控和优化。

十七、性能监控和优化

既然你的主机正在为你提供服务,那么继续这样做是很重要的。随着业务的增长,服务器上的工作负载也会增加。在本章中,我们将向您展示如何监控磁盘空间、处理器和 RAM 使用等资源。这将帮助您识别性能瓶颈,并更好地利用您拥有的资源。

少量的调优可以减少购买额外硬件的需求,因此监控性能可以帮助您节省资金和时间。

基本健康检查

在本节中,我们将看一些帮助您确定主机状态的基本工具。

CPU 使用情况

当应用程序运行缓慢时,您应该检查的第一件事是系统是否真的在忙着做什么。最快的方法是通过uptime命令,如清单 17-1 所示。

$ uptime
12:51:04 up 116 days, 7:09, 3 users, load average: 0.01, 0.07, 0.02
Listing 17-1.The uptime Command

该命令打印系统状态的简要概述,包括当前时间、系统已开启(或启动)的时间、登录用户的数量以及代表系统工作负载的三个数字。

在我们的示例中,系统上次启动是在 116 天 7 小时 9 分钟之前。如果您失去了与远程服务器的连接,这使您可以在重新登录后轻松确定系统是否重新启动。

登录用户的数量是当前通过终端控制台、X 和 SSH 登录的总数量。如果您登录两次,您将被计为两个用户。连接到 Samba 或 Apache 等服务的用户在这里不计算在内。

最后,系统负载以三个数字显示。这些数字形成了一个指数,表明在过去的 1 分钟、5 分钟和 15 分钟内,CPU 计划完成的工作与实际完成的工作的平均比率。

负载为 1.00 意味着被调度的工作量等于 CPU 可以处理的工作量。令人困惑的是,如果一台主机包含多个 CPU,每个 CPU 的负载相加得到一个总数;在具有四个 CPU 内核的主机上,4.00 的负载与单核系统上 1.00 的负载具有相同的含义。

在我们的示例中,过去一分钟的平均工作负载约为 1%。一些额外的任务正在运行,可能在几分钟前完成,因为过去 5 分钟的平均工作负载为 7%。

尽管这些数字绝不应该被视为真理,但它们确实提供了一种快速检查系统是否健康的方法。如果在一个有八个 CPU 内核的主机上,系统负载是 6.00,那么许多任务可能刚刚运行完。如果负载为 60,则存在问题。

这个问题可能是主机正在运行几个独占 CPU 的进程,不允许其他进程有足够的周期。稍后我们将向您展示如何使用top实用程序找到这些进程。或者,您的主机可能试图运行远远超出其处理能力的进程,同样没有为它们留下足够的 CPU 周期。如果写得不好的应用程序或守护进程不断产生自己的副本,就会发生这种情况。有关更多信息,请参见“Fork Bomb”侧栏。稍后我们将向您展示如何使用ulimit命令来防止这种情况。

最后,可能有一个进程正在等待主机执行一个不能被中断的任务,比如将数据移入或移出磁盘。如果此任务必须在进程继续之前完成,则称该进程处于不可中断的睡眠状态。它将计入更高的平均负载,但实际上不会使用 CPU。稍后,我们将在“高级工具”部分向您展示如何检查磁盘的使用量。

Fork Bomb

我们在第五章中解释过,当一个守护进程或服务在后台运行时,它会派生出一个子进程,并使init成为父进程,而原来的进程会退出。对于一个行为不端的应用程序来说,做错误的事情并继续派生子进程而不是退出是完全可能的。这叫做叉形炸弹。

fork bomb 会在宿主允许的情况下尽快创建自己的新副本。一个进程产生两个进程,两个进程产生两个进程,四个进程产生八个进程,以此类推,从而耗尽其他进程的资源,比如 CPU 周期,最终耗尽 RAM。

内存使用

性能降低的另一个原因是内存使用过多。如果一个任务开始使用大量内存,主机将被迫通过将其他任务放入交换内存来释放内存。与访问 RAM 相比,访问交换内存的速度非常慢,毕竟交换内存只是一块磁盘,就像内存一样使用。

您可以通过free命令快速检查主机正在使用多少 RAM 和交换内存。我们将把-h(人类可读)选项传递给它,这样它就可以用千兆字节(-g)、兆字节(-m)或千字节(-k)来显示大小,如清单 17-2 所示。

$ free -h
               total        used        free      shared       buff/cache     available
Mem:           3.1G         59M         2.7G      13M          350M           2.8G
Swap:          1.5G         0B          1.5G
Listing 17-2.The free Command

该列表让您可以快速了解系统的可用内存是否不足。第一行告诉您 RAM 使用的状态。总数是该主机可用的 RAM 量。然后,这被分成正在使用的 RAM 和空闲的 RAM。不同版本的自由程序可能在同一系统上显示不同的内存值。

shared是指共享内存,主要是tmpfs。这与/proc/meminfo Shmem值(内核使用的空间)相匹配。

buffers 列告诉您内核用作磁盘写缓冲区的内存量。这个缓冲区允许应用程序快速写入数据,并让内核在后台处理写入磁盘的数据。也可以从这个缓冲器中读取数据,进一步提高速度。最后一列cached,告诉您内核使用了多少内存作为高速缓存来快速访问信息。

缓冲区和高速缓存都根据需要调整大小。如果应用程序需要更多的 RAM,内核将释放部分缓存或缓冲区空间,并重新分配内存。如果交换空间可用,内核可以将不活动的内存移动到交换空间。

最后,最后一行告诉我们主机使用了多少交换空间。随着时间的推移,这个数字会略有上升,因为没有被使用的服务可以被内核放在交换空间中。这样做允许它回收空闲的 RAM,并将其用作缓冲区或缓存。

这意味着让您的主机使用交换空间不一定是一件坏事。但是,如果所有的内存和所有的交换空间都在使用中,显然就有问题了。在我们的示例中,我们没有使用任何交换空间,因此它显示为可用的 1.5GB 空间中的 0。

Note

在 Linux 上,空闲内存意味着浪费内存,因为它没有被利用,甚至没有作为缓冲区或缓存。

我们将在“高级工具”部分向您展示如何找出单个任务使用了多少内存。

磁盘空间

计算机系统拥有的其他有限资源是磁盘空间和磁盘速度。一般来说,当磁盘变满时,系统不会变慢,但服务可能会崩溃并给用户带来痛苦,因此关注使用情况并在需要时提供更多存储空间是值得的。

我们将在下一节向您展示如何检查磁盘速度问题。

Note

我们在第九章中介绍了用于检查可用和已用磁盘空间的dfdu工具。

日志

最后,如果应用程序或内核发生了问题,您可能会在系统或内核日志中找到一个日志条目。您当然希望重启任何崩溃的服务,但是检查问题原因的日志将帮助您防止同样的事情再次发生。

如果日志守护进程本身已经停止,您仍然可以通过dmesg命令检查内核日志缓冲区。

Note

我们将在第十八章中详细介绍日志记录。

高级工具

基本工具为您提供快速概述,但不提供任何信息来帮助您确定问题的原因(如果有的话)。为此,我们将向您展示一些可以帮助您查明瓶颈的工具。

CPU 和内存使用

为了列出当前运行任务的详细信息,Linux 提供了top实用程序。这类似于你可能习惯于从 Windows 或 macOS 上的活动监控器使用的任务管理器,但它在终端窗口中运行,如图 17-1 所示。它提供了主机上正在运行的进程和线程的可排序和可配置列表。您可以使用以下命令来访问它:

A185439_2_En_17_Fig1_HTML.jpg

图 17-1。

The top utility

$ top

输出的顶部给出了几个标题行,其中包括来自uptime的信息,以及来自freevmstat命令的一些汇总数据,稍后我们将在“交换空间使用”和“磁盘访问”部分讨论这些数据。您可以打开和关闭这些标题。L 键切换平均负载线的显示。您可以通过 T 键切换任务摘要,通过 m 键切换内存信息。

显示屏的其余部分由几列关于正在运行的进程和线程的信息组成。您在这里看到的列是默认显示的,但是您也可以启用或禁用其他列。我们在表 17-1 中列出了它们的标题和含义。

表 17-1。

top Column Headers

| 页眉 | 意义 | | --- | --- | | `PID` | 任务的进程 ID。这个唯一的标识符允许您操作任务。 | | `USER` | 任务所有者的用户名;它运行的帐户。 | | `PR` | 任务优先级。 | | `NI` | 任务精细度;表示该任务愿意将 CPU 周期让给其他任务的程度。较低或负面的好意味着高优先级。 | | `VIRT` | 任务分配的内存总量,包括共享内存和交换内存。 | | `RES` | 任务使用的物理内存总量,不包括交换内存。 | | `SHR` | 任务使用的共享内存量。这种内存通常由库分配,也可供其他任务使用。 | | `S` | 任务状态。这表明任务是正在运行(`R`)、休眠(`D`或`S`)、停止(`T`)还是僵死(`Z`)。 | | `%` `CPU` | 自上次屏幕更新以来,该任务使用的 CPU 周期的百分比。 | | `%MEM` | 该任务使用的可用 RAM 的百分比。 | | `TIME+` | 任务自启动以来使用的总 CPU 时间。 | | `COMMAND` | 被监控的任务的名称。 |

您可以在man top手册页中获得这些和所有其他可用字段的完整描述。

默认情况下,任务按降序显示,并按%CPU排序。消耗 CPU 时间最多的进程将位于列表的顶部,这对解决问题非常有用。

我们可以对top中的许多列进行排序。为了对 CPU 使用情况进行排序,我们需要对%CPU列进行排序。我们可以通过按 F 键选择要排序的列,这将弹出一个可用列的列表,如图 17-2 所示。

A185439_2_En_17_Fig2_HTML.jpg

图 17-2。

Choosing a sort field

第一行告诉我们当前的排序字段%CPU。下面是如何选择不同排序字段的说明。向上和向下箭头导航到不同的字段。如果您按 s,您将看到该字段现在已被选中。d 键和空格键切换是否显示该字段。您可以使用 q 或 Escape 键返回到您的选择的顶部屏幕。我们可以按 s 使%CPU列成为我们的排序字段,然后按 q 返回到任务列表。现在所有的进程都是按照它们使用的 CPU 周期数来排序的,所以如果一个进程正在疯狂运行,你将能够很容易地发现它。

Note

可以选择比屏幕显示更多的字段。如果发生这种情况,您将需要取消选择一些其他字段来腾出空间。

Orphans and Zombies: Undead Tasks

除了初始进程systemdinit之外,Linux 主机上的每个任务都由一个父进程控制,当它完成执行时,它向这个父进程报告它的状态。

但是,有时事情会出错,父进程可能会崩溃,留下一个已完成的子进程等待向其父进程报告其状态。这就创建了一个孤儿进程,它被systemd(或init)采用。当它完成时,它报告并被收获(或清理)。

僵死进程是一个已经终止但仍存在于进程表中的子进程,等待其父进程读取其退出状态。每个子进程都是这样结束的。父进程将向子进程发送一个等待系统调用来读取其退出状态。如果没有发生这种情况,该进程就不能从进程列表中删除并保留在那里。这不一定是一个问题,因为它最终应该由父代或由systemd ( init)获得。在这种状态下,它已经释放了内存,并且没有使用 CPU。然而,它可以指出一个程序的问题,该程序的父程序崩溃了,没有得到它的子程序。

您可以通过查看进程列表输出来查看僵尸进程。

$ ps aux |grep Z
USER    PID    %CPU %MEM   VSZ   RSS   TTY     STAT    START   TIME   COMMAND
ubuntu  7814   0.0  0.0    0     0     pts/0   Z+      11:52   0:00   [zombie] <defunct>

你可以在 http://en.wikipedia.org/wiki/Zombie_process 阅读更多关于僵尸的内容。

top中有更多的显示选项,您可以在帮助菜单中找到它们。新闻?去访问它。当您根据自己的喜好定制了top显示信息的方式后,您可以按 w 保存配置。这将把设置写入您的主目录中一个名为.toprc的文件。

如果发现一个行为不端的任务,可以用top退出。为此,请按 k 并输入相关任务的 PID。然后会要求您输入发送给进程的信号。使进程正常终止的信号是 15。如果这不起作用,您可能想尝试发送信号 3,这是有点严厉,或信号 9,这是类似于斧头谋杀过程。

要了解更多关于信号的信息,请阅读“杀人并不总是谋杀”侧栏。

Note

您必须是流程所有者或根用户,才能向流程发送信号。

Killing Is Not Always Murder

尽管向进程发送信号的行为被称为终止,并且通常是通过kill命令完成的,但它不一定会退出进程。它使用内核中的工具向进程发送信号。

提供的服务已被设计为侦听此信号;可以让他们执行一个动作。最常用的是信号 1,也称为 HUP。这个信号会导致大多数服务重新加载它们的配置文件,不再需要停止和重新启动它们。

如果你想让一个应用程序或进程退出,你可以通过向它发送SIGTERMSIGQUIT信号来请求它退出,这两个信号分别是 15 和 3。一个无视礼貌的停止请求的任务可能会通过 9 或SIGKILL信号被强制停止。

发送SIGKILL将立即停止进程,不管它正在做什么。这可能会使您的流程(可能还有数据)处于混乱状态,所以要小心使用。SIGTERM将允许进程在退出前正常关闭。

您可以在man 7 signal手册页中阅读更多关于信号及其功能的信息。

如果你有一个进程占用了太多的 CPU 时间并且中断了更重要的进程,你可以通过降低它的优先级来让它变得更好。这意味着它将更容易让位于其他进程,允许它们使用分配给它的一些 CPU 周期。您可以通过按 R 键来更改加工精度。top将询问您进程 ID 和新的 niceness 值。

除非你以 root 或者通过sudo运行top,否则你只能让进程变得更好。你不能提高他们的优先级,只能降低优先级。

完成后,按 q 退出top

Note

您还可以使用renice实用程序来更改 niceness 和 priority,而无需使用 top。例如,要改变进程 8612 的精确性,您可以运行renice +5 8612

交换空间使用

如果您看到大量交换空间正在使用,这可能表明系统内存不足。交换空间和 RAM 构成了系统可用的总可分配内存。当一个特定的进程需要内存并且没有立即可用的内存时,内核会将较少使用的内存页换出到交换空间中。这不一定是一件坏事,但是如果系统“大量交换”(也就是说,频繁地将内存页面移入和移出交换空间,那么因为交换比 RAM 慢得多,所以会导致性能很差)。

您可以通过vmstat实用程序检查是否是这种情况。您通常希望让这个实用程序每隔几秒钟打印一次信息,这样您就可以发现任何趋势。传递-n选项,防止标题被重新打印,并告诉它每 5 秒重新显示一次统计,如图 17-3 所示。按 Ctrl+C 退出vmstat

A185439_2_En_17_Fig3_HTML.jpg

图 17-3。

vmstat output on heavily swapping host

这个令人印象深刻的混乱数字向我们表明了有多少数据在系统中流动。每一行由六组数字组成,这些数字提供了关于进程、内存、交换、输入/输出、系统和 CPU 的信息。

由于过多的交换空间使用而导致的性能下降将出现在其中的两个组中:swapcpu

swap组中的siso列显示从(si—换入)和写入(so—换出)交换空间的交换内存量。cpu组中的wasy列告诉您 CPU 等待数据处理系统(或内核)请求的时间百分比——因此不处理系统指令。

如果主机将大部分 CPU 周期用于将应用程序移入和移出交换空间,则siso列的值将会很高。实际值将取决于发生的交换数量和交换设备的速度。wa列通常也会显示 90 多的值,表明 CPU 正在等待从交换空间中检索应用程序或数据。

您可以通过使用top找出哪个应用程序使用了过多的 RAM,并可能更改其配置以使用较少的 RAM 来解决这个问题。大多数情况下,你会发现你的主应用程序使用了最多的内存。在这种情况下,请为您的系统分配或安装更多内存。

Note

top本身是相当资源饥渴的,所以如果一个系统没有响应并且负载非常重,重启它可能比等待top启动更容易更快。

procs - ---------memory------------- ---swap-- ---io--- --system-- ------cpu-----
 r  b   swpd   free     buff  cache   si   so    bi    bo    in    cs     us sy id wa st
 0  0   422336 750352   8252 130736    0    0     0     0     27   56     0  0 100  0  0
 0  0   422332 750352   8252 130736    1    0     1     0     27   55     0  0 100  0  0

在前面的示例中,这是一个主机处于轻负载下的示例。该主机交换空间低,CPU 大部分时间处于空闲状态。

磁盘存取

vmstat实用程序还将告诉我们有多少数据正在从磁盘读取和写入磁盘。在图 17-4 中,我们突出显示了io组中的bi(闭塞)和bo(闭塞)列。通过传递第二个数字作为参数,我们告诉vmstat在五次间隔后退出。

A185439_2_En_17_Fig4_HTML.jpg

图 17-4。

vmstat with low disk I/O

这两个数字告诉您在每个时间间隔内,从(bi)磁盘读取和写入(bo)磁盘的确切数据块数量。在我们的例子中,系统从磁盘读取的数据比写回的数据多,但是两个数字都很低。因为我们磁盘上的块大小是 4KB,这意味着它们负载不是很重。

Tip

如果您忘记了您的磁盘或分区的块大小,您可以发出以下命令:blockdev –getbsz <partition>

构成重负载的因素取决于数据块大小和磁盘速度。如果正在写入一个大文件,该数字将在写入操作期间上升,但稍后会再次下降。

当在一个空闲的系统上创建一个大文件时,通过检查得到的数字,可以了解主机的能力。运行dd命令在后台创建一个 1Gb 的只包含零的文件,同时运行vmstat,如图 17-5 所示。

A185439_2_En_17_Fig5_HTML.jpg

图 17-5。

vmstat with high disk I/O

在同一台主机上,我们将运行以下命令:

$ dd if=/dev/zero of=./largefile bs=1M count=1024

然后当我们同时运行vmstat命令时,你会看到类似于图 17-5 的东西。

在两次运行中,bo列都很低,因为文件数据仍然在内核缓冲区中。但是,在下一个时间间隔,您可以看到它增加到超过 222,000 个数据块。请记住,这超过了 5 秒,因此我们可以计算出该特定主机上的峰值写入速率大约为每秒 45,000 个数据块。

如果您注意到性能下降,并且vmstat显示的bo值接近长时间测试时系统可以管理的最大速率,则您的应用程序或服务正在非常努力地写入大量数据。这通常表明存在问题,因此您应该找出哪个应用程序是罪魁祸首并纠正问题。

dstat 更深入

您如何发现哪个应用程序可能有问题?vmstat命令对于发现问题很有用,但是它不允许您更深入地调查。另一方面,dstat命令是一个很好的挖掘工具。

您可以使用软件包管理器 YUM 或 Apt 通过 Ubuntu 和 CentOS 上的dstat软件包安装dstat。一旦安装了一个,您就可以发出如图 17-6 所示的输出。

A185439_2_En_17_Fig6_HTML.jpg

图 17-6。

dstat output

到目前为止,它与vmstat没有太大的不同,除了我们有为我们计算的磁盘读写,我们还有网络发送和接收信息。CPU、分页、系统与vmstat相同。

让我们假设我们已经注意到我们的应用程序性能很差,我们认为这是由于磁盘性能。当我们查看vmstat时,我们看到了图 17-7 中的结果。

A185439_2_En_17_Fig7_HTML.jpg

图 17-7。

Checking I/O with vmstat

我们可以看到有相当多的数据正在写入(bo),但我们不知道是哪个磁盘。对于我们的虚拟应用程序,我们有三个分区,分别安装到/app/log/app/sys/app/data。分区分别是sdc1sdb1sdb2。我们将利用这些信息来看看dstat说了什么。

在图 17-8 中,我们可以看到我们已经使用了–D切换到dstat来传递我们想要监控的分区。我们有大量的数据被写入sdc1,这是我们的应用程序日志。在我们假想的世界中,我怀疑我们的应用程序已经用调试日志记录启动了。

A185439_2_En_17_Fig8_HTML.jpg

图 17-8。

dstat disk access

同样,您可以使用–N开关向dstat ( -N em1,em2)提供网络接口,以报告每个接口的网络流量。–C将为 CPU 做类似的工作。你应该查看helpman页面了解更多信息。

当我们在第十八章讨论 Nagios 时,我们将回到如何在系统性能下降时获得通知。

持续性能监控

现在您已经有了诊断性能瓶颈的基本工具,我们将向您展示如何自动化持续的监控。这将使您能够访问更长期的性能和资源使用数据,进而使您能够更好地确定是否以及何时升级硬件或将服务迁移到其他主机。我们将向您介绍以下工具:

  • Collectd:系统统计信息收集守护程序
  • Graphite:收集和绘制度量数据的商店
  • Grafana:一个很好的前端界面,用于显示度量图

已收集

Collectd 是一个强大的系统指标收集守护程序,可以收集关于您的系统和各种应用程序和服务的信息。它是高度可配置的,开箱即可为您收集指标。

我们将使用 Collectd 从我们的主机收集指标,并将它们发送到 Graphite,一个指标收集存储。但是,您可以使用 Write HTTP 或 Network 插件将指标写入许多其他后端。

你可以通过你的软件包管理器在 Ubuntu 和 CentOS 上安装 Collectd。对于 CentOS,您需要安装epel-release包,因为标准存储库中没有 Collectd 包。

可从标准 Ubuntu 库获得的 Ubuntu 版本稍微落后于当前版本,但是 CentOS 版本是相当新的;根据您需要的功能,这可能会影响您的安装决策。

安装很简单。在 CentOS 上,您可以发出以下命令来安装 Collectd 和mysql插件:

$ sudo yum install collectd collectd-mysql

根据您的需求,您可以用相同的方式安装几个插件包(使用yum search collectd获取列表)。在 Ubuntu 上简单如下:

$ sudo aptitude install collectd

集合配置

Collectd 服务是从主collectd.conf文件配置的。当然,这些安装在哪里略有不同。在 Ubuntu 上,它被安装到/etc/collectd目录中,在 CentOS 上,它被安装到/etc/目录中。

您也可以将配置文件放入collectd.d目录。这在 Ubuntu ( /etc/collectd/collectd.conf.d/)和 CentOS ( /etc/collectd.d/)上当然是不同的。

Collectd 使用的配置文件有一个必需的格式。内容如下:

global options

# plugin without options
LoadPlugin plugin_name
# loading a plugin and overriding options
<LoadPlugin plugin_name>
    ...plugin options...
</LoadPlugin>
# provide an option block to plugins without a LoadPlugin stanza (requires AutoLoadPlugin true)
<Plugin plugin_name>
    ... plugin options...
</Plugin>

插件有两种:读插件和写插件(有些可以是读写插件)。读插件会收集数据,写插件会把收集到的数据放到某个地方。每个插件都可以放在自己的配置文件中;但是,如果 LoadPlugin 调用了多个副本,那么将执行读入的第一个副本,该插件的任何其他配置都将被忽略。该配置类似于 Apache web 服务器配置文件。配置块包含在<>...</>中。

在这个例子中,我们将收集基本的系统指标,比如 CPU、内存和磁盘空间,并且我们将使用mysql插件测量一些数据库统计数据。我们的数据库服务器恰好是 CentOS,安装mysql collectd插件已经在/etc/collectd.d目录下放置了一个mysql.conf文件。无论您使用哪种发行版,Collectd 配置都是相同的。

我们将首先看一下collectd.conf文件(清单 17-3 )。

#Hostname "localhost"
FQDNLookup true
#BaseDir "/var/lib/collectd"
#PluginDir "/usr/lib/collectd"
#TypesDB "/usr/share/collectd/types.db" "/etc/collectd/my_types.db"
#AutoLoadPlugin false
#CollectInternalStats false
#Interval 10
#MaxReadInterval 86400
#Timeout         2
#ReadThreads     5
#WriteThreads    5
#WriteQueueLimitHigh 1000000
#WriteQueueLimitLow   800000
Listing 17-3.collectd.conf Configuration Global Options

Collectd 的全局选项适用于 Collectd 服务本身,并定义了基本目录、插件目录、间隔和读/写线程以及队列限制等内容。这些可以大部分保留为默认值。FQDNLookup行意味着它将查找运行该程序的计算机的主机名,或者您可以通过Hostname设置强制这样做。

LoadPlugin syslog
<Plugin syslog>
    LogLevel info
</Plugin>

我们加载我们的第一个插件,它用于将我们的日志定向到带有infoLogLevelsyslog服务。debug值只有在 Collectd 已经编译了调试支持时才可用,notice、warning 和 err 也是可接受的。

LoadPlugin cpu
LoadPlugin df
LoadPlugin disk
LoadPlugin interface
LoadPlugin load
LoadPlugin memory
LoadPlugin mysql
LoadPlugin swap
LoadPlugin rrdtool

在本节中,我们将加载我们的插件,我们将使用这些插件来收集指标。我们涵盖了cpudfdiskinterfaceloadmemoryswap的基础知识。到目前为止,这些对您来说应该是不言自明的了。mysql插件将用于从我们的 MariaDB 数据库中收集一些 InnoDB 统计数据。最后,我们有了rrdtool插件,它将用于将指标输出为 RRD 文件。

RRD 代表“循环数据库”文件,通常用于保存时序数据。时序数据库保存db记录类型,如计数、仪表和直方图。他们可以以不同的粒度保存这些记录。您可能拥有每秒收集一次的记录,它们可以以这种粒度存储在 RRD 文件中。或者,我们可以为这些指标选择 5 分钟的粒度,1 秒钟的指标累计为 5 分钟的平均值。

还有其他存储指标的选项,我们将在下一节向您展示如何配置它们。

<Plugin cpu>
    ReportByCpu true
    ReportByState true
    ValuesPercentage false
</Plugin>

当我们为我们的 CPU 声明配置选项时,我们给出了按 CPU 报告和按状态报告的选项,并且我们没有以百分比显示值。这将为我们提供 CPU 工作负载的准确情况。

<Plugin df>
    FSType rootfs
    FSType sysfs
    FSType proc
    FSType devtmpfs
    FSType devpts
    FSType tmpfs
    FSType fusectl
    FSType cgroup
    IgnoreSelected true
</Plugin>

df插件展示了另一个如何细化我们想要选择的指标的例子。在这个例子中,我们列出了我们不想在指标中收集的值,然后我们使用IgnoreSelected true将它们从我们的收集中排除。列出的文件类型是伪文件系统,只会增加噪音。

<Plugin disk>
    Disk "/^(xv)?[hs]?d[a-z]\d?$/"
    IgnoreSelected false
    UseBSDName false
    UdevNameAttr "DEVNAME"
</Plugin>

使用disk插件,我们正在做与使用df插件相反的事情。在这里,我们选择我们想要监控的设备,并通过正则表达式给它们命名。我们可能有以下列方式命名的磁盘:

/dev/sda2
/dev/hdb4
/dev/xvda3

我们需要能够匹配这些磁盘命名约定中的任何一个。首先,我们一起寻找xv``(^(xv)?),并在字符串的开始(^)匹配这个模式零次或多次。接下来,我们可能有一个h或者一个s零次或者更多次([hs]?)。然后我们锚定在d周围,接着是任何az的磁盘([a-z]。然后,我们期望零个或多个分区号(\d?)。最后还匹配了行尾($)。

当我们与IgnoreSelected false匹配时,我们应该能够匹配我们的磁盘名称。UseBSDName false只在 MAC 上使用,UdevNameAttr "DEVNAME"表示我们尝试对磁盘设备使用 Udev 名称。

<Plugin interface>
    Interface "localhost"
    IgnoreSelected true
</Plugin>

同样,对于接口配置,我们使用了IgnoreSelected选项。我们希望收集除本地主机或环回接口之外的每个网络接口的指标。

<Plugin load>
    ReportRelative true
</Plugin>

<Plugin memory>
    ValuesAbsolute true
    ValuesPercentage false
</Plugin>

前面是内存和负载的默认值,类似于我们正在收集的其他系统指标。

<Plugin mysql>
    <Database no_db>
        Host "localhost"
        Port "3306"
        User “monitor"
        Password “monitorpasswd"
        ConnectTimeout 10
        InnodbStats true
    </Database>
</Plugin>

这个插件将和我们的 MariaDB 服务器一起工作。它只需要拥有USAGE权限的用户,并将收集内部指标,相当于mysql SHOW STATUS;命令。如果您需要查询指标,您可以查看dbi插件,您可以使用它对您的数据库运行指标。

mysql插件中,您可以在<Database> </Database>块中指定一个或多个数据库。

<Plugin rrdtool>
    DataDir "/var/lib/collectd/rrd"
    CacheTimeout 120
    CacheFlush 900
    WritesPerSecond 30
    CreateFilesAsync false
    RandomTimeout 0
</Plugin>

rrdtool插件是一个写插件,用于将rrd文件写入本地磁盘。一旦你启动 Collectd 服务,你会看到许多文件在/var/lib/collectd/rrd中被创建。虽然这对于小型系统来说很好,但是它不能很好地扩展,并且在系统使用量很大的情况下,会影响磁盘性能。这就是为什么许多人已经从这个系统转移到其他公制存储和收集系统,如石墨,它将很快取代它。

<Include "/etc/collectd/collectd.conf.d">
    Filter "*.conf"
</Include>

最后,我们有一个 include 语句,它表示将所有以.conf后缀结尾的文件包含在/etc/collectd/collectd.conf.d目录中。

开始和停止收集 d

启动和停止 Collectd 也非常简单。在 Ubuntu 和 CentOS 上,您可以发出以下命令:

$ sudo systemctl start collectd
$ sudo systemctl status collectd
$ sudo systemctl enable collectd

在前面几行中,我们已经启动了 Collectd,检查了状态,并在启动时启用了它。成功的状态输出如下所示:

-- Unit collectd.service has begun starting up.
Nov 15 21:55:26 backup collectd[8000]: plugin_load: plugin "syslog" successfully loaded.
Nov 15 21:55:26 backup collectd[8000]: plugin_load: plugin "cpu" successfully loaded.
Nov 15 21:55:26 backup collectd[8000]: plugin_load: plugin "interface" successfully loaded.
Nov 15 21:55:26 backup collectd[8000]: plugin_load: plugin "load" successfully loaded.
Nov 15 21:55:26 backup collectd[8000]: plugin_load: plugin "memory" successfully loaded.
Nov 15 21:55:26 backup collectd[8000]: plugin_load: plugin "mysql" successfully loaded.
Nov 15 21:55:26 backup collectd[8000]: plugin_load: plugin "rrdtool" successfully loaded.
Nov 15 21:55:26 backup collectd[8000]: Systemd detected, trying to signal readyness.
Nov 15 21:55:26 backup collectd[8000]: Initialization complete, entering read-loop.
Nov 15 21:55:26 backup systemd[1]: Started Collectd statistics daemon.
-- Subject: Unit collectd.service has finished start-up
-- Defined-By: systemd
-- Support: http://lists.freedesktop.org/mailman/listinfo/systemd-devel
--
-- Unit collectd.service has finished starting up.
--
-- The start-up result is done.
Nov 15 21:55:26 backup collectd[8000]: mysql plugin: Successfully connected to database <none> at server Localhost via UNIX socket with cipher <none> (server version: 5.5.50-MariaDB, protocol version: 10)

您可以看到我们已经成功地连接到我们的数据库。如果有任何问题,您应该检查systemctl状态或syslog

现在我们已经成功地设置好了,我们将看看如何设置 Graphite,这样我们就可以收集指标并集中存储它们。

石墨

根据网站上的说法,Graphite 只做三件事:踢屁股,嚼泡泡糖,让存储和绘制度量变得容易。我们没有资格验证前两种说法,但第三种说法是正确的。

石墨有四种成分。

  • Carbon:接收时序数据的高性能监听器
  • Whisper:度量数据的时间序列数据库
  • Graphite-web:一个呈现图形和仪表板的 web UI
  • Graphite-API:从时序 Whisper 数据库获取 JSON 数据的 API

在本练习中,我们将安装 Carbon、时序 Whisper 数据库和 Graphite-API。我们将使用 Grafana 来显示我们的指标(在下一节中),因此不需要安装 Graphite-web。

碳组件由碳继电器、碳聚合器和碳缓存组成。我们有兴趣安装碳缓存和碳继电器。所有这三个组件都有助于扩展碳服务,以支持每秒收集数千个指标。

carbon-cache 是一个必需的组件,它构成了基本的集合模型、carbon-cache、Whisper 和 Graphite-API。它能够监听 UDP 或 TCP 端口(2003、2004、2007),并支持 Python pickle 和换行符分隔的格式。carbon-cache 还配置数据在 Whisper 数据库中的存储方式。您可以基于某些度量模式存储不同的保留期。这些被称为存储模式。

carbon-relay 用于根据指标模式向某些后端服务器发送指标。有两种方式可以使用 carbon-relay,一种是将请求分片(使用一致散列)到多个后端,另一种是复制,将指标发送到任意数量的不同 carbon-cache 服务器或聚合器。

carbon-aggregator 用于缓冲以 carbon-cache 为目的地的指标,以帮助减少 I/O。可以对指标进行平均或求和,并作为单个指标推送到 Whisper,从而减少 carbon-cache 和 Whisper 数据库上的 I/O。

Whisper 是一个时间序列数据库,用于长期存储您的指标数据。它是用 Python 编写的,允许高精度数据分辨率随着时间的推移而降低。当数据降级时,数据点被“汇总”或聚合。您可以选择使用以下函数进行聚合:

  • 平均(默认)
  • 总额
  • 最大
  • 福建话
  • 最后的

您可以在这里找到有关 Whisper 的更多信息:

Note

有许多后端时间序列数据库可以用来代替 Whisper。这里列举了一些: https://graphite.readthedocs.io/en/latest/tools.html#storage-backend-alternates

Graphite-web 是查看数据的一种方式。它是一个 Python 应用程序,需要数据库的支持,可以是 SQLite(用于非常小的部署)、MariaDB 或 PostgreSQL。它还支持 LDAP 认证。

如果您想通过不同的图形表示服务来表示您的指标,您只需安装 Graphite-API,它将返回对指标查询的 JSON 响应。这为来自任意数量的第三方图形 web UIs 的度量提供了一个更轻量级的无状态界面。

石墨装置

使用公共打包工具 YUM 和 Apt,或者通过 Pip(Python 打包工具),可以很容易地安装 Graphite 组件。

在 CentOS 上,您可以安装 Graphite 和以下组件:

$ sudo yum install -y python-carbon

这将安装 carbon-cache 和 Whisper 数据库(python-whisper)。Graphite-API 是通过graphite-api包安装的。

$ sudo yum install –y graphite-api

在 Ubuntu 上,你需要安装以下软件来安装 carbon-cache 和 Whisper:

$ sudo aptitude install -y graphite-carbon

同样,要安装 Graphite-API 组件,您需要发出以下命令:

$ sudo aptitude install –y graphite-api

另一种方法是安装和试验石墨。Graphite 的开发者提供了一种快速安装和配置它的方法。

在撰写本文时,这将启动一个浮动的 Ubuntu 14.04,并安装 Graphite 软件的 0.9.5 版本。您将拥有一个快速启动并运行的环境,并开始对其进行测试。

配置碳缓存

石墨的大部分配置是在我们如何配置碳缓存。我们使用相同的文件来配置 carbon 缓存、聚合器和中继。为了实现这一点,可以将文件分成几个部分:[cache][aggregator][relay]。这个配置文件经过了很好的注释,是您可以使用的选项的真实来源。

Carbon-cache 和 carbon-relay 可以在同一个实例上运行;聚合器应该是一个独立的主机。在本练习中,我们将配置碳缓存和碳中继。

首先,我们将配置碳缓存。我们可以在[cache]部分配置通用或全局选项。

[cache]
STORAGE_DIR    = /var/lib/graphite/
LOCAL_DATA_DIR = /var/lib/graphite/whisper/
CONF_DIR       = /etc/carbon/
LOG_DIR        = /var/log/carbon/
PID_DIR        = /var/run/
ENABLE_LOGROTATION = False
USER = _graphite

在这里,我们将描述存储或检索数据和服务内务的一般配置。这里需要注意的是USER在 CentOS 上将是 carbon,_graphite在 Ubuntu 上将是 carbon。这些也必须为[relay][aggregator]部分设置。下一节将进一步描述该服务。在清单中,17-4 是随软件包一起安装的默认设置;这两个发行版是相同的。

ENABLE_UDP_LISTENER = False
UDP_RECEIVER_INTERFACE = 0.0.0.0
MAX_CACHE_SIZE = inf
MAX_UPDATES_PER_SECOND = 500
MAX_CREATES_PER_MINUTE = 50
CACHE_WRITE_STRATEGY = sorted
WHISPER_AUTOFLUSH = False
WHISPER_FALLOCATE_CREATE = True
USE_FLOW_CONTROL = True
LOG_LISTENER_CONNECTIONS = True
LOG_UPDATES = False
LOG_CACHE_HITS = False
LOG_CACHE_QUEUE_SORTS = True
USE_INSECURE_UNPICKLER = False
Listing 17-4.Default Carbon-Cache Settings

首先,缺省情况下禁用 UDP 监听器,将 TCP 监听器保留为缺省状态。然后我们有一些缓存写的设置。MAX_CACHE_SIZE设置为inf,或者无穷大。如果您看到大量交换或 CPU 受限的进程,您应该限制这种情况。随着缓存大小的增长,缓存的排序和查询变得越来越昂贵。MAX_UPDATES_PER_SECONDMAX_CREATES_PER_MINUTE被设置为限制高磁盘 I/O 争用。

CACHE_WRITE_STRATEGY可以有三种可能的设置。

  • sorted(默认):数据点作为有序列表刷新到磁盘。
  • max:频繁更新的数据点被刷新到磁盘,不频繁的数据点将在关机或磁盘 I/O 低的时候被刷新(如果有的话)。
  • naive:数据点在无序列表中刷新。

carbon.conf文件的注释中有关于这些设置的更多细节。WHISPER_AUTOFLUSHWHISPER_FALLOCATE_CREATE与内核选项有关。WHISPER_AUTOFLUSH设置为True将导致 Whisper 同步写入,由 carbon-cache 处理,WHISPER_FALLOCATE_CREATE(仅限 Linux)可以加速文件创建(因此允许您增加MAX_CREATES_PER_MINUTE)。

USE_FLOW_CONTROL设置为True(默认)意味着如果达到MAX_CACHE_SIZE,carbon-cache 将停止接受连接,直到缓存低于 95%。

接下来,我们有一组日志选项。将这些设置为True会产生高 I/O,如果您需要进一步调查问题,应该将其打开和关闭;我们保留了默认设置。

最后,在清单 17-4 中,有一个选项允许旧的和不太安全的版本 unpickler,USE_INSECURE_UNPICKLER = False。除非你有非常强烈的需求,否则就让它保持原样。

在下一节中,我们将定义碳缓存服务的接口和端口:

LINE_RECEIVER_INTERFACE = 0.0.0.0
PICKLE_RECEIVER_INTERFACE = 0.0.0.0
CACHE_QUERY_INTERFACE = 0.0.0.0
LINE_RECEIVER_PORT = 2003
PICKLE_RECEIVER_PORT = 2004
CACHE_QUERY_PORT = 7002

在这里,您可以让您的监听器监听某些 IP 地址(IP v4);您应该将端口保留为这里的默认值。行接收器支持换行符分隔的格式(这意味着换行符表示度量的结束)。pickle 接收器支持 Python pickle 对象序列化( https://docs.python.org/3/library/pickle.html )。

您可以通过多个缓存声明来提高碳缓存的性能。类似于[cache:1]的声明指定了不同的接收端口,如下所示:

[cache:1]
LINE_RECEIVER_PORT = 2103
PICKLE_RECEIVER_PORT = 2104

如果您在一个有几个 CPU 的主机上运行,那么每个 CPU 可以有一个碳缓存。

碳缓存的最后一部分是我们在 Whisper 数据库中存储指标数据的方式。我们通过 file / etc/carbon/storage-schemas.conf做到这一点。该文件可能如下所示:

[carbon]
pattern = ^carbon\.
retentions = 60s:90d

[default_1min_for_1day]
pattern = .*
retentions = 60s:1d

不同的指标可以有不同的保留期。你在给它们一个[name]之后定义它们。您可以看到,我们可以匹配与模式^carbon\.相匹配的指标(即以单词 carbon 开头的指标)。碳本身产生的指标可能是这样的:

carbon.agents.host1.cache.queries

对于此模式,我们可以看到它们具有 60 秒的分辨率,并以此分辨率存储 90 天(frequency:retention)。默认情况下,我们将在 1 天内以一分钟的分辨率记录所有指标,作为一个总括收集器。

假设我们想从数据库中收集指标。从数据库之类的地方收集指标会产生大量数据,因此我们可以相应地更改频率和保留时间。例如,您可以选择如下内容:

[database_metrics]
pattern = ^collectd_(db|backup)_*
retentions = 15s:7d,1m:90d,15m:180d

在这里,我们基于模式collectd_选择指标,后跟db_backup_,我们将在 7 天内保持 15 秒的频率,90 天内保持 1 分钟,180 天内保持 15 分钟。这意味着,默认情况下,我们的 15 秒指标将在 7 天后的一分钟内取平均值,失去了精确的分辨率。这将持续到第 90 天,届时它们将在 15 分钟内再次被平均化。

您可能需要更新防火墙,以允许访问端口 2003 和 2004。这就是配置一个基本碳缓存所需的全部内容。

配置碳继电器

在本节中,我们将配置碳继电器。在小型网络中,您不需要这样做,因为单个 Graphite carbon-cache 可以管理每台主机的大量指标。在这个场景中,我们的主机将在端口 2013 上向我们的relay.example.com主机发送它们的指标(这是一个行分隔的接收器)。从那里,我们将把指标发送到我们的monitor.example.com主机,它有一个 carbon-cache 守护进程监听端口 2004。

配置碳继电器需要一个carbon.conf文件和一个名为relay-rules.conf的文件。看一看carbon.conf,你会看到我们有一个[cache]部分,类似于我们之前看到的碳缓存配置。

[cache]
STORAGE_DIR    = /var/lib/graphite/
LOCAL_DATA_DIR = /var/lib/graphite/whisper/
CONF_DIR       = /etc/carbon/
LOG_DIR        = /var/log/carbon/
PID_DIR        = /var/run/
ENABLE_LOGROTATION = False
USER = carbon
LOG_LISTENER_CONNECTIONS = True
USE_INSECURE_UNPICKLER = False
USE_FLOW_CONTROL = True
CACHE_WRITE_STRATEGY = sorted

接下来,我们将创建一个[relay]部分。

[relay]
LINE_RECEIVER_INTERFACE = 0.0.0.0
PICKLE_RECEIVER_INTERFACE = 0.0.0.0
LINE_RECEIVER_PORT = 2013
PICKLE_RECEIVER_PORT = 2014
DESTINATIONS = monitor.example.com:2004
LOG_LISTENER_CONNECTIONS = True
RELAY_METHOD = rules
MAX_DATAPOINTS_PER_MESSAGE = 500
MAX_QUEUE_SIZE = 10000
QUEUE_LOW_WATERMARK_PCT = 0.8
USE_FLOW_CONTROL = True

它的设置类似于碳缓存。我们将监听端口 2013(行分隔指标)和 2014 (pickle 格式)。对于碳接力,我们需要为我们的指标提供一个(或多个)目的地。如果像我们一样使用RELAY_METHOD规则,那么您需要指定您在relay-rules.conf文件中列出的每个碳缓存。relay-rules.conf文件是基于特定匹配模式的中继目的地的有序列表。我们将很快讨论这一点,但现在我们将包括monitor.example.com目的地,因为我们正在讨论碳中继到碳缓存,我们将使用 pickle 端口。

MAX_DATAPOINTS_PER_MESSAGEMAX_QUEUE_SIZEQUEUE_LOW_WATERMARK_PCTUSE_FLOW_CONTROL是流量控制的选项。MAX_QUEUEMAX_DATAPOINTS应谨慎调整。LOW_WATERMARK意味着如果队列超过 80 %,我们将停止接受指标。

relay-rules.conf文件使我们能够将某些指标指向特定的碳缓存目的地。每个规则都需要一个唯一的名称、一个模式、一个目的地,以及我们是否在这个规则之后继续处理。在我们的示例中,我们使用了一个简单的relay-rules.conf文件,如下所示:

[default]
default = true
destinations = monitor.example.com:2004

default = true的规则只能有一个。默认情况下,我们将所有指标发送到端口 2004 上的monitor.example.com。假设我们想将指标从backup.example.com发送到不同的碳缓存,我们可以这样做:

[backup_example]
pattern = ^collectd_backup_example_com\.*
destination = 192.168.0.250:2004
continue = true

我们需要将 192.168.0.250 添加到carbon.conf文件中的DESTINATIONS字段。

你可以像这样在 CentOS 上启动和停止你的碳接力服务:

$ sudo systemctl start carbon-relay

像 Ubuntu 这样,我们将实例名传递给systemd服务:

$ sudo systemctl start carbon-relay@default

正在更新 Collectd 配置

现在,我们需要对 Collectd 配置进行更改,以便将指标发送到我们的碳继电器。有一个名为write_graphite的插件,它会将收集到的指标发送到石墨碳缓存或中继。

配置很简单,如下所示:

LoadPlugin write_graphite

<Plugin write_graphite>
  <Node "monitor">
    Host "relay.example.com"
    Port "2013"
    Protocol "tcp"
    ReconnectInterval 0
    LogSendErrors true
    Prefix "collectd_"
    StoreRates true
    AlwaysAppendDS false
    EscapeCharacter "_"
  </Node>
</Plugin>

这与我们之前看到的插件格式相同。在节点块中,我们正在设置中继主机和端口。我们还可以在度量标准和后缀中添加前缀。这里我们添加了collectd_作为每个指标的前缀,以便更容易地跟踪指标的来源。

我们现在可以重新启动 Collectd,服务和指标应该开始通过中继发送到监控服务器。但是,如果运行 CentOS,如果不修改 SELinux,将无法向端口 2013 发送指标。

您可以发出以下命令,允许 Collectd 使用tcp连接到网络:

$ sudo getsebool collectd_tcp_network_connect
collectd_tcp_network_connect --> off
$ sudo setsebool collectd_tcp_network_connect on

您还应该检查/var/log/audit/audit.log是否有任何被拒绝的消息。

检查日志

一旦我们重新启动了 carbon-cache、carbon-relay 和 collectd 服务,您应该会在日志中看到它们与服务的连接。

Collectd 将记录到我们配置中的 syslog,Graphite 将记录到/var/log/carbon目录。看看 Collectd,我们希望在日志中看到以下内容:

Nov 22 23:19:06 backup collectd[5999]: plugin_load: plugin "write_graphite" successfully loaded.

在我们的中继主机上,我们希望看到我们的主机在/var/log/carbon/listener.log中连接到我们。

22/11/2016 12:29:56 :: MetricLineReceiver connection with 192.168.0.30:38309 established

然后在我们的监控主机上,我们希望在/var/log/carbon/listener.log中看到这个。

20/11/2016 10:19:03 :: MetricPickleReceiver connection with 192.168.0.251:52146 established

因此,我们可以从日志中看到,我们正在备份主机上运行的 Collectd 服务与我们的中继和监控器之间建立连接。现在,我们将快速检查是否正在为我们的备份主机创建指标。监控主机上的/var/log/carbon/creates.log文件将显示我们是否正在从备份主机获取指标。

21/11/2016 11:35:48 :: new metric collectd_backup_example_com.cpu-0.cpu-user matched schema default_1min_for_1day

在这里,您可以看到我们为 CPU 创建了一个指标。好了,现在我们在备份主机上生成指标,并通过中继发送到我们的监控器。现在让我们设置我们的 Graphite-API 服务,这样我们就可以使用 Grafana 来查看它们。

配置 Graphite-API

正如我们已经说过的,Graphite-API 是 Graphite 的轻量级前端。它允许应用程序从我们的碳缓存中查询指标。要运行它,我们需要其他组件,Gunicorn 和 Nginx。

Gunicorn 是 unicorn 项目的 Python 分支,unicorn 是一个 web 服务器网关接口,支持许多 Ruby on Rails 应用程序。我们将用它将 Nginx web 服务器请求粘合到 Graphite-API 应用程序。

Nginx 我们已经简单谈过了。它是一个非常快速、低资源的 web 服务器,将位于 Gunicorn 的前面,并将 web 请求传递给它。我们将使用 Nginx 作为应用程序的代理服务。

设置 Gunicorn

我们将建立 Gunicorn 来为我们的 Graphite-API 应用程序服务。为此,我们将让它启动我们的应用程序,在一个环回接口上监听,并在一个特定的端口上监听。这将允许 Nginx 将请求代理到这个端口并提供响应。

我们可以通过pip命令安装 Gunicorn,这是 Python 包管理器。在 Ubuntu 上,我们需要使用pip3或 pip 的第 3 版来安装 Gunicorn,因为 Graphite-API 是 Python 3 安装的。在 CentOS 上,标准的pip命令就足够了,因为它是 Python 2.7 版本。

$ sudo pip3 install gunicorn

同样,特别是对于 Ubuntu,我们将为/usr/local/bin/gunicorn创建一个符号链接作为/usr/bin/gunicorn。这将使我们能够为 Ubuntu 和 CentOS 编写一个systemd服务文件。

$ ln –s /usr/local/bin/gunicorn /usr/bin/gunicorn

现在我们将创建systemd文件来启动 Gunicorn 服务。我们将创建一个服务文件和套接字文件,因为我们希望运行一个套接字来连接 Nginx。您可能还记得,本地的systemd文件位于/etc/systemd/system。我们将创建文件/etc/systemd/system/graphite-api.socket

[Unit]
Description=graphite-api socket

[Socket]
ListenStream=/run/graphite-api.sock
ListenStream=127.0.0.1:8881

[Install]
WantedBy=sockets.target

这里我们对systemd说,创建一个套接字/run/graphite-api.sock来监听端口 8881 上的环回地址。WantedBy将意味着当socket.target被激活时这将被启动。

现在查看服务文件/etc/systemd/system/graphite-api.service,我们可以看到以下详细信息:

[Unit]
Description=Graphite-API service
Requires=graphite-api.socket

[Service]
ExecStart=/usr/bin/gunicorn -b 127.0.0.1:8881 -w2 graphite_api.app:app
Restart=on-failure
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

这里可以看到我们调用了gunicorn命令来启动graphite_api.app。我们要求它连接到端口 8881 上的 127.0.0.1。我们还要求graphite-api.socket–w2表示启动两个工作进程来处理请求。

我们现在可以使用systemctl命令来启动和停止服务和套接字。

$ sudo systemctl start graphite-api.service

你可以在这里阅读更多关于 Gunicorn 的内容:

设置 Nginx

虽然 Gunicorn 是一个 web 服务器,但建议部署在代理服务器之后。代理服务器可以处理成千上万个连接,并充当客户端和 Gunicorn 进程之间的缓冲。

我们需要做的就是将下面的graphite.conf文件放在 Nginx 服务器可以找到的地方:

upstream graphite {
    server 127.0.0.1:8881 fail_timeout=0;
}

server {
    server_name monitor;
    listen 80;
    root /var/www;

    location / {
        try_files $uri @graphite;
    }

    location @graphite {
        proxy_pass http://graphite;
    }
}

使用 Nginx,代理服务器被声明为上游服务器。这是在上游<name> { .. }部分声明的。在本节中,我们告诉 Nginx 将对 Graphite 上游服务器的任何请求发送到服务器 127.0.0.1:8881,我们的 Gunicorn 服务器将在那里监听。如果您列出了不止一个后端服务器,Nginx 将在两者之间进行循环调度。

server { ... }部分是前端服务器声明。我们提供一个服务器名、我们监听的端口和根路径。根不需要存在,因为所有请求都将传递到后端服务器。我们用location / { ... }指令来做这件事。当我们向任何 URI 发出请求时,我们首先尝试返回 URI,然后,如果找不到,我们将请求发送到@graphite位置。位置@graphite { ... }然后将请求发送到位于http://graphite的上游服务器(这是我们在文件顶部声明的上游服务器)。

根据您的发行版,您将执行以下任一操作。在 Ubuntu 上,你将graphite.conf放在/etc/nginx/sites-available/目录中。然后我们需要创建到sites-enabled的符号链接,并像这样启动 Nginx:

$ sudo ln –s /etc/nginx/sites-available/graphite.conf /etc/nginx/sites-enabled/graphite.conf
$ sudo systemctl start nginx

在 CentOS 上,我们需要将graphite.conf放在下面的目录中,/etc/nginx/conf.d/。然后我们可以像平常一样启动 Nginx。

$ sudo systemctl start nginx

默认情况下,这两个发行版都将记录到/var/log/nginx。我们可以通过以下方式测试我们的 API 是否正常工作:

$ curl -H 'Host: monitor' http://127.0.0.1:80/metrics/find?query=*
[{"text": "carbon", "expandable": 1, "allowChildren": 1, "id": "carbon", "leaf": 0}, {"text": "relay", "expandable": 1, "allowChildren": 1, "id": "relay", "leaf": 0}]

这表明我们可以联系 Nginx web 服务器,这将向后端 Gunicorn 服务发出请求,后者将返回我们对指标的查询请求。

格拉凡娜

Grafana 是一个可视化 web 应用程序,用于绘制时间序列数据。它可以与各种数据源对话,包括 Graphite-API。使用新创建的指标,您现在可以创建仪表板并显示这些指标。

Grafana 支持 LDAP 认证,并可以使用数据库来存储用户的仪表板。它有一个独立的 web 服务,您可以与之交互。您可以将它安装在本地主机或中央服务器上;它不在本地存储指标数据,而是向后端存储服务发出请求,在我们的例子中是 Graphite。

安装 Grafana

我们将在我们的 Ubuntu 服务器上安装 Grafana。我们从这里开始要按照指示: http://docs.grafana.org/installation/debian 。可以按照这里的说明进行 CentOS: http://docs.grafana.org/installation/rpm/ 。也可以在 macOS 或 Windows 上安装 Grafana。

首先,我们将添加 Grafana 存储库。

$ sudo bash -c 'echo "deb https://packagecloud.io/grafana/stable/debian/ jessie main" > /etc/apt/sources.list.d/grafana.list'
$ curl https://packagecloud.io/gpg.key | sudo apt-key add -
$ sudo aptitude update
$ sudo aptitude install –y grafana

一旦安装完毕,我们就可以启动 Grafana 服务了。这对于 Ubuntu 和 CentOS 都是一样的:

$ sudo systemctl start grafana-server

现在我们可以通过以下网址访问 Grafana:http://monitor:3000。默认情况下,用户名和密码是 admin(图 17-9 )。

A185439_2_En_17_Fig9_HTML.jpg

图 17-9。

Logging into Grafana

添加存储后端

在我们可以绘制任何新收集的指标之前,我们需要添加我们的石墨后端。我们需要将其添加为数据源(图 17-10 )。

A185439_2_En_17_Fig10_HTML.jpg

图 17-10。

Selecting data sources

从数据源屏幕,我们点击绿色的“添加数据源”按钮,并填写详细信息(图 17-11 )。

A185439_2_En_17_Fig11_HTML.jpg

图 17-11。

Adding Graphite as our data source

点击 Save & Test 将尝试向我们的 Graphite 主机发出一个请求,并测试它是否可以获得适当的数据。如果我们单击 Dashboards 选项卡,我们将导入 Graphite metrics 仪表板(图 17-12 )。

A185439_2_En_17_Fig12_HTML.jpg

图 17-12。

Clicking Import

导入仪表板后,您可以点击石墨碳指标,您将看到图 17-13 。

A185439_2_En_17_Fig13_HTML.jpg

图 17-13。

Carbon Metrics graph

让我们通过选择新建来创建一个新的仪表板,如图 17-14 所示。

A185439_2_En_17_Fig14_HTML.jpg

图 17-14。

Creating a new graph

创建仪表板时,左侧会出现一个绿色栏。然后选择添加面板和图形,如图 17-15 所示。

A185439_2_En_17_Fig15_HTML.jpg

图 17-15。

From the left drop-down, select Graph

当您添加一个面板时,您会得到一个基于假数据的测试度量图(图 17-6 )。

A185439_2_En_17_Fig16_HTML.jpg

图 17-16。

Test metric

要创建我们自己的图表,我们必须首先选择数据源。就这么办吧,如图 17-17 所示。

A185439_2_En_17_Fig17_HTML.jpg

图 17-17。

Selecting the Panel data source

一旦我们选择了数据源,测试数据就会消失。您可以单击“选择指标”并选择您想要绘制的指标的来源。我们已经选择了“collectd_backup_example_com”,然后我们可以选择我们感兴趣的指标。我们将从列表中选择“加载”。然后,我们选择所有可用的指标(*),然后再次选择(*)。在图 17-18 中,您可以看到我们在 15 分钟内跟踪了主机的负载。你可以在 Grafana 的这个 YouTube 教程中看到更多关于 Grafana 的内容: https://youtu.be/1kJyQKgk_oY

A185439_2_En_17_Fig18_HTML.jpg

图 17-18。

Seeing load metrics on backup host

既然您已经收集并发现了您的度量标准,那么您就可以进行性能调整,并能够测量它们的效果。

性能优化

安装您的主机时,它的默认设置可为大多数配置提供合理的性能。但是,由于每个主机都不同,您通常可以调整设置,使配置对于特定的工作负载来说更加优化。

在本节中,我们将向您展示一些提示和技巧,它们应该有助于加快您的主机执行大多数与服务器相关的任务的速度。

资源限制

Linux 允许您通过ulimit命令对用户实施资源限制。这实际上是 Bash shell 的一个内置函数。通过该命令设置的任何限制仅适用于当前 shell 及其启动的进程。

要报告当前限值,运行带有-a选项的ulimit,如图 17-19 所示。

A185439_2_En_17_Fig19_HTML.jpg

图 17-19。

ulimit -a

为了防止这个 shell 用户运行 fork bomb,您可以使用-u选项更改正在运行的进程的最大数量,例如从 3909 更改为 1。如果您试图运行一个新的进程,系统会阻止您这样做。

$ ulimit -u 1
$ ls
bash: fork: Resource temporarily unavailable

您会收到一个错误,指出 shell 无法派生您尝试运行的命令。我们并不建议您以这种方式管理用户,而是演示该命令的效果。

Caution

如果您通过 SSH 或 X 登录,您将已经有几个正在运行的进程。

其他有用的限制是最大内存大小,可以通过-m选项设置,以及打开文件的数量,可以通过-n设置。针对资源限制进行调优时,最常见的更改是用户可以打开的文件数量。

您可以在man bash页面的ulimit部分获得选项及其功能的完整列表。

为所有用户设置限制

ulimit命令为当前 shell 设置限制,因此您可以通过/etc/profile/etc/bashrc为登录的用户添加限制。然而,大多数守护进程不使用 shell,因此向 shell 配置文件添加限制不会有任何效果。

您可以使用 PAM 的limits模块。当用户创建一个新流程时,这个模块被调用,它设置您可以在/etc/security/limits.conf中定义的限制。该文件为用户和组定义了软限制和硬限制。我们在图 17-20 中包含了 CentOS 的样本limits.conf

A185439_2_En_17_Fig20_HTML.jpg

图 17-20。

Setting limits for all users

每一行指定该限制适用的域。该域可以是通配符、用户名或组名。接下来是限制类型,可以是软的,也可以是硬的。硬限制只能由 root 用户设置,当用户或进程试图突破此限制时,系统会阻止。用户可以使用ulimit命令调整软限值,但只能增加到硬限值。

接下来是资源是有限的。可用资源的完整列表包含在示例文件中,也可以在man limits.conf页面上找到。每行的最后一个是限值应该设置的值。以#开头的行是注释或例子。

在示例文件中,@faculty组被允许根据它们的软限制运行 20 个并发进程(nproc)。但是,该组中的任何成员都可以将该限制更改为不超过 50 的任何值,但不能高于该值。

sysctl 和 proc 文件系统

我们在第九章中简单提到了 proc 文件系统,作为一种直接从内核获取系统信息的方式。它还提供了一种调整正在运行的内核以提高系统性能的方法。为此,您可以在/proc/sys目录下的虚拟文件中更改值。

虚拟文件根据它们影响的系统部分分组在子目录中,如表 17-2 所列。

表 17-2。

/proc/sys Subdirectories

| 目录 | 使用人 | | --- | --- | | 踝肱指数 | 系统仿真(例如,在 64 位主机上运行 32 位应用程序)。 | | 秘密党员 | 与加密相关的信息,如密码、模块等。 | | 调试 | 这是一个空目录;没用过。 | | 偏差 | 设备特定的信息。 | | 满量程 | 文件系统设置和调整。 | | 核心 | 内核设置和调优。 | | 网 | 网络设置和调谐。 | | sun RPC(sun RPC) | Sun 远程过程调用(NFS)设置。 | | 伏特计 | 内存、缓冲区和缓存设置和调整。 |

我们不会详细讨论每个文件,但是我们会给你一个可以做的调整的概念。

每个虚拟文件包含一个或多个可以通过catsysctl实用程序读取的值。要列出所有当前设置,我们可以使用以下内容:

$ sysctl –a
abi.vsyscall32 = 1
crypto.fips_enabled = 0
debug.exception-trace = 1
debug.kprobes-optimization = 1
dev.hpet.max-user-freq = 64
dev.mac_hid.mouse_button2_keycode = 97
...
vm.percpu_pagelist_fraction = 0
vm.stat_interval = 1
vm.swappiness = 30
vm.user_reserve_kbytes = 29608
vm.vfs_cache_pressure = 100
vm.zone_reclaim_mode = 0

要读取特定的值,将其键作为参数传递给sysctl

$ sudo sysctl vm.swappiness
vm.swappiness = 30

关键是文件的完整路径,去掉了/proc/sys/,斜杠也可以替换为句号。例如,为了检查您的主机使用交换内存的可能性,您可以检查/proc/sys/vm/swappiness的内容。

$ cat /proc/sys/vm/swappiness
30

这个特定的值表明,在一段时间没有使用信息之后,内核将信息从 RAM 转移到交换空间的可能性有多大。数字越高,这种可能性越大。在这种情况下,该值的范围为 0 到 100,默认值为 60。如果您想确保您的主机不太可能使用交换内存,您可以通过sysctl实用程序和-w选项将该值设置为 20。然后,您需要传递想要更改其值的键和新值。

$ sudo sysctl -w vm.swappiness=20

另一个例子是系统中任何时刻可以打开的文件和目录的数量。这在/proc/sys/fs/file-max中定义,您可以通过命令sysctl fs.file-max读取该值。要将打开文件的最大数量增加到 50 万,运行sudo sysctl -w fs.file-max=500000

Caution

更改 proc 文件系统中的内核变量不仅会对系统性能产生积极影响,还会产生巨大的负面影响。除非有充分的理由,否则我们建议您不要更改任何内容。一个好的调谐方法是测量、改变、测量。如果你不能测量,就要非常小心。

当系统重新启动时,这些变量被重置为默认值。为了使它们持久化,您可以在/etc/sysctl.conf文件中添加一个适当的条目。当系统启动时,该文件中的所有设置都通过sysctl -p命令应用于系统。

在 Ubuntu 上,不用直接编辑/etc/sysctl.conf,可以在/etc/sysctl.d/<number>-<option-name>.conf中创建一个文件,它们会在开机时被读入。

在 CentOS 上,您可以在/usr/lib/sysctl.d中找到默认的系统设置。你可以在/etc/systctl.conf中覆盖这些,或者在/etc/sysctl.d/中放置一个类似 Ubuntu 的文件。

关于sysctl设置的更多信息可通过man sysctl.d找到。在 https://www.kernel.org/doc/Documentation/sysctl/ 可获得可用变量的完整列表和文档。

存储设备

在第九章中,您看到了在磁盘故障的情况下,一旦添加了替换磁盘,内核就需要重建 RAID 阵列。此任务会生成大量 I/O,并会降低机器可能提供的其他服务的性能。或者,您可能希望以牺牲服务为代价,优先考虑重建过程。

当重建发生时,内核将重建速度保持在设定的最小值和最大值之间。我们可以通过/proc/sys/dev/raid目录中的speed_min_limitspeed_max_limit条目来改变这些数字。

更可接受的最低速度是每个磁盘每秒 20,000K,您可以使用sysctl来设置它。

$ sudo sysctl -w dev.raid.speed_limit_min=20000

将最小值设置得太高会对系统性能产生负面影响,因此请确保将该值设置得低于系统可以处理的最大吞吐量除以 RAID 阵列中的磁盘数。

通过设置dev.raid.speed_limit_max变量可以改变的最大值已经相当高了。如果您希望 RAID 重建对性能的影响更小,以更长的等待时间为代价,您可以降低该数值。

为了使这些更改永久化,将这些键值对添加到/etc/sysctl.conf

文件系统调整

每次访问文件或目录时,即使只是为了读取,也需要更新其最后访问的时间戳(或atime)并将其写入磁盘。除非您需要这个时间戳,否则您可以通过告诉您的主机不要更新它们来加速磁盘访问。

默认情况下,您的系统应该使用relatime选项挂载您的磁盘。此选项意味着每次文件访问不会启动对文件元数据的更新,这通过不发出不必要的操作来提高性能。它是noatime的同义词。

如果没有设置,您只需将noatime选项添加到您想要启用该选项的每个文件系统的/etc/fstab文件的options字段中。

UUID=f87a71b8-a323-4e8e-acc9-efb0758a0642 / ext4 defaults,
   errors=remount-ro,relatime 0 1

这将在下次挂载文件系统时启用该选项。要立即激活它,您可以使用mount命令重新挂载文件系统。

$ sudo mount -o remount,relatime /

除了挂载选项之外,文件系统本身也提供了一些可以提高性能的特性;这取决于特定文件系统的用途。其中最主要的是dir_index,它适用于 ext2、ext3 和 ext4 文件系统。启用此功能会导致文件系统创建内部索引,从而加快对包含大量文件或子目录的目录的访问。您可以使用tune2fs实用程序来检查它是否已启用。

$sudo tune2fs -l /dev/md0 | grep features
Filesystem features: has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg
    sparse_super large_file huge_file uninit_bg dir_nlink extra_isize

如果你需要改变特性,而你很少这样做,你可以使用tune2fs来启用一个特定的特性。

sudo tune2fs -O dir_index /dev/md0

或者,可以通过在名称前加一个脱字符号来关闭特性。

sudo tune2fs -O ^dir_index /dev/md0

您可以通过更改/etc/mke2fs.conf文件中的默认值来设置应该在各种 ext 文件系统上启用哪些选项。

输入输出调度程序

I/O 调度程序,也称为 I/O 电梯,是内核用来对磁盘子系统的 I/O 进行排序的算法。可以更改调度程序来提高该过程的效率。内核有三个调度程序可用。

  • Cfq:这是 SATA 设备的默认调度程序,并试图为处理 I/O 实现完全公平的队列。
  • 截止时间:这是块设备的默认调度程序。它优先选择读取请求,并试图通过对开始时间设置一个截止日期来为请求提供确定性。
  • Noop:这是一个先进先出队列,其中 I/O 留给下级子系统排序;它适用于虚拟机。

您可以通过执行类似下面的命令在您的设备上找到调度程序:

$ cat /sys/block/sda/queue/scheduler
noop deadline [cfq]

[]中的调度程序是当前设置。您可以使用以下命令更改它:

$ sudo bash -c  'echo deadline > /sys/block/sda/queue/scheduler'
$ cat /sys/block/sda/queue/scheduler
noop [deadline] cfq

您可以通过编辑 GRUB 在引导时设置调度程序。您需要通过添加以下内容来编辑以下文件:

$ vi /etc/default/grub
GRUB_CMDLINE_LINUX="console=tty0 ... rhgb quiet elevator=deadline"

这会将deadline设置为整个服务器的默认调度程序。然后,您需要使用以下内容重新制作 GRUB:

$ sudo grub2-mkconfig -o /boot/grub2//grub.cfg

现在,当您重新启动主机时,默认的调度程序是deadline

$ cat /sys/block/sda/queue/scheduler
noop [deadline] cfq

记住在 Ubuntu 上grub2-mkconfig需要这样:

$ sudo grub2-mkconfig -o /boot/grub/grub2.cfg

现在,您的更改将在重新启动后保持不变。

摘要

在本章中,我们向您展示了简单的工具,这些工具使您能够轻松地确定正在运行的主机的基本健康状况。您学习了如何执行以下操作:

  • 检查 CPU 使用率
  • 检查内存和交换空间使用情况

我们还引入了更高级的系统指标收集工具,如 Collectd 和 Graphite,它们将帮助您持续监控资源使用和性能。您学习了如何执行以下操作:

  • 安装和配置 Collectd
  • 安装和配置石墨
  • 使用 Grafana 监控和可视化主机的运行状况

我们还提供了一些关于一些常见的性能调优以及如何使用sysctl改变内核设置的信息。

在下一章中,您将看到如何配置对您的主机和服务的一些监控。我们还将向您展示如何配置日志记录,以及如何监控异常或可疑活动的日志。

十八、日志记录和监控

在本书中,我们讨论了日志记录和监控,以及它们在应用程序和服务故障诊断中的价值。在本章的第一节,我们将会看到日志在 Linux 操作系统上是如何工作的,以及如何利用这些数据。我们将了解如何存储、聚合、分析、发送警报以及循环日志条目和日志。我们还将看看一些工具,使它更容易与您的日志进行交互。

在本章的第二部分,我们将展示如何使用一个名为 Nagios 的开源工具来监控我们介绍的应用程序和服务。Nagios 允许您定义您的主机和在其上运行的服务。然后,您可以确保这些主机已启动,并且您的服务正常运行。如果它们不起作用,您的监控系统可以通知您出现了什么问题。这一过程可以大大加快发现和解决问题的速度。

记录

你已经在整本书中看到,许多应用程序和工具记录关于它们的动作和状态的数据。您已经看到我们可以使用journalctl命令来查看日志,并且日志可以在各种文件中的/var/log目录中结束。日志记录通常由两个守护进程完成:?? 守护进程和 ?? 守护进程。现在,我们将逐一看一下。

-报纸

作为systemd的一部分,引入了新的日志功能。Journald 创建二进制结构的日志文件,可以用来代替传统的(r ) syslog日志记录。以下是日记日志文件的一些功能:

  • 日志篡改可以被检测到,并且不容易被手动编辑。
  • 日志是索引,可以加快搜索速度。
  • 日志采用结构化格式,具有明确定义的字段。
  • journald为每个日志消息收集额外的日志元数据。
  • Journald 支持导出格式(比如 JSON)。

默认情况下,日志存储在系统内存或环形缓冲区中,不会永久存储。由于额外的元数据,日志事件比普通日志更大,占用更多的空间。您可以将它们持久化到磁盘上,我们将在本章稍后向您展示如何做到这一点。

我们使用已经多次使用的journalctl命令来访问日志文件。如果您在没有任何参数或过滤器的情况下执行journalctl命令,您将看到类似清单 18-1 的内容。

$ sudo journalctl
-- Logs begin at Fri 2016-11-25 22:16:00 AEDT, end at Mon 2016-11-28 21:07:54 AEDT. --
Nov 25 22:16:00 au-mel-centos-1 systemd-journal[89]: Runtime journal is using 8.0M...current limit 92.0M).
Nov 25 22:16:00 au-mel-centos-1 systemd-journal[89]: Runtime journal is using 8.0M...current limit 92.0M).
Nov 25 22:16:00 au-mel-centos-1 kernel: Initializing cgroup subsys cpuset
Nov 25 22:16:00 au-mel-centos-1 kernel: Initializing cgroup subsys cpu
Nov 25 22:16:00 au-mel-centos-1 kernel: Initializing cgroup subsys cpuacct
Nov 25 22:16:00 au-mel-centos-1 kernel: Linux version 3.10.0-327.4.5.el7.x86_64 ...CentOS 4.8.3-9) (GCC)...
Nov 25 22:16:00 au-mel-centos-1 kernel: Command line: BOOT_IMAGE=/vmlinuz-3.10.0-327.4.5.el7.x86_64...
Nov 25 22:16:00 au-mel-centos-1 kernel: e820: BIOS-provided physical RAM map:
Listing 18-1.The journalctl Command

journalctl的输出将显示我们启动主机时的日志,为了清楚起见,我们已经截断了一些长行。您首先看到日志开始,然后看到cgroups的初始化,接着是 Linux 内核版本和它是如何构建的。然后我们看到内核加载命令,接着是低级内存初始化。

清单 18-1 的第一行显示了第一个条目的日期和最后一个条目的日期。您可以看到这里的每个日志都由四个部分组成。

  • 时间戳
  • 主机名
  • 发出日志的服务
  • 日志消息

进一步看一下journalctl命令,我们将探索它的一些可用选项。例如,要查看日志的最后十行,我们将发出以下命令:

$ sudo journalctl –n 10
-- Logs begin at Fri 2016-11-25 22:16:00 AEDT, end at Mon 2016-11-28 21:24:54 AEDT. --
Nov 28 21:01:01 backup run-parts(/etc/cron.hourly)[9627]: starting 0yum-hourly.cron
...
Nov 28 21:24:54 backup sudo[9647]:   bsingh : TTY=pts/0 ; PWD=/home/bsingh ; USER=root ; COMMAND=/bin/journalctl -n 10

这里我们看到最后十行(省略了八行),最后一个日志条目是我们刚刚输入的sudo命令。我们在这里看到的是由我们刚刚谈到的四个字段组成的日志的简化视图。有很多关于日志的数据我们看不到。让我们更仔细地看看最后一个条目的详细输出(参见清单 18-2 )。

$ sudo journalctl -n 1 -o verbose
-- Logs begin at Fri 2016-11-25 22:16:00 AEDT, end at Mon 2016-11-28 21:29:46 AEDT. --
Mon 2016-11-28 21:29:46.407435 AEDT [s=1798dd89d9ff412b8edcc9e7b5cb8484;i=1c0b;b=11ffa7916baa42a89cbbc756af6d26bc;m=2486cb476a;t=54259f362b5ab;x=68026ef74697a39]
    _BOOT_ID=11ffa7916baa42a89cbbc756af6d26bc
    _MACHINE_ID=e3c7fd86ed8b4ef69e569a93e30db6ab
    PRIORITY=5
    _CAP_EFFECTIVE=1fffffffff
    _HOSTNAME=backup
    _TRANSPORT=rsyslog
    SYSLOG_FACILITY=10
    _AUDIT_LOGINUID=1000
    _SYSTEMD_OWNER_UID=1000
    _SYSTEMD_SLICE=user-1000.slice
    SYSLOG_IDENTIFIER=sudo
    _COMM=sudo
    _EXE=/usr/bin/sudo
    _SELINUX_CONTEXT=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
    _AUDIT_SESSION=40
    _SYSTEMD_CGROUP=/user.slice/user-1000.slice/session-40.scope
    _SYSTEMD_SESSION=40
    _SYSTEMD_UNIT=session-40.scope
    _UID=1005
    _GID=1006
    MESSAGE=  bsingh : TTY=pts/0 ; PWD=/home/bsingh ; USER=root ; COMMAND=/bin/journalctl -n 1 -o verbose
    _PID=9653
    _CMDLINE=sudo journalctl -n 1 -o verbose
    _SOURCE_REALTIME_TIMESTAMP=1480328986407435
Listing 18-2.Journal Metadata

您可以看到每个日志都包含大量相关的元数据。日志日期范围信息后的第一行是光标位置信息,或日志中条目的位置。其余的是带有大量可过滤数据的键/值对。现在让我们来看看一些常见的过滤器。

Journalctl Filters

日志记录工具的伟大之处在于它是结构化的,这意味着我们可以轻松地过滤我们感兴趣的信息。任何有 Linux 经验的人都会告诉你他们最喜欢的调查日志的咒语。有了过滤器,我们不再需要如此依赖通过命令grepawk来传输日志。

我们将从使用–k选项过滤内核消息的journalctl输出开始。

$ sudo journalctl –k
-- Logs begin at Fri 2016-11-25 22:16:00 AEDT, end at Mon 2016-11-28 22:06:08 AEDT. --
Nov 25 22:16:00 au-mel-centos-1 kernel: Initializing cgroup subsys cpuset
Nov 25 22:16:00 au-mel-centos-1 kernel: Initializing cgroup subsys cpu
...
Nov 28 21:01:01 backup kernel: SELinux: initialized (dev tmpfs, type tmpfs), uses transition SIDs
Nov 28 22:01:01 backup kernel: SELinux: initialized (dev tmpfs, type tmpfs), uses transition SIDs

现在让我们将这个长长的列表缩减为只有那些具有ERROR优先级(-p)的内核日志消息。

$ sudo journalctl -p err -k
-- Logs begin at Fri 2016-11-25 22:16:00 AEDT, end at Mon 2016-11-28 22:10:14 AEDT. --
Nov 25 22:16:13 backup kernel: CIFS VFS: Send error in SessSetup = -127
Nov 25 22:16:13 backup kernel: CIFS VFS: cifs_mount failed w/return code = -127
Nov 25 22:17:13 backup kernel: CIFS VFS: Send error in SessSetup = -13

这里我们列出了所有出错的内核日志。有七个优先级可供使用;单词或数字都可以。

  • emerg(0) —紧急情况
  • alert(1)
  • crit(2) —关键
  • err(3) —错误
  • warning(4)
  • notice(5)
  • info(6)—信息
  • debug(7)

–k选项与使用dmesg命令查看日志是一样的,但是正如您所见,journalctl更容易阅读和过滤。

我们可以组合过滤器。我们可以使用–b选项查看自上次系统启动以来的消息。

$ sudo journalctl –p info –b

这通常仍然是大量的消息,因此我们可以使用更具体的时间过滤器来进一步细化。

$ sudo journalctl -p info --since "2016-11-28 22:44:00" --until "2016-11-28 22:54:00"

这里我们以优先级info显示过去十分钟的日志。时间是当地时间。如果您想查看 UTC 时间,您可以发出以下命令:

$ sudo journalctl --utc

在清单 18-2 中,我们看到了日志中包含的字段的完整列表。我们也可以使用这些元数据作为过滤器。清单 18-2 中的一个字段是_COMM,它记录了用于生成日志的命令。我们可以列出该字段的所有不同值,如下所示:

$ sudo journalctl –F _COMM
unix_chkpwd
request-key
freshclam-sleep
run-parts
pickup
usermod
...

假设我们想要搜索自上次引导以来发生的任何usermod变化。

$ sudo journalctl _COMM=usermod -b
-- Logs begin at Fri 2016-11-25 22:16:00 AEDT, end at Mon 2016-11-28 23:09:04 AEDT. --
Nov 25 22:48:41 backup usermod[4844]: add 'jsmith' to group 'sales'
Nov 25 22:48:41 backup usermod[4844]: add 'jsmith' to shadow group 'sales'

我们还可以将这些字段中的任何一个与--since--until时间过滤器结合起来,以获得更窄的视图。列出两个字段(用空格分隔)提供了一个逻辑 AND 列表(_COMM=usermod  _COMM=useradd)。使用一个+将给出一个逻辑 OR 列表(_COMM=usermod + _HOSTNAME=backup,它将提供一个带有usermod的列表和任何带有主机名备份的列表。

Tip

键入journalctl并按下 Tab 键两次将给出可用字段的列表。

我们也可以通过它们的systemd单元名称来列出日志。在这里,我们将使用下面的代码来跟踪(类似于tail命令)httpd日志:

$ sudo journalctl -f -u httpd.service
-- Logs begin at Fri 2016-11-25 22:16:00 AEDT. --
Nov 28 23:27:11 backup systemd[1]: Starting The Apache HTTP Server...
Nov 28 23:27:11 backup systemd[1]: Started The Apache HTTP Server.

这里您可以看到 Apache 服务启动时的输出。

使用 fss 保护日志

我们前面说过,我们可以检测日志是否被篡改。这是通过一个名为前向安全密封(FSS)的特性实现的,该特性使用生成的密钥对之一对日志进行签名。密封密钥将在指定的时间间隔密封日志,验证密钥可用于检测篡改。日志以可配置的定期间隔被签名或密封。这为您的日志提供了一定程度的安全性。

但是,它不能阻止攻击您系统的人掩盖他们的踪迹,他们可以通过删除日志或在密封时间间隔之间进行编辑来绕过这一点。如果有人篡改您的日志,它不会提供任何额外的信息,但会给你一个时间范围,在这种情况下发生。它可以被视为整个系统安全的一小部分。

要使用 FSS,首先需要启用日记日志文件的持久性存储。这可以通过发出以下命令轻松实现:

$ sudo mkdir /var/log/journal
$ sudo systemctl restart systemd-journald

然后,为了生成密钥对,我们将发出图 18-1 中的命令。

A185439_2_En_18_Fig1_HTML.jpg

图 18-1。

FSS key generation and QR code

正如图 18-1 中的文字所说,我们应该将秘密验证密钥存储在一个安全的地方,它让我们能够方便地使用二维码将其存储在我们的手机上。签名密钥已经放在了/var/log/journal/ e3c7f…db6ab/目录下的fss文件中。每 15 分钟轮换一次。

让我们用我们的密钥来验证日志,以确保它们没有被篡改。

$ sudo journalctl --verify-key 4f5f8f-9eb38b-eff95a-bc0bc8/191914-35a4e900
PASS: /var/log/journal/e3c7fd86ed8b4ef69e569a93e30db6ab/system.journal
PASS: /var/log/journal/e3c7fd86ed8b4ef69e569a93e30db6ab/user-1005.journal

日志-远程

另一种防止日志丢失或被篡改的方法是尽快将它们发送到一个集中的日志主机。Systemd Journal 提供了一个systemd-journal-remote服务,可以从其他主机接收日志消息,并提供一个集中的日志服务。这是一个相当新的概念,目前有一些批评者,因为 TLS 服务并不真正安全(不强制客户端证书验证),但随着它的成熟,它显示出巨大的前景。

该服务可以被动地(等待日志消息)或主动地(从远程主机拉取消息)。它可以配置为通过 HTTP 或 HTTPS 进行监听。我们将设置一台主机gateway.example.com,它将把日志上传到我们的主backup.example.com服务器。

我们将为我们的日志使用https:// transport,我们将假设我们已经创建了 CA 所需的 TLS 密钥。在本例中,我们还使用了两台 CentOS 主机,但是 CentOS 和 Ubuntu 的配置应该是相同的。

首先,使用 CentOS,您需要安装systemd-journal-gateway包。在 Ubuntu 上是systemd-journal-remote包。两种套餐都提供systemd-journal-gatewaysystemd-journal-uploadsystemd-journal-remote服务。

网关服务是一个 HTTP 服务器,可用于查询日志。远程服务用于从其他服务器接收日志。当然,upload 用于将日志上传到远程服务器。这里我们只展示上传和远程服务。

我们将首先设置监听日志消息的远程服务。为此,我们需要编辑以下内容:

$ sudo vi /etc/systemd/journal-remote.conf
[Remote]
ServerKeyFile=/etc/pki/tls/private/backup.example.com.key
ServerCertificateFile=/etc/pki/tls/certs/backup.example.com.cert
TrustedCertificateFile=/etc/pki/tls/certs/cacert.pem

这提供了我们需要的 TLS 密钥和证书的详细信息。systemd-journal-remote用户需要能够读取私有密钥文件。接下来,我们需要做一些额外的文件系统更改。

$ sudo mkdir –p /var/log/journal/remote && sudo chown systemd-journal-remote /var/log/journal/remote

默认情况下,远程日志服务将监听端口19532。我们需要将此添加到防火墙的允许规则中。

$ sudo firewall-cmd --permanent --zone public --add-port=19532/tcp
$ sudo firewall-cmd --reload

默认情况下,服务被配置为使用https://进行监听。这可在/usr/lib/systemd/system/systemd-journal-remote.service文件中配置。现在,我们可以使用以下命令启动远程日志服务:

$ sudo systemctl enable systemd-journal-remote
$ sudo systemctl start systemd-journal-remote
$ sudo systemctl status systemd-journal-remote

我们在继续之前检查状态,以确保我们的服务已成功启动。我们需要配置的下一个服务是在网关主机上。我们需要编辑以下文件:

$ sudo vi /etc/systemd/journal-upload.conf
[Upload]
URL=https://backup.example.com:19532
ServerKeyFile=/etc/pki/tls/private/gateway.example.com.key
ServerCertificateFile=/etc/pki/tls/certs/gateway.example.com.cert
TrustedCertificateFile=/etc/pki/tls/certs/cacert.pem

这类似于远程服务文件,只有一个显著的不同,这一点您无疑可以看到。URL选项指向端口 19532 上的backup.example.com主机。同样,私钥必须对systemd-journal-upload用户可读。我们现在需要做一些类似的文件系统更改,就像我们对远程服务所做的那样。

首先,我们需要访问记录我们发送了哪些日志的状态文件,并访问存储在/run/log/journal中的日志。

$ sudo chown systemd-journal-upload /var/lib/systemd/journal-upload
$ sudo usermod –aG systemd-journal systemd-journal-upload

我们现在准备开始我们的日志上传。

$ sudo systemctl enable systemd-journal-upload
$ sudo systemctl start systemd-journal-upload

我们应该很快就会开始看到日志进入backup.example.com主机上的/var/log/journal/remote目录。

[jsmith@backup ∼]$ sudo ls -l /var/log/journal/remote/
total 16388
-rw-r-----. 1 systemd-journal-remote systemd-journal 8388608 Nov 29 22:58 remote-
gateway@3a016bda55334bcd88d8a6fa52b1dc61-0000000000000001-0005426ea713ed3c.journal

现在,我们让远程主机向备份服务器发送日志。在下一节中,我们将进一步探讨如何将日志记录放入rsyslog作为替代。

rsyslogd(rsyslogd)

应用程序将数据输出到rsyslog守护进程,其中包含守护进程可以解析的特殊格式的日志条目。然后,守护程序获取日志条目,并可以对它们执行各种操作,包括将它们写到文件中。

你已经在本书的前面看到了一些条目。现在来看几条rsyslog线:

Nov 30 00:54:25 backup systemd: Stopping System Logging Service...
Nov 30 00:54:25 backup systemd: Starting System Logging Service...
Nov 30 00:54:28 backup systemd: Started System Logging Service.

一个rsyslog条目由日期、记录该条目的主机名称(通常是发出该条目的服务)和日志数据本身组成。这里我们展示了一个rsyslog服务的重启,它首先被停止和启动,然后确认它被启动。

rsyslog的前身syslog,是无处不在的 Unix 日志格式。它已经出现在所有版本的 Linux 上,实际上几乎出现在所有版本的 Unix 上。您可以使用第三方工具将它添加到 Windows 系统中,大多数网络设备,如防火墙、路由器和交换机都能够生成syslog消息。这导致了syslog格式成为现存的最接近通用日志标准的东西。rsyslog是对syslog的改进,它提供了 TCP (SSL/TLS)、额外的模块和可靠的事件日志协议(RELP)。

Tip

RFC 5424 记录了核心的rsyslog功能,你可以在 https://tools.ietf.org/html/rfc5424 阅读。

各种工具都使用syslog格式,这些工具的功能和复杂性各不相同,通常统称为syslog守护进程。这些守护进程包括基本的syslog工具以及更高级的变体,如syslog-NG(NG 的意思是“下一代”)和rsyslog. rsyslog已经从syslogsyslog-NG的工作中受益匪浅。

我们将介绍基本的rsyslog工具,因为它在 CentOS 和 Ubuntu 上都是默认的。它还为理解日志如何在 Linux 系统上工作打下了基础。

rsyslog实用程序旨在生成、处理和存储有意义的事件通知消息,为管理员提供管理其系统所需的信息。syslog既是一系列程序和库,包括rsyslogdrsyslog守护进程,也是一个通信协议。

rsyslog最常用的组件是rsyslogd守护进程。这个守护进程从启动开始就在您的系统上运行,并监听来自您的操作系统和应用程序的消息。需要注意的是rsyslogd守护进程是一个被动的工具。它只是等待来自设备或程序的输入。它不出去主动收集信息。

工具的下一个主要特性是 RELP 通信协议。有了这个协议,可以通过网络将您的日志数据发送到远程系统,在那里另一个rsyslog守护进程可以收集和集中您的日志。

Tip

流量通常通过 TCP 在端口 514 上传输。

配置 rsyslog

rsyslog守护进程由位于/etc中名为rsyslog.conf的配置文件控制。该文件包含关于rsyslogd正在监听什么设备和程序、该信息将被存储在哪里以及当该信息被接收时将采取什么动作的信息。

你可以在这里看到 Ubuntu 的默认配置文件:

#################
#### MODULES ####
#################

module(load="imuxsock") # provides support for local system logging
module(load="imklog")   # provides kernel logging support
#module(load="immark")  # provides --MARK-- message capability

# provides UDP syslog reception
#module(load="imudp")
#input(type="imudp" port="514")

# provides TCP syslog reception
#module(load="imtcp")
#input(type="imtcp" port="514")

# Enable non-kernel facility klog messages
$KLogPermitNonKernelFacility on

###########################
#### GLOBAL DIRECTIVES ####
###########################

#
# Use traditional timestamp format.
# To enable high precision timestamps

, comment out the following line.
#
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat

# Filter duplicated messages
$RepeatedMsgReduction on

#
# Set the default permissions for all log files.
#
$FileOwner syslog
$FileGroup adm
$FileCreateMode 0640
$DirCreateMode 0755
$Umask 0022
$PrivDropToUser syslog
$PrivDropToGroup syslog

#
# Where to place spool and state files
#
$WorkDirectory /var/spool/rsyslog

#
# Include all config files in /etc/rsyslog.d/
#
$IncludeConfig /etc/rsyslog.d/*.conf

如您所见,第一部分加载基本模块。我们加载imuxsock模块(module(load="imuxsock"),它需要将syslog系统调用传递给rsyslog,以及imklog ( module(load="imklog"),它允许内核日志记录。可以加载的模块有很多,你可以在man rsyslog.conf页面找到与rsyslog捆绑在一起的模块列表。也可以安装其他模块;这些将由相关的包提供,比如为 MySQL 提供日志支持的rsyslog-mysql

模块部分之后是全局指令部分。这定义了用户、文件和目录模式以及其他内务管理。这些很少被编辑。在文件的底部,你会发现IncludeConfig语句告诉rsyslog加载它在rsyslog.d目录中找到的以*.conf结尾的任何文件。

在 Ubuntu 上的rsyslog.d目录中有一个包含一些默认值的文件,名为50-default.conf;该文件包含额外的指令。CentOS rsyslog.conf文件是这两个文件的组合,并且非常相似。

#
# First some standard log files.  Log by facility.
#
auth,authpriv.*            /var/log/auth.log
*.*;auth,authpriv.none        -/var/log/syslog
#cron.*                /var/log/cron.log
#daemon.*            -/var/log/daemon.log
kern.*                -/var/log/kern.log
#lpr.*                -/var/log/lpr.log
mail.*                -/var/log/mail.log
#user.*                -/var/log/user.log

#
# Logging for the mail system.  Split it up so that
# it is easy to write scripts to parse these files.
#
#mail.info            -/var/log/mail.info
#mail.warn            -/var/log/mail.warn
mail.err            /var/log/mail.err

#
# Logging for INN news system.
#
news.crit            /var/log/news/news.crit
news.err            /var/log/news/news.err
news.notice            -/var/log/news/news.notice

#
# Some "catch-all" log files.
#
#*.=debug;\
#    auth,authpriv.none;\
#    news.none;mail.none    -/var/log/debug
#*.=info;*.=notice;*.=warn;\
#    auth,authpriv.none;\
#    cron,daemon.none;\
#    mail,news.none        -/var/log/messages

#
# Emergencies are sent to everybody logged in.
#
*.emerg                                :omusrmsg:*

#
# I like to have messages displayed on the console, but only on a virtual
# console I usually leave idle.
#
#daemon,mail.*;\
#    news.=crit;news.=err;news.=notice;\
#    *.=debug;*.=info;\
#    *.=notice;*.=warn    /dev/tty8

# The named pipe /dev/xconsole is for the `xconsole' utility.  To use it,
# you must invoke `xconsole' with the `-file' option:
#
#    $ xconsole -file /dev/xconsole [...]
#
# NOTE: adjust the list below, or you'll go crazy if you have a reasonably
#      busy site..
#
daemon.*;mail.*;\
    news.err;\
    *.=debug;*.=info;\
    *.=notice;*.=warn    |/dev/xconsole

正如您已经发现的,CentOS 和 Ubuntu 都将其日志文件存储在/var/log目录中,但是使用不同的文件名来存储不同类型的日志条目;例如,您在第十二章中看到(并且您可以在前面的50-default.conf配置文件中看到)Ubuntu 在mail.log文件中存储了与邮件相关的rsyslog条目。然而,在 CentOS 上,与邮件相关的rsyslog条目被写入maillog文件。您可以检查您的主机的rsyslog.conf配置文件,以确定您想要的信息将被写入何处。

rsyslog.conf文件中的每一行都由两个字段组成:一个选择器字段和一个动作字段。这些字段由空格或制表符分隔。你可以在清单 18-3 中看到一行的例子。

mail.*        -/var/log/mail.log
Listing 18-3.rsyslog.conf Syntax

这个例子展示了一个选择器mail.*,以及动作/var/log/mail.log。选择器指定一个工具和一个优先级,用句点分隔。工具告诉您日志消息的来源;例如,mail工具用于记录与邮件服务(如 Postfix)相关的消息。有许多可用的工具,我们将在下一节中逐一介绍。每个应用程序指定它将用于其日志条目的工具。

优先级(.*)告诉rsyslog正在发送的消息的重要性。有一系列的优先级,我们将很快解释每一个。同样,当应用程序将消息发送到rsyslog时,它们会选择每条消息的优先级。

动作告诉rsyslog如何处理消息;通常这意味着将其写入文件。在清单 18-3 中,来自mail设施的具有any优先级的所有消息都将被写入文件/var/log/mail.log

工具

该工具识别rsyslog消息的来源。一些操作系统守护程序和其他常见的应用程序守护程序都附带有标准的工具。mailkern设施是两个很好的例子,分别是邮件相关的事件通知消息和所有内核相关的消息。

没有指定工具的其他进程和守护进程可以使用local工具,范围从local0local7。表 18-1 列出了所有rsyslog设施。

表 18-1。

syslog Facilities on Linux

| 设施 | 目的 | | --- | --- | | `auth` | 与安全相关的消息 | | `auth-priv` | 访问控制消息 | | `cron` | `cron`-相关消息 | | `daemon` | 守护进程和进程消息 | | `kern` | 内核消息 | | `local0–local7` | 为本地定义的消息保留 | | `lpr` | 假脱机(打印)子系统消息 | | `mail` | 与邮件相关的消息 | | `mark` | 由`rsyslogd` ( `internal use only`)生成的带时间戳的消息 | | `news` | 网络新闻相关消息(例如,新闻组) | | `syslog` | `syslog`-相关消息 | | `user` | 未指定设施时的默认设施 | | `uucp` | 与 UUCP 相关的消息 |

Tip

在 CentOS 系统上,默认情况下,local7用于引导消息,这些消息被定向到/var/log/boot.log

Note

mark设施是一个特例。当您使用-m(分钟)标志时,rsyslogd生成的带时间戳的消息使用它。

还有两个特殊的工具:*(一个通配符,表示所有工具)和none(否定一个工具选择)。

您可以在清单 18-4 中看到通配符选择器。

*.emerg                                :omusrmsg:*
Listing 18-4.rsyslog.conf * Wildcard Selector

这将把所有优先级为emerg的消息发送给所有登录的人,而不考虑设备。

Tip

omusrmsg中的 om 代表输出模块。这些用于处理消息并将它们传送到不同的目标;你可以在这里看到更多: www.rsyslog.com/doc/v8-stable/configuration/modules/idx_output.html

您可以使用none通配符选择器来不选择来自特定设施的消息。清单 18-5 中所示的例子将告诉rsyslog不要将任何内核消息记录到文件/var/log/messages中。

kern.none                      /var/log/messages
Listing 18-5.rsyslog.conf none Wildcard Selector

优先

优先级是按照重要性的递增顺序来组织的。分别是debuginfonoticewarningerrcritalertemerg。每个优先级选择器适用于规定的优先级和所有更高的优先级,因此mail.err表示errcritalertemerg优先级的所有mail设备信息。

与工具一样,您可以使用通配符选择器*none。此外,您可以使用另外两个修饰符:=!=修饰符表示只选择了一个优先级;例如,cron.=crit表示只选择优先级为critcron设备消息。!修改器有负面效果;例如,cron.!crit选择除了优先级为crit或更高的cron设施信息之外的所有cron设施信息。您还可以组合这两个修饰符来创建与=修饰符相反的效果,以便cron.!=crit选择所有cron工具消息,除了那些关键优先级的消息。每个选择器只能列出一个优先级和一个优先级通配符。

行动

动作告诉rsyslogd如何处理它收到的事件通知消息。根据加载的输出模块,rsyslog可以执行几个潜在的动作。

  • 记录到文件
  • 登录到设备
  • 记录到命名管道
  • 登录到特定用户或控制台
  • 将日志发送到另一台主机
  • 记录到数据库表
  • 执行命令
  • 丢弃

清单 18-6 展示了rsyslogd可以采取的前四个动作的例子,包括记录到文件、设备文件、命名管道和控制台或用户屏幕。

cron.err          /var/log/cron
auth.!=emerg    /dev/lpr1
news.=notice     |/tmp/pipe
auth-priv         root,jsmith
Listing 18-6.File, Device, and Named Pipe Actions

在第一行中,err优先级及更高优先级的所有cron消息被记录到文件/var/log/cron中。

Note

当登录到文件时,rsyslogd允许您在文件名前添加一个连字符(-),如下所示:-/var/log/auth。这告诉rsyslog在写入文件后不要同步文件。这是为了加快写入日志的过程。但这也意味着,如果您的系统在两次写入尝试之间崩溃,您将会丢失数据。

第二行包含除了发送到本地打印机lpr1emerg优先级消息之外的所有auth消息。

第三个将所有优先级为notice或更高的news消息发送到一个名为/tmp/pipe的命名管道。

Note

发送到命名管道允许您将rsyslog数据发送到其他应用程序;例如,使用命名管道收集日志消息,并将它们传递给日志关联引擎或数据库。

第四行也是最后一行将所有的auth-priv消息发送给用户rootjsmith,如果他们已经登录的话。

您还可以执行最后一个操作,将日志发送到另一个主机,如清单 18-7 所示。

mail     @backup.example.com
Listing 18-7.Logging to a Remote System

在这个例子中,所有的mail消息都被发送到主机backup.example.com

要发送所有日志,我们将使用以下语法:

*.*     @backup.example.com

rsyslog使用 UDP 端口 514 传输日志消息。这里假设远程主机上的rsyslog守护进程已经被配置为接收日志,并且您有合适的防火墙规则来接收日志条目。这里有一个例子:

$ sudo firewall-cmd --permanent --zone public --add-port=514/udp

这里我们创建了一个防火墙规则,允许主机在 UDP 端口 514 上接收来自主机 192.168.0.254 的rsyslog数据。但是,由于这是 UDP,记住 UDP 是一个“一劳永逸”的协议,不能保证另一端的服务器会收到它。

为了避免这种情况并防止可能的消息丢失,我们将使用 RELP,这是一种可靠的事件记录协议(参见 www.rsyslog.com/doc/v8-stable/configuration/modules/omrelp.html )。

*.*    :omrelp:backup.example.com:2514

当然,我们需要加载我们的rsyslog.conf文件的模块部分中的模块,我们将很快展示如何配置 RELP。

组合多个选择器

您也可以在您的rsyslog.conf文件中组合多个选择器,允许更复杂的选择和过滤。例如,您可以在一个选择器中列出多个用逗号分隔的设施(参见清单 18-8 )。

auth,auth-priv.crit              /var/log/auth
Listing 18-8.Multiple Facilities

这会将优先级为crit或更高的所有auth消息和所有auth-priv消息发送到文件/var/log/auth

但是,你不能通过优先级来做到这一点。如果要列出多个优先级,需要列出多个选择器,用分号隔开,如清单 18-9 所示。

auth;auth-priv.debug;auth-priv.!=emerg        /var/log/auth
Listing 18-9.Multiple Priorities

该示例向您展示了如何将优先级为debug或更高的所有auth消息和所有auth-priv消息(不包括优先级为emergauth-priv消息)发送到文件/var/log/auth

Tip

请记住,对于多个选择器,过滤是从左到右进行的;rsyslogd将从左边的选择器开始处理该行,并移动到每个后续选择器的右边。记住这一点,将较宽的过滤器放在左边,向右移动时缩小过滤标准。

您还可以使用多条线路将消息发送到多个位置,如清单 18-10 所示。

auth                               /var/log/auth
auth.crit                         jsmith
auth.emerg                    /dev/console
Listing 18-10.Logging to Multiple Places

在这里,所有的auth消息如前所述被记录到/var/log/auth中,但是如果用户jsmith登录,优先级为crit或更高的auth消息也被发送给用户【】。那些emerg优先级的也被发送到控制台。

使用omfwd输出模块也很常见,它允许您通过 TCP 或 UDP 将日志发送到远程服务器。如果我们想将日志消息发送到本地文件和远程服务器,我们可以使用如下方式:

mail.*    action(type="omfile" sync="no" file="/var/log/maillog”)
                   action(type="omfwd" Target="monitor.example.com" Port="10514" Protocol="tcp")

你可以在 www.rsyslog.com/doc/v8-stable/configuration/modules/omfwd.html 了解更多关于这个模块的内容。

配置 RELP

RELP 允许我们设置中央日志服务器,可以从任意数量的客户端服务器收集和存储日志。RELP 比 Journald 的远程日志记录更成熟,并且经过了数千次安装的考验。它的配置也相当简单,模块可以通过包管理安装。

$ sudo yum install –y rsyslog-relp rsyslog-gnutls
$ sudo aptitude install –y rsyslog-relp rsyslog-gnutls

我们需要配置客户端和服务器。RELP 允许您将日志从客户端转发到服务器,服务器可以将日志推送到其他服务器上。我们将使用 TLS 来加密我们的日志流量,并避免 rouge 客户端连接并潜在地发送虚假的日志流量或发起 DoS 攻击,这在普通的 UDP/TCP 模块中是可能的。

在 Ubuntu 上,syslog用户运行rsyslogd服务。因此,您需要确保syslog用户能够读取 TLS 私有密钥。为此,您可以使用setfacl命令:

$ sudo setfacl -m u:syslog:rx /etc/ssl/private
$ sudo setfacl -m u:syslog:r /etc/ssl/private/nfs.example.com.key

root用户在 CentOS 上运行rsyslogd服务,不需要更改。

配置 RELP 客户端

我们将从 RELP 客户端配置开始。我们将假设主机已经有一个 TLS 密钥和由我们的私有 CA 签名的公共证书。我们将再次使用网关主机,它将连接并将其日志发送到backup.example.com主机。

$ sudo vi /etc/rsyslog.d/relp.conf
# make gtls driver the default
$DefaultNetstreamDriver gtls

# certificate files
$DefaultNetstreamDriverCAFile /etc/pki/tls/certs/cacert.pem
$DefaultNetstreamDriverCertFile /etc/pki/tls/certs/gateway.example.com.cert
$DefaultNetstreamDriverKeyFile /etc/pki/tls/private/gateway.example.com.key

$ActionSendStreamDriverAuthMode x509/name
$ActionSendStreamDriverPermittedPeer backup.example.com
$ActionSendStreamDriverMode 1
*.* @@backup.example.com:6514

我们创建了一个名为relp.conf的文件,并将其配置为在客户端和backup.example.com主机之间使用加密传输。我们通过一个名为gtls的 NetStreamDriver 来实现这一点,它实现了我们的 TLS 传输(GnuTLS)。NetStreamDriver 可以提供顺序传递、身份验证和安全传输。

我们需要提供我们的 TLS 证书文件,它们的描述如前所示。记住在 Ubuntu 上 TLS 文件会有不同的路径。

我们通过我们的证书名称(x509/name)来处理认证。这意味着,我们通过测试对等方 TLS 证书中的通用名称来验证我们与对等方的连接。如果它匹配对等体backup.example.com,那么我们将允许连接;否则,我们将失败。模式1表示我们将使用 TLS。

最后一行说我们将把所有设施和优先权(*.*)运到6514港的backup.example.com。您现在将重启rsyslog守护进程。

$ sudo systemctl restart rsyslog

配置 RELP 服务器

现在我们可以配置我们的 RELP 服务器了。这将从我们的网络中收集日志并存储它们。配置类似于客户端,我们的备份主机如下所示:

$ sudo vi /etc/rsyslog.d/relp.conf
$ModLoad imtcp

$DefaultNetstreamDriver gtls

$DefaultNetstreamDriverCAFile /etc/pki/tls/certs/cacert.pem
$DefaultNetstreamDriverCertFile /etc/pki/tls/certs/backup.example.com.cert
$DefaultNetstreamDriverKeyFile /etc/pki/tls/private/backup.example.com.key

$InputTCPServerStreamDriverAuthMode x509/name
$InputTCPServerStreamDriverPermittedPeer *.example.com
$InputTCPServerStreamDriverMode 1
$InputTCPServerRun 6514

首先,我们需要加载我们的imtcp模块,它将提供 TCP 连接。然后,除了更改名称之外,我们拥有与客户端相同的 TLS 密钥配置选项。我们接受所有由我们的 CA 签名的、名字中带有example.com的对等体。最后一行告诉rsyslogd在端口 6514 上运行一个 TCP 服务器。

现在,我们需要确保我们的备份服务器(CentOS 服务器)上的端口 6514 是打开的,因此我们使用firewall-cmd命令。

$ sudo firewall-cmd --permanent --zone public --add-port=6514/tcp
$ sudo firewall-cmd --reload

然后,我们也在备份服务器上重启rsyslog守护进程。

$ sudo systemctl restart rsyslog

在备份服务器上,我们现在可以检查我们的/var/log/messages文件,我们应该会看到类似如下的日志:

Dec  1 12:01:17 gateway chronyd[608]: Selected source 27.124.125.250
Dec  1 12:01:19 gateway chronyd[608]: Selected source 27.124.125.250
Dec  1 12:01:21 backup systemd: Starting user-0.slice.
Dec  1 12:01:01 backup systemd: Started Session 143 of user root.
Dec  1 12:05:51 gateway chronyd[608]: Selected source 202.127.210.37
Dec  1 12:08:22 dc1 systemd[1]: Started CUPS Scheduler.

日志中包含时间戳、主机名、服务和正在写入的消息。在这里,我们可以看到主机网关、主机dc1backup服务器现在都登录到我们的备份服务器。

启动和停止 rsyslog

rsyslogd守护进程通常在系统启动时启动。您可以使用systemctl命令手动启动和停止rsyslog守护进程。

$ sudo systemctl start rsyslog
$ sudo systemctl stop rsyslog
$ sudo systemctl status rsyslog

在 CentOS 和 Ubuntu 上,你可以分别使用/etc/sysconfig/rsyslog/etc/default/rsyslogd文件定制rsyslogd的选项。

设置主机时,注意以下几点很重要。如果您的日志位于根分区,您的系统可能会崩溃。为了降低这种潜在崩溃的风险,我们建议您将日志存储在非根(非/)分区上。这意味着即使你磁盘上的所有空间都用完了,系统也不会崩溃。

使用记录器测试日志记录

在 CentOS 和 Ubuntu 上都有,logger是测试日志配置的有用命令行工具。

$ logger -p mail.info "This is a test message for facility mail and priority info"

这将向您的rsyslog守护进程写入消息“这是工具邮件和优先级信息的测试消息”,并写入您为工具为mail、优先级为info的消息配置的任何目的地。

如您所见,-p参数允许您指定一个工具和优先级组合,然后测试消息包含在引号中。

出于测试目的,我们经常在 bash 脚本中使用logger来生成多条消息。清单 18-11 中的脚本为每个设施和优先级组合生成一个rsyslog消息。

#!/bin/bash

for f in
{auth,authpriv,cron,daemon,kern,lpr,mail,mark,news,rsyslog,user,uucp,local0,local1,
local2,local3,local4,local5,local6,local7}

   do
    for p in {debug,info,notice,warning,err,crit,alert,emerg}
   do
     logger -p $f.$p "Test rsyslog messages from facility $f with priority $p"
   done
   done
exit 0

Listing 18-11.Log Testing bash Script

您还可以使用logger将一个不断增长的文件传输到rsyslog

$ tail -f /tmp/logfile | logger -p daemon.info

这里我们将文件/tmp/logfile添加到了logger命令中。文件中的每一行都将被写入优先级为infodaemon设施。

日志管理和轮换

管理日志环境的一个重要部分是控制日志文件的数量,并将日志文件保持在可管理的大小。为此,您可以轮换日志。

日志轮换是定期复制日志文件的过程,通常会添加一个后缀,如日期或递增的数字。然后rsyslog守护进程记录到一个新文件中。您通常会将循环日志文件保留一段固定的时间,例如一周或一个月。

让我们看一个例子。我们已经拿到了/var/log/mail.log文件。我们可以每天轮换这个文件,并将轮换后的文件保留七天。日志轮转过程将在我们指定的时间开始,例如,将现有的mail.log文件复制到mail.log.1,然后创建一个空的mail.log文件。原木旋转过程也将递增;如果一个mail.log.1文件存在,这个文件将被重命名为mail.log.2,以此类推。如果有一个mail.log.7文件,这个文件将被删除,并且mail.log.6文件增加到mail.log.7

手动管理日志循环可能相当复杂,因此我们建议您使用logrotate工具。CentOS 和 Ubuntu 都附带了logrotate工具,并且通常已经为您安装和配置好了。默认配置处理来自主机上安装的应用程序的大多数典型日志文件。

logrotate命令配置简单,并且依赖于crontab按计划运行。基本logrotate配置位于/etc/logrotate.conf,你可以在清单 18-12 中看到一个典型文件。

weekly
rotate 4
create
dateext
include /etc/logrotate.d
/var/log/wtmp {
    monthly
    create 0664 root utmp
    minsize 1M
    rotate 1
}
/var/log/btmp {
    missingok
    monthly
    create 0600 root utmp
    rotate 1
}
Listing 18-12.logrotate.conf

这个简单的文件包含了logrotate用来处理日志文件的全局选项。在本例中,我们删除了空行和注释。所有日志文件每周轮换一次,日志在被删除之前会轮换四次,新的日志文件会被创建,轮换后的文件会添加一个日期扩展名,并且logrotate工具会检查logrotate.d目录中是否有任何新的logrotate文件。您可以使用其他选项,其中一些选项如表 18-2 所示。你可以钻研一下logrotate的个人档案来寻找其他的选择。

表 18-2。

logrotate.conf Options

| [计]选项 | 描述 | | --- | --- | | `daily` | 日志每天轮换一次。 | | `weekly` | 日志每周轮换一次。 | | `monthly` | 日志每月轮换一次。 | | `compress` | 旧的日志文件用 gzip 压缩。 | | `create mode owner group` | 创建新的日志文件,模式为八进制 0700,所有者和组(相反的是`nocreate`)。 | | `ifempty` | 即使日志文件为空,它也会被循环。 | | `include directory or filename` | 由`logrotate`处理的列出的文件和目录的内容。 | | `are included.` |   | | `mail address` | 当日志被淘汰时,它会被邮寄到地址。 | | `nomail` | 最后的日志没有邮寄到任何地址。 | | `missingok` | 如果日志文件丢失,它将被跳过,而`logrotate`将进入下一个日志文件,而不会发出错误消息。 | | `nomissingok` | 如果日志文件丢失,将发出一条错误消息(默认行为)。 | | `rotate count` | 日志文件在被删除之前会旋转`count`次。如果`count`为 0,则删除旧的日志文件,而不是旋转。 | | `size size[M,k]` | 当日志文件超过最大值时,将被循环;`M`表示以兆字节为单位的大小,`k`表示以千字节为单位的大小。 | | `sharedscripts` | 可以为每个被循环的日志文件运行前脚本和后脚本。如果日志文件定义由一组日志文件组成(例如,`/var/ log/samba/*`)并且设置了`sharedscripts`,那么前脚本/后脚本只运行一次。对面是`nosharedscripts`。 |

清单 18-12 显示了最后一个命令include,它主要驱动logrotate。清单 18-12 中的logrotate.d目录包含一个文件集合,告诉logrotate如何处理你的各种日志文件。

您还可以定义额外的目录和文件,并将它们包含在logrotate.conf文件中,以适应您的环境。然而,大多数发行版使用logrotate.d目录,并在该目录中附带了许多预定义的文件来处理常见的日志循环,如mailcronrsyslog消息。我们建议您在此添加任何新的日志轮换文件。

Note

许多软件包在安装时也会将日志循环文件添加到该目录中。

清单 18-13 向您展示了其中一个文件。

/var/log/messages /var/log/secure /var/log/maillog /var/log/spooler
/var/log/boot.log /var/log/cron
{
daily
rotate 7
sharedscripts
postrotate
     /bin/kill -HUP 'cat /var/run/rsyslogd.pid 2> /dev/null' 2> /dev/null || true
endscript
}
Listing 18-13.CentOS rsyslog logrotate File

在这些文件中,您可以覆盖logrotate.conf中的大多数全局选项,为单个文件或目录定制日志轮换。清单 18-13 首先列出所有要旋转的文件。这也可以包括使用语法/path/to/log/files/*的目录。

然后,包含在{ }中的是这组特定文件的任何选项。在本例中,我们覆盖了全局日志记录选项,每天轮换这些文件,并保留日志文件的七次轮换。

接下来,我们运行一个脚本。您可以使用prerotate命令运行脚本,该命令在旋转任何日志之前运行脚本,或者使用postrotate,该命令在旋转日志文件(或多个日志文件)之后运行脚本。

清单 18-13 显示了一个脚本,它在日志文件(或多个日志文件)被轮转之后重启rsyslog守护进程。由于选项sharedscripts被启用,无论循环多少个日志文件,脚本都将只运行一次。脚本语句以endscript选项结束。

那么,logrotate是如何运行的呢?默认情况下,在 CentOS 和 Ubuntu 上,cron在预定的时间运行logrotate(通过脚本/etc/cron.daily;它由 Anacron 执行)。您也可以在命令行上手动运行它。

如果在命令行上运行,logrotate默认为/etc/logrotate.conf的配置文件。您可以覆盖该配置文件,如下行所示:

$ sudo logrotate /etc/logrotate2.conf

logrotate命令也有几个命令行选项可以使用,如表 18-3 所示。

表 18-3。

logrotate Command-Line Options

| [计]选项 | 描述 | | --- | --- | | `-d` | 调试模式,在该模式下不会对日志文件进行任何更改;它将输出它可能已经旋转的结果。也意味着详细模式。 | | `-v` | 详细模式。 | | `-f` | 即使不需要,也强制进行日志轮换。 |

在大多数系统上,默认情况下,cron每天运行logrotate,这是我们推荐您使用的模型。

测井分析和对比

现在您有了所有这些日志文件,您可以用它们做什么呢?嗯,日志有两个用途。

  • 发现什么时候出了问题
  • 当出现问题时,帮助诊断问题

为了实现第一个目标,您需要一个工具来识别特定的日志消息并提醒您它们的存在。这个过程被称为日志分析和关联,它通常被认为是一门黑色艺术。好消息是,我们将向您介绍一个名为 Logstash 的工具。

Logstash 将把我们所有的日志输入到一个叫做 Elasticsearch 的分布式搜索和分析工具中。在那里,我们可以通过一个名为 Kibana 的界面查看我们收集的日志数据。这将有助于使日志分析和关联成为日常监控工作的一部分。

首先要记住的是,分析和关联是两回事。分析是对组成整体的组成部分及其相互关系的研究。作为一个系统管理员,最好的分析工具就是你。系统管理员了解其主机的操作模式,通常可以比自动监控或警报系统更快地检测到问题。

然而,这个模型有两个问题。首先,你不可能同时出现在所有地方。第二,系统收集的数据量越来越大,可能会变得不堪重负。

这就是相关性发挥作用的地方。相关性最好定义为检测数据之间关系的行为。您设置工具来收集您的数据,从谷壳中过滤“小麦”,然后关联剩余的数据,将正确的信息放在您的面前,以便您可以提供准确的分析。

正确设置和管理的工具可以整理来自主机日常操作的持续数据流。他们可以检测这些数据之间的关系,或者将这些数据整合成一个连贯的整体,或者为您提供合适的数据,让您自己进行分析。

但是,您必须确保这些工具是正确的工具,并且被配置为寻找正确的东西,这样您就可以依靠它们来告诉您出现了问题,您需要进行干预。

构建这样一个自动化日志监控系统的第一步是确保您收集了正确的东西并将它们放在正确的位置。列出您所有的应用程序、设备和主机以及它们的记录位置。第二阶段是把所有的信息汇集在一起,找出你真正想知道的。列出对你和你的主人很重要的关键信息。

将这些列表分组为优先列表;有些消息您可能希望收到,有些可以通过电子邮件发送,有些可能会触发自动化流程或尝试自我恢复,如重新启动流程。

第三阶段是实现日志关联和分析,包括配置关联工具和设计所需的响应。请确保仔细记录每条消息、对消息的响应以及与此消息相关的任何特殊信息。

介绍 Beats 和 Logstash

我们看了系统如何写日志和管理日志;现在我们想看一下日志记录的下一个层次,也就是将这些日志传送并转换成有用的信息。Logstash 和 Beats 帮助我们将日志信息从主机上移走,并对它们进行处理以备存储,这样我们就可以使用发现工具来查看对系统上发生的事情的分析。存储和发现由 Elasticsearch 和 Kibana 处理,我们将在下一节中讨论这些内容。

Beats 是日志、度量、网络数据包数据和 Windows 事件的轻量级转发器。轻量级意味着他们除了将数据发送到远程位置,跟踪他们发送了什么之外什么也不做。我们将查看 Filebeat,它将我们的日志传送到 Logstash。

Logstash 是一个工具,可以将日志(或其他数据,如指标等)转换为可以索引和标记的数据,然后存储(或再次发布)以使数据的发现更容易。Logstash 可以接收各种各样的输入,并有各种各样的输出,其中之一就是能够写入 Elasticsearch。

Logstash 提供了一系列插件,可以帮助解析、过滤和转换数据,或者您也可以编写自己的插件。插件是用 Ruby 编写的,如果您找不到任何现有的插件,您可以创建输入、编解码器、过滤器和输出插件来满足您的需求。

顾名思义,输入插件负责将数据接收到 Logstash 中。编解码器插件用于更改事件或流过滤器的数据表示。过滤器允许在将事件发送到输出之前对其进行处理(在特定条件下丢弃事件、匿名化数据等)。Outputs 将 Logstash 处理的输出写到一个 stash 或类似 Elasticsearch 的某种存储中。其他输出可以是 S3 或石墨。输入、编解码器、过滤器和输出的组合创建了一个管道。

关于 Logstash 如何工作的更深入的讨论,请参见这里:

安装和配置 Beats

现在让我们来看看我们的日志路径的第一步。使用 Beats,尤其是 Filebeat,我们将从我们的主机收集数据,并将其发送到 Logstash 服务器。在这个场景中,Filebeat 将在我们的网关主机上运行,并将其数据发送到我们的监控服务器。我们在第十七章中谈到的监控服务器设置了我们的 Graphite 服务,并用于存储我们通过 Collectd 从主机收集的指标。

为了简单起见,我们将在我们的监控主机上运行 Logstash 服务和 Elasticsearch。在现实场景中,根据转换和数据收集的数量,一个集群中至少有三个 Elasticsearch 节点,并且可能有几层 Logtash 服务运行在一两个单独的节点上。

在网关节点上,我们将安装 Filebeat。我们可以从 https://www.elastic.co/downloads/beats/filebeat 下载 Filebeat,我们可以选择最合适的包格式(RPM,DEB,tar.gz)。网关主机恰好是 CentOS 主机,所以我们将安装一个 RPM 版本。

$ sudo yum install -y https://artifacts.elastic.co/downloads/beats/filebeat/filebeat-5.0.2-x86_64.rpm

Tip

你也可以在你的机器上设置 APT 或者 YUM 库;详情见此: https://www.elastic.co/guide/en/beats/libbeat/current/setup-repositories.html

对于 Ubuntu 和 CentOS,Filebeat 的配置文件存储在/etc/filebeat/filebeat.yml中。我们将把所有来自/var/log的日志发送到位于监控服务器上的 Logstash 服务器。我们用清单 18-14 中所示的配置来实现。

filebeat.prospectors:
- input_type: log
  paths:
    - /var/log/messages
    - /var/log/*.log
    - /var/log/audit/audit.log
tags: ["security", "network"]
fields:
  env: production
output.logstash:
  hosts: ["monitor.example.com:5044"]
  ssl.certificate_authorities: ["/etc/pki/tls/certs/cacert.pem"]
  ssl.certificate: "/etc/pki/tls/certs/gateway.example.com.cert"
  ssl.key: "/etc/pki/tls/private/gateway.example.com.key"
Listing 18-14.Filebeat.yml

通常将数据发送到本地 Logstash 服务,而不是像我们在这里所做的那样发送到远程服务。这样,在通过网络发送之前,您可以在本地转换数据。如果您只需要匿名数据在网络中传输,或者您可能希望使用主机的分布式处理能力,而不是依赖集中式 Logstash 服务来转换数据,那么您可能希望这样做。

Filebeat 配置文件的格式是 YAML(更多信息参见“YAML 格式”侧栏)。在清单 18-14 中,第一行声明了我们的filebeat.prospectors。您可以有不同类型的输入,prospectors 文件是您声明它们的地方。在清单 18-14 中,你可以看到我们声明了一个loginput_type值。另一种选择是stdin的输入类型。然后,我们可以列出希望从中接收日志的路径。当然,我们希望从/var/log/messages收集日志。您可以使用 globs 来捕获所有日志(/var/log/*.log),或者您可以针对特定的日志(/var/log/audit/audit.log)。这里我们捕获的是/var/log目录中的任何内容(不包括任何.gz或-20161131 旋转日志),除了我们指定的内容(audit.log),我们不捕获/var/log/*/*.log中的任何内容。

YAML Format

YAML 不是标记语言,或者只是 YAML,是一种人类可读的数据序列化语言。YAML 是 JSON 数据序列化格式的超集,通常用于配置文件。

它是结构化数据,可以包含字符串、列表、关联数组和块标量。

---
key: value
- lista
- listb
keya:
  - valuea
  - valueb

你可以在 www.yaml.org/start.html 了解更多关于 YAML 的信息。

如果我们想要一个不同的来捕获我们的 Apache 日志,我们可以像这样单独完成:

- input_type: log
  paths:
    - /var/log/*.log
- input_type: log
  paths:
    - /var/log/apache/httpd-*.log
  document_type: apache

我们没有在网关上运行 Apache 服务器,但是在这里您可以看到我们将 Apache 日志作为不同的输入类型,并且我们将应用apachedocument_type。这将事件类型字段(我们应用于日志的元数据标签之一)更改为apache,而不是log。这有助于我们在处理链的下游进行转换。

在清单 18-14 中,您还会看到我们对所有数据都应用了全局指令。我们用securitynetwork标记我们的数据,并给它一个环境标记production。这又增加了数据的丰富性,我们以后可以用它来提取、转换和过滤我们的数据。

我们的输出将被发送到主机上的 Logstash 服务器。我们将使用 TLS 加密传输来发送数据。我们在这里没有匿名化我们的日志,所以发送到监控主机的任何内容都可以以明文形式读取,这使我们容易受到窥探。加密数据可以防止这种情况。

您可以在 Filebeat prospectors 上使用更多配置选项。您可以包括或排除特定行、排除文件、添加特定标记、添加特定字段以及添加多行模式匹配。你可以在这里了解更多信息:

我们不会立即启动我们的 Filebeat 服务,但是您可以像平常一样使用systemctl来启动和停止它。

$ sudo systemctl enable filebeat
$ sudo systemctl start filebeat

在启动 Filebeat 服务之前,我们将配置 Logstash 服务来接受日志。

安装和配置 Logstash

Logstash 是一个 Java 进程,用于在将输出“存储”在某种存储中以供进一步分析或查看之前转换数据。它非常强大,你可以用它做很多事情。这一章不会公正地对待它,因为我们只能展示它的力量的最基本要素。在本节中,我们将向您展示如何安装,然后进行基本配置,以便我们可以将日志数据输出到 Elasticsearch。

Logstash 的当前版本需要 Java 8。我们将通过 https://www.elastic.co 提供的 APT 库在我们的 Ubuntu 监控主机上安装这项服务,该公司帮助设计和支持 Kibana、Elasticsearch、Logstash 和 Beats 的开源项目。

让我们首先添加 APT 存储库,首先添加公共 GPG 密钥,然后添加包apt-transport-https(如果还没有安装的话),最后将实际的存储库添加到 APT。

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
sudo aptitude install –y apt-transport-https
echo "deb https://artifacts.elastic.co/packages/5.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-5.x.list
sudo aptitude update

这里我们使用了我们已经安装的aptitude包管理器,而不是您也可以使用的apt-get。现在我们可以安装 Logstash 了。

$ sudo aptitude install –y logstash

对于 CentOS 和最新的安装说明,您可以在此处查看说明:

Logstash 的配置文件保存在/etc/logstash中。大多数情况下,您不需要更改服务启动的任何配置,但是如果您愿意,您可以使用/etc/logstash/startup.options文件来这样做。该文件包含运行服务的用户、您可能希望包含的JAVA_OPTS,等等。

我们现在将创建一个管道来处理我们的日志。我们可以用两种方法做到这一点。我们可以编辑并添加到/etc/logstash/logstash.yml文件,这是主要的管道配置文件,或者我们可以在/etc/logstash/conf.d/中创建一个文件,该文件将由 Logstash 服务读取。让我们看看如何从网关主机和任何其他 Beats 服务收集日志文件。

$ sudo vi /etc/logstash/conf.d/general.conf
input {
  beats {
    port => 5044
    ssl  => true
    ssl_certificate => “/etc/ssl/certs/monitor.example.com.cert”
    ssl_key => “/etc/ssl/private/monitor.example.com.key”
    ssl_certificate_authorities [ “/etc/ssl/certs/cacert.pem” ]
    ssl_verify_mode => force_peer
  }
}
output {
  stdout { codec => rubydebug }
}

看一下这个文件,您可以看到它由两部分组成:输入和输出。通常,您会看到三个部分,包括一个过滤器部分。

Input {
  ...
}
filter {
  ...
}
output {
  ...
}

熟悉 Ruby 的人会认为这是 Ruby 散列语法。JRuby,for Java Ruby,与 Logstash 一起使用,配置文件采用原生 Ruby 语法。

在我们的input部分,我们已经包含了我们的beats插件,并且beats插件像前面一样接受端口和 SSL 配置选项。这对您来说应该已经很熟悉了,但是需要注意的是,这里我们指定了一个force_peerssl_verify_mode值。这意味着如果客户端不向服务器提供证书,我们将立即断开连接。这将使我们的系统更加安全,并尽早丢弃未授权的连接。

输出部分描述了我们处理完数据后将把数据发送到哪里。我们可以看到,我们将数据输出到stdout,为了帮助我们调试该输出,我们将使用一个名为rubydebug的编解码器。这对于我们开始旅程时查看 Logstash 正在做什么很有好处。我们还将指定elasticsearch作为输出,但是我们在这个阶段不想这么做。

我们已经准备好启动我们的 Logstash 服务,并开始了解它是如何工作的。我们通过以下命令来实现:

$ sudo -u logstash /usr/share/logstash/bin/logstash -f /etc/logstash/conf.d/general.conf
WARNING: Could not find logstash.yml which is typically located in $LS_HOME/config or /etc/logstash. You can specify the path using --path.settings. Continuing using the defaults
Could not find log4j2 configuration at path /usr/share/logstash/config/log4j2.properties. Using default config which logs to console
04:20:40.036 [[main]-pipeline-manager] INFO  logstash.inputs.beats - Beats inputs: Starting input listener {:address=>"0.0.0.0:5044"}
04:20:40.072 [[main]-pipeline-manager] INFO  logstash.pipeline - Starting pipeline {"id"=>"main", "pipeline.workers"=>2, "pipeline.batch.size"=>125, "pipeline.batch.delay"=>5, "pipeline.max_inflight"=>250}
04:20:40.075 [[main]-pipeline-manager] INFO  logstash.pipeline - Pipeline main started
04:20:40.087 [[main]<beats] INFO  org.logstash.beats.Server - Starting server on port: 5044
04:20:40.134 [Api Webserver] INFO  logstash.agent - Successfully started Logstash API endpoint {:port=>9600}

在第一部分中,我们将看到启动输出,它给出了使用什么参数启动什么的详细信息。您将会看到,我们正在主管道端口 5044 上启动 Beats 输入,并在端口 9600 上启动一个 web API。

现在,在网关主机上,我们可以启动我们的filebeat服务。我们用像这样的systemctl命令来做这件事,然后我们可以跟踪服务产生的日志。

$ sudo systemctl start filebeat && tail –f /var/log/filebeat/filebeat

现在,在monitor主机上,我们可以启动logstash服务,我们应该会看到大量数据从网关服务器上的 Filebeat 传入。

在清单 18-15 中,我们看到了从网关主机捕获的日志消息之一。每个节拍都被赋予了自己的时间戳,我们还被赋予了一系列其他相关的元数据,比如input_type、它可以来自的主机、我们与节拍相关联的标签等等。每个节拍都有一个宿主、一个来源和一个信息。该消息将具有我们已经看到的rsyslog格式。

{
    "@timestamp" => 2016-12-04T06:33:33.868Z,
    "offset" => 11979,
    "@version" => "1",
    "input_type" => "log",
    "beat" => {
        "hostname" => "gateway.example.com",
            "name" => "gateway.example.com",
         "version" => "5.0.2"
     },
     "host" => "gateway.example.com",
     "source" => "/var/log/messages",
      "message" => "Dec  4 06:33:24 gateway jsmith: tesing this is a test",
      "fields" => {
           "env" => "production"
       },
       "type" => "log",
       "tags" => [
          [0] "security",
          [1] "network",
          [2] "beats_input_codec_plain_applied"
       ]
}
Listing 18-15.Beat from Gateway Server Logs

让我们简单地看一下我们正在收集的审计日志:

         "host" => "gateway.example.com",
         "source" => "/var/log/audit/audit.log",
         "message" => "type=SERVICE_STOP msg=audit(1480834167.796:997): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=NetworkManager-dispatcher comm=\"systemd\" exe=\"/usr/lib/systemd/systemd\" hostname=? addr=? terminal=? res=success'",
         "type" => "log",

你会立即注意到audit.log与你正常的rsyslog格式不同。auditd文件是一组键/值对(key=value)。它们看起来可能与我们在这里看到的非常不同,但基本上它们由一个类型和一条消息组成(包括一个时间戳和唯一的 ID)。然后,根据类型,它们可以有任意数量的其他键和值。在本例中,我们已经停止了openvpn服务,这是生成的日志通知。

这不是我们想要的格式,也不像是一条rsyslog消息。让我们更改一下,以便将此记录为不同类型的事件。为此,我们将编辑网关主机上的 Filebeat 配置,并更改以下内容:

filebeat.prospectors:
- input_type: log
  paths:
    - /var/log/messages
    - /var/log/*.log
- input_type: log
  paths:
    - /var/log/audit/audit.log
  document_type: auditd

我们已经将audit.log移到了它自己的input_type部分。在该部分中,我们添加了document_type选项,并将其设置为auditd。现在让我们向你展示它的作用;继续并重新加载 Filebeat 服务。

    "host" => "gateway.example.com",
    "source" => "/var/log/audit/audit.log",
    "message" => "type=SERVICE_STOP msg=...terminal=? res=success'",
    "fields" => {
        "env" => "production"
    },
    "type" => "auditd",

现在,当我们从停止网关主机上的openvpn服务得到相同的消息时,type被设置为auditd。现在,我们可以在过滤器部分使用它,使审计日志更容易进一步处理。

日志隐藏过滤器

Logstash 过滤器是一种解析和转换数据的方式,可以更容易地发现日志中的内容。我们将获取我们的auditd日志,提取信息并将其添加到我们的事件数据中。

当我们查看原始的audit.log文件时,我们会看到这样的信息:

type=SERVICE_STOP msg=audit(1480844911.323:1080): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=openvpn@gateway comm="systemd" exe="/usr/lib/systemd/systemd" hostname=? addr=? terminal=? res=success'

这些是key=value日志。您可以看到type=SERVICE_STOPuid=0msg='...'都是键/值对。Logstash 能理解如何对付key=values。我们通过告诉过滤器获取每个键和值并对其赋值来实现这一点。您还可以看到,我们有一个audit(1480844911.323:1080),它是时间戳(1480844911.323,在 Unix 纪元时间中)加上一个唯一的 ID ( 1080,用于标记事件。

那么,我们如何让 Logstash 处理这些事件呢?在我们的节拍配置中,我们用auditddocument_type标记audit.logs。我们看到,从 Filebeat 进入 Logstash 的事件现在已经附加了类型auditd。我们现在可以匹配这个值,并处理具体包含该数据的日志。我们在过滤器部分使用了一个条件if语句。

filter {
  if [type] == "auditd" {
    # audit.log get matched and worked on here
  }
}

这里我们使用了一个条件语句if { ... },如果标签[type]等于字符串"auditd",它就匹配标签[type]。既然我们正在处理正确的日志,我们现在可以告诉 Logstash 根据它所看到的创建键/值对。我们使用以下代码行来实现这一点:

filter {
  if [type] =∼ "auditd" {
    kv { }
  }
}

这只会在我们的元数据中创建更多的标签,我们可以在这些标签上进一步工作并用于发现。让我们看看这对我们的 Logstash 数据有什么影响:

{
    "msg" => [
        [0] "audit(1480844911.323:1080):",
        [1] "unit=openvpn@gateway comm=\"systemd\" exe=\"/usr/lib/systemd/systemd\" hostname=? addr=? terminal=? res=success"
    ],
    "uid" => "0",
    "ses" => "4294967295",
    "auid" => "4294967295",
    "pid" => "1",
    "source" => "/var/log/audit/audit.log",
    "message" => "type=SERVICE_STOP msg=audit(1480844911.323:1080): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='unit=openvpn@gateway comm=\"systemd\" exe=\"/usr/lib/systemd/systemd\" hostname=? addr=? terminal=? res=success'",
    "type" => "SERVICE_STOP",
    "subj" => "system_u:system_r:init_t:s0"
    "tags" => [
        [0] "security",
        [1] "network",
        [2] "beats_input_codec_plain_applied"
    ],
    "offset" => 5738705,
    "input_type" => "log",
    "@timestamp" => 2016-12-04T09:48:31.650Z,
    "@version" => "1",
    "beat" => {
        "hostname" => "gateway.example.com",
        "name" => "gateway.example.com",
        "version" => "5.0.2"
    },
    "host" => "gateway.example.com",
    "fields" => {
        "env" => "production"
    },
}

现在你可以看到,我们通过用更多可用的数据来标记数据,从而给数据添加了更多的纹理。我们要求输出更易于阅读,但是您可以看到输出的前几行是来自消息内容的键/值对。如果我们给其中一些标签更多的细节,我们也可以使它更具可读性。我们用mutaterename函数来做这件事。

filter {
  if [type] =∼ "auditd" {
    kv { }
    mutate {
      rename => {
        "type"        => "audit_type"
        "auid"        => "audit_uid"
        "fsuid"       => "audit_fs_uid"
        "suid"        => "audit_set_uid"
        “subj”        => “audit_subject”
        "ses"         => "session_id"
        “hostname => “audit_hostname”
      }
    }
  }
}

现在,当我们查看我们的 Logstash 数据时,它看起来类似于这样:

{
...
    "audit_uid"       => "4294967295",
    "audit_subject" => "system_u:system_r:init_t:s0",
    "audit_type"     => "SERVICE_STOP"
    "session_id"     => "4294967295",
...
}

mutate功能改变了日志信息的状态。它允许我们将日志消息中的某些字段重命名为更清晰的标签。

最后,如果我们查看日志消息中的时间戳,它不会被提取。我们将确保数据最终也成为一个标签。为此,我们使用了一个名为grok的函数。一个grok函数由两部分组成,语法和语义,编写如下:

%{SYNTAX:SEMANTIC}

SYNTAX是与您的文本匹配的模式的名称。Logstash 附带了很多,您可以在这里查看它们:

模式是映射到名称的正则表达式。我们正在处理的时间戳是 epoch 或 Unix epoch,是自 1970 年以来的秒数。既然它是一个数字,我们可以使用本地 Logstash NUMBER模式来匹配它。

SEMANTIC只是我们将赋予它的标识符,以便我们为元数据标签增加更大的价值。我们给它audit_epoch

正如我们已经说过的,时间戳audit(1480844911.323:1080)有两个部分:时间和唯一标识符。我们将使用grok来搜索包含纪元和唯一 ID 的字符串,并提取它们作为我们的标签。

    grok {
      match => { "msg" => "audit\(%{NUMBER:audit_epoch}:%{NUMBER:audit_counter}\):" }
    }

这里我们告诉grok匹配msg键并找到audit(<somenumber>:<somenumber):字符串。对于第一场比赛,我们将其命名为audit_epoch,对于第二场比赛,我们将其命名为audit_counter。现在,当我们再次运行我们的 Logstash 服务时,我们将看到以下内容:

{
...
    "offset" => 5745528,
    "audit_epoch" => "1480846476.689",
   "audit_counter" => "1106",
...
}

我们现在已经成功地转换了审计日志,并向我们的 Logstash 数据添加了额外的标签。我们现在可以开始把它加入到弹性搜索中。

日志存储的弹性搜索

我们有从 Filebeat 上传的日志,我们有进入 Logstash 服务的日志,我们正在转换我们的一些日志,使它们在存储后更容易被发现。现在让我们看看我们将在哪里存储我们的日志数据,即 Elasticsearch。

Elasticsearch 是一个基于 Apache Lucene 的全文搜索和分析引擎。(Lucene 是一个代码库和 API: http://lucene.apache.org/core/ )。)Elasticsearch 非常擅长搜索和理解基于文本的数据。它有一个 RESTful API,允许您查询和存储数据。它基于分布式架构,可以存储数 Pb 的可搜索数据。

Elasticsearch 会将我们的日志数据存储在一个索引中。索引是数据的逻辑名称空间。使用 Logstash,默认情况下,我们为每天的日志数据创建一个索引。索引由文档组成,文档相当于关系数据库的行。每个索引都有一个映射,该映射定义了索引中的类型和其他索引设置,并描述了如何搜索索引。类型是文档的类型,就像用户或日志一样,被 API 用作过滤器。文档是一个 JSON 对象,每个对象都有一个类型和一个 ID。文档由一个或多个键/值对组成。

每个文档存储在一个主碎片中,可以有零个或多个副本碎片,这是为了冗余和性能。碎片分布在 Elasticsearch 集群的节点中。当一个节点关闭时,副本碎片可以提升为主碎片,并且碎片可以在整个集群中重新分布。

我们将在我们的监控主机上安装和配置它,这是一个单一节点。根据您想要的数据量,您显然需要对您的部署做出一些决定。对于分布式系统,以奇数部署总是好的,比如 1、3、5、7 等等。这有助于降低大脑分裂事件的风险,就像我们在第十三章中谈到的 GlusterFS。

弹性搜索安装和配置

Elasticsearch 安装需要至少 4GB 内存来运行服务,没有它将无法启动。一旦我们配置了由 Elastic Co .提供的 APT 存储库(我们在安装 Logstash 时配置的存储库),安装就很简单了。

毫不奇怪,安装只是使用 aptitude 包管理器:

$ sudo aptitude install –y elasticsearch

在我们的单服务器场景中,我们不需要太多额外的配置。我们现在可以启动 Elasticsearch 服务,并开始在其中存储我们的日志。然而,如果我们想要创建一个 Elasticsearch 集群,我们将需要编辑/etc/elasticsearch/elasticsearch.yml文件。

要创建 Elasticsearch 集群,您需要提供集群名和节点名。让我们像这样编辑文件:

cluster.name: monitor
node.name: monitor-node-1

同样在elasticsearch.yml文件中,我们可以设置存储数据的数据路径、日志目录、网络选项等等。

对于服务发现,或者说 Elasticsearch 如何发现集群中的其他节点,Elasticsearch 有一个 Zen 发现模块。Zen Discovery 模块用于发现其他集群节点、执行主节点选举、检测故障以及通知集群更新。它通过向节点发送网络 pings 或单播数据包来实现这一点,参与的主机可以对这些数据包做出反应。

因为我们没有设置集群,所以我们现在可以启动服务,而无需更改配置。

$ sudo systemctl start elasticsearch

当然,我们总是可以使用systemctlstatus子命令来检查我们的 Elasticsearch 服务的状态,但是我们也可以从命令行发出以下命令:

$ curl http://localhost:9200
{
  "name" : "7mf_JBi",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "69nMGfoYRYiwatKO6Nj1BA",
  "version" : {
    "number" : "5.0.2",
    "build_hash" : "f6b4951",
    "build_date" : "2016-11-24T10:07:18.101Z",
    "build_snapshot" : false,
    "lucene_version" : "6.2.1"
  },
  "tagline" : "You Know, for Search"
}

这个到端口 9200 的curl命令显示我们的 Elasticsearch 服务器正在响应。响应是 JSON 格式的。我们可以用类似这样的内容进一步查询 Elasticsearch:

curl -H "Content-Type: application/json" "http://localhost:9200/logstash-*/_search" -d '{
   "query": {
     "match": {
       "event_type": "SERVICE_STOP"
     }
   },
   "sort": [ "_doc" ]
 }'

这个curl命令现在在 Elasticsearch 上查询_search URI,以检查我们是否有与audit_type "SERVICE_STOP"匹配的内容。curl–d选项允许我们发送一个数据 POST,我们将其作为 JSON 格式的查询发送。

该查询的结果可能如下所示:

{
  "took":2,
  "timed_out":false,
  "_shards": {
     "total":5,
     "successful":5,
     "failed":0
   },
  "hits":  {
    "total":74,
    "max_score":null,
    "hits":[...the events...]
  }
}

第一部分告诉我们运行查询花了多长时间,我们成功地查询了五个碎片。在点击量部分,我们总共有 74 次点击SERVICE_STOP audit_type。还会有匹配打印的事件列表。因此,现在我们可以看到,我们的 Logstash 服务正在按照我们的预期从网关服务器发送数据。

除了使用curl命令搜索日志,还有另一个开源工具 Kibana 可以帮助我们可视化日志。我们现在将向您展示如何安装和配置它。

Kibana 安装和配置

Kibana 也被 Elastic 公司打包,可以从他们的 APT(和 YUM)存储库中获得。这使得安装对我们来说又变得容易了。您可以猜到,安装如下:

$ sudo aptitude install –y kibana

默认情况下,Kibana 的配置会查看弹性搜索服务的localhost:9200。配置文件位于此处:/etc/kibana/kibana.yml

server.port: 5601
server.host: "192.168.0.250"
elasticsearch.url: "http://localhost:9200"

在这里,我们用以下设置来设置我们的 Kibana 服务器。我们指定监听的端口、监听的 IP 地址以及在哪里可以找到 Elasticsearch 服务。现在我们可以开始 Kibana 服务了。

$ sudo systemctl start kibana

我们现在可以打开一个浏览器到 http://192.168.0.250 ,就会呈现给我们基巴纳的首页。

在图 18-2 中,我们看到了基巴纳的第一页。这允许我们配置我们的索引模式。缺省值Logstash-*在这个实例中是有意义的,因为我们处理的索引的命名格式是Logstash-<date>。我们还可以设置时间戳字段,告诉 Kibana 哪个字段代表时间,但是我们将保留它的默认值。单击创建继续。

A185439_2_En_18_Fig2_HTML.jpg

图 18-2。

Configuring index pattern

图 18-3 显示了Logstash-*索引中的所有字段及其相关类型。要改变这些,您需要使用 mappings API。我们不会改变这里的任何东西。

A185439_2_En_18_Fig3_HTML.jpg

图 18-3。

Index patterns

图 18-4 显示了高级设置选项卡。我们可以更改默认索引、日期格式和默认列。我们不需要在这里做任何改变。

A185439_2_En_18_Fig4_HTML.jpg

图 18-4。

Advanced Settings tab

图 18-5 中保存的对象选项卡目前没有显示任何内容。当您创建仪表板和可视化时,您将能够在此选项卡上管理它们。

A185439_2_En_18_Fig5_HTML.jpg

图 18-5。

Saved Objects tab

图 18-6 显示了基巴纳的搜索界面。这里的默认视图是我们日志事件的最后 15 分钟。在左上角,我们可以看到我们可以搜索多少个事件(14,094 次点击)。下面是搜索栏,我们可以在这里输入我们的搜索查询。目前,*意味着我们正在搜索(并显示)所有 14,094 个事件。有一个直观的显示,显示我们在特定时间段有多少事件。我们可以通过从右上方的下拉菜单中选择一个时间段来更改时间段。

A185439_2_En_18_Fig6_HTML.jpg

图 18-6。

All logs for the last 15 minutes

中间是按事件创建时间的日期顺序显示的结果。左侧是我们可以进一步细化查询和结果列表的所有字段。让我们看看现在该怎么做。

同样,我们将发现与SERVICE_ STOP审计事件相关的所有日志。在图 18-7 中,我们可以看到使用了audit_type字段,这就是我们在 Logstash 过滤器中标记它的方式。然后我们在SERVICE_STOP中指定我们正在寻找的字段的值。在过去的 30 分钟里,我们有 3 次点击,它们出现在中央面板。

A185439_2_En_18_Fig7_HTML.jpg

图 18-7。

Searching for SERVICE_STOP

进一步的信息

有关 Beats、Logstash、Elasticsearch 和 Kibana 的更多信息,您可以查看以下内容:

监控

一旦所有的应用程序和服务都在运行,就需要有某种机制来监控它们。这可确保您的主机在发生重要事件时通知您,例如当主机上的磁盘空间耗尽或服务意外停止时。

在 IT 界,这种监控机制被称为企业监控。就像我们在本书中向您介绍的其他应用程序和工具一样,有许多开源工具可以执行这种监控。

其中最著名的可能是 Nagios,接下来我们将对其进行更深入的研究。

Nagios-Core 简介

Nagios-Core 是一个流行的 GPL 许可的监控工具,它允许您监控基础设施、应用程序,甚至是环境特征,比如电力和空调。它附带了一个简单的 web 控制台,为您提供了主机和服务状态的可视化视图。您可以在图 18-8 中看到一个示例控制台屏幕。

A185439_2_En_18_Fig8_HTML.jpg

图 18-8。

An example console

我们将向您介绍 Nagios-Core,以及如何使用它来监控您的主机和服务。Nagios-Core 是 Nagios 的开源版本,已经存在很多年了。如果您需要额外的特性或支持,现在可以使用 Nagios 的商业版本。我们将向您展示如何为您的主机和本书前面介绍的一些服务设置一些基本的监控。

对于这本书,我们将研究 Nagios 的最新版本,即版本 4——它包含了所有的特性和功能。然而,Nagios 太复杂了,无法在这一章中完全解释清楚。幸运的是,Nagios 有很好的文档,你可以在 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/ 找到版本 4 的文档。CentOS 的第 4 版可从 EPEL 资料库获得。Ubuntu 仍然是第 3 版,但是你可以从 Nagios 网站下载第 4 版的源代码并编译它。

Nagios 能够监控许多平台上的各种主机和服务,包括 Linux、BSD、Solaris,甚至 Windows(参见 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/monitoring-windows.html 了解关于在 Windows 上监控的说明)。

Nagios 作为守护进程运行,监控主机上的服务及其状态。它可以通过监控来确认服务是否正在运行以及是否正常运行。例如,如果我们要使用 ICMP ping(我们在第七章中讨论过)来监控主机是否处于活动状态,我们可以将其配置为在无法联系主机或响应返回时间超过指定时间时发出警报。

要进行这种监控,您需要通过在 Nagios 配置中将主机和服务定义为对象来告诉 Nagios。每个主机都被定义给 Nagios,然后运行在该主机上的服务也被定义。

您还可以告诉 Nagios 如何通过定义命令来监控每个主机和服务。每个服务使用特定的命令来检查其状态,每个命令指定用于检查服务状态的二进制文件或脚本。

Note

您还将定义在服务检查失败时发送通知的命令(例如,生成电子邮件或发送页面)。

为了使创建命令更容易,Nagios 附带了一组插件,这些插件是为检查特定服务而设计的二进制文件;例如,Nagios 有一个名为check_icmp的插件,它使用 IMCP ping 请求来确认主机是活动的。

Nagios 可以查询本地主机和远程主机上的服务。这种查询可以直接对主机进行(例如,在端口 25 上连接到 SMTP 服务器并测试您是否可以接收电子邮件),也可以通过安装在主机上的代理进行,代理将结果返回给监控主机。

除了监控主机和服务,Nagios 还有许多其他有用的功能,包括:

安装 Nagios

Nagios 易于安装,CentOS 和 Ubuntu 都有软件包。

在 CentOS 上安装 Nagios

在 CentOS 上,我们可以安装包含以下内容的 Nagios 包:

$ sudo yum install nagios nagios-plugins nagios-plugins-ssh nagios-plugins-smtp nagios-plugins-bacula
nagios-plugins-disk nagios-plugins-fping nagios-plugins-http nagios-plugins-ldap nagios-plugins-mysql

我们还选择了一些要安装的插件。虽然我们不会向您展示如何使用每个插件,但是您可以看到,对于我们在本书中展示的大多数服务,都有可用的插件。

在 Ubuntu 上安装 Nagios

在 Ubuntu 上,Ubuntu 的默认版本是版本 3。如果我们想安装版本 4,我们将不得不从源安装。

$ sudo apt-get install nagios3 nagios-plugins

这将安装 Nagios 版本 3 和所有需要的支持包,包括 Nagios 插件。

如果您想从源代码安装(对于 CentOS 或 Ubuntu,但这是针对 Ubuntu 的),您需要执行以下操作。

首先,我们需要创建用户和组来运行和管理 Nagios 服务。

$ sudo useradd nagios  && \
sudo groupadd nagcmd && \
sudo usermod -aG nagcmd www-data && \
sudo usermod –aG nagcmd nagios

我们已经将nagcmd组添加到了www-datanagios用户中,这样 web 服务就可以读取 Nagios 结果。现在我们需要下载源码,解包,然后进入目录。

$ wget https://assets.nagios.com/downloads/nagioscore/releases/nagios-4.2.3.tar.gz && \
tar zxf nagios-4.2.3.tar.gz && \
cd nagios-4.2.3/

如果你记得我们在第八章编译 Nginx 的时候,我们现在会做一些类似 Nagios 的事情。我们首先必须为我们的安装配置任何特殊需求,然后编译任何二进制文件,最后在我们的系统上安装 Nagios。

$ ./configure --with-httpd-conf=/etc/apache2/conf-available \
  --with-nagios-user=nagios \
  --with-nagios-group=nagcmd \
  --sysconfdir=/etc/nagios

在这里,我们已经配置了 Nagios 服务,将 Apache 配置放到适当的位置(您可能希望在这里选择/etc/apache2/sites-available)。我们已经声明了用户和组,并将我们的--sysconfdir(系统配置目录)放在/etc/nagios中,这在 CentOS 中很常见(在 Ubuntu 中通常是/etc/nagios<version  number>,但我们在这里不选择它)。

现在我们需要编译源代码,然后安装各种组件。

$ sudo make all
$ sudo bash -c 'make install
make install-init
make install-config
make install-commandmode
make install-webconf'

配置文件安装在/etc/nagios中,Nagios 二进制文件安装在/usr/local/nagios/bin/nagios中。如果您想安装最新的插件,您可以这样做:

$ wget https://nagios-plugins.org/download/nagios-plugins-2.1.4.tar.gz && \
tar zxf nagios-plugins-2.1.4.tar.gz && \
cd nagios-plugins-2.1.4/ && \
./configure --with-nagios-user=nagios --with-nagios-group=nagcmd && \
make && sudo make install

这将把插件安装到/usr/local/nagios/libexec/目录中。我们需要做的最后一件事是确保我们有一个systemd服务文件来启动服务。

$ sudo vi /etc/systemd/system/nagios.service
[Unit]
Documentation=man:systemd-sysv-generator(8)
SourcePath=/etc/init.d/nagios
Description=LSB: nagios host/service/network monitoring and management system
Before=multi-user.target
Before=multi-user.target
Before=multi-user.target
Before=graphical.target
Before=shutdown.target
After=local-fs.target
After=remote-fs.target
After=systemd-journald-dev-log.socket
After=nss-lookup.target
After=network-online.target
After=time-sync.target
Wants=network-online.target
Conflicts=shutdown.target

[Service]
Type=forking
Restart=no
TimeoutSec=5min
IgnoreSIGPIPE=no
KillMode=process
GuessMainPID=no
RemainAfterExit=yes
ExecStart=/etc/init.d/nagios start
ExecStop=/etc/init.d/nagios stop
ExecReload=/etc/init.d/nagios reload

因为还没有适用于 Ubuntu 的原生systemd服务文件,Nagios 安装将部署/etc/init.d/nagios文件,我们只需将这个服务文件指向它(我们在第六章谈到了 Ubuntu 对systemd的采用,以及它如何仍然使用一些 LSB init文件来启动服务)。

启动 Nagios

在 CentOS 和 Ubuntu 上,我们用systemctl命令启动和停止 Nagios。

$ sudo systemctl start nagios

nagios守护进程记录到/var/log/nagios/nagios.log日志文件中。您可以确认守护程序已成功启动。如果没有,您将在该文件中看到错误。当然,你也可以使用journalctlsystemctl status命令。

Nagios 配置

我们将快速向您介绍如何配置 Nagios。简单地说,配置 Nagios 的步骤如下:

  1. 为要监控的主机创建定义。
  2. 为要在主机上监控的服务创建定义。
  3. 创建命令来监控您的服务。
  4. 当你想监控他们的时候告诉纳吉奥斯。
  5. 告诉 Nagios 如果检查失败应该告诉谁。
  6. 告诉 Nagios 如果检查失败应该如何通知人们——电子邮件、即时消息、短信、寻呼机、聊天机器人等等。

Nagios 配置由对象组成。您将要监控的主机定义为主机对象,将要监控的每个服务定义为服务对象。还存在各种其他对象类型,比如用于监控时间段的时间段对象和用于告诉 Nagios 在发生事情时应该通知谁的联系对象。

我们将向您展示如何在 Nagios 中配置您的主机,然后如何配置各种类型的服务。在这个过程中,我们将向您展示 Nagios 配置中的各种其他元素。

Nagios 配置存储在 CentOS 上的/etc/nagios中,根据您如何配置 Ubuntu 安装,也存储在/etc/nagios中。Nagios 配置文件的后缀是.cfg,Nagios 的主配置文件叫做nagios.cfg

CentOS 和 Ubuntu 都提供了一些示例配置来帮助您开始使用 Nagios。在这两个发行版中,/etc/nagios/objects/localhost.cfg配置文件包含本地主机的一些基本配置和一些服务。此外,在同一个目录中,您将看到打印机、Windows 主机、时间段、模板和交换机的一些其他配置示例。

Nagios 还有一种模式,使您能够在运行守护进程之前检查配置中的错误。这有助于确认您没有任何错误。

在 CentOS 上,我们将运行以下内容:

$ sudo nagios -v /etc/nagios/nagios.cfg

在 Ubuntu 上,根据您是选择 Nagios 版本 3 还是像我们之前一样构建版本 4,您可以在下面找到它:

$ sudo nagios3 -v /etc/nagios3/nagios.cfg

或者这里:

$ sudo /usr/local/nagios/bin/nagios -v /etc/nagios/nagios.cfg

-v选项检查所有配置是否正确,如果正确,输出一个统计报告,显示定义的配置对象的数量。

Note

更改 Nagios 配置后,您需要重新启动守护进程,以便解析新的配置。

nagios.cfg 文件

nagios.cfg配置文件包含 Nagios 安装的基本配置。这个文件中的每个选项都是一个option-value对的形式。例如,使用log_file选项指定 Nagios 日志文件的位置;在 CentOS 上,这将按如下方式完成:

log_file=/var/log/nagios/nagios.log

这通常是你的nagios.cfg文件中的第一个选项,接着是指定你的对象配置文件位置的cfg_filecfg_dir选项。cfg_file选项允许您指定包含 Nagios 对象配置的单个文件。这里有一个例子:

# Definitions for monitoring the local (Linux) host
cfg_file=/etc/nagios/objects/localhost.cfg

您可以指定多个文件;事实上,许多人在单独的文件中指定每个对象类型来组织它们。

cfg_file=/etc/nagios/objects/commands.cfg
cfg_file=/etc/nagios/objects/contacts.cfg
cfg_file=/etc/nagios/objects/timeperiods.cfg
cfg_file=/etc/nagios/objects/templates.cfg
...

Note

将文件放入版本控制系统(VCS)是个好主意,比如 Subversion 或 Git。这种系统跟踪你的文件和文件的变化。程序员通常使用它们来跟踪源代码,系统管理员也越来越多地使用它们来跟踪配置文件。在第三章中,我们向你展示了如何安装 Git。

在配置文件中,您可以找到cfg_dir选项,它指定了一个目录。Nagios 将加载这个目录中后缀为.cfg的任何文件。这里有一个例子:

cfg_dir=/etc/nagios/conf.d

nagios.cfg文件包含许多其他有用的选项,其中一些你可以在表 18-4 中看到。

表 18-4。

nagios.cfg Configuration File Options

| [计]选项 | 描述 | | --- | --- | | `resource_file` | 一个单独的配置文件,用于保存路径和密码等系统变量。 | | `nagios_user` | 运行 Nagios 的用户。这默认为`nagios`。 | | `nagios_group` | 运行 Nagios 的组。这默认为`nagios`。 | | `log_rotation_method` | 何时轮换日志。值为`n`表示不轮换,`h`表示每小时,`d`表示每天,`w`表示每周,`m`表示每月。 | | `log_archive_path` | 存储存档的循环日志文件的目录。 | | `use_syslog` | 是否将 Nagios 输出记录到`syslog` ( `rsyslog`)。对于`syslog`记录,默认为`1`。设置为 0 表示没有`syslog`记录。 |

您还可以在nagios.cfg配置文件中打开和关闭对主机和服务的检查,以及在全局级别发送警报。有关可用选项的完整列表,请参见 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/configmain.html

主机配置

让我们通过打开 CentOS 的/etc/nagios/objects/localhost.cfg配置文件并查看其内容来开始检查 Nagios 的配置,从主机对象定义开始。

Note

我们在本章的源代码中包含了 CentOS 和 Ubuntu 示例配置文件。

我们将从您在清单 18-16 中看到的文件中的主机对象定义开始。

define host {
        use                   linux-server    ; Name of host template to use
                                                       ; This host definition will inherit all variables that are defined
                                                       ; in (or inherited by) the linux-server host template definition.
        host_name       localhost
        alias                 localhost
        address            127.0.0.1
        }
Listing 18-16.A Host Object

您可以看到一个对象定义以define、要定义的对象类型(在我们的例子中是一个主机对象)和定义开始,定义包含在{ }花括号中。在定义中,对象的属性由一系列键/值语句定义,用空格分隔。我们的主机对象定义有四个属性:usehost_namealiasaddress

Tip

某些属性对于某些对象定义是强制性的,这意味着您必须为它们指定属性和值。在 Nagios 文档中,这些值在 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/objectdefinitions.html 处用红色表示。

属性告诉我们的主机对象引用一个模板。模板是 Nagios 用来在对象定义中填充值的技术,这些值在许多对象中可能是相同的。例如,宿主对象将共享许多相同的属性和特征。您可以引用模板,而不是在每个宿主对象定义中指定每个属性。Nagios 然后用主机定义中的所有属性加上模板中的那些属性创建主机对象。一会儿我们将看看清单 18-17 中的linux-server模板中定义的附加属性。

在这种情况下,我们的主机对象的其余属性定义了它的身份。host_name属性定义了主机对象的名称。该名称必须唯一。您只能有一个名为localhostheadoffice.example.com的主机对象。Nagios 还将host_name属性作为名为$HOSTNAME$的宏提供。

Note

宏允许 Nagios 在其他对象定义中嵌入关于主机和服务的信息,特别是 Nagios 用来检查服务和发送通知的命令。在本章后面你会看到更多的宏;同时,你可以在 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/macros.html 看到这些宏的完整列表。

alias属性是对象的另一个名称;在本例中,我们使用了别名localhost。该别名通常用作主机的更长描述,也可用作名为$HOSTALIAS$的宏。

最后一个属性address,提供主机的 IP 地址;在本例中,我们正在监控本地主机 127.0.0.1。Nagios 必须能够联系到这个 IP 地址,以便进行监控。它也可以作为宏$HOSTADDRESS$使用。

Note

您也可以指定主机的完全限定域名,但这要求您的 DNS 正在工作并且可以解析主机名。如果 DNS 失败,Nagios 检查也可能失败。我们建议在适当的情况下使用 IP 地址作为该属性的值,尤其是在核心服务上,如交换机和 DNS 服务器。

主机模板

现在让我们看看我们的linux-server模板提供了哪些附加属性。在清单 18-17 中,您可以看到linux-server主机对象模板。

define host {
        name                           linux-server
        use                              generic-host
        check_period               24x7
        check_interval             5
        retry_interval               1
        max_check_attempts  10
        check_command         check-host-alive
        notification_period       workhours
        notification_interval     120
        notification_options     d,u,r
        contact_groups           admins
        register                       0
        }
Listing 18-17.A Host Object Template

你可以看到我们在模板中定义了更多的属性。首先,我们定义这是哪种类型的对象的模板,在我们的例子中是宿主对象。接下来,使用name属性,我们给模板一个名称,这个名称必须是唯一的。不能有两个名为linux-server的模板。

下一个属性是您以前见过的一个属性,use,它允许我们指定一个模板,这个模板从这个模板继承而来。迷茫?简单地说,Nagios 允许您将模板链接在一起。这使您能够构建非常复杂的模板模型,从而最大限度地减少定义监控环境所需的输入量。我们一会儿还会看看generic-host模板。

dns-servers <- critical-servers <- linux-servers <- generic-host

您可以在generic-host模板中定义您想要的默认值,覆盖或添加到linux-servers中的默认值,覆盖或添加到critical-servers中的默认值,最后覆盖或添加到dns-servers模板中的那些定义。host定义也可以覆盖或添加到模板中定义的那些定义中。

Tip

如果在多个模板中定义同一个属性—使用哪个属性值呢?Nagios 向下继承:对属性的最后一个引用是被使用的那个。例如,如果属性check_period是在模板generic-hostlinux-server以及本地主机的主机对象定义中定义的,那么它在localhost对象中的值就是所使用的值。

清单 18-17 中接下来的五个属性、check_periodcheck_intervalretry_intervalmax_check_attemptscheck_command,都是相关的。

时间段

第一个属性check_period告诉 Nagios 何时检查主机。在我们的例子中,我们指定了一个称为24x7的时间段。我们还需要在 Nagios 配置中定义这个时间段。

define timeperiod{
          timeperiod_name       24x7
          alias                 24 Hours A Day, 7 Days A Week
          Sunday                00:00-24:00
          monday                00:00-24:00
          tuesday               00:00-24:00
          wednesday             00:00-24:00
          Thursday              00:00-24:00
          friday                00:00-24:00
          saturday              00:00-24:00
          }

这是一个简单的时间段定义,它有一个timeperiod_name,在我们的例子中是24x7,和一个alias描述。然后,我们定义了一周中的每一天,以及我们希望时间段覆盖的那些天中的时间。在这个时间段定义中,我们定义了一周中的每一天和一天 24 小时。

为了不覆盖某一天,你只需不指定它。时间以 24 小时制指定,您可以指定多个范围。这里有一个例子:

Sunday    00:00-02:00,17:00-19:00

这里我们的时间段是周日午夜到凌晨 2 点,下午 5 点到 7 点

Nagios 中很多地方都使用时间段,但最常见的是,它们指定何时应该检查主机和服务,以及何时应该发送通知(当主机和服务失败或偏离其所需状态时生成的消息)。

下一个属性max_check_attempts,指定 Nagios 在确定有问题之前检查主机或服务的次数。

check_interval指定了检查之间的时间间隔,如果出现故障,我们可以使用retry_interval来增加或减少检查频率。

命令

最后一个属性check_ command告诉 Nagios 使用什么命令来检查主机的状态,在本例中是check-host-alive

这是我们之前讨论过的命令之一。现在就来看看吧。

define command{
          command_name    check-host-alive
          command_line    $USER1$/check_ping -H $HOSTADDRESS$ -w 3000.0,80% -c 5000.0,100% -p 1
          }

命令的定义与其他对象一样。它们用command_name属性命名,要执行的实际命令通过command_line属性指定。在本例中,我们指定了以下行:

$USER1$/check_ping -H $HOSTADDRESS$ -w 3000.0,80% -c 5000.0,100% -p 1

命令的第一部分$USER1$是另一个 Nagios 宏。$USERx$宏是配置变量,通常在一个名为resource.cfg的文件中配置(或在nagios.cfg配置文件中使用resource_file配置选项指定的另一个文件)。在这个例子中,$USER1$宏的值是包含 Nagios 插件的目录。

$USER1$=/usr/lib64/nagios/plugins

该命令的下一部分是该命令将使用的 Nagios 插件check_ping,它使用 ICMP pings 来检查主机的状态。

Note

正如我们在第七章中所讨论的,这假设您的主机的防火墙被配置为接受 ICMP pings。

在这个命令中,您可以看到 Nagios 的一个宏$HOSTADDRESS$的使用,您之前了解到它包含主机的 IP 地址。每当主机执行 check 命令时,它的地址就会替换宏。这允许多个主机对象使用同一个命令。该宏被指定为-H选项的值,该选项指定要 ping 的主机。

Note

通过运行带有--help选项的命令,您可以从大多数 Nagios 插件中获得帮助文本。您还可以在命令行上运行大多数 Nagios 插件,看看它们是如何工作的,它们的命令行选项,以及它们返回什么结果。

接下来的两个选项-w-c指定了该检查的阈值。如果超过了这些阈值,Nagios 将更新主机或服务的状态。

主机和服务具有不同的状态。主机可以处于UPDOWNUNREACHABLE状态,服务可以处于UPWARNINGCRITICALUNKNOWN状态。

Note

UNREACHABLE状态用于依赖关系和父子关系已配置,且主机因其依赖的父主机或主机不可用而不可用的情况。

但是,插件本身只返回WARNINGCRITICALUNKNOWN状态(通常在插件运行失败或返回错误而不是有效状态时设置UNKNOWN状态)。当这些插件为主机运行时,Nagios 解释这些状态并将其转换成适当的UPDOWN状态,如表 18-5 所示。

表 18-5。

Nagios Plug-in Status Conversions

| 插件状态 | 主机状态 | 状态描述 | | --- | --- | --- | | `OK` | `UP` | 主持人起来了。 | | `WARNING` | `UP`或`DOWN` | 主机可以启动或关闭,但默认情况下是启动的。 | | `UNKNOWN` | `DOWN` | 主机停机了。 | | `CRITICAL` | `DOWN` | 主机停机了。 |

Note

Nagios 也可以使用这个主机状态作为一个名为$HOSTSTATE$的宏。另外,请在此处查看有关主机检查选项的更多信息: https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/configmain.html

-w-c选项有两个值:以毫秒为单位的往返时间和不超过阈值所需的成功 pings 的百分比。因此,如果突破了-w阈值,就会生成WARNING状态,Nagios 会将主机标记为UP。但是,如果突破了-c阈值,就会生成CRITICAL状态,Nagios 会将主机标记为DOWN。然后通常会生成一个通知。我们命令行的最后一个选项-p,指定要发送的 pings 的数量。

Note

除了插件返回的状态之外,例如,WARNINGCRITICAL,您还将获得一些描述该状态的输出,您可以在通知中使用这些输出,或者在控制台中显示这些输出。例如,check_ping插件返回PING OK - Packet loss = 0%, RTA = 3.98 ms。这个输出也可以作为一个宏使用,对于主机叫做$HOSTOUTPUT$,对于服务检查叫做$SERVICEOUTPUT$

通知期

清单 18-17 中的下一个属性是notification_period属性。这与检查期间略有不同。虽然检查发生在check_period时间段,在我们的例子中是24x7时间段,但是通知(状态改变时生成的警报)将只在workhours时间段内发送。

define timeperiod{
          timeperiod_name      workhours
          alias                "Normal" Working Hours
          monday               09:00-17:00
          tuesday              09:00-17:00
          wednesday            09:00-17:00
          thursday             09:00-17:00
          Friday               09:00-17:00
          }

您可以看到工作时间周期是上午 9 点到下午 5 点,而不是我们的24x7检查周期指定的一周每天 24 小时。

清单 18-17 ,notification_interval中的下一个属性配置如果主机状态不变,Nagios 将多长时间重发一次通知;这里是每 120 分钟或 2 小时一次。

notification_options属性指定 Nagios 应该何时发送通知。这里它被设置为dur,这意味着当主机是DOWN (d)UNREACHABLE (u)时,Nagios 将发送通知。最后一个选项r,如果主机已经恢复(即从DOWNUNREACHABLE状态变为UP状态),则发送通知。

联系人和联系人组

下一个属性contact_groups,告诉 Nagios 在生成通知时通知谁。在我们的例子中,这个值是admins,它指向一个contactgroup对象。联系人组是联系人的集合,是您希望在生成通知时通知的人,例如您自己或其他系统管理员。一个contactgroup物体看起来像这样:

define contactgroup{
          contactgroup_name       admins
          alias                   Nagios Administrators
          members                 nagios-admin
          }

一个联系人组有一个通过contactgroup_name属性定义的名称,一个由alias属性提供的描述,以及一个使用members属性指定的该组中的联系人列表。在这种情况下,联系人组的唯一成员是nagios-admin,您可以在这里看到:

define contact{
         contact_name                             nagios-admin
         alias                                            Nagios Admin
         service_notification_period         24x7
         host_notification_period            24x7
         service_notification_options       w,u,c,r
         host_notification_options            d,r
         service_notification_commands  notify-by-email
         host_notification_commands       host-notify-by-email
         email                                           nagios-admin@localhost
         }

联系人很容易定义。每个都有一个通过contact_name属性和一个alias提供的名称。

对于每个联系人,我们指定他们应该何时接收通知以及应该接收什么通知。

为了指定何时接收通知,我们使用service_notification_periodhost_notification_period。在我们的例子中,nagios-admin联系人将在您之前看到的24x7时间段内收到通知,实际上是一周的 24 小时,包括主机和服务。

为了指定哪些通知,我们使用了service_notification_optionshost_notification_options属性。对于服务,nagios-admin联系人将收到WARNINGUNKNOWNCRITICAL,分别由wuc选项指示;r选项意味着它也将接收恢复通知。对于主机,联系人将仅接收DOWN (d)recovery (r)通知。

service_notification_commandshost_notification_commands属性指定 Nagios 用来发送通知的命令。您可以通过用逗号分隔来指定多个命令。这些命令的定义就像用于检查主机和服务的命令一样。让我们来看看其中的一个命令,notify-by-email

define command{
          command_name      host-notify-by-email
           command_line    /usr/bin/printf "%b" "***** Nagios *****\n\nNotification Type: $NOTIFICATIONTYPE$\nHost: $HOSTNAME$\nState: $HOSTSTATE$\nAddress: $HOSTADDRESS$\nInfo: $HOSTOUTPUT$\n\nDate/Time: $LONGDATETIME$\n" | /usr/bin/mail -s "** $NOTIFICATIONTYPE$ Host Alert: $HOSTNAME$ is $HOSTSTATE$ **" $CONTACTEMAIL$
   }

与您看到的前一个命令一样,名称由command_name属性提供,实际命令由command_line属性执行。在本例中,我们打印一些文本,包括一些宏到/bin/mail二进制文件中。

这将向任何所需的联系人发送电子邮件,通知他们状态的变化。例如,如果 Nagios 正在监控我们的gateway.example.com主机,并且对该主机的检查失败了,那么将会生成一个类似于下面的通知:

***** Nagios *****

Notification Type: PROBLEM
Host: gateway.example.com
State: DOWN

Address: 192.168.0.254
Info: PING CRITICAL - Packet loss = 100%

Date/Time: Fri Feb 13 00:30:28 EST 2009

Tip

Nagios 不仅可以通过电子邮件发布警报。事实上,Nagios 可以向您可以为之构建警报命令的任何东西发出警报,例如,通过 API webhooks、聊天机器人(Hubot、HipChat、Slack)、Jabber 之类的即时消息、PagerDuty 之类的寻呼机,甚至是通过它们的 API 的票务系统。

表 18-6 提供了我们的通知命令中使用的宏列表。

表 18-6。

Macros in the Notification Command

| 巨 | 描述 | | --- | --- | | `$NOTIFICATIONTYPE$` | 通知的类型,例如,对于问题为`PROBLEM`,如果主机已恢复则为`RECOVERY` | | `$HOSTNAME$` | 通知您的主机的名称 | | `$HOSTSTATE$` | 当前主机状态,例如,`UP`或`DOWN` | | `$HOSTADDRESS$` | 主机的 IP 地址 | | `$HOSTOUTPUT$` | 用于检查主机状态的命令的文本输出 | | `$LONGDATETIME$$` | 长格式的日期和时间(如`Fri Feb 13 00:30:28 EST 2009`) | | `$CONTACTEMAIL$` | 要发送电子邮件的联系人的电子邮件地址 |

Note

你可以在 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/macrolist.html 看到宏的完整列表和使用位置。

让我们回到我们的接触定义;您可以看到最后一个属性email,它指定了通知要发送到的电子邮件地址,您已经看到它是作为$CONTACTEMAIL$宏提供的。

在我们的模板中,你可以看到清单 18-17 模板中的最后一个属性是register。这个属性告诉 Nagios 这是一个模板,而不是真正的主机定义;当register设置为 0 时,Nagios 不会尝试创建主机对象。反而忽略了。register的默认设置是 1,这意味着任何没有显式指定register 0 的对象定义都将被认为是一个真正的主机对象,并由 Nagios 进行监控。

现在让我们快速看一下父模板generic-host,如清单 18-18 所示。

define host{
          name                                   generic-host
          notifications_enabled              1
          event_handler_enabled         1
          flap_detection_enabled           1
          failure_prediction_enabled    1
          process_perf_data                  1
          retain_status_information        1
          retain_nonstatus_information  1
          notification_period                   24x7
          register                              0
          }
Listing 18-18.The generic-host Template

Note

我们不打算在这里详细解释这些选项;你可以在 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/objectdefinitions.html - host看到关于他们的阅读。

定义我们的主机

我们将定义自己的主机。我们从linux-server模板继承了我们的一些定义,反过来,它将从generic-host模板继承,就像我们在这里展示的一样。例如,我们的gateway.example.com主机可以这样定义:

define host{
          use                      linux-server
          host_name          gateway.example.com
          alias                    gateway.example.com
          address               192.168.0.254
          }

Tip

不要忘记,在添加任何新的配置之后,都需要重启 Nagios。

这里我们已经为gateway.example.com定义了一个主机对象,并指定我们将使用我们刚刚探索过的主机模板。我们已经指定了它的内部 IP 地址 192.168.0.254,Nagios 将使用这个地址尝试通过 ICMP 监控主机。我们的gateway.example.com主机上的防火墙必须允许 ICMP 数据包,以确保监控是可能的。

Note

还有几个与主机监控相关的对象定义,我们还没有看到,它们允许您将主机分组在一起,支持主机之间的依赖关系,并提供类似的功能。您可以在 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/objectinheritance.htmlxodtemplate.html 查看对象类型及其属性的完整列表。

服务配置

现在您已经了解了一些关于主机对象的知识,我们将研究一个服务对象。服务是使用service-type对象定义的,并链接到它们的底层主机。例如,基于我们现有的配置示例,清单 18-19 显示了一个检查我们根分区的磁盘空间的服务。

define service{
          use                                local-service
          host_name                    localhost
          service_description       Root Partition
          check_command           check_local_disk!20%!10%!/
          }
Listing 18-19.A Service Definition

我们的服务定义很简单。属性指定了我们的服务将要使用的模板。host_name属性指定服务在哪个主机上运行,在我们的例子中是localhostservice_description描述了服务。最后,check_ command属性指定了服务用来检查被监控对象状态的命令。这个check_command略有不同;在我们要使用的命令之后,您可以看到一个字符串。

!20%!10%!/

这个字符串由我们传递给命令定义的变量组成,每个变量值前面都有一个感叹号(!)。所以这里我们将值20%10%/传递给命令。这允许我们为多个服务重用一个命令,您马上就会看到这一点。

让我们快速看一下check_local_disk命令:

define command{
          command_name    check_local_disk
          command_line       $USER1$/check_disk -w $ARG1$ -c $ARG2$ -p $ARG3$
          }

就像我们之前的命令一样,用command_line我们指定了$USER1$宏来给我们提供正在执行的插件的路径。这个插件是check_disk,它检查本地磁盘的状态。

您还可以看到-w-c选项——我们之前告诉过您,它们设置了WARNINGCRITICAL状态的阈值。最后,我们有-p选项,它指定我们正在监控的磁盘分区。然而,在这个命令中,每个选项的值分别是$ARGx$: $ARG1$$ARG2$$ARG3$。这些参数中的每一个都代表我们在清单 18-19 中的check_command属性中传递的一个参数,所以我们的command_line命令实际上看起来如下:

command_line    $USER1$/check_disk -w 20% -c 10% -p /

这导致当只有 20%的磁盘空间可用时生成WARNING状态,当 10%的磁盘空间可用时生成CRITICAL状态,这两种状态都应用于根文件系统,即/

要创建一个服务来监控另一个分区上的磁盘,例如,/var,我们将创建如下所示的服务:

define service{
          use                            local-service
          host_name                localhost
          service_description   Var Partition
          check_command       check_local_disk!20%!10%!/var
          }

在我们讨论其他服务之前,让我们快速看一下我们的服务正在使用的local- service模板:

define service{
        name                                     local-service
        use                                        generic-service
        max_check_attempts            4
        normal_check_interval         5
        retry_check_interval              1
        register                                  0
        }

服务模板local- service类似于您之前看到的模板,但是具有一些附加属性。这些新属性中的第一个属性是normal_check_interval,它指定 Nagios 检查服务是否正常的频率,在本例中是每 5 分钟一次。第二个新属性retry_check_interval是相关的。如果在检查服务时,Nagios 发现服务不正常,它会按照在max_check_attempts属性中指定的次数重试检查。这是在将服务标记为不正常之前完成的。在此重试期间,不是按照normal_check_interval中的规定每 5 分钟检查一次,而是按照retry_check_interval中的规定每 1 分钟检查一次。

Note

Nagios 有软状态和硬状态的概念。当检查失败时,我们发现 Nagios 会检查它由max_check_attempts指定的次数。在 Nagios 用尽所有检查尝试之前,主机或服务会被标记为软故障状态。当检查尝试用尽并生成通知时,主机或服务现在处于硬失败状态。这种软故障模式意味着,如果主机或服务暂时出现故障并随后恢复,您不会收到通知,从而减少了来自监控系统的潜在误报警报数量。你可以在 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/statetypes.html 了解更多。

注意,这个模板还有一个父模板generic-service,我们不打算详细讨论。可以说,像notification_optionsnotification_periodcontact_groupscheck_period这样的选项是在那里定义的,并且可以在下游模板中被覆盖。在 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/objectdefinitions.html - service中解释了该模板中使用的选项。

Note

Nagios 试图智能地进行监控,通常不检查主机的状态,除非运行在该主机上的服务出现问题。如果主机上的服务失败,Nagios 通常也会安排对底层主机的检查。你可以在 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/hostchecks.html 了解更多信息。

让我们来看看另一个服务定义,这个定义用于监控基于网络的服务,如清单 18-20 所示。

define service{
          use                               local-service   ; Name of service template to use
          host_name                    gateway.example.com
          service_description       Check SMTP
          check_command           check_smtp!25
          }
Listing 18-20.A Network-Based Service Definition

在清单 18-20 中,我们有一个名为Check SMTP的新服务,它使用我们的local-service模板和check_smtp!25check_command。这将值 25 传递给一个名为check_smtp的命令。现在让我们来看看这个命令:

define command{
         command_name    check_smtp
         command_line    $USER1$/check_smtp -H $HOSTADDRESS$ -p $ARG1$
         }

这里我们有一个运行名为check_ smtp的插件的命令。它接受$HOSTADDRESS$宏,这是我们想要检查的 SMTP 服务器的 IP 地址。-p选项指定了端口(插件默认为端口 25),我们将这个值作为$ARG1$宏传入。

您可以在此处的nagios.log日志文件中看到该服务生成的服务警报:

[1481325559] SERVICE ALERT: gateway.example.com;Check SMTP;CRITICAL;HARD;4;
CRITICAL - Socket timeout after 10 seconds

nagios.log条目指定 Unix 纪元时间(1481325559Fri Dec 9 23:19:19 UTC 2016)、警报类型、主机和服务以及警报的性质,包括插件的输出。

Note

您可以从命令行转换纪元时间:date --date='@1235270465'

Remote Monitoring

到目前为止,您只看到了如何监控本地主机上的服务,比如我们的本地磁盘,或者可以通过网络访问的服务,比如 SMTP、IMAP 或 SSH。Nagios 还可以监控未暴露在网络中的远程主机上的服务。Nagios 附带了关于如何监控各种远程主机的各种说明,但是两种主要的流行机制是 NRPE 和 NSCA 插件。这里还可以找到其他几个: https://www.nagios.org/downloads/nagios-core-addons/

对于不适合安装任何一个插件的操作系统,您也可以使用check_by_ssh插件通过 SSH 进行检查。

NRPE 是一个允许您在远程主机上执行 Nagios 插件并将结果返回给nagios守护进程的工具。Nagios 服务器将安排和执行每个检查。在大型分布式环境中,这可能会很麻烦,因为延迟和检查次数确实会影响性能。你可以在 https://github.com/NagiosEnterprises/nrpe 找到 NRPE 文档。

NSCA 是一种允许被动检查的服务。被动检查是指,被监控客户机上的 NSCA 服务将运行检查,然后将结果发送给 Nagios 服务器,而不是 Nagios 服务器联系被监控客户机并执行命令。这在分布式环境中很有用。详情在此: https://exchange.nagios.org/directory/Addons/Passive-Checks/NSCA--2D-Nagios-Service-Check-Acceptor/details

NCPA 被设计成既主动又被动。它支持多种操作系统(包括 MAC 和 Windows ),并包括一个本地监控界面。看这里: https://exchange.nagios.org/directory/Addons/Monitoring-Agents/NCPA/details

check_by_ssh插件允许您通过 SSH 登录到远程主机,执行命令并返回结果。当操作系统只支持 SSH 访问时,这很有用。

您可以在以下文档中找到有关监控方法的更多信息:

简单的远程监控

您可以使用 Nagios 插件创建各种基于网络的服务,但是如果您想监控不面向网络或者不在本地主机上的服务呢?实现这一点的方法之一是一个名为check_by_ssh的特殊插件(关于其他方法,请参见“远程监控”侧栏)。

check_by_ssh插件使用 SSH 连接到远程主机并执行命令。因此,要使用这个插件,您需要在远程主机上运行一个 SSH 守护进程,任何介入的防火墙都必须允许 SSH 访问主机。

您还需要在主机之间使用基于密钥的认证,因为 Nagios 在检查服务时不能输入密码。因此,我们将从创建一个在 Nagios 服务器和远程主机之间使用的密钥开始。

Note

我们在第十章中介绍了基于密钥的 SSH 认证。

要创建这个键,我们应该是运行 Nagios 的用户,通常是nagios。我们可以使用sudo命令来执行来自 Nagios 主目录/var/spool/nagiosssh-keygen命令。

# sudo -u nagios ssh-keygen -t rsa -b 4096
Generating public/private rsa key pair.
Enter file in which to save the key (/var/spool/nagios/.ssh/id_rsa):
Created directory '/var/spool/nagios/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/spool/nagios/.ssh/id_rsa.
Your public key has been saved in /var/spool/nagios/.ssh/id_rsa.pub.
The key fingerprint is:
df:fc:d6:d2:50:66:65:51:79:d1:f8:56:a1:78:a4:df nagios@nagios.example.com

我们使用-t rsa选项来创建一个 RSA 密钥。我们被提示输入密钥的位置,通常是在nagios用户主目录下的.ssh目录中——在本例中是/var/spool/nagios/.ssh。私钥在id_rsa文件中,公钥在id_rsa.pub文件中。我们没有输入密钥的密码,而是按 Enter 键指定一个空的密码,因为我们需要在没有密码或密码提示的情况下建立连接。

然后,我们需要将公钥id_rsa.pub复制到远程主机,并将其存储在我们要连接的用户的authorized_keys文件中。如果您遵循这个示例,您应该在远程主机上创建一个用户,并为其指定一个密码。在我们的例子中,我们在远程gateway.example.com主机上这样做,如下所示:

gateway$ sudo useradd nagios

我们还需要在远程主机上创建.ssh目录并保护它。

gateway$ sudo mkdir /home/nagios/.ssh
gateway$ sudo chmod 0700 /home/nagios/.ssh

然后,我们可以复制文件,假设jsmith拥有对网关服务器的适当访问权。

nagios$ scp .ssh/id_rsa.pub jsmith@gateway.example.com:/tmp/authorized_keys
nagios$ ssh jsmith@gateway.example.com
gateway$ sudo mv /tmp/authorized_keys /home/nagios/.ssh/authorized_keys && \
sudo chown nagios /home/nagios/.ssh/authorized_keys && \
sudo chmod 0644 /home/nagios/.ssh/authorized_keys

如果成功了,我们现在应该能够从 Nagios 服务器 SSH 到gateway主机,而不需要密码。作为root,发出以下命令(因为 Nagios 用户帐户无法访问 shell,因为其帐户被设置为/sbin/nologin;参见/etc/passwd文件):

nagios# sudo –u nagios –E ssh nagios@gateway
The authenticity of host 'gateway (192.168.0.254)' can't be established.
ECDSA key fingerprint is 2d:94:d5:bd:3e:40:93:fe:d4:9b:eb:6f:93:4d:f3:a1.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'gateway,192.168.0.254' (ECDSA) to the list of known hosts.

这里我们使用sudo作为nagios用户执行ssh命令。–E表示使用 Nagios 用户的环境。这很重要,因为这个命令会将主机密钥写入 Nagios 用户的∼/.ssh/known_hosts文件。我们现在配置 Nagios 使用这个连接来检查服务。check_by_ssh插件还依赖于在远程主机上安装要执行的命令。这个命令通常是 Nagios 插件,因此最简单的方法是在远程主机上安装 Nagios 插件包。在 CentOS 上,我们发布以下内容:

gateway$ sudo yum install nagios-plugins nagios-plugins-load

在 Ubuntu 上,您可以像我们在 Nagios 服务器上一样安装插件,或者您应该能够从 APT 存储库中安装它们。

gateway$ sudo apt-get install nagios-plugins

然后我们可以定义一个命令,使用check_by_ssh插件来监控远程主机上的服务。例如,要监控远程主机上的负载,我们可以使用以下命令:

$ sudo vi /etc/nagios/objects/commands.cfg
# ssh_check_commands
define command{
         command_name       check_load_ssh
         command_line       $USER1$/check_by_ssh -H $HOSTADDRESS$ -l nagios  
-C "/usr/lib64/nagios/plugins/check_load -w $ARG1$ -c $ARG2$"
        }

我们已经将之前的命令定义添加到了/etc/nagios/objects/commands.cfg文件中。我们称我们的命令为check_load_sshcommand_line部分指定我们正在执行check_by_ssh插件并连接到由-H选项指定的主机。

-l选项指定我们想要在远程主机上连接的用户的名称;这里我们使用刚刚创建的nagios用户。

-C指定了我们想要在远程主机上运行的命令。在本例中,我们运行另一个本地安装的 Nagios 插件check_load,并向它传递两个参数作为-w-c ( WARNINGCRITICAL)阈值的值。

Tip

命令可以做更多的事情。使用--help选项运行它,查看它的所有功能。

我们将在/etc/nagios/objects目录中创建一个名为linux-servers-base.cfg的文件。这将用于定义我们的网关主机,并定义我们将用于检查网关服务器负载的服务检查。该文件将类似于清单 18-21 。

define host{
        name            basic-ssh-checks
        use               linux-server
        hostgroups   linux-group
        register        0
        }

define host{
        host_name       gateway
        alias                 gateway.example.com
        use                  basic-ssh-checks
        }

define service{
         use                              local-service
         hostgroup_name         linux-group
         service_description     Current Load
         check_command         check_load_ssh!5.0,4.0,3.0!10.0,6.0,4.0
        }

define hostgroup{
        hostgroup_name  linux-group
        }

Listing 18-21.linux-servers-base.cfg

在清单 18-21 中,我们定义了一个主机模板、一个主机、一个服务和一个主机组。主机模板主要用于将使用该模板的任何主机附加到linux-group主机组。我们在这里使用 hostgroup 将主机模板和服务链接在一起。每个使用模板basic-ssh-checks的主机都将获得当前的负载服务,因为它们共享同一个主机组。

仔细看看我们的服务定义,我们可以看到我们是这样声明的:

define service{
         use                              local-service
         hostgroup_name         linux-group
         service_description     Current Load
         check_command         check_load_ssh!5.0,4.0,3.0!10.0,6.0,4.0
        }

我们的服务名为Current Load,它执行check_load_ssh命令并传递两个参数,这两个参数指定了触发WARNINGCRITICAL状态所需的 1 分钟、5 分钟和 15 分钟间隔内的平均负载。

Note

我们在第十七章讨论了负载。

最后,在清单 18-21 中,我们有了网关服务器的主机定义。这使用了模板basic-ssh-checks,它通过 hostgroup 调用当前的负载服务检查。它还将包含由linux-server模板和generic-host模板提供的所有通知和检查周期定义,因为我们不会覆盖这里或模板中的任何内容。

这是一个如何执行远程监控的简单示例;这并不理想(基于密钥的 SSH 连接可能是一个安全漏洞),但这是最简单的方法。对于更复杂的环境,使用 NRPE 服务器和相应的命令通常是更好的方法。NCAP 和 NRDP 也是可用的,它们需要不同语言的不同插件的组合(这使得它们难以安装和配置)。

Note

与主机一样,我们还没有涵盖服务的所有可用功能。您还可以将服务组合在一起,使它们相互依赖,并使用它们执行各种其他有用的技巧。我们建议您阅读可用的 Nagios 文档和许多其他有用的资源,以获得更多信息。

日志记录和调试

您可以在/var/log/nagios/nagios.log文件中查看 Nagios 的日志。您也可以将这些通知复制到syslog ( rsyslog)守护进程。

如果您不想要这个功能,您可以将您的use_syslog指令设置为如下:

use_syslog=0

当您需要调试 Nagios 时,您将需要编辑/etc/nagios/nagios.cfg文件并编辑以下内容:

debug_level=0

您还记得在 LDAP 日志记录中,我们有通过将二进制数相加来给出不同日志级别的附加日志记录指令吗?纳吉奥斯也是一样。以下是可能的日志记录级别列表:

#          -1 = Everything
#          0 = Nothing
#          1 = Functions
#          2 = Configuration
#          4 = Process information
#          8 = Scheduled events
#          16 = Host/service checks
#          32 = Notifications
#          64 = Event broker
#          128 = External commands
#          256 = Commands
#          512 = Scheduled downtime
#          1024 = Comments
#          2048 = Macros

因此,如果我们希望在日志和外部命令中看到主机和服务检查,我们可以将debug=0更改为debug=144,即 16(主机/服务检查)+ 128(外部命令)。

您将需要重新启动 Nagios 以使其生效,并且调试日志将出现在一个/var/log/nagios/debug.log文件中。

Nagios 插件

您可以从大量插件中选择创建服务和命令来检查它们。您可以在表 18-7 中看到可用插件的部分列表。

表 18-7。

Nagios Plug-Ins

| 插件 | 描述 | | --- | --- | | `check_ntp` | 检查 NTP 服务的状态 | | `check_swap` | 检查您的互换 | | `check_ifstatus` | 检查网络接口的状态 | | `check_tcp` | 检查基于 TCP 的网络服务的状态 | | `check_by_ssh` | 通过 SSH 检查服务的状态 | | `check_imap` | 检查 IMAP 服务的状态 | | `check_clamd` | 检查 ClamAV 守护程序的状态 | | `check_udp` | 检查基于 UDP 的网络服务的状态 | | `check_dig` | 通过`dig`检查 DNS 状态 | | `check_ping` | 通过 ICMP 检查主机的状态 | | `check_simap` | 检查 IMAP 服务的状态 | | `check_nagios` | 检查 Nagios 进程的状态 | | `check_snmp` | 通过 SNMP 进行检查 | | `check_http` | 检查 web 服务器的状态 | | `check_ssh` | 检查`sshd`服务的状态 |

这是可用插件的一小部分选择,但是它应该为您为您的环境创建适当的检查提供一个良好的基础。大多数 Nagios 插件都很简单,不言自明。几乎所有的都提供了--help选项来显示它们的功能和选项。

Nagios 插件包以外的其他插件也是可用的。例如,你可以在 https://exchange.nagios.org/directory/Plugins 找到大量这样的插件。

Note

你也可以在 https://www.nagios.org/downloads/nagios-core-addons/ 找到一些有用的附加组件。

如果需要,您可以开发自己的插件。在 https://assets.nagios.com/downloads/nagioscore/docs/nagioscore/4/en/pluginapi.html 中提供了一些简单的开发指南和示例。

设置 Nagios 控制台

现在您已经了解了如何定义主机和服务,您可以创建自己的主机和服务来补充这两个发行版中提供的示例。一旦创建了这些主机和服务,拥有一个控制台来查看它们是非常有用的。Nagios 附带了一个全功能的基于 web 的控制台,我们将向您展示如何设置它。

Tip

Nagios 控制台的替代产品也是可用的。你可以在 https://exchange.nagios.org/directory/Addons/Frontends-(GUIs-and-CLIs)/Web-Interfaces 看到一个很好的列表。

Nagios 控制台可以在我们在第十一章中演示的 Apache web 服务器内部运行。CentOS 和 Ubuntu 都包含 web 控制台的默认安装。在 CentOS 上,您可以在/etc/httpd/conf.d/nagios.conf找到 Nagios 的 Apache 配置;在 Ubuntu 上,是在/etc/apache2/conf-available/nagios.conf

控制台可以在/nagios的 web 服务目录中找到。如果您的 web 服务器正在运行,浏览到 http://nagios.example.com/nagios/ (用您的 Nagios 主机名替换主机名)应该会显示您的控制台。

Note

您可以使用在第十一章中学到的知识将 Nagios 控制台移动到虚拟主机或其他位置。

控制台身份验证

为了防止有人恶意使用您的 Nagios 控制台,web 服务器有一些基本的认证。Nagios 使用 Apache 的基本 HTTP 认证来保护控制台。当你打开控制台时,会提示你输入用户名和密码,如图 18-9 所示。

A185439_2_En_18_Fig9_HTML.jpg

图 18-9。

Nagios authentication

Apache 基本身份验证是通过指定一个保存 web 服务器用户名和密码的文件来配置的。然后,受基本身份验证保护的网站会查询该文件。默认用户名和密码是 nagiosadmin。

Caution

然而,这是强调基本的基本认证。Apache 基本身份验证使用简单的加密密码,易于拦截和解密。为了确保更好地保护 Nagios 控制台,您应该考虑为它启用 SSL。

让我们在 Nagios Apache 配置中查找密码文件的位置。在 CentOS 的/etc/httpd/conf.d/nagios.conf文件中,我们发现了以下内容:

AuthName "Nagios Access"
AuthType Basic
AuthUserFile /etc/nagios/passwd
<IfModule mod_authz_core.c>
      # Apache 2.4
     <RequireAll>
        Require all granted
        Require valid-user
    </RequireAll>
</IfModule>

在 Ubuntu 上的/etc/apache2/conf-available/nagios.conf文件中,我们有这样的内容:

AuthName "Nagios Access"
AuthType Basic
AuthUserFile /etc/nagios/htpasswd.users
Require valid-user

AuthNameAuthType指令支持基本认证。AuthUserFile指定密码文件的位置,/etc/nagios/passwd在 CentOS 上,/etc/nagios/htpasswd.users在 Ubuntu 上。这是我们需要添加用户的文件。Require valid-user指令表示只有该文件中指定的用户才能登录控制台。

一旦您找到了身份验证文件的位置,您需要创建文件来保存您的用户和密码。您可以使用一个名为htpasswd的命令来完成这项工作。

清单 18-22 展示了这个命令的运行。

$ sudo htpasswd -c /etc/nagios/passwd jsmith
New password:
Re-type new password:
Adding password for user jsmith
Listing 18-22.Using the htpasswd Command

htpasswd命令有两个变量:保存用户名和密码的文件的位置以及用户的用户名。我们还使用一个命令行开关,-c。第一次创建密码文件时使用-c开关。之后,您可以删除这个开关,只需指定要添加的文件和用户。

在清单 18-22 中,我们用名为/etc/nagios/passwd-c选项给文件添加了一个新密码。我们指定要添加的用户jsmith,系统会提示输入密码,然后验证输入的密码。两个密码必须匹配。如果是,该命令将会成功,用户将被添加到指定的密码文件中。

然后,我们可以使用这个用户名和密码登录 Nagios 控制台并显示控制台屏幕,如图 18-10 所示。

A185439_2_En_18_Fig10_HTML.jpg

图 18-10。

The Nagios console

一旦创建了用户和密码,我们就可以登录到控制台,但是我们还需要配置每个用户的访问权限。这是通过编辑位于/etc/nagios目录中的名为cgi.cfg的配置文件来完成的。

Nagios 在控制台上有两种类型的用户:经过身份验证的用户和经过身份验证的联系人。这两种类型的用户都需要用户名和密码来登录控制台。认证用户的访问权限在cgi.cfg配置文件中指定。经过身份验证的联系人是经过身份验证的用户,每个人的用户名都与 Nagios 联系人的名称相匹配。

因此,例如,如果用户名jsmith与联系人的姓名匹配,这是指令contact_name的值,那么这个经过身份验证的用户就成为经过身份验证的联系人。

那么,有什么区别呢?经过身份验证的用户被授予一些查看 web 控制台的一般权限。经过身份验证的联系人被授予查看和操作他们作为联系人的主机和服务的更多权限。

我们来看一下cgi.cfg文件。文件cgi.cfg中的第一个指令叫做use_authentication。它控制是否为 Nagios web 控制台启用身份验证,以及 Nagios 是否使用 web 服务器提供的身份验证凭证。该指令如下所示:

use_authentication=1

默认设置 1 启用身份验证,设置 0 禁用身份验证。控制台上特定功能的授权由cgi.cfg文件中的一系列指令提供,这些指令将逗号分隔的用户列表作为选项。这里有一个例子:

authorized_for_all_services=jsmith,nagiosadmin

authorized_for_all_services指令控制谁可以在控制台上查看服务,我们已经指定用户jsmithnagiosadmin拥有这种访问权限。

表 18-8 包含了可能的授权指令的完整列表,并对每个指令进行了描述。

表 18-8。

Authorization Directives

| 管理的 | 描述 | | --- | --- | | `authorized_for_system_information` | 可以访问 Nagios 进程信息的用户 | | `authorized_for_configuration_information` | 可以查看所有配置信息的用户 | | `authorized_for_system_commands` | 可以通过 web 控制台发出命令的用户 | | `authorized_for_all_services` | 被授权使用所有服务的用户 | | `authorized_for_all_hosts` | 被授权访问所有主机的用户 | | `authorized_for_all_service_commands` | 可以发出与服务相关的外部命令的用户 | | `authorized_for_all_host_commands` | 可以发出与主机相关的外部命令的用户 |

表 18-8 、authorized_for_system_information中的第一个指令提供了对 Nagios 进程和服务器的查看信息的访问,例如进程何时启动以及服务器上设置了什么设置。

第二个指令authorized_for_configuration_information提供了查看监控环境的所有配置信息和对象定义的授权。这包括主机、服务、联系人和命令以及所有其他对象类型的配置。

第三个指令authorized_for_system_commands控制谁有权从 web 控制台启动、停止或重启 Nagios 进程。

接下来的两个指令authorized_for_all_servicesauthorized_for_all_hosts控制哪些用户可以在 web 控制台上查看所有服务和主机信息。请记住,经过身份验证的联系人可以查看他们所联系的主机和服务的相关信息。

表 18-8 、authorized_for_all_service_commandsauthorized_for_all_host_commands中的最后两条指令允许您指定被授权分别向服务和主机发出外部命令的用户。这允许您执行一些操作,例如禁用主机或服务的主动检查,或者启用或禁用主机或服务的通知。

Note

默认情况下,所有授权指令都在cgi.cfg文件中被注释掉。您需要取消对它们的注释,并将任何必需的用户添加到指令中。

如果您想要指定所有用户都可以访问特定功能,请使用*符号。

authorized_for_all_services=*

这个指令设置将为所有经过身份验证的用户提供访问权限,以查看 Nagios 服务器上定义的所有服务的信息。*符号适用于所有授权指令。

正如我们前面提到的,除了授予他们的任何授权之外,作为联系人的用户还可以访问他们作为联系人的主机和服务。对于服务,这种访问包括

  • 查看服务状态
  • 查看服务配置
  • 能够查看服务历史和通知
  • 向服务发出命令的能力(例如,开始和停止检查)

对于主机,这种访问包括

  • 查看主机状态
  • 查看主机配置
  • 能够查看主机历史记录和通知
  • 能够向主机发出命令(例如,开始和停止检查)

因为是特定主机的联系人而有权访问该主机的经过身份验证的联系人也可以访问该主机上的所有服务,就像他们是这些服务的联系人一样。例如,如果您是 server1 主机的经过身份验证的联系人,则可以查看状态、配置、服务历史记录和通知,并向该主机上定义的所有服务发出命令。

控制台功能

Nagios 控制台不仅提供了查看主机和服务的界面,还提供了控制如何监控它们的能力。我们将带您浏览控制台中可用的几个屏幕,但我们建议您查看控制台上可用的视图、图形、列表和控件。

对于您环境的状态摘要,最佳视图是战术监控概览屏幕,您可以在图 18-11 中看到。

A185439_2_En_18_Fig11_HTML.jpg

图 18-11。

The Tactical Monitoring Overview screen

该屏幕显示 Nagios 服务器的当前状态以及您的主机和服务状态的摘要。点击左侧菜单中的战术监控概览链接即可到达。

要更详细地查看您的主机和服务,左侧菜单中的“主机详细信息”和“服务详细信息”链接会显示受监控的主机和服务的完整列表。您可以在图 18-12 中看到服务详细信息屏幕。

A185439_2_En_18_Fig12_HTML.jpg

图 18-12。

The Hosts Detail screen

您可以看到这里列出了两台主机:localhost 或 Nagios 服务器和我们之前配置的网关服务器。您可以看到,我们可以 ping 通两台主机,它们都处于UP状态。在右侧,您可以看到关于 ping 响应的信息;此外,您还可以看到它们上次被检查的时间以及它们处于当前状态的时间。

在图 18-13 中,我们显示了服务详情屏幕。

A185439_2_En_18_Fig13_HTML.jpg

图 18-13。

The Service Detail screen

在图 18-13 中,我们可以看到我们正在主机上检查的服务的状态。您可以看到对当前负载的检查正在我们的网关服务器上进行。HTTP 检查处于WARNING状态,因为httpd服务正在响应,但是结果不是 200 OK 而是 403。在我们得到 200 OK 并让它变绿之前,我们必须修改检查以处理 Nagios web 服务所需的认证。

在这两个屏幕上,您可以单击主机和服务,深入查看它们的状态和配置。

我们将在图 18-14 中展示的最后一个控制台屏幕是进程信息屏幕,它显示 Nagios 进程的状态及其配置。您还可以通过这个屏幕配置各种 Nagios 选项,比如启用和禁用检查和通知。

A185439_2_En_18_Fig14_HTML.jpg

图 18-14。

The Nagios Process Information screen

您可以通过此控制台执行其他操作,如主机和服务报告、检查事件日志以及查看和安排主机和服务的停机时间。

Nagios 故障排除

有很多资源可以帮助您使用 Nagios,包括网站、论坛和书籍,比如本书的作者之一 James Turnbull 的 Pro Nagios 2.0(a press,2006)。你也可能会发现同一作者的《监控的艺术》很有用; https://www.artofmonitoring.com/

您可以找到许多支持选项,包括位于 https://www.nagios.org/support/ 的商业支持、 https://www.nagios.org/documentation/ 的综合文档,以及位于 https://support.nagios.com/knowledge-base/ 的 Nagios 常见问题解答。您还可以通过位于 https://support.nagios.com/forum/index.php 的 Nagios 论坛订阅位于 http://lists.nagios.com/mailman/listinfo 的活跃且有用的邮件列表,这是获得用户帮助的首选方式。

摘要

在本章中,您学习了日志记录和监控,以及如何让它们为您服务。您发现了rsyslog是如何工作的,以及如何充分利用您的rsyslog守护进程。您还学习了如何使用 Kibana 搜索您的日志数据,以及如何通过 Logstash 和 Filebeats 将您的日志数据放入 Elasticsearch。

此外,本章还探讨了 Nagios,一个企业监控工具。您看到了如何安装和配置 Nagios。您还了解了如何配置您的主机和服务,以及监控您的环境所需的支持配置。

在下一章中,我们将讨论配置主机,并向您展示如何使用自动化配置工具管理您的配置。

十九、结构管理

现在,您已经了解了如何为一些系统构建组件,我们将向您展示如何快速地同时构建数千个组件,并为每种类型的系统提供正确的配置!在前 18 章中,我们想向您展示 Linux 系统的每个部分是如何配置的,什么开关适用于什么命令,以及每个开关的结果是什么。所有这些对于了解自动化配置和配置管理现在将为您做些什么至关重要。

在这一章中,我们将会看到配置管理的三个方面。

  • 自动调配和安装新主机
  • 自动管理您的配置,包括文件、用户和软件包
  • 如何以代码形式测试您的配置

我们要研究的第一个过程是自动配置或安装新主机,有时称为引导。在 CentOS 世界中,自举通常被称为 Kickstart(在 Kickstart 工具用于执行它之后)。在 Ubuntu 和 Debian 上,这个过程叫做种子预置。

资源调配是自动向主机安装分发版的一种方式。当我们在第二章第一次看到安装发行版时,我们演示了如何手动安装。您插入了一张 DVD,并按照屏幕提示安装您的发行版。自动配置是一种安装发行版的方式,不会受到配置问题的提示。这使得资源调配变得快速而简单,并且它还具有确保每个构建都相同的优势。

Tip

您可以对服务器主机和桌面主机使用资源调配。这不仅是一种快速构建(或重建)服务器主机的方法,也是一种为用户自动安装桌面的快速方法。

我们要考察的第二个过程是配置管理和自动化。到目前为止,您已经看到您可以积累许多已安装的包、用户、配置文件和其他设置。如果您不采取措施来控制和自动化您的环境,它会很快变得复杂和难以管理。配置管理允许您集中配置、记录并自动化配置。这使您能够管理和控制对环境的更改,并防止意外或恶意的配置更改。

第三是测试您的基础设施,就像测试您的应用程序代码一样。因为配置管理只是代码,这使得测试您的配置更容易,并有助于确保更少的错误进入您的生产服务器。这可以减少管理上的错误,因为在投入生产之前,可以对事物进行测试和审查。这些都可以连接到您的常规 Jenkins 或其他持续集成/持续部署(CI/CD)基础架构中,以实现无缝操作。

供应、配置管理和测试应该在工作流中使用。在您的系统配置过程中,您可以使用 Cobbler 或不同的配置系统来安装操作系统,然后使用 Ansible 或 Puppet 来应用经过测试的配置,作为该过程的一部分。这意味着当您第一次访问控制台时,它已经具有正确的磁盘布局、正确的操作系统、正确的网络配置、正确的用户和软件包、正确的服务配置,并且这些服务已经启动。不仅如此,在那之后你建立的每一个系统也是恰到好处的,即使它由成千上万个这样的系统组成。在您完成 CI 构建后,它会自动为您完成!

准备金提取

我们已经谈了一点什么是供应,但是在不同的发行版之间,如何进行供应会有所不同。我们将解释如何自动配置 CentOS 和 Ubuntu 主机。

资源调配通常是一个两阶段的过程。

  1. 启动您的主机,并向其发送安装所需的文件。
  2. 自动化安装步骤。

该过程从主机启动开始。还记得我们在第五章中告诉你的启动顺序吗?在许多主机上,您可以配置引导序列,在其他地方寻找引导指令;例如,您可以从 DVD 或 u 盘启动。除了这些方法之外,您还可以从网络资源获取引导指令。

这个引导过程背后的技术被称为预引导执行环境(PXE)。网络引导服务器因此被称为 PXE 引导(发音为“pixie boot”)服务器。我们打算构建的主机使用网络查询来查找 PXE 启动服务器,通常是对 DHCP 服务器的网络查询,该服务器可能会提供启动所需的文件,然后使用称为小文件传输协议(TFTP)的文件传输协议将这些文件传输到主机。

Note

你可以在 http://en.wikipedia.org/wiki/Preboot_Execution_Environment 了解更多关于 PXE 的信息。

初始引导完成后,您的配置过程将继续安装预打包版本的发行版,通常会有一系列针对安装时提示您的各种配置问题的自动脚本响应。

Note

我们使用基于网络的配置来创建我们的主机,而不是任何替代品,如 CD 或 DVD。这是因为我们相信基于网络的资源调配是自动构建主机的最简单、最容易、最高效的方式。

使用 CentOS Cobbler 进行配置

CentOS 有各种各样的工具来配置主机,从最基本的自动安装的 Kickstart,到用于主机配置的全功能 GUI 管理工具,如 Cobbler ( http://cobbler.github.io/ )和 Spacewalk ( http://spacewalk.redhat.com/ )。

我们将看看三种工具的组合:

  • Kickstart:基于 Red Hat 的操作系统的安装自动化工具
  • Preseed:一个用于基于 Debian 的操作系统的安装自动化工具
  • Cobbler:提供 PXE 引导服务器的配置服务器

我们将带您完成创建一个 Cobbler 服务器和一个要安装的构建的过程。在本章的稍后部分,我们将向您展示如何配置 Kickstart 来自动化您的配置和安装选项。

安装补鞋匠

让我们从在您的主机上安装 Cobbler 开始。要运行 Cobbler,您需要安装 EPEL 存储库。

$ sudo yum install –y epel-release

然后我们需要安装补鞋匠。

$ sudo yum install –y cobbler

这将安装一些额外的 YUM 实用程序和createrepo包,它们有助于存储库管理。我们还安装了一些 Cobbler 使用的附加包:DHCP 守护进程、TFTP 服务器和 Apache web 服务器。您可能已经安装了这些包,在这种情况下,YUM 将跳过它们。

Note

我们在第十章中讨论 DHCP,在第十一章中讨论 Apache。

一旦所有东西都安装好了,我们需要在引导时启用cobblerd(守护进程)并启动它。

$ sudo systemctl enable cobblerd httpd
$ sudo systemctl start cobblerd
$ sudo systemctl start httpd

Cobbler 需要访问 Apache 服务器才能启动。此外,我们需要确保cobblerd服务可以访问httpd服务器端口。SELinux 默认情况下会阻止这一点,因此我们需要发出以下命令:

$ sudo setsebool -P httpd_can_network_connect true

Cobbler 有一些特定的 SELinux 设置,您可以使用以下命令查看它们:

$ sudo getsebool -a|grep cobbler
cobbler_anon_write --> off
cobbler_can_network_connect --> off
cobbler_use_cifs --> off
cobbler_use_nfs --> off
httpd_can_network_connect_cobbler --> off
httpd_serve_cobbler_files --> off

我们将启用以下 SELinux 布尔值:

$ sudo setsebool -P httpd_serve_cobbler_files on
$ sudo setsebool -P httpd_can_network_connect_cobbler  on

配置补鞋匠

在安装了所需的包之后,您需要配置 Cobbler。Cobbler 带有一个方便的检查功能,告诉你需要做些什么来配置它。要查看需要做什么,请运行以下命令:

The following are potential configuration items that you may want to change:

1 : The 'server' field in /etc/cobbler/settings must ... by all machines that will use it.
2 : For PXE to be functional, the 'next_server' field ... the IP of the boot server on the PXE network.
3 : SELinux is enabled. Please review the following ... https://github.com/cobbler/cobbler/wiki/Selinux
4 : change 'disable' to 'no' in /etc/xinetd.d/tftp
5 : some network boot-loaders are missing from /var/lib/cobbler/loaders, ...is the easiest way to resolve these requirements.
6 : debmirror package is not installed, it will be required to manage debian deployments and repositories
7 : ksvalidator was not found, install pykickstart
8 : The default password used by the sample templates ...  'your-password-here'" to generate new one
9 : fencing tools were not found, and are required to use ... cman or fence-agents to use them

Restart cobblerd and then run 'cobbler sync' to apply changes.

您可以看到,要让 Cobbler 运行起来,您需要做几件事情。让我们逐一解决这些问题。

首先,配置/etc/cobbler/settings文件。您需要更新该文件中的两个字段:servernext_server。您需要用主机的 IP 地址替换现有的值(通常是 127.0.0.1 ),这样 PXE 启动的主机才能找到您的补丁包主机。在我们的例子中,我们指定以下内容:

server 192.168.0.1
next_server 192.168.0.1

要更新 Cobbler 的配置,您可以运行以下命令:

$ sudo cobbler sync

Note

您需要在任何时候更改/etc/cobbler/settings文件时运行$ sudo cobbler sync命令。常见的错误包括在settings文件中的选项后留下尾随空格。请确保删除文件中任何多余的空格。

你还需要配置一个 DHCP 服务器(就像我们在第十章中介绍的那样)。这里您有两个选择:您可以让 Cobbler 管理您现有的 DHCP 服务器,或者您可以告诉您现有的 DHCP 服务器指向 Cobbler。

在您运行了cobbler sync并重新运行cobbler check之后,您会注意到需要检查的未完成事项列表已经减少了。我们现在要安装补鞋匠加载器和debmirror二进制文件。

$ sudo cobbler get-loaders

对于debmirror,你需要从 Debian 下载文件,解压,并复制到一个公共位置(或者,你可以使用 FPM,就像我们在第九章中展示的那样,创建一个包,以可重复的方式为你做这件事!).

我们至少需要安装这些 Perl 模块:

$ sudo yum install -y perl-LockFile-Simple perl-IO-Zlib perl-Digest-MD5 perl-Net-INET6Glue perl-LWP-Protocol-https

接下来,我们将下载并安装debmirror包,解压缩它,并将其放在/usr/local/bin目录中。

$ curl -s http://archive.ubuntu.com/ubuntu/pool/universe/d/debmirror/debmirror_2.25ubuntu2.tar.xz -o debmirror_2.25.tar.xz
$ tar xf debmirror_2.25.tar.xz && sudo cp debmirror-2.25ubuntu2/debmirror /usr/local/bin/

为了测试我们已经为debmirror正确安装了所有东西,运行debmirror --help并确保您没有得到任何 Perl 模块错误。

最后,我们将更改主机上的默认 root 密码。首先,您可以使用python3创建一个安全的 SHA-512 密码,如下所示:

python3 -c 'import crypt; print(crypt.crypt("yourpasswordhere", crypt.mksalt(crypt.METHOD_SHA512)))'
$6$KnsQG.tEetSCSmid$HpqUNyEk1UPkt9Dc9MPcwPY...guKOGdUeNXoA7.ugUBGGaDIk8RY8FRYVOwzmsM.u01

然后需要更新/etc/cobbler/settings文件中的default_password_ crypted :设置。每次换车后记得跑cobbler sync

Note

Python 3 默认不安装在 CentOS 上,但可以在 Ubuntu 上获得。前面生成密码的脚本可以在任何已经安装了 Python 3 的主机上运行,并且可以跨主机复制。

现在,当我们运行$ sudo cobbler check时,列表只包含三个项目,我们不需要处理它们。

补鞋匠管理您的 DHCP

如果您想让 Cobbler 管理您的 DHCP 服务器,那么您需要在/etc/cobbler/settings文件中启用另一个选项。

manage_dhcp: 1

您还需要更新一个模板文件,Cobbler 将使用它来配置您的 DHCP 服务器,/etc/cobbler/dhcp.template。清单 19-1 展示了这个文件的一个例子。

# ******************************************************************
# Cobbler managed dhcpd.conf file
#
# generated from cobbler dhcp.conf template ($date)
# Do NOT make changes to /etc/dhcpd.conf. Instead, make your changes
# in /etc/cobbler/dhcp.template, as /etc/dhcpd.conf will be
# overwritten.
#
# ******************************************************************

ddns-update-style interim;

allow booting;
allow bootp;

ignore client-updates;
set vendorclass = option vendor-class-identifier;

option pxe-system-type code 93 = unsigned integer 16;

key dynamic-update-key {
        algorithm hmac-sha256;
        secret "RZqM/JutbhgHiBR8ICG0LDyN+9c1LpNU83ycuU9LPaY=";
}

zone 0.168.192.in-addr.arpa. {
    key dynamic-update-key;
    primary 192.168.0.1;
}

zone example.com. {
    key dynamic-update-key;
   primary 192.168.0.1;
}

subnet 192.168.0.0 netmask

255.255.255.0 {
    option routers 192.168.0.254;
    option domain-name "example.com";
    option domain-name-servers 192.168.0.1;
    option broadcast-address 192.168.0.255;

    next-server $next_server;
    filename "/pxelinux.0";
    group "static" {
       use-host-decl-names on;
        host au-mel-rhel-1 {
             hardware ethernet 00:16:3E:15:3C:C2;
           fixed-address au-mel-rhel-1.example.com;
       }
    }
    pool {
        range 192.168.0.101 192.168.0.150;
        deny unknown clients;
   }
   pool {
      range 192.168.0.151 192.168.0.200;
      allow unknown clients;
     default-lease-time 7200;
      max-lease-time 21600;
   }
}

Listing 19-1.The /etc/cobbler/dhcp.template File

如果您有一个带有配置的现有 DHCP 服务器,您应该更新此模板以反映该配置。你可以看到我们已经调整了清单 19-1 中的模板,以反映我们在第十章中使用的 DHCP 配置。我们添加了两个设置。

allow booting;
allow bootp;

这两个选项告诉 DHCP 服务器响应来自请求网络启动的主机的查询。

清单 19-1 中需要注意的另外两个重要设置是next-server和文件名配置选项。next-server选项设置为$next_server。这个值将被我们刚刚在/etc/cobbler/settings文件的next_server选项中配置的 IP 地址所取代。这告诉我们的 DHCP 服务器将请求网络引导的主机路由到哪里。

filename选项被设置为/pxelinux.0,这是 PXE 启动的主机在启动过程中应该查找的启动文件的名称。我们将很快建立这个文件。

现在,更改这些文件后,您需要运行以下命令:

$ sudo cobbler sync

Caution

如果您有一个现有的 DHCP 服务器,这个模板将通过覆盖/etc/dhcpd.conf配置文件来覆盖它的配置。只有在您确定知道自己在做什么的情况下才这样做,并在运行命令之前复制一份现有的/etc/dhcpd.conf文件。

补鞋匠没有管理你的 DHCP

如果你不想让 Cobbler 管理你的 DHCP,那么你只需要调整你现有的 DHCP 配置文件,/ etc/dhcpd.conf,添加next-serverfilename选项。让我们用这个选项更新我们在第九章中创建的配置的相关部分,如清单 19-2 所示。

allow booting;
allow bootp;

subnet 192.168.0.0 netmask 255.255.255.0 {
     option routers 192.168.0.254;
     option domain-name "example.com";
     option domain-name-servers 192.168.0.1;
     option broadcast-address 192.168.0.255;
     filename "/pxelinux.0";
     next-server 192.168.0.1;
     group "static" {
        use-host-decl-names on;
         host au-mel-rhel-1 {
             hardware ethernet 00:16:3E:15:3C:C2;
           fixed-address au-mel-rhel-1.example.com;
          }
      }
      pool {
        range 192.168.0.101 192.168.0.150;
        deny unknown clients;
     }
     pool {
      range 192.168.0.151 192.168.0.200;
      allow unknown clients;
      default-lease-time 7200;
      max-lease-time 21600;
    }
}

Listing 19-2.Existing dhcpd.conf Configuration File

您可以看到,我们在 DHCP 部分的开头添加了两个选项。

allow booting;
allow bootp;

这两个选项告诉 DHCP 服务器响应来自启动客户端的查询。

我们还在子网定义中添加了next-server选项。

next-server 192.168.0.1

next-server选项告诉 DHCP 将请求 PXE 网络启动的主机发送到哪里。我们需要指定我们的服务器的 IP 地址。

最后,我们添加了设置为/pxelinux.0filename选项,这是 PXE 启动的主机在启动过程中应该查找的启动文件的名称。我们将很快建立这个文件。

Tip

配置完 DHCP 服务器后,您需要重新启动 Cobbler 服务器以应用新的配置。

配置 TFTP

一旦守护程序启动,您需要启用您的 TFTP 服务器将您的引导文件发送到要安装的主机。为此,您可以编辑/etc/xinet.d/tftp文件来启用 TFTP 服务器。在这个文件中,找到这一行:

disable = yes

把它改成这样:

disable = no

接下来,像这样启用 TFTP 服务器:

$ sudo systemctl enable tftp
$ sudo systemctl start tftp

您需要通过打开一些必需的端口,例如 69、80、25150 和 25151,来确保您的主机可以通过您的防火墙连接到 Cobbler 服务器,方法是创建如下所示的firewalld规则:

$ sudo firewall-cmd --zone=public --add-service=tftp --permanent
$ sudo firewall-cmd --zone=public --add-service=httpd –permanent
$ sudo firewall-cmd --zone=public --add-port=25150:25151/tcp –permanent

这些规则允许 192.168.0.0/24 子网中的任何主机通过适当的端口访问引导服务器。你可以在第七章找到更多关于防火墙规则的信息。

使用鞋匠

一旦您配置了 Cobbler,您就可以开始使用它了。Cobbler 允许您指定一个您希望用来构建主机的发行版,导入该发行版的文件,然后创建一个配置文件。然后,您可以使用这个发行版和概要文件构建主机。

我们已经将我们的 ISO 文件分别挂载到了/mnt/centos/mnt/ubuntu。这是这样做的:

$ sudo mount –o loop /path/to/downloaded.iso /path/to/mountpoint

让我们从使用import命令创建第一个概要文件开始。

$ sudo cobbler import --path=/mnt/centos --name=CentOS7 --arch=x86_64
task started: 2016-12-22_055922_import
task started (id=Media import, time=Thu Dec 22 05:59:22 2016)
Found a candidate signature: breed=redhat, version=rhel6
Found a candidate signature: breed=redhat, version=rhel7
Found a matching signature: breed=redhat, version=rhel7
Adding distros from path /var/www/cobbler/ks_mirror/CentOS7-x86_64:
creating new distro: CentOS7-x86_64
trying symlink: /var/www/cobbler/ks_mirror/CentOS7-x86_64 -> /var/www/cobbler/links/CentOS7-x86_64
creating new profile: CentOS7-x86_64
associating repos
checking for rsync repo(s)
checking for rhn repo(s)
checking for yum repo(s)
starting descent into /var/www/cobbler/ks_mirror/CentOS7-x86_64 for CentOS7-x86_64
processing repo at : /var/www/cobbler/ks_mirror/CentOS7-x86_64
need to process repo/comps: /var/www/cobbler/ks_mirror/CentOS7-x86_64
looking for /var/www/cobbler/ks_mirror/CentOS7-x86_64/repodata/*comps*.xml
Keeping repodata as-is :/var/www/cobbler/ks_mirror/CentOS7-x86_64/repodata
*** TASK COMPLETE ***

我们也将导入我们的 Ubuntu ISO。

$ sudo cobbler import --path=/mnt/ubuntu --name Ubuntu-16.04 --breed=ubuntu --os-version=xenial

您发出带有import选项的cobbler命令。--path选项指定了您想要导入的发行版的来源——在我们的例子中是/mnt/ubuntu/mnt/centos--name是您希望为发行版指定的任何名称,您可以添加--breed--os-version来帮助 import 命令找到与您的发行版匹配的正确签名。

Note

如果在导入时出现错误,请确保运行$ sudo cobbler signature update并重试。在这里了解更多签名: http://cobbler.github.io/manuals/2.8.0/3/2/3_-_Distro_Signatures.html

您还可以与在线存储库同步。这里有一个例子:

$ sudo cobbler reposync
task started: 2016-12-22_063019_reposync
task started (id=Reposync, time=Thu Dec 22 06:30:19 2016)
hello, reposync
run, reposync, run!
running: /usr/bin/debmirror --nocleanup --verbose --ignore-release-gpg --method=http --host=archive.ubuntu.com --root=/ubuntu --dist=xenial,xenial-updates,xenial-security --section=main,universe /var/www/cobbler/repo_mirror/Ubuntu-16.04-x86_64 --nosource -a amd64

这将与在线存储库同步,在这种情况下,使用我们之前安装的debmirror二进制文件来同步我们的 Ubuntu Xenial 版本。

Tip

您的主机上需要足够的磁盘空间来复制您想要保留的任何发行版。托管您自己的存储库同步将大大加快您的部署,并减少在线网络下载。

Cobbler 将运行导入过程,然后返回到提示符处。根据主机的性能(如果通过网络导入,还取决于连接速度),这可能需要一些时间。

您可以通过以下命令查看发行版安装:

$ sudo cobbler distro list
   CentOS7-x86_64
   Ubuntu-16.04-x86_64

导入也将创建两个概要文件;我们可以使用以下命令来查看它们:

$ sudo cobbler profile list
   CentOS7-x86_64
   Ubuntu-16.04-x86_64

在您创建了您的发行版和概要文件之后,您可以使用report选项在 Cobbler 中看到完整的细节,如清单 19-3 所示。

$ sudo cobbler report
distros:
==========
Name                           : CentOS7-x86_64
Architecture                   : x86_64
TFTP Boot Files                : {}
Breed                          : redhat
Comment                        :
Fetchable Files                : {}
Initrd                         : /var/www/cobbler/ks_mirror/CentOS7-x86_img/initrd.img
Kernel                         : /var/www/cobbler/ks_mirror/CentOS7-x86_img/vmlinuz
Kernel Options                 : {}
Kernel Options (Post Install)  : {}
Kickstart Metadata             : {'tree': 'http://@@http_server@@/cblr/links/CentOS7-x86_64'}
Management Classes             : []
OS Version                     : rhel7
Owners                         : ['admin']
Red Hat Management Key         : <<inherit>>
Red Hat Management Server      : <<inherit>>
Template Files                 : {}

Name                           : Ubuntu-16.04-x86_64
Architecture                   : x86_64
TFTP Boot Files                : {}
Breed                          : ubuntu
Comment                        :
Fetchable Files                : {}
Initrd                         : /var/www/cobbler/ks_mirror/Ubuntu-16.04/install/netboot/ubuntu-installer/amd64/initrd.gz
Kernel                         : /var/www/cobbler/ks_mirror/Ubuntu-16.04/install/netboot/ubuntu-installer/amd64/linux
Kernel Options                 : {}
Kernel Options (Post Install)  : {}
Kickstart Metadata             : {'tree': 'http://@@http_server@@/cblr/links/Ubuntu-16.04-x86_64'}
Management Classes             : []
OS Version                     : xenial
Owners                         : ['admin']
Red Hat Management Key         : <<inherit>>
Red Hat Management Server      : <<inherit>>
Template Files                 : {}

Listing 19-3.A Cobbler Report

该选项显示所有的发行版,它们的概要文件当前被导入到 Cobbler 中。

Note

您可能会看到通过导入一个发行版而创建的多个发行版和配置文件。

清单 19-3 显示了我们的普通CentOS7-x86_64发行版和我们创建的概要文件CentOS7-x86_64。清单 19-3 中的大部分信息对我们来说并不重要;我们将很快使用这些配置文件创建一个新系统。

Note

通过查看cobbler命令的man页面或访问 http://cobbler.github.io/manuals/2.8.0/ 的文档,您可以在您的个人资料中看到其他可以编辑的选项。

用鞋匠建造一个主机

现在您已经添加了一个概要文件和一个发行版,您可以引导一个主机并安装您的发行版。选择要构建的主机(或虚拟机)并重新启动它。您的主机可能会自动搜索网络上的引导设备,但您更有可能需要调整其 BIOS 设置来调整引导顺序。要从 Cobbler 引导,您需要指定您的主机首先从网络引导。

我们在与 Cobbler 服务器相同的主机专用适配器接口上创建了一个 VirtualBox 主机。我们在第三章中创建了 VirtualBox 主机,并创建了一个 8Gb 硬盘的基本主机。在系统设置中,我们将为引导设备选择网络。

在图 19-1 中,我们正在选择网络引导选项。当我们完成时,我们将回到这里并把它设置到硬盘上。我们现在像平常一样启动主机。

A185439_2_En_19_Fig1_HTML.jpg

图 19-1。

Setting Network Boot

当您的主机启动时,它将从网络请求一个 IP 地址,并从您的 DHCP 服务器获得一个答案,如图 19-2 所示。

A185439_2_En_19_Fig2_HTML.jpg

图 19-2。

Requesting DHCP address

您的主机将引导至引导菜单。你可以在图 19-3 中看到这个菜单的例子。

A185439_2_En_19_Fig3_HTML.jpg

图 19-3。

The Cobbler menu

从该菜单中,您可以选择您想要安装的配置文件(如CentOS7-x86_64)。如果您没有选择要安装的配置文件,Cobbler 将自动启动菜单上的第一项(local),这将继续本地主机上的引导过程。

Note

如果这台主机上没有安装操作系统,local引导过程显然会失败。

我们将选择CentOS7-x86_64,这将自动在我们的主机上安装 CentOS。如果我们选择Ubuntu-16.04-x86_64,我们会安装 Ubuntu 数字 19-4 到 19-6 讲述了这个故事。

A185439_2_En_19_Fig6_HTML.jpg

图 19-6。

Selecting Boot from Hard Disk

A185439_2_En_19_Fig5_HTML.jpg

图 19-5。

Installing Ubuntu

A185439_2_En_19_Fig4_HTML.jpg

图 19-4。

Installing CentOS

请记住,当我们在 VirtualBox 上安装完主机后,我们需要打开主机电源,并需要更改引导设备,然后再次启动主机。

A185439_2_En_19_Fig8_HTML.jpg

图 19-8。

Ubuntu installed

A185439_2_En_19_Fig7_HTML.jpg

图 19-7。

CentOS installed

我们选择了一个配置文件,然后该配置文件使用相关 Kickstart 或预置文件中包含的说明开始安装过程。如果您正在观看安装过程,您将看到安装屏幕的进度—所有这些都不需要您输入来继续或选择选项。

使用 Cobbler,您还可以为特定主机指定配置选项。您不需要这样做,但是如果您对主机有一个特定的角色,并且想要指定一个特定的配置文件或 Kickstart 配置,这将非常有用。

为此,您可以使用system命令将主机添加到 Cobbler 中,通过它们的 MAC 或 IP 地址来识别它们。

$ sudo cobbler system add --name=web1.example.com --mac=08:00:27:66:EF:C2
--profile=CentOS7-x86_64 --interface eth1

这里,我们添加了一个名为web1.example.com的系统,它具有指定的 MAC 地址。

Note

您通常可以在网络引导过程中看到您的 MAC 地址,或者您经常可以在网卡的标签上找到它。或者,你也可以像在 VirtualBox 中那样看到你的虚拟界面。

新主机使用CentOS7-x86_64配置文件。到目前为止,它与我们以前构建的主机没有什么不同。如果具有适当 MAC 地址的主机连接到我们的 Cobbler 主机,那么 Cobbler 将使用这些配置设置来配置主机。

如果您需要更改构建主机的方式,可以创建新的配置文件。您的新配置文件可以继承其他配置文件的设置,这些配置文件被视为其父配置文件。我们将创建一个名为centos-base的概要文件,它将从父CentOS7-x86_64继承发行版和其他设置。

$ sudo cobbler profile add --name centos-base --parent CentOS7-x86_64

这就是我们如何为不同的主机组使用不同的公共 Kickstart 或预置文件。Kickstart 和预置文件可能有不同的磁盘配置或为特定配置文件定制的包列表。要添加一个特定的 Kickstart 或 Preseed 文件,首先要复制并修改任何一个现有的文件,将其添加到/var/lib/cobbler/kickstarts目录中。然后,您可以使用--kickstart选项将其添加到配置文件中。

您可以使用listreport选项列出已配置的主机。

$ sudo cobbler system list
web1.example.com

使用report选项可以看到gateway.example.com系统定义的完整列表。

$ sudo cobbler system report –name=web1.example.com

我们还可以使用remove命令删除一个系统。

$ sudo cobbler system remove --name=web1.example.com

Note

你可以在cobbler命令的man页面上阅读更多的补鞋匠功能。

Cobbler Web 界面

Cobbler 还有一个简单的 web 界面,您可以使用它来管理它的一些选项。在这个阶段,它非常简单,命令行界面的功能更加全面,但是如果您想要实现它,它是可用的。您可以在 http://cobbler.github.io/manuals/2.8.0/5_-_Web_Interface.html 找到说明。

故障排除补鞋匠

您可以通过监控主机上的元素(包括日志文件)和使用网络监控工具(如tcpdumptshark命令)来排除网络引导过程的故障。

您可以通过查看/var/log/messages日志文件来监控 DHCP 进程的输出。补鞋匠还记录到/var/log/cobbler/cobbler.log文件和包含在kicklogsyslog目录中的文件,它们也在/var/log/cobbler下。

您还可以监控引导主机和引导服务器之间的网络流量。为此,您可以使用各种网络监控工具。

$ sudo tcpdump port tftp

Cobbler 有一个 wiki 页面,包含位于 http://cobbler.github.io/manuals 的文档。该文档包括一些在 http://cobbler.github.io/manuals/2.8.0/7_-_Troubleshooting.html 进行故障排除的有用提示。补鞋匠社区也有邮件列表和其他支持渠道,你可以在这里看到: http://cobbler.github.io/community.html

安装

在 CentOS 上,用于自动安装主机的语言称为 Kickstart。在 Ubuntu 上,它被称为 Preseed。为了简单起见,因为它是一种更容易使用的语言,我们将向您展示如何使用 Kickstart 来自动安装 CentOS 和 Ubuntu。在 Ubuntu 不支持的地方,我们将向您展示如何使用 Preseed 来配置它。

Kickstart 配置文件包含自动化安装过程所需的说明。对于大多数安装选项来说,这是一个简单的脚本化过程,但可以扩展到一些复杂的配置。

您可以在 http://pykickstart.readthedocs.io/en/latest/kickstart-docs.html 找到 Kickstart 的最新详细文档。

您可以在 https://wiki.debian.org/DebianInstaller/Preseed 找到关于 Preseed 及其指令的文档。在本节的后面,我们将使用其中的一些指令。

您已经看到了如何使用 Cobbler 为您的配置环境指定 Kickstart 文件。让我们从清单 19-4 中一个简单的 Kickstart 文件的一些内容开始。

install
# System authorization information
auth --enableshadow  --passalgo sha512
# System bootloader configuration
bootloader --location=mbr
# Partition clearing information
clearpart --all --initlabel
# Use text mode install
text
Listing 19-4.A Kickstart File

清单 19-4 显示了以install选项开始的配置指令列表,它通过执行安装来规定安装过程的行为。

然后您可以看到带有选项的配置指令,例如auth --enableshadow  --passalgo sha512,它告诉 Kickstart 如何回答特定的安装问题。auth语句在这里有值--enableshadow--passalgo sha512,它们分别启用影子密码和指定密码散列必须使用 SHA512 密码算法。

接下来的选项bootloader的值为--location=mbr,告诉 Kickstart 将引导加载程序安装到 MBR 中。接下来是指令clearpart,它清除主机上的所有分区,并为它们创建默认标签。最后一个选项text,指定我们应该使用基于文本的安装,而不是 GUI。

Tip

您可以使用 Kickstart 来升级和安装主机。如果您有现有的主机,您可以从新版本的操作系统进行网络引导,并使用 Kickstart 文件来编写脚本和升级。

指令太多,无法一一讨论,因此我们在表 19-1 中向您展示了必须指定的指令以及一些您可能会发现有用的其他主要指令。

表 19-1。

Required Kickstart Directives

| 管理的 | 描述 | | --- | --- | | `auth` | 配置身份验证。 | | `bootloader` | 配置引导加载程序。 | | `keyboard` | 配置键盘类型。 | | `lang` | 配置主机上的语言。 | | `part` | 配置分区。这是安装所必需的,但升级时不需要。 | | `rootpw` | 指定 root 用户的密码。 | | `timezone` | 指定主机所在的时区。 |

您还可以在 http://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#chapter-2-kickstart-commands-in-fedora 找到有用的可用指令列表和解释。

Tip

如果您在 CentOS 上,您可以在/root/anaconda-ks.cfg文件中看到一个示例 Kickstart 文件,它是在您安装主机时创建的。这将向您展示您当前的主机是如何构建的,并且可以用作构建类似主机的示例。

安装源

您已经看到了指定安装行为的installupgrade指令。您还可以指定安装文件的来源。

url --url http://192.168.0.1/centos/

对于 Cobbler,我们定义了一个变量来指定安装源的位置。

url --url=$tree

指令也可以用来指定一个 FTP 服务器。

url --url ftp://jsmith:passsword@192.168.0.1/centos

当从本地安装的 CD 或 DVD 和硬盘安装到本地分区时,我们可以指定一些替代源,包括cdrom

harddrive --dir=/centos --partition=/installsource

键盘、语言和时区

下一个片段我们将向您展示如何配置我们的键盘、语言和时区。

# System keyboard
keyboard us
# System language
lang en_AU
# System timezone
timezone Australia/Melbourne

在这里,我们将us指定为keyboard指令的值,以表示美国键盘。我们已经指定我们的语言为en_AU(英语澳大利亚语),我们的时区为Australia/ Melbourne

管理用户

您还可以使用 Kickstart rootpw指令设置root用户的密码。

rootpw --iscrypted $6$D7CxLkSBeC9.k.k3$S8G9s3/Y5LJ4dio....S5GS78p2laxALxaJ.lCN9tzKB1zIpYz38Fs9/

rootpw指令是所有 Kickstart 文件的必需 Kickstart 选项。当指定了--iscrtypted选项时,root 用户的密码可以是纯文本值,也可以是加密值。您可以锁定 root 用户帐户,这样就没有人可以使用--lock选项登录了(如果指定了--lock,那么您也不需要密码)。

您可以使用user指令在 Kickstart 上创建一个新用户。

user jsmith --password password

前面的代码创建了一个名为jsmith的新用户,密码为password。通过添加--iscrypted选项,您可以添加一个带有加密密码的用户。我们将像使用rootpw指令一样创建我们的加密密码。

防火墙和网络

在 CentOS 上,您可以配置主机的初始防火墙和网络配置。

# Firewall configuration
firewall --enabled --http --ssh --smtp
# SELinux configuration
selinux --enabled

这里,我们使用firewall选项启用了防火墙,并允许通过 HTTP、SSH 和 SMTP 进行访问。(您可以使用--disabled选项禁用防火墙。)我们还启用了 SELinux——如果您真的需要,可以使用selinux --disabled选项禁用它。

您可以使用 Kickstart 配置网络连接,如下所示:

# Network information
network --bootproto=static --device=eth0 --gateway=192.168.0.254
--ip=192.168.0.1 --nameserver=192.168.0.1 --netmask=255.255.255.0 --onboot=on

您还可以使用network选项为一个或多个接口指定网络配置。您可以看到我们已经设置了配置eth0接口所需的各种选项。您也可以指定 DHCP,例如:

network --bootproto=dhcp --device=eth0 --onboot=on

在带有 Cobbler 的 CentOS 上,如果您使用特定的主机(使用cobbler system命令创建的主机),您可以将特定的网络配置值传递给 Cobbler 系统配置。

$ sudo cobbler system edit --name=gateway.example.com --mac=00:0C:29:3B:22:46

--profile=centos-base --interface=eth0 --ip=192.168.0.1 --subnet=255.255.255.0 --

gateway=192.168.0.254 --hostname=gateway --bootproto=static

这里,我们指定了edit命令来更改现有的 Cobbler 定义的系统,并将网络配置值传递给我们的系统。这将为接口eth0定义一个静态网络配置。我们使用--static=1选项指定引导协议是静态的;我们将为 DHCP 配置指定--static=0。使用--interface=eth0选项指定要配置的接口。

然后,在我们的 Kickstart 文件中,我们指定了 Cobbler 所谓的代码片段,而不是指定网络线路。

$SNIPPET('network_config')

当构建您的主机时,Cobbler 将您指定的网络配置传递给这个代码片段和它包含的一个模板。然后这被转换成适当的network行,您的主机就配置好了。

Tip

这个片段是对 Cobbler 的片段系统的简单使用。您可以使用代码片段定义各种其他的动作,您可以在/var/lib/cobbler/snippets目录中看到这些动作的选择,includ使用我们在本节中使用的network_config代码片段。你可以在sample.ks文件中看到如何使用这些代码片段,也可以在 http://cobbler.github.io/manuals/2.8.0/3/5_-_Kickstart_Templating.htmlhttp://cobbler.github.io/manuals/2.8.0/3/6_-_Snippets.html 找到如何使用模板和代码片段的说明。

磁盘和分区

您已经看到了 Kickstart 用来配置磁盘和分区的一个选项clearpart,它清除主机上的分区。然后,您可以使用 part 选项在主机上配置分区,如下所示:

# Partition clearing information
clearpart --all --initlabel
part /boot --asprimary --bytes-per-inode=4096 --fstype="ext4" --size=150
part / --asprimary --bytes-per-inode=4096 --fstype="ext4" --size=4000
part swap --bytes-per-inode=4096 --fstype="swap" --size=512

Note

在 CentOS 上,您可以通过指定autopart选项来创建类似的配置。autopart选项自动创建三个分区。第一个分区是 1GB 或更大的根(/)分区,第二个是交换分区,第三个是适合该架构的引导分区。一个或多个默认分区大小可以用part指令重新定义。

您可以使用part选项来创建特定的分区。在前面的代码中,我们首先创建了两个分区,/boot/,都是 ext4。我们为/boot分区指定了 150MB 的大小,为/或根分区指定了 4000MB(或 4GB)的大小。我们还创建了一个大小为 512MB 的交换分区。

使用 CentOS 上的 Kickstart,我们可以创建软件 RAID 配置,例如:

part raid.01 --asprimary --bytes-per-inode=4096 --fstype="raid" --grow --ondisk=sda
--size=1
part raid.02 --asprimary --bytes-per-inode=4096 --fstype="raid" --grow --ondisk=sdb
--size=1
part raid.03 --asprimary --bytes-per-inode=4096 --fstype="raid" --grow --ondisk=sdc
--size=1
part raid.04 --asprimary --bytes-per-inode=4096 --fstype="raid" --grow --ondisk=sdd
--size=1
part raid.05 --asprimary --bytes-per-inode=4096 --fstype="raid" --grow --ondisk=sde
--size=1
raid / --bytes-per-inode=4096 --device=md0 --fstype="ext4" --level=5 raid.01 raid.02
raid.03 raid.04 raid.05

我们指定了五个 RAID 磁盘,每个磁盘使用其全部内容,如--grow选项所示。要使用的相应磁盘由--ondisk选项指定,这里的范围从sdasde。最后,我们使用raid选项将md0 RAID 磁盘指定为/或根分区。

您还可以在自动安装过程中使用 LVM 创建分区。例如,在 CentOS 上,您可以像这样创建它们:

part /boot --fstype ext4 --size=150
part swap --size=1024
part pv1 --size=1 --grow
volgroup vg_root pv1
logvol / --vgname=vg_root --size=81920 --name=lv_root

在前面的示例中,我们在磁盘的剩余部分创建了一个 150MB 的引导分区、一个 1GB 的交换分区和一个名为pv1的物理卷,使用--grow选项来填充磁盘的剩余部分。然后,我们创建了一个名为vg_root的 80GB LVM 逻辑卷。

包装管理

使用 Kickstart,您可以指定要安装的软件包。在 CentOS 上,您可以指定一个以%packages开头的部分,然后是您想要安装的软件包组或软件包的列表。

%packages
@ Administration Tools
@ Server Configuration Tools
@ System Tools
@ Text-based Internet
dhcp

我们指定一个 at 符号(@)、一个空格,然后指定我们想要安装的软件包组的名称,例如管理工具。我们也可以通过列出名字来指定单个的包,不带符号和空格,就像我们在这里对dhcp包所做的那样。

Ubuntu 使用类似的设置。

%packages
@ kubuntu-desktop
dhcp-client

这里我们已经安装了 Kubuntu-Desktop 包组和dhcp-client包。详见 http://pykickstart.readthedocs.io/en/latest/kickstart-docs.html#chapter-7-package-selection

Note

我们将在第八章中讨论包组。

安装前和安装后

您可以在 Kickstart 安装您的主机之前和之后运行脚本。预运行脚本在解析 Kickstart 配置文件之后、配置主机之前运行。任何预运行脚本都在 Kickstart 文件的末尾指定,并以行%pre为前缀。

每个%post%pre段必须有一个对应的%end

完成配置并安装主机后,将触发运行后脚本。它们也应该在 Kickstart 文件的末尾指定,并以一个%post行作为前缀。这是我们的sample.ks配置文件中的%post部分:

%post
$SNIPPET('post_install_kernel_options')
$SNIPPET('post_install_network_config')
%end

这里我们指定了两个配置内核和网络选项的 postrun Cobbler 代码片段。

这个运行后脚本空间对于运行任何所需的安装应用程序或脚本非常有用。

Preseed 是 Debian 安装自动化工具。它比 Kickstart 更不透明,但它执行相同的自动安装功能。

为了提供一些上下文,您看到的每一行d-i都是对 Debian 安装程序的调用。该文件的格式与传递给debconf-set-selection命令的指令相同。它采取以下形式:

<owner> <question name> <question type> <value>

因此,为您的系统设置语言环境类似于下面这样:

d-i debian-installer/locale string en

主人是debian-installer,问题是debian-installer/locale,类型是string,数值是en,或者英文。

安装源

通过live-installer问题选择 Cobbler 作为初始安装源。

d-i live-installer/net-image string http://$http_server/cobbler/links/$distro_name/install/filesystem.squashfs

在安装过程中,您可以设置您的 apt 库。这将你的 apt 源指向了 Ubuntu 镜像。

d-i mirror/country string manual
d-i mirror/http/hostname string archive.ubuntu.com
d-i mirror/http/directory string /ubuntu
d-i mirror/http/proxy string

您可以使用这些设置选择不同的 apt 池,以从 backports 等获得可用的包:

#d-i apt-setup/restricted boolean true
#d-i apt-setup/universe boolean true
#d-i apt-setup/backports boolean true

您可以取消注释您想要的池。

键盘、语言和时区

设置键盘和语言可能是一个耗时的过程,但安装程序不会。您可以预先选择以下选项:

d-i debian-installer/locale string en
d-i debian-installer/country string AU
d-i debian-installer/locale string en_AU.UTF-8
d-i debian-installer/language string en
d-i console-setup/ask_detect boolean false
d-i console-setup/layoutcode string us
d-i console-setup/variantcode string
d-i keyboard-configuration/layoutcode string us
d-i clock-setup/ntp boolean true
d-i clock-setup/ntp-server string ntp.ubuntu.com
d-i time/zone string UTC
d-i clock-setup/utc boolean true

这里我们设置了地区和国家,我们禁用了键盘提示来询问我们的选择,并回答了所有关于键盘布局的问题。然后,我们为我们的时钟启用 NTP,并将它们的时间设置为 UTC。

管理用户

有了 Cobbler 和 Preseed,我们可以启用 root 用户,这是 Ubuntu 通常做不到的。

d-i passwd/root-login boolean true
d-i passwd/root-password-crypted password $default_password_crypted
d-i passwd/make-user boolean false

因此,当您构建主机时,您需要以 root 用户身份登录。为了保持您熟悉的设置,您可以添加一个用户ubuntu,或者在安装结束时添加一个用户SNIPPET

#d-i passwd/user-fullname string Ubuntu User
#d-i passwd/username string ubuntu

防火墙和网络

你可以用 Preseed 以任何适合你的方式建立网络,但是你不能做任何防火墙配置。如果需要,您还可以在 Cobbler 的安装后脚本中添加防火墙配置。

# IPv4 example
#d-i netcfg/get_ipaddress string 192.168.1.42
#d-i netcfg/get_netmask string 255.255.255.0
#d-i netcfg/get_gateway string 192.168.1.1
#d-i netcfg/get_nameservers string 192.168.1.1
#d-i netcfg/confirm_static boolean true

您可以在网络设置中设定静态 IP 或允许 DHCP。

磁盘和分区

目前,磁盘分区可能相当复杂。这里我们只是创建一个简单的 LVM 分区设置:

d-i partman-auto/method string lvm
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-lvm/confirm boolean true
d-i partman-auto/choose_recipe select atomic
d-i partman/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/confirm boolean true

包装管理

使用 Preseed,您可以指定要安装的软件包。对于分组包,您可以使用tasksel(或者任务选择所有者)来多选一个包组——就像ubuntu-desktop一样。

tasksel tasksel/first multiselect ubuntu-desktop

对于单个包,您可以只使用以下内容:

d-i pkgsel/include string openssh-server

如果您愿意,可以选择多个包。

d-i pkgsel/include string openssh-server build-essential

马斯河

我们将向您简要介绍 MAAS 工具——或者 Ubuntu 的金属即服务。你可能还记得,当我们第一次创建我们的 Ubuntu 安装时,我们在初始闪屏中看到了“安装 MAAS 机架或区域服务器”。这是一个 Ubuntu 服务,用于管理裸机或物理计算机,就像管理虚拟计算机一样。该服务能够安装裸机 Ubuntu 和 CentOS 服务器,以及 RHEL 和 Windows。

MAAS 服务致力于在您的金属服务器上提供云功能。这个工具的技术基础并不新。它使用 PXE、小文件传输协议(TFTP)和 Debian Preseed 来构建节点。它旨在管理物理数据中心,并且具有很强的可扩展性,可以管理数千个节点。

一旦 MAAS 启动了主机,您就可以使用 Juju(一个 Ubuntu 供应框架)来供应它。这可以将软件、用户帐户和其他资源安装到您的服务器上,或者您可以使用其他配置服务,如 Puppet 或 Ansible。你可以在这里了解更多: https://maas.ubuntu.com/docs/juju-quick-start.html

你可以在这里看到它是如何工作的: https://maas.io/how-it-works 。这里有个快速入门教程: https://maas.io/get-started 。你甚至可以在这里试着运行一个流浪测试套件: https://github.com/battlemidget/vagrant-maas-in-a-box

结构管理

我们已经在本书中向您展示了配置 Linux 服务器包括相当多的任务,例如,配置主机;创建用户;以及管理应用程序、守护程序和服务。这些任务可以在一个主机的生命周期中重复多次,以便添加新的配置或补救由于错误、熵或开发而改变的配置。它们也很耗时,而且通常不能有效利用时间和精力。

对此问题通常的第一反应是尝试自动化任务,这导致了定制脚本和应用程序的开发。以这种特殊方式开发的脚本很少被发布、记录或重用,所以相同的工具被反复开发。这些脚本也往往不能很好地扩展,并且经常需要频繁维护。

配置管理工具可以高效地自动化这些任务,并为您的主机提供一致且可重复的生命周期。我们将向您展示如何使用这些工具之一 Puppet 来自动化您的配置。

介绍木偶

Puppet ( https://puppet.com/ ,前身为 Puppetlabs)是一个开源的配置管理工具,在大多数安装中依赖于客户机/服务器部署模型。Puppet 可以作为开源或商业企业产品使用。企业产品结合了多个 Puppet 开源产品,并提供了一个企业仪表板来协调和配置您的资源以及商业支持协议。开源版本没有花哨的企业功能,是社区支持的,并使用 Apache 2.0 许可证进行许可。我们将向您概述 Puppet 以及如何使用它来配置您的环境和主机。

Note

在撰写本文时,Puppet world 正在从 3.x 版向 4.x 版转变。3.x 版将于 2016 年底停产,因此您应该使用 4.x 版。4 版与 3 版在本质上是不同的。撰写本文时的最新版本是 v4.8。

当使用 Puppet 时,安装并配置称为 Puppet masters 的中央服务器。然后,客户端软件安装在您要管理的目标主机(称为节点)上。当一个节点连接到傀儡主节点时,该节点的配置清单在主节点上编译,发送到该节点,然后由傀儡代理应用到该节点上。

Tip

还有另一种方法将清单应用于节点,这种方法被称为无主控 puppet,或称为puppet apply。它不依赖于傀儡主架构和证书签名。

为了提供客户机/服务器连接,Puppet 使用在 TCP 端口 8140 上的 HTTPS 上运行的 RESTful web 服务。在 4.x 版中,Puppet 服务器是一个基于 JVM 的应用程序。为了提供安全性,使用内部生成的自签名证书对会话进行加密和验证。每个傀儡客户机生成一个自签名证书,然后在傀儡主机上进行验证和授权。

此后,每个客户端都会联系服务器,以确认其配置是最新的。默认情况下,每 30 分钟联系一次服务器,但这个时间间隔是可定制的。如果有新的配置可用,或者节点上的配置已经更改,则重新编译配置清单,然后应用到客户端。这样,如果客户机上的任何现有配置发生了变化,它将根据服务器的预期配置进行修正。任何活动的结果都会被记录下来并传送给主机。

Puppet 工作原理的核心是一种语言,它允许您清晰地表达您的配置。这被称为 Puppet 声明脚本语言(Puppet DSL)。您的配置组件被组织到称为资源的实体中,这些实体又可以组合到集合中。资源包括以下内容:

  • 类型
  • 标题
  • 属性

清单 19-5 显示了一个简单资源的例子。

file { '/etc/passwd':
       owner => 'root',
       group => 'root',
       mode => '0644',
}
Listing 19-5.A Puppet Resource

清单 19-5 中的资源是一个file类型的资源。file资源配置被管理文件的属性。在这种情况下,它配置了/etc/passwd文件,并将其所有者和组设置为 root 用户,将其权限设置为 0644。

Tip

有一个编写木偶清单的风格指南,你应该尽早熟悉它。这里可以看到: https://docs.puppet.com/guides/style_guide.html

资源类型告诉 Puppet 您正在管理哪种资源——例如,userfile类型分别用于管理节点上的用户和文件操作。默认情况下,Puppet 附带了许多资源类型,包括管理文件、服务、包、cron作业和存储库等类型。

Tip

https://docs.puppet.com/puppet/4.8/type.html 可以看到内置资源类型的完整列表。你也可以用 Ruby 编程语言开发你自己的类型。

资源的标题将它标识为 Puppet。每个标题由资源类型的名称(如file)和资源的名称(如/etc/passwd)组成。这两个值组合在一起构成资源的标题(例如,File['/etc/passwd'])。

Note

在资源标题中,资源类型的名称大写(File),资源的名称封装在块括号和单引号(['/etc/passwd'])中。

这里的名字/etc/passwd也告诉 Puppet 要管理的文件的路径。Puppet 管理的每个资源必须是惟一的——例如,只能有一个名为File['/etc/passwd']的资源。

资源的属性描述被管理的配置细节,例如定义特定用户和该用户的属性(例如,用户所属的组或用户主目录的位置)。在清单 19-5 中,我们管理文件的所有者、组和模式(或权限)属性。每个属性用=>符号与它的值分开,并用逗号结束。

Puppet 还使用了集合的概念,它允许您将许多资源组合在一起。例如,Apache 这样的应用程序由一个包、一个服务和许多配置文件组成。在 Puppet 中,这些组件中的每一个都被表示为一个资源(或多个资源),然后被收集并应用到一个节点。在本章的后面,我们将会看到其中的一些集合类型。

安装木偶

让我们从安装木偶开始。对于 Puppet,客户机和服务器的安装略有不同,我们将向您展示如何分别安装。

傀儡主人需要至少 3Gb 的内存,以便 JVM 和操作系统有足够的空间。另外,TCP 端口 8140 需要在傀儡主机上打开。

CentOS 装置

使用 Puppet 的最新包,当我们安装服务器时,默认情况下会安装所有需要的包。在 CentOS 上,在服务器和客户机上,您都需要为基于 Red Hat 的机器添加 Puppet 存储库。

$ sudo yum install -y https://yum.puppetlabs.com/puppetlabs-release-pc1-el-7.noarch.rpm

有几个组件组成了 Puppet 生态系统,比如 Facter,这是一个用于发现节点上的系统事实的工具。系统事实包括操作系统、IP 地址和任何自定义事实。另一个是 Hiera,这是一个用于声明 Puppet 配置数据的键/值查找数据库。最后,还有 MCollective,一个用于管理 Puppet 节点的编排工具。

在主服务器上,您安装puppetserver,这将安装facterhieraagent以及来自傀儡库的其他所需的包。

$ sudo yum install puppetserver

在 Puppet 节点或客户机上,我们可以自行安装puppet-agent包,它将包含或需要它运行所需的所有内容。

$ sudo yum install –y puppet-agent

和前面一样,这当然需要首先安装 YUM 存储库。

Ubuntu 安装

在 Ubuntu 上,我们再次安装 apt 库,然后在主服务器上安装puppetserver,这将关闭所有必要的 Puppet 组件,如 Facter、Hiera 和代理。

在服务器或主服务器上,我们需要这样做:

$ wget https://apt.puppetlabs.com/puppetlabs-release-pc1-xenial.deb -O xenial.deb && sudo dpkg -i xenial.deb
$ sudo apt-get update
$ sudo apt-get install -y puppetserver

On the client, you need the following:
$ sudo apt-get install –y puppet-agent

现在,您的系统上已经安装了所有必需的组件。

配置木偶

我们将通过设置我们的傀儡师来开始配置傀儡。我们对傀儡主人的配置将位于/etc/puppetlabs目录下。

正如我们已经说过的,Puppet 有几个组成生态系统的组件。Puppet 的主体服务器配置文件位于/etc/puppetlabs/puppetserver/conf.d/puppetserver.conf。您很少需要编辑这个文件,但是它有各种路径设置和使用的 TLS 密码。

另一个主配置文件位于代理和主服务器上。它位于/etc/puppetlabs/puppet/puppet.conf。您可以在[service]部分定义全局配置设置或特定业务设置,如[main][master][agent][user]

配置主服务器

通常情况下,主人的/etc/puppetlabs/puppet/puppet.conf看起来像这样:

[main]
certname = puppetmaster.example.com
server = puppet
environment = production
runinterval = 30m
strict_variables = true

[master]
dns_alt_names = puppetmaster,puppet,puppet.example.com

[main]部分包含主服务器和代理服务器的默认值。这里我们确定certname,它将是我们在启动时生成的 TLS 证书中指定的通用名称。这与dns_alt_names设置有关,它提供了代理可以用来验证傀儡主人的替代 DNS 名称。server = puppet是这个傀儡代理将尝试连接的傀儡主人的名字。你可以看到这与dns_alt_names中的一个相匹配。

当傀儡代理连接到傀儡主服务器时,它们可以指定应该用来收集其目录的环境。这通常用于测试您的傀儡代码的版本控制系统(VCS)分支,或者可以用于为多个组织多宿主您的傀儡主人。

不要犯这样的错误,即创建反映您的主机可能扮演的角色的环境。也就是说,不要有用于开发、UAT、试运行和生产的环境,并为这些环境分配相关的主机。将你所有的主机视为产品,并处理这些主机在傀儡清单中可能扮演的不同角色和角色,会更容易。这通常会导致您的 Puppet 代码在系统和 VCS 分支之间产生可怕的分歧。无论如何都要创建一个环境来测试您的 Puppet 代码,但是要尽快将它投入到产品分支中。使用 Hiera 和“角色和概要”模式来实现这一点。参见 https://docs.puppet.com/hiera/3.2/https://docs.puppet.com/pe/2016.4/r_n_p_full_example.html

runinterval是每次木偶运行之间的时间间隔,也就是说,代理将调用木偶大师的目录。strict_variables意味着当引用未知变量时解析会产生错误。

[master]部分,我们定义了傀儡主服务器的设置。除了dns_alt_names值,我们不打算在这里设置任何东西。可能属于这里的设置是codedir,Puppet 将在这里寻找我们将要编写的 Puppet 代码或清单。然而,我们将采用默认值,这意味着我们的codedir将是/etc/puppetlabs/code

在这里,您将设置使用 PuppetDB 的报告设置和配置。使用 PuppetDB 是一个很好的主意,因为它允许您从多个节点收集数据,从而进行复杂的编目,但这超出了本练习的范围。更多详情请看这里: https://docs.puppet.com/puppetdb/

我们建议您为您的傀儡主机(例如puppet.example.com)创建一个 DNS CNAME,或者将其添加到您的/etc/hosts文件中。

# /etc/hosts
127.0.0.1 localhost
192.168.0.1 au-mel-ubuntu-1 puppet puppetmaster puppet.example.com puppetmaster.example.com

Note

我们将在第十章中讲述如何创建CNAME s。

编写清单

我们将把我们的实际配置存储在目录/etc/puppetlabs/code/environments/production下的目录manifests中。在该目录中,您很可能会看到以下目录和文件:

ll /etc/puppetlabs/code/environments/production/
-rw-r--r-- 1 root root  879 Dec  5 23:53 environment.conf
drwxr-xr-x 2 root root 4096 Dec  5 23:53 hieradata/
drwxr-xr-x 2 root root 4096 Dec  5 23:53 manifests/
drwxr-xr-x 2 root root 4096 Dec  5 23:53 modules/

Puppet 服务器将读取一个environment.conf文件,以确定这个环境需要的具体设置。hieradata目录将包含 Hiera 数据库,用于变量查找。清单目录是木偶寻找site.pp文件的地方。这个文件用于创建我们的配置的根目录。目录是我们安装木偶模块的地方。模块是执行一组特定任务的木偶文件的集合。我们稍后会更详细地解释它们。

manifests目录需要包含一个名为site.pp的文件,它是我们配置的根目录。让我们现在就创建它。

$ sudo touch /etc/puppetlabs/code/environments/production/manifests/site.pp

Note

包含配置的清单文件的后缀为.pp

我们还将在我们的production目录的基础上再创建三个目录,第一个是site,在那个目录中还有profilerole

$ sudo mkdir -p /etc/puppetlabs/code/environments/production/site/{profile,role}

site目录实际上是另一个模块,像role一样,将用于包含这个特定环境的特定角色和配置文件信息。我们将需要编辑我们的environment.conf文件,让木偶看到这些。我们需要在modulepath指令中添加以下内容:

$ sudo vi /etc/puppetlabs/code/environments/production/environment.conf
modulepath = ./sites:./modules:$basemodulepath

Modules

在 Puppet 中,您可以创建包含资源、类和定义集合的*.pp文件,但是 Puppet 有另一种更复杂的集合类型,称为模块。您可以将类、定义、模板、文件和资源的集合组合到模块中。模块是配置的可移植集合;例如,一个模块可能包含配置 Postfix 或 Apache 所需的所有资源。

您可以在此阅读如何使用模块:

另外,在 https://forge.puppet.com/ 站点上有大量用户贡献的模块。其他人几乎肯定已经编写了一个模块来配置您可能需要的服务或应用程序,在许多情况下,您可以下载并重用这些模块,而不必自己编写模块。要安装这些模块,可以使用 r10k ( https://docs.puppet.com/pe/latest/r10k.html )来帮助管理模块的安装。

您也可以使用puppet module命令来管理模块。puppet module命令将为您创建骨架模块,从傀儡锻造厂搜索现有模块,安装模块,并为您管理模块的生命周期。例如,您可以像这样从 Forge 安装一个模块:

$ puppet module search apache
NAME                         DESCRIPTION                     AUTHOR            KEYWORDS
puppetlabs-apache           Installs, configures, ...       @puppetlabs       apache web
example42-apache             Puppet module for apache        @example42        apache
herculesteam-augeasproviders_apache  Augeas-based apache ty... @herculesteam     types apache
...
$ sudo /opt/puppetlabs/bin/puppet module install puppetlabs-apache
Notice: Preparing to install into /etc/puppetlabs/code/environments/production/sites ...
Notice: Created target directory /etc/puppetlabs/code/environments/production/sites
Notice: Downloading from https://forgeapi.puppet.com ...
Notice: Installing -- do not interrupt ...
/etc/puppetlabs/code/environments/production/sites
└─┬ puppetlabs-apache (v1.11.0)
    ├── puppetlabs-concat (v2.2.0)
     └── puppetlabs-stdlib (v4.14.0)

你可以看到,我们首先搜索了一个apache模块,找到了 900 多个具有关键字apache的模块。然后我们安装了puppetlabs-apache模块。然后,您可以阅读这个模块的文档并创建您的 Apache 服务( https://forge.puppet.com/puppetlabs/apache )。

你可以在 https://docs.puppet.com/puppet/4.8/modules_fundamentals.html#writing-modules 阅读更多关于如何创建你自己的模块以及它们是如何构成的。

我们现在将创建一个节点定义,以便我们可以将每个节点与一个配置文件相匹配。配置文件可以被描述为它是哪种主机。相比之下,角色就像是主人提供的服务。比如我们可以有一个web_server的角色。我们可以有一个 UAT 的侧面图web_server。也就是说,它是一个 web 服务器,拥有 UAT 人需要的东西,可能使它与我们的生产 web 服务器略有不同——不同的数据库后端配置,不同的身份验证要求,等等——但本质上它仍然有一个 web 服务器的角色,可以在其上部署我们的应用程序。

这可能需要一点时间来理解,对于如何将这个结构实现到你的木偶清单中,没有完美的答案。各个公司将根据最适合其公司的实践来进行不同的实现。有关角色和档案模式的更多讨论,请参见 https://www.youtube.com/watch?v=RYMNmfM6UHw

我们将通过定义我们的site.pp文件来继续我们的配置,如清单 19-6 所示。

sudo vi /etc/puppetlabs/code/environments/production/manifests/site.pp
node /^web\d+\.example\.com$/ {
  include profile::web_server
}
Listing 19-6.The site.pp File

清单 19-6 中的节点声明是傀儡主人在节点“登记”时知道如何处理它们的方式这里我们使用了正则表达式,但是您也可以使用如下计划字符串:

node 'web1.example.com' { ... }
node 'web1.example.com', 'web2.example.com' { ... }

在我们的声明中,我们说任何使用 TLS 证书名称登记的节点都以web ( ^ web)开始,后面有一个或多个数字(^web \d+),然后是域名(\.example\.com),仅此而已(\.com $)。然后我们为这个节点提供profile::web_server概要文件。

当没有匹配时,有一个特殊的节点声明,即default节点定义。

node default { ... }

您可以使用这个默认的节点声明来通知人们某个节点没有定义,或者应用一组默认的安全限制。如果没有default节点,也没有节点的匹配定义,Puppet 将无法为该节点编译清单。

Note

你可以在这里找到更多关于定义节点的信息: https://docs.puppet.com/puppet/latest/lang_node_definitions.html

用 RAL 启动傀儡服务器

这里有一个巧妙的技巧。您可以使用 Puppet resource 命令来启动您的 Puppet master 服务器(puppetserver)。木偶资源命令允许你直接与木偶资源抽象层(RAL)交互。RAL 是 Puppet 交互和管理系统的方式。使用 Puppet 资源,我们将启动puppetserver,并使它像这样在引导时启动:

sudo /opt/puppetlabs/bin/puppet resource service puppetserver ensure=running enable=true

我们还没有描述 Puppet 是如何管理资源的,您很快就会对这个命令的作用有更深的理解,但是简单地说,它的作用如下:

  • 启动服务(ensure=running)
  • 进行必要的更改以在引导时启动服务(enable=true)
  • 使用任何底层系统来启动服务(service puppetserver)

在我们的例子中,它将使用systemctl命令(startenable)。您可以在 CentOS、Ubuntu 或任何其他支持的系统上运行这个命令,它将启动puppetserver进程。如果你在 Mac 上启动 Apache 服务,它会使用launchctl——它使用任何适合它运行的系统的东西。

我们可以看到它是否已经开始使用正常的systemctl命令,我们可以在这里看到日志:

$ sudo journalctl -u puppetserver -f
-- Logs begin at Tue 2016-12-20 09:24:04 UTC. --
Dec 21 09:25:29 puppetmaster systemd[1]: Starting puppetserver Service...
Dec 21 09:25:29 puppetmaster puppetserver[4877]: OpenJDK 64-Bit Server VM warning: ignoring option MaxPermSize=256m; support was removed in 8.0
Dec 21 09:26:30 puppetmaster systemd[1]: Started puppetserver Service.

此外,可以在/var/log/puppetlabs中找到正在运行的服务器日志。

我们可以用它来关注下一部分的任务。

连接我们的第一个客户

一旦配置并启动了 Puppet master,就可以配置并启动您的第一个客户机了。在客户端,正如我们前面提到的,您需要使用您的发行版的包管理系统安装puppet-agent包。我们将在web.example.com主机上安装一个客户端,然后连接到我们的puppet.example.com主机。这个安装还将创建一个带有puppet.conf配置文件的/etc/puppetlabs/puppet/目录。

当连接我们的客户机时,我们首先希望从命令行运行 Puppet 客户机,而不是作为服务运行。这将允许我们在连接时看到发生了什么。傀儡客户端二进制文件被称为puppet agent,您可以在清单 19-7 中看到一个到主客户端的连接。

web$ sudo /opt/puppetlabs/bin/puppet agent --server puppet.example.com --test --waitforcert 15
Info: Creating a new SSL key for web1.example.com
Info: Caching certificate for ca
Info: csr_attributes file loading from /etc/puppetlabs/puppet/csr_attributes.yaml
Info: Creating a new SSL certificate request for web1.example.com
Info: Certificate Request fingerprint (SHA256): 3E:D9:02:08:98:79:FB:8C:40:65:75:4E:15:7C:51:89:4C:14:25:90:16:2A:DB:29:D6:3C:F4:82:64:7E:C8:62
Info: Caching certificate for ca
Notice: Did not receive certificate
Listing 19-7.Puppet Client Connection to the Puppet Master

在清单 19-7 中,我们执行了带有许多选项的puppet agent二进制文件。第一个选项,- server,指定要连接的傀儡主机的名称或地址。我们也可以在客户端的/etc/puppetlabs/puppet/puppet.conf配置文件的主要部分指定这一点。

 [main]
server=puppet.example.com

--test选项在前台运行 Puppet 客户机,并阻止它作为守护进程运行,这是默认行为。--test通常是错误的,人们认为它只是“测试”木偶的运行,并没有破坏性。这个被错误命名的选项实际上是onetimeverboseno-daemonizeno-usecacheonfailuredetailed-exitcodesno-splayshow_diffno-use_cached_catalog的元参数。如果你想要一个非破坏性的木偶运行,你需要指定--noop

Tip

--debug选项提供对故障排除有用的进一步输出。

在清单 19-7 中,您可以看到我们的连接的输出。客户端创建了一个证书签名请求和一个私钥来保护我们的连接。Puppet 使用 TLS 证书来验证主服务器和客户端之间的连接。客户端现在正在等待主服务器签署其证书并启用连接。此时,客户端仍在运行并等待签名的证书。它将继续每 15 秒检查一次签名证书,直到它收到一个或被取消(使用 Ctrl+C 等)。

Note

您可以像我们一样使用- waitforcert选项来更改 Puppet 客户机等待的时间。您可以指定不等待证书的时间(以秒为单位)或 0。

现在在 master 上,我们需要签署证书。我们使用puppet cert命令来完成这项工作。

puppet$ sudo /opt/puppetlabs/puppet/bin/puppet cert list
  "web1.example.com" (SHA256) 3E:D9:02:08:98:79:FB:8C:40:65:75:4E:15:7C:51:89:4C:14:25:90:16:2A:DB:29:D6:3C:F4:82:64:7E:C8:62

--list选项显示所有等待签名的证书。然后,我们可以使用sign选项签署我们的证书。您可以使用证书指纹来验证您签署的是正确的证书。

puppet$ sudo /opt/puppetlabs/puppet/bin/puppet cert sign web1.example.com
Signing Certificate Request for:
  "web1.example.com" (SHA256) 3E:D9:02:08:98:79:FB:8C:40:65:75:4E:15:7C:51:89:4C:14:25:90:16:2A:DB:29:D6:3C:F4:82:64:7E:C8:62
Notice: Signed certificate request for web1.example.com
Notice: Removing file Puppet::SSL::CertificateRequest web1.example.com at '/etc/puppetlabs/puppet/ssl/ca/requests/web1.example.com.pem'

Note

您可以使用puppet cert sign --all命令签署所有等待证书。

在客户端上,我们签署了证书后,应该会看到以下条目:

Notice: Did not receive certificate
Info: Caching certificate for web1.example.com
Info: Caching certificate_revocation_list for ca
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Evaluation Error: Error while evaluating a Function Call, Could not find class ::profile::web_server for web1.example.com at /etc/puppetlabs/code/environments/production/manifests/site.pp:2:3 on node web1.example.com
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run

客户端现在已经通过了主服务器的身份验证,但是我们有一个错误,没有应用任何东西。

Error: Could not retrieve catalog from remote server: Error 500 on SERVER: Server Error: Evaluation Error: Error while evaluating a Function Call, Could not find class ::profile::web_server for web1.example.com at /etc/puppetlabs/code/environments/production/manifests/site.pp:2:3 on node web1.example.com
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run

错误相当详细。我们预料到了这个错误,所以让我们看看它告诉我们什么。它说在/etc/puppetlabs/code/environments/production/manifests/site.pp的第 2 行我们找不到web1.example.com::profile::web_server类。查看site.pp文件的第 2 行,我们看到以下内容:

include profile::web_server

我们已经告诉它包含一个我们还没有创建的概要文件。我们必须创造它。让我们接下来做那件事。

Tip

在错误中,您是否注意到::profile::之前?这表明错误在 Puppet 的顶层范围内。

创建我们的第一个配置

现在我们的客户端已经连接上了,我们将为它添加一些配置。在 Puppet master 上,我们需要添加我们的profile模块,并添加一些配置以应用到我们的客户端。

模块应具有以下结构:

modulename/
                    |- manifests
                                      |- init.pp
                    |- files
                    |- templates

至少,你需要manifests目录;您可能会看到有更多目录的其他模块,比如speclib,分别用于测试和模块代码。

我们已经在/etc/puppetlabs/code/environments/production/sites中创建了profile模块目录。让我们在profile目录下创建一个manifests文件。在这个目录中,我们将创建一个名为init.pp的文件。该文件在技术上不是必需的,并且不会保存任何配置。您可以在清单 19-8 中看到这个文件的内容。

class profile {

}

Listing 19-8.Our init.pp Configuration

它只是一个空的傀儡文件。这是木偶文件的标准格式。class声明是一个傀儡类型。profile是一个标题。那么该类类型期望该类中的木偶代码在花括号{...}之间。木偶语言基础见 https://docs.puppet.com/puppet/4.8/lang_resources.html

现在,在配置文件目录中,我们将创建我们的web_server.pp文件。傀儡主机自动加载器,寻找和加载傀儡文件的机制,当它看到include profile::web_server时,将首先在它的模块路径中寻找目录概要文件,然后在其中的manifests目录中寻找。然后,它将加载所有的*.p p 文件,直到它找到像这里声明的类profile::webserver { ... }指令:

$ sudo vi /etc/puppetlabs/code/environments/production/sites/profile/manifests/web_server.pp
class profile::web_server {

}

在这个文件中,在{...}之间我们将声明一个资源。这个资源叫做notify,一个资源声明如下:

  notify { "profile::webserver – loaded": }

notify是资源类型。"profile::webserver – loaded":就是标题。它所做的是在 Puppet 运行的运行时日志中打印一条消息。与所有资源类型一样,您可以添加属性,notify可以获得一个namemessagewithpath属性。所以,你可以这样写:

 notify { "profile::webserver – loaded":
    name      => 'a name',
    message => 'this is our message'
}

请随意尝试。你会在下面的链接中找到所有的资源及其属性: https://docs.puppet.com/puppet/latest/type.html 。保存该文件,让我们再次在web1.example.com上运行傀儡代理。

$ sudo /opt/puppetlabs/bin/puppet agent --server puppet.example.com --test
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for web1.example.com
Info: Applying configuration version '1482320904'
Notice: profile::webserver – loaded
Notice: /Stage[main]/Profile::Web_server/Notify[profile::webserver – loaded]/message: defined 'message' as 'profile::webserver – loaded'
Notice: Applied catalog in 0.01 seconds

在这里,您可以看到我们的notify的输出。Notify 可能是调试 Puppet 代码的一种便捷方式,因为您可以打印出变量之类的东西来查看您的代码是否如您所期望的那样工作。

我们现在让我们的profile::web_server模块工作了。现在,在这个简介中,我们将很快加入角色apache_web。这可能开始听起来有点像俄罗斯娃娃,但这个想法是你从服务器的类型中抽象出指定逻辑。现在,让我们继续配置我们的角色。

创建以下目录:/etc/puppetlabs/code/environments/production/sites/role/manifests。在那里我们创建了清单 19-9 中的文件。

$ sudo vi /etc/puppetlabs/code/environments/production/sites/role/manifests/apache_web.pp
class role::apache_web (
  String $vhost_name,
  String $vhost_doc_root,
  Numeric $vhost_port
) {
  include apache

  apache::vhost { $vhost_name:
    port      => $vhost_port,
    docroot => $vhost_doc_root,
  }
}

Listing 19-9.The role::apache_web Class

这个类有更多的内容。在这里我们声明了类role::apache_web,并且我们提供了一个参数列表,我们期望这个类在被使用时被提供。Puppet 中的类参数既可以在创建类时声明,也可以在为节点编译该类时从键/值数据库(如 Hiera)中查找。它们被声明在类名之后,括号内用逗号分隔。在这里阅读更多关于职业参数: https://docs.puppet.com/puppet/4.8/lang_classes.html

在 Puppet 中,您可以定义传入变量的数据类型,如果它们不是正确的类型,Puppet 将会出错。我们使用了字符串和数字,但是其他的,像布尔、数组和散列在这里描述: https://docs.puppet.com/puppet/4.8/lang_data_type.html

在清单 9-11 中,我们已经包含了apache模块。这是一个模块,我们现在要使用前面描述的 Puppet module 命令来安装,它将是puppetlab/apache模块。查看文档,我们可以通过给定名称、端口和doc_root来声明一个虚拟主机,并且我们已经使用了提供给该类的参数。

apache::vhost就是所谓的已定义资源类型。定义的资源类型是可以多次评估的普通 Puppet 代码块。你仍然不能有多个同名的已定义资源,所以你不能有两个声明apache::vhost {' www.example.com ': },但是你可以在同一个清单中声明apache::vhost {' www.example.com ': }apache::vhost {'api.example.com': }就可以了。

现在让我们在傀儡服务器上安装puppetlabs-apache模块。

$ sudo /opt/puppetlabs/puppet/bin/puppet module install puppetlabs-apache
Notice: Preparing to install into /etc/puppetlabs/code/environments/production/sites ...
Notice: Created target directory /etc/puppetlabs/code/environments/production/sites
Notice: Downloading from https://forgeapi.puppet.com ...
Notice: Installing -- do not interrupt ...
/etc/puppetlabs/code/environments/production/sites
└─┬ puppetlabs-apache (v1.11.0)
  ├── puppetlabs-concat (v2.2.0)
  └── puppetlabs-stdlib (v4.14.0)

这已经安装了puppetlabs-apache模块以及所需的concatstdlib模块,并将它们安装到/etc/puppetlabs/code/environments/production/sites目录中。你可以在这里看到那个模块的文档: https://forge.puppet.com/puppetlabs/apache

现在让我们再次进入profile::web_server类,添加我们想要安装的虚拟主机。

sudo vi /etc/puppetlabs/code/environments/production/sites/profile/manifests/web_server.pp
class profile::web_server {
  class { role::apache_web:
    vhost_name      => 'www.example.com',
    vhost_doc_root => '/var/www/html',
    vhost_port         => 80
  }
}

我们现在已经调用了类role::apache_ web,并提供了我们在role::apache_web类中需要的vhost_参数。在这个概要文件中,你也可以包含一些将站点部署到/var/www/html的傀儡代码。

应用我们的第一个配置

现在让我们在web1上运行我们的傀儡代理,看看会发生什么。

Caution

下一个动作是破坏性的,将从 Puppet 节点中清除任何现有的 Apache 配置。

$ sudo /opt/puppetlabs/bin/puppet agent --server puppet.example.com --test
Info: Using configured environment 'production'
Info: Retrieving pluginfacts
Info: Retrieving plugin
Info: Loading facts
Info: Caching catalog for web1.example.com
Info: Applying configuration version '1482323449'
Notice: profile::webserver – loaded
Notice: /Stage[main]/Apache/Package[httpd]/ensure: created
Info: /Stage[main]/Apache/Package[httpd]: Scheduling refresh of Class[Apache::Service]
Info: Computing checksum on file /etc/httpd/conf.d/README
Info: /Stage[main]/Apache/File[/etc/httpd/conf.d/README]: Filebucketed /etc/httpd/conf.d/README to puppet with sum 20b886e8496027dcbc31ed28d404ebb1
...
Notice: /Stage[main]/Apache::Service/Service[httpd]/ensure: ensure changed 'stopped' to 'running'
Info: /Stage[main]/Apache::Service/Service[httpd]: Unscheduling refresh on Service[httpd]
Notice: Applied catalog in 20.26 seconds

这是我们木偶表演的缩短版。您可以看到 Apache 包已经安装。我们移除了README(并将其保存到一个文件桶中;见 https://docs.puppet.com/puppet/latest/man/filebucket.html )并启动了 Apache 服务。

我们现在可以使用以下命令测试 Apache 服务是否启动并运行:

$ curl -I  http://localhost
HTTP/1.1 200 OK
Date: Wed, 21 Dec 2016 13:07:46 GMT
Server: Apache/2.4.6 (CentOS)
Connection: close
Content-Type: text/html;charset=UTF-8

Puppet 代理的每次后续运行都将确保节点保持其当前配置。如果你再做一次木偶表演,什么都不会改变。现在自己试试吧。在我们的 web 服务器节点上,我们将删除在 Puppet 运行期间创建的/etc/httpd/conf.d/25- www.example.com.conf 文件。然后,我们将在该节点上再次运行 Puppet。

$ sudo rm –f /etc/httpd/conf.d/25-www.example.com.conf
$ sudo /opt/puppetlabs/bin/puppet agent --server puppet.example.com --test
Info: Using configured environment 'production'
...
Notice: /Stage[main]/Role::Apache_web/Apache::Vhost[www.example.com]/Concat[25-www.example.com.conf]/File[/etc/httpd/conf.d/25-www.example.com.conf]/ensure: defined content as '{md5}6bee975590cb7b26b89cfd48d8d65bdf'
Info: Concat[25-www.example.com.conf]: Scheduling refresh of Class[Apache::Service]
Info: Class[Apache::Service]: Scheduling refresh of Service[httpd]
Notice: /Stage[main]/Apache::Service/Service[httpd]: Triggered 'refresh' from 1 events
Notice: Applied catalog in 1.71 seconds

在这里,您可以看到文件被替换,Apache 服务被重新启动。

Creating a Puppet Configuration

将现有配置转换为 Puppet 的最佳方式是从小处着手。选择一个函数或应用程序,比如sudo或 SSH 守护进程,并将其配置管理从手动转换为使用 Puppet 进行管理。当这些功能稳定后,向 Puppet 配置添加额外的组件。完成这项任务的一个好方法是根据功能对主机进行分类。例如,我们的 www.example.co m 主机可以运行许多服务,比如 Apache 或 Postfix,因此合乎逻辑的第一步是配置这些服务,然后慢慢添加该主机上也支持的附加功能。

为多台主机指定配置

我们仅仅触及了 Puppet 配置功能的表面,所以让我们看看如何将当前的配置扩展到多个客户机或节点。我们将演示如何区分两个客户机上的配置,并对每个客户机应用稍有不同的配置。

为了实现这种区分,我们将使用 Puppet 的合作伙伴工具 Facter。Facter 是一个系统清单工具,它返回关于您的主机的事实。我们可以使用 facter 二进制文件从命令行运行 Facter,看看它对我们的web1.example.com客户机了解多少。

web1$ sudo /opt/puppetlabs/bin/facter -p
...
facterversion => 3.5.0
filesystems => ext2,ext3,ext4
identity => {
  gid => 0,
  group => ‘root’,
  privileged => true,
  uid => 0,
  user => ‘root’
}
is_virtual => true
kernel => Linux
kernelmajversion => 3.10
kernelrelease => 3.10.0-327.28.3.el7.x86_64
kernelversion => 3.10.0
load_averages => {
  15m => 0.05,
  1m => 0.05,
  5m => 0.04
}
...

我们已经向您展示了 Facter 中可用的一小部分事实,但是您可以看到它了解我们主机的很多信息,包括它的名称、网络信息、操作系统,甚至操作系统的版本。

那么,这对木偶有什么用呢?好吧,这些事实中的每一个都可以作为变量提供给 Puppet。Puppet 在应用任何配置之前运行 Facter,收集客户机的事实,然后将它们发送给 Puppet master 用于配置客户机。例如,hostname事实在我们的 Puppet 配置中作为变量$hostname – or as $fact['hostname']可用。让我们看看清单 19-10 中的一个例子。

More About Facter

事实以结构化事实或数组或散列的形式提供。这意味着您可以通过使用结构化语法的$facts变量来使用它们,换句话说,$facts['hostname']。你可以在这里了解更多: https://docs.puppet.com/puppet/4.8/lang_facts_and_builtin_vars.html

某些事实被称为可信事实。这些都是节点无法自报的事实。它们源自 TLS 证书。这在处理敏感数据时提供了某种程度的保证。您可以通过$trusted['fact_name']访问它们。你可以在这里了解更多: https://docs.puppet.com/puppet/latest/lang_facts_and_builtin_vars.html#trusted-facts

更多信息可以访问 https://docs.puppet.com/facter/

Facter 也是高度可扩展的。使用少量的 Ruby 代码,您可以添加自己的事实,例如,为您的环境定制的信息。你可以在 https://docs.puppet.com/facter/3.5/fact_overview.html 了解如何添加这些自定义事实。

class sudo {
     package { sudo:
         ensure => 'present',
     }

   file { '/etc/sudoers':
            source => [
              "puppet:///modules/sudo/sudo_${hostname}",
              "puppet:///modules/sudo/sudo_${os['family']}",
              'puppet:///modules/sudo/sudo_default'
          ]
          owner => 'root',
          group => 'root',
          mode => '0440',
    }
}

Listing 19-10.Using Facts

这里我们定义了一个提供sudo的类。您可以看到,在这个sudo类中,我们定义了一个文件资源类型,它指定了该文件和一个源文件的基本安全要求,或者是一个傀儡主机的本地文件,当代理签入时,我们会将该文件发送到节点。

当代理签到时,傀儡师会在sudo模块中搜索modules/sudo/files目录中一个名为sudo_web1的文件;如果找不到,它将寻找sudo_Redhat(对于 CentOS 主机,sudo_Debian对于 Ubuntu),如果找不到匹配,它将提供sudo_default文件。

根据所连接的客户端,他们将获得适合自己的文件。但这不是事实的唯一用途。我们还可以使用事实来确定如何配置特定的节点,如清单 19-11 所示。

node default {

  case $facts['os']['name'] {
        'CentOS', 'RedHat':     { include centos } # include the centos
        /^(Ubuntu|Debian)$/:  { include ubuntu } # include the ubuntu class
        default:                       { include common } # include the common class
    }
}

Listing 19-11.A Fact in a case Statement

这里我们创建了默认的节点定义,这是用于所有没有明确定义节点的节点的节点配置。在这个节点定义中,我们使用了 Puppet 语言的一个特性,一个case语句。case语句是许多编程语言通用的概念,它根据变量的值指定结果——在本例中,我们使用了$facts['os']['name']事实,它包含客户端上运行的操作系统的名称(例如,CentOS 或 Red Hat 或 Debian 或 Ubuntu)。

Tip

Puppet 还有另外两种条件句:选择器和if/else子句。你可以在 https://docs.puppet.com/puppet/4.8/lang_conditional.html 了解这些。

在清单 19-11 中,如果$facts['os']['name']的值是CentOS,那么centos类就包含在这个客户端上。我们可以定义不止一种情况,只要它们是由逗号分隔的字符串。如果值为Ubuntu,则包含ubuntu类;这里你可以看到我们可以使用一个正则表达式来匹配 Ubuntu 或 Debian。最后一个值default,是该值与redhatubuntu都不匹配时的行为。在这种情况下,common类被应用到客户端。

在清单 19-12 中,我们使用了另一个傀儡条件,一个选择器。

  $ssh_service = $facts['os']['name'] ? {
        'CentOS' => 'sshd',
        'Ubuntu' => 'ssh',
        default => 'ssh',
    }

  service { $ssh_service:
    ensure => ‘running’,
  }

Listing 19-12.A Selector

在清单 19-12 中,我们引入了一个新的类型service,它管理主机上的服务。我们将我们的service资源命名为$ ssh_service,并在它上面定义了变量。我们使用了一个叫做选择器的傀儡语言结构,结合$fact['os']['name']事实来指定 SSH 服务的名称。这是因为在我们指定的每个操作系统上,SSH 守护进程的名称都不同。例如,在 CentOS 上 SSH 守护进程被称为sshd,而在 Ubuntu 上它被称为ssh

title属性使用$ssh_service的值来指定在每个发行版上将调用什么守护进程。反过来,Puppet 使用这个来决定启动或停止什么服务。当$facts['os']['name']的值既不是CentOS也不是Ubuntu时,使用default值。

最后,ensure属性已经被设置为running以确保服务将被启动。我们可以将ensure属性设置为stopped,以确保它不会启动。

Note

Puppet 语言有很多有用的特性和很多不同的方式来表达你的代码。记得在 https://docs.puppet.com/guides/style_guide.html 查阅木偶风格指南。

相关资源

Puppet 中的资源也有关系的概念。例如,service资源可以连接到安装它的包。使用这个,我们可以在安装新版本的包时触发服务的重启。这让我们可以做一些有用的事情。考虑清单 19-13 中的简单例子。

class ssh {
    service { 'sshdaemon':
        name => $facts['os']['name'] ? {
            'CentOS' => 'sshd',
            'Ubuntu' => 'ssh',
            default   => 'ssh',
        },
        ensure => 'running',
        require => File['/etc/ssh/sshd_config'],
    }

    file { '/etc/ssh/sshd_config':
         owner  => 'root',
         group   => 'root',
         mode   => '0644',
         source => 'puppet://modules/ssh/sshd_config',
         notify   => Service['sshdaemon'],
    }
}

Listing 19-13.Requiring Resources

清单 19-13 显示了一个名为ssh的新类,它包含了我们在清单 19-12 中创建的service资源。我们已经创建了一个file资源来管理/etc/ssh/sshd_config文件。我们在这里创建的ssh服务略有不同;我们在服务类型的name属性上做了一个选择器。它的工作方式与清单 19-12 中的完全相同。除了service资源中的requirefile资源中的notify之外,您已经看到了这些资源中几乎所有的属性。然而,这些不是普通的属性,它们被称为元参数。让我们看看每个元参数,看看它做什么。

require元参数允许您建立与一个或多个资源的关系。您在require元参数中指定的任何资源都将在此资源之前配置;因此,Puppet 将在处理Service['sshdaemon']资源之前处理和配置File['/etc/ssh/sshd_config']资源。这种方法确保在启动 SSH 守护进程服务之前安装适当的配置文件。您可以对包资源做类似的事情。

class httpd {
    package { 'httpd':
        ensure => 'present',
    }

    service { 'httpd':
        ensure => 'running',
        enabled => true,
        require => Package['httpd'],
    }
}

这里,必须在启动Service['httpd']服务之前安装包资源Package['httpd']

Tip

我们还向Service['http']资源添加了enabled属性。当设置为true时,该属性确保我们的服务在主机启动时启动(类似于使用systemctl enable命令)。

在清单 19-13 中,我们还指定了另一个元参数,称为notify。这个元参数已经被添加到File['/etc/ssh/sshd_config']资源中。notify元参数告诉其他资源关于资源的变化和更新。在这种情况下,如果File['/etc/ssh/sshd_config']资源被更改(例如,如果配置文件被更新),那么 Puppet 将通知Service['sshdaemon']资源,使其运行,从而重启 SSH 守护进程服务。

Tip

您可以构建的另外两个关系是subscribebefore。你可以在 https://docs.puppet.com/puppet/latest/metaparameter.html 看到这两个,也可以阅读其他有用的元参数。

使用模板

除了从 Puppet 文件服务器检索文件之外,您还可以利用模板函数在这些文件中应用特定的值来配置服务或应用程序。Puppet 模板使用一种叫做 EPP 的 Ruby 模板语言(参见 https://docs.puppet.com/puppet/latest/lang_template_epp.html )。这是一个在 Puppet master 上编译时发生的函数,使用起来很简单,如清单 19-14 所示。

file { '/etc/ssh/sshd_config':
            path      => '/etc/ssh/sshd_config',
            owner   => 'root',
            group    => 'root',
            mode    => '0644',
            content =>  epp('ssh/sshd_config.epp', { 'root_login' => 'no' }),
            notify    => Service['sshdaemon'],
    }
Listing 19-14.Using Templates

在清单 19-14 中,我们使用了之前创建的相同的File['/etc/ssh/sshd_config']资源,但是我们将源属性换成了content属性。使用content属性,而不是从 Puppet 文件服务器中检索文件,文件的内容从该属性中填充。该文件的内容可以用字符串指定,如下所示:

content => 'this is the content of a file',

或者,如清单 19-14 所示,我们可以使用一个叫做epp的特殊木偶函数。为了使用 template 函数,我们指定一个模板文件,Puppet 用适当的值填充模板中的任何 EPP 代码,这些值已经作为散列传递给函数。清单 19-15 显示了一个简单的模板。

Port 22
Protocol 2
ListenAddress <%= $ipaddress %>

SyslogFacility AUTHPRIV
PermitRootLogin <%= $root_login %>
PasswordAuthentication no
ChallengeResponseAuthentication no
GSSAPIAuthentication yes
GSSAPICleanupCredentials yes
UsePAM yes
X11Forwarding yes
Banner /etc/motd

Listing 19-15.sshd_config Template

我们在清单 19-15 中只使用了一部分 EPP 来指定我们的 SSH 守护进程<%= $ipaddress %>ListenAddress<%= $value %>语法是如何在模板中指定变量的。这里我们指定 Puppet 应该将ListenAddress设置为$ipaddress变量的值。这个变量又是ipaddress事实的值,它包含了eth0接口的 IP 地址。我们还将{ 'root_login' => 'no' }键/值作为散列值传入。这现在作为变量<%= $root_login %>可用。

当我们现在连接一个应用了File['/etc/ssh/sshd_config']资源的客户端时,客户端上的ipaddress事实的值将被添加到模板中,并且root_login将被评估为no,然后在客户端上的/etc/ssh/sshd_config文件中被应用。

您可以在 EPP 模板中执行各种各样的功能——不仅仅是指定变量。你可以在 https://docs.puppet.com/puppet/latest/lang_template_epp.html 阅读更多关于如何使用模板的细节。

您还可以使用 Ruby 的 ERB 模板语言探索更古老的 Puppet 模板风格。句法上类似 EPP 你可以在这里看到它的页面: https://docs.puppet.com/puppet/latest/lang_template_erb.html

更多木偶

在这一章中,我们几乎没有谈到木偶——还有很多要看的。在接下来的小节中,我们将描述一些我们没有涉及到的主题,您可以进一步探索以充分利用 Puppet。

功能

Puppet 还有一个函数集合。函数是有用的命令,可以在傀儡主人上运行来执行动作。您已经看到了两个函数:template,我们用它来创建一个模板配置文件,以及include,我们用它来为我们的节点指定类。还有许多其他函数,包括调用外部命令并返回结果的generate函数,以及在主服务器上记录消息并用于测试配置的notice函数。

您可以在 https://docs.puppet.com/puppet/latest/function.html 查看完整的功能列表。

报告

Puppet 能够报告发生在您的节点或客户机上的事件。结合 PuppetDB,您可以获得关于您的系统的大量报告;如果您有企业版,您将能够在 Puppet 仪表板中看到这些。您可以在 https://docs.puppet.com/puppet/latest/reporting_about.html 了解更多报道。

外部节点

正如您可能想象的那样,当您开始拥有大量节点时,您的配置会变得非常复杂。如果在清单中定义所有节点及其配置变得很麻烦,那么可以使用一个称为外部节点的特性来更好地扩展它。外部节点允许您将节点及其配置存储在外部源中。

ENC 作为一个命令在傀儡主机上运行,并返回一个描述任何特定节点清单的 YAML 文档。它可以来自任何来源,例如数据库。

您可以在 https://docs.puppet.com/guides/external_nodes.html 了解更多关于外部节点量词的信息。

记录您的配置

许多系统管理员的一大苦恼是文档,他们既需要编写文档,又需要保持文档的更新。然而,Puppet 对于如何为您创建的任何模块编写文档并希望通过 Puppet Forge 等网站发布到更广泛的社区有一些建议。你可以在 https://docs.puppet.com/puppet/latest/modules_documentation.html 阅读清单文档。

Puppet 故障排除

Puppet 有一个大而有用的社区以及大量的文档。从这些傀儡网站开始:

推荐以下书籍:

  • 由斯潘塞·克鲁姆、威廉·范·赫韦林根、本·克罗、詹姆斯·特恩布尔和杰弗里·麦丘恩制作的《木偶》
  • 由 Alessandro Franceschi 和 Jaime Soriano Pastor 制作的延长木偶(Packt,2016 年)

这些还有更多可以在这里找到: https://puppet.com/resources/books

Ansible 简介

Ansible 的方法与 Puppet 不同。其核心是开源的 Ansible 软件,可以协调大规模车队的供应。它最初是由迈克尔·德汉设计的,他也是写《补鞋匠》的人。Ansible Inc .是为 Ansible(塔式产品)的商业支持而成立的公司,已被 Red Hat 收购,该公司继续支持开源社区并提供基于订阅的商业支持服务。

用 Python 写的,它被设计成无代理的。它使用 SSH 作为其传输机制,这意味着没有像使用 Puppet 那样的证书管理;相反,您使用现有的 SSH 密钥管理来提供安全的传输。它的工作原理是通过 SSH 向目标服务器发送一个可解析的负载(一个剧本)。有效负载是一组在目标系统上执行的 Python 脚本。

Note

在撰写本文时,Ansible 目前支持 Python 的 2.6 和 2.7 版本。从 Ansible 版本 2.2 开始,它已经初步支持 Python 版本 3。

像 Puppet 一样,Ansible 可以安装文件和管理包以及许多其他资源,包括创建云资源。它通过调用剧本来实现这一点。可行的行动手册由连续的任务组成。当您逐步执行每个任务时,您就执行了一个模块。

模块是应该在目标节点上执行的动作。有大量的核心模块。它们被调用来管理文件、包和服务;他们还可以管理云服务基础设施。Ansible 自带的核心模块有很多,这里都有记载: http://docs.ansible.com/ansible/modules_by_category.html

目标系统或清单是主机的集合,可以组合成组。这些可以是静态的,也可以是用助手脚本动态收集的。您可以为这些主机和组分配变量,以便在您的行动手册中使用。你可以在这里看到更多关于主机和组的信息: http://docs.ansible.com/ansible/intro_inventory.html

在主机或组中声明的任何变量都可以在您的行动手册中使用。Ansible 使用 Jinja2 Python 模板引擎来支持复杂的过滤和剧本编译。您也可以在命令行或行动手册本身中声明变量。

您还可以找到系统事实,比如木偶事实,它们也可以被剧本中的模板引擎使用。我们还可以执行可以从外部服务或本地文件中读取的查找。你可以在 http://docs.ansible.com/ansible/playbooks_variables.html#variables 找到更多关于变量的信息。

在本节中,我们将构建一个 web 服务器。然后,我们将使用 ServerSpec 来验证我们的配置。

可行的安装和配置

Ansible 易于安装,可以从.deb.rpm包、Python Pip 安装或 tar 文件等中获得,这取决于您的系统。我们将使用 Debian 包,因为我们在 Xenial 服务器上运行它。

让我们像这样运行aptitude命令:

$ sudo aptitude install –y ansible

也可通过yum install获得。您也可以通过 Python Pip 安装它,这是一个 Python 包管理器。这在大多数操作系统上都是可用的。

$ sudo pip install ansible

安装完成后,您可以编辑全局配置文件ansible.cfg,它将位于/etc/ansible目录中。当 Ansible 启动时,它会查找配置文件,首先在环境变量ANSIBLE_CONFIG中,然后在本地目录的ansible.cfg中,然后在主目录的∼/.ansible.cfg中,最后在/etc/ansible/ansible.cfg中是系统默认值。

开始时,不需要编辑这个文件。如果你已经下载了第三方的 Ansible 模块到一个特定的位置,你可以在ansible.cfg文件中声明这个位置。其他东西,比如 SSH 选项,也可以放在这里。

inventory     = /etc/ansible/hosts
library       = /usr/share/my_modules/
roles_path    = /etc/ansible/roles
log_path      = /var/log/ansible.log
fact_caching  = memory
ssh_args      = -o ControlMaster=auto -o ControlPersist=60s -o ProxyCommand="ssh  -W %h:%p -q jumphost"

在这里,您可以看到您可能想要更改或添加的一小部分内容。清单是 Ansible 期望看到你的主机列表或一个程序,它将动态地收集你的主机列表。该库用于您自己的或共享的模块。role_path是您可能安装角色的地方。角色是任务、变量、模板、文件和处理程序的集合,可用于配置特定的服务,如 Nginx(参见 https://galaxy.ansible.com/ 了解人们创建和共享的一大堆角色)。

fact_ caching可以存储在本地主机的内存中,也可以存储在不同的共享服务中,比如 Redis (Redis 是一个开源的键/值存储数据库)。这有助于加速 Ansible 多个用户的事实收集。

对于ssh_args,默认的 SSH 选项是ControlMaster=autoControlPersist=60s,它们允许在一个连接上共享多个会话(这意味着我们不需要连接目标主机、执行任务、断开连接、再次连接、执行另一个任务,等等)。我们在这里添加的选项是如何通过跳转主机运行命令,因此任何目标主机都将通过这个 SSH 代理服务器进行访问。

没有要启动的ansible服务。但是,您可能希望自动化在您的主机上运行行动手册的过程;这就是安斯比尔塔( https://www.ansible.com/tower )的用武之地。它是一个由 Red Hat 提供的商业自动化和作业调度工具。

当然,您也可以通过使用其他连续交付(CD)解决方案来自动化您的 Ansible 剧本,例如在 Jenkins ( https://jenkins.io/ )这样的工具中将运行作为构建步骤的一部分来执行。

使用 ansible 命令

Ansible 非常适合在多台主机上运行特别命令。任何模块(记录在 http://docs.ansible.com/ansible/modules_by_category.html )可用于通过ansible命令执行特别命令。如果我们把特定的任务捆绑成一个任务列表,这就叫做剧本,可以用ansible-playbook命令调用剧本。

让我们首先看看运行一个简单任务的ansible命令。在基础层面,Ansible 需要知道三件事。

  • 如何找到目标主机
  • 作为目标的主机
  • 要运行的模块以及该模块的任何参数

虽然ansible命令有更多的选项,但我们可以用它来运行我们的第一个任务。我们将使用setup模块,该模块收集我们可以在任务或行动手册中使用的事实。

$ ansible –c local localhost –m setup
localhost | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.0.61",
            "10.0.2.15"
        ],
...
        "ansible_virtualization_type": "virtualbox",
        "module_setup": true
    },
    "changed": false
}

作为 JSON 字符串返回了一长串可回答的事实;我们只展示了一小部分。ansible命令使用本地连接(-c local)并在目标主机localhost上运行。我们在该主机上执行设置模块(-m setup)。

现在让我们更进一步;在这个本地主机上,我们将安装 Nginx 包。为此,我们使用aptyum模块,这取决于我们所针对的主机操作系统。

$ ansible -c local localhost -m apt -a 'name=nginx state=latest update_cache=yes'
localhost | FAILED! => {
    "changed": false,
    "cmd": "apt-get update '&&' apt-get install python-apt -y -q --force-yes",
    "failed": true,
    "msg": "W: chmod 0700 of directory /var/lib/apt/lists/partial failed - SetupAPTPartialDirectory (1: Operation not permitted)\nE: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)\nE: Unable to lock directory /var/lib/apt/lists/\nW: Problem unlinking the file /var/cache/apt/pkgcache.bin - RemoveCaches (13: Permission denied)\nW: Problem unlinking the file /var/cache/apt/srcpkgcache.bin - RemoveCaches (13: Permission denied)\nE: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)\nE: Unable to lock the administration directory (/var/lib/dpkg/), are you root?",
    "rc": 100,
    "stderr": "W: chmod 0700 of directory /var/lib/apt/lists/partial failed - SetupAPTPartialDirectory (1: Operation not permitted)\nE: Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)\nE: Unable to lock directory /var/lib/apt/lists/\nW: Problem unlinking the file /var/cache/apt/pkgcache.bin - RemoveCaches (13: Permission denied)\nW: Problem unlinking the file /var/cache/apt/srcpkgcache.bin - RemoveCaches (13: Permission denied)\nE: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)\nE: Unable to lock the administration directory (/var/lib/dpkg/), are you root?\n",
    "stdout": "",
    "stdout_lines": []
}

我们出错了!您可以从输出中看到,我们试图执行"apt-get update '&&' apt-get install python-apt -y -q --force-yes"命令,但是被拒绝了。这不应该是一个惊喜;我们不会让未经授权的用户在没有适当的sudo权限的情况下安装软件包。让我们为 Ansible 提供在请求中使用sudo的能力。

$ ansible -c local localhost --become -m apt -a 'name=nginx state=latest update_cache=yes'
localhost | SUCCESS => {
    "cache_update_time": 1481951319,
    "cache_updated": true,
    "changed": true,
    "stderr": "",
    "stdout": "....”
}

现在我们已经为ansible命令添加了--become参数,现在它将尝试通过sudo来执行这些命令。输出再次被缩短,但是您可以看到我们有"change": true,这意味着任务是在系统上执行的,并且系统被改变了。

如果我们再次运行那个ansible任务会发生什么?

$ ansible -c local localhost --become -m apt -a 'name=nginx state=latest update_cache=yes'
localhost | SUCCESS => {
    "cache_update_time": 1481951681,
    "cache_updated": true,
    "changed": false
}

同样,我们成功了,但是这一次,因为 Nginx 已经安装,没有什么需要更改,所以"changed"false。也就是说,在一台主机上安装一个东西,如何在多台主机上做到这一点?

易变库存

Ansible inventory 是一种定义我们希望对其执行命令的主机列表的方法。这可以在脚本的帮助下动态发现,或者作为一个普通的静态主机列表。我们将向您展示如何配置静态主机列表。如果你愿意,你可以在这里阅读动态主机列表: http://docs.ansible.com/ansible/intro_dynamic_inventory.html

清单文件可以在本地目录中,也可以在系统范围的/etc/ansible/hosts文件中。在我们的主机清单中,我们可以定义主机和主机组。主机组在方括号中定义,其中可以有嵌套的主机组。

$ sudo vi /etc/ansible/hosts

somehost.example.com

[all_centos]
gateway.example.com
backup.example.com

[all_ubuntu]
mail.example.com
monitor.example.com

[dbs]
db.example.com

[all_servers:children]
all_centos
all_ubuntu
dbs

在我们的主机列表中,我们定义了一个主机,例如somehost.example.com。然后,我们用[]括号定义了三个主机组。它们包括属于特定操作系统的主机,或者全是 CentOS 或者全是 Ubuntu,但是这些组可以是对您的安装有意义的任何内容。最后,我们有一组组[all_servers:children]主机组,它包含all_ubuntuall_centos主机组以及[ dbs]主机。

让我们看看现在如何在几台主机上执行一些东西。我们将假设运行这个命令的用户已经将他们的公共 SSH 密钥部署到所有主机上。在某些情况下,比如运行流浪主机,你会发现你使用的用户名在每台主机上都是不同的。Xenial 主机将使用ubuntu用户,CentOS 将默认使用vagrant用户。我们可以通过添加以下变量声明来满足宿主文件中的这些差异:

[all_ubuntu:vars]
ansible_user=ubuntu

[all_centos:vars]
ansible_user=vagrant

您会注意到,[dbs]组中的主机没有被声明为 Ubuntu 或 CentOS,因此我们可以使用类似的声明来管理这些主机。

[dbs]
db.example.com ansible_user=vagrant

我们可能还希望通过特定的 jumphost(有时称为堡垒或代理)到达特定的主机。我们可以声明如下:

[remote:vars]
ansible_ssh_common_args: '-o ProxyCommand="ssh  -W %h:%p -q jumphost"'

这样,分类在[remote]组中的所有主机都将通过主机jumphost到达。

Tip

不确定ProxyCommand是做什么的?查看这个页面,了解这个和其他有趣的 SSH 技巧: https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Proxies_and_Jump_Hosts

现在有了主机配置,我们可以运行一个测试,看看我们可以使用下面的ansible命令看到我们的所有主机:

$ ansible all_servers -m ping
gateway.example.com | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
...
db.example.com | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

我们已经成功地在主机上连接、验证并执行了模块ping。如果我们成功了,这个模块就用pong来响应。如果我们无法获得成功的连接,我们将得到类似于以下内容的错误:

mail.example.com | UNREACHABLE! => {
    "changed": false,
    "msg": "ERROR! SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue",
    "unreachable": true
}

确保您能够以执行ansible的用户身份 SSH 到目标主机。您可以使用–vvv选项来增加ansible命令输出的详细程度,这将有助于您跟踪任何连接问题。

假设出于某种原因,我们需要在所有主机上重启sshd。单独管理每台主机将是一项艰巨的任务。我们只有大约五个不同的主机,但是你可以有几千个。对于 Ansible,执行 1 和执行 1,000 是相同的命令。

$ ansible all_servers --become –m service –a "name=sshd state=restarted"

这里,我们已经为目标(all_servers)提供了主机,我们将在目标主机(--become)上使用sudo来执行我们的命令,我们希望使用服务模块(-m service),该模块将接受参数-a "name=sshd state=restarted"

每个模块都有不同的参数,您可以将它们作为键/值对(key=value)来传递。它们都在我们之前给出的模块文档链接中明确列出。

--become选项有更多可用的选项。默认的--become-methodsudo,但是根据你的系统,你可以选择supbrunpfexecrunas或者doas。如果您需要为这些选项提供验证密码,您可以使用--ask-become-pass,它会提示您输入密码。如果不作为根用户运行操作,可以用--become-user选项选择不同的用户。

现在,您可以在整个系统群中发出临时命令,或者只针对较小的组或单个主机。但是我们如何执行几个任务呢?这就是我们使用战术手册的地方。

翻译剧本

既然我们已经配置了 Ansible,并且可以在我们的主机上执行模块,我们将运行几个任务来启动一个特定的服务。Ansible 提供了一个名为ansible-playbook的命令,它被设计用来连接特定的主机并运行一系列任务。行动手册非常强大,它们可以在您的主机上串行或并行运行,并且可以将任务委派给其他主机,等待这些任务完成后再继续下一个任务。通过这种方式,您可以构建复杂的部署行动手册。

剧本是 YAML 的文件。我们之前已经谈过 YAML 档案。YAML 文件是一种用于数据序列化的标记语言,它让我们描述包括列表和关联数组在内的键/值。行动手册可以描述要在一组主机上运行的任务,或者包括其他行动手册以及可指派的角色。现在就来看一个吧。

---
- hosts: ahostgroup
  become: true
  gather_facts: true
  vars:
    - akey: avalue
  tasks:
    - name: do something
       module: module=arguments
  handlers:
  - name: a handler
    module: module=arguments

这是一个基本的剧本布局。我们声明关键主机,并给它一个我们想要运行它的主机的值,可以是一个组或一个单独的主机。我们可以声明其他键值,比如becomegather_facts,它们是布尔值,可以是truefalse

gather_facts选项将触发对所有目标主机的初始请求,并收集它们所有可用的事实。如果你在游戏中没有使用任何事实,你可以将它设置为false,这样会加快你的跑步速度。如果你使用它,你就可以在你的戏剧中使用这些事实作为条件句或者作为戏剧中的价值。

我们可以在vars键中列出我们自己的变量作为关联数组。我们的模板引擎可以使用这些键/值对。任务和处理程序类似,本质上都是任务。处理程序主要用于“处理”服务的重启。您可以从任务通知处理程序执行任务,例如重新启动服务。

让我们看看下面的剧本。在本例中,我们将创建一个行动手册来安装我们的备份软件 Bareos。在本例中,我们将在同一台主机上安装 Bareos 及其所需的 MariaDB。行动手册的第一部分是这样的:

$ vi playbooks/backup.yml
- hosts: backup.example.com
  become: true
  gather_facts: true
  vars:
    url: http://download.bareos.org/bareos/release/latest/{{ ansible_distribution }}_{{ ansible_distribution_major_version }}
    bareos_db_packages: bareos-database-mysql
    sql_import: '/usr/lib/bareos/scripts/ddl/creates/mysql.sql'

这里我们针对的是一个主机,backup.example.com。我们将用sudo执行需要升级权限的操作(so执行本行动手册的用户必须已经在目标主机上拥有sudo权限)。我们将收集关于东道主的事实,并在我们的剧本中使用它们。

vars部分,我们已经指定了我们将在行动手册中使用的一些变量。这些可以很容易地看到和编辑,使我们的行动手册的持续管理更容易。你会注意到{{ words }}。这是我们的模板引擎语法。它告诉 Ansible,{{ }}中的值是变量,或者是事实,或者像我们刚刚创建的那样。

回想我们用ansible命令运行setup模块时,那里的输出列表包含了ansible_distributionansible_distribution_major_version的键值。

        "ansible_distribution": "CentOS",
        "ansible_distribution_major_version": "7",

在 CentOS 系统上,当我们运行该剧时,Jinja2 模板引擎将像这样替换变量:

    url: http://download.bareos.org/bareos/release/latest/CentOS/7

Variables and Conditionals

Ansible 和 Jinja2 可厉害了。有了 Jinja2,您就拥有了巨大的能力,可以使用 Python 方法处理变量来添加或修改剧本中的值。

为了说明这是如何工作的,让我们举一个例子。我们有这样一个文件路径:

/etc/bareos/bareos-dir.conf

通常,您只想看到没有路径的文件名。使用 Ansible 和 Jinja2,我们可以做到这一点:

{{ /etc/bareos/bareos-dir.conf | basename }}

我们可以使用 Ansible 来测试我们的结果。使用带有debug模块的ansible命令,我们可以像这样打印替换的结果:

$ ansible -c local localhost -m debug -a "msg={{ '/etc/bareos/bareos-dir.conf' |basename }}"
localhost | SUCCESS => {
    "msg": "bareos-dir.conf"
}

这里我们采用了完整的路径,并使用管道(|)进入 Jinja2 basename过滤器,我们可以只提取文件名。使用debug模块可以打印出替换的结果。

有关过滤器的更多信息,请参见 http://docs.ansible.com/ansible/playbooks_filters.html

您还可以使用主机组名和 YAML 文件向 Ansible 提供变量。您可以在您的/etc/ansible目录或本地目录中创建一个名为group_vars的目录。在那里,如果您创建一个与您的主机组同名的目录,那么当您执行一个 Ansible 命令时,任何*.yml文件都将被用来查找任何变量(它们也可以被命名为*.yaml或者是 JSON 文件,*.json)。

更多信息请看这里: http://docs.ansible.com/ansible/intro_inventory.html#group-variables

Ansible 还提供了使用工具 Ansible Vault 加密敏感变量的能力。Ansible Vault 允许将秘密(如数据库密码、私有 SSH 密钥和其他敏感数据)与 Ansible 剧本一起存储。当您运行ansibleansible-playbook命令时,您可以用一个可以在命令行上传递的密码来加密这些秘密。

加密文件只是普通的 YAML 文件,可以存在于你的group_vars子目录中。我们将很快展示更多这方面的内容。

更多详情请看这里: http://docs.ansible.com/ansible/playbooks_vault.html

使用条件句,我们可以根据某个值来决定何时运行一个任务。使用 Jinja2,您还可以在模板引擎中执行复杂的条件,以返回想要的结果或动作。Ansible 提供了一个您会经常使用的简单的when条件。

  - name: install mariadb
    apt: name=mariadb-server state=latest
    when: ansible_distribution == "Ubuntu"

这里我们选择当ansible_distribution等于Ubuntu时运行这个任务。当发行版不是 Ubuntu 时,这个任务将被跳过。

如果我们有这样的数据结构:

our_config: {
    our_url:  ‘https://endpoint.example.com’

}

然后,我们可以在 Jinja2 模板文件中使用类似这样的复杂条件,以确保我们的 URL 在未声明的情况下被赋予默认值。

{% if our_config["our_url"] is not defined -%}
  url: {{ our_url | default('https://www.example.com') }}
{% endif %}

这里我们说如果关联数组our_config没有定义键our_url,那么我们应该得到 https://www.example.comdefault URL。在这种特殊情况下,既然定义了our_url,那么 URL 将是 https://endpoint.example.com

定义行动手册任务

让我们继续定义我们的剧本任务。我们已经定义了我们的主机和变量,现在我们必须按照我们希望的顺序执行任务。一般来说,我们希望确保安装了必要的库,下载并安装了正确的包,然后在最终启动服务之前配置它们。

让我们看一下在我们的备份服务器上安装 Bareos 所需的任务。

  tasks:
  - name: install epel
    yum: name=epel-release state=latest

在第一部分中,我们使用yum模块首先安装epel存储库。我们指定我们想要latest发布。name是可选的,但有助于讲述每一步的故事。yumapt模块采用相似的参数,但是当然只能在支持任一包管理器的系统上运行。任务的格式如下:

- name: optional or description
  module_name: module_arg1=value1.... module_argx=valuex

我们也可以这样安装存储库。

  - name: add bareos
    get_url:
      url: "{{ url }}/bareos.repo"
      dest: /etc/yum.repos.d/bareos.repo
      mode: 0444

当然,如果你愿意,这也可以适用于其他类型的文件。这次我们使用get_url来建立一个http://连接,下载 URI,并将内容复制到/etc/yum.repos.d/bareos.repo文件中。显然,内容是 Bareos 存储库,我们使用了变量部分中列出的url变量,并将其与/bareos.repo组合来完成 URI。我们可以使用yum_repository使用 URI 的细节为我们创建存储库(您也可以用类似的方式添加 Apt 存储库)。

有关管理软件包和存储库的更多信息,请参见 http://docs.ansible.com/ansible/list_of_packaging_modules.html

  - name: install pip
    yum: name={{ item }} state=latest update_cache=yes
    with_items:
      - python-pip
      - python-devel

  - name: install mariadb
    yum: name={{ item }} state=latest
    with_items:
      - mariadb-devel
      - mariadb-server
    notify: mariadb_restarted

接下来的任务与第一个相似,但是使用了一个循环。我们说我们想使用yum模块来安装一些包。要安装这些包,我们可以为每个包写一个任务,说安装最新的包,并确保我们有一个最新的存储库缓存(update_cache=yes)。但是由于这是重复的,我们将使用一个循环。

我们说,遍历with_items:列表中列出的项目并安装它们。Ansible 将用为我们列出的那些包替换{{ item }}

你可以在这里阅读更多关于循环的内容: http://docs.ansible.com/ansible/playbooks_loops.html

您还会注意到有一个对处理程序的notify: mariadb_ restarted调用。处理程序只是在剧本任务块的末尾运行的任务。它告诉名为mariadb_restarted的处理程序,如果这些包发生变化,就执行与之相关的任务。但是,这并不会立即启动数据库,我们很快就会这样做。实际的mariadb_restarted处理器将在稍后描述。

  - name: install pre-reqs
    pip: name=mysql state=latest

在这个任务中,我们再次安装一个名为mysql的 Pip 包。Pip 是 Python 模块的一个包管理器,它采用与aptyum模块相似的参数。接下来,我们将启动数据库。

  - name: start db service
    service: name=mariadb enabled=yes state=started

前面是一个使用service模块启动(started)数据库的例子。其他服务状态有stoppedrestartedreloaded。这里的enabled表示我们希望这个服务在启动时启动。我们要求这个步骤在create db步骤到来之前运行。

接下来,我们将继续安装 Bareos 软件包。

  - name: install bareos
    yum: name={{ item }} state=installed
    with_items:
      - bareos-database-mysql
      - bareos-client
      - bareos-director
      - bareos-storage
      - bareos-storage-glusterfs
      - bareos-bconsole
      - bareos-filedaemon

这里我们正在安装 Bareos 包,并再次使用with_items循环来避免重复。bareos-database-mysql包将在我们的{{ sql_import }}变量中创建文件,我们将在下一步中使用它来创建我们的数据库。

  - name: create db
    mysql_db: login_user=root name=bareos state=import target={{ sql_import }}

  - name: create db user bareos
    mysql_user: login_user=root name=bareos password={{ backup_database_password }} encrypted=yes priv=bareos.*:ALL state=present

接下来,我们将使用mysql_db模块为 Bareos 创建数据库。我们可以导入数据库结构,这就是state=importtarget={{ sql_import }}的目的。在这个实例中,我们使用默认的根用户访问,但是我们也可以在这个模块中使用用户/密码组合来获得对数据库的访问。sql_import变量在我们剧本的顶部定义,是由 Bareos 安装提供的导入 SQL 脚本。

我们继续在 MariaDB 数据库上创建 Bareos 用户。我们为用户提供用户、密码、密码类型和权限。statepresent,意思是我们要创建用户;如果我们想删除用户,我们可以选择absent

我们在这里使用的密码变量(password={{ backup_database_password }})很有趣。这是一个敏感的秘密,所以我们需要确保在任何地方都没有明文,但是 Ansible 仍然需要访问它。有了 Ansible Vault,我们可以提供这种保护。

我们已经使用密码生成器生成了一个强密码,并成功存储了该密码。然后,使用现有的 MySQL 安装,我们创建了一个mysql密码散列。

SELECT PASSWORD(‘strongpasswordstring’);
+---------------------------------------------------------+
| password('strongpasswordstring')                                 |
+---------------------------------------------------------+
| *35D93ADBD68F80D63FF0D744BA55CF920B2A45BD |
+---------------------------------------------------------+

然后我们创建了一个playbooks/group_vars/dbs目录、一个vars.yml文件和一个vault.yml文件。在vars.yml文件中,我们将添加以下内容:

backup_database_password: "{{ vault_backup_database_password }}"

然后在vault.yml文件中,我们将像这样添加我们的散列 MySQL 密码:

vault_backup_database_password: '*35D93ADBD68F80D63FF0D744BA55CF920B2A45BD'

我们这样做的原因是,一旦我们加密了这个文件,我们将无法看到我们正在使用的密钥。通过设置纯文本变量(backup_database_password)来查看加密变量(vault_backup_database_password),我们让关注我们的人更容易知道这些变量是如何存储的。所以要明确的是,当ansible命令查找backup_database_password时,它将查找vault_backup_database_password并返回该值。

我们现在将使用ansible-vault命令加密这个文件。

$ ansible-vault encrypt playbooks/group_vars/dbs/vault.yml

我们被要求创建并输入密码,我们也会安全地存储该密码。

接下来我们有配置文件。我们创建了一个playbooks/files目录,并在其中添加了我们的bareos-*.conf文件。

  - name: add bareos-dir.conf
    copy: src=files/bareos-dir.conf dest=/etc/bareos/bareos-dir.conf owner=bareos group=bareos mode=0640

  - name: add bareos-sd.conf
    copy: src=files/bareos-sd.conf dest=/etc/bareos/bareos-sd.conf owner=bareos group=bareos mode=0640

  - name: add bareos-fd.conf
    copy: src=files/bareos-fd.conf dest=/etc/bareos/bareos-fd.conf owner=bareos group=bareos mode=0640

  - name: add bconsole.conf
    copy: src=files/bconsole.conf dest=/etc/bareos/bconsole.conf owner=root group=bareos mode=0640

我们使用copy模块将本地文件复制到服务器,并将它们放在适当的目标文件中。copy模块支持为我们创建的文件分配ownergroupmode权限。src的路径是相对于playbooks目录的。

在这个简单的剧本中,我们没有利用 Ansible 自带的模板引擎。如果您还记得第十四章,在那里我们设置了 Bareos,我们需要将客户端和密码添加到我们的 Bareos 配置文件中。我们可以使用模板来创建这些值,并使设置这些文件的协调变得更容易。

模板就像一个文件,但是它被模板引擎解析来查找和替换变量。所以,像下面这样的值:

$ vi playbooks/files/bareos-fd.conf
Client {
  Name = bareos-fd
  Description = "Client resource of the Director itself."
  Address = localhost
  Password = "YVcb9Ck0MvIXpZkZCM8wBV1qyEi1FD6kJjHUrk+39xun"          # password for FileDaemon
}

可以替换为以下内容:

$ vi playbooks/templates/backup_bareos_fd.conf.j2
Client {
  Name = bareos-fd
  Description = "Client resource of the Director itself."
  Address = localhost
  Password = "{{ bareos_fd_dir_password }}"
}

模板文件一般都有.j2后缀来表示 Jinja2 模板引擎。我们将密码值存储在 Ansible Vault 加密文件中,就像我们存储数据库密码一样。template模块的语法类似于copy模块。关于模板的更多信息,请参见 http://docs.ansible.com/ansible/template_module.html

我们还可以通过其他方式将值添加到文件中。我们可以搜索和替换行,替换文件中标记的文本块等等。关于不同类型文件模块的更多信息可以在这里找到: http://docs.ansible.com/ansible/list_of_files_modules.html

  - name: create backup directory
    file: path=/data/backups/FileStorage state=directory owner=bareos group=bareos mode=0750

接下来,我们使用file模块创建目录来存储我们的备份。这个目录path在目标主机上,state可以是absent删除文件或目录,file to create a filelink to create a symlinkdirectory to create a directorytouch to create an empty filehard创建硬链接。

最后,我们有我们的处理程序。正如我们所说,这将在行动手册中任务块的末尾运行。

  handlers:
  - name: mariadb_restart
    service: name=mariadb state=restarted

因此,现在如果我们运行我们的行动手册,并且我们的数据库有一个包更改,我们的数据库将在行动手册结束时自动重启。您也可以为 Bareos 组件服务添加处理程序。

Caution

对于数据库,自动升级和重新启动数据库版本可能不是明智的做法,因为包版本更新会导致数据库中不可预测的行为,这可能是灾难性的!

运行行动手册

从这一点来看,运行剧本相当容易。Ansible 使用ansible-playbook命令来执行剧本。它有类似于ansible命令的命令选项。现在就让我们来看看它们;见表 19-2 。

表 19-2。

ansible-playbook Options

| `-i` | 库存文件的路径。 | | `--ask-become-pass` | 提示输入远程主机密码以提升权限。 | | `--list-hosts` | 向您展示您的行动手册将针对的主机。 | | `--list-tags` | 列出您的行动手册中可用的标签。 | | `--list-tasks` | 显示将在您的行动手册中执行的任务。 | | `--module-path=` | 添加不同的模块路径。 | | `-v -vv –vvv` | 增加调试的详细程度。 | | `--syntax-check` | 验证行动手册语法是否正确。 | | `--` `user` | 远程用户登录身份。 | | `--private-key=` | 用户的私有 SSH 密钥。 | | `--connection=` | 选择连接类型(`paramiko`、`ssh`、`winrm`、`local`);默认为智能。 | | `--extra-vars=` | 您可以在运行时向剧本添加键/值对,也可以传入任何包含变量的文件(包括`ansible-vault`文件)。 | | `--start-at-task=` | 从这项任务开始。 | | `--step` | 在进行下一项任务之前,逐步完成每项任务并要求确认。 | | `--tags` | 仅运行带有这些标签的任务。 | | `--skip-tags` | 不要运行这些标记的任务。 |

为了确保我们在执行本行动手册时以正确的主机为目标,我们发布以下内容:

$ sudo ansible-playbook --list-hosts -b playbooks/backup.yml
ERROR! Decryption failed

我们已经发出了--list-hosts,但是我们有一个解密失败的消息。这是因为我们有加密的 Ansible 保险库,我们不能读取它。让我们添加添加到密码的提示。

$ sudo ansible-playbook --list-hosts --ask-vault-pass -b playbooks/backup.yml
Vault password:

playbook: playbooks/backup.yml

  play #1 (backup.example.com):         TAGS: []
    pattern: [u'backup.example.com']
    hosts (1):
      backup.example.com

这并不奇怪,因为在我们的行动手册的host: section中,我们的目标是这台主机。如果我们使用一个组或一个正则表达式来定义我们的主机,这个清单会更直接有用。

在运行我们的playbook命令之前,我们还需要做一件事,这并不明显。我们已经在名为dbs的组vars目录中创建了数据库机密,这意味着backup.example.com主机必须是该主机组的成员。如果我们不添加它,当我们运行我们的剧本时,我们会看到这样的错误。

TASK [create db user bareos] ***************************************************
fatal: [backup.example.com]: FAILED! => {"failed": true, "msg": "ERROR! 'backup_database_password' is undefined"}

因此,我们将把backup.example.com添加到/etc/ansible/hosts文件中的[dbs]主机组中。

[dbs]
db.example.com ansible_user=vagrant
backup.example.com

一个主机可以是多个组的一部分,这将使 Ansible 能够看到那个主机组的vars。现在我们已经准备好运行剧本了。

$ sudo ansible-playbook  --ask-vault-pass -b playbooks/backup.yml
Vault password:

PLAY ***************************************************************************

TASK [setup] *******************************************************************
ok: [backup.example.com]

TASK [install epel] ************************************************************
changed: [backup.example.com]

TASK [install pip] *************************************************************
changed: [backup.example.com] => (item=[u'python-pip', u'python-devel'])

TASK [add bareos] **************************************************************
changed: [backup.example.com]

TASK [install mariadb] *********************************************************
changed: [backup.example.com] => (item=[u'mariadb-devel', u'mariadb-server'])

TASK

[install pre-reqs] ********************************************************
changed: [backup.example.com]

TASK [start db service] ********************************************************
changed: [backup.example.com]

TASK [install bareos] **********************************************************
changed: [backup.example.com] => (item=[u'bareos-client', u'bareos-director', u'bareos-storage', u'bareos-storage-glusterfs', u'bareos-bconsole', u'bareos-filedaemon'])

TASK [create db] ***************************************************************
changed: [backup.example.com]

TASK [create db user bareos] ***************************************************
changed: [backup.example.com]

TASK [install bareos-database-mysql CentOS] ************************************
changed: [backup.example.com]

TASK [add bareos-dir.conf] *****************************************************
changed: [backup.example.com]

TASK [add bareos-sd.conf] ******************************************************
changed: [backup.example.com]

TASK [add bareos-fd.conf] ******************************************************
changed: [backup.example.com]

TASK [add bconsole.conf] *******************************************************
changed: [backup.example.com]

TASK [create backup directory] *************************************************
ok: [backup.example.com]

PLAY RECAP *********************************************************************
backup.example.com         : ok=2   changed=14   unreachable=0    failed=0

我们已经成功地运行了我们的剧本。当任务执行并对您的系统进行更改时,它将被更改并添加到最后的摘要行。在不需要执行任务的地方,它将被ok并添加到摘要行。如果没有失败的任务,那么我们认为这是成功的。

Serverspec 测试

我们现在可以自动化构建我们的主机,但是我们如何知道如果我们对我们的任务之一进行更改,它不会无意中破坏一些其他重要的配置?如果我们有一些基本的遵循需求,我们如何知道我们仍然满足每个新构建的那些义务呢?嗯,就像普通的代码一样,我们可以编写测试来保证我们构建的主机满足我们测试中定义的需求。

Ansible 和 Puppet 都适合接受测试。我们将向您展示如何使用名为 ServerSpec 的工具来帮助您的测试,但是您可以使用任何脚本语言中的其他测试框架来帮助测试您的代码。事实上,您可以使用测试驱动开发(TDD)实践,首先编写定义成功和失败场景的测试,然后编写 Ansible 或 Puppet 代码来通过这些测试。

Serverspec 是用 Ruby 编写的,并使用 rspec 框架来运行测试。虽然我们不会试图深入解释 RSpec,但是您可以在线访问一些教程站点。RSpec 的主要网站在这里: http://rspec.info/

如果您不喜欢安装 Ruby 和 RSpec,可以使用这个替代的基于 Python 的框架,它被设计成 Serverspec 的 Python 等价物。 https://testinfra.readthedocs.io/en/latest/。如果您使用 Ansible,那么这可能是您更好的选择。我们将同时测试 Puppet 和 Ansible,因此我们将选择 Serverspec。

安装 Serverspec

在这个测试场景中,我们已经检查了一个特定的 Git 存储库,它将包含我们想要测试的配置管理文件。我们将使用 Vagrant 来帮助启动和测试如何将我们的配置应用到我们的主机上。我们使用 Serverspec 来启动我们的流浪主机(如果它们尚未启动),应用我们的配置指令,然后测试这些指令的结果。

Note

我们在第三章解释了 Git 和如何设置 Vagrant,所以如果你已经忘记了,现在是重温那一章的好时机。

假设我们已经创建了一个 Git 存储库,我们将克隆到我们的本地系统。

$ git clone git@some.git.hosting:/ouruser/ourrepo.git
$ cd ourrepo

在此签出中,我们已经有一个包含以下内容的 Vagrant 文件:

$ vi Vagrantfile
Vagrant.configure(2) do |config|
  config.vm.provider "virtualbox" do |vb|
     vb.memory = "1024"
  end

  config.vm.define "ansible" do |xenial|
    xenial.vm.box = "ubuntu/xenial64"
    xenial.vm.hostname = "ansible"
    xenial.vm.provision :shell do |shell|
      shell.path = "bootscript.sh"
    end
    xenial.vm.provision :ansible do |ansible|
      ansible.playbook = 'ansible/playbooks/ansible.yml'
    end
  end
  config.vm.define "puppet" do |centos|
    centos.vm.box = "centos/7"
    centos.vm.hostname = "puppet"
    centos.vm.provision :shell do |shell|
      shell.path = "bootscript.sh"
    end
    centos.vm.provision "puppet"
  end
end

这将允许我们运行 Xenial Ubuntu 主机和 CentOS 7 主机。Xenial 主机将应用 Ansible 剧本,而 CentOS 主机将运行 Puppet 应用。我们将很快创建配置文件。然后,我们将测试两台主机是否具有相同的配置,这将是一台监听端口 80 的 HTTPD 服务器。

Tip

请记住,使用 vagger 是共享您的配置环境的一个很好的方式,因为您可以共享相同的代码来构建您的主机。

现在我们来谈谈 Serverspec。Serverspec 是一个 Ruby gem。您可以使用另一个名为 Bundler ( http://bundler.io/ )的 Ruby gem 在一个名为 Gemfile 的文件中跟踪您为特定应用程序安装的 gem。我们将使用它来安装我们需要的宝石。这也有助于我们将 gems 的特定版本固定到我们的git提交中,并且有助于我们跟踪我们正在使用的gem发布版本的变化。

$ sudo yum install –y ruby rubygems && gem install bundler --no-ri --no-rdoc

在这里,我们使用 CentOS 主机,安装rubyrubygems包,然后将 gem bundler安装到本地帐户中(没有相关的文档和帮助,安装速度更快)。

安装 Bundler gem 后,我们现在将在我们的存储库目录中创建一个 gem 文件,如下所示:

$ vi Gemfile
source 'https://rubygems.org'

gem 'serverspec'

这里我们已经将 Serverspec gem 添加到了 gem 文件中。source语句是我们下载宝石的地方,也就是人们发布 Ruby 宝石的公共rubygems.org服务器。

现在我们可以使用bundle命令在本地安装 gem。

$ bundle install --path vendor/cache
Fetching gem metadata from https://rubygems.org/.......
Fetching version metadata from https://rubygems.org/.
Resolving dependencies...
Installing diff-lcs 1.2.5
Installing multi_json 1.12.1
Installing net-ssh 3.2.0
...

这将安装serverspec宝石和任何serverspec gem需求。我们现在已经准备好为我们当前签出的存储库初始化serverspec,这相当于运行一个设置脚本。我们用serverspec-init命令来做这件事。

$ serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

有人问我们要测试什么类型的操作系统;这里我们选择1) UN*X

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

我们现在可以选择如何访问主机:要么在本地运行serverspec命令,要么使用 SSH 登录到主机并从该主机内部运行命令。我们将使用 SSH,因此选择 1。

Vagrant instance y/n: y
Auto-configure Vagrant from Vagrantfile? y/n: y
0) ansible
1) puppet
Choose a VM from the Vagrantfile: 0
 + spec/
 + spec/ansible/
 + spec/ansible/sample_spec.rb
 + spec/spec_helper.rb

Serverspec 已经检测到了 Vagrant 文件,现在想知道我们是否要为我们自动配置这些主机中的一个。这里选择puppetansible都可以,所以我们将使用copy命令添加另一个。

我们现在已经设置了 Serverspec。已经创建了一个spec目录,通过spec/spec_helper.rb文件管理 Serverspec。Serverspec 将在spec目录中查找与在Vagrantfile中声明的主机匹配的目录,并运行它在那些以*_spec.rb结尾的目录中找到的任何测试。因此,我们现在可以将spec/ansible目录复制到spec/puppet,现在两台主机都将被测试。

$ cp –r spec/ansible spec/puppet

如果我们看一下sample_spec.rb文件,它将显示我们的 Serverspec 测试。

require 'spec_helper'

describe package('httpd'), :if => os[:family] == 'redhat' do
  it { should be_installed }
end

describe package('apache2'), :if => os[:family] == 'ubuntu' do
  it { should be_installed }
end

describe service('httpd'), :if => os[:family] == 'redhat' do
  it { should be_enabled }
  it { should be_running }
end

describe service('apache2'), :if => os[:family] == 'ubuntu' do
  it { should be_enabled }
  it { should be_running }
end

describe service('org.apache.httpd'), :if => os[:family] == 'darwin' do
  it { should be_enabled }
  it { should be_running }
end

describe port(80) do
  it { should be_listening }
end

_spec.rb文件应该易于阅读,这是 RSpec 的设计目标之一,使测试变得清晰。第一行是 Rubyrequire类似于 Python 导入语句,只是让spec_helper对我们可用。

接下来的几行是这样的。我们想describe一个叫做httpd的包。如果我们在一个redhat家庭主机上,那么应该安装这个包。现在你可以阅读其他的,它们是相似的,描述了当我们运行 Serverspec 时我们期望发现什么。Serverspec 将接受这些简单的描述,并处理它将如何验证我们的测试。

我们将删除描述 Darwin 系列操作系统(Mac OS)的倒数第二个测试,但是其余的测试非常适合我们的目的。

运行测试

让我们从运行测试开始;然后,我们可以看到为了通过测试我们需要做的工作。我们使用安装 Serverspec 时附带的一些工具来完成这项工作。

$ rake spec:ansible

Package "apache2"
  should be installed (FAILED - 1)

Service "apache2"
  should be enabled (FAILED - 2)
  should be running (FAILED - 3)

Port "80"
  should be listening (FAILED - 4)

为了运行我们的测试,我们将执行所谓的rake任务。rake是一个 Ruby 版本的make工具;这里可以看到更多: https://github.com/ruby/rake 。我们将要运行的任务叫做spec:ansible,它将启动 Ansible 漫游主机,然后运行 Serverspec 测试。

您可以在Failures:部分看到测试失败的原因。

Failures:

  1) Package "httpd" should be installed
     On host `puppet'
     Failure/Error: it { should be_installed }
       expected Package "httpd" to be installed
       sudo -p 'Password: ' /bin/sh -c rpm\ -q\ httpd
       package httpd is not installed

     # ./spec/puppet/sample_spec.rb:4:in `block (2 levels) in <top (required)>'

您可以看到 Serverspec 试图运行rpm –q httpd命令,但是找不到安装的httpd。这是意料之中的,因为我们还没有安装它。我们现在将编写 Ansible 代码来安装它,并在我们的 Vagrant 主机上提供它。

$ vi ansible/playbooks/ansible.yml
---
- hosts: all
  gather_facts: true
  become: true
  tasks:
  - name: install apache2
     apt: name=apache2 state=latest

我们现在也将为木偶做同样的事情。

$ vi manifests/site.pp
class httpd {

  package {'httpd': ensure => 'latest' }

}

include httpd

我们现在将使用vagrant provision命令调配我们的主机。让我们再次运行 Serverspec 测试。

$ rake spec
Package "apache2"
  should be installed

Service "apache2"
  should be enabled
  should be running

Port "80"
  should be listening

Finished in 0.06987 seconds (files took 11.03 seconds to load)
4 examples, 0 failures
...
Package "httpd"
  should be installed

Service "httpd"
  should be enabled (FAILED - 1)
  should be running (FAILED - 2)

Port "80"
  should be listening (FAILED - 3)

运行rake spec将在我们的spec/文件夹中的任何主机上运行测试。我们对 Ansible 主机的测试已经全部通过。这是因为在 Ubuntu 上,当你安装服务包时,它们会自动启动。有了 CentOS,他们不会这样做,直到他们被告知。让我们将 CentOS 主机设置为绿色。

$ vi manifests/site.pp
class httpd {

  package {'httpd': ensure => 'latest' }
  service { ‘httpd’: enable => true, ensure => true }

}

include httpd

再次运行 provision 和rake spec,我们现在可以看到我们在 Ansible 和 Puppet 中都是绿色的。

Package "apache2"
  should be installed

Service "apache2"
  should be enabled
  should be running

Port "80"
  should be listening

Finished in 0.06992 seconds (files took 8.41 seconds to load)
4 examples, 0 failures

-----
Package "httpd"
  should be installed

Service "httpd"
  should be enabled
  should be running

Port "80"
  should be listening

Finished in 0.11897 seconds (files took 7.98 seconds to load)
4 examples, 0 failures

你可以运行更多的测试,这些都是非常基本的,但是我们现在知道了当你对系统做很多改变的时候这是多么的有帮助。现在,您应该将这些测试连接到您的 Jenkins 或 CI 测试基础架构,并在提交到您的主 VCS 分支之前运行它。

这里有一个 Serverspec 的教程: http://serverspec.org/tutorial.html

你可以在这里看到更多要测试的资源类型: http://serverspec.org/resource_types.html

摘要

在本章中,我们向您介绍了一些简单的配置工具,这些工具使构建和安装主机的过程变得快速而简单。您学习了如何执行以下操作:

  • 安装和配置 Cobbler
  • 使用选定的操作系统自动引导主机
  • 安装选定的操作系统并自动回答安装问题

我们还引入了一个配置管理工具 Puppet,它将帮助您一致而准确地管理您的环境。您学习了如何执行以下操作:

  • 安装木偶
  • 配置木偶
  • 使用 Puppet 来管理主机的配置
  • 使用 Puppet 的更高级功能
  • 安装和配置 Ansible
  • 运行可翻译的行动手册
  • 安装并运行 Serverspec 来测试您的配置

第一部分:开始

第二部分:让 Linux 为你工作

posted @ 2024-08-02 19:35  绝不原创的飞龙  阅读(6)  评论(0编辑  收藏  举报