summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2018-07-30 18:06:17 -0700
committerTim Smith <tsmith@chef.io>2018-08-14 11:23:59 -0700
commitd0f19a989b93754b3f781e00ee334866dde4e7b0 (patch)
treed47fc22b973910fca448c4d890aa5309a614bf72
parentb9a1f16e85fd5fee48f7e03799828046251b6dc5 (diff)
downloadchef-d0f19a989b93754b3f781e00ee334866dde4e7b0.tar.gz
Add new openssl_* resources
openssl_ec_private_key openssl_ec_public_key openssl_x509_certificate openssl_x509_request Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r--lib/chef/resource/openssl_ec_private_key.rb90
-rw-r--r--lib/chef/resource/openssl_ec_public_key.rb74
-rw-r--r--lib/chef/resource/openssl_x509_certificate.rb164
-rw-r--r--lib/chef/resource/openssl_x509_request.rb144
-rw-r--r--lib/chef/resources.rb4
5 files changed, 476 insertions, 0 deletions
diff --git a/lib/chef/resource/openssl_ec_private_key.rb b/lib/chef/resource/openssl_ec_private_key.rb
new file mode 100644
index 0000000000..b0c509ac27
--- /dev/null
+++ b/lib/chef/resource/openssl_ec_private_key.rb
@@ -0,0 +1,90 @@
+#
+# Copyright:: Copyright 2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# 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.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class OpensslEcPrivateKey < Chef::Resource
+ require "chef/mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ resource_name :openssl_ec_private_key
+ provides(:openssl_ec_private_key) { true }
+
+ description "Use the openssl_ec_private_key resource to generate..."
+ introduced "14.4"
+
+ property :path, String,
+ description: "The path to write the file to it's different than the resource name.",
+ name_property: true
+
+ property :key_curve, String,
+ equal_to: %w{secp384r1 secp521r1 prime256v1 secp224r1 secp256k1},
+ description: "The desired curve of the generated key (if key_type is equal to 'ec'). Run openssl ecparam -list_curves to see available options.",
+ default: "prime256v1"
+
+ property :key_pass, String,
+ description: "The desired passphrase for the key."
+
+ property :key_cipher, String,
+ equal_to: OpenSSL::Cipher.ciphers,
+ validation_message: "key_cipher must be a cipher known to openssl. Run `openssl list-cipher-algorithms` to see available options.",
+ description: "The designed cipher to use when generating your key. Run `openssl list-cipher-algorithms` to see available options.",
+ default: "des3"
+
+ property :owner, [String, nil],
+ description: "The owner of all files created by the resource."
+
+ property :group, [String, nil],
+ description: "The group of all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode of all files created by the resource.",
+ default: "0600"
+
+ property :force, [TrueClass, FalseClass],
+ description: "Force creating the key even if the existing key exists.",
+ default: false, desired_state: false
+
+ action :create do
+ unless new_resource.force || priv_key_file_valid?(new_resource.path, new_resource.key_pass)
+ converge_by("Create an EC private key #{new_resource.path}") do
+ log "Generating an #{new_resource.key_curve} "\
+ "EC key file at #{new_resource.name}, this may take some time"
+
+ if new_resource.key_pass
+ unencrypted_ec_key = gen_ec_priv_key(new_resource.key_curve)
+ ec_key_content = encrypt_ec_key(unencrypted_ec_key, new_resource.key_pass, new_resource.key_cipher)
+ else
+ ec_key_content = gen_ec_priv_key(new_resource.key_curve).to_pem
+ end
+
+ file new_resource.path do
+ action :create
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode
+ sensitive true
+ content ec_key_content
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/openssl_ec_public_key.rb b/lib/chef/resource/openssl_ec_public_key.rb
new file mode 100644
index 0000000000..6a1b11548f
--- /dev/null
+++ b/lib/chef/resource/openssl_ec_public_key.rb
@@ -0,0 +1,74 @@
+#
+# Copyright:: Copyright 2018, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# 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.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class OpensslEcPublicKey < Chef::Resource
+ require "chef/mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ resource_name :openssl_ec_public_key
+ provides(:openssl_ec_public_key) { true }
+
+ description "Use the openssl_ec_public_key resource to..."
+ introduced "14.4"
+
+ property :path, String,
+ description: "The path to write the file to if different than the resource's name.",
+ name_property: true
+
+ property :private_key_path, String,
+ description: "The path to the private key."
+
+ property :private_key_content, String,
+ description: "The content of the private key including new lines. Used instead of private_key_path to avoid having to first write a key to disk."
+
+ property :private_key_pass, String,
+ description: "The passphrase of the provided private key."
+
+ property :owner, [String, nil],
+ description: "The owner of all files created by the resource."
+
+ property :group, [String, nil],
+ description: "The group of all files created by the resource."
+
+ property :mode, [Integer, String],
+ description: "The permission mode of all files created by the resource.",
+ default: "0640"
+
+ action :create do
+ description ""
+
+ raise ArgumentError, "You cannot specify both 'private_key_path' and 'private_key_content' properties at the same time." if new_resource.private_key_path && new_resource.private_key_content
+ raise ArgumentError, "You must specify the private key with either 'private_key_path' or 'private_key_content' properties." unless new_resource.private_key_path || new_resource.private_key_content
+ raise "#{new_resource.private_key_path} not a valid private RSA key or password is invalid" unless priv_key_file_valid?((new_resource.private_key_path || new_resource.private_key_content), new_resource.private_key_pass)
+
+ ec_key_content = gen_ec_public_key((new_resource.private_key_path || new_resource.private_key_content), new_resource.private_key_pass)
+
+ file new_resource.path do
+ action :create
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode
+ content ec_key_content
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/openssl_x509_certificate.rb b/lib/chef/resource/openssl_x509_certificate.rb
new file mode 100644
index 0000000000..cb50e60fca
--- /dev/null
+++ b/lib/chef/resource/openssl_x509_certificate.rb
@@ -0,0 +1,164 @@
+#
+# License:: Apache License, Version 2.0
+#
+# 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.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class OpensslX509Certificate < Chef::Resource
+ require "chef/mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ resource_name :openssl_x509_certificate
+ provides(:openssl_x509) { true }
+ provides(:openssl_x509_certificate) { true }
+
+ property :path, String, name_property: true
+
+ property :owner, [String, nil],
+ description: "The owner of all files created by the resource."
+
+ property :group, [String, nil],
+ description: "The group of all files created by the resource."
+
+ property :expire, Integer, default: 365
+ property :mode, [Integer, String], default: "0644"
+ property :country, String
+ property :state, String
+ property :city, String
+ property :org, String
+ property :org_unit, String
+ property :common_name, String
+ property :email, String
+ property :extensions, Hash, default: {}
+ property :subject_alt_name, Array, default: []
+ property :key_file, String
+ property :key_pass, String
+ property :key_type, equal_to: %w{rsa ec}, default: "rsa"
+ property :key_length, equal_to: [1024, 2048, 4096, 8192], default: 2048
+ property :key_curve, equal_to: %w{secp384r1 secp521r1 prime256v1}, default: "prime256v1"
+ property :csr_file, String
+ property :ca_cert_file, String
+ property :ca_key_file, String
+ property :ca_key_pass, String
+
+ action :create do
+ unless ::File.exist? new_resource.path
+ converge_by("Create #{@new_resource}") do
+ file new_resource.path do
+ action :create_if_missing
+ mode new_resource.mode
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ sensitive true
+ content cert.to_pem
+ end
+
+ if new_resource.csr_file.nil?
+ file new_resource.key_file do
+ action :create_if_missing
+ mode new_resource.mode
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ sensitive true
+ content key.to_pem
+ end
+ end
+ end
+ end
+ end
+
+ action_class do
+ def generate_key_file
+ unless new_resource.key_file
+ path, file = ::File.split(new_resource.path)
+ filename = ::File.basename(file, ::File.extname(file))
+ new_resource.key_file path + "/" + filename + ".key"
+ end
+ new_resource.key_file
+ end
+
+ def key
+ @key ||= if priv_key_file_valid?(generate_key_file, new_resource.key_pass)
+ OpenSSL::PKey.read ::File.read(generate_key_file), new_resource.key_pass
+ elsif new_resource.key_type == "rsa"
+ gen_rsa_priv_key(new_resource.key_length)
+ else
+ gen_ec_priv_key(new_resource.key_curve)
+ end
+ @key
+ end
+
+ def request
+ request = if new_resource.csr_file.nil?
+ gen_x509_request(subject, key)
+ else
+ OpenSSL::X509::Request.new ::File.read(new_resource.csr_file)
+ end
+ request
+ end
+
+ def subject
+ subject = OpenSSL::X509::Name.new()
+ subject.add_entry("C", new_resource.country) unless new_resource.country.nil?
+ subject.add_entry("ST", new_resource.state) unless new_resource.state.nil?
+ subject.add_entry("L", new_resource.city) unless new_resource.city.nil?
+ subject.add_entry("O", new_resource.org) unless new_resource.org.nil?
+ subject.add_entry("OU", new_resource.org_unit) unless new_resource.org_unit.nil?
+ subject.add_entry("CN", new_resource.common_name)
+ subject.add_entry("emailAddress", new_resource.email) unless new_resource.email.nil?
+ subject
+ end
+
+ def ca_private_key
+ ca_private_key = if new_resource.csr_file.nil?
+ key
+ else
+ OpenSSL::PKey.read ::File.read(new_resource.ca_key_file), new_resource.ca_key_pass
+ end
+ ca_private_key
+ end
+
+ def ca_info
+ # Will contain issuer (if any) & expiration
+ ca_info = {}
+
+ unless new_resource.ca_cert_file.nil?
+ ca_info["issuer"] = OpenSSL::X509::Certificate.new ::File.read(new_resource.ca_cert_file)
+ end
+ ca_info["validity"] = new_resource.expire
+
+ ca_info
+ end
+
+ def extensions
+ extensions = gen_x509_extensions(new_resource.extensions)
+
+ unless new_resource.subject_alt_name.empty?
+ extensions += gen_x509_extensions("subjectAltName" => { "values" => new_resource.subject_alt_name, "critical" => false })
+ end
+
+ extensions
+ end
+
+ def cert
+ cert = gen_x509_cert(request, extensions, ca_info, ca_private_key)
+ cert
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resource/openssl_x509_request.rb b/lib/chef/resource/openssl_x509_request.rb
new file mode 100644
index 0000000000..5357863122
--- /dev/null
+++ b/lib/chef/resource/openssl_x509_request.rb
@@ -0,0 +1,144 @@
+#
+# License:: Apache License, Version 2.0
+#
+# 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.
+#
+
+require "chef/resource"
+
+class Chef
+ class Resource
+ class OpensslX509Certificate < Chef::Resource
+ require "chef/mixin/openssl_helper"
+ include Chef::Mixin::OpenSSLHelper
+
+ resource_name :openssl_x509_request
+ provides(:openssl_x509_request) { true }
+
+ property :path, String, name_property: true,
+ description: "The optional path to write the file to if you'd like to specify it here instead of in the resource name."
+
+ property :owner, [String, nil],
+ description: "The owner of all files created by the resource."
+
+ property :group, [String, nil],
+ description: "The group of all files created by the resource."
+
+ property :mode, [Integer, String], default: "0644",
+ description: ""
+
+ property :country, String,
+ description: ""
+
+ property :state, String,
+ description: ""
+
+ property :city, String,
+ description: ""
+
+ property :org, String,
+ description: ""
+
+ property :org_unit, String,
+ description: ""
+
+ property :common_name, String,
+ required: true,
+ description: ""
+
+ property :email, String,
+ description: ""
+
+ property :key_file, String,
+ description: ""
+
+ property :key_pass, String,
+ description: ""
+
+ property :key_type, String,
+ equal_to: %w{rsa ec}, default: "ec",
+ description: ""
+
+ property :key_length, Integer,
+ equal_to: [1024, 2048, 4096, 8192], default: 2048,
+ description: ""
+
+ property :key_curve, String,
+ equal_to: %w{secp384r1 secp521r1 prime256v1}, default: "prime256v1",
+ description: ""
+
+ default_action :create
+
+ action :create do
+ unless ::File.exist? new_resource.path
+ converge_by("Create CSR #{@new_resource}") do
+ file new_resource.name do
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ mode new_resource.mode
+ content csr.to_pem
+ action :create
+ end
+
+ file new_resource.key_file do
+ mode new_resource.mode
+ owner new_resource.owner unless new_resource.owner.nil?
+ group new_resource.group unless new_resource.group.nil?
+ content key.to_pem
+ sensitive true
+ action :create_if_missing
+ end
+ end
+ end
+ end
+
+ action_class do
+ def generate_key_file
+ unless new_resource.key_file
+ path, file = ::File.split(new_resource.path)
+ filename = ::File.basename(file, ::File.extname(file))
+ new_resource.key_file path + "/" + filename + ".key"
+ end
+ new_resource.key_file
+ end
+
+ def key
+ @key ||= if priv_key_file_valid?(generate_key_file, new_resource.key_pass)
+ OpenSSL::PKey.read ::File.read(generate_key_file), new_resource.key_pass
+ elsif new_resource.key_type == "rsa"
+ gen_rsa_priv_key(new_resource.key_length)
+ else
+ gen_ec_priv_key(new_resource.key_curve)
+ end
+ @key
+ end
+
+ def subject
+ csr_subject = OpenSSL::X509::Name.new()
+ csr_subject.add_entry("C", new_resource.country) unless new_resource.country.nil?
+ csr_subject.add_entry("ST", new_resource.state) unless new_resource.state.nil?
+ csr_subject.add_entry("L", new_resource.city) unless new_resource.city.nil?
+ csr_subject.add_entry("O", new_resource.org) unless new_resource.org.nil?
+ csr_subject.add_entry("OU", new_resource.org_unit) unless new_resource.org_unit.nil?
+ csr_subject.add_entry("CN", new_resource.common_name)
+ csr_subject.add_entry("emailAddress", new_resource.email) unless new_resource.email.nil?
+ csr_subject
+ end
+
+ def csr
+ gen_x509_request(subject, key)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index 069c35691f..3a4822e8f4 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -66,8 +66,12 @@ require "chef/resource/ohai"
require "chef/resource/ohai_hint"
require "chef/resource/openbsd_package"
require "chef/resource/openssl_dhparam"
+require "chef/resource/openssl_ec_private_key"
+require "chef/resource/openssl_ec_public_key"
require "chef/resource/openssl_rsa_private_key"
require "chef/resource/openssl_rsa_public_key"
+require "chef/resource/openssl_x509_certificate"
+require "chef/resource/openssl_x509_request"
require "chef/resource/package"
require "chef/resource/pacman_package"
require "chef/resource/paludis_package"