RHEL-网络学习指南-全-
RHEL 网络学习指南(全)
原文:
annas-archive.org/md5/c57fcd6597d60f278d896cd91be8a8f9
译者:飞龙
序言
欢迎来到学习 RHEL 网络。我叫Andrew Mallett,将为您提供专家指导和培训,帮助您掌握这款强大且流行的 Linux 发行版。我们将使用 Red Hat Enterprise Linux 7.1。这个最新的版本带来了许多改进,并且更有可能是下一个版本。迁移到新系统,systemd 的服务管理以及由此衍生的生态系统为管理员提供了很多新内容需要吸收。
写关于企业 Linux 发行版的内容非常重要,因为我们看到越来越多的组织开始部署 Linux。因此,我们需要有知识的专业人士来管理这些系统。Linux 基金会与招聘公司 Dice 联合调查了许多大型组织,并得出了以下结果:
-
93% 参与调查的组织表示他们正在寻找 Linux 专业人士
-
91% 的招聘经理表示,他们发现很难找到熟练的 Linux 管理员
-
另外值得一提的是,Linux 专业人士的薪资在过去 12 个月里上涨了 9%。
由于这么多组织对 Linux 的信心,本书的重点必须是商业驱动的,既是为了我也是为了你。我们希望你不仅能够提高自己的职业前景,还能提升你的 Linux 知识。
企业级 Linux 发行版,如 CentOS、Red Hat、Debian 和 SUSE Enterprise Linux,并不会部署你可能在家庭或爱好者导向的发行版(如 Fedora 或 openSUSE)中发现的最新前沿技术。相反,它们允许这些发行版成为开发平台,用以打磨和完善软件,然后在几个月甚至几年后再将其迁移到企业环境中。企业级 Linux 必须是可靠、稳定、具有韧性,并且能得到部署它的组织及来自社区或付费支持团队的后端支持的支持。从定义上讲,最新的软件开发技术并不适合这一需求;这些技术处于最新开发阶段,了解这些技术的进展和最佳实践将需要时间来发展和成熟。
尽管本书将聚焦于 RHEL,但你也可以使用 Fedora 21 或 CentOS;这两种版本都能为你提供兼容的平台,在该平台上我们可以通过书中提供的多个示例进行实践。
本书的内容
第一章,介绍企业 Linux 7,帮助你了解企业级 Linux 与其他前沿发行版的区别,以及 Red Hat、CentOS 和 Fedora 之间的关系。这一简短的章节能让你对 RHEL 有更深入的理解,并帮助你在选择的平台上学习 RHEL 7。
第二章, 配置网络设置,讨论了如何配置网络设置,以及 Red Hat 如何允许你在主机上设置 IP 地址配置。
第三章, 配置关键网络服务,帮助你的 RHEL 主机设置网络地址。本章教你如何添加一些命令行网络服务,以及如何配置 NTP、DNS、DHCP、SMTP、时间、名称解析、IP 地址分配和电子邮件服务。
第四章, 实现 iSCSI SANs,介绍了 RHEL 7。它提供了一个基于内核的模块来实现基于网络的存储。本章教你如何部署 iSCSI 目标并从 RHEL 客户端进行连接。
第五章, 实现 btrfs,介绍了Better FS。文件系统内置的卷管理功能使得存储管理变得更加容易,并且是将文件系统共享到网络上的常用基础。
第六章, 使用 NFS 共享文件,解释了 NFS 这一事实上的 Unix 文件共享服务,它在企业 Linux 市场中仍然具有重要地位。本章介绍了如何使用 NFSv4,并与 V3 进行了对比,以便你了解它在防火墙管理等新特性方面的优势。
第七章, 使用 Samba 4 实现 Windows 共享,介绍了 RHEL 如何在网络上提供服务,而客户端工作站端安装了 Windows 操作系统。这需要 RHEL 支持这些 Windows 客户端。文件和打印服务可以通过 RHEL 7 上的 Samba 4 服务提供。
第八章, 将 RHEL 7 集成到 Microsoft Active Directory 域,探讨了许多企业组织已经建立了身份服务并使用 Microsoft 的 Active Directory 运行。合理的做法是使用现有的域账户来访问 RHEL 7 服务器上的资源。RHEL 服务器可以加入域服务器,成为成员服务器,允许你通过单点登录共享 Linux 系统上的共享资源。
第九章, 部署 Apache HTTPD 服务器,部署了一个可能对你的网络至关重要的 Web 服务器。这可能是为了提供内部网的 Web 访问或外部访问 Internet。许多管理员使用 Apache Web 服务器来提供对本地软件仓库和安装源的访问,因此该服务的重要性不容忽视。
第十章,使用 SELinux 保证系统安全,提供了以下见解:随着越来越多的系统连接到互联网,网络服务面临的漏洞呈指数增长。SELinux 从 RHEL 4 版本开始就被纳入其中,但我们经常看到博客建议禁用 SELinux。本章将教您如何有效地部署启用 SELinux 的系统。
第十一章,使用 firewalld 进行网络安全,提供了如何在您的 RHEL 7 系统上有效使用防火墙的见解,涵盖了最新的命令行工具、firewalld 服务和 firewall-cmd 命令。全书展示了最新防火墙的实际应用,以及如何打开所需的端口和服务。本书最后详细介绍了如何使用 firewalld 服务有效保护您的服务器。
本书所需内容
本书使用的是 Red Hat Enterprise Linux 7.1。可以直接从 Red Hat 下载评估版:access.redhat.com/downloads
。
如果您不打算使用 RHEL,您可以使用 Fedora 21 或 CentOS 7,从以下网站获取:
-
Fedora 可以从
getfedora.org/en/workstation/download/
下载。 -
CentOS 7 可以从
www.centos.org/download/
下载。
本书适用对象
本书是为 Linux 管理员或那些想从零开始学习 Linux 管理的读者设计的。
约定
本书中,您将会看到多种不同的文本样式,用来区分不同种类的信息。以下是一些样式示例及其含义的解释。
文本中的代码词语如下所示:“/etc/issue
的内容将在登录提示符之前显示。”
代码块的格式如下:
zone "tup.local." IN {
type master;
file "named.tup";
};
任何命令行输入或输出都写作如下形式:
$ sudo vi /etc/named.conf
注意
警告或重要提示以框体形式显示,如下所示。
小贴士
小贴士和技巧以这种形式出现。
读者反馈
我们始终欢迎读者的反馈。请告诉我们您对本书的看法——您喜欢或不喜欢的部分。读者反馈对我们开发出真正能让您受益的书籍至关重要。
如需发送一般反馈,只需通过电子邮件发送到 <feedback@packtpub.com>
,并在邮件主题中提及书名。
如果您在某个主题上有专业知识,并且有兴趣写作或为书籍做贡献,请查看我们的作者指南:www.packtpub.com/authors。
客户支持
既然您已经拥有了一本 Packt 出版的书,我们为您提供了多项帮助您从购买中获得最大收益的资源。
勘误
虽然我们已尽一切努力确保内容准确性,但错误偶尔还是会发生。如果您在我们的书籍中发现错误,无论是文本还是代码方面的,请向我们报告。这样一来,您可以帮助其他读者避免困扰,并帮助我们改进后续版本的书籍。如果您发现任何勘误,请访问 www.packtpub.com/support
,选择您的书籍,点击勘误提交表链接,并输入您的勘误详情。一旦您的勘误被验证,您的提交将被接受,并且勘误将被上传到我们的网站,或添加到该书籍的现有勘误列表中的勘误部分。
盗版
互联网上的版权盗版问题是所有媒体的长期问题。在 Packt,我们非常重视对我们版权和许可的保护。如果您发现我们作品的任何形式的非法副本,请立即向我们提供位置地址或网站名称,以便我们采取补救措施。
如果发现涉嫌盗版材料,请联系我们 <copyright@packtpub.com>
并附上链接。
我们感谢您帮助保护我们的作者,以及我们为您提供有价值内容的能力。
问题
如果您在书籍的任何方面遇到问题,请联系我们 <questions@packtpub.com>
,我们将尽力解决。
第一章:介绍企业级 Linux 7
欢迎来到企业级 Linux 版本 7 的世界。这在 2014 年 6 月 9 日首次向我们介绍。Red Hat 从 2013 年 12 月 11 日的 RHEL 7 beta 版开始了它的旅程。随后在 4 月 23 日发布了下一个候选版本。最后,正如预期的那样,黄金版在 2014 年 6 月上市。在撰写本书时,我们的示范将使用 beta 版本的 Update 1。
本章将帮助您理解为什么企业级 Linux 与其他最前沿的发行版不同。它还将帮助您了解 Red Hat、CentOS 和 Fedora 之间的关系。我们还希望本章能够帮助您深入了解如何在您选择的硬件平台上使用 RHEL 7。本章的主题如下:
-
Red Hat Enterprise Linux
-
CentOS
-
Fedora
-
确定您的发行版和版本
Red Hat Enterprise Linux
当我们谈到 Linux 时,很多时候,Red Hat 将是主要考虑的对象;尤其是在企业级别工作时,Red Hat 几乎肯定会成为我们的一部分。可靠性、可预测性和稳定性是与这家非常赚钱和成功的组织密切相关的词汇。为了让大家了解他们的最近成功,该公司在 2010 年纳斯达克(RHT)的股价不到 30 美元。然而,到了 2014 年底,他们的价值围绕着 60 美元左右。
企业级 Linux 不太可能处于最前沿。作为企业发行版,它必须是可支持和可靠的。随着 RHEL 7 的发布,我们看到 Linux 内核的第三个版本在 RHEL 中首次使用。Linux 内核版本 3 于 2011 年 7 月 22 日首次亮相。因此,我们可以说企业级 Linux 可能落后于最新和最伟大的版本约 3-4 年。
在许多方面,可靠性比版本 3 将提供的新内核功能更为重要。这些新功能通常与硬件相关,但并不重要,因为企业级硬件必须在关键任务环境中采取类似的谨慎方法。我们发现企业级硬件必须是可靠的,这也许导致缺乏未经测试的新功能。新硬件和驱动程序的开发可以在小型企业和家庭用户中进行测试。这些测试者可以经历折磨,而开发工作可以为我们的关键任务服务器改进。蓝筹企业公司要求的支持水平超出了在支持论坛中发布技术查询并希望有人看到并回应的范围。几乎可以肯定,任何金融机构都必须能够证明其对系统的支持水平。这可以通过提供支持协议或合同以及相关的服务水平协议(SLA)来轻松实现。为此,Red Hat 并非免费,但收费是为了提供支持,而非为了发行目的。最基本的支持水平每年大约为 350 美元(美国 dollars)。
Red Hat 于 2002 年推出了企业 Linux,并发布了 RHEL 版本 2.1。最初,支持期为 10 年,但随着 RHEL 7 的发布,已延长至 13 年。这意味着 RHEL 7 的支持期可以延长至 2027 年 6 月 30 日。目前的 RHEL 7.1 beta 版本使用的是 Linux 内核 3.10.0-210,相较于 7.0 版本的 3.10.0-123,我们可以看到内核版本的微小增长,显示出在推出任何版本的 RHEL 时的谨慎态度。在写作时,维护者提供的最新 Linux 内核版本是 3.18.1(来自www.kernel.org
)。
Red Hat 产品可以从access.redhat.com/downloads
下载。你需要创建一个账户才能开始评估并下载 RHEL。
CentOS
CentOS(社区企业操作系统)多年来作为 Red Hat 重建版本被广泛使用,并且完全免费。这是指 Red Hat 的标识和品牌从系统中移除,然后作为“CentOS”重新分发。这听起来并没有想象中那么糟糕。Red Hat 使用开源代码,且重新分发完全符合GPL(GNU 通用公共许可证)协议的要求。你失去的是支持。因此,你可能会发现 CentOS 更多地用于较小的商业操作和学术界(在这些地方,外部支持并不是那么重要)。CentOS 的支持只能通过公共论坛获得。当然,这意味着没有可保证的服务水平。
CentOS 于 2004 年开始运营,现在已进入第二个十年。它推出的免费产品在市场上复制了与其 Red Hat 兄弟相同的可靠性和可预测性。Red Hat 与 CentOS 之间的关系在 2014 年 1 月变得更加正式。CentOS 的治理小组现在包括 Red Hat 董事会成员。
CentOS 不像 Red Hat 那样发布测试版本。这意味着 CentOS 稳定版本中最新的版本是 7.0。它将使用与 RHEL 7.0 发行版相同的内核和版本 3.10.0-123。CentOS 与 Red Hat 的高度相似,通常意味着 CentOS 成为那些希望学习 Red Hat 并可能获得认证的用户的完美学习平台。这无疑是一个非常可行的选项,同样适用于学习本书内容。尽管我们将使用 RHEL 7.1 测试版,但如果你想使用 CentOS,它应该非常相似,且大部分与 CentOS 7 兼容。
由于 CentOS 不提供订阅支持,这反过来会影响产品生命周期。为了获得 RHEL 7 提供的 13 年完整支持,RHEL 客户将需要购买额外的支持来覆盖最后 3 年的服务。这意味着 CentOS 有仓库可以分发更新,持续更新 10 年,这导致 CentOS 7 可以持续更新到 2024 年 6 月。从这个角度看,CentOS 7 的支持时间相当不错,且完全没有财务成本。
你可以直接从 centos.org/download/
下载 CentOS 的最新版本,无需创建账户。
Fedora
我们可以说 Fedora 是 Red Hat 的家庭版。虽然我们将其标记为家庭版,Fedora 也有服务器版,而你可以自由选择如何和在哪里使用 Fedora。它对新款笔记本和最新硬件的支持将大大增强,这使得它通常成为家庭用户和爱好者的首选。当前版本是 Fedora 21,几乎采用了所有最新的内核,版本为 3.17.4-301。
使用 Fedora 的另一个好处,即使不是用于生产环境,也能让你熟悉一些技术。这些技术最终会在某个时刻变得企业级可用。通过这种方式,你会在产品开发的过程中学习。例如,RHEL 7 基于 Fedora 19 和 20。如果你是 Fedora 的忠实支持者,你将已经熟悉 GRUB2、BTRFS、docker 和 systemd(这些都首次出现在 RHEL 7 中)。
Fedora 的支持是由社区驱动的,软件更新大约会在初始产品发布后的 13 个月内提供。例如,Fedora 21 会在 Fedora 23 发布后再支持 1 个月。发布周期大约为每 6 个月一次,这使得我们大致可以得到 13 个月的支持生命周期。这也是为什么 Fedora(以及类似的 Fedora 发行版)通常无法进入企业级类别的原因,因为更新生命周期如此之短。
对于学习和家庭使用,这是一个非常棒的发行版。你可以选择下载工作站版、服务器版或云版本,网址是 getfedora.org/
。
从流行度的角度来看,Fedora 绝对算得上是其中之一。过去十二个月中,Fedora 下载页面的点击次数使其成为第四大最受欢迎的发行版。为了支持这些数据并查看我们从哪里获取这些信息,你可以访问 www.distrowatch.com
。
确定你的发行版和版本
如果你是从头开始安装,那么我们希望你能确定自己到底在安装什么。如果你不能确定,那我们在安装之前就需要解决一些问题。然而,你可能会遇到一台预装的机器或一台你可以访问的实验室机器。进行任何故障排查的明显第一步是确定我们将要使用的操作系统和补丁级别。接下来我们将介绍多种方法,帮助你确定你将使用的 Linux 发行版。
/etc/system-release 文件
/etc/system-release
文件在我们讨论过的所有 Red Hat 变种中都是一致的。可以通过 cat
命令(即连接命令)简单地读取它。实际上,在所有三种系统上,这个文件都是一个符号链接,指向以下列表中的相关文件:
/etc/redhat-release
/etc/centos-release
/etc/fedora-release
然而,阅读链接中的文件是有意义的,因为 /etc/system-release
文件在所有这些发行版中都会存在,并指向正确的操作系统文件。在示范的 RHEL 7.1 系统上运行以下命令会显示如下内容:
$ cat /etc/system-release
Red Hat Enterprise Linux Server release 7.1 Beta (Maipo)
/etc/issue 文件
第二种方法可以是从物理机器上的标准终端读取登录横幅。如果设备上没有运行图形界面,物理终端可以是 tty1
到 tty6
。然而,如果你在桌面或服务器上运行图形界面,通常 tty2
是第一个命令行终端。你可以通过 CTRL + ALT + F2 键序列从图形界面访问此终端。在登录提示符之前,将显示 /etc/issue
的内容。/etc/issue
内容需要通过 /sbin/agetty
TTY 程序来读取。我们可以连接这个文件,但它没有什么用,因为它包含了特殊的转义字符,这些字符由 agetty 扩展。以纯文本查看该文件时,看到如下命令:
$ cat /etc/issue
\S
Kernel \r on an \m
\S
命令将显示操作系统,\r
命令将显示内核版本,\m
命令将显示机器类型。在 RHEL 7.1 系统上,我们将使用这个文件,在终端登录时显示如下内容:
Red Hat Enterprise Linux Server 7.1 (Maipo)
Kernel 3.10.0-210.el7.x86_64 on an x86_64
使用 lsb_release
如果有一种方法可以显示你正在使用的操作系统的详细信息,为什么不可以有三种方法呢!像 Linux 一般,我们可以通过多种方式解决这个问题。第三种方法是使用 lsb_release
命令。该命令通常不会作为默认安装的一部分,因此需要添加到你的系统中(如果尚未完成此操作)。
安装此软件可以通过 yum
实现,但需要以 root
用户(管理员)身份运行。因此,可以使用 su -
切换到 root 账户,或者如果你的账户已设置为管理员,则使用 sudo
,如下面的代码所示:
$ sudo yum install redhat-lsb-core
提示
如果你是 Linux 管理的新手,那么下一章将从如何在 RHEL 中获取和管理管理员权限的快速教程开始。
尽管软件包名称中有 redhat
元素,但此命令也可以在 CentOS 上使用(如果你使用的是 CentOS 来学习企业级 Linux 7)。安装该软件包后,我们将使用 lsb_release
命令来识别操作系统。在本书中使用的系统上,我们可以查看以下输出:
$ lsb_release -a
LSB Version: :core-4.1-amd64:core-4.1-noarch
Distributor ID: RedHatEnterpriseServer
Description: Red Hat Enterprise Linux Server release 7.1 Beta (Maipo)
Release: 7.1
Codename: Maipo
如果你使用的是 Fedora,可以通过以下命令安装该软件包:
$ sudo yum install redhat-lsb
输出结果类似,但与 Fedora 版本相关,以下是来自 Fedora 21 服务器的输出:
$ lsb_release -a
LSB Version: :core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch:desktop-4.1-amd64:desktop-4.1-noarch:languages-4.1-amd64:languages-4.1-noarch:printing-4.1-amd64:printing-4.1-noarch
Distributor ID: Fedora
Description: Fedora release 21 (Twenty One)
Release: 21
Codename: TwentyOne
确定内核版本
我们已经看到,在登录终端时,Linux 内核版本可能会通过 /etc/issue 文件显示。然而,我们也可以通过 uname -r
命令轻松地显示当前内核的版本。内核是操作系统的核心,由 Linux 基金会作为开源软件进行维护。该命令可以作为普通用户运行。在 RHEL 7.1 系统上,它显示以下信息:
$ uname -r
3.10.0-210.el7.x86_64
同样,了解 Linux 内核的版本是建立系统故障排除和进行支持请求的一个很好的起点。
概述
到现在为止,我希望你对如何跟随本书学习 Red Hat Enterprise Linux 7 网络有了更好的理解,无论是在 RHEL、CentOS 还是 Fedora 上。你现在应该能够区分每个发行版的优势,并识别你将要使用的版本。
在下一章中,我们将开始研究如何在 RHEL 7 上配置网络。此外,我们还将探讨如何使用 su
或 sudo
获得管理员权限及其好处。这对于那些 Linux 管理新手以及不熟悉作为管理员运行任务的用户特别有用。
第二章:配置网络设置
坐在这里急切地敲击键盘,我合理地希望本章标题能在某种程度上暗示我们将要讨论的内容。因此,我期待当我揭示我们将在本章学习如何配置 RHEL 7 系统的网络时,不会让你感到太惊讶。不过,稍微拆解一下,我们不仅仅讨论网络。首先,我们会确保你了解如何在 Linux 中获取管理员权限。虽然这与网络无关,但获取管理员权限是我们书中许多操作的基础。一旦我们完成了初步的权限部分,我们将迅速转向调查如何在 RHEL 7 中配置网络。在本章中,我们将讨论以下主题:
-
提升权限
-
使用
ip
和hostnamectl
-
NetworkManager 和网络脚本
-
与 NetworkManager 交互
-
使用控制中心
-
使用
nmtui
菜单 -
与
nmcli
交互
提升权限
作为 RHEL 服务器或桌面系统的管理员,有时需要 root 访问权限。root
用户或用户 ID 0
是系统的本地管理员。尽管可以直接以 root
用户身份登录系统,但与大多数系统一样,建议根据需要获取 root
访问权限。可以使用两种机制:
-
替代用户或
su
命令 -
使用
sudo
命令
首先,我们将介绍 su
命令。
su
命令
当用户在没有指定用户名的情况下输入 su
命令时,系统将提示输入 root 密码。如果身份验证成功,用户将进入 root shell。以下是使用 su
获取 root 权限的有效机制:
-
su -l
:这会启动一个完整的登录 shell,所有的环境变量都会为 root 设置。用户的工作目录会被更改为 root 用户的主目录,通常是/root
。 -
su
:这与su -l
相同。 -
su
:这会启动一个非登录 shell,在这个 shell 中不会加载 root 用户的完整配置文件或环境变量。结果是,一些变量——比如$USER
——不会被重置,当前目录也保持不变。尽管是以非登录 shell 展示,但仍然需要输入正确的 root 密码进行身份验证。
使用 su
命令是一种简单的方式来获取权限。这可能是管理员的一种方便选择。对于一个小型环境,这可能是可以接受的;然而,在企业环境中,这通常不可行,因为审计受限。尽管可以追踪谁使用了 su
命令来获取权限,但所有这些操作都会记录在 /var/log/secure
日志文件中。由于从此时起所有活动都将以 root
用户身份记录,我们无法详细了解是哪位管理员执行了某个特定命令。另一大缺点是,用户需要知道 root 密码。这再次是一个重大的安全问题,对我来说完全不能接受。
虽然我们想要使用 su
命令,但可以通过 PAM(可插拔认证模块)和 wheel
组来控制谁可以访问 su
。通过将用户添加到特殊的管理组 wheel
,我们可以将对 su
命令的访问限制为该组的成员。
要将用户添加到 wheel
组,您需要以 root 用户身份运行 # usermod -a -G wheel <用户名>
,其中 <用户名>
是应添加到 wheel
组的帐户的登录名。-a
选项用于将一个组追加到用户当前的组成员列表中。
为了确保只有 wheel
组的成员可以使用 su
命令,您必须以 root
用户身份编辑 /etc/pam.d/su
的 PAM 配置文件。在您喜欢的文本编辑器中打开该文件——例如 vi
或 nano
——并通过删除行首的 #
字符来取消注释以下行:
#auth required pam_wheel.so use_uid
一旦此更改生效,只有 wheel
管理组的成员才能使用 su
命令切换到另一个用户 ID。
如果您愿意,您可以对 /etc/pam.d/su
PAM 文件进行第二次更改,以确保 wheel
组的成员可以轻松访问 su
。此文件的推荐设置将仅限于那些安全性不重要的系统——如教室或实验室计算机。
编辑 /etc/pam.d/su
文件,并通过删除行首的 #
字符来取消注释以下行:
#auth sufficient pam_wheel.so trust use_uid
有了这个更改,wheel
组的成员在使用 su
时不需要进行密码认证;这是 root 用户的默认行为。
提示
这两个 PAM 编辑在我们讨论的 Red Hat 变体中是一致的:RHEL 7、CentOS 7 和 Fedora 21。此外,默认情况下,root
用户是 wheel
组的成员。
使用 sudo 命令进行授权
在我看来,使用 sudo
系统是一种更加安全的授权管理员权限的方式。该系统通过 sudo
命令前缀来执行管理命令,并通过 /etc/sudoers
文件进行细粒度授权。
一旦用户被信任,并且任务被委派到他们的 /etc/sudoers
文件中,他们就可以使用 sudo
执行分配给他们的命令。基本命令语法如下:
$ sudo <command>
在前面的示例中,<command>
将被替换为通常保留给 root 用户的管理命令,如以下命令所示:
$ sudo useradd bob
前面列出的命令字符串允许一个受信任的用户创建一个新的用户账户:bob
。当第一次使用sudo
运行命令时,系统会提示用户输入密码。系统默认缓存用户凭证 5 分钟。这样,如果他们需要在短时间内多次使用sudo
作为 root 用户执行多个命令,他们只会被提示一次输入密码。
使用sudo
时,我们不需要向管理员泄露 root 用户的密码,也不需要将特定命令或一组命令委托给个人或组。
要委托一个名为sally
的用户能够运行useradd
命令以及passwd
命令,可以在/etc/sudoers
文件中添加一个条目。我们还可以防止sally
在同一条目中更改 root 密码。该条目类似于以下命令:
sally ALL=(root) /sbin/useradd, /bin/passwd , !/bin/passwd root
编辑应以root
身份通过visudo
命令进行。通过这种方式,在保存更改之前会先进行验证(防止文件损坏)。更多详细的配置示例可以通过查看man
页面获得:
$ man sudoers
默认情况下,sudo
允许wheel
管理组的成员运行所有命令,而无需额外的管理操作。
为了提升安全性,以便在每次执行sudo
命令时获取用户密码并覆盖默认的 5 分钟超时设置,请使用visudo
并在/etc/sudoers
文件中添加以下行:
Default timestamp_timeout=0
在本书的其余部分,管理命令将作为标准用户运行,并以sudo
命令为前缀。该用户将是wheel
组的成员。通过这种方式,我们希望将最佳实践和安全性作为您思考的核心。
使用 ip 和 hostnamectl
许多 Linux 管理员习惯使用ifconfig
命令来显示和设置 Linux 主机上的 IP 地址。尽管ifconfig
命令仍然有效,但它已被标记为过时,推荐使用ip
命令。对于从 Windows 迁移到 Linux 的管理员来说,使用ifconfig
成为了显而易见的选择。由于ipconfig
与 Windows 命令行非常相似,我鼓励您学习并掌握持续更新的ip
命令及其功能。在 RHEL 7 上使用ifconfig
或ip
命令还会引入新的、统一的设备名称。这对那些习惯于/dev/eth0
的人来说,可能会有些冲击。
最后,我们将探讨使用hostnamectl
命令来设置 RHEL 中新颖的功能。该命令可以用于设置当前会话的hostname
,并且一次性永久生效,而无需使用hostname
命令并编辑/etc/hostname
文件。
网络设备的一致命名
在我们服务器和桌面计算机上使用的硬件中,现在可以看到更多的多端口接口卡和LOM(板载局域网)接口。如果你依赖更传统的eth0
和eth1
命名方案,所有这些将导致网络设备命名不一致。
在 RHEL 7 及相关的类似发行版中,udev
支持多种不同的网络设备命名方案。默认情况下,系统会根据固件、拓扑和设备本身返回的位置信息来分配固定名称。这样,命名与物理设备本身相关,即使更换了故障硬件,名称仍然保持一致且可预测。我们需要实现的是避免eth0
设备变成eth1
,反之亦然。缺点是名称可能较长,不易记住。参考我们在本书中将使用的 RHEL 7.1 系统,VMWare 托管系统上的单一以太网接口被命名为eno16777736
。
命名方面由systemd
(新的初始化守护进程)管理,并在启动阶段检测硬件时支持以下命名方案:
-
方案 1:此方案指定的名称可以包含从板载设备返回的固件或 BIOS 信息。这些名称可以采用
enoxxx
(字母o
代表板载设备)的形式。如果失败,命名系统将回退到方案 2。 -
方案 2:此方案指定的名称可以包含从 PCI Express 插槽卡返回的固件或 BIOS 信息。这些名称可以采用
ensxxx
的形式。如果失败,命名系统将回退到方案 3。 -
方案 3:此方案指定的名称可以包含连接器的物理位置——例如主板上的插槽地址。这些名称可以采用
enpxxx
的形式。如果失败,命名系统将回退到方案 5(注意方案 4 是可选的)。 -
方案 4:此方案基于MAC(媒体访问控制)地址识别名称,该地址来自NIC(网络接口卡),并由管理员通过在网络配置文件中设置
HWADDR
(硬件地址)属性进行选择。这些名称采用在接口配置文件中DEVICE
属性中提供的名称。例如,如果你想将一个 LOM 接口卡从eno16777736
重命名为internal
,在以 root 身份工作时,你需要编辑/etc/sysconfig/network-scripts/ifcfg-eno16777736
文件。你需要添加HWADDR
属性,并编辑DEVICE
属性,使文件内容类似以下摘录:HWADDR="00:0c:29:57:ef:c4" #Using the MAC address for your NICDEVICE="internal"
-
方案 5:如果所有其他方法都失败,命名系统将回退到传统的内核不可预测命名方案,如
eth0
、eth1
等。
总结一下,每个接口设备通常会有一个两字符的前缀。这个前缀表示 NIC 的协议类型。以下是这些前缀的示例:
-
en
:这表示以太网 -
wl
:这表示无线局域网 -
ww
:这表示广域无线
前缀后面的字符表示使用的命名方案和检测到的硬件类型,如下表所示:
设备名称的位置 3 | 描述 |
---|---|
o |
这是板载设备 |
s |
这是热插拔插槽 |
p |
这是 PCI 或 USB 设备 |
一个实际的网络设备命名示例
为了展示我们迄今为止在物理机和虚拟机上使用的一致的网络设备命名系统,我们将走出我的戴尔笔记本电脑,它运行的是 Fedora 21 工作站。它有一个有线网卡(当前未连接)和一个无线端口(这是活动连接)。使用ip address show
命令,我们可以看到两个物理接口和本地或loopback
接口:
当我们查看设备名称并忽略本地接口lo
时,我们看到接口2
为enp9s0
,接口3
为wlp12s0
。
对于接口 2:
-
有线以太网是
en
-
PCI 总线地址是
p9
-
插槽编号是
s0
我们可以使用lspci
命令查看这个 PCI 设备;命令和输出如下:
$ lspci | grep 09:00.0
09:00.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5755M Gigabit Ethernet PCI Express (rev 02)
我们可以看到这确实与命名方案中提到的物理设备相关(PCI 总线9
和以太网卡中的插槽0
)。
对于接口 3:
-
无线以太网是
wl
-
PCI 总线地址是
p12
-
插槽编号是
s0
再次使用lspci
和grep
命令,我们可以看到这个设备。PCI 总线(12
)的十六进制值在lspci
的输出中显示为0c
,这是因为使用了十六进制,而设备命名方案使用的是十进制值:
$ lspci | grep 0c:00.0
0c:00.0 Network controller: Intel Corporation PRO/Wireless 3945ABG [Golan] Network Connection (rev 02)
禁用一致的网络设备命名
为了简化,特别是当你只有单一接口时,你可以优先使用传统名称(eth0
)。你可能也有需要此命名方案的旧版软件。这些旧版名称仍然可以使用,正如你在使用命名方案 4 时所学到的。通过向网络配置文件中添加HWADDR
属性,并重命名/etc/sysconfig/network-scripts/ifcfg-eth0
文件,或者将DEVICE
名称属性配置为eth0
,可以帮助你实现目标。
要在系统中全局设置此配置,以便所有接口都应用,您需要在启动时使用附加的内核参数。这可以通过在/etc/default/grub
文件中设置GRUB2
来实现。GRUB_CMDLINE_LINUX
行应更改为以下代码,并附加biosdevname
和net.ifname
命令:
$ cat /etc/default/grub
编辑并保存文件后,我们可以使用以下命令更新GRUB2
配置:
$ sudo grub2-mkconfig -o /boot/grub/grub.cfg
然后我们需要重启系统,以便查看接口名称的变化:
$ sudo shutdown -r now
提示
强烈建议坚持使用一致的名称,并接受这种命名方案,它解决了传统内核名称以前给管理员带来的设备命名不一致问题。
在本书的其余部分,我们将使用与 RHEL 7.1 系统上单个 NIC 相关的标准命名系统,即 eno16777736
。
使用 ip 命令显示配置
本章开头我们提到,RHEL 7.1 中显示和配置 IP 地址的首选命令是 ip
。ip
命令是 iproute
RPM 包的一部分,取代了现在已经过时的 ifconfig
命令,后者属于 net-tools RPM 包。ifconfig
命令仍然可以使用,但建议使用 ip
。
我们可以使用 ip
命令的 address show
选项来显示所有接口的 IP 地址。这可以通过以下三种方式之一来实现:
-
$ ip address show
-
$ ip a s
-
$ ip a
我们首先使用详细选项,其中使用了完整的 address show
命令。可以将其缩写为 a s
,或者由于地址命令的默认动作是 show
,只需使用 ip a
。稍微扩展一下,我们可以仅显示单个接口或单个协议的 IP 地址,如下所示:
$ ip a s eno1677736
$ ip -4 a s eno1677736
$ ip -6 a s eno1677736
下图显示了在演示系统中查看配置的网络接口卡(NIC)的 IPv4 地址时,命令及其输出:
提示
scope global dynamic eno16777766
输出中第三行使用了动态一词,表示该地址是通过 DHCP(动态主机配置协议)分配的。
要查看此同一接口的传输统计信息,我们切换到 link
选项,如以下命令行和输出所示:
$ ip -s link show eno16777736
我们已经开始感受到这个命令的灵活性,但我们不仅限于 link
和 address
选项。在接下来的命令中,我们首先查看路由表,然后查看 ARP(地址解析协议)缓存。每个命令都展示了详细形式和简化形式。简化形式特别有用,如果你不会拼写 neighbor(邻居)的话:
$ ip route show
$ ip r
$ ip neighbor show
$ ip n
提示
ARP 缓存显示了你连接到同一网络中设备的 MAC 地址。
使用 ip 命令实现配置更改
作为展示配置信息的得力工具,ip
命令在更改 IP 地址的动态配置时也非常擅长,这次使用 add
来代替 show
。例如,要向我们的接口添加一个额外的 IPv4 地址,我们将使用以下命令:
$ sudo ip address add 192.168.140.3/24 dev eno16777736
我们现在可以使用之前查看过的show
命令来查看这些信息,如下所示的命令和输出:
$ ip -4 a s eno16777736
当我们仔细查看输出时,可以看到之前的 DHCP 地址和我们刚刚应用的附加地址。虽然这些设置仅针对本次会话添加,但在网络或接口重启时,我们将恢复为单一的 DHCP 分配地址。
要使用网络服务重启所有接口,我们将使用以下命令:
$ sudo systemctl restart network.service
如果系统中有多个接口,并且我们正在使用 NetworkManager 服务(默认接口),我们可以使用以下命令停止并启动单个接口:
$ sudo nmcli dev disconnect eno16777736
$ sudo nmcli con up ifname eno16777736
提示
本章稍后将有更多关于nmcli
命令的信息。
因此,尽管我们可以动态地向运行中的系统添加 IP 地址,但如果我们希望更改永久生效,那么我们需要将配置添加到配置文件中。
持久化网络配置更改
要从我们在演示 RHEL 7.1 系统上使用的 DHCP 分配地址更改为静态地址,我们将在与/etc/sysconfig/network-scripts/ifcfg-eno16777736
接口相关的网络配置文件中分配一个静态地址。要编辑文本文件,你可以使用你喜欢的文本编辑器:vi
或nano
;在这里,我们将使用vi
:
$ sudo vi /etc/sysconfig/network-scripts/ifcfg-eno16777736
在前面的命令行中编辑文件后,它应类似于以下文件内容。当然,根据你的网络相关信息进行设置,而不是 IP 地址,我们使用:
TYPE="Ethernet"
BOOTPROTO="none" #Change from dhcp to none
DEVICE="eno16777736" #use your device name
ONBOOT="yes"
PEERDNS="yes"
PEERROUTES="yes"
IPADDR="192.168.40.3" #use the IP Address that you want to assign
NETMASK="255.255.255.0" #Use the appropriate subnet mask
DNS1="192.168.40.2" #the address or your DNS server
GATEWAY="192.168.40.2" #the default gateway to use
DEFROUTE="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_FAILURE_FATAL="no"
NAME="eno16777736" #use your device name
UUID="980c9e81-f018-42ae-9272-1233873f9135" #use your device UUID
IPV6_PEERDNS="yes"
IPV6_PEERROUTES="yes"
IPV6_PRIVACY="no"
一如既往,编辑文件时要小心。实际上,文件中的大部分内容可以保持不变,因为我们只编辑了以下一行:
BOOTPROTO="none"
添加四行新内容:
IPADDR="192.168.40.3" #use the IP Address that you want to assign
NETMASK="255.255.255.0" #Use the appropriate subnet mask
DNS1="192.168.40.2" #the address or your DNS server
GATEWAY="192.168.40.2" #the default gateway to use
在做出更改并保存后,我们需要使用以下命令刷新 NetworkManager:
$ sudo nmcli connection reload
这将重新缓存网络配置文件。完成此操作后,我们可以停止并启动以下接口:
$ sudo nmcli dev disconnect eno16777736
$ sudo nmcli con up ifname eno16777736
或者,像之前一样重新启动网络服务。这个单一命令替代了这里的三个命令。但是,它会中断所有接口。因此,以下命令应该仅在我们只有单一接口时使用:
$ sudo systemctl restart network.service
现在,我们已经为接口配置了静态 IPv4 地址,我们会发现从ip address show
命令的输出中失去了dynamic
关键字:
$ ip -4 a s eno16777736
我们现在已经看到如何通过命令行和配置文件成功配置 IPv4 设置。接下来,我们将进入网络配置的最后部分:主机名。
使用 hostnamectl 配置 RHEL 7 的主机名
随着systemd
在 RHEL 7 及其衍生版上的出现,我们有了一种全新的方式来使用hostnamectl
命令显示和设置主机名。这个工具的优势是可以一步配置静态名称和临时名称。
我们将编辑 /etc/hostname
文件,并添加新的静态主机名。然后,内核在系统启动时读取它,并显示为瞬时主机名,这经常作为您的 BASH
shell 提示的一部分使用。可以使用 hostname
命令显示和设置瞬时主机名。这是一个两部分过程:使用 hostname
设置内核维护的瞬时名称,并编辑 /etc/hostname
文件以确保它在重新启动后保持不变。
在 RHEL 7
中,我们有这两个主机名和第三个主机名:漂亮名称。漂亮名称可以显示 UTF-8
字符,允许您嵌入空格和撇号。设置漂亮名称时,它将存储在 /etc/machine-info
文件中。
要显示配置的主机名,可以使用 hostnamectl
命令。如果配置的主机名包含不能构成静态主机名的字符,则只会显示漂亮名称。同样,如果使用漂亮名称存储与 /etc/hostname
文件不兼容的名称,则 /etc/machine-info
文件将存在。
要以标准用户身份显示主机名,可以发出以下命令:
$ hostnamectl
如果主机名中包含空格,则 漂亮主机名 将显示出来。漂亮 和 静态 名称与分别对应 /etc/machine-info
和 /etc/hostname
文件,并可用于以下命令:
$ cat /etc/machine-info /etc/hostname
上述命令行的输出结果如下:
PRETTY_HOSTNAME="Red Hat 7-1.tup.com"
redhat7-1.tup.com
要使用 hostnamectl
配置主机名,我们使用 set-name
选项,如以下命令所示。如果用户是 wheel
管理组的成员,则此命令无需以 sudo
为前缀,但用户将被提示输入密码。这些权限是使用策略工具包配置的:
$ hostnamectl set-hostname "Red Hat 7.tup.com"
这将设置所有三个名称;要查看瞬时名称,应通过运行 bash
命令初始化新的 shell。要设置单个名称,请按照以下 hostnamectl
命令正确使用选项:
--transient
--static
--pretty
简介 Red Hat NetworkManager
自 RHEL 6
版本起,NetworkManager
服务已成为 RHEL
的一部分,最简单的形式允许用户配置网络配置设置(如加入 Wi-Fi 网络)。当然,考虑到使用 Fedora 或 RHEL 笔记本电脑的用户,这真的是非常必要的。此服务远远超出了 GUI,适用于安装有或没有 X 服务器环境的服务器产品。
随 RHEL 7 发布的 NetworkManager
服务是一个动态网络控制与配置守护进程,旨在保持网络接口在可用时处于活动状态。正如我们所看到的,NetworkManager
服务不仅维护对传统 ifcfg-
文件类型的支持,还扩展了对其他配置文件的支持。通过这种方式,我们可以轻松为你的笔记本配置静态 IP 地址,以适应你可能访问的不同办公室,而不必依赖每个站点的 DHCP。
NetworkManager
服务的配置可以通过 GUI 控制中心或 nmtui
命令行菜单进行维护。我们还看到,我们可以避免使用菜单,通过命令行启用脚本事件,使用 nmcli
命令。
要查询 NetworkManager
服务的状态,我们可以使用 systemctl
工具,如下所示的命令及相关输出截图:
$ sudo systemctl status NetworkManager.service
用户和管理员可以通过以下工具与 NetworkManager 服务进行交互:
-
GNOME 通知区域图标
-
GNOME 网络设置控制中心
-
nmtui
菜单 -
nmcli
命令行工具
使用控制中心与 NetworkManager 进行交互
如果你在图形化环境下使用 RHEL、CentOS 或 Fedora,那么通过 GNOME 控制中心,我们可以与 NetworkManager 服务进行交互。我们也可以通过通知区域图标访问网络设置。这可以在下面的 RHEL 7.1 系统截图中看到:
要通过控制中心访问相同内容,我们可以使用 SUPER 键。在搜索对话框中输入 control network
,如下所示的截图:
一旦我们进入 网络设置,就可以通过传统的 飞行模式 简单地禁用所有无线接口。通过这种方式,你可以确保在起飞和降落时不会因网络问题而面临生命危险,同时还能享受 糖果传奇 游戏的乐趣。
在左侧面板中,我们可以查看当前已知的接口以及 网络代理 设置。在这里,如果需要,可以添加 Web 代理。在本书使用的 RHEL 7.1 系统中,我们看到左侧面板有两个网络接口组:
-
有线
-
未知
在图示中,有线接口代表我的千兆以太网卡,而未知接口代表本地回环连接。如果您的系统包括无线网卡,您可能还会看到Wi-Fi作为选项。在左侧面板中选择有线接口时,右侧面板将显示当前的网络配置文件。由于我们只有一个配置文件,因此该配置文件的名称未显示,但它代表我们在本章中先前配置的默认系统配置文件:eno16777736
。
在右侧面板的底部,我们可以通过添加配置文件按钮创建额外的配置文件,而右下角的齿轮图标将允许您更改当前配置文件的属性。以下截图展示了这一切:
通过控制中心添加新配置文件
对于移动系统,如笔记本电脑和平板电脑,我们可以配置配置文件,以便轻松加载与您使用设备的地点相关的网络配置信息。例如,如果您在家使用笔记本电脑,您可能会设置一个特定的静态 IP 地址,而在工作时,您可能会使用 DHCP 分配的地址。配置文件可以轻松且无缝地处理这种情况。
使用添加配置文件按钮从网络设置控制中心打开新配置文件对话框。在左侧面板中,我们可以从以下给定选项中选择一个:
-
安全性
-
身份
-
IPv4
-
IPv6
我们将为家庭网络创建一个新的 DHCP 配置文件;如果您记得,我们在之前的章节中使用位于/etc/sysconfig/network-scripts
目录下的传统ifcfg-脚本
设置了静态 IPv4 地址。我们将保留此设置,并允许我们根据需要在静态地址和 DHCP 之间切换。
从左侧面板选择身份选项,我们将名称设置为home-DHCP
。从下拉列表中选择与我们希望分配给此配置文件的接口相关的MAC 地址。最后,我们可以取消选中自动连接复选框,这样默认连接将仍然是我们之前选择的静态分配。我们可以根据需要手动选择此配置文件。其他所有设置保持不变,包括IPv4和IPv6设置中的自动 DHCP 地址分配。导航到对话框右下角并选择添加按钮来创建配置文件。以下截图展示了我们选择的设置:
创建新配置文件后,我们可以轻松在两个配置文件之间进行选择,利用 GNOME 通知面板,简化了不同网络间的切换。以下截图显示了当前选中的eno16777736配置文件,以及如何切换到新创建的home-DHCP配置文件:
现在,我们已经看到如何使用图形工具在 RHEL 7.1 上设置网络配置文件信息。对于没有 X 服务器的 RHEL 或 Fedora 系统,用户可以通过nmtui
ncurses 菜单轻松管理NetworkManager
连接。
使用 nmtui 与 NetworkManager 交互
就像在 GNOME 控制中心中的 GUI 配置文件管理一样,我们也可以使用nmtui
命令提供的文本用户界面。这是由 ncurses 系统提供的传统蓝色屏幕命令行菜单。如果系统中没有该命令,可以通过yum
进行安装,以下命令演示了安装方式:
$ sudo yum install NetworkManager-tui
安装完成后,可以使用以下命令访问 NetworkManager 菜单:
$ sudo nmtui
如果你通过 PuTTY 使用 SSH 连接到服务器,为了确保菜单边框显示正确,你应该将字符集翻译选项设置为UTF-8。这个设置可以在连接设置中的窗口|翻译找到。
本书中使用的 RHEL 7.1 系统显示的NetworkManager菜单看起来干净简洁,虽然有点简单,以下截图展示了该菜单:
nmtui
命令还提供了快捷方式,能快速执行菜单中的特定任务。这些快捷方式包括nmtui-edit
、nmtui-connect
和nmtui-hostname
命令。前两个命令适用于你已经知道要激活或编辑的连接配置文件名称,而最后一个命令则用于全局设置主机名。
要激活我们之前创建的 home-DHCP 配置文件,我们将发出以下命令:
$ nmtui-connect home-DHCP
这将有效地将静态 IP 地址切换为自动分配的 DHCP 地址。你应该从控制台发出此命令,而不是远程操作,因为地址从静态切换到 DHCP 后,你的连接会丢失。
如果你足够“极客”,并在星巴克使用命令行版 Fedora,也可以使用这个命令来连接到新的 Wi-Fi SSID:
$ nmtui-connect Coffee-Shop-Wifi
要更改相同连接配置文件的属性,我们将使用以下命令:
$ sudo nmtui-edit home-DHCP
这将打开 homeDHCP 连接配置文件的属性页面,准备进行编辑。如果你想打开主机名菜单页面以进行编辑,可以使用以下命令:
$ sudo nmtui-hostname
使用 nmcli 与 NetworkManager 进行高级交互
对于那些认为 Linux 唯一的真正形式是没有菜单辅助的,只能依靠通过你那位绝地父母传授的智慧的人,我们为你们准备了极限运动 nmcli
。开个玩笑,使用这种不依赖菜单互动的方式与 NetworkManager
配合工作,将让你能够在脚本中做出更改,进而可以在多个系统中实施。
作为简单的入门,我们可以使用 nmcli
扫描可用的 Wi-Fi 网络;输出应显示 Wi-Fi SSID 和信号强度,如下所示:
$ nmcli device wifi list
与我们之前使用 iw
命令显示 SSID 的传统命令行机制相比,这个过程大大简化了:
$ sudo iw wlp12s0 scan | grep SSID
SSID: hobbit
SSID: virginmedia1671684
SSID: VM260970-2G
SSID: virginmedia9066074
SSID: Edinburgh2013
SSID: TALKTALK-4C89F0
使用 nmcli
的过程对我们来说简化了,因为 NetworkManager
可以使用已配置的 polkit
权限。这些权限或操作(使用 polkit
语言)由系统管理员配置,并不打算由用户更改。策略文件位于 /usr/share/polkit-1/actions/org.freedesktop.NetworkManager.policy
位置。
我们可以使用 nmcli
显示已配置的权限,命令如下:
$ nmcli general permissions
如果我们希望能够在有线接口启动并可用时创建一个连接,我们可以通过 nmcli
来实现。这个过程也可以根据需要轻松地在多个设备上编写脚本。首先,我们创建连接配置文件,如下所示命令和输出:
$ sudo nmcli connection add con-name wired-home \
ifname enp9s0 type ethernet ip4 192.168.0.8 gw4 192.168.0.1
Connection 'wired-home' (e17cb6b7-685f-4cf2-9e8b-16cbfae1f73a) successfully added.
当你知道启用了自动完成,甚至是对于我们作为 ifname
值添加的子命令和值(如 enp9s0
)也会自动完成时,这个命令就大大简化了。
为了完成任务,我们需要向连接配置文件添加 DNS 配置,这可以通过以下命令实现:
$ sudo nmcli connection modify wired-home ipv4.dns "192.168.0.3 8.8.8.8"
我们现在可以通过以下命令显示属性:
$ nmcli -p connection show wired-home
这里使用的 -p
选项是为了得到漂亮的输出;对于简洁的输出,可以使用 -t
。无论如何,输出都太冗长,无法作为书中的一部分展示。
我们现在已经复制了创建连接配置文件的过程,这是我们在使用控制中心时第一次看到的。我们并不认为这很简单,但能够通过脚本完成这一过程提供了许多不可通过任何形式的交互式菜单(无论是 nmtui
的文本菜单还是控制中心的 GUI)实现的选项。
总结
在这一章中,我们实际上已经建立了理解 RHEL 7 系列网络的基础知识。首先,你学会了如何使用 su
和 sudo
在 RHEL 上获取和管理权限。此外,我们还看了如何通过 PAM 限制 su
的使用,仅限于 wheel
组成员。我们也开始了我们将继续采用的管理方式,使用 sudo
来管理管理任务,而不是登录为 root 或使用 su
。
在建立了正确设置的基础知识后,我们开始理解 Red Hat 发行版上网络设备的新命名约定。在转向网络配置之前,我们了解了相对传统命名更受欢迎的原因。
要配置网络接口,我们可以使用传统的 ifcfg-
脚本,默认情况下会使用这些脚本。我们可以扩展到其他网络配置文件,这对于连接到不同网络位置的移动设备(如笔记本电脑)可能是最有用的。我们看到可以通过从菜单到原始命令行工具的多种方式来配置这些内容。
接下来,我们将讨论如何配置关键网络服务,如 DNS、DHCP 和 SMTP。
第三章:配置关键网络服务
必须说,拥有一个网络是非常棒且充实的;然而,使用该网络的原因必须受到挑战并且得到理解。没有人会仅仅为了拥有一个网络而拥有网络,因此我们必须为我们的网络带来服务,以赋予其目的、意义和存在的理由。
当然,我们可以添加许多种类的服务,其中许多将在本书中涉及。首先,我们将关注以下主题:
-
DNS(名称解析)
-
DHCP(IP 地址分配)
-
NTP / PTP(时间服务)
-
SMTP(电子邮件)
域名系统
域名系统(DNS)服务器帮助我们将友好的计算机名称(如www.packtpub.com)解析为不太人性化的 IP 地址(如83.166.169.231
)。通过这种方式,人类只需猜测地址或 DNS 名称就能访问许多计算机系统。在 UNIX 计算机的早期,访问仅限于少数学术系统时,主机名是通过网络信息服务(NIS)分发的;一台中央计算机维护一个映射主机名到 IP 地址的文件。然后,这个文件被推送到客户端订阅者。尽管这种方法有效,但显然不可扩展。
1988 年,DNS 服务器的第一个版本由四位来自加利福尼亚大学伯克利分校(UCB)的研究生开发。这款软件目前由ISC(互联网系统联盟)维护。即使在今天,它仍然被称为伯克利互联网域名系统(BIND)。
在 Red Hat Enterprise Linux 7.1 中,随附的 DNS 服务器或 bind 版本为 9.9.4。安装完成后,可以通过输入以下命令来验证版本:
$ named -V
DNS 服务器可以安装并运行,而无需更改配置。在这种模式下,它将作为仅本地缓存服务器运行。当以这种方式操作时,服务器可以解析互联网中的名称,但不会托管任何自己的记录。此外,在没有配置更改的设置中,只有本地主机能够查询服务器。在其最简单的形式下,缓存-only 服务器仍然有用,但为了使其有用,至少需要打开访问控制列表,以允许来自本地网络的查询。通过这种方式,局域网中的计算机可以从本地服务器解析名称。仅有一个服务器需要访问互联网进行名称解析,从而减少了组织的互联网足迹。
安装和配置仅缓存 DNS 服务器
首先,我们将在系统上安装bind
软件包,并将其配置为解析本地网络中主机的名称。除了对配置文件进行简单修改外,其他操作几乎不需要,但这将帮助我们入门。
要从控制台安装bind
,请输入以下命令:
$ sudo yum install bind
安装完软件包后,我们现在需要编辑/etc/named.conf
配置文件:
$ sudo vi /etc/named.conf
我们将按如下方式编辑三个现有的行:
-
listen-on port 53 { 127.0.0.1; };
更改为以下内容:listen-on port 53 { any; };
-
listen-on-v6 port 53 { ::1; };
更改为以下内容:listen-on-v6 port 53 { none; };
-
allow-query { localhost; };
更改为以下内容:allow-query { localhost; 192.168.40.0/24; };
我们所做的更改在此解释:
-
允许 DNS 服务器监听所有 IPv4 接口。
-
禁止 DNS 服务器监听 IPv6,除非您需要它监听 IPv6。
-
允许来自我本地网络的查询
-
调整地址范围以匹配您的网络和子网掩码。
进行这些更改后,我们将能够保存它们。在启动服务器之前,我们可以测试这些更改。为此,我们将使用以下命令:
$ sudo named-checkconf
输出应该是静默的。只有在配置中出现错误时才会输出。如果您需要输出,您可以使用 -p
选项:
$ sudo named-checkconf -p
使用此选项,我们将看到由服务解释的配置选项。如果存在错误,我们只会看到错误,而不是配置。
我们现在可以使用 systemctl
命令启动并启用 DNS 服务;命令执行的顺序不重要:
$ sudo systemctl start named
$ sudo systemctl enable named
出于这些实验的目的,目前我们将不运行防火墙。您可以通过以下命令检查系统:
$ sudo systemctl status firewalld
如果它正在运行,那么要么停止防火墙(如果不需要的话),要么启用 UDP 和 TCP 端口 53
上的 DNS 流量,允许它进入系统。要停止并禁用防火墙,请使用以下命令:
$ sudo systemctl stop firewalld
$ sudo systemctl disable firewalld
从本地系统,我们可以使用我们的 DNS 服务器测试名称解析;我们将使用我们服务器的 IP 地址来证明它能从网络中访问,而不仅仅是从本地别名访问。您可以使用相同的命令并确保序列末尾的@address
指向您的服务器地址:
$ dig www.packtpub.com @192.168.40.3
提示
在这里,使用您 DNS 服务器的 IP 地址替代我们使用的地址。
您现在应该在输出中看到 ANSWER 部分,详细列出 Packt 服务器的地址。
如果您在同一网络上有其他系统,您可以使用相同的命令验证它是否确实适用于网络中的任何主机。
在这一阶段,我们将拥有一个配置为仅缓存模式的工作 DNS。
配置客户端使用此服务器
之前我们使用 dig
命令测试 DNS 查询时,我们硬编码了使用的名称服务器,忽略了客户端配置。传统上,客户端配置文件是 /etc/resolv.conf
。我们仍然可以使用它,但在更多现代的 Linux 系统中,这通常会被 NetworkManager 服务覆盖,该服务会从接口配置中读取名称服务器。这样,我们就可以为不同的连接配置文件使用不同的 DNS 服务器。当然,DNS 也适用于其他操作系统,包括您的 Microsoft Windows 台式机和 Apple iPhone。通常,DNS 服务器列表由 DHCP 服务(动态主机配置协议)分配,影响所有 DHCP 客户端的配置。稍后我们将在本章中讨论 DHCP。
在 RHEL 7.1 上,我们可以使用以下命令来显示活动连接:
$ nmcli con show active
我的活动连接显示为 eno16777736
;这与我们在第二章,配置网络设置中使用的系统相同,我们在该章节中讨论了现代 Linux 系统中一致的网络设备名称。
我们可以使用 nmcli
命令修改此配置的 DNS 服务器:
$ sudo nmcli con modify eno16777736 ipv4.dns "192.168.40.3"
如果正在修改的连接涉及传统的 ifcfg-
脚本,那么相应的文件也会通过前面的 nmcli
命令进行编辑。这就是我们修改的连接情况。我们可以通过查看 nmcli
修改的文件来查看更改:
$ sudo cat /etc/sysconfig/network-scripts/ifcfg-eno16777736
要实施更改,我们可以通过以下方式重新启动 NetworkManager
服务:
$ sudo systemctl restart NetworkManager
我们现在可以使用 dig 命令执行 DNS 查询,而无需指定服务器的地址:
$ dig www.packtpub.com
在这里,我们已经能够配置本地客户端,从本地配置的 DNS 服务器解析主机名。
提示
作为一种快速解决方案,编辑 /etc/resolv.conf
文件是可行的,但如果不通过 NetworkManager 服务进行更改,可能会被覆盖。
配置 DNS 区域
配置本地 DNS 服务器的一个原因是为本地服务器的名称查找提供集中式配置。接下来我们将介绍如何配置一个 DNS 区域,为本地服务器提供名称解析。我们将配置一个名为 tup.local.
的区域;使用本地域后缀将确保该区域仅在我们配置的 DNS 服务器上可用。
作为一个仅缓存的 DNS 服务器,我们目前没有托管 DNS 区域。DNS 区域只是一个文本文件,包含主机名与 IP 地址的映射。我们可以查看在 /etc/named.conf
文件中托管了哪些区域。区域文件存储在 /var/named
目录中。
引用 /etc/named.conf 中的区域
在 BIND 配置文件中,我们必须指向区域文件。以下示例演示了如何实现这一点:
使用以下命令打开 named.conf
:
$ sudo vi /etc/named.conf
在文件末尾添加以下部分:
zone "tup.local." IN {
type master;
file "named.tup";
};
提示
不要忘记域名后的点:tup.local
。
配置并保存文件后,我们可以像之前一样检查配置文件:
$ sudo named-checkconf
创建区域文件
我们已配置 DNS 服务器指向named.tup
文件。默认情况下,该文件需要存在于 RHEL 7.1 的/var/named/
目录中。该目录位置由named.conf
文件主选项中的目录指令决定。此摘录来自/etc/named.conf
文件,显示了目录根的配置:
directory "/var/named";
首先,创建一个空的区域文件,并设置权限,使其可以被 DNS 服务器读取。touch
命令将创建一个空文件。我们将权限设置为用户所有者可读/写,组所有者仅可读。最后,我们将文件的组所有者权限设置为named
(DNS 服务器使用的组):
$ sudo touch /var/named/named.tup
$ sudo chmod 640 /var/named/named.tup
$ sudo chgrp named /var/named/named.tup
文件和权限到位后,我们可以使用sudo
以 root 身份编辑文件,使其与以下截图类似:
我们可以逐行解释上面的截图如下:
-
$ORIGIN tup.local.
:这设置了 DNS 域名的名称,所有未以点号结尾的名称将附加此域名。 -
$TTL 1D
:该指令设置记录从缓存中检索的默认时间TTL(生存时间)。该值通常以秒为单位,但我们可以使用字母来定义更大的单位,例如H
表示小时,D
表示天,甚至W
表示周。也可以在单独的 DNS 记录中覆盖此默认值。TTL 值越大,传播更改到互联网的时间越长,因为记录可能会在 TTL 时间段内仍从缓存中提供。TTL 设置得越短,传播更改的时间越短,但服务器上的查询次数会增加。 -
@ IN SOA ns1 root ( … )
:这设置了**Start Of Authority**
(SOA)记录,适用于tup.local.
DNS 域名。@
符号表示该域,SOA 记录设置为ns1.tup.local
计算机。记住
\(ORIGIN`以及电子邮件联系地址是`root@tup.local.`。括号中包含了该域使用的序列号和超时时间。过期记录由`\)TTL`指令覆盖(如果它被使用)。 -
NS ns1
:这设置了域的名称服务器记录。如果我们有额外的从属服务器,可以在区域中添加更多的 NS 记录。 -
ns1 A 192.168.40.3
:这设置了ns1.tup.local.
服务器的 IP 地址映射到我们在系统中设置的 IP 地址。单个A
表示 IPv4 地址映射,而AAAA
表示 IPv6 地址映射。
编辑完成后,我们可以检查区域的语法和完整性。在命令行中,输入以下命令:
$ sudo named-checkzone tup.local. /var/named/named.tup
输出应类似于演示系统中的以下截图:
在编辑了/etc/named.conf
文件并实现了新的区域/var/named/named.tup
数据库文件后,我们现在准备重新启动服务。我们还使用named-checkconf
命令检查了named.conf
,并使用named-checkzone
命令检查了区域文件,因此我们可以放心我们所做的更改是完整的;考虑到这一点,我们将使用systemctl
重新启动服务:
$ sudo systemctl restart named
我们现在可以使用dig
或甚至只是ping
来检查区域的操作:
$ ping ns1.tup.local
$ dig ns1.tup.local
虽然我们只添加了一个记录,但没有任何阻止我们根据需要添加更多记录的原因。序列号通常会在区域更改后递增,但它更多地用于指示更改发生的时间。从属服务器应同步这些更改。如果你只有主服务器,则不需要递增序列号。本章后面,我们将添加一个MX或邮件交换记录,以支持在外部域中使用电子邮件传递。
配置 DHCP 服务器
DHCP(动态主机配置协议)服务器用于为网络主机分配 IP 地址,而不需要为每个主机静态分配记录。当然,这在客用设备——如手机和平板电脑——连接到网络时特别有用。
在 Red Hat Enterprise Linux 7 上,一个 DHCP 服务器可以同时提供 IPv4 和 IPv6 配置。每个配置都有自己的独立文件:IPv4 配置文件是/etc/dhcp/dhcpd.conf
,IPv6 配置文件是/etc/dhcpd/dhcpd6.conf
。此外,如果你习惯于以前版本的 Red Hat,不再需要在/etc/sysconfig/dhcp
中配置接口。RHEL 7 会自动监听所有与dhcpd.conf
或dhcpd6.conf
文件中子网定义匹配的接口。换句话说,DHCP 服务器监听 DHCP 请求的接口将匹配那些在定义的 DHCP 子网内有地址的接口。
提示
如果你没有在同一系统上配置 DNS 服务器,你需要在服务启动之前创建一个空的租约文件,如以下命令所示:
$ sudo touch /var/lib/dhcpd/dhcpd.leases
你可以使用以下命令行安装 DHCP 服务器:
$ sudo yum install dhcp
在这个示例中,我们将通过编辑/etc/dhcp/dhcpd.conf
文件来配置服务器发放 IPv4 地址配置。配置文件将是空的,除了几条注释。一个简单的配置如下所示,这将与我们在演示系统中使用的配置相匹配:
$ sudo vi /etc/dhcp/dhcpd.conf
生成的文件应类似于以下屏幕截图:
让我们逐行解释前面的屏幕截图:
-
option domain-name "tup.local";
:这将配置要附加到客户端名称的域名。 -
option domain-name-servers 192.168.40.3;
:这设置了 DNS 服务器。如果使用多个服务器,地址之间用空格分隔。 -
default-lease-time 86400;
:这是一个以秒为单位的值,我们用它来设置 DHCP 租约时间。值86400
表示一天。最大租约时间是类似的,但仅在客户端请求租约时间时使用。 -
ddns-update-style none;
:此命令禁用动态 DNS,根据该设置,DHCP 服务器将为获得 IP 地址的客户端创建 DNS 记录。 -
log-facility local7;
:此命令设置 DHCP 使用的 syslog 功能。日志文件中的条目将显示为来自local7
功能。DHCP 没有自己的功能条目。 -
接下来是子网定义,我们在此描述网络、子网掩码、地址以及将为子网发放的选项。
完成此操作后,我们应该熟悉接下来的步骤。在此,我们使用系统启动并启用该服务:
$ sudo systemctl enable dhcpd
$ sudo systemctl start dhcpd
提示
如果你在一个已经存在 DHCP 服务器的网络上运行,需要确保在启动自己的服务器之前停止该服务器。这包括可以通过其自己的 DHCP 服务在其私人网络上发放地址的 VMware Player 和 Virtual Box 等应用程序。如有需要,请参考你的虚拟化软件获取更多帮助。
如果你的网络中有额外的 RHEL 7 客户端,可以通过以下命令强制它们续订 DHCP 租约:
$ sudo dhclient -r <interface>
下面是一个示例:
$ sudo dhclient -r enp12s0
上述命令将会续订插入到 PCI 总线地址为 12
且插槽为 0
的以太网 PCI 卡上的 DHCP 租约。如果你的服务器已经发放了租约,你可以通过以下命令检查租约文件。该文件可以作为普通用户读取:
$ cat /var/lib/dhcpd/dhcpd.leases
以下截图显示了来自演示 RHEL 7.1 系统的租约,以及名为 trusty
主机的租约:
在配置了 DHCP 服务器后,我们将继续了解如何使用 DNS 和 DHCP 服务器配置基本的网络服务。我们会通过查看时间服务使其更加完善。
在 RHEL 7 上配置时间服务
网络中的一个基本服务是提供准确的时间。这对于与 Active Directory 或其他基于 Kerberos 的机制进行身份验证以及使日志文件中的时间戳具有实际意义是必需的。
网络时间协议(NTP)使用 UDP 协议和传统的端口 123
。该协议规定自 1900 年 1 月 1 日午夜以来已经过去的秒数。NTP 是 32 位的,这意味着最大时间将于 2036 年到达;然而,由于仅使用时间戳之间的差异,而不是实际时间,因此日期不会出现问题,不像 UNIX 时间在 2038 年结束时会出现较为严重的问题。
一旦时间服务启动并与另一个时间源同步,只要时间被认为是正常的,客户端时间就可以调整为与服务器的时间匹配。异常时间指的是服务器提供的时间与客户端之间的偏差超过 1000 秒。通过这种时间正常性,客户端可以防止与不可信的时间源同步。
也可以部署 精确时间协议 (PTP)。这需要硬件或软件支持,支持的设备包括 网络接口卡 (NIC)。首先,我们将查看常见的 NTP 实现,然后再讨论 PTP。
在 RHEL 7 上提供 NTP 服务可以通过 chronyd
或 ntpd
守护进程本身实现。默认情况下启用 chronyd
守护进程;然而,这主要是为桌面和经常与网络断开连接的机器设计的。chronyd
的时间同步速度比 ntpd
快,因此更适合那些频繁启动或经常挂起的机器。由于 ntpd
支持更多的认证选项并能在网络上传播时间,因此仍然是服务器的首选。
为了在时间服务器不可用时也能保持准确时间,chronyd
和 ntpd
都可以实现漂移文件。漂移文件保存在客户端,并显示本地硬件时钟与远程时间源频率之间的偏移。
NTP 基于一个服务器层次结构。每个服务器都分配一个层次编号。可能的起始值范围从 0 到 15。层次 16 表示时间服务不可用。层次 0 的时间服务器从物理时间源(如 GPS 时钟或原子钟)获取时间。层次 1 的服务器从层次 0 服务器获取时间,以此类推。
实现 chronyd
如本节介绍所提到的,chronyd
在我们为本书使用的 RHEL 7.1 系统上默认启用。我们可以在以下截图中查看该服务的 systemctl
状态子命令输出:
从之前的 sudo systemctl status
输出中可以看到,chronyd
被描述为一个 NTP 客户端/服务器。这与我们从传统的 ntpd
守护进程中预期的类似。如果 chronyd
也作为服务运行,那么我们应该监听 123
端口。我们可以使用 netstat
命令来显示这一点。如果我们使用根权限执行该命令,我们还会看到持有该端口的服务是 chronyd
。-p
选项显示此信息,但需要根权限。
我们运行以下命令:
$ sudo netstat -aunp | grep 123
输出如以下截图所示:
超越systemctl status
输出所能显示的内容,我们可以使用chronyc
命令行工具查看更多关于chronyd
同步状态的细节。希望你现在能看到命名中的模式:chronyd
是服务或守护进程,而chronyc
是命令行工具。
为了显示选定的服务器以及同步状态,我们将使用以下命令:
$ chronyc tracking
以下截图显示了chronyc
命令的输出。如果仔细观察输出,你会看到命令及其后续的输出:
Reference ID
显示了当前用于同步时间的服务器。Frequency
显示为ppm
(百万分之一)。这表示系统时钟在未同步的情况下可能出错的速率。这里的值0.8
意味着,如果 1,000,000 秒内没有同步,时钟将有0.8
秒的误差;这并不算太糟。Update Interval
显示了我们当前同步的频率。这个值比ntpd
限制的 2 ^ 10 秒(1024 秒)要大得多。鉴于我们时钟的精度,这个间隔是完全可以接受的。
使用以下命令,我们可以显示所有已配置的同步服务器:
$ chronyc sources
如果我们希望在屏幕上显示列的含义,可以使用-v
选项。输出结果如下所示:
如果我们想要与自己的本地时间服务器同步,可以编辑/etc/chrony.conf
配置文件。合理的做法是将你网络中的一个服务器分配为时间服务器,并将该服务器作为网络的时间参考。然后,这个单独的服务器可以与外部时间源同步。这样可以确保网络中所有服务器的时间准确,同时保持较小的互联网带宽,从而限制需要访问互联网时间服务器的服务器数量。
配置文件中的server
指令定义了潜在的同步伙伴。为了偏向本地服务器,可以使用prefer
选项。以下截图展示了我编辑后的文件,在文件中我保留了原始条目中的一个,并添加了自己的本地时间源:
为了使任何更改生效,应该重启chronyd
服务:
$ sudo systemctl restart chronyd
同步频率将从较低的频率开始,逐渐上升,直到匹配偏移频率的精度。在重启后不久,检查chronyc
跟踪的输出会显示Update Frequency
为 64 秒;然而,随着时间的推移,这个间隔会增加。
实现 ntpd
如果你想恢复到传统的ntpd
守护进程提供时间服务,你可以安装ntp
包。传统包的主要优点是广播选项和更强的ACL(访问控制列表)。要安装该包,可以使用以下命令:
$ sudo yum install ntp
配置文件位于/etc/ntp.conf
,它与chrony
文件不同。当我们像编辑chrony.conf
一样编辑ntp.conf
时,我们将能够像之前一样配置本地服务器,如下图所示:
我们在此找到的额外 ACL 是使用restrict
关键字定义的。虽然本地主机地址不受任何限制,但其他所有人都受到默认限制的影响。
默认的限制在此处进行了解释:
-
nomodify
:这阻止通过 NTP 协议对配置进行任何更改。 -
notrap
:这阻止了ntpdc
控制陷阱,控制陷阱是由远程日志程序使用的。 -
nopeer
:这阻止了对等关联的创建,其中每个对等方都会进行 50%的调整。 -
noquery
:这可以防止访问时间服务器状态的信息。不要将其与阻止访问时间服务器混淆。此选项仍然允许客户端进行正常的 NTP 时间查询。如果你想限制对时间服务的访问,请使用noserve
。
当你对/etc/ntp.conf
文件中的更改感到满意时,我们需要首先停止并禁用chronyd
服务,然后启用并启动ntpd
服务:
$ sudo systemctl stop chronyd
$ sudo systemctl disable chronyd
$ sudo systemctl enable ntpd
$ sudo systemctl start ntpd
要查询服务的状态,我们将使用ntpq -p
命令。输出如下图所示,指示在我的系统中用于同步的服务器是192.168.0.3
(由星号标记):
在 RHEL 7 上实现 PTP
精确时间协议 (PTP) 与 NTP 一样用于在网络上同步时钟,但与 NTP 不同,如果 PTP 与硬件支持一起使用,可以实现亚微秒级的精度;PTP 的支持来自于你的 NIC,可以是软件或硬件形式。linuxptp
包提供了ptp4l
和phc2sys
程序(Linux 的 PTP 和物理时钟到系统时钟)。然而,phc2sys
仅在硬件时间戳情况下需要。虽然在 NIC 内实现,但许多网络组件(包括交换机)支持软件或硬件模式的 PTP,从而使服务器能够与其交换机同步时间。这个过程比传统的 NTP 更加自动化,并且每个时钟运行最好的主控软件,能够选择最佳主机进行同步。它最适合企业网络,因为交换机会经常为它们所服务的设备提供时间。
许多网络接口卡(NIC)支持软件时间戳,但要查询自己的接口,请使用以下命令,它将显示所选接口的时间戳能力:
$ ethtool -T eno16777736
调整前面的命令以匹配您希望查询的接口。对于软件支持,我们需要在输出中找到以下行:
-
SOF_TIMESTAMPING_SOFTWARE
-
SOF_TIMESTAMPING_TX_SOFTWARE
-
SOF_TIMESTAMPING_RX_SOFTWARE
对于硬件支持,我们需要以下选项:
-
SOF_TIMESTAMPING_RAW_HARDWARE
-
SOF_TIMESTAMPING_TX_HARDWARE
-
SOF_TIMESTAMPING_RX_HARDWARE
我系统的输出显示如下截图:
要在系统上安装 linuxptp
,我们可以使用以下命令从标准 Red Hat 仓库进行安装:
$ sudo yum install linuxptp
服务可以通过命令行进行简单测试。但在此之前,我们应该停止正在运行的 NTP。以下命令展示了这一操作:
$ sudo systemctl disable ntpd
$ sudo systemctl stop ntpd
要从命令行启动服务,而不是作为守护进程运行,我们可以使用以下命令验证它是否正常工作:
$ sudo ptp4l -i eno16777736 -m -S
这会使用我们通过 -i
选项指定的接口启动服务,我们通过 -m
选项要求将输出显示到屏幕上,并通过 -S
选项设置软件模式。
服务将在 UDP 端口 319
和 320
上监听。以下截图显示了该命令的输出:
一旦我们对配置感到满意,我们可以创建启动配置文件并调整 sysconfig
文件。我们将从 sysconfig
文件开始:
$ sudo vi /etc/sysconfig/ptp4l
删除行尾的接口选项。文件应与以下截图相同:
接下来,我们将重命名现有配置,并创建一个最小化配置,支持我们从命令行使用的相同选项,如下所示:
$ sudo mv /etc/ptp4l.conf /etc/ptp4l.conf.orig
$ sudo vi /etc/ptp4l.conf
ptp4l.conf
文件应类似于调整接口,以便与您系统上的接口匹配。以下截图显示了我系统上的 ptp4l.conf
文件:
现在我们准备将 ptp4l
实现为服务。使用 systemctl
命令,我们可以启用并启动该服务,如下所示的命令:
$ sudo systemctl enable ptp4l
$ sudo systemctl start ptp4l
使用 systemctl
的 status
子命令,和前面的命令一样,我们将看到类似以下的输出,指示服务已启动并正在寻找时钟公告:
当服务器运行时,它可能会监听其他时钟公告,然后协商确定哪一个将成为主时钟。在以下从演示系统拍摄的截图中,我们可以查看 systemctl status
命令的输出。从日志中提取的内容表明了新主时钟的选举过程:
我们现在已经看到如何使用 NTP、Cronyd 或 PTP 在 Linux 上实现时间服务。拥有准确的时间后,我们可以继续进行电子邮件和 SMTP(简单邮件传输协议)的配置。
在 RHEL 7 上实现电子邮件投递
当前版本的 RHEL 默认使用的 SMTP 服务器是 Postfix;原来的 Sendmail 包仍然可以使用,但不再是默认选择。
在使用默认设置时,电子邮件服务器仅监听本地主机或回环地址是正常现象。这样,本地电子邮件投递在开箱即用时即可实现。这被许多服务使用,包括 crond
作业调度管理器。如果我们希望 Postfix 启用监听所有接口并接收来自远程系统的消息,则需要进行一些配置。
首先,我们将备份并整理主要配置文件。许多软件包倾向于过度注释它们的配置文件。这可能导致一些问题:你认为已经实施了某个更改,然而该更改可能已在后续设置中出现,而且你可能没有注意到。默认配置文件 /etc/postfix/main.cf
中有 679 行。我们将备份该文件,以保留注释和文档,但同时也会创建一个新文件,其行数不到原文件的 10%。以下命令展示了如何操作:
$ sudo sed -i.bak '/^#/d;/^$/d' /etc/postfix/main.cf
这将文件的行数从 679 行减少到 25 行,便于操作。我们现在可以在没有任何干扰的情况下编辑此文件。我们将向 /etc/postfix/main.cf
文件中添加两行新内容并编辑两行现有内容。该文件需要以 root 用户权限进行编辑。
我们将编辑 inet_interfaces = localhost
行,使其读取如下:
inet_interfaces = all
这将使 SMTP 服务能够在所有接口上监听,而不仅仅是回环地址。
我们还需要确保接收来自正确主机和域的电子邮件。这是通过 mydestination
指令来控制的;然而,我们需要在 mydestination
行之前添加 myhostname
和 mydomain
指令。这是我们要添加的两行内容。我们还需要将 $mydomain
添加到现有的 mydestination
行。在我的系统中,这些行设置如下:
myhostname = ns1.tup.local
mydomain = tup.local
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain
以下截图显示了已实施这些更改的文件顶部几行:
完成这些编辑后,我们可以保存文件并重新启动 SMTP 服务。以下命令展示了如何重新启动 Postfix SMTP 服务:
$ sudo systemctl restart postfix
向 DNS 服务器添加 MX 记录
如果我们要接收某个域的电子邮件,则需要一个 MX 记录(邮件交换记录),该记录会被添加到我们在本节前面创建的 DNS 数据库中。之前用于 tup.local.
DNS 域的文件是 /var/named/named.tup
。我们为 MX 或邮件交换记录添加的记录应类似于以下内容:
tup.local. MX 10 ns1
使用此设置,我们将配置 MX(邮件交换)优先级为 10. 如果存在多个电子邮件服务器记录,优先级最低的 MX 记录将是首先使用的电子邮件服务器。
更新后的区域文件显示在以下截图中:
在这些更新到位后,我们可以检查区域并重启服务器。为方便起见,命令在这里列出:
$ sudo named-checkzone tup.local /var/named/named.tup
$ sudo systemctl restart named
我们可以通过以下命令验证 DNS 是否正常工作:
$ dig -t MX tup.local
为了测试电子邮件的发送,我们应该能够向该域发送电子邮件:
$ mail root@tup.local
我们将被提示输入Subject
信息,然后可以输入我们希望的电子邮件。要结束一封电子邮件,我们将在邮件中仅包含一个句号或点字符,其他内容不填写。这应该被传递到你系统上的根目录,并被识别为该域的电子邮件服务器。
要更详细地查看消息系统的工作情况,我们可以查看日志文件。要查看最近的活动,我们可以使用如下的tail
命令:
$ sudo tail /var/log/maillog
现在我们已经有了一个简单的电子邮件服务器,与时间、DNS 和 DHCP 服务配套使用。
总结
在本章中,你学习了如何配置一些常见的与 Linux 相关的基础网络服务。我们从如何使用 DNS 设置名称解析开始,并通过向 DNS 添加电子邮件服务器记录结束。了解了 DNS 后,我们紧接着学习了与 D 相关的内容,研究了 DHCP,从而为网络提供 IP 配置。这将我们带入了与chronyd
、ntpd
和ptp4l
等不同时间服务的复杂性。最后,在本章末,我们查看了 RHEL 7 上 Postfix SMTP 服务器的简单配置。这应该能为你提供一个简单的概览,帮助你了解在日常使用 Enterprise Linux 时会接触到的服务。
在下一章中,我们将了解 RHEL 7 和 7.1 中的新功能,并学习如何设置 iSCSI 存储服务。你会发现 ISCSI 目标现在是基于内核的,与 RHEL 6 中的旧版 iSCSI 目标服务工作方式截然不同。
第四章:实现 iSCSI SAN
在 RHEL 7 中,您可能会注意到一个非常大的变化,相比于早期版本,iSCSI 目标服务现在是内核的一部分。这是随着 Linux 内核版本 3.x.x 的发布而来的,这一版本出现在最新的 Raleigh(北卡罗来纳州)企业产品中。iSCSI 服务器或目标的管理和配置已经完全改进。您将很快学习如何与网络上的设备共享磁盘和分区。在这个过程中,我们将研究如何配置以下先决条件:
-
iSCSI 目标(服务器)
-
使用 LVM 的逻辑卷
-
安装
targetd
服务和targetcli
工具 -
使用
targetcli
管理 iSCSI 目标 -
iSCSI 发起客户端
iSCSI 目标(服务器)
iSCSI 目标是一个使磁盘空间能够在网络上共享的软件。该服务共享磁盘而非文件系统,并建立了 存储区域网络(SAN)。这种 SAN 存储可以让服务器共享相同的磁盘,这在其他网络服务集群并需要访问共享磁盘的场景中通常是必要的。通常情况下,任何时刻只有一个服务器可以访问每个共享磁盘。iSCSI 目标可以共享整个磁盘,但通过使用 LVM 实现的逻辑卷来共享客户端所需的确切空间通常更高效。除了共享块设备外,还可以创建文件,并通过目标服务器将文件空间作为磁盘共享。
磁盘 IO 通过标准网络连接传输到 iSCSI 服务器。因此,网络连接越快,存储性能越好。虽然 iSCSI 在 1 GB 以太网网络上也能工作,但企业级应用推荐使用 10 GB 以太网。尽管如此,对于家庭或小型办公室的使用,您会发现 1 GB 网络速度应该足够(尤其是当您能为 iSCSI 流量定义一个单独的网络段来与其他网络流量隔离时)。目标默认使用的 TCP 端口是 3260
。
使用 LVM 管理逻辑卷
尽管我们可以共享整个磁盘空间或磁盘分区,但实际上,只共享客户端服务所需的磁盘空间才更有意义。例如,如果 Web 服务器需要 20 GB 的空间来存储网站文件,我们可以只共享那部分空间,而不是共享整个可能达到 TB 级别的磁盘。为此,我们将创建逻辑卷并将其作为块设备共享。为了实现 LVM,我们需要创建三个对象:
-
物理卷:表示原始磁盘空间,作为磁盘分区。当我们使用分区时,分区类型应该设置为
Linux LVM
,ID 为8E
,使用fdisk
分区工具进行设置。 -
卷组:将物理卷聚合在一起,以便可以将磁盘空间分配给逻辑卷。
-
逻辑卷:表示可以共享的块设备。它占用了从卷组分配的空间。
在我们将用于本课程的演示 RHEL 7.1 系统上,我当前连接了三块磁盘。我们可以使用第三块磁盘的部分空间来配置 LVM 系统。我们将从分区第三块磁盘(当前未分区)开始,以便将该磁盘的一部分用于 LVM,另一部分用于其他文件系统。
磁盘分区
使用fdisk
命令,我们可以根据需要分区磁盘。我们将使用一个扩展分区并在其中创建逻辑分区。这纯粹是为了允许我们在这里以及后续章节中使用多个分区:
$ sudo fdisk /dev/sdc
使用时请注意设备名称!
当你运行fdisk
命令时,会显示一个菜单。可以使用m
命令查看菜单选项,但我们可以使用n
来创建一个新分区,然后用e
创建扩展分区。我们将按默认设置接受分区号、起始和结束扇区。我们将设置扩展分区使用整个磁盘。
现在,我们将再次使用n
创建另一个新分区,这次我们选择l
表示逻辑分区。分区号将默认为5
,因此在我的情况下是/dev/sdc5
。我们可以接受默认的起始扇区,但将结束扇区限制为 200M,使用+200M
设置。以下截图展示了此设置:
在输入了设置并仍然处于交互式fdisk
命令下时,我们可以使用t
选项设置类型。默认情况下,这将设置为83
。当你输入t
时,它会提示你输入分区号,默认是5
。为了输入分区代码,我们将使用8e
代表 LVM。接着,我们将使用p
打印配置,然后使用w
保存更改并退出程序。
创建物理卷
到目前为止,我们已经为 LVM 创建了一个分区,但它还没有成为任何 LVM 系统的一部分。为了标记它为可用,我们将使用pvcreate
命令:
$ sudo pvcreate /dev/sdc5
要在系统上显示 LVM 物理卷,可以使用pvs
命令或以 root 用户身份运行pvscan
命令。sudo pvscan
的输出如下截图所示:
输出显示我们系统上已经使用 LVM,因为在 RHEL 及许多其他系统中这是默认设置。新的PV
显示为/dev/sdc5
,但尚未属于任何卷组(VG)。
创建卷组
由于我们已经有了一个卷组,我们将使用该卷组并通过vgextend
命令扩展它,以包括新的 PV。我们希望将这个卷组保持独立,仅用于与 iSCSI 目标共享的空间。因此,我们将使用vgcreate
命令创建一个新的卷组,如下例所示:
$ sudo vgcreate iscsi /dev/sdc5
使用此命令,我们将创建一个名为iscsi
的新卷组,并使用/dev/sdc5
作为 PV。
与物理卷类似,我们可以使用 vgscan
或 vgs
来显示可用的卷组信息。sudo vgs
命令的输出如下所示的截图:
从上述输出中,我们可以看到我们新创建的名为 iscsi
的 VG
有一个连接的单个 PV
,但是尚无逻辑卷 (LV
),我们接下来将创建它们。
创建逻辑卷
逻辑卷 (LVs) 是我们可以在本地使用或(在我们的情况下)通过 iSCSI 共享的块设备单元。我们使用 lvcreate
命令创建 LVs。例如:
$ sudo lvcreate -n web -L 100M iscsi
通常情况下,我们不会使用所有可用空间,只会使用 Web 团队为其新的 Web 容量所请求的空间。 -L
选项设置我们分配的大小。我们将分配100M
; -n
设置名称为 Web,在这种情况下,VG
名称附加在命令字符串的末尾。
此命令将在 /dev
目录中创建一个块设备,但通常通过符号链接访问此设备。将创建以下两个符号链接:
-
/dev/mapper/iscsi-web
-
/dev/iscsi/web
在我们的情况下,这些链接到 /dev/dm-2
块设备。在您的系统上,实际的块设备名称将取决于您拥有多少现有的 LVs。这就是为什么操作系统使用符号链接的原因,因为这个名称是可以确定的,而实际的块设备名称并不是那么确定的。
如果您能意识到这里的模式,您会意识到,为了显示系统上 LVs 的信息,我们可以使用 lvs
或 lvscan
。sudo lvscan
命令的输出如下所示的截图:
在此阶段,我们拥有一个可以与网络上的服务器共享的工作块设备。现在,我们将查看如何在 RHEL 7.1 上配置 iSCSI 目标。
安装 targetd
服务和 targetcli
工具
要在 RHEL 7 上管理基于内核的 iSCSI 目标服务,我们需要安装 targetd
和 targetcli
包,如下所示的命令:
$ sudo yum install targetd targetcli
从输出中,我们可以看到安装了额外的软件包;然而,更有趣的是看到 Python 如何作为管理 iSCSI 的主要工具。以下截图是命令行输出的一部分:
尽管 iSCSI 目标作为内核的一部分运行,但 targetd
包提供了一个服务。此服务用于加载 iSCSI 目标配置。这就是 targetd
的全部功能,因此我们无需启动此服务;只需确保 targetd
已启用自动启动,如下命令所示:
$ sudo systemctl enable targetd
提示
一旦系统启动时启用了 targetd
服务,它确保执行 targetcli restoreconfig
命令。它还确保在启动时加载当前配置。
我们现在已经安装了 targetcli
iSCSI 目标和 targetd
服务的管理工具,该服务提供了一种机制,可以在启动时读取配置。接下来,我们将介绍如何使用 targetcli
配置目标。
使用 targetcli 管理 iSCSI 目标
targetcli
命令是一个用于查看、编辑、保存和加载 iSCSI 目标配置的 shell。当你查看配置时,你会发现 targetcli
提供了一种类似于文件系统的层级结构。
要启动 targetcli
shell,我们将以 root 用户身份使用 sudo
运行此命令。你会看到,在首次运行命令时,会创建一个首选项文件。以下截图说明了这一点:
如前面的输出所示,你可以输入 help
来显示可以输入的命令列表。要查看可用的配置对象,我们可以使用 ls
命令。输出如下所示:
我们将从 backstores
对象开始,以便我们可以将其添加到 LVM 块设备的配置中,除了 fileio
后端存储外。顾名思义,这将是文件系统中的一个文件;我们可以将其作为虚拟磁盘共享到网络中。
创建存储后端存储
我们将从 targetcli
配置的根目录开始;这应该正是我们所在的位置,但我们始终可以使用 pwd
命令来显示我们的工作目录。如果需要,我们可以通过 cd /
命令将其切换到配置的根目录。
提示
在使用 targetcli
命令时,我们可以像在 Bash 中一样使用 CTRL + L 来清除屏幕,但最重要的是,Tab 键补全功能可以使用,所以我们不需要输入对象和属性的完整名称或路径。
要在我们在本节前面创建的 LVM LV 上创建一个新的 block
后端存储。如果我们回忆一下,这个路径是 /dev/iscsi/web
:
/> backstore/block/ create web_lv /dev/iscsi/web
这将创建一个名为 web_lv
的块后端存储。再次使用 ls
命令将列出层级结构中的其他对象。在以下截图中,我们看到后端存储的创建及随后的列出:
我们还将添加一个名为 fileio
的新后端存储。新后端存储的创建与配置根目录类似:
/> backstores/fileio create file_store /tmp/fs 100M
该命令将创建后端存储和物理文件,我们将其用作虚拟磁盘。如果文件已存在,我们省略大小参数。如果我们再次选择使用 ls
命令,这两个对象都会显示在列表中。
其他后端存储类型包括 pscsi
和 ramdisk
。这些代表 Passthrough SCSI
连接,指向物理 iSCSI 设备,以及 基于内存的磁盘
,这些磁盘可以像 fileio
一样,使用 targetcli
动态创建。
创建 iSCSI 目标
我们在主列表中看到的 iSCSI 对象代表了 iSCSI 目标及其属性。首先,我们将创建一个具有默认名称的简单 iSCSI 目标。然后我们可以删除该对象,并查看如何使用正确的命名约定创建我们自己的目标:
/> iscsi/ create
这将创建一个 iSCSI 目标,并监听 TCP 端口 3260
。此时没有连接任何 LUN 或后端存储,IQN(iSCSI 合格名称)将由系统生成。我们可以随时添加后端存储,但大多数情况下,我们希望使用自定义名称。因此,在这种情况下,我们将删除该对象。我的系统上生成的 IQN 是 iqn.2003-01.org.linux-iscsi.redhat7.x8664:sn.ce1ebea336a2
,但不要忘记我们可以使用 Tab 键完成输入。因此,我们在删除或编辑时不需要写出完整的名称。以下命令显示了这一点,但它可能在显示或打印时换行,最终作为一行代码执行:
/> iscsi/ delete iqn.2003-01.org.linux-iscsi.redhat7.x8664:sn.ce1ebea336a2
我们现在将通过提供自定义 IQN 来创建一个 iSCSI 目标。为此,我们像之前一样创建对象,但这次指定通常包含日期和反向 DNS 名称的名称。以下命令是我们在本书中使用的示例:
/> iscsi/ create iqn.2015-01.com.tup.rhel7:web
IQN 以 iqn
开头,后面跟着创建的年份和月份以及反向 DNS 名称。我们可以通过在末尾加上 :web
来添加目标描述,表示这是一个面向 Web 服务器的目标。
我们可以通过使用 ls
命令并添加我们想要列出的对象层次结构来过滤显示的内容。例如,要列出目标,我们将使用 ls iscsi
命令。
该命令的输出如下面的截图所示:
现在我们有了目标的自定义名称,但我们仍然需要添加 LUN 或逻辑单元,以使 SAN(存储区域网络)生效。
向 iSCSI 目标添加 LUN
留在 targetcli
shell 中,我们现在将转到我们的目标和 TPG(目标门户组)对象。与文件系统类似,这通过使用 cd
命令来实现,如以下命令所示:
/> cd iscsi/iqn.2015-01.com.tup.rhel:web/tpg1/
我们可以从这里运行 ls
,但是内容已经包含在之前从配置根目录运行的列表中了。我们有一个门户,监听所有 IPv4 接口的 TCP 端口 3260
。目前,我们没有 acls
或 luns
。要添加 LUN,我们将使用以下命令,它将利用 LVM 块后端存储:
/iscsi/iqn.20...rhel:web/tpg1> luns/ create /backstores/block/web_lv
这还会有一个额外的副作用,激活一个后端存储。可以通过列出 /backstores
对象来查看这一点。命令和输出如下所示:
添加 ACL
我们不需要添加 ACL,但通常我们只想让一个主机,可能是一个虚拟集群设备来访问 LUN。如果没有 ACL,我们需要设置一个属性,以防 LUN 默认变为只读。
要创建 ACL,我们限制 LUN 对指定的发起者名称或我们在访问控制列表(ACL)中提到的名称的访问。发起者是 iSCSI 客户端,并将在发起者的/etc/iscsi/initiatorname.iscsi
文件中配置一个唯一的客户端 IQN。如果该文件不存在,你需要安装iscsi-initiator-utils
包。配置发起者名称的文件名在 Linux 客户端中是统一的,但在其他操作系统中会有所不同。要添加 ACL,我们将保持当前的配置层级:/iscsi/iqn….:web/tpg1
,并发出以下命令,再次作为一行写出:
/iscsi/iqn.20...rhel:web/tpg1> acls/ create iqn.2015-01.com.tup.web:web
这个 ACL 限制了对 ACL 中列出的发起者的访问。如果你更改发起者的名称,请小心,因为 ACL 也需要更新。发起者是 iSCSI 客户端。
使用ls
命令从当前配置层级中,我们看到类似于以下截图的输出,输出中还包括创建 ACL 的命令:
如果不添加 ACL,LUN 将为只读。如果需要使 LUN 可写,你需要使用以下命令来设置所需的属性:
/iscsi/iqn.20...rhel:web/tpg1> set attribute demo_mode_write_protect=0
现在,iSCSI 目标已配置。退出targetcli
应保存此配置,但你可能会觉得手动保存更为安全。
为此,请返回到配置的根目录并输入saveconfig
命令,如下例所示:
/iscsi/iqn.20...rhel:web/tpg1> cd /
/> saveconfig
/> exit
提示
我们在本章前面启用的targetd
服务从targetcli
运行restoreconfig
命令。此命令用于在系统启动时加载配置。
配置保存后,我们可以迁移到客户端,查看 iSCSI 发起者并观察 SAN 上磁盘共享的工作情况。
与 iSCSI 发起者一起工作
RHEL 7 上的 iSCSI 发起者或客户端是通过iscsi-initiator-utils
包安装的;你可以使用yum
命令验证该包是否已安装在系统上,如以下示例所示:
$ yum list iscsi-initiator-utils
如果显示为已安装
,那么一切正常,但如果显示为可用
,你需要安装它。
为了进行本练习,我们将使用一个独立的 RHEL 7 系统作为发起者,并将其连接到现有目标。我们需要在新的 RHEL 7 系统上编辑/etc/iscsi/initiatorname.iscsi
文件,确保名称与本章早些部分我们添加到 ACL 中的名称匹配;我们可以使用cat
命令显示此内容,如以下截图所示:
我们将使用主要客户端工具:iscsiadm
。这个工具是与前述包一起安装的。要发现目标上的 iSCSI LUN,我们将使用以下命令:
$ sudo iscsiadm --mode discovery --type sendtargets \
--portal 192.168.40.3 --discover
输出应类似于以下行:
192.168.40.3:3260,1 iqn.2015-01.com.tup.rhel7:web
现在,我们已经看到我们可以连接到 iSCSI 目标,并让其向我们发送已配置的 LUN。接下来,我们应该连接到此 LUN,并使用相同的命令,添加以下选项:
sudo iscsiadm --mode node \
--targetname iqn.2015-01.com.tup.rhel7:web \
--portal 192.168.40.3 --login
以下截图显示了命令及其输出:
对于发起端来说,共享的 LUN 现在就是一块磁盘。我们可以以正常方式对这块磁盘进行分区和格式化。我们将使用 lsblk
来列出各种连接的块设备。在该系统上,我们可以看到它作为 /dev/sdc
连接,并且与我们分配的 100M
大小匹配,如下截图所示:
使用传统的 fdisk
或 parted
命令,我们可以创建一个分区,然后格式化它以便在此系统上本地使用。由于我们在本章中使用了 fdisk
来交互式创建 LVM 分区,现在我们将直接从命令行使用 parted
来管理它。
该命令需要一个磁盘标签才能创建分区表。此标签可以设置为 msdos
或 gpt
。fdisk
命令会自动创建 msdos
标签,但这是因为它只能处理传统的 msdos
分区表。parted
可以同时支持 msdos
和 gpt
(GUID 分区表)。parted
命令还允许通过交互式或直接从命令行创建分区,因此它是可脚本化的。这里有一个额外的复杂性,即无法显示开始新分区的扇区。因此,我们需要计算出最佳的起始扇区。
一旦你知道了某种类型磁盘的这一信息,对于类似的磁盘,它将是相同的。
要确定磁盘的起始扇区,我们将从两个文件中读取值:/sys/block/sdc/queue/optimal_io_size
,然后将其除以 /sys/block/sdc/queue/physical_block_size
。
在演示系统上,这与 4194304 / 512 = 8192 相关;可以使用 cat
命令作为普通用户从文件中读取这些值。一旦我们了解了最佳对齐细节,就可以通过以下命令为磁盘打标签并创建分区:
$ sudo parted /dev/sdc mklabel msdos
$ sudo parted /dev/sdc mkpart primary 8192s 100%
我们创建一个单一的分区:/dev/sdc1
。当我们从最佳起始扇区开始时,该分区将使用 100% 的磁盘空间。
配置完成后,我们可以将分区格式化为所选择的文件系统,并以正常方式挂载它。iscsid
后台服务已启用,但仅在需要时运行。重启后,连接将重新建立到远程 iSCSI 存储服务器,以便/dev/sdc1
分区在客户端上保持不变。只要发起端没有更改默认设置,便会发生这种情况。你应该检查/etc/iscsi/iscsid.conf
文件,并确保设置如下:
node.startup = automatic
在这种配置下,这是 RHEL 7 的默认设置,iscsid
服务将在启动时重新连接。
总结
在本章中,我们已经看到 RHEL 7 如何准备好作为企业网络中的 SAN 服务器,利用基于新内核的 iSCSI 目标服务器。服务器的管理现在是通过基于 Python 的工具来完成的,例如targetcli
,而targetd
服务则负责在启动时加载配置。我们经常通过逻辑卷按需提供磁盘存储。我们还了解了如何使用 LVM 的三个组件来实现这一点:物理卷、卷组和逻辑卷。
在创建并共享我们的存储之后,我们查看了第二个 RHEL 7 系统,以及如何将其连接为 iSCSI 发起端,利用 iSCSI 目标上的共享存储。最初是使用iscsiadm
来管理连接,但这些连接通过iscsid
服务得以持久化。
在下一章,我们将介绍BTRFS(更好的文件系统),它在 RHEL 7 中首次亮相。我相信你一定会对这个文件系统所提供的功能感到印象深刻。
第五章:实现 btrfs
在本章中,我们将探讨 btrfs
(发音为 Better FS)提供的功能。尽管与网络无关,我们很快会讨论如何共享文件系统;正因为如此,且 btrfs
如此强大,我们现在就来看看它。Btrfs
是一个本地文件系统,提供集成的卷管理操作、易于扩展以及内置容错功能。它并未完全得到 Red Hat 的支持,并作为技术预览发布;必须说,Red Hat 对此非常谨慎,因为 SUSE 从 Enterprise Linux 11 SP2 开始便将 btrfs
作为默认文件系统,并且在 SLES 12 上继续使用。
本章将涵盖以下主题:
-
btrfs
概述 -
实验环境概述
-
创建
btrfs
文件系统 -
写时复制技术
-
调整
btrfs
文件系统大小 -
向
btrfs
文件系统添加设备 -
从
/etc/fstab
挂载多磁盘btrfs
卷 -
使用
btrfs
实现 RAID -
优化固态硬盘
-
使用快照进行时间点数据备份
-
使用 snappers 进行快照管理
btrfs
概述
如果现在有一样东西是 Linux 能够提供的,那就是拥有超过 55 个基于内核的文件系统,在 Linux 内核树上。那我们为什么还需要更多呢?我们已经看到,像 xfs
这样的老旧文件系统正在复苏,Red Hat 正在大力推广这个来自 SGI 的原始文件系统。btrfs
文件系统提供了一种独特的解决方案,将卷管理和文件系统合并为一个统一的解决方案。Btrfs
采用 通用公共许可证(GPL)授权,并作为标准随 Red Hat Enterprise 7 和 7.1 提供。它不仅提供文件管理访问,还提供卷管理和 冗余廉价磁盘阵列(RAID)管理。这种简单的管理方式意味着你可以通过单个命令创建 RAID 设备或扩展卷,而不需要依赖 LVM 进行逻辑卷管理或使用 mdadm
进行 RAID 管理。可扩展性也是选择 btrfs
的一个重要因素。它可以扩展到 16 EB(艾字节),并带来了以下以前未见过的可靠性特性:
-
非常快速的文件系统创建
-
数据和元数据校验和
-
快照功能
-
在线清理以修复问题
当你看到像 Facebook 和 TripAdvisor 等使用 btrfs
作为生产环境的组织时,你就会理解将它纳入本书的重要性。
在许多方面,btrfs
文件系统诞生于 ReiserFS 文件系统的失败,该文件系统在失去其首席开发者 Hans Reiser 后陷入困境。Chris Mason 曾在 SUSE 转向开发高端文件系统前参与了 ReiserFS 的开发,他被 Oracle 雇佣来开发高端文件系统。btrfs
的诞生正是从此开始的。
实验环境概述
本书中使用的 Red Hat Enterprise Linux 7.1 虚拟机将为本节添加额外的磁盘。目前,我们将使用三块磁盘:
-
/dev/sda
:此磁盘用于根文件系统 -
/dev/sdb
:此磁盘用于存储 yum 仓库 -
/dev/sdc
:此磁盘作为 iSCSI LUN 存储
为了演示btrfs
的关键功能,我们将向系统添加四个额外的虚拟磁盘,以便在演示btrfs
文件系统时使用。如果你使用的是虚拟化系统,也可以进行相同的操作。我们将添加的不同磁盘如下:
-
/dev/sdd
-
/dev/sde
-
/dev/sdf
-
/dev/sdg
在演示系统上使用lsblk
命令,你将能够查看我们从此时起将要使用的初始配置,正如以下截图所示:
安装 btrfs
使用 Red Hat Enterprise Linux 7 或更高版本时,即使是最小安装,btrfs
也会默认安装。然而,如果你使用的是早期版本,可以像通常一样通过 yum 安装btrfs
文件系统,如下所示:
# yum install -y btrfs-progs
文件系统安装完成后,我们可以使用以下命令检查已实现的版本:
$ btrfs --version
在 RHEL 7 中,版本为3.12
,而在 RHEL 7.1 中,版本为3.16.2
。
现在我们对btrfs
背后的强大功能有了些许了解,接下来让我们开始一些简单的实现示例。
创建 btrfs 文件系统
首先,我们将在/dev/sdd
整个磁盘上创建一个btrfs
文件系统。我们无需先分区,从一开始就节省了时间。以下是命令行展示:
# mkfs.btrfs /dev/sdd
文件系统创建完成后,我们可以花些时间熟悉完整性检查工具:
# btrfsck /dev/sdd
以下截图显示了我的系统的输出:
为了验证btrfs
文件系统是否在运行,我们将创建一个目录并挂载它。我们还将复制一些数据,并显示磁盘的使用情况信息:
# mkdir -p /data/simple
# mount /dev/sdd /data/simple
# find /usr/share/doc -name '*.pdf' -exec cp {} /data/simple \;
# btrfs filesystem show /dev/sdd
最后一个命令的输出显示在以下截图中。我们可以看到,已经使用了 5.96 MiB 的文件空间:
额外使用的空间(显示为138.38MiB
)包括与任何文件系统相关的典型元数据,但另外,默认情况下,btrfs
文件系统将空闲空间信息存储在磁盘上,以便快速检索,而不是扫描磁盘。这是通过space_cache
挂载选项控制的,默认情况下已经设置。如果你希望禁用此功能,可以使用nospace_cache
挂载选项。
写时复制技术
btrfs
文件系统成功的一个基础技术是 写时复制(CoW)。CoW
被用在逻辑卷管理文件系统中,包括 Solaris 中使用的 ZFS(一个 Oracle 产品)、微软的 卷影复制(Volume Shadow Copy)和 btrfs
。
这些 CoW 文件系统允许你进行即时快照或备份。这是因为在写入文件时会创建其副本,因此称为写时复制(Copy-on-Write)。当传统文件系统实现此功能时,虚拟磁盘技术也可以在 qcow2
中实现这一 CoW
技术。这样,qcow2
磁盘文件中分配的任何磁盘空间,在写入之前不会在主机上使用。
对于通用文件系统,你会发现 CoW
技术非常有用。能够恢复到先前的文件版本,就像传统文件服务器上的黄金一样。然而,如果你使用 btrfs
来存储非常大的数据文件,比如虚拟磁盘文件,CoW
技术可能会导致写入速度变慢。
在 Linux 中使用 chattr
命令,我们可以设置或更改文件和/或目录的属性。btrfs
文件系统支持一个禁用 CoW 的文件属性。这个属性仅在设置为空文件时有效。为了确保其有效性,我们通常将此属性设置在目录上,以便所有文件在创建时都会继承此属性。以下命令展示了如何实现这一点:
# mkdir /data/simple/cow
# chattr +C /data/simple/cow
# lsattr -d /data/simple/cow
# touch /data/simple/cow/vdisk1
# lsattr /data/simple/cow/vdisk1
在以下截图中,我们可以看到,创建新文件时会自动分配 NoDataCoW
选项。无论该文件是如何创建的,这一点都不重要:
调整 btrfs 文件系统大小
使用 btrfs
时,当文件系统在线并且被用户访问时,可以调整 btrfs
文件系统的大小。如果我们添加或移除设备,文件系统的大小会自动增长;我们将在本章的下一小节中看到这一点;然而,即使在我们创建的单个设备上,我们也可以根据需要调整文件系统的大小。使用以下命令,我们将文件系统分配的空间缩小 500MiB:
# btrfs filesystem resize -500m /data/simple
如果我们检查文件系统大小的变化,可以看到动态变化的过程:
向 btrfs 文件系统添加设备
我们已经在第四章《实现 iSCSI SANs》中略微了解了使用 LVM 的卷管理,实现 iSCSI SANs,它并不简单。
传统方式的卷管理
以下命令用于以传统的方式管理磁盘卷:
# pvcreate /dev/sde1
# vgextend vg1 /dev/sde1
# lvextend -L+1000M /dev/vg1/data_lv
# resize2fs /dev/vg1/data
使用 btrfs 进行卷管理
首先,我们将卷恢复到原始大小,然后再添加第二个磁盘。使用 max
选项,我们将确保 btrfs
文件系统使用我们目前拥有的单个磁盘上的最大可用空间:
# btrfs filesystem resize max /data/simple
在 LVM 和传统文件系统中,需要执行四个命令。在btrfs
中,我们可以用一个命令来完成这一切:
# btrfs device add /dev/sde /data/simple
这就是我们需要做的。设备被添加后,文件系统会自动扩展到可用的最大空间。我们可以使用btrfs filesystem show
命令来检查/dev/sdd
或/dev/sde
,因为默认情况下,这两个设备都会保存元数据的副本。在以下命令中,我们可以看到这一点,截图也会进一步验证这个信息:
# btrfs filesystem show /dev/sdd
# df -hT /data/simple
在查看以下截图后,我们可以看到生成的命令和输出:
将元数据存储在两个设备上可实现容错,并减弱了对设备的查询需求:
# btrfs fi show /dev/sdd
# btrfs filesystem show /dev/sde
提示
请注意,一些子命令可以缩短;在这种情况下,fi
相当于文件系统。
平衡 btrfs 文件系统
如果添加额外磁盘到卷中的原因是磁盘空间不足,那么我们可以选择通过将数据分散到两个设备来提升性能。这是通过使用balance
子命令来实现的:
# btrfs filesystem balance start -d -m /data/simple
-m
参数代表元数据,-d
代表数据。这样,磁盘就能以相等的比例使用。
演示系统的输出在以下命令中显示;请注意,您可以在balance
子命令中省略filesystem
,因为在这种情况下它是可选的:
从/etc/fstab
挂载多磁盘 btrfs 卷
如果我们是从/etc/fstab
文件挂载btrfs
卷,则需要确保在挂载/data/simple
目录之前执行btrfs
扫描。这将定位所有参与卷的设备。initramfs
文件系统可以在稍后的系统中为我们完成此任务,包括 RHEL 7。如果您的现有文件系统已经使用了btrfs
,那么扫描将被内建在您当前的initramfs
中。如果btrfs
是您的新系统,您需要生成一个新的初始 RAM 磁盘。运行以下命令时,请确保使用与您的系统相匹配的initramfs
和内核版本:
# dracut -v -a btrfs -f /boot/initramfs-$(uname -r) /boot/vmlinuz-$(uname -r)
然后,我们可以在/etc/fstab
文件中添加类似以下的条目:
/dev/sdd /data/simple btrfs defaults 0 0
创建 RAID1 镜像
RAID(廉价磁盘冗余阵列)软件也得到了btrfs
的支持。以下是当前支持的 RAID 级别:
-
RAID 0:没有冗余的条带化
-
RAID 1:磁盘镜像
-
RAID 10:条带化镜像
目前,我们有一个多磁盘的btrfs
文件系统,但没有容错功能。我们使用的实现方式是 RAID 0 / 条带化,没有奇偶校验。我们可以将其转换为 RAID 1 系统,并通过以下方式镜像元数据和文件系统数据:
# btrfs balance start -dconvert=raid1 -mconvert=raid1 /data/simple
正如您从上面的命令中看到的,元数据和文件系统数据已被转换为 RAID 1 的软镜像。
我们可以很容易且快速地使用btrfs
从一开始就创建一个镜像设备。镜像不会为我们提供额外的磁盘空间,但如果发生最坏情况,比如磁盘故障,它可以提供很好的容错性。我们可以使用目前未使用的额外磁盘在演示系统上展示这一点:
# mkfs.btrfs -m raid1 -d raid1 /dev/sdf /dev/sdg
# mkdir /data/mirror
# mount /dev/sdg /data/mirror
要创建一个镜像,我们将使用 RAID1 来处理元数据和-m
、-d
数据,正如我们在前面的转换示例中所做的那样。可用的磁盘空间是 1 GB。我们写入/dev/sdf
的数据会被镜像到/dev/sdg
;使用镜像时,我们会失去 50%的数据存储空间,但却获得了高度的冗余。我们同样需要在/etc/fstab
文件中添加一项条目,以确保 RAID 系统在启动时能正确挂载。由于initramfs
现在通过设备扫描来支持btrfs
,因此在此阶段不需要创建initramfs
:
/dev/sdf /data/mirror btrfs defaults 0 0
使用标准工具(如df
)显示空闲磁盘空间不会提供正确的信息;我们需要使用btrfs
工具。以下命令将列出/data/mirror
挂载点的可用空闲空间:
# btrfs fi df /data/mirror
以下截图显示了命令的输出:
我知道,甚至谈论它都会带来七年的霉运;然而,镜像确实可能会损坏。创建镜像的部分原因是为了提供容错能力。这本身就是对硬盘可能出现故障的接受。
在这个演示中,我们将销毁/data/simple/
卷,并重新使用之前用于简单卷的设备。为了销毁btrfs
元数据,推荐使用wipefs
,它是util-linux
包的一部分。首先,我们需要运行wipefs
命令,作用于我们需要清除的磁盘或分区,然后使用-o
选项指定偏移量。看看如何清除/dev/sdd
和/dev/sde
:
# umount /data/simple
# wipefs /dev/sdd
# wipefs -o 0x10040 /dev/sdd
# wipefs /dev/sde
# wipefs -o 0x10040 /dev/sde
第一个磁盘的输出如下所示,方便起见,截图中列出了此输出;第二个磁盘的操作会重复进行。别忘了从/etc/fstab
文件中删除相应条目:
清除这些磁盘后,我们可以将它们重新用于其他阵列。
我们将以与简单卷相同的方式向镜像卷中添加数据。通过这种方式,我们可以确保数据保持完整:
# find /usr/share/doc -name '*.pdf' -exec cp {} /data/mirror \;
我们现在将卸载镜像卷,并模拟其中一个磁盘的故障,具体步骤如下:
# umount /data/mirror
# wipefs -o 0x10040 /dev/sdg
当我们尝试使用 mount 命令重新挂载镜像卷时,会遇到问题,我们需要使用-o
降级选项挂载镜像卷:
# mount -o degraded /dev/sdf /data/mirror
在此阶段,我们的数据是可用的,因此可以松一口气:
# ls /data/mirror
我们仍然有一个 RAID 1 阵列,创建这个阵列的最少成员数量是两个,因此我们需要按如下方式添加一个新设备:
# btrfs device add /dev/sdd /data/mirror
现在我们可以移除故障或缺失的设备:
# btrfs device delete missing /data/mirror
missing
关键字将搜索数组中第一个缺失的成员。我们可以删除此设备。RAID 1 阵列现在已经完全投入使用,并通过两个设备重新提供软件镜像。
使用 btrfs 快照
希望到目前为止,您在 btrfs
中看到的内容能引起您的兴趣,但当然,总有更多的内容等待您去探索和学习。接下来,我们将讨论快照。Btrfs 快照可以作为数据的只读或读写副本使用。由于 btrfs
是基于写时复制(Copy-on-Write)的文件系统,因此无需复制大量数据,因为我们只需在数据发生变化时复制它。与此同时,原始数据会链接到新位置。通过这种方式,可以瞬间创建大型文件系统的快照。快照可以通过以下几种方式使用:
-
作为备份解决方案的一部分,您可能担心打开的文件会影响备份;快照将以只读方式创建。随后,您将实施对快照的备份。通过这种方式,备份将是快照创建时的主机文件系统状态。
-
快照在需要恢复到原始数据时非常有用,特别是在测试环境中,您可能需要进行大量更改,并能够非常快速地恢复到原始数据。
Btrfs 快照依赖于子卷;源子卷和目标子卷必须位于同一文件系统内。如果您还记得,数据仅在发生变化时才会被复制;这与传统硬链接的处理方式相同。
btrfs
文件系统中的子卷是离散的管理单元,使得对单一文件系统的元素能够更精细地控制。我们将首先创建一个单一的子卷,以便在创建快照之前对这项技术有一些了解。我们将重新使用 /dev/sde
磁盘作为我们的简单卷,并从头开始重新格式化镜像卷:
# mkfs.btrfs /dev/sde
# mount /dev/sde /data/simple
此时,/dev/sde
的完整文件系统已可用,并挂载在 /data/simple
目录下。这里尚未存储任何数据,但我们实际上已经通过简单目录拥有了文件系统的单一视图。子卷使您能够通过将文件系统的元素(子卷)挂载到我们选择的目录并使用适当的挂载选项,以不同的方式查看同一个文件系统。
我们将在现有的 /data/simple
目录后创建一个新的子卷:
# btrfs subvolume create /data/simple/vol1
输出非常简洁,如下图所示:
我们可以列出子卷,如以下命令和截图所示:
# btrfs subvolume list /data/simple
# ls /data/simple
以下截图显示了前述命令的输出:
我们还可以看到,创建子卷也在文件系统内创建了目录。由于这不仅仅是一个目录,还是一个子卷,我们将无法从文件系统中删除该目录。要删除目录,你需要删除子卷。
我们不会删除该目录,但如果以后需要删除它,删除命令如下:
# btrfs subvolume delete /data/simple/vol1
这将删除子卷及其中的目录,方式与创建子卷时也在文件系统内创建目录的方式非常相似。
现在我们将向子卷添加一些数据;如果你删除了它,可以重新创建它。我们可以将已经熟悉的 PDF 文件复制到这个卷中:
# cp /data/mirror/* /data/simple/vol1
如果我们需要将这些数据提供到其他地方,我们可以将子卷挂载到任何需要的位置,并使用我们认为合适的挂载选项。例如,我们在这个目录中有文档,因此可以将其以只读方式挂载到另一个目录:
# mount -o ro,subvol=vol1 /dev/sde /mnt
在/mnt
挂载点的根目录下,我们将看到我们添加到vol1
目录中的 PDF 文件。它们仍然可以在原始位置/data/simple/vol1
中访问。通过这种方式,我们可以通过挂载的方式控制对数据的访问。
现在我们已经了解了子卷,接下来我们将研究快照。快照必须在与目标数据相同的文件系统中创建;如前所述,快照的即时生成受到文件系统内一种内部链接形式的影响。
我们将生成现有vol1
数据的快照,并指定-r
选项以确保备份为只读。通过这种方式,我们可以通过将数据从backup
目录复制回来,恢复到这个时间点的备份。除非原始数据被更改,否则不会使用额外的磁盘空间:
# btrfs subvolume snapshot -r /data/simple/vol1/ /data/simple/backup
我们可以使用以下命令轻松列出子卷:
# btrfs subvolume list /data/simple
我们可能会将备份场景建立在文档可能频繁写入的事实基础上。此外,我们希望有一个解决方案,能够迅速恢复不当编辑带来的损失。
要创建工作子卷的只读快照,请使用以下命令:
# btrfs subvolume snapshot -r /data/simple/vol1 /data/simple/backup/
列出工作目录和备份目录的内容应该会发现它们的内容是相同的:
# ls /data/simple/vol1
# ls /data/simple/backup
backup
的名称并不重要,但在其使用的上下文中是有用的。像往常一样,一个好的命名方案有助于理解目录的用途,而不是像我们给vol1
取的名称那样。
如果我们不小心删除了/data/simple/vol1
中的所有文件,btrfs
中的 CoW 技术会将变更后的数据写入备份快照/data/simple/backup
。如果文件以任何方式被修改而不是删除,也会发生这种情况;快照会保留创建快照时的文件状态。在发生灾难时,我们可以简单地将文件复制回原始位置。
目前,我们将看看如何删除此快照。本章后面,我们将看到如何在 LVM 和 btrfs
系统上使用 snapper 作为简单的机制来管理快照:
# btrfs subvolume delete /data/simple/backup
优化用于固态驱动器的 btrfs
在多个 SSD 上创建 btrfs
文件系统时,使用单一 -m
选项将确保元数据不会重复。在 SSD 上,重复元数据被认为是一种空间浪费,并且有一个会减少磁盘寿命的开销,如以下代码所示:
# mkfs.btrfs -m single /dev/sdb
第二种方法是使用 ssd
挂载选项。此选项将设置一些性能选项:
-
允许大的元数据簇
-
允许更多的顺序数据分配
-
禁用叶写入以匹配 b 树数据库中的键和块顺序
-
提交 b 树日志片段而不批处理多个进程
使用 snapper 管理快照
快照命令包含在 RHEL 7 中,可用于轻松管理快照并查看其与原始数据的差异。它可以与 LVM 或 btrfs systems.h
一起使用。
要安装 snapper,我们回到 RHEL 的包管理:
# yum install snapper
目前,如果 SELinux 强制执行,似乎存在一个防止 snapper 工作的 bug 或功能。我们可以通过创建一个新的策略或简单地将 snapperd_t
设置为宽容域来允许正确的 SELinux 访问我们的资源。这样,我们仍然可以使用 SELinx 的强大安全性,但只是对 snapper 禁用如下:
# semanage permissive -a snapperd_t
以后,您可以使用 -d
选项来删除启用的 snapper 和 SELinux 支持:
# semanage permissive -d snapperd_t
目前,我们将把 snapper 留在宽容模式,并继续为 snapper 和我们的 /data/simple/vol1
数据创建配置:
# snapper -c simple_data create-config -f btrfs /data/simple/
使用以下命令,我们可以列出我们拥有的配置:
# snapper list-configs
以下截图显示了配置和列表命令的创建:
创建配置将在根目录 /data/simple/vol1
下创建一个隐藏目录 .snapshots
。配置本身存储在 /etc/snapper/configs
中;来自 /var/log/snapper.log
的故障排除日志文件存在。
现在我们已经创建了基础,我们将创建快照:
# snapper --config simple_data create --description "Start"
我们可以看到这个过程非常简单、快速,并且节省了我们大量的精力。如果我们检查现在在 /data/simple
后存在的子卷,我们会看到 .snapshots
和此后的编号子卷:
# btrfs subvolume list /data/simple
输出显示在以下截图中:
更容易和通常情况下,我们完全使用 snapper 来管理这一点,并且我们应该使用以下命令查看快照:
# snapper --config simple_data list
为了展示我们如何查看数据的差异,我们将从原始 vol1
位置删除一个 PDF 文件:
# rm /data/simple/vol1/tutorial.pdf
删除这个文件后,我们将拥有原始数据和快照之间的差异。CoW(写时复制)系统会将已删除的文件写入快照位置,正如删除操作所发生的那样。我们可以使用以下命令查看数据的差异,其中0
代表原始数据,1
代表快照:
# snapper -c simple_data status 0..1
命令的输出如下图所示,表明现在快照中已经包含了这个额外的文件:
为了恢复已删除的文件,我们将使用undochange
命令;请注意,我们需要显示从快照到原始数据或1..0
的效果,如以下命令所示:
# snapper -c simple_data undochange 1..0
我们现在可以在vol1
目录中找到返回给我们的tutorial.pdf
文件,如下所示:
# ls /data/simple/vol1/tutorial.pdf
从下图中,您将能够看到文件恢复命令和返回文件的列表:
总结
在本章中,我们了解了使用btrfs
文件系统能够释放的强大功能,以及与其他 Linux 逻辑卷系统(如 LVM)相比,使用它可以节省的时间。我们还看到了如何实现软件 RAID,并将文件管理、逻辑卷管理和 RAID 管理整合为一个命令。
使用 snapper 帮助管理快照在 LVM 和btrfs
系统中都能很好地工作。本章中我们使用了 snapper 与btrfs
文件系统。
在下一章中,我们将学习如何使用NFS(网络文件系统)共享文件,这是传统 UNIX 方式在网络上共享文件资源的方式。
第六章:NFS 文件共享
使用网络文件系统(NFS)共享文件是 Unix 和 Linux 上远程主机能够通过网络挂载文件系统并像本地挂载一样与之交互的传统方式。虽然 RHEL 7 支持 NFSv3 和 NFSv4,但不再支持 NFSv2。RHEL 7 客户端默认使用 NFSv4,如果无法建立连接,则会回退到 NFSv3。使用 NFSv4 简化了在防火墙后定位服务的过程,仅需要 TCP 端口2049
供客户端访问;然而,我们将演示 NFSv4 和 v3 的防火墙配置。本章将涉及以下内容:
-
NFS 概述
-
实验环境概述
-
NFS 服务器配置
-
使用
exportfs
-
在防火墙后托管 NFSv4
-
在防火墙后托管 NFSv3
-
NFS 客户端配置
-
使用
autofs
自动挂载 NFS
NFS 概述
我们已经习惯了 NFSv4 在 Red Hat Enterprise Linux 6 中默认包含。RHEL 7 新增了对pNFS
(并行 NFS)版本 4.1 的支持。pNFS
提供了安全性和性能增强,使得在防火墙和网络地址转换(NAT)路由器后更加高效地连接客户端。
不再支持 NFSv2,这并不是什么大损失,因为它不支持超过 2GB 的文件大小,并且没有版本 3 和 4 那样健壮。
使用 NFSv4 时,挂载和锁定协议都采用了一切打包的理念。这使得仅使用一个 TCP 端口2049
就足够。然而,使用 NFSv3 时,我们必须使用rpcbind
并为额外的服务设置静态端口,以便配置防火墙。这简化了防火墙配置,稍后您将看到,配置只需要访问 TCP 端口2049
。
服务器和客户端工具都来自nfs-utils
包进行安装。此包包含 NFSv4 和 v3 协议的工具,还包括其他有用的工具,例如nfsiostat
,可以用于监控 NFS 服务器上共享文件的使用情况。要列出已安装包的内容,可以使用rpm
命令,以下命令行可以作为普通用户运行:
$ rpm -ql nfs-utils #lists all files in the package
$ rpm -qd nfs-utils #lists just the documentation files
$ rpm -qc nfs-utils #list only the configuration files
$ rpm -qi nfs-utils #displays descriptive information on the package
实验环境概述
本章演示中,我们将使用在Oracle VirtualBox虚拟化环境中运行的两台虚拟机。VirtualBox 可以从www.virtualbox.org/
免费下载,并且支持 Windows、Mac OS X、Linux 和 Solaris 主机。
NFS 服务器将在 IP 地址为192.168.10.10
、主机名为nfshost
的 RHEL 7.1 主机上配置。NFS 客户端将在 IP 地址为192.168.10.11
、主机名为nfsclient
的 RHEL 7.1 主机上配置。
两台机器都以最小配置安装;我们在两台主机上都安装了nfs-utils
包,如下所示:
$ sudo yum install -y nfs-utils
此外,在nfshost
主机上,我们安装了 net-tools 软件包,以便我们可以使用netstat
命令显示打开的端口。安装net-tools
的命令如下:
$ sudo yum install -y net-tools
防火墙正在运行默认设置,并通过firewall-cmd
命令进行管理。为了允许 NFSv4 连接到nfshost
,我们使用以下命令额外开放了 TCP 端口2049
:
$ sudo firewall-cmd --add-port=2049/tcp --permanent
$ sudo firewall-cmd --reload
提示
我们将在本章稍后介绍 RHEL 7 上的防火墙,并且会详细讲解如何在本书的第十一章,使用 firewalld 进行网络安全一章中使用firewall-cmd
和firewalld
服务。
NFS 不仅使用防火墙来保护服务器,还支持 TCP 包装器来控制访问。访问服务的权限可以通过使用/etc/hosts.allow
和/etc/hosts.deny
文件来决定。
NFS 服务器配置
要配置 NFS 服务器,我们需要选择希望共享的目录。NFS 中用于共享目录的术语是导出目录;因此,共享的目录被称为导出。
要永久导出一个目录,我们需要将配置添加到/etc/exports
文件中。该文件是存在的,但在新系统中会为空。nfs-server
服务在启动时会读取此文件,以确定哪些目录应该对网络客户端可用。如果/etc/exports
文件发生更改,重新加载nfs-server
服务将强制该服务重新读取文件,如下所示的命令行:
$ sudo systemctl reload nfs-server
要显示服务器上的当前导出,我们可以使用exportfs
或showmount
命令。现在我们将花一点时间来启动所需的服务并创建我们的第一个简单导出。
首先,我们需要启动所需的服务。我们可以独立启动并启用每个服务,但为了自动化的精神,我们将在命令提示符下编写一个简单的循环,以节省一些打字和时间。我们将使用sudo
;你的用户账户需要在sudoers
文件中列出。一旦确保你有权限使用sudo
,命令将如下执行:
for s in rpcbind nfs-server nfs-lock nfs-idmap ; do
sudo systemctl enable $s
sudo systemctl start $s
done
如果这使语法对你来说更清晰,下面的截图显示了在nfshost
上执行的命令:
简单的导出
如果不编辑/etc/exports
文件,我们无法导出文件系统中的任何内容。因此,当我们使用exportfs
显示本地导出时,将没有输出,如下所示的命令行:
$ sudo exportfs
我们在使用showmount
命令时将不会有太多运气,如下图所示:
如你所见,showmount
命令会显示导出列表的标题,但当然,列表为空,直到我们明确地定义一些导出的目录。
提示
showmount
命令可以在远程主机上使用,比如 nfsclient
,以列出已导出的目录,但这将依赖于额外的服务。因此,nfshost
的防火墙需要为 NFSv3 配置。我们将在本章后面讨论这一点。
我承认什么都不共享,什么也没有,可能不是本书中最令人兴奋的功能,但至少我们发现了一些有用的工具,如 exportfs
和 showmount
。接下来,我们将导出一个现有目录,仅仅是为了熟悉 NFS。为此,我们需要以 root 身份编辑 /etc/exports
文件;我们可以使用 sudo
来完成。你可以直接以 root 身份登录,也可以使用 su
命令。我们将添加以下行以导出或共享 /usr/share/doc
目录。这只是一个简单的测试,稍后我们将添加自己的目录和内容。为了演示,我们将坚持使用 vi
来编辑文件;不过,你也可以使用你喜欢的编辑器:
$ sudo vi /etc/exports
打开文件后,在没有新 NFS 服务器内容的情况下,我们可以添加以下行来导出 /usr/share/doc
目录:
/usr/share/doc *(ro)
使用 cat
命令,我们可以显示应编辑的文件名以及编辑完成后文件的内容,如以下截图所示:
导出一个目录后,我们应该能够通过 exportfs
或 showmount
来查看它。
提示
exportfs
命令需要管理员权限,而 showmount
命令则不需要。
然而,在我们进一步讨论之前,我们需要回顾一下,nfs-server
服务在启动时会读取此文件,并且当前正在运行。我们可以重启该服务,但最好重新加载该服务。通过这种方式,如果远程主机当前已挂载了导出的目录,就无需停止该服务。运行以下命令将重新加载该服务,然后显示导出目录或多个目录:
$ sudo systemctl reload nfs-server
$ sudo exportfs
之前列出的两个命令的输出现在显示在以下截图中:
当我们定义导出时,我们将一个目录导出到所有主机,这些主机用星号符号表示;任何导出的选项都包含在括号中。我们通过添加 ro
选项指定该导出为只读。
作为一个简单的测试,我们现在可以使用 nfsclient
主机访问此导出。从 nfsclient
的控制台中,我们可以访问已导出的目录,并使用以下命令将其挂载到 nfsclient
本地的 /mnt
目录:
$ sudo mount 192.168.10.10:/usr/share/doc /mnt
我们可以使用服务器的 IP 地址或主机名,只要该主机名能够通过 DNS、mDNS(多播 DNS)或本地主机文件解析。服务器主机名或 IP 地址的末尾必须以冒号表示。
我们可以使用标准的 ls
命令轻松列出导出目录的内容,针对的是 /mnt
目录。以下截图显示了 ls
命令的截断输出:
高级导出
我们已经看到,如果仅需要简单的目录导出,Linux 系统管理员的工作可以变得多么简单。然而,尽管这个选项可能适用于一些目录导出和服务器,但其他情况可能需要更多的时间和精力。
/etc/exports
文件中的基本指令如下:
export host(options)
这些变量的结构如下:
-
export
:这是在 NFS 服务器上被导出或共享的目录。 -
host
:这是导出目录共享的主机或网络。 -
options
:这些是由主机或网络在括号后使用的特定选项。
还可以通过写一个单独的条目,将一个导出共享给不同的主机或网络,具有不同的选项,如下示例代码所示:
export host(options) host(options)
将这些变量扩展为实际值,替换后的工作示例如下:允许 192.168.10.11
具有读写访问权限,而对所有其他主机只有只读访问权限,我们可以查看以下代码:
/usr/share/doc *(ro) 192.168.10.11(rw,sync)
选项是以逗号分隔的,我们在 nfsclient 192.168.10.11
的选项中额外添加了 sync
选项。sync
选项将确保对该导出的写入操作会即时写入磁盘,而不是等待写缓冲区被刷新到磁盘。Linux 使用一种缓冲系统,促进脏缓存缓冲区的使用。这些缓冲区会随着数字的增长被写入磁盘。sync
选项确保这些缓冲区会立即写入磁盘。尽管这会对性能产生负面影响,但在连接不总是保持的情况下,它可以更可靠。
如果 /etc/exports
文件中的单行过长,则可以使用反斜杠(\
)字符进行换行。在文件中,每个导出必须单独占一行。额外的空白行会被忽略,可以添加以提高可读性。如果行以井号(#
)字符开头,则该行会被服务器忽略。
提示
如果导出授予了读写访问权限,而文件系统对用户是只读的,那么他们仍然只有只读访问权限。如果导出设置为只读,而文件系统通常会允许用户的读写访问,那么他们仍然只有只读访问权限。简单来说,当结合文件和导出权限时,最严格的权限将生效。
如果我们要确保百分百准确,导出的选项是可选的。如果没有设置选项,将应用默认选项。然后我们可以重写之前的示例,利用默认值,如下所示:
/usr/share/doc * 192.168.10.11(rw)
从修改后的示例中,您应该能够正确猜测,ro
和 sync
默认选项不再显式设置,但它们仍然有效。可以使用 exportfs
命令和 -v
选项查看导出目录的有效选项,如以下命令所示:
$ sudo exportfs -v
如果没有设置选项,并且在前一个命令的输出中没有显示该选项,则会看到默认选项。
NFS 服务器的默认选项包括以下内容;更多详情请参阅man
页面:
-
ro
:这使得导出的文件系统在远程主机上变为只读。 -
sync
:NFS 服务器在响应新请求之前会将更改写入磁盘。 -
wdelay
:与sync
选项一起使用;NFS 服务器将延迟写入磁盘,预计会有更多写入操作马上进行。 -
root_squash
:远程用户以 root 或UID 0
身份连接时,会被更改为nfsnobody
,因此我们将只能收集授予其他用户的权限。这有效地压制了 root 访问的权限,防止了未授权的 root 访问导出的文件系统。
我们现在将修改/etc/exports
文件,以表示我们将导出到的两个主机集,并验证我们可以从指定的主机192.168.10.11
连接。导出设置为rw
,这将覆盖设置为所有主机*
的ro
选项。我们将使用echo
命令覆盖exports
文件,以便您可以看到文件中的编辑内容以及其他命令。以下示例列出了这些命令,并附有截图以展示导出选项:
$ sudo bash -c 'echo "/usr/share/doc * 192.168.10.11(rw)" > /etc/exports'
$ sudo systemctl reload nfs-server
$ sudo exportfs -v
从前面的截图中,我们可以看到192.168.10.11
主机具有读写访问权限,而<world>
或其他所有主机仅具有只读访问权限。
提示
小心空格
/etc/exports
文件的格式非常精确,主机/网络前不应有空格,选项应紧跟其后。以下条目具有非常不同的含义:
/home server1(rw)
#correctly shares /home as read and write to server1
/home server1 (rw)
#shares the export /home to server1 using the default read-only and to <world> read-write is assigned, the asterisk can be omitted when designating all hosts.
伪根目录
如您在/usr/share/doc
当前导出中看到的那样,访问服务器时,导出目录的完整路径是正常的。通过在服务器上使用伪根目录,可以简化访问导出目录所需的路径。这仅适用于 NFSv4 服务器和客户端。伪根目录设置完成后,我们可以将其他目录挂载到该路径。让我们在nfshost
上查看这一点。
我们将清空当前的导出目录。这一次,我们将从头开始设置共享,稍加思考和规划。
首先,我们将在nfshost
上创建一个新的目录,作为伪根目录:
$ sudo mkdir -m 1777 /var/exports
我们可以同时创建该目录并设置模式或权限。在这里,我们设置了所有用户的权限,并包含了粘滞位,以便用户只能删除他们拥有的文件。
接下来,我们将在/etc/exports
文件中覆盖当前的导出设置,使用新创建的目录:
$ sudo bash -c 'echo "/var/exports 192.168.10.11(rw,fsid=0,crossmnt)" > /etc/exports'
$ sudo systemctl reload nfs-server
这些命令在nfshost
上运行,并显示在以下截图中:
这里我们实现了两个新选项:
-
fsid=0
:这将该目录设置为通过 NFS 访问时服务器的根目录。这样,/var/export
目录将从远程客户端以192.168.10.10:/
的方式访问。 -
crossmnt
:这是我们需要的一个巧妙选项,用于允许访问挂载点下的目录。为了将目录挂载到该导出点,我们将使用mount --bind
命令。稍后将详细介绍。
设置导出选项为读/写模式可以让我们通过nfshost
上的文件权限来控制访问权限。任何用户在从nfsclient
访问时都会拥有完全权限,因此需要在文件系统中进行限制。
设置好 NFS 根目录后,我们可以让文件系统中的任何目录在该入口点之后变得可用。我们需要在/var/exports
目录中创建子目录作为挂载点,然后将本地目标挂载到这些挂载点。我们将添加一个名为/home/marketing
的中央共享目录,并将其与现有的/usr/share/doc
目录一起挂载到新创建的exports
目录后面。实现这一操作的命令如下所示:
$ sudo mkdir -m 1777 /home/marketing
$ touch /home/marketing/marketing.doc
$ sudo mkdir -m 777 /var/exports/{doc,marketing}
$ sudo mount --bind /usr/share/doc /var/exports/doc
$ sudo mount --bind /home/marketing /var/exports/marketing
以下要点解释了前述命令行步骤:
-
在执行完命令列表后,我们首先创建了一个中央共享目录,这个目录将在
/home
结构之后添加。这样做可能是由于分区和配额设置,要求marketing
目录位于/home
分区上。 -
我们在此目录中添加一个文档,以便查看一些内容。
-
列表中的第三条命令创建了
doc
和marketing
目录。我们将把这些目录用作挂载点。这些目录是在/var/exports
的 NFS 根目录中创建的。 -
最后的两条命令将本地目录挂载到其导出挂载点。通过这种方式,我们可以轻松地将任何目录直接添加到
/var/exports
之后可用。
列出/var/exports/marketing
目录的内容应显示我们在/home/marketing
目录中创建的文件。请参见以下截图:
同样,查看/var/exports/doc
的内容应该显示/usr/share/doc
的内容。
为了确保本地挂载的持久性,我们需要将它们添加到/etc/fstab
文件中,格式如下:
/originaldir /newdir none bind
现在我们将以 root 身份编辑fstab
文件,并在文件末尾添加这两行,以确保在启动时挂载点被填充:
/usr/share/doc /var/exports/doc none bind
/home/marketing /var/exports/marketing none bind
当你返回到nfsclient
系统时,可以测试这两个导出和权限。原始的/home/marketing
目录是可写的,而/usr/share/doc
目录则不可写:
在nfsclient
系统上,我们可以执行以下命令:
$ sudo mount 192.168.10.10:/ /mnt
$ touch /mnt/marketing/file1
$ touch /mnt/doc/file1
现在文件路径变得更简洁,能够通过服务器的 NFS 根目录使用单一路径访问两个文件夹。我们还需要注意,尽管导出的目录是读/写的,我们可以通过第一个touch
命令写入marketing
目录,但第二个touch
命令会失败,因为目标文件系统是只读的。
使用exportfs
创建临时导出
在/etc/exports
文件中创建永久导出并不总是理想的。如果您想临时定义一个新的导出,可以使用exportfs
命令。由于我们已经将 NFS 根目录定义为/var/exports
,因此我们导出的所有目录都必须位于该结构之后。让我们临时将/var/export/doc
导出到所有主机。可以使用以下命令:
$ sudo exportfs *:/var/exports/doc
在下次重启nfs-server
时,此导出将丢失;然而,如果您需要在此之前删除它,可以执行以下命令:
$ sudo exportfs -u *:/var/exports/doc
如果您需要为临时导出添加导出选项,请像以下命令所示,使用-o
选项进行配置:
$ sudo exportfs *:/var/exports/doc -o ro,all_squash
要显示当前的导出信息,可以单独运行exportfs
命令:
$ sudo exportfs
在防火墙后托管 NFSv4
当您使用 v4 协议访问 NFS 服务器时,客户端和服务器都使用 v4 协议,防火墙配置非常简单,只需要打开 TCP 端口2049
。RHEL 7 的默认防火墙守护进程是firewalld
,并且可以通过命令行使用firewall-cmd
进行管理。
到目前为止,我们一直在使用标准防火墙进行演示,只打开了一个额外的端口2049
,正如本节实验概述中所述。
我们可以使用以下命令列出当前的防火墙配置:
$ sudo firewall-cmd --list-all
输出如下截图所示:
如果需要移除我们添加的端口设置,可以使用以下命令:
$ sudo firewall-cmd --remove-port=2049/tcp --permanent
$ sudo firewall-cmd --reload
当然,客户端将无法再访问 NFS 导出。我们可以选择添加端口或服务条目。要添加服务条目,需要在/etc/services
文件中定义端口及相关服务。可以使用grep
命令轻松检查此文件。以下命令展示了一个例子:
$ grep 2049 /etc/services
我们确实有一个2049
端口的条目,且该服务被称为nfs
。若要在防火墙配置中使用服务名称,可以使用以下命令:
$ sudo firewall-cmd --add-service=nfs --permanent
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-all
这一点在下图中有所说明:
现在,所有防火墙规则都允许该服务,我们可以继续从nfsclient
访问 NFS 导出。如果您想远程使用showmount
等工具,或者如果您有 NFSv3 客户端,则需要打开更多端口并静态设置一些端口。
在防火墙后托管 NFSv3
如果我们尝试从nfsclient
使用showmount
命令,我们应该能够列出远程 NFS 服务器上的导出。语法如下:
$ showmount -e 192.168.10.10
以下是命令和相应的错误截图:
在此阶段,我们可以选择以下选项:
-
收拾行李回家吧,也许明天会更好。
-
Google 搜索错误
-
自行调试错误
诊断 NFSv3 问题
现在,Google 通常在帮助我们方面做得很好,但如果你不学习故障排查技巧,那就失败了。所以让我们放弃第三个选项,安装tcpdump
命令行数据包分析器,这样我们就能看到发生了什么。这可以通过yum
在nfsclient
上安装,命令如下:
$ sudo yum install -y tcpdump
要捕获nfsclient
和nfshost
之间的网络流量并打印被访问的端口号,我们可以使用以下命令:
$ sudo tcpdump -nn -i enp0s8 host 192.168.10.10
这里使用的tcpdump
选项列出如下:
-
-nn
:此选项显示主机的 IP 地址和端口号,而不是其名称。 -
-i
:这是要使用的接口。你需要使用正确的接口名称,我们使用的是enp0s8
,这是我们需要监听的接口。 -
host 192.168.10.10
:这显示了该主机的进出流量。这是nfshost
的 IP 地址。
在另一个控制台或 SSH 会话中,再次尝试showmount
命令。与此同时,在运行tcpdump
的控制台中,我们应尝试两次访问服务器上的 UDP 端口111
并报告错误。以下截图展示了我系统的输出:
UDP 端口111
在nfshost
的防火墙配置中未开放。如果你还记得,我们刚才显示了防火墙允许的服务和端口,而111
不在其中。
端口111
由rpcbind
运行的portmapper
服务保持开放,并在/etc/services
文件中显示为sunrpc
服务。我们可以通过在nfshost
上运行netstat
来检查此项,命令如下:
$ sudo netstat -aunp
这里使用的netstat
选项列出如下:
-
-a
:此选项显示所有端口,包括监听和已建立的端口 -
-u
:此选项仅显示 UDP 端口 -
-n
:此选项显示端口和网络号,而不是将其解析为名称 -
-p
:此选项显示保持端口或连接开放的进程名称
要使用-p
选项,我们必须以 root 身份运行(使用sudo
);否则,进程列将保持空白。
rpcbind
服务的原理是它会返回端口地址,以便所需的服务能够对请求的客户端提供服务。这就是 NFSv3 的工作原理,而showmount
命令仍然使用这个旧协议。来自远程客户端的showmount
请求要求 NFS 挂载守护进程的地址。这是作为进程rpc.mountd
运行的服务。这些服务可以运行在动态分配的端口上。因此,它需要进一步配置,以确保它们能够长期稳定地通过防火墙。
showmount
的处理过程如下图所示,首先是请求rpc.mountd
端口:
我们可以从允许防火墙到nfshost
的rpcbind
流量开始:
$ sudo firewall-cmd --add-port=111/udp --permanent
$ sudo firewall-cmd --reload
提示
别忘了在添加端口后重新加载防火墙。完成这个步骤是很容易被忽略的。
现在我们可以连接到运行在 UDP 端口111
上的rpcbind portmapper
服务,接下来我们应该继续操作。记住,我们实际上是想调试这个过程,并学习一些有用的tcpdump
分析技巧。我们可以重复前面的操作,一边在控制台上运行tcpdump
,另一边在控制台上运行showmount
(这两个控制台都运行在nfsclient
上)。showmount
命令报告的错误现在应该略有不同。为了说明这一点,以下截图显示了当前的错误,其中 UDP 端口111
是开放的:
现在,错误略有不同,我们不再看到错误代码;但是,我们可以从tcpdump
的输出中看到已经收到了来自nfshost
的回复。随后,我们尝试重新连接到20048
端口上的主机。
要识别此端口的用途,我们可以再次使用netstat
,但这次我们将-u
替换为-t
,因为我们想要显示 TCP 端口。由于我们只需要查看监听端口,可以将-a
替换为-l
:
$ sudo netstat -ltnp
我们应该会看到我们尝试连接的端口是由rpc.mountd
打开的。当然,这个端口不能通过防火墙访问。
提示
rpc.mountd
监听的端口可能与您的系统上使用的端口不同,因此需要调整练习以适应您系统上的rpc.mountd
端口以及客户端正在使用的端口。
从tcpdump
获取的输出如下所示。我们可以通过附加的属性(如序列号seq
和窗口大小win
)将其识别为 TCP 流量,这些属性在下图中已被突出显示:
现在我们可以看到,我们还需要打开 NFS 服务器上的 TCP 端口20048
,通过防火墙进行通信;记住,该端口在你的nfshost
上可能不同;我们可以通过在nfshost
上使用firewall-cmd
快速解决这个问题,操作如下:
$ sudo firewall-cmd --add-port=20048/tcp --permanent
$ sudo firewall-cmd --reload
现在,我们可以回到nfsclient
,因为showmount
命令应该已经能够正常工作,如下图所示:
使用静态端口进行 NFSv3
portmapper
服务是必需的,它用于在非静态端口上操作的服务,包括 rpc.mountd
和其他基于 NFSv3 的服务。虽然配置 NFSv4 很简单,因为我们只需要将 TCP 端口 2049
作为防火墙的唯一要求来访问,但使用 v3 时,我们仍然需要访问更多的端口,而这些端口大多数是非静态的。不过,通过 /etc/sysconfig/nfs
文件可以提供帮助,在该文件中我们可以添加条目来为这些服务启用静态端口。RHEL 6 的配置与此不同。搜索引擎常常因提供过时文档而让你失望,其中包括 RHEL 7 的文档,它并未更新。在这里,我们展示了你在 /etc/sysconfig/nfs
文件中设置静态端口所需的正确设置。
当你以 root 用户在 nfshost
上工作并使用你选择的文本编辑器时,你需要编辑以下行:
RPCRQUOTADOPTS="-p 30001"
LOCKD_TCPPORT=30002
LOCKD_UDPPORT=30002
RPCMOUNTDOPTS="-p 30003"
STATDARG="-p 30004"
使用的端口是名义上的,你应选择在系统中未使用的端口。你可以看到一些服务使用 -p
选项来指定端口。rpc.lockd
工具有一个实际的端口配置。这是 RHEL 6 配置所有端口的方式,但在 RHEL 7 中已发生变化。
我们需要重新启动服务,可以单独重启它们,或者重新访问我们之前使用的 for
循环。编辑后的循环如下所示:
for s in rpcbind nfs-server nfs-lock nfs-idmap ; do
sudo systemctl restart $s
done
我们现在已经配置了 nfshost
,使其使用这些通常会因动态端口而导致问题的 NFS 服务的静态端口。我们仍然需要在防火墙规则中配置 UDP 端口 111
以允许访问 portmapper
,但我们现在知道将为其他服务返回的端口,并且这些端口可以添加到配置中。使用我们配置的端口的最终 NFSv3 防火墙配置如下所示:
sudo firewall-cmd --add-port=111/udp --permanent
sudo firewall-cmd --add-port=2049/tcp --permanent
sudo firewall-cmd --add-port=30001/tcp --permanent
sudo firewall-cmd --add-port=30001/udp --permanent
sudo firewall-cmd --add-port=30002/tcp --permanent
sudo firewall-cmd --add-port=30002/udp --permanent
sudo firewall-cmd --add-port=30003/tcp --permanent
sudo firewall-cmd --add-port=30003/udp --permanent
sudo firewall-cmd --add-port=30004/tcp --permanent
sudo firewall-cmd --add-port=30004/udp --permanent
sudo firewall-cmd --reload
如果我们想要完全测试 NFSv4 配置,你需要从现有的导出定义中移除 crossmnt
和 fsid
选项,因为这些是 v4 选项。
配置 NFS 客户端
在从客户端挂载文件系统时,默认实现的协议是 NFSv4 在 RHEL 7 中。我们可以使用 -t
选项将协议显式设置为 v3 或 v4,如下所示:
$ sudo mount -t nfs4 192.168.10.10:/var/exports /mnt #NFS 4
$ sudo mount -t nfs 192.168.10.10:/var/exports /mnt #NFS 3
在下面的截图中,你可以看到我们能够从 nfsclient
使用 NFSv4 或 NFSv3 进行连接:
其他挂载选项可以通过 -o
选项应用于 mount 命令。你可以考虑以下命令选项:
-
bg
:将挂载过程放到后台 -
rsize=xxxx
:指定最大读取请求大小(以字节为单位) -
wsize=xxxx
:指定最大写入缓冲区大小(以字节为单位)
要了解更多 NFSv3 和 NFSv4 的挂载选项,可以通过适当的手册页详细阅读,命令行如下所示:
$ man 5 nfs
自动挂载 NFS 使用 autofs
有一个名为autofs
的客户端服务,它充当本地和远程文件系统的自动挂载服务。它与内核模块和用户空间服务一起工作;当您进入某个目录时,挂载会自动创建。如果需要进行 NFS 挂载,还需要安装autofs
软件包以及nfs-utils
软件包。自动挂载功能不仅适用于 NFS,还可以与其他远程文件系统一起工作。要安装autofs
,请使用以下命令:
$ sudo yum install -y autofs
安装完成后,默认行为是使用/net
目录点来指向网络主机。然后,我们可以访问任何有权限访问的主机上的共享或导出目录,并在/net
目录后输入与服务器名称或 IP 地址匹配的目录。我们只需要创建顶级目录,无需创建子目录。我们只需将目录更改为/net/192.168.10.10
,该目录将自动创建。列出该目录的内容时,将列出nfshost
上的导出根目录。这听起来可能过于完美,但让我们实际演示一下。首先,我们将创建目录,然后启动服务并启用它,如下命令所示:
$ sudo mkdir /net
$ sudo systemctl start autofs
$ sudo systemctl enable autofs
配置完成后,我们可以简单地列出/net/192.168.10.10
目录的内容。我们应该会看到导出配置的顶级目录。对于我们来说,当前是/var
目录,导出目录是/var/export
。如果我们有更多导出的顶级目录,它们也会显示出来。/net/192.168.10.10
目录会自动创建,且autofs
的默认超时是 300 秒(即 5 分钟)。在 5 分钟不活动后,已挂载的文件系统将自动卸载,目录将消失,直到再次需要时才会出现。这是一个典型的安全值;不过,可以配置特定的超时设置。稍后我们将看到如何操作。
以下截图显示了按顺序执行的四个命令以及临时自动挂载目录的列出内容:
根据需要在客户端自动挂载目录,减少了客户端和服务器的负担,这是一种非常有效的挂载方式。要定义我们自己的挂载点,我们可以编辑/etc/auto.master
配置文件。我们将像之前一样添加一个顶级目录。
在/etc/auto.master
文件中,我们将添加以下命令:
/corp /etc/auto.corp --timeout=600
auto.master
文件中的此设置告诉autofs
服务,当进入/corp
目录时,可以从/etc/auto.corp
文件读取配置。此外,我们还将默认超时设置增加到 10 分钟,用于此自动挂载。我们将需要按以下方式创建顶级目录:
$ sudo mkdir /corp
对于该目录的配置文件,在我们的案例中应如下所示:
redhat -fstype=nfs4,rsize=4096,wsize=4096 192.168.10.10:/var/exports
通过这个条目,我们将能够看到服务器导出的内容,并进入/corp/redhat
目录。我们不会创建redhat
子目录。在测试之前,你需要重启autofs
服务:
$ sudo systemctl restart autofs
现在,我们可以访问/corp
目录,它会是空的。以下是截图展示:
如果我们现在访问redhat
目录,它还没有显示出来;但我们将能够列出服务器导出的内容。以下是截图展示:
我已经使用这个好几年了;它仍然是 Linux 上最神奇的体验之一。
总结
我希望你能觉得这一章既紧凑又实用。内容比较多,而且由于需要同时讲解 NFSv4 和 NFSv3,内容变得有些复杂。像大多数技术一样,遗留客户端在一段时间内仍然需要得到支持。这也给我们带来了一个巨大优势,那就是可以通过tcpdump
有效诊断防火墙问题。
使用 NFS 和防火墙的关键是尽可能使用 NFSv4,因为这样我们只需要打开一个静态端口:TCP 端口2049
。而对于 NFSv3,我们需要分配静态端口,并且通常需要为每个协议同时打开 UDP 和 TCP 端口,具体取决于连接的客户端。
完成autofs
这一章是一个真正的高光时刻,因为它非常简单且有效,能够根据需要自动创建目录并挂载它们。还有什么可以更好的呢!
在下一章,我们将继续探讨文件共享,但会研究如何使用 Samba 4 将共享内容提供给 Windows 系统。
第七章:使用 Samba 4 实现 Windows 共享
您的 Linux 设备几乎肯定不会独立运行,其他操作系统将与它们共存。无论您的基础设施位于何处,您很可能至少需要与 Windows 系统进行互操作。这在家庭环境中和企业中都同样成立。考虑家庭市场,您认识多少人使用 Windows 桌面并且拥有一个 Linux 服务器作为中央文件存储。请记住,Linux 服务器可能嵌入在网络附加存储(NAS)中,这是您从街头购买的设备。在大大小小的企业中,Microsoft 的 Active Directory 是一个非常普遍的身份存储,它跨多个系统共享用户帐户。
为了帮助将 RHEL 7 集成到 Windows 环境中,本章将为您提供以下主题的基础知识:
-
Samba 和 Samba 服务概述
-
实验室环境概述
-
配置时间和 DNS
-
管理 Samba 服务
-
RHEL 7 上的 Samba 客户端
-
在 Samba 上配置文件共享
-
故障排除 Samba
Samba 和 Samba 服务概述
在调查我们的主要服务并使用 Samba 时,我们需要安装 samba
软件包;可以通过以下方式安装此软件包:
$ sudo yum install -y samba
安装软件包后,以下服务将被添加到我们的系统中:
-
对于文件和打印共享,我们有
smbd
服务。对于此服务,我们需要打开 TCP 端口139
和445
。 -
如果我们需要响应旧版 NetBIOS 名称请求,我们将需要启动
nmbd
服务。对于较旧的客户端,如 Windows 95、98 和 ME,我们需要此服务。Windows 2000 和 XP 客户端也可以使用此网络浏览协议。如果我们需要此服务,虽然我对此持怀疑态度,我们将需要打开 UDP 端口137
。
如果您想加入 Windows 域,那么可以添加 samba-winbind
或 sssd-common
等客户端工具包。
如果您使用 firewall-cmd
命令,添加 Samba 服务将为您启用三个端口。要将 Samba 服务添加到防火墙中,我们将使用以下命令:
$ sudo firewall-cmd --permanent --add-service=samba
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-services
最后一条命令仅供参考,并非设置防火墙规则所必需。
实验室环境概述
在本章的演示中,我们将使用两台虚拟机,这些虚拟机运行在Oracle VirtualBox虚拟化环境中。
我们有一个 Microsoft Server 2008R2 活动目录域控制器,IP 地址为 192.168.0.252
,以及一个 RHEL 7.1 主机,IP 地址为 192.168.0.69
。
配置时间和 DNS
尽管如果我们使用 RHEL 主机进行简单的文件和打印共享,获取准确的时间和 DNS 并不是太大的问题;然而,我们很可能需要将 RHEL 服务器加入到 Active Directory 域,以便能够使用单点登录。用户将能够使用与 Active Directory 中相同的凭据访问他们的共享,而无需在 RHEL 服务器上创建用户账户和密码。
在本章中,我们将仅关注文件共享,而在第八章中,我们将服务器加入到 AD 域。
在第三章,配置关键网络服务中,我们配置了时间服务。现在,让我们来看看如何将chronyd
时间源设置为 Active Directory 时间服务器。如果我们使用 NTP,则将 NTP 时间源设置为 Active Directory 服务器。或者,确保 Active Directory 时间源与 RHEL 主机使用的时间源相同。
要在 Windows 2008R2 Active Directory 服务器上配置时间源,你需要打开管理员命令提示符,并键入以下命令:
c:\> net stop w32time
c:\> w32tm /config /syncfromflags:manual /manualpeerlist:"uk.pool.ntp.org"
c:\> w32tm /config /reliable:yes
c:\> net start w32time
c:\> w32tm /config /update
c:\> w32tm /resync
c:\> w32tm /query /status
最终状态子命令的输出如下所示:
我觉得在网络上拥有准确的时间是必须的,无论你是否打算加入一个域。
要加入 Active Directory 域,我们必须能够解析定位域控制器的服务记录。实现这一点的最简单方法是将 RHEL 的 DNS 解析器指向托管 DNS 的 Active Directory 服务器。我只有一个域控制器,并且它也是 DNS 服务器。为了确保我们能够正确解析这些名称,我们在 RHEL 7 上写入接口配置文件。我的系统中,这些文件如下:
/etc/sysconfig/network-scripts/ifcfg-eno1677736
/etc/sysconfig/network-scripts/ifcfg-eno33554992
确保 DNS1 指向 Active Directory 域控制器,并且在两个文件中都设置PEERDNS=yes
。或者,配置/etc/resolv.conf
文件,设置一个名称服务器,并确保在所有接口文件中设置PEERDNS=no
属性。
以下截图显示了仅在接口文件中进行设置的第一个配置选项:
在这两个接口文件中设置此项(如果你有两个网卡 NICs),你可以简单地重启NetworkManager
服务,方法如下:
$ sudo systemctl restart NetworkManager
要测试配置,你可以尝试解析托管在 DNS 中的 Active Directory 域的名称服务器。对于本书,我们简单使用example.com
。要从 RHEL 7 主机解析此域,我们运行以下命令:
$ dig -t ns example.com
dig
命令查找example.com
域的ns
类型或名称服务器记录。输出应类似于以下截图:
管理 Samba 服务
要访问 Samba 资源,用户需要一个可用的 POSIX(Linux)用户账户和一个 Samba 账户。POSIX 账户可以是/etc/passwd
文件中的普通账户,或者该账户可以集中在 LDAP 或 Active Directory 中。当一个 POSIX 账户启用 Samba 时,会向用户账户添加 Windows 系统所需的其他属性。要为现有的 POSIX 账户启用 Samba,我们可以使用/bin/pdbedit
命令。它可以与以下账户存储中的 Samba 账户配合使用:
-
/etc/samba/smbpasswd
文件 -
位于
/var/lib/samba/private/passdb.tdb
的tdbsam
数据库(这是默认的 Samba 账户存储) -
OpenLDAP 目录服务
由于现有的域账户已经具备了 Samba 所需的属性,因此无需为这些账户启用 Samba。
首先,我们将列出所有已启用的 Samba 账户。当然,我们只安装了 Samba 并未启用任何其他账户。此外,由于当前 RHEL 服务器并未加入域或 LDAP,因此我们只在/etc/passwd
文件中拥有本地账户:
$ sudo pdbedit -L
该命令应无输出,因为我们没有任何启用 Samba 的账户。如果您以标准用户身份运行命令而没有错误,则在尝试访问数据库时会看到权限违规。
我们现在将为root
和标准用户andrew
启用现有账户。我们不仅会创建一个账户并为 Samba 启用它,还会添加存储在分配的 Samba 账户后端中的属性。在默认情况下,这是tdbsam
数据库。要启用这两个账户,请使用以下命令:
$ sudo pdbedit -a -u root
$ sudo pdbedit -a -u andrew
系统会提示为每个账户设置一个新的 Samba 密码。理想情况下,这个密码应该与其 POSIX 密码不同。启用andrew
的 Samba 账户后的截断输出显示在以下命令行截图中:
神奇的是,当我们运行账户列表时,我们将能够看到这两个账户:
$ sudo pdbedit -L
输出应与以下截图相似,并将账户名称调整为与您自己的账户相符:
您可以使用-L
选项结合-v
,以获取类似于启用账户时所看到的详细信息。
Samba 的主要配置文件是/etc/samba/smb.conf
。该文件被划分为多个部分,每个部分(除了[global])定义了一些形式的共享资源。每个部分通过方括号 []
中的部分名称来表示。除了[global]
部分外,还有两个其他特殊部分。这些部分在以下列表中定义:
-
[global]
:在全局部分设置的属性指的是整个 Samba 服务器,而不是特定的共享资源。 -
[homes]
:此部分的存在允许用户轻松连接到他们自己的主目录,而无需额外的管理操作来共享用户的主目录。这可以在用户名和账户名与主目录名不完全相同的情况下正常工作。用户andrew
将通过类似以下命令的方式连接到他的主目录共享://<server-name or IP>/andrew
用户账户的主目录属性将被读取,然后用户将连接到分配给他们的主目录。
-
[printers]
:此部分的存在使得本地打印机可以自动共享,无需进一步的管理操作。用户将使用以下 URL 连接到打印机://<server-name or IP>/<printer-name>
你会发现 /etc/samba/smb.conf
文件中有很多注释,部分注释使用 #
符号,另一些则使用分号(;
)符号。这两者都是有效的注释,但这种不一致性有些烦人。
如果你想创建一个备份并删除空行和注释行,可以尝试从 /etc/samba
目录运行以下命令:
$ sudo sed -i.bak '/^\s*[;#]/d;/^$/d;' smb.conf
我们用来搜索要删除的行的正则表达式比通常的稍微复杂一些,但它仍然能为我们做出惊人的效果。示例中有两个 sed
表达式,/^\s*[;#]/d
。
上述表达式将删除文件中的注释行。
我们寻找包含(^
)和任何空白字符(\s
)的行。此外,使用 *
来检查零个或多个空白字符。通过这种方式,我们允许行首有一个可选的空白字符,但它必须紧跟着 ;
或 #
([;#]
)符号。用简单的英语来说,这扩展为查找被注释的行,不管它们是以空格/制表符开始,还是直接以注释开始。搜索字符串在两个 /
字符之间,并且 d
命令用于删除匹配的行:
/^$/d
上述表达式删除文件中的空行或空白行。
其效果是将文件的行数从 320 行减少到 20 行,仅需几秒钟。我们保留原始文件作为备份,即 smb.conf.bak
。
一个简单的 [global]
部分可能看起来像下面的截图:
这些设置的详细信息如下:
-
workgroup = MYGROUP
:此项表示要加入的 NetBIOS 工作组,在网络邻居视图中显示。 -
server string = Samba Server Version %v
:这将在网络视图中的服务器名称旁边显示描述。%v
变量将显示 Samba 版本;在我们的例子中,版本是4.1.2
。 -
log file = /var/log/samba/log.%m
:此项指定日志文件的路径;%m
变量代表机器名(主机名)。 -
max log size = 50
:此项指定日志文件在轮换之前的最大大小(以 KB 为单位)。 -
idmap config * : backend = tdb
:这是映射机制,用于将 POSIX 用户 ID 和组 ID 映射到 SIDs(安全标识符)。 -
cups options = raw
:这些是打印机设置,告诉 cupsCUPS 进程直接将作业发送到打印机,而不是尝试解释它。它已经由 Windows 打印机驱动程序处理过,不需要进一步处理。
您还可以使用指令控制哪些主机或网络可以访问系统,使用hosts allow
和hosts deny
指令。例如,以下[global]
部分中的属性设置将仅允许给定网络访问任何共享。127.0.0.1
主机将始终具有访问权限,除非被显式拒绝。以下任何一种方法都正确,允许访问192.168.0.0/24
:
hosts allow = 192.168.0.
或者:
hosts allow = 192.168.0.0/255.255.255.0
这些host allow
设置及其兄弟host deny
设置也可以在共享级别进行管理,但[global]
设置将优先于共享级别的任何配置。
如果对正在运行的配置进行更改,则可以运行预检查,测试我们更改的完整性,然后再重新启动服务。为此,我们使用testparm
命令。我们仅需以 root 身份运行,如下所示:
$ sudo testparm
nmb
和smb
服务是独立管理的。我们可以使用以下命令启动并启用 Samba 文件服务器:
$ sudo systemctl enable smb
$ sudo systemctl start smb
由于[homes]
部分是默认配置,我们现在可以开始测试系统。
RHEL 7 上的 Samba 客户端
目前,我的防火墙服务已禁用,因此我不需要担心防火墙;但是,我只需要添加 TCP 端口445
和139
,或者samba
服务。
如果我们安装了 Samba 客户端包,就可以列出给定用户可以访问的所有共享。以下是该命令的输出示例:
$ sudo yum install -y samba-client
$ smbclient -U andrew -L //localhost
一旦安装了 Samba 客户端,我们可以使用它以andrew
身份登录;系统会提示输入密码,并列出本地主机上的共享。我们应该看到来自[homes]
特殊共享部分的列出的主目录。默认情况下,始终会有此共享。我们将看到预期的输出,而不是共享名称andrew
,如下截图所示:
当然,我们可以在此处停留,并隐瞒当前设置中存在一些 SELinux 陷阱的事实。我们可以连接到一个共享,但 SELinux 会阻止访问用户的主目录。虽然第十章,使用 SELinux 保护系统,将详细讲解 SELinux,但我们可以通过简单的布尔值更改,快速且安全地访问共享。首先,我们将测试当前配置。SELinux 处于强制模式,防火墙未运行。为了挂载 Samba 共享,我们可以使用以下命令,以单行形式编写:
$ sudo mount -t cifs -o username=andrew,password=Password1 //192.168.0.69/andrew /mnt
我们不需要在挂载选项中包含密码;如果不包含密码,系统会在挂载过程中提示输入用户的 Samba 密码。这应该能成功,但如果我们尝试访问挂载目标(在本例中是 /mnt
),则会被拒绝访问。为了解决这个问题,我们可以查询 SELinux 配置中的 Samba 用户主目录设置。以下命令演示了如何实现这一点:
$ getsebool samba_enable_home_dirs
在 Samba 中,默认配置设置为 off
,并且设置为 as
,这会阻止对 home
目录的访问。我们使用以下命令启用 Samba 服务:
$ sudo setsebool -P samba_enable_home_dirs on
启用 Boolean 后,我们即可立即访问共享。无需卸载并重新挂载用户主目录。-P
选项使该更改成为永久性更改,这样我们可以确保该更改一直生效,直到我们需要禁用该设置,且系统仍然在 SELinux 的保护下。
要查看 SELinux、Samba Booleans 及其设置列表,可以使用以下命令:
$ getsebool -a | grep samba
当然,只要我们拥有正确的凭证,也可以通过 Windows 设备连接到共享。以下截图展示了来自 Windows 2008R2 服务器映射的驱动器,连接到 Andrew 的主目录:
在 Samba 中配置文件共享
我们已经看到了 Samba 使文件共享的主要功能,尤其是对用户主目录的支持。虽然此功能默认已启用,但我们仍然需要通过向 smb.conf
添加自己的配置段来创建我们自己的文件共享。在 Red Hat 服务器上,我们有一个 /data
目录,之前在 第五章,实施 btrfs 中使用过。如果我们需要将此目录共享给 Windows 客户端,那么我们就需要使用 Samba 工具。
我们可以以 root 权限编辑 smb.conf
文件并添加新的配置段。我们在该段中使用的属性控制共享的访问和使用权限。至少,我们需要为共享定义路径属性,才能使其有意义。有关完整的选项列表,可以参考 smb.conf
文件的 man
页面。以下截图展示了我们为服务器上的 /data
目录添加的共享定义:
读取列表限制共享数据的访问,仅允许列出的用户读取;在本例中,我们添加了一个组。@
符号表示列表中的一个组。
我们还需要通过更改 /data
目录及其内容的 SELinux 上下文来进一步处理 SELinux。我们可以使用 chcon
命令来修改上下文:
$ sudo chcon -R -t samba_share_t /data
不要忘记使用 testparm
测试配置;如果一切正常,我们可以按照如下方式重新启动 smb
服务:
$ sudo systemctl restart sm
b
现在,我们可以浏览并访问我们定义的 Samba 共享。使用 Windows 2008R2 服务器,我们现在可以浏览网络,并看到 andrew share
和 data share
的 home
目录。以下截图展示了这一点:
我们可以理解为什么 Samba 在小型和大型环境中都是如此重要的工具;文件共享如此简单,并且能够融入我们现有的基础设施中。
故障排除 Samba
如果我们遇到 Samba 问题,随时可以再次检查 testparm
命令的输出,确保没有遗漏任何重要的设置。我们可以从 smb.conf
的手册页中检查哪些设置是有效的,以便关注一些特定的配置项。
我们还可以检查日志文件,这些文件位于 /var/log/samba/
目录中。
会有代表客户端访问的日志,具体命令如下所示:
log.<client ip-address>
或者:
log.<client-hostname>
还会有 log.smbd
守护进程日志。
如果需要更多关于守护进程日志的详细信息,可以在 smb.conf
的 [global]
部分设置日志级别属性,具体如下所示:
log level = 3
这将增加日志记录的详细程度,可能有助于排查问题。我们建议不要长时间将日志级别设置得这么高,完成后应删除该设置。日志级别可以从 0
设置到 10
,其中 0
表示低级别,10
表示高级别,但 3
级别对于大多数人来说已经足够详细。
概述
在本章中,我们探讨了如何在不禁用 SELinux 的情况下与 Windows 客户共享文件系统。我希望你能理解 SELinux 的重要性,并且一旦掌握其基础,便能轻松保持 SELinux 的启用状态。
尽管我们在本章中没有实现防火墙,但我们再次审查了 firewalld
的设置以使其生效。有关使用 firewalld
进行防火墙配置的内容,请参阅第十一章,使用 firewalld 进行网络安全。
在下一章中,我们将探讨如何将 RHEL 7 集成到 Windows Active Directory 域中。
第八章:将 RHEL 7 集成到 Microsoft Active Directory 域中
在上一章中,我们讨论了如何将资源共享给微软的客户端。现在,在真正的共生精神下,我们将看到 RHEL 如何利用 Active Directory 用户和组,将域作为身份存储。如果用户可以访问 RHEL 服务器的控制台,那么仅凭他们的 Active Directory 凭据,他们就可以访问 RHEL。这不仅简化了控制台的访问,还简化了访问 RHEL 7 Samba 服务器上任何共享文件夹的过程。
我们将按照这样的结构来安排这一章内容,让你在深入了解如何使这些简单工具正常工作之前,先看到 Active Directory 集成所能提供的各项功能和优势(好东西)。
在本章中,我们将讨论以下主题:
-
身份管理概述
-
实验环境概述
-
准备加入 Active Directory 域
-
使用领域(realm)管理域的注册
-
使用 Active Directory 凭据登录 RHEL 7
-
使用
adcli
进行用户和组管理 -
使用
sudo
委派 Active Directory 账户 -
离开域
-
将 Active Directory 作为
sssd
的身份提供者
身份管理概述
为了开启我们的盛宴,我们将首先聚焦于身份管理在企业中的重要性。如果没有使用某种身份存储或保险库来集中管理用户账户,这些账户将需要被复制,因为它们需要被其他系统访问。正如你能想象的那样,随着大量用户账户的创建,每个系统上都会形成独立的账户孤岛,这些账户很容易失控。然而,我们不必太担心这些账户的创建和管理,除了这点外,它本身并不会构成安全问题。如果某个用户无法访问某个资源,他们很快就会通知你。账户孤岛的问题在于当用户离职时会发生什么;你是否认为每个离开组织的用户的账户都会被删除或(至少)禁用。无论系统多么完善,总会有一些账户未被处理,从而引发安全问题。良好的身份管理,确保每个用户只有一个账户,可以解决管理负担,更重要的是,解决安全漏洞。
当然,较小的问题与这些账户的管理有关,例如密码更改,以及随着时间的推移,可能需要更改名称。理想情况下,组织中的每个用户应该只有一个身份,即他们用于访问任何具有权限的资源的唯一凭据集。这可以通过某种形式的中央目录服务来实现,作为身份库。这可能是 Active Directory,但也可以是其他形式的LDAP(轻量级目录访问协议)服务器。在小型到中型环境中,Active Directory 可能就足够了,但随着组织的发展,身份库的规模不断扩大,可能需要为用户创建一个完全独立的目录。中央用户存储可以同步更改到其他连接的系统。
微软有其身份管理套件来围绕 Active Directory 构建,Red Hat 也有其身份管理目录服务器。本章将重点介绍并直接将 RHEL 7 集成到单一域 Active Directory 环境中。
实验环境概述
本章的演示将使用两台虚拟机运行在Oracle VirtualBox虚拟化环境中。
我们有一台 Microsoft Server 2008R2 的 Active Directory 域控制器,IP 地址为 192.168.0.252
,以及一台 IP 地址为 192.168.0.69
的 RHEL 7.1 主机。这与我们在第七章,使用 Samba 4 实现 Windows 共享中使用的设置相同;我们已经将时间和 DNS 配置为相同方式。如果你没有完成第七章,使用 Samba 4 实现 Windows 共享,请确保你已经将 RHEL 服务器配置为使用域控制器进行时间和名称解析。
准备加入 Active Directory 域
从我们在第七章,使用 Samba 4 实现 Windows 共享中所见,使用 Samba 共享文件,我们可以理解这是非常了不起的技术。我们始终需要提醒自己,这一切都没有任何价格标签,也不需要客户端访问许可证。
提示
Samba 文件共享是免费的,即无成本且自由的;你可以按照自己的意愿使用它。这是开源软件的基本前提,也是 Linux 的核心所在。
可能成为潜在障碍的一个大问题是需要在 RHEL 服务器和工作站所属的 AD 域中维护用户账户。如果我们实现多个服务器,这个问题会因为需要在每一台服务器及 AD 域上都有账户而更加复杂。简单的解决方案是将 RHEL 服务器并入 AD 域,并使用 AD 账户进行资源访问。这样,我们可以通过单一的登录方式访问 Active Directory,并访问 RHEL Samba 服务器上的共享资源。
提示
如果 Active Directory 未设置,可以通过在 RHEL 上安装 openLDAP 服务器来建立集中账户共享。这样,某一台 RHEL 服务器可以作为身份库,将账户共享给其他服务器上的 LDAP 客户端。
无论是 Samba 文件共享,你的 Active Directory 用户可能还需要通过 SSH 或其他机制访问 RHEL 服务器。为此,他们需要在每一台 RHEL 服务器上定义账户。将 RHEL 服务器加入 AD 域后,可以在登录任何成员服务器时使用用户的 AD 账户,这些成员服务器包括 RHEL 服务器或桌面。此外,还可以通过 /etc/sudoers
文件和正常的文件权限机制将权限委派给这些账户。
在加入 AD 域之前,我们需要确保已设置时间服务和 DNS,具体步骤详见 第七章,实现 Windows 共享与 Samba 4。在这些基础设施服务就绪后,我们还需要在 RHEL 服务器上安装以下软件包:
-
realmd
:用于管理加入和隶属 Active Directory 域 -
samba
:表示 Samba 服务 -
samba-common
:表示用于服务器和客户端的共享工具 -
oddjob
:这是一个运行客户端偶尔任务的 D-bus 服务 -
oddjob-mkhomedir
:与 odd job 服务一起使用,为 AD 账户创建主目录(如果需要的话) -
sssd
:系统安全服务守护进程,可根据需要转发客户端认证 -
adcli
:用于加入和管理 AD 域的工具
以下命令显示了必要软件包的安装:
$ sudo yum install oddjob realmd samba samba-common oddjob-mkhomedir sssd adcli
使用 realm 来管理域的加入
安装这些软件包后,我们可以使用 realm
命令来管理我们的加入。该命令是我们已安装的 realmd
包的一部分。我们可以使用 list 子命令确保我们当前没有加入任何域:
$ realm list
输出应该是空的。现在,我们可以继续进行下一步:加入域。在一个简单的环境中,你应该知道你要加入的域;至少我们希望你知道。在我们的案例中,我们确实知道它是 example.com
。通过使用 discover 子命令,我们可以验证是否已安装所有必需的软件包,如下所示:
$ realm discover example.com
此命令的输出将显示这是一个 Active Directory 域,以及在加入 AD 域之前应该具备的所需包。以下截图展示了这一点:
根据你的 Active Directory 功能级别,可能需要安装 samba-windbind
或 sssd
包。我们在 2008R2 的 Active Directory 上使用,默认配置为 Windows Server 2003 的级别。此时,你应该确认已安装所有必需的包。
提示
如果我们不需要共享资源,就不需要安装 samba
包;samba
仅用于共享,而不是用于加入域。
由于这是一个 Kerberos 域类型,join
子命令将把服务器作为成员服务器加入域,并初始化 /etc/krb5.keytab
Kerberos 密钥表文件和 /etc/krb5.conf
配置文件。有关这些文件的详细信息将在本章结束时提供。要加入 AD 域,使用以下命令将计算机添加到 AD 域中的默认文件夹:
$ sudo realm join --user=administrator@example.com example.com
如果你想将其添加到 Active Directory 中的指定组织单位(OU),你首先需要创建该 OU,或者至少确保它已经存在。在 OU 存在的情况下,命令将类似于以下内容,其中我们将其添加到 Linux OU:
$ sudo realm join --computer-ou="OU=Linux" \ --user=administrator@example.com example.com
这是我们将用来将 RHEL 服务器加入路径的方法:
OU=Linux,DC=example,DC=com
使用这两种方法中的任何一种,你将被提示输入域管理员密码或具有将计算机添加到 AD 域权限的用户密码,以及你的 sudo
用户密码(如果需要)。该命令可能需要几分钟才能生效,请耐心等待,直到返回命令行提示符。作为标准用户,你可以再次使用 realm list
命令列出已加入的域。我们应该注意到,第一次的输出可能看起来与我们之前运行的 realm discover example.com
命令类似;然而,经过仔细检查后,我们会发现我们现在是一个成员服务器,命令输出中会显示 configured: kerberos-member
,如以下命令所示:
$ realm list
前面命令的输出如以下截图所示:
使用 Active Directory 凭证登录 RHEL 7
欢迎来到集中式账户的世界。我想你会承认,使用 RHEL 7 这个过程非常简单,远比之前的 RHEL 版本要简单得多。我们现在可以开始使用来自 Active Directory 的中央用户账户了。
要登录 RHEL 7 服务器,我们可以使用 Active Directory 的 UPN(用户主体名称)。格式为 user@<完全限定域名>
。例如,如果我们在 example.com
域中有一个名为 jjones
的账户,我们可以使用以下命令登录 RHEL 服务器:
jjones@example.com
下图显示了当我们使用switch user
命令以jjones
的 AD 帐户登录时的过程。请注意,由于jjones
的主目录不存在,oddjob
会友好地为我们创建它,如下图所示:
要使用 SSH 工具(例如 Windows 的 PuTTY)远程连接,我们将使用以下语法,使用两个@
符号;这看起来可能有点奇怪,但它是正确的:
jjones@example.com@192.168.0.69
以下截图显示了从 Windows 的 PuTTY 客户端连接到 RHEL 的 SSH 连接:
我们现在已经看到,我们可以在 Linux 系统上使用 Active Directory 帐户。通过将 Red Hat 服务器作为我们域的一部分,我们可以使用一套凭据登录 Linux。当用户离开组织时,现在只需要删除或禁用一个用户帐户。我们已经在单台服务器上看到了这一点,但这同样适用于所有的 RHEL 7 或 CentOS 7 服务器和桌面;这个过程在各个设备上都是一样的,使我们更高效、更安全。
使用 adcli 进行用户和组管理
我们不仅仅局限于使用这些域帐户;我们还可以通过 Linux 服务器的命令行管理 Active Directory。在 Active Directory 中具有正确的权限后,我们可以:
-
创建用户和组
-
修改组成员资格
-
删除用户和组
虽然这些工具的功能不如本地操作系统中的工具丰富,特别是在使用 PowerShell 时,但 Linux 设备提供的某些管理功能仍然有其需求和优势。
如果你是 Linux 管理员并且主要在 Linux 上工作,那么将 Active Directory 用户添加到你用来在 Linux 上进行委派的组中是有意义的。例如,你可以维护一个名为LinuxAdmins
的 Active Directory 组,并通过/etc/sudoers
文件将权限委派给该组。完全正确的是,你管理和控制 AD 组,而不一定是 AD 中的Domain Admins
组。
列出 Active Directory 信息
首先,我们将使用adcli
命令,来看一下info
子命令。它可以显示已发现的域及域控制器的详细信息。我们可以作为标准用户运行此命令,如下所示:
$ adcli info example.com
输出将显示域控制器的 Active Directory 角色以及站点的详细信息,如下图所示:
通过这种方式,我们将能够验证连接以及我们连接的域控制器。
创建 Active Directory 用户
该命令可能不是最有用的工具,因为我们可以创建用户,但无法启用帐户或为新用户设置密码。因此,这个命令的使用价值不如adcli
中的其他一些工具。示例命令如下:
$ adcli create-user fjones --domain=example.com --display-name="Fred Jones"
该命令将尝试以管理员身份登录到域,并会提示输入密码。若要以其他用户身份登录,您可以使用-U
或--login-user
选项。
为了完整性,我们覆盖了create user
命令,但实际上,用户仍然需要在 Active Directory 中启用并设置密码。
要删除我们刚刚创建的帐户,我们将使用以下命令:
$ adcli delete-user --domain=example.com fjones
创建 Active Directory 组
在许多方面,adcli
命令对我们作为 Linux 管理员非常有用。因此,只要我们的域帐户具有在 AD 中创建和管理组的权限,我们就应该管理影响 Linux 访问的组成员身份。假设用户帐户已经创建,我们无需关注这些组的密码管理、创建和成员身份。我们将像以前一样使用管理员帐户访问域,但如果我们的帐户具有相应权限,也可以使用我们自己的帐户。
要在我们放置服务器的 Linux 组织单位(OU)中创建 Linux 用户组,我们将使用以下命令:
$ adcli create-group --domain=example.com \ --domain-ou="OU=Linux,DC=example,dc=com" "Linux Users"
我们可以通过导航到域控制器上的 Active Directory 用户和计算机中的OU(组织单位)来验证这一点。在以下截图中,我们可以看到我们有服务器组和新组:
我们将保留创建的组,因为我们将向其中添加用户;删除组的过程类似于删除用户的过程,如下命令所示:
$ adcli delete-group --domain=example.com "Linux Users"
如果我们需要任何命令的帮助,可以使用类似以下命令的语法帮助:
$ adcli delete-group --help
只需使用您需要帮助的正确子命令。
管理 Active Directory 组成员身份
现在我们有了 Linux 用户组,我们可以管理该组的成员身份。在 AD 域中,我们有jjones
用户,可以将其添加到此组中。以下命令展示了如何在我们的域中执行此操作:
$ adcli add-member --domain=example.com "Linux Users" jjones
提示
除非在特定上下文中创建组或用户,否则我们可以仅通过SAMAccountName
属性(即用户或组名称)来引用该对象。这是域中的唯一标识符。在前面的示例中,我们可以简单地将该组称为Linux Users
,将用户称为jjones
。如果组名中有空格,则需要使用引号保护。
委派 Active Directory 帐户使用 sudo
能够管理 Active Directory 组成员身份是我们管理 Linux 的基础。我们可以将文件和目录的所有权分配给这些组,并(更重要的是)通过/etc/sudoers
文件委派系统上的权限。
让我们看看这种委派是如何工作的。我们将在 Active Directory 中创建一个新组,并将管理员添加到该组中。作为一个简单的设置,我们仅限于已创建的用户,如以下命令所示:
$ adcli create-group --domain=example.com \ --domain-ou="OU=Linux,DC=example,dc=com" "Linux Admins"
$ adcli add-member --domain=example.com "Linux Admins" Administrator
现在我们有两个组,可能用于委派:Linux Users
和Linux Admins
。为了使用sudoers
系统进行委派,我们以 root 用户身份或使用sudo
运行visudo
命令。此文件可以用作委派,使选定的用户能够以 root 身份运行某些命令。这些命令必须以sudo
命令开头。你可以将sudo
视为类似于 Windows 系统中的runas
命令:
$ sudo visudo
这将打开/etc/sudoers
文件进行编辑。我们可以使用G
移动到文件末尾,然后按o
插入新行。
我们将向/etc/sudoers
文件中添加这两行代码:
%Linux\ Admins@example.com ALL=(root) ALL %Linux\ Users@example.com ALL=(root) /sbin/mount /mnt/cdrom, /sbin/umount /mnt/cdrom
提示
请注意这里使用\
来保护空格。这是必需的,因为sudoers
文件不允许使用引号。
Linux 管理员组被允许使用sudo
以 root 身份运行系统上的所有命令。Linux 用户组只能运行mount
和umount
命令,来挂载和卸载cdrom
设备。
在vi
中完成所有更改后,我们可以按ESC
键退出,再按:x
保存并退出插入模式。
以下是示例系统的截图,显示了应有的更改:
当我们以jjones
身份登录时,我们现在会发现我们属于Linux Users
组和Administrator
的Linux Admins
组。此外,两个用户都将属于Domain Users
组。
我们可以作为任一组的用户运行以下命令:
$ id -Gn
上面的命令将显示用户所属的组名。域管理员账户将有多个组成员身份,但最重要的是会包括Linux Admins
组。这将允许用户以 root 身份运行所有以sudo
开头的命令,就像我们看到的具有类似权限委派的andrew
账户。
以下截图显示了以管理员身份运行id
命令时的输出:
我们还可以将文件系统的所有权分配给用户和组。从目录中,我们可以进行这一操作。当前我们仍然以域管理员账户登录到 RHEL 7.1 系统,我们将通过更改目录的组所有权来验证sudo
条目的正确性;这通常是 root 用户的特权操作:
$ sudo chgrp Linux\ Usersexample.com /data
$ ls -ld /data
在前面的示例中,我们将/data
目录的组所有权更改为Linux Users
组;随后,我们还展示了该目录的所有权。为了更加清晰,我们提供了一张截图来演示这一过程:
离开一个域
到目前为止,我们已经能够通过使用sudo
的委托权限以及文件和目录的所有权和文件系统,展示与 Active Directory 的真正互操作性。这非常出色,完全符合企业级 Linux 系统的预期;然而,尽管这一点非常突出,但仍然会有需要将 Linux 服务器从域中移除的情况。通常,这发生在服务器从一个域中移除后,再加入另一个域。若需要这样操作,realm
命令使得这一过程变得简单,可以通过将操作反向到join
子命令来实现,示例如下:
$ sudo realm leave example.com --remove
额外选项:--remove
将确保计算机帐户也从域中删除;否则,它需要单独删除。暂时,我们将计算机保留在域中。
理解 Active Directory 作为 sssd 的身份提供者
在许多方面,Linux 上能实现如此简单的功能是非常受欢迎的;然而,这种简单性掩盖了幕后的复杂事件和过程。现在是时候深入了解sssd
是如何工作的了。
我们首先需要提醒自己回顾在过程中唯一的手动操作部分——即设置集成到 Active Directory 所需的时间和 DNS 基础设施服务。下图展示了 RHEL 服务器与 Active Directory 之间的关系:
当我们使用realm
命令查询 Active Directory 域时,从结果信息中可以看到,我们需要安装sssd
包等其他组件。系统安全服务守护进程(sssd
)提供了一组守护进程,用于管理对远程目录和身份验证机制的访问,在我们的案例中,指的是 Active Directory。sssd
服务为我们的系统提供NSS(名称服务切换)和PAM(可插拔认证机制)接口,以及一个模块化的后端系统,用于连接多个不同的帐户来源和 D-bus 接口。考虑到这一点,我们应该理解系统上已经为我们添加并配置了 NSS 和 PAM 模块。
在远程 Active Directory 上识别帐户是通过 LDAP 进行的,身份验证是通过 Kerberos 完成的,连接到 AD 域。LDAP 帐户查找参考并调用/usr/lib64/libnss_sss.so.2
NSS 模块和/etc/nsswitch.conf
文件。身份验证将通过/lib64/security/pam_nss.so
进行引用。
我们可以扩展关系图,加入sssd
,如下所示:
配置 NSS
名称服务切换(NSS)配置文件 /etc/nsswitch.conf
,被各种 NSS 库使用;其中一个 NSS 库是 /usr/lib64/libnss_sss.so.2
。NSS 配置文件确定您可以从中获取名称服务信息的源和其顺序,范围包括各种类别。每个信息类别由资源数据库名称标识;这可以是用于名称解析的hosts
和用于查找用户账户的passwd
。
在我看来,最简单的方法是使用 hosts
数据库来解释这些功能是如何工作的。在 /etc/nsswitch.conf
中 hosts
的条目如下所示:
hosts: files dns
通过生效的设置,首先通过解析/etc/hosts
本地文件来实现前述的名称解析,然后通过 DNS 解析库。如果颠倒这些条目,DNS 将在本地文件之前检查。
如果我们在文件中检查sss
,我们可以看到所有依赖特定库的数据库。grep
命令可用于隔离这些条目,如以下命令所示:
$ grep sss /etc/nsswitch.conf
查询的结果应该类似于以下的屏幕截图:
这些是默认设置,但我们不必接受它们;如果需要,我们可以实施更改。但是,这个顺序可能是最好的,因为它允许在搜索域之前解析本地账户(并非域账户将冲突,因为它们是使用用户的完整 UPN 指定的)。
这里解释了数据库名称:
-
passwd
: 这指定了用户账户。 -
shadow
: 这表示密码信息。 -
group
: 这指定了组账户。 -
services
: 这表示服务名称解析。 -
netgroup
: 这指定了可以在访问控制规则中使用的主机组。 -
automount
: 这指定了由autofs
自动挂载的目录。
在许多设置中,禁用底部三个元素 services
、netgroup
和 automount
很容易。例如,如果在运行工具(如netstat
)时保留默认设置,可以帮助调整目录访问,它将在 /etc/nsswitch.conf
的服务数据库条目中运行 LDAP 查询以解析端口地址到服务名称。
防止 LDAP 查询的服务条目将类似于以下命令:
services: files
配置 PAM
我们通常可以将可插入认证模块(PAM)保留为原样,但是我们将在这里查看它们的配置。
可以使用 PAM 的服务通过/etc/pam.d
目录中的相关 PAM 模块进行配置。它们可以是单独的文件,比如/etc/pam.d/login
,也可以通过被多个服务引用的命令文件(如/etc/pam.d/system-auth-ac
)。
我们可以使用grep
再次从/etc/pam.d/system-auth-ac
文件中筛选sss
,以显示sssd
与 PAM 一起使用的配置。输出如下截图所示:
我们可以看到认证模块在所有可能的触发条件下都会被使用:
-
auth
:在认证过程中使用 -
account
:用于账户限制 -
password
:用于密码更改事件 -
session
:在登录会话期间使用
让我们看看一些与认证模块一起使用的参数(例如use_first_pass
)。一些可能的参数如下:
-
forward_pass
:输入的密码可以用于其他模块 -
use_first_pass
:不会提示输入密码,而是使用先前输入的密码。 -
use_authtok
:更改密码时,可以使用先前输入的密码来认证密码更改。 -
retry=N
:如果设置了此项,当用户输入错误的密码时,可以提示用户多次输入密码。
配置 Kerberos
当你使用 realm 加入域时,会创建/etc/krb5.conf
密钥表文件来将 RHEL 系统认证到域,同时也会创建/etc/krb5.conf
文件。清理文件后,移除我们域的注释,文件看起来类似于以下截图:
我们可以看到/etc/krb5.conf
文件有四个部分:
-
logging
-
libdefaults
-
realms
-
domain_realm
由于演示实验室非常小,只有一个域控制器,因此无需进行更改。如果你有更大的设置,可能需要在 realm 中添加更多的详细信息。你可以指定持有正确角色的本地域控制器;否则,只需让 DNS 服务记录按以下方式解析这些记录:
[realms]
EXAMPLE.COM {
kdc = ad1.example.com
admin_server = ad1.example.com
}
配置 SSSD
sssd
的配置可以在/etc/sssd/sssd.conf
文件中找到。我们已经看到这默认情况下对我们有效,但也有自定义的空间,如下截图所示:
这里的一个简单修改是更改 AD 用户的主目录位置。默认情况下,这是/home/example.com/username
。如果你已将 Unix 扩展添加到 Active Directory 中,则我们将设置ldap_id_mapping
为false
,UID 和 GID 将在 Active Directory 中设置。
总结
在本章中,我们研究了如何使用 Active Directory 作为身份存储,利用 Linux 上的用户和组。设置的简便性使其成为全球企业中非常实用且迫切需要的解决方案。
在设置时间和 DNS 之前需要做一些基础工作。一旦完成此设置,使用 realm 命令配置sssd
以将 Active Directory 作为身份源就非常简单。
在 AD 域中的 RHEL 系统,我们可以通过adcli
在一定程度上管理此域,并通过控制台或 SSH 为用户提供访问 Linux 命令行的权限。
接下来,我们将继续探讨文件共享,但这次我们将使用 Apache HTTPD web 服务器。
第九章:部署 Apache HTTPD 服务器
超文本传输协议(HTTP)服务器通常被称为 Web 服务器。顾名思义,这是一个为客户端(通常是互联网中的 Web 浏览器)提供内容的网络服务。这通常意味着提供网页,但也可以提供其他文档,如图像、声音、视频,甚至是 RHEL 的 ISO 文件。
在 RHEL 7 上打包的 Web 服务器是 Apache httpd
服务。这是互联网上最常见的 Web 服务器,由 Apache 软件基金会开发。RHEL 已将 httpd
更新到 2.4 版本,取代了之前版本 RHEL 中使用的 2.2 版本。
本章将涵盖以下主题:
-
配置
httpd
服务 -
控制
httpd
服务 -
添加服务器模块
-
使用虚拟主机
配置 httpd
服务
Apache httpd
Web 服务器可以将页面提供给互联网上的客户端或我们内部局域网中的客户端,因此不要觉得部署 Web 服务器时一定需要加强安全性。当然,如果网站面向互联网,可能需要额外的安全性和隔离服务。然而,我们正在实验室环境中工作,将更多关注 Web 服务器的配置。
安装 Apache 2.4
默认情况下可能没有安装所需的包,因此我们至少需要添加 httpd
包。此外,你可能还想添加文档。考虑仅在开发服务器上添加文档;我不建议在生产服务器上添加文档。我们将按以下方式将两个包添加到服务器:
$ sudo yum install httpd httpd-manual
即使在这个阶段,添加这么少的工作量,我们也可以通过以下命令启动服务并使用浏览器访问 Web 服务器:
$ sudo systemctl start httpd
$ sudo systemct enable httpd
如果我们有图形环境,可以从本地系统使用 Firefox 浏览器访问本地主机。我们将看到一个类似于以下截图的欢迎页面:
虽然我希望认为我的工作已经完成;但不知为何,我感觉你可能还需要一点额外的指导。
配置
在 RHEL 7 上,httpd
服务的配置基础是 /etc/httpd
目录。通过 tree
命令,我们可以有效地展示配置层次或服务。这里需要的唯一配置是 httpd.conf
,但是 Red Hat 采用了更加模块化的方法,现在包括许多子配置文件到主文件中。以下是 tree
输出的截图,显示了默认安装后所有文件的位置:
提示
如果前述的 tree
没有安装,可以通过 sudo yum install tree
安装。
我们已经从/etc/httpd
目录运行了tree
命令。我们可以看到/etc/httpd/conf/httpd.conf
文件,这是主配置文件。它包括来自/etc/httpd/conf.d
和/etc/httpd/conf.modules.d
的其他文件。此外,还有三个符号链接的目录:
-
logs
-
modules
-
run
我们在浏览网站时看到的页面内容来自于/etc/httpd/conf.d/welcome.conf
中的配置。当没有实际网站内容时,会生成默认的欢迎页面。
随着userdir.conf
和autoindex.conf
文件的引入,独立模块配置与 RHEL 6 中的httpd
配置有很大不同,后者将这些文件都包含在主httpd.conf
文件中。
我们已经提到过,Web 服务器的配置根目录是/etc/httpd
目录。相关配置文件位于/etc/httpd/conf/httpd.conf
中。该文件中的一些关键指令如下:
-
ServerRoot
:/etc/httpd
-
DocumentRoot
:/var/www/html
-
DirectoryIndex
:index.html
ServerRoot
指令,正如我们所看到的,它是我们可以找到 Web 服务器配置、日志和模块的位置。DocumentRoot
指令表示可以找到 Web 内容的位置,而DirectoryIndex
HTML 页面是默认的查找页面。通过使用echo
,我们可以简单地创建我们自己的内容,如下所示。我们以 root 用户身份运行以下命令,创建一个非常基础的欢迎页面:
# echo '<h1>Welcome to our site</h1>' > /var/www/html/index.html
使用单引号允许将标签作为字面值传递。
我们可以直接查看页面;由于我们没有更改任何配置,因此无需重新加载服务。此时页面应类似于以下截图所示:
我们可以通过主机名或 IP 地址在本地或远程访问此站点。我们可以使用http://192.168.0.69
(或分配给主机接口的任何 IP 地址)访问相同的页面。要远程访问页面,防火墙需要在其firewalld
规则中包括http
和https
:
在以下代码中,我们打开了80
端口的 HTTP 端口和443
端口的 HTTPS 端口。这些是用于访问 Web 服务器的协议和默认端口:
$ sudo firewall-cmd --add-service=http --permanent
$ sudo firewall-cmd --add-service=https --permanent
$ sudo firewall-cmd --reload
作为 Web 服务器的管理员,我们不一定是网站内容的开发者,但我们可以证明 Web 服务器在配置后正常工作。
配置 DocumentRoot
目录
DocumentRoot
目录应该对httpd
服务可读。服务使用的帐户列在httpd.conf
中。与相应指令一起使用的默认帐户如下所示:
User apache
Group apache
在理想情况下,/var/www/html
目录的权限应为八进制表示的2750
或符号表示的rwx r_s _
。为目录设置组特殊位确保该目录中新创建的所有内容将由目录的组所有者拥有。通过这种方式,我们无需授予其他用户任何权限;只要目录归apache
组所有,文件就会对该组可访问。
首先,我们将为这个目录设置组所有权。由于我们已经在该目录之后创建了index
页面,因此我们将使用-R
选项,如以下命令所示:
$ sudo chgrp -R apache /var/www/html
现在,我们将为该目录设置组特殊位;这确保了在该结构中创建的所有新文件和目录都将归apache
组所有:
$ sudo chmod g+s /var/www/html/
最后,我们将移除授予其他用户的权限,从而帮助保护内容,具体如下:
$ sudo chmod -R o= /var/www/html/
虽然这并非绝对必要,但在一开始配置这个选项可以为以后可能需要更高安全性的情况节省工作。在httpd.conf
中,我们还有一个目录块,用来配置DocumentRoot
的访问和选项。以下截图显示了与DocumentRoot
目录相关的目录块:
Directory
块类似于基于 XML 的数据。它使用一个开始标签并在其中设置目标目录。该块以</Directory>
标签结束:
Options Indexes FollowSymLinks
Indexes
选项允许创建索引页面。如果客户端访问的 URL 中没有包含页面名称,并且没有index.html
,该选项会列出目录的内容。这对下载目录可能非常有用,避免了你需要创建一个页面来链接所有可用的下载内容;然而,在DocumentRoot
中,我们可能不希望启用这个设置,因为它可能会带来安全风险。
FollowSymLinks
选项可能不言自明,它允许你跟踪符号链接的路径。符号链接是指向文件系统中其他文件和目录的指针。
AllowOverride
指令指定可以通过用户控制的.htaccess
文件使用的设置。对于虚拟主机来说,管理员可能无法访问 Web 服务器的配置文件,因为他们只是租用服务器空间。他们可以通过将.htaccess
文件上传到与Directory
块相关的目录根目录来实现配置。作为主服务器管理员,你可以控制可以读取的设置;在这里,我们不允许即使.htaccess
文件存在也读取任何设置:
AllowOverride None
这里的最终设置是 Apache 2.4 中的新功能,它取代了 Apache 2.2 及更早版本中的Allow from / Deny from
指令:
Require all granted
默认设置相当于 Apache 2.2 中的Allow from all
设置。
如果需要,你可以使用类似于以下命令的配置来调整主机访问:
Require 192.168.0.0/24
Require 127.0.0.1
前面的访问控制只允许 localhost 和 192.168.0
网络访问相关的 Directory
块。
我们将通过编辑 /etc/httpd/conf/httpd.conf
文件来更改配置设置。一旦我们找到 /var/www/html
的正确 Directory
块,就可以移除 Index
选项并将其保留如下:
Options FollowSymLinks
这些更改应该被保存,但我们需要查看在尝试重启服务器之前,如何检查我们的设置。
控制 Apache 网络服务
当我们准备好测试所做的更改时,可以使用以下命令进行预飞行检查:
# apachectl configtest
你可能会收到一个错误信息,报告无法解析主机名。这是一个警告,目前没有问题。警告信息如下截图所示:
末尾的 Syntax Ok
消息是我们希望看到的;有了这个信息,我们就知道可以重启 web 服务器了。向服务发出 reload
命令将强制进行优雅重启;在启动重启之前,等待活跃连接完成:
$ sudo systemctl reload httpd
更改的影响是有限的,因为我们已经允许了本地网络和本地主机的访问。移除 indexes
选项没有效果,前提是 index.html
页面存在。如果我们移除 index.html
页面,将会收到访问被禁止的消息,因为系统将无法生成该页面。这一点很重要,因为它阻止了黑客能够获取我们网站服务器的目录列表。
设置服务器名称
我们还需要整理一下关于服务器名称的警告。这个问题是通过在 httpd.conf
文件中使用 ServerName
指令来控制的。在文件的顶部添加 ServerName
指令将解决该问题:
ServerName web.theurbanpenguin.com
要完全重启服务,我们将使用以下命令:
$ sudo systemctl restart httpd
设置自定义错误页面
如果我们尝试访问服务器上不存在的页面,浏览器将显示一个标准的 页面未找到
信息。我们可以通过添加自己的自定义页面来让这个过程更加可控和用户友好。
在 httpd.conf
的全局部分,我们可以添加以下指令来处理 404 页面未找到
错误。这个全局部分可以影响所有的目录块,但如果需要,我们也可以仅对单个目录块添加该指令。为 404 错误添加代码只会影响该特定错误,但我们也可以根据需要添加其他代码:
ErrorDocument 404 /404.html
我们可以使用以下命令重新加载服务器:
$ sudo systemctl reload httpd
现在,当访问错误的页面时,观看者将会看到我们创建的自定义错误页面:404.html
。此页面应创建在DocumentRoot
中,因为我们使用了/404.html
语法。如果我们有许多自定义页面,我们很可能会在DocumentRoot
中创建一个error
目录,然后将页面引用为/error/404.html
。
加载模块
Red Hat 已经摆脱了在标准的httpd.conf
文件中加载模块的方式。在之前的 Red Hat 版本(版本 6)中,配置文件可能会被许多LoadModule
指令所填满。
这些模块现在通过配置文件在/etc/httpd/conf.modules.d/
中加载。这样,主配置文件变得不那么混乱,可以根据需要轻松添加额外的配置文件。
要查看从命令提示符中当前加载的模块,请使用以下命令:
$ sudo httpd -M
我们可以看到我们加载了许多模块。我们可以将输出导入wc
命令来计算行数。使用 RHEL 7.1 演示系统,输出为82
:
$ sudo httpd -M | wc -l
通过原始输出,我们应该能够看到userdir_module
已加载。如果我们不需要在 Web 服务器上支持用户主目录,我们就不需要这个模块。要加载此模块,将引用此 Apache 模块的LoadModule
指令设置在/etc/httpd/conf.modules.d/00-base.conf
文件中。为确保在将来重新启动 Web 服务器时不加载它,请注释掉如下行:
LoadModule userdir_module modules/mod_userdir.so
现在注释掉该行后,您需要重新启动 Web 服务器,但在检查加载的模块时,应能够验证userdir_module
未加载。
虚拟服务器
Apache 有能力从同一服务器实例支持多个站点。这提供了很大的灵活性,同时也便于管理。这种灵活性称为虚拟主机。在 Apache 中有三种基本的虚拟主机运行方式:
基于名称 | 这为每个站点使用不同的名称和一个公共 IP 地址,可能是最流行的虚拟主机形式之一 |
---|---|
基于 IP | 这使用不同的 IP 地址为每个站点服务 |
基于端口 | 这使用各自的端口号为每个站点服务 |
我们将查看实施每种方法所需的所有三种方法和httpd.conf
中的配置。
基于名称
基于名称的虚拟主机是通过引入 HTTP 协议版本 1.1(也称为 HTTP/1.1)变得可能的。当浏览器只能支持 HTTP/1.0 时,协议尝试加载网页,经历以下步骤:
-
将
theurbanpinguin.org
主机名解析为一个 IP 地址。 -
通过 TCP 协议和端口
80
连接到解析后的 IP 地址。 -
请求
/index.html
页面。因此,在任何给定的 IP 地址上,只能托管一个网站(由其域名定义)。如果将另一个域名—例如
theboldeagle.net
—指向同一个 IP 地址,那么用户访问 URL 时将看到与访问另一个 URL 完全相同的内容,因为 Web 服务器无法区分这两个请求。当 HTTP/1.1 协议和支持 HTTP/1.1 的浏览器将域名和文档路径发送给 Web 服务器时,下一步(第 3 步)如下所示:
-
从
theurbanpinguin.org
服务器请求/index.html
页面。
现在可以在同一个 IP 地址上托管两个或更多不同的网站,因为 Web 服务器可以区分不同的域名,因为域名现在是 HTTP 请求的一部分。
所有现代 Web 浏览器都支持 HTTP/1.1。基于名称的虚拟主机有两个方面,我们现在来看看。
域名解析
网站的所有名称需要通过 DNS 或本地主机文件映射到同一个 IP 地址。如果系统中使用了其中任何一个名称,返回的 IP 地址将始终相同。请求被重定向到文件系统中的正确位置,由 httpd
服务和传入的 http
头处理。
Apache 配置
基于名称的虚拟主机的关键在于 httpd.conf
文件中的配置块指令:
<VirtualHost virtual host IP>
.
.
.
</VirtualHost>
几乎所有 Apache 条目都可以在这里使用,包括我们之前看到的 ErrorDocument
指令。我们在下面的示例中包含了最典型的条目:
<VirtualHost *.80>
ServerName www.packtpub.com
ServerAdmin andrew@example.com
DocumentRoot "/var/www/packt/html"
ErrorLog "/var/www/packt/logs/packt_error"
TransferLog "/var/www/packt/logs/packt_access"
</VirtualHost>
使用前面的示例,如果我们在访问服务器的 IP 地址时使用 www.packtpub.com/
的 URL,我们将被定向到 /var/www/packt/html
中的网页。
为了让 Apache 找到正确的虚拟主机条目,我们必须告诉它这些虚拟主机对应的 IP 地址。不过,仅仅使用之前的配置块定义虚拟主机是不够的,因为 Apache 默认不允许基于名称的虚拟主机。为了让基于名称的虚拟主机工作,我们需要使用 NameVirtualHost
指令,并将其包含在 httpd.conf
文件中。该指令必须位于主服务器部分:
NameVirtualHost *:80
基于 IP
使用的 IP 地址必须绑定到主 RHEL 服务器上。在 Apache 的 httpd.conf
中,我们再次使用 VirtualHost
配置块,但不需要设置 NameVirtualHost
指令。我们将不再使用 *:80
作为 VirtualHost
配置块的 IP 和端口选择符,而是使用 Apache 服务器监听的真实 IP 地址,即分配给 Apache 服务器所在机器某个接口的 IP 地址。以下命令显示了可以添加到主 httpd.conf
文件中的可能配置:
<VirtualHost 192.168.0.221:80>
ServerName www.example.com
ServerAdmin andrew@example.com
DocumentRoot "/var/www/example/html"
</VirtualHost>
默认情况下,Apache 会监听机器上所有接口的传入连接,因此只需要在<VirtualHost>
开头指令中指定一个接口的 IP 地址就足够了。然而,为了确保 Apache 监听到正确的接口,应该在httpd.conf
文件的主配置部分中包含以下命令:
Listen 192.168.0.221:80
使用前面的示例,当我们通过 IP 地址192.168.0.221
访问主机时,会被重定向到/var/www/example/html
中的网页。
基于端口
端口基础的虚拟主机仅涉及一个方面:使用主配置文件httpd.conf
进行的 Apache 配置。以下是一个示例:
<VirtualHost 192.168.0.220:7070>
ServerName www.example.com
ServerAdmin andrew@example.com
DocumentRoot "/var/www/example/html"
</VirtualHost>
使用前面的示例,当我们通过 IP 地址为192.168.0.220
的 Apache 主机访问端口7070
时,会被重定向到/var/www/example/html
中的网页。
在所有VirtualHost
块中,除了我们迄今为止展示的代码之外,还可以预期会有一个Directory
块。通过这种方式,可以为每个虚拟主机正确设置选项和访问控制列表。
自动化虚拟主机
如果我们为虚拟主机创建一个模板文件,就可以通过脚本轻松地添加新的虚拟主机。首先,我们需要一个类似以下命令的模板文件:
<VirtualHost *:80>
ServerAdmin webmaster@dummy-host.example.com
ServerName dummy-host.example.com
DocumentRoot /var/www/dummy-host.example.com
ErrorLog /var/log/httpd/dummy-host.example.com-error_log
CustomLog /var/log/httpd/dummy-host.example.com-access_log
UseCanonicalName Off
ServerSignature On
<Directory "/var/www/vhosts/dummy-host.example.com">
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
</VirtualHost>
如果该文件保存为/etc/httpd/conf.d/template
,由于文件名未以.conf
结尾,它将不会被用作配置文件。我们可以使用它作为模板,并通过类似以下命令的脚本来使用它:
#!/bin/bash
CONFDIR=/etc/httpd/conf.d
WEBDIR=/var/www/
mkdir -p $WEBDIR/$1
sed s/"dummy-host.example.com"/$1/g \
$CONFDIR/template > $CONFDIR/$1.conf
echo "This is a website in construction for $1" \
> $WEBDIR/$1/index.html
systemctl reload httpd
如果脚本名为/root/vhost.sh
,我们可以通过以下方式运行它:
# /root/vhost.sh www.example.com
上面的脚本将创建一个新的配置,并将dummy-host.example.com
替换为www.example.com
。
总结
本章介绍了在 RHEL 7.1 上运行的 Apache HTTPD 服务。我们查看了ServerRoot
为/etc/httpd
,DocumentRoot
为/var/www/html
。在掌握基础知识后,你学会了如何配置一个带有自定义错误页面和虚拟主机的服务器。
在下一章,我们将详细介绍 SELinux,并试图让你明白,使用 SELinux 时,不会对服务交付产生负面影响。实际上,“负面”这个词完全不准确。SELinux 将为你提供一个安全而稳健的平台,让你可以无惧安全威胁地部署面向公众的服务,并为现有的、较弱的DAC(Discretionary Access Controls)添加强制访问控制(MAC)。
第十章:使用 SELinux 保护系统
很多时候,你会发现一些教程或博客建议你禁用 SELinux。这是为了让 vservice 的某个特性能够工作。在许多情况下,人们不得不按照博客或教程的指示操作,因为关于 SELinux 了解甚少。本章的目的是为你提供一个解决方案,帮助你更好地理解 SELinux 的工作原理。通过本章的内容,你将更了解 SELinux 提供的保护,以便下次遇到博客建议你将车钥匙留在停车场的点火器上时,你能做出更明智的决策。
在本章中,我们将涵盖以下主题:
-
什么是 SELinux
-
了解 SELinux
-
使用目标策略类型
-
SELinux 中的策略
-
SELinux 工具
-
SELinux 故障排除
什么是 SELinux
SELinux 是一个MAC(强制访问控制)系统,和我们熟悉的现有DAC(自主访问控制)列表共同工作,比如文件权限列表。
提示
SELinux 只能限制权限;它不能添加权限。如果 DAC 不允许访问,SELinux 也无法允许。
为了与标记的对象进行交互,访问是基于这些标签进行授权的,并通过策略进行控制。所有对象——如用户、进程和文件——都有标签。你所拥有的标签或(更常见的)你运行的进程,必须与资源所提供的标签相匹配。简单来说,想象一下厕所;拥有 MEN 标签的人可以进入标记为 MEN 的厕所。在 Linux 术语中,Apache web 服务器进程被标记为httpd_t
,并可以访问具有httpd_sys_content_t
标签的文件。通过这种方式,系统能够防止恶意或被攻破(被妥协)的 web 服务器,因为它能够访问的文件范围受限于 SELinux。
SELinux 由 Red Hat、NSA 和 Secure Computing 维护,因此它有着丰富的背景。它由四个主要组件组成,我们将在本章中详细探讨:
-
模式
-
标签
-
策略类型
-
策略包
为了帮助你使用 SELinux,我们将安装一些附加的包。这些 RPM 包在以下命令行中列出。为了方便布局,我们添加了换行符:
$ sudo yum install policycoreutils-python policycoreutils-gui \
setools-console setools-gui setroubleshoot \
setroubleshoot-server
了解 SELinux
让我们开始揭开 SELinux 的面纱,深入了解这些控制机制如何工作,从 SELinux 的模式开始。
模式
首先,我们将讨论三种可以与 SELinux 一起运行的模式。这些模式将在下图中为你展示:
禁用模式
当 SELinux 被禁用时,SELinux 不会被使用,且对象不会被标记。在禁用模式下,我们完全依赖原始的 DAC。如果稍后需要启用 SELinux,启动过程会变得更长,因为所有对象需要重新标记。像这样完全禁用 SELinux 可能不是一个好主意,但如果需要,可以通过更改/etc/selinux/config
文件中的以下行来设置:
SELINUX=disabled
这样做不是一个好主意的原因之一是,必须重启才能生效。正如前面提到的,如果稍后启用 SELinux,则需要重新标记文件。我们可以通过运行以下命令强制重新标记所有文件系统对象:
# fixfiles relabel
或者,我们可以创建/.autorelabel
文件,如以下命令所示:
# touch /.autorelabel
宽松模式
如果你遇到服务问题并希望检查 SELinux 是否是可能的原因,你可以选择将 SELinux 设置为宽松模式。通过这种方式,SELinux 仍然启用,且对象保持其标签;然而,事件不会被阻止,而是被记录到/var/log/audit/audit.log
文件中。
要进入宽松模式,我们可以在系统运行时执行此操作,而无需重启系统。以下行演示了如何实现这一点:
# setenforce Permissive
如果以这种方式进行更改,则在重新启动时,宽松模式将从/etc/selinux/config
中应用。要将模式永久设置为宽松模式,我们应在以下行中设置宽松模式:
SELINUX=permissive
虽然我确实认为将模式设置为宽松模式作为快速和简单的测试是可以接受的,但你对 SELinux 了解得越多,就越不可能从强制模式下转移,强制模式能保证你的保护。在本章中,你将学习如何纠正问题,甚至为某个进程添加宽松模式,而不是将整个系统设置为宽松模式。
也可以通过启动加载程序设置宽松模式或强制模式,在内核行末尾添加以下命令(其中0
表示关闭或宽松模式,1
表示开启或强制模式):
enforcing=0
enforcing=1
强制模式
强制模式与宽松模式非常相似,你可以在命令行中通过setenforce
命令在宽松和强制模式之间切换。顾名思义,在此模式下,SELinux 被强制执行,并且也会报告到日志文件中。
要查询当前的 SELinux 模式,可以执行getenforce
命令。如果你已安装其他工具,还可以运行sestatus
命令,它是policycoreutils
包的一部分。此命令显示当前模式及配置文件中的模式;sestatus
的输出如下图所示:
标签
如前所述,当 SELinux 处于宽松或强制模式时,所有对象(如文件、用户和进程)都会有标签。在访问资源时,这些标签会被比较,以查看是否匹配兼容。
每个标签由四个冒号分隔的值组成:
-
SELinux 用户
-
SELinux 角色
-
SELinux 类型
-
SELinux 等级
通常情况下,级别仅在非常安全的政府环境中使用,其中用户的保密级别必须与文件或资源的保密级别相匹配。这里的想法是总统将能够阅读任何东西,但只能写入与其安全级别匹配的文件。这甚至阻止他写入具有较低安全级别的文件。当然,这些可以被较低授权的人员阅读,可能构成安全漏洞。
使用ls
命令,我们可以使用–Z
选项列出文件的标签。以下命令是列出/etc/hosts
文件中 SELinux 标签的示例:
$ ls –Z /etc/hosts
输出应该类似于以下截图:
阅读标签后,我们可以确定从前述截图中从左到右读取以下数值:
SELinux 用户 | system_u |
---|---|
SELinux 角色 | object_r |
SELinux 类型 | net_conf_t |
SELinux 等级 | s0 |
从 Linux 用户的角度来看,我们可以使用id –Z <username>
命令来读取标签。下面的截图显示了当前登录用户的情况,其中<username>
字段可以留空:
同样,我们可以使用ps
命令和–Z
选项检查进程的标签,如下命令所示:
$ ps -eZ | grep ssh
策略类型
默认的 SELinux 策略类型是有针对性的,但列出了三种策略类型如下:
-
最小
-
有针对性的
-
MLS
所有这些都包含在与selinux-policy-minimum
、selinux-policy-targeted
和selinux-policy-mls
名称匹配的软件包中。
最小
正如名称所示,这是为 SELinux 设计的最小配置。听起来可能很奇怪,但这是针对只想针对一个服务(例如 Apache Web 服务器)的情况。从基础开始,可以轻松地将其他策略包含在您的Minimum
类型中。以下命令显示了如何使用semodule
添加 Apache 策略:
# semodule -i /usr/share/selinux/minimum/Apache.pp.bz2
要配置 SELinux 使用Minimum
策略,我们使用/etc/selinux/config
文件设置SELINUXTYPE
指令:
SELINUXTYPE=minimum
有针对性的
这是默认的策略类型;默认情况下包含许多策略。在演示系统上,除了基本策略外,安装了 395 个策略,我们可以使用semodule
列出所有模块:
# semodule -l
MLS
多级安全或 MLS 策略类型将允许您添加额外的安全级别。可以通过标签查询这些级别,以帮助您控制对资源的访问。这通常仅在高安全性部署中使用。在 MLS 外,标签的级别元素不被使用。要启用 MLS,需要在/etc/selinux/config
文件中配置以下指令:
SELINUXTYPE=mls
策略
一旦策略被安装,单个策略将安装在适当的策略类型目录中;对于默认的 targeted 策略,进入/etc/selinux/targeted/modules/active/modules/
。策略文件具有.pp
后缀。
使用 targeted 策略类型
默认的策略类型是 targeted。因此,大多数 SELinux 部署将使用这种策略类型。在 targeted 策略类型的情况下,用于强制执行的标签的主要属性是type
。因此,targeted 策略类型通常被称为 TE 或type
enforcement。下图强调了在 targeted 策略类型中标签的 type 属性的重要性:
使用seinfo
命令,这是setools-console
包的一部分,我们可以显示有关当前 SELinux 环境的特定信息。让我们看一下可以使用的类型。要列出所有类型,我们将使用以下命令:
# seinfo -t
哇,真是太多了。如果我们数一数,RHEL 7 上大约有 4500 个;而 RHEL 6 上有 3500 个。这两个数字只是简单地说明了 SELinux 产品的增长及其持续的采用,但 Linux 软件开发人员。
我们还可以看到如何在带有用户属性的标签中导入 type 属性:
# seinfo -u
在这里,数字并不特别令人印象深刻;只是 8 个。这些不是 Linux 用户,而是 SELinux 用户;Linux 用户可以映射到 SELinux 用户,以帮助控制对资源的访问。要显示任何映射,我们可以使用semanage
,如下所示:
# semanage login -l
在没有任何映射设置的情况下,我们将看到 root 被映射到unconfined_u
,因为这是默认值。这个设置意味着所有没有特定映射的其他用户帐户将被映射到unconfined_u
SELinux 用户,这意味着我们在 SELinux 上忽略了标签中的用户属性,因为它是未受限的。同样,让我们使用seinfo
查看 ROLE 属性:
# seinfo -r
输出应显示 14 个角色;再说,这并不是一个很大的数字。角色属性在 targeted 策略类型中并没有被广泛使用。
未受限的域
TYPE
属性通常在设置到进程时被称为DOMAIN
;记住,我们可以使用以下进程状态命令查看正在运行的进程的 SELinux 标签:
$ ps -eZ
许多在用户空间中启动的进程也将是未受限的,可能是TYPE
属性设置为unconfined_t
。如果在用户空间中启动的进程通常是未受限的,我们可以说,服务,特别是面向网络的服务,将以某种方式受到强制执行,这也是 SELinux 存在的一个重要原因:保护免受网络暴露带来的攻击。并非只有aunconfined_t
标签是未受限的,SELinux 也未限制其他标签。要显示所有未受限的类型或域,我们可以再次使用seinfo
,并以 root 身份运行,如下所示:
# seinfo -aunconfined_domain_type -x
-a
选项告诉seinfo
我们正在搜索一个属性;这个属性需要紧接在选项后面,且不应有额外的空格。-x
属性则扩展显示所有具有该属性的TYPE
,而不仅仅是列出该属性本身。输出结果应确认,主要是那些不会面向网络的域(如bootloader_t
)是不受限制的。
以下截图显示了我的系统输出的开始部分。总共显示了 86 个不受限制的域;考虑到我们一开始有 4500 种类型,这个数量还不错:
当策略生效时,默认的访问级别是拒绝的;这意味着必须在策略包中存在规则,才能允许用户、角色或类型访问资源。默认拒绝访问确保了安全性,防止某些场景未被考虑到;另一方面,这也意味着如果你的特定场景未被考虑,就需要添加访问权限。可能需要一定的管理操作来调整环境以满足你的需求;然而,一旦设置完成,你将拥有一个安全的系统,它将持续可靠运行,同时降低暴露于风险的可能性。
当然,尽管策略中的默认设置是拒绝访问资源,但这些策略中默认提供了成千上万的允许规则
。使用sesearch
命令,我们可以显示它们;将结果发送给wc
命令可以统计规则的数量。以下命令演示了这一过程:
# sesearch --allow #display all allow rules
# sesearch --allow | grep wc -l #count the output
在我的系统上,默认情况下创建了超过 100,000 条规则。如果我们想更详细地查看这些规则,可以搜索httpd_sys_content_t
字符串。许多规则带有这个标签,但如果我们只看其中一条,最简单的就是考虑带有命令 tail 的最后一条。在这里,我们可以看到,资源访问权限已授予httpd_sys_content_t
标签的资源,以便与ftpd_t
标签的进程交互。简单来说,FTP 服务器可以访问你的网站内容,如下命令所示:
# sesearch --allow | grep httpd_sys_content_t | tail -n 1
allow ftpd_t httpd_sys_content_t : dir { getattr search open } ;
现在,我们对默认的目标策略类型有了更多了解,接下来让我们看看如何使用一些工具并查看 SELinux 的实际工作。
SELinux 工具
让我们来看看 SELinux 工具。
chcon 和 restorecon
我们可以使用的两种主要工具来帮助管理 SELinux 是chcon
和restorecon
。chcon
命令帮助更改 SELinux 上下文或类型,通常是单个文件或有时是一些可以通过某种形式的通配符轻松引用的文件。restorecon
命令可以用来重置文件或目录及其内容为默认的 SELinux 上下文。这些目录的默认设置存储在/etc/selinux/targeted/contexts/files/file-context
文件中。
使用grep
,我们可以搜索httpd_sys_content_t
,在输出中,我们应该看到/var/www
下文件的默认标签。这是我们期望找到网络服务器内容的目录:
# grep httpd_sys_content_t \
/etc/selinux/targeted/contexts/files/file_contexts
上述命令的输出如下:
/var/www(/.*)? system_u:object_r:httpd_sys_content_t:s0
现在,我们可以尝试通过更改index.html
页面的 SELinux 上下文来破坏系统。我们可以使用chcon
命令如下所示:
# chcon -t user_home_t /var/www/html/index.html
现在,如果我们通过localhost
URL 访问该网站,应该会看到某种访问被拒绝的消息。这是因为我们已将文件的TYPE
设置为user_home_t
;在运行网络服务器的httpd_t
上下文中不允许访问该类型。以下截图展示了chcon
的使用和随后的拒绝消息:
当然,我们可以通过使用chcon
将类型重新设置为httpd_sys_content_t
手动修复此问题;然而,如果我们不确定正确的上下文,可以运行restorecon
命令,如以下命令行所示:
# restorecon /var/www/html/index.html
现在访问网页应该可以正常工作。从技术上讲,我们可以通过在重启时创建/.autorelabel
文件来重新标记整个文件系统,从而实现与restorecon
相同的效果;如你所料,这样做有些过头,并且需要一些时间。不过,这样做的效果是对整个文件系统运行restorecon
。
布尔值
还有一些简单的布尔值,我们可以根据需要开关,以帮助调优系统,使其与我们的环境匹配。在本书使用的 RHEL 7.1 系统中,有 294 个布尔值可以调整。我们可以使用简单的getsebool
命令来显示它们:
# getsebool -a
我们将进一步深入,并列出与httpd
进程相关的那些。我们可以在以下截图中看到这一点:
要更改布尔值,我们可以使用setsebool
命令,这可以是临时的或永久的修复。如果我们希望布尔值更改为永久性的,则需要使用-P
选项。这也需要一段时间,因为活跃的策略将被写入并重新编译。
如果我们回到之前的设置,其中index.html
页面已设置为与用户主目录相关联的上下文,我们可以使用setsebool
来修复。如果不适合更改上下文,例如,如果我们需要暂时将用户主目录托管在网络服务器上,直到下次启动,我们可以使用以下命令:
# setsebool httpd_read_user_content on
如果我们需要将此设置为永久性,我们将使用以下命令:
# setsebool -P httpd_read_user_content on
临时设置显示在以下截图中。它还显示了成功访问网页,尽管该网页仍然具有不正确的上下文:
使用这些布尔值可以在解决与 SELinux 相关的问题时发挥重要作用。
SELinux 故障排除
让我们来看看不同的 SELinux 故障排除方法。
日志文件
如果我们还不确定之前在遇到 web 服务器错误时问题的原因,那么我们的故障排查应始终从日志文件开始。对于 SELinux,日志文件是 /var/log/audit/audit.log
。从 SELinux 登录的记录会标记为 AVC(访问向量缓存)。我们可以使用 grep
来搜索日志文件,类似于以下命令:
# grep AVC /var/log/audit/audit.log
然而,更合适的做法是使用 ausearch
命令。如果刚刚发生了错误,我们可以使用 recent
时间起始代码来帮助减少返回的结果。这是一个用于显示过去 10 分钟内错误的快捷方式:
# ausearch -m avc -ts recent
除此之外,我们还可以提供实际的时间、日期或两者。以下示例中,我们将使用 16:00
作为开始时间进行搜索。如果没有提供日期,默认使用今天的日期,如下所示:
# ausearch -m avc -ts 16:00
通过查看下面截图中的命令输出,我们可以看到进程和资源的标签不兼容:
audit2allow 命令
现在,即使在检查了日志文件之后,你仍然可能不完全清楚问题的原因或可能的修复方法。为了帮助排查,你可以尝试使用 audit2allow
命令。如果使用 -w
选项,它会在输出中提供问题的解释以及可能的解决方案。我们仍然需要查看日志,但这次我们会将输出通过管道传递给 audit2allow
命令,如下所示:
# ausearch -m avc -ts 16:00 | audit2allow -w
当我们重置原始布尔值时,测试系统的输出如下:
我们可以看到,建议与我们之前展示的布尔值设置相符。如果问题比更改布尔值更复杂,我们可以使用 -M
选项创建一个新的策略包。然后使用 semodule
导入 .pp
文件,如下所示:
# ausearch -m avc -ts 16:00 | audit2allow -M web.local
# semodule -i web.local.pp
宽容域
我们可以看到,有一些强大的工具设计来帮助我们部署 SELinux,但如果一切失败,还有一个叫做 宽容域 的选项。
与其将 SELinux 模式设置为宽容模式,我们可以仅为单个域或进程上下文启用宽容状态。默认情况下,宽容域是启用的,这些被称为内置宽容域。我们添加的域则是自定义域。
虽然 web 服务器是一个主要的面向网络的攻击向量,也许如果我们无法使 SELinux 与 httpd
配合使用,但我们又不想冒险让整个系统禁用 SELinux。我们可以通过以下命令为 httpd_t
启用宽容行为:
# semanage permissive -a httpd_t
如果我们以后需要删除这个行为,我们可以使用以下命令来恢复:
# semanage permissive -d httpd_t
在这两种情况下,我们都将写入活动策略,这需要一点时间。
总结
在本章中,你学习了如何管理 SELinux。我真诚地希望你对涉及的机制有了更深入的理解。SELinux 的目标是保护系统,特别是在涉及面向网络的服务时。禁用 SELinux 或设置为 Permissive 模式,通常不是正确的做法。通过这一章的学习,你现在应该能够选择正确的解决方案。
在下一章,我们将探讨 RHEL 7 中包含的新防火墙机制,以及相对于过去使用的标准IPtables
机制所做的改进。同样,我们希望能够说服你认识到firewalld
的优势,并保持该服务启用。
第十一章. 使用 firewalld 的网络安全
在 RHEL7 中,netfilter
(基于内核的防火墙)的默认用户界面是firewalld
。管理员现在可以选择使用firewalld
或iptables
来管理防火墙。无论选择哪种方式,底层都可以实现基于内核的netfilter
防火墙。此新界面的前端命令是firewall-cmd
。它的主要优点是能够在防火墙运行时刷新netfilter
设置。使用iptables
接口无法做到这一点;此外,我们还可以使用区域管理功能。这使我们能够根据连接的网络配置不同的防火墙规则。
在本章中,我们将涵盖以下主题:
-
防火墙状态
-
路由
-
区域管理
-
源管理
-
使用服务的防火墙规则
-
使用端口的防火墙规则
-
伪装和网络地址转换
-
使用丰富的规则
-
实现直接规则
-
回退到 iptables
防火墙状态
防火墙服务可以为您的 RHEL 系统及服务提供来自本地网络或互联网其他主机的保护。尽管防火墙通常在网络的边界路由器上维护,但通过基于主机的防火墙(如 Linux 内核中的netfilter
防火墙)可以提供额外的保护。在 RHEL 7 上,netfilter
防火墙可以通过iptables
或firewalld
服务来实现,后者为默认选项。
可以使用systemctl
命令以正常方式查询firewalld
服务的状态。如果该服务正在运行,将提供详细的输出,其中包括firewalld
的PID(进程 ID)以及最近的日志消息。以下是一个命令提取及来自 RHEL7.1 的输出截图:
# systemctl status firewalld
如果您只需要快速检查并获取较少的输出,可以使用firewall-cmd
命令。这是用于管理firewalld
的主要管理工具。--state
选项将提供所需的所有信息,如下截图所示:
如果firewalld
没有激活,输出将显示为not running
(未运行)。
路由
虽然防火墙中不严格需要网络路由,但您可能需要在 RHEL7 系统上实现路由。通常,这与拥有多个网络接口卡的多网卡系统相关;但是,这并不是网络路由的要求,网络路由允许将数据包转发到正确的目标网络。网络路由在procfs
中的/proc/sys/net/ipv4/ip_forward
文件中启用。如果该文件的值为0
,则禁用路由;如果值为1
,则启用路由。可以使用echo
命令设置此值,如下所示:
# echo 1 > /proc/sys/net/ipv4/ip_forward
然而,这将在下一次重启之前保持开启,重启后路由将恢复为配置的设置。要使此设置永久生效,传统上我们使用/etc/sysctl.conf
文件。现在建议将您的自定义配置添加到/etc/sysctl.d/
。以下是一个示例:
# echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/ipforward.conf
这将创建一个文件并设置其指令。为了使此设置在下一次重启之前生效,我们可以使用sysctl
命令,如以下命令所示:
# sysctl -p /etc/sysctl.d/ipforward.conf
这在以下截图中也可以看到。这里,您可以看到我们在实施更改之前从procfs
读取运行配置。它还显示了对运行中的procfs
的更改:
区域管理
在firewalld
中,您会发现一个新功能,它更侧重于移动系统——例如笔记本电脑——那就是区域的引入。然而,这些区域同样可以在多宿主系统中使用,将不同的 NIC 与适当的区域关联。无论是在移动系统还是多宿主系统中使用区域,防火墙规则可以分配给区域,并且这些规则将与该区域中包含的 NIC 相关联。如果一个接口没有显式分配到某个区域,它将成为默认区域的一部分。要查询您系统上的默认区域,我们可以使用firewall-cmd
命令,如下所示:
# firewall-cmd --get-default-zone
如果您需要列出系统上所有配置的区域,可以使用以下命令:
# firewall-cmd --get-zones
以下截图展示了此命令及 RHEL 7.1 上的默认区域:
更有用的是,我们可以显示已分配接口的区域;如果没有进行分配,所有接口将属于公共区域。--get-active-zones
选项将帮助我们做到这一点,如以下命令所示:
# firewall-cmd --get-active-zones
如果我们需要更详细的输出,可以列出所有区域名称、关联的规则和接口。以下命令展示了如何实现这一点:
# firewall-cmd --list-all-zones
如果您需要使用区域,您可以选择默认区域并将接口分配给特定的区域。首先,按如下方式分配一个新的默认区域:
# firewall-cmd --set-default-zone=work
在这里,我们将默认区域重定向到工作区域。这样,所有未显式分配的 NIC 将参与工作区域。前面的命令应返回success
。请查看以下截图,了解它是如何工作的:
我们还可以按如下方式显式地将区域分配给接口:
# firewall-cmd --zone=public --change-interface=eno16777736
通过此命令所做的更改将是临时的,直到下一次重启;要使其永久生效,我们需要添加--permanent
选项:
# firewall-cmd --zone=public --change-interface=eno16777736 --permanent
使设置永久生效将使配置保留在位于/etc/firewalld/zones/
目录中的区域文件中。在我们的例子中,文件是/etc/firewalld/zones/public.xml
。在此详细实现永久更改后,我们可以使用cat
命令列出 XML 文件的内容。以下截图显示了这一点:
我们可以通过查询单个 NIC 来查看它所关联的区域,或者列出区域内的所有接口;以下命令展示了这一点:
# firewall-cmd --get-zone-of-interface=eno16777736
# firewall-cmd --zone=public --list-all
提示
您可以使用 Tab 补全来辅助firewall-cmd
的选项和参数。
如果提供的区域不足,或者名称可能不适合您的命名方案,可以创建自己的区域并添加接口和规则。添加区域后,您可以重新加载配置以立即使用,方法如下:
# firewall-cmd --permanent --new-zone=packt
# firewall-cmd --reload
--reload
选项可以重新加载配置,允许当前连接继续不中断;而--complete-reload
选项将在过程中停止所有连接。
源管理
使用分配给区域的接口时,可能遇到的问题是它不会区分网络地址。通常,这不是问题,因为每个网络接口卡(NIC)只绑定一个网络地址;但是,如果您有多个地址绑定到 NIC 上,可能希望实现firewalld
源功能。像接口一样,源也可以分配给区域。在以下命令中,我们将添加一个网络范围到trusted
区域,并将另一个范围(可能在同一个 NIC 上)添加到public
区域:
# firewall-cmd --permanent --zone=trusted --add-source=192.168.1.0/24
# firewall-cmd --permanent --zone=public --add-source=172.17.0.0/16
类似于接口,将源绑定到区域将激活该区域,并且可以通过--get-active-zones
选项列出该区域。
使用服务的防火墙规则
当我们想到防火墙时,通常会想到允许或拒绝端口访问。使用服务的 XML 文件可以简化端口管理,每个服务可能列出多个端口。另一个需要注意的点是,firewalld
守护进程的默认策略是拒绝访问,因此任何所需的访问必须明确授予与服务关联的端口。要列出在默认区域上已允许的服务,我们可以简单地使用--list-services
选项,如下面的示例所示:
# firewall-cmd --list-services
类似地,我们可以通过包含--zone=
选项来访问特定区域中允许的服务。这可以在以下示例中看到:
# firewall-cmd --zone=home --list-services
该命令的输出如下面的截图所示。它列出了与home
区域相关的服务:
当您开始启用服务时,可以轻松地通过区域允许预定义的服务。预定义服务以 XML 文件形式列出,存储在/usr/lib/firewalld/services
目录中。它们在以下截图中列出,供您查看:
提示
RHEL 7 代表着更成熟的 Linux 发行版,因此它认识到将/usr
目录与root
文件系统分开的需求已不再推荐,/lib
、/bin
和/sbin
目录都被软连接到/usr/
后的相应目录。因此,/lib
现在与/usr/lib
相同。以下屏幕截图展示了这一点:
在定义您自己的服务时,您可以在/etc/firewalld/services
目录下创建 XML 文件。squid 代理服务器没有自己的服务文件,如果我们选择将其作为服务而不仅仅是开放所需端口,文件会类似于/etc/firewalld/services/squid.xml
,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>Squid</short>
<description>Squid Web Proxy</description>
<port protocol="tcp" port="3128"/>
</service>
假设我们在Enforcing
模式下使用 SELinux,我们需要使用以下命令为新文件设置正确的上下文:
# cd /etc/firewalld/services
# restorecon squid.xml
该文件的权限应为640
,并可以使用以下命令进行设置:
# chmod 640 /etc/firewalld/services/squid.xml
ls -lZ
的输出应类似于以下屏幕截图,显示正确的 SELinux 上下文和权限:
现在定义了新服务或使用现有服务后,我们可以将它们添加到某个区域。如果我们使用默认区域,可以通过以下命令轻松完成。请注意,我们在开始时重新加载配置,以便识别新的 squid 服务,如下所示:
# firewall-cmd --reload
# firewall-cmd --permanent --add-service=squid
# firewall-cmd --reload
类似地,要更新指定的非默认区域,我们将使用以下命令:
# firewall-cmd --permanent --add-service=squid --zone=work
# firewall-cmd --reload
如果我们稍后需要将此服务从工作区域中移除,可以使用以下命令:
# firewall-cmd --permanent --remove-service=squid --zone=work
# firewall-cmd --reload
使用端口的防火墙规则
在前面的示例中,squid 服务只需要一个端口,我们可以轻松地添加一个端口规则来允许访问服务。尽管过程简单,但在某些组织中,偏好仍然是创建服务文件,在描述字段中记录对端口的需求。
如果我们需要添加端口,我们可以使用--add-port
和--remove-port
等选项。以下命令展示了如何在工作区域中添加 squid 的 TCP 端口3128
,而不需要定义服务文件:
# firewall-cmd --permanent --add-port=3128/tcp --zone=work
# firewall-cmd --reload
伪装和网络地址转换
如果您的firewalld
服务器是运行 RHEL 7 的网络路由器,您可能希望为内部私有网络中的主机提供互联网访问。如果是这种情况,我们可以启用伪装。这也叫做NAT(网络地址转换),即服务器的公共 IP 地址被内部客户端使用。为此,我们可以利用内置的内部和外部区域,并在外部区域上配置伪装。内部网卡应分配给内部区域,外部网卡应分配给外部区域。
要在外部区域建立伪装,我们可以使用以下命令:
# firewall-cmd --zone=external --add-masquerade
使用--remove-masquerade
选项可以删除伪装。我们还可以使用--query-masquerade
选项查询某个区域的伪装状态。在下面的截图中,我们可以看到伪装被启用,然后通过查询返回yes
结果:
使用丰富规则
firewalld
丰富语言使管理员能够轻松配置更复杂的防火墙规则,而无需了解iptables
语法。这可以包括日志记录和源地址检查。
要添加允许 NTP 连接的规则到默认区域,并且对连接进行每分钟最多 1 次的日志记录,可以使用以下命令:
# firewall-cmd --permanent \
--add-rich-rule='rule service name="ntp" audit limit value="1/m" accept'
# firewall-cmd --reload
同样,我们可以添加一个规则,仅允许来自一个子网的访问到 squid 服务:
# firewall-cmd --permanent \
--add-rich-rule='rule family="ipv4" \
source address="192.166.0.0/24" service name="squid" accept'
# firewall-cmd --reload
从以下截图中,我们可以看到添加的丰富规则:
注意
Fedora 项目维护了firewalld
丰富规则的文档,您可以通过fedoraproject.org/wiki/Features/FirewalldRichLanguage
访问它们,以获取更多详细示例。
实现直接规则
如果您之前有使用iptables
的经验,并且希望将iptables
的知识与firewalld
中的功能结合使用,直接规则将帮助您进行迁移。首先,如果我们想在 INPUT 链上实现一个规则,可以使用以下命令检查当前设置:
# firewall-cmd --direct --get-rules ipv4 filter INPUT
如果您没有添加任何规则,输出将为空。我们将添加一个新规则,并使用优先级0
。这意味着它将排在链的顶部;然而,当没有其他规则时,这个优先级并没有太大意义。我们确实需要验证规则是否按正确的顺序添加,以便在有其他规则的情况下能够处理:
# firewall-cmd --permanent --direct --add-rule ipv4 filter \
INPUT 0 -p tcp --dport 3128 -j ACCEPT
# firewall-cmd --reload
还原为 iptables
此外,如果您最熟悉iptables
服务,也没有任何限制阻止您继续使用它。
首先,我们可以使用以下命令安装iptables
:
# yum install iptables-service
我们可以屏蔽firewalld
服务,以更有效地禁用该服务,防止它在没有先取消屏蔽的情况下被启动:
# systemctl mask firewalld
我们可以使用以下命令启用iptables
:
# systemctl enable iptables
# systemctl enable ip6tables
# systemctl start iptables
# systemctl start ip6tables
永久规则像往常一样添加,通过/etc/sysconfig
目录和iptables
与ip6tables
文件。
总结
firewalld
项目由 Fedora 维护,是 Linux 内核上 netfilter
防火墙的新管理服务和接口。作为管理员,我们可以选择使用这个默认服务,或者切换回 iptables
;然而,firewalld
可以为我们提供重新加载配置而不丢失连接的能力,并提供从 iptables
迁移的机制。我们已经看到,当我们需要在单一网卡上共享地址范围时,如何使用区域来隔离网络接口和源地址。无论是网卡还是源地址,都不与区域绑定。然后,我们可以向区域添加规则来控制对资源的访问。这些规则是基于服务或端口的。如果需要更复杂的配置,我们可以选择使用富规则或直接规则。富规则是用 firewalld
的富语言编写的,而直接规则则使用 iptables
语法编写。