序章

管理者は、DigitalOceanメタデータサービスを使用して、新しいサーバーが自動的に構成できるようにする手順を提供できます。 これは便利ですが、多くの組織は、ChefやPuppetなどの構成管理ツール内ですべてのインフラストラクチャ構成を処理することを好みます。

このガイドでは、メタデータサービスとCloudInitを使用してDigitalOceanサーバーをブートストラップし、既存の構成管理デプロイメントに接続する方法を示します。 サーバーの実際の構成は、構成管理サービスで処理できます。 ChefノードとPuppetノードの両方をブートストラップする方法を示します。

前提条件

このガイドを完了するには、DigitalOceanメタデータサービスにある程度精通している必要があります。 メタデータサービスに情報を入力したり、メタデータサービスから情報を取得したりする方法の詳細については、このガイドを参照してください。

このガイドでは、最初の実行構成を実行するために、DropletのCloudInitサービスによって最初の起動時に消費されるcloud-configと呼ばれるタイプのスクリプトを活用します。 このガイドに示されているスクリプトを変更する方法をよりよく理解するには、cloud-configスクリプト、それらの構文、および動作について基本的な知識を身に付ける必要があります。 cloud-configスクリプトの概要はここにあります。 より実用的な例については(フォーマットの制限に関するいくつかの議論とともに)、cloud-configここを使用していくつかの基本的なタスクを実行するためのガイドを読むことができます。

Cloud-Configスクリプトを使用してChefノードをブートストラップする

DigitalOceanメタデータサービスを使用すると、cloud-configスクリプトを使用して、新しいサーバーを既存のChef制御のインフラストラクチャに簡単に接続できます。

このシステムに新しいサーバーを追加するには、新しいサーバーが構成手順を受け取るために接続できるChefサーバーがすでに構成されている必要があります。 Chefサーバーと管理ワークステーションのデプロイについてサポートが必要な場合は、このガイドに従って開始できます。

一般計画

新しいサーバーをオンラインにするときは、Chefサーバーの制御下に置く必要があります。 通常、これは、knife管理コマンドを使用して新しいサーバーに接続し、bootstrapサブコマンドを使用することで実現できます。 これにより、新しいサーバーに接続し、Chefクライアントと、新しいノードがChefサーバーに接続できるようにする検証クレデンシャルをインストールします。 その後、Chefクライアントはサーバーに接続し、自身を検証し、新しいクライアントクレデンシャルを受け取り、サーバーから構成をプルダウンし、自身を目的の状態にするために必要なアクションを実行します。

このガイドでは、cloud-configスクリプトを使用して手動のブートストラップ手順を置き換え、新しいノードがChefサーバーに自動的に接続し、自身を検証し、クライアントクレデンシャルを受け取り、Chefクライアントの初期実行を実行できるようにします。 サーバーは、管理者の手動による支援なしで、最初の起動時にこれを自動的に実行します。

KnifeConfigファイルから必要なデータを収集する

cloud-configスクリプトを正常にブートストラップするには、knifeコマンドで通常使用できる資格情報にアクセスする必要があります。 具体的には、次の情報が必要です。

  • Chef検証名
  • 検証キー
  • ChefサーバーにアクセスできるURL

この情報はすべて、Chefインフラストラクチャの管理に使用されるワークステーションのknife構成ファイルで正しい形式で入手できます。 Chefリポジトリ内には、このファイルを含む.chefという隠しディレクトリが必要です。

Chefリポジトリがワークステーションのホームディレクトリにあり、chef-repoという名前であるとすると、次のように入力してファイルの内容を出力できます。

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

必要な情報を以下に示します。

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

検証名とChefサーバーのURLは、ファイルからそのまま直接取得できます。 これらの値をコピーして、cloud-configファイルで使用できるようにします。

validation_keyは、実際のキーが保持されている場所を指します。 上記の例では、これはknife.rbファイルと同じディレクトリにあり、digitalocean-validator.pemと呼ばれていることを示しています。 これは、構成によって異なる可能性があります。

このファイルの内容が必要なので、もう一度catコマンドを使用してください。 コマンドを変更して、バリデーターキーに指定された場所を指すようにします。

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

RSA秘密鍵が表示されます。

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU

. . .

sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
-----END RSA PRIVATE KEY-----

検証キー全体をコピーして、cloud-configスクリプトですぐに使用できるようにします。

基本的なCloud-ConfigChefクライアントのインストール

上記のデータを取得したら、スクリプトを作成できます。 Chefの設定は、chefと呼ばれる専用のcloud-configモジュールを介して実行できます。 cloud-configには有効なYAMLが含まれている必要があり、スクリプトの最初の行として#cloud-configが含まれている必要があります。

最初は、スクリプトは次のようになります。

#cloud-config
chef:

cloud-configのドキュメントには、Ruby gem、パッケージ、または従来の「オムニバス」インストール方法を使用してChefクライアントをインストールできると記載されています。 ただし、実際には、gemメソッドとpackageメソッドの両方が失敗する傾向があるため、「オムニバス」メソッドを使用します。 通常は必要ありませんが、オムニバスインストーラーの場所も明示的にリストします。

force_installを「false」に設定します。 このように、何らかの理由でChefクライアントがすでにイメージにインストールされている場合(たとえば、スナップショットからデプロイしている場合)、クライアントは再インストールされません。 これまでのところ、スクリプトは次のようになっています。

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

次に、node_nameディレクティブを使用して、Chefインフラストラクチャ内の新しいサーバーの名前を選択するオプションがあります。 これを設定しない場合、Chefはサーバーのホスト名を使用するため、これはオプションです。 ただし、これはChef環境で一意である必要があります。

その後、Chefワークステーションから取得したすべての接続情報を追加できます。 server_urlオプションを、knife.rbファイルの場合とまったく同じようにChefサーバーの場所に設定します。 validation_nameオプションについても同様です。

検証キーには、YAMLパイプ記号(|)を使用して、ワークステーションで見つかった検証キー全体を入力します。

#cloud-config
chef:
  install_type: "omnibus"
  omnibus_url: "https://www.opscode.com/chef/install.sh"
  force_install: false
  node_name: "new_node"
  server_url: "https://your_server.com/organizations/digitalocean"
  validation_name: "digitalocean-validator"
  validation_key: |
    -----BEGIN RSA PRIVATE KEY-----
    MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
    V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
    vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU

    . . .

    sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
    Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
    eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
    -----END RSA PRIVATE KEY-----

この時点で、スクリプトには、Chefサーバーに接続してクライアント資格情報を作成するために必要なすべての認証が含まれています。

Chef環境、run_list、および属性の構成

上記の詳細は、クライアントがChefサーバーに接続するための十分な情報を提供しますが、実際にそれ自体を構成する方法に関する情報をノードに提供していません。 この情報は、cloud-configスクリプトでも提供できます。

新しいノードを配置する環境を指定するには、environmentオプションを使用します。 これが設定されていない場合、_default環境が設定されます。これは、別の環境が与えられていないChefノードの一般的なデフォルトです。

chef:
  environment: "staging"

run_listは、クライアントが順番に適用する必要があるアイテムの単純なリストとして指定できます。 これらは、レシピまたはロールのいずれかです。

chef:
  run_list:
    - "recipe[lamp]"
    - "role[backend-web]"

initial_attributes階層を使用して、新しいノードの初期属性を指定できます。 これにより、run_listの適用方法に影響を与える初期属性が設定されます。

chef:
  initial_attributes:
    lamp:
      apache:
        port: 80
      mysql:
        username: webclient
        pass: $#fjeaiop34S

前のcloud-configスクリプトに接続すると、次のようになります。

#cloud-config
chef:
  install_type: "omnibus"
  omnibus_url: "https://www.opscode.com/chef/install.sh"
  force_install: false
  node_name: "new_node"
  server_url: "https://your_server.com/organizations/digitalocean"
  validation_name: "digitalocean-validator"
  validation_key: |
    -----BEGIN RSA PRIVATE KEY-----
    MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
    V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
    vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU

    . . .

    sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
    Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
    eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
    -----END RSA PRIVATE KEY-----
  environment: "staging"
  run_list:
    - "recipe[lamp]"
    - "role[backend-web]"
  initial_attributes:
    lamp:
      apache:
        port: 80
      mysql:
        username: webclient
        pass: $#fjeaiop34S

出力のリダイレクトとChefクライアント実行の構成

上記のスクリプトには、chef:セクションで必要なすべての情報が含まれています。 ただし、他のcloud-configモジュールを使用して実行する必要がある他のいくつかのことがあります。

まず、すべてのコマンドとサブコマンドからの出力をCloudInitプロセスの出力ログにリダイレクトすることを指定する必要があります。 これはデフォルトで/var/log/cloud-init-output.logにあります。 これは、outputモジュールを使用して次のように実行できます。

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

もう1つ実行したいのは、Chefクライアントをインストールして構成した後、実際に実行するように設定することです。 この記事の執筆時点では、オムニバスのインストール方法はこれを自動的に行いません。

コマンドを呼び出す前に、chef-client実行可能ファイルがサーバーにインストールされるまで待機することで、この動作を強制できます。 単純なbashループを使用して、5秒ごとにこのファイルの存在を確認します。 見つかったら、指定した初期構成を実装するためにchef-clientを実行します。

runcmdモジュールを使用して、任意のコマンドを発行できます。 bashループの理想的な場所です。

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

また、オプションで、別のcloud-configディレクティブを追加して、最初の起動後にメタデータエンドポイントをヌルルーティングすることもできます。 これは、ユーザーデータに秘密鍵を入れているので便利です。 メタデータエンドポイントをヌルルーティングしないと、サーバー上のすべてのユーザーがこれにアクセスできます。 次を追加してこれを実装します。

disable_ec2_metadata: true

これらをこれまでに作成したスクリプトと組み合わせると、ノードをブートストラップしてChefインフラストラクチャに接続するために必要な完全なスクリプトを取得できます。

#cloud-config
chef:
  install_type: "omnibus"
  omnibus_url: "https://www.opscode.com/chef/install.sh"
  force_install: false
  node_name: "new_node"
  server_url: "https://your_server.com/organizations/digitalocean"
  validation_name: "digitalocean-validator"
  validation_key: |
    -----BEGIN RSA PRIVATE KEY-----
    MIIEowIBAAKCAQEA3O60HT5pwEo6xUwcZ8WtExBUhoL3bTjlsvHVXg1JVmBUES+f
    V9jLu2N00uSZEDZneCIQyHLBXnqD/UNvWEPNvPzt1ecXzmw2BytB7lPDW4/F/8tJ
    vAVrKqC7B04VFGmcFY2zC8gf8BWmX8CNRDQooM7UO5OWe/H6GDGPPRIITerO3GrU

    . . .

    sWyRAoGBAKNc/ZUM8ljRV0UJxQ9nbdozXRZjtUaNgXMNiw+oP2HYYdHrlkKnGHYJ
    Js63rvjpq8pocjE8YI+2H0v4/4uWqW8GEBfrWbLMzGsYPnRyiHR5+hgjCUU50RB3
    eFoNbURwLYcq2Z/IAQZpDpJWpofz3OVMpMXtei1cIflrAAd2wtWO
    -----END RSA PRIVATE KEY-----
  environment: "staging"
  run_list:
    - "recipe[lamp]"
    - "role[backend-web]"
  initial_attributes:
    lamp:
      apache:
        port: 80
      mysql:
        username: webclient
        pass: $#fjeaiop34S
output: {all: '| tee -a /var/log/cloud-init-output.log'}
runcmd:
  - while [ ! -e /usr/bin/chef-client ]; do sleep 5; done; chef-client
disable_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)を取得する必要があります。 これを行うには、次のように入力します。

hostname -f

ほとんどの場合、次のようなものが返されます。

puppet.example.com

Puppetマスター構成ファイルをチェックして、dns_alt_namesオプションが設定されているかどうかを確認することもできます。

cat /etc/puppet/puppet.conf
. . .

dns_alt_names = puppet,puppet.example.com

. . .

これらのオプションを設定した後にPuppetマスターのSSL証明書が生成された場合は、それらも使用できる可能性があります。

収集する必要があるもう1つのアイテムは、Puppetマスターの認証局証明書です。 これは、/var/lib/puppet/ssl/certs/ca.pemまたは/var/lib/puppet/ssl/ca/ca_crt.pemのいずれかにあります。

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

結果は次のようになります。

-----BEGIN CERTIFICATE-----
MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC

. . .

arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
-----END CERTIFICATE-----

証明書全体をコピーします。 これをcloud-configファイルに含めて、新しいサーバーが正しいPuppetマスターに接続していることを確認できるようにします。

これらの情報を入手したら、cloud-configファイルの作成を開始して、新しいサーバーが既存のPuppetインフラストラクチャにプラグインできるようにします。

基本的なCloud-ConfigPuppetノードのインストール

新しいPuppetノードのcloud-config構成はかなり単純です。 すべてのPuppet固有の構成は、ファイルのpuppet:セクション内にあります。 すべてのcloud-configファイルと同様に、最初の行には#cloud-configが含まれている必要があります。

#cloud-config
puppet:

この下には、2つのサブセクションしかありません。 1つ目はca_certキーです。 これはパイプ文字を使用してYAMLテキストブロックを開始し、CA証明書全体をインデントされたブロックとして指定できるようにします。

#cloud-config
puppet:
  ca_cert: |
    -----BEGIN CERTIFICATE-----
    MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
    ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
    GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC
    
    . . .
    
    arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
    rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
    l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
    UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
    -----END CERTIFICATE-----

証明書全体を開始マーカーと終了マーカーとともに含め、適切にインデントするようにしてください。

puppet:傘下の2番目のセクションは、conf:セクションです。 これは、汎用puppet.confファイルに追加されるキーと値のペアを指定するために使用されます。 キーと値のペアは、puppet.confファイルの場合と同様に、セクションヘッダーの下に配置する必要があります。

たとえば、少なくとも、新しいサーバーはPuppetマスターサーバーのアドレスを知っている必要があります。 puppet.confファイルでは、次のように[agent]セクションにあります。

. . .

[agent]
server = puppet.example.com

. . .

cloud-config構文でこれを指定するには、これをこれまでの構文に追加します。

#cloud-config
puppet:
  ca_cert: |
    -----BEGIN CERTIFICATE-----
    MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
    ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
    GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC
    
    . . .
    
    arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
    rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
    l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
    UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
    -----END CERTIFICATE-----
  conf:
    agent:
      server: "puppet.example.com"

conf:セクションはca_certセクションとインラインであり、子要素ではないことに注意してください。 これは、Puppetマスターに接続するために最低限必要なものです。 puppet.confにある追加の構成アイテムは、最初にセクション名のレベルを作成し、次にキーと値のペアを定義することで、同様の方法で追加できます。

この後、今後のすべての出力をcloud-init-output.logファイルにリダイレクトし、Chef構成に追加したものと同等のruncmd行を追加する必要があります。 これにより、Puppetエージェントがインストールされるまで待機してから、有効にして再起動します。 Chefセクションで行ったように、最初の実行後にメタデータエンドポイントをnullルーティングすることもできます。 これらの行cloud-configディレクティブは、他のモジュールセクションの外側に配置する必要があります。

. . .

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

この情報を使用して、新しいサーバーはPuppetマスターサーバーに接続し、マスターに転送するためのクライアント証明書署名要求を生成できます。 デフォルトでは、クライアント証明書はPuppetマスターで手動で署名する必要があります。 これが完了すると、次のPuppetエージェントの更新間隔(デフォルトでは30分ごと)で、ノードはPuppetマスターから構成をプルダウンします。 この遅延を回避するために、比較的安全な自動署名メカニズムを実装する方法については、少し後で説明します。

ノードの証明書名の定義

新しいサーバーのpuppet.confファイルに配置できる値の1つは、固有のケースです。 cloud-configファイルでは、特定の変数が指定されている場合、certnameオプションで環境の値を置き換えることができます。 次の変数が認識されます。

  • %i :サーバーのインスタンスID。 これは、サーバーの作成時にhttp://169.254.169.254/metadata/v1/idから取得されます。 これは、液滴を一意に識別するために使用される液滴IDに対応します。
  • %f :サーバーのFQDN。

これを念頭に置いて、一般的なcertname設定は次のようになります。

#cloud-config
puppet:

. . .

  conf:
    agent:
      server: "puppet.example.com"
      certname: "%i.%f"

これにより、次のようなパターンのcertnameが生成されます。

   |-Droplet ID
   |
   |            |-Fully Qualified Domain Name
   |            |
|-----||-------------------|
123456.testnode.example.com

certnameの一部としてドロップレットIDを使用すると、次のセクションで説明するように、安全なPuppet自動署名を構成するのに役立ちます。

Puppet証明書の自動署名を実装する

管理者の介入の必要性を回避するために証明書自動署名システムを実装したい場合は、いくつかのオプションがあります。 最初にPuppetマスターサーバーでこれを設定する必要があります。

Puppetマスターサーバーのpuppet.confファイルで、ファイルの[master]セクションの下にあるautosignオプションを設定できます。 これは、いくつかの異なる値を取ることができます。

  • true :これにより、Puppetマスターサーバーは、チェックを行わずに、着信するすべての証明書要求に署名するように指示されます。 これは実際の環境では非常に危険です。どのホストもCSRに署名して、インフラストラクチャに入ることができるからです。
  • :2番目のオプションは、ホストまたはホスト正規表現のホワイトリストとして機能するファイルを指定することです。 Puppetマスターは、このリストに対して証明書署名要求をチェックして、証明書に署名する必要があるかどうかを確認します。 証明書名は簡単にスプーフィングされる可能性があるため、これもお勧めしません。
  • :3番目のオプションは、証明書署名要求に署名する必要があるかどうかを判断するために実行できるスクリプトまたは実行可能ファイルを指定することです。 Puppetは、引数としてcertnameを渡し、標準入力を介してCSR全体を渡します。 終了ステータス0が返された場合、証明書は署名されています。 別のステータスが与えられた場合、証明書は署名されません

ポリシーベースの自動署名は、自動キー署名を実装するための最も安全な方法です。これにより、正当な要求と非正当な要求を区別する方法を任意に複雑にすることができます。

ポリシーベースの自動署名を示すために、certname変数を%iインスタンスID変数を含むcloud-configに追加できます。 %i.%fを使用して、選択したホスト名も含まれるようにします。

#cloud-config
puppet:
  conf:
    agent:
      server: "puppet.example.com"
      certname: "%i.%f"
  ca_cert: |

   . . .

完全なcloud-configは次のようになります。

#cloud-config
puppet:
  conf:
    agent:
      server: "puppet.example.com"
      certname: "%i.%f"
  ca_cert: |
    -----BEGIN CERTIFICATE-----
    MIIFXjCCA0agAwIBAgIBATANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFQdXBw
    ZXQgQ0E6IHB1cHBldDAeFw8xNTAyMTkxOTA0MzVaFw0yMDAyMTkxOTA0MzVaMBwx
    GjAYBgNVBAMMEVB1cHBldCBDQTogcHVwcGV0MIICIjANBgkqhkiG9w0BAQEFAAOC
    
    . . .
    
    arsjZT5/CtIhtP33Jl3mCp7U2F6bsk4/GDGRaAsFXjJHvBbL93NzgpkZ7elf0zUP
    rOcSGrDrUuzuJk8lEAtrZr/IfAgfKKXPqbyYF95V1qN3OMY+aTcrK20XTydKVWSe
    l5UfYGY3S9UJFrSn9aBsZzN+10HXPkaFKo7HxpztlYyJNI8UVSatcRF4aYYqt9KR
    UClnR+2WxK5v7ix0CVd4/KpYH/6YivvyTwxrhjF2AksZKg==
    -----END CERTIFICATE-----
output: {all: '| tee -a /var/log/cloud-init-output.log'}
runcmd:
  - while [ ! -e /usr/bin/puppet ]; do sleep 5; done; puppet agent --enable; service puppet restart
disable_ec2_metadata: true

Puppetマスターサーバーで、検証スクリプトを設定する必要があります。 RubyはすでにPuppetにインストールされているので、簡単なRubyスクリプトを作成できます。

certnameには%i.%f形式を使用しているため、certnameの最初の部分(最初のドットの前の部分)が有効なドロップレットIDに対応しているかどうかを確認できます。私たちのアカウントのために。 これは単なるチェックであり、実際にはホワイトリストファイル以上のことはしません。 ただし、必要に応じて、このアイデアをさらに複雑にすることができます。

これを行うには、DigitalOceanコントロールパネルの「アプリとAPI」セクションからの個人用アクセストークンが必要になります。 また、DigitalOceanRubyライブラリの1つをインストールする必要があります。 以下に、BargeおよびDropletKitDigitalOceanRubyクライアントを使用するいくつかの簡略化されたスクリプトを示します。

Bargeクライアントを使用する場合は、次のように入力して、Puppetマスターにgemをインストールします。

sudo gem install barge

次のスクリプトを使用して、証明書署名要求のcertnameの最初の部分が有効なドロップレットIDに対応しているかどうかを確認できます。

#!/usr/bin/env ruby

require 'barge'

TOKEN = 'YOUR_DIGITALOCEAN_API_TOKEN'

droplet_ids = []
certname = ARGV[0]
id_string = certname.slice(0...(certname.index('.')))
id_to_check = id_string.to_i

client = Barge::Client.new(access_token: TOKEN)
droplets = client.droplet.all

droplets.droplets.each do |droplet|
        droplet_ids << droplet.id
end

Kernel.exit(droplet_ids.include?(id_to_check))

代わりに、DigitalOceanの公式RubyクライアントであるDropletKitを使用する場合は、次のように入力してgemをインストールできます。

sudo gem install droplet_kit

DropletKitgemはRuby2.0以降でのみ有効であるため、Puppetに付属しているバージョンのRubyを使用している場合はこれが不可能な場合があることに注意してください。

DropletKitのスクリプトは、次のように調整できます。

#!/usr/bin/env ruby

require 'droplet_kit'

TOKEN = 'YOUR_DIGITALOCEAN_API_TOKEN'

droplet_ids = []
certname = ARGV[0]
id_string = certname.slice(0...(certname.index('.')))
id_to_check = id_string.to_i

client = DropletKit::Client.new(access_token: TOKEN)
droplets = client.droplets.all

droplets.each do |droplet|
        droplet_ids << droplet.id
end

Kernel.exit(droplet_ids.include?(id_to_check))

インストールしたgemに対応するスクリプトを/etc/puppet/validate.rbというファイルに配置し、次のように入力して実行可能としてマークすることができます。

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

次に、puppet.confファイル(オープンソースPuppetを使用している場合は/etc/puppet/puppet.confにあります)に以下を追加できます。

. . .

[master]
autosign = /etc/puppet/validate.rb

. . .

Apacheサービスを再起動して、新しい署名ポリシーを実装します。

sudo service apache2 restart

これで、証明書署名要求がPuppetマスターによって受信されると、証明書名の最初の部分がアカウント内の有効なドロップレット名に対応しているかどうかがチェックされます。 これは、実行可能ファイルを使用してリクエストを検証する方法の大まかな例です。

結論

cloud-configスクリプトを活用することで、新しいサーバーを簡単にブートストラップして、既存の構成管理システムに渡すことができます。 これにより、管理ソリューションの範囲外で重要な変更を行う前に、既存のツールを使用してインフラストラクチャをすぐに制御できます。