From e44235b049f5de13e76cb12a2a1fa5b10c9b3b6b Mon Sep 17 00:00:00 2001 From: whitestar Date: Sun, 3 Jan 2016 22:48:00 +0900 Subject: [PATCH] add ssl_cert cookbook. --- cookbooks/ssl_cert/CHANGELOG.md | 7 ++ cookbooks/ssl_cert/README.md | 126 +++++++++++++++++++ cookbooks/ssl_cert/attributes/default.rb | 102 ++++++++++++++++ cookbooks/ssl_cert/libraries/helper.rb | 135 +++++++++++++++++++++ cookbooks/ssl_cert/metadata.rb | 7 ++ .../recipes/ca_certs.rb} | 18 ++- cookbooks/ssl_cert/recipes/default.rb | 22 ++++ cookbooks/ssl_cert/recipes/server_certs.rb | 25 ++++ cookbooks/ssl_cert/recipes/server_key_pairs.rb | 22 ++++ cookbooks/ssl_cert/recipes/server_keys.rb | 25 ++++ cookbooks/ssl_cert/spec/recipes/default_spec.rb | 20 +++ cookbooks/ssl_cert/spec/spec_helper.rb | 25 ++++ roles/grid-ns.rb | 13 +- 13 files changed, 535 insertions(+), 12 deletions(-) create mode 100644 cookbooks/ssl_cert/CHANGELOG.md create mode 100644 cookbooks/ssl_cert/README.md create mode 100644 cookbooks/ssl_cert/attributes/default.rb create mode 100644 cookbooks/ssl_cert/libraries/helper.rb create mode 100644 cookbooks/ssl_cert/metadata.rb rename cookbooks/{grid/recipes/cacert_deploy.rb => ssl_cert/recipes/ca_certs.rb} (70%) create mode 100644 cookbooks/ssl_cert/recipes/default.rb create mode 100644 cookbooks/ssl_cert/recipes/server_certs.rb create mode 100644 cookbooks/ssl_cert/recipes/server_key_pairs.rb create mode 100644 cookbooks/ssl_cert/recipes/server_keys.rb create mode 100644 cookbooks/ssl_cert/spec/recipes/default_spec.rb create mode 100644 cookbooks/ssl_cert/spec/spec_helper.rb diff --git a/cookbooks/ssl_cert/CHANGELOG.md b/cookbooks/ssl_cert/CHANGELOG.md new file mode 100644 index 0000000..f9233c3 --- /dev/null +++ b/cookbooks/ssl_cert/CHANGELOG.md @@ -0,0 +1,7 @@ +ssl_cert CHANGELOG +================== + +0.1.0 +----- +- Initial release of ssl_cert + diff --git a/cookbooks/ssl_cert/README.md b/cookbooks/ssl_cert/README.md new file mode 100644 index 0000000..d9e3337 --- /dev/null +++ b/cookbooks/ssl_cert/README.md @@ -0,0 +1,126 @@ +ssl_cert Cookbook +================= + +This cookbook deploys CA certificates, SSL server keys and/or certificates from Chef Vault items. + +Requirements +------------ + +#### packages +- nothing. + +Attributes +---------- + +#### ssl_cert::default + +|Key|Type|Description, example|Default| +|:--|:--|:--|:--| +|`['ssl_cert']['ca_names']`|Array|deployed CA certificates from chef-vault|empty| +|`['ssl_cert']['common_names']`|Array|deployed server keys and/or certificates from chef-vault|empty| +|`['ssl_cert']['chef_gem']['clear_sources']`|Boolean|chef_gem resource's clear_sources property.|`false`| +|`['ssl_cert']['chef_gem']['source']`|String|chef_gem resource's source property.|`nil`| +|`['ssl_cert']['chef_gem']['options']`|String|chef_gem resource's options property.|`nil`| +|`['ssl_cert']['chef-vault']['version']`|String|chef-vault installation version.|`'~> 2.6'`| +|`['ssl_cert']['env_context']`|String|node's environment.|`node.chef_environment`| +|`['ssl_cert']['ca_cert_vault']`|String|CA certificate stored vault name.|`'ca_certs'`| +|`['ssl_cert']['server_key_vault']`|String|SSL server key stored vault name.|`'ssl_server_keys'`| +|`['ssl_cert']['server_cert_vault']`|String|SSL server certificate stored vault name.|`'ssl_server_certs'`| +|`['ssl_cert']["#{ca}_cert_path"]`|String|deployed CA certificate file path.|`"#{node['ssl_cert']['certs_dir']}/00#{ca}.crt"`| +|`['ssl_cert']["#{undotted_cn}_key_path"]`|String|deployed SSL server key file path.|`"#{node['ssl_cert']['private_dir']}/01#{undotted_cn}.key"`| +|`['ssl_cert']["#{undotted_cn}_cert_path"]`|String|deployed SSL server certificate file path.|`"#{node['ssl_cert']['certs_dir']}/01#{undotted_cn}.crt"`| + +Usage +----- + +### recipes +- `ssl_cert::default` - deploys CA certificates, SSL server keys and/or certificates. +- `ssl_cert::ca_certs` - deploys CA certificates. +- `ssl_cert::server_key_pairs` - deploys SSL server keys and certificates. +- `ssl_cert::server_keys` - deploys SSL server keys. +- `ssl_cert::server_certs` - deploys SSL server certificates. + +### Vault items creation and cookbook attribute settings (with default attributes) + +#### CA certificates + +- create vault items. + +```text +$ 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 +``` + +- add cookbook attributes. + +```ruby +override_attributes( + 'ssl_cert' => { + 'ca_names' => [ + 'grid_ca', + # ... + ], + }, +) +``` + +#### SSL server keys and certificates + +- create vault items. + +```text +$ 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 + +$ 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 +``` + +- add cookbook attributes + +```ruby +override_attributes( + 'ssl_cert' => { + 'common_names' => [ + 'node.example.com', + # ... + ], + }, +) +``` + +### References of deployed key and certificate file paths (with default attributes) + +- `node['ssl_cert']["#{ca}_cert_path"]` - e.g. `node['ssl_cert']['grid_ca_cert_path']` +- `node['ssl_cert']["#{undotted_cn}_key_path"]` - e.g. `node['ssl_cert']['node_example_com_key_path']` +- `node['ssl_cert']["#{undotted_cn}_cert_path"]` - e.g. `node['ssl_cert']['node_example_com_cert_path']` + +License and Authors +------------------- +- Author:: whitestar at osdn.jp + +```text +Copyright 2016, whitestar + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + diff --git a/cookbooks/ssl_cert/attributes/default.rb b/cookbooks/ssl_cert/attributes/default.rb new file mode 100644 index 0000000..1ae053f --- /dev/null +++ b/cookbooks/ssl_cert/attributes/default.rb @@ -0,0 +1,102 @@ +# +# Cookbook Name:: ssl_cert +# Attributes:: default +# +# Copyright 2016, whitestar +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# deployed CA certificates from chef-vault +default['ssl_cert']['ca_names'] = [ + #'grid_ca', +] + +# deployed server keys and/or certificates from chef-vault +default['ssl_cert']['common_names'] = [ + #'ldap.grid.example.com', +] + +# for chef-vault installation +default['ssl_cert']['chef_gem']['clear_sources'] = false +default['ssl_cert']['chef_gem']['source'] = nil +default['ssl_cert']['chef_gem']['options'] = nil +default['ssl_cert']['chef-vault']['version'] = '~> 2.6' + +default['ssl_cert']['env_context'] = node.chef_environment + +default['ssl_cert']['ca_cert_vault'] = 'ca_certs' +=begin + CA certificate vault item name is + each CA name + ".#{node['ssl_cert']['env_context']}". + valut item key is 'public'. + + * vault item management + + $ ruby -rjson -e 'puts JSON.generate({"public" => File.read("grid_ca.prod.crt")})' \ + > > grid_ca.prod.crt.json + $ knife vault create ca_certs grid_ca.prod \ + > --json ~/tmp/grid_ca.prod.crt.json +=end + +default['ssl_cert']['server_key_vault'] = 'ssl_server_keys' +=begin + server key vault item name is + each common name + ".#{node['ssl_cert']['env_context']}". + valut item key is 'private'. + + * vault item management + + $ ruby -rjson -e 'puts JSON.generate({"private" => File.read("node_example_com.prod.key")})' \ + > > node_example_com.prod.key.json + $ knife vault create ssl_server_keys node.example.com.prod \ + > --json node_example_com.prod.key.json +=end + +default['ssl_cert']['server_cert_vault'] = 'ssl_server_certs' +=begin + server certificate vault item name is + each common name + ".#{node['ssl_cert']['env_context']}". + valut item key is 'public'. + + * vault item management + + $ ruby -rjson -e 'puts JSON.generate({"public" => File.read("node_example_com.prod.crt")})' \ + > > node_example_com.prod.crt.json + $ knife vault create ssl_server_certs node.example.com.prod \ + > --json node_example_com.prod.crt.json +=end + +undotted_cns = node['ssl_cert']['common_names'].map {|item| + item.gsub('.', '_') +} + +default['ssl_cert']['certs_dir'] = node.value_for_platform_family( + 'debian' => '/etc/ssl/certs', + 'rhel' => '/etc/pki/tls/certs' +) + +default['ssl_cert']['private_dir'] = node.value_for_platform_family( + 'debian' => '/etc/ssl/private', + 'rhel' => '/etc/pki/tls/private' +) + +node['ssl_cert']['ca_names'].each {|ca| + default['ssl_cert']["#{ca}_cert_path"] = "#{node['ssl_cert']['certs_dir']}/00#{ca}.crt" +} + +undotted_cns.each {|cn| + default['ssl_cert']["#{cn}_key_path"] = "#{node['ssl_cert']['private_dir']}/01#{cn}.key" + default['ssl_cert']["#{cn}_cert_path"] = "#{node['ssl_cert']['certs_dir']}/01#{cn}.crt" +} + diff --git a/cookbooks/ssl_cert/libraries/helper.rb b/cookbooks/ssl_cert/libraries/helper.rb new file mode 100644 index 0000000..ac00ded --- /dev/null +++ b/cookbooks/ssl_cert/libraries/helper.rb @@ -0,0 +1,135 @@ +# +# Cookbook Name:: ssl_cert +# Library:: Helper +# +# Copyright 2016, whitestar +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +module SSLCert + +module Helper + def ssl_cert_pkg + pkg = nil + + case node[:platform_family] + when 'debian' + pkg = 'ssl-cert' + when 'rhel' + pkg = nil + end + + if !pkg.nil? then + resources(:package => pkg) rescue package pkg do + action :install + end + end + end + + + def get_private_key_group + node.value_for_platform_family( + 'debian' => 'ssl-cert', + 'rhel' => 'root', + 'default' => 'root', + ) + end + + + def get_private_key_mode + node.value_for_platform_family( + 'debian' => 0640, + 'rhel' => 0400, + 'default' => 0400, + ) + end + + + def chef_gem_chef_vault + pkg = 'chef-vault' + resources(:chef_gem => pkg) rescue chef_gem pkg do + compile_time true if respond_to?(:compile_time) + clear_sources node['ssl_cert']['chef_gem']['clear_sources'] + source node['ssl_cert']['chef_gem']['source'] + options node['ssl_cert']['chef_gem']['options'] + version node['ssl_cert']['chef-vault']['version'] + action :install + end + end + + + def ca_certificate(ca) + undotted_ca = ca.gsub('.', '_') + + chef_gem_chef_vault + require 'chef-vault' + cert = ChefVault::Item.load( + node['ssl_cert']['ca_cert_vault'], + "#{ca}.#{node['ssl_cert']['env_context']}")['public'] + + cert_path = node['ssl_cert']["#{undotted_ca}_cert_path"] + resources(:file => cert_path) rescue file cert_path do + content cert + owner 'root' + group 'root' + mode 0644 + end + end + + + def server_certificate(cn) + undotted_cn = cn.gsub('.', '_') + + chef_gem_chef_vault + require 'chef-vault' + cert = ChefVault::Item.load( + node['ssl_cert']['server_cert_vault'], + "#{cn}.#{node['ssl_cert']['env_context']}")['public'] + + cert_path = node['ssl_cert']["#{undotted_cn}_cert_path"] + resources(:file => cert_path) rescue file cert_path do + content cert + owner 'root' + group 'root' + mode 0644 + end + end + + + def server_private_key(cn) + undotted_cn = cn.gsub('.', '_') + + ssl_cert_pkg + + chef_gem_chef_vault + require 'chef-vault' + secret = ChefVault::Item.load( + node['ssl_cert']['server_key_vault'], + "#{cn}.#{node['ssl_cert']['env_context']}")['private'] + + key_path = node['ssl_cert']["#{undotted_cn}_key_path"] + key_group = get_private_key_group + key_mode = get_private_key_mode + resources(:file => key_path) rescue file key_path do + content secret + sensitive true + owner 'root' + group key_group + mode key_mode + end + end +end + +end + diff --git a/cookbooks/ssl_cert/metadata.rb b/cookbooks/ssl_cert/metadata.rb new file mode 100644 index 0000000..a2f4011 --- /dev/null +++ b/cookbooks/ssl_cert/metadata.rb @@ -0,0 +1,7 @@ +name 'ssl_cert' +maintainer 'whitestar' +maintainer_email '' +license 'Apache 2.0' +description 'Installs/Configures ssl_cert' +long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) +version '0.1.0' diff --git a/cookbooks/grid/recipes/cacert_deploy.rb b/cookbooks/ssl_cert/recipes/ca_certs.rb similarity index 70% rename from cookbooks/grid/recipes/cacert_deploy.rb rename to cookbooks/ssl_cert/recipes/ca_certs.rb index b0a6b2b..d7670ee 100644 --- a/cookbooks/grid/recipes/cacert_deploy.rb +++ b/cookbooks/ssl_cert/recipes/ca_certs.rb @@ -1,8 +1,8 @@ # -# Cookbook Name:: grid -# Recipe:: cacert_deploy +# Cookbook Name:: ssl_cert +# Recipe:: ca_certs # -# Copyright 2013, whitestar +# Copyright 2016, whitestar # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,11 +17,9 @@ # limitations under the License. # -cookbook_file node['grid']['cacert']['path'] do - source node['grid']['cacert']['source'] - owner 'root' - group 'root' - mode '0644' - action :create -end +::Chef::Recipe.send(:include, SSLCert::Helper) + +node['ssl_cert']['ca_names'].each {|ca| + ca_certificate(ca) +} diff --git a/cookbooks/ssl_cert/recipes/default.rb b/cookbooks/ssl_cert/recipes/default.rb new file mode 100644 index 0000000..b5e39f4 --- /dev/null +++ b/cookbooks/ssl_cert/recipes/default.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: ssl_cert +# Recipe:: default +# +# Copyright 2016, whitestar +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "ssl_cert::ca_certs" +include_recipe "ssl_cert::server_key_pairs" + diff --git a/cookbooks/ssl_cert/recipes/server_certs.rb b/cookbooks/ssl_cert/recipes/server_certs.rb new file mode 100644 index 0000000..81324ee --- /dev/null +++ b/cookbooks/ssl_cert/recipes/server_certs.rb @@ -0,0 +1,25 @@ +# +# Cookbook Name:: ssl_cert +# Recipe:: server_certs +# +# Copyright 2016, whitestar +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +::Chef::Recipe.send(:include, SSLCert::Helper) + +node['ssl_cert']['common_names'].each {|cn| + server_certificate(cn) +} + diff --git a/cookbooks/ssl_cert/recipes/server_key_pairs.rb b/cookbooks/ssl_cert/recipes/server_key_pairs.rb new file mode 100644 index 0000000..9205710 --- /dev/null +++ b/cookbooks/ssl_cert/recipes/server_key_pairs.rb @@ -0,0 +1,22 @@ +# +# Cookbook Name:: ssl_cert +# Recipe:: server_key_pairs +# +# Copyright 2016, whitestar +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +include_recipe "ssl_cert::server_keys" +include_recipe "ssl_cert::server_certs" + diff --git a/cookbooks/ssl_cert/recipes/server_keys.rb b/cookbooks/ssl_cert/recipes/server_keys.rb new file mode 100644 index 0000000..daa92f5 --- /dev/null +++ b/cookbooks/ssl_cert/recipes/server_keys.rb @@ -0,0 +1,25 @@ +# +# Cookbook Name:: ssl_cert +# Recipe:: server_keys +# +# Copyright 2016, whitestar +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +::Chef::Recipe.send(:include, SSLCert::Helper) + +node['ssl_cert']['common_names'].each {|cn| + server_private_key(cn) +} + diff --git a/cookbooks/ssl_cert/spec/recipes/default_spec.rb b/cookbooks/ssl_cert/spec/recipes/default_spec.rb new file mode 100644 index 0000000..5419fad --- /dev/null +++ b/cookbooks/ssl_cert/spec/recipes/default_spec.rb @@ -0,0 +1,20 @@ +require_relative '../spec_helper' + +describe 'ssl_cert::default' do + subject { ChefSpec::Runner.new.converge(described_recipe) } + + # Write quick specs using `it` blocks with implied subjects + it { should do_something('...') } + + # Write full examples using the `expect` syntax + it 'does something' do + expect(subject).to do_something('...') + end + + # Use an explicit subject + let(:chef_run) { ChefSpec::Runner.new.converge(described_recipe) } + + it 'does something' do + expect(chef_run).to do_something('...') + end +end diff --git a/cookbooks/ssl_cert/spec/spec_helper.rb b/cookbooks/ssl_cert/spec/spec_helper.rb new file mode 100644 index 0000000..1995e2b --- /dev/null +++ b/cookbooks/ssl_cert/spec/spec_helper.rb @@ -0,0 +1,25 @@ +# Added by ChefSpec +require 'chefspec' + +# Uncomment to use ChefSpec's Berkshelf extension +# require 'chefspec/berkshelf' + +RSpec.configure do |config| + # Specify the path for Chef Solo to find cookbooks + # config.cookbook_path = '/var/cookbooks' + + # Specify the path for Chef Solo to find roles + # config.role_path = '/var/roles' + + # Specify the Chef log_level (default: :warn) + # config.log_level = :debug + + # Specify the path to a local JSON file with Ohai data + # config.path = 'ohai.json' + + # Specify the operating platform to mock Ohai data from + # config.platform = 'ubuntu' + + # Specify the operating version to mock Ohai data from + # config.version = '12.04' +end diff --git a/roles/grid-ns.rb b/roles/grid-ns.rb index 1e9c3e0..67a228e 100644 --- a/roles/grid-ns.rb +++ b/roles/grid-ns.rb @@ -1,5 +1,5 @@ # -# Copyright 2013-2014, whitestar +# Copyright 2013-2016, whitestar # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -24,11 +24,12 @@ description 'Grid nameservice node' run_list( # TODO: 'role[node_commons]', + 'recipe[ssl_cert::default]', 'role[grid-realm]', 'role[ganglia-gmond]' ) -#env_run_lists "prod" => ["recipe[apache2]"], "staging" => ["recipe[apache2::staging]"], "_default" => [] +#env_run_lists() this_subcluster = Grid::CLUSTERS[:base][:nameservices] mcast_addr = this_subcluster[:mcast_addr] @@ -52,6 +53,14 @@ default_attributes( ) override_attributes( + 'ssl_cert' => { + 'ca_names' => [ + 'grid_ca', + ], + 'common_names' => [ + 'ldap.grid.example.com', + ], + }, 'ganglia' => { # gmond 'cluster' => { -- 2.11.0