surtr 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -4
- data/README.md +9 -1
- data/exe/surtr +66 -14
- data/lib/surtr/acme.rb +4 -4
- data/lib/surtr/dns.rb +14 -1
- data/lib/surtr/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fca9124754acf3bf04d33a71a06648c8c0c1d947fb9484cd8f9ff8edcd90eb7f
|
4
|
+
data.tar.gz: e6d981c356da1eacccbe8f104af4c31d2b9a79497ca3753590dde0af5606a03d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5e2025e6f7267723ae027d0c991267aade795b4f423d4ed8d1e89c35b32eb0bed36ca9679bc7d268b3116099fb517431d145a686e980161d9651b54a14d66c6b
|
7
|
+
data.tar.gz: 2bbcd17b0a21d85a5caee4cdfd2d7208ec5ec6074cb07fb718490069b33b9670c5ed298a8f8ad5da1d071e4f397e2aee20231db3ec47ef42dd3ebcbc127e8f21
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -8,6 +8,10 @@ A tool for managing Let's Encrypt certificates.
|
|
8
8
|
|
9
9
|
## Commands
|
10
10
|
|
11
|
+
### surtr wizard DESTINATION DOMAINS...
|
12
|
+
|
13
|
+
Authorize DOMAINS, get a certificate for them, and store the files in DESTINATION.
|
14
|
+
|
11
15
|
### surtr acme genkey
|
12
16
|
|
13
17
|
Generate a signing key for ACME requests.
|
@@ -28,6 +32,10 @@ For when the DNS has updated: verify the authorization.
|
|
28
32
|
|
29
33
|
Obtain a certificate for DOMAINS and store the files in DESTINATION.
|
30
34
|
|
31
|
-
### surtr dns
|
35
|
+
### surtr dns NAME TYPE VALUE gcp
|
32
36
|
|
33
37
|
Add a DNS record to GCP.
|
38
|
+
|
39
|
+
### surtr dns NAME TYPE VALUE wait
|
40
|
+
|
41
|
+
Wait until the record propagates.
|
data/exe/surtr
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "surtr"
|
4
4
|
require "clamp"
|
5
5
|
require "abbrev"
|
6
|
+
require "logger"
|
6
7
|
|
7
8
|
module Clamp::Subcommand::Declaration
|
8
9
|
def find_subcommand (name)
|
@@ -13,17 +14,62 @@ module Clamp::Subcommand::Declaration
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
17
|
+
$logger = Logger.new(STDERR)
|
18
|
+
|
16
19
|
Clamp do
|
17
20
|
|
18
|
-
|
21
|
+
option "--keyfile", "FILE", "where to store the ACME key", default: ENV["HOME"] + "/.acme.pem", environment_variable: "SURTR_ACME_KEYFILE"
|
22
|
+
|
23
|
+
option "--endpoint", "STRING", "which ACME endpoint to use [staging, v01]", default: "v01", environment_variable: "SURTR_ACME_ENDPOINT" do |val|
|
24
|
+
fail ArgumentError, "endpoint must be either `staging' or `v01'" unless %w(staging v01).member?(val)
|
25
|
+
end
|
26
|
+
|
27
|
+
option "--project", "PROJECT", "google cloud project name", required: true, environment_variable: "SURTR_GCP_PROJECT"
|
28
|
+
|
29
|
+
subcommand %w(wizard), "Generate a certificate for some domains all automatic like" do
|
30
|
+
|
31
|
+
parameter "DESTINATION", "directory the certificate files will be put in"
|
32
|
+
parameter "DOMAIN ...", "domains to generate a certificate for"
|
33
|
+
|
34
|
+
def execute
|
35
|
+
$logger.info "verifying domains..."
|
36
|
+
domain_list.each do |domain|
|
37
|
+
$logger.info "#{domain}: verifying..."
|
38
|
+
ok, name, type, value = Surtr::ACME.challenge(keyfile, endpoint, domain)
|
39
|
+
if ok
|
40
|
+
$logger.info "#{domain}: verified"
|
41
|
+
else
|
42
|
+
$logger.info "#{domain}: not verified: adding challenge to DNS"
|
43
|
+
$logger.info "#{domain}: challenge: #{value}"
|
44
|
+
Surtr::DNS.gcp(project, name, type, value)
|
45
|
+
$logger.info "#{domain}: waiting for propagation"
|
46
|
+
Surtr::DNS.wait(name, type, value)
|
47
|
+
$logger.info "#{domain}: checking verification..."
|
48
|
+
Surtr::ACME.verify(keyfile, endpoint, domain)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
$logger.info "requesting certificate..."
|
52
|
+
Surtr::ACME.certificate(keyfile, endpoint, destination, domain_list)
|
53
|
+
$logger.info "done"
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
19
57
|
|
20
|
-
|
58
|
+
subcommand %w(dns), "Manage DNS records" do
|
21
59
|
|
22
|
-
|
60
|
+
parameter "NAME", "record name"
|
61
|
+
parameter "TYPE", "record type"
|
62
|
+
parameter "VALUE", "record value"
|
23
63
|
|
24
|
-
|
25
|
-
|
26
|
-
|
64
|
+
subcommand %w(wait), "Wait until the DNS record propagates" do
|
65
|
+
|
66
|
+
def execute
|
67
|
+
Surtr::DNS.wait name, type, value
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
subcommand %w(gcp), "Add the DNS record to Google's Cloud DNS" do
|
27
73
|
|
28
74
|
def execute
|
29
75
|
Surtr::DNS.gcp project, name, type, value
|
@@ -35,11 +81,6 @@ Clamp do
|
|
35
81
|
|
36
82
|
subcommand %w(acme), "Manage ACME" do
|
37
83
|
|
38
|
-
option "--keyfile", "FILE", "where to store the ACME key", default: ENV["HOME"] + "/.acme.pem", environment_variable: "SURTR_ACME_KEYFILE"
|
39
|
-
option "--endpoint", "STRING", "which ACME endpoint to use [staging, v01]", default: "v01", environment_variable: "SURTR_ACME_ENDPOINT" do |val|
|
40
|
-
fail ArgumentError, "endpoint must be either `staging' or `v01'" unless %w(staging v01).member?(val)
|
41
|
-
end
|
42
|
-
|
43
84
|
subcommand %w(key), "Generate ACME key" do
|
44
85
|
|
45
86
|
def execute
|
@@ -73,15 +114,26 @@ Clamp do
|
|
73
114
|
|
74
115
|
parameter "DOMAIN", "domain name", environment_variable: "SURTR_ACME_AUTH_DOMAIN"
|
75
116
|
|
76
|
-
subcommand %w(
|
117
|
+
subcommand %w(challenge), "Begin authorization challenge" do
|
77
118
|
def execute
|
78
|
-
Surtr::ACME.challenge keyfile, endpoint, domain
|
119
|
+
ok, name, type, value = Surtr::ACME.challenge keyfile, endpoint, domain
|
120
|
+
if ok
|
121
|
+
puts "#{domain}: verified"
|
122
|
+
else
|
123
|
+
puts "#{domain}: not verified. DNS record required:"
|
124
|
+
puts " " + [[challenge.record_name, domain].join("."), challenge.record_type, challenge.record_content.inspect].join(" ")
|
125
|
+
end
|
79
126
|
end
|
80
127
|
end
|
81
128
|
|
82
129
|
subcommand %w(verify validate), "Verify authorization" do
|
83
130
|
def execute
|
84
|
-
Surtr::ACME.verify keyfile, endpoint, domain
|
131
|
+
ok = Surtr::ACME.verify keyfile, endpoint, domain
|
132
|
+
if ok
|
133
|
+
puts "#{domain}: verified"
|
134
|
+
else
|
135
|
+
puts "#{domain}: not verified"
|
136
|
+
end
|
85
137
|
end
|
86
138
|
end
|
87
139
|
|
data/lib/surtr/acme.rb
CHANGED
@@ -23,10 +23,9 @@ module Surtr
|
|
23
23
|
case auth.status
|
24
24
|
when "pending"
|
25
25
|
challenge = auth.dns01
|
26
|
-
|
27
|
-
puts " " + [[challenge.record_name, domain].join("."), challenge.record_type, challenge.record_content.inspect].join(" ")
|
26
|
+
return [false, [challenge.record_name, domain].join("."), challenge.record_type, challenge.record_content]
|
28
27
|
when "valid"
|
29
|
-
|
28
|
+
return true
|
30
29
|
else
|
31
30
|
fail "#{domain}: unexpected authorization status: #{auth.status}"
|
32
31
|
end
|
@@ -43,8 +42,9 @@ module Surtr
|
|
43
42
|
while auth.verify_status == "pending"
|
44
43
|
sleep 0.1
|
45
44
|
end
|
45
|
+
return true
|
46
46
|
when "valid"
|
47
|
-
|
47
|
+
return true
|
48
48
|
else
|
49
49
|
fail "#{domain}: unexpected authorization status: #{auth.status}"
|
50
50
|
end
|
data/lib/surtr/dns.rb
CHANGED
@@ -1,15 +1,28 @@
|
|
1
1
|
require "google/cloud/dns"
|
2
|
+
require "resolv"
|
2
3
|
|
3
4
|
module Surtr
|
4
5
|
|
5
6
|
module DNS
|
6
7
|
|
8
|
+
def self.wait (name, type, value)
|
9
|
+
loop do
|
10
|
+
ok = %w(8.8.8.8 8.8.4.4).all? do |ns|
|
11
|
+
r = Resolv::DNS.new(nameserver: ns)
|
12
|
+
s = r.getresource(name, Resolv::DNS::Resource::IN::TXT).strings
|
13
|
+
s.member?(value)
|
14
|
+
end
|
15
|
+
break if ok
|
16
|
+
sleep 0.5
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
7
20
|
def self.gcp (project, name, type, value)
|
8
21
|
|
9
22
|
dns = Google::Cloud::Dns.new project: project
|
10
23
|
dns.zones.each do |zone|
|
11
24
|
if name.end_with?(zone.dns[0..-2])
|
12
|
-
zone.replace name, type,
|
25
|
+
zone.replace name, type, 1, value
|
13
26
|
break
|
14
27
|
end
|
15
28
|
end
|
data/lib/surtr/version.rb
CHANGED