如何在配置管理系统中自动添加新的 Droplet

简介

利用DigitalOcean元数据服务,管理员可以提供指令,让新服务器自动进行配置。 虽然这很有用,但许多企业还是喜欢在Chef或Puppet等配置管理工具中处理所有基础设施配置。

在本指南中,我们将演示如何使用元数据服务和CloudInit启动DigitalOcean服务器,以连接到现有的配置管理部署。 然后,服务器的实际配置可由配置管理服务处理。 我们将演示如何引导 Chef 和 Puppet 节点。

先决条件

为了完成本指南,你必须对DigitalOcean元数据服务有一定的了解。 你可以在本指南中找到更多关于如何在元数据服务中输入信息和检索信息的信息。

本指南将利用一种名为 "cloud-config "的脚本,在首次启动时由 Droplet 上的 CloudInit 服务使用,以执行首次运行配置。 您应该对 cloud-config 脚本、其语法和行为有一些基本的了解,以便更好地理解如何修改本指南中介绍的脚本。 您可以在 此处 找到有关 cloud-config 脚本的介绍。 如需了解更实用的示例(以及关于格式限制的讨论),请阅读我们关于使用 cloud-config 执行一些基本任务的指南 此处

使用云配置脚本引导 Chef 节点

使用DigitalOcean元数据服务,你可以通过 "cloud-config "脚本轻松地将新服务器连接到现有的由Chef控制的基础设施中。

要将新服务器添加到此系统中,你必须已经配置了一个 Chef 服务器,新服务器可以通过它来接收配置说明。 如果你在部署 Chef 服务器和管理工作站时需要帮助,可以按照 本指南 开始操作。

总体规划

当新服务器上线时,必须将其置于 Chef 服务器的控制之下。 通常,这可以通过使用 knife 管理命令连接到新服务器并使用 bootstrap 子命令来实现。 这将连接到新服务器,安装 Chef 客户端和验证凭据,允许新节点连接到 Chef 服务器。 之后,Chef 客户端会连接到服务器、验证自身、接收新的客户端凭据、从服务器提取配置,并执行任何必要的操作,使自身进入所需的状态。

在本指南中,我们将使用 "cloud-config "脚本来替代手动启动步骤,让新节点自动连接到 Chef 服务器、验证自身、接收客户端凭据并执行初始 Chef 客户端运行。 服务器将在首次启动时自动执行这些操作,无需管理员提供任何手动协助。

从刀片配置文件中收集必要数据

为了让我们的 "cloud-config "脚本成功启动,它需要访问 "knife "命令通常可用的凭据。 具体来说,我们需要以下信息:

  • 厨师验证名称
  • 验证密钥
  • 可以访问 Chef 服务器的 URL

所有这些信息都可以在用于管理 Chef 基础架构的工作站上的 knife 配置文件中以正确的格式找到。 在 Chef 软件仓库中,应该有一个名为 .chef的隐藏目录,其中包含该文件。

假设你的 Chef repo 位于工作站的主目录中,且名为 chef-repo,你可以键入以下内容输出该文件的内容:

1cat ~/chef-repo/.chef/knife.rb

下面重点介绍了您需要的信息:

 1current_dir = File.dirname(__FILE__)
 2log_level                :info
 3log_location STDOUT
 4node_name                "jellingwood"
 5client_key               "#{current_dir}/jellingwood.pem"
 6validation_client_name   "digitalocean-validator"
 7validation_key           "#{current_dir}/digitalocean-validator.pem"
 8chef_server_url          "https://your_server.com/organizations/digitalocean"
 9syntax_check_cache_path  "#{ENV['HOME']}/.chef/syntaxcache"
10cookbook_path            ["#{current_dir}/../cookbooks"]

验证名称和 Chef 服务器 URL 可直接从文件中获取。 复制这些值,以便在 cloud-config 文件中使用。

validation_key "指向保存实际密钥的位置。 在上述示例中,它与 knife.rb 文件位于同一目录,名为 digitalocean-validator.pem。 这可能与你的配置不同。

我们需要该文件的内容,因此再次使用 cat 命令。 修改命令,将其指向验证密钥的指定位置:

1cat ~/chef-repo/.chef/digitalocean-validator.pem

你会看到一个 RSA 私钥:

 1-----BEGIN RSA PRIVATE KEY-----
 2MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
 3V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
 4vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU
 5
 6. . .
 7
 8sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
 9Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
10eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
11-----END RSA PRIVATE KEY-----

复制验证密钥的全部内容,以便在 cloud-config 脚本中使用。

基本 Cloud-Config Chef 客户端安装

获得上述数据后,就可以创建脚本了。 Chef 配置可通过名为 chef 的专用 cloud-config 模块完成。 cloud-config 必须包含有效的 YAML,并且必须将 # cloud-config 作为脚本的第一行。

开始时,您的脚本将看起来像这样:

1#cloud-config
2chef:

cloud-config "文档声称可以通过 Ruby gem、软件包或传统的 "总括 "安装方法安装 Chef 客户端。 但在实践中,gem 和软件包方法往往都会失败,因此我们将使用 "omnibus "方法。 虽然通常没有必要,但我们也会明确列出总括安装程序的位置。

我们将把 force_install 设置为 "false"。 这样,如果由于某种原因,Chef 客户端已经安装在映像上(例如,从快照中部署),客户端将不会被重新安装。 到目前为止,我们的脚本看起来是这样的

1#cloud-config
2chef:
3  install_type: "omnibus"
4  omnibus_url: "https://www.opscode.com/chef/install.sh"
5  force_install: false

接下来,我们可以使用 node_name 指令为 Chef 基础架构中的新服务器选择一个名称。 如果不设置,Chef 将使用服务器的主机名,因此这是可选项。 不过,这个名称在 Chef 环境中必须是唯一的。

之后,我们可以添加从 Chef 工作站获取的所有连接信息。 我们将把 server_url 选项设置为与 knife.rb 文件中一模一样的 Chef 服务器位置。 validation_name 选项也是如此。

对于验证密钥,我们将使用 YAML 管道符号 (|) 输入在工作站上找到的整个验证密钥:

 1#cloud-config
 2chef:
 3  install_type: "omnibus"
 4  omnibus_url: "https://www.opscode.com/chef/install.sh"
 5  force_install: false
 6  node_name: "new_node"
 7  server_url: "https://your_server.com/organizations/digitalocean"
 8  validation_name: "digitalocean-validator"
 9  validation_key: |
10    -----BEGIN RSA PRIVATE KEY-----
11    MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
12    V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
13    vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU
14
15    . . .
16
17    sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
18    Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
19    eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
20    -----END RSA PRIVATE KEY-----

至此,你的脚本就拥有了连接 Chef 服务器和创建客户端凭证所需的所有身份验证。

配置 Chef 环境、运行列表和属性

虽然上述详细信息为客户端连接到 Chef 服务器提供了足够的信息,但我们还没有为节点提供任何关于如何实际配置自身的信息。 我们也可以在 cloud-config 脚本中提供这些信息。

要指定新节点应置于的环境,请使用 environment 选项。 如果未设置该选项,则将设置 _default(默认)环境,这是没有其他环境的 Chef 节点的通用默认环境。

1chef:
2  environment: "staging"

我们的 run_list 可以指定为客户应按顺序应用的项目的简单列表。 这些项目可以是配方,也可以是角色。

1chef:
2  run_list:
3    - "recipe[lamp]"
4    - "role[backend-web]"

您可以使用 initial_attributes 层次结构指定新节点的初始属性。 这将设置影响 run_list 应用方式的初始属性:

1chef:
2  initial_attributes:
3    lamp:
4      apache:
5        port: 80
6      mysql:
7        username: webclient
8        pass: $#fjeaiop34S

当连接到之前的 cloud-config 脚本时,它可能看起来像这样:

 1#cloud-config
 2chef:
 3  install_type: "omnibus"
 4  omnibus_url: "https://www.opscode.com/chef/install.sh"
 5  force_install: false
 6  node_name: "new_node"
 7  server_url: "https://your_server.com/organizations/digitalocean"
 8  validation_name: "digitalocean-validator"
 9  validation_key: |
10    -----BEGIN RSA PRIVATE KEY-----
11    MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
12    V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
13    vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU
14
15    . . .
16
17    sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
18    Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
19    eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
20    -----END RSA PRIVATE KEY-----
21  environment: "staging"
22  run_list:
23    - "recipe[lamp]"
24    - "role[backend-web]"
25  initial_attributes:
26    lamp:
27      apache:
28        port: 80
29      mysql:
30        username: webclient
31        pass: $#fjeaiop34S

重定向输出和配置 Chef 客户端运行

上述脚本包含了 "chef: "部分下所需的全部信息。 不过,我们还需要使用其他一些 cloud-config 模块来完成一些其他工作。

首先,我们应指定将每条命令和子命令的输出重定向到 CloudInit 进程的输出日志中。 默认情况下,输出日志位于 /var/log/cloud-init-output.log。 我们可以使用 output 模块这样做:

1output: {all: '| tee -a /var/log/cloud-init-output.log'}

我们要做的另一件事是在安装和配置好 Chef 客户端后,将其设置为实际运行。 在撰写本文时,总括安装方法不会自动完成这项工作。

我们可以等到服务器上安装了 chef-client 可执行文件后再调用该命令,从而强制执行该行为。 我们将使用一个简单的 bash 循环,每五秒检查一次该文件是否存在。 找到该文件后,我们将运行 chef-client,以执行我们指定的初始配置。

runcmd "模块可用于发布任意命令。 它是我们的 bash 循环的理想位置:

1runcmd:
2  - while [ ! -e /usr/bin/chef-client ]; do sleep 5; done; chef-client

此外,您还可以选择添加另一条 cloud-config 指令,以便在首次启动后对元数据端点进行空路由。 这很有用,因为我们在用户数据中放入了私钥。 如果不空路由元数据端点,服务器上的任何用户都可以访问这些数据。 通过添加

1disable_ec2_metadata: true

将这些脚本与我们迄今为止构建的脚本相结合,我们就能得到启动节点并将其连接到 Chef 基础架构所需的完整脚本:

 1#cloud-config
 2chef:
 3  install_type: "omnibus"
 4  omnibus_url: "https://www.opscode.com/chef/install.sh"
 5  force_install: false
 6  node_name: "new_node"
 7  server_url: "https://your_server.com/organizations/digitalocean"
 8  validation_name: "digitalocean-validator"
 9  validation_key: |
10    -----BEGIN RSA PRIVATE KEY-----
11    MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
12    V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
13    vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU
14
15    . . .
16
17    sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
18    Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
19    eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
20    -----END RSA PRIVATE KEY-----
21  environment: "staging"
22  run_list:
23    - "recipe[lamp]"
24    - "role[backend-web]"
25  initial_attributes:
26    lamp:
27      apache:
28        port: 80
29      mysql:
30        username: webclient
31        pass: $#fjeaiop34S
32output: {all: '| tee -a /var/log/cloud-init-output.log'}
33runcmd:
34  - while [ ! -e /usr/bin/chef-client ]; do sleep 5; done; chef-client
35disable_ec2_metadata: true

上述脚本可根据需要进行调整,以适用于基础设施中的每台新服务器。

使用 Cloud-Config 脚本引导 Puppet 节点

如果您的基础架构依赖 Puppet 进行配置管理,您可以使用 puppet 模块来代替。 与 Chef 的例子一样,启动 Puppet 节点需要使用 cloud-config 将新服务器连接到现有的配置管理基础架构。

在开始之前,你应该为你的基础架构配置一个 Puppet 主服务器。 如果需要帮助来启动和运行 Puppet 服务器,请查看 本指南

总体规划

当新的 Puppet 服务器上线时,会安装一个 Puppet 代理,以便与 Puppet 主服务器通信。 该代理负责接收和应用决定节点所需状态的信息。 为此,代理与主服务器连接,上传有关自身的数据,下拉描述其所需状态的当前目录,并执行达到该状态所需的操作。

在此之前,代理必须在首次运行时向主服务器注册。 它会创建一个证书签名请求,并发送给主服务器进行签名。 通常情况下,代理会定期重新连接主服务器,直到证书被签署,但如果你的环境适合,你可以配置 Puppet 自动签署具有某些特征的传入请求(我们将在后面介绍)。

使用我们的 cloud-config 脚本,我们将为新服务器配置它首次连接主服务器所需的信息。 此时,它就能以目录的形式从 Puppet 主服务器获取配置详细信息。

从傀儡主控器收集必要数据

在构建 "cloud-config "文件之前,我们需要做的第一件事是从 Puppet 主服务器收集我们需要连接的数据。 我们只需要几条信息。

首先,您需要获取 Puppet 主服务器的完全合格域名 (FQDN)。 键入

1hostname -f

在大多数情况下,它应该返回类似这样的信息:

1puppet.example.com

还可以检查 Puppet 主配置文件,查看是否设置了 dns_alt_names 选项:

1cat /etc/puppet/puppet.conf
1. . .
2
3dns_alt_names = puppet,puppet.example.com
4
5. . .

如果 Puppet 主站的 SSL 证书是在设置这些选项后生成的,那么它们也可能可用。

我们需要收集的另一项内容是 Puppet 主站的证书授权证书。 可在 /var/lib/puppet/ssl/certs/ca.pem/var/lib/puppet/ssl/ca/ca_crt.pem 中找到:

1sudo cat /var/lib/puppet/ssl/certs/ca.pem

结果会是这样的

 1-----BEGIN CERTIFICATE-----
 2MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
 3ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
 4GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC
 5
 6. . .
 7
 8arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
 9rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
10l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
11UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
12-----END CERTIFICATE-----

完整复制证书。 我们将在 "cloud-config "文件中包含该证书,这样我们的新服务器就能验证它们是否连接到了正确的 Puppet 主服务器。

获得这些信息后,就可以开始构建 "cloud-config "文件,这样新服务器就能插入现有的 Puppet 基础架构。

基本云配置 Puppet 节点安装

新 Puppet 节点的 cloud-config 配置相当简单。 所有 Puppet 特有的配置都位于文件的 puppet: 部分。 与每个 cloud-config 文件一样,第一行必须单独包含 # cloud-config

1#cloud-config
2puppet:

下面只有两个子部分。 第一个是 "ca_cert "键。 它将使用管道字符来开始一个 YAML 文本块,这样 CA 证书就可以作为一个缩进块完整地给出:

 1#cloud-config
 2puppet:
 3  ca_cert: |
 4    -----BEGIN CERTIFICATE-----
 5    MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
 6    ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
 7    GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC
 8
 9    . . .
10
11    arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
12    rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
13    l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
14    UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
15    -----END CERTIFICATE-----

请务必包含整个证书以及开头和结尾标记,并适当缩进。

puppet: "下的第二个部分是 "conf: "部分。 该部分用于指定键值对,这些键值对将附加到通用的 puppet.conf 文件中。 键值对应像在 puppet.conf 文件中一样,放在章节标题下。

例如,新服务器至少需要知道 Puppet 主服务器的地址。 在 puppet.conf 文件中,这可以在 [代理]部分找到,如下所示:

1. . .
2
3[agent]
4server = puppet.example.com
5
6. . .

要在 "云配置 "语法中指定这一点,您需要在目前的基础上添加以下内容:

 1#cloud-config
 2puppet:
 3  ca_cert: |
 4    -----BEGIN CERTIFICATE-----
 5    MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
 6    ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
 7    GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC
 8
 9    . . .
10
11    arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
12    rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
13    l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
14    UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
15    -----END CERTIFICATE-----
16  conf:
17    agent:
18      server: "puppet.example.com"

请注意,"conf: "部分与 "ca_cert "部分并列,而不是一个子元素。 这是连接 Puppet 主站所需的最基本配置。 在 puppet.conf 中找到的任何其他配置项都可以用类似的方式添加,首先为部分名称创建一个级别,然后定义键值对。

之后,我们应将所有未来的输出重定向到 cloud-init-output.log 文件,并添加一行 runcmd 行,与我们为 Chef 配置添加的行类似。 这会等到 Puppet 代理安装完毕,然后启用并重启它。 我们还可以像在 Chef 部分所做的那样,在首次运行后空路由元数据端点。 这些 "cloud-config "指令应放在任何其他模块部分之外:

1. . .
2
3  conf:
4    agent:
5      server: "puppet.example.com"
6output: {all: '| tee -a /var/log/cloud-init-output.log'}
7runcmd:
8  - while [ ! -e /usr/bin/puppet ]; do sleep 5; done; puppet agent --enable; service puppet restart
9disable_ec2_metadata: true

有了这些信息,新服务器就能连接到 Puppet 主服务器,然后生成客户端证书签名请求,传输给主服务器。 默认情况下,客户端证书必须在 Puppet 主服务器上手动签名。 一旦完成,在下一次 Puppet 代理更新间隔(默认情况下每 30 分钟一次)时,节点将从 Puppet 主服务器提取其配置。 我们稍后将演示如何实施相对安全的自动签名机制,以避免这种延迟。

为节点定义证书名

可放入新服务器puppet.conf文件的值之一是唯一案例。 在 cloud-config 文件中,如果给出某些变量,certname 选项可以替代环境中的值。 以下变量可被识别:

  • %i :服务器的实例 ID。 服务器创建时将从 http://169.254.169.254/metadata/v1/id中获取。 它与用于唯一标识 Droplet 的 Droplet ID 相对应。
  • %f :服务器的 FQDN。

有鉴于此,常见的 ertname 设置如下:

1#cloud-config
2puppet:
3
4. . .
5
6  conf:
7    agent:
8      server: "puppet.example.com"
9      certname: "%i.%f"

这将产生一个模式与此类似的 certname

1|-Droplet ID
2   |
3   |            |-Fully Qualified Domain Name
4   |            |
5|-----||-------------------|
6123456.testnode.example.com

将 Droplet ID 作为 "certname "的一部分有助于配置安全的 Puppet 自动签名,我们将在下一节看到这一点。

实施 Puppet 证书自动签名

如果你想实施证书自动签名系统,以避免管理员干预,有几种选择。 您必须先在 Puppet 主服务器上进行设置。

在 Puppet 主服务器上的 puppet.conf 文件中,你可以在文件的 [master] 部分设置 autosign 选项。 该选项可以有几种不同的值:

true :这将告诉 Puppet 主服务器签署每一个收到的证书请求,而不做任何检查。 这在实际环境中极其危险,因为任何主机都可以获得已签名的 CSR 并进入你的基础架构。 <whitelist_filename> :第二个选项是指定一个文件,作为主机白名单或主机正则表达式。 Puppet 主控程序将根据此列表检查证书签署请求,以确定是否应签署证书。 同样不建议这样做,因为证书名称很容易被伪造。 <policy_executable> :第三个选项是指定一个可运行的脚本或可执行文件,以确定是否要签署证书签名请求。 Puppet 会将证书名作为参数传入,并通过标准输入将整个 CSR 传入。 如果返回 0 的退出状态,则证书已签署。 如果返回其他状态,则证书不会被签署。

基于策略的自动签名是实施自动密钥签名的最安全方法,因为它允许你任意复杂地区分合法和非法请求。

要演示基于策略的自动签名,可以在包含 %i 实例 ID 变量的 cloud-config 中添加 certname 变量。 我们将使用 %i.%f,这样它还包括所选的主机名:

1#cloud-config
2puppet:
3  conf:
4    agent:
5      server: "puppet.example.com"
6      certname: "%i.%f"
7  ca_cert: |
8
9   . . .

您完整的 cloud-config 现在可能是这样的:

 1#cloud-config
 2puppet:
 3  conf:
 4    agent:
 5      server: "puppet.example.com"
 6      certname: "%i.%f"
 7  ca_cert: |
 8    -----BEGIN CERTIFICATE-----
 9    MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
10    ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
11    GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC
12
13    . . .
14
15    arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
16    rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
17    l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
18    UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
19    -----END CERTIFICATE-----
20output: {all: '| tee -a /var/log/cloud-init-output.log'}
21runcmd:
22  - while [ ! -e /usr/bin/puppet ]; do sleep 5; done; puppet agent --enable; service puppet restart
23disable_ec2_metadata: true

在 Puppet 主服务器上,我们必须设置一个验证脚本。 由于 Puppet 已经安装了 Ruby,我们可以制作一个简单的 Ruby 脚本。

由于我们使用%i.%f格式的certname,我们可以检查certname的第一部分(第一个点之前的部分)是否与我们账户的有效 Droplet ID 相对应。 这只是一个简单的检查,实际上并不比白名单文件做得多。 不过,如果您愿意,也可以将这一想法调整得更加复杂。

为此,我们需要从DigitalOcean控制面板的 "应用程序和API "部分获取个人访问令牌。 你还需要安装一个DigitalOcean Ruby库。 下面,我们将向你展示一些使用BargeDropletKitDigitalOcean Ruby客户端的简化脚本。

如果希望使用 Barge 客户端,请在 Puppet 主控器上安装 gem,键入

1sudo gem install barge

以下脚本可用于检查证书签名请求中的 certname 首部是否与有效的 Droplet ID 相对应:

 1#!/usr/bin/env ruby
 2
 3require 'barge'
 4
 5TOKEN = 'YOUR_DIGITALOCEAN_API_TOKEN'
 6
 7droplet_ids = []
 8certname = ARGV[0]
 9id_string = certname.slice(0...(certname.index('.')))
10id_to_check = id_string.to_i
11
12client = Barge::Client.new(access_token: TOKEN)
13droplets = client.droplet.all
14
15droplets.droplets.each do |droplet|
16        droplet_ids << droplet.id
17end
18
19Kernel.exit(droplet_ids.include?(id_to_check))

如果你想使用DropletKit(DigitalOcean的官方Ruby客户端),可以键入以下内容安装gem:

1sudo gem install droplet_kit

请注意,DropletKit gem 仅对 Ruby 2.0 及以上版本有效,因此在使用 Puppet 附带的 Ruby 版本时可能无法使用。

DropletKit 的脚本可以这样改编:

 1#!/usr/bin/env ruby
 2
 3require 'droplet_kit'
 4
 5TOKEN = 'YOUR_DIGITALOCEAN_API_TOKEN'
 6
 7droplet_ids = []
 8certname = ARGV[0]
 9id_string = certname.slice(0...(certname.index('.')))
10id_to_check = id_string.to_i
11
12client = DropletKit::Client.new(access_token: TOKEN)
13droplets = client.droplets.all
14
15droplets.each do |droplet|
16        droplet_ids << droplet.id
17end
18
19Kernel.exit(droplet_ids.include?(id_to_check))

您可以将与所安装 gem 相对应的脚本放在名为 /etc/puppet/validate.rb的文件中,并通过键入将其标记为可执行:

1sudo chmod +x /etc/puppet/validate.rb

然后,您可以将以下内容添加到您的 puppet.conf 文件(如果使用开源 Puppet,则位于 /etc/puppet/puppet.conf)中:

1. . .
2
3[master]
4autosign = /etc/puppet/validate.rb
5
6. . .

重启 Apache 服务以执行新的签名策略:

1sudo service apache2 restart

现在,当 Puppet 主控程序收到证书签名请求时,它会检查证书名称的第一部分是否与账户中有效的 Droplet 名称一致。 这是一个使用可执行文件验证请求的粗略示例。

结论

利用 cloud-config 脚本,您可以轻松启动新服务器,并将其移交给现有的配置管理系统。 这样,在进行管理解决方案范围之外的重要变更之前,您就可以通过现有工具立即控制基础设施。

Published At
Categories with 技术
comments powered by Disqus