如何编写 Ansible 的安全集成模块

Ansible 自动化为信息安全行业提供了巨大的潜力。 了解如何在 AnsibleFest 2019 演讲的总结中利用它。
155 位读者喜欢这个。
Security monster

Ansible 是一个非常简单的 IT 自动化平台,可以更轻松地部署应用程序和系统。 它允许您避免编写脚本或自定义代码来部署和更新您的应用程序、系统以及各种类型的网络连接设备。 Ansible 允许您以接近纯英语的语言进行自动化,无需在远程系统上安装任何代理,并使用基于设备类型的本机协议,例如用于 Unix 风格操作系统的 SSH、用于 Windows 系统的 WinRM、用于 REST API 设备的 REST API (httpapi) 等等。

背景

AnsibleFest 2019 上,我的同事 Sumit Jaiswal 和我做了一个题为“Ansible 开发深入探讨:如何编写 Ansible 的安全集成模块和集合”的演讲,关于我们一直在做的事情。 本文回顾了我们演讲的要点; 我希望它能突出 Ansible 在信息安全自动化领域所具有的潜力和能力。

很多事情始于我的同事 Massimo Ferrari 的声明,即“Ansible 自动化可以成为集成和编排分布在不同领域的许多安全平台的通用语言”。 我们花了很多时间仔细考虑这一点; 对于已经发现 Ansible 强大功能的 DevOps 和自动化专业人士来说,这可能是显而易见的,但信息安全行业没有类似的工具以相同的方式解决行业问题。 因此,法拉利的声明为信息安全行业提供了巨大的潜力。

在本文中,我将总结我们在 AnsbileFest 2019 演讲中强调的两个要点

  • Ansible 推荐的开发实践
  • 对您要集成的对象及其连接方式进行分类(API 还是 CLI?)

Ansible 工程团队不喜欢写“最佳实践”这个词,因为我们不可能知道什么最适合您的特定情况。 您是主题方面的专家,因为它们适用于您独特的环境和要求。 但是,我们可以提供建议,我将从 Ansible 模块开发人员的角度来概述这些建议。

模块

模块以用户为中心且是自包含的。 这主要意味着包含在您的模块中的代码应该是自包含在您的 模块中或 module_util 中。 后者允许我们在模块之间共享代码,但一切都必须尽可能地自包含,因为我们不想引入过多的外部依赖项。 我们还希望每个模块都执行某种状态管理。 每个模块都应该是 幂等的,这基本上意味着“如果需要则进行更改,否则,不进行更改”。 模块不应尝试包含工作流程(这是 playbook 的作用),并且我们希望将其留给用户。 模块不应尝试“做太多”,例如您有一个包含 100 个参数的庞大模块,并且根据用户提供的值,对目标设备执行截然不同的操作。

我们通常想要避免的一个例子可能是这样的

- name: Create a virtual machine
  some_module:
    thing_to_do: "create_virtual_machine"
    name: "bobs_awesome_vm"
    storage_size: 100G
    ram: 24G
    vcpus: 4

- name: Create a virtual storage volume
  some_module:
    thing_to_do: "create_virtual_storage_vol"
    name: "bobs_awesome_storage"
    storage_size: 1000G
    lun_id: 12

在此示例中,虚构的 some_module 正在根据 thing_to_do 的值执行完全不相干的操作。 从 Ansible 模块的角度来看,这不是一个离散的、自包含的工作单元。 这些应该是两个单独的模块,它们甚至可以通过自定义 module_util 在后端共享代码(如果这能让开发人员的生活更轻松)。 无论哪种方式,它们都应该是单独的模块,以便用户可以轻松地定义、阅读和理解编写的任务。 作为开发人员,您希望使模块的交互以用户为中心。

以用户为中心的另一个方面是,用户无需了解目标 API 即可有效地使用该模块。 该模块应提供有用的默认值、文档和示例,以允许用户选择自己的自动化路径。

集合

Ansible 集合是一个相对较新的概念,但它们通常被视为各种形状和大小的 Ansible 内容的未来。 它们允许 Ansible 内容(例如模块、module_utils、各种插件、角色、文档、测试、playbook 以及社区下一步想到的任何内容)作为统一的单元存在,以便作为一个实体进行测试、验证和分发。 更重要的是(这也是它对开发人员的真正优势),它将内容与 Ansible Core 运行时 解耦。 这允许 Ansible 内容与 Ansible 本身分开进行生命周期管理,这意味着它可以根据内容作者或维护者的意愿经常或不经常发布。 新功能不再需要等待六个月才能发布下一个 Ansible 版本。 集合作者可以根据需要经常发布。

集合旨在简单地过渡到一个勇敢的新世界,其中 Ansible Core 执行引擎在象征意义上类似于 CPython。 Ansible 集合在象征意义上类似于在 PyPI 上找到的 Python 模块。 Ansible Galaxy 在象征意义上类似于 PyPI,是事实上的分发机制。

从开发人员的角度来看,您只需要将文件放到正确的位置并更新任何自定义 module_utils Python 导入路径。 从用户的角度来看,您只需要将 collection 命名空间和名称添加到 playblock 中,该 play 或 block 旨在是使用该内容。

对您要集成的对象及其连接方式进行分类

在安全领域,旨在像设备一样使用的设备设备或软件(网络设备、嵌入式系统等)有时会向管理员提供 应用程序编程接口 (API) 和 命令行界面 (CLI)。 作为模块开发人员,您必须做出一些决定,以服务于易于开发、代码可维护性以及最终一致的用户体验。

CLI

如果您可能要包装 CLI,请问自己该 CLI 是否提供一致的界面,以及您可以合理且一致地解析的输出。 除此之外,CLI 是否提供制定幂等事务的能力? 虽然大多数 CLI 都提供 getset 类型的事务(尤其是在 Unix/Linux 系统上),但有些 CLI 不提供,这是模块作者需要考虑的事情。

当考虑使用具有标准 CLI 但不提供传统 Unix shell 的网络或嵌入式设备进行 CLI 实现时,您应该研究实现 cliconf 插件。 这种类型的插件使您的用户能够以对经验丰富的 Ansible 用户和初学者都自然的方式与设备或嵌入式设备进行交互。 或者,如果您发现自己拥有的设备允许您执行本地 Python 代码(local 到设备或系统本身;Ansible 术语中的“受管主机”),那么请考虑 run_command module_util。 后一种情况实际上只是一个传统的 模块开发 工作流程,就像传统的 GNU/Linux 发行版一样。

API

如果您尝试集成的技术提供 API,请确定该 API 是本地系统 API(本地到远程“受管主机”系统)还是远程 API,例如 REST API?

如果您发现自己拥有本地 Python API 并且使用它而不是 REST API(如果两者都可用)是有利的,那么这种情况实际上与 GNU/Linux 发行版中的传统 模块开发 工作流程相同。

但是,如果唯一的选择是 REST API,或者如果确定可用的 REST API 是最佳选择,那么编写 httpapi 连接插件 最适合一般易于实现、维护和处理 AuthN、AuthZ、会话等事项。 它还为与这些类型的设备进行通信提供了一种惯用的模式,即使它们的通信方式与 Ansible 使用的大多数其他设备有很大不同。

一个说明这一点的例子对于任何使用不提供 httpapi 连接插件的模块自动化 Web 服务的人来说可能很常见。 通常,在这些场景中,play、block 或 task 必须针对 localhost 运行,并且 Web 服务连接的各种信息必须传递到每个 task 的模块的每个调用中。

---
- name: talk to foo device
  hosts: localhost
  tasks:
    - name: do something
      foo_device_do_thing:
        url: foo.example.com
        username: "{{ foo_device_username }}"
        passwd: "{{ foo_device_password }}"
        validate_certs: true
        thing_state: present
        some_param: bar

如果此模块已针对 httpapi 连接插件实现,那么各种连接特定的参数将是主机变量或组变量,并且不必在 playbook 的任务级别随身携带。

这是一个清单条目,用于处理所有 Ansible 模块的 AuthN/AuthZ 连接,该条目是针对 httpapi 连接插件编写的。 它还执行会话处理以提高性能

[foo_devices]
foo.example.com

[foo_devices:vars]
ansible_network_os=foo_device
ansible_user=foo_device_username
ansible_httpapi_pass=foo_device_password
ansible_httpapi_validate_certs=true

这个 playbook 会更加惯用。 foo_devices 是一种一流的设备类型和 主机模式,适用于 playbook。

---
- name: talk to foo device
  hosts: foo_devices
  tasks:
    - name: do something
      foo_device_do_thing:
        thing_state: present
        some_param: bar

Playbook 必须为每个任务定义信息,所以想象一下一个有 20 个或 100 个任务的 playbook。开销会非常大。这感觉不太像是直接自动化在 host 字段中定义的主机。然而,httpapi 连接插件消除了重复定义连接信息的需要,并且它还通过 REST API 与设备进行本地通信,就像你在 playbook 中通过 SSH 在 Linux 系统上进行操作一样。

关于 httpapi 连接插件需要注意的是,即使使用者定义了主机、组、主机变量和组变量,就像在传统的 Unix/Linux 或 Windows 管理的主机中一样,这些模块实际上是在 localhost(Ansible 术语中的“控制主机”)上执行的。在开发时,要记住这一点。

这是什么?

如果你是 Ansible 模块开发的新手,这可能看起来很多,难以一下子理解。公平地说,确实如此。然而,随着你对针对各种设备类型和技术解决方案分类的 Ansible 模块开发的细节越来越熟悉,不同开发策略的动机就开始变得有意义了。某些设备分类具有特殊性,这种模型可以帮助 Ansible 开发者和用户以一致且可预测的方式处理这些特殊性。

总结

如果您对 Ansible 模块开发模型有疑问,请随时通过充满活力的 Ansible 社区联系,更具体地说,可以联系 Ansible 安全自动化工作组

接下来要读什么
标签
User profile image.
Adam Miller 是 Red Hat Ansible 工程团队的成员,专注于 Ansible Core 运行时、Linux 系统管理自动化、容器编排集成和信息安全自动化。 Adam 拥有 Sam Houston State University 的计算机科学学士学位和信息保证与安全硕士学位。

1 条评论

nich bro

© . All rights reserved.