acme_nsupdate 0.2.1 → 0.3.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 +4 -4
- data/lib/acme_nsupdate/strategies/dns01.rb +65 -10
- data/lib/acme_nsupdate/strategies/http01.rb +4 -4
- data/lib/acme_nsupdate/strategy.rb +21 -0
- data/lib/acme_nsupdate/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e22b3776fb2331b29ccdbded623176832232282
|
4
|
+
data.tar.gz: 395303634131286629fd3092f102651d4e4901da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b227c653a1414547d8317d4ada1091cbdf36c71f79be39ac4de08a6406a2b06bd0ea89f73a52076b9a7eeabadb34286c46b609f953c2d2cc4a384a5c63c5cffc
|
7
|
+
data.tar.gz: 8df03352e20f02af82cc2ccf2ac8fe9b1997976e10ddc86160e7a14fbc9ed601c0d92431f15e08953f2d318ad8f8e86be246f718b3802bda2971ba824a209f13
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "resolv"
|
2
|
+
|
1
3
|
require "acme_nsupdate/strategy"
|
2
4
|
|
3
5
|
module AcmeNsupdate
|
@@ -12,23 +14,24 @@ module AcmeNsupdate
|
|
12
14
|
end
|
13
15
|
|
14
16
|
def publish_challenges
|
15
|
-
|
16
|
-
|
17
|
-
challenges = @client.options[:domains].map {|domain|
|
18
|
-
nsupdate = @client.build_nsupdate
|
19
|
-
|
20
|
-
authorization = @client.client.authorize domain: domain
|
17
|
+
challenges = map_authorizations {|domain, authorization|
|
21
18
|
challenge = authorization.dns01
|
22
19
|
abort "Challenge dns-01 not supported by the given ACME server" unless challenge
|
20
|
+
|
21
|
+
nsupdate = @client.build_nsupdate
|
23
22
|
nsupdate.del(*record(domain, challenge, true)) unless @client.options[:keep]
|
24
23
|
nsupdate.add(*record(domain, challenge), @client.options[:txt_ttl])
|
25
24
|
nsupdate.send
|
26
25
|
|
27
|
-
|
28
|
-
}
|
26
|
+
challenge
|
27
|
+
}
|
29
28
|
|
30
|
-
|
31
|
-
|
29
|
+
unless challenges.empty?
|
30
|
+
@client.logger.info "Waiting up to 120 seconds for the DNS updates to go live"
|
31
|
+
unless verify_live_challenges(@client.options[:master], challenges)
|
32
|
+
raise AcmeNsupdate::Client::Error, "DNS challenges didn't appear on all nameservers within 120 seconds"
|
33
|
+
end
|
34
|
+
end
|
32
35
|
|
33
36
|
challenges
|
34
37
|
end
|
@@ -44,6 +47,58 @@ module AcmeNsupdate
|
|
44
47
|
|
45
48
|
private
|
46
49
|
|
50
|
+
def verify_live_challenges(primary, challenges, timeout=120)
|
51
|
+
waited = 0
|
52
|
+
public_nameservers(primary, challenges.first.first).all? {|nameserver|
|
53
|
+
@client.logger.debug "Verifying DNS challenges are present on #{nameserver}"
|
54
|
+
challenges.all? {|domain, challenge|
|
55
|
+
name, type, content = record(domain, challenge)
|
56
|
+
records = query(nameserver, name, type).map(&:strings).flatten.map {|content| %("#{content}") }
|
57
|
+
@client.logger.debug "Got #{records.size} TXT records for #{name}: #{records.map(&:inspect).join(", ")}"
|
58
|
+
if records.include? content
|
59
|
+
true
|
60
|
+
elsif waited == timeout
|
61
|
+
@client.logger.error "None matched, timeout reached, aborting"
|
62
|
+
return false
|
63
|
+
else
|
64
|
+
@client.logger.debug "None matched, pausing for 5 seconds, already waited #{waited} seconds"
|
65
|
+
sleep 5
|
66
|
+
waited += 5
|
67
|
+
redo
|
68
|
+
end
|
69
|
+
}
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def public_nameservers(primary, name)
|
74
|
+
# We have to hack into this because it gives us no way to fetch the SOA on a NXDOMAIN
|
75
|
+
authority = nil
|
76
|
+
Resolv::DNS.open(nameserver: [primary], search: [], ndots: 1) do |dns|
|
77
|
+
dns.lazy_initialize
|
78
|
+
message = Resolv::DNS::Message.new
|
79
|
+
message.rd = 1
|
80
|
+
message.add_question(name, Resolv::DNS::Resource::IN::SOA)
|
81
|
+
requester = dns.make_udp_requester
|
82
|
+
|
83
|
+
begin
|
84
|
+
sender = requester.sender(message, name, primary, 53)
|
85
|
+
reply, _ = requester.request(sender, 10)
|
86
|
+
authority = reply.authority.first.first.to_s
|
87
|
+
ensure
|
88
|
+
requester.close
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
return [] unless authority
|
93
|
+
query(primary, authority, :NS).map {|record| record.name.to_s }.uniq
|
94
|
+
end
|
95
|
+
|
96
|
+
def query(nameserver, name, qtype)
|
97
|
+
Resolv::DNS.open(nameserver: [nameserver], search: [], ndots: 1) do |dns|
|
98
|
+
return dns.getresources(name, Resolv::DNS::Resource::IN.const_get(qtype))
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
47
102
|
def record domain, challenge, nodata=false
|
48
103
|
["#{challenge.record_name}.#{domain}", challenge.record_type].tap do |record|
|
49
104
|
record << %("#{challenge.record_content}") unless nodata
|
@@ -14,17 +14,17 @@ module AcmeNsupdate
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def publish_challenges
|
17
|
-
|
18
|
-
@client.options[:domains].map {|domain|
|
19
|
-
authorization = @client.client.authorize domain: domain
|
17
|
+
map_authoriations {|domain, authorization|
|
20
18
|
challenge = authorization.http01
|
21
19
|
abort "Challenge http-01 not supported by this ACME server" unless challenge
|
20
|
+
|
22
21
|
path = path challenge
|
23
22
|
@client.logger.debug "Writing #{path} for #{domain}"
|
24
23
|
FileUtils.mkdir_p File.dirname path
|
25
24
|
File.write path, challenge.file_content
|
25
|
+
|
26
26
|
[domain, challenge]
|
27
|
-
}.to_h
|
27
|
+
}.compact.to_h
|
28
28
|
end
|
29
29
|
|
30
30
|
def cleanup challenges
|
@@ -21,8 +21,29 @@ module AcmeNsupdate
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
|
24
25
|
private
|
25
26
|
|
27
|
+
def map_authorizations
|
28
|
+
@client.logger.debug "Publishing challenges for #{@client.options[:domains].join(", ")}"
|
29
|
+
|
30
|
+
challenges = @client.options[:domains].map {|domain|
|
31
|
+
authorization = @client.client.authorize domain: domain
|
32
|
+
if authorization.status == "valid"
|
33
|
+
@client.logger.debug("Skipping challenge for #{domain}, already valid.")
|
34
|
+
next
|
35
|
+
end
|
36
|
+
|
37
|
+
challenge = yield domain, authorization
|
38
|
+
unless challenge
|
39
|
+
@client.logger.debug("Skipping challenge for #{domain}, not solvable.")
|
40
|
+
next
|
41
|
+
end
|
42
|
+
|
43
|
+
[domain, challenge]
|
44
|
+
}.compact.to_h
|
45
|
+
end
|
46
|
+
|
26
47
|
def wait_for_verification challenges
|
27
48
|
@client.logger.debug("Requesting verification")
|
28
49
|
challenges.each_value(&:request_verification)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acme_nsupdate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonne Haß
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: slop
|