acmesmith 2.7.1 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,14 +30,14 @@ module Acmesmith
30
30
  # client.authorize(*domains)
31
31
  end
32
32
 
33
- desc "order COMMON_NAME [SAN]", "order certificate for CN +COMMON_NAME+ with SANs +SAN+"
33
+ desc "order NAME [SAN]", "order certificate for CN +NAME+ with SANs +SAN+"
34
34
  method_option :show_certificate, type: :boolean, aliases: %w(-s), default: true, desc: 'show an issued certificate in PEM and text when exiting'
35
35
  method_option :key_type, type: :string, enum: %w(rsa ec), default: 'rsa', desc: 'key type'
36
36
  method_option :rsa_key_size, type: :numeric, default: 2048, desc: 'size of RSA key'
37
37
  method_option :elliptic_curve, type: :string, default: 'prime256v1', desc: 'elliptic curve group for EC key'
38
- def order(common_name, *sans)
38
+ def order(name, *sans)
39
39
  cert = client.order(
40
- common_name, *sans,
40
+ name, *sans,
41
41
  key_type: options[:key_type],
42
42
  rsa_key_size: options[:rsa_key_size],
43
43
  elliptic_curve: options[:elliptic_curve],
@@ -48,60 +48,73 @@ module Acmesmith
48
48
  end
49
49
  end
50
50
 
51
- desc "post-issue-hooks COMMON_NAME", "Run all post-issuing hooks for common name. (for testing purpose)"
52
- def post_issue_hooks(common_name)
53
- client.post_issue_hooks(common_name)
51
+ desc "post-issue-hooks NAME", "Run all post-issuing hooks for common name. (for testing purpose)"
52
+ def post_issue_hooks(name)
53
+ client.post_issue_hooks(name)
54
54
  end
55
55
  map 'post-issue-hooks' => :post_issue_hooks
56
56
 
57
- desc "list [COMMON_NAME]", "list certificates or its versions"
58
- def list(common_name = nil)
59
- if common_name
60
- puts client.certificate_versions(common_name)
57
+ desc "list-profiles", "List available ACME certificate profiles"
58
+ def list_profiles
59
+ profiles = client.list_profiles
60
+ if profiles.nil? || profiles.empty?
61
+ puts "No profiles available from this ACME directory"
62
+ return
63
+ end
64
+ profiles.each do |name, description|
65
+ puts "#{name}: #{description}"
66
+ end
67
+ end
68
+ map 'list-profiles' => :list_profiles
69
+
70
+ desc "list [NAME]", "list certificates or its versions"
71
+ def list(name = nil)
72
+ if name
73
+ puts client.certificate_versions(name)
61
74
  else
62
75
  puts client.certificates_list
63
76
  end
64
77
  end
65
78
 
66
- desc "current COMMON_NAME", "show current version for certificate"
67
- def current(common_name)
68
- puts client.current(common_name)
79
+ desc "current NAME", "show current version for certificate"
80
+ def current(name)
81
+ puts client.current(name)
69
82
  end
70
83
 
71
- desc "show-certificate COMMON_NAME", "show certificate"
84
+ desc "show-certificate NAME", "show certificate"
72
85
  method_option :version, type: :string, default: 'current'
73
86
  method_option :type, type: :string, enum: %w(text certificate chain fullchain), default: 'text'
74
- def show_certificate(common_name)
75
- certs = client.get_certificate(common_name, version: options[:version], type: options[:type])
87
+ def show_certificate(name)
88
+ certs = client.get_certificate(name, version: options[:version], type: options[:type])
76
89
  puts certs
77
90
  end
78
91
  map 'show-certiticate' => :show_certificate
79
92
 
80
- desc 'save-certificate COMMON_NAME', 'Save certificate to a file'
93
+ desc 'save-certificate NAME', 'Save certificate to a file'
81
94
  method_option :version, type: :string, default: 'current'
82
95
  method_option :type, type: :string, enum: %w(certificate chain fullchain), default: 'fullchain'
83
96
  method_option :output, type: :string, required: true, banner: 'PATH', desc: 'Path to output file'
84
97
  method_option :mode, type: :string, default: '0600', desc: 'Mode (permission) of the output file on create'
85
- def save_certificate(common_name)
86
- client.save_certificate(common_name, version: options[:version], mode: options[:mode], output: options[:output], type: options[:type])
98
+ def save_certificate(name)
99
+ client.save_certificate(name, version: options[:version], mode: options[:mode], output: options[:output], type: options[:type])
87
100
  end
88
101
 
89
- desc "show-private-key COMMON_NAME", "show private key"
102
+ desc "show-private-key NAME", "show private key"
90
103
  method_option :version, type: :string, default: 'current'
91
- def show_private_key(common_name)
92
- puts client.get_private_key(common_name, version: options[:version])
104
+ def show_private_key(name)
105
+ puts client.get_private_key(name, version: options[:version])
93
106
  end
94
107
  map 'show-private-key' => :show_private_key
95
108
 
96
- desc 'save-private-key COMMON_NAME', 'Save private key to a file'
109
+ desc 'save-private-key NAME', 'Save private key to a file'
97
110
  method_option :version, type: :string, default: 'current'
98
111
  method_option :output, type: :string, required: true, banner: 'PATH', desc: 'Path to output file'
99
112
  method_option :mode, type: :string, default: '0600', desc: 'Mode (permission) of the output file on create'
100
- def save_private_key(common_name)
101
- client.save_private_key(common_name, version: options[:version], mode: options[:mode], output: options[:output])
113
+ def save_private_key(name)
114
+ client.save_private_key(name, version: options[:version], mode: options[:mode], output: options[:output])
102
115
  end
103
116
 
104
- desc 'save COMMON_NAME', 'Save (or update) certificate and key files.'
117
+ desc 'save NAME', 'Save (or update) certificate and key files.'
105
118
  method_option :version, type: :string, default: 'current'
106
119
  method_option :key_mode, type: :string, default: '0600', desc: 'Mode (permission) of the key file on create'
107
120
  method_option :certificate_mode, type: :string, default: '0644', desc: 'Mode (permission) of the certificate files on create'
@@ -111,9 +124,9 @@ module Acmesmith
111
124
  method_option :chain_file, type: :string, required: false , banner: 'PATH', desc: 'Path to save a certificate chain (root and intermediate CA)'
112
125
  method_option :certificate_file, type: :string, required: false, banner: 'PATH', desc: 'Path to save a certficiate'
113
126
  method_option :atomic, type: :boolean, default: true, desc: 'Enable atomic file update with rename(2)'
114
- def save(common_name)
127
+ def save(name)
115
128
  client.save(
116
- common_name,
129
+ name,
117
130
  version: options[:version],
118
131
  key_mode: options[:key_mode],
119
132
  certificate_mode: options[:certificate_mode],
@@ -127,11 +140,11 @@ module Acmesmith
127
140
  )
128
141
  end
129
142
 
130
- desc 'save-pkcs12 COMMON_NAME', 'Save ceriticate and private key to .p12 file'
143
+ desc 'save-pkcs12 NAME', 'Save ceriticate and private key to .p12 file'
131
144
  method_option :version, type: :string, default: 'current'
132
145
  method_option :output, type: :string, required: true, banner: 'PATH', desc: 'Path to output file'
133
146
  method_option :mode, type: :string, default: '0600', desc: 'Mode (permission) of the output file on create'
134
- def save_pkcs12(common_name)
147
+ def save_pkcs12(name)
135
148
  print 'Passphrase: '
136
149
  passphrase = $stdin.noecho { $stdin.gets }.chomp
137
150
  print "\nPassphrase (confirm): "
@@ -139,13 +152,13 @@ module Acmesmith
139
152
  puts
140
153
 
141
154
  raise ArgumentError, "Passphrase doesn't match" if passphrase != passphrase2
142
- client.save_pkcs12(common_name, version: options[:version], mode: options[:mode], output: options[:output], passphrase: passphrase)
155
+ client.save_pkcs12(name, version: options[:version], mode: options[:mode], output: options[:output], passphrase: passphrase)
143
156
  end
144
157
 
145
- desc "autorenew [COMMON_NAMES]", "request renewal of certificates which expires soon"
158
+ desc "autorenew [NAMES]", "request renewal of certificates which expires soon"
146
159
  method_option :days, type: :numeric, aliases: %w(-d), default: nil, desc: 'specify threshold in days to select certificates to renew'
147
160
  method_option :remaining_life, type: :string, aliases: %w(-r), default: '1/3', desc: "Specify threshold based on remaining life. Accepts a percentage ('20%') or fraction ('1/3')"
148
- def autorenew(*common_names)
161
+ def autorenew(*names)
149
162
  remaining_life = case options[:remaining_life]
150
163
  when %r{\A\d+/\d+\z}
151
164
  Rational(options[:remaining_life])
@@ -156,12 +169,12 @@ module Acmesmith
156
169
  else
157
170
  raise ArgumentError, "invalid format for --remaining-life: it must be in '..%' or '../..'"
158
171
  end
159
- client.autorenew(days: options[:days], remaining_life: remaining_life, common_names: common_names.empty? ? nil : common_names)
172
+ client.autorenew(days: options[:days], remaining_life: remaining_life, names: names.empty? ? nil : names)
160
173
  end
161
174
 
162
- desc "add-san COMMON_NAME [ADDITIONAL_SANS]", "request renewal of existing certificate with additional SANs"
163
- def add_san(common_name, *add_sans)
164
- client.add_san(common_name, *add_sans)
175
+ desc "add-san NAME [ADDITIONAL_SANS]", "request renewal of existing certificate with additional SANs"
176
+ def add_san(name, *add_sans)
177
+ client.add_san(name, *add_sans)
165
178
  end
166
179
 
167
180
  desc "register CONTACT", "(deprecated, use 'acmesmith new-account')"
@@ -175,16 +188,16 @@ module Acmesmith
175
188
  new_account(contact)
176
189
  end
177
190
 
178
- desc "request COMMON_NAME [SAN]", "(deprecated, use 'acmesmith order')"
191
+ desc "request NAME [SAN]", "(deprecated, use 'acmesmith order')"
179
192
  method_option :show_certificate, type: :boolean, aliases: %w(-s), default: true, desc: 'show an issued certificate in PEM and text when exiting'
180
- def request(common_name, *sans)
193
+ def request(name, *sans)
181
194
  warn "!"
182
195
  warn "! DEPRECATION WARNING: Use 'acmesmith order' command"
183
196
  warn "! There is no user-facing breaking changes. It takes the same arguments with 'acmesmith request'."
184
197
  warn "!"
185
198
  warn "! This is due to change in semantics of ACME v2. ACME v2 defines 'order' instead of 'request' in v1."
186
199
  warn "!"
187
- order(common_name, *sans)
200
+ order(name, *sans)
188
201
  end
189
202
 
190
203
  private
@@ -2,6 +2,7 @@ require 'yaml'
2
2
  require 'acmesmith/storages'
3
3
  require 'acmesmith/challenge_responders'
4
4
  require 'acmesmith/challenge_responder_filter'
5
+ require 'acmesmith/subject_name_filter'
5
6
  require 'acmesmith/domain_name_filter'
6
7
  require 'acmesmith/post_issuing_hooks'
7
8
 
@@ -9,6 +10,7 @@ module Acmesmith
9
10
  class Config
10
11
  ChallengeResponderRule = Struct.new(:challenge_responder, :filter, keyword_init: true)
11
12
  ChainPreference = Struct.new(:root_issuer_name, :root_issuer_key_id, :filter, keyword_init: true)
13
+ ProfileRule = Data.define(:name, :filter)
12
14
 
13
15
  def self.load_yaml(path)
14
16
  new YAML.load_file(path)
@@ -35,6 +37,10 @@ module Acmesmith
35
37
  if @config.key?('chain_preferences') && !@config.fetch('chain_preferences').kind_of?(Array)
36
38
  raise ArgumentError, "config['chain_preferences'] must be an Array"
37
39
  end
40
+
41
+ if @config.key?('profiles') && !@config.fetch('profiles').kind_of?(Array)
42
+ raise ArgumentError, "config['profiles'] must be an Array"
43
+ end
38
44
  end
39
45
 
40
46
  def [](key)
@@ -124,6 +130,16 @@ module Acmesmith
124
130
  end
125
131
  end
126
132
 
133
+ def profile_rules
134
+ @profile_rules ||= begin
135
+ specs = @config['profiles'] || []
136
+ specs.map do |spec|
137
+ filter = spec.fetch('filter', {}).map { |k,v| [k.to_sym, v] }.to_h
138
+ ProfileRule.new(name: spec['name'], filter: SubjectNameFilter.new(**filter))
139
+ end
140
+ end
141
+ end
142
+
127
143
  # def post_actions
128
144
  # end
129
145
  end
@@ -7,23 +7,28 @@ module Acmesmith
7
7
  class NotCompleted < StandardError; end
8
8
 
9
9
  # @param acme [Acme::Client] ACME client
10
- # @param identifiers [Array<String>] Array of domain names for a ordering certificate. The first item will be a common name.
10
+ # @param common_name [String] Common Name for a ordering certificate
11
+ # @param identifiers [Array<String>] Array of domain names for a ordering certificate. common_name has to be explicitly included in this argument.
11
12
  # @param private_key [OpenSSL::PKey::PKey] Private key
12
13
  # @param challenge_responder_rules [Array<Acmesmith::Config::ChallengeResponderRule>] responders
13
14
  # @param chain_preferences [Array<Acmesmith::Config::ChainPreference>] chain_preferences
14
15
  # @param not_before [Time]
15
16
  # @param not_after [Time]
16
- def initialize(acme:, identifiers:, private_key:, challenge_responder_rules:, chain_preferences:, not_before: nil, not_after: nil)
17
+ def initialize(acme:, common_name:, identifiers:, private_key:, challenge_responder_rules:, chain_preferences:, profile_rules: [], not_before: nil, not_after: nil)
17
18
  @acme = acme
19
+ @common_name = common_name
18
20
  @identifiers = identifiers
19
21
  @private_key = private_key
20
22
  @challenge_responder_rules = challenge_responder_rules
21
23
  @chain_preferences = chain_preferences
24
+ @profile_rules = profile_rules
22
25
  @not_before = not_before
23
26
  @not_after = not_after
27
+
28
+ @order_url = nil # https://github.com/unixcharles/acme-client/pull/263
24
29
  end
25
30
 
26
- attr_reader :acme, :identifiers, :private_key, :challenge_responder_rules, :chain_preferences, :not_before, :not_after
31
+ attr_reader :acme, :common_name, :identifiers, :private_key, :challenge_responder_rules, :chain_preferences, :profile_rules, :not_before, :not_after
27
32
 
28
33
  def perform!
29
34
  puts "=> Ordering a certificate for the following identifiers:"
@@ -33,9 +38,15 @@ module Acmesmith
33
38
  puts " * SAN: #{san}"
34
39
  end
35
40
 
41
+ resolved_profile = profile
42
+ if resolved_profile
43
+ puts
44
+ puts " * Profile: #{resolved_profile}"
45
+ end
46
+
36
47
  puts
37
48
  puts "=> Placing an order"
38
- @order = acme.new_order(identifiers: identifiers, not_before: not_before, not_after: not_after)
49
+ @order = acme.new_order(identifiers: identifiers, not_before: not_before, not_after: not_after, profile: resolved_profile)
39
50
  puts " * URL: #{order.url}"
40
51
 
41
52
  ensure_authorization()
@@ -43,7 +54,7 @@ module Acmesmith
43
54
  finalize_order()
44
55
  wait_order_for_complete()
45
56
 
46
- @certificate = Certificate.by_issuance(pem_chain, csr)
57
+ @certificate = Certificate.by_issuance(pem_chain, csr, name: common_name)
47
58
 
48
59
  puts
49
60
  puts "=> Certificate issued"
@@ -70,12 +81,16 @@ module Acmesmith
70
81
  puts
71
82
 
72
83
  print " * Requesting..."
84
+ @order_url = order.url if defined?(Acme::Client::Error::OrderNotReloadable)
73
85
  order.finalize(csr: csr)
74
86
  puts" [ ok ]"
75
87
  end
76
88
 
77
89
  def wait_order_for_complete
90
+ # Workaround for https://github.com/unixcharles/acme-client/pull/263
91
+
78
92
  while %w(ready processing).include?(order.status)
93
+ order.instance_variable_set(:@url, @order_url) if @order_url
79
94
  order.reload()
80
95
  puts " * Waiting for complete: status=#{order.status}"
81
96
  sleep 2
@@ -97,16 +112,15 @@ module Acmesmith
97
112
  @order or raise "BUG: order not yet generated"
98
113
  end
99
114
 
100
- # @return [String]
101
- def common_name
102
- identifiers.first
103
- end
104
-
105
115
  # @return [Array<String>]
106
116
  def sans
107
117
  identifiers[1..-1]
108
118
  end
109
119
 
120
+ def profile
121
+ profile_rules.find { |rule| rule.filter.match?(common_name) }&.name
122
+ end
123
+
110
124
  # @return [Acme::Client::CertificateRequest]
111
125
  def csr
112
126
  @csr ||= Acme::Client::CertificateRequest.new(subject: { common_name: common_name }, names: sans, private_key: private_key)
@@ -10,10 +10,10 @@ module Acmesmith
10
10
  end
11
11
 
12
12
  def execute
13
- puts "=> Executing Post Issueing Hook for #{common_name} in #{self.class.name}"
13
+ puts "=> Executing Post Issuing Hook for #{certificate.name.inspect} in #{self.class.name}"
14
14
  puts " $ #{@command}"
15
15
 
16
- status = system({"COMMON_NAME" => common_name}, @command)
16
+ status = system({"CERT_NAME" => certificate.name, "COMMON_NAME" => common_name}.compact, @command)
17
17
 
18
18
  unless status
19
19
  if @ignore_failure
@@ -23,7 +23,7 @@ module Acmesmith
23
23
  return
24
24
  end
25
25
 
26
- log "Saving certificate CN=#{cert.common_name} (ver: #{cert.version})"
26
+ log "Saving certificate #{cert.name.inspect} (ver: #{cert.version})"
27
27
 
28
28
  write_file(key_file, key_mode, cert.private_key)
29
29
  write_file(certificate_file, certificate_mode, cert.certificate.to_pem)
@@ -25,28 +25,28 @@ module Acmesmith
25
25
  raise NotImplementedError
26
26
  end
27
27
 
28
- # @param common_name [String]
28
+ # @param name [String]
29
29
  # @param version [String, nil]
30
30
  # @return [Acmesmith::Certificate]
31
- def get_certificate(common_name, version: 'current')
31
+ def get_certificate(name, version: 'current')
32
32
  raise NotImplementedError
33
33
  end
34
34
 
35
- # @param common_name [String]
36
- # @return [String] array of common_names
35
+ # @param name [String]
36
+ # @return [String] array of certificate names
37
37
  def list_certificates
38
38
  raise NotImplementedError
39
39
  end
40
40
 
41
- # @param common_name [String]
41
+ # @param name [String]
42
42
  # @return [String] array of versions
43
- def list_certificate_versions(common_name)
43
+ def list_certificate_versions(name)
44
44
  raise NotImplementedError
45
45
  end
46
46
 
47
- # @param common_name [String]
47
+ # @param name [String]
48
48
  # @return [String] current version
49
- def get_current_certificate_version(common_name)
49
+ def get_current_certificate_version(name)
50
50
  raise NotImplementedError
51
51
  end
52
52
  end
@@ -25,22 +25,22 @@ module Acmesmith
25
25
 
26
26
  def put_certificate(cert, passphrase = nil, update_current: true)
27
27
  h = cert.export(passphrase)
28
- certificate_base_path(cert.common_name, cert.version).mkpath
29
- File.write certificate_path(cert.common_name, cert.version), "#{h[:certificate].rstrip}\n"
30
- File.write chain_path(cert.common_name, cert.version), "#{h[:chain].rstrip}\n"
31
- File.write fullchain_path(cert.common_name, cert.version), "#{h[:fullchain].rstrip}\n"
32
- File.write private_key_path(cert.common_name, cert.version), "#{h[:private_key].rstrip}\n", 0, perm: 0600
28
+ certificate_base_path(cert.name, cert.version).mkpath
29
+ File.write certificate_path(cert.name, cert.version), "#{h[:certificate].rstrip}\n"
30
+ File.write chain_path(cert.name, cert.version), "#{h[:chain].rstrip}\n"
31
+ File.write fullchain_path(cert.name, cert.version), "#{h[:fullchain].rstrip}\n"
32
+ File.write private_key_path(cert.name, cert.version), "#{h[:private_key].rstrip}\n", 0, perm: 0600
33
33
  if update_current
34
- File.symlink(cert.version, certificate_base_path(cert.common_name, 'current.new'))
35
- File.rename(certificate_base_path(cert.common_name, 'current.new'), certificate_base_path(cert.common_name, 'current'))
34
+ File.symlink(cert.version, certificate_base_path(cert.name, 'current.new'))
35
+ File.rename(certificate_base_path(cert.name, 'current.new'), certificate_base_path(cert.name, 'current'))
36
36
  end
37
37
  end
38
38
 
39
- def get_certificate(common_name, version: 'current')
40
- raise NotExist.new("Certificate for #{common_name.inspect} of #{version} version doesn't exist") unless certificate_base_path(common_name, version).exist?
41
- certificate = certificate_path(common_name, version).read
42
- chain = chain_path(common_name, version).read
43
- private_key = private_key_path(common_name, version).read
39
+ def get_certificate(name, version: 'current')
40
+ raise NotExist.new("Certificate for #{name.inspect} of #{version} version doesn't exist") unless certificate_base_path(name, version).exist?
41
+ certificate = certificate_path(name, version).read
42
+ chain = chain_path(name, version).read
43
+ private_key = private_key_path(name, version).read
44
44
  Certificate.new(certificate, chain, private_key)
45
45
  end
46
46
 
@@ -48,12 +48,12 @@ module Acmesmith
48
48
  Dir[path.join('certs', '*').to_s].map { |_| File.basename(_) }
49
49
  end
50
50
 
51
- def list_certificate_versions(common_name)
52
- Dir[path.join('certs', common_name, '*').to_s].map { |_| File.basename(_) }.reject { |_| _ == 'current' }
51
+ def list_certificate_versions(name)
52
+ Dir[path.join('certs', name, '*').to_s].map { |_| File.basename(_) }.reject { |_| _ == 'current' }
53
53
  end
54
54
 
55
- def get_current_certificate_version(common_name)
56
- path.join('certs', common_name, 'current').readlink
55
+ def get_current_certificate_version(name)
56
+ path.join('certs', name, 'current').readlink
57
57
  end
58
58
 
59
59
  private
@@ -83,34 +83,34 @@ module Acmesmith
83
83
  @s3.put_object(params)
84
84
  end
85
85
 
86
- put.call certificate_key(cert.common_name, cert.version), "#{h[:certificate].rstrip}\n", false
87
- put.call chain_key(cert.common_name, cert.version), "#{h[:chain].rstrip}\n", false
88
- put.call fullchain_key(cert.common_name, cert.version), "#{h[:fullchain].rstrip}\n", false
89
- put.call private_key_key(cert.common_name, cert.version), "#{h[:private_key].rstrip}\n", use_kms
86
+ put.call certificate_key(cert.name, cert.version), "#{h[:certificate].rstrip}\n", false
87
+ put.call chain_key(cert.name, cert.version), "#{h[:chain].rstrip}\n", false
88
+ put.call fullchain_key(cert.name, cert.version), "#{h[:fullchain].rstrip}\n", false
89
+ put.call private_key_key(cert.name, cert.version), "#{h[:private_key].rstrip}\n", use_kms
90
90
 
91
91
  if generate_pkcs12?(cert)
92
- put.call pkcs12_key(cert.common_name, cert.version), "#{cert.pkcs12(@pkcs12_passphrase).to_der}\n", use_kms, 'application/x-pkcs12'
92
+ put.call pkcs12_key(cert.name, cert.version), "#{cert.pkcs12(@pkcs12_passphrase).to_der}\n", use_kms, 'application/x-pkcs12'
93
93
  end
94
94
 
95
95
  if update_current
96
96
  @s3.put_object(
97
97
  bucket: bucket,
98
- key: certificate_current_key(cert.common_name),
98
+ key: certificate_current_key(cert.name),
99
99
  content_type: 'text/plain',
100
100
  body: cert.version,
101
101
  )
102
102
  end
103
103
  end
104
104
 
105
- def get_certificate(common_name, version: 'current')
106
- version = certificate_current(common_name) if version == 'current'
105
+ def get_certificate(name, version: 'current')
106
+ version = certificate_current(name) if version == 'current'
107
107
 
108
- certificate = @s3.get_object(bucket: bucket, key: certificate_key(common_name, version)).body.read
109
- chain = @s3.get_object(bucket: bucket, key: chain_key(common_name, version)).body.read
110
- private_key = @s3.get_object(bucket: bucket, key: private_key_key(common_name, version)).body.read
108
+ certificate = @s3.get_object(bucket: bucket, key: certificate_key(name, version)).body.read
109
+ chain = @s3.get_object(bucket: bucket, key: chain_key(name, version)).body.read
110
+ private_key = @s3.get_object(bucket: bucket, key: private_key_key(name, version)).body.read
111
111
  Certificate.new(certificate, chain, private_key)
112
112
  rescue Aws::S3::Errors::NoSuchKey
113
- raise NotExist.new("Certificate for #{common_name.inspect} of #{version} version doesn't exist")
113
+ raise NotExist.new("Certificate for #{name.inspect} of #{version} version doesn't exist")
114
114
  end
115
115
 
116
116
  def list_certificates
@@ -125,8 +125,8 @@ module Acmesmith
125
125
  end
126
126
  end
127
127
 
128
- def list_certificate_versions(common_name)
129
- cert_ver_prefix = "#{prefix}certs/#{common_name}/"
128
+ def list_certificate_versions(name)
129
+ cert_ver_prefix = "#{prefix}certs/#{name}/"
130
130
  @s3.list_objects(
131
131
  bucket: bucket,
132
132
  delimiter: '/',
@@ -137,8 +137,8 @@ module Acmesmith
137
137
  end.reject { |_| _ == 'current' }
138
138
  end
139
139
 
140
- def get_current_certificate_version(common_name)
141
- certificate_current(common_name)
140
+ def get_current_certificate_version(name)
141
+ certificate_current(name)
142
142
  end
143
143
 
144
144
  private
@@ -0,0 +1,17 @@
1
+ require 'acmesmith/domain_name_filter'
2
+
3
+ module Acmesmith
4
+ class SubjectNameFilter
5
+ def initialize(subject_name_exact: nil, subject_name_suffix: nil, subject_name_regexp: nil)
6
+ @domain_name_filter = DomainNameFilter.new(
7
+ exact: subject_name_exact,
8
+ suffix: subject_name_suffix,
9
+ regexp: subject_name_regexp,
10
+ )
11
+ end
12
+
13
+ def match?(domain)
14
+ @domain_name_filter.match?(domain)
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,3 @@
1
1
  module Acmesmith
2
- VERSION = "2.7.1"
2
+ VERSION = "2.9.0"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acmesmith
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.1
4
+ version: 2.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sorah Fukumori
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-08-20 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: acme-client
@@ -141,20 +141,12 @@ extensions: []
141
141
  extra_rdoc_files: []
142
142
  files:
143
143
  - ".dockerignore"
144
- - ".github/FUNDING.yml"
145
- - ".github/stale.yml"
146
- - ".github/workflows/build.yml"
147
- - ".gitignore"
148
- - ".rspec"
149
144
  - ".travis.yml"
150
145
  - CHANGELOG.md
151
146
  - Dockerfile
152
- - Gemfile
153
- - Gemfile.lock
154
147
  - LICENSE.txt
155
148
  - README.md
156
149
  - Rakefile
157
- - acmesmith.gemspec
158
150
  - bin/acmesmith
159
151
  - config.sample.yml
160
152
  - docs/challenge_responders/route53.md
@@ -191,14 +183,16 @@ files:
191
183
  - lib/acmesmith/storages/base.rb
192
184
  - lib/acmesmith/storages/filesystem.rb
193
185
  - lib/acmesmith/storages/s3.rb
186
+ - lib/acmesmith/subject_name_filter.rb
194
187
  - lib/acmesmith/utils/finder.rb
195
188
  - lib/acmesmith/version.rb
196
- - script/console
197
- - script/setup
198
189
  homepage: https://github.com/sorah/acmesmith
199
190
  licenses:
200
191
  - MIT
201
- metadata: {}
192
+ metadata:
193
+ homepage_uri: https://github.com/sorah/acmesmith
194
+ source_code_uri: https://github.com/sorah/acmesmith
195
+ changelog_uri: https://github.com/sorah/acmesmith/blob/master/CHANGELOG.md
202
196
  rdoc_options: []
203
197
  require_paths:
204
198
  - lib
@@ -213,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
213
207
  - !ruby/object:Gem::Version
214
208
  version: '0'
215
209
  requirements: []
216
- rubygems_version: 3.6.2
210
+ rubygems_version: 4.0.3
217
211
  specification_version: 4
218
212
  summary: ACME client (Let's encrypt client) to manage certificate in multi server
219
213
  environment with cloud services (e.g. AWS)
data/.github/FUNDING.yml DELETED
@@ -1,2 +0,0 @@
1
- ko_fi: sorah
2
- github: [sorah]
data/.github/stale.yml DELETED
@@ -1,17 +0,0 @@
1
- # Number of days of inactivity before an issue becomes stale
2
- daysUntilStale: 30
3
- # Number of days of inactivity before a stale issue is closed
4
- daysUntilClose: 7
5
- # Issues with these labels will never be considered stale
6
- exemptLabels:
7
- - pinned
8
- - security
9
- # Label to use when marking an issue as stale
10
- staleLabel: rotten
11
- # Comment to post when marking an issue as stale. Set to `false` to disable
12
- markComment: >
13
- This issue has been automatically marked as stale because it has not had
14
- recent activity. It will be closed if no further activity occurs. Thank you
15
- for your contributions.
16
- # Comment to post when closing a stale issue. Set to `false` to disable
17
- closeComment: false