Chef >

目次

概要

  • ChefVault は、Nordstrom, Inc. が開発したChefの Data Bag 上で動作する秘密情報管理ツールです。このツールを利用することにより、Chefを介してパスワードやサーバ秘密鍵等の秘密情報を対象ノードにセキュアに配備することを容易にしてくれます。
  • ChefVault では Encrypted data bag と異なり、(自動でvaultアイテム毎に共有秘密鍵が生成され、アクセス許可者の公開鍵で暗号化され提供されるため)ユーザが共有秘密鍵を明示的に取り扱う必要がなく、運用ミスが起こりにくく堅牢です。
  • 運用管理のための knife vault プラグインとレシピ実装のためのライブラリは、chef-vault gemパッケージで提供されており、Chef DKにはすでにそれが同梱されています。

インストール

Chef Workstation

  1. ChefVaultは、すでにChef DKに含まれていますが、Chef DKを利用しない場合には別途 Workstation に chef-vault パッケージをインストールします。
    $ sudo /opt/chef/embedded/bin/gem install chef-vault
  2. ChefVaultはデフォルトで solo モードで動作し、ローカルのファイルシステムに秘密情報が格納されます。一方、Chefサーバに格納させたい場合には、-M client オプションを付加する必要があります。都度このオプションを付加するのは面倒ですので、設定ファイルに以下の設定を追加しておきます。以下の例は、この設定がしてあるものとします。
    1. knife[:vault_mode] = 'client'

note.png設計上の留意点

  1. アクセス制御できる単位はvaultアイテム毎です。一つのアイテムにどこまでの範囲の秘密情報を含めるのか、事前によく検討しましょう。あまり詰め込みすぎるとアクセスできるクライアントが増えてしまい、適切なアクセス制御ができなくなる恐れがあります。
  2. 複数環境(environment)の秘密情報を一つのアイテムに収容するのは、議論の分かれるところです。管理の簡便さも重要ですが、よりセキュリティを重視しましょう。

note.png運用上の注意点

  • ChefVault は秘密情報をセキュアに管理する機能を提供する強力なツールですが、日常的には正しく運用し、非常(情報漏洩)時には迅速に対応を実施する必要があります。手放しでの運用が最も危険です。
  1. レシピからの参照実装時には、エコーされたり、ログ記録されないように注意しましょう。
  2. 共有秘密鍵は定期的にローテートしましょう。共有秘密鍵の事前漏洩対策になります。
  3. 定期的に管理者、アクセスできるユーザ、クライアントを見直しましょう。
  4. アクセスユーザの秘密鍵が漏洩した場合には、速やかにそのユーザのアクセス権を剥奪し、共有秘密鍵をローテートしましょう。
    1. 秘密情報の漏洩も疑われる場合には、(即時変更が可能であれば)秘密情報自体も変更しましょう。
  5. 定期的にバックアップし、適切に防御された場所に保管しましょう。
  6. 不要になった秘密情報は速やかに削除しましょう。

運用例

  • 以下の例では、organizationの管理ユーザである alice で作業している想定です。

vaultアイテムの管理

アイテムの作成

  1. JSON形式で秘密情報を与え、vaultアイテムを作成します。
    $ knife vault create dbpass_vault db_a '{"root": "change_on_install"}'
    $ knife vault list
    dbpass_vault
  2. vaultアイテムかどうか確認します。
    $ knife vault itemtype dbpass_vault db_a
    vault
  3. 実体は Data bag(ChefVaultでは特別に vault と呼びます) ですので、それ自体のパーミションを確認してみます。Chefのユーザは基本的にシステム運用者ですのでデフォルトのパーミションでも問題ないと思われますが、場合によっては特定のグループを作成して権限を絞ってもよいかもしれません。
    $ knife acl show data dbpass_vault -F pp
    {"create"=>{"actors"=>["pivotal", "alice"], "groups"=>["admins", "users"]},
     "read"  =>{"actors"=>["pivotal", "alice"], "groups"=>["clients", "admins", "users"]},
     "update"=>{"actors"=>["pivotal", "alice"], "groups"=>["admins", "users"]},
     "delete"=>{"actors"=>["pivotal", "alice"], "groups"=>["admins", "users"]},
     "grant" =>{"actors"=>["pivotal", "alice"], "groups"=>["admins"]}
    }
    • note.pngデフォルトでは全ユーザとクライアントに Data bag の参照権限が付与されていますので vault 自体を読むことは可能ですが、暗号化されていますので復号化して意味のある秘密情報を読み取ることができるのは、その vault についての権限が許可されたものに限られます。vaultに対する権限がないものが復号化しようとした場合、以下のようなエラーとなります。
      $ knife vault show dbpass_vault db_a -F json
      ERROR: ChefVault::Exceptions::SecretDecryption: dbpass_vault/db_a is not encrypted with your public key.
        Contact an administrator of the vault item to encrypt for you!
  4. 一つのvaultアイテムがどのようなData bagアイテムで構成されるか確認します。
    $ knife data bag show dbpass_vault
    db_a
    db_a_keys
    1. vaultアイテム本体には、生成された共有秘密鍵(乱数)によって秘密情報が暗号化格納されています。
      $ knife data bag show dbpass_vault db_a -F json
      WARNING: Encrypted data bag detected, but no secret provided for decoding.  Displaying
       encrypted data.
      {
        "id": "db_a",
        "root": {
          "encrypted_data": "rjbF1pjBiq2aOnNhK0KSdigrrzt1/azL4LhWaHaveMdDM56U2zZpNE/pcRCC\nbQMj
      \n",
          "iv": "7sazUX2To9SI92hC2qUQRQ==\n",
          "version": 1,
          "cipher": "aes-256-cbc"
        }
      }
    2. 認可情報(アクセスユーザリストと各ユーザの公開鍵で暗号化された共有秘密鍵リスト)は、vaultアイテム名に _keys というサフィックスを付加したアイテムに格納されます。
      $ knife data bag show dbpass_vault db_a_keys -F json
      Unencrypted data bag detected, ignoring any provided secret options.
      {
        "id": "db_a_keys",
        "admins": [
          "alice"
        ],
        "clients": [
      
        ],
        "search_query": [
      
        ],
        "alice": "PjnfxUsRO390nAypaFgnABuO6adl+LnVnkJT8q+12AhnFuIXBm6VKMBpiVQd\nuYe3p/ICJuxuaAv
      dLnxiO/RCBFy6jTpYsuT5r/Bs24YnELGl0zZaweE0IsbH\nyAfbDV4J0fZiVzmqrHyPVeFGk4f7Q5KySGKL2sUbJ8
      Bioce1zlCsVHkZZIZi\ne2WNkKWRHV9vk+cyB0130/6Oa4Y4rJx/tKp7NafliL0U9ibZgyEPQGSH9NdX\n85oca2E
      O99GF8Uqwyfhw34+SJXa/eRJ0TBXJvIJHTocn/ZYYJTxVbd6wnQzM\nzn4Ijopd8F5uyE9o86dgR6+GU4np9tngzL
      ZmH5O24w==\n"
      }

アイテムの復号化表示

  1. 復号化して表示します。
    $ knife vault show dbpass_vault db_a -F json
    {
      "id": "db_a",
      "root": "change_on_install"
    }

アイテムの編集

  1. あらかじめ EDITOR 環境変数を設定の上、設定値を編集してみます。
    $ knife vault edit dbpass_vault db_a
    {
      "root": "tiger"
    }
    ...
    ~
    :wq
    $ knife vault show dbpass_vault db_a -F json
    {
      "id": "db_a",
      "root": "tiger"
    }
  2. lightbulb.pngアイテムの内容が複雑かつ大きな場合には、JSON形式のファイルを用いて上書き編集することも可能です。
    $ knife vault update ssl_server_certs chef.io.example.com.prod --json ~/tmp/chef_io_example_com.crts.pem.json

アイテムの削除

  1. vaultアイテムを削除します。
    $ knife vault delete dbpass_vault db_a
    Do you really want to delete dbpass_vault/db_a? (Y/N) y
    Deleted chef_vault_item[dbpass_vault/db_a]
    $ knife data bag show dbpass_vault

複雑な構造のアイテム

  1. 秘密鍵などの秘密情報は Environment 毎に異なることが普通ですから、キーに environment 名を設定すると一つのvaultアイテムで一括管理することができます。note.pngただし、vaultアイテム毎のアクセス制御となりますので、クライアントが複数環境の秘密情報にアクセスしてもよいか事前に十分検討する必要があります。
    $ knife vault create dbpass_vault db_c '{"dev": "change_on_install", "prod": "tiger"}'
    
    $ knife data bag show dbpass_vault db_c -F json
    WARNING: Encrypted data bag detected, but no secret provided for decoding.  Displaying encrypted data.
    {
      "id": "db_c",
      "dev": {
        "encrypted_data": "l+1Tkazlm9nuwzI11P3082DT2HVYcRjt60i5UzOFT39afnTE9i746c0aCpGx\nQdWc
    \n",
        "iv": "1hFSVSb6jReRzHLnG56wTA==\n",
        "version": 1,
        "cipher": "aes-256-cbc"
      },
      "prod": {
        "encrypted_data": "z8vzGK/r5EU1jk+0EoIcTYU1nmY3J/NO8t9gBhk2w8Q=\n",
        "iv": "FG6o/KIDaZVXB59pB6Q5zA==\n",
        "version": 1,
        "cipher": "aes-256-cbc"
      }
    }
    
    $ knife vault show dbpass_vault db_c -F json
    {
      "id": "db_c",
      "dev": "change_on_install",
      "prod": "tiger"
    }
  2. chef-vault クックブックを利用すると、以下のようなヘルパメソッドにより参照のソースコードがよりスマート(透過的)になります。
    1. secret = chef_vault_item_for_environment('dbpass_vault', 'db_c')
    2. # is the same as
    3. #secret = chef_vault_item('dbpass_vault', 'db_c')[node.chef_environment]
  3. さらにネストさせ、Environment 毎に複数の秘密情報を格納できるように構造化してみます。2階層より深いハッシュは一括して暗号化されることが分かります。
    $ knife vault delete dbpass_vault db_d
    
    $ knife vault create dbpass_vault db_d '{"dev": {"root": "change_on_install", "web": "strong_pass"}, "prod": {"root": "tiger", "web": "lion"} }'
    
    $ knife data bag show dbpass_vault db_d -F json
    WARNING: Encrypted data bag detected, but no secret provided for decoding.  Displaying encrypted data.
    {
      "id": "db_d",
      "dev": {
        "encrypted_data": "NgIeX8EiDykeGmARj4dtC1agldP/GUiXNL/klgk2CWTyw8sLdWKqPN2Iez4p\nrndQ
    Y4hYLmNxlquE8ZLvbKtviI8WIddV/MwpVC2l/PeJRVk=\n",
        "iv": "7iHHJv4vmUfaA52bNdNMqg==\n",
        "version": 1,
        "cipher": "aes-256-cbc"
      },
      "prod": {
        "encrypted_data": "DqcAvGyOEFdqPKbid1/dyg0lhU7SU7ev5cetV5LVGOWVd2mu/1PfhZCrP5V4\nUpd7
    \n",
        "iv": "Yqhydz9IUKKjsUkcNEaJZA==\n",
        "version": 1,
        "cipher": "aes-256-cbc"
      }
    }
    
    $ knife vault show dbpass_vault db_d -F json          {
      "id": "db_d",
      "dev": {
        "root": "change_on_install",
        "web": "strong_pass"
      },
      "prod": {
        "root": "tiger",
        "web": "lion"
      }
    }
  4. chef-vault クックブックのヘルパメソッドを使用すると、以下のように参照できます。
    1. secret = chef_vault_item_for_environment('dbpass_vault', 'db_d')['web']
    2. # is the same as
    3. #secret = chef_vault_item('dbpass_vault', 'db_d')[node.chef_environment]['web']

アイテムの自動作成

  • 秘密情報の生成、管理はユーザの手によって行われることが多いですが、chef-vaultクックブックの chef_vault_secret リソースを使用することにより、レシピから動的に作成することが可能になります。
  • 各ノードのクライアント秘密鍵を生成しChefサーバに登録したり、鍵発行システムから自動で鍵を登録するといったケースが想定されます。
  1. 以下は、クライアントキーを動的に生成し登録している例です。
    1. require 'securerandom'
    2.  
    3. chef_vault_secret 'client00.example.com' do
    4.   data_bag 'client_keys'  # vault
    5.   raw_data({'private' => SecureRandom.base64(32)})
    6.   admins 'alice'
    7.   search 'name:client00.example.com'
    8. end

認可管理

管理ユーザの追加

  1. 管理ユーザとして、bob を追加してみます。bob の公開鍵で暗号化された共有秘密鍵エントリが追加されます。
    $ knife vault update dbpass_vault db_a -A bob
    
    $ knife data bag show dbpass_vault db_a_keys -F json
    Unencrypted data bag detected, ignoring any provided secret options.
    {
      "id": "db_a_keys",
      "admins": [
        "alice",
        "bob"
      ],
      "clients": [
    
      ],
      "search_query": [
    
      ],
      "alice": "M+rW5g0iCCg34av7tsM/DAkr9T+N1PRsXC9DDkEeAnvqW328BgdbR7modJJI\nBTkS9K985FwVIZK
    cSWLVUnJlZkgtMgFYh3xqz95Pf/SWHCn+jXgJljOw3+yX\nH6YoWORNlWvv9VBsORV9Nrxj27ph4XWbH3uSxdKvKA
    5yquRVuIJN1U74KEBt\ntrpWI4NuL5rsRDJj0NbuPNG7vfyZ90FVrIh+tTFb+wgAbT3PI8VCG3XqV7by\nTELmZk1
    BbDIiYBuCVKfw7UDFCFJo5Ac/nLwvls2CdlG90P8RR+rKGOkdl2L2\nrLgXT42mFsPMIRou1GZOpjYfXD6BEuBO4o
    /W+kBYyg==\n",
      "bob": "C/bn/OxmNfbOzncqk7stZV+UTF8c0yeXf6/YE9DYR/rYKtzTT7VqyP03Qbs1\n6Hx0VGO0c+pglUvmd
    5QzrGAKpMBUoawJa2W0uZvmhbIVmGqGuTaXHILDyjtG\nzt7dhAV6qQ3YzZD6BICHgZeQeRvgisoKDzY2etKnHP+N
    UjrRRTfgGKdbTtnQ\nQGXOi6Ex8fvhs+pSTxyCZWzVbE1f060wtROHGAAd7y6GNJLxEWZ8mE3przqi\nkmrcR10pl
    Px6t0+op0dk6HZxDx7++LgmUCXXxMSa1EDhvj4mLWlSI9qrw1Rx\nGrLNZ+vQ9Oy9R0vRz6G6tXdwAf4HlWe0FoOF
    IZR8dw==\n"
    }

検索クエリとしての読取専用クライアントの設定

  1. 読取専用クライアントとして、hare.wonderland.example.com を追加してみます。以下では例示のため name タイプで検索していますが、通常の運用では role タイプを指定する場合が多いでしょう。検索結果に含まれるそれぞれのノードの公開鍵で暗号化された共有秘密鍵エントリが追加されます。
    $ knife node list
    hare.wonderland.example.com
    hatter.wonderland.example.com
    
    $ knife vault update dbpass_vault db_a -S 'name:hare.wonderland.example.com'
    
    $ knife data bag show dbpass_vault db_a_keys -F json
    Unencrypted data bag detected, ignoring any provided secret options.
    {
      "id": "db_a_keys",
      "admins": [
        "alice",
        "bob"
      ],
      "clients": [
        "hare.wonderland.example.com"
      ],
      "search_query": "name:hare.wonderland.example.com",
      "alice": "r79vumGKqOYzgmFbes5U4ifzWpyhP8xgBEofwN7GVimSwCsQriq07O05TsPW\n+XOr4VdLypKvTkG
    0qNYW3qJp+yC9CaBn7PlKUIVhi7A94/qBAH2qo/fX1C7T\nv2zoa1w/CBA8eGnhwV5SaOwu56cw8YmlfhiWF8rmSM
    rRILxJapDvCiZVC1Kr\n+CMMgduLAJSvqVTC9PAmcOyZWfqbQxsXCWWtlHj3ax4d+H4tER5LMFLqTJd2\nzrYI1oc
    Ae6JjfORH+Q6RqJZWZZruQVSVHaHxqNsHJNgiXYgssY1gt7cvPB9o\nOzMy+b+En7PzmvxVeEun6OUywtVW5XHaGb
    e8w5YFyg==\n",
      "bob": "C/bn/OxmNfbOzncqk7stZV+UTF8c0yeXf6/YE9DYR/rYKtzTT7VqyP03Qbs1\n6Hx0VGO0c+pglUvmd
    5QzrGAKpMBUoawJa2W0uZvmhbIVmGqGuTaXHILDyjtG\nzt7dhAV6qQ3YzZD6BICHgZeQeRvgisoKDzY2etKnHP+N
    UjrRRTfgGKdbTtnQ\nQGXOi6Ex8fvhs+pSTxyCZWzVbE1f060wtROHGAAd7y6GNJLxEWZ8mE3przqi\nkmrcR10pl
    Px6t0+op0dk6HZxDx7++LgmUCXXxMSa1EDhvj4mLWlSI9qrw1Rx\nGrLNZ+vQ9Oy9R0vRz6G6tXdwAf4HlWe0FoOF
    IZR8dw==\n",
      "hare.wonderland.example.com": "ClXzlOGNGTB3eiRtrlyvUKRiP5zKH+qas2LqC6R8TSjCOZIIr+ZHEdT
    5pS2l\n+c/JxmVGpkrIuZqWOHFldk//WEDLM5MeV9kQs++2aTZmMDvqcHw5T8FAkmFm\nbOrysSfAxpeXFMwJhnLK
    omfMYOz2Wi3zzsTeplaYQEfbDfkisp8jAlNsZfMP\nWnVt1UZdM8YmzDSrM8dSyVN6lPCbj0rLxu/wa/sJdk5U3j2
    xRFjhkRZ2jhQ3\nX4e+IgI1geYqkfVlLUBnKgefT9aYGiFNk+SfwPKjGBTSrcMcesrkV4HHrTJW\noXOBuOh01SVc
    qDDN19RmgiMfdleBS37YPE4kmFuxGA==\n"
    }

検索クエリ文字列の設定ノウハウ

  • 検索クエリの結果に基いて読取専用クライアントのメンテナンスが行われますので、将来のノード追加や削除に耐えられる検索文字列を設定するのがポイントです。
  • クエリフォーマットは、knife search のそれと同様ですので複雑なクエリ文字列を与えることも可能ですが、ミスすることなくメンテナンスがしやすい文字列を設定するようにしましょう。
  • note.png検索クエリを変更しても、既に登録されているノードのアクセス権は自動で剥奪されません。knife vault refresh サブコマンドに --clean-unknown-clients オプションを追加して実行する必要があります。
  1. SSLサーバ秘密鍵の配備には、そのDNS名でサービスするノード群を過不足なく検索できるクエリ文字列にします。
    -S 'name:berks-api*.grid.example.com'
  2. バックエンドDBにアクセスするためのアカウント配備には、アクセスするノード群が過不足なく含まれるロールを指定するのが自然です。note.pngただし、roles キーではすでに一度レシピが適用されて(動的に)展開されたロール群がインデックスされますので、秘密情報にアクセスするレシピを含むロールを指定するとジレンマに陥ります。この問題は、ノードの run_list に直接含まれている(静的な)ロールのいずれかを role キーで指定することにより回避できます。
    -S 'roles:db_a-client'
    -S 'role:app-server'
  3. ロール指定による付与がどうしてもうまくいかない場合には、ノードにタグ付けをしてそれらのタグをキーにして権限付与を行うとよいでしょう。
    -S 'tags:db_a-client'
  4. 秘密情報にアクセスする必要のないノードにアクセス権が付与されていないか、十分注意します。クエリ文字列に、'*:*' を与えることは絶対にないでしょう。

読取専用クライアントのメンテナンス

  1. ノードが追加されたり削除された場合にはどのようにすればよいのでしょう。そのような場合には、knife vault refresh によってメンテナンスを行います。-S オプションによってあらかじめ設定された検索クエリにもとづき、参照クライアントの暗号化共有秘密鍵リストが更新されます。つまり、追加されたノードのためのエントリが追加されます。また、--clean-unknown-clients オプションを付加した場合には、同時に検索結果に含まれないクライアント(つまりは削除されたノード)のエントリが削除されます。ノードの追加、削除があった場合には、こまめに以下のコマンドを実行するようにしましょう。
    $ knife vault refresh dbpass_vault db_a --clean-unknown-clients

管理ユーザの削除

  1. 次に管理ユーザの bob を削除してみます。
    $ knife vault remove dbpass_vault db_a -A bob
    
    $ knife data bag show dbpass_vault db_a_keys -F json
    Unencrypted data bag detected, ignoring any provided secret options.
    {
      "id": "db_a_keys",
      "admins": [
        "alice"
      ],
      "clients": [
        "hare.wonderland.example.com"
      ],
      "search_query": "name:hare.wonderland.example.com",
      "alice": "hzeySMmb8f/GbnAXcNrrElHYBKdD12ztuUNGRSqVuwCoSQZddoeBb5nU3rML\nCNAC6OdUpvWgxMm
    uoZjuRUmqHURQBqzpGKkeiwz4zcsh7i/KJvvnIXekIj3C\nU70rGt7ikrCJlaMRdojM8Aa6LKltGRf274KXLqKmlO
    DToFXUrfMdG6/AvvyZ\n9eW3xuyV30TJxvosVLXtEbYF6oe6MmwcDOaJGDwdbG6skh3OV3TKoVUwAq04\ndR+y37Y
    4cDWstx9/G/ecvkBve2X88cCG6G+k9hmX/iEaqlwpS00c7DRWvll9\nHUly0oTlZ3mffWgwyuE1ddwdKVmpMpumAH
    gdXLh2Fw==\n",
      "hare.wonderland.example.com": "f5BgZriuTOt/WAXnu9GllhPlD30boj7fA2OjOJjzLCF1d9swcgfynxT
    Sqy2P\nioP/Ja/Gm5jQVY83dg2rPJvznSy/DD90BA9s92ZeoL2RpR4FDj4u8Y1COvdj\nFMnM+O6Pphf8H/xhRdbU
    io2897SZ+2c1fiaLJZNeQ0I0RBEsmSzFkjlI1FJA\nivHJ2+g5z6TlRYoiLPccNtALXQwOMubqw3xhgeiYSYRen3j
    fSFzxix0KYcug\n9+9t4cDNYm3jpSaaIFDG4gR4Oro1S6P4puKNbzk/A61CF+jqRrMJYQlZzzeH\nNxDQrVsQHF/Q
    RjCRSex9q2wQuG0clwJs9udOleZwbA==\n"
    }

鍵管理

アイテム毎のキーローテート

  1. 共有秘密鍵の漏洩対策を想定して、特定のvaultアイテムのキーをローテートしてみます。
    $ knife vault rotate_keys dbpass_vault db_a
    
    $ knife data bag show dbpass_vault db_a_keys -F json
    Unencrypted data bag detected, ignoring any provided secret options.
    {
      "id": "db_a_keys",
      "admins": [
        "alice"
      ],
      "clients": [
        "hare.wonderland.example.com"
      ],
      "search_query": "name:hare.wonderland.example.com",
      "alice": "FMvSduaPS7qA/kn7f8MXp7GH1ZAARdxoHNYye1Ut0Jbn6xeXIPOfnmetrwG8\nSjhnDs45P4wIgaG
    zTD2zWmxpkAVLDNdCWKT8onLkJyFRa+1Lpj/igHRSU5Kn\nz05NERlXTFo9Qv3CSrEQ1aYUd6otLvgQlm4skWjGsc
    BMeEFl9PHcjhze0Nr0\nDYOlx91zslkeLy0m72FSIBm7XfC6JJ41wUUk1gi3z7ZRrKJ8Ew4avT7dy64B\n66WEehi
    PPKVxDOj0V3qLvucKkVGJ/5F6YUvP6fYD1dHzktv9jvgxSSkamrcW\nC6Wz6UZt2qU5fDQ9v+IycPL7REyoJ7A64H
    7/Qx4q7Q==\n",
      "hare.wonderland.example.com": "LheaK71ZgvIlKCG7OSFCnWtuNImqWbas+ctIucdG0Cl1VwFhNdhB4IY
    NspW2\nN+txNW9i2GwcrbIh6WJn6xBwc7/+DUcxHoMVYFgJI8Q4D8Db8IxkIB6X5eBv\ntmdrOUOKn/r+QviL3Bwh
    cnXG0vHUX5vpb9gPPoHCXtV9/pc7K5MlCyblKf0W\ndE1Sx09zhe9Vy0d156bolSkhRXp2oFz6aBNr3qj03xbSaAy
    EoXIfkaOLekdc\nTwp3XJ03BYXHiJADLH1Wihj5EQjUFJfBZeNU2bq7nZdETtFFJGl2FO25BVBT\n7V1TAr0FYmb6
    jps+xJ0WjAgSsdSNovqpwRjhlOdlPw==\n"
    }

すべてのアイテムのキーローテート

  1. すべてのvaultアイテムのキーをローテートしてみます。
    $ knife vault rotate all keys
    Rotating keys for: dbpass_vault db_a
    Rotating keys for: dbpass_vault db_b
    
    $ knife data bag show dbpass_vault db_a_keys -F json
    Unencrypted data bag detected, ignoring any provided secret options.
    {
      "id": "db_a_keys",
      "admins": [
        "alice"
      ],
      "clients": [
        "hare.wonderland.example.com"
      ],
      "search_query": "name:hare.wonderland.example.com",
      "alice": "taDZSZh6oyUCEsjN4E7Jc1mU6EDCxdOpkHdBA3ALsLzpuzjiv/rXDEpFSlwK\nR0hgIyukNoT82tM
    xqofXj0OXK1shIWdmZs2lRbjEQy6bgL43hi3voNiceLg/\nitu8MdmgoIKraSWvvFjdnLRTPQgIYYK2EpJf8r/MAM
    /NdOxnv0mN1Upo6aZV\nMyolrxb7S+HBUZ1oeA47N7OswG0ysDEaN2fThG1SHG5xZ8pogXtqonzIrKPc\nIjaMfoG
    SPmrDgW5Ia3xZsbKaMjLPUCeRpbqjkwugIo0yxWhquzgzYG61ODih\n8rYmAmyadU8a8KVgE+XrNsapYyAtwIrKQS
    i8L3X8fQ==\n",
      "hare.wonderland.example.com": "mqsj3/3Os1h1ODW/JBhN8bMvuY9+MYu6fq/5PI9JWCUO7UyOfxAv6xx
    Um9MC\nwjXm/s5MvFvW+Tr69rDNiKcBUMg0qDSgs98Iiy+/c8fgDNw2UcuCT5nQ7Ir/\n5W6W7RuV9GvXz9wYu42i
    ewEHjBFja7cwuSx9a2RGY/NLwEfZTGTKElQo0a0M\nJQAm4anyNPXYUjni6D8wMzn+Zhc5n+jwp574Nl2OflIc2rL
    XZYhVA0AMTMpG\nxPwfeJIzI1sv7syBUB/JFexDHElnflZmzFdam802Lbuho8TM8NsrEg6HxzgP\nNiIVGBbWAyT3
    PT8ifi9xbUrBaEhTGeJgIJberfJsmA==\n"
    }

バックアップ

  1. Chefサーバ上の vault をダウンロードしてバックアップします。ダウンロードされるのは暗号化された秘密情報(データ本体と気共有秘密鍵)を格納したJSON形式のファイルですので、そのままでも機密性が保たれていますが、一度アクセスユーザの秘密鍵が漏洩すればセキュアではなくなりますので、アクセス制御された場所に保存するようにしましょう。
    $ knife download data_bags/dbpass_vault/
    Created data_bags/dbpass_vault
    Created data_bags/dbpass_vault/db_a_keys.json
    Created data_bags/dbpass_vault/db_a.json

リストア

  1. knife upload コマンドでChefServerへのリストアが可能です。
    $ knife upload data_bags/dbpass_vault/ 
    Created data_bags/dbpass_vault
    Created data_bags/dbpass_vault/db_a.json
    Created data_bags/dbpass_vault/db_a_keys.json

レシピからの参照

参照テスト

  1. あらためてvaultアイテムを作成します。
    $ knife vault create dbpass_vault db_a '{"root": "HelloChefVault!"}'
  2. cookbooks/drillbook/recipes/hello_chef_vault.rb: 以下のようなテスト用のレシピを用意します。note.pngあくまでテスト用ですので、本物の秘密情報を扱うプロダクションでは少しの動作テストといえども秘密情報をログ記録してはいけません。Chef Reportingが機能している場合、Chefサーバにログが永続化されます。
    1. pkg = 'chef-vault'
    2. resources(:chef_gem => pkg) rescue chef_gem pkg do
    3.   compile_time true if respond_to?(:compile_time)
    4.   action :install
    5. end
    6.  
    7. require 'chef-vault'
    8.  
    9. item = ChefVault::Item.load('dbpass_vault', 'db_a')
    10.  
    11. log "Hello Chef Vault! dbpass_vault/db_a.root: #{item['root']}" do
    12.   level :info
    13. end
  3. クックブックをアップロードします。
    $ knife cookbook upload drillbook
    Uploading drillbook    [0.1.0]
    Uploaded 1 cookbook.
  4. roles/hello_chef_vault.rb: 次に先ほどのレシピを適用するロールを用意します。
    1. name 'hello_chef_vault'
    2. description 'Hello Chef Vault!'
    3.  
    4. run_list(
    5.   'recipe[drillbook::hello_chef_vault]',
    6. )
  5. このロールをアップロードします。
    $ knife role from file roles/hello_chef_vault.rb
    Updated Role hello_chef_vault!
  6. 対象ノードの run_list にこのロールを追加します。
    $ knife node run_list add hare.wonderland.example.com 'role[hello_chef_vault]'
    hare.wonderland.example.com:
      run_list: role[hello_chef_vault]
    $ knife node run_list add hatter.wonderland.example.com 'role[hello_chef_vault]'
    hatter.wonderland.example.com:
      run_list: role[hello_chef_vault]
  7. ロールによる検索クエリで、きちんと対象ノードが検索されることを確認してから、vaultアイテムへのアクセス権を付与します。
    $ knife search node 'role:hello_chef_vault' -i
    2 items found
    
    hare.wonderland.example.com
    hatter.wonderland.example.com
    
    $ knife vault update dbpass_vault db_a -S 'role:hello_chef_vault'
    
    $ knife data bag show dbpass_vault db_a_keys -F json
    Unencrypted data bag detected, ignoring any provided secret options.
    {
      "id": "db_a_keys",
      "admins": [
        "alice"
      ],
      "clients": [
        "hatter.wonderland.example.com",
        "hare.wonderland.example.com"
      ],
      "search_query": "role:hello_chef_vault",
      "alice": "hVASeoa74ww9Gj1wswN58JXNdTaZG7gtdnS9dX0NgGemAePbeTqKyZIbnhtd\nZLIvsZKqc/WwGNW
    fEJ1cRfGMBn+lfCnMYVhC0ah0+rROR3fUbZsgb9uAi6NF\nOoPIpKADKVjH02z/m9WuhaP3MV57cmulBok/h1d+zk
    RtvXuFRjCXeTAvnLlw\nxvrZ9rmng0UvycvwyHk7cwvI9HK3lnFJHkt4L5yrMEnqRtvV13Eme/9xFkXv\nm1V55nY
    0RMkan/MD3F7OJAsXq4mne4JFngrag1THR8AYiRywlbnzjZH0i+Hy\nE1mPPfmSVMaV+Z+W7AVuIyNozQ895O+Kcj
    UOMkp2qw==\n",
      "hatter.wonderland.example.com": "LpnD85zGoje/yf+eSVmgzme9byx6MNf1Rtt4/ygGdorPwj0SrhN5M
    NchzKAI\n/cmioMKEF+TZJ/IjRccTp7/fAaJa7PtqExhbimOG2tGv3EO+y0I3wXmvhaEr\n8pZAIDrpTmNT1Es8su
    gMA+jZ22aSPJt2vpYS6j/o0a0qPLxMg7oVnj0WSGE+\nzQUr84QV6w1TPQE6RTwBqDQzwGVxHVh2Vhall0HtMMJJh
    AfOYAis82aQHApx\ntQ7rAD+LyyyZL3yfwODeh5KDRUZiR8i/Hai6LQgjVM6zgvGtsbvbMgZ27rUp\ngoXiRZxhaz
    EMU4vMRYdWYH8CgkrYJJphoSOFAruJbA==\n",
      "hare.wonderland.example.com": "sGz72pPcZrkFGEo3EVoSHUfNy0NmHErA7tTBTzRkGapryF+ta9b6TBz
    /cZr3\nJx8WaaY6fTFbDEdrrFMro/DFCM2MudFoxakmL5B/truUBo409Xh2b3riqIBi\nYoRrj2ty7sxD39YPEMkS
    Cdmn41s8Rfg6FpRVKCBXTGdfHNVu080if/7X/lPI\nJ2e+3tid6D9UZ6yGKLIbDFNWisX0tvgb6nEri01zFNKoPUA
    +WsFIiN597PmW\nQiMYwTUsSuzU2FPhDFiFHuslV+qdYd4ugPb8eoCi55nU1Qo6KkQ9ovUanCiy\nOJY+JUvgNwFk
    ry8/VRTBuaKCkoa75bKg+nxgAz5AjA==\n"
    }
  8. まず一つのノードを指定して収束させます。クックブックのコンパイル時に chef-vault パッケージがインストールされ、秘密情報が参照できていることが確認できます。
    $ knife ssh -m hare.wonderland.example.com 'sudo chef-client'
    hare.wonderland.example.com knife sudo password:
    Enter your password:
    hare.wonderland.example.com
    hare.wonderland.example.com Starting Chef Client, version 12.5.1
    hare.wonderland.example.com resolving cookbooks for run list: ["drillbook::hello_chef_vault"]
    hare.wonderland.example.com Synchronizing Cookbooks:
    hare.wonderland.example.com   - drillbook (0.1.0)
    hare.wonderland.example.com Compiling Cookbooks...
    hare.wonderland.example.com Recipe: drillbook::hello_chef_vault
    hare.wonderland.example.com   * chef_gem[chef-vault] action install
    hare.wonderland.example.com     - install version 2.6.1 of package chef-vault
    hare.wonderland.example.com   Converging 2 resources
    hare.wonderland.example.com   * chef_gem[chef-vault] action install (up to date)
    hare.wonderland.example.com   * log[Hello Chef Vault! dbpass_vault/db_a.root: HelloChefVault!] action write
    hare.wonderland.example.com
    hare.wonderland.example.com
    hare.wonderland.example.com Running handlers:
    hare.wonderland.example.com Running handlers complete
    hare.wonderland.example.com Chef Client finished, 2/3 resources updated in 08 seconds
  9. 次にロールによる検索クエリを指定して対象ノード群を一気に収束させてみます。並列実行されますのでログ出力が混在しますが、正しく参照できていることが確認できます。
    $ knife ssh 'role:hello_chef_vault' 'sudo chef-client'
    hare.wonderland.example.com   knife sudo password:
    Enter your password:
    hatter.wonderland.example.com knife sudo password:
    hatter.wonderland.example.com
    hare.wonderland.example.com
    hare.wonderland.example.com   Starting Chef Client, version 12.5.1
    hare.wonderland.example.com   resolving cookbooks for run list: ["drillbook::hello_chef_vault"]
    hare.wonderland.example.com   Synchronizing Cookbooks:
    hare.wonderland.example.com     - drillbook (0.1.0)
    hare.wonderland.example.com   Compiling Cookbooks...
    hare.wonderland.example.com   Recipe: drillbook::hello_chef_vault
    hare.wonderland.example.com     * chef_gem[chef-vault] action install (up to date)
    hatter.wonderland.example.com Starting Chef Client, version 12.5.1
    hare.wonderland.example.com     Converging 2 resources
    hare.wonderland.example.com     * chef_gem[chef-vault] action install (up to date)
    hare.wonderland.example.com     * log[Hello Chef Vault! dbpass_vault/db_a.root: HelloChefVault!] action write
    hare.wonderland.example.com
    hare.wonderland.example.com
    hare.wonderland.example.com   Running handlers:
    hare.wonderland.example.com   Running handlers complete
    hare.wonderland.example.com   Chef Client finished, 1/3 resources updated in 06 seconds
    hatter.wonderland.example.com resolving cookbooks for run list: ["drillbook::hello_chef_vault"]
    hatter.wonderland.example.com Synchronizing Cookbooks:
    hatter.wonderland.example.com   - drillbook (0.1.0)
    hatter.wonderland.example.com Compiling Cookbooks...
    hatter.wonderland.example.com Recipe: drillbook::hello_chef_vault
    hatter.wonderland.example.com   * chef_gem[chef-vault] action install
    hatter.wonderland.example.com     - install version 2.6.1 of package chef-vault
    hatter.wonderland.example.com   Converging 2 resources
    hatter.wonderland.example.com   * chef_gem[chef-vault] action install (up to date)
    hatter.wonderland.example.com   * log[Hello Chef Vault! dbpass_vault/db_a.root: HelloChefVault!] action write
    hatter.wonderland.example.com
    hatter.wonderland.example.com
    hatter.wonderland.example.com Running handlers:
    hatter.wonderland.example.com Running handlers complete
    hatter.wonderland.example.com Chef Client finished, 2/3 resources updated in 08 seconds

ファイル(秘密鍵等)としての配備

  1. サーバ秘密鍵をJSON形式に変換したファイルを用意します。
    $ ruby -rjson -e 'puts JSON.generate({"private" => File.read("berks-api_server.key")})' \
    > > berks-api_server.key.json
  2. そのファイルでvaultアイテムを作成し、内容を確認しておきます。
    $ knife vault create ssl_server_keys berks-api.grid.example.com \
    > --json ~/tmp/berks-api_server.key.json
    
    $ knife vault show ssl_server_keys berks-api.grid.example.com -F json
  3. 配備対象のノードを確認してから、参照権限を付与します。
    $ knife search node 'name:berks-api*.grid.example.com' -i
    2 items found
    
    berks-api00.grid.example.com
    berks-api01.grid.example.com
    
    $ knife vault update ssl_server_keys berks-api.grid.example.com -S 'name:berks-api*.grid.example.com'
  4. レシピは以下のような内容で作成し、対象ノードに適用されるように run_list を編集します。note.png平文の秘密情報がエコーされたり、ログ記録されないように、必ずリソース(execute、file、template)の sensitive プロパティに true を設定します。
    1. pkg = 'chef-vault'
    2. resources(:chef_gem => pkg) rescue chef_gem pkg do
    3.   compile_time true if respond_to?(:compile_time)
    4.   action :install
    5. end
    6.  
    7. require 'chef-vault'
    8. item = ChefVault::Item.load('ssl_server_keys', 'berks-api.grid.example.com')
    9. secret = item['private']
    10.  
    11. file '/path/to/berks-api_server.key' do
    12.   content secret
    13.   sensitive true
    14.   owner 'root'
    15.   group 'root'
    16.   mode 0400
    17.   notifies :reload, 'service[nginx]', :delayed
    18. end
    • 収束させると以下のようなログが出力されます。
      $ sudo chef-client
      ...
        * chef_gem[chef-vault] action install (up to date)
        * file[/path/to/berks-api_server.key] action create
          - create new file /path/to/berks-api_server.key
          - update content in file /path/to/berks-api_server.key from none to b729b5
          - suppressed sensitive resource
          - change mode from '' to '0400'
          - change owner from '' to 'root'
          - change group from '' to 'root'
      ...

ssl_cert クックブック

  • サーバ秘密鍵や証明書の配布は頻繁に行われる運用ですので、クックブックを作成してみました。
  • デフォルト設定時のvaultアイテムと配備先ファイルパスの対応は以下のようになります。CA証明書は、自動でシステムレベルにも登録されます。
    No.項目設定名(例)ソースvaultアイテムアイテムキー配備先ファイルパス
    1CA証明書'grid_ca'"ca_certs/grid_ca.#{node.chef_environment}"'public'debian.png'/etc/ssl/certs/grid_ca.crt'
    redhat.png'/etc/pki/tls/certs/grid_ca.crt'
    2サーバ秘密鍵'node.example.com'"ssl_server_keys/node.example.com.#{node.chef_environment}"'private'debian.png'/etc/ssl/private/node_example_com.key'
    redhat.png'/etc/pki/tls/private/node_example_com.key'
    3サーバ証明書'node.example.com'"ssl_server_certs/node.example.com.#{node.chef_environment}"'public'debian.png'/etc/ssl/certs/node_example_com.crt'
    redhat.png'/etc/pki/tls/certs/node_example_com.crt'
  1. 命名規約に従い、CA証明書、サーバ秘密鍵および証明書のvaultアイテムを作成し、配布先のクライアントにアクセス権を付与します。以下の例では、ノードが prod environment に属しているものとします。
    $ ruby -rjson -e 'puts JSON.generate({"public" => File.read("grid_ca.prod.crt")})' \
    > > ~/tmp/grid_ca.prod.crt.json
    $ knife vault create ca_certs grid_ca.prod \
    > --json ~/tmp/grid_ca.prod.crt.json
    $ knife vault update ca_certs grid_ca.prod -S 'name:*.example.com'
    
    $ ruby -rjson -e 'puts JSON.generate({"private" => File.read("node_example_com.prod.key")})' \
    > > ~/tmp/node_example_com.prod.key.json
    $ knife vault create ssl_server_keys node.example.com.prod \
    > --json ~/tmp/node_example_com.prod.key.json
    $ knife vault update ssl_server_keys node.example.com.prod -S 'name:node.example.com'
    
    $ ruby -rjson -e 'puts JSON.generate({"public" => File.read("node_example_com.prod.crt")})' \
    > > ~/tmp/node_example_com.prod.crt.json
    $ knife vault create ssl_server_certs node.example.com.prod \
    > --json ~/tmp/node_example_com.prod.crt.json
    $ knife vault update ssl_server_certs node.example.com.prod -S 'name:node.example.com'
  2. 適当な配布用レシピを run_list に追加します。
    1. run_list(
    2.   'recipe[ssl_cert::ca_certs]',           # CA証明書の配布
    3.   'recipe[ssl_cert::server_keys]',        # サーバ秘密鍵の配布
    4.   'recipe[ssl_cert::server_certs]',       # サーバ証明書の配布
    5.   #'recipe[ssl_cert::server_key_pairs]',  # server_keys + server_certs と同じ
    6. )
  3. ノードに配布するCA名またはサーバ名(Common Name)を属性に設定します。
    1. override_attributes(
    2.   'ssl_cert' => {
    3.     'ca_names' => [
    4.       'grid_ca',
    5.       # ...
    6.     ],
    7.     'common_names' => [
    8.       'node.example.com',
    9.       # ...
    10.     ],
    11.   },
    12. )
  4. 収束させると、設定したCA名、サーバ名に対応したvaultアイテムが参照され、ファイルとして配布されます。サーバ秘密鍵については、ログ記録等されない設定になっています。それぞれの配布先ファイルパスは、以下の命名規約による属性で他のレシピからも参照できます。また、CA証明書名やサーバのFQDNを引数にそれぞれのファイルパスを返すメソッドも使用できます。
    No.項目ファイルパス参照のための属性属性参照例メソッドアクセス例
    1CA証明書node['ssl_cert']["#{ca}_cert_path"]node['ssl_cert']['grid_ca_cert_path']ca_cert_path('grid_ca')
    2サーバ秘密鍵node['ssl_cert']["#{undotted_cn}_key_path"]node['ssl_cert']['node_example_com_key_path']server_key_path('node.example.com')
    3サーバ証明書node['ssl_cert']["#{undotted_cn}_cert_path"]node['ssl_cert']['node_example_com_cert_path']server_cert_path('node.example.com')
    • メソッド利用時には、以下の要領でインクルードしておきます。
      1. ::Chef::Recipe.send(:include, SSLCert::Helper)

リソース


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2017-06-29 (木) 23:09:50 (1614d)