使用 GNU cfengine 自动化安全

作者:Kirk Bauer

许多年前,我有一个小小的顿悟,我相信你们中的许多人也经历过。我意识到维护 10 个系统比管理一台计算机需要更多的工作。但是,如果使用适当的工具和方法,它不必花费那么多额外的工作。

当您想要更改单个系统时,您只需决定要更改什么,然后四处摸索直到一切正常运行。三个月后,您甚至可能不记得您做了什么或为什么要这样做。这重要吗?通常不重要。

但是,当您必须对多个系统进行这些更改时,您真的想手动执行相同的任务多次吗?如果您曾经有 10 个系统,但现在有 11 个,您会记得对新来的系统进行所有相同的更改吗?也许新系统略有不同——或者也许您的所有系统都不相同。如果可以确切地知道您做了什么以及为什么要这样做,那不是很好吗?

这就是 Mark Burgess 开发的 GNU cfengine 发挥作用的地方。它允许您毫不费力地跨任意数量的不同系统影响更改。也许更重要的是,它提供了您所做工作的自动文档记录。您甚至可以使用一些注释来解释您为什么这样做。您的每个系统都成为一个或多个类别的成员,并且更改是按类别进行的。如果新系统到来,它会自动获取之前对其类别其他成员所做的更改。

一旦安装了 cfengine(来自 www.cfengine.org)并运行,对您的系统组进行更改几乎就像更改单个系统一样容易。这使您有更多时间来决定做什么以及如何做,这仍然是管理员的首要责任。

入门

与许多程序一样,使用 cfengine 最困难的部分是入门。因此,我们将建立一个基本环境,包含一个客户端和一个服务器。如果可能,您应该在几个测试系统上执行这些步骤,以便跟随示例进行操作。稍后,您可以扩展此框架以构建您自己的 cfengine 环境。

我认为您应该始终从最简单的实用配置开始新的冒险。在这种情况下,假设系统各自具有单个静态 IP 地址和正确的 DNS 条目(正向和反向)。设置过程可以总结如下

  • 1. 在主服务器上创建主配置文件(cfservd.conf、update.conf 和 cfagent.conf)。

  • 2. 在每个系统上创建公钥/私钥,并适当分发。

  • 3. 在至少服务器上启动 cfservd,并运行cfexecd在所有系统上。

配置服务器

让我们从主服务器开始。它应该具有目录 /usr/local/var/cfengine/inputs,其中包含主配置文件集。它必须运行 cfservd 守护进程,并且必须具有有效的 cfservd.conf 配置文件,如下所示


control:
   domain = ( mydomain.com )
   AllowUsers = ( root )
   cfrunCommand = ( "/var/cfagent/bin/cfagent" )

admit:
   /usr/local/var/cfengine/inputs *.mydomain.com
   /var/cfagent/bin/cfagent       *.mydomain.com


这个简单的配置文件除了允许来自 mydomain.com 的所有主机下载主配置文件集之外,几乎没有做其他事情。它还允许远程系统使用以下命令执行 cfagent 命令cfrun,这是一个有用的功能,您应该在一切启动并运行后进行探索。

配置分发

我们现在可以继续讨论 update.conf,当 cfagent 在任何系统上运行时,它会首先被处理和执行。此文件的主要功能是将主配置文件传输到本地系统。它必须保持简单可靠,因为此文件中的任何错误都必须在每个系统上手动修复。

列表 1. 示例 update.conf


control:
   actionsequence = ( copy tidy )
   domain         = ( mydomain.com )
   workdir        = ( /var/cfengine )
   policyhost     = ( server.mydomain.com )
   master_cfinput = 
                  ( /usr/local/var/cfengine/inputs )
   cf_install_dir = ( /usr/local/sbin )

copy:
   $(master_cfinput)     dest=$(workdir)/inputs
                         r=inf
                         mode=644
                         type=binary
                         exclude=*.lst
                         exclude=*~
                         exclude=#*
                         server=$(policyhost)

   $(cf_install_dir)/cfagent    
                         dest=$(workdir)/bin/cfagent
                         mode=755
                         type=checksum

   $(cf_install_dir)/cfservd    
                         dest=$(workdir)/bin/cfservd
                         mode=755
                         type=checksum

   $(cf_install_dir)/cfexecd    
                         dest=$(workdir)/bin/cfexecd
                         mode=755
                         type=checksum

tidy:
   $(workdir)/outputs pattern=* age=7



列表 1 中显示的文件应该是您在大多数环境中需要的所有内容;只需将 server.mydomain.com 替换为您的 cfengine 服务器的主机名。执行后,此 update.conf 文件会在 /var/cfengine/bin 中创建所需 cfengine 二进制文件的本地副本(来自 /usr/local/sbin,假设它是 NFS 文件系统或类似物)。

更重要的是,配置文件从运行 cfservd 的服务器复制到每个系统上的 /var/cfengine/inputs,包括服务器本身。配置文件与主文件进行逐位比较(type=binary),而二进制文件会在其校验和与主副本不匹配时更新。

最后,tidy 部分会删除超过七天的输出日志文件,以防止您的驱动器被填满。tidy 部分也可以在主 cfagent.conf 中使用,以清理系统上的各种其他文件。

主配置:cfagent.conf

列表 2. 示例 cfagent.conf



control:
   actionsequence  = ( processes editfiles )
   domain          = ( mydomain.com )
   access          = ( root )
   # Where cfexecd sends reports
   smtpserver      = ( mail.mydomain.com )
   sysadm          = ( root@mydomain.com )

processes:
   # Make sure these processes are always running
   "cfservd" restart "/var/cfengine/bin/cfservd"
   "cfexecd" restart "/var/cfengine/bin/cfexecd"

editfiles:
   # Make sure cfexecd runs hourly from cron too
   {  /etc/crontab
      AppendIfNoSuchLine 
         "0 * * * * root /usr/local/sbin/cfexecd -F"
   }


您的 cfengine 安装的核心位于 cfengine.conf 中。现在,让我们使用一个基本的文件,让 cfengine 保持运行;实际的增强安全性的代码将在本文后面添加。此基本文件如列表 2 所示。control 部分是必需的,并设置各种全局参数。其中大多数都是不言自明的。例外情况是 access,它指定哪些用户可以运行 cfagent,以及 actionsequence 项,它定义要执行哪些部分以及按什么顺序执行。当您添加部分时,如果您希望它们产生任何影响,也必须将它们添加到此列表中。

在这种情况下,要执行的第一个部分是 processes。此部分所做的只是检查以确保 cfservd(在服务器上是强制性的,但在所有系统上都很有用)和 cfexecd 正在运行。如果进程未运行,则会通过执行指定的命令重新启动它。

默认情况下,cfexecd 守护进程每小时执行一次 cfagent。cfagent 反过来处理和执行所有这些配置文件。但是,如果 cfexecd 没有运行以启动整个序列,cfagent 如何能够运行并启动 cfexecd 呢?

好吧,这可以通过手动运行 cfagent 来实现,例如第一次配置系统时。您还可以让 cfexecd 从系统 cron 调度程序定期执行。这样,如果一种方法失败,另一种方法仍然应该有效并修复问题。editfiles 部分完全做到这一点——它通过在 /etc/crontab 中添加条目(如果不存在此类条目)来确保每天从 cron 运行一次 cfexecd。此文件的格式和位置在您的系统上可能有所不同,因此请进行相应的更改。您可能还需要在 processes 部分中添加一个条目,以确保 cron 守护进程正在运行。如果您有混合类型的主机,您可能必须使用类在不同的系统上进行不同的操作,如本文后面所述。

生成密钥

我们已经完成了基本配置文件,现在让我们继续讨论网络通信。cfengine 中的所有网络通信都为每个主机使用一对公钥和私钥。这有助于确保一个系统正在与其认为的远程系统进行通信。这意味着您需要通过运行 cfkey 命令为每个主机生成密钥对。这将在 /var/cfengine/ppkeys/ 目录中创建文件 localhost.pub 和 localhost.priv。然后,您需要将主服务器的公钥放在此目录中,命名为 root-10.1.1.1.pub,假设这是运行 cfservd 的服务器的 IP 地址。最后,您需要将每个客户端系统的公钥复制到主服务器上,例如命名为 root-10.2.2.2.pub。

执行 cfengine

现在 cfexecd 每小时为您执行 cfagent。您也可以随时在任何机器上手动运行 cfagent。此命令从服务器更新配置文件(如 update.conf 中定义)。它还检查并更改系统(如 cfagent.conf 中定义)。为了测试目的,您可以执行 cfagent --dry-run 以查看 cfagent 将采取的操作。在测试和调试时,您可能还需要添加行IfElapsed = ( 0 )在 cfagent.conf 的 control 部分中。这会禁用拒绝服务预防功能,该功能限制了操作的频率。

cfengine 的安全性

您可能会问,这一切与安全性有什么关系?好吧,现在您几乎可以通过修改主 cfagent.conf,然后等待机器从配置服务器拉取并执行此文件,从而对您的任何系统进行任何更改。即使机器尚不存在,或者机器是已断开连接一个月的笔记本电脑,每个系统也会尽快获取每个适当的更改。

那么,您可以对系统进行哪些更改以提高安全性?首先,cfengine 可以自动报告某些可疑文件和目录。以下任何或所有条目都可以添加到 cfagent.conf 的 control 部分

  • NonAlphaNumFiles = ( on ): 使 cfengine 报告并禁用(使用 .cf-nonalpha 扩展名重命名并仅使 root 可读)任何仅包含非字母数字字符的文件。

  • FileExtensions = ( o a c gif ... ): 提供常规文件扩展名的列表;如果找到具有这些扩展名的目录,cfengine 会报告它们。

  • SuspiciousNames = ( lrk3 lkr3 ): cfengine 应该警告用户的可疑文件名列表。您可以将其用于各种目的,例如列出属于已知 rootkit 的文件。

这些指令仅适用于 cfengine 出于其他原因扫描的文件和目录,例如 files、tidy 或 copy 部分。除非您指示它这样做,否则 cfengine 不会扫描您的整个文件系统以查找这些文件。

禁用网络服务

您还可以使用 cfengine 禁用您不希望在系统上运行的某些网络服务。例如,让我们禁用所有系统上的 rshd 和 rlogind 服务,以及与这些服务相关的任何文件


editfiles:
   { /etc/inetd.conf
      HashCommentLinesContaining "rshd"
      HashCommentLinesContaining "rlogind"
      DefineClasses "modified_inetd"
   }

processes:
   modified_inetd::
      "inetd" signal=hup

disable:
   /root/.rhosts
   /etc/hosts.equiv


上面的代码在添加到主 cfagent.conf 时,并在 actionsequence 列表扩展后,在标准 Linux 系统上完成了此任务。首先,/etc/inetd.conf 中包含字符串 rshd 或 rlogind 的任何行都被注释掉。如果发生这种情况,则定义类 modified_inetd。然后,当 processes 部分执行并且类被定义时,HUP 信号被发送到 inetd 进程,以使其重新加载其配置。

最后,我们告诉 cfengine 禁用文件 /root/.rhosts 和 /etc/hosts.equiv(如果它们存在)。它们对于除 root 之外的任何人都是不可读的,并使用 .cfdisabled 扩展名重命名。您也可以在用户的主目录中禁用此文件,但这需要更多超出本文范围的设置任务。

文件系统权限

您可以使用 cfengine 检查和修复重要系统文件的权限



files:
   /etc/passwd mode=644 owner=root group=root
      action=fixall
   /etc/shadow mode=600 owner=root group=root
      action=fixall
   /etc/group  mode=644 owner=root group=root
      action=fixall

directories:
   /etc mode=0755 owner=root group=root inform=true
   /tmp mode=1777 owner=root group=root inform=true


在本例中,我们关注某些系统帐户和密码文件。我们指定所需的所有权和权限,并指示 cfengine 修复它可能发现的任何问题 (action=fixall)。我们还列出了一些重要的目录,如果它们不存在,则应该创建这些目录,并且应该适当地设置其权限。

监控文件校验和

您可能熟悉 Tripwire 程序,这是一个出色的安全工具,用于监控系统上的文件是否有不需要的更改。您可以使用 cfengine 进行类似的文件监控。虽然不如 Tripwire 使用的方法安全,但如果您已经将 cfengine 用于其他任务,则此方法既简单又方便。

至少,我喜欢监控重要的系统文件,特别是 setuid root 文件,以检查其是否具有正确的权限和 md5 校验和


files:
   /bin/mount mode=4755 owner=root group=root
      action=fixall checksum=md5
   /bin/mount mode=4755 owner=root group=root
      action=fixall checksum=md5


我在示例中仅列出了两个文件,但您显然可以列出更多文件。与之前的示例一样,文件的权限会进行检查并在必要时修复。但是,此外,还会将每个文件的 md5 校验和与其先前存储的值进行比较,并报告任何异常情况。当 control 部分中存在 ChecksumUpdates = ( on ) 行时,Cfengine 仅更新存储的校验和。

监视 setuid Root 文件

您还可以让 cfengine 查找并消除不应设置 setuid 位的文件的 setuid 位。在列表 3 中,我们在 files 部分中有一个条目,它会遍历整个文件系统,并关闭 root 拥有的任何文件(但未明确列为要忽略的文件)的 setuid 位。在执行此代码之前,您显然应该扩展要忽略的文件列表,因为您的系统上可能还有许多其他 setuid root 文件。您可以通过运行命令确定当前系统上哪些文件是 setuid root 文件find / -perm +4000 -user root.

列表 3:消除不需要的 setuid Root 文件



filters:
   { root_owned_files
      Owner:     "root"
      Result:    "Owner"
   }

files:
   /
      filter=root_owned_files
      mode=u-s     # no SUID bit may be set
      recurse=inf
      action=fixall
      inform=true
      # And the full paths of files that *should* be SUID
      ignore=/proc
      ignore=/bin/ping
      ignore=/usr/bin/su
      ignore=/usr/bin/passwd
      ignore=/usr/bin/at
      ignore=/usr/bin/crontab
      ignore=/usr/bin/rsh
      ignore=/usr/sbin/traceroute
      ...


使用类

cfengine 的一个强大功能(我们在这些示例中没有过多使用)是它对类的支持。每个系统都会自动成为许多类的一部分,并且可以以多种方式定义自定义类——当执行特定操作时,或基于在系统上找到的某些文件。然后,您只能在特定类的系统上执行命令集。为我的系统自动定义的一些类的示例有:redhat、linux、redhat_7_2 和 www_kaybee_org。还有一些类是根据当前系统时间定义的。列表 4 显示了如何使用这些类的一些示例;我相信您可以为此功能找到更多用途。

列表 4. 类示例



classes:
   # Assume systems with this file are web servers
   web_server = ( 
      '/usr/bin/test -f /etc/httpd/conf/httpd.conf' 
   )

files:
   web_server::
      # Fix permissions on web server config files
      /etc/httpd/conf owner=root group=root
         mode=0644 action=fixall recurse=inf

   solaris::
      # On all Solaris systems
      /etc/inet/inetd.conf owner=root group=root
         mode=0644 action=fixall

   linux.!(redhat_7|redhat_8|redhat_9)::
      # On all Linux systems not running Red Hat
      # versions 7.X, 8.X, or 9.X.
      /etc/inetd.conf owner=root group=root
         mode=0644 action=fixall

   redhat_7|redhat_8|redhat_9::
      # On Red Hat Linux 7.X, 8.X, or 9.X
      /etc/xinetd.conf owner=root group=root
         mode=0644 action=fixall

   any::
      # This file exists on all systems
      /etc/passwd mode=644 owner=root group=root
         action=fixall

tidy:
   Sunday::
      # Clean out files more than 14 days old
      # in /tmp on Sunday only
      /tmp recurse=inf age=14 rmdirs=sub


结论

GNU cfengine 不仅仅是一个系统安全工具。它可以用于分发文件、维护您的系统以及自动执行任何系统上的几乎任何配置任务。它在各种平台上运行,并且一旦您入门,就非常容易使用。我已经向您展示了一些 cfengine 可以为您完成的事情的示例。我希望您认为 cfengine 可以增强您系统的安全性,并使用这个出色的工具来自动化您的许多其他系统管理任务。

Kirk Bauer 是 AutoRPM 和 Logwatch 的创建者,也是 Automating UNIX Administration 的作者。在他的业余时间,他跳出飞机,迄今为止已跳伞超过 1,400 次。

加载 Disqus 评论