第十九章:高级安全功能

FreeBSD包括各种用于保护网络流量和用户的工具。其中一些工具对系统管理员来说是不可见的,但可以在后台工作以提高安全性,例如沙盒API capcium(4) 。数据包过滤允许您控制谁可以访问您的系统。您还可以使用黑名单来阻止不断攻击您主机的网络地址。此外,FreeBSD还有一大堆可选的安全功能,您可以在安装过程中或以后启用。在本章中,我们将研究这些工具和技术,了解监控系统的安全性,并讨论在遭受入侵时如何做出反应。

让我们从一个核心安全主题开始:无特权用户。

无特权用户

无特权用户(unprivileged user)是特定任务的特定用户。他只有执行这项有限任务所需的权利。许多程序以无特权用户身份运行,或使用无特权用户执行特定任务。

“只有履行职责所需的权利”听起来像每个用户帐户,不是吗?这是真的,但特权最低的人使用的帐户仍然拥有比许多程序所需的更多的权利。任何具有shell访问权限的人都有一个主目录。普通用户可以在其主目录中创建文件、运行文本编辑器或处理电子邮件。你的普通shell用户需要这些最低权限,但程序不需要。通过让程序,特别是网络守护进程,以非常受限的用户身份运行,您可以控制入侵者对程序或用户造成的损害程度。

FreeBSD包括几个无特权用户。看看 /etc/passwd ,你会看到auditbinduucpwww 等帐户。这些都是特定服务器守护进程使用的无特权帐户。看看他们有什么共同点。

无特权用户没有正常的主目录。许多人的主目录为 /nonexistent ,而其他人,如 sshd ,则有一个特殊的主目录,如 /var/empty 。拥有一个不能写入或读取文件的主目录会降低帐户的灵活性,但对于服务器守护进程来说已经足够了。这些用户确实拥有系统上的文件,但他们通常无法写入这些文件。

同样,任何人都不应该登录这些帐户。如果帐户 bind 是为DNS系统保留的,那么任何人都不应该以该用户的身份登录系统!这样的帐户必须有一个专门拒绝登录的用户shell,如 /usr/sbin/nologin 。所有这些如何增强系统安全性?让我们来看一个例子。

无论您使用什么web服务器,它通常都在无特权帐户 www 下运行。假设入侵者在您使用的web服务器程序版本中发现了一个安全漏洞,可以使web服务器执行任意代码。这是最严重的安全问题之一,入侵者可以让服务器程序在其能力范围内做任何事情。该计划的权力范围是什么?

入侵者可能希望系统上有一个命令提示符。毕竟,类Unix系统上的命令提示符是通往更多访问的大门。无特权用户有一个指定的shell,专门禁止登录。这真的让入侵者很恼火,需要他们更加努力地工作才能到达命令提示符。

不过,如果她真的很聪明,nologin shell不会阻止入侵者。让我们假设,通过巧妙的技巧,她让web服务器执行一个简单的shell,如 /bin/sh ,并向她提供提示符。她在里面,可以造成无法估量的伤害。或者她能吗?

她没有主目录,也没有创建主目录的权限。这意味着她想要存储的任何文件都必须放在全局可访问的目录中,如 /tmp/var/tmp ,以提高她的可见性。Apache配置文件由root或您的web服务器管理组拥有,www 用户不属于该组。入侵者可能有进入网络服务器的路径,但她无法重新配置。她无法更改网站文件,因为 www 用户并不拥有这些文件。 www 用户除了web服务器本身之外,无法访问系统上的任何内容。一个足够熟练的入侵者可以让网络服务器提供不同的页面或重定向到另一个网站,至少在重新启动之前是这样。渗透服务器上运行的应用程序或主机本身需要另一整套安全漏洞。

请注意,无特权用户并不能解决所有安全问题。我们受损的 www 用户可以查看web应用程序源文件。如果你的应用程序写得不好,或者数据库密码被硬编码到隐藏文件中,你仍然会遇到很多麻烦。尽管如此,如果你保持系统更新,所有软件包都保持最新,入侵者将很难渗透到FreeBSD本身。

nobody 账户

多年来,系统管理员将 nobody 帐户用作通用的无特权用户。他们会运行网络服务器、代理服务器和其他任何东西,就像 nobody 一样。这比以root身份运行这些程序要好,但不如为每个守护进程设置单独的用户好。如果入侵者成功侵入其中一个程序,他就可以访问所有程序。我们假设的网络服务器入侵者不仅会突然访问网络服务器,还会访问以同一用户身份运行的任何其他程序!如果您使用的是NFS,请记住NFS默认将远程根帐户映射到任何人。使用无特权用户的全部目的是尽量减少成功入侵可能造成的损害。

虽然您可能会使用 nobody 帐户进行测试,但切勿使用它部署生产服务。请自由使用单独的无特权帐户。

无特权用户示例

以下是对一般无特权用户有用的参数:

这些设置使您的无特权用户确实非常无特权。您可以使用 adduser(8) 轻松设置所有这些,为帐户提供无密码、正确的主目录和适当的shell。

许多ports和包都分配了非特权用户和组,这些用户和组列在 /usr/ports/UID/usr/ports/GID 中。不要害怕添加更多。使用1000以上的UID,以免与包和FreeBSD核心分配的UID冲突。

网络流量控制

系统管理员必须能够控制进出其系统的流量。在合法用户访问时,必须阻止不想要的访问者。FreeBSD提供了各种工具,允许您控制对系统的外部访问,包括TCP包装器(TCP wrappers)、数据包过滤(packet filtering)和黑名单(blacklisting)。

TCP包装器,或简称为包装器(wrappers),控制对网络守护进程的访问。虽然程序必须编写为支持TCP包装器,但大多数现代软件多年来一直支持包装器。包装器的配置相当简单,不需要太多的网络知识。然而,随着访问控制的进行,包装器相当有限。不过,包装器确实可以让你用连接和提供连接的守护进程做有趣的事情,这就是我们将讨论它的原因。

数据包过滤(Packet filtering )控制系统允许哪些流量通过它,拒绝哪些流量。大多数防火墙都是数据包过滤器,顶部有一个漂亮的GUI,但你可以使用FreeBSD数据包过滤和代理软件来构建一个坚固的防火墙。被拒绝的连接请求永远不会到达任何用户区程序;它在网络堆栈中被阻止。数据包过滤可以控制对任何程序、服务或网络端口的访问,但确实需要更多的网络知识。

当您希望程序能够决定停止监听远程主机时,黑名单(Blacklisting )非常有用。列入黑名单最常见的工具是fail2ban(https://www.fail2ban.org/),这是灵活的,但需要很多特殊的配置。FreeBSD包括黑名单,这是一个更容易配置的黑名单工具,需要与使用它的程序集成。

你应该使用哪一种?对于基本的TCP/IP访问控制,我建议始终使用数据包过滤器。只有在需要TCP包装器的特定功能时才使用它们。我讨论了仅将阻止和允许与TCP包装器的连接作为这些高级功能的先决条件。如果您希望服务在连接尝试失败一定次数后阻止客户端,请考虑将其列入黑名单。

使用包装器或数据包过滤,您必须决定是要默认接受还是默认拒绝流量控制策略。

默认允许 vs. 默认拒绝

任何安全策略中的一个基本决策都是在默认接受和默认拒绝之间。default accept 安全立场意味着您允许任何类型的连接,除非您特别禁止。default deny 立场意味着您只允许从互联网的指定部分和/或到指定服务的连接,并且您拒绝所有其他连接。除非您制定了具体的规则,否则将使用默认值。一旦你选择了默认的安全立场,你就可以根据需要以某种方式创建异常来提供或阻止服务。真正的选择是,你是向世界提供服务(默认允许)还是只向少数人提供服务(缺省拒绝)。

例如,公司政策可能规定只能从公司内部访问内联网web服务器。如果是这样,请采用默认的拒绝立场,并明确列出谁可以访问服务器。或者,如果你有一个公共网站,但出于任何原因想阻止互联网的某些部分访问它,请采取默认的接受立场。

我总是建议采取默认拒绝立场。然而,如果你没有做出选择,你就选择了默认接受。

选择默认值并不意味着必须无例外地实现默认值。我的公共网络服务器有一个默认的拒绝安全立场,但我特别允许全世界访问这些网站。机器拒绝连接到其他程序的尝试,除非它们来自少数指定的IP地址之一。这是一个完全可以接受的默认拒绝立场。

不同的安全工具以不同的方式实现这些立场。例如,对于TCP包装器,应用第一个匹配规则。如果你的最后一个规则拒绝了所有内容,那么你已经制定了一个策略,上面写着:“除非我之前专门创建了一个规则来允许这种流量,否则阻止它。”另一方面,使用PF数据包过滤器,最后一个匹配规则适用。如果你的第一条规则说“阻止所有流量”,那么你已经实施了一项政策,即“除非我后来专门创建了一条允许此流量的规则,否则阻止它。”

默认接受和默认拒绝都会惹恼系统管理员。如果你有一个默认接受策略,你会花时间不断地填补漏洞。如果您选择默认拒绝策略,您将花费时间为人们打开访问权限。你会为任何一种选择一再道歉。默认拒绝时,您会说“我刚刚为您激活了服务。对于给您带来的不便,我深表歉意。”默认接受时,您可能会说“……这就是为什么入侵者能够访问我们的内部会计数据库,以及为什么我们损失了数百万美元。”在后一种情况下,“我为给您带来不便表示歉意”真的不够。

TCP 包装器

记住第7章,网络连接是与各种监听连接请求的程序建立的。当使用TCP包装器支持构建程序时,程序会根据包装器配置检查传入请求。如果包装器配置说拒绝连接,程序会立即丢弃请求。尽管名为TCP包装器,但它同时适用于TCP和UDP连接。包装器是一种长期运行的Unix标准,已被纳入FreeBSD。单个程序可能会也可能不会使用包装器;虽然基本FreeBSD系统中的几乎所有东西都可以,但一些第三方软件却不能。

TCP包装器被实现为一个名为 libwrap 的共享库。如第17章所示,共享库是可以在程序之间共享的小块代码。任何与libwrap链接的程序都可以使用TCP包装器函数。

包装器最常用于保护 inetd(8) ,inetd是处理较小程序的网络请求的超级服务器。我们将在第20章讨论inetd。虽然我们的示例涵盖了 inetd(8) ,但您可以以完全相同的方式保护任何其他支持包装器的程序。虽然包装器有助于保护 inetd(8) ,但请确保 inetd(8) 不会提供任何不必要的服务,就像您对主系统所做的那样。

配置包装器

包装器按顺序根据 /etc/hosts.allow 中的规则检查每个传入的连接请求。应用第一个匹配规则,处理立即停止。这使得规则顺序非常重要。每条规则都在单独的一行中,由三个部分组成,用冒号分隔:守护进程名称、客户端列表和选项列表。以下是一个示例规则:

守护程序名称为 ftpd ;客户端列表是 all ,意味着所有主机;选项是 deny ,告诉包装器拒绝所有连接。除非之前的规则明确授予访问权限,否则任何人都无法连接到此主机上的FTP服务器。

在早期的例子中,我只提到两种选择:acceptdeny 。它们分别允许和拒绝(reject)连接。我们稍后将讨论其他选项。

守护进程名称

守护进程名称是程序在命令行上显示的名称。例如, inetd(8) 在收到传入的FTP请求时启动 ftpd(8) 程序。Apache web服务器启动一个名为 httpd 的程序,因此,如果您的Apache版本支持包装器,请将守护进程名称命名为 httpd 。(请注意,Apache不会用完inetd,但无论如何它都可以支持包装器。)一个特殊的守护进程名称—— ALL ,与支持包装器的所有守护进程相匹配。

如果您的系统有多个IP地址,您可以在守护进程名称中为守护进程侦听的每个IP地址指定不同的包装规则:

在这个例子中,我们有两个守护进程名称,ftpd@203.0.113.1 以及 ftpd@203.0.113.2 。每个都有单独的TCP包装规则。

客户端列表

客户端列表是由空格分隔的特定IP地址、网络地址块、主机名、域名和关键字的列表。主机名和IP地址很简单:只需列出它们。

将此规则放在 /etc/hosts.allow 的顶部,包装器允许我的netmanager机器和IP地址为203.0.113.5的任何主机连接到此主机上的任何服务。(请注意,我可以通过其他方式阻止此访问。)

在客户端列表中指定网络号,IP地址和网络掩码之间用斜线隔开,如第7章所述。例如,如果脚本小子从一堆以192.0.2开头的地址攻击你的服务器,你可以这样阻止它们:

您还可以在客户端列表中使用域名,方法是在域名前加一个点。这是通过反向DNS实现的,这意味着任何控制一组地址的DNS服务器的人都可以逃避这一限制。

如果你有一长串客户端,你甚至可以将它们列在一个文件中,并将文件的完整路径放在 /etc/hosts.allow 的客户端空间中。我一直在使用大量分散的主机的网络,例如ISP或公司网络环境,其中网络管理工作站分散在世界各地。每个工作站与其他工作站共享相同的包装规则,并出现在 hosts.allow 中的六行中。通过维护一个带有工作站列表的单个文件,我可以集中所有更改。

除了专门列出客户端地址和名称外,包装器还提供了几个特殊的客户端关键字,可以将客户端组添加到您的列表中。表19-1显示了关键字及其用法。

表19-1中列出的大多数客户端关键字都需要一个可用的DNS服务器。如果你使用这些关键字,你必须有一个非常可靠的DNS服务,你必须记住DNS和其他程序之间的重要联系。如果您的DNS服务器发生故障,使用包装器和这些关键字的守护进程将无法识别任何主机。这意味着所有内容都符合您的 UNKNOWN 规则,这可能会拒绝连接。此外,客户端上损坏的DNS可能会拒绝远程用户访问您的服务器,因为您的DNS服务器将无法从客户端的服务器获取正确的信息。最后,如果您广泛使用基于DNS的包装,入侵者只需使您的名称服务器过载或以其他方式中断您的名称服务,即可对您的网络发起非常有效的拒绝服务攻击。

表19-1:TCP包装器关键字

关键字用法
ALL这与所有可能的主机相匹配。
LOCAL这匹配主机名不包含点的每台机器。通常,这意味着本地域中的机器。根据此规则,世界另一端碰巧共享您域名的机器被视为“local”机器。
UNKNOWN这将匹配具有无法识别的主机名或用户名的计算机。一般来说,任何进行IP连接的主机都有一个已知的IP地址。然而,跟踪主机名需要DNS,跟踪用户名需要 identd(8)。使用此选项时要非常小心,因为暂时的DNS问题甚至会使本地主机名无法解析,而且大多数主机默认情况下不会运行 identd(8)。您不希望服务仅仅因为您的名称服务器配置错误而变得不可用,尤其是如果该机器是您的名称服务器!
KNOWN这将匹配具有可确定主机名和IP地址的任何主机。使用时要非常小心,因为DNS中断可能会中断服务。
PARANOID这将匹配名称与其IP地址不匹配的任何主机。您可能会收到一个来自IP地址为192.168.84.3的主机的连接,该主机声称名为 mail.michaelwlucas.com 。包装器会反过来检查 mail.michaelwlucas.com 的IP地址。如果包装器获得了不同的IP地址,则主机符合此规则。没有时间维护DNS的系统管理员最有可能拥有未打补丁、不安全的系统。

TCP包装器提供了额外的关键字,但它们不如这些有用或安全。例如,可以根据远程计算机上的用户名允许连接。但是,您不希望依赖远程计算机上的客户端用户名。例如,如果我设置包装器,只允许用户名为 mwlucas 的人连接到我的主系统,那么有人可以很容易地将该名称的帐户添加到他的FreeBSD系统中并直接进入。此外,这依赖于前面提到的很少使用的 identd(1) 协议。您可以在 hosts_access(5) 中找到其他一些类似有用的模糊关键字。

ALL 和 ALL EXCEPT 关键字

守护进程名称和客户端列表都可以使用 ALLALL EXCEPT 关键字。ALL 关键字完全匹配所有内容。例如,默认的 hosts.allow 以一个规则开头,该规则允许从所有位置到任何守护进程的所有连接:

这与所有程序和所有客户端匹配。您可以通过为客户端列表或守护进程列表指定特定名称来限制这一点。

在这个例子中,我们拒绝来自主机203.0.113.87的所有连接。

分类阻止对所有主机的访问并不是一个好主意,但请记住,TCP包装器按顺序遵循规则,并在到达第一个匹配规则时退出。ALL 关键字可以让你很容易地设置默认姿态。考虑以下规则集:

我们的工作站192.168.8.3和192.168.8.4(可能是系统管理员的工作站)可以访问他们想要的任何内容。世界上任何人都可以访问FTP服务器。最后,我们放弃所有其他连接。这是一个有用的默认拒绝立场。

使用 ALL EXCEPT 关键字压缩规则。ALL EXCEPT 允许您按排除方式列出主机;未列出的匹配项。在这里,我们使用 ALL EXCEPT 对所有内容都编写了相同的规则:

当然,此规则依赖于有一个默认的接受策略,允许以后进行FTP连接。

有些人觉得用 ALL 写的规则更清晰,而另一些人则更喜欢 ALL EXCEPT 。要记住的重要一点是,第一个匹配规则结束了检查,所以在使用 ALL 时要小心。

允许来自本地主机的任何连接是个好主意;您可能会发现许多程序在无法与本地计算机通信时会中断。在 hosts.allow 中尽早设置这样的规则:

选项

我们已经看到了两种选择:allowdenyallow 允许连接,deny 阻止连接。默认 hosts.allow 中的第一条规则适用于所有守护进程和客户端,它匹配并允许所有可能的连接。如果你想包装你的服务,这个规则不能是 hosts.allow 中的第一个,但在默认接受安全立场中,这是一个很好的最终规则。同样,在默认的拒绝安全立场中,ALL:ALL:deny 规则是一个很好的最终规则。然而,TCP包装器支持除简单的 allowdeny 之外的其他选项,为您提供了极大的灵活性。

长规则 如果你使用了很多选项,包装规则可能会变得很长。为了帮助保持规则的可读性, hosts.allow 文件可以使用反斜杠(\)后跟return作为行连续字符。

日志(logging)

一旦您决定接受或拒绝连接尝试,您还可以记录连接。假设您想允许但具体记录来自竞争对手的所有传入请求。同样,您可能想知道在使用 PARANOID 客户端关键字时,由于DNS问题,服务器拒绝了多少连接。日志记录很好。更多的日志记录更好。磁盘空间比你的时间便宜。

severity 选项向系统日志 syslogd(8) 发送消息。您可以配置syslogd,根据您选择的syslogd功能和级别将这些消息定向到任意文件(请参阅第21章)。

此示例允许所有SSH连接,但也使用local0工具记录它们。

扭曲(twisting)

twist 选项允许您在有人尝试连接到打包的TCP守护进程并将输出返回给远程用户时运行任意shell命令和脚本。twist 选项仅适用于TCP连接。(记住,UDP是无连接的;没有连接可以返回响应,所以你必须跳过非常复杂和烦人的步骤,才能使 twist 与UDP一起工作。此外,通过UDP传输的协议通常不期望这样的响应,通常也没有能力接收或解释它。在UDP中使用 twist 不值得麻烦。)twist 选项将shell命令作为参数,并充当拒绝加执行此规则。您必须了解基本的shell脚本才能使用 twisttwist 的复杂用法是可能的,但我们将坚持使用简单的用法。

twist 选项对于默认拒绝姿态中的最终规则非常有用。使用 twist 向尝试连接的人返回答案,如下所示:

如果你只想拒绝特定主机的特定服务,你可以使用 twist 使用更具体的守护进程和客户端列表:

这对垃圾邮件无效,但可能会让你感觉好些。然而,遇到粗鲁信息的合法客户可能会触发会议。

如果你感觉很友好,你可以告诉人们你为什么拒绝他们的联系。以下 twist 拒绝来自主机名与IP地址不匹配的用户的所有连接,并告诉他们原因:

使用 twist 可以保持网络连接打开,直到shell命令完成。如果你的命令需要很长时间才能完成,你可能会发现你打开的连接比你想象的要多。这可能会影响系统性能。脚本小子可以使用 twist 使您的系统过载,从而创建一个非常简单的DoS攻击。使 twist 简单快速完成。

Spawning

twist 类似,spawn 选项拒绝连接并运行指定的shell命令。与 twist 不同,spawn 不会将结果返回给客户端。当您希望FreeBSD系统对连接请求采取行动,但不希望客户端知道时,请使用 spawn 。Spawned命令在后台运行。以下示例允许连接,但会将客户端的IP地址记录到文件中:

等一下,%a 是从哪里来的?TCP包装器支持在 twistspawn 命令中使用多个变量,因此您可以轻松自定义响应。这个特定的变量 %a 代表客户端地址。在命令运行之前,它会扩展到shell命令中的客户端IP地址。表19-2列出了其他变量。

表19-2:twistspawn 脚本的变量

变量描述
%a客户端地址。
%A服务器IP地址。
%c所有可用的客户端信息。
%d连接到的守护进程的名称。
%h客户端主机名,或IP地址(如果主机名不可用)。
%H服务器主机名,或IP地址(如果主机名不可用)。
%n客户端主机名,如果找不到主机名,则为 UNKNOWN 。如果主机名和IP地址不匹配,则返回 PARANOID
%N服务器主机名,但如果找不到主机名,则返回 UNKNOWNPARANOID
%pDaemon的进程ID。
%s所有可用的服务器信息。
%u客户端的用户名。
%%一个%字符。

在shell脚本中使用这些变量所表示的信息的任何地方都可以使用这些变量。例如,要在任何人连接到包装程序时将所有可用的客户端信息记录到文件中,您可以使用以下方法:

空格和反斜杠是shell命令中的非法字符,可能会导致问题。虽然在正常情况下两者都不会出现在主机名中,但互联网几乎从定义上讲是不正常的。TCP包装器用下划线(_)替换任何可能混淆命令shell的字符。检查日志中的下划线;它们可能表明可能存在入侵企图,或者只是不知道自己在做什么的人。

包装材料

让我们以本节到目前为止给出的所有示例为例,构建一个完整的 /etc/hosts.allow 来保护一个假设的网络系统。我们必须首先清点该系统提供的网络资源、我们在网络上的IP地址以及我们希望允许连接的远程系统。

虽然这些要求相当复杂,但它们可以归结为一个非常简单的规则集:

您可以在 /etc/hosts.allowhosts_allow(5)hosts_access(5) 中找到更多注释掉的示例。

包过滤

要控制对不支持TCP包装器的网络程序的访问,或者当您的需求超出包装器提供的范围时,请使用FreeBSD的内核级数据包过滤工具之一。如果你需要一个数据包过滤器,最好用数据包过滤完全替换你的TCP包装器实现。在同一台机器上同时使用这两个工具只会让你感到困惑。

数据包过滤器将进入系统的每个网络数据包与规则列表进行比较。当规则与数据包匹配时,内核会根据该规则进行操作。规则可以告诉系统允许、删除或更改数据包。但是,您不能使用TCP包装器提供的漂亮选项;客户端甚至在到达应用程序之前,就在网络级别切断了连接,而不是向客户端吐出相对友好的拒绝消息。

虽然数据包过滤的想法很简单,但你的第一次实现将是一场噩梦——呃,我的意思是,一次“宝贵的学习经历”。准备好花几个小时进行实验,不要因为失败而气馁。根据我的经验,是对基本TCP/IP的无知导致了数据包过滤的困扰,而不是数据包过滤器本身。试图在不了解网络的情况下过滤网络流量是令人沮丧和毫无意义的。然而,真正理解TCP/IP的唯一方法就是真正地使用它。再次学习第7章。如果这还不够,请深入阅读那里推荐的书籍。

FreeBSD有很多包过滤器:IPFW、IP过滤器和PF。

IPFW是原始的FreeBSD数据包过滤软件。它与FreeBSD紧密集成;事实上,通用名为 /etc/rc.firewall/etc/rc.firewall6 的文件纯粹是用于IPFW的。虽然功能强大,在经验丰富的FreeBSD管理员中非常受欢迎,但对于初学者来说有点困难。

第二个包过滤器,IP Filter ,不是FreeBSD特定的防火墙程序,但在几个类Unix操作系统上受支持。这主要是Darren Reed个人的工作,他通过英勇的努力开发了绝大多数代码,并将其移植到所有这些操作系统中。如果您想在多个操作系统之间共享一个防火墙配置,IP Filter 器最有用。

我们将重点介绍富有想象力的 PF ,即数据包过滤器(packet filter)。PF起源于OpenBSD,其设计特点是功能强大、灵活且易于使用。普通FreeBSD管理员可以使用PF来实现其他两个数据包过滤器可能达到的几乎任何效果。

启用 PF

PF包括数据包过滤内核模块 pf.ko 和用户空间程序 pfctl(8) 。在使用PF之前,您必须加载内核模块。最简单的方法是在rc.conf中启用PF:

PF默认为接受所有立场,这意味着您不会仅仅通过启用防火墙就将自己锁定在服务器之外。

数据包筛选中的默认接受和默认拒绝

安全立场(默认接受和默认拒绝)在数据包过滤中至关重要。如果您使用默认接受立场并希望保护您的系统或网络,则需要许多规则来阻止每一种可能的攻击。如果你使用默认的拒绝立场,你必须明确地为你提供的每一项小服务打开漏洞。在几乎所有情况下,默认拒绝都是可取的;虽然它可能更难管理,但它增加的安全性大大弥补了这一困难。

当使用默认的拒绝姿态时,很容易将自己锁定在远程访问机器之外。当您通过SSH连接到远程计算机并意外违反了允许SSH访问的规则时,您就有麻烦了。每个人都至少做过一次,所以当它发生在你身上时不要太尴尬。关键是,最好不要在远程机器上学习数据包过滤;从一台你可以操控的机器开始,这样你就可以很容易地恢复。我已经多次切断了自己的访问权限,通常是因为我在解决无关的数据包过滤问题时没有思考清楚。如果没有远程控制台或IPMI,唯一的解决办法就是在我爬上车、开车到远程位置时踢自己,并在我解决问题时向给我带来不便的人道歉。幸运的是,随着年龄的增长,这种情况越来越少。

尽管如此,在几乎所有情况下,默认拒绝立场都是正确的。作为一名新管理员,您可以合理学习数据包过滤的唯一方法是可以方便地访问系统控制台。如果你对自己的配置不完全有信心,除非你有远程控制台和电源访问、有能力的本地管理员或串行控制台,否则不要在全国各地设置数据包过滤系统。

基本数据包过滤和状态检测

回想第7章,TCP连接可以处于各种状态,如opening、open、closing等。例如,当客户端向服务器发送SYN数据包以请求连接同步时,每个连接都会打开。如果服务器正在侦听请求的端口,它会以SYN-ACK响应,意思是“我已经收到了您的请求,这是我们连接的基本信息。”客户端用ACK包确认收到了信息,意思是,“我确认收到了连接信息。”三方握手的每个部分都必须完成,才能建立连接。您的数据包过滤规则集必须允许握手的所有部分以及实际的数据传输发生。如果数据包过滤规则不允许传输SYN-ACK,则允许服务器接收传入的连接请求是无用的。

在20世纪90年代初,数据包过滤器单独检查每个数据包。如果数据包符合规则,则允许通过。系统不会记录它之前通过的内容,也不知道数据包是否是合法交易的一部分。例如,如果一个标记为SYN-ACK的数据包到达,并且数据包过滤器内有一个目的地地址,则数据包过滤器通常会决定该数据包必须是对它之前批准的数据包的响应。这样的数据包必须经过批准才能完成三次握手。因此,入侵者伪造了SYN-ACK数据包,并利用它们绕过看似安全的设备。由于数据包过滤器不知道谁以前发送过SYN数据包,因此无法拒绝非法的SYN-ACK数据包。一旦入侵者在网络中获得数据包,他通常可以触发随机设备的响应,并开始蠕虫入侵。

现代数据包过滤器使用状态检测来解决这个问题。状态检查(Stateful inspection)意味着跟踪每个连接及其当前状态。如果传入的SYN-ACK数据包似乎是正在进行的连接的一部分,但没有人发送相应的SYN请求,则该数据包将被拒绝。虽然这使内核复杂化,但编写有状态检查数据包过滤器规则比编写老式规则更容易。数据包过滤器必须跟踪许多可能的状态,所以这比看起来更难编程——尤其是当你添加了数据包碎片、反垃圾邮件等问题时。

PF默认执行状态检查。您不需要在规则中指定它。

如果你开始认为,“嘿,数据包过滤听起来像防火墙”,那么在某种程度上你是对的。防火墙(firewall )一词适用于各种网络保护设备。其中一些设备非常复杂;一些人在智力竞赛中输给了煤渣。如今,防火墙一词只不过是一个营销流行语,很少有具体的会议。防火墙这个词就像汽车这个词:你是指一辆生锈的1972年Gremlin,它有一个6马力的发动机和一个排放足够烟雾的排气系统,足以违反《京都议定书》,还是一辆闪亮的特斯拉跑车,有一个500马力的发动机、一个花哨的三色漆面和《启示录》的立体声系统?两者都有其用途,但其中一个显然是为性能而设计的。虽然防火墙的小精灵可能有他们的位置,但最好是得到你能负担得起的最好的。

话虽如此,FreeBSD可以像你想要的那样成为坚固的防火墙。数据包过滤只是个开始。软件包集合包含各种应用程序代理,可以让你的FreeBSD系统与Checkpoint或PIX竞争,并以节省数万美元的价格脱颖而出。

配置 PF

/etc/pf.conf 中配置PF。此文件包含语句和规则,其格式因配置的功能而异。不仅规则顺序非常重要,而且配置特征的顺序也非常重要。例如,如果在重新组装碎片数据包之前尝试进行状态检查,连接将无法正常工作。

默认的 /etc/pf.conf 具有正确顺序的示例规则,但如果您有丝毫混淆的危险,我建议您在部分之间放置大的注释标记,必要时使用大写字母。(使用哈希标记注释 pf.conf 。)必须按以下确切顺序输入特征:

  1. Macros —— 宏
  2. Tables —— 表
  3. Options —— 选项
  4. Packet normalization —— 数据包规范化
  5. Bandwidth management —— 带宽管理
  6. Translation —— 翻译
  7. Redirection —— 重定向
  8. Packet filtering —— 包过滤

是的,PF不仅仅是过滤数据包。它是一个通用的TCP/IP操作工具。我们不会在这里介绍它的所有功能;去读彼得的书。

Macros

macro允许您定义变量,使编写和阅读规则更容易。例如,以下是用于定义网络接口和IP地址的宏:

在规则的后面,您可以将网络接口描述为 $interface ,将服务器的IP地址描述为 $serveraddr 。这意味着,如果您重新编号服务器或更改网卡,在 pf.conf 中进行一次更改将完全更新您的规则。

有时,你会希望规则引用“当前此接口上的所有IP地址”。你不在乎流量到达哪个地址,你只想接受或拒绝该接口的流量。PF为此提供了简写。将接口名称括在括号中,我们稍后会看到。(您可以使用不带括号的接口名称,但PF不会注意到自上次重新加载或重新启动以来的任何IP更改。)

Tables 和 Options

PF可以通过表存储长地址列表。这是PF比我们将要使用的更复杂的用法,但你应该知道这种能力是存在的。

同样,PF有各种选项来控制网络连接时间、表大小和其他内部设置。默认设置通常足以用于正常(和大多数异常)使用。

Packet Normalization

TCP/IP数据包在传输过程中可能会被分解,处理这些数据碎片会增加系统负载和服务器为处理请求和过滤数据包所必须做的工作量。系统必须在将这些碎片交给客户端软件之前重新组装它们,同时决定如何处理到达的任何其他随机碎片。PF将这种重新组装称为 scrubbing 。例如,要重新组装网络接口中的所有片段,删除所有太小而不可能合法的片段,并对传入的数据流进行合理的清理,请使用以下规则:

这会影响进入计算机的所有数据包。

虽然擦洗看起来像是“很好”,但实际上它非常重要,因为PF过滤器是基于整个数据包的。碎片更难过滤,除非重新组装,否则需要特殊处理。不清理流量会导致连接问题。

Bandwidth, Translation, 和 Redirection

PF包括对防火墙至关重要的其他功能,并执行通常与网络设备相关的其他功能。通过排队,PF可以控制主机在每个IP甚至每个端口上传输的流量。PF包括一系列支持网络地址转换(NAT)和端口重定向的功能,这是两个关键的防火墙功能。这种支持超过了许多商业产品。

所有这些都会填满另一本书。字面上。彼得·汉森写了《PF之书》。去读一读,然后建一个防火墙。每个系统管理员一生中都应该至少用一次原始操作系统构建防火墙。即使你重新使用商业产品、小型嵌入式设备或pfSense或OPNsense等产品,你也会学到很多东西。

小型服务器 PF规则示例

这是一组保护小型互联网服务器的PF规则示例。从这里开始,编辑此内容以满足服务器的要求。

我们首先为接口名称➊定义一个宏,这样如果我们更换网卡,就不需要重写所有规则。

第二行指示PF不要在lo0接口上进行过滤➋。环回接口是机器的本地接口。唯一可以通过它进行通信的主机是本地计算机。

然后,我们清除传入的流量➌,将数据包重新组装成一个连贯的整体,扔掉无法重新组装的部分。

现在我们有了合理的传入数据流,我们可以对其进行过滤。此策略首先阻止所有传入流量➍, 设置默认拒绝策略。未明确允许的一切都是禁止的。

出站流量将获得默认的允许策略➎。

此策略中的最后三条规则涉及TCP、UDP和ICMP。它们有类似的格式,我们稍后将对此进行剖析。

首先,我们允许TCP流量到端口22、53、80和443➏。

接下来,我们允许UDP流量到端口53➐。如果此主机提供的服务比DNS多,我们的端口列表会更长。

最终规则允许重要的ICMP流量到达我们的主机,并允许主机做出响应➑。

让我们仔细看看TCP规则。

此主机对入站流量有默认的拒绝策略,因此使用传入语句➊,我们正在为该策略创建一个例外。

规则的下一块指定了此规则应用于哪个接口➋。此规则适用于宏 $ext_ifem1 定义的接口。

然后,我们指定一个协议➌。此规则适用于TCP连接。

您可以编写仅适用于特定源地址或目标地址的PF规则。此规则适用于来自任何主机的流量➍。如果您允许任何源地址,则可以删除此部分规则。

然后,我们指定一个目标地址➎。目标是括号中的接口名称,意思是“此接口上的任何IP地址”

最后,定义此规则适用的端口➏。大括号允许您将多个实体组合在一起。过滤器允许连接到端口22(ssh)、53(DNS)、80(HTTP)和443(HTTPS)。您可以通过名称指定端口(来自 /etc/services ),但我发现数字更可靠。编辑 /etc/services 不应该破坏你的防火墙!在此主机上部署新的TCP服务只需要向列表中添加端口并重新加载防火墙规则。

UDP规则略有不同。

最明显的变化是定义UDP协议而不是TCP➊。另一个不太明显的变化就是此规则删除了源地址。它适用于来自任何地址的数据包。该分组过滤器只允许一个端口,53➋。具有单个端口的规则不需要大括号。

ICMP规则看起来有点棘手,但实际上是一样的。

指定此规则适用于ICMP很简单➊。此规则也不列出源地址,因此适用于来自任何地方的流量。

如果TCP和UDP规则指定了目标端口,则此ICMP规则列出了ICMP类型➋。ICMP没有端口,但它有不同类型的流量。不过,就我们的目的而言,ICMP类型很像端口。类型有数字代码,但名称更容易。

此规则指定了四种不同类型的ICMP流量➌。

总体而言,该规则允许ICMP流量,这通常是正常互联网运行所必需的。您的环境可能需要其他ICMP类型。您的组织的安全策略可能会指定您可以和不能通过的ICMP。但对于面向互联网的服务器来说,这四个是合理的组合。

这个简单的策略定义了与我们的服务器通信的基本规则。虽然它并不完美,但它可以为入侵者设置障碍。那个闯入你的网络服务器并在端口10000上启动命令提示符的混蛋?如果你的防火墙规则不允许该端口上的传入连接,那么他们所有的努力都将付诸东流。很悲催。

管理 PF

使用 pfctl(8) 管理PF。如果您的规则没有错误,pfctl(8) 将静默运行;它仅在出现错误时才产生输出。您需要测试、激活、查看和删除规则。

测试规则

由于防火墙错误会给你带来很多麻烦,最好在激活规则之前检查一下。虽然规则检查只解析文件,但检查规则本身的语法错误,激活有语法错误的规则要么使您的系统不受保护,要么将您锁定,要么两者兼而有之。使用 -n 标志检查文件是否存在问题,使用 -f 指定PF规则文件。

如果您遇到错误,请修复它们并重试。

激活规则

语法检查静默运行后,通过删除 -n 标志来激活新规则。

更改PF配置非常快。这意味着您可以针对不同的时间或情况进行多种PF配置。也许你想只允许在一天中的某些时间访问某些服务;您可以安排一次 pfctl(8) 运行,以便在这些时间安装适当的规则。或者,您可能对灾难情况有单独的规则,并希望在失去互联网连接时安装一个特殊的规则集。使用 pfctl(8) 可以使所有这些配置变得简单。

查看规则

如果你想查看防火墙上当前运行的规则,请使用 pfctl -sr

您可以按照此处所示的格式编写PF规则。

请注意,虽然我们在配置文件中指定了多个TCP端口,但在数据包过滤器中,每个TCP和UDP端口都有自己的规则。同样,每种ICMP类型都有自己的规则。

删除规则

最后,使用 -Fa (flush all)标志从运行配置中删除所有规则。(您可以使用 a 以外的标志来删除防火墙配置的一部分,但这可能会使您的系统处于不一致的状态。)

您将看到PF系统地擦除配置中的所有规则、NAT配置和其他任何内容。在加载新配置之前,不要手动清除配置;只需加载新规则文件即可删除旧规则。

PF非常强大,非常灵活,几乎可以以任何你喜欢的方式(以及一些你不喜欢的方式)滥用TCP/IP。我们几乎没有触及表面。查看第462页“数据包过滤”开头列出的一些资源,深入探索PF。

Blacklistd(8)

有时,您需要比简单的允许或拒绝许可更周到的数据包过滤。我经常让SSH服务器向公共互联网开放,这样我就可以从任何地方登录。不过,我更讨厌僵尸网络认为我愚蠢到允许无密码登录。这就是 blacklistd(8) 发挥作用的地方。

Blacklistd让一个守护进程报告:“嘿,这个IP地址在窃听我。”一旦Blacklistd收到足够数量的关于某个地址的投诉,它就会告诉防火墙阻止该地址。那些机器人一直在戳你的SSH服务器?他们是历史。

这种黑名单对像Hail Mary Cloud这样的分布式僵尸网络只有微弱的帮助,但即使如此,你也可以配置敏感度来屏蔽最烦人的客户端。这一切都取决于每个僵尸网络成员的侵入程度。

要使用黑名单,您必须设置数据包过滤器以接受来自黑名单的输入,为每个服务设置容差级别,并将服务配置为使用黑名单。

PF 和 Blacklistd

PF通过锚点(anchors)处理动态规则。您可以使用 pfctl(8) 编辑活动锚点,让您在策略中的特定点插入规则。在第一个 blockpass 语句之前,将黑名单锚点添加到您的规则中。使用上一节中的策略,您的规则如下:

您必须在锚点名称周围加上引号,并且必须指定接口。

数据包过滤器现在已准备好进行动态黑名单。

配置 Blacklistd

Blacklistd的配置来自 /etc/Blacklistd.conf 。虽然它的大部分配置都在这个文件中,但您也可以使用命令行选项修改服务的行为。

首先在 /etc/rc.conf 中启用黑名单。

守护进程在您重新启动或手动启动之前不会启动,因此您现在可以对其进行配置。

/etc/blacklistd.conf

每个黑名单规则都支持一个服务、端口或一组地址。将您的规则放入 /etc/blacklistd.conf ,每行一条规则。黑名单规则分为两组,本地(local)和远程(remote)。

local 黑名单规则适用于运行黑名单的机器本地的项目。您可以在此处为本地SSH服务、端口99或其他任何本地设备设置规则。地方法规部分以 [local] 开头。

remote 黑名单规则适用于机器本地以外的项目。在这里,您可以定义诸如“此块的容忍度降低”或“在更短的时间内禁用这些地址”或“永远不要阻止这些地址”之类的规则。远程规则部分以 [remote] 开头。我们将首先讨论本地规则,然后讨论远程规则支持的添加。

下面是一个 blacklistd.conf 条目示例:

第一行是 [local] 语句。在此之后出现的每个规则都适用于本地计算机,直到我们点击 [remote] 条目。

每条规则有七个字段。前四个字段标识要列入黑名单的流量,而最后三个字段定义黑名单行为。星号(*)是通配符,表示任何内容都与此字段匹配。

因此,有了这个规则,三次未能通过SSH身份验证将导致客户端被阻止24小时。

一旦设置了本地规则,就可以配置远程规则。

blacklistd.conf 远程规则

使用远程规则指定黑名单如何根据远程主机改变其行为。远程规则中的每个字段都与本地规则中的字段相同,但黑名单如何使用它们会发生变化。以下是一个远程规则示例:

address 列是IP(IPv4或IPv6)地址、端口或两者。这允许您为特定的远程地址范围设置特殊规则。我们的示例规则适用于地址范围203.0.113.128/25。

type, protocol, 和 owner列的解释与本地规则相同。

name 列变得有趣起来。远程规则中的等号表示“使用您正在匹配的本地规则中的值”。此规则表示获取防火墙规则名称条目,并向其添加网络前缀/25(255.255.255.128网络掩码)。如果来自此地址范围的连接被列入黑名单,将影响整个子网。如果在此处放置PF锚点名称,则黑名单会将此地址块的规则添加到命名锚点中。通配符将恢复为默认表。

nfail 列允许您为此地址设置自定义失败次数。也许你想让一个客户在前30次额外尝试失败时都不知道如何输入密码。将此列设置为星号将禁用阻止。

disable 列允许您为此地址块设置自定义块时间。在此处使用通配符可禁用阻止。

远程规则允许您对不喜欢的人实施更严格的限制,同时告诉 blacklistd(8) 永远不要将您的办公室列入黑名单。

您现在可以开始列入黑名单。不过,它不会做任何事情,因为程序不知道他们应该向它投诉。但是一旦你配置了它们,它就准备好了。

配置 Blacklistd 客户端

FreeBSD包括一些黑名单感知(blacklistd-aware)客户端。您最可能使用的两个是 ftpd(8)sshd(8)

要在SSH服务器中启用黑名单,请在 /etc/ssh/sshd_config 中添加以下行。

重新启动sshd。

/etc/inetd.conf 或独立进程的 /etc/rc.conf 标志中,使用 -B 命令行选项在 ftpd(8) 中启用黑名单。

现在,只要有人登录失败,这些程序就会被列入黑名单。

管理 Blacklistd

将无权攻击您服务的烦人客户列入黑名单会减少您需要进行的日志分析量,但您可能想确切地了解黑名单阻止了什么。你想要 blacklistctl(8)

blacklistctl(8) 程序只有一个功能:显示被黑名单阻止的地址和网络。你总是想要 blacklistctl 转储命令。

默认情况下,blacklistctl 转储显示在要阻止的候选列表中但尚未阻止的主机。添加 -b 标志以查看所有被阻止的主机。

在这里,我们看到地址范围203.0.113.128/25尝试了3次允许的登录尝试中的6次。它是如何实现这一目标的?SSH允许客户端在单个TCP/IP连接上尝试多次登录。黑名单不会阻止实时连接。有罪主机上次尝试访问此服务是在 last access 中显示的日期。

您可能会发现剩余的时间比上次访问的时间更有用。添加 -r 标志。

很快,这个子网将可以自由骚扰和骚扰我无辜的SSH服务器。也许我需要增加黑名单的持续时间。

De-Blacklisting

尽管你尽了最大的努力,但总有一天你需要在黑名单自然到期之前从黑名单中提取一个地址。blacklistctl(8) 程序无法做到这一点:您必须手动从PF表中删除地址。这样做需要了解黑名单如何管理PF内部的地址。

每个被阻塞的端口在黑名单锚内都有一个子锚。此锚点以端口命名。阻止端口22的子锚点将被称为 blacklistd/22 。在子锚点中,您会发现一个包含被阻止地址的表。该表名为port,后跟端口号。无法再连接到端口22的主机出现在名为 port22 的表中。

在这里,我使用数据包过滤器控制程序 pfctl(8) 来检查子锚点黑名单/22内的port22表的内容。我不打算解释这一切;只需替换您的表名和子锚点名。

是的,我们的问题地址在里面。删除它需要一个相当神秘的 pfctl(8) 命令。

黑名单保存在PF之外的数据库中,因此黑名单地址仍将显示在 blacklistctl(8) 中。该数据库条目最终将无害地过期。如果主机再次行为不端,它将再次被阻止。

公钥加密

许多服务器守护进程依赖于公钥加密来确保通信的机密性、完整性和真实性。许多不同的互联网服务也使用公钥加密。您需要基本掌握公钥加密才能运行安全网站(https)和安全POP3邮件(pop3ssl)等服务。如果你已经熟悉公钥加密,你可能可以跳过这一节。如果没有,请做好准备,对该主题进行高度压缩的介绍。

加密系统使用密钥在可读(明文)和编码(密文)版本之间转换消息。虽然明文(cleartext)和密文(ciphertext)这两个词都包括文本,但它们并不局限于文本;它们还可以包括图形文件、二进制文件和您可能想要发送的任何其他数据。

所有密码系统都有三个主要目的:完整性、机密性和不可否认性。完整性(Integrity)意味着消息没有被篡改。保密性(Confidentiality)意味着信息只能由预期的受众阅读。不可否认性(nonrepudiation)意味着作者以后不能声称他或她没有写这条信息。

旧的密码依赖于一个密钥,任何拥有密钥的人都可以加密和解密消息。你可能不得不做很多工作来转换信息,就像二战期间让盟军发疯的恩尼格玛密码机一样,但钥匙使转换成为可能。一个典型的例子是任何需要密钥或密码的代码。间谍小说中流行的一次性信息板是终极单密钥密码,除非你有确切的密钥,否则不可能破解。

与单密钥密码不同,公钥(或非对称——asymmetric)加密系统使用两个密钥:私钥和公钥。消息用一个密钥加密,用另一个密钥解密,数字签名确保消息在传输过程中不会被篡改。解释这一点的数学方法真的很可怕,但它确实有效——只要接受一个事实,那就是非常大的数字的行为非常非常奇怪。一般来说,密钥所有者会对私钥保密,但会将公钥交给全世界,供任何人使用。密钥所有者使用私钥,而其他人使用公钥。密钥所有者可以加密任何人都可以读取的消息,而公众中的任何人都能发送只有密钥所有者才能读取的消息。

公钥密码学满足了我们对完整性、机密性和不可否认性的需求。如果作者希望任何人都能阅读他的消息,同时确保消息不被篡改,他可以用私钥加密消息。任何拥有公钥(即世界)的人都可以阅读该消息,但篡改该消息会使其难以辨认。(根据使用情况,他可能会选择对消息进行数字签名。)

以这种方式加密消息还可以确保消息的作者拥有私钥。如果有人想发送一条只能由特定人阅读的消息,他可以用所需受众的公钥对消息进行加密。只有拥有匹配私钥的人才能读取消息。

只要私钥保持私密,这就可以很好地工作。一旦私钥被盗、丢失或公开,安全性就会丧失。一个粗心的人,他的私钥被盗了,甚至可以找到其他人为他签署文件。小心你的私钥,除非你想知道有人用你的私钥订购了价值50万美元的高端图形工作站,并让他们在底特律市中心的一个废弃的房子里过夜。

所有这些操作的标准工具包是OpenSSL。

为什么选择OPENSSL? 多年来,OpenSSL是加密库的唯一选择。今天的新替代方案虽然可能更可靠,但不符合FreeBSD的长期支持模式。最明显的替代品LibreSSL只支持每个版本一年。在加密工具包既可靠又可以在FreeBSD版本的整个生命周期内升级之前,OpenSSL不会被取代。

OpenSSL

FreeBSD包括用于处理公钥加密的OpenSSL工具包。OpenSSL允许您执行全方位的加密操作。虽然许多程序使用OpenSSL功能,但系统管理员并不经常直接需要OpenSSL。

虽然OpenSSL开箱即用,但我发现设置一些默认值是值得的,这样我的生活会更轻松。使用 /etc/ssl/openssl.cnf 文件配置OpenSSL。此文件中的几乎所有设置都是正确的,除非您是密码学家,否则不应该更改它们。可以更改的几件事是生成加密签名的默认值。每个默认值都由字符串 _default 标记。您最感兴趣的是常见OpenSSL操作的以下设置,我已经根据需要进行了调整:

countryName_default ➊是您所在国家的两个字母的代码,在我的例子中是美国。stateOrProvinceName_default ➋是当地州的名称,长度可以是任何长度。我会把它放在密歇根州。0.organizationName_default 字段➌是您的公司名称。如果我要购买一份签名证书,我会在这里放上我想出现在证书上的东西。如果我只是在测试程序如何使用SSL,而没有真实的公司名称,我可能会使用我工作的公司名称或我编造的东西。

以下值不会显示在 openssl.cnf 中,但如果您设置了它们,它们将在openssl命令提示中显示为默认值。我发现这些很有用,尽管它们比以前的默认值更改得更频繁——即使没有别的,它们也会提醒我这些答案的正确格式。

localityName_default ➊是您所在城市的名称。organizationalUnitName_default ➋是本证书适用于贵公司的一部分。OpenSSL中最常被误解的值之一, commonName_default ➌,是此证书所针对的计算机的主机名,因为它出现在反向DNS中。记住,反向DNS不一定与主机名相同!您的web服务器可能有一个很好的友好名称,但托管公司可能会在反向DNS中为其分配一个完全不同的名称。最后, emailAddress_default ➍是站点管理员的电子邮件地址。

这些值都作为默认选项显示在OpenSSL命令的提示中。在配置文件中设置它们将节省您以后的烦恼。

证书

公钥加密的一个有趣之处在于,作者和观众不必是人。它们可以是程序。安全外壳(Secure Shell——SSH)和安全套接字层(Secure Sockets Layer——SSL)是两种不同的程序通信方式,无需担心入侵者的监听。公钥加密是安全网站和安全邮件服务使用的数字证书(digital certificates)的主要组成部分。当你打开Firefox在线购物时,你可能没有意识到浏览器正在疯狂地加密和解密网页。这就是为什么你的计算机可能会抱怨“无效证书”;某人的公钥已经过期,或者证书是自签名的。今天的协议使用传输层安全性(Transport Layer Security —— TLS)进行加密和解密,并使用TLS证书(TLS certificates)。

SSL与TLS 你经常听说SSL,但它往往是不正确的。如今,传输层安全(TLS)已基本取代SSL。SSL一词的大多数用法都是挥之不去的残余。一般来说,面向互联网的网站应该使用TLS 1.1或更高版本。TLS 1.0版本仅受到弱保护。任何版本的SSL协议保护的流量都不安全。

许多公司,如VeriSign,都提供公钥签名服务。这些公司被称为证书颁发机构(Certificate Authorities —— CA),因为它们提供 TLS certificates 。其他需要签署证书的公司提供身份证明,如公司文件和业务记录,这些公钥签署公司用他们的CA证书签署申请人的证书。通过签署证书,CA表示,“我已经检查了这个人的证件,他、她或它已经证明了他们的身份,让我满意。”然而,他们并不能保证其他任何事情。TLS证书所有者可以使用该证书来运行销售欺诈或危险产品的网站,或者使用它来加密勒索单。签名的TLS证书保证某些类型的技术安全,而不是个人完整性,甚至是单方面的技术安全。证书不会神奇地为您应用安全补丁。

Web浏览器和其他使用证书的软件包括主要CA的证书。当浏览器收到由CA签名的证书时,它会将该证书识别为合法的。从本质上讲,网络浏览器会说,“我信任证书颁发机构,证书颁发机构信任这家公司,所以我会信任该公司。”只要你信任CA,一切都会好起来的。

ca_root_nss 包含Mozilla项目识别的ca证书。如果某个软件在尝试验证证书时失败,请确保您安装了此软件包。

大多数CA都是大型商业公司。不过,无论你的组织规模有多大,我都鼓励你调查Let’s Encrypt(https://www.letsencrypt.org/). Let's Encrypt是一个提供免费、全球有效TLS证书的CA。

使用未经任何CA签名的证书进行测试是完全可以的。对于公司内部的应用程序来说,这可能也足够了,您可以在客户端web浏览器中安装证书或告诉用户信任证书。我们将从两个方面考虑。

证书的两种使用都需要主机密钥。

TLS 主机密钥

签名证书和自签名证书都需要主机的私钥。主机密钥只是一个精心设计的随机数。以下命令创建2048位主机密钥并将其放置在文件 host.key 中:

您将看到一条声明,说明OpenSSL正在创建主机密钥,并且随着密钥生成的进行,屏幕上会出现点。在短短几秒钟内,您将获得一个包含密钥的文件。密钥是一个明文文件,其中包含 BEGIN RSA PRIVATE KEY 和一堆随机字符。

保护您的主机密钥!使其归root所有,并且只能由root读取。一旦您将证书投入生产,任何拥有该密钥的人都可以使用它来窃听您的私人通信。

将此主机密钥放置在与密钥文件本身具有相同权限的目录中。

创建证书请求

您需要一个签名或自签名证书的证书请求。我们对OpenSSL做得不多,所以我们不会剖析这个命令。使用您的主机密钥转到目录,并逐字输入以下内容:

作为回应,您将看到说明,然后是一系列问题。按ENTER键,您将获得默认答案。如果您已配置OpenSSL,则默认答案是正确的。

国家的两个字母代码➊在ISO 3166标准中定义,因此快速的网络搜索会为您找到它。如果你不知道你居住的州➋和城市➌,问问偶尔离开服务器机房的人。组织名称➍可能是您的公司,您也列出了部门或部门名称➎。如果你没有公司,请列出你的姓氏或其他唯一标识自己的方式,对于自签名证书,你可以列出任何你想要的东西。不同的CA对非公司实体有不同的标准,因此请查看CA的说明。

常用名称(Common Name)➏经常被误解。这不是你的名字;它是反向DNS中显示的服务器名称。您必须在此处输入服务器名称,否则请求将无效。

我建议使用通用电子邮件地址➐而不是个人的电子邮件地址。在这种情况下,我是michaelwlucas.com,所以我最好使用我的地址。你不希望你的组织的证书与可能因任何原因离开公司的个人联系在一起。

挑战密码➊也称为密码短语(passphrase)。同样,请保守这个密码,因为任何拥有密码的人都可以使用您的证书。但是,使用证书密码是可选的。如果使用一个,则必须在服务器启动时键入它。这意味着,如果你的网络服务器崩溃,在有人输入密码之前,网站将无法运行。虽然密码短语的使用是非常可取的,但这可能是不可接受的。按ENTER键使用空白密码。 您已经输入了不少公司名称,因此第三个➋可能是不必要的。

返回命令提示符后,您将在当前目录中看到文件 csr.pem 。它看起来很像你的主机密钥,除了顶行显示的是 BEGIN CERTIFICATE REQUEST 而不是 BEGIN RSA PRIVATE KEY

csr.pem 提交给您的证书颁发机构,证书颁发机构将返回实际证书。我建议将证书保存在以主机命名的文件中,例如 www.mwl.io.crt。此签名证书适用于任何TLS服务,包括网页、pop3ssl或任何其他支持TLS的守护进程。

某些CA要求您在证书中使用中间证书(intermediate certificate)。虽然大多数守护进程都有指定中间证书的配置选项,但如果您没有,您可以将签名的证书附加到中间证书的末尾。

自己签署证书

自签名证书在技术上与签名证书相同,但它不会提交给证书颁发机构。相反,你自己提供签名。大多数客户不会接受生产服务上的自签名证书,但它非常适合测试。要签署自己的CSR,请运行以下命令:

就是这样!您现在在 selfsigned.crt ➋文件中拥有一个有效期为365天➊的自签名证书。只要您愿意忽略应用程序显示的警告,您就可以像使用签名证书一样使用此密钥。

如果你签署自己的证书,客户端软件会生成“证书签名者未知”的警告。这是意料之中的——毕竟,我办公室以外的人不知道Michael W.Lucas是谁,也不知道他为什么签署网络证书。出于某种原因,人们信任赛门铁克和其他大公司的CA。认识我的人信任我,但整个世界都不信任我。因此,不要在公众看到的任何地方使用自签名证书,因为警告会让他们感到困惑、恼火,甚至吓跑他们。

但在您在CA证书上投入任何金额之前,一定要查看Let's Encrypt。它真的会改变你的系统管理实践。

TLS Trick: 连接到 TLS-Protected 端口

我说过我们不会用OpenSSL做太多事情,这是正确的。然而,该软件提供的一个功能太有用了,不容错过,一旦你知道了,你每月至少会使用一次这个技巧,并很高兴你拥有它。

在本书中,我们通过使用 telnet(1) 连接到在该端口上运行的守护进程并发出命令来测试网络服务。这适用于SMTP、POP3和HTTP等明文服务。它不适用于HTTPS等加密服务。当您连接到这些服务时,您需要一个程序来为您管理加密。OpenSSL包含 openssl s_client 命令,该命令正是用于此类客户端调试的。虽然你会看到很多加密信息,但你也可以向守护进程发出明文命令并查看其响应。使用命令 openssl s_client -connect ,用冒号分隔主机名和端口号。在这里,我们连接到www.absolutefreebsd.com上的安全网络服务器:

你会看到很多关于信任链和责任限制的内容,以及一行行行随机数字证书。然而,在所有这些之后,您将看到一个没有命令提示符的空白行。您正在直接与服务器守护进程对话。由于这是一个web服务器,让我们尝试一个HTTP命令:

系统响应如下:

我想,自从我上次尝试以来,HTTP协议已经发生了变化。但我肯定连接到了网络服务器。网络工作。

你们中的一些人可能想知道,如果与加密服务通信如此容易,为什么我们要加密服务。加密无法保护守护进程;它保护客户端和服务器之间的数据流。TLS加密可防止有人在传输过程中窃听您的网络对话——它既不能保护服务器,也不能保护客户端。如果有人闯入你的桌面,TLS无法拯救你。

从这一点开始,我假设您理解这个OpenSSL命令以及我们使用它时会发生什么。

硬件加密支持 大多数现代硬件都有内置的加密加速功能。不幸的是,FreeBSD没有将其包含在默认配置中。硬件加密加速降低了CPU的负载,并可能加速加密。aesni(4) 内核模块激活对英特尔硬件加密加速器的访问。新AMD加速器的驱动程序正在开发中。内核内驱动程序仅影响内核中发生的加密,例如加密磁盘和IPSec。

全局安全设置

FreeBSD支持许多可选的安全设置。这些设置改变了FreeBSD的基本行为,使其与常见的Unix体验不同。然而,其他一些操作系统默认提供这些设置,因此它们不是FreeBSD独有的。

您是否应该以提高安全性的名义打开所有这些功能?这里没有普遍正确的答案。如果将系统的部分访问权限限制为root帐户意味着你需要给更多的人root访问权限,也许你不应该施加这种限制。不过,其中一些应该在所有系统上激活。

安装时的选项

FreeBSD安装程序提供了在首次启动时启用这些设置的选项。您可以稍后使用给定的sysctl设置启用和禁用它们。

其中许多功能在用户不多的服务器上特别有用。如果您的应用程序服务器除了应用程序使用的用户外没有其他无特权用户,您可能应该启用限制无特权用户的功能。不过,如果你有无特权用户,请更仔细地考虑情况。我的大多数无特权用户不应该查看服务器进程或其他用户,所以我将其锁定。

隐藏其他UID的进程

通常,像 ps -ax 这样的命令会显示系统上运行的所有进程。当您将sysctl security.bsd.see_other_ids 设置为0时,用户只能看到自己的进程。无论您如何设置,Root都可以看到所有进程。

隐藏其他GID的进程

同样,用户通常可以看到其他组拥有的进程。通过将sysctl security.bsd.see_other_gids 设置为0来禁用该功能。同样,无论如何设置,root都可以看到每个进程。

隐藏 Jailed 进程

主机上的用户通常可以看到jails中运行的所有进程。通过将 security.bsd.see_jail_proc 设置为0,无特权的非监禁用户无法看到监禁的进程。此功能出现在FreeBSD 12中。

隐藏消息缓冲区

非特权用户通常可以通过 dmesg(8) 看到系统消息缓冲区。通过将sysctl security.bsd.unprivileged_read_msgbuf 设置为0来禁用该访问。

禁用进程调试

调试器可以告诉用户一大堆有用的信息。将 security.bsd.unprivileged_proc_debug 设置为0,禁止无权限用户在进程上使用调试器。

随机化进程ID

传统的Unix系统按顺序创建进程ID,使攻击者有机会猜测下一个PID是什么。通过将sysctl kern.randompid 设置为随机大整数来随机化进程ID。如果将其设置为1,内核将在每次启动时选择一个100到1123之间的新随机数。

清除 /tmp

所有明智的类Unix系统在启动时都会清理 /tmp 以处置临时文件。在过去几年的某个时候,FreeBSD默认关闭了这种行为。您可以将 tmpfs(5) 用于 /tmp ,它在每次断电时都会被销毁。不过,如果你的 /tmp 在磁盘上,那么。因为你们都是明智和健康的系统管理员,所以在 /etc/rc.conf 中始终将 clear_tmp_enable 设置为 YES

禁用 Syslogd网络

默认情况下,syslogd(8) 在UDP端口514上创建一个半开放的套接字。没有人可以连接到这个端口;它只用作占位符,所以没有其他东西绑定到该端口。有些人认为这个半开放的套接字有问题。我会说这是一个功能;您不希望其他东西绑定到端口514,声称是syslogd,并向您的日志主机发送令人担忧或错误的舒缓消息。但是要禁用半开套接字,请在 /etc/rc.conf 中将 syslogd_flags 设置为 -ss

禁用 Sendmail

默认的FreeBSD安装不接受来自网络的电子邮件,但它确实运行了一个 sendmail(8) 守护进程来发送传出消息。要完全禁用从该主机发送邮件,请在 /etc/rc.conf 中将 sendmail_enable 设置为 NONE

禁用出站邮件不会阻止日常、每周和每月的维护任务运行。但是,除非您直接登录到主机,否则它将阻止您接收这些消息的输出。对于拥有多个主机的人来说,禁用出站邮件是不明智的。如果您使用其他邮件代理,如 dma(8) ,禁用Sendmail是有意义的(请参阅第20章)。

安全控制台

大多数Unix系统认为物理控制台是安全的。任何有权访问物理机器的人都可以对主机执行任何他们想要的操作,包括更改根密码。通过将所有表示安全的 /etc/tty 条目更改为不安全,您可以告诉FreeBSD即使在单用户模式下也需要root密码。这不会阻止某人物理访问您的操作系统,但这意味着他们必须做更多的工作来破坏您的机器。工作稍微多了一些。

不可执行堆栈和堆栈保护

一种基本的漏洞利用缓解技术是非执行堆栈。一旦程序被加载到内存中,分配给该程序的每一页内存都应该是可写的或可执行的,但不能同时是可写和可执行的。

一种常见的漏洞利用技术是欺骗程序将信息写入内存,然后执行该内存。攻击者可能会说服程序写入内存块,但由于堆栈不可执行,内核不会执行它。

在现代版本的FreeBSD上,堆栈默认为不可执行。禁用此功能的唯一原因是,如果你有一个编写糟糕的程序,依赖于执行和写入相同的内存块。在过去的15年里,大多数此类有缺陷的软件都被正确地从开源生态系统中清除了。如果你非常不幸,无法避免运行一个无法处理不可执行堆栈的程序,你可以通过将sysctls kern.elf32.nxstack (适用于32位程序)或 kern.elf64.nxstacg (适用于64位程序)设置为0来禁用它。

与不可执行的堆栈相关,堆栈保护页在程序的内存分配部分之间添加了随机大小的额外内存。这使得攻击者更难猜测内存地址。FreeBSD默认分配堆栈保护页,但您可以通过将sysctl security.bsd.stack_guard_page 设置为0来关闭它。

其他安全设置

FreeBSD的大多数其他内核级安全设置都可以在 security.bsd sysctl树中找到。每隔几个月就会增加更多。运行 sysctl -d security.bsd 以显示主机的可用选项。我在本节前面已经描述了其中的许多内容,但您可能会发现其他一些内容很有用。选项包括禁用root帐户的权限(security.bsd.suser_enabled),允许非root用户设置空闲优先级(security.bsd.unprivileged_idprio),以及阻止非特权用户使用 mlock(2)security.bsid.unprileged_mlock)。看看当前的选项,看看哪些可能有用。

使用 mtree(1) 为入侵做准备

对于系统管理员来说,最糟糕的事情之一就是让他认为他的系统可能被入侵了。如果你在 /tmp 中发现了神秘的文件,或者在 /usr/local/sbin 中发现了额外的命令,或者如果事情“感觉不对劲”,你会怀疑是否有人破坏了你的系统。这种感觉最糟糕的是,没有办法证明它没有发生。熟练的攻击者可以用她自己的自定义版本替换系统二进制文件,这样她的操作就不会被记录下来,你找到她的尝试也会失败。当放大镜是由罪犯提供的,并且包括特殊的罪犯隐形功能时,让夏洛克·福尔摩斯用放大镜检查你的服务器是没有用的!人们甚至劫持了系统编译器,这样新构建的二进制文件就包括了劫持者的后门。更糟糕的是,计算机一直在做奇怪的事情。操作系统极其复杂,应用程序更糟糕。也许 /tmp 中的那个奇怪文件是你的文本编辑器在你按得太快时弹出的,或者可能是草率入侵者留下的。

恢复受损系统的唯一方法是从头开始重新安装,从备份中恢复数据,并希望导致受损的安全漏洞得到修复。这是一个渺茫的希望,怀疑是如此容易获得,以至于许多系统管理员最终停止关心或欺骗自己,而不是生活在持续的担忧中。

大多数入侵者会更改系统上已经存在的文件。FreeBSD的 mtree(1) 可以记录系统上文件的权限、大小、日期和加密校验和。(虽然 freebsd-update(8) 包含类似的功能,而且您不必事先收集数据,但它只涵盖了基本系统。)如果在系统新安装时记录这些特征,则可以完整地记录这些文件的外观。当入侵者更改这些文件时,比较将突出显示差异。当你有一种被黑客攻击的模糊感觉时,你可以检查现有文件上的相同信息,看看是否有任何更改。

运行 mtree(1)

以下命令在根分区上运行 mtree(1) ,并存储SHA512和SHA256加密校验和,将它们放置在一个文件中以供以后分析:

虽然你可以在整个服务器上使用 mtree(1) ,但大多数人使用 -x ➊在每个分区运行一次。您不希望在频繁更改的文件上记录校验和,例如数据库服务器上的数据库分区。在NFS挂载上收集校验和具有运行速度非常慢和增加网络拥塞的双重特征。-ic 标志➋告诉mtree将其结果打印到屏幕上,文件系统中的每个后续层都缩进。此格式与 /etc/mtree 中的系统mtree文件匹配。-K 标志接受几个可选关键字;在这种情况下,我们想要生成SHA512校验和➌和SHA256校验和➍。-p 标志➎告诉mtree要检查哪个分区。几乎每个分区都有定期更改的文件或目录,因此您不想为其记录校验和。使用 -X ➏指定排除文件,该文件包含不匹配的路径列表。最后,将此命令的输出重定向到 /tmp/mtree.out ➐文件。

mtree(1) 输出: 规范文件

mtree(1) 的输出被称为规范或 spec 。虽然此规范最初用于安装软件,但我们使用它来验证软件安装。您的规范从注释开始,显示运行命令的用户、运行命令的机器、分析的文件系统和日期。规范中的第一个实际条目设置了此主机的默认值,并以 /set 开头。

mtree(1) 程序根据其对分区中文件的分析,选择这些设置作为默认设置。默认的文件系统对象是一个文件,由UID 0和GID 0拥有,权限为0755,有一个硬链接和用户存档标志。之后,系统上的每个文件和目录都有一个单独的条目。这是根目录的条目:

此文件是点(.)➊,或者 我们现在所在的目录。这是一个目录➋,它有19个硬链接➌。该目录被修改为Unix纪元时间➍1504101311.033742000秒。Unix纪元始于1970年1月1日。

划时代的秒和真实的日期 你不想从纪元开始数秒吗?要将划时代(epochal)秒转换为正常日期,请运行 date -r seconds。然而,在mtree时代结束时,剪掉这部分;date(1) 只喜欢整秒钟。

在某些方面,目录的条目相当无聊。毕竟,入侵者实际上无法替换目录本身!下面是根目录中实际文件的条目:

我们可以看到文件名以及与根目录中相同的模式、链接和时间信息,还可以获得文件大小➊。此外,还有从文件中计算出的SHA256➋和SHA512➌加密哈希。

虽然从理论上讲,入侵者可以制作一个与特定加密哈希匹配的文件,并且密码学家一直在努力找到创建与任意SHA256和SHA512校验和匹配的文件的实用方法,但入侵者不太可能创建一个与两个校验和匹配、包含他的后门并且仍然运行良好的假文件,以至于系统所有者不会立即注意到问题。当这种情况发生时,我们将有额外的校验和算法来抵抗这些方法,并将切换到它们。

排除文件

排除文件(用 -X 给出)列出了不希望 mtree(1) 分析的文件系统。许多文件系统在没有恶意干预的情况下会发生变化。日志文件和用户主目录应该更改。像 /tmp/var/db/entropy 这样的目录在功能系统上变化更好。在排除文件中,用一个前导点在单独的行中列出您不希望检查的每个目录。

等待一天左右,然后再次运行 mtree(1) 以生成新的规范文件。两个mtree文件之间的差异将使您改进排除文件。当你怀疑系统入侵时,你也会做同样的事情。

保存 Spec 文件

规范文件包含在可疑入侵后验证系统完整性所需的信息。将规范文件留在要验证的服务器上意味着入侵者可以编辑该文件并掩盖其不法行为。您不能将文件保存在系统本身上!偶尔会有人建议您校验mtree规范文件,但将其保留在服务器上。这没用;如果有人篡改了mtree文件和校验和,你怎么知道?或者更糟糕的是,如果有人篡改了规范文件,而你发现了它,你就无法知道发生了什么变化!将您的规范文件复制到安全位置,最好是在离线介质上,如闪存驱动器或光盘。

发现系统差异

当有什么引起你的怀疑,你开始认为你可能遭受了入侵时,创建一个新的mtree规范文件,并将其与离线存储的“已知良好”规范文件进行比较。使用 mtree(1) 检查规格文件之间的差异。

文件中的每个条目都发生了变化。我的排除文件经过了精细调整,消除了我希望更改的文件。此特定运行会生成两行输出。

文件 /bin/sh ➊在mtree运行之间的大小➍发生了变化。这可不好。此外,请注意两个不同的SHA256哈希➋➎和两个不同SHA512哈希➌➏。现在不要惊慌,但要开始问你的系统管理员同事尖锐的问题。如果你不能很好地回答为什么这个二进制文件发生了变化,你可以寻找你的安装介质。

或者,您可能需要更新排除文件。但如果 /bin/sh 改变了,可能不会。

监控系统安全

所以,你认为你的服务器是安全的。也许是。目前。

不幸的是,有一类入侵者没有什么比跟上最新的安全漏洞并在他们认为可能易受攻击的系统上进行尝试更好的事情了。即使你认真阅读 FreeBSD-security 并应用了每一个补丁,总有一天你可能会被黑客攻击。虽然没有办法绝对确定你没有被黑客攻击,但以下提示将帮助你找出什么时候发生了什么:

我特别推荐 lsof 包,以增加您对系统的熟悉度。lsof 程序列出系统上所有打开的文件。阅读 lsof(8) 输出本身就是一种教育;你可能不知道你的web服务器打开了这么多垃圾。看到奇怪的文件打开,要么表明你对系统不够熟悉,要么表明有人做了不恰当的事情。

包安全

FreeBSD项目在ports和packages系统中提供了一个安全漏洞数据库。该数据库以漏洞和可扩展标记语言Vulnerability and eXposure Markup Language —— VuXML)提供。当有人自愿维护port时,他们也自愿注意该port的安全问题。

安装了 pkg(8) 的连接互联网的FreeBSD主机在 periodic(8) 运行期间下载最新的VuXML文件(见第21章),并将其存储在 /var/db/pkg/vuln.xml 中。然后,它将安装的软件包与该数据库进行比较。如果您的某个软件包存在漏洞,您将在每日状态电子邮件中收到通知。(你正在阅读你的日常状态电子邮件,对吗?)

如果您的软件包不安全,请按照第15章进行升级。

如果需要,您可以使用 pkg.conf 中的 VULNXML_SITE 选项设置其他位置来获取 vuln.xml 文件。如果您维护自己的包存储库和漏洞数据库,则可以这样做。

如果你被黑客攻击

毕竟,如果你的系统被黑客入侵了,你会怎么做?没有简单的答案。关于这个主题写了很多书。然而,这里有一些一般性的建议。

首先也是最重要的:被黑客攻击的系统是不可信的。如果有人在你的互联网服务器上获得了root访问权限,他本可以替换系统上的任何程序。即使你堵住了他突破的漏洞,他也可能安装了一个被黑客攻击的 login(8) 版本,每次登录时都会将你的用户名和密码发送到某个IRC频道。不要相信这个系统。升级无法清除它,因为即使是 freebsd-update(8) 和编译器也是可疑的。

虽然rootkit狩猎软件可以帮助您验证入侵者的存在,但没有什么可以验证入侵者不在那里。请随意写作FreeBSD-security@FreeBSD.org寻求建议。描述你看到了什么,以及为什么你认为自己被黑客攻击了。不过,请为丑陋的答案做好准备:从已知的安全介质完全重新安装计算机,并从备份中恢复数据。你读过第五章,对吧?

良好的安全实践可以减少你被黑客攻击的机会,就像安全驾驶可以减少你发生车祸的机会一样。最终,你无论如何都会全力以赴,想知道自己为什么要费心。祝你好运!