surtr 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
  SHA256:
3
- metadata.gz: 6347d467f10a9aa0d07d119f77d9608bb633c684f61e05ed55bf3747ff56dae0
4
- data.tar.gz: 717b3f089df4fb666fa2459fa31dd2e45ac6487a5b7882bc7049f99fbaecad4b
3
+ metadata.gz: fca9124754acf3bf04d33a71a06648c8c0c1d947fb9484cd8f9ff8edcd90eb7f
4
+ data.tar.gz: e6d981c356da1eacccbe8f104af4c31d2b9a79497ca3753590dde0af5606a03d
5
5
  SHA512:
6
- metadata.gz: '0885c832971d73ba1769c86e4b905c801f67c0ffe806d2fdb1215b5dc91d7925e52c072d235b32443e028a99efd49b5d94027fb08fe50405b7a5df811ee4b088'
7
- data.tar.gz: 66bd2612418a162992e6911be922186697c8a274db2e74147954ba95afca588f6592c84c8872690eafce09906d8f4ecb8ec8466eb078a19125699a36e27a7b61
6
+ metadata.gz: 5e2025e6f7267723ae027d0c991267aade795b4f423d4ed8d1e89c35b32eb0bed36ca9679bc7d268b3116099fb517431d145a686e980161d9651b54a14d66c6b
7
+ data.tar.gz: 2bbcd17b0a21d85a5caee4cdfd2d7208ec5ec6074cb07fb718490069b33b9670c5ed298a8f8ad5da1d071e4f397e2aee20231db3ec47ef42dd3ebcbc127e8f21
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- surtr (0.1.0)
5
- acme-client
6
- clamp
7
- google-cloud-dns
4
+ surtr (0.2.0)
5
+ acme-client (~> 0.6)
6
+ clamp (~> 1.2)
7
+ google-cloud-dns (~> 0.28)
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
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 gcp NAME TYPE VALUE
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
- subcommand %w(dns), "Manage DNS" do
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
- option "--project", "PROJECT", "google cloud project name", required: true, environment_variable: "SURTR_GCP_PROJECT"
58
+ subcommand %w(dns), "Manage DNS records" do
21
59
 
22
- subcommand %w(gcp), "Add a DNS record" do
60
+ parameter "NAME", "record name"
61
+ parameter "TYPE", "record type"
62
+ parameter "VALUE", "record value"
23
63
 
24
- parameter "NAME", "record name"
25
- parameter "TYPE", "record type"
26
- parameter "VALUE", "record value"
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(c challenge), "Begin authorization challenge" do
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
- puts "#{domain}: not verified. DNS record required:"
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
- puts "#{domain}: verified"
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
- puts "#{domain}: verified"
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, 60, value
25
+ zone.replace name, type, 1, value
13
26
  break
14
27
  end
15
28
  end
data/lib/surtr/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Surtr
2
- VERSION = "0.1.1"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: surtr
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
  - Nathan Baum