Vendor the external cookbooks
Knife-Zero doesn't include Berkshelf support, so vendoring everything in the repo is convenient again
This commit is contained in:
217
cookbooks/openssl/CHANGELOG.md
Normal file
217
cookbooks/openssl/CHANGELOG.md
Normal file
@@ -0,0 +1,217 @@
|
||||
# openssl Cookbook CHANGELOG
|
||||
|
||||
This file is used to list changes made in each version of the openssl cookbook.
|
||||
|
||||
## 8.5.5 (2018-09-04)
|
||||
|
||||
All resources in this cookbook are now built into Chef 14.4+. When Chef 15.4 is released (April 2019) the resources will be removed from this cookbook as all users should be running Chef 14.4 or later at that point.
|
||||
|
||||
## 8.5.4 (2018-08-29)
|
||||
|
||||
- Add missing email documentation for the request property
|
||||
- Fix x509_crl to work on non-Linux platforms
|
||||
- Attribute -> Property in the readme
|
||||
- revokation -> revocation in the readme
|
||||
- Update group/owner documentation
|
||||
- Avoid deprecation warnings on Chef 14.3+
|
||||
|
||||
## 8.5.3 (2018-08-15)
|
||||
|
||||
- Call ::OpenSSL not OpenSSL to be more defensive in the helpers
|
||||
|
||||
## 8.5.2 (2018-08-14)
|
||||
|
||||
- Back out mode change in ec_private_key
|
||||
|
||||
## 8.5.1 (2018-08-14)
|
||||
|
||||
- Add license headers to the resources
|
||||
- Remove default_action setup from the resources since this is done automatically in custom resources now
|
||||
- Make sure to use the path name_property when creating the ec public key file
|
||||
- Make sure we're using openssl and not Chef's Openssl class
|
||||
- Simplify how we handle user/group properties
|
||||
|
||||
## 8.5.0 (2018-08-02)
|
||||
|
||||
- Use the system provided owner/group defaults in resources
|
||||
- Added new openssl_x509_crl resource
|
||||
- Fix openssl_ec_public_key with documentation & tests
|
||||
- Few corrections in the documentation
|
||||
- Fix backward compatibility with chef client 12
|
||||
|
||||
## 8.4.0 (2018-07-30)
|
||||
|
||||
This release is brought to you by Institut National de l'Audiovisuel, which contributed the following changes:
|
||||
|
||||
- openssl_x509 is renamed to openssl_x509_certificate with backwards compatibility for the old name
|
||||
- openssl_x509_certificate can now generate a signed certificate with a provided CA cert & key
|
||||
- openssl_x509_certificate now support x509 extensions
|
||||
- openssl_x509_certificate now support x509 csr
|
||||
- openssl_x509_certificate now generate a random serial for the certificate
|
||||
- openssl_x509_certificate expires has now a default value : 365
|
||||
- country field is now mandatory in x509_request
|
||||
- the private key file is not rewrited in x509_request if it already exist
|
||||
|
||||
## 8.3.0 (2018-07-25)
|
||||
|
||||
- Add resource x509_request
|
||||
|
||||
## 8.2.0 (2018-07-23)
|
||||
|
||||
- Add ec_private_key & ec_public_key resources
|
||||
|
||||
## 8.1.2 (2018-02-09)
|
||||
|
||||
- Fix typo in resources that caused failures on Windows.
|
||||
- Properly reference key_cipher in the readme
|
||||
|
||||
## 8.1.1 (2018-01-05)
|
||||
|
||||
- Add YARD comments to all the helpers
|
||||
- Move valid ciphers directly into the equal_to check
|
||||
- Remove the Chefspec matchers since modern ChefSpec does this automatically
|
||||
- Fix failures on Windows nodes
|
||||
|
||||
## 8.1.0 (2017-12-28)
|
||||
|
||||
- Adding x509 support for /ST and /L
|
||||
- Allow passing private key content to rsa_public_key resource via property
|
||||
- Fix openssl_rsa_public_key converging on every run
|
||||
- Fix undefied method "cipher" error in openssl_rsa_private_key resource
|
||||
|
||||
## 8.0.0 (2017-12-11)
|
||||
|
||||
- Added a new openssl_rsa_public_key resource which generates a public key from a private key
|
||||
- Rename openssl_rsa_key to openssl_rsa_private_key, while still allowing the old name to function. This resource actually generates private keys, but the previous name didn't make that clear
|
||||
- Added owner, group, and mode properties to all of the resources so you could control who owned the files you generated
|
||||
- Set the default modes of generated files to 640 instead of 644
|
||||
- Set the files to generate using node['root_group'] not 'root' for compatibility on other *nix systems such as FreeBSD and macOS
|
||||
- Added a new property to openssl_rsa_private_key for specifying the cipher to use
|
||||
- Converted integration tests to InSpec and moved all resources to a single Kitchen suite for quicker testing
|
||||
- Added a force property to allow overwriting any existing key that may exist
|
||||
- Fixed upgrade recipe failures on Debian 9
|
||||
- Added a new path property which allows you to set the path there instead of in the resource's name
|
||||
- Improved input validation in some of the helpers
|
||||
- Added a deprecation message in Opscode::OpenSSL::Password helper "secure_password" and removed readme documentation
|
||||
- Added a warning in the upgrade recipe if we're on an unsupported platform
|
||||
- Switched the upgrade recipe to a multipackage upgrade to speed up Chef runs
|
||||
|
||||
## 7.1.0 (2017-05-30)
|
||||
|
||||
- Add supported platforms to the metdata
|
||||
- Fix amazon support
|
||||
- Remove class_eval usage and require Chef 12.7+
|
||||
|
||||
## 7.0.1 (2017-03-21)
|
||||
|
||||
- Fix compatibility with Chef 12.5.1
|
||||
|
||||
## 7.0.0 (2017-03-06)
|
||||
|
||||
- Converted LWRPs to custom resources, increasing the chef-client dependency to 12.5+. This fixes the bus where each resource notified on every run even if it didn't actually update the files on disk.
|
||||
- Added testing for Chef 13
|
||||
- Test with Local Delivery instead of Rake
|
||||
|
||||
## 6.1.1 (2017-01-19)
|
||||
|
||||
- Resolve deprecation warnings in chefspec
|
||||
- Use proper ::File class and fix ^2 validation of dhparam key length
|
||||
- Disable .zero? in cookstyle for now
|
||||
|
||||
## 6.1.0 (2017-01-18)
|
||||
|
||||
- [#37] Support for Subject Alternative Names on generated self-signed certificates
|
||||
- rubocop
|
||||
- Cookstyle fixes
|
||||
|
||||
## 6.0.0 (2016-09-08)
|
||||
|
||||
- Update the minimum chef release to 12.1
|
||||
|
||||
## 5.0.1 (2016-09-01)
|
||||
- Update docs from node.normal as node.set has been deprecated
|
||||
- Testing updates
|
||||
|
||||
## 5.0.0 (2016-08-27)
|
||||
|
||||
- Remove the need for the chef-sugar cookbook
|
||||
- Remove the default['openssl']['packages'] attribute in the upgrades recipe and instead use the correct openssl packages based on platform
|
||||
- Remove support for Debian 6 and Ubuntu 10.04 in the upgrade recipe
|
||||
- Add support for Fedora and Suse in the upgrade recipe
|
||||
- Prevent errors with unset variable in error raising within the random password helper
|
||||
- Add cookstyle and resolve all warnings
|
||||
- Add testing, contributing, and maintainers documentation
|
||||
- Add integration testing in Travis CI with kitchen-dokken
|
||||
- Add issues_url, source_url and chef_version metadata
|
||||
- Update the requirements section of the README
|
||||
- Update the Chefspecs to avoid errors and run using caching for faster runs
|
||||
- Add issues and PR templates for Github
|
||||
|
||||
## v4.4.0 (2015-08-28)
|
||||
|
||||
- NEW: x509 certificates are now signed via SHA-256 instead of SHA-1
|
||||
- FIX: gen_dhparam error now correctly fails with TypeError instead of ArgumentError if Generator argument isn't an integer
|
||||
|
||||
## v4.3.2 (2015-08-01)
|
||||
|
||||
- FIX: Updated changelog
|
||||
|
||||
## v4.3 (2015-08-01)
|
||||
|
||||
- NEW: Add rsa_key lwrp
|
||||
- FIX: dhparam lwrp now correctly honors the generator parameter
|
||||
|
||||
## v4.2 (2015-06-23)
|
||||
|
||||
- NEW: Add dhparam lwrp
|
||||
- FIX: x509 lwrp now updates resource count correctly
|
||||
|
||||
## v4.1.2 (2015-06-20)
|
||||
|
||||
- Add Serverspec suite
|
||||
- Removed update suite from .kitchen.yml
|
||||
- Add explicit license to test cookbook recipes
|
||||
- Add Whyrun support to x509 LWRP
|
||||
- Expand Chefspec tests for x509 LWRP to step_into LWRP
|
||||
- Add helper library
|
||||
- Update x509 LWRP to verify existing keys, if specified
|
||||
|
||||
## v4.1.1 (2015-06-11)
|
||||
|
||||
- README.md fixes
|
||||
|
||||
## v4.1.0 (2015-06-11)
|
||||
|
||||
- Add new random_password Mixin (Thanks, Seth!)
|
||||
- Rewritten README.md
|
||||
- Refactor specs
|
||||
- Clear Rubocop violations
|
||||
|
||||
## v4.0.0 (2015-02-19)
|
||||
|
||||
- Reverting to Opscode module namespace
|
||||
|
||||
## v3.0.2 (2015-12-18)
|
||||
|
||||
- Accidently released 2.0.2 as 3.0.2
|
||||
- Re-namespaced `Opscode::OpenSSL::Password` module as `Chef::OpenSSL::Password`
|
||||
|
||||
## v2.0.2 (2014-12-30)
|
||||
|
||||
- Call cert.to_pem before recipe DSL
|
||||
|
||||
## v2.0.0 (2014-06-11)
|
||||
|
||||
- # 1 - **[COOK-847](https://tickets.chef.io/browse/COOK-847)** - Add LWRP for generating self signed certs
|
||||
|
||||
- # 4 - **[COOK-4715](https://tickets.chef.io/browse/COOK-4715)** - add upgrade recipe and complete test harness
|
||||
|
||||
## v1.1.0
|
||||
|
||||
### Improvement
|
||||
|
||||
- **[COOK-3222](https://tickets.chef.io/browse/COOK-3222)** - Allow setting length for `secure_password`
|
||||
|
||||
## v1.0.2
|
||||
|
||||
- Add name attribute to metadata
|
||||
2
cookbooks/openssl/CONTRIBUTING.md
Normal file
2
cookbooks/openssl/CONTRIBUTING.md
Normal file
@@ -0,0 +1,2 @@
|
||||
Please refer to
|
||||
https://github.com/chef-cookbooks/community_cookbook_documentation/blob/master/CONTRIBUTING.MD
|
||||
416
cookbooks/openssl/README.md
Normal file
416
cookbooks/openssl/README.md
Normal file
@@ -0,0 +1,416 @@
|
||||
# OpenSSL Cookbook
|
||||
|
||||
[](http://travis-ci.org/chef-cookbooks/openssl) [](https://supermarket.chef.io/cookbooks/openssl)
|
||||
|
||||
This cookbook provides tools for working with the Ruby OpenSSL library. It includes:
|
||||
|
||||
- A library method to generate secure random passwords in recipes, using the Ruby SecureRandom library.
|
||||
- A resource for generating RSA private keys.
|
||||
- A resource for generating RSA public keys.
|
||||
- A resource for generating EC private keys.
|
||||
- A resource for generating EC public keys.
|
||||
- A resource for generating x509 certificates.
|
||||
- A resource for generating x509 requests.
|
||||
- A resource for generating x509 crl.
|
||||
- A resource for generating dhparam.pem files.
|
||||
- An attribute-driven recipe for upgrading OpenSSL packages.
|
||||
|
||||
NOTE: All resources in this cookbook are now built-into Chef 14.4 and later so this cookbook is no longer necessary to use those resources. When Chef 15.4 is released (Aug 2019) the resources will be removed from this cookbook as all users should be running Chef 14.4 or later.
|
||||
|
||||
## Platforms
|
||||
|
||||
- Debian / Ubuntu derivatives
|
||||
- Fedora
|
||||
- FreeBSD
|
||||
- macOS
|
||||
- openSUSE / SUSE Linux Enterprises
|
||||
- RHEL/CentOS/Scientific/Amazon/Oracle
|
||||
- Solaris
|
||||
|
||||
## Chef
|
||||
|
||||
- Chef 12.7+
|
||||
|
||||
## Cookbooks
|
||||
|
||||
- none
|
||||
|
||||
## Attributes
|
||||
|
||||
- `node['openssl']['restart_services']` - An array of service resources that depend on the openssl packages. This array is empty by default, as Chef has no reasonable way to detect which applications or services are compiled against these packages. _Note_ Each service listed in this array should represent a "`service`" resource specified in the recipes of the node's run list.
|
||||
|
||||
## Recipes
|
||||
|
||||
### upgrade
|
||||
|
||||
The upgrade recipe iterates over the list of packages in the `node['openssl']['packages']` attribute, and manages them with the `:upgrade` action. Each package will send a `:restart` notification to service resources named in the `node['openssl']['restart_services']` attribute.
|
||||
|
||||
#### Example Usage
|
||||
|
||||
In this example, assume the node is running the `stats_collector` daemon, which depends on the openssl library. Imagine that a new openssl vulnerability has been disclosed, and the operating system vendor has released an update to openssl to address this vulnerability. In order to protect the node, an administrator crafts this recipe:
|
||||
|
||||
```ruby
|
||||
node.default['openssl']['restart_services'] = ['stats_collector']
|
||||
|
||||
# other recipe code here...
|
||||
service 'stats_collector' do
|
||||
action [:enable, :start]
|
||||
end
|
||||
|
||||
include_recipe 'openssl::upgrade'
|
||||
```
|
||||
|
||||
When executed, this recipe will ensure that openssl is upgraded to the latest version, and that the `stats_collector` service is restarted to pick up the latest security fixes released in the openssl package.
|
||||
|
||||
## Libraries
|
||||
|
||||
There are two mixins packaged with this cookbook.
|
||||
|
||||
### random_password (`OpenSSLCookbook::RandomPassword`)
|
||||
|
||||
The `RandomPassword` mixin can be used to generate secure random passwords in Chef cookbooks, usually for assignment to a variable or an attribute. `random_password` uses Ruby's SecureRandom library and is customizable.
|
||||
|
||||
#### Example Usage
|
||||
|
||||
```ruby
|
||||
Chef::Recipe.send(:include, OpenSSLCookbook::RandomPassword)
|
||||
node.normal['my_secure_attribute'] = random_password
|
||||
node.normal_unless['my_secure_attribute'] = random_password
|
||||
node.normal['my_secure_attribute'] = random_password(length: 50)
|
||||
node.normal['my_secure_attribute'] = random_password(length: 50, mode: :base64)
|
||||
node.normal['my_secure_attribute'] = random_password(length: 50, mode: :base64, encoding: 'ASCII')
|
||||
```
|
||||
|
||||
Note that node attributes are widely accessible. Storing unencrypted passwords in node attributes, as in this example, carries risk.
|
||||
|
||||
## Resources
|
||||
|
||||
### openssl_x509_certificate
|
||||
|
||||
This resource generates signed or self-signed, PEM-formatted x509 certificates. If no existing key is specified, the resource will automatically generate a passwordless key with the certificate. If a CA private key and certificate are provided, the certificate will be signed with them.
|
||||
|
||||
Note: This resource was renamed from openssl_x509 to openssl_x509_certificate. The legacy name will continue to function, but cookbook code should be updated for the new resource name.
|
||||
|
||||
#### Properties
|
||||
|
||||
Name | Type | Description
|
||||
------------------ | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
`path` | String (Optional) | Optional path to write the file to if you'd like to specify it here instead of in the resource name
|
||||
`common_name` | String (Optional) | Value for the `CN` certificate field.
|
||||
`org` | String (Optional) | Value for the `O` certificate field.
|
||||
`org_unit` | String (Optional) | Value for the `OU` certificate field.
|
||||
`city` | String (Optional) | Value for the `L` certificate field.
|
||||
`state` | String (Optional) | Value for the `ST` certificate field.
|
||||
`country` | String (Optional) | Value for the `C` ssl field.
|
||||
`email` | String (Optional) | Value for the `email` ssl field.
|
||||
`expire` | Integer (Optional) | Value representing the number of days from _now_ through which the issued certificate cert will remain valid. The certificate will expire after this period. _Default: 365
|
||||
`extensions` | Hash (Optional) | Hash of X509 Extensions entries, in format `{ 'keyUsage' => { 'values' => %w( keyEncipherment digitalSignature), 'critical' => true } }` _Default: empty_
|
||||
`subject_alt_name` | Array (Optional) | Array of _Subject Alternative Name_ entries, in format `DNS:example.com` or `IP:1.2.3.4` _Default: empty_
|
||||
`key_file` | String (Optional) | The path to a certificate key file on the filesystem. If the `key_file` property is specified, the resource will attempt to source a key from this location. If no key file is found, the resource will generate a new key file at this location. If the `key_file` property is not specified, the resource will generate a key file in the same directory as the generated certificate, with the same name as the generated certificate.
|
||||
`key_pass` | String (Optional) | The passphrase for an existing key's passphrase
|
||||
`key_type` | String (Optional) | The desired type of the generated key (rsa or ec). _Default: rsa_
|
||||
`key_length` | Integer (Optional) | The desired Bit Length of the generated key (if key_type is equal to 'rsa'). _Default: 2048_
|
||||
`key_curve` | String (Optional) | 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_
|
||||
`csr_file` | String (Optional) | The path to a X509 Certificate Request (CSR) on the filesystem. If the `csr_file` property is specified, the resource will attempt to source a CSR from this location. If no CSR file is found, the resource will generate a Self-Signed Certificate and the certificate fields must be specified (common_name at last).
|
||||
`ca_cert_file` | String (Optional) | The path to the CA X509 Certificate on the filesystem. If the `ca_cert_file` property is specified, the `ca_key_file` property must also be specified, the certificate will be signed with them.
|
||||
`ca_key_file` | String (Optional) | The path to the CA private key on the filesystem. If the `ca_key_file` property is specified, the `ca_cert_file' property must also be specified, the certificate will be signed with them.
|
||||
`ca_key_pass` | String (Optional) | The passphrase for CA private key's passphrase
|
||||
`owner` | String (optional) | The owner of all files created by the resource.
|
||||
`group` | String (optional) | The group of all files created by the resource.
|
||||
`mode` | String or Integer (Optional) | The permission mode of all files created by the resource.
|
||||
|
||||
#### Example Usage
|
||||
|
||||
In this example, an administrator wishes to create a self-signed x509 certificate for use with a web server. In order to create the certificate, the administrator crafts this recipe:
|
||||
|
||||
```ruby
|
||||
openssl_x509 '/etc/httpd/ssl/mycert.pem' do
|
||||
common_name 'www.f00bar.com'
|
||||
org 'Foo Bar'
|
||||
org_unit 'Lab'
|
||||
country 'US'
|
||||
end
|
||||
```
|
||||
|
||||
When executed, this recipe will generate a key certificate at `/etc/httpd/ssl/mycert.key`. It will then use that key to generate a new certificate file at `/etc/httpd/ssl/mycert.pem`.
|
||||
|
||||
In this example, an administrator wishes to create a x509 certificate signed with a CA certificate and key. In order to create the certificate, the administrator crafts this recipe:
|
||||
|
||||
```ruby
|
||||
openssl_x509_certificate '/etc/ssl_test/my_signed_cert.crt' do
|
||||
common_name 'www.f00bar.com'
|
||||
ca_key_file '/etc/ssl_test/my_ca.key'
|
||||
ca_cert_file '/etc/ssl_test/my_ca.crt'
|
||||
expire 365
|
||||
extensions(
|
||||
'keyUsage' => {
|
||||
'values' => %w(
|
||||
keyEncipherment
|
||||
digitalSignature),
|
||||
'critical' => true,
|
||||
},
|
||||
'extendedKeyUsage' => {
|
||||
'values' => %w(serverAuth),
|
||||
'critical' => false,
|
||||
}
|
||||
)
|
||||
subject_alt_name ['IP:127.0.0.1', 'DNS:localhost.localdomain']
|
||||
end
|
||||
```
|
||||
|
||||
When executed, this recipe will generate a key certificate at `/etc/ssl_test/my_signed_cert.key`. It will then use that key to generate a CSR and signed it with `my_ca.key/my_ca.crt`. A new certificate file at `/etc/ssl_test/my_signed_cert.cert` will be created as a result.
|
||||
|
||||
|
||||
### openssl_x509_request
|
||||
|
||||
This resource generates PEM-formatted x509 certificates requests. If no existing key is specified, the resource will automatically generate a passwordless key with the certificate.
|
||||
|
||||
#### Properties
|
||||
|
||||
Name | Type | Description
|
||||
--------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------
|
||||
`path` | String (Optional) | Optional path to write the file to if you'd like to specify it here instead of in the resource name
|
||||
`common_name` | String (Required) | Value for the `CN` certificate field.
|
||||
`org` | String (Optional) | Value for the `O` certificate field.
|
||||
`org_unit` | String (Optional) | Value for the `OU` certificate field.
|
||||
`city` | String (Optional) | Value for the `L` certificate field.
|
||||
`state` | String (Optional) | Value for the `ST` certificate field.
|
||||
`country` | String (Optional) | Value for the `C` ssl field.
|
||||
`email` | String (Optional) | Value for the `email` ssl field.
|
||||
`key_file` | String (Optional) | The path to a certificate key file on the filesystem. If the `key_file` property is specified, the resource will attempt to source a key from this location. If no key file is found, the resource will generate a new key file at this location. If the `key_file` property is not specified, the resource will generate a key file in the same directory as the generated certificate, with the same name as the generated certificate.
|
||||
`key_pass` | String (Optional) | The passphrase for an existing key's passphrase
|
||||
`key_type` | String (Optional) | The desired type of the generated key (rsa or ec). _Default: ec_
|
||||
`key_length` | Integer (Optional) | The desired Bit Length of the generated key (if key_type is equal to 'rsa'). _Default: 2048_
|
||||
`key_curve` | String (Optional) | 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
|
||||
`owner` | String (optional) | The owner of all files created by the resource.
|
||||
`group` | String (optional) | The group of all files created by the resource.
|
||||
`mode` | String or Integer (Optional) | The permission mode of all files created by the resource.
|
||||
|
||||
#### Example Usage
|
||||
|
||||
In this example, an administrator wishes to create a x509 CRL. In order to create the CRL, the administrator crafts this recipe:
|
||||
|
||||
```ruby
|
||||
openssl_x509_request '/etc/ssl_test/my_ec_request.csr' do
|
||||
common_name 'myecrequest.example.com'
|
||||
org 'Test Kitchen Example'
|
||||
org_unit 'Kitchens'
|
||||
country 'UK'
|
||||
end
|
||||
```
|
||||
|
||||
When executed, this recipe will generate a key certificate at `/etc/httpd/ssl/my_ec_request.key`. It will then use that key to generate a new csr file at `/etc/ssl_test/my_ec_request.csr`.
|
||||
|
||||
### openssl_x509_crl
|
||||
|
||||
This resource generates PEM-formatted x509 CRL.
|
||||
|
||||
#### Properties
|
||||
|
||||
Name | Type | Description
|
||||
--------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------
|
||||
`path` | String (Optional) | Optional path to write the file to if you'd like to specify it here instead of in the resource name
|
||||
`serial_to_revoke` | String or Integer(Optional) | Serial of the X509 Certificate to revoke
|
||||
`revocation_reason` | String or Integer(Optional) | [Reason of the revocation]((https://en.wikipedia.org/wiki/Certificate_revocation_list#Reasons_for_revocation)) _Default: 0_
|
||||
`expire` | Integer (Optional) | Value representing the number of days from _now_ through which the issued CRL will remain valid. The CRL will expire after this period. _Default: 8_
|
||||
`renewal_threshold` | Integer (Optional) | Number of days before the expiration. It this threshold is reached, the CRL will be renewed _Default: 1_
|
||||
`ca_cert_file` | String (Required) | The path to the CA X509 Certificate on the filesystem. If the `ca_cert_file` property is specified, the `ca_key_file` property must also be specified, the CRL will be signed with them.
|
||||
`ca_key_file` | String (Required) | The path to the CA private key on the filesystem. If the `ca_key_file` property is specified, the `ca_cert_file' property must also be specified, the CRL will be signed with them.
|
||||
`ca_key_pass` | String (Optional) | The passphrase for CA private key's passphrase
|
||||
`owner` | String (optional) | The owner of all files created by the resource.
|
||||
`group` | String (optional) | The group of all files created by the resource.
|
||||
`mode` | String or Integer (Optional) | The permission mode of all files created by the resource.
|
||||
|
||||
|
||||
#### Example Usage
|
||||
|
||||
In this example, an administrator wishes to create an empty X509 CRL. In order to create the CRL, the administrator crafts this recipe:
|
||||
|
||||
```ruby
|
||||
openssl_x509_crl '/etc/ssl_test/my_ca.crl' do
|
||||
ca_cert_file '/etc/ssl_test/my_ca.crt'
|
||||
ca_key_file '/etc/ssl_test/my_ca.key'
|
||||
end
|
||||
```
|
||||
|
||||
When executed, this recipe will generate a new CRL file at `/etc/ssl_test/my_ca.crl`.
|
||||
|
||||
In this example, an administrator wishes to revoke a certificate in an existing X509 CRL.
|
||||
|
||||
```ruby
|
||||
openssl_x509_crl '/etc/ssl_test/my_ca.crl' do
|
||||
ca_cert_file '/etc/ssl_test/my_ca.crt'
|
||||
ca_key_file '/etc/ssl_test/my_ca.key'
|
||||
serial_to_revoke C7BCB6602A2E4251EF4E2827A228CB52BC0CEA2F
|
||||
end
|
||||
```
|
||||
|
||||
### openssl_dhparam
|
||||
|
||||
This resource generates dhparam.pem files. If a valid dhparam.pem file is found at the specified location, no new file will be created. If a file is found at the specified location but it is not a valid dhparam file, it will be overwritten.
|
||||
|
||||
#### Properties
|
||||
|
||||
Name | Type | Description
|
||||
------------ | ---------------------------- | ---------------------------------------------------------------------------------------------------
|
||||
`path` | String (Optional) | Optional path to write the file to if you'd like to specify it here instead of in the resource name
|
||||
`key_length` | Integer (Optional) | The desired Bit Length of the generated key. _Default: 2048_
|
||||
`generator` | Integer (Optional) | The desired Diffie-Hellmann generator. Can be _2_ or _5_.
|
||||
`owner` | String (optional) | The owner of all files created by the resource.
|
||||
`group` | String (optional) | The group of all files created by the resource.
|
||||
`mode` | String or Integer (Optional) | The permission mode of all files created by the resource. _Default: "0640"_
|
||||
|
||||
#### Example Usage
|
||||
|
||||
In this example, an administrator wishes to create a dhparam.pem file for use with a web server. In order to create the .pem file, the administrator crafts this recipe:
|
||||
|
||||
```ruby
|
||||
openssl_dhparam '/etc/httpd/ssl/dhparam.pem' do
|
||||
key_length 2048
|
||||
generator 2
|
||||
end
|
||||
```
|
||||
|
||||
When executed, this recipe will generate a dhparam file at `/etc/httpd/ssl/dhparam.pem`.
|
||||
|
||||
### openssl_rsa_private_key
|
||||
|
||||
This resource generates rsa private key files. If a valid rsa key file can be opened at the specified location, no new file will be created. If the RSA key file cannot be opened, either because it does not exist or because the password to the RSA key file does not match the password in the recipe, it will be overwritten.
|
||||
|
||||
Note: This resource was renamed from openssl_rsa_key to openssl_rsa_private_key. The legacy name will continue to function, but cookbook code should be updated for the new resource name.
|
||||
|
||||
#### Properties
|
||||
|
||||
Name | Type | Description
|
||||
------------ | ---------------------------- | -----------------------------------------------------------------------------------------------------------------------------------
|
||||
`path` | String (Optional) | Optional path to write the file to if you'd like to specify it here instead of in the resource name
|
||||
`key_length` | Integer (Optional) | The desired Bit Length of the generated key. _Default: 2048_
|
||||
`key_cipher` | String (Optional) | The designed cipher to use when generating your key. Run `openssl list-cipher-algorithms` to see available options. _Default: des3_
|
||||
`key_pass` | String (Optional) | The desired passphrase for the key.
|
||||
`owner` | String (optional) | The owner of all files created by the resource.
|
||||
`group` | String (optional) | The group of all files created by the resource.
|
||||
`mode` | String or Integer (Optional) | The permission mode of all files created by the resource. _Default: "0640"_
|
||||
`force` | true/false (Optional) | Force creating the key even if the existing key exists. _Default: false_
|
||||
|
||||
#### Example Usage
|
||||
|
||||
In this example, an administrator wishes to create a new RSA private key file in order to generate other certificates and public keys. In order to create the key file, the administrator crafts this recipe:
|
||||
|
||||
```ruby
|
||||
openssl_rsa_private_key '/etc/httpd/ssl/server.key' do
|
||||
key_length 2048
|
||||
end
|
||||
```
|
||||
|
||||
When executed, this recipe will generate a passwordless RSA key file at `/etc/httpd/ssl/server.key`.
|
||||
|
||||
### openssl_rsa_public_key
|
||||
|
||||
This resource generates rsa public key files given a private key.
|
||||
|
||||
#### Properties
|
||||
|
||||
Name | Type | Description
|
||||
--------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------
|
||||
`path` | String (Optional) | Optional path to write the file to if you'd like to specify it here instead of in the resource name
|
||||
`private_key_path` | String (Required unless private_key_content used) | The path to the private key to generate the public key from
|
||||
`private_key_content` | String (Required unless private_key_path used) | The content of the private key including new lines. Used if you don't want to write a private key to disk and use `private_key_path`.
|
||||
`private_key_pass` | String (Optional) | The passphrase of the provided private key
|
||||
`owner` | String (optional) | The owner of all files created by the resource.
|
||||
`group` | String (optional) | The group of all files created by the resource.
|
||||
`mode` | String or Integer (Optional) | The permission mode of all files created by the resource. _Default: "0640"_
|
||||
|
||||
**Note**: To use `private_key_content` the private key string must be properly formatted including new lines. The easiest way to get the right string is to run the following from irb (/opt/chefdk/embedded/bin/irb from ChefDK)
|
||||
|
||||
```ruby
|
||||
File.read('/foo/bar/private.pem')
|
||||
```
|
||||
|
||||
#### Example Usage
|
||||
|
||||
```ruby
|
||||
openssl_rsa_public_key '/etc/foo/something.pub' do
|
||||
priv_key_path '/etc/foo/something.pem'
|
||||
end
|
||||
```
|
||||
|
||||
### openssl_ec_private_key
|
||||
|
||||
This resource generates ec private key files. If a valid ec key file can be opened at the specified location, no new file will be created. If the EC key file cannot be opened, either because it does not exist or because the password to the EC key file does not match the password in the recipe, it will be overwritten.
|
||||
|
||||
#### Properties
|
||||
|
||||
Name | Type | Description
|
||||
------------ | ---------------------------- | -----------------------------------------------------------------------------------------------------------------------------------
|
||||
`path` | String (Optional) | Optional path to write the file to if you'd like to specify it here instead of in the resource name
|
||||
`key_curve` | String (Optional) | The desired curve of the generated key. Run `openssl ecparam -list_curves` to see available options. _Default: prime256v1
|
||||
`key_cipher` | String (Optional) | The designed cipher to use when generating your key. Run `openssl list-cipher-algorithms` to see available options. _Default: des3_
|
||||
`key_pass` | String (Optional) | The desired passphrase for the key.
|
||||
`owner` | String (optional) | The owner of all files created by the resource.
|
||||
`group` | String (optional) | The group of all files created by the resource.
|
||||
`mode` | String or Integer (Optional) | The permission mode of all files created by the resource. _Default: "0640"_
|
||||
`force` | true/false (Optional) | Force creating the key even if the existing key exists. _Default: false_
|
||||
|
||||
#### Example Usage
|
||||
|
||||
In this example, an administrator wishes to create a new EC private key file in order to generate other certificates and public keys. In order to create the key file, the administrator crafts this recipe:
|
||||
|
||||
```ruby
|
||||
openssl_ec_private_key '/etc/httpd/ssl/server.key' do
|
||||
key_curve "prime256v1'
|
||||
end
|
||||
```
|
||||
|
||||
When executed, this recipe will generate a passwordless EC key file at `/etc/httpd/ssl/server.key`.
|
||||
|
||||
### openssl_ec_public_key
|
||||
|
||||
This resource generates ec public key files given a private key.
|
||||
|
||||
#### Properties
|
||||
|
||||
Name | Type | Description
|
||||
--------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------
|
||||
`path` | String (Optional) | Optional path to write the file to if you'd like to specify it here instead of in the resource name
|
||||
`private_key_path` | String (Required unless private_key_content used) | The path to the private key to generate the public key from
|
||||
`private_key_content` | String (Required unless private_key_path used) | The content of the private key including new lines. Used if you don't want to write a private key to disk and use `private_key_path`.
|
||||
`private_key_pass` | String (Optional) | The passphrase of the provided private key
|
||||
`owner` | String (optional) | The owner of all files created by the resource. _Default: "root"_
|
||||
`group` | String (optional) | The group of all files created by the resource. _Default: "root or wheel depending on platform"_
|
||||
`mode` | String or Integer (Optional) | The permission mode of all files created by the resource. _Default: "0640"_
|
||||
|
||||
**Note**: To use `private_key_content` the private key string must be properly formatted including new lines. The easiest way to get the right string is to run the following from irb (/opt/chefdk/embedded/bin/irb from ChefDK)
|
||||
|
||||
```ruby
|
||||
File.read('/foo/bar/private.pem')
|
||||
```
|
||||
|
||||
#### Example Usage
|
||||
|
||||
```ruby
|
||||
openssl_ec_public_key '/etc/foo/something.pub' do
|
||||
priv_key_path '/etc/foo/something.pem'
|
||||
end
|
||||
```
|
||||
|
||||
## Maintainers
|
||||
|
||||
This cookbook is maintained by Chef's Community Cookbook Engineering team. Our goal is to improve cookbook quality and to aid the community in contributing to cookbooks. To learn more about our team, process, and design goals see our [team documentation](https://github.com/chef-cookbooks/community_cookbook_documentation/blob/master/COOKBOOK_TEAM.MD). To learn more about contributing to cookbooks like this see our [contributing documentation](https://github.com/chef-cookbooks/community_cookbook_documentation/blob/master/CONTRIBUTING.MD), or if you have general questions about this cookbook come chat with us in #cookbok-engineering on the [Chef Community Slack](http://community-slack.chef.io/)
|
||||
|
||||
## License
|
||||
|
||||
**Copyright:** 2009-2018, Chef Software, Inc.
|
||||
|
||||
```
|
||||
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.
|
||||
```
|
||||
20
cookbooks/openssl/attributes/default.rb
Normal file
20
cookbooks/openssl/attributes/default.rb
Normal file
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# Cookbook:: openssl
|
||||
# Attributes:: default
|
||||
#
|
||||
# Copyright:: 2014-2017, Chef Software, Inc. <legal@chef.io>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
default['openssl']['restart_services'] = []
|
||||
399
cookbooks/openssl/libraries/helpers.rb
Normal file
399
cookbooks/openssl/libraries/helpers.rb
Normal file
@@ -0,0 +1,399 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
module OpenSSLCookbook
|
||||
# Helper functions for the OpenSSL cookbook.
|
||||
module Helpers
|
||||
def self.included(_base)
|
||||
require 'openssl' unless defined?(::OpenSSL)
|
||||
end
|
||||
|
||||
# determine the key filename from the cert filename
|
||||
# @param [String] cert_filename the path to the certfile
|
||||
# @return [String] the path to the keyfile
|
||||
def get_key_filename(cert_filename)
|
||||
cert_file_path, cert_filename = ::File.split(cert_filename)
|
||||
cert_filename = ::File.basename(cert_filename, ::File.extname(cert_filename))
|
||||
cert_file_path + ::File::SEPARATOR + cert_filename + '.key'
|
||||
end
|
||||
|
||||
# is the key length a valid key length
|
||||
# @param [Integer] number
|
||||
# @return [Boolean] is length valid
|
||||
def key_length_valid?(number)
|
||||
number >= 1024 && (number & (number - 1) == 0)
|
||||
end
|
||||
|
||||
# validate a dhparam file from path
|
||||
# @param [String] dhparam_pem_path the path to the pem file
|
||||
# @return [Boolean] is the key valid
|
||||
def dhparam_pem_valid?(dhparam_pem_path)
|
||||
# Check if the dhparam.pem file exists
|
||||
# Verify the dhparam.pem file contains a key
|
||||
return false unless ::File.exist?(dhparam_pem_path)
|
||||
dhparam = ::OpenSSL::PKey::DH.new File.read(dhparam_pem_path)
|
||||
dhparam.params_ok?
|
||||
end
|
||||
|
||||
# given either a key file path or key file content see if it's actually
|
||||
# a private key
|
||||
# @param [String] key_file the path to the keyfile or the key contents
|
||||
# @param [String] key_password optional password to the keyfile
|
||||
# @return [Boolean] is the key valid?
|
||||
def priv_key_file_valid?(key_file, key_password = nil)
|
||||
# if the file exists try to read the content
|
||||
# if not assume we were passed the key and set the string to the content
|
||||
key_content = ::File.exist?(key_file) ? File.read(key_file) : key_file
|
||||
|
||||
begin
|
||||
key = ::OpenSSL::PKey.read key_content, key_password
|
||||
rescue ::OpenSSL::PKey::PKeyError, ArgumentError
|
||||
return false
|
||||
end
|
||||
|
||||
if key.is_a?(::OpenSSL::PKey::EC)
|
||||
key.private_key?
|
||||
else
|
||||
key.private?
|
||||
end
|
||||
end
|
||||
|
||||
# given a crl file path see if it's actually a crl
|
||||
# @param [String] crl_file the path to the crlfile
|
||||
# @return [Boolean] is the key valid?
|
||||
def crl_file_valid?(crl_file)
|
||||
begin
|
||||
::OpenSSL::X509::CRL.new ::File.read(crl_file)
|
||||
rescue ::OpenSSL::X509::CRLError, Errno::ENOENT
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
# check is a serial given is revoked in a crl given
|
||||
# @param [OpenSSL::X509::CRL] crl X509 CRL to check
|
||||
# @param [String, Integer] serial X509 Certificate Serial Number
|
||||
# @return [true, false]
|
||||
def serial_revoked?(crl, serial)
|
||||
raise TypeError, 'crl must be a Ruby OpenSSL::X509::CRL object' unless crl.is_a?(::OpenSSL::X509::CRL)
|
||||
raise TypeError, 'serial must be a Ruby String or Integer object' unless serial.is_a?(String) || serial.is_a?(Integer)
|
||||
|
||||
serial_to_verify = if serial.is_a?(String)
|
||||
serial.to_i(16)
|
||||
else
|
||||
serial
|
||||
end
|
||||
status = false
|
||||
crl.revoked.each do |revoked|
|
||||
status = true if revoked.serial == serial_to_verify
|
||||
end
|
||||
status
|
||||
end
|
||||
|
||||
# generate a dhparam file
|
||||
# @param [String] key_length the length of the key
|
||||
# @param [Integer] generator the dhparam generator to use
|
||||
# @return [OpenSSL::PKey::DH]
|
||||
def gen_dhparam(key_length, generator)
|
||||
raise ArgumentError, 'Key length must be a power of 2 greater than or equal to 1024' unless key_length_valid?(key_length)
|
||||
raise TypeError, 'Generator must be an integer' unless generator.is_a?(Integer)
|
||||
|
||||
::OpenSSL::PKey::DH.new(key_length, generator)
|
||||
end
|
||||
|
||||
# generate an RSA private key given key length
|
||||
# @param [Integer] key_length the key length of the private key
|
||||
# @return [OpenSSL::PKey::DH]
|
||||
def gen_rsa_priv_key(key_length)
|
||||
raise ArgumentError, 'Key length must be a power of 2 greater than or equal to 1024' unless key_length_valid?(key_length)
|
||||
|
||||
::OpenSSL::PKey::RSA.new(key_length)
|
||||
end
|
||||
|
||||
# generate pem format of the public key given a private key
|
||||
# @param [String] priv_key either the contents of the private key or the path to the file
|
||||
# @param [String] priv_key_password optional password for the private key
|
||||
# @return [String] pem format of the public key
|
||||
def gen_rsa_pub_key(priv_key, priv_key_password = nil)
|
||||
# if the file exists try to read the content
|
||||
# if not assume we were passed the key and set the string to the content
|
||||
key_content = ::File.exist?(priv_key) ? File.read(priv_key) : priv_key
|
||||
key = ::OpenSSL::PKey::RSA.new key_content, priv_key_password
|
||||
key.public_key.to_pem
|
||||
end
|
||||
|
||||
# generate a pem file given a cipher, key, an optional key_password
|
||||
# @param [OpenSSL::PKey::RSA] rsa_key the private key object
|
||||
# @param [String] key_password the password for the private key
|
||||
# @param [String] key_cipher the cipher to use
|
||||
# @return [String] pem contents
|
||||
def encrypt_rsa_key(rsa_key, key_password, key_cipher)
|
||||
raise TypeError, 'rsa_key must be a Ruby OpenSSL::PKey::RSA object' unless rsa_key.is_a?(::OpenSSL::PKey::RSA)
|
||||
raise TypeError, 'key_password must be a string' unless key_password.is_a?(String)
|
||||
raise TypeError, 'key_cipher must be a string' unless key_cipher.is_a?(String)
|
||||
raise ArgumentError, 'Specified key_cipher is not available on this system' unless ::OpenSSL::Cipher.ciphers.include?(key_cipher)
|
||||
|
||||
cipher = ::OpenSSL::Cipher.new(key_cipher)
|
||||
rsa_key.to_pem(cipher, key_password)
|
||||
end
|
||||
|
||||
# generate an ec private key given curve type
|
||||
# @param [String] curve the kind of curve to use
|
||||
# @return [OpenSSL::PKey::DH]
|
||||
def gen_ec_priv_key(curve)
|
||||
raise TypeError, 'curve must be a string' unless curve.is_a?(String)
|
||||
raise ArgumentError, 'Specified curve is not available on this system' unless curve == 'prime256v1' || curve == 'secp384r1' || curve == 'secp521r1'
|
||||
::OpenSSL::PKey::EC.new(curve).generate_key
|
||||
end
|
||||
|
||||
# generate pem format of the public key given a private key
|
||||
# @param [String] priv_key either the contents of the private key or the path to the file
|
||||
# @param [String] priv_key_password optional password for the private key
|
||||
# @return [String] pem format of the public key
|
||||
def gen_ec_pub_key(priv_key, priv_key_password = nil)
|
||||
# if the file exists try to read the content
|
||||
# if not assume we were passed the key and set the string to the content
|
||||
key_content = ::File.exist?(priv_key) ? File.read(priv_key) : priv_key
|
||||
key = ::OpenSSL::PKey::EC.new key_content, priv_key_password
|
||||
|
||||
# Get curve type (prime256v1...)
|
||||
group = ::OpenSSL::PKey::EC::Group.new(key.group.curve_name)
|
||||
# Get Generator point & public point (priv * generator)
|
||||
generator = group.generator
|
||||
pub_point = generator.mul(key.private_key)
|
||||
key.public_key = pub_point
|
||||
|
||||
# Public Key in pem
|
||||
public_key = ::OpenSSL::PKey::EC.new
|
||||
public_key.group = group
|
||||
public_key.public_key = pub_point
|
||||
public_key.to_pem
|
||||
end
|
||||
|
||||
# generate a pem file given a cipher, key, an optional key_password
|
||||
# @param [OpenSSL::PKey::EC] ec_key the private key object
|
||||
# @param [String] key_password the password for the private key
|
||||
# @param [String] key_cipher the cipher to use
|
||||
# @return [String] pem contents
|
||||
def encrypt_ec_key(ec_key, key_password, key_cipher)
|
||||
raise TypeError, 'ec_key must be a Ruby OpenSSL::PKey::EC object' unless ec_key.is_a?(::OpenSSL::PKey::EC)
|
||||
raise TypeError, 'key_password must be a string' unless key_password.is_a?(String)
|
||||
raise TypeError, 'key_cipher must be a string' unless key_cipher.is_a?(String)
|
||||
raise ArgumentError, 'Specified key_cipher is not available on this system' unless ::OpenSSL::Cipher.ciphers.include?(key_cipher)
|
||||
|
||||
cipher = ::OpenSSL::Cipher.new(key_cipher)
|
||||
ec_key.to_pem(cipher, key_password)
|
||||
end
|
||||
|
||||
# generate a csr pem file given a subject and a private key
|
||||
# @param [OpenSSL::X509::Name] subject the x509 subject object
|
||||
# @param [OpenSSL::PKey::EC, OpenSSL::PKey::RSA] key the private key object
|
||||
# @return [OpenSSL::X509::Request]
|
||||
def gen_x509_request(subject, key)
|
||||
raise TypeError, 'subject must be a Ruby OpenSSL::X509::Name object' unless subject.is_a?(::OpenSSL::X509::Name)
|
||||
raise TypeError, 'key must be a Ruby OpenSSL::PKey::EC or a Ruby OpenSSL::PKey::RSA object' unless key.is_a?(::OpenSSL::PKey::EC) || key.is_a?(::OpenSSL::PKey::RSA)
|
||||
|
||||
request = ::OpenSSL::X509::Request.new
|
||||
request.version = 0
|
||||
request.subject = subject
|
||||
request.public_key = key
|
||||
|
||||
# Chef 12 backward compatibility
|
||||
::OpenSSL::PKey::EC.send(:alias_method, :private?, :private_key?)
|
||||
|
||||
request.sign(key, ::OpenSSL::Digest::SHA256.new)
|
||||
request
|
||||
end
|
||||
|
||||
# generate an array of X509 Extensions given a hash of extensions
|
||||
# @param [Hash] extensions hash of extensions
|
||||
# @return [Array]
|
||||
def gen_x509_extensions(extensions)
|
||||
raise TypeError, 'extensions must be a Ruby Hash object' unless extensions.is_a?(Hash)
|
||||
|
||||
exts = []
|
||||
extensions.each do |ext_name, ext_prop|
|
||||
raise TypeError, "#{ext_name} must contain a Ruby Hash" unless ext_prop.is_a?(Hash)
|
||||
raise ArgumentError, "keys in #{ext_name} must be 'values' and 'critical'" unless ext_prop.key?('values') && ext_prop.key?('critical')
|
||||
raise TypeError, "the key 'values' must contain a Ruby Arrays" unless ext_prop['values'].is_a?(Array)
|
||||
raise TypeError, "the key 'critical' must be a Ruby Boolean true/false" unless ext_prop['critical'].is_a?(TrueClass) || ext_prop['critical'].is_a?(FalseClass)
|
||||
|
||||
exts << ::OpenSSL::X509::ExtensionFactory.new.create_extension(ext_name, ext_prop['values'].join(','), ext_prop['critical'])
|
||||
end
|
||||
exts
|
||||
end
|
||||
|
||||
# generate a random Serial
|
||||
# @return [Integer]
|
||||
def gen_serial
|
||||
::OpenSSL::BN.generate_prime(160)
|
||||
end
|
||||
|
||||
# generate a Certificate given a X509 request
|
||||
# @param [OpenSSL::X509::Request] request X509 Certificate Request
|
||||
# @param [Array] extension Array of X509 Certificate Extension
|
||||
# @param [Hash] info issuer & validity
|
||||
# @param [OpenSSL::PKey::EC, OpenSSL::PKey::RSA] key private key to sign with
|
||||
# @return [OpenSSL::X509::Certificate]
|
||||
def gen_x509_cert(request, extension, info, key)
|
||||
raise TypeError, 'request must be a Ruby OpenSSL::X509::Request' unless request.is_a?(::OpenSSL::X509::Request)
|
||||
raise TypeError, 'extension must be a Ruby Array' unless extension.is_a?(Array)
|
||||
raise TypeError, 'info must be a Ruby Hash' unless info.is_a?(Hash)
|
||||
raise TypeError, 'key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object' unless key.is_a?(::OpenSSL::PKey::EC) || key.is_a?(::OpenSSL::PKey::RSA)
|
||||
|
||||
raise ArgumentError, 'info must contain a validity' unless info.key?('validity')
|
||||
raise TypeError, 'info[\'validity\'] must be a Ruby Integer object' unless info['validity'].is_a?(Integer)
|
||||
|
||||
cert = ::OpenSSL::X509::Certificate.new
|
||||
ef = ::OpenSSL::X509::ExtensionFactory.new
|
||||
|
||||
cert.serial = gen_serial()
|
||||
cert.version = 2
|
||||
cert.subject = request.subject
|
||||
cert.public_key = request.public_key
|
||||
cert.not_before = Time.now
|
||||
cert.not_after = cert.not_before + info['validity'] * 24 * 60 * 60
|
||||
|
||||
if info['issuer'].nil?
|
||||
cert.issuer = request.subject
|
||||
ef.issuer_certificate = cert
|
||||
extension << ef.create_extension('basicConstraints', 'CA:TRUE', true)
|
||||
else
|
||||
raise TypeError, 'info[\'issuer\'] must be a Ruby OpenSSL::X509::Certificate object' unless info['issuer'].is_a?(::OpenSSL::X509::Certificate)
|
||||
cert.issuer = info['issuer'].subject
|
||||
ef.issuer_certificate = info['issuer']
|
||||
end
|
||||
ef.subject_certificate = cert
|
||||
ef.config = ::OpenSSL::Config.load(::OpenSSL::Config::DEFAULT_CONFIG_FILE)
|
||||
|
||||
cert.extensions = extension
|
||||
cert.add_extension ef.create_extension('subjectKeyIdentifier', 'hash')
|
||||
cert.add_extension ef.create_extension('authorityKeyIdentifier',
|
||||
'keyid:always,issuer:always')
|
||||
|
||||
cert.sign(key, ::OpenSSL::Digest::SHA256.new)
|
||||
cert
|
||||
end
|
||||
|
||||
# generate a X509 CRL given a CA
|
||||
# @param [OpenSSL::PKey::EC, OpenSSL::PKey::RSA] ca_private_key private key from the CA
|
||||
# @param [Hash] info issuer & validity
|
||||
# @return [OpenSSL::X509::CRL]
|
||||
def gen_x509_crl(ca_private_key, info)
|
||||
raise TypeError, 'ca_private_key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object' unless ca_private_key.is_a?(::OpenSSL::PKey::EC) || ca_private_key.is_a?(::OpenSSL::PKey::RSA)
|
||||
raise TypeError, 'info must be a Ruby Hash' unless info.is_a?(Hash)
|
||||
|
||||
raise ArgumentError, 'info must contain a issuer and a validity' unless info.key?('issuer') && info.key?('validity')
|
||||
raise TypeError, 'info[\'issuer\'] must be a Ruby OpenSSL::X509::Certificate object' unless info['issuer'].is_a?(::OpenSSL::X509::Certificate)
|
||||
raise TypeError, 'info[\'validity\'] must be a Ruby Integer object' unless info['validity'].is_a?(Integer)
|
||||
|
||||
crl = ::OpenSSL::X509::CRL.new
|
||||
ef = ::OpenSSL::X509::ExtensionFactory.new
|
||||
|
||||
crl.version = 1
|
||||
crl.issuer = info['issuer'].subject
|
||||
crl.last_update = Time.now
|
||||
crl.next_update = Time.now + 3600 * 24 * info['validity']
|
||||
|
||||
ef.config = ::OpenSSL::Config.load(::OpenSSL::Config::DEFAULT_CONFIG_FILE)
|
||||
ef.issuer_certificate = info['issuer']
|
||||
|
||||
crl.add_extension ::OpenSSL::X509::Extension.new('crlNumber', ::OpenSSL::ASN1::Integer(1))
|
||||
crl.add_extension ef.create_extension('authorityKeyIdentifier',
|
||||
'keyid:always,issuer:always')
|
||||
crl.sign(ca_private_key, ::OpenSSL::Digest::SHA256.new)
|
||||
crl
|
||||
end
|
||||
|
||||
# generate the next CRL number available for a X509 CRL given
|
||||
# @param [OpenSSL::X509::CRL] crl x509 CRL
|
||||
# @return [Integer]
|
||||
def get_next_crl_number(crl)
|
||||
raise TypeError, 'crl must be a Ruby OpenSSL::X509::CRL object' unless crl.is_a?(::OpenSSL::X509::CRL)
|
||||
crlnum = 1
|
||||
crl.extensions.each do |e|
|
||||
crlnum = e.value if e.oid == 'crlNumber'
|
||||
end
|
||||
crlnum.to_i + 1
|
||||
end
|
||||
|
||||
# add a serial given in the crl given
|
||||
# @param [Hash] revoke_info serial to revoke & revokation reason
|
||||
# @param [OpenSSL::X509::CRL] crl X509 CRL
|
||||
# @param [OpenSSL::PKey::EC, OpenSSL::PKey::RSA] ca_private_key private key from the CA
|
||||
# @param [Hash] info issuer & validity
|
||||
# @return [OpenSSL::X509::CRL]
|
||||
def revoke_x509_crl(revoke_info, crl, ca_private_key, info)
|
||||
raise TypeError, 'revoke_info must be a Ruby Hash oject' unless revoke_info.is_a?(Hash)
|
||||
raise TypeError, 'crl must be a Ruby OpenSSL::X509::CRL object' unless crl.is_a?(::OpenSSL::X509::CRL)
|
||||
raise TypeError, 'ca_private_key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object' unless ca_private_key.is_a?(::OpenSSL::PKey::EC) || ca_private_key.is_a?(::OpenSSL::PKey::RSA)
|
||||
raise TypeError, 'info must be a Ruby Hash' unless info.is_a?(Hash)
|
||||
|
||||
raise ArgumentError, 'revoke_info must contain a serial and a reason' unless revoke_info.key?('serial') && revoke_info.key?('reason')
|
||||
raise TypeError, 'revoke_info[\'serial\'] must be a Ruby String or Integer object' unless revoke_info['serial'].is_a?(String) || revoke_info['serial'].is_a?(Integer)
|
||||
raise TypeError, 'revoke_info[\'reason\'] must be a Ruby Integer object' unless revoke_info['reason'].is_a?(Integer)
|
||||
|
||||
raise ArgumentError, 'info must contain a issuer and a validity' unless info.key?('issuer') && info.key?('validity')
|
||||
raise TypeError, 'info[\'issuer\'] must be a Ruby OpenSSL::X509::Certificate object' unless info['issuer'].is_a?(::OpenSSL::X509::Certificate)
|
||||
raise TypeError, 'info[\'validity\'] must be a Ruby Integer object' unless info['validity'].is_a?(Integer)
|
||||
|
||||
revoked = ::OpenSSL::X509::Revoked.new
|
||||
revoked.serial = if revoke_info['serial'].is_a?(String)
|
||||
revoke_info['serial'].to_i(16)
|
||||
else
|
||||
revoke_info['serial']
|
||||
end
|
||||
revoked.time = Time.now
|
||||
|
||||
ext = ::OpenSSL::X509::Extension.new('CRLReason',
|
||||
::OpenSSL::ASN1::Enumerated(revoke_info['reason']))
|
||||
revoked.add_extension(ext)
|
||||
crl.add_revoked(revoked)
|
||||
|
||||
crl = renew_x509_crl(crl, ca_private_key, info)
|
||||
crl
|
||||
end
|
||||
|
||||
# renew a X509 crl given
|
||||
# @param [OpenSSL::X509::CRL] crl CRL to renew
|
||||
# @param [OpenSSL::PKey::EC, OpenSSL::PKey::RSA] ca_private_key private key from the CA
|
||||
# @param [Hash] info issuer & validity
|
||||
# @return [OpenSSL::X509::CRL]
|
||||
def renew_x509_crl(crl, ca_private_key, info)
|
||||
raise TypeError, 'crl must be a Ruby OpenSSL::X509::CRL object' unless crl.is_a?(::OpenSSL::X509::CRL)
|
||||
raise TypeError, 'ca_private_key must be a Ruby OpenSSL::PKey::EC object or a Ruby OpenSSL::PKey::RSA object' unless ca_private_key.is_a?(::OpenSSL::PKey::EC) || ca_private_key.is_a?(::OpenSSL::PKey::RSA)
|
||||
raise TypeError, 'info must be a Ruby Hash' unless info.is_a?(Hash)
|
||||
|
||||
raise ArgumentError, 'info must contain a issuer and a validity' unless info.key?('issuer') && info.key?('validity')
|
||||
raise TypeError, 'info[\'issuer\'] must be a Ruby OpenSSL::X509::Certificate object' unless info['issuer'].is_a?(::OpenSSL::X509::Certificate)
|
||||
raise TypeError, 'info[\'validity\'] must be a Ruby Integer object' unless info['validity'].is_a?(Integer)
|
||||
|
||||
crl.last_update = Time.now
|
||||
crl.next_update = crl.last_update + 3600 * 24 * info['validity']
|
||||
|
||||
ef = ::OpenSSL::X509::ExtensionFactory.new
|
||||
ef.config = ::OpenSSL::Config.load(::OpenSSL::Config::DEFAULT_CONFIG_FILE)
|
||||
ef.issuer_certificate = info['issuer']
|
||||
|
||||
crl.extensions = [ ::OpenSSL::X509::Extension.new('crlNumber',
|
||||
::OpenSSL::ASN1::Integer(get_next_crl_number(crl)))]
|
||||
crl.add_extension ef.create_extension('authorityKeyIdentifier',
|
||||
'keyid:always,issuer:always')
|
||||
crl.sign(ca_private_key, ::OpenSSL::Digest::SHA256.new)
|
||||
crl
|
||||
end
|
||||
end
|
||||
end
|
||||
82
cookbooks/openssl/libraries/random_password.rb
Normal file
82
cookbooks/openssl/libraries/random_password.rb
Normal file
@@ -0,0 +1,82 @@
|
||||
#
|
||||
# Cookbook:: openssl
|
||||
# Library:: random_password
|
||||
# Author:: Seth Vargo <sethvargo@gmail.com>
|
||||
#
|
||||
# Copyright:: 2015-2017, Seth Vargo
|
||||
#
|
||||
# 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.
|
||||
# rubocop:disable UnusedMethodArgument, Style/RaiseArgs
|
||||
|
||||
module OpenSSLCookbook
|
||||
module RandomPassword
|
||||
# Override the included method to require securerandom if it is not defined.
|
||||
# This avoids the need to load the class on each Chef run unless the user is
|
||||
# explicitly requiring it.
|
||||
def self.included(base)
|
||||
require 'securerandom' unless defined?(SecureRandom)
|
||||
end
|
||||
|
||||
class InvalidPasswordMode < StandardError
|
||||
def initialize(given, _acceptable = nil)
|
||||
super <<-EOH
|
||||
The given password mode '#{given}' is not valid. Valid password modes are :hex,
|
||||
:base64, and :random_bytes!
|
||||
EOH
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Generates a random password using {SecureRandom}.
|
||||
#
|
||||
# @example Generating a random (hex) password (of 20 characters)
|
||||
# random_password #=> "1930e99aa035083bdd93d1d8f11cb7ac8f625c9c"
|
||||
#
|
||||
# @example Generating a random base64 password that is 50 characters
|
||||
# random_password(mode: :base64, length: 50) #=> "72o5oVbKHHEVYj1nOgFB2EijnzZfnrbfasVuF+oRH8wMgb0QWoYZF/OkrQricp1ENoI="
|
||||
#
|
||||
# @example Generate a password with a forced encoding
|
||||
# random_password(encoding: "ASCII")
|
||||
#
|
||||
# @param [Hash] options
|
||||
# @option options [Fixnum] :length
|
||||
# the number of bits to use in the password
|
||||
# @option options [Symbol] :mode
|
||||
# the type of random password to generate - valid values are
|
||||
# `:hex`, `:base64`, or `:random_bytes`
|
||||
# @option options [String, Symbol, Constant] :encoding
|
||||
# the encoding to force (default is "UTF-8")
|
||||
#
|
||||
# @return [String]
|
||||
#
|
||||
def random_password(options = {})
|
||||
length = options[:length] || 20
|
||||
mode = options[:mode] || :hex
|
||||
encoding = options[:encoding] || 'UTF-8'
|
||||
|
||||
# Convert to a "proper" length, since the size is actually in bytes
|
||||
length = case mode
|
||||
when :hex
|
||||
length / 2
|
||||
when :base64
|
||||
length * 3 / 4
|
||||
when :random_bytes
|
||||
length
|
||||
else
|
||||
raise InvalidPasswordMode.new(mode)
|
||||
end
|
||||
|
||||
SecureRandom.send(mode, length).force_encoding(encoding)
|
||||
end
|
||||
end
|
||||
end
|
||||
40
cookbooks/openssl/libraries/secure_password.rb
Normal file
40
cookbooks/openssl/libraries/secure_password.rb
Normal file
@@ -0,0 +1,40 @@
|
||||
#
|
||||
# Cookbook:: openssl
|
||||
# Library:: secure_password
|
||||
# Author:: Joshua Timberman <joshua@chef.io>
|
||||
#
|
||||
# Copyright:: 2009-2017, Chef Software, Inc.
|
||||
#
|
||||
# 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 OpenSSLCookbook::Helpers
|
||||
|
||||
module Opscode
|
||||
module OpenSSL
|
||||
# Generate secure passwords with OpenSSL
|
||||
module Password
|
||||
def secure_password(length = 20)
|
||||
Chef::Log.warn('The Opscode::OpenSSL::Password helper "secure_password" has been deprecated. Use the random_password method in OpenSSLCookbook::RandomPassword instead.')
|
||||
|
||||
pw = ''
|
||||
|
||||
while pw.length < length
|
||||
pw << ::OpenSSL::Random.random_bytes(1).gsub(/\W/, '')
|
||||
end
|
||||
|
||||
pw
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
1
cookbooks/openssl/metadata.json
Normal file
1
cookbooks/openssl/metadata.json
Normal file
File diff suppressed because one or more lines are too long
18
cookbooks/openssl/recipes/default.rb
Normal file
18
cookbooks/openssl/recipes/default.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
#
|
||||
# Cookbook Name:: openssl
|
||||
# Recipe:: default
|
||||
#
|
||||
# Copyright 2009-2016, Chef Software, Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
42
cookbooks/openssl/recipes/upgrade.rb
Normal file
42
cookbooks/openssl/recipes/upgrade.rb
Normal file
@@ -0,0 +1,42 @@
|
||||
#
|
||||
# Cookbook:: openssl
|
||||
# Recipe:: upgrade
|
||||
#
|
||||
# Copyright:: 2015-2017, Chef Software, Inc. <legal@chef.io>
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
case node['platform_family']
|
||||
when 'debian', 'ubuntu'
|
||||
packages = if platform?('debian') && node['platform_version'].to_i >= 9
|
||||
%w(libssl1.0.2 openssl)
|
||||
else
|
||||
%w(libssl1.0.0 openssl)
|
||||
end
|
||||
when 'rhel', 'fedora', 'suse', 'amazon'
|
||||
packages = %w(openssl)
|
||||
else
|
||||
packages = []
|
||||
end
|
||||
|
||||
if packages.empty?
|
||||
Chef::Log.warn("The openssl::upgrade recipe does not currently support #{node['platform']}. If you believe it could please open a PR at https://github.com/chef-cookbooks/openssl")
|
||||
else
|
||||
package packages do
|
||||
action :upgrade
|
||||
node['openssl']['restart_services'].each do |ssl_svc|
|
||||
notifies :restart, "service[#{ssl_svc}]"
|
||||
end
|
||||
end
|
||||
end
|
||||
48
cookbooks/openssl/resources/dhparam.rb
Normal file
48
cookbooks/openssl/resources/dhparam.rb
Normal file
@@ -0,0 +1,48 @@
|
||||
#
|
||||
# Copyright:: Copyright 2009-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.
|
||||
#
|
||||
|
||||
chef_version_for_provides '< 14.0' if respond_to?(:chef_version_for_provides)
|
||||
resource_name :openssl_dhparam
|
||||
|
||||
include OpenSSLCookbook::Helpers
|
||||
|
||||
property :path, String, name_property: true
|
||||
property :key_length, equal_to: [1024, 2048, 4096, 8192], default: 2048
|
||||
property :generator, equal_to: [2, 5], default: 2
|
||||
property :owner, String
|
||||
property :group, String
|
||||
property :mode, [Integer, String], default: '0640'
|
||||
|
||||
action :create do
|
||||
unless dhparam_pem_valid?(new_resource.path)
|
||||
converge_by("Create a dhparam file #{new_resource.path}") do
|
||||
dhparam_content = gen_dhparam(new_resource.key_length, new_resource.generator).to_pem
|
||||
|
||||
log "Generating #{new_resource.key_length} bit "\
|
||||
"dhparam file at #{new_resource.path}, this may take some time"
|
||||
|
||||
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 dhparam_content
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
55
cookbooks/openssl/resources/ec_private_key.rb
Normal file
55
cookbooks/openssl/resources/ec_private_key.rb
Normal file
@@ -0,0 +1,55 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
chef_version_for_provides '< 14.4' if respond_to?(:chef_version_for_provides)
|
||||
resource_name :openssl_ec_private_key
|
||||
|
||||
include OpenSSLCookbook::Helpers
|
||||
|
||||
property :path, String, name_property: true
|
||||
property :key_curve, equal_to: %w(secp384r1 secp521r1 prime256v1 secp224r1 secp256k1), default: 'prime256v1'
|
||||
property :key_pass, String
|
||||
property :key_cipher, String, default: 'des3', equal_to: ::OpenSSL::Cipher.ciphers
|
||||
property :owner, String
|
||||
property :group, String
|
||||
property :mode, [Integer, String], default: '0640'
|
||||
property :force, [true, false], default: 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
|
||||
45
cookbooks/openssl/resources/ec_public_key.rb
Normal file
45
cookbooks/openssl/resources/ec_public_key.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
chef_version_for_provides '< 14.4' if respond_to?(:chef_version_for_provides)
|
||||
resource_name :openssl_ec_public_key
|
||||
|
||||
include OpenSSLCookbook::Helpers
|
||||
|
||||
property :path, String, name_property: true
|
||||
property :private_key_path, String
|
||||
property :private_key_content, String
|
||||
property :private_key_pass, String
|
||||
property :owner, String
|
||||
property :group, String
|
||||
property :mode, [Integer, String], default: '0640'
|
||||
|
||||
action :create do
|
||||
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 EC 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_pub_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
|
||||
55
cookbooks/openssl/resources/rsa_private_key.rb
Normal file
55
cookbooks/openssl/resources/rsa_private_key.rb
Normal file
@@ -0,0 +1,55 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
chef_version_for_provides '< 14.0' if respond_to?(:chef_version_for_provides)
|
||||
resource_name :openssl_rsa_private_key
|
||||
provides :openssl_rsa_key # legacy name
|
||||
|
||||
include OpenSSLCookbook::Helpers
|
||||
|
||||
property :path, String, name_property: true
|
||||
property :key_length, equal_to: [1024, 2048, 4096, 8192], default: 2048
|
||||
property :key_pass, String
|
||||
property :key_cipher, String, default: 'des3', equal_to: ::OpenSSL::Cipher.ciphers
|
||||
property :owner, String
|
||||
property :group, String
|
||||
property :mode, [Integer, String], default: '0640'
|
||||
property :force, [true, false], default: false
|
||||
|
||||
action :create do
|
||||
unless new_resource.force || priv_key_file_valid?(new_resource.path, new_resource.key_pass)
|
||||
converge_by("Create an RSA private key #{new_resource.path}") do
|
||||
log "Generating #{new_resource.key_length} bit "\
|
||||
"RSA key file at #{new_resource.path}, this may take some time"
|
||||
|
||||
if new_resource.key_pass
|
||||
unencrypted_rsa_key = gen_rsa_priv_key(new_resource.key_length)
|
||||
rsa_key_content = encrypt_rsa_key(unencrypted_rsa_key, new_resource.key_pass, new_resource.key_cipher)
|
||||
else
|
||||
rsa_key_content = gen_rsa_priv_key(new_resource.key_length).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 rsa_key_content
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
45
cookbooks/openssl/resources/rsa_public_key.rb
Normal file
45
cookbooks/openssl/resources/rsa_public_key.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
chef_version_for_provides '< 14.0' if respond_to?(:chef_version_for_provides)
|
||||
resource_name :openssl_rsa_public_key
|
||||
|
||||
include OpenSSLCookbook::Helpers
|
||||
|
||||
property :path, String, name_property: true
|
||||
property :private_key_path, String
|
||||
property :private_key_content, String
|
||||
property :private_key_pass, String
|
||||
property :owner, String
|
||||
property :group, String
|
||||
property :mode, [Integer, String], default: '0640'
|
||||
|
||||
action :create do
|
||||
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)
|
||||
|
||||
rsa_key_content = gen_rsa_pub_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 rsa_key_content
|
||||
end
|
||||
end
|
||||
151
cookbooks/openssl/resources/x509_certificate.rb
Normal file
151
cookbooks/openssl/resources/x509_certificate.rb
Normal file
@@ -0,0 +1,151 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
chef_version_for_provides '< 14.4' if respond_to?(:chef_version_for_provides)
|
||||
resource_name :openssl_x509_certificate
|
||||
|
||||
provides :openssl_x509 # legacy_name
|
||||
|
||||
include OpenSSLCookbook::Helpers
|
||||
|
||||
property :path, String, name_property: true
|
||||
property :owner, String
|
||||
property :group, String
|
||||
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
|
||||
88
cookbooks/openssl/resources/x509_crl.rb
Normal file
88
cookbooks/openssl/resources/x509_crl.rb
Normal file
@@ -0,0 +1,88 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
chef_version_for_provides '< 14.4' if respond_to?(:chef_version_for_provides)
|
||||
resource_name :openssl_x509_crl
|
||||
|
||||
include OpenSSLCookbook::Helpers
|
||||
|
||||
property :path, String, name_property: true
|
||||
property :serial_to_revoke, [Integer, String]
|
||||
property :revocation_reason, Integer, default: 0
|
||||
property :expire, Integer, default: 8
|
||||
property :renewal_threshold, Integer, default: 1
|
||||
property :ca_cert_file, String, required: true
|
||||
property :ca_key_file, String, required: true
|
||||
property :ca_key_pass, String
|
||||
property :owner, String
|
||||
property :group, String
|
||||
property :mode, String
|
||||
|
||||
action :create do
|
||||
file new_resource.path do
|
||||
owner new_resource.owner unless new_resource.owner.nil?
|
||||
group new_resource.group unless new_resource.group.nil?
|
||||
mode new_resource.mode unless new_resource.mode.nil?
|
||||
content crl.to_pem
|
||||
action :create
|
||||
end
|
||||
end
|
||||
|
||||
action_class do
|
||||
def crl_info
|
||||
# Will contain issuer & expiration
|
||||
crl_info = {}
|
||||
|
||||
crl_info['issuer'] = ::OpenSSL::X509::Certificate.new ::File.read(new_resource.ca_cert_file)
|
||||
crl_info['validity'] = new_resource.expire
|
||||
|
||||
crl_info
|
||||
end
|
||||
|
||||
def revoke_info
|
||||
# Will contain Serial to revoke & reason
|
||||
revoke_info = {}
|
||||
|
||||
revoke_info['serial'] = new_resource.serial_to_revoke
|
||||
revoke_info['reason'] = new_resource.revocation_reason
|
||||
|
||||
revoke_info
|
||||
end
|
||||
|
||||
def ca_private_key
|
||||
ca_private_key = ::OpenSSL::PKey.read ::File.read(new_resource.ca_key_file), new_resource.ca_key_pass
|
||||
ca_private_key
|
||||
end
|
||||
|
||||
def crl
|
||||
if crl_file_valid?(new_resource.path)
|
||||
crl = ::OpenSSL::X509::CRL.new ::File.read(new_resource.path)
|
||||
else
|
||||
log "Creating a CRL #{new_resource.path} for CA #{new_resource.ca_cert_file}"
|
||||
crl = gen_x509_crl(ca_private_key, crl_info)
|
||||
end
|
||||
|
||||
if !new_resource.serial_to_revoke.nil? && serial_revoked?(crl, new_resource.serial_to_revoke) == false
|
||||
log "Revoking serial #{new_resource.serial_to_revoke} in CRL #{new_resource.path}"
|
||||
crl = revoke_x509_crl(revoke_info, crl, ca_private_key, crl_info)
|
||||
elsif crl.next_update <= Time.now + 3600 * 24 * new_resource.renewal_threshold
|
||||
log "Renewing CRL for CA #{new_resource.ca_cert_file}"
|
||||
crl = renew_x509_crl(crl, ca_private_key, crl_info)
|
||||
end
|
||||
|
||||
crl
|
||||
end
|
||||
end
|
||||
98
cookbooks/openssl/resources/x509_request.rb
Normal file
98
cookbooks/openssl/resources/x509_request.rb
Normal file
@@ -0,0 +1,98 @@
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
chef_version_for_provides '< 14.4' if respond_to?(:chef_version_for_provides)
|
||||
resource_name :openssl_x509_request
|
||||
|
||||
include OpenSSLCookbook::Helpers
|
||||
|
||||
property :path, String, name_property: true
|
||||
property :owner, String
|
||||
property :group, String
|
||||
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, required: true
|
||||
property :email, String
|
||||
property :key_file, String
|
||||
property :key_pass, String
|
||||
property :key_type, equal_to: %w(rsa ec), default: 'ec'
|
||||
property :key_length, equal_to: [1024, 2048, 4096, 8192], default: 2048
|
||||
property :key_curve, equal_to: %w(secp384r1 secp521r1 prime256v1), default: 'prime256v1'
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user