-
Notifications
You must be signed in to change notification settings - Fork 70
Expand file tree
/
Copy pathcertificate.rb
More file actions
177 lines (153 loc) · 6.67 KB
/
Copy pathcertificate.rb
File metadata and controls
177 lines (153 loc) · 6.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#
# Author:: Thijs Houtenbos <thoutenbos@schubergphilis.com>
# Cookbook:: acme
# Resource:: certificate
#
# Copyright:: 2015-2021, Schuberg Philis
#
# 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.
#
unified_mode true
default_action :create
property :cn, String, name_property: true
property :alt_names, Array, default: []
property :crt, [String, nil], required: true
property :key, [String, nil], required: true
property :owner, [String, Integer], default: 'root'
property :group, [String, Integer], default: 'root'
property :wwwroot, String, default: '/var/www'
property :key_size, Integer, default: lazy { node['acme']['key_size'] }, equal_to: [2048, 3072, 4096]
property :key_type, String, default: 'rsa', equal_to: %w(rsa ec)
property :ec_curve, String, default: lazy { node['acme']['ec_curve'] }, equal_to: %w(prime256v1 secp384r1 secp521r1)
property :hmac_key, [String, nil]
property :kid, [String, nil]
# From https://letsencrypt.org/docs/profiles/
property :profile, [String, nil], default: nil, equal_to: [nil] + %w(classic shortlived tlsclient tlsserver)
property :dir, [String, nil]
property :contact, Array, default: []
# if you want to use DNS authentication, you can pass the code to install and
# remove the challenge as a block
#
# the install_authz_block will be called for each authorization with the
# authorization and resource as parameter. It must return the authz object from
# the authorization.
# The resource will then call the acme verification process. After verification
# the remove_authz_block will be called with the authz as parameter. This is
# intended to allow cleanup of the challenge
property :install_authz_block, [Proc, nil]
property :remove_authz_block, [Proc, nil]
property :chain, String, deprecated: 'The chain property has been deprecated as the acme-client gem now returns the full certificate chain by default (on the crt property.) Please update your cookbooks to remove this property.'
deprecated_property_alias 'fullchain', 'crt', 'The fullchain property has been deprecated as the acme-client gem now returns the full certificate chain by default (on the crt property.) Please update your cookbooks to switch to \'crt\'.'
deprecated_property_alias 'endpoint', 'dir', 'The endpoint property was renamed to dir, to reflect ACME v2 changes. Please update your cookbooks to use the new property name.'
action :create do
file "#{new_resource.cn} SSL key" do
path new_resource.key
owner new_resource.owner
group new_resource.group
mode '400'
content case new_resource.key_type
when 'rsa'
OpenSSL::PKey::RSA.new(new_resource.key_size).to_pem
when 'ec'
OpenSSL::PKey::EC.generate(new_resource.ec_curve).to_pem
end
sensitive true
action :nothing
end.run_action(:create_if_missing)
mycert = nil
mykey = OpenSSL::PKey.read ::File.read new_resource.key
names = [new_resource.cn, new_resource.alt_names].flatten.compact
# Calculate renewal time based on profile
renew_days = new_resource.profile == 'shortlived' ? 2 : node['acme']['renew']
renew_at = ::Time.now + 60 * 60 * 24 * renew_days
if !new_resource.crt.nil? && ::File.exist?(new_resource.crt)
mycert = ::OpenSSL::X509::Certificate.new ::File.read new_resource.crt
end
if mycert.nil? || mycert.not_after <= renew_at || names_changed?(mycert, names)
order = acme_order_certs_for(format_names(names), profile: new_resource.profile)
all_validations = []
if !new_resource.hmac_key.nil? && !new_resource.kid.nil?
# Use the HMAC key and KID to validate the challenges
elsif new_resource.install_authz_block.nil?
order.authorizations.each do |authorization|
authz = install_http_validation(authorization, new_resource)
acme_validate(authz)
remove_http_validation(authz, new_resource)
all_validations.push(authz)
end
else
ruby_block 'install and validate challenges using custom method' do
block do
order.authorizations.each do |authorization|
authz, fqdn = new_resource.install_authz_block.call(authorization, new_resource)
acme_validate(authz)
new_resource.remove_authz_block.call(authz, fqdn)
end
end
end
end
ruby_block "create certificate for #{new_resource.cn}" do
block do
if !new_resource.hmac_key.nil? && !new_resource.kid.nil?
unless (all_validations.map { |authz| authz.status == 'valid' }).all?
errors = all_validations.select { |authz| authz.status != 'valid' }.map do |authz|
"{url: #{authz.url}, status: #{authz.status}, error: #{authz.error}} "
end.reduce(:+)
raise "[#{new_resource.cn}] Validation failed, unable to request certificate, Errors: [#{errors}]"
end
end
begin
newcert = acme_cert(order, new_resource.cn, mykey, new_resource.alt_names)
rescue Acme::Client::Error => e
raise "[#{new_resource.cn}] Certificate request failed: #{e.message}"
else
Chef::Resource::File.new("#{new_resource.cn} SSL new crt", run_context).tap do |f|
f.path new_resource.crt
f.owner new_resource.owner
f.group new_resource.group
f.content newcert
f.mode 00644
end.run_action :create
end
end
end
end
end
action_class.class_eval do
def install_http_validation(authorization, new_resource)
authz = authorization.http
tokenpath = "#{new_resource.wwwroot}/#{authz.filename}"
directory ::File.dirname(tokenpath) do
owner new_resource.owner
group new_resource.group
mode '755'
recursive true
action :nothing
end.run_action(:create)
file tokenpath do
owner new_resource.owner
group new_resource.group
mode '644'
content authz.file_content
action :nothing
end.run_action(:create)
authz
end
def remove_http_validation(authz, new_resource)
tokenpath = "#{new_resource.wwwroot}/#{authz.filename}"
file tokenpath do
backup false
action :delete
end
end
end