構成管理システムに新しいドロップレットを自動的に追加する方法

前書き

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

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

前提条件

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

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

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

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

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

一般計画

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

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

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

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

  • シェフ検証名

  • 検証キー

  • 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-Config Chefクライアントのインストール

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

開始すると、スクリプトは次のようになります。

#cloud-config
chef:

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

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 Client Runの構成

上記のスクリプトには、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ディレクティブを追加して、最初の起動後にメタデータエンドポイントをヌルルーティングすることもできます。 これは、ユーザーデータに秘密キーを入れているため便利です。 メタデータエンドポイントをnullルーティングしないと、サーバー上のすべてのユーザーがアクセスできます。 以下を追加してこれを実装します。

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サーバーを起動して実行するためのサポートが必要な場合は、this guideを確認してください。

一般計画

新しい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-Config Puppetノードのインストール

新しい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の一部としてDroplet IDを使用すると、次のセクションで説明するように、安全なPuppet自動署名を構成するのに役立ちます。

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

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

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

  • true:これにより、Puppetマスターサーバーは、チェックを行わずに、受信するすべての証明書要求に署名するように指示されます。 これは実際の環境では非常に危険です。どのホストもCSRに署名してインフラストラクチャに入ることができるからです。

  • <whitelist_filename>:2番目のオプションは、ホストまたはホスト正規表現のホワイトリストとして機能するファイルを指定することです。 Puppetマスターは、このリストに対して証明書署名リクエストをチェックし、証明書に署名する必要があるかどうかを確認します。 証明書名は簡単になりすまされる可能性があるため、これもお勧めしません。

  • <policy_executable>:3番目のオプションは、証明書署名要求に署名する必要があるかどうかを判断するために実行できるスクリプトまたは実行可能ファイルを指定することです。 Puppetは証明書名を引数として渡し、CSR全体を標準入力を介して渡します。 終了ステータス0が返された場合、証明書は署名されています。 別のステータスが与えられた場合、証明書はnot署名されます。

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

ポリシーベースの自動署名を示すために、%iインスタンスID変数を含むcloud-configcertname変数を追加できます。 %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]セクションからの個人アクセストークンが必要です。 DigitalOcean Rubyライブラリのいずれかをインストールする必要もあります。 以下に、BargeおよびDropletKit DigitalOceanRubyクライアントを使用するいくつかの簡略化されたスクリプトを示します。

割り込みクライアントを使用する場合は、次のように入力して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

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

Related