acmesmith-google-cloud-dns 0.1.1 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 610078ba72b04e675580cd85ca616d4be0de3097
4
- data.tar.gz: 73845b42a9a51dc1f39ce62d3c23b8a2754650fa
3
+ metadata.gz: c7d3669d3a965183f22b88bfaf0237dd96bdddb7
4
+ data.tar.gz: d792b2845d8f1f4865141e609d17e389974db7d1
5
5
  SHA512:
6
- metadata.gz: b72db3c4ba993de12218c9cc185e6402c4704b93333663fd3d25b12a9f27f3c477ed5fa15d2d49babaf113046484e6d56eeacddfbfe51d2d8aa73a382584270b
7
- data.tar.gz: 5ae01f49eae13b09d159e9ac940ae67a18c71d75a3daa42976f448ff595e062e1f8ffa4075cb2730cdebb601abe7b9f925539c5591e0bb68cd966a5c83839616
6
+ metadata.gz: b56650c70ad3329026efeccaaebe9713878806ae3096170792afc1c11ef0f9eec1f78a1c527516b4625685c04a651f31116550d5a428d5f9af220fdec22641dc
7
+ data.tar.gz: e13876bccf6c72ac5f6d84b7ef528ac382c1ff8be210e96da039e8cc20fe64d728e078207f3707a36442dc2f64f961101b0aeaf2fc05df007c24dcd428ab4a23
data/README.md CHANGED
@@ -22,7 +22,7 @@ Use `google-cloud-dns` challenge responder in your `acmesmith.yml`. General inst
22
22
  Write your `tenant_name`, `username`, `password` and `auth_url` in `acmesmith.yml`, or if you don't want to write them down into the file, export these values as the corresponding environment variables `OS_TENANT_NAME`, `OS_USERNAME`, `OS_PASSWORD` and `OS_AUTH_URL`.
23
23
 
24
24
  ```yaml
25
- endpoint: https://acme-v01.api.letsencrypt.org/
25
+ directory: https://acme-v02.api.letsencrypt.org/directory
26
26
 
27
27
  storage:
28
28
  type: filesystem
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_dependency "acmesmith"
22
+ spec.add_dependency "acmesmith", "~> 2.0"
23
23
  spec.add_dependency "google-api-client"
24
24
 
25
25
  spec.add_development_dependency "bundler", "~> 1.10"
@@ -1,3 +1,3 @@
1
1
  module AcmesmithGoogleCloudDns
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -3,6 +3,7 @@ require "acmesmith/challenge_responders/base"
3
3
  require "json"
4
4
  require "google/apis/dns_v1"
5
5
  require "resolv"
6
+ require "set"
6
7
 
7
8
  module Acmesmith
8
9
  module ChallengeResponders
@@ -11,6 +12,10 @@ module Acmesmith
11
12
  type == 'dns-01'
12
13
  end
13
14
 
15
+ def cap_respond_all?
16
+ true
17
+ end
18
+
14
19
  def initialize(config)
15
20
  @config = config
16
21
  @scope = "https://www.googleapis.com/auth/ndev.clouddns.readwrite"
@@ -32,21 +37,44 @@ module Acmesmith
32
37
  @project_id = @config[:project_id]
33
38
  end
34
39
 
35
- def respond(domain, challenge)
36
- puts "=> Responding challenge dns-01 for #{domain} in #{self.class.name}"
40
+ def respond_all(*domain_and_challenges)
41
+ challenges_by_zone_names = domain_and_challenges.group_by{ |domain, challenge|
42
+ domain = canonicalize(domain)
43
+ find_managed_zone(domain).name
44
+ }
37
45
 
38
- domain = canonicalize(domain)
39
- zone_name = find_managed_zone(domain).name
46
+ challenges_by_zone_names.each do |zone_name, dcs|
47
+ change = change_for_challenges(zone_name, dcs)
40
48
 
41
- puts " * create_change: #{challenge.record_type} #{[challenge.record_name, domain].join('.').inspect}, #{challenge.record_content.inspect}"
42
- change = Google::Apis::DnsV1::Change.new
43
- change.additions = [
44
- resource_record_set(domain, challenge)
45
- ]
46
- resp = @api.create_change(@project_id, zone_name, change)
49
+ resp = @api.create_change(@project_id, zone_name, change)
50
+ change_id = resp.id
47
51
 
48
- change_id = resp.id
52
+ wait_for_sync_by_api(zone_name, change_id)
53
+ wait_for_sync_by_dns(zone_name, change)
54
+ end
55
+ end
56
+
57
+ def cleanup_all(*domain_and_challenges)
58
+ challenges_by_zone_names = domain_and_challenges.group_by{ |domain, challenge|
59
+ domain = canonicalize(domain)
60
+ find_managed_zone(domain).name
61
+ }
62
+
63
+ challenges_by_zone_names.each do |zone_name, dcs|
64
+ change = change_for_challenges(zone_name, dcs, for_cleanup: true)
65
+
66
+ resp = @api.create_change(@project_id, zone_name, change)
67
+ change_id = resp.id
68
+
69
+ wait_for_sync_by_api(zone_name, change_id)
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def wait_for_sync_by_api(zone_name, change_id)
49
76
  puts " * requested change: #{change_id}"
77
+ resp = @api.get_change(@project_id, zone_name, change_id)
50
78
 
51
79
  while resp.status != 'done'
52
80
  puts " * change #{change_id.inspect} is still #{resp.status.inspect}"
@@ -55,38 +83,40 @@ module Acmesmith
55
83
  end
56
84
 
57
85
  puts " * synced!"
86
+ end
58
87
 
88
+ def wait_for_sync_by_dns(zone_name, change)
59
89
  puts "=> Checking DNS resource record"
60
90
  nameservers = @api.get_managed_zone(@project_id, zone_name).name_servers
61
91
  puts " * nameservers: #{nameservers.inspect}"
62
92
  nameservers.each do |ns|
63
93
  Resolv::DNS.open(:nameserver => Resolv.getaddresses(ns)) do |dns|
64
94
  dns.timeouts = 5
65
- begin
66
- ret = dns.getresource([challenge.record_name, domain].join('.'), Resolv::DNS::Resource::IN::TXT)
67
- rescue Resolv::ResolvError => e
68
- puts " * [#{ns}] failed: #{e.to_s}"
69
- sleep 5
70
- retry
95
+ change.additions.each do |rrset|
96
+ required_rrdatas = Set.new(rrset.rrdatas.map{|rrdata| rrdata.gsub(/(\A"|"\z)/, '') })
97
+
98
+ deletion = change.deletions.find{|_deletion| _deletion.name == rrset.name && _deletion.type == rrset.type }
99
+ if deletion
100
+ required_rrdatas -= Set.new(deletion.rrdatas)
101
+ end
102
+
103
+ loop do
104
+ resources = dns.getresources(rrset.name, Resolv::DNS::Resource::IN::TXT)
105
+ actual_rrdatas = resources.map(&:data)
106
+ if required_rrdatas.subset?(Set.new(actual_rrdatas))
107
+ puts " * [#{ns} -> #{rrset.name}] success. (actual=#{actual_rrdatas.inspect})"
108
+ sleep 1
109
+ break
110
+ else
111
+ puts " * [#{ns} -> #{rrset.name}] failed. (required=#{required_rrdatas.to_a.inspect}, but actual=#{actual_rrdatas.inspect})"
112
+ sleep 5
113
+ end
114
+ end
71
115
  end
72
- puts " * [#{ns}] success: ttl=#{ret.ttl.inspect}, data=#{ret.data.inspect}"
73
- sleep 1
74
116
  end
75
117
  end
76
118
  end
77
119
 
78
- def cleanup(domain, challenge)
79
- domain = canonicalize(domain)
80
- zone_name = find_managed_zone(domain).name
81
- change = Google::Apis::DnsV1::Change.new
82
- change.deletions = [
83
- resource_record_set(domain, challenge)
84
- ]
85
- @api.create_change(@project_id, zone_name, change)
86
- end
87
-
88
- private
89
-
90
120
  def load_json_key(filepath)
91
121
  obj = JSON.parse(File.read(filepath))
92
122
  {
@@ -109,13 +139,55 @@ module Acmesmith
109
139
  managed_zone
110
140
  end
111
141
 
112
- def resource_record_set(domain, challenge)
113
- Google::Apis::DnsV1::ResourceRecordSet.new(
114
- name: [challenge.record_name, domain].join("."),
115
- type: challenge.record_type,
116
- rrdatas: [challenge.record_content],
117
- ttl: @config[:ttl] || 5
118
- )
142
+ def change_for_challenges(zone_name, domain_and_challenges, for_cleanup: false)
143
+ current_rrsets = @api.fetch_all(items: :rrsets) do |token|
144
+ @api.list_resource_record_sets(@project_id, zone_name, page_token: token)
145
+ end
146
+
147
+ change = Google::Apis::DnsV1::Change.new
148
+
149
+ change.deletions = domain_and_challenges.map{ |domain, challenge|
150
+ domain = canonicalize(domain)
151
+ name = [challenge.record_name, domain].join('.')
152
+ type = challenge.record_type
153
+
154
+ current_rrsets.find{ |rrset| rrset.type == type && rrset.name == name }
155
+ }.uniq.compact
156
+
157
+ change.additions = domain_and_challenges.map{ |domain, challenge|
158
+ domain = canonicalize(domain)
159
+ name = [challenge.record_name, domain].join('.')
160
+ type = challenge.record_type
161
+ data = "\"#{challenge.record_content}\""
162
+
163
+ {
164
+ name: name,
165
+ type: type,
166
+ rrdatas: [data],
167
+ }
168
+ }.group_by{ |rrset_param|
169
+ [ rrset_param[:name], rrset_param[:type] ]
170
+ }.map{ |(name, type), rrset_params|
171
+ current_rrset = current_rrsets.find{ |rrset| rrset.type == type && rrset.name == name }
172
+
173
+ new_rrset = Google::Apis::DnsV1::ResourceRecordSet.new(
174
+ name: name,
175
+ type: type,
176
+ rrdatas: current_rrset ? current_rrset.rrdatas : [],
177
+ ttl: @config[:ttl] || 5,
178
+ )
179
+
180
+ if for_cleanup
181
+ new_rrset.rrdatas -= rrset_params.map{|rrset| rrset[:rrdatas] }.flatten
182
+ else
183
+ new_rrset.rrdatas += rrset_params.map{|rrset| rrset[:rrdatas] }.flatten
184
+ end
185
+ new_rrset
186
+ }.select{ |rrset|
187
+ rrset.rrdatas != []
188
+ }
189
+
190
+ change
119
191
  end
120
192
  end
121
193
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acmesmith-google-cloud-dns
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chikanaga Tomoyuki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-20 00:00:00.000000000 Z
11
+ date: 2018-11-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: acmesmith
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '2.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: google-api-client
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -119,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
119
  version: '0'
120
120
  requirements: []
121
121
  rubyforge_project:
122
- rubygems_version: 2.6.13
122
+ rubygems_version: 2.6.14.1
123
123
  signing_key:
124
124
  specification_version: 4
125
125
  summary: acmesmith plugin implementing dns-01 using Google Cloud DNS